diff --git a/pkgbuilds/linux-omarchy-bore/0010-archlinux-base.patch b/pkgbuilds/linux-omarchy-bore/0010-archlinux-base.patch index 55e9379..497422b 100644 --- a/pkgbuilds/linux-omarchy-bore/0010-archlinux-base.patch +++ b/pkgbuilds/linux-omarchy-bore/0010-archlinux-base.patch @@ -1,5 +1,4 @@ diff --git a/kernel/fork.c b/kernel/fork.c -index f0e2e131a9a5..7b611da9a27a 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -127,6 +127,12 @@ @@ -15,7 +14,7 @@ index f0e2e131a9a5..7b611da9a27a 100644 /* * Minimum number of threads to boot the kernel */ -@@ -2093,6 +2099,11 @@ __latent_entropy struct task_struct *copy_process( +@@ -2092,6 +2098,11 @@ __latent_entropy struct task_struct *copy_process( return ERR_PTR(-EPERM); } @@ -27,7 +26,7 @@ index f0e2e131a9a5..7b611da9a27a 100644 /* * Force any signals received before this point to be delivered * before the fork happens. Collect up signals sent to multiple -@@ -3163,6 +3174,10 @@ static int check_unshare_flags(unsigned long unshare_flags) +@@ -3165,6 +3176,10 @@ static int check_unshare_flags(unsigned long unshare_flags) if (!current_is_single_threaded()) return -EINVAL; } @@ -38,7 +37,7 @@ index f0e2e131a9a5..7b611da9a27a 100644 return 0; } -@@ -3398,6 +3413,15 @@ static const struct ctl_table fork_sysctl_table[] = { +@@ -3400,6 +3415,15 @@ static const struct ctl_table fork_sysctl_table[] = { .mode = 0644, .proc_handler = sysctl_max_threads, }, diff --git a/pkgbuilds/linux-omarchy-bore/0010-archlinux-base.patch.sig b/pkgbuilds/linux-omarchy-bore/0010-archlinux-base.patch.sig index a7eaa24..586266d 100644 Binary files a/pkgbuilds/linux-omarchy-bore/0010-archlinux-base.patch.sig and b/pkgbuilds/linux-omarchy-bore/0010-archlinux-base.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0110-bore-6.8.0.patch b/pkgbuilds/linux-omarchy-bore/0110-bore-6.8.0.patch index 1d049bd..c126c5a 100644 --- a/pkgbuilds/linux-omarchy-bore/0110-bore-6.8.0.patch +++ b/pkgbuilds/linux-omarchy-bore/0110-bore-6.8.0.patch @@ -177,7 +177,7 @@ diff --git a/kernel/fork.c b/kernel/fork.c #include #define CREATE_TRACE_POINTS -@@ -2441,6 +2445,11 @@ __latent_entropy struct task_struct *copy_process( +@@ -2443,6 +2447,11 @@ __latent_entropy struct task_struct *copy_process( p->start_time = ktime_get_ns(); p->start_boottime = ktime_get_boottime_ns(); @@ -189,7 +189,7 @@ diff --git a/kernel/fork.c b/kernel/fork.c /* * Make it visible to the rest of the system, but dont wake it up yet. * Need tasklist lock for parent etc handling! -@@ -2526,7 +2535,11 @@ __latent_entropy struct task_struct *copy_process( +@@ -2528,7 +2537,11 @@ __latent_entropy struct task_struct *copy_process( p->real_parent->signal->is_child_subreaper; if (clone_flags & CLONE_AUTOREAP) p->signal->autoreap = 1; @@ -749,7 +749,7 @@ diff --git a/kernel/sched/core.c b/kernel/sched/core.c struct load_weight lw; if (task_has_idle_policy(p)) { -@@ -8975,6 +8983,10 @@ void __init sched_init(void) +@@ -8989,6 +8997,10 @@ void __init sched_init(void) BUG_ON(!sched_class_above(&ext_sched_class, &idle_sched_class)); #endif @@ -934,7 +934,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c void __init sched_init_granularity(void) { -@@ -1083,7 +1108,11 @@ struct sched_entity *__pick_first_entity(struct cfs_rq *cfs_rq) +@@ -1093,7 +1118,11 @@ struct sched_entity *__pick_first_entity(struct cfs_rq *cfs_rq) */ static inline void set_protect_slice(struct cfs_rq *cfs_rq, struct sched_entity *se) { @@ -946,7 +946,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c u64 vprot = se->deadline; if (sched_feat(RUN_TO_PARITY)) -@@ -1160,8 +1189,17 @@ static struct sched_entity *pick_eevdf(struct cfs_rq *cfs_rq, bool protect) +@@ -1170,8 +1199,17 @@ static struct sched_entity *pick_eevdf(struct cfs_rq *cfs_rq, bool protect) if (curr && (!curr->on_rq || !entity_eligible(cfs_rq, curr))) curr = NULL; @@ -966,7 +966,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c /* Pick the leftmost entity if it's eligible */ if (se && entity_eligible(cfs_rq, se)) { -@@ -1217,6 +1255,7 @@ struct sched_entity *__pick_last_entity(struct cfs_rq *cfs_rq) +@@ -1227,6 +1265,7 @@ struct sched_entity *__pick_last_entity(struct cfs_rq *cfs_rq) /************************************************************** * Scheduling class statistics methods: */ @@ -974,7 +974,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c int sched_update_scaling(void) { unsigned int factor = get_update_sysctl_factor(); -@@ -1228,6 +1267,7 @@ int sched_update_scaling(void) +@@ -1238,6 +1277,7 @@ int sched_update_scaling(void) return 0; } @@ -982,7 +982,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c static void clear_buddies(struct cfs_rq *cfs_rq, struct sched_entity *se); -@@ -1971,6 +2011,38 @@ static void account_llc_dequeue(struct rq *rq, struct task_struct *p) {} +@@ -1979,6 +2019,38 @@ static void account_llc_dequeue(struct rq *rq, struct task_struct *p) {} #endif /* CONFIG_SCHED_CACHE */ @@ -1021,7 +1021,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c /* * Used by other classes to account runtime. */ -@@ -2006,6 +2078,11 @@ static void update_curr(struct cfs_rq *cfs_rq) +@@ -2014,6 +2086,11 @@ static void update_curr(struct cfs_rq *cfs_rq) resched = update_deadline(cfs_rq, curr); if (entity_is_task(curr)) { @@ -1033,7 +1033,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c /* * If the fair_server is active, we need to account for the * fair_server time whether or not the task is running on -@@ -4663,8 +4740,8 @@ rescale_entity(struct sched_entity *se, unsigned long weight, bool rel_vprot) +@@ -4671,8 +4748,8 @@ rescale_entity(struct sched_entity *se, unsigned long weight, bool rel_vprot) se->vprot = div64_long(se->vprot * old_weight, weight); } @@ -1044,7 +1044,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c { bool curr = cfs_rq->curr == se; bool rel_vprot = false; -@@ -5967,13 +6044,12 @@ void __setparam_fair(struct task_struct *p, const struct sched_attr *attr) +@@ -5976,13 +6053,12 @@ void __setparam_fair(struct task_struct *p, const struct sched_attr *attr) static void place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) { @@ -1059,7 +1059,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c /* * Due to how V is constructed as the weighted average of entities, -@@ -6085,7 +6161,18 @@ place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) +@@ -6094,7 +6170,18 @@ place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) se->rel_deadline = 0; return; } @@ -1079,7 +1079,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c /* * When joining the competition; the existing tasks will be, * on average, halfway through their slice, as such start tasks -@@ -6094,6 +6181,9 @@ place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) +@@ -6103,6 +6190,9 @@ place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) if (sched_feat(PLACE_DEADLINE_INITIAL) && (flags & ENQUEUE_INITIAL)) vslice /= 2; @@ -1089,7 +1089,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c /* * EEVDF: vd_i = ve_i + r_i/w_i */ -@@ -6104,7 +6194,7 @@ static void check_enqueue_throttle(struct cfs_rq *cfs_rq); +@@ -6113,7 +6203,7 @@ static void check_enqueue_throttle(struct cfs_rq *cfs_rq); static inline int cfs_rq_throttled(struct cfs_rq *cfs_rq); static void @@ -1098,7 +1098,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c static void enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) -@@ -7767,7 +7857,7 @@ static int choose_idle_cpu(int cpu, struct task_struct *p) +@@ -7776,7 +7866,7 @@ static int choose_idle_cpu(int cpu, struct task_struct *p) } static void @@ -1107,7 +1107,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c { struct cfs_rq *cfs_rq = cfs_rq_of(se); -@@ -7785,7 +7875,13 @@ requeue_delayed_entity(struct sched_entity *se) +@@ -7794,7 +7884,13 @@ requeue_delayed_entity(struct sched_entity *se) cfs_rq->nr_queued--; if (se != cfs_rq->curr) __dequeue_entity(cfs_rq, se); @@ -1122,7 +1122,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c if (se != cfs_rq->curr) __enqueue_entity(cfs_rq, se); cfs_rq->nr_queued++; -@@ -7824,7 +7920,7 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags) +@@ -7833,7 +7929,7 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags) util_est_enqueue(&rq->cfs, p); if (flags & ENQUEUE_DELAYED) { @@ -1131,7 +1131,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c return; } -@@ -7842,7 +7938,7 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags) +@@ -7851,7 +7947,7 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags) for_each_sched_entity(se) { if (se->on_rq) { if (se->sched_delayed) @@ -1140,7 +1140,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c break; } cfs_rq = cfs_rq_of(se); -@@ -8045,6 +8141,15 @@ static bool dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags) +@@ -8054,6 +8150,15 @@ static bool dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags) if (!p->se.sched_delayed) util_est_dequeue(&rq->cfs, p); @@ -1156,7 +1156,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c if (dequeue_entities(rq, &p->se, flags) < 0) return false; -@@ -9850,6 +9955,18 @@ static void wakeup_preempt_fair(struct rq *rq, struct task_struct *p, int wake_f +@@ -9860,6 +9965,18 @@ static void wakeup_preempt_fair(struct rq *rq, struct task_struct *p, int wake_f goto pick; } @@ -1175,7 +1175,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c /* * Ignore wakee preemption on WF_FORK as it is less likely that * there is shared data as exec often follow fork. Do not -@@ -10020,16 +10137,25 @@ static void yield_task_fair(struct rq *rq) +@@ -10030,16 +10147,25 @@ static void yield_task_fair(struct rq *rq) /* * Are we the only task in the tree? */ @@ -1201,7 +1201,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c /* * Tell update_rq_clock() that we've just updated, * so we don't do microscopic update in schedule() -@@ -15010,6 +15136,9 @@ static void switched_to_fair(struct rq *rq, struct task_struct *p) +@@ -15022,6 +15148,9 @@ static void switched_to_fair(struct rq *rq, struct task_struct *p) WARN_ON_ONCE(p->se.sched_delayed); attach_task_cfs_rq(p); diff --git a/pkgbuilds/linux-omarchy-bore/0110-bore-6.8.0.patch.sig b/pkgbuilds/linux-omarchy-bore/0110-bore-6.8.0.patch.sig index 6256900..1efa5e3 100644 Binary files a/pkgbuilds/linux-omarchy-bore/0110-bore-6.8.0.patch.sig and b/pkgbuilds/linux-omarchy-bore/0110-bore-6.8.0.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0120-tlbpull.patch b/pkgbuilds/linux-omarchy-bore/0120-tlbpull.patch index 712b9b7..14e2d46 100644 --- a/pkgbuilds/linux-omarchy-bore/0120-tlbpull.patch +++ b/pkgbuilds/linux-omarchy-bore/0120-tlbpull.patch @@ -22,19 +22,6 @@ diff --git a/arch/x86/include/asm/pgtable_types.h b/arch/x86/include/asm/pgtable typedef struct page *pgtable_t; extern pteval_t __supported_pte_mask; -diff --git a/arch/x86/include/asm/pkeys.h b/arch/x86/include/asm/pkeys.h ---- a/arch/x86/include/asm/pkeys.h -+++ b/arch/x86/include/asm/pkeys.h -@@ -88,6 +88,9 @@ int mm_pkey_alloc(struct mm_struct *mm) - u16 all_pkeys_mask = ((1U << arch_max_pkey()) - 1); - int ret; - -+ if (!arch_pkeys_enabled()) -+ return -1; -+ - /* - * Are we out of pkeys? We must handle this specially - * because ffz() behavior is undefined if there are no diff --git a/arch/x86/include/asm/tlbflush.h b/arch/x86/include/asm/tlbflush.h --- a/arch/x86/include/asm/tlbflush.h +++ b/arch/x86/include/asm/tlbflush.h @@ -168,35 +155,7 @@ diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c --- a/arch/x86/mm/pat/set_memory.c +++ b/arch/x86/mm/pat/set_memory.c -@@ -62,18 +62,18 @@ enum cpa_warn { - static const int cpa_warn_level = CPA_PROTECT; - - /* -- * Serialize cpa() (for !DEBUG_PAGEALLOC which uses large identity mappings) -- * using cpa_lock. So that we don't allow any other cpu, with stale large tlb -- * entries change the page attribute in parallel to some other cpu -- * splitting a large page entry along with changing the attribute. -+ * Serialize cpa() using cpa_lock so that we don't allow any other cpu, with -+ * stale large tlb entries, to change the page attribute in parallel to some -+ * other cpu splitting a large page entry along with changing the attribute. - */ - static DEFINE_SPINLOCK(cpa_lock); - --#define CPA_FLUSHTLB 1 --#define CPA_ARRAY 2 --#define CPA_PAGES_ARRAY 4 --#define CPA_NO_CHECK_ALIAS 8 /* Do not search for aliases */ --#define CPA_COLLAPSE 16 /* try to collapse large pages */ -+#define CPA_FLUSHTLB 0x01 -+#define CPA_ARRAY 0x02 -+#define CPA_PAGES_ARRAY 0x04 -+#define CPA_NO_CHECK_ALIAS 0x08 /* Do not search for aliases */ -+#define CPA_COLLAPSE 0x10 /* try to collapse large pages */ -+#define CPA_DEBUG_PAGEALLOC 0x20 - - static inline pgprot_t cachemode2pgprot(enum page_cache_mode pcm) - { -@@ -86,9 +86,8 @@ static unsigned long direct_pages_count[PG_LEVEL_NUM]; +@@ -88,9 +88,8 @@ static unsigned long direct_pages_count[PG_LEVEL_NUM]; void update_page_count(int level, unsigned long pages) { /* Protect against CPA */ @@ -207,16 +166,37 @@ diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c } static void split_page_count(int level) -@@ -418,6 +417,8 @@ static void cpa_collapse_large_pages(struct cpa_data *cpa) +@@ -420,7 +419,7 @@ static void __cpa_collapse_large_pages(struct cpa_data *cpa) int collapsed = 0; int i; +- spin_lock(&cpa_lock); + guard(spinlock)(&cpa_lock); -+ + if (cpa->flags & (CPA_PAGES_ARRAY | CPA_ARRAY)) { for (i = 0; i < cpa->numpages; i++) - collapsed += collapse_large_pages(__cpa_addr(cpa, i), -@@ -888,24 +889,23 @@ static void __set_pmd_pte(pte_t *kpte, unsigned long address, pte_t pte) +@@ -435,10 +434,8 @@ static void __cpa_collapse_large_pages(struct cpa_data *cpa) + collapsed += collapse_large_pages(addr, &pgtables); + } + +- if (!collapsed) { +- spin_unlock(&cpa_lock); ++ if (!collapsed) + return; +- } + + flush_tlb_all(); + +@@ -454,8 +451,6 @@ static void __cpa_collapse_large_pages(struct cpa_data *cpa) + else + pagetable_free(ptdesc); + } +- +- spin_unlock(&cpa_lock); + } + + static void cpa_collapse_large_pages(struct cpa_data *cpa) +@@ -916,24 +911,23 @@ static void __set_pmd_pte(pte_t *kpte, unsigned long address, pte_t pte) { /* change init_mm */ set_pte_atomic(kpte, pte); @@ -246,7 +226,7 @@ diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c } static pgprot_t pgprot_clear_protnone_bits(pgprot_t prot) -@@ -1075,16 +1075,11 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, +@@ -1103,16 +1097,11 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, static int should_split_large_page(pte_t *kpte, unsigned long address, struct cpa_data *cpa) { @@ -265,7 +245,7 @@ diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c } static void split_set_pte(struct cpa_data *cpa, pte_t *pte, unsigned long pfn, -@@ -1135,16 +1130,14 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address, +@@ -1162,16 +1151,14 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address, bool nx, rw; pte_t *tmp; @@ -284,7 +264,7 @@ diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c paravirt_alloc_pte(&init_mm, page_to_pfn(base)); -@@ -1177,7 +1170,6 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address, +@@ -1204,7 +1191,6 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address, break; default: @@ -292,7 +272,7 @@ diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c return 1; } -@@ -1225,7 +1217,6 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address, +@@ -1252,7 +1238,6 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address, * just split large page entry. */ flush_tlb_all(); @@ -300,21 +280,7 @@ diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c return 0; } -@@ -1235,11 +1226,9 @@ static int split_large_page(struct cpa_data *cpa, pte_t *kpte, - { - struct ptdesc *ptdesc; - -- if (!debug_pagealloc_enabled()) -- spin_unlock(&cpa_lock); -+ spin_unlock(&cpa_lock); - ptdesc = pagetable_alloc(GFP_KERNEL, 0); -- if (!debug_pagealloc_enabled()) -- spin_lock(&cpa_lock); -+ spin_lock(&cpa_lock); - if (!ptdesc) - return -ENOMEM; - -@@ -1298,11 +1287,11 @@ static int collapse_pmd_page(pmd_t *pmd, unsigned long addr, +@@ -1327,11 +1312,11 @@ static int collapse_pmd_page(pmd_t *pmd, unsigned long addr, list_add(&page_ptdesc(pmd_page(old_pmd))->pt_list, pgtables); if (IS_ENABLED(CONFIG_X86_32)) { @@ -329,7 +295,7 @@ diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c p4d_t *p4d = p4d_offset(pgd, addr); pud_t *pud = pud_offset(p4d, addr); pmd_t *pmd = pmd_offset(pud, addr); -@@ -1376,7 +1365,7 @@ static int collapse_pud_page(pud_t *pud, unsigned long addr, +@@ -1405,7 +1390,7 @@ static int collapse_pud_page(pud_t *pud, unsigned long addr, */ static int collapse_large_pages(unsigned long addr, struct list_head *pgtables) { @@ -338,7 +304,7 @@ diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c pgd_t *pgd; p4d_t *p4d; pud_t *pud; -@@ -1384,26 +1373,24 @@ static int collapse_large_pages(unsigned long addr, struct list_head *pgtables) +@@ -1413,26 +1398,24 @@ static int collapse_large_pages(unsigned long addr, struct list_head *pgtables) addr &= PMD_MASK; @@ -370,153 +336,6 @@ diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c return collapsed; } -@@ -2004,6 +1991,7 @@ static int __change_page_attr_set_clr(struct cpa_data *cpa, int primary) - { - unsigned long numpages = cpa->numpages; - unsigned long rempages = numpages; -+ bool lock = true; - int ret = 0; - - /* -@@ -2013,6 +2001,29 @@ static int __change_page_attr_set_clr(struct cpa_data *cpa, int primary) - !cpa->force_split) - return ret; - -+ /* -+ * DEBUG_PAGEALLOC is special; it is called from any context the -+ * page-allocator is, which violates the normal cpa_lock locking -+ * rules. -+ * -+ * However, since it is part of the page-allocator, things are still -+ * properly serialized by the page-allocator locking and the fact that -+ * when a page is owned by the page-allocator, it isn't owned by -+ * anybody else. That is, you *SHOULD NOT* be calling cpa() on memory -+ * that isn't allocated. -+ * -+ * Additionally, DEBUG_PAGEALLOC ensures (per probe_page_size_mask()) -+ * that the kernel mapping is 4k pages, therefore there are no large -+ * pages to split/collapse. -+ * -+ * Furthermore, the page-allocator strictly manages pages that -+ * *exist*, avoiding pgd_lock. -+ * -+ * Therefore, it is safe to not take cpa_lock. -+ */ -+ if (debug_pagealloc_enabled() && (cpa->flags & CPA_DEBUG_PAGEALLOC)) -+ lock = false; -+ - while (rempages) { - /* - * Store the remaining nr of pages for the large page -@@ -2023,11 +2034,12 @@ static int __change_page_attr_set_clr(struct cpa_data *cpa, int primary) - if (cpa->flags & (CPA_ARRAY | CPA_PAGES_ARRAY)) - cpa->numpages = 1; - -- if (!debug_pagealloc_enabled()) -- spin_lock(&cpa_lock); -- ret = __change_page_attr(cpa, primary); -- if (!debug_pagealloc_enabled()) -- spin_unlock(&cpa_lock); -+ if (lock) { -+ guard(spinlock)(&cpa_lock); -+ ret = __change_page_attr(cpa, primary); -+ } else { -+ ret = __change_page_attr(cpa, primary); -+ } - if (ret) - goto out; - -@@ -2606,7 +2618,7 @@ int set_pages_rw(struct page *page, int numpages) - return set_memory_rw(addr, numpages); - } - --static int __set_pages_p(struct page *page, int numpages) -+static int __set_pages_p(struct page *page, int numpages, unsigned int cpa_flags) - { - unsigned long tempaddr = (unsigned long) page_address(page); - struct cpa_data cpa = { .vaddr = &tempaddr, -@@ -2614,7 +2626,7 @@ static int __set_pages_p(struct page *page, int numpages) - .numpages = numpages, - .mask_set = __pgprot(_PAGE_PRESENT | _PAGE_RW), - .mask_clr = __pgprot(0), -- .flags = CPA_NO_CHECK_ALIAS }; -+ .flags = CPA_NO_CHECK_ALIAS | cpa_flags }; - - /* - * No alias checking needed for setting present flag. otherwise, -@@ -2625,7 +2637,7 @@ static int __set_pages_p(struct page *page, int numpages) - return __change_page_attr_set_clr(&cpa, 1); - } - --static int __set_pages_np(struct page *page, int numpages) -+static int __set_pages_np(struct page *page, int numpages, unsigned int cpa_flags) - { - unsigned long tempaddr = (unsigned long) page_address(page); - struct cpa_data cpa = { .vaddr = &tempaddr, -@@ -2633,7 +2645,7 @@ static int __set_pages_np(struct page *page, int numpages) - .numpages = numpages, - .mask_set = __pgprot(0), - .mask_clr = __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY), -- .flags = CPA_NO_CHECK_ALIAS }; -+ .flags = CPA_NO_CHECK_ALIAS | cpa_flags }; - - /* - * No alias checking needed for setting not present flag. otherwise, -@@ -2646,20 +2658,20 @@ static int __set_pages_np(struct page *page, int numpages) - - int set_direct_map_invalid_noflush(struct page *page) - { -- return __set_pages_np(page, 1); -+ return __set_pages_np(page, 1, 0); - } - - int set_direct_map_default_noflush(struct page *page) - { -- return __set_pages_p(page, 1); -+ return __set_pages_p(page, 1, 0); - } - - int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid) - { - if (valid) -- return __set_pages_p(page, nr); -+ return __set_pages_p(page, nr, 0); - -- return __set_pages_np(page, nr); -+ return __set_pages_np(page, nr, 0); - } - - #ifdef CONFIG_DEBUG_PAGEALLOC -@@ -2678,15 +2690,23 @@ void __kernel_map_pages(struct page *page, int numpages, int enable) - * and hence no memory allocations during large page split. - */ - if (enable) -- __set_pages_p(page, numpages); -+ __set_pages_p(page, numpages, CPA_DEBUG_PAGEALLOC); - else -- __set_pages_np(page, numpages); -+ __set_pages_np(page, numpages, CPA_DEBUG_PAGEALLOC); - - /* -- * We should perform an IPI and flush all tlbs, -- * but that can deadlock->flush only current cpu. -- * Preemption needs to be disabled around __flush_tlb_all() due to -- * CR3 reload in __native_flush_tlb(). -+ * We should perform an IPI and flush all tlbs, but that can -+ * deadlock, settle for a local flush. -+ * -+ * Not doing a global TLB flush means that remote CPUs will retain -+ * stale TLB entries. In case of P->NP (on free) this means the remote -+ * CPUs will not take the faults, making the debug scheme less -+ * reliable. On the NP->P (on alloc) this means the remote CPUs can -+ * take a spurious fault. However spurious_kernel_fault() will observe -+ * *_present() and fix it up. -+ * -+ * Preemption needs to be disabled around __flush_tlb_all() due to CR3 -+ * reload in __native_flush_tlb(). - */ - preempt_disable(); - __flush_tlb_all(); diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c --- a/arch/x86/mm/pgtable.c +++ b/arch/x86/mm/pgtable.c diff --git a/pkgbuilds/linux-omarchy-bore/0120-tlbpull.patch.sig b/pkgbuilds/linux-omarchy-bore/0120-tlbpull.patch.sig index 54e6137..766d275 100644 Binary files a/pkgbuilds/linux-omarchy-bore/0120-tlbpull.patch.sig and b/pkgbuilds/linux-omarchy-bore/0120-tlbpull.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0130-sched-detach-tasks.patch b/pkgbuilds/linux-omarchy-bore/0130-sched-detach-tasks.patch index 9c155d1..2e16e5f 100644 --- a/pkgbuilds/linux-omarchy-bore/0130-sched-detach-tasks.patch +++ b/pkgbuilds/linux-omarchy-bore/0130-sched-detach-tasks.patch @@ -1,7 +1,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c -@@ -13464,12 +13464,15 @@ static int sched_balance_rq(int this_cpu, struct rq *this_rq, +@@ -13476,12 +13476,15 @@ static int sched_balance_rq(int this_cpu, struct rq *this_rq, * still unbalanced. ld_moved simply stays zero, so it is * correctly treated as an imbalance. */ diff --git a/pkgbuilds/linux-omarchy-bore/0130-sched-detach-tasks.patch.sig b/pkgbuilds/linux-omarchy-bore/0130-sched-detach-tasks.patch.sig index 3e089bd..dd5fd2d 100644 Binary files a/pkgbuilds/linux-omarchy-bore/0130-sched-detach-tasks.patch.sig and b/pkgbuilds/linux-omarchy-bore/0130-sched-detach-tasks.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0131-sched-avg-idle.patch b/pkgbuilds/linux-omarchy-bore/0131-sched-avg-idle.patch deleted file mode 100644 index cdb96f8..0000000 --- a/pkgbuilds/linux-omarchy-bore/0131-sched-avg-idle.patch +++ /dev/null @@ -1,23 +0,0 @@ -diff --git a/kernel/sched/core.c b/kernel/sched/core.c ---- a/kernel/sched/core.c -+++ b/kernel/sched/core.c -@@ -3751,11 +3751,17 @@ static inline void ttwu_do_wakeup(struct task_struct *p) - - void update_rq_avg_idle(struct rq *rq) - { -- u64 delta = rq_clock(rq) - rq->idle_stamp; -- u64 max = 2*rq->max_idle_balance_cost; -+ u64 idle_stamp = rq->idle_stamp; -+ u64 delta, max; -+ -+ if (!idle_stamp) -+ return; -+ -+ delta = rq_clock(rq) - idle_stamp; - - update_avg(&rq->avg_idle, delta); - -+ max = 2 * rq->max_idle_balance_cost; - if (rq->avg_idle > max) - rq->avg_idle = max; - rq->idle_stamp = 0; diff --git a/pkgbuilds/linux-omarchy-bore/0131-sched-avg-idle.patch.sig b/pkgbuilds/linux-omarchy-bore/0131-sched-avg-idle.patch.sig deleted file mode 100644 index 3407a4e..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/0131-sched-avg-idle.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/0140-sched-always-inline.patch b/pkgbuilds/linux-omarchy-bore/0140-sched-always-inline.patch index 8754792..745455c 100644 --- a/pkgbuilds/linux-omarchy-bore/0140-sched-always-inline.patch +++ b/pkgbuilds/linux-omarchy-bore/0140-sched-always-inline.patch @@ -212,7 +212,7 @@ diff --git a/include/linux/vtime.h b/include/linux/vtime.h diff --git a/kernel/sched/core.c b/kernel/sched/core.c --- a/kernel/sched/core.c +++ b/kernel/sched/core.c -@@ -5099,7 +5099,7 @@ static inline void prepare_task(struct task_struct *next) +@@ -5106,7 +5106,7 @@ static inline void prepare_task(struct task_struct *next) WRITE_ONCE(next->on_cpu, 1); } @@ -221,7 +221,7 @@ diff --git a/kernel/sched/core.c b/kernel/sched/core.c { /* * This must be the very last reference to @prev from this CPU. After -@@ -5143,7 +5143,7 @@ static void zap_balance_callbacks(struct rq *rq) +@@ -5150,7 +5150,7 @@ static void zap_balance_callbacks(struct rq *rq) rq->balance_callback = found ? &balance_push_callback : NULL; } @@ -230,7 +230,7 @@ diff --git a/kernel/sched/core.c b/kernel/sched/core.c { void (*func)(struct rq *rq); struct balance_callback *next; -@@ -5178,7 +5178,7 @@ struct balance_callback balance_push_callback = { +@@ -5185,7 +5185,7 @@ struct balance_callback balance_push_callback = { .func = balance_push, }; @@ -239,7 +239,7 @@ diff --git a/kernel/sched/core.c b/kernel/sched/core.c __splice_balance_callbacks(struct rq *rq, bool split) { struct balance_callback *head = rq->balance_callback; -@@ -5252,7 +5252,7 @@ prepare_lock_switch(struct rq *rq, struct task_struct *next, struct rq_flags *rf +@@ -5259,7 +5259,7 @@ prepare_lock_switch(struct rq *rq, struct task_struct *next, struct rq_flags *rf __acquire(__rq_lockp(this_rq())); } @@ -248,7 +248,7 @@ diff --git a/kernel/sched/core.c b/kernel/sched/core.c __releases(__rq_lockp(rq)) { /* -@@ -5286,7 +5286,7 @@ static inline void kmap_local_sched_out(void) +@@ -5293,7 +5293,7 @@ static inline void kmap_local_sched_out(void) #endif } @@ -257,7 +257,7 @@ diff --git a/kernel/sched/core.c b/kernel/sched/core.c { #ifdef CONFIG_KMAP_LOCAL if (unlikely(current->kmap_ctrl.idx)) -@@ -5340,7 +5340,7 @@ prepare_task_switch(struct rq *rq, struct task_struct *prev, +@@ -5347,7 +5347,7 @@ prepare_task_switch(struct rq *rq, struct task_struct *prev, * past. 'prev == current' is still correct but we need to recalculate this_rq * because prev may have moved to another CPU. */ diff --git a/pkgbuilds/linux-omarchy-bore/0140-sched-always-inline.patch.sig b/pkgbuilds/linux-omarchy-bore/0140-sched-always-inline.patch.sig index 290ce7c..b867df1 100644 Binary files a/pkgbuilds/linux-omarchy-bore/0140-sched-always-inline.patch.sig and b/pkgbuilds/linux-omarchy-bore/0140-sched-always-inline.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0141-sched-urgent-fixes.patch b/pkgbuilds/linux-omarchy-bore/0141-sched-urgent-fixes.patch index e59e1f1..cddb0d9 100644 --- a/pkgbuilds/linux-omarchy-bore/0141-sched-urgent-fixes.patch +++ b/pkgbuilds/linux-omarchy-bore/0141-sched-urgent-fixes.patch @@ -1,21 +1,7 @@ -diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c ---- a/kernel/sched/deadline.c -+++ b/kernel/sched/deadline.c -@@ -3026,8 +3026,8 @@ static struct task_struct *pick_next_pushable_dl_task(struct rq *rq) - next_node = rb_first_cached(&rq->dl.pushable_dl_tasks_root); - while (next_node) { - i = __node_2_pdl(next_node); -- /* make sure task isn't on_cpu (possible with proxy-exec) */ -- if (!task_on_cpu(rq, i)) { -+ /* skip tasks that cannot be migrated */ -+ if (!task_on_cpu(rq, i) && !is_migration_disabled(i)) { - p = i; - break; - } diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c -@@ -10691,17 +10691,40 @@ static enum llc_mig can_migrate_llc(int src_cpu, int dst_cpu, +@@ -10701,17 +10701,40 @@ static enum llc_mig can_migrate_llc(int src_cpu, int dst_cpu, return mig_llc; } @@ -58,7 +44,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c mm = p->mm; if (!mm) return mig_unrestricted; -@@ -10758,6 +10781,14 @@ alb_break_llc(struct lb_env *env) +@@ -10768,6 +10791,14 @@ alb_break_llc(struct lb_env *env) unsigned long util = 0; struct task_struct *cur; @@ -73,7 +59,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c if (env->src_rq->nr_running <= 1) return true; -@@ -10765,7 +10796,8 @@ alb_break_llc(struct lb_env *env) +@@ -10775,7 +10806,8 @@ alb_break_llc(struct lb_env *env) if (cur && cur->sched_class == &fair_sched_class) util = task_util(cur); @@ -83,7 +69,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c util, false) == mig_forbid) return true; } -@@ -10805,8 +10837,7 @@ static bool migrate_degrades_llc(struct task_struct *p, struct lb_env *env) +@@ -10815,8 +10847,7 @@ static bool migrate_degrades_llc(struct task_struct *p, struct lb_env *env) READ_ONCE(p->preferred_llc) != llc_id(env->dst_cpu)) return true; @@ -93,7 +79,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c return false; return true; -@@ -11869,6 +11900,15 @@ static inline bool llc_balance(struct lb_env *env, struct sg_lb_stats *sgs, +@@ -11879,6 +11910,15 @@ static inline bool llc_balance(struct lb_env *env, struct sg_lb_stats *sgs, if (env->sd->flags & SD_SHARE_LLC) return false; @@ -109,17 +95,3 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c /* * Skip cache aware tagging if nr_balanced_failed is sufficiently high. * Threshold of cache_nice_tries is set to 1 higher than nr_balance_failed -diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c ---- a/kernel/sched/rt.c -+++ b/kernel/sched/rt.c -@@ -1871,8 +1871,8 @@ static struct task_struct *pick_next_pushable_task(struct rq *rq) - return NULL; - - plist_for_each_entry(i, head, pushable_tasks) { -- /* make sure task isn't on_cpu (possible with proxy-exec) */ -- if (!task_on_cpu(rq, i)) { -+ /* skip tasks that cannot be migrated */ -+ if (!task_on_cpu(rq, i) && !is_migration_disabled(i)) { - p = i; - break; - } diff --git a/pkgbuilds/linux-omarchy-bore/0141-sched-urgent-fixes.patch.sig b/pkgbuilds/linux-omarchy-bore/0141-sched-urgent-fixes.patch.sig index 89bb69e..81f23cb 100644 Binary files a/pkgbuilds/linux-omarchy-bore/0141-sched-urgent-fixes.patch.sig and b/pkgbuilds/linux-omarchy-bore/0141-sched-urgent-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0142-sched-itmt-no-debugfs-dependency.patch b/pkgbuilds/linux-omarchy-bore/0142-sched-itmt-no-debugfs-dependency.patch deleted file mode 100644 index a0f39ea..0000000 --- a/pkgbuilds/linux-omarchy-bore/0142-sched-itmt-no-debugfs-dependency.patch +++ /dev/null @@ -1,24 +0,0 @@ -diff --git a/arch/x86/kernel/itmt.c b/arch/x86/kernel/itmt.c ---- a/arch/x86/kernel/itmt.c -+++ b/arch/x86/kernel/itmt.c -@@ -110,18 +110,14 @@ int sched_set_itmt_support(void) - arch_debugfs_dir, - &sysctl_sched_itmt_enabled, - &dfs_sched_itmt_fops); -- if (IS_ERR_OR_NULL(dfs_sched_itmt)) { -+ if (IS_ERR(dfs_sched_itmt)) - dfs_sched_itmt = NULL; -- return -ENOMEM; -- } - - dfs_sched_core_prio = debugfs_create_file("sched_core_priority", 0644, - arch_debugfs_dir, NULL, - &sched_core_priority_fops); -- if (IS_ERR_OR_NULL(dfs_sched_core_prio)) { -+ if (IS_ERR(dfs_sched_core_prio)) - dfs_sched_core_prio = NULL; -- return -ENOMEM; -- } - - sched_itmt_capable = true; - diff --git a/pkgbuilds/linux-omarchy-bore/0142-sched-itmt-no-debugfs-dependency.patch.sig b/pkgbuilds/linux-omarchy-bore/0142-sched-itmt-no-debugfs-dependency.patch.sig deleted file mode 100644 index d64847c..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/0142-sched-itmt-no-debugfs-dependency.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/0143-sched-hybrid-cluster-balancing.patch b/pkgbuilds/linux-omarchy-bore/0143-sched-hybrid-cluster-balancing.patch index be80f96..2131bc4 100644 --- a/pkgbuilds/linux-omarchy-bore/0143-sched-hybrid-cluster-balancing.patch +++ b/pkgbuilds/linux-omarchy-bore/0143-sched-hybrid-cluster-balancing.patch @@ -14,22 +14,22 @@ diff --git a/include/linux/sched/sd_flags.h b/include/linux/sched/sd_flags.h diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c -@@ -12028,10 +12028,25 @@ static inline void update_sg_lb_stats(struct lb_env *env, +@@ -12038,12 +12038,25 @@ static inline void update_sg_lb_stats(struct lb_env *env, continue; if (sd_flags & SD_ASYM_CPUCAPACITY) { - /* Check for a misfit task on the cpu */ - if (sgs->group_misfit_task_load < rq->misfit_task_load) { - sgs->group_misfit_task_load = rq->misfit_task_load; -- *sg_overloaded = 1; +- + if (rq->misfit_task_load) { + /* + * Always mark the root domain overloaded so big + * CPUs can pick up misfit tasks via newly idle + * balance. + */ -+ if (balancing_at_rd) -+ *sg_overloaded = 1; + if (balancing_at_rd) + *sg_overloaded = 1; + + /* + * Only account misfit load if @dst_cpu can @@ -44,43 +44,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c } } else if (env->idle && sched_reduced_capacity(rq, env->sd)) { /* Check for a task running on a CPU with reduced capacity */ -@@ -12110,6 +12125,17 @@ static bool update_sd_pick_busiest(struct lb_env *env, - sds->local_stat.group_type != group_has_spare)) - return false; - -+ /* -+ * Candidate sg has no more than one task per CPU and has higher -+ * per-CPU capacity. Migrating tasks to less capable CPUs may harm -+ * throughput. Maximize throughput, power/energy consequences are not -+ * considered. -+ */ -+ if ((env->sd->flags & SD_ASYM_CPUCAPACITY) && -+ (sgs->group_type <= group_fully_busy) && -+ (capacity_greater(sg->sgc->min_capacity, capacity_of(env->dst_cpu)))) -+ return false; -+ - if (sgs->group_type > busiest->group_type) - return true; - -@@ -12216,17 +12242,6 @@ static bool update_sd_pick_busiest(struct lb_env *env, - break; - } - -- /* -- * Candidate sg has no more than one task per CPU and has higher -- * per-CPU capacity. Migrating tasks to less capable CPUs may harm -- * throughput. Maximize throughput, power/energy consequences are not -- * considered. -- */ -- if ((env->sd->flags & SD_ASYM_CPUCAPACITY) && -- (sgs->group_type <= group_fully_busy) && -- (capacity_greater(sg->sgc->min_capacity, capacity_of(env->dst_cpu)))) -- return false; -- - return true; - } - -@@ -13144,9 +13159,24 @@ static struct rq *sched_balance_find_src_rq(struct lb_env *env, +@@ -13156,9 +13169,24 @@ static struct rq *sched_balance_find_src_rq(struct lb_env *env, * average load. */ if (env->sd->flags & SD_ASYM_CPUCAPACITY && diff --git a/pkgbuilds/linux-omarchy-bore/0143-sched-hybrid-cluster-balancing.patch.sig b/pkgbuilds/linux-omarchy-bore/0143-sched-hybrid-cluster-balancing.patch.sig index 91abc02..7ca830c 100644 Binary files a/pkgbuilds/linux-omarchy-bore/0143-sched-hybrid-cluster-balancing.patch.sig and b/pkgbuilds/linux-omarchy-bore/0143-sched-hybrid-cluster-balancing.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0144-sched-nohz-idle-core.patch b/pkgbuilds/linux-omarchy-bore/0144-sched-nohz-idle-core.patch index f3af36b..7a22780 100644 --- a/pkgbuilds/linux-omarchy-bore/0144-sched-nohz-idle-core.patch +++ b/pkgbuilds/linux-omarchy-bore/0144-sched-nohz-idle-core.patch @@ -1,7 +1,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c -@@ -14050,29 +14050,62 @@ static inline int on_null_domain(struct rq *rq) +@@ -14060,29 +14060,62 @@ static inline int on_null_domain(struct rq *rq) */ static inline int find_new_ilb(void) { diff --git a/pkgbuilds/linux-omarchy-bore/0144-sched-nohz-idle-core.patch.sig b/pkgbuilds/linux-omarchy-bore/0144-sched-nohz-idle-core.patch.sig index a6bbb0d..d956e49 100644 Binary files a/pkgbuilds/linux-omarchy-bore/0144-sched-nohz-idle-core.patch.sig and b/pkgbuilds/linux-omarchy-bore/0144-sched-nohz-idle-core.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0200-idle.patch b/pkgbuilds/linux-omarchy-bore/0200-idle.patch deleted file mode 100644 index dc499e5..0000000 --- a/pkgbuilds/linux-omarchy-bore/0200-idle.patch +++ /dev/null @@ -1,61 +0,0 @@ -diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c ---- a/drivers/idle/intel_idle.c -+++ b/drivers/idle/intel_idle.c -@@ -53,6 +53,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -2697,6 +2698,9 @@ static void __init cmdline_table_adjust(struct cpuidle_driver *drv) - pr_info("Failed to adjust C-states with data from 'intel_idle.table'\n"); - } - -+#define INTEL_IDLE_INIT_QOS 20 -+static struct pm_qos_request qos_req __initdata; -+ - static int __init intel_idle_init(void) - { - const struct x86_cpu_id *id; -@@ -2766,6 +2770,13 @@ static int __init intel_idle_init(void) - if (retval) - pr_warn("failed to initialized sysfs"); - -+ /* -+ * Some platforms, in particular the Intel S1200BTL motherboard, have a -+ * problem with using package idle states too early, so prevent that -+ * from taking place until the device_initcall() phase is over. -+ */ -+ cpu_latency_qos_add_request(&qos_req, INTEL_IDLE_INIT_QOS); -+ - retval = cpuidle_register_driver(&intel_idle_driver); - if (retval) { - struct cpuidle_driver *drv = cpuidle_get_driver(); -@@ -2790,6 +2801,9 @@ static int __init intel_idle_init(void) - intel_idle_cpuidle_devices_uninit(); - cpuidle_unregister_driver(&intel_idle_driver); - init_driver_fail: -+ if (cpu_latency_qos_request_active((&qos_req))) -+ cpu_latency_qos_remove_request(&qos_req); -+ - intel_idle_sysfs_uninit(); - free_percpu(intel_idle_cpuidle_devices); - return retval; -@@ -2797,6 +2811,15 @@ static int __init intel_idle_init(void) - } - subsys_initcall_sync(intel_idle_init); - -+static int __init intel_idle_init_complete(void) -+{ -+ if (cpu_latency_qos_request_active((&qos_req))) -+ cpu_latency_qos_remove_request(&qos_req); -+ -+ return 0; -+} -+device_initcall_sync(intel_idle_init_complete); -+ - /* - * We are not really modular, but we used to support that. Meaning we also - * support "intel_idle.max_cstate=..." at boot and also a read-only export of diff --git a/pkgbuilds/linux-omarchy-bore/0200-idle.patch.sig b/pkgbuilds/linux-omarchy-bore/0200-idle.patch.sig deleted file mode 100644 index 20c9157..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/0200-idle.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/0210-pm-updates.patch b/pkgbuilds/linux-omarchy-bore/0210-pm-updates.patch new file mode 100644 index 0000000..5110d06 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0210-pm-updates.patch @@ -0,0 +1,2365 @@ +diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c +--- a/drivers/acpi/acpi_processor.c ++++ b/drivers/acpi/acpi_processor.c +@@ -994,3 +994,357 @@ end: + } + EXPORT_SYMBOL_NS_GPL(acpi_processor_evaluate_cst, "ACPI_PROCESSOR_IDLE"); + #endif /* CONFIG_ACPI_PROCESSOR_CSTATE */ ++ ++#ifdef CONFIG_ACPI_PROCESSOR_IDLE ++struct acpi_lpi_states_array { ++ unsigned int size; ++ unsigned int composite_states_size; ++ struct acpi_lpi_state *entries; ++ struct acpi_lpi_state *composite_states[ACPI_PROCESSOR_MAX_POWER]; ++}; ++ ++static int obj_get_integer(union acpi_object *obj, u32 *value) ++{ ++ if (obj->type != ACPI_TYPE_INTEGER) ++ return -EINVAL; ++ ++ *value = obj->integer.value; ++ return 0; ++} ++ ++#define lpi_state_debug(handle, message, state_idx) \ ++ acpi_handle_debug(handle, message " for _LPI state %u\n", state_idx) ++ ++static void process_lpi_state_package(union acpi_object *lpi_pkg, ++ struct acpi_lpi_state *lpi_state, ++ acpi_handle handle, ++ unsigned int state_idx, bool strict) ++{ ++ union acpi_object *lpi_pkg_elem, *obj; ++ ++ if (lpi_pkg->type != ACPI_TYPE_PACKAGE || lpi_pkg->package.count < 7) ++ return; ++ ++ lpi_pkg_elem = lpi_pkg->package.elements; ++ ++ /* Get the entry method first and skip the state if that fails. */ ++ obj = &lpi_pkg_elem[6]; ++ if (obj->type == ACPI_TYPE_BUFFER) { ++ struct acpi_power_register *reg; ++ ++ if (obj->buffer.length < sizeof(*reg)) { ++ lpi_state_debug(handle, "Invalid register data", state_idx); ++ return; ++ } ++ ++ reg = (struct acpi_power_register *)obj->buffer.pointer; ++ if (reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) { ++ lpi_state_debug(handle, "Unsupported entry method", state_idx); ++ return; ++ } ++ ++ lpi_state->entry_method = ACPI_CSTATE_FFH; ++ lpi_state->address = reg->address; ++ } else if (obj->type == ACPI_TYPE_INTEGER) { ++ lpi_state->entry_method = ACPI_CSTATE_INTEGER; ++ lpi_state->address = obj->integer.value; ++ } else { ++ lpi_state_debug(handle, "Invalid entry method", state_idx); ++ return; ++ } ++ ++ if (obj_get_integer(&lpi_pkg_elem[0], &lpi_state->min_residency)) { ++ if (strict) { ++ lpi_state_debug(handle, "No min. residency", state_idx); ++ return; ++ } ++ ++ lpi_state_debug(handle, "Assuming 10 us min. residency", state_idx); ++ lpi_state->min_residency = 10; ++ } ++ ++ if (obj_get_integer(&lpi_pkg_elem[1], &lpi_state->wake_latency)) { ++ if (strict) { ++ lpi_state_debug(handle, "No wake latency", state_idx); ++ return; ++ } ++ ++ lpi_state_debug(handle, "Assuming 10 us wake latency", state_idx); ++ lpi_state->wake_latency = 10; ++ } ++ ++ if (obj_get_integer(&lpi_pkg_elem[2], &lpi_state->flags)) ++ lpi_state->flags = 0; ++ ++ if (obj_get_integer(&lpi_pkg_elem[3], &lpi_state->arch_flags)) ++ lpi_state->arch_flags = 0; ++ ++ if (obj_get_integer(&lpi_pkg_elem[4], &lpi_state->res_cnt_freq)) ++ lpi_state->res_cnt_freq = 1; ++ ++ if (obj_get_integer(&lpi_pkg_elem[5], &lpi_state->enable_parent_state)) ++ lpi_state->enable_parent_state = 0; ++ ++ /* Skip elements [7-8] i.e. Residency/Usage counters. */ ++ ++ /* ++ * Avoid out-of-bounds access if the size of the package is less than ++ * expected. ++ */ ++ if (lpi_pkg->package.count < 10) ++ return; ++ ++ obj = &lpi_pkg_elem[9]; ++ if (obj->type == ACPI_TYPE_STRING) ++ strscpy(lpi_state->desc, obj->string.pointer, ACPI_CX_DESC_LEN); ++} ++ ++static int acpi_processor_evaluate_lpi(acpi_handle handle, ++ struct acpi_lpi_states_array *info, ++ bool strict) ++{ ++ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; ++ union acpi_object *lpi_data, *lpi_pkg; ++ unsigned int lpi_pkg_count, state_idx; ++ struct acpi_lpi_state *lpi_state; ++ acpi_status status; ++ int ret = 0; ++ ++ status = acpi_evaluate_object(handle, "_LPI", NULL, &buffer); ++ if (ACPI_FAILURE(status)) { ++ acpi_handle_debug(handle, "No _LPI, giving up\n"); ++ return -ENODEV; ++ } ++ ++ lpi_data = buffer.pointer; ++ ++ /* There must be at least 4 elements = 3 elements + 1 package */ ++ if (!lpi_data || lpi_data->type != ACPI_TYPE_PACKAGE || ++ lpi_data->package.count < 4) { ++ acpi_handle_debug(handle, "Not enough elements in _LPI\n"); ++ ret = -ENODATA; ++ goto end; ++ } ++ ++ lpi_pkg_count = lpi_data->package.elements[2].integer.value; ++ ++ /* Validate number of power states. */ ++ if (!lpi_pkg_count || lpi_pkg_count != lpi_data->package.count - 3) { ++ acpi_handle_debug(handle, "Invalid _LPI state count\n"); ++ ret = -ENODATA; ++ goto end; ++ } ++ ++ lpi_state = kzalloc_objs(*lpi_state, lpi_pkg_count); ++ if (!lpi_state) { ++ ret = -ENOMEM; ++ goto end; ++ } ++ ++ info->size = lpi_pkg_count; ++ info->entries = lpi_state; ++ ++ /* _LPI State packages start at index 3. */ ++ lpi_pkg = &lpi_data->package.elements[3]; ++ ++ for (state_idx = 1; state_idx <= lpi_pkg_count; state_idx++) { ++ lpi_state->index = state_idx; ++ process_lpi_state_package(lpi_pkg++, lpi_state++, handle, ++ state_idx, strict); ++ } ++ ++ acpi_handle_debug(handle, "Found %u power states\n", lpi_pkg_count); ++end: ++ kfree(buffer.pointer); ++ return ret; ++} ++ ++/** ++ * combine_lpi_states - combine local and parent LPI states to form a composite LPI state ++ * ++ * @local: local LPI state ++ * @parent: parent LPI state ++ * @result: composite LPI state ++ */ ++static bool combine_lpi_states(struct acpi_lpi_state *local, ++ struct acpi_lpi_state *parent, ++ struct acpi_lpi_state *result) ++{ ++ if (parent->entry_method == ACPI_CSTATE_INTEGER) { ++ if (!parent->address) /* 0 means autopromotable */ ++ return false; ++ result->address = local->address + parent->address; ++ } else { ++ result->address = parent->address; ++ } ++ ++ result->min_residency = max(local->min_residency, parent->min_residency); ++ result->wake_latency = local->wake_latency + parent->wake_latency; ++ result->enable_parent_state = parent->enable_parent_state; ++ result->entry_method = local->entry_method; ++ ++ result->flags = parent->flags; ++ result->arch_flags = parent->arch_flags; ++ result->index = parent->index; ++ ++ scnprintf(result->desc, ACPI_CX_DESC_LEN, "%s+%s", local->desc, parent->desc); ++ return true; ++} ++ ++#define ACPI_LPI_STATE_FLAGS_ENABLED BIT(0) ++ ++static void stash_composite_state(struct acpi_lpi_states_array *curr_level, ++ struct acpi_lpi_state *t) ++{ ++ curr_level->composite_states[curr_level->composite_states_size++] = t; ++} ++ ++static bool too_many_states(acpi_handle handle, unsigned int state_count) ++{ ++ if (state_count < ACPI_PROCESSOR_MAX_POWER) ++ return false; ++ ++ acpi_handle_info(handle, "No space for more _LPI states than %d\n", ++ ACPI_PROCESSOR_MAX_POWER); ++ return true; ++} ++ ++static unsigned int flatten_lpi_states(acpi_handle handle, ++ struct acpi_lpi_state *lpi_states, ++ unsigned int state_count, ++ struct acpi_lpi_states_array *curr, ++ struct acpi_lpi_states_array *prev) ++{ ++ struct acpi_lpi_state *parent_lpi = curr->entries; ++ unsigned int j; ++ ++ /* ++ * Combine each of the "raw" _LPI states from the current (processor ++ * container) level with all of the composite _LPI states from the ++ * previous (processor or processor container) level. ++ */ ++ for (j = 0; j < curr->size; j++, parent_lpi++) { ++ struct acpi_lpi_state *flpi; ++ int i; ++ ++ if (!(parent_lpi->flags & ACPI_LPI_STATE_FLAGS_ENABLED)) ++ continue; ++ ++ if (too_many_states(handle, state_count)) ++ break; ++ ++ flpi = &lpi_states[state_count]; ++ ++ for (i = 0; i < prev->composite_states_size; i++) { ++ struct acpi_lpi_state *local_lpi = prev->composite_states[i]; ++ ++ if (parent_lpi->index > local_lpi->enable_parent_state) ++ continue; ++ ++ if (!combine_lpi_states(local_lpi, parent_lpi, flpi)) ++ continue; ++ ++ stash_composite_state(curr, flpi); ++ state_count++; ++ flpi++; ++ ++ if (state_count >= ACPI_PROCESSOR_MAX_POWER) ++ break; ++ } ++ } ++ ++ return state_count; ++} ++ ++int acpi_processor_extract_lpi_info(acpi_handle pr_handle, ++ struct acpi_processor_power *pr_power, ++ bool strict) ++{ ++ struct acpi_lpi_states_array info[2], *prev, *curr; ++ acpi_handle handle = pr_handle; ++ unsigned int state_count = 0; ++ unsigned int i; ++ int ret; ++ ++ if (!osc_pc_lpi_support_confirmed) ++ return -EOPNOTSUPP; ++ ++ curr = &info[0]; ++ curr->composite_states_size = 0; ++ ++ ret = acpi_processor_evaluate_lpi(handle, curr, strict); ++ if (ret) ++ return ret; ++ ++ /* Copy all of the usable first-level states to power.lpi_states[]. */ ++ for (i = 0; i < curr->size; i++) { ++ struct acpi_lpi_state *lpi = &curr->entries[i]; ++ struct acpi_lpi_state *flpi; ++ ++ /* ++ * Skip states that are not enabled or have an inadequate entry ++ * method for this level. ++ */ ++ if (!(lpi->flags & ACPI_LPI_STATE_FLAGS_ENABLED) || ++ lpi->entry_method == ACPI_CSTATE_INTEGER) ++ continue; ++ ++ if (too_many_states(pr_handle, state_count)) ++ break; ++ ++ flpi = &pr_power->lpi_states[state_count++]; ++ memcpy(flpi, lpi, sizeof(*lpi)); ++ stash_composite_state(curr, flpi); ++ } ++ ++ kfree(curr->entries); ++ ++ /* ++ * If there are no _LPI states at the first level, there are no _LPI ++ * states at all. ++ */ ++ if (!state_count) ++ return -ENODATA; ++ ++ prev = curr; ++ curr = &info[1]; ++ ++ for (;;) { ++ struct acpi_lpi_states_array *tmp; ++ struct acpi_device *d; ++ ++ if (ACPI_FAILURE(acpi_get_parent(handle, &handle))) ++ break; ++ ++ d = acpi_fetch_acpi_dev(handle); ++ if (!d) ++ break; ++ ++ if (strcmp(acpi_device_hid(d), ACPI_PROCESSOR_CONTAINER_HID)) ++ break; ++ ++ curr->composite_states_size = 0; ++ ++ ret = acpi_processor_evaluate_lpi(handle, curr, strict); ++ if (ret) ++ break; ++ ++ /* flatten all the LPI states in this level of hierarchy */ ++ state_count = flatten_lpi_states(pr_handle, pr_power->lpi_states, ++ state_count, curr, prev); ++ ++ kfree(curr->entries); ++ ++ tmp = prev, prev = curr, curr = tmp; ++ } ++ ++ /* reset the index after flattening */ ++ for (i = 0; i < state_count; i++) ++ pr_power->lpi_states[i].index = i; ++ ++ pr_power->count = state_count; ++ ++ return 0; ++} ++EXPORT_SYMBOL_NS_GPL(acpi_processor_extract_lpi_info, "ACPI_PROCESSOR_IDLE"); ++#endif /* CONFIG_ACPI_PROCESSOR_IDLE */ +diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c +--- a/drivers/acpi/processor_idle.c ++++ b/drivers/acpi/processor_idle.c +@@ -853,237 +853,6 @@ static int acpi_processor_setup_cstates(struct acpi_processor *pr) + + #endif /* CONFIG_ACPI_PROCESSOR_CSTATE */ + +-struct acpi_lpi_states_array { +- unsigned int size; +- unsigned int composite_states_size; +- struct acpi_lpi_state *entries; +- struct acpi_lpi_state *composite_states[ACPI_PROCESSOR_MAX_POWER]; +-}; +- +-static int obj_get_integer(union acpi_object *obj, u32 *value) +-{ +- if (obj->type != ACPI_TYPE_INTEGER) +- return -EINVAL; +- +- *value = obj->integer.value; +- return 0; +-} +- +-static int acpi_processor_evaluate_lpi(acpi_handle handle, +- struct acpi_lpi_states_array *info) +-{ +- acpi_status status; +- int ret = 0; +- int pkg_count, state_idx = 1, loop; +- struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; +- union acpi_object *lpi_data; +- struct acpi_lpi_state *lpi_state; +- +- status = acpi_evaluate_object(handle, "_LPI", NULL, &buffer); +- if (ACPI_FAILURE(status)) { +- acpi_handle_debug(handle, "No _LPI, giving up\n"); +- return -ENODEV; +- } +- +- lpi_data = buffer.pointer; +- +- /* There must be at least 4 elements = 3 elements + 1 package */ +- if (!lpi_data || lpi_data->type != ACPI_TYPE_PACKAGE || +- lpi_data->package.count < 4) { +- pr_debug("not enough elements in _LPI\n"); +- ret = -ENODATA; +- goto end; +- } +- +- pkg_count = lpi_data->package.elements[2].integer.value; +- +- /* Validate number of power states. */ +- if (pkg_count < 1 || pkg_count != lpi_data->package.count - 3) { +- pr_debug("count given by _LPI is not valid\n"); +- ret = -ENODATA; +- goto end; +- } +- +- lpi_state = kzalloc_objs(*lpi_state, pkg_count); +- if (!lpi_state) { +- ret = -ENOMEM; +- goto end; +- } +- +- info->size = pkg_count; +- info->entries = lpi_state; +- +- /* LPI States start at index 3 */ +- for (loop = 3; state_idx <= pkg_count; loop++, state_idx++, lpi_state++) { +- union acpi_object *element, *pkg_elem, *obj; +- +- element = &lpi_data->package.elements[loop]; +- if (element->type != ACPI_TYPE_PACKAGE || element->package.count < 7) +- continue; +- +- pkg_elem = element->package.elements; +- +- obj = pkg_elem + 6; +- if (obj->type == ACPI_TYPE_BUFFER) { +- struct acpi_power_register *reg; +- +- if (obj->buffer.length < sizeof(*reg)) { +- acpi_handle_debug(handle, +- "Invalid register data for _LPI state %d\n", +- state_idx); +- continue; +- } +- +- reg = (struct acpi_power_register *)obj->buffer.pointer; +- if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO && +- reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) +- continue; +- +- lpi_state->address = reg->address; +- lpi_state->entry_method = +- reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE ? +- ACPI_CSTATE_FFH : ACPI_CSTATE_SYSTEMIO; +- } else if (obj->type == ACPI_TYPE_INTEGER) { +- lpi_state->entry_method = ACPI_CSTATE_INTEGER; +- lpi_state->address = obj->integer.value; +- } else { +- pr_debug("Entry method of state-%d is invalid, disable it.\n", +- state_idx); +- continue; +- } +- +- lpi_state->index = state_idx; +- if (obj_get_integer(pkg_elem + 0, &lpi_state->min_residency)) { +- pr_debug("No min. residency found, assuming 10 us\n"); +- lpi_state->min_residency = 10; +- } +- +- if (obj_get_integer(pkg_elem + 1, &lpi_state->wake_latency)) { +- pr_debug("No wakeup residency found, assuming 10 us\n"); +- lpi_state->wake_latency = 10; +- } +- +- if (obj_get_integer(pkg_elem + 2, &lpi_state->flags)) +- lpi_state->flags = 0; +- +- if (obj_get_integer(pkg_elem + 3, &lpi_state->arch_flags)) +- lpi_state->arch_flags = 0; +- +- if (obj_get_integer(pkg_elem + 4, &lpi_state->res_cnt_freq)) +- lpi_state->res_cnt_freq = 1; +- +- if (obj_get_integer(pkg_elem + 5, &lpi_state->enable_parent_state)) +- lpi_state->enable_parent_state = 0; +- +- /* Skip elements [7-8] i.e. Residency/Usage counters. */ +- +- /* +- * Avoid out-of-bounds access if the size of the package is less +- * than expected. +- */ +- if (element->package.count < 10) +- continue; +- +- obj = pkg_elem + 9; +- if (obj->type == ACPI_TYPE_STRING) +- strscpy(lpi_state->desc, obj->string.pointer, +- ACPI_CX_DESC_LEN); +- } +- +- acpi_handle_debug(handle, "Found %d power states\n", state_idx); +-end: +- kfree(buffer.pointer); +- return ret; +-} +- +-/** +- * combine_lpi_states - combine local and parent LPI states to form a composite LPI state +- * +- * @local: local LPI state +- * @parent: parent LPI state +- * @result: composite LPI state +- */ +-static bool combine_lpi_states(struct acpi_lpi_state *local, +- struct acpi_lpi_state *parent, +- struct acpi_lpi_state *result) +-{ +- if (parent->entry_method == ACPI_CSTATE_INTEGER) { +- if (!parent->address) /* 0 means autopromotable */ +- return false; +- result->address = local->address + parent->address; +- } else { +- result->address = parent->address; +- } +- +- result->min_residency = max(local->min_residency, parent->min_residency); +- result->wake_latency = local->wake_latency + parent->wake_latency; +- result->enable_parent_state = parent->enable_parent_state; +- result->entry_method = local->entry_method; +- +- result->flags = parent->flags; +- result->arch_flags = parent->arch_flags; +- result->index = parent->index; +- +- scnprintf(result->desc, ACPI_CX_DESC_LEN, "%s+%s", local->desc, parent->desc); +- return true; +-} +- +-#define ACPI_LPI_STATE_FLAGS_ENABLED BIT(0) +- +-static void stash_composite_state(struct acpi_lpi_states_array *curr_level, +- struct acpi_lpi_state *t) +-{ +- curr_level->composite_states[curr_level->composite_states_size++] = t; +-} +- +-static unsigned int flatten_lpi_states(struct acpi_processor *pr, +- unsigned int flat_state_cnt, +- struct acpi_lpi_states_array *curr_level, +- struct acpi_lpi_states_array *prev_level) +-{ +- int i, j, state_count = curr_level->size; +- struct acpi_lpi_state *p, *t = curr_level->entries; +- +- curr_level->composite_states_size = 0; +- for (j = 0; j < state_count; j++, t++) { +- struct acpi_lpi_state *flpi; +- +- if (!(t->flags & ACPI_LPI_STATE_FLAGS_ENABLED)) +- continue; +- +- if (flat_state_cnt >= ACPI_PROCESSOR_MAX_POWER) { +- pr_warn("Limiting number of LPI states to max (%d)\n", +- ACPI_PROCESSOR_MAX_POWER); +- pr_warn("Please increase ACPI_PROCESSOR_MAX_POWER if needed.\n"); +- break; +- } +- +- flpi = &pr->power.lpi_states[flat_state_cnt]; +- +- if (!prev_level) { /* leaf/processor node */ +- memcpy(flpi, t, sizeof(*t)); +- stash_composite_state(curr_level, flpi); +- flat_state_cnt++; +- continue; +- } +- +- for (i = 0; i < prev_level->composite_states_size; i++) { +- p = prev_level->composite_states[i]; +- if (t->index <= p->enable_parent_state && +- combine_lpi_states(p, t, flpi)) { +- stash_composite_state(curr_level, flpi); +- flat_state_cnt++; +- flpi++; +- if (flat_state_cnt >= ACPI_PROCESSOR_MAX_POWER) +- break; +- } +- } +- } +- +- kfree(curr_level->entries); +- return flat_state_cnt; +-} +- + int __weak acpi_processor_ffh_lpi_probe(unsigned int cpu) + { + return -EOPNOTSUPP; +@@ -1091,64 +860,16 @@ int __weak acpi_processor_ffh_lpi_probe(unsigned int cpu) + + static int acpi_processor_get_lpi_info(struct acpi_processor *pr) + { +- int ret, i; +- acpi_status status; +- acpi_handle handle = pr->handle, pr_ahandle; +- struct acpi_device *d = NULL; +- struct acpi_lpi_states_array info[2], *tmp, *prev, *curr; +- unsigned int state_count; ++ int ret; + + /* make sure our architecture has support */ + ret = acpi_processor_ffh_lpi_probe(pr->id); + if (ret == -EOPNOTSUPP) + return ret; + +- if (!osc_pc_lpi_support_confirmed) +- return -EOPNOTSUPP; +- +- if (!acpi_has_method(handle, "_LPI")) +- return -EINVAL; +- +- prev = &info[0]; +- curr = &info[1]; +- handle = pr->handle; +- ret = acpi_processor_evaluate_lpi(handle, prev); ++ ret = acpi_processor_extract_lpi_info(pr->handle, &pr->power, false); + if (ret) + return ret; +- state_count = flatten_lpi_states(pr, 0, prev, NULL); +- +- status = acpi_get_parent(handle, &pr_ahandle); +- while (ACPI_SUCCESS(status)) { +- d = acpi_fetch_acpi_dev(pr_ahandle); +- if (!d) +- break; +- +- handle = pr_ahandle; +- +- if (strcmp(acpi_device_hid(d), ACPI_PROCESSOR_CONTAINER_HID)) +- break; +- +- /* can be optional ? */ +- if (!acpi_has_method(handle, "_LPI")) +- break; +- +- ret = acpi_processor_evaluate_lpi(handle, curr); +- if (ret) +- break; +- +- /* flatten all the LPI states in this level of hierarchy */ +- state_count = flatten_lpi_states(pr, state_count, curr, prev); +- +- tmp = prev, prev = curr, curr = tmp; +- +- status = acpi_get_parent(handle, &pr_ahandle); +- } +- +- /* reset the index after flattening */ +- for (i = 0; i < state_count; i++) +- pr->power.lpi_states[i].index = i; +- +- pr->power.count = state_count; + + /* Tell driver that _LPI is supported. */ + pr->flags.has_lpi = 1; +diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c +--- a/drivers/base/power/main.c ++++ b/drivers/base/power/main.c +@@ -41,6 +41,9 @@ + #include "../base.h" + #include "power.h" + ++#undef MODULE_PARAM_PREFIX ++#define MODULE_PARAM_PREFIX "pm." ++ + typedef int (*pm_callback_t)(struct device *); + + /* +@@ -532,6 +535,11 @@ module_param(dpm_watchdog_all_cpu_backtrace, bool, 0644); + MODULE_PARM_DESC(dpm_watchdog_all_cpu_backtrace, + "Backtrace all CPUs on DPM watchdog timeout"); + ++static bool __read_mostly dpm_watchdog_enabled = ++ IS_ENABLED(CONFIG_DPM_WATCHDOG_ENABLED); ++module_param(dpm_watchdog_enabled, bool, 0644); ++MODULE_PARM_DESC(dpm_watchdog_enabled, "Enable DPM watchdog"); ++ + static unsigned int __read_mostly dpm_watchdog_timeout = CONFIG_DPM_WATCHDOG_TIMEOUT; + static unsigned int __read_mostly dpm_watchdog_warning_timeout = + CONFIG_DPM_WATCHDOG_WARNING_TIMEOUT; +@@ -627,6 +635,9 @@ static void dpm_watchdog_set(struct dpm_watchdog *wd, struct device *dev) + { + struct timer_list *timer = &wd->timer; + ++ if (!dpm_watchdog_enabled) ++ return; ++ + wd->dev = dev; + wd->tsk = current; + wd->fatal = dpm_watchdog_timeout == dpm_watchdog_warning_timeout; +@@ -645,6 +656,9 @@ static void dpm_watchdog_clear(struct dpm_watchdog *wd) + { + struct timer_list *timer = &wd->timer; + ++ if (!dpm_watchdog_enabled) ++ return; ++ + timer_delete_sync(timer); + timer_destroy_on_stack(timer); + } +diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c +--- a/drivers/base/power/runtime.c ++++ b/drivers/base/power/runtime.c +@@ -469,9 +469,6 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev) + if (retval == -EACCES) + retval = -EAGAIN; + +- if (retval != -EAGAIN && retval != -EBUSY) +- dev->power.runtime_error = retval; +- + return retval; + } + +@@ -751,6 +748,9 @@ static int rpm_suspend(struct device *dev, int rpmflags) + dev->power.deferred_resume = false; + wake_up_all(&dev->power.wait_queue); + ++ if (retval != -EAGAIN && retval != -EBUSY) ++ dev->power.runtime_error = retval; ++ + /* + * On transient errors, if the callback routine failed an autosuspend, + * and if the last_busy time has been updated so that there is a new +diff --git a/drivers/cpufreq/amd-pstate-ut.c b/drivers/cpufreq/amd-pstate-ut.c +--- a/drivers/cpufreq/amd-pstate-ut.c ++++ b/drivers/cpufreq/amd-pstate-ut.c +@@ -59,6 +59,7 @@ static int amd_pstate_ut_check_freq(u32 index); + static int amd_pstate_ut_epp(u32 index); + static int amd_pstate_ut_check_driver(u32 index); + static int amd_pstate_ut_check_freq_attrs(u32 index); ++static int amd_pstate_ut_check_floor_freq(u32 index); + + static struct amd_pstate_ut_struct amd_pstate_ut_cases[] = { + {"amd_pstate_ut_acpi_cpc_valid", amd_pstate_ut_acpi_cpc_valid }, +@@ -68,6 +69,7 @@ static struct amd_pstate_ut_struct amd_pstate_ut_cases[] = { + {"amd_pstate_ut_epp", amd_pstate_ut_epp }, + {"amd_pstate_ut_check_driver", amd_pstate_ut_check_driver }, + {"amd_pstate_ut_check_freq_attrs", amd_pstate_ut_check_freq_attrs }, ++ {"amd_pstate_ut_check_floor_freq", amd_pstate_ut_check_floor_freq }, + }; + + static bool test_in_list(const char *list, const char *name) +@@ -275,6 +277,7 @@ static int amd_pstate_set_mode(enum amd_pstate_mode mode) + static int amd_pstate_ut_epp(u32 index) + { + static const char * const epp_strings[] = { ++ "dynamic", + "power", + "balance_power", + "balance_performance", +@@ -282,10 +285,10 @@ static int amd_pstate_ut_epp(u32 index) + }; + char *buf __free(cleanup_page) = NULL; + struct cpufreq_policy *policy = NULL; ++ unsigned long orig_dynamic_epp = 0; + enum amd_pstate_mode orig_mode; + struct amd_cpudata *cpudata; + unsigned long orig_policy; +- bool orig_dynamic_epp; + int ret, cpu = 0; + u16 epp; + int i; +@@ -294,9 +297,11 @@ static int amd_pstate_ut_epp(u32 index) + if (!policy) + return -ENODEV; + +- cpudata = policy->driver_data; + orig_mode = amd_pstate_get_status(); +- orig_dynamic_epp = cpudata->dynamic_epp; ++ if (policy->driver_data) { ++ cpudata = policy->driver_data; ++ orig_dynamic_epp = cpudata->dynamic_epp; ++ } + + /* Drop reference before potential driver change. */ + cpufreq_cpu_put(policy); +@@ -321,16 +326,6 @@ static int amd_pstate_ut_epp(u32 index) + orig_policy = cpudata->policy; + cpudata->policy = CPUFREQ_POLICY_POWERSAVE; + +- /* +- * Disable dynamic EPP before running test. If "orig_dynamic_epp" is +- * true, the driver will do a redundant switch at the end and there +- * is no need for enabling it again at the end of the test. +- */ +- if (cpudata->dynamic_epp) { +- pr_debug("Dynamic EPP is enabled, disabling it\n"); +- amd_pstate_clear_dynamic_epp(policy); +- } +- + for (epp = 0; epp <= U8_MAX; epp++) { + u8 val; + +@@ -367,6 +362,11 @@ static int amd_pstate_ut_epp(u32 index) + if (ret < 0) + goto out; + strreplace(buf, '\n', '\0'); ++ /* ++ * "dynamic" mode reports the EPP as "dynamic(profile:X)" ++ * Trim at "(" and just compare tie the epp string. ++ */ ++ strreplace(buf, '(', '\0'); + + if (strcmp(buf, epp_strings[i])) { + pr_err("String EPP value mismatch: %s != %s\n", buf, epp_strings[i]); +@@ -380,18 +380,23 @@ static int amd_pstate_ut_epp(u32 index) + out: + if (policy) { + cpudata->policy = orig_policy; ++ /* ++ * If the driver had enabled dynamic_epp to begin with, ++ * restore it here before dropping policy reference. ++ */ ++ if (orig_dynamic_epp) { ++ int ret2; ++ ++ ret2 = store_energy_performance_preference(policy, ++ epp_strings[0], ++ strlen(epp_strings[0])); ++ if (!ret && (ret2 < 0)) ++ ret = ret2; ++ } + up_write(&policy->rwsem); + cpufreq_cpu_put(policy); + } + +- if (orig_dynamic_epp) { +- int ret2; +- +- ret2 = amd_pstate_set_mode(AMD_PSTATE_DISABLE); +- if (!ret && ret2) +- ret = ret2; +- } +- + if (orig_mode != amd_pstate_get_status()) { + int ret2; + +@@ -557,6 +562,80 @@ out: + return ret; + } + ++static int amd_pstate_ut_check_floor_freq(u32 index) ++{ ++ struct cpufreq_policy *policy __free(put_cpufreq_policy) = NULL; ++ char *buf __free(cleanup_page) = NULL; ++ unsigned int orig_floor_freq; ++ unsigned int floor_freq; ++ int ret, cpu = 0; ++ ++ if (!cpu_feature_enabled(X86_FEATURE_CPPC_PERF_PRIO)) ++ return -EOPNOTSUPP; ++ ++ policy = cpufreq_cpu_get(cpu); ++ if (!policy) ++ return -ENODEV; ++ ++ buf = (char *)__get_free_page(GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ guard(rwsem_write)(&policy->rwsem); ++ ++ if (!policy->driver_data) ++ return -ENODEV; ++ ++ /* Retrieve original floor frequency */ ++ memset(buf, 0, PAGE_SIZE); ++ ret = show_amd_pstate_floor_freq(policy, buf); ++ if (ret < 0) ++ return ret; ++ ++ ret = kstrtou32(buf, 0, &orig_floor_freq); ++ if (ret) ++ return ret; ++ ++ memset(buf, 0, PAGE_SIZE); ++ snprintf(buf, PAGE_SIZE, "%u", policy->cpuinfo.min_freq); ++ ++ /* Set floor frequency to cpuinfo.min_freq */ ++ ret = store_amd_pstate_floor_freq(policy, buf, strlen(buf)); ++ if (ret < 0) { ++ pr_err("Failed to set floor frequency to %s\n", buf); ++ return ret; ++ } ++ ++ memset(buf, 0, PAGE_SIZE); ++ ret = show_amd_pstate_floor_freq(policy, buf); ++ if (ret < 0) ++ return ret; ++ ++ strreplace(buf, '\n', '\0'); ++ ret = kstrtou32(buf, 0, &floor_freq); ++ if (ret) ++ return ret; ++ ++ /* Confirm sysfs reflects the change correctly. */ ++ if (floor_freq != policy->cpuinfo.min_freq) { ++ pr_err("Floor frequency value mismatch: %u != %u\n", ++ floor_freq, policy->cpuinfo.min_freq); ++ return -EINVAL; ++ } ++ ++ memset(buf, 0, PAGE_SIZE); ++ snprintf(buf, PAGE_SIZE, "%u", orig_floor_freq); ++ ++ /* Restore the original value. */ ++ ret = store_amd_pstate_floor_freq(policy, buf, strlen(buf)); ++ if (ret < 0) { ++ pr_err("Failed to restore floor frequency to %s\n", buf); ++ return ret; ++ } ++ ++ return 0; ++} ++ + static int __init amd_pstate_ut_init(void) + { + u32 i = 0, arr_size = ARRAY_SIZE(amd_pstate_ut_cases); +@@ -575,10 +654,16 @@ static int __init amd_pstate_ut_init(void) + + ret = amd_pstate_ut_cases[i].func(i); + +- if (ret) ++ if (ret) { ++ /* Platform does not support the feature being tested. */ ++ if (ret == -EOPNOTSUPP) { ++ pr_err("%-4d %-20s\t skipped!\n", i+1, amd_pstate_ut_cases[i].name); ++ continue; ++ } + pr_err("%-4d %-20s\t fail: %d!\n", i+1, amd_pstate_ut_cases[i].name, ret); +- else ++ } else { + pr_info("%-4d %-20s\t success!\n", i+1, amd_pstate_ut_cases[i].name); ++ } + } + + return 0; +diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c +--- a/drivers/cpufreq/amd-pstate.c ++++ b/drivers/cpufreq/amd-pstate.c +@@ -75,7 +75,7 @@ const char *amd_pstate_get_mode_string(enum amd_pstate_mode mode) + mode = AMD_PSTATE_UNDEFINED; + return amd_pstate_mode_string[mode]; + } +-EXPORT_SYMBOL_GPL(amd_pstate_get_mode_string); ++EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_get_mode_string); + + struct quirk_entry { + u32 nominal_freq; +@@ -87,7 +87,6 @@ static struct cpufreq_driver amd_pstate_driver; + static struct cpufreq_driver amd_pstate_epp_driver; + static int cppc_state = AMD_PSTATE_UNDEFINED; + static bool amd_pstate_prefcore = true; +-static bool dynamic_epp; + static struct quirk_entry *quirks; + + /* +@@ -106,6 +105,7 @@ static struct quirk_entry *quirks; + * 3 balance_power + * 4 power + * 5 custom (for raw EPP values) ++ * 6 dynamic (platform profile driven selection) + */ + enum energy_perf_value_index { + EPP_INDEX_DEFAULT = 0, +@@ -114,6 +114,7 @@ enum energy_perf_value_index { + EPP_INDEX_BALANCE_POWERSAVE, + EPP_INDEX_POWERSAVE, + EPP_INDEX_CUSTOM, ++ EPP_INDEX_DYNAMIC, + EPP_INDEX_MAX, + }; + +@@ -124,6 +125,7 @@ static const char * const energy_perf_strings[] = { + [EPP_INDEX_BALANCE_POWERSAVE] = "balance_power", + [EPP_INDEX_POWERSAVE] = "power", + [EPP_INDEX_CUSTOM] = "custom", ++ [EPP_INDEX_DYNAMIC] = "dynamic", + }; + static_assert(ARRAY_SIZE(energy_perf_strings) == EPP_INDEX_MAX); + +@@ -134,7 +136,7 @@ static unsigned int epp_values[] = { + [EPP_INDEX_BALANCE_POWERSAVE] = AMD_CPPC_EPP_BALANCE_POWERSAVE, + [EPP_INDEX_POWERSAVE] = AMD_CPPC_EPP_POWERSAVE, + }; +-static_assert(ARRAY_SIZE(epp_values) == EPP_INDEX_MAX - 1); ++static_assert(ARRAY_SIZE(epp_values) == EPP_INDEX_MAX - 2); + + typedef int (*cppc_mode_transition_fn)(int); + +@@ -462,7 +464,6 @@ static int msr_init_perf(struct amd_cpudata *cpudata) + { + union perf_cached perf = READ_ONCE(cpudata->perf); + u64 cap1, numerator, cppc_req; +- u8 min_perf; + + int ret = rdmsrq_safe_on_cpu(cpudata->cpu, MSR_AMD_CPPC_CAP1, + &cap1); +@@ -478,16 +479,6 @@ static int msr_init_perf(struct amd_cpudata *cpudata) + return ret; + + WRITE_ONCE(cpudata->cppc_req_cached, cppc_req); +- min_perf = FIELD_GET(AMD_CPPC_MIN_PERF_MASK, cppc_req); +- +- /* +- * Clear out the min_perf part to check if the rest of the MSR is 0, if yes, this is an +- * indication that the min_perf value is the one specified through the BIOS option +- */ +- cppc_req &= ~(AMD_CPPC_MIN_PERF_MASK); +- +- if (!cppc_req) +- perf.bios_min_perf = min_perf; + + perf.highest_perf = numerator; + perf.max_limit_perf = numerator; +@@ -495,6 +486,7 @@ static int msr_init_perf(struct amd_cpudata *cpudata) + perf.nominal_perf = FIELD_GET(AMD_CPPC_NOMINAL_PERF_MASK, cap1); + perf.lowest_nonlinear_perf = FIELD_GET(AMD_CPPC_LOWNONLIN_PERF_MASK, cap1); + perf.lowest_perf = FIELD_GET(AMD_CPPC_LOWEST_PERF_MASK, cap1); ++ perf.bios_min_perf = FIELD_GET(AMD_CPPC_MIN_PERF_MASK, cppc_req); + WRITE_ONCE(cpudata->perf, perf); + WRITE_ONCE(cpudata->prefcore_ranking, FIELD_GET(AMD_CPPC_HIGHEST_PERF_MASK, cap1)); + WRITE_ONCE(cpudata->floor_perf_cnt, FIELD_GET(AMD_CPPC_FLOOR_PERF_CNT_MASK, cap1)); +@@ -789,6 +781,7 @@ static unsigned int amd_pstate_fast_switch(struct cpufreq_policy *policy, + static void amd_pstate_adjust_perf(struct cpufreq_policy *policy, + unsigned long _min_perf, + unsigned long target_perf, ++ unsigned long _max_perf, + unsigned long capacity) + { + u8 max_perf, min_perf, des_perf, cap_perf; +@@ -1044,6 +1037,13 @@ static int amd_pstate_init_freq(struct amd_cpudata *cpudata) + return -EINVAL; + } + ++ if (perf.bios_min_perf) { ++ u32 bios_min_freq = perf_to_freq(perf, cpudata->nominal_freq, perf.bios_min_perf); ++ ++ pr_debug("Found Requested CPU Min Frequency of %uKHz on CPU%d\n", ++ bios_min_freq, cpudata->cpu); ++ } ++ + return 0; + } + +@@ -1192,6 +1192,24 @@ static int amd_pstate_power_supply_notifier(struct notifier_block *nb, + return NOTIFY_OK; + } + ++static int amd_pstate_get_epp_from_platform_profile(struct cpufreq_policy *policy, ++ enum platform_profile_option profile) ++{ ++ switch (profile) { ++ case PLATFORM_PROFILE_PERFORMANCE: ++ return AMD_CPPC_EPP_PERFORMANCE; ++ case PLATFORM_PROFILE_BALANCED: ++ return amd_pstate_get_balanced_epp(policy); ++ case PLATFORM_PROFILE_LOW_POWER: ++ return AMD_CPPC_EPP_POWERSAVE; ++ default: ++ break; ++ } ++ ++ pr_err("Unknown Platform Profile %d\n", profile); ++ return -EOPNOTSUPP; ++} ++ + static int amd_pstate_profile_probe(void *drvdata, unsigned long *choices) + { + set_bit(PLATFORM_PROFILE_LOW_POWER, choices); +@@ -1217,31 +1235,19 @@ static int amd_pstate_profile_set(struct device *dev, + struct amd_cpudata *cpudata = dev_get_drvdata(dev); + struct cpufreq_policy *policy __free(put_cpufreq_policy) = cpufreq_cpu_get(cpudata->cpu); + int ret; ++ u8 epp; + + if (!policy) + return -ENODEV; + +- switch (profile) { +- case PLATFORM_PROFILE_LOW_POWER: +- ret = amd_pstate_set_epp(policy, AMD_CPPC_EPP_POWERSAVE); +- if (ret) +- return ret; +- break; +- case PLATFORM_PROFILE_BALANCED: +- ret = amd_pstate_set_epp(policy, +- amd_pstate_get_balanced_epp(policy)); +- if (ret) +- return ret; +- break; +- case PLATFORM_PROFILE_PERFORMANCE: +- ret = amd_pstate_set_epp(policy, AMD_CPPC_EPP_PERFORMANCE); +- if (ret) +- return ret; +- break; +- default: +- pr_err("Unknown Platform Profile %d\n", profile); +- return -EOPNOTSUPP; +- } ++ ret = amd_pstate_get_epp_from_platform_profile(policy, profile); ++ if (ret < 0) ++ return ret; ++ ++ epp = (u8)ret; ++ ret = amd_pstate_set_epp(policy, epp); ++ if (ret) ++ return ret; + + cpudata->current_profile = profile; + +@@ -1267,28 +1273,20 @@ void amd_pstate_clear_dynamic_epp(struct cpufreq_policy *policy) + kfree(cpudata->profile_name); + cpudata->dynamic_epp = false; + } +-EXPORT_SYMBOL_GPL(amd_pstate_clear_dynamic_epp); ++EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_clear_dynamic_epp); + + static int amd_pstate_set_dynamic_epp(struct cpufreq_policy *policy) + { + struct amd_cpudata *cpudata = policy->driver_data; ++ u64 prev = READ_ONCE(cpudata->cppc_req_cached); + int ret; + u8 epp; + +- switch (cpudata->current_profile) { +- case PLATFORM_PROFILE_PERFORMANCE: +- epp = AMD_CPPC_EPP_PERFORMANCE; +- break; +- case PLATFORM_PROFILE_LOW_POWER: +- epp = AMD_CPPC_EPP_POWERSAVE; +- break; +- case PLATFORM_PROFILE_BALANCED: +- epp = amd_pstate_get_balanced_epp(policy); +- break; +- default: +- pr_err("Unknown Platform Profile %d\n", cpudata->current_profile); +- return -EOPNOTSUPP; +- } ++ ret = amd_pstate_get_epp_from_platform_profile(policy, cpudata->current_profile); ++ if (ret < 0) ++ return ret; ++ ++ epp = (u8)ret; + ret = amd_pstate_set_epp(policy, epp); + if (ret) + return ret; +@@ -1321,6 +1319,9 @@ static int amd_pstate_set_dynamic_epp(struct cpufreq_policy *policy) + cleanup: + amd_pstate_clear_dynamic_epp(policy); + ++ epp = FIELD_GET(AMD_CPPC_EPP_PERF_MASK, prev); ++ /* Restore previous EPP if toggling Dynamic EPP failed. */ ++ amd_pstate_set_epp(policy, epp); + return ret; + } + +@@ -1391,7 +1392,7 @@ static ssize_t show_amd_pstate_hw_prefcore(struct cpufreq_policy *policy, + static ssize_t show_energy_performance_available_preferences( + struct cpufreq_policy *policy, char *buf) + { +- int offset = 0, i; ++ int i, offset = 0; + struct amd_cpudata *cpudata = policy->driver_data; + + if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) +@@ -1414,11 +1415,6 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy, + bool raw_epp = false; + u8 epp; + +- if (cpudata->dynamic_epp) { +- pr_debug("EPP cannot be set when dynamic EPP is enabled\n"); +- return -EBUSY; +- } +- + /* + * if the value matches a number, use that, otherwise see if + * matches an index in the energy_perf_strings array +@@ -1429,6 +1425,25 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy, + ret = sysfs_match_string(energy_perf_strings, buf); + if (ret < 0 || ret == EPP_INDEX_CUSTOM) + return -EINVAL; ++ ++ if (ret == EPP_INDEX_DYNAMIC) { ++ if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) ++ return -EBUSY; ++ /* ++ * Dynamic EPP was already enabled for this CPU. ++ * Nothing to do. ++ */ ++ if (cpudata->dynamic_epp) ++ return count; ++ ++ cpudata->current_profile = PLATFORM_PROFILE_BALANCED; ++ ret = amd_pstate_set_dynamic_epp(policy); ++ if (ret) ++ return ret; ++ ++ return count; ++ } ++ + if (ret) + epp = epp_values[ret]; + else +@@ -1440,6 +1455,13 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy, + return -EBUSY; + } + ++ /* ++ * Dynamic EPP was enabled previously! ++ * Switch back to the static EPP mode. ++ */ ++ if (cpudata->dynamic_epp) ++ amd_pstate_clear_dynamic_epp(policy); ++ + ret = amd_pstate_set_epp(policy, epp); + if (ret) + return ret; +@@ -1448,7 +1470,7 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy, + + return count; + } +-EXPORT_SYMBOL_GPL(store_energy_performance_preference); ++EXPORT_SYMBOL_FOR_PSTATE_UT(store_energy_performance_preference); + + ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *buf) + { +@@ -1457,7 +1479,7 @@ ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char * + + epp = FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cpudata->cppc_req_cached); + +- if (cpudata->raw_epp) ++ if (!cpudata->dynamic_epp && cpudata->raw_epp) + return sysfs_emit(buf, "%u\n", epp); + + switch (epp) { +@@ -1477,12 +1499,14 @@ ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char * + return -EINVAL; + } + ++ if (cpudata->dynamic_epp) ++ return sysfs_emit(buf, "dynamic(profile:%s)\n", energy_perf_strings[preference]); ++ + return sysfs_emit(buf, "%s\n", energy_perf_strings[preference]); + } +-EXPORT_SYMBOL_GPL(show_energy_performance_preference); ++EXPORT_SYMBOL_FOR_PSTATE_UT(show_energy_performance_preference); + +-static ssize_t store_amd_pstate_floor_freq(struct cpufreq_policy *policy, +- const char *buf, size_t count) ++ssize_t store_amd_pstate_floor_freq(struct cpufreq_policy *policy, const char *buf, size_t count) + { + struct amd_cpudata *cpudata = policy->driver_data; + union perf_cached perf = READ_ONCE(cpudata->perf); +@@ -1505,13 +1529,15 @@ static ssize_t store_amd_pstate_floor_freq(struct cpufreq_policy *policy, + + return ret ?: count; + } ++EXPORT_SYMBOL_FOR_PSTATE_UT(store_amd_pstate_floor_freq); + +-static ssize_t show_amd_pstate_floor_freq(struct cpufreq_policy *policy, char *buf) ++ssize_t show_amd_pstate_floor_freq(struct cpufreq_policy *policy, char *buf) + { + struct amd_cpudata *cpudata = policy->driver_data; + + return sysfs_emit(buf, "%u\n", cpudata->floor_freq); + } ++EXPORT_SYMBOL_FOR_PSTATE_UT(show_amd_pstate_floor_freq); + + static ssize_t show_amd_pstate_floor_count(struct cpufreq_policy *policy, char *buf) + { +@@ -1579,7 +1605,7 @@ struct freq_attr **amd_pstate_get_current_attrs(void) + return NULL; + return current_pstate_driver->attr; + } +-EXPORT_SYMBOL_GPL(amd_pstate_get_current_attrs); ++EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_get_current_attrs); + + static struct freq_attr **get_freq_attrs(void) + { +@@ -1764,7 +1790,7 @@ int amd_pstate_get_status(void) + { + return cppc_state; + } +-EXPORT_SYMBOL_GPL(amd_pstate_get_status); ++EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_get_status); + + int amd_pstate_update_status(const char *buf, size_t size) + { +@@ -1784,7 +1810,7 @@ int amd_pstate_update_status(const char *buf, size_t size) + + return 0; + } +-EXPORT_SYMBOL_GPL(amd_pstate_update_status); ++EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_update_status); + + static ssize_t status_show(struct device *dev, + struct device_attribute *attr, char *buf) +@@ -1812,50 +1838,12 @@ static ssize_t prefcore_show(struct device *dev, + return sysfs_emit(buf, "%s\n", str_enabled_disabled(amd_pstate_prefcore)); + } + +-static ssize_t dynamic_epp_show(struct device *dev, +- struct device_attribute *attr, char *buf) +-{ +- return sysfs_emit(buf, "%s\n", str_enabled_disabled(dynamic_epp)); +-} +- +-static ssize_t dynamic_epp_store(struct device *a, struct device_attribute *b, +- const char *buf, size_t count) +-{ +- bool enabled; +- int ret; +- +- ret = kstrtobool(buf, &enabled); +- if (ret) +- return ret; +- +- guard(mutex)(&amd_pstate_driver_lock); +- +- if (cppc_state != AMD_PSTATE_ACTIVE) { +- pr_debug("dynamic_epp can only be toggled in active mode\n"); +- return -EINVAL; +- } +- +- /* Nothing to do */ +- if (dynamic_epp == enabled) +- return count; +- +- /* reinitialize with desired dynamic EPP value */ +- dynamic_epp = enabled; +- ret = amd_pstate_change_driver_mode(cppc_state); +- if (ret) +- dynamic_epp = false; +- +- return ret ? ret : count; +-} +- + static DEVICE_ATTR_RW(status); + static DEVICE_ATTR_RO(prefcore); +-static DEVICE_ATTR_RW(dynamic_epp); + + static struct attribute *pstate_global_attributes[] = { + &dev_attr_status.attr, + &dev_attr_prefcore.attr, +- &dev_attr_dynamic_epp.attr, + NULL + }; + +@@ -1938,12 +1926,13 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) + + policy->boost_supported = READ_ONCE(cpudata->boost_supported); + +- /* Fetch the firmware programmed default EPP value */ ++ /* Cache the firmware programmed EPP */ + default_epp = amd_pstate_get_epp(cpudata); + if (default_epp < 0) { + ret = default_epp; + goto free_cpudata1; + } ++ FIELD_MODIFY(AMD_CPPC_EPP_PERF_MASK, &cpudata->cppc_req_cached, default_epp); + + /* + * Set the policy to provide a valid fallback value in case +@@ -1961,10 +1950,7 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) + cpudata->current_profile = PLATFORM_PROFILE_BALANCED; + } + +- if (dynamic_epp) +- ret = amd_pstate_set_dynamic_epp(policy); +- else +- ret = amd_pstate_set_epp(policy, cpudata->epp_default_dc); ++ ret = amd_pstate_set_epp(policy, cpudata->epp_default_dc); + if (ret) + goto free_cpudata1; + +@@ -2036,6 +2022,18 @@ static int amd_pstate_epp_set_policy(struct cpufreq_policy *policy) + if (!policy->cpuinfo.max_freq) + return -ENODEV; + ++ /* Must be a switch between PERFORMANCE and POWERSAVE */ ++ if (cpudata->policy != policy->policy) { ++ /* ++ * Disable dynamic_epp when switching ++ * out of CPUFREQ_POLICY_POWERSAVE. ++ */ ++ if (cpudata->dynamic_epp) { ++ WARN_ON_ONCE(cpudata->policy != CPUFREQ_POLICY_POWERSAVE); ++ amd_pstate_clear_dynamic_epp(policy); ++ } ++ } ++ + cpudata->policy = policy->policy; + + ret = amd_pstate_epp_update_limit(policy, true); +@@ -2363,19 +2361,8 @@ static int __init amd_prefcore_param(char *str) + return 0; + } + +-static int __init amd_dynamic_epp_param(char *str) +-{ +- if (!strcmp(str, "disable")) +- dynamic_epp = false; +- if (!strcmp(str, "enable")) +- dynamic_epp = true; +- +- return 0; +-} +- + early_param("amd_pstate", amd_pstate_param); + early_param("amd_prefcore", amd_prefcore_param); +-early_param("amd_dynamic_epp", amd_dynamic_epp_param); + + MODULE_AUTHOR("Huang Rui "); + MODULE_DESCRIPTION("AMD Processor P-state Frequency Driver"); +diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h +--- a/drivers/cpufreq/amd-pstate.h ++++ b/drivers/cpufreq/amd-pstate.h +@@ -11,6 +11,13 @@ + #include + #include + ++#if IS_MODULE(CONFIG_X86_AMD_PSTATE_UT) ++#define EXPORT_SYMBOL_FOR_PSTATE_UT(symbol) \ ++ EXPORT_SYMBOL_FOR_MODULES(symbol, "amd-pstate-ut") ++#else ++#define EXPORT_SYMBOL_FOR_PSTATE_UT(symbol) ++#endif ++ + /********************************************************************* + * AMD P-state INTERFACE * + *********************************************************************/ +@@ -153,6 +160,8 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy, + const char *buf, size_t count); + ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *buf); + void amd_pstate_clear_dynamic_epp(struct cpufreq_policy *policy); ++ssize_t store_amd_pstate_floor_freq(struct cpufreq_policy *policy, const char *buf, size_t count); ++ssize_t show_amd_pstate_floor_freq(struct cpufreq_policy *policy, char *buf); + + struct freq_attr; + +diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c +--- a/drivers/cpufreq/cpufreq.c ++++ b/drivers/cpufreq/cpufreq.c +@@ -2225,14 +2225,17 @@ EXPORT_SYMBOL_GPL(cpufreq_driver_fast_switch); + * @policy: cpufreq policy object of the target CPU. + * @min_perf: Minimum (required) performance level (units of @capacity). + * @target_perf: Target (desired) performance level (units of @capacity). ++ * @max_perf: Maximum (allowed) performance level (units of @capacity). + * @capacity: Capacity of the target CPU. + * +- * Carry out a fast performance level switch of @cpu without sleeping. ++ * Carry out a fast performance level adjustment for the CPU represented by ++ * @policy without sleeping. + * + * The driver's ->adjust_perf() callback invoked by this function must be +- * suitable for being called from within RCU-sched read-side critical sections +- * and it is expected to select a suitable performance level equal to or above +- * @min_perf and preferably equal to or below @target_perf. ++ * suitable for calling from within RCU-sched read-side critical sections and ++ * it is expected to program the processor to select suitable performance ++ * levels between @min_perf and @max_perf inclusive and preferably close to ++ * @target_perf going forward for the CPU represented by @policy. + * + * This function must not be called if policy->fast_switch_enabled is unset. + * +@@ -2244,9 +2247,10 @@ EXPORT_SYMBOL_GPL(cpufreq_driver_fast_switch); + void cpufreq_driver_adjust_perf(struct cpufreq_policy *policy, + unsigned long min_perf, + unsigned long target_perf, ++ unsigned long max_perf, + unsigned long capacity) + { +- cpufreq_driver->adjust_perf(policy, min_perf, target_perf, capacity); ++ cpufreq_driver->adjust_perf(policy, min_perf, target_perf, max_perf, capacity); + } + + /** +diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c +--- a/drivers/cpufreq/intel_pstate.c ++++ b/drivers/cpufreq/intel_pstate.c +@@ -299,7 +299,6 @@ struct pstate_funcs { + static struct pstate_funcs pstate_funcs __read_mostly; + + static bool hwp_active __ro_after_init; +-static int hwp_mode_bdw __ro_after_init; + static bool per_cpu_limits __ro_after_init; + static bool hwp_forced __ro_after_init; + static bool hwp_boost __read_mostly; +@@ -587,21 +586,14 @@ static void intel_pstate_hybrid_hwp_adjust(struct cpudata *cpu) + + hwp_is_hybrid = true; + +- cpu->pstate.turbo_freq = rounddown(cpu->pstate.turbo_pstate * scaling, +- perf_ctl_scaling); +- cpu->pstate.max_freq = rounddown(cpu->pstate.max_pstate * scaling, +- perf_ctl_scaling); +- + freq = perf_ctl_max_phys * perf_ctl_scaling; + cpu->pstate.max_pstate_physical = intel_pstate_freq_to_hwp(cpu, freq); + +- freq = cpu->pstate.min_pstate * perf_ctl_scaling; +- cpu->pstate.min_freq = freq; + /* + * Cast the min P-state value retrieved via pstate_funcs.get_min() to + * the effective range of HWP performance levels. + */ +- cpu->pstate.min_pstate = intel_pstate_freq_to_hwp(cpu, freq); ++ cpu->pstate.min_pstate = intel_pstate_freq_to_hwp(cpu, cpu->pstate.min_freq); + } + + static bool turbo_is_disabled(void) +@@ -979,12 +971,10 @@ static int hybrid_get_cost(struct device *dev, unsigned long freq, + * capacity. Similarly, P-cores start to be populated when E-cores are + * utilized above 60% of the capacity. + */ +- if (hybrid_get_cpu_type(dev->id) == INTEL_CPU_TYPE_ATOM) { +- if (hybrid_has_l3(dev->id)) /* E-core */ +- *cost += 1; +- } else { /* P-core */ ++ if (hybrid_get_cpu_type(dev->id) == INTEL_CPU_TYPE_CORE) /* P-core */ + *cost += 2; +- } ++ else if (hybrid_has_l3(dev->id)) /* E-core */ ++ *cost += 1; + + return 0; + } +@@ -1185,6 +1175,22 @@ static bool hybrid_clear_max_perf_cpu(void) + return ret; + } + ++static void intel_pstate_update_freq_limits(struct cpudata *cpu) ++{ ++ int scaling = cpu->pstate.scaling; ++ unsigned int turbo_freq = cpu->pstate.turbo_pstate * scaling; ++ unsigned int max_freq = cpu->pstate.max_pstate * scaling; ++ int perf_ctl_scaling = cpu->pstate.perf_ctl_scaling; ++ ++ if (scaling != perf_ctl_scaling) { ++ turbo_freq = rounddown(turbo_freq, perf_ctl_scaling); ++ max_freq = rounddown(max_freq, perf_ctl_scaling); ++ } ++ ++ cpu->pstate.turbo_freq = turbo_freq; ++ cpu->pstate.max_freq = max_freq; ++} ++ + static void __intel_pstate_get_hwp_cap(struct cpudata *cpu) + { + u64 cap; +@@ -1197,20 +1203,8 @@ static void __intel_pstate_get_hwp_cap(struct cpudata *cpu) + + static void intel_pstate_get_hwp_cap(struct cpudata *cpu) + { +- int scaling = cpu->pstate.scaling; +- + __intel_pstate_get_hwp_cap(cpu); +- +- cpu->pstate.max_freq = cpu->pstate.max_pstate * scaling; +- cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * scaling; +- if (scaling != cpu->pstate.perf_ctl_scaling) { +- int perf_ctl_scaling = cpu->pstate.perf_ctl_scaling; +- +- cpu->pstate.max_freq = rounddown(cpu->pstate.max_freq, +- perf_ctl_scaling); +- cpu->pstate.turbo_freq = rounddown(cpu->pstate.turbo_freq, +- perf_ctl_scaling); +- } ++ intel_pstate_update_freq_limits(cpu); + } + + static void hybrid_update_capacity(struct cpudata *cpu) +@@ -2299,33 +2293,16 @@ static int hwp_get_cpu_scaling(int cpu) + return intel_pstate_cppc_get_scaling(cpu); + } + +-static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate) +-{ +- trace_cpu_frequency(pstate * cpu->pstate.scaling, cpu->cpu); +- cpu->pstate.current_pstate = pstate; +- /* +- * Generally, there is no guarantee that this code will always run on +- * the CPU being updated, so force the register update to run on the +- * right CPU. +- */ +- wrmsrq_on_cpu(cpu->cpu, MSR_IA32_PERF_CTL, +- pstate_funcs.get_val(cpu, pstate)); +-} +- +-static void intel_pstate_set_min_pstate(struct cpudata *cpu) +-{ +- intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate); +-} +- + static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) + { + int perf_ctl_scaling = pstate_funcs.get_scaling(); + + cpu->pstate.max_pstate_physical = pstate_funcs.get_max_physical(cpu->cpu); + cpu->pstate.min_pstate = pstate_funcs.get_min(cpu->cpu); ++ cpu->pstate.min_freq = cpu->pstate.min_pstate * perf_ctl_scaling; + cpu->pstate.perf_ctl_scaling = perf_ctl_scaling; + +- if (hwp_active && !hwp_mode_bdw) { ++ if (hwp_active) { + __intel_pstate_get_hwp_cap(cpu); + + if (pstate_funcs.get_cpu_scaling) { +@@ -2345,11 +2322,7 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) + cpu->pstate.turbo_pstate = pstate_funcs.get_turbo(cpu->cpu); + } + +- if (cpu->pstate.scaling == perf_ctl_scaling) { +- cpu->pstate.min_freq = cpu->pstate.min_pstate * perf_ctl_scaling; +- cpu->pstate.max_freq = cpu->pstate.max_pstate * perf_ctl_scaling; +- cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * perf_ctl_scaling; +- } ++ intel_pstate_update_freq_limits(cpu); + + if (pstate_funcs.get_aperf_mperf_shift) + cpu->aperf_mperf_shift = pstate_funcs.get_aperf_mperf_shift(); +@@ -2882,8 +2855,22 @@ static void intel_pstate_update_perf_limits(struct cpudata *cpu, + cpu->min_perf_ratio); + } + ++static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate) ++{ ++ trace_cpu_frequency(pstate * cpu->pstate.scaling, cpu->cpu); ++ cpu->pstate.current_pstate = pstate; ++ /* ++ * Generally, there is no guarantee that this code will always run on ++ * the CPU being updated, so force the register update to run on the ++ * right CPU. ++ */ ++ wrmsrq_on_cpu(cpu->cpu, MSR_IA32_PERF_CTL, ++ pstate_funcs.get_val(cpu, pstate)); ++} ++ + static int intel_pstate_set_policy(struct cpufreq_policy *policy) + { ++ unsigned int freq = policy->min; + struct cpudata *cpu; + + if (!policy->cpuinfo.max_freq) +@@ -2899,7 +2886,23 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy) + + intel_pstate_update_perf_limits(cpu, policy->min, policy->max); + +- if (cpu->policy == CPUFREQ_POLICY_PERFORMANCE) { ++ if (hwp_active) { ++ /* ++ * The active mode only requires an update util hook if HWP ++ * boost is used and the policy is not "performance". ++ */ ++ if (hwp_boost && cpu->policy != CPUFREQ_POLICY_PERFORMANCE) { ++ intel_pstate_set_update_util_hook(policy->cpu); ++ } else { ++ intel_pstate_clear_update_util_hook(policy->cpu); ++ if (cpu->policy == CPUFREQ_POLICY_PERFORMANCE) { ++ freq = cpu->max_perf_ratio * cpu->pstate.scaling; ++ if (cpu->pstate.scaling != cpu->pstate.perf_ctl_scaling) ++ freq = rounddown(freq, cpu->pstate.perf_ctl_scaling); ++ } ++ } ++ intel_pstate_hwp_set(policy->cpu); ++ } else if (cpu->policy == CPUFREQ_POLICY_PERFORMANCE) { + int pstate = max(cpu->pstate.min_pstate, cpu->max_perf_ratio); + + /* +@@ -2908,25 +2911,17 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy) + */ + intel_pstate_clear_update_util_hook(policy->cpu); + intel_pstate_set_pstate(cpu, pstate); ++ freq = pstate * cpu->pstate.scaling; + } else { + intel_pstate_set_update_util_hook(policy->cpu); + } +- +- if (hwp_active) { +- /* +- * When hwp_boost was active before and dynamically it +- * was turned off, in that case we need to clear the +- * update util hook. +- */ +- if (!hwp_boost) +- intel_pstate_clear_update_util_hook(policy->cpu); +- intel_pstate_hwp_set(policy->cpu); +- } + /* +- * policy->cur is never updated with the intel_pstate driver, but it +- * is used as a stale frequency value. So, keep it within limits. ++ * policy->cur is never updated in the intel_pstate driver, but it is ++ * used as a stale frequency value, so set it to reflect the actual ++ * requested P-state in the "performance" policy case and to the min ++ * otherwise. + */ +- policy->cur = policy->min; ++ policy->cur = freq; + + mutex_unlock(&intel_pstate_limits_lock); + +@@ -2969,6 +2964,11 @@ static int intel_pstate_verify_policy(struct cpufreq_policy_data *policy) + return 0; + } + ++static void intel_pstate_set_min_pstate(struct cpudata *cpu) ++{ ++ intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate); ++} ++ + static int intel_cpufreq_cpu_offline(struct cpufreq_policy *policy) + { + struct cpudata *cpu = all_cpu_data[policy->cpu]; +@@ -3242,6 +3242,7 @@ static unsigned int intel_cpufreq_fast_switch(struct cpufreq_policy *policy, + static void intel_cpufreq_adjust_perf(struct cpufreq_policy *policy, + unsigned long min_perf, + unsigned long target_perf, ++ unsigned long max_perf, + unsigned long capacity) + { + struct cpudata *cpu = all_cpu_data[policy->cpu]; +@@ -3272,7 +3273,13 @@ static void intel_cpufreq_adjust_perf(struct cpufreq_policy *policy, + if (min_pstate > cpu->max_perf_ratio) + min_pstate = cpu->max_perf_ratio; + +- max_pstate = min(cap_pstate, cpu->max_perf_ratio); ++ max_pstate = cap_pstate; ++ if (max_perf < capacity) ++ max_pstate = DIV_ROUND_UP(cap_pstate * max_perf, capacity); ++ ++ if (max_pstate > cpu->max_perf_ratio) ++ max_pstate = cpu->max_perf_ratio; ++ + if (max_pstate < min_pstate) + max_pstate = min_pstate; + +@@ -3679,14 +3686,14 @@ static inline bool intel_pstate_has_acpi_ppc(void) { return false; } + static inline void intel_pstate_request_control_from_smm(void) {} + #endif /* CONFIG_ACPI */ + +-#define INTEL_PSTATE_HWP_BROADWELL 0x01 ++#define INTEL_PSTATE_HWP_NOT_HYBRID 0x01 + + #define X86_MATCH_HWP(vfm, hwp_mode) \ + X86_MATCH_VFM_FEATURE(vfm, X86_FEATURE_HWP, hwp_mode) + + static const struct x86_cpu_id hwp_support_ids[] __initconst = { +- X86_MATCH_HWP(INTEL_BROADWELL_X, INTEL_PSTATE_HWP_BROADWELL), +- X86_MATCH_HWP(INTEL_BROADWELL_D, INTEL_PSTATE_HWP_BROADWELL), ++ X86_MATCH_HWP(INTEL_BROADWELL_X, INTEL_PSTATE_HWP_NOT_HYBRID), ++ X86_MATCH_HWP(INTEL_BROADWELL_D, INTEL_PSTATE_HWP_NOT_HYBRID), + X86_MATCH_HWP(INTEL_ANY, 0), + {} + }; +@@ -3811,7 +3818,6 @@ static int __init intel_pstate_init(void) + + if (!no_hwp) { + hwp_active = true; +- hwp_mode_bdw = id->driver_data; + intel_pstate.attr = hwp_cpufreq_attrs; + intel_cpufreq.attr = hwp_cpufreq_attrs; + intel_cpufreq.flags |= CPUFREQ_NEED_UPDATE_LIMITS; +@@ -3819,7 +3825,8 @@ static int __init intel_pstate_init(void) + if (!default_driver) + default_driver = &intel_pstate; + +- pstate_funcs.get_cpu_scaling = hwp_get_cpu_scaling; ++ if (!id->driver_data) ++ pstate_funcs.get_cpu_scaling = hwp_get_cpu_scaling; + + goto hwp_cpu_matched; + } +diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c +--- a/drivers/idle/intel_idle.c ++++ b/drivers/idle/intel_idle.c +@@ -1780,6 +1780,7 @@ module_param_named(no_native, no_native, bool, 0444); + MODULE_PARM_DESC(no_native, "Ignore cpu specific (native) idle states in lieu of ACPI idle states"); + + static struct acpi_processor_power acpi_state_table __initdata; ++static bool acpi_lpi_available __initdata; + + /** + * intel_idle_cst_usable - Check if the _CST information can be used. +@@ -1804,18 +1805,37 @@ static bool __init intel_idle_cst_usable(void) + return true; + } + +-static bool __init intel_idle_acpi_cst_extract(void) ++static bool __init intel_idle_acpi_extract_lpi_cstates(void) + { + unsigned int cpu; + +- if (no_acpi) { +- pr_debug("Not allowed to use ACPI _CST\n"); +- return false; ++ for_each_possible_cpu(cpu) { ++ struct acpi_processor *pr; ++ ++ pr = per_cpu(processors, cpu); ++ if (!pr) ++ continue; ++ ++ if (acpi_processor_extract_lpi_info(pr->handle, ++ &acpi_state_table, true)) ++ continue; ++ ++ acpi_lpi_available = true; ++ return true; + } + +- for_each_possible_cpu(cpu) { +- struct acpi_processor *pr = per_cpu(processors, cpu); ++ pr_debug("No ACPI _LPI idle states\n"); ++ return false; ++} + ++static bool __init intel_idle_acpi_extract_cst_cstates(void) ++{ ++ unsigned int cpu; ++ ++ for_each_possible_cpu(cpu) { ++ struct acpi_processor *pr; ++ ++ pr = per_cpu(processors, cpu); + if (!pr) + continue; + +@@ -1827,18 +1847,96 @@ static bool __init intel_idle_acpi_cst_extract(void) + if (!intel_idle_cst_usable()) + continue; + +- if (!acpi_processor_claim_cst_control()) +- break; +- + return true; + } + +- acpi_state_table.count = 0; + pr_debug("ACPI _CST not found or not usable\n"); + return false; + } + +-static void __init intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) ++static bool __init intel_idle_acpi_extract_cstates(void) ++{ ++ if (intel_idle_acpi_extract_lpi_cstates()) ++ return true; ++ ++ if (intel_idle_acpi_extract_cst_cstates()) ++ return true; ++ ++ return false; ++} ++ ++static bool __init intel_idle_acpi_probe(void) ++{ ++ if (no_acpi) { ++ pr_debug("Not allowed to use ACPI for C-states extraction\n"); ++ return false; ++ } ++ ++ if (intel_idle_acpi_extract_cstates() && ++ acpi_processor_claim_cst_control()) ++ return true; ++ ++ acpi_state_table.count = 0; ++ return false; ++} ++ ++static void __init intel_idle_complete_state_init(struct cpuidle_state *state) ++{ ++ if (intel_idle_state_needs_timer_stop(state)) ++ state->flags |= CPUIDLE_FLAG_TIMER_STOP; ++ ++ state->enter = intel_idle; ++ state->enter_dead = intel_idle_enter_dead; ++ state->enter_s2idle = intel_idle_s2idle; ++} ++ ++static void __init intel_idle_init_cstates_acpi_lpi(struct cpuidle_driver *drv) ++{ ++ int index; ++ ++ for (index = 0; index < acpi_state_table.count; index++) { ++ struct acpi_lpi_state *lpi_state; ++ struct cpuidle_state *state; ++ ++ if (intel_idle_max_cstate_reached(index)) ++ break; ++ ++ lpi_state = &acpi_state_table.lpi_states[index]; ++ ++ state = &drv->states[drv->state_count++]; ++ ++ scnprintf(state->name, CPUIDLE_NAME_LEN, "C%d_LPI", index + 1); ++ strscpy(state->desc, lpi_state->desc, CPUIDLE_DESC_LEN); ++ state->exit_latency = lpi_state->wake_latency; ++ state->target_residency = lpi_state->min_residency; ++ state->flags = MWAIT2flg(lpi_state->address); ++ /* ++ * Assume that entering any of the idle states extracted from ++ * _LPI except for the first two will cause the TLB to be ++ * flushed and let the core call leave_mm() for them upfront ++ * to avoid unnecessary wakeups due to TLB shootdowns. ++ */ ++ if (index > 1) ++ state->flags |= CPUIDLE_FLAG_TLB_FLUSHED; ++ ++ if (disabled_states_mask & BIT(index + 1)) ++ state->flags |= CPUIDLE_FLAG_OFF; ++ ++ intel_idle_complete_state_init(state); ++ ++ pr_info("%s: MWAIT hint 0x%x\n", state->name, flg2MWAIT(state->flags)); ++ } ++ ++ /* ++ * Assume the first idle state in the table to be C1 and if any deeper ++ * idle states are exposed while X86_FEATURE_NONSTOP_TSC is unset, mark ++ * the TSC as unstable. ++ */ ++ if (index > 1 && !boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) ++ mark_tsc_unstable("TSC halts in idle"); ++} ++ ++static void __init intel_idle_init_cstates_acpi_cst(struct cpuidle_driver *drv) + { + int cstate, limit = min_t(int, CPUIDLE_STATE_MAX, acpi_state_table.count); + +@@ -1880,49 +1978,76 @@ static void __init intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) + if (disabled_states_mask & BIT(cstate)) + state->flags |= CPUIDLE_FLAG_OFF; + +- if (intel_idle_state_needs_timer_stop(state)) +- state->flags |= CPUIDLE_FLAG_TIMER_STOP; +- + if (cx->type > ACPI_STATE_C1 && !boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) + mark_tsc_unstable("TSC halts in idle"); + +- state->enter = intel_idle; +- state->enter_dead = intel_idle_enter_dead; +- state->enter_s2idle = intel_idle_s2idle; ++ intel_idle_complete_state_init(state); + } + } + +-static bool __init intel_idle_off_by_default(unsigned int flags, u32 mwait_hint) ++static void __init intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) + { +- int cstate, limit; ++ if (acpi_lpi_available) ++ intel_idle_init_cstates_acpi_lpi(drv); ++ else ++ intel_idle_init_cstates_acpi_cst(drv); ++} + +- /* +- * If there are no _CST C-states, do not disable any C-states by +- * default. +- */ +- if (!acpi_state_table.count) +- return false; ++static bool __init intel_idle_acpi_hint_match(unsigned int flags, u32 acpi_hint, ++ u32 table_hint) ++{ ++ if (flags & CPUIDLE_FLAG_PARTIAL_HINT_MATCH) { ++ acpi_hint &= ~MWAIT_SUBSTATE_MASK; ++ table_hint &= ~MWAIT_SUBSTATE_MASK; ++ } ++ return acpi_hint == table_hint; ++} ++ ++static bool __init intel_idle_off_by_default_lpi(unsigned int flags, u32 mwait_hint) ++{ ++ int index; ++ ++ for (index = 0; index < acpi_state_table.count; index++) { ++ u32 acpi_hint = acpi_state_table.lpi_states[index].address; ++ ++ if (intel_idle_acpi_hint_match(flags, acpi_hint, mwait_hint)) ++ return false; ++ } ++ return true; ++} ++ ++static bool __init intel_idle_off_by_default_cst(unsigned int flags, u32 mwait_hint) ++{ ++ int cstate, limit = min_t(int, CPUIDLE_STATE_MAX, acpi_state_table.count); + +- limit = min_t(int, CPUIDLE_STATE_MAX, acpi_state_table.count); + /* + * If limit > 0, intel_idle_cst_usable() has returned 'true', so all of + * the interesting states are ACPI_CSTATE_FFH. + */ + for (cstate = 1; cstate < limit; cstate++) { + u32 acpi_hint = acpi_state_table.states[cstate].address; +- u32 table_hint = mwait_hint; + +- if (flags & CPUIDLE_FLAG_PARTIAL_HINT_MATCH) { +- acpi_hint &= ~MWAIT_SUBSTATE_MASK; +- table_hint &= ~MWAIT_SUBSTATE_MASK; +- } +- +- if (acpi_hint == table_hint) ++ if (intel_idle_acpi_hint_match(flags, acpi_hint, mwait_hint)) + return false; + } + return true; + } + ++static bool __init intel_idle_off_by_default(unsigned int flags, u32 mwait_hint) ++{ ++ /* ++ * If there is no C-states information in the ACPI tables, do not ++ * disable any C-states by default. ++ */ ++ if (!acpi_state_table.count) ++ return false; ++ ++ if (acpi_lpi_available) ++ return intel_idle_off_by_default_lpi(flags, mwait_hint); ++ ++ return intel_idle_off_by_default_cst(flags, mwait_hint); ++} ++ + static inline bool ignore_native(void) + { + return no_native && !no_acpi; +@@ -1930,7 +2055,7 @@ static inline bool ignore_native(void) + #else /* !CONFIG_ACPI_PROCESSOR_CSTATE */ + #define force_use_acpi (false) + +-static inline bool intel_idle_acpi_cst_extract(void) { return false; } ++static inline bool intel_idle_acpi_probe(void) { return false; } + static inline void intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) { } + static inline bool intel_idle_off_by_default(unsigned int flags, u32 mwait_hint) + { +@@ -2745,7 +2870,7 @@ static int __init intel_idle_init(void) + if (icpu) { + if (icpu->state_table) + cpuidle_state_table = icpu->state_table; +- else if (!intel_idle_acpi_cst_extract()) ++ else if (!intel_idle_acpi_probe()) + return -ENODEV; + + auto_demotion_disable_flags = icpu->auto_demotion_disable_flags; +@@ -2754,8 +2879,8 @@ static int __init intel_idle_init(void) + if (icpu->c1_demotion_supported) + c1_demotion_supported = true; + if (icpu->use_acpi || force_use_acpi) +- intel_idle_acpi_cst_extract(); +- } else if (!intel_idle_acpi_cst_extract()) { ++ intel_idle_acpi_probe(); ++ } else if (!intel_idle_acpi_probe()) { + return -ENODEV; + } + +diff --git a/drivers/opp/core.c b/drivers/opp/core.c +--- a/drivers/opp/core.c ++++ b/drivers/opp/core.c +@@ -1581,8 +1581,6 @@ static struct opp_table *_update_opp_table_clk(struct device *dev, + struct opp_table *opp_table, + bool getclk) + { +- int ret; +- + /* + * Return early if we don't need to get clk or we have already done it + * earlier. +@@ -1591,39 +1589,35 @@ static struct opp_table *_update_opp_table_clk(struct device *dev, + opp_table->clks) + return opp_table; + +- /* Find clk for the device */ +- opp_table->clk = clk_get(dev, NULL); ++ /* ++ * There are few platforms which don't want the OPP core to manage ++ * device's clock settings. In such cases neither the platform ++ * provides the clks explicitly to us, nor the DT contains a valid ++ * clk entry. The OPP nodes in DT may still contain "opp-hz" property ++ * though, which we need to parse and allow the platform to find an ++ * OPP based on freq later on. ++ * ++ * This is a simple solution to take care of such corner cases, i.e. ++ * make the clk_count 1, which lets us allocate space for frequency ++ * in opp->rates and also parse the entries in DT. Use ++ * clk_get_optional() instead of clk_get() so opp_table->clk stays ++ * NULL for such devices, instead of holding an ERR_PTR(-ENOENT) that ++ * consumers must remember to special-case. ++ */ ++ opp_table->clk = clk_get_optional(dev, NULL); + +- ret = PTR_ERR_OR_ZERO(opp_table->clk); +- if (!ret) { ++ if (IS_ERR(opp_table->clk)) { ++ dev_pm_opp_put_opp_table(opp_table); ++ dev_err_probe(dev, PTR_ERR(opp_table->clk), "Couldn't find clock\n"); ++ return ERR_CAST(opp_table->clk); ++ } ++ ++ if (opp_table->clk) + opp_table->config_clks = _opp_config_clk_single; +- opp_table->clk_count = 1; +- return opp_table; +- } + +- if (ret == -ENOENT) { +- /* +- * There are few platforms which don't want the OPP core to +- * manage device's clock settings. In such cases neither the +- * platform provides the clks explicitly to us, nor the DT +- * contains a valid clk entry. The OPP nodes in DT may still +- * contain "opp-hz" property though, which we need to parse and +- * allow the platform to find an OPP based on freq later on. +- * +- * This is a simple solution to take care of such corner cases, +- * i.e. make the clk_count 1, which lets us allocate space for +- * frequency in opp->rates and also parse the entries in DT. +- */ +- opp_table->clk_count = 1; ++ opp_table->clk_count = 1; + +- dev_dbg(dev, "%s: Couldn't find clock: %d\n", __func__, ret); +- return opp_table; +- } +- +- dev_pm_opp_put_opp_table(opp_table); +- dev_err_probe(dev, ret, "Couldn't find clock\n"); +- +- return ERR_PTR(ret); ++ return opp_table; + } + + /* +diff --git a/include/linux/acpi.h b/include/linux/acpi.h +--- a/include/linux/acpi.h ++++ b/include/linux/acpi.h +@@ -315,6 +315,19 @@ static inline int acpi_processor_evaluate_cst(acpi_handle handle, u32 cpu, + } + #endif + ++#ifdef CONFIG_ACPI_PROCESSOR_IDLE ++int acpi_processor_extract_lpi_info(acpi_handle pr_handle, ++ struct acpi_processor_power *pr_power, ++ bool strict); ++#else ++static inline int acpi_processor_extract_lpi_info(acpi_handle pr_handle, ++ struct acpi_processor_power *pr_power, ++ bool strict) ++{ ++ return -ENODEV; ++} ++#endif ++ + #ifdef CONFIG_ACPI_HOTPLUG_CPU + /* Arch dependent functions for cpu hotplug support */ + int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, u32 acpi_id, +diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h +--- a/include/linux/cpufreq.h ++++ b/include/linux/cpufreq.h +@@ -379,6 +379,7 @@ struct cpufreq_driver { + void (*adjust_perf)(struct cpufreq_policy *policy, + unsigned long min_perf, + unsigned long target_perf, ++ unsigned long max_perf, + unsigned long capacity); + + /* +@@ -624,6 +625,7 @@ unsigned int cpufreq_driver_fast_switch(struct cpufreq_policy *policy, + void cpufreq_driver_adjust_perf(struct cpufreq_policy *policy, + unsigned long min_perf, + unsigned long target_perf, ++ unsigned long max_perf, + unsigned long capacity); + bool cpufreq_driver_has_adjust_perf(void); + int cpufreq_driver_target(struct cpufreq_policy *policy, +diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig +--- a/kernel/power/Kconfig ++++ b/kernel/power/Kconfig +@@ -268,6 +268,16 @@ config DPM_WATCHDOG + captured in pstore device for inspection in subsequent + boot session. + ++config DPM_WATCHDOG_ENABLED ++ bool "Enable DPM watchdog by default" ++ depends on DPM_WATCHDOG ++ default y ++ help ++ If you say Y here, the DPM watchdog will be enabled by default. ++ If you say N, it will be compiled in but disabled. It can be ++ enabled at boot time via the "pm.dpm_watchdog_enabled" kernel ++ parameter or at runtime via sysfs. ++ + config DPM_WATCHDOG_TIMEOUT + int "Watchdog timeout to panic in seconds" + range 1 120 +diff --git a/kernel/power/swap.c b/kernel/power/swap.c +--- a/kernel/power/swap.c ++++ b/kernel/power/swap.c +@@ -430,19 +430,22 @@ static int swap_write_page(struct swap_map_handle *handle, void *buf, + + if (!handle->cur) + return -EINVAL; +- offset = alloc_swapdev_block(root_swap); +- error = write_page(buf, offset, hb); +- if (error) +- return error; +- handle->cur->entries[handle->k++] = offset; ++ ++ /* ++ * If the current map page is full, allocate and link next one first. ++ * Delaying this until here avoids writing an empty swap map page when ++ * the image size is an exact MAP_PAGE_ENTRIES multiple. ++ */ + if (handle->k >= MAP_PAGE_ENTRIES) { + offset = alloc_swapdev_block(root_swap); + if (!offset) + return -ENOSPC; ++ + handle->cur->next_swap = offset; + error = write_page(handle->cur, handle->cur_swap, hb); + if (error) +- goto out; ++ return error; ++ + clear_page(handle->cur); + handle->cur_swap = offset; + handle->k = 0; +@@ -450,7 +453,7 @@ static int swap_write_page(struct swap_map_handle *handle, void *buf, + if (hb && low_free_pages() <= handle->reqd_free_pages) { + error = hib_wait_io(hb); + if (error) +- goto out; ++ return error; + /* + * Recalculate the number of required free pages, to + * make sure we never take more than half. +@@ -458,14 +461,21 @@ static int swap_write_page(struct swap_map_handle *handle, void *buf, + handle->reqd_free_pages = reqd_free_pages(); + } + } +- out: +- return error; ++ ++ offset = alloc_swapdev_block(root_swap); ++ error = write_page(buf, offset, hb); ++ if (error) ++ return error; ++ handle->cur->entries[handle->k++] = offset; ++ return 0; + } + + static int flush_swap_writer(struct swap_map_handle *handle) + { +- if (handle->cur && handle->cur_swap) ++ if (handle->cur && handle->cur_swap && handle->k) + return write_page(handle->cur, handle->cur_swap, NULL); ++ else if (handle->cur && handle->cur_swap) ++ return 0; + else + return -EINVAL; + } +@@ -739,7 +749,7 @@ static int save_compressed_image(struct swap_map_handle *handle, + + data[thr].cc = crypto_alloc_acomp(hib_comp_algo, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR_OR_NULL(data[thr].cc)) { +- pr_err("Could not allocate comp stream %ld\n", PTR_ERR(data[thr].cc)); ++ pr_err("Could not allocate comp stream %pe\n", data[thr].cc); + ret = -EFAULT; + goto out_clean; + } +@@ -1243,7 +1253,7 @@ static int load_compressed_image(struct swap_map_handle *handle, + + data[thr].cc = crypto_alloc_acomp(hib_comp_algo, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR_OR_NULL(data[thr].cc)) { +- pr_err("Could not allocate comp stream %ld\n", PTR_ERR(data[thr].cc)); ++ pr_err("Could not allocate comp stream %pe\n", data[thr].cc); + ret = -EFAULT; + goto out_clean; + } +diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c +--- a/kernel/sched/cpufreq_schedutil.c ++++ b/kernel/sched/cpufreq_schedutil.c +@@ -50,6 +50,7 @@ struct sugov_cpu { + + unsigned long util; + unsigned long bw_min; ++ unsigned long bw_max; + + /* The field below is for single-CPU policies only: */ + #ifdef CONFIG_NO_HZ_COMMON +@@ -243,6 +244,7 @@ static void sugov_get_util(struct sugov_cpu *sg_cpu, unsigned long boost) + util = effective_cpu_util(sg_cpu->cpu, util, &min, &max); + util = max(util, boost); + sg_cpu->bw_min = min; ++ sg_cpu->bw_max = max; + sg_cpu->util = sugov_effective_cpu_perf(sg_cpu->cpu, util, min, max); + } + +@@ -495,7 +497,7 @@ static void sugov_update_single_perf(struct update_util_data *hook, u64 time, + sg_cpu->util = prev_util; + + cpufreq_driver_adjust_perf(sg_policy->policy, sg_cpu->bw_min, +- sg_cpu->util, max_cap); ++ sg_cpu->util, sg_cpu->bw_max, max_cap); + + sg_policy->need_freq_update = false; + sg_policy->last_freq_update_time = time; +@@ -601,7 +603,7 @@ static ssize_t rate_limit_us_show(struct gov_attr_set *attr_set, char *buf) + { + struct sugov_tunables *tunables = to_sugov_tunables(attr_set); + +- return sprintf(buf, "%u\n", tunables->rate_limit_us); ++ return sysfs_emit(buf, "%u\n", tunables->rate_limit_us); + } + + static ssize_t +diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs +--- a/rust/kernel/cpufreq.rs ++++ b/rust/kernel/cpufreq.rs +@@ -797,7 +797,13 @@ pub trait Driver { + } + + /// Driver's `adjust_perf` callback. +- fn adjust_perf(_policy: &mut Policy, _min_perf: usize, _target_perf: usize, _capacity: usize) { ++ fn adjust_perf( ++ _policy: &mut Policy, ++ _min_perf: usize, ++ _target_perf: usize, ++ _max_perf: usize, ++ _capacity: usize, ++ ) { + build_error!(VTABLE_DEFAULT_ERROR) + } + +@@ -1270,12 +1276,13 @@ impl Registration { + ptr: *mut bindings::cpufreq_policy, + min_perf: c_ulong, + target_perf: c_ulong, ++ max_perf: c_ulong, + capacity: c_ulong, + ) { + // SAFETY: The `ptr` is guaranteed to be valid by the contract with the C code for the + // lifetime of `policy`. + let policy = unsafe { Policy::from_raw_mut(ptr) }; +- T::adjust_perf(policy, min_perf, target_perf, capacity); ++ T::adjust_perf(policy, min_perf, target_perf, max_perf, capacity); + } + + /// Driver's `get_intermediate` callback. diff --git a/pkgbuilds/linux-omarchy-bore/0210-pm-updates.patch.sig b/pkgbuilds/linux-omarchy-bore/0210-pm-updates.patch.sig new file mode 100644 index 0000000..4f4c8b3 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0210-pm-updates.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0210-pstate.patch b/pkgbuilds/linux-omarchy-bore/0210-pstate.patch deleted file mode 100644 index 3d4eefc..0000000 --- a/pkgbuilds/linux-omarchy-bore/0210-pstate.patch +++ /dev/null @@ -1,480 +0,0 @@ -diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c ---- a/drivers/cpufreq/amd-pstate.c -+++ b/drivers/cpufreq/amd-pstate.c -@@ -782,6 +782,7 @@ static unsigned int amd_pstate_fast_switch(struct cpufreq_policy *policy, - static void amd_pstate_adjust_perf(struct cpufreq_policy *policy, - unsigned long _min_perf, - unsigned long target_perf, -+ unsigned long _max_perf, - unsigned long capacity) - { - u8 max_perf, min_perf, des_perf, cap_perf; -diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c ---- a/drivers/cpufreq/cpufreq.c -+++ b/drivers/cpufreq/cpufreq.c -@@ -2225,14 +2225,17 @@ EXPORT_SYMBOL_GPL(cpufreq_driver_fast_switch); - * @policy: cpufreq policy object of the target CPU. - * @min_perf: Minimum (required) performance level (units of @capacity). - * @target_perf: Target (desired) performance level (units of @capacity). -+ * @max_perf: Maximum (allowed) performance level (units of @capacity). - * @capacity: Capacity of the target CPU. - * -- * Carry out a fast performance level switch of @cpu without sleeping. -+ * Carry out a fast performance level adjustment for the CPU represented by -+ * @policy without sleeping. - * - * The driver's ->adjust_perf() callback invoked by this function must be -- * suitable for being called from within RCU-sched read-side critical sections -- * and it is expected to select a suitable performance level equal to or above -- * @min_perf and preferably equal to or below @target_perf. -+ * suitable for calling from within RCU-sched read-side critical sections and -+ * it is expected to program the processor to select suitable performance -+ * levels between @min_perf and @max_perf inclusive and preferably close to -+ * @target_perf going forward for the CPU represented by @policy. - * - * This function must not be called if policy->fast_switch_enabled is unset. - * -@@ -2244,9 +2247,10 @@ EXPORT_SYMBOL_GPL(cpufreq_driver_fast_switch); - void cpufreq_driver_adjust_perf(struct cpufreq_policy *policy, - unsigned long min_perf, - unsigned long target_perf, -+ unsigned long max_perf, - unsigned long capacity) - { -- cpufreq_driver->adjust_perf(policy, min_perf, target_perf, capacity); -+ cpufreq_driver->adjust_perf(policy, min_perf, target_perf, max_perf, capacity); - } - - /** -diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c ---- a/drivers/cpufreq/intel_pstate.c -+++ b/drivers/cpufreq/intel_pstate.c -@@ -299,7 +299,6 @@ struct pstate_funcs { - static struct pstate_funcs pstate_funcs __read_mostly; - - static bool hwp_active __ro_after_init; --static int hwp_mode_bdw __ro_after_init; - static bool per_cpu_limits __ro_after_init; - static bool hwp_forced __ro_after_init; - static bool hwp_boost __read_mostly; -@@ -587,21 +586,14 @@ static void intel_pstate_hybrid_hwp_adjust(struct cpudata *cpu) - - hwp_is_hybrid = true; - -- cpu->pstate.turbo_freq = rounddown(cpu->pstate.turbo_pstate * scaling, -- perf_ctl_scaling); -- cpu->pstate.max_freq = rounddown(cpu->pstate.max_pstate * scaling, -- perf_ctl_scaling); -- - freq = perf_ctl_max_phys * perf_ctl_scaling; - cpu->pstate.max_pstate_physical = intel_pstate_freq_to_hwp(cpu, freq); - -- freq = cpu->pstate.min_pstate * perf_ctl_scaling; -- cpu->pstate.min_freq = freq; - /* - * Cast the min P-state value retrieved via pstate_funcs.get_min() to - * the effective range of HWP performance levels. - */ -- cpu->pstate.min_pstate = intel_pstate_freq_to_hwp(cpu, freq); -+ cpu->pstate.min_pstate = intel_pstate_freq_to_hwp(cpu, cpu->pstate.min_freq); - } - - static bool turbo_is_disabled(void) -@@ -979,12 +971,10 @@ static int hybrid_get_cost(struct device *dev, unsigned long freq, - * capacity. Similarly, P-cores start to be populated when E-cores are - * utilized above 60% of the capacity. - */ -- if (hybrid_get_cpu_type(dev->id) == INTEL_CPU_TYPE_ATOM) { -- if (hybrid_has_l3(dev->id)) /* E-core */ -- *cost += 1; -- } else { /* P-core */ -+ if (hybrid_get_cpu_type(dev->id) == INTEL_CPU_TYPE_CORE) /* P-core */ - *cost += 2; -- } -+ else if (hybrid_has_l3(dev->id)) /* E-core */ -+ *cost += 1; - - return 0; - } -@@ -1185,6 +1175,22 @@ static bool hybrid_clear_max_perf_cpu(void) - return ret; - } - -+static void intel_pstate_update_freq_limits(struct cpudata *cpu) -+{ -+ int scaling = cpu->pstate.scaling; -+ unsigned int turbo_freq = cpu->pstate.turbo_pstate * scaling; -+ unsigned int max_freq = cpu->pstate.max_pstate * scaling; -+ int perf_ctl_scaling = cpu->pstate.perf_ctl_scaling; -+ -+ if (scaling != perf_ctl_scaling) { -+ turbo_freq = rounddown(turbo_freq, perf_ctl_scaling); -+ max_freq = rounddown(max_freq, perf_ctl_scaling); -+ } -+ -+ cpu->pstate.turbo_freq = turbo_freq; -+ cpu->pstate.max_freq = max_freq; -+} -+ - static void __intel_pstate_get_hwp_cap(struct cpudata *cpu) - { - u64 cap; -@@ -1197,20 +1203,8 @@ static void __intel_pstate_get_hwp_cap(struct cpudata *cpu) - - static void intel_pstate_get_hwp_cap(struct cpudata *cpu) - { -- int scaling = cpu->pstate.scaling; -- - __intel_pstate_get_hwp_cap(cpu); -- -- cpu->pstate.max_freq = cpu->pstate.max_pstate * scaling; -- cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * scaling; -- if (scaling != cpu->pstate.perf_ctl_scaling) { -- int perf_ctl_scaling = cpu->pstate.perf_ctl_scaling; -- -- cpu->pstate.max_freq = rounddown(cpu->pstate.max_freq, -- perf_ctl_scaling); -- cpu->pstate.turbo_freq = rounddown(cpu->pstate.turbo_freq, -- perf_ctl_scaling); -- } -+ intel_pstate_update_freq_limits(cpu); - } - - static void hybrid_update_capacity(struct cpudata *cpu) -@@ -2299,33 +2293,16 @@ static int hwp_get_cpu_scaling(int cpu) - return intel_pstate_cppc_get_scaling(cpu); - } - --static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate) --{ -- trace_cpu_frequency(pstate * cpu->pstate.scaling, cpu->cpu); -- cpu->pstate.current_pstate = pstate; -- /* -- * Generally, there is no guarantee that this code will always run on -- * the CPU being updated, so force the register update to run on the -- * right CPU. -- */ -- wrmsrq_on_cpu(cpu->cpu, MSR_IA32_PERF_CTL, -- pstate_funcs.get_val(cpu, pstate)); --} -- --static void intel_pstate_set_min_pstate(struct cpudata *cpu) --{ -- intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate); --} -- - static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) - { - int perf_ctl_scaling = pstate_funcs.get_scaling(); - - cpu->pstate.max_pstate_physical = pstate_funcs.get_max_physical(cpu->cpu); - cpu->pstate.min_pstate = pstate_funcs.get_min(cpu->cpu); -+ cpu->pstate.min_freq = cpu->pstate.min_pstate * perf_ctl_scaling; - cpu->pstate.perf_ctl_scaling = perf_ctl_scaling; - -- if (hwp_active && !hwp_mode_bdw) { -+ if (hwp_active) { - __intel_pstate_get_hwp_cap(cpu); - - if (pstate_funcs.get_cpu_scaling) { -@@ -2345,19 +2322,13 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) - cpu->pstate.turbo_pstate = pstate_funcs.get_turbo(cpu->cpu); - } - -- if (cpu->pstate.scaling == perf_ctl_scaling) { -- cpu->pstate.min_freq = cpu->pstate.min_pstate * perf_ctl_scaling; -- cpu->pstate.max_freq = cpu->pstate.max_pstate * perf_ctl_scaling; -- cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * perf_ctl_scaling; -- } -+ intel_pstate_update_freq_limits(cpu); - - if (pstate_funcs.get_aperf_mperf_shift) - cpu->aperf_mperf_shift = pstate_funcs.get_aperf_mperf_shift(); - - if (pstate_funcs.get_vid) - pstate_funcs.get_vid(cpu); -- -- intel_pstate_set_min_pstate(cpu); - } - - /* -@@ -2884,8 +2855,22 @@ static void intel_pstate_update_perf_limits(struct cpudata *cpu, - cpu->min_perf_ratio); - } - -+static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate) -+{ -+ trace_cpu_frequency(pstate * cpu->pstate.scaling, cpu->cpu); -+ cpu->pstate.current_pstate = pstate; -+ /* -+ * Generally, there is no guarantee that this code will always run on -+ * the CPU being updated, so force the register update to run on the -+ * right CPU. -+ */ -+ wrmsrq_on_cpu(cpu->cpu, MSR_IA32_PERF_CTL, -+ pstate_funcs.get_val(cpu, pstate)); -+} -+ - static int intel_pstate_set_policy(struct cpufreq_policy *policy) - { -+ unsigned int freq = policy->min; - struct cpudata *cpu; - - if (!policy->cpuinfo.max_freq) -@@ -2901,7 +2886,23 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy) - - intel_pstate_update_perf_limits(cpu, policy->min, policy->max); - -- if (cpu->policy == CPUFREQ_POLICY_PERFORMANCE) { -+ if (hwp_active) { -+ /* -+ * The active mode only requires an update util hook if HWP -+ * boost is used and the policy is not "performance". -+ */ -+ if (hwp_boost && cpu->policy != CPUFREQ_POLICY_PERFORMANCE) { -+ intel_pstate_set_update_util_hook(policy->cpu); -+ } else { -+ intel_pstate_clear_update_util_hook(policy->cpu); -+ if (cpu->policy == CPUFREQ_POLICY_PERFORMANCE) { -+ freq = cpu->max_perf_ratio * cpu->pstate.scaling; -+ if (cpu->pstate.scaling != cpu->pstate.perf_ctl_scaling) -+ freq = rounddown(freq, cpu->pstate.perf_ctl_scaling); -+ } -+ } -+ intel_pstate_hwp_set(policy->cpu); -+ } else if (cpu->policy == CPUFREQ_POLICY_PERFORMANCE) { - int pstate = max(cpu->pstate.min_pstate, cpu->max_perf_ratio); - - /* -@@ -2910,25 +2911,17 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy) - */ - intel_pstate_clear_update_util_hook(policy->cpu); - intel_pstate_set_pstate(cpu, pstate); -+ freq = pstate * cpu->pstate.scaling; - } else { - intel_pstate_set_update_util_hook(policy->cpu); - } -- -- if (hwp_active) { -- /* -- * When hwp_boost was active before and dynamically it -- * was turned off, in that case we need to clear the -- * update util hook. -- */ -- if (!hwp_boost) -- intel_pstate_clear_update_util_hook(policy->cpu); -- intel_pstate_hwp_set(policy->cpu); -- } - /* -- * policy->cur is never updated with the intel_pstate driver, but it -- * is used as a stale frequency value. So, keep it within limits. -+ * policy->cur is never updated in the intel_pstate driver, but it is -+ * used as a stale frequency value, so set it to reflect the actual -+ * requested P-state in the "performance" policy case and to the min -+ * otherwise. - */ -- policy->cur = policy->min; -+ policy->cur = freq; - - mutex_unlock(&intel_pstate_limits_lock); - -@@ -2971,6 +2964,11 @@ static int intel_pstate_verify_policy(struct cpufreq_policy_data *policy) - return 0; - } - -+static void intel_pstate_set_min_pstate(struct cpudata *cpu) -+{ -+ intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate); -+} -+ - static int intel_cpufreq_cpu_offline(struct cpufreq_policy *policy) - { - struct cpudata *cpu = all_cpu_data[policy->cpu]; -@@ -3063,6 +3061,7 @@ static int __intel_pstate_cpu_init(struct cpufreq_policy *policy) - static int intel_pstate_cpu_init(struct cpufreq_policy *policy) - { - int ret = __intel_pstate_cpu_init(policy); -+ struct cpudata *cpu; - - if (ret) - return ret; -@@ -3073,11 +3072,11 @@ static int intel_pstate_cpu_init(struct cpufreq_policy *policy) - */ - policy->policy = CPUFREQ_POLICY_POWERSAVE; - -- if (hwp_active) { -- struct cpudata *cpu = all_cpu_data[policy->cpu]; -- -+ cpu = all_cpu_data[policy->cpu]; -+ if (hwp_active) - cpu->epp_cached = intel_pstate_get_epp(cpu, 0); -- } -+ else -+ intel_pstate_set_min_pstate(cpu); - - return 0; - } -@@ -3243,6 +3242,7 @@ static unsigned int intel_cpufreq_fast_switch(struct cpufreq_policy *policy, - static void intel_cpufreq_adjust_perf(struct cpufreq_policy *policy, - unsigned long min_perf, - unsigned long target_perf, -+ unsigned long max_perf, - unsigned long capacity) - { - struct cpudata *cpu = all_cpu_data[policy->cpu]; -@@ -3273,7 +3273,13 @@ static void intel_cpufreq_adjust_perf(struct cpufreq_policy *policy, - if (min_pstate > cpu->max_perf_ratio) - min_pstate = cpu->max_perf_ratio; - -- max_pstate = min(cap_pstate, cpu->max_perf_ratio); -+ max_pstate = cap_pstate; -+ if (max_perf < capacity) -+ max_pstate = DIV_ROUND_UP(cap_pstate * max_perf, capacity); -+ -+ if (max_pstate > cpu->max_perf_ratio) -+ max_pstate = cpu->max_perf_ratio; -+ - if (max_pstate < min_pstate) - max_pstate = min_pstate; - -@@ -3301,8 +3307,6 @@ static int intel_cpufreq_cpu_init(struct cpufreq_policy *policy) - return ret; - - policy->cpuinfo.transition_latency = INTEL_CPUFREQ_TRANSITION_LATENCY; -- /* This reflects the intel_pstate_get_cpu_pstates() setting. */ -- policy->cur = policy->cpuinfo.min_freq; - - req = kzalloc_objs(*req, 2); - if (!req) { -@@ -3323,9 +3327,15 @@ static int intel_cpufreq_cpu_init(struct cpufreq_policy *policy) - WRITE_ONCE(cpu->hwp_req_cached, value); - - cpu->epp_cached = intel_pstate_get_epp(cpu, value); -+ -+ intel_cpufreq_hwp_update(cpu, cpu->pstate.min_pstate, -+ cpu->pstate.max_pstate, -+ cpu->pstate.min_pstate, false); - } else { - policy->transition_delay_us = INTEL_CPUFREQ_TRANSITION_DELAY; -+ intel_pstate_set_min_pstate(cpu); - } -+ policy->cur = policy->cpuinfo.min_freq; - - freq = DIV_ROUND_UP(cpu->pstate.turbo_freq * global.min_perf_pct, 100); - -@@ -3676,14 +3686,14 @@ static inline bool intel_pstate_has_acpi_ppc(void) { return false; } - static inline void intel_pstate_request_control_from_smm(void) {} - #endif /* CONFIG_ACPI */ - --#define INTEL_PSTATE_HWP_BROADWELL 0x01 -+#define INTEL_PSTATE_HWP_NOT_HYBRID 0x01 - - #define X86_MATCH_HWP(vfm, hwp_mode) \ - X86_MATCH_VFM_FEATURE(vfm, X86_FEATURE_HWP, hwp_mode) - - static const struct x86_cpu_id hwp_support_ids[] __initconst = { -- X86_MATCH_HWP(INTEL_BROADWELL_X, INTEL_PSTATE_HWP_BROADWELL), -- X86_MATCH_HWP(INTEL_BROADWELL_D, INTEL_PSTATE_HWP_BROADWELL), -+ X86_MATCH_HWP(INTEL_BROADWELL_X, INTEL_PSTATE_HWP_NOT_HYBRID), -+ X86_MATCH_HWP(INTEL_BROADWELL_D, INTEL_PSTATE_HWP_NOT_HYBRID), - X86_MATCH_HWP(INTEL_ANY, 0), - {} - }; -@@ -3808,7 +3818,6 @@ static int __init intel_pstate_init(void) - - if (!no_hwp) { - hwp_active = true; -- hwp_mode_bdw = id->driver_data; - intel_pstate.attr = hwp_cpufreq_attrs; - intel_cpufreq.attr = hwp_cpufreq_attrs; - intel_cpufreq.flags |= CPUFREQ_NEED_UPDATE_LIMITS; -@@ -3816,7 +3825,8 @@ static int __init intel_pstate_init(void) - if (!default_driver) - default_driver = &intel_pstate; - -- pstate_funcs.get_cpu_scaling = hwp_get_cpu_scaling; -+ if (!id->driver_data) -+ pstate_funcs.get_cpu_scaling = hwp_get_cpu_scaling; - - goto hwp_cpu_matched; - } -diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h ---- a/include/linux/cpufreq.h -+++ b/include/linux/cpufreq.h -@@ -379,6 +379,7 @@ struct cpufreq_driver { - void (*adjust_perf)(struct cpufreq_policy *policy, - unsigned long min_perf, - unsigned long target_perf, -+ unsigned long max_perf, - unsigned long capacity); - - /* -@@ -624,6 +625,7 @@ unsigned int cpufreq_driver_fast_switch(struct cpufreq_policy *policy, - void cpufreq_driver_adjust_perf(struct cpufreq_policy *policy, - unsigned long min_perf, - unsigned long target_perf, -+ unsigned long max_perf, - unsigned long capacity); - bool cpufreq_driver_has_adjust_perf(void); - int cpufreq_driver_target(struct cpufreq_policy *policy, -diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c ---- a/kernel/sched/cpufreq_schedutil.c -+++ b/kernel/sched/cpufreq_schedutil.c -@@ -50,6 +50,7 @@ struct sugov_cpu { - - unsigned long util; - unsigned long bw_min; -+ unsigned long bw_max; - - /* The field below is for single-CPU policies only: */ - #ifdef CONFIG_NO_HZ_COMMON -@@ -243,6 +244,7 @@ static void sugov_get_util(struct sugov_cpu *sg_cpu, unsigned long boost) - util = effective_cpu_util(sg_cpu->cpu, util, &min, &max); - util = max(util, boost); - sg_cpu->bw_min = min; -+ sg_cpu->bw_max = max; - sg_cpu->util = sugov_effective_cpu_perf(sg_cpu->cpu, util, min, max); - } - -@@ -495,7 +497,7 @@ static void sugov_update_single_perf(struct update_util_data *hook, u64 time, - sg_cpu->util = prev_util; - - cpufreq_driver_adjust_perf(sg_policy->policy, sg_cpu->bw_min, -- sg_cpu->util, max_cap); -+ sg_cpu->util, sg_cpu->bw_max, max_cap); - - sg_policy->need_freq_update = false; - sg_policy->last_freq_update_time = time; -diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs ---- a/rust/kernel/cpufreq.rs -+++ b/rust/kernel/cpufreq.rs -@@ -792,7 +792,13 @@ fn fast_switch(_policy: &mut Policy, _target_freq: u32) -> u32 { - } - - /// Driver's `adjust_perf` callback. -- fn adjust_perf(_policy: &mut Policy, _min_perf: usize, _target_perf: usize, _capacity: usize) { -+ fn adjust_perf( -+ _policy: &mut Policy, -+ _min_perf: usize, -+ _target_perf: usize, -+ _max_perf: usize, -+ _capacity: usize, -+ ) { - build_error!(VTABLE_DEFAULT_ERROR) - } - -@@ -1263,12 +1269,13 @@ impl Registration { - ptr: *mut bindings::cpufreq_policy, - min_perf: c_ulong, - target_perf: c_ulong, -+ max_perf: c_ulong, - capacity: c_ulong, - ) { - // SAFETY: The `ptr` is guaranteed to be valid by the contract with the C code for the - // lifetime of `policy`. - let policy = unsafe { Policy::from_raw_mut(ptr) }; -- T::adjust_perf(policy, min_perf, target_perf, capacity); -+ T::adjust_perf(policy, min_perf, target_perf, max_perf, capacity); - } - - /// Driver's `get_intermediate` callback. diff --git a/pkgbuilds/linux-omarchy-bore/0210-pstate.patch.sig b/pkgbuilds/linux-omarchy-bore/0210-pstate.patch.sig deleted file mode 100644 index 463f061..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/0210-pstate.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/0211-amd-pstate-fixes.patch b/pkgbuilds/linux-omarchy-bore/0211-amd-pstate-fixes.patch deleted file mode 100644 index 67a6cf4..0000000 --- a/pkgbuilds/linux-omarchy-bore/0211-amd-pstate-fixes.patch +++ /dev/null @@ -1,127 +0,0 @@ -diff --git a/drivers/cpufreq/amd-pstate-ut.c b/drivers/cpufreq/amd-pstate-ut.c ---- a/drivers/cpufreq/amd-pstate-ut.c -+++ b/drivers/cpufreq/amd-pstate-ut.c -@@ -560,6 +560,11 @@ static int amd_pstate_ut_check_freq_attrs(u32 index) - static int __init amd_pstate_ut_init(void) - { - u32 i = 0, arr_size = ARRAY_SIZE(amd_pstate_ut_cases); -+ enum amd_pstate_mode mode = amd_pstate_get_status(); -+ -+ /* don't test if no running amd-pstate driver */ -+ if (mode == AMD_PSTATE_UNDEFINED || mode == AMD_PSTATE_DISABLE) -+ return -EOPNOTSUPP; - - for (i = 0; i < arr_size; i++) { - int ret; -diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c ---- a/drivers/cpufreq/amd-pstate.c -+++ b/drivers/cpufreq/amd-pstate.c -@@ -199,7 +199,7 @@ static inline int get_mode_idx_from_str(const char *str, size_t size) - - static DEFINE_MUTEX(amd_pstate_driver_lock); - --static u8 msr_get_epp(struct amd_cpudata *cpudata) -+static int msr_get_epp(struct amd_cpudata *cpudata) - { - u64 value; - int ret; -@@ -215,12 +215,12 @@ static u8 msr_get_epp(struct amd_cpudata *cpudata) - - DEFINE_STATIC_CALL(amd_pstate_get_epp, msr_get_epp); - --static inline s16 amd_pstate_get_epp(struct amd_cpudata *cpudata) -+static inline int amd_pstate_get_epp(struct amd_cpudata *cpudata) - { - return static_call(amd_pstate_get_epp)(cpudata); - } - --static u8 shmem_get_epp(struct amd_cpudata *cpudata) -+static int shmem_get_epp(struct amd_cpudata *cpudata) - { - u64 epp; - int ret; -@@ -526,9 +526,6 @@ static int shmem_init_perf(struct amd_cpudata *cpudata) - WRITE_ONCE(cpudata->perf, perf); - WRITE_ONCE(cpudata->prefcore_ranking, cppc_perf.highest_perf); - -- if (cppc_state == AMD_PSTATE_ACTIVE) -- return 0; -- - ret = cppc_get_auto_sel(cpudata->cpu, &auto_sel); - if (ret) { - pr_warn("failed to get auto_sel, ret: %d\n", ret); -@@ -1174,6 +1171,9 @@ static int amd_pstate_power_supply_notifier(struct notifier_block *nb, - if (cpudata->current_profile != PLATFORM_PROFILE_BALANCED) - return 0; - -+ if (!policy) -+ return NOTIFY_OK; -+ - epp = amd_pstate_get_balanced_epp(policy); - - ret = amd_pstate_set_epp(policy, epp); -@@ -1209,6 +1209,9 @@ static int amd_pstate_profile_set(struct device *dev, - struct cpufreq_policy *policy __free(put_cpufreq_policy) = cpufreq_cpu_get(cpudata->cpu); - int ret; - -+ if (!policy) -+ return -ENODEV; -+ - switch (profile) { - case PLATFORM_PROFILE_LOW_POWER: - ret = amd_pstate_set_epp(policy, AMD_CPPC_EPP_POWERSAVE); -@@ -1877,6 +1880,7 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) - struct amd_cpudata *cpudata; - union perf_cached perf; - struct device *dev; -+ int default_epp; - int ret; - - /* -@@ -1925,6 +1929,13 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) - - policy->boost_supported = READ_ONCE(cpudata->boost_supported); - -+ /* Fetch the firmware programmed default EPP value */ -+ default_epp = amd_pstate_get_epp(cpudata); -+ if (default_epp < 0) { -+ ret = default_epp; -+ goto free_cpudata1; -+ } -+ - /* - * Set the policy to provide a valid fallback value in case - * the default cpufreq governor is neither powersave nor performance. -@@ -1932,7 +1943,7 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) - if (amd_pstate_acpi_pm_profile_server() || - amd_pstate_acpi_pm_profile_undefined()) { - policy->policy = CPUFREQ_POLICY_PERFORMANCE; -- cpudata->epp_default_ac = cpudata->epp_default_dc = amd_pstate_get_epp(cpudata); -+ cpudata->epp_default_ac = cpudata->epp_default_dc = default_epp; - cpudata->current_profile = PLATFORM_PROFILE_PERFORMANCE; - } else { - policy->policy = CPUFREQ_POLICY_POWERSAVE; -diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h ---- a/drivers/cpufreq/amd-pstate.h -+++ b/drivers/cpufreq/amd-pstate.h -@@ -32,6 +32,7 @@ - * @min_limit_perf: Cached value of the performance corresponding to policy->min - * @max_limit_perf: Cached value of the performance corresponding to policy->max - * @bios_min_perf: Cached perf value corresponding to the "Requested CPU Min Frequency" BIOS option -+ * @val: Raw 64-bit value for atomic access via READ_ONCE()/WRITE_ONCE() - */ - union perf_cached { - struct { -@@ -89,7 +90,12 @@ struct amd_aperf_mperf { - * @epp_default_ac: Default EPP value for AC power source - * @epp_default_dc: Default EPP value for DC power source - * @dynamic_epp: Whether dynamic EPP is enabled -+ * @raw_epp: Whether the last EPP write was a raw numeric value rather than a -+ * named preference - * @power_nb: Notifier block for power events -+ * @current_profile: Currently selected platform profile option -+ * @ppdev: Device registered with the platform profile handler -+ * @profile_name: Name under which @ppdev is registered - * - * The amd_cpudata is key private data for each CPU thread in AMD P-State, and - * represents all the attributes and goals that AMD P-State requests at runtime. diff --git a/pkgbuilds/linux-omarchy-bore/0211-amd-pstate-fixes.patch.sig b/pkgbuilds/linux-omarchy-bore/0211-amd-pstate-fixes.patch.sig deleted file mode 100644 index 018259f..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/0211-amd-pstate-fixes.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/0211-pm-fixes-1.patch b/pkgbuilds/linux-omarchy-bore/0211-pm-fixes-1.patch new file mode 100644 index 0000000..ed2d0f5 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0211-pm-fixes-1.patch @@ -0,0 +1,104 @@ +diff --git a/drivers/opp/core.c b/drivers/opp/core.c +--- a/drivers/opp/core.c ++++ b/drivers/opp/core.c +@@ -453,8 +453,8 @@ int dev_pm_opp_get_opp_count(struct device *dev) + _find_opp_table(dev); + + if (IS_ERR(opp_table)) { +- dev_dbg(dev, "%s: OPP table not found (%ld)\n", +- __func__, PTR_ERR(opp_table)); ++ dev_dbg(dev, "%s: OPP table not found (%pe)\n", ++ __func__, opp_table); + return PTR_ERR(opp_table); + } + +@@ -611,8 +611,8 @@ _find_key(struct device *dev, unsigned long *key, int index, bool available, + _find_opp_table(dev); + + if (IS_ERR(opp_table)) { +- dev_err(dev, "%s: OPP table not found (%ld)\n", __func__, +- PTR_ERR(opp_table)); ++ dev_err(dev, "%s: OPP table not found (%pe)\n", __func__, ++ opp_table); + return ERR_CAST(opp_table); + } + +@@ -722,8 +722,8 @@ struct dev_pm_opp *dev_pm_opp_find_key_exact(struct device *dev, + struct opp_table *opp_table __free(put_opp_table) = _find_opp_table(dev); + + if (IS_ERR(opp_table)) { +- dev_err(dev, "%s: OPP table not found (%ld)\n", __func__, +- PTR_ERR(opp_table)); ++ dev_err(dev, "%s: OPP table not found (%pe)\n", __func__, ++ opp_table); + return ERR_CAST(opp_table); + } + +@@ -1036,8 +1036,8 @@ static int _set_opp_voltage(struct device *dev, struct regulator *reg, + + /* Regulator not available for device */ + if (IS_ERR(reg)) { +- dev_dbg(dev, "%s: regulator not available: %ld\n", __func__, +- PTR_ERR(reg)); ++ dev_dbg(dev, "%s: regulator not available: %pe\n", __func__, ++ reg); + return 0; + } + +@@ -1448,8 +1448,8 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) + temp_freq = freq; + opp = _find_freq_ceil(opp_table, &temp_freq); + if (IS_ERR(opp)) { +- dev_err(dev, "%s: failed to find OPP for freq %lu (%ld)\n", +- __func__, freq, PTR_ERR(opp)); ++ dev_err(dev, "%s: failed to find OPP for freq %lu (%pe)\n", ++ __func__, freq, opp); + return PTR_ERR(opp); + } + +@@ -1581,6 +1581,8 @@ static struct opp_table *_update_opp_table_clk(struct device *dev, + struct opp_table *opp_table, + bool getclk) + { ++ int ret; ++ + /* + * Return early if we don't need to get clk or we have already done it + * earlier. +@@ -1607,9 +1609,9 @@ static struct opp_table *_update_opp_table_clk(struct device *dev, + opp_table->clk = clk_get_optional(dev, NULL); + + if (IS_ERR(opp_table->clk)) { ++ ret = dev_err_probe(dev, PTR_ERR(opp_table->clk), "Couldn't find clock\n"); + dev_pm_opp_put_opp_table(opp_table); +- dev_err_probe(dev, PTR_ERR(opp_table->clk), "Couldn't find clock\n"); +- return ERR_CAST(opp_table->clk); ++ return ERR_PTR(ret); + } + + if (opp_table->clk) +@@ -2869,8 +2871,8 @@ static int _opp_set_availability(struct device *dev, unsigned long freq, + struct dev_pm_opp *opp __free(put_opp) = ERR_PTR(-ENODEV), *tmp_opp; + + if (IS_ERR(opp_table)) { +- dev_warn(dev, "%s: Device OPP not found (%ld)\n", __func__, +- PTR_ERR(opp_table)); ++ dev_warn(dev, "%s: Device OPP not found (%pe)\n", __func__, ++ opp_table); + return PTR_ERR(opp_table); + } + +diff --git a/drivers/opp/of.c b/drivers/opp/of.c +--- a/drivers/opp/of.c ++++ b/drivers/opp/of.c +@@ -1345,8 +1345,8 @@ int of_get_required_opp_performance_state(struct device_node *np, int index) + _find_table_of_opp_np(required_np); + + if (IS_ERR(opp_table)) { +- pr_err("%s: Failed to find required OPP table %pOF: %ld\n", +- __func__, np, PTR_ERR(opp_table)); ++ pr_err("%s: Failed to find required OPP table %pOF: %pe\n", ++ __func__, np, opp_table); + return PTR_ERR(opp_table); + } + diff --git a/pkgbuilds/linux-omarchy-bore/0211-pm-fixes-1.patch.sig b/pkgbuilds/linux-omarchy-bore/0211-pm-fixes-1.patch.sig new file mode 100644 index 0000000..3282089 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0211-pm-fixes-1.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0212-amd-pstate-epp-cache.patch b/pkgbuilds/linux-omarchy-bore/0212-amd-pstate-epp-cache.patch deleted file mode 100644 index 61ea14c..0000000 --- a/pkgbuilds/linux-omarchy-bore/0212-amd-pstate-epp-cache.patch +++ /dev/null @@ -1,18 +0,0 @@ -diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c ---- a/drivers/cpufreq/amd-pstate.c -+++ b/drivers/cpufreq/amd-pstate.c -@@ -1929,12 +1929,13 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) - - policy->boost_supported = READ_ONCE(cpudata->boost_supported); - -- /* Fetch the firmware programmed default EPP value */ -+ /* Cache the firmware programmed EPP */ - default_epp = amd_pstate_get_epp(cpudata); - if (default_epp < 0) { - ret = default_epp; - goto free_cpudata1; - } -+ FIELD_MODIFY(AMD_CPPC_EPP_PERF_MASK, &cpudata->cppc_req_cached, default_epp); - - /* - * Set the policy to provide a valid fallback value in case diff --git a/pkgbuilds/linux-omarchy-bore/0212-amd-pstate-epp-cache.patch.sig b/pkgbuilds/linux-omarchy-bore/0212-amd-pstate-epp-cache.patch.sig deleted file mode 100644 index ef0e5da..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/0212-amd-pstate-epp-cache.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/0212-pm-fixes-2.patch b/pkgbuilds/linux-omarchy-bore/0212-pm-fixes-2.patch new file mode 100644 index 0000000..0e9c073 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0212-pm-fixes-2.patch @@ -0,0 +1,54 @@ +diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c +--- a/kernel/power/hibernate.c ++++ b/kernel/power/hibernate.c +@@ -408,9 +408,18 @@ int hibernation_snapshot(int platform_mode) + if (error) + goto Close; + ++ error = dpm_prepare(PMSG_FREEZE); ++ if (error) ++ goto Complete; ++ ++ /* Preallocate image memory before freezing kernel threads and shutting down devices. */ ++ error = hibernate_preallocate_memory(); ++ if (error) ++ goto Complete; ++ + error = freeze_kernel_threads(); + if (error) +- goto Close; ++ goto Cleanup; + + if (hibernation_test(TEST_FREEZER)) { + +@@ -422,15 +431,6 @@ int hibernation_snapshot(int platform_mode) + goto Thaw; + } + +- error = dpm_prepare(PMSG_FREEZE); +- if (error) +- goto Complete; +- +- /* Preallocate image memory before shutting down devices. */ +- error = hibernate_preallocate_memory(); +- if (error) +- goto Complete; +- + console_suspend_all(); + pm_restrict_gfp_mask(); + +@@ -464,10 +464,12 @@ int hibernation_snapshot(int platform_mode) + platform_end(platform_mode); + return error; + +- Complete: +- dpm_complete(PMSG_RECOVER); + Thaw: + thaw_kernel_threads(); ++ Cleanup: ++ swsusp_free(); ++ Complete: ++ dpm_complete(PMSG_RECOVER); + goto Close; + } + diff --git a/pkgbuilds/linux-omarchy-bore/0212-pm-fixes-2.patch.sig b/pkgbuilds/linux-omarchy-bore/0212-pm-fixes-2.patch.sig new file mode 100644 index 0000000..679057c Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0212-pm-fixes-2.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0213-pm-fixes-3.patch b/pkgbuilds/linux-omarchy-bore/0213-pm-fixes-3.patch new file mode 100644 index 0000000..e8c1523 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0213-pm-fixes-3.patch @@ -0,0 +1,61 @@ +diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c +--- a/drivers/cpufreq/cpufreq.c ++++ b/drivers/cpufreq/cpufreq.c +@@ -2590,8 +2590,8 @@ static void cpufreq_update_pressure(struct cpufreq_policy *policy) + + cpu = cpumask_first(policy->related_cpus); + max_freq = arch_scale_freq_ref(cpu); +- if (!max_freq) +- max_freq = policy->cpuinfo.max_freq; ++ if (!max_freq && cpufreq_driver->scale_freq_ref) ++ max_freq = cpufreq_driver->scale_freq_ref(policy); + + capped_freq = policy->max; + +diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c +--- a/drivers/cpufreq/intel_pstate.c ++++ b/drivers/cpufreq/intel_pstate.c +@@ -1175,6 +1175,14 @@ static bool hybrid_clear_max_perf_cpu(void) + return ret; + } + ++static unsigned int intel_pstate_scale_freq_ref(struct cpufreq_policy *policy) ++{ ++ if (READ_ONCE(all_cpu_data[policy->cpu]->capacity_perf)) ++ return policy->cpuinfo.max_freq; ++ ++ return 0; ++} ++ + static void intel_pstate_update_freq_limits(struct cpudata *cpu) + { + int scaling = cpu->pstate.scaling; +@@ -3092,6 +3100,7 @@ static struct cpufreq_driver intel_pstate = { + .offline = intel_pstate_cpu_offline, + .online = intel_pstate_cpu_online, + .update_limits = intel_pstate_update_limits, ++ .scale_freq_ref = intel_pstate_scale_freq_ref, + .name = "intel_pstate", + }; + +@@ -3415,6 +3424,7 @@ static struct cpufreq_driver intel_cpufreq = { + .suspend = intel_cpufreq_suspend, + .resume = intel_pstate_resume, + .update_limits = intel_pstate_update_limits, ++ .scale_freq_ref = intel_pstate_scale_freq_ref, + .name = "intel_cpufreq", + }; + +diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h +--- a/include/linux/cpufreq.h ++++ b/include/linux/cpufreq.h +@@ -420,6 +420,9 @@ struct cpufreq_driver { + /* Will be called after the driver is fully initialized */ + void (*ready)(struct cpufreq_policy *policy); + ++ /* Return the capacity reference frequency for policy. */ ++ unsigned int (*scale_freq_ref)(struct cpufreq_policy *policy); ++ + struct freq_attr **attr; + + /* platform specific boost support code */ diff --git a/pkgbuilds/linux-omarchy-bore/0213-pm-fixes-3.patch.sig b/pkgbuilds/linux-omarchy-bore/0213-pm-fixes-3.patch.sig new file mode 100644 index 0000000..caad5f0 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0213-pm-fixes-3.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0280-mm-updates.patch b/pkgbuilds/linux-omarchy-bore/0280-mm-updates.patch index 46a53c5..e0bf148 100644 --- a/pkgbuilds/linux-omarchy-bore/0280-mm-updates.patch +++ b/pkgbuilds/linux-omarchy-bore/0280-mm-updates.patch @@ -278,25 +278,7 @@ diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h -@@ -270,10 +270,15 @@ struct mem_cgroup { - #endif - int kmemcg_id; - -- struct memcg_vmstats_percpu __percpu *vmstats_percpu; -- - #ifdef CONFIG_CGROUP_WRITEBACK - struct list_head cgwb_list; -+#endif -+ -+ /* Keep the hot per-CPU stats pointer away from memory event counters. */ -+ struct memcg_vmstats_percpu __percpu *vmstats_percpu -+ ____cacheline_aligned_in_smp; -+ -+#ifdef CONFIG_CGROUP_WRITEBACK - struct wb_domain cgwb_domain; - struct memcg_cgwb_frn cgwb_frn[MEMCG_CGWB_FRN_CNT]; - #endif -@@ -947,6 +952,8 @@ unsigned long memcg_page_state_output(struct mem_cgroup *memcg, int item); +@@ -952,6 +952,8 @@ unsigned long memcg_page_state_output(struct mem_cgroup *memcg, int item); bool memcg_stat_item_valid(int idx); bool memcg_vm_event_item_valid(enum vm_event_item idx); unsigned long lruvec_page_state(struct lruvec *lruvec, enum node_stat_item idx); @@ -305,7 +287,7 @@ diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h unsigned long lruvec_page_state_local(struct lruvec *lruvec, enum node_stat_item idx); -@@ -1399,6 +1406,12 @@ static inline unsigned long lruvec_page_state(struct lruvec *lruvec, +@@ -1404,6 +1406,12 @@ static inline unsigned long lruvec_page_state(struct lruvec *lruvec, return node_page_state(lruvec_pgdat(lruvec), idx); } @@ -576,66 +558,6 @@ diff --git a/lib/maple_tree.c b/lib/maple_tree.c return wr_slot_store; return wr_node_store; -@@ -3793,35 +3825,40 @@ int mas_alloc_cyclic(struct ma_state *mas, unsigned long *startp, - void *entry, unsigned long range_lo, unsigned long range_hi, - unsigned long *next, gfp_t gfp) - { -- unsigned long min = range_lo; -- int ret = 0; -+ int ret; -+ unsigned long min; -+ -+ min = range_lo; -+ do { -+ range_lo = max(min, *next); -+ ret = mas_empty_area(mas, range_lo, range_hi, 1); -+ if (ret < 0 && range_lo > min) { -+ mas_reset(mas); -+ ret = mas_empty_area(mas, min, range_hi, 1); -+ if (ret == 0) -+ ret = 1; -+ } -+ if (ret < 0) -+ goto out; -+ -+ mas_insert(mas, entry); -+ } while (mas_nomem(mas, gfp)); -+ -+ if (mas_is_err(mas)) { -+ ret = xa_err(mas->node); -+ goto out; -+ } - -- range_lo = max(min, *next); -- ret = mas_empty_area(mas, range_lo, range_hi, 1); - if ((mas->tree->ma_flags & MT_FLAGS_ALLOC_WRAPPED) && ret == 0) { - mas->tree->ma_flags &= ~MT_FLAGS_ALLOC_WRAPPED; - ret = 1; - } -- if (ret < 0 && range_lo > min) { -- mas_reset(mas); -- ret = mas_empty_area(mas, min, range_hi, 1); -- if (ret == 0) -- ret = 1; -- } -- if (ret < 0) -- return ret; -- -- do { -- mas_insert(mas, entry); -- } while (mas_nomem(mas, gfp)); -- if (mas_is_err(mas)) -- return xa_err(mas->node); -- - *startp = mas->index; - *next = *startp + 1; - if (*next == 0) - mas->tree->ma_flags |= MT_FLAGS_ALLOC_WRAPPED; - -+out: - mas_destroy(mas); - return ret; - } diff --git a/mm/khugepaged.c b/mm/khugepaged.c --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -898,24 +820,7 @@ diff --git a/mm/swap.c b/mm/swap.c static void lru_activate(struct lruvec *lruvec, struct folio *folio) { long nr_pages = folio_nr_pages(folio); -@@ -1153,7 +1086,16 @@ static void lruvec_reparent_lru(struct lruvec *child_lruvec, - for_each_managed_zone_pgdat(zone, NODE_DATA(nid), zid, MAX_NR_ZONES - 1) { - unsigned long size = mem_cgroup_get_zone_lru_size(child_lruvec, lru, zid); - -+ if (!size) -+ continue; -+ -+ /* -+ * The folios are accounted to the parent from now on, so the -+ * size has to be moved, not just copied. Leaving it behind -+ * makes the dying child describe folios it no longer owns. -+ */ - mem_cgroup_update_lru_size(parent_lruvec, lru, zid, size); -+ mem_cgroup_update_lru_size(child_lruvec, lru, zid, -(long)size); - } - } - -@@ -1164,8 +1106,6 @@ void lru_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, int +@@ -1173,8 +1106,6 @@ void lru_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, int child_lruvec = mem_cgroup_lruvec(memcg, NODE_DATA(nid)); parent_lruvec = mem_cgroup_lruvec(parent, NODE_DATA(nid)); @@ -1129,20 +1034,7 @@ diff --git a/mm/vmscan.c b/mm/vmscan.c pte_t *pte = pvmw->pte; unsigned long addr = pvmw->address; struct vm_area_struct *vma = pvmw->vma; -@@ -4571,7 +4629,12 @@ void lru_gen_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, - for_each_managed_zone_pgdat(zone, NODE_DATA(nid), zid, MAX_NR_ZONES - 1) { - unsigned long size = mem_cgroup_get_zone_lru_size(child_lruvec, lru, zid); - -+ if (!size) -+ continue; -+ -+ /* Move the accounting, do not duplicate it. */ - mem_cgroup_update_lru_size(parent_lruvec, lru, zid, size); -+ mem_cgroup_update_lru_size(child_lruvec, lru, zid, -(long)size); - } - } - } -@@ -4814,7 +4877,8 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, +@@ -4819,7 +4877,8 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, struct reclaim_stat stat; struct lru_gen_mm_walk *walk; int scanned, reclaimed; @@ -1152,7 +1044,7 @@ diff --git a/mm/vmscan.c b/mm/vmscan.c bool skip_retry = false; struct mem_cgroup *memcg = lruvec_memcg(lruvec); struct pglist_data *pgdat = lruvec_pgdat(lruvec); -@@ -4826,6 +4890,7 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, +@@ -4831,6 +4890,7 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, scanned = isolate_folios(nr_to_scan, lruvec, sc, swappiness, &list, &isolated, &type, &type_scanned); @@ -1160,7 +1052,7 @@ diff --git a/mm/vmscan.c b/mm/vmscan.c /* Scanning may have emptied the oldest gen, flush it */ if (scanned) -@@ -4838,6 +4903,7 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, +@@ -4843,6 +4903,7 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, retry: reclaimed = shrink_folio_list(&list, pgdat, sc, &stat, false, memcg); sc->nr_reclaimed += reclaimed; @@ -1168,7 +1060,7 @@ diff --git a/mm/vmscan.c b/mm/vmscan.c /* Retry pass is only meant for clean folios without new isolation */ if (isolated) handle_reclaim_writeback(isolated, pgdat, sc, &stat); -@@ -4887,6 +4953,10 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, +@@ -4892,6 +4953,10 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, goto retry; } diff --git a/pkgbuilds/linux-omarchy-bore/0280-mm-updates.patch.sig b/pkgbuilds/linux-omarchy-bore/0280-mm-updates.patch.sig index 4e1b338..97d0386 100644 Binary files a/pkgbuilds/linux-omarchy-bore/0280-mm-updates.patch.sig and b/pkgbuilds/linux-omarchy-bore/0280-mm-updates.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0281-mm-no-deferred-kernel-page-table-free-at-boot.patch b/pkgbuilds/linux-omarchy-bore/0281-mm-no-deferred-kernel-page-table-free-at-boot.patch new file mode 100644 index 0000000..e8e9809 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0281-mm-no-deferred-kernel-page-table-free-at-boot.patch @@ -0,0 +1,35 @@ +diff --git a/mm/pgtable-generic.c b/mm/pgtable-generic.c +--- a/mm/pgtable-generic.c ++++ b/mm/pgtable-generic.c +@@ -438,12 +438,30 @@ static void kernel_pgtable_work_func(struct work_struct *work) + __pagetable_free(pt); + } + ++static void schedule_kernel_pgtable_free(void) ++{ ++ schedule_work(&kernel_pgtable_work.work); ++} ++ + void pagetable_free_kernel(struct ptdesc *pt) + { + spin_lock(&kernel_pgtable_work.lock); + list_add(&pt->pt_list, &kernel_pgtable_work.list); + spin_unlock(&kernel_pgtable_work.lock); + +- schedule_work(&kernel_pgtable_work.work); ++ /* ++ * The workqueue may not exist yet while the system is booting. ++ * kernel_pgtable_drain_early() schedules the work once it does. ++ */ ++ if (system_state != SYSTEM_BOOTING) ++ schedule_kernel_pgtable_free(); + } ++ ++static int __init kernel_pgtable_drain_early(void) ++{ ++ /* Free the kernel page tables queued while booting. */ ++ schedule_kernel_pgtable_free(); ++ return 0; ++} ++core_initcall(kernel_pgtable_drain_early); + #endif diff --git a/pkgbuilds/linux-omarchy-bore/0281-mm-no-deferred-kernel-page-table-free-at-boot.patch.sig b/pkgbuilds/linux-omarchy-bore/0281-mm-no-deferred-kernel-page-table-free-at-boot.patch.sig new file mode 100644 index 0000000..d9c680c Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0281-mm-no-deferred-kernel-page-table-free-at-boot.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0296-x86-mm-pmd-modify-keep-dirty-bit.patch b/pkgbuilds/linux-omarchy-bore/0296-x86-mm-pmd-modify-keep-dirty-bit.patch deleted file mode 100644 index 602ef86..0000000 --- a/pkgbuilds/linux-omarchy-bore/0296-x86-mm-pmd-modify-keep-dirty-bit.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h ---- a/arch/x86/include/asm/pgtable.h -+++ b/arch/x86/include/asm/pgtable.h -@@ -806,7 +806,7 @@ static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot) - pmdval_t val = pmd_val(pmd), oldval = val; - pmd_t pmd_result; - -- val &= (_HPAGE_CHG_MASK & ~_PAGE_DIRTY); -+ val &= _HPAGE_CHG_MASK; - val |= check_pgprot(newprot) & ~_HPAGE_CHG_MASK; - val = flip_protnone_guard(oldval, val, PHYSICAL_PMD_PAGE_MASK); - diff --git a/pkgbuilds/linux-omarchy-bore/0296-x86-mm-pmd-modify-keep-dirty-bit.patch.sig b/pkgbuilds/linux-omarchy-bore/0296-x86-mm-pmd-modify-keep-dirty-bit.patch.sig deleted file mode 100644 index b5e1f0f..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/0296-x86-mm-pmd-modify-keep-dirty-bit.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/0300-btrfs.patch b/pkgbuilds/linux-omarchy-bore/0300-btrfs.patch index 0cb9f8d..837f7bc 100644 --- a/pkgbuilds/linux-omarchy-bore/0300-btrfs.patch +++ b/pkgbuilds/linux-omarchy-bore/0300-btrfs.patch @@ -1,50 +1,7 @@ -diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c ---- a/fs/btrfs/acl.c -+++ b/fs/btrfs/acl.c -@@ -15,6 +15,7 @@ - #include "xattr.h" - #include "acl.h" - #include "misc.h" -+#include "btrfs_inode.h" - - struct posix_acl *btrfs_get_acl(struct inode *inode, int type, bool rcu) - { -@@ -107,6 +108,9 @@ int btrfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, - struct inode *inode = d_inode(dentry); - umode_t old_mode = inode->i_mode; - -+ if (btrfs_root_readonly(BTRFS_I(inode)->root)) -+ return -EROFS; -+ - if (type == ACL_TYPE_ACCESS && acl) { - ret = posix_acl_update_mode(idmap, inode, - &inode->i_mode, &acl); diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c -@@ -2047,6 +2047,11 @@ static int btrfs_reclaim_block_group(struct btrfs_block_group *bg, int *reclaime - - trace_btrfs_reclaim_block_group(bg); - ret = btrfs_relocate_chunk(fs_info, bg->start, false); -+ if (btrfs_is_zoned(fs_info) && ret == -EAGAIN) { -+ btrfs_dec_block_group_ro(bg); -+ btrfs_debug(fs_info, "deferring reclaim of chunk %llu", bg->start); -+ return ret; -+ } - if (ret) { - btrfs_dec_block_group_ro(bg); - btrfs_err(fs_info, "error relocating chunk %llu", -@@ -2113,7 +2118,8 @@ void btrfs_reclaim_block_groups(struct btrfs_fs_info *fs_info, unsigned int limi - spin_unlock(&fs_info->unused_bgs_lock); - ret = btrfs_reclaim_block_group(bg, &reclaimed); - -- if (ret && !READ_ONCE(space_info->periodic_reclaim)) -+ if ((btrfs_is_zoned(fs_info) && ret == -EAGAIN) || -+ (ret && !READ_ONCE(space_info->periodic_reclaim))) - btrfs_link_bg_list(bg, &retry_list); - btrfs_put_block_group(bg); - -@@ -2624,10 +2630,9 @@ static int fill_dummy_bgs(struct btrfs_fs_info *fs_info) +@@ -2630,10 +2630,9 @@ static int fill_dummy_bgs(struct btrfs_fs_info *fs_info) /* Fill dummy cache as FULL */ bg->length = map->chunk_len; @@ -56,7 +13,7 @@ diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c bg->space_info = btrfs_find_space_info(fs_info, bg->flags); ret = btrfs_add_block_group_cache(bg); /* -@@ -3916,7 +3921,7 @@ int btrfs_update_block_group(struct btrfs_trans_handle *trans, +@@ -3922,7 +3921,7 @@ int btrfs_update_block_group(struct btrfs_trans_handle *trans, old_val += num_bytes; cache->used = old_val; cache->reserved -= num_bytes; @@ -311,43 +268,7 @@ diff --git a/fs/btrfs/defrag.c b/fs/btrfs/defrag.c kfree(entry); } } -@@ -1130,20 +1130,15 @@ static_assert(PAGE_ALIGNED(CLUSTER_SIZE)); - * - * - Extent bits are locked - */ --static int defrag_one_locked_target(struct btrfs_inode *inode, -- struct defrag_target_range *target, -- struct folio **folios, int nr_pages, -- struct extent_state **cached_state) -+static void defrag_one_locked_target(struct btrfs_inode *inode, -+ struct defrag_target_range *target, -+ struct folio **folios, int nr_pages, -+ struct extent_state **cached_state) - { - struct btrfs_fs_info *fs_info = inode->root->fs_info; -- struct extent_changeset *data_reserved = NULL; - const u64 start = target->start; - const u64 len = target->len; -- int ret = 0; - -- ret = btrfs_delalloc_reserve_space(inode, &data_reserved, start, len); -- if (ret < 0) -- return ret; - btrfs_clear_extent_bit(&inode->io_tree, start, start + len - 1, - EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING | - EXTENT_DEFRAG, cached_state); -@@ -1164,10 +1159,6 @@ static int defrag_one_locked_target(struct btrfs_inode *inode, - continue; - btrfs_folio_clamp_set_dirty(fs_info, folio, start, len); - } -- btrfs_delalloc_release_extents(inode, len); -- extent_changeset_free(data_reserved); -- -- return ret; - } - - static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len, -@@ -1178,11 +1169,13 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len, +@@ -1169,7 +1169,7 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len, struct defrag_target_range *entry; struct defrag_target_range *tmp; LIST_HEAD(target_list); @@ -356,69 +277,45 @@ diff --git a/fs/btrfs/defrag.c b/fs/btrfs/defrag.c const u32 sectorsize = inode->root->fs_info->sectorsize; u64 cur = start; const unsigned int nr_pages = ((start + len - 1) >> PAGE_SHIFT) - - (start >> PAGE_SHIFT) + 1; -+ struct extent_changeset *data_reserved = NULL; -+ u64 last_defrag_end = start; - int ret = 0; - - ASSERT(nr_pages <= CLUSTER_SIZE / PAGE_SIZE); -@@ -1192,6 +1185,20 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len, - if (!folios) - return -ENOMEM; - -+ /* -+ * Reserve delalloc space before locking the range and before locking -+ * and dirtying any folios - otherwise we could deadlock, for example -+ * after defrag of one range we dirty folios and keep them locked when -+ * we move to the next range, so reserving delalloc space right before -+ * each range could trigger flushing of delalloc and deadlock on the -+ * extent lock or trigger a transaction commit with flushoncommit, which -+ * can either deadlock on the lock of a folio made dirty in the previous -+ * range or the extent lock. -+ */ -+ ret = btrfs_delalloc_reserve_space(inode, &data_reserved, start, len); +@@ -1196,10 +1196,8 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len, + * range or the extent lock. + */ + ret = btrfs_delalloc_reserve_space(inode, &data_reserved, start, len); +- if (ret < 0) { +- kfree(folios); + if (ret < 0) -+ return ret; -+ + return ret; +- } + /* Prepare all pages */ for (int i = 0; cur < start + len && i < nr_pages; i++) { - folios[i] = defrag_prepare_one_folio(inode, cur >> PAGE_SHIFT); -@@ -1225,15 +1232,12 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len, +@@ -1234,16 +1232,12 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len, if (ret < 0) goto unlock_extent; - list_for_each_entry(entry, &target_list, list) { -- ret = defrag_one_locked_target(inode, entry, folios, nr_pages, -- &cached_state); -- if (ret < 0) -- break; ++ list_for_each_entry_safe(entry, tmp, &target_list, list) { + defrag_one_locked_target(inode, entry, folios, nr_pages, &cached_state); + if (entry->start > last_defrag_end) + btrfs_delalloc_release_space(inode, data_reserved, last_defrag_end, + entry->start - last_defrag_end, true); + last_defrag_end = entry->start + entry->len; - } - - list_for_each_entry_safe(entry, tmp, &target_list, list) { +- list_for_each_entry_safe(entry, tmp, &target_list, list) { - list_del_init(&entry->list); -+ defrag_one_locked_target(inode, entry, folios, nr_pages, &cached_state); -+ if (entry->start > last_defrag_end) -+ btrfs_delalloc_release_space(inode, data_reserved, last_defrag_end, -+ entry->start - last_defrag_end, true); -+ last_defrag_end = entry->start + entry->len; kfree(entry); } unlock_extent: -@@ -1245,7 +1249,12 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len, +@@ -1255,7 +1249,6 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len, folio_unlock(folios[i]); folio_put(folios[i]); } - kfree(folios); -+ btrfs_delalloc_release_extents(inode, len); -+ if (last_defrag_end < start + len) -+ btrfs_delalloc_release_space(inode, data_reserved, last_defrag_end, -+ start + len - last_defrag_end, true); -+ extent_changeset_free(data_reserved); -+ - return ret; - } - -@@ -1310,10 +1319,8 @@ static int defrag_one_cluster(struct btrfs_inode *inode, + btrfs_delalloc_release_extents(inode, len); + if (last_defrag_end < start + len) + btrfs_delalloc_release_space(inode, data_reserved, last_defrag_end, +@@ -1326,10 +1319,8 @@ static int defrag_one_cluster(struct btrfs_inode *inode, inode->root->fs_info->sectorsize_bits; } out: @@ -744,29 +641,7 @@ diff --git a/fs/btrfs/direct-io.c b/fs/btrfs/direct-io.c diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c -@@ -271,14 +271,15 @@ int btree_csum_one_bio(struct btrfs_bio *bbio) - return -EIO; - - /* -- * If an extent_buffer is marked as EXTENT_BUFFER_ZONED_ZEROOUT, don't -- * checksum it but zero-out its content. This is done to preserve -- * ordering of I/O without unnecessarily writing out data. -+ * An extent_buffer marked EXTENT_BUFFER_ZONED_ZEROOUT is written out as -+ * zeros to preserve ordering of I/O without persisting the now -+ * unnecessary block. The bio is fed from the shared zero page (see -+ * write_one_eb()), so there is nothing to checksum here. Crucially, the -+ * buffer's own content is left intact: it may still be referenced, e.g. -+ * btrfs_free_tree_block() reads its header to add a delayed reference. - */ -- if (test_bit(EXTENT_BUFFER_ZONED_ZEROOUT, &eb->bflags)) { -- memzero_extent_buffer(eb, 0, eb->len); -+ if (test_bit(EXTENT_BUFFER_ZONED_ZEROOUT, &eb->bflags)) - return 0; -- } - - if (WARN_ON_ONCE(found_start != eb->start)) - return -EIO; -@@ -590,12 +591,13 @@ static const struct address_space_operations btree_aops = { +@@ -591,12 +591,13 @@ static const struct address_space_operations btree_aops = { struct extent_buffer *btrfs_find_create_tree_block( struct btrfs_fs_info *fs_info, @@ -781,7 +656,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c } /* -@@ -608,12 +610,13 @@ struct extent_buffer *btrfs_find_create_tree_block( +@@ -609,12 +610,13 @@ struct extent_buffer *btrfs_find_create_tree_block( struct extent_buffer *read_tree_block(struct btrfs_fs_info *fs_info, u64 bytenr, struct btrfs_tree_parent_check *check) { @@ -796,7 +671,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c check->level); if (IS_ERR(buf)) return buf; -@@ -666,10 +669,7 @@ static struct btrfs_root *btrfs_alloc_root(struct btrfs_fs_info *fs_info, +@@ -667,10 +669,7 @@ static struct btrfs_root *btrfs_alloc_root(struct btrfs_fs_info *fs_info, init_waitqueue_head(&root->log_commit_wait[1]); INIT_LIST_HEAD(&root->log_ctxs[0]); INIT_LIST_HEAD(&root->log_ctxs[1]); @@ -807,7 +682,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c refcount_set(&root->refs, 1); atomic_set(&root->snapshot_force_cow, 0); atomic_set(&root->nr_swapfiles, 0); -@@ -2052,7 +2052,7 @@ static int btrfs_replay_log(struct btrfs_fs_info *fs_info, +@@ -2053,7 +2052,7 @@ static int btrfs_replay_log(struct btrfs_fs_info *fs_info, if (IS_ERR(log_tree_root->node)) { ret = PTR_ERR(log_tree_root->node); log_tree_root->node = NULL; @@ -816,7 +691,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c btrfs_put_root(log_tree_root); return ret; } -@@ -2062,7 +2062,7 @@ static int btrfs_replay_log(struct btrfs_fs_info *fs_info, +@@ -2063,7 +2062,7 @@ static int btrfs_replay_log(struct btrfs_fs_info *fs_info, btrfs_put_root(log_tree_root); if (unlikely(ret)) { ASSERT(BTRFS_FS_ERROR(fs_info) != 0); @@ -825,7 +700,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c return ret; } -@@ -2303,8 +2303,8 @@ static int btrfs_read_roots(struct btrfs_fs_info *fs_info) +@@ -2304,8 +2303,8 @@ static int btrfs_read_roots(struct btrfs_fs_info *fs_info) return 0; out: @@ -836,7 +711,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c return ret; } -@@ -2395,8 +2395,8 @@ static int validate_sys_chunk_array(const struct btrfs_fs_info *fs_info, +@@ -2400,8 +2399,8 @@ static int validate_sys_chunk_array(const struct btrfs_fs_info *fs_info, int btrfs_validate_super(const struct btrfs_fs_info *fs_info, const struct btrfs_super_block *sb, int mirror_num) { @@ -847,7 +722,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c int ret = 0; const bool ignore_flags = btrfs_test_opt(fs_info, IGNORESUPERFLAGS); -@@ -2438,24 +2438,24 @@ int btrfs_validate_super(const struct btrfs_fs_info *fs_info, +@@ -2443,24 +2442,24 @@ int btrfs_validate_super(const struct btrfs_fs_info *fs_info, */ if (unlikely(!is_power_of_2(sectorsize) || sectorsize < BTRFS_MIN_BLOCKSIZE || sectorsize > BTRFS_MAX_METADATA_BLOCKSIZE)) { @@ -876,7 +751,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c le32_to_cpu(sb->__unused_leafsize), nodesize); ret = -EINVAL; } -@@ -2905,7 +2905,6 @@ void btrfs_init_fs_info(struct btrfs_fs_info *fs_info) +@@ -2910,7 +2909,6 @@ void btrfs_init_fs_info(struct btrfs_fs_info *fs_info) fs_info->nodesize = 4096; fs_info->sectorsize = 4096; fs_info->sectorsize_bits = ilog2(4096); @@ -884,7 +759,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c /* Default compress algorithm when user does -o compress */ fs_info->compress_type = BTRFS_COMPRESS_ZLIB; -@@ -2979,8 +2978,8 @@ static int btrfs_uuid_rescan_kthread(void *data) +@@ -2984,8 +2982,8 @@ static int btrfs_uuid_rescan_kthread(void *data) ret = btrfs_uuid_tree_iterate(fs_info); if (ret < 0) { if (ret != -EINTR) @@ -895,7 +770,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c up(&fs_info->uuid_tree_rescan_sem); return ret; } -@@ -3083,7 +3082,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) +@@ -3088,7 +3086,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) ret = btrfs_rebuild_free_space_tree(fs_info); if (ret) { btrfs_warn(fs_info, @@ -904,7 +779,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c return ret; } } -@@ -3094,7 +3093,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) +@@ -3099,7 +3097,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) ret = btrfs_delete_free_space_tree(fs_info); if (ret) { btrfs_warn(fs_info, @@ -913,7 +788,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c return ret; } } -@@ -3105,7 +3104,8 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) +@@ -3110,7 +3108,8 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) */ ret = btrfs_delete_orphan_free_space_entries(fs_info); if (ret < 0) { @@ -923,7 +798,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c return ret; } /* -@@ -3139,7 +3139,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) +@@ -3144,7 +3143,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) ret = btrfs_recover_relocation(fs_info); mutex_unlock(&fs_info->cleaner_mutex); if (ret < 0) { @@ -932,7 +807,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c return ret; } -@@ -3149,7 +3149,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) +@@ -3154,7 +3153,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) ret = btrfs_create_free_space_tree(fs_info); if (ret) { btrfs_warn(fs_info, @@ -941,7 +816,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c return ret; } } -@@ -3177,7 +3177,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) +@@ -3182,7 +3181,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) ret = btrfs_create_uuid_tree(fs_info); if (ret) { btrfs_warn(fs_info, @@ -950,46 +825,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c return ret; } } -@@ -3314,6 +3314,8 @@ static void invalidate_and_check_btree_folios(struct btrfs_fs_info *fs_info) - */ - rcu_read_lock(); - xa_for_each(&fs_info->buffer_tree, index, eb) { -+ unsigned int refs; -+ - /* Increase the ref so that the eb won't disappear. */ - if (!refcount_inc_not_zero(&eb->refs)) - continue; -@@ -3323,17 +3325,27 @@ static void invalidate_and_check_btree_folios(struct btrfs_fs_info *fs_info) - if (test_bit(EXTENT_BUFFER_READING, &eb->bflags)) - wait_on_bit_io(&eb->bflags, EXTENT_BUFFER_READING, - TASK_UNINTERRUPTIBLE); -+ /* -+ * We hold the spinlock to make sure above -+ * EXTENT_BUFFER_READING flag is cleared with the held -+ * ref dropped. -+ * Or we can hit a race window and lead to false alerts. -+ */ -+ spin_lock(&eb->refs_lock); -+ refs = refcount_read(&eb->refs); -+ spin_unlock(&eb->refs_lock); -+ - /* - * The refs threshold is 2, one held by us at the beginning - * of the loop, one for the ownership in the buffer tree. - */ -- if (unlikely(refcount_read(&eb->refs) > 2 || extent_buffer_under_io(eb))) { -+ if (unlikely(refs > 2 || extent_buffer_under_io(eb))) { - WARN_ON_ONCE(IS_ENABLED(CONFIG_BTRFS_DEBUG)); - btrfs_warn(fs_info, - "unable to release extent buffer %llu owner %llu gen %llu refs %u flags 0x%lx", - eb->start, btrfs_header_owner(eb), - btrfs_header_generation(eb), -- refcount_read(&eb->refs), eb->bflags); -+ refs, eb->bflags); - } - free_extent_buffer(eb); - rcu_read_lock(); -@@ -3355,7 +3367,6 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device +@@ -3372,7 +3371,6 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device { u32 sectorsize; u32 nodesize; @@ -997,7 +833,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c u64 generation; u16 csum_type; struct btrfs_super_block *disk_super; -@@ -3464,7 +3475,6 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device +@@ -3481,7 +3479,6 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device /* Set up fs_info before parsing mount options */ nodesize = btrfs_super_nodesize(disk_super); sectorsize = btrfs_super_sectorsize(disk_super); @@ -1005,7 +841,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c fs_info->dirty_metadata_batch = nodesize * (1 + ilog2(nr_cpu_ids)); fs_info->delalloc_batch = sectorsize * 512 * (1 + ilog2(nr_cpu_ids)); -@@ -3483,7 +3493,6 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device +@@ -3500,7 +3497,6 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device else fs_info->block_max_order = calc_block_max_order(fs_info->sectorsize_bits); fs_info->csums_per_leaf = BTRFS_MAX_ITEM_SIZE(fs_info) / fs_info->csum_size; @@ -1013,7 +849,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c fs_info->fs_devices->fs_info = fs_info; if (fs_info->sectorsize > PAGE_SIZE) -@@ -3549,7 +3558,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device +@@ -3566,7 +3562,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device ret = btrfs_read_sys_array(fs_info); mutex_unlock(&fs_info->chunk_mutex); if (ret) { @@ -1022,7 +858,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c goto fail_sb_buffer; } -@@ -3568,7 +3577,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device +@@ -3585,7 +3581,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device ret = btrfs_read_chunk_tree(fs_info); if (ret) { @@ -1031,7 +867,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c goto fail_tree_roots; } -@@ -3598,7 +3607,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device +@@ -3615,7 +3611,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device ret = btrfs_get_dev_zone_info_all_devices(fs_info); if (ret) { btrfs_err(fs_info, @@ -1040,7 +876,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c goto fail_block_groups; } -@@ -3621,72 +3630,73 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device +@@ -3638,72 +3634,73 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device ret = btrfs_verify_dev_extents(fs_info); if (ret) { btrfs_err(fs_info, @@ -1129,7 +965,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c goto fail_sysfs; } -@@ -3742,7 +3752,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device +@@ -3759,7 +3756,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device fs_info->fs_root = btrfs_get_fs_root(fs_info, BTRFS_FS_TREE_OBJECTID, true); if (IS_ERR(fs_info->fs_root)) { ret = PTR_ERR(fs_info->fs_root); @@ -1138,7 +974,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c fs_info->fs_root = NULL; goto fail_qgroup; } -@@ -3763,7 +3773,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device +@@ -3780,7 +3777,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device btrfs_info(fs_info, "checking UUID tree"); ret = btrfs_check_uuid_tree(fs_info); if (ret) { @@ -1147,7 +983,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c close_ctree(fs_info); return ret; } -@@ -3879,8 +3889,8 @@ static int write_dev_supers(struct btrfs_device *device, +@@ -3896,8 +3893,8 @@ static int write_dev_supers(struct btrfs_device *device, continue; } else if (ret < 0) { btrfs_err(device->fs_info, @@ -1158,7 +994,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c atomic_inc(&device->sb_write_errors); continue; } -@@ -3898,8 +3908,8 @@ static int write_dev_supers(struct btrfs_device *device, +@@ -3915,8 +3912,8 @@ static int write_dev_supers(struct btrfs_device *device, GFP_NOFS); if (IS_ERR(folio)) { btrfs_err(device->fs_info, @@ -1169,7 +1005,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c atomic_inc(&device->sb_write_errors); continue; } -@@ -4381,6 +4391,21 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info) +@@ -4398,6 +4395,21 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info) */ flush_workqueue(fs_info->fixup_workers); @@ -1191,7 +1027,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c /* * Handle the error fs first, as it will flush and wait for all ordered * extents. This will generate delayed iputs, thus we want to handle -@@ -4511,7 +4536,7 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info) +@@ -4528,7 +4540,7 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info) if (!btrfs_is_shutdown(fs_info)) { ret = btrfs_commit_super(fs_info); if (ret) @@ -1200,7 +1036,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c } } -@@ -4550,6 +4575,13 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info) +@@ -4567,6 +4579,13 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info) free_root_pointers(fs_info, true); btrfs_free_fs_roots(fs_info); @@ -1214,7 +1050,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c /* * We must make sure there is not any read request to * submit after we stop all workers. -@@ -4998,6 +5030,7 @@ static int btrfs_cleanup_transaction(struct btrfs_fs_info *fs_info) +@@ -5015,6 +5034,7 @@ static int btrfs_cleanup_transaction(struct btrfs_fs_info *fs_info) btrfs_assert_delayed_root_empty(fs_info); btrfs_destroy_all_delalloc_inodes(fs_info); btrfs_drop_all_logs(fs_info); @@ -1461,198 +1297,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c bio_ctrl->wbc->nr_to_write--; -@@ -2379,14 +2397,17 @@ static struct extent_buffer *find_extent_buffer_nolock( - static void end_bbio_meta_write(struct btrfs_bio *bbio) - { - struct extent_buffer *eb = bbio->private; -- struct folio_iter fi; - - if (bbio->bio.bi_status != BLK_STS_OK) - set_btree_ioerr(eb); - -- bio_for_each_folio_all(fi, &bbio->bio) { -- btrfs_meta_folio_clear_writeback(fi.folio, eb); -- } -+ /* -+ * Clear writeback on the buffer's own folios. The bio may carry the -+ * shared zero page instead (EXTENT_BUFFER_ZONED_ZEROOUT), so iterate -+ * the extent buffer folios rather than the bio folios. -+ */ -+ for (int i = 0; i < num_extent_folios(eb); i++) -+ btrfs_meta_folio_clear_writeback(eb->folios[i], eb); - - buffer_tree_clear_mark(eb, PAGECACHE_TAG_WRITEBACK); - clear_and_wake_up_bit(EXTENT_BUFFER_WRITEBACK, &eb->bflags); -@@ -2427,7 +2448,8 @@ static noinline_for_stack void write_one_eb(struct extent_buffer *eb, - struct btrfs_fs_info *fs_info = eb->fs_info; - struct btrfs_bio *bbio; - -- prepare_eb_write(eb); -+ if (!test_bit(EXTENT_BUFFER_ZONED_ZEROOUT, &eb->bflags)) -+ prepare_eb_write(eb); - - bbio = btrfs_bio_alloc(INLINE_EXTENT_BUFFER_PAGES, - REQ_OP_WRITE | REQ_META | wbc_to_write_flags(wbc), -@@ -2447,8 +2469,21 @@ static noinline_for_stack void write_one_eb(struct extent_buffer *eb, - btrfs_meta_folio_set_writeback(folio, eb); - if (!folio_test_dirty(folio)) - wbc->nr_to_write -= folio_nr_pages(folio); -- bio_add_folio_nofail(&bbio->bio, folio, range_len, -- offset_in_folio(folio, range_start)); -+ if (test_bit(EXTENT_BUFFER_ZONED_ZEROOUT, &eb->bflags)) { -+ u32 off = 0; -+ -+ while (off < range_len) { -+ u32 add = min_t(u32, PAGE_SIZE, range_len - off); -+ -+ bio_add_folio_nofail(&bbio->bio, -+ page_folio(ZERO_PAGE(0)), -+ add, 0); -+ off += add; -+ } -+ } else { -+ bio_add_folio_nofail(&bbio->bio, folio, range_len, -+ offset_in_folio(folio, range_start)); -+ } - wbc_account_cgroup_owner(wbc, folio, range_len); - folio_unlock(folio); - } -@@ -2496,6 +2531,76 @@ void btrfs_btree_wait_writeback_range(struct btrfs_fs_info *fs_info, u64 start, - } - } - -+static int write_meta_extent_buffer(struct btrfs_eb_write_context *ctx, -+ struct writeback_control *wbc) -+{ -+ struct extent_buffer *eb = ctx->eb; -+ int ret; -+ -+ ret = btrfs_check_meta_write_pointer(eb->fs_info, ctx); -+ if (ret) -+ return ret; -+ -+ if (!lock_extent_buffer_for_io(eb, wbc)) -+ return 0; -+ -+ /* Implies write in zoned mode. */ -+ if (ctx->zoned_bg) { -+ /* Mark the last eb in the block group. */ -+ btrfs_schedule_zone_finish_bg(ctx->zoned_bg, eb); -+ ctx->zoned_bg->meta_write_pointer += eb->len; -+ } -+ write_one_eb(eb, wbc); -+ return 0; -+} -+ -+/* -+ * On a zoned filesystem, write out the currently dirty metadata extent buffers -+ * of @bg. Used to flush the active metadata/system block group before the -+ * ascending-address walk in btree_writepages(), so that walk can pivot the -+ * active block group away (finishing it) instead of aborting the commit; see -+ * the caller for details. -+ */ -+static void flush_active_meta_bg(struct address_space *mapping, -+ struct writeback_control *wbc, -+ struct btrfs_eb_write_context *ctx, -+ struct btrfs_block_group *bg) -+{ -+ struct btrfs_fs_info *fs_info = inode_to_fs_info(mapping->host); -+ unsigned long index = bg->start >> fs_info->nodesize_bits; -+ unsigned long end = (btrfs_block_group_end(bg) - 1) >> fs_info->nodesize_bits; -+ struct eb_batch batch; -+ unsigned int nr_ebs; -+ -+ ASSERT(btrfs_is_zoned(fs_info)); -+ lockdep_assert_held(&fs_info->zoned_meta_io_lock); -+ -+ eb_batch_init(&batch); -+ while (index <= end && -+ (nr_ebs = buffer_tree_get_ebs_tag(fs_info, &index, end, -+ PAGECACHE_TAG_DIRTY, &batch))) { -+ struct extent_buffer *eb; -+ -+ while ((eb = eb_batch_next(&batch)) != NULL) { -+ ctx->eb = eb; -+ -+ /* -+ * If the eb is behind the write pointer (-EBUSY, e.g. -+ * already being written by someone else) skip it and -+ * carry on. Only a hole at the write pointer (-EAGAIN) -+ * stops the flush. The main walk in btree_writepages() -+ * then deals with it. -+ */ -+ if (write_meta_extent_buffer(ctx, wbc) == -EAGAIN) { -+ eb_batch_release(&batch); -+ return; -+ } -+ } -+ eb_batch_release(&batch); -+ cond_resched(); -+ } -+} -+ - int btree_writepages(struct address_space *mapping, struct writeback_control *wbc) - { - struct btrfs_eb_write_context ctx = { .wbc = wbc }; -@@ -2531,6 +2636,22 @@ int btree_writepages(struct address_space *mapping, struct writeback_control *wb - else - tag = PAGECACHE_TAG_DIRTY; - btrfs_zoned_meta_io_lock(fs_info); -+ -+ /* -+ * On a zoned filesystem, flush the currently active metadata/system -+ * block group(s) first, under this same lock, so the ascending-address -+ * walk below can pivot the active block group instead of aborting the -+ * transaction commit with -EAGAIN. -+ */ -+ if (btrfs_is_zoned(fs_info) && wbc->sync_mode == WB_SYNC_ALL && -+ !wbc->for_sync) { -+ if (fs_info->active_meta_bg) -+ flush_active_meta_bg(mapping, wbc, &ctx, -+ fs_info->active_meta_bg); -+ if (fs_info->active_system_bg) -+ flush_active_meta_bg(mapping, wbc, &ctx, -+ fs_info->active_system_bg); -+ } - retry: - if (wbc->sync_mode == WB_SYNC_ALL) - buffer_tree_tag_for_writeback(fs_info, index, end); -@@ -2541,28 +2662,13 @@ int btree_writepages(struct address_space *mapping, struct writeback_control *wb - while ((eb = eb_batch_next(&batch)) != NULL) { - ctx.eb = eb; - -- ret = btrfs_check_meta_write_pointer(eb->fs_info, &ctx); -- if (ret) { -- if (ret == -EBUSY) -- ret = 0; -- -- if (ret) { -- done = true; -- break; -- } -- continue; -+ ret = write_meta_extent_buffer(&ctx, wbc); -+ if (ret == -EBUSY) { -+ ret = 0; -+ } else if (ret) { -+ done = true; -+ break; - } -- -- if (!lock_extent_buffer_for_io(eb, wbc)) -- continue; -- -- /* Implies write in zoned mode. */ -- if (ctx.zoned_bg) { -- /* Mark the last eb in the block group. */ -- btrfs_schedule_zone_finish_bg(ctx.zoned_bg, eb); -- ctx.zoned_bg->meta_write_pointer += eb->len; -- } -- write_one_eb(eb, wbc); - } - nr_to_write_done = (wbc->nr_to_write <= 0); - eb_batch_release(&batch); -@@ -2888,13 +2994,25 @@ void btrfs_readahead(struct readahead_control *rac) +@@ -2976,13 +2994,25 @@ void btrfs_readahead(struct readahead_control *rac) struct extent_map *em_cached = NULL; struct fsverity_info *vi = NULL; @@ -1681,123 +1326,8 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c if (em_cached) btrfs_free_extent_map(em_cached); -@@ -3129,47 +3247,71 @@ static inline void btrfs_release_extent_buffer(struct extent_buffer *eb) - kmem_cache_free(extent_buffer_cache, eb); - } - -+/* -+ * Claim a slot to track an extent buffer in, evicting the coldest tracked buffer -+ * when the array is full. -+ * -+ * Slots fill in order until the array is full. After that a CLOCK (second -+ * chance) scan advances the hand, clearing one reference bit per step, until -+ * it lands on an unreferenced slot whose buffer is evicted. Clearing a bit per -+ * step bounds the scan to BTRFS_INHIBITED_EBS_SLOTS iterations. -+ */ -+static int btrfs_inhibit_claim_slot(struct btrfs_trans_handle *trans) -+{ -+ int slot; -+ -+ if (trans->nr_inhibited_ebs < BTRFS_INHIBITED_EBS_SLOTS) -+ return trans->nr_inhibited_ebs++; -+ -+ while (trans->inhibited_ebs_referenced & (1U << trans->inhibited_ebs_hand)) { -+ trans->inhibited_ebs_referenced &= ~(1U << trans->inhibited_ebs_hand); -+ trans->inhibited_ebs_hand = -+ (trans->inhibited_ebs_hand + 1) % BTRFS_INHIBITED_EBS_SLOTS; -+ } -+ slot = trans->inhibited_ebs_hand; -+ trans->inhibited_ebs_hand = (trans->inhibited_ebs_hand + 1) % BTRFS_INHIBITED_EBS_SLOTS; -+ -+ atomic_dec(&trans->inhibited_ebs[slot]->writeback_inhibitors); -+ free_extent_buffer(trans->inhibited_ebs[slot]); -+ -+ return slot; -+} -+ - /* - * Inhibit writeback on buffer during transaction. - * - * @trans: transaction handle that will own the inhibitor - * @eb: extent buffer to inhibit writeback on - * -- * Attempt to track this extent buffer in the transaction's inhibited set. If -- * memory allocation fails, the buffer is simply not tracked. It may be written -- * back and need re-COW, which is the original behavior. This is acceptable -- * since inhibiting writeback is an optimization. -+ * Attempt to track this extent buffer in the transaction's inhibited set. When -+ * the set is full the coldest tracked buffer is evicted instead. An untracked -+ * buffer may be written back and need re-COW, which is the original behavior. -+ * This is acceptable since inhibiting writeback is an optimization. - */ - void btrfs_inhibit_eb_writeback(struct btrfs_trans_handle *trans, struct extent_buffer *eb) - { -- unsigned long index = eb->start >> trans->fs_info->nodesize_bits; -- void *old; -+ int slot; - - lockdep_assert_held(&eb->lock); -- /* Check if already inhibited by this handle. */ -- old = xa_load(&trans->writeback_inhibited_ebs, index); -- if (old == eb) -- return; - -- /* Take reference for the xarray entry. */ -+ /* Already tracked: set its reference bit (second chance) and return. */ -+ for (int i = 0; i < trans->nr_inhibited_ebs; i++) { -+ if (trans->inhibited_ebs[i] == eb) { -+ trans->inhibited_ebs_referenced |= 1U << i; -+ return; -+ } -+ } -+ -+ slot = btrfs_inhibit_claim_slot(trans); -+ -+ /* -+ * Pin the eb while the array holds a raw pointer to it; the counter is -+ * what lock_extent_buffer_for_io() checks. -+ */ - refcount_inc(&eb->refs); -- -- old = xa_store(&trans->writeback_inhibited_ebs, index, eb, GFP_NOFS); -- if (xa_is_err(old)) { -- /* Allocation failed, just skip inhibiting this buffer. */ -- free_extent_buffer(eb); -- return; -- } -- -- /* Handle replacement of different eb at same index. */ -- if (old && old != eb) { -- struct extent_buffer *old_eb = old; -- -- atomic_dec(&old_eb->writeback_inhibitors); -- free_extent_buffer(old_eb); -- } -- - atomic_inc(&eb->writeback_inhibitors); -+ trans->inhibited_ebs[slot] = eb; -+ trans->inhibited_ebs_referenced |= 1U << slot; - } - - /* -@@ -3177,22 +3319,18 @@ void btrfs_inhibit_eb_writeback(struct btrfs_trans_handle *trans, struct extent_ - */ - void btrfs_uninhibit_all_eb_writeback(struct btrfs_trans_handle *trans) - { -- struct extent_buffer *eb; -- unsigned long index; -- -- xa_for_each(&trans->writeback_inhibited_ebs, index, eb) { -- atomic_dec(&eb->writeback_inhibitors); -- free_extent_buffer(eb); -+ for (int i = 0; i < trans->nr_inhibited_ebs; i++) { -+ atomic_dec(&trans->inhibited_ebs[i]->writeback_inhibitors); -+ free_extent_buffer(trans->inhibited_ebs[i]); - } -- xa_destroy(&trans->writeback_inhibited_ebs); -+ trans->nr_inhibited_ebs = 0; -+ trans->inhibited_ebs_referenced = 0; -+ trans->inhibited_ebs_hand = 0; +@@ -3298,12 +3328,9 @@ void btrfs_uninhibit_all_eb_writeback(struct btrfs_trans_handle *trans) + trans->inhibited_ebs_hand = 0; } -static struct extent_buffer *__alloc_extent_buffer(struct btrfs_fs_info *fs_info, @@ -1811,7 +1341,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c eb->start = start; eb->len = fs_info->nodesize; eb->fs_info = fs_info; -@@ -3205,7 +3343,15 @@ static struct extent_buffer *__alloc_extent_buffer(struct btrfs_fs_info *fs_info +@@ -3316,7 +3343,15 @@ static struct extent_buffer *__alloc_extent_buffer(struct btrfs_fs_info *fs_info refcount_set(&eb->refs, 1); ASSERT(eb->len <= BTRFS_MAX_METADATA_BLOCKSIZE); @@ -1827,7 +1357,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c return eb; } -@@ -3502,7 +3648,7 @@ static bool check_eb_alignment(struct btrfs_fs_info *fs_info, u64 start) +@@ -3613,7 +3648,7 @@ static bool check_eb_alignment(struct btrfs_fs_info *fs_info, u64 start) * The caller needs to free the existing folios and retry using the same order. */ static int attach_eb_folio_to_filemap(struct extent_buffer *eb, int i, @@ -1836,7 +1366,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c struct extent_buffer **found_eb_ret) { -@@ -3524,6 +3670,7 @@ static int attach_eb_folio_to_filemap(struct extent_buffer *eb, int i, +@@ -3635,6 +3670,7 @@ static int attach_eb_folio_to_filemap(struct extent_buffer *eb, int i, if (!ret) goto finish; @@ -1844,7 +1374,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c existing_folio = filemap_lock_folio(mapping, index + i); /* The page cache only exists for a very short time, just retry. */ if (IS_ERR(existing_folio)) -@@ -3532,7 +3679,27 @@ static int attach_eb_folio_to_filemap(struct extent_buffer *eb, int i, +@@ -3643,7 +3679,27 @@ static int attach_eb_folio_to_filemap(struct extent_buffer *eb, int i, /* For now, we should only have single-page folios for btree inode. */ ASSERT(folio_nr_pages(existing_folio) == 1); @@ -1872,7 +1402,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c folio_unlock(existing_folio); folio_put(existing_folio); return -EAGAIN; -@@ -3563,8 +3730,10 @@ static int attach_eb_folio_to_filemap(struct extent_buffer *eb, int i, +@@ -3674,8 +3730,10 @@ static int attach_eb_folio_to_filemap(struct extent_buffer *eb, int i, eb->folio_size = folio_size(eb->folios[i]); eb->folio_shift = folio_shift(eb->folios[i]); /* Should not fail, as we have preallocated the memory. */ @@ -1884,7 +1414,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c /* * To inform we have an extra eb under allocation, so that * detach_extent_buffer_page() won't release the folio private when the -@@ -3579,13 +3748,104 @@ static int attach_eb_folio_to_filemap(struct extent_buffer *eb, int i, +@@ -3690,13 +3748,104 @@ static int attach_eb_folio_to_filemap(struct extent_buffer *eb, int i, return 0; } @@ -1990,7 +1520,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c u64 lockdep_owner = owner_root; bool page_contig = true; bool uptodate = true; -@@ -3609,9 +3869,14 @@ struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info, +@@ -3720,9 +3869,14 @@ struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info, if (eb) return eb; @@ -2008,7 +1538,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c /* * The reloc trees are just snapshots, so we need them to appear to be -@@ -3622,66 +3887,18 @@ struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info, +@@ -3733,66 +3887,18 @@ struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info, btrfs_set_buffer_lockdep_class(lockdep_owner, eb, level); @@ -2079,7 +1609,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c attached++; /* -@@ -3758,6 +3975,10 @@ struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info, +@@ -3869,6 +3975,10 @@ struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info, out: WARN_ON(!refcount_dec_and_test(&eb->refs)); @@ -2090,71 +1620,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c /* * Any attached folios need to be detached before we unlock them. This * is because when we're inserting our new folios into the mapping, and -@@ -3842,12 +4063,31 @@ static int release_extent_buffer(struct extent_buffer *eb) - return 0; - } - --void free_extent_buffer(struct extent_buffer *eb) -+static void clear_extent_buffer_reading(struct extent_buffer *eb) -+{ -+ clear_and_wake_up_bit(EXTENT_BUFFER_READING, &eb->bflags); -+} -+ -+static void free_extent_buffer_clear_reading(struct extent_buffer *eb, -+ bool clear_reading) - { - int refs; -+ - if (!eb) - return; - -+ /* -+ * We want to clear EXTENT_BUFFER_READING flag and decrease refs -+ * in the same critical section. -+ * This will make sure invalidate_and_check_btree_folios() won't -+ * see an eb with EXTENT_BUFFER_READING cleared but refs not yet -+ * decreased. -+ */ -+ if (clear_reading) { -+ spin_lock(&eb->refs_lock); -+ clear_extent_buffer_reading(eb); -+ } -+ - refs = refcount_read(&eb->refs); - while (1) { - if (test_bit(EXTENT_BUFFER_UNMAPPED, &eb->bflags)) { -@@ -3858,11 +4098,16 @@ void free_extent_buffer(struct extent_buffer *eb) - } - - /* Optimization to avoid locking eb->refs_lock. */ -- if (atomic_try_cmpxchg(&eb->refs.refs, &refs, refs - 1)) -+ if (atomic_try_cmpxchg(&eb->refs.refs, &refs, refs - 1)) { -+ if (clear_reading) -+ spin_unlock(&eb->refs_lock); - return; -+ } - } - -- spin_lock(&eb->refs_lock); -+ if (!clear_reading) -+ spin_lock(&eb->refs_lock); -+ - if (refcount_read(&eb->refs) == 2 && - test_bit(EXTENT_BUFFER_STALE, &eb->bflags) && - !extent_buffer_under_io(eb) && -@@ -3876,6 +4121,11 @@ void free_extent_buffer(struct extent_buffer *eb) - release_extent_buffer(eb); - } - -+void free_extent_buffer(struct extent_buffer *eb) -+{ -+ return free_extent_buffer_clear_reading(eb, false); -+} -+ - void free_extent_buffer_stale(struct extent_buffer *eb) - { - if (!eb) -@@ -3890,6 +4140,32 @@ void free_extent_buffer_stale(struct extent_buffer *eb) +@@ -4030,6 +4140,32 @@ void free_extent_buffer_stale(struct extent_buffer *eb) release_extent_buffer(eb); } @@ -2187,7 +1653,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c void btrfs_clear_buffer_dirty(struct btrfs_trans_handle *trans, struct extent_buffer *eb) { -@@ -3914,26 +4190,42 @@ void btrfs_clear_buffer_dirty(struct btrfs_trans_handle *trans, +@@ -4054,26 +4190,42 @@ void btrfs_clear_buffer_dirty(struct btrfs_trans_handle *trans, return; } @@ -2246,29 +1712,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c } void set_extent_buffer_dirty(struct extent_buffer *eb) -@@ -4001,11 +4293,6 @@ void set_extent_buffer_uptodate(struct extent_buffer *eb) - btrfs_meta_folio_set_uptodate(eb->folios[i], eb); - } - --static void clear_extent_buffer_reading(struct extent_buffer *eb) --{ -- clear_and_wake_up_bit(EXTENT_BUFFER_READING, &eb->bflags); --} -- - static void end_bbio_meta_read(struct btrfs_bio *bbio) - { - struct extent_buffer *eb = bbio->private; -@@ -4029,8 +4316,7 @@ static void end_bbio_meta_read(struct btrfs_bio *bbio) - else - clear_extent_buffer_uptodate(eb); - -- clear_extent_buffer_reading(eb); -- free_extent_buffer(eb); -+ free_extent_buffer_clear_reading(eb, true); - - bio_put(&bbio->bio); - } -@@ -4789,6 +5075,8 @@ void btrfs_readahead_tree_block(struct btrfs_fs_info *fs_info, +@@ -4923,6 +5075,8 @@ void btrfs_readahead_tree_block(struct btrfs_fs_info *fs_info, .level = level, .transid = gen }; @@ -2277,7 +1721,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c struct extent_buffer *eb; int ret; -@@ -4797,7 +5085,7 @@ void btrfs_readahead_tree_block(struct btrfs_fs_info *fs_info, +@@ -4931,7 +5085,7 @@ void btrfs_readahead_tree_block(struct btrfs_fs_info *fs_info, check.has_first_key = true; } @@ -2347,197 +1791,10 @@ diff --git a/fs/btrfs/fiemap.c b/fs/btrfs/fiemap.c bool stopped = false; int ret; -@@ -660,7 +660,7 @@ static int extent_fiemap(struct btrfs_inode *inode, - range_end = round_up(start + len, sectorsize); - prev_extent_end = range_start; - -- btrfs_lock_extent(&inode->io_tree, range_start, range_end, &cached_state); -+ btrfs_lock_extent(&inode->io_tree, range_start, range_end - 1, &cached_state); - - ret = fiemap_find_last_extent_offset(inode, path, &last_extent_end); - if (ret < 0) -@@ -840,7 +840,7 @@ static int extent_fiemap(struct btrfs_inode *inode, - } - - out_unlock: -- btrfs_unlock_extent(&inode->io_tree, range_start, range_end, &cached_state); -+ btrfs_unlock_extent(&inode->io_tree, range_start, range_end - 1, &cached_state); - - if (ret == BTRFS_FIEMAP_FLUSH_CACHE) { - btrfs_release_path(path); diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c -@@ -875,70 +875,64 @@ static noinline int prepare_one_folio(struct inode *inode, struct folio **folio_ - - /* - * Locks the extent and properly waits for data=ordered extents to finish -- * before allowing the folios to be modified if need. -+ * before allowing the folios to be modified. - * - * Return: -- * 1 - the extent is locked -- * 0 - the extent is not locked, and everything is OK -+ * 0 - the extent is locked - * -EAGAIN - need to prepare the folios again - */ - static noinline int --lock_and_cleanup_extent_if_need(struct btrfs_inode *inode, struct folio *folio, -- loff_t pos, size_t write_bytes, -- u64 *lockstart, u64 *lockend, bool nowait, -- struct extent_state **cached_state) -+lock_and_cleanup_extent(struct btrfs_inode *inode, struct folio *folio, -+ loff_t pos, size_t write_bytes, -+ u64 *lockstart, u64 *lockend, bool nowait, -+ struct extent_state **cached_state) - { - struct btrfs_fs_info *fs_info = inode->root->fs_info; -+ struct btrfs_ordered_extent *ordered; - u64 start_pos; - u64 last_pos; -- int ret = 0; - - start_pos = round_down(pos, fs_info->sectorsize); - last_pos = round_up(pos + write_bytes, fs_info->sectorsize) - 1; - -- if (start_pos < inode->vfs_inode.i_size) { -- struct btrfs_ordered_extent *ordered; -- -- if (nowait) { -- if (!btrfs_try_lock_extent(&inode->io_tree, start_pos, -- last_pos, cached_state)) { -- folio_unlock(folio); -- folio_put(folio); -- return -EAGAIN; -- } -- } else { -- btrfs_lock_extent(&inode->io_tree, start_pos, last_pos, -- cached_state); -- } -- -- ordered = btrfs_lookup_ordered_range(inode, start_pos, -- last_pos - start_pos + 1); -- if (ordered && -- ordered->file_offset + ordered->num_bytes > start_pos && -- ordered->file_offset <= last_pos) { -- btrfs_unlock_extent(&inode->io_tree, start_pos, last_pos, -- cached_state); -+ if (nowait) { -+ if (!btrfs_try_lock_extent(&inode->io_tree, start_pos, -+ last_pos, cached_state)) { - folio_unlock(folio); - folio_put(folio); -- btrfs_start_ordered_extent(ordered); -- btrfs_put_ordered_extent(ordered); - return -EAGAIN; - } -- if (ordered) -- btrfs_put_ordered_extent(ordered); -- -- *lockstart = start_pos; -- *lockend = last_pos; -- ret = 1; -+ } else { -+ btrfs_lock_extent(&inode->io_tree, start_pos, last_pos, -+ cached_state); - } - -+ ordered = btrfs_lookup_ordered_range(inode, start_pos, -+ last_pos - start_pos + 1); -+ if (ordered && -+ ordered->file_offset + ordered->num_bytes > start_pos && -+ ordered->file_offset <= last_pos) { -+ btrfs_unlock_extent(&inode->io_tree, start_pos, last_pos, -+ cached_state); -+ folio_unlock(folio); -+ folio_put(folio); -+ btrfs_start_ordered_extent(ordered); -+ btrfs_put_ordered_extent(ordered); -+ return -EAGAIN; -+ } -+ if (ordered) -+ btrfs_put_ordered_extent(ordered); -+ -+ *lockstart = start_pos; -+ *lockend = last_pos; -+ - /* - * We should be called after prepare_one_folio() which should have locked - * all pages in the range. - */ - WARN_ON(!folio_test_locked(folio)); - -- return ret; -+ return 0; - } - - /* -@@ -1195,7 +1189,6 @@ static int copy_one_range(struct btrfs_inode *inode, struct iov_iter *iter, - const u64 reserved_start = round_down(start, fs_info->sectorsize); - u64 reserved_len; - struct folio *folio = NULL; -- int extents_locked; - u64 lockstart; - u64 lockend; - bool only_release_metadata = false; -@@ -1253,18 +1246,16 @@ static int copy_one_range(struct btrfs_inode *inode, struct iov_iter *iter, - reserved_len = last_block - reserved_start; - } - -- extents_locked = lock_and_cleanup_extent_if_need(inode, folio, start, -- write_bytes, &lockstart, -- &lockend, nowait, -- &cached_state); -- if (extents_locked < 0) { -- if (!nowait && extents_locked == -EAGAIN) -+ ret = lock_and_cleanup_extent(inode, folio, start, write_bytes, -+ &lockstart, &lockend, nowait, &cached_state); -+ if (ret < 0) { -+ if (!nowait) - goto again; - - btrfs_delalloc_release_extents(inode, reserved_len); - release_space(inode, *data_reserved, reserved_start, reserved_len, - only_release_metadata); -- return extents_locked; -+ return ret; - } - - copied = copy_folio_from_iter_atomic(folio, offset_in_folio(folio, start), -@@ -1288,11 +1279,8 @@ static int copy_one_range(struct btrfs_inode *inode, struct iov_iter *iter, - - /* No copied bytes, unlock, release reserved space and exit. */ - if (copied == 0) { -- if (extents_locked) -- btrfs_unlock_extent(&inode->io_tree, lockstart, lockend, -- &cached_state); -- else -- btrfs_free_extent_state(cached_state); -+ btrfs_unlock_extent(&inode->io_tree, lockstart, lockend, -+ &cached_state); - btrfs_delalloc_release_extents(inode, reserved_len); - release_space(inode, *data_reserved, reserved_start, reserved_len, - only_release_metadata); -@@ -1311,17 +1299,7 @@ static int copy_one_range(struct btrfs_inode *inode, struct iov_iter *iter, - - ret = btrfs_dirty_folio(inode, folio, start, copied, &cached_state, - only_release_metadata); -- /* -- * If we have not locked the extent range, because the range's start -- * offset is >= i_size, we might still have a non-NULL cached extent -- * state, acquired while marking the extent range as delalloc through -- * btrfs_dirty_page(). Therefore free any possible cached extent state -- * to avoid a memory leak. -- */ -- if (extents_locked) -- btrfs_unlock_extent(&inode->io_tree, lockstart, lockend, &cached_state); -- else -- btrfs_free_extent_state(cached_state); -+ btrfs_unlock_extent(&inode->io_tree, lockstart, lockend, &cached_state); - - btrfs_delalloc_release_extents(inode, reserved_len); - if (ret) { -@@ -1595,8 +1573,6 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) +@@ -1573,8 +1573,6 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) else btrfs_inode_lock(inode, BTRFS_ILOCK_MMAP); @@ -2546,7 +1803,7 @@ diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c /* * Before we acquired the inode's lock and the mmap lock, someone may * have dirtied more pages in the target range. We need to make sure -@@ -1679,8 +1655,6 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) +@@ -1657,8 +1655,6 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) if (ret) goto out_release_extents; @@ -2555,7 +1812,7 @@ diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c if (skip_inode_logging(&ctx)) { /* * We've had everything committed since the last time we were -@@ -2683,8 +2657,8 @@ static int btrfs_punch_hole(struct file *file, loff_t offset, loff_t len) +@@ -2663,8 +2659,8 @@ static int btrfs_punch_hole(struct file *file, loff_t offset, loff_t len) lockstart = round_up(offset, fs_info->sectorsize); lockend = round_down(offset + len, fs_info->sectorsize) - 1; @@ -2566,7 +1823,7 @@ diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c /* * Only do this if we are in the same block and we aren't doing the * entire block. -@@ -2888,7 +2862,7 @@ enum { +@@ -2868,7 +2864,7 @@ enum { static int btrfs_zero_range_check_range_boundary(struct btrfs_inode *inode, u64 offset) { @@ -2575,7 +1832,7 @@ diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c struct extent_map *em; int ret; -@@ -2918,7 +2892,7 @@ static int btrfs_zero_range(struct inode *inode, +@@ -2898,7 +2894,7 @@ static int btrfs_zero_range(struct inode *inode, struct extent_changeset *data_reserved = NULL; int ret; u64 alloc_hint = 0; @@ -2584,7 +1841,7 @@ diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c const u64 orig_start = offset; const u64 orig_end = offset + len - 1; u64 alloc_start = round_down(offset, sectorsize); -@@ -2967,8 +2941,8 @@ static int btrfs_zero_range(struct inode *inode, +@@ -2947,8 +2943,8 @@ static int btrfs_zero_range(struct inode *inode, } btrfs_free_extent_map(em); @@ -2735,7 +1992,7 @@ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c return ret; } -@@ -3050,7 +3051,7 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, +@@ -3065,7 +3066,7 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, u64 qgroup_reserved) { struct btrfs_root *root = inode->root; @@ -2744,7 +2001,7 @@ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c BTRFS_PATH_AUTO_FREE(path); struct extent_buffer *leaf; struct btrfs_key ins; -@@ -3907,7 +3908,7 @@ int btrfs_orphan_cleanup(struct btrfs_root *root) +@@ -3926,7 +3927,7 @@ int btrfs_orphan_cleanup(struct btrfs_root *root) out: if (ret) @@ -2753,7 +2010,7 @@ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c return ret; } -@@ -4210,8 +4211,8 @@ static int btrfs_read_locked_inode(struct btrfs_inode *inode, struct btrfs_path +@@ -4229,8 +4230,8 @@ static int btrfs_read_locked_inode(struct btrfs_inode *inode, struct btrfs_path ret = btrfs_load_inode_props(inode, path); if (ret) btrfs_err(fs_info, @@ -2764,7 +2021,7 @@ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c } /* -@@ -6825,8 +6826,8 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans, +@@ -6844,8 +6845,8 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans, } if (ret) { btrfs_err(fs_info, @@ -2775,7 +2032,7 @@ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c } /* -@@ -10205,6 +10206,8 @@ static void btrfs_free_swapfile_pins(struct inode *inode) +@@ -10224,6 +10225,8 @@ static void btrfs_free_swapfile_pins(struct inode *inode) struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info; struct btrfs_swapfile_pin *sp; struct rb_node *node, *next; @@ -2784,7 +2041,7 @@ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c spin_lock(&fs_info->swapfile_pins_lock); node = rb_first(&fs_info->swapfile_pins); -@@ -10214,15 +10217,24 @@ static void btrfs_free_swapfile_pins(struct inode *inode) +@@ -10233,15 +10236,24 @@ static void btrfs_free_swapfile_pins(struct inode *inode) if (sp->inode == inode) { rb_erase(&sp->node, &fs_info->swapfile_pins); if (sp->is_block_group) { @@ -2811,7 +2068,7 @@ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c } struct btrfs_swap_info { -@@ -10300,8 +10312,10 @@ static int btrfs_swap_activate(struct swap_info_struct *sis, struct file *file, +@@ -10319,8 +10331,10 @@ static int btrfs_swap_activate(struct swap_info_struct *sis, struct file *file, struct btrfs_backref_share_check_ctx *backref_ctx = NULL; struct btrfs_path *path = NULL; int ret = 0; @@ -2822,7 +2079,7 @@ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c /* * Acquire the inode's mmap lock to prevent races with memory mapped -@@ -10551,6 +10565,9 @@ static int btrfs_swap_activate(struct swap_info_struct *sis, struct file *file, +@@ -10570,6 +10584,9 @@ static int btrfs_swap_activate(struct swap_info_struct *sis, struct file *file, ret = 0; else goto out; @@ -2832,7 +2089,7 @@ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c } if (bsi.block_len && -@@ -10598,6 +10615,14 @@ static int btrfs_swap_activate(struct swap_info_struct *sis, struct file *file, +@@ -10617,6 +10634,14 @@ static int btrfs_swap_activate(struct swap_info_struct *sis, struct file *file, if (ret) return ret; @@ -3215,43 +2472,6 @@ diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c } } -@@ -4339,12 +4339,13 @@ static int qgroup_free_reserved_data(struct btrfs_inode *inode, - struct ulist_node *unode; - struct ulist_iterator uiter; - struct extent_changeset changeset; -+ const u32 sectorsize = root->fs_info->sectorsize; -+ const u64 aligned_start = round_down(start, sectorsize); -+ const u64 aligned_len = round_up(start + len, sectorsize) - aligned_start; - u64 freed = 0; - int ret; - - extent_changeset_init_bytes_only(&changeset); -- len = round_up(start + len, root->fs_info->sectorsize); -- start = round_down(start, root->fs_info->sectorsize); - - ULIST_ITER_INIT(&uiter); - while ((unode = ulist_next(&reserved->range_changed, &uiter))) { -@@ -4356,12 +4357,15 @@ static int qgroup_free_reserved_data(struct btrfs_inode *inode, - - extent_changeset_release(&changeset); - -- /* Only free range in range [start, start + len) */ -- if (range_start >= start + len || -- range_start + range_len <= start) -+ /* -+ * Only free the range within -+ * [aligned_start, aligned_start + aligned_len). -+ */ -+ if (range_start >= aligned_start + aligned_len || -+ range_start + range_len <= aligned_start) - continue; -- free_start = max(range_start, start); -- free_len = min(start + len, range_start + range_len) - -+ free_start = max(range_start, aligned_start); -+ free_len = min(aligned_start + aligned_len, range_start + range_len) - - free_start; - /* - * TODO: To also modify reserved->ranges_reserved to reflect diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c --- a/fs/btrfs/raid56.c +++ b/fs/btrfs/raid56.c @@ -3655,87 +2875,7 @@ diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c return false; } -@@ -1537,6 +1538,33 @@ static void clear_reloc_root(struct btrfs_root *root) - clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE, &root->state); - } - -+/* Drop the reloc trees of a relocation that is being deferred and retried. */ -+static void abort_reloc_roots(struct reloc_control *rc, struct list_head *list) -+{ -+ struct btrfs_fs_info *fs_info = rc->extent_root->fs_info; -+ struct btrfs_root *reloc_root, *tmp; -+ -+ list_for_each_entry_safe(reloc_root, tmp, list, root_list) { -+ struct btrfs_root *root; -+ -+ root = btrfs_get_fs_root(fs_info, reloc_root->root_key.offset, false); -+ if (!IS_ERR(root)) { -+ if (root->reloc_root == reloc_root) { -+ clear_reloc_root(root); -+ btrfs_put_root(reloc_root); -+ } -+ btrfs_put_root(root); -+ } -+ -+ btrfs_set_root_refs(&reloc_root->root_item, 0); -+ memset(&reloc_root->root_item.drop_progress, 0, sizeof(struct btrfs_disk_key)); -+ btrfs_set_root_drop_level(&reloc_root->root_item, 0); -+ -+ list_del_init(&reloc_root->root_list); -+ list_add_tail(&reloc_root->reloc_dirty_list, &rc->dirty_subvol_roots); -+ } -+} -+ - static int clean_dirty_subvols(struct reloc_control *rc) - { - struct btrfs_root *root; -@@ -1876,8 +1904,7 @@ int prepare_to_merge(struct reloc_control *rc, int err) - return err; - } - --static noinline_for_stack --void merge_reloc_roots(struct reloc_control *rc) -+static noinline_for_stack int merge_reloc_roots(struct reloc_control *rc) - { - struct btrfs_fs_info *fs_info = rc->extent_root->fs_info; - struct btrfs_root *root; -@@ -1975,7 +2002,15 @@ void merge_reloc_roots(struct reloc_control *rc) - goto again; - } - out: -- if (ret) { -+ if (btrfs_is_zoned(fs_info) && ret == -EAGAIN) { -+ abort_reloc_roots(rc, &reloc_roots); -+ -+ /* New reloc root may be added. */ -+ mutex_lock(&fs_info->reloc_mutex); -+ list_splice_init(&rc->reloc_roots, &reloc_roots); -+ mutex_unlock(&fs_info->reloc_mutex); -+ abort_reloc_roots(rc, &reloc_roots); -+ } else if (ret) { - btrfs_handle_fs_error(fs_info, ret, NULL); - free_reloc_roots(&reloc_roots); - -@@ -2001,6 +2036,7 @@ void merge_reloc_roots(struct reloc_control *rc) - * - * The remaining nodes will be cleaned up by put_reloc_control(). - */ -+ return ret; - } - - static void free_block_list(struct rb_root *blocks) -@@ -3730,7 +3766,9 @@ static noinline_for_stack int relocate_block_group(struct reloc_control *rc) - */ - err = prepare_to_merge(rc, err); - -- merge_reloc_roots(rc); -+ ret = merge_reloc_roots(rc); -+ if (ret && !err) -+ err = ret; - - rc->merge_reloc_tree = false; - unset_reloc_control(rc); -@@ -4114,10 +4152,10 @@ static int copy_remapped_data(struct btrfs_fs_info *fs_info, u64 old_addr, +@@ -4151,10 +4152,10 @@ static int copy_remapped_data(struct btrfs_fs_info *fs_info, u64 old_addr, u64 new_addr, u64 length) { int ret; @@ -3748,17 +2888,6 @@ diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c pages = kzalloc_objs(struct page *, nr_pages, GFP_NOFS); if (!pages) -@@ -5720,7 +5758,9 @@ int btrfs_recover_relocation(struct btrfs_fs_info *fs_info) - goto out_drop_reloc_refs; - release_recovered_fs_roots(&recovered_roots, false); - -- merge_reloc_roots(rc); -+ ret = merge_reloc_roots(rc); -+ if (ret) -+ goto out_unset; - - unset_reloc_control(rc); - diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c --- a/fs/btrfs/root-tree.c +++ b/fs/btrfs/root-tree.c @@ -4127,7 +3256,7 @@ diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c stripe->logical + (i << fs_info->sectorsize_bits), scrub_repair_read_endio, stripe); -@@ -1245,20 +1227,9 @@ static void scrub_stripe_read_repair_worker(struct work_struct *work) +@@ -1249,20 +1231,9 @@ static void scrub_stripe_read_repair_worker(struct work_struct *work) static void scrub_read_endio(struct btrfs_bio *bbio) { struct scrub_stripe *stripe = bbio->private; @@ -4149,7 +3278,7 @@ diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c if (atomic_dec_and_test(&stripe->pending_io)) { wake_up(&stripe->io_wait); INIT_WORK(&stripe->work, scrub_stripe_read_repair_worker); -@@ -1270,7 +1241,7 @@ static void scrub_write_endio(struct btrfs_bio *bbio) +@@ -1274,7 +1245,7 @@ static void scrub_write_endio(struct btrfs_bio *bbio) { struct scrub_stripe *stripe = bbio->private; struct btrfs_fs_info *fs_info = stripe->bg->fs_info; @@ -4158,7 +3287,7 @@ diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c const u32 bio_size = bio_get_size(&bbio->bio); if (bbio->bio.bi_status) { -@@ -1349,7 +1320,7 @@ static void scrub_write_sectors(struct scrub_ctx *sctx, struct scrub_stripe *str +@@ -1353,7 +1324,7 @@ static void scrub_write_sectors(struct scrub_ctx *sctx, struct scrub_stripe *str bbio = NULL; } if (!bbio) @@ -4167,7 +3296,7 @@ diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c stripe->logical + (sector_nr << fs_info->sectorsize_bits), scrub_write_endio, stripe); scrub_bio_add_sector(bbio, stripe, sector_nr); -@@ -1844,7 +1815,7 @@ static void scrub_submit_extent_sector_read(struct scrub_stripe *stripe) +@@ -1848,7 +1819,7 @@ static void scrub_submit_extent_sector_read(struct scrub_stripe *stripe) continue; } @@ -4176,7 +3305,7 @@ diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c logical, scrub_read_endio, stripe); } -@@ -1869,7 +1840,6 @@ static void scrub_submit_initial_read(struct scrub_ctx *sctx, +@@ -1873,7 +1844,6 @@ static void scrub_submit_initial_read(struct scrub_ctx *sctx, { struct btrfs_fs_info *fs_info = sctx->fs_info; struct btrfs_bio *bbio; @@ -4184,7 +3313,7 @@ diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c unsigned int nr_sectors = stripe_length(stripe) >> fs_info->sectorsize_bits; int mirror = stripe->mirror_num; -@@ -1882,7 +1852,7 @@ static void scrub_submit_initial_read(struct scrub_ctx *sctx, +@@ -1886,7 +1856,7 @@ static void scrub_submit_initial_read(struct scrub_ctx *sctx, return; } @@ -4193,7 +3322,7 @@ diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c stripe->logical, scrub_read_endio, stripe); /* Read the whole range inside the chunk boundary. */ for (unsigned int cur = 0; cur < nr_sectors; cur++) -@@ -2138,7 +2108,7 @@ static int scrub_raid56_cached_parity(struct scrub_ctx *sctx, +@@ -2142,7 +2112,7 @@ static int scrub_raid56_cached_parity(struct scrub_ctx *sctx, for (int i = 0; i < data_stripes; i++) { struct scrub_stripe *stripe = &sctx->raid56_data_stripes[i]; @@ -4202,7 +3331,7 @@ diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c full_stripe_start + (i << BTRFS_STRIPE_LEN_SHIFT)); } raid56_parity_submit_scrub_rbio(rbio); -@@ -3091,14 +3061,6 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start, +@@ -3095,14 +3065,6 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start, /* At mount time we have ensured nodesize is in the range of [4K, 64K]. */ ASSERT(fs_info->nodesize <= BTRFS_STRIPE_LEN); @@ -4436,16 +3565,7 @@ diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c -@@ -698,8 +698,6 @@ start_transaction(struct btrfs_root *root, unsigned int num_items, - goto alloc_fail; - } - -- xa_init(&h->writeback_inhibited_ebs); -- - /* - * If we are JOIN_NOLOCK we're already committing a transaction and - * waiting on this guy, so we don't need to do the sb_start_intwrite -@@ -1519,12 +1517,8 @@ static noinline int commit_fs_roots(struct btrfs_trans_handle *trans) +@@ -1528,12 +1528,8 @@ static noinline int commit_fs_roots(struct btrfs_trans_handle *trans) ASSERT(atomic_read(&root->log_writers) == 0, "atomic_read(&root->log_writers)=%d", atomic_read(&root->log_writers)); @@ -4460,7 +3580,7 @@ diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c radix_tree_tag_clear(&fs_info->fs_roots_radix, (unsigned long)btrfs_root_id(root), -@@ -1642,7 +1636,7 @@ static int qgroup_account_snapshot(struct btrfs_trans_handle *trans, +@@ -1651,7 +1647,7 @@ static int qgroup_account_snapshot(struct btrfs_trans_handle *trans, ret = btrfs_write_and_wait_transaction(trans); if (unlikely(ret)) { btrfs_err(fs_info, @@ -4469,16 +3589,16 @@ diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c return ret; } -@@ -2588,7 +2582,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) +@@ -2597,7 +2593,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) ret = btrfs_write_and_wait_transaction(trans); if (unlikely(ret)) { - btrfs_err(fs_info, "error while writing out transaction: %d", ret); + btrfs_err(fs_info, "error while writing out transaction: %pe", ERR_PTR(ret)); - mutex_unlock(&fs_info->tree_log_mutex); - goto scrub_continue; - } -@@ -2749,8 +2743,8 @@ void __cold __btrfs_abort_transaction(struct btrfs_trans_handle *trans, + /* + * Abort before releasing tree_log_mutex, so a log sync waiting + * on it sees the fs error and skips writing super_for_commit +@@ -2764,8 +2760,8 @@ void __cold __btrfs_abort_transaction(struct btrfs_trans_handle *trans, WRITE_ONCE(trans->transaction->aborted, error); trace_btrfs_transaction_abort(trans); if (first_hit) { @@ -4489,67 +3609,6 @@ diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c if (error == -ENOSPC) btrfs_dump_space_info_for_trans_abort(fs_info); } -diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h ---- a/fs/btrfs/transaction.h -+++ b/fs/btrfs/transaction.h -@@ -7,12 +7,12 @@ - #define BTRFS_TRANSACTION_H - - #include -+#include - #include - #include - #include - #include - #include --#include - #include "btrfs_inode.h" - #include "delayed-ref.h" - -@@ -23,6 +23,7 @@ struct btrfs_fs_info; - struct btrfs_root_item; - struct btrfs_root; - struct btrfs_path; -+struct extent_buffer; - - /* - * Signal that a direct IO write is in progress, to avoid deadlock for sync -@@ -136,6 +137,18 @@ enum { - - #define TRANS_EXTWRITERS (__TRANS_START | __TRANS_ATTACH) - -+/* -+ * Number of extent buffers a transaction handle tracks for writeback -+ * inhibition. The CLOCK reference bits pack into a u32 so this must not exceed -+ * 32, and keeping it a power of two lets the compiler reduce the CLOCK hand -+ * modulo to a mask. -+ */ -+#define BTRFS_INHIBITED_EBS_SLOTS 8 -+ -+static_assert(BTRFS_INHIBITED_EBS_SLOTS <= 32); -+static_assert(BTRFS_INHIBITED_EBS_SLOTS != 0 && -+ (BTRFS_INHIBITED_EBS_SLOTS & (BTRFS_INHIBITED_EBS_SLOTS - 1)) == 0); -+ - struct btrfs_trans_handle { - u64 transid; - u64 bytes_reserved; -@@ -163,8 +176,14 @@ struct btrfs_trans_handle { - struct btrfs_fs_info *fs_info; - struct list_head new_bgs; - struct btrfs_block_rsv delayed_rsv; -- /* Extent buffers with writeback inhibited by this handle. */ -- struct xarray writeback_inhibited_ebs; -+ -+ /* Extent buffers this handle has inhibited writeback on. */ -+ struct extent_buffer *inhibited_ebs[BTRFS_INHIBITED_EBS_SLOTS]; -+ /* CLOCK reference bit per slot. */ -+ u32 inhibited_ebs_referenced; -+ u32 nr_inhibited_ebs; -+ /* CLOCK hand. */ -+ u32 inhibited_ebs_hand; - }; - - /* diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -4811,7 +3870,7 @@ diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c diff --git a/fs/btrfs/verity.c b/fs/btrfs/verity.c --- a/fs/btrfs/verity.c +++ b/fs/btrfs/verity.c -@@ -638,7 +638,7 @@ static int btrfs_end_enable_verity(struct file *filp, const void *desc, +@@ -652,7 +652,7 @@ static int btrfs_end_enable_verity(struct file *filp, const void *desc, rollback_ret = rollback_verity(inode); if (rollback_ret) btrfs_err(inode->root->fs_info, @@ -4820,46 +3879,6 @@ diff --git a/fs/btrfs/verity.c b/fs/btrfs/verity.c return ret; } -@@ -720,14 +720,18 @@ static struct page *btrfs_read_merkle_tree_page(struct inode *inode, - goto out; - - folio_lock(folio); -- /* If it's not uptodate after we have the lock, we got a read error. */ -- if (!folio_test_uptodate(folio)) { -+ /* Folio was truncated from mapping. */ -+ if (!folio->mapping) { - folio_unlock(folio); - folio_put(folio); -- return ERR_PTR(-EIO); -+ goto again; - } -- folio_unlock(folio); -- goto out; -+ /* Another reader may have filled the folio while we waited. */ -+ if (folio_test_uptodate(folio)) { -+ folio_unlock(folio); -+ goto out; -+ } -+ goto read_folio; - } - - folio = filemap_alloc_folio(mapping_gfp_constraint(inode->i_mapping, ~__GFP_FS), -@@ -744,6 +748,7 @@ static struct page *btrfs_read_merkle_tree_page(struct inode *inode, - return ERR_PTR(ret); - } - -+read_folio: - /* - * Merkle item keys are indexed from byte 0 in the merkle tree. - * They have the form: -@@ -753,6 +758,7 @@ static struct page *btrfs_read_merkle_tree_page(struct inode *inode, - ret = read_key_bytes(BTRFS_I(inode), BTRFS_VERITY_MERKLE_ITEM_KEY, off, - folio_address(folio), PAGE_SIZE, folio); - if (ret < 0) { -+ folio_unlock(folio); - folio_put(folio); - return ERR_PTR(ret); - } diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -4914,7 +3933,7 @@ diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c const char *old_name; /* -@@ -4588,7 +4553,7 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_info) +@@ -4592,7 +4557,7 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_info) if (ret == -ENOSPC) { enospc_errors++; } else if (ret == -ETXTBSY) { @@ -4923,7 +3942,7 @@ diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c "skipping relocation of block group %llu due to active swapfile", found_key.offset); ret = 0; -@@ -6049,6 +6014,19 @@ struct btrfs_chunk_map *btrfs_alloc_chunk_map(int num_stripes, gfp_t gfp) +@@ -6053,6 +6018,19 @@ struct btrfs_chunk_map *btrfs_alloc_chunk_map(int num_stripes, gfp_t gfp) return map; } @@ -4943,7 +3962,7 @@ diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c static struct btrfs_block_group *create_chunk(struct btrfs_trans_handle *trans, struct alloc_chunk_ctl *ctl, struct btrfs_device_info *devices_info) -@@ -6067,11 +6045,10 @@ static struct btrfs_block_group *create_chunk(struct btrfs_trans_handle *trans, +@@ -6071,11 +6049,10 @@ static struct btrfs_block_group *create_chunk(struct btrfs_trans_handle *trans, map->start = start; map->chunk_len = ctl->chunk_size; map->stripe_size = ctl->stripe_size; @@ -4957,7 +3976,7 @@ diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c for (int i = 0; i < ctl->ndevs; i++) { for (int j = 0; j < ctl->dev_stripes; j++) { -@@ -6250,7 +6227,7 @@ int btrfs_chunk_alloc_add_chunk_item(struct btrfs_trans_handle *trans, +@@ -6254,7 +6231,7 @@ int btrfs_chunk_alloc_add_chunk_item(struct btrfs_trans_handle *trans, btrfs_set_stack_chunk_length(chunk, bg->length); btrfs_set_stack_chunk_owner(chunk, BTRFS_EXTENT_TREE_OBJECTID); btrfs_set_stack_chunk_stripe_len(chunk, BTRFS_STRIPE_LEN); @@ -4966,7 +3985,7 @@ diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c btrfs_set_stack_chunk_num_stripes(chunk, map->num_stripes); btrfs_set_stack_chunk_io_align(chunk, BTRFS_STRIPE_LEN); btrfs_set_stack_chunk_io_width(chunk, BTRFS_STRIPE_LEN); -@@ -7632,9 +7609,7 @@ static int read_one_chunk(struct btrfs_key *key, struct extent_buffer *leaf, +@@ -7636,9 +7613,7 @@ static int read_one_chunk(struct btrfs_key *key, struct extent_buffer *leaf, map->start = logical; map->chunk_len = length; map->num_stripes = num_stripes; @@ -4977,7 +3996,7 @@ diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c /* * We can't use the sub_stripes value, as for profiles other than * RAID10, they may have 0 as sub_stripes for filesystems created by -@@ -7645,6 +7620,7 @@ static int read_one_chunk(struct btrfs_key *key, struct extent_buffer *leaf, +@@ -7649,6 +7624,7 @@ static int read_one_chunk(struct btrfs_key *key, struct extent_buffer *leaf, */ map->sub_stripes = btrfs_raid_array[index].sub_stripes; map->verified_stripes = 0; diff --git a/pkgbuilds/linux-omarchy-bore/0300-btrfs.patch.sig b/pkgbuilds/linux-omarchy-bore/0300-btrfs.patch.sig index 1586085..c992405 100644 Binary files a/pkgbuilds/linux-omarchy-bore/0300-btrfs.patch.sig and b/pkgbuilds/linux-omarchy-bore/0300-btrfs.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0301-btrfs-fixes-1.patch b/pkgbuilds/linux-omarchy-bore/0301-btrfs-fixes-1.patch new file mode 100644 index 0000000..8cc1ec5 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0301-btrfs-fixes-1.patch @@ -0,0 +1,92 @@ +diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c +--- a/fs/btrfs/dev-replace.c ++++ b/fs/btrfs/dev-replace.c +@@ -627,7 +627,7 @@ static int btrfs_dev_replace_start(struct btrfs_fs_info *fs_info, + + ret = mark_block_group_to_copy(fs_info, src_device); + if (ret) +- return ret; ++ goto leave; + + down_write(&dev_replace->rwsem); + dev_replace->replace_task = current; +diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c +--- a/fs/btrfs/ioctl.c ++++ b/fs/btrfs/ioctl.c +@@ -384,6 +384,7 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap, + inode_flags &= ~BTRFS_INODE_COMPRESS; + inode_flags |= BTRFS_INODE_NOCOMPRESS; + } else if (fsflags & FS_COMPR_FL) { ++ enum btrfs_compression_type comp_type; + + if (IS_SWAPFILE(&inode->vfs_inode)) + return -ETXTBSY; +@@ -391,9 +392,23 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap, + inode_flags |= BTRFS_INODE_COMPRESS; + inode_flags &= ~BTRFS_INODE_NOCOMPRESS; + +- comp = btrfs_compress_type2str(fs_info->compress_type); +- if (!comp || comp[0] == 0) +- comp = btrfs_compress_type2str(BTRFS_COMPRESS_ZLIB); ++ /* ++ * Keep the algorithm recorded in the compression property, ++ * otherwise changing an unrelated attribute would reset it to ++ * the mount default, since FS_IOC_SETFLAGS callers write back ++ * the whole flag set they got from FS_IOC_GETFLAGS and that ++ * includes FS_COMPR_FL for any inode carrying the property. ++ * ++ * Inodes with the compress flag set but no property keep using ++ * the mount default, so they behave as before. ++ */ ++ if (inode->prop_compress) ++ comp_type = inode->prop_compress; ++ else if (fs_info->compress_type) ++ comp_type = fs_info->compress_type; ++ else ++ comp_type = BTRFS_COMPRESS_ZLIB; ++ comp = btrfs_compress_type2str(comp_type); + } else { + inode_flags &= ~(BTRFS_INODE_COMPRESS | BTRFS_INODE_NOCOMPRESS); + } +diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c +--- a/fs/btrfs/send.c ++++ b/fs/btrfs/send.c +@@ -6417,6 +6417,13 @@ static int process_extent(struct send_ctx *sctx, + + if (S_ISLNK(sctx->cur_inode_mode)) + return 0; ++ if (unlikely(!S_ISREG(sctx->cur_inode_mode))) { ++ btrfs_crit(sctx->send_root->fs_info, ++ "send: extent for non-regular inode %llu root %llu mode 0%llo", ++ key->objectid, btrfs_root_id(sctx->send_root), ++ sctx->cur_inode_mode & S_IFMT); ++ return -EUCLEAN; ++ } + + if (sctx->parent_root && !sctx->cur_inode_new) { + ret = is_extent_unchanged(sctx, path, key); +diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c +--- a/fs/btrfs/zoned.c ++++ b/fs/btrfs/zoned.c +@@ -2710,6 +2710,7 @@ int btrfs_zone_finish_endio(struct btrfs_fs_info *fs_info, u64 logical, u64 leng + { + struct btrfs_block_group *block_group; + u64 min_alloc_bytes; ++ int ret = 0; + + if (!btrfs_is_zoned(fs_info)) + return 0; +@@ -2729,11 +2730,11 @@ int btrfs_zone_finish_endio(struct btrfs_fs_info *fs_info, u64 logical, u64 leng + block_group->start + block_group->zone_capacity) + goto out; + +- do_zone_finish(block_group, true); ++ ret = do_zone_finish(block_group, true); + + out: + btrfs_put_block_group(block_group); +- return 0; ++ return ret; + } + + static void btrfs_zone_finish_endio_workfn(struct work_struct *work) diff --git a/pkgbuilds/linux-omarchy-bore/0301-btrfs-fixes-1.patch.sig b/pkgbuilds/linux-omarchy-bore/0301-btrfs-fixes-1.patch.sig new file mode 100644 index 0000000..e43d3d6 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0301-btrfs-fixes-1.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0301-btrfs-fixes.patch b/pkgbuilds/linux-omarchy-bore/0301-btrfs-fixes.patch deleted file mode 100644 index c5a4318..0000000 --- a/pkgbuilds/linux-omarchy-bore/0301-btrfs-fixes.patch +++ /dev/null @@ -1,386 +0,0 @@ -diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c ---- a/fs/btrfs/dev-replace.c -+++ b/fs/btrfs/dev-replace.c -@@ -626,7 +626,7 @@ static int btrfs_dev_replace_start(struct btrfs_fs_info *fs_info, - - ret = mark_block_group_to_copy(fs_info, src_device); - if (ret) -- return ret; -+ goto leave; - - down_write(&dev_replace->rwsem); - dev_replace->replace_task = current; -diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c ---- a/fs/btrfs/inode.c -+++ b/fs/btrfs/inode.c -@@ -3436,6 +3436,9 @@ int btrfs_finish_one_ordered(struct btrfs_ordered_extent *ordered_extent) - */ - btrfs_remove_ordered_extent(ordered_extent); - -+ /* Cleanup any remaining biocs attached to the OE. */ -+ btrfs_cleanup_ordered_bioc_list(ordered_extent); -+ - /* once for us */ - btrfs_put_ordered_extent(ordered_extent); - /* once for the tree */ -diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c ---- a/fs/btrfs/ioctl.c -+++ b/fs/btrfs/ioctl.c -@@ -384,6 +384,7 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap, - inode_flags &= ~BTRFS_INODE_COMPRESS; - inode_flags |= BTRFS_INODE_NOCOMPRESS; - } else if (fsflags & FS_COMPR_FL) { -+ enum btrfs_compression_type comp_type; - - if (IS_SWAPFILE(&inode->vfs_inode)) - return -ETXTBSY; -@@ -391,9 +392,23 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap, - inode_flags |= BTRFS_INODE_COMPRESS; - inode_flags &= ~BTRFS_INODE_NOCOMPRESS; - -- comp = btrfs_compress_type2str(fs_info->compress_type); -- if (!comp || comp[0] == 0) -- comp = btrfs_compress_type2str(BTRFS_COMPRESS_ZLIB); -+ /* -+ * Keep the algorithm recorded in the compression property, -+ * otherwise changing an unrelated attribute would reset it to -+ * the mount default, since FS_IOC_SETFLAGS callers write back -+ * the whole flag set they got from FS_IOC_GETFLAGS and that -+ * includes FS_COMPR_FL for any inode carrying the property. -+ * -+ * Inodes with the compress flag set but no property keep using -+ * the mount default, so they behave as before. -+ */ -+ if (inode->prop_compress) -+ comp_type = inode->prop_compress; -+ else if (fs_info->compress_type) -+ comp_type = fs_info->compress_type; -+ else -+ comp_type = BTRFS_COMPRESS_ZLIB; -+ comp = btrfs_compress_type2str(comp_type); - } else { - inode_flags &= ~(BTRFS_INODE_COMPRESS | BTRFS_INODE_NOCOMPRESS); - } -diff --git a/fs/btrfs/raid-stripe-tree.c b/fs/btrfs/raid-stripe-tree.c ---- a/fs/btrfs/raid-stripe-tree.c -+++ b/fs/btrfs/raid-stripe-tree.c -@@ -310,8 +310,10 @@ static int update_raid_extent_item(struct btrfs_trans_handle *trans, - - ret = btrfs_search_slot(trans, trans->fs_info->stripe_root, key, path, - 0, 1); -- if (ret) -- return (ret == 1 ? ret : -EINVAL); -+ if (ret > 0) -+ ret = -ENOENT; -+ if (ret < 0) -+ return ret; - - leaf = path->nodes[0]; - slot = path->slots[0]; -@@ -337,7 +339,6 @@ int btrfs_insert_one_raid_extent(struct btrfs_trans_handle *trans, - stripe_extent = kzalloc(item_size, GFP_NOFS); - if (unlikely(!stripe_extent)) { - btrfs_abort_transaction(trans, -ENOMEM); -- btrfs_end_transaction(trans); - return -ENOMEM; - } - -@@ -374,7 +375,7 @@ int btrfs_insert_raid_extent(struct btrfs_trans_handle *trans, - struct btrfs_ordered_extent *ordered_extent) - { - struct btrfs_io_context *bioc; -- int ret; -+ int ret = 0; - - if (!btrfs_fs_incompat(trans->fs_info, RAID_STRIPE_TREE)) - return 0; -@@ -382,17 +383,23 @@ int btrfs_insert_raid_extent(struct btrfs_trans_handle *trans, - list_for_each_entry(bioc, &ordered_extent->bioc_list, rst_ordered_entry) { - ret = btrfs_insert_one_raid_extent(trans, bioc); - if (ret) -- return ret; -+ break; - } - -- while (!list_empty(&ordered_extent->bioc_list)) { -- bioc = list_first_entry(&ordered_extent->bioc_list, -+ btrfs_cleanup_ordered_bioc_list(ordered_extent); -+ return ret; -+} -+ -+void btrfs_cleanup_ordered_bioc_list(struct btrfs_ordered_extent *ordered) -+{ -+ while (!list_empty(&ordered->bioc_list)) { -+ struct btrfs_io_context *bioc; -+ -+ bioc = list_first_entry(&ordered->bioc_list, - typeof(*bioc), rst_ordered_entry); - list_del(&bioc->rst_ordered_entry); - btrfs_put_bioc(bioc); - } -- -- return 0; - } - - int btrfs_get_raid_extent_offset(struct btrfs_fs_info *fs_info, -diff --git a/fs/btrfs/raid-stripe-tree.h b/fs/btrfs/raid-stripe-tree.h ---- a/fs/btrfs/raid-stripe-tree.h -+++ b/fs/btrfs/raid-stripe-tree.h -@@ -28,6 +28,7 @@ int btrfs_get_raid_extent_offset(struct btrfs_fs_info *fs_info, - u32 stripe_index, struct btrfs_io_stripe *stripe); - int btrfs_insert_raid_extent(struct btrfs_trans_handle *trans, - struct btrfs_ordered_extent *ordered_extent); -+void btrfs_cleanup_ordered_bioc_list(struct btrfs_ordered_extent *ordered); - - #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS - int btrfs_insert_one_raid_extent(struct btrfs_trans_handle *trans, -diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c ---- a/fs/btrfs/scrub.c -+++ b/fs/btrfs/scrub.c -@@ -1023,6 +1023,10 @@ static void scrub_stripe_report_errors(struct scrub_ctx *sctx, - - skip: - for_each_set_bit(sector_nr, &extent_bitmap, stripe->nr_sectors) { -+ const u64 sector_logical = stripe->logical + -+ ((u64)sector_nr << fs_info->sectorsize_bits); -+ const u64 sector_physical = physical + -+ ((u64)sector_nr << fs_info->sectorsize_bits); - bool repaired = false; - - if (scrub_bitmap_test_bit_is_metadata(stripe, sector_nr)) { -@@ -1051,12 +1055,12 @@ static void scrub_stripe_report_errors(struct scrub_ctx *sctx, - if (dev) { - btrfs_err_rl(fs_info, - "scrub: fixed up error at logical %llu on dev %s physical %llu", -- stripe->logical, btrfs_dev_name(dev), -- physical); -+ sector_logical, btrfs_dev_name(dev), -+ sector_physical); - } else { - btrfs_err_rl(fs_info, - "scrub: fixed up error at logical %llu on mirror %u", -- stripe->logical, stripe->mirror_num); -+ sector_logical, stripe->mirror_num); - } - continue; - } -@@ -1065,30 +1069,30 @@ static void scrub_stripe_report_errors(struct scrub_ctx *sctx, - if (dev) { - btrfs_err_rl(fs_info, - "scrub: unable to fixup (regular) error at logical %llu on dev %s physical %llu", -- stripe->logical, btrfs_dev_name(dev), -- physical); -+ sector_logical, btrfs_dev_name(dev), -+ sector_physical); - } else { - btrfs_err_rl(fs_info, - "scrub: unable to fixup (regular) error at logical %llu on mirror %u", -- stripe->logical, stripe->mirror_num); -+ sector_logical, stripe->mirror_num); - } - - if (scrub_bitmap_test_bit_io_error(stripe, sector_nr)) - if (__ratelimit(&rs) && dev) - scrub_print_common_warning("i/o error", dev, false, -- stripe->logical, physical); -+ sector_logical, sector_physical); - if (scrub_bitmap_test_bit_csum_error(stripe, sector_nr)) - if (__ratelimit(&rs) && dev) - scrub_print_common_warning("checksum error", dev, false, -- stripe->logical, physical); -+ sector_logical, sector_physical); - if (scrub_bitmap_test_bit_meta_error(stripe, sector_nr)) - if (__ratelimit(&rs) && dev) - scrub_print_common_warning("header error", dev, false, -- stripe->logical, physical); -+ sector_logical, sector_physical); - if (scrub_bitmap_test_bit_meta_gen_error(stripe, sector_nr)) - if (__ratelimit(&rs) && dev) - scrub_print_common_warning("generation error", dev, false, -- stripe->logical, physical); -+ sector_logical, sector_physical); - } - - /* Update the device stats. */ -diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c ---- a/fs/btrfs/send.c -+++ b/fs/btrfs/send.c -@@ -2065,7 +2065,7 @@ static int will_overwrite_ref(struct send_ctx *sctx, u64 dir, u64 dir_gen, - - ret = is_inode_existent(sctx, dir, dir_gen, NULL, &parent_root_dir_gen); - if (ret <= 0) -- return 0; -+ return ret; - - /* - * If we have a parent root we need to verify that the parent dir was -@@ -6417,6 +6417,13 @@ static int process_extent(struct send_ctx *sctx, - - if (S_ISLNK(sctx->cur_inode_mode)) - return 0; -+ if (unlikely(!S_ISREG(sctx->cur_inode_mode))) { -+ btrfs_crit(sctx->send_root->fs_info, -+ "send: extent for non-regular inode %llu root %llu mode 0%llo", -+ key->objectid, btrfs_root_id(sctx->send_root), -+ sctx->cur_inode_mode & S_IFMT); -+ return -EUCLEAN; -+ } - - if (sctx->parent_root && !sctx->cur_inode_new) { - ret = is_extent_unchanged(sctx, path, key); -diff --git a/fs/btrfs/tests/extent-io-tests.c b/fs/btrfs/tests/extent-io-tests.c ---- a/fs/btrfs/tests/extent-io-tests.c -+++ b/fs/btrfs/tests/extent-io-tests.c -@@ -133,14 +133,14 @@ static int test_find_delalloc(u32 sectorsize, u32 nodesize) - if (IS_ERR(root)) { - test_std_err(TEST_ALLOC_ROOT); - ret = PTR_ERR(root); -- goto out; -+ goto out_root_info; - } - - inode = btrfs_new_test_inode(); - if (!inode) { - test_std_err(TEST_ALLOC_INODE); - ret = -ENOMEM; -- goto out; -+ goto out_root_info; - } - tmp = &BTRFS_I(inode)->io_tree; - BTRFS_I(inode)->root = root; -@@ -333,6 +333,7 @@ static int test_find_delalloc(u32 sectorsize, u32 nodesize) - process_page_range(inode, 0, total_dirty - 1, - PROCESS_UNLOCK | PROCESS_RELEASE); - iput(inode); -+out_root_info: - btrfs_free_dummy_root(root); - btrfs_free_dummy_fs_info(fs_info); - return ret; -diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c ---- a/fs/btrfs/transaction.c -+++ b/fs/btrfs/transaction.c -@@ -458,8 +458,19 @@ static int record_root_in_trans(struct btrfs_trans_handle *trans, - * through btrfs_record_root_in_trans without having to take the - * lock. smp_wmb() makes sure that all the writes above are - * done before we pop in the zero below -+ * -+ * If @force is true, it means the call is from -+ * qgroup_account_snapshot(), which only requires radix tree -+ * tracking. -+ * We should not force reloc root creation here, as the root -+ * may have already been modified, and in that case -+ * root->commit_root has already been dropped. -+ * -+ * Using that commit root will cause the reloc root to refer -+ * to a deleted extent, causing extent tree corruption. - */ -- ret = btrfs_init_reloc_root(trans, root); -+ if (!force) -+ ret = btrfs_init_reloc_root(trans, root); - smp_mb__before_atomic(); - clear_bit(BTRFS_ROOT_IN_TRANS_SETUP, &root->state); - } -@@ -2583,6 +2594,12 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) - ret = btrfs_write_and_wait_transaction(trans); - if (unlikely(ret)) { - btrfs_err(fs_info, "error while writing out transaction: %pe", ERR_PTR(ret)); -+ /* -+ * Abort before releasing tree_log_mutex, so a log sync waiting -+ * on it sees the fs error and skips writing super_for_commit -+ * for this failed transaction. See btrfs_sync_log(). -+ */ -+ btrfs_abort_transaction(trans, ret); - mutex_unlock(&fs_info->tree_log_mutex); - goto scrub_continue; - } -diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c ---- a/fs/btrfs/volumes.c -+++ b/fs/btrfs/volumes.c -@@ -3035,7 +3035,11 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path - error_sysfs: - btrfs_sysfs_remove_device(device); - mutex_lock(&fs_info->fs_devices->device_list_mutex); -+ if (seeding_dev) -+ btrfs_assign_next_active_device(device, seed_devices->latest_dev); - mutex_lock(&fs_info->chunk_mutex); -+ if (!list_empty(&device->post_commit_list)) -+ list_del_init(&device->post_commit_list); - list_del_rcu(&device->dev_list); - list_del(&device->dev_alloc_list); - fs_info->fs_devices->num_devices--; -diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c ---- a/fs/btrfs/zoned.c -+++ b/fs/btrfs/zoned.c -@@ -2626,16 +2626,13 @@ static int do_zone_finish(struct btrfs_block_group *block_group, bool fully_writ - down_read(&dev_replace->rwsem); - map = block_group->physical_map; - for (i = 0; i < map->num_stripes; i++) { -- - ret = call_zone_finish(block_group, &map->stripes[i]); -- if (ret) { -- up_read(&dev_replace->rwsem); -- return ret; -- } -+ if (ret) -+ break; - } - up_read(&dev_replace->rwsem); - -- if (!fully_written) -+ if (!ret && !fully_written) - btrfs_dec_block_group_ro(block_group); - - spin_lock(&fs_info->zone_active_bgs_lock); -@@ -2648,7 +2645,7 @@ static int do_zone_finish(struct btrfs_block_group *block_group, bool fully_writ - - clear_and_wake_up_bit(BTRFS_FS_NEED_ZONE_FINISH, &fs_info->flags); - -- return 0; -+ return ret; - } - - int btrfs_zone_finish(struct btrfs_block_group *block_group) -@@ -2713,6 +2710,7 @@ int btrfs_zone_finish_endio(struct btrfs_fs_info *fs_info, u64 logical, u64 leng - { - struct btrfs_block_group *block_group; - u64 min_alloc_bytes; -+ int ret = 0; - - if (!btrfs_is_zoned(fs_info)) - return 0; -@@ -2732,11 +2730,11 @@ int btrfs_zone_finish_endio(struct btrfs_fs_info *fs_info, u64 logical, u64 leng - block_group->start + block_group->zone_capacity) - goto out; - -- do_zone_finish(block_group, true); -+ ret = do_zone_finish(block_group, true); - - out: - btrfs_put_block_group(block_group); -- return 0; -+ return ret; - } - - static void btrfs_zone_finish_endio_workfn(struct work_struct *work) -diff --git a/fs/btrfs/zstd.c b/fs/btrfs/zstd.c ---- a/fs/btrfs/zstd.c -+++ b/fs/btrfs/zstd.c -@@ -307,8 +307,17 @@ struct list_head *zstd_get_workspace(struct btrfs_fs_info *fs_info, int level) - DEFINE_WAIT(wait); - - prepare_to_wait(&zwsm->wait, &wait, TASK_UNINTERRUPTIBLE); -- schedule(); -+ /* -+ * Re-check after being queued: zstd_put_workspace() only wakes -+ * a queue that already has a sleeper, so a workspace returned -+ * since the failed allocation woke nobody. -+ */ -+ ws = zstd_find_workspace(fs_info, level); -+ if (!ws) -+ schedule(); - finish_wait(&zwsm->wait, &wait); -+ if (ws) -+ return ws; - - goto again; - } diff --git a/pkgbuilds/linux-omarchy-bore/0301-btrfs-fixes.patch.sig b/pkgbuilds/linux-omarchy-bore/0301-btrfs-fixes.patch.sig deleted file mode 100644 index b319318..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/0301-btrfs-fixes.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/0303-btrfs-fixes-2.patch b/pkgbuilds/linux-omarchy-bore/0303-btrfs-fixes-2.patch new file mode 100644 index 0000000..42654e7 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0303-btrfs-fixes-2.patch @@ -0,0 +1,97 @@ +diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c +--- a/fs/btrfs/block-group.c ++++ b/fs/btrfs/block-group.c +@@ -3074,6 +3074,18 @@ struct btrfs_block_group *btrfs_make_block_group(struct btrfs_trans_handle *tran + return ERR_PTR(ret); + } + ++ /* ++ * Ensure the corresponding space_info object is created and ++ * assigned to our block group. We want our bg to be added to the rbtree ++ * with its ->space_info set. ++ * ++ * On a zoned filesystem btrfs_add_new_free_space() ends up in ++ * __btrfs_add_free_space_zoned(), which dereferences ++ * block_group->space_info, so it has to be set beforehand. ++ */ ++ cache->space_info = space_info; ++ ASSERT(cache->space_info); ++ + ret = btrfs_add_new_free_space(cache, chunk_offset, chunk_offset + size, NULL); + btrfs_free_excluded_extents(cache); + if (ret) { +@@ -3081,14 +3093,6 @@ struct btrfs_block_group *btrfs_make_block_group(struct btrfs_trans_handle *tran + return ERR_PTR(ret); + } + +- /* +- * Ensure the corresponding space_info object is created and +- * assigned to our block group. We want our bg to be added to the rbtree +- * with its ->space_info set. +- */ +- cache->space_info = space_info; +- ASSERT(cache->space_info); +- + ret = btrfs_add_block_group_cache(cache); + if (ret) { + btrfs_remove_free_space_cache(cache); +diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c +--- a/fs/btrfs/tree-checker.c ++++ b/fs/btrfs/tree-checker.c +@@ -1909,6 +1909,16 @@ static int check_inode_ref(struct extent_buffer *leaf, + return -EUCLEAN; + } + ++ if (unlikely(btrfs_is_fstree(btrfs_header_owner(leaf)) && ++ (key->offset < BTRFS_FIRST_FREE_OBJECTID || ++ key->offset > BTRFS_LAST_FREE_OBJECTID))) { ++ inode_ref_err(leaf, slot, ++ "invalid offset for ref key, have %llu expect [%llu, %lld]", ++ key->offset, BTRFS_FIRST_FREE_OBJECTID, ++ BTRFS_LAST_FREE_OBJECTID); ++ return -EUCLEAN; ++ } ++ + ptr = btrfs_item_ptr_offset(leaf, slot); + end = ptr + btrfs_item_size(leaf, slot); + while (ptr < end) { +@@ -1952,12 +1962,14 @@ static int check_inode_extref(struct extent_buffer *leaf, + { + unsigned long ptr = btrfs_item_ptr_offset(leaf, slot); + unsigned long end = ptr + btrfs_item_size(leaf, slot); ++ const bool is_fstree = btrfs_is_fstree(btrfs_header_owner(leaf)); + + if (unlikely(!check_prev_ino(leaf, key, slot, prev_key))) + return -EUCLEAN; + + while (ptr < end) { + struct btrfs_inode_extref *extref = (struct btrfs_inode_extref *)ptr; ++ u64 parent; + u16 namelen; + + if (unlikely(ptr + sizeof(*extref) > end)) { +@@ -1967,7 +1979,24 @@ static int check_inode_extref(struct extent_buffer *leaf, + return -EUCLEAN; + } + ++ parent = btrfs_inode_extref_parent(leaf, extref); ++ if (unlikely(is_fstree && (parent < BTRFS_FIRST_FREE_OBJECTID || ++ parent > BTRFS_LAST_FREE_OBJECTID))) { ++ inode_ref_err(leaf, slot, ++ "invalid parent for extref key, have %llu expect [%llu, %lld]", ++ parent, BTRFS_FIRST_FREE_OBJECTID, ++ BTRFS_LAST_FREE_OBJECTID); ++ return -EUCLEAN; ++ } ++ + namelen = btrfs_inode_extref_name_len(leaf, extref); ++ if (unlikely(namelen == 0 || namelen > BTRFS_NAME_LEN)) { ++ inode_ref_err(leaf, slot, ++ "invalid inode extref name length, has %u expect [1, %u]", ++ namelen, BTRFS_NAME_LEN); ++ return -EUCLEAN; ++ } ++ + if (unlikely(ptr + sizeof(*extref) + namelen > end)) { + inode_ref_err(leaf, slot, + "inode extref overflow, ptr %lu end %lu namelen %u", diff --git a/pkgbuilds/linux-omarchy-bore/0303-btrfs-fixes-2.patch.sig b/pkgbuilds/linux-omarchy-bore/0303-btrfs-fixes-2.patch.sig new file mode 100644 index 0000000..705b8ba Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0303-btrfs-fixes-2.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0304-btrfs-fixes-3.patch b/pkgbuilds/linux-omarchy-bore/0304-btrfs-fixes-3.patch new file mode 100644 index 0000000..7d1ab48 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0304-btrfs-fixes-3.patch @@ -0,0 +1,77 @@ +diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c +--- a/fs/btrfs/tree-checker.c ++++ b/fs/btrfs/tree-checker.c +@@ -2306,7 +2306,7 @@ static int check_free_space_extent(struct extent_buffer *leaf, struct btrfs_key + + if (unlikely(btrfs_item_size(leaf, slot) != 0)) { + generic_err(leaf, slot, +- "invalid item size for free space info, has %u expect 0", ++ "invalid item size for free space extent, has %u expect 0", + btrfs_item_size(leaf, slot)); + return -EUCLEAN; + } +diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c +--- a/fs/btrfs/volumes.c ++++ b/fs/btrfs/volumes.c +@@ -740,6 +740,36 @@ const u8 *btrfs_sb_fsid_ptr(const struct btrfs_super_block *sb) + return has_metadata_uuid ? sb->metadata_uuid : sb->fsid; + } + ++static bool should_rename_device(const struct btrfs_device *dev) ++{ ++ bool ret; ++ const char *old_name; ++ ++ rcu_read_lock(); ++ old_name = rcu_dereference(dev->name); ++ /* ++ * For systems booted without an initramfs, the rootfs has the device ++ * name "/dev/root". ++ * ++ * Although using btrfs without an initramfs is not recommended (if a ++ * new device is added to the rootfs, the system can no longer boot, as ++ * there is no way to register all devices), there is still a minority ++ * of users doing this. ++ * ++ * And after the system is up, a later device scan on the real block ++ * device file will never get this device's name updated, as the ++ * device->devt is still the same. ++ * ++ * Here we add one and only one exception for "/dev/root", to allow the ++ * device name to be updated even if the new path points to the same ++ * block device. ++ */ ++ ret = (strcmp(old_name, "/dev/root") == 0); ++ rcu_read_unlock(); ++ ++ return ret; ++} ++ + /* + * Add new device to list of registered devices + * +@@ -860,7 +890,8 @@ static noinline struct btrfs_device *device_list_add(const char *path, + MAJOR(path_devt), MINOR(path_devt), + current->comm, task_pid_nr(current)); + +- } else if (!device->name || device->devt != path_devt) { ++ } else if (!device->name || device->devt != path_devt || ++ should_rename_device(device)) { + const char *old_name; + + /* +diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c +--- a/fs/btrfs/zoned.c ++++ b/fs/btrfs/zoned.c +@@ -2688,6 +2688,11 @@ bool btrfs_can_activate_zone(struct btrfs_fs_devices *fs_devices, u64 flags) + + switch (flags & BTRFS_BLOCK_GROUP_PROFILE_MASK) { + case 0: /* single */ ++ case BTRFS_BLOCK_GROUP_RAID0: ++ case BTRFS_BLOCK_GROUP_RAID1: ++ case BTRFS_BLOCK_GROUP_RAID1C3: ++ case BTRFS_BLOCK_GROUP_RAID1C4: ++ case BTRFS_BLOCK_GROUP_RAID10: + ret = (atomic_read(&zinfo->active_zones_left) >= (1 + reserved)); + break; + case BTRFS_BLOCK_GROUP_DUP: diff --git a/pkgbuilds/linux-omarchy-bore/0304-btrfs-fixes-3.patch.sig b/pkgbuilds/linux-omarchy-bore/0304-btrfs-fixes-3.patch.sig new file mode 100644 index 0000000..3e983dc Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0304-btrfs-fixes-3.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0310-fuse-eof-zeroing.patch b/pkgbuilds/linux-omarchy-bore/0310-fuse-eof-zeroing.patch index b34a909..38a6427 100644 --- a/pkgbuilds/linux-omarchy-bore/0310-fuse-eof-zeroing.patch +++ b/pkgbuilds/linux-omarchy-bore/0310-fuse-eof-zeroing.patch @@ -1,7 +1,7 @@ diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c -@@ -2280,6 +2280,9 @@ int fuse_do_setattr(struct mnt_idmap *idmap, struct dentry *dentry, +@@ -2289,6 +2289,9 @@ int fuse_do_setattr(struct mnt_idmap *idmap, struct dentry *dentry, */ if ((is_truncate || !is_wb) && S_ISREG(inode->i_mode) && oldsize != outarg.attr.size) { @@ -28,7 +28,7 @@ diff --git a/fs/fuse/file.c b/fs/fuse/file.c if (inode->i_size < pos + iov_iter_count(ii)) set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); -@@ -2908,6 +2912,11 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, +@@ -2909,6 +2913,11 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, /* we could have extended the file */ if (!(mode & FALLOC_FL_KEEP_SIZE)) { diff --git a/pkgbuilds/linux-omarchy-bore/0310-fuse-eof-zeroing.patch.sig b/pkgbuilds/linux-omarchy-bore/0310-fuse-eof-zeroing.patch.sig index dc63cd6..79483f9 100644 Binary files a/pkgbuilds/linux-omarchy-bore/0310-fuse-eof-zeroing.patch.sig and b/pkgbuilds/linux-omarchy-bore/0310-fuse-eof-zeroing.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0311-fuse-perf.patch b/pkgbuilds/linux-omarchy-bore/0311-fuse-perf.patch index 5162077..ca3c02a 100644 --- a/pkgbuilds/linux-omarchy-bore/0311-fuse-perf.patch +++ b/pkgbuilds/linux-omarchy-bore/0311-fuse-perf.patch @@ -1,7 +1,7 @@ diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c -@@ -216,10 +216,13 @@ EXPORT_SYMBOL_GPL(fuse_req_hash); +@@ -213,10 +213,13 @@ EXPORT_SYMBOL_GPL(fuse_req_hash); /* * A new request is available, wake fiq->waitq */ @@ -17,7 +17,7 @@ diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c kill_fasync(&fiq->fasync, SIGIO, POLL_IN); spin_unlock(&fiq->lock); } -@@ -236,7 +239,7 @@ void fuse_dev_queue_forget(struct fuse_iqueue *fiq, +@@ -233,7 +236,7 @@ void fuse_dev_queue_forget(struct fuse_iqueue *fiq, if (fiq->connected) { fiq->forget_list_tail->next = forget; fiq->forget_list_tail = forget; @@ -26,7 +26,7 @@ diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c } else { kfree(forget); spin_unlock(&fiq->lock); -@@ -258,7 +261,7 @@ void fuse_dev_queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req) +@@ -255,7 +258,7 @@ void fuse_dev_queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req) list_del_init(&req->intr_entry); spin_unlock(&fiq->lock); } else { @@ -35,7 +35,7 @@ diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c } } else { spin_unlock(&fiq->lock); -@@ -288,11 +291,13 @@ EXPORT_SYMBOL_GPL(fuse_request_assign_unique); +@@ -285,11 +288,13 @@ EXPORT_SYMBOL_GPL(fuse_request_assign_unique); static void fuse_dev_queue_req(struct fuse_iqueue *fiq, struct fuse_req *req) { @@ -50,7 +50,7 @@ diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c } else { spin_unlock(&fiq->lock); req->out.h.error = -ENOTCONN; -@@ -755,6 +760,11 @@ static void __fuse_request_send(struct fuse_req *req) +@@ -752,6 +757,11 @@ static void __fuse_request_send(struct fuse_req *req) /* acquire extra reference, since request is still needed after fuse_request_end() */ __fuse_get_request(req); @@ -62,7 +62,7 @@ diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c fuse_send_one(fiq, req); request_wait_answer(req); -@@ -1809,7 +1819,7 @@ void fuse_chan_resend(struct fuse_chan *fch) +@@ -1806,7 +1816,7 @@ void fuse_chan_resend(struct fuse_chan *fch) } /* iq and pq requests are both oldest to newest */ list_splice(&to_queue, &fiq->pending); @@ -94,7 +94,7 @@ diff --git a/fs/fuse/fuse_dev_i.h b/fs/fuse/fuse_dev_i.h diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c -@@ -1417,6 +1417,7 @@ static void process_init_reply(struct fuse_args *args, int error) +@@ -1420,6 +1420,7 @@ static void process_init_reply(struct fuse_args *args, int error) fm->sb->s_bdi->ra_pages = min(fm->sb->s_bdi->ra_pages, ra_pages); diff --git a/pkgbuilds/linux-omarchy-bore/0311-fuse-perf.patch.sig b/pkgbuilds/linux-omarchy-bore/0311-fuse-perf.patch.sig index ff6a782..ef5fe91 100644 Binary files a/pkgbuilds/linux-omarchy-bore/0311-fuse-perf.patch.sig and b/pkgbuilds/linux-omarchy-bore/0311-fuse-perf.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0313-fuse-background-wakeup.patch b/pkgbuilds/linux-omarchy-bore/0313-fuse-background-wakeup.patch index 77e750a..27fbcdc 100644 --- a/pkgbuilds/linux-omarchy-bore/0313-fuse-background-wakeup.patch +++ b/pkgbuilds/linux-omarchy-bore/0313-fuse-background-wakeup.patch @@ -1,7 +1,7 @@ diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c -@@ -409,7 +409,8 @@ void fuse_chan_max_background_set(struct fuse_chan *fch, unsigned int val) +@@ -406,7 +406,8 @@ void fuse_chan_max_background_set(struct fuse_chan *fch, unsigned int val) fch->max_background = val; fch->blocked = fch->num_background >= fch->max_background; if (!fch->blocked) diff --git a/pkgbuilds/linux-omarchy-bore/0313-fuse-background-wakeup.patch.sig b/pkgbuilds/linux-omarchy-bore/0313-fuse-background-wakeup.patch.sig index 8ec069b..a8e53e8 100644 Binary files a/pkgbuilds/linux-omarchy-bore/0313-fuse-background-wakeup.patch.sig and b/pkgbuilds/linux-omarchy-bore/0313-fuse-background-wakeup.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0320-vfs-open-consume-path-reference.patch b/pkgbuilds/linux-omarchy-bore/0320-vfs-open-consume-path-reference.patch new file mode 100644 index 0000000..eba1599 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0320-vfs-open-consume-path-reference.patch @@ -0,0 +1,119 @@ +diff --git a/fs/internal.h b/fs/internal.h +--- a/fs/internal.h ++++ b/fs/internal.h +@@ -204,6 +204,7 @@ int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, + int flag); + int chown_common(const struct path *path, uid_t user, gid_t group); + extern int vfs_open(const struct path *, struct file *); ++int vfs_open_consume(struct path *, struct file *); + + /* + * inode.c +diff --git a/fs/namei.c b/fs/namei.c +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -4652,6 +4652,7 @@ static const char *open_last_lookups(struct nameidata *nd, + static int do_open(struct nameidata *nd, + struct file *file, const struct open_flags *op) + { ++ struct vfsmount *mnt; + struct mnt_idmap *idmap; + int open_flag = op->open_flag; + bool do_truncate; +@@ -4693,11 +4694,17 @@ static int do_open(struct nameidata *nd, + error = mnt_want_write(nd->path.mnt); + if (error) + return error; ++ /* ++ * A dedicated reference is needed because after the call to ++ * vfs_open_consume() we no longer own the reference in nd->path.mnt ++ * while we need to undo write acess below. ++ */ ++ mnt = mntget(nd->path.mnt); + do_truncate = true; + } + error = may_open(idmap, &nd->path, acc_mode, open_flag); + if (!error && !(file->f_mode & FMODE_OPENED)) +- error = vfs_open(&nd->path, file); ++ error = vfs_open_consume(&nd->path, file); + if (!error) + error = security_file_post_open(file, op->acc_mode); + if (!error && do_truncate) +@@ -4706,8 +4713,10 @@ static int do_open(struct nameidata *nd, + WARN_ON(1); + error = -EINVAL; + } +- if (do_truncate) +- mnt_drop_write(nd->path.mnt); ++ if (do_truncate) { ++ mnt_drop_write(mnt); ++ mntput(mnt); ++ } + return error; + } + +diff --git a/fs/open.c b/fs/open.c +--- a/fs/open.c ++++ b/fs/open.c +@@ -882,6 +882,11 @@ static inline int file_get_write_access(struct file *f) + return error; + } + ++/* ++ * Populate struct file ++ * ++ * NOTE: it assumes f_path is populated and consumes the caller's reference. ++ */ + static int do_dentry_open(struct file *f, + int (*open)(struct inode *, struct file *)) + { +@@ -889,7 +894,6 @@ static int do_dentry_open(struct file *f, + struct inode *inode = f->f_path.dentry->d_inode; + int error; + +- path_get(&f->f_path); + f->f_inode = inode; + f->f_mapping = inode->i_mapping; + f->f_wb_err = filemap_sample_wb_err(f->f_mapping); +@@ -1006,6 +1010,7 @@ int finish_open(struct file *file, struct dentry *dentry, + BUG_ON(file->f_mode & FMODE_OPENED); /* once it's opened, it's opened */ + + file->__f_path.dentry = dentry; ++ path_get(&file->f_path); + return do_dentry_open(file, open); + } + EXPORT_SYMBOL(finish_open); +@@ -1049,6 +1054,7 @@ int vfs_open(const struct path *path, struct file *file) + int ret; + + file->__f_path = *path; ++ path_get(&file->f_path); + ret = do_dentry_open(file, NULL); + if (!ret) { + /* +@@ -1061,6 +1067,25 @@ int vfs_open(const struct path *path, struct file *file) + return ret; + } + ++/** ++ * vfs_open_consume - open the file at the given path and consume the reference ++ * @path: path to open ++ * @file: newly allocated file with f_flag initialized ++ */ ++int vfs_open_consume(struct path *path, struct file *file) ++{ ++ int ret; ++ ++ file->__f_path = *path; ++ path->mnt = NULL; ++ path->dentry = NULL; ++ ret = do_dentry_open(file, NULL); ++ if (!ret) { ++ fsnotify_open(file); ++ } ++ return ret; ++} ++ + struct file *dentry_open(const struct path *path, int flags, + const struct cred *cred) + { diff --git a/pkgbuilds/linux-omarchy-bore/0320-vfs-open-consume-path-reference.patch.sig b/pkgbuilds/linux-omarchy-bore/0320-vfs-open-consume-path-reference.patch.sig new file mode 100644 index 0000000..74dd333 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0320-vfs-open-consume-path-reference.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch b/pkgbuilds/linux-omarchy-bore/0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch deleted file mode 100644 index 6977a84..0000000 --- a/pkgbuilds/linux-omarchy-bore/0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c ---- a/drivers/gpu/drm/ttm/ttm_bo.c -+++ b/drivers/gpu/drm/ttm/ttm_bo.c -@@ -1343,7 +1343,7 @@ ttm_bo_swapout_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo) - - if (ttm_tt_is_populated(tt)) { - ret = ttm_tt_swapout(bdev, tt, swapout_walk->gfp_flags); -- if (!ret) { -+ if (ret > 0) { - spin_lock(&bdev->lru_lock); - ttm_resource_del_bulk_move_unevictable(bo->resource, bo); - ttm_resource_move_to_lru_tail(bo->resource); diff --git a/pkgbuilds/linux-omarchy-bore/0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch.sig b/pkgbuilds/linux-omarchy-bore/0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch.sig deleted file mode 100644 index bd99af4..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/0401-drm-i915-psr-exit-panel-replay-for-alpm-lag.patch.sig b/pkgbuilds/linux-omarchy-bore/0401-drm-i915-psr-exit-panel-replay-for-alpm-lag.patch.sig deleted file mode 100644 index 6e91496..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/0401-drm-i915-psr-exit-panel-replay-for-alpm-lag.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/0402-psr2-early-transport-panels.patch b/pkgbuilds/linux-omarchy-bore/0401-psr2-early-transport-panels.patch similarity index 100% rename from pkgbuilds/linux-omarchy-bore/0402-psr2-early-transport-panels.patch rename to pkgbuilds/linux-omarchy-bore/0401-psr2-early-transport-panels.patch diff --git a/pkgbuilds/linux-omarchy-bore/0402-psr2-early-transport-panels.patch.sig b/pkgbuilds/linux-omarchy-bore/0401-psr2-early-transport-panels.patch.sig similarity index 100% rename from pkgbuilds/linux-omarchy-bore/0402-psr2-early-transport-panels.patch.sig rename to pkgbuilds/linux-omarchy-bore/0401-psr2-early-transport-panels.patch.sig diff --git a/pkgbuilds/linux-omarchy/0401-drm-i915-psr-exit-panel-replay-for-alpm-lag.patch b/pkgbuilds/linux-omarchy-bore/0402-drm-i915-quirk-panel-replay-exit-on-flush.patch similarity index 64% rename from pkgbuilds/linux-omarchy/0401-drm-i915-psr-exit-panel-replay-for-alpm-lag.patch rename to pkgbuilds/linux-omarchy-bore/0402-drm-i915-quirk-panel-replay-exit-on-flush.patch index 28a6330..27e9b7d 100644 --- a/pkgbuilds/linux-omarchy/0401-drm-i915-psr-exit-panel-replay-for-alpm-lag.patch +++ b/pkgbuilds/linux-omarchy-bore/0402-drm-i915-quirk-panel-replay-exit-on-flush.patch @@ -1,5 +1,4 @@ diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h -index c21e0c0ef0b1..e62788328bfd 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1780,6 +1780,7 @@ struct intel_psr { @@ -11,10 +10,9 @@ index c21e0c0ef0b1..e62788328bfd 100644 u8 io_wake_lines; diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c -index beaa1d62613d..644ab9b1bee9 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c -@@ -2475,6 +2475,7 @@ void intel_psr_disable(struct intel_dp *intel_dp, +@@ -2481,6 +2481,7 @@ void intel_psr_disable(struct intel_dp *intel_dp, mutex_unlock(&intel_dp->psr.lock); cancel_work_sync(&intel_dp->psr.work); cancel_delayed_work_sync(&intel_dp->psr.dc3co_work); @@ -22,7 +20,7 @@ index beaa1d62613d..644ab9b1bee9 100644 } /** -@@ -2506,6 +2507,7 @@ void intel_psr_pause(struct intel_dp *intel_dp) +@@ -2512,6 +2513,7 @@ void intel_psr_pause(struct intel_dp *intel_dp) cancel_work_sync(&psr->work); cancel_delayed_work_sync(&psr->dc3co_work); @@ -30,48 +28,48 @@ index beaa1d62613d..644ab9b1bee9 100644 } /** -@@ -3539,6 +3541,17 @@ static void intel_psr_handle_irq(struct intel_dp *intel_dp) +@@ -3545,6 +3547,17 @@ static void intel_psr_handle_irq(struct intel_dp *intel_dp) drm_dp_dpcd_writeb(&intel_dp->aux, DP_SET_POWER, DP_SET_POWER_D0); } +#define PANEL_REPLAY_REENABLE_DELAY_MS 50 + -+static bool panel_replay_alpm_cursor_lag_workaround_enabled(struct intel_dp *intel_dp) ++static bool panel_replay_exit_on_flush_enabled(struct intel_dp *intel_dp) +{ + return intel_dp_is_edp(intel_dp) && + intel_dp->psr.panel_replay_enabled && + intel_dp->psr.sel_update_enabled && + intel_has_dpcd_quirk(intel_dp, -+ QUIRK_PANEL_REPLAY_ALPM_CURSOR_LAG); ++ QUIRK_PANEL_REPLAY_EXIT_ON_FLUSH); +} + static void intel_psr_work(struct work_struct *work) { struct intel_dp *intel_dp = -@@ -3557,6 +3570,11 @@ static void intel_psr_work(struct work_struct *work) +@@ -3563,6 +3576,11 @@ static void intel_psr_work(struct work_struct *work) if (intel_dp->psr.pause_counter) goto unlock; -+ /* The dedicated delayed work owns re-entry while the workaround is armed. */ -+ if (panel_replay_alpm_cursor_lag_workaround_enabled(intel_dp) && ++ /* The dedicated delayed work owns re-entry while the quirk is armed. */ ++ if (panel_replay_exit_on_flush_enabled(intel_dp) && + delayed_work_pending(&intel_dp->psr.panel_replay_reenable_work)) + goto unlock; + /* * We have to make sure PSR is ready for re-enable * otherwise it keeps disabled until next full enable/disable cycle. -@@ -3566,6 +3584,10 @@ static void intel_psr_work(struct work_struct *work) +@@ -3572,6 +3590,10 @@ static void intel_psr_work(struct work_struct *work) if (!__psr_wait_for_idle_locked(intel_dp)) goto unlock; -+ if (panel_replay_alpm_cursor_lag_workaround_enabled(intel_dp) && ++ if (panel_replay_exit_on_flush_enabled(intel_dp) && + delayed_work_pending(&intel_dp->psr.panel_replay_reenable_work)) + goto unlock; + /* * The delayed work can race with an invalidate hence we need to * recheck. Since psr_flush first clears this and then reschedules we -@@ -3579,6 +3601,33 @@ static void intel_psr_work(struct work_struct *work) +@@ -3585,6 +3607,33 @@ static void intel_psr_work(struct work_struct *work) mutex_unlock(&intel_dp->psr.lock); } @@ -84,7 +82,7 @@ index beaa1d62613d..644ab9b1bee9 100644 + mutex_lock(&intel_dp->psr.lock); + + if (!intel_dp->psr.enabled || -+ !panel_replay_alpm_cursor_lag_workaround_enabled(intel_dp) || ++ !panel_replay_exit_on_flush_enabled(intel_dp) || + intel_dp->psr.pause_counter || + READ_ONCE(intel_dp->psr.irq_aux_error)) + goto unlock; @@ -105,25 +103,25 @@ index beaa1d62613d..644ab9b1bee9 100644 static void intel_psr_configure_full_frame_update(struct intel_dp *intel_dp) { struct intel_display *display = to_intel_display(intel_dp); -@@ -3650,8 +3699,11 @@ void intel_psr_invalidate(struct intel_display *display, +@@ -3656,8 +3705,11 @@ void intel_psr_invalidate(struct intel_display *display, INTEL_FRONTBUFFER_ALL_MASK(intel_dp->psr.pipe); intel_dp->psr.busy_frontbuffer_bits |= pipe_frontbuffer_bits; - if (pipe_frontbuffer_bits) + if (pipe_frontbuffer_bits) { -+ if (panel_replay_alpm_cursor_lag_workaround_enabled(intel_dp)) ++ if (panel_replay_exit_on_flush_enabled(intel_dp)) + cancel_delayed_work(&intel_dp->psr.panel_replay_reenable_work); _psr_invalidate_handle(intel_dp); + } mutex_unlock(&intel_dp->psr.lock); } -@@ -3767,6 +3819,15 @@ void intel_psr_flush(struct intel_display *display, +@@ -3773,6 +3825,15 @@ void intel_psr_flush(struct intel_display *display, if (intel_dp->psr.pause_counter) goto unlock; + if (pipe_frontbuffer_bits && -+ panel_replay_alpm_cursor_lag_workaround_enabled(intel_dp)) { ++ panel_replay_exit_on_flush_enabled(intel_dp)) { + intel_psr_exit(intel_dp); + mod_delayed_work(display->wq.unordered, + &intel_dp->psr.panel_replay_reenable_work, @@ -134,7 +132,7 @@ index beaa1d62613d..644ab9b1bee9 100644 if (origin == ORIGIN_FLIP || (origin == ORIGIN_CURSOR_UPDATE && !intel_dp->psr.psr2_sel_fetch_enabled)) { -@@ -3830,6 +3891,8 @@ void intel_psr_init(struct intel_dp *intel_dp) +@@ -3836,6 +3897,8 @@ void intel_psr_init(struct intel_dp *intel_dp) INIT_WORK(&intel_dp->psr.work, intel_psr_work); INIT_DELAYED_WORK(&intel_dp->psr.dc3co_work, tgl_dc3co_disable_work); @@ -144,53 +142,31 @@ index beaa1d62613d..644ab9b1bee9 100644 } diff --git a/drivers/gpu/drm/i915/display/intel_quirks.c b/drivers/gpu/drm/i915/display/intel_quirks.c -index 33245f44c0d5..dc6576016400 100644 --- a/drivers/gpu/drm/i915/display/intel_quirks.c +++ b/drivers/gpu/drm/i915/display/intel_quirks.c -@@ -86,12 +86,13 @@ static void quirk_edp_limit_rate_hbr2(struct intel_display *display) - drm_info(display->drm, "Applying eDP Limit rate to HBR2 quirk\n"); - } - --static void quirk_disable_edp_panel_replay(struct intel_dp *intel_dp) -+static void quirk_panel_replay_alpm_cursor_lag(struct intel_dp *intel_dp) - { - struct intel_display *display = to_intel_display(intel_dp); - -- intel_set_dpcd_quirk(intel_dp, QUIRK_DISABLE_EDP_PANEL_REPLAY); -- drm_info(display->drm, "Applying disable Panel Replay quirk\n"); -+ intel_set_dpcd_quirk(intel_dp, QUIRK_PANEL_REPLAY_ALPM_CURSOR_LAG); -+ drm_info(display->drm, -+ "Applying Panel Replay ALPM cursor lag workaround\n"); +@@ -94,6 +94,14 @@ static void quirk_disable_edp_panel_replay(struct intel_dp *intel_dp) + drm_info(display->drm, "Applying disable Panel Replay quirk\n"); } ++static void quirk_panel_replay_exit_on_flush(struct intel_dp *intel_dp) ++{ ++ struct intel_display *display = to_intel_display(intel_dp); ++ ++ intel_set_dpcd_quirk(intel_dp, QUIRK_PANEL_REPLAY_EXIT_ON_FLUSH); ++ drm_info(display->drm, "Applying Panel Replay exit on flush quirk\n"); ++} ++ static void quirk_disable_psr2(struct intel_display *display) -@@ -276,7 +277,7 @@ static const struct intel_dpcd_quirk intel_dpcd_quirks[] = { - .subsystem_vendor = 0x1028, - .subsystem_device = 0x0db9, - .sink_oui = SINK_OUI(0x00, 0x22, 0xb9), -- .hook = quirk_disable_edp_panel_replay, -+ .hook = quirk_panel_replay_alpm_cursor_lag, - }, - /* Dell XPS 16 DA16260 */ - { -@@ -284,7 +285,7 @@ static const struct intel_dpcd_quirk intel_dpcd_quirks[] = { - .subsystem_vendor = 0x1028, - .subsystem_device = 0x0dba, - .sink_oui = SINK_OUI(0x00, 0x22, 0xb9), -- .hook = quirk_disable_edp_panel_replay, -+ .hook = quirk_panel_replay_alpm_cursor_lag, - }, - }; - + { + intel_set_quirk(display, QUIRK_DISABLE_PSR2); diff --git a/drivers/gpu/drm/i915/display/intel_quirks.h b/drivers/gpu/drm/i915/display/intel_quirks.h -index 970a4fe52faf..50ef333b87a9 100644 --- a/drivers/gpu/drm/i915/display/intel_quirks.h +++ b/drivers/gpu/drm/i915/display/intel_quirks.h @@ -23,6 +23,7 @@ enum intel_quirk_id { QUIRK_EDP_LIMIT_RATE_HBR2, QUIRK_DISABLE_EDP_PANEL_REPLAY, QUIRK_DISABLE_PSR2, -+ QUIRK_PANEL_REPLAY_ALPM_CURSOR_LAG, ++ QUIRK_PANEL_REPLAY_EXIT_ON_FLUSH, }; void intel_init_quirks(struct intel_display *display); diff --git a/pkgbuilds/linux-omarchy-bore/0402-drm-i915-quirk-panel-replay-exit-on-flush.patch.sig b/pkgbuilds/linux-omarchy-bore/0402-drm-i915-quirk-panel-replay-exit-on-flush.patch.sig new file mode 100644 index 0000000..cb36779 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0402-drm-i915-quirk-panel-replay-exit-on-flush.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0403-drm-i915-quirk-dell-xps14-ptl-panel-replay-exit-on-flush.patch b/pkgbuilds/linux-omarchy-bore/0403-drm-i915-quirk-dell-xps14-ptl-panel-replay-exit-on-flush.patch new file mode 100644 index 0000000..24c203e --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0403-drm-i915-quirk-dell-xps14-ptl-panel-replay-exit-on-flush.patch @@ -0,0 +1,12 @@ +diff --git a/drivers/gpu/drm/i915/display/intel_quirks.c b/drivers/gpu/drm/i915/display/intel_quirks.c +--- a/drivers/gpu/drm/i915/display/intel_quirks.c ++++ b/drivers/gpu/drm/i915/display/intel_quirks.c +@@ -284,7 +284,7 @@ static const struct intel_dpcd_quirk intel_dpcd_quirks[] = { + .subsystem_vendor = 0x1028, + .subsystem_device = 0x0db9, + .sink_oui = SINK_OUI(0x00, 0x22, 0xb9), +- .hook = quirk_disable_edp_panel_replay, ++ .hook = quirk_panel_replay_exit_on_flush, + }, + /* Dell XPS 16 DA16260 */ + { diff --git a/pkgbuilds/linux-omarchy-bore/0403-drm-i915-quirk-dell-xps14-ptl-panel-replay-exit-on-flush.patch.sig b/pkgbuilds/linux-omarchy-bore/0403-drm-i915-quirk-dell-xps14-ptl-panel-replay-exit-on-flush.patch.sig new file mode 100644 index 0000000..a828b25 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0403-drm-i915-quirk-dell-xps14-ptl-panel-replay-exit-on-flush.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0404-drm-i915-quirk-dell-xps16-ptl-panel-replay-exit-on-flush.patch b/pkgbuilds/linux-omarchy-bore/0404-drm-i915-quirk-dell-xps16-ptl-panel-replay-exit-on-flush.patch new file mode 100644 index 0000000..8eb9b65 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0404-drm-i915-quirk-dell-xps16-ptl-panel-replay-exit-on-flush.patch @@ -0,0 +1,12 @@ +diff --git a/drivers/gpu/drm/i915/display/intel_quirks.c b/drivers/gpu/drm/i915/display/intel_quirks.c +--- a/drivers/gpu/drm/i915/display/intel_quirks.c ++++ b/drivers/gpu/drm/i915/display/intel_quirks.c +@@ -292,7 +292,7 @@ static const struct intel_dpcd_quirk intel_dpcd_quirks[] = { + .subsystem_vendor = 0x1028, + .subsystem_device = 0x0dba, + .sink_oui = SINK_OUI(0x00, 0x22, 0xb9), +- .hook = quirk_disable_edp_panel_replay, ++ .hook = quirk_panel_replay_exit_on_flush, + }, + }; + diff --git a/pkgbuilds/linux-omarchy-bore/0404-drm-i915-quirk-dell-xps16-ptl-panel-replay-exit-on-flush.patch.sig b/pkgbuilds/linux-omarchy-bore/0404-drm-i915-quirk-dell-xps16-ptl-panel-replay-exit-on-flush.patch.sig new file mode 100644 index 0000000..0894c4b Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0404-drm-i915-quirk-dell-xps16-ptl-panel-replay-exit-on-flush.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0405-drm-i915-quirk-dell-xps13-dx13260-ptl-psr1-only.patch b/pkgbuilds/linux-omarchy-bore/0405-drm-i915-quirk-dell-xps13-dx13260-ptl-psr1-only.patch new file mode 100644 index 0000000..1da7f71 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0405-drm-i915-quirk-dell-xps13-dx13260-ptl-psr1-only.patch @@ -0,0 +1,28 @@ +diff --git a/drivers/gpu/drm/i915/display/intel_quirks.c b/drivers/gpu/drm/i915/display/intel_quirks.c +--- a/drivers/gpu/drm/i915/display/intel_quirks.c ++++ b/drivers/gpu/drm/i915/display/intel_quirks.c +@@ -267,6 +267,9 @@ static struct intel_quirk intel_quirks[] = { + + /* Xiaomi Book Pro 14 2026 */ + { 0xb081, 0x1d72, 0x2424, quirk_disable_psr2 }, ++ ++ /* Dell XPS 13 DX13260 */ ++ { DEVICE_ID_ANY, 0x1028, 0x0e54, quirk_disable_psr2 }, + }; + + static const struct intel_dpcd_quirk intel_dpcd_quirks[] = { +@@ -294,6 +297,14 @@ static const struct intel_dpcd_quirk intel_dpcd_quirks[] = { + .sink_oui = SINK_OUI(0x00, 0x22, 0xb9), + .hook = quirk_panel_replay_exit_on_flush, + }, ++ /* Dell XPS 13 DX13260 */ ++ { ++ .device = DEVICE_ID_ANY, ++ .subsystem_vendor = 0x1028, ++ .subsystem_device = 0x0e54, ++ .sink_oui = SINK_OUI(0x00, 0x22, 0xb9), ++ .hook = quirk_disable_edp_panel_replay, ++ }, + }; + + void intel_init_quirks(struct intel_display *display) diff --git a/pkgbuilds/linux-omarchy-bore/0405-drm-i915-quirk-dell-xps13-dx13260-ptl-psr1-only.patch.sig b/pkgbuilds/linux-omarchy-bore/0405-drm-i915-quirk-dell-xps13-dx13260-ptl-psr1-only.patch.sig new file mode 100644 index 0000000..833b6bd Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0405-drm-i915-quirk-dell-xps13-dx13260-ptl-psr1-only.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0406-drm-i915-quirk-dell-xps13-dx13260-wcl-panel-replay-exit-on-flush.patch b/pkgbuilds/linux-omarchy-bore/0406-drm-i915-quirk-dell-xps13-dx13260-wcl-panel-replay-exit-on-flush.patch new file mode 100644 index 0000000..94d0d55 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0406-drm-i915-quirk-dell-xps13-dx13260-wcl-panel-replay-exit-on-flush.patch @@ -0,0 +1,19 @@ +diff --git a/drivers/gpu/drm/i915/display/intel_quirks.c b/drivers/gpu/drm/i915/display/intel_quirks.c +--- a/drivers/gpu/drm/i915/display/intel_quirks.c ++++ b/drivers/gpu/drm/i915/display/intel_quirks.c +@@ -305,6 +305,15 @@ static const struct intel_dpcd_quirk intel_dpcd_quirks[] = { + .sink_oui = SINK_OUI(0x00, 0x22, 0xb9), + .hook = quirk_disable_edp_panel_replay, + }, ++ /* Dell XPS 13 DX13260 (Wildcat Lake), sink "Bamboo" */ ++ { ++ .device = DEVICE_ID_ANY, ++ .subsystem_vendor = 0x1028, ++ .subsystem_device = 0x0e53, ++ .sink_oui = SINK_OUI(0x00, 0x22, 0xb9), ++ .sink_device_id = SINK_DEVICE_ID(0x42, 0x61, 0x6d, 0x62, 0x6f, 0x6f), ++ .hook = quirk_panel_replay_exit_on_flush, ++ }, + }; + + void intel_init_quirks(struct intel_display *display) diff --git a/pkgbuilds/linux-omarchy-bore/0406-drm-i915-quirk-dell-xps13-dx13260-wcl-panel-replay-exit-on-flush.patch.sig b/pkgbuilds/linux-omarchy-bore/0406-drm-i915-quirk-dell-xps13-dx13260-wcl-panel-replay-exit-on-flush.patch.sig new file mode 100644 index 0000000..086a867 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0406-drm-i915-quirk-dell-xps13-dx13260-wcl-panel-replay-exit-on-flush.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0430-fbc.patch b/pkgbuilds/linux-omarchy-bore/0430-fbc.patch index 4548866..b64db93 100644 --- a/pkgbuilds/linux-omarchy-bore/0430-fbc.patch +++ b/pkgbuilds/linux-omarchy-bore/0430-fbc.patch @@ -55,7 +55,7 @@ diff --git a/drivers/gpu/drm/i915/display/intel_fbc.c b/drivers/gpu/drm/i915/dis diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c --- a/drivers/gpu/drm/i915/display/skl_universal_plane.c +++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c -@@ -1937,10 +1937,10 @@ static int intel_plane_min_height(struct intel_plane *plane, +@@ -1932,10 +1932,10 @@ static int intel_plane_min_height(struct intel_plane *plane, return 1; } @@ -70,7 +70,7 @@ diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/dr { if (plane->max_width) return plane->max_width(fb, color_plane, rotation); -@@ -1948,10 +1948,10 @@ static int intel_plane_max_width(struct intel_plane *plane, +@@ -1943,10 +1943,10 @@ static int intel_plane_max_width(struct intel_plane *plane, return INT_MAX; } diff --git a/pkgbuilds/linux-omarchy-bore/0430-fbc.patch.sig b/pkgbuilds/linux-omarchy-bore/0430-fbc.patch.sig index f0db1f3..7c6d7b4 100644 Binary files a/pkgbuilds/linux-omarchy-bore/0430-fbc.patch.sig and b/pkgbuilds/linux-omarchy-bore/0430-fbc.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0442-drm-i915-psr-clear-stale-sel-fetch-bits.patch b/pkgbuilds/linux-omarchy-bore/0442-drm-i915-psr-clear-stale-sel-fetch-bits.patch new file mode 100644 index 0000000..e2e1fac --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0442-drm-i915-psr-clear-stale-sel-fetch-bits.patch @@ -0,0 +1,95 @@ +diff --git a/drivers/gpu/drm/i915/display/intel_cursor.c b/drivers/gpu/drm/i915/display/intel_cursor.c +--- a/drivers/gpu/drm/i915/display/intel_cursor.c ++++ b/drivers/gpu/drm/i915/display/intel_cursor.c +@@ -536,7 +536,8 @@ static void i9xx_cursor_disable_sel_fetch_arm(struct intel_dsb *dsb, + struct intel_display *display = to_intel_display(plane); + enum pipe pipe = plane->pipe; + +- if (!crtc_state->enable_psr2_sel_fetch) ++ if (!crtc_state->enable_psr2_sel_fetch && ++ !crtc_state->clear_psr2_sel_fetch) + return; + + intel_de_write_dsb(display, dsb, SEL_FETCH_CUR_CTL(pipe), 0); +@@ -569,8 +570,10 @@ static void i9xx_cursor_update_sel_fetch_arm(struct intel_dsb *dsb, + struct intel_display *display = to_intel_display(plane); + enum pipe pipe = plane->pipe; + +- if (!crtc_state->enable_psr2_sel_fetch) ++ if (!crtc_state->enable_psr2_sel_fetch) { ++ i9xx_cursor_disable_sel_fetch_arm(dsb, plane, crtc_state); + return; ++ } + + if (drm_rect_height(&plane_state->psr2_sel_fetch_area) > 0) { + if (crtc_state->enable_psr2_su_region_et) { +diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h +--- a/drivers/gpu/drm/i915/display/intel_display_types.h ++++ b/drivers/gpu/drm/i915/display/intel_display_types.h +@@ -1177,6 +1177,8 @@ struct intel_crtc_state { + bool has_sel_update; + bool enable_psr2_sel_fetch; + bool enable_psr2_su_region_et; ++ /* Drop the stale selective fetch enable bits as selective fetch is turned off */ ++ bool clear_psr2_sel_fetch; + bool req_psr2_sdp_prior_scanline; + bool has_panel_replay; + bool link_off_after_as_sdp_when_pr_active; +diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c +--- a/drivers/gpu/drm/i915/display/intel_psr.c ++++ b/drivers/gpu/drm/i915/display/intel_psr.c +@@ -2941,6 +2941,8 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state, + struct intel_crtc *crtc) + { + struct intel_display *display = to_intel_display(state); ++ const struct intel_crtc_state *old_crtc_state = ++ intel_atomic_get_old_crtc_state(state, crtc); + struct intel_crtc_state *crtc_state = intel_atomic_get_new_crtc_state(state, crtc); + struct intel_plane_state *new_plane_state, *old_plane_state; + struct intel_plane *plane; +@@ -2953,6 +2955,19 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state, + bool full_update = false, su_area_changed; + int i, ret; + ++ /* ++ * Selective fetch is not always usable, for instance it is dropped ++ * while pipe CRC is active. The planes keep their selective fetch ++ * enable bit set in hardware over that, and a plane disabled while ++ * selective fetch is off never gets the bit cleared. Once selective ++ * fetch comes back the hardware would resume fetching for a plane that ++ * is no longer enabled and keep its DDB range reserved, so have the ++ * plane update drop the bit for every plane of the pipe as selective ++ * fetch is turned off. ++ */ ++ crtc_state->clear_psr2_sel_fetch = old_crtc_state->enable_psr2_sel_fetch && ++ !crtc_state->enable_psr2_sel_fetch; ++ + if (!crtc_state->enable_psr2_sel_fetch) + return 0; + +diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c +--- a/drivers/gpu/drm/i915/display/skl_universal_plane.c ++++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c +@@ -885,7 +885,8 @@ static void icl_plane_disable_sel_fetch_arm(struct intel_dsb *dsb, + struct intel_display *display = to_intel_display(plane); + enum pipe pipe = plane->pipe; + +- if (!crtc_state->enable_psr2_sel_fetch) ++ if (!crtc_state->enable_psr2_sel_fetch && ++ !crtc_state->clear_psr2_sel_fetch) + return; + + intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_CTL(pipe, plane->id), 0); +@@ -1634,10 +1635,8 @@ static void icl_plane_update_sel_fetch_arm(struct intel_dsb *dsb, + struct intel_display *display = to_intel_display(plane); + enum pipe pipe = plane->pipe; + +- if (!crtc_state->enable_psr2_sel_fetch) +- return; +- +- if (drm_rect_height(&plane_state->psr2_sel_fetch_area) > 0) ++ if (crtc_state->enable_psr2_sel_fetch && ++ drm_rect_height(&plane_state->psr2_sel_fetch_area) > 0) + intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_CTL(pipe, plane->id), + SEL_FETCH_PLANE_CTL_ENABLE); + else diff --git a/pkgbuilds/linux-omarchy-bore/0442-drm-i915-psr-clear-stale-sel-fetch-bits.patch.sig b/pkgbuilds/linux-omarchy-bore/0442-drm-i915-psr-clear-stale-sel-fetch-bits.patch.sig new file mode 100644 index 0000000..2a891ba Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0442-drm-i915-psr-clear-stale-sel-fetch-bits.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0443-drm-i915-dsb-delayed-vblank-scanline-shift.patch b/pkgbuilds/linux-omarchy-bore/0443-drm-i915-dsb-delayed-vblank-scanline-shift.patch new file mode 100644 index 0000000..820d377 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0443-drm-i915-dsb-delayed-vblank-scanline-shift.patch @@ -0,0 +1,22 @@ +diff --git a/drivers/gpu/drm/i915/display/intel_dsb.c b/drivers/gpu/drm/i915/display/intel_dsb.c +--- a/drivers/gpu/drm/i915/display/intel_dsb.c ++++ b/drivers/gpu/drm/i915/display/intel_dsb.c +@@ -902,9 +902,17 @@ void intel_dsb_wait_for_delayed_vblank(struct intel_atomic_state *state, + * the hardware itself guarantees that we're SCL lines + * away from the delayed vblank, and we won't be inside + * the vmin safe window so this extra wait does nothing. ++ * ++ * Experimentally, DSB may observe a slightly stale ++ * PIPEDSL value. When the actual scanline has just reached ++ * safe_window_start, WAIT_DSL_OUT may complete immediately ++ * due to the stale value. ++ * ++ * Shift the start back by one scanline to ensure the wait ++ * window is entered reliably. + */ + intel_dsb_wait_scanline_out(state, dsb, +- intel_vrr_safe_window_start(crtc_state), ++ intel_vrr_safe_window_start(crtc_state) - 1, + intel_vrr_vmin_safe_window_end(crtc_state)); + /* + * When the push is sent during vblank it will trigger diff --git a/pkgbuilds/linux-omarchy-bore/0443-drm-i915-dsb-delayed-vblank-scanline-shift.patch.sig b/pkgbuilds/linux-omarchy-bore/0443-drm-i915-dsb-delayed-vblank-scanline-shift.patch.sig new file mode 100644 index 0000000..f11347d Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0443-drm-i915-dsb-delayed-vblank-scanline-shift.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0444-drm-i915-vrr-dc-balance-off-by-default.patch b/pkgbuilds/linux-omarchy-bore/0444-drm-i915-vrr-dc-balance-off-by-default.patch new file mode 100644 index 0000000..9efda11 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0444-drm-i915-vrr-dc-balance-off-by-default.patch @@ -0,0 +1,42 @@ +diff --git a/drivers/gpu/drm/i915/display/intel_display_params.c b/drivers/gpu/drm/i915/display/intel_display_params.c +--- a/drivers/gpu/drm/i915/display/intel_display_params.c ++++ b/drivers/gpu/drm/i915/display/intel_display_params.c +@@ -120,6 +120,10 @@ intel_display_param_named_unsafe(enable_psr, int, 0400, + "(0=disabled, 1=enable up to PSR1, 2=enable up to PSR2) " + "Default: -1 (use per-chip default)"); + ++intel_display_param_named_unsafe(enable_dc_balance, bool, 0400, ++ "Enable VRR DC balance (0=disabled, 1=enabled). " ++ "Default: 0 (disabled)"); ++ + intel_display_param_named_unsafe(enable_panel_replay, int, 0400, + "Enable Panel Replay (0=disabled, 1=enabled). Default: -1 (use per-chip default)"); + +diff --git a/drivers/gpu/drm/i915/display/intel_display_params.h b/drivers/gpu/drm/i915/display/intel_display_params.h +--- a/drivers/gpu/drm/i915/display/intel_display_params.h ++++ b/drivers/gpu/drm/i915/display/intel_display_params.h +@@ -46,6 +46,7 @@ struct drm_printer; + param(bool, enable_dp_mst, true, 0600) \ + param(int, enable_fbc, -1, 0600) \ + param(int, enable_psr, -1, 0600) \ ++ param(bool, enable_dc_balance, false, 0600) \ + param(int, enable_panel_replay, -1, 0600) \ + param(bool, psr_safest_params, false, 0400) \ + param(bool, enable_psr2_sel_fetch, true, 0400) \ +diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c +--- a/drivers/gpu/drm/i915/display/intel_vrr.c ++++ b/drivers/gpu/drm/i915/display/intel_vrr.c +@@ -383,10 +383,12 @@ static bool intel_vrr_dc_balance_possible(const struct intel_crtc_state *crtc_st + static void + intel_vrr_dc_balance_compute_config(struct intel_crtc_state *crtc_state) + { ++ struct intel_display *display = to_intel_display(crtc_state); + int guardband_usec, adjustment_usec; + struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; + +- if (!intel_vrr_dc_balance_possible(crtc_state) || !crtc_state->vrr.enable) ++ if (!intel_vrr_dc_balance_possible(crtc_state) || ++ !crtc_state->vrr.enable || !display->params.enable_dc_balance) + return; + + crtc_state->vrr.dc_balance.vmax = crtc_state->vrr.vmax; diff --git a/pkgbuilds/linux-omarchy-bore/0444-drm-i915-vrr-dc-balance-off-by-default.patch.sig b/pkgbuilds/linux-omarchy-bore/0444-drm-i915-vrr-dc-balance-off-by-default.patch.sig new file mode 100644 index 0000000..497cbb1 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0444-drm-i915-vrr-dc-balance-off-by-default.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0450-amd-hdmi-vrr-allm.patch b/pkgbuilds/linux-omarchy-bore/0450-amd-hdmi-vrr-allm.patch index ebe9a36..02991bc 100644 --- a/pkgbuilds/linux-omarchy-bore/0450-amd-hdmi-vrr-allm.patch +++ b/pkgbuilds/linux-omarchy-bore/0450-amd-hdmi-vrr-allm.patch @@ -9,7 +9,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm #include "modules/inc/mod_power.h" #include "modules/power/power_helpers.h" -@@ -10068,6 +10069,9 @@ static void update_freesync_state_on_stream( +@@ -10088,6 +10089,9 @@ static void update_freesync_state_on_stream( &vrr_infopacket, pack_sdp_v1_3); @@ -19,7 +19,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm new_crtc_state->freesync_vrr_info_changed |= (memcmp(&new_crtc_state->vrr_infopacket, &vrr_infopacket, -@@ -10079,6 +10083,36 @@ static void update_freesync_state_on_stream( +@@ -10099,6 +10103,36 @@ static void update_freesync_state_on_stream( new_stream->vrr_infopacket = vrr_infopacket; new_stream->allow_freesync = mod_freesync_get_freesync_enabled(&vrr_params); @@ -56,7 +56,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm if (new_crtc_state->freesync_vrr_info_changed) drm_dbg_kms(adev_to_drm(adev), "VRR packet update: crtc=%u enabled=%d state=%d", new_crtc_state->base.crtc->base.id, -@@ -10645,9 +10679,12 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, +@@ -10665,9 +10699,12 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, } if (acrtc_state->stream) { @@ -70,7 +70,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm } } -@@ -12049,6 +12086,14 @@ static void get_freesync_config_for_crtc( +@@ -12069,6 +12106,14 @@ static void get_freesync_config_for_crtc( } out: new_crtc_state->freesync_config = config; @@ -85,7 +85,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm } static void reset_freesync_config_for_crtc( -@@ -14011,6 +14056,15 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, +@@ -14031,6 +14076,15 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, if (!adev->dm.freesync_module || !dc_supports_vrr(sink->ctx->dce_version)) goto update; @@ -101,7 +101,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm edid = drm_edid_raw(drm_edid); // FIXME: Get rid of drm_edid_raw() /* Some eDP panels only have the refresh rate range info in DisplayID */ -@@ -14036,7 +14090,9 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, +@@ -14056,7 +14110,9 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, amdgpu_dm_connector->as_type = ADAPTIVE_SYNC_TYPE_EDP; } @@ -112,7 +112,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info); if (i >= 0) { amdgpu_dm_connector->vsdb_info = vsdb_info; -@@ -14052,6 +14108,59 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, +@@ -14072,6 +14128,59 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz; } } @@ -172,7 +172,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm } if (amdgpu_dm_connector->dc_link) -@@ -14093,6 +14202,11 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, +@@ -14113,6 +14222,11 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, if (dm_con_state) dm_con_state->freesync_capable = freesync_capable; diff --git a/pkgbuilds/linux-omarchy-bore/0450-amd-hdmi-vrr-allm.patch.sig b/pkgbuilds/linux-omarchy-bore/0450-amd-hdmi-vrr-allm.patch.sig index ee35537..186828c 100644 Binary files a/pkgbuilds/linux-omarchy-bore/0450-amd-hdmi-vrr-allm.patch.sig and b/pkgbuilds/linux-omarchy-bore/0450-amd-hdmi-vrr-allm.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0451-amd-vtem-tmds-links.patch b/pkgbuilds/linux-omarchy-bore/0451-amd-vtem-tmds-links.patch index f61c400..5bc46ad 100644 --- a/pkgbuilds/linux-omarchy-bore/0451-amd-vtem-tmds-links.patch +++ b/pkgbuilds/linux-omarchy-bore/0451-amd-vtem-tmds-links.patch @@ -1,7 +1,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c -@@ -10069,7 +10069,10 @@ static void update_freesync_state_on_stream( +@@ -10089,7 +10089,10 @@ static void update_freesync_state_on_stream( &vrr_infopacket, pack_sdp_v1_3); diff --git a/pkgbuilds/linux-omarchy-bore/0451-amd-vtem-tmds-links.patch.sig b/pkgbuilds/linux-omarchy-bore/0451-amd-vtem-tmds-links.patch.sig index 7077a0e..ce4d4da 100644 Binary files a/pkgbuilds/linux-omarchy-bore/0451-amd-vtem-tmds-links.patch.sig and b/pkgbuilds/linux-omarchy-bore/0451-amd-vtem-tmds-links.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0453-amd-vtem-null-sink.patch b/pkgbuilds/linux-omarchy-bore/0453-amd-vtem-null-sink.patch new file mode 100644 index 0000000..0a3c3d3 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0453-amd-vtem-null-sink.patch @@ -0,0 +1,14 @@ +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +@@ -10090,8 +10090,8 @@ static void update_freesync_state_on_stream( + pack_sdp_v1_3); + + /* Per HDMI 2.1, VTEM is valid on TMDS as well as FRL */ +- if (new_stream->sink->sink_signal == SIGNAL_TYPE_HDMI_FRL || +- (new_stream->sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A && ++ if (new_stream->signal == SIGNAL_TYPE_HDMI_FRL || ++ (new_stream->signal == SIGNAL_TYPE_HDMI_TYPE_A && + aconn && aconn->base.display_info.hdmi.vrr_cap.supported)) + mod_build_infopacket_vtem(new_stream, &vrr_params, 0, &vrr_infopacket); + diff --git a/pkgbuilds/linux-omarchy-bore/0453-amd-vtem-null-sink.patch.sig b/pkgbuilds/linux-omarchy-bore/0453-amd-vtem-null-sink.patch.sig new file mode 100644 index 0000000..e8b34ac Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0453-amd-vtem-null-sink.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0454-amd-hdmi-frl-status-polling.patch b/pkgbuilds/linux-omarchy-bore/0454-amd-hdmi-frl-status-polling.patch new file mode 100644 index 0000000..62ada93 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0454-amd-hdmi-frl-status-polling.patch @@ -0,0 +1,12 @@ +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +@@ -2025,7 +2025,7 @@ static void hdmi_frl_status_polling_work(struct work_struct *work) + if (!dc_is_hdmi_signal(dc_link->connector_signal)) + continue; + +- if (dc_link->connector_signal != SIGNAL_TYPE_HDMI_FRL) ++ if (dc_link->frl_link_settings.frl_link_rate == 0) + continue; + + link_update = dc_link_frl_poll_status_flag(dc_link); diff --git a/pkgbuilds/linux-omarchy-bore/0454-amd-hdmi-frl-status-polling.patch.sig b/pkgbuilds/linux-omarchy-bore/0454-amd-hdmi-frl-status-polling.patch.sig new file mode 100644 index 0000000..0f7a5ed Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0454-amd-hdmi-frl-status-polling.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0455-amd-hdmi-frl-cap-on-nondestructive-verify.patch b/pkgbuilds/linux-omarchy-bore/0455-amd-hdmi-frl-cap-on-nondestructive-verify.patch new file mode 100644 index 0000000..26db142 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0455-amd-hdmi-frl-cap-on-nondestructive-verify.patch @@ -0,0 +1,12 @@ +diff --git a/drivers/gpu/drm/amd/display/dc/link/link_detection.c b/drivers/gpu/drm/amd/display/dc/link/link_detection.c +--- a/drivers/gpu/drm/amd/display/dc/link/link_detection.c ++++ b/drivers/gpu/drm/amd/display/dc/link/link_detection.c +@@ -882,7 +882,7 @@ static void verify_link_capability_non_destructive(struct dc_link *link) + else + link->verified_link_cap = dp_get_max_link_cap(link); + } else if (dc_is_hdmi_signal(link->local_sink->sink_signal)) { +- link->verified_link_cap = link->reported_link_cap; ++ link->frl_verified_link_cap = link->frl_reported_link_cap; + + if (is_hdmi_frl_in_use(link)) { + link->local_sink->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A; diff --git a/pkgbuilds/linux-omarchy-bore/0455-amd-hdmi-frl-cap-on-nondestructive-verify.patch.sig b/pkgbuilds/linux-omarchy-bore/0455-amd-hdmi-frl-cap-on-nondestructive-verify.patch.sig new file mode 100644 index 0000000..757e147 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0455-amd-hdmi-frl-cap-on-nondestructive-verify.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0456-amd-dml-frl-compressed-cap-check.patch b/pkgbuilds/linux-omarchy-bore/0456-amd-dml-frl-compressed-cap-check.patch new file mode 100644 index 0000000..4531604 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0456-amd-dml-frl-compressed-cap-check.patch @@ -0,0 +1,26 @@ +diff --git a/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c +--- a/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c ++++ b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c +@@ -630,17 +630,17 @@ enum frl_cap_chk_result dml1_frl_cap_chk(struct frl_cap_chk_params *params) + { + struct frl_cap_chk_intermediates inter; + +-#if defined (CONFIG_DRM_AMD_DC_FP) +- if (params->compressed) +- return dml1_frl_cap_chk_compressed(params, &inter); +-#endif +- + return dml1_frl_cap_chk_inter(params, &inter); + } + + enum frl_cap_chk_result dml1_frl_cap_chk_inter(struct frl_cap_chk_params *params, + struct frl_cap_chk_intermediates *inter) + { ++#if defined (CONFIG_DRM_AMD_DC_FP) ++ if (params->compressed) ++ return dml1_frl_cap_chk_compressed(params, inter); ++#endif ++ + return dml1_frl_cap_chk_uncompressed(params, inter); + } + diff --git a/pkgbuilds/linux-omarchy-bore/0456-amd-dml-frl-compressed-cap-check.patch.sig b/pkgbuilds/linux-omarchy-bore/0456-amd-dml-frl-compressed-cap-check.patch.sig new file mode 100644 index 0000000..3ebb7c2 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0456-amd-dml-frl-compressed-cap-check.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0460-vesa-displayid-dsc-bpp.patch b/pkgbuilds/linux-omarchy-bore/0460-vesa-displayid-dsc-bpp.patch index 1ba6ec7..7597723 100644 --- a/pkgbuilds/linux-omarchy-bore/0460-vesa-displayid-dsc-bpp.patch +++ b/pkgbuilds/linux-omarchy-bore/0460-vesa-displayid-dsc-bpp.patch @@ -1,7 +1,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c -@@ -7096,6 +7096,11 @@ static void fill_stream_properties_from_drm_display_mode( +@@ -7116,6 +7116,11 @@ static void fill_stream_properties_from_drm_display_mode( stream->output_color_space = get_output_color_space(timing_out, connector_state); stream->content_type = get_output_content_type(connector_state); @@ -13,7 +13,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm } static void fill_audio_info(struct audio_info *audio_info, -@@ -7616,6 +7621,7 @@ create_stream_for_sink(struct drm_connector *connector, +@@ -7636,6 +7641,7 @@ create_stream_for_sink(struct drm_connector *connector, struct drm_display_mode mode; struct drm_display_mode saved_mode; struct drm_display_mode *freesync_mode = NULL; @@ -21,7 +21,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm bool native_mode_found = false; bool recalculate_timing = false; bool scale = dm_state->scaling != RMX_OFF; -@@ -7707,6 +7713,16 @@ create_stream_for_sink(struct drm_connector *connector, +@@ -7727,6 +7733,16 @@ create_stream_for_sink(struct drm_connector *connector, } } diff --git a/pkgbuilds/linux-omarchy-bore/0460-vesa-displayid-dsc-bpp.patch.sig b/pkgbuilds/linux-omarchy-bore/0460-vesa-displayid-dsc-bpp.patch.sig index f76e652..51e6458 100644 Binary files a/pkgbuilds/linux-omarchy-bore/0460-vesa-displayid-dsc-bpp.patch.sig and b/pkgbuilds/linux-omarchy-bore/0460-vesa-displayid-dsc-bpp.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0461-vesa-dsc-passthru-mode-match-fix.patch b/pkgbuilds/linux-omarchy-bore/0461-vesa-dsc-passthru-mode-match-fix.patch index e27b493..b826d02 100644 --- a/pkgbuilds/linux-omarchy-bore/0461-vesa-dsc-passthru-mode-match-fix.patch +++ b/pkgbuilds/linux-omarchy-bore/0461-vesa-dsc-passthru-mode-match-fix.patch @@ -1,7 +1,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c -@@ -7622,6 +7622,7 @@ create_stream_for_sink(struct drm_connector *connector, +@@ -7642,6 +7642,7 @@ create_stream_for_sink(struct drm_connector *connector, struct drm_display_mode saved_mode; struct drm_display_mode *freesync_mode = NULL; struct drm_display_mode *dsc_passthru_mode = NULL; @@ -9,7 +9,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm bool native_mode_found = false; bool recalculate_timing = false; bool scale = dm_state->scaling != RMX_OFF; -@@ -7688,6 +7689,22 @@ create_stream_for_sink(struct drm_connector *connector, +@@ -7708,6 +7709,22 @@ create_stream_for_sink(struct drm_connector *connector, mode_refresh = drm_mode_vrefresh(&mode); @@ -32,7 +32,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm if (preferred_mode == NULL) { /* * This may not be an error, the use case is when we have no -@@ -7713,16 +7730,6 @@ create_stream_for_sink(struct drm_connector *connector, +@@ -7733,16 +7750,6 @@ create_stream_for_sink(struct drm_connector *connector, } } diff --git a/pkgbuilds/linux-omarchy-bore/0461-vesa-dsc-passthru-mode-match-fix.patch.sig b/pkgbuilds/linux-omarchy-bore/0461-vesa-dsc-passthru-mode-match-fix.patch.sig index d303e52..09c45c3 100644 Binary files a/pkgbuilds/linux-omarchy-bore/0461-vesa-dsc-passthru-mode-match-fix.patch.sig and b/pkgbuilds/linux-omarchy-bore/0461-vesa-dsc-passthru-mode-match-fix.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0473-i915-ptl-cdclk-sanitize.patch b/pkgbuilds/linux-omarchy-bore/0473-i915-ptl-cdclk-sanitize.patch deleted file mode 100644 index 08d39ec..0000000 --- a/pkgbuilds/linux-omarchy-bore/0473-i915-ptl-cdclk-sanitize.patch +++ /dev/null @@ -1,16 +0,0 @@ -diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c ---- a/drivers/gpu/drm/i915/display/intel_cdclk.c -+++ b/drivers/gpu/drm/i915/display/intel_cdclk.c -@@ -2366,8 +2366,10 @@ static void bxt_sanitize_cdclk(struct intel_display *display) - * dividers both syncing to an active pipe, or asynchronously - * (PIPE_NONE). - */ -- cdctl &= ~bxt_cdclk_cd2x_pipe(display, INVALID_PIPE); -- cdctl |= bxt_cdclk_cd2x_pipe(display, INVALID_PIPE); -+ if (DISPLAY_VER(display) < 30) { -+ cdctl &= ~bxt_cdclk_cd2x_pipe(display, INVALID_PIPE); -+ cdctl |= bxt_cdclk_cd2x_pipe(display, INVALID_PIPE); -+ } - - if (cdctl != expected) { - if (DISPLAY_VER(display) < 20) { diff --git a/pkgbuilds/linux-omarchy-bore/0473-i915-ptl-cdclk-sanitize.patch.sig b/pkgbuilds/linux-omarchy-bore/0473-i915-ptl-cdclk-sanitize.patch.sig deleted file mode 100644 index 1048bf7..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/0473-i915-ptl-cdclk-sanitize.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/0474-amd-display-oled-vesa-backlight.patch b/pkgbuilds/linux-omarchy-bore/0474-amd-display-oled-vesa-backlight.patch index 590d8a0..035758b 100644 --- a/pkgbuilds/linux-omarchy-bore/0474-amd-display-oled-vesa-backlight.patch +++ b/pkgbuilds/linux-omarchy-bore/0474-amd-display-oled-vesa-backlight.patch @@ -1,7 +1,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c -@@ -4160,8 +4160,12 @@ static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector) +@@ -4176,8 +4176,12 @@ static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector) else if (!IS_ERR_OR_NULL(panel_backlight_quirk) && panel_backlight_quirk->force_pwm) caps->aux_support = false; diff --git a/pkgbuilds/linux-omarchy-bore/0474-amd-display-oled-vesa-backlight.patch.sig b/pkgbuilds/linux-omarchy-bore/0474-amd-display-oled-vesa-backlight.patch.sig index 14aa526..3f0768a 100644 Binary files a/pkgbuilds/linux-omarchy-bore/0474-amd-display-oled-vesa-backlight.patch.sig and b/pkgbuilds/linux-omarchy-bore/0474-amd-display-oled-vesa-backlight.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0476-amd-display-ycbcr420-only-modes.patch b/pkgbuilds/linux-omarchy-bore/0476-amd-display-ycbcr420-only-modes.patch new file mode 100644 index 0000000..8d6c8a4 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0476-amd-display-ycbcr420-only-modes.patch @@ -0,0 +1,25 @@ +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +@@ -7020,16 +7020,11 @@ static void fill_stream_properties_from_drm_display_mode( + timing_out->v_border_top = 0; + timing_out->v_border_bottom = 0; + /* TODO: un-hardcode */ +- if (drm_mode_is_420_only(info, mode_in) +- && (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A || +- stream->signal == SIGNAL_TYPE_HDMI_FRL) +- && aconnector +- && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420) +- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420; +- else if (drm_mode_is_420_also(info, mode_in) +- && aconnector +- && (aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420 +- || aconnector->force_yuv420_output)) ++ if (drm_mode_is_420_only(info, mode_in) || ++ (aconnector && ++ (aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420 || ++ aconnector->force_yuv420_output) && ++ drm_mode_is_420_also(info, mode_in))) + timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420; + else if ((connector->display_info.color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422)) + && aconnector diff --git a/pkgbuilds/linux-omarchy-bore/0476-amd-display-ycbcr420-only-modes.patch.sig b/pkgbuilds/linux-omarchy-bore/0476-amd-display-ycbcr420-only-modes.patch.sig new file mode 100644 index 0000000..bf85c11 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0476-amd-display-ycbcr420-only-modes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0477-amd-display-irq-atomic-rmw.patch b/pkgbuilds/linux-omarchy-bore/0477-amd-display-irq-atomic-rmw.patch new file mode 100644 index 0000000..1b0eb75 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0477-amd-display-irq-atomic-rmw.patch @@ -0,0 +1,303 @@ +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +@@ -3345,7 +3345,7 @@ static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev, + if (acrtc && state->stream_status[i].plane_count != 0 && + amdgpu_ip_version(adev, DCE_HWIP, 0) == 0) { + irq_source = IRQ_TYPE_PFLIP + acrtc->otg_inst; +- rc = dc_interrupt_set(adev->dm.dc, irq_source, enable) ? 0 : -EBUSY; ++ rc = amdgpu_dm_irq_set(adev, irq_source, enable) ? 0 : -EBUSY; + if (rc) + drm_warn(adev_to_drm(adev), "Failed to %s pflip interrupts\n", + enable ? "enable" : "disable"); +@@ -3369,7 +3369,7 @@ static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev, + /* During gpu-reset we disable and then enable vblank irq, so + * don't use amdgpu_irq_get/put() to avoid refcount change. + */ +- if (!dc_interrupt_set(adev->dm.dc, irq_source, enable)) ++ if (!amdgpu_dm_irq_set(adev, irq_source, enable)) + drm_warn(adev_to_drm(adev), "Failed to %sable vblank interrupt\n", enable ? "en" : "dis"); + + } else if (acrtc && state->stream_status[i].plane_count != 0) { +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +@@ -550,6 +550,18 @@ struct amdgpu_display_manager { + struct common_irq_params + vupdate_params[DC_IRQ_SOURCE_VUPDATE6 - DC_IRQ_SOURCE_VUPDATE1 + 1]; + ++ /** ++ * @irq_reg_lock: ++ * ++ * Serializes the read-modify-writes of the HW interrupt control ++ * registers. Several interrupt sources share one register - e.g. the ++ * enable and clear bits of both VSTARTUP (vblank) and VUPDATE_NO_LOCK ++ * live in OTG_GLOBAL_SYNC_STATUS. Therefore, enabling one source must ++ * not race with acking another. Held only across amdgpu_dm_irq_set() ++ * and amdgpu_dm_irq_ack(). ++ */ ++ spinlock_t irq_reg_lock; ++ + /** + * @dmub_trace_params: + * +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c +@@ -31,6 +31,7 @@ + #include "amdgpu_dm_psr.h" + #include "amdgpu_dm_replay.h" + #include "amdgpu_dm_crtc.h" ++#include "amdgpu_dm_irq.h" + #include "amdgpu_dm_plane.h" + #include "amdgpu_dm_trace.h" + #include "amdgpu_dm_debugfs.h" +@@ -87,7 +88,7 @@ int amdgpu_dm_crtc_set_vupdate_irq(struct drm_crtc *crtc, bool enable) + + irq_source = IRQ_TYPE_VUPDATE + acrtc->otg_inst; + +- rc = dc_interrupt_set(adev->dm.dc, irq_source, enable) ? 0 : -EBUSY; ++ rc = amdgpu_dm_irq_set(adev, irq_source, enable) ? 0 : -EBUSY; + + DRM_DEBUG_VBL("crtc %d - vupdate irq %sabling: r=%d\n", + acrtc->crtc_id, enable ? "en" : "dis", rc); +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +@@ -1378,12 +1378,13 @@ void dm_helpers_free_gpu_mem( + + bool dm_helpers_dmub_outbox_interrupt_control(struct dc_context *ctx, bool enable) + { ++ struct amdgpu_device *adev = ctx->driver_context; + enum dc_irq_source irq_source; + bool ret; + + irq_source = DC_IRQ_SOURCE_DMCUB_OUTBOX; + +- ret = dc_interrupt_set(ctx->dc, irq_source, enable); ++ ret = amdgpu_dm_irq_set(adev, irq_source, enable); + + DRM_DEBUG_DRIVER("Dmub trace irq %sabling: r=%d\n", + enable ? "en" : "dis", ret); +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c +@@ -424,6 +424,7 @@ int amdgpu_dm_irq_init(struct amdgpu_device *adev) + DRM_DEBUG_KMS("DM_IRQ\n"); + + spin_lock_init(&adev->dm.irq_handler_list_table_lock); ++ spin_lock_init(&adev->dm.irq_reg_lock); + + for (src = 0; src < DAL_IRQ_SOURCES_NUMBER; src++) { + /* low context handler list init */ +@@ -496,7 +497,7 @@ void amdgpu_dm_irq_suspend(struct amdgpu_device *adev) + hnd_list_l = &adev->dm.irq_handler_list_low_tab[src]; + hnd_list_h = &adev->dm.irq_handler_list_high_tab[src]; + if (!list_empty(hnd_list_l) || !list_empty(hnd_list_h)) +- dc_interrupt_set(adev->dm.dc, src, false); ++ amdgpu_dm_irq_set(adev, src, false); + + DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); + +@@ -533,7 +534,7 @@ void amdgpu_dm_irq_resume_early(struct amdgpu_device *adev) + hnd_list_l = &adev->dm.irq_handler_list_low_tab[src]; + hnd_list_h = &adev->dm.irq_handler_list_high_tab[src]; + if (!list_empty(hnd_list_l) || !list_empty(hnd_list_h)) +- dc_interrupt_set(adev->dm.dc, src, true); ++ amdgpu_dm_irq_set(adev, src, true); + } + + DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); +@@ -558,7 +559,7 @@ void amdgpu_dm_irq_resume_late(struct amdgpu_device *adev) + hnd_list_l = &adev->dm.irq_handler_list_low_tab[src]; + hnd_list_h = &adev->dm.irq_handler_list_high_tab[src]; + if (!list_empty(hnd_list_l) || !list_empty(hnd_list_h)) +- dc_interrupt_set(adev->dm.dc, src, true); ++ amdgpu_dm_irq_set(adev, src, true); + } + + DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); +@@ -645,6 +646,21 @@ static void amdgpu_dm_irq_immediate_work(struct amdgpu_device *adev, + DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); + } + ++bool amdgpu_dm_irq_set(struct amdgpu_device *adev, enum dc_irq_source src, ++ bool enable) ++{ ++ guard(spinlock_irqsave)(&adev->dm.irq_reg_lock); ++ ++ return dc_interrupt_set(adev->dm.dc, src, enable); ++} ++ ++void amdgpu_dm_irq_ack(struct amdgpu_device *adev, enum dc_irq_source src) ++{ ++ guard(spinlock_irqsave)(&adev->dm.irq_reg_lock); ++ ++ dc_interrupt_ack(adev->dm.dc, src); ++} ++ + /** + * amdgpu_dm_irq_handler - Generic DM IRQ handler + * @adev: amdgpu base driver device containing the DM device +@@ -665,7 +681,7 @@ static int amdgpu_dm_irq_handler(struct amdgpu_device *adev, + entry->src_id, + entry->src_data[0]); + +- dc_interrupt_ack(adev->dm.dc, src); ++ amdgpu_dm_irq_ack(adev, src); + + /* Call high irq work immediately */ + amdgpu_dm_irq_immediate_work(adev, src); +@@ -703,7 +719,7 @@ static int amdgpu_dm_set_hpd_irq_state(struct amdgpu_device *adev, + enum dc_irq_source src = amdgpu_dm_hpd_to_dal_irq_source(type); + bool st = (state == AMDGPU_IRQ_STATE_ENABLE); + +- dc_interrupt_set(adev->dm.dc, src, st); ++ amdgpu_dm_irq_set(adev, src, st); + return 0; + } + +@@ -737,7 +753,7 @@ static inline int dm_irq_state(struct amdgpu_device *adev, + if (dc && dc->caps.ips_support && dc->idle_optimizations_allowed) + dc_allow_idle_optimizations(dc, false); + +- dc_interrupt_set(adev->dm.dc, irq_source, st); ++ amdgpu_dm_irq_set(adev, irq_source, st); + return 0; + } + +@@ -791,7 +807,7 @@ static int amdgpu_dm_set_dmub_outbox_irq_state(struct amdgpu_device *adev, + enum dc_irq_source irq_source = DC_IRQ_SOURCE_DMCUB_OUTBOX; + bool st = (state == AMDGPU_IRQ_STATE_ENABLE); + +- dc_interrupt_set(adev->dm.dc, irq_source, st); ++ amdgpu_dm_irq_set(adev, irq_source, st); + return 0; + } + +@@ -817,7 +833,7 @@ static int amdgpu_dm_set_dmub_trace_irq_state(struct amdgpu_device *adev, + enum dc_irq_source irq_source = DC_IRQ_SOURCE_DMCUB_OUTBOX0; + bool st = (state == AMDGPU_IRQ_STATE_ENABLE); + +- dc_interrupt_set(adev->dm.dc, irq_source, st); ++ amdgpu_dm_irq_set(adev, irq_source, st); + return 0; + } + +@@ -881,9 +897,7 @@ void amdgpu_dm_set_irq_funcs(struct amdgpu_device *adev) + } + void amdgpu_dm_outbox_init(struct amdgpu_device *adev) + { +- dc_interrupt_set(adev->dm.dc, +- DC_IRQ_SOURCE_DMCUB_OUTBOX, +- true); ++ amdgpu_dm_irq_set(adev, DC_IRQ_SOURCE_DMCUB_OUTBOX, true); + } + + /** +@@ -905,7 +919,7 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev) + + /* First, clear all hpd and hpdrx interrupts */ + for (i = DC_IRQ_SOURCE_HPD1; i <= DC_IRQ_SOURCE_HPD6RX; i++) { +- if (!dc_interrupt_set(adev->dm.dc, i, false)) ++ if (!amdgpu_dm_irq_set(adev, i, false)) + drm_err(dev, "Failed to clear hpd(rx) source=%d on init\n", + i); + } +@@ -934,7 +948,7 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev) + * of dm. Note that only hpd interrupt types are registered with + * base driver; hpd_rx types aren't. IOW, amdgpu_irq_get/put on + * hpd_rx isn't available. DM currently controls hpd_rx +- * explicitly with dc_interrupt_set() ++ * explicitly with amdgpu_dm_irq_set() + */ + if (dc_link->irq_source_hpd != DC_IRQ_SOURCE_INVALID) { + irq_type = dc_link->irq_source_hpd - DC_IRQ_SOURCE_HPD1; +@@ -943,23 +957,21 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev) + * and what bios reports as the # of connectors with hpd + * sources. Since the # of hpd source types registered + * with base driver == mode_info.num_hpd, we have to +- * fallback to dc_interrupt_set for the remaining types. ++ * fallback to amdgpu_dm_irq_set for the remaining types. + */ + if (irq_type < adev->mode_info.num_hpd) { + if (amdgpu_irq_get(adev, &adev->hpd_irq, irq_type)) + drm_err(dev, "DM_IRQ: Failed get HPD for source=%d)!\n", + dc_link->irq_source_hpd); + } else { +- dc_interrupt_set(adev->dm.dc, +- dc_link->irq_source_hpd, +- true); ++ amdgpu_dm_irq_set(adev, dc_link->irq_source_hpd, ++ true); + } + } + + if (dc_link->irq_source_hpd_rx != DC_IRQ_SOURCE_INVALID) { +- dc_interrupt_set(adev->dm.dc, +- dc_link->irq_source_hpd_rx, +- true); ++ amdgpu_dm_irq_set(adev, dc_link->irq_source_hpd_rx, ++ true); + } + } + drm_connector_list_iter_end(&iter); +@@ -1003,16 +1015,14 @@ void amdgpu_dm_hpd_fini(struct amdgpu_device *adev) + drm_err(dev, "DM_IRQ: Failed put HPD for source=%d!\n", + dc_link->irq_source_hpd); + } else { +- dc_interrupt_set(adev->dm.dc, +- dc_link->irq_source_hpd, +- false); ++ amdgpu_dm_irq_set(adev, dc_link->irq_source_hpd, ++ false); + } + } + + if (dc_link->irq_source_hpd_rx != DC_IRQ_SOURCE_INVALID) { +- dc_interrupt_set(adev->dm.dc, +- dc_link->irq_source_hpd_rx, +- false); ++ amdgpu_dm_irq_set(adev, dc_link->irq_source_hpd_rx, ++ false); + } + } + drm_connector_list_iter_end(&iter); +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h +@@ -81,6 +81,34 @@ void amdgpu_dm_irq_unregister_interrupt(struct amdgpu_device *adev, + enum dc_irq_source irq_source, + void *ih_index); + ++/** ++ * amdgpu_dm_irq_set - enable or disable a DC interrupt source. ++ * ++ * @adev: AMD DRM device ++ * @src: DC interrupt source to toggle ++ * @enable: true to enable the source, false to disable it ++ * ++ * DM-wide replacement for dc_interrupt_set(). As locking is DM's ++ * responsibility, this is a thin wrapper serializes the underlying ++ * read-modify-write against the other interrupt sources sharing HW control ++ * registers with @src, so DM must never call dc_interrupt_set() directly. ++ * ++ * Returns: true if the source was toggled. ++ */ ++bool amdgpu_dm_irq_set(struct amdgpu_device *adev, enum dc_irq_source src, ++ bool enable); ++ ++/** ++ * amdgpu_dm_irq_ack - acknowledge a DC interrupt source. ++ * ++ * @adev: AMD DRM device ++ * @src: DC interrupt source to acknowledge ++ * ++ * DM-wide replacement for dc_interrupt_ack(), serialized the same way as ++ * amdgpu_dm_irq_set(). ++ */ ++void amdgpu_dm_irq_ack(struct amdgpu_device *adev, enum dc_irq_source src); ++ + void amdgpu_dm_set_irq_funcs(struct amdgpu_device *adev); + + void amdgpu_dm_outbox_init(struct amdgpu_device *adev); diff --git a/pkgbuilds/linux-omarchy-bore/0477-amd-display-irq-atomic-rmw.patch.sig b/pkgbuilds/linux-omarchy-bore/0477-amd-display-irq-atomic-rmw.patch.sig new file mode 100644 index 0000000..ffb08a4 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0477-amd-display-irq-atomic-rmw.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0478-amd-freesync-vcp-code-check-hdmi-pcon.patch b/pkgbuilds/linux-omarchy-bore/0478-amd-freesync-vcp-code-check-hdmi-pcon.patch new file mode 100644 index 0000000..dcdb282 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0478-amd-freesync-vcp-code-check-hdmi-pcon.patch @@ -0,0 +1,16 @@ +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +@@ -14232,6 +14232,12 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, + } + } + ++ /* HDMI/PCON without an EDID FreeSync VCP code is not FreeSync-capable. */ ++ if ((sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A || ++ as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) && ++ !sink->edid_caps.freesync_vcp_code) ++ freesync_capable = false; ++ + /* Handle MCCS */ + if (do_mccs) { + dm_helpers_read_mccs_caps(adev->dm.dc->ctx, amdgpu_dm_connector->dc_link, sink); diff --git a/pkgbuilds/linux-omarchy-bore/0478-amd-freesync-vcp-code-check-hdmi-pcon.patch.sig b/pkgbuilds/linux-omarchy-bore/0478-amd-freesync-vcp-code-check-hdmi-pcon.patch.sig new file mode 100644 index 0000000..55dd043 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0478-amd-freesync-vcp-code-check-hdmi-pcon.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0479-amd-hdmi-allm-hf-vsif-in-vsp-infopacket.patch b/pkgbuilds/linux-omarchy-bore/0479-amd-hdmi-allm-hf-vsif-in-vsp-infopacket.patch new file mode 100644 index 0000000..13b9043 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0479-amd-hdmi-allm-hf-vsif-in-vsp-infopacket.patch @@ -0,0 +1,40 @@ +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +@@ -10135,20 +10135,19 @@ static void update_freesync_state_on_stream( + */ + if (new_stream->signal == SIGNAL_TYPE_HDMI_TYPE_A || + new_stream->signal == SIGNAL_TYPE_HDMI_FRL) { +- struct dc_info_packet hf_vsif = {0}; ++ struct dc_info_packet vsp_infopacket = {0}; + bool sink_allm = aconn && aconn->base.display_info.hdmi.allm; + bool allm = sink_allm && + (vrr_params.state == VRR_STATE_ACTIVE_VARIABLE || + vrr_params.state == VRR_STATE_ACTIVE_FIXED); + bool allm_changed; + +- if (allm) +- mod_build_hf_vsif_infopacket(new_stream, &hf_vsif, allm, allm); ++ mod_build_hf_vsif_infopacket(new_stream, &vsp_infopacket, allm, allm); + +- allm_changed = memcmp(&new_stream->hfvsif_infopacket, &hf_vsif, +- sizeof(hf_vsif)) != 0; ++ allm_changed = memcmp(&new_stream->vsp_infopacket, &vsp_infopacket, ++ sizeof(vsp_infopacket)) != 0; + new_crtc_state->freesync_vrr_info_changed |= allm_changed; +- new_stream->hfvsif_infopacket = hf_vsif; ++ new_stream->vsp_infopacket = vsp_infopacket; + + if (allm_changed) + drm_dbg_driver(adev_to_drm(adev), +@@ -10727,8 +10726,8 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, + if (acrtc_state->freesync_vrr_info_changed) { + bundle->stream_update.vrr_infopacket = + &acrtc_state->stream->vrr_infopacket; +- bundle->stream_update.hfvsif_infopacket = +- &acrtc_state->stream->hfvsif_infopacket; ++ bundle->stream_update.vsp_infopacket = ++ &acrtc_state->stream->vsp_infopacket; + } + } + } diff --git a/pkgbuilds/linux-omarchy-bore/0479-amd-hdmi-allm-hf-vsif-in-vsp-infopacket.patch.sig b/pkgbuilds/linux-omarchy-bore/0479-amd-hdmi-allm-hf-vsif-in-vsp-infopacket.patch.sig new file mode 100644 index 0000000..8cd5401 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0479-amd-hdmi-allm-hf-vsif-in-vsp-infopacket.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0510-sound-updates.patch b/pkgbuilds/linux-omarchy-bore/0510-sound-updates.patch index 29d72a3..1368f53 100644 --- a/pkgbuilds/linux-omarchy-bore/0510-sound-updates.patch +++ b/pkgbuilds/linux-omarchy-bore/0510-sound-updates.patch @@ -1,7 +1,7 @@ diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c -@@ -1752,6 +1752,7 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device) +@@ -1757,6 +1757,7 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device) * by the drivers/platform/x86/serial-multi-instantiate.c driver, which * knows which client device id to use for each resource. */ @@ -9,18 +9,6 @@ diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c {"BSG1160", }, {"BSG2150", }, {"CSC3551", }, -diff --git a/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c b/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c ---- a/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c -+++ b/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c -@@ -147,7 +147,7 @@ static const struct reg_default halo_register_defaults[] = { - { 0x2b805c0, 0 }, /* HALO_SCRATCH1 */ - { 0x2b805c8, 0 }, /* HALO_SCRATCH2 */ - { 0x2b805d0, 0 }, /* HALO_SCRATCH3 */ -- { 0x2b805c8, 0 }, /* HALO_SCRATCH4 */ -+ { 0x2b805d8, 0 }, /* HALO_SCRATCH4 */ - { 0x2bc1000, 0 }, /* HALO_CCM_CORE_CONTROL */ - { 0x2bc7000, 0 }, /* HALO_WDT_CONTROL */ - diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c --- a/drivers/platform/x86/serial-multi-instantiate.c +++ b/drivers/platform/x86/serial-multi-instantiate.c @@ -48,132 +36,6 @@ diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/ { "BSG1160", (unsigned long)&bsg1160_data }, { "BSG2150", (unsigned long)&bsg2150_data }, { "CSC3551", (unsigned long)&cs35l41_hda }, -diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c ---- a/drivers/soundwire/bus_type.c -+++ b/drivers/soundwire/bus_type.c -@@ -105,6 +105,9 @@ static int sdw_bus_probe(struct device *dev) - } - slave->index = ret; - -+ /* Create IRQ mapping now so the driver can get it in probe() */ -+ sdw_irq_create_mapping(slave); -+ - ret = drv->probe(slave, id); - if (ret) { - ida_free(&slave->bus->slave_ida, slave->index); -@@ -117,9 +120,6 @@ static int sdw_bus_probe(struct device *dev) - if (drv->ops && drv->ops->read_prop) - drv->ops->read_prop(slave); - -- if (slave->prop.use_domain_irq) -- sdw_irq_create_mapping(slave); -- - /* init the dynamic sysfs attributes we need */ - ret = sdw_slave_sysfs_dpn_init(slave); - if (ret < 0) -diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h ---- a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h -+++ b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h -@@ -152,6 +152,10 @@ - #define LPI_MI2S_TX_4 146 - #define SENARY_MI2S_RX 147 - #define SENARY_MI2S_TX 148 -+#define LPI_MI2S_RX_5 149 -+#define LPI_MI2S_TX_5 150 -+#define LPI_MI2S_RX_6 151 -+#define LPI_MI2S_TX_6 152 - - #define LPASS_CLK_ID_PRI_MI2S_IBIT 1 - #define LPASS_CLK_ID_PRI_MI2S_EBIT 2 -@@ -233,11 +237,39 @@ - /* Clock ID for RX CORE MCLK2 2X MCLK */ - #define LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK 70 - -+#define LAPSS_CLK_ID_QAIF_IF0_IBIT 71 -+#define LAPSS_CLK_ID_QAIF_IF0_EBIT 72 -+#define LAPSS_CLK_ID_QAIF_IF1_IBIT 73 -+#define LAPSS_CLK_ID_QAIF_IF1_EBIT 74 -+#define LAPSS_CLK_ID_QAIF_IF2_IBIT 75 -+#define LAPSS_CLK_ID_QAIF_IF2_EBIT 76 -+#define LAPSS_CLK_ID_QAIF_IF3_IBIT 77 -+#define LAPSS_CLK_ID_QAIF_IF3_EBIT 78 -+#define LAPSS_CLK_ID_QAIF_IF4_IBIT 79 -+#define LAPSS_CLK_ID_QAIF_IF4_EBIT 80 -+#define LAPSS_CLK_ID_QAIF_IF5_IBIT 81 -+#define LAPSS_CLK_ID_QAIF_IF5_EBIT 82 -+#define LAPSS_CLK_ID_QAIF_IF6_IBIT 83 -+#define LAPSS_CLK_ID_QAIF_IF6_EBIT 84 -+#define LAPSS_CLK_ID_QAIF_IF7_IBIT 85 -+#define LAPSS_CLK_ID_QAIF_IF7_EBIT 86 -+#define LAPSS_CLK_ID_QAIF_IF8_IBIT 87 -+#define LAPSS_CLK_ID_QAIF_IF8_EBIT 88 -+#define LAPSS_CLK_ID_QAIF_IF9_IBIT 89 -+#define LAPSS_CLK_ID_QAIF_IF9_EBIT 90 -+#define LAPSS_CLK_ID_QAIF_IF10_IBIT 91 -+#define LAPSS_CLK_ID_QAIF_IF10_EBIT 92 -+#define LAPSS_CLK_ID_QAIF_IF11_IBIT 93 -+#define LAPSS_CLK_ID_QAIF_IF11_EBIT 94 -+#define LAPSS_CLK_ID_QAIF_IF12_IBIT 95 -+#define LAPSS_CLK_ID_QAIF_IF12_EBIT 96 -+#define LAPSS_CLK_ID_VA_QAIF_IF0_IBIT 97 -+#define LAPSS_CLK_ID_VA_QAIF_IF0_EBIT 98 -+ - #define LPASS_HW_AVTIMER_VOTE 101 - #define LPASS_HW_MACRO_VOTE 102 - #define LPASS_HW_DCODEC_VOTE 103 -- --#define Q6AFE_MAX_CLK_ID 104 -+#define LPASS_HW_LPR_VOTE 104 - - #define LPASS_CLK_ATTRIBUTE_INVALID 0x0 - #define LPASS_CLK_ATTRIBUTE_COUPLE_NO 0x1 -diff --git a/include/linux/platform_data/omap-twl4030.h b/include/linux/platform_data/omap-twl4030.h -deleted file mode 100644 ---- a/include/linux/platform_data/omap-twl4030.h -+++ /dev/null -@@ -1,42 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/** -- * omap-twl4030.h - ASoC machine driver for TI SoC based boards with twl4030 -- * codec, header. -- * -- * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com -- * All rights reserved. -- * -- * Author: Peter Ujfalusi -- */ -- --#ifndef _OMAP_TWL4030_H_ --#define _OMAP_TWL4030_H_ -- --/* To select if only one channel is connected in a stereo port */ --#define OMAP_TWL4030_LEFT (1 << 0) --#define OMAP_TWL4030_RIGHT (1 << 1) -- --struct omap_tw4030_pdata { -- const char *card_name; -- /* Voice port is connected to McBSP3 */ -- bool voice_connected; -- -- /* The driver will parse the connection flags if this flag is set */ -- bool custom_routing; -- /* Flags to indicate connected audio ports. */ -- u8 has_hs; -- u8 has_hf; -- u8 has_predriv; -- u8 has_carkit; -- bool has_ear; -- -- bool has_mainmic; -- bool has_submic; -- bool has_hsmic; -- bool has_carkitmic; -- bool has_digimic0; -- bool has_digimic1; -- u8 has_linein; --}; -- --#endif /* _OMAP_TWL4030_H_ */ diff --git a/include/sound/ac97/codec.h b/include/sound/ac97/codec.h --- a/include/sound/ac97/codec.h +++ b/include/sound/ac97/codec.h @@ -868,88 +730,6 @@ diff --git a/include/sound/core.h b/include/sound/core.h struct module *module; /* top-level module */ void *private_data; /* private data for soundcard */ -diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h ---- a/include/sound/cs35l56.h -+++ b/include/sound/cs35l56.h -@@ -417,7 +417,6 @@ void cs35l56_wait_control_port_ready(void); - void cs35l56_wait_min_reset_pulse(void); - void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire); - int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq); --irqreturn_t cs35l56_irq(int irq, void *data); - int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base); - int cs35l56_runtime_suspend_common(struct cs35l56_base *cs35l56_base); - int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_soundwire); -diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h ---- a/include/sound/dmaengine_pcm.h -+++ b/include/sound/dmaengine_pcm.h -@@ -175,12 +175,6 @@ int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream *substream, - struct dmaengine_pcm { - struct dma_chan *chan[SNDRV_PCM_STREAM_LAST + 1]; - const struct snd_dmaengine_pcm_config *config; -- struct snd_soc_component component; - unsigned int flags; - }; -- --static inline struct dmaengine_pcm *soc_component_to_pcm(struct snd_soc_component *p) --{ -- return container_of(p, struct dmaengine_pcm, component); --} - #endif -diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h ---- a/include/sound/hda-mlink.h -+++ b/include/sound/hda-mlink.h -@@ -9,6 +9,22 @@ - struct hdac_bus; - struct hdac_ext_link; - -+/** -+ * enum hda_bus_ml_link_type - mlink link type, used by SOF link DMA -+ * allocator constraints (see struct sof_intel_hda_dev). -+ * -+ * @HDA_BUS_ML_LINK_HDA: non-alt link, i.e. HDA codec or iDisp -+ * @HDA_BUS_ML_LINK_SDW: alt link, SoundWire -+ * @HDA_BUS_ML_LINK_UAOL: alt link, USB Audio Offload -+ * @HDA_BUS_ML_LINK_OTHER: alt link, SSP or DMIC -+ */ -+enum hda_bus_ml_link_type { -+ HDA_BUS_ML_LINK_HDA, -+ HDA_BUS_ML_LINK_SDW, -+ HDA_BUS_ML_LINK_UAOL, -+ HDA_BUS_ML_LINK_OTHER, -+}; -+ - #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK) - - int hda_bus_ml_init(struct hdac_bus *bus); -@@ -49,11 +65,12 @@ int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num); - int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, - int channel_mask, int stream_id, int dir); - --void hda_bus_ml_put_all(struct hdac_bus *bus); - void hda_bus_ml_reset_losidv(struct hdac_bus *bus); - int hda_bus_ml_resume(struct hdac_bus *bus); - int hda_bus_ml_suspend(struct hdac_bus *bus); - -+enum hda_bus_ml_link_type hda_bus_ml_link_get_type(struct hdac_ext_link *hlink); -+ - struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus); - struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus); - struct hdac_ext_link *hdac_bus_eml_sdw_get_hlink(struct hdac_bus *bus); -@@ -169,11 +186,13 @@ hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, - return 0; - } - --static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { } - static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { } - static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; } - static inline int hda_bus_ml_suspend(struct hdac_bus *bus) { return 0; } - -+static inline enum hda_bus_ml_link_type -+hda_bus_ml_link_get_type(struct hdac_ext_link *hlink) { return HDA_BUS_ML_LINK_HDA; } -+ - static inline struct hdac_ext_link * - hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus) { return NULL; } - diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -1319,196 +1099,6 @@ diff --git a/include/sound/rawmidi.h b/include/sound/rawmidi.h /* set up the tied devices */ static inline void snd_rawmidi_tie_devices(struct snd_rawmidi *r1, struct snd_rawmidi *r2) -diff --git a/include/sound/sdca_asoc.h b/include/sound/sdca_asoc.h ---- a/include/sound/sdca_asoc.h -+++ b/include/sound/sdca_asoc.h -@@ -25,6 +25,8 @@ struct snd_soc_dai_driver; - struct snd_soc_dai_ops; - struct snd_soc_dapm_route; - struct snd_soc_dapm_widget; -+struct snd_soc_pcm_stream; -+struct sdca_entity; - - /* convenient macro to handle the mono volume in 7.8 fixed format representation */ - #define SDCA_SINGLE_Q78_TLV(xname, xreg, xmin, xmax, xstep, tlv_array) \ -@@ -82,6 +84,11 @@ int sdca_asoc_populate_component(struct device *dev, - struct snd_soc_dai_driver **dai_drv, int *num_dai_drv, - const struct snd_soc_dai_ops *ops); - -+int sdca_asoc_populate_rate_format(struct device *dev, -+ struct sdca_function_data *function, -+ struct sdca_entity *entity, -+ struct snd_soc_pcm_stream *stream); -+ - int sdca_asoc_set_constraints(struct device *dev, struct regmap *regmap, - struct sdca_function_data *function, - struct snd_pcm_substream *substream, -@@ -100,9 +107,9 @@ int sdca_asoc_q78_put_volsw(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); - int sdca_asoc_q78_get_volsw(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); --int sdca_asoc_pde_poll_actual_ps(struct device *dev, struct regmap *regmap, -+int sdca_asoc_pde_poll_actual_ps(struct regmap *regmap, - int function_id, int entity_id, -- int from_ps, int to_ps, -- const struct sdca_pde_delay *pde_delays, -- int num_delays); -+ int from_ps, int to_ps, -+ const struct sdca_pde_delay *pde_delays, -+ int num_delays); - #endif // __SDCA_ASOC_H__ -diff --git a/include/sound/sdca_fdl.h b/include/sound/sdca_fdl.h ---- a/include/sound/sdca_fdl.h -+++ b/include/sound/sdca_fdl.h -@@ -83,6 +83,10 @@ static inline int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt) - return 0; - } - -+static inline void sdca_fdl_free_state(struct sdca_interrupt *interrupt) -+{ -+} -+ - static inline int sdca_fdl_process(struct sdca_interrupt *interrupt) - { - return 0; -diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h ---- a/include/sound/sdca_function.h -+++ b/include/sound/sdca_function.h -@@ -298,7 +298,7 @@ enum sdca_xu_controls { - }; - - /** -- * enum sdca_set_index_range - Column definitions UMP SetIndex -+ * enum sdca_fdl_set_index_range - Column definitions UMP SetIndex - */ - enum sdca_fdl_set_index_range { - SDCA_FDL_SET_INDEX_SET_NUMBER = 0, -@@ -803,6 +803,8 @@ struct sdca_control_range { - * @mode: Access mode of the Control. - * @layers: Bitmask of access layers of the Control. - * @deferrable: Indicates if the access to the Control can be deferred. -+ * @is_volatile: Indicates the Control registers are forced to be treated -+ * as volatile. - * @has_default: Indicates the Control has a default value to be written. - * @has_reset: Indicates the Control has a defined reset value. - * @has_fixed: Indicates the Control only supports a single value. -@@ -1116,7 +1118,6 @@ struct sdca_entity_ge { - - /** - * struct sdca_entity_hide - information specific to HIDE Entities -- * @hid: HID device structure - * @num_hidtx_ids: number of HIDTx Report ID - * @num_hidrx_ids: number of HIDRx Report ID - * @hidtx_ids: HIDTx Report ID -@@ -1127,11 +1128,8 @@ struct sdca_entity_ge { - * within this Device - * @max_delay: the maximum time in microseconds allowed for the Device - * to change the ownership from Device to Host -- * @hid_report_desc: HID Report Descriptor for the HIDE Entity -- * @hid_desc: HID descriptor for the HIDE Entity - */ - struct sdca_entity_hide { -- struct hid_device *hid; - unsigned int *hidtx_ids; - unsigned int *hidrx_ids; - int num_hidtx_ids; -@@ -1139,12 +1137,10 @@ struct sdca_entity_hide { - unsigned int af_number_list[SDCA_MAX_FUNCTION_COUNT]; - unsigned int hide_reside_function_num; - unsigned int max_delay; -- unsigned char *hid_report_desc; -- struct hid_descriptor hid_desc; - }; - - /** -- * enum sdca_xu_reset_machanism - SDCA FDL Resets -+ * enum sdca_xu_reset_mechanism - SDCA FDL Resets - */ - enum sdca_xu_reset_mechanism { - SDCA_XU_RESET_FUNCTION = 0x0, -@@ -1401,6 +1397,16 @@ struct sdca_fdl_data { - int num_sets; - }; - -+/** -+ * struct sdca_function_hid - information about a function's HID descriptors -+ * @report_desc: HID Report Descriptor for the HID Function -+ * @desc: HID descriptor for the HID Function -+ */ -+struct sdca_function_hid { -+ unsigned char *report_desc; -+ struct hid_descriptor desc; -+}; -+ - /** - * struct sdca_function_data - top-level information for one SDCA function - * @desc: Pointer to short descriptor from initial parsing. -@@ -1415,6 +1421,7 @@ struct sdca_fdl_data { - * @reset_max_delay: Maximum Function reset delay in microseconds, before an - * error should be reported. - * @fdl_data: FDL data for this Function, if available. -+ * @hid: HID data for this Function, if available. - */ - struct sdca_function_data { - struct sdca_function_desc *desc; -@@ -1430,6 +1437,10 @@ struct sdca_function_data { - unsigned int reset_max_delay; - - struct sdca_fdl_data fdl_data; -+ -+ union { -+ struct sdca_function_hid hid; -+ }; - }; - - static inline u32 sdca_range(struct sdca_control_range *range, -@@ -1451,8 +1462,7 @@ static inline u32 sdca_range_search(struct sdca_control_range *range, - return 0; - } - --int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, -- struct sdca_function_data *function); -+int sdca_parse_function(struct device *dev, struct sdca_function_data *function); - - const char *sdca_find_terminal_name(enum sdca_terminal_type type); - -@@ -1469,5 +1479,7 @@ struct sdca_control_range *sdca_selector_find_range(struct device *dev, - struct sdca_cluster *sdca_id_find_cluster(struct device *dev, - struct sdca_function_data *function, - const int id); -+struct sdca_entity *sdca_find_entity_by_label(struct sdca_function_data *function, -+ const char *entity_label); - - #endif -diff --git a/include/sound/sdca_hid.h b/include/sound/sdca_hid.h ---- a/include/sound/sdca_hid.h -+++ b/include/sound/sdca_hid.h -@@ -16,18 +16,21 @@ struct sdca_interrupt; - - #if IS_ENABLED(CONFIG_SND_SOC_SDCA_HID) - --int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, -- struct sdca_entity *entity); -+int sdca_add_hid_device(struct sdca_interrupt *interrupt); -+void sdca_destroy_hid_device(struct sdca_interrupt *interrupt); - int sdca_hid_process_report(struct sdca_interrupt *interrupt); - - #else - --static inline int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, -- struct sdca_entity *entity) -+static inline int sdca_add_hid_device(struct sdca_interrupt *interrupt) - { - return 0; - } - -+static inline void sdca_destroy_hid_device(struct sdca_interrupt *interrupt) -+{ -+} -+ - static inline int sdca_hid_process_report(struct sdca_interrupt *interrupt) - { - return 0; diff --git a/include/sound/seq_virmidi.h b/include/sound/seq_virmidi.h --- a/include/sound/seq_virmidi.h +++ b/include/sound/seq_virmidi.h @@ -1520,571 +1110,6 @@ diff --git a/include/sound/seq_virmidi.h b/include/sound/seq_virmidi.h struct rw_semaphore filelist_sem; struct list_head filelist; }; -diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h ---- a/include/sound/simple_card_utils.h -+++ b/include/sound/simple_card_utils.h -@@ -181,7 +181,7 @@ void simple_util_canonicalize_platform(struct snd_soc_dai_link_component *platfo - void simple_util_canonicalize_cpu(struct snd_soc_dai_link_component *cpus, - int is_single_links); - --void simple_util_clean_reference(struct snd_soc_card *card); -+void simple_util_clean_reference(struct simple_util_priv *priv); - - void simple_util_parse_convert(struct device_node *np, char *prefix, - struct simple_util_data *data); -@@ -189,18 +189,37 @@ bool simple_util_is_convert_required(const struct simple_util_data *data); - - int simple_util_get_sample_fmt(struct simple_util_data *data); - --int simple_util_parse_routing(struct snd_soc_card *card, -- char *prefix); --int simple_util_parse_widgets(struct snd_soc_card *card, -- char *prefix); --int simple_util_parse_pin_switches(struct snd_soc_card *card, -- char *prefix); -+int simple_util_parse_property(struct simple_util_priv *priv, -+ int (*func)(struct snd_soc_card *card, const char *propname), -+ char *prefix, char *property); -+static inline int simple_util_parse_routing(struct simple_util_priv *priv, char *prefix) -+{ -+ return simple_util_parse_property(priv, snd_soc_of_parse_audio_routing, -+ prefix, "routing"); -+} -+ -+static inline int simple_util_parse_widgets(struct simple_util_priv *priv, char *prefix) -+{ -+ return simple_util_parse_property(priv, snd_soc_of_parse_audio_simple_widgets, -+ prefix, "widgets"); -+} -+ -+static inline int simple_util_parse_pin_switches(struct simple_util_priv *priv, char *prefix) -+{ -+ return simple_util_parse_property(priv, snd_soc_of_parse_pin_switches, -+ prefix, "pin-switches"); -+} -+ -+static inline int simple_util_parse_aux_devs(struct simple_util_priv *priv, char *prefix) -+{ -+ return simple_util_parse_property(priv, snd_soc_of_parse_aux_devs, -+ prefix, "aux-devs"); -+} - - int simple_util_init_jack(struct snd_soc_card *card, - struct simple_util_jack *sjack, - int is_hp, char *prefix, char *pin); --int simple_util_init_aux_jacks(struct simple_util_priv *priv, -- char *prefix); -+int simple_util_init_aux_jacks(struct snd_soc_card *card, char *prefix); - int simple_util_init_priv(struct simple_util_priv *priv, - struct link_info *li); - void simple_util_remove(struct platform_device *pdev); -diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h ---- a/include/sound/soc-acpi.h -+++ b/include/sound/soc-acpi.h -@@ -57,7 +57,7 @@ static inline struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg) - #endif - - /** -- * snd_soc_acpi_mach_params: interface for machine driver configuration -+ * struct snd_soc_acpi_mach_params - interface for machine driver configuration - * - * @acpi_ipc_irq_index: used for BYT-CR detection - * @platform: string used for HDAudio codec support -@@ -93,7 +93,7 @@ struct snd_soc_acpi_mach_params { - }; - - /** -- * snd_soc_acpi_endpoint - endpoint descriptor -+ * struct snd_soc_acpi_endpoint - endpoint descriptor - * @num: endpoint number (mandatory, unique per device) - * @aggregated: 0 (independent) or 1 (logically grouped) - * @group_position: zero-based order (only when @aggregated is 1) -@@ -107,7 +107,7 @@ struct snd_soc_acpi_endpoint { - }; - - /** -- * snd_soc_acpi_adr_device - descriptor for _ADR-enumerated device -+ * struct snd_soc_acpi_adr_device - descriptor for _ADR-enumerated device - * @adr: 64 bit ACPI _ADR value - * @num_endpoints: number of endpoints for this device - * @endpoints: array of endpoints -@@ -121,7 +121,7 @@ struct snd_soc_acpi_adr_device { - }; - - /** -- * snd_soc_acpi_link_adr - ACPI-based list of _ADR enumerated devices -+ * struct snd_soc_acpi_link_adr - ACPI-based list of _ADR enumerated devices - * @mask: one bit set indicates the link this list applies to - * @num_adr: ARRAY_SIZE of devices - * @adr_d: array of devices -@@ -167,8 +167,8 @@ struct snd_soc_acpi_link_adr { - #define SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME BIT(4) - - /** -- * snd_soc_acpi_mach: ACPI-based machine descriptor. Most of the fields are -- * related to the hardware, except for the firmware and topology file names. -+ * struct snd_soc_acpi_mach - ACPI-based machine descriptor. Most of the fields -+ * are related to the hardware, except for the firmware and topology file names. - * A platform supported by legacy and Sound Open Firmware (SOF) would expose - * all firmware/topology related fields. - * -@@ -192,6 +192,7 @@ struct snd_soc_acpi_link_adr { - * the initial selection in the snd_soc_acpi_mach table. - * @pdata: intended for platform data or machine specific-ops. This structure - * is not constant since this field may be updated at run-time -+ * @mach_params: machine driver configuration - * @sof_tplg_filename: Sound Open Firmware topology file name, if enabled - * @tplg_quirk_mask: quirks to select different topology files dynamically - * @get_function_tplg_files: This is an optional callback, if specified then instead of -@@ -199,11 +200,11 @@ struct snd_soc_acpi_link_adr { - * files to be loaded. - * Return value: The number of the files or negative ERRNO. 0 means that the single topology - * file should be used, no function topology split can be used on the machine. -- * @card: the pointer of the card -- * @mach: the pointer of the machine driver -- * @prefix: the prefix of the topology file name. Typically, it is the path. -- * @tplg_files: the pointer of the array of the topology file names. -- * @best_effort: ignore non supported links and try to build the card in best effort -+ * card: the pointer of the card -+ * mach: the pointer of the machine driver -+ * prefix: the prefix of the topology file name. Typically, it is the path. -+ * tplg_files: the pointer of the array of the topology file names. -+ * best_effort: ignore non supported links and try to build the card in best effort - * with supported links - */ - /* Descriptor for SST ASoC machine driver */ -diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h ---- a/include/sound/soc-component.h -+++ b/include/sound/soc-component.h -@@ -253,6 +253,9 @@ struct snd_soc_component { - void *mark_pm; - - struct dentry *debugfs_root; -+ -+ /* Component private data */ -+ void *priv; - }; - - #define for_each_component_dais(component, dai)\ -@@ -283,6 +286,14 @@ static inline int snd_soc_component_cache_sync( - return regcache_sync(component->regmap); - } - -+struct snd_soc_component *snd_soc_component_alloc(struct device *dev); -+ -+void snd_soc_component_set_name(struct snd_soc_component *component, const char *name); -+const char *snd_soc_component_name(struct snd_soc_component *component); -+ -+void snd_soc_component_set_priv(struct snd_soc_component *component, void *priv); -+void *snd_soc_component_to_priv(struct snd_soc_component *component); -+ - void snd_soc_component_set_aux(struct snd_soc_component *component, - struct snd_soc_aux_dev *aux); - int snd_soc_component_init(struct snd_soc_component *component); -diff --git a/include/sound/soc.h b/include/sound/soc.h ---- a/include/sound/soc.h -+++ b/include/sound/soc.h -@@ -447,15 +447,16 @@ static inline int snd_soc_resume(struct device *dev) - } - #endif - int snd_soc_poweroff(struct device *dev); --int snd_soc_component_initialize(struct snd_soc_component *component, -- const struct snd_soc_component_driver *driver, -- struct device *dev); --int snd_soc_add_component(struct snd_soc_component *component, -- struct snd_soc_dai_driver *dai_drv, -- int num_dai); --int snd_soc_register_component(struct device *dev, -+int snd_soc_register_component_c(struct snd_soc_component *component, - const struct snd_soc_component_driver *component_driver, - struct snd_soc_dai_driver *dai_drv, int num_dai); -+int snd_soc_register_component_d(struct device *dev, -+ const struct snd_soc_component_driver *component_driver, -+ struct snd_soc_dai_driver *dai_drv, int num_dai); -+#define snd_soc_register_component(x, ...) _Generic((x), \ -+struct device * : snd_soc_register_component_d, \ -+struct snd_soc_component * : snd_soc_register_component_c)(x, __VA_ARGS__) -+ - int devm_snd_soc_register_component(struct device *dev, - const struct snd_soc_component_driver *component_driver, - struct snd_soc_dai_driver *dai_drv, int num_dai); -diff --git a/include/sound/soc_sdw_utils.h b/include/sound/soc_sdw_utils.h ---- a/include/sound/soc_sdw_utils.h -+++ b/include/sound/soc_sdw_utils.h -@@ -44,6 +44,18 @@ - - struct asoc_sdw_codec_info; - -+struct asoc_sdw_mc_private { -+ struct snd_soc_card card; -+ struct snd_soc_jack sdw_headset; -+ struct device *headset_codec_dev; /* only one headset per card */ -+ struct device *amp_dev1, *amp_dev2; -+ bool append_dai_type; -+ bool ignore_internal_dmic; -+ void *private; -+ unsigned long mc_quirk; -+ int codec_info_list_count; -+}; -+ - struct asoc_sdw_dai_info { - const bool direction[2]; /* playback & capture support */ - const char *codec_name; -@@ -88,25 +100,13 @@ struct asoc_sdw_codec_info { - - int (*codec_card_late_probe)(struct snd_soc_card *card); - -- int (*count_sidecar)(struct snd_soc_card *card, -+ int (*count_sidecar)(struct asoc_sdw_mc_private *ctx, - int *num_dais, int *num_devs); - int (*add_sidecar)(struct snd_soc_card *card, - struct snd_soc_dai_link **dai_links, - struct snd_soc_codec_conf **codec_conf); - }; - --struct asoc_sdw_mc_private { -- struct snd_soc_card card; -- struct snd_soc_jack sdw_headset; -- struct device *headset_codec_dev; /* only one headset per card */ -- struct device *amp_dev1, *amp_dev2; -- bool append_dai_type; -- bool ignore_internal_dmic; -- void *private; -- unsigned long mc_quirk; -- int codec_info_list_count; --}; -- - struct asoc_sdw_endpoint { - struct list_head list; - -@@ -182,7 +182,8 @@ struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks - const struct snd_soc_acpi_endpoint *new); - int asoc_sdw_get_dai_type(u32 type); - --int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, -+int asoc_sdw_parse_sdw_endpoints(struct device *dev, -+ struct asoc_sdw_mc_private *ctx, - struct snd_soc_aux_dev *soc_aux, - struct asoc_sdw_dailink *soc_dais, - struct asoc_sdw_endpoint *soc_ends, -@@ -235,7 +236,7 @@ int asoc_sdw_es9356_amp_init(struct snd_soc_card *card, - int asoc_sdw_es9356_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); - - /* CS AMP support */ --int asoc_sdw_bridge_cs35l56_count_sidecar(struct snd_soc_card *card, -+int asoc_sdw_bridge_cs35l56_count_sidecar(struct asoc_sdw_mc_private *ctx, - int *num_dais, int *num_devs); - int asoc_sdw_bridge_cs35l56_add_sidecar(struct snd_soc_card *card, - struct snd_soc_dai_link **dai_links, -diff --git a/include/sound/sof/dai-amd.h b/include/sound/sof/dai-amd.h ---- a/include/sound/sof/dai-amd.h -+++ b/include/sound/sof/dai-amd.h -@@ -18,6 +18,7 @@ struct sof_ipc_dai_acp_params { - uint32_t fsync_rate; /* FSYNC frequency in Hz */ - uint32_t tdm_slots; - uint32_t tdm_mode; -+ uint32_t format; - } __packed; - - /* ACPDMIC Configuration Request - SOF_IPC_DAI_AMD_CONFIG */ -diff --git a/include/sound/sof/dai.h b/include/sound/sof/dai.h ---- a/include/sound/sof/dai.h -+++ b/include/sound/sof/dai.h -@@ -90,6 +90,8 @@ enum sof_ipc_dai_type { - SOF_DAI_AMD_HS_VIRTUAL, /**< AMD ACP HS VIRTUAL */ - SOF_DAI_IMX_MICFIL, /** < i.MX MICFIL PDM */ - SOF_DAI_AMD_SDW, /**< AMD ACP SDW */ -+ SOF_DAI_INTEL_UAOL, /**< Intel UAOL */ -+ SOF_DAI_AMD_I2S, /**< AMD ACP I2S */ - }; - - /* general purpose DAI configuration */ -@@ -121,6 +123,7 @@ struct sof_ipc_dai_config { - struct sof_ipc_dai_mtk_afe_params afe; - struct sof_ipc_dai_micfil_params micfil; - struct sof_ipc_dai_acp_sdw_params acp_sdw; -+ struct sof_ipc_dai_acp_params acp_i2s; - }; - } __packed; - -diff --git a/include/sound/sof/header.h b/include/sound/sof/header.h ---- a/include/sound/sof/header.h -+++ b/include/sound/sof/header.h -@@ -179,7 +179,7 @@ struct sof_ipc_compound_hdr { - uint32_t count; /**< count of 0 means end of compound sequence */ - } __packed; - --/** -+/* - * OOPS header architecture specific data. - */ - struct sof_ipc_dsp_oops_arch_hdr { -@@ -187,7 +187,7 @@ struct sof_ipc_dsp_oops_arch_hdr { - uint32_t totalsize; /* Total size of oops message */ - } __packed; - --/** -+/* - * OOPS header platform specific data. - */ - struct sof_ipc_dsp_oops_plat_hdr { -diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h ---- a/include/sound/sof/ipc4/header.h -+++ b/include/sound/sof/ipc4/header.h -@@ -187,6 +187,10 @@ enum sof_ipc4_pipeline_state { - #define SOF_IPC4_GLB_PIPE_EXT_CORE_ID_MASK GENMASK(23, 20) - #define SOF_IPC4_GLB_PIPE_EXT_CORE_ID(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_CORE_ID_SHIFT) - -+#define SOF_IPC4_GLB_PIPE_PAYLOAD_SHIFT 29 -+#define SOF_IPC4_GLB_PIPE_PAYLOAD_MASK BIT(29) -+#define SOF_IPC4_GLB_PIPE_PAYLOAD(x) ((x) << SOF_IPC4_GLB_PIPE_PAYLOAD_SHIFT) -+ - /* pipeline set state ipc msg */ - #define SOF_IPC4_GLB_PIPE_STATE_ID_SHIFT 16 - #define SOF_IPC4_GLB_PIPE_STATE_ID_MASK GENMASK(23, 16) -@@ -536,12 +540,63 @@ enum sof_ipc4_notification_type { - SOF_IPC4_NOTIFY_TYPE_LAST, - }; - -+enum sof_ipc4_resource_type { -+ SOF_IPC4_MODULE_INSTANCE, -+ SOF_IPC4_PIPELINE, -+ SOF_IPC4_GATEWAY, -+ SOF_IPC4_EDF_TASK, -+ SOF_IPC4_INVALID_RESOURCE_TYPE, -+}; -+ -+enum sof_ipc4_event_type { -+ /* Underrun detected by the Mixer */ -+ SOF_IPC4_MIXER_UNDERRUN_DETECTED = 1, -+ /* Error caught during data processing */ -+ SOF_IPC4_PROCESS_DATA_ERROR = 3, -+ /* Underrun detected by gateway. */ -+ SOF_IPC4_GATEWAY_UNDERRUN_DETECTED = 6, -+ /* Overrun detected by gateway */ -+ SOF_IPC4_GATEWAY_OVERRUN_DETECTED, -+}; -+ -+/** -+ * struct sof_ipc4_process_data_error_event_data - process data error event payload -+ * @error_code: Error code returned by data processing function -+ */ -+struct sof_ipc4_process_data_error_event_data { -+ uint32_t error_code; -+}; -+ -+/** -+ * struct sof_ipc4_mixer_underrun_event_data - mixer underrun event payload -+ * @eos_flag: Indicates EndOfStream -+ * @data_mixed: Data processed by module (in bytes) -+ * @expected_data_mixed: Expected data to be processed (in bytes) -+ */ -+struct sof_ipc4_mixer_underrun_event_data { -+ uint32_t eos_flag; -+ uint32_t data_mixed; -+ uint32_t expected_data_mixed; -+}; -+ -+/** -+ * union sof_ipc4_resource_event_data - resource event specific payload -+ * @dws: Raw event data payload as six dwords -+ * @process_data_error: SOF_IPC4_PROCESS_DATA_ERROR payload -+ * @mixer_underrun: SOF_IPC4_MIXER_UNDERRUN_DETECTED payload -+ */ -+union sof_ipc4_resource_event_data { -+ uint32_t dws[6]; -+ struct sof_ipc4_process_data_error_event_data process_data_error; -+ struct sof_ipc4_mixer_underrun_event_data mixer_underrun; -+}; -+ - struct sof_ipc4_notify_resource_data { - uint32_t resource_type; - uint32_t resource_id; - uint32_t event_type; - uint32_t reserved; -- uint32_t data[6]; -+ union sof_ipc4_resource_event_data data; - } __packed __aligned(4); - - #define SOF_IPC4_DEBUG_DESCRIPTOR_SIZE 12 /* 3 x u32 */ -@@ -654,11 +709,81 @@ enum sof_ipc4_mod_init_ext_obj_id { - SOF_IPC4_MOD_INIT_DATA_ID_MAX = SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA, - }; - --/* DP module memory configuration data object for ext_init object array */ -+/* DP module memory configuration data object for object array */ - struct sof_ipc4_mod_init_ext_dp_memory_data { -+ u32 domain_id; /* userspace domain ID */ -+ u32 stack_bytes; /* required stack size in bytes */ -+ u32 heap_bytes; /* required heap size in bytes */ -+} __packed __aligned(4); -+ -+/* -+ * This set of macros are very similar to the set above, but these are -+ * for building payload to SOF_IPC4_GLB_CREATE_PIPELINE message. -+ * -+ * Macros for creating struct sof_ipc4_glb_pipe_payload payload with -+ * its associated data. struct sof_ipc4_glb_pipe_payload should be the -+ * first piece of payload following SOF_IPC4_GLB_CREATE_PIPELINE msg, -+ * and its existence is indicated with SOF_IPC4_GLB_PIPE_PAYLOAD bit. -+ * -+ * The macros below apply to sof_ipc4_glb_pipe_payload.word0 -+ */ -+#define SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS_SHIFT 0 -+#define SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS_MASK GENMASK(23, 0) -+#define SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS(x) ((x) << SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS_SHIFT) -+ -+#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_SHIFT 24 -+#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_MASK BIT(24) -+#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_SHIFT) -+ -+struct sof_ipc4_glb_pipe_payload { -+ u32 word0; -+ u32 rsvd1; -+ u32 rsvd2; -+} __packed __aligned(4); -+ -+/* -+ * SOF_IPC4_GLB_CREATE_PIPELINE payload may be followed by arbitrary -+ * number of object array objects. SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY-bit -+ * indicates that an array object follows struct -+ * sof_ipc4_glb_pipe_payload. -+ * -+ * The object header's SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST-bit in struct -+ * sof_ipc4_glb_pipe_ext_object indicates if the array is continued -+ * with another object. The header has also fields to identify the -+ * object, SOF_IPC4_GLB_PIPE_EXT_OBJ_ID, and to indicate the object's -+ * size in 32-bit words, SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS, not -+ * including the header itself. -+ * -+ * The macros below apply to sof_ipc4_glb_pipe_ext_object.header -+ */ -+#define SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_SHIFT 0 -+#define SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_MASK BIT(0) -+#define SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_SHIFT) -+ -+#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ID_SHIFT 1 -+#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ID_MASK GENMASK(15, 1) -+#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ID(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_ID_SHIFT) -+ -+#define SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS_SHIFT 16 -+#define SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS_MASK GENMASK(31, 16) -+#define SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS_SHIFT) -+ -+struct sof_ipc4_glb_pipe_ext_object { -+ u32 header; -+ u32 data[]; -+} __packed __aligned(4); -+ -+enum sof_ipc4_glb_pipe_ext_obj_id { -+ SOF_IPC4_GLB_PIPE_DATA_ID_INVALID = 0, -+ SOF_IPC4_GLB_PIPE_DATA_ID_MEM_DATA, -+ SOF_IPC4_GLB_PIPE_DATA_ID_MAX = SOF_IPC4_GLB_PIPE_DATA_ID_MEM_DATA, -+}; -+ -+/* Pipeline memory configuration data object for ext_init object array */ -+struct sof_ipc4_glb_pipe_ext_obj_memory_data { - u32 domain_id; /* userspace domain ID */ -- u32 stack_bytes; /* stack size in bytes, 0 means default size */ -- u32 heap_bytes; /* stack size in bytes, 0 means default size */ -+ u32 stack_bytes; /* stack size in bytes */ -+ u32 heap_bytes; /* heap size in bytes */ - } __packed __aligned(4); - - /** @}*/ -diff --git a/include/sound/tas2781-dsp.h b/include/sound/tas2781-dsp.h ---- a/include/sound/tas2781-dsp.h -+++ b/include/sound/tas2781-dsp.h -@@ -201,6 +201,11 @@ struct tasdevice_rca { - int ncfgs; - struct tasdevice_config_info **cfg_info; - int profile_cfg_id; -+ /* -+ * Used among SmartAMP for PDM microphone recording or IV data -+ * capture. -+ */ -+ int capture_profile_id; - /* - * Since version 0x105, the keyword 'init' was introduced into the - * profile, which is used for chip initialization, particularly to -@@ -222,7 +227,7 @@ void tasdevice_calbin_remove(void *context); - int tasdevice_select_tuningprm_cfg(void *context, int prm, - int cfg_no, int rca_conf_no); - int tasdevice_prmg_load(void *context, int prm_no); --void tasdevice_tuning_switch(void *context, int state); -+void tasdevice_tuning_switch(void *context, int state, bool is_cap); - int tas2781_load_calibration(void *context, char *file_name, - unsigned short i); - -diff --git a/include/sound/tlv320aic32x4.h b/include/sound/tlv320aic32x4.h -deleted file mode 100644 ---- a/include/sound/tlv320aic32x4.h -+++ /dev/null -@@ -1,43 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * tlv320aic32x4.h -- TLV320AIC32X4 Soc Audio driver platform data -- * -- * Copyright 2011 Vista Silicon S.L. -- * -- * Author: Javier Martin -- */ -- --#ifndef _AIC32X4_PDATA_H --#define _AIC32X4_PDATA_H -- --#define AIC32X4_PWR_MICBIAS_2075_LDOIN 0x00000001 --#define AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE 0x00000002 --#define AIC32X4_PWR_AIC32X4_LDO_ENABLE 0x00000004 --#define AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36 0x00000008 --#define AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED 0x00000010 -- --#define AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K 0x00000001 --#define AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K 0x00000002 -- --/* GPIO API */ --#define AIC32X4_MFPX_DEFAULT_VALUE 0xff -- --#define AIC32X4_MFP1_DIN_DISABLED 0 --#define AIC32X4_MFP1_DIN_ENABLED 0x2 --#define AIC32X4_MFP1_GPIO_IN 0x4 -- --#define AIC32X4_MFP2_GPIO_OUT_LOW 0x0 --#define AIC32X4_MFP2_GPIO_OUT_HIGH 0x1 -- --#define AIC32X4_MFP_GPIO_ENABLED 0x4 -- --#define AIC32X4_MFP5_GPIO_DISABLED 0x0 --#define AIC32X4_MFP5_GPIO_INPUT 0x8 --#define AIC32X4_MFP5_GPIO_OUTPUT 0xc --#define AIC32X4_MFP5_GPIO_OUT_LOW 0x0 --#define AIC32X4_MFP5_GPIO_OUT_HIGH 0x1 -- --struct aic32x4_setup_data { -- unsigned int gpio_func[5]; --}; --#endif -diff --git a/include/sound/wm8904.h b/include/sound/wm8904.h ---- a/include/sound/wm8904.h -+++ b/include/sound/wm8904.h -@@ -116,7 +116,7 @@ - #define WM8904_DRC_REGS 4 - #define WM8904_EQ_REGS 24 - --/** -+/* - * DRC configurations are specified with a label and a set of register - * values to write (the enable bits will be ignored). At runtime an - * enumerated control will be presented for each DRC block allowing -@@ -131,7 +131,7 @@ struct wm8904_drc_cfg { - u16 regs[WM8904_DRC_REGS]; - }; - --/** -+/* - * ReTune Mobile configurations are specified with a label, sample - * rate and set of values to write (the enable bits will be ignored). - * diff --git a/include/uapi/sound/asequencer.h b/include/uapi/sound/asequencer.h --- a/include/uapi/sound/asequencer.h +++ b/include/uapi/sound/asequencer.h @@ -2445,649 +1470,6 @@ diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h * expected to be linear value (not logarithm) for audio signal level between 0.0 and +1.0. */ struct snd_firewire_motu_command_dsp_meter { -diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h -deleted file mode 100644 ---- a/include/uapi/sound/skl-tplg-interface.h -+++ /dev/null -@@ -1,168 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ --/* -- * skl-tplg-interface.h - Intel DSP FW private data interface -- * -- * Copyright (C) 2015 Intel Corp -- * Author: Jeeja KP -- * Nilofer, Samreen -- */ -- --#ifndef __HDA_TPLG_INTERFACE_H__ --#define __HDA_TPLG_INTERFACE_H__ -- --#include -- --/* -- * Default types range from 0~12. type can range from 0 to 0xff -- * SST types start at higher to avoid any overlapping in future -- */ --#define SKL_CONTROL_TYPE_BYTE_TLV 0x100 --#define SKL_CONTROL_TYPE_MIC_SELECT 0x102 --#define SKL_CONTROL_TYPE_MULTI_IO_SELECT 0x103 --#define SKL_CONTROL_TYPE_MULTI_IO_SELECT_DMIC 0x104 -- --#define HDA_SST_CFG_MAX 900 /* size of copier cfg*/ --#define MAX_IN_QUEUE 8 --#define MAX_OUT_QUEUE 8 -- --#define SKL_UUID_STR_SZ 40 --/* Event types goes here */ --/* Reserve event type 0 for no event handlers */ --enum skl_event_types { -- SKL_EVENT_NONE = 0, -- SKL_MIXER_EVENT, -- SKL_MUX_EVENT, -- SKL_VMIXER_EVENT, -- SKL_PGA_EVENT --}; -- --/** -- * enum skl_ch_cfg - channel configuration -- * -- * @SKL_CH_CFG_MONO: One channel only -- * @SKL_CH_CFG_STEREO: L & R -- * @SKL_CH_CFG_2_1: L, R & LFE -- * @SKL_CH_CFG_3_0: L, C & R -- * @SKL_CH_CFG_3_1: L, C, R & LFE -- * @SKL_CH_CFG_QUATRO: L, R, Ls & Rs -- * @SKL_CH_CFG_4_0: L, C, R & Cs -- * @SKL_CH_CFG_5_0: L, C, R, Ls & Rs -- * @SKL_CH_CFG_5_1: L, C, R, Ls, Rs & LFE -- * @SKL_CH_CFG_DUAL_MONO: One channel replicated in two -- * @SKL_CH_CFG_I2S_DUAL_STEREO_0: Stereo(L,R) in 4 slots, 1st stream:[ L, R, -, - ] -- * @SKL_CH_CFG_I2S_DUAL_STEREO_1: Stereo(L,R) in 4 slots, 2nd stream:[ -, -, L, R ] -- * @SKL_CH_CFG_INVALID: Invalid -- */ --enum skl_ch_cfg { -- SKL_CH_CFG_MONO = 0, -- SKL_CH_CFG_STEREO = 1, -- SKL_CH_CFG_2_1 = 2, -- SKL_CH_CFG_3_0 = 3, -- SKL_CH_CFG_3_1 = 4, -- SKL_CH_CFG_QUATRO = 5, -- SKL_CH_CFG_4_0 = 6, -- SKL_CH_CFG_5_0 = 7, -- SKL_CH_CFG_5_1 = 8, -- SKL_CH_CFG_DUAL_MONO = 9, -- SKL_CH_CFG_I2S_DUAL_STEREO_0 = 10, -- SKL_CH_CFG_I2S_DUAL_STEREO_1 = 11, -- SKL_CH_CFG_7_1 = 12, -- SKL_CH_CFG_4_CHANNEL = SKL_CH_CFG_7_1, -- SKL_CH_CFG_INVALID --}; -- --enum skl_module_type { -- SKL_MODULE_TYPE_MIXER = 0, -- SKL_MODULE_TYPE_COPIER, -- SKL_MODULE_TYPE_UPDWMIX, -- SKL_MODULE_TYPE_SRCINT, -- SKL_MODULE_TYPE_ALGO, -- SKL_MODULE_TYPE_BASE_OUTFMT, -- SKL_MODULE_TYPE_KPB, -- SKL_MODULE_TYPE_MIC_SELECT, --}; -- --enum skl_core_affinity { -- SKL_AFFINITY_CORE_0 = 0, -- SKL_AFFINITY_CORE_1, -- SKL_AFFINITY_CORE_MAX --}; -- --enum skl_pipe_conn_type { -- SKL_PIPE_CONN_TYPE_NONE = 0, -- SKL_PIPE_CONN_TYPE_FE, -- SKL_PIPE_CONN_TYPE_BE --}; -- --enum skl_hw_conn_type { -- SKL_CONN_NONE = 0, -- SKL_CONN_SOURCE = 1, -- SKL_CONN_SINK = 2 --}; -- --enum skl_dev_type { -- SKL_DEVICE_BT = 0x0, -- SKL_DEVICE_DMIC = 0x1, -- SKL_DEVICE_I2S = 0x2, -- SKL_DEVICE_SLIMBUS = 0x3, -- SKL_DEVICE_HDALINK = 0x4, -- SKL_DEVICE_HDAHOST = 0x5, -- SKL_DEVICE_NONE --}; -- --/** -- * enum skl_interleaving - interleaving style -- * -- * @SKL_INTERLEAVING_PER_CHANNEL: [s1_ch1...s1_chN,...,sM_ch1...sM_chN] -- * @SKL_INTERLEAVING_PER_SAMPLE: [s1_ch1...sM_ch1,...,s1_chN...sM_chN] -- */ --enum skl_interleaving { -- SKL_INTERLEAVING_PER_CHANNEL = 0, -- SKL_INTERLEAVING_PER_SAMPLE = 1, --}; -- --enum skl_sample_type { -- SKL_SAMPLE_TYPE_INT_MSB = 0, -- SKL_SAMPLE_TYPE_INT_LSB = 1, -- SKL_SAMPLE_TYPE_INT_SIGNED = 2, -- SKL_SAMPLE_TYPE_INT_UNSIGNED = 3, -- SKL_SAMPLE_TYPE_FLOAT = 4 --}; -- --enum module_pin_type { -- /* All pins of the module takes same PCM inputs or outputs -- * e.g. mixout -- */ -- SKL_PIN_TYPE_HOMOGENEOUS, -- /* All pins of the module takes different PCM inputs or outputs -- * e.g mux -- */ -- SKL_PIN_TYPE_HETEROGENEOUS, --}; -- --enum skl_module_param_type { -- SKL_PARAM_DEFAULT = 0, -- SKL_PARAM_INIT, -- SKL_PARAM_SET, -- SKL_PARAM_BIND --}; -- --struct skl_dfw_algo_data { -- __u32 set_params:2; -- __u32 rsvd:30; -- __u32 param_id; -- __u32 max; -- char params[]; --} __packed; -- --enum skl_tkn_dir { -- SKL_DIR_IN, -- SKL_DIR_OUT --}; -- --enum skl_tuple_type { -- SKL_TYPE_TUPLE, -- SKL_TYPE_DATA --}; -- --#endif -diff --git a/include/uapi/sound/snd_ar_tokens.h b/include/uapi/sound/snd_ar_tokens.h ---- a/include/uapi/sound/snd_ar_tokens.h -+++ b/include/uapi/sound/snd_ar_tokens.h -@@ -58,7 +58,7 @@ enum ar_event_types { - #define SND_SOC_AR_TPLG_FE_BE_GRAPH_CTL_MIX 256 - #define SND_SOC_AR_TPLG_VOL_CTL 257 - --/** -+/* - * %AR_TKN_U32_SUB_GRAPH_INSTANCE_ID: Sub Graph Instance Id - * - * %AR_TKN_U32_SUB_GRAPH_PERF_MODE: Performance mode of subgraph -@@ -168,6 +168,60 @@ enum ar_event_types { - * LOG_WAIT = 0, - * LOG_IMMEDIATELY = 1 - * -+ * %AR_TKN_U16_MODULE_SYNC_SRC: Frame sync source -+ * AR_AUDIO_IF_SYNC_SRC_EXTERNAL = 0, -+ * AR_AUDIO_IF_SYNC_SRC_INTERNAL = 1 -+ * -+ * %AR_TKN_U16_MODULE_CTRL_DATA_OUT_ENABLE: Enable data-out tri-state control -+ * AR_AUDIO_IF_CTRL_DATA_OE_DISABLE = 0, -+ * AR_AUDIO_IF_CTRL_DATA_OE_ENABLE = 1 -+ * -+ * %AR_TKN_U32_MODULE_SLOT_MASK: Active TDM slot bitmask -+ * -+ * %AR_TKN_U16_MODULE_NSLOTS_PER_FRAME: Number of slots per TDM frame -+ * -+ * %AR_TKN_U16_MODULE_SLOT_WIDTH: Slot width in bits (16 or 32) -+ * -+ * %AR_TKN_U16_MODULE_SYNC_MODE: Frame sync mode -+ * AR_AUDIO_IF_FRAME_SYNC_MODE_SHORT = 0, -+ * AR_AUDIO_IF_FRAME_SYNC_MODE_ONE_SLOT = 1, -+ * AR_AUDIO_IF_FRAME_SYNC_MODE_LONG = 2 -+ * -+ * %AR_TKN_U16_MODULE_CTRL_INVERT_SYNC_PULSE: Invert frame sync pulse polarity -+ * AR_AUDIO_IF_SYNC_NORMAL = 0, -+ * AR_AUDIO_IF_SYNC_INVERTED = 1 -+ * -+ * %AR_TKN_U16_MODULE_CTRL_SYNC_DATA_DELAY: Data delay relative to frame sync -+ * AR_AUDIO_IF_DATA_DELAY_NONE = 0, -+ * AR_AUDIO_IF_DATA_DELAY_1_CYCLE = 1, -+ * AR_AUDIO_IF_DATA_DELAY_2_CYCLE = 2 -+ * -+ * %AR_TKN_U16_MODULE_INTF_MODE: Audio IF interface mode -+ * AR_AUDIO_IF_INTF_MODE_TDM = 0, -+ * AR_AUDIO_IF_INTF_MODE_PCM = 1, -+ * AR_AUDIO_IF_INTF_MODE_I2S = 2 -+ * -+ * %AR_TKN_U16_MODULE_QAIF_TYPE: QAIF hardware port type index -+ * AR_AUDIO_IF_QAIF = 0, -+ * AR_AUDIO_IF_QAIF_VA = 1 -+ * -+ * %AR_TKN_U32_MODULE_ACTIVE_LANE_MASK: Active lane bitmask for multi-lane -+ * -+ * %AR_TKN_U32_MODULE_FRAME_SYNC_RATE: Frame sync rate in Hz -+ * -+ * %AR_TKN_U16_MODULE_BIT_CLK_TYPE: Bit clock type -+ * AR_AUDIO_IF_BIT_CLK_INTERNAL = 0, -+ * AR_AUDIO_IF_BIT_CLK_EXTERNAL = 1, -+ * AR_AUDIO_IF_BIT_CLK_SKIP = 2 -+ * -+ * %AR_TKN_U8_MODULE_INV_INT_BIT_CLK: Invert internal bit clock -+ * AR_AUDIO_IF_CLK_NORMAL = 0, -+ * AR_AUDIO_IF_CLK_INVERTED = 1 -+ * -+ * %AR_TKN_U8_MODULE_INV_EXT_BIT_CLK: Invert external bit clock -+ * AR_AUDIO_IF_CLK_NORMAL = 0, -+ * AR_AUDIO_IF_CLK_INVERTED = 1 -+ * - * %AR_TKN_DAI_INDEX: dai index - * - */ -@@ -240,6 +294,45 @@ enum ar_event_types { - #define AR_TKN_U32_MODULE_LOG_TAP_POINT_ID 260 - #define AR_TKN_U32_MODULE_LOG_MODE 261 - -+#define AR_TKN_U16_MODULE_SYNC_SRC 262 -+#define AR_TKN_U16_MODULE_CTRL_DATA_OUT_ENABLE 263 -+#define AR_TKN_U32_MODULE_SLOT_MASK 264 -+#define AR_TKN_U16_MODULE_NSLOTS_PER_FRAME 265 -+#define AR_TKN_U16_MODULE_SLOT_WIDTH 266 -+#define AR_TKN_U16_MODULE_SYNC_MODE 267 -+#define AR_TKN_U16_MODULE_CTRL_INVERT_SYNC_PULSE 268 -+#define AR_TKN_U16_MODULE_CTRL_SYNC_DATA_DELAY 269 -+#define AR_TKN_U16_MODULE_INTF_MODE 270 -+#define AR_TKN_U16_MODULE_QAIF_TYPE 271 -+#define AR_TKN_U32_MODULE_ACTIVE_LANE_MASK 272 -+#define AR_TKN_U32_MODULE_FRAME_SYNC_RATE 273 -+#define AR_TKN_U16_MODULE_BIT_CLK_TYPE 274 -+#define AR_TKN_U8_MODULE_INV_INT_BIT_CLK 275 -+#define AR_TKN_U8_MODULE_INV_EXT_BIT_CLK 276 -+ -+#define AR_AUDIO_IF_SYNC_SRC_EXTERNAL 0 -+#define AR_AUDIO_IF_SYNC_SRC_INTERNAL 1 -+#define AR_AUDIO_IF_CTRL_DATA_OE_DISABLE 0 -+#define AR_AUDIO_IF_CTRL_DATA_OE_ENABLE 1 -+#define AR_AUDIO_IF_INTF_MODE_TDM 0 -+#define AR_AUDIO_IF_INTF_MODE_PCM 1 -+#define AR_AUDIO_IF_INTF_MODE_I2S 2 -+#define AR_AUDIO_IF_QAIF 0 -+#define AR_AUDIO_IF_QAIF_VA 1 -+#define AR_AUDIO_IF_FRAME_SYNC_MODE_SHORT 0 -+#define AR_AUDIO_IF_FRAME_SYNC_MODE_ONE_SLOT 1 -+#define AR_AUDIO_IF_FRAME_SYNC_MODE_LONG 2 -+#define AR_AUDIO_IF_SYNC_NORMAL 0 -+#define AR_AUDIO_IF_SYNC_INVERTED 1 -+#define AR_AUDIO_IF_DATA_DELAY_NONE 0 -+#define AR_AUDIO_IF_DATA_DELAY_1_CYCLE 1 -+#define AR_AUDIO_IF_DATA_DELAY_2_CYCLE 2 -+#define AR_AUDIO_IF_BIT_CLK_INTERNAL 0 -+#define AR_AUDIO_IF_BIT_CLK_EXTERNAL 1 -+#define AR_AUDIO_IF_BIT_CLK_SKIP 2 -+#define AR_AUDIO_IF_CLK_NORMAL 0 -+#define AR_AUDIO_IF_CLK_INVERTED 1 -+ - #define SND_SOC_AR_TPLG_MODULE_CFG_TYPE 0x01001006 - struct audioreach_module_priv_data { - __le32 size; /* size in bytes of the array, including all elements */ -diff --git a/include/uapi/sound/snd_sst_tokens.h b/include/uapi/sound/snd_sst_tokens.h -deleted file mode 100644 ---- a/include/uapi/sound/snd_sst_tokens.h -+++ /dev/null -@@ -1,324 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ --/* -- * snd_sst_tokens.h - Intel SST tokens definition -- * -- * Copyright (C) 2016 Intel Corp -- * Author: Shreyas NC -- */ --#ifndef __SND_SST_TOKENS_H__ --#define __SND_SST_TOKENS_H__ -- --/** -- * %SKL_TKN_UUID: Module UUID -- * -- * %SKL_TKN_U8_BLOCK_TYPE: Type of the private data block.Can be: -- * tuples, bytes, short and words -- * -- * %SKL_TKN_U8_IN_PIN_TYPE: Input pin type, -- * homogenous=0, heterogenous=1 -- * -- * %SKL_TKN_U8_OUT_PIN_TYPE: Output pin type, -- * homogenous=0, heterogenous=1 -- * %SKL_TKN_U8_DYN_IN_PIN: Configure Input pin dynamically -- * if true -- * -- * %SKL_TKN_U8_DYN_OUT_PIN: Configure Output pin dynamically -- * if true -- * -- * %SKL_TKN_U8_IN_QUEUE_COUNT: Store the number of Input pins -- * -- * %SKL_TKN_U8_OUT_QUEUE_COUNT: Store the number of Output pins -- * -- * %SKL_TKN_U8_TIME_SLOT: TDM slot number -- * -- * %SKL_TKN_U8_CORE_ID: Stores module affinity value.Can take -- * the values: -- * SKL_AFFINITY_CORE_0 = 0, -- * SKL_AFFINITY_CORE_1, -- * SKL_AFFINITY_CORE_MAX -- * -- * %SKL_TKN_U8_MOD_TYPE: Module type value. -- * -- * %SKL_TKN_U8_CONN_TYPE: Module connection type can be a FE, -- * BE or NONE as defined : -- * SKL_PIPE_CONN_TYPE_NONE = 0, -- * SKL_PIPE_CONN_TYPE_FE = 1 (HOST_DMA) -- * SKL_PIPE_CONN_TYPE_BE = 2 (LINK_DMA) -- * -- * %SKL_TKN_U8_DEV_TYPE: Type of device to which the module is -- * connected -- * Can take the values: -- * SKL_DEVICE_BT = 0x0, -- * SKL_DEVICE_DMIC = 0x1, -- * SKL_DEVICE_I2S = 0x2, -- * SKL_DEVICE_SLIMBUS = 0x3, -- * SKL_DEVICE_HDALINK = 0x4, -- * SKL_DEVICE_HDAHOST = 0x5, -- * SKL_DEVICE_NONE -- * -- * %SKL_TKN_U8_HW_CONN_TYPE: Connection type of the HW to which the -- * module is connected -- * SKL_CONN_NONE = 0, -- * SKL_CONN_SOURCE = 1, -- * SKL_CONN_SINK = 2 -- * -- * %SKL_TKN_U16_PIN_INST_ID: Stores the pin instance id -- * -- * %SKL_TKN_U16_MOD_INST_ID: Stores the mdule instance id -- * -- * %SKL_TKN_U32_MAX_MCPS: Module max mcps value -- * -- * %SKL_TKN_U32_MEM_PAGES: Module resource pages -- * -- * %SKL_TKN_U32_OBS: Stores Output Buffer size -- * -- * %SKL_TKN_U32_IBS: Stores input buffer size -- * -- * %SKL_TKN_U32_VBUS_ID: Module VBUS_ID. PDM=0, SSP0=0, -- * SSP1=1,SSP2=2, -- * SSP3=3, SSP4=4, -- * SSP5=5, SSP6=6,INVALID -- * -- * %SKL_TKN_U32_PARAMS_FIXUP: Module Params fixup mask -- * %SKL_TKN_U32_CONVERTER: Module params converter mask -- * %SKL_TKN_U32_PIPE_ID: Stores the pipe id -- * -- * %SKL_TKN_U32_PIPE_CONN_TYPE: Type of the token to which the pipe is -- * connected to. It can be -- * SKL_PIPE_CONN_TYPE_NONE = 0, -- * SKL_PIPE_CONN_TYPE_FE = 1 (HOST_DMA), -- * SKL_PIPE_CONN_TYPE_BE = 2 (LINK_DMA), -- * -- * %SKL_TKN_U32_PIPE_PRIORITY: Pipe priority value -- * %SKL_TKN_U32_PIPE_MEM_PGS: Pipe resource pages -- * -- * %SKL_TKN_U32_DIR_PIN_COUNT: Value for the direction to set input/output -- * formats and the pin count. -- * The first 4 bits have the direction -- * value and the next 4 have -- * the pin count value. -- * SKL_DIR_IN = 0, SKL_DIR_OUT = 1. -- * The input and output formats -- * share the same set of tokens -- * with the distinction between input -- * and output made by reading direction -- * token. -- * -- * %SKL_TKN_U32_FMT_CH: Supported channel count -- * -- * %SKL_TKN_U32_FMT_FREQ: Supported frequency/sample rate -- * -- * %SKL_TKN_U32_FMT_BIT_DEPTH: Supported container size -- * -- * %SKL_TKN_U32_FMT_SAMPLE_SIZE:Number of samples in the container -- * -- * %SKL_TKN_U32_FMT_CH_CONFIG: Supported channel configurations for the -- * input/output. -- * -- * %SKL_TKN_U32_FMT_INTERLEAVE: Interleaving style which can be per -- * channel or per sample. The values can be : -- * SKL_INTERLEAVING_PER_CHANNEL = 0, -- * SKL_INTERLEAVING_PER_SAMPLE = 1, -- * -- * %SKL_TKN_U32_FMT_SAMPLE_TYPE: -- * Specifies the sample type. Can take the -- * values: SKL_SAMPLE_TYPE_INT_MSB = 0, -- * SKL_SAMPLE_TYPE_INT_LSB = 1, -- * SKL_SAMPLE_TYPE_INT_SIGNED = 2, -- * SKL_SAMPLE_TYPE_INT_UNSIGNED = 3, -- * SKL_SAMPLE_TYPE_FLOAT = 4 -- * -- * %SKL_TKN_U32_CH_MAP: Channel map values -- * %SKL_TKN_U32_MOD_SET_PARAMS: It can take these values: -- * SKL_PARAM_DEFAULT, SKL_PARAM_INIT, -- * SKL_PARAM_SET, SKL_PARAM_BIND -- * -- * %SKL_TKN_U32_MOD_PARAM_ID: ID of the module params -- * -- * %SKL_TKN_U32_CAPS_SET_PARAMS: -- * Set params value -- * -- * %SKL_TKN_U32_CAPS_PARAMS_ID: Params ID -- * -- * %SKL_TKN_U32_CAPS_SIZE: Caps size -- * -- * %SKL_TKN_U32_PROC_DOMAIN: Specify processing domain -- * -- * %SKL_TKN_U32_LIB_COUNT: Specifies the number of libraries -- * -- * %SKL_TKN_STR_LIB_NAME: Specifies the library name -- * -- * %SKL_TKN_U32_PMODE: Specifies the power mode for pipe -- * -- * %SKL_TKL_U32_D0I3_CAPS: Specifies the D0i3 capability for module -- * -- * %SKL_TKN_U32_DMA_BUF_SIZE: DMA buffer size in millisec -- * -- * %SKL_TKN_U32_PIPE_DIR: Specifies pipe direction. Can be -- * playback/capture. -- * -- * %SKL_TKN_U32_NUM_CONFIGS: Number of pipe configs -- * -- * %SKL_TKN_U32_PATH_MEM_PGS: Size of memory (in pages) required for pipeline -- * and its data -- * -- * %SKL_TKN_U32_PIPE_CONFIG_ID: Config id for the modules in the pipe -- * and PCM params supported by that pipe -- * config. This is used as index to fill -- * up the pipe config and module config -- * structure. -- * -- * %SKL_TKN_U32_CFG_FREQ: -- * %SKL_TKN_U8_CFG_CHAN: -- * %SKL_TKN_U8_CFG_BPS: PCM params (freq, channels, bits per sample) -- * supported for each of the pipe configs. -- * -- * %SKL_TKN_CFG_MOD_RES_ID: Module's resource index for each of the -- * pipe config -- * -- * %SKL_TKN_CFG_MOD_FMT_ID: Module's interface index for each of the -- * pipe config -- * -- * %SKL_TKN_U8_NUM_MOD: Number of modules in the manifest -- * -- * %SKL_TKN_MM_U8_MOD_IDX: Current index of the module in the manifest -- * -- * %SKL_TKN_MM_U8_NUM_RES: Number of resources for the module -- * -- * %SKL_TKN_MM_U8_NUM_INTF: Number of interfaces for the module -- * -- * %SKL_TKN_MM_U32_RES_ID: Resource index for the resource info to -- * be filled into. -- * A module can support multiple resource -- * configuration and is represnted as a -- * resource table. This index is used to -- * fill information into appropriate index. -- * -- * %SKL_TKN_MM_U32_CPS: DSP cycles per second -- * -- * %SKL_TKN_MM_U32_DMA_SIZE: Allocated buffer size for gateway DMA -- * -- * %SKL_TKN_MM_U32_CPC: DSP cycles allocated per frame -- * -- * %SKL_TKN_MM_U32_RES_PIN_ID: Resource pin index in the module -- * -- * %SKL_TKN_MM_U32_INTF_PIN_ID: Interface index in the module -- * -- * %SKL_TKN_MM_U32_PIN_BUF: Buffer size of the module pin -- * -- * %SKL_TKN_MM_U32_FMT_ID: Format index for each of the interface/ -- * format information to be filled into. -- * -- * %SKL_TKN_MM_U32_NUM_IN_FMT: Number of input formats -- * %SKL_TKN_MM_U32_NUM_OUT_FMT: Number of output formats -- * -- * %SKL_TKN_U32_ASTATE_IDX: Table Index for the A-State entry to be filled -- * with kcps and clock source -- * -- * %SKL_TKN_U32_ASTATE_COUNT: Number of valid entries in A-State table -- * -- * %SKL_TKN_U32_ASTATE_KCPS: Specifies the core load threshold (in kilo -- * cycles per second) below which DSP is clocked -- * from source specified by clock source. -- * -- * %SKL_TKN_U32_ASTATE_CLK_SRC: Clock source for A-State entry -- * -- * %SKL_TKN_U32_FMT_CFG_IDX: Format config index -- * -- * module_id and loadable flags dont have tokens as these values will be -- * read from the DSP FW manifest -- * -- * Tokens defined can be used either in the manifest or widget private data. -- * -- * SKL_TKN_MM is used as a suffix for all tokens that represent -- * module data in the manifest. -- */ --enum SKL_TKNS { -- SKL_TKN_UUID = 1, -- SKL_TKN_U8_NUM_BLOCKS, -- SKL_TKN_U8_BLOCK_TYPE, -- SKL_TKN_U8_IN_PIN_TYPE, -- SKL_TKN_U8_OUT_PIN_TYPE, -- SKL_TKN_U8_DYN_IN_PIN, -- SKL_TKN_U8_DYN_OUT_PIN, -- SKL_TKN_U8_IN_QUEUE_COUNT, -- SKL_TKN_U8_OUT_QUEUE_COUNT, -- SKL_TKN_U8_TIME_SLOT, -- SKL_TKN_U8_CORE_ID, -- SKL_TKN_U8_MOD_TYPE, -- SKL_TKN_U8_CONN_TYPE, -- SKL_TKN_U8_DEV_TYPE, -- SKL_TKN_U8_HW_CONN_TYPE, -- SKL_TKN_U16_MOD_INST_ID, -- SKL_TKN_U16_BLOCK_SIZE, -- SKL_TKN_U32_MAX_MCPS, -- SKL_TKN_U32_MEM_PAGES, -- SKL_TKN_U32_OBS, -- SKL_TKN_U32_IBS, -- SKL_TKN_U32_VBUS_ID, -- SKL_TKN_U32_PARAMS_FIXUP, -- SKL_TKN_U32_CONVERTER, -- SKL_TKN_U32_PIPE_ID, -- SKL_TKN_U32_PIPE_CONN_TYPE, -- SKL_TKN_U32_PIPE_PRIORITY, -- SKL_TKN_U32_PIPE_MEM_PGS, -- SKL_TKN_U32_DIR_PIN_COUNT, -- SKL_TKN_U32_FMT_CH, -- SKL_TKN_U32_FMT_FREQ, -- SKL_TKN_U32_FMT_BIT_DEPTH, -- SKL_TKN_U32_FMT_SAMPLE_SIZE, -- SKL_TKN_U32_FMT_CH_CONFIG, -- SKL_TKN_U32_FMT_INTERLEAVE, -- SKL_TKN_U32_FMT_SAMPLE_TYPE, -- SKL_TKN_U32_FMT_CH_MAP, -- SKL_TKN_U32_PIN_MOD_ID, -- SKL_TKN_U32_PIN_INST_ID, -- SKL_TKN_U32_MOD_SET_PARAMS, -- SKL_TKN_U32_MOD_PARAM_ID, -- SKL_TKN_U32_CAPS_SET_PARAMS, -- SKL_TKN_U32_CAPS_PARAMS_ID, -- SKL_TKN_U32_CAPS_SIZE, -- SKL_TKN_U32_PROC_DOMAIN, -- SKL_TKN_U32_LIB_COUNT, -- SKL_TKN_STR_LIB_NAME, -- SKL_TKN_U32_PMODE, -- SKL_TKL_U32_D0I3_CAPS, /* Typo added at v4.10 */ -- SKL_TKN_U32_D0I3_CAPS = SKL_TKL_U32_D0I3_CAPS, -- SKL_TKN_U32_DMA_BUF_SIZE, -- -- SKL_TKN_U32_PIPE_DIRECTION, -- SKL_TKN_U32_PIPE_CONFIG_ID, -- SKL_TKN_U32_NUM_CONFIGS, -- SKL_TKN_U32_PATH_MEM_PGS, -- -- SKL_TKN_U32_CFG_FREQ, -- SKL_TKN_U8_CFG_CHAN, -- SKL_TKN_U8_CFG_BPS, -- SKL_TKN_CFG_MOD_RES_ID, -- SKL_TKN_CFG_MOD_FMT_ID, -- SKL_TKN_U8_NUM_MOD, -- -- SKL_TKN_MM_U8_MOD_IDX, -- SKL_TKN_MM_U8_NUM_RES, -- SKL_TKN_MM_U8_NUM_INTF, -- SKL_TKN_MM_U32_RES_ID, -- SKL_TKN_MM_U32_CPS, -- SKL_TKN_MM_U32_DMA_SIZE, -- SKL_TKN_MM_U32_CPC, -- SKL_TKN_MM_U32_RES_PIN_ID, -- SKL_TKN_MM_U32_INTF_PIN_ID, -- SKL_TKN_MM_U32_PIN_BUF, -- SKL_TKN_MM_U32_FMT_ID, -- SKL_TKN_MM_U32_NUM_IN_FMT, -- SKL_TKN_MM_U32_NUM_OUT_FMT, -- -- SKL_TKN_U32_ASTATE_IDX, -- SKL_TKN_U32_ASTATE_COUNT, -- SKL_TKN_U32_ASTATE_KCPS, -- SKL_TKN_U32_ASTATE_CLK_SRC, -- -- SKL_TKN_U32_FMT_CFG_IDX = 96, -- SKL_TKN_MAX = SKL_TKN_U32_FMT_CFG_IDX, --}; -- --#endif -diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h ---- a/include/uapi/sound/sof/tokens.h -+++ b/include/uapi/sound/sof/tokens.h -@@ -111,8 +111,8 @@ - - #define SOF_TKN_COMP_SCHED_DOMAIN 418 - #define SOF_TKN_COMP_DOMAIN_ID 419 --#define SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT 420 --#define SOF_TKN_COMP_STACK_BYTES_REQUIREMENT 421 -+#define SOF_TKN_COMP_STACK_BYTES_REQUIREMENT 420 -+#define SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT 421 - - /* SSP */ - #define SOF_TKN_INTEL_SSP_CLKS_CONTROL 500 -@@ -223,6 +223,7 @@ - #define SOF_TKN_AMD_ACPI2S_RATE 1700 - #define SOF_TKN_AMD_ACPI2S_CH 1701 - #define SOF_TKN_AMD_ACPI2S_TDM_MODE 1702 -+#define SOF_TKN_AMD_ACPI2S_FORMAT 1703 - - /* MICFIL PDM */ - #define SOF_TKN_IMX_MICFIL_RATE 2000 diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c --- a/sound/atmel/ac97c.c +++ b/sound/atmel/ac97c.c @@ -3567,15 +1949,7 @@ diff --git a/sound/core/init.c b/sound/core/init.c }; /** -@@ -584,12 +583,17 @@ EXPORT_SYMBOL_GPL(snd_card_disconnect_sync); - - static int snd_card_do_free(struct snd_card *card) - { -+ bool managed = card->managed; -+ - card->releasing = true; - #if IS_ENABLED(CONFIG_SND_MIXER_OSS) - if (snd_mixer_oss_notify_callback) +@@ -592,6 +591,9 @@ static int snd_card_do_free(struct snd_card *card) snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE); #endif snd_device_free_all(card); @@ -3585,16 +1959,7 @@ diff --git a/sound/core/init.c b/sound/core/init.c if (card->private_free) card->private_free(card); #ifdef CONFIG_SND_CTL_DEBUG -@@ -601,7 +605,7 @@ static int snd_card_do_free(struct snd_card *card) - } - if (card->release_completion) - complete(card->release_completion); -- if (!card->managed) -+ if (!managed) - kfree(card); - return 0; - } -@@ -722,7 +726,7 @@ static void snd_card_set_id_no_lock(struct snd_card *card, const char *src, +@@ -725,7 +727,7 @@ static void snd_card_set_id_no_lock(struct snd_card *card, const char *src, int len, loops; bool is_default = false; char *id; @@ -3603,7 +1968,7 @@ diff --git a/sound/core/init.c b/sound/core/init.c copy_valid_id_string(card, src, nid); id = card->id; -@@ -1031,21 +1035,44 @@ int __init snd_card_info_init(void) +@@ -1034,21 +1036,44 @@ int __init snd_card_info_init(void) * * Return: Zero otherwise a negative error code. */ @@ -4227,7 +2592,7 @@ diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c offset = vmf->pgoff << PAGE_SHIFT; dma_bytes = PAGE_ALIGN(runtime->dma_bytes); if (offset > dma_bytes - PAGE_SIZE) -@@ -4241,29 +4245,29 @@ static unsigned long snd_pcm_get_unmapped_area(struct file *file, +@@ -4256,29 +4260,29 @@ static unsigned long snd_pcm_get_unmapped_area(struct file *file, const struct file_operations snd_pcm_f_ops[2] = { { @@ -4512,41 +2877,7 @@ diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c * If the receiver client is a kernel client, the original event is * quoted in SNDRV_SEQ_EVENT_KERNEL_ERROR, since this requires no extra * kmalloc. -@@ -528,7 +540,7 @@ static int bounce_error_event(struct snd_seq_client *client, - struct snd_seq_event *event, - int err, int atomic, int hop) - { -- struct snd_seq_event bounce_ev; -+ struct snd_seq_event bounce_ev, quoted; - int result; - - if (client == NULL || -@@ -548,15 +560,19 @@ static int bounce_error_event(struct snd_seq_client *client, - * For user clients, send SNDRV_SEQ_EVENT_BOUNCE with the - * original event embedded as variable-length data. This - * avoids exposing data.quote.event (a kernel pointer) to -- * userspace. The variable-length path in snd_seq_event_dup() -- * copies the event data from data.ext.ptr into chained cells, -- * and snd_seq_expand_var_event() copies only the data content -- * -- never the pointer -- to userspace. -+ * userspace. Sanitise the embedded copy too - a queued -+ * variable-length event carries the address of its own -+ * extension cell, and the payload goes out verbatim. - */ -+ quoted = *event; -+ if (snd_seq_ev_is_variable("ed)) { -+ quoted.data.ext.len &= ~SNDRV_SEQ_EXT_MASK; -+ quoted.data.ext.ptr = NULL; -+ } - bounce_ev.type = SNDRV_SEQ_EVENT_BOUNCE; - bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE; - bounce_ev.data.ext.len = sizeof(struct snd_seq_event); -- bounce_ev.data.ext.ptr = (char *)event; -+ bounce_ev.data.ext.ptr = (char *)"ed; - } else { - /* - * For kernel clients, quote the event pointer directly. -@@ -720,10 +736,11 @@ static int __deliver_to_subscribers(struct snd_seq_client *client, +@@ -724,10 +736,11 @@ static int __deliver_to_subscribers(struct snd_seq_client *client, /* lock list */ if (atomic) @@ -4560,7 +2891,7 @@ diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c /* both ports ready? */ if (atomic_read(&subs->ref_count) != 2) continue; -@@ -744,7 +761,7 @@ static int __deliver_to_subscribers(struct snd_seq_client *client, +@@ -748,7 +761,7 @@ static int __deliver_to_subscribers(struct snd_seq_client *client, memcpy(event, &event_saved, saved_size); } if (atomic) @@ -4569,7 +2900,7 @@ diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c else up_read(&grp->list_mutex); memcpy(event, &event_saved, saved_size); -@@ -1941,7 +1958,7 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void *arg) +@@ -1945,7 +1958,7 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void *arg) { struct snd_seq_query_subs *subs = arg; struct snd_seq_port_subs_info *group; @@ -4578,7 +2909,7 @@ diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c int i; struct snd_seq_client *cptr __free(snd_seq_client) = -@@ -1968,15 +1985,15 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void *arg) +@@ -1972,15 +1985,15 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void *arg) /* search for the subscriber */ subs->num_subs = group->count; i = 0; @@ -4597,7 +2928,7 @@ diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c subs->addr = s->info.sender; } subs->flags = s->info.flags; -@@ -2532,19 +2549,19 @@ static void snd_seq_info_dump_subscribers(struct snd_info_buffer *buffer, +@@ -2536,19 +2549,19 @@ static void snd_seq_info_dump_subscribers(struct snd_info_buffer *buffer, struct snd_seq_port_subs_info *group, int is_src, char *msg) { @@ -4622,7 +2953,7 @@ diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c if (count++) snd_iprintf(buffer, ", "); snd_iprintf(buffer, "%d:%d", -@@ -2666,16 +2683,15 @@ void snd_seq_info_clients_read(struct snd_info_entry *entry, +@@ -2670,16 +2683,15 @@ void snd_seq_info_clients_read(struct snd_info_entry *entry, * REGISTRATION PART */ @@ -4659,159 +2990,6 @@ diff --git a/sound/core/seq/seq_clientmgr.h b/sound/core/seq/seq_clientmgr.h struct mutex ports_mutex; struct mutex ioctl_mutex; int convert32; /* convert 32->64bit */ -diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c ---- a/sound/core/seq/seq_midi.c -+++ b/sound/core/seq/seq_midi.c -@@ -42,9 +42,11 @@ struct seq_midisynth { - struct snd_rawmidi *rmidi; - int device; - int subdevice; -+ struct snd_rawmidi_substream __rcu *input_substream; -+ snd_use_lock_t input_use_lock; /* in-flight event_input users */ - struct snd_rawmidi_file input_rfile; -- spinlock_t output_lock; /* protects output_rfile publication */ - snd_use_lock_t output_use_lock; /* in-flight event_input users */ -+ struct snd_rawmidi_substream __rcu *output_substream; - struct snd_rawmidi_file output_rfile; - int seq_client; - int seq_port; -@@ -76,6 +78,14 @@ static void snd_midi_input_event(struct snd_rawmidi_substream *substream) - msynth = runtime->private_data; - if (msynth == NULL) - return; -+ -+ scoped_guard(rcu) { -+ if (rcu_dereference(msynth->input_substream) != substream) -+ return; -+ -+ snd_use_lock_use(&msynth->input_use_lock); -+ } -+ - memset(&ev, 0, sizeof(ev)); - while (runtime->avail > 0) { - res = snd_rawmidi_kernel_read(substream, buf, sizeof(buf)); -@@ -95,6 +105,8 @@ static void snd_midi_input_event(struct snd_rawmidi_substream *substream) - memset(&ev, 0, sizeof(ev)); - } - } -+ -+ snd_use_lock_free(&msynth->input_use_lock); - } - - static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, int count) -@@ -134,8 +146,8 @@ static int event_process_midi(struct snd_seq_event *ev, int direct, - if (snd_BUG_ON(!msynth)) - return -EINVAL; - -- scoped_guard(spinlock_irqsave, &msynth->output_lock) { -- substream = msynth->output_rfile.output; -+ scoped_guard(rcu) { -+ substream = rcu_dereference(msynth->output_substream); - if (!substream) - return -ENODEV; - snd_use_lock_use(&msynth->output_use_lock); -@@ -177,7 +189,7 @@ static int snd_seq_midisynth_new(struct seq_midisynth *msynth, - msynth->card = card; - msynth->device = device; - msynth->subdevice = subdevice; -- spin_lock_init(&msynth->output_lock); -+ snd_use_lock_init(&msynth->input_use_lock); - snd_use_lock_init(&msynth->output_use_lock); - return 0; - } -@@ -188,28 +200,31 @@ static int midisynth_subscribe(void *private_data, struct snd_seq_port_subscribe - int err; - struct seq_midisynth *msynth = private_data; - struct snd_rawmidi_runtime *runtime; -+ struct snd_rawmidi_file rfile = {}; - struct snd_rawmidi_params params; - - /* open midi port */ - err = snd_rawmidi_kernel_open(msynth->rmidi, msynth->subdevice, - SNDRV_RAWMIDI_LFLG_INPUT, -- &msynth->input_rfile); -+ &rfile); - if (err < 0) { - pr_debug("ALSA: seq_midi: midi input open failed!!!\n"); - return err; - } -- runtime = msynth->input_rfile.input->runtime; -+ runtime = rfile.input->runtime; - memset(¶ms, 0, sizeof(params)); - params.avail_min = 1; - params.buffer_size = input_buffer_size; -- err = snd_rawmidi_input_params(msynth->input_rfile.input, ¶ms); -+ err = snd_rawmidi_input_params(rfile.input, ¶ms); - if (err < 0) { -- snd_rawmidi_kernel_release(&msynth->input_rfile); -+ snd_rawmidi_kernel_release(&rfile); - return err; - } - snd_midi_event_reset_encode(msynth->parser); - runtime->event = snd_midi_input_event; - runtime->private_data = msynth; -+ msynth->input_rfile = rfile; -+ rcu_assign_pointer(msynth->input_substream, rfile.input); - snd_rawmidi_kernel_read(msynth->input_rfile.input, NULL, 0); - return 0; - } -@@ -219,10 +234,19 @@ static int midisynth_unsubscribe(void *private_data, struct snd_seq_port_subscri - { - int err; - struct seq_midisynth *msynth = private_data; -+ struct snd_rawmidi_file rfile; - -- if (snd_BUG_ON(!msynth->input_rfile.input)) -+ rcu_assign_pointer(msynth->input_substream, NULL); -+ synchronize_rcu(); -+ snd_use_lock_sync(&msynth->input_use_lock); -+ -+ rfile = msynth->input_rfile; -+ msynth->input_rfile = (struct snd_rawmidi_file){}; -+ -+ if (snd_BUG_ON(!rfile.input)) - return -EINVAL; -- err = snd_rawmidi_kernel_release(&msynth->input_rfile); -+ -+ err = snd_rawmidi_kernel_release(&rfile); - return err; - } - -@@ -252,8 +276,8 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info - return err; - } - snd_midi_event_reset_decode(msynth->parser); -- scoped_guard(spinlock_irqsave, &msynth->output_lock) -- msynth->output_rfile = rfile; -+ msynth->output_rfile = rfile; -+ rcu_assign_pointer(msynth->output_substream, rfile.output); - return 0; - } - -@@ -261,17 +285,16 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info - static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *info) - { - struct seq_midisynth *msynth = private_data; -- struct snd_rawmidi_file rfile = {}; -+ struct snd_rawmidi_file rfile; - -- scoped_guard(spinlock_irqsave, &msynth->output_lock) { -- rfile = msynth->output_rfile; -- msynth->output_rfile = (struct snd_rawmidi_file){}; -- } -+ rcu_assign_pointer(msynth->output_substream, NULL); -+ synchronize_rcu(); -+ snd_use_lock_sync(&msynth->output_use_lock); -+ rfile = msynth->output_rfile; -+ msynth->output_rfile = (struct snd_rawmidi_file){}; - - if (snd_BUG_ON(!rfile.output)) - return -EINVAL; -- -- snd_use_lock_sync(&msynth->output_use_lock); - snd_rawmidi_drain_output(rfile.output); - return snd_rawmidi_kernel_release(&rfile); - } diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c --- a/sound/core/seq/seq_ports.c +++ b/sound/core/seq/seq_ports.c @@ -7149,24 +5327,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 static const struct coef_fw alc256_fixup_set_coef_defaults_coefs[] = { WRITE_COEF(0x10, 0x0020), WRITE_COEF(0x24, 0x0000), WRITE_COEF(0x26, 0x0000), WRITE_COEF(0x29, 0x3000), -@@ -3524,6 +3579,16 @@ static void alc287_fixup_yoga9_14iap7_bass_spk_pin(struct hda_codec *codec, - } - } - -+static void alc_fixup_yoga_pro7_audio(struct hda_codec *codec, -+ const struct hda_fixup *fix, int action) -+{ -+ /* Reuse the DAC routing selected for ThinkPad X1 Gen7 */ -+ alc285_fixup_thinkpad_x1_gen7(codec, fix, action); -+ -+ /* Limit the internal mic boost to 0 or 1 to avoid noise */ -+ alc269_fixup_limit_int_mic_boost(codec, fix, action); -+} -+ - static void alc295_fixup_dell_inspiron_top_speakers(struct hda_codec *codec, - const struct hda_fixup *fix, int action) - { -@@ -3929,6 +3994,7 @@ enum { +@@ -3939,6 +3994,7 @@ enum { ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572, ALC269_FIXUP_ACER_AC700, ALC269_FIXUP_LIMIT_INT_MIC_BOOST, @@ -7174,7 +5335,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 ALC269VB_FIXUP_ASUS_ZENBOOK, ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A, ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE, -@@ -3997,6 +4063,7 @@ enum { +@@ -4007,6 +4063,7 @@ enum { ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, ALC295_FIXUP_DISABLE_DAC3, ALC285_FIXUP_SPEAKER2_TO_DAC1, @@ -7182,7 +5343,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 ALC285_FIXUP_ASUS_SPEAKER2_TO_DAC1, ALC285_FIXUP_ASUS_HEADSET_MIC, ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS, -@@ -4066,7 +4133,6 @@ enum { +@@ -4076,7 +4133,6 @@ enum { ALC294_FIXUP_ASUS_ALLY, ALC294_FIXUP_ASUS_ALLY_PINS, ALC294_FIXUP_ASUS_ALLY_VERBS, @@ -7190,7 +5351,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 ALC294_FIXUP_ASUS_HPE, ALC294_FIXUP_ASUS_COEF_1B, ALC294_FIXUP_ASUS_GX502_HP, -@@ -4091,6 +4157,8 @@ enum { +@@ -4101,6 +4157,8 @@ enum { ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS, ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS, ALC298_FIXUP_LG_GRAM_STYLE_14, @@ -7199,7 +5360,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, -@@ -4108,7 +4176,6 @@ enum { +@@ -4118,7 +4176,6 @@ enum { ALC269_FIXUP_LEMOTE_A190X, ALC256_FIXUP_INTEL_NUC8_RUGGED, ALC233_FIXUP_INTEL_NUC8_DMIC, @@ -7207,7 +5368,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 ALC256_FIXUP_INTEL_NUC10, ALC255_FIXUP_XIAOMI_HEADSET_MIC, ALC274_FIXUP_HP_MIC, -@@ -4139,6 +4206,7 @@ enum { +@@ -4149,6 +4206,7 @@ enum { ALC298_FIXUP_LENOVO_C940_DUET7, ALC287_FIXUP_LENOVO_YOGA_BOOK_9I, ALC287_FIXUP_LENOVO_YOGA_PRO7, @@ -7215,7 +5376,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 ALC287_FIXUP_13S_GEN2_SPEAKERS, ALC256_FIXUP_SET_COEF_DEFAULTS, ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE, -@@ -4183,7 +4251,6 @@ enum { +@@ -4193,7 +4251,6 @@ enum { ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD, ALC2XX_FIXUP_HEADSET_MIC, ALC289_FIXUP_DELL_CS35L41_SPI_2, @@ -7223,7 +5384,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 ALC256_FIXUP_ACER_SFG16_MICMUTE_LED, ALC256_FIXUP_HEADPHONE_AMP_VOL, ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX, -@@ -4197,6 +4264,7 @@ enum { +@@ -4207,6 +4264,7 @@ enum { ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318, ALC256_FIXUP_CHROME_BOOK, ALC245_FIXUP_CLEVO_NOISY_MIC, @@ -7231,7 +5392,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE, ALC233_FIXUP_MEDION_MTL_SPK, ALC233_FIXUP_STARLABS_STARFIGHTER, -@@ -4206,7 +4274,6 @@ enum { +@@ -4216,7 +4274,6 @@ enum { ALC274_FIXUP_HP_AIO_BIND_DACS, ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2, ALC285_FIXUP_ASUS_GA605K_HEADSET_MIC, @@ -7239,7 +5400,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC, ALC289_FIXUP_ASUS_ZEPHYRUS_DUAL_SPK, ALC256_FIXUP_VAIO_RPL_MIC_NO_PRESENCE, -@@ -4226,6 +4293,10 @@ enum { +@@ -4236,6 +4293,10 @@ enum { ALC236_FIXUP_DELL_HP_POP_NOISE, ALC274_FIXUP_HP_89E9_GPIO, ALC274_FIXUP_HP_VERBS, @@ -7250,7 +5411,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 }; /* A special fixup for Lenovo C940 and Yoga Duet 7; -@@ -4263,6 +4334,20 @@ static void alc287_fixup_lenovo_yoga_book_9i(struct hda_codec *codec, +@@ -4273,6 +4334,20 @@ static void alc287_fixup_lenovo_yoga_book_9i(struct hda_codec *codec, } static const struct hda_fixup alc269_fixups[] = { @@ -7271,7 +5432,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 [ALC233_FIXUP_WUJIE_SPEAKERS] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { -@@ -4703,6 +4788,10 @@ static const struct hda_fixup alc269_fixups[] = { +@@ -4713,6 +4788,10 @@ static const struct hda_fixup alc269_fixups[] = { .chain_id = ALC271_FIXUP_DMIC, }, [ALC269_FIXUP_LIMIT_INT_MIC_BOOST] = { @@ -7282,7 +5443,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_limit_int_mic_boost, .chained = true, -@@ -5078,11 +5167,7 @@ static const struct hda_fixup alc269_fixups[] = { +@@ -5088,11 +5167,7 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic, .chained = true, @@ -7295,7 +5456,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 }, [ALC255_FIXUP_DELL_SPK_NOISE] = { .type = HDA_FIXUP_FUNC, -@@ -5143,6 +5228,10 @@ static const struct hda_fixup alc269_fixups[] = { +@@ -5153,6 +5228,10 @@ static const struct hda_fixup alc269_fixups[] = { [ALC285_FIXUP_SPEAKER2_TO_DAC1] = { .type = HDA_FIXUP_FUNC, .v.func = alc285_fixup_speaker2_to_dac1, @@ -7306,7 +5467,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 .chained = true, .chain_id = ALC269_FIXUP_THINKPAD_ACPI }, -@@ -5662,11 +5751,7 @@ static const struct hda_fixup alc269_fixups[] = { +@@ -5672,11 +5751,7 @@ static const struct hda_fixup alc269_fixups[] = { { } }, .chained = true, @@ -7319,13 +5480,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 }, [ALC285_FIXUP_THINKPAD_X1_GEN7] = { .type = HDA_FIXUP_FUNC, -@@ -6209,11 +6294,20 @@ static const struct hda_fixup alc269_fixups[] = { - }, - [ALC287_FIXUP_LENOVO_YOGA_PRO7] = { - .type = HDA_FIXUP_FUNC, -- /* Reuse the DAC routing selected for ThinkPad X1 Gen7 */ -- .v.func = alc285_fixup_thinkpad_x1_gen7, -+ .v.func = alc_fixup_yoga_pro7_audio, +@@ -6223,6 +6298,16 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_LENOVO_XPAD_ACPI, }, @@ -7342,7 +5497,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 [ALC623_FIXUP_LENOVO_THINKSTATION_P340] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_no_shutup, -@@ -6653,10 +6747,6 @@ static const struct hda_fixup alc269_fixups[] = { +@@ -6662,10 +6747,6 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC289_FIXUP_DUAL_SPK }, @@ -7353,7 +5508,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 [ALC256_FIXUP_ACER_SFG16_MICMUTE_LED] = { .type = HDA_FIXUP_FUNC, .v.func = alc256_fixup_acer_sfg16_micmute_led, -@@ -6729,6 +6819,10 @@ static const struct hda_fixup alc269_fixups[] = { +@@ -6738,6 +6819,10 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE, }, @@ -7364,7 +5519,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 [ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { -@@ -6749,6 +6843,8 @@ static const struct hda_fixup alc269_fixups[] = { +@@ -6758,6 +6843,8 @@ static const struct hda_fixup alc269_fixups[] = { [ALC233_FIXUP_STARLABS_STARFIGHTER] = { .type = HDA_FIXUP_FUNC, .v.func = alc233_fixup_starlabs_starfighter, @@ -7373,7 +5528,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 }, [ALC294_FIXUP_BASS_SPEAKER_15] = { .type = HDA_FIXUP_FUNC, -@@ -6776,11 +6872,7 @@ static const struct hda_fixup alc269_fixups[] = { +@@ -6785,11 +6872,7 @@ static const struct hda_fixup alc269_fixups[] = { { } }, .chained = true, @@ -7386,7 +5541,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 }, [ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC] = { .type = HDA_FIXUP_FUNC, -@@ -6898,6 +6990,26 @@ static const struct hda_fixup alc269_fixups[] = { +@@ -6907,6 +6990,26 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC274_FIXUP_HP_89E9_GPIO, }, @@ -7413,7 +5568,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 }; static const struct hda_quirk alc269_fixup_tbl[] = { -@@ -6951,8 +7063,10 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -6960,8 +7063,10 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x1597, "Acer Nitro 5 AN517-55", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x159e, "Acer Nitro 5 AN515-46", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x160e, "Acer PT316-51S", ALC2XX_FIXUP_HEADSET_MIC), @@ -7424,7 +5579,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x1025, 0x1679, "Acer Nitro 16 AN16-41", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x169a, "Acer Swift SFG16", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x171e, "Acer Nitro ANV15-51", ALC245_FIXUP_ACER_MICMUTE_LED), -@@ -6961,6 +7075,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -6970,6 +7075,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x1826, "Acer Helios ZPC", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), SND_PCI_QUIRK(0x1025, 0x182c, "Acer Helios ZPD", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), SND_PCI_QUIRK(0x1025, 0x1844, "Acer Helios ZPS", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), @@ -7432,7 +5587,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), SND_PCI_QUIRK(0x1028, 0x053c, "Dell Latitude E5430", ALC292_FIXUP_DELL_E7X), SND_PCI_QUIRK(0x1028, 0x054b, "Dell XPS one 2710", ALC275_FIXUP_DELL_XPS), -@@ -7145,10 +7260,12 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7154,10 +7260,12 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x85c6, "HP Pavilion x360 Convertible 14-dy1xxx", ALC295_FIXUP_HP_MUTE_LED_COEFBIT11), SND_PCI_QUIRK(0x103c, 0x85de, "HP Envy x360 13-ar0xxx", ALC285_FIXUP_HP_ENVY_X360), SND_PCI_QUIRK(0x103c, 0x85f0, "HP Laptop 15-dw0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), @@ -7445,7 +5600,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x86c1, "HP Laptop 15-da3001TU", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x86c7, "HP Envy AiO 32", ALC274_FIXUP_HP_ENVY_GPIO), -@@ -7259,6 +7376,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7268,6 +7376,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x89e7, "HP Elite x2 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a05, "HP Dragonfly Folio G3 2-in-1", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a06, "HP Dragonfly Folio G3 2-in-1", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), @@ -7453,7 +5608,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x103c, 0x8a0f, "HP Pavilion 14-ec1xxx", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a1b, "HP 255 15.6 inch G9 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x8a1f, "HP Laptop 14s-dr5xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), -@@ -7350,10 +7468,11 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7359,10 +7468,11 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8bf0, "HP", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c15, "HP Spectre x360 2-in-1 Laptop 14-eu0xxx", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX), SND_PCI_QUIRK(0x103c, 0x8c16, "HP Spectre x360 2-in-1 Laptop 16-aa0xxx", ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX), @@ -7466,7 +5621,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x103c, 0x8c46, "HP EliteBook 830 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c47, "HP EliteBook 840 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c48, "HP EliteBook 860 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), -@@ -7502,6 +7621,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7511,6 +7621,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8f42, "HP ZBook 8 G2a 14W", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8f57, "HP Trekker G7JC", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8f62, "HP ZBook 8 G2a 16W", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), @@ -7474,7 +5629,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x103c, 0x8f94, "HP ZBook 8 G2a 14", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED_INVERTED), SND_PCI_QUIRK(0x103c, 0x8f95, "HP ZBook 8 G2a 16", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED_INVERTED), SND_PCI_QUIRK(0x1043, 0x1024, "ASUS Zephyrus G14 2025", ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC), -@@ -7509,6 +7629,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7518,6 +7629,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1034, "ASUS GU605C", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), @@ -7482,7 +5637,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x1043, 0x1054, "ASUS G614FH/FM/FP", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1043, 0x106f, "ASUS VivoBook X515UA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), -@@ -7516,16 +7637,15 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7525,16 +7637,15 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x10a1, "ASUS UX391UA", ALC294_FIXUP_ASUS_SPK), SND_PCI_QUIRK(0x1043, 0x10a4, "ASUS TP3407SA", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x1043, 0x10c0, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), @@ -7502,7 +5657,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x1043, 0x125e, "ASUS Q524UQK", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1271, "ASUS X430UN", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1290, "ASUS X441SA", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE), -@@ -7574,7 +7694,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7583,7 +7694,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1863, "ASUS UX6404VI/VV", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1881, "ASUS Zephyrus S/M", ALC294_FIXUP_ASUS_GX502_PINS), SND_PCI_QUIRK(0x1043, 0x18b1, "Asus MJ401TA", ALC256_FIXUP_ASUS_HEADSET_MIC), @@ -7511,7 +5666,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x1043, 0x18f1, "Asus FX505DT", ALC256_FIXUP_ASUS_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x194e, "ASUS UX563FD", ALC294_FIXUP_ASUS_HPE), SND_PCI_QUIRK(0x1043, 0x1970, "ASUS UX550VE", ALC289_FIXUP_ASUS_GA401), -@@ -7585,7 +7705,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7594,7 +7705,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x19f4, "ASUS UM3405GA", ALC294_FIXUP_ASUS_I2C_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), SND_PCI_QUIRK(0x1043, 0x1a63, "ASUS UX3405MA", ALC294_FIXUP_ASUS_SPI_HEADSET_MIC), @@ -7520,7 +5675,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x1043, 0x1a8e, "ASUS G712LWS", ALC294_FIXUP_LENOVO_MIC_LOCATION), SND_PCI_QUIRK(0x1043, 0x1a8f, "ASUS UX582ZS", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1b11, "ASUS UX431DA", ALC294_FIXUP_ASUS_COEF_1B), -@@ -7614,6 +7734,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7623,6 +7734,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1264, "ASUS UM5606KA", ALC294_FIXUP_BASS_SPEAKER_15), SND_PCI_QUIRK(0x1043, 0x1e02, "ASUS UX3402ZA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1e10, "ASUS VivoBook X507UAR", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), @@ -7528,7 +5683,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x1043, 0x1e11, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA502), SND_PCI_QUIRK(0x1043, 0x1e12, "ASUS UM3402", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1e1f, "ASUS Vivobook 15 X1504VAP", ALC2XX_FIXUP_HEADSET_MIC), -@@ -7625,8 +7746,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7634,8 +7746,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1e93, "ASUS ExpertBook B9403CVAR", ALC294_FIXUP_ASUS_HPE), SND_PCI_QUIRK(0x1043, 0x1eb3, "ASUS Ally RC72LA", ALC287_FIXUP_ASUS_ALLY_X), SND_PCI_QUIRK(0x1043, 0x1ed3, "ASUS HN7306W", ALC287_FIXUP_CS35L41_I2C_2), @@ -7538,7 +5693,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x1043, 0x1c52, "ASUS Zephyrus G15 2022", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x1f11, "ASUS Zephyrus G14", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x1f12, "ASUS UM5302", ALC287_FIXUP_CS35L41_I2C_2), -@@ -7837,7 +7957,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7846,7 +7957,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x2211, "Thinkpad W541", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad T440", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad X240", ALC292_FIXUP_TPT440_DOCK), @@ -7547,7 +5702,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x17aa, 0x2218, "Thinkpad X1 Carbon 2nd", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2223, "ThinkPad T550", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK), -@@ -7853,7 +7973,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7862,7 +7973,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x224b, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x224c, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x224d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), @@ -7557,7 +5712,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x17aa, 0x2288, "Thinkpad X390", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK), SND_PCI_QUIRK(0x17aa, 0x2292, "Thinkpad X1 Carbon 7th", ALC285_FIXUP_THINKPAD_HEADSET_JACK), SND_PCI_QUIRK(0x17aa, 0x22be, "Thinkpad X1 Carbon 8th", ALC285_FIXUP_THINKPAD_HEADSET_JACK), -@@ -7928,7 +8049,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7937,7 +8049,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3869, "Lenovo Yoga7 14IAL7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), HDA_CODEC_QUIRK(0x17aa, 0x386a, "Lenovo Yoga 7 16IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), HDA_CODEC_QUIRK(0x17aa, 0x386e, "Legion Y9000X 2022 IAH7", ALC287_FIXUP_CS35L41_I2C_2), @@ -7566,7 +5721,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ HDA_CODEC_QUIRK(0x17aa, 0x38a7, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), -@@ -7963,6 +8084,12 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7972,6 +8084,12 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x38b8, "Yoga S780-14.5 proX AMD YC Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38b9, "Yoga S780-14.5 proX AMD LX Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38ba, "Yoga S780-14.5 Air AMD quad YC", ALC287_FIXUP_TAS2781_I2C), @@ -7579,7 +5734,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x17aa, 0x38bb, "Yoga S780-14.5 Air AMD quad AAC", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38be, "Yoga S980-14.5 proX YC Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38bf, "Yoga S980-14.5 proX LX Dual", ALC287_FIXUP_TAS2781_I2C), -@@ -7980,7 +8107,6 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7989,7 +8107,6 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x38df, "Yoga Y990 Intel YC Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38e0, "Yoga Y990 Intel VECO Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38f8, "Yoga Book 9i", ALC287_FIXUP_TAS2781_I2C), @@ -7587,7 +5742,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 /* Legion 7 15ASH11 shares PCI SSID 17aa:38f9 with Thinkbook 16P Gen5; * use codec SSID to distinguish them */ -@@ -7990,7 +8116,9 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7999,7 +8116,9 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x38fc, "Lenovo Yoga Pro 7 15ASH11", ALC287_FIXUP_LENOVO_YOGA_PRO7), SND_PCI_QUIRK(0x17aa, 0x38fd, "ThinkBook plus Gen5 Hybrid", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), @@ -7598,7 +5753,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x17aa, 0x3911, "Lenovo Yoga Pro 7 14IAH10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x3912, "Lenovo Xiaoxin 14 GT", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x3913, "Lenovo 145", ALC236_FIXUP_LENOVO_INV_DMIC), -@@ -7998,14 +8126,19 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -8007,14 +8126,19 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_TXNW2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3929, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x392b, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), @@ -7620,7 +5775,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x17aa, 0x5034, "Thinkpad T450", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x5036, "Thinkpad T450s", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x503c, "Thinkpad L450", ALC292_FIXUP_TPT440_DOCK), -@@ -8018,7 +8151,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -8027,7 +8151,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x505f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x5062, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x508b, "Thinkpad X12 Gen 1", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS), @@ -7629,7 +5784,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x17aa, 0x511e, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), -@@ -8032,9 +8165,14 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -8041,9 +8165,14 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1854, 0x0489, "LG gram 16 (16Z90R-A)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), SND_PCI_QUIRK(0x1854, 0x048a, "LG gram 17 (17ZD90R)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), SND_PCI_QUIRK(0x1854, 0x0490, "LG Gram Style 14 (14Z90RS)", ALC298_FIXUP_LG_GRAM_STYLE_14), @@ -7644,7 +5799,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x1b35, 0x1235, "CZC B20", ALC269_FIXUP_CZC_B20), SND_PCI_QUIRK(0x1b35, 0x1236, "CZC TMI", ALC269_FIXUP_CZC_TMI), SND_PCI_QUIRK(0x1b35, 0x1237, "CZC L101", ALC269_FIXUP_CZC_L101), -@@ -8069,13 +8207,17 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -8078,13 +8207,17 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1d72, 0x1947, "RedmiBook Air", ALC255_FIXUP_XIAOMI_HEADSET_MIC), SND_PCI_QUIRK(0x1e39, 0xca14, "MEDION NM14LNL", ALC233_FIXUP_MEDION_MTL_SPK), SND_PCI_QUIRK(0x1e50, 0x7007, "Positivo DN50E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), @@ -7662,7 +5817,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x2782, 0x0228, "Infinix ZERO BOOK 13", ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13), SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO), -@@ -8086,6 +8228,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -8095,6 +8228,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x2782, 0x1705, "MEDION E15433", ALC269VC_FIXUP_INFINIX_Y4_MAX), SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME), SND_PCI_QUIRK(0x2782, 0x4900, "MEDION E15443", ALC233_FIXUP_MEDION_MTL_SPK), @@ -7670,7 +5825,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x2782, 0xa212, "Lunnen Ground 14", ALC269VC_FIXUP_LUNNEN_GROUND_14), SND_PCI_QUIRK(0x7017, 0x2014, "Star Labs StarFighter", ALC233_FIXUP_STARLABS_STARFIGHTER), SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), -@@ -8159,6 +8302,7 @@ static const struct hda_quirk alc269_fixup_vendor_tbl[] = { +@@ -8168,6 +8302,7 @@ static const struct hda_quirk alc269_fixup_vendor_tbl[] = { SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo XPAD", ALC269_FIXUP_LENOVO_XPAD_ACPI), SND_PCI_QUIRK_VENDOR(0x19e5, "Huawei Matebook", ALC255_FIXUP_MIC_MUTE_LED), @@ -7678,7 +5833,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 {} }; -@@ -8298,6 +8442,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { +@@ -8307,6 +8442,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC2XX_FIXUP_HEADSET_MIC, .name = "alc2xx-fixup-headset-mic"}, {.id = ALC245_FIXUP_BASS_HP_DAC, .name = "alc245-fixup-bass-hp-dac"}, {.id = ALC256_FIXUP_HONOR_MRB_XXX_M1020_AUDIO, .name = "alc256-honor-mrb-xxx-m1020-audio"}, @@ -7686,7 +5841,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 {} }; #define ALC225_STANDARD_PINS \ -@@ -8903,6 +9048,7 @@ static int alc269_probe(struct hda_codec *codec, const struct hda_device_id *id) +@@ -8912,6 +9048,7 @@ static int alc269_probe(struct hda_codec *codec, const struct hda_device_id *id) spec->init_hook = alc256_init; spec->gen.mixer_nid = 0; /* ALC256 does not have any loopback mixer path */ if (codec->core.vendor_id == 0x10ec0236 && @@ -8476,49 +6631,6 @@ diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/s acpi_dev_put(adev); return ret; -@@ -173,13 +171,13 @@ static void tas2781_hda_playback_hook(struct device *dev, int action) - case HDA_GEN_PCM_ACT_OPEN: - pm_runtime_get_sync(dev); - scoped_guard(mutex, &tas_hda->priv->codec_lock) { -- tasdevice_tuning_switch(tas_hda->priv, 0); -+ tasdevice_tuning_switch(tas_hda->priv, 0, false); - tas_hda->priv->playback_started = true; - } - break; - case HDA_GEN_PCM_ACT_CLOSE: - scoped_guard(mutex, &tas_hda->priv->codec_lock) { -- tasdevice_tuning_switch(tas_hda->priv, 1); -+ tasdevice_tuning_switch(tas_hda->priv, 1, false); - tas_hda->priv->playback_started = false; - } - -@@ -722,7 +720,7 @@ static int tas2781_runtime_suspend(struct device *dev) - * Stop the playback if it's unused. - */ - if (tas_hda->priv->playback_started) { -- tasdevice_tuning_switch(tas_hda->priv, 1); -+ tasdevice_tuning_switch(tas_hda->priv, 1, false); - tas_hda->priv->playback_started = false; - } - -@@ -752,7 +750,7 @@ static int tas2781_system_suspend(struct device *dev) - - /* Shutdown chip before system suspend */ - if (tas_hda->priv->playback_started) -- tasdevice_tuning_switch(tas_hda->priv, 1); -+ tasdevice_tuning_switch(tas_hda->priv, 1, false); - - /* - * Reset GPIO may be shared, so cannot reset here. -@@ -785,7 +783,7 @@ static int tas2781_system_resume(struct device *dev) - TASDEVICE_BIN_BLK_PRE_POWER_UP); - - if (tas_hda->priv->playback_started) -- tasdevice_tuning_switch(tas_hda->priv, 0); -+ tasdevice_tuning_switch(tas_hda->priv, 0, false); - - return 0; - } diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c --- a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c @@ -8556,56 +6668,6 @@ diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c b/sound/hda/codecs/s return ret; } -@@ -400,11 +399,11 @@ static void tas2781_hda_playback_hook(struct device *dev, int action) - pm_runtime_get_sync(dev); - guard(mutex)(&tas_priv->codec_lock); - if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK) -- tasdevice_tuning_switch(tas_hda->priv, 0); -+ tasdevice_tuning_switch(tas_hda->priv, 0, false); - } else if (action == HDA_GEN_PCM_ACT_CLOSE) { - guard(mutex)(&tas_priv->codec_lock); - if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK) -- tasdevice_tuning_switch(tas_priv, 1); -+ tasdevice_tuning_switch(tas_priv, 1, false); - pm_runtime_put_autosuspend(dev); - } - } -@@ -848,7 +847,7 @@ static int tas2781_runtime_suspend(struct device *dev) - - if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK - && tas_priv->playback_started) -- tasdevice_tuning_switch(tas_priv, 1); -+ tasdevice_tuning_switch(tas_priv, 1, false); - - tas_priv->tasdevice[tas_priv->index].cur_book = -1; - tas_priv->tasdevice[tas_priv->index].cur_conf = -1; -@@ -865,7 +864,7 @@ static int tas2781_runtime_resume(struct device *dev) - - if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK - && tas_priv->playback_started) -- tasdevice_tuning_switch(tas_priv, 0); -+ tasdevice_tuning_switch(tas_priv, 0, false); - - return 0; - } -@@ -883,7 +882,7 @@ static int tas2781_system_suspend(struct device *dev) - /* Shutdown chip before system suspend */ - if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK - && tas_priv->playback_started) -- tasdevice_tuning_switch(tas_priv, 1); -+ tasdevice_tuning_switch(tas_priv, 1, false); - - return 0; - } -@@ -918,7 +917,7 @@ static int tas2781_system_resume(struct device *dev) - tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK; - - if (tas_priv->playback_started) -- tasdevice_tuning_switch(tas_priv, 0); -+ tasdevice_tuning_switch(tas_priv, 0, false); - } - - return ret; diff --git a/sound/hda/common/auto_parser.c b/sound/hda/common/auto_parser.c --- a/sound/hda/common/auto_parser.c +++ b/sound/hda/common/auto_parser.c @@ -8762,7 +6824,7 @@ diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c guard(mutex)(&bus->core.cmd_mutex); if (flags & HDA_RW_NO_RESPONSE_FALLBACK) bus->no_response_fallback = 1; -@@ -2967,8 +2973,11 @@ static void hda_codec_pm_complete(struct device *dev) +@@ -2969,8 +2975,11 @@ static void hda_codec_pm_complete(struct device *dev) dev->power.power_state = PMSG_RESUME; if (pm_runtime_suspended(dev) && (codec->jackpoll_interval || @@ -8775,7 +6837,7 @@ diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c } static int hda_codec_pm_suspend(struct device *dev) -@@ -3370,7 +3379,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, +@@ -3372,7 +3381,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, for (; knew->name; knew++) { struct snd_kcontrol *kctl; int addr = 0, idx = 0; @@ -8827,29 +6889,6 @@ diff --git a/sound/hda/common/jack.c b/sound/hda/common/jack.c err = snd_hda_jack_add_kctl(codec, nid, name, phantom_jack, 0, NULL); if (err < 0) return err; -diff --git a/sound/hda/common/proc.c b/sound/hda/common/proc.c ---- a/sound/hda/common/proc.c -+++ b/sound/hda/common/proc.c -@@ -624,7 +624,7 @@ static void print_conn_list(struct snd_info_buffer *buffer, - /* Get Cache connections info */ - cache_len = snd_hda_get_conn_list(codec, nid, &list); - if (cache_len >= 0 && (cache_len != conn_len || -- memcmp(list, conn, conn_len) != 0)) { -+ memcmp(list, conn, conn_len * sizeof(*conn)) != 0)) { - snd_iprintf(buffer, " In-driver Connection: %d\n", cache_len); - if (cache_len > 0) { - snd_iprintf(buffer, " "); -diff --git a/sound/hda/controllers/Kconfig b/sound/hda/controllers/Kconfig ---- a/sound/hda/controllers/Kconfig -+++ b/sound/hda/controllers/Kconfig -@@ -33,6 +33,7 @@ config SND_HDA_TEGRA - config SND_HDA_CIX_IPBLOQ - tristate "CIX IPBLOQ HD Audio" - depends on ARCH_CIX || COMPILE_TEST -+ depends on ARCH_DMA_ADDR_T_64BIT - select SND_HDA - select SND_HDA_ALIGNED_MMIO - help diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c --- a/sound/hda/controllers/intel.c +++ b/sound/hda/controllers/intel.c @@ -9012,7 +7051,7 @@ diff --git a/sound/hda/core/controller.c b/sound/hda/core/controller.c diff --git a/sound/hda/core/device.c b/sound/hda/core/device.c --- a/sound/hda/core/device.c +++ b/sound/hda/core/device.c -@@ -663,6 +663,7 @@ static const struct hda_vendor_id hda_vendor_ids[] = { +@@ -664,6 +664,7 @@ static const struct hda_vendor_id hda_vendor_ids[] = { { 0x1af4, "QEMU" }, { 0x1fa8, "Senarytech" }, { 0x434d, "C-Media" }, @@ -9020,7 +7059,7 @@ diff --git a/sound/hda/core/device.c b/sound/hda/core/device.c { 0x8086, "Intel" }, { 0x8384, "SigmaTel" }, {} /* terminator */ -@@ -765,7 +766,7 @@ unsigned int snd_hdac_stream_format_bits(snd_pcm_format_t format, snd_pcm_subfor +@@ -766,7 +767,7 @@ unsigned int snd_hdac_stream_format_bits(snd_pcm_format_t format, snd_pcm_subfor params_set_format(¶ms, snd_hdac_format_normalize(format)); snd_mask_set(hw_param_mask(¶ms, SNDRV_PCM_HW_PARAM_SUBFORMAT), @@ -9304,21 +7343,6 @@ diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c static const snd_pcm_format_t hpi_to_alsa_formats[] = { INVALID_FORMAT, /* INVALID */ -diff --git a/sound/pci/asihpi/hpi6000.c b/sound/pci/asihpi/hpi6000.c ---- a/sound/pci/asihpi/hpi6000.c -+++ b/sound/pci/asihpi/hpi6000.c -@@ -537,6 +537,11 @@ static short create_adapter_obj(struct hpi_adapter_obj *pao, - hr1.size = sizeof(hr1); - - error = hpi6000_message_response_sequence(pao, 0, &hm, &hr0); -+ if (error) { -+ HPI_DEBUG_LOG(ERROR, "message transport error %d\n", -+ error); -+ return error; -+ } - if (hr0.error) { - HPI_DEBUG_LOG(DEBUG, "message error %d\n", hr0.error); - return hr0.error; diff --git a/sound/pci/asihpi/hpidspcd.c b/sound/pci/asihpi/hpidspcd.c --- a/sound/pci/asihpi/hpidspcd.c +++ b/sound/pci/asihpi/hpidspcd.c @@ -9715,44 +7739,6 @@ diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c } rc = sis_alloc_suspend(sis); -diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c ---- a/sound/pci/via82xx_modem.c -+++ b/sound/pci/via82xx_modem.c -@@ -573,24 +573,18 @@ static inline unsigned int calc_linear_pos(struct via82xx_modem *chip, - viadev->bufsize2, viadev->idx_table[idx].offset, - viadev->idx_table[idx].size, count); - #endif -- if (count && size < count) { -+ if (! count) -+ /* bogus count 0 on the DMA boundary? */ -+ res = viadev->idx_table[idx].offset; -+ else -+ /* count register returns full size -+ * when end of buffer is reached -+ */ -+ res = viadev->idx_table[idx].offset + size; -+ if (check_invalid_pos(viadev, res)) { - dev_dbg(chip->card->dev, -- "invalid via82xx_cur_ptr, using last valid pointer\n"); -+ "invalid via82xx_cur_ptr (2), using last valid pointer\n"); - res = viadev->lastpos; -- } else { -- if (! count) -- /* bogus count 0 on the DMA boundary? */ -- res = viadev->idx_table[idx].offset; -- else -- /* count register returns full size -- * when end of buffer is reached -- */ -- res = viadev->idx_table[idx].offset + size; -- if (check_invalid_pos(viadev, res)) { -- dev_dbg(chip->card->dev, -- "invalid via82xx_cur_ptr (2), using last valid pointer\n"); -- res = viadev->lastpos; -- } - } - } - viadev->lastpos = res; /* remember the last position */ diff --git a/sound/sh/aica.c b/sound/sh/aica.c --- a/sound/sh/aica.c +++ b/sound/sh/aica.c @@ -9775,1351 +7761,10 @@ diff --git a/sound/sh/aica.c b/sound/sh/aica.c return err; } -diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c ---- a/sound/soc/amd/acp-config.c -+++ b/sound/soc/amd/acp-config.c -@@ -63,6 +63,13 @@ static const struct dmi_system_id acp70_acpi_flag_override_table[] = { - DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook 18 M1807GA"), - }, - }, -+ { -+ /* HP OmniBook X Flip 14-kc0xxx (Strix Point, ACP 7.2) */ -+ .matches = { -+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"), -+ DMI_MATCH(DMI_BOARD_NAME, "8EA1"), -+ }, -+ }, - {} - }; - -@@ -379,5 +386,17 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[] = { - }; - EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sof_machines); - -+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp7x_sof_machines[] = { -+ { -+ .id = "AMDI1010", -+ .drv_name = "acp7x-dsp", -+ .pdata = &acp_quirk_data, -+ .fw_filename = "sof-acp7x.ri", -+ .sof_tplg_filename = "sof-acp7x.tplg", -+ }, -+ {}, -+}; -+EXPORT_SYMBOL(snd_soc_acpi_amd_acp7x_sof_machines); -+ - MODULE_DESCRIPTION("AMD ACP Machine Configuration Module"); - MODULE_LICENSE("Dual BSD/GPL"); -diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c ---- a/sound/soc/amd/acp-pcm-dma.c -+++ b/sound/soc/amd/acp-pcm-dma.c -@@ -691,12 +691,10 @@ static irqreturn_t dma_irq_handler(int irq, void *arg) - { - u16 dscr_idx; - u32 intr_flag, ext_intr_status; -- struct audio_drv_data *irq_data; -+ struct audio_drv_data *irq_data = arg; - void __iomem *acp_mmio; -- struct device *dev = arg; - bool valid_irq = false; - -- irq_data = dev_get_drvdata(dev); - acp_mmio = irq_data->acp_mmio; - - ext_intr_status = acp_reg_read(acp_mmio, mmACP_EXTERNAL_INTR_STAT); -@@ -1291,10 +1289,10 @@ static int acp_audio_probe(struct platform_device *pdev) - - irq = platform_get_irq(pdev, 0); - if (irq < 0) -- return -ENODEV; -+ return irq; - - status = devm_request_irq(&pdev->dev, irq, dma_irq_handler, -- 0, "ACP_IRQ", &pdev->dev); -+ 0, "ACP_IRQ", audio_drv_data); - if (status) { - dev_err(&pdev->dev, "ACP IRQ request failed\n"); - return status; -diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c ---- a/sound/soc/amd/acp/acp-i2s.c -+++ b/sound/soc/amd/acp/acp-i2s.c -@@ -686,6 +686,10 @@ static int acp_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_d - return 0; - } - -+static const u64 acp_i2s_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A; -+ - const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = { - .startup = acp_i2s_startup, - .hw_params = acp_i2s_hwparams, -@@ -693,6 +697,8 @@ const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = { - .trigger = acp_i2s_trigger, - .set_fmt = acp_i2s_set_fmt, - .set_tdm_slot = acp_i2s_set_tdm_slot, -+ .auto_selectable_formats = &acp_i2s_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - EXPORT_SYMBOL_NS_GPL(asoc_acp_cpu_dai_ops, "SND_SOC_ACP_COMMON"); - -diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c ---- a/sound/soc/amd/acp/acp-mach-common.c -+++ b/sound/soc/amd/acp/acp-mach-common.c -@@ -938,15 +938,10 @@ static int acp_max98388_hw_params(struct snd_pcm_substream *substream, - struct snd_soc_dai *codec_dai = - snd_soc_card_get_codec_dai(card, - MAX98388_CODEC_DAI); -- int ret; - -- ret = snd_soc_dai_set_fmt(codec_dai, -- SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_I2S | -- SND_SOC_DAIFMT_NB_NF); -- if (ret < 0) -- return ret; -- -- return ret; -+ return snd_soc_dai_set_fmt(codec_dai, -+ SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_I2S | -+ SND_SOC_DAIFMT_NB_NF); - } - - static const struct snd_soc_ops acp_max98388_ops = { -diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c ---- a/sound/soc/amd/acp/acp-pci.c -+++ b/sound/soc/amd/acp/acp-pci.c -@@ -118,17 +118,10 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id - if (!chip) - return -ENOMEM; - -- if (pci_enable_device(pci)) -+ if (pcim_enable_device(pci)) - return dev_err_probe(&pci->dev, -ENODEV, - "pci_enable_device failed\n"); - -- ret = pci_request_regions(pci, "AMD ACP3x audio"); -- if (ret < 0) { -- dev_err(&pci->dev, "pci_request_regions failed\n"); -- ret = -ENOMEM; -- goto disable_pci; -- } -- - pci_set_master(pci); - - chip->acp_rev = pci->revision; -@@ -161,24 +154,21 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id - break; - default: - dev_err(dev, "Unsupported device revision:0x%x\n", pci->revision); -- ret = -EINVAL; -- goto release_regions; -+ return -EINVAL; - } - chip->flag = flag; - - addr = pci_resource_start(pci, 0); -- chip->base = devm_ioremap(&pci->dev, addr, pci_resource_len(pci, 0)); -- if (!chip->base) { -- ret = -ENOMEM; -- goto release_regions; -- } -+ chip->base = pcim_iomap_region(pci, 0, "AMD ACP3x audio"); -+ if (IS_ERR(chip->base)) -+ return PTR_ERR(chip->base); - - chip->addr = addr; - - chip->acp_hw_ops_init(chip); - ret = acp_hw_init(chip); - if (ret) -- goto release_regions; -+ goto de_init; - - ret = devm_request_irq(dev, pci->irq, irq_handler, - IRQF_SHARED, "ACP_I2S_IRQ", chip); -@@ -214,10 +204,6 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id - - de_init: - acp_hw_deinit(chip); --release_regions: -- pci_release_regions(pci); --disable_pci: -- pci_disable_device(pci); - - return ret; - }; -diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c ---- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c -+++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c -@@ -432,7 +432,7 @@ static int soc_card_dai_links_create(struct snd_soc_card *card) - if (!soc_aux) - return -ENOMEM; - -- ret = asoc_sdw_parse_sdw_endpoints(card, soc_aux, soc_dais, soc_ends, &num_confs); -+ ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, soc_aux, soc_dais, soc_ends, &num_confs); - if (ret < 0) - return ret; - -@@ -521,15 +521,15 @@ static int mc_probe(struct platform_device *pdev) - dmi_check_system(soc_sdw_quirk_table); - - if (quirk_override != -1) { -- dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n", -+ dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n", - soc_sdw_quirk, quirk_override); - soc_sdw_quirk = quirk_override; - } - -- log_quirks(card->dev); -+ log_quirks(&pdev->dev); - - ctx->mc_quirk = soc_sdw_quirk; -- dev_dbg(card->dev, "legacy quirk 0x%lx\n", ctx->mc_quirk); -+ dev_dbg(&pdev->dev, "legacy quirk 0x%lx\n", ctx->mc_quirk); - /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ - for (i = 0; i < ctx->codec_info_list_count; i++) - codec_info_list[i].amp_num = 0; -@@ -546,12 +546,12 @@ static int mc_probe(struct platform_device *pdev) - for (i = 0; i < ctx->codec_info_list_count; i++) - amp_num += codec_info_list[i].amp_num; - -- card->components = devm_kasprintf(card->dev, GFP_KERNEL, -+ card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, - " cfg-amp:%d", amp_num); - if (!card->components) - return -ENOMEM; - if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC) { -- card->components = devm_kasprintf(card->dev, GFP_KERNEL, -+ card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, - "%s mic:acp-dmic cfg-mics:%d", - card->components, - 1); -@@ -560,9 +560,9 @@ static int mc_probe(struct platform_device *pdev) - } - - /* Register the card */ -- ret = devm_snd_soc_register_card(card->dev, card); -+ ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) { -- dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret); -+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed %d\n", ret); - asoc_sdw_mc_dailink_exit_loop(card); - return ret; - } -@@ -580,8 +580,8 @@ static void mc_remove(struct platform_device *pdev) - } - - static const struct platform_device_id mc_id_table[] = { -- { "amd_sdw", }, -- {} -+ { .name = "amd_sdw" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, mc_id_table); - -diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c ---- a/sound/soc/amd/acp/acp-sdw-sof-mach.c -+++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c -@@ -303,7 +303,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) - if (!sof_aux) - return -ENOMEM; - -- ret = asoc_sdw_parse_sdw_endpoints(card, sof_aux, sof_dais, sof_ends, &num_devs); -+ ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_devs); - if (ret < 0) - return ret; - -@@ -388,12 +388,12 @@ static int mc_probe(struct platform_device *pdev) - dmi_check_system(sof_sdw_quirk_table); - - if (quirk_override != -1) { -- dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n", -+ dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n", - sof_sdw_quirk, quirk_override); - sof_sdw_quirk = quirk_override; - } - -- log_quirks(card->dev); -+ log_quirks(&pdev->dev); - - ctx->mc_quirk = sof_sdw_quirk; - /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ -@@ -412,15 +412,15 @@ static int mc_probe(struct platform_device *pdev) - for (i = 0; i < ctx->codec_info_list_count; i++) - amp_num += codec_info_list[i].amp_num; - -- card->components = devm_kasprintf(card->dev, GFP_KERNEL, -+ card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, - " cfg-amp:%d", amp_num); - if (!card->components) - return -ENOMEM; - - /* Register the card */ -- ret = devm_snd_soc_register_card(card->dev, card); -+ ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) { -- dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret); -+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed %d\n", ret); - asoc_sdw_mc_dailink_exit_loop(card); - return ret; - } -@@ -438,8 +438,8 @@ static void mc_remove(struct platform_device *pdev) - } - - static const struct platform_device_id mc_id_table[] = { -- { "amd_sof_sdw", }, -- {} -+ { .name = "amd_sof_sdw" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, mc_id_table); - -diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c ---- a/sound/soc/amd/acp/acp-sof-mach.c -+++ b/sound/soc/amd/acp/acp-sof-mach.c -@@ -128,31 +128,31 @@ static int acp_sof_probe(struct platform_device *pdev) - static const struct platform_device_id board_ids[] = { - { - .name = "rt5682-rt1019", -- .driver_data = (kernel_ulong_t)&sof_rt5682_rt1019_data -+ .driver_data = (kernel_ulong_t)&sof_rt5682_rt1019_data, - }, - { - .name = "rt5682-max", -- .driver_data = (kernel_ulong_t)&sof_rt5682_max_data -+ .driver_data = (kernel_ulong_t)&sof_rt5682_max_data, - }, - { - .name = "rt5682s-max", -- .driver_data = (kernel_ulong_t)&sof_rt5682s_max_data -+ .driver_data = (kernel_ulong_t)&sof_rt5682s_max_data, - }, - { - .name = "rt5682s-rt1019", -- .driver_data = (kernel_ulong_t)&sof_rt5682s_rt1019_data -+ .driver_data = (kernel_ulong_t)&sof_rt5682s_rt1019_data, - }, - { - .name = "nau8825-max", -- .driver_data = (kernel_ulong_t)&sof_nau8825_data -+ .driver_data = (kernel_ulong_t)&sof_nau8825_data, - }, - { - .name = "rt5682s-hs-rt1019", -- .driver_data = (kernel_ulong_t)&sof_rt5682s_hs_rt1019_data -+ .driver_data = (kernel_ulong_t)&sof_rt5682s_hs_rt1019_data, - }, - { - .name = "nau8821-max", -- .driver_data = (kernel_ulong_t)&sof_nau8821_max98388_data -+ .driver_data = (kernel_ulong_t)&sof_nau8821_max98388_data, - }, - { } - }; -diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c ---- a/sound/soc/amd/acp/amd-acp70-acpi-match.c -+++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c -@@ -659,6 +659,56 @@ static const struct snd_soc_acpi_link_adr acp70_rt721_l1u0_tas2783x2_l1u8b[] = { - {} - }; - -+static const struct snd_soc_acpi_adr_device rt712_vb_l1u0_adr[] = { -+ { -+ .adr = 0x000130025D071201ull, -+ /* -+ * On this platform speakers are provided by two TAS2783 amps, -+ * so the AIF2 amp path is left unused: jack + DMIC only. -+ */ -+ .num_endpoints = ARRAY_SIZE(jack_dmic_endpoints), -+ .endpoints = jack_dmic_endpoints, -+ .name_prefix = "rt712" -+ } -+}; -+ -+/* -+ * Unique ID 0xC drives the left speaker and 0x9 the right one. The order of the -+ * entries matters as much as the endpoints: asoc_sdw_parse_sdw_endpoints() -+ * appends them to the dailink in array order and sdw_compute_slave_ports() -+ * hands out payload block offsets in that same order, so the first entry is the -+ * one that receives channel 0. -+ */ -+static const struct snd_soc_acpi_adr_device tas2783x2_l0u9c_adr[] = { -+ { -+ .adr = 0x00003C0102000001ull, -+ .num_endpoints = 1, -+ .endpoints = &spk_l_endpoint, -+ .name_prefix = "tas2783-1", -+ }, -+ { -+ .adr = 0x0000390102000001ull, -+ .num_endpoints = 1, -+ .endpoints = &spk_r_endpoint, -+ .name_prefix = "tas2783-2", -+ }, -+}; -+ -+/* HP OmniBook X Flip 14-kc0xxx (board 8EA1) */ -+static const struct snd_soc_acpi_link_adr acp70_tas2783x2_l0u9c_rt712_vb_l1u0[] = { -+ { -+ .mask = BIT(0), -+ .num_adr = ARRAY_SIZE(tas2783x2_l0u9c_adr), -+ .adr_d = tas2783x2_l0u9c_adr, -+ }, -+ { -+ .mask = BIT(1), -+ .num_adr = ARRAY_SIZE(rt712_vb_l1u0_adr), -+ .adr_d = rt712_vb_l1u0_adr, -+ }, -+ {} -+}; -+ - static const struct snd_soc_acpi_endpoint rt721_endpoints[] = { - { /* Jack Playback/Capture Endpoint (AIF1) */ - .num = 0, -@@ -704,6 +754,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = { - .links = acp70_tas2783_2, - .drv_name = "amd_sdw", - }, -+ { -+ .link_mask = BIT(0) | BIT(1), -+ .links = acp70_tas2783x2_l0u9c_rt712_vb_l1u0, -+ .machine_check = snd_soc_acpi_amd_sdca_is_device_rt712_vb, -+ .drv_name = "amd_sdw", -+ }, - { - .link_mask = BIT(0) | BIT(1), - .links = acp70_rt1320_l0_rt722_l1, -diff --git a/sound/soc/amd/mach-config.h b/sound/soc/amd/mach-config.h ---- a/sound/soc/amd/mach-config.h -+++ b/sound/soc/amd/mach-config.h -@@ -28,6 +28,7 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_sdw_machines[]; - extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[]; - extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[]; - extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_sdw_machines[]; -+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp7x_sof_machines[]; - - struct config_entry { - u32 flags; -diff --git a/sound/soc/amd/raven/acp3x-i2s.c b/sound/soc/amd/raven/acp3x-i2s.c ---- a/sound/soc/amd/raven/acp3x-i2s.c -+++ b/sound/soc/amd/raven/acp3x-i2s.c -@@ -250,11 +250,17 @@ static int acp3x_i2s_trigger(struct snd_pcm_substream *substream, - return ret; - } - -+static const u64 acp3x_i2s_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A; -+ - static const struct snd_soc_dai_ops acp3x_i2s_dai_ops = { - .hw_params = acp3x_i2s_hwparams, - .trigger = acp3x_i2s_trigger, - .set_fmt = acp3x_i2s_set_fmt, - .set_tdm_slot = acp3x_i2s_set_tdm_slot, -+ .auto_selectable_formats = &acp3x_i2s_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static const struct snd_soc_component_driver acp3x_dai_component = { -diff --git a/sound/soc/amd/vangogh/acp5x-i2s.c b/sound/soc/amd/vangogh/acp5x-i2s.c ---- a/sound/soc/amd/vangogh/acp5x-i2s.c -+++ b/sound/soc/amd/vangogh/acp5x-i2s.c -@@ -337,11 +337,17 @@ static int acp5x_i2s_trigger(struct snd_pcm_substream *substream, - return ret; - } - -+static const u64 acp5x_i2s_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A; -+ - static const struct snd_soc_dai_ops acp5x_i2s_dai_ops = { - .hw_params = acp5x_i2s_hwparams, - .trigger = acp5x_i2s_trigger, - .set_fmt = acp5x_i2s_set_fmt, - .set_tdm_slot = acp5x_i2s_set_tdm_slot, -+ .auto_selectable_formats = &acp5x_i2s_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static const struct snd_soc_component_driver acp5x_dai_component = { -diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c ---- a/sound/soc/amd/yc/acp6x-mach.c -+++ b/sound/soc/amd/yc/acp6x-mach.c -@@ -795,12 +795,12 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { - } - }, - { -- .driver_data = &acp6x_card, -- .matches = { -- DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), -- DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook_ASUSLaptop M6501RR_M6501RR"), -- } -- }, -+ .driver_data = &acp6x_card, -+ .matches = { -+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), -+ DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook_ASUSLaptop M6501RR_M6501RR"), -+ } -+ }, - { - .driver_data = &acp6x_card, - .matches = { -diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c ---- a/sound/soc/apple/mca.c -+++ b/sound/soc/apple/mca.c -@@ -133,12 +133,17 @@ struct mca_cluster { - struct clk *clk_parent; - struct dma_chan *dma_chans[SNDRV_PCM_STREAM_LAST + 1]; - -- bool port_started[SNDRV_PCM_STREAM_LAST + 1]; -- int port_driver; /* The cluster driving this cluster's port */ -+ bool clk_provider; -+ -+ bool port_clk_started[SNDRV_PCM_STREAM_LAST + 1]; -+ int port_clk_driver; /* The cluster driving this cluster's port */ - - bool clocks_in_use[SNDRV_PCM_STREAM_LAST + 1]; - struct device_link *pd_link; - -+ /* In case of clock consumer FE */ -+ int syncgen_in_use; -+ - unsigned int bclk_ratio; - - /* Masks etc. picked up via the set_tdm_slot method */ -@@ -157,7 +162,7 @@ struct mca_data { - struct reset_control *rstc; - struct device_link *pd_link; - -- /* Mutex for accessing port_driver of foreign clusters */ -+ /* Mutex for accessing port_clk_driver of foreign clusters */ - struct mutex port_mutex; - - int nclusters; -@@ -210,16 +215,22 @@ static void mca_fe_early_trigger(struct snd_pcm_substream *substream, int cmd, - SERDES_STATUS_EN | SERDES_STATUS_RST, - SERDES_STATUS_RST); - /* -- * Experiments suggest that it takes at most ~1 us -- * for the bit to clear, so wait 2 us for good measure. -+ * The SERDES cluster needs a bit of time to reset itself -+ * and settle before we start poking it. This is... slow... - */ -- udelay(2); -+ udelay(25); - WARN_ON(readl_relaxed(cl->base + serdes_unit + REG_SERDES_STATUS) & - SERDES_STATUS_RST); - mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL, - FIELD_PREP(SERDES_CONF_SYNC_SEL, 0)); - mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL, - FIELD_PREP(SERDES_CONF_SYNC_SEL, cl->no + 1)); -+ /* -+ * ADMAC gets started right after this. This delay seems -+ * to be needed for that to be reliable, e.g. ensure the -+ * clock is stable? -+ */ -+ udelay(100); - break; - default: - break; -@@ -256,11 +267,29 @@ static int mca_fe_trigger(struct snd_pcm_substream *substream, int cmd, - return 0; - } - -+static int mca_fe_get_portmask(struct snd_pcm_substream *substream) -+{ -+ struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); -+ struct snd_soc_dpcm *dpcm; -+ int mask = 0; -+ -+ for_each_dpcm_be(fe, substream->stream, dpcm) { -+ int no = mca_dai_to_cluster(snd_soc_rtd_to_cpu(dpcm->be, 0))->no; -+ -+ mask |= 1 << no; -+ } -+ -+ return mask; -+} -+ - static int mca_fe_enable_clocks(struct mca_cluster *cl) - { - struct mca_data *mca = cl->host; - int ret; - -+ if (!cl->clk_provider) -+ return -EINVAL; -+ - ret = clk_prepare_enable(cl->clk_parent); - if (ret) { - dev_err(mca->dev, -@@ -311,7 +340,7 @@ static bool mca_fe_clocks_in_use(struct mca_cluster *cl) - for (i = 0; i < mca->nclusters; i++) { - be_cl = &mca->clusters[i]; - -- if (be_cl->port_driver != cl->no) -+ if (be_cl->port_clk_driver != cl->no) - continue; - - for_each_pcm_streams(stream) { -@@ -323,36 +352,6 @@ static bool mca_fe_clocks_in_use(struct mca_cluster *cl) - return false; - } - --static int mca_be_prepare(struct snd_pcm_substream *substream, -- struct snd_soc_dai *dai) --{ -- struct mca_cluster *cl = mca_dai_to_cluster(dai); -- struct mca_data *mca = cl->host; -- struct mca_cluster *fe_cl; -- int ret; -- -- if (cl->port_driver < 0) -- return -EINVAL; -- -- fe_cl = &mca->clusters[cl->port_driver]; -- -- /* -- * Typically the CODECs we are paired with will require clocks -- * to be present at time of unmute with the 'mute_stream' op -- * or at time of DAPM widget power-up. We need to enable clocks -- * here at the latest (frontend prepare would be too late). -- */ -- if (!mca_fe_clocks_in_use(fe_cl)) { -- ret = mca_fe_enable_clocks(fe_cl); -- if (ret < 0) -- return ret; -- } -- -- cl->clocks_in_use[substream->stream] = true; -- -- return 0; --} -- - static int mca_be_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) - { -@@ -360,7 +359,7 @@ static int mca_be_hw_free(struct snd_pcm_substream *substream, - struct mca_data *mca = cl->host; - struct mca_cluster *fe_cl; - -- if (cl->port_driver < 0) -+ if (cl->port_clk_driver < 0) - return -EINVAL; - - /* -@@ -368,7 +367,7 @@ static int mca_be_hw_free(struct snd_pcm_substream *substream, - * belong to the same PCM, accesses should have been - * synchronized at ASoC level. - */ -- fe_cl = &mca->clusters[cl->port_driver]; -+ fe_cl = &mca->clusters[cl->port_clk_driver]; - if (!mca_fe_clocks_in_use(fe_cl)) - return 0; /* Nothing to do */ - -@@ -380,6 +379,56 @@ static int mca_be_hw_free(struct snd_pcm_substream *substream, - return 0; - } - -+static int mca_fe_prepare(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ struct mca_cluster *cl = mca_dai_to_cluster(dai); -+ struct mca_data *mca = cl->host; -+ -+ if (cl->clk_provider) -+ return 0; -+ -+ /* Turn on the cluster power domain if not already in use */ -+ if (!cl->syncgen_in_use) { -+ int port = ffs(mca_fe_get_portmask(substream)); -+ -+ cl->pd_link = device_link_add(mca->dev, cl->pd_dev, -+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | -+ DL_FLAG_RPM_ACTIVE); -+ if (!cl->pd_link) { -+ dev_err(mca->dev, -+ "cluster %d: unable to prop-up power domain\n", cl->no); -+ return -EINVAL; -+ } -+ -+ mca_modify(cl, REG_SYNCGEN_MCLK_SEL, SYNCGEN_MCLK_SEL, BIT(port)); -+ mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN, -+ SYNCGEN_STATUS_EN); -+ } -+ cl->syncgen_in_use |= 1 << substream->stream; -+ -+ return 0; -+} -+ -+static int mca_fe_hw_free(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ struct mca_cluster *cl = mca_dai_to_cluster(dai); -+ -+ if (cl->clk_provider) -+ return 0; -+ -+ cl->syncgen_in_use &= ~(1 << substream->stream); -+ if (cl->syncgen_in_use) -+ return 0; -+ -+ mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN, 0); -+ if (cl->pd_link) -+ device_link_del(cl->pd_link); -+ -+ return 0; -+} -+ - static unsigned int mca_crop_mask(unsigned int mask, int nchans) - { - while (hweight32(mask) > nchans) -@@ -390,7 +439,7 @@ static unsigned int mca_crop_mask(unsigned int mask, int nchans) - - static int mca_configure_serdes(struct mca_cluster *cl, int serdes_unit, - unsigned int mask, int slots, int nchans, -- int slot_width, bool is_tx, int port) -+ int slot_width, bool is_tx, int portmask) - { - __iomem void *serdes_base = cl->base + serdes_unit; - u32 serdes_conf, serdes_conf_mask; -@@ -449,7 +498,7 @@ static int mca_configure_serdes(struct mca_cluster *cl, int serdes_unit, - serdes_base + REG_RX_SERDES_SLOTMASK); - writel_relaxed(~((u32)mca_crop_mask(mask, nchans)), - serdes_base + REG_RX_SERDES_SLOTMASK + 0x4); -- writel_relaxed(1 << port, -+ writel_relaxed(portmask, - serdes_base + REG_RX_SERDES_PORT); - } - -@@ -505,9 +554,18 @@ static int mca_fe_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) - u32 serdes_conf = 0; - u32 bitstart; - -- if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != -- SND_SOC_DAIFMT_BP_FP) -+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { -+ case SND_SOC_DAIFMT_BP_FP: -+ cl->clk_provider = true; -+ break; -+ -+ case SND_SOC_DAIFMT_BC_FC: -+ cl->clk_provider = false; -+ break; -+ -+ default: - goto err; -+ } - - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: -@@ -564,24 +622,6 @@ static int mca_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) - return 0; - } - --static int mca_fe_get_port(struct snd_pcm_substream *substream) --{ -- struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); -- struct snd_soc_pcm_runtime *be; -- struct snd_soc_dpcm *dpcm; -- -- be = NULL; -- for_each_dpcm_be(fe, substream->stream, dpcm) { -- be = dpcm->be; -- break; -- } -- -- if (!be) -- return -EINVAL; -- -- return mca_dai_to_cluster(snd_soc_rtd_to_cpu(be, 0))->no; --} -- - static int mca_fe_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -@@ -595,7 +635,7 @@ static int mca_fe_hw_params(struct snd_pcm_substream *substream, - unsigned long bclk_ratio; - unsigned int tdm_slots, tdm_slot_width, tdm_mask; - u32 regval, pad; -- int ret, port, nchans_ceiled; -+ int ret, portmask, nchans_ceiled; - - if (!cl->tdm_slot_width) { - /* -@@ -644,13 +684,13 @@ static int mca_fe_hw_params(struct snd_pcm_substream *substream, - tdm_mask = (1 << tdm_slots) - 1; - } - -- port = mca_fe_get_port(substream); -- if (port < 0) -- return port; -+ portmask = mca_fe_get_portmask(substream); -+ if (!portmask) -+ return -EINVAL; - - ret = mca_configure_serdes(cl, is_tx ? CLUSTER_TX_OFF : CLUSTER_RX_OFF, - tdm_mask, tdm_slots, params_channels(params), -- tdm_slot_width, is_tx, port); -+ tdm_slot_width, is_tx, portmask); - if (ret) - return ret; - -@@ -699,6 +739,19 @@ static int mca_fe_hw_params(struct snd_pcm_substream *substream, - return 0; - } - -+static const u64 mca_fe_selectable_formats[] = -+{ -+ /* pattern 1 */ -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_NB_IF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_IF, -+ -+ /* pattern 2 */ -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_NF -+}; -+ - static const struct snd_soc_dai_ops mca_fe_ops = { - .startup = mca_fe_startup, - .set_fmt = mca_fe_set_fmt, -@@ -706,66 +759,128 @@ static const struct snd_soc_dai_ops mca_fe_ops = { - .set_tdm_slot = mca_fe_set_tdm_slot, - .hw_params = mca_fe_hw_params, - .trigger = mca_fe_trigger, -+ .prepare = mca_fe_prepare, -+ .hw_free = mca_fe_hw_free, -+ .auto_selectable_formats = mca_fe_selectable_formats, -+ .num_auto_selectable_formats = ARRAY_SIZE(mca_fe_selectable_formats), - }; - --static bool mca_be_started(struct mca_cluster *cl) -+/* -+ * Is there a FE attached which will be feeding this port's clocks? -+ */ -+static bool mca_be_clk_started(struct mca_cluster *cl) - { - int stream; - - for_each_pcm_streams(stream) -- if (cl->port_started[stream]) -+ if (cl->port_clk_started[stream]) - return true; - return false; - } - -+static struct snd_soc_pcm_runtime *mca_be_get_fe(struct snd_soc_pcm_runtime *be, -+ int stream) -+{ -+ struct snd_soc_pcm_runtime *fe = NULL; -+ struct snd_soc_dpcm *dpcm; -+ -+ for_each_dpcm_fe(be, stream, dpcm) { -+ if (fe && dpcm->fe != fe) { -+ dev_err(be->dev, "many FE per one BE unsupported\n"); -+ return NULL; -+ } -+ -+ fe = dpcm->fe; -+ } -+ -+ return fe; -+} -+ -+static int mca_be_prepare(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream); -+ struct snd_soc_pcm_runtime *fe = mca_be_get_fe(be, substream->stream); -+ struct mca_cluster *cl = mca_dai_to_cluster(dai); -+ struct mca_data *mca = cl->host; -+ struct mca_cluster *fe_cl, *fe_clk_cl; -+ int ret; -+ -+ fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0)); -+ -+ if (!fe_cl->clk_provider) -+ return 0; -+ -+ if (cl->port_clk_driver < 0) -+ return 0; -+ -+ fe_clk_cl = &mca->clusters[cl->port_clk_driver]; -+ -+ /* -+ * Typically the CODECs we are paired with will require clocks -+ * to be present at time of unmute with the 'mute_stream' op -+ * or at time of DAPM widget power-up. We need to enable clocks -+ * here at the latest (frontend prepare would be too late). -+ */ -+ if (!mca_fe_clocks_in_use(fe_clk_cl)) { -+ ret = mca_fe_enable_clocks(fe_clk_cl); -+ if (ret < 0) -+ return ret; -+ } -+ -+ cl->clocks_in_use[substream->stream] = true; -+ -+ return 0; -+} -+ - static int mca_be_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) - { - struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream); -- struct snd_soc_pcm_runtime *fe; -+ struct snd_soc_pcm_runtime *fe = mca_be_get_fe(be, substream->stream); - struct mca_cluster *cl = mca_dai_to_cluster(dai); - struct mca_cluster *fe_cl; - struct mca_data *mca = cl->host; -- struct snd_soc_dpcm *dpcm; -- -- fe = NULL; -- -- for_each_dpcm_fe(be, substream->stream, dpcm) { -- if (fe && dpcm->fe != fe) { -- dev_err(mca->dev, "many FE per one BE unsupported\n"); -- return -EINVAL; -- } -- -- fe = dpcm->fe; -- } - - if (!fe) - return -EINVAL; -- - fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0)); - -- if (mca_be_started(cl)) { -+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { -+ writel_relaxed(PORT_DATA_SEL_TXA(fe_cl->no), -+ cl->base + REG_PORT_DATA_SEL); -+ mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_TX_DATA, -+ PORT_ENABLES_TX_DATA); -+ } -+ -+ if (!fe_cl->clk_provider) -+ return 0; -+ -+ if (mca_be_clk_started(cl)) { - /* - * Port is already started in the other direction. - * Make sure there isn't a conflict with another cluster -- * driving the port. -+ * driving the port clocks. - */ -- if (cl->port_driver != fe_cl->no) -+ if (cl->port_clk_driver != fe_cl->no) - return -EINVAL; - -- cl->port_started[substream->stream] = true; -+ cl->port_clk_started[substream->stream] = true; - return 0; - } - -- writel_relaxed(PORT_ENABLES_CLOCKS | PORT_ENABLES_TX_DATA, -- cl->base + REG_PORT_ENABLES); - writel_relaxed(FIELD_PREP(PORT_CLOCK_SEL, fe_cl->no + 1), - cl->base + REG_PORT_CLOCK_SEL); -+ - writel_relaxed(PORT_DATA_SEL_TXA(fe_cl->no), - cl->base + REG_PORT_DATA_SEL); -+ -+ mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_CLOCKS, -+ PORT_ENABLES_CLOCKS); -+ - scoped_guard(mutex, &mca->port_mutex) -- cl->port_driver = fe_cl->no; -- cl->port_started[substream->stream] = true; -+ cl->port_clk_driver = fe_cl->no; -+ cl->port_clk_started[substream->stream] = true; - - return 0; - } -@@ -773,20 +888,34 @@ static int mca_be_startup(struct snd_pcm_substream *substream, - static void mca_be_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) - { -+ struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream); -+ struct snd_soc_pcm_runtime *fe = mca_be_get_fe(be, substream->stream); - struct mca_cluster *cl = mca_dai_to_cluster(dai); -+ struct mca_cluster *fe_cl; - struct mca_data *mca = cl->host; - -- cl->port_started[substream->stream] = false; -+ if (!fe) -+ return; -+ fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0)); - -- if (!mca_be_started(cl)) { -+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { -+ mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_TX_DATA, 0); -+ writel_relaxed(0, cl->base + REG_PORT_DATA_SEL); -+ } -+ -+ if (!fe_cl->clk_provider) -+ return; -+ -+ cl->port_clk_started[substream->stream] = false; -+ if (!mca_be_clk_started(cl)) { - /* - * Were we the last direction to shutdown? -- * Turn off the lights. -+ * Turn off the lights (clocks). - */ -- writel_relaxed(0, cl->base + REG_PORT_ENABLES); -- writel_relaxed(0, cl->base + REG_PORT_DATA_SEL); -+ mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_CLOCKS, 0); -+ writel_relaxed(0, cl->base + REG_PORT_CLOCK_SEL); - scoped_guard(mutex, &mca->port_mutex) -- cl->port_driver = -1; -+ cl->port_clk_driver = -1; - } - } - -@@ -1092,7 +1221,7 @@ static int apple_mca_probe(struct platform_device *pdev) - cl->host = mca; - cl->no = i; - cl->base = base + CLUSTER_STRIDE * i; -- cl->port_driver = -1; -+ cl->port_clk_driver = -1; - cl->clk_parent = of_clk_get(pdev->dev.of_node, i); - if (IS_ERR(cl->clk_parent)) { - dev_err(&pdev->dev, "unable to obtain clock %d: %ld\n", -diff --git a/sound/soc/atmel/atmel-i2s.c b/sound/soc/atmel/atmel-i2s.c ---- a/sound/soc/atmel/atmel-i2s.c -+++ b/sound/soc/atmel/atmel-i2s.c -@@ -540,12 +540,16 @@ static int atmel_i2s_dai_probe(struct snd_soc_dai *dai) - return 0; - } - -+static const u64 atmel_i2s_selectable_formats = SND_SOC_POSSIBLE_DAIFMT_I2S; -+ - static const struct snd_soc_dai_ops atmel_i2s_dai_ops = { - .probe = atmel_i2s_dai_probe, - .prepare = atmel_i2s_prepare, - .trigger = atmel_i2s_trigger, - .hw_params = atmel_i2s_hw_params, - .set_fmt = atmel_i2s_set_dai_fmt, -+ .auto_selectable_formats = &atmel_i2s_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static struct snd_soc_dai_driver atmel_i2s_dai = { -diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c ---- a/sound/soc/atmel/atmel_ssc_dai.c -+++ b/sound/soc/atmel/atmel_ssc_dai.c -@@ -825,6 +825,11 @@ static int atmel_ssc_resume(struct snd_soc_component *component) - #define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ - SNDRV_PCM_FMTBIT_S32_LE) - -+static const u64 atmel_ssc_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A; -+ - static const struct snd_soc_dai_ops atmel_ssc_dai_ops = { - .startup = atmel_ssc_startup, - .shutdown = atmel_ssc_shutdown, -@@ -833,6 +838,8 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = { - .hw_params = atmel_ssc_hw_params, - .set_fmt = atmel_ssc_set_dai_fmt, - .set_clkdiv = atmel_ssc_set_dai_clkdiv, -+ .auto_selectable_formats = &atmel_ssc_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static struct snd_soc_dai_driver atmel_ssc_dai = { -diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c ---- a/sound/soc/atmel/atmel_wm8904.c -+++ b/sound/soc/atmel/atmel_wm8904.c -@@ -82,32 +82,33 @@ static struct snd_soc_card atmel_asoc_wm8904_card = { - - static int atmel_asoc_wm8904_dt_init(struct platform_device *pdev) - { -- struct device_node *np = pdev->dev.of_node; -+ struct device *dev = &pdev->dev; -+ struct device_node *np = dev->of_node; - struct device_node *codec_np, *cpu_np; - struct snd_soc_card *card = &atmel_asoc_wm8904_card; - struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink; - int ret; - - if (!np) { -- dev_err(&pdev->dev, "only device tree supported\n"); -+ dev_err(dev, "only device tree supported\n"); - return -EINVAL; - } - - ret = snd_soc_of_parse_card_name(card, "atmel,model"); - if (ret) { -- dev_err(&pdev->dev, "failed to parse card name\n"); -+ dev_err(dev, "failed to parse card name\n"); - return ret; - } - - ret = snd_soc_of_parse_audio_routing(card, "atmel,audio-routing"); - if (ret) { -- dev_err(&pdev->dev, "failed to parse audio routing\n"); -+ dev_err(dev, "failed to parse audio routing\n"); - return ret; - } - - cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0); - if (!cpu_np) { -- dev_err(&pdev->dev, "failed to get dai and pcm info\n"); -+ dev_err(dev, "failed to get dai and pcm info\n"); - ret = -EINVAL; - return ret; - } -@@ -117,7 +118,7 @@ static int atmel_asoc_wm8904_dt_init(struct platform_device *pdev) - - codec_np = of_parse_phandle(np, "atmel,audio-codec", 0); - if (!codec_np) { -- dev_err(&pdev->dev, "failed to get codec info\n"); -+ dev_err(dev, "failed to get codec info\n"); - ret = -EINVAL; - return ret; - } -diff --git a/sound/soc/atmel/mchp-i2s-mcc.c b/sound/soc/atmel/mchp-i2s-mcc.c ---- a/sound/soc/atmel/mchp-i2s-mcc.c -+++ b/sound/soc/atmel/mchp-i2s-mcc.c -@@ -912,6 +912,13 @@ static int mchp_i2s_mcc_dai_probe(struct snd_soc_dai *dai) - return 0; - } - -+static const u64 mchp_i2s_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A | -+ SND_SOC_POSSIBLE_DAIFMT_GATED | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF; -+ - static const struct snd_soc_dai_ops mchp_i2s_mcc_dai_ops = { - .probe = mchp_i2s_mcc_dai_probe, - .set_sysclk = mchp_i2s_mcc_set_sysclk, -@@ -922,6 +929,8 @@ static const struct snd_soc_dai_ops mchp_i2s_mcc_dai_ops = { - .hw_free = mchp_i2s_mcc_hw_free, - .set_fmt = mchp_i2s_mcc_set_dai_fmt, - .set_tdm_slot = mchp_i2s_mcc_set_dai_tdm_slot, -+ .auto_selectable_formats = &mchp_i2s_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - #define MCHP_I2SMCC_RATES SNDRV_PCM_RATE_8000_192000 -diff --git a/sound/soc/atmel/mchp-pdmc.c b/sound/soc/atmel/mchp-pdmc.c ---- a/sound/soc/atmel/mchp-pdmc.c -+++ b/sound/soc/atmel/mchp-pdmc.c -@@ -741,6 +741,8 @@ static int mchp_pdmc_pcm_new(struct snd_soc_pcm_runtime *rtd, - return ret; - } - -+static const u64 mchp_selectable_formats = SND_SOC_POSSIBLE_DAIFMT_PDM; -+ - static const struct snd_soc_dai_ops mchp_pdmc_dai_ops = { - .probe = mchp_pdmc_dai_probe, - .set_fmt = mchp_pdmc_set_fmt, -@@ -748,6 +750,8 @@ static const struct snd_soc_dai_ops mchp_pdmc_dai_ops = { - .hw_params = mchp_pdmc_hw_params, - .trigger = mchp_pdmc_trigger, - .pcm_new = &mchp_pdmc_pcm_new, -+ .auto_selectable_formats = &mchp_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static struct snd_soc_dai_driver mchp_pdmc_dai = { -diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c ---- a/sound/soc/au1x/db1200.c -+++ b/sound/soc/au1x/db1200.c -@@ -42,7 +42,7 @@ static const struct platform_device_id db1200_pids[] = { - .name = "db1550-i2s", - .driver_data = 5, - }, -- {}, -+ { } - }; - MODULE_DEVICE_TABLE(platform, db1200_pids); - -diff --git a/sound/soc/au1x/i2sc.c b/sound/soc/au1x/i2sc.c ---- a/sound/soc/au1x/i2sc.c -+++ b/sound/soc/au1x/i2sc.c -@@ -202,11 +202,22 @@ static int au1xi2s_startup(struct snd_pcm_substream *substream, - return 0; - } - -+static const u64 au1xi2s_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_NB_IF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_IF; -+ - static const struct snd_soc_dai_ops au1xi2s_dai_ops = { - .startup = au1xi2s_startup, - .trigger = au1xi2s_trigger, - .hw_params = au1xi2s_hw_params, - .set_fmt = au1xi2s_set_fmt, -+ .auto_selectable_formats = &au1xi2s_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static struct snd_soc_dai_driver au1xi2s_dai_driver = { -diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c ---- a/sound/soc/au1x/psc-ac97.c -+++ b/sound/soc/au1x/psc-ac97.c -@@ -210,9 +210,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, - { - struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai); - unsigned long r, ro, stat; -- int chans, t, stype = substream->stream; -- -- chans = params_channels(params); -+ int t, stype = substream->stream; - - r = ro = __raw_readl(AC97_CFG(pscdata)); - stat = __raw_readl(AC97_STAT(pscdata)); -diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c ---- a/sound/soc/au1x/psc-i2s.c -+++ b/sound/soc/au1x/psc-i2s.c -@@ -262,11 +262,22 @@ static int au1xpsc_i2s_startup(struct snd_pcm_substream *substream, - return 0; - } - -+static const u64 au1xpsc_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_NB_IF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_IF; -+ - static const struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = { - .startup = au1xpsc_i2s_startup, - .trigger = au1xpsc_i2s_trigger, - .hw_params = au1xpsc_i2s_hw_params, - .set_fmt = au1xpsc_i2s_set_fmt, -+ .auto_selectable_formats = &au1xpsc_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static const struct snd_soc_dai_driver au1xpsc_i2s_dai_template = { -diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c ---- a/sound/soc/bcm/bcm2835-i2s.c -+++ b/sound/soc/bcm/bcm2835-i2s.c -@@ -748,6 +748,17 @@ static int bcm2835_i2s_dai_probe(struct snd_soc_dai *dai) - return 0; - } - -+static const u64 bcm2835_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_B | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_NB_IF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_IF; -+ - static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = { - .probe = bcm2835_i2s_dai_probe, - .startup = bcm2835_i2s_startup, -@@ -758,6 +769,8 @@ static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = { - .set_fmt = bcm2835_i2s_set_dai_fmt, - .set_bclk_ratio = bcm2835_i2s_set_dai_bclk_ratio, - .set_tdm_slot = bcm2835_i2s_set_dai_tdm_slot, -+ .auto_selectable_formats = &bcm2835_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static struct snd_soc_dai_driver bcm2835_i2s_dai = { -diff --git a/sound/soc/bcm/cygnus-ssp.c b/sound/soc/bcm/cygnus-ssp.c ---- a/sound/soc/bcm/cygnus-ssp.c -+++ b/sound/soc/bcm/cygnus-ssp.c -@@ -1133,6 +1133,11 @@ static int cygnus_ssp_resume(struct snd_soc_component *component) - #define cygnus_ssp_resume NULL - #endif - -+static const u64 cygnus_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_B; -+ - static const struct snd_soc_dai_ops cygnus_ssp_dai_ops = { - .startup = cygnus_ssp_startup, - .shutdown = cygnus_ssp_shutdown, -@@ -1141,6 +1146,8 @@ static const struct snd_soc_dai_ops cygnus_ssp_dai_ops = { - .set_fmt = cygnus_ssp_set_fmt, - .set_sysclk = cygnus_ssp_set_sysclk, - .set_tdm_slot = cygnus_set_dai_tdm_slot, -+ .auto_selectable_formats = &cygnus_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static const struct snd_soc_dai_ops cygnus_spdif_dai_ops = { -diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c ---- a/sound/soc/cirrus/ep93xx-i2s.c -+++ b/sound/soc/cirrus/ep93xx-i2s.c -@@ -401,6 +401,15 @@ static int ep93xx_i2s_resume(struct snd_soc_component *component) - #define ep93xx_i2s_resume NULL - #endif - -+static const u64 ep93xx_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_NB_IF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_IF; -+ - static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = { - .probe = ep93xx_i2s_dai_probe, - .startup = ep93xx_i2s_startup, -@@ -408,6 +417,8 @@ static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = { - .hw_params = ep93xx_i2s_hw_params, - .set_sysclk = ep93xx_i2s_set_sysclk, - .set_fmt = ep93xx_i2s_set_dai_fmt, -+ .auto_selectable_formats = &ep93xx_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - #define EP93XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S32_LE) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig -@@ -238,6 +238,7 @@ config SND_SOC_ALL_CODECS - imply SND_SOC_RT715_SDCA_SDW - imply SND_SOC_RT721_SDCA_SDW - imply SND_SOC_RT722_SDCA_SDW -+ imply SND_SOC_RT766_SDCA_SDW - imply SND_SOC_RT1308_SDW - imply SND_SOC_RT1316_SDW - imply SND_SOC_RT1318 -@@ -367,6 +368,7 @@ config SND_SOC_ALL_CODECS - imply SND_SOC_WSA881X - imply SND_SOC_WSA883X - imply SND_SOC_WSA884X -+ imply SND_SOC_WSA885X - imply SND_SOC_ZL38060 - help - Normally ASoC codec drivers are only built if a machine driver which -@@ -615,8 +617,8 @@ config SND_SOC_AK4613 - depends on I2C - - config SND_SOC_AK4619 -- tristate "AKM AK4619 CODEC" -- depends on I2C -+ tristate "AKM AK4619 CODEC" -+ depends on I2C - - config SND_SOC_AK4642 - tristate "AKM AK4642 CODEC" -@@ -726,6 +728,10 @@ config SND_SOC_AW87390 +@@ -726,6 +726,10 @@ config SND_SOC_AW87390 sound quality, which is a new high efficiency, low noise, constant large volume, 6th Smart K audio amplifier. @@ -11130,7 +7775,7 @@ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig config SND_SOC_AW88399 tristate "Soc Audio for awinic aw88399" depends on I2C -@@ -733,6 +739,7 @@ config SND_SOC_AW88399 +@@ -733,6 +737,7 @@ config SND_SOC_AW88399 select REGMAP_I2C select GPIOLIB select SND_SOC_AW88395_LIB @@ -11138,127 +7783,6 @@ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig help This option enables support for aw88399 Smart PA. The awinic AW88399 is an I2S/TDM input, high efficiency -@@ -889,6 +896,7 @@ config SND_SOC_CS35L56_SDW - tristate "Cirrus Logic CS35L56 CODEC (SDW)" - depends on SOUNDWIRE - select REGMAP_SOUNDWIRE -+ select IRQ_DOMAIN - select SND_SOC_CS35L56 - select SND_SOC_CS35L56_SHARED - help -@@ -1201,16 +1209,16 @@ config SND_SOC_JZ4725B_CODEC - will be called snd-soc-jz4725b-codec. - - config SND_SOC_JZ4760_CODEC -- depends on MACH_INGENIC || COMPILE_TEST -- depends on OF -- select REGMAP -- tristate "Ingenic JZ4760 internal CODEC" -- help -- Enable support for the internal CODEC found in the JZ4760 SoC -- from Ingenic. -+ depends on MACH_INGENIC || COMPILE_TEST -+ depends on OF -+ select REGMAP -+ tristate "Ingenic JZ4760 internal CODEC" -+ help -+ Enable support for the internal CODEC found in the JZ4760 SoC -+ from Ingenic. - -- This driver can also be built as a module. If so, the module -- will be called snd-soc-jz4760-codec. -+ This driver can also be built as a module. If so, the module -+ will be called snd-soc-jz4760-codec. - - config SND_SOC_JZ4770_CODEC - depends on MACH_INGENIC || COMPILE_TEST -@@ -1888,7 +1896,7 @@ config SND_SOC_RT5670 - depends on I2C - - config SND_SOC_RT5677 -- tristate -+ tristate "Realtek RT5677 Codec" - depends on I2C - select REGMAP_I2C - select REGMAP_IRQ -@@ -1978,6 +1986,15 @@ config SND_SOC_RT715_SDCA_SDW - select REGMAP_SOUNDWIRE - select REGMAP_SOUNDWIRE_MBQ - -+config SND_SOC_RT766_SDCA_SDW -+ tristate "Realtek RT766 SDCA Codec - SDW" -+ depends on SOUNDWIRE -+ depends on SND_SOC_SDCA -+ select SND_SOC_SDCA_HID -+ select SND_SOC_SDCA_IRQ -+ select REGMAP_SOUNDWIRE -+ select REGMAP_SOUNDWIRE_MBQ -+ - config SND_SOC_RT9120 - tristate "Richtek RT9120 Stereo Class-D Amplifier" - depends on I2C -@@ -2297,8 +2314,8 @@ config SND_SOC_TLV320ADC3XXX - depends on I2C - depends on GPIOLIB - help -- Enable support for Texas Instruments TLV320ADC3001 and TLV320ADC3101 -- ADCs. -+ Enable support for Texas Instruments TLV320ADC3001 and TLV320ADC3101 -+ ADCs. - - config SND_SOC_TLV320AIC23 - tristate -@@ -2771,6 +2788,16 @@ config SND_SOC_WSA884X - This enables support for Qualcomm WSA8840/WSA8845/WSA8845H Class-D - Smart Speaker Amplifier. - -+config SND_SOC_WSA885X -+ tristate "WSA885X Codec" -+ depends on I2C -+ select REGMAP_I2C -+ help -+ This enables support for Qualcomm WSA885X Stereo Smart Speaker -+ Amplifier. The codec driver programs the amplifier register -+ map and exposes the DAI and mixer controls used by Qualcomm -+ audio machine drivers. -+ - config SND_SOC_ZL38060 - tristate "Microsemi ZL38060 Connected Home Audio Processor" - depends on SPI_MASTER -@@ -2894,28 +2921,32 @@ config SND_SOC_TPA6130A2 - depends on I2C - - config SND_SOC_LPASS_MACRO_COMMON -- tristate -+ tristate - - config SND_SOC_LPASS_WSA_MACRO - depends on COMMON_CLK -+ depends on PM_CLK - select REGMAP_MMIO - select SND_SOC_LPASS_MACRO_COMMON - tristate "Qualcomm WSA Macro in LPASS(Low Power Audio SubSystem)" - - config SND_SOC_LPASS_VA_MACRO - depends on COMMON_CLK -+ depends on PM_CLK - select REGMAP_MMIO - select SND_SOC_LPASS_MACRO_COMMON - tristate "Qualcomm VA Macro in LPASS(Low Power Audio SubSystem)" - - config SND_SOC_LPASS_RX_MACRO - depends on COMMON_CLK -+ depends on PM_CLK - select REGMAP_MMIO - select SND_SOC_LPASS_MACRO_COMMON - tristate "Qualcomm RX Macro in LPASS(Low Power Audio SubSystem)" - - config SND_SOC_LPASS_TX_MACRO - depends on COMMON_CLK -+ depends on PM_CLK - select REGMAP_MMIO - select SND_SOC_LPASS_MACRO_COMMON - tristate "Qualcomm TX Macro in LPASS(Low Power Audio SubSystem)" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -11270,23 +7794,7 @@ diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile snd-soc-bd28623-y := bd28623.o snd-soc-bt-sco-y := bt-sco.o snd-soc-chv3-codec-y := chv3-codec.o -@@ -284,6 +285,7 @@ snd-soc-rt715-y := rt715.o rt715-sdw.o - snd-soc-rt715-sdca-y := rt715-sdca.o rt715-sdca-sdw.o - snd-soc-rt721-sdca-y := rt721-sdca.o rt721-sdca-sdw.o - snd-soc-rt722-sdca-y := rt722-sdca.o rt722-sdca-sdw.o -+snd-soc-rt766-sdca-y := rt766-sdca.o rt766-sdca-sdw.o - snd-soc-rt9120-y := rt9120.o - snd-soc-rt9123-y := rt9123.o - snd-soc-rt9123p-y := rt9123p.o -@@ -426,6 +428,7 @@ snd-soc-wm-hubs-y := wm_hubs.o - snd-soc-wsa881x-y := wsa881x.o - snd-soc-wsa883x-y := wsa883x.o - snd-soc-wsa884x-y := wsa884x.o -+snd-soc-wsa885x-y := wsa885x.o - snd-soc-zl38060-y := zl38060.o - # Amp - snd-soc-max9877-y := max9877.o -@@ -496,6 +499,7 @@ obj-$(CONFIG_SND_SOC_AW88395) +=snd-soc-aw88395.o +@@ -496,6 +497,7 @@ obj-$(CONFIG_SND_SOC_AW88395) +=snd-soc-aw88395.o obj-$(CONFIG_SND_SOC_AW88166) +=snd-soc-aw88166.o obj-$(CONFIG_SND_SOC_AW88261) +=snd-soc-aw88261.o obj-$(CONFIG_SND_SOC_AW88399) += snd-soc-aw88399.o @@ -11294,1216 +7802,11 @@ diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile obj-$(CONFIG_SND_SOC_BD28623) += snd-soc-bd28623.o obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o obj-$(CONFIG_SND_SOC_CHV3_CODEC) += snd-soc-chv3-codec.o -@@ -724,6 +728,7 @@ obj-$(CONFIG_SND_SOC_RT715) += snd-soc-rt715.o - obj-$(CONFIG_SND_SOC_RT715_SDCA_SDW) += snd-soc-rt715-sdca.o - obj-$(CONFIG_SND_SOC_RT721_SDCA_SDW) += snd-soc-rt721-sdca.o - obj-$(CONFIG_SND_SOC_RT722_SDCA_SDW) += snd-soc-rt722-sdca.o -+obj-$(CONFIG_SND_SOC_RT766_SDCA_SDW) += snd-soc-rt766-sdca.o - obj-$(CONFIG_SND_SOC_RT9120) += snd-soc-rt9120.o - obj-$(CONFIG_SND_SOC_RT9123) += snd-soc-rt9123.o - obj-$(CONFIG_SND_SOC_RT9123P) += snd-soc-rt9123p.o -@@ -876,6 +881,7 @@ obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o - obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o - obj-$(CONFIG_SND_SOC_WSA883X) += snd-soc-wsa883x.o - obj-$(CONFIG_SND_SOC_WSA884X) += snd-soc-wsa884x.o -+obj-$(CONFIG_SND_SOC_WSA885X) += snd-soc-wsa885x.o - obj-$(CONFIG_SND_SOC_ZL38060) += snd-soc-zl38060.o - - # Amp -diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c ---- a/sound/soc/codecs/ab8500-codec.c -+++ b/sound/soc/codecs/ab8500-codec.c -@@ -14,6 +14,7 @@ - * for ST-Ericsson. - */ - -+#include - #include - #include - #include -@@ -989,9 +990,8 @@ static int sid_status_control_get(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev); - -- mutex_lock(&drvdata->ctrl_lock); -+ guard(mutex)(&drvdata->ctrl_lock); - ucontrol->value.enumerated.item[0] = drvdata->sid_status; -- mutex_unlock(&drvdata->ctrl_lock); - - return 0; - } -@@ -1014,7 +1014,7 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol, - return -EIO; - } - -- mutex_lock(&drvdata->ctrl_lock); -+ guard(mutex)(&drvdata->ctrl_lock); - - sidconf = snd_soc_component_read(component, AB8500_SIDFIRCONF); - if (((sidconf & BIT(AB8500_SIDFIRCONF_FIRSIDBUSY)) != 0)) { -@@ -1025,7 +1025,8 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol, - } else { - status = -EBUSY; - } -- goto out; -+ dev_dbg(component->dev, "%s: Exit\n", __func__); -+ return status; - } - - snd_soc_component_write(component, AB8500_SIDFIRADR, 0); -@@ -1043,9 +1044,6 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol, - - drvdata->sid_status = SID_FIR_CONFIGURED; - --out: -- mutex_unlock(&drvdata->ctrl_lock); -- - dev_dbg(component->dev, "%s: Exit\n", __func__); - - return status; -diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c ---- a/sound/soc/codecs/ad1836.c -+++ b/sound/soc/codecs/ad1836.c -@@ -193,9 +193,15 @@ static int ad1836_hw_params(struct snd_pcm_substream *substream, - return 0; - } - -+static const u64 ad1836_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A | -+ SND_SOC_POSSIBLE_DAIFMT_IB_IF; -+ - static const struct snd_soc_dai_ops ad1836_dai_ops = { - .hw_params = ad1836_hw_params, - .set_fmt = ad1836_set_dai_fmt, -+ .auto_selectable_formats = &ad1836_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - #define AD183X_DAI(_name, num_dacs, num_adcs) \ -diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c ---- a/sound/soc/codecs/ad193x.c -+++ b/sound/soc/codecs/ad193x.c -@@ -394,6 +394,14 @@ static int ad193x_startup(struct snd_pcm_substream *substream, - &constr); - } - -+static const u64 ad193x_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_NB_IF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_IF; -+ - static const struct snd_soc_dai_ops ad193x_dai_ops = { - .startup = ad193x_startup, - .hw_params = ad193x_hw_params, -@@ -401,6 +409,8 @@ static const struct snd_soc_dai_ops ad193x_dai_ops = { - .set_tdm_slot = ad193x_set_tdm_slot, - .set_sysclk = ad193x_set_dai_sysclk, - .set_fmt = ad193x_set_dai_fmt, -+ .auto_selectable_formats = &ad193x_selectable_formats, -+ .num_auto_selectable_formats = 1, - .no_capture_mute = 1, - }; - -diff --git a/sound/soc/codecs/adau7118-hw.c b/sound/soc/codecs/adau7118-hw.c ---- a/sound/soc/codecs/adau7118-hw.c -+++ b/sound/soc/codecs/adau7118-hw.c -@@ -22,7 +22,7 @@ static const struct of_device_id adau7118_of_match[] = { - MODULE_DEVICE_TABLE(of, adau7118_of_match); - - static const struct platform_device_id adau7118_id[] = { -- { .name = "adau7118" }, -+ { .name = "adau7118" }, - { } - }; - MODULE_DEVICE_TABLE(platform, adau7118_id); -diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c ---- a/sound/soc/codecs/adav80x.c -+++ b/sound/soc/codecs/adav80x.c -@@ -743,11 +743,19 @@ static void adav80x_dai_shutdown(struct snd_pcm_substream *substream, - adav80x->rate = 0; - } - -+static const u64 adav80x_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF; -+ - static const struct snd_soc_dai_ops adav80x_dai_ops = { - .set_fmt = adav80x_set_dai_fmt, - .hw_params = adav80x_hw_params, - .startup = adav80x_dai_startup, - .shutdown = adav80x_dai_shutdown, -+ .auto_selectable_formats = &adav80x_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - #define ADAV80X_PLAYBACK_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ -diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c ---- a/sound/soc/codecs/ak4613.c -+++ b/sound/soc/codecs/ak4613.c -@@ -95,6 +95,7 @@ - * see - * AK4613_ENABLE_TDM_TEST - */ -+#include - #include - #include - #include -@@ -384,7 +385,7 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream, - struct ak4613_priv *priv = snd_soc_component_get_drvdata(component); - struct device *dev = component->dev; - -- mutex_lock(&priv->lock); -+ guard(mutex)(&priv->lock); - priv->cnt--; - if (priv->cnt < 0) { - dev_err(dev, "unexpected counter error\n"); -@@ -392,7 +393,6 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream, - } - if (!priv->cnt) - priv->ctrl1 = 0; -- mutex_unlock(&priv->lock); - } - - static void ak4613_hw_constraints(struct ak4613_priv *priv, -@@ -507,10 +507,9 @@ static int ak4613_dai_startup(struct snd_pcm_substream *substream, - struct snd_soc_component *component = dai->component; - struct ak4613_priv *priv = snd_soc_component_get_drvdata(component); - -- mutex_lock(&priv->lock); -+ guard(mutex)(&priv->lock); - ak4613_hw_constraints(priv, substream); - priv->cnt++; -- mutex_unlock(&priv->lock); - - return 0; - } -@@ -599,45 +598,47 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, - */ - ret = -EINVAL; - -- mutex_lock(&priv->lock); -- if (priv->cnt > 1) { -- /* -- * If it was already working, use current priv->ctrl1 -- */ -- ret = 0; -- } else { -- /* -- * It is not yet working, -- */ -- unsigned int channel = params_channels(params); -- u8 tdm; -+ scoped_guard(mutex, &priv->lock) { -+ if (priv->cnt > 1) { -+ /* -+ * If it was already working, use current priv->ctrl1 -+ */ -+ ret = 0; -+ } else { -+ /* -+ * It is not yet working, -+ */ -+ unsigned int channel = params_channels(params); -+ u8 tdm; - -- /* STEREO or TDM */ -- if (channel == 2) -- tdm = AK4613_CONFIG_MODE_STEREO; -- else -- tdm = AK4613_CONFIG_GET(priv, MODE); -+ /* STEREO or TDM */ -+ if (channel == 2) -+ tdm = AK4613_CONFIG_MODE_STEREO; -+ else -+ tdm = AK4613_CONFIG_GET(priv, MODE); - -- for (i = ARRAY_SIZE(ak4613_iface) - 1; i >= 0; i--) { -- const struct ak4613_interface *iface = ak4613_iface + i; -+ for (i = ARRAY_SIZE(ak4613_iface) - 1; i >= 0; i--) { -+ const struct ak4613_interface *iface = ak4613_iface + i; - -- if ((iface->fmt == fmt) && (iface->width == width)) { -- /* -- * Ctrl1 -- * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | -- * |TDM1|TDM0|DIF2|DIF1|DIF0|ATS1|ATS0|SMUTE| -- * < tdm > < iface->dif > -- */ -- priv->ctrl1 = (tdm << 6) | (iface->dif << 3); -- ret = 0; -- break; -+ if (iface->fmt == fmt && iface->width == width) { -+ /* -+ * Ctrl1 -+ * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | -+ * |TDM1|TDM0|DIF2|DIF1|DIF0|ATS1|ATS0|SMUTE| -+ * < tdm > < iface->dif > -+ */ -+ priv->ctrl1 = (tdm << 6) | (iface->dif << 3); -+ ret = 0; -+ break; -+ } - } - } - } -- mutex_unlock(&priv->lock); - -- if (ret < 0) -- goto hw_params_end; -+ if (ret < 0) { -+ dev_warn(dev, "unsupported data width/format combination\n"); -+ return ret; -+ } - - snd_soc_component_update_bits(component, CTRL1, FMT_MASK, priv->ctrl1); - snd_soc_component_update_bits(component, CTRL2, DFS_MASK, ctrl2); -@@ -645,10 +646,6 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, - snd_soc_component_update_bits(component, ICTRL, ICTRL_MASK, priv->ic); - snd_soc_component_update_bits(component, OCTRL, OCTRL_MASK, priv->oc); - --hw_params_end: -- if (ret < 0) -- dev_warn(dev, "unsupported data width/format combination\n"); -- - return ret; - } - -diff --git a/sound/soc/codecs/arizona-jack.c b/sound/soc/codecs/arizona-jack.c ---- a/sound/soc/codecs/arizona-jack.c -+++ b/sound/soc/codecs/arizona-jack.c -@@ -5,6 +5,7 @@ - * Copyright (C) 2012-2014 Wolfson Microelectronics plc - */ - -+#include - #include - #include - #include -@@ -707,15 +708,13 @@ static void arizona_micd_timeout_work(struct work_struct *work) - struct arizona_priv, - micd_timeout_work.work); - -- mutex_lock(&info->lock); -+ guard(mutex)(&info->lock); - - dev_dbg(info->arizona->dev, "MICD timed out, reporting HP\n"); - - info->detecting = false; - - arizona_identify_headphone(info); -- -- mutex_unlock(&info->lock); - } - - static int arizona_micd_adc_read(struct arizona_priv *info) -@@ -921,12 +920,11 @@ static void arizona_micd_detect(struct work_struct *work) - - cancel_delayed_work_sync(&info->micd_timeout_work); - -- mutex_lock(&info->lock); -+ guard(mutex)(&info->lock); - - /* If the cable was removed while measuring ignore the result */ - if (!(info->jack->status & SND_JACK_MECHANICAL)) { - dev_dbg(arizona->dev, "Ignoring MICDET for removed cable\n"); -- mutex_unlock(&info->lock); - return; - } - -@@ -936,7 +934,6 @@ static void arizona_micd_detect(struct work_struct *work) - arizona_button_reading(info); - - pm_runtime_mark_last_busy(arizona->dev); -- mutex_unlock(&info->lock); - } - - static irqreturn_t arizona_micdet(int irq, void *data) -@@ -948,10 +945,10 @@ static irqreturn_t arizona_micdet(int irq, void *data) - cancel_delayed_work_sync(&info->micd_detect_work); - cancel_delayed_work_sync(&info->micd_timeout_work); - -- mutex_lock(&info->lock); -- if (!info->detecting) -- debounce = 0; -- mutex_unlock(&info->lock); -+ scoped_guard(mutex, &info->lock) { -+ if (!info->detecting) -+ debounce = 0; -+ } - - if (debounce) - queue_delayed_work(system_power_efficient_wq, -@@ -969,9 +966,8 @@ static void arizona_hpdet_work(struct work_struct *work) - struct arizona_priv, - hpdet_work.work); - -- mutex_lock(&info->lock); -+ guard(mutex)(&info->lock); - arizona_start_hpdet_acc_id(info); -- mutex_unlock(&info->lock); - } - - static int arizona_hpdet_wait(struct arizona_priv *info) -@@ -1018,9 +1014,9 @@ static irqreturn_t arizona_jackdet(int irq, void *data) - cancelled_hp = cancel_delayed_work_sync(&info->hpdet_work); - cancelled_mic = cancel_delayed_work_sync(&info->micd_timeout_work); - -- pm_runtime_get_sync(arizona->dev); -+ guard(pm_runtime_active_auto)(arizona->dev); - -- mutex_lock(&info->lock); -+ guard(mutex)(&info->lock); - - if (info->micd_clamp) { - mask = ARIZONA_MICD_CLAMP_STS; -@@ -1036,8 +1032,6 @@ static irqreturn_t arizona_jackdet(int irq, void *data) - ret = regmap_read(arizona->regmap, ARIZONA_AOD_IRQ_RAW_STATUS, &val); - if (ret) { - dev_err(arizona->dev, "Failed to read jackdet status: %d\n", ret); -- mutex_unlock(&info->lock); -- pm_runtime_put_autosuspend(arizona->dev); - return IRQ_NONE; - } - -@@ -1056,62 +1050,61 @@ static irqreturn_t arizona_jackdet(int irq, void *data) - &info->micd_timeout_work, - msecs_to_jiffies(micd_timeout)); - } -+ } else { -+ info->last_jackdet = val; - -- goto out; -- } -- info->last_jackdet = val; -+ if (info->last_jackdet == present) { -+ dev_dbg(arizona->dev, "Detected jack\n"); -+ snd_soc_jack_report(info->jack, SND_JACK_MECHANICAL, SND_JACK_MECHANICAL); - -- if (info->last_jackdet == present) { -- dev_dbg(arizona->dev, "Detected jack\n"); -- snd_soc_jack_report(info->jack, SND_JACK_MECHANICAL, SND_JACK_MECHANICAL); -+ info->detecting = true; -+ info->mic = false; -+ info->jack_flips = 0; - -- info->detecting = true; -- info->mic = false; -- info->jack_flips = 0; -+ if (!arizona->pdata.hpdet_acc_id) { -+ arizona_start_mic(info); -+ } else { -+ queue_delayed_work(system_power_efficient_wq, -+ &info->hpdet_work, -+ msecs_to_jiffies(HPDET_DEBOUNCE)); -+ } - -- if (!arizona->pdata.hpdet_acc_id) { -- arizona_start_mic(info); -+ if (info->micd_clamp || !arizona->pdata.jd_invert) -+ regmap_update_bits(arizona->regmap, -+ ARIZONA_JACK_DETECT_DEBOUNCE, -+ ARIZONA_MICD_CLAMP_DB | -+ ARIZONA_JD1_DB, 0); - } else { -- queue_delayed_work(system_power_efficient_wq, -- &info->hpdet_work, -- msecs_to_jiffies(HPDET_DEBOUNCE)); -- } -+ dev_dbg(arizona->dev, "Detected jack removal\n"); -+ -+ arizona_stop_mic(info); -+ -+ info->num_hpdet_res = 0; -+ for (i = 0; i < ARRAY_SIZE(info->hpdet_res); i++) -+ info->hpdet_res[i] = 0; -+ info->mic = false; -+ info->hpdet_done = false; -+ info->hpdet_retried = false; -+ -+ snd_soc_jack_report(info->jack, 0, -+ ARIZONA_JACK_MASK | info->micd_button_mask); -+ -+ /* -+ * If the jack was removed during a headphone detection we -+ * need to wait for the headphone detection to finish, as -+ * it can not be aborted. We don't want to be able to start -+ * a new headphone detection from a fresh insert until this -+ * one is finished. -+ */ -+ arizona_hpdet_wait(info); - -- if (info->micd_clamp || !arizona->pdata.jd_invert) - regmap_update_bits(arizona->regmap, - ARIZONA_JACK_DETECT_DEBOUNCE, -- ARIZONA_MICD_CLAMP_DB | -- ARIZONA_JD1_DB, 0); -- } else { -- dev_dbg(arizona->dev, "Detected jack removal\n"); -- -- arizona_stop_mic(info); -- -- info->num_hpdet_res = 0; -- for (i = 0; i < ARRAY_SIZE(info->hpdet_res); i++) -- info->hpdet_res[i] = 0; -- info->mic = false; -- info->hpdet_done = false; -- info->hpdet_retried = false; -- -- snd_soc_jack_report(info->jack, 0, ARIZONA_JACK_MASK | info->micd_button_mask); -- -- /* -- * If the jack was removed during a headphone detection we -- * need to wait for the headphone detection to finish, as -- * it can not be aborted. We don't want to be able to start -- * a new headphone detection from a fresh insert until this -- * one is finished. -- */ -- arizona_hpdet_wait(info); -- -- regmap_update_bits(arizona->regmap, -- ARIZONA_JACK_DETECT_DEBOUNCE, -- ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB, -- ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB); -+ ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB, -+ ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB); -+ } - } - --out: - /* Clear trig_sts to make sure DCVDD is not forced up */ - regmap_write(arizona->regmap, ARIZONA_AOD_WKUP_AND_TRIG, - ARIZONA_MICD_CLAMP_FALL_TRIG_STS | -@@ -1119,10 +1112,6 @@ static irqreturn_t arizona_jackdet(int irq, void *data) - ARIZONA_JD1_FALL_TRIG_STS | - ARIZONA_JD1_RISE_TRIG_STS); - -- mutex_unlock(&info->lock); -- -- pm_runtime_put_autosuspend(arizona->dev); -- - return IRQ_HANDLED; - } - -diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c ---- a/sound/soc/codecs/arizona.c -+++ b/sound/soc/codecs/arizona.c -@@ -7,6 +7,7 @@ - * Author: Mark Brown - */ - -+#include - #include - #include - #include -@@ -1158,17 +1159,16 @@ int arizona_dvfs_up(struct snd_soc_component *component, unsigned int flags) - struct arizona_priv *priv = snd_soc_component_get_drvdata(component); - int ret = 0; - -- mutex_lock(&priv->dvfs_lock); -+ guard(mutex)(&priv->dvfs_lock); - - if (!priv->dvfs_cached && !priv->dvfs_reqs) { - ret = arizona_dvfs_enable(component); - if (ret) -- goto err; -+ return ret; - } - - priv->dvfs_reqs |= flags; --err: -- mutex_unlock(&priv->dvfs_lock); -+ - return ret; - } - EXPORT_SYMBOL_GPL(arizona_dvfs_up); -@@ -1179,7 +1179,7 @@ int arizona_dvfs_down(struct snd_soc_component *component, unsigned int flags) - unsigned int old_reqs; - int ret = 0; - -- mutex_lock(&priv->dvfs_lock); -+ guard(mutex)(&priv->dvfs_lock); - - old_reqs = priv->dvfs_reqs; - priv->dvfs_reqs &= ~flags; -@@ -1187,7 +1187,6 @@ int arizona_dvfs_down(struct snd_soc_component *component, unsigned int flags) - if (!priv->dvfs_cached && old_reqs && !priv->dvfs_reqs) - ret = arizona_dvfs_disable(component); - -- mutex_unlock(&priv->dvfs_lock); - return ret; - } - EXPORT_SYMBOL_GPL(arizona_dvfs_down); -@@ -1199,7 +1198,7 @@ int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w, - struct arizona_priv *priv = snd_soc_component_get_drvdata(component); - int ret = 0; - -- mutex_lock(&priv->dvfs_lock); -+ guard(mutex)(&priv->dvfs_lock); - - switch (event) { - case SND_SOC_DAPM_POST_PMU: -@@ -1222,7 +1221,6 @@ int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w, - break; - } - -- mutex_unlock(&priv->dvfs_lock); - return ret; - } - EXPORT_SYMBOL_GPL(arizona_dvfs_sysclk_ev); -@@ -1657,13 +1655,11 @@ static void arizona_wm5102_set_dac_comp(struct snd_soc_component *component, - { 0x80, 0x0 }, - }; - -- mutex_lock(&arizona->dac_comp_lock); -- -- dac_comp[1].def = arizona->dac_comp_coeff; -- if (rate >= 176400) -- dac_comp[2].def = arizona->dac_comp_enabled; -- -- mutex_unlock(&arizona->dac_comp_lock); -+ scoped_guard(mutex, &arizona->dac_comp_lock) { -+ dac_comp[1].def = arizona->dac_comp_coeff; -+ if (rate >= 176400) -+ dac_comp[2].def = arizona->dac_comp_enabled; -+ } - - regmap_multi_reg_write(arizona->regmap, - dac_comp, -diff --git a/sound/soc/codecs/aw87390.c b/sound/soc/codecs/aw87390.c ---- a/sound/soc/codecs/aw87390.c -+++ b/sound/soc/codecs/aw87390.c -@@ -7,6 +7,7 @@ - // Author: Weidong Wang - // - -+#include - #include - #include - #include -@@ -225,11 +226,10 @@ static int aw87390_profile_set(struct snd_kcontrol *kcontrol, - struct aw87390 *aw87390 = snd_soc_component_get_drvdata(codec); - int ret; - -- mutex_lock(&aw87390->lock); -+ guard(mutex)(&aw87390->lock); - ret = aw87390_dev_set_profile_index(aw87390->aw_pa, ucontrol->value.integer.value[0]); - if (ret) { - dev_dbg(codec->dev, "profile index does not change\n"); -- mutex_unlock(&aw87390->lock); - return 0; - } - -@@ -238,8 +238,6 @@ static int aw87390_profile_set(struct snd_kcontrol *kcontrol, - aw87390_power_on(aw87390->aw_pa); - } - -- mutex_unlock(&aw87390->lock); -- - return 1; - } - -@@ -250,7 +248,7 @@ static const struct snd_kcontrol_new aw87390_controls[] = { - - static int aw87390_request_firmware_file(struct aw87390 *aw87390) - { -- const struct firmware *cont = NULL; -+ const struct firmware *cont __free(firmware) = NULL; - int ret; - - aw87390->aw_pa->fw_status = AW87390_DEV_FW_FAILED; -@@ -265,14 +263,11 @@ static int aw87390_request_firmware_file(struct aw87390 *aw87390) - - aw87390->aw_cfg = devm_kzalloc(aw87390->aw_pa->dev, - struct_size(aw87390->aw_cfg, data, cont->size), GFP_KERNEL); -- if (!aw87390->aw_cfg) { -- release_firmware(cont); -+ if (!aw87390->aw_cfg) - return -ENOMEM; -- } - - aw87390->aw_cfg->len = cont->size; - memcpy(aw87390->aw_cfg->data, cont->data, cont->size); -- release_firmware(cont); - - ret = aw88395_dev_load_acf_check(aw87390->aw_pa, aw87390->aw_cfg); - if (ret) { -@@ -280,14 +275,12 @@ static int aw87390_request_firmware_file(struct aw87390 *aw87390) - return ret; - } - -- mutex_lock(&aw87390->lock); -+ guard(mutex)(&aw87390->lock); - - ret = aw88395_dev_cfg_load(aw87390->aw_pa, aw87390->aw_cfg); - if (ret) - dev_err(aw87390->aw_pa->dev, "aw_dev acf parse failed\n"); - -- mutex_unlock(&aw87390->lock); -- - return ret; - } - -diff --git a/sound/soc/codecs/aw88081.c b/sound/soc/codecs/aw88081.c ---- a/sound/soc/codecs/aw88081.c -+++ b/sound/soc/codecs/aw88081.c -@@ -7,6 +7,7 @@ - // Author: Weidong Wang - // - -+#include - #include - #include - #include -@@ -763,9 +764,8 @@ static void aw88081_startup_work(struct work_struct *work) - struct aw88081 *aw88081 = - container_of(work, struct aw88081, start_work.work); - -- mutex_lock(&aw88081->lock); -+ guard(mutex)(&aw88081->lock); - aw88081_start_pa(aw88081); -- mutex_unlock(&aw88081->lock); - } - - static void aw88081_start(struct aw88081 *aw88081, bool sync_start) -@@ -942,11 +942,10 @@ static int aw88081_profile_set(struct snd_kcontrol *kcontrol, - int ret; - - /* pa stop or stopping just set profile */ -- mutex_lock(&aw88081->lock); -+ guard(mutex)(&aw88081->lock); - ret = aw88081_dev_set_profile_index(aw88081->aw_pa, ucontrol->value.integer.value[0]); - if (ret) { - dev_dbg(codec->dev, "profile index does not change"); -- mutex_unlock(&aw88081->lock); - return 0; - } - -@@ -955,8 +954,6 @@ static int aw88081_profile_set(struct snd_kcontrol *kcontrol, - aw88081_start(aw88081, AW88081_SYNC_START); - } - -- mutex_unlock(&aw88081->lock); -- - return 1; - } - -@@ -1136,7 +1133,7 @@ static int aw88081_dev_init(struct aw88081 *aw88081, struct aw_container *aw_cfg - - static int aw88081_request_firmware_file(struct aw88081 *aw88081) - { -- const struct firmware *cont = NULL; -+ const struct firmware *cont __free(firmware) = NULL; - struct aw_container *aw_cfg; - int ret; - -@@ -1150,26 +1147,21 @@ static int aw88081_request_firmware_file(struct aw88081 *aw88081) - AW88081_ACF_FILE, cont ? cont->size : 0); - - aw_cfg = devm_kzalloc(aw88081->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL); -- if (!aw_cfg) { -- release_firmware(cont); -+ if (!aw_cfg) - return -ENOMEM; -- } -+ - aw_cfg->len = (int)cont->size; - memcpy(aw_cfg->data, cont->data, cont->size); - - aw88081->aw_cfg = aw_cfg; - -- release_firmware(cont); -- - ret = aw88395_dev_load_acf_check(aw88081->aw_pa, aw88081->aw_cfg); - if (ret) - return ret; - -- mutex_lock(&aw88081->lock); -- ret = aw88081_dev_init(aw88081, aw88081->aw_cfg); -- mutex_unlock(&aw88081->lock); -+ guard(mutex)(&aw88081->lock); - -- return ret; -+ return aw88081_dev_init(aw88081, aw88081->aw_cfg); - } - - static int aw88081_playback_event(struct snd_soc_dapm_widget *w, -@@ -1178,7 +1170,7 @@ static int aw88081_playback_event(struct snd_soc_dapm_widget *w, - struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); - struct aw88081 *aw88081 = snd_soc_component_get_drvdata(component); - -- mutex_lock(&aw88081->lock); -+ guard(mutex)(&aw88081->lock); - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - aw88081_start(aw88081, AW88081_ASYNC_START); -@@ -1189,7 +1181,6 @@ static int aw88081_playback_event(struct snd_soc_dapm_widget *w, - default: - break; - } -- mutex_unlock(&aw88081->lock); - - return 0; - } -diff --git a/sound/soc/codecs/aw88166.c b/sound/soc/codecs/aw88166.c ---- a/sound/soc/codecs/aw88166.c -+++ b/sound/soc/codecs/aw88166.c -@@ -7,6 +7,7 @@ - // Author: Weidong Wang - // - -+#include - #include - #include - #include -@@ -1173,9 +1174,8 @@ static void aw88166_startup_work(struct work_struct *work) - struct aw88166 *aw88166 = - container_of(work, struct aw88166, start_work.work); - -- mutex_lock(&aw88166->lock); -+ guard(mutex)(&aw88166->lock); - aw88166_start_pa(aw88166); -- mutex_unlock(&aw88166->lock); - } - - static void aw88166_start(struct aw88166 *aw88166, bool sync_start) -@@ -1413,11 +1413,10 @@ static int aw88166_profile_set(struct snd_kcontrol *kcontrol, - struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec); - int ret; - -- mutex_lock(&aw88166->lock); -+ guard(mutex)(&aw88166->lock); - ret = aw88166_dev_set_profile_index(aw88166->aw_pa, ucontrol->value.integer.value[0]); - if (ret) { - dev_dbg(codec->dev, "profile index does not change"); -- mutex_unlock(&aw88166->lock); - return 0; - } - -@@ -1426,8 +1425,6 @@ static int aw88166_profile_set(struct snd_kcontrol *kcontrol, - aw88166_start(aw88166, AW88166_SYNC_START); - } - -- mutex_unlock(&aw88166->lock); -- - return 1; - } - -@@ -1573,7 +1570,7 @@ static int aw88166_dev_init(struct aw88166 *aw88166, struct aw_container *aw_cfg - - static int aw88166_request_firmware_file(struct aw88166 *aw88166) - { -- const struct firmware *cont = NULL; -+ const struct firmware *cont __free(firmware) = NULL; - const char *fw_name; - int ret; - -@@ -1593,13 +1590,11 @@ static int aw88166_request_firmware_file(struct aw88166 *aw88166) - - aw88166->aw_cfg = devm_kzalloc(aw88166->aw_pa->dev, - struct_size(aw88166->aw_cfg, data, cont->size), GFP_KERNEL); -- if (!aw88166->aw_cfg) { -- release_firmware(cont); -+ if (!aw88166->aw_cfg) - return -ENOMEM; -- } -+ - aw88166->aw_cfg->len = (int)cont->size; - memcpy(aw88166->aw_cfg->data, cont->data, cont->size); -- release_firmware(cont); - - ret = aw88395_dev_load_acf_check(aw88166->aw_pa, aw88166->aw_cfg); - if (ret) { -@@ -1607,12 +1602,12 @@ static int aw88166_request_firmware_file(struct aw88166 *aw88166) - return ret; - } - -- mutex_lock(&aw88166->lock); -- /* aw device init */ -- ret = aw88166_dev_init(aw88166, aw88166->aw_cfg); -- if (ret) -- dev_err(aw88166->aw_pa->dev, "dev init failed\n"); -- mutex_unlock(&aw88166->lock); -+ scoped_guard(mutex, &aw88166->lock) { -+ /* aw device init */ -+ ret = aw88166_dev_init(aw88166, aw88166->aw_cfg); -+ if (ret) -+ dev_err(aw88166->aw_pa->dev, "dev init failed\n"); -+ } - - return ret; - } -@@ -1639,7 +1634,7 @@ static int aw88166_playback_event(struct snd_soc_dapm_widget *w, - struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); - struct aw88166 *aw88166 = snd_soc_component_get_drvdata(component); - -- mutex_lock(&aw88166->lock); -+ guard(mutex)(&aw88166->lock); - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - aw88166_start(aw88166, AW88166_ASYNC_START); -@@ -1650,7 +1645,6 @@ static int aw88166_playback_event(struct snd_soc_dapm_widget *w, - default: - break; - } -- mutex_unlock(&aw88166->lock); - - return 0; - } -diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c ---- a/sound/soc/codecs/aw88261.c -+++ b/sound/soc/codecs/aw88261.c -@@ -8,6 +8,7 @@ - // Author: Weidong Wang - // - -+#include - #include - #include - #include -@@ -960,11 +961,10 @@ static int aw88261_profile_set(struct snd_kcontrol *kcontrol, - int ret; - - /* pa stop or stopping just set profile */ -- mutex_lock(&aw88261->lock); -+ guard(mutex)(&aw88261->lock); - ret = aw88261_dev_set_profile_index(aw88261->aw_pa, ucontrol->value.integer.value[0]); - if (ret) { - dev_dbg(codec->dev, "profile index does not change"); -- mutex_unlock(&aw88261->lock); - return 0; - } - -@@ -973,8 +973,6 @@ static int aw88261_profile_set(struct snd_kcontrol *kcontrol, - aw88261_start(aw88261); - } - -- mutex_unlock(&aw88261->lock); -- - return 1; - } - -@@ -1038,7 +1036,7 @@ static int aw88261_playback_event(struct snd_soc_dapm_widget *w, - struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); - struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); - -- mutex_lock(&aw88261->lock); -+ guard(mutex)(&aw88261->lock); - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - aw88261_start(aw88261); -@@ -1049,7 +1047,6 @@ static int aw88261_playback_event(struct snd_soc_dapm_widget *w, - default: - break; - } -- mutex_unlock(&aw88261->lock); - - return 0; - } -@@ -1152,7 +1149,7 @@ static int aw88261_dev_init(struct aw88261 *aw88261, struct aw_container *aw_cfg - - static int aw88261_request_firmware_file(struct aw88261 *aw88261) - { -- const struct firmware *cont = NULL; -+ const struct firmware *cont __free(firmware) = NULL; - struct aw_container *aw_cfg; - const char *fw_name; - int ret; -@@ -1172,13 +1169,11 @@ static int aw88261_request_firmware_file(struct aw88261 *aw88261) - fw_name, cont ? cont->size : 0); - - aw_cfg = devm_kzalloc(aw88261->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL); -- if (!aw_cfg) { -- release_firmware(cont); -+ if (!aw_cfg) - return -ENOMEM; -- } -+ - aw_cfg->len = (int)cont->size; - memcpy(aw_cfg->data, cont->data, cont->size); -- release_firmware(cont); - - aw88261->aw_cfg = aw_cfg; - -@@ -1188,12 +1183,12 @@ static int aw88261_request_firmware_file(struct aw88261 *aw88261) - return ret; - } - -- mutex_lock(&aw88261->lock); -- /* aw device init */ -- ret = aw88261_dev_init(aw88261, aw88261->aw_cfg); -- if (ret) -- dev_err(aw88261->aw_pa->dev, "dev init failed"); -- mutex_unlock(&aw88261->lock); -+ scoped_guard(mutex, &aw88261->lock) { -+ /* aw device init */ -+ ret = aw88261_dev_init(aw88261, aw88261->aw_cfg); -+ if (ret) -+ dev_err(aw88261->aw_pa->dev, "dev init failed"); -+ } - - return ret; - } -diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c ---- a/sound/soc/codecs/aw88395/aw88395.c -+++ b/sound/soc/codecs/aw88395/aw88395.c -@@ -8,6 +8,7 @@ - // Author: Weidong Wang - // - -+#include - #include - #include - #include -@@ -51,9 +52,8 @@ static void aw88395_startup_work(struct work_struct *work) - struct aw88395 *aw88395 = - container_of(work, struct aw88395, start_work.work); - -- mutex_lock(&aw88395->lock); -+ guard(mutex)(&aw88395->lock); - aw88395_start_pa(aw88395); -- mutex_unlock(&aw88395->lock); - } - - static void aw88395_start(struct aw88395 *aw88395, bool sync_start) -@@ -224,11 +224,10 @@ static int aw88395_profile_set(struct snd_kcontrol *kcontrol, - int ret; - - /* pa stop or stopping just set profile */ -- mutex_lock(&aw88395->lock); -+ guard(mutex)(&aw88395->lock); - ret = aw88395_dev_set_profile_index(aw88395->aw_pa, ucontrol->value.integer.value[0]); - if (ret < 0) { - dev_dbg(codec->dev, "profile index does not change"); -- mutex_unlock(&aw88395->lock); - return 0; - } - -@@ -237,8 +236,6 @@ static int aw88395_profile_set(struct snd_kcontrol *kcontrol, - aw88395_start(aw88395, AW88395_SYNC_START); - } - -- mutex_unlock(&aw88395->lock); -- - return 1; - } - -@@ -366,7 +363,7 @@ static int aw88395_playback_event(struct snd_soc_dapm_widget *w, - struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); - struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component); - -- mutex_lock(&aw88395->lock); -+ guard(mutex)(&aw88395->lock); - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - aw88395_start(aw88395, AW88395_ASYNC_START); -@@ -377,7 +374,6 @@ static int aw88395_playback_event(struct snd_soc_dapm_widget *w, - default: - break; - } -- mutex_unlock(&aw88395->lock); - - return 0; - } -@@ -461,7 +457,7 @@ static void aw88395_hw_reset(struct aw88395 *aw88395) - - static int aw88395_request_firmware_file(struct aw88395 *aw88395) - { -- const struct firmware *cont = NULL; -+ const struct firmware *cont __free(firmware) = NULL; - struct aw_container *aw_cfg; - int ret; - -@@ -477,13 +473,11 @@ static int aw88395_request_firmware_file(struct aw88395 *aw88395) - AW88395_ACF_FILE, cont ? cont->size : 0); - - aw_cfg = devm_kzalloc(aw88395->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL); -- if (!aw_cfg) { -- release_firmware(cont); -+ if (!aw_cfg) - return -ENOMEM; -- } -+ - aw_cfg->len = (int)cont->size; - memcpy(aw_cfg->data, cont->data, cont->size); -- release_firmware(cont); - - aw88395->aw_cfg = aw_cfg; - -@@ -495,12 +489,12 @@ static int aw88395_request_firmware_file(struct aw88395 *aw88395) - - dev_dbg(aw88395->aw_pa->dev, "%s : bin load success\n", __func__); - -- mutex_lock(&aw88395->lock); -- /* aw device init */ -- ret = aw88395_dev_init(aw88395->aw_pa, aw88395->aw_cfg); -- if (ret < 0) -- dev_err(aw88395->aw_pa->dev, "dev init failed"); -- mutex_unlock(&aw88395->lock); -+ scoped_guard(mutex, &aw88395->lock) { -+ /* aw device init */ -+ ret = aw88395_dev_init(aw88395->aw_pa, aw88395->aw_cfg); -+ if (ret < 0) -+ dev_err(aw88395->aw_pa->dev, "dev init failed"); -+ } - - return ret; - } -diff --git a/sound/soc/codecs/aw88395/aw88395_device.c b/sound/soc/codecs/aw88395/aw88395_device.c ---- a/sound/soc/codecs/aw88395/aw88395_device.c -+++ b/sound/soc/codecs/aw88395/aw88395_device.c -@@ -8,6 +8,7 @@ - // Author: Ben Yi - // - -+#include - #include - #include - #include -@@ -70,7 +71,7 @@ int aw_dev_dsp_write(struct aw_device *aw_dev, - u32 reg_value; - int ret; - -- mutex_lock(&aw_dev->dsp_lock); -+ guard(mutex)(&aw_dev->dsp_lock); - switch (data_type) { - case AW_DSP_16_DATA: - ret = aw_dev_dsp_write_16bit(aw_dev, dsp_addr, dsp_data); -@@ -93,7 +94,6 @@ int aw_dev_dsp_write(struct aw_device *aw_dev, - /* clear dsp chip select state*/ - if (regmap_read(aw_dev->regmap, AW88395_ID_REG, ®_value)) - dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret); -- mutex_unlock(&aw_dev->dsp_lock); - - return ret; - } -@@ -156,7 +156,7 @@ int aw_dev_dsp_read(struct aw_device *aw_dev, - u32 reg_value; - int ret; - -- mutex_lock(&aw_dev->dsp_lock); -+ guard(mutex)(&aw_dev->dsp_lock); - switch (data_type) { - case AW_DSP_16_DATA: - ret = aw_dev_dsp_read_16bit(aw_dev, dsp_addr, dsp_data); -@@ -179,7 +179,6 @@ int aw_dev_dsp_read(struct aw_device *aw_dev, - /* clear dsp chip select state*/ - if (regmap_read(aw_dev->regmap, AW88395_ID_REG, ®_value)) - dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret); -- mutex_unlock(&aw_dev->dsp_lock); - - return ret; - } -@@ -1110,42 +1109,36 @@ static int aw_dev_dsp_update_container(struct aw_device *aw_dev, - #ifdef AW88395_DSP_I2C_WRITES - u32 tmp_len; - -- mutex_lock(&aw_dev->dsp_lock); -+ guard(mutex)(&aw_dev->dsp_lock); - ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, base); - if (ret) -- goto error_operation; -+ return ret; - - for (i = 0; i < len; i += AW88395_MAX_RAM_WRITE_BYTE_SIZE) { - tmp_len = min(len - i, AW88395_MAX_RAM_WRITE_BYTE_SIZE); - ret = regmap_raw_write(aw_dev->regmap, AW88395_DSPMDAT_REG, - &data[i], tmp_len); - if (ret) -- goto error_operation; -+ return ret; - } -- mutex_unlock(&aw_dev->dsp_lock); - #else - __be16 reg_val; - -- mutex_lock(&aw_dev->dsp_lock); -+ guard(mutex)(&aw_dev->dsp_lock); - /* i2c write */ - ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, base); - if (ret) -- goto error_operation; -+ return ret; - for (i = 0; i < len; i += 2) { - reg_val = cpu_to_be16p((u16 *)(data + i)); - ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, - (u16)reg_val); - if (ret) -- goto error_operation; -+ return ret; - } -- mutex_unlock(&aw_dev->dsp_lock); - #endif - - return 0; -- --error_operation: -- mutex_unlock(&aw_dev->dsp_lock); -- return ret; - } - - static int aw_dev_dsp_update_fw(struct aw_device *aw_dev, -@@ -1231,14 +1224,14 @@ static int aw_dev_check_sram(struct aw_device *aw_dev) - { - unsigned int reg_val; - -- mutex_lock(&aw_dev->dsp_lock); -+ guard(mutex)(&aw_dev->dsp_lock); - /* check the odd bits of reg 0x40 */ - regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, AW88395_DSP_ODD_NUM_BIT_TEST); - regmap_read(aw_dev->regmap, AW88395_DSPMADD_REG, ®_val); - if (reg_val != AW88395_DSP_ODD_NUM_BIT_TEST) { - dev_err(aw_dev->dev, "check reg 0x40 odd bit failed, read[0x%x] != write[0x%x]", - reg_val, AW88395_DSP_ODD_NUM_BIT_TEST); -- goto error; -+ return -EPERM; - } - - /* check the even bits of reg 0x40 */ -@@ -1247,7 +1240,7 @@ static int aw_dev_check_sram(struct aw_device *aw_dev) - if (reg_val != AW88395_DSP_EVEN_NUM_BIT_TEST) { - dev_err(aw_dev->dev, "check reg 0x40 even bit failed, read[0x%x] != write[0x%x]", - reg_val, AW88395_DSP_EVEN_NUM_BIT_TEST); -- goto error; -+ return -EPERM; - } - - /* check dsp_fw_base_addr */ -@@ -1256,7 +1249,7 @@ static int aw_dev_check_sram(struct aw_device *aw_dev) - if (reg_val != AW88395_DSP_EVEN_NUM_BIT_TEST) { - dev_err(aw_dev->dev, "check dsp fw addr failed, read[0x%x] != write[0x%x]", - reg_val, AW88395_DSP_EVEN_NUM_BIT_TEST); -- goto error; -+ return -EPERM; - } - - /* check dsp_cfg_base_addr */ -@@ -1265,15 +1258,10 @@ static int aw_dev_check_sram(struct aw_device *aw_dev) - if (reg_val != AW88395_DSP_ODD_NUM_BIT_TEST) { - dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] != write[0x%x]", - reg_val, AW88395_DSP_ODD_NUM_BIT_TEST); -- goto error; -+ return -EPERM; - } -- mutex_unlock(&aw_dev->dsp_lock); - - return 0; -- --error: -- mutex_unlock(&aw_dev->dsp_lock); -- return -EPERM; - } - - int aw88395_dev_fw_update(struct aw_device *aw_dev, bool up_dsp_fw_en, bool force_up_en) diff --git a/sound/soc/codecs/aw88399-lib.c b/sound/soc/codecs/aw88399-lib.c new file mode 100644 --- /dev/null +++ b/sound/soc/codecs/aw88399-lib.c -@@ -0,0 +1,1409 @@ +@@ -0,0 +1,1411 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88399-lib.c -- AW88399 Common functions for ASoC and HDA audio drivers @@ -13788,7 +9091,7 @@ new file mode 100644 + +int aw88399_request_firmware_file(struct aw88399 *aw88399) +{ -+ const struct firmware *cont __free(firmware) = NULL; ++ const struct firmware *cont = NULL; + int ret; + + aw88399->aw_pa->fw_status = AW88399_DEV_FW_FAILED; @@ -13804,11 +9107,13 @@ new file mode 100644 + + aw88399->aw_cfg = devm_kzalloc(aw88399->aw_pa->dev, + struct_size(aw88399->aw_cfg, data, cont->size), GFP_KERNEL); -+ if (!aw88399->aw_cfg) ++ if (!aw88399->aw_cfg) { ++ release_firmware(cont); + return -ENOMEM; -+ ++ } + aw88399->aw_cfg->len = (int)cont->size; + memcpy(aw88399->aw_cfg->data, cont->data, cont->size); ++ release_firmware(cont); + + ret = aw88395_dev_load_acf_check(aw88399->aw_pa, aw88399->aw_cfg); + if (ret) { @@ -13916,11 +9221,7 @@ new file mode 100644 diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c --- a/sound/soc/codecs/aw88399.c +++ b/sound/soc/codecs/aw88399.c -@@ -7,1220 +7,16 @@ - // Author: Weidong Wang - // - -+#include +@@ -10,1217 +10,12 @@ #include #include #include @@ -15138,29 +10439,7 @@ diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c static struct snd_soc_dai_driver aw88399_dai[] = { { .name = "aw88399-aif", -@@ -1702,11 +498,10 @@ static int aw88399_profile_set(struct snd_kcontrol *kcontrol, - struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec); - int ret; - -- mutex_lock(&aw88399->lock); -+ guard(mutex)(&aw88399->lock); - ret = aw88399_dev_set_profile_index(aw88399->aw_pa, ucontrol->value.integer.value[0]); - if (ret) { - dev_dbg(codec->dev, "profile index does not change"); -- mutex_unlock(&aw88399->lock); - return 0; - } - -@@ -1715,8 +510,6 @@ static int aw88399_profile_set(struct snd_kcontrol *kcontrol, - aw88399_start(aw88399, AW88399_SYNC_START); - } - -- mutex_unlock(&aw88399->lock); -- - return 1; - } - -@@ -1869,86 +662,6 @@ static int aw88399_calib_set(struct snd_kcontrol *kcontrol, +@@ -1869,86 +664,6 @@ static int aw88399_calib_set(struct snd_kcontrol *kcontrol, return 0; } @@ -15247,24 +10526,7 @@ diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c static const struct snd_kcontrol_new aw88399_controls[] = { SOC_SINGLE_EXT("PCM Playback Volume", AW88399_SYSCTRL2_REG, 6, AW88399_MUTE_VOL, 0, aw88399_volume_get, -@@ -1975,7 +688,7 @@ static int aw88399_playback_event(struct snd_soc_dapm_widget *w, - struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); - struct aw88399 *aw88399 = snd_soc_component_get_drvdata(component); - -- mutex_lock(&aw88399->lock); -+ guard(mutex)(&aw88399->lock); - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - aw88399_start(aw88399, AW88399_ASYNC_START); -@@ -1986,7 +699,6 @@ static int aw88399_playback_event(struct snd_soc_dapm_widget *w, - default: - break; - } -- mutex_unlock(&aw88399->lock); - - return 0; - } -@@ -2040,70 +752,6 @@ static const struct snd_soc_component_driver soc_codec_dev_aw88399 = { +@@ -2040,70 +755,6 @@ static const struct snd_soc_component_driver soc_codec_dev_aw88399 = { .num_controls = ARRAY_SIZE(aw88399_controls), }; @@ -15934,11848 +11196,6 @@ diff --git a/sound/soc/codecs/aw88399.h b/sound/soc/codecs/aw88399.h - -#endif +#endif /* __AW88399_H__ */ -diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c ---- a/sound/soc/codecs/bt-sco.c -+++ b/sound/soc/codecs/bt-sco.c -@@ -85,13 +85,9 @@ static int bt_sco_probe(struct platform_device *pdev) - } - - static const struct platform_device_id bt_sco_driver_ids[] = { -- { -- .name = "dfbmcs320", -- }, -- { -- .name = "bt-sco", -- }, -- {}, -+ { .name = "dfbmcs320" }, -+ { .name = "bt-sco" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids); - -diff --git a/sound/soc/codecs/cpcap.c b/sound/soc/codecs/cpcap.c ---- a/sound/soc/codecs/cpcap.c -+++ b/sound/soc/codecs/cpcap.c -@@ -26,14 +26,6 @@ - /* Register 9 - CPCAP_REG_INTS2 --- Interrupt Sense 2 */ - #define CPCAP_BIT_PTT_S 11 /* Push To Talk */ - --/* Register 512 CPCAP_REG_VAUDIOC --- Audio Regulator and Bias Voltage */ --#define CPCAP_BIT_AUDIO_LOW_PWR 6 --#define CPCAP_BIT_AUD_LOWPWR_SPEED 5 --#define CPCAP_BIT_VAUDIOPRISTBY 4 --#define CPCAP_BIT_VAUDIO_MODE1 2 --#define CPCAP_BIT_VAUDIO_MODE0 1 --#define CPCAP_BIT_V_AUDIO_EN 0 -- - /* Register 513 CPCAP_REG_CC --- CODEC */ - #define CPCAP_BIT_CDC_CLK2 15 - #define CPCAP_BIT_CDC_CLK1 14 -@@ -239,7 +231,6 @@ struct cpcap_reg_info { - }; - - static const struct cpcap_reg_info cpcap_default_regs[] = { -- { CPCAP_REG_VAUDIOC, 0x003F, 0x0000 }, - { CPCAP_REG_CC, 0xFFFF, 0x0000 }, - { CPCAP_REG_CC, 0xFFFF, 0x0000 }, - { CPCAP_REG_CDI, 0xBFFF, 0x0000 }, -@@ -1265,6 +1256,7 @@ static int cpcap_voice_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) - { -+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_soc_component *component = dai->component; - struct device *dev = component->dev; - struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); -@@ -1298,7 +1290,7 @@ static int cpcap_voice_hw_params(struct snd_pcm_substream *substream, - return err; - } - -- return 0; -+ return snd_soc_runtime_set_dai_fmt(rtd, rtd->dai_link->dai_fmt); - } - - static int cpcap_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, -@@ -1391,121 +1383,8 @@ static int cpcap_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, - return 0; - } - -- --/* -- * Configure codec for voice call if requested. -- * -- * We can configure most with snd_soc_dai_set_sysclk(), snd_soc_dai_set_fmt() -- * and snd_soc_dai_set_tdm_slot(). This function configures the rest of the -- * cpcap related hardware as CPU is not involved in the voice call. -- */ --static int cpcap_voice_call(struct cpcap_audio *cpcap, struct snd_soc_dai *dai, -- bool voice_call) --{ -- int mask, err; -- -- /* Modem to codec VAUDIO_MODE1 */ -- mask = BIT(CPCAP_BIT_VAUDIO_MODE1); -- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_VAUDIOC, -- mask, voice_call ? mask : 0); -- if (err) -- return err; -- -- /* Clear MIC1_MUX for call */ -- mask = BIT(CPCAP_BIT_MIC1_MUX); -- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI, -- mask, voice_call ? 0 : mask); -- if (err) -- return err; -- -- /* Set MIC2_MUX for call */ -- mask = BIT(CPCAP_BIT_MB_ON1L) | BIT(CPCAP_BIT_MB_ON1R) | -- BIT(CPCAP_BIT_MIC2_MUX) | BIT(CPCAP_BIT_MIC2_PGA_EN); -- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI, -- mask, voice_call ? mask : 0); -- if (err) -- return err; -- -- /* Enable LDSP for call */ -- mask = BIT(CPCAP_BIT_A2_LDSP_L_EN) | BIT(CPCAP_BIT_A2_LDSP_R_EN); -- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXOA, -- mask, voice_call ? mask : 0); -- if (err) -- return err; -- -- /* Enable CPCAP_BIT_PGA_CDC_EN for call */ -- mask = BIT(CPCAP_BIT_PGA_CDC_EN); -- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXCOA, -- mask, voice_call ? mask : 0); -- if (err) -- return err; -- -- /* Unmute voice for call */ -- if (dai) { -- err = snd_soc_dai_digital_mute(dai, !voice_call, -- SNDRV_PCM_STREAM_PLAYBACK); -- if (err) -- return err; -- } -- -- /* Set modem to codec mic CDC and HPF for call */ -- mask = BIT(CPCAP_BIT_MIC2_CDC_EN) | BIT(CPCAP_BIT_CDC_EN_RX) | -- BIT(CPCAP_BIT_AUDOHPF_1) | BIT(CPCAP_BIT_AUDOHPF_0) | -- BIT(CPCAP_BIT_AUDIHPF_1) | BIT(CPCAP_BIT_AUDIHPF_0); -- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CC, -- mask, voice_call ? mask : 0); -- if (err) -- return err; -- -- /* Enable modem to codec CDC for call*/ -- mask = BIT(CPCAP_BIT_CDC_CLK_EN); -- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI, -- mask, voice_call ? mask : 0); -- -- return err; --} -- --static int cpcap_voice_set_tdm_slot(struct snd_soc_dai *dai, -- unsigned int tx_mask, unsigned int rx_mask, -- int slots, int slot_width) --{ -- struct snd_soc_component *component = dai->component; -- struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); -- int err, ts_mask, mask; -- bool voice_call; -- -- /* -- * Primitive test for voice call, probably needs more checks -- * later on for 16-bit calls detected, Bluetooth headset etc. -- */ -- if (tx_mask == 0 && rx_mask == 1 && slot_width == 8) -- voice_call = true; -- else -- voice_call = false; -- -- ts_mask = 0x7 << CPCAP_BIT_MIC2_TIMESLOT0; -- ts_mask |= 0x7 << CPCAP_BIT_MIC1_RX_TIMESLOT0; -- -- mask = (tx_mask & 0x7) << CPCAP_BIT_MIC2_TIMESLOT0; -- mask |= (rx_mask & 0x7) << CPCAP_BIT_MIC1_RX_TIMESLOT0; -- -- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI, -- ts_mask, mask); -- if (err) -- return err; -- -- err = cpcap_set_samprate(cpcap, CPCAP_DAI_VOICE, slot_width * 1000); -- if (err) -- return err; -- -- err = cpcap_voice_call(cpcap, dai, voice_call); -- if (err) -- return err; -- -- return 0; --} -- --static int cpcap_voice_set_mute(struct snd_soc_dai *dai, int mute, int direction) -+static int cpcap_voice_set_mute(struct snd_soc_dai *dai, -+ int mute, int direction) - { - struct snd_soc_component *component = dai->component; - struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); -@@ -1526,7 +1405,6 @@ static const struct snd_soc_dai_ops cpcap_dai_voice_ops = { - .hw_params = cpcap_voice_hw_params, - .set_sysclk = cpcap_voice_set_dai_sysclk, - .set_fmt = cpcap_voice_set_dai_fmt, -- .set_tdm_slot = cpcap_voice_set_tdm_slot, - .mute_stream = cpcap_voice_set_mute, - .no_capture_mute = 1, - }; -diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c ---- a/sound/soc/codecs/cros_ec_codec.c -+++ b/sound/soc/codecs/cros_ec_codec.c -@@ -12,6 +12,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -319,10 +320,18 @@ static int i2s_rx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) - (uint8_t *)&p, sizeof(p), NULL, 0); - } - -+static const u64 i2s_rx_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF; -+ - static const struct snd_soc_dai_ops i2s_rx_dai_ops = { - .hw_params = i2s_rx_hw_params, - .set_fmt = i2s_rx_set_fmt, - .set_bclk_ratio = i2s_rx_set_bclk_ratio, -+ .auto_selectable_formats = &i2s_rx_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static int i2s_rx_event(struct snd_soc_dapm_widget *w, -@@ -608,10 +617,10 @@ static void wov_copy_work(struct work_struct *w) - container_of(w, struct cros_ec_codec_priv, wov_copy_work.work); - int ret; - -- mutex_lock(&priv->wov_dma_lock); -+ guard(mutex)(&priv->wov_dma_lock); - if (!priv->wov_substream) { - dev_warn(priv->dev, "no pcm substream\n"); -- goto leave; -+ return; - } - - if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_AUDIO_SHM)) -@@ -624,8 +633,6 @@ static void wov_copy_work(struct work_struct *w) - msecs_to_jiffies(10)); - else if (ret) - dev_err(priv->dev, "failed to read audio data\n"); --leave: -- mutex_unlock(&priv->wov_dma_lock); - } - - static int wov_enable_get(struct snd_kcontrol *kcontrol, -@@ -895,12 +902,11 @@ static int wov_pcm_hw_params(struct snd_soc_component *component, - struct cros_ec_codec_priv *priv = - snd_soc_component_get_drvdata(component); - -- mutex_lock(&priv->wov_dma_lock); -+ guard(mutex)(&priv->wov_dma_lock); - priv->wov_substream = substream; - priv->wov_rp = priv->wov_wp = 0; - priv->wov_dma_offset = 0; - priv->wov_burst_read = true; -- mutex_unlock(&priv->wov_dma_lock); - - return 0; - } -@@ -911,10 +917,10 @@ static int wov_pcm_hw_free(struct snd_soc_component *component, - struct cros_ec_codec_priv *priv = - snd_soc_component_get_drvdata(component); - -- mutex_lock(&priv->wov_dma_lock); -- wov_queue_dequeue(priv, wov_queue_size(priv)); -- priv->wov_substream = NULL; -- mutex_unlock(&priv->wov_dma_lock); -+ scoped_guard(mutex, &priv->wov_dma_lock) { -+ wov_queue_dequeue(priv, wov_queue_size(priv)); -+ priv->wov_substream = NULL; -+ } - - cancel_delayed_work_sync(&priv->wov_copy_work); - -diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c ---- a/sound/soc/codecs/cs-amp-lib.c -+++ b/sound/soc/codecs/cs-amp-lib.c -@@ -7,6 +7,7 @@ - - #include - #include -+#include - #include - #include - #include -@@ -83,10 +84,12 @@ static int cs_amp_write_cal_coeff(struct cs_dsp *dsp, - KUNIT_STATIC_STUB_REDIRECT(cs_amp_write_cal_coeff, dsp, controls, ctl_name, val); - - if (IS_REACHABLE(CONFIG_FW_CS_DSP)) { -- mutex_lock(&dsp->pwr_lock); -- cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, controls->mem_region, controls->alg_id); -- ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, &beval, sizeof(beval)); -- mutex_unlock(&dsp->pwr_lock); -+ scoped_guard(mutex, &dsp->pwr_lock) { -+ cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, -+ controls->mem_region, -+ controls->alg_id); -+ ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, &beval, sizeof(beval)); -+ } - - if (ret < 0) { - dev_err(dsp->dev, "Failed to write to '%s': %d\n", ctl_name, ret); -diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c ---- a/sound/soc/codecs/cs35l32.c -+++ b/sound/soc/codecs/cs35l32.c -@@ -538,7 +538,15 @@ static int cs35l32_runtime_resume(struct device *dev) - gpiod_set_value_cansleep(cs35l32->reset_gpio, 1); - - regcache_cache_only(cs35l32->regmap, false); -- regcache_sync(cs35l32->regmap); -+ ret = regcache_sync(cs35l32->regmap); -+ if (ret) { -+ regcache_cache_only(cs35l32->regmap, true); -+ regcache_mark_dirty(cs35l32->regmap); -+ gpiod_set_value_cansleep(cs35l32->reset_gpio, 0); -+ regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies), -+ cs35l32->supplies); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c ---- a/sound/soc/codecs/cs35l56-i2c.c -+++ b/sound/soc/codecs/cs35l56-i2c.c -@@ -51,15 +51,7 @@ static int cs35l56_i2c_probe(struct i2c_client *client) - return dev_err_probe(cs35l56->base.dev, ret, "Failed to allocate register map\n"); - } - -- ret = cs35l56_common_probe(cs35l56); -- if (ret != 0) -- return ret; -- -- ret = cs35l56_irq_request(&cs35l56->base, client->irq); -- if (ret < 0) -- cs35l56_remove(cs35l56); -- -- return ret; -+ return cs35l56_common_probe(cs35l56, client->irq); - } - - static void cs35l56_i2c_remove(struct i2c_client *client) -diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c ---- a/sound/soc/codecs/cs35l56-sdw.c -+++ b/sound/soc/codecs/cs35l56-sdw.c -@@ -231,7 +231,7 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral) - * a soft reset. - */ - if (cs35l56->base.init_done) -- cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); -+ cs35l56_unmask_soundwire_interrupts(cs35l56); - - out: - pm_runtime_put_autosuspend(cs35l56->base.dev); -@@ -240,47 +240,17 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral) - static int cs35l56_sdw_interrupt(struct sdw_slave *peripheral, - struct sdw_slave_intr_status *status) - { -- struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); -- -- /* SoundWire core holds our pm_runtime when calling this function. */ -- -- dev_dbg(cs35l56->base.dev, "int control_port=%#x\n", status->control_port); -- -- if ((status->control_port & SDW_SCP_INT1_IMPL_DEF) == 0) -- return 0; -- - /* -- * Prevent bus manager suspending and possibly issuing a -- * bus-reset before the queued work has run. -+ * The IRQ itself was handled through the regmap_irq handler, this is -+ * just clearing up the additional Cirrus SoundWire registers that are -+ * not covered by the SoundWire framework or the IRQ handler itself. - */ -- pm_runtime_get_noresume(cs35l56->base.dev); -- -- /* -- * Mask and clear until it has been handled. -- * None of the interrupts are time-critical so use the -- * power-efficient queue. -- */ -- cs35l56_mask_soundwire_interrupts(peripheral); -- queue_work(system_power_efficient_wq, &cs35l56->sdw_irq_work); -+ sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1); -+ sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); - - return 0; - } - --static void cs35l56_sdw_irq_work(struct work_struct *work) --{ -- struct cs35l56_private *cs35l56 = container_of(work, -- struct cs35l56_private, -- sdw_irq_work); -- -- cs35l56_irq(-1, &cs35l56->base); -- -- /* unmask interrupts */ -- if (!cs35l56->sdw_irq_no_unmask) -- cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); -- -- pm_runtime_put_autosuspend(cs35l56->base.dev); --} -- - static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral) - { - struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); -@@ -302,6 +272,7 @@ static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral) - prop->source_ports = BIT(CS35L56_SDW1_CAPTURE_PORT); - prop->sink_ports = BIT(CS35L56_SDW1_PLAYBACK_PORT); - prop->paging_support = true; -+ prop->use_domain_irq = true; - prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; - prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY | SDW_SCP_INT1_IMPL_DEF; - -@@ -406,7 +377,7 @@ static int __maybe_unused cs35l56_sdw_runtime_resume(struct device *dev) - if (ret) - return ret; - -- cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); -+ cs35l56_unmask_soundwire_interrupts(cs35l56); - - return 0; - } -@@ -418,21 +389,12 @@ static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev) - if (!cs35l56->base.init_done) - return 0; - -- cs35l56_disable_sdw_interrupts(cs35l56); -+ /* runtime_resume unmasks the interrupt */ -+ cs35l56_mask_soundwire_interrupts(cs35l56); - - return cs35l56_system_suspend(dev); - } - --static int __maybe_unused cs35l56_sdw_system_resume(struct device *dev) --{ -- struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); -- -- cs35l56->sdw_irq_no_unmask = false; -- /* runtime_resume re-enables the interrupt */ -- -- return cs35l56_system_resume(dev); --} -- - static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id) - { - struct device *dev = &peripheral->dev; -@@ -447,7 +409,6 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi - cs35l56->base.dev = dev; - cs35l56->sdw_peripheral = peripheral; - cs35l56->sdw_link_num = peripheral->bus->link_id; -- INIT_WORK(&cs35l56->sdw_irq_work, cs35l56_sdw_irq_work); - - dev_set_drvdata(dev, cs35l56); - -@@ -457,6 +418,7 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi - regmap_config = &cs35l56_regmap_sdw; - break; - case 0x3563: -+ case 0x3562: - regmap_config = &cs35l63_regmap_sdw; - break; - default: -@@ -483,25 +445,21 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi - /* Start in cache-only until device is enumerated */ - regcache_cache_only(cs35l56->base.regmap, true); - -- ret = cs35l56_common_probe(cs35l56); -- if (ret != 0) -- return ret; -- -- return 0; -+ return cs35l56_common_probe(cs35l56, peripheral->irq); - } - - static void cs35l56_sdw_remove(struct sdw_slave *peripheral) - { - struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); - -- cs35l56_disable_sdw_interrupts(cs35l56); -+ cs35l56_mask_soundwire_interrupts(cs35l56); - - cs35l56_remove(cs35l56); - } - - static const struct dev_pm_ops cs35l56_sdw_pm = { - SET_RUNTIME_PM_OPS(cs35l56_sdw_runtime_suspend, cs35l56_sdw_runtime_resume, NULL) -- SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_sdw_system_resume) -+ SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_system_resume) - LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early) - /* NOIRQ stage not needed, SoundWire doesn't use a hard IRQ */ - }; -@@ -509,6 +467,7 @@ static const struct dev_pm_ops cs35l56_sdw_pm = { - static const struct sdw_device_id cs35l56_sdw_id[] = { - SDW_SLAVE_ENTRY(0x01FA, 0x3556, 0x3556), - SDW_SLAVE_ENTRY(0x01FA, 0x3557, 0x3557), -+ SDW_SLAVE_ENTRY(0x01FA, 0x3562, 0x3562), - SDW_SLAVE_ENTRY(0x01FA, 0x3563, 0x3563), - {}, - }; -diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c ---- a/sound/soc/codecs/cs35l56-shared.c -+++ b/sound/soc/codecs/cs35l56-shared.c -@@ -96,6 +96,7 @@ int cs35l56_set_patch(struct cs35l56_base *cs35l56_base) - ARRAY_SIZE(cs35l56_patch_fw)); - break; - case 0x63: -+ case 0x62: - ret = regmap_register_patch(cs35l56_base->regmap, cs35l63_patch_fw, - ARRAY_SIZE(cs35l63_patch_fw)); - break; -@@ -389,6 +390,7 @@ static void cs35l56_set_fw_reg_table(struct cs35l56_base *cs35l56_base) - } - break; - case 0x63: -+ case 0x62: - cs35l56_base->fw_reg = &cs35l63_fw_reg; - break; - } -@@ -595,6 +597,7 @@ void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire) - } - break; - case 0x63: -+ case 0x62: - regmap_multi_reg_write_bypassed(cs35l56_base->regmap, - cs35l63_system_reset_seq, - ARRAY_SIZE(cs35l63_system_reset_seq)); -@@ -613,26 +616,7 @@ void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire) - } - EXPORT_SYMBOL_NS_GPL(cs35l56_system_reset, "SND_SOC_CS35L56_SHARED"); - --int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq) --{ -- int ret; -- -- if (irq < 1) -- return 0; -- -- ret = devm_request_threaded_irq(cs35l56_base->dev, irq, NULL, cs35l56_irq, -- IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, -- "cs35l56", cs35l56_base); -- if (!ret) -- cs35l56_base->irq = irq; -- else -- dev_err(cs35l56_base->dev, "Failed to get IRQ: %d\n", ret); -- -- return ret; --} --EXPORT_SYMBOL_NS_GPL(cs35l56_irq_request, "SND_SOC_CS35L56_SHARED"); -- --irqreturn_t cs35l56_irq(int irq, void *data) -+static irqreturn_t cs35l56_irq(int irq, void *data) - { - struct cs35l56_base *cs35l56_base = data; - unsigned int status1 = 0, status8 = 0, status20 = 0; -@@ -640,23 +624,22 @@ irqreturn_t cs35l56_irq(int irq, void *data) - unsigned int val; - int rv; - -- irqreturn_t ret = IRQ_NONE; -- - if (!cs35l56_base->init_done) - return IRQ_NONE; - -- mutex_lock(&cs35l56_base->irq_lock); -+ guard(mutex)(&cs35l56_base->irq_lock); - -- rv = pm_runtime_resume_and_get(cs35l56_base->dev); -+ PM_RUNTIME_ACQUIRE_IF_ENABLED(cs35l56_base->dev, pm); -+ rv = PM_RUNTIME_ACQUIRE_ERR(&pm); - if (rv < 0) { - dev_err(cs35l56_base->dev, "irq: failed to get pm_runtime: %d\n", rv); -- goto err_unlock; -+ return IRQ_NONE; - } - - regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_STATUS, &val); - if ((val & CS35L56_IRQ1_STS_MASK) == 0) { - dev_dbg(cs35l56_base->dev, "Spurious IRQ: no pending interrupt\n"); -- goto err; -+ return IRQ_NONE; - } - - /* Ack interrupts */ -@@ -680,7 +663,7 @@ irqreturn_t cs35l56_irq(int irq, void *data) - - /* Check to see if unmasked bits are active */ - if (!status1 && !status8 && !status20) -- goto err; -+ return IRQ_NONE; - - if (status1 & CS35L56_AMP_SHORT_ERR_EINT1_MASK) - dev_crit(cs35l56_base->dev, "Amp short error\n"); -@@ -688,16 +671,27 @@ irqreturn_t cs35l56_irq(int irq, void *data) - if (status8 & CS35L56_TEMP_ERR_EINT1_MASK) - dev_crit(cs35l56_base->dev, "Overtemp error\n"); - -- ret = IRQ_HANDLED; -+ return IRQ_HANDLED; -+} - --err: -- pm_runtime_put(cs35l56_base->dev); --err_unlock: -- mutex_unlock(&cs35l56_base->irq_lock); -+int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq) -+{ -+ int ret; -+ -+ if (irq < 1) -+ return 0; -+ -+ ret = devm_request_threaded_irq(cs35l56_base->dev, irq, NULL, cs35l56_irq, -+ IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, -+ "cs35l56", cs35l56_base); -+ if (!ret) -+ cs35l56_base->irq = irq; -+ else -+ dev_err(cs35l56_base->dev, "Failed to get IRQ: %d\n", ret); - - return ret; - } --EXPORT_SYMBOL_NS_GPL(cs35l56_irq, "SND_SOC_CS35L56_SHARED"); -+EXPORT_SYMBOL_NS_GPL(cs35l56_irq_request, "SND_SOC_CS35L56_SHARED"); - - int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base) - { -@@ -816,14 +810,10 @@ int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_sou - if (!cs35l56_base->init_done) - return 0; - -- if (!cs35l56_base->can_hibernate) -- goto out_sync; -- -- /* Must be done before releasing cache-only */ -- if (!is_soundwire) -+ /* Hibernate wake must be done before releasing cache-only */ -+ if (cs35l56_base->can_hibernate && !is_soundwire) - cs35l56_issue_wake_event(cs35l56_base); - --out_sync: - ret = cs35l56_wait_for_firmware_boot(cs35l56_base); - if (ret) { - dev_err(cs35l56_base->dev, "Hibernate wake failed: %d\n", ret); -@@ -1470,6 +1460,7 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) - cs35l56_base->calibration_controls = &cs35l56_calibration_controls; - break; - case 0x35A630: -+ case 0x35A620: - cs35l56_base->calibration_controls = &cs35l63_calibration_controls; - devid = devid >> 4; - break; -diff --git a/sound/soc/codecs/cs35l56-spi.c b/sound/soc/codecs/cs35l56-spi.c ---- a/sound/soc/codecs/cs35l56-spi.c -+++ b/sound/soc/codecs/cs35l56-spi.c -@@ -40,15 +40,7 @@ static int cs35l56_spi_probe(struct spi_device *spi) - if (ret) - return ret; - -- ret = cs35l56_common_probe(cs35l56); -- if (ret != 0) -- return ret; -- -- ret = cs35l56_irq_request(&cs35l56->base, spi->irq); -- if (ret < 0) -- cs35l56_remove(cs35l56); -- -- return ret; -+ return cs35l56_common_probe(cs35l56, spi->irq); - } - - static void cs35l56_spi_remove(struct spi_device *spi) -diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c ---- a/sound/soc/codecs/cs35l56.c -+++ b/sound/soc/codecs/cs35l56.c -@@ -37,48 +37,49 @@ - #include "wm_adsp.h" - #include "cs35l56.h" - --void cs35l56_mask_soundwire_interrupts(struct sdw_slave *peripheral) -+void cs35l56_mask_soundwire_interrupts(struct cs35l56_private *cs35l56) - { - /* -+ * Mask unconditionally. -+ * - * The read of GEN_INT_STAT_1 is required as per the SoundWire spec - * for interrupt status bits to clear. - * GEN_INT_MASK_1 masks the _inputs_ to GEN_INT_STAT1. - */ -- sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); -- sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1); -- sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); -+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); -+ sdw_read_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1); -+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); - } - EXPORT_SYMBOL_NS_GPL(cs35l56_mask_soundwire_interrupts, "SND_SOC_CS35L56_CORE"); - --void cs35l56_unmask_soundwire_interrupts(struct sdw_slave *peripheral) -+void cs35l56_unmask_soundwire_interrupts(struct cs35l56_private *cs35l56) - { -- sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, CS35L56_SDW_INT_MASK_CODEC_IRQ); -+ if (!cs35l56->base.irq) -+ return; -+ -+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, -+ CS35L56_SDW_INT_MASK_CODEC_IRQ); - } - EXPORT_SYMBOL_NS_GPL(cs35l56_unmask_soundwire_interrupts, "SND_SOC_CS35L56_CORE"); - --void cs35l56_disable_sdw_interrupts(struct cs35l56_private *cs35l56) -+static void cs35l56_disable_sdw_interrupts(struct cs35l56_private *cs35l56) - { - if (!cs35l56->sdw_peripheral) - return; - -- cs35l56->sdw_irq_no_unmask = true; -- flush_work(&cs35l56->sdw_irq_work); -- -- /* Mask interrupts and flush in case sdw_irq_work was queued again */ -- cs35l56_mask_soundwire_interrupts(cs35l56->sdw_peripheral); -- flush_work(&cs35l56->sdw_irq_work); -+ cs35l56_mask_soundwire_interrupts(cs35l56); -+ if (cs35l56->base.irq) -+ disable_irq(cs35l56->base.irq); - } --EXPORT_SYMBOL_NS_GPL(cs35l56_disable_sdw_interrupts, "SND_SOC_CS35L56_CORE"); - --void cs35l56_enable_sdw_interrupts(struct cs35l56_private *cs35l56) -+static void cs35l56_enable_sdw_interrupts(struct cs35l56_private *cs35l56) - { -- if (!cs35l56->sdw_peripheral) -+ if (!cs35l56->sdw_peripheral || !cs35l56->base.irq) - return; - -- cs35l56->sdw_irq_no_unmask = false; -- cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); -+ enable_irq(cs35l56->base.irq); -+ cs35l56_unmask_soundwire_interrupts(cs35l56); - } --EXPORT_SYMBOL_NS_GPL(cs35l56_enable_sdw_interrupts, "SND_SOC_CS35L56_CORE"); - - static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event); -@@ -828,11 +829,7 @@ static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing - { - int ret; - -- /* -- * Disable SoundWire interrupts to prevent race with IRQ work. -- * Setting sdw_irq_no_unmask prevents the handler re-enabling -- * the SoundWire interrupt. -- */ -+ /* Disable SoundWire interrupts to prevent race with IRQ handler thread */ - cs35l56_disable_sdw_interrupts(cs35l56); - - ret = cs35l56_firmware_shutdown(&cs35l56->base); -@@ -1402,6 +1399,7 @@ static int _cs35l56_component_probe(struct snd_soc_component *component) - ARRAY_SIZE(cs35l56_controls)); - break; - case 0x63: -+ case 0x62: - ret = snd_soc_add_component_controls(component, cs35l63_controls, - ARRAY_SIZE(cs35l63_controls)); - break; -@@ -1941,7 +1939,7 @@ static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l5 - return ret; - } - --int cs35l56_common_probe(struct cs35l56_private *cs35l56) -+int cs35l56_common_probe(struct cs35l56_private *cs35l56, int irq) - { - int ret; - -@@ -2018,16 +2016,24 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) - goto err_remove_wm_adsp; - } - -+ ret = cs35l56_irq_request(&cs35l56->base, irq); -+ if (ret) -+ goto err_remove_wm_adsp; -+ - ret = snd_soc_register_component(cs35l56->base.dev, - &soc_component_dev_cs35l56, - cs35l56_dai, ARRAY_SIZE(cs35l56_dai)); - if (ret < 0) { - dev_err_probe(cs35l56->base.dev, ret, "Register codec failed\n"); -- goto err_remove_wm_adsp; -+ goto err_free_irq; - } - - return 0; - -+err_free_irq: -+ if (cs35l56->base.irq) -+ devm_free_irq(cs35l56->base.dev, cs35l56->base.irq, &cs35l56->base); -+ - err_remove_wm_adsp: - wm_adsp2_remove(&cs35l56->dsp); - -diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h ---- a/sound/soc/codecs/cs35l56.h -+++ b/sound/soc/codecs/cs35l56.h -@@ -39,8 +39,6 @@ struct cs35l56_private { - struct sdw_slave *sdw_peripheral; - struct regmap *sdw_bus_regmap; - const char *fallback_fw_suffix; -- struct work_struct sdw_irq_work; -- bool sdw_irq_no_unmask; - bool soft_resetting; - bool sdw_attached; - struct completion init_completion; -@@ -65,10 +63,8 @@ static inline struct cs35l56_private *cs35l56_private_from_base(struct cs35l56_b - - extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi; - --void cs35l56_mask_soundwire_interrupts(struct sdw_slave *peripheral); --void cs35l56_unmask_soundwire_interrupts(struct sdw_slave *peripheral); --void cs35l56_disable_sdw_interrupts(struct cs35l56_private *cs35l56); --void cs35l56_enable_sdw_interrupts(struct cs35l56_private *cs35l56); -+void cs35l56_mask_soundwire_interrupts(struct cs35l56_private *cs35l56); -+void cs35l56_unmask_soundwire_interrupts(struct cs35l56_private *cs35l56); - - int cs35l56_system_suspend(struct device *dev); - int cs35l56_system_suspend_late(struct device *dev); -@@ -76,9 +72,8 @@ int cs35l56_system_suspend_no_irq(struct device *dev); - int cs35l56_system_resume_no_irq(struct device *dev); - int cs35l56_system_resume_early(struct device *dev); - int cs35l56_system_resume(struct device *dev); --irqreturn_t cs35l56_irq(int irq, void *data); - int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq); --int cs35l56_common_probe(struct cs35l56_private *cs35l56); -+int cs35l56_common_probe(struct cs35l56_private *cs35l56, int irq); - int cs35l56_init(struct cs35l56_private *cs35l56); - void cs35l56_remove(struct cs35l56_private *cs35l56); - -diff --git a/sound/soc/codecs/cs40l50-codec.c b/sound/soc/codecs/cs40l50-codec.c ---- a/sound/soc/codecs/cs40l50-codec.c -+++ b/sound/soc/codecs/cs40l50-codec.c -@@ -288,8 +288,8 @@ static int cs40l50_codec_driver_probe(struct platform_device *pdev) - } - - static const struct platform_device_id cs40l50_id[] = { -- { "cs40l50-codec", }, -- {} -+ { .name = "cs40l50-codec" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, cs40l50_id); - -diff --git a/sound/soc/codecs/cs42l42-sdw.c b/sound/soc/codecs/cs42l42-sdw.c ---- a/sound/soc/codecs/cs42l42-sdw.c -+++ b/sound/soc/codecs/cs42l42-sdw.c -@@ -481,10 +481,20 @@ static int cs42l42_sdw_runtime_resume(struct device *dev) - regcache_cache_only(cs42l42->regmap, false); - - /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */ -- regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); -- regcache_sync(cs42l42->regmap); -+ ret = regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); -+ if (ret) -+ goto err_sync; -+ -+ ret = regcache_sync(cs42l42->regmap); -+ if (ret) -+ goto err_sync; - - return 0; -+ -+err_sync: -+ regcache_cache_only(cs42l42->regmap, true); -+ regcache_mark_dirty(cs42l42->regmap); -+ return ret; - } - - static int cs42l42_sdw_resume(struct device *dev) -diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c ---- a/sound/soc/codecs/cs42l42.c -+++ b/sound/soc/codecs/cs42l42.c -@@ -9,6 +9,7 @@ - * Author: Michael White - */ - -+#include - #include - #include - #include -@@ -565,7 +566,7 @@ static int cs42l42_set_jack(struct snd_soc_component *component, struct snd_soc_ - struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); - - /* Prevent race with interrupt handler */ -- mutex_lock(&cs42l42->irq_lock); -+ guard(mutex)(&cs42l42->irq_lock); - cs42l42->jack = jk; - - if (jk) { -@@ -581,7 +582,6 @@ static int cs42l42_set_jack(struct snd_soc_component *component, struct snd_soc_ - break; - } - } -- mutex_unlock(&cs42l42->irq_lock); - - return 0; - } -@@ -1668,13 +1668,10 @@ irqreturn_t cs42l42_irq_thread(int irq, void *data) - unsigned int current_button_status; - unsigned int i; - -- pm_runtime_get_sync(cs42l42->dev); -- mutex_lock(&cs42l42->irq_lock); -- if (cs42l42->suspended || !cs42l42->init_done) { -- mutex_unlock(&cs42l42->irq_lock); -- pm_runtime_put_autosuspend(cs42l42->dev); -+ guard(pm_runtime_active_auto)(cs42l42->dev); -+ guard(mutex)(&cs42l42->irq_lock); -+ if (cs42l42->suspended || !cs42l42->init_done) - return IRQ_NONE; -- } - - /* Read sticky registers to clear interurpt */ - for (i = 0; i < ARRAY_SIZE(stickies); i++) { -@@ -1774,9 +1771,6 @@ irqreturn_t cs42l42_irq_thread(int irq, void *data) - } - } - -- mutex_unlock(&cs42l42->irq_lock); -- pm_runtime_put_autosuspend(cs42l42->dev); -- - return IRQ_HANDLED; - } - EXPORT_SYMBOL_NS_GPL(cs42l42_irq_thread, "SND_SOC_CS42L42_CORE"); -@@ -2163,23 +2157,23 @@ int cs42l42_suspend(struct device *dev) - * future interrupts. This ensures a safe disable if the interrupt - * is shared. - */ -- mutex_lock(&cs42l42->irq_lock); -- cs42l42->suspended = true; -+ scoped_guard(mutex, &cs42l42->irq_lock) { -+ cs42l42->suspended = true; - -- /* Save register values that will be overwritten by shutdown sequence */ -- for (i = 0; i < ARRAY_SIZE(cs42l42_shutdown_seq); ++i) { -- regmap_read(cs42l42->regmap, cs42l42_shutdown_seq[i].reg, ®); -- save_regs[i] = (u8)reg; -+ /* Save register values that will be overwritten by shutdown sequence */ -+ for (i = 0; i < ARRAY_SIZE(cs42l42_shutdown_seq); ++i) { -+ regmap_read(cs42l42->regmap, cs42l42_shutdown_seq[i].reg, ®); -+ save_regs[i] = (u8)reg; -+ } -+ -+ /* Shutdown codec */ -+ regmap_multi_reg_write(cs42l42->regmap, -+ cs42l42_shutdown_seq, -+ ARRAY_SIZE(cs42l42_shutdown_seq)); -+ -+ /* All interrupt sources are now disabled */ - } - -- /* Shutdown codec */ -- regmap_multi_reg_write(cs42l42->regmap, -- cs42l42_shutdown_seq, -- ARRAY_SIZE(cs42l42_shutdown_seq)); -- -- /* All interrupt sources are now disabled */ -- mutex_unlock(&cs42l42->irq_lock); -- - /* Wait for power-down complete */ - msleep(CS42L42_PDN_DONE_TIME_MS); - ret = regmap_read_poll_timeout(cs42l42->regmap, -@@ -2250,13 +2244,13 @@ void cs42l42_resume_restore(struct device *dev) - regcache_cache_only(cs42l42->regmap, false); - regcache_mark_dirty(cs42l42->regmap); - -- mutex_lock(&cs42l42->irq_lock); -- /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */ -- regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); -- regcache_sync(cs42l42->regmap); -+ scoped_guard(mutex, &cs42l42->irq_lock) { -+ /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */ -+ regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); -+ regcache_sync(cs42l42->regmap); - -- cs42l42->suspended = false; -- mutex_unlock(&cs42l42->irq_lock); -+ cs42l42->suspended = false; -+ } - - dev_dbg(dev, "System resumed\n"); - } -diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c ---- a/sound/soc/codecs/cs42l43-jack.c -+++ b/sound/soc/codecs/cs42l43-jack.c -@@ -6,6 +6,7 @@ - // Cirrus Logic International Semiconductor Ltd. - - #include -+#include - #include - #include - #include -@@ -67,6 +68,21 @@ static int cs42l43_find_index(struct cs42l43_codec *priv, const char * const pro - return -EINVAL; - } - -+static void cs42l43_apply_accdet_config(struct cs42l43_codec *priv, -+ unsigned int autocontrol, -+ unsigned int pdncntl) -+{ -+ struct cs42l43 *cs42l43 = priv->core; -+ -+ regmap_update_bits(cs42l43->regmap, CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, -+ CS42L43_JACKDET_MODE_MASK | CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK | -+ CS42L43_HSBIAS_SENSE_TRIP_MASK, autocontrol); -+ regmap_update_bits(cs42l43->regmap, CS42L43_PDNCNTL, -+ CS42L43_RING_SENSE_EN_MASK, pdncntl); -+ -+ dev_dbg(priv->dev, "Successfully configured accessory detect\n"); -+} -+ - int cs42l43_set_jack(struct snd_soc_component *component, - struct snd_soc_jack *jack, void *d) - { -@@ -80,31 +96,34 @@ int cs42l43_set_jack(struct snd_soc_component *component, - - dev_dbg(priv->dev, "Configure accessory detect\n"); - -- ret = pm_runtime_resume_and_get(priv->dev); -+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(priv->dev, pm); -+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm); - if (ret) { - dev_err(priv->dev, "Failed to resume for jack config: %d\n", ret); - return ret; - } - -- mutex_lock(&priv->jack_lock); -+ guard(mutex)(&priv->jack_lock); - - priv->jack_hp = jack; - -- if (!jack) -- goto done; -+ if (!jack) { -+ cs42l43_apply_accdet_config(priv, autocontrol, pdncntl); -+ return 0; -+ } - - ret = device_property_count_u32(cs42l43->dev, "cirrus,buttons-ohms"); - if (ret != -EINVAL) { - if (ret < 0) { - dev_err(priv->dev, "Property cirrus,buttons-ohms malformed: %d\n", - ret); -- goto error; -+ return ret; - } - - if (ret > CS42L43_N_BUTTONS) { - ret = -EINVAL; - dev_err(priv->dev, "Property cirrus,buttons-ohms too many entries\n"); -- goto error; -+ return ret; - } - - ret = device_property_read_u32_array(cs42l43->dev, "cirrus,buttons-ohms", -@@ -112,7 +131,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, - if (ret < 0) { - dev_err(priv->dev, "Property cirrus,button-ohms malformed: %d\n", - ret); -- goto error; -+ return ret; - } - } else { - priv->buttons[0] = 70; -@@ -124,7 +143,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, - ret = cs42l43_find_index(priv, "cirrus,detect-us", 50000, &priv->detect_us, - cs42l43_accdet_us, ARRAY_SIZE(cs42l43_accdet_us)); - if (ret < 0) -- goto error; -+ return ret; - - hs2 |= ret << CS42L43_AUTO_HSDET_TIME_SHIFT; - -@@ -134,7 +153,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, - &priv->bias_ramp_ms, cs42l43_accdet_ramp_ms, - ARRAY_SIZE(cs42l43_accdet_ramp_ms)); - if (ret < 0) -- goto error; -+ return ret; - - hs2 |= ret << CS42L43_HSBIAS_RAMP_SHIFT; - -@@ -142,7 +161,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, - &priv->bias_sense_ua, cs42l43_accdet_bias_sense, - ARRAY_SIZE(cs42l43_accdet_bias_sense)); - if (ret < 0) -- goto error; -+ return ret; - - if (priv->bias_sense_ua) - autocontrol |= ret << CS42L43_HSBIAS_SENSE_TRIP_SHIFT; -@@ -154,7 +173,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, - &priv->tip_debounce_ms); - if (ret < 0 && ret != -EINVAL) { - dev_err(priv->dev, "Property cirrus,tip-debounce-ms malformed: %d\n", ret); -- goto error; -+ return ret; - } - - /* This tip sense invert is set normally, as TIPSENSE_INV already inverted */ -@@ -170,7 +189,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, - &priv->tip_fall_db_ms, cs42l43_accdet_db_ms, - ARRAY_SIZE(cs42l43_accdet_db_ms)); - if (ret < 0) -- goto error; -+ return ret; - - tip_deb |= ret << CS42L43_TIPSENSE_FALLING_DB_TIME_SHIFT; - -@@ -178,7 +197,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, - &priv->tip_rise_db_ms, cs42l43_accdet_db_ms, - ARRAY_SIZE(cs42l43_accdet_db_ms)); - if (ret < 0) -- goto error; -+ return ret; - - tip_deb |= ret << CS42L43_TIPSENSE_RISING_DB_TIME_SHIFT; - -@@ -199,7 +218,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, - NULL, cs42l43_accdet_db_ms, - ARRAY_SIZE(cs42l43_accdet_db_ms)); - if (ret < 0) -- goto error; -+ return ret; - - ring_deb |= ret << CS42L43_RINGSENSE_FALLING_DB_TIME_SHIFT; - -@@ -207,7 +226,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, - NULL, cs42l43_accdet_db_ms, - ARRAY_SIZE(cs42l43_accdet_db_ms)); - if (ret < 0) -- goto error; -+ return ret; - - ring_deb |= ret << CS42L43_RINGSENSE_RISING_DB_TIME_SHIFT; - pdncntl |= CS42L43_RING_SENSE_EN_MASK; -@@ -228,23 +247,9 @@ int cs42l43_set_jack(struct snd_soc_component *component, - CS42L43_HSBIAS_RAMP_MASK | CS42L43_HSDET_MODE_MASK | - CS42L43_AUTO_HSDET_TIME_MASK, hs2); - --done: -- ret = 0; -+ cs42l43_apply_accdet_config(priv, autocontrol, pdncntl); - -- regmap_update_bits(cs42l43->regmap, CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, -- CS42L43_JACKDET_MODE_MASK | CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK | -- CS42L43_HSBIAS_SENSE_TRIP_MASK, autocontrol); -- regmap_update_bits(cs42l43->regmap, CS42L43_PDNCNTL, -- CS42L43_RING_SENSE_EN_MASK, pdncntl); -- -- dev_dbg(priv->dev, "Successfully configured accessory detect\n"); -- --error: -- mutex_unlock(&priv->jack_lock); -- -- pm_runtime_put_autosuspend(priv->dev); -- -- return ret; -+ return 0; - } - - static void cs42l43_start_hs_bias(struct cs42l43_codec *priv, bool type_detect) -@@ -370,22 +375,22 @@ irqreturn_t cs42l43_button_press(int irq, void *data) - { - struct cs42l43_codec *priv = data; - struct cs42l43 *cs42l43 = priv->core; -- irqreturn_t iret = IRQ_NONE; - unsigned int buttons = 0; - unsigned int val = 0; - int i, ret; - -- ret = pm_runtime_resume_and_get(priv->dev); -+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(priv->dev, pm); -+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm); - if (ret) { - dev_err(priv->dev, "Failed to resume for button press: %d\n", ret); -- return iret; -+ return IRQ_NONE; - } - -- mutex_lock(&priv->jack_lock); -+ guard(mutex)(&priv->jack_lock); - - if (!priv->button_detect_running) { - dev_dbg(priv->dev, "Spurious button press IRQ\n"); -- goto error; -+ return IRQ_NONE; - } - - // Wait for 2 full cycles of comb filter to ensure good reading -@@ -396,12 +401,12 @@ irqreturn_t cs42l43_button_press(int irq, void *data) - /* Bail if jack removed, the button is irrelevant and likely invalid */ - if (!cs42l43_jack_present(priv)) { - dev_dbg(priv->dev, "Button ignored due to removal\n"); -- goto error; -+ return IRQ_NONE; - } - - if (val & CS42L43_HSBIAS_CLAMP_STS_MASK) { - dev_dbg(priv->dev, "Button ignored due to bias sense\n"); -- goto error; -+ return IRQ_NONE; - } - - val = (val & CS42L43_HSDET_DC_STS_MASK) >> CS42L43_HSDET_DC_STS_SHIFT; -@@ -424,45 +429,32 @@ irqreturn_t cs42l43_button_press(int irq, void *data) - - snd_soc_jack_report(priv->jack_hp, buttons, CS42L43_JACK_BUTTONS); - -- iret = IRQ_HANDLED; -- --error: -- mutex_unlock(&priv->jack_lock); -- -- pm_runtime_put_autosuspend(priv->dev); -- -- return iret; -+ return IRQ_HANDLED; - } - - irqreturn_t cs42l43_button_release(int irq, void *data) - { - struct cs42l43_codec *priv = data; -- irqreturn_t iret = IRQ_NONE; - int ret; - -- ret = pm_runtime_resume_and_get(priv->dev); -+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(priv->dev, pm); -+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm); - if (ret) { - dev_err(priv->dev, "Failed to resume for button release: %d\n", ret); -- return iret; -+ return IRQ_NONE; - } - -- mutex_lock(&priv->jack_lock); -+ guard(mutex)(&priv->jack_lock); - -- if (priv->button_detect_running) { -- dev_dbg(priv->dev, "Button release IRQ\n"); -- -- snd_soc_jack_report(priv->jack_hp, 0, CS42L43_JACK_BUTTONS); -- -- iret = IRQ_HANDLED; -- } else { -+ if (!priv->button_detect_running) { - dev_dbg(priv->dev, "Spurious button release IRQ\n"); -+ return IRQ_NONE; - } - -- mutex_unlock(&priv->jack_lock); -+ dev_dbg(priv->dev, "Button release IRQ\n"); -+ snd_soc_jack_report(priv->jack_hp, 0, CS42L43_JACK_BUTTONS); - -- pm_runtime_put_autosuspend(priv->dev); -- -- return iret; -+ return IRQ_HANDLED; - } - - void cs42l43_bias_sense_timeout(struct work_struct *work) -@@ -472,13 +464,14 @@ void cs42l43_bias_sense_timeout(struct work_struct *work) - struct cs42l43 *cs42l43 = priv->core; - int ret; - -- ret = pm_runtime_resume_and_get(priv->dev); -+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(priv->dev, pm); -+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm); - if (ret) { - dev_err(priv->dev, "Failed to resume for bias sense: %d\n", ret); - return; - } - -- mutex_lock(&priv->jack_lock); -+ guard(mutex)(&priv->jack_lock); - - if (cs42l43_jack_present(priv) && priv->button_detect_running) { - dev_dbg(priv->dev, "Bias sense timeout out, restore bias\n"); -@@ -491,10 +484,6 @@ void cs42l43_bias_sense_timeout(struct work_struct *work) - CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, - CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK); - } -- -- mutex_unlock(&priv->jack_lock); -- -- pm_runtime_put_autosuspend(priv->dev); - } - - static const struct reg_sequence cs42l43_3pole_patch[] = { -@@ -896,9 +885,8 @@ int cs42l43_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); - -- mutex_lock(&priv->jack_lock); -+ guard(mutex)(&priv->jack_lock); - ucontrol->value.integer.value[0] = priv->jack_override; -- mutex_unlock(&priv->jack_lock); - - return 0; - } -@@ -914,17 +902,13 @@ int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u - if (override >= e->items) - return -EINVAL; - -- mutex_lock(&priv->jack_lock); -+ guard(mutex)(&priv->jack_lock); - -- if (!cs42l43_jack_present(priv)) { -- mutex_unlock(&priv->jack_lock); -+ if (!cs42l43_jack_present(priv)) - return -EBUSY; -- } - -- if (override == priv->jack_override) { -- mutex_unlock(&priv->jack_lock); -+ if (override == priv->jack_override) - return 0; -- } - - priv->jack_override = override; - -@@ -984,7 +968,5 @@ int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u - cs42l43_jack_override_modes[override].report); - } - -- mutex_unlock(&priv->jack_lock); -- - return 1; - } -diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c ---- a/sound/soc/codecs/cs42l43.c -+++ b/sound/soc/codecs/cs42l43.c -@@ -8,6 +8,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -1207,14 +1208,12 @@ static void cs42l43_spk_vu_sync(struct cs42l43_codec *priv) - { - struct cs42l43 *cs42l43 = priv->core; - -- mutex_lock(&priv->spk_vu_lock); -+ guard(mutex)(&priv->spk_vu_lock); - - regmap_update_bits(cs42l43->regmap, CS42L43_INTP_VOLUME_CTRL1, - CS42L43_AMP1_2_VU_MASK, CS42L43_AMP1_2_VU_MASK); - regmap_update_bits(cs42l43->regmap, CS42L43_INTP_VOLUME_CTRL1, - CS42L43_AMP1_2_VU_MASK, 0); -- -- mutex_unlock(&priv->spk_vu_lock); - } - - static int cs42l43_shutter_get(struct cs42l43_codec *priv, unsigned int shift) -@@ -1601,7 +1600,7 @@ static int cs42l43_pll_ev(struct snd_soc_dapm_widget *w, - struct cs42l43 *cs42l43 = priv->core; - int ret; - -- mutex_lock(&cs42l43->pll_lock); -+ guard(mutex)(&cs42l43->pll_lock); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: -@@ -1626,8 +1625,6 @@ static int cs42l43_pll_ev(struct snd_soc_dapm_widget *w, - break; - } - -- mutex_unlock(&cs42l43->pll_lock); -- - return ret; - } - -@@ -2565,13 +2562,10 @@ static int cs42l43_set_sysclk(struct snd_soc_component *component, int clk_id, - { - struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); - struct cs42l43 *cs42l43 = priv->core; -- int ret; - -- mutex_lock(&cs42l43->pll_lock); -- ret = cs42l43_set_pll(priv, src, freq); -- mutex_unlock(&cs42l43->pll_lock); -+ guard(mutex)(&cs42l43->pll_lock); - -- return ret; -+ return cs42l43_set_pll(priv, src, freq); - } - - static int cs42l43_component_probe(struct snd_soc_component *component) -@@ -2941,8 +2935,8 @@ static const struct dev_pm_ops cs42l43_codec_pm_ops = { - }; - - static const struct platform_device_id cs42l43_codec_id_table[] = { -- { "cs42l43-codec", }, -- {} -+ { .name = "cs42l43-codec" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, cs42l43_codec_id_table); - -diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c ---- a/sound/soc/codecs/cs42l84.c -+++ b/sound/soc/codecs/cs42l84.c -@@ -10,6 +10,7 @@ - - #include - #include -+#include - #include - #include - #include -@@ -281,10 +282,9 @@ static int cs42l84_set_jack(struct snd_soc_component *component, struct snd_soc_ - struct cs42l84_private *cs42l84 = snd_soc_component_get_drvdata(component); - - /* Prevent race with interrupt handler */ -- mutex_lock(&cs42l84->irq_lock); -+ guard(mutex)(&cs42l84->irq_lock); - cs42l84->jack = jk; - snd_soc_jack_report(jk, cs42l84->hs_type, SND_JACK_HEADSET); -- mutex_unlock(&cs42l84->irq_lock); - - return 0; - } -@@ -831,7 +831,7 @@ static irqreturn_t cs42l84_irq_thread(int irq, void *data) - u8 current_ring_state; - int i; - -- mutex_lock(&cs42l84->irq_lock); -+ guard(mutex)(&cs42l84->irq_lock); - /* Read sticky registers to clear interrupt */ - for (i = 0; i < ARRAY_SIZE(stickies); i++) { - regmap_read(cs42l84->regmap, irq_params_table[i].status_addr, -@@ -902,8 +902,6 @@ static irqreturn_t cs42l84_irq_thread(int irq, void *data) - break; - } - -- mutex_unlock(&cs42l84->irq_lock); -- - return IRQ_HANDLED; - } - -@@ -919,8 +917,6 @@ static irqreturn_t cs42l84_irq_thread(int irq, void *data) - } - } - -- mutex_unlock(&cs42l84->irq_lock); -- - return IRQ_HANDLED; - } - -diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c ---- a/sound/soc/codecs/cs43130.c -+++ b/sound/soc/codecs/cs43130.c -@@ -6,6 +6,7 @@ - * - * Authors: Li Xu - */ -+#include - #include - #include - #include -@@ -818,26 +819,26 @@ static int cs43130_dsd_hw_params(struct snd_pcm_substream *substream, - unsigned int required_clk; - u8 dsd_speed; - -- mutex_lock(&cs43130->clk_mutex); -- if (!cs43130->clk_req) { -- /* no DAI is currently using clk */ -- if (!(CS43130_MCLK_22M % params_rate(params))) -- required_clk = CS43130_MCLK_22M; -- else -- required_clk = CS43130_MCLK_24M; -+ scoped_guard(mutex, &cs43130->clk_mutex) { -+ if (!cs43130->clk_req) { -+ /* no DAI is currently using clk */ -+ if (!(CS43130_MCLK_22M % params_rate(params))) -+ required_clk = CS43130_MCLK_22M; -+ else -+ required_clk = CS43130_MCLK_24M; - -- cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); -- if (cs43130->pll_bypass) -- cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); -- else -- cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); -+ cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); -+ if (cs43130->pll_bypass) -+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); -+ else -+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); -+ } -+ -+ cs43130->clk_req++; -+ if (cs43130->clk_req == 2) -+ cs43130_pcm_dsd_mix(true, cs43130->regmap); - } - -- cs43130->clk_req++; -- if (cs43130->clk_req == 2) -- cs43130_pcm_dsd_mix(true, cs43130->regmap); -- mutex_unlock(&cs43130->clk_mutex); -- - switch (params_rate(params)) { - case 176400: - dsd_speed = 0; -@@ -881,26 +882,26 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream, - unsigned int required_clk; - u8 dsd_speed; - -- mutex_lock(&cs43130->clk_mutex); -- if (!cs43130->clk_req) { -- /* no DAI is currently using clk */ -- if (!(CS43130_MCLK_22M % params_rate(params))) -- required_clk = CS43130_MCLK_22M; -- else -- required_clk = CS43130_MCLK_24M; -+ scoped_guard(mutex, &cs43130->clk_mutex) { -+ if (!cs43130->clk_req) { -+ /* no DAI is currently using clk */ -+ if (!(CS43130_MCLK_22M % params_rate(params))) -+ required_clk = CS43130_MCLK_22M; -+ else -+ required_clk = CS43130_MCLK_24M; - -- cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); -- if (cs43130->pll_bypass) -- cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); -- else -- cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); -+ cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); -+ if (cs43130->pll_bypass) -+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); -+ else -+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); -+ } -+ -+ cs43130->clk_req++; -+ if (cs43130->clk_req == 2) -+ cs43130_pcm_dsd_mix(true, cs43130->regmap); - } - -- cs43130->clk_req++; -- if (cs43130->clk_req == 2) -- cs43130_pcm_dsd_mix(true, cs43130->regmap); -- mutex_unlock(&cs43130->clk_mutex); -- - switch (dai->id) { - case CS43130_ASP_DOP_DAI: - case CS43130_XSP_DOP_DAI: -@@ -988,14 +989,13 @@ static int cs43130_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_component *component = dai->component; - struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); - -- mutex_lock(&cs43130->clk_mutex); -+ guard(mutex)(&cs43130->clk_mutex); - cs43130->clk_req--; - if (!cs43130->clk_req) { - /* no DAI is currently using clk */ - cs43130_change_clksrc(component, CS43130_MCLK_SRC_RCO); - cs43130_pcm_dsd_mix(false, cs43130->regmap); - } -- mutex_unlock(&cs43130->clk_mutex); - - return 0; - } -diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c ---- a/sound/soc/codecs/cs4349.c -+++ b/sound/soc/codecs/cs4349.c -@@ -340,7 +340,13 @@ static int cs4349_runtime_resume(struct device *dev) - gpiod_set_value_cansleep(cs4349->reset_gpio, 1); - - regcache_cache_only(cs4349->regmap, false); -- regcache_sync(cs4349->regmap); -+ ret = regcache_sync(cs4349->regmap); -+ if (ret) { -+ regcache_cache_only(cs4349->regmap, true); -+ regcache_mark_dirty(cs4349->regmap); -+ gpiod_set_value_cansleep(cs4349->reset_gpio, 0); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/cs47l15.c b/sound/soc/codecs/cs47l15.c ---- a/sound/soc/codecs/cs47l15.c -+++ b/sound/soc/codecs/cs47l15.c -@@ -6,6 +6,7 @@ - // Cirrus Logic International Semiconductor Ltd. - // - -+#include - #include - #include - #include -@@ -1285,9 +1286,8 @@ static int cs47l15_component_probe(struct snd_soc_component *component) - - snd_soc_component_init_regmap(component, madera->regmap); - -- mutex_lock(&madera->dapm_ptr_lock); -- madera->dapm = snd_soc_component_to_dapm(component); -- mutex_unlock(&madera->dapm_ptr_lock); -+ scoped_guard(mutex, &madera->dapm_ptr_lock) -+ madera->dapm = snd_soc_component_to_dapm(component); - - ret = madera_init_inputs(component); - if (ret) -@@ -1317,9 +1317,8 @@ static void cs47l15_component_remove(struct snd_soc_component *component) - struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component); - struct madera *madera = cs47l15->core.madera; - -- mutex_lock(&madera->dapm_ptr_lock); -- madera->dapm = NULL; -- mutex_unlock(&madera->dapm_ptr_lock); -+ scoped_guard(mutex, &madera->dapm_ptr_lock) -+ madera->dapm = NULL; - - wm_adsp2_component_remove(&cs47l15->core.adsp[0], component); - } -diff --git a/sound/soc/codecs/cs47l35.c b/sound/soc/codecs/cs47l35.c ---- a/sound/soc/codecs/cs47l35.c -+++ b/sound/soc/codecs/cs47l35.c -@@ -6,6 +6,7 @@ - // Cirrus Logic International Semiconductor Ltd. - // - -+#include - #include - #include - #include -@@ -1566,9 +1567,8 @@ static int cs47l35_component_probe(struct snd_soc_component *component) - - snd_soc_component_init_regmap(component, madera->regmap); - -- mutex_lock(&madera->dapm_ptr_lock); -- madera->dapm = snd_soc_component_to_dapm(component); -- mutex_unlock(&madera->dapm_ptr_lock); -+ scoped_guard(mutex, &madera->dapm_ptr_lock) -+ madera->dapm = snd_soc_component_to_dapm(component); - - ret = madera_init_inputs(component); - if (ret) -@@ -1600,9 +1600,8 @@ static void cs47l35_component_remove(struct snd_soc_component *component) - struct madera *madera = cs47l35->core.madera; - int i; - -- mutex_lock(&madera->dapm_ptr_lock); -- madera->dapm = NULL; -- mutex_unlock(&madera->dapm_ptr_lock); -+ scoped_guard(mutex, &madera->dapm_ptr_lock) -+ madera->dapm = NULL; - - for (i = 0; i < CS47L35_NUM_ADSP; i++) - wm_adsp2_component_remove(&cs47l35->core.adsp[i], component); -diff --git a/sound/soc/codecs/cs47l85.c b/sound/soc/codecs/cs47l85.c ---- a/sound/soc/codecs/cs47l85.c -+++ b/sound/soc/codecs/cs47l85.c -@@ -6,6 +6,7 @@ - // Cirrus Logic International Semiconductor Ltd. - // - -+#include - #include - #include - #include -@@ -2504,9 +2505,8 @@ static int cs47l85_component_probe(struct snd_soc_component *component) - - snd_soc_component_init_regmap(component, madera->regmap); - -- mutex_lock(&madera->dapm_ptr_lock); -- madera->dapm = snd_soc_component_to_dapm(component); -- mutex_unlock(&madera->dapm_ptr_lock); -+ scoped_guard(mutex, &madera->dapm_ptr_lock) -+ madera->dapm = snd_soc_component_to_dapm(component); - - ret = madera_init_inputs(component); - if (ret) -@@ -2537,9 +2537,8 @@ static void cs47l85_component_remove(struct snd_soc_component *component) - struct madera *madera = cs47l85->core.madera; - int i; - -- mutex_lock(&madera->dapm_ptr_lock); -- madera->dapm = NULL; -- mutex_unlock(&madera->dapm_ptr_lock); -+ scoped_guard(mutex, &madera->dapm_ptr_lock) -+ madera->dapm = NULL; - - for (i = 0; i < CS47L85_NUM_ADSP; i++) - wm_adsp2_component_remove(&cs47l85->core.adsp[i], component); -diff --git a/sound/soc/codecs/cs47l90.c b/sound/soc/codecs/cs47l90.c ---- a/sound/soc/codecs/cs47l90.c -+++ b/sound/soc/codecs/cs47l90.c -@@ -6,6 +6,7 @@ - // Cirrus Logic International Semiconductor Ltd. - // - -+#include - #include - #include - #include -@@ -2423,9 +2424,8 @@ static int cs47l90_component_probe(struct snd_soc_component *component) - - snd_soc_component_init_regmap(component, madera->regmap); - -- mutex_lock(&madera->dapm_ptr_lock); -- madera->dapm = snd_soc_component_to_dapm(component); -- mutex_unlock(&madera->dapm_ptr_lock); -+ scoped_guard(mutex, &madera->dapm_ptr_lock) -+ madera->dapm = snd_soc_component_to_dapm(component); - - ret = madera_init_inputs(component); - if (ret) -@@ -2456,9 +2456,8 @@ static void cs47l90_component_remove(struct snd_soc_component *component) - struct madera *madera = cs47l90->core.madera; - int i; - -- mutex_lock(&madera->dapm_ptr_lock); -- madera->dapm = NULL; -- mutex_unlock(&madera->dapm_ptr_lock); -+ scoped_guard(mutex, &madera->dapm_ptr_lock) -+ madera->dapm = NULL; - - for (i = 0; i < CS47L90_NUM_ADSP; i++) - wm_adsp2_component_remove(&cs47l90->core.adsp[i], component); -diff --git a/sound/soc/codecs/cs47l92.c b/sound/soc/codecs/cs47l92.c ---- a/sound/soc/codecs/cs47l92.c -+++ b/sound/soc/codecs/cs47l92.c -@@ -6,6 +6,7 @@ - // Cirrus Logic International Semiconductor Ltd. - // - -+#include - #include - #include - #include -@@ -1892,9 +1893,8 @@ static int cs47l92_component_probe(struct snd_soc_component *component) - - snd_soc_component_init_regmap(component, madera->regmap); - -- mutex_lock(&madera->dapm_ptr_lock); -- madera->dapm = snd_soc_component_to_dapm(component); -- mutex_unlock(&madera->dapm_ptr_lock); -+ scoped_guard(mutex, &madera->dapm_ptr_lock) -+ madera->dapm = snd_soc_component_to_dapm(component); - - ret = madera_init_inputs(component); - if (ret) -@@ -1922,9 +1922,8 @@ static void cs47l92_component_remove(struct snd_soc_component *component) - struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component); - struct madera *madera = cs47l92->core.madera; - -- mutex_lock(&madera->dapm_ptr_lock); -- madera->dapm = NULL; -- mutex_unlock(&madera->dapm_ptr_lock); -+ scoped_guard(mutex, &madera->dapm_ptr_lock) -+ madera->dapm = NULL; - - wm_adsp2_component_remove(&cs47l92->core.adsp[0], component); - } -diff --git a/sound/soc/codecs/cs48l32.c b/sound/soc/codecs/cs48l32.c ---- a/sound/soc/codecs/cs48l32.c -+++ b/sound/soc/codecs/cs48l32.c -@@ -8,6 +8,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -236,15 +237,13 @@ static int cs48l32_rate_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v - int ret; - - /* Prevent any mixer mux changes while we do this */ -- mutex_lock(&cs48l32_codec->rate_lock); -+ guard(mutex)(&cs48l32_codec->rate_lock); - - /* The write must be guarded by a number of SYSCLK cycles */ - cs48l32_spin_sysclk(cs48l32_codec); - ret = snd_soc_put_enum_double(kcontrol, ucontrol); - cs48l32_spin_sysclk(cs48l32_codec); - -- mutex_unlock(&cs48l32_codec->rate_lock); -- - return ret; - } - -@@ -2242,7 +2241,6 @@ static int cs48l32_dai_set_sysclk(struct snd_soc_dai *dai, - struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[dai->id - 1]; - unsigned int base = dai->driver->base; - unsigned int current_asp_rate, target_asp_rate; -- bool change_rate_domain = false; - int ret; - - if (clk_id == dai_priv->clk) -@@ -2284,19 +2282,15 @@ static int cs48l32_dai_set_sysclk(struct snd_soc_dai *dai, - - if ((current_asp_rate & CS48L32_ASP_RATE_MASK) != - (target_asp_rate & CS48L32_ASP_RATE_MASK)) { -- change_rate_domain = true; -- -- mutex_lock(&cs48l32_codec->rate_lock); - /* Guard the rate change with SYSCLK cycles */ -- cs48l32_spin_sysclk(cs48l32_codec); -- } -- -- snd_soc_component_update_bits(component, base + CS48L32_ASP_CONTROL1, -- CS48L32_ASP_RATE_MASK, target_asp_rate); -- -- if (change_rate_domain) { -- cs48l32_spin_sysclk(cs48l32_codec); -- mutex_unlock(&cs48l32_codec->rate_lock); -+ scoped_guard(mutex, &cs48l32_codec->rate_lock) { -+ cs48l32_spin_sysclk(cs48l32_codec); -+ snd_soc_component_update_bits(component, -+ base + CS48L32_ASP_CONTROL1, -+ CS48L32_ASP_RATE_MASK, -+ target_asp_rate); -+ cs48l32_spin_sysclk(cs48l32_codec); -+ } - } - } - -diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c ---- a/sound/soc/codecs/cx2072x.c -+++ b/sound/soc/codecs/cx2072x.c -@@ -9,6 +9,7 @@ - // - - #include -+#include - #include - #include - #include -@@ -1408,7 +1409,7 @@ static int cx2072x_jack_status_check(void *data) - unsigned int type = 0; - int state = 0; - -- mutex_lock(&cx2072x->lock); -+ guard(mutex)(&cx2072x->lock); - - regmap_read(cx2072x->regmap, CX2072X_PORTA_PIN_SENSE, &jack); - jack = jack >> 24; -@@ -1434,8 +1435,6 @@ static int cx2072x_jack_status_check(void *data) - /* clear interrupt */ - regmap_write(cx2072x->regmap, CX2072X_UM_INTERRUPT_CRTL_E, 0x12 << 24); - -- mutex_unlock(&cx2072x->lock); -- - dev_dbg(codec->dev, "CX2072X_HSDETECT type=0x%X,Jack state = %x\n", - type, state); - return state; -diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c ---- a/sound/soc/codecs/da7213.c -+++ b/sound/soc/codecs/da7213.c -@@ -11,6 +11,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -216,13 +217,10 @@ static int da7213_volsw_locked_get(struct snd_kcontrol *kcontrol, - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); -- int ret; - -- mutex_lock(&da7213->ctrl_lock); -- ret = snd_soc_get_volsw(kcontrol, ucontrol); -- mutex_unlock(&da7213->ctrl_lock); -+ guard(mutex)(&da7213->ctrl_lock); - -- return ret; -+ return snd_soc_get_volsw(kcontrol, ucontrol); - } - - static int da7213_volsw_locked_put(struct snd_kcontrol *kcontrol, -@@ -230,13 +228,10 @@ static int da7213_volsw_locked_put(struct snd_kcontrol *kcontrol, - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); -- int ret; - -- mutex_lock(&da7213->ctrl_lock); -- ret = snd_soc_put_volsw(kcontrol, ucontrol); -- mutex_unlock(&da7213->ctrl_lock); -+ guard(mutex)(&da7213->ctrl_lock); - -- return ret; -+ return snd_soc_put_volsw(kcontrol, ucontrol); - } - - static int da7213_enum_locked_get(struct snd_kcontrol *kcontrol, -@@ -244,13 +239,10 @@ static int da7213_enum_locked_get(struct snd_kcontrol *kcontrol, - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); -- int ret; - -- mutex_lock(&da7213->ctrl_lock); -- ret = snd_soc_get_enum_double(kcontrol, ucontrol); -- mutex_unlock(&da7213->ctrl_lock); -+ guard(mutex)(&da7213->ctrl_lock); - -- return ret; -+ return snd_soc_get_enum_double(kcontrol, ucontrol); - } - - static int da7213_enum_locked_put(struct snd_kcontrol *kcontrol, -@@ -258,13 +250,10 @@ static int da7213_enum_locked_put(struct snd_kcontrol *kcontrol, - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); -- int ret; - -- mutex_lock(&da7213->ctrl_lock); -- ret = snd_soc_put_enum_double(kcontrol, ucontrol); -- mutex_unlock(&da7213->ctrl_lock); -+ guard(mutex)(&da7213->ctrl_lock); - -- return ret; -+ return snd_soc_put_enum_double(kcontrol, ucontrol); - } - - /* ALC */ -@@ -465,9 +454,8 @@ static int da7213_tonegen_freq_get(struct snd_kcontrol *kcontrol, - __le16 val; - int ret; - -- mutex_lock(&da7213->ctrl_lock); -- ret = regmap_raw_read(da7213->regmap, reg, &val, sizeof(val)); -- mutex_unlock(&da7213->ctrl_lock); -+ scoped_guard(mutex, &da7213->ctrl_lock) -+ ret = regmap_raw_read(da7213->regmap, reg, &val, sizeof(val)); - - if (ret) - return ret; -@@ -499,12 +487,11 @@ static int da7213_tonegen_freq_put(struct snd_kcontrol *kcontrol, - */ - val_new = cpu_to_le16(ucontrol->value.integer.value[0]); - -- mutex_lock(&da7213->ctrl_lock); -+ guard(mutex)(&da7213->ctrl_lock); - ret = regmap_raw_read(da7213->regmap, reg, &val_old, sizeof(val_old)); - if (ret == 0 && (val_old != val_new)) - ret = regmap_raw_write(da7213->regmap, reg, - &val_new, sizeof(val_new)); -- mutex_unlock(&da7213->ctrl_lock); - - if (ret < 0) - return ret; -diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c ---- a/sound/soc/codecs/da7219.c -+++ b/sound/soc/codecs/da7219.c -@@ -8,6 +8,7 @@ - */ - - #include -+#include - #include - #include - #include -@@ -256,13 +257,10 @@ static int da7219_volsw_locked_get(struct snd_kcontrol *kcontrol, - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); -- int ret; - -- mutex_lock(&da7219->ctrl_lock); -- ret = snd_soc_get_volsw(kcontrol, ucontrol); -- mutex_unlock(&da7219->ctrl_lock); -+ guard(mutex)(&da7219->ctrl_lock); - -- return ret; -+ return snd_soc_get_volsw(kcontrol, ucontrol); - } - - static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol, -@@ -270,13 +268,10 @@ static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol, - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); -- int ret; - -- mutex_lock(&da7219->ctrl_lock); -- ret = snd_soc_put_volsw(kcontrol, ucontrol); -- mutex_unlock(&da7219->ctrl_lock); -+ guard(mutex)(&da7219->ctrl_lock); - -- return ret; -+ return snd_soc_put_volsw(kcontrol, ucontrol); - } - - static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol, -@@ -284,13 +279,10 @@ static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol, - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); -- int ret; - -- mutex_lock(&da7219->ctrl_lock); -- ret = snd_soc_get_enum_double(kcontrol, ucontrol); -- mutex_unlock(&da7219->ctrl_lock); -+ guard(mutex)(&da7219->ctrl_lock); - -- return ret; -+ return snd_soc_get_enum_double(kcontrol, ucontrol); - } - - static int da7219_enum_locked_put(struct snd_kcontrol *kcontrol, -@@ -298,13 +290,10 @@ static int da7219_enum_locked_put(struct snd_kcontrol *kcontrol, - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); -- int ret; - -- mutex_lock(&da7219->ctrl_lock); -- ret = snd_soc_put_enum_double(kcontrol, ucontrol); -- mutex_unlock(&da7219->ctrl_lock); -+ guard(mutex)(&da7219->ctrl_lock); - -- return ret; -+ return snd_soc_put_enum_double(kcontrol, ucontrol); - } - - /* ALC */ -@@ -422,9 +411,8 @@ static int da7219_tonegen_freq_get(struct snd_kcontrol *kcontrol, - __le16 val; - int ret; - -- mutex_lock(&da7219->ctrl_lock); -- ret = regmap_raw_read(da7219->regmap, reg, &val, sizeof(val)); -- mutex_unlock(&da7219->ctrl_lock); -+ scoped_guard(mutex, &da7219->ctrl_lock) -+ ret = regmap_raw_read(da7219->regmap, reg, &val, sizeof(val)); - - if (ret) - return ret; -@@ -456,12 +444,11 @@ static int da7219_tonegen_freq_put(struct snd_kcontrol *kcontrol, - */ - val_new = cpu_to_le16(ucontrol->value.integer.value[0]); - -- mutex_lock(&da7219->ctrl_lock); -+ guard(mutex)(&da7219->ctrl_lock); - ret = regmap_raw_read(da7219->regmap, reg, &val_old, sizeof(val_old)); - if (ret == 0 && (val_old != val_new)) - ret = regmap_raw_write(da7219->regmap, reg, -- &val_new, sizeof(val_new)); -- mutex_unlock(&da7219->ctrl_lock); -+ &val_new, sizeof(val_new)); - - if (ret < 0) - return ret; -@@ -1167,15 +1154,12 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, - struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); - int ret = 0; - -- mutex_lock(&da7219->pll_lock); -+ guard(mutex)(&da7219->pll_lock); - -- if ((da7219->clk_src == clk_id) && (da7219->mclk_rate == freq)) { -- mutex_unlock(&da7219->pll_lock); -+ if (da7219->clk_src == clk_id && da7219->mclk_rate == freq) - return 0; -- } - -- if ((freq < 2000000) || (freq > 54000000)) { -- mutex_unlock(&da7219->pll_lock); -+ if (freq < 2000000 || freq > 54000000) { - dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", - freq); - return -EINVAL; -@@ -1193,7 +1177,6 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, - break; - default: - dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id); -- mutex_unlock(&da7219->pll_lock); - return -EINVAL; - } - -@@ -1203,17 +1186,13 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, - freq = clk_round_rate(da7219->mclk, freq); - ret = clk_set_rate(da7219->mclk, freq); - if (ret) { -- dev_err(codec_dai->dev, "Failed to set clock rate %d\n", -- freq); -- mutex_unlock(&da7219->pll_lock); -+ dev_err(codec_dai->dev, "Failed to set clock rate %d\n", freq); - return ret; - } - } - - da7219->mclk_rate = freq; - -- mutex_unlock(&da7219->pll_lock); -- - return 0; - } - -@@ -1296,13 +1275,10 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, - { - struct snd_soc_component *component = codec_dai->component; - struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); -- int ret; - -- mutex_lock(&da7219->pll_lock); -- ret = da7219_set_pll(component, source, fout); -- mutex_unlock(&da7219->pll_lock); -+ guard(mutex)(&da7219->pll_lock); - -- return ret; -+ return da7219_set_pll(component, source, fout); - } - - static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) -diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c ---- a/sound/soc/codecs/es8316.c -+++ b/sound/soc/codecs/es8316.c -@@ -9,11 +9,13 @@ - - #include - #include -+#include - #include - #include - #include - #include - #include -+#include - #include - #include - #include -@@ -30,6 +32,13 @@ static const unsigned int supported_mclk_lrck_ratios[] = { - 256, 384, 400, 500, 512, 768, 1024 - }; - -+static const char * const es8316_supply_names[] = { -+ "avdd", -+ "cpvdd", -+ "dvdd", -+ "pvdd", -+}; -+ - struct es8316_priv { - struct mutex lock; - struct clk *mclk; -@@ -620,15 +629,15 @@ static irqreturn_t es8316_irq(int irq, void *data) - struct snd_soc_component *comp = es8316->component; - unsigned int flags; - -- mutex_lock(&es8316->lock); -+ guard(mutex)(&es8316->lock); - - regmap_read(es8316->regmap, ES8316_GPIO_FLAG, &flags); - if (flags == 0x00) -- goto out; /* Powered-down / reset */ -+ return IRQ_HANDLED; /* Powered-down / reset */ - - /* Catch spurious IRQ before set_jack is called */ - if (!es8316->jack) -- goto out; -+ return IRQ_HANDLED; - - if (es8316->jd_inverted) - flags ^= ES8316_GPIO_FLAG_HP_NOT_INSERTED; -@@ -681,8 +690,6 @@ static irqreturn_t es8316_irq(int irq, void *data) - } - } - --out: -- mutex_unlock(&es8316->lock); - return IRQ_HANDLED; - } - -@@ -699,18 +706,16 @@ static void es8316_enable_jack_detect(struct snd_soc_component *component, - es8316->jd_inverted = device_property_read_bool(component->dev, - "everest,jack-detect-inverted"); - -- mutex_lock(&es8316->lock); -+ scoped_guard(mutex, &es8316->lock) { -+ es8316->jack = jack; - -- es8316->jack = jack; -+ if (es8316->jack->status & SND_JACK_MICROPHONE) -+ es8316_enable_micbias_for_mic_gnd_short_detect(component); - -- if (es8316->jack->status & SND_JACK_MICROPHONE) -- es8316_enable_micbias_for_mic_gnd_short_detect(component); -- -- snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, -- ES8316_GPIO_ENABLE_INTERRUPT, -- ES8316_GPIO_ENABLE_INTERRUPT); -- -- mutex_unlock(&es8316->lock); -+ snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, -+ ES8316_GPIO_ENABLE_INTERRUPT, -+ ES8316_GPIO_ENABLE_INTERRUPT); -+ } - - /* Enable irq and sync initial jack state */ - enable_irq(es8316->irq); -@@ -726,7 +731,7 @@ static void es8316_disable_jack_detect(struct snd_soc_component *component) - - disable_irq(es8316->irq); - -- mutex_lock(&es8316->lock); -+ guard(mutex)(&es8316->lock); - - snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, - ES8316_GPIO_ENABLE_INTERRUPT, 0); -@@ -737,8 +742,6 @@ static void es8316_disable_jack_detect(struct snd_soc_component *component) - } - - es8316->jack = NULL; -- -- mutex_unlock(&es8316->lock); - } - - static int es8316_set_jack(struct snd_soc_component *component, -@@ -871,6 +874,11 @@ static int es8316_i2c_probe(struct i2c_client *i2c_client) - - i2c_set_clientdata(i2c_client, es8316); - -+ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(es8316_supply_names), -+ es8316_supply_names); -+ if (ret) -+ return dev_err_probe(dev, ret, "unable to enable supplies\n"); -+ - es8316->regmap = devm_regmap_init_i2c(i2c_client, &es8316_regmap); - if (IS_ERR(es8316->regmap)) - return PTR_ERR(es8316->regmap); -diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c ---- a/sound/soc/codecs/es8326.c -+++ b/sound/soc/codecs/es8326.c -@@ -6,6 +6,7 @@ - // Authors: David Yang - // - -+#include - #include - #include - #include -@@ -790,7 +791,7 @@ static void es8326_jack_button_handler(struct work_struct *work) - if (!(es8326->jack->status & SND_JACK_HEADSET)) /* Jack unplugged */ - return; - -- mutex_lock(&es8326->lock); -+ guard(mutex)(&es8326->lock); - iface = snd_soc_component_read(comp, ES8326_HPDET_STA); - switch (iface) { - case 0x93: -@@ -845,7 +846,6 @@ static void es8326_jack_button_handler(struct work_struct *work) - } - es8326_disable_micbias(es8326->component); - } -- mutex_unlock(&es8326->lock); - } - - static void es8326_jack_detect_handler(struct work_struct *work) -@@ -855,7 +855,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) - struct snd_soc_component *comp = es8326->component; - unsigned int iface; - -- mutex_lock(&es8326->lock); -+ guard(mutex)(&es8326->lock); - iface = snd_soc_component_read(comp, ES8326_HPDET_STA); - dev_dbg(comp->dev, "gpio flag %#04x", iface); - -@@ -873,7 +873,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) - regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, - ES8326_HP_DET_JACK_POL, (es8326->jd_inverted ? - ~es8326->jack_pol : es8326->jack_pol)); -- goto exit; -+ return; - } - - if ((iface & ES8326_HPINSERT_FLAG) == 0) { -@@ -930,7 +930,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) - queue_delayed_work(system_dfl_wq, &es8326->jack_detect_work, - msecs_to_jiffies(400)); - es8326->hp = 1; -- goto exit; -+ return; - } - if (es8326->jack->status & SND_JACK_HEADSET) { - /* detect button */ -@@ -939,7 +939,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) - (ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON)); - es8326_enable_micbias(es8326->component); - queue_delayed_work(system_dfl_wq, &es8326->button_press_work, 10); -- goto exit; -+ return; - } - if ((iface & ES8326_HPBUTTON_FLAG) == 0x01) { - dev_dbg(comp->dev, "Headphone detected\n"); -@@ -958,8 +958,6 @@ static void es8326_jack_detect_handler(struct work_struct *work) - usleep_range(10000, 15000); - } - } --exit: -- mutex_unlock(&es8326->lock); - } - - static irqreturn_t es8326_irq(int irq, void *dev_id) -@@ -1200,13 +1198,12 @@ static void es8326_enable_jack_detect(struct snd_soc_component *component, - { - struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); - -- mutex_lock(&es8326->lock); -- if (es8326->jd_inverted) -- snd_soc_component_update_bits(component, ES8326_HPDET_TYPE, -- ES8326_HP_DET_JACK_POL, ~es8326->jack_pol); -- es8326->jack = jack; -- -- mutex_unlock(&es8326->lock); -+ scoped_guard(mutex, &es8326->lock) { -+ if (es8326->jd_inverted) -+ snd_soc_component_update_bits(component, ES8326_HPDET_TYPE, -+ ES8326_HP_DET_JACK_POL, ~es8326->jack_pol); -+ es8326->jack = jack; -+ } - es8326_irq(es8326->irq, es8326); - } - -@@ -1219,13 +1216,12 @@ static void es8326_disable_jack_detect(struct snd_soc_component *component) - return; /* Already disabled (or never enabled) */ - cancel_delayed_work_sync(&es8326->jack_detect_work); - -- mutex_lock(&es8326->lock); -+ guard(mutex)(&es8326->lock); - if (es8326->jack->status & SND_JACK_MICROPHONE) { - es8326_disable_micbias(component); - snd_soc_jack_report(es8326->jack, 0, SND_JACK_HEADSET); - } - es8326->jack = NULL; -- mutex_unlock(&es8326->lock); - } - - static int es8326_set_jack(struct snd_soc_component *component, -diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c ---- a/sound/soc/codecs/es8328.c -+++ b/sound/soc/codecs/es8328.c -@@ -405,6 +405,11 @@ static const struct snd_soc_dapm_route es8328_dapm_routes[] = { - - { "Mic Bias", NULL, "Mic Bias Gen" }, - -+ { "LINPUT1", NULL, "Mic Bias" }, -+ { "RINPUT1", NULL, "Mic Bias" }, -+ { "LINPUT2", NULL, "Mic Bias" }, -+ { "RINPUT2", NULL, "Mic Bias" }, -+ - { "Left Mixer", NULL, "Left DAC" }, - { "Left Mixer", "Left Bypass Switch", "Left Line Mux" }, - { "Left Mixer", "Right Playback Switch", "Right DAC" }, -diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c ---- a/sound/soc/codecs/es8389.c -+++ b/sound/soc/codecs/es8389.c -@@ -36,6 +36,10 @@ struct es8389_private { - unsigned int sysclk; - int mastermode; - -+ u8 hpfl; -+ u8 hpfr; -+ u32 hpf_freq; -+ u32 capture_rate; - u8 mclk_src; - u8 vddd; - int version; -@@ -50,10 +54,29 @@ static const char * const es8389_core_supplies[] = { - static bool es8389_volatile_register(struct device *dev, - unsigned int reg) - { -- if ((reg <= 0xff)) -- return true; -- else -+ switch (reg) { -+ case ES8389_ADCL_VOL: -+ case ES8389_ADCR_VOL: -+ case ES8389_MIC1_GAIN: -+ case ES8389_MIC2_GAIN: -+ case ES8389_DACL_VOL: -+ case ES8389_DACR_VOL: -+ case ES8389_ALC_ON: -+ case ES8389_ALC_CTL: -+ case ES8389_ALC_TARGET: -+ case ES8389_ALC_GAIN: -+ case ES8389_ADC_MUTE: -+ case ES8389_OSR_VOL: -+ case ES8389_DAC_INV: -+ case ES8389_MIX_VOL: -+ case ES8389_DAC_MIX: -+ case ES8389_ADC_RESET: -+ case ES8389_ADC_MODE: -+ case ES8389_DMIC_EN: - return false; -+ default: -+ return true; -+ } - } - - static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9550, 50, 0); -@@ -63,6 +86,92 @@ static const DECLARE_TLV_DB_SCALE(mix_vol_tlv, -9500, 100, 0); - static const DECLARE_TLV_DB_SCALE(alc_target_tlv, -3200, 200, 0); - static const DECLARE_TLV_DB_SCALE(alc_max_level, -3200, 200, 0); - -+static const u32 hpf_table[10][10] = { -+ {1020, 754, 624, 559, 527, 511, 502, 498, 497, 496}, -+ {754, 495, 368, 306, 274, 259, 251, 247, 246, 244}, -+ {624, 368, 243, 182, 151, 136, 128, 124, 123, 121}, -+ {559, 306, 182, 120, 90, 75, 68, 63, 62, 60}, -+ {527, 274, 151, 90, 60, 45, 38, 33, 32, 31}, -+ {511, 259, 136, 75, 45, 30, 23, 19, 18, 17}, -+ {502, 251, 128, 68, 38, 23, 16, 13, 11, 11}, -+ {498, 247, 124, 63, 33, 19, 13, 10, 8, 8}, -+ {497, 246, 123, 62, 32, 18, 11, 8, 8, 0}, -+ {496, 244, 121, 60, 31, 17, 11, 8, 0, 0} -+}; -+ -+static bool find_best_hpf_freq(u32 target_hz, u8 *hpf1, u8 *hpf2, u32 *out) -+{ -+ int best_row = -1, best_col = -1; -+ u32 min_diff = U32_MAX; -+ u32 f, diff; -+ int i, j; -+ -+ if (target_hz > 1020) -+ return false; -+ -+ for (i = 0; i < 10; i++) { -+ for (j = i; j < 10; j++) { -+ f = hpf_table[i][j]; -+ -+ diff = (target_hz > f) ? (target_hz - f) : (f - target_hz); -+ if (diff < min_diff) { -+ min_diff = diff; -+ best_row = i; -+ best_col = j; -+ *out = f; -+ } -+ } -+ } -+ -+ *hpf1 = best_col + ES8389_HPF_OFFSET; -+ *hpf2 = best_row + ES8389_HPF_OFFSET; -+ -+ return true; -+} -+ -+static int es8389_hpf_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); -+ -+ ucontrol->value.integer.value[0] = es8389->hpf_freq; -+ return 0; -+} -+ -+static int es8389_hpf_set(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); -+ u32 freq; -+ bool hpf; -+ -+ if (es8389->hpf_freq == ucontrol->value.integer.value[0]) -+ return 0; -+ -+ if (es8389->capture_rate) { -+ freq = (ucontrol->value.integer.value[0] * 48000) / es8389->capture_rate; -+ -+ hpf = find_best_hpf_freq(freq, &es8389->hpfl, &es8389->hpfr, &es8389->hpf_freq); -+ if (!hpf) -+ return -EBUSY; -+ -+ if (es8389->hpf_freq != ucontrol->value.integer.value[0]) -+ dev_dbg(component->dev, "At the %u Hz sampling rate, %ld Hz could not be obtained." -+ "the frequency has been set to the closest value, %u Hz\n", -+ es8389->capture_rate, ucontrol->value.integer.value[0], es8389->hpf_freq); -+ -+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, es8389->hpfl); -+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, es8389->hpfr); -+ } else { -+ es8389->hpf_freq = ucontrol->value.integer.value[0]; -+ dev_dbg(component->dev, "PCM_STREAM_CAPTURE is not active.retain the input frequency\n"); -+ } -+ -+ return 1; -+} -+ - static int es8389_dmic_set(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) - { -@@ -143,6 +252,16 @@ static const struct soc_enum alc_ramprate = - static const struct soc_enum alc_winsize = - SOC_ENUM_SINGLE(ES8389_ALC_CTL, 0, 16, winsize); - -+static const char *const es8389_adcl_mux_txt[] = { -+ "Normal", -+ "ADC2 channel to ADC1 channel", -+}; -+ -+static const char *const es8389_adcr_mux_txt[] = { -+ "Normal", -+ "ADC1 channel to ADC2 channel", -+}; -+ - static const char *const es8389_outl_mux_txt[] = { - "Normal", - "DAC2 channel to DAC1 channel", -@@ -170,6 +289,20 @@ static const unsigned int es8389_pga_values[] = { - 1, 5, 6 - }; - -+static const struct soc_enum es8389_adcl_mux_enum = -+ SOC_ENUM_SINGLE(ES8389_ADC_MODE, 5, -+ ARRAY_SIZE(es8389_adcl_mux_txt), es8389_adcl_mux_txt); -+ -+static const struct snd_kcontrol_new es8389_adcl_mux_controls = -+ SOC_DAPM_ENUM("INPUTL MUX", es8389_adcl_mux_enum); -+ -+static const struct soc_enum es8389_adcr_mux_enum = -+ SOC_ENUM_SINGLE(ES8389_ADC_MODE, 4, -+ ARRAY_SIZE(es8389_adcr_mux_txt), es8389_adcr_mux_txt); -+ -+static const struct snd_kcontrol_new es8389_adcr_mux_controls = -+ SOC_DAPM_ENUM("INPUTR MUX", es8389_adcr_mux_enum); -+ - static const struct soc_enum es8389_outl_mux_enum = - SOC_ENUM_SINGLE(ES8389_DAC_MIX, 5, - ARRAY_SIZE(es8389_outl_mux_txt), es8389_outl_mux_txt); -@@ -240,6 +373,8 @@ static const struct snd_kcontrol_new es8389_snd_controls[] = { - SOC_DOUBLE("ADC OSR Volume ON Switch", ES8389_ADC_MUTE, 6, 7, 1, 0), - SOC_SINGLE_TLV("ADC OSR Volume", ES8389_OSR_VOL, 0, 0xFF, 0, adc_vol_tlv), - SOC_DOUBLE("ADC OUTPUT Invert Switch", ES8389_ADC_HPF2, 5, 6, 1, 0), -+ SOC_SINGLE_EXT("ADC HPF Freq Select", SND_SOC_NOPM, 0, 1020, 0, -+ es8389_hpf_get, es8389_hpf_set), - - SOC_SINGLE_TLV("DACL Playback Volume", ES8389_DACL_VOL, 0, 0xFF, 0, dac_vol_tlv), - SOC_SINGLE_TLV("DACR Playback Volume", ES8389_DACR_VOL, 0, 0xFF, 0, dac_vol_tlv), -@@ -298,6 +433,8 @@ static const struct snd_soc_dapm_widget es8389_dapm_widgets[] = { - &es8389_adc_mixer_controls[0], - ARRAY_SIZE(es8389_adc_mixer_controls)), - SND_SOC_DAPM_MUX("ADC MUX", SND_SOC_NOPM, 0, 0, &es8389_dmic_mux_controls), -+ SND_SOC_DAPM_MUX("INPUTL MUX", SND_SOC_NOPM, 0, 0, &es8389_adcl_mux_controls), -+ SND_SOC_DAPM_MUX("INPUTR MUX", SND_SOC_NOPM, 0, 0, &es8389_adcr_mux_controls), - - SND_SOC_DAPM_MUX("OUTL MUX", SND_SOC_NOPM, 0, 0, &es8389_outl_mux_controls), - SND_SOC_DAPM_MUX("OUTR MUX", SND_SOC_NOPM, 0, 0, &es8389_outr_mux_controls), -@@ -311,10 +448,15 @@ static const struct snd_soc_dapm_route es8389_dapm_routes[] = { - {"ADCL", NULL, "PGAL"}, - {"ADCR", NULL, "PGAR"}, - -+ {"INPUTL MUX", "Normal", "ADCL"}, -+ {"INPUTL MUX", "ADC2 channel to ADC1 channel", "ADCR"}, -+ {"INPUTR MUX", "Normal", "ADCR"}, -+ {"INPUTR MUX", "ADC1 channel to ADC2 channel", "ADCL"}, -+ - {"ADC Mixer", "DACL ADCL Mixer", "DACL"}, - {"ADC Mixer", "DACR ADCR Mixer", "DACR"}, -- {"ADC Mixer", NULL, "ADCL"}, -- {"ADC Mixer", NULL, "ADCR"}, -+ {"ADC Mixer", NULL, "INPUTL MUX"}, -+ {"ADC Mixer", NULL, "INPUTR MUX"}, - - {"ADC MUX", "AMIC", "ADC Mixer"}, - {"ADC MUX", "DMIC", "DMIC"}, -@@ -415,52 +557,54 @@ static const struct _coeff_div coeff_div[] = { - {36, 576000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {48, 768000, 16000, 0x02, 0x57, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {50, 800000, 16000, 0x00, 0x7E, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -- {64, 1024000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -- {72, 1152000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -+ {64, 1024000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -+ {72, 1152000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {96, 1536000, 16000, 0x02, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {128, 2048000, 16000, 0x00, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {144, 2304000, 16000, 0x00, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -+ {150, 2400000, 16000, 0x02, 0x7E, 0x01, 0xC9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {192, 3072000, 16000, 0x02, 0x65, 0x25, 0xE0, 0x00, 0xE1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -- {256, 4096000, 16000, 0x00, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -+ {256, 4096000, 16000, 0x00, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {300, 4800000, 16000, 0x02, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {384, 6144000, 16000, 0x02, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -- {512, 8192000, 16000, 0x01, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -+ {512, 8192000, 16000, 0x01, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {750, 12000000, 16000, 0x0E, 0x7E, 0x01, 0xC9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -- {768, 12288000, 16000, 0x02, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -- {1024, 16384000, 16000, 0x03, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -+ {768, 12288000, 16000, 0x02, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -+ {1024, 16384000, 16000, 0x03, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {1152, 18432000, 16000, 0x08, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {1200, 19200000, 16000, 0x0B, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {1500, 24000000, 16000, 0x0E, 0x26, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -- {1536, 24576000, 16000, 0x05, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -+ {1536, 24576000, 16000, 0x05, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {1625, 26000000, 16000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {800, 19200000, 24000, 0x07, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x1A, 0x49, 0x14, 2, 2}, - {375, 12000000, 32000, 0x0E, 0x2E, 0x05, 0xC8, 0x00, 0xC2, 0x80, 0x40, 0x01, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B, 2, 0}, -- {600, 19200000, 32000, 0x05, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B, 2, 2}, -- {32, 1411200, 44100, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {600, 19200000, 32000, 0x05, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B, 2, 2}, -+ {32, 1411200, 44100, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {64, 2822400, 44100, 0x00, 0x51, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -- {128, 5644800, 44100, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -- {256, 11289600, 44100, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -- {512, 22579200, 44100, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -- {32, 1536000, 48000, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {128, 5644800, 44100, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {256, 11289600, 44100, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {512, 22579200, 44100, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {32, 1536000, 48000, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {48, 2304000, 48000, 0x02, 0x55, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {50, 2400000, 48000, 0x00, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {64, 3072000, 48000, 0x00, 0x51, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -- {100, 4800000, 48000, 0x00, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {100, 4800000, 48000, 0x00, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {125, 6000000, 48000, 0x04, 0x6E, 0x05, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x01, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -- {128, 6144000, 48000, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -- {200, 9600000, 48000, 0x01, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {128, 6144000, 48000, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {200, 9600000, 48000, 0x01, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {250, 12000000, 48000, 0x04, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -- {256, 12288000, 48000, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -- {384, 18432000, 48000, 0x02, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -- {400, 19200000, 48000, 0x03, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -- {500, 24000000, 48000, 0x04, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -- {512, 24576000, 48000, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {256, 12288000, 48000, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {384, 18432000, 48000, 0x02, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {400, 19200000, 48000, 0x03, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {500, 24000000, 48000, 0x04, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {512, 24576000, 48000, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {800, 38400000, 48000, 0x18, 0x45, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {128, 11289600, 88200, 0x00, 0x50, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x32, 0x89, 0x25, 2, 2}, -- {64, 6144000, 96000, 0x00, 0x41, 0x00, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, -+ {64, 6144000, 96000, 0x00, 0x41, 0x00, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, - {96, 9216000, 96000, 0x02, 0x43, 0x00, 0xC0, 0x10, 0xC0, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, - {256, 24576000, 96000, 0x00, 0x40, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, - {128, 24576000, 192000, 0x00, 0x50, 0x00, 0xC0, 0x18, 0xC1, 0x81, 0xC0, 0x00, 0x8F, 0x7F, 0xBF, 0xC0, 0x3F, 0x7F, 0x80, 0x12, 0xC0, 0x3F, 0xF9, 0x3F, 2, 2}, -+ {64, 12288000, 192000, 0x00, 0x41, 0x00, 0xC0, 0x18, 0xC1, 0x80, 0x00, 0x00, 0x8F, 0x7F, 0xEF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x3F, 0xF9, 0x3F, 1, 0}, - }; - - static inline int get_coeff(u8 vddd, u8 dmic, int mclk, int rate) -@@ -564,6 +708,8 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream, - int coeff, ret; - u8 dmic_enable, state = 0; - unsigned int regv; -+ u32 freq; -+ bool hpf; - - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: -@@ -590,7 +736,7 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream, - - if (es8389->mclk_src == ES8389_SCLK_PIN) { - regmap_update_bits(es8389->regmap, ES8389_MASTER_CLK, -- ES8389_MCLK_SOURCE, es8389->mclk_src); -+ ES8389_MCLK_MASK, ES8389_MCLK_FROM_SCLK); - es8389->sysclk = params_channels(params) * params_width(params) * params_rate(params); - } - -@@ -641,6 +787,28 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream, - dev_warn(component->dev, "Clock coefficients do not match"); - } - -+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { -+ es8389->capture_rate = params_rate(params); -+ freq = (es8389->hpf_freq * 48000) / params_rate(params); -+ hpf = find_best_hpf_freq(freq, &es8389->hpfl, &es8389->hpfr, &es8389->hpf_freq); -+ if (!hpf) { -+ dev_err(component->dev, "The HPF frequency is invalid\n"); -+ return -EINVAL; -+ } -+ } -+ -+ return 0; -+} -+ -+static int es8389_pcm_hw_free(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ struct snd_soc_component *component = dai->component; -+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); -+ -+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) -+ es8389->capture_rate = 0; -+ - return 0; - } - -@@ -721,8 +889,8 @@ static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction) - regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE, - 0x03, 0x00); - } else { -- regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x0a); -- regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x0a); -+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, es8389->hpfl); -+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, es8389->hpfr); - regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE, - 0x03, 0x00); - } -@@ -738,6 +906,7 @@ static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction) - - static const struct snd_soc_dai_ops es8389_ops = { - .hw_params = es8389_pcm_hw_params, -+ .hw_free = es8389_pcm_hw_free, - .set_fmt = es8389_set_dai_fmt, - .set_sysclk = es8389_set_dai_sysclk, - .set_tdm_slot = es8389_set_tdm_slot, -@@ -771,7 +940,7 @@ static void es8389_init(struct snd_soc_component *component) - - regmap_read(es8389->regmap, ES8389_MAX_REGISTER, ®); - es8389->version = reg; -- regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x00); -+ regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x56); - regmap_write(es8389->regmap, ES8389_RESET, 0x7E); - regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x38); - regmap_write(es8389->regmap, ES8389_ADC_HPF1, 0x64); -@@ -823,7 +992,7 @@ static void es8389_init(struct snd_soc_component *component) - regmap_write(es8389->regmap, ES8389_SCLK_DIV, 0x04); - regmap_write(es8389->regmap, ES8389_LRCK_DIV1, 0x01); - regmap_write(es8389->regmap, ES8389_LRCK_DIV2, 0x00); -- regmap_write(es8389->regmap, ES8389_OSC_CLK, 0x00); -+ regmap_write(es8389->regmap, ES8389_OSC_CLK, 0x10); - regmap_write(es8389->regmap, ES8389_ADC_OSR, 0x1F); - regmap_write(es8389->regmap, ES8389_ADC_DSP, 0x7F); - regmap_write(es8389->regmap, ES8389_ADC_MUTE, 0xC0); -@@ -861,13 +1030,13 @@ static int es8389_resume(struct snd_soc_component *component) - regcache_cache_only(es8389->regmap, false); - regcache_cache_bypass(es8389->regmap, true); - regmap_read(es8389->regmap, ES8389_RESET, ®v); -- regcache_cache_bypass(es8389->regmap, false); - - if (regv == 0xff) - es8389_init(component); - else - es8389_set_bias_level(component, SND_SOC_BIAS_ON); - -+ regcache_cache_bypass(es8389->regmap, false); - regcache_sync(es8389->regmap); - - return 0; -@@ -913,6 +1082,7 @@ static int es8389_probe(struct snd_soc_component *component) - return ret; - } - -+ es8389->hpf_freq = ES8389_HPF_DEFAULT; - es8389_init(component); - es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY); - -diff --git a/sound/soc/codecs/es8389.h b/sound/soc/codecs/es8389.h ---- a/sound/soc/codecs/es8389.h -+++ b/sound/soc/codecs/es8389.h -@@ -106,6 +106,9 @@ - #define ES8389_MIC_SEL_MASK (7 << 4) - #define ES8389_MIC_DEFAULT (1 << 4) - -+#define ES8389_HPF_DEFAULT 16 -+#define ES8389_HPF_OFFSET 4 -+ - #define ES8389_MASTER_MODE_EN (1 << 0) - - #define ES8389_TDM_OFF (0 << 0) -@@ -116,9 +119,11 @@ - #define ES8389_TDM_SLOT (0x70 << 0) - #define ES8389_TDM_SHIFT 4 - --#define ES8389_MCLK_SOURCE (1 << 6) --#define ES8389_MCLK_PIN (1 << 6) --#define ES8389_SCLK_PIN (0 << 6) -+#define ES8389_MCLK_MASK (3 << 6) -+#define ES8389_MCLK_FROM_SCLK (1 << 6) -+#define ES8389_MCLK_SOURCE ES8389_MCLK_PIN -+#define ES8389_MCLK_PIN 0 -+#define ES8389_SCLK_PIN 1 - - /* ES8389_FMT */ - #define ES8389_S24_LE (0 << 5) -diff --git a/sound/soc/codecs/es9356.c b/sound/soc/codecs/es9356.c ---- a/sound/soc/codecs/es9356.c -+++ b/sound/soc/codecs/es9356.c -@@ -6,6 +6,7 @@ - // - // - -+#include - #include - #include - #include -@@ -19,8 +20,6 @@ - #include - #include - #include --#include --#include - #include - #include - #include -@@ -500,16 +499,19 @@ static int es9356_power_state(struct snd_soc_dai *dai, unsigned char ps, unsigne - } - - /* power state changes are not independent across functions */ -- mutex_lock(&es9356->pde_lock); -- ret = es9356_pde_transition_delay(es9356, func, pde_entity, ps?ps0:ps3); -- if (ret) { -- regmap_write(es9356->regmap, -- SDW_SDCA_CTL(func, pde_entity, ES9356_SDCA_CTL_REQ_POWER_STATE, 0), ps?ps3:ps0); -- es9356_pde_transition_delay(es9356, func, pde_entity, ps?ps3:ps0); -- } else -- dev_dbg(component->dev, "%s PDE is already %d\n", __func__, ps?ps0:ps3); -- -- mutex_unlock(&es9356->pde_lock); -+ scoped_guard(mutex, &es9356->pde_lock) { -+ ret = es9356_pde_transition_delay(es9356, func, pde_entity, ps ? ps0 : ps3); -+ if (ret) { -+ regmap_write(es9356->regmap, -+ SDW_SDCA_CTL(func, pde_entity, -+ ES9356_SDCA_CTL_REQ_POWER_STATE, 0), -+ ps ? ps3 : ps0); -+ es9356_pde_transition_delay(es9356, func, pde_entity, ps ? ps3 : ps0); -+ } else { -+ dev_dbg(component->dev, "%s PDE is already %d\n", __func__, -+ ps ? ps0 : ps3); -+ } -+ } - - if (rate) - regmap_write(es9356->regmap, -@@ -671,7 +673,7 @@ static int es9356_sdca_mbq_size(struct device *dev, unsigned int reg) - } - } - --static struct regmap_sdw_mbq_cfg es9356_mbq_config = { -+static const struct regmap_sdw_mbq_cfg es9356_mbq_config = { - .mbq_size = es9356_sdca_mbq_size, - }; - -@@ -1091,9 +1093,8 @@ static int es9356_sdca_dev_system_suspend(struct device *dev) - { - struct es9356_sdw_priv *es9356 = dev_get_drvdata(dev); - -- mutex_lock(&es9356->disable_irq_lock); -- es9356->disable_irq = true; -- mutex_unlock(&es9356->disable_irq_lock); -+ scoped_guard(mutex, &es9356->disable_irq_lock) -+ es9356->disable_irq = true; - - return es9356_sdca_dev_suspend(dev); - } -diff --git a/sound/soc/codecs/fs-amp-lib.c b/sound/soc/codecs/fs-amp-lib.c ---- a/sound/soc/codecs/fs-amp-lib.c -+++ b/sound/soc/codecs/fs-amp-lib.c -@@ -221,7 +221,7 @@ static void fs_print_firmware_info(struct fs_amp_lib *amp_lib) - - int fs_amp_load_firmware(struct fs_amp_lib *amp_lib, const char *name) - { -- const struct firmware *cont; -+ const struct firmware *cont __free(firmware) = NULL; - struct fs_fwm_header *hdr; - int ret; - -@@ -237,7 +237,6 @@ int fs_amp_load_firmware(struct fs_amp_lib *amp_lib, const char *name) - dev_info(amp_lib->dev, "Loading %s - size: %zu\n", name, cont->size); - - hdr = devm_kmemdup(amp_lib->dev, cont->data, cont->size, GFP_KERNEL); -- release_firmware(cont); - if (!hdr) - return -ENOMEM; - -diff --git a/sound/soc/codecs/fs210x.c b/sound/soc/codecs/fs210x.c ---- a/sound/soc/codecs/fs210x.c -+++ b/sound/soc/codecs/fs210x.c -@@ -4,6 +4,7 @@ - // - // Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd. - -+#include - #include - #include - #include -@@ -771,9 +772,8 @@ static int fs210x_dai_hw_params(struct snd_pcm_substream *substream, - if (fs210x->devid == FS2105S_DEVICE_ID && fs210x->srate == 16000) - return -EOPNOTSUPP; - -- mutex_lock(&fs210x->lock); -- ret = fs210x_set_hw_params(fs210x); -- mutex_unlock(&fs210x->lock); -+ scoped_guard(mutex, &fs210x->lock) -+ ret = fs210x_set_hw_params(fs210x); - if (ret) - dev_err(fs210x->dev, "Failed to set hw params: %d\n", ret); - -@@ -790,15 +790,11 @@ static int fs210x_dai_mute(struct snd_soc_dai *dai, int mute, int stream) - - fs210x = snd_soc_component_get_drvdata(dai->component); - -- mutex_lock(&fs210x->lock); -- -- if (!fs210x->is_inited || fs210x->is_suspended) { -- mutex_unlock(&fs210x->lock); -- return 0; -+ scoped_guard(mutex, &fs210x->lock) { -+ if (!fs210x->is_inited || fs210x->is_suspended) -+ return 0; - } - -- mutex_unlock(&fs210x->lock); -- - if (mute) { - cancel_delayed_work_sync(&fs210x->fault_check_work); - cancel_delayed_work_sync(&fs210x->start_work); -@@ -817,15 +813,11 @@ static int fs210x_dai_trigger(struct snd_pcm_substream *substream, - - fs210x = snd_soc_component_get_drvdata(dai->component); - -- mutex_lock(&fs210x->lock); -- -- if (!fs210x->is_inited || fs210x->is_suspended || fs210x->is_playing) { -- mutex_unlock(&fs210x->lock); -- return 0; -+ scoped_guard(mutex, &fs210x->lock) { -+ if (!fs210x->is_inited || fs210x->is_suspended || fs210x->is_playing) -+ return 0; - } - -- mutex_unlock(&fs210x->lock); -- - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: -@@ -853,13 +845,11 @@ static void fs210x_start_work(struct work_struct *work) - - fs210x = container_of(work, struct fs210x_priv, start_work.work); - -- mutex_lock(&fs210x->lock); -+ guard(mutex)(&fs210x->lock); - - ret = fs210x_dev_play(fs210x); - if (ret) - dev_err(fs210x->dev, "Failed to start playing: %d\n", ret); -- -- mutex_unlock(&fs210x->lock); - } - - static void fs210x_fault_check_work(struct work_struct *work) -@@ -870,15 +860,13 @@ static void fs210x_fault_check_work(struct work_struct *work) - - fs210x = container_of(work, struct fs210x_priv, fault_check_work.work); - -- mutex_lock(&fs210x->lock); -+ scoped_guard(mutex, &fs210x->lock) { -+ if (!fs210x->is_inited || fs210x->is_suspended || !fs210x->is_playing) -+ return; - -- if (!fs210x->is_inited || fs210x->is_suspended || !fs210x->is_playing) { -- mutex_unlock(&fs210x->lock); -- return; -+ ret = fs210x_reg_read(fs210x, FS210X_05H_ANASTAT, &status); - } - -- ret = fs210x_reg_read(fs210x, FS210X_05H_ANASTAT, &status); -- mutex_unlock(&fs210x->lock); - if (ret) - return; - -@@ -991,7 +979,7 @@ static int fs210x_effect_scene_get(struct snd_kcontrol *kcontrol, - if (fs210x->scene_id < 1) - return -EINVAL; - -- mutex_lock(&fs210x->lock); -+ guard(mutex)(&fs210x->lock); - /* - * FS210x has scene(s) as below: - * init scene: id = 0 -@@ -1000,7 +988,6 @@ static int fs210x_effect_scene_get(struct snd_kcontrol *kcontrol, - */ - index = fs210x->scene_id - 1; - ucontrol->value.integer.value[0] = index; -- mutex_unlock(&fs210x->lock); - - return 0; - } -@@ -1019,7 +1006,7 @@ static int fs210x_effect_scene_put(struct snd_kcontrol *kcontrol, - return -EINVAL; - } - -- mutex_lock(&fs210x->lock); -+ guard(mutex)(&fs210x->lock); - - /* - * FS210x has scene(s) as below: -@@ -1029,17 +1016,14 @@ static int fs210x_effect_scene_put(struct snd_kcontrol *kcontrol, - */ - scene_id = ucontrol->value.integer.value[0] + 1; - scene_count = fs210x->amp_lib.scene_count - 1; /* Skip init scene */ -- if (scene_id < 1 || scene_id > scene_count) { -- mutex_unlock(&fs210x->lock); -+ if (scene_id < 1 || scene_id > scene_count) - return -ERANGE; -- } - - if (scene_id != fs210x->scene_id) - is_changed = true; - - if (fs210x->is_suspended) { - fs210x->scene_id = scene_id; -- mutex_unlock(&fs210x->lock); - return is_changed; - } - -@@ -1047,8 +1031,6 @@ static int fs210x_effect_scene_put(struct snd_kcontrol *kcontrol, - if (ret) - dev_err(fs210x->dev, "Failed to set scene: %d\n", ret); - -- mutex_unlock(&fs210x->lock); -- - if (!ret && is_changed) - return 1; - -@@ -1062,12 +1044,10 @@ static int fs210x_playback_event(struct snd_soc_dapm_widget *w, - struct fs210x_priv *fs210x = snd_soc_component_get_drvdata(cmpnt); - int ret = 0; - -- mutex_lock(&fs210x->lock); -+ guard(mutex)(&fs210x->lock); - -- if (fs210x->is_suspended) { -- mutex_unlock(&fs210x->lock); -+ if (fs210x->is_suspended) - return 0; -- } - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: -@@ -1088,8 +1068,6 @@ static int fs210x_playback_event(struct snd_soc_dapm_widget *w, - break; - } - -- mutex_unlock(&fs210x->lock); -- - return ret; - } - -@@ -1220,11 +1198,9 @@ static int fs210x_probe(struct snd_soc_component *cmpnt) - if (ret) - return ret; - -- mutex_lock(&fs210x->lock); -- ret = fs210x_init_chip(fs210x); -- mutex_unlock(&fs210x->lock); -+ guard(mutex)(&fs210x->lock); - -- return ret; -+ return fs210x_init_chip(fs210x); - } - - static void fs210x_remove(struct snd_soc_component *cmpnt) -@@ -1251,15 +1227,15 @@ static int fs210x_suspend(struct snd_soc_component *cmpnt) - - regcache_cache_only(fs210x->regmap, true); - -- mutex_lock(&fs210x->lock); -- fs210x->cur_scene = NULL; -- fs210x->is_inited = false; -- fs210x->is_playing = false; -- fs210x->is_suspended = true; -+ scoped_guard(mutex, &fs210x->lock) { -+ fs210x->cur_scene = NULL; -+ fs210x->is_inited = false; -+ fs210x->is_playing = false; -+ fs210x->is_suspended = true; - -- gpiod_set_value_cansleep(fs210x->gpio_sdz, 1); /* Active */ -- fsleep(30000); /* >= 30ms */ -- mutex_unlock(&fs210x->lock); -+ gpiod_set_value_cansleep(fs210x->gpio_sdz, 1); /* Active */ -+ fsleep(30000); /* >= 30ms */ -+ } - - cancel_delayed_work_sync(&fs210x->start_work); - cancel_delayed_work_sync(&fs210x->fault_check_work); -@@ -1288,14 +1264,11 @@ static int fs210x_resume(struct snd_soc_component *cmpnt) - return ret; - } - -- mutex_lock(&fs210x->lock); -+ guard(mutex)(&fs210x->lock); - - fs210x->is_suspended = false; -- ret = fs210x_init_chip(fs210x); - -- mutex_unlock(&fs210x->lock); -- -- return ret; -+ return fs210x_init_chip(fs210x); - } - #else - #define fs210x_suspend NULL -diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c ---- a/sound/soc/codecs/hdac_hda.c -+++ b/sound/soc/codecs/hdac_hda.c -@@ -437,7 +437,7 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) - - #ifdef CONFIG_SND_HDA_PATCH_LOADER - if (loadable_patch[hda_pvt->dev_index] && *loadable_patch[hda_pvt->dev_index]) { -- const struct firmware *fw; -+ const struct firmware *fw __free(firmware) = NULL; - - dev_info(&hdev->dev, "Applying patch firmware '%s'\n", - loadable_patch[hda_pvt->dev_index]); -@@ -451,7 +451,6 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) - dev_err(&hdev->dev, "%s: failed to load hda patch %d\n", __func__, ret); - goto error_no_pm; - } -- release_firmware(fw); - } - } - #endif -diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c ---- a/sound/soc/codecs/hdac_hdmi.c -+++ b/sound/soc/codecs/hdac_hdmi.c -@@ -10,6 +10,7 @@ - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - -+#include - #include - #include - #include -@@ -537,10 +538,11 @@ static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt( - continue; - - list_for_each_entry(port, &pcm->port_list, head) { -- mutex_lock(&pcm->lock); -- ret = hdac_hdmi_query_port_connlist(hdev, -- port->pin, port); -- mutex_unlock(&pcm->lock); -+ scoped_guard(mutex, &pcm->lock) { -+ ret = hdac_hdmi_query_port_connlist(hdev, -+ port->pin, -+ port); -+ } - if (ret < 0) - continue; - -@@ -640,11 +642,11 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, - pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt); - - if (pcm) { -- mutex_lock(&pcm->lock); -- pcm->chmap_set = false; -- memset(pcm->chmap, 0, sizeof(pcm->chmap)); -- pcm->channels = 0; -- mutex_unlock(&pcm->lock); -+ scoped_guard(mutex, &pcm->lock) { -+ pcm->chmap_set = false; -+ memset(pcm->chmap, 0, sizeof(pcm->chmap)); -+ pcm->channels = 0; -+ } - } - - if (dai_map->port) -@@ -922,7 +924,7 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, - if (port == NULL) - return -EINVAL; - -- mutex_lock(&hdmi->pin_mutex); -+ guard(mutex)(&hdmi->pin_mutex); - list_for_each_entry(pcm, &hdmi->pcm_list, head) { - if (list_empty(&pcm->port_list)) - continue; -@@ -945,12 +947,10 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, - list_add_tail(&port->head, &pcm->port_list); - if (port->eld.monitor_present && port->eld.eld_valid) { - hdac_hdmi_jack_report_sync(pcm, port, true); -- mutex_unlock(&hdmi->pin_mutex); - return ret; - } - } - } -- mutex_unlock(&hdmi->pin_mutex); - - return ret; - } -@@ -1274,67 +1274,65 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, - * In case of non MST pin, get_eld info API expectes port - * to be -1. - */ -- mutex_lock(&hdmi->pin_mutex); -- port->eld.monitor_present = false; -+ scoped_guard(mutex, &hdmi->pin_mutex) { -+ port->eld.monitor_present = false; - -- if (pin->mst_capable) -- port_id = port->id; -+ if (pin->mst_capable) -+ port_id = port->id; - -- size = snd_hdac_acomp_get_eld(hdev, pin->nid, port_id, -- &port->eld.monitor_present, -- port->eld.eld_buffer, -- ELD_MAX_SIZE); -+ size = snd_hdac_acomp_get_eld(hdev, pin->nid, port_id, -+ &port->eld.monitor_present, -+ port->eld.eld_buffer, -+ ELD_MAX_SIZE); - -- if (size > 0) { -- size = min(size, ELD_MAX_SIZE); -- if (hdac_hdmi_parse_eld(hdev, port) < 0) -- size = -EINVAL; -- } -- -- eld_valid = port->eld.eld_valid; -- -- if (size > 0) { -- port->eld.eld_valid = true; -- port->eld.eld_size = size; -- } else { -- port->eld.eld_valid = false; -- port->eld.eld_size = 0; -- } -- -- eld_changed = (eld_valid != port->eld.eld_valid); -- -- pcm = hdac_hdmi_get_pcm(hdev, port); -- -- if (!port->eld.monitor_present || !port->eld.eld_valid) { -- -- dev_dbg(&hdev->dev, "%s: disconnect for pin:port %d:%d\n", -- __func__, pin->nid, port->id); -- -- /* -- * PCMs are not registered during device probe, so don't -- * report jack here. It will be done in usermode mux -- * control select. -- */ -- if (pcm) { -- hdac_hdmi_jack_report(pcm, port, false); -- schedule_work(&port->dapm_work); -+ if (size > 0) { -+ size = min(size, ELD_MAX_SIZE); -+ if (hdac_hdmi_parse_eld(hdev, port) < 0) -+ size = -EINVAL; - } - -- mutex_unlock(&hdmi->pin_mutex); -- return; -- } -+ eld_valid = port->eld.eld_valid; - -- if (port->eld.monitor_present && port->eld.eld_valid) { -- if (pcm) { -- hdac_hdmi_jack_report(pcm, port, true); -- schedule_work(&port->dapm_work); -+ if (size > 0) { -+ port->eld.eld_valid = true; -+ port->eld.eld_size = size; -+ } else { -+ port->eld.eld_valid = false; -+ port->eld.eld_size = 0; - } - -- print_hex_dump_debug("ELD: ", DUMP_PREFIX_OFFSET, 16, 1, -- port->eld.eld_buffer, port->eld.eld_size, false); -+ eld_changed = (eld_valid != port->eld.eld_valid); - -+ pcm = hdac_hdmi_get_pcm(hdev, port); -+ -+ if (!port->eld.monitor_present || !port->eld.eld_valid) { -+ -+ dev_dbg(&hdev->dev, "%s: disconnect for pin:port %d:%d\n", -+ __func__, pin->nid, port->id); -+ -+ /* -+ * PCMs are not registered during device probe, so don't -+ * report jack here. It will be done in usermode mux -+ * control select. -+ */ -+ if (pcm) { -+ hdac_hdmi_jack_report(pcm, port, false); -+ schedule_work(&port->dapm_work); -+ } -+ -+ return; -+ } -+ -+ if (port->eld.monitor_present && port->eld.eld_valid) { -+ if (pcm) { -+ hdac_hdmi_jack_report(pcm, port, true); -+ schedule_work(&port->dapm_work); -+ } -+ -+ print_hex_dump_debug("ELD: ", DUMP_PREFIX_OFFSET, 16, 1, -+ port->eld.eld_buffer, port->eld.eld_size, false); -+ } - } -- mutex_unlock(&hdmi->pin_mutex); - - if (eld_changed && pcm) - snd_ctl_notify(hdmi->card, -@@ -1795,13 +1793,12 @@ static void hdac_hdmi_set_chmap(struct hdac_device *hdev, int pcm_idx, - if (list_empty(&pcm->port_list)) - return; - -- mutex_lock(&pcm->lock); -+ guard(mutex)(&pcm->lock); - pcm->chmap_set = true; - memcpy(pcm->chmap, chmap, ARRAY_SIZE(pcm->chmap)); - list_for_each_entry(port, &pcm->port_list, head) - if (prepared) - hdac_hdmi_setup_audio_infoframe(hdev, pcm, port); -- mutex_unlock(&pcm->lock); - } - - static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdev, int pcm_idx) -@@ -1866,8 +1863,10 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) - snd_hdac_ext_bus_link_get(hdev->bus, hlink); - - hdmi_priv = devm_kzalloc(&hdev->dev, sizeof(*hdmi_priv), GFP_KERNEL); -- if (hdmi_priv == NULL) -+ if (hdmi_priv == NULL) { -+ snd_hdac_ext_bus_link_put(hdev->bus, hlink); - return -ENOMEM; -+ } - - snd_hdac_register_chmap_ops(hdev, &hdmi_priv->chmap); - hdmi_priv->chmap.ops.get_chmap = hdac_hdmi_get_chmap; -@@ -1876,8 +1875,10 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) - hdmi_priv->chmap.ops.get_spk_alloc = hdac_hdmi_get_spk_alloc; - hdmi_priv->hdev = hdev; - -- if (!hdac_id) -+ if (!hdac_id) { -+ snd_hdac_ext_bus_link_put(hdev->bus, hlink); - return -ENODEV; -+ } - - if (hdac_id->driver_data) - hdmi_priv->drv_data = -@@ -1902,6 +1903,8 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) - if (ret < 0) { - dev_err(&hdev->dev, - "Failed in parse and map nid with err: %d\n", ret); -+ snd_hdac_ext_bus_link_put(hdev->bus, hlink); -+ snd_hdac_display_power(hdev->bus, hdev->addr, false); - return ret; - } - snd_hdac_refresh_widgets(hdev); -diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c ---- a/sound/soc/codecs/hdmi-codec.c -+++ b/sound/soc/codecs/hdmi-codec.c -@@ -4,6 +4,7 @@ - * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ - * Author: Jyri Sarha - */ -+#include - #include - #include - #include -@@ -452,31 +453,30 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream, - if (!((has_playback && tx) || (has_capture && !tx))) - return 0; - -- mutex_lock(&hcp->lock); -+ guard(mutex)(&hcp->lock); - if (hcp->busy) { - dev_err(dai->dev, "Only one simultaneous stream supported!\n"); -- mutex_unlock(&hcp->lock); - return -EINVAL; - } - - if (hcp->hcd.ops->audio_startup) { - ret = hcp->hcd.ops->audio_startup(dai->dev->parent, hcp->hcd.data); - if (ret) -- goto err; -+ return ret; - } - - if (tx && hcp->hcd.ops->get_eld) { - ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->hcd.data, - hcp->eld, sizeof(hcp->eld)); - if (ret) -- goto err; -+ return ret; - - snd_parse_eld(dai->dev, &hcp->eld_parsed, - hcp->eld, sizeof(hcp->eld)); - - ret = snd_pcm_hw_constraint_eld(substream->runtime, hcp->eld); - if (ret) -- goto err; -+ return ret; - - /* Select chmap supported */ - hdmi_codec_eld_chmap(hcp); -@@ -484,8 +484,6 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream, - - hcp->busy = true; - --err: -- mutex_unlock(&hcp->lock); - return ret; - } - -@@ -503,9 +501,8 @@ static void hdmi_codec_shutdown(struct snd_pcm_substream *substream, - hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; - hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data); - -- mutex_lock(&hcp->lock); -+ guard(mutex)(&hcp->lock); - hcp->busy = false; -- mutex_unlock(&hcp->lock); - } - - static int hdmi_codec_fill_codec_params(struct snd_soc_dai *dai, -diff --git a/sound/soc/codecs/idt821034.c b/sound/soc/codecs/idt821034.c ---- a/sound/soc/codecs/idt821034.c -+++ b/sound/soc/codecs/idt821034.c -@@ -7,6 +7,7 @@ - // Author: Herve Codina - - #include -+#include - #include - #include - #include -@@ -413,12 +414,12 @@ static int idt821034_kctrl_gain_get(struct snd_kcontrol *kcontrol, - - ch = IDT821034_ID_GET_CHAN(mc->reg); - -- mutex_lock(&idt821034->mutex); -- if (IDT821034_ID_IS_OUT(mc->reg)) -- val = idt821034->amps.ch[ch].amp_out.gain; -- else -- val = idt821034->amps.ch[ch].amp_in.gain; -- mutex_unlock(&idt821034->mutex); -+ scoped_guard(mutex, &idt821034->mutex) { -+ if (IDT821034_ID_IS_OUT(mc->reg)) -+ val = idt821034->amps.ch[ch].amp_out.gain; -+ else -+ val = idt821034->amps.ch[ch].amp_in.gain; -+ } - - ucontrol->value.integer.value[0] = val & mask; - if (invert) -@@ -456,7 +457,7 @@ static int idt821034_kctrl_gain_put(struct snd_kcontrol *kcontrol, - - ch = IDT821034_ID_GET_CHAN(mc->reg); - -- mutex_lock(&idt821034->mutex); -+ guard(mutex)(&idt821034->mutex); - - if (IDT821034_ID_IS_OUT(mc->reg)) { - amp = &idt821034->amps.ch[ch].amp_out; -@@ -466,22 +467,18 @@ static int idt821034_kctrl_gain_put(struct snd_kcontrol *kcontrol, - gain_type = IDT821034_GAIN_TX; - } - -- if (amp->gain == val) { -- ret = 0; -- goto end; -- } -+ if (amp->gain == val) -+ return 0; - - if (!amp->is_muted) { - ret = idt821034_set_gain_channel(idt821034, ch, gain_type, val); - if (ret) -- goto end; -+ return ret; - } - - amp->gain = val; -- ret = 1; /* The value changed */ --end: -- mutex_unlock(&idt821034->mutex); -- return ret; -+ -+ return 1; - } - - static int idt821034_kctrl_mute_get(struct snd_kcontrol *kcontrol, -@@ -495,11 +492,11 @@ static int idt821034_kctrl_mute_get(struct snd_kcontrol *kcontrol, - - ch = IDT821034_ID_GET_CHAN(id); - -- mutex_lock(&idt821034->mutex); -- is_muted = IDT821034_ID_IS_OUT(id) ? -- idt821034->amps.ch[ch].amp_out.is_muted : -- idt821034->amps.ch[ch].amp_in.is_muted; -- mutex_unlock(&idt821034->mutex); -+ scoped_guard(mutex, &idt821034->mutex) { -+ is_muted = IDT821034_ID_IS_OUT(id) ? -+ idt821034->amps.ch[ch].amp_out.is_muted : -+ idt821034->amps.ch[ch].amp_in.is_muted; -+ } - - ucontrol->value.integer.value[0] = !is_muted; - -@@ -521,7 +518,7 @@ static int idt821034_kctrl_mute_put(struct snd_kcontrol *kcontrol, - ch = IDT821034_ID_GET_CHAN(id); - is_mute = !ucontrol->value.integer.value[0]; - -- mutex_lock(&idt821034->mutex); -+ guard(mutex)(&idt821034->mutex); - - if (IDT821034_ID_IS_OUT(id)) { - amp = &idt821034->amps.ch[ch].amp_out; -@@ -531,21 +528,17 @@ static int idt821034_kctrl_mute_put(struct snd_kcontrol *kcontrol, - gain_type = IDT821034_GAIN_TX; - } - -- if (amp->is_muted == is_mute) { -- ret = 0; -- goto end; -- } -+ if (amp->is_muted == is_mute) -+ return 0; - - ret = idt821034_set_gain_channel(idt821034, ch, gain_type, - is_mute ? 0 : amp->gain); - if (ret) -- goto end; -+ return ret; - - amp->is_muted = is_mute; -- ret = 1; /* The value changed */ --end: -- mutex_unlock(&idt821034->mutex); -- return ret; -+ -+ return 1; - } - - static const DECLARE_TLV_DB_LINEAR(idt821034_gain_in, -300, 1300); -@@ -623,24 +616,20 @@ static int idt821034_power_event(struct snd_soc_dapm_widget *w, - struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component); - unsigned int id = w->shift; - u8 power, mask; -- int ret; - u8 ch; - - ch = IDT821034_ID_GET_CHAN(id); - mask = IDT821034_ID_IS_OUT(id) ? IDT821034_CONF_PWRUP_RX : IDT821034_CONF_PWRUP_TX; - -- mutex_lock(&idt821034->mutex); -+ guard(mutex)(&idt821034->mutex); - - power = idt821034_get_channel_power(idt821034, ch); - if (SND_SOC_DAPM_EVENT_ON(event)) - power |= mask; - else - power &= ~mask; -- ret = idt821034_set_channel_power(idt821034, ch, power); - -- mutex_unlock(&idt821034->mutex); -- -- return ret; -+ return idt821034_set_channel_power(idt821034, ch, power); - } - - static const struct snd_soc_dapm_widget idt821034_dapm_widgets[] = { -@@ -717,9 +706,9 @@ static int idt821034_dai_set_tdm_slot(struct snd_soc_dai *dai, - ch = 0; - while (mask && ch < IDT821034_NB_CHANNEL) { - if (mask & 0x1) { -- mutex_lock(&idt821034->mutex); -- ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_RX, slot); -- mutex_unlock(&idt821034->mutex); -+ scoped_guard(mutex, &idt821034->mutex) -+ ret = idt821034_set_channel_ts(idt821034, ch, -+ IDT821034_CH_RX, slot); - if (ret) { - dev_err(dai->dev, "ch%u set tx tdm slot failed (%d)\n", - ch, ret); -@@ -742,9 +731,9 @@ static int idt821034_dai_set_tdm_slot(struct snd_soc_dai *dai, - ch = 0; - while (mask && ch < IDT821034_NB_CHANNEL) { - if (mask & 0x1) { -- mutex_lock(&idt821034->mutex); -- ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_TX, slot); -- mutex_unlock(&idt821034->mutex); -+ scoped_guard(mutex, &idt821034->mutex) -+ ret = idt821034_set_channel_ts(idt821034, ch, -+ IDT821034_CH_TX, slot); - if (ret) { - dev_err(dai->dev, "ch%u set rx tdm slot failed (%d)\n", - ch, ret); -@@ -769,9 +758,8 @@ static int idt821034_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) - { - struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component); - u8 conf; -- int ret; - -- mutex_lock(&idt821034->mutex); -+ guard(mutex)(&idt821034->mutex); - - conf = idt821034_get_codec_conf(idt821034); - -@@ -785,13 +773,10 @@ static int idt821034_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) - default: - dev_err(dai->dev, "Unsupported DAI format 0x%x\n", - fmt & SND_SOC_DAIFMT_FORMAT_MASK); -- ret = -EINVAL; -- goto end; -+ return -EINVAL; - } -- ret = idt821034_set_codec_conf(idt821034, conf); --end: -- mutex_unlock(&idt821034->mutex); -- return ret; -+ -+ return idt821034_set_codec_conf(idt821034, conf); - } - - static int idt821034_dai_hw_params(struct snd_pcm_substream *substream, -@@ -800,9 +785,8 @@ static int idt821034_dai_hw_params(struct snd_pcm_substream *substream, - { - struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component); - u8 conf; -- int ret; - -- mutex_lock(&idt821034->mutex); -+ guard(mutex)(&idt821034->mutex); - - conf = idt821034_get_codec_conf(idt821034); - -@@ -816,13 +800,10 @@ static int idt821034_dai_hw_params(struct snd_pcm_substream *substream, - default: - dev_err(dai->dev, "Unsupported PCM format 0x%x\n", - params_format(params)); -- ret = -EINVAL; -- goto end; -+ return -EINVAL; - } -- ret = idt821034_set_codec_conf(idt821034, conf); --end: -- mutex_unlock(&idt821034->mutex); -- return ret; -+ -+ return idt821034_set_codec_conf(idt821034, conf); - } - - static const unsigned int idt821034_sample_bits[] = {8}; -@@ -897,11 +878,11 @@ static int idt821034_reset_audio(struct idt821034 *idt821034) - int ret; - u8 i; - -- mutex_lock(&idt821034->mutex); -+ guard(mutex)(&idt821034->mutex); - - ret = idt821034_set_codec_conf(idt821034, 0); - if (ret) -- goto end; -+ return ret; - - for (i = 0; i < IDT821034_NB_CHANNEL; i++) { - idt821034->amps.ch[i].amp_out.gain = IDT821034_GAIN_OUT_INIT_RAW; -@@ -909,24 +890,21 @@ static int idt821034_reset_audio(struct idt821034 *idt821034) - ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_RX, - idt821034->amps.ch[i].amp_out.gain); - if (ret) -- goto end; -+ return ret; - - idt821034->amps.ch[i].amp_in.gain = IDT821034_GAIN_IN_INIT_RAW; - idt821034->amps.ch[i].amp_in.is_muted = false; - ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_TX, - idt821034->amps.ch[i].amp_in.gain); - if (ret) -- goto end; -+ return ret; - - ret = idt821034_set_channel_power(idt821034, i, 0); - if (ret) -- goto end; -+ return ret; - } - -- ret = 0; --end: -- mutex_unlock(&idt821034->mutex); -- return ret; -+ return 0; - } - - static int idt821034_component_probe(struct snd_soc_component *component) -@@ -965,7 +943,7 @@ static int idt821034_chip_gpio_set(struct gpio_chip *c, unsigned int offset, - u8 slic_raw; - int ret; - -- mutex_lock(&idt821034->mutex); -+ guard(mutex)(&idt821034->mutex); - - slic_raw = idt821034_get_written_slic_raw(idt821034, ch); - if (val) -@@ -974,8 +952,6 @@ static int idt821034_chip_gpio_set(struct gpio_chip *c, unsigned int offset, - slic_raw &= ~mask; - ret = idt821034_write_slic_raw(idt821034, ch, slic_raw); - -- mutex_unlock(&idt821034->mutex); -- - if (ret) - dev_err(&idt821034->spi->dev, "set gpio %d (%u, 0x%x) failed (%d)\n", - offset, ch, mask, ret); -@@ -991,9 +967,8 @@ static int idt821034_chip_gpio_get(struct gpio_chip *c, unsigned int offset) - u8 slic_raw; - int ret; - -- mutex_lock(&idt821034->mutex); -- ret = idt821034_read_slic_raw(idt821034, ch, &slic_raw); -- mutex_unlock(&idt821034->mutex); -+ scoped_guard(mutex, &idt821034->mutex) -+ ret = idt821034_read_slic_raw(idt821034, ch, &slic_raw); - if (ret) { - dev_err(&idt821034->spi->dev, "get gpio %d (%u, 0x%x) failed (%d)\n", - offset, ch, mask, ret); -@@ -1015,9 +990,8 @@ static int idt821034_chip_get_direction(struct gpio_chip *c, unsigned int offset - struct idt821034 *idt821034 = gpiochip_get_data(c); - u8 slic_dir; - -- mutex_lock(&idt821034->mutex); -+ guard(mutex)(&idt821034->mutex); - slic_dir = idt821034_get_slic_conf(idt821034, ch); -- mutex_unlock(&idt821034->mutex); - - return slic_dir & mask ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT; - } -@@ -1034,7 +1008,7 @@ static int idt821034_chip_direction_input(struct gpio_chip *c, unsigned int offs - if (mask & ~(IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN)) - return -EPERM; - -- mutex_lock(&idt821034->mutex); -+ guard(mutex)(&idt821034->mutex); - - slic_conf = idt821034_get_slic_conf(idt821034, ch) | mask; - -@@ -1044,7 +1018,6 @@ static int idt821034_chip_direction_input(struct gpio_chip *c, unsigned int offs - offset, ch, mask, ret); - } - -- mutex_unlock(&idt821034->mutex); - return ret; - } - -@@ -1060,7 +1033,7 @@ static int idt821034_chip_direction_output(struct gpio_chip *c, unsigned int off - if (ret) - return ret; - -- mutex_lock(&idt821034->mutex); -+ guard(mutex)(&idt821034->mutex); - - slic_conf = idt821034_get_slic_conf(idt821034, ch) & ~mask; - -@@ -1070,7 +1043,6 @@ static int idt821034_chip_direction_output(struct gpio_chip *c, unsigned int off - offset, ch, mask, ret); - } - -- mutex_unlock(&idt821034->mutex); - return ret; - } - -@@ -1079,24 +1051,22 @@ static int idt821034_reset_gpio(struct idt821034 *idt821034) - int ret; - u8 i; - -- mutex_lock(&idt821034->mutex); -+ guard(mutex)(&idt821034->mutex); - - /* IO0 and IO1 as input for all channels and output IO set to 0 */ - for (i = 0; i < IDT821034_NB_CHANNEL; i++) { - ret = idt821034_set_slic_conf(idt821034, i, - IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN); - if (ret) -- goto end; -+ return ret; - - ret = idt821034_write_slic_raw(idt821034, i, 0); - if (ret) -- goto end; -+ return ret; - - } -- ret = 0; --end: -- mutex_unlock(&idt821034->mutex); -- return ret; -+ -+ return 0; - } - - static int idt821034_gpio_init(struct idt821034 *idt821034) -diff --git a/sound/soc/codecs/lochnagar-sc.c b/sound/soc/codecs/lochnagar-sc.c ---- a/sound/soc/codecs/lochnagar-sc.c -+++ b/sound/soc/codecs/lochnagar-sc.c -@@ -137,15 +137,23 @@ static int lochnagar_sc_set_usb_fmt(struct snd_soc_dai *dai, unsigned int fmt) - return lochnagar_sc_check_fmt(dai, fmt, SND_SOC_DAIFMT_CBP_CFP); - } - -+static const u64 lochnagar_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF; -+ - static const struct snd_soc_dai_ops lochnagar_sc_line_ops = { - .startup = lochnagar_sc_line_startup, - .shutdown = lochnagar_sc_line_shutdown, - .set_fmt = lochnagar_sc_set_line_fmt, -+ .auto_selectable_formats = &lochnagar_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static const struct snd_soc_dai_ops lochnagar_sc_usb_ops = { - .startup = lochnagar_sc_startup, - .set_fmt = lochnagar_sc_set_usb_fmt, -+ .auto_selectable_formats = &lochnagar_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static struct snd_soc_dai_driver lochnagar_sc_dai[] = { -diff --git a/sound/soc/codecs/lpass-macro-common.c b/sound/soc/codecs/lpass-macro-common.c ---- a/sound/soc/codecs/lpass-macro-common.c -+++ b/sound/soc/codecs/lpass-macro-common.c -@@ -1,6 +1,7 @@ - // SPDX-License-Identifier: GPL-2.0-only - // Copyright (c) 2022, The Linux Foundation. All rights reserved. - -+#include - #include - #include - #include -@@ -71,21 +72,16 @@ EXPORT_SYMBOL_GPL(lpass_macro_pds_exit); - - void lpass_macro_set_codec_version(enum lpass_codec_version version) - { -- mutex_lock(&lpass_codec_mutex); -+ guard(mutex)(&lpass_codec_mutex); - lpass_codec_version = version; -- mutex_unlock(&lpass_codec_mutex); - } - EXPORT_SYMBOL_GPL(lpass_macro_set_codec_version); - - enum lpass_codec_version lpass_macro_get_codec_version(void) - { -- enum lpass_codec_version ver; -+ guard(mutex)(&lpass_codec_mutex); - -- mutex_lock(&lpass_codec_mutex); -- ver = lpass_codec_version; -- mutex_unlock(&lpass_codec_mutex); -- -- return ver; -+ return lpass_codec_version; - } - EXPORT_SYMBOL_GPL(lpass_macro_get_codec_version); - -diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c ---- a/sound/soc/codecs/lpass-rx-macro.c -+++ b/sound/soc/codecs/lpass-rx-macro.c -@@ -6,6 +6,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -2030,9 +2031,10 @@ static struct snd_soc_dai_driver rx_macro_dai[] = { - }, - }; - --static void rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable) -+static int rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable) - { - struct regmap *regmap = rx->regmap; -+ int ret; - - if (mclk_enable) { - if (rx->rx_mclk_users == 0) { -@@ -2047,14 +2049,16 @@ static void rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable) - CDC_RX_FS_MCLK_CNT_EN_MASK, - CDC_RX_FS_MCLK_CNT_ENABLE); - regcache_mark_dirty(regmap); -- regcache_sync(regmap); -+ ret = regcache_sync(regmap); -+ if (ret) -+ return ret; - } - rx->rx_mclk_users++; - } else { - if (rx->rx_mclk_users <= 0) { - dev_err(rx->dev, "%s: clock already disabled\n", __func__); - rx->rx_mclk_users = 0; -- return; -+ return 0; - } - rx->rx_mclk_users--; - if (rx->rx_mclk_users == 0) { -@@ -2068,6 +2072,8 @@ static void rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable) - CDC_RX_CLK_MCLK2_EN_MASK, 0x0); - } - } -+ -+ return 0; - } - - static int rx_macro_mclk_event(struct snd_soc_dapm_widget *w, -@@ -2079,11 +2085,9 @@ static int rx_macro_mclk_event(struct snd_soc_dapm_widget *w, - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: -- rx_macro_mclk_enable(rx, true); -- break; -+ return rx_macro_mclk_enable(rx, true); - case SND_SOC_DAPM_POST_PMD: -- rx_macro_mclk_enable(rx, false); -- break; -+ return rx_macro_mclk_enable(rx, false); - default: - dev_err(component->dev, "%s: invalid DAPM event %d\n", __func__, event); - ret = -EINVAL; -@@ -3671,14 +3675,16 @@ static int swclk_gate_enable(struct clk_hw *hw) - struct rx_macro *rx = to_rx_macro(hw); - int ret; - -- ret = clk_prepare_enable(rx->mclk); -+ ret = pm_runtime_resume_and_get(rx->dev); -+ if (ret < 0) -+ return ret; -+ -+ ret = rx_macro_mclk_enable(rx, true); - if (ret) { -- dev_err(rx->dev, "unable to prepare mclk\n"); -+ clk_disable_unprepare(rx->mclk); - return ret; - } - -- rx_macro_mclk_enable(rx, true); -- - regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, - CDC_RX_SWR_CLK_EN_MASK, 1); - -@@ -3693,7 +3699,7 @@ static void swclk_gate_disable(struct clk_hw *hw) - CDC_RX_SWR_CLK_EN_MASK, 0); - - rx_macro_mclk_enable(rx, false); -- clk_disable_unprepare(rx->mclk); -+ pm_runtime_put_autosuspend(rx->dev); - } - - static int swclk_gate_is_enabled(struct clk_hw *hw) -@@ -3864,28 +3870,31 @@ static int rx_macro_probe(struct platform_device *pdev) - rx->dev = dev; - - /* set MCLK and NPL rates */ -- clk_set_rate(rx->mclk, MCLK_FREQ); -- clk_set_rate(rx->npl, MCLK_FREQ); -- -- ret = clk_prepare_enable(rx->macro); -+ ret = clk_set_rate(rx->mclk, MCLK_FREQ); - if (ret) - return ret; - -- ret = clk_prepare_enable(rx->dcodec); -+ ret = clk_set_rate(rx->npl, MCLK_FREQ); - if (ret) -- goto err_dcodec; -+ return ret; - -- ret = clk_prepare_enable(rx->mclk); -+ ret = devm_pm_clk_create(dev); - if (ret) -- goto err_mclk; -+ return ret; - -- ret = clk_prepare_enable(rx->npl); -- if (ret) -- goto err_npl; -+ ret = of_pm_clk_add_clks(dev); -+ if (ret < 0) -+ return ret; - -- ret = clk_prepare_enable(rx->fsgen); -+ pm_runtime_set_autosuspend_delay(dev, 100); -+ pm_runtime_use_autosuspend(dev); -+ ret = devm_pm_runtime_enable(dev); - if (ret) -- goto err_fsgen; -+ return ret; -+ -+ ret = pm_runtime_resume_and_get(dev); -+ if (ret) -+ return ret; - - /* reset swr block */ - regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, -@@ -3902,46 +3911,25 @@ static int rx_macro_probe(struct platform_device *pdev) - rx_macro_dai, - ARRAY_SIZE(rx_macro_dai)); - if (ret) -- goto err_clkout; -- -- -- pm_runtime_set_autosuspend_delay(dev, 3000); -- pm_runtime_use_autosuspend(dev); -- pm_runtime_mark_last_busy(dev); -- pm_runtime_set_active(dev); -- pm_runtime_enable(dev); -+ goto err_rpm_put; - - ret = rx_macro_register_mclk_output(rx); - if (ret) -- goto err_clkout; -+ goto err_rpm_put; -+ -+ ret = pm_runtime_put_autosuspend(dev); -+ if (ret < 0) -+ dev_warn(dev, "runtime PM put failed after probe: %d\n", ret); - - return 0; - --err_clkout: -- clk_disable_unprepare(rx->fsgen); --err_fsgen: -- clk_disable_unprepare(rx->npl); --err_npl: -- clk_disable_unprepare(rx->mclk); --err_mclk: -- clk_disable_unprepare(rx->dcodec); --err_dcodec: -- clk_disable_unprepare(rx->macro); -+err_rpm_put: -+ if (pm_runtime_put_sync_suspend(dev) < 0) -+ dev_warn(dev, "runtime PM sync suspend failed in probe unwind\n"); - - return ret; - } - --static void rx_macro_remove(struct platform_device *pdev) --{ -- struct rx_macro *rx = dev_get_drvdata(&pdev->dev); -- -- clk_disable_unprepare(rx->mclk); -- clk_disable_unprepare(rx->npl); -- clk_disable_unprepare(rx->fsgen); -- clk_disable_unprepare(rx->macro); -- clk_disable_unprepare(rx->dcodec); --} -- - static const struct of_device_id rx_macro_dt_match[] = { - { - .compatible = "qcom,sc7280-lpass-rx-macro", -@@ -3969,13 +3957,17 @@ MODULE_DEVICE_TABLE(of, rx_macro_dt_match); - static int rx_macro_runtime_suspend(struct device *dev) - { - struct rx_macro *rx = dev_get_drvdata(dev); -+ int ret; - - regcache_cache_only(rx->regmap, true); -- regcache_mark_dirty(rx->regmap); - -- clk_disable_unprepare(rx->fsgen); -- clk_disable_unprepare(rx->npl); -- clk_disable_unprepare(rx->mclk); -+ ret = pm_clk_suspend(dev); -+ if (ret) { -+ regcache_cache_only(rx->regmap, false); -+ return ret; -+ } -+ -+ regcache_mark_dirty(rx->regmap); - - return 0; - } -@@ -3985,33 +3977,23 @@ static int rx_macro_runtime_resume(struct device *dev) - struct rx_macro *rx = dev_get_drvdata(dev); - int ret; - -- ret = clk_prepare_enable(rx->mclk); -+ ret = pm_clk_resume(dev); - if (ret) { -- dev_err(dev, "unable to prepare mclk\n"); -+ regcache_cache_only(rx->regmap, true); -+ regcache_mark_dirty(rx->regmap); - return ret; - } - -- ret = clk_prepare_enable(rx->npl); -- if (ret) { -- dev_err(dev, "unable to prepare mclkx2\n"); -- goto err_npl; -- } -- -- ret = clk_prepare_enable(rx->fsgen); -- if (ret) { -- dev_err(dev, "unable to prepare fsgen\n"); -- goto err_fsgen; -- } - regcache_cache_only(rx->regmap, false); -- regcache_sync(rx->regmap); -+ ret = regcache_sync(rx->regmap); -+ if (ret) { -+ regcache_cache_only(rx->regmap, true); -+ regcache_mark_dirty(rx->regmap); -+ pm_clk_suspend(dev); -+ return ret; -+ } - - return 0; --err_fsgen: -- clk_disable_unprepare(rx->npl); --err_npl: -- clk_disable_unprepare(rx->mclk); -- -- return ret; - } - - static const struct dev_pm_ops rx_macro_pm_ops = { -@@ -4026,7 +4008,6 @@ static struct platform_driver rx_macro_driver = { - .pm = pm_ptr(&rx_macro_pm_ops), - }, - .probe = rx_macro_probe, -- .remove = rx_macro_remove, - }; - - module_platform_driver(rx_macro_driver); -diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c ---- a/sound/soc/codecs/lpass-tx-macro.c -+++ b/sound/soc/codecs/lpass-tx-macro.c -@@ -6,6 +6,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -614,6 +615,7 @@ static int tx_macro_mclk_enable(struct tx_macro *tx, - bool mclk_enable) - { - struct regmap *regmap = tx->regmap; -+ int ret; - - if (mclk_enable) { - if (tx->tx_mclk_users == 0) { -@@ -626,7 +628,9 @@ static int tx_macro_mclk_enable(struct tx_macro *tx, - CDC_TX_FS_CNT_EN_MASK, - CDC_TX_FS_CNT_ENABLE); - regcache_mark_dirty(regmap); -- regcache_sync(regmap); -+ ret = regcache_sync(regmap); -+ if (ret) -+ return ret; - } - tx->tx_mclk_users++; - } else { -@@ -739,11 +743,9 @@ static int tx_macro_mclk_event(struct snd_soc_dapm_widget *w, - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: -- tx_macro_mclk_enable(tx, true); -- break; -+ return tx_macro_mclk_enable(tx, true); - case SND_SOC_DAPM_POST_PMD: -- tx_macro_mclk_enable(tx, false); -- break; -+ return tx_macro_mclk_enable(tx, false); - default: - break; - } -@@ -2149,17 +2151,20 @@ static int swclk_gate_enable(struct clk_hw *hw) - struct regmap *regmap = tx->regmap; - int ret; - -- ret = clk_prepare_enable(tx->mclk); -+ ret = pm_runtime_resume_and_get(tx->dev); -+ if (ret < 0) -+ return ret; -+ -+ ret = tx_macro_mclk_enable(tx, true); - if (ret) { -- dev_err(tx->dev, "failed to enable mclk\n"); -+ pm_runtime_put_autosuspend(tx->dev); - return ret; - } - -- tx_macro_mclk_enable(tx, true); -- - regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, - CDC_TX_SWR_CLK_EN_MASK, - CDC_TX_SWR_CLK_ENABLE); -+ - return 0; - } - -@@ -2172,7 +2177,7 @@ static void swclk_gate_disable(struct clk_hw *hw) - CDC_TX_SWR_CLK_EN_MASK, 0x0); - - tx_macro_mclk_enable(tx, false); -- clk_disable_unprepare(tx->mclk); -+ pm_runtime_put_autosuspend(tx->dev); - } - - static int swclk_gate_is_enabled(struct clk_hw *hw) -@@ -2315,28 +2320,31 @@ static int tx_macro_probe(struct platform_device *pdev) - tx->active_decimator[TX_MACRO_AIF3_CAP] = -1; - - /* set MCLK and NPL rates */ -- clk_set_rate(tx->mclk, MCLK_FREQ); -- clk_set_rate(tx->npl, MCLK_FREQ); -- -- ret = clk_prepare_enable(tx->macro); -+ ret = clk_set_rate(tx->mclk, MCLK_FREQ); - if (ret) - goto err; - -- ret = clk_prepare_enable(tx->dcodec); -+ ret = clk_set_rate(tx->npl, MCLK_FREQ); - if (ret) -- goto err_dcodec; -+ goto err; - -- ret = clk_prepare_enable(tx->mclk); -+ ret = devm_pm_clk_create(dev); - if (ret) -- goto err_mclk; -+ goto err; - -- ret = clk_prepare_enable(tx->npl); -- if (ret) -- goto err_npl; -+ ret = of_pm_clk_add_clks(dev); -+ if (ret < 0) -+ goto err; - -- ret = clk_prepare_enable(tx->fsgen); -+ pm_runtime_set_autosuspend_delay(dev, 100); -+ pm_runtime_use_autosuspend(dev); -+ ret = devm_pm_runtime_enable(dev); - if (ret) -- goto err_fsgen; -+ goto err; -+ -+ ret = pm_runtime_resume_and_get(dev); -+ if (ret < 0) -+ goto err; - - - /* reset soundwire block */ -@@ -2356,30 +2364,21 @@ static int tx_macro_probe(struct platform_device *pdev) - tx_macro_dai, - ARRAY_SIZE(tx_macro_dai)); - if (ret) -- goto err_clkout; -- -- pm_runtime_set_autosuspend_delay(dev, 3000); -- pm_runtime_use_autosuspend(dev); -- pm_runtime_mark_last_busy(dev); -- pm_runtime_set_active(dev); -- pm_runtime_enable(dev); -+ goto err_rpm_put; - - ret = tx_macro_register_mclk_output(tx); - if (ret) -- goto err_clkout; -+ goto err_rpm_put; -+ -+ ret = pm_runtime_put_autosuspend(dev); -+ if (ret < 0) -+ dev_warn(dev, "runtime PM put failed after probe: %d\n", ret); - - return 0; - --err_clkout: -- clk_disable_unprepare(tx->fsgen); --err_fsgen: -- clk_disable_unprepare(tx->npl); --err_npl: -- clk_disable_unprepare(tx->mclk); --err_mclk: -- clk_disable_unprepare(tx->dcodec); --err_dcodec: -- clk_disable_unprepare(tx->macro); -+err_rpm_put: -+ if (pm_runtime_put_sync_suspend(dev) < 0) -+ dev_warn(dev, "runtime PM sync suspend failed in probe unwind\n"); - err: - lpass_macro_pds_exit(tx->pds); - -@@ -2390,25 +2389,23 @@ static void tx_macro_remove(struct platform_device *pdev) - { - struct tx_macro *tx = dev_get_drvdata(&pdev->dev); - -- clk_disable_unprepare(tx->macro); -- clk_disable_unprepare(tx->dcodec); -- clk_disable_unprepare(tx->mclk); -- clk_disable_unprepare(tx->npl); -- clk_disable_unprepare(tx->fsgen); -- - lpass_macro_pds_exit(tx->pds); - } - - static int tx_macro_runtime_suspend(struct device *dev) - { - struct tx_macro *tx = dev_get_drvdata(dev); -+ int ret; - - regcache_cache_only(tx->regmap, true); -- regcache_mark_dirty(tx->regmap); - -- clk_disable_unprepare(tx->fsgen); -- clk_disable_unprepare(tx->npl); -- clk_disable_unprepare(tx->mclk); -+ ret = pm_clk_suspend(dev); -+ if (ret) { -+ regcache_cache_only(tx->regmap, false); -+ return ret; -+ } -+ -+ regcache_mark_dirty(tx->regmap); - - return 0; - } -@@ -2418,34 +2415,23 @@ static int tx_macro_runtime_resume(struct device *dev) - struct tx_macro *tx = dev_get_drvdata(dev); - int ret; - -- ret = clk_prepare_enable(tx->mclk); -+ ret = pm_clk_resume(dev); - if (ret) { -- dev_err(dev, "unable to prepare mclk\n"); -+ regcache_cache_only(tx->regmap, true); -+ regcache_mark_dirty(tx->regmap); - return ret; - } - -- ret = clk_prepare_enable(tx->npl); -- if (ret) { -- dev_err(dev, "unable to prepare npl\n"); -- goto err_npl; -- } -- -- ret = clk_prepare_enable(tx->fsgen); -- if (ret) { -- dev_err(dev, "unable to prepare fsgen\n"); -- goto err_fsgen; -- } -- - regcache_cache_only(tx->regmap, false); -- regcache_sync(tx->regmap); -+ ret = regcache_sync(tx->regmap); -+ if (ret) { -+ regcache_cache_only(tx->regmap, true); -+ regcache_mark_dirty(tx->regmap); -+ pm_clk_suspend(dev); -+ return ret; -+ } - - return 0; --err_fsgen: -- clk_disable_unprepare(tx->npl); --err_npl: -- clk_disable_unprepare(tx->mclk); -- -- return ret; - } - - static const struct dev_pm_ops tx_macro_pm_ops = { -diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c ---- a/sound/soc/codecs/lpass-va-macro.c -+++ b/sound/soc/codecs/lpass-va-macro.c -@@ -10,6 +10,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -1351,34 +1352,43 @@ static int fsgen_gate_enable(struct clk_hw *hw) - { - struct va_macro *va = to_va_macro(hw); - struct regmap *regmap = va->regmap; -- int ret; -+ int ret, rpm_ret; - -- if (va->has_swr_master) { -- ret = clk_prepare_enable(va->mclk); -- if (ret) -- return ret; -- } -+ ret = pm_runtime_resume_and_get(va->dev); -+ if (ret < 0) -+ return ret; - - ret = va_macro_mclk_enable(va, true); -+ if (ret) { -+ rpm_ret = pm_runtime_put_autosuspend(va->dev); -+ if (rpm_ret < 0) -+ dev_warn(va->dev, -+ "runtime PM put failed in fsgen enable unwind: %d\n", -+ rpm_ret); -+ return ret; -+ } - if (va->has_swr_master) - regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, - CDC_VA_SWR_CLK_EN_MASK, CDC_VA_SWR_CLK_ENABLE); - -- return ret; -+ return 0; - } - - static void fsgen_gate_disable(struct clk_hw *hw) - { - struct va_macro *va = to_va_macro(hw); - struct regmap *regmap = va->regmap; -+ int ret; - - if (va->has_swr_master) - regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, - CDC_VA_SWR_CLK_EN_MASK, 0x0); - - va_macro_mclk_enable(va, false); -- if (va->has_swr_master) -- clk_disable_unprepare(va->mclk); -+ -+ ret = pm_runtime_put_autosuspend(va->dev); -+ if (ret < 0) -+ dev_warn(va->dev, "runtime PM put failed in fsgen disable: %d\n", ret); - } - - static int fsgen_gate_is_enabled(struct clk_hw *hw) -@@ -1539,6 +1549,7 @@ static int va_macro_probe(struct platform_device *pdev) - void __iomem *base; - u32 sample_rate = 0; - int ret; -+ int rpm_ret; - - va = devm_kzalloc(dev, sizeof(*va), GFP_KERNEL); - if (!va) -@@ -1594,7 +1605,9 @@ static int va_macro_probe(struct platform_device *pdev) - va->has_npl_clk = data->has_npl_clk; - - /* mclk rate */ -- clk_set_rate(va->mclk, 2 * VA_MACRO_MCLK_FREQ); -+ ret = clk_set_rate(va->mclk, 2 * VA_MACRO_MCLK_FREQ); -+ if (ret) -+ goto err; - - if (va->has_npl_clk) { - va->npl = devm_clk_get(dev, "npl"); -@@ -1603,25 +1616,29 @@ static int va_macro_probe(struct platform_device *pdev) - goto err; - } - -- clk_set_rate(va->npl, 2 * VA_MACRO_MCLK_FREQ); -+ ret = clk_set_rate(va->npl, 2 * VA_MACRO_MCLK_FREQ); -+ if (ret) -+ goto err; - } - -- ret = clk_prepare_enable(va->macro); -+ ret = devm_pm_clk_create(dev); - if (ret) - goto err; - -- ret = clk_prepare_enable(va->dcodec); -- if (ret) -- goto err_dcodec; -+ ret = of_pm_clk_add_clks(dev); -+ if (ret < 0) -+ goto err; - -- ret = clk_prepare_enable(va->mclk); -+ pm_runtime_set_autosuspend_delay(dev, 100); -+ pm_runtime_use_autosuspend(dev); -+ ret = devm_pm_runtime_enable(dev); - if (ret) -- goto err_mclk; -+ goto err; - -- if (va->has_npl_clk) { -- ret = clk_prepare_enable(va->npl); -- if (ret) -- goto err_npl; -+ rpm_ret = pm_runtime_resume_and_get(dev); -+ if (rpm_ret < 0) { -+ ret = rpm_ret; -+ goto err; - } - - /** -@@ -1634,7 +1651,7 @@ static int va_macro_probe(struct platform_device *pdev) - /* read version from register */ - ret = va_macro_set_lpass_codec_version(va); - if (ret) -- goto err_clkout; -+ goto err_rpm_put; - } - - if (va->has_swr_master) { -@@ -1664,35 +1681,28 @@ static int va_macro_probe(struct platform_device *pdev) - va_macro_dais, - ARRAY_SIZE(va_macro_dais)); - if (ret) -- goto err_clkout; -- -- pm_runtime_set_autosuspend_delay(dev, 3000); -- pm_runtime_use_autosuspend(dev); -- pm_runtime_mark_last_busy(dev); -- pm_runtime_set_active(dev); -- pm_runtime_enable(dev); -+ goto err_rpm_put; - - ret = va_macro_register_fsgen_output(va); - if (ret) -- goto err_clkout; -+ goto err_rpm_put; - - va->fsgen = devm_clk_hw_get_clk(dev, &va->hw, "fsgen"); - if (IS_ERR(va->fsgen)) { - ret = PTR_ERR(va->fsgen); -- goto err_clkout; -+ goto err_rpm_put; - } - -+ rpm_ret = pm_runtime_put_autosuspend(dev); -+ if (rpm_ret < 0) -+ dev_warn(dev, "runtime PM put failed after probe: %d\n", rpm_ret); -+ - return 0; - --err_clkout: -- if (va->has_npl_clk) -- clk_disable_unprepare(va->npl); --err_npl: -- clk_disable_unprepare(va->mclk); --err_mclk: -- clk_disable_unprepare(va->dcodec); --err_dcodec: -- clk_disable_unprepare(va->macro); -+err_rpm_put: -+ rpm_ret = pm_runtime_put_sync_suspend(dev); -+ if (rpm_ret < 0) -+ dev_warn(dev, "runtime PM sync suspend failed in probe unwind: %d\n", rpm_ret); - err: - lpass_macro_pds_exit(va->pds); - -@@ -1703,53 +1713,52 @@ static void va_macro_remove(struct platform_device *pdev) - { - struct va_macro *va = dev_get_drvdata(&pdev->dev); - -- if (va->has_npl_clk) -- clk_disable_unprepare(va->npl); -- -- clk_disable_unprepare(va->mclk); -- clk_disable_unprepare(va->dcodec); -- clk_disable_unprepare(va->macro); -- - lpass_macro_pds_exit(va->pds); - } - - static int va_macro_runtime_suspend(struct device *dev) - { - struct va_macro *va = dev_get_drvdata(dev); -+ int ret; - - regcache_cache_only(va->regmap, true); -+ -+ ret = pm_clk_suspend(dev); -+ if (ret) { -+ regcache_cache_only(va->regmap, false); -+ return ret; -+ } -+ - regcache_mark_dirty(va->regmap); - -- if (va->has_npl_clk) -- clk_disable_unprepare(va->npl); -- -- clk_disable_unprepare(va->mclk); -- - return 0; - } - - static int va_macro_runtime_resume(struct device *dev) - { - struct va_macro *va = dev_get_drvdata(dev); -- int ret; -+ int ret, sret; - -- ret = clk_prepare_enable(va->mclk); -+ ret = pm_clk_resume(dev); - if (ret) { -- dev_err(va->dev, "unable to prepare mclk\n"); -+ regcache_cache_only(va->regmap, true); -+ regcache_mark_dirty(va->regmap); - return ret; - } - -- if (va->has_npl_clk) { -- ret = clk_prepare_enable(va->npl); -- if (ret) { -- clk_disable_unprepare(va->mclk); -- dev_err(va->dev, "unable to prepare npl\n"); -- return ret; -- } -- } -- - regcache_cache_only(va->regmap, false); -- regcache_sync(va->regmap); -+ -+ ret = regcache_sync(va->regmap); -+ if (ret) { -+ regcache_cache_only(va->regmap, true); -+ regcache_mark_dirty(va->regmap); -+ sret = pm_clk_suspend(dev); -+ if (sret) -+ dev_err(va->dev, -+ "failed to suspend clocks after regcache sync failure: %d\n", -+ sret); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c ---- a/sound/soc/codecs/lpass-wsa-macro.c -+++ b/sound/soc/codecs/lpass-wsa-macro.c -@@ -12,6 +12,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -2529,15 +2530,13 @@ static const struct snd_soc_dapm_route wsa_audio_map[] = { - static int wsa_swrm_clock(struct wsa_macro *wsa, bool enable) - { - struct regmap *regmap = wsa->regmap; -+ int ret; - - if (enable) { -- int ret; -- -- ret = clk_prepare_enable(wsa->mclk); -- if (ret) { -- dev_err(wsa->dev, "failed to enable mclk\n"); -+ ret = pm_runtime_resume_and_get(wsa->dev); -+ if (ret < 0) - return ret; -- } -+ - wsa_macro_mclk_enable(wsa, true); - - regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, -@@ -2548,7 +2547,10 @@ static int wsa_swrm_clock(struct wsa_macro *wsa, bool enable) - regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, - CDC_WSA_SWR_CLK_EN_MASK, 0); - wsa_macro_mclk_enable(wsa, false); -- clk_disable_unprepare(wsa->mclk); -+ -+ ret = pm_runtime_put_autosuspend(wsa->dev); -+ if (ret < 0) -+ dev_warn(wsa->dev, "runtime PM put failed: %d\n", ret); - } - - return 0; -@@ -2656,7 +2658,7 @@ static int wsa_macro_register_mclk_output(struct wsa_macro *wsa) - init.num_parents = 1; - wsa->hw.init = &init; - hw = &wsa->hw; -- ret = clk_hw_register(wsa->dev, hw); -+ ret = devm_clk_hw_register(wsa->dev, hw); - if (ret) - return ret; - -@@ -2771,28 +2773,31 @@ static int wsa_macro_probe(struct platform_device *pdev) - wsa->dev = dev; - - /* set MCLK and NPL rates */ -- clk_set_rate(wsa->mclk, WSA_MACRO_MCLK_FREQ); -- clk_set_rate(wsa->npl, WSA_MACRO_MCLK_FREQ); -- -- ret = clk_prepare_enable(wsa->macro); -+ ret = clk_set_rate(wsa->mclk, WSA_MACRO_MCLK_FREQ); - if (ret) -- goto err; -+ return ret; - -- ret = clk_prepare_enable(wsa->dcodec); -+ ret = clk_set_rate(wsa->npl, WSA_MACRO_MCLK_FREQ); - if (ret) -- goto err_dcodec; -+ return ret; - -- ret = clk_prepare_enable(wsa->mclk); -+ ret = devm_pm_clk_create(dev); - if (ret) -- goto err_mclk; -+ return ret; - -- ret = clk_prepare_enable(wsa->npl); -- if (ret) -- goto err_npl; -+ ret = of_pm_clk_add_clks(dev); -+ if (ret < 0) -+ return ret; - -- ret = clk_prepare_enable(wsa->fsgen); -+ pm_runtime_set_autosuspend_delay(dev, 100); -+ pm_runtime_use_autosuspend(dev); -+ ret = devm_pm_runtime_enable(dev); - if (ret) -- goto err_fsgen; -+ return ret; -+ -+ ret = pm_runtime_resume_and_get(dev); -+ if (ret < 0) -+ return ret; - - /* reset swr ip */ - regmap_update_bits(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, -@@ -2809,56 +2814,37 @@ static int wsa_macro_probe(struct platform_device *pdev) - wsa_macro_dai, - ARRAY_SIZE(wsa_macro_dai)); - if (ret) -- goto err_clkout; -- -- pm_runtime_set_autosuspend_delay(dev, 3000); -- pm_runtime_use_autosuspend(dev); -- pm_runtime_mark_last_busy(dev); -- pm_runtime_set_active(dev); -- pm_runtime_enable(dev); -+ goto err_rpm_put; - - ret = wsa_macro_register_mclk_output(wsa); - if (ret) -- goto err_clkout; -+ goto err_rpm_put; -+ -+ ret = pm_runtime_put_autosuspend(dev); -+ if (ret < 0) -+ dev_warn(dev, "runtime PM put failed after probe: %d\n", ret); - - return 0; -- --err_clkout: -- clk_disable_unprepare(wsa->fsgen); --err_fsgen: -- clk_disable_unprepare(wsa->npl); --err_npl: -- clk_disable_unprepare(wsa->mclk); --err_mclk: -- clk_disable_unprepare(wsa->dcodec); --err_dcodec: -- clk_disable_unprepare(wsa->macro); --err: -+err_rpm_put: -+ if (pm_runtime_put_sync_suspend(dev) < 0) -+ dev_warn(dev, "runtime PM sync suspend failed in probe unwind\n"); - return ret; -- --} -- --static void wsa_macro_remove(struct platform_device *pdev) --{ -- struct wsa_macro *wsa = dev_get_drvdata(&pdev->dev); -- -- clk_disable_unprepare(wsa->macro); -- clk_disable_unprepare(wsa->dcodec); -- clk_disable_unprepare(wsa->mclk); -- clk_disable_unprepare(wsa->npl); -- clk_disable_unprepare(wsa->fsgen); - } - - static int wsa_macro_runtime_suspend(struct device *dev) - { - struct wsa_macro *wsa = dev_get_drvdata(dev); -+ int ret; - - regcache_cache_only(wsa->regmap, true); -- regcache_mark_dirty(wsa->regmap); - -- clk_disable_unprepare(wsa->fsgen); -- clk_disable_unprepare(wsa->npl); -- clk_disable_unprepare(wsa->mclk); -+ ret = pm_clk_suspend(dev); -+ if (ret) { -+ regcache_cache_only(wsa->regmap, false); -+ return ret; -+ } -+ -+ regcache_mark_dirty(wsa->regmap); - - return 0; - } -@@ -2866,36 +2852,29 @@ static int wsa_macro_runtime_suspend(struct device *dev) - static int wsa_macro_runtime_resume(struct device *dev) - { - struct wsa_macro *wsa = dev_get_drvdata(dev); -- int ret; -+ int ret, sret; - -- ret = clk_prepare_enable(wsa->mclk); -+ ret = pm_clk_resume(dev); - if (ret) { -- dev_err(dev, "unable to prepare mclk\n"); -+ regcache_cache_only(wsa->regmap, true); -+ regcache_mark_dirty(wsa->regmap); -+ return ret; -+ } -+ regcache_cache_only(wsa->regmap, false); -+ -+ ret = regcache_sync(wsa->regmap); -+ if (ret) { -+ regcache_cache_only(wsa->regmap, true); -+ regcache_mark_dirty(wsa->regmap); -+ sret = pm_clk_suspend(dev); -+ if (sret) -+ dev_err(dev, -+ "failed to suspend clocks after regcache sync failure: %d\n", -+ sret); - return ret; - } - -- ret = clk_prepare_enable(wsa->npl); -- if (ret) { -- dev_err(dev, "unable to prepare mclkx2\n"); -- goto err_npl; -- } -- -- ret = clk_prepare_enable(wsa->fsgen); -- if (ret) { -- dev_err(dev, "unable to prepare fsgen\n"); -- goto err_fsgen; -- } -- -- regcache_cache_only(wsa->regmap, false); -- regcache_sync(wsa->regmap); -- - return 0; --err_fsgen: -- clk_disable_unprepare(wsa->npl); --err_npl: -- clk_disable_unprepare(wsa->mclk); -- -- return ret; - } - - static const struct dev_pm_ops wsa_macro_pm_ops = { -@@ -2929,7 +2908,6 @@ static struct platform_driver wsa_macro_driver = { - .pm = pm_ptr(&wsa_macro_pm_ops), - }, - .probe = wsa_macro_probe, -- .remove = wsa_macro_remove, - }; - - module_platform_driver(wsa_macro_driver); -diff --git a/sound/soc/codecs/madera.c b/sound/soc/codecs/madera.c ---- a/sound/soc/codecs/madera.c -+++ b/sound/soc/codecs/madera.c -@@ -6,6 +6,7 @@ - // Cirrus Logic International Semiconductor Ltd. - // - -+#include - #include - #include - #include -@@ -513,7 +514,7 @@ int madera_domain_clk_ev(struct snd_soc_dapm_widget *w, - * We can't rely on the DAPM mutex for locking because we need a lock - * that can safely be called in hw_params - */ -- mutex_lock(&priv->rate_lock); -+ guard(mutex)(&priv->rate_lock); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: -@@ -532,8 +533,6 @@ int madera_domain_clk_ev(struct snd_soc_dapm_widget *w, - - madera_debug_dump_domain_groups(priv); - -- mutex_unlock(&priv->rate_lock); -- - return 0; - } - EXPORT_SYMBOL_GPL(madera_domain_clk_ev); -@@ -875,9 +874,8 @@ static int madera_adsp_rate_get(struct snd_kcontrol *kcontrol, - const int adsp_num = e->shift_l; - int item; - -- mutex_lock(&priv->rate_lock); -- cached_rate = priv->adsp_rate_cache[adsp_num]; -- mutex_unlock(&priv->rate_lock); -+ scoped_guard(mutex, &priv->rate_lock) -+ cached_rate = priv->adsp_rate_cache[adsp_num]; - - item = snd_soc_enum_val_to_item(e, cached_rate); - ucontrol->value.enumerated.item[0] = item; -@@ -893,7 +891,6 @@ static int madera_adsp_rate_put(struct snd_kcontrol *kcontrol, - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - const int adsp_num = e->shift_l; - const unsigned int item = ucontrol->value.enumerated.item[0]; -- int ret = 0; - - if (item >= e->items) - return -EINVAL; -@@ -903,22 +900,20 @@ static int madera_adsp_rate_put(struct snd_kcontrol *kcontrol, - * maintain consistent behaviour that rate domains cannot be changed - * while in use since this is a hardware requirement - */ -- mutex_lock(&priv->rate_lock); -+ guard(mutex)(&priv->rate_lock); - - if (!madera_can_change_grp_rate(priv, priv->adsp[adsp_num].cs_dsp.base)) { - dev_warn(priv->madera->dev, - "Cannot change '%s' while in use by active audio paths\n", - kcontrol->id.name); -- ret = -EBUSY; -+ return -EBUSY; - } else if (priv->adsp_rate_cache[adsp_num] != e->values[item]) { - /* Volatile register so defer until the codec is powered up */ - priv->adsp_rate_cache[adsp_num] = e->values[item]; -- ret = 1; -+ return 1; - } - -- mutex_unlock(&priv->rate_lock); -- -- return ret; -+ return 0; - } - - static const struct soc_enum madera_adsp_rate_enum[] = { -@@ -1061,15 +1056,13 @@ int madera_rate_put(struct snd_kcontrol *kcontrol, - * Prevent the domain powering up while we're checking whether it's - * safe to change rate domain - */ -- mutex_lock(&priv->rate_lock); -+ guard(mutex)(&priv->rate_lock); - - val = snd_soc_component_read(component, e->reg); - val >>= e->shift_l; - val &= e->mask; -- if (snd_soc_enum_item_to_val(e, item) == val) { -- ret = 0; -- goto out; -- } -+ if (snd_soc_enum_item_to_val(e, item) == val) -+ return 0; - - if (!madera_can_change_grp_rate(priv, e->reg)) { - dev_warn(priv->madera->dev, -@@ -1082,8 +1075,6 @@ int madera_rate_put(struct snd_kcontrol *kcontrol, - ret = snd_soc_put_enum_double(kcontrol, ucontrol); - madera_spin_sysclk(priv); - } --out: -- mutex_unlock(&priv->rate_lock); - - return ret; - } -@@ -3041,12 +3032,11 @@ static int madera_hw_params_rate(struct snd_pcm_substream *substream, - if ((cur & MADERA_AIF1_RATE_MASK) == (tar & MADERA_AIF1_RATE_MASK)) - return 0; - -- mutex_lock(&priv->rate_lock); -+ guard(mutex)(&priv->rate_lock); - - if (!madera_can_change_grp_rate(priv, base + MADERA_AIF_RATE_CTRL)) { - madera_aif_warn(dai, "Cannot change rate while active\n"); -- ret = -EBUSY; -- goto out; -+ return -EBUSY; - } - - /* Guard the rate change with SYSCLK cycles */ -@@ -3055,9 +3045,6 @@ static int madera_hw_params_rate(struct snd_pcm_substream *substream, - MADERA_AIF1_RATE_MASK, tar); - madera_spin_sysclk(priv); - --out: -- mutex_unlock(&priv->rate_lock); -- - return ret; - } - -@@ -3345,6 +3332,16 @@ static int madera_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, - return 0; - } - -+static const u64 madera_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_B | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_NB_IF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_IF; -+ - const struct snd_soc_dai_ops madera_dai_ops = { - .startup = &madera_startup, - .set_fmt = &madera_set_fmt, -@@ -3352,6 +3349,8 @@ const struct snd_soc_dai_ops madera_dai_ops = { - .hw_params = &madera_hw_params, - .set_sysclk = &madera_dai_set_sysclk, - .set_tristate = &madera_set_tristate, -+ .auto_selectable_formats = &madera_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - EXPORT_SYMBOL_GPL(madera_dai_ops); - -diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c ---- a/sound/soc/codecs/max98090.c -+++ b/sound/soc/codecs/max98090.c -@@ -2341,6 +2341,7 @@ static irqreturn_t max98090_interrupt(int irq, void *data) - * - * @component: MAX98090 component - * @jack: jack to report detection events on -+ * @data: can be used if codec driver need extra data for configuring jack - * - * Enable microphone detection via IRQ on the MAX98090. If GPIOs are - * being used to bring out signals to the processor then only platform -@@ -2646,12 +2647,18 @@ static void max98090_i2c_remove(struct i2c_client *client) - static int max98090_runtime_resume(struct device *dev) - { - struct max98090_priv *max98090 = dev_get_drvdata(dev); -+ int ret; - - regcache_cache_only(max98090->regmap, false); - - max98090_reset(max98090); - -- regcache_sync(max98090->regmap); -+ ret = regcache_sync(max98090->regmap); -+ if (ret < 0) { -+ regcache_cache_only(max98090->regmap, true); -+ regcache_mark_dirty(max98090->regmap); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c ---- a/sound/soc/codecs/max98095.c -+++ b/sound/soc/codecs/max98095.c -@@ -5,6 +5,7 @@ - * Copyright 2011 Maxim Integrated Products - */ - -+#include - #include - #include - #include -@@ -1532,15 +1533,17 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol, - regsave = snd_soc_component_read(component, M98095_088_CFG_LEVEL); - snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, 0); - -- mutex_lock(&max98095->lock); -- snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG); -- m98095_eq_band(component, channel, 0, coef_set->band1); -- m98095_eq_band(component, channel, 1, coef_set->band2); -- m98095_eq_band(component, channel, 2, coef_set->band3); -- m98095_eq_band(component, channel, 3, coef_set->band4); -- m98095_eq_band(component, channel, 4, coef_set->band5); -- snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, 0); -- mutex_unlock(&max98095->lock); -+ scoped_guard(mutex, &max98095->lock) { -+ snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, -+ M98095_SEG, M98095_SEG); -+ m98095_eq_band(component, channel, 0, coef_set->band1); -+ m98095_eq_band(component, channel, 1, coef_set->band2); -+ m98095_eq_band(component, channel, 2, coef_set->band3); -+ m98095_eq_band(component, channel, 3, coef_set->band4); -+ m98095_eq_band(component, channel, 4, coef_set->band5); -+ snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, -+ M98095_SEG, 0); -+ } - - /* Restore the original on/off state */ - snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, regsave); -@@ -1683,12 +1686,14 @@ static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol, - regsave = snd_soc_component_read(component, M98095_088_CFG_LEVEL); - snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, 0); - -- mutex_lock(&max98095->lock); -- snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG); -- m98095_biquad_band(component, channel, 0, coef_set->band1); -- m98095_biquad_band(component, channel, 1, coef_set->band2); -- snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, 0); -- mutex_unlock(&max98095->lock); -+ scoped_guard(mutex, &max98095->lock) { -+ snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, -+ M98095_SEG, M98095_SEG); -+ m98095_biquad_band(component, channel, 0, coef_set->band1); -+ m98095_biquad_band(component, channel, 1, coef_set->band2); -+ snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, -+ M98095_SEG, 0); -+ } - - /* Restore the original on/off state */ - snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, regsave); -diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c ---- a/sound/soc/codecs/max98363.c -+++ b/sound/soc/codecs/max98363.c -@@ -100,7 +100,12 @@ static int max98363_resume(struct device *dev) - return ret; - - regcache_cache_only(max98363->regmap, false); -- regcache_sync(max98363->regmap); -+ ret = regcache_sync(max98363->regmap); -+ if (ret) { -+ regcache_cache_only(max98363->regmap, true); -+ regcache_mark_dirty(max98363->regmap); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/max98373-i2c.c b/sound/soc/codecs/max98373-i2c.c ---- a/sound/soc/codecs/max98373-i2c.c -+++ b/sound/soc/codecs/max98373-i2c.c -@@ -488,10 +488,17 @@ static int max98373_suspend(struct device *dev) - static int max98373_resume(struct device *dev) - { - struct max98373_priv *max98373 = dev_get_drvdata(dev); -+ int ret; - - regcache_cache_only(max98373->regmap, false); - max98373_reset(max98373, dev); -- regcache_sync(max98373->regmap); -+ ret = regcache_sync(max98373->regmap); -+ if (ret) { -+ regcache_cache_only(max98373->regmap, true); -+ regcache_mark_dirty(max98373->regmap); -+ return ret; -+ } -+ - return 0; - } - -diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c ---- a/sound/soc/codecs/max98373-sdw.c -+++ b/sound/soc/codecs/max98373-sdw.c -@@ -277,7 +277,12 @@ static int max98373_resume(struct device *dev) - } - - regcache_cache_only(max98373->regmap, false); -- regcache_sync(max98373->regmap); -+ ret = regcache_sync(max98373->regmap); -+ if (ret) { -+ regcache_cache_only(max98373->regmap, true); -+ regcache_mark_dirty(max98373->regmap); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/max98388.c b/sound/soc/codecs/max98388.c ---- a/sound/soc/codecs/max98388.c -+++ b/sound/soc/codecs/max98388.c -@@ -863,10 +863,16 @@ static int max98388_suspend(struct device *dev) - static int max98388_resume(struct device *dev) - { - struct max98388_priv *max98388 = dev_get_drvdata(dev); -+ int ret; - - regcache_cache_only(max98388->regmap, false); - max98388_reset(max98388, dev); -- regcache_sync(max98388->regmap); -+ ret = regcache_sync(max98388->regmap); -+ if (ret) { -+ regcache_cache_only(max98388->regmap, true); -+ regcache_mark_dirty(max98388->regmap); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c ---- a/sound/soc/codecs/max98390.c -+++ b/sound/soc/codecs/max98390.c -@@ -788,7 +788,6 @@ static int max98390_dsm_init(struct snd_soc_component *component) - const char *vendor, *product; - struct max98390_priv *max98390 = - snd_soc_component_get_drvdata(component); -- const struct firmware *fw; - char *dsm_param; - - vendor = dmi_get_system_info(DMI_SYS_VENDOR); -@@ -805,6 +804,8 @@ static int max98390_dsm_init(struct snd_soc_component *component) - snprintf(filename, sizeof(filename), "%s", - max98390->dsm_param_name); - } -+ -+ const struct firmware *fw __free(firmware) = NULL; - ret = request_firmware(&fw, filename, component->dev); - if (ret) { - ret = request_firmware(&fw, "dsm_param.bin", component->dev); -@@ -812,7 +813,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) - ret = request_firmware(&fw, "dsmparam.bin", - component->dev); - if (ret) -- goto err; -+ return ret; - } - } - -@@ -822,8 +823,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) - if (fw->size < MAX98390_DSM_PARAM_MIN_SIZE) { - dev_err(component->dev, - "param fw is invalid.\n"); -- ret = -EINVAL; -- goto err_alloc; -+ return -EINVAL; - } - dsm_param = (char *)fw->data; - param_start_addr = (dsm_param[0] & 0xff) | (dsm_param[1] & 0xff) << 8; -@@ -833,8 +833,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) - fw->size < param_size + MAX98390_DSM_PAYLOAD_OFFSET) { - dev_err(component->dev, - "param fw is invalid.\n"); -- ret = -EINVAL; -- goto err_alloc; -+ return -EINVAL; - } - regmap_write(max98390->regmap, MAX98390_R203A_AMP_EN, 0x80); - dsm_param += MAX98390_DSM_PAYLOAD_OFFSET; -@@ -842,10 +841,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) - dsm_param, param_size); - regmap_write(max98390->regmap, MAX98390_R23E1_DSP_GLOBAL_EN, 0x01); - --err_alloc: -- release_firmware(fw); --err: -- return ret; -+ return 0; - } - - static void max98390_init_regs(struct snd_soc_component *component) -@@ -952,11 +948,17 @@ static int max98390_suspend(struct device *dev) - static int max98390_resume(struct device *dev) - { - struct max98390_priv *max98390 = dev_get_drvdata(dev); -+ int ret; - - dev_dbg(dev, "%s:Enter\n", __func__); - - regcache_cache_only(max98390->regmap, false); -- regcache_sync(max98390->regmap); -+ ret = regcache_sync(max98390->regmap); -+ if (ret) { -+ regcache_cache_only(max98390->regmap, true); -+ regcache_mark_dirty(max98390->regmap); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c ---- a/sound/soc/codecs/max98396.c -+++ b/sound/soc/codecs/max98396.c -@@ -1600,19 +1600,37 @@ static int max98396_resume(struct device *dev) - if (max98396->pvdd) { - ret = regulator_enable(max98396->pvdd); - if (ret < 0) -- return ret; -+ goto err_core_supplies; - } - - if (max98396->vbat) { - ret = regulator_enable(max98396->vbat); - if (ret < 0) -- return ret; -+ goto err_pvdd; - } - - regcache_cache_only(max98396->regmap, false); - max98396_reset(max98396, dev); -- regcache_sync(max98396->regmap); -+ ret = regcache_sync(max98396->regmap); -+ if (ret < 0) { -+ regcache_cache_only(max98396->regmap, true); -+ regcache_mark_dirty(max98396->regmap); -+ goto err_vbat; -+ } -+ - return 0; -+ -+err_vbat: -+ if (max98396->vbat) -+ regulator_disable(max98396->vbat); -+err_pvdd: -+ if (max98396->pvdd) -+ regulator_disable(max98396->pvdd); -+err_core_supplies: -+ regulator_bulk_disable(MAX98396_NUM_CORE_SUPPLIES, -+ max98396->core_supplies); -+ -+ return ret; - } - - static const struct dev_pm_ops max98396_pm = { -diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c ---- a/sound/soc/codecs/max98927.c -+++ b/sound/soc/codecs/max98927.c -@@ -742,11 +742,17 @@ static int max98927_suspend(struct device *dev) - static int max98927_resume(struct device *dev) - { - struct max98927_priv *max98927 = dev_get_drvdata(dev); -+ int ret; - - regmap_write(max98927->regmap, MAX98927_R0100_SOFT_RESET, - MAX98927_SOFT_RESET); - regcache_cache_only(max98927->regmap, false); -- regcache_sync(max98927->regmap); -+ ret = regcache_sync(max98927->regmap); -+ if (ret) { -+ regcache_cache_only(max98927->regmap, true); -+ regcache_mark_dirty(max98927->regmap); -+ return ret; -+ } - return 0; - } - -diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c ---- a/sound/soc/codecs/msm8916-wcd-analog.c -+++ b/sound/soc/codecs/msm8916-wcd-analog.c -@@ -217,9 +217,11 @@ - #define CDC_A_RX_HPH_BIAS_LDO_OCP (0xf195) - #define CDC_A_RX_HPH_BIAS_CNP (0xf196) - #define CDC_A_RX_HPH_CNP_EN (0xf197) -+#define CDC_A_RX_HPH_L_TEST (0xf19A) - #define CDC_A_RX_HPH_L_PA_DAC_CTL (0xf19B) - #define RX_HPA_L_PA_DAC_CTL_DATA_RESET_MASK BIT(1) - #define RX_HPA_L_PA_DAC_CTL_DATA_RESET_RESET BIT(1) -+#define CDC_A_RX_HPH_R_TEST (0xf19C) - #define CDC_A_RX_HPH_R_PA_DAC_CTL (0xf19D) - #define RX_HPH_R_PA_DAC_CTL_DATA_RESET BIT(1) - #define RX_HPH_R_PA_DAC_CTL_DATA_RESET_MASK BIT(1) -@@ -232,6 +234,8 @@ - #define RX_EAR_CTL_PA_SEL_MASK BIT(7) - #define RX_EAR_CTL_PA_SEL BIT(7) - -+#define CDC_A_RX_EAR_STATUS (0xf1A1) -+ - #define CDC_A_SPKR_DAC_CTL (0xf1B0) - #define SPKR_DAC_CTL_DAC_RESET_MASK BIT(4) - #define SPKR_DAC_CTL_DAC_RESET_NORMAL 0 -@@ -250,6 +254,7 @@ - SPKR_DRV_CAL_EN | SPKR_DRV_SETTLE_EN | \ - SPKR_DRV_FW_EN | SPKR_DRV_BOOST_SET | \ - SPKR_DRV_CMFB_SET | SPKR_DRV_GAIN_SET) -+#define CDC_A_SPKR_ANA_BIAS_SET (0xf1B3) - #define CDC_A_SPKR_OCP_CTL (0xf1B4) - #define CDC_A_SPKR_PWRSTG_CTL (0xf1B5) - #define SPKR_PWRSTG_CTL_DAC_EN_MASK BIT(0) -@@ -264,6 +269,7 @@ - - #define CDC_A_SPKR_DRV_DBG (0xf1B7) - #define CDC_A_CURRENT_LIMIT (0xf1C0) -+#define CDC_A_BYPASS_MODE (0xf1C2) - #define CDC_A_BOOST_EN_CTL (0xf1C3) - #define CDC_A_SLOPE_COMP_IP_ZERO (0xf1C4) - #define CDC_A_SEC_ACCESS (0xf1D0) -@@ -286,6 +292,11 @@ static const char * const supply_names[] = { - - #define MBHC_MAX_BUTTONS (5) - -+struct wcd_reg_seq { -+ const struct reg_default *seq; -+ int seq_size; -+}; -+ - struct pm8916_wcd_analog_priv { - u16 pmic_rev; - u16 codec_version; -@@ -696,6 +707,59 @@ static int pm8916_wcd_analog_enable_ear_pa(struct snd_soc_dapm_widget *w, - return 0; - } - -+static int pm8916_wcd_analog_enable_hphl_pa(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *kcontrol, -+ int event) -+{ -+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); -+ struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev); -+ -+ /* This quirk is not required for revisions prior to CAJON_2_0 */ -+ if (priv->codec_version < 4) -+ return 0; -+ -+ switch (event) { -+ case SND_SOC_DAPM_POST_PMU: -+ usleep_range(7000, 7100); -+ snd_soc_component_update_bits(component, CDC_A_RX_HPH_L_TEST, -+ 0x04, 0x04); -+ break; -+ case SND_SOC_DAPM_POST_PMD: -+ /* wait 20 ms after the digital codec has powered down */ -+ msleep(20); -+ snd_soc_component_update_bits(component, CDC_A_RX_HPH_L_TEST, -+ 0x04, 0x00); -+ break; -+ } -+ return 0; -+} -+ -+static int pm8916_wcd_analog_enable_hphr_pa(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *kcontrol, -+ int event) -+{ -+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); -+ struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev); -+ -+ /* This quirk is not required for revisions prior to CAJON_2_0 */ -+ if (priv->codec_version < 4) -+ return 0; -+ -+ switch (event) { -+ case SND_SOC_DAPM_POST_PMU: -+ usleep_range(7000, 7100); -+ snd_soc_component_update_bits(component, CDC_A_RX_HPH_R_TEST, -+ 0x04, 0x04); -+ break; -+ case SND_SOC_DAPM_POST_PMD: -+ msleep(20); -+ snd_soc_component_update_bits(component, CDC_A_RX_HPH_R_TEST, -+ 0x04, 0x00); -+ break; -+ } -+ return 0; -+} -+ - static const struct reg_default wcd_reg_defaults_2_0[] = { - {CDC_A_RX_COM_OCP_CTL, 0xD1}, - {CDC_A_RX_COM_OCP_COUNT, 0xFF}, -@@ -715,9 +779,69 @@ static const struct reg_default wcd_reg_defaults_2_0[] = { - {CDC_A_MASTER_BIAS_CTL, 0x30}, - }; - -+static const struct wcd_reg_seq pm8916_data = { -+ .seq = wcd_reg_defaults_2_0, -+ .seq_size = ARRAY_SIZE(wcd_reg_defaults_2_0), -+}; -+ -+static const struct reg_default wcd_reg_defaults_pm8950[] = { -+ {CDC_A_RX_COM_OCP_CTL, 0xd1}, -+ {CDC_A_RX_COM_OCP_COUNT, 0xff}, -+ {CDC_D_SEC_ACCESS, 0xa5}, -+ {CDC_D_PERPH_RESET_CTL3, 0x0f}, -+ {CDC_A_TX_1_2_OPAMP_BIAS, 0x4c}, -+ {CDC_A_NCP_FBCTRL, 0xa8}, -+ {CDC_A_NCP_VCTRL, 0xa4}, -+ {CDC_A_SPKR_DRV_CTL, 0x69}, -+ {CDC_A_SPKR_DRV_DBG, 0x01}, -+ {CDC_A_SEC_ACCESS, 0xa5}, -+ {CDC_A_PERPH_RESET_CTL3, 0x0f}, -+ {CDC_A_CURRENT_LIMIT, 0x82}, -+ {CDC_A_SPKR_ANA_BIAS_SET, 0x41}, -+ {CDC_A_SPKR_DAC_CTL, 0x03}, -+ {CDC_A_SPKR_OCP_CTL, 0xe1}, -+ {CDC_A_RX_HPH_BIAS_PA, 0xfa}, -+ {CDC_A_MASTER_BIAS_CTL, 0x30}, -+ {CDC_A_MICB_1_INT_RBIAS, 0x00}, -+}; -+ -+static const struct wcd_reg_seq pm8950_data = { -+ .seq = wcd_reg_defaults_pm8950, -+ .seq_size = ARRAY_SIZE(wcd_reg_defaults_pm8950), -+}; -+ -+static const struct reg_default wcd_reg_defaults_pm8953[] = { -+ {CDC_A_RX_COM_OCP_CTL, 0xd1}, -+ {CDC_A_RX_COM_OCP_COUNT, 0xff}, -+ {CDC_D_SEC_ACCESS, 0xa5}, -+ {CDC_D_PERPH_RESET_CTL3, 0x0f}, -+ {CDC_A_TX_1_2_OPAMP_BIAS, 0x4c}, -+ {CDC_A_NCP_FBCTRL, 0xa8}, -+ {CDC_A_NCP_VCTRL, 0xa4}, -+ {CDC_A_SPKR_DRV_CTL, 0x69}, -+ {CDC_A_SPKR_DRV_DBG, 0x01}, -+ {CDC_A_SEC_ACCESS, 0xa5}, -+ {CDC_A_PERPH_RESET_CTL3, 0x0f}, -+ {CDC_A_CURRENT_LIMIT, 0xa2}, -+ {CDC_A_BYPASS_MODE, 0x18}, -+ {CDC_A_SPKR_ANA_BIAS_SET, 0x41}, -+ {CDC_A_SPKR_DAC_CTL, 0x03}, -+ {CDC_A_SPKR_OCP_CTL, 0xe1}, -+ {CDC_A_RX_HPH_BIAS_PA, 0xfa}, -+ {CDC_A_RX_EAR_STATUS, 0x10}, -+ {CDC_A_MASTER_BIAS_CTL, 0x30}, -+ {CDC_A_MICB_1_INT_RBIAS, 0x00}, -+}; -+ -+static const struct wcd_reg_seq pm8953_data = { -+ .seq = wcd_reg_defaults_pm8953, -+ .seq_size = ARRAY_SIZE(wcd_reg_defaults_pm8953), -+}; -+ - static int pm8916_wcd_analog_probe(struct snd_soc_component *component) - { - struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev); -+ const struct wcd_reg_seq *wcd_reg_init_data; - int err, reg; - - err = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); -@@ -738,9 +862,11 @@ static int pm8916_wcd_analog_probe(struct snd_soc_component *component) - snd_soc_component_write(component, CDC_D_PERPH_RESET_CTL4, 0x01); - snd_soc_component_write(component, CDC_A_PERPH_RESET_CTL4, 0x01); - -- for (reg = 0; reg < ARRAY_SIZE(wcd_reg_defaults_2_0); reg++) -- snd_soc_component_write(component, wcd_reg_defaults_2_0[reg].reg, -- wcd_reg_defaults_2_0[reg].def); -+ wcd_reg_init_data = of_device_get_match_data(component->dev); -+ -+ for (reg = 0; reg < wcd_reg_init_data->seq_size; reg++) -+ snd_soc_component_write(component, wcd_reg_init_data->seq[reg].reg, -+ wcd_reg_init_data->seq[reg].def); - - priv->component = component; - -@@ -883,11 +1009,15 @@ static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = { - SND_SOC_DAPM_MUX("EAR_S", SND_SOC_NOPM, 0, 0, &ear_mux), - SND_SOC_DAPM_SUPPLY("EAR CP", CDC_A_NCP_EN, 4, 0, NULL, 0), - -- SND_SOC_DAPM_PGA("HPHL PA", CDC_A_RX_HPH_CNP_EN, 5, 0, NULL, 0), -+ SND_SOC_DAPM_PGA_E("HPHL PA", CDC_A_RX_HPH_CNP_EN, 5, 0, NULL, 0, -+ pm8916_wcd_analog_enable_hphl_pa, -+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MUX("HPHL", SND_SOC_NOPM, 0, 0, &hphl_mux), - SND_SOC_DAPM_MIXER("HPHL DAC", CDC_A_RX_HPH_L_PA_DAC_CTL, 3, 0, NULL, - 0), -- SND_SOC_DAPM_PGA("HPHR PA", CDC_A_RX_HPH_CNP_EN, 4, 0, NULL, 0), -+ SND_SOC_DAPM_PGA_E("HPHR PA", CDC_A_RX_HPH_CNP_EN, 4, 0, NULL, 0, -+ pm8916_wcd_analog_enable_hphr_pa, -+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MUX("HPHR", SND_SOC_NOPM, 0, 0, &hphr_mux), - SND_SOC_DAPM_MIXER("HPHR DAC", CDC_A_RX_HPH_R_PA_DAC_CTL, 3, 0, NULL, - 0), -@@ -1259,7 +1389,9 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev) - } - - static const struct of_device_id pm8916_wcd_analog_spmi_match_table[] = { -- { .compatible = "qcom,pm8916-wcd-analog-codec", }, -+ { .compatible = "qcom,pm8916-wcd-analog-codec", .data = &pm8916_data }, -+ { .compatible = "qcom,pm8950-wcd-analog-codec", .data = &pm8950_data }, -+ { .compatible = "qcom,pm8953-wcd-analog-codec", .data = &pm8953_data }, - { } - }; - -diff --git a/sound/soc/codecs/mt6357.c b/sound/soc/codecs/mt6357.c ---- a/sound/soc/codecs/mt6357.c -+++ b/sound/soc/codecs/mt6357.c -@@ -1834,8 +1834,8 @@ static int mt6357_platform_driver_probe(struct platform_device *pdev) - } - - static const struct platform_device_id mt6357_platform_ids[] = { -- {"mt6357-sound", 0}, -- { /* sentinel */ }, -+ { .name = "mt6357-sound" }, -+ { /* sentinel */ } - }; - MODULE_DEVICE_TABLE(platform, mt6357_platform_ids); - -diff --git a/sound/soc/codecs/mt6359-accdet.c b/sound/soc/codecs/mt6359-accdet.c ---- a/sound/soc/codecs/mt6359-accdet.c -+++ b/sound/soc/codecs/mt6359-accdet.c -@@ -6,6 +6,7 @@ - // Author: Argus Lin - // - -+#include - #include - #include - #include -@@ -398,14 +399,13 @@ static void mt6359_accdet_work(struct work_struct *work) - struct mt6359_accdet *priv = - container_of(work, struct mt6359_accdet, accdet_work); - -- mutex_lock(&priv->res_lock); -+ guard(mutex)(&priv->res_lock); - priv->pre_accdet_status = priv->accdet_status; - check_jack_btn_type(priv); - - if (priv->jack_plugged && - priv->pre_accdet_status != priv->accdet_status) - mt6359_accdet_jack_report(priv); -- mutex_unlock(&priv->res_lock); - } - - static void mt6359_accdet_jd_work(struct work_struct *work) -@@ -416,7 +416,7 @@ static void mt6359_accdet_jd_work(struct work_struct *work) - struct mt6359_accdet *priv = - container_of(work, struct mt6359_accdet, jd_work); - -- mutex_lock(&priv->res_lock); -+ guard(mutex)(&priv->res_lock); - if (priv->jd_sts == M_PLUG_IN) { - priv->jack_plugged = true; - -@@ -450,7 +450,6 @@ static void mt6359_accdet_jd_work(struct work_struct *work) - - if (priv->caps & ACCDET_PMIC_EINT_IRQ) - recover_eint_setting(priv); -- mutex_unlock(&priv->res_lock); - } - - static irqreturn_t mt6359_accdet_irq(int irq, void *data) -@@ -459,7 +458,7 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) - unsigned int irq_val = 0, val = 0, value = 0; - int ret; - -- mutex_lock(&priv->res_lock); -+ guard(mutex)(&priv->res_lock); - regmap_read(priv->regmap, ACCDET_IRQ_ADDR, &irq_val); - - if (irq_val & ACCDET_IRQ_MASK_SFT) { -@@ -474,7 +473,6 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) - 1000); - if (ret) { - dev_err(priv->dev, "%s(), ret %d\n", __func__, ret); -- mutex_unlock(&priv->res_lock); - return IRQ_NONE; - } - regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, -@@ -498,7 +496,6 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) - if (ret) { - dev_err(priv->dev, "%s(), ret %d\n", __func__, - ret); -- mutex_unlock(&priv->res_lock); - return IRQ_NONE; - } - regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, -@@ -521,7 +518,6 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) - if (ret) { - dev_err(priv->dev, "%s(), ret %d\n", __func__, - ret); -- mutex_unlock(&priv->res_lock); - return IRQ_NONE; - } - regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, -@@ -540,7 +536,6 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) - - queue_work(priv->jd_workqueue, &priv->jd_work); - } -- mutex_unlock(&priv->res_lock); - - return IRQ_HANDLED; - } -diff --git a/sound/soc/codecs/ntpfw.c b/sound/soc/codecs/ntpfw.c ---- a/sound/soc/codecs/ntpfw.c -+++ b/sound/soc/codecs/ntpfw.c -@@ -89,7 +89,7 @@ int ntpfw_load(struct i2c_client *i2c, const char *name, u32 magic) - { - struct device *dev = &i2c->dev; - const struct ntpfw_chunk *chunk; -- const struct firmware *fw; -+ const struct firmware *fw __free(firmware) = NULL; - const u8 *data; - size_t leftover; - int ret; -@@ -101,10 +101,8 @@ int ntpfw_load(struct i2c_client *i2c, const char *name, u32 magic) - return ret; - } - -- if (!ntpfw_verify(dev, fw->data, fw->size, magic)) { -- ret = -EINVAL; -- goto done; -- } -+ if (!ntpfw_verify(dev, fw->data, fw->size, magic)) -+ return -EINVAL; - - data = fw->data + sizeof(struct ntpfw_header); - leftover = fw->size - sizeof(struct ntpfw_header); -@@ -112,23 +110,18 @@ int ntpfw_load(struct i2c_client *i2c, const char *name, u32 magic) - while (leftover) { - chunk = (struct ntpfw_chunk *)data; - -- if (!ntpfw_verify_chunk(dev, chunk, leftover)) { -- ret = -EINVAL; -- goto done; -- } -+ if (!ntpfw_verify_chunk(dev, chunk, leftover)) -+ return -EINVAL; - - ret = ntpfw_send_chunk(i2c, chunk); - if (ret) -- goto done; -+ return ret; - - data += be16_to_cpu(chunk->length) + sizeof(*chunk); - leftover -= be16_to_cpu(chunk->length) + sizeof(*chunk); - } - --done: -- release_firmware(fw); -- -- return ret; -+ return 0; - } - EXPORT_SYMBOL_GPL(ntpfw_load); - -diff --git a/sound/soc/codecs/ntpfw.h b/sound/soc/codecs/ntpfw.h ---- a/sound/soc/codecs/ntpfw.h -+++ b/sound/soc/codecs/ntpfw.h -@@ -1,5 +1,5 @@ - /* SPDX-License-Identifier: GPL-2.0-only */ --/** -+/* - * ntpfw.h - Firmware helper functions for Neofidelity codecs - * - * Copyright (c) 2024, SaluteDevices. All Rights Reserved. -@@ -13,10 +13,11 @@ - /** - * ntpfw_load - load firmware to amplifier over i2c interface. - * -- * @i2c Pointer to amplifier's I2C client. -- * @name Firmware file name. -- * @magic Magic number to validate firmware. -- * @return 0 or error code upon error. -+ * @i2c: Pointer to amplifier's I2C client. -+ * @name: Firmware file name. -+ * @magic: Magic number to validate firmware. -+ * -+ * Returns: 0 or error code upon error. - */ - int ntpfw_load(struct i2c_client *i2c, const char *name, const u32 magic); - -diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c ---- a/sound/soc/codecs/pcm1681.c -+++ b/sound/soc/codecs/pcm1681.c -@@ -199,10 +199,17 @@ static int pcm1681_hw_params(struct snd_pcm_substream *substream, - return pcm1681_set_deemph(component); - } - -+static const u64 pcm1681_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J; -+ - static const struct snd_soc_dai_ops pcm1681_dai_ops = { - .set_fmt = pcm1681_set_dai_fmt, - .hw_params = pcm1681_hw_params, - .mute_stream = pcm1681_mute, -+ .auto_selectable_formats = &pcm1681_selectable_formats, -+ .num_auto_selectable_formats = 1, - .no_capture_mute = 1, - }; - -diff --git a/sound/soc/codecs/pcm1754.c b/sound/soc/codecs/pcm1754.c ---- a/sound/soc/codecs/pcm1754.c -+++ b/sound/soc/codecs/pcm1754.c -@@ -78,10 +78,16 @@ static int pcm1754_mute_stream(struct snd_soc_dai *dai, int mute, int stream) - return 0; - } - -+static const u64 pcm1754_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J; -+ - static const struct snd_soc_dai_ops pcm1754_dai_ops = { - .set_fmt = pcm1754_set_dai_fmt, - .hw_params = pcm1754_hw_params, - .mute_stream = pcm1754_mute_stream, -+ .auto_selectable_formats = &pcm1754_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static const struct snd_soc_dai_driver pcm1754_dai = { -diff --git a/sound/soc/codecs/pcm1789.c b/sound/soc/codecs/pcm1789.c ---- a/sound/soc/codecs/pcm1789.c -+++ b/sound/soc/codecs/pcm1789.c -@@ -164,11 +164,18 @@ static int pcm1789_trigger(struct snd_pcm_substream *substream, int cmd, - return ret; - } - -+static const u64 pcm1789_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J; -+ - static const struct snd_soc_dai_ops pcm1789_dai_ops = { - .set_fmt = pcm1789_set_dai_fmt, - .hw_params = pcm1789_hw_params, - .mute_stream = pcm1789_mute, - .trigger = pcm1789_trigger, -+ .auto_selectable_formats = &pcm1789_selectable_formats, -+ .num_auto_selectable_formats = 1, - .no_capture_mute = 1, - }; - -diff --git a/sound/soc/codecs/pcm179x.c b/sound/soc/codecs/pcm179x.c ---- a/sound/soc/codecs/pcm179x.c -+++ b/sound/soc/codecs/pcm179x.c -@@ -142,10 +142,16 @@ static int pcm179x_hw_params(struct snd_pcm_substream *substream, - return 0; - } - -+static const u64 pcm179x_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J; -+ - static const struct snd_soc_dai_ops pcm179x_dai_ops = { - .set_fmt = pcm179x_set_dai_fmt, - .hw_params = pcm179x_hw_params, - .mute_stream = pcm179x_mute, -+ .auto_selectable_formats = &pcm179x_selectable_formats, -+ .num_auto_selectable_formats = 1, - .no_capture_mute = 1, - }; - -diff --git a/sound/soc/codecs/pcm186x.c b/sound/soc/codecs/pcm186x.c ---- a/sound/soc/codecs/pcm186x.c -+++ b/sound/soc/codecs/pcm186x.c -@@ -473,11 +473,20 @@ static int pcm186x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, - return 0; - } - -+static const u64 pcm186x_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_B | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF; -+ - static const struct snd_soc_dai_ops pcm186x_dai_ops = { - .set_sysclk = pcm186x_set_dai_sysclk, - .set_tdm_slot = pcm186x_set_tdm_slot, - .set_fmt = pcm186x_set_fmt, - .hw_params = pcm186x_hw_params, -+ .auto_selectable_formats = &pcm186x_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static struct snd_soc_dai_driver pcm1863_dai = { -diff --git a/sound/soc/codecs/pcm3060.c b/sound/soc/codecs/pcm3060.c ---- a/sound/soc/codecs/pcm3060.c -+++ b/sound/soc/codecs/pcm3060.c -@@ -164,10 +164,18 @@ static int pcm3060_hw_params(struct snd_pcm_substream *substream, - return 0; - } - -+static const u64 pcm3060_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF; -+ - static const struct snd_soc_dai_ops pcm3060_dai_ops = { - .set_sysclk = pcm3060_set_sysclk, - .set_fmt = pcm3060_set_fmt, - .hw_params = pcm3060_hw_params, -+ .auto_selectable_formats = &pcm3060_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - #define PCM3060_DAI_RATES_ADC (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | \ -diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c ---- a/sound/soc/codecs/pcm512x.c -+++ b/sound/soc/codecs/pcm512x.c -@@ -6,7 +6,7 @@ - * Copyright 2014 Linaro Ltd - */ - -- -+#include - #include - #include - #include -@@ -399,10 +399,9 @@ static int pcm512x_digital_playback_switch_get(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); - -- mutex_lock(&pcm512x->mutex); -+ guard(mutex)(&pcm512x->mutex); - ucontrol->value.integer.value[0] = !(pcm512x->mute & 0x4); - ucontrol->value.integer.value[1] = !(pcm512x->mute & 0x2); -- mutex_unlock(&pcm512x->mutex); - - return 0; - } -@@ -414,7 +413,7 @@ static int pcm512x_digital_playback_switch_put(struct snd_kcontrol *kcontrol, - struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); - int ret, changed = 0; - -- mutex_lock(&pcm512x->mutex); -+ guard(mutex)(&pcm512x->mutex); - - if ((pcm512x->mute & 0x4) == (ucontrol->value.integer.value[0] << 2)) { - pcm512x->mute ^= 0x4; -@@ -430,13 +429,10 @@ static int pcm512x_digital_playback_switch_put(struct snd_kcontrol *kcontrol, - if (ret != 0) { - dev_err(component->dev, - "Failed to update digital mute: %d\n", ret); -- mutex_unlock(&pcm512x->mutex); - return ret; - } - } - -- mutex_unlock(&pcm512x->mutex); -- - return changed; - } - -@@ -1465,7 +1461,7 @@ static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) - int ret; - unsigned int mute_det; - -- mutex_lock(&pcm512x->mutex); -+ guard(mutex)(&pcm512x->mutex); - - if (mute) { - pcm512x->mute |= 0x1; -@@ -1475,7 +1471,7 @@ static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) - if (ret != 0) { - dev_err(component->dev, - "Failed to set digital mute: %d\n", ret); -- goto unlock; -+ return ret; - } - - regmap_read_poll_timeout(pcm512x->regmap, -@@ -1488,7 +1484,7 @@ static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) - if (ret != 0) { - dev_err(component->dev, - "Failed to update digital mute: %d\n", ret); -- goto unlock; -+ return ret; - } - - regmap_read_poll_timeout(pcm512x->regmap, -@@ -1499,18 +1495,24 @@ static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) - 200, 10000); - } - --unlock: -- mutex_unlock(&pcm512x->mutex); -- - return ret; - } - -+static const u64 pcm512x_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_B; -+ - static const struct snd_soc_dai_ops pcm512x_dai_ops = { - .startup = pcm512x_dai_startup, - .hw_params = pcm512x_hw_params, - .set_fmt = pcm512x_set_fmt, - .mute_stream = pcm512x_mute, - .set_bclk_ratio = pcm512x_set_bclk_ratio, -+ .auto_selectable_formats = &pcm512x_selectable_formats, -+ .num_auto_selectable_formats = 1, - .no_capture_mute = 1, - }; - -diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c ---- a/sound/soc/codecs/pcm6240.c -+++ b/sound/soc/codecs/pcm6240.c -@@ -12,6 +12,7 @@ - // Author: Shenghao Ding - // - -+#include - #include - #include - #include -@@ -605,7 +606,7 @@ static int pcmdev_get_volsw(struct snd_kcontrol *kcontrol, - unsigned int reg = mc->reg; - unsigned int val; - -- mutex_lock(&pcm_dev->codec_lock); -+ guard(mutex)(&pcm_dev->codec_lock); - - if (pcm_dev->chip_id == PCM1690) { - ret = pcmdev_dev_read(pcm_dev, dev_no, PCM1690_REG_MODE_CTRL, -@@ -613,18 +614,18 @@ static int pcmdev_get_volsw(struct snd_kcontrol *kcontrol, - if (ret) { - dev_err(pcm_dev->dev, "%s: read mode err=%d\n", - __func__, ret); -- goto out; -+ return ret; - } - val &= PCM1690_REG_MODE_CTRL_DAMS_MSK; - /* Set to wide-range mode, before using vol ctrl. */ - if (!val && vol_ctrl_type == PCMDEV_PCM1690_VOL_CTRL) { - ucontrol->value.integer.value[0] = -25500; -- goto out; -+ return ret; - } - /* Set to fine mode, before using fine vol ctrl. */ - if (val && vol_ctrl_type == PCMDEV_PCM1690_FINE_VOL_CTRL) { - ucontrol->value.integer.value[0] = -12750; -- goto out; -+ return ret; - } - } - -@@ -632,15 +633,14 @@ static int pcmdev_get_volsw(struct snd_kcontrol *kcontrol, - if (ret) { - dev_err(pcm_dev->dev, "%s: read err=%d\n", - __func__, ret); -- goto out; -+ return ret; - } - - val = (val >> shift) & mask; - val = (val > max) ? max : val; - val = mc->invert ? max - val : val; - ucontrol->value.integer.value[0] = val; --out: -- mutex_unlock(&pcm_dev->codec_lock); -+ - return ret; - } - -@@ -678,7 +678,7 @@ static int pcmdev_put_volsw(struct snd_kcontrol *kcontrol, - unsigned int val, val_mask; - unsigned int reg = mc->reg; - -- mutex_lock(&pcm_dev->codec_lock); -+ guard(mutex)(&pcm_dev->codec_lock); - val = ucontrol->value.integer.value[0] & mask; - val = (val > max) ? max : val; - val = mc->invert ? max - val : val; -@@ -702,7 +702,7 @@ static int pcmdev_put_volsw(struct snd_kcontrol *kcontrol, - __func__, rc); - else - rc = 1; -- mutex_unlock(&pcm_dev->codec_lock); -+ - return rc; - } - -@@ -1577,10 +1577,10 @@ static int pcmdevice_comp_probe(struct snd_soc_component *comp) - { - struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(comp); - struct i2c_adapter *adap = pcm_dev->client->adapter; -- const struct firmware *fw_entry = NULL; -+ const struct firmware *fw_entry __free(firmware) = NULL; - int ret, i, j; - -- mutex_lock(&pcm_dev->codec_lock); -+ guard(mutex)(&pcm_dev->codec_lock); - - pcm_dev->component = comp; - -@@ -1588,7 +1588,7 @@ static int pcmdevice_comp_probe(struct snd_soc_component *comp) - for (j = 0; j < 2; j++) { - ret = pcmdev_gain_ctrl_add(pcm_dev, i, j); - if (ret < 0) -- goto out; -+ return ret; - } - } - -@@ -1621,21 +1621,17 @@ static int pcmdevice_comp_probe(struct snd_soc_component *comp) - if (ret) { - dev_err(pcm_dev->dev, "%s: request %s err = %d\n", __func__, - pcm_dev->bin_name, ret); -- goto out; -+ return ret; - } - - ret = pcmdev_regbin_ready(fw_entry, pcm_dev); - if (ret) { - dev_err(pcm_dev->dev, "%s: %s parse err = %d\n", __func__, - pcm_dev->bin_name, ret); -- goto out; -+ return ret; - } -- ret = pcmdev_profile_ctrl_add(pcm_dev); --out: -- release_firmware(fw_entry); - -- mutex_unlock(&pcm_dev->codec_lock); -- return ret; -+ return pcmdev_profile_ctrl_add(pcm_dev); - } - - -@@ -1645,9 +1641,8 @@ static void pcmdevice_comp_remove(struct snd_soc_component *codec) - - if (!pcm_dev) - return; -- mutex_lock(&pcm_dev->codec_lock); -+ guard(mutex)(&pcm_dev->codec_lock); - pcmdevice_config_info_remove(pcm_dev); -- mutex_unlock(&pcm_dev->codec_lock); - } - - static const struct snd_soc_dapm_widget pcmdevice_dapm_widgets[] = { -@@ -1890,9 +1885,9 @@ static int pcmdevice_mute(struct snd_soc_dai *dai, int mute, int stream) - else - block_type = PCMDEVICE_BIN_BLK_PRE_POWER_UP; - -- mutex_lock(&pcm_dev->codec_lock); -+ guard(mutex)(&pcm_dev->codec_lock); - pcmdevice_select_cfg_blk(pcm_dev, pcm_dev->cur_conf, block_type); -- mutex_unlock(&pcm_dev->codec_lock); -+ - return 0; - } - -diff --git a/sound/soc/codecs/peb2466.c b/sound/soc/codecs/peb2466.c ---- a/sound/soc/codecs/peb2466.c -+++ b/sound/soc/codecs/peb2466.c -@@ -6,6 +6,7 @@ - // - // Author: Herve Codina - -+#include - #include - #include - #include -@@ -1537,17 +1538,14 @@ static int peb2466_fw_parse(struct snd_soc_component *component, - - static int peb2466_load_coeffs(struct snd_soc_component *component, const char *fw_name) - { -- const struct firmware *fw; -+ const struct firmware *fw __free(firmware) = NULL; - int ret; - - ret = request_firmware(&fw, fw_name, component->dev); - if (ret) - return ret; - -- ret = peb2466_fw_parse(component, fw->data, fw->size); -- release_firmware(fw); -- -- return ret; -+ return peb2466_fw_parse(component, fw->data, fw->size); - } - - static int peb2466_component_probe(struct snd_soc_component *component) -@@ -1704,13 +1702,11 @@ static int peb2466_chip_gpio_update_bits(struct peb2466 *peb2466, unsigned int x - * So, a specific cache value is used. - */ - -- mutex_lock(&peb2466->gpio.lock); -+ guard(mutex)(&peb2466->gpio.lock); - - cache = peb2466_chip_gpio_get_cache(peb2466, xr_reg); -- if (!cache) { -- ret = -EINVAL; -- goto end; -- } -+ if (!cache) -+ return -EINVAL; - - tmp = *cache; - tmp &= ~mask; -@@ -1718,14 +1714,11 @@ static int peb2466_chip_gpio_update_bits(struct peb2466 *peb2466, unsigned int x - - ret = regmap_write(peb2466->regmap, xr_reg, tmp); - if (ret) -- goto end; -+ return ret; - - *cache = tmp; -- ret = 0; - --end: -- mutex_unlock(&peb2466->gpio.lock); -- return ret; -+ return 0; - } - - static int peb2466_chip_gpio_set(struct gpio_chip *c, unsigned int offset, -diff --git a/sound/soc/codecs/pm4125-sdw.c b/sound/soc/codecs/pm4125-sdw.c ---- a/sound/soc/codecs/pm4125-sdw.c -+++ b/sound/soc/codecs/pm4125-sdw.c -@@ -464,10 +464,16 @@ static int __maybe_unused pm4125_sdw_runtime_suspend(struct device *dev) - static int __maybe_unused pm4125_sdw_runtime_resume(struct device *dev) - { - struct pm4125_sdw_priv *priv = dev_get_drvdata(dev); -+ int ret; - - if (priv->regmap) { - regcache_cache_only(priv->regmap, false); -- regcache_sync(priv->regmap); -+ ret = regcache_sync(priv->regmap); -+ if (ret) { -+ regcache_cache_only(priv->regmap, true); -+ regcache_mark_dirty(priv->regmap); -+ return ret; -+ } - } - - return 0; -diff --git a/sound/soc/codecs/pm4125.c b/sound/soc/codecs/pm4125.c ---- a/sound/soc/codecs/pm4125.c -+++ b/sound/soc/codecs/pm4125.c -@@ -5,6 +5,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -1295,7 +1296,7 @@ static const struct irq_domain_ops pm4125_domain_ops = { - - static int pm4125_irq_init(struct pm4125_priv *pm4125, struct device *dev) - { -- pm4125->virq = irq_domain_add_linear(NULL, 1, &pm4125_domain_ops, NULL); -+ pm4125->virq = irq_domain_create_linear(NULL, 1, &pm4125_domain_ops, NULL); - if (!(pm4125->virq)) { - dev_err(dev, "%s: Failed to add IRQ domain\n", __func__); - return -EINVAL; -diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c ---- a/sound/soc/codecs/rt1017-sdca-sdw.c -+++ b/sound/soc/codecs/rt1017-sdca-sdw.c -@@ -784,7 +784,12 @@ static int rt1017_sdca_dev_resume(struct device *dev) - } - - regcache_cache_only(rt1017->regmap, false); -- regcache_sync(rt1017->regmap); -+ ret = regcache_sync(rt1017->regmap); -+ if (ret) { -+ regcache_cache_only(rt1017->regmap, true); -+ regcache_mark_dirty(rt1017->regmap); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c ---- a/sound/soc/codecs/rt1316-sdw.c -+++ b/sound/soc/codecs/rt1316-sdw.c -@@ -756,7 +756,12 @@ static int rt1316_dev_resume(struct device *dev) - } - - regcache_cache_only(rt1316->regmap, false); -- regcache_sync(rt1316->regmap); -+ ret = regcache_sync(rt1316->regmap); -+ if (ret) { -+ regcache_cache_only(rt1316->regmap, true); -+ regcache_mark_dirty(rt1316->regmap); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c ---- a/sound/soc/codecs/rt1318-sdw.c -+++ b/sound/soc/codecs/rt1318-sdw.c -@@ -830,7 +830,12 @@ static int rt1318_dev_resume(struct device *dev) - return ret; - - regcache_cache_only(rt1318->regmap, false); -- regcache_sync(rt1318->regmap); -+ ret = regcache_sync(rt1318->regmap); -+ if (ret) { -+ regcache_cache_only(rt1318->regmap, true); -+ regcache_mark_dirty(rt1318->regmap); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c ---- a/sound/soc/codecs/rt1320-sdw.c -+++ b/sound/soc/codecs/rt1320-sdw.c -@@ -410,6 +410,402 @@ static const struct reg_sequence rt1321_blind_write[] = { - { 0x41001988, 0x03 }, - }; - -+static const struct reg_sequence rt1321_va1_blind_write[] = { -+ { 0x0000c003, 0xf0 }, -+ { 0x0000c01b, 0xfc }, -+ { 0x0000c5c3, 0xf2 }, -+ { 0x0000c5c2, 0x00 }, -+ { 0x0000c5c1, 0x10 }, -+ { 0x0000c5c0, 0x04 }, -+ { 0x0000c5c7, 0x03 }, -+ { 0x0000c5c6, 0x10 }, -+ { 0x0000c526, 0x47 }, -+ { 0x0000c5c4, 0x12 }, -+ { 0x0000c5c5, 0x60 }, -+ { 0x0000c520, 0x10 }, -+ { 0x0000c521, 0x32 }, -+ { 0x0000c5c7, 0x00 }, -+ { 0x0000c5c8, 0x03 }, -+ { 0x0000c5d3, 0x08 }, -+ { 0x0000c5d2, 0x0a }, -+ { 0x0000c5d1, 0x49 }, -+ { 0x0000c5d0, 0x0f }, -+ { 0x0000c580, 0x10 }, -+ { 0x0000c581, 0x32 }, -+ { 0x0000c582, 0x01 }, -+ { 0x0000c682, 0x60 }, -+ { 0x0000c019, 0x10 }, -+ { 0x0000c5f0, 0x01 }, -+ { 0x0000c5f7, 0x22 }, -+ { 0x0000c5f6, 0x22 }, -+ { 0x0000c057, 0x51 }, -+ { 0x0000c054, 0x55 }, -+ { 0x0000c053, 0x55 }, -+ { 0x0000c052, 0x55 }, -+ { 0x0000c051, 0x01 }, -+ { 0x0000c050, 0x15 }, -+ { 0x0000c060, 0x99 }, -+ { 0x0000c030, 0x55 }, -+ { 0x0000c061, 0x55 }, -+ { 0x0000c063, 0x55 }, -+ { 0x0000c065, 0xa5 }, -+ { 0x0000c06b, 0x0a }, -+ { 0x0000ca05, 0xd6 }, -+ { 0x0000ca06, 0x11 }, -+ { 0x0000ca07, 0x1e }, -+ { 0x0000ca25, 0xd6 }, -+ { 0x0000ca26, 0x11 }, -+ { 0x0000ca27, 0x1e }, -+ { 0x0000cd00, 0x05 }, -+ { 0x0000cd81, 0x49 }, -+ { 0x0000cd82, 0x49 }, -+ { 0x0000c604, 0x40 }, -+ { 0x0000c609, 0x40 }, -+ { 0x0000c046, 0xf7 }, -+ { 0x0000c045, 0xff }, -+ { 0x0000c044, 0xff }, -+ { 0x0000c043, 0xff }, -+ { 0x0000c042, 0xff }, -+ { 0x0000c041, 0xff }, -+ { 0x0000c040, 0xff }, -+ { 0x0000c049, 0xff }, -+ { 0x0000c028, 0x3f }, -+ { 0x0000c020, 0x3f }, -+ { 0x0000c032, 0x13 }, -+ { 0x0000c033, 0x01 }, -+ { 0x0000cc10, 0x01 }, -+ { 0x0000dc20, 0x03 }, -+ { 0x0000de03, 0x05 }, -+ { 0x0000dc00, 0x00 }, -+ { 0x0000c700, 0xf0 }, -+ { 0x0000c701, 0x13 }, -+ { 0x0000c900, 0xc3 }, -+ { 0x0000c570, 0x08 }, -+ { 0x0000c086, 0x02 }, -+ { 0x0000c085, 0x7f }, -+ { 0x0000c084, 0x00 }, -+ { 0x0000c081, 0xff }, -+ { 0x0000f084, 0x0f }, -+ { 0x0000f083, 0xff }, -+ { 0x0000f082, 0xff }, -+ { 0x0000f081, 0xff }, -+ { 0x0000f080, 0xff }, -+ { 0x20003003, 0x3f }, -+ { 0x20005818, 0x81 }, -+ { 0x20009018, 0x81 }, -+ { 0x2000301c, 0x81 }, -+ { 0x0000c003, 0xc0 }, -+ { 0x0000c047, 0x80 }, -+ { 0x0000d541, 0x80 }, -+ { 0x0000d487, 0x0b }, -+ { 0x0000d487, 0x3b }, -+ { 0x0000d486, 0xc3 }, -+ { 0x0000d470, 0x89 }, -+ { 0x0000d471, 0x3a }, -+ { 0x0000d472, 0x3d }, -+ { 0x0000d474, 0x11 }, -+ { 0x0000d475, 0x32 }, -+ { 0x0000d476, 0x64 }, -+ { 0x0000d477, 0x10 }, -+ { 0x0000d478, 0xff }, -+ { 0x0000d479, 0x20 }, -+ { 0x0000d47a, 0x10 }, -+ { 0x0000d73c, 0xb7 }, -+ { 0x0000d73d, 0xd7 }, -+ { 0x0000d73e, 0x00 }, -+ { 0x0000d73f, 0x10 }, -+ { 0x1000cd56, 0x00 }, -+ { 0x3fc2dfc0, 0x03 }, -+ { 0x3fc2dfc1, 0x00 }, -+ { 0x3fc2dfc2, 0x00 }, -+ { 0x3fc2dfc3, 0x00 }, -+ { 0x3fc2dfc4, 0x01 }, -+ { 0x3fc2dfc5, 0x00 }, -+ { 0x3fc2dfc6, 0x00 }, -+ { 0x3fc2dfc7, 0x00 }, -+ { 0x3fc2df80, 0x00 }, -+ { 0x3fc2df81, 0x00 }, -+ { 0x3fc2df82, 0x00 }, -+ { 0x3fc2df83, 0x00 }, -+ { 0x0000d541, 0x40 }, -+ { 0x0000d486, 0x43 }, -+ { 0x1000db00, 0x04 }, -+ { 0x1000db01, 0x00 }, -+ { 0x1000db02, 0x10 }, -+ { 0x1000db03, 0x00 }, -+ { 0x1000db04, 0x00 }, -+ { 0x1000db05, 0x45 }, -+ { 0x1000db06, 0x0d }, -+ { 0x1000db07, 0x01 }, -+ { 0x1000db08, 0x00 }, -+ { 0x1000db09, 0x00 }, -+ { 0x1000db0a, 0xbf }, -+ { 0x1000db0b, 0x0b }, -+ { 0x1000db0c, 0x11 }, -+ { 0x1000db0d, 0x00 }, -+ { 0x1000db0e, 0x00 }, -+ { 0x1000db0f, 0x00 }, -+ { 0x1000db10, 0x2c }, -+ { 0x1000db11, 0xfa }, -+ { 0x1000db12, 0x00 }, -+ { 0x1000db13, 0x00 }, -+ { 0x1000db14, 0x09 }, -+ { 0x0000d540, 0x21 }, -+ { 0x0000c570, 0x08 }, -+ { 0x0000d714, 0x17 }, -+ { 0x0000c5c3, 0xf2 }, -+ { 0x0000c5c8, 0x03 }, -+ { 0x20009012, 0x00 }, -+ { 0x0000dd08, 0x17 }, -+ { 0x0000dd09, 0x0e }, -+ { 0x0000dd0a, 0x17 }, -+ { 0x0000dd0b, 0x0e }, -+ { 0x0000c570, 0x08 }, -+ { 0x0000d471, 0x3a }, -+ { 0x0000db00, 0x00 }, -+ { 0x0000db01, 0x00 }, -+ { 0x0000db02, 0x73 }, -+ { 0x0000db03, 0x00 }, -+ { 0x0000db04, 0x00 }, -+ { 0x0000db05, 0x00 }, -+ { 0x0000db06, 0x00 }, -+ { 0x0000db07, 0x00 }, -+ { 0x0000db08, 0x7f }, -+ { 0x0000db09, 0x00 }, -+ { 0x0000db1a, 0x00 }, -+ { 0x0000db1b, 0x00 }, -+ { 0x0000db19, 0x00 }, -+}; -+ -+static const struct reg_sequence rt1321_va2_blind_write[] = { -+ { 0x0000c003, 0xf0 }, -+ { 0x0000c01b, 0xfc }, -+ { 0x0000c5c3, 0xf2 }, -+ { 0x0000c5c2, 0x00 }, -+ { 0x0000c5c1, 0x10 }, -+ { 0x0000c5c0, 0x04 }, -+ { 0x0000c5c7, 0x03 }, -+ { 0x0000c5c6, 0x10 }, -+ { 0x0000c526, 0x47 }, -+ { 0x0000c5c4, 0x12 }, -+ { 0x0000c5c5, 0x60 }, -+ { 0x0000c520, 0x10 }, -+ { 0x0000c521, 0x32 }, -+ { 0x0000c5c7, 0x00 }, -+ { 0x0000c5c8, 0x03 }, -+ { 0x0000c5d3, 0x08 }, -+ { 0x0000c5d2, 0x0a }, -+ { 0x0000c5d1, 0x49 }, -+ { 0x0000c5d0, 0x0f }, -+ { 0x0000c580, 0x10 }, -+ { 0x0000c581, 0x32 }, -+ { 0x0000c582, 0x01 }, -+ { 0x0000c682, 0x60 }, -+ { 0x0000c019, 0x10 }, -+ { 0x0000c5f0, 0x01 }, -+ { 0x0000c5f7, 0x22 }, -+ { 0x0000c5f6, 0x22 }, -+ { 0x0000c057, 0x51 }, -+ { 0x0000c054, 0x55 }, -+ { 0x0000c053, 0x55 }, -+ { 0x0000c052, 0x55 }, -+ { 0x0000c051, 0x01 }, -+ { 0x0000c050, 0x15 }, -+ { 0x0000c060, 0x99 }, -+ { 0x0000c030, 0x55 }, -+ { 0x0000c061, 0x55 }, -+ { 0x0000c063, 0x55 }, -+ { 0x0000c065, 0xa5 }, -+ { 0x0000c06b, 0x0a }, -+ { 0x0000ca05, 0xd6 }, -+ { 0x0000ca06, 0x11 }, -+ { 0x0000ca07, 0x1e }, -+ { 0x0000ca25, 0xd6 }, -+ { 0x0000ca26, 0x11 }, -+ { 0x0000ca27, 0x1e }, -+ { 0x0000cd00, 0x05 }, -+ { 0x0000cd81, 0x49 }, -+ { 0x0000cd82, 0x49 }, -+ { 0x0000c604, 0x40 }, -+ { 0x0000c609, 0x40 }, -+ { 0x0000c046, 0xf7 }, -+ { 0x0000c045, 0xff }, -+ { 0x0000c044, 0xff }, -+ { 0x0000c043, 0xff }, -+ { 0x0000c042, 0xff }, -+ { 0x0000c041, 0xff }, -+ { 0x0000c040, 0xff }, -+ { 0x0000c049, 0xff }, -+ { 0x0000c028, 0x3f }, -+ { 0x0000c020, 0x3f }, -+ { 0x0000c032, 0x13 }, -+ { 0x0000c033, 0x01 }, -+ { 0x0000cc10, 0x01 }, -+ { 0x0000dc20, 0x03 }, -+ { 0x0000de03, 0x05 }, -+ { 0x0000dc00, 0x00 }, -+ { 0x0000c700, 0xf0 }, -+ { 0x0000c701, 0x13 }, -+ { 0x0000c900, 0xc3 }, -+ { 0x0000c570, 0x08 }, -+ { 0x0000c086, 0x02 }, -+ { 0x0000c085, 0x7f }, -+ { 0x0000c084, 0x00 }, -+ { 0x0000c081, 0xff }, -+ { 0x0000f084, 0x0f }, -+ { 0x0000f083, 0xff }, -+ { 0x0000f082, 0xff }, -+ { 0x0000f081, 0xff }, -+ { 0x0000f080, 0xff }, -+ { 0x20003003, 0x3f }, -+ { 0x20005818, 0x81 }, -+ { 0x20009018, 0x81 }, -+ { 0x2000301c, 0x81 }, -+ { 0x0000c003, 0xc0 }, -+ { 0x0000c047, 0x80 }, -+ { 0x0000d541, 0x80 }, -+ { 0x0000d487, 0x0b }, -+ { 0x0000d487, 0x3b }, -+ { 0x0000d486, 0xc3 }, -+ { 0x0000d470, 0x89 }, -+ { 0x0000d471, 0x3a }, -+ { 0x0000d472, 0x3d }, -+ { 0x0000d474, 0x11 }, -+ { 0x0000d475, 0x32 }, -+ { 0x0000d476, 0x64 }, -+ { 0x0000d477, 0x10 }, -+ { 0x0000d478, 0xff }, -+ { 0x0000d479, 0x20 }, -+ { 0x0000d47a, 0x10 }, -+ { 0x10008000, 0x67 }, -+ { 0x10008001, 0x80 }, -+ { 0x10008002, 0x00 }, -+ { 0x10008003, 0x00 }, -+ { 0x1000cd56, 0x00 }, -+ { 0x0000d486, 0x43 }, -+ { 0x3fc2dfc3, 0x00 }, -+ { 0x3fc2dfc2, 0x00 }, -+ { 0x3fc2dfc1, 0x00 }, -+ { 0x3fc2dfc0, 0x03 }, -+ { 0x3fc2dfc7, 0x00 }, -+ { 0x3fc2dfc6, 0x00 }, -+ { 0x3fc2dfc5, 0x00 }, -+ { 0x3fc2dfc4, 0x01 }, -+ { 0x3fc2dfa3, 0x00 }, -+ { 0x3fc2dfa2, 0x00 }, -+ { 0x3fc2dfa1, 0x00 }, -+ { 0x3fc2dfa0, 0x00 }, -+ { 0x3fc2df80, 0x10 }, -+ { 0x3fc2df81, 0x20 }, -+ { 0x3fc2df82, 0x00 }, -+ { 0x3fc2df83, 0x00 }, -+ { 0x3fc2df84, 0x50 }, -+ { 0x3fc2df85, 0x19 }, -+ { 0x3fc2df86, 0x00 }, -+ { 0x3fc2df87, 0x00 }, -+ { 0x3fc2df88, 0x52 }, -+ { 0x3fc2df89, 0x23 }, -+ { 0x3fc2df8a, 0x00 }, -+ { 0x3fc2df8b, 0x00 }, -+ { 0x3fc2df8c, 0xe0 }, -+ { 0x3fc2df8d, 0x2e }, -+ { 0x3fc2df8e, 0x00 }, -+ { 0x3fc2df8f, 0x00 }, -+ { 0x3fc2df90, 0xe0 }, -+ { 0x3fc2df91, 0x2e }, -+ { 0x3fc2df92, 0x00 }, -+ { 0x3fc2df93, 0x00 }, -+ { 0x3fc2df94, 0x01 }, -+ { 0x3fc2df95, 0x08 }, -+ { 0x3fc2df96, 0x00 }, -+ { 0x3fc2df97, 0x00 }, -+ { 0x3fc2df40, 0x80 }, -+ { 0x3fc2df41, 0xbb }, -+ { 0x3fc2df42, 0x00 }, -+ { 0x3fc2df43, 0x00 }, -+ { 0x3fc2df44, 0xc0 }, -+ { 0x3fc2df45, 0x99 }, -+ { 0x3fc2df46, 0x01 }, -+ { 0x3fc2df47, 0x00 }, -+ { 0x3fc2df48, 0x00 }, -+ { 0x3fc2df49, 0x00 }, -+ { 0x3fc2df4a, 0x00 }, -+ { 0x3fc2df4b, 0x00 }, -+ { 0x3fc2df4c, 0x00 }, -+ { 0x3fc2df4d, 0x00 }, -+ { 0x3fc2df4e, 0x00 }, -+ { 0x3fc2df4f, 0x00 }, -+ { 0x3fc2df50, 0x01 }, -+ { 0x3fc2df51, 0x00 }, -+ { 0x3fc2df52, 0x00 }, -+ { 0x3fc2df53, 0x00 }, -+ { 0x3fc2df54, 0x01 }, -+ { 0x3fc2df55, 0x00 }, -+ { 0x3fc2df56, 0x00 }, -+ { 0x3fc2df57, 0x00 }, -+ { 0x3fc2df58, 0x00 }, -+ { 0x3fc2df59, 0x00 }, -+ { 0x3fc2df5a, 0x00 }, -+ { 0x3fc2df5b, 0x00 }, -+ { 0x3fc2df5c, 0x01 }, -+ { 0x3fc2df5d, 0x00 }, -+ { 0x3fc2df5e, 0x00 }, -+ { 0x3fc2df5f, 0x00 }, -+ { 0x3fc2df60, 0x00 }, -+ { 0x3fc2df61, 0x00 }, -+ { 0x3fc2df62, 0x00 }, -+ { 0x3fc2df63, 0x00 }, -+ { 0x3fc2df64, 0x00 }, -+ { 0x3fc2df65, 0x00 }, -+ { 0x3fc2df66, 0x00 }, -+ { 0x3fc2df67, 0x10 }, -+ { 0x3fc2df68, 0x01 }, -+ { 0x3fc2df69, 0x00 }, -+ { 0x3fc2df6a, 0x00 }, -+ { 0x3fc2df6b, 0x00 }, -+ { 0x3fc2df6c, 0x01 }, -+ { 0x3fc2df6d, 0x00 }, -+ { 0x3fc2df6e, 0x00 }, -+ { 0x3fc2df6f, 0x00 }, -+ { 0x3fc2df70, 0x04 }, -+ { 0x3fc2df71, 0x00 }, -+ { 0x3fc2df72, 0x00 }, -+ { 0x3fc2df73, 0x00 }, -+ { 0x3fc2df74, 0x01 }, -+ { 0x3fc2df75, 0x00 }, -+ { 0x3fc2df76, 0x00 }, -+ { 0x3fc2df77, 0x00 }, -+ { 0x1000db00, 0x04 }, -+ { 0x1000db01, 0x00 }, -+ { 0x1000db02, 0x10 }, -+ { 0x1000db03, 0x00 }, -+ { 0x1000db04, 0x00 }, -+ { 0x1000db05, 0x45 }, -+ { 0x1000db06, 0x0d }, -+ { 0x1000db07, 0x01 }, -+ { 0x1000db08, 0x00 }, -+ { 0x1000db09, 0x00 }, -+ { 0x1000db0a, 0xbf }, -+ { 0x1000db0b, 0x0b }, -+ { 0x1000db0c, 0x11 }, -+ { 0x1000db0d, 0x00 }, -+ { 0x1000db0e, 0x00 }, -+ { 0x1000db0f, 0x00 }, -+ { 0x1000db10, 0x2c }, -+ { 0x1000db11, 0xfa }, -+ { 0x1000db12, 0x00 }, -+ { 0x1000db13, 0x00 }, -+ { 0x1000db14, 0x09 }, -+ { 0x0000d540, 0x21 }, -+ { 0x0000d714, 0x17 }, -+ { 0x0000dd0b, 0x0d }, -+ { 0x0000dd0a, 0xff }, -+ { 0x0000dd09, 0x0d }, -+ { 0x0000dd08, 0xff }, -+ { 0x0000c5fb, 0x12 }, -+ { 0x0000c570, 0x08 }, -+}; -+ - static const struct reg_default rt1320_reg_defaults[] = { - { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, -@@ -440,6 +836,7 @@ static const struct reg_default rt1320_mbq_defaults[] = { - static bool rt1320_readable_register(struct device *dev, unsigned int reg) - { - switch (reg) { -+ case 0x004d: - case 0xc000 ... 0xc086: - case 0xc400 ... 0xc409: - case 0xc480 ... 0xc48f: -@@ -497,10 +894,15 @@ static bool rt1320_readable_register(struct device *dev, unsigned int reg) - case RT1321_PATCH_MAIN_VER ... RT1321_PATCH_BETA_VER: - case 0x1000f008: - case 0x1000f021: -- case 0x2000300f: -+ case 0x20003000 ... 0x2000300f: - case 0x2000301c: -- case 0x2000900f: -+ case 0x20003040 ... 0x20003059: -+ case 0x20005800 ... 0x2000580f: -+ case 0x20005818: -+ case 0x20005840 ... 0x20005859: -+ case 0x20009000 ... 0x2000900f: - case 0x20009018: -+ case 0x20009040 ... 0x20009059: - case 0x3fc000c0 ... 0x3fc2dfc8: - case 0x3fe00000 ... 0x3fe36fff: - /* 0x40801508/0x40801809/0x4080180a/0x40801909/0x4080190a */ -@@ -545,6 +947,7 @@ static bool rt1320_readable_register(struct device *dev, unsigned int reg) - static bool rt1320_volatile_register(struct device *dev, unsigned int reg) - { - switch (reg) { -+ case 0x004d: - case 0xc000: - case 0xc003: - case 0xc081: -@@ -596,15 +999,20 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg) - case 0x1000c000 ... 0x1000dfff: - case 0x1000f008: - case 0x1000f021: -- case 0x2000300f: -+ case 0x2000300e ... 0x2000300f: - case 0x2000301c: -- case 0x2000900f: -+ case 0x20003040 ... 0x20003059: -+ case 0x2000580e ... 0x2000580f: -+ case 0x20005818: -+ case 0x20005840 ... 0x20005859: -+ case 0x2000900e ... 0x2000900f: - case 0x20009018: -+ case 0x20009040 ... 0x20009059: - case 0x3fc2ab80 ... 0x3fc2ac4c: - case 0x3fc2b780: - case 0x3fc2bf80 ... 0x3fc2bf83: - case 0x3fc2bfc0 ... 0x3fc2bfc8: -- case 0x3fc2d300 ... 0x3fc2d354: -+ case 0x3fc2d300 ... 0x3fc2d3cc: - case 0x3fc2dfc0 ... 0x3fc2dfc8: - case 0x3fe2e000 ... 0x3fe2e003: - case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0): -@@ -892,6 +1300,42 @@ static int rt1320_check_fw_ready(struct rt1320_sdw_priv *rt1320) - return 0; - } - -+static int rt1320_dspfw_status(struct rt1320_sdw_priv *rt1320) -+{ -+ struct device *dev = &rt1320->sdw_slave->dev; -+ unsigned int fw_status_addr, fw_ready; -+ unsigned int dspfw_run; -+ -+ switch (rt1320->dev_id) { -+ case RT1320_DEV_ID: -+ fw_status_addr = RT1320_DSPFW_STATUS_ADDR; -+ break; -+ case RT1321_DEV_ID: -+ fw_status_addr = RT1321_DSPFW_STATUS_ADDR; -+ break; -+ default: -+ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); -+ return -EINVAL; -+ } -+ -+ regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); -+ fw_ready &= 0x1; -+ -+ if (rt1320->dev_id == RT1321_DEV_ID) { -+ regmap_read(rt1320->regmap, 0xf01e, &dspfw_run); -+ dspfw_run &= 0x1; -+ fw_ready = (!dspfw_run && fw_ready); -+ } -+ -+ if (fw_ready) { -+ dev_dbg(dev, "%s, DSP FW was already\n", __func__); -+ return 1; -+ } -+ -+ dev_dbg(dev, "%s, DSP FW is NOT ready. Please load DSP FW first\n", __func__); -+ return 0; -+} -+ - static int rt1320_check_power_state_ready(struct rt1320_sdw_priv *rt1320, enum rt1320_power_state ps) - { - struct device *dev = &rt1320->sdw_slave->dev; -@@ -982,6 +1426,9 @@ static int rt1320_fw_param_protocol(struct rt1320_sdw_priv *rt1320, enum rt1320_ - if (!tempbuf) - return -ENOMEM; - -+ if (rt1320->dev_id == RT1321_DEV_ID && rt1320->version_id == RT1321_VA2) -+ paramid += 0x0bff0000; -+ - paramhr.moudleid = 1; - paramhr.commandtype = cmdid; - /* 8 is "sizeof(paramid) + sizeof(paramlength)" */ -@@ -1120,45 +1567,73 @@ static void rt1320_calc_r0(struct rt1320_sdw_priv *rt1320) - l_calir0, l_calir0_lo, r_calir0, r_calir0_lo); - } - -+static int rt1320_pilot_tone_output(struct rt1320_sdw_priv *rt1320) -+{ -+ struct device *dev = &rt1320->sdw_slave->dev; -+ int l_targetpostgain, r_targetpostgain; -+ unsigned long long factor = (1 << 12); -+ int l_pilotgain[9], r_pilotgain[9]; -+ const int postgain_step = 234; -+ int targetGain; -+ -+ switch (rt1320->dev_id) { -+ case RT1320_DEV_ID: -+ targetGain = -320000; -+ break; -+ case RT1321_DEV_ID: -+ targetGain = -420000; -+ if (rt1320->version_id == RT1321_VA0) -+ targetGain = -320000; -+ break; -+ default: -+ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); -+ return -EINVAL; -+ } -+ -+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 70, &l_pilotgain[0], sizeof(l_pilotgain)); -+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 71, &r_pilotgain[0], sizeof(r_pilotgain)); -+ dev_dbg(dev, "%s, LR pilotgain %d, %d\n", __func__, l_pilotgain[2], r_pilotgain[2]); -+ -+ /* calculate pilot tone gain */ -+ l_pilotgain[2] = (l_pilotgain[2] * 10000) / factor; -+ r_pilotgain[2] = (r_pilotgain[2] * 10000) / factor; -+ -+ /* calculate post gain to meet target gain */ -+ l_targetpostgain = abs(targetGain - l_pilotgain[2]) / postgain_step; -+ r_targetpostgain = abs(targetGain - r_pilotgain[2]) / postgain_step; -+ l_targetpostgain = 0xfff - l_targetpostgain; -+ r_targetpostgain = 0xfff - r_targetpostgain; -+ dev_dbg(dev, "%s, LR targetpostgain=0x%x, 0x%x\n", __func__, l_targetpostgain, r_targetpostgain); -+ -+ regmap_write(rt1320->regmap, 0xdd0b, (l_targetpostgain & 0xf00) >> 8); -+ regmap_write(rt1320->regmap, 0xdd0a, l_targetpostgain & 0xff); -+ regmap_write(rt1320->regmap, 0xdd09, (r_targetpostgain & 0xf00) >> 8); -+ regmap_write(rt1320->regmap, 0xdd08, r_targetpostgain & 0xff); -+ -+ return 0; -+} -+ - static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) - { - struct device *dev = &rt1320->sdw_slave->dev; - struct rt1320_datafixpoint audfixpoint[2]; - unsigned int reg_c5fb, reg_c570, reg_cd00; -- unsigned int vol_reg[4], fw_ready; -+ unsigned int vol_reg[4]; - unsigned long long l_meanr0, r_meanr0; -- unsigned int fw_status_addr; - int l_re[5], r_re[5]; - int ret, tmp; - unsigned long long factor = (1 << 27); - unsigned short l_advancegain, r_advancegain; - unsigned int delay_s = 7; /* delay seconds for the calibration */ -+ int dspfw_status; - - if (!rt1320->component) - return; - -- switch (rt1320->dev_id) { -- case RT1320_DEV_ID: -- fw_status_addr = RT1320_DSPFW_STATUS_ADDR; -- break; -- case RT1321_DEV_ID: -- fw_status_addr = RT1321_DSPFW_STATUS_ADDR; -- break; -- default: -- dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); -- return; -- } -- -- /* set volume 0dB */ - regmap_read(rt1320->regmap, 0xdd0b, &vol_reg[3]); - regmap_read(rt1320->regmap, 0xdd0a, &vol_reg[2]); - regmap_read(rt1320->regmap, 0xdd09, &vol_reg[1]); - regmap_read(rt1320->regmap, 0xdd08, &vol_reg[0]); -- regmap_write(rt1320->regmap, 0xdd0b, 0x0f); -- regmap_write(rt1320->regmap, 0xdd0a, 0xff); -- regmap_write(rt1320->regmap, 0xdd09, 0x0f); -- regmap_write(rt1320->regmap, 0xdd08, 0xff); -- - regmap_read(rt1320->regmap, 0xc5fb, ®_c5fb); - regmap_read(rt1320->regmap, 0xc570, ®_c570); - regmap_read(rt1320->regmap, 0xcd00, ®_cd00); -@@ -1171,9 +1646,8 @@ static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) - goto _finish_; - } - -- regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); -- fw_ready &= 0x1; -- if (!fw_ready) { -+ dspfw_status = rt1320_dspfw_status(rt1320); -+ if (dspfw_status <= 0) { - dev_dbg(dev, "%s, DSP FW is NOT ready. Please load DSP FW first\n", __func__); - goto _finish_; - } -@@ -1184,8 +1658,19 @@ static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) - goto _finish_; - } - -- if (rt1320->dev_id == RT1320_DEV_ID) -- regmap_write(rt1320->regmap, 0xc5fb, 0x00); -+ /* fine tune pilot tone output */ -+ ret = rt1320_pilot_tone_output(rt1320); -+ if (ret < 0) { -+ dev_dbg(dev, "%s, Failed to tune pilot tone output\n", __func__); -+ goto _finish_; -+ } -+ -+ if (rt1320->dev_id == RT1321_DEV_ID) { -+ regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x00); -+ regmap_write(rt1320->regmap, 0xc5c4, 0x12); -+ } -+ -+ regmap_write(rt1320->regmap, 0xc5fb, 0x00); - regmap_write(rt1320->regmap, 0xc570, 0x0b); - regmap_write(rt1320->regmap, 0xcd00, 0xc5); - -@@ -1246,6 +1731,11 @@ static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) - SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03); - rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x03); - -+ if (rt1320->dev_id == RT1321_DEV_ID) { -+ regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x80); -+ regmap_write(rt1320->regmap, 0xc5c4, 0x10); -+ } -+ - /* advance gain will be set when R0 load, not here */ - regmap_write(rt1320->regmap, 0xdd0b, vol_reg[3]); - regmap_write(rt1320->regmap, 0xdd0a, vol_reg[2]); -@@ -1296,7 +1786,7 @@ static int rt1320_r0_cali_put(struct snd_kcontrol *kcontrol, - static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320) - { - struct sdw_slave *slave = rt1320->sdw_slave; -- const struct firmware *patch; -+ const struct firmware *patch __free(firmware) = NULL; - const char *filename; - unsigned int addr, val, min_addr, max_addr; - const unsigned char *ptr; -@@ -1312,6 +1802,9 @@ static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320) - max_addr = 0x10007fff; - break; - case RT1321_DEV_ID: -+ if (rt1320->version_id == RT1321_VA2) -+ return; -+ - filename = RT1321_VA_MCU_PATCH; - min_addr = 0x10008000; - max_addr = 0x10008fff; -@@ -1347,17 +1840,15 @@ static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320) - - if (addr > max_addr || addr < min_addr) { - dev_err(&slave->dev, "%s: the address 0x%x is wrong", __func__, addr); -- goto _exit_; -+ return; - } - if (val > 0xff) { - dev_err(&slave->dev, "%s: the value 0x%x is wrong", __func__, val); -- goto _exit_; -+ return; - } - regmap_write(rt1320->regmap, addr, val); - } - } --_exit_: -- release_firmware(patch); - } - } - -@@ -1382,30 +1873,17 @@ static void rt1320_vab_preset(struct rt1320_sdw_priv *rt1320) - static void rt1320_t0_load(struct rt1320_sdw_priv *rt1320, unsigned int l_t0, unsigned int r_t0) - { - struct device *dev = &rt1320->sdw_slave->dev; -- unsigned int factor = (1 << 22), fw_ready; -+ unsigned int factor = (1 << 22); - int l_t0_data[38], r_t0_data[38]; -- unsigned int fw_status_addr; -- -- switch (rt1320->dev_id) { -- case RT1320_DEV_ID: -- fw_status_addr = RT1320_DSPFW_STATUS_ADDR; -- break; -- case RT1321_DEV_ID: -- fw_status_addr = RT1321_DSPFW_STATUS_ADDR; -- break; -- default: -- dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); -- return; -- } -+ int dspfw_status; - - regmap_write(rt1320->regmap, - SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, - RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00); - rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00); - -- regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); -- fw_ready &= 0x1; -- if (!fw_ready) { -+ dspfw_status = rt1320_dspfw_status(rt1320); -+ if (dspfw_status <= 0) { - dev_warn(dev, "%s, DSP FW is NOT ready\n", __func__); - goto _exit_; - } -@@ -1444,7 +1922,7 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) - struct device *dev = &rt1320->sdw_slave->dev; - static const char func_tag[] = "FUNC"; - static const char xu_tag[] = "XU"; -- const struct firmware *rae_fw = NULL; -+ const struct firmware *rae_fw __free(firmware) = NULL; - unsigned int fw_offset; - unsigned char *fw_data; - unsigned char *param_data; -@@ -1483,22 +1961,46 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) - request_firmware(&rae_fw, rae_filename, dev); - if (rae_fw) { - -- /* RAE CRC clear */ -- regmap_write(rt1320->regmap, 0xe80b, 0x0f); -- -- /* RAE stop & CRC disable */ -- regmap_update_bits(rt1320->regmap, 0xe803, 0xbc, 0x00); -- -- while (--retry) { -- regmap_read(rt1320->regmap, 0xe83f, &value); -- if (value & 0x40) -- break; -- usleep_range(1000, 1100); -- } -- if (!retry && !(value & 0x40)) { -- dev_err(dev, "%s: RAE is not ready to load\n", __func__); -- release_firmware(rae_fw); -- return -ETIMEDOUT; -+ switch (rt1320->dev_id) { -+ case RT1320_DEV_ID: -+ /* RAE CRC clear */ -+ regmap_write(rt1320->regmap, 0xe80b, 0x0f); -+ /* RAE stop & CRC disable */ -+ regmap_update_bits(rt1320->regmap, 0xe803, 0xbc, 0x00); -+ while (--retry) { -+ regmap_read(rt1320->regmap, 0xe83f, &value); -+ if (value & 0x40) -+ break; -+ usleep_range(1000, 1100); -+ } -+ if (!retry && !(value & 0x40)) { -+ dev_err(dev, "%s: RAE is not ready to load\n", __func__); -+ return -ETIMEDOUT; -+ } -+ break; -+ case RT1321_DEV_ID: -+ /* RAE CRC clear */ -+ regmap_write(rt1320->regmap, 0x2000300e, 0xc0); -+ regmap_write(rt1320->regmap, 0x2000300f, 0x0f); -+ /* RAE stop & Phase sync & CRC disable */ -+ regmap_update_bits(rt1320->regmap, 0x20003003, 0xfe, 0x00); -+ regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x00); -+ regmap_update_bits(rt1320->regmap, 0x2000301c, 0x01, 0x00); -+ /* check whether write state is ready */ -+ while (--retry) { -+ regmap_read(rt1320->regmap, 0x20003043, &value); -+ if (value & 0x40) -+ break; -+ usleep_range(1000, 1100); -+ } -+ if (!retry && !(value & 0x40)) { -+ dev_err(dev, "%s: RAE is not ready to load\n", __func__); -+ return -ETIMEDOUT; -+ } -+ break; -+ default: -+ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); -+ return -EINVAL; - } - - dev_dbg(dev, "%s, rae_fw size=0x%zx\n", __func__, rae_fw->size); -@@ -1551,7 +2053,6 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) - } - - regcache_cache_bypass(rt1320->regmap, false); -- release_firmware(rae_fw); - - } else { - dev_err(dev, "%s: Failed to load %s firmware\n", __func__, rae_filename); -@@ -1559,18 +2060,34 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) - goto _exit_; - } - -- /* RAE CRC enable */ -- regmap_update_bits(rt1320->regmap, 0xe803, 0x0c, 0x0c); -- -- /* RAE update */ -- regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x00); -- regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x80); -- -- /* RAE run */ -- regmap_update_bits(rt1320->regmap, 0xe803, 0x80, 0x80); -- -- regmap_read(rt1320->regmap, 0xe80b, &value); -- dev_dbg(dev, "%s: CAE run => 0xe80b reg = 0x%x\n", __func__, value); -+ switch (rt1320->dev_id) { -+ case RT1320_DEV_ID: -+ /* RAE CRC enable */ -+ regmap_update_bits(rt1320->regmap, 0xe803, 0x0c, 0x0c); -+ /* RAE update */ -+ regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x00); -+ regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x80); -+ /* RAE run */ -+ regmap_update_bits(rt1320->regmap, 0xe803, 0x80, 0x80); -+ regmap_read(rt1320->regmap, 0xe80b, &value); -+ dev_dbg(dev, "%s: CAE run => 0xe80b reg = 0x%x\n", __func__, value); -+ break; -+ case RT1321_DEV_ID: -+ /* RAE CRC enable */ -+ regmap_update_bits(rt1320->regmap, 0x20003003, 0x30, 0x30); -+ /* RAE update */ -+ regmap_update_bits(rt1320->regmap, 0x2000301c, 0x80, 0x00); -+ regmap_update_bits(rt1320->regmap, 0x2000301c, 0x80, 0x80); -+ regmap_update_bits(rt1320->regmap, 0x20009018, 0x80, 0x00); -+ regmap_update_bits(rt1320->regmap, 0x20009018, 0x80, 0x80); -+ regmap_update_bits(rt1320->regmap, 0x20005818, 0x80, 0x00); -+ regmap_update_bits(rt1320->regmap, 0x20005818, 0x80, 0x80); -+ /* RAE run */ -+ regmap_update_bits(rt1320->regmap, 0x2000301c, 0x01, 0x01); -+ /* Phase sync eanble */ -+ regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x80); -+ break; -+ } - - rt1320->rae_update_done = true; - -@@ -1598,32 +2115,21 @@ struct rt1320_dspfwheader { - - struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt1320->component); - struct device *dev = &rt1320->sdw_slave->dev; -- unsigned int val, i, fw_offset, fw_ready; -- unsigned int fw_status_addr; -+ unsigned int val, i, fw_offset; - struct rt1320_dspfwheader *fwheader; - struct rt1320_imageinfo *ptr_img; - struct sdw_bpt_section sec[10]; -- const struct firmware *fw = NULL; -+ const struct firmware *fw __free(firmware) = NULL; - unsigned char *fw_data; - bool dev_fw_match = false; - static const char hdr_sig[] = "AFX"; - unsigned int hdr_size = 0; - const char *dmi_vendor, *dmi_product, *dmi_sku; - int len_vendor, len_product, len_sku; -+ unsigned char boot_mode = 0; /* 0: from RAM; 1: from ROM */ -+ unsigned char has_0x3fc00000 = 0; - char filename[512]; - -- switch (rt1320->dev_id) { -- case RT1320_DEV_ID: -- fw_status_addr = RT1320_DSPFW_STATUS_ADDR; -- break; -- case RT1321_DEV_ID: -- fw_status_addr = RT1321_DSPFW_STATUS_ADDR; -- break; -- default: -- dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); -- return; -- } -- - dmi_vendor = dmi_get_system_info(DMI_SYS_VENDOR); - dmi_product = dmi_get_system_info(DMI_PRODUCT_NAME); - dmi_sku = dmi_get_system_info(DMI_PRODUCT_SKU); -@@ -1653,17 +2159,12 @@ struct rt1320_dspfwheader { - RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00); - rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00); - -- regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); -- fw_ready &= 0x1; -- if (fw_ready) { -+ if (rt1320_dspfw_status(rt1320)) { - dev_dbg(dev, "%s, DSP FW was already\n", __func__); - rt1320->fw_load_done = true; - goto _exit_; - } - -- /* change to IRAM */ -- regmap_update_bits(rt1320->regmap, 0xf01e, 0x80, 0x00); -- - request_firmware(&fw, filename, dev); - if (fw) { - fwheader = (struct rt1320_dspfwheader *)fw->data; -@@ -1672,7 +2173,6 @@ struct rt1320_dspfwheader { - - if (fwheader->sync != 0x0a1c5679) { - dev_err(dev, "%s: FW sync error\n", __func__); -- release_firmware(fw); - goto _exit_; - } - -@@ -1709,9 +2209,11 @@ struct rt1320_dspfwheader { - dev_fw_match = true; - break; - case RT1321_DEV_ID: -- if (ptr_img->addr == 0x3fc00000) -- if (fw_data[9] == '1') -+ if (ptr_img->addr == 0x3fc00000) { -+ if (fw_data[7] == '1') - dev_fw_match = true; -+ has_0x3fc00000 = 1; -+ } - break; - default: - dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); -@@ -1721,6 +2223,22 @@ struct rt1320_dspfwheader { - fw_offset += ptr_img->size; - } - -+ if (rt1320->dev_id == RT1321_DEV_ID && rt1320->version_id == RT1321_VA2) { -+ /* -+ * For 1321 VA2, if the FW doesn't include the section for address 0x3fc00000, -+ * it means the FW will boot from ROM and force dev_fw_match to true to download FW by BRA. -+ */ -+ if (!has_0x3fc00000) { -+ boot_mode = 1; -+ dev_fw_match = true; -+ } -+ dev_dbg(dev, "%s: Boot from %s for VA2\n", __func__, (boot_mode ? "ROM" : "RAM")); -+ } -+ -+ /* change to IRAM */ -+ if (!boot_mode) -+ regmap_update_bits(rt1320->regmap, 0xf01e, 0x80, 0x00); -+ - if (dev_fw_match) { - dev_dbg(dev, "%s, starting BRA downloading FW..\n", __func__); - rt1320->bra_msg.dev_num = rt1320->sdw_slave->dev_num; -@@ -1732,7 +2250,6 @@ struct rt1320_dspfwheader { - } - - regcache_cache_bypass(rt1320->regmap, false); -- release_firmware(fw); - - if (!dev_fw_match) { - dev_err(dev, "%s: FW file doesn't match to device\n", __func__); -@@ -1743,24 +2260,39 @@ struct rt1320_dspfwheader { - goto _exit_; - } - -- /* run RAM code */ -- regmap_read(rt1320->regmap, 0x3fc2bfc0, &val); -- val |= 0x8; -- regmap_write(rt1320->regmap, 0x3fc2bfc0, val); -- -- /* clear frame counter */ - switch (rt1320->dev_id) { - case RT1320_DEV_ID: -+ /* run RAM code */ -+ regmap_read(rt1320->regmap, 0x3fc2bfc0, &val); -+ val |= 0x8; -+ regmap_write(rt1320->regmap, 0x3fc2bfc0, val); -+ /* clear frame counter */ - regmap_write(rt1320->regmap, 0x3fc2bfcb, 0x00); - regmap_write(rt1320->regmap, 0x3fc2bfca, 0x00); - regmap_write(rt1320->regmap, 0x3fc2bfc9, 0x00); - regmap_write(rt1320->regmap, 0x3fc2bfc8, 0x00); - break; - case RT1321_DEV_ID: -+ if (!boot_mode) { -+ /* run RAM code */ -+ regmap_read(rt1320->regmap, 0x3fc2dfc0, &val); -+ val |= 0x8; -+ regmap_write(rt1320->regmap, 0x3fc2dfc0, val); -+ } -+ /* clear frame counter */ - regmap_write(rt1320->regmap, 0x3fc2dfcb, 0x00); - regmap_write(rt1320->regmap, 0x3fc2dfca, 0x00); - regmap_write(rt1320->regmap, 0x3fc2dfc9, 0x00); - regmap_write(rt1320->regmap, 0x3fc2dfc8, 0x00); -+ /* enable handshake */ -+ regmap_write(rt1320->regmap, 0x3fc2dfc4, 0x00); -+ regmap_write(rt1320->regmap, 0xd470, 0xad); -+ /* minimum phase settings */ -+ regmap_write(rt1320->regmap, 0xc5c4, 0x10); -+ regmap_write(rt1320->regmap, 0x20003003, 0x31); -+ regmap_update_bits(rt1320->regmap, 0x20003002, 0x40, 0x00); -+ regmap_write(rt1320->regmap, 0xc5b3, 0x01); -+ regmap_write(rt1320->regmap, 0xc052, 0x11); - break; - } - -@@ -1843,14 +2375,35 @@ static void rt1320_vc_preset(struct rt1320_sdw_priv *rt1320) - - static void rt1321_preset(struct rt1320_sdw_priv *rt1320) - { -+ const struct reg_sequence *blindwrite; - unsigned int i, reg, val, delay; -+ unsigned int array_size; - -- for (i = 0; i < ARRAY_SIZE(rt1321_blind_write); i++) { -- reg = rt1321_blind_write[i].reg; -- val = rt1321_blind_write[i].def; -- delay = rt1321_blind_write[i].delay_us; -+ switch (rt1320->version_id) { -+ case RT1321_VA0: -+ blindwrite = rt1321_blind_write; -+ array_size = ARRAY_SIZE(rt1321_blind_write); -+ break; -+ case RT1321_VA1: -+ blindwrite = rt1321_va1_blind_write; -+ array_size = ARRAY_SIZE(rt1321_va1_blind_write); -+ break; -+ case RT1321_VA2: -+ blindwrite = rt1321_va2_blind_write; -+ array_size = ARRAY_SIZE(rt1321_va2_blind_write); -+ break; -+ default: -+ dev_err(&rt1320->sdw_slave->dev, "%s: Unknown version ID %d\n", -+ __func__, rt1320->version_id); -+ return; -+ } - -- if (reg == 0x3fc2dfc3) -+ for (i = 0; i < array_size; i++) { -+ reg = blindwrite[i].reg; -+ val = blindwrite[i].def; -+ delay = blindwrite[i].delay_us; -+ -+ if (reg == 0x1000cd56) - rt1320_load_mcu_patch(rt1320); - - regmap_write(rt1320->regmap, reg, val); -@@ -1892,6 +2445,24 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave) - regmap_read(rt1320->regmap, RT1320_DEV_ID_0, &val); - regmap_read(rt1320->regmap, RT1320_DEV_ID_1, &tmp); - rt1320->dev_id = (val << 8) | tmp; -+ -+ /* This is a workaround that reads the value twice to obtain the correct result. */ -+ rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_0, &val); -+ rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_1, &tmp); -+ rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_0, &val); -+ rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_1, &tmp); -+ val = (val << 8) | tmp; -+ -+ if (rt1320->dev_id == RT1321_DEV_ID) { -+ if (rt1320->version_id == 0x01) -+ rt1320->version_id = RT1321_VA2; -+ else if (val == RT1321_DEV_HV_VA0_ID) -+ rt1320->version_id = RT1321_VA0; -+ else if (val == RT1321_DEV_HV_VA1_ID) -+ rt1320->version_id = RT1321_VA1; -+ else -+ dev_err(dev, "%s: Unknown version ID 0x%x for RT1321\n", __func__, rt1320->version_id); -+ } - } - - regmap_read(rt1320->regmap, -@@ -2326,25 +2897,12 @@ static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); - static int rt1320_r0_load(struct rt1320_sdw_priv *rt1320) - { - struct device *dev = regmap_get_device(rt1320->regmap); -- unsigned int fw_status_addr; -- unsigned int fw_ready; -+ int dspfw_status; - int ret = 0; - - if (!rt1320->r0_l_reg || !rt1320->r0_r_reg) - return -EINVAL; - -- switch (rt1320->dev_id) { -- case RT1320_DEV_ID: -- fw_status_addr = RT1320_DSPFW_STATUS_ADDR; -- break; -- case RT1321_DEV_ID: -- fw_status_addr = RT1321_DSPFW_STATUS_ADDR; -- break; -- default: -- dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); -- return -EINVAL; -- } -- - regmap_write(rt1320->regmap, - SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00); - ret = rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00); -@@ -2353,9 +2911,8 @@ static int rt1320_r0_load(struct rt1320_sdw_priv *rt1320) - goto _timeout_; - } - -- regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); -- fw_ready &= 0x1; -- if (!fw_ready) { -+ dspfw_status = rt1320_dspfw_status(rt1320); -+ if (dspfw_status <= 0) { - dev_dbg(dev, "%s, DSP FW is NOT ready\n", __func__); - goto _timeout_; - } -@@ -2458,7 +3015,7 @@ static int rt1320_dspfw_load_put(struct snd_kcontrol *kcontrol, - if (!rt1320->hw_init) - return 0; - -- ret = pm_runtime_resume(component->dev); -+ ret = pm_runtime_resume_and_get(component->dev); - if (ret < 0 && ret != -EACCES) - return ret; - -@@ -2469,6 +3026,8 @@ static int rt1320_dspfw_load_put(struct snd_kcontrol *kcontrol, - if (!ucontrol->value.integer.value[0]) - rt1320->fw_load_done = false; - -+ pm_runtime_mark_last_busy(component->dev); -+ pm_runtime_put_autosuspend(component->dev); - return 0; - } - -@@ -2819,6 +3378,37 @@ static int rt1320_sdw_pcm_hw_free(struct snd_pcm_substream *substream, - return 0; - } - -+static int rt1320_bus_config(struct sdw_slave *slave, -+ struct sdw_bus_params *params) -+{ -+ struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(&slave->dev); -+ unsigned int clk_base; -+ -+ regmap_read(rt1320->regmap, 0x004d, &clk_base); -+ dev_dbg(&rt1320->sdw_slave->dev, "%s clk_base=%x", __func__, clk_base); -+ -+ switch (clk_base) { -+ case RT1320_CLK_FREQ_19_2_MHZ: -+ case RT1320_CLK_FREQ_24MHZ: -+ regmap_write(rt1320->regmap, 0xc600, 0x04); -+ regmap_write(rt1320->regmap, 0xc601, 0x83); -+ regmap_write(rt1320->regmap, 0xc602, 0x1f); -+ regmap_write(rt1320->regmap, 0xc603, 0x40); -+ break; -+ case RT1320_CLK_FREQ_24_576MHZ: -+ case RT1320_CLK_FREQ_22_5792MHZ: -+ regmap_write(rt1320->regmap, 0xc600, 0x07); -+ regmap_write(rt1320->regmap, 0xc601, 0x80); -+ regmap_write(rt1320->regmap, 0xc602, 0x0f); -+ regmap_write(rt1320->regmap, 0xc603, 0x40); -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ - /* - * slave_ops: callbacks for get_clock_stop_mode, clock_stop and - * port_prep are not defined for now -@@ -2826,12 +3416,13 @@ static int rt1320_sdw_pcm_hw_free(struct snd_pcm_substream *substream, - static const struct sdw_slave_ops rt1320_slave_ops = { - .read_prop = rt1320_read_prop, - .update_status = rt1320_update_status, -+ .bus_config = rt1320_bus_config, - }; - - static int rt1320_sdw_component_probe(struct snd_soc_component *component) - { -- int ret; - struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); -+ int ret; - - rt1320->component = component; - -@@ -3062,10 +3653,23 @@ static int rt1320_dev_resume(struct device *dev) - return ret; - - regcache_cache_only(rt1320->regmap, false); -- regcache_sync(rt1320->regmap); -+ ret = regcache_sync(rt1320->regmap); -+ if (ret) -+ goto err_sync; -+ - regcache_cache_only(rt1320->mbq_regmap, false); -- regcache_sync(rt1320->mbq_regmap); -+ ret = regcache_sync(rt1320->mbq_regmap); -+ if (ret) -+ goto err_sync; -+ - return 0; -+ -+err_sync: -+ regcache_cache_only(rt1320->regmap, true); -+ regcache_cache_only(rt1320->mbq_regmap, true); -+ regcache_mark_dirty(rt1320->regmap); -+ regcache_mark_dirty(rt1320->mbq_regmap); -+ return ret; - } - - static const struct dev_pm_ops rt1320_pm = { -diff --git a/sound/soc/codecs/rt1320-sdw.h b/sound/soc/codecs/rt1320-sdw.h ---- a/sound/soc/codecs/rt1320-sdw.h -+++ b/sound/soc/codecs/rt1320-sdw.h -@@ -17,12 +17,17 @@ - - #define RT1320_DEV_ID 0x6981 - #define RT1321_DEV_ID 0x7045 -+#define RT1321_DEV_HV_VA0_ID 0x6997 -+#define RT1321_DEV_HV_VA1_ID 0x7071 - - /* imp-defined registers */ - #define RT1320_DEV_VERSION_ID_1 0xc404 - #define RT1320_DEV_ID_1 0xc405 - #define RT1320_DEV_ID_0 0xc406 - -+#define RT1320_HV_DEV_ID_0 0xf622 -+#define RT1320_HV_DEV_ID_1 0xf623 -+ - #define RT1320_POWER_STATE 0xc560 - - #define RT1321_PATCH_MAIN_VER 0x1000cffe -@@ -94,6 +99,12 @@ enum rt1320_version_id { - RT1320_VC, - }; - -+enum rt1321_version_id { -+ RT1321_VA0, -+ RT1321_VA1, -+ RT1321_VA2, -+}; -+ - #define RT1320_VER_B_ID 0x07392238 - #define RT1320_VAB_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vab.bin" - #define RT1320_VC_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vc.bin" -@@ -121,6 +132,7 @@ struct rt1320_datafixpoint { - int invrs; - }; - -+/* FW parameter id 1300 */ - typedef struct FwPara_HwSwGain { - unsigned int SwAdvGain; - unsigned int SwBasGain; -@@ -163,6 +175,13 @@ enum rt1320_rw_type { - RT1320_PARAM_READ = 3, - }; - -+enum { -+ RT1320_CLK_FREQ_19_2_MHZ = 1, -+ RT1320_CLK_FREQ_24MHZ = 2, -+ RT1320_CLK_FREQ_24_576MHZ = 3, -+ RT1320_CLK_FREQ_22_5792MHZ = 4, -+}; -+ - struct rt1320_sdw_priv { - struct snd_soc_component *component; - struct regmap *regmap; -diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c ---- a/sound/soc/codecs/rt5514-spi.c -+++ b/sound/soc/codecs/rt5514-spi.c -@@ -6,6 +6,7 @@ - * Author: Oder Chiou - */ - -+#include - #include - #include - #include -@@ -79,17 +80,17 @@ static void rt5514_spi_copy_work(struct work_struct *work) - unsigned int cur_wp, remain_data; - u8 buf[8]; - -- mutex_lock(&rt5514_dsp->dma_lock); -+ guard(mutex)(&rt5514_dsp->dma_lock); - if (!rt5514_dsp->substream) { - dev_err(rt5514_dsp->dev, "No pcm substream\n"); -- goto done; -+ return; - } - - runtime = rt5514_dsp->substream->runtime; - period_bytes = snd_pcm_lib_period_bytes(rt5514_dsp->substream); - if (!period_bytes) { - schedule_delayed_work(&rt5514_dsp->copy_work, 5); -- goto done; -+ return; - } - - if (rt5514_dsp->buf_size % period_bytes) -@@ -111,7 +112,7 @@ static void rt5514_spi_copy_work(struct work_struct *work) - - if (remain_data < period_bytes) { - schedule_delayed_work(&rt5514_dsp->copy_work, 5); -- goto done; -+ return; - } - } - -@@ -146,9 +147,6 @@ static void rt5514_spi_copy_work(struct work_struct *work) - snd_pcm_period_elapsed(rt5514_dsp->substream); - - schedule_delayed_work(&rt5514_dsp->copy_work, 5); -- --done: -- mutex_unlock(&rt5514_dsp->dma_lock); - } - - static void rt5514_schedule_copy(struct rt5514_dsp *rt5514_dsp) -@@ -216,7 +214,7 @@ static int rt5514_spi_hw_params(struct snd_soc_component *component, - snd_soc_component_get_drvdata(component); - u8 buf[8]; - -- mutex_lock(&rt5514_dsp->dma_lock); -+ guard(mutex)(&rt5514_dsp->dma_lock); - rt5514_dsp->substream = substream; - rt5514_dsp->dma_offset = 0; - -@@ -225,8 +223,6 @@ static int rt5514_spi_hw_params(struct snd_soc_component *component, - if (buf[0] & RT5514_IRQ_STATUS_BIT) - rt5514_schedule_copy(rt5514_dsp); - -- mutex_unlock(&rt5514_dsp->dma_lock); -- - return 0; - } - -@@ -236,9 +232,8 @@ static int rt5514_spi_hw_free(struct snd_soc_component *component, - struct rt5514_dsp *rt5514_dsp = - snd_soc_component_get_drvdata(component); - -- mutex_lock(&rt5514_dsp->dma_lock); -- rt5514_dsp->substream = NULL; -- mutex_unlock(&rt5514_dsp->dma_lock); -+ scoped_guard(mutex, &rt5514_dsp->dma_lock) -+ rt5514_dsp->substream = NULL; - - cancel_delayed_work_sync(&rt5514_dsp->copy_work); - -diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c ---- a/sound/soc/codecs/rt5514.c -+++ b/sound/soc/codecs/rt5514.c -@@ -1073,12 +1073,18 @@ static int rt5514_set_bias_level(struct snd_soc_component *component, - * settings to make sure recording properly. - */ - if (rt5514->dsp_enabled) { -- rt5514->dsp_enabled = 0; -- regmap_multi_reg_write(rt5514->i2c_regmap, -- rt5514_i2c_patch, -- ARRAY_SIZE(rt5514_i2c_patch)); -+ ret = regmap_multi_reg_write(rt5514->i2c_regmap, -+ rt5514_i2c_patch, -+ ARRAY_SIZE(rt5514_i2c_patch)); -+ if (ret) -+ return ret; -+ - regcache_mark_dirty(rt5514->regmap); -- regcache_sync(rt5514->regmap); -+ ret = regcache_sync(rt5514->regmap); -+ if (ret) -+ return ret; -+ -+ rt5514->dsp_enabled = 0; - } - } - break; -diff --git a/sound/soc/codecs/rt5575-spi.c b/sound/soc/codecs/rt5575-spi.c ---- a/sound/soc/codecs/rt5575-spi.c -+++ b/sound/soc/codecs/rt5575-spi.c -@@ -93,7 +93,6 @@ static void rt5575_spi_burst_write(struct spi_device *spi, u32 addr, const u8 *t - int rt5575_spi_fw_load(struct spi_device *spi) - { - struct device *dev = &spi->dev; -- const struct firmware *firmware; - int i, ret; - static const char * const fw_path[] = { - "realtek/rt5575/rt5575_fw1.bin", -@@ -104,6 +103,7 @@ int rt5575_spi_fw_load(struct spi_device *spi) - static const u32 fw_addr[] = { 0x5f400000, 0x5f600000, 0x5f7fe000, 0x5f7ff000 }; - - for (i = 0; i < ARRAY_SIZE(fw_addr); i++) { -+ const struct firmware *firmware __free(firmware) = NULL; - ret = request_firmware(&firmware, fw_path[i], dev); - if (ret) { - dev_err(dev, "Request firmware failure: %d\n", ret); -@@ -111,7 +111,6 @@ int rt5575_spi_fw_load(struct spi_device *spi) - } - - rt5575_spi_burst_write(spi, fw_addr[i], firmware->data, firmware->size); -- release_firmware(firmware); - } - - return 0; -diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c ---- a/sound/soc/codecs/rt5645.c -+++ b/sound/soc/codecs/rt5645.c -@@ -6,6 +6,7 @@ - * Author: Bard Liao - */ - -+#include - #include - #include - #include -@@ -3323,92 +3324,89 @@ static void rt5645_jack_detect_work(struct work_struct *work) - if (!rt5645->component) - return; - -- mutex_lock(&rt5645->jd_mutex); -- -- switch (rt5645->pdata.jd_mode) { -- case 0: /* Not using rt5645 JD */ -- if (rt5645->gpiod_hp_det) { -- gpio_state = gpiod_get_value(rt5645->gpiod_hp_det); -- if (rt5645->pdata.inv_hp_pol) -- gpio_state ^= 1; -- dev_dbg(rt5645->component->dev, "gpio_state = %d\n", -- gpio_state); -- report = rt5645_jack_detect(rt5645->component, gpio_state); -- } -- snd_soc_jack_report(rt5645->hp_jack, -- report, SND_JACK_HEADPHONE); -- snd_soc_jack_report(rt5645->mic_jack, -- report, SND_JACK_MICROPHONE); -- mutex_unlock(&rt5645->jd_mutex); -- return; -- case 4: -- val = snd_soc_component_read(rt5645->component, RT5645_A_JD_CTRL1) & 0x0020; -- break; -- default: /* read rt5645 jd1_1 status */ -- val = snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x1000; -- break; -- -- } -- -- if (!val && (rt5645->jack_type == 0)) { /* jack in */ -- report = rt5645_jack_detect(rt5645->component, 1); -- } else if (!val && rt5645->jack_type == SND_JACK_HEADSET) { -- /* for push button and jack out */ -- btn_type = 0; -- if (snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x4) { -- /* button pressed */ -- report = SND_JACK_HEADSET; -- btn_type = rt5645_button_detect(rt5645->component); -- /* rt5650 can report three kinds of button behavior, -- one click, double click and hold. However, -- currently we will report button pressed/released -- event. So all the three button behaviors are -- treated as button pressed. */ -- switch (btn_type) { -- case 0x8000: -- case 0x4000: -- case 0x2000: -- report |= SND_JACK_BTN_0; -- break; -- case 0x1000: -- case 0x0800: -- case 0x0400: -- report |= SND_JACK_BTN_1; -- break; -- case 0x0200: -- case 0x0100: -- case 0x0080: -- report |= SND_JACK_BTN_2; -- break; -- case 0x0040: -- case 0x0020: -- case 0x0010: -- report |= SND_JACK_BTN_3; -- break; -- case 0x0000: /* unpressed */ -- break; -- default: -- dev_err(rt5645->component->dev, -- "Unexpected button code 0x%04x\n", -- btn_type); -- break; -+ scoped_guard(mutex, &rt5645->jd_mutex) { -+ switch (rt5645->pdata.jd_mode) { -+ case 0: /* Not using rt5645 JD */ -+ if (rt5645->gpiod_hp_det) { -+ gpio_state = gpiod_get_value(rt5645->gpiod_hp_det); -+ if (rt5645->pdata.inv_hp_pol) -+ gpio_state ^= 1; -+ dev_dbg(rt5645->component->dev, "gpio_state = %d\n", -+ gpio_state); -+ report = rt5645_jack_detect(rt5645->component, gpio_state); - } -+ snd_soc_jack_report(rt5645->hp_jack, -+ report, SND_JACK_HEADPHONE); -+ snd_soc_jack_report(rt5645->mic_jack, -+ report, SND_JACK_MICROPHONE); -+ return; -+ case 4: -+ val = snd_soc_component_read(rt5645->component, RT5645_A_JD_CTRL1) & 0x0020; -+ break; -+ default: /* read rt5645 jd1_1 status */ -+ val = snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x1000; -+ break; - } -- if (btn_type == 0)/* button release */ -- report = rt5645->jack_type; -- else { -- mod_timer(&rt5645->btn_check_timer, -- msecs_to_jiffies(100)); -- } -- } else { -- /* jack out */ -- report = 0; -- snd_soc_component_update_bits(rt5645->component, -- RT5645_INT_IRQ_ST, 0x1, 0x0); -- rt5645_jack_detect(rt5645->component, 0); -- } - -- mutex_unlock(&rt5645->jd_mutex); -+ if (!val && rt5645->jack_type == 0) { /* jack in */ -+ report = rt5645_jack_detect(rt5645->component, 1); -+ } else if (!val && rt5645->jack_type == SND_JACK_HEADSET) { -+ /* for push button and jack out */ -+ btn_type = 0; -+ if (snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x4) { -+ /* button pressed */ -+ report = SND_JACK_HEADSET; -+ btn_type = rt5645_button_detect(rt5645->component); -+ /* -+ * rt5650 can report three kinds of button behavior, -+ * one click, double click and hold. However, -+ * currently we will report button pressed/released -+ * event. So all the three button behaviors are -+ * treated as button pressed. -+ */ -+ switch (btn_type) { -+ case 0x8000: -+ case 0x4000: -+ case 0x2000: -+ report |= SND_JACK_BTN_0; -+ break; -+ case 0x1000: -+ case 0x0800: -+ case 0x0400: -+ report |= SND_JACK_BTN_1; -+ break; -+ case 0x0200: -+ case 0x0100: -+ case 0x0080: -+ report |= SND_JACK_BTN_2; -+ break; -+ case 0x0040: -+ case 0x0020: -+ case 0x0010: -+ report |= SND_JACK_BTN_3; -+ break; -+ case 0x0000: /* unpressed */ -+ break; -+ default: -+ dev_err(rt5645->component->dev, -+ "Unexpected button code 0x%04x\n", -+ btn_type); -+ break; -+ } -+ } -+ if (btn_type == 0)/* button release */ -+ report = rt5645->jack_type; -+ else -+ mod_timer(&rt5645->btn_check_timer, -+ msecs_to_jiffies(100)); -+ } else { -+ /* jack out */ -+ report = 0; -+ snd_soc_component_update_bits(rt5645->component, -+ RT5645_INT_IRQ_ST, 0x1, 0x0); -+ rt5645_jack_detect(rt5645->component, 0); -+ } -+ } - - snd_soc_jack_report(rt5645->hp_jack, report, SND_JACK_HEADPHONE); - snd_soc_jack_report(rt5645->mic_jack, report, SND_JACK_MICROPHONE); -@@ -3522,9 +3520,15 @@ static int rt5645_suspend(struct snd_soc_component *component) - static int rt5645_resume(struct snd_soc_component *component) - { - struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); -+ int ret; - - regcache_cache_only(rt5645->regmap, false); -- regcache_sync(rt5645->regmap); -+ ret = regcache_sync(rt5645->regmap); -+ if (ret) { -+ regcache_cache_only(rt5645->regmap, true); -+ regcache_mark_dirty(rt5645->regmap); -+ return ret; -+ } - - return 0; - } -@@ -4335,9 +4339,15 @@ static int rt5645_sys_suspend(struct device *dev) - static int rt5645_sys_resume(struct device *dev) - { - struct rt5645_priv *rt5645 = dev_get_drvdata(dev); -+ int ret; - - regcache_cache_only(rt5645->regmap, false); -- regcache_sync(rt5645->regmap); -+ ret = regcache_sync(rt5645->regmap); -+ if (ret) { -+ regcache_cache_only(rt5645->regmap, true); -+ regcache_mark_dirty(rt5645->regmap); -+ return ret; -+ } - - if (rt5645->hp_jack) { - rt5645->jack_type = 0; -diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c ---- a/sound/soc/codecs/rt5665.c -+++ b/sound/soc/codecs/rt5665.c -@@ -6,6 +6,7 @@ - * Author: Bard Liao - */ - -+#include - #include - #include - #include -@@ -1208,7 +1209,7 @@ static void rt5665_jack_detect_handler(struct work_struct *work) - usleep_range(10000, 15000); - } - -- mutex_lock(&rt5665->calibrate_mutex); -+ guard(mutex)(&rt5665->calibrate_mutex); - - val = snd_soc_component_read(rt5665->component, RT5665_AJD1_CTRL) & 0x0010; - if (!val) { -@@ -1274,8 +1275,6 @@ static void rt5665_jack_detect_handler(struct work_struct *work) - schedule_delayed_work(&rt5665->jd_check_work, 0); - else - cancel_delayed_work_sync(&rt5665->jd_check_work); -- -- mutex_unlock(&rt5665->calibrate_mutex); - } - - static const char * const rt5665_clk_sync[] = { -@@ -4564,7 +4563,7 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665) - { - int value, count; - -- mutex_lock(&rt5665->calibrate_mutex); -+ guard(mutex)(&rt5665->calibrate_mutex); - - regcache_cache_bypass(rt5665->regmap, true); - -@@ -4601,7 +4600,8 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665) - pr_err("HP Calibration Failure\n"); - regmap_write(rt5665->regmap, RT5665_RESET, 0); - regcache_cache_bypass(rt5665->regmap, false); -- goto out_unlock; -+ rt5665->calibration_done = true; -+ return; - } - - count++; -@@ -4620,7 +4620,8 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665) - pr_err("MONO Calibration Failure\n"); - regmap_write(rt5665->regmap, RT5665_RESET, 0); - regcache_cache_bypass(rt5665->regmap, false); -- goto out_unlock; -+ rt5665->calibration_done = true; -+ return; - } - - count++; -@@ -4635,9 +4636,7 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665) - regmap_write(rt5665->regmap, RT5665_BIAS_CUR_CTRL_8, 0xa602); - regmap_write(rt5665->regmap, RT5665_ASRC_8, 0x0120); - --out_unlock: - rt5665->calibration_done = true; -- mutex_unlock(&rt5665->calibrate_mutex); - } - - static void rt5665_calibrate_handler(struct work_struct *work) -diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c ---- a/sound/soc/codecs/rt5668.c -+++ b/sound/soc/codecs/rt5668.c -@@ -6,6 +6,7 @@ - * Author: Bard Liao - */ - -+#include - #include - #include - #include -@@ -986,7 +987,7 @@ static void rt5668_jack_detect_handler(struct work_struct *work) - return; - } - -- mutex_lock(&rt5668->calibrate_mutex); -+ guard(mutex)(&rt5668->calibrate_mutex); - - val = snd_soc_component_read(rt5668->component, RT5668_AJD1_CTRL) - & RT5668_JDH_RS_MASK; -@@ -1053,8 +1054,6 @@ static void rt5668_jack_detect_handler(struct work_struct *work) - schedule_delayed_work(&rt5668->jd_check_work, 0); - else - cancel_delayed_work_sync(&rt5668->jd_check_work); -- -- mutex_unlock(&rt5668->calibrate_mutex); - } - - static const struct snd_kcontrol_new rt5668_snd_controls[] = { -@@ -2356,7 +2355,7 @@ static void rt5668_calibrate(struct rt5668_priv *rt5668) - { - int value, count; - -- mutex_lock(&rt5668->calibrate_mutex); -+ guard(mutex)(&rt5668->calibrate_mutex); - - rt5668_reset(rt5668->regmap); - regmap_write(rt5668->regmap, RT5668_PWR_ANLG_1, 0xa2bf); -@@ -2400,9 +2399,6 @@ static void rt5668_calibrate(struct rt5668_priv *rt5668) - /* restore settings */ - regmap_write(rt5668->regmap, RT5668_STO1_ADC_MIXER, 0xc0c4); - regmap_write(rt5668->regmap, RT5668_PWR_DIG_1, 0x0000); -- -- mutex_unlock(&rt5668->calibrate_mutex); -- - } - - static int rt5668_i2c_probe(struct i2c_client *i2c) -diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c ---- a/sound/soc/codecs/rt5677-spi.c -+++ b/sound/soc/codecs/rt5677-spi.c -@@ -6,6 +6,7 @@ - * Author: Oder Chiou - */ - -+#include - #include - #include - #include -@@ -133,9 +134,8 @@ static int rt5677_spi_hw_params( - struct rt5677_dsp *rt5677_dsp = - snd_soc_component_get_drvdata(component); - -- mutex_lock(&rt5677_dsp->dma_lock); -+ guard(mutex)(&rt5677_dsp->dma_lock); - rt5677_dsp->substream = substream; -- mutex_unlock(&rt5677_dsp->dma_lock); - - return 0; - } -@@ -147,9 +147,8 @@ static int rt5677_spi_hw_free( - struct rt5677_dsp *rt5677_dsp = - snd_soc_component_get_drvdata(component); - -- mutex_lock(&rt5677_dsp->dma_lock); -+ guard(mutex)(&rt5677_dsp->dma_lock); - rt5677_dsp->substream = NULL; -- mutex_unlock(&rt5677_dsp->dma_lock); - - return 0; - } -@@ -311,17 +310,17 @@ static void rt5677_spi_copy_work(struct work_struct *work) - int ret = 0; - - /* Ensure runtime->dma_area buffer does not go away while copying. */ -- mutex_lock(&rt5677_dsp->dma_lock); -+ guard(mutex)(&rt5677_dsp->dma_lock); - if (!rt5677_dsp->substream) { - dev_err(rt5677_dsp->dev, "No pcm substream\n"); -- goto done; -+ return; - } - - runtime = rt5677_dsp->substream->runtime; - - if (rt5677_spi_mic_write_offset(&mic_write_offset)) { - dev_err(rt5677_dsp->dev, "No mic_write_offset\n"); -- goto done; -+ return; - } - - /* If this is the first time that we've asked for streaming data after -@@ -355,7 +354,7 @@ static void rt5677_spi_copy_work(struct work_struct *work) - ret = rt5677_spi_copy(rt5677_dsp, copy_bytes); - if (ret) { - dev_err(rt5677_dsp->dev, "Copy failed %d\n", ret); -- goto done; -+ return; - } - rt5677_dsp->avail_bytes += copy_bytes; - if (rt5677_dsp->avail_bytes >= period_bytes) { -@@ -367,8 +366,6 @@ static void rt5677_spi_copy_work(struct work_struct *work) - - delay = bytes_to_frames(runtime, period_bytes) / runtime->rate; - schedule_delayed_work(&rt5677_dsp->copy_work, secs_to_jiffies(delay)); --done: -- mutex_unlock(&rt5677_dsp->dma_lock); - } - - static int rt5677_spi_pcm_new(struct snd_soc_component *component, -@@ -507,10 +504,8 @@ int rt5677_spi_read(u32 addr, void *rxbuf, size_t len) - header[3] = ((addr + offset) & 0x0000ff00) >> 8; - header[4] = ((addr + offset) & 0x000000ff) >> 0; - -- mutex_lock(&spi_mutex); -- status |= spi_sync(g_spi, &m); -- mutex_unlock(&spi_mutex); -- -+ scoped_guard(mutex, &spi_mutex) -+ status |= spi_sync(g_spi, &m); - - /* Copy data back to caller buffer */ - rt5677_spi_reverse(cb + offset, len - offset, body, t[1].len); -@@ -564,9 +559,8 @@ int rt5677_spi_write(u32 addr, const void *txbuf, size_t len) - offset += t.len; - t.len += RT5677_SPI_HEADER + 1; - -- mutex_lock(&spi_mutex); -- status |= spi_sync(g_spi, &m); -- mutex_unlock(&spi_mutex); -+ scoped_guard(mutex, &spi_mutex) -+ status |= spi_sync(g_spi, &m); - } - return status; - } -@@ -591,10 +585,10 @@ void rt5677_spi_hotword_detected(void) - return; - } - -- mutex_lock(&rt5677_dsp->dma_lock); -- dev_info(rt5677_dsp->dev, "Hotword detected\n"); -- rt5677_dsp->new_hotword = true; -- mutex_unlock(&rt5677_dsp->dma_lock); -+ scoped_guard(mutex, &rt5677_dsp->dma_lock) { -+ dev_info(rt5677_dsp->dev, "Hotword detected\n"); -+ rt5677_dsp->new_hotword = true; -+ } - - schedule_delayed_work(&rt5677_dsp->copy_work, 0); - } -diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c ---- a/sound/soc/codecs/rt5677.c -+++ b/sound/soc/codecs/rt5677.c -@@ -6,6 +6,8 @@ - * Author: Oder Chiou - */ - -+#include -+#include - #include - #include - #include -@@ -563,46 +565,43 @@ static int rt5677_dsp_mode_i2c_write_addr(struct rt5677_priv *rt5677, - struct snd_soc_component *component = rt5677->component; - int ret; - -- mutex_lock(&rt5677->dsp_cmd_lock); -+ guard(mutex)(&rt5677->dsp_cmd_lock); - - ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB, - addr >> 16); - if (ret < 0) { - dev_err(component->dev, "Failed to set addr msb value: %d\n", ret); -- goto err; -+ return ret; - } - - ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB, - addr & 0xffff); - if (ret < 0) { - dev_err(component->dev, "Failed to set addr lsb value: %d\n", ret); -- goto err; -+ return ret; - } - - ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_MSB, - value >> 16); - if (ret < 0) { - dev_err(component->dev, "Failed to set data msb value: %d\n", ret); -- goto err; -+ return ret; - } - - ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_LSB, - value & 0xffff); - if (ret < 0) { - dev_err(component->dev, "Failed to set data lsb value: %d\n", ret); -- goto err; -+ return ret; - } - - ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE, - opcode); - if (ret < 0) { - dev_err(component->dev, "Failed to set op code value: %d\n", ret); -- goto err; -+ return ret; - } - --err: -- mutex_unlock(&rt5677->dsp_cmd_lock); -- - return ret; - } - -@@ -622,36 +621,33 @@ static int rt5677_dsp_mode_i2c_read_addr( - int ret; - unsigned int msb, lsb; - -- mutex_lock(&rt5677->dsp_cmd_lock); -+ guard(mutex)(&rt5677->dsp_cmd_lock); - - ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB, - addr >> 16); - if (ret < 0) { - dev_err(component->dev, "Failed to set addr msb value: %d\n", ret); -- goto err; -+ return ret; - } - - ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB, - addr & 0xffff); - if (ret < 0) { - dev_err(component->dev, "Failed to set addr lsb value: %d\n", ret); -- goto err; -+ return ret; - } - - ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE, - 0x0002); - if (ret < 0) { - dev_err(component->dev, "Failed to set op code value: %d\n", ret); -- goto err; -+ return ret; - } - - regmap_read(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_MSB, &msb); - regmap_read(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_LSB, &lsb); - *value = (msb << 16) | lsb; - --err: -- mutex_unlock(&rt5677->dsp_cmd_lock); -- - return ret; - } - -@@ -853,11 +849,11 @@ static int rt5677_parse_and_load_dsp(struct rt5677_priv *rt5677, const u8 *buf, - - static int rt5677_load_dsp_from_file(struct rt5677_priv *rt5677) - { -- const struct firmware *fwp; - struct device *dev = rt5677->component->dev; -- int ret = 0; -+ int ret; - - /* Load dsp firmware from rt5677_elf_vad file */ -+ const struct firmware *fwp __free(firmware) = NULL; - ret = request_firmware(&fwp, "rt5677_elf_vad", dev); - if (ret) { - dev_err(dev, "Request rt5677_elf_vad failed %d\n", ret); -@@ -865,9 +861,7 @@ static int rt5677_load_dsp_from_file(struct rt5677_priv *rt5677) - } - dev_info(dev, "Requested rt5677_elf_vad (%zu)\n", fwp->size); - -- ret = rt5677_parse_and_load_dsp(rt5677, fwp->data, fwp->size); -- release_firmware(fwp); -- return ret; -+ return rt5677_parse_and_load_dsp(rt5677, fwp->data, fwp->size); - } - - static int rt5677_set_dsp_vad(struct snd_soc_component *component, bool on) -@@ -940,21 +934,20 @@ static void rt5677_dsp_work(struct work_struct *work) - activity = false; - - /* Don't turn off the DSP while handling irqs */ -- mutex_lock(&rt5677->irq_lock); -- /* Set DSP CPU to Stop */ -- regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, -- RT5677_PWR_DSP_CPU, RT5677_PWR_DSP_CPU); -+ scoped_guard(mutex, &rt5677->irq_lock) { -+ /* Set DSP CPU to Stop */ -+ regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, -+ RT5677_PWR_DSP_CPU, RT5677_PWR_DSP_CPU); - -- rt5677_set_dsp_mode(rt5677, false); -+ rt5677_set_dsp_mode(rt5677, false); - -- /* Disable and clear VAD interrupt */ -- regmap_write(rt5677->regmap, RT5677_VAD_CTRL1, 0x2184); -+ /* Disable and clear VAD interrupt */ -+ regmap_write(rt5677->regmap, RT5677_VAD_CTRL1, 0x2184); - -- /* Set GPIO1 pin back to be IRQ output for jack detect */ -- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, -- RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ); -- -- mutex_unlock(&rt5677->irq_lock); -+ /* Set GPIO1 pin back to be IRQ output for jack detect */ -+ regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, -+ RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ); -+ } - } - } - -@@ -4767,6 +4760,21 @@ static int rt5677_gpio_direction_in(struct gpio_chip *chip, unsigned offset) - return rt5677_update_gpio_bits(rt5677, offset, m, v); - } - -+static int rt5677_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) -+{ -+ struct rt5677_priv *rt5677 = gpiochip_get_data(chip); -+ unsigned int shift = RT5677_GPIOx_DIR_SFT + (offset % 5) * 3; -+ unsigned int bank = offset / 5; -+ unsigned int reg = bank ? RT5677_GPIO_CTRL3 : RT5677_GPIO_CTRL2; -+ int ret; -+ -+ ret = regmap_test_bits(rt5677->regmap, reg, BIT(shift)); -+ if (ret < 0) -+ return ret; -+ -+ return ret ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; -+} -+ - /* - * Configures the GPIO as - * 0 - floating -@@ -4834,6 +4842,7 @@ static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset) - static const struct gpio_chip rt5677_template_chip = { - .label = RT5677_DRV_NAME, - .owner = THIS_MODULE, -+ .get_direction = rt5677_gpio_get_direction, - .direction_output = rt5677_gpio_direction_out, - .set = rt5677_gpio_set, - .direction_input = rt5677_gpio_direction_in, -@@ -4980,11 +4989,11 @@ static int rt5677_read(void *context, unsigned int reg, unsigned int *val) - - if (rt5677->is_dsp_mode) { - if (reg > 0xff) { -- mutex_lock(&rt5677->dsp_pri_lock); -- rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, -- reg & 0xff); -- rt5677_dsp_mode_i2c_read(rt5677, RT5677_PRIV_DATA, val); -- mutex_unlock(&rt5677->dsp_pri_lock); -+ scoped_guard(mutex, &rt5677->dsp_pri_lock) { -+ rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, -+ reg & 0xff); -+ rt5677_dsp_mode_i2c_read(rt5677, RT5677_PRIV_DATA, val); -+ } - } else { - rt5677_dsp_mode_i2c_read(rt5677, reg, val); - } -@@ -5002,12 +5011,12 @@ static int rt5677_write(void *context, unsigned int reg, unsigned int val) - - if (rt5677->is_dsp_mode) { - if (reg > 0xff) { -- mutex_lock(&rt5677->dsp_pri_lock); -- rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, -- reg & 0xff); -- rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_DATA, -- val); -- mutex_unlock(&rt5677->dsp_pri_lock); -+ scoped_guard(mutex, &rt5677->dsp_pri_lock) { -+ rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, -+ reg & 0xff); -+ rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_DATA, -+ val); -+ } - } else { - rt5677_dsp_mode_i2c_write(rt5677, reg, val); - } -@@ -5399,7 +5408,7 @@ static void rt5677_resume_irq_check(struct work_struct *work) - * Without this explicit check, unplug the headset right after suspend - * starts, then after resume the headset is still shown as plugged in. - */ -- mutex_lock(&rt5677->irq_lock); -+ guard(mutex)(&rt5677->irq_lock); - for (i = 0; i < RT5677_IRQ_NUM; i++) { - if (rt5677->irq_en & rt5677_irq_descs[i].enable_mask) { - virq = irq_find_mapping(rt5677->domain, i); -@@ -5407,7 +5416,6 @@ static void rt5677_resume_irq_check(struct work_struct *work) - handle_nested_irq(virq); - } - } -- mutex_unlock(&rt5677->irq_lock); - } - - static void rt5677_irq_bus_lock(struct irq_data *data) -diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c ---- a/sound/soc/codecs/rt5682-sdw.c -+++ b/sound/soc/codecs/rt5682-sdw.c -@@ -6,6 +6,7 @@ - // Author: Oder Chiou - // - -+#include - #include - #include - #include -@@ -410,7 +411,9 @@ static int rt5682_io_init(struct device *dev, struct sdw_slave *slave) - if (rt5682->first_hw_init) { - regcache_cache_bypass(rt5682->regmap, false); - regcache_mark_dirty(rt5682->regmap); -- regcache_sync(rt5682->regmap); -+ ret = regcache_sync(rt5682->regmap); -+ if (ret) -+ goto err_sync; - - /* volatile registers */ - regmap_update_bits(rt5682->regmap, RT5682_CBJ_CTRL_2, -@@ -472,8 +475,16 @@ static int rt5682_io_init(struct device *dev, struct sdw_slave *slave) - /* Mark Slave initialization complete */ - rt5682->hw_init = true; - rt5682->first_hw_init = true; -+ goto out; -+ -+err_sync: -+ regcache_cache_bypass(rt5682->regmap, false); -+ regcache_cache_only(rt5682->sdw_regmap, true); -+ regcache_cache_only(rt5682->regmap, true); -+ regcache_mark_dirty(rt5682->regmap); - - err_nodev: -+out: - pm_runtime_put_autosuspend(&slave->dev); - - dev_dbg(&slave->dev, "%s hw_init complete: %d\n", __func__, ret); -@@ -660,12 +671,11 @@ static int rt5682_interrupt_callback(struct sdw_slave *slave, - dev_dbg(&slave->dev, - "%s control_port_stat=%x", __func__, status->control_port); - -- mutex_lock(&rt5682->disable_irq_lock); -+ guard(mutex)(&rt5682->disable_irq_lock); - if (status->control_port & 0x4 && !rt5682->disable_irq) { - mod_delayed_work(system_power_efficient_wq, - &rt5682->jack_detect_work, msecs_to_jiffies(rt5682->irq_work_delay_time)); - } -- mutex_unlock(&rt5682->disable_irq_lock); - - return 0; - } -@@ -736,11 +746,11 @@ static int rt5682_dev_system_suspend(struct device *dev) - * deferred work completes and before the parent disables - * interrupts on the link - */ -- mutex_lock(&rt5682->disable_irq_lock); -- rt5682->disable_irq = true; -- ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, -- SDW_SCP_INT1_IMPL_DEF, 0); -- mutex_unlock(&rt5682->disable_irq_lock); -+ scoped_guard(mutex, &rt5682->disable_irq_lock) { -+ rt5682->disable_irq = true; -+ ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, -+ SDW_SCP_INT1_IMPL_DEF, 0); -+ } - - if (ret < 0) { - /* log but don't prevent suspend from happening */ -@@ -760,12 +770,12 @@ static int rt5682_dev_resume(struct device *dev) - return 0; - - if (!slave->unattach_request) { -- mutex_lock(&rt5682->disable_irq_lock); -- if (rt5682->disable_irq == true) { -- sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); -- rt5682->disable_irq = false; -+ scoped_guard(mutex, &rt5682->disable_irq_lock) { -+ if (rt5682->disable_irq) { -+ sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); -+ rt5682->disable_irq = false; -+ } - } -- mutex_unlock(&rt5682->disable_irq_lock); - } - - ret = sdw_slave_wait_for_init(slave, RT5682_PROBE_TIMEOUT); -@@ -776,7 +786,13 @@ static int rt5682_dev_resume(struct device *dev) - - regcache_cache_only(rt5682->sdw_regmap, false); - regcache_cache_only(rt5682->regmap, false); -- regcache_sync(rt5682->regmap); -+ ret = regcache_sync(rt5682->regmap); -+ if (ret) { -+ regcache_cache_only(rt5682->sdw_regmap, true); -+ regcache_cache_only(rt5682->regmap, true); -+ regcache_mark_dirty(rt5682->regmap); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c ---- a/sound/soc/codecs/rt5682.c -+++ b/sound/soc/codecs/rt5682.c -@@ -6,6 +6,7 @@ - // Author: Bard Liao - // - -+#include - #include - #include - #include -@@ -3125,7 +3126,7 @@ void rt5682_calibrate(struct rt5682_priv *rt5682) - { - int value, count; - -- mutex_lock(&rt5682->calibrate_mutex); -+ guard(mutex)(&rt5682->calibrate_mutex); - - rt5682_reset(rt5682); - regmap_write(rt5682->regmap, RT5682_I2C_CTRL, 0x000f); -@@ -3175,8 +3176,6 @@ void rt5682_calibrate(struct rt5682_priv *rt5682) - regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x2005); - regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0xc0c4); - regmap_write(rt5682->regmap, RT5682_CAL_REC, 0x0c0c); -- -- mutex_unlock(&rt5682->calibrate_mutex); - } - EXPORT_SYMBOL_GPL(rt5682_calibrate); - -diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c ---- a/sound/soc/codecs/rt5682s.c -+++ b/sound/soc/codecs/rt5682s.c -@@ -6,6 +6,7 @@ - // Author: Derek Fang - // - -+#include - #include - #include - #include -@@ -648,7 +649,7 @@ static void rt5682s_sar_power_mode(struct snd_soc_component *component, int mode - { - struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); - -- mutex_lock(&rt5682s->sar_mutex); -+ guard(mutex)(&rt5682s->sar_mutex); - - switch (mode) { - case SAR_PWR_SAVING: -@@ -695,8 +696,6 @@ static void rt5682s_sar_power_mode(struct snd_soc_component *component, int mode - dev_err(component->dev, "Invalid SAR Power mode: %d\n", mode); - break; - } -- -- mutex_unlock(&rt5682s->sar_mutex); - } - - static void rt5682s_enable_push_button_irq(struct snd_soc_component *component) -@@ -2534,7 +2533,7 @@ static int rt5682s_wclk_prepare(struct clk_hw *hw) - if (!rt5682s_clk_check(rt5682s)) - return -EINVAL; - -- mutex_lock(&rt5682s->wclk_mutex); -+ guard(mutex)(&rt5682s->wclk_mutex); - - snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1, - RT5682S_PWR_VREF2 | RT5682S_PWR_FV2 | RT5682S_PWR_MB, -@@ -2556,8 +2555,6 @@ static int rt5682s_wclk_prepare(struct clk_hw *hw) - - rt5682s->wclk_enabled = 1; - -- mutex_unlock(&rt5682s->wclk_mutex); -- - return 0; - } - -@@ -2570,7 +2567,7 @@ static void rt5682s_wclk_unprepare(struct clk_hw *hw) - if (!rt5682s_clk_check(rt5682s)) - return; - -- mutex_lock(&rt5682s->wclk_mutex); -+ guard(mutex)(&rt5682s->wclk_mutex); - - if (!rt5682s->jack_type) - snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1, -@@ -2585,8 +2582,6 @@ static void rt5682s_wclk_unprepare(struct clk_hw *hw) - rt5682s_set_pllb_power(rt5682s, 0); - - rt5682s->wclk_enabled = 0; -- -- mutex_unlock(&rt5682s->wclk_mutex); - } - - static unsigned long rt5682s_wclk_recalc_rate(struct clk_hw *hw, -@@ -2997,7 +2992,7 @@ static void rt5682s_calibrate(struct rt5682s_priv *rt5682s) - { - unsigned int count, value; - -- mutex_lock(&rt5682s->calibrate_mutex); -+ guard(mutex)(&rt5682s->calibrate_mutex); - - regmap_write(rt5682s->regmap, RT5682S_PWR_ANLG_1, 0xaa80); - usleep_range(15000, 20000); -@@ -3034,8 +3029,6 @@ static void rt5682s_calibrate(struct rt5682s_priv *rt5682s) - regmap_write(rt5682s->regmap, RT5682S_PWR_DIG_1, 0x00c0); - regmap_write(rt5682s->regmap, RT5682S_PWR_ANLG_1, 0x0800); - regmap_write(rt5682s->regmap, RT5682S_GLB_CLK, 0x0000); -- -- mutex_unlock(&rt5682s->calibrate_mutex); - } - - static const struct regmap_config rt5682s_regmap = { -diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c ---- a/sound/soc/codecs/rt700-sdw.c -+++ b/sound/soc/codecs/rt700-sdw.c -@@ -6,6 +6,7 @@ - // - // - -+#include - #include - #include - #include -@@ -415,12 +416,11 @@ static int rt700_interrupt_callback(struct sdw_slave *slave, - dev_dbg(&slave->dev, - "%s control_port_stat=%x", __func__, status->control_port); - -- mutex_lock(&rt700->disable_irq_lock); -+ guard(mutex)(&rt700->disable_irq_lock); - if (status->control_port & 0x4 && !rt700->disable_irq) { - mod_delayed_work(system_power_efficient_wq, - &rt700->jack_detect_work, msecs_to_jiffies(250)); - } -- mutex_unlock(&rt700->disable_irq_lock); - - return 0; - } -@@ -458,10 +458,8 @@ static void rt700_sdw_remove(struct sdw_slave *slave) - { - struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev); - -- if (rt700->hw_init) { -- cancel_delayed_work_sync(&rt700->jack_detect_work); -- cancel_delayed_work_sync(&rt700->jack_btn_check_work); -- } -+ cancel_delayed_work_sync(&rt700->jack_detect_work); -+ cancel_delayed_work_sync(&rt700->jack_btn_check_work); - - pm_runtime_disable(&slave->dev); - } -@@ -501,11 +499,11 @@ static int rt700_dev_system_suspend(struct device *dev) - * deferred work completes and before the parent disables - * interrupts on the link - */ -- mutex_lock(&rt700->disable_irq_lock); -- rt700->disable_irq = true; -- ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, -- SDW_SCP_INT1_IMPL_DEF, 0); -- mutex_unlock(&rt700->disable_irq_lock); -+ scoped_guard(mutex, &rt700->disable_irq_lock) { -+ rt700->disable_irq = true; -+ ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, -+ SDW_SCP_INT1_IMPL_DEF, 0); -+ } - - if (ret < 0) { - /* log but don't prevent suspend from happening */ -diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c ---- a/sound/soc/codecs/rt711-sdca-sdw.c -+++ b/sound/soc/codecs/rt711-sdca-sdw.c -@@ -6,6 +6,7 @@ - // - // - -+#include - #include - #include - #include -@@ -415,13 +416,13 @@ static int rt711_sdca_dev_system_suspend(struct device *dev) - * deferred work completes and before the parent disables - * interrupts on the link - */ -- mutex_lock(&rt711_sdca->disable_irq_lock); -- rt711_sdca->disable_irq = true; -- ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, -- SDW_SCP_SDCA_INTMASK_SDCA_0, 0); -- ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, -- SDW_SCP_SDCA_INTMASK_SDCA_8, 0); -- mutex_unlock(&rt711_sdca->disable_irq_lock); -+ scoped_guard(mutex, &rt711_sdca->disable_irq_lock) { -+ rt711_sdca->disable_irq = true; -+ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, -+ SDW_SCP_SDCA_INTMASK_SDCA_0, 0); -+ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, -+ SDW_SCP_SDCA_INTMASK_SDCA_8, 0); -+ } - - if (ret1 < 0 || ret2 < 0) { - /* log but don't prevent suspend from happening */ -@@ -443,13 +444,15 @@ static int rt711_sdca_dev_resume(struct device *dev) - return 0; - - if (!slave->unattach_request) { -- mutex_lock(&rt711->disable_irq_lock); -- if (rt711->disable_irq == true) { -- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); -- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); -- rt711->disable_irq = false; -+ scoped_guard(mutex, &rt711->disable_irq_lock) { -+ if (rt711->disable_irq) { -+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, -+ SDW_SCP_SDCA_INTMASK_SDCA_0); -+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, -+ SDW_SCP_SDCA_INTMASK_SDCA_8); -+ rt711->disable_irq = false; -+ } - } -- mutex_unlock(&rt711->disable_irq_lock); - } - - ret = sdw_slave_wait_for_init(slave, RT711_PROBE_TIMEOUT); -diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c ---- a/sound/soc/codecs/rt711-sdca.c -+++ b/sound/soc/codecs/rt711-sdca.c -@@ -6,6 +6,7 @@ - // - // - -+#include - #include - #include - #include -@@ -450,7 +451,7 @@ static void rt711_sdca_btn_check_handler(struct work_struct *work) - - static void rt711_sdca_jack_init(struct rt711_sdca_priv *rt711) - { -- mutex_lock(&rt711->calibrate_mutex); -+ guard(mutex)(&rt711->calibrate_mutex); - - if (rt711->hs_jack) { - /* Enable HID1 event & set button RTC mode */ -@@ -515,8 +516,6 @@ static void rt711_sdca_jack_init(struct rt711_sdca_priv *rt711) - - dev_dbg(&rt711->slave->dev, "in %s disable\n", __func__); - } -- -- mutex_unlock(&rt711->calibrate_mutex); - } - - static int rt711_sdca_set_jack_detect(struct snd_soc_component *component, -diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c ---- a/sound/soc/codecs/rt711-sdw.c -+++ b/sound/soc/codecs/rt711-sdw.c -@@ -6,6 +6,7 @@ - // - // - -+#include - #include - #include - #include -@@ -422,12 +423,11 @@ static int rt711_interrupt_callback(struct sdw_slave *slave, - dev_dbg(&slave->dev, - "%s control_port_stat=%x", __func__, status->control_port); - -- mutex_lock(&rt711->disable_irq_lock); -+ guard(mutex)(&rt711->disable_irq_lock); - if (status->control_port & 0x4 && !rt711->disable_irq) { - mod_delayed_work(system_power_efficient_wq, - &rt711->jack_detect_work, msecs_to_jiffies(250)); - } -- mutex_unlock(&rt711->disable_irq_lock); - - return 0; - } -@@ -509,11 +509,11 @@ static int rt711_dev_system_suspend(struct device *dev) - * deferred work completes and before the parent disables - * interrupts on the link - */ -- mutex_lock(&rt711->disable_irq_lock); -- rt711->disable_irq = true; -- ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, -- SDW_SCP_INT1_IMPL_DEF, 0); -- mutex_unlock(&rt711->disable_irq_lock); -+ scoped_guard(mutex, &rt711->disable_irq_lock) { -+ rt711->disable_irq = true; -+ ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, -+ SDW_SCP_INT1_IMPL_DEF, 0); -+ } - - if (ret < 0) { - /* log but don't prevent suspend from happening */ -@@ -535,12 +535,12 @@ static int rt711_dev_resume(struct device *dev) - return 0; - - if (!slave->unattach_request) { -- mutex_lock(&rt711->disable_irq_lock); -- if (rt711->disable_irq == true) { -- sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); -- rt711->disable_irq = false; -+ scoped_guard(mutex, &rt711->disable_irq_lock) { -+ if (rt711->disable_irq) { -+ sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); -+ rt711->disable_irq = false; -+ } - } -- mutex_unlock(&rt711->disable_irq_lock); - } - - ret = sdw_slave_wait_for_init(slave, RT711_PROBE_TIMEOUT); -diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c ---- a/sound/soc/codecs/rt711.c -+++ b/sound/soc/codecs/rt711.c -@@ -6,6 +6,7 @@ - // - // - -+#include - #include - #include - #include -@@ -89,24 +90,24 @@ static int rt711_calibration(struct rt711_priv *rt711) - struct regmap *regmap = rt711->regmap; - int ret = 0; - -- mutex_lock(&rt711->calibrate_mutex); -+ guard(mutex)(&rt711->calibrate_mutex); - regmap_write(rt711->regmap, -- RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); -+ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); - - dev = regmap_get_device(regmap); - - /* Calibration manual mode */ - rt711_index_update_bits(regmap, RT711_VENDOR_REG, RT711_FSM_CTL, -- 0xf, 0x0); -+ 0xf, 0x0); - - /* trigger */ - rt711_index_update_bits(regmap, RT711_VENDOR_CALI, -- RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_CALI_TRIGGER, -- RT711_DAC_DC_CALI_TRIGGER); -+ RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_CALI_TRIGGER, -+ RT711_DAC_DC_CALI_TRIGGER); - - /* wait for calibration process */ - rt711_index_read(regmap, RT711_VENDOR_CALI, -- RT711_DAC_DC_CALI_CTL1, &val); -+ RT711_DAC_DC_CALI_CTL1, &val); - - while (val & RT711_DAC_DC_CALI_TRIGGER) { - if (loop >= 500) { -@@ -119,16 +120,15 @@ static int rt711_calibration(struct rt711_priv *rt711) - - usleep_range(10000, 11000); - rt711_index_read(regmap, RT711_VENDOR_CALI, -- RT711_DAC_DC_CALI_CTL1, &val); -+ RT711_DAC_DC_CALI_CTL1, &val); - } - - /* depop mode */ - rt711_index_update_bits(regmap, RT711_VENDOR_REG, -- RT711_FSM_CTL, 0xf, RT711_DEPOP_CTL); -+ RT711_FSM_CTL, 0xf, RT711_DEPOP_CTL); - - regmap_write(rt711->regmap, -- RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); -- mutex_unlock(&rt711->calibrate_mutex); -+ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); - - dev_dbg(dev, "%s calibration complete, ret=%d\n", __func__, ret); - return ret; -@@ -362,24 +362,24 @@ static void rt711_jack_init(struct rt711_priv *rt711) - { - struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt711->component); - -- mutex_lock(&rt711->calibrate_mutex); -+ guard(mutex)(&rt711->calibrate_mutex); - /* power on */ - if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY) - regmap_write(rt711->regmap, -- RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); -+ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); - - if (rt711->hs_jack) { - /* unsolicited response & IRQ control */ - regmap_write(rt711->regmap, -- RT711_SET_MIC2_UNSOLICITED_ENABLE, 0x82); -+ RT711_SET_MIC2_UNSOLICITED_ENABLE, 0x82); - regmap_write(rt711->regmap, -- RT711_SET_HP_UNSOLICITED_ENABLE, 0x81); -+ RT711_SET_HP_UNSOLICITED_ENABLE, 0x81); - regmap_write(rt711->regmap, -- RT711_SET_INLINE_UNSOLICITED_ENABLE, 0x83); -+ RT711_SET_INLINE_UNSOLICITED_ENABLE, 0x83); - rt711_index_write(rt711->regmap, RT711_VENDOR_REG, -- 0x10, 0x2420); -+ 0x10, 0x2420); - rt711_index_write(rt711->regmap, RT711_VENDOR_REG, -- 0x19, 0x2e11); -+ 0x19, 0x2e11); - - switch (rt711->jd_src) { - case RT711_JD1: -@@ -449,8 +449,7 @@ static void rt711_jack_init(struct rt711_priv *rt711) - /* power off */ - if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY) - regmap_write(rt711->regmap, -- RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); -- mutex_unlock(&rt711->calibrate_mutex); -+ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); - } - - static int rt711_set_jack_detect(struct snd_soc_component *component, -@@ -511,7 +510,7 @@ static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol, - unsigned int read_ll, read_rl; - int i; - -- mutex_lock(&rt711->calibrate_mutex); -+ guard(mutex)(&rt711->calibrate_mutex); - - /* Can't use update bit function, so read the original value first */ - addr_h = mc->reg; -@@ -599,7 +598,6 @@ static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol, - regmap_write(rt711->regmap, - RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); - -- mutex_unlock(&rt711->calibrate_mutex); - return 0; - } - -@@ -908,11 +906,11 @@ static int rt711_set_bias_level(struct snd_soc_component *component, - break; - - case SND_SOC_BIAS_STANDBY: -- mutex_lock(&rt711->calibrate_mutex); -- regmap_write(rt711->regmap, -- RT711_SET_AUDIO_POWER_STATE, -- AC_PWRST_D3); -- mutex_unlock(&rt711->calibrate_mutex); -+ scoped_guard(mutex, &rt711->calibrate_mutex) { -+ regmap_write(rt711->regmap, -+ RT711_SET_AUDIO_POWER_STATE, -+ AC_PWRST_D3); -+ } - break; - - default: -diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c ---- a/sound/soc/codecs/rt712-sdca-dmic.c -+++ b/sound/soc/codecs/rt712-sdca-dmic.c -@@ -916,10 +916,23 @@ static int rt712_sdca_dmic_dev_resume(struct device *dev) - } - - regcache_cache_only(rt712->regmap, false); -- regcache_sync(rt712->regmap); -+ ret = regcache_sync(rt712->regmap); -+ if (ret) -+ goto err_sync; -+ - regcache_cache_only(rt712->mbq_regmap, false); -- regcache_sync(rt712->mbq_regmap); -+ ret = regcache_sync(rt712->mbq_regmap); -+ if (ret) -+ goto err_sync; -+ - return 0; -+ -+err_sync: -+ regcache_cache_only(rt712->regmap, true); -+ regcache_cache_only(rt712->mbq_regmap, true); -+ regcache_mark_dirty(rt712->regmap); -+ regcache_mark_dirty(rt712->mbq_regmap); -+ return ret; - } - - static const struct dev_pm_ops rt712_sdca_dmic_pm = { -diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c ---- a/sound/soc/codecs/rt712-sdca-sdw.c -+++ b/sound/soc/codecs/rt712-sdca-sdw.c -@@ -6,6 +6,7 @@ - // - // - -+#include - #include - #include - #include -@@ -427,13 +428,13 @@ static int rt712_sdca_dev_system_suspend(struct device *dev) - * deferred work completes and before the parent disables - * interrupts on the link - */ -- mutex_lock(&rt712_sdca->disable_irq_lock); -- rt712_sdca->disable_irq = true; -- ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, -- SDW_SCP_SDCA_INTMASK_SDCA_0, 0); -- ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, -- SDW_SCP_SDCA_INTMASK_SDCA_8, 0); -- mutex_unlock(&rt712_sdca->disable_irq_lock); -+ scoped_guard(mutex, &rt712_sdca->disable_irq_lock) { -+ rt712_sdca->disable_irq = true; -+ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, -+ SDW_SCP_SDCA_INTMASK_SDCA_0, 0); -+ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, -+ SDW_SCP_SDCA_INTMASK_SDCA_8, 0); -+ } - - if (ret1 < 0 || ret2 < 0) { - /* log but don't prevent suspend from happening */ -@@ -455,14 +456,15 @@ static int rt712_sdca_dev_resume(struct device *dev) - return 0; - - if (!slave->unattach_request) { -- mutex_lock(&rt712->disable_irq_lock); -- if (rt712->disable_irq == true) { -- -- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); -- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); -- rt712->disable_irq = false; -+ scoped_guard(mutex, &rt712->disable_irq_lock) { -+ if (rt712->disable_irq) { -+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, -+ SDW_SCP_SDCA_INTMASK_SDCA_0); -+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, -+ SDW_SCP_SDCA_INTMASK_SDCA_8); -+ rt712->disable_irq = false; -+ } - } -- mutex_unlock(&rt712->disable_irq_lock); - } - - ret = sdw_slave_wait_for_init(slave, RT712_PROBE_TIMEOUT); -@@ -472,9 +474,20 @@ static int rt712_sdca_dev_resume(struct device *dev) - } - - regcache_cache_only(rt712->regmap, false); -- regcache_sync(rt712->regmap); -+ ret = regcache_sync(rt712->regmap); -+ if (ret) { -+ regcache_cache_only(rt712->regmap, true); -+ return ret; -+ } -+ - regcache_cache_only(rt712->mbq_regmap, false); -- regcache_sync(rt712->mbq_regmap); -+ ret = regcache_sync(rt712->mbq_regmap); -+ if (ret) { -+ regcache_cache_only(rt712->mbq_regmap, true); -+ regcache_cache_only(rt712->regmap, true); -+ return ret; -+ } -+ - return 0; - } - -diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c ---- a/sound/soc/codecs/rt712-sdca.c -+++ b/sound/soc/codecs/rt712-sdca.c -@@ -7,6 +7,7 @@ - // - - #include -+#include - #include - #include - #include -@@ -403,7 +404,7 @@ static void rt712_sdca_btn_check_handler(struct work_struct *work) - - static void rt712_sdca_jack_init(struct rt712_sdca_priv *rt712) - { -- mutex_lock(&rt712->calibrate_mutex); -+ guard(mutex)(&rt712->calibrate_mutex); - - if (rt712->hs_jack) { - /* Enable HID1 event & set button RTC mode */ -@@ -450,8 +451,6 @@ static void rt712_sdca_jack_init(struct rt712_sdca_priv *rt712) - - dev_dbg(&rt712->slave->dev, "in %s disable\n", __func__); - } -- -- mutex_unlock(&rt712->calibrate_mutex); - } - - static int rt712_sdca_set_jack_detect(struct snd_soc_component *component, -diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c ---- a/sound/soc/codecs/rt721-sdca-sdw.c -+++ b/sound/soc/codecs/rt721-sdca-sdw.c -@@ -6,6 +6,7 @@ - // - // - -+#include - #include - #include - #include -@@ -466,13 +467,13 @@ static int rt721_sdca_dev_system_suspend(struct device *dev) - * deferred work completes and before the parent disables - * interrupts on the link - */ -- mutex_lock(&rt721_sdca->disable_irq_lock); -- rt721_sdca->disable_irq = true; -- ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, -- SDW_SCP_SDCA_INTMASK_SDCA_0, 0); -- ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, -- SDW_SCP_SDCA_INTMASK_SDCA_8, 0); -- mutex_unlock(&rt721_sdca->disable_irq_lock); -+ scoped_guard(mutex, &rt721_sdca->disable_irq_lock) { -+ rt721_sdca->disable_irq = true; -+ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, -+ SDW_SCP_SDCA_INTMASK_SDCA_0, 0); -+ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, -+ SDW_SCP_SDCA_INTMASK_SDCA_8, 0); -+ } - - if (ret1 < 0 || ret2 < 0) { - /* log but don't prevent suspend from happening */ -@@ -494,13 +495,15 @@ static int rt721_sdca_dev_resume(struct device *dev) - return 0; - - if (!slave->unattach_request) { -- mutex_lock(&rt721->disable_irq_lock); -- if (rt721->disable_irq == true) { -- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); -- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); -- rt721->disable_irq = false; -+ scoped_guard(mutex, &rt721->disable_irq_lock) { -+ if (rt721->disable_irq) { -+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, -+ SDW_SCP_SDCA_INTMASK_SDCA_0); -+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, -+ SDW_SCP_SDCA_INTMASK_SDCA_8); -+ rt721->disable_irq = false; -+ } - } -- mutex_unlock(&rt721->disable_irq_lock); - } - - ret = sdw_slave_wait_for_init(slave, RT721_PROBE_TIMEOUT); -@@ -510,9 +513,20 @@ static int rt721_sdca_dev_resume(struct device *dev) - } - - regcache_cache_only(rt721->regmap, false); -- regcache_sync(rt721->regmap); -+ ret = regcache_sync(rt721->regmap); -+ if (ret) { -+ regcache_cache_only(rt721->regmap, true); -+ return ret; -+ } -+ - regcache_cache_only(rt721->mbq_regmap, false); -- regcache_sync(rt721->mbq_regmap); -+ ret = regcache_sync(rt721->mbq_regmap); -+ if (ret) { -+ regcache_cache_only(rt721->mbq_regmap, true); -+ regcache_cache_only(rt721->regmap, true); -+ return ret; -+ } -+ - return 0; - } - -diff --git a/sound/soc/codecs/rt721-sdca.c b/sound/soc/codecs/rt721-sdca.c ---- a/sound/soc/codecs/rt721-sdca.c -+++ b/sound/soc/codecs/rt721-sdca.c -@@ -5,7 +5,7 @@ - // Copyright(c) 2024 Realtek Semiconductor Corp. - // - // -- -+#include - #include - #include - #include -@@ -289,7 +289,7 @@ static void rt721_sdca_jack_preset(struct rt721_sdca_priv *rt721) - - static void rt721_sdca_jack_init(struct rt721_sdca_priv *rt721) - { -- mutex_lock(&rt721->calibrate_mutex); -+ guard(mutex)(&rt721->calibrate_mutex); - if (rt721->hs_jack) { - sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INTMASK1, - SDW_SCP_SDCA_INTMASK_SDCA_0); -@@ -309,7 +309,6 @@ static void rt721_sdca_jack_init(struct rt721_sdca_priv *rt721) - rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT, - RT721_GE_REL_CTRL1, 0x4000, 0x4000); - } -- mutex_unlock(&rt721->calibrate_mutex); - } - - static int rt721_sdca_set_jack_detect(struct snd_soc_component *component, -diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c ---- a/sound/soc/codecs/rt722-sdca-sdw.c -+++ b/sound/soc/codecs/rt722-sdca-sdw.c -@@ -6,6 +6,7 @@ - // - // - -+#include - #include - #include - #include -@@ -517,13 +518,13 @@ static int rt722_sdca_dev_system_suspend(struct device *dev) - * deferred work completes and before the parent disables - * interrupts on the link - */ -- mutex_lock(&rt722_sdca->disable_irq_lock); -- rt722_sdca->disable_irq = true; -- ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, -- SDW_SCP_SDCA_INTMASK_SDCA_0, 0); -- ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, -- SDW_SCP_SDCA_INTMASK_SDCA_8, 0); -- mutex_unlock(&rt722_sdca->disable_irq_lock); -+ scoped_guard(mutex, &rt722_sdca->disable_irq_lock) { -+ rt722_sdca->disable_irq = true; -+ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, -+ SDW_SCP_SDCA_INTMASK_SDCA_0, 0); -+ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, -+ SDW_SCP_SDCA_INTMASK_SDCA_8, 0); -+ } - - if (ret1 < 0 || ret2 < 0) { - /* log but don't prevent suspend from happening */ -@@ -545,13 +546,15 @@ static int rt722_sdca_dev_resume(struct device *dev) - return 0; - - if (!slave->unattach_request) { -- mutex_lock(&rt722->disable_irq_lock); -- if (rt722->disable_irq == true) { -- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); -- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); -- rt722->disable_irq = false; -+ scoped_guard(mutex, &rt722->disable_irq_lock) { -+ if (rt722->disable_irq) { -+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, -+ SDW_SCP_SDCA_INTMASK_SDCA_0); -+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, -+ SDW_SCP_SDCA_INTMASK_SDCA_8); -+ rt722->disable_irq = false; -+ } - } -- mutex_unlock(&rt722->disable_irq_lock); - } - - ret = sdw_slave_wait_for_init(slave, RT722_PROBE_TIMEOUT); -@@ -561,7 +564,13 @@ static int rt722_sdca_dev_resume(struct device *dev) - } - - regcache_cache_only(rt722->regmap, false); -- regcache_sync(rt722->regmap); -+ ret = regcache_sync(rt722->regmap); -+ if (ret) { -+ regcache_cache_only(rt722->regmap, true); -+ regcache_mark_dirty(rt722->regmap); -+ return ret; -+ } -+ - return 0; - } - -diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c ---- a/sound/soc/codecs/rt722-sdca.c -+++ b/sound/soc/codecs/rt722-sdca.c -@@ -6,6 +6,7 @@ - // - // - -+#include - #include - #include - #include -@@ -294,7 +295,7 @@ static void rt722_sdca_btn_check_handler(struct work_struct *work) - - static void rt722_sdca_jack_init(struct rt722_sdca_priv *rt722) - { -- mutex_lock(&rt722->calibrate_mutex); -+ guard(mutex)(&rt722->calibrate_mutex); - if (rt722->hs_jack) { - /* set SCP_SDCA_IntMask1[0]=1 */ - sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK1, -@@ -317,7 +318,6 @@ static void rt722_sdca_jack_init(struct rt722_sdca_priv *rt722) - rt722_sdca_index_update_bits(rt722, RT722_VENDOR_HDA_CTL, - RT722_GE_RELATED_CTL2, 0x4000, 0x4000); - } -- mutex_unlock(&rt722->calibrate_mutex); - } - - static int rt722_sdca_set_jack_detect(struct snd_soc_component *component, -@@ -352,8 +352,6 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) - static const char func_tag[] = "FUNC"; - static const char xu_tag[] = "XU"; - const char *dmi_vendor, *dmi_product, *dmi_sku; -- char *cae_filename; -- const struct firmware *cae_fw = NULL; - unsigned int cae_st_spk, cae_st_hp, cae_st_mic; - unsigned int func, value; - unsigned int combined_val; -@@ -385,7 +383,8 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) - space = strchr(dmi_sku, ' '); - s_len = space ? space - dmi_sku : strlen(dmi_sku); - -- cae_filename = kasprintf(GFP_KERNEL, -+ char *cae_filename __free(kfree) = -+ kasprintf(GFP_KERNEL, - "realtek/rt722/rt722_RAE_%.*s_%.*s_%.*s.dat", - v_len, dmi_vendor, - p_len, dmi_product, -@@ -399,8 +398,8 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) - regmap_write(rt722->regmap, RT722_MIC_CAE_PARAM39, 0x5f); - usleep_range(50000, 60000); - -+ const struct firmware *cae_fw __free(firmware) = NULL; - request_firmware(&cae_fw, cae_filename, dev); -- kfree(cae_filename); - if (!cae_fw) { - dev_err(dev, "%s: Failed to load CAE firmware\n", __func__); - return -ENOENT; -@@ -555,7 +554,6 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) - regcache_cache_bypass(rt722->regmap, false); - rt722->cae_update_done = 1; - dev_dbg(dev, "%s: CAE FW update done.\n", __func__); -- release_firmware(cae_fw); - return 0; - - verify_abort: -@@ -565,7 +563,6 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) - out_release: - rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG, - RT722_MISC_CTRL1, 0x8000, 0x0000); -- release_firmware(cae_fw); - dev_err(dev, "%s: CAE FW update aborted (ret=%d).\n", __func__, ret); - return ret; - } -@@ -1906,6 +1903,9 @@ int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave) - rt722_sdca_amp_preset(rt722); - rt722_sdca_jack_preset(rt722); - -+ if (rt722->hs_jack && (!rt722->first_hw_init)) -+ rt722_sdca_jack_init(rt722); -+ - if (rt722->first_hw_init) { - regcache_cache_bypass(rt722->regmap, false); - regcache_mark_dirty(rt722->regmap); -diff --git a/sound/soc/codecs/rt766-sdca-sdw.c b/sound/soc/codecs/rt766-sdca-sdw.c -new file mode 100644 ---- /dev/null -+++ b/sound/soc/codecs/rt766-sdca-sdw.c -@@ -0,0 +1,339 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+// -+// rt766-sdca-sdw.c -- rt766 SDCA ALSA SoC audio driver -+// -+// Copyright(c) 2026 Realtek Semiconductor Corp. -+// -+// -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "rt766-sdca.h" -+#include "rt766-sdca-sdw.h" -+ -+#define RT766_PROBE_TIMEOUT 5000 -+ -+static bool rt766_sdca_readable_register(struct device *dev, unsigned int reg) -+{ -+ switch (reg) { -+ case SDW_SCP_SDCA_INT1 ... SDW_SCP_SDCA_INTMASK4: -+ case RT766_VERSION_ID ... RT766_BOND_LATCH_ID: -+ case 0xc344 ... 0xc345: -+ case 0xc900: -+ case 0xc920: -+ case 0xd540 ... 0xd542: -+ case 0xf01e: -+ case RT766_HP_POWER_STATE ... RT766_HP_FSM_CTL2_1: -+ case 0x310100: -+ case RT766_MCU_PATCH_ADDR1_START ... RT766_MCU_PATCH_ADDR1_END: -+ case RT766_MCU_PATCH_ADDR2_START ... RT766_MCU_PATCH_ADDR2_END: -+ case RT766_MUTE_REG(UAJ, USER_FU41, 1): -+ case RT766_MUTE_REG(UAJ, USER_FU41, 2): -+ case RT766_VOLUME_REG(UAJ, USER_FU41, 1): -+ case RT766_VOLUME_REG(UAJ, USER_FU41, 2): -+ case RT766_MUTE_REG(UAJ, USER_FU36, 1): -+ case RT766_MUTE_REG(UAJ, USER_FU36, 2): -+ case RT766_VOLUME_REG(UAJ, USER_FU36, 1): -+ case RT766_VOLUME_REG(UAJ, USER_FU36, 2): -+ case RT766_PDE_REQ_REG(UAJ, PDE47): -+ case RT766_PDE_REQ_REG(UAJ, PDE34): -+ case RT766_SDCA_CTL(UAJ, CS41, SDCA_CTL_CS_SAMPLERATEINDEX): -+ case RT766_SDCA_CTL(UAJ, CS36, SDCA_CTL_CS_SAMPLERATEINDEX): -+ case RT766_FUNC_STATUS_REG(UAJ): /* 0x40480000 */ -+ case RT766_PDE_ACTUAL_REG(UAJ, PDE47): /* 0x40481400 */ -+ case RT766_PDE_ACTUAL_REG(UAJ, PDE34): /* 0x40481480 */ -+ case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1): -+ case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2): -+ case RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_SELECTED_MODE): -+ case RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_DETECTED_MODE): /* 0x40600490 */ -+ case RT766_PDE_REQ_REG(MIC, PDE11): -+ case RT766_MUTE_REG(MIC, USER_FU113, 1): -+ case RT766_MUTE_REG(MIC, USER_FU113, 2): -+ case RT766_MUTE_REG(MIC, USER_FU113, 3): -+ case RT766_MUTE_REG(MIC, USER_FU113, 4): -+ case RT766_VOLUME_REG(MIC, USER_FU113, 1): -+ case RT766_VOLUME_REG(MIC, USER_FU113, 2): -+ case RT766_VOLUME_REG(MIC, USER_FU113, 3): -+ case RT766_VOLUME_REG(MIC, USER_FU113, 4): -+ case RT766_FUNC_STATUS_REG(MIC): /* 0x40880000 */ -+ case RT766_SDCA_CTL(MIC, CS113, SDCA_CTL_CS_SAMPLERATEINDEX): -+ case RT766_PDE_ACTUAL_REG(MIC, PDE11): /* 0x40881500 */ -+ case RT766_FUNC_STATUS_REG(HID): /* 0x40c80000 */ -+ /* 0x40c80080 - 0x40c80098 */ -+ case RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_CURRENTOWNER) ... -+ RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_MESSAGELENGTH): -+ case RT766_MUTE_REG(AMP, USER_FU21, 1): -+ case RT766_MUTE_REG(AMP, USER_FU21, 2): -+ case RT766_VOLUME_REG(AMP, USER_FU21, 1): -+ case RT766_VOLUME_REG(AMP, USER_FU21, 2): -+ case RT766_PDE_REQ_REG(AMP, PDE23): -+ case RT766_FUNC_STATUS_REG(AMP): /* 0x41080000 */ -+ case RT766_SDCA_CTL(AMP, PPU21, SDCA_CTL_PPU_POSTURENUMBER): -+ case RT766_SDCA_CTL(AMP, CS21, SDCA_CTL_CS_SAMPLERATEINDEX): -+ case RT766_PDE_ACTUAL_REG(AMP, PDE23): /* 0x41081980 */ -+ case RT766_BUF_ADDR_HID1 ... RT766_BUF_ADDR_HID2: -+ return true; -+ default: -+ return false; -+ } -+} -+ -+static bool rt766_sdca_volatile_register(struct device *dev, unsigned int reg) -+{ -+ switch (reg) { -+ case SDW_SCP_SDCA_INT1 ... SDW_SCP_SDCA_INTMASK4: -+ case RT766_VERSION_ID ... RT766_BOND_LATCH_ID: -+ case 0xc344 ... 0xc345: -+ case 0xc900: -+ case 0xc920: -+ case 0xd540 ... 0xd542: -+ case 0xf01e: -+ case RT766_HP_POWER_STATE ... RT766_HP_FSM_CTL2_1: -+ case 0x310100: -+ case RT766_MCU_PATCH_ADDR1_START ... RT766_MCU_PATCH_ADDR1_END: -+ case RT766_MCU_PATCH_ADDR2_START ... RT766_MCU_PATCH_ADDR2_END: -+ case RT766_FUNC_STATUS_REG(UAJ): -+ case RT766_PDE_ACTUAL_REG(UAJ, PDE47): -+ case RT766_PDE_ACTUAL_REG(UAJ, PDE34): -+ case RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_DETECTED_MODE): -+ case RT766_FUNC_STATUS_REG(MIC): -+ case RT766_PDE_ACTUAL_REG(MIC, PDE11): -+ case RT766_FUNC_STATUS_REG(HID): -+ case RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_CURRENTOWNER) ... -+ RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_MESSAGELENGTH): -+ case RT766_FUNC_STATUS_REG(AMP): -+ case RT766_PDE_ACTUAL_REG(AMP, PDE23): -+ case RT766_BUF_ADDR_HID1 ... RT766_BUF_ADDR_HID2: -+ return true; -+ default: -+ return false; -+ } -+} -+ -+static int rt766_sdca_mbq_size(struct device *dev, unsigned int reg) -+{ -+ switch (reg) { -+ case RT766_VOLUME_REG(UAJ, USER_FU41, 1): -+ case RT766_VOLUME_REG(UAJ, USER_FU41, 2): -+ case RT766_VOLUME_REG(UAJ, USER_FU36, 1): -+ case RT766_VOLUME_REG(UAJ, USER_FU36, 2): -+ case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1): -+ case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2): -+ case RT766_VOLUME_REG(MIC, USER_FU113, 1): -+ case RT766_VOLUME_REG(MIC, USER_FU113, 2): -+ case RT766_VOLUME_REG(MIC, USER_FU113, 3): -+ case RT766_VOLUME_REG(MIC, USER_FU113, 4): -+ case RT766_VOLUME_REG(AMP, USER_FU21, 1): -+ case RT766_VOLUME_REG(AMP, USER_FU21, 2): -+ return 2; -+ default: -+ return 1; -+ } -+} -+ -+static const struct regmap_sdw_mbq_cfg rt766_sdca_mbq_cfg = { -+ .mbq_size = rt766_sdca_mbq_size, -+}; -+ -+static const struct regmap_config rt766_sdca_regmap = { -+ .reg_bits = 32, -+ .val_bits = 16, -+ .readable_reg = rt766_sdca_readable_register, -+ .volatile_reg = rt766_sdca_volatile_register, -+ .reg_defaults = rt766_sdca_defaults, -+ .num_reg_defaults = ARRAY_SIZE(rt766_sdca_defaults), -+ .max_register = SDW_SDCA_MAX_REGISTER, -+ .cache_type = REGCACHE_MAPLE, -+ .use_single_read = true, -+ .use_single_write = true, -+}; -+ -+static int rt766_sdca_update_status(struct sdw_slave *slave, -+ enum sdw_slave_status status) -+{ -+ struct rt766_sdca_priv *rt766 = dev_get_drvdata(&slave->dev); -+ -+ if (status == SDW_SLAVE_UNATTACHED) -+ rt766->hw_init = false; -+ -+ if (status == SDW_SLAVE_ATTACHED) { -+ if (rt766->hs_jack) { -+ /* -+ * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then -+ * if the device attached again, we will need to set the setting back. -+ * It could avoid losing the jack detection interrupt. -+ * This also could sync with the cache value as the rt766_sdca_jack_init set. -+ */ -+ sdw_write_no_pm(rt766->slave, SDW_SCP_SDCA_INTMASK3, -+ SDW_SCP_SDCA_INTMASK_SDCA_16); -+ sdw_write_no_pm(rt766->slave, SDW_SCP_SDCA_INTMASK4, -+ SDW_SCP_SDCA_INTMASK_SDCA_24); -+ } -+ } -+ -+ /* -+ * Perform initialization only if slave status is present and -+ * hw_init flag is false -+ */ -+ if (rt766->hw_init || status != SDW_SLAVE_ATTACHED) -+ return 0; -+ -+ /* perform I/O transfers required for Slave initialization */ -+ return rt766_sdca_io_init(&slave->dev, slave); -+} -+ -+static int rt766_sdca_read_prop(struct sdw_slave *slave) -+{ -+ struct sdw_slave_prop *prop = &slave->prop; -+ int ret; -+ -+ ret = sdw_slave_read_prop(slave); -+ if (ret < 0) -+ return ret; -+ -+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; -+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; -+ /* -+ * SDCA interrupts are routed through SoundWire domain IRQ. -+ */ -+ prop->use_domain_irq = true; -+ -+ return 0; -+} -+ -+static const struct sdw_slave_ops rt766_sdca_slave_ops = { -+ .read_prop = rt766_sdca_read_prop, -+ .update_status = rt766_sdca_update_status, -+}; -+ -+static int rt766_sdca_sdw_probe(struct sdw_slave *slave, -+ const struct sdw_device_id *id) -+{ -+ struct regmap *regmap; -+ -+ /* Regmap Initialization */ -+ regmap = devm_regmap_init_sdw_mbq_cfg(&slave->dev, slave, -+ &rt766_sdca_regmap, &rt766_sdca_mbq_cfg); -+ if (IS_ERR(regmap)) -+ return PTR_ERR(regmap); -+ -+ return rt766_sdca_init(&slave->dev, regmap, slave); -+} -+ -+static void rt766_sdca_sdw_remove(struct sdw_slave *slave) -+{ -+ pm_runtime_disable(&slave->dev); -+} -+ -+static const struct sdw_device_id rt766_sdca_id[] = { -+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x766, 0x3, 0x1, 0), -+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x767, 0x3, 0x1, 0), -+ {}, -+}; -+MODULE_DEVICE_TABLE(sdw, rt766_sdca_id); -+ -+static int rt766_sdca_dev_suspend(struct device *dev) -+{ -+ struct rt766_sdca_priv *rt766 = dev_get_drvdata(dev); -+ -+ if (!rt766->hw_init) -+ return 0; -+ -+ regcache_cache_only(rt766->regmap, true); -+ return 0; -+} -+ -+static int rt766_sdca_dev_system_suspend(struct device *dev) -+{ -+ struct rt766_sdca_priv *rt766 = dev_get_drvdata(dev); -+ struct sdw_slave *slave = dev_to_sdw_dev(dev); -+ int ret1, ret2; -+ -+ if (!rt766->hw_init) -+ return 0; -+ -+ /* -+ * prevent new interrupts from being handled after the -+ * deferred work completes and before the parent disables -+ * interrupts on the link -+ */ -+ mutex_lock(&rt766->disable_irq_lock); -+ rt766->disable_irq = true; -+ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK3, -+ SDW_SCP_SDCA_INTMASK_SDCA_16, 0); -+ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK4, -+ SDW_SCP_SDCA_INTMASK_SDCA_24, 0); -+ mutex_unlock(&rt766->disable_irq_lock); -+ -+ if (ret1 < 0 || ret2 < 0) { -+ /* log but don't prevent suspend from happening */ -+ dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__); -+ } -+ -+ return rt766_sdca_dev_suspend(dev); -+} -+ -+static int rt766_sdca_dev_resume(struct device *dev) -+{ -+ struct sdw_slave *slave = dev_to_sdw_dev(dev); -+ struct rt766_sdca_priv *rt766 = dev_get_drvdata(dev); -+ int ret; -+ -+ if (!rt766->first_hw_init) -+ return 0; -+ -+ if (!slave->unattach_request) { -+ mutex_lock(&rt766->disable_irq_lock); -+ if (rt766->disable_irq == true) { -+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK3, SDW_SCP_SDCA_INTMASK_SDCA_16); -+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK4, SDW_SCP_SDCA_INTMASK_SDCA_24); -+ rt766->disable_irq = false; -+ } -+ mutex_unlock(&rt766->disable_irq_lock); -+ goto regmap_sync; -+ } -+ -+ ret = sdw_slave_wait_for_init(slave, RT766_PROBE_TIMEOUT); -+ if (ret) { -+ sdw_show_ping_status(slave->bus, true); -+ return ret; -+ } -+ -+regmap_sync: -+ regcache_cache_only(rt766->regmap, false); -+ ret = regcache_sync(rt766->regmap); -+ if (ret) { -+ regcache_cache_only(rt766->regmap, true); -+ regcache_mark_dirty(rt766->regmap); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static const struct dev_pm_ops rt766_sdca_pm = { -+ SYSTEM_SLEEP_PM_OPS(rt766_sdca_dev_system_suspend, rt766_sdca_dev_resume) -+ RUNTIME_PM_OPS(rt766_sdca_dev_suspend, rt766_sdca_dev_resume, NULL) -+}; -+ -+static struct sdw_driver rt766_sdca_sdw_driver = { -+ .driver = { -+ .name = "rt766-sdca", -+ .pm = pm_ptr(&rt766_sdca_pm), -+ }, -+ .probe = rt766_sdca_sdw_probe, -+ .remove = rt766_sdca_sdw_remove, -+ .ops = &rt766_sdca_slave_ops, -+ .id_table = rt766_sdca_id, -+}; -+module_sdw_driver(rt766_sdca_sdw_driver); -+ -+MODULE_DESCRIPTION("ASoC RT766 SDCA SDW driver"); -+MODULE_AUTHOR("Shuming Fan "); -+MODULE_LICENSE("GPL"); -+MODULE_IMPORT_NS("SND_SOC_SDCA"); -diff --git a/sound/soc/codecs/rt766-sdca-sdw.h b/sound/soc/codecs/rt766-sdca-sdw.h -new file mode 100644 ---- /dev/null -+++ b/sound/soc/codecs/rt766-sdca-sdw.h -@@ -0,0 +1,61 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * rt766-sdca-sdw.h -- RT766 SDCA ALSA SoC audio driver header -+ * -+ * Copyright(c) 2026 Realtek Semiconductor Corp. -+ */ -+ -+#ifndef __RT766_SDW_H__ -+#define __RT766_SDW_H__ -+ -+#include -+#include -+ -+static const struct reg_default rt766_sdca_defaults[] = { -+ /* 0x40400289 - 0x4040028a */ -+ { RT766_MUTE_REG(UAJ, USER_FU41, 1), 0x01 }, -+ { RT766_MUTE_REG(UAJ, USER_FU41, 2), 0x01 }, -+ /* 0x40400291 - 0x40400292 */ -+ { RT766_VOLUME_REG(UAJ, USER_FU41, 1), 0x0000 }, -+ { RT766_VOLUME_REG(UAJ, USER_FU41, 2), 0x0000 }, -+ /* 0x40400789 - 0x4040078a */ -+ { RT766_MUTE_REG(UAJ, USER_FU36, 1), 0x01 }, -+ { RT766_MUTE_REG(UAJ, USER_FU36, 2), 0x01 }, -+ /* 0x40400791 - 0x40400792 */ -+ { RT766_VOLUME_REG(UAJ, USER_FU36, 1), 0x0000 }, -+ { RT766_VOLUME_REG(UAJ, USER_FU36, 2), 0x0000 }, -+ { RT766_PDE_REQ_REG(UAJ, PDE47), 0x03 }, /* 0x40401408 */ -+ { RT766_PDE_REQ_REG(UAJ, PDE34), 0x03 }, /* 0x40401488 */ -+ { RT766_SDCA_CTL(UAJ, CS41, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 }, /* 0x40480080 */ -+ { RT766_SDCA_CTL(UAJ, CS36, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 }, /* 0x40480880 */ -+ /* 0x40600259 - 0x4060025a */ -+ { RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1), 0xfe00 }, -+ { RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2), 0xfe00 }, -+ { RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_SELECTED_MODE), 0x00 }, /* 0x40600488 */ -+ -+ { RT766_PDE_REQ_REG(MIC, PDE11), 0x03 }, /* 0x40801508 */ -+ /* 0x40801809 - 0x4080180c */ -+ { RT766_MUTE_REG(MIC, USER_FU113, 1), 0x01 }, -+ { RT766_MUTE_REG(MIC, USER_FU113, 2), 0x01 }, -+ { RT766_MUTE_REG(MIC, USER_FU113, 3), 0x01 }, -+ { RT766_MUTE_REG(MIC, USER_FU113, 4), 0x01 }, -+ /* 0x40801811 - 0x40801814 */ -+ { RT766_VOLUME_REG(MIC, USER_FU113, 1), 0x0000 }, -+ { RT766_VOLUME_REG(MIC, USER_FU113, 2), 0x0000 }, -+ { RT766_VOLUME_REG(MIC, USER_FU113, 3), 0x0000 }, -+ { RT766_VOLUME_REG(MIC, USER_FU113, 4), 0x0000 }, -+ { RT766_SDCA_CTL(MIC, CS113, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 }, /* 0x40880900 */ -+ -+ /* 0x41000189 - 0x4100018a */ -+ { RT766_MUTE_REG(AMP, USER_FU21, 1), 0x01 }, -+ { RT766_MUTE_REG(AMP, USER_FU21, 2), 0x01 }, -+ /* 0x41000191 - 0x41000192 */ -+ { RT766_VOLUME_REG(AMP, USER_FU21, 1), 0x0000 }, -+ { RT766_VOLUME_REG(AMP, USER_FU21, 2), 0x0000 }, -+ { RT766_PDE_REQ_REG(AMP, PDE23), 0x03 }, /* 0x41001988 */ -+ { RT766_SDCA_CTL(AMP, PPU21, SDCA_CTL_PPU_POSTURENUMBER), 0x00 }, /* 0x41080200 */ -+ { RT766_SDCA_CTL(AMP, CS21, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 }, /* 0x41081080 */ -+ -+}; -+ -+#endif /* __RT766_SDW_H__ */ -diff --git a/sound/soc/codecs/rt766-sdca.c b/sound/soc/codecs/rt766-sdca.c -new file mode 100644 ---- /dev/null -+++ b/sound/soc/codecs/rt766-sdca.c -@@ -0,0 +1,1378 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+// -+// rt766-sdca.c -- rt766 SDCA ALSA SoC audio driver -+// -+// Copyright(c) 2026 Realtek Semiconductor Corp. -+// -+// -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "rt766-sdca.h" -+#include "rt-sdw-common.h" -+ -+static int rt766_sdca_btn_detect(struct sdca_interrupt *interrupt) -+{ -+ struct rt766_sdca_priv *rt766 = interrupt->priv; -+ unsigned char *buf = NULL; -+ unsigned int offset, owner, length; -+ unsigned int det_mode, idx, val; -+ int ret; -+ -+ ret = regmap_read(rt766->regmap, -+ RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_DETECTED_MODE), -+ &det_mode); -+ if (ret < 0) -+ goto io_error; -+ -+ /* get current UMP message owner */ -+ ret = regmap_read(rt766->regmap, -+ RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_CURRENTOWNER), -+ &owner); -+ if (ret < 0) -+ goto io_error; -+ -+ /* if owner is device then there is no button event from device */ -+ if (owner == 1) -+ return 0; -+ -+ if (det_mode) { -+ /* read UMP message length */ -+ ret = regmap_read(rt766->regmap, -+ RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_MESSAGELENGTH), -+ &length); -+ if (ret < 0) -+ goto _end_btn_det_; -+ -+ /* read UMP message offset */ -+ ret = regmap_read(rt766->regmap, -+ RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_MESSAGEOFFSET), -+ &offset); -+ if (ret < 0) -+ goto _end_btn_det_; -+ -+ buf = devm_kzalloc(&rt766->slave->dev, length, GFP_KERNEL); -+ if (!buf) { -+ dev_err(&rt766->slave->dev, "%s: alloc buf failed\n", __func__); -+ goto _end_btn_det_; -+ } -+ -+ for (idx = 0; idx < length; idx++) { -+ ret = regmap_read(rt766->regmap, -+ RT766_BUF_ADDR_HID1 + offset + idx, &val); -+ if (ret < 0) -+ goto _end_btn_det_; -+ buf[idx] = val & 0xff; -+ } -+ -+ if (rt766->hid) -+ hid_input_report(rt766->hid, HID_INPUT_REPORT, -+ buf, length, 1); -+ } -+ -+_end_btn_det_: -+ if (buf) -+ devm_kfree(&rt766->slave->dev, buf); -+ -+ /* Host is owner, so set back to device */ -+ if (owner == 0) { -+ regmap_write(rt766->regmap, -+ RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_CURRENTOWNER), 0x01); -+ } -+ -+ return 0; -+ -+io_error: -+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); -+ return ret; -+} -+ -+static irqreturn_t rt766_sdca_irq_btn_handler(int irq, void *data) -+{ -+ struct sdca_interrupt *interrupt = data; -+ struct rt766_sdca_priv *rt766 = interrupt->priv; -+ -+ if (!rt766->hs_jack) -+ return IRQ_HANDLED; -+ -+ if (!rt766->component->card || !rt766->component->card->instantiated) -+ return IRQ_HANDLED; -+ -+ mutex_lock(&rt766->disable_irq_lock); -+ if (!rt766->disable_irq) -+ rt766_sdca_btn_detect(interrupt); -+ mutex_unlock(&rt766->disable_irq_lock); -+ return IRQ_HANDLED; -+} -+ -+static int rt766_sdca_headset_detect(struct rt766_sdca_priv *rt766) -+{ -+ unsigned int det_mode; -+ int ret; -+ -+ /* get detected_mode */ -+ ret = regmap_read(rt766->regmap, -+ RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_DETECTED_MODE), -+ &det_mode); -+ if (ret < 0) -+ goto io_error; -+ -+ switch (det_mode) { -+ case 0x00: -+ rt766->jack_type = 0; -+ break; -+ case 0x03: -+ rt766->jack_type = SND_JACK_HEADPHONE; -+ break; -+ case 0x05: -+ rt766->jack_type = SND_JACK_HEADSET; -+ break; -+ } -+ -+ /* write selected_mode */ -+ if (det_mode) { -+ ret = regmap_write(rt766->regmap, -+ RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_SELECTED_MODE), -+ det_mode); -+ if (ret < 0) -+ goto io_error; -+ } -+ -+ dev_dbg(&rt766->slave->dev, -+ "%s, detected_mode=0x%x\n", __func__, det_mode); -+ -+ return 0; -+ -+io_error: -+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); -+ return ret; -+} -+ -+static irqreturn_t rt766_sdca_irq_jd_handler(int irq, void *data) -+{ -+ struct sdca_interrupt *interrupt = data; -+ struct rt766_sdca_priv *rt766 = interrupt->priv; -+ -+ if (!rt766->hs_jack) -+ return IRQ_HANDLED; -+ -+ if (!rt766->component->card || !rt766->component->card->instantiated) -+ return IRQ_HANDLED; -+ -+ mutex_lock(&rt766->disable_irq_lock); -+ if (!rt766->disable_irq) -+ rt766_sdca_headset_detect(rt766); -+ mutex_unlock(&rt766->disable_irq_lock); -+ -+ dev_dbg(&rt766->slave->dev, -+ "in %s, jack_type=%d\n", __func__, rt766->jack_type); -+ -+ snd_soc_jack_report(rt766->hs_jack, rt766->jack_type, SND_JACK_HEADSET); -+ return IRQ_HANDLED; -+} -+ -+static void rt766_sdca_destroy_hid_device(struct sdca_interrupt *interrupt) -+{ -+ struct rt766_sdca_priv *rt766 = interrupt->priv; -+ -+ hid_destroy_device(rt766->hid); -+} -+ -+static int rt766_sdca_irq_ctl(struct rt766_sdca_priv *rt766, -+ struct sdca_function_data *function, -+ struct snd_soc_component *component, -+ struct sdca_interrupt_info *info, -+ bool enabled) -+{ -+ struct device *dev = &rt766->slave->dev; -+ struct sdca_interrupt *interrupt; -+ struct sdca_control *control; -+ struct sdca_entity *entity; -+ irq_handler_t handler; -+ int i, j, irq, ret; -+ -+ for (i = 0; i < function->num_entities; i++) { -+ entity = &function->entities[i]; -+ -+ for (j = 0; j < entity->num_controls; j++) { -+ control = &entity->controls[j]; -+ irq = control->interrupt_position; -+ -+ switch (SDCA_CTL_TYPE(entity->type, control->sel)) { -+ case SDCA_CTL_TYPE_S(GE, DETECTED_MODE): -+ handler = rt766_sdca_irq_jd_handler; -+ break; -+ case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER): -+ handler = rt766_sdca_irq_btn_handler; -+ break; -+ default: -+ continue; -+ } -+ -+ interrupt = &info->irqs[irq]; -+ -+ if (enabled) { -+ ret = sdca_irq_data_populate(dev, rt766->regmap, component, -+ function, entity, control, -+ interrupt); -+ if (ret) -+ return ret; -+ -+ if (handler == rt766_sdca_irq_btn_handler) { -+ ret = sdca_add_hid_device(interrupt); -+ if (ret) -+ return ret; -+ -+ interrupt->free_priv = rt766_sdca_destroy_hid_device; -+ rt766->hid = interrupt->priv; -+ } -+ -+ interrupt->priv = rt766; -+ ret = sdca_irq_request(dev, info, irq, interrupt->name, -+ handler, interrupt); -+ if (ret) { -+ dev_err(dev, "failed to request irq %s: %d\n", -+ interrupt->name, ret); -+ sdca_irq_cleanup_late(dev, function, info); -+ return ret; -+ } -+ dev_dbg(dev, "Requesting IRQ %d InterruptName=%s\n", irq, interrupt->name); -+ } else { -+ sdca_irq_cleanup_late(dev, function, info); -+ dev_dbg(dev, "Freeing IRQ %d\n", irq); -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+static int rt766_sdca_set_jack_detect(struct snd_soc_component *component, -+ struct snd_soc_jack *hs_jack, void *data) -+{ -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ int ret; -+ -+ if (!rt766->uaj_func_data) { -+ dev_err(&rt766->slave->dev, "The SDCA UAJ function is not supported.\n"); -+ return -EINVAL; -+ } -+ -+ rt766->hs_jack = hs_jack; -+ -+ if (!rt766->first_hw_init) -+ return 0; -+ -+ ret = pm_runtime_resume_and_get(component->dev); -+ if (ret < 0) { -+ if (ret != -EACCES) { -+ dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret); -+ return ret; -+ } -+ -+ /* pm_runtime not enabled yet */ -+ dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__); -+ return 0; -+ } -+ -+ /* disable interrupts if hs_jack is not set */ -+ if (!rt766->hs_jack) { -+ if (rt766->uaj_func_data) -+ rt766_sdca_irq_ctl(rt766, rt766->uaj_func_data, -+ rt766->component, rt766->irq_info, false); -+ -+ if (rt766->hid_func_data) -+ rt766_sdca_irq_ctl(rt766, rt766->hid_func_data, -+ rt766->component, rt766->irq_info, false); -+ } -+ -+ pm_runtime_put_autosuspend(component->dev); -+ -+ return 0; -+} -+ -+static int rt766_sdca_set_fu_ctl(struct rt766_sdca_priv *rt766, int func_num, int fu_num) -+{ -+ unsigned int fu01_reg, fu02_reg; -+ unsigned int ch_01, ch_02; -+ unsigned int ch_mute; -+ unsigned int fu_reg; -+ int err, i; -+ -+ switch (fu_num) { -+ case RT766_SDCA_ENT_USER_FU41: -+ ch_01 = (rt766->fu41_dapm_mute || rt766->fu41_mixer_l_mute) ? 0x01 : 0x00; -+ ch_02 = (rt766->fu41_dapm_mute || rt766->fu41_mixer_r_mute) ? 0x01 : 0x00; -+ break; -+ case RT766_SDCA_ENT_USER_FU36: -+ ch_01 = (rt766->fu36_dapm_mute || rt766->fu36_mixer_l_mute) ? 0x01 : 0x00; -+ ch_02 = (rt766->fu36_dapm_mute || rt766->fu36_mixer_r_mute) ? 0x01 : 0x00; -+ break; -+ case RT766_SDCA_ENT_USER_FU21: -+ ch_01 = (rt766->fu21_dapm_mute || rt766->fu21_mixer_l_mute) ? 0x01 : 0x00; -+ ch_02 = (rt766->fu21_dapm_mute || rt766->fu21_mixer_r_mute) ? 0x01 : 0x00; -+ break; -+ case RT766_SDCA_ENT_USER_FU113: -+ for (i = 0; i < ARRAY_SIZE(rt766->fu113_mixer_mute); i++) { -+ ch_mute = (rt766->fu113_dapm_mute || rt766->fu113_mixer_mute[i]) ? 0x01 : 0x00; -+ fu_reg = SDW_SDCA_CTL(func_num, fu_num, SDCA_CTL_FU_MUTE, 1) + i; -+ err = regmap_write(rt766->regmap, fu_reg, ch_mute); -+ if (err < 0) -+ return err; -+ } -+ return 0; -+ } -+ -+ fu01_reg = SDW_SDCA_CTL(func_num, fu_num, SDCA_CTL_FU_MUTE, 1); -+ fu02_reg = SDW_SDCA_CTL(func_num, fu_num, SDCA_CTL_FU_MUTE, 2); -+ err = regmap_write(rt766->regmap, fu01_reg, ch_01); -+ if (err < 0) -+ return err; -+ err = regmap_write(rt766->regmap, fu02_reg, ch_02); -+ if (err < 0) -+ return err; -+ -+ return 0; -+} -+ -+static int rt766_sdca_fu41_playback_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ -+ ucontrol->value.integer.value[0] = !rt766->fu41_mixer_l_mute; -+ ucontrol->value.integer.value[1] = !rt766->fu41_mixer_r_mute; -+ return 0; -+} -+ -+static int rt766_sdca_fu41_playback_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ int err; -+ -+ if (rt766->fu41_mixer_l_mute == !ucontrol->value.integer.value[0] && -+ rt766->fu41_mixer_r_mute == !ucontrol->value.integer.value[1]) -+ return 0; -+ -+ rt766->fu41_mixer_l_mute = !ucontrol->value.integer.value[0]; -+ rt766->fu41_mixer_r_mute = !ucontrol->value.integer.value[1]; -+ -+ err = rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU41); -+ if (err < 0) -+ return err; -+ -+ return 1; -+} -+ -+static int rt766_sdca_fu36_capture_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ -+ ucontrol->value.integer.value[0] = !rt766->fu36_mixer_l_mute; -+ ucontrol->value.integer.value[1] = !rt766->fu36_mixer_r_mute; -+ return 0; -+} -+ -+static int rt766_sdca_fu36_capture_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ int err; -+ -+ if (rt766->fu36_mixer_l_mute == !ucontrol->value.integer.value[0] && -+ rt766->fu36_mixer_r_mute == !ucontrol->value.integer.value[1]) -+ return 0; -+ -+ rt766->fu36_mixer_l_mute = !ucontrol->value.integer.value[0]; -+ rt766->fu36_mixer_r_mute = !ucontrol->value.integer.value[1]; -+ err = rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU36); -+ if (err < 0) -+ return err; -+ -+ return 1; -+} -+ -+static int rt766_sdca_fu21_playback_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ -+ ucontrol->value.integer.value[0] = !rt766->fu21_mixer_l_mute; -+ ucontrol->value.integer.value[1] = !rt766->fu21_mixer_r_mute; -+ return 0; -+} -+ -+static int rt766_sdca_fu21_playback_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ int err; -+ -+ if (rt766->fu21_mixer_l_mute == !ucontrol->value.integer.value[0] && -+ rt766->fu21_mixer_r_mute == !ucontrol->value.integer.value[1]) -+ return 0; -+ -+ rt766->fu21_mixer_l_mute = !ucontrol->value.integer.value[0]; -+ rt766->fu21_mixer_r_mute = !ucontrol->value.integer.value[1]; -+ -+ err = rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_AMP, RT766_SDCA_ENT_USER_FU21); -+ if (err < 0) -+ return err; -+ -+ return 1; -+} -+ -+static int rt766_sdca_fu113_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *kcontrol, int event) -+{ -+ struct snd_soc_component *component = -+ snd_soc_dapm_to_component(w->dapm); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ -+ switch (event) { -+ case SND_SOC_DAPM_POST_PMU: -+ rt766->fu113_dapm_mute = false; -+ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_MIC, RT766_SDCA_ENT_USER_FU113); -+ break; -+ case SND_SOC_DAPM_PRE_PMD: -+ rt766->fu113_dapm_mute = true; -+ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_MIC, RT766_SDCA_ENT_USER_FU113); -+ break; -+ } -+ return 0; -+} -+ -+static int rt766_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ struct rt_sdca_dmic_kctrl_priv *p = -+ (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value; -+ const unsigned int interval_offset = 0xc0; -+ unsigned int regvalue, ctl, i; -+ -+ /* check all channels */ -+ for (i = 0; i < p->count; i++) { -+ regmap_read(rt766->regmap, p->reg_base + i, ®value); -+ ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset); -+ -+ ucontrol->value.integer.value[i] = ctl; -+ } -+ -+ return 0; -+} -+ -+static int rt766_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct rt_sdca_dmic_kctrl_priv *p = -+ (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value; -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ const unsigned int interval_offset = 0xc0; -+ unsigned int gain_val[4]; -+ unsigned int i, changed = 0; -+ unsigned int regvalue[4]; -+ int err; -+ -+ /* check all channels */ -+ for (i = 0; i < p->count; i++) { -+ regmap_read(rt766->regmap, p->reg_base + i, ®value[i]); -+ -+ gain_val[i] = ucontrol->value.integer.value[i]; -+ if (gain_val[i] > p->max) -+ gain_val[i] = p->max; -+ -+ gain_val[i] = 0x1e00 - ((p->max - gain_val[i]) * interval_offset); -+ gain_val[i] &= 0xffff; -+ -+ if (regvalue[i] != gain_val[i]) -+ changed = 1; -+ } -+ -+ if (!changed) -+ return 0; -+ -+ for (i = 0; i < p->count; i++) { -+ err = regmap_write(rt766->regmap, p->reg_base + i, gain_val[i]); -+ if (err < 0) -+ dev_err(&rt766->slave->dev, "0x%08x can't be set\n", p->reg_base + i); -+ } -+ -+ return changed; -+} -+ -+static int rt766_sdca_dmic_fu113_capture_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ unsigned int i; -+ -+ for (i = 0; i < 4; i++) -+ ucontrol->value.integer.value[i] = !rt766->fu113_mixer_mute[i]; -+ return 0; -+} -+ -+static int rt766_sdca_dmic_fu113_capture_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ int err, changed = 0, i; -+ -+ for (i = 0; i < 4; i++) { -+ if (rt766->fu113_mixer_mute[i] != !ucontrol->value.integer.value[i]) -+ changed = 1; -+ rt766->fu113_mixer_mute[i] = !ucontrol->value.integer.value[i]; -+ } -+ -+ err = rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_MIC, RT766_SDCA_ENT_USER_FU113); -+ if (err < 0) -+ return err; -+ return changed; -+} -+ -+static int rt766_sdca_fu41_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *kcontrol, int event) -+{ -+ struct snd_soc_component *component = -+ snd_soc_dapm_to_component(w->dapm); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ -+ switch (event) { -+ case SND_SOC_DAPM_POST_PMU: -+ rt766->fu41_dapm_mute = false; -+ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU41); -+ break; -+ case SND_SOC_DAPM_PRE_PMD: -+ rt766->fu41_dapm_mute = true; -+ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU41); -+ break; -+ } -+ return 0; -+} -+ -+static int rt766_sdca_pde_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *kcontrol, int event, int func_num, int pde_num, const char *pde_ent) -+{ -+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ struct sdca_function_data *func_data; -+ unsigned char ps0 = 0x0, ps3 = 0x3; -+ const struct sdca_entity *entity; -+ unsigned int pde_req_reg; -+ int from_ps, to_ps; -+ int ret; -+ -+ pde_req_reg = SDW_SDCA_CTL(func_num, pde_num, SDCA_CTL_PDE_REQUESTED_PS, 0); -+ -+ switch (func_num) { -+ case RT766_FUNC_NUM_UAJ: -+ func_data = rt766->uaj_func_data; -+ break; -+ case RT766_FUNC_NUM_AMP: -+ func_data = rt766->sa_func_data; -+ break; -+ case RT766_FUNC_NUM_MIC: -+ func_data = rt766->sm_func_data; -+ break; -+ default: -+ dev_err(component->dev, "%s: unsupported func_num %d\n", -+ __func__, func_num); -+ return -EINVAL; -+ } -+ -+ switch (event) { -+ case SND_SOC_DAPM_POST_PMU: -+ regmap_write(rt766->regmap, pde_req_reg, ps0); -+ from_ps = ps3; -+ to_ps = ps0; -+ break; -+ case SND_SOC_DAPM_PRE_PMD: -+ regmap_write(rt766->regmap, pde_req_reg, ps3); -+ from_ps = ps0; -+ to_ps = ps3; -+ break; -+ } -+ -+ entity = sdca_find_entity_by_label(func_data, pde_ent); -+ if (!entity) { -+ dev_err(component->dev, "%s: failed to find entity %s\n", -+ __func__, pde_ent); -+ return -EINVAL; -+ } -+ -+ ret = sdca_asoc_pde_poll_actual_ps(rt766->regmap, -+ func_num, -+ pde_num, -+ from_ps, to_ps, -+ entity->pde.max_delay, -+ entity->pde.num_max_delay); -+ if (ret) -+ dev_err(component->dev, "%s: PDE transition %x -> %x failed, err=%d\n", -+ __func__, from_ps, to_ps, ret); -+ -+ return ret; -+} -+ -+static int rt766_sdca_pde47_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *kcontrol, int event) -+{ -+ return rt766_sdca_pde_event(w, kcontrol, event, -+ RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_PDE47, "PDE 47"); -+} -+ -+static int rt766_sdca_fu36_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *kcontrol, int event) -+{ -+ struct snd_soc_component *component = -+ snd_soc_dapm_to_component(w->dapm); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ -+ switch (event) { -+ case SND_SOC_DAPM_POST_PMU: -+ rt766->fu36_dapm_mute = false; -+ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU36); -+ break; -+ case SND_SOC_DAPM_PRE_PMD: -+ rt766->fu36_dapm_mute = true; -+ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU36); -+ break; -+ } -+ return 0; -+} -+ -+static int rt766_sdca_pde34_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *kcontrol, int event) -+{ -+ return rt766_sdca_pde_event(w, kcontrol, event, -+ RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_PDE34, "PDE 34"); -+} -+ -+static int rt766_sdca_fu21_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *kcontrol, int event) -+{ -+ struct snd_soc_component *component = -+ snd_soc_dapm_to_component(w->dapm); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ -+ switch (event) { -+ case SND_SOC_DAPM_POST_PMU: -+ rt766->fu21_dapm_mute = false; -+ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_AMP, RT766_SDCA_ENT_USER_FU21); -+ break; -+ case SND_SOC_DAPM_PRE_PMD: -+ rt766->fu21_dapm_mute = true; -+ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_AMP, RT766_SDCA_ENT_USER_FU21); -+ break; -+ } -+ return 0; -+} -+ -+static int rt766_sdca_pde23_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *kcontrol, int event) -+{ -+ return rt766_sdca_pde_event(w, kcontrol, event, -+ RT766_FUNC_NUM_AMP, RT766_SDCA_ENT_PDE23, "PDE 23"); -+} -+ -+static int rt766_sdca_pde11_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *kcontrol, int event) -+{ -+ return rt766_sdca_pde_event(w, kcontrol, event, -+ RT766_FUNC_NUM_MIC, RT766_SDCA_ENT_PDE11, "PDE 11"); -+} -+ -+static int rt766_dmic_fu_info(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_info *uinfo) -+{ -+ struct rt_sdca_dmic_kctrl_priv *p = -+ (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value; -+ -+ if (p->max == 1) -+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; -+ else -+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; -+ uinfo->count = p->count; -+ uinfo->value.integer.min = 0; -+ uinfo->value.integer.max = p->max; -+ return 0; -+} -+ -+static const char * const rt766_rx_data_ch_select[] = { -+ "L,R", -+ "R,L", -+ "L,L", -+ "R,R", -+ "L,L+R", -+ "R,L+R", -+ "L+R,L", -+ "L+R,R", -+ "L+R,L+R", -+}; -+ -+static SOC_ENUM_SINGLE_DECL(rt766_rx_data_ch_enum, -+ RT766_SDCA_CTL(AMP, PPU21, SDCA_CTL_PPU_POSTURENUMBER), 0, -+ rt766_rx_data_ch_select); -+ -+static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -9525, 75, 0); -+static const DECLARE_TLV_DB_SCALE(spk_vol_tlv, -6525, 75, 0); -+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -1725, 75, 0); -+static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, -200, 200, 0); -+ -+#define RT766_P75DB_STEP 0xC0 /* 0.75 dB in Q7.8 format */ -+#define RT766_2DB_STEP 0x200 /* 2 dB in Q7.8 format */ -+#define RT766_HP_VOL_MIN (-127) /* -95.25 dB / 0.75 dB step */ -+#define RT766_HS_VOL_MIN (-23) /* -17.25 dB / 0.75 dB step */ -+#define RT766_HS_BOOST_VOL_MIN (-1) /* -2 dB / 2 dB step */ -+#define RT766_SPK_VOL_MIN (-87) /* -65.25 dB / 0.75 dB step */ -+#define RT766_P_VOL_MAX 0 /* 0 dB / 0.75 dB step */ -+#define RT766_HS_VOL_MAX 40 /* 30 dB / 0.75 dB step */ -+#define RT766_HS_BOOST_VOL_MAX 20 /* 40 dB / 2 dB step */ -+ -+static const struct snd_kcontrol_new rt766_sdca_controls[] = { -+ SOC_DOUBLE_EXT("FU41 Playback Switch", SND_SOC_NOPM, 0, 1, 1, 0, -+ rt766_sdca_fu41_playback_get, rt766_sdca_fu41_playback_put), -+ SDCA_DOUBLE_Q78_TLV("FU41 Playback Volume", -+ RT766_VOLUME_REG(UAJ, USER_FU41, 1), -+ RT766_VOLUME_REG(UAJ, USER_FU41, 2), -+ RT766_HP_VOL_MIN, RT766_P_VOL_MAX, RT766_P75DB_STEP, hp_vol_tlv), -+ SOC_DOUBLE_EXT("FU36 Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0, -+ rt766_sdca_fu36_capture_get, rt766_sdca_fu36_capture_put), -+ SDCA_DOUBLE_Q78_TLV("FU36 Capture Volume", -+ RT766_VOLUME_REG(UAJ, USER_FU36, 1), -+ RT766_VOLUME_REG(UAJ, USER_FU36, 2), -+ RT766_HS_VOL_MIN, RT766_HS_VOL_MAX, RT766_P75DB_STEP, mic_vol_tlv), -+ SDCA_DOUBLE_Q78_TLV("FU33 Boost Volume", -+ RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1), -+ RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2), -+ RT766_HS_BOOST_VOL_MIN, RT766_HS_BOOST_VOL_MAX, RT766_2DB_STEP, boost_vol_tlv), -+ -+ SOC_DOUBLE_EXT("FU21 Playback Switch", SND_SOC_NOPM, 0, 1, 1, 0, -+ rt766_sdca_fu21_playback_get, rt766_sdca_fu21_playback_put), -+ SDCA_DOUBLE_Q78_TLV("FU21 Playback Volume", -+ RT766_VOLUME_REG(AMP, USER_FU21, 1), -+ RT766_VOLUME_REG(AMP, USER_FU21, 2), -+ RT766_SPK_VOL_MIN, RT766_P_VOL_MAX, RT766_P75DB_STEP, spk_vol_tlv), -+ SOC_ENUM("RX Channel Select", rt766_rx_data_ch_enum), -+ -+ RT_SDCA_FU_CTRL("FU113 Capture Switch", -+ RT766_MUTE_REG(MIC, USER_FU113, 1), 1, 1, 4, rt766_dmic_fu_info, -+ rt766_sdca_dmic_fu113_capture_get, rt766_sdca_dmic_fu113_capture_put), -+ RT_SDCA_EXT_TLV("FU113 Capture Volume", -+ RT766_VOLUME_REG(MIC, USER_FU113, 1), -+ rt766_sdca_dmic_set_gain_get, rt766_sdca_dmic_set_gain_put, -+ 4, 0x3f, mic_vol_tlv, rt766_dmic_fu_info), -+}; -+ -+static const struct snd_soc_dapm_widget rt766_sdca_dapm_widgets[] = { -+ /* UAJ */ -+ SND_SOC_DAPM_OUTPUT("HP"), -+ SND_SOC_DAPM_INPUT("MIC2"), -+ SND_SOC_DAPM_SUPPLY("PDE 47", SND_SOC_NOPM, 0, 0, -+ rt766_sdca_pde47_event, -+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), -+ SND_SOC_DAPM_SUPPLY("PDE 34", SND_SOC_NOPM, 0, 0, -+ rt766_sdca_pde34_event, -+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), -+ SND_SOC_DAPM_DAC_E("FU 41", NULL, SND_SOC_NOPM, 0, 0, -+ rt766_sdca_fu41_event, -+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), -+ SND_SOC_DAPM_ADC_E("FU 36", NULL, SND_SOC_NOPM, 0, 0, -+ rt766_sdca_fu36_event, -+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), -+ SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_OUT("DP12TX", "DP12 Capture", 0, SND_SOC_NOPM, 0, 0), -+ -+ /* AMP */ -+ SND_SOC_DAPM_OUTPUT("SPOL"), -+ SND_SOC_DAPM_OUTPUT("SPOR"), -+ SND_SOC_DAPM_DAC_E("FU 21", NULL, SND_SOC_NOPM, 0, 0, -+ rt766_sdca_fu21_event, -+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), -+ SND_SOC_DAPM_SUPPLY("PDE 23", SND_SOC_NOPM, 0, 0, -+ rt766_sdca_pde23_event, -+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), -+ SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0), -+ -+ /* DMIC */ -+ SND_SOC_DAPM_INPUT("DMIC1"), -+ SND_SOC_DAPM_INPUT("DMIC2"), -+ SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0, -+ rt766_sdca_pde11_event, -+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), -+ SND_SOC_DAPM_ADC_E("FU 113", NULL, SND_SOC_NOPM, 0, 0, -+ rt766_sdca_fu113_event, -+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), -+ SND_SOC_DAPM_AIF_OUT("DP8TX", "DP8 Capture", 0, SND_SOC_NOPM, 0, 0), -+}; -+ -+static const struct snd_soc_dapm_route rt766_sdca_audio_map[] = { -+ { "FU 41", NULL, "DP3RX" }, -+ { "DP12TX", NULL, "FU 36" }, -+ { "FU 36", NULL, "PDE 34" }, -+ { "FU 36", NULL, "MIC2" }, -+ { "HP", NULL, "PDE 47" }, -+ { "HP", NULL, "FU 41" }, -+ -+ { "FU 21", NULL, "DP1RX" }, -+ { "FU 21", NULL, "PDE 23" }, -+ { "SPOL", NULL, "FU 21" }, -+ { "SPOR", NULL, "FU 21" }, -+ -+ {"DP8TX", NULL, "FU 113"}, -+ {"FU 113", NULL, "PDE 11"}, -+ {"FU 113", NULL, "DMIC1"}, -+ {"FU 113", NULL, "DMIC2"}, -+}; -+ -+static int rt766_sdca_probe(struct snd_soc_component *component) -+{ -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ struct device *dev = &rt766->slave->dev; -+ int ret; -+ -+ rt766->component = component; -+ -+ ret = pm_runtime_resume(component->dev); -+ if (ret < 0 && ret != -EACCES) -+ return ret; -+ -+ if (rt766->uaj_func_data) { -+ dev_dbg(dev, "%s : irq %d\n", __func__, rt766->slave->irq); -+ -+ rt766->irq_info = devm_sdca_irq_allocate(dev, rt766->regmap, rt766->slave->irq); -+ if (IS_ERR(rt766->irq_info)) -+ return PTR_ERR(rt766->irq_info); -+ -+ ret = rt766_sdca_irq_ctl(rt766, rt766->uaj_func_data, -+ component, rt766->irq_info, true); -+ if (ret < 0) { -+ dev_err(dev, "Failed to request UAJ SDCA IRQ: %d\n", ret); -+ return ret; -+ } -+ -+ if (rt766->hid_func_data) { -+ ret = rt766_sdca_irq_ctl(rt766, rt766->hid_func_data, -+ component, rt766->irq_info, true); -+ if (ret < 0) { -+ dev_err(dev, "Failed to request HID SDCA IRQ: %d\n", ret); -+ return ret; -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+static void rt766_sdca_remove(struct snd_soc_component *component) -+{ -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ -+ sdca_irq_cleanup_late(component->dev, rt766->uaj_func_data, rt766->irq_info); -+ sdca_irq_cleanup_late(component->dev, rt766->hid_func_data, rt766->irq_info); -+} -+ -+static const struct snd_soc_component_driver soc_sdca_dev_rt766 = { -+ .probe = rt766_sdca_probe, -+ .remove = rt766_sdca_remove, -+ .controls = rt766_sdca_controls, -+ .num_controls = ARRAY_SIZE(rt766_sdca_controls), -+ .dapm_widgets = rt766_sdca_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(rt766_sdca_dapm_widgets), -+ .dapm_routes = rt766_sdca_audio_map, -+ .num_dapm_routes = ARRAY_SIZE(rt766_sdca_audio_map), -+ .set_jack = rt766_sdca_set_jack_detect, -+ .endianness = 1, -+}; -+ -+static int rt766_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, -+ int direction) -+{ -+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream); -+ -+ return 0; -+} -+ -+static void rt766_sdca_shutdown(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ snd_soc_dai_set_dma_data(dai, substream, NULL); -+} -+ -+static int rt766_sdca_pcm_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params, -+ struct snd_soc_dai *dai) -+{ -+ struct snd_soc_component *component = dai->component; -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ struct sdw_stream_config stream_config; -+ struct sdw_port_config port_config; -+ enum sdw_data_direction direction; -+ struct sdw_stream_runtime *sdw_stream; -+ unsigned int sampling_rate; -+ int retval, port; -+ -+ dev_dbg(dai->dev, "%s %s id %d", __func__, dai->name, dai->id); -+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream); -+ -+ if (!sdw_stream) -+ return -EINVAL; -+ -+ if (!rt766->slave) -+ return -EINVAL; -+ -+ /* SoundWire specific configuration */ -+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config); -+ -+ /* SoundWire specific configuration */ -+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { -+ direction = SDW_DATA_DIR_RX; -+ if (dai->id == RT766_AIF1) -+ port = 3; -+ else if (dai->id == RT766_AIF2) -+ port = 1; -+ else -+ return -EINVAL; -+ } else { -+ direction = SDW_DATA_DIR_TX; -+ if (dai->id == RT766_AIF1) -+ port = 12; -+ else if (dai->id == RT766_AIF3) -+ port = 8; -+ else -+ return -EINVAL; -+ } -+ -+ port_config.num = port; -+ retval = sdw_stream_add_slave(rt766->slave, &stream_config, -+ &port_config, 1, sdw_stream); -+ if (retval) { -+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__); -+ return retval; -+ } -+ -+ if (params_channels(params) > 16) { -+ dev_err(component->dev, "%s: Unsupported channels %d\n", -+ __func__, params_channels(params)); -+ return -EINVAL; -+ } -+ -+ /* sampling rate configuration */ -+ switch (params_rate(params)) { -+ case 44100: -+ sampling_rate = RT766_SDCA_RATE_44100HZ; -+ break; -+ case 48000: -+ sampling_rate = RT766_SDCA_RATE_48000HZ; -+ break; -+ case 96000: -+ sampling_rate = RT766_SDCA_RATE_96000HZ; -+ break; -+ case 192000: -+ sampling_rate = RT766_SDCA_RATE_192000HZ; -+ break; -+ default: -+ dev_err(component->dev, "%s: Rate %d is not supported\n", -+ __func__, params_rate(params)); -+ return -EINVAL; -+ } -+ -+ /* set sampling frequency */ -+ switch (dai->id) { -+ case RT766_AIF1: -+ regmap_write(rt766->regmap, -+ RT766_SDCA_CTL(UAJ, CS41, SDCA_CTL_CS_SAMPLERATEINDEX), -+ sampling_rate); -+ regmap_write(rt766->regmap, -+ RT766_SDCA_CTL(UAJ, CS36, SDCA_CTL_CS_SAMPLERATEINDEX), -+ sampling_rate); -+ break; -+ case RT766_AIF2: -+ regmap_write(rt766->regmap, -+ RT766_SDCA_CTL(AMP, CS21, SDCA_CTL_CS_SAMPLERATEINDEX), -+ sampling_rate); -+ break; -+ case RT766_AIF3: -+ regmap_write(rt766->regmap, -+ RT766_SDCA_CTL(MIC, CS113, SDCA_CTL_CS_SAMPLERATEINDEX), -+ sampling_rate); -+ break; -+ default: -+ dev_err(component->dev, "%s: Wrong DAI id\n", __func__); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int rt766_sdca_pcm_hw_free(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ struct snd_soc_component *component = dai->component; -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ struct sdw_stream_runtime *sdw_stream = -+ snd_soc_dai_get_dma_data(dai, substream); -+ -+ if (!rt766->slave) -+ return -EINVAL; -+ -+ sdw_stream_remove_slave(rt766->slave, sdw_stream); -+ return 0; -+} -+ -+#define RT766_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \ -+ SNDRV_PCM_RATE_192000) -+#define RT766_DAC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) -+#define RT766_ADC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ -+ SNDRV_PCM_FMTBIT_S32_LE) -+ -+static const struct snd_soc_dai_ops rt766_sdca_ops = { -+ .hw_params = rt766_sdca_pcm_hw_params, -+ .hw_free = rt766_sdca_pcm_hw_free, -+ .set_stream = rt766_sdca_set_sdw_stream, -+ .shutdown = rt766_sdca_shutdown, -+}; -+ -+static struct snd_soc_dai_driver rt766_sdca_dai[] = { -+ { -+ .name = "rt766-sdca-aif1", -+ .id = RT766_AIF1, -+ .playback = { -+ .stream_name = "DP3 Playback", -+ .channels_min = 1, -+ .channels_max = 2, -+ .rates = RT766_STEREO_RATES, -+ .formats = RT766_DAC_FORMATS, -+ }, -+ .capture = { -+ .stream_name = "DP12 Capture", -+ .channels_min = 1, -+ .channels_max = 2, -+ .rates = RT766_STEREO_RATES, -+ .formats = RT766_ADC_FORMATS, -+ }, -+ .ops = &rt766_sdca_ops, -+ .symmetric_rate = 1, -+ }, -+ { -+ .name = "rt766-sdca-aif2", -+ .id = RT766_AIF2, -+ .playback = { -+ .stream_name = "DP1 Playback", -+ .channels_min = 1, -+ .channels_max = 4, -+ .rates = RT766_STEREO_RATES, -+ .formats = RT766_DAC_FORMATS, -+ }, -+ .ops = &rt766_sdca_ops, -+ }, -+ { -+ .name = "rt766-sdca-aif3", -+ .id = RT766_AIF3, -+ .capture = { -+ .stream_name = "DP8 Capture", -+ .channels_min = 1, -+ .channels_max = 4, -+ .rates = RT766_STEREO_RATES, -+ .formats = RT766_ADC_FORMATS, -+ }, -+ .ops = &rt766_sdca_ops, -+ } -+}; -+ -+static unsigned int rt766_find_dt_rates(struct device *dev, struct sdca_function_data *function, -+ const char *label) -+{ -+ struct snd_soc_pcm_stream stream; -+ struct sdca_entity *entity; -+ int i, ret; -+ -+ for (i = 0; i < function->num_entities; i++) { -+ entity = &function->entities[i]; -+ -+ if (strcmp(entity->label, label)) -+ continue; -+ -+ /* Can't check earlier as only terminals have an iot member. */ -+ if (!entity->iot.is_dataport) -+ continue; -+ -+ ret = sdca_asoc_populate_rate_format(dev, function, entity, &stream); -+ if (ret < 0) { -+ dev_dbg(dev, "%s: failed to parse rates for entity %s\n", -+ __func__, entity->label); -+ return 0; -+ } -+ -+ dev_dbg(dev, "%s: %s supports rates 0x%08x\n", __func__, entity->label, stream.rates); -+ } -+ -+ return stream.rates; -+} -+ -+int rt766_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave *slave) -+{ -+ struct sdca_function_data *func_data_ptr; -+ struct snd_soc_dai_driver *dai_drv; -+ struct rt766_sdca_priv *rt766; -+ unsigned int rates; -+ int ret; -+ int i; -+ -+ rt766 = devm_kzalloc(dev, sizeof(*rt766), GFP_KERNEL); -+ if (!rt766) -+ return -ENOMEM; -+ -+ dev_set_drvdata(dev, rt766); -+ rt766->slave = slave; -+ rt766->regmap = regmap; -+ -+ regcache_cache_only(rt766->regmap, true); -+ -+ ret = devm_mutex_init(dev, &rt766->disable_irq_lock); -+ if (ret < 0) { -+ dev_err(dev, "Failed to initialize mutex\n"); -+ return ret; -+ } -+ -+ /* -+ * Mark hw_init to false -+ * HW init will be performed when device reports present -+ */ -+ rt766->hw_init = false; -+ rt766->first_hw_init = false; -+ rt766->fu41_dapm_mute = true; -+ rt766->fu41_mixer_l_mute = rt766->fu41_mixer_r_mute = false; -+ rt766->fu36_dapm_mute = true; -+ rt766->fu36_mixer_l_mute = rt766->fu36_mixer_r_mute = true; -+ rt766->fu21_dapm_mute = true; -+ rt766->fu21_mixer_l_mute = rt766->fu21_mixer_r_mute = false; -+ rt766->fu113_dapm_mute = true; -+ rt766->fu113_mixer_mute[0] = rt766->fu113_mixer_mute[1] = -+ rt766->fu113_mixer_mute[2] = rt766->fu113_mixer_mute[3] = true; -+ -+ dai_drv = devm_kzalloc(dev, sizeof(struct snd_soc_dai_driver) * ARRAY_SIZE(rt766_sdca_dai), GFP_KERNEL); -+ if (!dai_drv) { -+ dev_err(dev, "Failed to allocate memory for DAI driver\n"); -+ ret = -ENOMEM; -+ goto _sdw_init_err_; -+ } -+ memcpy(dai_drv, rt766_sdca_dai, sizeof(struct snd_soc_dai_driver) * ARRAY_SIZE(rt766_sdca_dai)); -+ -+ /* get SDCA function data */ -+ dev_dbg(dev, "SDCA functions found: %d", slave->sdca_data.num_functions); -+ for (i = 0; i < slave->sdca_data.num_functions; i++) { -+ func_data_ptr = devm_kzalloc(dev, sizeof(*func_data_ptr), GFP_KERNEL); -+ if (!func_data_ptr) { -+ dev_err(dev, "Failed to allocate memory for function data\n"); -+ ret = -ENOMEM; -+ goto _free_dai_drv_; -+ } -+ -+ func_data_ptr->desc = &slave->sdca_data.function[i]; -+ ret = sdca_parse_function(dev, func_data_ptr); -+ if (ret) { -+ devm_kfree(dev, func_data_ptr); -+ goto _free_dai_drv_; -+ } -+ dev_dbg(dev, "Function type=%d, num_entities=%d", -+ slave->sdca_data.function[i].type, func_data_ptr->num_entities); -+ -+ switch (slave->sdca_data.function[i].type) { -+ case SDCA_FUNCTION_TYPE_UAJ: -+ rt766->uaj_func_data = func_data_ptr; -+ /* -+ * Some machines may only support a subset of the sample rates supported by the codec. -+ * Therefore, we need to parse the supported sample rates from the DisCo table and -+ * configure them in the DAI. If the DisCo table does not provide sample rate information, -+ * we will fall back to the default supported rates defined in the codec driver. -+ */ -+ rates = rt766_find_dt_rates(dev, func_data_ptr, "IT 41"); -+ if (rates) -+ dai_drv[RT766_DAI_UAJ].playback.rates = rates; -+ -+ rates = rt766_find_dt_rates(dev, func_data_ptr, "OT 36"); -+ if (rates) -+ dai_drv[RT766_DAI_UAJ].capture.rates = rates; -+ break; -+ case SDCA_FUNCTION_TYPE_SMART_AMP: -+ rt766->sa_func_data = func_data_ptr; -+ rates = rt766_find_dt_rates(dev, func_data_ptr, "IT 21"); -+ if (rates) -+ dai_drv[RT766_DAI_AMP].playback.rates = rates; -+ break; -+ case SDCA_FUNCTION_TYPE_SMART_MIC: -+ rt766->sm_func_data = func_data_ptr; -+ rates = rt766_find_dt_rates(dev, func_data_ptr, "OT 113"); -+ if (rates) -+ dai_drv[RT766_DAI_MIC].capture.rates = rates; -+ break; -+ case SDCA_FUNCTION_TYPE_HID: -+ rt766->hid_func_data = func_data_ptr; -+ break; -+ default: -+ dev_dbg(dev, "Unexpected SDCA function type found: %d", -+ slave->sdca_data.function[i].type); -+ } -+ } -+ -+ ret = devm_snd_soc_register_component(dev, -+ &soc_sdca_dev_rt766, dai_drv, ARRAY_SIZE(rt766_sdca_dai)); -+ if (ret < 0) -+ goto _free_dai_drv_; -+ -+ /* set autosuspend parameters */ -+ pm_runtime_set_autosuspend_delay(dev, 3000); -+ pm_runtime_use_autosuspend(dev); -+ -+ /* make sure the device does not suspend immediately */ -+ pm_runtime_mark_last_busy(dev); -+ -+ pm_runtime_enable(dev); -+ dev_dbg(dev, "%s\n", __func__); -+ return 0; -+ -+_free_dai_drv_: -+ if (dai_drv) -+ devm_kfree(dev, dai_drv); -+ -+_sdw_init_err_: -+ return ret; -+} -+ -+static int rt766_func_initialize(struct rt766_sdca_priv *rt766, struct sdca_function_data *func_data) -+{ -+ struct device *dev = &rt766->slave->dev; -+ unsigned int func_status_reg; -+ unsigned int func_status; -+ int ret; -+ -+ switch (func_data->desc->type) { -+ case SDCA_FUNCTION_TYPE_UAJ: -+ func_status_reg = RT766_FUNC_STATUS_REG(UAJ); -+ break; -+ case SDCA_FUNCTION_TYPE_SMART_AMP: -+ func_status_reg = RT766_FUNC_STATUS_REG(AMP); -+ break; -+ case SDCA_FUNCTION_TYPE_SMART_MIC: -+ func_status_reg = RT766_FUNC_STATUS_REG(MIC); -+ break; -+ case SDCA_FUNCTION_TYPE_HID: -+ func_status_reg = RT766_FUNC_STATUS_REG(HID); -+ break; -+ default: -+ dev_dbg(dev, "Unexpected SDCA function type found: %d", -+ func_data->desc->type); -+ return -EINVAL; -+ } -+ -+ regmap_read(rt766->regmap, func_status_reg, &func_status); -+ dev_dbg(dev, "%s, %s func_status=0x%x\n", __func__, func_data->desc->name, func_status); -+ -+ if ((func_status & SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION) || (!rt766->first_hw_init)) { -+ ret = sdca_regmap_write_init(dev, rt766->regmap, func_data); -+ if (ret) { -+ dev_err(dev, "%s initialization table update failed\n", func_data->desc->name); -+ goto _func_init_err_; -+ } -+ -+ regmap_write(rt766->regmap, func_status_reg, -+ SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION); -+ } -+ -+ return 0; -+ -+_func_init_err_: -+ dev_err(dev, "%s: %s init writes failed, err=%d", __func__, func_data->desc->name, ret); -+ return ret; -+} -+ -+int rt766_sdca_io_init(struct device *dev, struct sdw_slave *slave) -+{ -+ struct rt766_sdca_priv *rt766 = dev_get_drvdata(dev); -+ unsigned int val; -+ -+ rt766->disable_irq = false; -+ -+ if (rt766->hw_init) -+ return 0; -+ -+ regcache_cache_only(rt766->regmap, false); -+ if (rt766->first_hw_init) { -+ regcache_cache_bypass(rt766->regmap, true); -+ } else { -+ /* -+ * PM runtime status is marked as 'active' only when a Slave reports as Attached -+ */ -+ -+ /* update count of parent 'active' children */ -+ pm_runtime_set_active(&slave->dev); -+ } -+ -+ pm_runtime_get_noresume(&slave->dev); -+ -+ regmap_read(rt766->regmap, RT766_BOND_LATCH_ID, &val); -+ dev_dbg(&slave->dev, "%s bond ID=0x%x (%s)\n", __func__, val, (val == 0x1) ? "RT767" : "RT766"); -+ -+ /* check function status and initialize if needed */ -+ if (rt766->uaj_func_data) -+ rt766_func_initialize(rt766, rt766->uaj_func_data); -+ if (rt766->sa_func_data) -+ rt766_func_initialize(rt766, rt766->sa_func_data); -+ if (rt766->sm_func_data) -+ rt766_func_initialize(rt766, rt766->sm_func_data); -+ if (rt766->hid_func_data) -+ rt766_func_initialize(rt766, rt766->hid_func_data); -+ -+ if (rt766->first_hw_init) { -+ regcache_cache_bypass(rt766->regmap, false); -+ regcache_mark_dirty(rt766->regmap); -+ } else { -+ rt766->first_hw_init = true; -+ } -+ -+ /* Mark Slave initialization complete */ -+ rt766->hw_init = true; -+ -+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); -+ -+ pm_runtime_put_autosuspend(&slave->dev); -+ -+ return 0; -+} -+ -+MODULE_DESCRIPTION("ASoC RT766 SDCA SDW driver"); -+MODULE_AUTHOR("Shuming Fan "); -+MODULE_LICENSE("GPL"); -+MODULE_IMPORT_NS("SND_SOC_SDCA"); -diff --git a/sound/soc/codecs/rt766-sdca.h b/sound/soc/codecs/rt766-sdca.h -new file mode 100644 ---- /dev/null -+++ b/sound/soc/codecs/rt766-sdca.h -@@ -0,0 +1,140 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * rt766-sdca.h -- RT766 SDCA ALSA SoC audio driver header -+ * -+ * Copyright(c) 2026 Realtek Semiconductor Corp. -+ */ -+ -+#ifndef __RT766_H__ -+#define __RT766_H__ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+struct rt766_sdca_priv { -+ struct regmap *regmap; -+ struct snd_soc_component *component; -+ struct sdw_slave *slave; -+ bool hw_init; -+ bool first_hw_init; -+ struct snd_soc_jack *hs_jack; -+ struct mutex disable_irq_lock; /* SDCA irq lock protection */ -+ bool disable_irq; -+ int jack_type; -+ bool fu41_dapm_mute; -+ bool fu41_mixer_l_mute; -+ bool fu41_mixer_r_mute; -+ bool fu113_dapm_mute; -+ bool fu113_mixer_mute[4]; -+ bool fu21_dapm_mute; -+ bool fu21_mixer_l_mute; -+ bool fu21_mixer_r_mute; -+ bool fu36_dapm_mute; -+ bool fu36_mixer_l_mute; -+ bool fu36_mixer_r_mute; -+ struct sdca_function_data *uaj_func_data; -+ struct sdca_function_data *sm_func_data; -+ struct sdca_function_data *sa_func_data; -+ struct sdca_function_data *hid_func_data; -+ struct sdca_interrupt_info *irq_info; -+ struct hid_device *hid; -+}; -+ -+/* vendor registers */ -+#define RT766_VERSION_ID 0xc404 -+#define RT766_DEV_ID1 0xc405 -+#define RT766_DEV_ID0 0xc406 -+#define RT766_BOND_LATCH_ID 0xc407 -+ -+#define RT766_HP_POWER_STATE 0x1000004 -+#define RT766_HP_FSM_CTL2_1 0x100000d -+ -+/* MCU Patch address */ -+#define RT766_MCU_PATCH_ADDR1_START 0x10010000 -+#define RT766_MCU_PATCH_ADDR1_END 0x10011fff -+#define RT766_MCU_PATCH_ADDR2_START 0x10020000 -+#define RT766_MCU_PATCH_ADDR2_END 0x10023fff -+ -+/* Buffer address for HID */ -+#define RT766_BUF_ADDR_HID1 0x44030000 -+#define RT766_BUF_ADDR_HID2 0x44030020 -+ -+/* SDCA (Channel) */ -+#define RT766_CH_1 0x01 -+#define RT766_CH_2 0x02 -+#define RT766_CH_3 0x03 -+#define RT766_CH_4 0x04 -+ -+/* RT766 SDCA Control - function number */ -+#define RT766_FUNC_NUM_UAJ 0x01 -+#define RT766_FUNC_NUM_MIC 0x02 -+#define RT766_FUNC_NUM_HID 0x03 -+#define RT766_FUNC_NUM_AMP 0x04 -+ -+/* RT766 SDCA entity */ -+#define RT766_SDCA_ENT_0 0x00 -+#define RT766_SDCA_ENT_HID101 0x01 -+#define RT766_SDCA_ENT_GE49 0x49 -+#define RT766_SDCA_ENT_USER_FU41 0x05 -+#define RT766_SDCA_ENT_USER_FU36 0x0f -+#define RT766_SDCA_ENT_USER_FU21 0x03 -+#define RT766_SDCA_ENT_USER_FU113 0x30 -+#define RT766_SDCA_ENT_PDE23 0x33 -+#define RT766_SDCA_ENT_PDE47 0x28 -+#define RT766_SDCA_ENT_PDE11 0x2a -+#define RT766_SDCA_ENT_PDE34 0x29 -+#define RT766_SDCA_ENT_CS41 0x01 -+#define RT766_SDCA_ENT_CS36 0x11 -+#define RT766_SDCA_ENT_CS113 0x12 -+#define RT766_SDCA_ENT_CS21 0x21 -+#define RT766_SDCA_ENT_PLATFORM_FU33 0x44 -+#define RT766_SDCA_ENT_PPU21 0x04 -+ -+/* sample frequency index */ -+#define RT766_SDCA_RATE_44100HZ 0x08 -+#define RT766_SDCA_RATE_48000HZ 0x09 -+#define RT766_SDCA_RATE_96000HZ 0x0b -+#define RT766_SDCA_RATE_192000HZ 0x0d -+ -+/* SDCA Register macros */ -+#define RT766_MUTE_REG(func, fu, ch) \ -+ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##fu, SDCA_CTL_FU_MUTE, RT766_CH_##ch) -+ -+#define RT766_VOLUME_REG(func, fu, ch) \ -+ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##fu, SDCA_CTL_FU_CHANNEL_VOLUME, RT766_CH_##ch) -+ -+#define RT766_GAIN_REG(func, fu, ch) \ -+ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##fu, SDCA_CTL_FU_GAIN, RT766_CH_##ch) -+ -+#define RT766_PDE_REQ_REG(func, pde) \ -+ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##pde, SDCA_CTL_PDE_REQUESTED_PS, 0) -+ -+#define RT766_PDE_ACTUAL_REG(func, pde) \ -+ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##pde, SDCA_CTL_PDE_ACTUAL_PS, 0) -+ -+#define RT766_FUNC_STATUS_REG(func) \ -+ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_0, SDCA_CTL_ENTITY_0_FUNCTION_STATUS, 0) -+ -+#define RT766_SDCA_CTL(func, ent, ctl) \ -+ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##ent, ctl, 0) -+ -+enum { -+ RT766_AIF1, -+ RT766_AIF2, -+ RT766_AIF3, -+}; -+ -+enum { -+ RT766_DAI_UAJ, -+ RT766_DAI_AMP, -+ RT766_DAI_MIC, -+}; -+ -+int rt766_sdca_io_init(struct device *dev, struct sdw_slave *slave); -+int rt766_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave *slave); -+#endif /* __RT766_H__ */ -diff --git a/sound/soc/codecs/rt9120.c b/sound/soc/codecs/rt9120.c ---- a/sound/soc/codecs/rt9120.c -+++ b/sound/soc/codecs/rt9120.c -@@ -606,12 +606,19 @@ static int rt9120_runtime_suspend(struct device *dev) - static int rt9120_runtime_resume(struct device *dev) - { - struct rt9120_data *data = dev_get_drvdata(dev); -+ int ret; - - if (data->pwdnn_gpio) { - gpiod_set_value(data->pwdnn_gpio, 1); - msleep(RT9120_CHIPON_WAITMS); - regcache_cache_only(data->regmap, false); -- regcache_sync(data->regmap); -+ ret = regcache_sync(data->regmap); -+ if (ret) { -+ regcache_cache_only(data->regmap, true); -+ regcache_mark_dirty(data->regmap); -+ gpiod_set_value(data->pwdnn_gpio, 0); -+ return ret; -+ } - } - - return 0; -diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c ---- a/sound/soc/codecs/sigmadsp.c -+++ b/sound/soc/codecs/sigmadsp.c -@@ -5,6 +5,7 @@ - * Copyright 2009-2014 Analog Devices Inc. - */ - -+#include - #include - #include - #include -@@ -135,7 +136,7 @@ static int sigmadsp_ctrl_put(struct snd_kcontrol *kcontrol, - uint8_t *data; - int ret = 0; - -- mutex_lock(&sigmadsp->lock); -+ guard(mutex)(&sigmadsp->lock); - - data = ucontrol->value.bytes.data; - -@@ -148,8 +149,6 @@ static int sigmadsp_ctrl_put(struct snd_kcontrol *kcontrol, - ctrl->cached = true; - } - -- mutex_unlock(&sigmadsp->lock); -- - return ret; - } - -@@ -160,7 +159,7 @@ static int sigmadsp_ctrl_get(struct snd_kcontrol *kcontrol, - struct sigmadsp *sigmadsp = snd_kcontrol_chip(kcontrol); - int ret = 0; - -- mutex_lock(&sigmadsp->lock); -+ guard(mutex)(&sigmadsp->lock); - - if (!ctrl->cached) { - ret = sigmadsp_read(sigmadsp, ctrl->addr, ctrl->cache, -@@ -174,8 +173,6 @@ static int sigmadsp_ctrl_get(struct snd_kcontrol *kcontrol, - ctrl->num_bytes); - } - -- mutex_unlock(&sigmadsp->lock); -- - return ret; - } - -@@ -487,7 +484,7 @@ static void devm_sigmadsp_release(struct device *dev, void *res) - static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) - { - const struct sigma_firmware_header *ssfw_head; -- const struct firmware *fw; -+ const struct firmware *fw __free(firmware) = NULL; - int ret; - u32 crc; - -@@ -495,7 +492,7 @@ static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) - ret = request_firmware(&fw, name, sigmadsp->dev); - if (ret) { - pr_debug("%s: request_firmware() failed with %i\n", __func__, ret); -- goto done; -+ return ret; - } - - /* then verify the header */ -@@ -509,13 +506,13 @@ static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) - */ - if (fw->size < sizeof(*ssfw_head) || fw->size >= 0x4000000) { - dev_err(sigmadsp->dev, "Failed to load firmware: Invalid size\n"); -- goto done; -+ return -EINVAL; - } - - ssfw_head = (void *)fw->data; - if (memcmp(ssfw_head->magic, SIGMA_MAGIC, ARRAY_SIZE(ssfw_head->magic))) { - dev_err(sigmadsp->dev, "Failed to load firmware: Invalid magic\n"); -- goto done; -+ return -EINVAL; - } - - crc = crc32(0, fw->data + sizeof(*ssfw_head), -@@ -524,7 +521,7 @@ static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) - if (crc != le32_to_cpu(ssfw_head->crc)) { - dev_err(sigmadsp->dev, "Failed to load firmware: Wrong crc checksum: expected %x got %x\n", - le32_to_cpu(ssfw_head->crc), crc); -- goto done; -+ return -EINVAL; - } - - switch (ssfw_head->version) { -@@ -545,9 +542,6 @@ static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) - if (ret) - sigmadsp_firmware_release(sigmadsp); - --done: -- release_firmware(fw); -- - return ret; - } - -@@ -677,10 +671,10 @@ static void sigmadsp_activate_ctrl(struct sigmadsp *sigmadsp, - return; - changed = snd_ctl_activate_id(card, &ctrl->kcontrol->id, active); - if (active && changed > 0) { -- mutex_lock(&sigmadsp->lock); -- if (ctrl->cached) -- sigmadsp_ctrl_write(sigmadsp, ctrl, ctrl->cache); -- mutex_unlock(&sigmadsp->lock); -+ scoped_guard(mutex, &sigmadsp->lock) { -+ if (ctrl->cached) -+ sigmadsp_ctrl_write(sigmadsp, ctrl, ctrl->cache); -+ } - } - } - -diff --git a/sound/soc/codecs/sma1303.c b/sound/soc/codecs/sma1303.c ---- a/sound/soc/codecs/sma1303.c -+++ b/sound/soc/codecs/sma1303.c -@@ -1777,6 +1777,8 @@ static void sma1303_i2c_remove(struct i2c_client *client) - struct sma1303_priv *sma1303 = - (struct sma1303_priv *) i2c_get_clientdata(client); - -+ if (sma1303->attr_grp) -+ sysfs_remove_group(sma1303->kobj, sma1303->attr_grp); - cancel_delayed_work_sync(&sma1303->check_fault_work); - } - -diff --git a/sound/soc/codecs/sma1307.c b/sound/soc/codecs/sma1307.c ---- a/sound/soc/codecs/sma1307.c -+++ b/sound/soc/codecs/sma1307.c -@@ -1690,7 +1690,7 @@ static void sma1307_check_fault_worker(struct work_struct *work) - - static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *file) - { -- const struct firmware *fw; -+ const struct firmware *fw __free(firmware) = NULL; - int size, offset, num_mode; - int ret; - -@@ -1703,22 +1703,18 @@ static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *fil - return; - } else if ((fw->size) < SMA1307_SETTING_HEADER_SIZE) { - dev_err(sma1307->dev, "%s: Invalid file\n", __func__); -- release_firmware(fw); - sma1307->set.status = false; - return; - } - - int *data __free(kfree) = kzalloc(fw->size, GFP_KERNEL); - if (!data) { -- release_firmware(fw); - sma1307->set.status = false; - return; - } - size = fw->size >> 2; - memcpy(data, fw->data, fw->size); - -- release_firmware(fw); -- - /* HEADER */ - sma1307->set.header_size = SMA1307_SETTING_HEADER_SIZE; - sma1307->set.checksum = data[sma1307->set.header_size - 2]; -diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c ---- a/sound/soc/codecs/sta32x.c -+++ b/sound/soc/codecs/sta32x.c -@@ -17,6 +17,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -269,9 +270,9 @@ static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol, - int numcoef = kcontrol->private_value >> 16; - int index = kcontrol->private_value & 0xffff; - unsigned int cfud, val; -- int i, ret = 0; -+ int i; - -- mutex_lock(&sta32x->coeff_lock); -+ guard(mutex)(&sta32x->coeff_lock); - - /* preserve reserved bits in STA32X_CFUD */ - regmap_read(sta32x->regmap, STA32X_CFUD, &cfud); -@@ -283,24 +284,20 @@ static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol, - regmap_write(sta32x->regmap, STA32X_CFUD, cfud); - - regmap_write(sta32x->regmap, STA32X_CFADDR2, index); -- if (numcoef == 1) { -+ if (numcoef == 1) - regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x04); -- } else if (numcoef == 5) { -+ else if (numcoef == 5) - regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x08); -- } else { -- ret = -EINVAL; -- goto exit_unlock; -- } -+ else -+ return -EINVAL; -+ - - for (i = 0; i < 3 * numcoef; i++) { - regmap_read(sta32x->regmap, STA32X_B1CF1 + i, &val); - ucontrol->value.bytes.data[i] = val; - } - --exit_unlock: -- mutex_unlock(&sta32x->coeff_lock); -- -- return ret; -+ return 0; - } - - static int sta32x_coefficient_put(struct snd_kcontrol *kcontrol, -diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c ---- a/sound/soc/codecs/sta350.c -+++ b/sound/soc/codecs/sta350.c -@@ -16,6 +16,7 @@ - - #define pr_fmt(fmt) KBUILD_MODNAME ":%s:%d: " fmt, __func__, __LINE__ - -+#include - #include - #include - #include -@@ -306,9 +307,9 @@ static int sta350_coefficient_get(struct snd_kcontrol *kcontrol, - int numcoef = kcontrol->private_value >> 16; - int index = kcontrol->private_value & 0xffff; - unsigned int cfud, val; -- int i, ret = 0; -+ int i; - -- mutex_lock(&sta350->coeff_lock); -+ guard(mutex)(&sta350->coeff_lock); - - /* preserve reserved bits in STA350_CFUD */ - regmap_read(sta350->regmap, STA350_CFUD, &cfud); -@@ -320,24 +321,19 @@ static int sta350_coefficient_get(struct snd_kcontrol *kcontrol, - regmap_write(sta350->regmap, STA350_CFUD, cfud); - - regmap_write(sta350->regmap, STA350_CFADDR2, index); -- if (numcoef == 1) { -+ if (numcoef == 1) - regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x04); -- } else if (numcoef == 5) { -+ else if (numcoef == 5) - regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x08); -- } else { -- ret = -EINVAL; -- goto exit_unlock; -- } -+ else -+ return -EINVAL; - - for (i = 0; i < 3 * numcoef; i++) { - regmap_read(sta350->regmap, STA350_B1CF1 + i, &val); - ucontrol->value.bytes.data[i] = val; - } - --exit_unlock: -- mutex_unlock(&sta350->coeff_lock); -- -- return ret; -+ return 0; - } - - static int sta350_coefficient_put(struct snd_kcontrol *kcontrol, -diff --git a/sound/soc/codecs/tac5xx2-sdw.c b/sound/soc/codecs/tac5xx2-sdw.c ---- a/sound/soc/codecs/tac5xx2-sdw.c -+++ b/sound/soc/codecs/tac5xx2-sdw.c -@@ -88,6 +88,9 @@ - #define TAC_FW_FILE_HDR 20 - #define TAC_MAX_FW_CHUNKS 512 - -+#define TAC_UAJ_PREP_CONNECTED 0xff -+#define TAC_UAJ_PREP_DISCONNECTED 0xdf -+ - struct tac_fw_hdr { - u32 size; - u32 version_offset; -@@ -138,6 +141,7 @@ struct tac5xx2_prv { - bool hw_init; - bool first_hw_init_done; - u32 part_id; -+ u32 rev_id; - struct snd_soc_jack *hs_jack; - int jack_type; - /* Custom fw binary. UMP File Download is not used. */ -@@ -261,7 +265,6 @@ static const struct reg_default tac_reg_default[] = { - {TAC_REG_SDW(0, 0, 0x78), 0x0}, - {TAC_REG_SDW(0, 0, 0x7b), 0x0}, - {TAC_REG_SDW(0, 0, 0x7c), 0xd0}, -- {TAC_REG_SDW(0, 0, 0x7d), 0x0}, - {TAC_REG_SDW(0, 0, 0x7e), 0x0}, - {TAC_REG_SDW(0, 1, 0x1), 0x0}, - {TAC_REG_SDW(0, 1, 0x2), 0x0}, -@@ -507,18 +510,6 @@ static const struct regmap_config tac_regmap = { - .use_single_write = true, - }; - --/* Check if device has DSP algo that needs status monitoring */ --static bool tac_has_dsp_algo(struct tac5xx2_prv *tac_dev) --{ -- switch (tac_dev->part_id) { -- case 0x5682: -- case 0x2883: -- return true; -- default: -- return false; -- } --} -- - /* Check if device has UAJ (Universal Audio Jack) support */ - static bool tac_has_uaj_support(struct tac5xx2_prv *tac_dev) - { -@@ -800,7 +791,7 @@ static int tac_sdw_hw_params(struct snd_pcm_substream *substream, - return ret; - } - -- ret = sdca_asoc_pde_poll_actual_ps(tac_dev->dev, tac_dev->regmap, function_id, pde_entity, -+ ret = sdca_asoc_pde_poll_actual_ps(tac_dev->regmap, function_id, pde_entity, - SDCA_PDE_PS3, SDCA_PDE_PS0, NULL, 0); - if (ret) - dev_err(tac_dev->dev, "failed to transition func %d, pde %d from PS3 -> PS0, err=%d\n", -@@ -847,7 +838,7 @@ static int tac_sdw_pcm_hw_free(struct snd_pcm_substream *substream, - return ret; - } - -- ret = sdca_asoc_pde_poll_actual_ps(tac_dev->dev, tac_dev->regmap, function_id, -+ ret = sdca_asoc_pde_poll_actual_ps(tac_dev->regmap, function_id, - pde_entity, SDCA_PDE_PS0, SDCA_PDE_PS3, - NULL, 0); - if (ret) -@@ -942,6 +933,7 @@ static int tac5xx2_sdca_button_detect(struct tac5xx2_prv *tac_dev) - static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev) - { - int val, ret; -+ u8 jack_prep; - - ret = regmap_read(tac_dev->regmap, - SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_GE35, -@@ -951,6 +943,8 @@ static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev) - return ret; - } - -+ jack_prep = TAC_UAJ_PREP_CONNECTED; -+ - switch (val) { - case 4: - tac_dev->jack_type = SND_JACK_MICROPHONE; -@@ -964,6 +958,7 @@ static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev) - case 0: - default: - tac_dev->jack_type = 0; -+ jack_prep = TAC_UAJ_PREP_DISCONNECTED; - break; - } - -@@ -973,26 +968,44 @@ static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev) - if (ret) - dev_err(tac_dev->dev, "Failed to update the jack type to device"); - -+ /* -+ * When uaj is uplugged and booted, we end up with the channel prepare -+ * timeout error for the uaj ports. This writes allows the channel prepare -+ * to succeed when the uaj is not plugged in. -+ */ -+ if (tac_dev->rev_id >= 0x30) { -+ ret = regmap_write(tac_dev->regmap, TAC_REG_SDW(0, 3, 127), jack_prep); -+ if (ret) -+ dev_warn(tac_dev->dev, "Failed to write jack_prep register: %d\n", ret); -+ } -+ - return 0; - } - - static int tac5xx2_jack_init(struct tac5xx2_prv *tac_dev) - { -+ u32 jd_int_mask, hid_int_mask; - int ret = 0; - -+ if (tac_dev->rev_id >= 0x30) { -+ jd_int_mask = SDW_SCP_SDCA_INTMASK_SDCA_12; -+ hid_int_mask = SDW_SCP_SDCA_INTMASK_SDCA_17; -+ } else { -+ jd_int_mask = SDW_SCP_SDCA_INTMASK_SDCA_11; -+ hid_int_mask = SDW_SCP_SDCA_INTMASK_SDCA_16; -+ } -+ - if (!tac_dev->hs_jack) - goto disable_interrupts; - -- ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK2, -- SDW_SCP_SDCA_INTMASK_SDCA_11); -+ ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK2, jd_int_mask); - if (ret) { - dev_err(tac_dev->dev, - "Failed to register jack detection interrupt: %d\n", ret); - goto disable_interrupts; - } - -- ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK3, -- SDW_SCP_SDCA_INTMASK_SDCA_16); -+ ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK3, hid_int_mask); - if (ret) { - dev_err(tac_dev->dev, - "Failed to register for button detect interrupt: %d\n", ret); -@@ -1051,6 +1064,7 @@ static int tac_interrupt_callback(struct sdw_slave *slave, - unsigned int sdca_int2, sdca_int3, jack_report_mask = 0; - struct tac5xx2_prv *tac_dev = dev_get_drvdata(&slave->dev); - struct device *dev = &slave->dev; -+ u32 headset_detect_chk, hid_detect_chk; - int btn_type = 0; - int ret = 0; - -@@ -1081,14 +1095,22 @@ static int tac_interrupt_callback(struct sdw_slave *slave, - dev_dbg(dev, "SDCA_INT2: 0x%02x, SDCA_INT3: 0x%02x\n", - sdca_int2, sdca_int3); - -- if (sdca_int2 & SDW_SCP_SDCA_INT_SDCA_11) { -+ if (tac_dev->rev_id >= 0x30) { -+ headset_detect_chk = SDW_SCP_SDCA_INT_SDCA_12; -+ hid_detect_chk = SDW_SCP_SDCA_INT_SDCA_17; -+ } else { -+ headset_detect_chk = SDW_SCP_SDCA_INT_SDCA_11; -+ hid_detect_chk = SDW_SCP_SDCA_INT_SDCA_16; -+ } -+ -+ if (sdca_int2 & headset_detect_chk) { - ret = tac5xx2_sdca_headset_detect(tac_dev); - if (ret < 0) - goto clear; - jack_report_mask |= SND_JACK_HEADSET; - } - -- if (sdca_int3 & SDW_SCP_SDCA_INT_SDCA_16) { -+ if (sdca_int3 & hid_detect_chk) { - btn_type = tac5xx2_sdca_button_detect(tac_dev); - if (btn_type < 0) - btn_type = 0; -@@ -1695,6 +1717,15 @@ static int tac_io_init(struct device *dev, struct sdw_slave *slave, bool first) - goto io_init_err; - } - -+ if (!tac_dev->rev_id) { -+ ret = regmap_read(tac_dev->regmap, TAC_REV_ID, &tac_dev->rev_id); -+ if (ret) { -+ dev_err(tac_dev->dev, "failed to rev id, err=%d\n", ret); -+ goto io_init_err; -+ } -+ dev_dbg(tac_dev->dev, "detected rev_id 0x%x", tac_dev->rev_id); -+ } -+ - if (tac_dev->fw_files && tac_dev->fw_file_cnt > 0) { - ret = tac_download_fw_to_hw(tac_dev); - if (ret) { -@@ -1940,7 +1971,7 @@ static s32 tac_sdw_probe(struct sdw_slave *peripheral, - "failed to allocate %s function data", - func_name); - function_data->desc = &peripheral->sdca_data.function[i]; -- ret = sdca_parse_function(dev, peripheral, function_data); -+ ret = sdca_parse_function(dev, function_data); - if (!ret) - *func_ptr = function_data; - else -@@ -1959,6 +1990,7 @@ static s32 tac_sdw_probe(struct sdw_slave *peripheral, - tac_dev->hw_init = false; - tac_dev->first_hw_init_done = false; - tac_dev->part_id = id->part_id; -+ tac_dev->rev_id = 0x0; - dev_set_drvdata(dev, tac_dev); - - regmap = devm_regmap_init_sdw_mbq_cfg(&peripheral->dev, peripheral, -@@ -1972,17 +2004,13 @@ static s32 tac_sdw_probe(struct sdw_slave *peripheral, - tac_dev->jack_type = 0; - init_completion(&tac_dev->fw_caching_complete); - -- if (tac_has_dsp_algo(tac_dev)) { -- tac_generate_fw_name(peripheral, tac_dev->fw_binaryname, -- sizeof(tac_dev->fw_binaryname)); -+ tac_generate_fw_name(peripheral, tac_dev->fw_binaryname, -+ sizeof(tac_dev->fw_binaryname)); - -- ret = tac_load_and_cache_firmware_async(tac_dev); -- if (ret) { -- complete_all(&tac_dev->fw_caching_complete); -- dev_dbg(dev, "failed to load fw: %d, use rom mode\n", ret); -- } -- } else { -+ ret = tac_load_and_cache_firmware_async(tac_dev); -+ if (ret) { - complete_all(&tac_dev->fw_caching_complete); -+ dev_dbg(dev, "failed to load fw: %d, use rom mode\n", ret); - } - - ret = tac_init(tac_dev); -diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c ---- a/sound/soc/codecs/tas2552.c -+++ b/sound/soc/codecs/tas2552.c -@@ -495,13 +495,21 @@ static int tas2552_runtime_suspend(struct device *dev) - static int tas2552_runtime_resume(struct device *dev) - { - struct tas2552_data *tas2552 = dev_get_drvdata(dev); -+ int ret; - - gpiod_set_value_cansleep(tas2552->enable_gpio, 1); - - tas2552_sw_shutdown(tas2552, 0); - - regcache_cache_only(tas2552->regmap, false); -- regcache_sync(tas2552->regmap); -+ ret = regcache_sync(tas2552->regmap); -+ if (ret) { -+ regcache_cache_only(tas2552->regmap, true); -+ regcache_mark_dirty(tas2552->regmap); -+ tas2552_sw_shutdown(tas2552, 1); -+ gpiod_set_value_cansleep(tas2552->enable_gpio, 0); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/tas2781-comlib-i2c.c b/sound/soc/codecs/tas2781-comlib-i2c.c ---- a/sound/soc/codecs/tas2781-comlib-i2c.c -+++ b/sound/soc/codecs/tas2781-comlib-i2c.c -@@ -6,6 +6,7 @@ - // - // Author: Shenghao Ding - -+#include - #include - #include - #include -@@ -342,7 +343,7 @@ int tascodec_init(struct tasdevice_priv *tas_priv, void *codec, - /* Codec Lock Hold to ensure that codec_probe and firmware parsing and - * loading do not simultaneously execute. - */ -- mutex_lock(&tas_priv->codec_lock); -+ guard(mutex)(&tas_priv->codec_lock); - - if (tas_priv->name_prefix) - scnprintf(tas_priv->rca_binaryname, 64, "%s-%sRCA%d.bin", -@@ -360,8 +361,6 @@ int tascodec_init(struct tasdevice_priv *tas_priv, void *codec, - dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n", - ret); - -- /* Codec Lock Release*/ -- mutex_unlock(&tas_priv->codec_lock); - return ret; - } - EXPORT_SYMBOL_GPL(tascodec_init); diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c --- a/sound/soc/codecs/tas2781-fmwlib.c +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -27808,6973 +11228,10 @@ diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib } else if (ret < 0) /*err in current device, skip it*/ break; } -@@ -2243,7 +2253,7 @@ int tas2781_load_calibration(void *context, char *file_name, - { - struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; - struct tasdevice *tasdev = &(tas_priv->tasdevice[i]); -- const struct firmware *fw_entry = NULL; -+ const struct firmware *fw_entry __free(firmware) = NULL; - struct tasdevice_fw *tas_fmw; - struct firmware fmw; - int offset = 0; -@@ -2253,60 +2263,50 @@ int tas2781_load_calibration(void *context, char *file_name, - if (ret) { - dev_err(tas_priv->dev, "%s: Request firmware %s failed\n", - __func__, file_name); -- goto out; -+ return ret; - } - - if (!fw_entry->size) { - dev_err(tas_priv->dev, "%s: file read error: size = %lu\n", - __func__, (unsigned long)fw_entry->size); -- ret = -EINVAL; -- goto out; -+ return -EINVAL; - } - fmw.size = fw_entry->size; - fmw.data = fw_entry->data; - - tas_fmw = tasdev->cali_data_fmw = kzalloc_obj(struct tasdevice_fw); -- if (!tasdev->cali_data_fmw) { -- ret = -ENOMEM; -- goto out; -- } -+ if (!tasdev->cali_data_fmw) -+ return -ENOMEM; -+ - tas_fmw->dev = tas_priv->dev; - offset = fw_parse_header(tas_priv, tas_fmw, &fmw, offset); - if (offset == -EINVAL) { - dev_err(tas_priv->dev, "fw_parse_header EXIT!\n"); -- ret = offset; -- goto out; -+ return -EINVAL; - } - offset = fw_parse_variable_hdr_cal(tas_priv, tas_fmw, &fmw, offset); - if (offset == -EINVAL) { - dev_err(tas_priv->dev, - "%s: fw_parse_variable_header_cal EXIT!\n", __func__); -- ret = offset; -- goto out; -+ return -EINVAL; - } - offset = fw_parse_program_data(tas_priv, tas_fmw, &fmw, offset); - if (offset < 0) { - dev_err(tas_priv->dev, "fw_parse_program_data EXIT!\n"); -- ret = offset; -- goto out; -+ return offset; - } - offset = fw_parse_configuration_data(tas_priv, tas_fmw, &fmw, offset); - if (offset < 0) { - dev_err(tas_priv->dev, "fw_parse_configuration_data EXIT!\n"); -- ret = offset; -- goto out; -+ return offset; - } - offset = fw_parse_calibration_data(tas_priv, tas_fmw, &fmw, offset); - if (offset < 0) { - dev_err(tas_priv->dev, "fw_parse_calibration_data EXIT!\n"); -- ret = offset; -- goto out; -+ return offset; - } - --out: -- release_firmware(fw_entry); -- -- return ret; -+ return 0; - } - EXPORT_SYMBOL_NS_GPL(tas2781_load_calibration, "SND_SOC_TAS2781_FMWLIB"); - -@@ -2399,7 +2399,7 @@ static int tasdevice_dspfw_ready(const struct firmware *fmw, - int tasdevice_dsp_parser(void *context) - { - struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; -- const struct firmware *fw_entry; -+ const struct firmware *fw_entry __free(firmware) = NULL; - int ret; - - ret = request_firmware(&fw_entry, tas_priv->coef_binaryname, -@@ -2407,15 +2407,10 @@ int tasdevice_dsp_parser(void *context) - if (ret) { - dev_err(tas_priv->dev, "%s: load %s error\n", __func__, - tas_priv->coef_binaryname); -- goto out; -+ return ret; - } - -- ret = tasdevice_dspfw_ready(fw_entry, tas_priv); -- release_firmware(fw_entry); -- fw_entry = NULL; -- --out: -- return ret; -+ return tasdevice_dspfw_ready(fw_entry, tas_priv); - } - EXPORT_SYMBOL_NS_GPL(tasdevice_dsp_parser, "SND_SOC_TAS2781_FMWLIB"); - -@@ -2797,11 +2792,12 @@ int tasdevice_prmg_load(void *context, int prm_no) - } - EXPORT_SYMBOL_NS_GPL(tasdevice_prmg_load, "SND_SOC_TAS2781_FMWLIB"); - --void tasdevice_tuning_switch(void *context, int state) -+void tasdevice_tuning_switch(void *context, int state, bool is_cap) - { - struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; - struct tasdevice_fw *tas_fmw = tas_priv->fmw; -- int profile_cfg_id = tas_priv->rcabin.profile_cfg_id; -+ int profile_cfg_id = is_cap ? tas_priv->rcabin.capture_profile_id : -+ tas_priv->rcabin.profile_cfg_id; - - /* - * Only RCA-based Playback can still work with no dsp program running -@@ -2818,7 +2814,6 @@ void tasdevice_tuning_switch(void *context, int state) - if (state == 0) { - if (tas_fmw && tas_priv->cur_prog < tas_fmw->nr_programs) { - /* dsp mode or tuning mode */ -- profile_cfg_id = tas_priv->rcabin.profile_cfg_id; - tasdevice_select_tuningprm_cfg(tas_priv, - tas_priv->cur_prog, tas_priv->cur_conf, - profile_cfg_id); -diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c ---- a/sound/soc/codecs/tas2781-i2c.c -+++ b/sound/soc/codecs/tas2781-i2c.c -@@ -13,6 +13,7 @@ - // Author: Kevin Lu - // - -+#include - #include - #include - #include -@@ -617,6 +618,7 @@ static int tasdev_calib_stop_put(struct snd_kcontrol *kcontrol, - { - struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); - struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp); -+ int i; - - guard(mutex)(&priv->codec_lock); - if (priv->chip_id == TAS2563) -@@ -624,6 +626,14 @@ static int tasdev_calib_stop_put(struct snd_kcontrol *kcontrol, - else - tas2781_calib_stop_put(priv); - -+ /* -+ * Set reloading-firmware flag after calibration, the flag will work -+ * during next playback, then set to the program id after reloading -+ * firmware. -+ */ -+ for (i = 0; i < priv->ndev; i++) -+ priv->tasdevice[i].cur_prog = -1; -+ - return 1; - } - -@@ -843,12 +853,12 @@ static int tasdevice_digital_gain_get( - unsigned char data[4]; - int ret; - -- mutex_lock(&tas_dev->codec_lock); -+ guard(mutex)(&tas_dev->codec_lock); - /* Read the primary device */ - ret = tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4); - if (ret) { - dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__); -- goto out; -+ return ret; - } - - target = get_unaligned_be32(&data[0]); -@@ -868,8 +878,7 @@ static int tasdevice_digital_gain_get( - /* find out the member same as or closer to the current volume */ - ucontrol->value.integer.value[0] = - abs(target - ar_l) <= abs(target - ar_r) ? l : r; --out: -- mutex_unlock(&tas_dev->codec_lock); -+ - return 0; - } - -@@ -882,29 +891,26 @@ static int tasdevice_digital_gain_put( - struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol); - struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec); - int vol = ucontrol->value.integer.value[0]; -- int status = 0, max = mc->max, rc = 1; -+ int status = 0, max = mc->max; - int i, ret; - unsigned int reg = mc->reg; - unsigned int volrd, volwr; - unsigned char data[4]; - - vol = clamp(vol, 0, max); -- mutex_lock(&tas_dev->codec_lock); -+ guard(mutex)(&tas_dev->codec_lock); - /* Read the primary device */ - ret = tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4); - if (ret) { - dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__); -- rc = -1; -- goto out; -+ return -1; - } - - volrd = get_unaligned_be32(&data[0]); - volwr = get_unaligned_be32(tas_dev->dvc_tlv_table[vol]); - -- if (volrd == volwr) { -- rc = 0; -- goto out; -- } -+ if (volrd == volwr) -+ return 0; - - for (i = 0; i < tas_dev->ndev; i++) { - ret = tasdevice_dev_bulk_write(tas_dev, i, reg, -@@ -918,10 +924,9 @@ static int tasdevice_digital_gain_put( - } - - if (status) -- rc = -1; --out: -- mutex_unlock(&tas_dev->codec_lock); -- return rc; -+ return -1; -+ -+ return 1; - } - - static const struct snd_kcontrol_new tasdevice_cali_controls[] = { -@@ -995,6 +1000,57 @@ static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol, - return ret; - } - -+/** -+ * tasdevice_set_capture_profile_id - Set runtime capture profile index via -+ * ALSA control -+ * @kcontrol: ALSA kcontrol handle that triggers this operation -+ * @ucontrol: User space control value carrying the new profile index -+ * -+ * This mixer control handler validates the user-provided capture profile ID -+ * against the maximum valid index parsed from the loaded DSP firmware, -+ * then updates the runtime stored capture profile ID only if the new value -+ * differs from the current active one. It will immediately return -EINVAL -+ * if the submitted profile ID falls outside the valid range, including the -+ * edge case that no valid configuration blocks are detected in firmware. -+ * -+ * No actual DSP register write is performed in this handler. The updated -+ * profile ID will be applied to the hardware when the next ALSA capture -+ * stream starts up. Caller does not need to take extra codec lock here, -+ * as the ALSA control core already guarantees serialized execution. -+ * -+ * Return: 1 if profile ID value was changed, 0 if no modification needed, -+ * -EINVAL if the input profile ID is out of valid range -+ */ -+static int tasdevice_set_capture_profile_id(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol); -+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); -+ unsigned int user_prof_id = ucontrol->value.integer.value[0]; -+ unsigned int max_valid_id; -+ int ret = 0; -+ -+ /* -+ * Align valid range with the bound defined in -+ * tasdevice_info_profile() -+ */ -+ max_valid_id = tas_priv->rcabin.ncfgs - 1; -+ -+ /* -+ * Reject invalid input including zero total configuration edge -+ * case -+ */ -+ if (tas_priv->rcabin.ncfgs == 0 || user_prof_id > max_valid_id) -+ return -EINVAL; -+ -+ if (tas_priv->rcabin.capture_profile_id != user_prof_id) { -+ tas_priv->rcabin.capture_profile_id = user_prof_id; -+ ret = 1; -+ } -+ -+ return ret; -+} -+ - static int tasdevice_info_active_num(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) - { -@@ -1075,6 +1131,41 @@ static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol, - return 0; - } - -+/** -+ * tasdevice_get_capture_profile_id - Report current active capture profile -+ * ID to user space -+ * @kcontrol: ALSA kcontrol structure passed from ALSA core -+ * @ucontrol: User-space control element value buffer to write the result back -+ * -+ * This function ensures the returned profile ID is always clamped inside the -+ * valid range advertised by the info callback, preventing accidental invalid -+ * values from being exposed to applications even if internal driver state is -+ * temporarily inconsistent. -+ * -+ * Returns 0 on successful fill of the control value, no error conditions -+ * are defined for this getter callback. -+ */ -+static int tasdevice_get_capture_profile_id(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol); -+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); -+ unsigned int max_valid_id, current_prof_id; -+ -+ max_valid_id = tas_priv->rcabin.ncfgs > 0 ? -+ (tas_priv->rcabin.ncfgs - 1U) : 0; -+ -+ /* -+ * Cast current profile id to unsigned to match type with max_valid_id, -+ * avoid signedness mismatch; -+ */ -+ current_prof_id = (unsigned int)tas_priv->rcabin.capture_profile_id; -+ /* Prevent underflow when there are no loaded capture profiles. */ -+ ucontrol->value.integer.value[0] = min(current_prof_id, max_valid_id); -+ -+ return 0; -+} -+ - static int tasdevice_get_chip_id(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) - { -@@ -1117,6 +1208,41 @@ static int tasdevice_create_control(struct tasdevice_priv *tas_priv) - ret = snd_soc_add_component_controls(tas_priv->codec, - prof_ctrls, nr_controls < mix_index ? nr_controls : mix_index); - -+ mix_index = 0; -+ switch (tas_priv->chip_id) { -+ case TAS2563: -+ case TAS2568: -+ case TAS2570: -+ case TAS2572: -+ case TAS2573: -+ case TAS2574: -+ case TAS2781: -+ prof_ctrls = devm_kcalloc(tas_priv->dev, nr_controls, -+ sizeof(prof_ctrls[0]), GFP_KERNEL); -+ if (!prof_ctrls) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ /* Create a mixer item for selecting the capture profile */ -+ name = devm_kstrdup(tas_priv->dev, "Speaker Capture Profile Id", -+ GFP_KERNEL); -+ if (!name) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ prof_ctrls[mix_index].name = name; -+ prof_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; -+ prof_ctrls[mix_index].info = tasdevice_info_profile; -+ prof_ctrls[mix_index].get = tasdevice_get_capture_profile_id; -+ prof_ctrls[mix_index].put = tasdevice_set_capture_profile_id; -+ mix_index++; -+ -+ ret = snd_soc_add_component_controls(tas_priv->codec, -+ prof_ctrls, nr_controls < mix_index ? nr_controls : mix_index); -+ break; -+ } -+ - out: - return ret; - } -@@ -1330,6 +1456,43 @@ static void alpa_cali_update(struct bulk_reg_val *p, - p->val_len = 4; - } - -+static int create_tas2781_cali_start_ktrl(struct tasdevice_priv -+ *priv, struct snd_kcontrol_new *cali_ctrl) -+{ -+ struct soc_bytes_ext *ext_cali_start; -+ char *cali_start_name; -+ -+ ext_cali_start = devm_kzalloc(priv->dev, -+ sizeof(*ext_cali_start), GFP_KERNEL); -+ if (!ext_cali_start) -+ return -ENOMEM; -+ -+ cali_start_name = devm_kstrdup(priv->dev, -+ "Calibration Start", GFP_KERNEL); -+ if (!cali_start_name) -+ return -ENOMEM; -+ /* -+ * package structure for tas2781 ftc start: -+ * Pkg len (1 byte) -+ * Reg id (1 byte, constant 'r') -+ * book, page, register for pilot threshold, pilot tone -+ * and sine gain (12 bytes) -+ * for (i = 0; i < Device-Sum; i++) { -+ * Device #i index_info (1 byte) -+ * Sine gain for Device #i (8 bytes) -+ * } -+ */ -+ ext_cali_start->max = 14 + priv->ndev * 9; -+ cali_ctrl->name = cali_start_name; -+ cali_ctrl->iface = SNDRV_CTL_ELEM_IFACE_MIXER; -+ cali_ctrl->info = snd_soc_bytes_info_ext; -+ cali_ctrl->put = tas2781_calib_start_put; -+ cali_ctrl->get = tasdev_nop_get; -+ cali_ctrl->private_value = (unsigned long)ext_cali_start; -+ -+ return 0; -+} -+ - static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv) - { - struct calidata *cali_data = &priv->cali_data; -@@ -1448,37 +1611,11 @@ static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv) - */ - cali_data->data[0] = 0xff; - if (priv->chip_id == TAS2781) { -- struct soc_bytes_ext *ext_cali_start; -- char *cali_start_name; -- -- ext_cali_start = devm_kzalloc(priv->dev, -- sizeof(*ext_cali_start), GFP_KERNEL); -- if (!ext_cali_start) -- return -ENOMEM; -- -- cali_start_name = devm_kstrdup(priv->dev, -- "Calibration Start", GFP_KERNEL); -- if (!cali_start_name) -- return -ENOMEM; -- /* -- * package structure for tas2781 ftc start: -- * Pkg len (1 byte) -- * Reg id (1 byte, constant 'r') -- * book, page, register for pilot threshold, pilot tone -- * and sine gain (12 bytes) -- * for (i = 0; i < Device-Sum; i++) { -- * Device #i index_info (1 byte) -- * Sine gain for Device #i (8 bytes) -- * } -- */ -- ext_cali_start->max = 14 + priv->ndev * 9; -- cali_ctrls[i].name = cali_start_name; -- cali_ctrls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; -- cali_ctrls[i].info = snd_soc_bytes_info_ext; -- cali_ctrls[i].put = tas2781_calib_start_put; -- cali_ctrls[i].get = tasdev_nop_get; -- cali_ctrls[i].private_value = (unsigned long)ext_cali_start; -+ rc = create_tas2781_cali_start_ktrl(priv, &cali_ctrls[i]); -+ if (rc != 0) -+ return rc; - i++; -+ - } - - return snd_soc_add_component_controls(priv->codec, cali_ctrls, -@@ -1765,13 +1902,25 @@ static int tasdevice_dapm_event(struct snd_soc_dapm_widget *w, - struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); - int state = 0; - -- /* Codec Lock Hold */ -- mutex_lock(&tas_priv->codec_lock); -+ guard(mutex)(&tas_priv->codec_lock); - if (event == SND_SOC_DAPM_PRE_PMD) - state = 1; -- tasdevice_tuning_switch(tas_priv, state); -- /* Codec Lock Release*/ -- mutex_unlock(&tas_priv->codec_lock); -+ tasdevice_tuning_switch(tas_priv, state, false); -+ -+ return 0; -+} -+ -+static int tasdevice_capture_dapm_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *kcontrol, int event) -+{ -+ struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); -+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); -+ int state = 0; -+ -+ guard(mutex)(&tas_priv->codec_lock); -+ if (event == SND_SOC_DAPM_PRE_PMD) -+ state = 1; -+ tasdevice_tuning_switch(tas_priv, state, true); - - return 0; - } -@@ -1779,7 +1928,7 @@ static int tasdevice_dapm_event(struct snd_soc_dapm_widget *w, - static const struct snd_soc_dapm_widget tasdevice_dapm_widgets[] = { - SND_SOC_DAPM_AIF_IN("ASI", "ASI Playback", 0, SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_AIF_OUT_E("ASI OUT", "ASI Capture", 0, SND_SOC_NOPM, -- 0, 0, tasdevice_dapm_event, -+ 0, 0, tasdevice_capture_dapm_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - SND_SOC_DAPM_SPK("SPK", tasdevice_dapm_event), - SND_SOC_DAPM_OUTPUT("OUT"), -diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c ---- a/sound/soc/codecs/tas2783-sdw.c -+++ b/sound/soc/codecs/tas2783-sdw.c -@@ -12,6 +12,7 @@ - // Author: Baojun Xu - // Author: Kevin Lu - -+#include - #include - #include - #include -@@ -494,6 +495,56 @@ static bool tas2783_readable_register(struct device *dev, unsigned int reg) - return tas2783_sdca_mbq_size(dev, reg) > 0; - } - -+static bool tas2783_writeable_register(struct device *dev, unsigned int reg) -+{ -+ /* -+ * The Latency Control of every Entity, together with the Power Domain -+ * actual state and the protection status, is read-only. They are -+ * listed in tas2783_reg_default[] with a placeholder value, so without -+ * this a regcache_sync() would try to write them back and the -+ * peripheral would reject the transaction, aborting the sync. -+ */ -+ switch (reg) { -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU21, 0x10, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU23, 0x10, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU26, 0x10, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_XU22, 0x06, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_XU22, 0x07, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_XU22, 0x08, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS24, 0x02, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS21, 0x02, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS25, 0x02, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS26, 0x02, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS28, 0x02, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_PDE23, 0x10, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_UDMPU23, 0x06, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_SAPU29, 0x05, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_SAPU29, 0x11, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_PPU21, 0x06, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_PPU26, 0x06, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT21, 0x08, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT29, 0x08, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT26, 0x08, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT28, 0x08, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT24, 0x08, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT23, 0x08, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT25, 0x08, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT28, 0x08, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MU26, 0x06, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT127, 0x08, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU127, 0x10, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS127, 0x02, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU21, 0x08, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU21, 0x04, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU26, 0x08, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU26, 0x04, 0): -+ return false; -+ -+ default: -+ return tas2783_sdca_mbq_size(dev, reg) > 0; -+ } -+} -+ - static bool tas2783_volatile_register(struct device *dev, u32 reg) - { - switch (reg) { -@@ -515,6 +566,7 @@ static const struct regmap_config tas_regmap = { - .reg_bits = 32, - .val_bits = 8, - .readable_reg = tas2783_readable_register, -+ .writeable_reg = tas2783_writeable_register, - .volatile_reg = tas2783_volatile_register, - .reg_defaults = tas2783_reg_default, - .num_reg_defaults = ARRAY_SIZE(tas2783_reg_default), -@@ -692,12 +744,12 @@ static s32 tas2783_update_calibdata(struct tas2783_prv *tas_dev) - return 0; - } - -- mutex_lock(&tas_dev->calib_lock); -- ret = tas2783_validate_calibdata(tas_dev, tas_dev->cali_data.data, -- tas_dev->cali_data.read_sz); -- if (!ret) -- tas2783_set_calib_params_to_device(tas_dev, tmp_val); -- mutex_unlock(&tas_dev->calib_lock); -+ scoped_guard(mutex, &tas_dev->calib_lock) { -+ ret = tas2783_validate_calibdata(tas_dev, tas_dev->cali_data.data, -+ tas_dev->cali_data.read_sz); -+ if (!ret) -+ tas2783_set_calib_params_to_device(tas_dev, tmp_val); -+ } - - return ret; - } -@@ -926,22 +978,23 @@ static s32 tas_sdw_hw_params(struct snd_pcm_substream *substream, - dev_err(tas_dev->dev, - "clear latch failed, err=%d", ret); - -- mutex_lock(&tas_dev->pde_lock); -- /* -- * Sometimes, there is error returned during power on. -- * So added retry logic to ensure power on so that -- * port prepare succeeds -- */ -- do { -- ret = regmap_write(tas_dev->regmap, -- SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, -- TAS2783_SDCA_CTL_REQ_POW_STATE, 0), -- TAS2783_SDCA_POW_STATE_ON); -- if (!ret) -- break; -- usleep_range(2000, 2200); -- } while (retry--); -- mutex_unlock(&tas_dev->pde_lock); -+ scoped_guard(mutex, &tas_dev->pde_lock) { -+ /* -+ * Sometimes, there is error returned during power on. -+ * So added retry logic to ensure power on so that -+ * port prepare succeeds -+ */ -+ do { -+ ret = regmap_write(tas_dev->regmap, -+ SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, -+ TAS2783_SDCA_CTL_REQ_POW_STATE, 0), -+ TAS2783_SDCA_POW_STATE_ON); -+ if (!ret) -+ break; -+ usleep_range(2000, 2200); -+ } while (retry--); -+ } -+ - if (ret) - return ret; - -@@ -965,7 +1018,6 @@ static s32 tas_sdw_hw_params(struct snd_pcm_substream *substream, - static s32 tas_sdw_pcm_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) - { -- s32 ret; - struct snd_soc_component *component = dai->component; - struct tas2783_prv *tas_dev = - snd_soc_component_get_drvdata(component); -@@ -974,14 +1026,11 @@ static s32 tas_sdw_pcm_hw_free(struct snd_pcm_substream *substream, - - sdw_stream_remove_slave(tas_dev->sdw_peripheral, sdw_stream); - -- mutex_lock(&tas_dev->pde_lock); -- ret = regmap_write(tas_dev->regmap, -- SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, -- TAS2783_SDCA_CTL_REQ_POW_STATE, 0), -- TAS2783_SDCA_POW_STATE_OFF); -- mutex_unlock(&tas_dev->pde_lock); -- -- return ret; -+ guard(mutex)(&tas_dev->pde_lock); -+ return regmap_write(tas_dev->regmap, -+ SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, -+ TAS2783_SDCA_CTL_REQ_POW_STATE, 0), -+ TAS2783_SDCA_POW_STATE_OFF); - } - - static const struct snd_soc_dai_ops tas_dai_ops = { -@@ -1098,7 +1147,13 @@ static s32 tas2783_sdca_dev_resume(struct device *dev) - } - - regcache_cache_only(tas_dev->regmap, false); -- regcache_sync(tas_dev->regmap); -+ ret = regcache_sync(tas_dev->regmap); -+ if (ret) { -+ regcache_cache_only(tas_dev->regmap, true); -+ regcache_mark_dirty(tas_dev->regmap); -+ return ret; -+ } -+ - return 0; - } - -@@ -1224,9 +1279,23 @@ static s32 tas_update_status(struct sdw_slave *slave, - if (tas_dev->hw_init || tas_dev->status != SDW_SLAVE_ATTACHED) - return 0; - -- /* updated the cache data to device */ - regcache_cache_only(tas_dev->regmap, false); -- regcache_sync(tas_dev->regmap); -+ -+ /* -+ * The device is attaching uninitialized: either this is the first -+ * attach, or it lost power (and with it all register and DSP state) -+ * while the controller was power-gated during system suspend. The -+ * cache still holds the pre-suspend values, and tas_io_init() below -+ * resets the device via TAS2783_SW_RESET anyway, so syncing it back -+ * is both useless and harmful: later read-modify-write updates would -+ * compare against stale data and skip the hardware write. -+ * -+ * Drop the cache instead, so that subsequent accesses see the real -+ * hardware state. Syncing after the reset is not an option either: -+ * the cache accepts registers for which tas2783_sdca_mbq_size() -+ * returns 0, and writing those back fails with -EINVAL. -+ */ -+ regcache_drop_region(tas_dev->regmap, 0, UINT_MAX); - - /* perform I/O transfers required for Slave initialization */ - return tas_io_init(&slave->dev, slave); -@@ -1329,7 +1398,7 @@ static s32 tas_sdw_probe(struct sdw_slave *peripheral, - function_data->desc = &peripheral->sdca_data.function[i]; - - /* Parse the function */ -- ret = sdca_parse_function(dev, peripheral, function_data); -+ ret = sdca_parse_function(dev, function_data); - if (!ret) - tas_dev->sa_func_data = function_data; - else -diff --git a/sound/soc/codecs/tas5805m.c b/sound/soc/codecs/tas5805m.c ---- a/sound/soc/codecs/tas5805m.c -+++ b/sound/soc/codecs/tas5805m.c -@@ -12,6 +12,7 @@ - // - // It has been simplified a little and reworked for the 5.x ALSA SoC API. - -+#include - #include - #include - #include -@@ -230,10 +231,9 @@ static int tas5805m_vol_get(struct snd_kcontrol *kcontrol, - struct tas5805m_priv *tas5805m = - snd_soc_component_get_drvdata(component); - -- mutex_lock(&tas5805m->lock); -+ guard(mutex)(&tas5805m->lock); - ucontrol->value.integer.value[0] = tas5805m->vol[0]; - ucontrol->value.integer.value[1] = tas5805m->vol[1]; -- mutex_unlock(&tas5805m->lock); - - return 0; - } -@@ -249,13 +249,12 @@ static int tas5805m_vol_put(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct tas5805m_priv *tas5805m = - snd_soc_component_get_drvdata(component); -- int ret = 0; - - if (!(volume_is_valid(ucontrol->value.integer.value[0]) && - volume_is_valid(ucontrol->value.integer.value[1]))) - return -EINVAL; - -- mutex_lock(&tas5805m->lock); -+ guard(mutex)(&tas5805m->lock); - if (tas5805m->vol[0] != ucontrol->value.integer.value[0] || - tas5805m->vol[1] != ucontrol->value.integer.value[1]) { - tas5805m->vol[0] = ucontrol->value.integer.value[0]; -@@ -265,11 +264,10 @@ static int tas5805m_vol_put(struct snd_kcontrol *kcontrol, - tas5805m->is_powered); - if (tas5805m->is_powered) - tas5805m_refresh(tas5805m); -- ret = 1; -+ return 1; - } -- mutex_unlock(&tas5805m->lock); - -- return ret; -+ return 0; - } - - static const struct snd_kcontrol_new tas5805m_snd_controls[] = { -@@ -332,7 +330,7 @@ static void do_work(struct work_struct *work) - - dev_dbg(&tas5805m->i2c->dev, "DSP startup\n"); - -- mutex_lock(&tas5805m->lock); -+ guard(mutex)(&tas5805m->lock); - /* We mustn't issue any I2C transactions until the I2S - * clock is stable. Furthermore, we must allow a 5ms - * delay after the first set of register writes to -@@ -345,7 +343,6 @@ static void do_work(struct work_struct *work) - - tas5805m->is_powered = true; - tas5805m_refresh(tas5805m); -- mutex_unlock(&tas5805m->lock); - } - - static int tas5805m_dac_event(struct snd_soc_dapm_widget *w, -@@ -362,7 +359,7 @@ static int tas5805m_dac_event(struct snd_soc_dapm_widget *w, - dev_dbg(component->dev, "DSP shutdown\n"); - cancel_work_sync(&tas5805m->work); - -- mutex_lock(&tas5805m->lock); -+ guard(mutex)(&tas5805m->lock); - if (tas5805m->is_powered) { - tas5805m->is_powered = false; - -@@ -379,7 +376,6 @@ static int tas5805m_dac_event(struct snd_soc_dapm_widget *w, - - regmap_write(rm, REG_DEVICE_CTRL_2, DCTRL2_MODE_HIZ); - } -- mutex_unlock(&tas5805m->lock); - } - - return 0; -@@ -414,14 +410,13 @@ static int tas5805m_mute(struct snd_soc_dai *dai, int mute, int direction) - struct tas5805m_priv *tas5805m = - snd_soc_component_get_drvdata(component); - -- mutex_lock(&tas5805m->lock); -+ guard(mutex)(&tas5805m->lock); - dev_dbg(component->dev, "set mute=%d (is_powered=%d)\n", - mute, tas5805m->is_powered); - - tas5805m->is_muted = mute; - if (tas5805m->is_powered) - tas5805m_refresh(tas5805m); -- mutex_unlock(&tas5805m->lock); - - return 0; - } -@@ -462,7 +457,6 @@ static int tas5805m_i2c_probe(struct i2c_client *i2c) - struct tas5805m_priv *tas5805m; - char filename[128]; - const char *config_name; -- const struct firmware *fw; - int ret; - - regmap = devm_regmap_init_i2c(i2c, &tas5805m_regmap); -@@ -507,24 +501,20 @@ static int tas5805m_i2c_probe(struct i2c_client *i2c) - - snprintf(filename, sizeof(filename), "tas5805m_dsp_%s.bin", - config_name); -+ const struct firmware *fw __free(firmware) = NULL; - ret = request_firmware(&fw, filename, dev); - if (ret) - return ret; - - if ((fw->size < 2) || (fw->size & 1)) { - dev_err(dev, "firmware is invalid\n"); -- release_firmware(fw); - return -EINVAL; - } - - tas5805m->dsp_cfg_len = fw->size; - tas5805m->dsp_cfg_data = devm_kmemdup(dev, fw->data, fw->size, GFP_KERNEL); -- if (!tas5805m->dsp_cfg_data) { -- release_firmware(fw); -+ if (!tas5805m->dsp_cfg_data) - return -ENOMEM; -- } -- -- release_firmware(fw); - - /* Do the first part of the power-on here, while we can expect - * the I2S interface to be quiet. We must raise PDN# and then -diff --git a/sound/soc/codecs/tas675x.c b/sound/soc/codecs/tas675x.c ---- a/sound/soc/codecs/tas675x.c -+++ b/sound/soc/codecs/tas675x.c -@@ -1924,15 +1924,6 @@ static const struct reg_default tas675x_reg_defaults[] = { - { TAS675X_PWM_PHASE_M_CTRL_CH2_REG, 0x00 }, - { TAS675X_PWM_PHASE_M_CTRL_CH3_REG, 0x00 }, - { TAS675X_PWM_PHASE_M_CTRL_CH4_REG, 0x00 }, -- { TAS675X_DC_LDG_CTRL_REG, 0x00 }, -- { TAS675X_DC_LDG_LO_CTRL_REG, 0x00 }, -- { TAS675X_DC_LDG_TIME_CTRL_REG, 0x00 }, -- { TAS675X_DC_LDG_SL_CH1_CH2_CTRL_REG, 0x11 }, -- { TAS675X_DC_LDG_SL_CH3_CH4_CTRL_REG, 0x11 }, -- { TAS675X_AC_LDG_CTRL_REG, 0x10 }, -- { TAS675X_TWEETER_DETECT_CTRL_REG, 0x08 }, -- { TAS675X_TWEETER_DETECT_THRESH_REG, 0x00 }, -- { TAS675X_AC_LDG_FREQ_CTRL_REG, 0xC8 }, - { TAS675X_REPORT_ROUTING_1_REG, 0x00 }, - { TAS675X_OTSD_RECOVERY_EN_REG, 0x00 }, - { TAS675X_REPORT_ROUTING_2_REG, 0xA2 }, -@@ -1943,6 +1934,15 @@ static const struct reg_default tas675x_reg_defaults[] = { - { TAS675X_GPIO1_OUTPUT_SEL_REG, 0x00 }, - { TAS675X_GPIO2_OUTPUT_SEL_REG, 0x00 }, - { TAS675X_GPIO_CTRL_REG, TAS675X_GPIO_CTRL_RSTVAL }, -+ { TAS675X_DC_LDG_CTRL_REG, 0x00 }, -+ { TAS675X_DC_LDG_LO_CTRL_REG, 0x00 }, -+ { TAS675X_DC_LDG_TIME_CTRL_REG, 0x00 }, -+ { TAS675X_DC_LDG_SL_CH1_CH2_CTRL_REG, 0x11 }, -+ { TAS675X_DC_LDG_SL_CH3_CH4_CTRL_REG, 0x11 }, -+ { TAS675X_AC_LDG_CTRL_REG, 0x10 }, -+ { TAS675X_TWEETER_DETECT_CTRL_REG, 0x08 }, -+ { TAS675X_TWEETER_DETECT_THRESH_REG, 0x00 }, -+ { TAS675X_AC_LDG_FREQ_CTRL_REG, 0xC8 }, - { TAS675X_OTW_CTRL_CH1_CH2_REG, 0x11 }, - { TAS675X_OTW_CTRL_CH3_CH4_REG, 0x11 }, - }; -diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c ---- a/sound/soc/codecs/tlv320aic26.c -+++ b/sound/soc/codecs/tlv320aic26.c -@@ -320,8 +320,14 @@ static int aic26_probe(struct snd_soc_component *component) - return 0; - } - -+static void aic26_remove(struct snd_soc_component *component) -+{ -+ device_remove_file(component->dev, &dev_attr_keyclick); -+} -+ - static const struct snd_soc_component_driver aic26_soc_component_dev = { - .probe = aic26_probe, -+ .remove = aic26_remove, - .controls = aic26_snd_controls, - .num_controls = ARRAY_SIZE(aic26_snd_controls), - .dapm_widgets = tlv320aic26_dapm_widgets, -diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c ---- a/sound/soc/codecs/tlv320aic31xx.c -+++ b/sound/soc/codecs/tlv320aic31xx.c -@@ -1720,18 +1720,14 @@ static int tlv320dac3100_fw_load(struct aic31xx_priv *aic31xx, - static int tlv320dac3100_load_coeffs(struct aic31xx_priv *aic31xx, - const char *fw_name) - { -- const struct firmware *fw; -+ const struct firmware *fw __free(firmware) = NULL; - int ret; - - ret = request_firmware(&fw, fw_name, aic31xx->dev); - if (ret) - return ret; - -- ret = tlv320dac3100_fw_load(aic31xx, fw->data, fw->size); -- -- release_firmware(fw); -- -- return ret; -+ return tlv320dac3100_fw_load(aic31xx, fw->data, fw->size); - } - - static int aic31xx_i2c_probe(struct i2c_client *i2c) -diff --git a/sound/soc/codecs/tlv320aic32x4-clk.c b/sound/soc/codecs/tlv320aic32x4-clk.c ---- a/sound/soc/codecs/tlv320aic32x4-clk.c -+++ b/sound/soc/codecs/tlv320aic32x4-clk.c -@@ -1,5 +1,5 @@ --/* SPDX-License-Identifier: GPL-2.0 -- * -+// SPDX-License-Identifier: GPL-2.0 -+/* - * Clock Tree for the Texas Instruments TLV320AIC32x4 - * - * Copyright 2019 Annaliese McDermond -@@ -9,6 +9,7 @@ - - #include - #include -+#include - #include - #include - -@@ -57,7 +58,7 @@ static void clk_aic32x4_pll_unprepare(struct clk_hw *hw) - struct clk_aic32x4 *pll = to_clk_aic32x4(hw); - - regmap_update_bits(pll->regmap, AIC32X4_PLLPR, -- AIC32X4_PLLEN, 0); -+ AIC32X4_PLLEN, 0); - } - - static int clk_aic32x4_pll_is_prepared(struct clk_hw *hw) -@@ -75,7 +76,7 @@ static int clk_aic32x4_pll_is_prepared(struct clk_hw *hw) - } - - static int clk_aic32x4_pll_get_muldiv(struct clk_aic32x4 *pll, -- struct clk_aic32x4_pll_muldiv *settings) -+ struct clk_aic32x4_pll_muldiv *settings) - { - /* Change to use regmap_bulk_read? */ - unsigned int val; -@@ -106,19 +107,19 @@ static int clk_aic32x4_pll_get_muldiv(struct clk_aic32x4 *pll, - } - - static int clk_aic32x4_pll_set_muldiv(struct clk_aic32x4 *pll, -- struct clk_aic32x4_pll_muldiv *settings) -+ struct clk_aic32x4_pll_muldiv *settings) - { - int ret; - /* Change to use regmap_bulk_write for some if not all? */ - - ret = regmap_update_bits(pll->regmap, AIC32X4_PLLPR, -- AIC32X4_PLL_R_MASK, settings->r); -+ AIC32X4_PLL_R_MASK, settings->r); - if (ret < 0) - return ret; - - ret = regmap_update_bits(pll->regmap, AIC32X4_PLLPR, -- AIC32X4_PLL_P_MASK, -- settings->p << AIC32X4_PLL_P_SHIFT); -+ AIC32X4_PLL_P_MASK, -+ settings->p << AIC32X4_PLL_P_SHIFT); - if (ret < 0) - return ret; - -@@ -136,23 +137,21 @@ static int clk_aic32x4_pll_set_muldiv(struct clk_aic32x4 *pll, - return 0; - } - --static unsigned long clk_aic32x4_pll_calc_rate( -- struct clk_aic32x4_pll_muldiv *settings, -- unsigned long parent_rate) -+static unsigned long clk_aic32x4_pll_calc_rate(struct clk_aic32x4_pll_muldiv *settings, -+ unsigned long parent_rate) - { - u64 rate; - /* - * We scale j by 10000 to account for the decimal part of P and divide - * it back out later. - */ -- rate = (u64) parent_rate * settings->r * -- ((settings->j * 10000) + settings->d); -+ rate = (u64)parent_rate * settings->r * ((settings->j * 10000) + settings->d); - -- return (unsigned long) DIV_ROUND_UP_ULL(rate, settings->p * 10000); -+ return (unsigned long)DIV_ROUND_UP_ULL(rate, settings->p * 10000); - } - - static int clk_aic32x4_pll_calc_muldiv(struct clk_aic32x4_pll_muldiv *settings, -- unsigned long rate, unsigned long parent_rate) -+ unsigned long rate, unsigned long parent_rate) - { - u64 multiplier; - -@@ -165,14 +164,14 @@ static int clk_aic32x4_pll_calc_muldiv(struct clk_aic32x4_pll_muldiv *settings, - * of the multiplier. This is because we can't do floating point - * math in the kernel. - */ -- multiplier = (u64) rate * settings->p * 10000; -+ multiplier = (u64)rate * settings->p * 10000; - do_div(multiplier, parent_rate); - - /* - * J can't be over 64, so R can scale this. - * R can't be greater than 4. - */ -- settings->r = ((u32) multiplier / 640000) + 1; -+ settings->r = ((u32)multiplier / 640000) + 1; - if (settings->r > 4) - return -1; - do_div(multiplier, settings->r); -@@ -184,14 +183,14 @@ static int clk_aic32x4_pll_calc_muldiv(struct clk_aic32x4_pll_muldiv *settings, - return -1; - - /* Figure out the integer part, J, and the fractional part, D. */ -- settings->j = (u32) multiplier / 10000; -- settings->d = (u32) multiplier % 10000; -+ settings->j = (u32)multiplier / 10000; -+ settings->d = (u32)multiplier % 10000; - - return 0; - } - - static unsigned long clk_aic32x4_pll_recalc_rate(struct clk_hw *hw, -- unsigned long parent_rate) -+ unsigned long parent_rate) - { - struct clk_aic32x4 *pll = to_clk_aic32x4(hw); - struct clk_aic32x4_pll_muldiv settings; -@@ -220,8 +219,8 @@ static int clk_aic32x4_pll_determine_rate(struct clk_hw *hw, - } - - static int clk_aic32x4_pll_set_rate(struct clk_hw *hw, -- unsigned long rate, -- unsigned long parent_rate) -+ unsigned long rate, -+ unsigned long parent_rate) - { - struct clk_aic32x4 *pll = to_clk_aic32x4(hw); - struct clk_aic32x4_pll_muldiv settings; -@@ -236,7 +235,7 @@ static int clk_aic32x4_pll_set_rate(struct clk_hw *hw, - return ret; - - /* 10ms is the delay to wait before the clocks are stable */ -- msleep(10); -+ usleep_range(10000, 20000); - - return 0; - } -@@ -261,7 +260,6 @@ static u8 clk_aic32x4_pll_get_parent(struct clk_hw *hw) - return (val & AIC32X4_PLL_CLKIN_MASK) >> AIC32X4_PLL_CLKIN_SHIFT; - } - -- - static const struct clk_ops aic32x4_pll_ops = { - .prepare = clk_aic32x4_pll_prepare, - .unprepare = clk_aic32x4_pll_unprepare, -@@ -311,11 +309,11 @@ static void clk_aic32x4_div_unprepare(struct clk_hw *hw) - struct clk_aic32x4 *div = to_clk_aic32x4(hw); - - regmap_update_bits(div->regmap, div->reg, -- AIC32X4_DIVEN, 0); -+ AIC32X4_DIVEN, 0); - } - - static int clk_aic32x4_div_set_rate(struct clk_hw *hw, unsigned long rate, -- unsigned long parent_rate) -+ unsigned long parent_rate) - { - struct clk_aic32x4 *div = to_clk_aic32x4(hw); - u8 divisor; -@@ -342,7 +340,7 @@ static int clk_aic32x4_div_determine_rate(struct clk_hw *hw, - } - - static unsigned long clk_aic32x4_div_recalc_rate(struct clk_hw *hw, -- unsigned long parent_rate) -+ unsigned long parent_rate) - { - struct clk_aic32x4 *div = to_clk_aic32x4(hw); - unsigned int val; -@@ -399,7 +397,7 @@ static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { - { - .name = "pll", - .parent_names = -- (const char* []) { "mclk", "bclk", "gpio", "din" }, -+ (const char *[]) { "mclk", "bclk", "gpio", "din" }, - .num_parents = 4, - .ops = &aic32x4_pll_ops, - .reg = 0, -@@ -414,28 +412,28 @@ static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { - }, - { - .name = "ndac", -- .parent_names = (const char * []) { "codec_clkin" }, -+ .parent_names = (const char *[]) { "codec_clkin" }, - .num_parents = 1, - .ops = &aic32x4_div_ops, - .reg = AIC32X4_NDAC, - }, - { - .name = "mdac", -- .parent_names = (const char * []) { "ndac" }, -+ .parent_names = (const char *[]) { "ndac" }, - .num_parents = 1, - .ops = &aic32x4_div_ops, - .reg = AIC32X4_MDAC, - }, - { - .name = "nadc", -- .parent_names = (const char * []) { "codec_clkin" }, -+ .parent_names = (const char *[]) { "codec_clkin" }, - .num_parents = 1, - .ops = &aic32x4_div_ops, - .reg = AIC32X4_NADC, - }, - { - .name = "madc", -- .parent_names = (const char * []) { "nadc" }, -+ .parent_names = (const char *[]) { "nadc" }, - .num_parents = 1, - .ops = &aic32x4_div_ops, - .reg = AIC32X4_MADC, -@@ -451,7 +449,7 @@ static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { - }; - - static struct clk *aic32x4_register_clk(struct device *dev, -- struct aic32x4_clkdesc *desc) -+ struct aic32x4_clkdesc *desc) - { - struct clk_init_data init; - struct clk_aic32x4 *priv; -@@ -464,8 +462,8 @@ static struct clk *aic32x4_register_clk(struct device *dev, - init.flags = 0; - - priv = devm_kzalloc(dev, sizeof(struct clk_aic32x4), GFP_KERNEL); -- if (priv == NULL) -- return (struct clk *) -ENOMEM; -+ if (!priv) -+ return ERR_PTR(-ENOMEM); - - priv->dev = dev; - priv->hw.init = &init; -diff --git a/sound/soc/codecs/tlv320aic32x4-i2c.c b/sound/soc/codecs/tlv320aic32x4-i2c.c ---- a/sound/soc/codecs/tlv320aic32x4-i2c.c -+++ b/sound/soc/codecs/tlv320aic32x4-i2c.c -@@ -1,11 +1,10 @@ --/* SPDX-License-Identifier: GPL-2.0 -- * -+// SPDX-License-Identifier: GPL-2.0 -+/* - * Copyright 2011-2019 NW Digital Radio - * - * Author: Annaliese McDermond - * - * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27. -- * - */ - - #include -@@ -16,17 +15,23 @@ - - #include "tlv320aic32x4.h" - -+static const struct regmap_config aic32x4_i2c_regmap_config = { -+ .reg_bits = 8, -+ .val_bits = 8, -+ .max_register = AIC32X4_REFPOWERUP, -+ .ranges = aic32x4_regmap_pages, -+ .num_ranges = 1, -+}; -+ - static int aic32x4_i2c_probe(struct i2c_client *i2c) - { - struct regmap *regmap; -- struct regmap_config config; - enum aic32x4_type type; - -- config = aic32x4_regmap_config; -- config.reg_bits = 8; -- config.val_bits = 8; -+ regmap = devm_regmap_init_i2c(i2c, &aic32x4_i2c_regmap_config); -+ if (IS_ERR(regmap)) -+ return PTR_ERR(regmap); - -- regmap = devm_regmap_init_i2c(i2c, &config); - type = (uintptr_t)i2c_get_match_data(i2c); - - return aic32x4_probe(&i2c->dev, regmap, type); -diff --git a/sound/soc/codecs/tlv320aic32x4-spi.c b/sound/soc/codecs/tlv320aic32x4-spi.c ---- a/sound/soc/codecs/tlv320aic32x4-spi.c -+++ b/sound/soc/codecs/tlv320aic32x4-spi.c -@@ -1,11 +1,10 @@ --/* SPDX-License-Identifier: GPL-2.0 -- * -+// SPDX-License-Identifier: GPL-2.0 -+/* - * Copyright 2011-2019 NW Digital Radio - * - * Author: Annaliese McDermond - * - * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27. -- * - */ - - #include -@@ -16,19 +15,25 @@ - - #include "tlv320aic32x4.h" - -+static const struct regmap_config aic32x4_spi_regmap_config = { -+ .reg_bits = 7, -+ .pad_bits = 1, -+ .val_bits = 8, -+ .read_flag_mask = 0x01, -+ .max_register = AIC32X4_REFPOWERUP, -+ .ranges = aic32x4_regmap_pages, -+ .num_ranges = 1, -+}; -+ - static int aic32x4_spi_probe(struct spi_device *spi) - { - struct regmap *regmap; -- struct regmap_config config; - enum aic32x4_type type; - -- config = aic32x4_regmap_config; -- config.reg_bits = 7; -- config.pad_bits = 1; -- config.val_bits = 8; -- config.read_flag_mask = 0x01; -+ regmap = devm_regmap_init_spi(spi, &aic32x4_spi_regmap_config); -+ if (IS_ERR(regmap)) -+ return PTR_ERR(regmap); - -- regmap = devm_regmap_init_spi(spi, &config); - type = (uintptr_t)spi_get_device_match_data(spi); - - return aic32x4_probe(&spi->dev, regmap, type); -diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c ---- a/sound/soc/codecs/tlv320aic32x4.c -+++ b/sound/soc/codecs/tlv320aic32x4.c -@@ -1,7 +1,5 @@ - // SPDX-License-Identifier: GPL-2.0-or-later - /* -- * linux/sound/soc/codecs/tlv320aic32x4.c -- * - * Copyright 2011 Vista Silicon S.L. - * - * Author: Javier Martin -@@ -28,7 +26,6 @@ - #include - #include - #include --#include - - #include "tlv320aic32x4.h" - -@@ -45,7 +42,8 @@ struct aic32x4_priv { - struct regulator *supply_dv; - struct regulator *supply_av; - -- struct aic32x4_setup_data *setup; -+ unsigned int gpio_func[5]; -+ - struct device *dev; - enum aic32x4_type type; - -@@ -75,7 +73,7 @@ static int aic32x4_reset_adc(struct snd_soc_dapm_widget *w, - }; - - static int mic_bias_event(struct snd_soc_dapm_widget *w, -- struct snd_kcontrol *kcontrol, int event) -+ struct snd_kcontrol *kcontrol, int event) - { - struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); - -@@ -83,25 +81,23 @@ static int mic_bias_event(struct snd_soc_dapm_widget *w, - case SND_SOC_DAPM_POST_PMU: - /* Change Mic Bias Registor */ - snd_soc_component_update_bits(component, AIC32X4_MICBIAS, -- AIC32x4_MICBIAS_MASK, -- AIC32X4_MICBIAS_LDOIN | -- AIC32X4_MICBIAS_2075V); -- printk(KERN_DEBUG "%s: Mic Bias will be turned ON\n", __func__); -+ AIC32x4_MICBIAS_MASK, -+ AIC32X4_MICBIAS_LDOIN | -+ AIC32X4_MICBIAS_2075V); -+ dev_dbg(component->dev, "Mic Bias will be turned ON\n"); - break; - case SND_SOC_DAPM_PRE_PMD: - snd_soc_component_update_bits(component, AIC32X4_MICBIAS, -- AIC32x4_MICBIAS_MASK, 0); -- printk(KERN_DEBUG "%s: Mic Bias will be turned OFF\n", -- __func__); -+ AIC32x4_MICBIAS_MASK, 0); -+ dev_dbg(component->dev, "Mic Bias will be turned OFF\n"); - break; - } - - return 0; - } - -- - static int aic32x4_get_mfp1_gpio(struct snd_kcontrol *kcontrol, -- struct snd_ctl_elem_value *ucontrol) -+ struct snd_ctl_elem_value *ucontrol) - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - u8 val; -@@ -114,7 +110,7 @@ static int aic32x4_get_mfp1_gpio(struct snd_kcontrol *kcontrol, - }; - - static int aic32x4_set_mfp2_gpio(struct snd_kcontrol *kcontrol, -- struct snd_ctl_elem_value *ucontrol) -+ struct snd_ctl_elem_value *ucontrol) - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - u8 val; -@@ -123,8 +119,7 @@ static int aic32x4_set_mfp2_gpio(struct snd_kcontrol *kcontrol, - val = snd_soc_component_read(component, AIC32X4_DOUTCTL); - gpio_check = (val & AIC32X4_MFP_GPIO_ENABLED); - if (gpio_check != AIC32X4_MFP_GPIO_ENABLED) { -- printk(KERN_ERR "%s: MFP2 is not configure as a GPIO output\n", -- __func__); -+ dev_err(component->dev, "MFP2 is not configure as a GPIO output\n"); - return -EINVAL; - } - -@@ -142,7 +137,7 @@ static int aic32x4_set_mfp2_gpio(struct snd_kcontrol *kcontrol, - }; - - static int aic32x4_get_mfp3_gpio(struct snd_kcontrol *kcontrol, -- struct snd_ctl_elem_value *ucontrol) -+ struct snd_ctl_elem_value *ucontrol) - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - u8 val; -@@ -155,7 +150,7 @@ static int aic32x4_get_mfp3_gpio(struct snd_kcontrol *kcontrol, - }; - - static int aic32x4_set_mfp4_gpio(struct snd_kcontrol *kcontrol, -- struct snd_ctl_elem_value *ucontrol) -+ struct snd_ctl_elem_value *ucontrol) - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - u8 val; -@@ -164,8 +159,7 @@ static int aic32x4_set_mfp4_gpio(struct snd_kcontrol *kcontrol, - val = snd_soc_component_read(component, AIC32X4_MISOCTL); - gpio_check = (val & AIC32X4_MFP_GPIO_ENABLED); - if (gpio_check != AIC32X4_MFP_GPIO_ENABLED) { -- printk(KERN_ERR "%s: MFP4 is not configure as a GPIO output\n", -- __func__); -+ dev_err(component->dev, "MFP4 is not configure as a GPIO output\n"); - return -EINVAL; - } - -@@ -183,7 +177,7 @@ static int aic32x4_set_mfp4_gpio(struct snd_kcontrol *kcontrol, - }; - - static int aic32x4_get_mfp5_gpio(struct snd_kcontrol *kcontrol, -- struct snd_ctl_elem_value *ucontrol) -+ struct snd_ctl_elem_value *ucontrol) - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - u8 val; -@@ -195,7 +189,7 @@ static int aic32x4_get_mfp5_gpio(struct snd_kcontrol *kcontrol, - }; - - static int aic32x4_set_mfp5_gpio(struct snd_kcontrol *kcontrol, -- struct snd_ctl_elem_value *ucontrol) -+ struct snd_ctl_elem_value *ucontrol) - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - u8 val; -@@ -204,8 +198,7 @@ static int aic32x4_set_mfp5_gpio(struct snd_kcontrol *kcontrol, - val = snd_soc_component_read(component, AIC32X4_GPIOCTL); - gpio_check = (val & AIC32X4_MFP5_GPIO_OUTPUT); - if (gpio_check != AIC32X4_MFP5_GPIO_OUTPUT) { -- printk(KERN_ERR "%s: MFP5 is not configure as a GPIO output\n", -- __func__); -+ dev_err(component->dev, "MFP5 is not configure as a GPIO output\n"); - return -EINVAL; - } - -@@ -222,25 +215,31 @@ static int aic32x4_set_mfp5_gpio(struct snd_kcontrol *kcontrol, - return 0; - }; - --static const struct snd_kcontrol_new aic32x4_mfp1[] = { -- SOC_SINGLE_BOOL_EXT("MFP1 GPIO", 0, aic32x4_get_mfp1_gpio, NULL), --}; -- --static const struct snd_kcontrol_new aic32x4_mfp2[] = { -- SOC_SINGLE_BOOL_EXT("MFP2 GPIO", 0, NULL, aic32x4_set_mfp2_gpio), --}; -- --static const struct snd_kcontrol_new aic32x4_mfp3[] = { -- SOC_SINGLE_BOOL_EXT("MFP3 GPIO", 0, aic32x4_get_mfp3_gpio, NULL), --}; -- --static const struct snd_kcontrol_new aic32x4_mfp4[] = { -- SOC_SINGLE_BOOL_EXT("MFP4 GPIO", 0, NULL, aic32x4_set_mfp4_gpio), --}; -- --static const struct snd_kcontrol_new aic32x4_mfp5[] = { -- SOC_SINGLE_BOOL_EXT("MFP5 GPIO", 0, aic32x4_get_mfp5_gpio, -- aic32x4_set_mfp5_gpio), -+static const struct { -+ unsigned int reg; -+ struct snd_kcontrol_new ctrl; -+} aic32x4_mfp_cfg[] = { -+ { -+ .reg = AIC32X4_DINCTL, -+ .ctrl = SOC_SINGLE_BOOL_EXT("MFP1 GPIO", 0, aic32x4_get_mfp1_gpio, NULL), -+ }, -+ { -+ .reg = AIC32X4_DOUTCTL, -+ .ctrl = SOC_SINGLE_BOOL_EXT("MFP2 GPIO", 0, NULL, aic32x4_set_mfp2_gpio), -+ }, -+ { -+ .reg = AIC32X4_SCLKCTL, -+ .ctrl = SOC_SINGLE_BOOL_EXT("MFP3 GPIO", 0, aic32x4_get_mfp3_gpio, NULL), -+ }, -+ { -+ .reg = AIC32X4_MISOCTL, -+ .ctrl = SOC_SINGLE_BOOL_EXT("MFP4 GPIO", 0, NULL, aic32x4_set_mfp4_gpio), -+ }, -+ { -+ .reg = AIC32X4_GPIOCTL, -+ .ctrl = SOC_SINGLE_BOOL_EXT("MFP5 GPIO", 0, aic32x4_get_mfp5_gpio, -+ aic32x4_set_mfp5_gpio), -+ }, - }; - - /* 0dB min, 0.5dB steps */ -@@ -270,53 +269,42 @@ static SOC_ENUM_SINGLE_DECL(r_ptm_enum, AIC32X4_RPLAYBACK, 2, ptm_text); - - static const struct snd_kcontrol_new aic32x4_snd_controls[] = { - SOC_DOUBLE_R_S_TLV("PCM Playback Volume", AIC32X4_LDACVOL, -- AIC32X4_RDACVOL, 0, -0x7f, 0x30, 7, 0, tlv_pcm), -+ AIC32X4_RDACVOL, 0, -0x7f, 0x30, 7, 0, tlv_pcm), - SOC_ENUM("DAC Left Playback PowerTune Switch", l_ptm_enum), - SOC_ENUM("DAC Right Playback PowerTune Switch", r_ptm_enum), - SOC_DOUBLE_R_S_TLV("HP Driver Gain Volume", AIC32X4_HPLGAIN, -- AIC32X4_HPRGAIN, 0, -0x6, 0x1d, 5, 0, -- tlv_driver_gain), -+ AIC32X4_HPRGAIN, 0, -0x6, 0x1d, 5, 0, tlv_driver_gain), - SOC_DOUBLE_R_S_TLV("LO Driver Gain Volume", AIC32X4_LOLGAIN, -- AIC32X4_LORGAIN, 0, -0x6, 0x1d, 5, 0, -- tlv_driver_gain), -+ AIC32X4_LORGAIN, 0, -0x6, 0x1d, 5, 0, tlv_driver_gain), - SOC_DOUBLE_R("HP DAC Playback Switch", AIC32X4_HPLGAIN, -- AIC32X4_HPRGAIN, 6, 0x01, 1), -+ AIC32X4_HPRGAIN, 6, 0x01, 1), - SOC_DOUBLE_R("LO DAC Playback Switch", AIC32X4_LOLGAIN, -- AIC32X4_LORGAIN, 6, 0x01, 1), -+ AIC32X4_LORGAIN, 6, 0x01, 1), - SOC_ENUM("LO Playback Common Mode Switch", lo_cm_enum), - SOC_DOUBLE_R("Mic PGA Switch", AIC32X4_LMICPGAVOL, -- AIC32X4_RMICPGAVOL, 7, 0x01, 1), -+ AIC32X4_RMICPGAVOL, 7, 0x01, 1), - - SOC_SINGLE("ADCFGA Left Mute Switch", AIC32X4_ADCFGA, 7, 1, 0), - SOC_SINGLE("ADCFGA Right Mute Switch", AIC32X4_ADCFGA, 3, 1, 0), - - SOC_DOUBLE_R_S_TLV("ADC Level Volume", AIC32X4_LADCVOL, -- AIC32X4_RADCVOL, 0, -0x18, 0x28, 6, 0, tlv_adc_vol), -+ AIC32X4_RADCVOL, 0, -0x18, 0x28, 6, 0, tlv_adc_vol), - SOC_DOUBLE_R_TLV("PGA Level Volume", AIC32X4_LMICPGAVOL, -- AIC32X4_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5), -+ AIC32X4_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5), - - SOC_SINGLE("Auto-mute Switch", AIC32X4_DACMUTE, 4, 7, 0), - - SOC_SINGLE("AGC Left Switch", AIC32X4_LAGC1, 7, 1, 0), - SOC_SINGLE("AGC Right Switch", AIC32X4_RAGC1, 7, 1, 0), -- SOC_DOUBLE_R("AGC Target Level", AIC32X4_LAGC1, AIC32X4_RAGC1, -- 4, 0x07, 0), -- SOC_DOUBLE_R("AGC Gain Hysteresis", AIC32X4_LAGC1, AIC32X4_RAGC1, -- 0, 0x03, 0), -- SOC_DOUBLE_R("AGC Hysteresis", AIC32X4_LAGC2, AIC32X4_RAGC2, -- 6, 0x03, 0), -- SOC_DOUBLE_R("AGC Noise Threshold", AIC32X4_LAGC2, AIC32X4_RAGC2, -- 1, 0x1F, 0), -- SOC_DOUBLE_R("AGC Max PGA", AIC32X4_LAGC3, AIC32X4_RAGC3, -- 0, 0x7F, 0), -- SOC_DOUBLE_R("AGC Attack Time", AIC32X4_LAGC4, AIC32X4_RAGC4, -- 3, 0x1F, 0), -- SOC_DOUBLE_R("AGC Decay Time", AIC32X4_LAGC5, AIC32X4_RAGC5, -- 3, 0x1F, 0), -- SOC_DOUBLE_R("AGC Noise Debounce", AIC32X4_LAGC6, AIC32X4_RAGC6, -- 0, 0x1F, 0), -- SOC_DOUBLE_R("AGC Signal Debounce", AIC32X4_LAGC7, AIC32X4_RAGC7, -- 0, 0x0F, 0), -+ SOC_DOUBLE_R("AGC Target Level", AIC32X4_LAGC1, AIC32X4_RAGC1, 4, 0x07, 0), -+ SOC_DOUBLE_R("AGC Gain Hysteresis", AIC32X4_LAGC1, AIC32X4_RAGC1, 0, 0x03, 0), -+ SOC_DOUBLE_R("AGC Hysteresis", AIC32X4_LAGC2, AIC32X4_RAGC2, 6, 0x03, 0), -+ SOC_DOUBLE_R("AGC Noise Threshold", AIC32X4_LAGC2, AIC32X4_RAGC2, 1, 0x1F, 0), -+ SOC_DOUBLE_R("AGC Max PGA", AIC32X4_LAGC3, AIC32X4_RAGC3, 0, 0x7F, 0), -+ SOC_DOUBLE_R("AGC Attack Time", AIC32X4_LAGC4, AIC32X4_RAGC4, 3, 0x1F, 0), -+ SOC_DOUBLE_R("AGC Decay Time", AIC32X4_LAGC5, AIC32X4_RAGC5, 3, 0x1F, 0), -+ SOC_DOUBLE_R("AGC Noise Debounce", AIC32X4_LAGC6, AIC32X4_RAGC6, 0, 0x1F, 0), -+ SOC_DOUBLE_R("AGC Signal Debounce", AIC32X4_LAGC7, AIC32X4_RAGC7, 0, 0x0F, 0), - }; - - static const struct snd_kcontrol_new hpl_output_mixer_controls[] = { -@@ -354,21 +342,27 @@ static SOC_ENUM_SINGLE_DECL(in3r_lpga_n_enum, AIC32X4_LMICPGANIN, 2, resistor_te - static const struct snd_kcontrol_new in1l_to_lmixer_controls[] = { - SOC_DAPM_ENUM("IN1_L L+ Switch", in1l_lpga_p_enum), - }; -+ - static const struct snd_kcontrol_new in2l_to_lmixer_controls[] = { - SOC_DAPM_ENUM("IN2_L L+ Switch", in2l_lpga_p_enum), - }; -+ - static const struct snd_kcontrol_new in3l_to_lmixer_controls[] = { - SOC_DAPM_ENUM("IN3_L L+ Switch", in3l_lpga_p_enum), - }; -+ - static const struct snd_kcontrol_new in1r_to_lmixer_controls[] = { - SOC_DAPM_ENUM("IN1_R L+ Switch", in1r_lpga_p_enum), - }; -+ - static const struct snd_kcontrol_new cml_to_lmixer_controls[] = { - SOC_DAPM_ENUM("CM_L L- Switch", cml_lpga_n_enum), - }; -+ - static const struct snd_kcontrol_new in2r_to_lmixer_controls[] = { - SOC_DAPM_ENUM("IN2_R L- Switch", in2r_lpga_n_enum), - }; -+ - static const struct snd_kcontrol_new in3r_to_lmixer_controls[] = { - SOC_DAPM_ENUM("IN3_R L- Switch", in3r_lpga_n_enum), - }; -@@ -385,21 +379,27 @@ static SOC_ENUM_SINGLE_DECL(in3l_rpga_n_enum, AIC32X4_RMICPGANIN, 2, resistor_te - static const struct snd_kcontrol_new in1r_to_rmixer_controls[] = { - SOC_DAPM_ENUM("IN1_R R+ Switch", in1r_rpga_p_enum), - }; -+ - static const struct snd_kcontrol_new in2r_to_rmixer_controls[] = { - SOC_DAPM_ENUM("IN2_R R+ Switch", in2r_rpga_p_enum), - }; -+ - static const struct snd_kcontrol_new in3r_to_rmixer_controls[] = { - SOC_DAPM_ENUM("IN3_R R+ Switch", in3r_rpga_p_enum), - }; -+ - static const struct snd_kcontrol_new in2l_to_rmixer_controls[] = { - SOC_DAPM_ENUM("IN2_L R+ Switch", in2l_rpga_p_enum), - }; -+ - static const struct snd_kcontrol_new cmr_to_rmixer_controls[] = { - SOC_DAPM_ENUM("CM_R R- Switch", cmr_rpga_n_enum), - }; -+ - static const struct snd_kcontrol_new in1l_to_rmixer_controls[] = { - SOC_DAPM_ENUM("IN1_L R- Switch", in1l_rpga_n_enum), - }; -+ - static const struct snd_kcontrol_new in3l_to_rmixer_controls[] = { - SOC_DAPM_ENUM("IN3_L R- Switch", in3l_rpga_n_enum), - }; -@@ -428,38 +428,38 @@ static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = { - - SND_SOC_DAPM_ADC("Right ADC", "Right Capture", AIC32X4_ADCSETUP, 6, 0), - SND_SOC_DAPM_MUX("IN1_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, -- in1r_to_rmixer_controls), -+ in1r_to_rmixer_controls), - SND_SOC_DAPM_MUX("IN2_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, -- in2r_to_rmixer_controls), -+ in2r_to_rmixer_controls), - SND_SOC_DAPM_MUX("IN3_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, -- in3r_to_rmixer_controls), -+ in3r_to_rmixer_controls), - SND_SOC_DAPM_MUX("IN2_L to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, -- in2l_to_rmixer_controls), -+ in2l_to_rmixer_controls), - SND_SOC_DAPM_MUX("CM_R to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, -- cmr_to_rmixer_controls), -+ cmr_to_rmixer_controls), - SND_SOC_DAPM_MUX("IN1_L to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, -- in1l_to_rmixer_controls), -+ in1l_to_rmixer_controls), - SND_SOC_DAPM_MUX("IN3_L to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, -- in3l_to_rmixer_controls), -+ in3l_to_rmixer_controls), - - SND_SOC_DAPM_ADC("Left ADC", "Left Capture", AIC32X4_ADCSETUP, 7, 0), - SND_SOC_DAPM_MUX("IN1_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, -- in1l_to_lmixer_controls), -+ in1l_to_lmixer_controls), - SND_SOC_DAPM_MUX("IN2_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, -- in2l_to_lmixer_controls), -+ in2l_to_lmixer_controls), - SND_SOC_DAPM_MUX("IN3_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, -- in3l_to_lmixer_controls), -+ in3l_to_lmixer_controls), - SND_SOC_DAPM_MUX("IN1_R to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, -- in1r_to_lmixer_controls), -+ in1r_to_lmixer_controls), - SND_SOC_DAPM_MUX("CM_L to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, -- cml_to_lmixer_controls), -+ cml_to_lmixer_controls), - SND_SOC_DAPM_MUX("IN2_R to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, -- in2r_to_lmixer_controls), -+ in2r_to_lmixer_controls), - SND_SOC_DAPM_MUX("IN3_R to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, -- in3r_to_lmixer_controls), -+ in3r_to_lmixer_controls), - - SND_SOC_DAPM_SUPPLY("Mic Bias", AIC32X4_MICBIAS, 6, 0, mic_bias_event, -- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), -+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - - SND_SOC_DAPM_POST("ADC Reset", aic32x4_reset_adc), - -@@ -575,7 +575,7 @@ static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = { - {"IN3_R to Left Mixer Negative Resistor", "40 kOhm", "IN3_R"}, - }; - --static const struct regmap_range_cfg aic32x4_regmap_pages[] = { -+const struct regmap_range_cfg aic32x4_regmap_pages[] = { - { - .selector_reg = 0, - .selector_mask = 0xff, -@@ -585,13 +585,7 @@ static const struct regmap_range_cfg aic32x4_regmap_pages[] = { - .range_max = AIC32X4_REFPOWERUP, - }, - }; -- --const struct regmap_config aic32x4_regmap_config = { -- .max_register = AIC32X4_REFPOWERUP, -- .ranges = aic32x4_regmap_pages, -- .num_ranges = ARRAY_SIZE(aic32x4_regmap_pages), --}; --EXPORT_SYMBOL(aic32x4_regmap_config); -+EXPORT_SYMBOL_GPL(aic32x4_regmap_pages); - - static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai, - int clk_id, unsigned int freq, int dir) -@@ -624,7 +618,7 @@ static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) - case SND_SOC_DAIFMT_CBC_CFC: - break; - default: -- printk(KERN_ERR "aic32x4: invalid clock provider\n"); -+ dev_err(component->dev, "invalid clock provider\n"); - return -EINVAL; - } - -@@ -651,19 +645,20 @@ static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) - AIC32X4_IFACE1_DATATYPE_SHIFT); - break; - default: -- printk(KERN_ERR "aic32x4: invalid DAI interface format\n"); -+ dev_err(component->dev, "invalid DAI interface format\n"); - return -EINVAL; - } - - aic32x4->fmt = fmt; - - snd_soc_component_update_bits(component, AIC32X4_IFACE1, -- AIC32X4_IFACE1_DATATYPE_MASK | -- AIC32X4_IFACE1_MASTER_MASK, iface_reg_1); -+ AIC32X4_IFACE1_DATATYPE_MASK | -+ AIC32X4_IFACE1_MASTER_MASK, -+ iface_reg_1); - snd_soc_component_update_bits(component, AIC32X4_IFACE2, -- AIC32X4_DATA_OFFSET_MASK, iface_reg_2); -+ AIC32X4_DATA_OFFSET_MASK, iface_reg_2); - snd_soc_component_update_bits(component, AIC32X4_IFACE3, -- AIC32X4_BCLKINV_MASK, iface_reg_3); -+ AIC32X4_BCLKINV_MASK, iface_reg_3); - - return 0; - } -@@ -676,14 +671,13 @@ static int aic32x4_set_aosr(struct snd_soc_component *component, u8 aosr) - static int aic32x4_set_dosr(struct snd_soc_component *component, u16 dosr) - { - snd_soc_component_write(component, AIC32X4_DOSRMSB, dosr >> 8); -- snd_soc_component_write(component, AIC32X4_DOSRLSB, -- (dosr & 0xff)); -+ snd_soc_component_write(component, AIC32X4_DOSRLSB, dosr & 0xff); - - return 0; - } - - static int aic32x4_set_processing_blocks(struct snd_soc_component *component, -- u8 r_block, u8 p_block) -+ u8 r_block, u8 p_block) - { - struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); - -@@ -703,6 +697,45 @@ static int aic32x4_set_processing_blocks(struct snd_soc_component *component, - return 0; - } - -+static int aic32x4_configure_rate(struct snd_soc_component *component, -+ unsigned int rate, u8 *aosr, u8 *adc_rc, -+ u8 *dac_rc, u8 *dosr_inc) -+{ -+ struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); -+ u8 prb_rx, prb_tx; -+ -+ if (rate <= 48000) { -+ *aosr = 128; -+ *adc_rc = 6; -+ *dac_rc = 8; -+ *dosr_inc = 8; -+ prb_rx = 1; -+ prb_tx = 1; -+ } else if (rate <= 96000) { -+ *aosr = 64; -+ *adc_rc = 6; -+ *dac_rc = 8; -+ *dosr_inc = 4; -+ prb_rx = 1; -+ prb_tx = (aic32x4->type == AIC32X4_TYPE_TAS2505) ? 1 : 9; -+ } else if (rate == 192000) { -+ *aosr = 32; -+ *adc_rc = 3; -+ *dac_rc = 4; -+ *dosr_inc = 2; -+ prb_rx = 13; -+ prb_tx = (aic32x4->type == AIC32X4_TYPE_TAS2505) ? 1 : 19; -+ } else { -+ dev_err(component->dev, "Sampling rate %u not supported\n", rate); -+ return -EINVAL; -+ } -+ -+ if (aic32x4->type == AIC32X4_TYPE_TAS2505) -+ prb_rx = 0; -+ -+ return aic32x4_set_processing_blocks(component, prb_rx, prb_tx); -+} -+ - static int aic32x4_setup_clocks(struct snd_soc_component *component, - unsigned int sample_rate, unsigned int channels, - unsigned int bit_depth) -@@ -717,7 +750,7 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, - unsigned long adc_clock_rate, dac_clock_rate; - int ret; - -- static struct clk_bulk_data clocks[] = { -+ struct clk_bulk_data clocks[] = { - { .id = "pll" }, - { .id = "nadc" }, - { .id = "madc" }, -@@ -729,37 +762,11 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, - if (ret) - return ret; - -- if (sample_rate <= 48000) { -- aosr = 128; -- adc_resource_class = 6; -- dac_resource_class = 8; -- dosr_increment = 8; -- if (aic32x4->type == AIC32X4_TYPE_TAS2505) -- aic32x4_set_processing_blocks(component, 0, 1); -- else -- aic32x4_set_processing_blocks(component, 1, 1); -- } else if (sample_rate <= 96000) { -- aosr = 64; -- adc_resource_class = 6; -- dac_resource_class = 8; -- dosr_increment = 4; -- if (aic32x4->type == AIC32X4_TYPE_TAS2505) -- aic32x4_set_processing_blocks(component, 0, 1); -- else -- aic32x4_set_processing_blocks(component, 1, 9); -- } else if (sample_rate == 192000) { -- aosr = 32; -- adc_resource_class = 3; -- dac_resource_class = 4; -- dosr_increment = 2; -- if (aic32x4->type == AIC32X4_TYPE_TAS2505) -- aic32x4_set_processing_blocks(component, 0, 1); -- else -- aic32x4_set_processing_blocks(component, 13, 19); -- } else { -- dev_err(component->dev, "Sampling rate not supported\n"); -- return -EINVAL; -- } -+ ret = aic32x4_configure_rate(component, sample_rate, &aosr, -+ &adc_resource_class, &dac_resource_class, -+ &dosr_increment); -+ if (ret) -+ return ret; - - /* PCM over I2S is always 2-channel */ - if ((aic32x4->fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S) -@@ -781,50 +788,39 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, - (min_mdac * dosr * sample_rate); - for (mdac = min_mdac; mdac <= 128; ++mdac) { - for (ndac = max_ndac; ndac > 0; --ndac) { -- dac_clock_rate = ndac * mdac * dosr * -- sample_rate; -- if (dac_clock_rate == adc_clock_rate) { -- if (clk_round_rate(clocks[0].clk, dac_clock_rate) == 0) -- continue; -+ dac_clock_rate = ndac * mdac * dosr * sample_rate; -+ if (dac_clock_rate != adc_clock_rate) -+ continue; - -- clk_set_rate(clocks[0].clk, -- dac_clock_rate); -+ if (clk_round_rate(clocks[0].clk, dac_clock_rate) == 0) -+ continue; - -- clk_set_rate(clocks[1].clk, -- sample_rate * aosr * -- madc); -- clk_set_rate(clocks[2].clk, -- sample_rate * aosr); -- aic32x4_set_aosr(component, -- aosr); -+ clk_set_rate(clocks[0].clk, dac_clock_rate); - -- clk_set_rate(clocks[3].clk, -- sample_rate * dosr * -- mdac); -- clk_set_rate(clocks[4].clk, -- sample_rate * dosr); -- aic32x4_set_dosr(component, -- dosr); -+ clk_set_rate(clocks[1].clk, sample_rate * aosr * madc); -+ clk_set_rate(clocks[2].clk, sample_rate * aosr); -+ aic32x4_set_aosr(component, aosr); - -- clk_set_rate(clocks[5].clk, -- sample_rate * channels * -- bit_depth); -+ clk_set_rate(clocks[3].clk, sample_rate * dosr * mdac); -+ clk_set_rate(clocks[4].clk, sample_rate * dosr); -+ aic32x4_set_dosr(component, dosr); - -- return 0; -- } -+ clk_set_rate(clocks[5].clk, -+ sample_rate * channels * bit_depth); -+ -+ return 0; - } - } - } - } - -- dev_err(component->dev, -- "Could not set clocks to support sample rate.\n"); -+ dev_err(component->dev, "Could not set clocks to support sample rate.\n"); - return -EINVAL; - } - - static int aic32x4_hw_params(struct snd_pcm_substream *substream, -- struct snd_pcm_hw_params *params, -- struct snd_soc_dai *dai) -+ struct snd_pcm_hw_params *params, -+ struct snd_soc_dai *dai) - { - struct snd_soc_component *component = dai->component; - struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); -@@ -837,24 +833,20 @@ static int aic32x4_hw_params(struct snd_pcm_substream *substream, - - switch (params_physical_width(params)) { - case 16: -- iface1_reg |= (AIC32X4_WORD_LEN_16BITS << -- AIC32X4_IFACE1_DATALEN_SHIFT); -+ iface1_reg |= (AIC32X4_WORD_LEN_16BITS << AIC32X4_IFACE1_DATALEN_SHIFT); - break; - case 20: -- iface1_reg |= (AIC32X4_WORD_LEN_20BITS << -- AIC32X4_IFACE1_DATALEN_SHIFT); -+ iface1_reg |= (AIC32X4_WORD_LEN_20BITS << AIC32X4_IFACE1_DATALEN_SHIFT); - break; - case 24: -- iface1_reg |= (AIC32X4_WORD_LEN_24BITS << -- AIC32X4_IFACE1_DATALEN_SHIFT); -+ iface1_reg |= (AIC32X4_WORD_LEN_24BITS << AIC32X4_IFACE1_DATALEN_SHIFT); - break; - case 32: -- iface1_reg |= (AIC32X4_WORD_LEN_32BITS << -- AIC32X4_IFACE1_DATALEN_SHIFT); -+ iface1_reg |= (AIC32X4_WORD_LEN_32BITS << AIC32X4_IFACE1_DATALEN_SHIFT); - break; - } - snd_soc_component_update_bits(component, AIC32X4_IFACE1, -- AIC32X4_IFACE1_DATALEN_MASK, iface1_reg); -+ AIC32X4_IFACE1_DATALEN_MASK, iface1_reg); - - if (params_channels(params) == 1) { - dacsetup_reg = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2LCHN; -@@ -865,7 +857,7 @@ static int aic32x4_hw_params(struct snd_pcm_substream *substream, - dacsetup_reg = AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN; - } - snd_soc_component_update_bits(component, AIC32X4_DACSETUP, -- AIC32X4_DAC_CHAN_MASK, dacsetup_reg); -+ AIC32X4_DAC_CHAN_MASK, dacsetup_reg); - - return 0; - } -@@ -875,7 +867,7 @@ static int aic32x4_mute(struct snd_soc_dai *dai, int mute, int direction) - struct snd_soc_component *component = dai->component; - - snd_soc_component_update_bits(component, AIC32X4_DACMUTE, -- AIC32X4_MUTEON, mute ? AIC32X4_MUTEON : 0); -+ AIC32X4_MUTEON, mute ? AIC32X4_MUTEON : 0); - - return 0; - } -@@ -886,7 +878,7 @@ static int aic32x4_set_bias_level(struct snd_soc_component *component, - struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); - int ret; - -- static struct clk_bulk_data clocks[] = { -+ struct clk_bulk_data clocks[] = { - { .id = "madc" }, - { .id = "mdac" }, - { .id = "bdiv" }, -@@ -955,44 +947,14 @@ static void aic32x4_setup_gpios(struct snd_soc_component *component) - struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); - - /* setup GPIO functions */ -- /* MFP1 */ -- if (aic32x4->setup->gpio_func[0] != AIC32X4_MFPX_DEFAULT_VALUE) { -- snd_soc_component_write(component, AIC32X4_DINCTL, -- aic32x4->setup->gpio_func[0]); -- snd_soc_add_component_controls(component, aic32x4_mfp1, -- ARRAY_SIZE(aic32x4_mfp1)); -- } -+ BUILD_BUG_ON(ARRAY_SIZE(aic32x4->gpio_func) != ARRAY_SIZE(aic32x4_mfp_cfg)); -+ for (int i = 0; i < ARRAY_SIZE(aic32x4->gpio_func); i++) { -+ if (aic32x4->gpio_func[i] == AIC32X4_MFPX_DEFAULT_VALUE) -+ continue; - -- /* MFP2 */ -- if (aic32x4->setup->gpio_func[1] != AIC32X4_MFPX_DEFAULT_VALUE) { -- snd_soc_component_write(component, AIC32X4_DOUTCTL, -- aic32x4->setup->gpio_func[1]); -- snd_soc_add_component_controls(component, aic32x4_mfp2, -- ARRAY_SIZE(aic32x4_mfp2)); -- } -- -- /* MFP3 */ -- if (aic32x4->setup->gpio_func[2] != AIC32X4_MFPX_DEFAULT_VALUE) { -- snd_soc_component_write(component, AIC32X4_SCLKCTL, -- aic32x4->setup->gpio_func[2]); -- snd_soc_add_component_controls(component, aic32x4_mfp3, -- ARRAY_SIZE(aic32x4_mfp3)); -- } -- -- /* MFP4 */ -- if (aic32x4->setup->gpio_func[3] != AIC32X4_MFPX_DEFAULT_VALUE) { -- snd_soc_component_write(component, AIC32X4_MISOCTL, -- aic32x4->setup->gpio_func[3]); -- snd_soc_add_component_controls(component, aic32x4_mfp4, -- ARRAY_SIZE(aic32x4_mfp4)); -- } -- -- /* MFP5 */ -- if (aic32x4->setup->gpio_func[4] != AIC32X4_MFPX_DEFAULT_VALUE) { -- snd_soc_component_write(component, AIC32X4_GPIOCTL, -- aic32x4->setup->gpio_func[4]); -- snd_soc_add_component_controls(component, aic32x4_mfp5, -- ARRAY_SIZE(aic32x4_mfp5)); -+ snd_soc_component_write(component, aic32x4_mfp_cfg[i].reg, -+ aic32x4->gpio_func[i]); -+ snd_soc_add_component_controls(component, &aic32x4_mfp_cfg[i].ctrl, 1); - } - } - -@@ -1002,7 +964,7 @@ static int aic32x4_component_probe(struct snd_soc_component *component) - u32 tmp_reg; - int ret; - -- static struct clk_bulk_data clocks[] = { -+ struct clk_bulk_data clocks[] = { - { .id = "codec_clkin" }, - { .id = "pll" }, - { .id = "bdiv" }, -@@ -1013,8 +975,7 @@ static int aic32x4_component_probe(struct snd_soc_component *component) - if (ret) - return ret; - -- if (aic32x4->setup) -- aic32x4_setup_gpios(component); -+ aic32x4_setup_gpios(component); - - clk_set_parent(clocks[0].clk, clocks[1].clk); - clk_set_parent(clocks[2].clk, clocks[3].clk); -@@ -1022,7 +983,7 @@ static int aic32x4_component_probe(struct snd_soc_component *component) - /* Power platform configuration */ - if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) { - snd_soc_component_write(component, AIC32X4_MICBIAS, -- AIC32X4_MICBIAS_LDOIN | AIC32X4_MICBIAS_2075V); -+ AIC32X4_MICBIAS_LDOIN | AIC32X4_MICBIAS_2075V); - } - if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE) - snd_soc_component_write(component, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE); -@@ -1041,16 +1002,16 @@ static int aic32x4_component_probe(struct snd_soc_component *component) - /* Mic PGA routing */ - if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K) - snd_soc_component_write(component, AIC32X4_LMICPGANIN, -- AIC32X4_LMICPGANIN_IN2R_10K); -+ AIC32X4_LMICPGANIN_IN2R_10K); - else - snd_soc_component_write(component, AIC32X4_LMICPGANIN, -- AIC32X4_LMICPGANIN_CM1L_10K); -+ AIC32X4_LMICPGANIN_CM1L_10K); - if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K) - snd_soc_component_write(component, AIC32X4_RMICPGANIN, -- AIC32X4_RMICPGANIN_IN1L_10K); -+ AIC32X4_RMICPGANIN_IN1L_10K); - else - snd_soc_component_write(component, AIC32X4_RMICPGANIN, -- AIC32X4_RMICPGANIN_CM1R_10K); -+ AIC32X4_RMICPGANIN_CM1R_10K); - - /* - * Workaround: for an unknown reason, the ADC needs to be powered up -@@ -1058,8 +1019,8 @@ static int aic32x4_component_probe(struct snd_soc_component *component) - * a HW BUG or some kind of behavior not documented in the datasheet. - */ - tmp_reg = snd_soc_component_read(component, AIC32X4_ADCSETUP); -- snd_soc_component_write(component, AIC32X4_ADCSETUP, tmp_reg | -- AIC32X4_LADC_EN | AIC32X4_RADC_EN); -+ snd_soc_component_write(component, AIC32X4_ADCSETUP, -+ tmp_reg | AIC32X4_LADC_EN | AIC32X4_RADC_EN); - snd_soc_component_write(component, AIC32X4_ADCSETUP, tmp_reg); - - /* -@@ -1102,13 +1063,13 @@ static const struct snd_kcontrol_new aic32x4_tas2505_snd_controls[] = { - SOC_ENUM("DAC Playback PowerTune Switch", l_ptm_enum), - - SOC_SINGLE_TLV("HP Driver Gain Volume", -- AIC32X4_HPLGAIN, 0, 0x74, 1, tlv_tas_driver_gain), -+ AIC32X4_HPLGAIN, 0, 0x74, 1, tlv_tas_driver_gain), - SOC_SINGLE("HP DAC Playback Switch", AIC32X4_HPLGAIN, 6, 1, 1), - - SOC_SINGLE_TLV("Speaker Driver Playback Volume", -- TAS2505_SPKVOL1, 0, 0x74, 1, tlv_tas_driver_gain), -+ TAS2505_SPKVOL1, 0, 0x74, 1, tlv_tas_driver_gain), - SOC_SINGLE_TLV("Speaker Amplifier Playback Volume", -- TAS2505_SPKVOL2, 4, 5, 0, tlv_amp_vol), -+ TAS2505_SPKVOL2, 4, 5, 0, tlv_amp_vol), - - SOC_SINGLE("Auto-mute Switch", AIC32X4_DACMUTE, 4, 7, 0), - }; -@@ -1144,11 +1105,12 @@ static const struct snd_soc_dapm_route aic32x4_tas2505_dapm_routes[] = { - static struct snd_soc_dai_driver aic32x4_tas2505_dai = { - .name = "tas2505-hifi", - .playback = { -- .stream_name = "Playback", -- .channels_min = 1, -- .channels_max = 2, -- .rates = SNDRV_PCM_RATE_8000_96000, -- .formats = AIC32X4_FORMATS,}, -+ .stream_name = "Playback", -+ .channels_min = 1, -+ .channels_max = 2, -+ .rates = SNDRV_PCM_RATE_8000_96000, -+ .formats = AIC32X4_FORMATS, -+ }, - .ops = &aic32x4_ops, - .symmetric_rate = 1, - }; -@@ -1159,7 +1121,7 @@ static int aic32x4_tas2505_component_probe(struct snd_soc_component *component) - u32 tmp_reg; - int ret; - -- static struct clk_bulk_data clocks[] = { -+ struct clk_bulk_data clocks[] = { - { .id = "codec_clkin" }, - { .id = "pll" }, - { .id = "bdiv" }, -@@ -1170,8 +1132,7 @@ static int aic32x4_tas2505_component_probe(struct snd_soc_component *component) - if (ret) - return ret; - -- if (aic32x4->setup) -- aic32x4_setup_gpios(component); -+ aic32x4_setup_gpios(component); - - clk_set_parent(clocks[0].clk, clocks[1].clk); - clk_set_parent(clocks[2].clk, clocks[3].clk); -@@ -1192,7 +1153,7 @@ static int aic32x4_tas2505_component_probe(struct snd_soc_component *component) - snd_soc_component_write(component, AIC32X4_CMMODE, tmp_reg); - - /* -- * Enable the fast charging feature and ensure the needed 40ms ellapsed -+ * Enable the fast charging feature and ensure the needed 40ms elapsed - * before using the analog circuits. - */ - snd_soc_component_write(component, TAS2505_REFPOWERUP, -@@ -1218,17 +1179,10 @@ static const struct snd_soc_component_driver soc_component_dev_aic32x4_tas2505 = - .endianness = 1, - }; - --static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, -- struct device_node *np) -+static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, struct device_node *np) - { -- struct aic32x4_setup_data *aic32x4_setup; - int ret; - -- aic32x4_setup = devm_kzalloc(aic32x4->dev, sizeof(*aic32x4_setup), -- GFP_KERNEL); -- if (!aic32x4_setup) -- return -ENOMEM; -- - ret = of_property_match_string(np, "clock-names", "mclk"); - if (ret < 0) - return -EINVAL; -@@ -1245,9 +1199,11 @@ static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, - gpiod_set_consumer_name(aic32x4->rstn_gpio, "tlv320aic32x4_rstn"); - } - -- if (of_property_read_u32_array(np, "aic32x4-gpio-func", -- aic32x4_setup->gpio_func, 5) >= 0) -- aic32x4->setup = aic32x4_setup; -+ for (int i = 0; i < ARRAY_SIZE(aic32x4->gpio_func); i++) -+ aic32x4->gpio_func[i] = AIC32X4_MFPX_DEFAULT_VALUE; -+ of_property_read_u32_array(np, "aic32x4-gpio-func", -+ aic32x4->gpio_func, ARRAY_SIZE(aic32x4->gpio_func)); -+ - return 0; - } - -@@ -1266,7 +1222,7 @@ static void aic32x4_disable_regulators(struct aic32x4_priv *aic32x4) - } - - static int aic32x4_setup_regulators(struct device *dev, -- struct aic32x4_priv *aic32x4) -+ struct aic32x4_priv *aic32x4) - { - int ret = 0; - -@@ -1356,12 +1312,8 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap, - struct device_node *np = dev->of_node; - int ret; - -- if (IS_ERR(regmap)) -- return PTR_ERR(regmap); -- -- aic32x4 = devm_kzalloc(dev, sizeof(struct aic32x4_priv), -- GFP_KERNEL); -- if (aic32x4 == NULL) -+ aic32x4 = devm_kzalloc(dev, sizeof(struct aic32x4_priv), GFP_KERNEL); -+ if (!aic32x4) - return -ENOMEM; - - aic32x4->dev = dev; -@@ -1405,11 +1357,13 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap, - switch (aic32x4->type) { - case AIC32X4_TYPE_TAS2505: - ret = devm_snd_soc_register_component(dev, -- &soc_component_dev_aic32x4_tas2505, &aic32x4_tas2505_dai, 1); -+ &soc_component_dev_aic32x4_tas2505, -+ &aic32x4_tas2505_dai, 1); - break; - default: - ret = devm_snd_soc_register_component(dev, -- &soc_component_dev_aic32x4, &aic32x4_dai, 1); -+ &soc_component_dev_aic32x4, -+ &aic32x4_dai, 1); - } - - if (ret) { -diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h ---- a/sound/soc/codecs/tlv320aic32x4.h -+++ b/sound/soc/codecs/tlv320aic32x4.h -@@ -3,7 +3,6 @@ - * tlv320aic32x4.h - */ - -- - #ifndef _TLV320AIC32X4_H - #define _TLV320AIC32X4_H - -@@ -16,7 +15,7 @@ enum aic32x4_type { - AIC32X4_TYPE_TAS2505, - }; - --extern const struct regmap_config aic32x4_regmap_config; -+extern const struct regmap_range_cfg aic32x4_regmap_pages[]; - int aic32x4_probe(struct device *dev, struct regmap *regmap, - enum aic32x4_type type); - void aic32x4_remove(struct device *dev); -@@ -24,7 +23,7 @@ int aic32x4_register_clocks(struct device *dev, const char *mclk_name); - - /* tlv320aic32x4 register space (in decimal to match datasheet) */ - --#define AIC32X4_REG(page, reg) ((page * 128) + reg) -+#define AIC32X4_REG(page, reg) (((page) * 128) + (reg)) - - #define AIC32X4_PSEL AIC32X4_REG(0, 0) - -@@ -234,4 +233,31 @@ int aic32x4_register_clocks(struct device *dev, const char *mclk_name); - #define AIC32X4_MAX_CODEC_CLKIN_FREQ 110000000 - #define AIC32X4_MAX_PLL_CLKIN 20000000 - -+#define AIC32X4_PWR_MICBIAS_2075_LDOIN 0x00000001 -+#define AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE 0x00000002 -+#define AIC32X4_PWR_AIC32X4_LDO_ENABLE 0x00000004 -+#define AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36 0x00000008 -+#define AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED 0x00000010 -+ -+#define AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K 0x00000001 -+#define AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K 0x00000002 -+ -+/* GPIO API */ -+#define AIC32X4_MFPX_DEFAULT_VALUE 0xff -+ -+#define AIC32X4_MFP1_DIN_DISABLED 0 -+#define AIC32X4_MFP1_DIN_ENABLED 0x2 -+#define AIC32X4_MFP1_GPIO_IN 0x4 -+ -+#define AIC32X4_MFP2_GPIO_OUT_LOW 0x0 -+#define AIC32X4_MFP2_GPIO_OUT_HIGH 0x1 -+ -+#define AIC32X4_MFP_GPIO_ENABLED 0x4 -+ -+#define AIC32X4_MFP5_GPIO_DISABLED 0x0 -+#define AIC32X4_MFP5_GPIO_INPUT 0x8 -+#define AIC32X4_MFP5_GPIO_OUTPUT 0xc -+#define AIC32X4_MFP5_GPIO_OUT_LOW 0x0 -+#define AIC32X4_MFP5_GPIO_OUT_HIGH 0x1 -+ - #endif /* _TLV320AIC32X4_H */ -diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c ---- a/sound/soc/codecs/tlv320dac33.c -+++ b/sound/soc/codecs/tlv320dac33.c -@@ -7,6 +7,7 @@ - * Copyright: (C) 2009 Nokia Corporation - */ - -+#include - #include - #include - #include -@@ -236,13 +237,10 @@ static int dac33_write_locked(struct snd_soc_component *component, unsigned int - unsigned int value) - { - struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); -- int ret; - -- mutex_lock(&dac33->mutex); -- ret = dac33_write(component, reg, value); -- mutex_unlock(&dac33->mutex); -+ guard(mutex)(&dac33->mutex); - -- return ret; -+ return dac33_write(component, reg, value); - } - - #define DAC33_I2C_ADDR_AUTOINC 0x80 -@@ -365,13 +363,13 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) - struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); - int ret = 0; - -- mutex_lock(&dac33->mutex); -+ guard(mutex)(&dac33->mutex); - - /* Safety check */ - if (unlikely(power == dac33->chip_power)) { - dev_dbg(component->dev, "Trying to set the same power state: %s\n", - power ? "ON" : "OFF"); -- goto exit; -+ return ret; - } - - if (power) { -@@ -380,7 +378,7 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) - if (ret != 0) { - dev_err(component->dev, - "Failed to enable supplies: %d\n", ret); -- goto exit; -+ return ret; - } - - if (dac33->reset_gpiod) { -@@ -388,7 +386,7 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) - if (ret < 0) { - dev_err(&dac33->i2c->dev, - "Failed to set reset GPIO: %d\n", ret); -- goto exit; -+ return ret; - } - } - -@@ -400,7 +398,7 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) - if (ret < 0) { - dev_err(&dac33->i2c->dev, - "Failed to set reset GPIO: %d\n", ret); -- goto exit; -+ return ret; - } - } - -@@ -409,14 +407,12 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) - if (ret != 0) { - dev_err(component->dev, - "Failed to disable supplies: %d\n", ret); -- goto exit; -+ return ret; - } - - dac33->chip_power = 0; - } - --exit: -- mutex_unlock(&dac33->mutex); - return ret; - } - -@@ -659,7 +655,6 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) - { - struct snd_soc_component *component = dac33->component; - unsigned int delay; -- unsigned long flags; - - switch (dac33->fifo_mode) { - case DAC33_FIFO_MODE1: -@@ -667,10 +662,10 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) - DAC33_THRREG(dac33->nsample)); - - /* Take the timestamps */ -- spin_lock_irqsave(&dac33->lock, flags); -- dac33->t_stamp2 = ktime_to_us(ktime_get()); -- dac33->t_stamp1 = dac33->t_stamp2; -- spin_unlock_irqrestore(&dac33->lock, flags); -+ scoped_guard(spinlock_irqsave, &dac33->lock) { -+ dac33->t_stamp2 = ktime_to_us(ktime_get()); -+ dac33->t_stamp1 = dac33->t_stamp2; -+ } - - dac33_write16(component, DAC33_PREFILL_MSB, - DAC33_THRREG(dac33->alarm_threshold)); -@@ -682,11 +677,11 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) - break; - case DAC33_FIFO_MODE7: - /* Take the timestamp */ -- spin_lock_irqsave(&dac33->lock, flags); -- dac33->t_stamp1 = ktime_to_us(ktime_get()); -- /* Move back the timestamp with drain time */ -- dac33->t_stamp1 -= dac33->mode7_us_to_lthr; -- spin_unlock_irqrestore(&dac33->lock, flags); -+ scoped_guard(spinlock_irqsave, &dac33->lock) { -+ dac33->t_stamp1 = ktime_to_us(ktime_get()); -+ /* Move back the timestamp with drain time */ -+ dac33->t_stamp1 -= dac33->mode7_us_to_lthr; -+ } - - dac33_write16(component, DAC33_PREFILL_MSB, - DAC33_THRREG(DAC33_MODE7_MARGIN)); -@@ -704,14 +699,12 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) - static inline void dac33_playback_handler(struct tlv320dac33_priv *dac33) - { - struct snd_soc_component *component = dac33->component; -- unsigned long flags; - - switch (dac33->fifo_mode) { - case DAC33_FIFO_MODE1: - /* Take the timestamp */ -- spin_lock_irqsave(&dac33->lock, flags); -- dac33->t_stamp2 = ktime_to_us(ktime_get()); -- spin_unlock_irqrestore(&dac33->lock, flags); -+ scoped_guard(spinlock_irqsave, &dac33->lock) -+ dac33->t_stamp2 = ktime_to_us(ktime_get()); - - dac33_write16(component, DAC33_NSAMPLE_MSB, - DAC33_THRREG(dac33->nsample)); -@@ -735,7 +728,7 @@ static void dac33_work(struct work_struct *work) - dac33 = container_of(work, struct tlv320dac33_priv, work); - component = dac33->component; - -- mutex_lock(&dac33->mutex); -+ guard(mutex)(&dac33->mutex); - switch (dac33->state) { - case DAC33_PREFILL: - dac33->state = DAC33_PLAYBACK; -@@ -757,18 +750,15 @@ static void dac33_work(struct work_struct *work) - dac33_write(component, DAC33_FIFO_CTRL_A, reg); - break; - } -- mutex_unlock(&dac33->mutex); - } - - static irqreturn_t dac33_interrupt_handler(int irq, void *dev) - { - struct snd_soc_component *component = dev; - struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); -- unsigned long flags; - -- spin_lock_irqsave(&dac33->lock, flags); -- dac33->t_stamp1 = ktime_to_us(ktime_get()); -- spin_unlock_irqrestore(&dac33->lock, flags); -+ scoped_guard(spinlock_irqsave, &dac33->lock) -+ dac33->t_stamp1 = ktime_to_us(ktime_get()); - - /* Do not schedule the workqueue in Mode7 */ - if (dac33->fifo_mode != DAC33_FIFO_MODE7) -@@ -902,14 +892,13 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream, - return -EINVAL; - } - -- mutex_lock(&dac33->mutex); -+ guard(mutex)(&dac33->mutex); - - if (!dac33->chip_power) { - /* - * Chip is not powered yet. - * Do the init in the dac33_set_bias_level later. - */ -- mutex_unlock(&dac33->mutex); - return 0; - } - -@@ -1053,8 +1042,6 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream, - break; - } - -- mutex_unlock(&dac33->mutex); -- - return 0; - } - -@@ -1156,21 +1143,20 @@ static snd_pcm_sframes_t dac33_dai_delay( - unsigned int time_delta, uthr; - int samples_out, samples_in, samples; - snd_pcm_sframes_t delay = 0; -- unsigned long flags; - - switch (dac33->fifo_mode) { - case DAC33_FIFO_BYPASS: - break; - case DAC33_FIFO_MODE1: -- spin_lock_irqsave(&dac33->lock, flags); -- t0 = dac33->t_stamp1; -- t1 = dac33->t_stamp2; -- spin_unlock_irqrestore(&dac33->lock, flags); -+ scoped_guard(spinlock_irqsave, &dac33->lock) { -+ t0 = dac33->t_stamp1; -+ t1 = dac33->t_stamp2; -+ } - t_now = ktime_to_us(ktime_get()); - - /* We have not started to fill the FIFO yet, delay is 0 */ - if (!t1) -- goto out; -+ return 0; - - if (t0 > t1) { - /* -@@ -1230,23 +1216,22 @@ static snd_pcm_sframes_t dac33_dai_delay( - } - break; - case DAC33_FIFO_MODE7: -- spin_lock_irqsave(&dac33->lock, flags); -- t0 = dac33->t_stamp1; -- uthr = dac33->uthr; -- spin_unlock_irqrestore(&dac33->lock, flags); -+ scoped_guard(spinlock_irqsave, &dac33->lock) { -+ t0 = dac33->t_stamp1; -+ uthr = dac33->uthr; -+ } - t_now = ktime_to_us(ktime_get()); - - /* We have not started to fill the FIFO yet, delay is 0 */ - if (!t0) -- goto out; -+ return 0; - - if (t_now <= t0) { - /* - * Either the timestamps are messed or equal. Report - * maximum delay - */ -- delay = uthr; -- goto out; -+ return uthr; - } - - time_delta = t_now - t0; -@@ -1287,7 +1272,7 @@ static snd_pcm_sframes_t dac33_dai_delay( - dac33->fifo_mode); - break; - } --out: -+ - return delay; - } - -diff --git a/sound/soc/codecs/tscs42xx.c b/sound/soc/codecs/tscs42xx.c ---- a/sound/soc/codecs/tscs42xx.c -+++ b/sound/soc/codecs/tscs42xx.c -@@ -12,6 +12,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -210,25 +211,21 @@ static int power_up_audio_plls(struct snd_soc_component *component) - return ret; - } - -- mutex_lock(&tscs42xx->pll_lock); -+ guard(mutex)(&tscs42xx->pll_lock); - - ret = snd_soc_component_update_bits(component, R_PLLCTL1C, mask, val); - if (ret < 0) { - dev_err(component->dev, "Failed to turn PLL on (%d)\n", ret); -- goto exit; -+ return ret; - } - - if (!plls_locked(component)) { - dev_err(component->dev, "Failed to lock plls\n"); - ret = -ENOMSG; -- goto exit; -+ return ret; - } - -- ret = 0; --exit: -- mutex_unlock(&tscs42xx->pll_lock); -- -- return ret; -+ return 0; - } - - static int power_down_audio_plls(struct snd_soc_component *component) -@@ -236,28 +233,24 @@ static int power_down_audio_plls(struct snd_soc_component *component) - struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); - int ret; - -- mutex_lock(&tscs42xx->pll_lock); -+ guard(mutex)(&tscs42xx->pll_lock); - - ret = snd_soc_component_update_bits(component, R_PLLCTL1C, - RM_PLLCTL1C_PDB_PLL1, - RV_PLLCTL1C_PDB_PLL1_DISABLE); - if (ret < 0) { - dev_err(component->dev, "Failed to turn PLL off (%d)\n", ret); -- goto exit; -+ return ret; - } - ret = snd_soc_component_update_bits(component, R_PLLCTL1C, - RM_PLLCTL1C_PDB_PLL2, - RV_PLLCTL1C_PDB_PLL2_DISABLE); - if (ret < 0) { - dev_err(component->dev, "Failed to turn PLL off (%d)\n", ret); -- goto exit; -+ return ret; - } - -- ret = 0; --exit: -- mutex_unlock(&tscs42xx->pll_lock); -- -- return ret; -+ return 0; - } - - static int coeff_ram_get(struct snd_kcontrol *kcontrol, -@@ -269,13 +262,11 @@ static int coeff_ram_get(struct snd_kcontrol *kcontrol, - (struct coeff_ram_ctl *)kcontrol->private_value; - struct soc_bytes_ext *params = &ctl->bytes_ext; - -- mutex_lock(&tscs42xx->coeff_ram_lock); -+ guard(mutex)(&tscs42xx->coeff_ram_lock); - - memcpy(ucontrol->value.bytes.data, - &tscs42xx->coeff_ram[ctl->addr * COEFF_SIZE], params->max); - -- mutex_unlock(&tscs42xx->coeff_ram_lock); -- - return 0; - } - -@@ -290,14 +281,14 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol, - unsigned int coeff_cnt = params->max / COEFF_SIZE; - int ret; - -- mutex_lock(&tscs42xx->coeff_ram_lock); -+ guard(mutex)(&tscs42xx->coeff_ram_lock); - - tscs42xx->coeff_ram_synced = false; - - memcpy(&tscs42xx->coeff_ram[ctl->addr * COEFF_SIZE], - ucontrol->value.bytes.data, params->max); - -- mutex_lock(&tscs42xx->pll_lock); -+ guard(mutex)(&tscs42xx->pll_lock); - - if (plls_locked(component)) { - ret = write_coeff_ram(component, tscs42xx->coeff_ram, -@@ -305,18 +296,12 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol, - if (ret < 0) { - dev_err(component->dev, - "Failed to flush coeff ram cache (%d)\n", ret); -- goto exit; -+ return ret; - } - tscs42xx->coeff_ram_synced = true; - } - -- ret = 0; --exit: -- mutex_unlock(&tscs42xx->pll_lock); -- -- mutex_unlock(&tscs42xx->coeff_ram_lock); -- -- return ret; -+ return 0; - } - - /* Input L Capture Route */ -@@ -385,21 +370,17 @@ static int dac_event(struct snd_soc_dapm_widget *w, - struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); - int ret; - -- mutex_lock(&tscs42xx->coeff_ram_lock); -+ guard(mutex)(&tscs42xx->coeff_ram_lock); - - if (!tscs42xx->coeff_ram_synced) { - ret = write_coeff_ram(component, tscs42xx->coeff_ram, 0x00, - COEFF_RAM_COEFF_COUNT); - if (ret < 0) -- goto exit; -+ return ret; - tscs42xx->coeff_ram_synced = true; - } - -- ret = 0; --exit: -- mutex_unlock(&tscs42xx->coeff_ram_lock); -- -- return ret; -+ return 0; - } - - static const struct snd_soc_dapm_widget tscs42xx_dapm_widgets[] = { -@@ -926,12 +907,10 @@ static int setup_sample_rate(struct snd_soc_component *component, - return ret; - } - -- mutex_lock(&tscs42xx->audio_params_lock); -+ guard(mutex)(&tscs42xx->audio_params_lock); - - tscs42xx->samplerate = rate; - -- mutex_unlock(&tscs42xx->audio_params_lock); -- - return 0; - } - -@@ -1253,12 +1232,10 @@ static int tscs42xx_set_dai_bclk_ratio(struct snd_soc_dai *codec_dai, - return ret; - } - -- mutex_lock(&tscs42xx->audio_params_lock); -+ guard(mutex)(&tscs42xx->audio_params_lock); - - tscs42xx->bclk_ratio = ratio; - -- mutex_unlock(&tscs42xx->audio_params_lock); -- - return 0; - } - -diff --git a/sound/soc/codecs/tscs454.c b/sound/soc/codecs/tscs454.c ---- a/sound/soc/codecs/tscs454.c -+++ b/sound/soc/codecs/tscs454.c -@@ -4,6 +4,7 @@ - // Author: Steven Eckhoff - - #include -+#include - #include - #include - #include -@@ -329,12 +330,10 @@ static int coeff_ram_get(struct snd_kcontrol *kcontrol, - return -EINVAL; - } - -- mutex_lock(coeff_ram_lock); -- -- memcpy(ucontrol->value.bytes.data, -- &coeff_ram[ctl->addr * COEFF_SIZE], params->max); -- -- mutex_unlock(coeff_ram_lock); -+ scoped_guard(mutex, coeff_ram_lock) { -+ memcpy(ucontrol->value.bytes.data, -+ &coeff_ram[ctl->addr * COEFF_SIZE], params->max); -+ } - - return 0; - } -@@ -428,15 +427,15 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol, - return -EINVAL; - } - -- mutex_lock(coeff_ram_lock); -+ guard(mutex)(coeff_ram_lock); - - *coeff_ram_synced = false; - - memcpy(&coeff_ram[ctl->addr * COEFF_SIZE], - ucontrol->value.bytes.data, params->max); - -- mutex_lock(&tscs454->pll1.lock); -- mutex_lock(&tscs454->pll2.lock); -+ guard(mutex)(&tscs454->pll1.lock); -+ guard(mutex)(&tscs454->pll2.lock); - - val = snd_soc_component_read(component, R_PLLSTAT); - if (val) { /* PLLs locked */ -@@ -446,18 +445,12 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol, - if (ret < 0) { - dev_err(component->dev, - "Failed to flush coeff ram cache (%d)\n", ret); -- goto exit; -+ return ret; - } - *coeff_ram_synced = true; - } - -- ret = 0; --exit: -- mutex_unlock(&tscs454->pll2.lock); -- mutex_unlock(&tscs454->pll1.lock); -- mutex_unlock(coeff_ram_lock); -- -- return ret; -+ return 0; - } - - static inline int coeff_ram_sync(struct snd_soc_component *component, -@@ -465,41 +458,35 @@ static inline int coeff_ram_sync(struct snd_soc_component *component, - { - int ret; - -- mutex_lock(&tscs454->dac_ram.lock); -- if (!tscs454->dac_ram.synced) { -- ret = write_coeff_ram(component, tscs454->dac_ram.cache, -- R_DACCRS, R_DACCRADD, R_DACCRWDL, -- 0x00, COEFF_RAM_COEFF_COUNT); -- if (ret < 0) { -- mutex_unlock(&tscs454->dac_ram.lock); -- return ret; -+ scoped_guard(mutex, &tscs454->dac_ram.lock) { -+ if (!tscs454->dac_ram.synced) { -+ ret = write_coeff_ram(component, tscs454->dac_ram.cache, -+ R_DACCRS, R_DACCRADD, R_DACCRWDL, -+ 0x00, COEFF_RAM_COEFF_COUNT); -+ if (ret < 0) -+ return ret; - } - } -- mutex_unlock(&tscs454->dac_ram.lock); - -- mutex_lock(&tscs454->spk_ram.lock); -- if (!tscs454->spk_ram.synced) { -- ret = write_coeff_ram(component, tscs454->spk_ram.cache, -- R_SPKCRS, R_SPKCRADD, R_SPKCRWDL, -- 0x00, COEFF_RAM_COEFF_COUNT); -- if (ret < 0) { -- mutex_unlock(&tscs454->spk_ram.lock); -- return ret; -+ scoped_guard(mutex, &tscs454->spk_ram.lock) { -+ if (!tscs454->spk_ram.synced) { -+ ret = write_coeff_ram(component, tscs454->spk_ram.cache, -+ R_SPKCRS, R_SPKCRADD, R_SPKCRWDL, -+ 0x00, COEFF_RAM_COEFF_COUNT); -+ if (ret < 0) -+ return ret; - } - } -- mutex_unlock(&tscs454->spk_ram.lock); - -- mutex_lock(&tscs454->sub_ram.lock); -- if (!tscs454->sub_ram.synced) { -- ret = write_coeff_ram(component, tscs454->sub_ram.cache, -- R_SUBCRS, R_SUBCRADD, R_SUBCRWDL, -- 0x00, COEFF_RAM_COEFF_COUNT); -- if (ret < 0) { -- mutex_unlock(&tscs454->sub_ram.lock); -- return ret; -+ scoped_guard(mutex, &tscs454->sub_ram.lock) { -+ if (!tscs454->sub_ram.synced) { -+ ret = write_coeff_ram(component, tscs454->sub_ram.cache, -+ R_SUBCRS, R_SUBCRADD, R_SUBCRWDL, -+ 0x00, COEFF_RAM_COEFF_COUNT); -+ if (ret < 0) -+ return ret; - } - } -- mutex_unlock(&tscs454->sub_ram.lock); - - return 0; - } -@@ -658,16 +645,14 @@ static int set_sysclk(struct snd_soc_component *component) - - static inline void reserve_pll(struct pll *pll) - { -- mutex_lock(&pll->lock); -+ guard(mutex)(&pll->lock); - pll->users++; -- mutex_unlock(&pll->lock); - } - - static inline void free_pll(struct pll *pll) - { -- mutex_lock(&pll->lock); -+ guard(mutex)(&pll->lock); - pll->users--; -- mutex_unlock(&pll->lock); - } - - static int pll_connected(struct snd_soc_dapm_widget *source, -@@ -679,15 +664,13 @@ static int pll_connected(struct snd_soc_dapm_widget *source, - int users; - - if (strstr(source->name, "PLL 1")) { -- mutex_lock(&tscs454->pll1.lock); -- users = tscs454->pll1.users; -- mutex_unlock(&tscs454->pll1.lock); -+ scoped_guard(mutex, &tscs454->pll1.lock) -+ users = tscs454->pll1.users; - dev_dbg(component->dev, "%s(): PLL 1 users = %d\n", __func__, - users); - } else { -- mutex_lock(&tscs454->pll2.lock); -- users = tscs454->pll2.users; -- mutex_unlock(&tscs454->pll2.lock); -+ scoped_guard(mutex, &tscs454->pll2.lock) -+ users = tscs454->pll2.users; - dev_dbg(component->dev, "%s(): PLL 2 users = %d\n", __func__, - users); - } -@@ -806,7 +789,7 @@ static inline int aif_free(struct snd_soc_component *component, - { - struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); - -- mutex_lock(&tscs454->aifs_status_lock); -+ guard(mutex)(&tscs454->aifs_status_lock); - - dev_dbg(component->dev, "%s(): aif %d\n", __func__, aif->id); - -@@ -829,8 +812,6 @@ static inline int aif_free(struct snd_soc_component *component, - free_pll(tscs454->internal_rate.pll); - } - -- mutex_unlock(&tscs454->aifs_status_lock); -- - return 0; - } - -@@ -3174,7 +3155,7 @@ static int tscs454_hw_params(struct snd_pcm_substream *substream, - unsigned int val; - int ret; - -- mutex_lock(&tscs454->aifs_status_lock); -+ guard(mutex)(&tscs454->aifs_status_lock); - - dev_dbg(component->dev, "%s(): aif %d fs = %u\n", __func__, - aif->id, fs); -@@ -3207,14 +3188,14 @@ static int tscs454_hw_params(struct snd_pcm_substream *substream, - ret = set_aif_fs(component, aif->id, fs); - if (ret < 0) { - dev_err(component->dev, "Failed to set aif fs (%d)\n", ret); -- goto exit; -+ return ret; - } - - ret = set_aif_sample_format(component, params_format(params), aif->id); - if (ret < 0) { - dev_err(component->dev, - "Failed to set aif sample format (%d)\n", ret); -- goto exit; -+ return ret; - } - - set_aif_status_active(&tscs454->aifs_status, aif->id, -@@ -3223,11 +3204,7 @@ static int tscs454_hw_params(struct snd_pcm_substream *substream, - dev_dbg(component->dev, "Set aif %d active. Streams status is 0x%x\n", - aif->id, tscs454->aifs_status.streams); - -- ret = 0; --exit: -- mutex_unlock(&tscs454->aifs_status_lock); -- -- return ret; -+ return 0; - } - - static int tscs454_hw_free(struct snd_pcm_substream *substream, -diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c ---- a/sound/soc/codecs/twl6040.c -+++ b/sound/soc/codecs/twl6040.c -@@ -8,6 +8,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -273,7 +274,7 @@ static void twl6040_hs_jack_report(struct snd_soc_component *component, - struct twl6040_data *priv = snd_soc_component_get_drvdata(component); - int status; - -- mutex_lock(&priv->mutex); -+ guard(mutex)(&priv->mutex); - - /* Sync status */ - status = twl6040_read(component, TWL6040_REG_STATUS); -@@ -281,8 +282,6 @@ static void twl6040_hs_jack_report(struct snd_soc_component *component, - snd_soc_jack_report(jack, report, report); - else - snd_soc_jack_report(jack, 0, report); -- -- mutex_unlock(&priv->mutex); - } - - void twl6040_hs_jack_detect(struct snd_soc_component *component, -diff --git a/sound/soc/codecs/uda1342.c b/sound/soc/codecs/uda1342.c ---- a/sound/soc/codecs/uda1342.c -+++ b/sound/soc/codecs/uda1342.c -@@ -308,9 +308,16 @@ static int uda1342_suspend(struct device *dev) - static int uda1342_resume(struct device *dev) - { - struct uda1342_priv *uda1342 = dev_get_drvdata(dev); -+ int ret; - -+ regcache_cache_only(uda1342->regmap, false); - regcache_mark_dirty(uda1342->regmap); -- regcache_sync(uda1342->regmap); -+ ret = regcache_sync(uda1342->regmap); -+ if (ret) { -+ regcache_cache_only(uda1342->regmap, true); -+ regcache_mark_dirty(uda1342->regmap); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c ---- a/sound/soc/codecs/wcd9335.c -+++ b/sound/soc/codecs/wcd9335.c -@@ -5010,9 +5010,9 @@ static int wcd9335_power_on_reset(struct wcd9335_codec *wcd) - */ - usleep_range(600, 650); - -- gpiod_set_value(wcd->reset_gpio, 1); -+ gpiod_set_value_cansleep(wcd->reset_gpio, 1); - msleep(20); -- gpiod_set_value(wcd->reset_gpio, 0); -+ gpiod_set_value_cansleep(wcd->reset_gpio, 0); - msleep(20); - - return 0; -diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c ---- a/sound/soc/codecs/wcd934x.c -+++ b/sound/soc/codecs/wcd934x.c -@@ -1265,13 +1265,10 @@ static int wcd934x_set_sido_input_src(struct wcd934x_codec *wcd, int sido_src) - - static int wcd934x_enable_ana_bias_and_sysclk(struct wcd934x_codec *wcd) - { -- mutex_lock(&wcd->sysclk_mutex); -- -- if (++wcd->sysclk_users != 1) { -- mutex_unlock(&wcd->sysclk_mutex); -- return 0; -+ scoped_guard(mutex, &wcd->sysclk_mutex) { -+ if (++wcd->sysclk_users != 1) -+ return 0; - } -- mutex_unlock(&wcd->sysclk_mutex); - - regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS, - WCD934X_ANA_BIAS_EN_MASK, -@@ -1328,12 +1325,10 @@ static int wcd934x_enable_ana_bias_and_sysclk(struct wcd934x_codec *wcd) - - static int wcd934x_disable_ana_bias_and_syclk(struct wcd934x_codec *wcd) - { -- mutex_lock(&wcd->sysclk_mutex); -- if (--wcd->sysclk_users != 0) { -- mutex_unlock(&wcd->sysclk_mutex); -- return 0; -+ scoped_guard(mutex, &wcd->sysclk_mutex) { -+ if (--wcd->sysclk_users != 0) -+ return 0; - } -- mutex_unlock(&wcd->sysclk_mutex); - - regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG, - WCD934X_EXT_CLK_BUF_EN_MASK | -@@ -2384,7 +2379,7 @@ static int wcd934x_micbias_control(struct snd_soc_component *component, - __func__, micb_num); - return -EINVAL; - } -- mutex_lock(&wcd934x->micb_lock); -+ guard(mutex)(&wcd934x->micb_lock); - - switch (req) { - case MICB_PULLUP_ENABLE: -@@ -2446,8 +2441,6 @@ static int wcd934x_micbias_control(struct snd_soc_component *component, - break; - } - -- mutex_unlock(&wcd934x->micb_lock); -- - return 0; - } - -@@ -2488,7 +2481,7 @@ static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - int req_volt, int micb_num) - { - struct wcd934x_codec *wcd934x = snd_soc_component_get_drvdata(component); -- int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0; -+ int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en; - - switch (micb_num) { - case MIC_BIAS_1: -@@ -2506,7 +2499,7 @@ static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - default: - return -EINVAL; - } -- mutex_lock(&wcd934x->micb_lock); -+ guard(mutex)(&wcd934x->micb_lock); - /* - * If requested micbias voltage is same as current micbias - * voltage, then just return. Otherwise, adjust voltage as -@@ -2521,15 +2514,11 @@ static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - WCD934X_MICB_VAL_MASK); - - req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt); -- if (req_vout_ctl < 0) { -- ret = -EINVAL; -- goto exit; -- } -+ if (req_vout_ctl < 0) -+ return -EINVAL; - -- if (cur_vout_ctl == req_vout_ctl) { -- ret = 0; -- goto exit; -- } -+ if (cur_vout_ctl == req_vout_ctl) -+ return 0; - - if (micb_en == WCD934X_MICB_ENABLE) - snd_soc_component_write_field(component, micb_reg, -@@ -2550,9 +2539,8 @@ static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - */ - usleep_range(2000, 2100); - } --exit: -- mutex_unlock(&wcd934x->micb_lock); -- return ret; -+ -+ return 0; - } - - static int wcd934x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, -@@ -5899,10 +5887,8 @@ static int wcd934x_codec_probe(struct platform_device *pdev) - } - - static const struct platform_device_id wcd934x_driver_id[] = { -- { -- .name = "wcd934x-codec", -- }, -- {}, -+ { .name = "wcd934x-codec" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, wcd934x_driver_id); - -diff --git a/sound/soc/codecs/wcd937x-sdw.c b/sound/soc/codecs/wcd937x-sdw.c ---- a/sound/soc/codecs/wcd937x-sdw.c -+++ b/sound/soc/codecs/wcd937x-sdw.c -@@ -1084,10 +1084,16 @@ static int wcd937x_sdw_runtime_suspend(struct device *dev) - static int wcd937x_sdw_runtime_resume(struct device *dev) - { - struct wcd937x_sdw_priv *wcd = dev_get_drvdata(dev); -+ int ret; - - if (wcd->regmap) { - regcache_cache_only(wcd->regmap, false); -- regcache_sync(wcd->regmap); -+ ret = regcache_sync(wcd->regmap); -+ if (ret) { -+ regcache_cache_only(wcd->regmap, true); -+ regcache_mark_dirty(wcd->regmap); -+ return ret; -+ } - } - - return 0; -diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c ---- a/sound/soc/codecs/wcd937x.c -+++ b/sound/soc/codecs/wcd937x.c -@@ -2,6 +2,7 @@ - // Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved. - - #include -+#include - #include - #include - #include -@@ -1056,7 +1057,7 @@ static int wcd937x_micbias_control(struct snd_soc_component *component, - return -EINVAL; - } - -- mutex_lock(&wcd937x->micb_lock); -+ guard(mutex)(&wcd937x->micb_lock); - switch (req) { - case MICB_PULLUP_ENABLE: - wcd937x->pullup_ref[micb_index]++; -@@ -1136,7 +1137,6 @@ static int wcd937x_micbias_control(struct snd_soc_component *component, - } - break; - } -- mutex_unlock(&wcd937x->micb_lock); - - return 0; - } -@@ -1460,7 +1460,7 @@ static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - int req_volt, int micb_num) - { - struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); -- int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0; -+ int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en; - - switch (micb_num) { - case MIC_BIAS_1: -@@ -1475,7 +1475,7 @@ static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - default: - return -EINVAL; - } -- mutex_lock(&wcd937x->micb_lock); -+ guard(mutex)(&wcd937x->micb_lock); - /* - * If requested micbias voltage is same as current micbias - * voltage, then just return. Otherwise, adjust voltage as -@@ -1490,15 +1490,11 @@ static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - WCD937X_MICB_VOUT_MASK); - - req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt); -- if (req_vout_ctl < 0) { -- ret = -EINVAL; -- goto exit; -- } -+ if (req_vout_ctl < 0) -+ return -EINVAL; - -- if (cur_vout_ctl == req_vout_ctl) { -- ret = 0; -- goto exit; -- } -+ if (cur_vout_ctl == req_vout_ctl) -+ return 0; - - if (micb_en == WCD937X_MICB_ENABLE) - snd_soc_component_write_field(component, micb_reg, -@@ -1519,9 +1515,8 @@ static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - */ - usleep_range(2000, 2100); - } --exit: -- mutex_unlock(&wcd937x->micb_lock); -- return ret; -+ -+ return 0; - } - - static int wcd937x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, -diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c ---- a/sound/soc/codecs/wcd938x-sdw.c -+++ b/sound/soc/codecs/wcd938x-sdw.c -@@ -1245,10 +1245,16 @@ static int wcd938x_sdw_runtime_suspend(struct device *dev) - static int wcd938x_sdw_runtime_resume(struct device *dev) - { - struct wcd938x_sdw_priv *wcd = dev_get_drvdata(dev); -+ int ret; - - if (wcd->regmap) { - regcache_cache_only(wcd->regmap, false); -- regcache_sync(wcd->regmap); -+ ret = regcache_sync(wcd->regmap); -+ if (ret) { -+ regcache_cache_only(wcd->regmap, true); -+ regcache_mark_dirty(wcd->regmap); -+ return ret; -+ } - } - - pm_runtime_mark_last_busy(dev); -diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c ---- a/sound/soc/codecs/wcd938x.c -+++ b/sound/soc/codecs/wcd938x.c -@@ -1,6 +1,7 @@ - // SPDX-License-Identifier: GPL-2.0-only - // Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. - -+#include - #include - #include - #include -@@ -1976,7 +1977,7 @@ static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - int req_volt, int micb_num) - { - struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); -- int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0; -+ int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en; - - switch (micb_num) { - case MIC_BIAS_1: -@@ -1994,7 +1995,7 @@ static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - default: - return -EINVAL; - } -- mutex_lock(&wcd938x->micb_lock); -+ guard(mutex)(&wcd938x->micb_lock); - /* - * If requested micbias voltage is same as current micbias - * voltage, then just return. Otherwise, adjust voltage as -@@ -2009,15 +2010,11 @@ static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - WCD938X_MICB_VOUT_MASK); - - req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt); -- if (req_vout_ctl < 0) { -- ret = -EINVAL; -- goto exit; -- } -+ if (req_vout_ctl < 0) -+ return -EINVAL; - -- if (cur_vout_ctl == req_vout_ctl) { -- ret = 0; -- goto exit; -- } -+ if (cur_vout_ctl == req_vout_ctl) -+ return 0; - - if (micb_en == WCD938X_MICB_ENABLE) - snd_soc_component_write_field(component, micb_reg, -@@ -2038,9 +2035,8 @@ static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - */ - usleep_range(2000, 2100); - } --exit: -- mutex_unlock(&wcd938x->micb_lock); -- return ret; -+ -+ return 0; - } - - static int wcd938x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, -diff --git a/sound/soc/codecs/wcd939x-sdw.c b/sound/soc/codecs/wcd939x-sdw.c ---- a/sound/soc/codecs/wcd939x-sdw.c -+++ b/sound/soc/codecs/wcd939x-sdw.c -@@ -1431,10 +1431,16 @@ static int wcd939x_sdw_runtime_suspend(struct device *dev) - static int wcd939x_sdw_runtime_resume(struct device *dev) - { - struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev); -+ int ret; - - if (wcd->regmap) { - regcache_cache_only(wcd->regmap, false); -- regcache_sync(wcd->regmap); -+ ret = regcache_sync(wcd->regmap); -+ if (ret) { -+ regcache_cache_only(wcd->regmap, true); -+ regcache_mark_dirty(wcd->regmap); -+ return ret; -+ } - } - - return 0; -diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c ---- a/sound/soc/codecs/wcd939x.c -+++ b/sound/soc/codecs/wcd939x.c -@@ -8,6 +8,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -1923,7 +1924,6 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); - unsigned int micb_reg, cur_vout_ctl, micb_en; - int req_vout_ctl; -- int ret = 0; - - switch (micb_num) { - case MIC_BIAS_1: -@@ -1941,7 +1941,7 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - default: - return -EINVAL; - } -- mutex_lock(&wcd939x->micb_lock); -+ guard(mutex)(&wcd939x->micb_lock); - - /* - * If requested micbias voltage is same as current micbias -@@ -1957,15 +1957,11 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - WCD939X_MICB_VOUT_CTL); - - req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt); -- if (req_vout_ctl < 0) { -- ret = req_vout_ctl; -- goto exit; -- } -+ if (req_vout_ctl < 0) -+ return req_vout_ctl; - -- if (cur_vout_ctl == req_vout_ctl) { -- ret = 0; -- goto exit; -- } -+ if (cur_vout_ctl == req_vout_ctl) -+ return 0; - - dev_dbg(component->dev, "%s: micb_num: %d, cur_mv: %d, req_mv: %d, micb_en: %d\n", - __func__, micb_num, WCD_VOUT_CTL_TO_MICB(cur_vout_ctl), -@@ -1990,9 +1986,7 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - usleep_range(2000, 2100); - } - --exit: -- mutex_unlock(&wcd939x->micb_lock); -- return ret; -+ return 0; - } - - static int wcd939x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, -diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c ---- a/sound/soc/codecs/wm0010.c -+++ b/sound/soc/codecs/wm0010.c -@@ -9,6 +9,7 @@ - * Scott Ling - */ - -+#include - #include - #include - #include -@@ -148,13 +149,11 @@ static const char *wm0010_state_to_str(enum wm0010_state state) - static void wm0010_halt(struct snd_soc_component *component) - { - struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); -- unsigned long flags; - enum wm0010_state state; - - /* Fetch the wm0010 state */ -- spin_lock_irqsave(&wm0010->irq_lock, flags); -- state = wm0010->state; -- spin_unlock_irqrestore(&wm0010->irq_lock, flags); -+ scoped_guard(spinlock_irqsave, &wm0010->irq_lock) -+ state = wm0010->state; - - switch (state) { - case WM0010_POWER_OFF: -@@ -173,9 +172,8 @@ static void wm0010_halt(struct snd_soc_component *component) - break; - } - -- spin_lock_irqsave(&wm0010->irq_lock, flags); -- wm0010->state = WM0010_POWER_OFF; -- spin_unlock_irqrestore(&wm0010->irq_lock, flags); -+ scoped_guard(spinlock_irqsave, &wm0010->irq_lock) -+ wm0010->state = WM0010_POWER_OFF; - } - - struct wm0010_boot_xfer { -@@ -190,11 +188,9 @@ struct wm0010_boot_xfer { - static void wm0010_mark_boot_failure(struct wm0010_priv *wm0010) - { - enum wm0010_state state; -- unsigned long flags; - -- spin_lock_irqsave(&wm0010->irq_lock, flags); -- state = wm0010->state; -- spin_unlock_irqrestore(&wm0010->irq_lock, flags); -+ scoped_guard(spinlock_irqsave, &wm0010->irq_lock) -+ state = wm0010->state; - - dev_err(wm0010->dev, "Failed to transition from `%s' state to `%s' state\n", - wm0010_state_to_str(state), wm0010_state_to_str(state + 1)); -@@ -337,7 +333,6 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp - struct wm0010_boot_xfer *xfer; - int ret; - DECLARE_COMPLETION_ONSTACK(done); -- const struct firmware *fw; - const struct dfw_binrec *rec; - const struct dfw_inforec *inforec; - u64 *img; -@@ -346,6 +341,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp - - INIT_LIST_HEAD(&xfer_list); - -+ const struct firmware *fw __free(firmware) = NULL; - ret = request_firmware(&fw, name, component->dev); - if (ret != 0) { - dev_err(component->dev, "Failed to request application(%s): %d\n", -@@ -364,16 +360,14 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp - /* First record should be INFO */ - if (rec->command != DFW_CMD_INFO) { - dev_err(component->dev, "First record not INFO\r\n"); -- ret = -EINVAL; -- goto abort; -+ return -EINVAL; - } - - if (inforec->info_version != INFO_VERSION) { - dev_err(component->dev, - "Unsupported version (%02d) of INFO record\r\n", - inforec->info_version); -- ret = -EINVAL; -- goto abort; -+ return -EINVAL; - } - - dev_dbg(component->dev, "Version v%02d INFO record found\r\n", -@@ -382,8 +376,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp - /* Check it's a DSP file */ - if (dsp != DEVICE_ID_WM0010) { - dev_err(component->dev, "Not a WM0010 firmware file.\r\n"); -- ret = -EINVAL; -- goto abort; -+ return -EINVAL; - } - - /* Skip the info record as we don't need to send it */ -@@ -408,14 +401,14 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp - out = kzalloc(len, GFP_KERNEL | GFP_DMA); - if (!out) { - ret = -ENOMEM; -- goto abort1; -+ goto abort; - } - xfer->t.rx_buf = out; - - img = kzalloc(len, GFP_KERNEL | GFP_DMA); - if (!img) { - ret = -ENOMEM; -- goto abort1; -+ goto abort; - } - xfer->t.tx_buf = img; - -@@ -453,13 +446,13 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp - ret = spi_async(spi, &xfer->m); - if (ret != 0) { - dev_err(component->dev, "Write failed: %d\n", ret); -- goto abort1; -+ goto abort; - } - - if (wm0010->boot_failed) { - dev_dbg(component->dev, "Boot fail!\n"); - ret = -EINVAL; -- goto abort1; -+ goto abort; - } - } - -@@ -467,7 +460,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp - - ret = 0; - --abort1: -+abort: - while (!list_empty(&xfer_list)) { - xfer = list_first_entry(&xfer_list, struct wm0010_boot_xfer, - list); -@@ -477,8 +470,6 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp - kfree(xfer); - } - --abort: -- release_firmware(fw); - return ret; - } - -@@ -486,14 +477,12 @@ static int wm0010_stage2_load(struct snd_soc_component *component) - { - struct spi_device *spi = to_spi_device(component->dev); - struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); -- const struct firmware *fw; - struct spi_message m; - struct spi_transfer t; -- u32 *img; -- u8 *out; - int i; - int ret = 0; - -+ const struct firmware *fw __free(firmware) = NULL; - ret = request_firmware(&fw, "wm0010_stage2.bin", component->dev); - if (ret != 0) { - dev_err(component->dev, "Failed to request stage2 loader: %d\n", -@@ -504,17 +493,15 @@ static int wm0010_stage2_load(struct snd_soc_component *component) - dev_dbg(component->dev, "Downloading %zu byte stage 2 loader\n", fw->size); - - /* Copy to local buffer first as vmalloc causes problems for dma */ -- img = kmemdup(&fw->data[0], fw->size, GFP_KERNEL | GFP_DMA); -- if (!img) { -- ret = -ENOMEM; -- goto abort2; -- } -+ u32 *img __free(kfree) = -+ kmemdup(&fw->data[0], fw->size, GFP_KERNEL | GFP_DMA); -+ if (!img) -+ return -ENOMEM; - -- out = kzalloc(fw->size, GFP_KERNEL | GFP_DMA); -- if (!out) { -- ret = -ENOMEM; -- goto abort1; -- } -+ u8 *out __free(kfree) = -+ kzalloc(fw->size, GFP_KERNEL | GFP_DMA); -+ if (!out) -+ return -ENOMEM; - - spi_message_init(&m); - memset(&t, 0, sizeof(t)); -@@ -531,7 +518,7 @@ static int wm0010_stage2_load(struct snd_soc_component *component) - ret = spi_sync(spi, &m); - if (ret != 0) { - dev_err(component->dev, "Initial download failed: %d\n", ret); -- goto abort; -+ return ret; - } - - /* Look for errors from the boot ROM */ -@@ -540,18 +527,11 @@ static int wm0010_stage2_load(struct snd_soc_component *component) - dev_err(component->dev, "Boot ROM error: %x in %d\n", - out[i], i); - wm0010_mark_boot_failure(wm0010); -- ret = -EBUSY; -- goto abort; -+ return -EBUSY; - } - } --abort: -- kfree(out); --abort1: -- kfree(img); --abort2: -- release_firmware(fw); - -- return ret; -+ return 0; - } - - static int wm0010_boot(struct snd_soc_component *component) -@@ -734,9 +714,8 @@ static int wm0010_set_bias_level(struct snd_soc_component *component, - break; - case SND_SOC_BIAS_STANDBY: - if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_PREPARE) { -- mutex_lock(&wm0010->lock); -- wm0010_halt(component); -- mutex_unlock(&wm0010->lock); -+ scoped_guard(mutex, &wm0010->lock) -+ wm0010_halt(component); - } - break; - case SND_SOC_BIAS_OFF: -@@ -832,9 +811,8 @@ static irqreturn_t wm0010_irq(int irq, void *data) - case WM0010_OUT_OF_RESET: - case WM0010_BOOTROM: - case WM0010_STAGE2: -- spin_lock(&wm0010->irq_lock); -- complete(&wm0010->boot_completion); -- spin_unlock(&wm0010->irq_lock); -+ scoped_guard(spinlock, &wm0010->irq_lock) -+ complete(&wm0010->boot_completion); - return IRQ_HANDLED; - default: - return IRQ_NONE; -diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c ---- a/sound/soc/codecs/wm2000.c -+++ b/sound/soc/codecs/wm2000.c -@@ -23,6 +23,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -612,20 +613,15 @@ static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); - unsigned int anc_active = ucontrol->value.integer.value[0]; -- int ret; - - if (anc_active > 1) - return -EINVAL; - -- mutex_lock(&wm2000->lock); -+ guard(mutex)(&wm2000->lock); - - wm2000->anc_active = anc_active; - -- ret = wm2000_anc_set_mode(wm2000); -- -- mutex_unlock(&wm2000->lock); -- -- return ret; -+ return wm2000_anc_set_mode(wm2000); - } - - static int wm2000_speaker_get(struct snd_kcontrol *kcontrol, -@@ -645,20 +641,15 @@ static int wm2000_speaker_put(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); - unsigned int val = ucontrol->value.integer.value[0]; -- int ret; - - if (val > 1) - return -EINVAL; - -- mutex_lock(&wm2000->lock); -+ guard(mutex)(&wm2000->lock); - - wm2000->spk_ena = val; - -- ret = wm2000_anc_set_mode(wm2000); -- -- mutex_unlock(&wm2000->lock); -- -- return ret; -+ return wm2000_anc_set_mode(wm2000); - } - - static const struct snd_kcontrol_new wm2000_controls[] = { -@@ -676,9 +667,8 @@ static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w, - { - struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); - struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); -- int ret; - -- mutex_lock(&wm2000->lock); -+ guard(mutex)(&wm2000->lock); - - if (SND_SOC_DAPM_EVENT_ON(event)) - wm2000->anc_eng_ena = 1; -@@ -686,11 +676,7 @@ static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w, - if (SND_SOC_DAPM_EVENT_OFF(event)) - wm2000->anc_eng_ena = 0; - -- ret = wm2000_anc_set_mode(wm2000); -- -- mutex_unlock(&wm2000->lock); -- -- return ret; -+ return wm2000_anc_set_mode(wm2000); - } - - static const struct snd_soc_dapm_widget wm2000_dapm_widgets[] = { -@@ -810,7 +796,7 @@ static int wm2000_i2c_probe(struct i2c_client *i2c) - struct wm2000_priv *wm2000; - struct wm2000_platform_data *pdata; - const char *filename; -- const struct firmware *fw = NULL; -+ const struct firmware *fw __free(firmware) = NULL; - int ret, i; - unsigned int reg; - u16 id; -@@ -828,7 +814,7 @@ static int wm2000_i2c_probe(struct i2c_client *i2c) - ret = PTR_ERR(wm2000->regmap); - dev_err(&i2c->dev, "Failed to allocate register map: %d\n", - ret); -- goto out; -+ return ret; - } - - for (i = 0; i < WM2000_NUM_SUPPLIES; i++) -@@ -922,9 +908,6 @@ static int wm2000_i2c_probe(struct i2c_client *i2c) - - err_supplies: - regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); -- --out: -- release_firmware(fw); - return ret; - } - -diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c ---- a/sound/soc/codecs/wm2200.c -+++ b/sound/soc/codecs/wm2200.c -@@ -2461,7 +2461,15 @@ static int wm2200_runtime_resume(struct device *dev) - } - - regcache_cache_only(wm2200->regmap, false); -- regcache_sync(wm2200->regmap); -+ ret = regcache_sync(wm2200->regmap); -+ if (ret) { -+ regcache_cache_only(wm2200->regmap, true); -+ regcache_mark_dirty(wm2200->regmap); -+ gpiod_set_value_cansleep(wm2200->ldo_ena, 0); -+ regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies), -+ wm2200->core_supplies); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c ---- a/sound/soc/codecs/wm5100.c -+++ b/sound/soc/codecs/wm5100.c -@@ -2659,7 +2659,15 @@ static int wm5100_runtime_resume(struct device *dev) - } - - regcache_cache_only(wm5100->regmap, false); -- regcache_sync(wm5100->regmap); -+ ret = regcache_sync(wm5100->regmap); -+ if (ret) { -+ regcache_cache_only(wm5100->regmap, true); -+ regcache_mark_dirty(wm5100->regmap); -+ gpiod_set_value_cansleep(wm5100->ldo_ena, 0); -+ regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies), -+ wm5100->core_supplies); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c ---- a/sound/soc/codecs/wm5102.c -+++ b/sound/soc/codecs/wm5102.c -@@ -7,6 +7,7 @@ - * Author: Mark Brown - */ - -+#include - #include - #include - #include -@@ -667,10 +668,9 @@ static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct arizona *arizona = dev_get_drvdata(component->dev->parent); - -- mutex_lock(&arizona->dac_comp_lock); -+ guard(mutex)(&arizona->dac_comp_lock); - put_unaligned_be16(arizona->dac_comp_coeff, - ucontrol->value.bytes.data); -- mutex_unlock(&arizona->dac_comp_lock); - - return 0; - } -@@ -681,16 +681,14 @@ static int wm5102_out_comp_coeff_put(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct arizona *arizona = dev_get_drvdata(component->dev->parent); - uint16_t dac_comp_coeff = get_unaligned_be16(ucontrol->value.bytes.data); -- int ret = 0; - -- mutex_lock(&arizona->dac_comp_lock); -+ guard(mutex)(&arizona->dac_comp_lock); - if (arizona->dac_comp_coeff != dac_comp_coeff) { - arizona->dac_comp_coeff = dac_comp_coeff; -- ret = 1; -+ return 1; - } -- mutex_unlock(&arizona->dac_comp_lock); - -- return ret; -+ return 0; - } - - static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol, -@@ -699,9 +697,8 @@ static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct arizona *arizona = dev_get_drvdata(component->dev->parent); - -- mutex_lock(&arizona->dac_comp_lock); -+ guard(mutex)(&arizona->dac_comp_lock); - ucontrol->value.integer.value[0] = arizona->dac_comp_enabled; -- mutex_unlock(&arizona->dac_comp_lock); - - return 0; - } -@@ -712,19 +709,17 @@ static int wm5102_out_comp_switch_put(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct arizona *arizona = dev_get_drvdata(component->dev->parent); - struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; -- int ret = 0; - - if (ucontrol->value.integer.value[0] > mc->max) - return -EINVAL; - -- mutex_lock(&arizona->dac_comp_lock); -+ guard(mutex)(&arizona->dac_comp_lock); - if (arizona->dac_comp_enabled != ucontrol->value.integer.value[0]) { - arizona->dac_comp_enabled = ucontrol->value.integer.value[0]; -- ret = 1; -+ return 1; - } -- mutex_unlock(&arizona->dac_comp_lock); - -- return ret; -+ return 0; - } - - static const char * const wm5102_osr_text[] = { -diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c ---- a/sound/soc/codecs/wm8731.c -+++ b/sound/soc/codecs/wm8731.c -@@ -10,6 +10,7 @@ - * Based on wm8753.c by Liam Girdwood - */ - -+#include - #include - #include - #include -@@ -110,22 +111,20 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component); - unsigned int deemph = ucontrol->value.integer.value[0]; -- int ret = 0; - - if (deemph > 1) - return -EINVAL; - -- mutex_lock(&wm8731->lock); -+ guard(mutex)(&wm8731->lock); - if (wm8731->deemph != deemph) { - wm8731->deemph = deemph; - - wm8731_set_deemph(component); - -- ret = 1; -+ return 1; - } -- mutex_unlock(&wm8731->lock); - -- return ret; -+ return 0; - } - - static const DECLARE_TLV_DB_SCALE(in_tlv, -3450, 150, 0); -diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c ---- a/sound/soc/codecs/wm8903.c -+++ b/sound/soc/codecs/wm8903.c -@@ -14,6 +14,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -458,22 +459,20 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component); - unsigned int deemph = ucontrol->value.integer.value[0]; -- int ret = 0; - - if (deemph > 1) - return -EINVAL; - -- mutex_lock(&wm8903->lock); -+ guard(mutex)(&wm8903->lock); - if (wm8903->deemph != deemph) { - wm8903->deemph = deemph; - - wm8903_set_deemph(component); - -- ret = 1; -+ return 1; - } -- mutex_unlock(&wm8903->lock); - -- return ret; -+ return 0; - } - - /* ALSA can only do steps of .01dB */ -diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c ---- a/sound/soc/codecs/wm8940.c -+++ b/sound/soc/codecs/wm8940.c -@@ -333,7 +333,7 @@ static const struct snd_soc_dapm_route wm8940_dapm_routes[] = { - {"ADC", NULL, "Boost Mixer"}, - }; - --#define wm8940_reset(c) snd_soc_component_write(c, WM8940_SOFTRESET, 0); -+#define wm8940_reset(c) snd_soc_component_write(c, WM8940_SOFTRESET, 0) - - static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai, - unsigned int fmt) -diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c ---- a/sound/soc/codecs/wm8958-dsp2.c -+++ b/sound/soc/codecs/wm8958-dsp2.c -@@ -7,6 +7,7 @@ - * Author: Mark Brown - */ - -+#include - #include - #include - #include -@@ -864,9 +865,8 @@ static void wm8958_enh_eq_loaded(const struct firmware *fw, void *context) - struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); - - if (fw && (wm8958_dsp2_fw(component, "ENH_EQ", fw, true) == 0)) { -- mutex_lock(&wm8994->fw_lock); -+ guard(mutex)(&wm8994->fw_lock); - wm8994->enh_eq = fw; -- mutex_unlock(&wm8994->fw_lock); - } - } - -@@ -876,9 +876,8 @@ static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context) - struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); - - if (fw && (wm8958_dsp2_fw(component, "MBC+VSS", fw, true) == 0)) { -- mutex_lock(&wm8994->fw_lock); -+ guard(mutex)(&wm8994->fw_lock); - wm8994->mbc_vss = fw; -- mutex_unlock(&wm8994->fw_lock); - } - } - -@@ -888,9 +887,8 @@ static void wm8958_mbc_loaded(const struct firmware *fw, void *context) - struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); - - if (fw && (wm8958_dsp2_fw(component, "MBC", fw, true) == 0)) { -- mutex_lock(&wm8994->fw_lock); -+ guard(mutex)(&wm8994->fw_lock); - wm8994->mbc = fw; -- mutex_unlock(&wm8994->fw_lock); - } - } - -diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c ---- a/sound/soc/codecs/wm8962.c -+++ b/sound/soc/codecs/wm8962.c -@@ -10,6 +10,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -1564,11 +1565,10 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); - int old = wm8962->dsp2_ena; -- int ret = 0; - int dsp2_running = snd_soc_component_read(component, WM8962_DSP2_POWER_MANAGEMENT) & - WM8962_DSP2_ENA; - -- mutex_lock(&wm8962->dsp2_ena_lock); -+ guard(mutex)(&wm8962->dsp2_ena_lock); - - if (ucontrol->value.integer.value[0]) - wm8962->dsp2_ena |= 1 << shift; -@@ -1576,9 +1576,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol, - wm8962->dsp2_ena &= ~(1 << shift); - - if (wm8962->dsp2_ena == old) -- goto out; -- -- ret = 1; -+ return 0; - - if (dsp2_running) { - if (wm8962->dsp2_ena) -@@ -1587,10 +1585,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol, - wm8962_dsp2_stop(component); - } - --out: -- mutex_unlock(&wm8962->dsp2_ena_lock); -- -- return ret; -+ return 1; - } - - /* The VU bits for the headphones are in a different register to the mute -@@ -3937,7 +3932,9 @@ static int wm8962_runtime_resume(struct device *dev) - WM8962_OSC_ENA | WM8962_PLL2_ENA | WM8962_PLL3_ENA, - 0); - -- regcache_sync(wm8962->regmap); -+ ret = regcache_sync(wm8962->regmap); -+ if (ret) -+ goto cache_sync_err; - - regmap_update_bits(wm8962->regmap, WM8962_ANTI_POP, - WM8962_STARTUP_BIAS_ENA | WM8962_VMID_BUF_ENA, -@@ -3955,6 +3952,11 @@ static int wm8962_runtime_resume(struct device *dev) - - return 0; - -+cache_sync_err: -+ regcache_cache_only(wm8962->regmap, true); -+ regcache_mark_dirty(wm8962->regmap); -+ regulator_bulk_disable(ARRAY_SIZE(wm8962->supplies), -+ wm8962->supplies); - disable_clock: - clk_disable_unprepare(wm8962->pdata.mclk); - return ret; -diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c ---- a/sound/soc/codecs/wm8994.c -+++ b/sound/soc/codecs/wm8994.c -@@ -7,6 +7,7 @@ - * Author: Mark Brown - */ - -+#include - #include - #include - #include -@@ -766,7 +767,7 @@ static void active_reference(struct snd_soc_component *component) - { - struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); - -- mutex_lock(&wm8994->accdet_lock); -+ guard(mutex)(&wm8994->accdet_lock); - - wm8994->active_refcount++; - -@@ -775,8 +776,6 @@ static void active_reference(struct snd_soc_component *component) - - /* If we're using jack detection go into audio mode */ - wm1811_jackdet_set_mode(component, WM1811_JACKDET_MODE_AUDIO); -- -- mutex_unlock(&wm8994->accdet_lock); - } - - static void active_dereference(struct snd_soc_component *component) -@@ -784,7 +783,7 @@ static void active_dereference(struct snd_soc_component *component) - struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); - u16 mode; - -- mutex_lock(&wm8994->accdet_lock); -+ guard(mutex)(&wm8994->accdet_lock); - - wm8994->active_refcount--; - -@@ -800,8 +799,6 @@ static void active_dereference(struct snd_soc_component *component) - - wm1811_jackdet_set_mode(component, mode); - } -- -- mutex_unlock(&wm8994->accdet_lock); - } - - static int clk_sys_event(struct snd_soc_dapm_widget *w, -@@ -3704,7 +3701,7 @@ static void wm8958_open_circuit_work(struct work_struct *work) - open_circuit_work.work); - struct device *dev = wm8994->wm8994->dev; - -- mutex_lock(&wm8994->accdet_lock); -+ guard(mutex)(&wm8994->accdet_lock); - - wm1811_micd_stop(wm8994->hubs.component); - -@@ -3718,8 +3715,6 @@ static void wm8958_open_circuit_work(struct work_struct *work) - snd_soc_jack_report(wm8994->micdet[0].jack, 0, - wm8994->btn_mask | - SND_JACK_HEADSET); -- -- mutex_unlock(&wm8994->accdet_lock); - } - - static void wm8958_mic_id(void *data, u16 status) -@@ -3777,7 +3772,7 @@ static void wm1811_mic_work(struct work_struct *work) - struct snd_soc_component *component = wm8994->hubs.component; - struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); - -- pm_runtime_get_sync(component->dev); -+ guard(pm_runtime_active)(component->dev); - - /* If required for an external cap force MICBIAS on */ - if (control->pdata.jd_ext_cap) { -@@ -3785,7 +3780,7 @@ static void wm1811_mic_work(struct work_struct *work) - snd_soc_dapm_sync(dapm); - } - -- mutex_lock(&wm8994->accdet_lock); -+ guard(mutex)(&wm8994->accdet_lock); - - dev_dbg(component->dev, "Starting mic detection\n"); - -@@ -3803,10 +3798,6 @@ static void wm1811_mic_work(struct work_struct *work) - snd_soc_component_update_bits(component, WM8958_MIC_DETECT_1, - WM8958_MICD_ENA, WM8958_MICD_ENA); - } -- -- mutex_unlock(&wm8994->accdet_lock); -- -- pm_runtime_put(component->dev); - } - - static irqreturn_t wm1811_jackdet_irq(int irq, void *data) -@@ -4026,15 +4017,10 @@ static void wm8958_mic_work(struct work_struct *work) - mic_complete_work.work); - struct snd_soc_component *component = wm8994->hubs.component; - -- pm_runtime_get_sync(component->dev); -- -- mutex_lock(&wm8994->accdet_lock); -+ guard(pm_runtime_active)(component->dev); -+ guard(mutex)(&wm8994->accdet_lock); - - wm8994->mic_id_cb(wm8994->mic_id_cb_data, wm8994->mic_status); -- -- mutex_unlock(&wm8994->accdet_lock); -- -- pm_runtime_put(component->dev); - } - - static irqreturn_t wm8958_mic_irq(int irq, void *data) -diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c ---- a/sound/soc/codecs/wm9712.c -+++ b/sound/soc/codecs/wm9712.c -@@ -6,6 +6,7 @@ - * Author: Liam Girdwood - */ - -+#include - #include - #include - #include -@@ -229,7 +230,7 @@ static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol, - shift = mc->shift & 0xff; - mask = 1 << shift; - -- mutex_lock(&wm9712->lock); -+ guard(mutex)(&wm9712->lock); - old = wm9712->hp_mixer[mixer]; - if (ucontrol->value.integer.value[0]) - wm9712->hp_mixer[mixer] |= mask; -@@ -251,8 +252,6 @@ static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol, - &update); - } - -- mutex_unlock(&wm9712->lock); -- - return change; - } - -diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c ---- a/sound/soc/codecs/wm9713.c -+++ b/sound/soc/codecs/wm9713.c -@@ -11,6 +11,7 @@ - * o Support for DAPM - */ - -+#include - #include - #include - #include -@@ -238,7 +239,7 @@ static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol, - shift = mc->shift & 0xff; - mask = (1 << shift); - -- mutex_lock(&wm9713->lock); -+ guard(mutex)(&wm9713->lock); - old = wm9713->hp_mixer[mixer]; - if (ucontrol->value.integer.value[0]) - wm9713->hp_mixer[mixer] |= mask; -@@ -260,8 +261,6 @@ static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol, - &update); - } - -- mutex_unlock(&wm9713->lock); -- - return change; - } - -diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c ---- a/sound/soc/codecs/wm_adsp.c -+++ b/sound/soc/codecs/wm_adsp.c -@@ -348,7 +348,6 @@ int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); -- int ret = 1; - - if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw) - return 0; -@@ -356,16 +355,14 @@ int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, - if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW) - return -EINVAL; - -- mutex_lock(&dsp[e->shift_l].cs_dsp.pwr_lock); -+ guard(mutex)(&dsp[e->shift_l].cs_dsp.pwr_lock); - - if (dsp[e->shift_l].cs_dsp.booted || !list_empty(&dsp[e->shift_l].compr_list)) -- ret = -EBUSY; -+ return -EBUSY; - else - dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; - -- mutex_unlock(&dsp[e->shift_l].cs_dsp.pwr_lock); -- -- return ret; -+ return 1; - } - EXPORT_SYMBOL_GPL(wm_adsp_fw_put); - -@@ -450,15 +447,13 @@ static int wm_coeff_put_acked(struct snd_kcontrol *kctl, - if (val == 0) - return 0; /* 0 means no event */ - -- mutex_lock(&cs_ctl->dsp->pwr_lock); -+ guard(mutex)(&cs_ctl->dsp->pwr_lock); - - if (cs_ctl->enabled) - ret = cs_dsp_coeff_write_acked_control(cs_ctl, val); - else - ret = -EPERM; - -- mutex_unlock(&cs_ctl->dsp->pwr_lock); -- - if (ret < 0) - return ret; - -@@ -486,15 +481,13 @@ static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, - struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; - int ret = 0; - -- mutex_lock(&cs_ctl->dsp->pwr_lock); -+ guard(mutex)(&cs_ctl->dsp->pwr_lock); - - ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, cs_ctl->cache, size); - - if (!ret && copy_to_user(bytes, cs_ctl->cache, size)) - ret = -EFAULT; - -- mutex_unlock(&cs_ctl->dsp->pwr_lock); -- - return ret; - } - -@@ -694,10 +687,9 @@ int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, - struct cs_dsp_coeff_ctl *cs_ctl; - int ret; - -- mutex_lock(&dsp->cs_dsp.pwr_lock); -+ guard(mutex)(&dsp->cs_dsp.pwr_lock); - cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg); - ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len); -- mutex_unlock(&dsp->cs_dsp.pwr_lock); - - if (ret < 0) - return ret; -@@ -709,14 +701,10 @@ EXPORT_SYMBOL_GPL(wm_adsp_write_ctl); - int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type, - unsigned int alg, void *buf, size_t len) - { -- int ret; -+ guard(mutex)(&dsp->cs_dsp.pwr_lock); - -- mutex_lock(&dsp->cs_dsp.pwr_lock); -- ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg), -+ return cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg), - 0, buf, len); -- mutex_unlock(&dsp->cs_dsp.pwr_lock); -- -- return ret; - } - EXPORT_SYMBOL_GPL(wm_adsp_read_ctl); - -@@ -1270,38 +1258,32 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) - { - struct wm_adsp_compr *compr, *tmp; - struct snd_soc_pcm_runtime *rtd = stream->private_data; -- int ret = 0; - -- mutex_lock(&dsp->cs_dsp.pwr_lock); -+ guard(mutex)(&dsp->cs_dsp.pwr_lock); - - if (wm_adsp_fw[dsp->fw].num_caps == 0) { - adsp_err(dsp, "%s: Firmware does not support compressed API\n", - snd_soc_rtd_to_codec(rtd, 0)->name); -- ret = -ENXIO; -- goto out; -+ return -ENXIO; - } - - if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { - adsp_err(dsp, "%s: Firmware does not support stream direction\n", - snd_soc_rtd_to_codec(rtd, 0)->name); -- ret = -EINVAL; -- goto out; -+ return -EINVAL; - } - - list_for_each_entry(tmp, &dsp->compr_list, list) { - if (!strcmp(tmp->name, snd_soc_rtd_to_codec(rtd, 0)->name)) { - adsp_err(dsp, "%s: Only a single stream supported per dai\n", - snd_soc_rtd_to_codec(rtd, 0)->name); -- ret = -EBUSY; -- goto out; -+ return -EBUSY; - } - } - - compr = kzalloc_obj(*compr); -- if (!compr) { -- ret = -ENOMEM; -- goto out; -- } -+ if (!compr) -+ return -ENOMEM; - - compr->dsp = dsp; - compr->stream = stream; -@@ -1311,10 +1293,7 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) - - stream->runtime->private_data = compr; - --out: -- mutex_unlock(&dsp->cs_dsp.pwr_lock); -- -- return ret; -+ return 0; - } - EXPORT_SYMBOL_GPL(wm_adsp_compr_open); - -@@ -1324,7 +1303,7 @@ int wm_adsp_compr_free(struct snd_soc_component *component, - struct wm_adsp_compr *compr = stream->runtime->private_data; - struct wm_adsp *dsp = compr->dsp; - -- mutex_lock(&dsp->cs_dsp.pwr_lock); -+ guard(mutex)(&dsp->cs_dsp.pwr_lock); - - wm_adsp_compr_detach(compr); - list_del(&compr->list); -@@ -1332,8 +1311,6 @@ int wm_adsp_compr_free(struct snd_soc_component *component, - kfree(compr->raw_buf); - kfree(compr); - -- mutex_unlock(&dsp->cs_dsp.pwr_lock); -- - return 0; - } - EXPORT_SYMBOL_GPL(wm_adsp_compr_free); -@@ -1741,7 +1718,7 @@ int wm_adsp_compr_trigger(struct snd_soc_component *component, - - compr_dbg(compr, "Trigger: %d\n", cmd); - -- mutex_lock(&dsp->cs_dsp.pwr_lock); -+ guard(mutex)(&dsp->cs_dsp.pwr_lock); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: -@@ -1777,8 +1754,6 @@ int wm_adsp_compr_trigger(struct snd_soc_component *component, - break; - } - -- mutex_unlock(&dsp->cs_dsp.pwr_lock); -- - return ret; - } - EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger); -@@ -1907,21 +1882,20 @@ int wm_adsp_compr_pointer(struct snd_soc_component *component, - - compr_dbg(compr, "Pointer request\n"); - -- mutex_lock(&dsp->cs_dsp.pwr_lock); -+ guard(mutex)(&dsp->cs_dsp.pwr_lock); - - buf = compr->buf; - - if (dsp->fatal_error || !buf || buf->error) { - snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN); -- ret = -EIO; -- goto out; -+ return -EIO; - } - - if (buf->avail < wm_adsp_compr_frag_words(compr)) { - ret = wm_adsp_buffer_update_avail(buf); - if (ret < 0) { - compr_err(compr, "Error reading avail: %d\n", ret); -- goto out; -+ return ret; - } - - /* -@@ -1934,14 +1908,14 @@ int wm_adsp_compr_pointer(struct snd_soc_component *component, - if (buf->error) - snd_compr_stop_error(stream, - SNDRV_PCM_STATE_XRUN); -- goto out; -+ return ret; - } - - ret = wm_adsp_buffer_reenable_irq(buf); - if (ret < 0) { - compr_err(compr, "Failed to re-enable buffer IRQ: %d\n", - ret); -- goto out; -+ return ret; - } - } - } -@@ -1950,9 +1924,6 @@ int wm_adsp_compr_pointer(struct snd_soc_component *component, - tstamp->copied_total += buf->avail * CS_DSP_DATA_WORD_SIZE; - tstamp->sampling_rate = compr->sample_rate; - --out: -- mutex_unlock(&dsp->cs_dsp.pwr_lock); -- - return ret; - } - EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer); -@@ -2063,15 +2034,13 @@ int wm_adsp_compr_copy(struct snd_soc_component *component, - struct wm_adsp *dsp = compr->dsp; - int ret; - -- mutex_lock(&dsp->cs_dsp.pwr_lock); -+ guard(mutex)(&dsp->cs_dsp.pwr_lock); - - if (stream->direction == SND_COMPRESS_CAPTURE) - ret = wm_adsp_compr_read(compr, buf, count); - else - ret = -ENOTSUPP; - -- mutex_unlock(&dsp->cs_dsp.pwr_lock); -- - return ret; - } - EXPORT_SYMBOL_GPL(wm_adsp_compr_copy); -diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c ---- a/sound/soc/codecs/wsa881x.c -+++ b/sound/soc/codecs/wsa881x.c -@@ -1178,7 +1178,13 @@ static int wsa881x_runtime_resume(struct device *dev) - } - - regcache_cache_only(regmap, false); -- regcache_sync(regmap); -+ ret = regcache_sync(regmap); -+ if (ret) { -+ regcache_cache_only(regmap, true); -+ regcache_mark_dirty(regmap); -+ gpiod_direction_output(wsa881x->sd_n, 1); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c ---- a/sound/soc/codecs/wsa883x.c -+++ b/sound/soc/codecs/wsa883x.c -@@ -4,6 +4,7 @@ - */ - - #include -+#include - #include - #include - #include -@@ -1237,9 +1238,8 @@ static int wsa883x_spkr_event(struct snd_soc_dapm_widget *w, - - switch (event) { - case SND_SOC_DAPM_POST_PMU: -- mutex_lock(&wsa883x->sp_lock); -- wsa883x->pa_on = true; -- mutex_unlock(&wsa883x->sp_lock); -+ scoped_guard(mutex, &wsa883x->sp_lock) -+ wsa883x->pa_on = true; - - switch (wsa883x->dev_mode) { - case RECEIVER: -@@ -1290,9 +1290,8 @@ static int wsa883x_spkr_event(struct snd_soc_dapm_widget *w, - WSA883X_GLOBAL_PA_EN_MASK, 0); - snd_soc_component_write_field(component, WSA883X_PDM_WD_CTL, - WSA883X_PDM_EN_MASK, 0); -- mutex_lock(&wsa883x->sp_lock); -- wsa883x->pa_on = false; -- mutex_unlock(&wsa883x->sp_lock); -+ scoped_guard(mutex, &wsa883x->sp_lock) -+ wsa883x->pa_on = false; - break; - } - return 0; -@@ -1696,9 +1695,15 @@ static int wsa883x_runtime_suspend(struct device *dev) - static int wsa883x_runtime_resume(struct device *dev) - { - struct regmap *regmap = dev_get_regmap(dev, NULL); -+ int ret; - - regcache_cache_only(regmap, false); -- regcache_sync(regmap); -+ ret = regcache_sync(regmap); -+ if (ret) { -+ regcache_cache_only(regmap, true); -+ regcache_mark_dirty(regmap); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c ---- a/sound/soc/codecs/wsa884x.c -+++ b/sound/soc/codecs/wsa884x.c -@@ -1701,9 +1701,8 @@ static int wsa884x_spkr_event(struct snd_soc_dapm_widget *w, - - switch (event) { - case SND_SOC_DAPM_POST_PMU: -- mutex_lock(&wsa884x->sp_lock); -- wsa884x->pa_on = true; -- mutex_unlock(&wsa884x->sp_lock); -+ scoped_guard(mutex, &wsa884x->sp_lock) -+ wsa884x->pa_on = true; - - wsa884x_spkr_post_pmu(component, wsa884x); - -@@ -1717,9 +1716,8 @@ static int wsa884x_spkr_event(struct snd_soc_dapm_widget *w, - WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK, - 0x0); - -- mutex_lock(&wsa884x->sp_lock); -- wsa884x->pa_on = false; -- mutex_unlock(&wsa884x->sp_lock); -+ scoped_guard(mutex, &wsa884x->sp_lock) -+ wsa884x->pa_on = false; - break; - } - -@@ -2147,9 +2145,15 @@ static int wsa884x_runtime_suspend(struct device *dev) - static int wsa884x_runtime_resume(struct device *dev) - { - struct regmap *regmap = dev_get_regmap(dev, NULL); -+ int ret; - - regcache_cache_only(regmap, false); -- regcache_sync(regmap); -+ ret = regcache_sync(regmap); -+ if (ret) { -+ regcache_cache_only(regmap, true); -+ regcache_mark_dirty(regmap); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/wsa885x.c b/sound/soc/codecs/wsa885x.c -new file mode 100644 ---- /dev/null -+++ b/sound/soc/codecs/wsa885x.c -@@ -0,0 +1,1533 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. -+ */ -+ -+/* WSA885X codec driver */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* Control Registers - Audio Processing */ -+#define WSA885X_SMP_AMP_CTRL_STEREO_STEREO_SMP_AMP_CTRL_I2S 0x0000 -+#define WSA885X_SMP_AMP_CTRL_STEREO_CMT_GRP_MASK 0x0004 -+#define WSA885X_SMP_AMP_CTRL_STEREO_IT21_CLUSERINDEX 0x0140 -+#define WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID 0x0208 -+#define WSA885X_SMP_AMP_CTRL_STEREO_CS21_SAMPLERATEINDEX 0x0240 -+#define WSA885X_SMP_AMP_CTRL_STEREO_PPU21_POSTURENUMBER 0x0340 -+#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0 0x4405 -+#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1 0x4406 -+#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB 0x4409 -+#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_MSB 0x6409 -+#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB 0x440a -+#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_MSB 0x640a -+#define WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS 0x0a04 -+#define WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS 0x0a40 -+#define WSA885X_SMP_AMP_CTRL_STEREO_OT23_USAGE 0x0b10 -+#define WSA885X_SMP_AMP_CTRL_STEREO_CS24_SAMPLERATEINDEX 0x0e40 -+ -+/* Analog Top Registers - Power and Clock Control */ -+#define WSA885X_ANA_TOP_PON_CKSK_CTL_0 0x800d -+#define WSA885X_ANA_TOP_BG_TVP_UVLO1_PROG 0x8024 -+#define WSA885X_ANA_TOP_BG_TVP_UVLO2_PROG 0x8025 -+#define WSA885X_ANA_TOP_BG_TVP_OVRD_CTL 0x8034 -+ -+/* Analog PLL Registers */ -+#define WSA885X_ANA_PLL_DIV_CTL_0 0x8090 -+#define WSA885X_ANA_PLL_DIV_CTL_1 0x8091 -+#define WSA885X_ANA_TOP_PLL_VCO_CTL 0x8092 -+#define WSA885X_ANA_TOP_PLL_LOOPFILT_0 0x8093 -+#define WSA885X_ANA_TOP_PLL_OVRD_CTL 0x8098 -+#define WSA885X_ANA_TOP_PLL_STATUS_0 0x809a -+#define WSA885X_ANA_TOP_PLL_STATUS_1 0x809b -+ -+/* Analog Boost Control Registers */ -+#define WSA885X_ANA_TOP_BOOST_STB_CTRL2 0x805b -+#define WSA885X_ANA_TOP_BOOST_STB_CTRL3 0x805c -+#define WSA885X_ANA_TOP_BOOST_BYP_CTRL2 0x805e -+#define WSA885X_ANA_TOP_BOOST_BYP_CTRL3 0x805f -+#define WSA885X_ANA_TOP_BOOST_MISC 0x8063 -+#define WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL2 0x8065 -+#define WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL4 0x8067 -+ -+/* Analog IV Sense ADC Registers */ -+#define WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL2 0x80ca -+#define WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3 0x80cb -+#define WSA885X_ANA_TOP_IVSENSE_ADC_REF_CTL 0x80cc -+#define WSA885X_ANA_TOP_IVSENSE_ADC_CDAC_CAL_CTL2 0x80d0 -+ -+/* Analog Speaker Power Stage Registers */ -+#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_CTRL3 0x8108 -+#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_TUNE3 0x810b -+#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_CTRL3 0x810e -+#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_TUNE3 0x8111 -+#define WSA885X_ANA_TOP_SPK_TOP_SPARE3 0x813c -+#define WSA885X_SPK_TOP_LF_CH1_CTRL11 0x811c -+#define WSA885X_SPK_TOP_LF_CH1_TUNE1 0x811d -+#define WSA885X_SPK_TOP_LF_CH2_TUNE1 0x8129 -+#define WSA885X_SPK_TOP_LF_CH1_CTRL9 0x811a -+#define WSA885X_SPK_TOP_LF_CH2_CTRL9 0x8126 -+#define WSA885X_SPK_TOP_LF_CH2_CTRL11 0x8128 -+#define WSA885X_SPK_TOP_COMMON_CTRL2 0x8102 -+#define WSA885X_SPK_TOP_COMMON_TUNE1 0x8103 -+#define WSA885X_IVSENSE_VSNS_ISNS_CTL_CH1 0x80ba -+#define WSA885X_DIG_CTRL0_TOP_CLK_CFG 0x8418 -+#define WSA885X_DIG_CTRL0_SDCA_COMMIT 0x8419 -+#define WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE 0x841a -+#define WSA885X_DIG_CTRL0_SYS_CLK_SEL 0x841b -+#define WSA885X_DIG_CTRL0_CDC_CLK_CTL 0x841c -+#define WSA885X_DIG_CTRL0_PA_FSM_CTL 0x8420 -+#define WSA885X_DIG_CTRL0_POWER_FSM_CTL0 0x8423 -+#define WSA885X_DIG_CTRL0_POWER_FSM_CTL1 0x8424 -+#define WSA885X_DIG_CTRL0_PA0_FSM_CTL1 0x842b -+#define WSA885X_DIG_CTRL0_PA1_FSM_CTL1 0x8435 -+#define WSA885X_DIG_CTRL0_VBAT_THRM_FLT_CTL 0x8458 -+#define WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL 0x8470 -+#define WSA885X_DIG_CTRL0_GAIN_RAMP0_CTL1 0x84b4 -+#define WSA885X_DIG_CTRL0_GAIN_RAMP1_CTL1 0x84b7 -+#define WSA885X_DIG_CTRL0_PCM_DATA_WD0_CTL1 0x84A0 -+#define WSA885X_DIG_CTRL0_PCM_DATA_WD1_CTL1 0x84A4 -+ -+/* Digital Control 1 Registers - I2S/TDM Interface */ -+#define WSA885X_DIG_CTRL1_I2S_CTL0 0x85A0 -+#define WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX 0x85A2 -+#define WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX 0x85A3 -+#define WSA885X_DIG_CTRL1_I2S_TDM_CTL0 0x85A7 -+#define WSA885X_DIG_CTRL1_I2S_TDM_CTL1 0x85A9 -+#define WSA885X_DIG_CTRL1_I2S_TDM_CH_RX 0x85AA -+#define WSA885X_DIG_CTRL1_I2S_TDM_CH_TX 0x85AB -+#define WSA885X_DIG_CTRL1_I2S_RESET_CTL 0x85AE -+ -+/* CDC RX Path Registers - Audio Data Path */ -+#define WSA885X_CDC_RX0_RX_PATH_CFG0 0x8601 -+#define WSA885X_CDC_RX0_RX_PATH_CFG1 0x8602 -+#define WSA885X_CDC_RX0_RX_PATH_CTL 0x8606 -+#define WSA885X_RX0_RX_PATH_DSMDEM_CTL 0x8613 -+#define WSA885X_CDC_RX1_RX_PATH_CFG0 0x8621 -+#define WSA885X_CDC_RX1_RX_PATH_CFG1 0x8622 -+#define WSA885X_CDC_RX1_RX_PATH_CTL 0x8626 -+#define WSA885X_RX1_RX_PATH_DSMDEM_CTL 0x8633 -+ -+/* CDC Compander Registers - Dynamic Range Control */ -+#define WSA885X_CDC_COMPANDER0_CTL0 0x8640 -+#define WSA885X_CDC_COMPANDER0_CTL7 0x8647 -+#define WSA885X_CDC_COMPANDER1_CTL0 0x8660 -+#define WSA885X_CDC_COMPANDER1_CTL7 0x8667 -+ -+/* CDC Speaker Protection Registers - IV Sense */ -+#define WSA885X_CDC_VSENSE0_SPKR_PROT_PATH_CTL 0x86A1 -+#define WSA885X_CDC_VSENSE1_SPKR_PROT_PATH_CTL 0x86B1 -+#define WSA885X_CDC_ISENSE0_SPKR_PROT_PATH_CTL 0x86A9 -+#define WSA885X_CDC_ISENSE1_SPKR_PROT_PATH_CTL 0x86B9 -+ -+/* CDC Class-H Registers - Headroom Control */ -+#define WSA885X_CDC_CLSH_V1P8_BP_CTL1 0x86CD -+#define WSA885X_CDC_CLSH_V1P8_BP_CTL0 0x86CC -+#define WSA885X_CDC_CLSH_CLSH_SIG_DP_CTL0 0x86C7 -+#define WSA885X_CDC_CLSH_CLSH_V_HD_PA 0x86C3 -+#define WSA885X_CDC_CLSH_V1P8_BP_CTL2 0x86CE -+ -+/* Driver Constants */ -+#define WSA885X_CLK_RATE_FIXED 73728000 -+#define WSA885X_NUM_REGS 0x03 -+ -+/* Interrupt Registers */ -+#define WSA885X_INTR_STATUS0 0x8584 -+#define WSA885X_INTR_MASK0 0x8581 -+#define WSA885X_INTR_CLEAR0 0x8587 -+ -+/* Power and PA FSM Control Registers */ -+#define WSA885X_PA0_FSM_CTL0 0x842A -+#define WSA885X_PA1_FSM_CTL0 0x8434 -+ -+/* Digital Control GPIO and Interrupt Registers */ -+#define WSA885X_DIG_CTRL1_PIN_CT 0x8510 -+#define WSA885X_DIG_CTRL1_SPMI_PAD_GPIO2_CTL 0x8518 -+#define WSA885X_DIG_CTRL1_INTR_MODE 0x8580 -+ -+#define WSA885X_I2S_CTL0_PCM_RATE_MASK GENMASK(4, 1) -+#define WSA885X_I2S_CTL0_ENABLE_MASK BIT(0) -+#define WSA885X_I2S_CTL0_PCM_RATE(v) \ -+ FIELD_PREP(WSA885X_I2S_CTL0_PCM_RATE_MASK, (v)) -+#define WSA885X_I2S_CTL0_PCM_RATE_8KHZ 0x0 -+#define WSA885X_I2S_CTL0_PCM_RATE_16KHZ 0x1 -+#define WSA885X_I2S_CTL0_PCM_RATE_32KHZ 0x2 -+#define WSA885X_I2S_CTL0_PCM_RATE_48_OR_44KHZ 0x3 -+#define WSA885X_I2S_CTL0_PCM_RATE_96_OR_88KHZ 0x4 -+#define WSA885X_I2S_CTL0_PCM_RATE_192_OR_176KHZ 0x5 -+#define WSA885X_I2S_CTL0_PCM_RATE_384_OR_352KHZ 0x6 -+#define WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK GENMASK(2, 0) -+#define WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK GENMASK(6, 4) -+#define WSA885X_I2S_CFG0_TDM_TX_SLOT0(v) \ -+ FIELD_PREP_CONST(WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK, (v)) -+#define WSA885X_I2S_CFG0_TDM_TX_SLOT1(v) \ -+ FIELD_PREP_CONST(WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK, (v)) -+#define WSA885X_I2S_CFG1_TDM_TX_SLOT2_MASK GENMASK(2, 0) -+#define WSA885X_I2S_CFG1_TDM_TX_SLOT3_MASK GENMASK(6, 4) -+#define WSA885X_I2S_CFG1_TDM_TX_SLOT2(v) \ -+ FIELD_PREP_CONST(WSA885X_I2S_CFG1_TDM_TX_SLOT2_MASK, (v)) -+#define WSA885X_I2S_CFG1_TDM_TX_SLOT3(v) \ -+ FIELD_PREP_CONST(WSA885X_I2S_CFG1_TDM_TX_SLOT3_MASK, (v)) -+#define WSA885X_I2S_TDM_CTL0_I2S_TDM_EN_MASK BIT(0) -+#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK GENMASK(3, 2) -+#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_2 \ -+ FIELD_PREP_CONST(WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, 0) -+#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_4 \ -+ FIELD_PREP_CONST(WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, 1) -+#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_8 \ -+ FIELD_PREP_CONST(WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, 3) -+#define WSA885X_I2S_TDM_CH_TX_CH0_EN BIT(0) -+#define WSA885X_I2S_TDM_CH_TX_CH1_EN BIT(1) -+#define WSA885X_I2S_TDM_CH_TX_CH2_EN BIT(2) -+#define WSA885X_I2S_TDM_CH_TX_CH3_EN BIT(3) -+#define WSA885X_I2S_TDM_CH_RX_CH0_EN BIT(0) -+#define WSA885X_I2S_TDM_CH_RX_CH3_EN BIT(3) -+#define WSA885X_I2S_RESET_CTL_RESET_MASK BIT(0) -+#define WSA885X_PCM_DATA_WD_CTL1_PCM_DATA_WD_EN_MASK BIT(2) -+#define WSA885X_POWER_FSM_CTL0_CLEAR_ERROR_MASK BIT(3) -+#define WSA885X_PA_FSM_CTL0_CLEAR_ERROR_MASK BIT(2) -+ -+#define WSA885X_I2S_TX_SLOT_ISENSE0 0x1 -+#define WSA885X_I2S_TX_SLOT_ISENSE1 0x2 -+#define WSA885X_I2S_TX_SLOT_CUR_SENSE0 0x5 -+#define WSA885X_I2S_TX_SLOT_CUR_SENSE1 0x6 -+ -+/* RX Sample Rate Index Values - Audio Playback Path */ -+#define WSA885X_RX_RATE_8000HZ 0x00 -+#define WSA885X_RX_RATE_16000HZ 0x01 -+#define WSA885X_RX_RATE_32000HZ 0x02 -+#define WSA885X_RX_RATE_44100HZ 0x03 -+#define WSA885X_RX_RATE_48000HZ 0x04 -+#define WSA885X_RX_RATE_96000HZ 0x05 -+#define WSA885X_RX_RATE_192000HZ 0x06 -+#define WSA885X_RX_RATE_384000HZ 0x07 -+ -+/* VI Sample Rate Index Values - Voltage/Current Sensing Path */ -+#define WSA885X_VI_RATE_8000HZ 0x00 -+#define WSA885X_VI_RATE_16000HZ 0x01 -+#define WSA885X_VI_RATE_44100HZ 0x02 -+#define WSA885X_VI_RATE_48000HZ 0x03 -+#define WSA885X_VI_RATE_96000HZ 0x04 -+#define WSA885X_VI_RATE_22050HZ 0x05 -+#define WSA885X_VI_RATE_24000HZ 0x06 -+#define WSA885X_VI_RATE_192000HZ 0x07 -+#define WSA885X_VI_RATE_384000HZ 0x08 -+ -+/* Channel Configuration Masks */ -+#define WSA885X_CHANNEL_STEREO 0x03 -+#define WSA885X_CHANNEL_MONO_LEFT 0x01 -+#define WSA885X_CHANNEL_MONO_RIGHT 0x02 -+ -+#define WSA885X_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \ -+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | \ -+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) -+ -+#define WSA885X_PLL_LOCK_BIT BIT(0) -+ -+#define WSA885X_FU21_VOL_STEPS 124 -+#define WSA885X_USAGE_MODE_MAX 8 -+static const DECLARE_TLV_DB_SCALE(wsa885x_fu21_digital_gain, -8400, 100, 0); -+ -+static bool wsa885x_is_valid_rx_slot_mask(u32 mask) -+{ -+ return mask == WSA885X_CHANNEL_MONO_LEFT || -+ mask == WSA885X_CHANNEL_MONO_RIGHT || -+ mask == WSA885X_CHANNEL_STEREO; -+} -+ -+static const char *const wsa885x_supply_name[] = { -+ "vdd-1p8", -+ "vdd-io", -+}; -+ -+enum { -+ WSA885X_BATT_1S = 1, -+ WSA885X_BATT_2S, -+}; -+ -+enum { -+ WSA885X_IRQ_INT_SAF2WAR = 0, -+ WSA885X_IRQ_INT_WAR2SAF, -+ WSA885X_IRQ_INT_DISABLE, -+ WSA885X_IRQ_INT_PA0_OCP, -+ WSA885X_IRQ_INT_PA1_OCP, -+ WSA885X_IRQ_INT_CLIP0, -+ WSA885X_IRQ_INT_CLIP1, -+ WSA885X_IRQ_INT_CLK_WD, -+ WSA885X_IRQ_INT_INTR_GPIO1_PIN, -+ WSA885X_IRQ_INT_INTR_GPIO2_PIN, -+ WSA885X_IRQ_INT_UVLO, -+ WSA885X_IRQ_INT_BOP, -+ WSA885X_IRQ_INT_PA0_FSM_ERR, -+ WSA885X_IRQ_INT_PA1_FSM_ERR, -+ WSA885X_IRQ_INT_MAIN_FSM_ERR, -+ WSA885X_IRQ_INT_PCM_DATA0_WD, -+ WSA885X_IRQ_INT_PCM_DATA1_WD, -+ WSA885X_IRQ_INT_PCM_DATA0_DC, -+ WSA885X_IRQ_INT_PCM_DATA1_DC, -+ WSA885X_IRQ_INT_PLL_UNLOCKED, -+ WSA885X_IRQ_INT_PROT_MODE_CHANGE, -+ WSA885X_IRQ_INT_PB_CLOCK_VALID, -+ WSA885X_IRQ_INT_SENSE_CLOCK_VALID, -+ WSA885X_IRQ_MAX, -+}; -+ -+struct wsa885x_priv { -+ struct i2c_client *client; -+ struct regmap *regmap; -+ struct device *dev; -+ struct snd_soc_component *component; -+ struct gpio_desc *sd_n; -+ struct reset_control *sd_reset; -+ u32 usage_mode; -+ u32 rx_slot_mask; -+ u32 batt_conf; -+ int stereo_vol_db; -+ struct mutex state_lock; /* protects mutable control state */ -+}; -+ -+struct wsa885x_reg_update { -+ unsigned int reg; -+ unsigned int mask; -+ unsigned int val; -+}; -+ -+static const struct regmap_range_cfg wsa885x_regmap_ranges[] = { -+ { -+ .range_min = 0, -+ .range_max = 0x88ff, -+ .selector_reg = 0x0, -+ .selector_mask = 0xFF, -+ .selector_shift = 0, -+ .window_start = 0, -+ .window_len = 0x100, -+ }, -+}; -+ -+static const struct reg_default wsa885x_codec_reg_defaults[] = { -+ {WSA885X_SMP_AMP_CTRL_STEREO_STEREO_SMP_AMP_CTRL_I2S, 0x00}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_IT21_CLUSERINDEX, 0x01}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_CMT_GRP_MASK, 0x00}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_OT23_USAGE, 0x00}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID, 0x00}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_CS21_SAMPLERATEINDEX, 0x04}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_PPU21_POSTURENUMBER, 0x01}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0, 0x01}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1, 0x01}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_MSB, 0xac}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB, 0x00}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_MSB, 0xac}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB, 0x00}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS, 0x03}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS, 0x03}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_CS24_SAMPLERATEINDEX, 0x03}, -+ {WSA885X_ANA_TOP_PON_CKSK_CTL_0, 0x00}, -+ {WSA885X_ANA_TOP_BG_TVP_UVLO1_PROG, 0x19}, -+ {WSA885X_ANA_TOP_BG_TVP_UVLO2_PROG, 0x22}, -+ {WSA885X_ANA_PLL_DIV_CTL_0, 0x0c}, -+ {WSA885X_ANA_PLL_DIV_CTL_1, 0x50}, -+ {WSA885X_ANA_TOP_PLL_VCO_CTL, 0x00}, -+ {WSA885X_ANA_TOP_PLL_LOOPFILT_0, 0xb4}, -+ {WSA885X_ANA_TOP_PLL_OVRD_CTL, 0x00}, -+ {WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x00}, -+ {WSA885X_ANA_TOP_BOOST_STB_CTRL2, 0x03}, -+ {WSA885X_ANA_TOP_BOOST_STB_CTRL3, 0x3c}, -+ {WSA885X_ANA_TOP_BOOST_BYP_CTRL2, 0xc5}, -+ {WSA885X_ANA_TOP_BOOST_BYP_CTRL3, 0x13}, -+ {WSA885X_ANA_TOP_BOOST_MISC, 0x79}, -+ {WSA885X_ANA_TOP_SPK_TOP_SPARE3, 0x00}, -+ {WSA885X_SPK_TOP_COMMON_CTRL2, 0x08}, -+ {WSA885X_SPK_TOP_LF_CH1_CTRL11, 0x09}, -+ {WSA885X_SPK_TOP_LF_CH1_TUNE1, 0x00}, -+ {WSA885X_SPK_TOP_LF_CH2_TUNE1, 0x00}, -+ {WSA885X_SPK_TOP_LF_CH1_CTRL9, 0x00}, -+ {WSA885X_SPK_TOP_LF_CH2_CTRL9, 0x00}, -+ {WSA885X_SPK_TOP_LF_CH2_CTRL11, 0x09}, -+ {WSA885X_SPK_TOP_COMMON_TUNE1, 0x03}, -+ {WSA885X_IVSENSE_VSNS_ISNS_CTL_CH1, 0x00}, -+ {WSA885X_DIG_CTRL0_CDC_CLK_CTL, 0x0e}, -+ {WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL2, 0x40}, -+ {WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL4, 0xff}, -+ {WSA885X_ANA_TOP_PLL_STATUS_0, 0x00}, -+ {WSA885X_ANA_TOP_PLL_STATUS_1, 0x00}, -+ {WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL2, 0x84}, -+ {WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3, 0x02}, -+ {WSA885X_ANA_TOP_IVSENSE_ADC_REF_CTL, 0x00}, -+ {WSA885X_ANA_TOP_IVSENSE_ADC_CDAC_CAL_CTL2, 0xe0}, -+ {WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_CTRL3, 0xa4}, -+ {WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_TUNE3, 0xc9}, -+ {WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_CTRL3, 0xa4}, -+ {WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_TUNE3, 0xc9}, -+ {WSA885X_DIG_CTRL0_TOP_CLK_CFG, 0x00}, -+ {WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x00}, -+ {WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE, 0x00}, -+ {WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x00}, -+ {WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00}, -+ {WSA885X_DIG_CTRL0_POWER_FSM_CTL0, 0x05}, -+ {WSA885X_DIG_CTRL0_POWER_FSM_CTL1, 0x00}, -+ {WSA885X_DIG_CTRL0_PA0_FSM_CTL1, 0x45}, -+ {WSA885X_DIG_CTRL0_PA1_FSM_CTL1, 0x45}, -+ {WSA885X_DIG_CTRL0_VBAT_THRM_FLT_CTL, 0x7f}, -+ {WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x00}, -+ {WSA885X_DIG_CTRL0_GAIN_RAMP0_CTL1, 0x01}, -+ {WSA885X_DIG_CTRL0_GAIN_RAMP1_CTL1, 0x01}, -+ {WSA885X_DIG_CTRL1_I2S_CTL0, 0x06}, -+ {WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, 0x00}, -+ {WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX, 0x00}, -+ {WSA885X_DIG_CTRL1_I2S_TDM_CTL0, 0x00}, -+ {WSA885X_DIG_CTRL1_I2S_TDM_CTL1, 0x05}, -+ {WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, 0x00}, -+ {WSA885X_DIG_CTRL1_I2S_RESET_CTL, 0x00}, -+ {WSA885X_DIG_CTRL1_I2S_TDM_CH_RX, WSA885X_I2S_TDM_CH_RX_CH3_EN}, -+ {WSA885X_CDC_RX0_RX_PATH_CFG0, 0x89}, -+ {WSA885X_CDC_RX0_RX_PATH_CFG1, 0x64}, -+ {WSA885X_CDC_RX0_RX_PATH_CTL, 0x24}, -+ {WSA885X_RX0_RX_PATH_DSMDEM_CTL, 0x01}, -+ {WSA885X_CDC_RX1_RX_PATH_CFG0, 0x89}, -+ {WSA885X_CDC_RX1_RX_PATH_CFG1, 0x64}, -+ {WSA885X_CDC_RX1_RX_PATH_CTL, 0x04}, -+ {WSA885X_RX1_RX_PATH_DSMDEM_CTL, 0x01}, -+ {WSA885X_CDC_COMPANDER0_CTL0, 0x01}, -+ {WSA885X_CDC_COMPANDER0_CTL7, 0x2a}, -+ {WSA885X_CDC_COMPANDER1_CTL0, 0x01}, -+ {WSA885X_CDC_COMPANDER1_CTL7, 0x2a}, -+ {WSA885X_CDC_VSENSE0_SPKR_PROT_PATH_CTL, 0x14}, -+ {WSA885X_CDC_VSENSE1_SPKR_PROT_PATH_CTL, 0x14}, -+ {WSA885X_CDC_ISENSE0_SPKR_PROT_PATH_CTL, 0x14}, -+ {WSA885X_CDC_ISENSE1_SPKR_PROT_PATH_CTL, 0x14}, -+ {WSA885X_CDC_CLSH_V1P8_BP_CTL1, 0x50}, -+ {WSA885X_CDC_CLSH_V1P8_BP_CTL0, 0x6c}, -+ {WSA885X_CDC_CLSH_CLSH_SIG_DP_CTL0, 0x0d}, -+ {WSA885X_CDC_CLSH_CLSH_V_HD_PA, 0x03}, -+ {WSA885X_CDC_CLSH_V1P8_BP_CTL2, 0x05}, -+}; -+ -+static void wsa885x_multi_update_bits(struct regmap *regmap, -+ const struct wsa885x_reg_update *updates, -+ size_t num_updates) -+{ -+ size_t i; -+ -+ for (i = 0; i < num_updates; i++) -+ regmap_update_bits(regmap, updates[i].reg, -+ updates[i].mask, updates[i].val); -+} -+ -+static void wsa885x_toggle_irq_bit(struct wsa885x_priv *wsa885x, -+ unsigned int reg, unsigned int mask) -+{ -+ regmap_update_bits(wsa885x->regmap, reg, mask, 0); -+ regmap_update_bits(wsa885x->regmap, reg, mask, mask); -+} -+ -+static void wsa885x_pulse_irq_bit(struct wsa885x_priv *wsa885x, -+ unsigned int reg, unsigned int mask) -+{ -+ regmap_update_bits(wsa885x->regmap, reg, mask, 0); -+ regmap_update_bits(wsa885x->regmap, reg, mask, mask); -+ regmap_update_bits(wsa885x->regmap, reg, mask, 0); -+} -+ -+static int wsa885x_tdm_ctl0_slot_num_val(int slots, unsigned int *slot_num_val) -+{ -+ if (!slot_num_val) -+ return -EINVAL; -+ -+ switch (slots) { -+ case 2: -+ *slot_num_val = WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_2; -+ return 0; -+ case 4: -+ *slot_num_val = WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_4; -+ return 0; -+ case 8: -+ *slot_num_val = WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_8; -+ return 0; -+ default: -+ return -EINVAL; -+ } -+} -+ -+static int wsa885x_reg_update_sequence(struct regmap *regmap, int slots) -+{ -+ static const struct reg_sequence regs[] = { -+ { WSA885X_DIG_CTRL1_I2S_TDM_CTL1, 0x15 }, -+ { WSA885X_DIG_CTRL1_I2S_TDM_CTL1, 0x11 }, -+ }; -+ unsigned int slot_num_val; -+ int ret; -+ -+ if (!regmap) -+ return -EINVAL; -+ -+ ret = wsa885x_tdm_ctl0_slot_num_val(slots, &slot_num_val); -+ if (ret) -+ return ret; -+ -+ regmap_multi_reg_write(regmap, regs, ARRAY_SIZE(regs)); -+ -+ regmap_update_bits(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CTL0, -+ WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, -+ slot_num_val); -+ regmap_update_bits(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CTL0, -+ WSA885X_I2S_TDM_CTL0_I2S_TDM_EN_MASK, -+ WSA885X_I2S_TDM_CTL0_I2S_TDM_EN_MASK); -+ regmap_write(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, -+ WSA885X_I2S_TDM_CH_TX_CH0_EN); -+ regmap_update_bits(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, -+ WSA885X_I2S_TDM_CH_TX_CH1_EN, -+ WSA885X_I2S_TDM_CH_TX_CH1_EN); -+ -+ return 0; -+} -+ -+static int wsa885x_wait_for_pll_lock(struct wsa885x_priv *wsa885x) -+{ -+ unsigned int status = 0; -+ int cnt = 0, ret = 0; -+ -+ do { -+ usleep_range(1000, 1100); -+ ret = regmap_read(wsa885x->regmap, WSA885X_ANA_TOP_PLL_STATUS_0, &status); -+ if (ret) { -+ dev_err(wsa885x->dev, "PLL status read failed: %d\n", ret); -+ return ret; -+ } -+ -+ if (status & WSA885X_PLL_LOCK_BIT) -+ return 0; -+ } while (++cnt < 20); -+ -+ dev_warn(wsa885x->dev, "PLL lock timeout after 20ms, status=0x%x\n", status); -+ return -ETIMEDOUT; -+} -+ -+static int wsa885x_2s_conf(struct wsa885x_priv *wsa885x) -+{ -+ static const struct reg_sequence regs[] = { -+ { WSA885X_SPK_TOP_COMMON_TUNE1, 0x26 }, -+ { WSA885X_SPK_TOP_LF_CH1_CTRL11, 0x0d }, -+ { WSA885X_SPK_TOP_LF_CH2_CTRL11, 0x0d }, -+ { WSA885X_CDC_CLSH_V1P8_BP_CTL1, 0x71 }, -+ { WSA885X_CDC_CLSH_V1P8_BP_CTL0, 0xAA }, -+ }; -+ -+ return regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs)); -+} -+ -+static const struct reg_sequence wsa885x_reg_init[] = { -+ { WSA885X_CDC_RX0_RX_PATH_CTL, 0x24 }, -+ { WSA885X_CDC_RX1_RX_PATH_CTL, 0x24 }, -+ { WSA885X_RX0_RX_PATH_DSMDEM_CTL, 0x01 }, -+ { WSA885X_RX1_RX_PATH_DSMDEM_CTL, 0x01 }, -+ { WSA885X_CDC_COMPANDER0_CTL0, 0x01 }, -+ { WSA885X_CDC_COMPANDER1_CTL0, 0x01 }, -+ { WSA885X_CDC_VSENSE0_SPKR_PROT_PATH_CTL, 0x14 }, -+ { WSA885X_CDC_VSENSE1_SPKR_PROT_PATH_CTL, 0x14 }, -+ { WSA885X_CDC_ISENSE0_SPKR_PROT_PATH_CTL, 0x14 }, -+ { WSA885X_CDC_ISENSE1_SPKR_PROT_PATH_CTL, 0x14 }, -+ { WSA885X_DIG_CTRL0_CDC_CLK_CTL, 0x0f }, -+ { WSA885X_DIG_CTRL0_CDC_CLK_CTL, 0x4f }, -+ { WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x02 }, -+ { WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x00 }, -+ { WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x01 }, -+ { WSA885X_SMP_AMP_CTRL_STEREO_CMT_GRP_MASK, 0x01 }, -+ { WSA885X_CDC_RX0_RX_PATH_CFG1, 0x60 }, -+ { WSA885X_CDC_RX1_RX_PATH_CFG1, 0x60 }, -+ { WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_CTRL3, 0xa5 }, -+ { WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_CTRL3, 0xa5 }, -+ { WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL2, 0x85 }, -+ { WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3, 0x0c }, -+ { WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3, 0x0e }, -+ { WSA885X_ANA_TOP_IVSENSE_ADC_REF_CTL, 0x0c }, -+ { WSA885X_DIG_CTRL0_GAIN_RAMP0_CTL1, 0x01 }, -+ { WSA885X_DIG_CTRL0_GAIN_RAMP1_CTL1, 0x01 }, -+ { WSA885X_CDC_RX0_RX_PATH_CFG0, 0x88 }, -+ { WSA885X_CDC_RX0_RX_PATH_CFG0, 0x89 }, -+ { WSA885X_CDC_RX1_RX_PATH_CFG0, 0x88 }, -+ { WSA885X_CDC_RX1_RX_PATH_CFG0, 0x89 }, -+ { WSA885X_ANA_TOP_BOOST_STB_CTRL2, 0x82 }, -+ { WSA885X_ANA_TOP_BOOST_STB_CTRL3, 0x34 }, -+ { WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL2, 0x41 }, -+ { WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL4, 0x7f }, -+ { WSA885X_CDC_CLSH_V1P8_BP_CTL1, 0x50 }, -+ { WSA885X_CDC_CLSH_V1P8_BP_CTL0, 0x6c }, -+ { WSA885X_CDC_CLSH_CLSH_SIG_DP_CTL0, 0x0d }, -+ { WSA885X_CDC_CLSH_CLSH_V_HD_PA, 0x03 }, -+ { WSA885X_DIG_CTRL0_POWER_FSM_CTL0, 0x05 }, -+ { WSA885X_ANA_TOP_PON_CKSK_CTL_0, 0x20 }, -+ { WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_TUNE3, 0x45 }, -+ { WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_TUNE3, 0x45 }, -+ { WSA885X_CDC_CLSH_V1P8_BP_CTL2, 0x05 }, -+ { WSA885X_ANA_TOP_BG_TVP_UVLO1_PROG, 0x35 }, -+ { WSA885X_ANA_TOP_BG_TVP_UVLO2_PROG, 0x21 }, -+ { WSA885X_ANA_TOP_BOOST_BYP_CTRL2, 0xc7 }, -+ { WSA885X_ANA_TOP_BOOST_BYP_CTRL3, 0x11 }, -+ { WSA885X_ANA_TOP_IVSENSE_ADC_CDAC_CAL_CTL2, 0x80 }, -+ { WSA885X_ANA_TOP_SPK_TOP_SPARE3, 0x08 }, -+ { WSA885X_DIG_CTRL0_PA0_FSM_CTL1, 0x47 }, -+ { WSA885X_DIG_CTRL0_PA1_FSM_CTL1, 0x47 }, -+ { WSA885X_CDC_COMPANDER0_CTL7, 0x34 }, -+ { WSA885X_CDC_COMPANDER1_CTL7, 0x34 }, -+ { WSA885X_DIG_CTRL0_VBAT_THRM_FLT_CTL, 0x79 }, -+}; -+ -+static int wsa885x_hw_init(struct wsa885x_priv *wsa885x) -+{ -+ static const struct reg_sequence regs[] = { -+ { WSA885X_DIG_CTRL1_SPMI_PAD_GPIO2_CTL, 0x2e }, -+ { WSA885X_DIG_CTRL1_INTR_MODE, 0x01 }, -+ { WSA885X_DIG_CTRL1_PIN_CT, 0x04 }, -+ }; -+ int ret; -+ -+ ret = regmap_multi_reg_write(wsa885x->regmap, wsa885x_reg_init, -+ ARRAY_SIZE(wsa885x_reg_init)); -+ if (ret) -+ return ret; -+ -+ if (wsa885x->batt_conf == WSA885X_BATT_2S) { -+ ret = wsa885x_2s_conf(wsa885x); -+ if (ret) -+ return ret; -+ } -+ -+ return regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs)); -+} -+ -+static int wsa885x_unmask_interrupts(struct wsa885x_priv *wsa885x) -+{ -+ static const struct reg_sequence regs[] = { -+ { WSA885X_INTR_MASK0, 0x00 }, -+ { WSA885X_INTR_MASK0 + 1, 0x00 }, -+ { WSA885X_INTR_MASK0 + 2, 0xf8 }, -+ }; -+ -+ return regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs)); -+} -+ -+static int wsa885x_wait_for_pde_state(struct wsa885x_priv *wsa885x, int ps) -+{ -+ unsigned int act_ps = 0, clock_valid = 0; -+ int rc = 0, cnt = 0; -+ -+ if (ps < 0 || ps > 3) -+ return -EINVAL; -+ -+ do { -+ usleep_range(1000, 1500); -+ rc = regmap_read(wsa885x->regmap, -+ WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS, -+ &act_ps); -+ if (rc) { -+ dev_err(wsa885x->dev, "PDE state read failed: %d\n", rc); -+ return rc; -+ } -+ if (act_ps == ps) -+ return 0; -+ } while (++cnt < 5); -+ -+ if (regmap_read(wsa885x->regmap, -+ WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID, -+ &clock_valid)) -+ dev_err(wsa885x->dev, -+ "PDE power state %d request failed, actual_ps %d, clock_valid read failed\n", -+ ps, act_ps); -+ else -+ dev_err(wsa885x->dev, -+ "PDE power state %d request failed, actual_ps %d, clock_valid:%d\n", -+ ps, act_ps, clock_valid); -+ -+ return -ETIMEDOUT; -+} -+ -+static void wsa885x_program_stereo_volume(struct wsa885x_priv *wsa885x, -+ int stereo_vol_db, bool commit) -+{ -+ regmap_write(wsa885x->regmap, -+ WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_MSB, -+ (u8)(s8)stereo_vol_db); -+ regmap_write(wsa885x->regmap, -+ WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB, 0x00); -+ regmap_write(wsa885x->regmap, -+ WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_MSB, -+ (u8)(s8)stereo_vol_db); -+ regmap_write(wsa885x->regmap, -+ WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB, 0x00); -+ -+ if (commit) -+ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01); -+} -+ -+static int wsa885x_codec_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params, -+ struct snd_soc_dai *dai) -+{ -+ struct wsa885x_priv *wsa885x; -+ u8 pcm_rate, cs21_sample_rate_idx, cs24_sample_rate_idx; -+ -+ wsa885x = snd_soc_component_get_drvdata(dai->component); -+ -+ switch (params_rate(params)) { -+ case 8000: -+ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_8KHZ; -+ cs21_sample_rate_idx = WSA885X_RX_RATE_8000HZ; -+ cs24_sample_rate_idx = WSA885X_VI_RATE_8000HZ; -+ break; -+ case 16000: -+ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_16KHZ; -+ cs21_sample_rate_idx = WSA885X_RX_RATE_16000HZ; -+ cs24_sample_rate_idx = WSA885X_VI_RATE_16000HZ; -+ break; -+ case 32000: -+ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_32KHZ; -+ cs21_sample_rate_idx = WSA885X_RX_RATE_32000HZ; -+ cs24_sample_rate_idx = WSA885X_VI_RATE_48000HZ; -+ break; -+ case 44100: -+ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_48_OR_44KHZ; -+ cs21_sample_rate_idx = WSA885X_RX_RATE_44100HZ; -+ cs24_sample_rate_idx = WSA885X_VI_RATE_44100HZ; -+ break; -+ case 48000: -+ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_48_OR_44KHZ; -+ cs21_sample_rate_idx = WSA885X_RX_RATE_48000HZ; -+ cs24_sample_rate_idx = WSA885X_VI_RATE_48000HZ; -+ break; -+ case 88200: -+ case 96000: -+ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_96_OR_88KHZ; -+ cs21_sample_rate_idx = WSA885X_RX_RATE_96000HZ; -+ cs24_sample_rate_idx = WSA885X_VI_RATE_96000HZ; -+ break; -+ case 176400: -+ case 192000: -+ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_192_OR_176KHZ; -+ cs21_sample_rate_idx = WSA885X_RX_RATE_192000HZ; -+ cs24_sample_rate_idx = WSA885X_VI_RATE_192000HZ; -+ break; -+ case 352800: -+ case 384000: -+ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_384_OR_352KHZ; -+ cs21_sample_rate_idx = WSA885X_RX_RATE_384000HZ; -+ cs24_sample_rate_idx = WSA885X_VI_RATE_384000HZ; -+ break; -+ default: -+ dev_err(wsa885x->dev, "sampling rate %d is not supported\n", params_rate(params)); -+ return -EINVAL; -+ } -+ -+ regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_CTL0, -+ WSA885X_I2S_CTL0_PCM_RATE_MASK | -+ WSA885X_I2S_CTL0_ENABLE_MASK, -+ WSA885X_I2S_CTL0_PCM_RATE(pcm_rate) | -+ WSA885X_I2S_CTL0_ENABLE_MASK); -+ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_RESET_CTL, 0x00); -+ regmap_write(wsa885x->regmap, WSA885X_SMP_AMP_CTRL_STEREO_CS21_SAMPLERATEINDEX, -+ cs21_sample_rate_idx); -+ regmap_write(wsa885x->regmap, WSA885X_SMP_AMP_CTRL_STEREO_CS24_SAMPLERATEINDEX, -+ cs24_sample_rate_idx); -+ -+ mutex_lock(&wsa885x->state_lock); -+ wsa885x_program_stereo_volume(wsa885x, wsa885x->stereo_vol_db, false); -+ mutex_unlock(&wsa885x->state_lock); -+ -+ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01); -+ -+ return 0; -+} -+ -+static int wsa885x_codec_set_tdm_slot(struct snd_soc_dai *dai, -+ unsigned int tx_slot_mask, -+ unsigned int rx_slot_mask, int slots, -+ int slot_width) -+{ -+ static const struct wsa885x_reg_update stereo_updates[] = { -+ { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK, -+ WSA885X_I2S_CFG0_TDM_TX_SLOT0(WSA885X_I2S_TX_SLOT_ISENSE0) }, -+ { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK, -+ WSA885X_I2S_CFG0_TDM_TX_SLOT1(WSA885X_I2S_TX_SLOT_ISENSE1) }, -+ { WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX, WSA885X_I2S_CFG1_TDM_TX_SLOT2_MASK, -+ WSA885X_I2S_CFG1_TDM_TX_SLOT2(WSA885X_I2S_TX_SLOT_CUR_SENSE0) }, -+ { WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX, WSA885X_I2S_CFG1_TDM_TX_SLOT3_MASK, -+ WSA885X_I2S_CFG1_TDM_TX_SLOT3(WSA885X_I2S_TX_SLOT_CUR_SENSE1) }, -+ }; -+ static const struct wsa885x_reg_update mono_left_updates[] = { -+ { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK, -+ WSA885X_I2S_CFG0_TDM_TX_SLOT0(WSA885X_I2S_TX_SLOT_ISENSE0) }, -+ { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK, -+ WSA885X_I2S_CFG0_TDM_TX_SLOT1(WSA885X_I2S_TX_SLOT_CUR_SENSE0) }, -+ }; -+ static const struct wsa885x_reg_update mono_right_updates[] = { -+ { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK, -+ WSA885X_I2S_CFG0_TDM_TX_SLOT0(WSA885X_I2S_TX_SLOT_ISENSE1) }, -+ { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK, -+ WSA885X_I2S_CFG0_TDM_TX_SLOT1(WSA885X_I2S_TX_SLOT_CUR_SENSE1) }, -+ }; -+ struct wsa885x_priv *wsa885x; -+ unsigned int slot_num_val; -+ u32 mask; -+ int ret; -+ -+ wsa885x = snd_soc_component_get_drvdata(dai->component); -+ -+ ret = wsa885x_tdm_ctl0_slot_num_val(slots, &slot_num_val); -+ if (ret) { -+ dev_err(wsa885x->dev, "%s: unsupported slot count %d\n", -+ __func__, slots); -+ return ret; -+ } -+ -+ if (rx_slot_mask && !wsa885x_is_valid_rx_slot_mask(rx_slot_mask)) { -+ dev_err(wsa885x->dev, -+ "%s: unsupported rx_slot_mask 0x%x\n", -+ __func__, rx_slot_mask); -+ return -EINVAL; -+ } -+ -+ mutex_lock(&wsa885x->state_lock); -+ if (rx_slot_mask) -+ wsa885x->rx_slot_mask = rx_slot_mask; -+ else if (!wsa885x_is_valid_rx_slot_mask(wsa885x->rx_slot_mask)) -+ wsa885x->rx_slot_mask = WSA885X_CHANNEL_STEREO; -+ mask = wsa885x->rx_slot_mask; -+ -+ regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_RESET_CTL, -+ WSA885X_I2S_RESET_CTL_RESET_MASK, -+ WSA885X_I2S_RESET_CTL_RESET_MASK); -+ -+ if (mask == WSA885X_CHANNEL_STEREO) { -+ wsa885x_multi_update_bits(wsa885x->regmap, stereo_updates, -+ ARRAY_SIZE(stereo_updates)); -+ ret = wsa885x_reg_update_sequence(wsa885x->regmap, slots); -+ if (ret) -+ goto exit_unlock; -+ regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, -+ WSA885X_I2S_TDM_CH_TX_CH2_EN, -+ WSA885X_I2S_TDM_CH_TX_CH2_EN); -+ regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, -+ WSA885X_I2S_TDM_CH_TX_CH3_EN, -+ WSA885X_I2S_TDM_CH_TX_CH3_EN); -+ } else if (mask == WSA885X_CHANNEL_MONO_LEFT) { -+ wsa885x_multi_update_bits(wsa885x->regmap, mono_left_updates, -+ ARRAY_SIZE(mono_left_updates)); -+ ret = wsa885x_reg_update_sequence(wsa885x->regmap, slots); -+ if (ret) -+ goto exit_unlock; -+ } else if (mask == WSA885X_CHANNEL_MONO_RIGHT) { -+ wsa885x_multi_update_bits(wsa885x->regmap, mono_right_updates, -+ ARRAY_SIZE(mono_right_updates)); -+ ret = wsa885x_reg_update_sequence(wsa885x->regmap, slots); -+ if (ret) -+ goto exit_unlock; -+ } -+ -+ regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_CTL0, -+ WSA885X_I2S_CTL0_ENABLE_MASK, -+ WSA885X_I2S_CTL0_ENABLE_MASK); -+ regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_RESET_CTL, -+ WSA885X_I2S_RESET_CTL_RESET_MASK, 0); -+ -+ ret = 0; -+ -+exit_unlock: -+ mutex_unlock(&wsa885x->state_lock); -+ -+ return ret; -+} -+ -+static int wsa885x_codec_set_sysclk(struct snd_soc_dai *dai, int clk_id, -+ unsigned int freq, int dir) -+{ -+ static const struct reg_sequence pll_prep[] = { -+ { WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x03 }, -+ { WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x04 }, -+ { WSA885X_ANA_TOP_PLL_LOOPFILT_0, 0xB4 }, -+ { WSA885X_ANA_TOP_PLL_VCO_CTL, 0x00 }, -+ { WSA885X_ANA_TOP_PLL_OVRD_CTL, 0x00 }, -+ }; -+ static const struct reg_sequence pll_cleanup[] = { -+ { WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE, 0x00 }, -+ { WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x00 }, -+ { WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x00 }, -+ }; -+ struct wsa885x_priv *wsa885x; -+ u32 pll_div; -+ int ret = 0; -+ -+ wsa885x = snd_soc_component_get_drvdata(dai->component); -+ -+ if (!freq) -+ return -EINVAL; -+ if (WSA885X_CLK_RATE_FIXED % freq) -+ return -EINVAL; -+ pll_div = WSA885X_CLK_RATE_FIXED / freq; -+ if (pll_div > 0xff) -+ return -EINVAL; -+ -+ regmap_multi_reg_write(wsa885x->regmap, pll_prep, ARRAY_SIZE(pll_prep)); -+ regmap_write(wsa885x->regmap, WSA885X_ANA_PLL_DIV_CTL_0, pll_div); -+ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE, 0x02); -+ -+ ret = wsa885x_wait_for_pll_lock(wsa885x); -+ if (ret) { -+ dev_err(wsa885x->dev, "PLL lock failed, aborting sysclk configuration\n"); -+ regmap_multi_reg_write(wsa885x->regmap, pll_cleanup, -+ ARRAY_SIZE(pll_cleanup)); -+ return ret; -+ } -+ -+ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x00); -+ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_POWER_FSM_CTL1, 0x01); -+ regmap_write(wsa885x->regmap, WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x00); -+ -+ return 0; -+} -+ -+static int wsa885x_codec_mute_stream(struct snd_soc_dai *dai, int mute, int stream) -+{ -+ static const struct reg_sequence mute_regs[] = { -+ { WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00 }, -+ { WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS, 0x03 }, -+ }; -+ static const struct reg_sequence mute_commit_regs[] = { -+ { WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0, 0x01 }, -+ { WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1, 0x01 }, -+ { WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01 }, -+ }; -+ static const struct reg_sequence unmute_prep_head_regs[] = { -+ { WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00 }, -+ }; -+ static const struct reg_sequence unmute_prep_tail_regs[] = { -+ { WSA885X_SMP_AMP_CTRL_STEREO_IT21_CLUSERINDEX, 0x01 }, -+ { WSA885X_SMP_AMP_CTRL_STEREO_PPU21_POSTURENUMBER, 0x01 }, -+ }; -+ static const struct reg_sequence unmute_volume_regs[] = { -+ { WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB, 0x00 }, -+ { WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB, 0x00 }, -+ }; -+ static const struct reg_sequence unmute_commit_regs[] = { -+ { WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01 }, -+ { WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS, 0x00 }, -+ }; -+ static const struct reg_sequence unmute_finish_regs[] = { -+ { WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0, 0x00 }, -+ { WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1, 0x00 }, -+ { WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01 }, -+ }; -+ struct wsa885x_priv *wsa885x; -+ int ret = 0, ps0 = 0, ps3 = 3; -+ -+ wsa885x = snd_soc_component_get_drvdata(dai->component); -+ -+ if (stream != SNDRV_PCM_STREAM_PLAYBACK) -+ return 0; -+ -+ mutex_lock(&wsa885x->state_lock); -+ -+ if (wsa885x->usage_mode > WSA885X_USAGE_MODE_MAX) { -+ ret = -EINVAL; -+ goto exit_unlock; -+ } -+ -+ if (!wsa885x_is_valid_rx_slot_mask(wsa885x->rx_slot_mask)) -+ wsa885x->rx_slot_mask = WSA885X_CHANNEL_STEREO; -+ -+ if (mute) { -+ regmap_multi_reg_write(wsa885x->regmap, mute_regs, -+ ARRAY_SIZE(mute_regs)); -+ ret = wsa885x_wait_for_pde_state(wsa885x, ps3); -+ if (ret) { -+ dev_err(wsa885x->dev, -+ "PS3 transition failed: %d\n", ret); -+ } else { -+ regmap_multi_reg_write(wsa885x->regmap, mute_commit_regs, -+ ARRAY_SIZE(mute_commit_regs)); -+ } -+ } else { -+ regmap_multi_reg_write(wsa885x->regmap, unmute_prep_head_regs, -+ ARRAY_SIZE(unmute_prep_head_regs)); -+ regmap_write(wsa885x->regmap, WSA885X_SMP_AMP_CTRL_STEREO_OT23_USAGE, -+ wsa885x->usage_mode); -+ regmap_multi_reg_write(wsa885x->regmap, unmute_prep_tail_regs, -+ ARRAY_SIZE(unmute_prep_tail_regs)); -+ wsa885x_program_stereo_volume(wsa885x, wsa885x->stereo_vol_db, false); -+ regmap_multi_reg_write(wsa885x->regmap, unmute_volume_regs, -+ ARRAY_SIZE(unmute_volume_regs)); -+ regmap_multi_reg_write(wsa885x->regmap, unmute_commit_regs, -+ ARRAY_SIZE(unmute_commit_regs)); -+ ret = wsa885x_wait_for_pde_state(wsa885x, ps0); -+ if (ret) -+ goto exit_unlock; -+ -+ if (wsa885x->rx_slot_mask == WSA885X_CHANNEL_STEREO) { -+ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_RX, -+ WSA885X_I2S_TDM_CH_RX_CH0_EN | -+ WSA885X_I2S_TDM_CH_RX_CH3_EN); -+ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x03); -+ } else if (wsa885x->rx_slot_mask == WSA885X_CHANNEL_MONO_LEFT) { -+ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_RX, -+ WSA885X_I2S_TDM_CH_RX_CH3_EN); -+ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x01); -+ } else if (wsa885x->rx_slot_mask == WSA885X_CHANNEL_MONO_RIGHT) { -+ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_RX, -+ WSA885X_I2S_TDM_CH_RX_CH0_EN); -+ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x02); -+ } -+ -+ regmap_multi_reg_write(wsa885x->regmap, unmute_finish_regs, -+ ARRAY_SIZE(unmute_finish_regs)); -+ } -+ -+exit_unlock: -+ mutex_unlock(&wsa885x->state_lock); -+ -+ return ret; -+} -+ -+static int wsa885x_codec_hw_free(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ static const struct reg_sequence regs[] = { -+ { WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00 }, -+ }; -+ struct wsa885x_priv *wsa885x; -+ -+ wsa885x = snd_soc_component_get_drvdata(dai->component); -+ -+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) -+ return 0; -+ -+ mutex_lock(&wsa885x->state_lock); -+ regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs)); -+ mutex_unlock(&wsa885x->state_lock); -+ -+ return 0; -+} -+ -+static const struct snd_soc_dai_ops wsa885x_dai_ops = { -+ .hw_params = wsa885x_codec_hw_params, -+ .set_tdm_slot = wsa885x_codec_set_tdm_slot, -+ .set_sysclk = wsa885x_codec_set_sysclk, -+ .mute_stream = wsa885x_codec_mute_stream, -+ .hw_free = wsa885x_codec_hw_free, -+}; -+ -+static struct snd_soc_dai_driver wsa885x_dai[] = { -+ { -+ .name = "wsa885x_dai_drv", -+ .playback = { -+ .stream_name = "WSA885X TDM Playback", -+ .channels_min = 1, -+ .channels_max = 2, -+ .rates = WSA885X_RATES, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | -+ SNDRV_PCM_FMTBIT_S32_LE, -+ }, -+ .ops = &wsa885x_dai_ops, -+ }, -+}; -+ -+static void wsa885x_reset_assert(void *data) -+{ -+ struct wsa885x_priv *wsa885x = data; -+ -+ if (wsa885x->sd_reset) -+ reset_control_assert(wsa885x->sd_reset); -+ else -+ gpiod_direction_output(wsa885x->sd_n, 1); -+} -+ -+static void wsa885x_reset_deassert(struct wsa885x_priv *wsa885x) -+{ -+ if (wsa885x->sd_reset) -+ reset_control_deassert(wsa885x->sd_reset); -+ else -+ gpiod_direction_output(wsa885x->sd_n, 0); -+} -+ -+static int wsa885x_get_reset(struct device *dev, struct wsa885x_priv *wsa885x) -+{ -+ wsa885x->sd_reset = devm_reset_control_get_optional_shared(dev, NULL); -+ if (IS_ERR(wsa885x->sd_reset)) -+ return dev_err_probe(dev, PTR_ERR(wsa885x->sd_reset), -+ "Failed to get reset\n"); -+ else if (wsa885x->sd_reset) -+ return 0; -+ -+ wsa885x->sd_n = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); -+ if (IS_ERR(wsa885x->sd_n)) -+ return dev_err_probe(dev, PTR_ERR(wsa885x->sd_n), -+ "Shutdown Control GPIO not found\n"); -+ -+ return 0; -+} -+ -+static bool wsa885x_volatile_register(struct device *dev, unsigned int reg) -+{ -+ switch (reg) { -+ case WSA885X_ANA_TOP_PLL_STATUS_0: -+ case WSA885X_ANA_TOP_PLL_STATUS_1: -+ case WSA885X_DIG_CTRL0_SDCA_COMMIT: -+ case WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS: -+ case WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID: -+ case WSA885X_INTR_STATUS0: -+ case WSA885X_INTR_STATUS0 + 1: -+ case WSA885X_INTR_STATUS0 + 2: -+ case WSA885X_INTR_CLEAR0: -+ case WSA885X_INTR_CLEAR0 + 1: -+ case WSA885X_INTR_CLEAR0 + 2: -+ return true; -+ default: -+ return false; -+ } -+} -+ -+static bool wsa885x_readable_register(struct device *dev, unsigned int reg) -+{ -+ if (reg == WSA885X_INTR_CLEAR0 || -+ reg == WSA885X_INTR_CLEAR0 + 1 || -+ reg == WSA885X_INTR_CLEAR0 + 2) -+ return false; -+ return reg <= 0x88ff; -+} -+ -+static bool wsa885x_writeable_register(struct device *dev, unsigned int reg) -+{ -+ if (reg > 0x88ff) -+ return false; -+ -+ switch (reg) { -+ case WSA885X_ANA_TOP_PLL_STATUS_0: -+ case WSA885X_ANA_TOP_PLL_STATUS_1: -+ case WSA885X_INTR_STATUS0: -+ case WSA885X_INTR_STATUS0 + 1: -+ case WSA885X_INTR_STATUS0 + 2: -+ case WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS: -+ case WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID: -+ return false; -+ default: -+ return true; -+ } -+} -+ -+static const struct regmap_config wsa885x_regmap_cfg = { -+ .reg_bits = 8, -+ .val_bits = 8, -+ .max_register = 0x88FF, -+ .ranges = wsa885x_regmap_ranges, -+ .num_ranges = ARRAY_SIZE(wsa885x_regmap_ranges), -+ .reg_defaults = wsa885x_codec_reg_defaults, -+ .num_reg_defaults = ARRAY_SIZE(wsa885x_codec_reg_defaults), -+ .volatile_reg = wsa885x_volatile_register, -+ .writeable_reg = wsa885x_writeable_register, -+ .readable_reg = wsa885x_readable_register, -+ .cache_type = REGCACHE_MAPLE, -+ .use_single_read = true, -+ .use_single_write = true, -+}; -+ -+static int wsa885x_component_probe(struct snd_soc_component *component) -+{ -+ struct wsa885x_priv *wsa885x = -+ snd_soc_component_get_drvdata(component); -+ int ret; -+ -+ wsa885x->component = component; -+ snd_soc_component_init_regmap(component, wsa885x->regmap); -+ -+ ret = wsa885x_hw_init(wsa885x); -+ if (ret) -+ return ret; -+ -+ return wsa885x_unmask_interrupts(wsa885x); -+} -+ -+static int wsa885x_stereo_gain_offset_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); -+ int val; -+ -+ mutex_lock(&wsa885x->state_lock); -+ val = wsa885x->stereo_vol_db + 84; -+ mutex_unlock(&wsa885x->state_lock); -+ if (val < 0 || val > WSA885X_FU21_VOL_STEPS) -+ return -ERANGE; -+ -+ ucontrol->value.integer.value[0] = val; -+ return 0; -+} -+ -+static int wsa885x_stereo_gain_offset_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); -+ long val; -+ int stereo_vol_db; -+ -+ val = ucontrol->value.integer.value[0]; -+ -+ if (val < 0 || val > WSA885X_FU21_VOL_STEPS) { -+ dev_err(component->dev, "%s: Invalid range, Val: %ld\n", __func__, val); -+ return -EINVAL; -+ } -+ -+ stereo_vol_db = (int)val - 84; -+ -+ mutex_lock(&wsa885x->state_lock); -+ if (wsa885x->stereo_vol_db == stereo_vol_db) { -+ mutex_unlock(&wsa885x->state_lock); -+ return 0; -+ } -+ -+ wsa885x_program_stereo_volume(wsa885x, stereo_vol_db, true); -+ wsa885x->stereo_vol_db = stereo_vol_db; -+ mutex_unlock(&wsa885x->state_lock); -+ -+ return 1; -+} -+ -+static int wsa885x_usage_modes_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); -+ -+ mutex_lock(&wsa885x->state_lock); -+ if (wsa885x->usage_mode > WSA885X_USAGE_MODE_MAX) { -+ mutex_unlock(&wsa885x->state_lock); -+ return -ERANGE; -+ } -+ -+ ucontrol->value.integer.value[0] = wsa885x->usage_mode; -+ mutex_unlock(&wsa885x->state_lock); -+ -+ return 0; -+} -+ -+static int wsa885x_usage_modes_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); -+ u32 val = ucontrol->value.integer.value[0]; -+ -+ if (val > WSA885X_USAGE_MODE_MAX) -+ return -EINVAL; -+ -+ mutex_lock(&wsa885x->state_lock); -+ if (wsa885x->usage_mode == val) { -+ mutex_unlock(&wsa885x->state_lock); -+ return 0; -+ } -+ -+ wsa885x->usage_mode = val; -+ mutex_unlock(&wsa885x->state_lock); -+ -+ return 1; -+} -+ -+static int wsa885x_rx_slot_mask_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); -+ u32 mask; -+ -+ mutex_lock(&wsa885x->state_lock); -+ mask = wsa885x->rx_slot_mask; -+ mutex_unlock(&wsa885x->state_lock); -+ if (!wsa885x_is_valid_rx_slot_mask(mask)) -+ return -ERANGE; -+ -+ ucontrol->value.integer.value[0] = mask; -+ -+ return 0; -+} -+ -+static int wsa885x_rx_slot_mask_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); -+ u32 mask = ucontrol->value.integer.value[0]; -+ -+ if (!wsa885x_is_valid_rx_slot_mask(mask)) -+ return -EINVAL; -+ -+ mutex_lock(&wsa885x->state_lock); -+ if (wsa885x->rx_slot_mask == mask) { -+ mutex_unlock(&wsa885x->state_lock); -+ return 0; -+ } -+ -+ wsa885x->rx_slot_mask = mask; -+ mutex_unlock(&wsa885x->state_lock); -+ -+ return 1; -+} -+ -+static const struct snd_kcontrol_new wsa885x_snd_controls[] = { -+ SOC_SINGLE_EXT("Usage Mode", SND_SOC_NOPM, 0, WSA885X_USAGE_MODE_MAX, 0, -+ wsa885x_usage_modes_get, -+ wsa885x_usage_modes_put), -+ -+ SOC_SINGLE_EXT_TLV("Speaker Volume", SND_SOC_NOPM, -+ 0, WSA885X_FU21_VOL_STEPS, 0, -+ wsa885x_stereo_gain_offset_get, -+ wsa885x_stereo_gain_offset_put, -+ wsa885x_fu21_digital_gain), -+ -+ SOC_SINGLE_EXT("Rx Slot Mask", SND_SOC_NOPM, 0, 3, 0, -+ wsa885x_rx_slot_mask_get, -+ wsa885x_rx_slot_mask_put), -+}; -+ -+static const struct snd_soc_component_driver wsa885x_component = { -+ .name = "wsa885x", -+ .probe = wsa885x_component_probe, -+ .controls = wsa885x_snd_controls, -+ .num_controls = ARRAY_SIZE(wsa885x_snd_controls), -+}; -+ -+static irqreturn_t wsa885x_handle_irq(int irq_idx, void *data) -+{ -+ struct wsa885x_priv *wsa885x = data; -+ -+ if (irq_idx < 0 || irq_idx >= WSA885X_IRQ_MAX) -+ return IRQ_NONE; -+ -+ switch (irq_idx) { -+ case WSA885X_IRQ_INT_SAF2WAR: -+ case WSA885X_IRQ_INT_WAR2SAF: -+ case WSA885X_IRQ_INT_DISABLE: -+ case WSA885X_IRQ_INT_INTR_GPIO1_PIN: -+ case WSA885X_IRQ_INT_INTR_GPIO2_PIN: -+ case WSA885X_IRQ_INT_PA0_OCP: -+ case WSA885X_IRQ_INT_PA1_OCP: -+ case WSA885X_IRQ_INT_CLIP0: -+ case WSA885X_IRQ_INT_CLIP1: -+ case WSA885X_IRQ_INT_CLK_WD: -+ case WSA885X_IRQ_INT_BOP: -+ case WSA885X_IRQ_INT_UVLO: -+ case WSA885X_IRQ_INT_PCM_DATA0_DC: -+ case WSA885X_IRQ_INT_PCM_DATA1_DC: -+ case WSA885X_IRQ_INT_PLL_UNLOCKED: -+ case WSA885X_IRQ_INT_PROT_MODE_CHANGE: -+ case WSA885X_IRQ_INT_PB_CLOCK_VALID: -+ case WSA885X_IRQ_INT_SENSE_CLOCK_VALID: -+ break; -+ case WSA885X_IRQ_INT_PCM_DATA0_WD: -+ case WSA885X_IRQ_INT_PCM_DATA1_WD: -+ if (irq_idx == WSA885X_IRQ_INT_PCM_DATA0_WD) -+ wsa885x_toggle_irq_bit(wsa885x, WSA885X_DIG_CTRL0_PCM_DATA_WD0_CTL1, -+ WSA885X_PCM_DATA_WD_CTL1_PCM_DATA_WD_EN_MASK); -+ else -+ wsa885x_toggle_irq_bit(wsa885x, WSA885X_DIG_CTRL0_PCM_DATA_WD1_CTL1, -+ WSA885X_PCM_DATA_WD_CTL1_PCM_DATA_WD_EN_MASK); -+ break; -+ case WSA885X_IRQ_INT_PA0_FSM_ERR: -+ case WSA885X_IRQ_INT_PA1_FSM_ERR: -+ case WSA885X_IRQ_INT_MAIN_FSM_ERR: -+ if (irq_idx == WSA885X_IRQ_INT_MAIN_FSM_ERR) { -+ wsa885x_pulse_irq_bit(wsa885x, WSA885X_DIG_CTRL0_POWER_FSM_CTL0, -+ WSA885X_POWER_FSM_CTL0_CLEAR_ERROR_MASK); -+ } else if (irq_idx == WSA885X_IRQ_INT_PA0_FSM_ERR) { -+ wsa885x_pulse_irq_bit(wsa885x, WSA885X_PA0_FSM_CTL0, -+ WSA885X_PA_FSM_CTL0_CLEAR_ERROR_MASK); -+ } else if (irq_idx == WSA885X_IRQ_INT_PA1_FSM_ERR) { -+ wsa885x_pulse_irq_bit(wsa885x, WSA885X_PA1_FSM_CTL0, -+ WSA885X_PA_FSM_CTL0_CLEAR_ERROR_MASK); -+ } -+ break; -+ default: -+ break; -+ } -+ -+ return IRQ_HANDLED; -+} -+ -+static irqreturn_t wsa885x_interrupt_handler(int irq, void *data) -+{ -+ static const unsigned int status_reg[WSA885X_NUM_REGS] = { -+ WSA885X_INTR_STATUS0, -+ WSA885X_INTR_STATUS0 + 1, -+ WSA885X_INTR_STATUS0 + 2, -+ }; -+ static const unsigned int clear_reg[WSA885X_NUM_REGS] = { -+ WSA885X_INTR_CLEAR0, -+ WSA885X_INTR_CLEAR0 + 1, -+ WSA885X_INTR_CLEAR0 + 2, -+ }; -+ unsigned int status[WSA885X_NUM_REGS] = { 0 }; -+ struct wsa885x_priv *wsa885x = data; -+ irqreturn_t handled = IRQ_NONE; -+ irqreturn_t irq_ret; -+ int i, bit, ret, irq_num; -+ -+ for (i = 0; i < WSA885X_NUM_REGS; i++) { -+ ret = regmap_read(wsa885x->regmap, status_reg[i], &status[i]); -+ if (ret) { -+ dev_err(wsa885x->dev, -+ "Failed to read status_reg[%d] (0x%x): %d\n", -+ i, status_reg[i], ret); -+ status[i] = 0; -+ continue; -+ } -+ } -+ -+ for (i = 0; i < WSA885X_NUM_REGS; i++) { -+ for (bit = 0; bit < 8; bit++) { -+ if (status[i] & BIT(bit)) { -+ irq_num = i * 8 + bit; -+ regmap_write(wsa885x->regmap, clear_reg[i], BIT(bit)); -+ regmap_write(wsa885x->regmap, clear_reg[i], 0); -+ if (irq_num >= WSA885X_IRQ_MAX) { -+ dev_warn_ratelimited(wsa885x->dev, -+ "Unexpected IRQ bit %d (reg %d)\n", -+ bit, i); -+ handled = IRQ_HANDLED; -+ continue; -+ } -+ irq_ret = wsa885x_handle_irq(irq_num, wsa885x); -+ if (irq_ret == IRQ_HANDLED) -+ handled = IRQ_HANDLED; -+ } -+ } -+ } -+ return handled; -+} -+ -+static int wsa885x_register_irq(struct wsa885x_priv *wsa885x) -+{ -+ if (!wsa885x->client->irq) -+ return dev_err_probe(wsa885x->dev, -EINVAL, -+ "IRQ is not configured\n"); -+ -+ return devm_request_threaded_irq(wsa885x->dev, wsa885x->client->irq, NULL, -+ wsa885x_interrupt_handler, -+ IRQF_ONESHOT, -+ dev_name(wsa885x->dev), wsa885x); -+} -+ -+static int wsa885x_probe(struct i2c_client *client) -+{ -+ struct wsa885x_priv *wsa885x; -+ const struct snd_soc_component_driver *component_driver = &wsa885x_component; -+ const char *battery_config; -+ unsigned int i; -+ int ret; -+ struct device *dev = &client->dev; -+ -+ wsa885x = devm_kzalloc(dev, sizeof(*wsa885x), GFP_KERNEL); -+ if (!wsa885x) -+ return -ENOMEM; -+ -+ wsa885x->client = client; -+ wsa885x->dev = dev; -+ wsa885x->stereo_vol_db = -84; -+ wsa885x->rx_slot_mask = WSA885X_CHANNEL_STEREO; -+ mutex_init(&wsa885x->state_lock); -+ wsa885x->regmap = devm_regmap_init_i2c(client, &wsa885x_regmap_cfg); -+ -+ if (IS_ERR(wsa885x->regmap)) -+ return PTR_ERR(wsa885x->regmap); -+ -+ ret = device_property_read_string(dev, "qcom,battery-config", -+ &battery_config); -+ if (ret) { -+ wsa885x->batt_conf = WSA885X_BATT_1S; -+ } else if (!strcmp(battery_config, "1s")) { -+ wsa885x->batt_conf = WSA885X_BATT_1S; -+ } else if (!strcmp(battery_config, "2s")) { -+ wsa885x->batt_conf = WSA885X_BATT_2S; -+ } else { -+ return dev_err_probe(dev, -EINVAL, -+ "Invalid battery config %s (expected 1s or 2s)\n", -+ battery_config); -+ } -+ -+ for (i = 0; i < ARRAY_SIZE(wsa885x_supply_name); i++) { -+ ret = devm_regulator_get_enable(dev, wsa885x_supply_name[i]); -+ if (ret) -+ return dev_err_probe(dev, ret, -+ "Failed to enable regulator %s\n", -+ wsa885x_supply_name[i]); -+ } -+ -+ ret = wsa885x_get_reset(dev, wsa885x); -+ if (ret) -+ return ret; -+ -+ wsa885x_reset_deassert(wsa885x); -+ usleep_range(5000, 5500); -+ -+ ret = devm_add_action_or_reset(dev, wsa885x_reset_assert, wsa885x); -+ if (ret) -+ return dev_err_probe(dev, ret, "devm_add_action_or_reset failed\n"); -+ -+ i2c_set_clientdata(client, wsa885x); -+ -+ ret = wsa885x_register_irq(wsa885x); -+ if (ret) -+ return dev_err_probe(dev, ret, "wsa885x irq registration failed\n"); -+ -+ ret = devm_snd_soc_register_component(dev, component_driver, -+ wsa885x_dai, -+ ARRAY_SIZE(wsa885x_dai)); -+ if (ret) -+ return dev_err_probe(dev, ret, "Codec component registration failed\n"); -+ -+ return 0; -+} -+ -+static const struct of_device_id wsa885x_dt_match[] = { -+ { -+ .compatible = "qcom,wsa8855", -+ }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, wsa885x_dt_match); -+ -+static const struct i2c_device_id wsa885x_id[] = { -+ { -+ .name = "wsa885x", -+ .driver_data = 0, -+ }, -+ {} -+}; -+MODULE_DEVICE_TABLE(i2c, wsa885x_id); -+ -+static struct i2c_driver wsa885x_driver = { -+ .driver = { -+ .name = "wsa885x", -+ .of_match_table = wsa885x_dt_match, -+ }, -+ .probe = wsa885x_probe, -+ .id_table = wsa885x_id, -+}; -+ -+module_i2c_driver(wsa885x_driver); -+ -+MODULE_DESCRIPTION("ASoC WSA885X Stereo Smart PA Codec Driver"); -+MODULE_LICENSE("GPL"); -diff --git a/sound/soc/codecs/zl38060.c b/sound/soc/codecs/zl38060.c ---- a/sound/soc/codecs/zl38060.c -+++ b/sound/soc/codecs/zl38060.c -@@ -162,7 +162,7 @@ static int zl38_fw_send_xaddr(struct regmap *regmap, const void *data) - static int zl38_load_firmware(struct device *dev, struct regmap *regmap) - { - const struct ihex_binrec *rec; -- const struct firmware *fw; -+ const struct firmware *fw __free(firmware) = NULL; - u32 addr; - u16 len; - int err; -@@ -180,7 +180,7 @@ static int zl38_load_firmware(struct device *dev, struct regmap *regmap) - return err; - err = zl38_fw_enter_boot_mode(regmap); - if (err) -- goto out; -+ return err; - rec = (const struct ihex_binrec *)fw->data; - while (rec) { - addr = be32_to_cpu(rec->addr); -@@ -195,15 +195,12 @@ static int zl38_load_firmware(struct device *dev, struct regmap *regmap) - err = -EINVAL; - } - if (err) -- goto out; -+ return err; - /* next ! */ - rec = ihex_next_binrec(rec); - } -- err = zl38_fw_go(regmap); - --out: -- release_firmware(fw); -- return err; -+ return zl38_fw_go(regmap); - } - - -diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c ---- a/sound/soc/dwc/dwc-i2s.c -+++ b/sound/soc/dwc/dwc-i2s.c -@@ -955,7 +955,9 @@ static int dw_i2s_probe(struct platform_device *pdev) - } - - irq = platform_get_irq_optional(pdev, 0); -- if (irq >= 0) { -+ if (irq == -EPROBE_DEFER) -+ return irq; -+ if (irq > 0) { - ret = devm_request_irq(&pdev->dev, irq, i2s_irq_handler, 0, - pdev->name, dev); - if (ret < 0) { diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c -@@ -69,7 +69,6 @@ static const struct snd_pcm_hw_constraint_list cs42888_channel_constraints = { - - /** - * struct codec_priv - CODEC private data -- * @mclk: Main clock of the CODEC - * @mclk_freq: Clock rate of MCLK - * @free_freq: Clock rate of MCLK for hw_free() - * @mclk_id: MCLK (or main clock) id for set_sysclk() -@@ -80,7 +79,6 @@ static const struct snd_pcm_hw_constraint_list cs42888_channel_constraints = { - * to stay within PLL frequency limits - */ - struct codec_priv { -- struct clk *mclk; - unsigned long mclk_freq; - unsigned long free_freq; - u32 mclk_id; -@@ -109,12 +107,58 @@ struct cpu_priv { - u32 slot_num; - }; - -+struct fsl_asoc_card_priv; -+ -+/* -+ * struct fsl_asoc_card_pdata - per-compatible static card description -+ * @sysclk_dir: initial CPU SYSCLK direction override (0 = leave default IN) -+ * @sysclk_ratio: SYSCLK ratio on sample rate (0 = not used) -+ * @slot_width: TDM slot width (0 = not TDM) -+ * @codec_dai_name: name of the codec DAI -+ * @codec_mclk_id: MCLK id passed to set_sysclk() for the codec -+ * @codec_fll_id: FLL id; only valid when has_pll is true -+ * @codec_pll_id: PLL id; only valid when has_pll is true -+ * @codec_pll_ratio_s24: PLL output ratio for S24_LE -+ * @has_pll: codec uses PLL/FLL; codec_fll_id and codec_pll_id are valid -+ * @dai_fmt: DAI format flags -+ * @playback_only: restrict card to playback direction -+ * @capture_only: restrict card to capture direction -+ * @dapm_routes: DAPM route table override -+ * @num_dapm_routes: number of entries in dapm_routes -+ * @exclude_format: PCM format bitmask excluded (for SAI + WM8960/WM8962) -+ * @codec_init: codec-specific init run after mclk_freq is populated -+ * @probe_init: optional DT-driven init run at end of probe() (e.g. SPDIF codec discovery) -+ */ -+struct fsl_asoc_card_pdata { -+ u32 sysclk_dir[2]; -+ u32 sysclk_ratio[2]; -+ u32 slot_width; -+ const char *codec_dai_name; -+ u32 codec_mclk_id; -+ int codec_fll_id; -+ int codec_pll_id; -+ int codec_pll_ratio_s24; -+ bool has_pll; -+ bool playback_only; -+ bool capture_only; -+ u32 dai_fmt; -+ const struct snd_soc_dapm_route *dapm_routes; -+ int num_dapm_routes; -+ u64 exclude_format; -+ int (*codec_init)(struct fsl_asoc_card_priv *priv); -+ int (*probe_init)(struct device_node *codec_np[], -+ struct device_node *cpu_np, -+ const char *codec_dai_name[], -+ struct fsl_asoc_card_priv *priv); -+}; -+ - /** - * struct fsl_asoc_card_priv - Freescale Generic ASOC card private data - * @dai_link: DAI link structure including normal one and DPCM link - * @hp_jack: Headphone Jack structure - * @mic_jack: Microphone Jack structure - * @pdev: platform device pointer -+ * @pdata: pointer to the per-compatible card platform data - * @codec_priv: CODEC private data - * @cpu_priv: CPU private data - * @card: ASoC card structure -@@ -135,6 +179,7 @@ struct fsl_asoc_card_priv { - struct simple_util_jack hp_jack; - struct simple_util_jack mic_jack; - struct platform_device *pdev; -+ const struct fsl_asoc_card_pdata *pdata; - struct codec_priv codec_priv[2]; - struct cpu_priv cpu_priv; - struct snd_soc_card card; -@@ -609,6 +654,192 @@ static int fsl_asoc_card_spdif_init(struct device_node *codec_np[], - return 0; - } - -+static int fsl_asoc_card_cs42888_codec_init(struct fsl_asoc_card_priv *priv) -+{ -+ unsigned long mclk_freq = priv->codec_priv[0].mclk_freq; -+ -+ /* -+ * Set CPU sysclk frequency from codec MCLK only if not already -+ * set by the CPU DAI init (e.g. ESAI extal clock takes precedence). -+ */ -+ if (!priv->cpu_priv.sysclk_freq[TX]) -+ priv->cpu_priv.sysclk_freq[TX] = mclk_freq; -+ if (!priv->cpu_priv.sysclk_freq[RX]) -+ priv->cpu_priv.sysclk_freq[RX] = mclk_freq; -+ -+ priv->constraint_channels = &cs42888_channel_constraints; -+ if (mclk_freq % 12288000 == 0) -+ priv->constraint_rates = &cs42888_rate_48k_constraints; -+ else if (mclk_freq % 11289600 == 0) -+ priv->constraint_rates = &cs42888_rate_44k_constraints; -+ else -+ dev_warn(&priv->pdev->dev, -+ "Unknown MCLK frequency %lu, no rate constraints\n", -+ mclk_freq); -+ -+ return 0; -+} -+ -+static int fsl_asoc_card_wm8958_codec_init(struct fsl_asoc_card_priv *priv) -+{ -+ priv->codec_priv[0].free_freq = priv->codec_priv[0].mclk_freq; -+ return 0; -+} -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_cs42888_pdata = { -+ .codec_dai_name = "cs42888", -+ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBC_CFC, -+ .sysclk_dir = { SND_SOC_CLOCK_OUT, SND_SOC_CLOCK_OUT }, -+ .slot_width = 32, -+ .dapm_routes = audio_map, -+ .num_dapm_routes = ARRAY_SIZE(audio_map), -+ .codec_init = fsl_asoc_card_cs42888_codec_init, -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_cs427x_pdata = { -+ .codec_dai_name = "cs4271-hifi", -+ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, -+ .codec_mclk_id = CS427x_SYSCLK_MCLK, -+ .dapm_routes = audio_map, -+ .num_dapm_routes = ARRAY_SIZE(audio_map), -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_sgtl5000_pdata = { -+ .codec_dai_name = "sgtl5000", -+ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, -+ .codec_mclk_id = SGTL5000_SYSCLK, -+ .dapm_routes = audio_map, -+ .num_dapm_routes = ARRAY_SIZE(audio_map), -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_tlv320aic32x4_pdata = { -+ .codec_dai_name = "tlv320aic32x4-hifi", -+ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, -+ .dapm_routes = audio_map, -+ .num_dapm_routes = ARRAY_SIZE(audio_map), -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_tlv320aic31xx_pdata = { -+ .codec_dai_name = "tlv320dac31xx-hifi", -+ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBC_CFC, -+ .sysclk_dir = { SND_SOC_CLOCK_OUT, SND_SOC_CLOCK_OUT }, -+ .playback_only = true, -+ .dapm_routes = audio_map_tx, -+ .num_dapm_routes = ARRAY_SIZE(audio_map_tx), -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_wm8962_pdata = { -+ .codec_dai_name = "wm8962", -+ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, -+ .codec_mclk_id = WM8962_SYSCLK_MCLK, -+ .has_pll = true, -+ .codec_fll_id = WM8962_SYSCLK_FLL, -+ .codec_pll_id = WM8962_FLL, -+ /* -+ * WM8962 has same BCLK generation limitations as WM8960. -+ * See WM8960 section for detailed explanation. -+ */ -+ .exclude_format = SNDRV_PCM_FMTBIT_S20_3LE, -+ .dapm_routes = audio_map, -+ .num_dapm_routes = ARRAY_SIZE(audio_map), -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_wm8960_pdata = { -+ .codec_dai_name = "wm8960-hifi", -+ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, -+ .has_pll = true, -+ .codec_fll_id = WM8960_SYSCLK_AUTO, -+ .codec_pll_id = WM8960_SYSCLK_AUTO, -+ /* -+ * WM8960 in master mode cannot generate exact 1.92 MHz BCLK -+ * required for S20_3LE (48kHz x 2ch x 20bit). Closest available -+ * is 2.048 MHz (SYSCLK/6), which causes right channel corruption. -+ * -+ * In SAI master mode, SAI derive BCLK from MCLK using integer -+ * dividers only. S20_3LE requires non-integer divider ratios -+ * with standard MCLK frequencies. For example, 48kHz stereo -+ * needs 1.920 MHz BCLK, which requires a divider of 6.4 from -+ * 12.288 MHz MCLK (not an integer). -+ */ -+ .exclude_format = SNDRV_PCM_FMTBIT_S20_3LE, -+ .dapm_routes = audio_map, -+ .num_dapm_routes = ARRAY_SIZE(audio_map), -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_ac97_pdata = { -+ .codec_dai_name = "ac97-hifi", -+ .dai_fmt = SND_SOC_DAIFMT_AC97, -+ .dapm_routes = audio_map_ac97, -+ .num_dapm_routes = ARRAY_SIZE(audio_map_ac97), -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_mqs_pdata = { -+ .codec_dai_name = "fsl-mqs-dai", -+ .dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBC_CFC | -+ SND_SOC_DAIFMT_NB_NF, -+ .playback_only = true, -+ .dapm_routes = audio_map_tx, -+ .num_dapm_routes = ARRAY_SIZE(audio_map_tx), -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_wm8524_pdata = { -+ .codec_dai_name = "wm8524-hifi", -+ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBC_CFC, -+ /* RX=0, TX=1: set TX (index 1) to CLOCK_OUT, RX stays at default IN */ -+ .sysclk_dir = { 0, SND_SOC_CLOCK_OUT }, -+ .sysclk_ratio = { 0, 256 }, -+ .slot_width = 32, -+ .playback_only = true, -+ .dapm_routes = audio_map_tx, -+ .num_dapm_routes = ARRAY_SIZE(audio_map_tx), -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_si476x_pdata = { -+ .codec_dai_name = "si476x-codec", -+ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBC_CFC, -+ .dapm_routes = audio_map_rx, -+ .num_dapm_routes = ARRAY_SIZE(audio_map_rx), -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_wm8958_pdata = { -+ .codec_dai_name = "wm8994-aif1", -+ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, -+ .codec_mclk_id = WM8994_FLL_SRC_MCLK1, -+ .has_pll = true, -+ .codec_fll_id = WM8994_SYSCLK_FLL1, -+ .codec_pll_id = WM8994_FLL1, -+ .codec_init = fsl_asoc_card_wm8958_codec_init, -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_nau8822_pdata = { -+ .codec_dai_name = "nau8822-hifi", -+ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, -+ .codec_mclk_id = NAU8822_CLK_MCLK, -+ .has_pll = true, -+ .codec_fll_id = NAU8822_CLK_PLL, -+ .codec_pll_id = NAU8822_CLK_PLL, -+ .dapm_routes = audio_map, -+ .num_dapm_routes = ARRAY_SIZE(audio_map), -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_wm8904_pdata = { -+ .codec_dai_name = "wm8904-hifi", -+ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, -+ .codec_mclk_id = WM8904_FLL_MCLK, -+ .has_pll = true, -+ .codec_fll_id = WM8904_CLK_FLL, -+ .codec_pll_id = WM8904_FLL_MCLK, -+ .codec_pll_ratio_s24 = 192, -+ .dapm_routes = audio_map, -+ .num_dapm_routes = ARRAY_SIZE(audio_map), -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_spdif_pdata = { -+ .codec_dai_name = "spdif", -+ .dai_fmt = DAI_FMT_BASE, -+ .probe_init = fsl_asoc_card_spdif_init, -+}; -+ - static int hp_jack_event(struct notifier_block *nb, unsigned long event, - void *data) - { -@@ -643,17 +874,173 @@ static struct notifier_block mic_jack_nb = { - .notifier_call = mic_jack_event, - }; - --static int fsl_asoc_card_late_probe(struct snd_soc_card *card) -+/* -+ * fsl_asoc_card_init_cpu - configure CPU DAI-specific settings. -+ * -+ * Called from late_probe() when the CPU DAI component is guaranteed bound. -+ */ -+static int fsl_asoc_card_init_cpu(struct snd_soc_card *card, -+ struct snd_soc_pcm_runtime *rtd) - { - struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); -- struct snd_soc_pcm_runtime *rtd = list_first_entry( -- &card->rtd_list, struct snd_soc_pcm_runtime, list); -+ struct device_node *np = priv->pdev->dev.of_node; -+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); -+ const char *comp_drv_name = cpu_dai->component->driver->name; -+ struct device *dev = card->dev; -+ int ret; -+ -+ if (!strcmp(comp_drv_name, "fsl-ssi")) { -+ /* Only SSI needs to configure AUDMUX */ -+ ret = fsl_asoc_card_audmux_init(np, priv); -+ if (ret) { -+ dev_err(dev, "failed to init audmux\n"); -+ return ret; -+ } -+ } else if (!strcmp(comp_drv_name, "fsl-esai")) { -+ struct clk *esai_clk = clk_get(cpu_dai->dev, "extal"); -+ -+ if (!IS_ERR(esai_clk)) { -+ priv->cpu_priv.sysclk_freq[TX] = clk_get_rate(esai_clk); -+ priv->cpu_priv.sysclk_freq[RX] = clk_get_rate(esai_clk); -+ clk_put(esai_clk); -+ } else { -+ dev_warn(dev, "failed to get ESAI extal clock: %ld\n", PTR_ERR(esai_clk)); -+ } -+ -+ priv->cpu_priv.sysclk_id[TX] = ESAI_HCKT_EXTAL; -+ priv->cpu_priv.sysclk_id[RX] = ESAI_HCKR_EXTAL; -+ } else if (!strcmp(comp_drv_name, "fsl-sai")) { -+ priv->cpu_priv.sysclk_id[TX] = FSL_SAI_CLK_MAST1; -+ priv->cpu_priv.sysclk_id[RX] = FSL_SAI_CLK_MAST1; -+ -+ if (priv->pdata->exclude_format) -+ priv->exclude_format = priv->pdata->exclude_format; -+ } -+ -+ return 0; -+} -+ -+/* -+ * fsl_asoc_card_init_codecs - read codec MCLK rates and set codec sysclk. -+ * -+ * Called from late_probe() after all components are bound. -+ */ -+static int fsl_asoc_card_init_codecs(struct snd_soc_card *card, -+ struct snd_soc_pcm_runtime *rtd) -+{ -+ struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); -+ const struct fsl_asoc_card_pdata *pdata = priv->pdata; - struct snd_soc_dai *codec_dai; - struct codec_priv *codec_priv; - struct device *dev = card->dev; - int codec_idx; - int ret; - -+ /* Read MCLK rate from each bound codec component */ -+ for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { -+ struct clk *codec_clk = clk_get(codec_dai->component->dev, NULL); -+ -+ codec_priv = &priv->codec_priv[codec_idx]; -+ if (!IS_ERR(codec_clk)) { -+ codec_priv->mclk_freq = clk_get_rate(codec_clk); -+ clk_put(codec_clk); -+ } -+ } -+ -+ if (pdata->codec_init) { -+ ret = pdata->codec_init(priv); -+ if (ret) -+ return ret; -+ } -+ -+ for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { -+ codec_priv = &priv->codec_priv[codec_idx]; -+ -+ ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, -+ codec_priv->mclk_freq, SND_SOC_CLOCK_IN); -+ if (ret && ret != -ENOTSUPP) { -+ dev_err(dev, "failed to set sysclk in %s\n", __func__); -+ return ret; -+ } -+ } -+ -+ return 0; -+} -+ -+static void fsl_asoc_card_free_jack(struct snd_soc_card *card) -+{ -+ struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); -+ -+ if (priv->hp_jack.gpio.desc) { -+ snd_soc_jack_notifier_unregister(&priv->hp_jack.jack, &hp_jack_nb); -+ snd_soc_jack_free_gpios(&priv->hp_jack.jack, 1, &priv->hp_jack.gpio); -+ priv->hp_jack.gpio.desc = NULL; -+ } -+ -+ if (priv->mic_jack.gpio.desc) { -+ snd_soc_jack_notifier_unregister(&priv->mic_jack.jack, &mic_jack_nb); -+ snd_soc_jack_free_gpios(&priv->mic_jack.jack, 1, &priv->mic_jack.gpio); -+ priv->mic_jack.gpio.desc = NULL; -+ } -+} -+ -+/* -+ * fsl_asoc_card_init_jack - register optional headphone and mic jacks. -+ * -+ * Called from late_probe() once per card bind cycle. -+ */ -+static int fsl_asoc_card_init_jack(struct snd_soc_card *card) -+{ -+ struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); -+ struct device_node *np = priv->pdev->dev.of_node; -+ int ret; -+ -+ /* -+ * Properties "hp-det-gpios" and "mic-det-gpios" are optional. -+ * simple_util_init_jack() checks for the GPIO property and -+ * does nothing if it is absent. -+ */ -+ if (of_property_present(np, "hp-det-gpios") || -+ of_property_present(np, "hp-det-gpio") /* deprecated */) { -+ ret = simple_util_init_jack(card, &priv->hp_jack, -+ 1, NULL, "Headphone Jack"); -+ if (ret) -+ return ret; -+ -+ snd_soc_jack_notifier_register(&priv->hp_jack.jack, &hp_jack_nb); -+ } -+ -+ if (of_property_present(np, "mic-det-gpios") || -+ of_property_present(np, "mic-det-gpio") /* deprecated */) { -+ ret = simple_util_init_jack(card, &priv->mic_jack, -+ 0, NULL, "Mic Jack"); -+ if (ret) -+ return ret; -+ -+ snd_soc_jack_notifier_register(&priv->mic_jack.jack, &mic_jack_nb); -+ } -+ -+ return 0; -+} -+ -+static int fsl_asoc_card_late_probe(struct snd_soc_card *card) -+{ -+ struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); -+ struct snd_soc_pcm_runtime *rtd; -+ int ret; -+ -+ /* Use the first rtd which carries the CPU+codec DAIs */ -+ rtd = list_first_entry(&card->rtd_list, -+ struct snd_soc_pcm_runtime, list); -+ -+ ret = fsl_asoc_card_init_jack(card); -+ if (ret) -+ goto jack_fail; -+ -+ ret = fsl_asoc_card_init_cpu(card, rtd); -+ if (ret) -+ goto jack_fail; -+ - if (fsl_asoc_card_is_ac97(priv)) { - #if IS_ENABLED(CONFIG_SND_AC97_CODEC) - struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component; -@@ -671,19 +1058,20 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card) - return 0; - } - -- for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { -- codec_priv = &priv->codec_priv[codec_idx]; -+ ret = fsl_asoc_card_init_codecs(card, rtd); -+ if (ret) -+ goto jack_fail; - -- ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, -- codec_priv->mclk_freq, SND_SOC_CLOCK_IN); -- if (ret && ret != -ENOTSUPP) { -- dev_err(dev, "failed to set sysclk in %s\n", __func__); -- return ret; -- } -+ return 0; - -- if (!IS_ERR_OR_NULL(codec_priv->mclk)) -- clk_prepare_enable(codec_priv->mclk); -- } -+jack_fail: -+ fsl_asoc_card_free_jack(card); -+ return ret; -+} -+ -+static int fsl_asoc_card_card_remove(struct snd_soc_card *card) -+{ -+ fsl_asoc_card_free_jack(card); - - return 0; - } -@@ -697,12 +1085,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) - struct platform_device *asrc_pdev = NULL; - struct device_node *bitclkprovider = NULL; - struct device_node *frameprovider = NULL; -- struct platform_device *cpu_pdev; - struct fsl_asoc_card_priv *priv; -- struct device *codec_dev[2] = { NULL, NULL }; -+ const struct fsl_asoc_card_pdata *pdata; - struct snd_soc_dai_link_component *dlc; -- const char *codec_dai_name[2]; -- const char *codec_dev_name[2]; -+ const char *codec_dai_name[2] = { NULL, NULL }; - u32 asrc_fmt = 0; - int codec_idx; - u32 width; -@@ -714,6 +1100,13 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) - - priv->pdev = pdev; - -+ pdata = of_device_get_match_data(&pdev->dev); -+ if (!pdata) { -+ dev_err(&pdev->dev, "unknown Device Tree compatible\n"); -+ return -EINVAL; -+ } -+ priv->pdata = pdata; -+ - cpu_np = of_parse_phandle(np, "audio-cpu", 0); - /* Give a chance to old DT bindings */ - if (!cpu_np) -@@ -726,52 +1119,13 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) - goto fail; - } - -- cpu_pdev = of_find_device_by_node(cpu_np); -- if (!cpu_pdev) { -- dev_err(&pdev->dev, "failed to find CPU DAI device\n"); -- ret = -EINVAL; -- goto fail; -- } -- - codec_np[0] = of_parse_phandle(np, "audio-codec", 0); - codec_np[1] = of_parse_phandle(np, "audio-codec", 1); - -- for (codec_idx = 0; codec_idx < 2; codec_idx++) { -- if (codec_np[codec_idx]) { -- struct platform_device *codec_pdev; -- struct i2c_client *codec_i2c; -- -- codec_i2c = of_find_i2c_device_by_node(codec_np[codec_idx]); -- if (codec_i2c) { -- codec_dev[codec_idx] = &codec_i2c->dev; -- codec_dev_name[codec_idx] = codec_i2c->name; -- } -- if (!codec_dev[codec_idx]) { -- codec_pdev = of_find_device_by_node(codec_np[codec_idx]); -- if (codec_pdev) { -- codec_dev[codec_idx] = &codec_pdev->dev; -- codec_dev_name[codec_idx] = codec_pdev->name; -- } -- } -- } -- } -- - asrc_np = of_parse_phandle(np, "audio-asrc", 0); - if (asrc_np) - asrc_pdev = of_find_device_by_node(asrc_np); - -- /* Get the MCLK rate only, and leave it controlled by CODEC drivers */ -- for (codec_idx = 0; codec_idx < 2; codec_idx++) { -- if (codec_dev[codec_idx]) { -- struct clk *codec_clk = clk_get(codec_dev[codec_idx], NULL); -- -- if (!IS_ERR(codec_clk)) { -- priv->codec_priv[codec_idx].mclk_freq = clk_get_rate(codec_clk); -- clk_put(codec_clk); -- } -- } -- } -- - /* Default sample rate and format, will be updated in hw_params() */ - priv->sample_rate = 44100; - priv->sample_format = SNDRV_PCM_FORMAT_S16_LE; -@@ -823,133 +1177,40 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) - } - - /* Diversify the card configurations */ -- if (of_device_is_compatible(np, "fsl,imx-audio-cs42888")) { -- codec_dai_name[0] = "cs42888"; -- priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv[0].mclk_freq; -- priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv[0].mclk_freq; -- priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; -- priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT; -- priv->cpu_priv.slot_width = 32; -- priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; -- priv->constraint_channels = &cs42888_channel_constraints; -- if (priv->codec_priv[0].mclk_freq % 12288000 == 0) -- priv->constraint_rates = &cs42888_rate_48k_constraints; -- else if (priv->codec_priv[0].mclk_freq % 11289600 == 0) -- priv->constraint_rates = &cs42888_rate_44k_constraints; -- else -- dev_warn(&pdev->dev, "Unknown MCLK frequency %lu, no rate constraints\n", -- priv->codec_priv[0].mclk_freq); -- } else if (of_device_is_compatible(np, "fsl,imx-audio-cs427x")) { -- codec_dai_name[0] = "cs4271-hifi"; -- priv->codec_priv[0].mclk_id = CS427x_SYSCLK_MCLK; -- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; -- } else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) { -- codec_dai_name[0] = "sgtl5000"; -- priv->codec_priv[0].mclk_id = SGTL5000_SYSCLK; -- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; -- } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic32x4")) { -- codec_dai_name[0] = "tlv320aic32x4-hifi"; -- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; -- } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic31xx")) { -- codec_dai_name[0] = "tlv320dac31xx-hifi"; -- priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; -+ priv->cpu_priv.sysclk_dir[TX] = pdata->sysclk_dir[TX]; -+ priv->cpu_priv.sysclk_dir[RX] = pdata->sysclk_dir[RX]; -+ priv->cpu_priv.sysclk_ratio[TX] = pdata->sysclk_ratio[TX]; -+ priv->cpu_priv.sysclk_ratio[RX] = pdata->sysclk_ratio[RX]; -+ priv->cpu_priv.slot_width = pdata->slot_width; -+ -+ codec_dai_name[0] = pdata->codec_dai_name; -+ priv->codec_priv[0].mclk_id = pdata->codec_mclk_id; -+ if (pdata->has_pll) { -+ priv->codec_priv[0].fll_id = pdata->codec_fll_id; -+ priv->codec_priv[0].pll_id = pdata->codec_pll_id; -+ } -+ if (pdata->codec_pll_ratio_s24) -+ priv->codec_priv[0].pll_ratio_s24 = pdata->codec_pll_ratio_s24; -+ -+ if (pdata->playback_only) { - priv->dai_link[1].playback_only = 1; - priv->dai_link[2].playback_only = 1; -- priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; -- priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT; -- priv->card.dapm_routes = audio_map_tx; -- priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); -- } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) { -- codec_dai_name[0] = "wm8962"; -- priv->codec_priv[0].mclk_id = WM8962_SYSCLK_MCLK; -- priv->codec_priv[0].fll_id = WM8962_SYSCLK_FLL; -- priv->codec_priv[0].pll_id = WM8962_FLL; -- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; -- /* -- * WM8962 has same BCLK generation limitations as WM8960. -- * See WM8960 section for detailed explanation. -- */ -- if (of_node_name_eq(cpu_np, "sai")) -- priv->exclude_format = SNDRV_PCM_FMTBIT_S20_3LE; -- } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8960")) { -- codec_dai_name[0] = "wm8960-hifi"; -- priv->codec_priv[0].fll_id = WM8960_SYSCLK_AUTO; -- priv->codec_priv[0].pll_id = WM8960_SYSCLK_AUTO; -- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; -- /* -- * WM8960 in master mode cannot generate exact 1.92 MHz BCLK -- * required for S20_3LE (48kHz × 2ch × 20bit). Closest available -- * is 2.048 MHz (SYSCLK/6), which causes right channel corruption. -- * -- * In SAI master mode, SAI derive BCLK from MCLK using integer -- * dividers only. S20_3LE requires non-integer divider ratios -- * with standard MCLK frequencies. For example, 48kHz stereo -- * needs 1.920 MHz BCLK, which requires a divider of 6.4 from -- * 12.288 MHz MCLK (not an integer). -- */ -- if (of_node_name_eq(cpu_np, "sai")) -- priv->exclude_format = SNDRV_PCM_FMTBIT_S20_3LE; -- } else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) { -- codec_dai_name[0] = "ac97-hifi"; -- priv->dai_fmt = SND_SOC_DAIFMT_AC97; -- priv->card.dapm_routes = audio_map_ac97; -- priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_ac97); -- } else if (of_device_is_compatible(np, "fsl,imx-audio-mqs")) { -- codec_dai_name[0] = "fsl-mqs-dai"; -- priv->dai_fmt = SND_SOC_DAIFMT_LEFT_J | -- SND_SOC_DAIFMT_CBC_CFC | -- SND_SOC_DAIFMT_NB_NF; -- priv->dai_link[1].playback_only = 1; -- priv->dai_link[2].playback_only = 1; -- priv->card.dapm_routes = audio_map_tx; -- priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); -- } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8524")) { -- codec_dai_name[0] = "wm8524-hifi"; -- priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; -- priv->dai_link[1].playback_only = 1; -- priv->dai_link[2].playback_only = 1; -- priv->cpu_priv.slot_width = 32; -- priv->card.dapm_routes = audio_map_tx; -- priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); -- priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; -- priv->cpu_priv.sysclk_ratio[TX] = 256; -- } else if (of_device_is_compatible(np, "fsl,imx-audio-si476x")) { -- codec_dai_name[0] = "si476x-codec"; -- priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; -- priv->card.dapm_routes = audio_map_rx; -- priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_rx); -- } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8958")) { -- codec_dai_name[0] = "wm8994-aif1"; -- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; -- priv->codec_priv[0].mclk_id = WM8994_FLL_SRC_MCLK1; -- priv->codec_priv[0].fll_id = WM8994_SYSCLK_FLL1; -- priv->codec_priv[0].pll_id = WM8994_FLL1; -- priv->codec_priv[0].free_freq = priv->codec_priv[0].mclk_freq; -- priv->card.dapm_routes = NULL; -- priv->card.num_dapm_routes = 0; -- } else if (of_device_is_compatible(np, "fsl,imx-audio-nau8822")) { -- codec_dai_name[0] = "nau8822-hifi"; -- priv->codec_priv[0].mclk_id = NAU8822_CLK_MCLK; -- priv->codec_priv[0].fll_id = NAU8822_CLK_PLL; -- priv->codec_priv[0].pll_id = NAU8822_CLK_PLL; -- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; -- if (codec_dev[0]) -- priv->codec_priv[0].mclk = devm_clk_get(codec_dev[0], NULL); -- } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8904")) { -- codec_dai_name[0] = "wm8904-hifi"; -- priv->codec_priv[0].mclk_id = WM8904_FLL_MCLK; -- priv->codec_priv[0].fll_id = WM8904_CLK_FLL; -- priv->codec_priv[0].pll_id = WM8904_FLL_MCLK; -- priv->codec_priv[0].pll_ratio_s24 = 192; -- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; -- } else if (of_device_is_compatible(np, "fsl,imx-audio-spdif")) { -- ret = fsl_asoc_card_spdif_init(codec_np, cpu_np, codec_dai_name, priv); -+ } -+ if (pdata->capture_only) { -+ priv->dai_link[1].capture_only = 1; -+ priv->dai_link[2].capture_only = 1; -+ } -+ -+ priv->dai_fmt = pdata->dai_fmt; -+ -+ priv->card.dapm_routes = pdata->dapm_routes; -+ priv->card.num_dapm_routes = pdata->num_dapm_routes; -+ -+ if (pdata->probe_init) { -+ ret = pdata->probe_init(codec_np, cpu_np, -+ codec_dai_name, priv); - if (ret) - goto asrc_fail; -- } else { -- dev_err(&pdev->dev, "unknown Device Tree compatible\n"); -- ret = -EINVAL; -- goto asrc_fail; - } - - /* -@@ -995,51 +1256,21 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) - of_node_put(bitclkprovider); - of_node_put(frameprovider); - -- if (!fsl_asoc_card_is_ac97(priv) && !codec_dev[0] -- && codec_dai_name[0] != snd_soc_dummy_dlc.dai_name) { -- dev_dbg(&pdev->dev, "failed to find codec device\n"); -- ret = -EPROBE_DEFER; -- goto asrc_fail; -- } -- -- /* Common settings for corresponding Freescale CPU DAI driver */ -- if (of_node_name_eq(cpu_np, "ssi")) { -- /* Only SSI needs to configure AUDMUX */ -- ret = fsl_asoc_card_audmux_init(np, priv); -- if (ret) { -- dev_err(&pdev->dev, "failed to init audmux\n"); -- goto asrc_fail; -- } -- } else if (of_node_name_eq(cpu_np, "esai")) { -- struct clk *esai_clk = clk_get(&cpu_pdev->dev, "extal"); -- -- if (!IS_ERR(esai_clk)) { -- priv->cpu_priv.sysclk_freq[TX] = clk_get_rate(esai_clk); -- priv->cpu_priv.sysclk_freq[RX] = clk_get_rate(esai_clk); -- clk_put(esai_clk); -- } else if (PTR_ERR(esai_clk) == -EPROBE_DEFER) { -- ret = -EPROBE_DEFER; -- goto asrc_fail; -- } -- -- priv->cpu_priv.sysclk_id[1] = ESAI_HCKT_EXTAL; -- priv->cpu_priv.sysclk_id[0] = ESAI_HCKR_EXTAL; -- } else if (of_node_name_eq(cpu_np, "sai")) { -- priv->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1; -- priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1; -- } -- - /* Initialize sound card */ - priv->card.dev = &pdev->dev; - priv->card.owner = THIS_MODULE; - ret = snd_soc_of_parse_card_name(&priv->card, "model"); - if (ret) { -- snprintf(priv->name, sizeof(priv->name), "%s-audio", -- fsl_asoc_card_is_ac97(priv) ? "ac97" : codec_dev_name[0]); -- priv->card.name = priv->name; -+ /* -+ * "model" is required by the DT binding. Enforce it here so -+ * the driver fails with a clear message. -+ */ -+ dev_err(&pdev->dev, "Error parsing card name: %d\n", ret); -+ goto asrc_fail; - } - priv->card.dai_link = priv->dai_link; - priv->card.late_probe = fsl_asoc_card_late_probe; -+ priv->card.remove = fsl_asoc_card_card_remove; - priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets; - priv->card.num_dapm_widgets = ARRAY_SIZE(fsl_asoc_card_dapm_widgets); - -@@ -1118,7 +1349,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) +@@ -1118,7 +1118,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) } ret = of_property_read_u32(asrc_np, "fsl,asrc-format", &asrc_fmt); @@ -34783,129 +11240,10 @@ diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c if (ret) { /* Fallback to old binding; translate to asrc_format */ ret = of_property_read_u32(asrc_np, "fsl,asrc-width", -@@ -1146,39 +1377,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) - goto asrc_fail; - } - -- /* -- * Properties "hp-det-gpios" and "mic-det-gpios" are optional, and -- * simple_util_init_jack() uses these properties for creating -- * Headphone Jack and Microphone Jack. -- * -- * The notifier is initialized in snd_soc_card_jack_new(), then -- * snd_soc_jack_notifier_register can be called. -- */ -- if (of_property_present(np, "hp-det-gpios") || -- of_property_present(np, "hp-det-gpio") /* deprecated */) { -- ret = simple_util_init_jack(&priv->card, &priv->hp_jack, -- 1, NULL, "Headphone Jack"); -- if (ret) -- goto asrc_fail; -- -- snd_soc_jack_notifier_register(&priv->hp_jack.jack, &hp_jack_nb); -- } -- -- if (of_property_present(np, "mic-det-gpios") || -- of_property_present(np, "mic-det-gpio") /* deprecated */) { -- ret = simple_util_init_jack(&priv->card, &priv->mic_jack, -- 0, NULL, "Mic Jack"); -- if (ret) -- goto asrc_fail; -- -- snd_soc_jack_notifier_register(&priv->mic_jack.jack, &mic_jack_nb); -- } -- - asrc_fail: - of_node_put(asrc_np); - of_node_put(codec_np[0]); - of_node_put(codec_np[1]); -- put_device(&cpu_pdev->dev); - fail: - of_node_put(cpu_np); - -@@ -1186,21 +1388,21 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) - } - - static const struct of_device_id fsl_asoc_card_dt_ids[] = { -- { .compatible = "fsl,imx-audio-ac97", }, -- { .compatible = "fsl,imx-audio-cs42888", }, -- { .compatible = "fsl,imx-audio-cs427x", }, -- { .compatible = "fsl,imx-audio-tlv320aic32x4", }, -- { .compatible = "fsl,imx-audio-tlv320aic31xx", }, -- { .compatible = "fsl,imx-audio-sgtl5000", }, -- { .compatible = "fsl,imx-audio-wm8962", }, -- { .compatible = "fsl,imx-audio-wm8960", }, -- { .compatible = "fsl,imx-audio-mqs", }, -- { .compatible = "fsl,imx-audio-wm8524", }, -- { .compatible = "fsl,imx-audio-si476x", }, -- { .compatible = "fsl,imx-audio-wm8958", }, -- { .compatible = "fsl,imx-audio-nau8822", }, -- { .compatible = "fsl,imx-audio-wm8904", }, -- { .compatible = "fsl,imx-audio-spdif", }, -+ { .compatible = "fsl,imx-audio-ac97", .data = &fsl_asoc_ac97_pdata }, -+ { .compatible = "fsl,imx-audio-cs42888", .data = &fsl_asoc_cs42888_pdata }, -+ { .compatible = "fsl,imx-audio-cs427x", .data = &fsl_asoc_cs427x_pdata }, -+ { .compatible = "fsl,imx-audio-tlv320aic32x4", .data = &fsl_asoc_tlv320aic32x4_pdata }, -+ { .compatible = "fsl,imx-audio-tlv320aic31xx", .data = &fsl_asoc_tlv320aic31xx_pdata }, -+ { .compatible = "fsl,imx-audio-sgtl5000", .data = &fsl_asoc_sgtl5000_pdata }, -+ { .compatible = "fsl,imx-audio-wm8962", .data = &fsl_asoc_wm8962_pdata }, -+ { .compatible = "fsl,imx-audio-wm8960", .data = &fsl_asoc_wm8960_pdata }, -+ { .compatible = "fsl,imx-audio-mqs", .data = &fsl_asoc_mqs_pdata }, -+ { .compatible = "fsl,imx-audio-wm8524", .data = &fsl_asoc_wm8524_pdata }, -+ { .compatible = "fsl,imx-audio-si476x", .data = &fsl_asoc_si476x_pdata }, -+ { .compatible = "fsl,imx-audio-wm8958", .data = &fsl_asoc_wm8958_pdata }, -+ { .compatible = "fsl,imx-audio-nau8822", .data = &fsl_asoc_nau8822_pdata }, -+ { .compatible = "fsl,imx-audio-wm8904", .data = &fsl_asoc_wm8904_pdata }, -+ { .compatible = "fsl,imx-audio-spdif", .data = &fsl_asoc_spdif_pdata }, - {} - }; - MODULE_DEVICE_TABLE(of, fsl_asoc_card_dt_ids); diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c -@@ -222,10 +222,9 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair) - enum asrc_pair_index index = ASRC_INVALID_PAIR; - struct fsl_asrc *asrc = pair->asrc; - struct device *dev = &asrc->pdev->dev; -- unsigned long lock_flags; - int i, ret = 0; - -- spin_lock_irqsave(&asrc->lock, lock_flags); -+ guard(spinlock_irqsave)(&asrc->lock); - - for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) { - if (asrc->pair[i] != NULL) -@@ -250,8 +249,6 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair) - pair->index = index; - } - -- spin_unlock_irqrestore(&asrc->lock, lock_flags); -- - return ret; - } - -@@ -265,19 +262,16 @@ static void fsl_asrc_release_pair(struct fsl_asrc_pair *pair) - { - struct fsl_asrc *asrc = pair->asrc; - enum asrc_pair_index index = pair->index; -- unsigned long lock_flags; - - /* Make sure the pair is disabled */ - regmap_update_bits(asrc->regmap, REG_ASRCTR, - ASRCTR_ASRCEi_MASK(index), 0); - -- spin_lock_irqsave(&asrc->lock, lock_flags); -+ guard(spinlock_irqsave)(&asrc->lock); - - asrc->channel_avail += pair->channels; - asrc->pair[index] = NULL; - pair->error = 0; -- -- spin_unlock_irqrestore(&asrc->lock, lock_flags); - } - - /** -@@ -1358,7 +1352,7 @@ static int fsl_asrc_probe(struct platform_device *pdev) +@@ -1358,7 +1358,7 @@ static int fsl_asrc_probe(struct platform_device *pdev) } ret = of_property_read_u32(np, "fsl,asrc-format", &asrc_fmt); @@ -34914,50 +11252,6 @@ diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c if (ret) { ret = of_property_read_u32(np, "fsl,asrc-width", &width); if (ret) { -diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h ---- a/sound/soc/fsl/fsl_asrc.h -+++ b/sound/soc/fsl/fsl_asrc.h -@@ -444,7 +444,7 @@ struct dma_block { - }; - - /** -- * fsl_asrc_soc_data: soc specific data -+ * struct fsl_asrc_soc_data - soc specific data - * - * @use_edma: using edma as dma device or not - * @channel_bits: width of ASRCNCR register for each pair -@@ -457,7 +457,7 @@ struct fsl_asrc_soc_data { - }; - - /** -- * fsl_asrc_pair_priv: ASRC Pair private data -+ * struct fsl_asrc_pair_priv - ASRC Pair private data - * - * @config: configuration profile - */ -@@ -466,7 +466,7 @@ struct fsl_asrc_pair_priv { - }; - - /** -- * fsl_asrc_priv: ASRC private data -+ * struct fsl_asrc_priv - ASRC private data - * - * @asrck_clk: clock sources to driver ASRC internal logic - * @soc: soc specific data -diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c ---- a/sound/soc/fsl/fsl_asrc_dma.c -+++ b/sound/soc/fsl/fsl_asrc_dma.c -@@ -219,7 +219,9 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component, - */ - component_be = snd_soc_lookup_component_nolocked(dev_be, SND_DMAENGINE_PCM_DRV_NAME); - if (component_be) { -- be_chan = soc_component_to_pcm(component_be)->chan[substream->stream]; -+ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component_be); -+ -+ be_chan = pcm->chan[substream->stream]; - tmp_chan = be_chan; - } - if (!tmp_chan) { diff --git a/sound/soc/fsl/fsl_asrc_m2m.c b/sound/soc/fsl/fsl_asrc_m2m.c --- a/sound/soc/fsl/fsl_asrc_m2m.c +++ b/sound/soc/fsl/fsl_asrc_m2m.c @@ -34988,307 +11282,10 @@ diff --git a/sound/soc/fsl/fsl_asrc_m2m.c b/sound/soc/fsl/fsl_asrc_m2m.c codec->descriptor[j].pcm_formats = cap.fmt_out; codec->descriptor[j].src.out_sample_rate_min = cap.rate_out[0]; codec->descriptor[j].src.out_sample_rate_max = -diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c ---- a/sound/soc/fsl/fsl_audmix.c -+++ b/sound/soc/fsl/fsl_audmix.c -@@ -286,7 +286,6 @@ static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) - { - struct fsl_audmix *priv = snd_soc_dai_get_drvdata(dai); -- unsigned long lock_flags; - - /* Capture stream shall not be handled */ - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) -@@ -296,16 +295,14 @@ static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd, - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: -- spin_lock_irqsave(&priv->lock, lock_flags); -- priv->tdms |= BIT(dai->driver->id); -- spin_unlock_irqrestore(&priv->lock, lock_flags); -+ scoped_guard(spinlock_irqsave, &priv->lock) -+ priv->tdms |= BIT(dai->driver->id); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: -- spin_lock_irqsave(&priv->lock, lock_flags); -- priv->tdms &= ~BIT(dai->driver->id); -- spin_unlock_irqrestore(&priv->lock, lock_flags); -+ scoped_guard(spinlock_irqsave, &priv->lock) -+ priv->tdms &= ~BIT(dai->driver->id); - break; - default: - return -EINVAL; -@@ -457,6 +454,9 @@ static const struct of_device_id fsl_audmix_ids[] = { - }; - MODULE_DEVICE_TABLE(of, fsl_audmix_ids); - -+static int fsl_audmix_runtime_resume(struct device *dev); -+static int fsl_audmix_runtime_suspend(struct device *dev); -+ - static int fsl_audmix_probe(struct platform_device *pdev) - { - struct device *dev = &pdev->dev; -@@ -488,13 +488,25 @@ static int fsl_audmix_probe(struct platform_device *pdev) - spin_lock_init(&priv->lock); - platform_set_drvdata(pdev, priv); - pm_runtime_enable(dev); -+ if (!pm_runtime_enabled(dev)) { -+ ret = fsl_audmix_runtime_resume(dev); -+ if (ret) -+ goto err_disable_pm; -+ } -+ -+ ret = pm_runtime_resume_and_get(dev); -+ if (ret < 0) -+ goto err_pm_get_sync; -+ -+ /* To enable regmap cache only when runtime PM enabled */ -+ pm_runtime_put(dev); - - ret = devm_snd_soc_register_component(dev, &fsl_audmix_component, - fsl_audmix_dai, - ARRAY_SIZE(fsl_audmix_dai)); - if (ret) { - dev_err(dev, "failed to register ASoC DAI\n"); -- goto err_disable_pm; -+ goto err_pm_get_sync; - } - - /* -@@ -506,12 +518,15 @@ static int fsl_audmix_probe(struct platform_device *pdev) - if (IS_ERR(priv->pdev)) { - ret = PTR_ERR(priv->pdev); - dev_err(dev, "failed to register platform: %d\n", ret); -- goto err_disable_pm; -+ goto err_pm_get_sync; - } - } - - return 0; - -+err_pm_get_sync: -+ if (!pm_runtime_status_suspended(dev)) -+ fsl_audmix_runtime_suspend(dev); - err_disable_pm: - pm_runtime_disable(dev); - return ret; -@@ -522,6 +537,8 @@ static void fsl_audmix_remove(struct platform_device *pdev) - struct fsl_audmix *priv = dev_get_drvdata(&pdev->dev); - - pm_runtime_disable(&pdev->dev); -+ if (!pm_runtime_status_suspended(&pdev->dev)) -+ fsl_audmix_runtime_suspend(&pdev->dev); - - if (priv->pdev) - platform_device_unregister(priv->pdev); -diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c ---- a/sound/soc/fsl/fsl_dma.c -+++ b/sound/soc/fsl/fsl_dma.c -@@ -18,8 +18,6 @@ - #include - #include - #include --#include --#include - #include - #include - -@@ -824,9 +822,34 @@ static int fsl_soc_dma_probe(struct platform_device *pdev) - struct device_node *np = pdev->dev.of_node; - struct device_node *ssi_np; - struct resource res; -+ void __iomem *channel; - const uint32_t *iprop; -+ int irq; - int ret; - -+ channel = devm_platform_ioremap_resource(pdev, 0); -+ if (IS_ERR(channel)) -+ return PTR_ERR(channel); -+ -+ irq = platform_get_irq(pdev, 0); -+ if (irq < 0) -+ return irq; -+ -+ dma = devm_kzalloc(&pdev->dev, sizeof(*dma), GFP_KERNEL); -+ if (!dma) -+ return -ENOMEM; -+ -+ dma->dai.name = DRV_NAME; -+ dma->dai.open = fsl_dma_open; -+ dma->dai.close = fsl_dma_close; -+ dma->dai.hw_params = fsl_dma_hw_params; -+ dma->dai.hw_free = fsl_dma_hw_free; -+ dma->dai.pointer = fsl_dma_pointer; -+ dma->dai.pcm_new = fsl_dma_new; -+ -+ dma->channel = channel; -+ dma->irq = irq; -+ - /* Find the SSI node that points to us. */ - ssi_np = find_ssi_node(np); - if (!ssi_np) { -@@ -842,55 +865,19 @@ static int fsl_soc_dma_probe(struct platform_device *pdev) - return ret; - } - -- dma = kzalloc_obj(*dma); -- if (!dma) { -- of_node_put(ssi_np); -- return -ENOMEM; -- } -- -- dma->dai.name = DRV_NAME; -- dma->dai.open = fsl_dma_open; -- dma->dai.close = fsl_dma_close; -- dma->dai.hw_params = fsl_dma_hw_params; -- dma->dai.hw_free = fsl_dma_hw_free; -- dma->dai.pointer = fsl_dma_pointer; -- dma->dai.pcm_new = fsl_dma_new; -- - /* Store the SSI-specific information that we need */ - dma->ssi_stx_phys = res.start + REG_SSI_STX0; - dma->ssi_srx_phys = res.start + REG_SSI_SRX0; - - iprop = of_get_property(ssi_np, "fsl,fifo-depth", NULL); -+ of_node_put(ssi_np); - if (iprop) - dma->ssi_fifo_depth = be32_to_cpup(iprop); - else - /* Older 8610 DTs didn't have the fifo-depth property */ - dma->ssi_fifo_depth = 8; - -- of_node_put(ssi_np); -- -- ret = devm_snd_soc_register_component(&pdev->dev, &dma->dai, NULL, 0); -- if (ret) { -- dev_err(&pdev->dev, "could not register platform\n"); -- kfree(dma); -- return ret; -- } -- -- dma->channel = of_iomap(np, 0); -- dma->irq = irq_of_parse_and_map(np, 0); -- -- dev_set_drvdata(&pdev->dev, dma); -- -- return 0; --} -- --static void fsl_soc_dma_remove(struct platform_device *pdev) --{ -- struct dma_object *dma = dev_get_drvdata(&pdev->dev); -- -- iounmap(dma->channel); -- irq_dispose_mapping(dma->irq); -- kfree(dma); -+ return devm_snd_soc_register_component(&pdev->dev, &dma->dai, NULL, 0); - } - - static const struct of_device_id fsl_soc_dma_ids[] = { -@@ -905,7 +892,6 @@ static struct platform_driver fsl_soc_dma_driver = { - .of_match_table = fsl_soc_dma_ids, - }, - .probe = fsl_soc_dma_probe, -- .remove = fsl_soc_dma_remove, - }; - - module_platform_driver(fsl_soc_dma_driver); -diff --git a/sound/soc/fsl/fsl_dma.h b/sound/soc/fsl/fsl_dma.h ---- a/sound/soc/fsl/fsl_dma.h -+++ b/sound/soc/fsl/fsl_dma.h -@@ -92,7 +92,7 @@ static inline u32 CCSR_DMA_ECLNDAR_ADDR(u64 x) - #define CCSR_DMA_ATR_SNOOP 0x00050000 - #define CCSR_DMA_ATR_ESAD_MASK 0x0000000F - --/** -+/* - * List Descriptor for extended chaining mode DMA operations. - * - * The CLSDAR register points to the first (in a linked-list) List -@@ -107,7 +107,7 @@ struct fsl_dma_list_descriptor { - u8 res[8]; /* Reserved */ - } __attribute__ ((aligned(32), packed)); - --/** -+/* - * Link Descriptor for basic and extended chaining mode DMA operations. - * - * A Link Descriptor points to a single DMA buffer. Each link descriptor diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c --- a/sound/soc/fsl/fsl_easrc.c +++ b/sound/soc/fsl/fsl_easrc.c -@@ -1025,7 +1025,6 @@ static int fsl_easrc_config_context(struct fsl_asrc *easrc, unsigned int ctx_id) - struct fsl_easrc_ctx_priv *ctx_priv; - struct fsl_asrc_pair *ctx; - struct device *dev; -- unsigned long lock_flags; - int ret; - - if (!easrc) -@@ -1053,9 +1052,8 @@ static int fsl_easrc_config_context(struct fsl_asrc *easrc, unsigned int ctx_id) - if (ret) - return ret; - -- spin_lock_irqsave(&easrc->lock, lock_flags); -- ret = fsl_easrc_config_slot(easrc, ctx->index); -- spin_unlock_irqrestore(&easrc->lock, lock_flags); -+ scoped_guard(spinlock_irqsave, &easrc->lock) -+ ret = fsl_easrc_config_slot(easrc, ctx->index); - if (ret) - return ret; - -@@ -1301,13 +1299,12 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx) - enum asrc_pair_index index = ASRC_INVALID_PAIR; - struct fsl_asrc *easrc = ctx->asrc; - struct device *dev; -- unsigned long lock_flags; - int ret = 0; - int i; - - dev = &easrc->pdev->dev; - -- spin_lock_irqsave(&easrc->lock, lock_flags); -+ guard(spinlock_irqsave)(&easrc->lock); - - for (i = ASRC_PAIR_A; i < EASRC_CTX_MAX_NUM; i++) { - if (easrc->pair[i]) -@@ -1331,8 +1328,6 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx) - easrc->channel_avail -= channels; - } - -- spin_unlock_irqrestore(&easrc->lock, lock_flags); -- - return ret; - } - -@@ -1343,7 +1338,6 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx) - */ - static void fsl_easrc_release_context(struct fsl_asrc_pair *ctx) - { -- unsigned long lock_flags; - struct fsl_asrc *easrc; - - if (!ctx) -@@ -1351,14 +1345,12 @@ static void fsl_easrc_release_context(struct fsl_asrc_pair *ctx) - - easrc = ctx->asrc; - -- spin_lock_irqsave(&easrc->lock, lock_flags); -+ guard(spinlock_irqsave)(&easrc->lock); - - fsl_easrc_release_slot(easrc, ctx->index); - - easrc->channel_avail += ctx->channels; - easrc->pair[ctx->index] = NULL; -- -- spin_unlock_irqrestore(&easrc->lock, lock_flags); - } - - /* -@@ -2227,7 +2219,7 @@ static int fsl_easrc_probe(struct platform_device *pdev) +@@ -2227,7 +2227,7 @@ static int fsl_easrc_probe(struct platform_device *pdev) } ret = of_property_read_u32(np, "fsl,asrc-format", &asrc_fmt); @@ -35297,134 +11294,6 @@ diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c if (ret) { dev_err(dev, "failed to asrc format\n"); return ret; -@@ -2292,15 +2284,13 @@ static int fsl_easrc_runtime_suspend(struct device *dev) - { - struct fsl_asrc *easrc = dev_get_drvdata(dev); - struct fsl_easrc_priv *easrc_priv = easrc->private; -- unsigned long lock_flags; - - regcache_cache_only(easrc->regmap, true); - - clk_disable_unprepare(easrc->mem_clk); - -- spin_lock_irqsave(&easrc->lock, lock_flags); -- easrc_priv->firmware_loaded = 0; -- spin_unlock_irqrestore(&easrc->lock, lock_flags); -+ scoped_guard(spinlock_irqsave, &easrc->lock) -+ easrc_priv->firmware_loaded = 0; - - return 0; - } -@@ -2311,7 +2301,6 @@ static int fsl_easrc_runtime_resume(struct device *dev) - struct fsl_easrc_priv *easrc_priv = easrc->private; - struct fsl_easrc_ctx_priv *ctx_priv; - struct fsl_asrc_pair *ctx; -- unsigned long lock_flags; - int ret; - int i; - -@@ -2323,13 +2312,11 @@ static int fsl_easrc_runtime_resume(struct device *dev) - regcache_mark_dirty(easrc->regmap); - regcache_sync(easrc->regmap); - -- spin_lock_irqsave(&easrc->lock, lock_flags); -- if (easrc_priv->firmware_loaded) { -- spin_unlock_irqrestore(&easrc->lock, lock_flags); -- goto skip_load; -+ scoped_guard(spinlock_irqsave, &easrc->lock) { -+ if (easrc_priv->firmware_loaded) -+ return 0; -+ easrc_priv->firmware_loaded = 1; - } -- easrc_priv->firmware_loaded = 1; -- spin_unlock_irqrestore(&easrc->lock, lock_flags); - - ret = fsl_easrc_get_firmware(easrc); - if (ret) { -@@ -2377,9 +2364,7 @@ static int fsl_easrc_runtime_resume(struct device *dev) - goto disable_mem_clk; - } - --skip_load: - return 0; -- - disable_mem_clk: - clk_disable_unprepare(easrc->mem_clk); - return ret; -diff --git a/sound/soc/fsl/fsl_easrc.h b/sound/soc/fsl/fsl_easrc.h ---- a/sound/soc/fsl/fsl_easrc.h -+++ b/sound/soc/fsl/fsl_easrc.h -@@ -584,7 +584,7 @@ struct fsl_easrc_slot { - }; - - /** -- * fsl_easrc_ctx_priv: EASRC context private data -+ * struct fsl_easrc_ctx_priv - EASRC context private data - * - * @in_params: input parameter - * @out_params: output parameter -@@ -625,7 +625,7 @@ struct fsl_easrc_ctx_priv { - }; - - /** -- * fsl_easrc_priv: EASRC private data -+ * struct fsl_easrc_priv - EASRC private data - * - * @slot: slot setting - * @firmware_hdr: the header of firmware -diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c ---- a/sound/soc/fsl/fsl_esai.c -+++ b/sound/soc/fsl/fsl_esai.c -@@ -709,10 +709,9 @@ static void fsl_esai_hw_reset(struct work_struct *work) - { - struct fsl_esai *esai_priv = container_of(work, struct fsl_esai, work); - bool tx = true, rx = false, enabled[2]; -- unsigned long lock_flags; - u32 tfcr, rfcr; - -- spin_lock_irqsave(&esai_priv->lock, lock_flags); -+ guard(spinlock_irqsave)(&esai_priv->lock); - /* Save the registers */ - regmap_read(esai_priv->regmap, REG_ESAI_TFCR, &tfcr); - regmap_read(esai_priv->regmap, REG_ESAI_RFCR, &rfcr); -@@ -750,8 +749,6 @@ static void fsl_esai_hw_reset(struct work_struct *work) - fsl_esai_trigger_start(esai_priv, tx); - if (enabled[rx]) - fsl_esai_trigger_start(esai_priv, rx); -- -- spin_unlock_irqrestore(&esai_priv->lock, lock_flags); - } - - static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, -@@ -759,7 +756,6 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, - { - struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); - bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; -- unsigned long lock_flags; - - esai_priv->channels[tx] = substream->runtime->channels; - -@@ -767,16 +763,14 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: -- spin_lock_irqsave(&esai_priv->lock, lock_flags); -- fsl_esai_trigger_start(esai_priv, tx); -- spin_unlock_irqrestore(&esai_priv->lock, lock_flags); -+ scoped_guard(spinlock_irqsave, &esai_priv->lock) -+ fsl_esai_trigger_start(esai_priv, tx); - break; - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: -- spin_lock_irqsave(&esai_priv->lock, lock_flags); -- fsl_esai_trigger_stop(esai_priv, tx); -- spin_unlock_irqrestore(&esai_priv->lock, lock_flags); -+ scoped_guard(spinlock_irqsave, &esai_priv->lock) -+ fsl_esai_trigger_stop(esai_priv, tx); - break; - default: - return -EINVAL; diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c --- a/sound/soc/fsl/fsl_qmc_audio.c +++ b/sound/soc/fsl/fsl_qmc_audio.c @@ -35450,318 +11319,6 @@ diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c ret = snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_ACCESS, access); if (ret) { -diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c ---- a/sound/soc/fsl/fsl_spdif.c -+++ b/sound/soc/fsl/fsl_spdif.c -@@ -853,17 +853,15 @@ static int fsl_spdif_subcode_get(struct snd_kcontrol *kcontrol, - struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); - struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); - struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; -- unsigned long flags; - int ret = -EAGAIN; - -- spin_lock_irqsave(&ctrl->ctl_lock, flags); -+ guard(spinlock_irqsave)(&ctrl->ctl_lock); - if (ctrl->ready_buf) { - int idx = (ctrl->ready_buf - 1) * SPDIF_UBITS_SIZE; - memcpy(&ucontrol->value.iec958.subcode[0], - &ctrl->subcode[idx], SPDIF_UBITS_SIZE); - ret = 0; - } -- spin_unlock_irqrestore(&ctrl->ctl_lock, flags); - - return ret; - } -@@ -885,17 +883,15 @@ static int fsl_spdif_qget(struct snd_kcontrol *kcontrol, - struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); - struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); - struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; -- unsigned long flags; - int ret = -EAGAIN; - -- spin_lock_irqsave(&ctrl->ctl_lock, flags); -+ guard(spinlock_irqsave)(&ctrl->ctl_lock); - if (ctrl->ready_buf) { - int idx = (ctrl->ready_buf - 1) * SPDIF_QSUB_SIZE; - memcpy(&ucontrol->value.bytes.data[0], - &ctrl->qsub[idx], SPDIF_QSUB_SIZE); - ret = 0; - } -- spin_unlock_irqrestore(&ctrl->ctl_lock, flags); - - return ret; - } -diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c ---- a/sound/soc/fsl/fsl_ssi.c -+++ b/sound/soc/fsl/fsl_ssi.c -@@ -1218,13 +1218,13 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, - if (reg > 0x7f) - return; - -- mutex_lock(&fsl_ac97_data->ac97_reg_lock); -+ guard(mutex)(&fsl_ac97_data->ac97_reg_lock); - - ret = clk_prepare_enable(fsl_ac97_data->clk); - if (ret) { - pr_err("ac97 write clk_prepare_enable failed: %d\n", - ret); -- goto ret_unlock; -+ return; - } - - lreg = reg << 12; -@@ -1238,9 +1238,6 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, - udelay(100); - - clk_disable_unprepare(fsl_ac97_data->clk); -- --ret_unlock: -- mutex_unlock(&fsl_ac97_data->ac97_reg_lock); - } - - static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, -@@ -1252,12 +1249,12 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, - unsigned int lreg; - int ret; - -- mutex_lock(&fsl_ac97_data->ac97_reg_lock); -+ guard(mutex)(&fsl_ac97_data->ac97_reg_lock); - - ret = clk_prepare_enable(fsl_ac97_data->clk); - if (ret) { - pr_err("ac97 read clk_prepare_enable failed: %d\n", ret); -- goto ret_unlock; -+ return val; - } - - lreg = (reg & 0x7f) << 12; -@@ -1272,8 +1269,6 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, - - clk_disable_unprepare(fsl_ac97_data->clk); - --ret_unlock: -- mutex_unlock(&fsl_ac97_data->ac97_reg_lock); - return val; - } - -diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c ---- a/sound/soc/fsl/fsl_xcvr.c -+++ b/sound/soc/fsl/fsl_xcvr.c -@@ -797,10 +797,9 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, - { - struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); - bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; -- unsigned long lock_flags; - int ret = 0; - -- spin_lock_irqsave(&xcvr->lock, lock_flags); -+ guard(spinlock_irqsave)(&xcvr->lock); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: -@@ -812,7 +811,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, - FSL_XCVR_EXT_CTRL_DPTH_RESET(tx)); - if (ret < 0) { - dev_err(dai->dev, "Failed to set DPATH RESET: %d\n", ret); -- goto release_lock; -+ return ret; - } - - if (tx) { -@@ -824,7 +823,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, - FSL_XCVR_ISR_CMDC_TX_EN); - if (ret < 0) { - dev_err(dai->dev, "err updating isr %d\n", ret); -- goto release_lock; -+ return ret; - } - fallthrough; - case FSL_XCVR_MODE_SPDIF: -@@ -833,7 +832,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, - FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX); - if (ret < 0) { - dev_err(dai->dev, "Failed to start DATA_TX: %d\n", ret); -- goto release_lock; -+ return ret; - } - break; - } -@@ -844,14 +843,14 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, - FSL_XCVR_EXT_CTRL_DMA_DIS(tx), 0); - if (ret < 0) { - dev_err(dai->dev, "Failed to enable DMA: %d\n", ret); -- goto release_lock; -+ return ret; - } - - ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0, - FSL_XCVR_IRQ_EARC_ALL, FSL_XCVR_IRQ_EARC_ALL); - if (ret < 0) { - dev_err(dai->dev, "Error while setting IER0: %d\n", ret); -- goto release_lock; -+ return ret; - } - - /* clear DPATH RESET */ -@@ -860,7 +859,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, - 0); - if (ret < 0) { - dev_err(dai->dev, "Failed to clear DPATH RESET: %d\n", ret); -- goto release_lock; -+ return ret; - } - - break; -@@ -873,14 +872,14 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, - FSL_XCVR_EXT_CTRL_DMA_DIS(tx)); - if (ret < 0) { - dev_err(dai->dev, "Failed to disable DMA: %d\n", ret); -- goto release_lock; -+ return ret; - } - - ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0, - FSL_XCVR_IRQ_EARC_ALL, 0); - if (ret < 0) { - dev_err(dai->dev, "Failed to clear IER0: %d\n", ret); -- goto release_lock; -+ return ret; - } - - if (tx) { -@@ -891,7 +890,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, - FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX); - if (ret < 0) { - dev_err(dai->dev, "Failed to stop DATA_TX: %d\n", ret); -- goto release_lock; -+ return ret; - } - if (xcvr->soc_data->spdif_only) - break; -@@ -905,7 +904,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, - if (ret < 0) { - dev_err(dai->dev, - "Err updating ISR %d\n", ret); -- goto release_lock; -+ return ret; - } - break; - } -@@ -916,18 +915,16 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, - break; - } - --release_lock: -- spin_unlock_irqrestore(&xcvr->lock, lock_flags); - return ret; - } - - static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) - { - struct device *dev = &xcvr->pdev->dev; -- const struct firmware *fw; - int ret = 0, rem, off, out, page = 0, size = FSL_XCVR_REG_OFFSET; - u32 mask, val; - -+ const struct firmware *fw __free(firmware) = NULL; - ret = request_firmware(&fw, xcvr->soc_data->fw_name, dev); - if (ret) { - dev_err(dev, "failed to request firmware.\n"); -@@ -939,7 +936,6 @@ static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) - /* RAM is 20KiB = 16KiB code + 4KiB data => max 10 pages 2KiB each */ - if (rem > 16384) { - dev_err(dev, "FW size %d is bigger than 16KiB.\n", rem); -- release_firmware(fw); - return -ENOMEM; - } - -@@ -950,7 +946,7 @@ static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) - if (ret < 0) { - dev_err(dev, "FW: failed to set page %d, err=%d\n", - page, ret); -- goto err_firmware; -+ return ret; - } - - off = page * size; -@@ -971,11 +967,6 @@ static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) - } - } - --err_firmware: -- release_firmware(fw); -- if (ret < 0) -- return ret; -- - /* configure watermarks */ - mask = FSL_XCVR_EXT_CTRL_RX_FWM_MASK | FSL_XCVR_EXT_CTRL_TX_FWM_MASK; - val = FSL_XCVR_EXT_CTRL_RX_FWM(FSL_XCVR_FIFO_WMK_RX); -@@ -1448,11 +1439,10 @@ static void reset_rx_work(struct work_struct *work) - { - struct fsl_xcvr *xcvr = container_of(work, struct fsl_xcvr, work_rst); - struct device *dev = &xcvr->pdev->dev; -- unsigned long lock_flags; - u32 ext_ctrl; - - dev_dbg(dev, "reset rx path\n"); -- spin_lock_irqsave(&xcvr->lock, lock_flags); -+ guard(spinlock_irqsave)(&xcvr->lock); - regmap_read(xcvr->regmap, FSL_XCVR_EXT_CTRL, &ext_ctrl); - - if (!(ext_ctrl & FSL_XCVR_EXT_CTRL_DMA_RD_DIS)) { -@@ -1469,7 +1459,6 @@ static void reset_rx_work(struct work_struct *work) - FSL_XCVR_EXT_CTRL_RX_DPTH_RESET, - 0); - } -- spin_unlock_irqrestore(&xcvr->lock, lock_flags); - } - - static irqreturn_t irq0_isr(int irq, void *devid) -diff --git a/sound/soc/fsl/imx-audio-rpmsg.c b/sound/soc/fsl/imx-audio-rpmsg.c ---- a/sound/soc/fsl/imx-audio-rpmsg.c -+++ b/sound/soc/fsl/imx-audio-rpmsg.c -@@ -22,7 +22,6 @@ static int imx_audio_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, - struct rpmsg_r_msg *r_msg = (struct rpmsg_r_msg *)data; - struct rpmsg_info *info; - struct rpmsg_msg *msg; -- unsigned long flags; - - if (!rpmsg->rpmsg_pdev) - return 0; -@@ -37,21 +36,21 @@ static int imx_audio_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, - /* TYPE C is notification from M core */ - switch (r_msg->header.cmd) { - case TX_PERIOD_DONE: -- spin_lock_irqsave(&info->lock[TX], flags); -- msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM]; -- msg->r_msg.param.buffer_tail = -- r_msg->param.buffer_tail; -- msg->r_msg.param.buffer_tail %= info->num_period[TX]; -- spin_unlock_irqrestore(&info->lock[TX], flags); -+ scoped_guard(spinlock_irqsave, &info->lock[TX]) { -+ msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM]; -+ msg->r_msg.param.buffer_tail = -+ r_msg->param.buffer_tail; -+ msg->r_msg.param.buffer_tail %= info->num_period[TX]; -+ } - info->callback[TX](info->callback_param[TX]); - break; - case RX_PERIOD_DONE: -- spin_lock_irqsave(&info->lock[RX], flags); -- msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM]; -- msg->r_msg.param.buffer_tail = -- r_msg->param.buffer_tail; -- msg->r_msg.param.buffer_tail %= info->num_period[1]; -- spin_unlock_irqrestore(&info->lock[RX], flags); -+ scoped_guard(spinlock_irqsave, &info->lock[RX]) { -+ msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM]; -+ msg->r_msg.param.buffer_tail = -+ r_msg->param.buffer_tail; -+ msg->r_msg.param.buffer_tail %= info->num_period[1]; -+ } - info->callback[RX](info->callback_param[RX]); - break; - default: diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c --- a/sound/soc/fsl/imx-card.c +++ b/sound/soc/fsl/imx-card.c @@ -35783,2271 +11340,6 @@ diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c if (ret) { /* Fallback to old binding; translate to asrc_format */ ret = of_property_read_u32(args.np, "fsl,asrc-width", &width); -diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c ---- a/sound/soc/fsl/imx-pcm-rpmsg.c -+++ b/sound/soc/fsl/imx-pcm-rpmsg.c -@@ -39,10 +39,9 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, - struct rpmsg_device *rpdev = info->rpdev; - int ret = 0; - -- mutex_lock(&info->msg_lock); -+ guard(mutex)(&info->msg_lock); - if (!rpdev) { - dev_err(info->dev, "rpmsg channel not ready\n"); -- mutex_unlock(&info->msg_lock); - return -EINVAL; - } - -@@ -55,15 +54,12 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, - sizeof(struct rpmsg_s_msg)); - if (ret) { - dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret); -- mutex_unlock(&info->msg_lock); - return ret; - } - - /* No receive msg for TYPE_C command */ -- if (msg->s_msg.header.type == MSG_TYPE_C) { -- mutex_unlock(&info->msg_lock); -+ if (msg->s_msg.header.type == MSG_TYPE_C) - return 0; -- } - - /* wait response from rpmsg */ - ret = wait_for_completion_timeout(&info->cmd_complete, -@@ -71,7 +67,6 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, - if (!ret) { - dev_err(&rpdev->dev, "rpmsg_send cmd %d timeout!\n", - msg->s_msg.header.cmd); -- mutex_unlock(&info->msg_lock); - return -ETIMEDOUT; - } - -@@ -100,8 +95,6 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, - dev_dbg(&rpdev->dev, "cmd:%d, resp %d\n", msg->s_msg.header.cmd, - info->r_msg.param.resp); - -- mutex_unlock(&info->msg_lock); -- - return 0; - } - -@@ -109,14 +102,13 @@ static int imx_rpmsg_insert_workqueue(struct snd_pcm_substream *substream, - struct rpmsg_msg *msg, - struct rpmsg_info *info) - { -- unsigned long flags; - int ret = 0; - - /* - * Queue the work to workqueue. - * If the queue is full, drop the message. - */ -- spin_lock_irqsave(&info->wq_lock, flags); -+ guard(spinlock_irqsave)(&info->wq_lock); - if (info->work_write_index != info->work_read_index) { - int index = info->work_write_index; - -@@ -130,7 +122,6 @@ static int imx_rpmsg_insert_workqueue(struct snd_pcm_substream *substream, - info->msg_drop_count[substream->stream]++; - ret = -EPIPE; - } -- spin_unlock_irqrestore(&info->wq_lock, flags); - - return ret; - } -@@ -523,7 +514,6 @@ static int imx_rpmsg_pcm_ack(struct snd_soc_component *component, - snd_pcm_sframes_t avail; - struct timer_list *timer; - struct rpmsg_msg *msg; -- unsigned long flags; - int buffer_tail = 0; - int written_num; - -@@ -553,11 +543,11 @@ static int imx_rpmsg_pcm_ack(struct snd_soc_component *component, - msg->s_msg.param.buffer_tail = buffer_tail; - - /* The notification message is updated to latest */ -- spin_lock_irqsave(&info->lock[substream->stream], flags); -- memcpy(&info->notify[substream->stream], msg, -- sizeof(struct rpmsg_s_msg)); -- info->notify_updated[substream->stream] = true; -- spin_unlock_irqrestore(&info->lock[substream->stream], flags); -+ scoped_guard(spinlock_irqsave, &info->lock[substream->stream]) { -+ memcpy(&info->notify[substream->stream], msg, -+ sizeof(struct rpmsg_s_msg)); -+ info->notify_updated[substream->stream] = true; -+ } - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - avail = snd_pcm_playback_hw_avail(runtime); -@@ -641,7 +631,7 @@ static void imx_rpmsg_pcm_work(struct work_struct *work) - bool is_notification = false; - struct rpmsg_info *info; - struct rpmsg_msg msg; -- unsigned long flags; -+ bool updated; - - work_of_rpmsg = container_of(work, struct work_of_rpmsg, work); - info = work_of_rpmsg->info; -@@ -652,25 +642,26 @@ static void imx_rpmsg_pcm_work(struct work_struct *work) - * enough data in M core side, need to let M core know - * data is updated immediately. - */ -- spin_lock_irqsave(&info->lock[TX], flags); -- if (info->notify_updated[TX]) { -- memcpy(&msg, &info->notify[TX], sizeof(struct rpmsg_s_msg)); -- info->notify_updated[TX] = false; -- spin_unlock_irqrestore(&info->lock[TX], flags); -- info->send_message(&msg, info); -- } else { -- spin_unlock_irqrestore(&info->lock[TX], flags); -+ scoped_guard(spinlock_irqsave, &info->lock[TX]) { -+ updated = info->notify_updated[TX]; -+ if (updated) { -+ memcpy(&msg, &info->notify[TX], sizeof(struct rpmsg_s_msg)); -+ info->notify_updated[TX] = false; -+ } - } -+ if (updated) -+ info->send_message(&msg, info); - -- spin_lock_irqsave(&info->lock[RX], flags); -- if (info->notify_updated[RX]) { -- memcpy(&msg, &info->notify[RX], sizeof(struct rpmsg_s_msg)); -- info->notify_updated[RX] = false; -- spin_unlock_irqrestore(&info->lock[RX], flags); -- info->send_message(&msg, info); -- } else { -- spin_unlock_irqrestore(&info->lock[RX], flags); -+ scoped_guard(spinlock_irqsave, &info->lock[RX]) { -+ updated = info->notify_updated[RX]; -+ if (updated) { -+ memcpy(&msg, &info->notify[RX], sizeof(struct rpmsg_s_msg)); -+ info->notify_updated[RX] = false; -+ } - } -+ if (updated) -+ info->send_message(&msg, info); -+ - - /* Skip the notification message for it has been processed above */ - if (work_of_rpmsg->msg.s_msg.header.type == MSG_TYPE_C && -@@ -682,10 +673,10 @@ static void imx_rpmsg_pcm_work(struct work_struct *work) - info->send_message(&work_of_rpmsg->msg, info); - - /* update read index */ -- spin_lock_irqsave(&info->wq_lock, flags); -- info->work_read_index++; -- info->work_read_index %= WORK_MAX_NUM; -- spin_unlock_irqrestore(&info->wq_lock, flags); -+ scoped_guard(spinlock_irqsave, &info->wq_lock) { -+ info->work_read_index++; -+ info->work_read_index %= WORK_MAX_NUM; -+ } - } - - static int imx_rpmsg_pcm_probe(struct platform_device *pdev) -@@ -819,9 +810,9 @@ static const struct dev_pm_ops imx_rpmsg_pcm_pm_ops = { - }; - - static const struct platform_device_id imx_rpmsg_pcm_id_table[] = { -- { .name = "rpmsg-audio-channel" }, -- { .name = "rpmsg-micfil-channel" }, -- { }, -+ { .name = "rpmsg-audio-channel" }, -+ { .name = "rpmsg-micfil-channel" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, imx_rpmsg_pcm_id_table); - -diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c ---- a/sound/soc/fsl/mpc5200_dma.c -+++ b/sound/soc/fsl/mpc5200_dma.c -@@ -77,18 +77,20 @@ static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream) - { - struct psc_dma_stream *s = _psc_dma_stream; - -- spin_lock(&s->psc_dma->lock); -- /* For each finished period, dequeue the completed period buffer -- * and enqueue a new one in it's place. */ -- while (bcom_buffer_done(s->bcom_task)) { -- bcom_retrieve_buffer(s->bcom_task, NULL, NULL); -+ scoped_guard(spinlock, &s->psc_dma->lock) { -+ /* -+ * For each finished period, dequeue the completed period buffer -+ * and enqueue a new one in its place -+ */ -+ while (bcom_buffer_done(s->bcom_task)) { -+ bcom_retrieve_buffer(s->bcom_task, NULL, NULL); - -- s->period_current = (s->period_current+1) % s->runtime->periods; -- s->period_count++; -+ s->period_current = (s->period_current+1) % s->runtime->periods; -+ s->period_count++; - -- psc_dma_bcom_enqueue_next_buffer(s); -+ psc_dma_bcom_enqueue_next_buffer(s); -+ } - } -- spin_unlock(&s->psc_dma->lock); - - /* If the stream is active, then also inform the PCM middle layer - * of the period finished event. */ -@@ -116,7 +118,6 @@ static int psc_dma_trigger(struct snd_soc_component *component, - struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); - struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; - u16 imr; -- unsigned long flags; - int i; - - switch (cmd) { -@@ -135,19 +136,18 @@ static int psc_dma_trigger(struct snd_soc_component *component, - /* Fill up the bestcomm bd queue and enable DMA. - * This will begin filling the PSC's fifo. - */ -- spin_lock_irqsave(&psc_dma->lock, flags); -+ scoped_guard(spinlock_irqsave, &psc_dma->lock) { -+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) -+ bcom_gen_bd_rx_reset(s->bcom_task); -+ else -+ bcom_gen_bd_tx_reset(s->bcom_task); - -- if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) -- bcom_gen_bd_rx_reset(s->bcom_task); -- else -- bcom_gen_bd_tx_reset(s->bcom_task); -+ for (i = 0; i < runtime->periods; i++) -+ if (!bcom_queue_full(s->bcom_task)) -+ psc_dma_bcom_enqueue_next_buffer(s); - -- for (i = 0; i < runtime->periods; i++) -- if (!bcom_queue_full(s->bcom_task)) -- psc_dma_bcom_enqueue_next_buffer(s); -- -- bcom_enable(s->bcom_task); -- spin_unlock_irqrestore(&psc_dma->lock, flags); -+ bcom_enable(s->bcom_task); -+ } - - out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); - -@@ -158,13 +158,13 @@ static int psc_dma_trigger(struct snd_soc_component *component, - substream->pstr->stream, s->period_count); - s->active = 0; - -- spin_lock_irqsave(&psc_dma->lock, flags); -- bcom_disable(s->bcom_task); -- if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) -- bcom_gen_bd_rx_reset(s->bcom_task); -- else -- bcom_gen_bd_tx_reset(s->bcom_task); -- spin_unlock_irqrestore(&psc_dma->lock, flags); -+ scoped_guard(spinlock_irqsave, &psc_dma->lock) { -+ bcom_disable(s->bcom_task); -+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) -+ bcom_gen_bd_rx_reset(s->bcom_task); -+ else -+ bcom_gen_bd_tx_reset(s->bcom_task); -+ } - - break; - -@@ -314,35 +314,29 @@ int mpc5200_audio_dma_create(struct platform_device *op) - { - phys_addr_t fifo; - struct psc_dma *psc_dma; -- struct resource res; -+ struct resource *res; - int size, irq, rc; - const __be32 *prop; - void __iomem *regs; -- int ret; -+ -+ regs = devm_platform_get_and_ioremap_resource(op, 0, &res); -+ if (IS_ERR(regs)) -+ return PTR_ERR(regs); - - /* Fetch the registers and IRQ of the PSC */ -- irq = irq_of_parse_and_map(op->dev.of_node, 0); -- if (of_address_to_resource(op->dev.of_node, 0, &res)) { -- dev_err(&op->dev, "Missing reg property\n"); -- return -ENODEV; -- } -- regs = devm_ioremap(&op->dev, res.start, resource_size(&res)); -- if (!regs) { -- dev_err(&op->dev, "Could not map registers\n"); -- return -ENODEV; -- } -+ irq = platform_get_irq(op, 0); -+ if (irq < 0) -+ return irq; - - /* Allocate and initialize the driver private data */ -- psc_dma = kzalloc_obj(*psc_dma); -+ psc_dma = devm_kzalloc(&op->dev, sizeof(*psc_dma), GFP_KERNEL); - if (!psc_dma) - return -ENOMEM; - - /* Get the PSC ID */ - prop = of_get_property(op->dev.of_node, "cell-index", &size); -- if (!prop || size < sizeof *prop) { -- ret = -ENODEV; -- goto out_free; -- } -+ if (!prop || size < sizeof *prop) -+ return -ENODEV; - - spin_lock_init(&psc_dma->lock); - mutex_init(&psc_dma->mutex); -@@ -357,7 +351,7 @@ int mpc5200_audio_dma_create(struct platform_device *op) - - /* Find the address of the fifo data registers and setup the - * DMA tasks */ -- fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32); -+ fifo = res->start + offsetof(struct mpc52xx_psc, buffer.buffer_32); - psc_dma->capture.bcom_task = - bcom_psc_gen_bd_rx_init(psc_dma->id, 10, fifo, 512); - psc_dma->playback.bcom_task = -@@ -365,8 +359,7 @@ int mpc5200_audio_dma_create(struct platform_device *op) - if (!psc_dma->capture.bcom_task || - !psc_dma->playback.bcom_task) { - dev_err(&op->dev, "Could not allocate bestcomm tasks\n"); -- ret = -ENODEV; -- goto out_free; -+ return -ENODEV; - } - - /* Disable all interrupts and reset the PSC */ -@@ -399,16 +392,14 @@ int mpc5200_audio_dma_create(struct platform_device *op) - psc_dma->capture.irq = - bcom_get_task_irq(psc_dma->capture.bcom_task); - -- rc = request_irq(psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED, -+ rc = devm_request_irq(&op->dev, psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED, - "psc-dma-status", psc_dma); -- rc |= request_irq(psc_dma->capture.irq, &psc_dma_bcom_irq, IRQF_SHARED, -+ rc |= devm_request_irq(&op->dev, psc_dma->capture.irq, &psc_dma_bcom_irq, IRQF_SHARED, - "psc-dma-capture", &psc_dma->capture); -- rc |= request_irq(psc_dma->playback.irq, &psc_dma_bcom_irq, IRQF_SHARED, -+ rc |= devm_request_irq(&op->dev, psc_dma->playback.irq, &psc_dma_bcom_irq, IRQF_SHARED, - "psc-dma-playback", &psc_dma->playback); -- if (rc) { -- ret = -ENODEV; -- goto out_irq; -- } -+ if (rc) -+ return -ENODEV; - - /* Save what we've done so it can be found again later */ - dev_set_drvdata(&op->dev, psc_dma); -@@ -416,13 +407,6 @@ int mpc5200_audio_dma_create(struct platform_device *op) - /* Tell the ASoC OF helpers about it */ - return devm_snd_soc_register_component(&op->dev, - &mpc5200_audio_dma_component, NULL, 0); --out_irq: -- free_irq(psc_dma->irq, psc_dma); -- free_irq(psc_dma->capture.irq, &psc_dma->capture); -- free_irq(psc_dma->playback.irq, &psc_dma->playback); --out_free: -- kfree(psc_dma); -- return ret; - } - EXPORT_SYMBOL_GPL(mpc5200_audio_dma_create); - -@@ -435,12 +419,6 @@ int mpc5200_audio_dma_destroy(struct platform_device *op) - bcom_gen_bd_rx_release(psc_dma->capture.bcom_task); - bcom_gen_bd_tx_release(psc_dma->playback.bcom_task); - -- /* Release irqs */ -- free_irq(psc_dma->irq, psc_dma); -- free_irq(psc_dma->capture.irq, &psc_dma->capture); -- free_irq(psc_dma->playback.irq, &psc_dma->playback); -- -- kfree(psc_dma); - dev_set_drvdata(&op->dev, NULL); - - return 0; -diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c ---- a/sound/soc/fsl/mpc5200_psc_ac97.c -+++ b/sound/soc/fsl/mpc5200_psc_ac97.c -@@ -30,14 +30,13 @@ static unsigned short psc_ac97_read(struct snd_ac97 *ac97, unsigned short reg) - int status; - unsigned int val; - -- mutex_lock(&psc_dma->mutex); -+ guard(mutex)(&psc_dma->mutex); - - /* Wait for command send status zero = ready */ - status = spin_event_timeout(!(in_be16(&psc_dma->psc_regs->sr_csr.status) & - MPC52xx_PSC_SR_CMDSEND), 100, 0); - if (status == 0) { - pr_err("timeout on ac97 bus (rdy)\n"); -- mutex_unlock(&psc_dma->mutex); - return -ENODEV; - } - -@@ -53,19 +52,16 @@ static unsigned short psc_ac97_read(struct snd_ac97 *ac97, unsigned short reg) - if (status == 0) { - pr_err("timeout on ac97 read (val) %x\n", - in_be16(&psc_dma->psc_regs->sr_csr.status)); -- mutex_unlock(&psc_dma->mutex); - return -ENODEV; - } - /* Get the data */ - val = in_be32(&psc_dma->psc_regs->ac97_data); - if (((val >> 24) & 0x7f) != reg) { - pr_err("reg echo error on ac97 read\n"); -- mutex_unlock(&psc_dma->mutex); - return -ENODEV; - } - val = (val >> 8) & 0xffff; - -- mutex_unlock(&psc_dma->mutex); - return (unsigned short) val; - } - -@@ -74,52 +70,46 @@ static void psc_ac97_write(struct snd_ac97 *ac97, - { - int status; - -- mutex_lock(&psc_dma->mutex); -+ guard(mutex)(&psc_dma->mutex); - - /* Wait for command status zero = ready */ - status = spin_event_timeout(!(in_be16(&psc_dma->psc_regs->sr_csr.status) & - MPC52xx_PSC_SR_CMDSEND), 100, 0); - if (status == 0) { - pr_err("timeout on ac97 bus (write)\n"); -- goto out; -+ return; - } - /* Write data */ - out_be32(&psc_dma->psc_regs->ac97_cmd, - ((reg & 0x7f) << 24) | (val << 8)); -- -- out: -- mutex_unlock(&psc_dma->mutex); - } - - static void psc_ac97_warm_reset(struct snd_ac97 *ac97) - { - struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; - -- mutex_lock(&psc_dma->mutex); -+ guard(mutex)(&psc_dma->mutex); - - out_be32(®s->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_AWR); - udelay(3); - out_be32(®s->sicr, psc_dma->sicr); -- -- mutex_unlock(&psc_dma->mutex); - } - - static void psc_ac97_cold_reset(struct snd_ac97 *ac97) - { - struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; - -- mutex_lock(&psc_dma->mutex); -- dev_dbg(psc_dma->dev, "cold reset\n"); -+ scoped_guard(mutex, &psc_dma->mutex) { -+ dev_dbg(psc_dma->dev, "cold reset\n"); - -- mpc5200_psc_ac97_gpio_reset(psc_dma->id); -+ mpc5200_psc_ac97_gpio_reset(psc_dma->id); - -- /* Notify the PSC that a reset has occurred */ -- out_be32(®s->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_ACRB); -+ /* Notify the PSC that a reset has occurred */ -+ out_be32(®s->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_ACRB); - -- /* Re-enable RX and TX */ -- out_8(®s->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); -- -- mutex_unlock(&psc_dma->mutex); -+ /* Re-enable RX and TX */ -+ out_8(®s->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); -+ } - - usleep_range(1000, 2000); - psc_ac97_warm_reset(ac97); -@@ -286,7 +276,7 @@ static int psc_ac97_of_probe(struct platform_device *op) - return rc; - } - -- rc = snd_soc_register_component(&op->dev, &psc_ac97_component, -+ rc = devm_snd_soc_register_component(&op->dev, &psc_ac97_component, - psc_ac97_dai, ARRAY_SIZE(psc_ac97_dai)); - if (rc != 0) { - dev_err(&op->dev, "Failed to register DAI\n"); -@@ -312,8 +302,6 @@ static int psc_ac97_of_probe(struct platform_device *op) - static void psc_ac97_of_remove(struct platform_device *op) - { - mpc5200_audio_dma_destroy(op); -- snd_soc_unregister_component(&op->dev); -- snd_soc_set_ac97_ops(NULL); - } - - /* Match table for of_platform binding */ -diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c ---- a/sound/soc/fsl/mpc5200_psc_i2s.c -+++ b/sound/soc/fsl/mpc5200_psc_i2s.c -@@ -79,9 +79,12 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream, - * and we don't care about the frequency. Return an error if the direction - * is not SND_SOC_CLOCK_IN. - * -+ * @cpu_dai: DAI runtime data pointer - * @clk_id: reserved, should be zero - * @freq: the frequency of the given clock ID, currently ignored - * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master) -+ * -+ * Returns: %0 on success or %-EINVAL on failure. - */ - static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, - int clk_id, unsigned int freq, int dir) -@@ -101,7 +104,10 @@ static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, - * This driver only supports I2S mode. Return an error if the format is - * not SND_SOC_DAIFMT_I2S. - * -+ * @cpu_dai: DAI runtime data pointer - * @format: one of SND_SOC_DAIFMT_xxx -+ * -+ * Returns: %0 on success or %-EINVAL on failure. - */ - static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) - { -@@ -119,7 +125,7 @@ static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) - */ - - /** -- * psc_i2s_dai_template: template CPU Digital Audio Interface -+ * var psc_i2s_dai_ops - template CPU Digital Audio Interface - */ - static const struct snd_soc_dai_ops psc_i2s_dai_ops = { - .hw_params = psc_i2s_hw_params, -@@ -166,7 +172,7 @@ static int psc_i2s_of_probe(struct platform_device *op) - if (rc != 0) - return rc; - -- rc = snd_soc_register_component(&op->dev, &psc_i2s_component, -+ rc = devm_snd_soc_register_component(&op->dev, &psc_i2s_component, - psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai)); - if (rc != 0) { - pr_err("Failed to register DAI\n"); -@@ -213,7 +219,6 @@ static int psc_i2s_of_probe(struct platform_device *op) - static void psc_i2s_of_remove(struct platform_device *op) - { - mpc5200_audio_dma_destroy(op); -- snd_soc_unregister_component(&op->dev); - } - - /* Match table for of_platform binding */ -diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c ---- a/sound/soc/fsl/p1022_ds.c -+++ b/sound/soc/fsl/p1022_ds.c -@@ -73,6 +73,8 @@ struct machine_data { - char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */ - }; - -+#define card_to_mdata(_card) container_of(_card, struct machine_data, card) -+ - /** - * p1022_ds_machine_probe: initialize the board - * -@@ -82,8 +84,7 @@ struct machine_data { - */ - static int p1022_ds_machine_probe(struct snd_soc_card *card) - { -- struct machine_data *mdata = -- container_of(card, struct machine_data, card); -+ struct machine_data *mdata = card_to_mdata(card); - struct ccsr_guts __iomem *guts; - - guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); -@@ -122,8 +123,7 @@ static int p1022_ds_machine_probe(struct snd_soc_card *card) - static int p1022_ds_startup(struct snd_pcm_substream *substream) - { - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); -- struct machine_data *mdata = -- container_of(rtd->card, struct machine_data, card); -+ struct machine_data *mdata = card_to_mdata(rtd->card); - struct device *dev = rtd->card->dev; - int ret = 0; - -@@ -156,8 +156,7 @@ static int p1022_ds_startup(struct snd_pcm_substream *substream) - */ - static int p1022_ds_machine_remove(struct snd_soc_card *card) - { -- struct machine_data *mdata = -- container_of(card, struct machine_data, card); -+ struct machine_data *mdata = card_to_mdata(card); - struct ccsr_guts __iomem *guts; - - guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); -@@ -399,8 +398,7 @@ static int p1022_ds_probe(struct platform_device *pdev) - static void p1022_ds_remove(struct platform_device *pdev) - { - struct snd_soc_card *card = platform_get_drvdata(pdev); -- struct machine_data *mdata = -- container_of(card, struct machine_data, card); -+ struct machine_data *mdata = card_to_mdata(card); - - snd_soc_unregister_card(card); - kfree(mdata); -diff --git a/sound/soc/fsl/p1022_rdk.c b/sound/soc/fsl/p1022_rdk.c ---- a/sound/soc/fsl/p1022_rdk.c -+++ b/sound/soc/fsl/p1022_rdk.c -@@ -79,6 +79,8 @@ struct machine_data { - char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */ - }; - -+#define card_to_mdata(_card) container_of(_card, struct machine_data, card) -+ - /** - * p1022_rdk_machine_probe - initialize the board - * @card: ASoC card instance -@@ -91,8 +93,7 @@ struct machine_data { - */ - static int p1022_rdk_machine_probe(struct snd_soc_card *card) - { -- struct machine_data *mdata = -- container_of(card, struct machine_data, card); -+ struct machine_data *mdata = card_to_mdata(card); - struct ccsr_guts __iomem *guts; - - guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); -@@ -134,8 +135,7 @@ static int p1022_rdk_machine_probe(struct snd_soc_card *card) - static int p1022_rdk_startup(struct snd_pcm_substream *substream) - { - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); -- struct machine_data *mdata = -- container_of(rtd->card, struct machine_data, card); -+ struct machine_data *mdata = card_to_mdata(rtd->card); - struct device *dev = rtd->card->dev; - int ret = 0; - -@@ -169,8 +169,7 @@ static int p1022_rdk_startup(struct snd_pcm_substream *substream) - */ - static int p1022_rdk_machine_remove(struct snd_soc_card *card) - { -- struct machine_data *mdata = -- container_of(card, struct machine_data, card); -+ struct machine_data *mdata = card_to_mdata(card); - struct ccsr_guts __iomem *guts; - - guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); -@@ -361,8 +360,7 @@ static int p1022_rdk_probe(struct platform_device *pdev) - static void p1022_rdk_remove(struct platform_device *pdev) - { - struct snd_soc_card *card = platform_get_drvdata(pdev); -- struct machine_data *mdata = -- container_of(card, struct machine_data, card); -+ struct machine_data *mdata = card_to_mdata(card); - - snd_soc_unregister_card(card); - kfree(mdata); -diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c ---- a/sound/soc/generic/audio-graph-card.c -+++ b/sound/soc/generic/audio-graph-card.c -@@ -579,11 +579,11 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) - goto end; - } - -- ret = simple_util_parse_widgets(card, NULL); -+ ret = simple_util_parse_widgets(priv, NULL); - if (ret < 0) - goto end; - -- ret = simple_util_parse_routing(card, NULL); -+ ret = simple_util_parse_routing(priv, NULL); - if (ret < 0) - goto end; - -@@ -594,6 +594,7 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) - if (ret < 0) - goto err; - -+ /* Card name should be set after graph_for_each_link() */ - ret = simple_util_parse_card_name(priv, NULL); - if (ret < 0) - goto err; -@@ -603,14 +604,13 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) - simple_util_debug_info(priv); - - ret = devm_snd_soc_register_card(dev, card); -- if (ret < 0) -- goto err; -- -- return 0; - err: -- simple_util_clean_reference(card); -+ if (ret < 0) { -+ simple_util_clean_reference(priv); -+ return dev_err_probe(dev, ret, "parse error\n"); -+ } - end: -- return dev_err_probe(dev, ret, "parse error\n"); -+ return graph_ret(priv, ret); - } - EXPORT_SYMBOL_GPL(audio_graph_parse_of); - -diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c ---- a/sound/soc/generic/audio-graph-card2.c -+++ b/sound/soc/generic/audio-graph-card2.c -@@ -1303,11 +1303,11 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, - struct graph2_custom_hooks *hooks) - { - struct snd_soc_card *card = simple_priv_to_card(priv); -- int ret; -+ int ret = -ENOMEM; - - struct link_info *li __free(kfree) = kzalloc_obj(*li); - if (!li) -- return -ENOMEM; -+ goto end; - - card->probe = graph_util_card_probe; - card->owner = THIS_MODULE; -@@ -1316,31 +1316,35 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, - if ((hooks) && (hooks)->hook_pre) { - ret = (hooks)->hook_pre(priv); - if (ret < 0) -- goto err; -+ goto end; - } - - ret = graph_for_each_link(priv, hooks, li, graph_count); - if (!li->link) - ret = -EINVAL; - if (ret < 0) -- goto err; -+ goto end; - - ret = simple_util_init_priv(priv, li); - if (ret < 0) -- goto err; -+ goto end; - - priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW); - if (IS_ERR(priv->pa_gpio)) { - ret = PTR_ERR(priv->pa_gpio); - dev_err(dev, "failed to get amplifier gpio: %d\n", ret); -- goto err; -+ goto end; - } - -- ret = simple_util_parse_widgets(card, NULL); -+ ret = simple_util_parse_widgets(priv, NULL); - if (ret < 0) -- goto err; -+ goto end; - -- ret = simple_util_parse_routing(card, NULL); -+ ret = simple_util_parse_routing(priv, NULL); -+ if (ret < 0) -+ goto end; -+ -+ ret = simple_util_parse_aux_devs(priv, NULL); - if (ret < 0) - goto err; - -@@ -1349,6 +1353,7 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, - if (ret < 0) - goto err; - -+ /* Card name should be set after graph_for_each_link() */ - ret = simple_util_parse_card_name(priv, NULL); - if (ret < 0) - goto err; -@@ -1363,15 +1368,13 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, - - simple_util_debug_info(priv); - -- ret = snd_soc_of_parse_aux_devs(card, "aux-devs"); -- if (ret < 0) -- goto err; -- - ret = devm_snd_soc_register_card(dev, card); - err: -- if (ret < 0) -- dev_err_probe(dev, ret, "parse error\n"); -- -+ if (ret < 0) { -+ simple_util_clean_reference(priv); -+ return dev_err_probe(dev, ret, "parse error\n"); -+ } -+end: - return graph_ret(priv, ret); - } - EXPORT_SYMBOL_GPL(audio_graph2_parse_of); -diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c ---- a/sound/soc/generic/simple-card-utils.c -+++ b/sound/soc/generic/simple-card-utils.c -@@ -216,6 +216,27 @@ int simple_util_set_dailink_name(struct simple_util_priv *priv, - } - EXPORT_SYMBOL_GPL(simple_util_set_dailink_name); - -+int simple_util_parse_property(struct simple_util_priv *priv, -+ int (*func)(struct snd_soc_card *card, const char *propname), -+ char *prefix, char *property) -+{ -+ struct snd_soc_card *card = simple_priv_to_card(priv); -+ struct device_node *node = card->dev->of_node; -+ char prop[128]; -+ -+ if (!prefix) -+ prefix = ""; -+ -+ snprintf(prop, sizeof(prop), "%s%s", prefix, property); -+ -+ /* no property is not error */ -+ if (!of_property_present(node, prop)) -+ return 0; -+ -+ return func(card, prop); -+} -+EXPORT_SYMBOL_GPL(simple_util_parse_property); -+ - int simple_util_parse_card_name(struct simple_util_priv *priv, - char *prefix) - { -@@ -731,11 +752,12 @@ void simple_util_canonicalize_cpu(struct snd_soc_dai_link_component *cpus, - } - EXPORT_SYMBOL_GPL(simple_util_canonicalize_cpu); - --void simple_util_clean_reference(struct snd_soc_card *card) -+void simple_util_clean_reference(struct simple_util_priv *priv) - { - struct snd_soc_dai_link *dai_link; - struct snd_soc_dai_link_component *cpu; - struct snd_soc_dai_link_component *codec; -+ struct snd_soc_card *card = simple_priv_to_card(priv); - int i, j; - - for_each_card_prelinks(card, i, dai_link) { -@@ -747,57 +769,6 @@ void simple_util_clean_reference(struct snd_soc_card *card) - } - EXPORT_SYMBOL_GPL(simple_util_clean_reference); - --int simple_util_parse_routing(struct snd_soc_card *card, -- char *prefix) --{ -- struct device_node *node = card->dev->of_node; -- char prop[128]; -- -- if (!prefix) -- prefix = ""; -- -- snprintf(prop, sizeof(prop), "%s%s", prefix, "routing"); -- -- if (!of_property_present(node, prop)) -- return 0; -- -- return snd_soc_of_parse_audio_routing(card, prop); --} --EXPORT_SYMBOL_GPL(simple_util_parse_routing); -- --int simple_util_parse_widgets(struct snd_soc_card *card, -- char *prefix) --{ -- struct device_node *node = card->dev->of_node; -- char prop[128]; -- -- if (!prefix) -- prefix = ""; -- -- snprintf(prop, sizeof(prop), "%s%s", prefix, "widgets"); -- -- if (of_property_present(node, prop)) -- return snd_soc_of_parse_audio_simple_widgets(card, prop); -- -- /* no widgets is not error */ -- return 0; --} --EXPORT_SYMBOL_GPL(simple_util_parse_widgets); -- --int simple_util_parse_pin_switches(struct snd_soc_card *card, -- char *prefix) --{ -- char prop[128]; -- -- if (!prefix) -- prefix = ""; -- -- snprintf(prop, sizeof(prop), "%s%s", prefix, "pin-switches"); -- -- return snd_soc_of_parse_pin_switches(card, prop); --} --EXPORT_SYMBOL_GPL(simple_util_parse_pin_switches); -- - int simple_util_init_jack(struct snd_soc_card *card, - struct simple_util_jack *sjack, - int is_hp, char *prefix, -@@ -854,9 +825,9 @@ int simple_util_init_jack(struct snd_soc_card *card, - } - EXPORT_SYMBOL_GPL(simple_util_init_jack); - --int simple_util_init_aux_jacks(struct simple_util_priv *priv, char *prefix) -+int simple_util_init_aux_jacks(struct snd_soc_card *card, char *prefix) - { -- struct snd_soc_card *card = simple_priv_to_card(priv); -+ struct simple_util_priv *priv = snd_soc_card_get_drvdata(card); - struct snd_soc_component *component; - int found_jack_index = 0; - int type = 0; -@@ -1026,8 +997,9 @@ EXPORT_SYMBOL_GPL(simple_util_init_priv); - void simple_util_remove(struct platform_device *pdev) - { - struct snd_soc_card *card = platform_get_drvdata(pdev); -+ struct simple_util_priv *priv = snd_soc_card_get_drvdata(card); - -- simple_util_clean_reference(card); -+ simple_util_clean_reference(priv); - } - EXPORT_SYMBOL_GPL(simple_util_remove); - -diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c ---- a/sound/soc/generic/simple-card.c -+++ b/sound/soc/generic/simple-card.c -@@ -517,44 +517,6 @@ static int simple_populate_aux(struct simple_util_priv *priv) - return simple_ret(priv, ret); - } - --static int simple_parse_of(struct simple_util_priv *priv, struct link_info *li) --{ -- struct snd_soc_card *card = simple_priv_to_card(priv); -- int ret; -- -- ret = simple_util_parse_widgets(card, PREFIX); -- if (ret < 0) -- goto end; -- -- ret = simple_util_parse_routing(card, PREFIX); -- if (ret < 0) -- goto end; -- -- ret = simple_util_parse_pin_switches(card, PREFIX); -- if (ret < 0) -- goto end; -- -- /* Single/Muti DAI link(s) & New style of DT node */ -- memset(li, 0, sizeof(*li)); -- ret = simple_for_each_link(priv, li, -- simple_dai_link_of, -- simple_dai_link_of_dpcm); -- if (ret < 0) -- goto end; -- -- ret = simple_util_parse_card_name(priv, PREFIX); -- if (ret < 0) -- goto end; -- -- ret = simple_populate_aux(priv); -- if (ret < 0) -- goto end; -- -- ret = snd_soc_of_parse_aux_devs(card, PREFIX "aux-devs"); --end: -- return simple_ret(priv, ret); --} -- - static int simple_count_noml(struct simple_util_priv *priv, - struct device_node *np, - struct device_node *codec, -@@ -699,28 +661,19 @@ static int simple_soc_probe(struct snd_soc_card *card) - if (ret < 0) - goto end; - -- ret = simple_util_init_aux_jacks(priv, PREFIX); -+ ret = simple_util_init_aux_jacks(card, PREFIX); - end: - return simple_ret(priv, ret); - } - --static int simple_probe(struct platform_device *pdev) -+static int simple_parse_of(struct simple_util_priv *priv) - { -- struct simple_util_priv *priv; -- struct device *dev = &pdev->dev; -- struct snd_soc_card *card; -- int ret; -+ struct snd_soc_card *card = simple_priv_to_card(priv); -+ struct device *dev = card->dev; -+ int ret = -EINVAL; - -- /* Allocate the private data and the DAI link array */ -- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -- if (!priv) -- return -ENOMEM; -- -- card = simple_priv_to_card(priv); -- card->owner = THIS_MODULE; -- card->dev = dev; -- card->probe = simple_soc_probe; -- card->driver_name = "simple-card"; -+ if (!dev) -+ return simple_ret(priv, ret); - - ret = -ENOMEM; - struct link_info *li __free(kfree) = kzalloc_obj(*li); -@@ -739,25 +692,71 @@ static int simple_probe(struct platform_device *pdev) - if (ret < 0) - goto end; - -- ret = simple_parse_of(priv, li); -- if (ret < 0) { -- dev_err_probe(dev, ret, "parse error\n"); -+ ret = simple_util_parse_widgets(priv, PREFIX); -+ if (ret < 0) -+ goto end; -+ -+ ret = simple_util_parse_routing(priv, PREFIX); -+ if (ret < 0) -+ goto end; -+ -+ ret = simple_util_parse_pin_switches(priv, PREFIX); -+ if (ret < 0) -+ goto end; -+ -+ ret = simple_util_parse_aux_devs(priv, PREFIX); -+ if (ret < 0) -+ goto err; -+ -+ /* Single/Muti DAI link(s) & New style of DT node */ -+ memset(li, 0, sizeof(*li)); -+ ret = simple_for_each_link(priv, li, -+ simple_dai_link_of, -+ simple_dai_link_of_dpcm); -+ if (ret < 0) -+ goto err; -+ -+ /* Card name should be set after simple_for_each_link() */ -+ ret = simple_util_parse_card_name(priv, PREFIX); -+ if (ret < 0) -+ goto err; -+ -+ ret = simple_populate_aux(priv); -+ if (ret < 0) - goto err; -- } - - snd_soc_card_set_drvdata(card, priv); - - simple_util_debug_info(priv); - - ret = devm_snd_soc_register_card(dev, card); -- if (ret < 0) -- goto err; -- -- return 0; - err: -- simple_util_clean_reference(card); -+ if (ret < 0) { -+ simple_util_clean_reference(priv); -+ return dev_err_probe(dev, ret, "parse error\n"); -+ } - end: -- return dev_err_probe(dev, ret, "parse error\n"); -+ return simple_ret(priv, ret); -+} -+ -+static int simple_probe(struct platform_device *pdev) -+{ -+ struct simple_util_priv *priv; -+ struct device *dev = &pdev->dev; -+ struct snd_soc_card *card; -+ -+ /* Allocate the private data and the DAI link array */ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ card = simple_priv_to_card(priv); -+ card->owner = THIS_MODULE; -+ card->dev = dev; -+ card->probe = simple_soc_probe; -+ card->driver_name = "simple-card"; -+ -+ return simple_parse_of(priv); - } - - static const struct of_device_id simple_of_match[] = { -diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c ---- a/sound/soc/intel/atom/sst-atom-controls.c -+++ b/sound/soc/intel/atom/sst-atom-controls.c -@@ -14,6 +14,7 @@ - */ - #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -+#include - #include - #include - #include -@@ -73,14 +74,10 @@ static int sst_fill_and_send_cmd(struct sst_data *drv, - u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id, - void *cmd_data, u16 len) - { -- int ret; -+ guard(mutex)(&drv->lock); - -- mutex_lock(&drv->lock); -- ret = sst_fill_and_send_cmd_unlocked(drv, ipc_msg, block, -- task_id, pipe_id, cmd_data, len); -- mutex_unlock(&drv->lock); -- -- return ret; -+ return sst_fill_and_send_cmd_unlocked(drv, ipc_msg, block, -+ task_id, pipe_id, cmd_data, len); - } - - /* -@@ -167,7 +164,7 @@ static int sst_slot_get(struct snd_kcontrol *kcontrol, - unsigned int val, mux; - u8 *map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map; - -- mutex_lock(&drv->lock); -+ guard(mutex)(&drv->lock); - val = 1 << ctl_no; - /* search which slot/channel has this bit set - there should be only one */ - for (mux = e->max; mux > 0; mux--) -@@ -175,7 +172,6 @@ static int sst_slot_get(struct snd_kcontrol *kcontrol, - break; - - ucontrol->value.enumerated.item[0] = mux; -- mutex_unlock(&drv->lock); - - dev_dbg(c->dev, "%s - %s map = %#x\n", - is_tx ? "tx channel" : "rx slot", -@@ -235,7 +231,7 @@ static int sst_slot_put(struct snd_kcontrol *kcontrol, - if (mux > e->max - 1) - return -EINVAL; - -- mutex_lock(&drv->lock); -+ guard(mutex)(&drv->lock); - /* first clear all registers of this bit */ - for (i = 0; i < e->max; i++) - map[i] &= ~val; -@@ -244,7 +240,6 @@ static int sst_slot_put(struct snd_kcontrol *kcontrol, - /* kctl set to 'none' and we reset the bits so send IPC */ - ret = sst_check_and_send_slot_map(drv, kcontrol); - -- mutex_unlock(&drv->lock); - return ret; - } - -@@ -258,7 +253,6 @@ static int sst_slot_put(struct snd_kcontrol *kcontrol, - - ret = sst_check_and_send_slot_map(drv, kcontrol); - -- mutex_unlock(&drv->lock); - return ret; - } - -@@ -354,13 +348,12 @@ static int sst_algo_control_set(struct snd_kcontrol *kcontrol, - struct sst_algo_control *bc = (void *)kcontrol->private_value; - - dev_dbg(cmpnt->dev, "control_name=%s\n", kcontrol->id.name); -- mutex_lock(&drv->lock); -+ guard(mutex)(&drv->lock); - switch (bc->type) { - case SST_ALGO_PARAMS: - memcpy(bc->params, ucontrol->value.bytes.data, bc->max); - break; - default: -- mutex_unlock(&drv->lock); - dev_err(cmpnt->dev, "Invalid Input- algo type:%d\n", - bc->type); - return -EINVAL; -@@ -368,7 +361,6 @@ static int sst_algo_control_set(struct snd_kcontrol *kcontrol, - /*if pipe is enabled, need to send the algo params from here*/ - if (bc->w && bc->w->power) - ret = sst_send_algo_cmd(drv, bc); -- mutex_unlock(&drv->lock); - - return ret; - } -@@ -475,7 +467,7 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, - struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value; - struct sst_gain_value *gv = mc->gain_val; - -- mutex_lock(&drv->lock); -+ guard(mutex)(&drv->lock); - - switch (mc->type) { - case SST_GAIN_TLV: -@@ -497,7 +489,6 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, - break; - - default: -- mutex_unlock(&drv->lock); - dev_err(cmpnt->dev, "Invalid Input- gain type:%d\n", - mc->type); - return -EINVAL; -@@ -506,7 +497,6 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, - if (mc->w && mc->w->power) - ret = sst_send_gain_cmd(drv, gv, mc->task_id, - mc->pipe_id | mc->instance_id, mc->module_id, 0); -- mutex_unlock(&drv->lock); - - return ret; - } -@@ -521,10 +511,9 @@ static int sst_send_pipe_module_params(struct snd_soc_dapm_widget *w, - struct sst_data *drv = snd_soc_component_get_drvdata(c); - struct sst_ids *ids = w->priv; - -- mutex_lock(&drv->lock); -+ guard(mutex)(&drv->lock); - sst_find_and_send_pipe_algo(drv, w->name, ids); - sst_set_pipe_gain(ids, drv, 0); -- mutex_unlock(&drv->lock); - - return 0; - } -@@ -761,27 +750,29 @@ int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable) - return ret; - } - -- mutex_lock(&drv->lock); -- if (enable) -- timer_usage++; -- else -- timer_usage--; -- -- /* -- * Send the command only if this call is the first enable or last -- * disable -- */ -- if ((enable && (timer_usage == 1)) || -- (!enable && (timer_usage == 0))) { -- ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD, -- SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd, -- sizeof(cmd.header) + cmd.header.length); -- if (ret && enable) { -+ scoped_guard(mutex, &drv->lock) { -+ if (enable) -+ timer_usage++; -+ else - timer_usage--; -- enable = false; -+ -+ /* -+ * Send the command only if this call is the first enable or last -+ * disable -+ */ -+ if ((enable && timer_usage == 1) || -+ (!enable && timer_usage == 0)) { -+ ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD, -+ SST_FLAG_BLOCKED, -+ SST_TASK_SBA, 0, &cmd, -+ sizeof(cmd.header) + -+ cmd.header.length); -+ if (ret && enable) { -+ timer_usage--; -+ enable = false; -+ } - } - } -- mutex_unlock(&drv->lock); - - if (!enable) - sst->ops->power(sst->dev, false); -diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c ---- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c -+++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c -@@ -11,6 +11,7 @@ - */ - #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -+#include - #include - #include - #include -@@ -32,7 +33,7 @@ int sst_register_dsp(struct sst_device *dev) - return -EINVAL; - if (!try_module_get(dev->dev->driver->owner)) - return -ENODEV; -- mutex_lock(&sst_lock); -+ guard(mutex)(&sst_lock); - if (sst) { - dev_err(dev->dev, "we already have a device %s\n", sst->name); - module_put(dev->dev->driver->owner); -@@ -41,7 +42,7 @@ int sst_register_dsp(struct sst_device *dev) - } - dev_dbg(dev->dev, "registering device %s\n", dev->name); - sst = dev; -- mutex_unlock(&sst_lock); -+ - return 0; - } - EXPORT_SYMBOL_GPL(sst_register_dsp); -@@ -53,17 +54,15 @@ int sst_unregister_dsp(struct sst_device *dev) - if (dev != sst) - return -EINVAL; - -- mutex_lock(&sst_lock); -+ guard(mutex)(&sst_lock); - -- if (!sst) { -- mutex_unlock(&sst_lock); -+ if (!sst) - return -EIO; -- } - - module_put(sst->dev->driver->owner); - dev_dbg(dev->dev, "unreg %s\n", sst->name); - sst = NULL; -- mutex_unlock(&sst_lock); -+ - return 0; - } - EXPORT_SYMBOL_GPL(sst_unregister_dsp); -@@ -103,21 +102,14 @@ static int sst_media_digital_mute(struct snd_soc_dai *dai, int mute, int stream) - void sst_set_stream_status(struct sst_runtime_stream *stream, - int state) - { -- unsigned long flags; -- spin_lock_irqsave(&stream->status_lock, flags); -+ guard(spinlock_irqsave)(&stream->status_lock); - stream->stream_status = state; -- spin_unlock_irqrestore(&stream->status_lock, flags); - } - - static inline int sst_get_stream_status(struct sst_runtime_stream *stream) - { -- int state; -- unsigned long flags; -- -- spin_lock_irqsave(&stream->status_lock, flags); -- state = stream->stream_status; -- spin_unlock_irqrestore(&stream->status_lock, flags); -- return state; -+ guard(spinlock_irqsave)(&stream->status_lock); -+ return stream->stream_status; - } - - static void sst_fill_alloc_params(struct snd_pcm_substream *substream, -@@ -244,12 +236,7 @@ static int sst_platform_alloc_stream(struct snd_pcm_substream *substream, - - stream->stream_info.str_id = str_params.stream_id; - -- ret_val = stream->ops->open(sst->dev, &str_params); -- if (ret_val <= 0) -- return ret_val; -- -- -- return ret_val; -+ return stream->ops->open(sst->dev, &str_params); - } - - static void sst_period_elapsed(void *arg) -@@ -304,7 +291,7 @@ static int sst_media_open(struct snd_pcm_substream *substream, - { - int ret_val = 0; - struct snd_pcm_runtime *runtime = substream->runtime; -- struct sst_runtime_stream *stream; -+ struct sst_runtime_stream *stream __free(kfree) = NULL; - - stream = kzalloc_obj(*stream); - if (!stream) -@@ -312,15 +299,14 @@ static int sst_media_open(struct snd_pcm_substream *substream, - spin_lock_init(&stream->status_lock); - - /* get the sst ops */ -- mutex_lock(&sst_lock); -- if (!sst || -- !try_module_get(sst->dev->driver->owner)) { -- dev_err(dai->dev, "no device available to run\n"); -- ret_val = -ENODEV; -- goto out_ops; -+ scoped_guard(mutex, &sst_lock) { -+ if (!sst || -+ !try_module_get(sst->dev->driver->owner)) { -+ dev_err(dai->dev, "no device available to run\n"); -+ return -ENODEV; -+ } -+ stream->ops = sst->ops; - } -- stream->ops = sst->ops; -- mutex_unlock(&sst_lock); - - stream->stream_info.str_id = 0; - -@@ -330,7 +316,7 @@ static int sst_media_open(struct snd_pcm_substream *substream, - - ret_val = power_up_sst(stream); - if (ret_val < 0) -- goto out_power_up; -+ return ret_val; - - /* - * Make sure the period to be multiple of 1ms to align the -@@ -347,12 +333,14 @@ static int sst_media_open(struct snd_pcm_substream *substream, - snd_pcm_hw_constraint_step(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_PERIODS, 2); - -- return snd_pcm_hw_constraint_integer(runtime, -- SNDRV_PCM_HW_PARAM_PERIODS); --out_ops: -- mutex_unlock(&sst_lock); --out_power_up: -- kfree(stream); -+ ret_val = snd_pcm_hw_constraint_integer(runtime, -+ SNDRV_PCM_HW_PARAM_PERIODS); -+ -+ if (ret_val < 0) -+ return ret_val; -+ -+ stream = NULL; -+ - return ret_val; - } - -diff --git a/sound/soc/intel/atom/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c ---- a/sound/soc/intel/atom/sst/sst_ipc.c -+++ b/sound/soc/intel/atom/sst/sst_ipc.c -@@ -11,6 +11,7 @@ - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ -+#include - #include - #include - #include -@@ -180,9 +181,8 @@ void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx) - union interrupt_reg_mrfld isr; - union interrupt_reg_mrfld imr; - union ipc_header_mrfld clear_ipc; -- unsigned long irq_flags; - -- spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags); -+ guard(spinlock_irqsave)(&sst_drv_ctx->ipc_spin_lock); - imr.full = sst_shim_read64(sst_drv_ctx->shim, SST_IMRX); - isr.full = sst_shim_read64(sst_drv_ctx->shim, SST_ISRX); - -@@ -200,7 +200,6 @@ void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx) - /* un mask busy interrupt */ - imr.part.busy_interrupt = 0; - sst_shim_write64(sst_drv_ctx->shim, SST_IMRX, imr.full); -- spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); - } - - -diff --git a/sound/soc/intel/atom/sst/sst_pvt.c b/sound/soc/intel/atom/sst/sst_pvt.c ---- a/sound/soc/intel/atom/sst/sst_pvt.c -+++ b/sound/soc/intel/atom/sst/sst_pvt.c -@@ -11,6 +11,7 @@ - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ -+#include - #include - #include - #include -@@ -64,9 +65,8 @@ u64 sst_shim_read64(void __iomem *addr, int offset) - void sst_set_fw_state_locked( - struct intel_sst_drv *sst_drv_ctx, int sst_state) - { -- mutex_lock(&sst_drv_ctx->sst_lock); -+ guard(mutex)(&sst_drv_ctx->sst_lock); - sst_drv_ctx->sst_state = sst_state; -- mutex_unlock(&sst_drv_ctx->sst_lock); - } - - /* -@@ -302,18 +302,17 @@ int sst_assign_pvt_id(struct intel_sst_drv *drv) - { - int local; - -- spin_lock(&drv->block_lock); -+ guard(spinlock)(&drv->block_lock); - /* find first zero index from lsb */ - local = ffz(drv->pvt_id); - dev_dbg(drv->dev, "pvt_id assigned --> %d\n", local); - if (local >= SST_MAX_BLOCKS){ -- spin_unlock(&drv->block_lock); - dev_err(drv->dev, "PVT _ID error: no free id blocks "); - return -EINVAL; - } - /* toggle the index */ - change_bit(local, &drv->pvt_id); -- spin_unlock(&drv->block_lock); -+ - return local; - } - -diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c ---- a/sound/soc/intel/avs/apl.c -+++ b/sound/soc/intel/avs/apl.c -@@ -6,6 +6,7 @@ - // Amadeusz Slawinski - // - -+#include - #include - #include - #include -@@ -190,7 +191,7 @@ static bool avs_apl_lp_streaming(struct avs_dev *adev) - { - struct avs_path *path; - -- spin_lock(&adev->path_list_lock); -+ guard(spinlock)(&adev->path_list_lock); - /* Any gateway without buffer allocated in LP area disqualifies D0IX. */ - list_for_each_entry(path, &adev->path_list, node) { - struct avs_path_pipeline *ppl; -@@ -210,14 +211,11 @@ static bool avs_apl_lp_streaming(struct avs_dev *adev) - if (cfg->copier.dma_type == INVALID_OBJECT_ID) - continue; - -- if (!mod->gtw_attrs.lp_buffer_alloc) { -- spin_unlock(&adev->path_list_lock); -+ if (!mod->gtw_attrs.lp_buffer_alloc) - return false; -- } - } - } - } -- spin_unlock(&adev->path_list_lock); - - return true; - } -diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h ---- a/sound/soc/intel/avs/avs.h -+++ b/sound/soc/intel/avs/avs.h -@@ -336,14 +336,13 @@ int avs_icl_load_basefw(struct avs_dev *adev, struct firmware *fw); - /* Soc component members */ - - struct avs_soc_component { -- struct snd_soc_component base; -+ struct snd_soc_component *base; - struct avs_tplg *tplg; - - struct list_head node; - }; - --#define to_avs_soc_component(comp) \ -- container_of(comp, struct avs_soc_component, base) -+#define to_avs_soc_component(comp) snd_soc_component_to_priv(comp) - - extern const struct snd_soc_dai_ops avs_dai_fe_ops; - -diff --git a/sound/soc/intel/avs/boards/da7219.c b/sound/soc/intel/avs/boards/da7219.c ---- a/sound/soc/intel/avs/boards/da7219.c -+++ b/sound/soc/intel/avs/boards/da7219.c -@@ -175,7 +175,6 @@ static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot, - if (!dl || !platform) - return -ENOMEM; - -- dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port); - dl->name = devm_kasprintf(dev, GFP_KERNEL, - AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot)); - dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL); -@@ -259,10 +258,8 @@ static int avs_da7219_probe(struct platform_device *pdev) - } - - static const struct platform_device_id avs_da7219_driver_ids[] = { -- { -- .name = "avs_da7219", -- }, -- {}, -+ { .name = "avs_da7219" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_da7219_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/dmic.c b/sound/soc/intel/avs/boards/dmic.c ---- a/sound/soc/intel/avs/boards/dmic.c -+++ b/sound/soc/intel/avs/boards/dmic.c -@@ -104,10 +104,8 @@ static int avs_dmic_probe(struct platform_device *pdev) - } - - static const struct platform_device_id avs_dmic_driver_ids[] = { -- { -- .name = "avs_dmic", -- }, -- {}, -+ { .name = "avs_dmic" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_dmic_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/es8336.c b/sound/soc/intel/avs/boards/es8336.c ---- a/sound/soc/intel/avs/boards/es8336.c -+++ b/sound/soc/intel/avs/boards/es8336.c -@@ -309,10 +309,8 @@ static int avs_es8336_probe(struct platform_device *pdev) - } - - static const struct platform_device_id avs_es8336_driver_ids[] = { -- { -- .name = "avs_es8336", -- }, -- {}, -+ { .name = "avs_es8336" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_es8336_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/hdaudio.c b/sound/soc/intel/avs/boards/hdaudio.c ---- a/sound/soc/intel/avs/boards/hdaudio.c -+++ b/sound/soc/intel/avs/boards/hdaudio.c -@@ -231,10 +231,8 @@ static int avs_hdaudio_probe(struct platform_device *pdev) - } - - static const struct platform_device_id avs_hdaudio_driver_ids[] = { -- { -- .name = "avs_hdaudio", -- }, -- {}, -+ { .name = "avs_hdaudio" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_hdaudio_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/i2s_test.c b/sound/soc/intel/avs/boards/i2s_test.c ---- a/sound/soc/intel/avs/boards/i2s_test.c -+++ b/sound/soc/intel/avs/boards/i2s_test.c -@@ -107,10 +107,8 @@ static int avs_i2s_test_probe(struct platform_device *pdev) - } - - static const struct platform_device_id avs_i2s_test_driver_ids[] = { -- { -- .name = "avs_i2s_test", -- }, -- {}, -+ { .name = "avs_i2s_test" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_i2s_test_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/max98357a.c b/sound/soc/intel/avs/boards/max98357a.c ---- a/sound/soc/intel/avs/boards/max98357a.c -+++ b/sound/soc/intel/avs/boards/max98357a.c -@@ -136,10 +136,8 @@ static int avs_max98357a_probe(struct platform_device *pdev) - } - - static const struct platform_device_id avs_max98357a_driver_ids[] = { -- { -- .name = "avs_max98357a", -- }, -- {}, -+ { .name = "avs_max98357a" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_max98357a_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/max98373.c b/sound/soc/intel/avs/boards/max98373.c ---- a/sound/soc/intel/avs/boards/max98373.c -+++ b/sound/soc/intel/avs/boards/max98373.c -@@ -191,10 +191,8 @@ static int avs_max98373_probe(struct platform_device *pdev) - } - - static const struct platform_device_id avs_max98373_driver_ids[] = { -- { -- .name = "avs_max98373", -- }, -- {}, -+ { .name = "avs_max98373" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_max98373_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/max98927.c b/sound/soc/intel/avs/boards/max98927.c ---- a/sound/soc/intel/avs/boards/max98927.c -+++ b/sound/soc/intel/avs/boards/max98927.c -@@ -188,10 +188,8 @@ static int avs_max98927_probe(struct platform_device *pdev) - } - - static const struct platform_device_id avs_max98927_driver_ids[] = { -- { -- .name = "avs_max98927", -- }, -- {}, -+ { .name = "avs_max98927" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_max98927_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/nau8825.c b/sound/soc/intel/avs/boards/nau8825.c ---- a/sound/soc/intel/avs/boards/nau8825.c -+++ b/sound/soc/intel/avs/boards/nau8825.c -@@ -293,10 +293,8 @@ static int avs_nau8825_probe(struct platform_device *pdev) - } - - static const struct platform_device_id avs_nau8825_driver_ids[] = { -- { -- .name = "avs_nau8825", -- }, -- {}, -+ { .name = "avs_nau8825" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_nau8825_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/pcm3168a.c b/sound/soc/intel/avs/boards/pcm3168a.c ---- a/sound/soc/intel/avs/boards/pcm3168a.c -+++ b/sound/soc/intel/avs/boards/pcm3168a.c -@@ -132,10 +132,8 @@ static int avs_pcm3168a_probe(struct platform_device *pdev) - } - - static const struct platform_device_id avs_pcm3168a_driver_ids[] = { -- { -- .name = "avs_pcm3168a", -- }, -- {}, -+ { .name = "avs_pcm3168a" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_pcm3168a_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/probe.c b/sound/soc/intel/avs/boards/probe.c ---- a/sound/soc/intel/avs/boards/probe.c -+++ b/sound/soc/intel/avs/boards/probe.c -@@ -64,7 +64,7 @@ static const struct platform_device_id avs_probe_mb_driver_ids[] = { - { - .name = "avs_probe_mb", - }, -- {}, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_probe_mb_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/rt274.c b/sound/soc/intel/avs/boards/rt274.c ---- a/sound/soc/intel/avs/boards/rt274.c -+++ b/sound/soc/intel/avs/boards/rt274.c -@@ -261,7 +261,7 @@ static const struct platform_device_id avs_rt274_driver_ids[] = { - { - .name = "avs_rt274", - }, -- {}, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_rt274_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/rt286.c b/sound/soc/intel/avs/boards/rt286.c ---- a/sound/soc/intel/avs/boards/rt286.c -+++ b/sound/soc/intel/avs/boards/rt286.c -@@ -231,7 +231,7 @@ static const struct platform_device_id avs_rt286_driver_ids[] = { - { - .name = "avs_rt286", - }, -- {}, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_rt286_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/rt298.c b/sound/soc/intel/avs/boards/rt298.c ---- a/sound/soc/intel/avs/boards/rt298.c -+++ b/sound/soc/intel/avs/boards/rt298.c -@@ -250,7 +250,7 @@ static const struct platform_device_id avs_rt298_driver_ids[] = { - { - .name = "avs_rt298", - }, -- {}, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_rt298_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/rt5514.c b/sound/soc/intel/avs/boards/rt5514.c ---- a/sound/soc/intel/avs/boards/rt5514.c -+++ b/sound/soc/intel/avs/boards/rt5514.c -@@ -178,7 +178,7 @@ static const struct platform_device_id avs_rt5514_driver_ids[] = { - { - .name = "avs_rt5514", - }, -- {}, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_rt5514_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/rt5640.c b/sound/soc/intel/avs/boards/rt5640.c ---- a/sound/soc/intel/avs/boards/rt5640.c -+++ b/sound/soc/intel/avs/boards/rt5640.c -@@ -252,7 +252,7 @@ static const struct platform_device_id avs_rt5640_driver_ids[] = { - { - .name = "avs_rt5640", - }, -- {}, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_rt5640_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/rt5663.c b/sound/soc/intel/avs/boards/rt5663.c ---- a/sound/soc/intel/avs/boards/rt5663.c -+++ b/sound/soc/intel/avs/boards/rt5663.c -@@ -249,7 +249,7 @@ static const struct platform_device_id avs_rt5663_driver_ids[] = { - { - .name = "avs_rt5663", - }, -- {}, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_rt5663_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/rt5682.c b/sound/soc/intel/avs/boards/rt5682.c ---- a/sound/soc/intel/avs/boards/rt5682.c -+++ b/sound/soc/intel/avs/boards/rt5682.c -@@ -325,7 +325,7 @@ static const struct platform_device_id avs_rt5682_driver_ids[] = { - { - .name = "avs_rt5682", - }, -- {}, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_rt5682_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/ssm4567.c b/sound/soc/intel/avs/boards/ssm4567.c ---- a/sound/soc/intel/avs/boards/ssm4567.c -+++ b/sound/soc/intel/avs/boards/ssm4567.c -@@ -180,7 +180,7 @@ static const struct platform_device_id avs_ssm4567_driver_ids[] = { - { - .name = "avs_ssm4567", - }, -- {}, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_ssm4567_driver_ids); - -diff --git a/sound/soc/intel/avs/control.c b/sound/soc/intel/avs/control.c ---- a/sound/soc/intel/avs/control.c -+++ b/sound/soc/intel/avs/control.c -@@ -27,7 +27,7 @@ static struct avs_path_module *avs_get_volume_module(struct avs_dev *adev, u32 i - struct avs_path_pipeline *ppl; - struct avs_path_module *mod; - -- spin_lock(&adev->path_list_lock); -+ guard(spinlock)(&adev->path_list_lock); - list_for_each_entry(path, &adev->path_list, node) { - list_for_each_entry(ppl, &path->ppl_list, node) { - list_for_each_entry(mod, &ppl->mod_list, node) { -@@ -35,14 +35,11 @@ static struct avs_path_module *avs_get_volume_module(struct avs_dev *adev, u32 i - - if ((guid_equal(type, &AVS_PEAKVOL_MOD_UUID) || - guid_equal(type, &AVS_GAIN_MOD_UUID)) && -- mod->template->ctl_id == id) { -- spin_unlock(&adev->path_list_lock); -+ mod->template->ctl_id == id) - return mod; -- } - } - } - } -- spin_unlock(&adev->path_list_lock); - - return NULL; - } -diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c ---- a/sound/soc/intel/avs/core.c -+++ b/sound/soc/intel/avs/core.c -@@ -14,6 +14,7 @@ - // foundation of this driver - // - -+#include - #include - #include - #include -@@ -273,7 +274,7 @@ static irqreturn_t avs_hda_interrupt(struct hdac_bus *bus) - if (snd_hdac_bus_handle_stream_irq(bus, status, hdac_update_stream)) - ret = IRQ_HANDLED; - -- spin_lock_irq(&bus->reg_lock); -+ guard(spinlock_irq)(&bus->reg_lock); - /* Clear RIRB interrupt. */ - status = snd_hdac_chip_readb(bus, RIRBSTS); - if (status & RIRB_INT_MASK) { -@@ -283,7 +284,6 @@ static irqreturn_t avs_hda_interrupt(struct hdac_bus *bus) - ret = IRQ_HANDLED; - } - -- spin_unlock_irq(&bus->reg_lock); - return ret; - } - -diff --git a/sound/soc/intel/avs/debug.h b/sound/soc/intel/avs/debug.h ---- a/sound/soc/intel/avs/debug.h -+++ b/sound/soc/intel/avs/debug.h -@@ -9,6 +9,7 @@ - #ifndef __SOUND_SOC_INTEL_AVS_DEBUG_H - #define __SOUND_SOC_INTEL_AVS_DEBUG_H - -+#include - #include "messages.h" - #include "registers.h" - -@@ -26,14 +27,9 @@ struct avs_dev; - - static inline int avs_log_buffer_status_locked(struct avs_dev *adev, union avs_notify_msg *msg) - { -- unsigned long flags; -- int ret; -+ guard(spinlock_irqsave)(&adev->trace_lock); - -- spin_lock_irqsave(&adev->trace_lock, flags); -- ret = avs_dsp_op(adev, log_buffer_status, msg); -- spin_unlock_irqrestore(&adev->trace_lock, flags); -- -- return ret; -+ return avs_dsp_op(adev, log_buffer_status, msg); - } - - struct avs_apl_log_buffer_layout { -diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c ---- a/sound/soc/intel/avs/debugfs.c -+++ b/sound/soc/intel/avs/debugfs.c -@@ -6,6 +6,7 @@ - // Amadeusz Slawinski - // - -+#include - #include - #include - #include -@@ -251,24 +252,22 @@ static int strace_release(struct inode *inode, struct file *file) - union avs_notify_msg msg = AVS_NOTIFICATION(LOG_BUFFER_STATUS); - struct avs_dev *adev = file->private_data; - unsigned long resource_mask; -- unsigned long flags, i; -+ unsigned long i; - u32 num_cores; - - resource_mask = adev->logged_resources; - num_cores = adev->hw_cfg.dsp_cores; - -- spin_lock_irqsave(&adev->trace_lock, flags); -+ scoped_guard(spinlock_irqsave, &adev->trace_lock) { -+ /* Gather any remaining logs. */ -+ for_each_set_bit(i, &resource_mask, num_cores) { -+ msg.log.core = i; -+ avs_dsp_op(adev, log_buffer_status, &msg); -+ } - -- /* Gather any remaining logs. */ -- for_each_set_bit(i, &resource_mask, num_cores) { -- msg.log.core = i; -- avs_dsp_op(adev, log_buffer_status, &msg); -+ kfifo_free(&adev->trace_fifo); - } - -- kfifo_free(&adev->trace_fifo); -- -- spin_unlock_irqrestore(&adev->trace_lock, flags); -- - module_put(adev->dev->driver->owner); - return 0; - } -diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c ---- a/sound/soc/intel/avs/ipc.c -+++ b/sound/soc/intel/avs/ipc.c -@@ -6,6 +6,7 @@ - // Amadeusz Slawinski - // - -+#include - #include - #include - #include -@@ -99,39 +100,39 @@ static void avs_dsp_recovery(struct avs_dev *adev) - unsigned int core_mask; - int ret; - -- mutex_lock(&adev->comp_list_mutex); -- /* disconnect all running streams */ -- list_for_each_entry(acomp, &adev->comp_list, node) { -- struct snd_soc_pcm_runtime *rtd; -- struct snd_soc_card *card; -+ scoped_guard(mutex, &adev->comp_list_mutex) { -+ /* disconnect all running streams */ -+ list_for_each_entry(acomp, &adev->comp_list, node) { -+ struct snd_soc_pcm_runtime *rtd; -+ struct snd_soc_card *card; - -- card = acomp->base.card; -- if (!card) -- continue; -- -- for_each_card_rtds(card, rtd) { -- struct snd_pcm *pcm; -- int dir; -- -- pcm = rtd->pcm; -- if (!pcm || rtd->dai_link->no_pcm) -+ card = acomp->base->card; -+ if (!card) - continue; - -- for_each_pcm_streams(dir) { -- struct snd_pcm_substream *substream; -+ for_each_card_rtds(card, rtd) { -+ struct snd_pcm *pcm; -+ int dir; - -- substream = pcm->streams[dir].substream; -- if (!substream || !substream->runtime) -+ pcm = rtd->pcm; -+ if (!pcm || rtd->dai_link->no_pcm) - continue; - -- /* No need for _irq() as we are in nonatomic context. */ -- snd_pcm_stream_lock(substream); -- snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); -- snd_pcm_stream_unlock(substream); -+ for_each_pcm_streams(dir) { -+ struct snd_pcm_substream *substream; -+ -+ substream = pcm->streams[dir].substream; -+ if (!substream || !substream->runtime) -+ continue; -+ -+ /* No need for _irq() as we are in nonatomic context. */ -+ snd_pcm_stream_lock(substream); -+ snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); -+ snd_pcm_stream_unlock(substream); -+ } - } - } - } -- mutex_unlock(&adev->comp_list_mutex); - - /* forcibly shutdown all cores */ - core_mask = GENMASK(adev->hw_cfg.dsp_cores - 1, 0); -@@ -397,7 +398,7 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request - if (!ipc->ready) - return -EPERM; - -- mutex_lock(&ipc->msg_mutex); -+ guard(mutex)(&ipc->msg_mutex); - - spin_lock(&ipc->rx_lock); - avs_ipc_msg_init(ipc, reply); -@@ -412,7 +413,7 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request - /* Same treatment as on exception, just stack_dump=0. */ - avs_dsp_exception_caught(adev, &msg); - } -- goto exit; -+ return ret; - } - - ret = ipc->rx.rsp.status; -@@ -436,8 +437,6 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request - memcpy(reply->data, ipc->rx.data, reply->size); - } - --exit: -- mutex_unlock(&ipc->msg_mutex); - return ret; - } - -@@ -501,7 +500,7 @@ static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *req - struct avs_ipc *ipc = adev->ipc; - int ret; - -- mutex_lock(&ipc->msg_mutex); -+ guard(mutex)(&ipc->msg_mutex); - - spin_lock(&ipc->rx_lock); - avs_ipc_msg_init(ipc, NULL); -@@ -522,8 +521,6 @@ static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *req - dev_err(adev->dev, "%s (0x%08x 0x%08x) failed: %d\n", - name, request->glb.primary, request->glb.ext.val, ret); - -- mutex_unlock(&ipc->msg_mutex); -- - return ret; - } - -diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c ---- a/sound/soc/intel/avs/loader.c -+++ b/sound/soc/intel/avs/loader.c -@@ -6,6 +6,7 @@ - // Amadeusz Slawinski - // - -+#include - #include - #include - #include -@@ -630,15 +631,15 @@ static int avs_load_firmware(struct avs_dev *adev, bool purge) - if (ret) - goto reenable_gating; - -- mutex_lock(&adev->comp_list_mutex); -- list_for_each_entry(acomp, &adev->comp_list, node) { -- struct avs_tplg *tplg = acomp->tplg; -+ scoped_guard(mutex, &adev->comp_list_mutex) { -+ list_for_each_entry(acomp, &adev->comp_list, node) { -+ struct avs_tplg *tplg = acomp->tplg; - -- ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs); -- if (ret < 0) -- break; -+ ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs); -+ if (ret < 0) -+ break; -+ } - } -- mutex_unlock(&adev->comp_list_mutex); - - reenable_gating: - avs_hda_l1sen_enable(adev, true); -diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c ---- a/sound/soc/intel/avs/path.c -+++ b/sound/soc/intel/avs/path.c -@@ -6,6 +6,7 @@ - // Amadeusz Slawinski - // - -+#include - #include - #include - #include -@@ -69,16 +70,13 @@ avs_path_find_path(struct avs_dev *adev, const char *name, u32 template_id) - if (!template) - return NULL; - -- spin_lock(&adev->path_list_lock); -+ guard(spinlock)(&adev->path_list_lock); - /* Only one variant of given path template may be instantiated at a time. */ - list_for_each_entry(path, &adev->path_list, node) { -- if (path->template->owner == template) { -- spin_unlock(&adev->path_list_lock); -+ if (path->template->owner == template) - return path; -- } - } - -- spin_unlock(&adev->path_list_lock); - return NULL; - } - -@@ -1305,7 +1303,7 @@ void avs_path_free(struct avs_path *path) - struct avs_path *cpath, *csave; - struct avs_dev *adev = path->owner; - -- mutex_lock(&adev->path_mutex); -+ guard(mutex)(&adev->path_mutex); - - /* Free all condpaths this path spawned. */ - list_for_each_entry_safe(cpath, csave, &path->source_list, source_node) -@@ -1314,8 +1312,6 @@ void avs_path_free(struct avs_path *path) - avs_condpath_free(path->owner, cpath); - - avs_path_free_unlocked(path); -- -- mutex_unlock(&adev->path_mutex); - } - - struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id, -@@ -1334,13 +1330,13 @@ struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id, - } - - /* Serialize path and its components creation. */ -- mutex_lock(&adev->path_mutex); -+ guard(mutex)(&adev->path_mutex); - /* Satisfy needs of avs_path_find_tplg(). */ -- mutex_lock(&adev->comp_list_mutex); -+ guard(mutex)(&adev->comp_list_mutex); - - path = avs_path_create_unlocked(adev, dma_id, variant); - if (IS_ERR(path)) -- goto exit; -+ return path; - - ret = avs_condpaths_walk_all(adev, path); - if (ret) { -@@ -1348,10 +1344,6 @@ struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id, - path = ERR_PTR(ret); - } - --exit: -- mutex_unlock(&adev->comp_list_mutex); -- mutex_unlock(&adev->path_mutex); -- - return path; - } - -@@ -1496,15 +1488,13 @@ static void avs_condpaths_pause(struct avs_dev *adev, struct avs_path *path) - { - struct avs_path *cpath; - -- mutex_lock(&adev->path_mutex); -+ guard(mutex)(&adev->path_mutex); - - /* If either source or sink stops, so do the attached conditional paths. */ - list_for_each_entry(cpath, &path->source_list, source_node) - avs_condpath_pause(adev, cpath); - list_for_each_entry(cpath, &path->sink_list, sink_node) - avs_condpath_pause(adev, cpath); -- -- mutex_unlock(&adev->path_mutex); - } - - int avs_path_pause(struct avs_path *path) -@@ -1560,7 +1550,7 @@ static void avs_condpaths_run(struct avs_dev *adev, struct avs_path *path, int t - { - struct avs_path *cpath; - -- mutex_lock(&adev->path_mutex); -+ guard(mutex)(&adev->path_mutex); - - /* Run conditional paths only if source and sink are both running. */ - list_for_each_entry(cpath, &path->source_list, source_node) -@@ -1572,8 +1562,6 @@ static void avs_condpaths_run(struct avs_dev *adev, struct avs_path *path, int t - if (cpath->source->state == AVS_PPL_STATE_RUNNING && - cpath->sink->state == AVS_PPL_STATE_RUNNING) - avs_condpath_run(adev, cpath, trigger); -- -- mutex_unlock(&adev->path_mutex); - } - - int avs_path_run(struct avs_path *path, int trigger) -diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c ---- a/sound/soc/intel/avs/pcm.c -+++ b/sound/soc/intel/avs/pcm.c -@@ -958,7 +958,7 @@ static const struct file_operations topology_name_fops = { - static int avs_component_load_libraries(struct avs_soc_component *acomp) - { - struct avs_tplg *tplg = acomp->tplg; -- struct avs_dev *adev = to_avs_dev(acomp->base.dev); -+ struct avs_dev *adev = to_avs_dev(acomp->base->dev); - int ret; - - if (!tplg->num_libs) -@@ -1387,25 +1387,28 @@ int avs_register_component(struct device *dev, const char *name, - struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais) - { - struct avs_soc_component *acomp; -- int ret; -+ const char *comp_name; - - acomp = devm_kzalloc(dev, sizeof(*acomp), GFP_KERNEL); - if (!acomp) - return -ENOMEM; - -- acomp->base.name = devm_kstrdup(dev, name, GFP_KERNEL); -- if (!acomp->base.name) -+ acomp->base = snd_soc_component_alloc(dev); -+ if (!acomp->base) -+ return -ENOMEM; -+ -+ comp_name = devm_kstrdup(dev, name, GFP_KERNEL); -+ if (!comp_name) - return -ENOMEM; - - INIT_LIST_HEAD(&acomp->node); - - drv->use_dai_pcm_id = !obsolete_card_names; - -- ret = snd_soc_component_initialize(&acomp->base, drv, dev); -- if (ret < 0) -- return ret; -+ snd_soc_component_set_name(acomp->base, comp_name); -+ snd_soc_component_set_priv(acomp->base, acomp); - -- return snd_soc_add_component(&acomp->base, cpu_dais, num_cpu_dais); -+ return snd_soc_register_component(acomp->base, drv, cpu_dais, num_cpu_dais); - } - - static struct snd_soc_dai_driver dmic_cpu_dais[] = { diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c --- a/sound/soc/intel/avs/probes.c +++ b/sound/soc/intel/avs/probes.c @@ -38060,1561 +11352,6 @@ diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c if (bps < 0) return bps; format_val = snd_hdac_stream_format(params->codec.ch_out, bps, params->codec.sample_rate); -@@ -296,19 +296,19 @@ static const struct snd_soc_component_driver avs_probe_component_driver = { - int avs_register_probe_component(struct avs_dev *adev, const char *name) - { - struct snd_soc_component *component; -- int ret; -+ const char *comp_name; - -- component = devm_kzalloc(adev->dev, sizeof(*component), GFP_KERNEL); -+ component = snd_soc_component_alloc(adev->dev); - if (!component) - return -ENOMEM; - -- component->name = devm_kstrdup(adev->dev, name, GFP_KERNEL); -- if (!component->name) -+ comp_name = devm_kstrdup(adev->dev, name, GFP_KERNEL); -+ if (!comp_name) - return -ENOMEM; - -- ret = snd_soc_component_initialize(component, &avs_probe_component_driver, adev->dev); -- if (ret) -- return ret; -+ snd_soc_component_set_name(component, comp_name); - -- return snd_soc_add_component(component, probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais)); -+ return snd_soc_register_component(component, -+ &avs_probe_component_driver, -+ probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais)); - } -diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c ---- a/sound/soc/intel/avs/topology.c -+++ b/sound/soc/intel/avs/topology.c -@@ -2222,7 +2222,7 @@ struct avs_tplg *avs_tplg_new(struct snd_soc_component *comp) - - int avs_load_topology(struct snd_soc_component *comp, const char *filename) - { -- const struct firmware *fw; -+ const struct firmware *fw __free(firmware) = NULL; - int ret; - - ret = request_firmware(&fw, filename, comp->dev); -@@ -2235,7 +2235,6 @@ int avs_load_topology(struct snd_soc_component *comp, const char *filename) - if (ret < 0) - dev_err(comp->dev, "load topology \"%s\" failed: %d\n", filename, ret); - -- release_firmware(fw); - return ret; - } - -diff --git a/sound/soc/intel/avs/utils.c b/sound/soc/intel/avs/utils.c ---- a/sound/soc/intel/avs/utils.c -+++ b/sound/soc/intel/avs/utils.c -@@ -6,6 +6,7 @@ - // Amadeusz Slawinski - // - -+#include - #include - #include - #include -@@ -48,13 +49,12 @@ int avs_get_module_entry(struct avs_dev *adev, const guid_t *uuid, struct avs_mo - { - int idx; - -- mutex_lock(&adev->modres_mutex); -+ guard(mutex)(&adev->modres_mutex); - - idx = avs_module_entry_index(adev, uuid); - if (idx >= 0) - memcpy(entry, &adev->mods_info->entries[idx], sizeof(*entry)); - -- mutex_unlock(&adev->modres_mutex); - return (idx < 0) ? idx : 0; - } - -@@ -62,13 +62,12 @@ int avs_get_module_id_entry(struct avs_dev *adev, u32 module_id, struct avs_modu - { - int idx; - -- mutex_lock(&adev->modres_mutex); -+ guard(mutex)(&adev->modres_mutex); - - idx = avs_module_id_entry_index(adev, module_id); - if (idx >= 0) - memcpy(entry, &adev->mods_info->entries[idx], sizeof(*entry)); - -- mutex_unlock(&adev->modres_mutex); - return (idx < 0) ? idx : 0; - } - -@@ -86,13 +85,12 @@ bool avs_is_module_ida_empty(struct avs_dev *adev, u32 module_id) - bool ret = false; - int idx; - -- mutex_lock(&adev->modres_mutex); -+ guard(mutex)(&adev->modres_mutex); - - idx = avs_module_id_entry_index(adev, module_id); - if (idx >= 0) - ret = ida_is_empty(adev->mod_idas[idx]); - -- mutex_unlock(&adev->modres_mutex); - return ret; - } - -@@ -163,68 +161,57 @@ int avs_module_info_init(struct avs_dev *adev, bool purge) - if (ret) - return AVS_IPC_RET(ret); - -- mutex_lock(&adev->modres_mutex); -+ guard(mutex)(&adev->modres_mutex); - - ret = avs_module_ida_alloc(adev, info, purge); - if (ret < 0) { - dev_err(adev->dev, "initialize module idas failed: %d\n", ret); -- goto exit; -+ return ret; - } - - /* Refresh current information with newly received table. */ - kfree(adev->mods_info); - adev->mods_info = info; - --exit: -- mutex_unlock(&adev->modres_mutex); - return ret; - } - - void avs_module_info_free(struct avs_dev *adev) - { -- mutex_lock(&adev->modres_mutex); -+ guard(mutex)(&adev->modres_mutex); - - avs_module_ida_destroy(adev); - kfree(adev->mods_info); - adev->mods_info = NULL; -- -- mutex_unlock(&adev->modres_mutex); - } - - int avs_module_id_alloc(struct avs_dev *adev, u16 module_id) - { -- int ret, idx, max_id; -+ int idx, max_id; - -- mutex_lock(&adev->modres_mutex); -+ guard(mutex)(&adev->modres_mutex); - - idx = avs_module_id_entry_index(adev, module_id); - if (idx == -ENOENT) { - dev_err(adev->dev, "invalid module id: %d", module_id); -- ret = -EINVAL; -- goto exit; -+ return -EINVAL; - } - max_id = adev->mods_info->entries[idx].instance_max_count - 1; -- ret = ida_alloc_max(adev->mod_idas[idx], max_id, GFP_KERNEL); --exit: -- mutex_unlock(&adev->modres_mutex); -- return ret; -+ -+ return ida_alloc_max(adev->mod_idas[idx], max_id, GFP_KERNEL); - } - - void avs_module_id_free(struct avs_dev *adev, u16 module_id, u8 instance_id) - { - int idx; - -- mutex_lock(&adev->modres_mutex); -+ guard(mutex)(&adev->modres_mutex); - - idx = avs_module_id_entry_index(adev, module_id); -- if (idx == -ENOENT) { -+ if (idx == -ENOENT) - dev_err(adev->dev, "invalid module id: %d", module_id); -- goto exit; -- } -- -- ida_free(adev->mod_idas[idx], instance_id); --exit: -- mutex_unlock(&adev->modres_mutex); -+ else -+ ida_free(adev->mod_idas[idx], instance_id); - } - - /* -diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c ---- a/sound/soc/intel/boards/sof_sdw.c -+++ b/sound/soc/intel/boards/sof_sdw.c -@@ -1288,7 +1288,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) - goto err_dai; - } - -- ret = asoc_sdw_parse_sdw_endpoints(card, sof_aux, sof_dais, sof_ends, &num_confs); -+ ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_confs); - if (ret < 0) - goto err_end; - -@@ -1483,12 +1483,12 @@ static int mc_probe(struct platform_device *pdev) - dmi_check_system(sof_sdw_quirk_table); - - if (quirk_override != -1) { -- dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n", -+ dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n", - sof_sdw_quirk, quirk_override); - sof_sdw_quirk = quirk_override; - } - -- log_quirks(card->dev); -+ log_quirks(&pdev->dev); - - ctx->mc_quirk = sof_sdw_quirk; - /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ -@@ -1507,13 +1507,13 @@ static int mc_probe(struct platform_device *pdev) - for (i = 0; i < ctx->codec_info_list_count; i++) - amp_num += codec_info_list[i].amp_num; - -- card->components = devm_kasprintf(card->dev, GFP_KERNEL, -+ card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, - " cfg-amp:%d", amp_num); - if (!card->components) - return -ENOMEM; - - if (mach->mach_params.dmic_num) { -- card->components = devm_kasprintf(card->dev, GFP_KERNEL, -+ card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, - "%s mic:dmic cfg-mics:%d", - card->components, - mach->mach_params.dmic_num); -@@ -1524,7 +1524,7 @@ static int mc_probe(struct platform_device *pdev) - /* Register the card */ - ret = devm_snd_soc_register_card(card->dev, card); - if (ret) { -- dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret); -+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed %d\n", ret); - asoc_sdw_mc_dailink_exit_loop(card); - return ret; - } -@@ -1542,8 +1542,8 @@ static void mc_remove(struct platform_device *pdev) - } - - static const struct platform_device_id mc_id_table[] = { -- { "sof_sdw", }, -- {} -+ { .name = "sof_sdw" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, mc_id_table); - -diff --git a/sound/soc/intel/catpt/core.h b/sound/soc/intel/catpt/core.h ---- a/sound/soc/intel/catpt/core.h -+++ b/sound/soc/intel/catpt/core.h -@@ -139,9 +139,7 @@ int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request, - - int catpt_first_boot_firmware(struct catpt_dev *cdev); - int catpt_boot_firmware(struct catpt_dev *cdev, bool restore); --int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan); --int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan); --int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan); -+int catpt_store_firmware_context(struct catpt_dev *cdev); - int catpt_coredump(struct catpt_dev *cdev); - - #include -diff --git a/sound/soc/intel/catpt/device.c b/sound/soc/intel/catpt/device.c ---- a/sound/soc/intel/catpt/device.c -+++ b/sound/soc/intel/catpt/device.c -@@ -28,44 +28,17 @@ - static int catpt_do_suspend(struct device *dev) - { - struct catpt_dev *cdev = dev_get_drvdata(dev); -- struct dma_chan *chan; - int ret; - -- chan = catpt_dma_request_config_chan(cdev); -- if (IS_ERR(chan)) -- return PTR_ERR(chan); -- - memset(&cdev->dx_ctx, 0, sizeof(cdev->dx_ctx)); - ret = catpt_ipc_enter_dxstate(cdev, CATPT_DX_STATE_D3, &cdev->dx_ctx); -- if (ret) { -- ret = CATPT_IPC_RET(ret); -- goto release_dma_chan; -- } -- -- ret = catpt_dsp_stall(cdev, true); - if (ret) -- goto release_dma_chan; -+ return CATPT_IPC_RET(ret); - -- ret = catpt_store_memdumps(cdev, chan); -- if (ret) { -- dev_err(cdev->dev, "store memdumps failed: %d\n", ret); -- goto release_dma_chan; -- } -- -- ret = catpt_store_module_states(cdev, chan); -- if (ret) { -- dev_err(cdev->dev, "store module states failed: %d\n", ret); -- goto release_dma_chan; -- } -- -- ret = catpt_store_streams_context(cdev, chan); -- if (ret) -- dev_err(cdev->dev, "store streams ctx failed: %d\n", ret); -- --release_dma_chan: -- dma_release_channel(chan); -+ ret = catpt_store_firmware_context(cdev); - if (ret) - return ret; -+ - return catpt_dsp_power_down(cdev); - } - -diff --git a/sound/soc/intel/catpt/loader.c b/sound/soc/intel/catpt/loader.c ---- a/sound/soc/intel/catpt/loader.c -+++ b/sound/soc/intel/catpt/loader.c -@@ -26,7 +26,7 @@ struct catpt_fw_hdr { - u32 reserved[4]; - } __packed; - --struct catpt_fw_mod_hdr { -+struct catpt_fw_module_hdr { - char signature[FW_SIGNATURE_SIZE]; - u32 mod_size; - u32 blocks; -@@ -81,7 +81,7 @@ catpt_request_region(struct resource *root, resource_size_t size) - return __request_region(root, addr, size, NULL, 0); - } - --int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) -+static int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) - { - struct catpt_stream_runtime *stream; - -@@ -108,7 +108,7 @@ int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) - return 0; - } - --int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan) -+static int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan) - { - int i; - -@@ -138,7 +138,7 @@ int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan) - return 0; - } - --int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) -+static int catpt_store_dram_data(struct catpt_dev *cdev, struct dma_chan *chan) - { - int i; - -@@ -171,6 +171,39 @@ int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) - return 0; - } - -+int catpt_store_firmware_context(struct catpt_dev *cdev) -+{ -+ struct dma_chan *chan; -+ int ret; -+ -+ chan = catpt_dma_request_config_chan(cdev); -+ if (IS_ERR(chan)) -+ return PTR_ERR(chan); -+ -+ ret = catpt_dsp_stall(cdev, true); -+ if (ret) -+ goto exit; -+ -+ ret = catpt_store_dram_data(cdev, chan); -+ if (ret) { -+ dev_err(cdev->dev, "store memdumps failed: %d\n", ret); -+ goto exit; -+ } -+ -+ ret = catpt_store_module_states(cdev, chan); -+ if (ret) { -+ dev_err(cdev->dev, "store module states failed: %d\n", ret); -+ goto exit; -+ } -+ -+ ret = catpt_store_streams_context(cdev, chan); -+ if (ret) -+ dev_err(cdev->dev, "store streams ctx failed: %d\n", ret); -+exit: -+ dma_release_channel(chan); -+ return ret; -+} -+ - static int - catpt_restore_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) - { -@@ -199,7 +232,7 @@ catpt_restore_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) - return 0; - } - --static int catpt_restore_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) -+static int catpt_restore_dram_data(struct catpt_dev *cdev, struct dma_chan *chan) - { - int i; - -@@ -234,9 +267,9 @@ static int catpt_restore_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) - return 0; - } - --static int catpt_restore_fwimage(struct catpt_dev *cdev, -- struct dma_chan *chan, dma_addr_t paddr, -- struct catpt_fw_block_hdr *blk) -+static int catpt_restore_dram_rodata(struct catpt_dev *cdev, -+ struct dma_chan *chan, dma_addr_t paddr, -+ struct catpt_fw_block_hdr *blk) - { - struct resource r1 = {}; - int i; -@@ -324,28 +357,26 @@ static int catpt_load_block(struct catpt_dev *cdev, - - static int catpt_restore_basefw(struct catpt_dev *cdev, - struct dma_chan *chan, dma_addr_t paddr, -- struct catpt_fw_mod_hdr *basefw) -+ struct catpt_fw_module_hdr *basefw) - { -- u32 offset = sizeof(*basefw); -+ u32 off = sizeof(*basefw); - int ret, i; - - print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, - basefw, sizeof(*basefw), false); - -- /* restore basefw image */ -+ /* Restore IRAM and .rodata for DRAM based on the firmware image. */ - for (i = 0; i < basefw->blocks; i++) { - struct catpt_fw_block_hdr *blk; - -- blk = (struct catpt_fw_block_hdr *)((u8 *)basefw + offset); -+ blk = (struct catpt_fw_block_hdr *)((u8 *)basefw + off); - - switch (blk->ram_type) { - case CATPT_RAM_TYPE_IRAM: -- ret = catpt_load_block(cdev, chan, paddr + offset, -- blk, false); -+ ret = catpt_load_block(cdev, chan, paddr + off, blk, false); - break; - default: -- ret = catpt_restore_fwimage(cdev, chan, paddr + offset, -- blk); -+ ret = catpt_restore_dram_rodata(cdev, chan, paddr + off, blk); - break; - } - -@@ -354,11 +385,11 @@ static int catpt_restore_basefw(struct catpt_dev *cdev, - return ret; - } - -- offset += sizeof(*blk) + blk->size; -+ off += sizeof(*blk) + blk->size; - } - -- /* then proceed with memory dumps */ -- ret = catpt_restore_memdumps(cdev, chan); -+ /* Then proceed with DRAM .data saved before D3. */ -+ ret = catpt_restore_dram_data(cdev, chan); - if (ret) - dev_err(cdev->dev, "restore memdumps failed: %d\n", ret); - -@@ -367,9 +398,9 @@ static int catpt_restore_basefw(struct catpt_dev *cdev, - - static int catpt_restore_module(struct catpt_dev *cdev, - struct dma_chan *chan, dma_addr_t paddr, -- struct catpt_fw_mod_hdr *mod) -+ struct catpt_fw_module_hdr *mod) - { -- u32 offset = sizeof(*mod); -+ u32 off = sizeof(*mod); - int i; - - print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, -@@ -379,7 +410,7 @@ static int catpt_restore_module(struct catpt_dev *cdev, - struct catpt_fw_block_hdr *blk; - int ret; - -- blk = (struct catpt_fw_block_hdr *)((u8 *)mod + offset); -+ blk = (struct catpt_fw_block_hdr *)((u8 *)mod + off); - - switch (blk->ram_type) { - case CATPT_RAM_TYPE_INSTANCE: -@@ -390,7 +421,7 @@ static int catpt_restore_module(struct catpt_dev *cdev, - ALIGN(blk->size, 4)); - break; - default: -- ret = catpt_load_block(cdev, chan, paddr + offset, -+ ret = catpt_load_block(cdev, chan, paddr + off, - blk, false); - break; - } -@@ -400,7 +431,7 @@ static int catpt_restore_module(struct catpt_dev *cdev, - return ret; - } - -- offset += sizeof(*blk) + blk->size; -+ off += sizeof(*blk) + blk->size; - } - - return 0; -@@ -408,10 +439,10 @@ static int catpt_restore_module(struct catpt_dev *cdev, - - static int catpt_load_module(struct catpt_dev *cdev, - struct dma_chan *chan, dma_addr_t paddr, -- struct catpt_fw_mod_hdr *mod) -+ struct catpt_fw_module_hdr *mod) - { - struct catpt_module_type *type; -- u32 offset = sizeof(*mod); -+ u32 off = sizeof(*mod); - int i; - - print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, -@@ -423,9 +454,9 @@ static int catpt_load_module(struct catpt_dev *cdev, - struct catpt_fw_block_hdr *blk; - int ret; - -- blk = (struct catpt_fw_block_hdr *)((u8 *)mod + offset); -+ blk = (struct catpt_fw_block_hdr *)((u8 *)mod + off); - -- ret = catpt_load_block(cdev, chan, paddr + offset, blk, true); -+ ret = catpt_load_block(cdev, chan, paddr + off, blk, true); - if (ret) { - dev_err(cdev->dev, "load block failed: %d\n", ret); - return ret; -@@ -440,7 +471,7 @@ static int catpt_load_module(struct catpt_dev *cdev, - type->state_size = blk->size; - } - -- offset += sizeof(*blk) + blk->size; -+ off += sizeof(*blk) + blk->size; - } - - /* init module type static info */ -@@ -457,17 +488,17 @@ static int catpt_restore_firmware(struct catpt_dev *cdev, - struct dma_chan *chan, dma_addr_t paddr, - struct catpt_fw_hdr *fw) - { -- u32 offset = sizeof(*fw); -+ u32 off = sizeof(*fw); - int i; - - print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, - fw, sizeof(*fw), false); - - for (i = 0; i < fw->modules; i++) { -- struct catpt_fw_mod_hdr *mod; -+ struct catpt_fw_module_hdr *mod; - int ret; - -- mod = (struct catpt_fw_mod_hdr *)((u8 *)fw + offset); -+ mod = (struct catpt_fw_module_hdr *)((u8 *)fw + off); - if (strncmp(fw->signature, mod->signature, - FW_SIGNATURE_SIZE)) { - dev_err(cdev->dev, "module signature mismatch\n"); -@@ -479,12 +510,10 @@ static int catpt_restore_firmware(struct catpt_dev *cdev, - - switch (mod->module_id) { - case CATPT_MODID_BASE_FW: -- ret = catpt_restore_basefw(cdev, chan, paddr + offset, -- mod); -+ ret = catpt_restore_basefw(cdev, chan, paddr + off, mod); - break; - default: -- ret = catpt_restore_module(cdev, chan, paddr + offset, -- mod); -+ ret = catpt_restore_module(cdev, chan, paddr + off, mod); - break; - } - -@@ -493,7 +522,7 @@ static int catpt_restore_firmware(struct catpt_dev *cdev, - return ret; - } - -- offset += sizeof(*mod) + mod->mod_size; -+ off += sizeof(*mod) + mod->mod_size; - } - - return 0; -@@ -503,17 +532,17 @@ static int catpt_load_firmware(struct catpt_dev *cdev, - struct dma_chan *chan, dma_addr_t paddr, - struct catpt_fw_hdr *fw) - { -- u32 offset = sizeof(*fw); -+ u32 off = sizeof(*fw); - int i; - - print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, - fw, sizeof(*fw), false); - - for (i = 0; i < fw->modules; i++) { -- struct catpt_fw_mod_hdr *mod; -+ struct catpt_fw_module_hdr *mod; - int ret; - -- mod = (struct catpt_fw_mod_hdr *)((u8 *)fw + offset); -+ mod = (struct catpt_fw_module_hdr *)((u8 *)fw + off); - if (strncmp(fw->signature, mod->signature, - FW_SIGNATURE_SIZE)) { - dev_err(cdev->dev, "module signature mismatch\n"); -@@ -523,44 +552,40 @@ static int catpt_load_firmware(struct catpt_dev *cdev, - if (mod->module_id > CATPT_MODID_LAST) - return -EINVAL; - -- ret = catpt_load_module(cdev, chan, paddr + offset, mod); -+ ret = catpt_load_module(cdev, chan, paddr + off, mod); - if (ret) { - dev_err(cdev->dev, "load module failed: %d\n", ret); - return ret; - } - -- offset += sizeof(*mod) + mod->mod_size; -+ off += sizeof(*mod) + mod->mod_size; - } - - return 0; - } - --static int catpt_load_image(struct catpt_dev *cdev, struct dma_chan *chan, -- const char *name, const char *signature, -- bool restore) -+static int catpt_request_load_firmware(struct catpt_dev *cdev, struct dma_chan *chan, -+ const char *name, bool restore) - { - struct catpt_fw_hdr *fw; -- struct firmware *img; - dma_addr_t paddr; - void *vaddr; - int ret; - -- ret = request_firmware((const struct firmware **)&img, name, cdev->dev); -+ const struct firmware *img __free(firmware) = NULL; -+ ret = request_firmware(&img, name, cdev->dev); - if (ret) - return ret; - - fw = (struct catpt_fw_hdr *)img->data; -- if (strncmp(fw->signature, signature, FW_SIGNATURE_SIZE)) { -+ if (strncmp(fw->signature, FW_SIGNATURE, FW_SIGNATURE_SIZE)) { - dev_err(cdev->dev, "firmware signature mismatch\n"); -- ret = -EINVAL; -- goto release_fw; -+ return -EINVAL; - } - - vaddr = dma_alloc_coherent(cdev->dev, img->size, &paddr, GFP_KERNEL); -- if (!vaddr) { -- ret = -ENOMEM; -- goto release_fw; -- } -+ if (!vaddr) -+ return -ENOMEM; - - memcpy(vaddr, img->data, img->size); - fw = (struct catpt_fw_hdr *)vaddr; -@@ -570,12 +595,10 @@ static int catpt_load_image(struct catpt_dev *cdev, struct dma_chan *chan, - ret = catpt_load_firmware(cdev, chan, paddr, fw); - - dma_free_coherent(cdev->dev, img->size, vaddr, paddr); --release_fw: -- release_firmware(img); - return ret; - } - --static int catpt_load_images(struct catpt_dev *cdev, bool restore) -+static int catpt_request_dma_load_firmware(struct catpt_dev *cdev, bool restore) - { - struct dma_chan *chan; - int ret; -@@ -584,8 +607,7 @@ static int catpt_load_images(struct catpt_dev *cdev, bool restore) - if (IS_ERR(chan)) - return PTR_ERR(chan); - -- ret = catpt_load_image(cdev, chan, cdev->spec->fw_name, -- FW_SIGNATURE, restore); -+ ret = catpt_request_load_firmware(cdev, chan, cdev->spec->fw_name, restore); - if (ret) - goto release_dma_chan; - -@@ -605,7 +627,7 @@ int catpt_boot_firmware(struct catpt_dev *cdev, bool restore) - - catpt_dsp_stall(cdev, true); - -- ret = catpt_load_images(cdev, restore); -+ ret = catpt_request_dma_load_firmware(cdev, restore); - if (ret) { - dev_err(cdev->dev, "load binaries failed: %d\n", ret); - return ret; -@@ -619,10 +641,10 @@ int catpt_boot_firmware(struct catpt_dev *cdev, bool restore) - if (!ret) { - dev_err(cdev->dev, "firmware ready timeout\n"); - return -ETIMEDOUT; -- /* Wake up does not mean FW is ready, an exception could occur. */ -- } else if (!cdev->ipc.ready) { -- return -EREMOTEIO; - } -+ /* Wake up does not mean FW is ready, an exception could occur. */ -+ if (!cdev->ipc.ready) -+ return -EREMOTEIO; - - /* update sram pg & clock once done booting */ - catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask); -diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c ---- a/sound/soc/intel/catpt/pcm.c -+++ b/sound/soc/intel/catpt/pcm.c -@@ -75,8 +75,8 @@ static struct catpt_stream_template *catpt_topology[] = { - static struct catpt_stream_template * - catpt_get_stream_template(struct snd_pcm_substream *substream) - { -- struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream); -- struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtm, 0); -+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); -+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); - enum catpt_stream_type type; - - type = cpu_dai->driver->id; -@@ -159,11 +159,11 @@ static void catpt_stream_read_position(struct catpt_dev *cdev, - static void catpt_arrange_page_table(struct snd_pcm_substream *substream, - struct snd_dma_buffer *pgtbl) - { -- struct snd_pcm_runtime *rtm = substream->runtime; -+ struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_dma_buffer *databuf = snd_pcm_get_dma_buf(substream); - int i, pages; - -- pages = snd_sgbuf_aligned_pages(rtm->dma_bytes); -+ pages = snd_sgbuf_aligned_pages(runtime->dma_bytes); - - for (i = 0; i < pages; i++) { - u32 pfn, offset; -@@ -386,7 +386,7 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) - { -- struct snd_pcm_runtime *rtm = substream->runtime; -+ struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_dma_buffer *dmab; - struct catpt_stream_runtime *stream; - struct catpt_audio_format afmt; -@@ -412,8 +412,8 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream, - - memset(&rinfo, 0, sizeof(rinfo)); - rinfo.page_table_addr = stream->pgtbl.addr; -- rinfo.num_pages = DIV_ROUND_UP(rtm->dma_bytes, PAGE_SIZE); -- rinfo.size = rtm->dma_bytes; -+ rinfo.num_pages = DIV_ROUND_UP(runtime->dma_bytes, PAGE_SIZE); -+ rinfo.size = runtime->dma_bytes; - rinfo.offset = 0; - rinfo.ring_first_page_pfn = PFN_DOWN(snd_sgbuf_get_addr(dmab, 0)); - -@@ -544,11 +544,11 @@ void catpt_stream_update_position(struct catpt_dev *cdev, - struct catpt_notify_position *pos) - { - struct snd_pcm_substream *substream = stream->substream; -- struct snd_pcm_runtime *r = substream->runtime; -+ struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_uframes_t dsppos, newpos; - int ret; - -- dsppos = bytes_to_frames(r, pos->stream_position); -+ dsppos = bytes_to_frames(runtime, pos->stream_position); - - if (!stream->prepared) - goto exit; -@@ -556,8 +556,8 @@ void catpt_stream_update_position(struct catpt_dev *cdev, - if (stream->template->type != CATPT_STRM_TYPE_RENDER) - goto exit; - -- if (dsppos >= r->buffer_size / 2) -- newpos = r->buffer_size / 2; -+ if (dsppos >= runtime->buffer_size / 2) -+ newpos = runtime->buffer_size / 2; - else - newpos = 0; - /* -@@ -565,7 +565,7 @@ void catpt_stream_update_position(struct catpt_dev *cdev, - * (buffer half consumed) update wp to allow stream progression. - */ - ret = catpt_ipc_set_write_pos(cdev, stream->info.stream_hw_id, -- frames_to_bytes(r, newpos), -+ frames_to_bytes(runtime, newpos), - false, false); - if (ret) { - dev_err(cdev->dev, "update position for stream %d failed: %d\n", -@@ -600,11 +600,11 @@ static const struct snd_pcm_hardware catpt_pcm_hardware = { - }; - - static int catpt_component_pcm_new(struct snd_soc_component *component, -- struct snd_soc_pcm_runtime *rtm) -+ struct snd_soc_pcm_runtime *rtd) - { - struct catpt_dev *cdev = dev_get_drvdata(component->dev); - -- snd_pcm_set_managed_buffer_all(rtm->pcm, SNDRV_DMA_TYPE_DEV_SG, -+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV_SG, - cdev->dev, - catpt_pcm_hardware.buffer_bytes_max, - catpt_pcm_hardware.buffer_bytes_max); -@@ -615,9 +615,9 @@ static int catpt_component_pcm_new(struct snd_soc_component *component, - static int catpt_component_open(struct snd_soc_component *component, - struct snd_pcm_substream *substream) - { -- struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream); -+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - -- if (!rtm->dai_link->no_pcm) -+ if (!rtd->dai_link->no_pcm) - snd_soc_set_runtime_hwparams(substream, &catpt_pcm_hardware); - return 0; - } -@@ -626,13 +626,13 @@ static snd_pcm_uframes_t - catpt_component_pointer(struct snd_soc_component *component, - struct snd_pcm_substream *substream) - { -- struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream); -- struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtm, 0); -+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); -+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); - struct catpt_stream_runtime *stream; - struct catpt_dev *cdev = dev_get_drvdata(component->dev); - u32 pos; - -- if (rtm->dai_link->no_pcm) -+ if (rtd->dai_link->no_pcm) - return 0; - - stream = snd_soc_dai_get_dma_data(cpu_dai, substream); -@@ -650,10 +650,10 @@ static const struct snd_soc_dai_ops catpt_fe_dai_ops = { - .trigger = catpt_dai_trigger, - }; - --static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtm, -+static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_dai *dai) - { -- struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtm, 0); -+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); - struct catpt_ssp_device_format devfmt; - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); - int ret; -@@ -871,8 +871,7 @@ static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol) - return CATPT_IPC_RET(ret); - } - --static int catpt_volume_info(struct snd_kcontrol *kcontrol, -- struct snd_ctl_elem_info *uinfo) -+static int catpt_volume_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) - { - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = CATPT_CHANNELS_MAX; -@@ -1073,18 +1072,14 @@ int catpt_arm_stream_templates(struct catpt_dev *cdev) - int catpt_register_plat_component(struct catpt_dev *cdev) - { - struct snd_soc_component *component; -- int ret; - -- component = devm_kzalloc(cdev->dev, sizeof(*component), GFP_KERNEL); -+ component = snd_soc_component_alloc(cdev->dev); - if (!component) - return -ENOMEM; - -- ret = snd_soc_component_initialize(component, &catpt_comp_driver, -- cdev->dev); -- if (ret) -- return ret; -+ snd_soc_component_set_name(component, catpt_comp_driver.name); - -- component->name = catpt_comp_driver.name; -- return snd_soc_add_component(component, dai_drivers, -- ARRAY_SIZE(dai_drivers)); -+ return snd_soc_register_component(component, -+ &catpt_comp_driver, -+ dai_drivers, ARRAY_SIZE(dai_drivers)); - } -diff --git a/sound/soc/intel/common/soc-acpi-intel-arl-match.c b/sound/soc/intel/common/soc-acpi-intel-arl-match.c ---- a/sound/soc/intel/common/soc-acpi-intel-arl-match.c -+++ b/sound/soc/intel/common/soc-acpi-intel-arl-match.c -@@ -572,6 +572,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = { - .links = arl_rt711_l0_rt1316_l3, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-arl-rt711-l0-rt1316-l3.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0) | BIT(3), -@@ -600,12 +601,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = { - .links = arl_rvp, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-arl-rt711.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = 0x1, /* link0 required */ - .links = arl_sdca_rvp, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-arl-rt711-l0.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(2), -diff --git a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c ---- a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c -+++ b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c -@@ -717,24 +717,28 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { - .links = lnl_3_in_1_sdca, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-lnl-rt711-l0-rt1316-l23-rt714-l1.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0) | BIT(2) | BIT(3), - .links = lnl_cs42l43_l0_cs35l56_l23, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-lnl-cs42l43-l0-cs35l56-l23.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(1) | BIT(2) | BIT(3), - .links = lnl_cs42l43_l2_cs35l56x6_l13, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-lnl-cs42l43-l2-cs35l56x6-l13.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0) | BIT(3), - .links = lnl_cs42l43_l0_cs35l56_l3, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-lnl-cs42l43-l0-cs35l56-l3.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0), -@@ -748,12 +752,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { - .links = lnl_rvp, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-lnl-rt711.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(2) | BIT(3), - .links = lnl_712_only, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-lnl-rt712-l2-rt1712-l3.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0), -@@ -766,19 +772,22 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { - .link_mask = GENMASK(2, 0), - .links = lnl_sdw_rt1318_l12_rt714_l0, - .drv_name = "sof_sdw", -- .sof_tplg_filename = "sof-lnl-rt1318-l12-rt714-l0.tplg" -+ .sof_tplg_filename = "sof-lnl-rt1318-l12-rt714-l0.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = GENMASK(2, 0), - .links = lnl_sdw_rt1320_l12_rt714_l0, - .drv_name = "sof_sdw", -- .sof_tplg_filename = "sof-lnl-rt1320-l12-rt714-l0.tplg" -+ .sof_tplg_filename = "sof-lnl-rt1320-l12-rt714-l0.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0) | BIT(1), - .links = lnl_sdw_rt713_l0_rt1318_l1, - .drv_name = "sof_sdw", -- .sof_tplg_filename = "sof-lnl-rt713-l0-rt1318-l1.tplg" -+ .sof_tplg_filename = "sof-lnl-rt713-l0-rt1318-l1.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(1) | BIT(2), -diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c ---- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c -+++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c -@@ -1321,60 +1321,70 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { - .links = tac5572_l0, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-tac5572.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0), - .links = tac5672_l0, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-tac5672.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0), - .links = tac5682_l0, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-tac5682.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0), - .links = tas2783_link0, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-tas2783.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0), - .links = tas2883_l0, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-tas2883.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = GENMASK(3, 0), - .links = mtl_rt713_l0_rt1316_l12_rt1713_l3, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-rt713-l0-rt1316-l12-rt1713-l3.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = GENMASK(3, 0), - .links = mtl_rt713_l0_rt1318_l12_rt1713_l3, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-rt713-l0-rt1318-l12-rt1713-l3.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0) | BIT(1) | BIT(3), - .links = mtl_rt713_l0_rt1318_l1_rt1713_l3, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-rt713-l0-rt1318-l1-rt1713-l3.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = GENMASK(2, 0), - .links = mtl_rt713_l0_rt1316_l12, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-rt713-l0-rt1316-l12.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(3) | BIT(0), - .links = mtl_712_l0_1712_l3, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-rt712-l0-rt1712-l3.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0), -@@ -1395,7 +1405,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { - .link_mask = GENMASK(2, 0), - .links = mtl_sdw_rt1318_l12_rt714_l0, - .drv_name = "sof_sdw", -- .sof_tplg_filename = "sof-mtl-rt1318-l12-rt714-l0.tplg" -+ .sof_tplg_filename = "sof-mtl-rt1318-l12-rt714-l0.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0) | BIT(2) | BIT(3), -@@ -1455,12 +1466,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { - .links = mtl_3_in_1_sdca, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-rt711-l0-rt1316-l23-rt714-l1.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = 0x9, /* 2 active links required */ - .links = mtl_rt711_l0_rt1316_l3, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-rt711-l0-rt1316-l3.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0), -@@ -1474,18 +1487,21 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { - .links = mtl_rvp, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-rt711.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0) | BIT(2), - .links = rt5682_link2_max98373_link0, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-sdw-rt5682-l2-max98373-l0.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0) | BIT(2), - .links = cs42l42_link0_max98363_link2, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-sdw-cs42l42-l0-max98363-l2.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - {}, - }; -diff --git a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c ---- a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c -+++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c -@@ -492,7 +492,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = { - .links = ptl_rt722_l0_rt1320_l23, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-ptl-rt722-l0-rt1320-l23.tplg", -- .get_function_tplg_files = sof_sdw_get_tplg_files, -+ .machine_check = snd_soc_acpi_intel_no_function_topology, - }, - { - .link_mask = BIT(1) | BIT(2), -@@ -522,14 +522,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = { - .links = ptl_rvp, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-ptl-rt711.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(3), - .links = ptl_sdw_rt712_vb_l3_rt1320_l3, - .drv_name = "sof_sdw", -- .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, -+ .machine_check = snd_soc_acpi_intel_rt712_vb_no_function_topology, - .sof_tplg_filename = "sof-ptl-rt712-l3-rt1320-l3.tplg", -- .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - {}, - }; -diff --git a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c ---- a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c -+++ b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c -@@ -53,6 +53,14 @@ bool snd_soc_acpi_intel_no_function_topology(void *arg) - } - EXPORT_SYMBOL_NS(snd_soc_acpi_intel_no_function_topology, "SND_SOC_ACPI_INTEL_SDCA_QUIRKS"); - -+bool snd_soc_acpi_intel_rt712_vb_no_function_topology(void *arg) -+{ -+ return snd_soc_acpi_intel_sdca_is_device_rt712_vb(arg) && -+ snd_soc_acpi_intel_no_function_topology(arg); -+} -+EXPORT_SYMBOL_NS(snd_soc_acpi_intel_rt712_vb_no_function_topology, -+ "SND_SOC_ACPI_INTEL_SDCA_QUIRKS"); -+ - MODULE_DESCRIPTION("ASoC ACPI Intel SDCA quirks"); - MODULE_LICENSE("GPL"); - MODULE_IMPORT_NS("SND_SOC_SDCA"); -diff --git a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h ---- a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h -+++ b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h -@@ -11,5 +11,6 @@ - - bool snd_soc_acpi_intel_sdca_is_device_rt712_vb(void *arg); - bool snd_soc_acpi_intel_no_function_topology(void *arg); -+bool snd_soc_acpi_intel_rt712_vb_no_function_topology(void *arg); - - #endif -diff --git a/sound/soc/intel/keembay/kmb_platform.c b/sound/soc/intel/keembay/kmb_platform.c ---- a/sound/soc/intel/keembay/kmb_platform.c -+++ b/sound/soc/intel/keembay/kmb_platform.c -@@ -873,6 +873,8 @@ static int kmb_plat_dai_probe(struct platform_device *pdev) - - if (kmb_i2s->use_pio) { - irq = platform_get_irq_optional(pdev, 0); -+ if (irq == -EPROBE_DEFER) -+ return irq; - if (irq > 0) { - ret = devm_request_irq(dev, irq, kmb_i2s_irq_handler, 0, - pdev->name, kmb_i2s); -diff --git a/sound/soc/loongson/loongson_card.c b/sound/soc/loongson/loongson_card.c ---- a/sound/soc/loongson/loongson_card.c -+++ b/sound/soc/loongson/loongson_card.c -@@ -2,24 +2,131 @@ - // - // Loongson ASoC Audio Machine driver - // --// Copyright (C) 2023 Loongson Technology Corporation Limited -+// Copyright (C) 2023-2026 Loongson Technology Corporation Limited - // Author: Yingkun Meng -+// Binbin Zhou - // - -+#include -+#include - #include -+#include -+#include -+#include - #include - #include --#include --#include --#include - - static char codec_name[SND_ACPI_I2C_ID_LEN]; - - struct loongson_card_data { - struct snd_soc_card snd_card; - unsigned int mclk_fs; -+ struct gpio_desc *gpiod_hp_det; -+ struct gpio_desc *gpiod_hp_ctl; -+ struct gpio_desc *gpiod_spkr_en; -+ const struct loongson_card_config *cfg; - }; - -+struct loongson_card_config { -+ unsigned int fmt; -+ bool add_hp_jack; -+ bool add_dapm_widgets; -+ bool add_dapm_routes; -+}; -+ -+static const struct loongson_card_config ls2k1000_card_config = { -+ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBC_CFC, -+ .add_hp_jack = false, -+ .add_dapm_widgets = false, -+ .add_dapm_routes = false, -+}; -+ -+static const struct loongson_card_config ls2k0300_forever_pi_card_config = { -+ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC, -+ .add_hp_jack = false, -+ .add_dapm_widgets = false, -+ .add_dapm_routes = false, -+}; -+ -+static const struct loongson_card_config ls2k0300_dl2k0300b_card_config = { -+ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC, -+ .add_hp_jack = true, -+ .add_dapm_widgets = true, -+ .add_dapm_routes = true, -+}; -+ -+static int tegra_machine_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *k, int event) -+{ -+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm); -+ struct loongson_card_data *priv = snd_soc_card_get_drvdata(card); -+ -+ if (!snd_soc_dapm_widget_name_cmp(w, "Speaker")) -+ gpiod_set_value_cansleep(priv->gpiod_spkr_en, -+ SND_SOC_DAPM_EVENT_ON(event)); -+ -+ if (!snd_soc_dapm_widget_name_cmp(w, "Headphone")) -+ gpiod_set_value_cansleep(priv->gpiod_hp_ctl, -+ SND_SOC_DAPM_EVENT_ON(event)); -+ -+ return 0; -+} -+ -+static const struct snd_soc_dapm_widget loongson_aosc_dapm_widgets[] = { -+ SND_SOC_DAPM_HP("Headphone", tegra_machine_event), -+ SND_SOC_DAPM_SPK("Speaker", tegra_machine_event), -+}; -+ -+/* Headphones Jack */ -+ -+static struct snd_soc_jack loongson_asoc_hp_jack; -+ -+static struct snd_soc_jack_pin loongson_asoc_hp_jack_pins[] = { -+ { -+ .pin = "Headphone", -+ .mask = SND_JACK_HEADPHONE -+ }, -+ { -+ .pin = "Speaker", -+ .mask = SND_JACK_HEADPHONE, -+ .invert = 1 -+ }, -+}; -+ -+static struct snd_soc_jack_gpio loongson_asoc_hp_jack_gpio = { -+ .name = "Headphones detection", -+ .report = SND_JACK_HEADPHONE, -+ .debounce_time = 150, -+}; -+ -+static int loongson_asoc_machine_init(struct snd_soc_pcm_runtime *rtd) -+{ -+ struct snd_soc_card *card = rtd->card; -+ struct loongson_card_data *ls_priv = snd_soc_card_get_drvdata(card); -+ int ret = 0; -+ -+ if (!ls_priv->cfg->add_hp_jack || !ls_priv->gpiod_hp_det) -+ return 0; -+ -+ ret = snd_soc_card_jack_new_pins(card, "Headphones Jack", -+ SND_JACK_HEADPHONE, -+ &loongson_asoc_hp_jack, -+ loongson_asoc_hp_jack_pins, -+ ARRAY_SIZE(loongson_asoc_hp_jack_pins)); -+ if (ret) { -+ dev_err(rtd->dev, "Headphones Jack creation failed: %d\n", ret); -+ return ret; -+ } -+ -+ loongson_asoc_hp_jack_gpio.desc = ls_priv->gpiod_hp_det; -+ -+ ret = snd_soc_jack_add_gpios(&loongson_asoc_hp_jack, 1, &loongson_asoc_hp_jack_gpio); -+ if (ret) -+ dev_err(rtd->dev, "Headphone GPIO not added: %d\n", ret); -+ -+ return ret; -+} -+ - static int loongson_card_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) - { -@@ -45,7 +152,7 @@ static int loongson_card_hw_params(struct snd_pcm_substream *substream, - return ret; - } - -- return 0; -+ return snd_soc_runtime_set_dai_fmt(rtd, ls_card->cfg->fmt); - } - - static const struct snd_soc_ops loongson_ops = { -@@ -61,8 +168,7 @@ static struct snd_soc_dai_link loongson_dai_links[] = { - { - .name = "Loongson Audio Port", - .stream_name = "Loongson Audio", -- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_NF -- | SND_SOC_DAIFMT_CBC_CFC, -+ .init = loongson_asoc_machine_init, - SND_SOC_DAILINK_REG(analog), - .ops = &loongson_ops, - }, -@@ -123,16 +229,35 @@ static int loongson_card_parse_acpi(struct loongson_card_data *data) - - static int loongson_card_parse_of(struct loongson_card_data *data) - { -- struct device_node *cpu, *codec; - struct snd_soc_card *card = &data->snd_card; -+ struct device_node *cpu, *codec; - struct device *dev = card->dev; - int ret, i; - -+ data->gpiod_hp_det = devm_gpiod_get_optional(dev, "hp-det", GPIOD_IN); -+ if (IS_ERR(data->gpiod_hp_det)) -+ return PTR_ERR(data->gpiod_hp_det); -+ -+ data->gpiod_hp_ctl = devm_gpiod_get_optional(dev, "hp-ctl", GPIOD_OUT_LOW); -+ if (IS_ERR(data->gpiod_hp_ctl)) -+ return PTR_ERR(data->gpiod_hp_ctl); -+ -+ data->gpiod_spkr_en = devm_gpiod_get_optional(dev, "spkr-en", GPIOD_OUT_LOW); -+ if (IS_ERR(data->gpiod_spkr_en)) -+ return PTR_ERR(data->gpiod_spkr_en); -+ -+ if (data->cfg->add_dapm_routes) { -+ ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); -+ if (ret) -+ return ret; -+ } -+ - cpu = of_get_child_by_name(dev->of_node, "cpu"); - if (!cpu) { - dev_err(dev, "platform property missing or invalid\n"); - return -EINVAL; - } -+ - codec = of_get_child_by_name(dev->of_node, "codec"); - if (!codec) { - dev_err(dev, "audio-codec property missing or invalid\n"); -@@ -177,12 +302,22 @@ static int loongson_asoc_card_probe(struct platform_device *pdev) - if (!ls_priv) - return -ENOMEM; - -+ ls_priv->cfg = (const struct loongson_card_config *)device_get_match_data(dev); -+ if (!ls_priv->cfg) -+ return -EINVAL; -+ - card = &ls_priv->snd_card; - - card->dev = dev; - card->owner = THIS_MODULE; - card->dai_link = loongson_dai_links; - card->num_links = ARRAY_SIZE(loongson_dai_links); -+ -+ if (ls_priv->cfg->add_dapm_widgets) { -+ card->dapm_widgets = loongson_aosc_dapm_widgets; -+ card->num_dapm_widgets = ARRAY_SIZE(loongson_aosc_dapm_widgets); -+ } -+ - snd_soc_card_set_drvdata(card, ls_priv); - - ret = device_property_read_string(dev, "model", &card->name); -@@ -202,7 +337,19 @@ static int loongson_asoc_card_probe(struct platform_device *pdev) - } - - static const struct of_device_id loongson_asoc_dt_ids[] = { -- { .compatible = "loongson,ls-audio-card" }, -+ /* Loongson-2K1000/Loongson-2K2000/LS7A */ -+ { -+ .compatible = "loongson,ls-audio-card", -+ .data = &ls2k1000_card_config -+ }, -+ { -+ .compatible = "loongson,ls2k0300-forever-pi-audio-card", -+ .data = &ls2k0300_forever_pi_card_config -+ }, -+ { -+ .compatible = "loongson,ls2k0300-dl2k0300b-audio-card", -+ .data = &ls2k0300_dl2k0300b_card_config -+ }, - { /* sentinel */ }, - }; - MODULE_DEVICE_TABLE(of, loongson_asoc_dt_ids); -diff --git a/sound/soc/loongson/loongson_i2s_plat.c b/sound/soc/loongson/loongson_i2s_plat.c ---- a/sound/soc/loongson/loongson_i2s_plat.c -+++ b/sound/soc/loongson/loongson_i2s_plat.c -@@ -2,7 +2,7 @@ - // - // Loongson I2S controller master mode dirver(platform device) - // --// Copyright (C) 2023-2024 Loongson Technology Corporation Limited -+// Copyright (C) 2023-2026 Loongson Technology Corporation Limited - // - // Author: Yingkun Meng - // Binbin Zhou -@@ -21,6 +21,7 @@ - #include "loongson_i2s.h" - #include "loongson_dma.h" - -+/* Loongson-2K1000 APBDMA routing */ - #define LOONGSON_I2S_RX_DMA_OFFSET 21 - #define LOONGSON_I2S_TX_DMA_OFFSET 18 - -@@ -30,6 +31,11 @@ - #define LOONGSON_DMA3_CONF 0x3 - #define LOONGSON_DMA4_CONF 0x4 - -+struct loongson_i2s_plat_config { -+ int rev_id; -+ int (*i2s_dma_config)(struct platform_device *pdev); -+}; -+ - static int loongson_i2s_apbdma_config(struct platform_device *pdev) - { - int val; -@@ -47,8 +53,18 @@ static int loongson_i2s_apbdma_config(struct platform_device *pdev) - return 0; - } - -+static const struct loongson_i2s_plat_config ls2k0300_i2s_plat_config = { -+ .rev_id = 1, -+}; -+ -+static const struct loongson_i2s_plat_config ls2k1000_i2s_plat_config = { -+ .rev_id = 0, -+ .i2s_dma_config = loongson_i2s_apbdma_config, -+}; -+ - static int loongson_i2s_plat_probe(struct platform_device *pdev) - { -+ const struct loongson_i2s_plat_config *plat_config; - struct device *dev = &pdev->dev; - struct loongson_i2s *i2s; - struct resource *res; -@@ -59,12 +75,17 @@ static int loongson_i2s_plat_probe(struct platform_device *pdev) - if (!i2s) - return -ENOMEM; - -- ret = loongson_i2s_apbdma_config(pdev); -- if (ret) -- return ret; -+ plat_config = device_get_match_data(dev); -+ if (!plat_config) -+ return -EINVAL; - -- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- i2s->reg_base = devm_ioremap_resource(&pdev->dev, res); -+ if (plat_config->i2s_dma_config) { -+ ret = plat_config->i2s_dma_config(pdev); -+ if (ret) -+ return ret; -+ } -+ -+ i2s->reg_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(i2s->reg_base)) - return dev_err_probe(dev, PTR_ERR(i2s->reg_base), - "devm_ioremap_resource failed\n"); -@@ -87,11 +108,17 @@ static int loongson_i2s_plat_probe(struct platform_device *pdev) - if (IS_ERR(i2s_clk)) - return dev_err_probe(dev, PTR_ERR(i2s_clk), "clock property invalid\n"); - i2s->clk_rate = clk_get_rate(i2s_clk); -+ i2s->rev_id = plat_config->rev_id; - - dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); - dev_set_name(dev, LS_I2S_DRVNAME); - dev_set_drvdata(dev, i2s); - -+ if (i2s->rev_id == 1) { -+ regmap_update_bits(i2s->regmap, LS_I2S_CTRL, I2S_CTRL_RESET, I2S_CTRL_RESET); -+ fsleep(200); -+ } -+ - ret = devm_snd_soc_register_component(dev, &loongson_i2s_edma_component, - &loongson_i2s_dai, 1); - if (ret) -@@ -102,7 +129,8 @@ static int loongson_i2s_plat_probe(struct platform_device *pdev) - } - - static const struct of_device_id loongson_i2s_ids[] = { -- { .compatible = "loongson,ls2k1000-i2s" }, -+ { .compatible = "loongson,ls2k0300-i2s", .data = &ls2k0300_i2s_plat_config }, -+ { .compatible = "loongson,ls2k1000-i2s", .data = &ls2k1000_i2s_plat_config }, - { /* sentinel */ }, - }; - MODULE_DEVICE_TABLE(of, loongson_i2s_ids); -diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig ---- a/sound/soc/mediatek/Kconfig -+++ b/sound/soc/mediatek/Kconfig -@@ -7,7 +7,7 @@ config SND_SOC_MEDIATEK - - config SND_SOC_MT2701 - tristate "ASoC support for Mediatek MT2701 chip" -- depends on ARCH_MEDIATEK -+ depends on ARCH_MEDIATEK || COMPILE_TEST - select SND_SOC_MEDIATEK - help - This adds ASoC driver for Mediatek MT2701 boards -diff --git a/sound/soc/mediatek/common/mtk-dsp-sof-common.c b/sound/soc/mediatek/common/mtk-dsp-sof-common.c ---- a/sound/soc/mediatek/common/mtk-dsp-sof-common.c -+++ b/sound/soc/mediatek/common/mtk-dsp-sof-common.c -@@ -232,6 +232,7 @@ int mtk_sof_dailink_parse_of(struct device *dev, struct snd_soc_card *card, - const char *propname) - { - struct device_node *np = dev->of_node; -+ struct snd_soc_dai_link *dai_link; - struct snd_soc_dai_link *parsed_dai_link; - const char *dai_name = NULL; - int i, j, ret, num_links, parsed_num_links = 0; -@@ -254,9 +255,9 @@ int mtk_sof_dailink_parse_of(struct device *dev, struct snd_soc_card *card, - return ret; - } - dev_dbg(dev, "ASoC: Property get dai_name:%s\n", dai_name); -- for (j = 0; j < card->num_links; j++) { -- if (!strcmp(dai_name, card->dai_link[j].name)) { -- memcpy(&parsed_dai_link[parsed_num_links++], &card->dai_link[j], -+ for_each_card_prelinks(card, j, dai_link) { -+ if (!strcmp(dai_name, dai_link->name)) { -+ memcpy(&parsed_dai_link[parsed_num_links++], dai_link, - sizeof(struct snd_soc_dai_link)); - break; - } -diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c ---- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c -+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c -@@ -315,7 +315,7 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev) - &mt8173_rt5650_priv.pll_from); - if (ret) { - dev_err(&pdev->dev, -- "%s snd_soc_register_card fail %d\n", -+ "%s device_property_read_u32() fail %d\n", - __func__, ret); - } - } diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c --- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c @@ -39681,18 +11418,6 @@ diff --git a/sound/soc/mediatek/mt8188/mt8188-mt6359.c b/sound/soc/mediatek/mt81 params_set_format(params, SNDRV_PCM_FORMAT_S32_LE); -diff --git a/sound/soc/mediatek/mt8189/mt8189-dai-adda.c b/sound/soc/mediatek/mt8189/mt8189-dai-adda.c ---- a/sound/soc/mediatek/mt8189/mt8189-dai-adda.c -+++ b/sound/soc/mediatek/mt8189/mt8189-dai-adda.c -@@ -385,8 +385,6 @@ static int mtk_adda_pad_top_event(struct snd_soc_dapm_widget *w, - if (event == SND_SOC_DAPM_PRE_PMU) { - if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2_CLK_P2) - regmap_write(afe->regmap, AFE_AUD_PAD_TOP_CFG0, 0xB8); -- else if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2) -- regmap_write(afe->regmap, AFE_AUD_PAD_TOP_CFG0, 0xB0); - else - regmap_write(afe->regmap, AFE_AUD_PAD_TOP_CFG0, 0xB0); - } diff --git a/sound/soc/mediatek/mt8189/mt8189-nau8825.c b/sound/soc/mediatek/mt8189/mt8189-nau8825.c --- a/sound/soc/mediatek/mt8189/mt8189-nau8825.c +++ b/sound/soc/mediatek/mt8189/mt8189-nau8825.c @@ -39717,52 +11442,6 @@ diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); -@@ -1019,7 +1019,7 @@ static struct snd_soc_card mt8192_mt6359_rt1015p_rt5682x_card = { - .num_dapm_routes = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682x_routes), - }; - --static int mt8192_mt6359_card_set_be_link(struct snd_soc_card *card, -+static int mt8192_mt6359_card_set_be_link(struct device *dev, - struct snd_soc_dai_link *link, - struct device_node *node, - char *link_name) -@@ -1027,9 +1027,9 @@ static int mt8192_mt6359_card_set_be_link(struct snd_soc_card *card, - int ret; - - if (node && strcmp(link->name, link_name) == 0) { -- ret = snd_soc_of_get_dai_link_codecs(card->dev, node, link); -+ ret = snd_soc_of_get_dai_link_codecs(dev, node, link); - if (ret < 0) { -- dev_err_probe(card->dev, ret, "get dai link codecs fail\n"); -+ dev_err_probe(dev, ret, "get dai link codecs fail\n"); - return ret; - } - } -@@ -1065,21 +1065,21 @@ static int mt8192_mt6359_legacy_probe(struct mtk_soc_card_data *soc_card_data) - } - - for_each_card_prelinks(card, i, dai_link) { -- ret = mt8192_mt6359_card_set_be_link(card, dai_link, speaker_codec, "I2S3"); -+ ret = mt8192_mt6359_card_set_be_link(dev, dai_link, speaker_codec, "I2S3"); - if (ret) { - dev_err_probe(dev, ret, "%s set speaker_codec fail\n", - dai_link->name); - break; - } - -- ret = mt8192_mt6359_card_set_be_link(card, dai_link, headset_codec, "I2S8"); -+ ret = mt8192_mt6359_card_set_be_link(dev, dai_link, headset_codec, "I2S8"); - if (ret) { - dev_err_probe(dev, ret, "%s set headset_codec fail\n", - dai_link->name); - break; - } - -- ret = mt8192_mt6359_card_set_be_link(card, dai_link, headset_codec, "I2S9"); -+ ret = mt8192_mt6359_card_set_be_link(dev, dai_link, headset_codec, "I2S9"); - if (ret) { - dev_err_probe(dev, ret, "%s set headset_codec fail\n", - dai_link->name); diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359.c b/sound/soc/mediatek/mt8195/mt8195-mt6359.c --- a/sound/soc/mediatek/mt8195/mt8195-mt6359.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359.c @@ -39787,16 +11466,7 @@ diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359.c b/sound/soc/mediatek/mt81 diff --git a/sound/soc/mediatek/mt8196/mt8196-nau8825.c b/sound/soc/mediatek/mt8196/mt8196-nau8825.c --- a/sound/soc/mediatek/mt8196/mt8196-nau8825.c +++ b/sound/soc/mediatek/mt8196/mt8196-nau8825.c -@@ -104,8 +104,6 @@ static const struct snd_kcontrol_new mt8196_nau8825_controls[] = { - - #define EXT_SPK_AMP_W_NAME "Ext_Speaker_Amp" - --static struct snd_soc_card mt8196_nau8825_soc_card; -- - static const struct snd_soc_dapm_widget mt8196_nau8825_card_widgets[] = { - /* SOF Uplink */ - SND_SOC_DAPM_MIXER("SOF_DMA_UL0", SND_SOC_NOPM, 0, 0, NULL, 0), -@@ -180,7 +178,7 @@ static int mt8196_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, +@@ -180,7 +180,7 @@ static int mt8196_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, /* fix BE i2s format to 32bit, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), @@ -39805,1491 +11475,6 @@ diff --git a/sound/soc/mediatek/mt8196/mt8196-nau8825.c b/sound/soc/mediatek/mt8 params_set_format(params, SNDRV_PCM_FORMAT_S32_LE); return 0; -diff --git a/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c b/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c ---- a/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c -+++ b/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c -@@ -2120,19 +2120,15 @@ static int mt8365_afe_pcm_dev_probe(struct platform_device *pdev) - spin_lock_init(&afe_priv->afe_ctrl_lock); - mutex_init(&afe_priv->afe_clk_mutex); - -- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- afe->base_addr = devm_ioremap_resource(&pdev->dev, res); -+ afe->base_addr = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(afe->base_addr)) - return PTR_ERR(afe->base_addr); - -- res = platform_get_resource(pdev, IORESOURCE_MEM, 1); -- if (res) { -- afe_priv->afe_sram_vir_addr = -- devm_ioremap_resource(&pdev->dev, res); -- if (!IS_ERR(afe_priv->afe_sram_vir_addr)) { -- afe_priv->afe_sram_phy_addr = res->start; -- afe_priv->afe_sram_size = resource_size(res); -- } -+ afe_priv->afe_sram_vir_addr = -+ devm_platform_get_and_ioremap_resource(pdev, 1, &res); -+ if (!IS_ERR(afe_priv->afe_sram_vir_addr)) { -+ afe_priv->afe_sram_phy_addr = res->start; -+ afe_priv->afe_sram_size = resource_size(res); - } - - /* initial audio related clock */ -diff --git a/sound/soc/mediatek/mt8365/mt8365-mt6357.c b/sound/soc/mediatek/mt8365/mt8365-mt6357.c ---- a/sound/soc/mediatek/mt8365/mt8365-mt6357.c -+++ b/sound/soc/mediatek/mt8365/mt8365-mt6357.c -@@ -70,7 +70,8 @@ static const struct snd_soc_dapm_route mt8365_mt6357_routes[] = { - static int mt8365_mt6357_int_adda_startup(struct snd_pcm_substream *substream) - { - struct snd_soc_pcm_runtime *rtd = substream->private_data; -- struct mt8365_mt6357_priv *priv = snd_soc_card_get_drvdata(rtd->card); -+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card); -+ struct mt8365_mt6357_priv *priv = soc_card_data->mach_priv; - int ret = 0; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { -@@ -101,7 +102,8 @@ static int mt8365_mt6357_int_adda_startup(struct snd_pcm_substream *substream) - static void mt8365_mt6357_int_adda_shutdown(struct snd_pcm_substream *substream) - { - struct snd_soc_pcm_runtime *rtd = substream->private_data; -- struct mt8365_mt6357_priv *priv = snd_soc_card_get_drvdata(rtd->card); -+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card); -+ struct mt8365_mt6357_priv *priv = soc_card_data->mach_priv; - int ret = 0; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { -@@ -245,7 +247,8 @@ static struct snd_soc_dai_link mt8365_mt6357_dais[] = { - - static int mt8365_mt6357_gpio_probe(struct snd_soc_card *card) - { -- struct mt8365_mt6357_priv *priv = snd_soc_card_get_drvdata(card); -+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card); -+ struct mt8365_mt6357_priv *priv = soc_card_data->mach_priv; - struct device *dev = card->dev; - int ret, i; - -@@ -294,7 +297,6 @@ static int mt8365_mt6357_dev_probe(struct mtk_soc_card_data *soc_card_data, bool - struct mt8365_mt6357_priv *mach_priv; - int ret; - -- card->dev = dev; - ret = parse_dai_link_info(card); - if (ret) - goto err; -diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile ---- a/sound/soc/meson/Makefile -+++ b/sound/soc/meson/Makefile -@@ -4,6 +4,8 @@ snd-soc-meson-aiu-y := aiu.o - snd-soc-meson-aiu-y += aiu-acodec-ctrl.o - snd-soc-meson-aiu-y += aiu-codec-ctrl.o - snd-soc-meson-aiu-y += aiu-encoder-i2s.o -+snd-soc-meson-aiu-y += gx-formatter.o -+snd-soc-meson-aiu-y += aiu-formatter-i2s.o - snd-soc-meson-aiu-y += aiu-encoder-spdif.o - snd-soc-meson-aiu-y += aiu-fifo.o - snd-soc-meson-aiu-y += aiu-fifo-i2s.o -diff --git a/sound/soc/meson/aiu-encoder-i2s.c b/sound/soc/meson/aiu-encoder-i2s.c ---- a/sound/soc/meson/aiu-encoder-i2s.c -+++ b/sound/soc/meson/aiu-encoder-i2s.c -@@ -10,14 +10,11 @@ - #include - - #include "aiu.h" -+#include "gx-formatter.h" -+#include "gx-interface.h" - --#define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0) --#define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5) --#define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9) - #define AIU_I2S_SOURCE_DESC_MODE_SPLIT BIT(11) --#define AIU_RST_SOFT_I2S_FAST BIT(0) - --#define AIU_I2S_DAC_CFG_MSB_FIRST BIT(2) - #define AIU_CLK_CTRL_I2S_DIV_EN BIT(0) - #define AIU_CLK_CTRL_I2S_DIV GENMASK(3, 2) - #define AIU_CLK_CTRL_AOCLK_INVERT BIT(6) -@@ -35,49 +32,6 @@ static void aiu_encoder_i2s_divider_enable(struct snd_soc_component *component, - enable ? AIU_CLK_CTRL_I2S_DIV_EN : 0); - } - --static int aiu_encoder_i2s_setup_desc(struct snd_soc_component *component, -- struct snd_pcm_hw_params *params) --{ -- /* Always operate in split (classic interleaved) mode */ -- unsigned int desc = AIU_I2S_SOURCE_DESC_MODE_SPLIT; -- -- /* Reset required to update the pipeline */ -- snd_soc_component_write(component, AIU_RST_SOFT, AIU_RST_SOFT_I2S_FAST); -- snd_soc_component_read(component, AIU_I2S_SYNC); -- -- switch (params_physical_width(params)) { -- case 16: /* Nothing to do */ -- break; -- -- case 32: -- desc |= (AIU_I2S_SOURCE_DESC_MODE_24BIT | -- AIU_I2S_SOURCE_DESC_MODE_32BIT); -- break; -- -- default: -- return -EINVAL; -- } -- -- switch (params_channels(params)) { -- case 2: /* Nothing to do */ -- break; -- case 8: -- desc |= AIU_I2S_SOURCE_DESC_MODE_8CH; -- break; -- default: -- return -EINVAL; -- } -- -- snd_soc_component_update_bits(component, AIU_I2S_SOURCE_DESC, -- AIU_I2S_SOURCE_DESC_MODE_8CH | -- AIU_I2S_SOURCE_DESC_MODE_24BIT | -- AIU_I2S_SOURCE_DESC_MODE_32BIT | -- AIU_I2S_SOURCE_DESC_MODE_SPLIT, -- desc); -- -- return 0; --} -- - static int aiu_encoder_i2s_set_legacy_div(struct snd_soc_component *component, - struct snd_pcm_hw_params *params, - unsigned int bs) -@@ -108,6 +62,32 @@ static int aiu_encoder_i2s_set_legacy_div(struct snd_soc_component *component, - return 0; - } - -+/* -+ * Return true if the given combination of channels and sample width requires -+ * the bs quirk. Return false otherwise. -+ */ -+static bool aiu_encoder_is_bs_quirk(unsigned int channels, int width) -+{ -+ return (channels == 8) && (width == 16); -+} -+ -+static int aiu_encoder_check_bs_quirk(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params, -+ struct snd_soc_dai *dai) -+{ -+ struct gx_stream *other_stream = snd_soc_dai_dma_data_get(dai, !substream->stream); -+ -+ /* Nothing to do if the other stream doesn't exist or it's not configured yet. */ -+ if (!other_stream || !other_stream->channels) -+ return 0; -+ -+ if (aiu_encoder_is_bs_quirk(other_stream->channels, other_stream->width) != -+ aiu_encoder_is_bs_quirk(params_channels(params), params_width(params))) -+ return -EINVAL; -+ -+ return 0; -+} -+ - static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component, - struct snd_pcm_hw_params *params, - unsigned int bs) -@@ -119,7 +99,7 @@ static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component, - * increased by 50% to get the correct output rate. - * No idea why ! - */ -- if (params_width(params) == 16 && params_channels(params) == 8) { -+ if (aiu_encoder_is_bs_quirk(params_channels(params), params_width(params))) { - if (bs % 2) { - dev_err(component->dev, - "Cannot increase i2s divider by 50%%\n"); -@@ -141,25 +121,23 @@ static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component, - return 0; - } - --static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component, -- struct snd_pcm_hw_params *params) -+static int aiu_encoder_i2s_set_clocks(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params, -+ struct snd_soc_dai *dai) - { -+ struct snd_soc_component *component = dai->component; - struct aiu *aiu = snd_soc_component_get_drvdata(component); -+ struct gx_iface *iface = &aiu->i2s.iface; - unsigned int srate = params_rate(params); - unsigned int fs, bs; - int ret; - - /* Get the oversampling factor */ -- fs = DIV_ROUND_CLOSEST(clk_get_rate(aiu->i2s.clks[MCLK].clk), srate); -+ fs = DIV_ROUND_CLOSEST(iface->mclk_rate, srate); - -- if (fs % 64) -+ if ((fs % 64) || (fs == 0)) - return -EINVAL; - -- /* Send data MSB first */ -- snd_soc_component_update_bits(component, AIU_I2S_DAC_CFG, -- AIU_I2S_DAC_CFG_MSB_FIRST, -- AIU_I2S_DAC_CFG_MSB_FIRST); -- - /* Set bclk to lrlck ratio */ - snd_soc_component_update_bits(component, AIU_CODEC_DAC_LRCLK_CTRL, - AIU_CODEC_DAC_LRCLK_CTRL_DIV, -@@ -168,10 +146,22 @@ static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component, - - bs = fs / 64; - -- if (aiu->platform->has_clk_ctrl_more_i2s_div) -+ if (aiu->platform->has_clk_ctrl_more_i2s_div) { -+ /* -+ * The hw rules added in startup() make this unreachable in the -+ * sequential case, but both streams may be refined concurrently -+ * before either commits its config, since only ops->hw_params -+ * runs under the card's pcm_mutex. Re-check against the committed -+ * state of the other stream, which is stable under that mutex. -+ */ -+ if (aiu_encoder_check_bs_quirk(substream, params, dai)) { -+ dev_err(dai->dev, "bclk requirements incompatible with other stream\n"); -+ return -EINVAL; -+ } - ret = aiu_encoder_i2s_set_more_div(component, params, bs); -- else -+ } else { - ret = aiu_encoder_i2s_set_legacy_div(component, params, bs); -+ } - - if (ret) - return ret; -@@ -188,23 +178,37 @@ static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) - { -+ struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); -+ int ret; -+ -+ ret = aiu_encoder_i2s_set_clocks(substream, params, dai); -+ if (ret) { -+ dev_err(dai->dev, "setting i2s clocks failed: %d\n", ret); -+ return ret; -+ } -+ -+ ts->physical_width = params_physical_width(params); -+ ts->width = params_width(params); -+ ts->channels = params_channels(params); -+ -+ return 0; -+} -+ -+static int aiu_encoder_i2s_prepare(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); - struct snd_soc_component *component = dai->component; - int ret; - -- /* Disable the clock while changing the settings */ -- aiu_encoder_i2s_divider_enable(component, false); -+ if (ts->clk_enabled) -+ return 0; - -- ret = aiu_encoder_i2s_setup_desc(component, params); -- if (ret) { -- dev_err(dai->dev, "setting i2s desc failed\n"); -+ ret = clk_prepare_enable(ts->iface->mclk); -+ if (ret) - return ret; -- } - -- ret = aiu_encoder_i2s_set_clocks(component, params); -- if (ret) { -- dev_err(dai->dev, "setting i2s clocks failed\n"); -- return ret; -- } -+ ts->clk_enabled = true; - - aiu_encoder_i2s_divider_enable(component, true); - -@@ -214,9 +218,24 @@ static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream, - static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) - { -+ struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); - struct snd_soc_component *component = dai->component; - -- aiu_encoder_i2s_divider_enable(component, false); -+ /* -+ * If this is the last substream being closed then disable the i2s -+ * clock divider. -+ */ -+ if (snd_soc_dai_active(dai) <= 1) -+ aiu_encoder_i2s_divider_enable(component, 0); -+ -+ if (ts->clk_enabled) { -+ clk_disable_unprepare(ts->iface->mclk); -+ ts->clk_enabled = false; -+ } -+ -+ ts->channels = 0; -+ ts->width = 0; -+ ts->physical_width = 0; - - return 0; - } -@@ -224,6 +243,8 @@ static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream, - static int aiu_encoder_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) - { - struct snd_soc_component *component = dai->component; -+ struct aiu *aiu = snd_soc_component_get_drvdata(component); -+ struct gx_iface *iface = &aiu->i2s.iface; - unsigned int inv = fmt & SND_SOC_DAIFMT_INV_MASK; - unsigned int val = 0; - unsigned int skew; -@@ -255,9 +276,12 @@ static int aiu_encoder_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) - skew = 0; - break; - default: -+ dev_err(dai->dev, "unsupported dai format\n"); - return -EINVAL; - } - -+ iface->fmt = fmt; -+ - val |= FIELD_PREP(AIU_CLK_CTRL_LRCLK_SKEW, skew); - snd_soc_component_update_bits(component, AIU_CLK_CTRL, - AIU_CLK_CTRL_LRCLK_INVERT | -@@ -272,6 +296,7 @@ static int aiu_encoder_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, - unsigned int freq, int dir) - { - struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); -+ struct gx_iface *iface = &aiu->i2s.iface; - int ret; - - if (WARN_ON(clk_id != 0)) -@@ -280,11 +305,15 @@ static int aiu_encoder_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, - if (dir == SND_SOC_CLOCK_IN) - return 0; - -- ret = clk_set_rate(aiu->i2s.clks[MCLK].clk, freq); -- if (ret) -- dev_err(dai->dev, "Failed to set sysclk to %uHz", freq); -+ ret = clk_set_rate(iface->mclk, freq); -+ if (ret) { -+ dev_err(dai->dev, "Failed to set sysclk to %uHz: %d", freq, ret); -+ return ret; -+ } - -- return ret; -+ iface->mclk_rate = freq; -+ -+ return 0; - } - - static const unsigned int hw_channels[] = {2, 8}; -@@ -294,10 +323,45 @@ static const struct snd_pcm_hw_constraint_list hw_channel_constraints = { - .mask = 0, - }; - -+static int aiu_encoder_i2s_pcm_hw_rule(struct snd_pcm_hw_params *params, -+ struct snd_pcm_hw_rule *rule) -+{ -+ struct gx_stream *other = rule->private; -+ struct snd_interval *ch = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); -+ /* -+ * The quirk is technically based on the significant bits whereas here -+ * we're using the physical width for simplicity. This works because -+ * S16_LE is the only format supported by this encoder that has: -+ * significant bits = physical width = 16-bits -+ */ -+ struct snd_interval *phys_width = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); -+ struct snd_interval new_i; -+ -+ if (other->channels == 0) -+ return 0; -+ -+ snd_interval_any(&new_i); -+ -+ if (rule->var == SNDRV_PCM_HW_PARAM_CHANNELS) { -+ if (aiu_encoder_is_bs_quirk(other->channels, other->width)) -+ new_i.min = new_i.max = 8; -+ else if (snd_interval_single(phys_width) && phys_width->min == 16) -+ new_i.max = 2; /* Force 2ch */ -+ } else { /* SNDRV_PCM_HW_PARAM_SAMPLE_BITS */ -+ if (aiu_encoder_is_bs_quirk(other->channels, other->width)) -+ new_i.min = new_i.max = 16; -+ else if (snd_interval_single(ch) && ch->min == 8) -+ new_i.min = 17; /* Request physical width > 16 bits */ -+ } -+ -+ return snd_interval_refine(hw_param_interval(params, rule->var), &new_i); -+} -+ - static int aiu_encoder_i2s_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) - { - struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); -+ struct gx_stream *other_stream = snd_soc_dai_dma_data_get(dai, !substream->stream); - int ret; - - /* Make sure the encoder gets either 2 or 8 channels */ -@@ -305,15 +369,79 @@ static int aiu_encoder_i2s_startup(struct snd_pcm_substream *substream, - SNDRV_PCM_HW_PARAM_CHANNELS, - &hw_channel_constraints); - if (ret) { -- dev_err(dai->dev, "adding channels constraints failed\n"); -+ dev_err(dai->dev, "adding channels constraints failed: %d\n", ret); - return ret; - } - -- ret = clk_bulk_prepare_enable(aiu->i2s.clk_num, aiu->i2s.clks); -- if (ret) -- dev_err(dai->dev, "failed to enable i2s clocks\n"); -+ /* -+ * If DAI supports both playback and capture streams ensure the bs-quirk is -+ * handled correctly. -+ * This is only valid for GX platforms (has_clk_ctrl_more_i2s_div=true). -+ */ -+ if (aiu->platform->has_clk_ctrl_more_i2s_div && other_stream) { -+ ret = snd_pcm_hw_rule_add(substream->runtime, 0, -+ SNDRV_PCM_HW_PARAM_CHANNELS, -+ aiu_encoder_i2s_pcm_hw_rule, -+ other_stream, -+ SNDRV_PCM_HW_PARAM_CHANNELS, -+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); -+ if (ret) -+ return ret; - -- return ret; -+ ret = snd_pcm_hw_rule_add(substream->runtime, 0, -+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -+ aiu_encoder_i2s_pcm_hw_rule, -+ other_stream, -+ SNDRV_PCM_HW_PARAM_CHANNELS, -+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); -+ if (ret) -+ return ret; -+ } -+ -+ /* -+ * Enable only clocks which are required for the interface internal -+ * logic. MCLK is enabled/disabled from the formatter and the I2S -+ * divider is enabled/disabled in "hw_params"/"hw_free", respectively. -+ */ -+ ret = clk_prepare_enable(aiu->i2s.clks[PCLK].clk); -+ if (ret) { -+ dev_err(dai->dev, "failed to enable PCLK: %d\n", ret); -+ return ret; -+ } -+ ret = clk_prepare_enable(aiu->i2s.clks[MIXER].clk); -+ if (ret) { -+ dev_err(dai->dev, "failed to enable MIXER: %d\n", ret); -+ clk_disable_unprepare(aiu->i2s.clks[PCLK].clk); -+ return ret; -+ } -+ ret = clk_prepare_enable(aiu->i2s.clks[AOCLK].clk); -+ if (ret) { -+ dev_err(dai->dev, "failed to enable AOCLK: %d\n", ret); -+ clk_disable_unprepare(aiu->i2s.clks[MIXER].clk); -+ clk_disable_unprepare(aiu->i2s.clks[PCLK].clk); -+ return ret; -+ } -+ -+ /* -+ * We're always operating in split mode for the playback stream. -+ * -+ * This setting arguably belong to the 'aiu-formatter', but it's kept -+ * here for backward compatibility reason. At reset the I2S encoder -+ * operates in normal mode which would only support 8ch, but by default -+ * only 2ch are enabled. If a playback stream is started without -+ * changing to split mode, then the I2S encoder doesn't consume audio -+ * samples and the playback fails. -+ * Moving this to 'aiu-formatter' would cause the split mode to be set -+ * only when the formatter is enabled, which doesn't happen at boot as -+ * the default value for "HDMI CTRL SRC" is "DISABLED". -+ */ -+ ret = snd_soc_component_update_bits(dai->component, AIU_I2S_SOURCE_DESC, -+ AIU_I2S_SOURCE_DESC_MODE_SPLIT, -+ AIU_I2S_SOURCE_DESC_MODE_SPLIT); -+ if (ret < 0) -+ dev_err(dai->dev, "failed to update AIU_I2S_SOURCE_DESC: %d", ret); -+ -+ return 0; - } - - static void aiu_encoder_i2s_shutdown(struct snd_pcm_substream *substream, -@@ -321,14 +449,89 @@ static void aiu_encoder_i2s_shutdown(struct snd_pcm_substream *substream, - { - struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); - -- clk_bulk_disable_unprepare(aiu->i2s.clk_num, aiu->i2s.clks); -+ clk_disable_unprepare(aiu->i2s.clks[AOCLK].clk); -+ clk_disable_unprepare(aiu->i2s.clks[MIXER].clk); -+ clk_disable_unprepare(aiu->i2s.clks[PCLK].clk); -+} -+ -+static int aiu_encoder_i2s_trigger(struct snd_pcm_substream *substream, -+ int cmd, -+ struct snd_soc_dai *dai) -+{ -+ struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); -+ int ret; -+ -+ switch (cmd) { -+ case SNDRV_PCM_TRIGGER_START: -+ case SNDRV_PCM_TRIGGER_RESUME: -+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: -+ ret = gx_stream_start(ts); -+ break; -+ case SNDRV_PCM_TRIGGER_SUSPEND: -+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: -+ case SNDRV_PCM_TRIGGER_STOP: -+ gx_stream_stop(ts); -+ ret = 0; -+ break; -+ default: -+ ret = -EINVAL; -+ } -+ -+ return ret; -+} -+ -+static int aiu_encoder_i2s_remove_dai(struct snd_soc_dai *dai) -+{ -+ int stream; -+ -+ for_each_pcm_streams(stream) { -+ struct gx_stream *ts; -+ -+ ts = snd_soc_dai_dma_data_get(dai, stream); -+ if (ts) -+ gx_stream_free(ts); -+ -+ snd_soc_dai_dma_data_set(dai, stream, NULL); -+ } -+ -+ return 0; -+} -+ -+static int aiu_encoder_i2s_probe_dai(struct snd_soc_dai *dai) -+{ -+ struct aiu *aiu = snd_soc_dai_get_drvdata(dai); -+ struct gx_iface *iface = &aiu->i2s.iface; -+ int stream; -+ -+ for_each_pcm_streams(stream) { -+ struct gx_stream *ts; -+ -+ if (!snd_soc_dai_get_widget(dai, stream)) -+ continue; -+ -+ ts = gx_stream_alloc(iface); -+ if (!ts) { -+ aiu_encoder_i2s_remove_dai(dai); -+ return -ENOMEM; -+ } -+ snd_soc_dai_dma_data_set(dai, stream, ts); -+ } -+ -+ iface->mclk = aiu->i2s.clks[MCLK].clk; -+ iface->mclk_rate = clk_get_rate(iface->mclk); -+ -+ return 0; - } - - const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops = { -+ .probe = aiu_encoder_i2s_probe_dai, -+ .remove = aiu_encoder_i2s_remove_dai, - .hw_params = aiu_encoder_i2s_hw_params, -+ .prepare = aiu_encoder_i2s_prepare, - .hw_free = aiu_encoder_i2s_hw_free, - .set_fmt = aiu_encoder_i2s_set_fmt, - .set_sysclk = aiu_encoder_i2s_set_sysclk, - .startup = aiu_encoder_i2s_startup, - .shutdown = aiu_encoder_i2s_shutdown, -+ .trigger = aiu_encoder_i2s_trigger, - }; -diff --git a/sound/soc/meson/aiu-formatter-i2s.c b/sound/soc/meson/aiu-formatter-i2s.c -new file mode 100644 ---- /dev/null -+++ b/sound/soc/meson/aiu-formatter-i2s.c -@@ -0,0 +1,103 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// -+// Copyright (c) 2026 BayLibre, SAS. -+// Author: Valerio Setti -+ -+#include -+#include -+#include -+ -+#include "aiu.h" -+#include "gx-formatter.h" -+ -+#define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0) -+#define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5) -+#define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9) -+ -+#define AIU_I2S_DAC_CFG_MSB_FIRST BIT(2) -+ -+static struct snd_soc_dai * -+aiu_formatter_i2s_get_be(struct snd_soc_dapm_widget *w) -+{ -+ struct snd_soc_dapm_path *p; -+ struct snd_soc_dai *be; -+ -+ snd_soc_dapm_widget_for_each_sink_path(w, p) { -+ if (!p->connect) -+ continue; -+ -+ if (p->sink->id == snd_soc_dapm_dai_in) -+ return (struct snd_soc_dai *)p->sink->priv; -+ -+ be = aiu_formatter_i2s_get_be(p->sink); -+ if (be) -+ return be; -+ } -+ -+ return NULL; -+} -+ -+static struct gx_stream * -+aiu_formatter_i2s_get_stream(struct snd_soc_dapm_widget *w) -+{ -+ struct snd_soc_dai *be = aiu_formatter_i2s_get_be(w); -+ -+ if (!be) -+ return NULL; -+ -+ return snd_soc_dai_dma_data_get_playback(be); -+} -+ -+static int aiu_formatter_i2s_prepare(struct regmap *map, -+ const struct gx_formatter_hw *quirks, -+ struct gx_stream *ts) -+{ -+ /* Always operate in split (classic interleaved) mode */ -+ unsigned int desc = 0; -+ -+ /* -+ * Pipeline reset is already implemented in aiu_fifo_i2s_trigger() at -+ * trigger time. -+ */ -+ -+ switch (ts->physical_width) { -+ case 16: /* Nothing to do */ -+ break; -+ -+ case 32: -+ desc |= (AIU_I2S_SOURCE_DESC_MODE_24BIT | -+ AIU_I2S_SOURCE_DESC_MODE_32BIT); -+ break; -+ -+ default: -+ return -EINVAL; -+ } -+ -+ switch (ts->channels) { -+ case 2: /* Nothing to do */ -+ break; -+ case 8: -+ desc |= AIU_I2S_SOURCE_DESC_MODE_8CH; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ regmap_update_bits(map, AIU_I2S_SOURCE_DESC, -+ AIU_I2S_SOURCE_DESC_MODE_8CH | -+ AIU_I2S_SOURCE_DESC_MODE_24BIT | -+ AIU_I2S_SOURCE_DESC_MODE_32BIT, -+ desc); -+ -+ /* Send data MSB first */ -+ regmap_update_bits(map, AIU_I2S_DAC_CFG, -+ AIU_I2S_DAC_CFG_MSB_FIRST, -+ AIU_I2S_DAC_CFG_MSB_FIRST); -+ -+ return 0; -+} -+ -+const struct gx_formatter_ops aiu_formatter_i2s_ops = { -+ .get_stream = aiu_formatter_i2s_get_stream, -+ .prepare = aiu_formatter_i2s_prepare, -+}; -diff --git a/sound/soc/meson/aiu.c b/sound/soc/meson/aiu.c ---- a/sound/soc/meson/aiu.c -+++ b/sound/soc/meson/aiu.c -@@ -29,13 +29,22 @@ static SOC_ENUM_SINGLE_DECL(aiu_spdif_encode_sel_enum, AIU_I2S_MISC, - static const struct snd_kcontrol_new aiu_spdif_encode_mux = - SOC_DAPM_ENUM("SPDIF Buffer Src", aiu_spdif_encode_sel_enum); - --static const struct snd_soc_dapm_widget aiu_cpu_dapm_widgets[] = { -- SND_SOC_DAPM_MUX("SPDIF SRC SEL", SND_SOC_NOPM, 0, 0, -- &aiu_spdif_encode_mux), -+#define AIU_WIDGET_SPDIF_SRC_SEL 0 -+#define AIU_WIDGET_I2S_FORMATTER 1 -+ -+static struct snd_soc_dapm_widget aiu_cpu_dapm_widgets[] = { -+ [AIU_WIDGET_SPDIF_SRC_SEL] = -+ SND_SOC_DAPM_MUX("SPDIF SRC SEL", SND_SOC_NOPM, 0, 0, -+ &aiu_spdif_encode_mux), -+ [AIU_WIDGET_I2S_FORMATTER] = -+ SND_SOC_DAPM_PGA_E("I2S Formatter", SND_SOC_NOPM, 0, 0, NULL, 0, -+ gx_formatter_event, -+ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD)), - }; - - static const struct snd_soc_dapm_route aiu_cpu_dapm_routes[] = { -- { "I2S Encoder Playback", NULL, "I2S FIFO Playback" }, -+ { "I2S Formatter", NULL, "I2S FIFO Playback" }, -+ { "I2S Encoder Playback", NULL, "I2S Formatter" }, - { "SPDIF SRC SEL", "SPDIF", "SPDIF FIFO Playback" }, - { "SPDIF SRC SEL", "I2S", "I2S FIFO Playback" }, - { "SPDIF Encoder Playback", NULL, "SPDIF SRC SEL" }, -@@ -145,6 +154,7 @@ static struct snd_soc_dai_driver aiu_cpu_dai_drv[] = { - .formats = AIU_FORMATS, - }, - .ops = &aiu_encoder_i2s_dai_ops, -+ .symmetric_rate = 1, - }, - [CPU_SPDIF_ENCODER] = { - .name = "SPDIF Encoder", -@@ -172,6 +182,11 @@ static const struct regmap_config aiu_regmap_cfg = { - .max_register = 0x2ac, - }; - -+static const struct gx_formatter_driver aiu_formatter_i2s_drv = { -+ .regmap_cfg = &aiu_regmap_cfg, -+ .ops = &aiu_formatter_i2s_ops, -+}; -+ - static int aiu_clk_bulk_get(struct device *dev, - const char * const *ids, - unsigned int num, -@@ -282,6 +297,14 @@ static int aiu_probe(struct platform_device *pdev) - if (ret) - return ret; - -+ /* Allocate the aiu-formatter into its widget */ -+ ret = gx_formatter_create(dev, &aiu_cpu_dapm_widgets[AIU_WIDGET_I2S_FORMATTER], -+ &aiu_formatter_i2s_drv, map); -+ if (ret) { -+ dev_err(dev, "Failed to allocate aiu formatter\n"); -+ goto err; -+ } -+ - /* Register the cpu component of the aiu */ - ret = snd_soc_register_component(dev, &aiu_cpu_component, - aiu_cpu_dai_drv, -@@ -310,12 +333,14 @@ static int aiu_probe(struct platform_device *pdev) - - return 0; - err: -+ gx_formatter_free(&aiu_cpu_dapm_widgets[AIU_WIDGET_I2S_FORMATTER]); - snd_soc_unregister_component(dev); - return ret; - } - - static void aiu_remove(struct platform_device *pdev) - { -+ gx_formatter_free(&aiu_cpu_dapm_widgets[AIU_WIDGET_I2S_FORMATTER]); - snd_soc_unregister_component(&pdev->dev); - } - -diff --git a/sound/soc/meson/aiu.h b/sound/soc/meson/aiu.h ---- a/sound/soc/meson/aiu.h -+++ b/sound/soc/meson/aiu.h -@@ -7,6 +7,8 @@ - #ifndef _MESON_AIU_H - #define _MESON_AIU_H - -+#include "gx-formatter.h" -+ - struct clk; - struct clk_bulk_data; - struct device; -@@ -25,6 +27,7 @@ struct aiu_interface { - struct clk_bulk_data *clks; - unsigned int clk_num; - int irq; -+ struct gx_iface iface; - }; - - struct aiu_platform_data { -@@ -58,6 +61,7 @@ extern const struct snd_soc_dai_ops aiu_fifo_i2s_dai_ops; - extern const struct snd_soc_dai_ops aiu_fifo_spdif_dai_ops; - extern const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops; - extern const struct snd_soc_dai_ops aiu_encoder_spdif_dai_ops; -+extern const struct gx_formatter_ops aiu_formatter_i2s_ops; - - #define AIU_IEC958_BPF 0x000 - #define AIU_958_MISC 0x010 -diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c ---- a/sound/soc/meson/axg-card.c -+++ b/sound/soc/meson/axg-card.c -@@ -107,6 +107,7 @@ static int axg_card_add_tdm_loopback(struct snd_soc_card *card, - struct snd_soc_dai_link *pad; - struct snd_soc_dai_link *lb; - struct snd_soc_dai_link_component *dlc; -+ struct device *dev = card->dev; - int ret; - - /* extend links */ -@@ -117,11 +118,11 @@ static int axg_card_add_tdm_loopback(struct snd_soc_card *card, - pad = &card->dai_link[*index]; - lb = &card->dai_link[*index + 1]; - -- lb->name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-lb", pad->name); -+ lb->name = devm_kasprintf(dev, GFP_KERNEL, "%s-lb", pad->name); - if (!lb->name) - return -ENOMEM; - -- dlc = devm_kzalloc(card->dev, sizeof(*dlc), GFP_KERNEL); -+ dlc = devm_kzalloc(dev, sizeof(*dlc), GFP_KERNEL); - if (!dlc) - return -ENOMEM; - -@@ -158,13 +159,14 @@ static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card, - struct device_node *node, - struct axg_dai_link_tdm_data *be) - { -+ struct device *dev = card->dev; - char propname[32]; - u32 tx, rx; - int i; - -- be->tx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES, -+ be->tx_mask = devm_kcalloc(dev, AXG_TDM_NUM_LANES, - sizeof(*be->tx_mask), GFP_KERNEL); -- be->rx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES, -+ be->rx_mask = devm_kcalloc(dev, AXG_TDM_NUM_LANES, - sizeof(*be->rx_mask), GFP_KERNEL); - if (!be->tx_mask || !be->rx_mask) - return -ENOMEM; -@@ -191,7 +193,7 @@ static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card, - - /* ... but the interface should at least have one direction */ - if (!tx && !rx) { -- dev_err(card->dev, "tdm link has no cpu slots\n"); -+ dev_err(dev, "tdm link has no cpu slots\n"); - return -EINVAL; - } - -@@ -207,7 +209,7 @@ static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card, - * Error if the slots can't accommodate the largest mask or - * if it is just too big - */ -- dev_err(card->dev, "bad slot number\n"); -+ dev_err(dev, "bad slot number\n"); - return -EINVAL; - } - -@@ -222,8 +224,9 @@ static int axg_card_parse_codecs_masks(struct snd_soc_card *card, - struct axg_dai_link_tdm_data *be) - { - struct axg_dai_link_tdm_mask *codec_mask; -+ struct device *dev = card->dev; - -- codec_mask = devm_kcalloc(card->dev, link->num_codecs, -+ codec_mask = devm_kcalloc(dev, link->num_codecs, - sizeof(*codec_mask), GFP_KERNEL); - if (!codec_mask) - return -ENOMEM; -@@ -249,10 +252,11 @@ static int axg_card_parse_tdm(struct snd_soc_card *card, - struct meson_card *priv = snd_soc_card_get_drvdata(card); - struct snd_soc_dai_link *link = &card->dai_link[*index]; - struct axg_dai_link_tdm_data *be; -+ struct device *dev = card->dev; - int ret; - - /* Allocate tdm link parameters */ -- be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL); -+ be = devm_kzalloc(dev, sizeof(*be), GFP_KERNEL); - if (!be) - return -ENOMEM; - priv->link_data[*index] = be; -@@ -266,7 +270,7 @@ static int axg_card_parse_tdm(struct snd_soc_card *card, - - ret = axg_card_parse_cpu_tdm_slots(card, link, node, be); - if (ret) { -- dev_err(card->dev, "error parsing tdm link slots\n"); -+ dev_err(dev, "error parsing tdm link slots\n"); - return ret; - } - -@@ -310,9 +314,10 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np, - { - struct snd_soc_dai_link *dai_link = &card->dai_link[*index]; - struct snd_soc_dai_link_component *cpu; -+ struct device *dev = card->dev; - int ret; - -- cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL); -+ cpu = devm_kzalloc(dev, sizeof(*cpu), GFP_KERNEL); - if (!cpu) - return -ENOMEM; - -diff --git a/sound/soc/meson/g12a-toacodec.c b/sound/soc/meson/g12a-toacodec.c ---- a/sound/soc/meson/g12a-toacodec.c -+++ b/sound/soc/meson/g12a-toacodec.c -@@ -312,7 +312,7 @@ static int g12a_toacodec_probe(struct platform_device *pdev) - - ret = device_reset(dev); - if (ret) -- return ret; -+ return dev_err_probe(dev, ret, "failed to reset device\n"); - - regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(regs)) -diff --git a/sound/soc/meson/g12a-tohdmitx.c b/sound/soc/meson/g12a-tohdmitx.c ---- a/sound/soc/meson/g12a-tohdmitx.c -+++ b/sound/soc/meson/g12a-tohdmitx.c -@@ -251,7 +251,7 @@ static int g12a_tohdmitx_probe(struct platform_device *pdev) - - ret = device_reset(dev); - if (ret) -- return ret; -+ return dev_err_probe(dev, ret, "failed to reset device\n"); - - regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(regs)) -diff --git a/sound/soc/meson/gx-card.c b/sound/soc/meson/gx-card.c ---- a/sound/soc/meson/gx-card.c -+++ b/sound/soc/meson/gx-card.c -@@ -48,9 +48,10 @@ static int gx_card_parse_i2s(struct snd_soc_card *card, - struct meson_card *priv = snd_soc_card_get_drvdata(card); - struct snd_soc_dai_link *link = &card->dai_link[*index]; - struct gx_dai_link_i2s_data *be; -+ struct device *dev = card->dev; - - /* Allocate i2s link parameters */ -- be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL); -+ be = devm_kzalloc(dev, sizeof(*be), GFP_KERNEL); - if (!be) - return -ENOMEM; - priv->link_data[*index] = be; -@@ -81,9 +82,10 @@ static int gx_card_add_link(struct snd_soc_card *card, struct device_node *np, - { - struct snd_soc_dai_link *dai_link = &card->dai_link[*index]; - struct snd_soc_dai_link_component *cpu; -+ struct device *dev = card->dev; - int ret; - -- cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL); -+ cpu = devm_kzalloc(dev, sizeof(*cpu), GFP_KERNEL); - if (!cpu) - return -ENOMEM; - -diff --git a/sound/soc/meson/gx-formatter.c b/sound/soc/meson/gx-formatter.c -new file mode 100644 ---- /dev/null -+++ b/sound/soc/meson/gx-formatter.c -@@ -0,0 +1,282 @@ -+// SPDX-License-Identifier: (GPL-2.0 OR MIT) -+// -+// Copyright (c) 2026 BayLibre, SAS. -+// Author: Valerio Setti -+ -+#include -+#include -+#include -+#include -+ -+#include "gx-formatter.h" -+ -+struct gx_formatter { -+ struct list_head list; -+ struct gx_stream *stream; -+ const struct gx_formatter_driver *drv; -+ bool enabled; -+ struct regmap *map; -+}; -+ -+static int gx_formatter_enable(struct gx_formatter *formatter) -+{ -+ int ret; -+ -+ /* Do nothing if the formatter is already enabled */ -+ if (formatter->enabled) -+ return 0; -+ -+ /* Setup the stream parameter in the formatter */ -+ if (formatter->drv->ops->prepare) { -+ ret = formatter->drv->ops->prepare(formatter->map, -+ formatter->drv->quirks, -+ formatter->stream); -+ if (ret) -+ return ret; -+ } -+ -+ /* Finally, actually enable the formatter */ -+ if (formatter->drv->ops->enable) -+ formatter->drv->ops->enable(formatter->map); -+ -+ formatter->enabled = true; -+ -+ return 0; -+} -+ -+static void gx_formatter_disable(struct gx_formatter *formatter) -+{ -+ /* Do nothing if the formatter is already disabled */ -+ if (!formatter->enabled) -+ return; -+ -+ if (formatter->drv->ops->disable) -+ formatter->drv->ops->disable(formatter->map); -+ -+ formatter->enabled = false; -+} -+ -+static int gx_formatter_attach(struct gx_formatter *formatter) -+{ -+ struct gx_stream *ts = formatter->stream; -+ int ret = 0; -+ -+ mutex_lock(&ts->lock); -+ -+ /* Catch up if the stream is already running when we attach */ -+ if (ts->ready) { -+ ret = gx_formatter_enable(formatter); -+ if (ret) { -+ pr_err("failed to enable formatter\n"); -+ goto out; -+ } -+ } -+ -+ list_add_tail(&formatter->list, &ts->formatter_list); -+out: -+ mutex_unlock(&ts->lock); -+ return ret; -+} -+ -+static void gx_formatter_detach(struct gx_formatter *formatter) -+{ -+ struct gx_stream *ts = formatter->stream; -+ -+ if (!ts) -+ return; -+ -+ mutex_lock(&ts->lock); -+ list_del(&formatter->list); -+ mutex_unlock(&ts->lock); -+ -+ gx_formatter_disable(formatter); -+} -+ -+static int gx_formatter_power_up(struct gx_formatter *formatter, -+ struct snd_soc_dapm_widget *w) -+{ -+ struct gx_stream *ts = formatter->drv->ops->get_stream(w); -+ int ret; -+ -+ /* -+ * If we don't get a stream at this stage, it would mean that the -+ * widget is powering up but is not attached to any backend DAI. -+ * It should not happen, ever ! -+ */ -+ if (WARN_ON(!ts)) -+ return -ENODEV; -+ -+ formatter->stream = ts; -+ INIT_LIST_HEAD(&formatter->list); -+ ret = gx_formatter_attach(formatter); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+static void gx_formatter_power_down(struct gx_formatter *formatter) -+{ -+ gx_formatter_detach(formatter); -+ formatter->stream = NULL; -+} -+ -+int gx_formatter_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *control, -+ int event) -+{ -+ struct snd_soc_component *c; -+ struct gx_formatter *formatter; -+ int ret = 0; -+ -+ c = snd_soc_dapm_to_component(w->dapm); -+ -+ if (w->priv) -+ formatter = w->priv; -+ else -+ formatter = snd_soc_component_get_drvdata(c); -+ -+ switch (event) { -+ case SND_SOC_DAPM_PRE_PMU: -+ ret = gx_formatter_power_up(formatter, w); -+ break; -+ -+ case SND_SOC_DAPM_PRE_PMD: -+ gx_formatter_power_down(formatter); -+ break; -+ -+ default: -+ dev_err(c->dev, "Unexpected event %d\n", event); -+ return -EINVAL; -+ } -+ -+ return ret; -+} -+EXPORT_SYMBOL_GPL(gx_formatter_event); -+ -+int gx_formatter_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ const struct gx_formatter_driver *drv; -+ struct gx_formatter *formatter; -+ void __iomem *regs; -+ -+ drv = of_device_get_match_data(dev); -+ if (!drv) { -+ dev_err(dev, "failed to match device\n"); -+ return -ENODEV; -+ } -+ -+ formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL); -+ if (!formatter) -+ return -ENOMEM; -+ platform_set_drvdata(pdev, formatter); -+ formatter->drv = drv; -+ -+ regs = devm_platform_ioremap_resource(pdev, 0); -+ if (IS_ERR(regs)) -+ return PTR_ERR(regs); -+ -+ formatter->map = devm_regmap_init_mmio(dev, regs, drv->regmap_cfg); -+ if (IS_ERR(formatter->map)) { -+ dev_err(dev, "failed to init regmap: %ld\n", -+ PTR_ERR(formatter->map)); -+ return PTR_ERR(formatter->map); -+ } -+ -+ return devm_snd_soc_register_component(dev, drv->component_drv, -+ NULL, 0); -+} -+EXPORT_SYMBOL_GPL(gx_formatter_probe); -+ -+int gx_formatter_create(struct device *dev, -+ struct snd_soc_dapm_widget *w, -+ const struct gx_formatter_driver *drv, -+ struct regmap *regmap) -+{ -+ struct gx_formatter *formatter; -+ -+ formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL); -+ if (!formatter) -+ return -ENOMEM; -+ -+ formatter->drv = drv; -+ formatter->map = regmap; -+ -+ w->priv = formatter; -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(gx_formatter_create); -+ -+int gx_stream_start(struct gx_stream *ts) -+{ -+ struct gx_formatter *formatter; -+ int ret = 0; -+ -+ mutex_lock(&ts->lock); -+ -+ /* Start all the formatters attached to the stream */ -+ list_for_each_entry(formatter, &ts->formatter_list, list) { -+ ret = gx_formatter_enable(formatter); -+ if (ret) { -+ pr_err("failed to enable formatter\n"); -+ goto out; -+ } -+ } -+ -+ ts->ready = true; -+ -+out: -+ mutex_unlock(&ts->lock); -+ return ret; -+} -+EXPORT_SYMBOL_GPL(gx_stream_start); -+ -+void gx_stream_stop(struct gx_stream *ts) -+{ -+ struct gx_formatter *formatter; -+ -+ mutex_lock(&ts->lock); -+ ts->ready = false; -+ -+ /* Stop all the formatters attached to the stream */ -+ list_for_each_entry(formatter, &ts->formatter_list, list) { -+ gx_formatter_disable(formatter); -+ } -+ -+ mutex_unlock(&ts->lock); -+} -+EXPORT_SYMBOL_GPL(gx_stream_stop); -+ -+struct gx_stream *gx_stream_alloc(struct gx_iface *iface) -+{ -+ struct gx_stream *ts; -+ -+ ts = kzalloc(sizeof(*ts), GFP_KERNEL); -+ if (ts) { -+ INIT_LIST_HEAD(&ts->formatter_list); -+ mutex_init(&ts->lock); -+ ts->iface = iface; -+ } -+ -+ return ts; -+} -+EXPORT_SYMBOL_GPL(gx_stream_alloc); -+ -+void gx_stream_free(struct gx_stream *ts) -+{ -+ /* -+ * If the list is not empty, it would mean that one of the formatter -+ * widget is still powered and attached to the interface while we -+ * are removing the TDM DAI. It should not be possible -+ */ -+ WARN_ON(!list_empty(&ts->formatter_list)); -+ mutex_destroy(&ts->lock); -+ kfree(ts); -+} -+EXPORT_SYMBOL_GPL(gx_stream_free); -+ -+MODULE_DESCRIPTION("Amlogic GX formatter driver"); -+MODULE_AUTHOR("Valerio Setti "); -+MODULE_LICENSE("GPL"); -diff --git a/sound/soc/meson/gx-formatter.h b/sound/soc/meson/gx-formatter.h -new file mode 100644 ---- /dev/null -+++ b/sound/soc/meson/gx-formatter.h -@@ -0,0 +1,56 @@ -+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ -+/* -+ * Copyright (c) 2026 Baylibre SAS. -+ * Author: Valerio Setti -+ */ -+ -+#ifndef _MESON_GX_FORMATTER_H -+#define _MESON_GX_FORMATTER_H -+ -+#include "gx-interface.h" -+ -+struct platform_device; -+struct regmap; -+struct snd_soc_dapm_widget; -+struct snd_kcontrol; -+ -+struct gx_formatter_hw { -+ unsigned int skew_offset; -+}; -+ -+struct gx_formatter_ops { -+ struct gx_stream *(*get_stream)(struct snd_soc_dapm_widget *w); -+ void (*enable)(struct regmap *map); -+ void (*disable)(struct regmap *map); -+ int (*prepare)(struct regmap *map, -+ const struct gx_formatter_hw *quirks, -+ struct gx_stream *ts); -+}; -+ -+struct gx_formatter_driver { -+ const struct snd_soc_component_driver *component_drv; -+ const struct regmap_config *regmap_cfg; -+ const struct gx_formatter_ops *ops; -+ const struct gx_formatter_hw *quirks; -+}; -+ -+int gx_formatter_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *control, -+ int event); -+int gx_formatter_probe(struct platform_device *pdev); -+ -+int gx_formatter_create(struct device *dev, -+ struct snd_soc_dapm_widget *w, -+ const struct gx_formatter_driver *drv, -+ struct regmap *regmap); -+ -+/* -+ * Formatter data is already freed when the associated device is removed, -+ * so we just need to remove the pointer from the widget. -+ */ -+static inline void gx_formatter_free(struct snd_soc_dapm_widget *w) -+{ -+ w->priv = NULL; -+} -+ -+#endif /* _MESON_GX_FORMATTER_H */ -diff --git a/sound/soc/meson/gx-interface.h b/sound/soc/meson/gx-interface.h -new file mode 100644 ---- /dev/null -+++ b/sound/soc/meson/gx-interface.h -@@ -0,0 +1,42 @@ -+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ -+/* -+ * Copyright (c) 2026 Baylibre SAS. -+ * Author: Valerio Setti -+ */ -+ -+#ifndef _MESON_GX_INTERFACE_H -+#define _MESON_GX_INTERFACE_H -+ -+#include -+#include -+#include -+#include -+#include -+ -+struct gx_iface { -+ struct clk *mclk; -+ unsigned long mclk_rate; -+ -+ /* format is common to all the DAIs of the iface */ -+ unsigned int fmt; -+}; -+ -+struct gx_stream { -+ struct gx_iface *iface; -+ struct list_head formatter_list; -+ struct mutex lock; -+ unsigned int channels; -+ unsigned int width; -+ unsigned int physical_width; -+ bool ready; -+ -+ /* For continuous clock tracking */ -+ bool clk_enabled; -+}; -+ -+struct gx_stream *gx_stream_alloc(struct gx_iface *iface); -+void gx_stream_free(struct gx_stream *ts); -+int gx_stream_start(struct gx_stream *ts); -+void gx_stream_stop(struct gx_stream *ts); -+ -+#endif /* _MESON_GX_INTERFACE_H */ -diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c ---- a/sound/soc/meson/meson-card-utils.c -+++ b/sound/soc/meson/meson-card-utils.c -@@ -50,25 +50,20 @@ int meson_card_reallocate_links(struct snd_soc_card *card, - num_links * sizeof(*priv->card.dai_link), - GFP_KERNEL | __GFP_ZERO); - if (!links) -- goto err_links; -+ return -ENOMEM; -+ -+ priv->card.dai_link = links; -+ priv->card.num_links = num_links; - - ldata = krealloc(priv->link_data, - num_links * sizeof(*priv->link_data), - GFP_KERNEL | __GFP_ZERO); -+ /* meson_card_clean_references() will free the links on this error path */ - if (!ldata) -- goto err_ldata; -+ return -ENOMEM; - -- priv->card.dai_link = links; - priv->link_data = ldata; -- priv->card.num_links = num_links; - return 0; -- --err_ldata: -- kfree(links); --err_links: -- dev_err(priv->card.dev, "failed to allocate links\n"); -- return -ENOMEM; -- - } - EXPORT_SYMBOL_GPL(meson_card_reallocate_links); - -@@ -76,6 +71,7 @@ int meson_card_parse_dai(struct snd_soc_card *card, - struct device_node *node, - struct snd_soc_dai_link_component *dlc) - { -+ struct device *dev = card->dev; - int ret; - - if (!dlc || !node) -@@ -83,7 +79,7 @@ int meson_card_parse_dai(struct snd_soc_card *card, - - ret = snd_soc_of_get_dlc(node, NULL, dlc, 0); - if (ret) -- return dev_err_probe(card->dev, ret, "can't parse dai\n"); -+ return dev_err_probe(dev, ret, "can't parse dai\n"); - - return ret; - } -@@ -94,7 +90,8 @@ static int meson_card_set_link_name(struct snd_soc_card *card, - struct device_node *node, - const char *prefix) - { -- char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s", -+ struct device *dev = card->dev; -+ char *name = devm_kasprintf(dev, GFP_KERNEL, "%s.%s", - prefix, node->full_name); - if (!name) - return -ENOMEM; -@@ -137,16 +134,17 @@ int meson_card_set_be_link(struct snd_soc_card *card, - struct device_node *node) - { - struct snd_soc_dai_link_component *codec; -+ struct device *dev = card->dev; - int ret, num_codecs; - - num_codecs = of_get_child_count(node); - if (!num_codecs) { -- dev_err(card->dev, "be link %s has no codec\n", -+ dev_err(dev, "be link %s has no codec\n", - node->full_name); - return -EINVAL; - } - -- codec = devm_kcalloc(card->dev, num_codecs, sizeof(*codec), GFP_KERNEL); -+ codec = devm_kcalloc(dev, num_codecs, sizeof(*codec), GFP_KERNEL); - if (!codec) - return -ENOMEM; - -@@ -163,7 +161,7 @@ int meson_card_set_be_link(struct snd_soc_card *card, - - ret = meson_card_set_link_name(card, link, node, "be"); - if (ret) -- dev_err(card->dev, "error setting %pOFn link name\n", node); -+ dev_err(dev, "error setting %pOFn link name\n", node); - - return ret; - } -@@ -194,12 +192,13 @@ EXPORT_SYMBOL_GPL(meson_card_set_fe_link); - static int meson_card_add_links(struct snd_soc_card *card) - { - struct meson_card *priv = snd_soc_card_get_drvdata(card); -- struct device_node *node = card->dev->of_node; -+ struct device *dev = card->dev; -+ struct device_node *node = dev->of_node; - int num, i, ret; - - num = of_get_child_count(node); - if (!num) { -- dev_err(card->dev, "card has no links\n"); -+ dev_err(dev, "card has no links\n"); - return -EINVAL; - } - -@@ -224,8 +223,10 @@ static int meson_card_parse_of_optional(struct snd_soc_card *card, - int (*func)(struct snd_soc_card *c, - const char *p)) - { -+ struct device *dev = card->dev; -+ - /* If property is not provided, don't fail ... */ -- if (!of_property_present(card->dev->of_node, propname)) -+ if (!of_property_present(dev->of_node, propname)) - return 0; - - /* ... but do fail if it is provided and the parsing fails */ diff --git a/sound/soc/meson/meson-codec-glue.c b/sound/soc/meson/meson-codec-glue.c --- a/sound/soc/meson/meson-codec-glue.c +++ b/sound/soc/meson/meson-codec-glue.c @@ -41302,8834 +11487,6 @@ diff --git a/sound/soc/meson/meson-codec-glue.c b/sound/soc/meson/meson-codec-gl data->params.channels_min = params_channels(params); data->params.channels_max = params_channels(params); data->params.sig_bits = dai->driver->playback.sig_bits; -diff --git a/sound/soc/meson/t9015.c b/sound/soc/meson/t9015.c ---- a/sound/soc/meson/t9015.c -+++ b/sound/soc/meson/t9015.c -@@ -78,8 +78,14 @@ static int t9015_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) - return 0; - } - -+static const u64 t9015_dai_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J; -+ - static const struct snd_soc_dai_ops t9015_dai_ops = { - .set_fmt = t9015_dai_set_fmt, -+ .auto_selectable_formats = &t9015_dai_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static struct snd_soc_dai_driver t9015_dai = { -@@ -265,10 +271,8 @@ static int t9015_probe(struct platform_device *pdev) - return dev_err_probe(dev, PTR_ERR(priv->avdd), "failed to AVDD\n"); - - ret = device_reset(dev); -- if (ret) { -- dev_err(dev, "reset failed\n"); -- return ret; -- } -+ if (ret) -+ return dev_err_probe(dev, ret, "failed to reset device\n"); - - regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(regs)) { -diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c ---- a/sound/soc/mxs/mxs-saif.c -+++ b/sound/soc/mxs/mxs-saif.c -@@ -826,12 +826,9 @@ static int mxs_saif_probe(struct platform_device *pdev) - mxs_saif[saif->id] = saif; - - saif->clk = devm_clk_get(&pdev->dev, NULL); -- if (IS_ERR(saif->clk)) { -- ret = PTR_ERR(saif->clk); -- dev_err(&pdev->dev, "Cannot get the clock: %d\n", -- ret); -- return ret; -- } -+ if (IS_ERR(saif->clk)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(saif->clk), -+ "Cannot get the clock\n"); - - saif->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(saif->base)) -@@ -844,10 +841,8 @@ static int mxs_saif_probe(struct platform_device *pdev) - saif->dev = &pdev->dev; - ret = devm_request_irq(&pdev->dev, irq, mxs_saif_irq, 0, - dev_name(&pdev->dev), saif); -- if (ret) { -- dev_err(&pdev->dev, "failed to request irq\n"); -+ if (ret) - return ret; -- } - - platform_set_drvdata(pdev, saif); - -@@ -860,16 +855,12 @@ static int mxs_saif_probe(struct platform_device *pdev) - - ret = devm_snd_soc_register_component(&pdev->dev, &mxs_saif_component, - &mxs_saif_dai, 1); -- if (ret) { -- dev_err(&pdev->dev, "register DAI failed\n"); -+ if (ret) - return ret; -- } - - ret = mxs_pcm_platform_register(&pdev->dev); -- if (ret) { -- dev_err(&pdev->dev, "register PCM failed: %d\n", ret); -+ if (ret) - return ret; -- } - - return 0; - } -diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c ---- a/sound/soc/mxs/mxs-sgtl5000.c -+++ b/sound/soc/mxs/mxs-sgtl5000.c -@@ -155,8 +155,6 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev) - - ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); - if (ret) { -- dev_err(&pdev->dev, "failed to parse audio-routing (%d)\n", -- ret); - mxs_saif_put_mclk(0); - return ret; - } -diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c ---- a/sound/soc/pxa/pxa-ssp.c -+++ b/sound/soc/pxa/pxa-ssp.c -@@ -769,13 +769,10 @@ static int pxa_ssp_probe(struct snd_soc_dai *dai) - goto err_priv; - } - -- priv->extclk = devm_clk_get(dev, "extclk"); -+ priv->extclk = devm_clk_get_optional(dev, "extclk"); - if (IS_ERR(priv->extclk)) { - ret = PTR_ERR(priv->extclk); -- if (ret == -EPROBE_DEFER) -- goto err_priv; -- -- priv->extclk = NULL; -+ goto err_priv; - } - } else { - priv->ssp = pxa_ssp_request(dai->id + 1, "SoC audio"); -diff --git a/sound/soc/pxa/pxa2xx-ac97-lib.c b/sound/soc/pxa/pxa2xx-ac97-lib.c ---- a/sound/soc/pxa/pxa2xx-ac97-lib.c -+++ b/sound/soc/pxa/pxa2xx-ac97-lib.c -@@ -316,10 +316,8 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev) - int irq; - - ac97_reg_base = devm_platform_ioremap_resource(dev, 0); -- if (IS_ERR(ac97_reg_base)) { -- dev_err(&dev->dev, "Missing MMIO resource\n"); -+ if (IS_ERR(ac97_reg_base)) - return PTR_ERR(ac97_reg_base); -- } - - if (cpu_is_pxa27x()) { - /* Assert reset using GPIOD_OUT_HIGH, because reset is GPIO_ACTIVE_LOW */ -diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c ---- a/sound/soc/qcom/common.c -+++ b/sound/soc/qcom/common.c -@@ -23,6 +23,161 @@ static const struct snd_soc_dapm_widget qcom_jack_snd_widgets[] = { - SND_SOC_DAPM_SPK("DP7 Jack", NULL), - }; - -+static struct device_node *qcom_snd_get_link_node(struct snd_soc_pcm_runtime *rtd) -+{ -+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); -+ struct snd_soc_card *card = rtd->card; -+ struct of_phandle_args args; -+ int ret; -+ -+ if (!card->dev || !card->dev->of_node) -+ return NULL; -+ -+ for_each_available_child_of_node_scoped(card->dev->of_node, np) { -+ struct device_node *cpu_np __free(device_node) = -+ of_get_child_by_name(np, "cpu"); -+ -+ if (!cpu_np) -+ continue; -+ -+ ret = of_parse_phandle_with_args(cpu_np, "sound-dai", "#sound-dai-cells", 0, -+ &args); -+ if (ret) -+ continue; -+ -+ if (args.np == rtd->dai_link->cpus[0].of_node && -+ args.args_count == 1 && args.args[0] == cpu_dai->id) { -+ of_node_put(args.np); -+ return of_node_get(np); -+ } -+ -+ of_node_put(args.np); -+ } -+ -+ return NULL; -+} -+ -+static int qcom_snd_parse_tdm_slot(struct device_node *np, -+ struct qcom_snd_tdm_slot_cfg *cfg) -+{ -+ memset(cfg, 0, sizeof(*cfg)); -+ -+ return snd_soc_of_parse_tdm_slot(np, &cfg->tx_mask, &cfg->rx_mask, -+ &cfg->slots, &cfg->slot_width); -+} -+ -+static int qcom_snd_normalize_tdm_slots(struct qcom_snd_tdm_slot_cfg *cpu_cfg, -+ struct qcom_snd_tdm_slot_cfg *codec_cfg) -+{ -+ unsigned int slots; -+ unsigned int slot_width; -+ -+ if (cpu_cfg->slots && codec_cfg->slots && cpu_cfg->slots != codec_cfg->slots) -+ return -EINVAL; -+ -+ if (cpu_cfg->slot_width && codec_cfg->slot_width && -+ cpu_cfg->slot_width != codec_cfg->slot_width) -+ return -EINVAL; -+ -+ slots = cpu_cfg->slots ?: codec_cfg->slots; -+ if (!slots) -+ return 0; -+ -+ slot_width = cpu_cfg->slot_width ?: codec_cfg->slot_width; -+ if (!slot_width) -+ return -EINVAL; -+ -+ cpu_cfg->slots = slots; -+ codec_cfg->slots = slots; -+ cpu_cfg->slot_width = slot_width; -+ codec_cfg->slot_width = slot_width; -+ -+ return 0; -+} -+ -+static int qcom_snd_parse_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, -+ struct qcom_snd_tdm_slot_cfg *cpu_cfg, -+ struct qcom_snd_tdm_slot_cfg *codec_cfg) -+{ -+ struct device_node *link_np __free(device_node) = qcom_snd_get_link_node(rtd); -+ int ret; -+ -+ if (!link_np) -+ return -EINVAL; -+ -+ struct device_node *cpu_np __free(device_node) = -+ of_get_child_by_name(link_np, "cpu"); -+ struct device_node *codec_np __free(device_node) = -+ of_get_child_by_name(link_np, "codec"); -+ if (!cpu_np || !codec_np) -+ return -EINVAL; -+ -+ ret = qcom_snd_parse_tdm_slot(cpu_np, cpu_cfg); -+ if (ret) -+ return ret; -+ -+ return qcom_snd_parse_tdm_slot(codec_np, codec_cfg); -+} -+ -+int qcom_snd_get_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, -+ struct qcom_snd_tdm_slot_cfg *cpu_cfg, -+ struct qcom_snd_tdm_slot_cfg *codec_cfg) -+{ -+ int ret; -+ -+ ret = qcom_snd_parse_dai_tdm_slots(rtd, cpu_cfg, codec_cfg); -+ if (ret) -+ return ret; -+ -+ return qcom_snd_normalize_tdm_slots(cpu_cfg, codec_cfg); -+} -+EXPORT_SYMBOL_GPL(qcom_snd_get_dai_tdm_slots); -+ -+int qcom_snd_apply_dai_tdm_slots_cfg(struct snd_soc_pcm_runtime *rtd, -+ const struct qcom_snd_tdm_slot_cfg *cpu_cfg, -+ const struct qcom_snd_tdm_slot_cfg *codec_cfg) -+{ -+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); -+ struct snd_soc_dai *codec_dai; -+ int i; -+ int ret; -+ -+ if (!cpu_cfg->slots) -+ return 0; -+ -+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, cpu_cfg->tx_mask, cpu_cfg->rx_mask, -+ cpu_cfg->slots, cpu_cfg->slot_width); -+ if (ret) -+ return ret; -+ -+ for_each_rtd_codec_dais(rtd, i, codec_dai) { -+ ret = snd_soc_dai_set_tdm_slot(codec_dai, -+ codec_cfg->tx_mask, -+ codec_cfg->rx_mask, -+ codec_cfg->slots, -+ codec_cfg->slot_width); -+ if (ret) -+ return ret; -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(qcom_snd_apply_dai_tdm_slots_cfg); -+ -+int qcom_snd_apply_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd) -+{ -+ struct qcom_snd_tdm_slot_cfg cpu_cfg; -+ struct qcom_snd_tdm_slot_cfg codec_cfg; -+ int ret; -+ -+ ret = qcom_snd_get_dai_tdm_slots(rtd, &cpu_cfg, &codec_cfg); -+ if (ret) -+ return ret == -EINVAL ? 0 : ret; -+ -+ return qcom_snd_apply_dai_tdm_slots_cfg(rtd, &cpu_cfg, &codec_cfg); -+} -+EXPORT_SYMBOL_GPL(qcom_snd_apply_dai_tdm_slots); -+ - int qcom_snd_parse_of(struct snd_soc_card *card) - { - struct device *dev = card->dev; -@@ -91,7 +246,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) - - ret = of_property_read_string(np, "link-name", &link->name); - if (ret) { -- dev_err(card->dev, "error getting codec dai_link name\n"); -+ dev_err(dev, "error getting codec dai_link name\n"); - return ret; - } - -@@ -109,7 +264,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) - - ret = snd_soc_of_get_dlc(cpu, &args, link->cpus, 0); - if (ret) { -- dev_err_probe(card->dev, ret, -+ dev_err_probe(dev, ret, - "%s: error getting cpu dai name\n", link->name); - return ret; - } -@@ -126,7 +281,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) - "sound-dai", - 0); - if (!link->platforms->of_node) { -- dev_err(card->dev, "%s: platform dai not found\n", link->name); -+ dev_err(dev, "%s: platform dai not found\n", link->name); - return -EINVAL; - } - } else { -@@ -136,7 +291,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) - if (codec) { - ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); - if (ret < 0) { -- dev_err_probe(card->dev, ret, -+ dev_err_probe(dev, ret, - "%s: codec dai not found\n", link->name); - return ret; - } -@@ -215,6 +370,7 @@ int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd, - } - - switch (cpu_dai->id) { -+ case LPI_MI2S_RX_0: - case TX_CODEC_DMA_TX_0: - case TX_CODEC_DMA_TX_1: - case TX_CODEC_DMA_TX_2: -diff --git a/sound/soc/qcom/common.h b/sound/soc/qcom/common.h ---- a/sound/soc/qcom/common.h -+++ b/sound/soc/qcom/common.h -@@ -7,9 +7,23 @@ - #include - #include - --#define LPASS_MAX_PORT (SENARY_MI2S_TX + 1) -+#define LPASS_MAX_PORT (LPI_MI2S_TX_6 + 1) -+ -+struct qcom_snd_tdm_slot_cfg { -+ unsigned int tx_mask; -+ unsigned int rx_mask; -+ unsigned int slots; -+ unsigned int slot_width; -+}; - - int qcom_snd_parse_of(struct snd_soc_card *card); -+int qcom_snd_get_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, -+ struct qcom_snd_tdm_slot_cfg *cpu_cfg, -+ struct qcom_snd_tdm_slot_cfg *codec_cfg); -+int qcom_snd_apply_dai_tdm_slots_cfg(struct snd_soc_pcm_runtime *rtd, -+ const struct qcom_snd_tdm_slot_cfg *cpu_cfg, -+ const struct qcom_snd_tdm_slot_cfg *codec_cfg); -+int qcom_snd_apply_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd); - int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_jack *jack, bool *jack_setup); - int qcom_snd_dp_jack_setup(struct snd_soc_pcm_runtime *rtd, -diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c ---- a/sound/soc/qcom/qdsp6/audioreach.c -+++ b/sound/soc/qcom/qdsp6/audioreach.c -@@ -152,6 +152,13 @@ struct apm_i2s_module_intf_cfg { - - #define APM_I2S_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_i2s_module_intf_cfg), 8) - -+struct apm_audio_if_module_intf_cfg { -+ struct apm_module_param_data param_data; -+ struct param_id_audio_if_intf_cfg cfg; -+} __packed; -+ -+#define APM_AUDIO_IF_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_audio_if_module_intf_cfg), 8) -+ - struct apm_module_hw_ep_mf_cfg { - struct apm_module_param_data param_data; - struct param_id_hw_ep_mf mf; -@@ -168,6 +175,13 @@ struct apm_module_frame_size_factor_cfg { - - #define APM_FS_CFG_PSIZE ALIGN(sizeof(struct apm_module_frame_size_factor_cfg), 8) - -+struct apm_module_hw_ep_frame_duration_cfg { -+ struct apm_module_param_data param_data; -+ struct param_id_hw_ep_frame_duration frame_duration; -+} __packed; -+ -+#define APM_HW_EP_FRAME_DURATION_PSIZE ALIGN(sizeof(struct apm_module_hw_ep_frame_duration_cfg), 8) -+ - struct apm_module_hw_ep_power_mode_cfg { - struct apm_module_param_data param_data; - struct param_id_hw_ep_power_mode_cfg power_mode; -@@ -703,6 +717,7 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph, - int pm_sz = APM_HW_EP_PMODE_CFG_PSIZE; - int size = ic_sz + ep_sz + fs_sz + pm_sz; - void *p; -+ int i; - - struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0); - if (IS_ERR(pkt)) -@@ -741,7 +756,12 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph, - - intf_cfg->cfg.lpaif_type = module->hw_interface_type; - intf_cfg->cfg.intf_index = module->hw_interface_idx; -- intf_cfg->cfg.active_channels_mask = (1 << cfg->num_channels) - 1; -+ intf_cfg->cfg.active_channels_mask = 0; -+ /* Convert the physical channel mapping into a bit field */ -+ for (i = 0; i < AR_PCM_MAX_NUM_CHANNEL; i++) -+ if (cfg->channel_map[i]) -+ intf_cfg->cfg.active_channels_mask |= BIT(i); -+ - p += ic_sz; - - pm_cfg = p; -@@ -840,7 +860,7 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, - uint32_t num_channels = cfg->num_channels; - int payload_size = APM_MFC_CFG_PSIZE(media_format, num_channels) + - APM_MODULE_PARAM_DATA_SIZE; -- int i; -+ int i, j; - void *p; - - struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); -@@ -860,8 +880,12 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, - media_format->sample_rate = cfg->sample_rate; - media_format->bit_width = cfg->bit_width; - media_format->num_channels = cfg->num_channels; -- for (i = 0; i < num_channels; i++) -- media_format->channel_mapping[i] = cfg->channel_map[i]; -+ /* Convert the physical mapping to a logical mapping of the channels */ -+ for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < cfg->num_channels; i++) { -+ if (!cfg->channel_map[i]) -+ continue; -+ media_format->channel_mapping[j++] = cfg->channel_map[i]; -+ } - - return q6apm_send_cmd_sync(graph->apm, pkt, 0); - } -@@ -1042,6 +1066,81 @@ static int audioreach_i2s_set_media_format(struct q6apm_graph *graph, - return q6apm_send_cmd_sync(graph->apm, pkt, 0); - } - -+static int audioreach_audio_if_set_media_format(struct q6apm_graph *graph, -+ const struct audioreach_module *module, -+ const struct audioreach_module_config *cfg) -+{ -+ struct apm_module_hw_ep_frame_duration_cfg *fd_cfg; -+ struct apm_module_param_data *param_data; -+ struct apm_audio_if_module_intf_cfg *intf_cfg; -+ struct apm_module_hw_ep_mf_cfg *hw_cfg; -+ int ic_sz = APM_AUDIO_IF_INTF_CFG_PSIZE; -+ int ep_sz = APM_HW_EP_CFG_PSIZE; -+ int fd_sz = APM_HW_EP_FRAME_DURATION_PSIZE; -+ int size = ic_sz + ep_sz + fd_sz; -+ u32 slot_mask = cfg->slot_mask ? cfg->slot_mask : module->slot_mask; -+ u16 nslots_per_frame = cfg->nslots_per_frame ? -+ (u16)cfg->nslots_per_frame : module->nslots_per_frame; -+ u16 slot_width = cfg->slot_width ? (u16)cfg->slot_width : module->slot_width; -+ void *p; -+ -+ struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0); -+ if (IS_ERR(pkt)) -+ return PTR_ERR(pkt); -+ -+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; -+ intf_cfg = p; -+ -+ param_data = &intf_cfg->param_data; -+ param_data->module_instance_id = module->instance_id; -+ param_data->error_code = 0; -+ param_data->param_id = PARAM_ID_AUDIO_IF_INTF_CFG; -+ param_data->param_size = ic_sz - APM_MODULE_PARAM_DATA_SIZE; -+ intf_cfg->cfg.qaif_type = module->qaif_type; -+ intf_cfg->cfg.intf_idx = (u16)module->hw_interface_idx; -+ intf_cfg->cfg.intf_mode = module->intf_mode; -+ intf_cfg->cfg.ctrl_data_out_enable = module->ctrl_data_out_enable; -+ intf_cfg->cfg.active_slot_mask = slot_mask; -+ intf_cfg->cfg.nslots_per_frame = nslots_per_frame; -+ intf_cfg->cfg.slot_width = slot_width; -+ intf_cfg->cfg.active_lane_mask = module->active_lane_mask; -+ intf_cfg->cfg.frame_sync_rate = module->frame_sync_rate; -+ intf_cfg->cfg.frame_sync_src = module->sync_src; -+ intf_cfg->cfg.frame_sync_mode = module->sync_mode; -+ intf_cfg->cfg.invert_frame_sync_pulse = module->ctrl_invert_sync_pulse; -+ intf_cfg->cfg.frame_sync_data_delay = module->ctrl_sync_data_delay; -+ intf_cfg->cfg.bit_clk_type = module->bit_clk_type; -+ intf_cfg->cfg.inv_int_bit_clk = module->inv_int_bit_clk; -+ intf_cfg->cfg.inv_ext_bit_clk = module->inv_ext_bit_clk; -+ -+ p += ic_sz; -+ hw_cfg = p; -+ param_data = &hw_cfg->param_data; -+ param_data->module_instance_id = module->instance_id; -+ param_data->error_code = 0; -+ param_data->param_id = PARAM_ID_HW_EP_MF_CFG; -+ param_data->param_size = ep_sz - APM_MODULE_PARAM_DATA_SIZE; -+ -+ hw_cfg->mf.sample_rate = cfg->sample_rate; -+ hw_cfg->mf.bit_width = cfg->bit_width; -+ hw_cfg->mf.num_channels = cfg->num_channels; -+ hw_cfg->mf.data_format = module->data_format; -+ -+ p += ep_sz; -+ fd_cfg = p; -+ param_data = &fd_cfg->param_data; -+ param_data->module_instance_id = module->instance_id; -+ param_data->error_code = 0; -+ param_data->param_id = PARAM_ID_HW_EP_FRAME_DURATION; -+ param_data->param_size = fd_sz - APM_MODULE_PARAM_DATA_SIZE; -+ fd_cfg->frame_duration.frame_duration_in_us = AUDIO_IF_FRAME_DURATION_US; -+ fd_cfg->frame_duration.allow_frame_duration_normalization = AUDIO_IF_FRAME_DURATION_NORMALIZATION_ENABLE; -+ fd_cfg->frame_duration.min_normalized_frame_dur_us = AUDIO_IF_FRAME_DURATION_MIN_US; -+ fd_cfg->frame_duration.max_normalized_frame_dur_us = AUDIO_IF_FRAME_DURATION_MAX_US; -+ -+ return q6apm_send_cmd_sync(graph->apm, pkt, 0); -+} -+ - static int audioreach_logging_set_media_format(struct q6apm_graph *graph, - const struct audioreach_module *module) - { -@@ -1080,6 +1179,7 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph, - struct apm_pcm_module_media_fmt_cmd *cfg; - struct apm_module_param_data *param_data; - int payload_size; -+ int i, j; - - if (num_channels > 4) { - dev_err(graph->dev, "Error: Invalid channels (%d)!\n", num_channels); -@@ -1113,7 +1213,12 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph, - media_cfg->num_channels = mcfg->num_channels; - media_cfg->q_factor = mcfg->bit_width - 1; - media_cfg->bits_per_sample = mcfg->bit_width; -- memcpy(media_cfg->channel_mapping, mcfg->channel_map, mcfg->num_channels); -+ /* Convert the physical mapping to a logical mapping of the channels */ -+ for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < mcfg->num_channels; i++) { -+ if (!mcfg->channel_map[i]) -+ continue; -+ media_cfg->channel_mapping[j++] = mcfg->channel_map[i]; -+ } - - return q6apm_send_cmd_sync(graph->apm, pkt, 0); - } -@@ -1163,6 +1268,7 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph, - struct payload_media_fmt_pcm *cfg; - struct media_format *header; - int rc, payload_size; -+ int i, j; - void *p; - - if (num_channels > 4) { -@@ -1202,7 +1308,12 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph, - cfg->q_factor = mcfg->bit_width - 1; - cfg->endianness = PCM_LITTLE_ENDIAN; - cfg->num_channels = mcfg->num_channels; -- memcpy(cfg->channel_mapping, mcfg->channel_map, mcfg->num_channels); -+ /* Convert the physical mapping to a logical mapping of the channels */ -+ for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < cfg->num_channels; i++) { -+ if (!mcfg->channel_map[i]) -+ continue; -+ cfg->channel_mapping[j++] = mcfg->channel_map[i]; -+ } - } else { - rc = audioreach_set_compr_media_format(header, p, mcfg); - if (rc) -@@ -1279,7 +1390,7 @@ static int audioreach_speaker_protection_vi(struct q6apm_graph *graph, - struct apm_module_sp_vi_ex_mode_cfg *ex_cfg; - int op_sz, cm_sz, ex_sz; - struct apm_module_param_data *param_data; -- int rc, i, payload_size; -+ int rc, i, payload_size, j; - struct gpr_pkt *pkt; - void *p; - -@@ -1320,14 +1431,19 @@ static int audioreach_speaker_protection_vi(struct q6apm_graph *graph, - param_data->param_size = cm_sz - APM_MODULE_PARAM_DATA_SIZE; - - cm_cfg->cfg.num_channels = num_channels * 2; -- for (i = 0; i < num_channels; i++) { -+ /* Convert the physical mapping to a logical mapping of the channels */ -+ for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < num_channels; i++) { -+ if (!mcfg->channel_map[i]) -+ continue; - /* - * Map speakers into Vsense and then Isense of each channel. - * E.g. for PCM_CHANNEL_FL and PCM_CHANNEL_FR to: - * [1, 2, 3, 4] - */ -- cm_cfg->cfg.channel_mapping[2 * i] = (mcfg->channel_map[i] - 1) * 2 + 1; -- cm_cfg->cfg.channel_mapping[2 * i + 1] = (mcfg->channel_map[i] - 1) * 2 + 2; -+ cm_cfg->cfg.channel_mapping[2 * j] = (mcfg->channel_map[i] - 1) * 2 + 1; -+ cm_cfg->cfg.channel_mapping[2 * j + 1] = (mcfg->channel_map[i] - 1) * 2 + 2; -+ -+ ++j; - } - - p += cm_sz; -@@ -1411,6 +1527,10 @@ int audioreach_set_media_format(struct q6apm_graph *graph, - if (!rc) - rc = audioreach_module_enable(graph, module, true); - break; -+ case MODULE_ID_AUDIO_IF_SOURCE: -+ case MODULE_ID_AUDIO_IF_SINK: -+ rc = audioreach_audio_if_set_media_format(graph, module, cfg); -+ break; - - default: - rc = 0; -diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h ---- a/sound/soc/qcom/qdsp6/audioreach.h -+++ b/sound/soc/qcom/qdsp6/audioreach.h -@@ -36,6 +36,8 @@ struct q6apm_graph; - #define MODULE_ID_SPEAKER_PROTECTION 0x070010E2 - #define MODULE_ID_SPEAKER_PROTECTION_VI 0x070010E3 - #define MODULE_ID_OPUS_DEC 0x07001174 -+#define MODULE_ID_AUDIO_IF_SINK 0x0700117C -+#define MODULE_ID_AUDIO_IF_SOURCE 0x0700117D - - #define APM_CMD_GET_SPF_STATE 0x01001021 - #define APM_CMD_RSP_GET_SPF_STATE 0x02001007 -@@ -544,6 +546,74 @@ struct param_id_i2s_intf_cfg { - #define PORT_ID_I2S_OUPUT 1 - #define I2S_STACK_SIZE 2048 - -+#define PARAM_ID_AUDIO_IF_INTF_CFG 0x08001B11 -+ -+/* -+ * struct param_id_audio_if_intf_cfg - Audio interface configuration -+ * @qaif_type: Audio interface type (e.g. QAIF, QAIF_VA) -+ * @intf_idx: Interface instance index -+ * @intf_mode: Interface operating mode (TDM/PCM/I2S) -+ * @ctrl_data_out_enable: Enable sharing of data-out signal with other masters -+ * @active_slot_mask: Bitmask indicating active slots -+ * @nslots_per_frame: Number of slots per audio frame -+ * @slot_width: Width of each slot in bits -+ * @active_lane_mask: Bitmask of active data lanes -+ * @frame_sync_rate: Frame sync rate in Hz -+ * @frame_sync_src: Frame sync source selection -+ * @frame_sync_mode: Frame sync mode configuration -+ * @invert_frame_sync_pulse: Invert frame sync polarity when set -+ * @frame_sync_data_delay: Data delay from frame sync in bit clocks -+ * @bit_clk_type: Bit clock type (internal / external) -+ * @inv_int_bit_clk: Invert internal bit clock when set -+ * @inv_ext_bit_clk: Invert external bit clock when set -+ * -+ * This structure defines configuration parameters for the Qualcomm -+ * Audio Interface (QAIF) block. It is used to program interface -+ * characteristics such as slot configuration, clocking and frame -+ * synchronization behaviour. -+ */ -+struct param_id_audio_if_intf_cfg { -+ uint16_t qaif_type; -+ uint16_t intf_idx; -+ uint16_t intf_mode; -+ uint16_t ctrl_data_out_enable; -+ uint32_t active_slot_mask; -+ uint16_t nslots_per_frame; -+ uint16_t slot_width; -+ uint32_t active_lane_mask; -+ uint32_t frame_sync_rate; -+ uint16_t frame_sync_src; -+ uint16_t frame_sync_mode; -+ uint16_t invert_frame_sync_pulse; -+ uint16_t frame_sync_data_delay; -+ uint16_t bit_clk_type; -+ uint8_t inv_int_bit_clk; -+ uint8_t inv_ext_bit_clk; -+} __packed; -+ -+#define PARAM_ID_HW_EP_FRAME_DURATION 0x08001B2F -+#define AUDIO_IF_FRAME_DURATION_US 1000 -+#define AUDIO_IF_FRAME_DURATION_NORMALIZATION_ENABLE 1 -+#define AUDIO_IF_FRAME_DURATION_MIN_US 1 -+#define AUDIO_IF_FRAME_DURATION_MAX_US 100000 -+ -+/** -+ * struct param_id_hw_ep_frame_duration - Hardware endpoint frame duration -+ * @frame_duration_in_us: Frame duration in microseconds. -+ * @allow_frame_duration_normalization: Permit SPF to normalize frame duration. -+ * @min_normalized_frame_dur_us: Minimum normalized frame duration in microseconds. -+ * @max_normalized_frame_dur_us: Maximum normalized frame duration in microseconds. -+ * -+ * This structure configures the frame duration for the Audio IF hardware -+ * endpoint and, when enabled, the allowed normalization range. -+ */ -+struct param_id_hw_ep_frame_duration { -+ uint32_t frame_duration_in_us; -+ uint32_t allow_frame_duration_normalization; -+ uint32_t min_normalized_frame_dur_us; -+ uint32_t max_normalized_frame_dur_us; -+} __packed; -+ - #define PARAM_ID_DISPLAY_PORT_INTF_CFG 0x08001154 - - struct param_id_display_port_intf_cfg { -@@ -877,6 +947,23 @@ struct audioreach_module { - uint32_t data_format; - uint32_t hw_interface_type; - -+ /* Audio IF module (TDM/PCM/I2S) */ -+ u32 slot_mask; -+ u32 active_lane_mask; -+ u32 frame_sync_rate; -+ u16 qaif_type; -+ u16 sync_src; -+ u16 ctrl_data_out_enable; -+ u16 nslots_per_frame; -+ u16 slot_width; -+ u16 intf_mode; -+ u16 sync_mode; -+ u16 ctrl_invert_sync_pulse; -+ u16 ctrl_sync_data_delay; -+ u16 bit_clk_type; -+ u8 inv_int_bit_clk; -+ u8 inv_ext_bit_clk; -+ - /* PCM module specific */ - uint32_t interleave_type; - -@@ -907,6 +994,9 @@ struct audioreach_module_config { - u32 channel_allocation; - u32 sd_line_mask; - int fmt; -+ u32 slot_mask; -+ u16 nslots_per_frame; -+ u16 slot_width; - struct snd_codec codec; - u8 channel_map[AR_PCM_MAX_NUM_CHANNEL]; - }; -diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c ---- a/sound/soc/qcom/qdsp6/q6afe-dai.c -+++ b/sound/soc/qcom/qdsp6/q6afe-dai.c -@@ -412,6 +412,8 @@ static int q6afe_dai_prepare(struct snd_pcm_substream *substream, - case SENARY_MI2S_RX ... SENARY_MI2S_TX: - case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: - case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: -+ case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: -+ case LPI_MI2S_RX_5 ... LPI_MI2S_TX_6: - rc = q6afe_i2s_port_prepare(dai_data->port[dai->id], - &dai_data->port_config[dai->id].i2s_cfg); - if (rc < 0) { -@@ -665,6 +667,21 @@ static const struct snd_soc_dapm_route q6afe_dapm_routes[] = { - - /* USB playback AFE port receives data for playback, hence use the RX port */ - {"USB Playback", NULL, "USB_RX"}, -+ -+ {"LPI RX0 MI2S Playback", NULL, "LPI_MI2S_RX_0"}, -+ {"LPI_MI2S_TX_0", NULL, "LPI TX0 MI2S Capture"}, -+ {"LPI RX1 MI2S Playback", NULL, "LPI_MI2S_RX_1"}, -+ {"LPI_MI2S_TX_1", NULL, "LPI TX1 MI2S Capture"}, -+ {"LPI RX2 MI2S Playback", NULL, "LPI_MI2S_RX_2"}, -+ {"LPI_MI2S_TX_2", NULL, "LPI TX2 MI2S Capture"}, -+ {"LPI RX3 MI2S Playback", NULL, "LPI_MI2S_RX_3"}, -+ {"LPI_MI2S_TX_3", NULL, "LPI TX3 MI2S Capture"}, -+ {"LPI RX4 MI2S Playback", NULL, "LPI_MI2S_RX_4"}, -+ {"LPI_MI2S_TX_4", NULL, "LPI TX4 MI2S Capture"}, -+ {"LPI RX5 MI2S Playback", NULL, "LPI_MI2S_RX_5"}, -+ {"LPI_MI2S_TX_5", NULL, "LPI TX5 MI2S Capture"}, -+ {"LPI RX6 MI2S Playback", NULL, "LPI_MI2S_RX_6"}, -+ {"LPI_MI2S_TX_6", NULL, "LPI TX6 MI2S Capture"}, - }; - - static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai) -@@ -1011,6 +1028,35 @@ static const struct snd_soc_dapm_widget q6afe_dai_widgets[] = { - 0, SND_SOC_NOPM, 0, 0), - - SND_SOC_DAPM_AIF_IN("USB_RX", NULL, 0, SND_SOC_NOPM, 0, 0), -+ -+ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_0", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_0", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_1", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_1", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_2", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_2", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_3", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_3", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_4", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_4", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_5", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_5", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_6", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_6", NULL, -+ 0, SND_SOC_NOPM, 0, 0), - }; - - static const struct snd_soc_component_driver q6afe_dai_component = { -@@ -1045,6 +1091,8 @@ static void of_q6afe_parse_dai_data(struct device *dev, - case SENARY_MI2S_RX ... SENARY_MI2S_TX: - case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: - case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: -+ case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: -+ case LPI_MI2S_RX_5 ... LPI_MI2S_TX_6: - priv = &data->priv[id]; - ret = of_property_read_variable_u32_array(node, - "qcom,sd-lines", -diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c ---- a/sound/soc/qcom/qdsp6/q6afe.c -+++ b/sound/soc/qcom/qdsp6/q6afe.c -@@ -132,6 +132,20 @@ - #define AFE_PORT_ID_QUINARY_MI2S_TX 0x1017 - #define AFE_PORT_ID_SENARY_MI2S_RX 0x1018 - #define AFE_PORT_ID_SENARY_MI2S_TX 0x1019 -+#define AFE_PORT_ID_INT0_MI2S_RX 0x102e -+#define AFE_PORT_ID_INT0_MI2S_TX 0x102f -+#define AFE_PORT_ID_INT1_MI2S_RX 0x1030 -+#define AFE_PORT_ID_INT1_MI2S_TX 0x1031 -+#define AFE_PORT_ID_INT2_MI2S_RX 0x1032 -+#define AFE_PORT_ID_INT2_MI2S_TX 0x1033 -+#define AFE_PORT_ID_INT3_MI2S_RX 0x1034 -+#define AFE_PORT_ID_INT3_MI2S_TX 0x1035 -+#define AFE_PORT_ID_INT4_MI2S_RX 0x1036 -+#define AFE_PORT_ID_INT4_MI2S_TX 0x1037 -+#define AFE_PORT_ID_INT5_MI2S_RX 0x1038 -+#define AFE_PORT_ID_INT5_MI2S_TX 0x1039 -+#define AFE_PORT_ID_INT6_MI2S_RX 0x103a -+#define AFE_PORT_ID_INT6_MI2S_TX 0x103b - - /* Start of the range of port IDs for TDM devices. */ - #define AFE_PORT_ID_TDM_PORT_RANGE_START 0x9000 -@@ -931,6 +945,34 @@ static struct afe_port_map port_maps[AFE_PORT_MAX] = { - [RX_CODEC_DMA_RX_7] = { AFE_PORT_ID_RX_CODEC_DMA_RX_7, - RX_CODEC_DMA_RX_7, 1, 1}, - [USB_RX] = { AFE_PORT_ID_USB_RX, USB_RX, 1, 1}, -+ [LPI_MI2S_RX_0] = { AFE_PORT_ID_INT0_MI2S_RX, -+ LPI_MI2S_RX_0, 1, 1}, -+ [LPI_MI2S_TX_0] = { AFE_PORT_ID_INT0_MI2S_TX, -+ LPI_MI2S_TX_0, 0, 1}, -+ [LPI_MI2S_RX_1] = { AFE_PORT_ID_INT1_MI2S_RX, -+ LPI_MI2S_RX_1, 1, 1}, -+ [LPI_MI2S_TX_1] = { AFE_PORT_ID_INT1_MI2S_TX, -+ LPI_MI2S_TX_1, 0, 1}, -+ [LPI_MI2S_RX_2] = { AFE_PORT_ID_INT2_MI2S_RX, -+ LPI_MI2S_RX_2, 1, 1}, -+ [LPI_MI2S_TX_2] = { AFE_PORT_ID_INT2_MI2S_TX, -+ LPI_MI2S_TX_2, 0, 1}, -+ [LPI_MI2S_RX_3] = { AFE_PORT_ID_INT3_MI2S_RX, -+ LPI_MI2S_RX_3, 1, 1}, -+ [LPI_MI2S_TX_3] = { AFE_PORT_ID_INT3_MI2S_TX, -+ LPI_MI2S_TX_3, 0, 1}, -+ [LPI_MI2S_RX_4] = { AFE_PORT_ID_INT4_MI2S_RX, -+ LPI_MI2S_RX_4, 1, 1}, -+ [LPI_MI2S_TX_4] = { AFE_PORT_ID_INT4_MI2S_TX, -+ LPI_MI2S_TX_4, 0, 1}, -+ [LPI_MI2S_RX_5] = { AFE_PORT_ID_INT5_MI2S_RX, -+ LPI_MI2S_RX_5, 1, 1}, -+ [LPI_MI2S_TX_5] = { AFE_PORT_ID_INT5_MI2S_TX, -+ LPI_MI2S_TX_5, 0, 1}, -+ [LPI_MI2S_RX_6] = { AFE_PORT_ID_INT6_MI2S_RX, -+ LPI_MI2S_RX_6, 1, 1}, -+ [LPI_MI2S_TX_6] = { AFE_PORT_ID_INT6_MI2S_TX, -+ LPI_MI2S_TX_6, 0, 1}, - }; - - static void q6afe_port_free(struct kref *ref) -@@ -1785,6 +1827,20 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id) - case AFE_PORT_ID_QUINARY_MI2S_TX: - case AFE_PORT_ID_SENARY_MI2S_RX: - case AFE_PORT_ID_SENARY_MI2S_TX: -+ case AFE_PORT_ID_INT0_MI2S_RX: -+ case AFE_PORT_ID_INT0_MI2S_TX: -+ case AFE_PORT_ID_INT1_MI2S_RX: -+ case AFE_PORT_ID_INT1_MI2S_TX: -+ case AFE_PORT_ID_INT2_MI2S_RX: -+ case AFE_PORT_ID_INT2_MI2S_TX: -+ case AFE_PORT_ID_INT3_MI2S_RX: -+ case AFE_PORT_ID_INT3_MI2S_TX: -+ case AFE_PORT_ID_INT4_MI2S_RX: -+ case AFE_PORT_ID_INT4_MI2S_TX: -+ case AFE_PORT_ID_INT5_MI2S_RX: -+ case AFE_PORT_ID_INT5_MI2S_TX: -+ case AFE_PORT_ID_INT6_MI2S_RX: -+ case AFE_PORT_ID_INT6_MI2S_TX: - cfg_type = AFE_PARAM_ID_I2S_CONFIG; - break; - case AFE_PORT_ID_PRIMARY_TDM_RX ... AFE_PORT_ID_QUINARY_TDM_TX_7: -diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c ---- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c -+++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c -@@ -2,10 +2,12 @@ - // Copyright (c) 2021, Linaro Limited - - #include -+#include - #include - #include - #include - #include -+#include - #include - #include - #include -@@ -15,15 +17,54 @@ - #include "q6dsp-common.h" - #include "audioreach.h" - #include "q6apm.h" -+#include "q6prm.h" - - #define AUDIOREACH_BE_PCM_BASE 16 - -+struct q6apm_dai_priv_data { -+ struct clk *mclk; -+ struct clk *bclk; -+ bool mclk_enabled, bclk_enabled; -+}; -+ - struct q6apm_lpass_dai_data { - struct q6apm_graph *graph[APM_PORT_MAX]; - bool is_port_started[APM_PORT_MAX]; - struct audioreach_module_config module_config[APM_PORT_MAX]; -+ struct q6apm_dai_priv_data priv[APM_PORT_MAX]; - }; - -+static void q6apm_lpass_dai_disable_clocks(struct q6apm_lpass_dai_data *dai_data, int id) -+{ -+ if (dai_data->priv[id].mclk_enabled) { -+ clk_disable_unprepare(dai_data->priv[id].mclk); -+ dai_data->priv[id].mclk_enabled = false; -+ } -+ -+ if (dai_data->priv[id].bclk_enabled) { -+ clk_disable_unprepare(dai_data->priv[id].bclk); -+ dai_data->priv[id].bclk_enabled = false; -+ } -+} -+ -+static void q6apm_lpass_dai_put_clocks(struct q6apm_lpass_dai_data *dai_data) -+{ -+ int i; -+ -+ for (i = 0; i < APM_PORT_MAX; i++) { -+ q6apm_lpass_dai_disable_clocks(dai_data, i); -+ -+ if (dai_data->priv[i].mclk) { -+ clk_put(dai_data->priv[i].mclk); -+ dai_data->priv[i].mclk = NULL; -+ } -+ if (dai_data->priv[i].bclk) { -+ clk_put(dai_data->priv[i].bclk); -+ dai_data->priv[i].bclk = NULL; -+ } -+ } -+} -+ - static int q6dma_set_channel_map(struct snd_soc_dai *dai, - unsigned int tx_num, - const unsigned int *tx_ch_mask, -@@ -251,6 +292,62 @@ static int q6apm_lpass_dai_startup(struct snd_pcm_substream *substream, struct s - return 0; - } - -+static int q6i2s_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) -+{ -+ return q6apm_lpass_dai_startup(substream, dai); -+} -+ -+static void q6i2s_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) -+{ -+ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); -+ -+ q6apm_lpass_dai_shutdown(substream, dai); -+ q6apm_lpass_dai_disable_clocks(dai_data, dai->id); -+} -+ -+static int q6i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) -+{ -+ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); -+ struct clk *sysclk = NULL; -+ bool *enabled = NULL; -+ int ret = 0; -+ -+ switch (clk_id) { -+ case LPAIF_MI2S_MCLK: -+ sysclk = dai_data->priv[dai->id].mclk; -+ enabled = &dai_data->priv[dai->id].mclk_enabled; -+ break; -+ case LPAIF_MI2S_BCLK: -+ sysclk = dai_data->priv[dai->id].bclk; -+ enabled = &dai_data->priv[dai->id].bclk_enabled; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ if (sysclk) { -+ ret = clk_set_rate(sysclk, freq); -+ if (ret) { -+ dev_err(dai->dev, "Error, Unable to set rate (%d) for sysclk %d\n", -+ freq, clk_id); -+ return ret; -+ } -+ -+ if (*enabled) -+ return 0; -+ -+ ret = clk_prepare_enable(sysclk); -+ if (ret) { -+ dev_err(dai->dev, "Error, Unable to prepare (%d) sysclk\n", clk_id); -+ return ret; -+ } -+ -+ *enabled = true; -+ } -+ -+ return ret; -+} -+ - static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) - { - struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); -@@ -261,6 +358,53 @@ static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) - return 0; - } - -+static int q6tdm_set_tdm_slot(struct snd_soc_dai *dai, -+ unsigned int tx_mask, -+ unsigned int rx_mask, -+ int slots, int slot_width) -+{ -+ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); -+ struct audioreach_module_config *cfg = &dai_data->module_config[dai->id]; -+ unsigned int cap_mask, slot_mask; -+ -+ if (slot_width != 16 && slot_width != 32) { -+ dev_err(dai->dev, "%s: invalid slot_width %d\n", __func__, slot_width); -+ return -EINVAL; -+ } -+ -+ switch (slots) { -+ case 2: -+ case 4: -+ case 8: -+ case 16: -+ cap_mask = GENMASK(slots - 1, 0); -+ break; -+ default: -+ dev_err(dai->dev, "%s: invalid slots %d\n", __func__, slots); -+ return -EINVAL; -+ } -+ -+ switch (dai->id) { -+ case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: -+ slot_mask = (dai->id & 0x1) ? tx_mask : rx_mask; -+ if (slot_mask & ~cap_mask) { -+ dev_err(dai->dev, "%s: invalid slot mask 0x%x for %d slots\n", -+ __func__, slot_mask, slots); -+ return -EINVAL; -+ } -+ -+ cfg->nslots_per_frame = slots; -+ cfg->slot_width = slot_width; -+ cfg->slot_mask = slot_mask; -+ break; -+ default: -+ dev_err(dai->dev, "%s: invalid dai id 0x%x\n", __func__, dai->id); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ - static const struct snd_soc_dai_ops q6dma_ops = { - .prepare = q6apm_lpass_dai_prepare, - .startup = q6apm_lpass_dai_startup, -@@ -272,11 +416,12 @@ static const struct snd_soc_dai_ops q6dma_ops = { - - static const struct snd_soc_dai_ops q6i2s_ops = { - .prepare = q6apm_lpass_dai_prepare, -- .startup = q6apm_lpass_dai_startup, -- .shutdown = q6apm_lpass_dai_shutdown, -+ .startup = q6i2s_dai_startup, -+ .shutdown = q6i2s_lpass_dai_shutdown, - .set_channel_map = q6dma_set_channel_map, - .hw_params = q6dma_hw_params, - .set_fmt = q6i2s_set_fmt, -+ .set_sysclk = q6i2s_set_sysclk, - .trigger = q6apm_lpass_dai_trigger, - }; - -@@ -289,6 +434,17 @@ static const struct snd_soc_dai_ops q6hdmi_ops = { - .trigger = q6apm_lpass_dai_trigger, - }; - -+static const struct snd_soc_dai_ops q6tdm_ops = { -+ .prepare = q6apm_lpass_dai_prepare, -+ .startup = q6apm_lpass_dai_startup, -+ .shutdown = q6i2s_lpass_dai_shutdown, -+ .set_tdm_slot = q6tdm_set_tdm_slot, -+ .hw_params = q6dma_hw_params, -+ .set_fmt = q6i2s_set_fmt, -+ .set_sysclk = q6i2s_set_sysclk, -+ .trigger = q6apm_lpass_dai_trigger, -+}; -+ - static const struct snd_soc_component_driver q6apm_lpass_dai_component = { - .name = "q6apm-be-dai-component", - .of_xlate_dai_name = q6dsp_audio_ports_of_xlate_dai_name, -@@ -297,6 +453,65 @@ static const struct snd_soc_component_driver q6apm_lpass_dai_component = { - .remove_order = SND_SOC_COMP_ORDER_FIRST, - }; - -+static int of_q6apm_parse_dai_data(struct device *dev, -+ struct q6apm_lpass_dai_data *data) -+{ -+ int ret; -+ -+ for_each_child_of_node_scoped(dev->of_node, node) { -+ struct q6apm_dai_priv_data *priv; -+ int id; -+ -+ ret = of_property_read_u32(node, "reg", &id); -+ if (ret || id < 0 || id >= APM_PORT_MAX) { -+ dev_err(dev, "valid dai id not found:%d\n", ret); -+ continue; -+ } -+ -+ switch (id) { -+ /* MI2S specific properties */ -+ case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: -+ case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: -+ case SENARY_MI2S_RX ... SENARY_MI2S_TX: -+ case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: -+ priv = &data->priv[id]; -+ priv->mclk = of_clk_get_by_name(node, "mclk"); -+ if (IS_ERR(priv->mclk)) { -+ int err = PTR_ERR(priv->mclk); -+ -+ priv->mclk = NULL; -+ if (err == -EPROBE_DEFER) { -+ q6apm_lpass_dai_put_clocks(data); -+ return dev_err_probe(dev, err, -+ "unable to get mi2s mclk\n"); -+ } -+ } -+ -+ priv->bclk = of_clk_get_by_name(node, "bclk"); -+ if (IS_ERR(priv->bclk)) { -+ int err = PTR_ERR(priv->bclk); -+ -+ priv->bclk = NULL; -+ if (err == -EPROBE_DEFER) { -+ q6apm_lpass_dai_put_clocks(data); -+ return dev_err_probe(dev, err, -+ "unable to get mi2s bclk\n"); -+ } -+ } -+ break; -+ default: -+ break; -+ } -+ } -+ -+ return 0; -+} -+ -+static void q6apm_lpass_dai_clocks_action(void *data) -+{ -+ q6apm_lpass_dai_put_clocks(data); -+} -+ - static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) - { - struct q6dsp_audio_port_dai_driver_config cfg; -@@ -304,17 +519,26 @@ static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) - struct snd_soc_dai_driver *dais; - struct device *dev = &pdev->dev; - int num_dais; -+ int ret; - - dai_data = devm_kzalloc(dev, sizeof(*dai_data), GFP_KERNEL); - if (!dai_data) - return -ENOMEM; - - dev_set_drvdata(dev, dai_data); -+ ret = of_q6apm_parse_dai_data(dev, dai_data); -+ if (ret) -+ return ret; -+ -+ ret = devm_add_action_or_reset(dev, q6apm_lpass_dai_clocks_action, dai_data); -+ if (ret) -+ return ret; - - memset(&cfg, 0, sizeof(cfg)); - cfg.q6i2s_ops = &q6i2s_ops; - cfg.q6dma_ops = &q6dma_ops; - cfg.q6hdmi_ops = &q6hdmi_ops; -+ cfg.q6tdm_ops = &q6tdm_ops; - dais = q6dsp_audio_ports_set_config(dev, &cfg, &num_dais); - - return devm_snd_soc_register_component(dev, &q6apm_lpass_dai_component, dais, num_dais); -diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c ---- a/sound/soc/qcom/qdsp6/q6apm.c -+++ b/sound/soc/qcom/qdsp6/q6apm.c -@@ -802,14 +802,17 @@ EXPORT_SYMBOL_GPL(q6apm_graph_prepare); - int q6apm_graph_start(struct q6apm_graph *graph) - { - struct audioreach_graph *ar_graph = graph->ar_graph; -- int ret = 0; -+ int ret; - -- if (ar_graph->start_count == 0) -+ if (ar_graph->start_count == 0) { - ret = audioreach_graph_mgmt_cmd(ar_graph, APM_CMD_GRAPH_START); -+ if (ret) -+ return ret; -+ } - - ar_graph->start_count++; - -- return ret; -+ return 0; - } - EXPORT_SYMBOL_GPL(q6apm_graph_start); - -@@ -817,6 +820,9 @@ int q6apm_graph_stop(struct q6apm_graph *graph) - { - struct audioreach_graph *ar_graph = graph->ar_graph; - -+ if (ar_graph->start_count == 0) -+ return 0; -+ - if (--ar_graph->start_count > 0) - return 0; - -diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c ---- a/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c -+++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c -@@ -12,7 +12,7 @@ - #include - #include "q6dsp-lpass-clocks.h" - --#define Q6DSP_MAX_CLK_ID 104 -+#define Q6DSP_MAX_CLK_ID 105 - #define Q6DSP_LPASS_CLK_ROOT_DEFAULT 0 - - -diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c ---- a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c -+++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c -@@ -553,11 +553,15 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { - Q6AFE_MI2S_RX_DAI("LPI RX2", LPI_MI2S_RX_2), - Q6AFE_MI2S_RX_DAI("LPI RX3", LPI_MI2S_RX_3), - Q6AFE_MI2S_RX_DAI("LPI RX4", LPI_MI2S_RX_4), -+ Q6AFE_MI2S_RX_DAI("LPI RX5", LPI_MI2S_RX_5), -+ Q6AFE_MI2S_RX_DAI("LPI RX6", LPI_MI2S_RX_6), - Q6AFE_MI2S_TX_DAI("LPI TX0", LPI_MI2S_TX_0), - Q6AFE_MI2S_TX_DAI("LPI TX1", LPI_MI2S_TX_1), - Q6AFE_MI2S_TX_DAI("LPI TX2", LPI_MI2S_TX_2), - Q6AFE_MI2S_TX_DAI("LPI TX3", LPI_MI2S_TX_3), - Q6AFE_MI2S_TX_DAI("LPI TX4", LPI_MI2S_TX_4), -+ Q6AFE_MI2S_TX_DAI("LPI TX5", LPI_MI2S_TX_5), -+ Q6AFE_MI2S_TX_DAI("LPI TX6", LPI_MI2S_TX_6), - Q6AFE_TDM_PB_DAI("Primary", 0, PRIMARY_TDM_RX_0), - Q6AFE_TDM_PB_DAI("Primary", 1, PRIMARY_TDM_RX_1), - Q6AFE_TDM_PB_DAI("Primary", 2, PRIMARY_TDM_RX_2), -@@ -712,6 +716,7 @@ struct snd_soc_dai_driver *q6dsp_audio_ports_set_config(struct device *dev, - case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: - case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: - case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: -+ case LPI_MI2S_RX_5 ... LPI_MI2S_TX_6: - q6dsp_audio_fe_dais[i].ops = cfg->q6i2s_ops; - break; - case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: -diff --git a/sound/soc/qcom/qdsp6/q6prm-clocks.c b/sound/soc/qcom/qdsp6/q6prm-clocks.c ---- a/sound/soc/qcom/qdsp6/q6prm-clocks.c -+++ b/sound/soc/qcom/qdsp6/q6prm-clocks.c -@@ -42,6 +42,11 @@ static const struct q6dsp_clk_init q6prm_clks[] = { - Q6PRM_CLK(LPASS_CLK_ID_INT5_MI2S_IBIT), - Q6PRM_CLK(LPASS_CLK_ID_INT6_MI2S_IBIT), - Q6PRM_CLK(LPASS_CLK_ID_QUI_MI2S_OSR), -+ Q6PRM_CLK(LPASS_CLK_ID_MCLK_1), -+ Q6PRM_CLK(LPASS_CLK_ID_MCLK_2), -+ Q6PRM_CLK(LPASS_CLK_ID_MCLK_3), -+ Q6PRM_CLK(LPASS_CLK_ID_MCLK_4), -+ Q6PRM_CLK(LPASS_CLK_ID_MCLK_5), - Q6PRM_CLK(LPASS_CLK_ID_WSA_CORE_MCLK), - Q6PRM_CLK(LPASS_CLK_ID_WSA_CORE_NPL_MCLK), - Q6PRM_CLK(LPASS_CLK_ID_VA_CORE_MCLK), -@@ -59,10 +64,40 @@ static const struct q6dsp_clk_init q6prm_clks[] = { - Q6PRM_CLK(LPASS_CLK_ID_WSA2_CORE_TX_MCLK), - Q6PRM_CLK(LPASS_CLK_ID_WSA2_CORE_TX_2X_MCLK), - Q6PRM_CLK(LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF0_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF0_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF1_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF1_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF2_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF2_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF3_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF3_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF4_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF4_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF5_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF5_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF6_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF6_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF7_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF7_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF8_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF8_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF9_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF9_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF10_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF10_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF11_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF11_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF12_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF12_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_VA_QAIF_IF0_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_VA_QAIF_IF0_EBIT), - Q6DSP_VOTE_CLK(LPASS_HW_MACRO_VOTE, Q6PRM_HW_CORE_ID_LPASS, - "LPASS_HW_MACRO"), - Q6DSP_VOTE_CLK(LPASS_HW_DCODEC_VOTE, Q6PRM_HW_CORE_ID_DCODEC, - "LPASS_HW_DCODEC"), -+ Q6DSP_VOTE_CLK(LPASS_HW_LPR_VOTE, Q6PRM_HW_LPR_VOTE, -+ "LPASS_HW_LPR_VOTE"), - }; - - static const struct q6dsp_clk_desc q6dsp_clk_q6prm __maybe_unused = { -diff --git a/sound/soc/qcom/qdsp6/q6prm.c b/sound/soc/qcom/qdsp6/q6prm.c ---- a/sound/soc/qcom/qdsp6/q6prm.c -+++ b/sound/soc/qcom/qdsp6/q6prm.c -@@ -31,10 +31,16 @@ struct q6prm { - #define PARAM_ID_RSC_HW_CORE 0x08001032 - #define PARAM_ID_RSC_LPASS_CORE 0x0800102B - #define PARAM_ID_RSC_AUDIO_HW_CLK 0x0800102C -+#define PARAM_ID_RSC_CPU_LPR 0x08001A6E -+ -+#define LPR_CPU_SS_SLEEP_DISABLE 0x1 - - struct prm_cmd_request_hw_core { - struct apm_module_param_data param_data; -- uint32_t hw_clk_id; -+ union { -+ u32 hw_clk_id; -+ u32 lpr_state; -+ }; - } __packed; - - struct prm_cmd_request_rsc { -@@ -62,6 +68,7 @@ static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool - struct prm_cmd_request_hw_core *req; - gpr_device_t *gdev = prm->gdev; - uint32_t opcode, rsp_opcode; -+ bool lpr_req = (hw_block_id == Q6PRM_HW_LPR_VOTE); - - if (enable) { - opcode = PRM_CMD_REQUEST_HW_RSC; -@@ -82,10 +89,13 @@ static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool - - param_data->module_instance_id = GPR_PRM_MODULE_IID; - param_data->error_code = 0; -- param_data->param_id = PARAM_ID_RSC_HW_CORE; -+ param_data->param_id = lpr_req ? PARAM_ID_RSC_CPU_LPR : PARAM_ID_RSC_HW_CORE; - param_data->param_size = sizeof(*req) - APM_MODULE_PARAM_DATA_SIZE; - -- req->hw_clk_id = hw_block_id; -+ if (lpr_req) -+ req->lpr_state = LPR_CPU_SS_SLEEP_DISABLE; -+ else -+ req->hw_clk_id = hw_block_id; - - return q6prm_send_cmd_sync(prm, pkt, rsp_opcode); - } -diff --git a/sound/soc/qcom/qdsp6/q6prm.h b/sound/soc/qcom/qdsp6/q6prm.h ---- a/sound/soc/qcom/qdsp6/q6prm.h -+++ b/sound/soc/qcom/qdsp6/q6prm.h -@@ -3,6 +3,9 @@ - #ifndef __Q6PRM_H__ - #define __Q6PRM_H__ - -+#define LPAIF_MI2S_MCLK 1 -+#define LPAIF_MI2S_BCLK 2 -+ - /* Clock ID for Primary I2S IBIT */ - #define Q6PRM_LPASS_CLK_ID_PRI_MI2S_IBIT 0x100 - /* Clock ID for Primary I2S EBIT */ -@@ -52,6 +55,17 @@ - /* Clock ID for QUINARY MI2S OSR CLK */ - #define Q6PRM_LPASS_CLK_ID_QUI_MI2S_OSR 0x116 - -+/* Clock ID for MCLK1 */ -+#define Q6PRM_LPASS_CLK_ID_MCLK_1 0x300 -+/* Clock ID for MCLK2 */ -+#define Q6PRM_LPASS_CLK_ID_MCLK_2 0x301 -+/* Clock ID for MCLK3 */ -+#define Q6PRM_LPASS_CLK_ID_MCLK_3 0x302 -+/* Clock ID for MCLK4 */ -+#define Q6PRM_LPASS_CLK_ID_MCLK_4 0x303 -+/* Clock ID for MCLK5 */ -+#define Q6PRM_LPASS_CLK_ID_MCLK_5 0x304 -+ - #define Q6PRM_LPASS_CLK_ID_WSA_CORE_MCLK 0x305 - #define Q6PRM_LPASS_CLK_ID_WSA_CORE_NPL_MCLK 0x306 - -@@ -83,10 +97,40 @@ - /* Clock ID for RX CORE MCLK2 2X MCLK */ - #define Q6PRM_LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK 0x318 - -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF0_IBIT 0x500 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF0_EBIT 0x501 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF1_IBIT 0x502 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF1_EBIT 0x503 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF2_IBIT 0x504 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF2_EBIT 0x505 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF3_IBIT 0x506 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF3_EBIT 0x507 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF4_IBIT 0x508 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF4_EBIT 0x509 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF5_IBIT 0x50A -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF5_EBIT 0x50B -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF6_IBIT 0x50C -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF6_EBIT 0x50D -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF7_IBIT 0x50E -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF7_EBIT 0x50F -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF8_IBIT 0x510 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF8_EBIT 0x511 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF9_IBIT 0x512 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF9_EBIT 0x513 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF10_IBIT 0x514 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF10_EBIT 0x515 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF11_IBIT 0x516 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF11_EBIT 0x517 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF12_IBIT 0x518 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF12_EBIT 0x519 -+#define Q6PRM_LAPSS_CLK_ID_VA_QAIF_IF0_IBIT 0x550 -+#define Q6PRM_LAPSS_CLK_ID_VA_QAIF_IF0_EBIT 0x551 -+ - #define Q6PRM_LPASS_CLK_SRC_INTERNAL 1 - #define Q6PRM_LPASS_CLK_ROOT_DEFAULT 0 - #define Q6PRM_HW_CORE_ID_LPASS 1 - #define Q6PRM_HW_CORE_ID_DCODEC 2 -+#define Q6PRM_HW_LPR_VOTE 3 - - int q6prm_set_lpass_clock(struct device *dev, int clk_id, int clk_attr, - int clk_root, unsigned int freq); -diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c ---- a/sound/soc/qcom/qdsp6/q6routing.c -+++ b/sound/soc/qcom/qdsp6/q6routing.c -@@ -127,7 +127,14 @@ - { mix_name, "TX_CODEC_DMA_TX_2", "TX_CODEC_DMA_TX_2"}, \ - { mix_name, "TX_CODEC_DMA_TX_3", "TX_CODEC_DMA_TX_3"}, \ - { mix_name, "TX_CODEC_DMA_TX_4", "TX_CODEC_DMA_TX_4"}, \ -- { mix_name, "TX_CODEC_DMA_TX_5", "TX_CODEC_DMA_TX_5"} -+ { mix_name, "TX_CODEC_DMA_TX_5", "TX_CODEC_DMA_TX_5"}, \ -+ { mix_name, "LPI_MI2S_TX_0", "LPI_MI2S_TX_0" }, \ -+ { mix_name, "LPI_MI2S_TX_1", "LPI_MI2S_TX_1" }, \ -+ { mix_name, "LPI_MI2S_TX_2", "LPI_MI2S_TX_2" }, \ -+ { mix_name, "LPI_MI2S_TX_3", "LPI_MI2S_TX_3" }, \ -+ { mix_name, "LPI_MI2S_TX_4", "LPI_MI2S_TX_4" }, \ -+ { mix_name, "LPI_MI2S_TX_5", "LPI_MI2S_TX_5" }, \ -+ { mix_name, "LPI_MI2S_TX_6", "LPI_MI2S_TX_6" } - - #define Q6ROUTING_TX_MIXERS(id) \ - SOC_SINGLE_EXT("PRI_MI2S_TX", PRIMARY_MI2S_TX, \ -@@ -320,6 +327,27 @@ - id, 1, 0, msm_routing_get_audio_mixer, \ - msm_routing_put_audio_mixer), \ - SOC_SINGLE_EXT("TX_CODEC_DMA_TX_5", TX_CODEC_DMA_TX_5, \ -+ id, 1, 0, msm_routing_get_audio_mixer, \ -+ msm_routing_put_audio_mixer), \ -+ SOC_SINGLE_EXT("LPI_MI2S_TX_0", LPI_MI2S_TX_0, \ -+ id, 1, 0, msm_routing_get_audio_mixer, \ -+ msm_routing_put_audio_mixer), \ -+ SOC_SINGLE_EXT("LPI_MI2S_TX_1", LPI_MI2S_TX_1, \ -+ id, 1, 0, msm_routing_get_audio_mixer, \ -+ msm_routing_put_audio_mixer), \ -+ SOC_SINGLE_EXT("LPI_MI2S_TX_2", LPI_MI2S_TX_2, \ -+ id, 1, 0, msm_routing_get_audio_mixer, \ -+ msm_routing_put_audio_mixer), \ -+ SOC_SINGLE_EXT("LPI_MI2S_TX_3", LPI_MI2S_TX_3, \ -+ id, 1, 0, msm_routing_get_audio_mixer, \ -+ msm_routing_put_audio_mixer), \ -+ SOC_SINGLE_EXT("LPI_MI2S_TX_4", LPI_MI2S_TX_4, \ -+ id, 1, 0, msm_routing_get_audio_mixer, \ -+ msm_routing_put_audio_mixer), \ -+ SOC_SINGLE_EXT("LPI_MI2S_TX_5", LPI_MI2S_TX_5, \ -+ id, 1, 0, msm_routing_get_audio_mixer, \ -+ msm_routing_put_audio_mixer), \ -+ SOC_SINGLE_EXT("LPI_MI2S_TX_6", LPI_MI2S_TX_6, \ - id, 1, 0, msm_routing_get_audio_mixer, \ - msm_routing_put_audio_mixer), - -@@ -709,6 +737,26 @@ static const struct snd_kcontrol_new rxcodec_dma_rx_6_mixer_controls[] = { - static const struct snd_kcontrol_new rx_codec_dma_rx_7_mixer_controls[] = { - Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_7) }; - -+static const struct snd_kcontrol_new lpi_mi2s_rx_0_mixer_controls[] = { -+ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_0) }; -+ -+static const struct snd_kcontrol_new lpi_mi2s_rx_1_mixer_controls[] = { -+ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_1) }; -+ -+static const struct snd_kcontrol_new lpi_mi2s_rx_2_mixer_controls[] = { -+ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_2) }; -+ -+static const struct snd_kcontrol_new lpi_mi2s_rx_3_mixer_controls[] = { -+ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_3) }; -+ -+static const struct snd_kcontrol_new lpi_mi2s_rx_4_mixer_controls[] = { -+ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_4) }; -+ -+static const struct snd_kcontrol_new lpi_mi2s_rx_5_mixer_controls[] = { -+ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_5) }; -+ -+static const struct snd_kcontrol_new lpi_mi2s_rx_6_mixer_controls[] = { -+ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_6) }; - - static const struct snd_kcontrol_new mmul1_mixer_controls[] = { - Q6ROUTING_TX_MIXERS(MSM_FRONTEND_DAI_MULTIMEDIA1) }; -@@ -938,6 +986,27 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { - SND_SOC_DAPM_MIXER("USB_RX Audio Mixer", SND_SOC_NOPM, 0, 0, - usb_rx_mixer_controls, - ARRAY_SIZE(usb_rx_mixer_controls)), -+ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, -+ lpi_mi2s_rx_0_mixer_controls, -+ ARRAY_SIZE(lpi_mi2s_rx_0_mixer_controls)), -+ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, -+ lpi_mi2s_rx_1_mixer_controls, -+ ARRAY_SIZE(lpi_mi2s_rx_1_mixer_controls)), -+ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0, -+ lpi_mi2s_rx_2_mixer_controls, -+ ARRAY_SIZE(lpi_mi2s_rx_2_mixer_controls)), -+ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0, -+ lpi_mi2s_rx_3_mixer_controls, -+ ARRAY_SIZE(lpi_mi2s_rx_3_mixer_controls)), -+ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0, -+ lpi_mi2s_rx_4_mixer_controls, -+ ARRAY_SIZE(lpi_mi2s_rx_4_mixer_controls)), -+ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_5 Audio Mixer", SND_SOC_NOPM, 0, 0, -+ lpi_mi2s_rx_5_mixer_controls, -+ ARRAY_SIZE(lpi_mi2s_rx_5_mixer_controls)), -+ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_6 Audio Mixer", SND_SOC_NOPM, 0, 0, -+ lpi_mi2s_rx_6_mixer_controls, -+ ARRAY_SIZE(lpi_mi2s_rx_6_mixer_controls)), - SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0, - mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)), - SND_SOC_DAPM_MIXER("MultiMedia2 Mixer", SND_SOC_NOPM, 0, 0, -@@ -1031,6 +1100,13 @@ static const struct snd_soc_dapm_route intercon[] = { - Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_6 Audio Mixer", "RX_CODEC_DMA_RX_6"), - Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_7 Audio Mixer", "RX_CODEC_DMA_RX_7"), - Q6ROUTING_RX_DAPM_ROUTE("USB_RX Audio Mixer", "USB_RX"), -+ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_0 Audio Mixer", "LPI_MI2S_RX_0"), -+ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_1 Audio Mixer", "LPI_MI2S_RX_1"), -+ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_2 Audio Mixer", "LPI_MI2S_RX_2"), -+ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_3 Audio Mixer", "LPI_MI2S_RX_3"), -+ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_4 Audio Mixer", "LPI_MI2S_RX_4"), -+ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_5 Audio Mixer", "LPI_MI2S_RX_5"), -+ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_6 Audio Mixer", "LPI_MI2S_RX_6"), - Q6ROUTING_TX_DAPM_ROUTE("MultiMedia1 Mixer"), - Q6ROUTING_TX_DAPM_ROUTE("MultiMedia2 Mixer"), - Q6ROUTING_TX_DAPM_ROUTE("MultiMedia3 Mixer"), -diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c ---- a/sound/soc/qcom/qdsp6/topology.c -+++ b/sound/soc/qcom/qdsp6/topology.c -@@ -753,6 +753,103 @@ static int audioreach_widget_i2s_module_load(struct audioreach_module *mod, - return 0; - } - -+static int audioreach_widget_audio_if_module_load(struct audioreach_module *mod, -+ const struct snd_soc_tplg_vendor_array *mod_array) -+{ -+ const struct snd_soc_tplg_vendor_value_elem *mod_elem; -+ int tkn_count = 0; -+ u32 val; -+ -+ mod_elem = mod_array->value; -+ -+ while (tkn_count < le32_to_cpu(mod_array->num_elems)) { -+ val = le32_to_cpu(mod_elem->value); -+ switch (le32_to_cpu(mod_elem->token)) { -+ case AR_TKN_U32_MODULE_HW_IF_IDX: -+ mod->hw_interface_idx = val; -+ break; -+ case AR_TKN_U32_MODULE_FMT_DATA: -+ mod->data_format = val; -+ break; -+ case AR_TKN_U16_MODULE_SYNC_SRC: -+ if (val > U16_MAX) -+ return -EINVAL; -+ mod->sync_src = (u16)val; -+ break; -+ case AR_TKN_U16_MODULE_CTRL_DATA_OUT_ENABLE: -+ if (val > U16_MAX) -+ return -EINVAL; -+ mod->ctrl_data_out_enable = (u16)val; -+ break; -+ case AR_TKN_U32_MODULE_SLOT_MASK: -+ mod->slot_mask = val; -+ break; -+ case AR_TKN_U16_MODULE_NSLOTS_PER_FRAME: -+ if (val > U16_MAX) -+ return -EINVAL; -+ mod->nslots_per_frame = (u16)val; -+ break; -+ case AR_TKN_U16_MODULE_SLOT_WIDTH: -+ if (val > U16_MAX) -+ return -EINVAL; -+ mod->slot_width = (u16)val; -+ break; -+ case AR_TKN_U16_MODULE_INTF_MODE: -+ if (val > U16_MAX) -+ return -EINVAL; -+ mod->intf_mode = (u16)val; -+ break; -+ case AR_TKN_U16_MODULE_SYNC_MODE: -+ if (val > U16_MAX) -+ return -EINVAL; -+ mod->sync_mode = (u16)val; -+ break; -+ case AR_TKN_U16_MODULE_CTRL_INVERT_SYNC_PULSE: -+ if (val > U16_MAX) -+ return -EINVAL; -+ mod->ctrl_invert_sync_pulse = (u16)val; -+ break; -+ case AR_TKN_U16_MODULE_CTRL_SYNC_DATA_DELAY: -+ if (val > U16_MAX) -+ return -EINVAL; -+ mod->ctrl_sync_data_delay = (u16)val; -+ break; -+ case AR_TKN_U16_MODULE_QAIF_TYPE: -+ if (val > U16_MAX) -+ return -EINVAL; -+ mod->qaif_type = (u16)val; -+ break; -+ case AR_TKN_U32_MODULE_ACTIVE_LANE_MASK: -+ mod->active_lane_mask = val; -+ break; -+ case AR_TKN_U32_MODULE_FRAME_SYNC_RATE: -+ mod->frame_sync_rate = val; -+ break; -+ case AR_TKN_U16_MODULE_BIT_CLK_TYPE: -+ if (val > U16_MAX) -+ return -EINVAL; -+ mod->bit_clk_type = (u16)val; -+ break; -+ case AR_TKN_U8_MODULE_INV_INT_BIT_CLK: -+ if (val > U8_MAX) -+ return -EINVAL; -+ mod->inv_int_bit_clk = (u8)val; -+ break; -+ case AR_TKN_U8_MODULE_INV_EXT_BIT_CLK: -+ if (val > U8_MAX) -+ return -EINVAL; -+ mod->inv_ext_bit_clk = (u8)val; -+ break; -+ default: -+ break; -+ } -+ tkn_count++; -+ mod_elem++; -+ } -+ -+ return 0; -+} -+ - static int audioreach_widget_dp_module_load(struct audioreach_module *mod, - const struct snd_soc_tplg_vendor_array *mod_array) - { -@@ -806,6 +903,12 @@ static int audioreach_widget_load_buffer(struct snd_soc_component *component, - case MODULE_ID_I2S_SOURCE: - audioreach_widget_i2s_module_load(mod, mod_array); - break; -+ case MODULE_ID_AUDIO_IF_SINK: -+ case MODULE_ID_AUDIO_IF_SOURCE: -+ ret = audioreach_widget_audio_if_module_load(mod, mod_array); -+ if (ret) -+ return ret; -+ break; - case MODULE_ID_DISPLAY_PORT_SINK: - audioreach_widget_dp_module_load(mod, mod_array); - break; -@@ -1313,7 +1416,6 @@ int audioreach_tplg_init(struct snd_soc_component *component) - { - struct snd_soc_card *card = component->card; - struct device *dev = component->dev; -- const struct firmware *fw; - int ret; - - /* Inline with Qualcomm UCM configs and linux-firmware path */ -@@ -1323,6 +1425,7 @@ int audioreach_tplg_init(struct snd_soc_component *component) - if (!tplg_fw_name) - return -ENOMEM; - -+ const struct firmware *fw __free(firmware) = NULL; - ret = request_firmware(&fw, tplg_fw_name, dev); - if (ret < 0) { - dev_err(dev, "tplg firmware loading %s failed %d\n", tplg_fw_name, ret); -@@ -1335,8 +1438,6 @@ int audioreach_tplg_init(struct snd_soc_component *component) - dev_err(dev, "tplg component load failed: %d\n", ret); - } - -- release_firmware(fw); -- - return ret; - } - EXPORT_SYMBOL_GPL(audioreach_tplg_init); -diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c ---- a/sound/soc/qcom/sc8280xp.c -+++ b/sound/soc/qcom/sc8280xp.c -@@ -12,17 +12,157 @@ - #include - #include - #include "qdsp6/q6afe.h" -+#include "qdsp6/q6apm.h" -+#include "qdsp6/q6prm.h" -+#include "qdsp6/q6dsp-common.h" - #include "common.h" - #include "sdw.h" - -+#define I2S_MCLKFS 256 -+ -+#define I2S_MCLK_RATE(rate) \ -+ ((rate) * (I2S_MCLKFS)) -+#define I2S_BIT_RATE(rate, channels, format) \ -+ ((rate) * (channels) * (format)) -+ -+static struct snd_soc_dapm_widget sc8280xp_dapm_widgets[] = { -+ SND_SOC_DAPM_HP("Headphone Jack", NULL), -+ SND_SOC_DAPM_MIC("Mic Jack", NULL), -+ SND_SOC_DAPM_SPK("DP0 Jack", NULL), -+ SND_SOC_DAPM_SPK("DP1 Jack", NULL), -+ SND_SOC_DAPM_SPK("DP2 Jack", NULL), -+ SND_SOC_DAPM_SPK("DP3 Jack", NULL), -+ SND_SOC_DAPM_SPK("DP4 Jack", NULL), -+ SND_SOC_DAPM_SPK("DP5 Jack", NULL), -+ SND_SOC_DAPM_SPK("DP6 Jack", NULL), -+ SND_SOC_DAPM_SPK("DP7 Jack", NULL), -+}; -+ -+static const struct snd_kcontrol_new max98090_controls[] = { -+ SOC_DAPM_PIN_SWITCH("Headset Mic12"), -+ SOC_DAPM_PIN_SWITCH("Headphone"), -+ SOC_DAPM_PIN_SWITCH("Headset Mic56"), -+ SOC_DAPM_PIN_SWITCH("Speaker"), -+ SOC_DAPM_PIN_SWITCH("Receiver"), -+ SOC_DAPM_PIN_SWITCH("Int Mic"), -+}; -+ -+static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = { -+ SND_SOC_DAPM_HP("Headphone Jack", NULL), -+ SND_SOC_DAPM_MIC("Mic Jack", NULL), -+ SND_SOC_DAPM_HP("Headphone", NULL), -+ SND_SOC_DAPM_MIC("Headset Mic12", NULL), -+ SND_SOC_DAPM_MIC("Headset Mic56", NULL), -+ SND_SOC_DAPM_MIC("Int Mic", NULL), -+ SND_SOC_DAPM_SPK("Receiver", NULL), -+ SND_SOC_DAPM_SPK("Speaker", NULL), -+}; -+ -+struct qcom_snd_soc_common { -+ const char *driver_name; -+ const struct snd_soc_dapm_widget *dapm_widgets; -+ int num_dapm_widgets; -+ const struct snd_soc_dapm_route *dapm_routes; -+ int num_dapm_routes; -+ const struct snd_kcontrol_new *controls; -+ int num_controls; -+ unsigned int codec_dai_fmt; -+ bool codec_sysclk_set; -+ bool mi2s_mclk_enable; -+ bool mi2s_bclk_enable; -+ bool wcd_jack; -+ int (*snd_prepare)(struct snd_pcm_substream *substream); -+}; -+ - struct sc8280xp_snd_data { - bool stream_prepared[AFE_PORT_MAX]; - struct snd_soc_card *card; - struct snd_soc_jack jack; - struct snd_soc_jack dp_jack[8]; -+ const struct qcom_snd_soc_common *priv; - bool jack_setup; - }; - -+static inline int sc8280xp_get_mclk_freq(struct snd_pcm_hw_params *params) -+{ -+ int rate = params_rate(params); -+ -+ switch (rate) { -+ case 11025: -+ case 44100: -+ case 88200: -+ return I2S_MCLK_RATE(44100); -+ default: -+ break; -+ } -+ -+ return I2S_MCLK_RATE(rate); -+} -+ -+static inline int sc8280xp_get_bclk_freq(struct snd_pcm_hw_params *params) -+{ -+ return I2S_BIT_RATE(params_rate(params), -+ params_channels(params), -+ snd_pcm_format_width(params_format(params))); -+} -+ -+static int sc8280xp_tdm_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params) -+{ -+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); -+ struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); -+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); -+ struct snd_soc_dai *codec_dai; -+ struct qcom_snd_tdm_slot_cfg cpu_cfg; -+ struct qcom_snd_tdm_slot_cfg codec_cfg; -+ unsigned int bclk_freq; -+ int ret; -+ int i; -+ -+ ret = qcom_snd_get_dai_tdm_slots(rtd, &cpu_cfg, &codec_cfg); -+ if (ret) -+ return ret == -EINVAL ? 0 : ret; -+ -+ if (!cpu_cfg.slots) -+ return 0; -+ -+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); -+ if (ret) -+ return ret; -+ -+ ret = qcom_snd_apply_dai_tdm_slots_cfg(rtd, &cpu_cfg, &codec_cfg); -+ if (ret) -+ return ret; -+ -+ bclk_freq = snd_soc_tdm_params_to_bclk(params, cpu_cfg.slot_width, cpu_cfg.slots, 1); -+ if (!bclk_freq) -+ return -EINVAL; -+ -+ if (data->priv->mi2s_bclk_enable) { -+ ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_MI2S_BCLK, bclk_freq, -+ SND_SOC_CLOCK_IN); -+ if (ret) { -+ dev_err(rtd->dev, "%s: failed to set cpu sysclk: %d\n", -+ __func__, ret); -+ return ret; -+ } -+ } -+ -+ if (data->priv->codec_sysclk_set) { -+ for_each_rtd_codec_dais(rtd, i, codec_dai) { -+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, bclk_freq, -+ SND_SOC_CLOCK_IN); -+ if (ret) { -+ dev_err(rtd->dev, "%s: failed to set codec sysclk on %s: %d\n", -+ __func__, codec_dai->name, ret); -+ return ret; -+ } -+ } -+ } -+ -+ return 0; -+} -+ - static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) - { - struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); -@@ -32,10 +172,6 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) - int dp_pcm_id = 0; - - switch (cpu_dai->id) { -- case PRIMARY_MI2S_RX...QUATERNARY_MI2S_TX: -- case QUINARY_MI2S_RX...QUINARY_MI2S_TX: -- snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); -- break; - case WSA_CODEC_DMA_RX_0: - case WSA_CODEC_DMA_RX_1: - /* -@@ -64,7 +200,10 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) - if (dp_jack) - return qcom_snd_dp_jack_setup(rtd, dp_jack, dp_pcm_id); - -- return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); -+ if (data->priv->wcd_jack) -+ return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); -+ -+ return 0; - } - - static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, -@@ -96,12 +235,118 @@ static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, - return 0; - } - -+static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params) -+{ -+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); -+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); -+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); -+ struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); -+ int mclk_freq = sc8280xp_get_mclk_freq(params); -+ int bclk_freq = sc8280xp_get_bclk_freq(params); -+ int ret; -+ -+ switch (cpu_dai->id) { -+ case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: -+ case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: -+ case SENARY_MI2S_RX ... SENARY_MI2S_TX: -+ case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: -+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); -+ if (ret && ret != -ENOTSUPP) -+ return ret; -+ -+ if (data->priv->codec_dai_fmt) { -+ ret = snd_soc_dai_set_fmt(codec_dai, -+ data->priv->codec_dai_fmt); -+ if (ret && ret != -ENOTSUPP) -+ return ret; -+ } -+ -+ if (data->priv->mi2s_mclk_enable) { -+ ret = snd_soc_dai_set_sysclk(cpu_dai, -+ LPAIF_MI2S_MCLK, mclk_freq, -+ SND_SOC_CLOCK_OUT); -+ if (ret) -+ return ret; -+ } -+ -+ if (data->priv->mi2s_bclk_enable) { -+ ret = snd_soc_dai_set_sysclk(cpu_dai, -+ LPAIF_MI2S_BCLK, bclk_freq, -+ SND_SOC_CLOCK_OUT); -+ if (ret) -+ return ret; -+ } -+ -+ if (data->priv->codec_sysclk_set) { -+ ret = snd_soc_dai_set_sysclk(codec_dai, -+ 0, mclk_freq, -+ SND_SOC_CLOCK_IN); -+ if (ret && ret != -ENOTSUPP) -+ return ret; -+ } -+ break; -+ case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: -+ return sc8280xp_tdm_hw_params(substream, params); -+ default: -+ break; -+ } -+ -+ return 0; -+} -+ -+/* -+ * WSA and WSA2 are handled as a single interface with the -+ * following channels mask: -+ * __________________________________________________ -+ * | Bits | 3 | 2 | 1 | 0 | -+ * --------------------------------------------------- -+ * | Line | WSA2 Ch2 | WSA2 Ch1 | WSA Ch2 | WSA Ch1 | -+ * --------------------------------------------------- -+ * -+ * The Ayaneo Pocket S2 speakers are connected only to -+ * the WSA2 interface and the WSA interface is not enabled. -+ * -+ * Set the channel mapping on the WSA2 channels only. -+ */ -+static const unsigned int ayaneo_ps2_channels_mapping[] = { -+ 0, /* WSA Ch1 */ -+ 0, /* WSA Ch2 */ -+ PCM_CHANNEL_FL, /* WSA2 Ch1 */ -+ PCM_CHANNEL_FR /* WSA2 Ch2 */ -+}; -+ -+static int ayaneo_ps2_snd_prepare(struct snd_pcm_substream *substream) -+{ -+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); -+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); -+ unsigned int channels = substream->runtime->channels; -+ -+ if (cpu_dai->id != WSA_CODEC_DMA_RX_0) -+ return 0; -+ -+ if (channels != 2) -+ return -EINVAL; -+ -+ return snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, -+ ARRAY_SIZE(ayaneo_ps2_channels_mapping), -+ ayaneo_ps2_channels_mapping); -+} -+ - static int sc8280xp_snd_prepare(struct snd_pcm_substream *substream) - { - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); - struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); - -+ if (data->priv->snd_prepare) { -+ int ret; -+ -+ ret = data->priv->snd_prepare(substream); -+ if (ret) -+ return ret; -+ } -+ - return qcom_snd_sdw_prepare(substream, &data->stream_prepared[cpu_dai->id]); - } - -@@ -117,6 +362,7 @@ static int sc8280xp_snd_hw_free(struct snd_pcm_substream *substream) - static const struct snd_soc_ops sc8280xp_be_ops = { - .startup = qcom_snd_sdw_startup, - .shutdown = qcom_snd_sdw_shutdown, -+ .hw_params = sc8280xp_snd_hw_params, - .hw_free = sc8280xp_snd_hw_free, - .prepare = sc8280xp_snd_prepare, - }; -@@ -145,38 +391,182 @@ static int sc8280xp_platform_probe(struct platform_device *pdev) - card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); - if (!card) - return -ENOMEM; -- card->owner = THIS_MODULE; -+ - /* Allocate the private data */ - data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - -+ data->priv = of_device_get_match_data(dev); -+ if (!data->priv) -+ return -ENODEV; -+ -+ card->owner = THIS_MODULE; - card->dev = dev; - dev_set_drvdata(dev, card); - snd_soc_card_set_drvdata(card, data); -+ card->dapm_widgets = data->priv->dapm_widgets; -+ card->num_dapm_widgets = data->priv->num_dapm_widgets; -+ card->dapm_routes = data->priv->dapm_routes; -+ card->num_dapm_routes = data->priv->num_dapm_routes; -+ card->controls = data->priv->controls; -+ card->num_controls = data->priv->num_controls; -+ - ret = qcom_snd_parse_of(card); - if (ret) - return ret; - -- card->driver_name = of_device_get_match_data(dev); -+ card->driver_name = data->priv->driver_name; - sc8280xp_add_be_ops(card); - return devm_snd_soc_register_card(dev, card); - } - -+static struct qcom_snd_soc_common ayaneo_ps2_priv_data = { -+ .driver_name = "ayaneo-ps2", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .snd_prepare = ayaneo_ps2_snd_prepare, -+ .wcd_jack = true, -+}; -+ -+static const struct qcom_snd_soc_common eliza_priv_data = { -+ .driver_name = "eliza", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .wcd_jack = true, -+}; -+ -+static const struct qcom_snd_soc_common hawi_priv_data = { -+ .driver_name = "hawi", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .codec_sysclk_set = true, -+ .mi2s_bclk_enable = true, -+ .wcd_jack = true, -+}; -+ -+static const struct qcom_snd_soc_common kaanapali_priv_data = { -+ .driver_name = "kaanapali", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .wcd_jack = true, -+}; -+ -+static const struct qcom_snd_soc_common qcs9100_priv_data = { -+ .driver_name = "sa8775p", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+}; -+ -+static const struct qcom_snd_soc_common qcs615_priv_data = { -+ .driver_name = "qcs615", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .codec_sysclk_set = true, -+}; -+ -+static const struct qcom_snd_soc_common qcm6490_priv_data = { -+ .driver_name = "qcm6490", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .wcd_jack = true, -+}; -+ -+static const struct qcom_snd_soc_common qcs6490_priv_data = { -+ .driver_name = "qcs6490", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .wcd_jack = true, -+}; -+ -+static const struct qcom_snd_soc_common qcs8275_priv_data = { -+ .driver_name = "qcs8300", -+ .dapm_widgets = max98090_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(max98090_dapm_widgets), -+ .controls = max98090_controls, -+ .num_controls = ARRAY_SIZE(max98090_controls), -+ .codec_sysclk_set = true, -+ .codec_dai_fmt = SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_BC_FC, -+}; -+ -+static const struct qcom_snd_soc_common sc8280xp_priv_data = { -+ .driver_name = "sc8280xp", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .wcd_jack = true, -+}; -+ -+static const struct qcom_snd_soc_common sm8450_priv_data = { -+ .driver_name = "sm8450", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .wcd_jack = true, -+ /* I2S Connected to HDMI */ -+ .mi2s_mclk_enable = true, -+ .mi2s_bclk_enable = true, -+ .codec_dai_fmt = SND_SOC_DAIFMT_BC_FC | -+ SND_SOC_DAIFMT_NB_NF | -+ SND_SOC_DAIFMT_I2S, -+}; -+ -+static const struct qcom_snd_soc_common sm8475_priv_data = { -+ .driver_name = "sm8475", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .wcd_jack = true, -+}; -+ -+static const struct qcom_snd_soc_common sm8550_priv_data = { -+ .driver_name = "sm8550", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .wcd_jack = true, -+ /* I2S Connected to HDMI */ -+ .mi2s_mclk_enable = true, -+ .mi2s_bclk_enable = true, -+ .codec_dai_fmt = SND_SOC_DAIFMT_BC_FC | -+ SND_SOC_DAIFMT_NB_NF | -+ SND_SOC_DAIFMT_I2S, -+}; -+ -+static const struct qcom_snd_soc_common sm8650_priv_data = { -+ .driver_name = "sm8650", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .wcd_jack = true, -+ /* I2S Connected to HDMI */ -+ .mi2s_mclk_enable = true, -+ .mi2s_bclk_enable = true, -+ .codec_dai_fmt = SND_SOC_DAIFMT_BC_FC | -+ SND_SOC_DAIFMT_NB_NF | -+ SND_SOC_DAIFMT_I2S, -+}; -+ -+static const struct qcom_snd_soc_common sm8750_priv_data = { -+ .driver_name = "sm8750", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .wcd_jack = true, -+}; -+ - static const struct of_device_id snd_sc8280xp_dt_match[] = { -- {.compatible = "qcom,eliza-sndcard", "eliza"}, -- {.compatible = "qcom,kaanapali-sndcard", "kaanapali"}, -- {.compatible = "qcom,qcm6490-idp-sndcard", "qcm6490"}, -- {.compatible = "qcom,qcs615-sndcard", "qcs615"}, -- {.compatible = "qcom,qcs6490-rb3gen2-sndcard", "qcs6490"}, -- {.compatible = "qcom,qcs8275-sndcard", "qcs8300"}, -- {.compatible = "qcom,qcs9075-sndcard", "sa8775p"}, -- {.compatible = "qcom,qcs9100-sndcard", "sa8775p"}, -- {.compatible = "qcom,sc8280xp-sndcard", "sc8280xp"}, -- {.compatible = "qcom,sm8450-sndcard", "sm8450"}, -- {.compatible = "qcom,sm8550-sndcard", "sm8550"}, -- {.compatible = "qcom,sm8650-sndcard", "sm8650"}, -- {.compatible = "qcom,sm8750-sndcard", "sm8750"}, -+ { .compatible = "ayaneo,pocket-s2-sndcard", .data = &ayaneo_ps2_priv_data }, -+ { .compatible = "qcom,eliza-sndcard", .data = &eliza_priv_data }, -+ { .compatible = "qcom,hawi-sndcard", .data = &hawi_priv_data }, -+ { .compatible = "qcom,kaanapali-sndcard", .data = &kaanapali_priv_data }, -+ { .compatible = "qcom,maili-sndcard", .data = &hawi_priv_data }, -+ { .compatible = "qcom,qcm6490-idp-sndcard", .data = &qcm6490_priv_data }, -+ { .compatible = "qcom,qcs615-sndcard", .data = &qcs615_priv_data }, -+ { .compatible = "qcom,qcs6490-rb3gen2-sndcard", .data = &qcs6490_priv_data }, -+ { .compatible = "qcom,qcs8275-sndcard", .data = &qcs8275_priv_data }, -+ { .compatible = "qcom,qcs9075-sndcard", .data = &qcs9100_priv_data }, -+ { .compatible = "qcom,qcs9100-sndcard", .data = &qcs9100_priv_data }, -+ { .compatible = "qcom,sc8280xp-sndcard", .data = &sc8280xp_priv_data }, -+ { .compatible = "qcom,sm8450-sndcard", .data = &sm8450_priv_data }, -+ { .compatible = "qcom,sm8475-sndcard", .data = &sm8475_priv_data }, -+ { .compatible = "qcom,sm8550-sndcard", .data = &sm8550_priv_data }, -+ { .compatible = "qcom,sm8650-sndcard", .data = &sm8650_priv_data }, -+ { .compatible = "qcom,sm8750-sndcard", .data = &sm8750_priv_data }, - {} - }; - -diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c ---- a/sound/soc/qcom/sdm845.c -+++ b/sound/soc/qcom/sdm845.c -@@ -36,7 +36,6 @@ struct sdm845_snd_data { - bool jack_setup; - bool slim_port_setup; - bool stream_prepared[AFE_PORT_MAX]; -- struct snd_soc_card *card; - uint32_t pri_mi2s_clk_count; - uint32_t sec_mi2s_clk_count; - uint32_t quat_tdm_clk_count; -@@ -564,7 +563,6 @@ static int sdm845_snd_platform_probe(struct platform_device *pdev) - if (ret) - return ret; - -- data->card = card; - snd_soc_card_set_drvdata(card, data); - - sdm845_add_ops(card); -diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c ---- a/sound/soc/qcom/sm8250.c -+++ b/sound/soc/qcom/sm8250.c -@@ -112,6 +112,22 @@ static int sm8250_snd_startup(struct snd_pcm_substream *substream) - snd_soc_dai_set_fmt(cpu_dai, fmt); - snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); - break; -+ case LPI_MI2S_RX_0: -+ codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S; -+ snd_soc_dai_set_sysclk(cpu_dai, -+ Q6AFE_LPASS_CLK_ID_INT0_MI2S_IBIT, -+ MI2S_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); -+ snd_soc_dai_set_fmt(cpu_dai, fmt); -+ snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); -+ break; -+ case LPI_MI2S_TX_3: -+ codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S; -+ snd_soc_dai_set_sysclk(cpu_dai, -+ Q6AFE_LPASS_CLK_ID_INT3_MI2S_IBIT, -+ MI2S_BCLK_RATE, SNDRV_PCM_STREAM_CAPTURE); -+ snd_soc_dai_set_fmt(cpu_dai, fmt); -+ snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); -+ break; - default: - break; - } -@@ -194,6 +210,7 @@ static const struct of_device_id snd_sm8250_dt_match[] = { - { .compatible = "qcom,qrb2210-sndcard", .data = "qcm2290" }, - { .compatible = "qcom,qrb4210-rb2-sndcard", .data = "sm4250" }, - { .compatible = "qcom,qrb5165-rb5-sndcard", .data = "sm8250" }, -+ { .compatible = "qcom,sdm660-sndcard", .data = "sdm660" }, - { .compatible = "qcom,sm8250-sndcard", .data = "sm8250" }, - {} - }; -diff --git a/sound/soc/qcom/storm.c b/sound/soc/qcom/storm.c ---- a/sound/soc/qcom/storm.c -+++ b/sound/soc/qcom/storm.c -@@ -64,21 +64,21 @@ static struct snd_soc_dai_link storm_dai_link = { - SND_SOC_DAILINK_REG(hifi), - }; - --static int storm_parse_of(struct snd_soc_card *card) -+static int storm_parse_of(struct device *dev) - { -- struct snd_soc_dai_link *dai_link = card->dai_link; -- struct device_node *np = card->dev->of_node; -+ struct snd_soc_dai_link *dai_link = &storm_dai_link; -+ struct device_node *np = dev->of_node; - - dai_link->cpus->of_node = of_parse_phandle(np, "cpu", 0); - if (!dai_link->cpus->of_node) { -- dev_err(card->dev, "error getting cpu phandle\n"); -+ dev_err(dev, "error getting cpu phandle\n"); - return -EINVAL; - } - dai_link->platforms->of_node = dai_link->cpus->of_node; - - dai_link->codecs->of_node = of_parse_phandle(np, "codec", 0); - if (!dai_link->codecs->of_node) { -- dev_err(card->dev, "error getting codec phandle\n"); -+ dev_err(dev, "error getting codec phandle\n"); - return -EINVAL; - } - -@@ -106,7 +106,7 @@ static int storm_platform_probe(struct platform_device *pdev) - card->dai_link = &storm_dai_link; - card->num_links = 1; - -- ret = storm_parse_of(card); -+ ret = storm_parse_of(&pdev->dev); - if (ret) { - dev_err(&pdev->dev, "error resolving dai links: %d\n", ret); - return ret; -diff --git a/sound/soc/renesas/fsi.c b/sound/soc/renesas/fsi.c ---- a/sound/soc/renesas/fsi.c -+++ b/sound/soc/renesas/fsi.c -@@ -1979,8 +1979,8 @@ static const struct of_device_id fsi_of_match[] = { - MODULE_DEVICE_TABLE(of, fsi_of_match); - - static const struct platform_device_id fsi_id_table[] = { -- { "sh_fsi", (kernel_ulong_t)&fsi1_core }, -- {}, -+ /* an array with no valid entry prevents matching on driver name */ -+ { } - }; - MODULE_DEVICE_TABLE(platform, fsi_id_table); - -@@ -1992,7 +1992,7 @@ static int fsi_probe(struct platform_device *pdev) - const struct fsi_core *core; - struct fsi_priv *fsi; - struct resource *res; -- unsigned int irq; -+ int irq; - int ret; - - memset(&info, 0, sizeof(info)); -@@ -2007,12 +2007,15 @@ static int fsi_probe(struct platform_device *pdev) - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- irq = platform_get_irq(pdev, 0); -- if (!res || (int)irq <= 0) { -+ if (!res) { - dev_err(&pdev->dev, "Not enough FSI platform resources.\n"); - return -ENODEV; - } - -+ irq = platform_get_irq(pdev, 0); -+ if (irq < 0) -+ return irq; -+ - master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL); - if (!master) - return -ENOMEM; -diff --git a/sound/soc/renesas/rcar/adg.c b/sound/soc/renesas/rcar/adg.c ---- a/sound/soc/renesas/rcar/adg.c -+++ b/sound/soc/renesas/rcar/adg.c -@@ -565,7 +565,7 @@ static struct clk *rsnd_adg_create_null_clk(struct rsnd_priv *priv, - struct clk *clk; - - clk = clk_register_fixed_rate(dev, name, parent, 0, 0); -- if (IS_ERR_OR_NULL(clk)) { -+ if (IS_ERR(clk)) { - dev_err(dev, "create null clk error\n"); - return ERR_CAST(clk); - } -@@ -618,7 +618,7 @@ static int rsnd_adg_get_clkin(struct rsnd_priv *priv) - * No "adg" is not error - */ - clk = devm_clk_get(dev, "adg"); -- if (IS_ERR_OR_NULL(clk)) -+ if (IS_ERR(clk)) - clk = rsnd_adg_null_clk_get(priv); - adg->adg = clk; - -@@ -626,9 +626,9 @@ static int rsnd_adg_get_clkin(struct rsnd_priv *priv) - for (i = 0; i < clkin_size; i++) { - clk = devm_clk_get(dev, clkin_name[i]); - -- if (IS_ERR_OR_NULL(clk)) -+ if (IS_ERR(clk)) - clk = rsnd_adg_null_clk_get(priv); -- if (IS_ERR_OR_NULL(clk)) -+ if (IS_ERR(clk)) - goto err; - - adg->clkin[i] = clk; -diff --git a/sound/soc/renesas/siu_dai.c b/sound/soc/renesas/siu_dai.c ---- a/sound/soc/renesas/siu_dai.c -+++ b/sound/soc/renesas/siu_dai.c -@@ -715,7 +715,6 @@ static struct snd_soc_dai_driver siu_i2s_dai = { - - static int siu_probe(struct platform_device *pdev) - { -- const struct firmware *fw_entry; - struct resource *res, *region; - struct siu_info *info; - int ret; -@@ -726,6 +725,7 @@ static int siu_probe(struct platform_device *pdev) - siu_i2s_data = info; - info->dev = &pdev->dev; - -+ const struct firmware *fw_entry __free(firmware) = NULL; - ret = request_firmware(&fw_entry, "siu_spb.bin", &pdev->dev); - if (ret) - return ret; -@@ -736,8 +736,6 @@ static int siu_probe(struct platform_device *pdev) - */ - memcpy(&info->fw, fw_entry->data, fw_entry->size); - -- release_firmware(fw_entry); -- - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; -diff --git a/sound/soc/rockchip/rk3288_hdmi_analog.c b/sound/soc/rockchip/rk3288_hdmi_analog.c ---- a/sound/soc/rockchip/rk3288_hdmi_analog.c -+++ b/sound/soc/rockchip/rk3288_hdmi_analog.c -@@ -185,10 +185,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) - gpiod_set_consumer_name(machine->gpio_hp_en, "hp_en"); - - ret = snd_soc_of_parse_card_name(card, "rockchip,model"); -- if (ret) { -- dev_err(card->dev, "SoC parse card name failed %d\n", ret); -+ if (ret) - return ret; -- } - - rk_dailink.codecs[0].of_node = of_parse_phandle(np, - "rockchip,audio-codec", -@@ -207,10 +205,9 @@ static int snd_rk_mc_probe(struct platform_device *pdev) - } - - ret = snd_soc_get_dai_name(&args, &rk_dailink.codecs[0].dai_name); -- if (ret) { -- dev_err(&pdev->dev, "Unable to get codec_dai_name\n"); -- return ret; -- } -+ if (ret) -+ return dev_err_probe(&pdev->dev, ret, -+ "Unable to get codec_dai_name\n"); - - rk_dailink.cpus->of_node = of_parse_phandle(np, "rockchip,i2s-controller", - 0); -@@ -223,11 +220,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) - rk_dailink.platforms->of_node = rk_dailink.cpus->of_node; - - ret = snd_soc_of_parse_audio_routing(card, "rockchip,routing"); -- if (ret) { -- dev_err(&pdev->dev, -- "Unable to parse 'rockchip,routing' property\n"); -+ if (ret) - return ret; -- } - - snd_soc_card_set_drvdata(card, machine); - -diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c ---- a/sound/soc/rockchip/rockchip_i2s.c -+++ b/sound/soc/rockchip/rockchip_i2s.c -@@ -767,16 +767,14 @@ static int rockchip_i2s_probe(struct platform_device *pdev) - - /* try to prepare related clocks */ - i2s->hclk = devm_clk_get_enabled(&pdev->dev, "i2s_hclk"); -- if (IS_ERR(i2s->hclk)) { -- dev_err(&pdev->dev, "Can't retrieve i2s bus clock\n"); -- return PTR_ERR(i2s->hclk); -- } -+ if (IS_ERR(i2s->hclk)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->hclk), -+ "Can't retrieve i2s bus clock\n"); - - i2s->mclk = devm_clk_get(&pdev->dev, "i2s_clk"); -- if (IS_ERR(i2s->mclk)) { -- dev_err(&pdev->dev, "Can't retrieve i2s master clock\n"); -- return PTR_ERR(i2s->mclk); -- } -+ if (IS_ERR(i2s->mclk)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->mclk), -+ "Can't retrieve i2s master clock\n"); - - regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(regs)) -@@ -792,7 +790,11 @@ static int rockchip_i2s_probe(struct platform_device *pdev) - - i2s->bclk_ratio = 64; - i2s->pinctrl = devm_pinctrl_get(&pdev->dev); -- if (!IS_ERR(i2s->pinctrl)) { -+ if (IS_ERR(i2s->pinctrl)) { -+ if (PTR_ERR(i2s->pinctrl) == -EPROBE_DEFER) -+ return -EPROBE_DEFER; -+ dev_dbg(&pdev->dev, "failed to find i2s pinctrl\n"); -+ } else { - i2s->bclk_on = pinctrl_lookup_state(i2s->pinctrl, "bclk_on"); - if (!IS_ERR_OR_NULL(i2s->bclk_on)) { - i2s->bclk_off = pinctrl_lookup_state(i2s->pinctrl, "bclk_off"); -@@ -801,8 +803,6 @@ static int rockchip_i2s_probe(struct platform_device *pdev) - return -EINVAL; - } - } -- } else { -- dev_dbg(&pdev->dev, "failed to find i2s pinctrl\n"); - } - - i2s_pinctrl_select_bclk_off(i2s); -@@ -831,16 +831,12 @@ static int rockchip_i2s_probe(struct platform_device *pdev) - &rockchip_i2s_component, - dai, 1); - -- if (ret) { -- dev_err(&pdev->dev, "Could not register DAI\n"); -+ if (ret) - return ret; -- } - - ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); -- if (ret) { -- dev_err(&pdev->dev, "Could not register PCM\n"); -+ if (ret) - return ret; -- } - - return 0; - } -diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c ---- a/sound/soc/rockchip/rockchip_i2s_tdm.c -+++ b/sound/soc/rockchip/rockchip_i2s_tdm.c -@@ -1263,16 +1263,14 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) - i2s_tdm->tx_reset = devm_reset_control_get_optional_exclusive(&pdev->dev, - "tx-m"); - if (IS_ERR(i2s_tdm->tx_reset)) { -- ret = PTR_ERR(i2s_tdm->tx_reset); -- return dev_err_probe(i2s_tdm->dev, ret, -+ return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->tx_reset), - "Error in tx-m reset control\n"); - } - - i2s_tdm->rx_reset = devm_reset_control_get_optional_exclusive(&pdev->dev, - "rx-m"); - if (IS_ERR(i2s_tdm->rx_reset)) { -- ret = PTR_ERR(i2s_tdm->rx_reset); -- return dev_err_probe(i2s_tdm->dev, ret, -+ return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->rx_reset), - "Error in rx-m reset control\n"); - } - -@@ -1363,17 +1361,12 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) - ret = devm_snd_soc_register_component(&pdev->dev, - &rockchip_i2s_tdm_component, - i2s_tdm->dai, 1); -- -- if (ret) { -- dev_err(&pdev->dev, "Could not register DAI\n"); -+ if (ret) - goto err_suspend; -- } - - ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); -- if (ret) { -- dev_err(&pdev->dev, "Could not register PCM\n"); -+ if (ret) - goto err_suspend; -- } - - return 0; - -diff --git a/sound/soc/rockchip/rockchip_max98090.c b/sound/soc/rockchip/rockchip_max98090.c ---- a/sound/soc/rockchip/rockchip_max98090.c -+++ b/sound/soc/rockchip/rockchip_max98090.c -@@ -428,11 +428,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) - - /* Parse card name. */ - ret = snd_soc_of_parse_card_name(card, "rockchip,model"); -- if (ret) { -- dev_err(&pdev->dev, -- "Soc parse card name failed %d\n", ret); -+ if (ret) - return ret; -- } - - /* register the soc card */ - ret = devm_snd_soc_register_card(&pdev->dev, card); -diff --git a/sound/soc/rockchip/rockchip_pdm.c b/sound/soc/rockchip/rockchip_pdm.c ---- a/sound/soc/rockchip/rockchip_pdm.c -+++ b/sound/soc/rockchip/rockchip_pdm.c -@@ -630,10 +630,8 @@ static int rockchip_pdm_probe(struct platform_device *pdev) - &rockchip_pdm_component, - &rockchip_pdm_dai, 1); - -- if (ret) { -- dev_err(&pdev->dev, "could not register dai: %d\n", ret); -+ if (ret) - goto err_suspend; -- } - - rockchip_pdm_rxctrl(pdm, 0); - -@@ -642,10 +640,8 @@ static int rockchip_pdm_probe(struct platform_device *pdev) - goto err_suspend; - - ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); -- if (ret) { -- dev_err(&pdev->dev, "could not register pcm: %d\n", ret); -+ if (ret) - goto err_suspend; -- } - - return 0; - -diff --git a/sound/soc/rockchip/rockchip_rt5645.c b/sound/soc/rockchip/rockchip_rt5645.c ---- a/sound/soc/rockchip/rockchip_rt5645.c -+++ b/sound/soc/rockchip/rockchip_rt5645.c -@@ -191,11 +191,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) - rk_dailink.platforms->of_node = rk_dailink.cpus->of_node; - - ret = snd_soc_of_parse_card_name(card, "rockchip,model"); -- if (ret) { -- dev_err(&pdev->dev, -- "Soc parse card name failed %d\n", ret); -+ if (ret) - goto put_cpu_of_node; -- } - - ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) { -diff --git a/sound/soc/rockchip/rockchip_sai.c b/sound/soc/rockchip/rockchip_sai.c ---- a/sound/soc/rockchip/rockchip_sai.c -+++ b/sound/soc/rockchip/rockchip_sai.c -@@ -1428,12 +1428,13 @@ static int rockchip_sai_probe(struct platform_device *pdev) - "Failed to initialize regmap\n"); - - irq = platform_get_irq_optional(pdev, 0); -+ if (irq == -EPROBE_DEFER) -+ return irq; - if (irq > 0) { - ret = devm_request_irq(&pdev->dev, irq, rockchip_sai_isr, - IRQF_SHARED, node->name, sai); - if (ret) -- return dev_err_probe(&pdev->dev, ret, -- "Failed to request irq %d\n", irq); -+ return ret; - } else { - dev_dbg(&pdev->dev, "Asked for an IRQ but got %d\n", irq); - } -@@ -1456,7 +1457,7 @@ static int rockchip_sai_probe(struct platform_device *pdev) - - ret = rockchip_sai_parse_paths(sai, node); - if (ret) -- return dev_err_probe(&pdev->dev, ret, "Failed to parse paths\n"); -+ return ret; - - /* - * From here on, all register accesses need to be wrapped in -@@ -1471,18 +1472,14 @@ static int rockchip_sai_probe(struct platform_device *pdev) - return dev_err_probe(&pdev->dev, ret, "Failed to resume device\n"); - - ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); -- if (ret) { -- dev_err(&pdev->dev, "Failed to register PCM: %d\n", ret); -+ if (ret) - goto err_runtime_suspend; -- } - - ret = devm_snd_soc_register_component(&pdev->dev, - &rockchip_sai_component, - dai, 1); -- if (ret) { -- dev_err(&pdev->dev, "Failed to register component: %d\n", ret); -+ if (ret) - goto err_runtime_suspend; -- } - - pm_runtime_use_autosuspend(&pdev->dev); - pm_runtime_put(&pdev->dev); -diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c ---- a/sound/soc/rockchip/rockchip_spdif.c -+++ b/sound/soc/rockchip/rockchip_spdif.c -@@ -396,13 +396,13 @@ static int rk_spdif_probe(struct platform_device *pdev) - - ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); - if (ret) -- return dev_err_probe(&pdev->dev, ret, "Could not register PCM\n"); -+ return ret; - - ret = devm_snd_soc_register_component(&pdev->dev, - &rk_spdif_component, - &rk_spdif_dai, 1); - if (ret) -- return dev_err_probe(&pdev->dev, ret, "Could not register DAI\n"); -+ return ret; - - return 0; - } -diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c ---- a/sound/soc/samsung/i2s.c -+++ b/sound/soc/samsung/i2s.c -@@ -8,6 +8,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -512,7 +513,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, - u32 mod, mask, val = 0; - int ret = 0; - -- pm_runtime_get_sync(dai->dev); -+ guard(pm_runtime_active)(dai->dev); - - scoped_guard(spinlock_irqsave, &priv->lock) - mod = readl(priv->addr + I2SMOD); -@@ -537,8 +538,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, - && (mod & cdcon_mask))))) { - dev_err(&i2s->pdev->dev, - "%s:%d Other DAI busy\n", __func__, __LINE__); -- ret = -EAGAIN; -- goto err; -+ return -EAGAIN; - } - - if (dir == SND_SOC_CLOCK_IN) -@@ -566,7 +566,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, - } else { - priv->rclk_srcrate = - clk_get_rate(priv->op_clk); -- goto done; -+ return 0; - } - } - -@@ -580,14 +580,14 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, - if (WARN_ON(IS_ERR(priv->op_clk))) { - ret = PTR_ERR(priv->op_clk); - priv->op_clk = NULL; -- goto err; -+ return ret; - } - - ret = clk_prepare_enable(priv->op_clk); - if (ret) { - clk_put(priv->op_clk); - priv->op_clk = NULL; -- goto err; -+ return ret; - } - priv->rclk_srcrate = clk_get_rate(priv->op_clk); - -@@ -595,11 +595,10 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, - || (clk_id && !(mod & rsrc_mask))) { - dev_err(&i2s->pdev->dev, - "%s:%d Other DAI busy\n", __func__, __LINE__); -- ret = -EAGAIN; -- goto err; -+ return -EAGAIN; - } else { - /* Call can't be on the active DAI */ -- goto done; -+ return 0; - } - - if (clk_id == 1) -@@ -607,8 +606,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, - break; - default: - dev_err(&i2s->pdev->dev, "We don't serve that!\n"); -- ret = -EINVAL; -- goto err; -+ return -EINVAL; - } - - scoped_guard(spinlock_irqsave, &priv->lock) { -@@ -616,13 +614,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, - mod = (mod & ~mask) | val; - writel(mod, priv->addr + I2SMOD); - } --done: -- pm_runtime_put(dai->dev); - - return 0; --err: -- pm_runtime_put(dai->dev); -- return ret; - } - - static int i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) -@@ -1123,15 +1116,31 @@ static const struct snd_soc_dapm_widget samsung_i2s_widgets[] = { - - static const struct snd_soc_dapm_route samsung_i2s_dapm_routes[] = { - { "Playback Mixer", NULL, "Primary Playback" }, -- { "Playback Mixer", NULL, "Secondary Playback" }, -- - { "Mixer DAI TX", NULL, "Playback Mixer" }, - { "Primary Capture", NULL, "Mixer DAI RX" }, - }; - -+static const struct snd_soc_dapm_route samsung_i2s_dapm_routes_sec_play[] = { -+ { "Playback Mixer", NULL, "Secondary Playback" }, -+}; -+ -+static int samsung_i2s_component_probe(struct snd_soc_component *component) -+{ -+ struct samsung_i2s_priv *priv = snd_soc_component_get_drvdata(component); -+ -+ if (priv->quirks & QUIRK_SEC_DAI) -+ snd_soc_dapm_add_routes(snd_soc_component_to_dapm(component), -+ samsung_i2s_dapm_routes_sec_play, -+ ARRAY_SIZE(samsung_i2s_dapm_routes_sec_play)); -+ -+ return 0; -+} -+ - static const struct snd_soc_component_driver samsung_i2s_component = { - .name = "samsung-i2s", - -+ .probe = samsung_i2s_component_probe, -+ - .dapm_widgets = samsung_i2s_widgets, - .num_dapm_widgets = ARRAY_SIZE(samsung_i2s_widgets), - -@@ -1448,10 +1457,10 @@ static int samsung_i2s_probe(struct platform_device *pdev) - regs_base = res->start; - - priv->clk = devm_clk_get(&pdev->dev, "iis"); -- if (IS_ERR(priv->clk)) { -- dev_err(&pdev->dev, "Failed to get iis clock\n"); -- return PTR_ERR(priv->clk); -- } -+ if (IS_ERR(priv->clk)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk), -+ "Failed to get iis clock\n"); -+ - - ret = clk_prepare_enable(priv->clk); - if (ret != 0) { -@@ -1657,8 +1666,7 @@ static const struct samsung_i2s_dai_data i2sv6_dai_type __maybe_unused = { - }; - - static const struct samsung_i2s_dai_data i2sv7_dai_type __maybe_unused = { -- .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | -- QUIRK_SUPPORTS_TDM, -+ .quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR | QUIRK_SUPPORTS_TDM, - .pcm_rates = SNDRV_PCM_RATE_8000_192000, - .i2s_variant_regs = &i2sv7_regs, - }; -@@ -1682,7 +1690,7 @@ static const struct platform_device_id samsung_i2s_driver_ids[] = { - .name = "samsung-i2s", - .driver_data = (kernel_ulong_t)&i2sv3_dai_type, - }, -- {}, -+ { } - }; - MODULE_DEVICE_TABLE(platform, samsung_i2s_driver_ids); - -diff --git a/sound/soc/samsung/smdk_spdif.c b/sound/soc/samsung/smdk_spdif.c ---- a/sound/soc/samsung/smdk_spdif.c -+++ b/sound/soc/samsung/smdk_spdif.c -@@ -130,12 +130,8 @@ static int smdk_hw_params(struct snd_pcm_substream *substream, - return ret; - - /* Set S/PDIF uses internal source clock */ -- ret = snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK, -- rclk_rate, SND_SOC_CLOCK_IN); -- if (ret < 0) -- return ret; -- -- return ret; -+ return snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK, -+ rclk_rate, SND_SOC_CLOCK_IN); - } - - static const struct snd_soc_ops smdk_spdif_ops = { -diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c ---- a/sound/soc/samsung/spdif.c -+++ b/sound/soc/samsung/spdif.c -@@ -380,8 +380,8 @@ static int spdif_probe(struct platform_device *pdev) - - spdif->pclk = devm_clk_get(&pdev->dev, "spdif"); - if (IS_ERR(spdif->pclk)) { -- dev_err(&pdev->dev, "failed to get peri-clock\n"); -- ret = -ENOENT; -+ ret = dev_err_probe(&pdev->dev, PTR_ERR(spdif->pclk), -+ "failed to get peri-clock\n"); - goto err0; - } - ret = clk_prepare_enable(spdif->pclk); -@@ -390,8 +390,8 @@ static int spdif_probe(struct platform_device *pdev) - - spdif->sclk = devm_clk_get(&pdev->dev, "sclk_spdif"); - if (IS_ERR(spdif->sclk)) { -- dev_err(&pdev->dev, "failed to get internal source clock\n"); -- ret = -ENOENT; -+ ret = dev_err_probe(&pdev->dev, PTR_ERR(spdif->sclk), -+ "failed to get internal source clock\n"); - goto err1; - } - ret = clk_prepare_enable(spdif->sclk); -diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c ---- a/sound/soc/sdca/sdca_asoc.c -+++ b/sound/soc/sdca/sdca_asoc.c -@@ -364,7 +364,6 @@ static int entity_parse_ot(struct device *dev, - - /** - * sdca_asoc_pde_poll_actual_ps - Verify PDE power state reached target state -- * @dev: Pointer to the device for error logging. - * @regmap: Register map for reading ACTUAL_PS register. - * @function_id: SDCA function identifier. - * @entity_id: SDCA entity identifier for the power domain. -@@ -389,11 +388,11 @@ static int entity_parse_ot(struct device *dev, - * polling times out before reaching the target state, or a negative error code if - * a register read fails. - */ --int sdca_asoc_pde_poll_actual_ps(struct device *dev, struct regmap *regmap, -+int sdca_asoc_pde_poll_actual_ps(struct regmap *regmap, - int function_id, int entity_id, -- int from_ps, int to_ps, -- const struct sdca_pde_delay *pde_delays, -- int num_delays) -+ int from_ps, int to_ps, -+ const struct sdca_pde_delay *pde_delays, -+ int num_delays) - { - static const int polls = 100; - static const int default_poll_us = 1000; -@@ -451,14 +450,14 @@ static int entity_pde_event(struct snd_soc_dapm_widget *widget, - return 0; - } - -- ret = sdca_asoc_pde_poll_actual_ps(component->dev, component->regmap, -+ ret = sdca_asoc_pde_poll_actual_ps(component->regmap, - SDW_SDCA_CTL_FUNC(widget->reg), - SDW_SDCA_CTL_ENT(widget->reg), - from, to, - entity->pde.max_delay, - entity->pde.num_max_delay); - if (ret) -- dev_err(component->dev, "%s: PDE transition %x -> %x failed, err=%d\n", -+ dev_err(component->dev, "%s: pde transition %x -> %x failed: %d\n", - entity->label, from, to, ret); - - return ret; -@@ -1231,7 +1230,7 @@ static u64 width_find_mask(unsigned int bits) - } - } - --static int populate_rate_format(struct device *dev, -+int sdca_asoc_populate_rate_format(struct device *dev, - struct sdca_function_data *function, - struct sdca_entity *entity, - struct snd_soc_pcm_stream *stream) -@@ -1292,6 +1291,7 @@ static int populate_rate_format(struct device *dev, - - return 0; - } -+EXPORT_SYMBOL_NS(sdca_asoc_populate_rate_format, "SND_SOC_SDCA"); - - /** - * sdca_asoc_populate_dais - fill in an array of DAI drivers for a Function -@@ -1344,7 +1344,7 @@ int sdca_asoc_populate_dais(struct device *dev, struct sdca_function_data *funct - stream->channels_min = 1; - stream->channels_max = SDCA_MAX_CHANNEL_COUNT; - -- ret = populate_rate_format(dev, function, entity, stream); -+ ret = sdca_asoc_populate_rate_format(dev, function, entity, stream); - if (ret) - return ret; - -diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c ---- a/sound/soc/sdca/sdca_class_function.c -+++ b/sound/soc/sdca/sdca_class_function.c -@@ -329,7 +329,7 @@ static int class_function_probe(struct auxiliary_device *auxdev, - drv->core = core; - drv->function = &sdev->function; - -- ret = sdca_parse_function(dev, core->sdw, drv->function); -+ ret = sdca_parse_function(dev, drv->function); - if (ret) - return ret; - -diff --git a/sound/soc/sdca/sdca_fdl.c b/sound/soc/sdca/sdca_fdl.c ---- a/sound/soc/sdca/sdca_fdl.c -+++ b/sound/soc/sdca/sdca_fdl.c -@@ -195,7 +195,6 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, - { - struct device *dev = interrupt->dev; - struct sdca_fdl_data *fdl_data = &interrupt->function->fdl_data; -- const struct firmware *firmware = NULL; - struct acpi_sw_file *swf = NULL, *tmp; - struct sdca_fdl_file *fdl_file; - char *disk_filename; -@@ -230,6 +229,7 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, - - dev_dbg(dev, "FDL disk filename: %s\n", disk_filename); - -+ const struct firmware *firmware __free(firmware) = NULL; - ret = firmware_request_nowarn(&firmware, disk_filename, dev); - kfree(disk_filename); - if (ret) { -@@ -258,8 +258,7 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, - - if (!swf) { - dev_err(dev, "failed to locate SWF\n"); -- ret = -ENOENT; -- goto error; -+ return -ENOENT; - } - - dev_info(dev, "loading SWF: %x-%x-%x\n", -@@ -271,9 +270,6 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, - SDCA_CTL_XU_FDL_MESSAGEOFFSET, fdl_file->fdl_offset, - SDCA_CTL_XU_FDL_MESSAGELENGTH, swf->data, - swf->file_length - offsetof(struct acpi_sw_file, data)); -- --error: -- release_firmware(firmware); - return ret; - } - -diff --git a/sound/soc/sdca/sdca_function_device.c b/sound/soc/sdca/sdca_function_device.c ---- a/sound/soc/sdca/sdca_function_device.c -+++ b/sound/soc/sdca/sdca_function_device.c -@@ -32,7 +32,8 @@ static void sdca_dev_release(struct device *dev) - - /* alloc, init and add link devices */ - static struct sdca_dev *sdca_dev_register(struct device *parent, -- struct sdca_function_desc *function_desc) -+ struct sdca_function_desc *function_desc, -+ struct acpi_table_swft *swft) - { - struct sdca_dev *sdev; - struct auxiliary_device *auxdev; -@@ -50,6 +51,7 @@ static struct sdca_dev *sdca_dev_register(struct device *parent, - auxdev->dev.release = sdca_dev_release; - - sdev->function.desc = function_desc; -+ sdev->function.fdl_data.swft = swft; - - rc = ida_alloc(&sdca_function_ida, GFP_KERNEL); - if (rc < 0) { -@@ -99,7 +101,8 @@ int sdca_dev_register_functions(struct sdw_slave *slave) - struct sdca_dev *func_dev; - - func_dev = sdca_dev_register(&slave->dev, -- &sdca_data->function[i]); -+ &sdca_data->function[i], -+ sdca_data->swft); - if (IS_ERR(func_dev)) { - ret = PTR_ERR(func_dev); - /* -diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c ---- a/sound/soc/sdca/sdca_functions.c -+++ b/sound/soc/sdca/sdca_functions.c -@@ -7,6 +7,7 @@ - */ - - #include -+#include - #include - #include - #include -@@ -18,7 +19,6 @@ - #include - #include - #include --#include - - /* - * Should be long enough to encompass all the MIPI DisCo properties. -@@ -1365,90 +1365,76 @@ static int find_sdca_entity_ge(struct device *dev, - return -EINVAL; - } - --static int --find_sdca_entity_hide(struct device *dev, struct sdw_slave *sdw, -- struct fwnode_handle *function_node, -- struct fwnode_handle *entity_node, struct sdca_entity *entity) -+static int find_sdca_entity_hide(struct device *dev, -+ struct fwnode_handle *entity_node, -+ struct sdca_entity *entity) - { - struct sdca_entity_hide *hide = &entity->hide; -- unsigned int delay, *af_list = hide->af_number_list; -- int nval, ret; -- unsigned char *report_desc = NULL; -+ int num_reports, ret; -+ unsigned int delay; - - ret = fwnode_property_read_u32(entity_node, -- "mipi-sdca-RxUMP-ownership-transition-max-delay", &delay); -+ "mipi-sdca-RxUMP-ownership-transition-max-delay", -+ &delay); - if (!ret) - hide->max_delay = delay; - -- nval = fwnode_property_count_u32(entity_node, "mipi-sdca-HIDTx-supported-report-ids"); -- if (nval > 0) { -- hide->num_hidtx_ids = nval; -+ num_reports = fwnode_property_count_u32(entity_node, -+ "mipi-sdca-HIDTx-supported-report-ids"); -+ if (num_reports < 0 && num_reports != -EINVAL) { -+ dev_err(dev, "%pfwP: failed to read hid tx ids: %d\n", -+ entity_node, num_reports); -+ return num_reports; -+ } else if (num_reports > 0) { -+ hide->num_hidtx_ids = num_reports; - hide->hidtx_ids = devm_kcalloc(dev, hide->num_hidtx_ids, - sizeof(*hide->hidtx_ids), GFP_KERNEL); - if (!hide->hidtx_ids) - return -ENOMEM; - -- ret = fwnode_property_read_u32_array(entity_node, -- "mipi-sdca-HIDTx-supported-report-ids", -- hide->hidtx_ids, -- hide->num_hidtx_ids); -- if (ret < 0) -- return ret; -+ fwnode_property_read_u32_array(entity_node, -+ "mipi-sdca-HIDTx-supported-report-ids", -+ hide->hidtx_ids, hide->num_hidtx_ids); - } - -- nval = fwnode_property_count_u32(entity_node, "mipi-sdca-HIDRx-supported-report-ids"); -- if (nval > 0) { -- hide->num_hidrx_ids = nval; -+ num_reports = fwnode_property_count_u32(entity_node, -+ "mipi-sdca-HIDRx-supported-report-ids"); -+ if (num_reports < 0 && num_reports != -EINVAL) { -+ dev_err(dev, "%pfwP: failed to read hid rx ids: %d\n", -+ entity_node, num_reports); -+ return num_reports; -+ } else if (num_reports > 0) { -+ hide->num_hidrx_ids = num_reports; - hide->hidrx_ids = devm_kcalloc(dev, hide->num_hidrx_ids, - sizeof(*hide->hidrx_ids), GFP_KERNEL); - if (!hide->hidrx_ids) - return -ENOMEM; - -- ret = fwnode_property_read_u32_array(entity_node, -- "mipi-sdca-HIDRx-supported-report-ids", -- hide->hidrx_ids, -- hide->num_hidrx_ids); -- if (ret < 0) -- return ret; -+ fwnode_property_read_u32_array(entity_node, -+ "mipi-sdca-HIDRx-supported-report-ids", -+ hide->hidrx_ids, hide->num_hidrx_ids); - } - -- nval = fwnode_property_count_u32(entity_node, "mipi-sdca-hide-related-audio-function-list"); -- if (nval <= 0) { -+ /* -+ * FIXME: This should probably link to the actual sdca_function_data pointer, -+ * but updating to do so should probably wait until we have a user. -+ */ -+ num_reports = fwnode_property_count_u32(entity_node, -+ "mipi-sdca-hide-related-audio-function-list"); -+ if (num_reports <= 0) { - dev_err(dev, "%pfwP: audio function numbers list missing: %d\n", -- entity_node, nval); -+ entity_node, num_reports); - return -EINVAL; -- } else if (nval > SDCA_MAX_FUNCTION_COUNT) { -- dev_err(dev, "%pfwP: maximum number of audio function exceeded\n", entity_node); -+ } else if (num_reports > ARRAY_SIZE(hide->af_number_list)) { -+ dev_err(dev, "%pfwP: maximum number of audio function exceeded\n", -+ entity_node); - return -EINVAL; - } - -- hide->hide_reside_function_num = nval; -+ hide->hide_reside_function_num = num_reports; - fwnode_property_read_u32_array(entity_node, -- "mipi-sdca-hide-related-audio-function-list", af_list, nval); -- -- nval = fwnode_property_count_u8(function_node, "mipi-sdca-hid-descriptor"); -- if (nval) -- fwnode_property_read_u8_array(function_node, "mipi-sdca-hid-descriptor", -- (u8 *)&hide->hid_desc, nval); -- -- if (hide->hid_desc.bNumDescriptors) { -- nval = fwnode_property_count_u8(function_node, "mipi-sdca-report-descriptor"); -- if (nval) { -- report_desc = devm_kzalloc(dev, nval, GFP_KERNEL); -- if (!report_desc) -- return -ENOMEM; -- hide->hid_report_desc = report_desc; -- fwnode_property_read_u8_array(function_node, "mipi-sdca-report-descriptor", -- report_desc, nval); -- -- /* add HID device */ -- ret = sdca_add_hid_device(dev, sdw, entity); -- if (ret) { -- dev_err(dev, "%pfwP: failed to add HID device: %d\n", entity_node, ret); -- return ret; -- } -- } -- } -+ "mipi-sdca-hide-related-audio-function-list", -+ hide->af_number_list, num_reports); - - return 0; - } -@@ -1475,8 +1461,7 @@ static int find_sdca_entity_xu(struct device *dev, - return 0; - } - --static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw, -- struct sdca_function_data *function, -+static int find_sdca_entity(struct device *dev, struct sdca_function_data *function, - struct fwnode_handle *function_node, - struct fwnode_handle *entity_node, - struct sdca_entity *entity) -@@ -1528,8 +1513,7 @@ static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw, - ret = find_sdca_entity_ge(dev, entity_node, entity); - break; - case SDCA_ENTITY_TYPE_HIDE: -- ret = find_sdca_entity_hide(dev, sdw, function_node, -- entity_node, entity); -+ ret = find_sdca_entity_hide(dev, entity_node, entity); - break; - default: - break; -@@ -1544,8 +1528,7 @@ static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw, - return 0; - } - --static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw, -- struct fwnode_handle *function_node, -+static int find_sdca_entities(struct device *dev, struct fwnode_handle *function_node, - struct sdca_function_data *function) - { - struct sdca_entity *entities; -@@ -1596,7 +1579,7 @@ static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw, - return -EINVAL; - } - -- ret = find_sdca_entity(dev, sdw, function, function_node, -+ ret = find_sdca_entity(dev, function, function_node, - entity_node, &entities[i]); - fwnode_handle_put(entity_node); - if (ret) -@@ -1619,7 +1602,7 @@ static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw, - return 0; - } - --static struct sdca_entity *find_sdca_entity_by_label(struct sdca_function_data *function, -+struct sdca_entity *sdca_find_entity_by_label(struct sdca_function_data *function, - const char *entity_label) - { - struct sdca_entity *entity = NULL; -@@ -1648,6 +1631,7 @@ static struct sdca_entity *find_sdca_entity_by_label(struct sdca_function_data * - - return NULL; - } -+EXPORT_SYMBOL_NS(sdca_find_entity_by_label, "SND_SOC_SDCA"); - - static struct sdca_entity *find_sdca_entity_by_id(struct sdca_function_data *function, - const int id) -@@ -1688,7 +1672,7 @@ static int find_sdca_entity_connection_iot(struct device *dev, - return ret; - } - -- clock_entity = find_sdca_entity_by_label(function, clock_label); -+ clock_entity = sdca_find_entity_by_label(function, clock_label); - if (!clock_entity) { - dev_err(dev, "%s: failed to find clock with label %s\n", - entity->label, clock_label); -@@ -1873,7 +1857,7 @@ static int find_sdca_entity_connection(struct device *dev, - return ret; - } - -- connected_entity = find_sdca_entity_by_label(function, connected_label); -+ connected_entity = sdca_find_entity_by_label(function, connected_label); - if (!connected_entity) { - dev_err(dev, "%s: failed to find entity with label %s\n", - entity->label, connected_label); -@@ -2088,8 +2072,7 @@ static int find_sdca_clusters(struct device *dev, - return 0; - } - --static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, -- struct fwnode_handle *function_node, -+static int find_sdca_filesets(struct device *dev, struct fwnode_handle *function_node, - struct sdca_function_data *function) - { - static const int mult_fileset = 3; -@@ -2171,23 +2154,65 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, - set->files = files; - } - -- function->fdl_data.swft = sdw->sdca_data.swft; - function->fdl_data.num_sets = num_sets; - function->fdl_data.sets = sets; - - return 0; - } - -+static int find_sdca_hid(struct device *dev, struct fwnode_handle *function_node, -+ struct sdca_function_data *function) -+{ -+ int num_desc; -+ -+ num_desc = fwnode_property_count_u8(function_node, "mipi-sdca-hid-descriptor"); -+ if (!num_desc) { -+ return 0; -+ } else if (num_desc < 0) { -+ dev_err(dev, "%pfwP: failed to read hid descriptor: %d\n", -+ function_node, num_desc); -+ return num_desc; -+ } else if (num_desc > sizeof(function->hid.desc)) { -+ dev_err(dev, "%pfwP: hid descriptor too large: %d\n", -+ function_node, num_desc); -+ return -EINVAL; -+ } -+ -+ fwnode_property_read_u8_array(function_node, "mipi-sdca-hid-descriptor", -+ (u8 *)&function->hid.desc, num_desc); -+ -+ if (!function->hid.desc.bNumDescriptors) -+ return 0; -+ -+ num_desc = fwnode_property_count_u8(function_node, "mipi-sdca-report-descriptor"); -+ if (num_desc <= 0) { -+ dev_err(dev, "%pfwP: failed to read report descriptor: %d\n", -+ function_node, num_desc); -+ -+ if (!num_desc) -+ return -EINVAL; -+ -+ return num_desc; -+ } -+ -+ function->hid.report_desc = devm_kzalloc(dev, num_desc, GFP_KERNEL); -+ if (!function->hid.report_desc) -+ return -ENOMEM; -+ -+ fwnode_property_read_u8_array(function_node, "mipi-sdca-report-descriptor", -+ function->hid.report_desc, num_desc); -+ -+ return 0; -+} -+ - /** - * sdca_parse_function - parse ACPI DisCo for a Function - * @dev: Pointer to device against which function data will be allocated. -- * @sdw: SoundWire slave device to be processed. - * @function: Pointer to the Function information, to be populated. - * - * Return: Returns 0 for success. - */ --int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, -- struct sdca_function_data *function) -+int sdca_parse_function(struct device *dev, struct sdca_function_data *function) - { - struct fwnode_handle *node = function->desc->node; - u32 tmp; -@@ -2213,7 +2238,7 @@ int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, - if (ret) - return ret; - -- ret = find_sdca_entities(dev, sdw, node, function); -+ ret = find_sdca_entities(dev, node, function); - if (ret) - return ret; - -@@ -2225,10 +2250,20 @@ int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, - if (ret < 0) - return ret; - -- ret = find_sdca_filesets(dev, sdw, node, function); -+ ret = find_sdca_filesets(dev, node, function); - if (ret) - return ret; - -+ switch (function->desc->type) { -+ case SDCA_FUNCTION_TYPE_HID: -+ ret = find_sdca_hid(dev, node, function); -+ if (ret) -+ return ret; -+ break; -+ default: -+ break; -+ } -+ - return 0; - } - EXPORT_SYMBOL_NS(sdca_parse_function, "SND_SOC_SDCA"); -diff --git a/sound/soc/sdca/sdca_hid.c b/sound/soc/sdca/sdca_hid.c ---- a/sound/soc/sdca/sdca_hid.c -+++ b/sound/soc/sdca/sdca_hid.c -@@ -14,6 +14,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -23,18 +24,18 @@ - - static int sdwhid_parse(struct hid_device *hid) - { -- struct sdca_entity *entity = hid->driver_data; -+ struct sdca_function_data *function = hid->driver_data; - unsigned int rsize; - int ret; - -- rsize = le16_to_cpu(entity->hide.hid_desc.rpt_desc.wDescriptorLength); -+ rsize = le16_to_cpu(function->hid.desc.rpt_desc.wDescriptorLength); - - if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { - dev_err(&hid->dev, "invalid size of report descriptor (%u)\n", rsize); - return -EINVAL; - } - -- ret = hid_parse_report(hid, entity->hide.hid_report_desc, rsize); -+ ret = hid_parse_report(hid, function->hid.report_desc, rsize); - - if (!ret) - return 0; -@@ -85,10 +86,16 @@ static const struct hid_ll_driver sdw_hid_driver = { - .raw_request = sdwhid_raw_request, - }; - --int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, -- struct sdca_entity *entity) -+/** -+ * sdca_add_hid_device - create a new SDCA HID device -+ * @interrupt: Pointer to the SDCA interrupt information structure. -+ * -+ * Return: Zero on success, and a negative error code on failure. -+ */ -+int sdca_add_hid_device(struct sdca_interrupt *interrupt) - { -- struct sdw_bus *bus = sdw->bus; -+ struct device *dev = interrupt->dev; -+ struct sdca_function_data *function = interrupt->function; - struct hid_device *hid; - int ret; - -@@ -100,16 +107,13 @@ int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, - - hid->dev.parent = dev; - hid->bus = BUS_SDW; -- hid->version = le16_to_cpu(entity->hide.hid_desc.bcdHID); -+ hid->version = le16_to_cpu(function->hid.desc.bcdHID); - -- snprintf(hid->name, sizeof(hid->name), -- "HID sdw:%01x:%01x:%04x:%04x:%02x", -- bus->controller_id, bus->link_id, sdw->id.mfg_id, -- sdw->id.part_id, sdw->id.class_id); -+ strscpy(hid->phys, dev_name(dev)); -+ snprintf(hid->name, sizeof(hid->name), "SDCA %s:%02x", -+ function->desc->name, function->desc->adr); - -- snprintf(hid->phys, sizeof(hid->phys), "%s", dev->bus->name); -- -- hid->driver_data = entity; -+ hid->driver_data = function; - - ret = hid_add_device(hid); - if (ret && ret != -ENODEV) { -@@ -118,12 +122,24 @@ int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, - return ret; - } - -- entity->hide.hid = hid; -+ interrupt->priv = hid; - - return 0; - } - EXPORT_SYMBOL_NS(sdca_add_hid_device, "SND_SOC_SDCA"); - -+/** -+ * sdca_destroy_hid_device - destroy the HID device -+ * @interrupt: Pointer to the SDCA interrupt information structure. -+ */ -+void sdca_destroy_hid_device(struct sdca_interrupt *interrupt) -+{ -+ struct hid_device *hid = interrupt->priv; -+ -+ hid_destroy_device(hid); -+} -+EXPORT_SYMBOL_NS(sdca_destroy_hid_device, "SND_SOC_SDCA"); -+ - /** - * sdca_hid_process_report - read a HID event from the device and report - * @interrupt: Pointer to the SDCA interrupt information structure. -@@ -133,7 +149,7 @@ EXPORT_SYMBOL_NS(sdca_add_hid_device, "SND_SOC_SDCA"); - int sdca_hid_process_report(struct sdca_interrupt *interrupt) - { - struct device *dev = interrupt->dev; -- struct hid_device *hid = interrupt->entity->hide.hid; -+ struct hid_device *hid = interrupt->priv; - void *val __free(kfree) = NULL; - int len, ret; - -diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c ---- a/sound/soc/sdca/sdca_interrupts.c -+++ b/sound/soc/sdca/sdca_interrupts.c -@@ -487,6 +487,12 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *regmap, - } - break; - case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER): -+ interrupt->free_priv = sdca_destroy_hid_device; -+ -+ ret = sdca_add_hid_device(interrupt); -+ if (ret) -+ return ret; -+ - interrupt->handler = hid_handler; - break; - default: -diff --git a/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c b/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c ---- a/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c -+++ b/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c -@@ -99,11 +99,9 @@ static const struct snd_soc_dai_link bridge_dai_template = { - SND_SOC_DAILINK_REG(asoc_sdw_bridge_dai), - }; - --int asoc_sdw_bridge_cs35l56_count_sidecar(struct snd_soc_card *card, -+int asoc_sdw_bridge_cs35l56_count_sidecar(struct asoc_sdw_mc_private *ctx, - int *num_dais, int *num_devs) - { -- struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); -- - if (ctx->mc_quirk & SOC_SDW_SIDECAR_AMPS) { - (*num_dais)++; - (*num_devs) += ARRAY_SIZE(bridge_cs35l56_name_prefixes); -diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c ---- a/sound/soc/sdw_utils/soc_sdw_utils.c -+++ b/sound/soc/sdw_utils/soc_sdw_utils.c -@@ -834,6 +834,35 @@ struct asoc_sdw_codec_info codec_info_list[] = { - }, - .dai_num = 2, - }, -+ { -+ .vendor_id = 0x01fa, -+ .part_id = 0x3562, -+ .name_prefix = "AMP", -+ .is_amp = true, -+ .dais = { -+ { -+ .direction = {true, false}, -+ .dai_name = "cs35l56-sdw1", -+ .component_name = "cs35l56", -+ .dai_type = SOC_SDW_DAI_TYPE_AMP, -+ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, -+ .init = asoc_sdw_cs_amp_init, -+ .rtd_init = asoc_sdw_cs_spk_rtd_init, -+ .controls = generic_spk_controls, -+ .num_controls = ARRAY_SIZE(generic_spk_controls), -+ .widgets = generic_spk_widgets, -+ .num_widgets = ARRAY_SIZE(generic_spk_widgets), -+ }, -+ { -+ .direction = {false, true}, -+ .dai_name = "cs35l56-sdw1c", -+ .dai_type = SOC_SDW_DAI_TYPE_AMP, -+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, -+ .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, -+ }, -+ }, -+ .dai_num = 2, -+ }, - { - .vendor_id = 0x01fa, - .part_id = 0x3563, -@@ -1789,9 +1818,7 @@ static int is_sdca_aux_dev_present(struct device *dev, - int adr_index) - { - struct sdw_slave *slave; -- struct device *sdw_dev; - const char *sdw_codec_name; -- int ret = 0; - int i; - - if (!aux_codec_name) -@@ -1801,7 +1828,8 @@ static int is_sdca_aux_dev_present(struct device *dev, - if (!sdw_codec_name) - return -ENOMEM; - -- sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); -+ struct device *sdw_dev __free(put_device) = -+ bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); - if (!sdw_dev) { - dev_err(dev, "codec %s not found\n", sdw_codec_name); - return -EINVAL; -@@ -1811,24 +1839,19 @@ static int is_sdca_aux_dev_present(struct device *dev, - - if (!slave->sdca_data.interface_revision) { - dev_warn(dev, "No SDCA properties, assuming aux '%s' present\n", aux_codec_name); -- ret = 1; -- goto put_dev; -+ return 1; - } - - for (i = 0; i < slave->sdca_data.num_functions; i++) { - const char *fname = slave->sdca_data.function[i].name; - -- if (fname && strstr(aux_codec_name, fname)) { -- ret = 1; -- goto put_dev; -- } -+ if (fname && strstr(aux_codec_name, fname)) -+ return 1; - } - - dev_dbg(dev, "SDCA function for aux '%s' NOT FOUND on slave, skipping\n", aux_codec_name); - --put_dev: -- put_device(sdw_dev); -- return ret; -+ return 0; - } - - int asoc_sdw_count_sdw_endpoints(struct snd_soc_card *card, -@@ -1928,9 +1951,8 @@ static int is_sdca_endpoint_present(struct device *dev, - const struct snd_soc_acpi_endpoint *adr_end; - const struct asoc_sdw_dai_info *dai_info; - struct sdw_slave *slave; -- struct device *sdw_dev; - const char *sdw_codec_name; -- int ret, i; -+ int i; - - adr_end = &adr_dev->endpoints[end_index]; - dai_info = &codec_info->dais[adr_end->num]; -@@ -1939,7 +1961,8 @@ static int is_sdca_endpoint_present(struct device *dev, - if (!sdw_codec_name) - return -ENOMEM; - -- sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); -+ struct device *sdw_dev __free(put_device) = -+ bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); - if (!sdw_dev) { - dev_err(dev, "codec %s not found\n", sdw_codec_name); - return -EINVAL; -@@ -1950,8 +1973,7 @@ static int is_sdca_endpoint_present(struct device *dev, - /* Make sure BIOS provides SDCA properties */ - if (!slave->sdca_data.interface_revision) { - dev_warn(&slave->dev, "SDCA properties not found in the BIOS\n"); -- ret = 1; -- goto put_device; -+ return 1; - } - - for (i = 0; i < slave->sdca_data.num_functions; i++) { -@@ -1960,8 +1982,7 @@ static int is_sdca_endpoint_present(struct device *dev, - if (dai_type == dai_info->dai_type) { - dev_dbg(&slave->dev, "DAI type %d sdca function %s found\n", - dai_type, slave->sdca_data.function[i].name); -- ret = 1; -- goto put_device; -+ return 1; - } - } - -@@ -1969,21 +1990,16 @@ static int is_sdca_endpoint_present(struct device *dev, - "SDCA device function for DAI type %d not supported, skip endpoint\n", - dai_info->dai_type); - -- ret = 0; -- --put_device: -- put_device(sdw_dev); -- return ret; -+ return 0; - } - --int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, -+int asoc_sdw_parse_sdw_endpoints(struct device *dev, -+ struct asoc_sdw_mc_private *ctx, - struct snd_soc_aux_dev *soc_aux, - struct asoc_sdw_dailink *soc_dais, - struct asoc_sdw_endpoint *soc_ends, - int *num_devs) - { -- struct device *dev = card->dev; -- struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); - struct snd_soc_acpi_mach *mach = dev_get_platdata(dev); - struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; - const struct snd_soc_acpi_link_adr *adr_link; -@@ -2045,7 +2061,7 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, - ctx->ignore_internal_dmic |= codec_info->ignore_internal_dmic; - - if (codec_info->count_sidecar && codec_info->add_sidecar) { -- ret = codec_info->count_sidecar(card, &num_dais, num_devs); -+ ret = codec_info->count_sidecar(ctx, &num_dais, num_devs); - if (ret) - return ret; - -diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c ---- a/sound/soc/soc-component.c -+++ b/sound/soc/soc-component.c -@@ -29,17 +29,42 @@ static inline int _soc_component_ret_reg_rw(struct snd_soc_component *component, - func, component->name, reg); - } - --static inline int soc_component_field_shift(struct snd_soc_component *component, -- unsigned int mask) -+struct snd_soc_component *snd_soc_component_alloc(struct device *dev) - { -- if (!mask) { -- dev_err(component->dev, "ASoC: error field mask is zero for %s\n", -- component->name); -- return 0; -- } -+ struct snd_soc_component *component = devm_kzalloc(dev, sizeof(*component), GFP_KERNEL); - -- return (ffs(mask) - 1); -+ if (!component) -+ return NULL; -+ -+ component->dev = dev; -+ -+ return component; - } -+EXPORT_SYMBOL_GPL(snd_soc_component_alloc); -+ -+void snd_soc_component_set_name(struct snd_soc_component *component, const char *name) -+{ -+ component->name = name; -+} -+EXPORT_SYMBOL_GPL(snd_soc_component_set_name); -+ -+const char *snd_soc_component_name(struct snd_soc_component *component) -+{ -+ return component->name; -+} -+EXPORT_SYMBOL_GPL(snd_soc_component_name); -+ -+void snd_soc_component_set_priv(struct snd_soc_component *component, void *priv) -+{ -+ component->priv = priv; -+} -+EXPORT_SYMBOL_GPL(snd_soc_component_set_priv); -+ -+void *snd_soc_component_to_priv(struct snd_soc_component *component) -+{ -+ return component->priv; -+} -+EXPORT_SYMBOL_GPL(snd_soc_component_to_priv); - - /* - * We might want to check substream by using list. -@@ -820,6 +845,18 @@ int snd_soc_component_update_bits_async(struct snd_soc_component *component, - } - EXPORT_SYMBOL_GPL(snd_soc_component_update_bits_async); - -+static inline int soc_component_field_shift(struct snd_soc_component *component, -+ unsigned int mask) -+{ -+ if (!mask) { -+ dev_err(component->dev, "ASoC: error field mask is zero for %s\n", -+ component->name); -+ return 0; -+ } -+ -+ return (ffs(mask) - 1); -+} -+ - /** - * snd_soc_component_read_field() - Read register field value - * @component: Component to read from -diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c ---- a/sound/soc/soc-core.c -+++ b/sound/soc/soc-core.c -@@ -2707,10 +2707,11 @@ static void snd_soc_del_component_unlocked(struct snd_soc_component *component) - list_del(&component->list); - } - --int snd_soc_component_initialize(struct snd_soc_component *component, -- const struct snd_soc_component_driver *driver, -- struct device *dev) -+static int soc_component_initialize(struct snd_soc_component *component, -+ const struct snd_soc_component_driver *driver) - { -+ struct device *dev = component->dev; -+ - component->dapm = snd_soc_dapm_alloc(dev); - if (!component->dapm) - return -ENOMEM; -@@ -2730,16 +2731,14 @@ int snd_soc_component_initialize(struct snd_soc_component *component, - } - } - -- component->dev = dev; - component->driver = driver; - - return 0; - } --EXPORT_SYMBOL_GPL(snd_soc_component_initialize); - --int snd_soc_add_component(struct snd_soc_component *component, -- struct snd_soc_dai_driver *dai_drv, -- int num_dai) -+static int soc_component_add(struct snd_soc_component *component, -+ struct snd_soc_dai_driver *dai_drv, -+ int num_dai) - { - struct snd_soc_card *card, *c; - int ret; -@@ -2778,27 +2777,36 @@ int snd_soc_add_component(struct snd_soc_component *component, - - return ret; - } --EXPORT_SYMBOL_GPL(snd_soc_add_component); - --int snd_soc_register_component(struct device *dev, -+int snd_soc_register_component_c(struct snd_soc_component *component, - const struct snd_soc_component_driver *component_driver, - struct snd_soc_dai_driver *dai_drv, - int num_dai) - { -- struct snd_soc_component *component; - int ret; - -- component = devm_kzalloc(dev, sizeof(*component), GFP_KERNEL); -- if (!component) -- return -ENOMEM; -- -- ret = snd_soc_component_initialize(component, component_driver, dev); -+ ret = soc_component_initialize(component, component_driver); - if (ret < 0) - return ret; - -- return snd_soc_add_component(component, dai_drv, num_dai); -+ return soc_component_add(component, dai_drv, num_dai); - } --EXPORT_SYMBOL_GPL(snd_soc_register_component); -+EXPORT_SYMBOL_GPL(snd_soc_register_component_c); -+ -+int snd_soc_register_component_d(struct device *dev, -+ const struct snd_soc_component_driver *component_driver, -+ struct snd_soc_dai_driver *dai_drv, -+ int num_dai) -+{ -+ struct snd_soc_component *component; -+ -+ component = snd_soc_component_alloc(dev); -+ if (!component) -+ return -ENOMEM; -+ -+ return snd_soc_register_component_c(component, component_driver, dai_drv, num_dai); -+} -+EXPORT_SYMBOL_GPL(snd_soc_register_component_d); - - /** - * snd_soc_unregister_component_by_driver - Unregister component using a given driver -diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c ---- a/sound/soc/soc-dapm.c -+++ b/sound/soc/soc-dapm.c -@@ -1203,6 +1203,8 @@ static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w, - case snd_soc_dapm_pga: - case snd_soc_dapm_effect: - case snd_soc_dapm_out_drv: -+ case snd_soc_dapm_encoder: -+ case snd_soc_dapm_decoder: - wname_in_long_name = true; - kcname_in_long_name = true; - break; -@@ -2785,6 +2787,8 @@ static ssize_t dapm_widget_show_component(struct snd_soc_component *component, - case snd_soc_dapm_pga: - case snd_soc_dapm_effect: - case snd_soc_dapm_out_drv: -+ case snd_soc_dapm_encoder: -+ case snd_soc_dapm_decoder: - case snd_soc_dapm_mixer: - case snd_soc_dapm_mixer_named_ctl: - case snd_soc_dapm_supply: -@@ -3367,6 +3371,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) - case snd_soc_dapm_pga: - case snd_soc_dapm_effect: - case snd_soc_dapm_out_drv: -+ case snd_soc_dapm_encoder: -+ case snd_soc_dapm_decoder: - dapm_new_pga(w); - break; - case snd_soc_dapm_dai_link: -diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c ---- a/sound/soc/soc-generic-dmaengine-pcm.c -+++ b/sound/soc/soc-generic-dmaengine-pcm.c -@@ -3,6 +3,7 @@ - // Copyright (C) 2013, Analog Devices Inc. - // Author: Lars-Peter Clausen - -+#include - #include - #include - #include -@@ -77,7 +78,7 @@ static int dmaengine_pcm_hw_params(struct snd_soc_component *component, - struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) - { -- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); -+ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); - struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); - struct dma_slave_config slave_config; - int ret; -@@ -99,7 +100,7 @@ dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component, - struct snd_pcm_substream *substream) - { - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); -- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); -+ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); - struct device *dma_dev = dmaengine_dma_dev(pcm, substream); - struct dma_chan *chan = pcm->chan[substream->stream]; - struct snd_dmaengine_dai_dma_data *dma_data; -@@ -148,7 +149,7 @@ dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component, - static int dmaengine_pcm_open(struct snd_soc_component *component, - struct snd_pcm_substream *substream) - { -- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); -+ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); - struct dma_chan *chan = pcm->chan[substream->stream]; - int ret; - -@@ -176,7 +177,7 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel( - struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_substream *substream) - { -- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); -+ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); - struct snd_dmaengine_dai_dma_data *dma_data; - - if (rtd->dai_link->num_cpus > 1) { -@@ -219,7 +220,7 @@ static bool dmaengine_pcm_can_report_residue(struct device *dev, - static int dmaengine_pcm_new(struct snd_soc_component *component, - struct snd_soc_pcm_runtime *rtd) - { -- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); -+ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); - const struct snd_dmaengine_pcm_config *config = pcm->config; - struct device *dev = component->dev; - size_t prealloc_buffer_size; -@@ -279,7 +280,7 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer( - struct snd_soc_component *component, - struct snd_pcm_substream *substream) - { -- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); -+ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); - - if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE) - return snd_dmaengine_pcm_pointer_no_residue(substream); -@@ -293,7 +294,7 @@ static int dmaengine_copy(struct snd_soc_component *component, - struct iov_iter *iter, unsigned long bytes) - { - struct snd_pcm_runtime *runtime = substream->runtime; -- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); -+ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); - int (*process)(struct snd_pcm_substream *substream, - int channel, unsigned long hwoff, - unsigned long bytes) = pcm->config->process; -@@ -395,6 +396,27 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, - */ - if (PTR_ERR(chan) == -EPROBE_DEFER) - return -EPROBE_DEFER; -+ -+ bool has_fw_node = dev->of_node || is_acpi_device_node(dev->fwnode); -+ bool name_exists_in_fw = false; -+ -+ if (has_fw_node) -+ name_exists_in_fw = device_property_match_string(dev, -+ "dma-names", -+ name) >= 0; -+ -+ if (has_fw_node && name_exists_in_fw) -+ dev_warn(dev, "DTS/ACPI DMA channel '%s' request failed (%ld)\n", -+ name, PTR_ERR(chan)); -+ -+ if (has_fw_node && !name_exists_in_fw) -+ dev_warn(dev, "DTS/ACPI name '%s' not found, legacy failed (%ld)\n", -+ name, PTR_ERR(chan)); -+ -+ if (!has_fw_node) -+ dev_warn(dev, "Legacy DMA channel '%s' request failed (%ld)\n", -+ name, PTR_ERR(chan)); -+ - pcm->chan[i] = NULL; - } else { - pcm->chan[i] = chan; -@@ -406,6 +428,12 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, - if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) - pcm->chan[1] = pcm->chan[0]; - -+ if (!pcm->chan[0] && -+ !pcm->chan[1]) { -+ dev_err(dev, "no DMA channel found for either playback or capture\n"); -+ return -ENODEV; -+ } -+ - return 0; - } - -@@ -435,10 +463,15 @@ static const struct snd_dmaengine_pcm_config snd_dmaengine_pcm_default_config = - int snd_dmaengine_pcm_register(struct device *dev, - const struct snd_dmaengine_pcm_config *config, unsigned int flags) - { -+ struct snd_soc_component *component; - const struct snd_soc_component_driver *driver; - struct dmaengine_pcm *pcm; - int ret; - -+ component = snd_soc_component_alloc(dev); -+ if (!component) -+ return -ENOMEM; -+ - pcm = kzalloc_obj(*pcm); - if (!pcm) - return -ENOMEM; -@@ -449,7 +482,8 @@ int snd_dmaengine_pcm_register(struct device *dev, - pcm->flags = flags; - - if (config->name) -- pcm->component.name = config->name; -+ snd_soc_component_set_name(component, config->name); -+ snd_soc_component_set_priv(component, pcm); - - ret = dmaengine_pcm_request_chan_of(pcm, dev, config); - if (ret) -@@ -460,11 +494,7 @@ int snd_dmaengine_pcm_register(struct device *dev, - else - driver = &dmaengine_pcm_component; - -- ret = snd_soc_component_initialize(&pcm->component, driver, dev); -- if (ret) -- goto err_free_dma; -- -- ret = snd_soc_add_component(&pcm->component, NULL, 0); -+ ret = snd_soc_register_component(component, driver, NULL, 0); - if (ret) - goto err_free_dma; - -@@ -493,7 +523,7 @@ void snd_dmaengine_pcm_unregister(struct device *dev) - if (!component) - return; - -- pcm = soc_component_to_pcm(component); -+ pcm = snd_soc_component_to_priv(component); - - snd_soc_unregister_component_by_driver(dev, component->driver); - dmaengine_pcm_release_chan(pcm); -diff --git a/sound/soc/soc-topology-test.c b/sound/soc/soc-topology-test.c ---- a/sound/soc/soc-topology-test.c -+++ b/sound/soc/soc-topology-test.c -@@ -45,15 +45,13 @@ static void snd_soc_tplg_test_exit(struct kunit *test) - struct kunit_soc_component { - struct kunit *kunit; - int expect; /* what result we expect when loading topology */ -- struct snd_soc_component comp; - struct snd_soc_card card; - struct firmware fw; - }; - - static int d_probe(struct snd_soc_component *component) - { -- struct kunit_soc_component *kunit_comp = -- container_of(component, struct kunit_soc_component, comp); -+ struct kunit_soc_component *kunit_comp = snd_soc_component_to_priv(component); - int ret; - - ret = snd_soc_tplg_component_load(component, NULL, &kunit_comp->fw); -@@ -65,8 +63,7 @@ static int d_probe(struct snd_soc_component *component) - - static void d_remove(struct snd_soc_component *component) - { -- struct kunit_soc_component *kunit_comp = -- container_of(component, struct kunit_soc_component, comp); -+ struct kunit_soc_component *kunit_comp = snd_soc_component_to_priv(component); - int ret; - - ret = snd_soc_tplg_component_remove(component); -@@ -214,8 +211,7 @@ static struct tplg_tmpl_002 tplg_tmpl_with_pcm = { - */ - static int d_probe_null_comp(struct snd_soc_component *component) - { -- struct kunit_soc_component *kunit_comp = -- container_of(component, struct kunit_soc_component, comp); -+ struct kunit_soc_component *kunit_comp = snd_soc_component_to_priv(component); - int ret; - - /* instead of passing component pointer as first argument, pass NULL here */ -@@ -234,6 +230,7 @@ static const struct snd_soc_component_driver test_component_null_comp = { - static void snd_soc_tplg_test_load_with_null_comp(struct kunit *test) - { - struct kunit_soc_component *kunit_comp; -+ struct snd_soc_component *component; - int ret; - - /* prepare */ -@@ -249,15 +246,17 @@ static void snd_soc_tplg_test_load_with_null_comp(struct kunit *test) - kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); - kunit_comp->card.fully_routed = true; - -+ component = snd_soc_component_alloc(test_dev); -+ KUNIT_ASSERT_NOT_NULL(test, component); -+ -+ snd_soc_component_set_priv(component, kunit_comp); -+ - /* run test */ - ret = snd_soc_register_card(&kunit_comp->card); - if (ret != 0 && ret != -EPROBE_DEFER) - KUNIT_FAIL(test, "Failed to register card"); - -- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component_null_comp, test_dev); -- KUNIT_EXPECT_EQ(test, 0, ret); -- -- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); -+ ret = snd_soc_register_component(component, &test_component_null_comp, NULL, 0); - KUNIT_EXPECT_EQ(test, 0, ret); - - /* cleanup */ -@@ -276,6 +275,7 @@ static void snd_soc_tplg_test_load_with_null_comp(struct kunit *test) - static void snd_soc_tplg_test_load_with_null_ops(struct kunit *test) - { - struct kunit_soc_component *kunit_comp; -+ struct snd_soc_component *component; - int ret; - - /* prepare */ -@@ -291,15 +291,17 @@ static void snd_soc_tplg_test_load_with_null_ops(struct kunit *test) - kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); - kunit_comp->card.fully_routed = true; - -+ component = snd_soc_component_alloc(test_dev); -+ KUNIT_ASSERT_NOT_NULL(test, component); -+ -+ snd_soc_component_set_priv(component, kunit_comp); -+ - /* run test */ - ret = snd_soc_register_card(&kunit_comp->card); - if (ret != 0 && ret != -EPROBE_DEFER) - KUNIT_FAIL(test, "Failed to register card"); - -- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); -- KUNIT_EXPECT_EQ(test, 0, ret); -- -- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); -+ ret = snd_soc_register_component(component, &test_component, NULL, 0); - KUNIT_EXPECT_EQ(test, 0, ret); - - /* cleanup */ -@@ -318,8 +320,7 @@ static void snd_soc_tplg_test_load_with_null_ops(struct kunit *test) - */ - static int d_probe_null_fw(struct snd_soc_component *component) - { -- struct kunit_soc_component *kunit_comp = -- container_of(component, struct kunit_soc_component, comp); -+ struct kunit_soc_component *kunit_comp = snd_soc_component_to_priv(component); - int ret; - - /* instead of passing fw pointer as third argument, pass NULL here */ -@@ -338,6 +339,7 @@ static const struct snd_soc_component_driver test_component_null_fw = { - static void snd_soc_tplg_test_load_with_null_fw(struct kunit *test) - { - struct kunit_soc_component *kunit_comp; -+ struct snd_soc_component *component; - int ret; - - /* prepare */ -@@ -353,15 +355,17 @@ static void snd_soc_tplg_test_load_with_null_fw(struct kunit *test) - kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); - kunit_comp->card.fully_routed = true; - -+ component = snd_soc_component_alloc(test_dev); -+ KUNIT_ASSERT_NOT_NULL(test, component); -+ -+ snd_soc_component_set_priv(component, kunit_comp); -+ - /* run test */ - ret = snd_soc_register_card(&kunit_comp->card); - if (ret != 0 && ret != -EPROBE_DEFER) - KUNIT_FAIL(test, "Failed to register card"); - -- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component_null_fw, test_dev); -- KUNIT_EXPECT_EQ(test, 0, ret); -- -- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); -+ ret = snd_soc_register_component(component, &test_component_null_fw, NULL, 0); - KUNIT_EXPECT_EQ(test, 0, ret); - - /* cleanup */ -@@ -375,6 +379,7 @@ static void snd_soc_tplg_test_load_with_null_fw(struct kunit *test) - static void snd_soc_tplg_test_load_empty_tplg(struct kunit *test) - { - struct kunit_soc_component *kunit_comp; -+ struct snd_soc_component *component; - struct tplg_tmpl_001 *data; - int size; - int ret; -@@ -401,15 +406,17 @@ static void snd_soc_tplg_test_load_empty_tplg(struct kunit *test) - kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); - kunit_comp->card.fully_routed = true; - -+ component = snd_soc_component_alloc(test_dev); -+ KUNIT_ASSERT_NOT_NULL(test, component); -+ -+ snd_soc_component_set_priv(component, kunit_comp); -+ - /* run test */ - ret = snd_soc_register_card(&kunit_comp->card); - if (ret != 0 && ret != -EPROBE_DEFER) - KUNIT_FAIL(test, "Failed to register card"); - -- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); -- KUNIT_EXPECT_EQ(test, 0, ret); -- -- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); -+ ret = snd_soc_register_component(component, &test_component, NULL, 0); - KUNIT_EXPECT_EQ(test, 0, ret); - - /* cleanup */ -@@ -425,6 +432,7 @@ static void snd_soc_tplg_test_load_empty_tplg(struct kunit *test) - static void snd_soc_tplg_test_load_empty_tplg_bad_magic(struct kunit *test) - { - struct kunit_soc_component *kunit_comp; -+ struct snd_soc_component *component; - struct tplg_tmpl_001 *data; - int size; - int ret; -@@ -456,15 +464,17 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_magic(struct kunit *test) - kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); - kunit_comp->card.fully_routed = true; - -+ component = snd_soc_component_alloc(test_dev); -+ KUNIT_ASSERT_NOT_NULL(test, component); -+ -+ snd_soc_component_set_priv(component, kunit_comp); -+ - /* run test */ - ret = snd_soc_register_card(&kunit_comp->card); - if (ret != 0 && ret != -EPROBE_DEFER) - KUNIT_FAIL(test, "Failed to register card"); - -- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); -- KUNIT_EXPECT_EQ(test, 0, ret); -- -- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); -+ ret = snd_soc_register_component(component, &test_component, NULL, 0); - KUNIT_EXPECT_EQ(test, 0, ret); - - /* cleanup */ -@@ -480,6 +490,7 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_magic(struct kunit *test) - static void snd_soc_tplg_test_load_empty_tplg_bad_abi(struct kunit *test) - { - struct kunit_soc_component *kunit_comp; -+ struct snd_soc_component *component; - struct tplg_tmpl_001 *data; - int size; - int ret; -@@ -511,15 +522,17 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_abi(struct kunit *test) - kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); - kunit_comp->card.fully_routed = true; - -+ component = snd_soc_component_alloc(test_dev); -+ KUNIT_ASSERT_NOT_NULL(test, component); -+ -+ snd_soc_component_set_priv(component, kunit_comp); -+ - /* run test */ - ret = snd_soc_register_card(&kunit_comp->card); - if (ret != 0 && ret != -EPROBE_DEFER) - KUNIT_FAIL(test, "Failed to register card"); - -- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); -- KUNIT_EXPECT_EQ(test, 0, ret); -- -- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); -+ ret = snd_soc_register_component(component, &test_component, NULL, 0); - KUNIT_EXPECT_EQ(test, 0, ret); - - /* cleanup */ -@@ -535,6 +548,7 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_abi(struct kunit *test) - static void snd_soc_tplg_test_load_empty_tplg_bad_size(struct kunit *test) - { - struct kunit_soc_component *kunit_comp; -+ struct snd_soc_component *component; - struct tplg_tmpl_001 *data; - int size; - int ret; -@@ -566,15 +580,17 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_size(struct kunit *test) - kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); - kunit_comp->card.fully_routed = true; - -+ component = snd_soc_component_alloc(test_dev); -+ KUNIT_ASSERT_NOT_NULL(test, component); -+ -+ snd_soc_component_set_priv(component, kunit_comp); -+ - /* run test */ - ret = snd_soc_register_card(&kunit_comp->card); - if (ret != 0 && ret != -EPROBE_DEFER) - KUNIT_FAIL(test, "Failed to register card"); - -- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); -- KUNIT_EXPECT_EQ(test, 0, ret); -- -- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); -+ ret = snd_soc_register_component(component, &test_component, NULL, 0); - KUNIT_EXPECT_EQ(test, 0, ret); - - /* cleanup */ -@@ -590,6 +606,7 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_size(struct kunit *test) - static void snd_soc_tplg_test_load_empty_tplg_bad_payload_size(struct kunit *test) - { - struct kunit_soc_component *kunit_comp; -+ struct snd_soc_component *component; - struct tplg_tmpl_001 *data; - int size; - int ret; -@@ -622,15 +639,17 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_payload_size(struct kunit *tes - kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); - kunit_comp->card.fully_routed = true; - -+ component = snd_soc_component_alloc(test_dev); -+ KUNIT_ASSERT_NOT_NULL(test, component); -+ -+ snd_soc_component_set_priv(component, kunit_comp); -+ - /* run test */ - ret = snd_soc_register_card(&kunit_comp->card); - if (ret != 0 && ret != -EPROBE_DEFER) - KUNIT_FAIL(test, "Failed to register card"); - -- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); -- KUNIT_EXPECT_EQ(test, 0, ret); -- -- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); -+ ret = snd_soc_register_component(component, &test_component, NULL, 0); - KUNIT_EXPECT_EQ(test, 0, ret); - - /* cleanup */ -@@ -644,6 +663,7 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_payload_size(struct kunit *tes - static void snd_soc_tplg_test_load_pcm_tplg(struct kunit *test) - { - struct kunit_soc_component *kunit_comp; -+ struct snd_soc_component *component; - u8 *data; - int size; - int ret; -@@ -670,15 +690,17 @@ static void snd_soc_tplg_test_load_pcm_tplg(struct kunit *test) - kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); - kunit_comp->card.fully_routed = true; - -+ component = snd_soc_component_alloc(test_dev); -+ KUNIT_ASSERT_NOT_NULL(test, component); -+ -+ snd_soc_component_set_priv(component, kunit_comp); -+ - /* run test */ - ret = snd_soc_register_card(&kunit_comp->card); - if (ret != 0 && ret != -EPROBE_DEFER) - KUNIT_FAIL(test, "Failed to register card"); - -- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); -- KUNIT_EXPECT_EQ(test, 0, ret); -- -- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); -+ ret = snd_soc_register_component(component, &test_component, NULL, 0); - KUNIT_EXPECT_EQ(test, 0, ret); - - snd_soc_unregister_component(test_dev); -@@ -693,6 +715,7 @@ static void snd_soc_tplg_test_load_pcm_tplg(struct kunit *test) - static void snd_soc_tplg_test_load_pcm_tplg_reload_comp(struct kunit *test) - { - struct kunit_soc_component *kunit_comp; -+ struct snd_soc_component *component; - u8 *data; - int size; - int ret; -@@ -720,16 +743,19 @@ static void snd_soc_tplg_test_load_pcm_tplg_reload_comp(struct kunit *test) - kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); - kunit_comp->card.fully_routed = true; - -+ component = snd_soc_component_alloc(test_dev); -+ KUNIT_ASSERT_NOT_NULL(test, component); -+ -+ snd_soc_component_set_priv(component, kunit_comp); -+ - /* run test */ - ret = snd_soc_register_card(&kunit_comp->card); - if (ret != 0 && ret != -EPROBE_DEFER) - KUNIT_FAIL(test, "Failed to register card"); - - for (i = 0; i < 100; i++) { -- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); -- KUNIT_EXPECT_EQ(test, 0, ret); - -- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); -+ ret = snd_soc_register_component(component, &test_component, NULL, 0); - KUNIT_EXPECT_EQ(test, 0, ret); - - snd_soc_unregister_component(test_dev); -@@ -745,6 +771,7 @@ static void snd_soc_tplg_test_load_pcm_tplg_reload_comp(struct kunit *test) - static void snd_soc_tplg_test_load_pcm_tplg_reload_card(struct kunit *test) - { - struct kunit_soc_component *kunit_comp; -+ struct snd_soc_component *component; - u8 *data; - int size; - int ret; -@@ -772,11 +799,13 @@ static void snd_soc_tplg_test_load_pcm_tplg_reload_card(struct kunit *test) - kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); - kunit_comp->card.fully_routed = true; - -- /* run test */ -- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); -- KUNIT_EXPECT_EQ(test, 0, ret); -+ component = snd_soc_component_alloc(test_dev); -+ KUNIT_ASSERT_NOT_NULL(test, component); - -- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); -+ snd_soc_component_set_priv(component, kunit_comp); -+ -+ /* run test */ -+ ret = snd_soc_register_component(component, &test_component, NULL, 0); - KUNIT_EXPECT_EQ(test, 0, ret); - - for (i = 0; i < 100; i++) { -diff --git a/sound/soc/sof/amd/Kconfig b/sound/soc/sof/amd/Kconfig ---- a/sound/soc/sof/amd/Kconfig -+++ b/sound/soc/sof/amd/Kconfig -@@ -104,4 +104,15 @@ config SND_SOC_SOF_AMD_ACP70 - AMD ACP7.0/ACP7.1 version based platforms. - Say Y if you want to enable SOF on ACP7.0/ACP7.1 based platforms. - -+config SND_SOC_SOF_AMD_ACP7X -+ tristate "SOF support for ACP7.B/7.F platforms" -+ depends on SND_SOC_SOF_PCI -+ depends on AMD_NODE -+ select SND_SOC_SOF_AMD_COMMON -+ help -+ Select this option for SOF support on AMD ACP7.B and ACP7.F PCI -+ revision based platforms. -+ Say Y if you want to enable SOF on ACP7.B/7.F based platforms. -+ If unsure select "N". -+ - endif -diff --git a/sound/soc/sof/amd/Makefile b/sound/soc/sof/amd/Makefile ---- a/sound/soc/sof/amd/Makefile -+++ b/sound/soc/sof/amd/Makefile -@@ -11,6 +11,7 @@ snd-sof-amd-rembrandt-y := pci-rmb.o rembrandt.o - snd-sof-amd-vangogh-y := pci-vangogh.o vangogh.o - snd-sof-amd-acp63-y := pci-acp63.o acp63.o - snd-sof-amd-acp70-y := pci-acp70.o acp70.o -+snd-sof-amd-acp7x-y := pci-acp7x.o acp7x.o - - obj-$(CONFIG_SND_SOC_SOF_AMD_COMMON) += snd-sof-amd-acp.o - obj-$(CONFIG_SND_SOC_SOF_AMD_RENOIR) += snd-sof-amd-renoir.o -@@ -18,3 +19,4 @@ obj-$(CONFIG_SND_SOC_SOF_AMD_REMBRANDT) += snd-sof-amd-rembrandt.o - obj-$(CONFIG_SND_SOC_SOF_AMD_VANGOGH) += snd-sof-amd-vangogh.o - obj-$(CONFIG_SND_SOC_SOF_AMD_ACP63) += snd-sof-amd-acp63.o - obj-$(CONFIG_SND_SOC_SOF_AMD_ACP70) += snd-sof-amd-acp70.o -+obj-$(CONFIG_SND_SOC_SOF_AMD_ACP7X) += snd-sof-amd-acp7x.o -diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h ---- a/sound/soc/sof/amd/acp-dsp-offset.h -+++ b/sound/soc/sof/amd/acp-dsp-offset.h -@@ -68,12 +68,14 @@ - #define ACP5X_PGFSM_BASE 0x1424 - #define ACP6X_PGFSM_BASE 0x1024 - #define ACP70_PGFSM_BASE ACP6X_PGFSM_BASE -+#define ACP7X_PGFSM_BASE ACP6X_PGFSM_BASE - #define PGFSM_CONTROL_OFFSET 0x0 - #define PGFSM_STATUS_OFFSET 0x4 - #define ACP3X_CLKMUX_SEL 0x1424 - #define ACP5X_CLKMUX_SEL 0x142C - #define ACP6X_CLKMUX_SEL 0x102C - #define ACP70_CLKMUX_SEL ACP6X_CLKMUX_SEL -+#define ACP7X_CLKMUX_SEL ACP6X_CLKMUX_SEL - - /* Registers from ACP_INTR block */ - #define ACP3X_EXT_INTR_STAT 0x1808 -@@ -86,21 +88,31 @@ - #define ACP70_EXTERNAL_INTR_CNTL ACP6X_EXTERNAL_INTR_CNTL - #define ACP70_EXT_INTR_STAT ACP6X_EXT_INTR_STAT - #define ACP70_EXT_INTR_STAT1 ACP6X_EXT_INTR_STAT1 -+#define ACP7X_EXTERNAL_INTR_ENB ACP6X_EXTERNAL_INTR_ENB -+#define ACP7X_EXTERNAL_INTR_CNTL 0x1A04 -+#define ACP7X_EXT_INTR_STAT 0x1A1C -+#define ACP7X_EXTERNAL_INTR_CNTL1 0x1A08 -+#define ACP7X_EXT_INTR_STAT1 0x1A20 - - #define ACP3X_DSP_SW_INTR_BASE 0x1814 - #define ACP5X_DSP_SW_INTR_BASE 0x1814 - #define ACP6X_DSP_SW_INTR_BASE 0x1808 - #define ACP70_DSP_SW_INTR_BASE ACP6X_DSP_SW_INTR_BASE -+#define ACP7X_DSP_SW_INTR_BASE 0x1860 - #define DSP_SW_INTR_CNTL_OFFSET 0x0 - #define DSP_SW_INTR_STAT_OFFSET 0x4 -+#define ACP7X_DSP_SW_INTR_STAT (ACP7X_DSP_SW_INTR_BASE + DSP_SW_INTR_STAT_OFFSET) - #define DSP_SW_INTR_TRIG_OFFSET 0x8 -+#define ACP7X_DSP_SW_INTR_TRIG_OFFSET 0x30 - #define ACP3X_ERROR_STATUS 0x18C4 - #define ACP6X_ERROR_STATUS 0x1A4C - #define ACP70_ERROR_STATUS ACP6X_ERROR_STATUS -+#define ACP7X_ERROR_STATUS 0x1A88 - #define ACP3X_AXI2DAGB_SEM_0 0x1880 - #define ACP5X_AXI2DAGB_SEM_0 0x1884 - #define ACP6X_AXI2DAGB_SEM_0 0x1874 - #define ACP70_AXI2DAGB_SEM_0 ACP6X_AXI2DAGB_SEM_0 -+#define ACP7X_AXI2DAGB_SEM_0 0x18F4 - - /* ACP common registers to report errors related to I2S & SoundWire interfaces */ - #define ACP3X_SW_I2S_ERROR_REASON 0x18C8 -@@ -123,6 +135,7 @@ - #define ACP_SCRATCH_REG_0 0x10000 - #define ACP6X_DSP_FUSION_RUNSTALL 0x0644 - #define ACP70_DSP_FUSION_RUNSTALL ACP6X_DSP_FUSION_RUNSTALL -+#define ACP7X_DSP_FUSION_RUNSTALL ACP6X_DSP_FUSION_RUNSTALL - - /* Cache window registers */ - #define ACP_DSP0_CACHE_OFFSET0 0x0420 -@@ -139,5 +152,9 @@ - #define ACP70_SDW1_HOST_WAKE_STAT BIT(25) - #define ACP70_SDW0_PME_STAT BIT(26) - #define ACP70_SDW1_PME_STAT BIT(27) -+#define ACP7X_DSP0_IDMA_ERROR_MASK 0x4B0 -+#define ACP7X_IDMA_ERROR_MASK 0x1FF9FF -+#define ACP7X_ZSC_DSP_CTRL 0x001014 -+#define ACP7X_PME_EN ACP70_PME_EN - - #endif -diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c ---- a/sound/soc/sof/amd/acp-ipc.c -+++ b/sound/soc/sof/amd/acp-ipc.c -@@ -32,11 +32,20 @@ static void acpbus_trigger_host_to_dsp_swintr(struct acp_dev_data *adata) - struct snd_sof_dev *sdev = adata->dev; - const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); - u32 swintr_trigger; -+ unsigned int swintr_trigger_reg_offset; - -+ switch (adata->pci_rev) { -+ case ACP7B_PCI_ID: -+ case ACP7F_PCI_ID: -+ swintr_trigger_reg_offset = ACP7X_DSP_SW_INTR_TRIG_OFFSET; -+ break; -+ default: -+ swintr_trigger_reg_offset = DSP_SW_INTR_TRIG_OFFSET; -+ } - swintr_trigger = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->dsp_intr_base + -- DSP_SW_INTR_TRIG_OFFSET); -+ swintr_trigger_reg_offset); - swintr_trigger |= 0x01; -- snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->dsp_intr_base + DSP_SW_INTR_TRIG_OFFSET, -+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->dsp_intr_base + swintr_trigger_reg_offset, - swintr_trigger); - } - -diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c ---- a/sound/soc/sof/amd/acp-loader.c -+++ b/sound/soc/sof/amd/acp-loader.c -@@ -14,6 +14,7 @@ - #include - #include - #include -+#include - - #include "../ops.h" - #include "acp-dsp-offset.h" -@@ -173,10 +174,31 @@ int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev) - - adata = sdev->pdata->hw_pdata; - -- if (adata->quirks && adata->quirks->signed_fw_image) -+ if (adata->pci_rev >= ACP7B_PCI_ID) { -+ if (adata->acp_sof_signed_firmware_image) { -+ if (adata->fw_bin_size <= ACP_IMAGE_HEADER_SIZE) { -+ dev_err(sdev->dev, "Invalid signed firmware size %u\n", -+ adata->fw_bin_size); -+ return -EINVAL; -+ } -+ size_fw = get_unaligned_le32(adata->bin_buf + -+ ACP_IMAGE_HDR_SIZE_FW_SIGNED_OFF); -+ if (!size_fw || -+ size_fw > adata->fw_bin_size - ACP_IMAGE_HEADER_SIZE) { -+ dev_err(sdev->dev, -+ "Invalid signed firmware payload size %u (max %u)\n", -+ size_fw, adata->fw_bin_size - ACP_IMAGE_HEADER_SIZE); -+ return -EINVAL; -+ } -+ size_fw += ACP_IMAGE_HEADER_SIZE; -+ } else { -+ size_fw = adata->fw_bin_size; -+ } -+ } else if (adata->quirks && adata->quirks->signed_fw_image) { - size_fw = adata->fw_bin_size - ACP_FIRMWARE_SIGNATURE; -- else -+ } else { - size_fw = adata->fw_bin_size; -+ } - - page_count = PAGE_ALIGN(size_fw) >> PAGE_SHIFT; - adata->fw_bin_page_count = page_count; -@@ -312,9 +334,14 @@ int acp_sof_load_signed_firmware(struct snd_sof_dev *sdev) - } - kfree(fw_filename); - -- ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_DRAM, 0, -- (void *)adata->fw_dbin->data, -- adata->fw_dbin->size); -+ if (adata->pci_rev >= ACP7B_PCI_ID) -+ ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_SRAM, 0, -+ (void *)adata->fw_dbin->data, -+ adata->fw_dbin->size); -+ else -+ ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_DRAM, 0, -+ (void *)adata->fw_dbin->data, -+ adata->fw_dbin->size); - return ret; - } - EXPORT_SYMBOL_NS(acp_sof_load_signed_firmware, "SND_SOC_SOF_AMD_COMMON"); -diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c ---- a/sound/soc/sof/amd/acp.c -+++ b/sound/soc/sof/amd/acp.c -@@ -12,6 +12,7 @@ - * Hardware interface for generic AMD ACP processor - */ - -+#include - #include - #include - #include -@@ -60,6 +61,8 @@ static void init_dma_descriptor(struct acp_dev_data *adata) - case ACP70_PCI_ID: - case ACP71_PCI_ID: - case ACP72_PCI_ID: -+ case ACP7B_PCI_ID: -+ case ACP7F_PCI_ID: - acp_dma_desc_base_addr = ACP70_DMA_DESC_BASE_ADDR; - acp_dma_desc_max_num_dscr = ACP70_DMA_DESC_MAX_NUM_DSCR; - break; -@@ -101,6 +104,8 @@ static int config_dma_channel(struct acp_dev_data *adata, unsigned int ch, - case ACP70_PCI_ID: - case ACP71_PCI_ID: - case ACP72_PCI_ID: -+ case ACP7B_PCI_ID: -+ case ACP7F_PCI_ID: - acp_dma_cntl_0 = ACP70_DMA_CNTL_0; - acp_dma_ch_rst_sts = ACP70_DMA_CH_RST_STS; - acp_dma_dscr_err_sts_0 = ACP70_DMA_ERR_STS_0; -@@ -283,7 +288,8 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr, - } - } - -- if (adata->quirks && adata->quirks->signed_fw_image) -+ if ((adata->quirks && adata->quirks->signed_fw_image) || -+ adata->acp_sof_signed_firmware_image) - snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_INCLUDE_HDR, ACP_SHA_HEADER); - - snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_STRT_ADDR, start_addr); -@@ -342,6 +348,8 @@ int acp_dma_status(struct acp_dev_data *adata, unsigned char ch) - case ACP70_PCI_ID: - case ACP71_PCI_ID: - case ACP72_PCI_ID: -+ case ACP7B_PCI_ID: -+ case ACP7F_PCI_ID: - acp_dma_ch_sts = ACP70_DMA_CH_STS; - break; - default: -@@ -534,8 +542,17 @@ static irqreturn_t acp_irq_handler(int irq, void *dev_id) - snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP_ERROR_IRQ_MASK); - snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_sw0_i2s_err_reason, 0); - /* ACP_SW1_I2S_ERROR_REASON is newly added register from rmb platform onwards */ -- if (adata->pci_rev >= ACP_RMB_PCI_ID) -+ switch (adata->pci_rev) { -+ case ACP_RMB_PCI_ID: -+ case ACP63_PCI_ID: -+ case ACP70_PCI_ID: -+ case ACP71_PCI_ID: -+ case ACP72_PCI_ID: - snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SW1_I2S_ERROR_REASON, 0); -+ break; -+ default: -+ break; -+ } - snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_error_stat, 0); - irq_flag = 1; - } -@@ -564,6 +581,35 @@ static irqreturn_t acp_irq_handler(int irq, void *dev_id) - return IRQ_NONE; - } - -+static irqreturn_t acp7x_irq_handler(int irq, void *dev_id) -+{ -+ struct snd_sof_dev *sdev = dev_id; -+ const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); -+ unsigned int base = desc->dsp_intr_base; -+ unsigned int val; -+ unsigned int ext_intr_stat; -+ int irq_flag = 0; -+ -+ val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET); -+ if (val & ACP_DSP_TO_HOST_IRQ) { -+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET, -+ ACP_DSP_TO_HOST_IRQ); -+ return IRQ_WAKE_THREAD; -+ } -+ -+ ext_intr_stat = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat); -+ if (ext_intr_stat & ACP_ERROR_IRQ_MASK) { -+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP_ERROR_IRQ_MASK); -+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_error_stat, 0); -+ irq_flag = 1; -+ } -+ -+ if (irq_flag) -+ return IRQ_HANDLED; -+ -+ return IRQ_NONE; -+} -+ - static int acp_power_on(struct snd_sof_dev *sdev) - { - const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); -@@ -571,6 +617,7 @@ static int acp_power_on(struct snd_sof_dev *sdev) - unsigned int base = desc->pgfsm_base; - unsigned int val; - unsigned int acp_pgfsm_status_mask, acp_pgfsm_cntl_mask; -+ bool use_masked_status = false; - int ret; - - val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + PGFSM_STATUS_OFFSET); -@@ -595,6 +642,12 @@ static int acp_power_on(struct snd_sof_dev *sdev) - acp_pgfsm_status_mask = ACP70_PGFSM_STATUS_MASK; - acp_pgfsm_cntl_mask = ACP70_PGFSM_CNTL_POWER_ON_MASK; - break; -+ case ACP7B_PCI_ID: -+ case ACP7F_PCI_ID: -+ acp_pgfsm_status_mask = ACP7X_PGFSM_STATUS_MASK; -+ acp_pgfsm_cntl_mask = ACP7X_PGFSM_CNTL_POWER_ON_MASK; -+ use_masked_status = true; -+ break; - default: - return -EINVAL; - } -@@ -603,8 +656,17 @@ static int acp_power_on(struct snd_sof_dev *sdev) - snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + PGFSM_CONTROL_OFFSET, - acp_pgfsm_cntl_mask); - -- ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, base + PGFSM_STATUS_OFFSET, val, -- !val, ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US); -+ if (use_masked_status) -+ ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, -+ base + PGFSM_STATUS_OFFSET, val, -+ !(val & acp_pgfsm_status_mask), -+ ACP_REG_POLL_INTERVAL, -+ ACP_REG_POLL_TIMEOUT_US); -+ else -+ ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, -+ base + PGFSM_STATUS_OFFSET, val, -+ !val, ACP_REG_POLL_INTERVAL, -+ ACP_REG_POLL_TIMEOUT_US); - if (ret < 0) - dev_err(sdev->dev, "timeout in ACP_PGFSM_STATUS read\n"); - -@@ -703,6 +765,13 @@ static int acp_init(struct snd_sof_dev *sdev) - - snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP70_PME_EN, 1); - break; -+ case ACP7B_PCI_ID: -+ case ACP7F_PCI_ID: -+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_ZSC_DSP_CTRL, 0); -+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_PME_EN, 1); -+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_DSP0_IDMA_ERROR_MASK, -+ ACP7X_IDMA_ERROR_MASK); -+ break; - } - return 0; - } -@@ -749,6 +818,8 @@ int amd_sof_acp_suspend(struct snd_sof_dev *sdev, u32 target_state) - case ACP72_PCI_ID: - enable = true; - break; -+ default: -+ break; - } - snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_CONTROL, enable); - -@@ -990,6 +1061,194 @@ void amd_sof_acp_remove(struct snd_sof_dev *sdev) - } - EXPORT_SYMBOL_NS(amd_sof_acp_remove, "SND_SOC_SOF_AMD_COMMON"); - -+int amd_sof_acp7x_probe(struct snd_sof_dev *sdev) -+{ -+ struct pci_dev *pci = to_pci_dev(sdev->dev); -+ struct acp_dev_data *adata; -+ const struct sof_amd_acp_desc *chip; -+ const union acpi_object *obj; -+ struct acpi_device *adev; -+ unsigned int addr; -+ unsigned int irqflags; -+ int ret; -+ -+ chip = get_chip_info(sdev->pdata); -+ if (!chip) { -+ dev_err(sdev->dev, "no such device supported, chip id:%x\n", pci->device); -+ return -EIO; -+ } -+ adata = devm_kzalloc(sdev->dev, sizeof(struct acp_dev_data), GFP_KERNEL); -+ if (!adata) -+ return -ENOMEM; -+ -+ adata->dev = sdev; -+ adata->dmic_dev = platform_device_register_data(sdev->dev, "dmic-codec", -+ PLATFORM_DEVID_NONE, NULL, 0); -+ if (IS_ERR(adata->dmic_dev)) { -+ dev_err(sdev->dev, "failed to register platform for dmic codec\n"); -+ return PTR_ERR(adata->dmic_dev); -+ } -+ -+ addr = pci_resource_start(pci, ACP_DSP_BAR); -+ sdev->bar[ACP_DSP_BAR] = devm_ioremap(sdev->dev, addr, pci_resource_len(pci, ACP_DSP_BAR)); -+ if (!sdev->bar[ACP_DSP_BAR]) { -+ dev_err(sdev->dev, "ioremap error\n"); -+ ret = -ENXIO; -+ goto unregister_dev; -+ } -+ -+ pci_set_master(pci); -+ adata->addr = addr; -+ adata->reg_range = chip->reg_end_addr - chip->reg_start_addr; -+ adata->pci_rev = pci->revision; -+ mutex_init(&adata->acp_lock); -+ sdev->pdata->hw_pdata = adata; -+ -+ ret = acp_init(sdev); -+ if (ret < 0) -+ goto unregister_dev; -+ -+ adev = ACPI_COMPANION(&pci->dev); -+ -+ sdev->ipc_irq = pci->irq; -+ irqflags = IRQF_SHARED; -+ -+ ret = request_threaded_irq(pci->irq, acp7x_irq_handler, acp_irq_thread, -+ irqflags, "AudioDSP", sdev); -+ if (ret < 0) { -+ dev_err(sdev->dev, "failed to register IRQ %d\n", sdev->ipc_irq); -+ goto unregister_dev; -+ } -+ -+ if (adev) { -+ if (!acpi_dev_get_property(adev, "acp-sof-signed-firmware-image", -+ ACPI_TYPE_INTEGER, &obj)) -+ adata->acp_sof_signed_firmware_image = obj->integer.value; -+ } -+ -+ sdev->dsp_box.offset = 0; -+ sdev->dsp_box.size = BOX_SIZE_512; -+ -+ sdev->host_box.offset = sdev->dsp_box.offset + sdev->dsp_box.size; -+ sdev->host_box.size = BOX_SIZE_512; -+ -+ sdev->debug_box.offset = sdev->host_box.offset + sdev->host_box.size; -+ sdev->debug_box.size = BOX_SIZE_1024; -+ -+ if (adata->acp_sof_signed_firmware_image) { -+ adata->fw_code_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, -+ "sof-%s-code.bin", chip->name); -+ if (!adata->fw_code_bin) { -+ ret = -ENOMEM; -+ goto free_ipc_irq; -+ } -+ adata->fw_data_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, -+ "sof-%s-data.bin", chip->name); -+ if (!adata->fw_data_bin) { -+ ret = -ENOMEM; -+ goto free_ipc_irq; -+ } -+ } -+ -+ adata->enable_fw_debug = enable_fw_debug; -+ acp_memory_init(sdev); -+ acp_dsp_stream_init(sdev); -+ -+ return 0; -+ -+free_ipc_irq: -+ free_irq(sdev->ipc_irq, sdev); -+unregister_dev: -+ platform_device_unregister(adata->dmic_dev); -+ return ret; -+} -+EXPORT_SYMBOL_NS(amd_sof_acp7x_probe, "SND_SOC_SOF_AMD_COMMON"); -+ -+void amd_sof_acp7x_remove(struct snd_sof_dev *sdev) -+{ -+ struct acp_dev_data *adata = sdev->pdata->hw_pdata; -+ -+ if (sdev->ipc_irq) -+ free_irq(sdev->ipc_irq, sdev); -+ -+ if (adata->dmic_dev) -+ platform_device_unregister(adata->dmic_dev); -+ -+ acp_reset(sdev); -+} -+EXPORT_SYMBOL_NS(amd_sof_acp7x_remove, "SND_SOC_SOF_AMD_COMMON"); -+ -+int amd_sof_acp7x_suspend(struct snd_sof_dev *sdev, u32 target_state) -+{ -+ struct acp_dev_data *acp_data; -+ int ret; -+ bool enable = false; -+ -+ acp_data = sdev->pdata->hw_pdata; -+ -+ ret = acp_reset(sdev); -+ if (ret) { -+ dev_err(sdev->dev, "ACP Reset failed\n"); -+ return ret; -+ } -+ switch (acp_data->pci_rev) { -+ case ACP7B_PCI_ID: -+ case ACP7F_PCI_ID: -+ enable = true; -+ break; -+ default: -+ break; -+ } -+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_CONTROL, enable); -+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_ZSC_DSP_CTRL, 1); -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS(amd_sof_acp7x_suspend, "SND_SOC_SOF_AMD_COMMON"); -+ -+int amd_sof_acp7x_resume(struct snd_sof_dev *sdev) -+{ -+ struct acp_dev_data *acp_data; -+ int ret; -+ -+ acp_data = sdev->pdata->hw_pdata; -+ -+ ret = acp_init(sdev); -+ if (ret) { -+ dev_err(sdev->dev, "ACP Init failed\n"); -+ return ret; -+ } -+ ret = acp_memory_init(sdev); -+ if (ret) { -+ dev_err(sdev->dev, "ACP Memory init failed\n"); -+ return ret; -+ } -+ -+ switch (acp_data->pci_rev) { -+ case ACP7B_PCI_ID: -+ case ACP7F_PCI_ID: -+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_PME_EN, 1); -+ break; -+ default: -+ break; -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS(amd_sof_acp7x_resume, "SND_SOC_SOF_AMD_COMMON"); -+ -+int amd_sof_acp7x_suspend_runtime(struct snd_sof_dev *sdev) -+{ -+ return amd_sof_acp7x_suspend(sdev, 0); -+} -+EXPORT_SYMBOL_NS(amd_sof_acp7x_suspend_runtime, "SND_SOC_SOF_AMD_COMMON"); -+ -+int amd_sof_acp7x_resume_runtime(struct snd_sof_dev *sdev) -+{ -+ return amd_sof_acp7x_resume(sdev); -+} -+EXPORT_SYMBOL_NS(amd_sof_acp7x_resume_runtime, "SND_SOC_SOF_AMD_COMMON"); -+ - MODULE_LICENSE("Dual BSD/GPL"); - MODULE_DESCRIPTION("AMD ACP sof driver"); - MODULE_IMPORT_NS("SOUNDWIRE_AMD_INIT"); -diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h ---- a/sound/soc/sof/amd/acp.h -+++ b/sound/soc/sof/amd/acp.h -@@ -76,6 +76,12 @@ - #define ACP70_PCI_ID 0x70 - #define ACP71_PCI_ID 0x71 - #define ACP72_PCI_ID 0x72 -+#define ACP7B_PCI_ID 0x7B -+#define ACP7F_PCI_ID 0x7F -+ -+#define ACP7X_PGFSM_CNTL_POWER_ON_MASK 0x7F -+#define ACP7X_PGFSM_STATUS_MASK 0xFFF -+#define ACP7X_SRAM_PTE_OFFSET ACP6X_SRAM_PTE_OFFSET - - #define HOST_BRIDGE_CZN 0x1630 - #define HOST_BRIDGE_VGH 0x1645 -@@ -107,6 +113,9 @@ - #define PROBE_STATUS_BIT BIT(31) - - #define ACP_FIRMWARE_SIGNATURE 0x100 -+#define ACP_IMAGE_HEADER_SIZE ACP_FIRMWARE_SIGNATURE -+#define ACP_IMAGE_HDR_SIZE_FW_SIGNED_OFF 0x14 -+ - #define ACP_ERROR_IRQ_MASK BIT(29) - #define ACP_SDW0_IRQ_MASK BIT(21) - #define ACP_SDW1_IRQ_MASK BIT(2) -@@ -271,6 +280,7 @@ struct acp_dev_data { - /* acp70_sdw1_wake_event flag set to true when wake irq asserted for SW1 instance */ - bool acp70_sdw1_wake_event; - unsigned int pci_rev; -+ int acp_sof_signed_firmware_image; - }; - - void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes); -@@ -344,6 +354,16 @@ int sof_acp63_ops_init(struct snd_sof_dev *sdev); - extern struct snd_sof_dsp_ops sof_acp70_ops; - int sof_acp70_ops_init(struct snd_sof_dev *sdev); - -+extern struct snd_sof_dsp_ops sof_acp7x_ops; -+int sof_acp7x_ops_init(struct snd_sof_dev *sdev); -+ -+int amd_sof_acp7x_probe(struct snd_sof_dev *sdev); -+void amd_sof_acp7x_remove(struct snd_sof_dev *sdev); -+int amd_sof_acp7x_suspend(struct snd_sof_dev *sdev, u32 target_state); -+int amd_sof_acp7x_resume(struct snd_sof_dev *sdev); -+int amd_sof_acp7x_suspend_runtime(struct snd_sof_dev *sdev); -+int amd_sof_acp7x_resume_runtime(struct snd_sof_dev *sdev); -+ - struct snd_soc_acpi_mach *amd_sof_machine_select(struct snd_sof_dev *sdev); - /* Machine configuration */ - int snd_amd_acp_find_config(struct pci_dev *pci); -diff --git a/sound/soc/sof/amd/acp7x.c b/sound/soc/sof/amd/acp7x.c -new file mode 100644 ---- /dev/null -+++ b/sound/soc/sof/amd/acp7x.c -@@ -0,0 +1,185 @@ -+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) -+// -+// This file is provided under a dual BSD/GPLv2 license. When using or -+// redistributing this file, you may do so under either license. -+// -+// Copyright(c) 2025 Advanced Micro Devices, Inc. -+// -+// Authors: Vijendar Mukunda -+ -+/* -+ * Hardware interface for Audio DSP on ACP7.B/7.F platforms -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include "../ops.h" -+#include "../sof-audio.h" -+#include "acp.h" -+#include "acp-dsp-offset.h" -+ -+#define I2S_TDM0_INSTANCE 0 -+#define I2S_TDM1_INSTANCE 1 -+#define I2S_TDM2_INSTANCE 2 -+#define PDM0_DMIC_INSTANCE 3 -+#define PDM1_DMIC_INSTANCE 4 -+ -+static struct snd_soc_dai_driver acp7x_sof_dai[] = { -+ [I2S_TDM0_INSTANCE] = { -+ .id = I2S_TDM0_INSTANCE, -+ .name = "acp-sof-i2s0", -+ .playback = { -+ .rates = SNDRV_PCM_RATE_8000_96000, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | -+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, -+ .channels_min = 2, -+ .channels_max = 8, -+ .rate_min = 8000, -+ .rate_max = 96000, -+ }, -+ .capture = { -+ .rates = SNDRV_PCM_RATE_8000_48000, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | -+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, -+ /* Supporting only stereo for I2S HS controller capture */ -+ .channels_min = 2, -+ .channels_max = 2, -+ .rate_min = 8000, -+ .rate_max = 48000, -+ }, -+ }, -+ -+ [I2S_TDM1_INSTANCE] = { -+ .id = I2S_TDM1_INSTANCE, -+ .name = "acp-sof-i2s1", -+ .playback = { -+ .rates = SNDRV_PCM_RATE_8000_96000, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | -+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, -+ .channels_min = 2, -+ .channels_max = 8, -+ .rate_min = 8000, -+ .rate_max = 96000, -+ }, -+ .capture = { -+ .rates = SNDRV_PCM_RATE_8000_48000, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | -+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, -+ /* Supporting only stereo for I2S BT controller capture */ -+ .channels_min = 2, -+ .channels_max = 2, -+ .rate_min = 8000, -+ .rate_max = 48000, -+ }, -+ }, -+ -+ [I2S_TDM2_INSTANCE] = { -+ .id = I2S_TDM2_INSTANCE, -+ .name = "acp-sof-i2s2", -+ .playback = { -+ .rates = SNDRV_PCM_RATE_8000_96000, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | -+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, -+ .channels_min = 2, -+ .channels_max = 8, -+ .rate_min = 8000, -+ .rate_max = 96000, -+ }, -+ .capture = { -+ .rates = SNDRV_PCM_RATE_8000_48000, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | -+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, -+ /* Supporting only stereo for I2S SP controller capture */ -+ .channels_min = 2, -+ .channels_max = 2, -+ .rate_min = 8000, -+ .rate_max = 48000, -+ }, -+ }, -+ -+ [PDM0_DMIC_INSTANCE] = { -+ .id = PDM0_DMIC_INSTANCE, -+ .name = "acp-sof-dmic0", -+ .capture = { -+ .rates = SNDRV_PCM_RATE_8000_48000, -+ .formats = SNDRV_PCM_FMTBIT_S32_LE, -+ .channels_min = 2, -+ .channels_max = 4, -+ .rate_min = 8000, -+ .rate_max = 48000, -+ }, -+ }, -+ -+ [PDM1_DMIC_INSTANCE] = { -+ .id = PDM1_DMIC_INSTANCE, -+ .name = "acp-sof-dmic1", -+ .capture = { -+ .rates = SNDRV_PCM_RATE_8000_96000, -+ .formats = SNDRV_PCM_FMTBIT_S32_LE, -+ .channels_min = 2, -+ .channels_max = 4, -+ .rate_min = 8000, -+ .rate_max = 96000, -+ }, -+ }, -+}; -+ -+static int sof_acp7x_post_fw_run_delay(struct snd_sof_dev *sdev) -+{ -+ /* -+ * Resuming from suspend in some cases may cause the DSP firmware -+ * to enter an unrecoverable faulty state. Delaying a bit any host -+ * to DSP transmission right after firmware boot completion seems -+ * to resolve the issue. -+ */ -+ if (!sdev->first_boot) -+ usleep_range(100, 150); -+ -+ return 0; -+} -+ -+struct snd_sof_dsp_ops sof_acp7x_ops; -+EXPORT_SYMBOL_NS(sof_acp7x_ops, "SND_SOC_SOF_AMD_COMMON"); -+ -+int sof_acp7x_ops_init(struct snd_sof_dev *sdev) -+{ -+ struct acpi_device *adev = ACPI_COMPANION(&to_pci_dev(sdev->dev)->dev); -+ const union acpi_object *obj; -+ int acp_sof_signed_firmware_image = 0; -+ int acp_sof_post_fw_run_delay = 0; -+ -+ /* common defaults */ -+ memcpy(&sof_acp7x_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops)); -+ -+ sof_acp7x_ops.drv = acp7x_sof_dai; -+ sof_acp7x_ops.num_drv = ARRAY_SIZE(acp7x_sof_dai); -+ sof_acp7x_ops.probe = amd_sof_acp7x_probe; -+ sof_acp7x_ops.remove = amd_sof_acp7x_remove; -+ -+ if (adev) { -+ if (!acpi_dev_get_property(adev, "acp-sof-signed-firmware-image", -+ ACPI_TYPE_INTEGER, &obj)) -+ acp_sof_signed_firmware_image = obj->integer.value; -+ -+ if (!acpi_dev_get_property(adev, "acp-sof-post_fw_run_delay", -+ ACPI_TYPE_INTEGER, &obj)) -+ acp_sof_post_fw_run_delay = obj->integer.value; -+ } -+ -+ if (acp_sof_signed_firmware_image) -+ sof_acp7x_ops.load_firmware = acp_sof_load_signed_firmware; -+ -+ if (acp_sof_post_fw_run_delay) -+ sof_acp7x_ops.post_fw_run = sof_acp7x_post_fw_run_delay; -+ -+ sof_acp7x_ops.suspend = amd_sof_acp7x_suspend; -+ sof_acp7x_ops.resume = amd_sof_acp7x_resume; -+ sof_acp7x_ops.runtime_suspend = amd_sof_acp7x_suspend_runtime; -+ sof_acp7x_ops.runtime_resume = amd_sof_acp7x_resume_runtime; -+ -+ return 0; -+} -diff --git a/sound/soc/sof/amd/pci-acp7x.c b/sound/soc/sof/amd/pci-acp7x.c -new file mode 100644 ---- /dev/null -+++ b/sound/soc/sof/amd/pci-acp7x.c -@@ -0,0 +1,116 @@ -+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) -+// -+// This file is provided under a dual BSD/GPLv2 license. When using or -+// redistributing this file, you may do so under either license. -+// -+// Copyright(c) 2025 Advanced Micro Devices, Inc. All rights reserved. -+// -+// Authors: Vijendar Mukunda -+ -+/* -+ * PCI interface for ACP7.B/7.F devices -+ */ -+ -+#include -+#include -+#include -+#include -+ -+#include "../ops.h" -+#include "../sof-pci-dev.h" -+#include "../../amd/mach-config.h" -+#include "acp.h" -+#include "acp-dsp-offset.h" -+ -+#define ACP7X_FUTURE_REG_ACLK_0 0x18e0 -+#define ACP7X_REG_START 0x1240000 -+#define ACP7X_REG_END 0x125C000 -+ -+static const struct sof_amd_acp_desc acp7x_chip_info = { -+ .name = "acp7x", -+ .pgfsm_base = ACP7X_PGFSM_BASE, -+ .ext_intr_enb = ACP6X_EXTERNAL_INTR_ENB, -+ .ext_intr_cntl = ACP7X_EXTERNAL_INTR_CNTL, -+ .ext_intr_stat = ACP7X_EXT_INTR_STAT, -+ .ext_intr_stat1 = ACP7X_EXT_INTR_STAT1, -+ .dsp_intr_base = ACP7X_DSP_SW_INTR_BASE, -+ .acp_error_stat = ACP7X_ERROR_STATUS, -+ .sram_pte_offset = ACP7X_SRAM_PTE_OFFSET, -+ .hw_semaphore_offset = ACP7X_AXI2DAGB_SEM_0, -+ .fusion_dsp_offset = ACP7X_DSP_FUSION_RUNSTALL, -+ .probe_reg_offset = ACP7X_FUTURE_REG_ACLK_0, -+ .reg_start_addr = ACP7X_REG_START, -+ .reg_end_addr = ACP7X_REG_END, -+}; -+ -+static const struct sof_dev_desc acp7x_desc = { -+ .machines = snd_soc_acpi_amd_acp7x_sof_machines, -+ .resindex_lpe_base = 0, -+ .resindex_pcicfg_base = -1, -+ .resindex_imr_base = -1, -+ .irqindex_host_ipc = -1, -+ .chip_info = &acp7x_chip_info, -+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_3), -+ .ipc_default = SOF_IPC_TYPE_3, -+ .default_fw_path = { -+ [SOF_IPC_TYPE_3] = "amd/sof", -+ }, -+ .default_tplg_path = { -+ [SOF_IPC_TYPE_3] = "amd/sof-tplg", -+ }, -+ .default_fw_filename = { -+ [SOF_IPC_TYPE_3] = "sof-acp7x.ri", -+ }, -+ .nocodec_tplg_filename = "sof-acp.tplg", -+ .ops = &sof_acp7x_ops, -+ .ops_init = sof_acp7x_ops_init, -+}; -+ -+static int acp7x_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) -+{ -+ unsigned int flag; -+ -+ switch (pci->revision) { -+ case ACP7B_PCI_ID: -+ case ACP7F_PCI_ID: -+ break; -+ default: -+ return -ENODEV; -+ } -+ -+ flag = snd_amd_acp_find_config(pci); -+ if (flag != FLAG_AMD_SOF && flag != FLAG_AMD_SOF_ONLY_DMIC) -+ return -ENODEV; -+ -+ return sof_pci_probe(pci, pci_id); -+} -+ -+static void acp7x_pci_remove(struct pci_dev *pci) -+{ -+ sof_pci_remove(pci); -+} -+ -+/* PCI IDs */ -+static const struct pci_device_id acp7x_pci_ids[] = { -+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID), -+ .driver_data = (unsigned long)&acp7x_desc}, -+ { 0, } -+}; -+MODULE_DEVICE_TABLE(pci, acp7x_pci_ids); -+ -+/* pci_driver definition */ -+static struct pci_driver snd_sof_pci_amd_acp7x_driver = { -+ .name = KBUILD_MODNAME, -+ .id_table = acp7x_pci_ids, -+ .probe = acp7x_pci_probe, -+ .remove = acp7x_pci_remove, -+ .driver = { -+ .pm = pm_ptr(&sof_pci_pm), -+ }, -+}; -+module_pci_driver(snd_sof_pci_amd_acp7x_driver); -+ -+MODULE_LICENSE("Dual BSD/GPL"); -+MODULE_DESCRIPTION("ACP7X SOF Driver"); -+MODULE_IMPORT_NS("SND_SOC_SOF_AMD_COMMON"); -+MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV"); -diff --git a/sound/soc/sof/fw-file-profile.c b/sound/soc/sof/fw-file-profile.c ---- a/sound/soc/sof/fw-file-profile.c -+++ b/sound/soc/sof/fw-file-profile.c -@@ -16,20 +16,19 @@ static int sof_test_firmware_file(struct device *dev, - enum sof_ipc_type *ipc_type_to_adjust) - { - enum sof_ipc_type fw_ipc_type; -- const struct firmware *fw; -- const char *fw_filename; - const u32 *magic; - int ret; - -- fw_filename = kasprintf(GFP_KERNEL, "%s/%s", profile->fw_path, -- profile->fw_name); -+ const char *fw_filename __free(kfree) = -+ kasprintf(GFP_KERNEL, "%s/%s", profile->fw_path, -+ profile->fw_name); - if (!fw_filename) - return -ENOMEM; - -+ const struct firmware *fw __free(firmware) = NULL; - ret = firmware_request_nowarn(&fw, fw_filename, dev); - if (ret < 0) { - dev_dbg(dev, "Failed to open firmware file: %s\n", fw_filename); -- kfree(fw_filename); - return ret; - } - -@@ -44,8 +43,7 @@ static int sof_test_firmware_file(struct device *dev, - break; - default: - dev_err(dev, "Invalid firmware magic: %#x\n", *magic); -- ret = -EINVAL; -- goto out; -+ return -EINVAL; - } - - if (ipc_type_to_adjust) { -@@ -54,13 +52,10 @@ static int sof_test_firmware_file(struct device *dev, - dev_err(dev, - "ipc type mismatch between %s and expected: %d vs %d\n", - fw_filename, fw_ipc_type, profile->ipc_type); -- ret = -EINVAL; -+ return -EINVAL; - } --out: -- release_firmware(fw); -- kfree(fw_filename); - -- return ret; -+ return 0; - } - - static int sof_test_topology_file(struct device *dev, -diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c ---- a/sound/soc/sof/intel/hda-ctrl.c -+++ b/sound/soc/sof/intel/hda-ctrl.c -@@ -186,6 +186,7 @@ EXPORT_SYMBOL_NS(hda_dsp_ctrl_clock_power_gating, "SND_SOC_SOF_INTEL_HDA_COMMON" - int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec) - { - struct hdac_bus *bus = sof_to_bus(sdev); -+ struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); - struct hdac_stream *stream; - int sd_offset, ret = 0; - u32 gctl; -@@ -193,6 +194,16 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec) - if (bus->chip_init) - return 0; - -+ /* -+ * The controller reset clears the ACE2+ link DMA stream allocation -+ * constraints; reset the masks to reflect this. -+ */ -+ memset(sof_hda->link_dma_active_sdw_mask, 0, -+ sizeof(sof_hda->link_dma_active_sdw_mask)); -+ memset(sof_hda->link_dma_active_multi_mask, 0, -+ sizeof(sof_hda->link_dma_active_multi_mask)); -+ sof_hda->link_dma_out_hda_used_mask = 0; -+ - hda_codec_set_codec_wakeup(sdev, true); - - hda_dsp_ctrl_misc_clock_gating(sdev, false); -@@ -223,6 +234,14 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec) - /* Accept unsolicited responses */ - snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL); - -+ /* Perform a one-time enumeration of the Multi-Link capability */ -+ ret = hda_bus_ml_init(bus); -+ if (ret < 0) { -+ dev_err(sdev->dev, "%s: failed to enumerate multi-links\n", -+ __func__); -+ goto err; -+ } -+ - if (detect_codec) - hda_codec_detect_mask(sdev); - -diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c ---- a/sound/soc/sof/intel/hda-dai-ops.c -+++ b/sound/soc/sof/intel/hda-dai-ops.c -@@ -20,7 +20,7 @@ - /* These ops are only applicable for the HDA DAI's in their current form */ - #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK) - /* -- * This function checks if the host dma channel corresponding -+ * This function checks if the host DMA stream corresponding - * to the link DMA stream_tag argument is assigned to one - * of the FEs connected to the BE DAI. - */ -@@ -42,23 +42,53 @@ static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd, - } - - static struct hdac_ext_stream * --hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream) -+hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream, -+ enum hda_bus_ml_link_type link_type) - { - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); -+ struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); - struct sof_intel_hda_stream *hda_stream; - const struct sof_intel_dsp_desc *chip; - struct snd_sof_dev *sdev; - struct hdac_ext_stream *res = NULL; - struct hdac_stream *hstream = NULL; -- - int stream_dir = substream->stream; -+ bool is_multi = link_type == HDA_BUS_ML_LINK_HDA || link_type == HDA_BUS_ML_LINK_UAOL; -+ bool is_play = stream_dir == SNDRV_PCM_STREAM_PLAYBACK; -+ bool is_sdw = link_type == HDA_BUS_ML_LINK_SDW; -+ bool is_hda = link_type == HDA_BUS_ML_LINK_HDA; -+ u32 concur_block_mask = 0; -+ u32 seq_block_mask = 0; -+ unsigned int stream_idx; - - if (!bus->ppcap) { - dev_err(bus->dev, "stream type not supported\n"); - return NULL; - } - -+ /* -+ * On ACE2+ the link DMA stream allocator must avoid two HW errata, -+ * see the comment on struct sof_intel_hda_dev. -+ * -+ * - Concurrent cross-direction: SoundWire conflicts with HDA, iDisp -+ * and UAOL on the same physical stream index; SSP and DMIC are safe. -+ * - Sequential playback: a stream index previously used by an HDA/iDisp -+ * link cannot drive any non-HDA/iDisp link in the same direction -+ * until the next controller reset. -+ * -+ * The masks are protected by bus->reg_lock; sample them inside the -+ * lock together with the stream walk to keep the decision atomic -+ * with concurrent allocations and releases. -+ */ - guard(spinlock_irq)(&bus->reg_lock); -+ -+ if (is_sdw) -+ concur_block_mask = sof_hda->link_dma_active_multi_mask[!stream_dir]; -+ else if (is_multi) -+ concur_block_mask = sof_hda->link_dma_active_sdw_mask[!stream_dir]; -+ if (is_play && !is_hda) -+ seq_block_mask = sof_hda->link_dma_out_hda_used_mask; -+ - list_for_each_entry(hstream, &bus->stream_list, list) { - struct hdac_ext_stream *hext_stream = - stream_to_hdac_ext_stream(hstream); -@@ -69,6 +99,12 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream - sdev = hda_stream->sdev; - chip = get_chip_info(sdev->pdata); - -+ stream_idx = hstream->stream_tag - 1; -+ -+ /* skip streams blocked by the ACE2+ allocator constraints */ -+ if ((concur_block_mask | seq_block_mask) & BIT(stream_idx)) -+ continue; -+ - /* check if link is available */ - if (!hext_stream->link_locked) { - /* -@@ -95,7 +131,7 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream - - /* - * This must be a hostless stream. -- * So reserve the host DMA channel. -+ * So reserve the host DMA stream. - */ - hda_stream->host_reserved = 1; - break; -@@ -109,6 +145,16 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream - - res->link_locked = 1; - res->link_substream = substream; -+ -+ stream_idx = res->hstream.stream_tag - 1; -+ if (is_sdw) -+ sof_hda->link_dma_active_sdw_mask[stream_dir] |= BIT(stream_idx); -+ else if (is_multi) -+ sof_hda->link_dma_active_multi_mask[stream_dir] |= BIT(stream_idx); -+ -+ /* persistent OUT HDA/iDisp shadow, cleared only on CRST# */ -+ if (is_hda && is_play) -+ sof_hda->link_dma_out_hda_used_mask |= BIT(stream_idx); - } - - return res; -@@ -143,11 +189,13 @@ static struct hdac_ext_stream *hda_ipc4_get_hext_stream(struct snd_sof_dev *sdev - - static struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev, - struct snd_soc_dai *cpu_dai, -- struct snd_pcm_substream *substream) -+ struct snd_pcm_substream *substream, -+ struct hdac_ext_link *hlink) - { - struct hdac_ext_stream *hext_stream; -+ enum hda_bus_ml_link_type link_type = hda_bus_ml_link_get_type(hlink); - -- hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream); -+ hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream, link_type); - if (!hext_stream) - return NULL; - -@@ -160,6 +208,22 @@ static void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai - struct snd_pcm_substream *substream) - { - struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream); -+ struct sof_intel_hda_dev *sof_hda = sdev->pdata->hw_pdata; -+ struct hdac_bus *bus = sof_to_bus(sdev); -+ int dir = substream->stream; -+ unsigned int stream_idx = hext_stream->hstream.stream_tag - 1; -+ -+ /* -+ * Drop the stream index from the per-direction active concurrency masks. -+ * The two masks are mutually exclusive for a given stream/direction -+ * (and a stream of the SSP/DMIC kind appears in neither), so a blind -+ * clear of both is safe and lets us avoid having to remember the -+ * link type at allocation time. -+ */ -+ scoped_guard(spinlock_irq, &bus->reg_lock) { -+ sof_hda->link_dma_active_sdw_mask[dir] &= ~BIT(stream_idx); -+ sof_hda->link_dma_active_multi_mask[dir] &= ~BIT(stream_idx); -+ } - - snd_soc_dai_set_dma_data(cpu_dai, substream, NULL); - snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK); -diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c ---- a/sound/soc/sof/intel/hda-dai.c -+++ b/sound/soc/sof/intel/hda-dai.c -@@ -188,7 +188,7 @@ static int hda_link_dma_hw_params(struct snd_pcm_substream *substream, - - if (!hext_stream) { - if (ops->assign_hext_stream) -- hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream); -+ hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream, hlink); - } - - if (!hext_stream) -diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c ---- a/sound/soc/sof/intel/hda-dsp.c -+++ b/sound/soc/sof/intel/hda-dsp.c -@@ -1114,11 +1114,7 @@ static int hda_dsp_s5_quirk(struct snd_sof_dev *sdev) - usleep_range(500, 1000); - - /* Restore state for shutdown, back to reset */ -- ret = hda_dsp_ctrl_link_reset(sdev, true); -- if (ret < 0) -- return ret; -- -- return ret; -+ return hda_dsp_ctrl_link_reset(sdev, true); - } - - int hda_dsp_shutdown_dma_flush(struct snd_sof_dev *sdev) -diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c ---- a/sound/soc/sof/intel/hda-loader.c -+++ b/sound/soc/sof/intel/hda-loader.c -@@ -216,9 +216,10 @@ int hda_cl_trigger(struct device *dev, struct hdac_ext_stream *hext_stream, int - EXPORT_SYMBOL_NS(hda_cl_trigger, "SND_SOC_SOF_INTEL_HDA_COMMON"); - - int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab, -- bool persistent_buffer, struct hdac_ext_stream *hext_stream) -+ bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool is_iccmax) - { -- return hda_data_stream_cleanup(dev, dmab, persistent_buffer, hext_stream, false); -+ return hda_data_stream_cleanup(dev, dmab, persistent_buffer, hext_stream, -+ is_iccmax, false); - } - EXPORT_SYMBOL_NS(hda_cl_cleanup, "SND_SOC_SOF_INTEL_HDA_COMMON"); - -@@ -302,7 +303,7 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev) - * If the cleanup also fails, we return the initial error - */ - ret1 = hda_cl_cleanup(sdev->dev, &hda->iccmax_dmab, -- persistent_cl_buffer, iccmax_stream); -+ persistent_cl_buffer, iccmax_stream, true); - if (ret1 < 0) { - dev_err(sdev->dev, "error: ICCMAX stream cleanup failed\n"); - -@@ -458,7 +459,7 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) - * If the cleanup also fails, we return the initial error - */ - ret1 = hda_cl_cleanup(sdev->dev, &hda->cl_dmab, -- persistent_cl_buffer, hext_stream); -+ persistent_cl_buffer, hext_stream, false); - if (ret1 < 0) { - dev_err(sdev->dev, "error: Code loader DSP cleanup failed\n"); - -@@ -587,7 +588,7 @@ int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev, - cleanup: - /* clean up even in case of error and return the first error */ - ret1 = hda_cl_cleanup(sdev->dev, &hda->cl_dmab, persistent_cl_buffer, -- hext_stream); -+ hext_stream, false); - if (ret1 < 0) { - dev_err(sdev->dev, "%s: Code loader DSP cleanup failed\n", __func__); - -diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c ---- a/sound/soc/sof/intel/hda-mlink.c -+++ b/sound/soc/sof/intel/hda-mlink.c -@@ -432,6 +432,10 @@ int hda_bus_ml_init(struct hdac_bus *bus) - if (!bus->mlcap) - return 0; - -+ /* Enumeration is a one time operation, skip if already done */ -+ if (!list_empty(&bus->hlink_list)) -+ return 0; -+ - link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1; - - dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count); -@@ -880,19 +884,6 @@ int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, - return 0; - } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_map_stream_ch, "SND_SOC_SOF_HDA_MLINK"); - --void hda_bus_ml_put_all(struct hdac_bus *bus) --{ -- struct hdac_ext_link *hlink; -- -- list_for_each_entry(hlink, &bus->hlink_list, list) { -- struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); -- -- if (!h2link->alt) -- snd_hdac_ext_bus_link_put(bus, hlink); -- } --} --EXPORT_SYMBOL_NS(hda_bus_ml_put_all, "SND_SOC_SOF_HDA_MLINK"); -- - void hda_bus_ml_reset_losidv(struct hdac_bus *bus) - { - struct hdac_ext_link *hlink; -@@ -903,6 +894,24 @@ void hda_bus_ml_reset_losidv(struct hdac_bus *bus) - } - EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, "SND_SOC_SOF_HDA_MLINK"); - -+enum hda_bus_ml_link_type hda_bus_ml_link_get_type(struct hdac_ext_link *hlink) -+{ -+ struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); -+ -+ if (!h2link->alt) -+ return HDA_BUS_ML_LINK_HDA; -+ -+ switch (h2link->elid) { -+ case AZX_REG_ML_LEPTR_ID_SDW: -+ return HDA_BUS_ML_LINK_SDW; -+ case AZX_REG_ML_LEPTR_ID_INTEL_UAOL: -+ return HDA_BUS_ML_LINK_UAOL; -+ default: -+ return HDA_BUS_ML_LINK_OTHER; -+ } -+} -+EXPORT_SYMBOL_NS(hda_bus_ml_link_get_type, "SND_SOC_SOF_HDA_MLINK"); -+ - int hda_bus_ml_resume(struct hdac_bus *bus) - { - struct hdac_ext_link *hlink; -diff --git a/sound/soc/sof/intel/hda-sdw-bpt.c b/sound/soc/sof/intel/hda-sdw-bpt.c ---- a/sound/soc/sof/intel/hda-sdw-bpt.c -+++ b/sound/soc/sof/intel/hda-sdw-bpt.c -@@ -163,7 +163,7 @@ static int hda_sdw_bpt_dma_deprepare(struct device *dev, struct hdac_ext_stream - u32 mask; - int ret; - -- ret = hda_data_stream_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream, true); -+ ret = hda_data_stream_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream, false, true); - if (ret < 0) { - dev_err(sdev->dev, "%s: SDW BPT DMA cleanup failed\n", - __func__); -diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c ---- a/sound/soc/sof/intel/hda-stream.c -+++ b/sound/soc/sof/intel/hda-stream.c -@@ -198,13 +198,18 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, - - mask = (1 << hstream->index); - -+ /* Reset the spib_addr before disabling SPIB */ -+ if (!enable) -+ sof_io_write(sdev, hstream->spib_addr, 0); -+ - /* enable/disable SPIB for the stream */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_SPIB_BAR, - SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCCTL, mask, - enable << hstream->index); - - /* set the SPIB value */ -- sof_io_write(sdev, hstream->spib_addr, size); -+ if (enable) -+ sof_io_write(sdev, hstream->spib_addr, size); - - return 0; - } -@@ -608,6 +613,9 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, - return ret; - } - -+ /* Host DMA is not running */ -+ hstream->running = false; -+ - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, - sd_offset + SOF_HDA_ADSP_REG_SD_STS, - SOF_HDA_CL_DMA_SD_INT_MASK, -@@ -1323,16 +1331,18 @@ hda_data_stream_prepare(struct device *dev, unsigned int format, unsigned int si - EXPORT_SYMBOL_NS(hda_data_stream_prepare, "SND_SOC_SOF_INTEL_HDA_COMMON"); - - int hda_data_stream_cleanup(struct device *dev, struct snd_dma_buffer *dmab, -- bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool pair) -+ bool persistent_buffer, struct hdac_ext_stream *hext_stream, -+ bool is_iccmax, bool pair) - { - struct snd_sof_dev *sdev = dev_get_drvdata(dev); - struct hdac_stream *hstream = hdac_stream(hext_stream); - int sd_offset = SOF_STREAM_SD_OFFSET(hstream); - int ret = 0; - -- if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK) -+ if (!is_iccmax) - ret = hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0); -- else -+ -+ if (hstream->direction == SNDRV_PCM_STREAM_CAPTURE) - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, - SOF_HDA_SD_CTL_DMA_START, 0); - -diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c ---- a/sound/soc/sof/intel/hda.c -+++ b/sound/soc/sof/intel/hda.c -@@ -421,6 +421,20 @@ static inline void hda_dsp_sdw_process_mic_privacy(struct snd_sof_dev *sdev) { } - /* pre fw run operations */ - int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev) - { -+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; -+ const struct sof_intel_dsp_desc *chip = hda->desc; -+ int ret; -+ -+ /* Power down DSP if left enabled to ensure a clean boot state. */ -+ if (hda_dsp_core_is_enabled(sdev, chip->host_managed_cores_mask)) { -+ dev_dbg(sdev->dev, "DSP core enabled, power down DSP first\n"); -+ -+ ret = chip->power_down_dsp(sdev); -+ if (ret < 0) -+ dev_warn(sdev->dev, -+ "%s: failed to power down already-enabled DSP\n", __func__); -+ } -+ - /* disable clock gating and power gating */ - return hda_dsp_ctrl_clock_power_gating(sdev, false); - } -@@ -625,8 +639,6 @@ static int hda_init_caps(struct snd_sof_dev *sdev) - return ret; - } - -- hda_bus_ml_init(bus); -- - /* Skip SoundWire if it is not supported */ - if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH))) - goto skip_soundwire; -@@ -670,8 +682,6 @@ static int hda_init_caps(struct snd_sof_dev *sdev) - if (!HDA_IDISP_CODEC(bus->codec_mask)) - hda_codec_i915_display_power(sdev, false); - -- hda_bus_ml_put_all(bus); -- - return 0; - } - -diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h ---- a/sound/soc/sof/intel/hda.h -+++ b/sound/soc/sof/intel/hda.h -@@ -523,6 +523,29 @@ struct sof_intel_hda_dev { - /* the maximum number of streams (playback + capture) supported */ - u32 stream_max; - -+ /* -+ * ACE2+ link DMA stream allocation constraints (stream index = -+ * stream_tag - 1, shared between input and output directions). All -+ * masks are cleared by hda_dsp_ctrl_init_chip() on controller reset -+ * (CRST#). -+ * -+ * - Concurrent (cross-direction) constraint: a SoundWire stream and -+ * a HDA/iDisp/UAOL stream cannot share a physical stream index -+ * across directions, the resulting LLP/timestamp values are wrong. -+ * link_dma_active_sdw_mask and link_dma_active_multi_mask -+ * (indexed by SNDRV_PCM_STREAM_*) track currently allocated -+ * streams per direction in each of the conflicting groups; SSP -+ * and DMIC do not participate. Bits are cleared on stream release. -+ * -+ * - Sequential (playback only) constraint: once a HDA/iDisp link -+ * has used a playback stream index, that index cannot drive a -+ * non-HDA/iDisp link in the same direction until the next CRST#. -+ * link_dma_out_hda_used_mask records this. -+ */ -+ u32 link_dma_active_sdw_mask[SNDRV_PCM_STREAM_LAST + 1]; -+ u32 link_dma_active_multi_mask[SNDRV_PCM_STREAM_LAST + 1]; -+ u32 link_dma_out_hda_used_mask; -+ - /* PM related */ - bool l1_disabled;/* is DMI link L1 disabled? */ - -@@ -739,7 +762,7 @@ struct hdac_ext_stream *hda_cl_prepare(struct device *dev, unsigned int format, - int hda_cl_trigger(struct device *dev, struct hdac_ext_stream *hext_stream, int cmd); - - int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab, -- bool persistent_buffer, struct hdac_ext_stream *hext_stream); -+ bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool is_iccmax); - int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot); - #define HDA_CL_STREAM_FORMAT 0x40 - -@@ -911,7 +934,8 @@ hda_data_stream_prepare(struct device *dev, unsigned int format, unsigned int si - bool is_iccmax, bool pair); - - int hda_data_stream_cleanup(struct device *dev, struct snd_dma_buffer *dmab, -- bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool pair); -+ bool persistent_buffer, struct hdac_ext_stream *hext_stream, -+ bool is_iccmax, bool pair); - - /* common dai driver */ - extern struct snd_soc_dai_driver skl_dai[]; -@@ -1030,7 +1054,8 @@ struct hda_dai_widget_dma_ops { - struct snd_pcm_substream *substream); - struct hdac_ext_stream *(*assign_hext_stream)(struct snd_sof_dev *sdev, - struct snd_soc_dai *cpu_dai, -- struct snd_pcm_substream *substream); -+ struct snd_pcm_substream *substream, -+ struct hdac_ext_link *hlink); - void (*release_hext_stream)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, - struct snd_pcm_substream *substream); - void (*setup_hext_stream)(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream, -diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c ---- a/sound/soc/sof/intel/icl.c -+++ b/sound/soc/sof/intel/icl.c -@@ -9,10 +9,14 @@ - * Hardware interface for audio DSP on IceLake. - */ - --#include --#include --#include -+#include - #include -+#include -+#include -+#include -+#include -+#include -+ - #include "../ipc4-priv.h" - #include "../ops.h" - #include "hda.h" -diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c ---- a/sound/soc/sof/intel/mtl.c -+++ b/sound/soc/sof/intel/mtl.c -@@ -236,6 +236,17 @@ int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable) - } - EXPORT_SYMBOL_NS(mtl_enable_interrupts, "SND_SOC_SOF_INTEL_MTL"); - -+static bool mtl_dsp_is_enabled(struct snd_sof_dev *sdev) -+{ -+ int val; -+ -+ val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_HFDSSCS); -+ if (val & MTL_HFDSSCS_CPA_MASK) -+ return true; -+ -+ return false; -+} -+ - /* pre fw run operations */ - static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev) - { -@@ -249,6 +260,18 @@ static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev) - u32 dsppwrsts; - const struct sof_intel_dsp_desc *chip; - -+ /* Power down the DSP if it is left enabled to ensure clean boot state */ -+ if (mtl_dsp_is_enabled(sdev)) { -+ dev_dbg(sdev->dev, "powering down DSP first\n"); -+ -+ ret = mtl_power_down_dsp(sdev); -+ if (ret < 0) { -+ dev_warn(sdev->dev, -+ "%s: failed to power down already-enabled DSP\n", __func__); -+ /* Continue anyway to attempt recovery */ -+ } -+ } -+ - chip = get_chip_info(sdev->pdata); - if (chip->hw_ip_version > SOF_INTEL_ACE_2_0) { - dsppwrctl = PTL_HFPWRCTL2; -diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c ---- a/sound/soc/sof/ipc3-pcm.c -+++ b/sound/soc/sof/ipc3-pcm.c -@@ -421,6 +421,12 @@ static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, - dev_dbg(component->dev, "AMD_SDW channels_min: %d channels_max: %d\n", - channels->min, channels->max); - break; -+ case SOF_DAI_AMD_I2S: -+ rate->min = private->dai_config->acp_i2s.fsync_rate; -+ rate->max = private->dai_config->acp_i2s.fsync_rate; -+ channels->min = private->dai_config->acp_i2s.tdm_slots; -+ channels->max = private->dai_config->acp_i2s.tdm_slots; -+ break; - default: - dev_err(component->dev, "Invalid DAI type %d\n", private->dai_config->type); - break; -diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c ---- a/sound/soc/sof/ipc3-topology.c -+++ b/sound/soc/sof/ipc3-topology.c -@@ -281,7 +281,10 @@ static const struct sof_topology_token acpdmic_tokens[] = { - offsetof(struct sof_ipc_dai_acpdmic_params, pdm_ch)}, - }; - --/* ACPI2S */ -+/* -+ * ACPI2S tokens fill struct sof_ipc_dai_acp_params; SOF_DAI_AMD_I2S (ACPTDM -+ * on ACP7.B/7.F) reuses this tuple group rather than defining a parallel set. -+ */ - static const struct sof_topology_token acpi2s_tokens[] = { - {SOF_TKN_AMD_ACPI2S_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_acp_params, fsync_rate)}, -@@ -289,6 +292,8 @@ static const struct sof_topology_token acpi2s_tokens[] = { - offsetof(struct sof_ipc_dai_acp_params, tdm_slots)}, - {SOF_TKN_AMD_ACPI2S_TDM_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_acp_params, tdm_mode)}, -+ {SOF_TKN_AMD_ACPI2S_FORMAT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, -+ offsetof(struct sof_ipc_dai_acp_params, format)}, - }; - - /* MICFIL PDM */ -@@ -1363,6 +1368,35 @@ static int sof_link_acp_sdw_load(struct snd_soc_component *scomp, struct snd_sof - return 0; - } - -+static int sof_link_acp_i2s_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, -+ struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) -+{ -+ struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; -+ struct sof_dai_private_data *private = dai->private; -+ u32 size = sizeof(*config); -+ int ret; -+ -+ /* handle master/slave and inverted clocks */ -+ sof_dai_set_format(hw_config, config); -+ -+ /* init IPC */ -+ memset(&config->acp_i2s, 0, sizeof(config->acp_i2s)); -+ config->hdr.size = size; -+ -+ ret = sof_update_ipc_object(scomp, &config->acp_i2s, SOF_ACPI2S_TOKENS, slink->tuples, -+ slink->num_tuples, size, slink->num_hw_configs); -+ if (ret < 0) -+ return ret; -+ -+ dai->number_configs = 1; -+ dai->current_config = 0; -+ private->dai_config = kmemdup(config, size, GFP_KERNEL); -+ if (!private->dai_config) -+ return -ENOMEM; -+ -+ return 0; -+} -+ - static int sof_link_afe_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, - struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) - { -@@ -1692,6 +1726,9 @@ static int sof_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget) - case SOF_DAI_AMD_SDW: - ret = sof_link_acp_sdw_load(scomp, slink, config, dai); - break; -+ case SOF_DAI_AMD_I2S: -+ ret = sof_link_acp_i2s_load(scomp, slink, config, dai); -+ break; - default: - break; - } -diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c ---- a/sound/soc/sof/ipc4-topology.c -+++ b/sound/soc/sof/ipc4-topology.c -@@ -163,11 +163,11 @@ static const struct sof_topology_token comp_ext_tokens[] = { - {SOF_TKN_COMP_SCHED_DOMAIN, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_domain, - offsetof(struct snd_sof_widget, comp_domain)}, - {SOF_TKN_COMP_DOMAIN_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, -- offsetof(struct snd_sof_widget, dp_domain_id)}, -+ offsetof(struct snd_sof_widget, domain_id)}, - {SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, -- offsetof(struct snd_sof_widget, dp_heap_bytes)}, -+ offsetof(struct snd_sof_widget, heap_bytes)}, - {SOF_TKN_COMP_STACK_BYTES_REQUIREMENT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, -- offsetof(struct snd_sof_widget, dp_stack_bytes)}, -+ offsetof(struct snd_sof_widget, stack_bytes)}, - }; - - static const struct sof_topology_token gain_tokens[] = { -@@ -406,6 +406,39 @@ sof_ipc4_get_input_pin_audio_fmt(struct snd_sof_widget *swidget, int pin_index) - return NULL; - } - -+static void -+sof_ipc4_evaluate_params_change(struct sof_ipc4_available_audio_format *available_fmt) -+{ -+ struct sof_ipc4_audio_format *fmt; -+ u32 in_rate, in_channels, in_valid_bits; -+ u32 out_rate, out_channels, out_valid_bits; -+ u32 changed_params = 0; -+ int i, j; -+ -+ for (i = 0; i < available_fmt->num_input_formats; i++) { -+ fmt = &available_fmt->input_pin_fmts[i].audio_fmt; -+ in_rate = fmt->sampling_frequency; -+ in_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg); -+ in_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); -+ -+ for (j = 0; j < available_fmt->num_output_formats; j++) { -+ fmt = &available_fmt->output_pin_fmts[j].audio_fmt; -+ out_rate = fmt->sampling_frequency; -+ out_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg); -+ out_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); -+ -+ if (in_rate != out_rate) -+ changed_params |= BIT(SNDRV_PCM_HW_PARAM_RATE); -+ if (in_channels != out_channels) -+ changed_params |= BIT(SNDRV_PCM_HW_PARAM_CHANNELS); -+ if (in_valid_bits != out_valid_bits) -+ changed_params |= BIT(SNDRV_PCM_HW_PARAM_FORMAT); -+ } -+ } -+ -+ available_fmt->changed_params = changed_params; -+} -+ - /** - * sof_ipc4_get_audio_fmt - get available audio formats from swidget->tuples - * @scomp: pointer to pointer to SOC component -@@ -497,6 +530,8 @@ static int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp, - available_fmt->num_output_formats); - } - -+ sof_ipc4_evaluate_params_change(available_fmt); -+ - return 0; - - err_out: -@@ -661,6 +696,9 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget) - if (ret) - goto free_copier; - -+ /* Copier can only change format */ -+ available_fmt->changed_params &= BIT(SNDRV_PCM_HW_PARAM_FORMAT); -+ - /* - * This callback is used by host copier and module-to-module copier, - * and only host copier needs to set gtw_cfg. -@@ -789,6 +827,9 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) - if (ret) - goto free_copier; - -+ /* Copier can only change format */ -+ available_fmt->changed_params &= BIT(SNDRV_PCM_HW_PARAM_FORMAT); -+ - ret = sof_update_ipc_object(scomp, &node_type, - SOF_COPIER_TOKENS, swidget->tuples, - swidget->num_tuples, sizeof(node_type), 1); -@@ -1127,6 +1168,7 @@ static int sof_ipc4_widget_setup_comp_src(struct snd_sof_widget *swidget) - "Invalid number of formats: input: %d, output: %d\n", - src->available_fmt.num_input_formats, - src->available_fmt.num_output_formats); -+ ret = -EINVAL; - goto err; - } - -@@ -1179,6 +1221,7 @@ static int sof_ipc4_widget_setup_comp_asrc(struct snd_sof_widget *swidget) - "Invalid number of formats: input: %d, output: %d\n", - asrc->available_fmt.num_input_formats, - asrc->available_fmt.num_output_formats); -+ ret = -EINVAL; - goto err; - } - -@@ -1369,6 +1412,22 @@ sof_ipc4_update_resource_usage(struct snd_sof_dev *sdev, struct snd_sof_widget * - pipeline = pipe_widget->private; - pipeline->mem_usage += total; - -+ /* -+ * If this is not a Data Processing module instance, add the -+ * required heap sizes to the sum of all module instances belonging -+ * to the same pipeline, and find the maximum stack requirement -+ * among all module instances belonging to the same pipeline. -+ */ -+ if (swidget->comp_domain != SOF_COMP_DOMAIN_DP) { -+ pipe_widget->heap_bytes += swidget->heap_bytes; -+ if (pipe_widget->stack_bytes < swidget->stack_bytes) -+ pipe_widget->stack_bytes = swidget->stack_bytes; -+ -+ dev_dbg(sdev->dev, "%s mem reqs to %s heap %u stack %u", -+ swidget->widget->name, pipe_widget->widget->name, -+ pipe_widget->heap_bytes, pipe_widget->stack_bytes); -+ } -+ - /* Update base_config->cpc from the module manifest */ - sof_ipc4_update_cpc_from_manifest(sdev, fw_module, base_config); - -@@ -1686,6 +1745,8 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget) - pipe_widget = swidget->spipe->pipe_widget; - pipeline = pipe_widget->private; - pipeline->mem_usage = 0; -+ pipe_widget->heap_bytes = 0; -+ pipe_widget->stack_bytes = 0; - - if (WIDGET_IS_AIF(swidget->id) || swidget->id == snd_soc_dapm_buffer) { - if (pipeline->use_chain_dma) { -@@ -2063,10 +2124,57 @@ static void sof_ipc4_host_config(struct snd_sof_dev *sdev, struct snd_sof_widget - } - - static int --sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, -- struct snd_pcm_hw_params *fe_params, -- struct snd_sof_platform_stream_params *platform_params, -- struct snd_pcm_hw_params *pipeline_params, int dir) -+sof_ipc4_copier_module_update_params(struct snd_sof_widget *swidget, -+ struct snd_pcm_hw_params *pipeline_params) -+{ -+ struct snd_soc_component *scomp = swidget->scomp; -+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); -+ struct sof_ipc4_copier_data *copier_data; -+ struct sof_ipc4_copier *ipc4_copier; -+ -+ switch (swidget->id) { -+ case snd_soc_dapm_aif_in: -+ case snd_soc_dapm_aif_out: -+ case snd_soc_dapm_buffer: -+ ipc4_copier = swidget->private; -+ copier_data = &ipc4_copier->data; -+ break; -+ case snd_soc_dapm_dai_in: -+ case snd_soc_dapm_dai_out: -+ { -+ struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; -+ struct sof_ipc4_pipeline *pipeline = pipe_widget->private; -+ struct snd_sof_dai *dai; -+ -+ if (pipeline->use_chain_dma) -+ return 0; -+ -+ dai = swidget->private; -+ -+ ipc4_copier = (struct sof_ipc4_copier *)dai->private; -+ copier_data = &ipc4_copier->data; -+ -+ break; -+ } -+ default: -+ dev_err(sdev->dev, "unsupported type %d for copier %s", -+ swidget->id, swidget->widget->name); -+ return -EINVAL; -+ } -+ -+ /* modify the input params for the next widget */ -+ return sof_ipc4_update_hw_params(sdev, pipeline_params, -+ &copier_data->out_format, -+ BIT(SNDRV_PCM_HW_PARAM_FORMAT) | -+ BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | -+ BIT(SNDRV_PCM_HW_PARAM_RATE)); -+} -+ -+static int -+_sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, -+ struct snd_pcm_hw_params *fe_params, -+ struct snd_sof_platform_stream_params *platform_params, -+ struct snd_pcm_hw_params *pipeline_params, int dir) - { - struct sof_ipc4_available_audio_format *available_fmt; - struct snd_soc_component *scomp = swidget->scomp; -@@ -2538,6 +2646,21 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, - return 0; - } - -+static int -+sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, -+ struct snd_pcm_hw_params *fe_params, -+ struct snd_sof_platform_stream_params *platform_params, -+ struct snd_pcm_hw_params *pipeline_params, int dir) -+{ -+ if (swidget->prepared) -+ return sof_ipc4_copier_module_update_params(swidget, -+ pipeline_params); -+ -+ return _sof_ipc4_prepare_copier_module(swidget, fe_params, -+ platform_params, pipeline_params, -+ dir); -+} -+ - static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, - struct snd_pcm_hw_params *fe_params, - struct snd_sof_platform_stream_params *platform_params, -@@ -2551,6 +2674,10 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, - u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type; - int input_fmt_index, output_fmt_index; - -+ /* This cannot happen */ -+ if (unlikely(swidget->prepared)) -+ return 0; -+ - input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, - &gain->data.base_config, - pipeline_params, -@@ -2596,6 +2723,10 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget, - u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type; - int input_fmt_index, output_fmt_index; - -+ /* Already prepared, nothing to do */ -+ if (swidget->prepared) -+ return 0; -+ - input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, - &mixer->base_config, - pipeline_params, -@@ -2642,6 +2773,10 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, - u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type; - int output_fmt_index, input_fmt_index; - -+ /* This cannot happen */ -+ if (unlikely(swidget->prepared)) -+ return 0; -+ - input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, - &src->data.base_config, - pipeline_params, -@@ -2808,6 +2943,18 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, - int ret; - - if (available_fmt->num_input_formats) { -+ if (swidget->prepared) { -+ if (!available_fmt->num_output_formats) -+ return 0; -+ -+ /* modify the pipeline params with the output format */ -+ return sof_ipc4_update_hw_params(sdev, pipeline_params, -+ &process->output_format, -+ BIT(SNDRV_PCM_HW_PARAM_FORMAT) | -+ BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | -+ BIT(SNDRV_PCM_HW_PARAM_RATE)); -+ } -+ - input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, - &process->base_config, - pipeline_params, -@@ -2820,39 +2967,69 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, - if (available_fmt->num_output_formats) { - struct sof_ipc4_audio_format *in_fmt; - struct sof_ipc4_pin_format *pin_fmt; -- u32 out_ref_rate, out_ref_channels; -- int out_ref_valid_bits, out_ref_type; -+ u32 ref_rate, ref_channels; -+ int ref_valid_bits, ref_type; - - if (available_fmt->num_input_formats) { -+ /* -+ * The process module can change parameters and their operation -+ * depends on the direction: -+ * Playback: typically they have single output format. This is -+ * to 'force' the conversion from input to output. -+ * Use the input format as reference since the single -+ * format is going to be picked. -+ * Capture: typically they have multiple output formats to -+ * convert from dai (input) to FE (output) parameters. -+ * Use the input format as base and replace the param -+ * which is changed by the module with the FE parameter -+ * Reason: we can have module which changes the -+ * parameters in path, we cannot use the full -+ * FE param set for the module output lookup. -+ */ - in_fmt = &available_fmt->input_pin_fmts[input_fmt_index].audio_fmt; - -- out_ref_rate = in_fmt->sampling_frequency; -- out_ref_channels = -+ ref_rate = in_fmt->sampling_frequency; -+ ref_channels = - SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg); -- out_ref_valid_bits = -+ ref_valid_bits = - SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); -- out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg); -+ ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg); - } else { - /* for modules without input formats, use FE params as reference */ -- out_ref_rate = params_rate(fe_params); -- out_ref_channels = params_channels(fe_params); -+ ref_rate = params_rate(fe_params); -+ ref_channels = params_channels(fe_params); - ret = sof_ipc4_get_sample_type(sdev, fe_params); - if (ret < 0) - return ret; -- out_ref_type = (u32)ret; -+ ref_type = (u32)ret; - -- out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); -- if (out_ref_valid_bits < 0) -- return out_ref_valid_bits; -+ ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); -+ if (ref_valid_bits < 0) -+ return ref_valid_bits; - } - -+ if (dir == SNDRV_PCM_STREAM_CAPTURE) { -+ if (available_fmt->changed_params & BIT(SNDRV_PCM_HW_PARAM_RATE)) -+ ref_rate = params_rate(fe_params); -+ if (available_fmt->changed_params & BIT(SNDRV_PCM_HW_PARAM_CHANNELS)) -+ ref_channels = params_channels(fe_params); -+ if (available_fmt->changed_params & BIT(SNDRV_PCM_HW_PARAM_FORMAT)) { -+ ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); -+ if (ref_valid_bits < 0) -+ return ref_valid_bits; -+ -+ ref_type = sof_ipc4_get_sample_type(sdev, fe_params); -+ if (ref_type < 0) -+ return ref_type; -+ } -+ } - output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget, - &process->base_config, - available_fmt, -- out_ref_rate, -- out_ref_channels, -- out_ref_valid_bits, -- out_ref_type); -+ ref_rate, -+ ref_channels, -+ ref_valid_bits, -+ ref_type); - if (output_fmt_index < 0) - return output_fmt_index; - -@@ -2866,9 +3043,7 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, - /* modify the pipeline params with the output format */ - ret = sof_ipc4_update_hw_params(sdev, pipeline_params, - &process->output_format, -- BIT(SNDRV_PCM_HW_PARAM_FORMAT) | -- BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | -- BIT(SNDRV_PCM_HW_PARAM_RATE)); -+ available_fmt->changed_params); - if (ret) - return ret; - } -@@ -3083,27 +3258,47 @@ static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr - return 0; - } - --static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev, -- struct snd_sof_widget *swidget, -- struct sof_ipc4_msg *msg, -- void *ipc_data, u32 ipc_size, -- void **new_data) -+static void sof_ipc4_add_init_ext_dp_memory_data(struct snd_sof_dev *sdev, -+ struct snd_sof_widget *swidget, -+ u32 *payload, u32 *ext_pos, -+ struct sof_ipc4_module_init_ext_object **hdr) -+{ -+ /* Add memory_data if comp_domain indicates DP */ -+ if (swidget->comp_domain == SOF_COMP_DOMAIN_DP) { -+ struct sof_ipc4_mod_init_ext_dp_memory_data *dp_mem_data; -+ -+ *hdr = (struct sof_ipc4_module_init_ext_object *)&payload[*ext_pos]; -+ (*hdr)->header = -+ SOF_IPC4_MOD_INIT_EXT_OBJ_ID(SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA) | -+ SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS(DIV_ROUND_UP(sizeof(*dp_mem_data), -+ sizeof(u32))); -+ *ext_pos += DIV_ROUND_UP(sizeof(**hdr), sizeof(u32)); -+ dp_mem_data = (struct sof_ipc4_mod_init_ext_dp_memory_data *)&payload[*ext_pos]; -+ dp_mem_data->domain_id = swidget->domain_id; -+ dp_mem_data->stack_bytes = swidget->stack_bytes; -+ dp_mem_data->heap_bytes = swidget->heap_bytes; -+ *ext_pos += DIV_ROUND_UP(sizeof(*dp_mem_data), sizeof(u32)); -+ } -+} -+ -+static int sof_ipc4_widget_mod_init_msg_payload(struct snd_sof_dev *sdev, -+ struct snd_sof_widget *swidget, -+ struct sof_ipc4_msg *msg, -+ void *ipc_data, u32 ipc_size, -+ void **new_data) - { -- struct sof_ipc4_mod_init_ext_dp_memory_data *dp_mem_data; - struct sof_ipc4_module_init_ext_init *ext_init; -- struct sof_ipc4_module_init_ext_object *hdr; -+ struct sof_ipc4_module_init_ext_object *hdr = NULL; - int new_size; - u32 *payload; - u32 ext_pos; - -- /* For the moment the only reason for adding init_ext_init payload is DP -- * memory data. If both stack and heap size are 0 (= use default), then -- * there is no need for init_ext_init payload. -+ /* -+ * Only DP widgets currently add init-ext objects here. Avoid allocating -+ * a max-sized payload buffer for widgets that will immediately return 0. - */ -- if (swidget->comp_domain != SOF_COMP_DOMAIN_DP) { -- msg->extension &= ~SOF_IPC4_MOD_EXT_EXTENDED_INIT_MASK; -+ if (swidget->comp_domain != SOF_COMP_DOMAIN_DP) - return 0; -- } - - payload = kzalloc(sdev->ipc->max_payload_size, GFP_KERNEL); - if (!payload) -@@ -3111,27 +3306,26 @@ static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev, - - /* Add ext_init first and set objects array flag to 1 */ - ext_init = (struct sof_ipc4_module_init_ext_init *)payload; -- ext_init->word0 |= SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_MASK; - ext_pos = DIV_ROUND_UP(sizeof(*ext_init), sizeof(u32)); - - /* Add object array objects after ext_init */ - -- /* Add dp_memory_data if comp_domain indicates DP */ -- if (swidget->comp_domain == SOF_COMP_DOMAIN_DP) { -- hdr = (struct sof_ipc4_module_init_ext_object *)&payload[ext_pos]; -- hdr->header = SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_MASK | -- SOF_IPC4_MOD_INIT_EXT_OBJ_ID(SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA) | -- SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS(DIV_ROUND_UP(sizeof(*dp_mem_data), -- sizeof(u32))); -- ext_pos += DIV_ROUND_UP(sizeof(*hdr), sizeof(u32)); -- dp_mem_data = (struct sof_ipc4_mod_init_ext_dp_memory_data *)&payload[ext_pos]; -- dp_mem_data->domain_id = swidget->dp_domain_id; -- dp_mem_data->stack_bytes = swidget->dp_stack_bytes; -- dp_mem_data->heap_bytes = swidget->dp_heap_bytes; -- ext_pos += DIV_ROUND_UP(sizeof(*dp_mem_data), sizeof(u32)); -+ sof_ipc4_add_init_ext_dp_memory_data(sdev, swidget, payload, &ext_pos, &hdr); -+ -+ /* Add following object array items here */ -+ -+ if (!hdr) { -+ /* -+ * NOTE: Remove this early bail out, when struct -+ * sof_ipc4_module_init_ext_init alone has some -+ * function. -+ */ -+ kfree(payload); -+ return 0; - } - -- /* If another array object is added, remember clear previous OBJ_LAST bit */ -+ ext_init->word0 |= SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_MASK; -+ hdr->header |= SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_MASK; - - /* Calculate final size and check that it fits to max payload size */ - new_size = ext_pos * sizeof(u32) + ipc_size; -@@ -3154,6 +3348,69 @@ static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev, - return new_size; - } - -+static void sof_ipc4_widget_pipe_ext_obj_memory_data(struct snd_sof_dev *sdev, -+ struct snd_sof_widget *swidget, -+ u32 *payload, u32 *ext_pos, -+ struct sof_ipc4_glb_pipe_ext_object **hdr) -+{ -+ struct sof_ipc4_glb_pipe_ext_obj_memory_data *mem_data; -+ -+ *hdr = (struct sof_ipc4_glb_pipe_ext_object *)&payload[*ext_pos]; -+ (*hdr)->header = -+ SOF_IPC4_GLB_PIPE_EXT_OBJ_ID(SOF_IPC4_GLB_PIPE_DATA_ID_MEM_DATA) | -+ SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS(DIV_ROUND_UP(sizeof(*mem_data), -+ sizeof(u32))); -+ *ext_pos += DIV_ROUND_UP(sizeof(**hdr), sizeof(u32)); -+ mem_data = (struct sof_ipc4_glb_pipe_ext_obj_memory_data *)&payload[*ext_pos]; -+ mem_data->domain_id = swidget->domain_id; -+ mem_data->stack_bytes = swidget->stack_bytes; -+ mem_data->heap_bytes = swidget->heap_bytes; -+ *ext_pos += DIV_ROUND_UP(sizeof(*mem_data), sizeof(u32)); -+ -+ dev_dbg(sdev->dev, -+ "%s; domain_id %u stack %u heap %u bytes", -+ swidget->widget->name, mem_data->domain_id, mem_data->stack_bytes, -+ mem_data->heap_bytes); -+} -+ -+static int sof_ipc4_widget_pipe_create_msg_payload(struct snd_sof_dev *sdev, -+ struct snd_sof_widget *swidget, -+ struct sof_ipc4_msg *msg, -+ void **new_data) -+{ -+ struct sof_ipc4_glb_pipe_payload *payload_hdr; -+ struct sof_ipc4_glb_pipe_ext_object *hdr = NULL; -+ u32 *payload; -+ u32 ext_pos; -+ -+ payload = kzalloc(sdev->ipc->max_payload_size, GFP_KERNEL); -+ if (!payload) -+ return -ENOMEM; -+ -+ /* Add sof_ipc4_glb_pipe_payload and set array bit to 1 */ -+ payload_hdr = (struct sof_ipc4_glb_pipe_payload *)payload; -+ payload_hdr->word0 |= SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_MASK; -+ ext_pos = DIV_ROUND_UP(sizeof(*payload_hdr), sizeof(u32)); -+ -+ sof_ipc4_widget_pipe_ext_obj_memory_data(sdev, swidget, payload, &ext_pos, &hdr); -+ /* Add following array objects here */ -+ -+ /* Mark end of object array */ -+ hdr->header |= SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_MASK; -+ -+ /* Put total payload size in words to the payload header */ -+ payload_hdr->word0 |= SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS(ext_pos); -+ *new_data = payload; -+ -+ /* Update msg extension bits according to the payload changes */ -+ msg->extension |= SOF_IPC4_GLB_PIPE_PAYLOAD_MASK; -+ -+ dev_dbg(sdev->dev, "%s: payload word0 %#x", swidget->widget->name, -+ payload_hdr->word0); -+ -+ return ext_pos * sizeof(int32_t); -+} -+ - static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) - { - struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; -@@ -3340,8 +3597,8 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget - swidget->widget->name, swidget->pipeline_id, module_id, - swidget->instance_id, swidget->core); - -- ret = sof_ipc4_widget_setup_msg_payload(sdev, swidget, msg, ipc_data, ipc_size, -- &ext_data); -+ ret = sof_ipc4_widget_mod_init_msg_payload(sdev, swidget, msg, ipc_data, ipc_size, -+ &ext_data); - if (ret < 0) - goto fail; - -@@ -3353,6 +3610,17 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget - dev_dbg(sdev->dev, "Create pipeline %s (pipe %d) - instance %d, core %d\n", - swidget->widget->name, swidget->pipeline_id, - swidget->instance_id, swidget->core); -+ -+ msg->extension &= ~SOF_IPC4_GLB_PIPE_PAYLOAD_MASK; -+ ret = sof_ipc4_widget_pipe_create_msg_payload(sdev, swidget, msg, -+ &ext_data); -+ if (ret < 0) -+ goto fail; -+ -+ if (ret > 0) { -+ ipc_size = ret; -+ ipc_data = ext_data; -+ } - } - - msg->data_size = ipc_size; -@@ -3410,6 +3678,8 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget - swidget->widget->name); - - pipeline->mem_usage = 0; -+ swidget->heap_bytes = 0; -+ swidget->stack_bytes = 0; - pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED; - ida_free(&pipeline_ida, swidget->instance_id); - swidget->instance_id = -EINVAL; -diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h ---- a/sound/soc/sof/ipc4-topology.h -+++ b/sound/soc/sof/ipc4-topology.h -@@ -167,7 +167,7 @@ struct sof_ipc4_pipeline { - }; - - /** -- * struct sof_ipc4_multi_pipeline_data - multi pipeline trigger IPC data -+ * struct ipc4_pipeline_set_state_data - multi pipeline trigger IPC data - * @count: Number of pipelines to be triggered - * @pipeline_instance_ids: Flexible array of IDs of the pipelines to be triggered - */ -@@ -197,12 +197,15 @@ struct sof_ipc4_pin_format { - * @input_pin_fmts: Available input pin formats - * @num_input_formats: Number of input pin formats - * @num_output_formats: Number of output pin formats -+ * @changed_params: Mask of changed params by the module instance between it's -+ * input and output formts (rate, channels, depth) - */ - struct sof_ipc4_available_audio_format { - struct sof_ipc4_pin_format *output_pin_fmts; - struct sof_ipc4_pin_format *input_pin_fmts; - u32 num_input_formats; - u32 num_output_formats; -+ u32 changed_params; - }; - - /** -@@ -210,7 +213,7 @@ struct sof_ipc4_available_audio_format { - * @node_id: ID of Gateway Node - * @dma_buffer_size: Preferred Gateway DMA buffer size (in bytes) - * @config_length: Length of gateway node configuration blob specified in #config_data -- * config_data: Gateway node configuration blob -+ * @config_data: Gateway node configuration blob - */ - struct sof_copier_gateway_cfg { - uint32_t node_id; -@@ -285,7 +288,9 @@ struct sof_ipc4_dma_stream_ch_map { - struct sof_ipc4_dma_config { - uint8_t dma_method; - uint8_t pre_allocated_by_host; -+ /* private: */ - uint16_t rsvd; -+ /* public: */ - uint32_t dma_channel_id; - uint32_t stream_id; - struct sof_ipc4_dma_stream_ch_map dma_stream_channel_map; -@@ -296,7 +301,7 @@ struct sof_ipc4_dma_config { - #define SOF_IPC4_GTW_DMA_CONFIG_ID 0x1000 - - /** -- * struct sof_ipc4_dma_config: DMA configuration -+ * struct sof_ipc4_dma_config_tlv - DMA configuration - * @type: set to SOF_IPC4_GTW_DMA_CONFIG_ID - * @length: sizeof(struct sof_ipc4_dma_config) + dma_config.dma_priv_config_size - * @dma_config: actual DMA configuration -@@ -321,6 +326,7 @@ struct sof_ipc4_alh_configuration_blob { - * @data: IPC copier data - * @copier_config: Copier + blob - * @ipc_config_size: Size of copier_config -+ * @ipc_config_data: Copier module config data - * @available_fmt: Available audio format - * @frame_fmt: frame format - * @msg: message structure for copier -diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c ---- a/sound/soc/sof/ipc4.c -+++ b/sound/soc/sof/ipc4.c -@@ -289,6 +289,89 @@ static void sof_ipc4_dump_payload(struct snd_sof_dev *sdev, - 16, 4, ipc_data, size, false); - } - -+static const char *sof_ipc4_resource_type_str(u32 type) -+{ -+ switch (type) { -+ case SOF_IPC4_MODULE_INSTANCE: -+ return "resource: MODULE_INSTANCE"; -+ case SOF_IPC4_PIPELINE: -+ return "resource: PIPELINE"; -+ case SOF_IPC4_GATEWAY: -+ return "resource: GATEWAY"; -+ case SOF_IPC4_EDF_TASK: -+ return "resource: EDF_TASK"; -+ case SOF_IPC4_INVALID_RESOURCE_TYPE: -+ return "Resource is invalid"; -+ default: -+ return "Unknown resource type"; -+ } -+} -+ -+static const char *sof_ipc4_resource_event_type_str(u32 event_type) -+{ -+ switch (event_type) { -+ case SOF_IPC4_MIXER_UNDERRUN_DETECTED: -+ return "event: MIXER_UNDERRUN_DETECTED"; -+ case SOF_IPC4_PROCESS_DATA_ERROR: -+ return "event: PROCESS_DATA_ERROR"; -+ case SOF_IPC4_GATEWAY_UNDERRUN_DETECTED: -+ return "event: GATEWAY_UNDERRUN_DETECTED"; -+ case SOF_IPC4_GATEWAY_OVERRUN_DETECTED: -+ return "event: GATEWAY_OVERRUN_DETECTED"; -+ default: -+ return "Unknown event type"; -+ } -+} -+ -+static void sof_ipc4_resource_event_handler(struct snd_sof_dev *sdev, -+ struct sof_ipc4_msg *ipc4_msg) -+{ -+ struct sof_ipc4_notify_resource_data *data = ipc4_msg->data_ptr; -+ -+ /* Print event details */ -+ switch (data->event_type) { -+ case SOF_IPC4_MIXER_UNDERRUN_DETECTED: -+ dev_dbg(sdev->dev, "%s (%u): eos %u, mixed %u, expected %u\n", -+ sof_ipc4_resource_event_type_str(data->event_type), -+ data->event_type, data->data.mixer_underrun.eos_flag, -+ data->data.mixer_underrun.data_mixed, -+ data->data.mixer_underrun.expected_data_mixed); -+ break; -+ case SOF_IPC4_PROCESS_DATA_ERROR: -+ dev_dbg(sdev->dev, "%s (%u): error_code %#x\n", -+ sof_ipc4_resource_event_type_str(data->event_type), -+ data->event_type, data->data.process_data_error.error_code); -+ break; -+ case SOF_IPC4_GATEWAY_UNDERRUN_DETECTED: -+ case SOF_IPC4_GATEWAY_OVERRUN_DETECTED: -+ dev_dbg(sdev->dev, "%s (%u)\n", -+ sof_ipc4_resource_event_type_str(data->event_type), -+ data->event_type); -+ break; -+ default: -+ dev_dbg(sdev->dev, "%s (%u): raw dws %#x %#x %#x %#x %#x %#x\n", -+ sof_ipc4_resource_event_type_str(data->event_type), -+ data->event_type, -+ data->data.dws[0], data->data.dws[1], data->data.dws[2], -+ data->data.dws[3], data->data.dws[4], data->data.dws[5]); -+ break; -+ } -+ -+ /* Print resource details */ -+ if (data->resource_type == SOF_IPC4_MODULE_INSTANCE) { -+ u32 module_id = SOF_IPC4_MOD_ID_GET(data->resource_id); -+ u32 instance_id = SOF_IPC4_MOD_INSTANCE_GET(data->resource_id); -+ -+ dev_dbg(sdev->dev, "%s (%u), module_id %u, instance_id %u\n", -+ sof_ipc4_resource_type_str(data->resource_type), -+ data->resource_type, module_id, instance_id); -+ } else if (data->resource_type != SOF_IPC4_INVALID_RESOURCE_TYPE) { -+ dev_dbg(sdev->dev, "%s (%u), id %u\n", -+ sof_ipc4_resource_type_str(data->resource_type), -+ data->resource_type, data->resource_id); -+ } -+} -+ - static int sof_ipc4_get_reply(struct snd_sof_dev *sdev) - { - struct snd_sof_ipc_msg *msg = sdev->msg; -@@ -734,6 +817,7 @@ static void sof_ipc4_rx_msg(struct snd_sof_dev *sdev) - break; - case SOF_IPC4_NOTIFY_RESOURCE_EVENT: - data_size = sizeof(struct sof_ipc4_notify_resource_data); -+ handler_func = sof_ipc4_resource_event_handler; - break; - case SOF_IPC4_NOTIFY_LOG_BUFFER_STATUS: - sof_ipc4_mtrace_update_pos(sdev, SOF_IPC4_LOG_CORE_GET(ipc4_msg->primary)); -diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c ---- a/sound/soc/sof/nocodec.c -+++ b/sound/soc/sof/nocodec.c -@@ -21,12 +21,13 @@ static struct snd_soc_card sof_nocodec_card = { - static int sof_nocodec_bes_setup(struct device *dev, - struct snd_soc_dai_driver *drv, - struct snd_soc_dai_link *links, -- int link_num, struct snd_soc_card *card) -+ int link_num) - { -+ struct snd_soc_card *card = &sof_nocodec_card; - struct snd_soc_dai_link_component *dlc; - int i; - -- if (!drv || !links || !card) -+ if (!drv || !links) - return -EINVAL; - - /* set up BE dai_links */ -@@ -67,10 +68,10 @@ static int sof_nocodec_bes_setup(struct device *dev, - return 0; - } - --static int sof_nocodec_setup(struct device *dev, -- u32 num_dai_drivers, -- struct snd_soc_dai_driver *dai_drivers) -+static int sof_nocodec_setup(struct device *dev, struct snd_soc_acpi_mach *mach) - { -+ u32 num_dai_drivers = mach->mach_params.num_dai_drivers; -+ struct snd_soc_dai_driver *dai_drivers = mach->mach_params.dai_drivers; - struct snd_soc_dai_link *links; - - /* create dummy BE dai_links */ -@@ -78,7 +79,7 @@ static int sof_nocodec_setup(struct device *dev, - if (!links) - return -ENOMEM; - -- return sof_nocodec_bes_setup(dev, dai_drivers, links, num_dai_drivers, &sof_nocodec_card); -+ return sof_nocodec_bes_setup(dev, dai_drivers, links, num_dai_drivers); - } - - static int sof_nocodec_probe(struct platform_device *pdev) -@@ -92,8 +93,7 @@ static int sof_nocodec_probe(struct platform_device *pdev) - - snd_soc_card_set_topology_name(card, "sof"); - -- ret = sof_nocodec_setup(card->dev, mach->mach_params.num_dai_drivers, -- mach->mach_params.dai_drivers); -+ ret = sof_nocodec_setup(card->dev, mach); - if (ret < 0) - return ret; - -diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c ---- a/sound/soc/sof/pcm.c -+++ b/sound/soc/sof/pcm.c -@@ -62,7 +62,7 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream) - * To avoid sending IPC before the previous IPC is handled, we - * schedule delayed work here to call the snd_pcm_period_elapsed(). - */ -- schedule_work(&spcm->stream[substream->stream].period_elapsed_work); -+ queue_work(system_highpri_wq, &spcm->stream[substream->stream].period_elapsed_work); - } - EXPORT_SYMBOL(snd_sof_pcm_period_elapsed); - -diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c ---- a/sound/soc/sof/sof-audio.c -+++ b/sound/soc/sof/sof-audio.c -@@ -501,9 +501,8 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget - !sof_widget_in_same_direction(swidget, dir)) - return 0; - -- /* skip widgets already prepared or aggregated DAI widgets*/ -- if (!widget_ops[widget->id].ipc_prepare || swidget->prepared || -- is_aggregated_dai(swidget)) -+ /* skip widgets aggregated DAI widgets */ -+ if (!widget_ops[widget->id].ipc_prepare || is_aggregated_dai(swidget)) - goto sink_prepare; - - /* prepare the source widget */ -diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h ---- a/sound/soc/sof/sof-audio.h -+++ b/sound/soc/sof/sof-audio.h -@@ -459,10 +459,10 @@ struct snd_sof_widget { - /* Scheduling domain (enum sof_comp_domain), unset, Low Latency, or Data Processing */ - u32 comp_domain; - -- /* The values below are added to mod_init pay load if comp_domain indicates DP component */ -- u32 dp_domain_id; /* DP process userspace domain ID */ -- u32 dp_stack_bytes; /* DP process stack size requirement in bytes */ -- u32 dp_heap_bytes; /* DP process heap size requirement in bytes */ -+ /* Module instance's memory configuration. */ -+ u32 domain_id; /* Module instance's userspace domain ID */ -+ u32 stack_bytes; /* Module instance's stack size requirement */ -+ u32 heap_bytes; /* Module instance's heap size requirement */ - - struct snd_soc_dapm_widget *widget; - struct list_head list; /* list in sdev widget list */ -diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c ---- a/sound/soc/sof/topology.c -+++ b/sound/soc/sof/topology.c -@@ -309,7 +309,7 @@ static const struct sof_dai_types sof_dais[] = { - {"ACPHS_VIRTUAL", SOF_DAI_AMD_HS_VIRTUAL}, - {"MICFIL", SOF_DAI_IMX_MICFIL}, - {"ACP_SDW", SOF_DAI_AMD_SDW}, -- -+ {"ACPTDM", SOF_DAI_AMD_I2S}, - }; - - static enum sof_ipc_dai_type find_dai(const char *name) -@@ -846,6 +846,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, - struct snd_soc_tplg_mixer_control *mc = - container_of(hdr, struct snd_soc_tplg_mixer_control, hdr); - int tlv[SOF_TLV_ITEMS]; -+ u32 min, max; - unsigned int mask; - int ret; - -@@ -853,6 +854,11 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, - if (le32_to_cpu(mc->num_channels) > SND_SOC_TPLG_MAX_CHAN) - return -EINVAL; - -+ min = le32_to_cpu(mc->min); -+ max = le32_to_cpu(mc->max); -+ if (min > max || max >= INT_MAX) -+ return -EINVAL; -+ - /* - * If control has more than 2 channels we need to override the info. This is because even if - * ASoC layer has defined topology's max channel count to SND_SOC_TPLG_MAX_CHAN = 8, the -@@ -863,12 +869,12 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, - kc->info = snd_sof_volume_info; - - scontrol->comp_id = sdev->next_comp_id; -- scontrol->min_volume_step = le32_to_cpu(mc->min); -- scontrol->max_volume_step = le32_to_cpu(mc->max); -+ scontrol->min_volume_step = min; -+ scontrol->max_volume_step = max; - scontrol->num_channels = le32_to_cpu(mc->num_channels); - -- scontrol->max = le32_to_cpu(mc->max); -- if (le32_to_cpu(mc->max) == 1) -+ scontrol->max = max; -+ if (max == 1) - goto skip; - - /* extract tlv data */ -@@ -878,7 +884,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, - } - - /* set up volume table */ -- ret = set_up_volume_table(scontrol, tlv, le32_to_cpu(mc->max) + 1); -+ ret = set_up_volume_table(scontrol, tlv, max + 1); - if (ret < 0) { - dev_err(scomp->dev, "error: setting up volume table\n"); - return ret; -@@ -911,7 +917,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, - return 0; - - err: -- if (le32_to_cpu(mc->max) > 1) -+ if (max > 1) - kfree(scontrol->volume_table); - - return ret; -@@ -1102,7 +1108,7 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, - - full = NULL; - partial = NULL; -- list_for_each_entry(rtd, &card->rtd_list, list) { -+ for_each_card_rtds(card, rtd) { - /* does stream match DAI link ? */ - if (rtd->dai_link->stream_name) { - if (!strcmp(rtd->dai_link->stream_name, w->sname)) { -@@ -1167,7 +1173,7 @@ static void sof_disconnect_dai_widget(struct snd_soc_component *scomp, - else - return; - -- list_for_each_entry(rtd, &card->rtd_list, list) { -+ for_each_card_rtds(card, rtd) { - /* does stream match DAI link ? */ - if (!rtd->dai_link->stream_name || - !strstr(rtd->dai_link->stream_name, sname)) -@@ -1943,8 +1949,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ - private->array, le32_to_cpu(private->size)); - if (ret < 0) { - dev_err(scomp->dev, "Failed tp parse common DAI link tokens\n"); -- kfree(slink); -- return ret; -+ goto free_slink; - } - - token_list = tplg_ops ? tplg_ops->token_list : NULL; -@@ -1995,6 +2000,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ - case SOF_DAI_AMD_HS: - case SOF_DAI_AMD_SP_VIRTUAL: - case SOF_DAI_AMD_HS_VIRTUAL: -+ case SOF_DAI_AMD_I2S: - token_id = SOF_ACPI2S_TOKENS; - num_tuples += token_list[SOF_ACPI2S_TOKENS].count; - break; -@@ -2013,8 +2019,8 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ - /* allocate memory for tuples array */ - slink->tuples = kzalloc_objs(*slink->tuples, num_tuples); - if (!slink->tuples) { -- kfree(slink); -- return -ENOMEM; -+ ret = -ENOMEM; -+ goto free_slink; - } - - if (token_list[SOF_DAI_LINK_TOKENS].tokens) { -@@ -2070,6 +2076,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ - - err: - kfree(slink->tuples); -+free_slink: - kfree(slink); - - return ret; -@@ -2505,13 +2512,12 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_sof_pdata *sof_pdata = sdev->pdata; - const char *tplg_filename_prefix = sof_pdata->tplg_filename_prefix; -- const struct firmware *fw; -- const char **tplg_files; - int tplg_cnt = 0; - int ret; - int i; - -- tplg_files = kcalloc(scomp->card->num_links, sizeof(char *), GFP_KERNEL); -+ const char **tplg_files __free(kfree) = -+ kcalloc(scomp->card->num_links, sizeof(char *), GFP_KERNEL); - if (!tplg_files) - return -ENOMEM; - -@@ -2537,10 +2543,8 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) - tplg_filename_prefix, - &tplg_files, - no_fallback); -- if (tplg_cnt < 0) { -- kfree(tplg_files); -+ if (tplg_cnt < 0) - return tplg_cnt; -- } - } - - /* -@@ -2551,8 +2555,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) - if (strstr(file, "dummy")) { - dev_err(scomp->dev, - "Function topology is required, please upgrade sof-firmware\n"); -- -- kfree(tplg_files); - return -EINVAL; - } - tplg_files[0] = file; -@@ -2567,6 +2569,7 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) - if (tplg_files[0] != file) - dev_info(scomp->dev, "loading topology %d: %s\n", i, tplg_files[i]); - -+ const struct firmware *fw __free(firmware) = NULL; - ret = request_firmware(&fw, tplg_files[i], scomp->dev); - if (ret < 0) { - /* -@@ -2585,8 +2588,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) - else - ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw); - -- release_firmware(fw); -- - if (ret < 0) { - dev_err(scomp->dev, "tplg %s component load failed %d\n", - tplg_files[i], ret); -@@ -2605,6 +2606,8 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) - goto out; - } - dev_info(scomp->dev, "loading feature topology %d: %s\n", i, feature_topology); -+ -+ const struct firmware *fw __free(firmware) = NULL; - ret = request_firmware(&fw, feature_topology, scomp->dev); - if (ret < 0) { - /* -@@ -2629,8 +2632,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) - else - ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw); - -- release_firmware(fw); -- - if (ret < 0) { - dev_err(scomp->dev, "feature tplg %s component load failed %d\n", - feature_topologies[i], ret); -@@ -2649,8 +2650,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) - if (ret >= 0 && sdev->led_present) - ret = snd_ctl_led_request(); - -- kfree(tplg_files); -- - return ret; - } - EXPORT_SYMBOL(snd_sof_load_topology); -diff --git a/sound/soc/sophgo/cv1800b-tdm.c b/sound/soc/sophgo/cv1800b-tdm.c ---- a/sound/soc/sophgo/cv1800b-tdm.c -+++ b/sound/soc/sophgo/cv1800b-tdm.c -@@ -558,7 +558,7 @@ static const struct snd_soc_dai_ops cv1800b_i2s_dai_ops = { - .set_sysclk = cv1800b_i2s_dai_set_sysclk, - }; - --static const struct snd_soc_dai_driver cv1800b_i2s_dai_template = { -+static struct snd_soc_dai_driver cv1800b_i2s_dai_template = { - .name = "cv1800b-i2s", - .playback = { - .stream_name = "Playback", -@@ -636,7 +636,6 @@ static int cv1800b_i2s_probe(struct platform_device *pdev) - struct cv1800b_i2s *i2s; - struct resource *res; - void __iomem *regs; -- struct snd_soc_dai_driver *dai; - int ret; - - i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL); -@@ -666,21 +665,14 @@ static int cv1800b_i2s_probe(struct platform_device *pdev) - platform_set_drvdata(pdev, i2s); - cv1800b_i2s_setup_tdm(i2s); - -- dai = devm_kmemdup(dev, &cv1800b_i2s_dai_template, sizeof(*dai), -- GFP_KERNEL); -- if (!dai) -- return -ENOMEM; -- -- ret = devm_snd_soc_register_component(dev, &cv1800b_i2s_component, dai, -- 1); -+ ret = devm_snd_soc_register_component(dev, &cv1800b_i2s_component, -+ &cv1800b_i2s_dai_template, 1); - if (ret) - return ret; - - ret = devm_snd_dmaengine_pcm_register(dev, &cv1800b_i2s_pcm_config, 0); -- if (ret) { -- dev_err(dev, "dmaengine_pcm_register failed: %d\n", ret); -+ if (ret) - return ret; -- } - - return 0; - } -diff --git a/sound/soc/spacemit/k1_i2s.c b/sound/soc/spacemit/k1_i2s.c ---- a/sound/soc/spacemit/k1_i2s.c -+++ b/sound/soc/spacemit/k1_i2s.c -@@ -52,7 +52,7 @@ struct spacemit_i2s_dev { - - struct clk *sysclk; - struct clk *bclk; -- struct clk *sspa_clk; -+ struct clk *func_clk; - struct clk *sysclk_div; - struct clk *c_sysclk; - struct clk *c_bclk; -@@ -221,7 +221,7 @@ static int spacemit_i2s_hw_params(struct snd_pcm_substream *substream, - if (ret) - return ret; - -- return clk_set_rate(i2s->sspa_clk, bclk_rate); -+ return clk_set_rate(i2s->func_clk, bclk_rate); - } - - static int spacemit_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, -@@ -335,6 +335,11 @@ static int spacemit_i2s_dai_remove(struct snd_soc_dai *dai) - return 0; - } - -+static const u64 spacemit_i2s_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_B; -+ - static const struct snd_soc_dai_ops spacemit_i2s_dai_ops = { - .probe = spacemit_i2s_dai_probe, - .remove = spacemit_i2s_dai_remove, -@@ -343,26 +348,12 @@ static const struct snd_soc_dai_ops spacemit_i2s_dai_ops = { - .set_sysclk = spacemit_i2s_set_sysclk, - .set_fmt = spacemit_i2s_set_fmt, - .trigger = spacemit_i2s_trigger, -+ .auto_selectable_formats = &spacemit_i2s_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static struct snd_soc_dai_driver spacemit_i2s_dai = { - .ops = &spacemit_i2s_dai_ops, -- .playback = { -- .channels_min = 1, -- .channels_max = 2, -- .rates = SPACEMIT_PCM_RATES, -- .rate_min = SNDRV_PCM_RATE_8000, -- .rate_max = SNDRV_PCM_RATE_48000, -- .formats = SPACEMIT_PCM_FORMATS, -- }, -- .capture = { -- .channels_min = 1, -- .channels_max = 2, -- .rates = SPACEMIT_PCM_RATES, -- .rate_min = SNDRV_PCM_RATE_8000, -- .rate_max = SNDRV_PCM_RATE_48000, -- .formats = SPACEMIT_PCM_FORMATS, -- }, - .symmetric_rate = 1, - }; - -@@ -392,6 +383,8 @@ static int spacemit_i2s_init_dai(struct spacemit_i2s_dev *i2s, - dai->playback.channels_min = 1; - dai->playback.channels_max = 2; - dai->playback.rates = SPACEMIT_PCM_RATES; -+ dai->playback.rate_min = SNDRV_PCM_RATE_8000; -+ dai->playback.rate_max = SNDRV_PCM_RATE_48000; - dai->playback.formats = SPACEMIT_PCM_FORMATS; - - i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; -@@ -404,6 +397,8 @@ static int spacemit_i2s_init_dai(struct spacemit_i2s_dev *i2s, - dai->capture.channels_min = 1; - dai->capture.channels_max = 2; - dai->capture.rates = SPACEMIT_PCM_RATES; -+ dai->capture.rate_min = SNDRV_PCM_RATE_8000; -+ dai->capture.rate_max = SNDRV_PCM_RATE_48000; - dai->capture.formats = SPACEMIT_PCM_FORMATS; - - i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; -@@ -445,14 +440,14 @@ static int spacemit_i2s_probe(struct platform_device *pdev) - if (IS_ERR(i2s->bclk)) - return dev_err_probe(i2s->dev, PTR_ERR(i2s->bclk), "failed to enable bit clock\n"); - -- clk = devm_clk_get_enabled(i2s->dev, "sspa_bus"); -+ clk = devm_clk_get_enabled(i2s->dev, "bus"); - if (IS_ERR(clk)) -- return dev_err_probe(i2s->dev, PTR_ERR(clk), "failed to enable sspa_bus clock\n"); -+ return dev_err_probe(i2s->dev, PTR_ERR(clk), "failed to enable bus clock\n"); - -- i2s->sspa_clk = devm_clk_get_enabled(i2s->dev, "sspa"); -- if (IS_ERR(i2s->sspa_clk)) -- return dev_err_probe(i2s->dev, PTR_ERR(i2s->sspa_clk), -- "failed to enable sspa clock\n"); -+ i2s->func_clk = devm_clk_get_enabled(i2s->dev, "func"); -+ if (IS_ERR(i2s->func_clk)) -+ return dev_err_probe(i2s->dev, PTR_ERR(i2s->func_clk), -+ "failed to enable func clock\n"); - - i2s->sysclk_div = devm_clk_get_optional_enabled(i2s->dev, "sysclk_div"); - if (IS_ERR(i2s->sysclk_div)) -@@ -471,7 +466,7 @@ static int spacemit_i2s_probe(struct platform_device *pdev) - - i2s->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(i2s->base)) -- return dev_err_probe(i2s->dev, PTR_ERR(i2s->base), "failed to map registers\n"); -+ return PTR_ERR(i2s->base); - - i2s->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL); - if (IS_ERR(i2s->reset)) -@@ -488,7 +483,7 @@ static int spacemit_i2s_probe(struct platform_device *pdev) - &spacemit_i2s_component, - dai, 1); - if (ret) -- return dev_err_probe(i2s->dev, ret, "failed to register component"); -+ return ret; - - return devm_snd_dmaengine_pcm_register(&pdev->dev, &spacemit_dmaengine_pcm_config, 0); - } -diff --git a/sound/soc/spear/spdif_in.c b/sound/soc/spear/spdif_in.c ---- a/sound/soc/spear/spdif_in.c -+++ b/sound/soc/spear/spdif_in.c -@@ -55,7 +55,7 @@ static int spdif_in_dai_probe(struct snd_soc_dai *dai) - struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai); - - host->dma_params_rx.filter_data = &host->dma_params; -- dai->capture_dma_data = &host->dma_params_rx; -+ snd_soc_dai_dma_data_set_capture(dai, &host->dma_params_rx); - - return 0; - } -@@ -150,12 +150,12 @@ static int spdif_in_trigger(struct snd_pcm_substream *substream, int cmd, - - static const struct snd_soc_dai_ops spdif_in_dai_ops = { - .shutdown = spdif_in_shutdown, -+ .probe = spdif_in_dai_probe, - .trigger = spdif_in_trigger, - .hw_params = spdif_in_hw_params, - }; - - static struct snd_soc_dai_driver spdif_in_dai = { -- .probe = spdif_in_dai_probe, - .capture = { - .channels_min = 2, - .channels_max = 2, -@@ -218,10 +218,8 @@ static int spdif_in_probe(struct platform_device *pdev) - - host->io_base = io_base; - host->irq = platform_get_irq(pdev, 0); -- if (host->irq < 0) { -- dev_warn(&pdev->dev, "failed to get IRQ: %d\n", host->irq); -+ if (host->irq < 0) - return host->irq; -- } - - host->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(host->clk)) -@@ -243,10 +241,8 @@ static int spdif_in_probe(struct platform_device *pdev) - - ret = devm_request_irq(&pdev->dev, host->irq, spdif_in_irq, 0, - "spdif-in", host); -- if (ret) { -- dev_warn(&pdev->dev, "request_irq failed\n"); -+ if (ret) - return ret; -- } - - ret = devm_snd_soc_register_component(&pdev->dev, &spdif_in_component, - &spdif_in_dai, 1); -diff --git a/sound/soc/spear/spdif_out.c b/sound/soc/spear/spdif_out.c ---- a/sound/soc/spear/spdif_out.c -+++ b/sound/soc/spear/spdif_out.c -@@ -249,6 +249,7 @@ static int spdif_soc_dai_probe(struct snd_soc_dai *dai) - } - - static const struct snd_soc_dai_ops spdif_out_dai_ops = { -+ .probe = spdif_soc_dai_probe, - .mute_stream = spdif_mute, - .startup = spdif_out_startup, - .shutdown = spdif_out_shutdown, -@@ -266,7 +267,6 @@ static struct snd_soc_dai_driver spdif_out_dai = { - SNDRV_PCM_RATE_192000), - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, -- .probe = spdif_soc_dai_probe, - .ops = &spdif_out_dai_ops, - }; - -diff --git a/sound/soc/sprd/sprd-mcdt.c b/sound/soc/sprd/sprd-mcdt.c ---- a/sound/soc/sprd/sprd-mcdt.c -+++ b/sound/soc/sprd/sprd-mcdt.c -@@ -931,10 +931,8 @@ static int sprd_mcdt_probe(struct platform_device *pdev) - - ret = devm_request_irq(&pdev->dev, irq, sprd_mcdt_irq_handler, - 0, "sprd-mcdt", mcdt); -- if (ret) { -- dev_err(&pdev->dev, "Failed to request MCDT IRQ\n"); -+ if (ret) - return ret; -- } - - sprd_mcdt_init_chans(mcdt, res); - -diff --git a/sound/soc/sprd/sprd-pcm-dma.c b/sound/soc/sprd/sprd-pcm-dma.c ---- a/sound/soc/sprd/sprd-pcm-dma.c -+++ b/sound/soc/sprd/sprd-pcm-dma.c -@@ -469,8 +469,6 @@ static int sprd_soc_platform_probe(struct platform_device *pdev) - - ret = devm_snd_soc_register_component(&pdev->dev, &sprd_soc_component, - NULL, 0); -- if (ret) -- dev_err(&pdev->dev, "could not register platform:%d\n", ret); - - return ret; - } -diff --git a/sound/soc/starfive/jh7110_pwmdac.c b/sound/soc/starfive/jh7110_pwmdac.c ---- a/sound/soc/starfive/jh7110_pwmdac.c -+++ b/sound/soc/starfive/jh7110_pwmdac.c -@@ -477,25 +477,22 @@ static int jh7110_pwmdac_probe(struct platform_device *pdev) - &jh7110_pwmdac_component, - &jh7110_pwmdac_dai, 1); - if (ret) -- return dev_err_probe(&pdev->dev, ret, "failed to register dai\n"); -+ return ret; - - ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); - if (ret) -- return dev_err_probe(&pdev->dev, ret, "failed to register pcm\n"); -+ return ret; - - pm_runtime_enable(dev->dev); - if (!pm_runtime_enabled(&pdev->dev)) { - ret = jh7110_pwmdac_runtime_resume(&pdev->dev); -- if (ret) -- goto err_pm_disable; -+ if (ret) { -+ pm_runtime_disable(&pdev->dev); -+ return ret; -+ } - } - - return 0; -- --err_pm_disable: -- pm_runtime_disable(&pdev->dev); -- -- return ret; - } - - static void jh7110_pwmdac_remove(struct platform_device *pdev) -diff --git a/sound/soc/starfive/jh7110_tdm.c b/sound/soc/starfive/jh7110_tdm.c ---- a/sound/soc/starfive/jh7110_tdm.c -+++ b/sound/soc/starfive/jh7110_tdm.c -@@ -559,10 +559,8 @@ static int jh7110_tdm_clk_reset_get(struct platform_device *pdev, - tdm->clks[5].id = "tdm"; - - ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(tdm->clks), tdm->clks); -- if (ret) { -- dev_err(&pdev->dev, "Failed to get tdm clocks\n"); -+ if (ret) - return ret; -- } - - tdm->resets = devm_reset_control_array_get_exclusive(&pdev->dev); - if (IS_ERR(tdm->resets)) { -@@ -589,42 +587,33 @@ static int jh7110_tdm_probe(struct platform_device *pdev) - tdm->dev = &pdev->dev; - - ret = jh7110_tdm_clk_reset_get(pdev, tdm); -- if (ret) { -- dev_err(&pdev->dev, "Failed to enable audio-tdm clock\n"); -+ if (ret) - return ret; -- } - - jh7110_tdm_init_params(tdm); - - dev_set_drvdata(&pdev->dev, tdm); - ret = devm_snd_soc_register_component(&pdev->dev, &jh7110_tdm_component, - &jh7110_tdm_dai, 1); -- if (ret) { -- dev_err(&pdev->dev, "Failed to register dai\n"); -+ if (ret) - return ret; -- } - - ret = devm_snd_dmaengine_pcm_register(&pdev->dev, - &jh7110_dmaengine_pcm_config, - SND_DMAENGINE_PCM_FLAG_COMPAT); -- if (ret) { -- dev_err(&pdev->dev, "Could not register pcm: %d\n", ret); -+ if (ret) - return ret; -- } - - pm_runtime_enable(&pdev->dev); - if (!pm_runtime_enabled(&pdev->dev)) { - ret = jh7110_tdm_runtime_resume(&pdev->dev); -- if (ret) -- goto err_pm_disable; -+ if (ret) { -+ pm_runtime_disable(&pdev->dev); -+ return ret; -+ } - } - - return 0; -- --err_pm_disable: -- pm_runtime_disable(&pdev->dev); -- -- return ret; - } - - static void jh7110_tdm_dev_remove(struct platform_device *pdev) -diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c ---- a/sound/soc/stm/stm32_adfsdm.c -+++ b/sound/soc/stm/stm32_adfsdm.c -@@ -360,11 +360,8 @@ static int stm32_adfsdm_probe(struct platform_device *pdev) - ret = devm_snd_soc_register_component(&pdev->dev, - &stm32_adfsdm_soc_platform, - NULL, 0); -- if (ret < 0) { -- dev_err(&pdev->dev, "%s: Failed to register PCM platform\n", -- __func__); -+ if (ret < 0) - return ret; -- } - - pm_runtime_enable(&pdev->dev); - -diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c ---- a/sound/soc/stm/stm32_i2s.c -+++ b/sound/soc/stm/stm32_i2s.c -@@ -1238,10 +1238,8 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev, - - ret = devm_request_irq(&pdev->dev, irq, stm32_i2s_isr, 0, - dev_name(&pdev->dev), i2s); -- if (ret) { -- dev_err(&pdev->dev, "irq request returned %d\n", ret); -+ if (ret) - return ret; -- } - - /* Reset */ - rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); -@@ -1295,7 +1293,7 @@ static int stm32_i2s_probe(struct platform_device *pdev) - - ret = snd_dmaengine_pcm_register(&pdev->dev, &stm32_i2s_pcm_config, 0); - if (ret) -- return dev_err_probe(&pdev->dev, ret, "PCM DMA register error\n"); -+ return ret; - - ret = snd_soc_register_component(&pdev->dev, &stm32_i2s_component, - i2s->dai_drv, 1); -diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c ---- a/sound/soc/stm/stm32_sai_sub.c -+++ b/sound/soc/stm/stm32_sai_sub.c -@@ -1696,19 +1696,15 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) - - ret = devm_request_irq(&pdev->dev, sai->pdata->irq, stm32_sai_isr, - IRQF_SHARED, dev_name(&pdev->dev), sai); -- if (ret) { -- dev_err(&pdev->dev, "IRQ request returned %d\n", ret); -+ if (ret) - goto err_unprepare_pclk; -- } - - if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) - conf = &stm32_sai_pcm_config_spdif; - - ret = snd_dmaengine_pcm_register(&pdev->dev, conf, 0); -- if (ret) { -- ret = dev_err_probe(&pdev->dev, ret, "Could not register pcm dma\n"); -+ if (ret) - goto err_unprepare_pclk; -- } - - ret = snd_soc_register_component(&pdev->dev, &stm32_component, - &sai->cpu_dai_drv, 1); -diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c ---- a/sound/soc/stm/stm32_spdifrx.c -+++ b/sound/soc/stm/stm32_spdifrx.c -@@ -970,10 +970,8 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) - - ret = devm_request_irq(&pdev->dev, spdifrx->irq, stm32_spdifrx_isr, 0, - dev_name(&pdev->dev), spdifrx); -- if (ret) { -- dev_err(&pdev->dev, "IRQ request returned %d\n", ret); -+ if (ret) - return ret; -- } - - rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); - if (IS_ERR(rst)) -@@ -987,7 +985,7 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) - pcm_config = &stm32_spdifrx_pcm_config; - ret = snd_dmaengine_pcm_register(&pdev->dev, pcm_config, 0); - if (ret) -- return dev_err_probe(&pdev->dev, ret, "PCM DMA register error\n"); -+ return ret; - - ret = snd_soc_register_component(&pdev->dev, - &stm32_spdifrx_component, -diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c ---- a/sound/soc/sunxi/sun4i-codec.c -+++ b/sound/soc/sunxi/sun4i-codec.c -@@ -22,13 +22,13 @@ - #include - - #include -+#include -+#include - #include - #include - #include - #include - #include --#include --#include - - /* Codec DAC digital controls and FIFO registers */ - #define SUN4I_CODEC_DAC_DPC (0x00) -@@ -2317,67 +2317,54 @@ static int sun4i_codec_probe(struct platform_device *pdev) - - scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base, - quirks->regmap_config); -- if (IS_ERR(scodec->regmap)) { -- dev_err(&pdev->dev, "Failed to create our regmap\n"); -- return PTR_ERR(scodec->regmap); -- } -+ if (IS_ERR(scodec->regmap)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->regmap), -+ "Failed to create our regmap\n"); - - /* Get the clocks from the DT */ - scodec->clk_apb = devm_clk_get_enabled(&pdev->dev, "apb"); -- if (IS_ERR(scodec->clk_apb)) { -- dev_err(&pdev->dev, "Failed to get the APB clock\n"); -- return PTR_ERR(scodec->clk_apb); -- } -+ if (IS_ERR(scodec->clk_apb)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->clk_apb), -+ "Failed to get the APB clock\n"); - - scodec->clk_module = devm_clk_get(&pdev->dev, "codec"); -- if (IS_ERR(scodec->clk_module)) { -- dev_err(&pdev->dev, "Failed to get the module clock\n"); -- return PTR_ERR(scodec->clk_module); -- } -+ if (IS_ERR(scodec->clk_module)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->clk_module), -+ "Failed to get the module clock\n"); - - if (quirks->has_reset) { - scodec->rst = devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL); -- if (IS_ERR(scodec->rst)) { -- dev_err(&pdev->dev, "Failed to get reset control\n"); -- return PTR_ERR(scodec->rst); -- } -+ if (IS_ERR(scodec->rst)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->rst), -+ "Failed to get reset control\n"); - } - - scodec->gpio_pa = devm_gpiod_get_optional(&pdev->dev, "allwinner,pa", - GPIOD_OUT_LOW); -- if (IS_ERR(scodec->gpio_pa)) { -- ret = PTR_ERR(scodec->gpio_pa); -- dev_err_probe(&pdev->dev, ret, "Failed to get pa gpio\n"); -- return ret; -- } -+ if (IS_ERR(scodec->gpio_pa)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->gpio_pa), -+ "Failed to get pa gpio\n"); -+ - - scodec->gpio_hp = devm_gpiod_get_optional(&pdev->dev, "hp-det", GPIOD_IN); -- if (IS_ERR(scodec->gpio_hp)) { -- ret = PTR_ERR(scodec->gpio_hp); -- dev_err_probe(&pdev->dev, ret, "Failed to get hp-det gpio\n"); -- return ret; -- } -+ if (IS_ERR(scodec->gpio_hp)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->gpio_hp), -+ "Failed to get hp-det gpio\n"); - - /* reg_field setup */ - scodec->reg_adc_fifoc = devm_regmap_field_alloc(&pdev->dev, - scodec->regmap, - quirks->reg_adc_fifoc); -- if (IS_ERR(scodec->reg_adc_fifoc)) { -- ret = PTR_ERR(scodec->reg_adc_fifoc); -- dev_err(&pdev->dev, "Failed to create regmap fields: %d\n", -- ret); -- return ret; -- } -+ if (IS_ERR(scodec->reg_adc_fifoc)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->reg_adc_fifoc), -+ "Failed to create regmap fields\n"); - - scodec->reg_dac_fifoc = devm_regmap_field_alloc(&pdev->dev, - scodec->regmap, - quirks->reg_dac_fifoc); -- if (IS_ERR(scodec->reg_dac_fifoc)) { -- ret = PTR_ERR(scodec->reg_dac_fifoc); -- dev_err(&pdev->dev, "Failed to create regmap fields: %d\n", -- ret); -- return ret; -- } -+ if (IS_ERR(scodec->reg_dac_fifoc)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->reg_dac_fifoc), -+ "Failed to create regmap fields\n"); - - /* DMA configuration for TX FIFO */ - scodec->playback_dma_data.addr = res->start + quirks->reg_dac_txdata; -@@ -2394,39 +2381,29 @@ static int sun4i_codec_probe(struct platform_device *pdev) - - ret = devm_snd_soc_register_component(&pdev->dev, quirks->codec, - &sun4i_codec_dai, 1); -- if (ret) { -- dev_err(&pdev->dev, "Failed to register our codec\n"); -+ if (ret) - return ret; -- } - - ret = devm_snd_soc_register_component(&pdev->dev, - &sun4i_codec_component, - &dummy_cpu_dai, 1); -- if (ret) { -- dev_err(&pdev->dev, "Failed to register our DAI\n"); -+ if (ret) - return ret; -- } - - ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); -- if (ret) { -- dev_err(&pdev->dev, "Failed to register against DMAEngine\n"); -+ if (ret) - return ret; -- } - - card = quirks->create_card(&pdev->dev); -- if (IS_ERR(card)) { -- ret = PTR_ERR(card); -- dev_err(&pdev->dev, "Failed to create our card\n"); -- return ret; -- } -+ if (IS_ERR(card)) -+ return PTR_ERR(card); - - snd_soc_card_set_drvdata(card, scodec); - - ret = snd_soc_register_card(card); -- if (ret) { -- dev_err_probe(&pdev->dev, ret, "Failed to register our card\n"); -- return ret; -- } -+ if (ret) -+ return dev_err_probe(&pdev->dev, ret, -+ "Failed to register our card\n"); - - return 0; - } -diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c ---- a/sound/soc/sunxi/sun4i-i2s.c -+++ b/sound/soc/sunxi/sun4i-i2s.c -@@ -1550,30 +1550,26 @@ static int sun4i_i2s_probe(struct platform_device *pdev) - } - - i2s->bus_clk = devm_clk_get(&pdev->dev, "apb"); -- if (IS_ERR(i2s->bus_clk)) { -- dev_err(&pdev->dev, "Can't get our bus clock\n"); -- return PTR_ERR(i2s->bus_clk); -- } -+ if (IS_ERR(i2s->bus_clk)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->bus_clk), -+ "Can't get our bus clock\n"); - - i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs, - i2s->variant->sun4i_i2s_regmap); -- if (IS_ERR(i2s->regmap)) { -- dev_err(&pdev->dev, "Regmap initialisation failed\n"); -- return PTR_ERR(i2s->regmap); -- } -+ if (IS_ERR(i2s->regmap)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->regmap), -+ "Regmap initialisation failed\n"); - - i2s->mod_clk = devm_clk_get(&pdev->dev, "mod"); -- if (IS_ERR(i2s->mod_clk)) { -- dev_err(&pdev->dev, "Can't get our mod clock\n"); -- return PTR_ERR(i2s->mod_clk); -- } -+ if (IS_ERR(i2s->mod_clk)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->mod_clk), -+ "Can't get our mod clock\n"); - - if (i2s->variant->has_reset) { - i2s->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); -- if (IS_ERR(i2s->rst)) { -- dev_err(&pdev->dev, "Failed to get reset control\n"); -- return PTR_ERR(i2s->rst); -- } -+ if (IS_ERR(i2s->rst)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->rst), -+ "Failed to get reset control\n"); - } - - if (!IS_ERR(i2s->rst)) { -diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c ---- a/sound/soc/sunxi/sun4i-spdif.c -+++ b/sound/soc/sunxi/sun4i-spdif.c -@@ -684,26 +684,23 @@ static int sun4i_spdif_probe(struct platform_device *pdev) - - host->regmap = devm_regmap_init_mmio(&pdev->dev, base, - &sun4i_spdif_regmap_config); -- if (IS_ERR(host->regmap)) { -- dev_err(&pdev->dev, "failed to initialise regmap.\n"); -- return PTR_ERR(host->regmap); -- } -+ if (IS_ERR(host->regmap)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(host->regmap), -+ "failed to initialise regmap.\n"); - - /* Clocks */ - host->apb_clk = devm_clk_get(&pdev->dev, "apb"); -- if (IS_ERR(host->apb_clk)) { -- dev_err(&pdev->dev, "failed to get a apb clock.\n"); -- return PTR_ERR(host->apb_clk); -- } -+ if (IS_ERR(host->apb_clk)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(host->apb_clk), -+ "failed to get a apb clock.\n"); - - if (quirks->tx_clk_name) - tx_clk_name = quirks->tx_clk_name; - host->spdif_clk = devm_clk_get(&pdev->dev, tx_clk_name); -- if (IS_ERR(host->spdif_clk)) { -- dev_err(&pdev->dev, "failed to get the \"%s\" clock.\n", -- tx_clk_name); -- return PTR_ERR(host->spdif_clk); -- } -+ if (IS_ERR(host->spdif_clk)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(host->spdif_clk), -+ "failed to get the \"%s\" clock.\n", -+ tx_clk_name); - - host->dma_params_tx.addr = res->start + quirks->reg_dac_txdata; - host->dma_params_tx.maxburst = 8; -diff --git a/sound/soc/sunxi/sun50i-codec-analog.c b/sound/soc/sunxi/sun50i-codec-analog.c ---- a/sound/soc/sunxi/sun50i-codec-analog.c -+++ b/sound/soc/sunxi/sun50i-codec-analog.c -@@ -551,16 +551,13 @@ static int sun50i_codec_analog_probe(struct platform_device *pdev) - bool enable; - - base = devm_platform_ioremap_resource(pdev, 0); -- if (IS_ERR(base)) { -- dev_err(&pdev->dev, "Failed to map the registers\n"); -+ if (IS_ERR(base)) - return PTR_ERR(base); -- } - - regmap = sun8i_adda_pr_regmap_init(&pdev->dev, base); -- if (IS_ERR(regmap)) { -- dev_err(&pdev->dev, "Failed to create regmap\n"); -- return PTR_ERR(regmap); -- } -+ if (IS_ERR(regmap)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(regmap), -+ "Failed to create regmap\n"); - - enable = device_property_read_bool(&pdev->dev, - "allwinner,internal-bias-resistor"); -diff --git a/sound/soc/sunxi/sun8i-codec-analog.c b/sound/soc/sunxi/sun8i-codec-analog.c ---- a/sound/soc/sunxi/sun8i-codec-analog.c -+++ b/sound/soc/sunxi/sun8i-codec-analog.c -@@ -822,16 +822,13 @@ static int sun8i_codec_analog_probe(struct platform_device *pdev) - void __iomem *base; - - base = devm_platform_ioremap_resource(pdev, 0); -- if (IS_ERR(base)) { -- dev_err(&pdev->dev, "Failed to map the registers\n"); -+ if (IS_ERR(base)) - return PTR_ERR(base); -- } - - regmap = sun8i_adda_pr_regmap_init(&pdev->dev, base); -- if (IS_ERR(regmap)) { -- dev_err(&pdev->dev, "Failed to create regmap\n"); -- return PTR_ERR(regmap); -- } -+ if (IS_ERR(regmap)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(regmap), -+ "Failed to create regmap\n"); - - return devm_snd_soc_register_component(&pdev->dev, - &sun8i_codec_analog_cmpnt_drv, -diff --git a/sound/soc/tegra/tegra20_das.c b/sound/soc/tegra/tegra20_das.c ---- a/sound/soc/tegra/tegra20_das.c -+++ b/sound/soc/tegra/tegra20_das.c -@@ -167,10 +167,9 @@ static int tegra20_das_probe(struct platform_device *pdev) - - das->regmap = devm_regmap_init_mmio(&pdev->dev, regs, - &tegra20_das_regmap_config); -- if (IS_ERR(das->regmap)) { -- dev_err(&pdev->dev, "regmap init failed\n"); -- return PTR_ERR(das->regmap); -- } -+ if (IS_ERR(das->regmap)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(das->regmap), -+ "regmap init failed\n"); - - tegra20_das_connect_dap_to_dac(das, TEGRA20_DAS_DAP_ID_1, - TEGRA20_DAS_DAP_SEL_DAC1); -diff --git a/sound/soc/tegra/tegra210_adx.c b/sound/soc/tegra/tegra210_adx.c ---- a/sound/soc/tegra/tegra210_adx.c -+++ b/sound/soc/tegra/tegra210_adx.c -@@ -496,16 +496,12 @@ static struct snd_kcontrol_new tegra264_adx_controls[] = { - static int tegra210_adx_component_probe(struct snd_soc_component *component) - { - struct tegra210_adx *adx = snd_soc_component_get_drvdata(component); -- int err = 0; - -- if (adx->soc_data->num_controls) { -- err = snd_soc_add_component_controls(component, adx->soc_data->controls, -- adx->soc_data->num_controls); -- if (err) -- dev_err(component->dev, "can't add ADX controls, err: %d\n", err); -- } -+ if (adx->soc_data->num_controls) -+ return snd_soc_add_component_controls(component, adx->soc_data->controls, -+ adx->soc_data->num_controls); - -- return err; -+ return 0; - } - - static const struct snd_soc_component_driver tegra210_adx_cmpnt = { -diff --git a/sound/soc/tegra/tegra210_amx.c b/sound/soc/tegra/tegra210_amx.c ---- a/sound/soc/tegra/tegra210_amx.c -+++ b/sound/soc/tegra/tegra210_amx.c -@@ -504,16 +504,12 @@ static struct snd_kcontrol_new tegra264_amx_controls[] = { - static int tegra210_amx_component_probe(struct snd_soc_component *component) - { - struct tegra210_amx *amx = snd_soc_component_get_drvdata(component); -- int err = 0; - -- if (amx->soc_data->num_controls) { -- err = snd_soc_add_component_controls(component, amx->soc_data->controls, -- amx->soc_data->num_controls); -- if (err) -- dev_err(component->dev, "can't add AMX controls, err: %d\n", err); -- } -+ if (amx->soc_data->num_controls) -+ return snd_soc_add_component_controls(component, amx->soc_data->controls, -+ amx->soc_data->num_controls); - -- return err; -+ return 0; - } - - static const struct snd_soc_component_driver tegra210_amx_cmpnt = { -diff --git a/sound/soc/tegra/tegra210_i2s.c b/sound/soc/tegra/tegra210_i2s.c ---- a/sound/soc/tegra/tegra210_i2s.c -+++ b/sound/soc/tegra/tegra210_i2s.c -@@ -1090,13 +1090,13 @@ static int tegra210_i2s_probe(struct platform_device *pdev) - "can't retrieve I2S bit clock\n"); - - /* -- * Not an error, as this clock is needed only when some other I/O -- * requires input clock from current I2S instance, which is -- * configurable from DT. -+ * This clock is optional and is only needed when another I/O uses -+ * the current I2S instance as its input clock, as configured in DT. - */ -- i2s->clk_sync_input = devm_clk_get(dev, "sync_input"); -+ i2s->clk_sync_input = devm_clk_get_optional(dev, "sync_input"); - if (IS_ERR(i2s->clk_sync_input)) -- dev_dbg(dev, "can't retrieve I2S sync input clock\n"); -+ return dev_err_probe(dev, PTR_ERR(i2s->clk_sync_input), -+ "can't retrieve I2S sync input clock\n"); - - regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(regs)) -diff --git a/sound/soc/ti/ams-delta.c b/sound/soc/ti/ams-delta.c ---- a/sound/soc/ti/ams-delta.c -+++ b/sound/soc/ti/ams-delta.c -@@ -562,21 +562,21 @@ static int ams_delta_probe(struct platform_device *pdev) - - card->dev = &pdev->dev; - -- handset_mute = devm_gpiod_get(card->dev, "handset_mute", -+ handset_mute = devm_gpiod_get(&pdev->dev, "handset_mute", - GPIOD_OUT_HIGH); - if (IS_ERR(handset_mute)) - return PTR_ERR(handset_mute); - -- handsfree_mute = devm_gpiod_get(card->dev, "handsfree_mute", -+ handsfree_mute = devm_gpiod_get(&pdev->dev, "handsfree_mute", - GPIOD_OUT_HIGH); - if (IS_ERR(handsfree_mute)) - return PTR_ERR(handsfree_mute); - - ret = snd_soc_register_card(card); - if (ret) { -- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); - card->dev = NULL; -- return ret; -+ return dev_err_probe(&pdev->dev, ret, -+ "snd_soc_register_card() failed\n"); - } - return 0; - } -diff --git a/sound/soc/ti/davinci-evm.c b/sound/soc/ti/davinci-evm.c ---- a/sound/soc/ti/davinci-evm.c -+++ b/sound/soc/ti/davinci-evm.c -@@ -245,9 +245,8 @@ static int davinci_evm_probe(struct platform_device *pdev) - - snd_soc_card_set_drvdata(&evm_soc_card, drvdata); - ret = devm_snd_soc_register_card(&pdev->dev, &evm_soc_card); -- - if (ret) { -- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); -+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); - goto err_put; - } - -diff --git a/sound/soc/ti/j721e-evm.c b/sound/soc/ti/j721e-evm.c ---- a/sound/soc/ti/j721e-evm.c -+++ b/sound/soc/ti/j721e-evm.c -@@ -4,6 +4,7 @@ - * Author: Peter Ujfalusi - */ - -+#include - #include - #include - #include -@@ -263,7 +264,7 @@ static int j721e_audio_startup(struct snd_pcm_substream *substream) - int ret = 0; - int i; - -- guard(mutex)(&priv->mutex); -+ mutex_lock(&priv->mutex); - - domain->active++; - -@@ -303,6 +304,7 @@ static int j721e_audio_startup(struct snd_pcm_substream *substream) - out: - if (ret) - domain->active--; -+ mutex_unlock(&priv->mutex); - - return ret; - } -@@ -868,10 +870,9 @@ static int j721e_soc_probe(struct platform_device *pdev) - card->num_dapm_routes = ARRAY_SIZE(j721e_cpb_dapm_routes); - card->fully_routed = 1; - -- if (snd_soc_of_parse_card_name(card, "model")) { -- dev_err(&pdev->dev, "Card name is not provided\n"); -- return -ENODEV; -- } -+ ret = snd_soc_of_parse_card_name(card, "model"); -+ if (ret) -+ return ret; - - link_cnt = 0; - conf_cnt = 0; -diff --git a/sound/soc/ti/omap-abe-twl6040.c b/sound/soc/ti/omap-abe-twl6040.c ---- a/sound/soc/ti/omap-abe-twl6040.c -+++ b/sound/soc/ti/omap-abe-twl6040.c -@@ -234,16 +234,13 @@ static int omap_abe_probe(struct platform_device *pdev) - card->dapm_routes = audio_map; - card->num_dapm_routes = ARRAY_SIZE(audio_map); - -- if (snd_soc_of_parse_card_name(card, "ti,model")) { -- dev_err(&pdev->dev, "Card name is not provided\n"); -- return -ENODEV; -- } -+ ret = snd_soc_of_parse_card_name(card, "ti,model"); -+ if (ret) -+ return ret; - - ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing"); -- if (ret) { -- dev_err(&pdev->dev, "Error while parsing DAPM routing\n"); -+ if (ret) - return ret; -- } - - dai_node = of_parse_phandle(node, "ti,mcpdm", 0); - if (!dai_node) { -@@ -299,8 +296,8 @@ static int omap_abe_probe(struct platform_device *pdev) - - ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) -- dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n", -- ret); -+ dev_err_probe(&pdev->dev, ret, -+ "devm_snd_soc_register_card() failed\n"); - - return ret; - } -diff --git a/sound/soc/ti/omap-dmic.c b/sound/soc/ti/omap-dmic.c ---- a/sound/soc/ti/omap-dmic.c -+++ b/sound/soc/ti/omap-dmic.c -@@ -465,10 +465,9 @@ static int asoc_dmic_probe(struct platform_device *pdev) - mutex_init(&dmic->mutex); - - dmic->fclk = devm_clk_get(dmic->dev, "fck"); -- if (IS_ERR(dmic->fclk)) { -- dev_err(dmic->dev, "can't get fck\n"); -- return -ENODEV; -- } -+ if (IS_ERR(dmic->fclk)) -+ return dev_err_probe(dmic->dev, PTR_ERR(dmic->fclk), -+ "can't get fck\n"); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); - if (!res) { -diff --git a/sound/soc/ti/omap-hdmi.c b/sound/soc/ti/omap-hdmi.c ---- a/sound/soc/ti/omap-hdmi.c -+++ b/sound/soc/ti/omap-hdmi.c -@@ -24,8 +24,6 @@ - #define DRV_NAME "omap-hdmi-audio" - - struct hdmi_audio_data { -- struct snd_soc_card *card; -- - const struct omap_hdmi_audio_ops *ops; - struct device *dssdev; - struct snd_dmaengine_dai_dma_data dma_data; -@@ -375,12 +373,9 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev) - card->dev = dev; - - ret = devm_snd_soc_register_card(dev, card); -- if (ret) { -- dev_err(dev, "snd_soc_register_card failed (%d)\n", ret); -- return ret; -- } -+ if (ret) -+ return dev_err_probe(dev, ret, "snd_soc_register_card() failed\n"); - -- ad->card = card; - snd_soc_card_set_drvdata(card, ad); - - dev_set_drvdata(dev, ad); -diff --git a/sound/soc/ti/omap-twl4030.c b/sound/soc/ti/omap-twl4030.c ---- a/sound/soc/ti/omap-twl4030.c -+++ b/sound/soc/ti/omap-twl4030.c -@@ -17,7 +17,6 @@ - */ - - #include --#include - #include - #include - -@@ -29,6 +28,7 @@ - #include "omap-mcbsp.h" - - struct omap_twl4030 { -+ struct snd_soc_jack_gpio hs_jack_gpio; - struct snd_soc_jack hs_jack; - }; - -@@ -124,38 +124,18 @@ static struct snd_soc_jack_pin hs_jack_pins[] = { - }, - }; - --/* Headset jack detection gpios */ --static struct snd_soc_jack_gpio hs_jack_gpios[] = { -- { -- .name = "ti,jack-det", -- .report = SND_JACK_HEADSET, -- .debounce_time = 200, -- }, --}; -- --static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm, -- int connected, char *pin) --{ -- if (!connected) -- snd_soc_dapm_disable_pin(dapm, pin); --} -- - static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd) - { - struct snd_soc_card *card = rtd->card; -- struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card); -- struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev); -+ - struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card); -- int ret = 0; -+ int ret; - - /* - * This is a bit of a hack, but the GPIO is optional so we - * only want to add the jack detection if the GPIO is there. - */ - if (of_property_present(card->dev->of_node, "ti,jack-det-gpio")) { -- hs_jack_gpios[0].gpiod_dev = card->dev; -- hs_jack_gpios[0].idx = 0; -- - ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack", - SND_JACK_HEADSET, - &priv->hs_jack, hs_jack_pins, -@@ -163,36 +143,19 @@ static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd) - if (ret) - return ret; - -- ret = snd_soc_jack_add_gpios(&priv->hs_jack, -- ARRAY_SIZE(hs_jack_gpios), -- hs_jack_gpios); -+ priv->hs_jack_gpio.name = "ti,jack-det"; -+ priv->hs_jack_gpio.report = SND_JACK_HEADSET; -+ priv->hs_jack_gpio.debounce_time = 200; -+ priv->hs_jack_gpio.gpiod_dev = card->dev; -+ priv->hs_jack_gpio.idx = 0; -+ -+ ret = snd_soc_jack_add_gpios(&priv->hs_jack, 1, -+ &priv->hs_jack_gpio); - if (ret) - return ret; - } - -- /* -- * NULL pdata means we booted with DT. In this case the routing is -- * provided and the card is fully routed, no need to mark pins. -- */ -- if (!pdata || !pdata->custom_routing) -- return ret; -- -- /* Disable not connected paths if not used */ -- twl4030_disconnect_pin(dapm, pdata->has_ear, "Earpiece Spk"); -- twl4030_disconnect_pin(dapm, pdata->has_hf, "Handsfree Spk"); -- twl4030_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone"); -- twl4030_disconnect_pin(dapm, pdata->has_predriv, "Ext Spk"); -- twl4030_disconnect_pin(dapm, pdata->has_carkit, "Carkit Spk"); -- -- twl4030_disconnect_pin(dapm, pdata->has_mainmic, "Main Mic"); -- twl4030_disconnect_pin(dapm, pdata->has_submic, "Sub Mic"); -- twl4030_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic"); -- twl4030_disconnect_pin(dapm, pdata->has_carkitmic, "Carkit Mic"); -- twl4030_disconnect_pin(dapm, pdata->has_digimic0, "Digital0 Mic"); -- twl4030_disconnect_pin(dapm, pdata->has_digimic1, "Digital1 Mic"); -- twl4030_disconnect_pin(dapm, pdata->has_linein, "Line In"); -- -- return ret; -+ return 0; - } - - /* Digital audio interface glue - connects codec <--> CPU */ -@@ -237,11 +200,15 @@ static struct snd_soc_card omap_twl4030_card = { - - static int omap_twl4030_probe(struct platform_device *pdev) - { -- struct omap_tw4030_pdata *pdata = dev_get_platdata(&pdev->dev); -- struct device_node *node = pdev->dev.of_node; - struct snd_soc_card *card = &omap_twl4030_card; -+ struct device_node *node, *dai_node; - struct omap_twl4030 *priv; -- int ret = 0; -+ struct property *prop; -+ int ret; -+ -+ node = pdev->dev.of_node; -+ if (!node) -+ return -ENODEV; - - card->dev = &pdev->dev; - -@@ -249,69 +216,52 @@ static int omap_twl4030_probe(struct platform_device *pdev) - if (priv == NULL) - return -ENOMEM; - -- if (node) { -- struct device_node *dai_node; -- struct property *prop; -+ ret = snd_soc_of_parse_card_name(card, "ti,model"); -+ if (ret) -+ return ret; - -- if (snd_soc_of_parse_card_name(card, "ti,model")) { -- dev_err(&pdev->dev, "Card name is not provided\n"); -- return -ENODEV; -- } -- -- dai_node = of_parse_phandle(node, "ti,mcbsp", 0); -- if (!dai_node) { -- dev_err(&pdev->dev, "McBSP node is not provided\n"); -- return -EINVAL; -- } -- omap_twl4030_dai_links[0].cpus->dai_name = NULL; -- omap_twl4030_dai_links[0].cpus->of_node = dai_node; -- -- omap_twl4030_dai_links[0].platforms->name = NULL; -- omap_twl4030_dai_links[0].platforms->of_node = dai_node; -- -- dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0); -- if (!dai_node) { -- card->num_links = 1; -- } else { -- omap_twl4030_dai_links[1].cpus->dai_name = NULL; -- omap_twl4030_dai_links[1].cpus->of_node = dai_node; -- -- omap_twl4030_dai_links[1].platforms->name = NULL; -- omap_twl4030_dai_links[1].platforms->of_node = dai_node; -- } -- -- /* Optional: audio routing can be provided */ -- prop = of_find_property(node, "ti,audio-routing", NULL); -- if (prop) { -- ret = snd_soc_of_parse_audio_routing(card, -- "ti,audio-routing"); -- if (ret) -- return ret; -- -- card->fully_routed = 1; -- } -- } else if (pdata) { -- if (pdata->card_name) { -- card->name = pdata->card_name; -- } else { -- dev_err(&pdev->dev, "Card name is not provided\n"); -- return -ENODEV; -- } -- -- if (!pdata->voice_connected) -- card->num_links = 1; -- } else { -- dev_err(&pdev->dev, "Missing pdata\n"); -+ if (!card->name) { -+ dev_err(&pdev->dev, "Card name is not provided\n"); - return -ENODEV; - } - -+ dai_node = of_parse_phandle(node, "ti,mcbsp", 0); -+ if (!dai_node) { -+ dev_err(&pdev->dev, "McBSP node is not provided\n"); -+ return -EINVAL; -+ } -+ omap_twl4030_dai_links[0].cpus->dai_name = NULL; -+ omap_twl4030_dai_links[0].cpus->of_node = dai_node; -+ -+ omap_twl4030_dai_links[0].platforms->name = NULL; -+ omap_twl4030_dai_links[0].platforms->of_node = dai_node; -+ -+ dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0); -+ if (!dai_node) { -+ card->num_links = 1; -+ } else { -+ omap_twl4030_dai_links[1].cpus->dai_name = NULL; -+ omap_twl4030_dai_links[1].cpus->of_node = dai_node; -+ -+ omap_twl4030_dai_links[1].platforms->name = NULL; -+ omap_twl4030_dai_links[1].platforms->of_node = dai_node; -+ } -+ -+ /* Optional: audio routing can be provided */ -+ prop = of_find_property(node, "ti,audio-routing", NULL); -+ if (prop) { -+ ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing"); -+ if (ret) -+ return ret; -+ -+ card->fully_routed = 1; -+ } -+ - snd_soc_card_set_drvdata(card, priv); - ret = devm_snd_soc_register_card(&pdev->dev, card); -- if (ret) { -- dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n", -- ret); -- return ret; -- } -+ if (ret) -+ return dev_err_probe(&pdev->dev, ret, -+ "devm_snd_soc_register_card() failed\n"); - - return 0; - } -diff --git a/sound/soc/ti/rx51.c b/sound/soc/ti/rx51.c ---- a/sound/soc/ti/rx51.c -+++ b/sound/soc/ti/rx51.c -@@ -358,21 +358,22 @@ static struct snd_soc_card rx51_sound_card = { - static int rx51_soc_probe(struct platform_device *pdev) - { - struct rx51_audio_pdata *pdata; -- struct device_node *np = pdev->dev.of_node; -+ struct device *dev = &pdev->dev; -+ struct device_node *np = dev->of_node; - struct snd_soc_card *card = &rx51_sound_card; - int err; - - if (!of_machine_is_compatible("nokia,omap3-n900")) - return -ENODEV; - -- card->dev = &pdev->dev; -+ card->dev = dev; - - if (np) { - struct device_node *dai_node; - - dai_node = of_parse_phandle(np, "nokia,cpu-dai", 0); - if (!dai_node) { -- dev_err(card->dev, "McBSP node is not provided\n"); -+ dev_err(dev, "McBSP node is not provided\n"); - return -EINVAL; - } - rx51_dai[0].cpus->dai_name = NULL; -@@ -382,7 +383,7 @@ static int rx51_soc_probe(struct platform_device *pdev) - - dai_node = of_parse_phandle(np, "nokia,audio-codec", 0); - if (!dai_node) { -- dev_err(card->dev, "Codec node is not provided\n"); -+ dev_err(dev, "Codec node is not provided\n"); - return -EINVAL; - } - rx51_dai[0].codecs->name = NULL; -@@ -390,7 +391,7 @@ static int rx51_soc_probe(struct platform_device *pdev) - - dai_node = of_parse_phandle(np, "nokia,audio-codec", 1); - if (!dai_node) { -- dev_err(card->dev, "Auxiliary Codec node is not provided\n"); -+ dev_err(dev, "Auxiliary Codec node is not provided\n"); - return -EINVAL; - } - rx51_aux_dev[0].dlc.name = NULL; -@@ -400,7 +401,7 @@ static int rx51_soc_probe(struct platform_device *pdev) - - dai_node = of_parse_phandle(np, "nokia,headphone-amplifier", 0); - if (!dai_node) { -- dev_err(card->dev, "Headphone amplifier node is not provided\n"); -+ dev_err(dev, "Headphone amplifier node is not provided\n"); - return -EINVAL; - } - rx51_aux_dev[1].dlc.name = NULL; -@@ -409,40 +410,36 @@ static int rx51_soc_probe(struct platform_device *pdev) - rx51_codec_conf[1].dlc.of_node = dai_node; - } - -- pdata = devm_kzalloc(card->dev, sizeof(*pdata), GFP_KERNEL); -+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (pdata == NULL) - return -ENOMEM; - - snd_soc_card_set_drvdata(card, pdata); - -- pdata->tvout_selection_gpio = devm_gpiod_get(card->dev, -+ pdata->tvout_selection_gpio = devm_gpiod_get(dev, - "tvout-selection", - GPIOD_OUT_LOW); -- if (IS_ERR(pdata->tvout_selection_gpio)) { -- dev_err(card->dev, "could not get tvout selection gpio\n"); -- return PTR_ERR(pdata->tvout_selection_gpio); -- } -+ if (IS_ERR(pdata->tvout_selection_gpio)) -+ return dev_err_probe(dev, PTR_ERR(pdata->tvout_selection_gpio), -+ "could not get tvout selection gpio\n"); - -- pdata->eci_sw_gpio = devm_gpiod_get(card->dev, "eci-switch", -+ pdata->eci_sw_gpio = devm_gpiod_get(dev, "eci-switch", - GPIOD_OUT_HIGH); -- if (IS_ERR(pdata->eci_sw_gpio)) { -- dev_err(card->dev, "could not get eci switch gpio\n"); -- return PTR_ERR(pdata->eci_sw_gpio); -- } -+ if (IS_ERR(pdata->eci_sw_gpio)) -+ return dev_err_probe(dev, PTR_ERR(pdata->eci_sw_gpio), -+ "could not get eci switch gpio\n"); - -- pdata->speaker_amp_gpio = devm_gpiod_get(card->dev, -+ pdata->speaker_amp_gpio = devm_gpiod_get(dev, - "speaker-amplifier", - GPIOD_OUT_LOW); -- if (IS_ERR(pdata->speaker_amp_gpio)) { -- dev_err(card->dev, "could not get speaker enable gpio\n"); -- return PTR_ERR(pdata->speaker_amp_gpio); -- } -+ if (IS_ERR(pdata->speaker_amp_gpio)) -+ return dev_err_probe(dev, PTR_ERR(pdata->speaker_amp_gpio), -+ "could not get speaker enable gpio\n"); - -- err = devm_snd_soc_register_card(card->dev, card); -- if (err) { -- dev_err(card->dev, "snd_soc_register_card failed (%d)\n", err); -- return err; -- } -+ err = devm_snd_soc_register_card(dev, card); -+ if (err) -+ return dev_err_probe(dev, err, -+ "snd_soc_register_card() failed\n"); - - return 0; - } -diff --git a/sound/soc/uniphier/aio-cpu.c b/sound/soc/uniphier/aio-cpu.c ---- a/sound/soc/uniphier/aio-cpu.c -+++ b/sound/soc/uniphier/aio-cpu.c -@@ -795,16 +795,12 @@ int uniphier_aio_probe(struct platform_device *pdev) - ret = devm_snd_soc_register_component(dev, &uniphier_aio_component, - chip->chip_spec->dais, - chip->chip_spec->num_dais); -- if (ret) { -- dev_err(dev, "Register component failed.\n"); -+ if (ret) - goto err_out_reset; -- } - - ret = uniphier_aiodma_soc_register_platform(pdev); -- if (ret) { -- dev_err(dev, "Register platform failed.\n"); -+ if (ret) - goto err_out_reset; -- } - - return 0; - -diff --git a/sound/soc/uniphier/aio.h b/sound/soc/uniphier/aio.h ---- a/sound/soc/uniphier/aio.h -+++ b/sound/soc/uniphier/aio.h -@@ -156,7 +156,7 @@ struct uniphier_aio_selector { - int hw; - }; - --/** -+/* - * 'SoftWare MAPping' setting of UniPhier AIO registers. - * - * We have to setup 'virtual' register maps to access 'real' registers of AIO. -diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c ---- a/sound/soc/ux500/mop500.c -+++ b/sound/soc/ux500/mop500.c -@@ -68,10 +68,11 @@ static void mop500_of_node_put(void) - of_node_put(mop500_dai_links[0].codecs->of_node); - } - --static int mop500_of_probe(struct platform_device *pdev, -- struct device_node *np) -+static int mop500_of_probe(struct snd_soc_card *card) - { -+ struct device *dev = card->dev; - struct device_node *codec_np, *msp_np[2]; -+ struct device_node *np = dev->of_node; - int i; - - msp_np[0] = of_parse_phandle(np, "stericsson,cpu-dai", 0); -@@ -79,7 +80,7 @@ static int mop500_of_probe(struct platform_device *pdev, - codec_np = of_parse_phandle(np, "stericsson,audio-codec", 0); - - if (!(msp_np[0] && msp_np[1] && codec_np)) { -- dev_err(&pdev->dev, "Phandle missing or invalid\n"); -+ dev_err(dev, "Phandle missing or invalid\n"); - for (i = 0; i < 2; i++) - of_node_put(msp_np[i]); - of_node_put(codec_np); -@@ -95,21 +96,20 @@ static int mop500_of_probe(struct platform_device *pdev, - mop500_dai_links[i].codecs->name = NULL; - } - -- snd_soc_of_parse_card_name(&mop500_card, "stericsson,card-name"); -+ snd_soc_of_parse_card_name(card, "stericsson,card-name"); - - return 0; - } - - static int mop500_probe(struct platform_device *pdev) - { -- struct device_node *np = pdev->dev.of_node; - int ret; - - dev_dbg(&pdev->dev, "%s: Enter.\n", __func__); - - mop500_card.dev = &pdev->dev; - -- ret = mop500_of_probe(pdev, np); -+ ret = mop500_of_probe(&mop500_card); - if (ret) - return ret; - -diff --git a/sound/soc/ux500/mop500_ab8500.c b/sound/soc/ux500/mop500_ab8500.c ---- a/sound/soc/ux500/mop500_ab8500.c -+++ b/sound/soc/ux500/mop500_ab8500.c -@@ -433,5 +433,5 @@ void mop500_ab8500_remove(struct snd_soc_card *card) - clk_put(drvdata->clk_ptr_ulpclk); - clk_put(drvdata->clk_ptr_intclk); - -- snd_soc_card_set_drvdata(card, drvdata); -+ snd_soc_card_set_drvdata(card, NULL); - } -diff --git a/sound/soc/xilinx/xlnx_formatter_pcm.c b/sound/soc/xilinx/xlnx_formatter_pcm.c ---- a/sound/soc/xilinx/xlnx_formatter_pcm.c -+++ b/sound/soc/xilinx/xlnx_formatter_pcm.c -@@ -9,8 +9,6 @@ - #include - #include - #include --#include --#include - #include - - #include -@@ -384,7 +382,7 @@ static int xlnx_formatter_pcm_open(struct snd_soc_component *component, - if (err) { - dev_err(component->dev, - "Unable to set constraint on period bytes\n"); -- return err; -+ goto error; - } - - /* Resize the buffer bytes as divisible by 64 */ -@@ -394,7 +392,7 @@ static int xlnx_formatter_pcm_open(struct snd_soc_component *component, - if (err) { - dev_err(component->dev, - "Unable to set constraint on buffer bytes\n"); -- return err; -+ goto error; - } - - /* Set periods as integer multiple */ -@@ -403,7 +401,7 @@ static int xlnx_formatter_pcm_open(struct snd_soc_component *component, - if (err < 0) { - dev_err(component->dev, - "Unable to set constraint on periods to be integer\n"); -- return err; -+ goto error; - } - - /* enable DMA IOC irq */ -@@ -412,6 +410,14 @@ static int xlnx_formatter_pcm_open(struct snd_soc_component *component, - writel(val, stream_data->mmio + XLNX_AUD_CTRL); - - return 0; -+ -+error: -+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) -+ adata->play_stream = NULL; -+ else -+ adata->capture_stream = NULL; -+ kfree(stream_data); -+ return err; - } - - static int xlnx_formatter_pcm_close(struct snd_soc_component *component, -diff --git a/sound/soc/xilinx/xlnx_i2s.c b/sound/soc/xilinx/xlnx_i2s.c ---- a/sound/soc/xilinx/xlnx_i2s.c -+++ b/sound/soc/xilinx/xlnx_i2s.c -@@ -9,9 +9,8 @@ - - #include - #include --#include --#include - #include -+#include - #include - #include - -@@ -174,7 +173,6 @@ static int xlnx_i2s_probe(struct platform_device *pdev) - int ret; - u32 format; - struct device *dev = &pdev->dev; -- struct device_node *node = dev->of_node; - - drv_data = devm_kzalloc(&pdev->dev, sizeof(*drv_data), GFP_KERNEL); - if (!drv_data) -@@ -184,18 +182,16 @@ static int xlnx_i2s_probe(struct platform_device *pdev) - if (IS_ERR(drv_data->base)) - return PTR_ERR(drv_data->base); - -- ret = of_property_read_u32(node, "xlnx,num-channels", &drv_data->channels); -- if (ret < 0) { -- dev_err(dev, "cannot get supported channels\n"); -- return ret; -- } -+ ret = device_property_read_u32(dev, "xlnx,num-channels", &drv_data->channels); -+ if (ret < 0) -+ return dev_err_probe(dev, ret, "cannot get supported channels\n"); -+ - drv_data->channels *= 2; - -- ret = of_property_read_u32(node, "xlnx,dwidth", &drv_data->data_width); -- if (ret < 0) { -- dev_err(dev, "cannot get data width\n"); -- return ret; -- } -+ ret = device_property_read_u32(dev, "xlnx,dwidth", &drv_data->data_width); -+ if (ret < 0) -+ return dev_err_probe(dev, ret, "cannot get data width\n"); -+ - switch (drv_data->data_width) { - case 16: - format = SNDRV_PCM_FMTBIT_S16_LE; -@@ -207,7 +203,7 @@ static int xlnx_i2s_probe(struct platform_device *pdev) - return -EINVAL; - } - -- if (of_device_is_compatible(node, "xlnx,i2s-transmitter-1.0")) { -+ if (device_is_compatible(dev, "xlnx,i2s-transmitter-1.0")) { - drv_data->dai_drv.name = "xlnx_i2s_playback"; - drv_data->dai_drv.playback.stream_name = "Playback"; - drv_data->dai_drv.playback.formats = format; -@@ -215,7 +211,7 @@ static int xlnx_i2s_probe(struct platform_device *pdev) - drv_data->dai_drv.playback.channels_max = drv_data->channels; - drv_data->dai_drv.playback.rates = SNDRV_PCM_RATE_8000_192000; - drv_data->dai_drv.ops = &xlnx_i2s_dai_ops; -- } else if (of_device_is_compatible(node, "xlnx,i2s-receiver-1.0")) { -+ } else if (device_is_compatible(dev, "xlnx,i2s-receiver-1.0")) { - drv_data->dai_drv.name = "xlnx_i2s_capture"; - drv_data->dai_drv.capture.stream_name = "Capture"; - drv_data->dai_drv.capture.formats = format; -@@ -233,10 +229,8 @@ static int xlnx_i2s_probe(struct platform_device *pdev) - - ret = devm_snd_soc_register_component(&pdev->dev, &xlnx_i2s_component, - &drv_data->dai_drv, 1); -- if (ret) { -- dev_err(&pdev->dev, "i2s component registration failed\n"); -+ if (ret) - return ret; -- } - - dev_info(&pdev->dev, "%s DAI registered\n", drv_data->dai_drv.name); - -diff --git a/sound/soc/xilinx/xlnx_spdif.c b/sound/soc/xilinx/xlnx_spdif.c ---- a/sound/soc/xilinx/xlnx_spdif.c -+++ b/sound/soc/xilinx/xlnx_spdif.c -@@ -10,8 +10,7 @@ - #include - #include - #include --#include --#include -+#include - #include - #include - #include -@@ -242,28 +241,24 @@ static int xlnx_spdif_probe(struct platform_device *pdev) - struct spdif_dev_data *ctx; - - struct device *dev = &pdev->dev; -- struct device_node *node = dev->of_node; - - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - ctx->axi_clk = devm_clk_get_enabled(dev, "s_axi_aclk"); -- if (IS_ERR(ctx->axi_clk)) { -- ret = PTR_ERR(ctx->axi_clk); -- dev_err(dev, "failed to get s_axi_aclk(%d)\n", ret); -- return ret; -- } -+ if (IS_ERR(ctx->axi_clk)) -+ return dev_err_probe(dev, PTR_ERR(ctx->axi_clk), -+ "failed to get s_axi_aclk\n"); - - ctx->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(ctx->base)) - return PTR_ERR(ctx->base); - -- ret = of_property_read_u32(node, "xlnx,spdif-mode", &ctx->mode); -- if (ret < 0) { -- dev_err(dev, "cannot get SPDIF mode\n"); -- return ret; -- } -+ ret = device_property_read_u32(dev, "xlnx,spdif-mode", &ctx->mode); -+ if (ret < 0) -+ return dev_err_probe(dev, ret, "cannot get SPDIF mode\n"); -+ - if (ctx->mode) { - dai_drv = &xlnx_spdif_tx_dai; - } else { -@@ -274,29 +269,23 @@ static int xlnx_spdif_probe(struct platform_device *pdev) - ret = devm_request_irq(dev, ret, - xlnx_spdifrx_irq_handler, - 0, "XLNX_SPDIF_RX", ctx); -- if (ret) { -- dev_err(dev, "spdif rx irq request failed\n"); -- return -ENODEV; -- } -+ if (ret) -+ return ret; - - init_waitqueue_head(&ctx->chsts_q); - dai_drv = &xlnx_spdif_rx_dai; - } - -- ret = of_property_read_u32(node, "xlnx,aud_clk_i", &ctx->aclk); -- if (ret < 0) { -- dev_err(dev, "cannot get aud_clk_i value\n"); -- return ret; -- } -+ ret = device_property_read_u32(dev, "xlnx,aud_clk_i", &ctx->aclk); -+ if (ret < 0) -+ return dev_err_probe(dev, ret, "cannot get aud_clk_i value\n"); - - dev_set_drvdata(dev, ctx); - - ret = devm_snd_soc_register_component(dev, &xlnx_spdif_component, - dai_drv, 1); -- if (ret) { -- dev_err(dev, "SPDIF component registration failed\n"); -+ if (ret) - return ret; -- } - - writel(XSPDIF_SOFT_RESET_VALUE, ctx->base + XSPDIF_SOFT_RESET_REG); - dev_info(dev, "%s DAI registered\n", dai_drv->name); -diff --git a/sound/soc/xtensa/xtfpga-i2s.c b/sound/soc/xtensa/xtfpga-i2s.c ---- a/sound/soc/xtensa/xtfpga-i2s.c -+++ b/sound/soc/xtensa/xtfpga-i2s.c -@@ -533,34 +533,25 @@ static int xtfpga_i2s_probe(struct platform_device *pdev) - int err, irq; - - i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); -- if (!i2s) { -- err = -ENOMEM; -- goto err; -- } -+ if (!i2s) -+ return -ENOMEM; -+ - platform_set_drvdata(pdev, i2s); - i2s->dev = &pdev->dev; - dev_dbg(&pdev->dev, "dev: %p, i2s: %p\n", &pdev->dev, i2s); - - i2s->regs = devm_platform_ioremap_resource(pdev, 0); -- if (IS_ERR(i2s->regs)) { -- err = PTR_ERR(i2s->regs); -- goto err; -- } -+ if (IS_ERR(i2s->regs)) -+ return PTR_ERR(i2s->regs); - - i2s->regmap = devm_regmap_init_mmio(&pdev->dev, i2s->regs, - &xtfpga_i2s_regmap_config); -- if (IS_ERR(i2s->regmap)) { -- dev_err(&pdev->dev, "regmap init failed\n"); -- err = PTR_ERR(i2s->regmap); -- goto err; -- } -+ if (IS_ERR(i2s->regmap)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->regmap), "regmap init failed\n"); - - i2s->clk = devm_clk_get(&pdev->dev, NULL); -- if (IS_ERR(i2s->clk)) { -- dev_err(&pdev->dev, "couldn't get clock\n"); -- err = PTR_ERR(i2s->clk); -- goto err; -- } -+ if (IS_ERR(i2s->clk)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->clk), "couldn't get clock\n"); - - regmap_write(i2s->regmap, XTFPGA_I2S_CONFIG, - (0x1 << XTFPGA_I2S_CONFIG_CHANNEL_BASE)); -@@ -568,41 +559,34 @@ static int xtfpga_i2s_probe(struct platform_device *pdev) - regmap_write(i2s->regmap, XTFPGA_I2S_INT_MASK, XTFPGA_I2S_INT_UNDERRUN); - - irq = platform_get_irq(pdev, 0); -- if (irq < 0) { -- err = irq; -- goto err; -- } -+ if (irq < 0) -+ return irq; -+ - err = devm_request_threaded_irq(&pdev->dev, irq, NULL, - xtfpga_i2s_threaded_irq_handler, - IRQF_SHARED | IRQF_ONESHOT, - pdev->name, i2s); -- if (err < 0) { -- dev_err(&pdev->dev, "request_irq failed\n"); -- goto err; -- } -+ if (err < 0) -+ return err; - - err = devm_snd_soc_register_component(&pdev->dev, - &xtfpga_i2s_component, - xtfpga_i2s_dai, - ARRAY_SIZE(xtfpga_i2s_dai)); -- if (err < 0) { -- dev_err(&pdev->dev, "couldn't register component\n"); -- goto err; -- } -+ if (err < 0) -+ return err; -+ - - pm_runtime_enable(&pdev->dev); - if (!pm_runtime_enabled(&pdev->dev)) { - err = xtfpga_i2s_runtime_resume(&pdev->dev); -- if (err) -- goto err_pm_disable; -+ if (err) { -+ pm_runtime_disable(&pdev->dev); -+ return err; -+ } - } -- return 0; - --err_pm_disable: -- pm_runtime_disable(&pdev->dev); --err: -- dev_err(&pdev->dev, "%s: err = %d\n", __func__, err); -- return err; -+ return 0; - } - - static void xtfpga_i2s_remove(struct platform_device *pdev) diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c --- a/sound/sparc/dbri.c +++ b/sound/sparc/dbri.c @@ -50398,7 +11755,7 @@ diff --git a/sound/usb/clock.c b/sound/usb/clock.c diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c -@@ -1236,7 +1236,10 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep) +@@ -1237,7 +1237,10 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep) /* try to use enough URBs to contain an entire ALSA buffer */ max_urbs = min((unsigned) MAX_URBS, MAX_QUEUE * packs_per_ms / urb_packs); @@ -50409,8 +11766,8 @@ diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c + ep->nurbs = min(max_urbs, urbs_per_period * ep->cur_buffer_periods); } - /* allocate and initialize data urbs */ -@@ -1260,6 +1263,8 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep) + if (fmt->fmt_type == UAC_FORMAT_TYPE_II) +@@ -1262,6 +1265,8 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep) goto out_of_memory; u->urb->pipe = ep->pipe; u->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; diff --git a/pkgbuilds/linux-omarchy-bore/0510-sound-updates.patch.sig b/pkgbuilds/linux-omarchy-bore/0510-sound-updates.patch.sig index cfb31fc..95975e7 100644 Binary files a/pkgbuilds/linux-omarchy-bore/0510-sound-updates.patch.sig and b/pkgbuilds/linux-omarchy-bore/0510-sound-updates.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0511-asoc-updates.patch b/pkgbuilds/linux-omarchy-bore/0511-asoc-updates.patch new file mode 100644 index 0000000..75c49b4 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0511-asoc-updates.patch @@ -0,0 +1,37891 @@ +diff --git a/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c b/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c +--- a/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c ++++ b/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c +@@ -147,7 +147,7 @@ static const struct reg_default halo_register_defaults[] = { + { 0x2b805c0, 0 }, /* HALO_SCRATCH1 */ + { 0x2b805c8, 0 }, /* HALO_SCRATCH2 */ + { 0x2b805d0, 0 }, /* HALO_SCRATCH3 */ +- { 0x2b805c8, 0 }, /* HALO_SCRATCH4 */ ++ { 0x2b805d8, 0 }, /* HALO_SCRATCH4 */ + { 0x2bc1000, 0 }, /* HALO_CCM_CORE_CONTROL */ + { 0x2bc7000, 0 }, /* HALO_WDT_CONTROL */ + +diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c +--- a/drivers/soundwire/bus_type.c ++++ b/drivers/soundwire/bus_type.c +@@ -105,6 +105,9 @@ static int sdw_bus_probe(struct device *dev) + } + slave->index = ret; + ++ /* Create IRQ mapping now so the driver can get it in probe() */ ++ sdw_irq_create_mapping(slave); ++ + ret = drv->probe(slave, id); + if (ret) { + ida_free(&slave->bus->slave_ida, slave->index); +@@ -117,9 +120,6 @@ static int sdw_bus_probe(struct device *dev) + if (drv->ops && drv->ops->read_prop) + drv->ops->read_prop(slave); + +- if (slave->prop.use_domain_irq) +- sdw_irq_create_mapping(slave); +- + /* init the dynamic sysfs attributes we need */ + ret = sdw_slave_sysfs_dpn_init(slave); + if (ret < 0) +diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h +--- a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h ++++ b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h +@@ -152,6 +152,10 @@ + #define LPI_MI2S_TX_4 146 + #define SENARY_MI2S_RX 147 + #define SENARY_MI2S_TX 148 ++#define LPI_MI2S_RX_5 149 ++#define LPI_MI2S_TX_5 150 ++#define LPI_MI2S_RX_6 151 ++#define LPI_MI2S_TX_6 152 + + #define LPASS_CLK_ID_PRI_MI2S_IBIT 1 + #define LPASS_CLK_ID_PRI_MI2S_EBIT 2 +@@ -233,11 +237,39 @@ + /* Clock ID for RX CORE MCLK2 2X MCLK */ + #define LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK 70 + ++#define LAPSS_CLK_ID_QAIF_IF0_IBIT 71 ++#define LAPSS_CLK_ID_QAIF_IF0_EBIT 72 ++#define LAPSS_CLK_ID_QAIF_IF1_IBIT 73 ++#define LAPSS_CLK_ID_QAIF_IF1_EBIT 74 ++#define LAPSS_CLK_ID_QAIF_IF2_IBIT 75 ++#define LAPSS_CLK_ID_QAIF_IF2_EBIT 76 ++#define LAPSS_CLK_ID_QAIF_IF3_IBIT 77 ++#define LAPSS_CLK_ID_QAIF_IF3_EBIT 78 ++#define LAPSS_CLK_ID_QAIF_IF4_IBIT 79 ++#define LAPSS_CLK_ID_QAIF_IF4_EBIT 80 ++#define LAPSS_CLK_ID_QAIF_IF5_IBIT 81 ++#define LAPSS_CLK_ID_QAIF_IF5_EBIT 82 ++#define LAPSS_CLK_ID_QAIF_IF6_IBIT 83 ++#define LAPSS_CLK_ID_QAIF_IF6_EBIT 84 ++#define LAPSS_CLK_ID_QAIF_IF7_IBIT 85 ++#define LAPSS_CLK_ID_QAIF_IF7_EBIT 86 ++#define LAPSS_CLK_ID_QAIF_IF8_IBIT 87 ++#define LAPSS_CLK_ID_QAIF_IF8_EBIT 88 ++#define LAPSS_CLK_ID_QAIF_IF9_IBIT 89 ++#define LAPSS_CLK_ID_QAIF_IF9_EBIT 90 ++#define LAPSS_CLK_ID_QAIF_IF10_IBIT 91 ++#define LAPSS_CLK_ID_QAIF_IF10_EBIT 92 ++#define LAPSS_CLK_ID_QAIF_IF11_IBIT 93 ++#define LAPSS_CLK_ID_QAIF_IF11_EBIT 94 ++#define LAPSS_CLK_ID_QAIF_IF12_IBIT 95 ++#define LAPSS_CLK_ID_QAIF_IF12_EBIT 96 ++#define LAPSS_CLK_ID_VA_QAIF_IF0_IBIT 97 ++#define LAPSS_CLK_ID_VA_QAIF_IF0_EBIT 98 ++ + #define LPASS_HW_AVTIMER_VOTE 101 + #define LPASS_HW_MACRO_VOTE 102 + #define LPASS_HW_DCODEC_VOTE 103 +- +-#define Q6AFE_MAX_CLK_ID 104 ++#define LPASS_HW_LPR_VOTE 104 + + #define LPASS_CLK_ATTRIBUTE_INVALID 0x0 + #define LPASS_CLK_ATTRIBUTE_COUPLE_NO 0x1 +diff --git a/include/linux/platform_data/omap-twl4030.h b/include/linux/platform_data/omap-twl4030.h +deleted file mode 100644 +--- a/include/linux/platform_data/omap-twl4030.h ++++ /dev/null +@@ -1,42 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-only */ +-/** +- * omap-twl4030.h - ASoC machine driver for TI SoC based boards with twl4030 +- * codec, header. +- * +- * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com +- * All rights reserved. +- * +- * Author: Peter Ujfalusi +- */ +- +-#ifndef _OMAP_TWL4030_H_ +-#define _OMAP_TWL4030_H_ +- +-/* To select if only one channel is connected in a stereo port */ +-#define OMAP_TWL4030_LEFT (1 << 0) +-#define OMAP_TWL4030_RIGHT (1 << 1) +- +-struct omap_tw4030_pdata { +- const char *card_name; +- /* Voice port is connected to McBSP3 */ +- bool voice_connected; +- +- /* The driver will parse the connection flags if this flag is set */ +- bool custom_routing; +- /* Flags to indicate connected audio ports. */ +- u8 has_hs; +- u8 has_hf; +- u8 has_predriv; +- u8 has_carkit; +- bool has_ear; +- +- bool has_mainmic; +- bool has_submic; +- bool has_hsmic; +- bool has_carkitmic; +- bool has_digimic0; +- bool has_digimic1; +- u8 has_linein; +-}; +- +-#endif /* _OMAP_TWL4030_H_ */ +diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h +--- a/include/sound/cs35l56.h ++++ b/include/sound/cs35l56.h +@@ -417,7 +417,6 @@ void cs35l56_wait_control_port_ready(void); + void cs35l56_wait_min_reset_pulse(void); + void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire); + int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq); +-irqreturn_t cs35l56_irq(int irq, void *data); + int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base); + int cs35l56_runtime_suspend_common(struct cs35l56_base *cs35l56_base); + int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_soundwire); +diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h +--- a/include/sound/dmaengine_pcm.h ++++ b/include/sound/dmaengine_pcm.h +@@ -175,12 +175,6 @@ int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream *substream, + struct dmaengine_pcm { + struct dma_chan *chan[SNDRV_PCM_STREAM_LAST + 1]; + const struct snd_dmaengine_pcm_config *config; +- struct snd_soc_component component; + unsigned int flags; + }; +- +-static inline struct dmaengine_pcm *soc_component_to_pcm(struct snd_soc_component *p) +-{ +- return container_of(p, struct dmaengine_pcm, component); +-} + #endif +diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h +--- a/include/sound/hda-mlink.h ++++ b/include/sound/hda-mlink.h +@@ -9,6 +9,22 @@ + struct hdac_bus; + struct hdac_ext_link; + ++/** ++ * enum hda_bus_ml_link_type - mlink link type, used by SOF link DMA ++ * allocator constraints (see struct sof_intel_hda_dev). ++ * ++ * @HDA_BUS_ML_LINK_HDA: non-alt link, i.e. HDA codec or iDisp ++ * @HDA_BUS_ML_LINK_SDW: alt link, SoundWire ++ * @HDA_BUS_ML_LINK_UAOL: alt link, USB Audio Offload ++ * @HDA_BUS_ML_LINK_OTHER: alt link, SSP or DMIC ++ */ ++enum hda_bus_ml_link_type { ++ HDA_BUS_ML_LINK_HDA, ++ HDA_BUS_ML_LINK_SDW, ++ HDA_BUS_ML_LINK_UAOL, ++ HDA_BUS_ML_LINK_OTHER, ++}; ++ + #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK) + + int hda_bus_ml_init(struct hdac_bus *bus); +@@ -49,11 +65,12 @@ int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num); + int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, + int channel_mask, int stream_id, int dir); + +-void hda_bus_ml_put_all(struct hdac_bus *bus); + void hda_bus_ml_reset_losidv(struct hdac_bus *bus); + int hda_bus_ml_resume(struct hdac_bus *bus); + int hda_bus_ml_suspend(struct hdac_bus *bus); + ++enum hda_bus_ml_link_type hda_bus_ml_link_get_type(struct hdac_ext_link *hlink); ++ + struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus); + struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus); + struct hdac_ext_link *hdac_bus_eml_sdw_get_hlink(struct hdac_bus *bus); +@@ -169,11 +186,13 @@ hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, + return 0; + } + +-static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { } + static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { } + static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; } + static inline int hda_bus_ml_suspend(struct hdac_bus *bus) { return 0; } + ++static inline enum hda_bus_ml_link_type ++hda_bus_ml_link_get_type(struct hdac_ext_link *hlink) { return HDA_BUS_ML_LINK_HDA; } ++ + static inline struct hdac_ext_link * + hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus) { return NULL; } + +diff --git a/include/sound/sdca_asoc.h b/include/sound/sdca_asoc.h +--- a/include/sound/sdca_asoc.h ++++ b/include/sound/sdca_asoc.h +@@ -25,6 +25,8 @@ struct snd_soc_dai_driver; + struct snd_soc_dai_ops; + struct snd_soc_dapm_route; + struct snd_soc_dapm_widget; ++struct snd_soc_pcm_stream; ++struct sdca_entity; + + /* convenient macro to handle the mono volume in 7.8 fixed format representation */ + #define SDCA_SINGLE_Q78_TLV(xname, xreg, xmin, xmax, xstep, tlv_array) \ +@@ -82,6 +84,11 @@ int sdca_asoc_populate_component(struct device *dev, + struct snd_soc_dai_driver **dai_drv, int *num_dai_drv, + const struct snd_soc_dai_ops *ops); + ++int sdca_asoc_populate_rate_format(struct device *dev, ++ struct sdca_function_data *function, ++ struct sdca_entity *entity, ++ struct snd_soc_pcm_stream *stream); ++ + int sdca_asoc_set_constraints(struct device *dev, struct regmap *regmap, + struct sdca_function_data *function, + struct snd_pcm_substream *substream, +@@ -100,9 +107,9 @@ int sdca_asoc_q78_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + int sdca_asoc_q78_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +-int sdca_asoc_pde_poll_actual_ps(struct device *dev, struct regmap *regmap, ++int sdca_asoc_pde_poll_actual_ps(struct regmap *regmap, + int function_id, int entity_id, +- int from_ps, int to_ps, +- const struct sdca_pde_delay *pde_delays, +- int num_delays); ++ int from_ps, int to_ps, ++ const struct sdca_pde_delay *pde_delays, ++ int num_delays); + #endif // __SDCA_ASOC_H__ +diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h +--- a/include/sound/sdca_function.h ++++ b/include/sound/sdca_function.h +@@ -298,7 +298,7 @@ enum sdca_xu_controls { + }; + + /** +- * enum sdca_set_index_range - Column definitions UMP SetIndex ++ * enum sdca_fdl_set_index_range - Column definitions UMP SetIndex + */ + enum sdca_fdl_set_index_range { + SDCA_FDL_SET_INDEX_SET_NUMBER = 0, +@@ -803,6 +803,8 @@ struct sdca_control_range { + * @mode: Access mode of the Control. + * @layers: Bitmask of access layers of the Control. + * @deferrable: Indicates if the access to the Control can be deferred. ++ * @is_volatile: Indicates the Control registers are forced to be treated ++ * as volatile. + * @has_default: Indicates the Control has a default value to be written. + * @has_reset: Indicates the Control has a defined reset value. + * @has_fixed: Indicates the Control only supports a single value. +@@ -1116,7 +1118,6 @@ struct sdca_entity_ge { + + /** + * struct sdca_entity_hide - information specific to HIDE Entities +- * @hid: HID device structure + * @num_hidtx_ids: number of HIDTx Report ID + * @num_hidrx_ids: number of HIDRx Report ID + * @hidtx_ids: HIDTx Report ID +@@ -1127,11 +1128,8 @@ struct sdca_entity_ge { + * within this Device + * @max_delay: the maximum time in microseconds allowed for the Device + * to change the ownership from Device to Host +- * @hid_report_desc: HID Report Descriptor for the HIDE Entity +- * @hid_desc: HID descriptor for the HIDE Entity + */ + struct sdca_entity_hide { +- struct hid_device *hid; + unsigned int *hidtx_ids; + unsigned int *hidrx_ids; + int num_hidtx_ids; +@@ -1139,12 +1137,10 @@ struct sdca_entity_hide { + unsigned int af_number_list[SDCA_MAX_FUNCTION_COUNT]; + unsigned int hide_reside_function_num; + unsigned int max_delay; +- unsigned char *hid_report_desc; +- struct hid_descriptor hid_desc; + }; + + /** +- * enum sdca_xu_reset_machanism - SDCA FDL Resets ++ * enum sdca_xu_reset_mechanism - SDCA FDL Resets + */ + enum sdca_xu_reset_mechanism { + SDCA_XU_RESET_FUNCTION = 0x0, +@@ -1401,6 +1397,16 @@ struct sdca_fdl_data { + int num_sets; + }; + ++/** ++ * struct sdca_function_hid - information about a function's HID descriptors ++ * @report_desc: HID Report Descriptor for the HID Function ++ * @desc: HID descriptor for the HID Function ++ */ ++struct sdca_function_hid { ++ unsigned char *report_desc; ++ struct hid_descriptor desc; ++}; ++ + /** + * struct sdca_function_data - top-level information for one SDCA function + * @desc: Pointer to short descriptor from initial parsing. +@@ -1415,6 +1421,7 @@ struct sdca_fdl_data { + * @reset_max_delay: Maximum Function reset delay in microseconds, before an + * error should be reported. + * @fdl_data: FDL data for this Function, if available. ++ * @hid: HID data for this Function, if available. + */ + struct sdca_function_data { + struct sdca_function_desc *desc; +@@ -1430,6 +1437,10 @@ struct sdca_function_data { + unsigned int reset_max_delay; + + struct sdca_fdl_data fdl_data; ++ ++ union { ++ struct sdca_function_hid hid; ++ }; + }; + + static inline u32 sdca_range(struct sdca_control_range *range, +@@ -1451,8 +1462,7 @@ static inline u32 sdca_range_search(struct sdca_control_range *range, + return 0; + } + +-int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, +- struct sdca_function_data *function); ++int sdca_parse_function(struct device *dev, struct sdca_function_data *function); + + const char *sdca_find_terminal_name(enum sdca_terminal_type type); + +@@ -1469,5 +1479,7 @@ struct sdca_control_range *sdca_selector_find_range(struct device *dev, + struct sdca_cluster *sdca_id_find_cluster(struct device *dev, + struct sdca_function_data *function, + const int id); ++struct sdca_entity *sdca_find_entity_by_label(struct sdca_function_data *function, ++ const char *entity_label); + + #endif +diff --git a/include/sound/sdca_hid.h b/include/sound/sdca_hid.h +--- a/include/sound/sdca_hid.h ++++ b/include/sound/sdca_hid.h +@@ -16,18 +16,21 @@ struct sdca_interrupt; + + #if IS_ENABLED(CONFIG_SND_SOC_SDCA_HID) + +-int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, +- struct sdca_entity *entity); ++int sdca_add_hid_device(struct sdca_interrupt *interrupt); ++void sdca_destroy_hid_device(struct sdca_interrupt *interrupt); + int sdca_hid_process_report(struct sdca_interrupt *interrupt); + + #else + +-static inline int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, +- struct sdca_entity *entity) ++static inline int sdca_add_hid_device(struct sdca_interrupt *interrupt) + { + return 0; + } + ++static inline void sdca_destroy_hid_device(struct sdca_interrupt *interrupt) ++{ ++} ++ + static inline int sdca_hid_process_report(struct sdca_interrupt *interrupt) + { + return 0; +diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h +--- a/include/sound/simple_card_utils.h ++++ b/include/sound/simple_card_utils.h +@@ -181,7 +181,7 @@ void simple_util_canonicalize_platform(struct snd_soc_dai_link_component *platfo + void simple_util_canonicalize_cpu(struct snd_soc_dai_link_component *cpus, + int is_single_links); + +-void simple_util_clean_reference(struct snd_soc_card *card); ++void simple_util_clean_reference(struct simple_util_priv *priv); + + void simple_util_parse_convert(struct device_node *np, char *prefix, + struct simple_util_data *data); +@@ -189,18 +189,37 @@ bool simple_util_is_convert_required(const struct simple_util_data *data); + + int simple_util_get_sample_fmt(struct simple_util_data *data); + +-int simple_util_parse_routing(struct snd_soc_card *card, +- char *prefix); +-int simple_util_parse_widgets(struct snd_soc_card *card, +- char *prefix); +-int simple_util_parse_pin_switches(struct snd_soc_card *card, +- char *prefix); ++int simple_util_parse_property(struct simple_util_priv *priv, ++ int (*func)(struct snd_soc_card *card, const char *propname), ++ char *prefix, char *property); ++static inline int simple_util_parse_routing(struct simple_util_priv *priv, char *prefix) ++{ ++ return simple_util_parse_property(priv, snd_soc_of_parse_audio_routing, ++ prefix, "routing"); ++} ++ ++static inline int simple_util_parse_widgets(struct simple_util_priv *priv, char *prefix) ++{ ++ return simple_util_parse_property(priv, snd_soc_of_parse_audio_simple_widgets, ++ prefix, "widgets"); ++} ++ ++static inline int simple_util_parse_pin_switches(struct simple_util_priv *priv, char *prefix) ++{ ++ return simple_util_parse_property(priv, snd_soc_of_parse_pin_switches, ++ prefix, "pin-switches"); ++} ++ ++static inline int simple_util_parse_aux_devs(struct simple_util_priv *priv, char *prefix) ++{ ++ return simple_util_parse_property(priv, snd_soc_of_parse_aux_devs, ++ prefix, "aux-devs"); ++} + + int simple_util_init_jack(struct snd_soc_card *card, + struct simple_util_jack *sjack, + int is_hp, char *prefix, char *pin); +-int simple_util_init_aux_jacks(struct simple_util_priv *priv, +- char *prefix); ++int simple_util_init_aux_jacks(struct snd_soc_card *card, char *prefix); + int simple_util_init_priv(struct simple_util_priv *priv, + struct link_info *li); + void simple_util_remove(struct platform_device *pdev); +diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h +--- a/include/sound/soc-acpi.h ++++ b/include/sound/soc-acpi.h +@@ -57,7 +57,7 @@ static inline struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg) + #endif + + /** +- * snd_soc_acpi_mach_params: interface for machine driver configuration ++ * struct snd_soc_acpi_mach_params - interface for machine driver configuration + * + * @acpi_ipc_irq_index: used for BYT-CR detection + * @platform: string used for HDAudio codec support +@@ -93,7 +93,7 @@ struct snd_soc_acpi_mach_params { + }; + + /** +- * snd_soc_acpi_endpoint - endpoint descriptor ++ * struct snd_soc_acpi_endpoint - endpoint descriptor + * @num: endpoint number (mandatory, unique per device) + * @aggregated: 0 (independent) or 1 (logically grouped) + * @group_position: zero-based order (only when @aggregated is 1) +@@ -107,7 +107,7 @@ struct snd_soc_acpi_endpoint { + }; + + /** +- * snd_soc_acpi_adr_device - descriptor for _ADR-enumerated device ++ * struct snd_soc_acpi_adr_device - descriptor for _ADR-enumerated device + * @adr: 64 bit ACPI _ADR value + * @num_endpoints: number of endpoints for this device + * @endpoints: array of endpoints +@@ -121,7 +121,7 @@ struct snd_soc_acpi_adr_device { + }; + + /** +- * snd_soc_acpi_link_adr - ACPI-based list of _ADR enumerated devices ++ * struct snd_soc_acpi_link_adr - ACPI-based list of _ADR enumerated devices + * @mask: one bit set indicates the link this list applies to + * @num_adr: ARRAY_SIZE of devices + * @adr_d: array of devices +@@ -167,8 +167,8 @@ struct snd_soc_acpi_link_adr { + #define SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME BIT(4) + + /** +- * snd_soc_acpi_mach: ACPI-based machine descriptor. Most of the fields are +- * related to the hardware, except for the firmware and topology file names. ++ * struct snd_soc_acpi_mach - ACPI-based machine descriptor. Most of the fields ++ * are related to the hardware, except for the firmware and topology file names. + * A platform supported by legacy and Sound Open Firmware (SOF) would expose + * all firmware/topology related fields. + * +@@ -192,6 +192,7 @@ struct snd_soc_acpi_link_adr { + * the initial selection in the snd_soc_acpi_mach table. + * @pdata: intended for platform data or machine specific-ops. This structure + * is not constant since this field may be updated at run-time ++ * @mach_params: machine driver configuration + * @sof_tplg_filename: Sound Open Firmware topology file name, if enabled + * @tplg_quirk_mask: quirks to select different topology files dynamically + * @get_function_tplg_files: This is an optional callback, if specified then instead of +@@ -199,11 +200,11 @@ struct snd_soc_acpi_link_adr { + * files to be loaded. + * Return value: The number of the files or negative ERRNO. 0 means that the single topology + * file should be used, no function topology split can be used on the machine. +- * @card: the pointer of the card +- * @mach: the pointer of the machine driver +- * @prefix: the prefix of the topology file name. Typically, it is the path. +- * @tplg_files: the pointer of the array of the topology file names. +- * @best_effort: ignore non supported links and try to build the card in best effort ++ * card: the pointer of the card ++ * mach: the pointer of the machine driver ++ * prefix: the prefix of the topology file name. Typically, it is the path. ++ * tplg_files: the pointer of the array of the topology file names. ++ * best_effort: ignore non supported links and try to build the card in best effort + * with supported links + */ + /* Descriptor for SST ASoC machine driver */ +diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h +--- a/include/sound/soc-component.h ++++ b/include/sound/soc-component.h +@@ -253,6 +253,9 @@ struct snd_soc_component { + void *mark_pm; + + struct dentry *debugfs_root; ++ ++ /* Component private data */ ++ void *priv; + }; + + #define for_each_component_dais(component, dai)\ +@@ -283,6 +286,14 @@ static inline int snd_soc_component_cache_sync( + return regcache_sync(component->regmap); + } + ++struct snd_soc_component *snd_soc_component_alloc(struct device *dev); ++ ++void snd_soc_component_set_name(struct snd_soc_component *component, const char *name); ++const char *snd_soc_component_name(struct snd_soc_component *component); ++ ++void snd_soc_component_set_priv(struct snd_soc_component *component, void *priv); ++void *snd_soc_component_to_priv(struct snd_soc_component *component); ++ + void snd_soc_component_set_aux(struct snd_soc_component *component, + struct snd_soc_aux_dev *aux); + int snd_soc_component_init(struct snd_soc_component *component); +diff --git a/include/sound/soc.h b/include/sound/soc.h +--- a/include/sound/soc.h ++++ b/include/sound/soc.h +@@ -447,15 +447,16 @@ static inline int snd_soc_resume(struct device *dev) + } + #endif + int snd_soc_poweroff(struct device *dev); +-int snd_soc_component_initialize(struct snd_soc_component *component, +- const struct snd_soc_component_driver *driver, +- struct device *dev); +-int snd_soc_add_component(struct snd_soc_component *component, +- struct snd_soc_dai_driver *dai_drv, +- int num_dai); +-int snd_soc_register_component(struct device *dev, ++int snd_soc_register_component_c(struct snd_soc_component *component, + const struct snd_soc_component_driver *component_driver, + struct snd_soc_dai_driver *dai_drv, int num_dai); ++int snd_soc_register_component_d(struct device *dev, ++ const struct snd_soc_component_driver *component_driver, ++ struct snd_soc_dai_driver *dai_drv, int num_dai); ++#define snd_soc_register_component(x, ...) _Generic((x), \ ++struct device * : snd_soc_register_component_d, \ ++struct snd_soc_component * : snd_soc_register_component_c)(x, __VA_ARGS__) ++ + int devm_snd_soc_register_component(struct device *dev, + const struct snd_soc_component_driver *component_driver, + struct snd_soc_dai_driver *dai_drv, int num_dai); +diff --git a/include/sound/sof/dai-amd.h b/include/sound/sof/dai-amd.h +--- a/include/sound/sof/dai-amd.h ++++ b/include/sound/sof/dai-amd.h +@@ -18,6 +18,7 @@ struct sof_ipc_dai_acp_params { + uint32_t fsync_rate; /* FSYNC frequency in Hz */ + uint32_t tdm_slots; + uint32_t tdm_mode; ++ uint32_t format; + } __packed; + + /* ACPDMIC Configuration Request - SOF_IPC_DAI_AMD_CONFIG */ +diff --git a/include/sound/sof/dai.h b/include/sound/sof/dai.h +--- a/include/sound/sof/dai.h ++++ b/include/sound/sof/dai.h +@@ -90,6 +90,8 @@ enum sof_ipc_dai_type { + SOF_DAI_AMD_HS_VIRTUAL, /**< AMD ACP HS VIRTUAL */ + SOF_DAI_IMX_MICFIL, /** < i.MX MICFIL PDM */ + SOF_DAI_AMD_SDW, /**< AMD ACP SDW */ ++ SOF_DAI_INTEL_UAOL, /**< Intel UAOL */ ++ SOF_DAI_AMD_I2S, /**< AMD ACP I2S */ + }; + + /* general purpose DAI configuration */ +@@ -121,6 +123,7 @@ struct sof_ipc_dai_config { + struct sof_ipc_dai_mtk_afe_params afe; + struct sof_ipc_dai_micfil_params micfil; + struct sof_ipc_dai_acp_sdw_params acp_sdw; ++ struct sof_ipc_dai_acp_params acp_i2s; + }; + } __packed; + +diff --git a/include/sound/sof/header.h b/include/sound/sof/header.h +--- a/include/sound/sof/header.h ++++ b/include/sound/sof/header.h +@@ -179,7 +179,7 @@ struct sof_ipc_compound_hdr { + uint32_t count; /**< count of 0 means end of compound sequence */ + } __packed; + +-/** ++/* + * OOPS header architecture specific data. + */ + struct sof_ipc_dsp_oops_arch_hdr { +@@ -187,7 +187,7 @@ struct sof_ipc_dsp_oops_arch_hdr { + uint32_t totalsize; /* Total size of oops message */ + } __packed; + +-/** ++/* + * OOPS header platform specific data. + */ + struct sof_ipc_dsp_oops_plat_hdr { +diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h +--- a/include/sound/sof/ipc4/header.h ++++ b/include/sound/sof/ipc4/header.h +@@ -187,6 +187,10 @@ enum sof_ipc4_pipeline_state { + #define SOF_IPC4_GLB_PIPE_EXT_CORE_ID_MASK GENMASK(23, 20) + #define SOF_IPC4_GLB_PIPE_EXT_CORE_ID(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_CORE_ID_SHIFT) + ++#define SOF_IPC4_GLB_PIPE_PAYLOAD_SHIFT 29 ++#define SOF_IPC4_GLB_PIPE_PAYLOAD_MASK BIT(29) ++#define SOF_IPC4_GLB_PIPE_PAYLOAD(x) ((x) << SOF_IPC4_GLB_PIPE_PAYLOAD_SHIFT) ++ + /* pipeline set state ipc msg */ + #define SOF_IPC4_GLB_PIPE_STATE_ID_SHIFT 16 + #define SOF_IPC4_GLB_PIPE_STATE_ID_MASK GENMASK(23, 16) +@@ -536,12 +540,63 @@ enum sof_ipc4_notification_type { + SOF_IPC4_NOTIFY_TYPE_LAST, + }; + ++enum sof_ipc4_resource_type { ++ SOF_IPC4_MODULE_INSTANCE, ++ SOF_IPC4_PIPELINE, ++ SOF_IPC4_GATEWAY, ++ SOF_IPC4_EDF_TASK, ++ SOF_IPC4_INVALID_RESOURCE_TYPE, ++}; ++ ++enum sof_ipc4_event_type { ++ /* Underrun detected by the Mixer */ ++ SOF_IPC4_MIXER_UNDERRUN_DETECTED = 1, ++ /* Error caught during data processing */ ++ SOF_IPC4_PROCESS_DATA_ERROR = 3, ++ /* Underrun detected by gateway. */ ++ SOF_IPC4_GATEWAY_UNDERRUN_DETECTED = 6, ++ /* Overrun detected by gateway */ ++ SOF_IPC4_GATEWAY_OVERRUN_DETECTED, ++}; ++ ++/** ++ * struct sof_ipc4_process_data_error_event_data - process data error event payload ++ * @error_code: Error code returned by data processing function ++ */ ++struct sof_ipc4_process_data_error_event_data { ++ uint32_t error_code; ++}; ++ ++/** ++ * struct sof_ipc4_mixer_underrun_event_data - mixer underrun event payload ++ * @eos_flag: Indicates EndOfStream ++ * @data_mixed: Data processed by module (in bytes) ++ * @expected_data_mixed: Expected data to be processed (in bytes) ++ */ ++struct sof_ipc4_mixer_underrun_event_data { ++ uint32_t eos_flag; ++ uint32_t data_mixed; ++ uint32_t expected_data_mixed; ++}; ++ ++/** ++ * union sof_ipc4_resource_event_data - resource event specific payload ++ * @dws: Raw event data payload as six dwords ++ * @process_data_error: SOF_IPC4_PROCESS_DATA_ERROR payload ++ * @mixer_underrun: SOF_IPC4_MIXER_UNDERRUN_DETECTED payload ++ */ ++union sof_ipc4_resource_event_data { ++ uint32_t dws[6]; ++ struct sof_ipc4_process_data_error_event_data process_data_error; ++ struct sof_ipc4_mixer_underrun_event_data mixer_underrun; ++}; ++ + struct sof_ipc4_notify_resource_data { + uint32_t resource_type; + uint32_t resource_id; + uint32_t event_type; + uint32_t reserved; +- uint32_t data[6]; ++ union sof_ipc4_resource_event_data data; + } __packed __aligned(4); + + #define SOF_IPC4_DEBUG_DESCRIPTOR_SIZE 12 /* 3 x u32 */ +@@ -654,11 +709,81 @@ enum sof_ipc4_mod_init_ext_obj_id { + SOF_IPC4_MOD_INIT_DATA_ID_MAX = SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA, + }; + +-/* DP module memory configuration data object for ext_init object array */ ++/* DP module memory configuration data object for object array */ + struct sof_ipc4_mod_init_ext_dp_memory_data { ++ u32 domain_id; /* userspace domain ID */ ++ u32 stack_bytes; /* required stack size in bytes */ ++ u32 heap_bytes; /* required heap size in bytes */ ++} __packed __aligned(4); ++ ++/* ++ * This set of macros are very similar to the set above, but these are ++ * for building payload to SOF_IPC4_GLB_CREATE_PIPELINE message. ++ * ++ * Macros for creating struct sof_ipc4_glb_pipe_payload payload with ++ * its associated data. struct sof_ipc4_glb_pipe_payload should be the ++ * first piece of payload following SOF_IPC4_GLB_CREATE_PIPELINE msg, ++ * and its existence is indicated with SOF_IPC4_GLB_PIPE_PAYLOAD bit. ++ * ++ * The macros below apply to sof_ipc4_glb_pipe_payload.word0 ++ */ ++#define SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS_SHIFT 0 ++#define SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS_MASK GENMASK(23, 0) ++#define SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS(x) ((x) << SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS_SHIFT) ++ ++#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_SHIFT 24 ++#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_MASK BIT(24) ++#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_SHIFT) ++ ++struct sof_ipc4_glb_pipe_payload { ++ u32 word0; ++ u32 rsvd1; ++ u32 rsvd2; ++} __packed __aligned(4); ++ ++/* ++ * SOF_IPC4_GLB_CREATE_PIPELINE payload may be followed by arbitrary ++ * number of object array objects. SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY-bit ++ * indicates that an array object follows struct ++ * sof_ipc4_glb_pipe_payload. ++ * ++ * The object header's SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST-bit in struct ++ * sof_ipc4_glb_pipe_ext_object indicates if the array is continued ++ * with another object. The header has also fields to identify the ++ * object, SOF_IPC4_GLB_PIPE_EXT_OBJ_ID, and to indicate the object's ++ * size in 32-bit words, SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS, not ++ * including the header itself. ++ * ++ * The macros below apply to sof_ipc4_glb_pipe_ext_object.header ++ */ ++#define SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_SHIFT 0 ++#define SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_MASK BIT(0) ++#define SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_SHIFT) ++ ++#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ID_SHIFT 1 ++#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ID_MASK GENMASK(15, 1) ++#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ID(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_ID_SHIFT) ++ ++#define SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS_SHIFT 16 ++#define SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS_MASK GENMASK(31, 16) ++#define SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS_SHIFT) ++ ++struct sof_ipc4_glb_pipe_ext_object { ++ u32 header; ++ u32 data[]; ++} __packed __aligned(4); ++ ++enum sof_ipc4_glb_pipe_ext_obj_id { ++ SOF_IPC4_GLB_PIPE_DATA_ID_INVALID = 0, ++ SOF_IPC4_GLB_PIPE_DATA_ID_MEM_DATA, ++ SOF_IPC4_GLB_PIPE_DATA_ID_MAX = SOF_IPC4_GLB_PIPE_DATA_ID_MEM_DATA, ++}; ++ ++/* Pipeline memory configuration data object for ext_init object array */ ++struct sof_ipc4_glb_pipe_ext_obj_memory_data { + u32 domain_id; /* userspace domain ID */ +- u32 stack_bytes; /* stack size in bytes, 0 means default size */ +- u32 heap_bytes; /* stack size in bytes, 0 means default size */ ++ u32 stack_bytes; /* stack size in bytes */ ++ u32 heap_bytes; /* heap size in bytes */ + } __packed __aligned(4); + + /** @}*/ +diff --git a/include/sound/tas2781-dsp.h b/include/sound/tas2781-dsp.h +--- a/include/sound/tas2781-dsp.h ++++ b/include/sound/tas2781-dsp.h +@@ -201,6 +201,11 @@ struct tasdevice_rca { + int ncfgs; + struct tasdevice_config_info **cfg_info; + int profile_cfg_id; ++ /* ++ * Used among SmartAMP for PDM microphone recording or IV data ++ * capture. ++ */ ++ int capture_profile_id; + /* + * Since version 0x105, the keyword 'init' was introduced into the + * profile, which is used for chip initialization, particularly to +@@ -222,7 +227,7 @@ void tasdevice_calbin_remove(void *context); + int tasdevice_select_tuningprm_cfg(void *context, int prm, + int cfg_no, int rca_conf_no); + int tasdevice_prmg_load(void *context, int prm_no); +-void tasdevice_tuning_switch(void *context, int state); ++void tasdevice_tuning_switch(void *context, int state, bool is_cap); + int tas2781_load_calibration(void *context, char *file_name, + unsigned short i); + +diff --git a/include/sound/tlv320aic32x4.h b/include/sound/tlv320aic32x4.h +deleted file mode 100644 +--- a/include/sound/tlv320aic32x4.h ++++ /dev/null +@@ -1,43 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-only */ +-/* +- * tlv320aic32x4.h -- TLV320AIC32X4 Soc Audio driver platform data +- * +- * Copyright 2011 Vista Silicon S.L. +- * +- * Author: Javier Martin +- */ +- +-#ifndef _AIC32X4_PDATA_H +-#define _AIC32X4_PDATA_H +- +-#define AIC32X4_PWR_MICBIAS_2075_LDOIN 0x00000001 +-#define AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE 0x00000002 +-#define AIC32X4_PWR_AIC32X4_LDO_ENABLE 0x00000004 +-#define AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36 0x00000008 +-#define AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED 0x00000010 +- +-#define AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K 0x00000001 +-#define AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K 0x00000002 +- +-/* GPIO API */ +-#define AIC32X4_MFPX_DEFAULT_VALUE 0xff +- +-#define AIC32X4_MFP1_DIN_DISABLED 0 +-#define AIC32X4_MFP1_DIN_ENABLED 0x2 +-#define AIC32X4_MFP1_GPIO_IN 0x4 +- +-#define AIC32X4_MFP2_GPIO_OUT_LOW 0x0 +-#define AIC32X4_MFP2_GPIO_OUT_HIGH 0x1 +- +-#define AIC32X4_MFP_GPIO_ENABLED 0x4 +- +-#define AIC32X4_MFP5_GPIO_DISABLED 0x0 +-#define AIC32X4_MFP5_GPIO_INPUT 0x8 +-#define AIC32X4_MFP5_GPIO_OUTPUT 0xc +-#define AIC32X4_MFP5_GPIO_OUT_LOW 0x0 +-#define AIC32X4_MFP5_GPIO_OUT_HIGH 0x1 +- +-struct aic32x4_setup_data { +- unsigned int gpio_func[5]; +-}; +-#endif +diff --git a/include/sound/wm8904.h b/include/sound/wm8904.h +--- a/include/sound/wm8904.h ++++ b/include/sound/wm8904.h +@@ -116,7 +116,7 @@ + #define WM8904_DRC_REGS 4 + #define WM8904_EQ_REGS 24 + +-/** ++/* + * DRC configurations are specified with a label and a set of register + * values to write (the enable bits will be ignored). At runtime an + * enumerated control will be presented for each DRC block allowing +@@ -131,7 +131,7 @@ struct wm8904_drc_cfg { + u16 regs[WM8904_DRC_REGS]; + }; + +-/** ++/* + * ReTune Mobile configurations are specified with a label, sample + * rate and set of values to write (the enable bits will be ignored). + * +diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h +deleted file mode 100644 +--- a/include/uapi/sound/skl-tplg-interface.h ++++ /dev/null +@@ -1,168 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +-/* +- * skl-tplg-interface.h - Intel DSP FW private data interface +- * +- * Copyright (C) 2015 Intel Corp +- * Author: Jeeja KP +- * Nilofer, Samreen +- */ +- +-#ifndef __HDA_TPLG_INTERFACE_H__ +-#define __HDA_TPLG_INTERFACE_H__ +- +-#include +- +-/* +- * Default types range from 0~12. type can range from 0 to 0xff +- * SST types start at higher to avoid any overlapping in future +- */ +-#define SKL_CONTROL_TYPE_BYTE_TLV 0x100 +-#define SKL_CONTROL_TYPE_MIC_SELECT 0x102 +-#define SKL_CONTROL_TYPE_MULTI_IO_SELECT 0x103 +-#define SKL_CONTROL_TYPE_MULTI_IO_SELECT_DMIC 0x104 +- +-#define HDA_SST_CFG_MAX 900 /* size of copier cfg*/ +-#define MAX_IN_QUEUE 8 +-#define MAX_OUT_QUEUE 8 +- +-#define SKL_UUID_STR_SZ 40 +-/* Event types goes here */ +-/* Reserve event type 0 for no event handlers */ +-enum skl_event_types { +- SKL_EVENT_NONE = 0, +- SKL_MIXER_EVENT, +- SKL_MUX_EVENT, +- SKL_VMIXER_EVENT, +- SKL_PGA_EVENT +-}; +- +-/** +- * enum skl_ch_cfg - channel configuration +- * +- * @SKL_CH_CFG_MONO: One channel only +- * @SKL_CH_CFG_STEREO: L & R +- * @SKL_CH_CFG_2_1: L, R & LFE +- * @SKL_CH_CFG_3_0: L, C & R +- * @SKL_CH_CFG_3_1: L, C, R & LFE +- * @SKL_CH_CFG_QUATRO: L, R, Ls & Rs +- * @SKL_CH_CFG_4_0: L, C, R & Cs +- * @SKL_CH_CFG_5_0: L, C, R, Ls & Rs +- * @SKL_CH_CFG_5_1: L, C, R, Ls, Rs & LFE +- * @SKL_CH_CFG_DUAL_MONO: One channel replicated in two +- * @SKL_CH_CFG_I2S_DUAL_STEREO_0: Stereo(L,R) in 4 slots, 1st stream:[ L, R, -, - ] +- * @SKL_CH_CFG_I2S_DUAL_STEREO_1: Stereo(L,R) in 4 slots, 2nd stream:[ -, -, L, R ] +- * @SKL_CH_CFG_INVALID: Invalid +- */ +-enum skl_ch_cfg { +- SKL_CH_CFG_MONO = 0, +- SKL_CH_CFG_STEREO = 1, +- SKL_CH_CFG_2_1 = 2, +- SKL_CH_CFG_3_0 = 3, +- SKL_CH_CFG_3_1 = 4, +- SKL_CH_CFG_QUATRO = 5, +- SKL_CH_CFG_4_0 = 6, +- SKL_CH_CFG_5_0 = 7, +- SKL_CH_CFG_5_1 = 8, +- SKL_CH_CFG_DUAL_MONO = 9, +- SKL_CH_CFG_I2S_DUAL_STEREO_0 = 10, +- SKL_CH_CFG_I2S_DUAL_STEREO_1 = 11, +- SKL_CH_CFG_7_1 = 12, +- SKL_CH_CFG_4_CHANNEL = SKL_CH_CFG_7_1, +- SKL_CH_CFG_INVALID +-}; +- +-enum skl_module_type { +- SKL_MODULE_TYPE_MIXER = 0, +- SKL_MODULE_TYPE_COPIER, +- SKL_MODULE_TYPE_UPDWMIX, +- SKL_MODULE_TYPE_SRCINT, +- SKL_MODULE_TYPE_ALGO, +- SKL_MODULE_TYPE_BASE_OUTFMT, +- SKL_MODULE_TYPE_KPB, +- SKL_MODULE_TYPE_MIC_SELECT, +-}; +- +-enum skl_core_affinity { +- SKL_AFFINITY_CORE_0 = 0, +- SKL_AFFINITY_CORE_1, +- SKL_AFFINITY_CORE_MAX +-}; +- +-enum skl_pipe_conn_type { +- SKL_PIPE_CONN_TYPE_NONE = 0, +- SKL_PIPE_CONN_TYPE_FE, +- SKL_PIPE_CONN_TYPE_BE +-}; +- +-enum skl_hw_conn_type { +- SKL_CONN_NONE = 0, +- SKL_CONN_SOURCE = 1, +- SKL_CONN_SINK = 2 +-}; +- +-enum skl_dev_type { +- SKL_DEVICE_BT = 0x0, +- SKL_DEVICE_DMIC = 0x1, +- SKL_DEVICE_I2S = 0x2, +- SKL_DEVICE_SLIMBUS = 0x3, +- SKL_DEVICE_HDALINK = 0x4, +- SKL_DEVICE_HDAHOST = 0x5, +- SKL_DEVICE_NONE +-}; +- +-/** +- * enum skl_interleaving - interleaving style +- * +- * @SKL_INTERLEAVING_PER_CHANNEL: [s1_ch1...s1_chN,...,sM_ch1...sM_chN] +- * @SKL_INTERLEAVING_PER_SAMPLE: [s1_ch1...sM_ch1,...,s1_chN...sM_chN] +- */ +-enum skl_interleaving { +- SKL_INTERLEAVING_PER_CHANNEL = 0, +- SKL_INTERLEAVING_PER_SAMPLE = 1, +-}; +- +-enum skl_sample_type { +- SKL_SAMPLE_TYPE_INT_MSB = 0, +- SKL_SAMPLE_TYPE_INT_LSB = 1, +- SKL_SAMPLE_TYPE_INT_SIGNED = 2, +- SKL_SAMPLE_TYPE_INT_UNSIGNED = 3, +- SKL_SAMPLE_TYPE_FLOAT = 4 +-}; +- +-enum module_pin_type { +- /* All pins of the module takes same PCM inputs or outputs +- * e.g. mixout +- */ +- SKL_PIN_TYPE_HOMOGENEOUS, +- /* All pins of the module takes different PCM inputs or outputs +- * e.g mux +- */ +- SKL_PIN_TYPE_HETEROGENEOUS, +-}; +- +-enum skl_module_param_type { +- SKL_PARAM_DEFAULT = 0, +- SKL_PARAM_INIT, +- SKL_PARAM_SET, +- SKL_PARAM_BIND +-}; +- +-struct skl_dfw_algo_data { +- __u32 set_params:2; +- __u32 rsvd:30; +- __u32 param_id; +- __u32 max; +- char params[]; +-} __packed; +- +-enum skl_tkn_dir { +- SKL_DIR_IN, +- SKL_DIR_OUT +-}; +- +-enum skl_tuple_type { +- SKL_TYPE_TUPLE, +- SKL_TYPE_DATA +-}; +- +-#endif +diff --git a/include/uapi/sound/snd_ar_tokens.h b/include/uapi/sound/snd_ar_tokens.h +--- a/include/uapi/sound/snd_ar_tokens.h ++++ b/include/uapi/sound/snd_ar_tokens.h +@@ -58,7 +58,7 @@ enum ar_event_types { + #define SND_SOC_AR_TPLG_FE_BE_GRAPH_CTL_MIX 256 + #define SND_SOC_AR_TPLG_VOL_CTL 257 + +-/** ++/* + * %AR_TKN_U32_SUB_GRAPH_INSTANCE_ID: Sub Graph Instance Id + * + * %AR_TKN_U32_SUB_GRAPH_PERF_MODE: Performance mode of subgraph +@@ -168,6 +168,60 @@ enum ar_event_types { + * LOG_WAIT = 0, + * LOG_IMMEDIATELY = 1 + * ++ * %AR_TKN_U16_MODULE_SYNC_SRC: Frame sync source ++ * AR_AUDIO_IF_SYNC_SRC_EXTERNAL = 0, ++ * AR_AUDIO_IF_SYNC_SRC_INTERNAL = 1 ++ * ++ * %AR_TKN_U16_MODULE_CTRL_DATA_OUT_ENABLE: Enable data-out tri-state control ++ * AR_AUDIO_IF_CTRL_DATA_OE_DISABLE = 0, ++ * AR_AUDIO_IF_CTRL_DATA_OE_ENABLE = 1 ++ * ++ * %AR_TKN_U32_MODULE_SLOT_MASK: Active TDM slot bitmask ++ * ++ * %AR_TKN_U16_MODULE_NSLOTS_PER_FRAME: Number of slots per TDM frame ++ * ++ * %AR_TKN_U16_MODULE_SLOT_WIDTH: Slot width in bits (16 or 32) ++ * ++ * %AR_TKN_U16_MODULE_SYNC_MODE: Frame sync mode ++ * AR_AUDIO_IF_FRAME_SYNC_MODE_SHORT = 0, ++ * AR_AUDIO_IF_FRAME_SYNC_MODE_ONE_SLOT = 1, ++ * AR_AUDIO_IF_FRAME_SYNC_MODE_LONG = 2 ++ * ++ * %AR_TKN_U16_MODULE_CTRL_INVERT_SYNC_PULSE: Invert frame sync pulse polarity ++ * AR_AUDIO_IF_SYNC_NORMAL = 0, ++ * AR_AUDIO_IF_SYNC_INVERTED = 1 ++ * ++ * %AR_TKN_U16_MODULE_CTRL_SYNC_DATA_DELAY: Data delay relative to frame sync ++ * AR_AUDIO_IF_DATA_DELAY_NONE = 0, ++ * AR_AUDIO_IF_DATA_DELAY_1_CYCLE = 1, ++ * AR_AUDIO_IF_DATA_DELAY_2_CYCLE = 2 ++ * ++ * %AR_TKN_U16_MODULE_INTF_MODE: Audio IF interface mode ++ * AR_AUDIO_IF_INTF_MODE_TDM = 0, ++ * AR_AUDIO_IF_INTF_MODE_PCM = 1, ++ * AR_AUDIO_IF_INTF_MODE_I2S = 2 ++ * ++ * %AR_TKN_U16_MODULE_QAIF_TYPE: QAIF hardware port type index ++ * AR_AUDIO_IF_QAIF = 0, ++ * AR_AUDIO_IF_QAIF_VA = 1 ++ * ++ * %AR_TKN_U32_MODULE_ACTIVE_LANE_MASK: Active lane bitmask for multi-lane ++ * ++ * %AR_TKN_U32_MODULE_FRAME_SYNC_RATE: Frame sync rate in Hz ++ * ++ * %AR_TKN_U16_MODULE_BIT_CLK_TYPE: Bit clock type ++ * AR_AUDIO_IF_BIT_CLK_INTERNAL = 0, ++ * AR_AUDIO_IF_BIT_CLK_EXTERNAL = 1, ++ * AR_AUDIO_IF_BIT_CLK_SKIP = 2 ++ * ++ * %AR_TKN_U8_MODULE_INV_INT_BIT_CLK: Invert internal bit clock ++ * AR_AUDIO_IF_CLK_NORMAL = 0, ++ * AR_AUDIO_IF_CLK_INVERTED = 1 ++ * ++ * %AR_TKN_U8_MODULE_INV_EXT_BIT_CLK: Invert external bit clock ++ * AR_AUDIO_IF_CLK_NORMAL = 0, ++ * AR_AUDIO_IF_CLK_INVERTED = 1 ++ * + * %AR_TKN_DAI_INDEX: dai index + * + */ +@@ -240,6 +294,45 @@ enum ar_event_types { + #define AR_TKN_U32_MODULE_LOG_TAP_POINT_ID 260 + #define AR_TKN_U32_MODULE_LOG_MODE 261 + ++#define AR_TKN_U16_MODULE_SYNC_SRC 262 ++#define AR_TKN_U16_MODULE_CTRL_DATA_OUT_ENABLE 263 ++#define AR_TKN_U32_MODULE_SLOT_MASK 264 ++#define AR_TKN_U16_MODULE_NSLOTS_PER_FRAME 265 ++#define AR_TKN_U16_MODULE_SLOT_WIDTH 266 ++#define AR_TKN_U16_MODULE_SYNC_MODE 267 ++#define AR_TKN_U16_MODULE_CTRL_INVERT_SYNC_PULSE 268 ++#define AR_TKN_U16_MODULE_CTRL_SYNC_DATA_DELAY 269 ++#define AR_TKN_U16_MODULE_INTF_MODE 270 ++#define AR_TKN_U16_MODULE_QAIF_TYPE 271 ++#define AR_TKN_U32_MODULE_ACTIVE_LANE_MASK 272 ++#define AR_TKN_U32_MODULE_FRAME_SYNC_RATE 273 ++#define AR_TKN_U16_MODULE_BIT_CLK_TYPE 274 ++#define AR_TKN_U8_MODULE_INV_INT_BIT_CLK 275 ++#define AR_TKN_U8_MODULE_INV_EXT_BIT_CLK 276 ++ ++#define AR_AUDIO_IF_SYNC_SRC_EXTERNAL 0 ++#define AR_AUDIO_IF_SYNC_SRC_INTERNAL 1 ++#define AR_AUDIO_IF_CTRL_DATA_OE_DISABLE 0 ++#define AR_AUDIO_IF_CTRL_DATA_OE_ENABLE 1 ++#define AR_AUDIO_IF_INTF_MODE_TDM 0 ++#define AR_AUDIO_IF_INTF_MODE_PCM 1 ++#define AR_AUDIO_IF_INTF_MODE_I2S 2 ++#define AR_AUDIO_IF_QAIF 0 ++#define AR_AUDIO_IF_QAIF_VA 1 ++#define AR_AUDIO_IF_FRAME_SYNC_MODE_SHORT 0 ++#define AR_AUDIO_IF_FRAME_SYNC_MODE_ONE_SLOT 1 ++#define AR_AUDIO_IF_FRAME_SYNC_MODE_LONG 2 ++#define AR_AUDIO_IF_SYNC_NORMAL 0 ++#define AR_AUDIO_IF_SYNC_INVERTED 1 ++#define AR_AUDIO_IF_DATA_DELAY_NONE 0 ++#define AR_AUDIO_IF_DATA_DELAY_1_CYCLE 1 ++#define AR_AUDIO_IF_DATA_DELAY_2_CYCLE 2 ++#define AR_AUDIO_IF_BIT_CLK_INTERNAL 0 ++#define AR_AUDIO_IF_BIT_CLK_EXTERNAL 1 ++#define AR_AUDIO_IF_BIT_CLK_SKIP 2 ++#define AR_AUDIO_IF_CLK_NORMAL 0 ++#define AR_AUDIO_IF_CLK_INVERTED 1 ++ + #define SND_SOC_AR_TPLG_MODULE_CFG_TYPE 0x01001006 + struct audioreach_module_priv_data { + __le32 size; /* size in bytes of the array, including all elements */ +diff --git a/include/uapi/sound/snd_sst_tokens.h b/include/uapi/sound/snd_sst_tokens.h +deleted file mode 100644 +--- a/include/uapi/sound/snd_sst_tokens.h ++++ /dev/null +@@ -1,324 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +-/* +- * snd_sst_tokens.h - Intel SST tokens definition +- * +- * Copyright (C) 2016 Intel Corp +- * Author: Shreyas NC +- */ +-#ifndef __SND_SST_TOKENS_H__ +-#define __SND_SST_TOKENS_H__ +- +-/** +- * %SKL_TKN_UUID: Module UUID +- * +- * %SKL_TKN_U8_BLOCK_TYPE: Type of the private data block.Can be: +- * tuples, bytes, short and words +- * +- * %SKL_TKN_U8_IN_PIN_TYPE: Input pin type, +- * homogenous=0, heterogenous=1 +- * +- * %SKL_TKN_U8_OUT_PIN_TYPE: Output pin type, +- * homogenous=0, heterogenous=1 +- * %SKL_TKN_U8_DYN_IN_PIN: Configure Input pin dynamically +- * if true +- * +- * %SKL_TKN_U8_DYN_OUT_PIN: Configure Output pin dynamically +- * if true +- * +- * %SKL_TKN_U8_IN_QUEUE_COUNT: Store the number of Input pins +- * +- * %SKL_TKN_U8_OUT_QUEUE_COUNT: Store the number of Output pins +- * +- * %SKL_TKN_U8_TIME_SLOT: TDM slot number +- * +- * %SKL_TKN_U8_CORE_ID: Stores module affinity value.Can take +- * the values: +- * SKL_AFFINITY_CORE_0 = 0, +- * SKL_AFFINITY_CORE_1, +- * SKL_AFFINITY_CORE_MAX +- * +- * %SKL_TKN_U8_MOD_TYPE: Module type value. +- * +- * %SKL_TKN_U8_CONN_TYPE: Module connection type can be a FE, +- * BE or NONE as defined : +- * SKL_PIPE_CONN_TYPE_NONE = 0, +- * SKL_PIPE_CONN_TYPE_FE = 1 (HOST_DMA) +- * SKL_PIPE_CONN_TYPE_BE = 2 (LINK_DMA) +- * +- * %SKL_TKN_U8_DEV_TYPE: Type of device to which the module is +- * connected +- * Can take the values: +- * SKL_DEVICE_BT = 0x0, +- * SKL_DEVICE_DMIC = 0x1, +- * SKL_DEVICE_I2S = 0x2, +- * SKL_DEVICE_SLIMBUS = 0x3, +- * SKL_DEVICE_HDALINK = 0x4, +- * SKL_DEVICE_HDAHOST = 0x5, +- * SKL_DEVICE_NONE +- * +- * %SKL_TKN_U8_HW_CONN_TYPE: Connection type of the HW to which the +- * module is connected +- * SKL_CONN_NONE = 0, +- * SKL_CONN_SOURCE = 1, +- * SKL_CONN_SINK = 2 +- * +- * %SKL_TKN_U16_PIN_INST_ID: Stores the pin instance id +- * +- * %SKL_TKN_U16_MOD_INST_ID: Stores the mdule instance id +- * +- * %SKL_TKN_U32_MAX_MCPS: Module max mcps value +- * +- * %SKL_TKN_U32_MEM_PAGES: Module resource pages +- * +- * %SKL_TKN_U32_OBS: Stores Output Buffer size +- * +- * %SKL_TKN_U32_IBS: Stores input buffer size +- * +- * %SKL_TKN_U32_VBUS_ID: Module VBUS_ID. PDM=0, SSP0=0, +- * SSP1=1,SSP2=2, +- * SSP3=3, SSP4=4, +- * SSP5=5, SSP6=6,INVALID +- * +- * %SKL_TKN_U32_PARAMS_FIXUP: Module Params fixup mask +- * %SKL_TKN_U32_CONVERTER: Module params converter mask +- * %SKL_TKN_U32_PIPE_ID: Stores the pipe id +- * +- * %SKL_TKN_U32_PIPE_CONN_TYPE: Type of the token to which the pipe is +- * connected to. It can be +- * SKL_PIPE_CONN_TYPE_NONE = 0, +- * SKL_PIPE_CONN_TYPE_FE = 1 (HOST_DMA), +- * SKL_PIPE_CONN_TYPE_BE = 2 (LINK_DMA), +- * +- * %SKL_TKN_U32_PIPE_PRIORITY: Pipe priority value +- * %SKL_TKN_U32_PIPE_MEM_PGS: Pipe resource pages +- * +- * %SKL_TKN_U32_DIR_PIN_COUNT: Value for the direction to set input/output +- * formats and the pin count. +- * The first 4 bits have the direction +- * value and the next 4 have +- * the pin count value. +- * SKL_DIR_IN = 0, SKL_DIR_OUT = 1. +- * The input and output formats +- * share the same set of tokens +- * with the distinction between input +- * and output made by reading direction +- * token. +- * +- * %SKL_TKN_U32_FMT_CH: Supported channel count +- * +- * %SKL_TKN_U32_FMT_FREQ: Supported frequency/sample rate +- * +- * %SKL_TKN_U32_FMT_BIT_DEPTH: Supported container size +- * +- * %SKL_TKN_U32_FMT_SAMPLE_SIZE:Number of samples in the container +- * +- * %SKL_TKN_U32_FMT_CH_CONFIG: Supported channel configurations for the +- * input/output. +- * +- * %SKL_TKN_U32_FMT_INTERLEAVE: Interleaving style which can be per +- * channel or per sample. The values can be : +- * SKL_INTERLEAVING_PER_CHANNEL = 0, +- * SKL_INTERLEAVING_PER_SAMPLE = 1, +- * +- * %SKL_TKN_U32_FMT_SAMPLE_TYPE: +- * Specifies the sample type. Can take the +- * values: SKL_SAMPLE_TYPE_INT_MSB = 0, +- * SKL_SAMPLE_TYPE_INT_LSB = 1, +- * SKL_SAMPLE_TYPE_INT_SIGNED = 2, +- * SKL_SAMPLE_TYPE_INT_UNSIGNED = 3, +- * SKL_SAMPLE_TYPE_FLOAT = 4 +- * +- * %SKL_TKN_U32_CH_MAP: Channel map values +- * %SKL_TKN_U32_MOD_SET_PARAMS: It can take these values: +- * SKL_PARAM_DEFAULT, SKL_PARAM_INIT, +- * SKL_PARAM_SET, SKL_PARAM_BIND +- * +- * %SKL_TKN_U32_MOD_PARAM_ID: ID of the module params +- * +- * %SKL_TKN_U32_CAPS_SET_PARAMS: +- * Set params value +- * +- * %SKL_TKN_U32_CAPS_PARAMS_ID: Params ID +- * +- * %SKL_TKN_U32_CAPS_SIZE: Caps size +- * +- * %SKL_TKN_U32_PROC_DOMAIN: Specify processing domain +- * +- * %SKL_TKN_U32_LIB_COUNT: Specifies the number of libraries +- * +- * %SKL_TKN_STR_LIB_NAME: Specifies the library name +- * +- * %SKL_TKN_U32_PMODE: Specifies the power mode for pipe +- * +- * %SKL_TKL_U32_D0I3_CAPS: Specifies the D0i3 capability for module +- * +- * %SKL_TKN_U32_DMA_BUF_SIZE: DMA buffer size in millisec +- * +- * %SKL_TKN_U32_PIPE_DIR: Specifies pipe direction. Can be +- * playback/capture. +- * +- * %SKL_TKN_U32_NUM_CONFIGS: Number of pipe configs +- * +- * %SKL_TKN_U32_PATH_MEM_PGS: Size of memory (in pages) required for pipeline +- * and its data +- * +- * %SKL_TKN_U32_PIPE_CONFIG_ID: Config id for the modules in the pipe +- * and PCM params supported by that pipe +- * config. This is used as index to fill +- * up the pipe config and module config +- * structure. +- * +- * %SKL_TKN_U32_CFG_FREQ: +- * %SKL_TKN_U8_CFG_CHAN: +- * %SKL_TKN_U8_CFG_BPS: PCM params (freq, channels, bits per sample) +- * supported for each of the pipe configs. +- * +- * %SKL_TKN_CFG_MOD_RES_ID: Module's resource index for each of the +- * pipe config +- * +- * %SKL_TKN_CFG_MOD_FMT_ID: Module's interface index for each of the +- * pipe config +- * +- * %SKL_TKN_U8_NUM_MOD: Number of modules in the manifest +- * +- * %SKL_TKN_MM_U8_MOD_IDX: Current index of the module in the manifest +- * +- * %SKL_TKN_MM_U8_NUM_RES: Number of resources for the module +- * +- * %SKL_TKN_MM_U8_NUM_INTF: Number of interfaces for the module +- * +- * %SKL_TKN_MM_U32_RES_ID: Resource index for the resource info to +- * be filled into. +- * A module can support multiple resource +- * configuration and is represnted as a +- * resource table. This index is used to +- * fill information into appropriate index. +- * +- * %SKL_TKN_MM_U32_CPS: DSP cycles per second +- * +- * %SKL_TKN_MM_U32_DMA_SIZE: Allocated buffer size for gateway DMA +- * +- * %SKL_TKN_MM_U32_CPC: DSP cycles allocated per frame +- * +- * %SKL_TKN_MM_U32_RES_PIN_ID: Resource pin index in the module +- * +- * %SKL_TKN_MM_U32_INTF_PIN_ID: Interface index in the module +- * +- * %SKL_TKN_MM_U32_PIN_BUF: Buffer size of the module pin +- * +- * %SKL_TKN_MM_U32_FMT_ID: Format index for each of the interface/ +- * format information to be filled into. +- * +- * %SKL_TKN_MM_U32_NUM_IN_FMT: Number of input formats +- * %SKL_TKN_MM_U32_NUM_OUT_FMT: Number of output formats +- * +- * %SKL_TKN_U32_ASTATE_IDX: Table Index for the A-State entry to be filled +- * with kcps and clock source +- * +- * %SKL_TKN_U32_ASTATE_COUNT: Number of valid entries in A-State table +- * +- * %SKL_TKN_U32_ASTATE_KCPS: Specifies the core load threshold (in kilo +- * cycles per second) below which DSP is clocked +- * from source specified by clock source. +- * +- * %SKL_TKN_U32_ASTATE_CLK_SRC: Clock source for A-State entry +- * +- * %SKL_TKN_U32_FMT_CFG_IDX: Format config index +- * +- * module_id and loadable flags dont have tokens as these values will be +- * read from the DSP FW manifest +- * +- * Tokens defined can be used either in the manifest or widget private data. +- * +- * SKL_TKN_MM is used as a suffix for all tokens that represent +- * module data in the manifest. +- */ +-enum SKL_TKNS { +- SKL_TKN_UUID = 1, +- SKL_TKN_U8_NUM_BLOCKS, +- SKL_TKN_U8_BLOCK_TYPE, +- SKL_TKN_U8_IN_PIN_TYPE, +- SKL_TKN_U8_OUT_PIN_TYPE, +- SKL_TKN_U8_DYN_IN_PIN, +- SKL_TKN_U8_DYN_OUT_PIN, +- SKL_TKN_U8_IN_QUEUE_COUNT, +- SKL_TKN_U8_OUT_QUEUE_COUNT, +- SKL_TKN_U8_TIME_SLOT, +- SKL_TKN_U8_CORE_ID, +- SKL_TKN_U8_MOD_TYPE, +- SKL_TKN_U8_CONN_TYPE, +- SKL_TKN_U8_DEV_TYPE, +- SKL_TKN_U8_HW_CONN_TYPE, +- SKL_TKN_U16_MOD_INST_ID, +- SKL_TKN_U16_BLOCK_SIZE, +- SKL_TKN_U32_MAX_MCPS, +- SKL_TKN_U32_MEM_PAGES, +- SKL_TKN_U32_OBS, +- SKL_TKN_U32_IBS, +- SKL_TKN_U32_VBUS_ID, +- SKL_TKN_U32_PARAMS_FIXUP, +- SKL_TKN_U32_CONVERTER, +- SKL_TKN_U32_PIPE_ID, +- SKL_TKN_U32_PIPE_CONN_TYPE, +- SKL_TKN_U32_PIPE_PRIORITY, +- SKL_TKN_U32_PIPE_MEM_PGS, +- SKL_TKN_U32_DIR_PIN_COUNT, +- SKL_TKN_U32_FMT_CH, +- SKL_TKN_U32_FMT_FREQ, +- SKL_TKN_U32_FMT_BIT_DEPTH, +- SKL_TKN_U32_FMT_SAMPLE_SIZE, +- SKL_TKN_U32_FMT_CH_CONFIG, +- SKL_TKN_U32_FMT_INTERLEAVE, +- SKL_TKN_U32_FMT_SAMPLE_TYPE, +- SKL_TKN_U32_FMT_CH_MAP, +- SKL_TKN_U32_PIN_MOD_ID, +- SKL_TKN_U32_PIN_INST_ID, +- SKL_TKN_U32_MOD_SET_PARAMS, +- SKL_TKN_U32_MOD_PARAM_ID, +- SKL_TKN_U32_CAPS_SET_PARAMS, +- SKL_TKN_U32_CAPS_PARAMS_ID, +- SKL_TKN_U32_CAPS_SIZE, +- SKL_TKN_U32_PROC_DOMAIN, +- SKL_TKN_U32_LIB_COUNT, +- SKL_TKN_STR_LIB_NAME, +- SKL_TKN_U32_PMODE, +- SKL_TKL_U32_D0I3_CAPS, /* Typo added at v4.10 */ +- SKL_TKN_U32_D0I3_CAPS = SKL_TKL_U32_D0I3_CAPS, +- SKL_TKN_U32_DMA_BUF_SIZE, +- +- SKL_TKN_U32_PIPE_DIRECTION, +- SKL_TKN_U32_PIPE_CONFIG_ID, +- SKL_TKN_U32_NUM_CONFIGS, +- SKL_TKN_U32_PATH_MEM_PGS, +- +- SKL_TKN_U32_CFG_FREQ, +- SKL_TKN_U8_CFG_CHAN, +- SKL_TKN_U8_CFG_BPS, +- SKL_TKN_CFG_MOD_RES_ID, +- SKL_TKN_CFG_MOD_FMT_ID, +- SKL_TKN_U8_NUM_MOD, +- +- SKL_TKN_MM_U8_MOD_IDX, +- SKL_TKN_MM_U8_NUM_RES, +- SKL_TKN_MM_U8_NUM_INTF, +- SKL_TKN_MM_U32_RES_ID, +- SKL_TKN_MM_U32_CPS, +- SKL_TKN_MM_U32_DMA_SIZE, +- SKL_TKN_MM_U32_CPC, +- SKL_TKN_MM_U32_RES_PIN_ID, +- SKL_TKN_MM_U32_INTF_PIN_ID, +- SKL_TKN_MM_U32_PIN_BUF, +- SKL_TKN_MM_U32_FMT_ID, +- SKL_TKN_MM_U32_NUM_IN_FMT, +- SKL_TKN_MM_U32_NUM_OUT_FMT, +- +- SKL_TKN_U32_ASTATE_IDX, +- SKL_TKN_U32_ASTATE_COUNT, +- SKL_TKN_U32_ASTATE_KCPS, +- SKL_TKN_U32_ASTATE_CLK_SRC, +- +- SKL_TKN_U32_FMT_CFG_IDX = 96, +- SKL_TKN_MAX = SKL_TKN_U32_FMT_CFG_IDX, +-}; +- +-#endif +diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h +--- a/include/uapi/sound/sof/tokens.h ++++ b/include/uapi/sound/sof/tokens.h +@@ -111,8 +111,8 @@ + + #define SOF_TKN_COMP_SCHED_DOMAIN 418 + #define SOF_TKN_COMP_DOMAIN_ID 419 +-#define SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT 420 +-#define SOF_TKN_COMP_STACK_BYTES_REQUIREMENT 421 ++#define SOF_TKN_COMP_STACK_BYTES_REQUIREMENT 420 ++#define SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT 421 + + /* SSP */ + #define SOF_TKN_INTEL_SSP_CLKS_CONTROL 500 +@@ -223,6 +223,7 @@ + #define SOF_TKN_AMD_ACPI2S_RATE 1700 + #define SOF_TKN_AMD_ACPI2S_CH 1701 + #define SOF_TKN_AMD_ACPI2S_TDM_MODE 1702 ++#define SOF_TKN_AMD_ACPI2S_FORMAT 1703 + + /* MICFIL PDM */ + #define SOF_TKN_IMX_MICFIL_RATE 2000 +diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c +--- a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c ++++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c +@@ -171,13 +171,13 @@ static void tas2781_hda_playback_hook(struct device *dev, int action) + case HDA_GEN_PCM_ACT_OPEN: + pm_runtime_get_sync(dev); + scoped_guard(mutex, &tas_hda->priv->codec_lock) { +- tasdevice_tuning_switch(tas_hda->priv, 0); ++ tasdevice_tuning_switch(tas_hda->priv, 0, false); + tas_hda->priv->playback_started = true; + } + break; + case HDA_GEN_PCM_ACT_CLOSE: + scoped_guard(mutex, &tas_hda->priv->codec_lock) { +- tasdevice_tuning_switch(tas_hda->priv, 1); ++ tasdevice_tuning_switch(tas_hda->priv, 1, false); + tas_hda->priv->playback_started = false; + } + +@@ -720,7 +720,7 @@ static int tas2781_runtime_suspend(struct device *dev) + * Stop the playback if it's unused. + */ + if (tas_hda->priv->playback_started) { +- tasdevice_tuning_switch(tas_hda->priv, 1); ++ tasdevice_tuning_switch(tas_hda->priv, 1, false); + tas_hda->priv->playback_started = false; + } + +@@ -750,7 +750,7 @@ static int tas2781_system_suspend(struct device *dev) + + /* Shutdown chip before system suspend */ + if (tas_hda->priv->playback_started) +- tasdevice_tuning_switch(tas_hda->priv, 1); ++ tasdevice_tuning_switch(tas_hda->priv, 1, false); + + /* + * Reset GPIO may be shared, so cannot reset here. +@@ -783,7 +783,7 @@ static int tas2781_system_resume(struct device *dev) + TASDEVICE_BIN_BLK_PRE_POWER_UP); + + if (tas_hda->priv->playback_started) +- tasdevice_tuning_switch(tas_hda->priv, 0); ++ tasdevice_tuning_switch(tas_hda->priv, 0, false); + + return 0; + } +diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c +--- a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c ++++ b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c +@@ -399,11 +399,11 @@ static void tas2781_hda_playback_hook(struct device *dev, int action) + pm_runtime_get_sync(dev); + guard(mutex)(&tas_priv->codec_lock); + if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK) +- tasdevice_tuning_switch(tas_hda->priv, 0); ++ tasdevice_tuning_switch(tas_hda->priv, 0, false); + } else if (action == HDA_GEN_PCM_ACT_CLOSE) { + guard(mutex)(&tas_priv->codec_lock); + if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK) +- tasdevice_tuning_switch(tas_priv, 1); ++ tasdevice_tuning_switch(tas_priv, 1, false); + pm_runtime_put_autosuspend(dev); + } + } +@@ -847,7 +847,7 @@ static int tas2781_runtime_suspend(struct device *dev) + + if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK + && tas_priv->playback_started) +- tasdevice_tuning_switch(tas_priv, 1); ++ tasdevice_tuning_switch(tas_priv, 1, false); + + tas_priv->tasdevice[tas_priv->index].cur_book = -1; + tas_priv->tasdevice[tas_priv->index].cur_conf = -1; +@@ -864,7 +864,7 @@ static int tas2781_runtime_resume(struct device *dev) + + if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK + && tas_priv->playback_started) +- tasdevice_tuning_switch(tas_priv, 0); ++ tasdevice_tuning_switch(tas_priv, 0, false); + + return 0; + } +@@ -882,7 +882,7 @@ static int tas2781_system_suspend(struct device *dev) + /* Shutdown chip before system suspend */ + if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK + && tas_priv->playback_started) +- tasdevice_tuning_switch(tas_priv, 1); ++ tasdevice_tuning_switch(tas_priv, 1, false); + + return 0; + } +@@ -917,7 +917,7 @@ static int tas2781_system_resume(struct device *dev) + tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK; + + if (tas_priv->playback_started) +- tasdevice_tuning_switch(tas_priv, 0); ++ tasdevice_tuning_switch(tas_priv, 0, false); + } + + return ret; +diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c +--- a/sound/soc/amd/acp-config.c ++++ b/sound/soc/amd/acp-config.c +@@ -63,6 +63,13 @@ static const struct dmi_system_id acp70_acpi_flag_override_table[] = { + DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook 18 M1807GA"), + }, + }, ++ { ++ /* HP OmniBook X Flip 14-kc0xxx (Strix Point, ACP 7.2) */ ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "HP"), ++ DMI_MATCH(DMI_BOARD_NAME, "8EA1"), ++ }, ++ }, + {} + }; + +@@ -379,5 +386,17 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[] = { + }; + EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sof_machines); + ++struct snd_soc_acpi_mach snd_soc_acpi_amd_acp7x_sof_machines[] = { ++ { ++ .id = "AMDI1010", ++ .drv_name = "acp7x-dsp", ++ .pdata = &acp_quirk_data, ++ .fw_filename = "sof-acp7x.ri", ++ .sof_tplg_filename = "sof-acp7x.tplg", ++ }, ++ {}, ++}; ++EXPORT_SYMBOL(snd_soc_acpi_amd_acp7x_sof_machines); ++ + MODULE_DESCRIPTION("AMD ACP Machine Configuration Module"); + MODULE_LICENSE("Dual BSD/GPL"); +diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c +--- a/sound/soc/amd/acp-pcm-dma.c ++++ b/sound/soc/amd/acp-pcm-dma.c +@@ -691,12 +691,10 @@ static irqreturn_t dma_irq_handler(int irq, void *arg) + { + u16 dscr_idx; + u32 intr_flag, ext_intr_status; +- struct audio_drv_data *irq_data; ++ struct audio_drv_data *irq_data = arg; + void __iomem *acp_mmio; +- struct device *dev = arg; + bool valid_irq = false; + +- irq_data = dev_get_drvdata(dev); + acp_mmio = irq_data->acp_mmio; + + ext_intr_status = acp_reg_read(acp_mmio, mmACP_EXTERNAL_INTR_STAT); +@@ -1291,10 +1289,10 @@ static int acp_audio_probe(struct platform_device *pdev) + + irq = platform_get_irq(pdev, 0); + if (irq < 0) +- return -ENODEV; ++ return irq; + + status = devm_request_irq(&pdev->dev, irq, dma_irq_handler, +- 0, "ACP_IRQ", &pdev->dev); ++ 0, "ACP_IRQ", audio_drv_data); + if (status) { + dev_err(&pdev->dev, "ACP IRQ request failed\n"); + return status; +diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c +--- a/sound/soc/amd/acp/acp-i2s.c ++++ b/sound/soc/amd/acp/acp-i2s.c +@@ -686,6 +686,10 @@ static int acp_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_d + return 0; + } + ++static const u64 acp_i2s_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A; ++ + const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = { + .startup = acp_i2s_startup, + .hw_params = acp_i2s_hwparams, +@@ -693,6 +697,8 @@ const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = { + .trigger = acp_i2s_trigger, + .set_fmt = acp_i2s_set_fmt, + .set_tdm_slot = acp_i2s_set_tdm_slot, ++ .auto_selectable_formats = &acp_i2s_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + EXPORT_SYMBOL_NS_GPL(asoc_acp_cpu_dai_ops, "SND_SOC_ACP_COMMON"); + +diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c +--- a/sound/soc/amd/acp/acp-mach-common.c ++++ b/sound/soc/amd/acp/acp-mach-common.c +@@ -938,15 +938,10 @@ static int acp_max98388_hw_params(struct snd_pcm_substream *substream, + struct snd_soc_dai *codec_dai = + snd_soc_card_get_codec_dai(card, + MAX98388_CODEC_DAI); +- int ret; + +- ret = snd_soc_dai_set_fmt(codec_dai, +- SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_I2S | +- SND_SOC_DAIFMT_NB_NF); +- if (ret < 0) +- return ret; +- +- return ret; ++ return snd_soc_dai_set_fmt(codec_dai, ++ SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_I2S | ++ SND_SOC_DAIFMT_NB_NF); + } + + static const struct snd_soc_ops acp_max98388_ops = { +diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c +--- a/sound/soc/amd/acp/acp-pci.c ++++ b/sound/soc/amd/acp/acp-pci.c +@@ -118,17 +118,10 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id + if (!chip) + return -ENOMEM; + +- if (pci_enable_device(pci)) ++ if (pcim_enable_device(pci)) + return dev_err_probe(&pci->dev, -ENODEV, + "pci_enable_device failed\n"); + +- ret = pci_request_regions(pci, "AMD ACP3x audio"); +- if (ret < 0) { +- dev_err(&pci->dev, "pci_request_regions failed\n"); +- ret = -ENOMEM; +- goto disable_pci; +- } +- + pci_set_master(pci); + + chip->acp_rev = pci->revision; +@@ -161,24 +154,21 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id + break; + default: + dev_err(dev, "Unsupported device revision:0x%x\n", pci->revision); +- ret = -EINVAL; +- goto release_regions; ++ return -EINVAL; + } + chip->flag = flag; + + addr = pci_resource_start(pci, 0); +- chip->base = devm_ioremap(&pci->dev, addr, pci_resource_len(pci, 0)); +- if (!chip->base) { +- ret = -ENOMEM; +- goto release_regions; +- } ++ chip->base = pcim_iomap_region(pci, 0, "AMD ACP3x audio"); ++ if (IS_ERR(chip->base)) ++ return PTR_ERR(chip->base); + + chip->addr = addr; + + chip->acp_hw_ops_init(chip); + ret = acp_hw_init(chip); + if (ret) +- goto release_regions; ++ goto de_init; + + ret = devm_request_irq(dev, pci->irq, irq_handler, + IRQF_SHARED, "ACP_I2S_IRQ", chip); +@@ -214,10 +204,6 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id + + de_init: + acp_hw_deinit(chip); +-release_regions: +- pci_release_regions(pci); +-disable_pci: +- pci_disable_device(pci); + + return ret; + }; +diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c +--- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c ++++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c +@@ -531,15 +531,15 @@ static int mc_probe(struct platform_device *pdev) + dmi_check_system(soc_sdw_quirk_table); + + if (quirk_override != -1) { +- dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n", ++ dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n", + soc_sdw_quirk, quirk_override); + soc_sdw_quirk = quirk_override; + } + +- log_quirks(card->dev); ++ log_quirks(&pdev->dev); + + ctx->mc_quirk = soc_sdw_quirk; +- dev_dbg(card->dev, "legacy quirk 0x%lx\n", ctx->mc_quirk); ++ dev_dbg(&pdev->dev, "legacy quirk 0x%lx\n", ctx->mc_quirk); + /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ + for (i = 0; i < ctx->codec_info_list_count; i++) + codec_info_list[i].amp_num = 0; +@@ -556,12 +556,12 @@ static int mc_probe(struct platform_device *pdev) + for (i = 0; i < ctx->codec_info_list_count; i++) + amp_num += codec_info_list[i].amp_num; + +- card->components = devm_kasprintf(card->dev, GFP_KERNEL, ++ card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, + " cfg-amp:%d", amp_num); + if (!card->components) + return -ENOMEM; + if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC) { +- card->components = devm_kasprintf(card->dev, GFP_KERNEL, ++ card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, + "%s mic:acp-dmic cfg-mics:%d", + card->components, + 1); +@@ -570,9 +570,9 @@ static int mc_probe(struct platform_device *pdev) + } + + /* Register the card */ +- ret = devm_snd_soc_register_card(card->dev, card); ++ ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) { +- dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret); ++ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed %d\n", ret); + asoc_sdw_mc_dailink_exit_loop(card); + return ret; + } +@@ -590,8 +590,8 @@ static void mc_remove(struct platform_device *pdev) + } + + static const struct platform_device_id mc_id_table[] = { +- { "amd_sdw", }, +- {} ++ { .name = "amd_sdw" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, mc_id_table); + +diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c +--- a/sound/soc/amd/acp/acp-sdw-sof-mach.c ++++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c +@@ -314,7 +314,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) + if (!sof_aux) + return -ENOMEM; + +- ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_confs); ++ ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_devs); + if (ret < 0) + return ret; + +@@ -399,12 +399,12 @@ static int mc_probe(struct platform_device *pdev) + dmi_check_system(sof_sdw_quirk_table); + + if (quirk_override != -1) { +- dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n", ++ dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n", + sof_sdw_quirk, quirk_override); + sof_sdw_quirk = quirk_override; + } + +- log_quirks(card->dev); ++ log_quirks(&pdev->dev); + + ctx->mc_quirk = sof_sdw_quirk; + /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ +@@ -423,15 +423,15 @@ static int mc_probe(struct platform_device *pdev) + for (i = 0; i < ctx->codec_info_list_count; i++) + amp_num += codec_info_list[i].amp_num; + +- card->components = devm_kasprintf(card->dev, GFP_KERNEL, ++ card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, + " cfg-amp:%d", amp_num); + if (!card->components) + return -ENOMEM; + + /* Register the card */ +- ret = devm_snd_soc_register_card(card->dev, card); ++ ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) { +- dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret); ++ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed %d\n", ret); + asoc_sdw_mc_dailink_exit_loop(card); + return ret; + } +@@ -449,8 +449,8 @@ static void mc_remove(struct platform_device *pdev) + } + + static const struct platform_device_id mc_id_table[] = { +- { "amd_sof_sdw", }, +- {} ++ { .name = "amd_sof_sdw" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, mc_id_table); + +diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c +--- a/sound/soc/amd/acp/acp-sof-mach.c ++++ b/sound/soc/amd/acp/acp-sof-mach.c +@@ -128,31 +128,31 @@ static int acp_sof_probe(struct platform_device *pdev) + static const struct platform_device_id board_ids[] = { + { + .name = "rt5682-rt1019", +- .driver_data = (kernel_ulong_t)&sof_rt5682_rt1019_data ++ .driver_data = (kernel_ulong_t)&sof_rt5682_rt1019_data, + }, + { + .name = "rt5682-max", +- .driver_data = (kernel_ulong_t)&sof_rt5682_max_data ++ .driver_data = (kernel_ulong_t)&sof_rt5682_max_data, + }, + { + .name = "rt5682s-max", +- .driver_data = (kernel_ulong_t)&sof_rt5682s_max_data ++ .driver_data = (kernel_ulong_t)&sof_rt5682s_max_data, + }, + { + .name = "rt5682s-rt1019", +- .driver_data = (kernel_ulong_t)&sof_rt5682s_rt1019_data ++ .driver_data = (kernel_ulong_t)&sof_rt5682s_rt1019_data, + }, + { + .name = "nau8825-max", +- .driver_data = (kernel_ulong_t)&sof_nau8825_data ++ .driver_data = (kernel_ulong_t)&sof_nau8825_data, + }, + { + .name = "rt5682s-hs-rt1019", +- .driver_data = (kernel_ulong_t)&sof_rt5682s_hs_rt1019_data ++ .driver_data = (kernel_ulong_t)&sof_rt5682s_hs_rt1019_data, + }, + { + .name = "nau8821-max", +- .driver_data = (kernel_ulong_t)&sof_nau8821_max98388_data ++ .driver_data = (kernel_ulong_t)&sof_nau8821_max98388_data, + }, + { } + }; +diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c +--- a/sound/soc/amd/acp/amd-acp70-acpi-match.c ++++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c +@@ -659,6 +659,56 @@ static const struct snd_soc_acpi_link_adr acp70_rt721_l1u0_tas2783x2_l1u8b[] = { + {} + }; + ++static const struct snd_soc_acpi_adr_device rt712_vb_l1u0_adr[] = { ++ { ++ .adr = 0x000130025D071201ull, ++ /* ++ * On this platform speakers are provided by two TAS2783 amps, ++ * so the AIF2 amp path is left unused: jack + DMIC only. ++ */ ++ .num_endpoints = ARRAY_SIZE(jack_dmic_endpoints), ++ .endpoints = jack_dmic_endpoints, ++ .name_prefix = "rt712" ++ } ++}; ++ ++/* ++ * Unique ID 0xC drives the left speaker and 0x9 the right one. The order of the ++ * entries matters as much as the endpoints: asoc_sdw_parse_sdw_endpoints() ++ * appends them to the dailink in array order and sdw_compute_slave_ports() ++ * hands out payload block offsets in that same order, so the first entry is the ++ * one that receives channel 0. ++ */ ++static const struct snd_soc_acpi_adr_device tas2783x2_l0u9c_adr[] = { ++ { ++ .adr = 0x00003C0102000001ull, ++ .num_endpoints = 1, ++ .endpoints = &spk_l_endpoint, ++ .name_prefix = "tas2783-1", ++ }, ++ { ++ .adr = 0x0000390102000001ull, ++ .num_endpoints = 1, ++ .endpoints = &spk_r_endpoint, ++ .name_prefix = "tas2783-2", ++ }, ++}; ++ ++/* HP OmniBook X Flip 14-kc0xxx (board 8EA1) */ ++static const struct snd_soc_acpi_link_adr acp70_tas2783x2_l0u9c_rt712_vb_l1u0[] = { ++ { ++ .mask = BIT(0), ++ .num_adr = ARRAY_SIZE(tas2783x2_l0u9c_adr), ++ .adr_d = tas2783x2_l0u9c_adr, ++ }, ++ { ++ .mask = BIT(1), ++ .num_adr = ARRAY_SIZE(rt712_vb_l1u0_adr), ++ .adr_d = rt712_vb_l1u0_adr, ++ }, ++ {} ++}; ++ + static const struct snd_soc_acpi_endpoint rt721_endpoints[] = { + { /* Jack Playback/Capture Endpoint (AIF1) */ + .num = 0, +@@ -704,6 +754,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = { + .links = acp70_tas2783_2, + .drv_name = "amd_sdw", + }, ++ { ++ .link_mask = BIT(0) | BIT(1), ++ .links = acp70_tas2783x2_l0u9c_rt712_vb_l1u0, ++ .machine_check = snd_soc_acpi_amd_sdca_is_device_rt712_vb, ++ .drv_name = "amd_sdw", ++ }, + { + .link_mask = BIT(0) | BIT(1), + .links = acp70_rt1320_l0_rt722_l1, +diff --git a/sound/soc/amd/mach-config.h b/sound/soc/amd/mach-config.h +--- a/sound/soc/amd/mach-config.h ++++ b/sound/soc/amd/mach-config.h +@@ -28,6 +28,7 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_sdw_machines[]; + extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[]; + extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[]; + extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_sdw_machines[]; ++extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp7x_sof_machines[]; + + struct config_entry { + u32 flags; +diff --git a/sound/soc/amd/raven/acp3x-i2s.c b/sound/soc/amd/raven/acp3x-i2s.c +--- a/sound/soc/amd/raven/acp3x-i2s.c ++++ b/sound/soc/amd/raven/acp3x-i2s.c +@@ -250,11 +250,17 @@ static int acp3x_i2s_trigger(struct snd_pcm_substream *substream, + return ret; + } + ++static const u64 acp3x_i2s_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A; ++ + static const struct snd_soc_dai_ops acp3x_i2s_dai_ops = { + .hw_params = acp3x_i2s_hwparams, + .trigger = acp3x_i2s_trigger, + .set_fmt = acp3x_i2s_set_fmt, + .set_tdm_slot = acp3x_i2s_set_tdm_slot, ++ .auto_selectable_formats = &acp3x_i2s_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static const struct snd_soc_component_driver acp3x_dai_component = { +diff --git a/sound/soc/amd/vangogh/acp5x-i2s.c b/sound/soc/amd/vangogh/acp5x-i2s.c +--- a/sound/soc/amd/vangogh/acp5x-i2s.c ++++ b/sound/soc/amd/vangogh/acp5x-i2s.c +@@ -337,11 +337,17 @@ static int acp5x_i2s_trigger(struct snd_pcm_substream *substream, + return ret; + } + ++static const u64 acp5x_i2s_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A; ++ + static const struct snd_soc_dai_ops acp5x_i2s_dai_ops = { + .hw_params = acp5x_i2s_hwparams, + .trigger = acp5x_i2s_trigger, + .set_fmt = acp5x_i2s_set_fmt, + .set_tdm_slot = acp5x_i2s_set_tdm_slot, ++ .auto_selectable_formats = &acp5x_i2s_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static const struct snd_soc_component_driver acp5x_dai_component = { +diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c +--- a/sound/soc/amd/yc/acp6x-mach.c ++++ b/sound/soc/amd/yc/acp6x-mach.c +@@ -795,12 +795,12 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { + } + }, + { +- .driver_data = &acp6x_card, +- .matches = { +- DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), +- DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook_ASUSLaptop M6501RR_M6501RR"), +- } +- }, ++ .driver_data = &acp6x_card, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook_ASUSLaptop M6501RR_M6501RR"), ++ } ++ }, + { + .driver_data = &acp6x_card, + .matches = { +diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c +--- a/sound/soc/apple/mca.c ++++ b/sound/soc/apple/mca.c +@@ -133,12 +133,17 @@ struct mca_cluster { + struct clk *clk_parent; + struct dma_chan *dma_chans[SNDRV_PCM_STREAM_LAST + 1]; + +- bool port_started[SNDRV_PCM_STREAM_LAST + 1]; +- int port_driver; /* The cluster driving this cluster's port */ ++ bool clk_provider; ++ ++ bool port_clk_started[SNDRV_PCM_STREAM_LAST + 1]; ++ int port_clk_driver; /* The cluster driving this cluster's port */ + + bool clocks_in_use[SNDRV_PCM_STREAM_LAST + 1]; + struct device_link *pd_link; + ++ /* In case of clock consumer FE */ ++ int syncgen_in_use; ++ + unsigned int bclk_ratio; + + /* Masks etc. picked up via the set_tdm_slot method */ +@@ -157,7 +162,7 @@ struct mca_data { + struct reset_control *rstc; + struct device_link *pd_link; + +- /* Mutex for accessing port_driver of foreign clusters */ ++ /* Mutex for accessing port_clk_driver of foreign clusters */ + struct mutex port_mutex; + + int nclusters; +@@ -220,6 +225,12 @@ static void mca_fe_early_trigger(struct snd_pcm_substream *substream, int cmd, + FIELD_PREP(SERDES_CONF_SYNC_SEL, 0)); + mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL, + FIELD_PREP(SERDES_CONF_SYNC_SEL, cl->no + 1)); ++ /* ++ * ADMAC gets started right after this. This delay seems ++ * to be needed for that to be reliable, e.g. ensure the ++ * clock is stable? ++ */ ++ udelay(100); + break; + default: + break; +@@ -256,11 +267,29 @@ static int mca_fe_trigger(struct snd_pcm_substream *substream, int cmd, + return 0; + } + ++static int mca_fe_get_portmask(struct snd_pcm_substream *substream) ++{ ++ struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); ++ struct snd_soc_dpcm *dpcm; ++ int mask = 0; ++ ++ for_each_dpcm_be(fe, substream->stream, dpcm) { ++ int no = mca_dai_to_cluster(snd_soc_rtd_to_cpu(dpcm->be, 0))->no; ++ ++ mask |= 1 << no; ++ } ++ ++ return mask; ++} ++ + static int mca_fe_enable_clocks(struct mca_cluster *cl) + { + struct mca_data *mca = cl->host; + int ret; + ++ if (!cl->clk_provider) ++ return -EINVAL; ++ + ret = clk_prepare_enable(cl->clk_parent); + if (ret) { + dev_err(mca->dev, +@@ -311,7 +340,7 @@ static bool mca_fe_clocks_in_use(struct mca_cluster *cl) + for (i = 0; i < mca->nclusters; i++) { + be_cl = &mca->clusters[i]; + +- if (be_cl->port_driver != cl->no) ++ if (be_cl->port_clk_driver != cl->no) + continue; + + for_each_pcm_streams(stream) { +@@ -323,36 +352,6 @@ static bool mca_fe_clocks_in_use(struct mca_cluster *cl) + return false; + } + +-static int mca_be_prepare(struct snd_pcm_substream *substream, +- struct snd_soc_dai *dai) +-{ +- struct mca_cluster *cl = mca_dai_to_cluster(dai); +- struct mca_data *mca = cl->host; +- struct mca_cluster *fe_cl; +- int ret; +- +- if (cl->port_driver < 0) +- return -EINVAL; +- +- fe_cl = &mca->clusters[cl->port_driver]; +- +- /* +- * Typically the CODECs we are paired with will require clocks +- * to be present at time of unmute with the 'mute_stream' op +- * or at time of DAPM widget power-up. We need to enable clocks +- * here at the latest (frontend prepare would be too late). +- */ +- if (!mca_fe_clocks_in_use(fe_cl)) { +- ret = mca_fe_enable_clocks(fe_cl); +- if (ret < 0) +- return ret; +- } +- +- cl->clocks_in_use[substream->stream] = true; +- +- return 0; +-} +- + static int mca_be_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + { +@@ -360,7 +359,7 @@ static int mca_be_hw_free(struct snd_pcm_substream *substream, + struct mca_data *mca = cl->host; + struct mca_cluster *fe_cl; + +- if (cl->port_driver < 0) ++ if (cl->port_clk_driver < 0) + return -EINVAL; + + /* +@@ -368,7 +367,7 @@ static int mca_be_hw_free(struct snd_pcm_substream *substream, + * belong to the same PCM, accesses should have been + * synchronized at ASoC level. + */ +- fe_cl = &mca->clusters[cl->port_driver]; ++ fe_cl = &mca->clusters[cl->port_clk_driver]; + if (!mca_fe_clocks_in_use(fe_cl)) + return 0; /* Nothing to do */ + +@@ -380,6 +379,56 @@ static int mca_be_hw_free(struct snd_pcm_substream *substream, + return 0; + } + ++static int mca_fe_prepare(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct mca_cluster *cl = mca_dai_to_cluster(dai); ++ struct mca_data *mca = cl->host; ++ ++ if (cl->clk_provider) ++ return 0; ++ ++ /* Turn on the cluster power domain if not already in use */ ++ if (!cl->syncgen_in_use) { ++ int port = ffs(mca_fe_get_portmask(substream)); ++ ++ cl->pd_link = device_link_add(mca->dev, cl->pd_dev, ++ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | ++ DL_FLAG_RPM_ACTIVE); ++ if (!cl->pd_link) { ++ dev_err(mca->dev, ++ "cluster %d: unable to prop-up power domain\n", cl->no); ++ return -EINVAL; ++ } ++ ++ mca_modify(cl, REG_SYNCGEN_MCLK_SEL, SYNCGEN_MCLK_SEL, BIT(port)); ++ mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN, ++ SYNCGEN_STATUS_EN); ++ } ++ cl->syncgen_in_use |= 1 << substream->stream; ++ ++ return 0; ++} ++ ++static int mca_fe_hw_free(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct mca_cluster *cl = mca_dai_to_cluster(dai); ++ ++ if (cl->clk_provider) ++ return 0; ++ ++ cl->syncgen_in_use &= ~(1 << substream->stream); ++ if (cl->syncgen_in_use) ++ return 0; ++ ++ mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN, 0); ++ if (cl->pd_link) ++ device_link_del(cl->pd_link); ++ ++ return 0; ++} ++ + static unsigned int mca_crop_mask(unsigned int mask, int nchans) + { + while (hweight32(mask) > nchans) +@@ -390,7 +439,7 @@ static unsigned int mca_crop_mask(unsigned int mask, int nchans) + + static int mca_configure_serdes(struct mca_cluster *cl, int serdes_unit, + unsigned int mask, int slots, int nchans, +- int slot_width, bool is_tx, int port) ++ int slot_width, bool is_tx, int portmask) + { + __iomem void *serdes_base = cl->base + serdes_unit; + u32 serdes_conf, serdes_conf_mask; +@@ -449,7 +498,7 @@ static int mca_configure_serdes(struct mca_cluster *cl, int serdes_unit, + serdes_base + REG_RX_SERDES_SLOTMASK); + writel_relaxed(~((u32)mca_crop_mask(mask, nchans)), + serdes_base + REG_RX_SERDES_SLOTMASK + 0x4); +- writel_relaxed(1 << port, ++ writel_relaxed(portmask, + serdes_base + REG_RX_SERDES_PORT); + } + +@@ -505,9 +554,18 @@ static int mca_fe_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) + u32 serdes_conf = 0; + u32 bitstart; + +- if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != +- SND_SOC_DAIFMT_BP_FP) ++ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { ++ case SND_SOC_DAIFMT_BP_FP: ++ cl->clk_provider = true; ++ break; ++ ++ case SND_SOC_DAIFMT_BC_FC: ++ cl->clk_provider = false; ++ break; ++ ++ default: + goto err; ++ } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: +@@ -564,24 +622,6 @@ static int mca_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) + return 0; + } + +-static int mca_fe_get_port(struct snd_pcm_substream *substream) +-{ +- struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); +- struct snd_soc_pcm_runtime *be; +- struct snd_soc_dpcm *dpcm; +- +- be = NULL; +- for_each_dpcm_be(fe, substream->stream, dpcm) { +- be = dpcm->be; +- break; +- } +- +- if (!be) +- return -EINVAL; +- +- return mca_dai_to_cluster(snd_soc_rtd_to_cpu(be, 0))->no; +-} +- + static int mca_fe_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +@@ -595,7 +635,7 @@ static int mca_fe_hw_params(struct snd_pcm_substream *substream, + unsigned long bclk_ratio; + unsigned int tdm_slots, tdm_slot_width, tdm_mask; + u32 regval, pad; +- int ret, port, nchans_ceiled; ++ int ret, portmask, nchans_ceiled; + + if (!cl->tdm_slot_width) { + /* +@@ -644,13 +684,13 @@ static int mca_fe_hw_params(struct snd_pcm_substream *substream, + tdm_mask = (1 << tdm_slots) - 1; + } + +- port = mca_fe_get_port(substream); +- if (port < 0) +- return port; ++ portmask = mca_fe_get_portmask(substream); ++ if (!portmask) ++ return -EINVAL; + + ret = mca_configure_serdes(cl, is_tx ? CLUSTER_TX_OFF : CLUSTER_RX_OFF, + tdm_mask, tdm_slots, params_channels(params), +- tdm_slot_width, is_tx, port); ++ tdm_slot_width, is_tx, portmask); + if (ret) + return ret; + +@@ -699,6 +739,19 @@ static int mca_fe_hw_params(struct snd_pcm_substream *substream, + return 0; + } + ++static const u64 mca_fe_selectable_formats[] = ++{ ++ /* pattern 1 */ ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_NB_IF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_IF, ++ ++ /* pattern 2 */ ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_NF ++}; ++ + static const struct snd_soc_dai_ops mca_fe_ops = { + .startup = mca_fe_startup, + .set_fmt = mca_fe_set_fmt, +@@ -706,66 +759,128 @@ static const struct snd_soc_dai_ops mca_fe_ops = { + .set_tdm_slot = mca_fe_set_tdm_slot, + .hw_params = mca_fe_hw_params, + .trigger = mca_fe_trigger, ++ .prepare = mca_fe_prepare, ++ .hw_free = mca_fe_hw_free, ++ .auto_selectable_formats = mca_fe_selectable_formats, ++ .num_auto_selectable_formats = ARRAY_SIZE(mca_fe_selectable_formats), + }; + +-static bool mca_be_started(struct mca_cluster *cl) ++/* ++ * Is there a FE attached which will be feeding this port's clocks? ++ */ ++static bool mca_be_clk_started(struct mca_cluster *cl) + { + int stream; + + for_each_pcm_streams(stream) +- if (cl->port_started[stream]) ++ if (cl->port_clk_started[stream]) + return true; + return false; + } + ++static struct snd_soc_pcm_runtime *mca_be_get_fe(struct snd_soc_pcm_runtime *be, ++ int stream) ++{ ++ struct snd_soc_pcm_runtime *fe = NULL; ++ struct snd_soc_dpcm *dpcm; ++ ++ for_each_dpcm_fe(be, stream, dpcm) { ++ if (fe && dpcm->fe != fe) { ++ dev_err(be->dev, "many FE per one BE unsupported\n"); ++ return NULL; ++ } ++ ++ fe = dpcm->fe; ++ } ++ ++ return fe; ++} ++ ++static int mca_be_prepare(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream); ++ struct snd_soc_pcm_runtime *fe = mca_be_get_fe(be, substream->stream); ++ struct mca_cluster *cl = mca_dai_to_cluster(dai); ++ struct mca_data *mca = cl->host; ++ struct mca_cluster *fe_cl, *fe_clk_cl; ++ int ret; ++ ++ fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0)); ++ ++ if (!fe_cl->clk_provider) ++ return 0; ++ ++ if (cl->port_clk_driver < 0) ++ return 0; ++ ++ fe_clk_cl = &mca->clusters[cl->port_clk_driver]; ++ ++ /* ++ * Typically the CODECs we are paired with will require clocks ++ * to be present at time of unmute with the 'mute_stream' op ++ * or at time of DAPM widget power-up. We need to enable clocks ++ * here at the latest (frontend prepare would be too late). ++ */ ++ if (!mca_fe_clocks_in_use(fe_clk_cl)) { ++ ret = mca_fe_enable_clocks(fe_clk_cl); ++ if (ret < 0) ++ return ret; ++ } ++ ++ cl->clocks_in_use[substream->stream] = true; ++ ++ return 0; ++} ++ + static int mca_be_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + { + struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream); +- struct snd_soc_pcm_runtime *fe; ++ struct snd_soc_pcm_runtime *fe = mca_be_get_fe(be, substream->stream); + struct mca_cluster *cl = mca_dai_to_cluster(dai); + struct mca_cluster *fe_cl; + struct mca_data *mca = cl->host; +- struct snd_soc_dpcm *dpcm; +- +- fe = NULL; +- +- for_each_dpcm_fe(be, substream->stream, dpcm) { +- if (fe && dpcm->fe != fe) { +- dev_err(mca->dev, "many FE per one BE unsupported\n"); +- return -EINVAL; +- } +- +- fe = dpcm->fe; +- } + + if (!fe) + return -EINVAL; +- + fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0)); + +- if (mca_be_started(cl)) { ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ writel_relaxed(PORT_DATA_SEL_TXA(fe_cl->no), ++ cl->base + REG_PORT_DATA_SEL); ++ mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_TX_DATA, ++ PORT_ENABLES_TX_DATA); ++ } ++ ++ if (!fe_cl->clk_provider) ++ return 0; ++ ++ if (mca_be_clk_started(cl)) { + /* + * Port is already started in the other direction. + * Make sure there isn't a conflict with another cluster +- * driving the port. ++ * driving the port clocks. + */ +- if (cl->port_driver != fe_cl->no) ++ if (cl->port_clk_driver != fe_cl->no) + return -EINVAL; + +- cl->port_started[substream->stream] = true; ++ cl->port_clk_started[substream->stream] = true; + return 0; + } + +- writel_relaxed(PORT_ENABLES_CLOCKS | PORT_ENABLES_TX_DATA, +- cl->base + REG_PORT_ENABLES); + writel_relaxed(FIELD_PREP(PORT_CLOCK_SEL, fe_cl->no + 1), + cl->base + REG_PORT_CLOCK_SEL); ++ + writel_relaxed(PORT_DATA_SEL_TXA(fe_cl->no), + cl->base + REG_PORT_DATA_SEL); ++ ++ mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_CLOCKS, ++ PORT_ENABLES_CLOCKS); ++ + scoped_guard(mutex, &mca->port_mutex) +- cl->port_driver = fe_cl->no; +- cl->port_started[substream->stream] = true; ++ cl->port_clk_driver = fe_cl->no; ++ cl->port_clk_started[substream->stream] = true; + + return 0; + } +@@ -773,20 +888,34 @@ static int mca_be_startup(struct snd_pcm_substream *substream, + static void mca_be_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + { ++ struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream); ++ struct snd_soc_pcm_runtime *fe = mca_be_get_fe(be, substream->stream); + struct mca_cluster *cl = mca_dai_to_cluster(dai); ++ struct mca_cluster *fe_cl; + struct mca_data *mca = cl->host; + +- cl->port_started[substream->stream] = false; ++ if (!fe) ++ return; ++ fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0)); + +- if (!mca_be_started(cl)) { ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_TX_DATA, 0); ++ writel_relaxed(0, cl->base + REG_PORT_DATA_SEL); ++ } ++ ++ if (!fe_cl->clk_provider) ++ return; ++ ++ cl->port_clk_started[substream->stream] = false; ++ if (!mca_be_clk_started(cl)) { + /* + * Were we the last direction to shutdown? +- * Turn off the lights. ++ * Turn off the lights (clocks). + */ +- writel_relaxed(0, cl->base + REG_PORT_ENABLES); +- writel_relaxed(0, cl->base + REG_PORT_DATA_SEL); ++ mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_CLOCKS, 0); ++ writel_relaxed(0, cl->base + REG_PORT_CLOCK_SEL); + scoped_guard(mutex, &mca->port_mutex) +- cl->port_driver = -1; ++ cl->port_clk_driver = -1; + } + } + +@@ -1092,7 +1221,7 @@ static int apple_mca_probe(struct platform_device *pdev) + cl->host = mca; + cl->no = i; + cl->base = base + CLUSTER_STRIDE * i; +- cl->port_driver = -1; ++ cl->port_clk_driver = -1; + cl->clk_parent = of_clk_get(pdev->dev.of_node, i); + if (IS_ERR(cl->clk_parent)) { + dev_err(&pdev->dev, "unable to obtain clock %d: %ld\n", +diff --git a/sound/soc/atmel/atmel-i2s.c b/sound/soc/atmel/atmel-i2s.c +--- a/sound/soc/atmel/atmel-i2s.c ++++ b/sound/soc/atmel/atmel-i2s.c +@@ -540,12 +540,16 @@ static int atmel_i2s_dai_probe(struct snd_soc_dai *dai) + return 0; + } + ++static const u64 atmel_i2s_selectable_formats = SND_SOC_POSSIBLE_DAIFMT_I2S; ++ + static const struct snd_soc_dai_ops atmel_i2s_dai_ops = { + .probe = atmel_i2s_dai_probe, + .prepare = atmel_i2s_prepare, + .trigger = atmel_i2s_trigger, + .hw_params = atmel_i2s_hw_params, + .set_fmt = atmel_i2s_set_dai_fmt, ++ .auto_selectable_formats = &atmel_i2s_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static struct snd_soc_dai_driver atmel_i2s_dai = { +diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c +--- a/sound/soc/atmel/atmel_ssc_dai.c ++++ b/sound/soc/atmel/atmel_ssc_dai.c +@@ -825,6 +825,11 @@ static int atmel_ssc_resume(struct snd_soc_component *component) + #define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + ++static const u64 atmel_ssc_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A; ++ + static const struct snd_soc_dai_ops atmel_ssc_dai_ops = { + .startup = atmel_ssc_startup, + .shutdown = atmel_ssc_shutdown, +@@ -833,6 +838,8 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = { + .hw_params = atmel_ssc_hw_params, + .set_fmt = atmel_ssc_set_dai_fmt, + .set_clkdiv = atmel_ssc_set_dai_clkdiv, ++ .auto_selectable_formats = &atmel_ssc_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static struct snd_soc_dai_driver atmel_ssc_dai = { +diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c +--- a/sound/soc/atmel/atmel_wm8904.c ++++ b/sound/soc/atmel/atmel_wm8904.c +@@ -82,32 +82,33 @@ static struct snd_soc_card atmel_asoc_wm8904_card = { + + static int atmel_asoc_wm8904_dt_init(struct platform_device *pdev) + { +- struct device_node *np = pdev->dev.of_node; ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; + struct device_node *codec_np, *cpu_np; + struct snd_soc_card *card = &atmel_asoc_wm8904_card; + struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink; + int ret; + + if (!np) { +- dev_err(&pdev->dev, "only device tree supported\n"); ++ dev_err(dev, "only device tree supported\n"); + return -EINVAL; + } + + ret = snd_soc_of_parse_card_name(card, "atmel,model"); + if (ret) { +- dev_err(&pdev->dev, "failed to parse card name\n"); ++ dev_err(dev, "failed to parse card name\n"); + return ret; + } + + ret = snd_soc_of_parse_audio_routing(card, "atmel,audio-routing"); + if (ret) { +- dev_err(&pdev->dev, "failed to parse audio routing\n"); ++ dev_err(dev, "failed to parse audio routing\n"); + return ret; + } + + cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0); + if (!cpu_np) { +- dev_err(&pdev->dev, "failed to get dai and pcm info\n"); ++ dev_err(dev, "failed to get dai and pcm info\n"); + ret = -EINVAL; + return ret; + } +@@ -117,7 +118,7 @@ static int atmel_asoc_wm8904_dt_init(struct platform_device *pdev) + + codec_np = of_parse_phandle(np, "atmel,audio-codec", 0); + if (!codec_np) { +- dev_err(&pdev->dev, "failed to get codec info\n"); ++ dev_err(dev, "failed to get codec info\n"); + ret = -EINVAL; + return ret; + } +diff --git a/sound/soc/atmel/mchp-i2s-mcc.c b/sound/soc/atmel/mchp-i2s-mcc.c +--- a/sound/soc/atmel/mchp-i2s-mcc.c ++++ b/sound/soc/atmel/mchp-i2s-mcc.c +@@ -912,6 +912,13 @@ static int mchp_i2s_mcc_dai_probe(struct snd_soc_dai *dai) + return 0; + } + ++static const u64 mchp_i2s_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A | ++ SND_SOC_POSSIBLE_DAIFMT_GATED | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF; ++ + static const struct snd_soc_dai_ops mchp_i2s_mcc_dai_ops = { + .probe = mchp_i2s_mcc_dai_probe, + .set_sysclk = mchp_i2s_mcc_set_sysclk, +@@ -922,6 +929,8 @@ static const struct snd_soc_dai_ops mchp_i2s_mcc_dai_ops = { + .hw_free = mchp_i2s_mcc_hw_free, + .set_fmt = mchp_i2s_mcc_set_dai_fmt, + .set_tdm_slot = mchp_i2s_mcc_set_dai_tdm_slot, ++ .auto_selectable_formats = &mchp_i2s_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + #define MCHP_I2SMCC_RATES SNDRV_PCM_RATE_8000_192000 +diff --git a/sound/soc/atmel/mchp-pdmc.c b/sound/soc/atmel/mchp-pdmc.c +--- a/sound/soc/atmel/mchp-pdmc.c ++++ b/sound/soc/atmel/mchp-pdmc.c +@@ -741,6 +741,8 @@ static int mchp_pdmc_pcm_new(struct snd_soc_pcm_runtime *rtd, + return ret; + } + ++static const u64 mchp_selectable_formats = SND_SOC_POSSIBLE_DAIFMT_PDM; ++ + static const struct snd_soc_dai_ops mchp_pdmc_dai_ops = { + .probe = mchp_pdmc_dai_probe, + .set_fmt = mchp_pdmc_set_fmt, +@@ -748,6 +750,8 @@ static const struct snd_soc_dai_ops mchp_pdmc_dai_ops = { + .hw_params = mchp_pdmc_hw_params, + .trigger = mchp_pdmc_trigger, + .pcm_new = &mchp_pdmc_pcm_new, ++ .auto_selectable_formats = &mchp_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static struct snd_soc_dai_driver mchp_pdmc_dai = { +diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c +--- a/sound/soc/au1x/db1200.c ++++ b/sound/soc/au1x/db1200.c +@@ -42,7 +42,7 @@ static const struct platform_device_id db1200_pids[] = { + .name = "db1550-i2s", + .driver_data = 5, + }, +- {}, ++ { } + }; + MODULE_DEVICE_TABLE(platform, db1200_pids); + +diff --git a/sound/soc/au1x/i2sc.c b/sound/soc/au1x/i2sc.c +--- a/sound/soc/au1x/i2sc.c ++++ b/sound/soc/au1x/i2sc.c +@@ -202,11 +202,22 @@ static int au1xi2s_startup(struct snd_pcm_substream *substream, + return 0; + } + ++static const u64 au1xi2s_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_NB_IF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_IF; ++ + static const struct snd_soc_dai_ops au1xi2s_dai_ops = { + .startup = au1xi2s_startup, + .trigger = au1xi2s_trigger, + .hw_params = au1xi2s_hw_params, + .set_fmt = au1xi2s_set_fmt, ++ .auto_selectable_formats = &au1xi2s_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static struct snd_soc_dai_driver au1xi2s_dai_driver = { +diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c +--- a/sound/soc/au1x/psc-ac97.c ++++ b/sound/soc/au1x/psc-ac97.c +@@ -210,9 +210,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, + { + struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai); + unsigned long r, ro, stat; +- int chans, t, stype = substream->stream; +- +- chans = params_channels(params); ++ int t, stype = substream->stream; + + r = ro = __raw_readl(AC97_CFG(pscdata)); + stat = __raw_readl(AC97_STAT(pscdata)); +diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c +--- a/sound/soc/au1x/psc-i2s.c ++++ b/sound/soc/au1x/psc-i2s.c +@@ -262,11 +262,22 @@ static int au1xpsc_i2s_startup(struct snd_pcm_substream *substream, + return 0; + } + ++static const u64 au1xpsc_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_NB_IF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_IF; ++ + static const struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = { + .startup = au1xpsc_i2s_startup, + .trigger = au1xpsc_i2s_trigger, + .hw_params = au1xpsc_i2s_hw_params, + .set_fmt = au1xpsc_i2s_set_fmt, ++ .auto_selectable_formats = &au1xpsc_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static const struct snd_soc_dai_driver au1xpsc_i2s_dai_template = { +diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c +--- a/sound/soc/bcm/bcm2835-i2s.c ++++ b/sound/soc/bcm/bcm2835-i2s.c +@@ -748,6 +748,17 @@ static int bcm2835_i2s_dai_probe(struct snd_soc_dai *dai) + return 0; + } + ++static const u64 bcm2835_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_B | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_NB_IF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_IF; ++ + static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = { + .probe = bcm2835_i2s_dai_probe, + .startup = bcm2835_i2s_startup, +@@ -758,6 +769,8 @@ static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = { + .set_fmt = bcm2835_i2s_set_dai_fmt, + .set_bclk_ratio = bcm2835_i2s_set_dai_bclk_ratio, + .set_tdm_slot = bcm2835_i2s_set_dai_tdm_slot, ++ .auto_selectable_formats = &bcm2835_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static struct snd_soc_dai_driver bcm2835_i2s_dai = { +diff --git a/sound/soc/bcm/cygnus-ssp.c b/sound/soc/bcm/cygnus-ssp.c +--- a/sound/soc/bcm/cygnus-ssp.c ++++ b/sound/soc/bcm/cygnus-ssp.c +@@ -1133,6 +1133,11 @@ static int cygnus_ssp_resume(struct snd_soc_component *component) + #define cygnus_ssp_resume NULL + #endif + ++static const u64 cygnus_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_B; ++ + static const struct snd_soc_dai_ops cygnus_ssp_dai_ops = { + .startup = cygnus_ssp_startup, + .shutdown = cygnus_ssp_shutdown, +@@ -1141,6 +1146,8 @@ static const struct snd_soc_dai_ops cygnus_ssp_dai_ops = { + .set_fmt = cygnus_ssp_set_fmt, + .set_sysclk = cygnus_ssp_set_sysclk, + .set_tdm_slot = cygnus_set_dai_tdm_slot, ++ .auto_selectable_formats = &cygnus_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static const struct snd_soc_dai_ops cygnus_spdif_dai_ops = { +diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c +--- a/sound/soc/cirrus/ep93xx-i2s.c ++++ b/sound/soc/cirrus/ep93xx-i2s.c +@@ -401,6 +401,15 @@ static int ep93xx_i2s_resume(struct snd_soc_component *component) + #define ep93xx_i2s_resume NULL + #endif + ++static const u64 ep93xx_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_NB_IF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_IF; ++ + static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = { + .probe = ep93xx_i2s_dai_probe, + .startup = ep93xx_i2s_startup, +@@ -408,6 +417,8 @@ static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = { + .hw_params = ep93xx_i2s_hw_params, + .set_sysclk = ep93xx_i2s_set_sysclk, + .set_fmt = ep93xx_i2s_set_dai_fmt, ++ .auto_selectable_formats = &ep93xx_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + #define EP93XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S32_LE) +diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -238,6 +238,7 @@ config SND_SOC_ALL_CODECS + imply SND_SOC_RT715_SDCA_SDW + imply SND_SOC_RT721_SDCA_SDW + imply SND_SOC_RT722_SDCA_SDW ++ imply SND_SOC_RT766_SDCA_SDW + imply SND_SOC_RT1308_SDW + imply SND_SOC_RT1316_SDW + imply SND_SOC_RT1318 +@@ -367,6 +368,7 @@ config SND_SOC_ALL_CODECS + imply SND_SOC_WSA881X + imply SND_SOC_WSA883X + imply SND_SOC_WSA884X ++ imply SND_SOC_WSA885X + imply SND_SOC_ZL38060 + help + Normally ASoC codec drivers are only built if a machine driver which +@@ -615,8 +617,8 @@ config SND_SOC_AK4613 + depends on I2C + + config SND_SOC_AK4619 +- tristate "AKM AK4619 CODEC" +- depends on I2C ++ tristate "AKM AK4619 CODEC" ++ depends on I2C + + config SND_SOC_AK4642 + tristate "AKM AK4642 CODEC" +@@ -894,6 +896,7 @@ config SND_SOC_CS35L56_SDW + tristate "Cirrus Logic CS35L56 CODEC (SDW)" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE ++ select IRQ_DOMAIN + select SND_SOC_CS35L56 + select SND_SOC_CS35L56_SHARED + help +@@ -1206,16 +1209,16 @@ config SND_SOC_JZ4725B_CODEC + will be called snd-soc-jz4725b-codec. + + config SND_SOC_JZ4760_CODEC +- depends on MACH_INGENIC || COMPILE_TEST +- depends on OF +- select REGMAP +- tristate "Ingenic JZ4760 internal CODEC" +- help +- Enable support for the internal CODEC found in the JZ4760 SoC +- from Ingenic. ++ depends on MACH_INGENIC || COMPILE_TEST ++ depends on OF ++ select REGMAP ++ tristate "Ingenic JZ4760 internal CODEC" ++ help ++ Enable support for the internal CODEC found in the JZ4760 SoC ++ from Ingenic. + +- This driver can also be built as a module. If so, the module +- will be called snd-soc-jz4760-codec. ++ This driver can also be built as a module. If so, the module ++ will be called snd-soc-jz4760-codec. + + config SND_SOC_JZ4770_CODEC + depends on MACH_INGENIC || COMPILE_TEST +@@ -1893,7 +1896,7 @@ config SND_SOC_RT5670 + depends on I2C + + config SND_SOC_RT5677 +- tristate ++ tristate "Realtek RT5677 Codec" + depends on I2C + select REGMAP_I2C + select REGMAP_IRQ +@@ -1983,6 +1986,15 @@ config SND_SOC_RT715_SDCA_SDW + select REGMAP_SOUNDWIRE + select REGMAP_SOUNDWIRE_MBQ + ++config SND_SOC_RT766_SDCA_SDW ++ tristate "Realtek RT766 SDCA Codec - SDW" ++ depends on SOUNDWIRE ++ depends on SND_SOC_SDCA ++ select SND_SOC_SDCA_HID ++ select SND_SOC_SDCA_IRQ ++ select REGMAP_SOUNDWIRE ++ select REGMAP_SOUNDWIRE_MBQ ++ + config SND_SOC_RT9120 + tristate "Richtek RT9120 Stereo Class-D Amplifier" + depends on I2C +@@ -2302,8 +2314,8 @@ config SND_SOC_TLV320ADC3XXX + depends on I2C + depends on GPIOLIB + help +- Enable support for Texas Instruments TLV320ADC3001 and TLV320ADC3101 +- ADCs. ++ Enable support for Texas Instruments TLV320ADC3001 and TLV320ADC3101 ++ ADCs. + + config SND_SOC_TLV320AIC23 + tristate +@@ -2776,6 +2788,16 @@ config SND_SOC_WSA884X + This enables support for Qualcomm WSA8840/WSA8845/WSA8845H Class-D + Smart Speaker Amplifier. + ++config SND_SOC_WSA885X ++ tristate "WSA885X Codec" ++ depends on I2C ++ select REGMAP_I2C ++ help ++ This enables support for Qualcomm WSA885X Stereo Smart Speaker ++ Amplifier. The codec driver programs the amplifier register ++ map and exposes the DAI and mixer controls used by Qualcomm ++ audio machine drivers. ++ + config SND_SOC_ZL38060 + tristate "Microsemi ZL38060 Connected Home Audio Processor" + depends on SPI_MASTER +@@ -2899,28 +2921,32 @@ config SND_SOC_TPA6130A2 + depends on I2C + + config SND_SOC_LPASS_MACRO_COMMON +- tristate ++ tristate + + config SND_SOC_LPASS_WSA_MACRO + depends on COMMON_CLK ++ depends on PM_CLK + select REGMAP_MMIO + select SND_SOC_LPASS_MACRO_COMMON + tristate "Qualcomm WSA Macro in LPASS(Low Power Audio SubSystem)" + + config SND_SOC_LPASS_VA_MACRO + depends on COMMON_CLK ++ depends on PM_CLK + select REGMAP_MMIO + select SND_SOC_LPASS_MACRO_COMMON + tristate "Qualcomm VA Macro in LPASS(Low Power Audio SubSystem)" + + config SND_SOC_LPASS_RX_MACRO + depends on COMMON_CLK ++ depends on PM_CLK + select REGMAP_MMIO + select SND_SOC_LPASS_MACRO_COMMON + tristate "Qualcomm RX Macro in LPASS(Low Power Audio SubSystem)" + + config SND_SOC_LPASS_TX_MACRO + depends on COMMON_CLK ++ depends on PM_CLK + select REGMAP_MMIO + select SND_SOC_LPASS_MACRO_COMMON + tristate "Qualcomm TX Macro in LPASS(Low Power Audio SubSystem)" +diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile +--- a/sound/soc/codecs/Makefile ++++ b/sound/soc/codecs/Makefile +@@ -285,6 +285,7 @@ snd-soc-rt715-y := rt715.o rt715-sdw.o + snd-soc-rt715-sdca-y := rt715-sdca.o rt715-sdca-sdw.o + snd-soc-rt721-sdca-y := rt721-sdca.o rt721-sdca-sdw.o + snd-soc-rt722-sdca-y := rt722-sdca.o rt722-sdca-sdw.o ++snd-soc-rt766-sdca-y := rt766-sdca.o rt766-sdca-sdw.o + snd-soc-rt9120-y := rt9120.o + snd-soc-rt9123-y := rt9123.o + snd-soc-rt9123p-y := rt9123p.o +@@ -427,6 +428,7 @@ snd-soc-wm-hubs-y := wm_hubs.o + snd-soc-wsa881x-y := wsa881x.o + snd-soc-wsa883x-y := wsa883x.o + snd-soc-wsa884x-y := wsa884x.o ++snd-soc-wsa885x-y := wsa885x.o + snd-soc-zl38060-y := zl38060.o + # Amp + snd-soc-max9877-y := max9877.o +@@ -726,6 +728,7 @@ obj-$(CONFIG_SND_SOC_RT715) += snd-soc-rt715.o + obj-$(CONFIG_SND_SOC_RT715_SDCA_SDW) += snd-soc-rt715-sdca.o + obj-$(CONFIG_SND_SOC_RT721_SDCA_SDW) += snd-soc-rt721-sdca.o + obj-$(CONFIG_SND_SOC_RT722_SDCA_SDW) += snd-soc-rt722-sdca.o ++obj-$(CONFIG_SND_SOC_RT766_SDCA_SDW) += snd-soc-rt766-sdca.o + obj-$(CONFIG_SND_SOC_RT9120) += snd-soc-rt9120.o + obj-$(CONFIG_SND_SOC_RT9123) += snd-soc-rt9123.o + obj-$(CONFIG_SND_SOC_RT9123P) += snd-soc-rt9123p.o +@@ -878,6 +881,7 @@ obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o + obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o + obj-$(CONFIG_SND_SOC_WSA883X) += snd-soc-wsa883x.o + obj-$(CONFIG_SND_SOC_WSA884X) += snd-soc-wsa884x.o ++obj-$(CONFIG_SND_SOC_WSA885X) += snd-soc-wsa885x.o + obj-$(CONFIG_SND_SOC_ZL38060) += snd-soc-zl38060.o + + # Amp +diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c +--- a/sound/soc/codecs/ab8500-codec.c ++++ b/sound/soc/codecs/ab8500-codec.c +@@ -14,14 +14,17 @@ + * for ST-Ericsson. + */ + ++#include + #include + #include + #include + #include + #include + #include ++#include + #include + #include ++#include + #include + #include + #include +@@ -51,9 +54,32 @@ + /* Macrocell register definitions */ + #define AB8500_GPIO_DIR4_REG 0x13 /* Bank AB8500_MISC */ + ++/* Nr of FIR/IIR-coeff banks in ANC-block */ ++#define AB8500_NR_OF_ANC_COEFF_BANKS 2 ++ ++/* Minimum duration to keep ANC IIR Init bit high or ++low before proceeding with the configuration sequence */ ++#define AB8500_ANC_SM_DELAY 2000 ++ ++/* Sidetone states */ ++static const char * const enum_sid_state[] = { ++ "Unconfigured", ++ "Apply FIR", ++ "FIR is configured", ++}; ++enum sid_state { ++ SID_UNCONFIGURED = 0, ++ SID_APPLY_FIR = 1, ++ SID_FIR_CONFIGURED = 2, ++}; ++ + /* Private data for AB8500 device-driver */ + struct ab8500_codec_drvdata { + struct regmap *regmap; ++ struct mutex ctrl_lock; ++ ++ /* Sidetone */ ++ enum sid_state sid_status; + }; + + static inline const char *amic_micbias_str(enum amic_micbias micbias) +@@ -616,6 +642,9 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = { + NULL, 0), + /* Acoustical Noise Cancellation path */ + ++ SND_SOC_DAPM_INPUT("ANC Configure Input"), ++ SND_SOC_DAPM_OUTPUT("ANC Configure Output"), ++ + SND_SOC_DAPM_MUX("ANC Source", + SND_SOC_NOPM, 0, 0, + dapm_anc_in_select), +@@ -658,6 +687,10 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { + {"Main Supply", NULL, "Audio Power"}, + {"Main Supply", NULL, "Audio Analog Power"}, + ++ /* ANC Configure */ ++ {"ANC Configure Input", NULL, "Main Supply"}, ++ {"ANC Configure Output", NULL, "ANC Configure Input"}, ++ + /* Powerup charge pump if DA1/2 is in use */ + + {"DA_IN1", NULL, "ab8500_0p"}, +@@ -939,6 +972,75 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes_mic2_vamicx[] = { + {"MIC2 V-AMICx Enable", NULL, "V-AMIC2"}, + }; + ++/* ++ * Control-events ++ */ ++ ++static int sid_status_control_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev); ++ ++ guard(mutex)(&drvdata->ctrl_lock); ++ ucontrol->value.enumerated.item[0] = drvdata->sid_status; ++ ++ return 0; ++} ++ ++/* Write sidetone FIR-coefficients configuration sequence */ ++static int sid_status_control_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev); ++ unsigned int param, sidconf; ++ int status = 1; ++ ++ dev_dbg(component->dev, "%s: Enter\n", __func__); ++ ++ if (ucontrol->value.enumerated.item[0] != SID_APPLY_FIR) { ++ dev_err(component->dev, ++ "%s: ERROR: This control supports '%s' only!\n", ++ __func__, enum_sid_state[SID_APPLY_FIR]); ++ return -EIO; ++ } ++ ++ guard(mutex)(&drvdata->ctrl_lock); ++ ++ sidconf = snd_soc_component_read(component, AB8500_SIDFIRCONF); ++ if (((sidconf & BIT(AB8500_SIDFIRCONF_FIRSIDBUSY)) != 0)) { ++ if ((sidconf & BIT(AB8500_SIDFIRCONF_ENFIRSIDS)) == 0) { ++ dev_err(component->dev, "%s: Sidetone busy while off!\n", ++ __func__); ++ status = -EPERM; ++ } else { ++ status = -EBUSY; ++ } ++ dev_dbg(component->dev, "%s: Exit\n", __func__); ++ return status; ++ } ++ ++ snd_soc_component_write(component, AB8500_SIDFIRADR, 0); ++ ++ for (param = 0; param < AB8500_SID_FIR_COEFFS; param++) { ++ snd_soc_component_write(component, AB8500_SIDFIRCOEF1, 0); ++ snd_soc_component_write(component, AB8500_SIDFIRCOEF2, 0); ++ } ++ ++ snd_soc_component_update_bits(component, AB8500_SIDFIRADR, ++ BIT(AB8500_SIDFIRADR_FIRSIDSET), ++ BIT(AB8500_SIDFIRADR_FIRSIDSET)); ++ snd_soc_component_update_bits(component, AB8500_SIDFIRADR, ++ BIT(AB8500_SIDFIRADR_FIRSIDSET), 0); ++ ++ drvdata->sid_status = SID_FIR_CONFIGURED; ++ ++ dev_dbg(component->dev, "%s: Exit\n", __func__); ++ ++ return status; ++} ++ + /* + * Controls - Non-DAPM ASoC + */ +@@ -1222,6 +1324,9 @@ static SOC_ENUM_SINGLE_DECL(soc_enum_bfifomast, + AB8500_FIFOCONF3, AB8500_FIFOCONF3_BFIFOMAST_SHIFT, + enum_slavemaster); + ++/* Sidetone */ ++static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_sidstate, enum_sid_state); ++ + /* ANC */ + + static struct snd_kcontrol_new ab8500_ctrls[] = { +@@ -1512,6 +1617,8 @@ static struct snd_kcontrol_new ab8500_ctrls[] = { + AB8500_ANC_WARP_DELAY_MIN, AB8500_ANC_WARP_DELAY_MAX, 0), + + /* Sidetone */ ++ SOC_ENUM_EXT("Sidetone Status", soc_enum_sidstate, ++ sid_status_control_get, sid_status_control_put), + SOC_SINGLE_STROBE("Sidetone Reset", + AB8500_SIDFIRADR, AB8500_SIDFIRADR_FIRSIDSET, 0), + }; +@@ -2041,8 +2148,10 @@ static void ab8500_codec_of_probe(struct device *dev, struct device_node *np, + + static int ab8500_codec_probe(struct snd_soc_component *component) + { ++ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); + struct device *dev = component->dev; + struct device_node *np = dev->of_node; ++ struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(dev); + struct ab8500_codec_platform_data codec_pdata; + int status; + +@@ -2075,6 +2184,10 @@ static int ab8500_codec_probe(struct snd_soc_component *component) + snd_soc_component_write(component, AB8500_SHORTCIRCONF, + BIT(AB8500_SHORTCIRCONF_HSZCDDIS)); + ++ snd_soc_dapm_disable_pin(dapm, "ANC Configure Input"); ++ ++ mutex_init(&drvdata->ctrl_lock); ++ + return status; + } + +@@ -2103,6 +2216,7 @@ static int ab8500_codec_driver_probe(struct platform_device *pdev) + GFP_KERNEL); + if (!drvdata) + return -ENOMEM; ++ drvdata->sid_status = SID_UNCONFIGURED; + dev_set_drvdata(&pdev->dev, drvdata); + + drvdata->regmap = devm_regmap_init(&pdev->dev, NULL, &pdev->dev, +diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c +--- a/sound/soc/codecs/ad1836.c ++++ b/sound/soc/codecs/ad1836.c +@@ -193,9 +193,15 @@ static int ad1836_hw_params(struct snd_pcm_substream *substream, + return 0; + } + ++static const u64 ad1836_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A | ++ SND_SOC_POSSIBLE_DAIFMT_IB_IF; ++ + static const struct snd_soc_dai_ops ad1836_dai_ops = { + .hw_params = ad1836_hw_params, + .set_fmt = ad1836_set_dai_fmt, ++ .auto_selectable_formats = &ad1836_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + #define AD183X_DAI(_name, num_dacs, num_adcs) \ +diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c +--- a/sound/soc/codecs/ad193x.c ++++ b/sound/soc/codecs/ad193x.c +@@ -394,6 +394,14 @@ static int ad193x_startup(struct snd_pcm_substream *substream, + &constr); + } + ++static const u64 ad193x_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_NB_IF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_IF; ++ + static const struct snd_soc_dai_ops ad193x_dai_ops = { + .startup = ad193x_startup, + .hw_params = ad193x_hw_params, +@@ -401,6 +409,8 @@ static const struct snd_soc_dai_ops ad193x_dai_ops = { + .set_tdm_slot = ad193x_set_tdm_slot, + .set_sysclk = ad193x_set_dai_sysclk, + .set_fmt = ad193x_set_dai_fmt, ++ .auto_selectable_formats = &ad193x_selectable_formats, ++ .num_auto_selectable_formats = 1, + .no_capture_mute = 1, + }; + +diff --git a/sound/soc/codecs/adau7118-hw.c b/sound/soc/codecs/adau7118-hw.c +--- a/sound/soc/codecs/adau7118-hw.c ++++ b/sound/soc/codecs/adau7118-hw.c +@@ -22,7 +22,7 @@ static const struct of_device_id adau7118_of_match[] = { + MODULE_DEVICE_TABLE(of, adau7118_of_match); + + static const struct platform_device_id adau7118_id[] = { +- { .name = "adau7118" }, ++ { .name = "adau7118" }, + { } + }; + MODULE_DEVICE_TABLE(platform, adau7118_id); +diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c +--- a/sound/soc/codecs/adav80x.c ++++ b/sound/soc/codecs/adav80x.c +@@ -743,11 +743,19 @@ static void adav80x_dai_shutdown(struct snd_pcm_substream *substream, + adav80x->rate = 0; + } + ++static const u64 adav80x_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF; ++ + static const struct snd_soc_dai_ops adav80x_dai_ops = { + .set_fmt = adav80x_set_dai_fmt, + .hw_params = adav80x_hw_params, + .startup = adav80x_dai_startup, + .shutdown = adav80x_dai_shutdown, ++ .auto_selectable_formats = &adav80x_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + #define ADAV80X_PLAYBACK_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ +diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c +--- a/sound/soc/codecs/ak4613.c ++++ b/sound/soc/codecs/ak4613.c +@@ -95,6 +95,7 @@ + * see + * AK4613_ENABLE_TDM_TEST + */ ++#include + #include + #include + #include +@@ -384,7 +385,7 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream, + struct ak4613_priv *priv = snd_soc_component_get_drvdata(component); + struct device *dev = component->dev; + +- mutex_lock(&priv->lock); ++ guard(mutex)(&priv->lock); + priv->cnt--; + if (priv->cnt < 0) { + dev_err(dev, "unexpected counter error\n"); +@@ -392,7 +393,6 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream, + } + if (!priv->cnt) + priv->ctrl1 = 0; +- mutex_unlock(&priv->lock); + } + + static void ak4613_hw_constraints(struct ak4613_priv *priv, +@@ -507,10 +507,9 @@ static int ak4613_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_component *component = dai->component; + struct ak4613_priv *priv = snd_soc_component_get_drvdata(component); + +- mutex_lock(&priv->lock); ++ guard(mutex)(&priv->lock); + ak4613_hw_constraints(priv, substream); + priv->cnt++; +- mutex_unlock(&priv->lock); + + return 0; + } +@@ -599,45 +598,47 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, + */ + ret = -EINVAL; + +- mutex_lock(&priv->lock); +- if (priv->cnt > 1) { +- /* +- * If it was already working, use current priv->ctrl1 +- */ +- ret = 0; +- } else { +- /* +- * It is not yet working, +- */ +- unsigned int channel = params_channels(params); +- u8 tdm; ++ scoped_guard(mutex, &priv->lock) { ++ if (priv->cnt > 1) { ++ /* ++ * If it was already working, use current priv->ctrl1 ++ */ ++ ret = 0; ++ } else { ++ /* ++ * It is not yet working, ++ */ ++ unsigned int channel = params_channels(params); ++ u8 tdm; + +- /* STEREO or TDM */ +- if (channel == 2) +- tdm = AK4613_CONFIG_MODE_STEREO; +- else +- tdm = AK4613_CONFIG_GET(priv, MODE); ++ /* STEREO or TDM */ ++ if (channel == 2) ++ tdm = AK4613_CONFIG_MODE_STEREO; ++ else ++ tdm = AK4613_CONFIG_GET(priv, MODE); + +- for (i = ARRAY_SIZE(ak4613_iface) - 1; i >= 0; i--) { +- const struct ak4613_interface *iface = ak4613_iface + i; ++ for (i = ARRAY_SIZE(ak4613_iface) - 1; i >= 0; i--) { ++ const struct ak4613_interface *iface = ak4613_iface + i; + +- if ((iface->fmt == fmt) && (iface->width == width)) { +- /* +- * Ctrl1 +- * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | +- * |TDM1|TDM0|DIF2|DIF1|DIF0|ATS1|ATS0|SMUTE| +- * < tdm > < iface->dif > +- */ +- priv->ctrl1 = (tdm << 6) | (iface->dif << 3); +- ret = 0; +- break; ++ if (iface->fmt == fmt && iface->width == width) { ++ /* ++ * Ctrl1 ++ * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | ++ * |TDM1|TDM0|DIF2|DIF1|DIF0|ATS1|ATS0|SMUTE| ++ * < tdm > < iface->dif > ++ */ ++ priv->ctrl1 = (tdm << 6) | (iface->dif << 3); ++ ret = 0; ++ break; ++ } + } + } + } +- mutex_unlock(&priv->lock); + +- if (ret < 0) +- goto hw_params_end; ++ if (ret < 0) { ++ dev_warn(dev, "unsupported data width/format combination\n"); ++ return ret; ++ } + + snd_soc_component_update_bits(component, CTRL1, FMT_MASK, priv->ctrl1); + snd_soc_component_update_bits(component, CTRL2, DFS_MASK, ctrl2); +@@ -645,10 +646,6 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, + snd_soc_component_update_bits(component, ICTRL, ICTRL_MASK, priv->ic); + snd_soc_component_update_bits(component, OCTRL, OCTRL_MASK, priv->oc); + +-hw_params_end: +- if (ret < 0) +- dev_warn(dev, "unsupported data width/format combination\n"); +- + return ret; + } + +diff --git a/sound/soc/codecs/arizona-jack.c b/sound/soc/codecs/arizona-jack.c +--- a/sound/soc/codecs/arizona-jack.c ++++ b/sound/soc/codecs/arizona-jack.c +@@ -5,6 +5,7 @@ + * Copyright (C) 2012-2014 Wolfson Microelectronics plc + */ + ++#include + #include + #include + #include +@@ -707,15 +708,13 @@ static void arizona_micd_timeout_work(struct work_struct *work) + struct arizona_priv, + micd_timeout_work.work); + +- mutex_lock(&info->lock); ++ guard(mutex)(&info->lock); + + dev_dbg(info->arizona->dev, "MICD timed out, reporting HP\n"); + + info->detecting = false; + + arizona_identify_headphone(info); +- +- mutex_unlock(&info->lock); + } + + static int arizona_micd_adc_read(struct arizona_priv *info) +@@ -921,12 +920,11 @@ static void arizona_micd_detect(struct work_struct *work) + + cancel_delayed_work_sync(&info->micd_timeout_work); + +- mutex_lock(&info->lock); ++ guard(mutex)(&info->lock); + + /* If the cable was removed while measuring ignore the result */ + if (!(info->jack->status & SND_JACK_MECHANICAL)) { + dev_dbg(arizona->dev, "Ignoring MICDET for removed cable\n"); +- mutex_unlock(&info->lock); + return; + } + +@@ -936,7 +934,6 @@ static void arizona_micd_detect(struct work_struct *work) + arizona_button_reading(info); + + pm_runtime_mark_last_busy(arizona->dev); +- mutex_unlock(&info->lock); + } + + static irqreturn_t arizona_micdet(int irq, void *data) +@@ -948,10 +945,10 @@ static irqreturn_t arizona_micdet(int irq, void *data) + cancel_delayed_work_sync(&info->micd_detect_work); + cancel_delayed_work_sync(&info->micd_timeout_work); + +- mutex_lock(&info->lock); +- if (!info->detecting) +- debounce = 0; +- mutex_unlock(&info->lock); ++ scoped_guard(mutex, &info->lock) { ++ if (!info->detecting) ++ debounce = 0; ++ } + + if (debounce) + queue_delayed_work(system_power_efficient_wq, +@@ -969,9 +966,8 @@ static void arizona_hpdet_work(struct work_struct *work) + struct arizona_priv, + hpdet_work.work); + +- mutex_lock(&info->lock); ++ guard(mutex)(&info->lock); + arizona_start_hpdet_acc_id(info); +- mutex_unlock(&info->lock); + } + + static int arizona_hpdet_wait(struct arizona_priv *info) +@@ -1018,9 +1014,9 @@ static irqreturn_t arizona_jackdet(int irq, void *data) + cancelled_hp = cancel_delayed_work_sync(&info->hpdet_work); + cancelled_mic = cancel_delayed_work_sync(&info->micd_timeout_work); + +- pm_runtime_get_sync(arizona->dev); ++ guard(pm_runtime_active_auto)(arizona->dev); + +- mutex_lock(&info->lock); ++ guard(mutex)(&info->lock); + + if (info->micd_clamp) { + mask = ARIZONA_MICD_CLAMP_STS; +@@ -1036,8 +1032,6 @@ static irqreturn_t arizona_jackdet(int irq, void *data) + ret = regmap_read(arizona->regmap, ARIZONA_AOD_IRQ_RAW_STATUS, &val); + if (ret) { + dev_err(arizona->dev, "Failed to read jackdet status: %d\n", ret); +- mutex_unlock(&info->lock); +- pm_runtime_put_autosuspend(arizona->dev); + return IRQ_NONE; + } + +@@ -1056,62 +1050,61 @@ static irqreturn_t arizona_jackdet(int irq, void *data) + &info->micd_timeout_work, + msecs_to_jiffies(micd_timeout)); + } ++ } else { ++ info->last_jackdet = val; + +- goto out; +- } +- info->last_jackdet = val; ++ if (info->last_jackdet == present) { ++ dev_dbg(arizona->dev, "Detected jack\n"); ++ snd_soc_jack_report(info->jack, SND_JACK_MECHANICAL, SND_JACK_MECHANICAL); + +- if (info->last_jackdet == present) { +- dev_dbg(arizona->dev, "Detected jack\n"); +- snd_soc_jack_report(info->jack, SND_JACK_MECHANICAL, SND_JACK_MECHANICAL); ++ info->detecting = true; ++ info->mic = false; ++ info->jack_flips = 0; + +- info->detecting = true; +- info->mic = false; +- info->jack_flips = 0; ++ if (!arizona->pdata.hpdet_acc_id) { ++ arizona_start_mic(info); ++ } else { ++ queue_delayed_work(system_power_efficient_wq, ++ &info->hpdet_work, ++ msecs_to_jiffies(HPDET_DEBOUNCE)); ++ } + +- if (!arizona->pdata.hpdet_acc_id) { +- arizona_start_mic(info); ++ if (info->micd_clamp || !arizona->pdata.jd_invert) ++ regmap_update_bits(arizona->regmap, ++ ARIZONA_JACK_DETECT_DEBOUNCE, ++ ARIZONA_MICD_CLAMP_DB | ++ ARIZONA_JD1_DB, 0); + } else { +- queue_delayed_work(system_power_efficient_wq, +- &info->hpdet_work, +- msecs_to_jiffies(HPDET_DEBOUNCE)); +- } ++ dev_dbg(arizona->dev, "Detected jack removal\n"); ++ ++ arizona_stop_mic(info); ++ ++ info->num_hpdet_res = 0; ++ for (i = 0; i < ARRAY_SIZE(info->hpdet_res); i++) ++ info->hpdet_res[i] = 0; ++ info->mic = false; ++ info->hpdet_done = false; ++ info->hpdet_retried = false; ++ ++ snd_soc_jack_report(info->jack, 0, ++ ARIZONA_JACK_MASK | info->micd_button_mask); ++ ++ /* ++ * If the jack was removed during a headphone detection we ++ * need to wait for the headphone detection to finish, as ++ * it can not be aborted. We don't want to be able to start ++ * a new headphone detection from a fresh insert until this ++ * one is finished. ++ */ ++ arizona_hpdet_wait(info); + +- if (info->micd_clamp || !arizona->pdata.jd_invert) + regmap_update_bits(arizona->regmap, + ARIZONA_JACK_DETECT_DEBOUNCE, +- ARIZONA_MICD_CLAMP_DB | +- ARIZONA_JD1_DB, 0); +- } else { +- dev_dbg(arizona->dev, "Detected jack removal\n"); +- +- arizona_stop_mic(info); +- +- info->num_hpdet_res = 0; +- for (i = 0; i < ARRAY_SIZE(info->hpdet_res); i++) +- info->hpdet_res[i] = 0; +- info->mic = false; +- info->hpdet_done = false; +- info->hpdet_retried = false; +- +- snd_soc_jack_report(info->jack, 0, ARIZONA_JACK_MASK | info->micd_button_mask); +- +- /* +- * If the jack was removed during a headphone detection we +- * need to wait for the headphone detection to finish, as +- * it can not be aborted. We don't want to be able to start +- * a new headphone detection from a fresh insert until this +- * one is finished. +- */ +- arizona_hpdet_wait(info); +- +- regmap_update_bits(arizona->regmap, +- ARIZONA_JACK_DETECT_DEBOUNCE, +- ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB, +- ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB); ++ ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB, ++ ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB); ++ } + } + +-out: + /* Clear trig_sts to make sure DCVDD is not forced up */ + regmap_write(arizona->regmap, ARIZONA_AOD_WKUP_AND_TRIG, + ARIZONA_MICD_CLAMP_FALL_TRIG_STS | +@@ -1119,10 +1112,6 @@ static irqreturn_t arizona_jackdet(int irq, void *data) + ARIZONA_JD1_FALL_TRIG_STS | + ARIZONA_JD1_RISE_TRIG_STS); + +- mutex_unlock(&info->lock); +- +- pm_runtime_put_autosuspend(arizona->dev); +- + return IRQ_HANDLED; + } + +diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c +--- a/sound/soc/codecs/arizona.c ++++ b/sound/soc/codecs/arizona.c +@@ -7,6 +7,7 @@ + * Author: Mark Brown + */ + ++#include + #include + #include + #include +@@ -1158,17 +1159,16 @@ int arizona_dvfs_up(struct snd_soc_component *component, unsigned int flags) + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + int ret = 0; + +- mutex_lock(&priv->dvfs_lock); ++ guard(mutex)(&priv->dvfs_lock); + + if (!priv->dvfs_cached && !priv->dvfs_reqs) { + ret = arizona_dvfs_enable(component); + if (ret) +- goto err; ++ return ret; + } + + priv->dvfs_reqs |= flags; +-err: +- mutex_unlock(&priv->dvfs_lock); ++ + return ret; + } + EXPORT_SYMBOL_GPL(arizona_dvfs_up); +@@ -1179,7 +1179,7 @@ int arizona_dvfs_down(struct snd_soc_component *component, unsigned int flags) + unsigned int old_reqs; + int ret = 0; + +- mutex_lock(&priv->dvfs_lock); ++ guard(mutex)(&priv->dvfs_lock); + + old_reqs = priv->dvfs_reqs; + priv->dvfs_reqs &= ~flags; +@@ -1187,7 +1187,6 @@ int arizona_dvfs_down(struct snd_soc_component *component, unsigned int flags) + if (!priv->dvfs_cached && old_reqs && !priv->dvfs_reqs) + ret = arizona_dvfs_disable(component); + +- mutex_unlock(&priv->dvfs_lock); + return ret; + } + EXPORT_SYMBOL_GPL(arizona_dvfs_down); +@@ -1199,7 +1198,7 @@ int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w, + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + int ret = 0; + +- mutex_lock(&priv->dvfs_lock); ++ guard(mutex)(&priv->dvfs_lock); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: +@@ -1222,7 +1221,6 @@ int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w, + break; + } + +- mutex_unlock(&priv->dvfs_lock); + return ret; + } + EXPORT_SYMBOL_GPL(arizona_dvfs_sysclk_ev); +@@ -1657,13 +1655,11 @@ static void arizona_wm5102_set_dac_comp(struct snd_soc_component *component, + { 0x80, 0x0 }, + }; + +- mutex_lock(&arizona->dac_comp_lock); +- +- dac_comp[1].def = arizona->dac_comp_coeff; +- if (rate >= 176400) +- dac_comp[2].def = arizona->dac_comp_enabled; +- +- mutex_unlock(&arizona->dac_comp_lock); ++ scoped_guard(mutex, &arizona->dac_comp_lock) { ++ dac_comp[1].def = arizona->dac_comp_coeff; ++ if (rate >= 176400) ++ dac_comp[2].def = arizona->dac_comp_enabled; ++ } + + regmap_multi_reg_write(arizona->regmap, + dac_comp, +diff --git a/sound/soc/codecs/aw87390.c b/sound/soc/codecs/aw87390.c +--- a/sound/soc/codecs/aw87390.c ++++ b/sound/soc/codecs/aw87390.c +@@ -7,6 +7,7 @@ + // Author: Weidong Wang + // + ++#include + #include + #include + #include +@@ -225,11 +226,10 @@ static int aw87390_profile_set(struct snd_kcontrol *kcontrol, + struct aw87390 *aw87390 = snd_soc_component_get_drvdata(codec); + int ret; + +- mutex_lock(&aw87390->lock); ++ guard(mutex)(&aw87390->lock); + ret = aw87390_dev_set_profile_index(aw87390->aw_pa, ucontrol->value.integer.value[0]); + if (ret) { + dev_dbg(codec->dev, "profile index does not change\n"); +- mutex_unlock(&aw87390->lock); + return 0; + } + +@@ -238,8 +238,6 @@ static int aw87390_profile_set(struct snd_kcontrol *kcontrol, + aw87390_power_on(aw87390->aw_pa); + } + +- mutex_unlock(&aw87390->lock); +- + return 1; + } + +@@ -250,7 +248,7 @@ static const struct snd_kcontrol_new aw87390_controls[] = { + + static int aw87390_request_firmware_file(struct aw87390 *aw87390) + { +- const struct firmware *cont = NULL; ++ const struct firmware *cont __free(firmware) = NULL; + int ret; + + aw87390->aw_pa->fw_status = AW87390_DEV_FW_FAILED; +@@ -265,14 +263,11 @@ static int aw87390_request_firmware_file(struct aw87390 *aw87390) + + aw87390->aw_cfg = devm_kzalloc(aw87390->aw_pa->dev, + struct_size(aw87390->aw_cfg, data, cont->size), GFP_KERNEL); +- if (!aw87390->aw_cfg) { +- release_firmware(cont); ++ if (!aw87390->aw_cfg) + return -ENOMEM; +- } + + aw87390->aw_cfg->len = cont->size; + memcpy(aw87390->aw_cfg->data, cont->data, cont->size); +- release_firmware(cont); + + ret = aw88395_dev_load_acf_check(aw87390->aw_pa, aw87390->aw_cfg); + if (ret) { +@@ -280,14 +275,12 @@ static int aw87390_request_firmware_file(struct aw87390 *aw87390) + return ret; + } + +- mutex_lock(&aw87390->lock); ++ guard(mutex)(&aw87390->lock); + + ret = aw88395_dev_cfg_load(aw87390->aw_pa, aw87390->aw_cfg); + if (ret) + dev_err(aw87390->aw_pa->dev, "aw_dev acf parse failed\n"); + +- mutex_unlock(&aw87390->lock); +- + return ret; + } + +diff --git a/sound/soc/codecs/aw88081.c b/sound/soc/codecs/aw88081.c +--- a/sound/soc/codecs/aw88081.c ++++ b/sound/soc/codecs/aw88081.c +@@ -7,6 +7,7 @@ + // Author: Weidong Wang + // + ++#include + #include + #include + #include +@@ -763,9 +764,8 @@ static void aw88081_startup_work(struct work_struct *work) + struct aw88081 *aw88081 = + container_of(work, struct aw88081, start_work.work); + +- mutex_lock(&aw88081->lock); ++ guard(mutex)(&aw88081->lock); + aw88081_start_pa(aw88081); +- mutex_unlock(&aw88081->lock); + } + + static void aw88081_start(struct aw88081 *aw88081, bool sync_start) +@@ -942,11 +942,10 @@ static int aw88081_profile_set(struct snd_kcontrol *kcontrol, + int ret; + + /* pa stop or stopping just set profile */ +- mutex_lock(&aw88081->lock); ++ guard(mutex)(&aw88081->lock); + ret = aw88081_dev_set_profile_index(aw88081->aw_pa, ucontrol->value.integer.value[0]); + if (ret) { + dev_dbg(codec->dev, "profile index does not change"); +- mutex_unlock(&aw88081->lock); + return 0; + } + +@@ -955,8 +954,6 @@ static int aw88081_profile_set(struct snd_kcontrol *kcontrol, + aw88081_start(aw88081, AW88081_SYNC_START); + } + +- mutex_unlock(&aw88081->lock); +- + return 1; + } + +@@ -1136,7 +1133,7 @@ static int aw88081_dev_init(struct aw88081 *aw88081, struct aw_container *aw_cfg + + static int aw88081_request_firmware_file(struct aw88081 *aw88081) + { +- const struct firmware *cont = NULL; ++ const struct firmware *cont __free(firmware) = NULL; + struct aw_container *aw_cfg; + int ret; + +@@ -1150,26 +1147,21 @@ static int aw88081_request_firmware_file(struct aw88081 *aw88081) + AW88081_ACF_FILE, cont ? cont->size : 0); + + aw_cfg = devm_kzalloc(aw88081->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL); +- if (!aw_cfg) { +- release_firmware(cont); ++ if (!aw_cfg) + return -ENOMEM; +- } ++ + aw_cfg->len = (int)cont->size; + memcpy(aw_cfg->data, cont->data, cont->size); + + aw88081->aw_cfg = aw_cfg; + +- release_firmware(cont); +- + ret = aw88395_dev_load_acf_check(aw88081->aw_pa, aw88081->aw_cfg); + if (ret) + return ret; + +- mutex_lock(&aw88081->lock); +- ret = aw88081_dev_init(aw88081, aw88081->aw_cfg); +- mutex_unlock(&aw88081->lock); ++ guard(mutex)(&aw88081->lock); + +- return ret; ++ return aw88081_dev_init(aw88081, aw88081->aw_cfg); + } + + static int aw88081_playback_event(struct snd_soc_dapm_widget *w, +@@ -1178,7 +1170,7 @@ static int aw88081_playback_event(struct snd_soc_dapm_widget *w, + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct aw88081 *aw88081 = snd_soc_component_get_drvdata(component); + +- mutex_lock(&aw88081->lock); ++ guard(mutex)(&aw88081->lock); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + aw88081_start(aw88081, AW88081_ASYNC_START); +@@ -1189,7 +1181,6 @@ static int aw88081_playback_event(struct snd_soc_dapm_widget *w, + default: + break; + } +- mutex_unlock(&aw88081->lock); + + return 0; + } +diff --git a/sound/soc/codecs/aw88166.c b/sound/soc/codecs/aw88166.c +--- a/sound/soc/codecs/aw88166.c ++++ b/sound/soc/codecs/aw88166.c +@@ -7,6 +7,7 @@ + // Author: Weidong Wang + // + ++#include + #include + #include + #include +@@ -1173,9 +1174,8 @@ static void aw88166_startup_work(struct work_struct *work) + struct aw88166 *aw88166 = + container_of(work, struct aw88166, start_work.work); + +- mutex_lock(&aw88166->lock); ++ guard(mutex)(&aw88166->lock); + aw88166_start_pa(aw88166); +- mutex_unlock(&aw88166->lock); + } + + static void aw88166_start(struct aw88166 *aw88166, bool sync_start) +@@ -1413,11 +1413,10 @@ static int aw88166_profile_set(struct snd_kcontrol *kcontrol, + struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec); + int ret; + +- mutex_lock(&aw88166->lock); ++ guard(mutex)(&aw88166->lock); + ret = aw88166_dev_set_profile_index(aw88166->aw_pa, ucontrol->value.integer.value[0]); + if (ret) { + dev_dbg(codec->dev, "profile index does not change"); +- mutex_unlock(&aw88166->lock); + return 0; + } + +@@ -1426,8 +1425,6 @@ static int aw88166_profile_set(struct snd_kcontrol *kcontrol, + aw88166_start(aw88166, AW88166_SYNC_START); + } + +- mutex_unlock(&aw88166->lock); +- + return 1; + } + +@@ -1573,7 +1570,7 @@ static int aw88166_dev_init(struct aw88166 *aw88166, struct aw_container *aw_cfg + + static int aw88166_request_firmware_file(struct aw88166 *aw88166) + { +- const struct firmware *cont = NULL; ++ const struct firmware *cont __free(firmware) = NULL; + const char *fw_name; + int ret; + +@@ -1593,13 +1590,11 @@ static int aw88166_request_firmware_file(struct aw88166 *aw88166) + + aw88166->aw_cfg = devm_kzalloc(aw88166->aw_pa->dev, + struct_size(aw88166->aw_cfg, data, cont->size), GFP_KERNEL); +- if (!aw88166->aw_cfg) { +- release_firmware(cont); ++ if (!aw88166->aw_cfg) + return -ENOMEM; +- } ++ + aw88166->aw_cfg->len = (int)cont->size; + memcpy(aw88166->aw_cfg->data, cont->data, cont->size); +- release_firmware(cont); + + ret = aw88395_dev_load_acf_check(aw88166->aw_pa, aw88166->aw_cfg); + if (ret) { +@@ -1607,12 +1602,12 @@ static int aw88166_request_firmware_file(struct aw88166 *aw88166) + return ret; + } + +- mutex_lock(&aw88166->lock); +- /* aw device init */ +- ret = aw88166_dev_init(aw88166, aw88166->aw_cfg); +- if (ret) +- dev_err(aw88166->aw_pa->dev, "dev init failed\n"); +- mutex_unlock(&aw88166->lock); ++ scoped_guard(mutex, &aw88166->lock) { ++ /* aw device init */ ++ ret = aw88166_dev_init(aw88166, aw88166->aw_cfg); ++ if (ret) ++ dev_err(aw88166->aw_pa->dev, "dev init failed\n"); ++ } + + return ret; + } +@@ -1639,7 +1634,7 @@ static int aw88166_playback_event(struct snd_soc_dapm_widget *w, + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct aw88166 *aw88166 = snd_soc_component_get_drvdata(component); + +- mutex_lock(&aw88166->lock); ++ guard(mutex)(&aw88166->lock); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + aw88166_start(aw88166, AW88166_ASYNC_START); +@@ -1650,7 +1645,6 @@ static int aw88166_playback_event(struct snd_soc_dapm_widget *w, + default: + break; + } +- mutex_unlock(&aw88166->lock); + + return 0; + } +diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c +--- a/sound/soc/codecs/aw88261.c ++++ b/sound/soc/codecs/aw88261.c +@@ -8,6 +8,7 @@ + // Author: Weidong Wang + // + ++#include + #include + #include + #include +@@ -960,11 +961,10 @@ static int aw88261_profile_set(struct snd_kcontrol *kcontrol, + int ret; + + /* pa stop or stopping just set profile */ +- mutex_lock(&aw88261->lock); ++ guard(mutex)(&aw88261->lock); + ret = aw88261_dev_set_profile_index(aw88261->aw_pa, ucontrol->value.integer.value[0]); + if (ret) { + dev_dbg(codec->dev, "profile index does not change"); +- mutex_unlock(&aw88261->lock); + return 0; + } + +@@ -973,8 +973,6 @@ static int aw88261_profile_set(struct snd_kcontrol *kcontrol, + aw88261_start(aw88261); + } + +- mutex_unlock(&aw88261->lock); +- + return 1; + } + +@@ -1038,7 +1036,7 @@ static int aw88261_playback_event(struct snd_soc_dapm_widget *w, + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); + +- mutex_lock(&aw88261->lock); ++ guard(mutex)(&aw88261->lock); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + aw88261_start(aw88261); +@@ -1049,7 +1047,6 @@ static int aw88261_playback_event(struct snd_soc_dapm_widget *w, + default: + break; + } +- mutex_unlock(&aw88261->lock); + + return 0; + } +@@ -1152,7 +1149,7 @@ static int aw88261_dev_init(struct aw88261 *aw88261, struct aw_container *aw_cfg + + static int aw88261_request_firmware_file(struct aw88261 *aw88261) + { +- const struct firmware *cont = NULL; ++ const struct firmware *cont __free(firmware) = NULL; + struct aw_container *aw_cfg; + const char *fw_name; + int ret; +@@ -1172,13 +1169,11 @@ static int aw88261_request_firmware_file(struct aw88261 *aw88261) + fw_name, cont ? cont->size : 0); + + aw_cfg = devm_kzalloc(aw88261->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL); +- if (!aw_cfg) { +- release_firmware(cont); ++ if (!aw_cfg) + return -ENOMEM; +- } ++ + aw_cfg->len = (int)cont->size; + memcpy(aw_cfg->data, cont->data, cont->size); +- release_firmware(cont); + + aw88261->aw_cfg = aw_cfg; + +@@ -1188,12 +1183,12 @@ static int aw88261_request_firmware_file(struct aw88261 *aw88261) + return ret; + } + +- mutex_lock(&aw88261->lock); +- /* aw device init */ +- ret = aw88261_dev_init(aw88261, aw88261->aw_cfg); +- if (ret) +- dev_err(aw88261->aw_pa->dev, "dev init failed"); +- mutex_unlock(&aw88261->lock); ++ scoped_guard(mutex, &aw88261->lock) { ++ /* aw device init */ ++ ret = aw88261_dev_init(aw88261, aw88261->aw_cfg); ++ if (ret) ++ dev_err(aw88261->aw_pa->dev, "dev init failed"); ++ } + + return ret; + } +diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c +--- a/sound/soc/codecs/aw88395/aw88395.c ++++ b/sound/soc/codecs/aw88395/aw88395.c +@@ -8,6 +8,7 @@ + // Author: Weidong Wang + // + ++#include + #include + #include + #include +@@ -51,9 +52,8 @@ static void aw88395_startup_work(struct work_struct *work) + struct aw88395 *aw88395 = + container_of(work, struct aw88395, start_work.work); + +- mutex_lock(&aw88395->lock); ++ guard(mutex)(&aw88395->lock); + aw88395_start_pa(aw88395); +- mutex_unlock(&aw88395->lock); + } + + static void aw88395_start(struct aw88395 *aw88395, bool sync_start) +@@ -224,11 +224,10 @@ static int aw88395_profile_set(struct snd_kcontrol *kcontrol, + int ret; + + /* pa stop or stopping just set profile */ +- mutex_lock(&aw88395->lock); ++ guard(mutex)(&aw88395->lock); + ret = aw88395_dev_set_profile_index(aw88395->aw_pa, ucontrol->value.integer.value[0]); + if (ret < 0) { + dev_dbg(codec->dev, "profile index does not change"); +- mutex_unlock(&aw88395->lock); + return 0; + } + +@@ -237,8 +236,6 @@ static int aw88395_profile_set(struct snd_kcontrol *kcontrol, + aw88395_start(aw88395, AW88395_SYNC_START); + } + +- mutex_unlock(&aw88395->lock); +- + return 1; + } + +@@ -366,7 +363,7 @@ static int aw88395_playback_event(struct snd_soc_dapm_widget *w, + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component); + +- mutex_lock(&aw88395->lock); ++ guard(mutex)(&aw88395->lock); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + aw88395_start(aw88395, AW88395_ASYNC_START); +@@ -377,7 +374,6 @@ static int aw88395_playback_event(struct snd_soc_dapm_widget *w, + default: + break; + } +- mutex_unlock(&aw88395->lock); + + return 0; + } +@@ -461,7 +457,7 @@ static void aw88395_hw_reset(struct aw88395 *aw88395) + + static int aw88395_request_firmware_file(struct aw88395 *aw88395) + { +- const struct firmware *cont = NULL; ++ const struct firmware *cont __free(firmware) = NULL; + struct aw_container *aw_cfg; + int ret; + +@@ -477,13 +473,11 @@ static int aw88395_request_firmware_file(struct aw88395 *aw88395) + AW88395_ACF_FILE, cont ? cont->size : 0); + + aw_cfg = devm_kzalloc(aw88395->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL); +- if (!aw_cfg) { +- release_firmware(cont); ++ if (!aw_cfg) + return -ENOMEM; +- } ++ + aw_cfg->len = (int)cont->size; + memcpy(aw_cfg->data, cont->data, cont->size); +- release_firmware(cont); + + aw88395->aw_cfg = aw_cfg; + +@@ -495,12 +489,12 @@ static int aw88395_request_firmware_file(struct aw88395 *aw88395) + + dev_dbg(aw88395->aw_pa->dev, "%s : bin load success\n", __func__); + +- mutex_lock(&aw88395->lock); +- /* aw device init */ +- ret = aw88395_dev_init(aw88395->aw_pa, aw88395->aw_cfg); +- if (ret < 0) +- dev_err(aw88395->aw_pa->dev, "dev init failed"); +- mutex_unlock(&aw88395->lock); ++ scoped_guard(mutex, &aw88395->lock) { ++ /* aw device init */ ++ ret = aw88395_dev_init(aw88395->aw_pa, aw88395->aw_cfg); ++ if (ret < 0) ++ dev_err(aw88395->aw_pa->dev, "dev init failed"); ++ } + + return ret; + } +diff --git a/sound/soc/codecs/aw88395/aw88395_device.c b/sound/soc/codecs/aw88395/aw88395_device.c +--- a/sound/soc/codecs/aw88395/aw88395_device.c ++++ b/sound/soc/codecs/aw88395/aw88395_device.c +@@ -8,6 +8,7 @@ + // Author: Ben Yi + // + ++#include + #include + #include + #include +@@ -70,7 +71,7 @@ int aw_dev_dsp_write(struct aw_device *aw_dev, + u32 reg_value; + int ret; + +- mutex_lock(&aw_dev->dsp_lock); ++ guard(mutex)(&aw_dev->dsp_lock); + switch (data_type) { + case AW_DSP_16_DATA: + ret = aw_dev_dsp_write_16bit(aw_dev, dsp_addr, dsp_data); +@@ -93,7 +94,6 @@ int aw_dev_dsp_write(struct aw_device *aw_dev, + /* clear dsp chip select state*/ + if (regmap_read(aw_dev->regmap, AW88395_ID_REG, ®_value)) + dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret); +- mutex_unlock(&aw_dev->dsp_lock); + + return ret; + } +@@ -156,7 +156,7 @@ int aw_dev_dsp_read(struct aw_device *aw_dev, + u32 reg_value; + int ret; + +- mutex_lock(&aw_dev->dsp_lock); ++ guard(mutex)(&aw_dev->dsp_lock); + switch (data_type) { + case AW_DSP_16_DATA: + ret = aw_dev_dsp_read_16bit(aw_dev, dsp_addr, dsp_data); +@@ -179,7 +179,6 @@ int aw_dev_dsp_read(struct aw_device *aw_dev, + /* clear dsp chip select state*/ + if (regmap_read(aw_dev->regmap, AW88395_ID_REG, ®_value)) + dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret); +- mutex_unlock(&aw_dev->dsp_lock); + + return ret; + } +@@ -1110,42 +1109,36 @@ static int aw_dev_dsp_update_container(struct aw_device *aw_dev, + #ifdef AW88395_DSP_I2C_WRITES + u32 tmp_len; + +- mutex_lock(&aw_dev->dsp_lock); ++ guard(mutex)(&aw_dev->dsp_lock); + ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, base); + if (ret) +- goto error_operation; ++ return ret; + + for (i = 0; i < len; i += AW88395_MAX_RAM_WRITE_BYTE_SIZE) { + tmp_len = min(len - i, AW88395_MAX_RAM_WRITE_BYTE_SIZE); + ret = regmap_raw_write(aw_dev->regmap, AW88395_DSPMDAT_REG, + &data[i], tmp_len); + if (ret) +- goto error_operation; ++ return ret; + } +- mutex_unlock(&aw_dev->dsp_lock); + #else + __be16 reg_val; + +- mutex_lock(&aw_dev->dsp_lock); ++ guard(mutex)(&aw_dev->dsp_lock); + /* i2c write */ + ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, base); + if (ret) +- goto error_operation; ++ return ret; + for (i = 0; i < len; i += 2) { + reg_val = cpu_to_be16p((u16 *)(data + i)); + ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, + (u16)reg_val); + if (ret) +- goto error_operation; ++ return ret; + } +- mutex_unlock(&aw_dev->dsp_lock); + #endif + + return 0; +- +-error_operation: +- mutex_unlock(&aw_dev->dsp_lock); +- return ret; + } + + static int aw_dev_dsp_update_fw(struct aw_device *aw_dev, +@@ -1231,14 +1224,14 @@ static int aw_dev_check_sram(struct aw_device *aw_dev) + { + unsigned int reg_val; + +- mutex_lock(&aw_dev->dsp_lock); ++ guard(mutex)(&aw_dev->dsp_lock); + /* check the odd bits of reg 0x40 */ + regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, AW88395_DSP_ODD_NUM_BIT_TEST); + regmap_read(aw_dev->regmap, AW88395_DSPMADD_REG, ®_val); + if (reg_val != AW88395_DSP_ODD_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check reg 0x40 odd bit failed, read[0x%x] != write[0x%x]", + reg_val, AW88395_DSP_ODD_NUM_BIT_TEST); +- goto error; ++ return -EPERM; + } + + /* check the even bits of reg 0x40 */ +@@ -1247,7 +1240,7 @@ static int aw_dev_check_sram(struct aw_device *aw_dev) + if (reg_val != AW88395_DSP_EVEN_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check reg 0x40 even bit failed, read[0x%x] != write[0x%x]", + reg_val, AW88395_DSP_EVEN_NUM_BIT_TEST); +- goto error; ++ return -EPERM; + } + + /* check dsp_fw_base_addr */ +@@ -1256,7 +1249,7 @@ static int aw_dev_check_sram(struct aw_device *aw_dev) + if (reg_val != AW88395_DSP_EVEN_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check dsp fw addr failed, read[0x%x] != write[0x%x]", + reg_val, AW88395_DSP_EVEN_NUM_BIT_TEST); +- goto error; ++ return -EPERM; + } + + /* check dsp_cfg_base_addr */ +@@ -1265,15 +1258,10 @@ static int aw_dev_check_sram(struct aw_device *aw_dev) + if (reg_val != AW88395_DSP_ODD_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] != write[0x%x]", + reg_val, AW88395_DSP_ODD_NUM_BIT_TEST); +- goto error; ++ return -EPERM; + } +- mutex_unlock(&aw_dev->dsp_lock); + + return 0; +- +-error: +- mutex_unlock(&aw_dev->dsp_lock); +- return -EPERM; + } + + int aw88395_dev_fw_update(struct aw_device *aw_dev, bool up_dsp_fw_en, bool force_up_en) +diff --git a/sound/soc/codecs/aw88399-lib.c b/sound/soc/codecs/aw88399-lib.c +--- a/sound/soc/codecs/aw88399-lib.c ++++ b/sound/soc/codecs/aw88399-lib.c +@@ -1282,7 +1282,7 @@ static int aw88399_dev_init(struct aw88399 *aw88399, struct aw_container *aw_cfg + + int aw88399_request_firmware_file(struct aw88399 *aw88399) + { +- const struct firmware *cont = NULL; ++ const struct firmware *cont __free(firmware) = NULL; + int ret; + + aw88399->aw_pa->fw_status = AW88399_DEV_FW_FAILED; +@@ -1298,13 +1298,11 @@ int aw88399_request_firmware_file(struct aw88399 *aw88399) + + aw88399->aw_cfg = devm_kzalloc(aw88399->aw_pa->dev, + struct_size(aw88399->aw_cfg, data, cont->size), GFP_KERNEL); +- if (!aw88399->aw_cfg) { +- release_firmware(cont); ++ if (!aw88399->aw_cfg) + return -ENOMEM; +- } ++ + aw88399->aw_cfg->len = (int)cont->size; + memcpy(aw88399->aw_cfg->data, cont->data, cont->size); +- release_firmware(cont); + + ret = aw88395_dev_load_acf_check(aw88399->aw_pa, aw88399->aw_cfg); + if (ret) { +diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c +--- a/sound/soc/codecs/aw88399.c ++++ b/sound/soc/codecs/aw88399.c +@@ -7,6 +7,7 @@ + // Author: Weidong Wang + // + ++#include + #include + #include + #include +@@ -497,11 +498,10 @@ static int aw88399_profile_set(struct snd_kcontrol *kcontrol, + struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec); + int ret; + +- mutex_lock(&aw88399->lock); ++ guard(mutex)(&aw88399->lock); + ret = aw88399_dev_set_profile_index(aw88399->aw_pa, ucontrol->value.integer.value[0]); + if (ret) { + dev_dbg(codec->dev, "profile index does not change"); +- mutex_unlock(&aw88399->lock); + return 0; + } + +@@ -510,8 +510,6 @@ static int aw88399_profile_set(struct snd_kcontrol *kcontrol, + aw88399_start(aw88399, AW88399_SYNC_START); + } + +- mutex_unlock(&aw88399->lock); +- + return 1; + } + +@@ -690,7 +688,7 @@ static int aw88399_playback_event(struct snd_soc_dapm_widget *w, + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct aw88399 *aw88399 = snd_soc_component_get_drvdata(component); + +- mutex_lock(&aw88399->lock); ++ guard(mutex)(&aw88399->lock); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + aw88399_start(aw88399, AW88399_ASYNC_START); +@@ -701,7 +699,6 @@ static int aw88399_playback_event(struct snd_soc_dapm_widget *w, + default: + break; + } +- mutex_unlock(&aw88399->lock); + + return 0; + } +diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c +--- a/sound/soc/codecs/bt-sco.c ++++ b/sound/soc/codecs/bt-sco.c +@@ -85,13 +85,9 @@ static int bt_sco_probe(struct platform_device *pdev) + } + + static const struct platform_device_id bt_sco_driver_ids[] = { +- { +- .name = "dfbmcs320", +- }, +- { +- .name = "bt-sco", +- }, +- {}, ++ { .name = "dfbmcs320" }, ++ { .name = "bt-sco" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids); + +diff --git a/sound/soc/codecs/cpcap.c b/sound/soc/codecs/cpcap.c +--- a/sound/soc/codecs/cpcap.c ++++ b/sound/soc/codecs/cpcap.c +@@ -26,14 +26,6 @@ + /* Register 9 - CPCAP_REG_INTS2 --- Interrupt Sense 2 */ + #define CPCAP_BIT_PTT_S 11 /* Push To Talk */ + +-/* Register 512 CPCAP_REG_VAUDIOC --- Audio Regulator and Bias Voltage */ +-#define CPCAP_BIT_AUDIO_LOW_PWR 6 +-#define CPCAP_BIT_AUD_LOWPWR_SPEED 5 +-#define CPCAP_BIT_VAUDIOPRISTBY 4 +-#define CPCAP_BIT_VAUDIO_MODE1 2 +-#define CPCAP_BIT_VAUDIO_MODE0 1 +-#define CPCAP_BIT_V_AUDIO_EN 0 +- + /* Register 513 CPCAP_REG_CC --- CODEC */ + #define CPCAP_BIT_CDC_CLK2 15 + #define CPCAP_BIT_CDC_CLK1 14 +@@ -239,7 +231,6 @@ struct cpcap_reg_info { + }; + + static const struct cpcap_reg_info cpcap_default_regs[] = { +- { CPCAP_REG_VAUDIOC, 0x003F, 0x0000 }, + { CPCAP_REG_CC, 0xFFFF, 0x0000 }, + { CPCAP_REG_CC, 0xFFFF, 0x0000 }, + { CPCAP_REG_CDI, 0xBFFF, 0x0000 }, +@@ -1265,6 +1256,7 @@ static int cpcap_voice_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) + { ++ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_component *component = dai->component; + struct device *dev = component->dev; + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); +@@ -1298,7 +1290,7 @@ static int cpcap_voice_hw_params(struct snd_pcm_substream *substream, + return err; + } + +- return 0; ++ return snd_soc_runtime_set_dai_fmt(rtd, rtd->dai_link->dai_fmt); + } + + static int cpcap_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, +@@ -1391,121 +1383,8 @@ static int cpcap_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, + return 0; + } + +- +-/* +- * Configure codec for voice call if requested. +- * +- * We can configure most with snd_soc_dai_set_sysclk(), snd_soc_dai_set_fmt() +- * and snd_soc_dai_set_tdm_slot(). This function configures the rest of the +- * cpcap related hardware as CPU is not involved in the voice call. +- */ +-static int cpcap_voice_call(struct cpcap_audio *cpcap, struct snd_soc_dai *dai, +- bool voice_call) +-{ +- int mask, err; +- +- /* Modem to codec VAUDIO_MODE1 */ +- mask = BIT(CPCAP_BIT_VAUDIO_MODE1); +- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_VAUDIOC, +- mask, voice_call ? mask : 0); +- if (err) +- return err; +- +- /* Clear MIC1_MUX for call */ +- mask = BIT(CPCAP_BIT_MIC1_MUX); +- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI, +- mask, voice_call ? 0 : mask); +- if (err) +- return err; +- +- /* Set MIC2_MUX for call */ +- mask = BIT(CPCAP_BIT_MB_ON1L) | BIT(CPCAP_BIT_MB_ON1R) | +- BIT(CPCAP_BIT_MIC2_MUX) | BIT(CPCAP_BIT_MIC2_PGA_EN); +- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI, +- mask, voice_call ? mask : 0); +- if (err) +- return err; +- +- /* Enable LDSP for call */ +- mask = BIT(CPCAP_BIT_A2_LDSP_L_EN) | BIT(CPCAP_BIT_A2_LDSP_R_EN); +- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXOA, +- mask, voice_call ? mask : 0); +- if (err) +- return err; +- +- /* Enable CPCAP_BIT_PGA_CDC_EN for call */ +- mask = BIT(CPCAP_BIT_PGA_CDC_EN); +- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXCOA, +- mask, voice_call ? mask : 0); +- if (err) +- return err; +- +- /* Unmute voice for call */ +- if (dai) { +- err = snd_soc_dai_digital_mute(dai, !voice_call, +- SNDRV_PCM_STREAM_PLAYBACK); +- if (err) +- return err; +- } +- +- /* Set modem to codec mic CDC and HPF for call */ +- mask = BIT(CPCAP_BIT_MIC2_CDC_EN) | BIT(CPCAP_BIT_CDC_EN_RX) | +- BIT(CPCAP_BIT_AUDOHPF_1) | BIT(CPCAP_BIT_AUDOHPF_0) | +- BIT(CPCAP_BIT_AUDIHPF_1) | BIT(CPCAP_BIT_AUDIHPF_0); +- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CC, +- mask, voice_call ? mask : 0); +- if (err) +- return err; +- +- /* Enable modem to codec CDC for call*/ +- mask = BIT(CPCAP_BIT_CDC_CLK_EN); +- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI, +- mask, voice_call ? mask : 0); +- +- return err; +-} +- +-static int cpcap_voice_set_tdm_slot(struct snd_soc_dai *dai, +- unsigned int tx_mask, unsigned int rx_mask, +- int slots, int slot_width) +-{ +- struct snd_soc_component *component = dai->component; +- struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); +- int err, ts_mask, mask; +- bool voice_call; +- +- /* +- * Primitive test for voice call, probably needs more checks +- * later on for 16-bit calls detected, Bluetooth headset etc. +- */ +- if (tx_mask == 0 && rx_mask == 1 && slot_width == 8) +- voice_call = true; +- else +- voice_call = false; +- +- ts_mask = 0x7 << CPCAP_BIT_MIC2_TIMESLOT0; +- ts_mask |= 0x7 << CPCAP_BIT_MIC1_RX_TIMESLOT0; +- +- mask = (tx_mask & 0x7) << CPCAP_BIT_MIC2_TIMESLOT0; +- mask |= (rx_mask & 0x7) << CPCAP_BIT_MIC1_RX_TIMESLOT0; +- +- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI, +- ts_mask, mask); +- if (err) +- return err; +- +- err = cpcap_set_samprate(cpcap, CPCAP_DAI_VOICE, slot_width * 1000); +- if (err) +- return err; +- +- err = cpcap_voice_call(cpcap, dai, voice_call); +- if (err) +- return err; +- +- return 0; +-} +- +-static int cpcap_voice_set_mute(struct snd_soc_dai *dai, int mute, int direction) ++static int cpcap_voice_set_mute(struct snd_soc_dai *dai, ++ int mute, int direction) + { + struct snd_soc_component *component = dai->component; + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); +@@ -1526,7 +1405,6 @@ static const struct snd_soc_dai_ops cpcap_dai_voice_ops = { + .hw_params = cpcap_voice_hw_params, + .set_sysclk = cpcap_voice_set_dai_sysclk, + .set_fmt = cpcap_voice_set_dai_fmt, +- .set_tdm_slot = cpcap_voice_set_tdm_slot, + .mute_stream = cpcap_voice_set_mute, + .no_capture_mute = 1, + }; +diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c +--- a/sound/soc/codecs/cros_ec_codec.c ++++ b/sound/soc/codecs/cros_ec_codec.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -319,10 +320,18 @@ static int i2s_rx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) + (uint8_t *)&p, sizeof(p), NULL, 0); + } + ++static const u64 i2s_rx_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF; ++ + static const struct snd_soc_dai_ops i2s_rx_dai_ops = { + .hw_params = i2s_rx_hw_params, + .set_fmt = i2s_rx_set_fmt, + .set_bclk_ratio = i2s_rx_set_bclk_ratio, ++ .auto_selectable_formats = &i2s_rx_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static int i2s_rx_event(struct snd_soc_dapm_widget *w, +@@ -608,10 +617,10 @@ static void wov_copy_work(struct work_struct *w) + container_of(w, struct cros_ec_codec_priv, wov_copy_work.work); + int ret; + +- mutex_lock(&priv->wov_dma_lock); ++ guard(mutex)(&priv->wov_dma_lock); + if (!priv->wov_substream) { + dev_warn(priv->dev, "no pcm substream\n"); +- goto leave; ++ return; + } + + if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_AUDIO_SHM)) +@@ -624,8 +633,6 @@ static void wov_copy_work(struct work_struct *w) + msecs_to_jiffies(10)); + else if (ret) + dev_err(priv->dev, "failed to read audio data\n"); +-leave: +- mutex_unlock(&priv->wov_dma_lock); + } + + static int wov_enable_get(struct snd_kcontrol *kcontrol, +@@ -895,12 +902,11 @@ static int wov_pcm_hw_params(struct snd_soc_component *component, + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + +- mutex_lock(&priv->wov_dma_lock); ++ guard(mutex)(&priv->wov_dma_lock); + priv->wov_substream = substream; + priv->wov_rp = priv->wov_wp = 0; + priv->wov_dma_offset = 0; + priv->wov_burst_read = true; +- mutex_unlock(&priv->wov_dma_lock); + + return 0; + } +@@ -911,10 +917,10 @@ static int wov_pcm_hw_free(struct snd_soc_component *component, + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + +- mutex_lock(&priv->wov_dma_lock); +- wov_queue_dequeue(priv, wov_queue_size(priv)); +- priv->wov_substream = NULL; +- mutex_unlock(&priv->wov_dma_lock); ++ scoped_guard(mutex, &priv->wov_dma_lock) { ++ wov_queue_dequeue(priv, wov_queue_size(priv)); ++ priv->wov_substream = NULL; ++ } + + cancel_delayed_work_sync(&priv->wov_copy_work); + +diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c +--- a/sound/soc/codecs/cs-amp-lib.c ++++ b/sound/soc/codecs/cs-amp-lib.c +@@ -7,6 +7,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -83,10 +84,12 @@ static int cs_amp_write_cal_coeff(struct cs_dsp *dsp, + KUNIT_STATIC_STUB_REDIRECT(cs_amp_write_cal_coeff, dsp, controls, ctl_name, val); + + if (IS_REACHABLE(CONFIG_FW_CS_DSP)) { +- mutex_lock(&dsp->pwr_lock); +- cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, controls->mem_region, controls->alg_id); +- ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, &beval, sizeof(beval)); +- mutex_unlock(&dsp->pwr_lock); ++ scoped_guard(mutex, &dsp->pwr_lock) { ++ cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, ++ controls->mem_region, ++ controls->alg_id); ++ ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, &beval, sizeof(beval)); ++ } + + if (ret < 0) { + dev_err(dsp->dev, "Failed to write to '%s': %d\n", ctl_name, ret); +diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c +--- a/sound/soc/codecs/cs35l32.c ++++ b/sound/soc/codecs/cs35l32.c +@@ -538,7 +538,15 @@ static int cs35l32_runtime_resume(struct device *dev) + gpiod_set_value_cansleep(cs35l32->reset_gpio, 1); + + regcache_cache_only(cs35l32->regmap, false); +- regcache_sync(cs35l32->regmap); ++ ret = regcache_sync(cs35l32->regmap); ++ if (ret) { ++ regcache_cache_only(cs35l32->regmap, true); ++ regcache_mark_dirty(cs35l32->regmap); ++ gpiod_set_value_cansleep(cs35l32->reset_gpio, 0); ++ regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies), ++ cs35l32->supplies); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c +--- a/sound/soc/codecs/cs35l56-sdw.c ++++ b/sound/soc/codecs/cs35l56-sdw.c +@@ -231,7 +231,7 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral) + * a soft reset. + */ + if (cs35l56->base.init_done) +- cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); ++ cs35l56_unmask_soundwire_interrupts(cs35l56); + + out: + pm_runtime_put_autosuspend(cs35l56->base.dev); +@@ -240,47 +240,17 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral) + static int cs35l56_sdw_interrupt(struct sdw_slave *peripheral, + struct sdw_slave_intr_status *status) + { +- struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); +- +- /* SoundWire core holds our pm_runtime when calling this function. */ +- +- dev_dbg(cs35l56->base.dev, "int control_port=%#x\n", status->control_port); +- +- if ((status->control_port & SDW_SCP_INT1_IMPL_DEF) == 0) +- return 0; +- + /* +- * Prevent bus manager suspending and possibly issuing a +- * bus-reset before the queued work has run. ++ * The IRQ itself was handled through the regmap_irq handler, this is ++ * just clearing up the additional Cirrus SoundWire registers that are ++ * not covered by the SoundWire framework or the IRQ handler itself. + */ +- pm_runtime_get_noresume(cs35l56->base.dev); +- +- /* +- * Mask and clear until it has been handled. +- * None of the interrupts are time-critical so use the +- * power-efficient queue. +- */ +- cs35l56_mask_soundwire_interrupts(peripheral); +- queue_work(system_power_efficient_wq, &cs35l56->sdw_irq_work); ++ sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1); ++ sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); + + return 0; + } + +-static void cs35l56_sdw_irq_work(struct work_struct *work) +-{ +- struct cs35l56_private *cs35l56 = container_of(work, +- struct cs35l56_private, +- sdw_irq_work); +- +- cs35l56_irq(-1, &cs35l56->base); +- +- /* unmask interrupts */ +- if (!cs35l56->sdw_irq_no_unmask) +- cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); +- +- pm_runtime_put_autosuspend(cs35l56->base.dev); +-} +- + static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral) + { + struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); +@@ -302,6 +272,7 @@ static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral) + prop->source_ports = BIT(CS35L56_SDW1_CAPTURE_PORT); + prop->sink_ports = BIT(CS35L56_SDW1_PLAYBACK_PORT); + prop->paging_support = true; ++ prop->use_domain_irq = true; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY | SDW_SCP_INT1_IMPL_DEF; + +@@ -406,7 +377,7 @@ static int __maybe_unused cs35l56_sdw_runtime_resume(struct device *dev) + if (ret) + return ret; + +- cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); ++ cs35l56_unmask_soundwire_interrupts(cs35l56); + + return 0; + } +@@ -418,21 +389,12 @@ static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev) + if (!cs35l56->base.init_done) + return 0; + +- cs35l56_disable_sdw_interrupts(cs35l56); ++ /* runtime_resume unmasks the interrupt */ ++ cs35l56_mask_soundwire_interrupts(cs35l56); + + return cs35l56_system_suspend(dev); + } + +-static int __maybe_unused cs35l56_sdw_system_resume(struct device *dev) +-{ +- struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); +- +- cs35l56->sdw_irq_no_unmask = false; +- /* runtime_resume re-enables the interrupt */ +- +- return cs35l56_system_resume(dev); +-} +- + static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id) + { + struct device *dev = &peripheral->dev; +@@ -447,7 +409,6 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi + cs35l56->base.dev = dev; + cs35l56->sdw_peripheral = peripheral; + cs35l56->sdw_link_num = peripheral->bus->link_id; +- INIT_WORK(&cs35l56->sdw_irq_work, cs35l56_sdw_irq_work); + + dev_set_drvdata(dev, cs35l56); + +@@ -457,6 +418,7 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi + regmap_config = &cs35l56_regmap_sdw; + break; + case 0x3563: ++ case 0x3562: + regmap_config = &cs35l63_regmap_sdw; + break; + default: +@@ -483,21 +445,21 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi + /* Start in cache-only until device is enumerated */ + regcache_cache_only(cs35l56->base.regmap, true); + +- return cs35l56_common_probe(cs35l56, -EINVAL); ++ return cs35l56_common_probe(cs35l56, peripheral->irq); + } + + static void cs35l56_sdw_remove(struct sdw_slave *peripheral) + { + struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); + +- cs35l56_disable_sdw_interrupts(cs35l56); ++ cs35l56_mask_soundwire_interrupts(cs35l56); + + cs35l56_remove(cs35l56); + } + + static const struct dev_pm_ops cs35l56_sdw_pm = { + SET_RUNTIME_PM_OPS(cs35l56_sdw_runtime_suspend, cs35l56_sdw_runtime_resume, NULL) +- SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_sdw_system_resume) ++ SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_system_resume) + LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early) + /* NOIRQ stage not needed, SoundWire doesn't use a hard IRQ */ + }; +@@ -505,6 +467,7 @@ static const struct dev_pm_ops cs35l56_sdw_pm = { + static const struct sdw_device_id cs35l56_sdw_id[] = { + SDW_SLAVE_ENTRY(0x01FA, 0x3556, 0x3556), + SDW_SLAVE_ENTRY(0x01FA, 0x3557, 0x3557), ++ SDW_SLAVE_ENTRY(0x01FA, 0x3562, 0x3562), + SDW_SLAVE_ENTRY(0x01FA, 0x3563, 0x3563), + {}, + }; +diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c +--- a/sound/soc/codecs/cs35l56-shared.c ++++ b/sound/soc/codecs/cs35l56-shared.c +@@ -96,6 +96,7 @@ int cs35l56_set_patch(struct cs35l56_base *cs35l56_base) + ARRAY_SIZE(cs35l56_patch_fw)); + break; + case 0x63: ++ case 0x62: + ret = regmap_register_patch(cs35l56_base->regmap, cs35l63_patch_fw, + ARRAY_SIZE(cs35l63_patch_fw)); + break; +@@ -389,6 +390,7 @@ static void cs35l56_set_fw_reg_table(struct cs35l56_base *cs35l56_base) + } + break; + case 0x63: ++ case 0x62: + cs35l56_base->fw_reg = &cs35l63_fw_reg; + break; + } +@@ -595,6 +597,7 @@ void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire) + } + break; + case 0x63: ++ case 0x62: + regmap_multi_reg_write_bypassed(cs35l56_base->regmap, + cs35l63_system_reset_seq, + ARRAY_SIZE(cs35l63_system_reset_seq)); +@@ -613,26 +616,7 @@ void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire) + } + EXPORT_SYMBOL_NS_GPL(cs35l56_system_reset, "SND_SOC_CS35L56_SHARED"); + +-int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq) +-{ +- int ret; +- +- if (irq < 1) +- return 0; +- +- ret = devm_request_threaded_irq(cs35l56_base->dev, irq, NULL, cs35l56_irq, +- IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, +- "cs35l56", cs35l56_base); +- if (!ret) +- cs35l56_base->irq = irq; +- else +- dev_err(cs35l56_base->dev, "Failed to get IRQ: %d\n", ret); +- +- return ret; +-} +-EXPORT_SYMBOL_NS_GPL(cs35l56_irq_request, "SND_SOC_CS35L56_SHARED"); +- +-irqreturn_t cs35l56_irq(int irq, void *data) ++static irqreturn_t cs35l56_irq(int irq, void *data) + { + struct cs35l56_base *cs35l56_base = data; + unsigned int status1 = 0, status8 = 0, status20 = 0; +@@ -640,23 +624,22 @@ irqreturn_t cs35l56_irq(int irq, void *data) + unsigned int val; + int rv; + +- irqreturn_t ret = IRQ_NONE; +- + if (!cs35l56_base->init_done) + return IRQ_NONE; + +- mutex_lock(&cs35l56_base->irq_lock); ++ guard(mutex)(&cs35l56_base->irq_lock); + +- rv = pm_runtime_resume_and_get(cs35l56_base->dev); ++ PM_RUNTIME_ACQUIRE_IF_ENABLED(cs35l56_base->dev, pm); ++ rv = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (rv < 0) { + dev_err(cs35l56_base->dev, "irq: failed to get pm_runtime: %d\n", rv); +- goto err_unlock; ++ return IRQ_NONE; + } + + regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_STATUS, &val); + if ((val & CS35L56_IRQ1_STS_MASK) == 0) { + dev_dbg(cs35l56_base->dev, "Spurious IRQ: no pending interrupt\n"); +- goto err; ++ return IRQ_NONE; + } + + /* Ack interrupts */ +@@ -680,7 +663,7 @@ irqreturn_t cs35l56_irq(int irq, void *data) + + /* Check to see if unmasked bits are active */ + if (!status1 && !status8 && !status20) +- goto err; ++ return IRQ_NONE; + + if (status1 & CS35L56_AMP_SHORT_ERR_EINT1_MASK) + dev_crit(cs35l56_base->dev, "Amp short error\n"); +@@ -688,16 +671,27 @@ irqreturn_t cs35l56_irq(int irq, void *data) + if (status8 & CS35L56_TEMP_ERR_EINT1_MASK) + dev_crit(cs35l56_base->dev, "Overtemp error\n"); + +- ret = IRQ_HANDLED; ++ return IRQ_HANDLED; ++} + +-err: +- pm_runtime_put(cs35l56_base->dev); +-err_unlock: +- mutex_unlock(&cs35l56_base->irq_lock); ++int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq) ++{ ++ int ret; ++ ++ if (irq < 1) ++ return 0; ++ ++ ret = devm_request_threaded_irq(cs35l56_base->dev, irq, NULL, cs35l56_irq, ++ IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, ++ "cs35l56", cs35l56_base); ++ if (!ret) ++ cs35l56_base->irq = irq; ++ else ++ dev_err(cs35l56_base->dev, "Failed to get IRQ: %d\n", ret); + + return ret; + } +-EXPORT_SYMBOL_NS_GPL(cs35l56_irq, "SND_SOC_CS35L56_SHARED"); ++EXPORT_SYMBOL_NS_GPL(cs35l56_irq_request, "SND_SOC_CS35L56_SHARED"); + + int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base) + { +@@ -816,14 +810,10 @@ int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_sou + if (!cs35l56_base->init_done) + return 0; + +- if (!cs35l56_base->can_hibernate) +- goto out_sync; +- +- /* Must be done before releasing cache-only */ +- if (!is_soundwire) ++ /* Hibernate wake must be done before releasing cache-only */ ++ if (cs35l56_base->can_hibernate && !is_soundwire) + cs35l56_issue_wake_event(cs35l56_base); + +-out_sync: + ret = cs35l56_wait_for_firmware_boot(cs35l56_base); + if (ret) { + dev_err(cs35l56_base->dev, "Hibernate wake failed: %d\n", ret); +@@ -1470,6 +1460,7 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) + cs35l56_base->calibration_controls = &cs35l56_calibration_controls; + break; + case 0x35A630: ++ case 0x35A620: + cs35l56_base->calibration_controls = &cs35l63_calibration_controls; + devid = devid >> 4; + break; +diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c +--- a/sound/soc/codecs/cs35l56.c ++++ b/sound/soc/codecs/cs35l56.c +@@ -18,11 +18,9 @@ + #include + #include + #include +-#include + #include + #include + #include +-#include + #include + #include + #include +@@ -39,55 +37,49 @@ + #include "wm_adsp.h" + #include "cs35l56.h" + +-/* +- * snd_soc_register_component() can call component_probe() on all instances +- * in a card, so deferred registration must be protected across all instances. +- */ +-static DEFINE_MUTEX(cs35l56_component_register_lock); +-static bool cs35l56_shutting_down; +- +-void cs35l56_mask_soundwire_interrupts(struct sdw_slave *peripheral) ++void cs35l56_mask_soundwire_interrupts(struct cs35l56_private *cs35l56) + { + /* ++ * Mask unconditionally. ++ * + * The read of GEN_INT_STAT_1 is required as per the SoundWire spec + * for interrupt status bits to clear. + * GEN_INT_MASK_1 masks the _inputs_ to GEN_INT_STAT1. + */ +- sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); +- sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1); +- sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); ++ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); ++ sdw_read_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1); ++ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); + } + EXPORT_SYMBOL_NS_GPL(cs35l56_mask_soundwire_interrupts, "SND_SOC_CS35L56_CORE"); + +-void cs35l56_unmask_soundwire_interrupts(struct sdw_slave *peripheral) ++void cs35l56_unmask_soundwire_interrupts(struct cs35l56_private *cs35l56) + { +- sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, CS35L56_SDW_INT_MASK_CODEC_IRQ); ++ if (!cs35l56->base.irq) ++ return; ++ ++ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, ++ CS35L56_SDW_INT_MASK_CODEC_IRQ); + } + EXPORT_SYMBOL_NS_GPL(cs35l56_unmask_soundwire_interrupts, "SND_SOC_CS35L56_CORE"); + +-void cs35l56_disable_sdw_interrupts(struct cs35l56_private *cs35l56) ++static void cs35l56_disable_sdw_interrupts(struct cs35l56_private *cs35l56) + { + if (!cs35l56->sdw_peripheral) + return; + +- cs35l56->sdw_irq_no_unmask = true; +- flush_work(&cs35l56->sdw_irq_work); +- +- /* Mask interrupts and flush in case sdw_irq_work was queued again */ +- cs35l56_mask_soundwire_interrupts(cs35l56->sdw_peripheral); +- flush_work(&cs35l56->sdw_irq_work); ++ cs35l56_mask_soundwire_interrupts(cs35l56); ++ if (cs35l56->base.irq) ++ disable_irq(cs35l56->base.irq); + } +-EXPORT_SYMBOL_NS_GPL(cs35l56_disable_sdw_interrupts, "SND_SOC_CS35L56_CORE"); + +-void cs35l56_enable_sdw_interrupts(struct cs35l56_private *cs35l56) ++static void cs35l56_enable_sdw_interrupts(struct cs35l56_private *cs35l56) + { +- if (!cs35l56->sdw_peripheral) ++ if (!cs35l56->sdw_peripheral || !cs35l56->base.irq) + return; + +- cs35l56->sdw_irq_no_unmask = false; +- cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); ++ enable_irq(cs35l56->base.irq); ++ cs35l56_unmask_soundwire_interrupts(cs35l56); + } +-EXPORT_SYMBOL_NS_GPL(cs35l56_enable_sdw_interrupts, "SND_SOC_CS35L56_CORE"); + + static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); +@@ -837,11 +829,7 @@ static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing + { + int ret; + +- /* +- * Disable SoundWire interrupts to prevent race with IRQ work. +- * Setting sdw_irq_no_unmask prevents the handler re-enabling +- * the SoundWire interrupt. +- */ ++ /* Disable SoundWire interrupts to prevent race with IRQ handler thread */ + cs35l56_disable_sdw_interrupts(cs35l56); + + ret = cs35l56_firmware_shutdown(&cs35l56->base); +@@ -1405,6 +1393,7 @@ static int _cs35l56_component_probe(struct snd_soc_component *component) + ARRAY_SIZE(cs35l56_controls)); + break; + case 0x63: ++ case 0x62: + ret = snd_soc_add_component_controls(component, cs35l63_controls, + ARRAY_SIZE(cs35l63_controls)); + break; +@@ -1968,11 +1957,6 @@ static void cs35l56_component_register_work(struct work_struct *work) + component_register_work); + int ret; + +- guard(mutex)(&cs35l56_component_register_lock); +- +- if (cs35l56_shutting_down) +- return; +- + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(cs35l56->base.dev, pm_err); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm_err); + if (ret) { +@@ -2233,37 +2217,6 @@ EXPORT_NS_GPL_DEV_PM_OPS(cs35l56_pm_ops_i2c_spi, SND_SOC_CS35L56_CORE) = { + }; + #endif + +-static int cs35l56_reboot_notify(struct notifier_block *nb, +- unsigned long action, void *data) +-{ +- guard(mutex)(&cs35l56_component_register_lock); +- cs35l56_shutting_down = true; +- +- return NOTIFY_DONE; +-} +- +-static struct notifier_block cs35l56_reboot_notifier = { +- .notifier_call = cs35l56_reboot_notify, +-}; +- +-static int __init cs35l56_modinit(void) +-{ +- /* +- * Use reboot notifier to prevent race between shutdown and +- * snd_soc_register_component(). Driver shutdown() callback would +- * run too late, after device_shutdown() is already walking the +- * device list that component registration can modify. +- */ +- return register_reboot_notifier(&cs35l56_reboot_notifier); +-} +-module_init(cs35l56_modinit); +- +-static void __exit cs35l56_modexit(void) +-{ +- unregister_reboot_notifier(&cs35l56_reboot_notifier); +-} +-module_exit(cs35l56_modexit); +- + MODULE_DESCRIPTION("ASoC CS35L56 driver"); + MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED"); + MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB"); +diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h +--- a/sound/soc/codecs/cs35l56.h ++++ b/sound/soc/codecs/cs35l56.h +@@ -32,7 +32,6 @@ struct sdw_slave; + struct cs35l56_private { + struct wm_adsp dsp; /* must be first member */ + struct cs35l56_base base; +- struct work_struct component_register_work; + struct work_struct dsp_work; + struct workqueue_struct *dsp_wq; + struct snd_soc_component *component; +@@ -40,11 +39,8 @@ struct cs35l56_private { + struct sdw_slave *sdw_peripheral; + struct regmap *sdw_bus_regmap; + const char *fallback_fw_suffix; +- struct work_struct sdw_irq_work; +- bool sdw_irq_no_unmask; + bool soft_resetting; + bool sdw_attached; +- bool component_registered; + struct completion init_completion; + + int speaker_id; +@@ -67,10 +63,8 @@ static inline struct cs35l56_private *cs35l56_private_from_base(struct cs35l56_b + + extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi; + +-void cs35l56_mask_soundwire_interrupts(struct sdw_slave *peripheral); +-void cs35l56_unmask_soundwire_interrupts(struct sdw_slave *peripheral); +-void cs35l56_disable_sdw_interrupts(struct cs35l56_private *cs35l56); +-void cs35l56_enable_sdw_interrupts(struct cs35l56_private *cs35l56); ++void cs35l56_mask_soundwire_interrupts(struct cs35l56_private *cs35l56); ++void cs35l56_unmask_soundwire_interrupts(struct cs35l56_private *cs35l56); + + int cs35l56_system_suspend(struct device *dev); + int cs35l56_system_suspend_late(struct device *dev); +@@ -78,7 +72,6 @@ int cs35l56_system_suspend_no_irq(struct device *dev); + int cs35l56_system_resume_no_irq(struct device *dev); + int cs35l56_system_resume_early(struct device *dev); + int cs35l56_system_resume(struct device *dev); +-irqreturn_t cs35l56_irq(int irq, void *data); + int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq); + int cs35l56_common_probe(struct cs35l56_private *cs35l56, int irq); + int cs35l56_init(struct cs35l56_private *cs35l56); +diff --git a/sound/soc/codecs/cs40l50-codec.c b/sound/soc/codecs/cs40l50-codec.c +--- a/sound/soc/codecs/cs40l50-codec.c ++++ b/sound/soc/codecs/cs40l50-codec.c +@@ -288,8 +288,8 @@ static int cs40l50_codec_driver_probe(struct platform_device *pdev) + } + + static const struct platform_device_id cs40l50_id[] = { +- { "cs40l50-codec", }, +- {} ++ { .name = "cs40l50-codec" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, cs40l50_id); + +diff --git a/sound/soc/codecs/cs42l42-sdw.c b/sound/soc/codecs/cs42l42-sdw.c +--- a/sound/soc/codecs/cs42l42-sdw.c ++++ b/sound/soc/codecs/cs42l42-sdw.c +@@ -481,10 +481,20 @@ static int cs42l42_sdw_runtime_resume(struct device *dev) + regcache_cache_only(cs42l42->regmap, false); + + /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */ +- regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); +- regcache_sync(cs42l42->regmap); ++ ret = regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); ++ if (ret) ++ goto err_sync; ++ ++ ret = regcache_sync(cs42l42->regmap); ++ if (ret) ++ goto err_sync; + + return 0; ++ ++err_sync: ++ regcache_cache_only(cs42l42->regmap, true); ++ regcache_mark_dirty(cs42l42->regmap); ++ return ret; + } + + static int cs42l42_sdw_resume(struct device *dev) +diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c +--- a/sound/soc/codecs/cs42l42.c ++++ b/sound/soc/codecs/cs42l42.c +@@ -9,6 +9,7 @@ + * Author: Michael White + */ + ++#include + #include + #include + #include +@@ -565,7 +566,7 @@ static int cs42l42_set_jack(struct snd_soc_component *component, struct snd_soc_ + struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); + + /* Prevent race with interrupt handler */ +- mutex_lock(&cs42l42->irq_lock); ++ guard(mutex)(&cs42l42->irq_lock); + cs42l42->jack = jk; + + if (jk) { +@@ -581,7 +582,6 @@ static int cs42l42_set_jack(struct snd_soc_component *component, struct snd_soc_ + break; + } + } +- mutex_unlock(&cs42l42->irq_lock); + + return 0; + } +@@ -1668,13 +1668,10 @@ irqreturn_t cs42l42_irq_thread(int irq, void *data) + unsigned int current_button_status; + unsigned int i; + +- pm_runtime_get_sync(cs42l42->dev); +- mutex_lock(&cs42l42->irq_lock); +- if (cs42l42->suspended || !cs42l42->init_done) { +- mutex_unlock(&cs42l42->irq_lock); +- pm_runtime_put_autosuspend(cs42l42->dev); ++ guard(pm_runtime_active_auto)(cs42l42->dev); ++ guard(mutex)(&cs42l42->irq_lock); ++ if (cs42l42->suspended || !cs42l42->init_done) + return IRQ_NONE; +- } + + /* Read sticky registers to clear interurpt */ + for (i = 0; i < ARRAY_SIZE(stickies); i++) { +@@ -1774,9 +1771,6 @@ irqreturn_t cs42l42_irq_thread(int irq, void *data) + } + } + +- mutex_unlock(&cs42l42->irq_lock); +- pm_runtime_put_autosuspend(cs42l42->dev); +- + return IRQ_HANDLED; + } + EXPORT_SYMBOL_NS_GPL(cs42l42_irq_thread, "SND_SOC_CS42L42_CORE"); +@@ -2163,23 +2157,23 @@ int cs42l42_suspend(struct device *dev) + * future interrupts. This ensures a safe disable if the interrupt + * is shared. + */ +- mutex_lock(&cs42l42->irq_lock); +- cs42l42->suspended = true; ++ scoped_guard(mutex, &cs42l42->irq_lock) { ++ cs42l42->suspended = true; + +- /* Save register values that will be overwritten by shutdown sequence */ +- for (i = 0; i < ARRAY_SIZE(cs42l42_shutdown_seq); ++i) { +- regmap_read(cs42l42->regmap, cs42l42_shutdown_seq[i].reg, ®); +- save_regs[i] = (u8)reg; ++ /* Save register values that will be overwritten by shutdown sequence */ ++ for (i = 0; i < ARRAY_SIZE(cs42l42_shutdown_seq); ++i) { ++ regmap_read(cs42l42->regmap, cs42l42_shutdown_seq[i].reg, ®); ++ save_regs[i] = (u8)reg; ++ } ++ ++ /* Shutdown codec */ ++ regmap_multi_reg_write(cs42l42->regmap, ++ cs42l42_shutdown_seq, ++ ARRAY_SIZE(cs42l42_shutdown_seq)); ++ ++ /* All interrupt sources are now disabled */ + } + +- /* Shutdown codec */ +- regmap_multi_reg_write(cs42l42->regmap, +- cs42l42_shutdown_seq, +- ARRAY_SIZE(cs42l42_shutdown_seq)); +- +- /* All interrupt sources are now disabled */ +- mutex_unlock(&cs42l42->irq_lock); +- + /* Wait for power-down complete */ + msleep(CS42L42_PDN_DONE_TIME_MS); + ret = regmap_read_poll_timeout(cs42l42->regmap, +@@ -2250,13 +2244,13 @@ void cs42l42_resume_restore(struct device *dev) + regcache_cache_only(cs42l42->regmap, false); + regcache_mark_dirty(cs42l42->regmap); + +- mutex_lock(&cs42l42->irq_lock); +- /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */ +- regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); +- regcache_sync(cs42l42->regmap); ++ scoped_guard(mutex, &cs42l42->irq_lock) { ++ /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */ ++ regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); ++ regcache_sync(cs42l42->regmap); + +- cs42l42->suspended = false; +- mutex_unlock(&cs42l42->irq_lock); ++ cs42l42->suspended = false; ++ } + + dev_dbg(dev, "System resumed\n"); + } +diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c +--- a/sound/soc/codecs/cs42l43-jack.c ++++ b/sound/soc/codecs/cs42l43-jack.c +@@ -6,6 +6,7 @@ + // Cirrus Logic International Semiconductor Ltd. + + #include ++#include + #include + #include + #include +@@ -67,6 +68,21 @@ static int cs42l43_find_index(struct cs42l43_codec *priv, const char * const pro + return -EINVAL; + } + ++static void cs42l43_apply_accdet_config(struct cs42l43_codec *priv, ++ unsigned int autocontrol, ++ unsigned int pdncntl) ++{ ++ struct cs42l43 *cs42l43 = priv->core; ++ ++ regmap_update_bits(cs42l43->regmap, CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, ++ CS42L43_JACKDET_MODE_MASK | CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK | ++ CS42L43_HSBIAS_SENSE_TRIP_MASK, autocontrol); ++ regmap_update_bits(cs42l43->regmap, CS42L43_PDNCNTL, ++ CS42L43_RING_SENSE_EN_MASK, pdncntl); ++ ++ dev_dbg(priv->dev, "Successfully configured accessory detect\n"); ++} ++ + int cs42l43_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *d) + { +@@ -80,31 +96,34 @@ int cs42l43_set_jack(struct snd_soc_component *component, + + dev_dbg(priv->dev, "Configure accessory detect\n"); + +- ret = pm_runtime_resume_and_get(priv->dev); ++ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(priv->dev, pm); ++ ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) { + dev_err(priv->dev, "Failed to resume for jack config: %d\n", ret); + return ret; + } + +- mutex_lock(&priv->jack_lock); ++ guard(mutex)(&priv->jack_lock); + + priv->jack_hp = jack; + +- if (!jack) +- goto done; ++ if (!jack) { ++ cs42l43_apply_accdet_config(priv, autocontrol, pdncntl); ++ return 0; ++ } + + ret = device_property_count_u32(cs42l43->dev, "cirrus,buttons-ohms"); + if (ret != -EINVAL) { + if (ret < 0) { + dev_err(priv->dev, "Property cirrus,buttons-ohms malformed: %d\n", + ret); +- goto error; ++ return ret; + } + + if (ret > CS42L43_N_BUTTONS) { + ret = -EINVAL; + dev_err(priv->dev, "Property cirrus,buttons-ohms too many entries\n"); +- goto error; ++ return ret; + } + + ret = device_property_read_u32_array(cs42l43->dev, "cirrus,buttons-ohms", +@@ -112,7 +131,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, + if (ret < 0) { + dev_err(priv->dev, "Property cirrus,button-ohms malformed: %d\n", + ret); +- goto error; ++ return ret; + } + } else { + priv->buttons[0] = 70; +@@ -124,7 +143,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, + ret = cs42l43_find_index(priv, "cirrus,detect-us", 50000, &priv->detect_us, + cs42l43_accdet_us, ARRAY_SIZE(cs42l43_accdet_us)); + if (ret < 0) +- goto error; ++ return ret; + + hs2 |= ret << CS42L43_AUTO_HSDET_TIME_SHIFT; + +@@ -134,7 +153,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, + &priv->bias_ramp_ms, cs42l43_accdet_ramp_ms, + ARRAY_SIZE(cs42l43_accdet_ramp_ms)); + if (ret < 0) +- goto error; ++ return ret; + + hs2 |= ret << CS42L43_HSBIAS_RAMP_SHIFT; + +@@ -142,7 +161,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, + &priv->bias_sense_ua, cs42l43_accdet_bias_sense, + ARRAY_SIZE(cs42l43_accdet_bias_sense)); + if (ret < 0) +- goto error; ++ return ret; + + if (priv->bias_sense_ua) + autocontrol |= ret << CS42L43_HSBIAS_SENSE_TRIP_SHIFT; +@@ -154,7 +173,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, + &priv->tip_debounce_ms); + if (ret < 0 && ret != -EINVAL) { + dev_err(priv->dev, "Property cirrus,tip-debounce-ms malformed: %d\n", ret); +- goto error; ++ return ret; + } + + /* This tip sense invert is set normally, as TIPSENSE_INV already inverted */ +@@ -170,7 +189,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, + &priv->tip_fall_db_ms, cs42l43_accdet_db_ms, + ARRAY_SIZE(cs42l43_accdet_db_ms)); + if (ret < 0) +- goto error; ++ return ret; + + tip_deb |= ret << CS42L43_TIPSENSE_FALLING_DB_TIME_SHIFT; + +@@ -178,7 +197,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, + &priv->tip_rise_db_ms, cs42l43_accdet_db_ms, + ARRAY_SIZE(cs42l43_accdet_db_ms)); + if (ret < 0) +- goto error; ++ return ret; + + tip_deb |= ret << CS42L43_TIPSENSE_RISING_DB_TIME_SHIFT; + +@@ -199,7 +218,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, + NULL, cs42l43_accdet_db_ms, + ARRAY_SIZE(cs42l43_accdet_db_ms)); + if (ret < 0) +- goto error; ++ return ret; + + ring_deb |= ret << CS42L43_RINGSENSE_FALLING_DB_TIME_SHIFT; + +@@ -207,7 +226,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, + NULL, cs42l43_accdet_db_ms, + ARRAY_SIZE(cs42l43_accdet_db_ms)); + if (ret < 0) +- goto error; ++ return ret; + + ring_deb |= ret << CS42L43_RINGSENSE_RISING_DB_TIME_SHIFT; + pdncntl |= CS42L43_RING_SENSE_EN_MASK; +@@ -228,23 +247,9 @@ int cs42l43_set_jack(struct snd_soc_component *component, + CS42L43_HSBIAS_RAMP_MASK | CS42L43_HSDET_MODE_MASK | + CS42L43_AUTO_HSDET_TIME_MASK, hs2); + +-done: +- ret = 0; ++ cs42l43_apply_accdet_config(priv, autocontrol, pdncntl); + +- regmap_update_bits(cs42l43->regmap, CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, +- CS42L43_JACKDET_MODE_MASK | CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK | +- CS42L43_HSBIAS_SENSE_TRIP_MASK, autocontrol); +- regmap_update_bits(cs42l43->regmap, CS42L43_PDNCNTL, +- CS42L43_RING_SENSE_EN_MASK, pdncntl); +- +- dev_dbg(priv->dev, "Successfully configured accessory detect\n"); +- +-error: +- mutex_unlock(&priv->jack_lock); +- +- pm_runtime_put_autosuspend(priv->dev); +- +- return ret; ++ return 0; + } + + static void cs42l43_start_hs_bias(struct cs42l43_codec *priv, bool type_detect) +@@ -370,22 +375,22 @@ irqreturn_t cs42l43_button_press(int irq, void *data) + { + struct cs42l43_codec *priv = data; + struct cs42l43 *cs42l43 = priv->core; +- irqreturn_t iret = IRQ_NONE; + unsigned int buttons = 0; + unsigned int val = 0; + int i, ret; + +- ret = pm_runtime_resume_and_get(priv->dev); ++ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(priv->dev, pm); ++ ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) { + dev_err(priv->dev, "Failed to resume for button press: %d\n", ret); +- return iret; ++ return IRQ_NONE; + } + +- mutex_lock(&priv->jack_lock); ++ guard(mutex)(&priv->jack_lock); + + if (!priv->button_detect_running) { + dev_dbg(priv->dev, "Spurious button press IRQ\n"); +- goto error; ++ return IRQ_NONE; + } + + // Wait for 2 full cycles of comb filter to ensure good reading +@@ -396,12 +401,12 @@ irqreturn_t cs42l43_button_press(int irq, void *data) + /* Bail if jack removed, the button is irrelevant and likely invalid */ + if (!cs42l43_jack_present(priv)) { + dev_dbg(priv->dev, "Button ignored due to removal\n"); +- goto error; ++ return IRQ_NONE; + } + + if (val & CS42L43_HSBIAS_CLAMP_STS_MASK) { + dev_dbg(priv->dev, "Button ignored due to bias sense\n"); +- goto error; ++ return IRQ_NONE; + } + + val = (val & CS42L43_HSDET_DC_STS_MASK) >> CS42L43_HSDET_DC_STS_SHIFT; +@@ -424,45 +429,32 @@ irqreturn_t cs42l43_button_press(int irq, void *data) + + snd_soc_jack_report(priv->jack_hp, buttons, CS42L43_JACK_BUTTONS); + +- iret = IRQ_HANDLED; +- +-error: +- mutex_unlock(&priv->jack_lock); +- +- pm_runtime_put_autosuspend(priv->dev); +- +- return iret; ++ return IRQ_HANDLED; + } + + irqreturn_t cs42l43_button_release(int irq, void *data) + { + struct cs42l43_codec *priv = data; +- irqreturn_t iret = IRQ_NONE; + int ret; + +- ret = pm_runtime_resume_and_get(priv->dev); ++ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(priv->dev, pm); ++ ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) { + dev_err(priv->dev, "Failed to resume for button release: %d\n", ret); +- return iret; ++ return IRQ_NONE; + } + +- mutex_lock(&priv->jack_lock); ++ guard(mutex)(&priv->jack_lock); + +- if (priv->button_detect_running) { +- dev_dbg(priv->dev, "Button release IRQ\n"); +- +- snd_soc_jack_report(priv->jack_hp, 0, CS42L43_JACK_BUTTONS); +- +- iret = IRQ_HANDLED; +- } else { ++ if (!priv->button_detect_running) { + dev_dbg(priv->dev, "Spurious button release IRQ\n"); ++ return IRQ_NONE; + } + +- mutex_unlock(&priv->jack_lock); ++ dev_dbg(priv->dev, "Button release IRQ\n"); ++ snd_soc_jack_report(priv->jack_hp, 0, CS42L43_JACK_BUTTONS); + +- pm_runtime_put_autosuspend(priv->dev); +- +- return iret; ++ return IRQ_HANDLED; + } + + void cs42l43_bias_sense_timeout(struct work_struct *work) +@@ -472,13 +464,14 @@ void cs42l43_bias_sense_timeout(struct work_struct *work) + struct cs42l43 *cs42l43 = priv->core; + int ret; + +- ret = pm_runtime_resume_and_get(priv->dev); ++ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(priv->dev, pm); ++ ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) { + dev_err(priv->dev, "Failed to resume for bias sense: %d\n", ret); + return; + } + +- mutex_lock(&priv->jack_lock); ++ guard(mutex)(&priv->jack_lock); + + if (cs42l43_jack_present(priv) && priv->button_detect_running) { + dev_dbg(priv->dev, "Bias sense timeout out, restore bias\n"); +@@ -491,10 +484,6 @@ void cs42l43_bias_sense_timeout(struct work_struct *work) + CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, + CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK); + } +- +- mutex_unlock(&priv->jack_lock); +- +- pm_runtime_put_autosuspend(priv->dev); + } + + static const struct reg_sequence cs42l43_3pole_patch[] = { +@@ -896,9 +885,8 @@ int cs42l43_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + +- mutex_lock(&priv->jack_lock); ++ guard(mutex)(&priv->jack_lock); + ucontrol->value.integer.value[0] = priv->jack_override; +- mutex_unlock(&priv->jack_lock); + + return 0; + } +@@ -914,17 +902,13 @@ int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u + if (override >= e->items) + return -EINVAL; + +- mutex_lock(&priv->jack_lock); ++ guard(mutex)(&priv->jack_lock); + +- if (!cs42l43_jack_present(priv)) { +- mutex_unlock(&priv->jack_lock); ++ if (!cs42l43_jack_present(priv)) + return -EBUSY; +- } + +- if (override == priv->jack_override) { +- mutex_unlock(&priv->jack_lock); ++ if (override == priv->jack_override) + return 0; +- } + + priv->jack_override = override; + +@@ -984,7 +968,5 @@ int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u + cs42l43_jack_override_modes[override].report); + } + +- mutex_unlock(&priv->jack_lock); +- + return 1; + } +diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c +--- a/sound/soc/codecs/cs42l43.c ++++ b/sound/soc/codecs/cs42l43.c +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1207,14 +1208,12 @@ static void cs42l43_spk_vu_sync(struct cs42l43_codec *priv) + { + struct cs42l43 *cs42l43 = priv->core; + +- mutex_lock(&priv->spk_vu_lock); ++ guard(mutex)(&priv->spk_vu_lock); + + regmap_update_bits(cs42l43->regmap, CS42L43_INTP_VOLUME_CTRL1, + CS42L43_AMP1_2_VU_MASK, CS42L43_AMP1_2_VU_MASK); + regmap_update_bits(cs42l43->regmap, CS42L43_INTP_VOLUME_CTRL1, + CS42L43_AMP1_2_VU_MASK, 0); +- +- mutex_unlock(&priv->spk_vu_lock); + } + + static int cs42l43_shutter_get(struct cs42l43_codec *priv, unsigned int shift) +@@ -1601,7 +1600,7 @@ static int cs42l43_pll_ev(struct snd_soc_dapm_widget *w, + struct cs42l43 *cs42l43 = priv->core; + int ret; + +- mutex_lock(&cs42l43->pll_lock); ++ guard(mutex)(&cs42l43->pll_lock); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: +@@ -1626,8 +1625,6 @@ static int cs42l43_pll_ev(struct snd_soc_dapm_widget *w, + break; + } + +- mutex_unlock(&cs42l43->pll_lock); +- + return ret; + } + +@@ -2565,13 +2562,10 @@ static int cs42l43_set_sysclk(struct snd_soc_component *component, int clk_id, + { + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; +- int ret; + +- mutex_lock(&cs42l43->pll_lock); +- ret = cs42l43_set_pll(priv, src, freq); +- mutex_unlock(&cs42l43->pll_lock); ++ guard(mutex)(&cs42l43->pll_lock); + +- return ret; ++ return cs42l43_set_pll(priv, src, freq); + } + + static int cs42l43_component_probe(struct snd_soc_component *component) +@@ -2941,8 +2935,8 @@ static const struct dev_pm_ops cs42l43_codec_pm_ops = { + }; + + static const struct platform_device_id cs42l43_codec_id_table[] = { +- { "cs42l43-codec", }, +- {} ++ { .name = "cs42l43-codec" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, cs42l43_codec_id_table); + +diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c +--- a/sound/soc/codecs/cs42l84.c ++++ b/sound/soc/codecs/cs42l84.c +@@ -10,6 +10,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -281,10 +282,9 @@ static int cs42l84_set_jack(struct snd_soc_component *component, struct snd_soc_ + struct cs42l84_private *cs42l84 = snd_soc_component_get_drvdata(component); + + /* Prevent race with interrupt handler */ +- mutex_lock(&cs42l84->irq_lock); ++ guard(mutex)(&cs42l84->irq_lock); + cs42l84->jack = jk; + snd_soc_jack_report(jk, cs42l84->hs_type, SND_JACK_HEADSET); +- mutex_unlock(&cs42l84->irq_lock); + + return 0; + } +@@ -831,7 +831,7 @@ static irqreturn_t cs42l84_irq_thread(int irq, void *data) + u8 current_ring_state; + int i; + +- mutex_lock(&cs42l84->irq_lock); ++ guard(mutex)(&cs42l84->irq_lock); + /* Read sticky registers to clear interrupt */ + for (i = 0; i < ARRAY_SIZE(stickies); i++) { + regmap_read(cs42l84->regmap, irq_params_table[i].status_addr, +@@ -902,8 +902,6 @@ static irqreturn_t cs42l84_irq_thread(int irq, void *data) + break; + } + +- mutex_unlock(&cs42l84->irq_lock); +- + return IRQ_HANDLED; + } + +@@ -919,8 +917,6 @@ static irqreturn_t cs42l84_irq_thread(int irq, void *data) + } + } + +- mutex_unlock(&cs42l84->irq_lock); +- + return IRQ_HANDLED; + } + +diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c +--- a/sound/soc/codecs/cs43130.c ++++ b/sound/soc/codecs/cs43130.c +@@ -6,6 +6,7 @@ + * + * Authors: Li Xu + */ ++#include + #include + #include + #include +@@ -818,26 +819,26 @@ static int cs43130_dsd_hw_params(struct snd_pcm_substream *substream, + unsigned int required_clk; + u8 dsd_speed; + +- mutex_lock(&cs43130->clk_mutex); +- if (!cs43130->clk_req) { +- /* no DAI is currently using clk */ +- if (!(CS43130_MCLK_22M % params_rate(params))) +- required_clk = CS43130_MCLK_22M; +- else +- required_clk = CS43130_MCLK_24M; ++ scoped_guard(mutex, &cs43130->clk_mutex) { ++ if (!cs43130->clk_req) { ++ /* no DAI is currently using clk */ ++ if (!(CS43130_MCLK_22M % params_rate(params))) ++ required_clk = CS43130_MCLK_22M; ++ else ++ required_clk = CS43130_MCLK_24M; + +- cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); +- if (cs43130->pll_bypass) +- cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); +- else +- cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); ++ cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); ++ if (cs43130->pll_bypass) ++ cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); ++ else ++ cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); ++ } ++ ++ cs43130->clk_req++; ++ if (cs43130->clk_req == 2) ++ cs43130_pcm_dsd_mix(true, cs43130->regmap); + } + +- cs43130->clk_req++; +- if (cs43130->clk_req == 2) +- cs43130_pcm_dsd_mix(true, cs43130->regmap); +- mutex_unlock(&cs43130->clk_mutex); +- + switch (params_rate(params)) { + case 176400: + dsd_speed = 0; +@@ -881,26 +882,26 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream, + unsigned int required_clk; + u8 dsd_speed; + +- mutex_lock(&cs43130->clk_mutex); +- if (!cs43130->clk_req) { +- /* no DAI is currently using clk */ +- if (!(CS43130_MCLK_22M % params_rate(params))) +- required_clk = CS43130_MCLK_22M; +- else +- required_clk = CS43130_MCLK_24M; ++ scoped_guard(mutex, &cs43130->clk_mutex) { ++ if (!cs43130->clk_req) { ++ /* no DAI is currently using clk */ ++ if (!(CS43130_MCLK_22M % params_rate(params))) ++ required_clk = CS43130_MCLK_22M; ++ else ++ required_clk = CS43130_MCLK_24M; + +- cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); +- if (cs43130->pll_bypass) +- cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); +- else +- cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); ++ cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); ++ if (cs43130->pll_bypass) ++ cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); ++ else ++ cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); ++ } ++ ++ cs43130->clk_req++; ++ if (cs43130->clk_req == 2) ++ cs43130_pcm_dsd_mix(true, cs43130->regmap); + } + +- cs43130->clk_req++; +- if (cs43130->clk_req == 2) +- cs43130_pcm_dsd_mix(true, cs43130->regmap); +- mutex_unlock(&cs43130->clk_mutex); +- + switch (dai->id) { + case CS43130_ASP_DOP_DAI: + case CS43130_XSP_DOP_DAI: +@@ -988,14 +989,13 @@ static int cs43130_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_component *component = dai->component; + struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); + +- mutex_lock(&cs43130->clk_mutex); ++ guard(mutex)(&cs43130->clk_mutex); + cs43130->clk_req--; + if (!cs43130->clk_req) { + /* no DAI is currently using clk */ + cs43130_change_clksrc(component, CS43130_MCLK_SRC_RCO); + cs43130_pcm_dsd_mix(false, cs43130->regmap); + } +- mutex_unlock(&cs43130->clk_mutex); + + return 0; + } +diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c +--- a/sound/soc/codecs/cs4349.c ++++ b/sound/soc/codecs/cs4349.c +@@ -340,7 +340,13 @@ static int cs4349_runtime_resume(struct device *dev) + gpiod_set_value_cansleep(cs4349->reset_gpio, 1); + + regcache_cache_only(cs4349->regmap, false); +- regcache_sync(cs4349->regmap); ++ ret = regcache_sync(cs4349->regmap); ++ if (ret) { ++ regcache_cache_only(cs4349->regmap, true); ++ regcache_mark_dirty(cs4349->regmap); ++ gpiod_set_value_cansleep(cs4349->reset_gpio, 0); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/cs47l15.c b/sound/soc/codecs/cs47l15.c +--- a/sound/soc/codecs/cs47l15.c ++++ b/sound/soc/codecs/cs47l15.c +@@ -6,6 +6,7 @@ + // Cirrus Logic International Semiconductor Ltd. + // + ++#include + #include + #include + #include +@@ -1285,9 +1286,8 @@ static int cs47l15_component_probe(struct snd_soc_component *component) + + snd_soc_component_init_regmap(component, madera->regmap); + +- mutex_lock(&madera->dapm_ptr_lock); +- madera->dapm = snd_soc_component_to_dapm(component); +- mutex_unlock(&madera->dapm_ptr_lock); ++ scoped_guard(mutex, &madera->dapm_ptr_lock) ++ madera->dapm = snd_soc_component_to_dapm(component); + + ret = madera_init_inputs(component); + if (ret) +@@ -1317,9 +1317,8 @@ static void cs47l15_component_remove(struct snd_soc_component *component) + struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component); + struct madera *madera = cs47l15->core.madera; + +- mutex_lock(&madera->dapm_ptr_lock); +- madera->dapm = NULL; +- mutex_unlock(&madera->dapm_ptr_lock); ++ scoped_guard(mutex, &madera->dapm_ptr_lock) ++ madera->dapm = NULL; + + wm_adsp2_component_remove(&cs47l15->core.adsp[0], component); + } +diff --git a/sound/soc/codecs/cs47l35.c b/sound/soc/codecs/cs47l35.c +--- a/sound/soc/codecs/cs47l35.c ++++ b/sound/soc/codecs/cs47l35.c +@@ -6,6 +6,7 @@ + // Cirrus Logic International Semiconductor Ltd. + // + ++#include + #include + #include + #include +@@ -1566,9 +1567,8 @@ static int cs47l35_component_probe(struct snd_soc_component *component) + + snd_soc_component_init_regmap(component, madera->regmap); + +- mutex_lock(&madera->dapm_ptr_lock); +- madera->dapm = snd_soc_component_to_dapm(component); +- mutex_unlock(&madera->dapm_ptr_lock); ++ scoped_guard(mutex, &madera->dapm_ptr_lock) ++ madera->dapm = snd_soc_component_to_dapm(component); + + ret = madera_init_inputs(component); + if (ret) +@@ -1600,9 +1600,8 @@ static void cs47l35_component_remove(struct snd_soc_component *component) + struct madera *madera = cs47l35->core.madera; + int i; + +- mutex_lock(&madera->dapm_ptr_lock); +- madera->dapm = NULL; +- mutex_unlock(&madera->dapm_ptr_lock); ++ scoped_guard(mutex, &madera->dapm_ptr_lock) ++ madera->dapm = NULL; + + for (i = 0; i < CS47L35_NUM_ADSP; i++) + wm_adsp2_component_remove(&cs47l35->core.adsp[i], component); +diff --git a/sound/soc/codecs/cs47l85.c b/sound/soc/codecs/cs47l85.c +--- a/sound/soc/codecs/cs47l85.c ++++ b/sound/soc/codecs/cs47l85.c +@@ -6,6 +6,7 @@ + // Cirrus Logic International Semiconductor Ltd. + // + ++#include + #include + #include + #include +@@ -2504,9 +2505,8 @@ static int cs47l85_component_probe(struct snd_soc_component *component) + + snd_soc_component_init_regmap(component, madera->regmap); + +- mutex_lock(&madera->dapm_ptr_lock); +- madera->dapm = snd_soc_component_to_dapm(component); +- mutex_unlock(&madera->dapm_ptr_lock); ++ scoped_guard(mutex, &madera->dapm_ptr_lock) ++ madera->dapm = snd_soc_component_to_dapm(component); + + ret = madera_init_inputs(component); + if (ret) +@@ -2537,9 +2537,8 @@ static void cs47l85_component_remove(struct snd_soc_component *component) + struct madera *madera = cs47l85->core.madera; + int i; + +- mutex_lock(&madera->dapm_ptr_lock); +- madera->dapm = NULL; +- mutex_unlock(&madera->dapm_ptr_lock); ++ scoped_guard(mutex, &madera->dapm_ptr_lock) ++ madera->dapm = NULL; + + for (i = 0; i < CS47L85_NUM_ADSP; i++) + wm_adsp2_component_remove(&cs47l85->core.adsp[i], component); +diff --git a/sound/soc/codecs/cs47l90.c b/sound/soc/codecs/cs47l90.c +--- a/sound/soc/codecs/cs47l90.c ++++ b/sound/soc/codecs/cs47l90.c +@@ -6,6 +6,7 @@ + // Cirrus Logic International Semiconductor Ltd. + // + ++#include + #include + #include + #include +@@ -2423,9 +2424,8 @@ static int cs47l90_component_probe(struct snd_soc_component *component) + + snd_soc_component_init_regmap(component, madera->regmap); + +- mutex_lock(&madera->dapm_ptr_lock); +- madera->dapm = snd_soc_component_to_dapm(component); +- mutex_unlock(&madera->dapm_ptr_lock); ++ scoped_guard(mutex, &madera->dapm_ptr_lock) ++ madera->dapm = snd_soc_component_to_dapm(component); + + ret = madera_init_inputs(component); + if (ret) +@@ -2456,9 +2456,8 @@ static void cs47l90_component_remove(struct snd_soc_component *component) + struct madera *madera = cs47l90->core.madera; + int i; + +- mutex_lock(&madera->dapm_ptr_lock); +- madera->dapm = NULL; +- mutex_unlock(&madera->dapm_ptr_lock); ++ scoped_guard(mutex, &madera->dapm_ptr_lock) ++ madera->dapm = NULL; + + for (i = 0; i < CS47L90_NUM_ADSP; i++) + wm_adsp2_component_remove(&cs47l90->core.adsp[i], component); +diff --git a/sound/soc/codecs/cs47l92.c b/sound/soc/codecs/cs47l92.c +--- a/sound/soc/codecs/cs47l92.c ++++ b/sound/soc/codecs/cs47l92.c +@@ -6,6 +6,7 @@ + // Cirrus Logic International Semiconductor Ltd. + // + ++#include + #include + #include + #include +@@ -1892,9 +1893,8 @@ static int cs47l92_component_probe(struct snd_soc_component *component) + + snd_soc_component_init_regmap(component, madera->regmap); + +- mutex_lock(&madera->dapm_ptr_lock); +- madera->dapm = snd_soc_component_to_dapm(component); +- mutex_unlock(&madera->dapm_ptr_lock); ++ scoped_guard(mutex, &madera->dapm_ptr_lock) ++ madera->dapm = snd_soc_component_to_dapm(component); + + ret = madera_init_inputs(component); + if (ret) +@@ -1922,9 +1922,8 @@ static void cs47l92_component_remove(struct snd_soc_component *component) + struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component); + struct madera *madera = cs47l92->core.madera; + +- mutex_lock(&madera->dapm_ptr_lock); +- madera->dapm = NULL; +- mutex_unlock(&madera->dapm_ptr_lock); ++ scoped_guard(mutex, &madera->dapm_ptr_lock) ++ madera->dapm = NULL; + + wm_adsp2_component_remove(&cs47l92->core.adsp[0], component); + } +diff --git a/sound/soc/codecs/cs48l32.c b/sound/soc/codecs/cs48l32.c +--- a/sound/soc/codecs/cs48l32.c ++++ b/sound/soc/codecs/cs48l32.c +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -236,15 +237,13 @@ static int cs48l32_rate_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v + int ret; + + /* Prevent any mixer mux changes while we do this */ +- mutex_lock(&cs48l32_codec->rate_lock); ++ guard(mutex)(&cs48l32_codec->rate_lock); + + /* The write must be guarded by a number of SYSCLK cycles */ + cs48l32_spin_sysclk(cs48l32_codec); + ret = snd_soc_put_enum_double(kcontrol, ucontrol); + cs48l32_spin_sysclk(cs48l32_codec); + +- mutex_unlock(&cs48l32_codec->rate_lock); +- + return ret; + } + +@@ -2242,7 +2241,6 @@ static int cs48l32_dai_set_sysclk(struct snd_soc_dai *dai, + struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[dai->id - 1]; + unsigned int base = dai->driver->base; + unsigned int current_asp_rate, target_asp_rate; +- bool change_rate_domain = false; + int ret; + + if (clk_id == dai_priv->clk) +@@ -2284,19 +2282,15 @@ static int cs48l32_dai_set_sysclk(struct snd_soc_dai *dai, + + if ((current_asp_rate & CS48L32_ASP_RATE_MASK) != + (target_asp_rate & CS48L32_ASP_RATE_MASK)) { +- change_rate_domain = true; +- +- mutex_lock(&cs48l32_codec->rate_lock); + /* Guard the rate change with SYSCLK cycles */ +- cs48l32_spin_sysclk(cs48l32_codec); +- } +- +- snd_soc_component_update_bits(component, base + CS48L32_ASP_CONTROL1, +- CS48L32_ASP_RATE_MASK, target_asp_rate); +- +- if (change_rate_domain) { +- cs48l32_spin_sysclk(cs48l32_codec); +- mutex_unlock(&cs48l32_codec->rate_lock); ++ scoped_guard(mutex, &cs48l32_codec->rate_lock) { ++ cs48l32_spin_sysclk(cs48l32_codec); ++ snd_soc_component_update_bits(component, ++ base + CS48L32_ASP_CONTROL1, ++ CS48L32_ASP_RATE_MASK, ++ target_asp_rate); ++ cs48l32_spin_sysclk(cs48l32_codec); ++ } + } + } + +diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c +--- a/sound/soc/codecs/cx2072x.c ++++ b/sound/soc/codecs/cx2072x.c +@@ -9,6 +9,7 @@ + // + + #include ++#include + #include + #include + #include +@@ -1408,7 +1409,7 @@ static int cx2072x_jack_status_check(void *data) + unsigned int type = 0; + int state = 0; + +- mutex_lock(&cx2072x->lock); ++ guard(mutex)(&cx2072x->lock); + + regmap_read(cx2072x->regmap, CX2072X_PORTA_PIN_SENSE, &jack); + jack = jack >> 24; +@@ -1434,8 +1435,6 @@ static int cx2072x_jack_status_check(void *data) + /* clear interrupt */ + regmap_write(cx2072x->regmap, CX2072X_UM_INTERRUPT_CRTL_E, 0x12 << 24); + +- mutex_unlock(&cx2072x->lock); +- + dev_dbg(codec->dev, "CX2072X_HSDETECT type=0x%X,Jack state = %x\n", + type, state); + return state; +diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c +--- a/sound/soc/codecs/da7213.c ++++ b/sound/soc/codecs/da7213.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -216,13 +217,10 @@ static int da7213_volsw_locked_get(struct snd_kcontrol *kcontrol, + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); +- int ret; + +- mutex_lock(&da7213->ctrl_lock); +- ret = snd_soc_get_volsw(kcontrol, ucontrol); +- mutex_unlock(&da7213->ctrl_lock); ++ guard(mutex)(&da7213->ctrl_lock); + +- return ret; ++ return snd_soc_get_volsw(kcontrol, ucontrol); + } + + static int da7213_volsw_locked_put(struct snd_kcontrol *kcontrol, +@@ -230,13 +228,10 @@ static int da7213_volsw_locked_put(struct snd_kcontrol *kcontrol, + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); +- int ret; + +- mutex_lock(&da7213->ctrl_lock); +- ret = snd_soc_put_volsw(kcontrol, ucontrol); +- mutex_unlock(&da7213->ctrl_lock); ++ guard(mutex)(&da7213->ctrl_lock); + +- return ret; ++ return snd_soc_put_volsw(kcontrol, ucontrol); + } + + static int da7213_enum_locked_get(struct snd_kcontrol *kcontrol, +@@ -244,13 +239,10 @@ static int da7213_enum_locked_get(struct snd_kcontrol *kcontrol, + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); +- int ret; + +- mutex_lock(&da7213->ctrl_lock); +- ret = snd_soc_get_enum_double(kcontrol, ucontrol); +- mutex_unlock(&da7213->ctrl_lock); ++ guard(mutex)(&da7213->ctrl_lock); + +- return ret; ++ return snd_soc_get_enum_double(kcontrol, ucontrol); + } + + static int da7213_enum_locked_put(struct snd_kcontrol *kcontrol, +@@ -258,13 +250,10 @@ static int da7213_enum_locked_put(struct snd_kcontrol *kcontrol, + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); +- int ret; + +- mutex_lock(&da7213->ctrl_lock); +- ret = snd_soc_put_enum_double(kcontrol, ucontrol); +- mutex_unlock(&da7213->ctrl_lock); ++ guard(mutex)(&da7213->ctrl_lock); + +- return ret; ++ return snd_soc_put_enum_double(kcontrol, ucontrol); + } + + /* ALC */ +@@ -465,9 +454,8 @@ static int da7213_tonegen_freq_get(struct snd_kcontrol *kcontrol, + __le16 val; + int ret; + +- mutex_lock(&da7213->ctrl_lock); +- ret = regmap_raw_read(da7213->regmap, reg, &val, sizeof(val)); +- mutex_unlock(&da7213->ctrl_lock); ++ scoped_guard(mutex, &da7213->ctrl_lock) ++ ret = regmap_raw_read(da7213->regmap, reg, &val, sizeof(val)); + + if (ret) + return ret; +@@ -499,12 +487,11 @@ static int da7213_tonegen_freq_put(struct snd_kcontrol *kcontrol, + */ + val_new = cpu_to_le16(ucontrol->value.integer.value[0]); + +- mutex_lock(&da7213->ctrl_lock); ++ guard(mutex)(&da7213->ctrl_lock); + ret = regmap_raw_read(da7213->regmap, reg, &val_old, sizeof(val_old)); + if (ret == 0 && (val_old != val_new)) + ret = regmap_raw_write(da7213->regmap, reg, + &val_new, sizeof(val_new)); +- mutex_unlock(&da7213->ctrl_lock); + + if (ret < 0) + return ret; +diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c +--- a/sound/soc/codecs/da7219.c ++++ b/sound/soc/codecs/da7219.c +@@ -8,6 +8,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -256,13 +257,10 @@ static int da7219_volsw_locked_get(struct snd_kcontrol *kcontrol, + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); +- int ret; + +- mutex_lock(&da7219->ctrl_lock); +- ret = snd_soc_get_volsw(kcontrol, ucontrol); +- mutex_unlock(&da7219->ctrl_lock); ++ guard(mutex)(&da7219->ctrl_lock); + +- return ret; ++ return snd_soc_get_volsw(kcontrol, ucontrol); + } + + static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol, +@@ -270,13 +268,10 @@ static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol, + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); +- int ret; + +- mutex_lock(&da7219->ctrl_lock); +- ret = snd_soc_put_volsw(kcontrol, ucontrol); +- mutex_unlock(&da7219->ctrl_lock); ++ guard(mutex)(&da7219->ctrl_lock); + +- return ret; ++ return snd_soc_put_volsw(kcontrol, ucontrol); + } + + static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol, +@@ -284,13 +279,10 @@ static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol, + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); +- int ret; + +- mutex_lock(&da7219->ctrl_lock); +- ret = snd_soc_get_enum_double(kcontrol, ucontrol); +- mutex_unlock(&da7219->ctrl_lock); ++ guard(mutex)(&da7219->ctrl_lock); + +- return ret; ++ return snd_soc_get_enum_double(kcontrol, ucontrol); + } + + static int da7219_enum_locked_put(struct snd_kcontrol *kcontrol, +@@ -298,13 +290,10 @@ static int da7219_enum_locked_put(struct snd_kcontrol *kcontrol, + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); +- int ret; + +- mutex_lock(&da7219->ctrl_lock); +- ret = snd_soc_put_enum_double(kcontrol, ucontrol); +- mutex_unlock(&da7219->ctrl_lock); ++ guard(mutex)(&da7219->ctrl_lock); + +- return ret; ++ return snd_soc_put_enum_double(kcontrol, ucontrol); + } + + /* ALC */ +@@ -422,9 +411,8 @@ static int da7219_tonegen_freq_get(struct snd_kcontrol *kcontrol, + __le16 val; + int ret; + +- mutex_lock(&da7219->ctrl_lock); +- ret = regmap_raw_read(da7219->regmap, reg, &val, sizeof(val)); +- mutex_unlock(&da7219->ctrl_lock); ++ scoped_guard(mutex, &da7219->ctrl_lock) ++ ret = regmap_raw_read(da7219->regmap, reg, &val, sizeof(val)); + + if (ret) + return ret; +@@ -456,12 +444,11 @@ static int da7219_tonegen_freq_put(struct snd_kcontrol *kcontrol, + */ + val_new = cpu_to_le16(ucontrol->value.integer.value[0]); + +- mutex_lock(&da7219->ctrl_lock); ++ guard(mutex)(&da7219->ctrl_lock); + ret = regmap_raw_read(da7219->regmap, reg, &val_old, sizeof(val_old)); + if (ret == 0 && (val_old != val_new)) + ret = regmap_raw_write(da7219->regmap, reg, +- &val_new, sizeof(val_new)); +- mutex_unlock(&da7219->ctrl_lock); ++ &val_new, sizeof(val_new)); + + if (ret < 0) + return ret; +@@ -1167,15 +1154,12 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + int ret = 0; + +- mutex_lock(&da7219->pll_lock); ++ guard(mutex)(&da7219->pll_lock); + +- if ((da7219->clk_src == clk_id) && (da7219->mclk_rate == freq)) { +- mutex_unlock(&da7219->pll_lock); ++ if (da7219->clk_src == clk_id && da7219->mclk_rate == freq) + return 0; +- } + +- if ((freq < 2000000) || (freq > 54000000)) { +- mutex_unlock(&da7219->pll_lock); ++ if (freq < 2000000 || freq > 54000000) { + dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", + freq); + return -EINVAL; +@@ -1193,7 +1177,6 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, + break; + default: + dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id); +- mutex_unlock(&da7219->pll_lock); + return -EINVAL; + } + +@@ -1203,17 +1186,13 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, + freq = clk_round_rate(da7219->mclk, freq); + ret = clk_set_rate(da7219->mclk, freq); + if (ret) { +- dev_err(codec_dai->dev, "Failed to set clock rate %d\n", +- freq); +- mutex_unlock(&da7219->pll_lock); ++ dev_err(codec_dai->dev, "Failed to set clock rate %d\n", freq); + return ret; + } + } + + da7219->mclk_rate = freq; + +- mutex_unlock(&da7219->pll_lock); +- + return 0; + } + +@@ -1296,13 +1275,10 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + { + struct snd_soc_component *component = codec_dai->component; + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); +- int ret; + +- mutex_lock(&da7219->pll_lock); +- ret = da7219_set_pll(component, source, fout); +- mutex_unlock(&da7219->pll_lock); ++ guard(mutex)(&da7219->pll_lock); + +- return ret; ++ return da7219_set_pll(component, source, fout); + } + + static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c +--- a/sound/soc/codecs/es8316.c ++++ b/sound/soc/codecs/es8316.c +@@ -9,11 +9,13 @@ + + #include + #include ++#include + #include + #include + #include + #include + #include ++#include + #include + #include + #include +@@ -30,6 +32,13 @@ static const unsigned int supported_mclk_lrck_ratios[] = { + 256, 384, 400, 500, 512, 768, 1024 + }; + ++static const char * const es8316_supply_names[] = { ++ "avdd", ++ "cpvdd", ++ "dvdd", ++ "pvdd", ++}; ++ + struct es8316_priv { + struct mutex lock; + struct clk *mclk; +@@ -620,15 +629,15 @@ static irqreturn_t es8316_irq(int irq, void *data) + struct snd_soc_component *comp = es8316->component; + unsigned int flags; + +- mutex_lock(&es8316->lock); ++ guard(mutex)(&es8316->lock); + + regmap_read(es8316->regmap, ES8316_GPIO_FLAG, &flags); + if (flags == 0x00) +- goto out; /* Powered-down / reset */ ++ return IRQ_HANDLED; /* Powered-down / reset */ + + /* Catch spurious IRQ before set_jack is called */ + if (!es8316->jack) +- goto out; ++ return IRQ_HANDLED; + + if (es8316->jd_inverted) + flags ^= ES8316_GPIO_FLAG_HP_NOT_INSERTED; +@@ -681,8 +690,6 @@ static irqreturn_t es8316_irq(int irq, void *data) + } + } + +-out: +- mutex_unlock(&es8316->lock); + return IRQ_HANDLED; + } + +@@ -699,18 +706,16 @@ static void es8316_enable_jack_detect(struct snd_soc_component *component, + es8316->jd_inverted = device_property_read_bool(component->dev, + "everest,jack-detect-inverted"); + +- mutex_lock(&es8316->lock); ++ scoped_guard(mutex, &es8316->lock) { ++ es8316->jack = jack; + +- es8316->jack = jack; ++ if (es8316->jack->status & SND_JACK_MICROPHONE) ++ es8316_enable_micbias_for_mic_gnd_short_detect(component); + +- if (es8316->jack->status & SND_JACK_MICROPHONE) +- es8316_enable_micbias_for_mic_gnd_short_detect(component); +- +- snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, +- ES8316_GPIO_ENABLE_INTERRUPT, +- ES8316_GPIO_ENABLE_INTERRUPT); +- +- mutex_unlock(&es8316->lock); ++ snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, ++ ES8316_GPIO_ENABLE_INTERRUPT, ++ ES8316_GPIO_ENABLE_INTERRUPT); ++ } + + /* Enable irq and sync initial jack state */ + enable_irq(es8316->irq); +@@ -726,7 +731,7 @@ static void es8316_disable_jack_detect(struct snd_soc_component *component) + + disable_irq(es8316->irq); + +- mutex_lock(&es8316->lock); ++ guard(mutex)(&es8316->lock); + + snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, + ES8316_GPIO_ENABLE_INTERRUPT, 0); +@@ -737,8 +742,6 @@ static void es8316_disable_jack_detect(struct snd_soc_component *component) + } + + es8316->jack = NULL; +- +- mutex_unlock(&es8316->lock); + } + + static int es8316_set_jack(struct snd_soc_component *component, +@@ -871,6 +874,11 @@ static int es8316_i2c_probe(struct i2c_client *i2c_client) + + i2c_set_clientdata(i2c_client, es8316); + ++ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(es8316_supply_names), ++ es8316_supply_names); ++ if (ret) ++ return dev_err_probe(dev, ret, "unable to enable supplies\n"); ++ + es8316->regmap = devm_regmap_init_i2c(i2c_client, &es8316_regmap); + if (IS_ERR(es8316->regmap)) + return PTR_ERR(es8316->regmap); +diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c +--- a/sound/soc/codecs/es8326.c ++++ b/sound/soc/codecs/es8326.c +@@ -6,6 +6,7 @@ + // Authors: David Yang + // + ++#include + #include + #include + #include +@@ -790,7 +791,7 @@ static void es8326_jack_button_handler(struct work_struct *work) + if (!(es8326->jack->status & SND_JACK_HEADSET)) /* Jack unplugged */ + return; + +- mutex_lock(&es8326->lock); ++ guard(mutex)(&es8326->lock); + iface = snd_soc_component_read(comp, ES8326_HPDET_STA); + switch (iface) { + case 0x93: +@@ -845,7 +846,6 @@ static void es8326_jack_button_handler(struct work_struct *work) + } + es8326_disable_micbias(es8326->component); + } +- mutex_unlock(&es8326->lock); + } + + static void es8326_jack_detect_handler(struct work_struct *work) +@@ -855,7 +855,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) + struct snd_soc_component *comp = es8326->component; + unsigned int iface; + +- mutex_lock(&es8326->lock); ++ guard(mutex)(&es8326->lock); + iface = snd_soc_component_read(comp, ES8326_HPDET_STA); + dev_dbg(comp->dev, "gpio flag %#04x", iface); + +@@ -873,7 +873,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) + regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, + ES8326_HP_DET_JACK_POL, (es8326->jd_inverted ? + ~es8326->jack_pol : es8326->jack_pol)); +- goto exit; ++ return; + } + + if ((iface & ES8326_HPINSERT_FLAG) == 0) { +@@ -930,7 +930,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) + queue_delayed_work(system_dfl_wq, &es8326->jack_detect_work, + msecs_to_jiffies(400)); + es8326->hp = 1; +- goto exit; ++ return; + } + if (es8326->jack->status & SND_JACK_HEADSET) { + /* detect button */ +@@ -939,7 +939,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) + (ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON)); + es8326_enable_micbias(es8326->component); + queue_delayed_work(system_dfl_wq, &es8326->button_press_work, 10); +- goto exit; ++ return; + } + if ((iface & ES8326_HPBUTTON_FLAG) == 0x01) { + dev_dbg(comp->dev, "Headphone detected\n"); +@@ -958,8 +958,6 @@ static void es8326_jack_detect_handler(struct work_struct *work) + usleep_range(10000, 15000); + } + } +-exit: +- mutex_unlock(&es8326->lock); + } + + static irqreturn_t es8326_irq(int irq, void *dev_id) +@@ -1200,13 +1198,12 @@ static void es8326_enable_jack_detect(struct snd_soc_component *component, + { + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + +- mutex_lock(&es8326->lock); +- if (es8326->jd_inverted) +- snd_soc_component_update_bits(component, ES8326_HPDET_TYPE, +- ES8326_HP_DET_JACK_POL, ~es8326->jack_pol); +- es8326->jack = jack; +- +- mutex_unlock(&es8326->lock); ++ scoped_guard(mutex, &es8326->lock) { ++ if (es8326->jd_inverted) ++ snd_soc_component_update_bits(component, ES8326_HPDET_TYPE, ++ ES8326_HP_DET_JACK_POL, ~es8326->jack_pol); ++ es8326->jack = jack; ++ } + es8326_irq(es8326->irq, es8326); + } + +@@ -1219,13 +1216,12 @@ static void es8326_disable_jack_detect(struct snd_soc_component *component) + return; /* Already disabled (or never enabled) */ + cancel_delayed_work_sync(&es8326->jack_detect_work); + +- mutex_lock(&es8326->lock); ++ guard(mutex)(&es8326->lock); + if (es8326->jack->status & SND_JACK_MICROPHONE) { + es8326_disable_micbias(component); + snd_soc_jack_report(es8326->jack, 0, SND_JACK_HEADSET); + } + es8326->jack = NULL; +- mutex_unlock(&es8326->lock); + } + + static int es8326_set_jack(struct snd_soc_component *component, +diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c +--- a/sound/soc/codecs/es8328.c ++++ b/sound/soc/codecs/es8328.c +@@ -405,6 +405,11 @@ static const struct snd_soc_dapm_route es8328_dapm_routes[] = { + + { "Mic Bias", NULL, "Mic Bias Gen" }, + ++ { "LINPUT1", NULL, "Mic Bias" }, ++ { "RINPUT1", NULL, "Mic Bias" }, ++ { "LINPUT2", NULL, "Mic Bias" }, ++ { "RINPUT2", NULL, "Mic Bias" }, ++ + { "Left Mixer", NULL, "Left DAC" }, + { "Left Mixer", "Left Bypass Switch", "Left Line Mux" }, + { "Left Mixer", "Right Playback Switch", "Right DAC" }, +diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c +--- a/sound/soc/codecs/es8389.c ++++ b/sound/soc/codecs/es8389.c +@@ -36,6 +36,10 @@ struct es8389_private { + unsigned int sysclk; + int mastermode; + ++ u8 hpfl; ++ u8 hpfr; ++ u32 hpf_freq; ++ u32 capture_rate; + u8 mclk_src; + u8 vddd; + int version; +@@ -50,10 +54,29 @@ static const char * const es8389_core_supplies[] = { + static bool es8389_volatile_register(struct device *dev, + unsigned int reg) + { +- if ((reg <= 0xff)) +- return true; +- else ++ switch (reg) { ++ case ES8389_ADCL_VOL: ++ case ES8389_ADCR_VOL: ++ case ES8389_MIC1_GAIN: ++ case ES8389_MIC2_GAIN: ++ case ES8389_DACL_VOL: ++ case ES8389_DACR_VOL: ++ case ES8389_ALC_ON: ++ case ES8389_ALC_CTL: ++ case ES8389_ALC_TARGET: ++ case ES8389_ALC_GAIN: ++ case ES8389_ADC_MUTE: ++ case ES8389_OSR_VOL: ++ case ES8389_DAC_INV: ++ case ES8389_MIX_VOL: ++ case ES8389_DAC_MIX: ++ case ES8389_ADC_RESET: ++ case ES8389_ADC_MODE: ++ case ES8389_DMIC_EN: + return false; ++ default: ++ return true; ++ } + } + + static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9550, 50, 0); +@@ -63,6 +86,92 @@ static const DECLARE_TLV_DB_SCALE(mix_vol_tlv, -9500, 100, 0); + static const DECLARE_TLV_DB_SCALE(alc_target_tlv, -3200, 200, 0); + static const DECLARE_TLV_DB_SCALE(alc_max_level, -3200, 200, 0); + ++static const u32 hpf_table[10][10] = { ++ {1020, 754, 624, 559, 527, 511, 502, 498, 497, 496}, ++ {754, 495, 368, 306, 274, 259, 251, 247, 246, 244}, ++ {624, 368, 243, 182, 151, 136, 128, 124, 123, 121}, ++ {559, 306, 182, 120, 90, 75, 68, 63, 62, 60}, ++ {527, 274, 151, 90, 60, 45, 38, 33, 32, 31}, ++ {511, 259, 136, 75, 45, 30, 23, 19, 18, 17}, ++ {502, 251, 128, 68, 38, 23, 16, 13, 11, 11}, ++ {498, 247, 124, 63, 33, 19, 13, 10, 8, 8}, ++ {497, 246, 123, 62, 32, 18, 11, 8, 8, 0}, ++ {496, 244, 121, 60, 31, 17, 11, 8, 0, 0} ++}; ++ ++static bool find_best_hpf_freq(u32 target_hz, u8 *hpf1, u8 *hpf2, u32 *out) ++{ ++ int best_row = -1, best_col = -1; ++ u32 min_diff = U32_MAX; ++ u32 f, diff; ++ int i, j; ++ ++ if (target_hz > 1020) ++ return false; ++ ++ for (i = 0; i < 10; i++) { ++ for (j = i; j < 10; j++) { ++ f = hpf_table[i][j]; ++ ++ diff = (target_hz > f) ? (target_hz - f) : (f - target_hz); ++ if (diff < min_diff) { ++ min_diff = diff; ++ best_row = i; ++ best_col = j; ++ *out = f; ++ } ++ } ++ } ++ ++ *hpf1 = best_col + ES8389_HPF_OFFSET; ++ *hpf2 = best_row + ES8389_HPF_OFFSET; ++ ++ return true; ++} ++ ++static int es8389_hpf_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); ++ ++ ucontrol->value.integer.value[0] = es8389->hpf_freq; ++ return 0; ++} ++ ++static int es8389_hpf_set(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); ++ u32 freq; ++ bool hpf; ++ ++ if (es8389->hpf_freq == ucontrol->value.integer.value[0]) ++ return 0; ++ ++ if (es8389->capture_rate) { ++ freq = (ucontrol->value.integer.value[0] * 48000) / es8389->capture_rate; ++ ++ hpf = find_best_hpf_freq(freq, &es8389->hpfl, &es8389->hpfr, &es8389->hpf_freq); ++ if (!hpf) ++ return -EBUSY; ++ ++ if (es8389->hpf_freq != ucontrol->value.integer.value[0]) ++ dev_dbg(component->dev, "At the %u Hz sampling rate, %ld Hz could not be obtained." ++ "the frequency has been set to the closest value, %u Hz\n", ++ es8389->capture_rate, ucontrol->value.integer.value[0], es8389->hpf_freq); ++ ++ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, es8389->hpfl); ++ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, es8389->hpfr); ++ } else { ++ es8389->hpf_freq = ucontrol->value.integer.value[0]; ++ dev_dbg(component->dev, "PCM_STREAM_CAPTURE is not active.retain the input frequency\n"); ++ } ++ ++ return 1; ++} ++ + static int es8389_dmic_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) + { +@@ -143,6 +252,16 @@ static const struct soc_enum alc_ramprate = + static const struct soc_enum alc_winsize = + SOC_ENUM_SINGLE(ES8389_ALC_CTL, 0, 16, winsize); + ++static const char *const es8389_adcl_mux_txt[] = { ++ "Normal", ++ "ADC2 channel to ADC1 channel", ++}; ++ ++static const char *const es8389_adcr_mux_txt[] = { ++ "Normal", ++ "ADC1 channel to ADC2 channel", ++}; ++ + static const char *const es8389_outl_mux_txt[] = { + "Normal", + "DAC2 channel to DAC1 channel", +@@ -170,6 +289,20 @@ static const unsigned int es8389_pga_values[] = { + 1, 5, 6 + }; + ++static const struct soc_enum es8389_adcl_mux_enum = ++ SOC_ENUM_SINGLE(ES8389_ADC_MODE, 5, ++ ARRAY_SIZE(es8389_adcl_mux_txt), es8389_adcl_mux_txt); ++ ++static const struct snd_kcontrol_new es8389_adcl_mux_controls = ++ SOC_DAPM_ENUM("INPUTL MUX", es8389_adcl_mux_enum); ++ ++static const struct soc_enum es8389_adcr_mux_enum = ++ SOC_ENUM_SINGLE(ES8389_ADC_MODE, 4, ++ ARRAY_SIZE(es8389_adcr_mux_txt), es8389_adcr_mux_txt); ++ ++static const struct snd_kcontrol_new es8389_adcr_mux_controls = ++ SOC_DAPM_ENUM("INPUTR MUX", es8389_adcr_mux_enum); ++ + static const struct soc_enum es8389_outl_mux_enum = + SOC_ENUM_SINGLE(ES8389_DAC_MIX, 5, + ARRAY_SIZE(es8389_outl_mux_txt), es8389_outl_mux_txt); +@@ -240,6 +373,8 @@ static const struct snd_kcontrol_new es8389_snd_controls[] = { + SOC_DOUBLE("ADC OSR Volume ON Switch", ES8389_ADC_MUTE, 6, 7, 1, 0), + SOC_SINGLE_TLV("ADC OSR Volume", ES8389_OSR_VOL, 0, 0xFF, 0, adc_vol_tlv), + SOC_DOUBLE("ADC OUTPUT Invert Switch", ES8389_ADC_HPF2, 5, 6, 1, 0), ++ SOC_SINGLE_EXT("ADC HPF Freq Select", SND_SOC_NOPM, 0, 1020, 0, ++ es8389_hpf_get, es8389_hpf_set), + + SOC_SINGLE_TLV("DACL Playback Volume", ES8389_DACL_VOL, 0, 0xFF, 0, dac_vol_tlv), + SOC_SINGLE_TLV("DACR Playback Volume", ES8389_DACR_VOL, 0, 0xFF, 0, dac_vol_tlv), +@@ -298,6 +433,8 @@ static const struct snd_soc_dapm_widget es8389_dapm_widgets[] = { + &es8389_adc_mixer_controls[0], + ARRAY_SIZE(es8389_adc_mixer_controls)), + SND_SOC_DAPM_MUX("ADC MUX", SND_SOC_NOPM, 0, 0, &es8389_dmic_mux_controls), ++ SND_SOC_DAPM_MUX("INPUTL MUX", SND_SOC_NOPM, 0, 0, &es8389_adcl_mux_controls), ++ SND_SOC_DAPM_MUX("INPUTR MUX", SND_SOC_NOPM, 0, 0, &es8389_adcr_mux_controls), + + SND_SOC_DAPM_MUX("OUTL MUX", SND_SOC_NOPM, 0, 0, &es8389_outl_mux_controls), + SND_SOC_DAPM_MUX("OUTR MUX", SND_SOC_NOPM, 0, 0, &es8389_outr_mux_controls), +@@ -311,10 +448,15 @@ static const struct snd_soc_dapm_route es8389_dapm_routes[] = { + {"ADCL", NULL, "PGAL"}, + {"ADCR", NULL, "PGAR"}, + ++ {"INPUTL MUX", "Normal", "ADCL"}, ++ {"INPUTL MUX", "ADC2 channel to ADC1 channel", "ADCR"}, ++ {"INPUTR MUX", "Normal", "ADCR"}, ++ {"INPUTR MUX", "ADC1 channel to ADC2 channel", "ADCL"}, ++ + {"ADC Mixer", "DACL ADCL Mixer", "DACL"}, + {"ADC Mixer", "DACR ADCR Mixer", "DACR"}, +- {"ADC Mixer", NULL, "ADCL"}, +- {"ADC Mixer", NULL, "ADCR"}, ++ {"ADC Mixer", NULL, "INPUTL MUX"}, ++ {"ADC Mixer", NULL, "INPUTR MUX"}, + + {"ADC MUX", "AMIC", "ADC Mixer"}, + {"ADC MUX", "DMIC", "DMIC"}, +@@ -415,52 +557,54 @@ static const struct _coeff_div coeff_div[] = { + {36, 576000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {48, 768000, 16000, 0x02, 0x57, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {50, 800000, 16000, 0x00, 0x7E, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, +- {64, 1024000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, +- {72, 1152000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, ++ {64, 1024000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, ++ {72, 1152000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {96, 1536000, 16000, 0x02, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {128, 2048000, 16000, 0x00, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {144, 2304000, 16000, 0x00, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, ++ {150, 2400000, 16000, 0x02, 0x7E, 0x01, 0xC9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {192, 3072000, 16000, 0x02, 0x65, 0x25, 0xE0, 0x00, 0xE1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, +- {256, 4096000, 16000, 0x00, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, ++ {256, 4096000, 16000, 0x00, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {300, 4800000, 16000, 0x02, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {384, 6144000, 16000, 0x02, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, +- {512, 8192000, 16000, 0x01, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, ++ {512, 8192000, 16000, 0x01, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {750, 12000000, 16000, 0x0E, 0x7E, 0x01, 0xC9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, +- {768, 12288000, 16000, 0x02, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, +- {1024, 16384000, 16000, 0x03, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, ++ {768, 12288000, 16000, 0x02, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, ++ {1024, 16384000, 16000, 0x03, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {1152, 18432000, 16000, 0x08, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {1200, 19200000, 16000, 0x0B, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {1500, 24000000, 16000, 0x0E, 0x26, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, +- {1536, 24576000, 16000, 0x05, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, ++ {1536, 24576000, 16000, 0x05, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {1625, 26000000, 16000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {800, 19200000, 24000, 0x07, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x1A, 0x49, 0x14, 2, 2}, + {375, 12000000, 32000, 0x0E, 0x2E, 0x05, 0xC8, 0x00, 0xC2, 0x80, 0x40, 0x01, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B, 2, 0}, +- {600, 19200000, 32000, 0x05, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B, 2, 2}, +- {32, 1411200, 44100, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {600, 19200000, 32000, 0x05, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B, 2, 2}, ++ {32, 1411200, 44100, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {64, 2822400, 44100, 0x00, 0x51, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, +- {128, 5644800, 44100, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, +- {256, 11289600, 44100, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, +- {512, 22579200, 44100, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, +- {32, 1536000, 48000, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {128, 5644800, 44100, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {256, 11289600, 44100, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {512, 22579200, 44100, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {32, 1536000, 48000, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {48, 2304000, 48000, 0x02, 0x55, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {50, 2400000, 48000, 0x00, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {64, 3072000, 48000, 0x00, 0x51, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, +- {100, 4800000, 48000, 0x00, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {100, 4800000, 48000, 0x00, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {125, 6000000, 48000, 0x04, 0x6E, 0x05, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x01, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, +- {128, 6144000, 48000, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, +- {200, 9600000, 48000, 0x01, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {128, 6144000, 48000, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {200, 9600000, 48000, 0x01, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {250, 12000000, 48000, 0x04, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, +- {256, 12288000, 48000, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, +- {384, 18432000, 48000, 0x02, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, +- {400, 19200000, 48000, 0x03, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, +- {500, 24000000, 48000, 0x04, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, +- {512, 24576000, 48000, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {256, 12288000, 48000, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {384, 18432000, 48000, 0x02, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {400, 19200000, 48000, 0x03, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {500, 24000000, 48000, 0x04, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {512, 24576000, 48000, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {800, 38400000, 48000, 0x18, 0x45, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {128, 11289600, 88200, 0x00, 0x50, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x32, 0x89, 0x25, 2, 2}, +- {64, 6144000, 96000, 0x00, 0x41, 0x00, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, ++ {64, 6144000, 96000, 0x00, 0x41, 0x00, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, + {96, 9216000, 96000, 0x02, 0x43, 0x00, 0xC0, 0x10, 0xC0, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, + {256, 24576000, 96000, 0x00, 0x40, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, + {128, 24576000, 192000, 0x00, 0x50, 0x00, 0xC0, 0x18, 0xC1, 0x81, 0xC0, 0x00, 0x8F, 0x7F, 0xBF, 0xC0, 0x3F, 0x7F, 0x80, 0x12, 0xC0, 0x3F, 0xF9, 0x3F, 2, 2}, ++ {64, 12288000, 192000, 0x00, 0x41, 0x00, 0xC0, 0x18, 0xC1, 0x80, 0x00, 0x00, 0x8F, 0x7F, 0xEF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x3F, 0xF9, 0x3F, 1, 0}, + }; + + static inline int get_coeff(u8 vddd, u8 dmic, int mclk, int rate) +@@ -564,6 +708,8 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream, + int coeff, ret; + u8 dmic_enable, state = 0; + unsigned int regv; ++ u32 freq; ++ bool hpf; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: +@@ -590,7 +736,7 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream, + + if (es8389->mclk_src == ES8389_SCLK_PIN) { + regmap_update_bits(es8389->regmap, ES8389_MASTER_CLK, +- ES8389_MCLK_SOURCE, es8389->mclk_src); ++ ES8389_MCLK_MASK, ES8389_MCLK_FROM_SCLK); + es8389->sysclk = params_channels(params) * params_width(params) * params_rate(params); + } + +@@ -641,6 +787,28 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream, + dev_warn(component->dev, "Clock coefficients do not match"); + } + ++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { ++ es8389->capture_rate = params_rate(params); ++ freq = (es8389->hpf_freq * 48000) / params_rate(params); ++ hpf = find_best_hpf_freq(freq, &es8389->hpfl, &es8389->hpfr, &es8389->hpf_freq); ++ if (!hpf) { ++ dev_err(component->dev, "The HPF frequency is invalid\n"); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++static int es8389_pcm_hw_free(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); ++ ++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ++ es8389->capture_rate = 0; ++ + return 0; + } + +@@ -721,8 +889,8 @@ static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction) + regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE, + 0x03, 0x00); + } else { +- regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x0a); +- regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x0a); ++ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, es8389->hpfl); ++ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, es8389->hpfr); + regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE, + 0x03, 0x00); + } +@@ -738,6 +906,7 @@ static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction) + + static const struct snd_soc_dai_ops es8389_ops = { + .hw_params = es8389_pcm_hw_params, ++ .hw_free = es8389_pcm_hw_free, + .set_fmt = es8389_set_dai_fmt, + .set_sysclk = es8389_set_dai_sysclk, + .set_tdm_slot = es8389_set_tdm_slot, +@@ -771,7 +940,7 @@ static void es8389_init(struct snd_soc_component *component) + + regmap_read(es8389->regmap, ES8389_MAX_REGISTER, ®); + es8389->version = reg; +- regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x00); ++ regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x56); + regmap_write(es8389->regmap, ES8389_RESET, 0x7E); + regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x38); + regmap_write(es8389->regmap, ES8389_ADC_HPF1, 0x64); +@@ -823,7 +992,7 @@ static void es8389_init(struct snd_soc_component *component) + regmap_write(es8389->regmap, ES8389_SCLK_DIV, 0x04); + regmap_write(es8389->regmap, ES8389_LRCK_DIV1, 0x01); + regmap_write(es8389->regmap, ES8389_LRCK_DIV2, 0x00); +- regmap_write(es8389->regmap, ES8389_OSC_CLK, 0x00); ++ regmap_write(es8389->regmap, ES8389_OSC_CLK, 0x10); + regmap_write(es8389->regmap, ES8389_ADC_OSR, 0x1F); + regmap_write(es8389->regmap, ES8389_ADC_DSP, 0x7F); + regmap_write(es8389->regmap, ES8389_ADC_MUTE, 0xC0); +@@ -861,13 +1030,13 @@ static int es8389_resume(struct snd_soc_component *component) + regcache_cache_only(es8389->regmap, false); + regcache_cache_bypass(es8389->regmap, true); + regmap_read(es8389->regmap, ES8389_RESET, ®v); +- regcache_cache_bypass(es8389->regmap, false); + + if (regv == 0xff) + es8389_init(component); + else + es8389_set_bias_level(component, SND_SOC_BIAS_ON); + ++ regcache_cache_bypass(es8389->regmap, false); + regcache_sync(es8389->regmap); + + return 0; +@@ -913,6 +1082,7 @@ static int es8389_probe(struct snd_soc_component *component) + return ret; + } + ++ es8389->hpf_freq = ES8389_HPF_DEFAULT; + es8389_init(component); + es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY); + +diff --git a/sound/soc/codecs/es8389.h b/sound/soc/codecs/es8389.h +--- a/sound/soc/codecs/es8389.h ++++ b/sound/soc/codecs/es8389.h +@@ -106,6 +106,9 @@ + #define ES8389_MIC_SEL_MASK (7 << 4) + #define ES8389_MIC_DEFAULT (1 << 4) + ++#define ES8389_HPF_DEFAULT 16 ++#define ES8389_HPF_OFFSET 4 ++ + #define ES8389_MASTER_MODE_EN (1 << 0) + + #define ES8389_TDM_OFF (0 << 0) +@@ -116,9 +119,11 @@ + #define ES8389_TDM_SLOT (0x70 << 0) + #define ES8389_TDM_SHIFT 4 + +-#define ES8389_MCLK_SOURCE (1 << 6) +-#define ES8389_MCLK_PIN (1 << 6) +-#define ES8389_SCLK_PIN (0 << 6) ++#define ES8389_MCLK_MASK (3 << 6) ++#define ES8389_MCLK_FROM_SCLK (1 << 6) ++#define ES8389_MCLK_SOURCE ES8389_MCLK_PIN ++#define ES8389_MCLK_PIN 0 ++#define ES8389_SCLK_PIN 1 + + /* ES8389_FMT */ + #define ES8389_S24_LE (0 << 5) +diff --git a/sound/soc/codecs/es9356.c b/sound/soc/codecs/es9356.c +--- a/sound/soc/codecs/es9356.c ++++ b/sound/soc/codecs/es9356.c +@@ -6,6 +6,7 @@ + // + // + ++#include + #include + #include + #include +@@ -19,8 +20,6 @@ + #include + #include + #include +-#include +-#include + #include + #include + #include +@@ -500,16 +499,19 @@ static int es9356_power_state(struct snd_soc_dai *dai, unsigned char ps, unsigne + } + + /* power state changes are not independent across functions */ +- mutex_lock(&es9356->pde_lock); +- ret = es9356_pde_transition_delay(es9356, func, pde_entity, ps?ps0:ps3); +- if (ret) { +- regmap_write(es9356->regmap, +- SDW_SDCA_CTL(func, pde_entity, ES9356_SDCA_CTL_REQ_POWER_STATE, 0), ps?ps3:ps0); +- es9356_pde_transition_delay(es9356, func, pde_entity, ps?ps3:ps0); +- } else +- dev_dbg(component->dev, "%s PDE is already %d\n", __func__, ps?ps0:ps3); +- +- mutex_unlock(&es9356->pde_lock); ++ scoped_guard(mutex, &es9356->pde_lock) { ++ ret = es9356_pde_transition_delay(es9356, func, pde_entity, ps ? ps0 : ps3); ++ if (ret) { ++ regmap_write(es9356->regmap, ++ SDW_SDCA_CTL(func, pde_entity, ++ ES9356_SDCA_CTL_REQ_POWER_STATE, 0), ++ ps ? ps3 : ps0); ++ es9356_pde_transition_delay(es9356, func, pde_entity, ps ? ps3 : ps0); ++ } else { ++ dev_dbg(component->dev, "%s PDE is already %d\n", __func__, ++ ps ? ps0 : ps3); ++ } ++ } + + if (rate) + regmap_write(es9356->regmap, +@@ -671,7 +673,7 @@ static int es9356_sdca_mbq_size(struct device *dev, unsigned int reg) + } + } + +-static struct regmap_sdw_mbq_cfg es9356_mbq_config = { ++static const struct regmap_sdw_mbq_cfg es9356_mbq_config = { + .mbq_size = es9356_sdca_mbq_size, + }; + +@@ -1091,9 +1093,8 @@ static int es9356_sdca_dev_system_suspend(struct device *dev) + { + struct es9356_sdw_priv *es9356 = dev_get_drvdata(dev); + +- mutex_lock(&es9356->disable_irq_lock); +- es9356->disable_irq = true; +- mutex_unlock(&es9356->disable_irq_lock); ++ scoped_guard(mutex, &es9356->disable_irq_lock) ++ es9356->disable_irq = true; + + return es9356_sdca_dev_suspend(dev); + } +diff --git a/sound/soc/codecs/fs-amp-lib.c b/sound/soc/codecs/fs-amp-lib.c +--- a/sound/soc/codecs/fs-amp-lib.c ++++ b/sound/soc/codecs/fs-amp-lib.c +@@ -221,7 +221,7 @@ static void fs_print_firmware_info(struct fs_amp_lib *amp_lib) + + int fs_amp_load_firmware(struct fs_amp_lib *amp_lib, const char *name) + { +- const struct firmware *cont; ++ const struct firmware *cont __free(firmware) = NULL; + struct fs_fwm_header *hdr; + int ret; + +@@ -237,7 +237,6 @@ int fs_amp_load_firmware(struct fs_amp_lib *amp_lib, const char *name) + dev_info(amp_lib->dev, "Loading %s - size: %zu\n", name, cont->size); + + hdr = devm_kmemdup(amp_lib->dev, cont->data, cont->size, GFP_KERNEL); +- release_firmware(cont); + if (!hdr) + return -ENOMEM; + +diff --git a/sound/soc/codecs/fs210x.c b/sound/soc/codecs/fs210x.c +--- a/sound/soc/codecs/fs210x.c ++++ b/sound/soc/codecs/fs210x.c +@@ -4,6 +4,7 @@ + // + // Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd. + ++#include + #include + #include + #include +@@ -771,9 +772,8 @@ static int fs210x_dai_hw_params(struct snd_pcm_substream *substream, + if (fs210x->devid == FS2105S_DEVICE_ID && fs210x->srate == 16000) + return -EOPNOTSUPP; + +- mutex_lock(&fs210x->lock); +- ret = fs210x_set_hw_params(fs210x); +- mutex_unlock(&fs210x->lock); ++ scoped_guard(mutex, &fs210x->lock) ++ ret = fs210x_set_hw_params(fs210x); + if (ret) + dev_err(fs210x->dev, "Failed to set hw params: %d\n", ret); + +@@ -790,15 +790,11 @@ static int fs210x_dai_mute(struct snd_soc_dai *dai, int mute, int stream) + + fs210x = snd_soc_component_get_drvdata(dai->component); + +- mutex_lock(&fs210x->lock); +- +- if (!fs210x->is_inited || fs210x->is_suspended) { +- mutex_unlock(&fs210x->lock); +- return 0; ++ scoped_guard(mutex, &fs210x->lock) { ++ if (!fs210x->is_inited || fs210x->is_suspended) ++ return 0; + } + +- mutex_unlock(&fs210x->lock); +- + if (mute) { + cancel_delayed_work_sync(&fs210x->fault_check_work); + cancel_delayed_work_sync(&fs210x->start_work); +@@ -817,15 +813,11 @@ static int fs210x_dai_trigger(struct snd_pcm_substream *substream, + + fs210x = snd_soc_component_get_drvdata(dai->component); + +- mutex_lock(&fs210x->lock); +- +- if (!fs210x->is_inited || fs210x->is_suspended || fs210x->is_playing) { +- mutex_unlock(&fs210x->lock); +- return 0; ++ scoped_guard(mutex, &fs210x->lock) { ++ if (!fs210x->is_inited || fs210x->is_suspended || fs210x->is_playing) ++ return 0; + } + +- mutex_unlock(&fs210x->lock); +- + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: +@@ -853,13 +845,11 @@ static void fs210x_start_work(struct work_struct *work) + + fs210x = container_of(work, struct fs210x_priv, start_work.work); + +- mutex_lock(&fs210x->lock); ++ guard(mutex)(&fs210x->lock); + + ret = fs210x_dev_play(fs210x); + if (ret) + dev_err(fs210x->dev, "Failed to start playing: %d\n", ret); +- +- mutex_unlock(&fs210x->lock); + } + + static void fs210x_fault_check_work(struct work_struct *work) +@@ -870,15 +860,13 @@ static void fs210x_fault_check_work(struct work_struct *work) + + fs210x = container_of(work, struct fs210x_priv, fault_check_work.work); + +- mutex_lock(&fs210x->lock); ++ scoped_guard(mutex, &fs210x->lock) { ++ if (!fs210x->is_inited || fs210x->is_suspended || !fs210x->is_playing) ++ return; + +- if (!fs210x->is_inited || fs210x->is_suspended || !fs210x->is_playing) { +- mutex_unlock(&fs210x->lock); +- return; ++ ret = fs210x_reg_read(fs210x, FS210X_05H_ANASTAT, &status); + } + +- ret = fs210x_reg_read(fs210x, FS210X_05H_ANASTAT, &status); +- mutex_unlock(&fs210x->lock); + if (ret) + return; + +@@ -991,7 +979,7 @@ static int fs210x_effect_scene_get(struct snd_kcontrol *kcontrol, + if (fs210x->scene_id < 1) + return -EINVAL; + +- mutex_lock(&fs210x->lock); ++ guard(mutex)(&fs210x->lock); + /* + * FS210x has scene(s) as below: + * init scene: id = 0 +@@ -1000,7 +988,6 @@ static int fs210x_effect_scene_get(struct snd_kcontrol *kcontrol, + */ + index = fs210x->scene_id - 1; + ucontrol->value.integer.value[0] = index; +- mutex_unlock(&fs210x->lock); + + return 0; + } +@@ -1019,7 +1006,7 @@ static int fs210x_effect_scene_put(struct snd_kcontrol *kcontrol, + return -EINVAL; + } + +- mutex_lock(&fs210x->lock); ++ guard(mutex)(&fs210x->lock); + + /* + * FS210x has scene(s) as below: +@@ -1029,17 +1016,14 @@ static int fs210x_effect_scene_put(struct snd_kcontrol *kcontrol, + */ + scene_id = ucontrol->value.integer.value[0] + 1; + scene_count = fs210x->amp_lib.scene_count - 1; /* Skip init scene */ +- if (scene_id < 1 || scene_id > scene_count) { +- mutex_unlock(&fs210x->lock); ++ if (scene_id < 1 || scene_id > scene_count) + return -ERANGE; +- } + + if (scene_id != fs210x->scene_id) + is_changed = true; + + if (fs210x->is_suspended) { + fs210x->scene_id = scene_id; +- mutex_unlock(&fs210x->lock); + return is_changed; + } + +@@ -1047,8 +1031,6 @@ static int fs210x_effect_scene_put(struct snd_kcontrol *kcontrol, + if (ret) + dev_err(fs210x->dev, "Failed to set scene: %d\n", ret); + +- mutex_unlock(&fs210x->lock); +- + if (!ret && is_changed) + return 1; + +@@ -1062,12 +1044,10 @@ static int fs210x_playback_event(struct snd_soc_dapm_widget *w, + struct fs210x_priv *fs210x = snd_soc_component_get_drvdata(cmpnt); + int ret = 0; + +- mutex_lock(&fs210x->lock); ++ guard(mutex)(&fs210x->lock); + +- if (fs210x->is_suspended) { +- mutex_unlock(&fs210x->lock); ++ if (fs210x->is_suspended) + return 0; +- } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: +@@ -1088,8 +1068,6 @@ static int fs210x_playback_event(struct snd_soc_dapm_widget *w, + break; + } + +- mutex_unlock(&fs210x->lock); +- + return ret; + } + +@@ -1220,11 +1198,9 @@ static int fs210x_probe(struct snd_soc_component *cmpnt) + if (ret) + return ret; + +- mutex_lock(&fs210x->lock); +- ret = fs210x_init_chip(fs210x); +- mutex_unlock(&fs210x->lock); ++ guard(mutex)(&fs210x->lock); + +- return ret; ++ return fs210x_init_chip(fs210x); + } + + static void fs210x_remove(struct snd_soc_component *cmpnt) +@@ -1251,15 +1227,15 @@ static int fs210x_suspend(struct snd_soc_component *cmpnt) + + regcache_cache_only(fs210x->regmap, true); + +- mutex_lock(&fs210x->lock); +- fs210x->cur_scene = NULL; +- fs210x->is_inited = false; +- fs210x->is_playing = false; +- fs210x->is_suspended = true; ++ scoped_guard(mutex, &fs210x->lock) { ++ fs210x->cur_scene = NULL; ++ fs210x->is_inited = false; ++ fs210x->is_playing = false; ++ fs210x->is_suspended = true; + +- gpiod_set_value_cansleep(fs210x->gpio_sdz, 1); /* Active */ +- fsleep(30000); /* >= 30ms */ +- mutex_unlock(&fs210x->lock); ++ gpiod_set_value_cansleep(fs210x->gpio_sdz, 1); /* Active */ ++ fsleep(30000); /* >= 30ms */ ++ } + + cancel_delayed_work_sync(&fs210x->start_work); + cancel_delayed_work_sync(&fs210x->fault_check_work); +@@ -1288,14 +1264,11 @@ static int fs210x_resume(struct snd_soc_component *cmpnt) + return ret; + } + +- mutex_lock(&fs210x->lock); ++ guard(mutex)(&fs210x->lock); + + fs210x->is_suspended = false; +- ret = fs210x_init_chip(fs210x); + +- mutex_unlock(&fs210x->lock); +- +- return ret; ++ return fs210x_init_chip(fs210x); + } + #else + #define fs210x_suspend NULL +diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c +--- a/sound/soc/codecs/hdac_hda.c ++++ b/sound/soc/codecs/hdac_hda.c +@@ -437,7 +437,7 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) + + #ifdef CONFIG_SND_HDA_PATCH_LOADER + if (loadable_patch[hda_pvt->dev_index] && *loadable_patch[hda_pvt->dev_index]) { +- const struct firmware *fw; ++ const struct firmware *fw __free(firmware) = NULL; + + dev_info(&hdev->dev, "Applying patch firmware '%s'\n", + loadable_patch[hda_pvt->dev_index]); +@@ -451,7 +451,6 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) + dev_err(&hdev->dev, "%s: failed to load hda patch %d\n", __func__, ret); + goto error_no_pm; + } +- release_firmware(fw); + } + } + #endif +diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c +--- a/sound/soc/codecs/hdac_hdmi.c ++++ b/sound/soc/codecs/hdac_hdmi.c +@@ -10,6 +10,7 @@ + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + ++#include + #include + #include + #include +@@ -537,10 +538,11 @@ static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt( + continue; + + list_for_each_entry(port, &pcm->port_list, head) { +- mutex_lock(&pcm->lock); +- ret = hdac_hdmi_query_port_connlist(hdev, +- port->pin, port); +- mutex_unlock(&pcm->lock); ++ scoped_guard(mutex, &pcm->lock) { ++ ret = hdac_hdmi_query_port_connlist(hdev, ++ port->pin, ++ port); ++ } + if (ret < 0) + continue; + +@@ -640,11 +642,11 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, + pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt); + + if (pcm) { +- mutex_lock(&pcm->lock); +- pcm->chmap_set = false; +- memset(pcm->chmap, 0, sizeof(pcm->chmap)); +- pcm->channels = 0; +- mutex_unlock(&pcm->lock); ++ scoped_guard(mutex, &pcm->lock) { ++ pcm->chmap_set = false; ++ memset(pcm->chmap, 0, sizeof(pcm->chmap)); ++ pcm->channels = 0; ++ } + } + + if (dai_map->port) +@@ -922,7 +924,7 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, + if (port == NULL) + return -EINVAL; + +- mutex_lock(&hdmi->pin_mutex); ++ guard(mutex)(&hdmi->pin_mutex); + list_for_each_entry(pcm, &hdmi->pcm_list, head) { + if (list_empty(&pcm->port_list)) + continue; +@@ -945,12 +947,10 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, + list_add_tail(&port->head, &pcm->port_list); + if (port->eld.monitor_present && port->eld.eld_valid) { + hdac_hdmi_jack_report_sync(pcm, port, true); +- mutex_unlock(&hdmi->pin_mutex); + return ret; + } + } + } +- mutex_unlock(&hdmi->pin_mutex); + + return ret; + } +@@ -1274,67 +1274,65 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, + * In case of non MST pin, get_eld info API expectes port + * to be -1. + */ +- mutex_lock(&hdmi->pin_mutex); +- port->eld.monitor_present = false; ++ scoped_guard(mutex, &hdmi->pin_mutex) { ++ port->eld.monitor_present = false; + +- if (pin->mst_capable) +- port_id = port->id; ++ if (pin->mst_capable) ++ port_id = port->id; + +- size = snd_hdac_acomp_get_eld(hdev, pin->nid, port_id, +- &port->eld.monitor_present, +- port->eld.eld_buffer, +- ELD_MAX_SIZE); ++ size = snd_hdac_acomp_get_eld(hdev, pin->nid, port_id, ++ &port->eld.monitor_present, ++ port->eld.eld_buffer, ++ ELD_MAX_SIZE); + +- if (size > 0) { +- size = min(size, ELD_MAX_SIZE); +- if (hdac_hdmi_parse_eld(hdev, port) < 0) +- size = -EINVAL; +- } +- +- eld_valid = port->eld.eld_valid; +- +- if (size > 0) { +- port->eld.eld_valid = true; +- port->eld.eld_size = size; +- } else { +- port->eld.eld_valid = false; +- port->eld.eld_size = 0; +- } +- +- eld_changed = (eld_valid != port->eld.eld_valid); +- +- pcm = hdac_hdmi_get_pcm(hdev, port); +- +- if (!port->eld.monitor_present || !port->eld.eld_valid) { +- +- dev_dbg(&hdev->dev, "%s: disconnect for pin:port %d:%d\n", +- __func__, pin->nid, port->id); +- +- /* +- * PCMs are not registered during device probe, so don't +- * report jack here. It will be done in usermode mux +- * control select. +- */ +- if (pcm) { +- hdac_hdmi_jack_report(pcm, port, false); +- schedule_work(&port->dapm_work); ++ if (size > 0) { ++ size = min(size, ELD_MAX_SIZE); ++ if (hdac_hdmi_parse_eld(hdev, port) < 0) ++ size = -EINVAL; + } + +- mutex_unlock(&hdmi->pin_mutex); +- return; +- } ++ eld_valid = port->eld.eld_valid; + +- if (port->eld.monitor_present && port->eld.eld_valid) { +- if (pcm) { +- hdac_hdmi_jack_report(pcm, port, true); +- schedule_work(&port->dapm_work); ++ if (size > 0) { ++ port->eld.eld_valid = true; ++ port->eld.eld_size = size; ++ } else { ++ port->eld.eld_valid = false; ++ port->eld.eld_size = 0; + } + +- print_hex_dump_debug("ELD: ", DUMP_PREFIX_OFFSET, 16, 1, +- port->eld.eld_buffer, port->eld.eld_size, false); ++ eld_changed = (eld_valid != port->eld.eld_valid); + ++ pcm = hdac_hdmi_get_pcm(hdev, port); ++ ++ if (!port->eld.monitor_present || !port->eld.eld_valid) { ++ ++ dev_dbg(&hdev->dev, "%s: disconnect for pin:port %d:%d\n", ++ __func__, pin->nid, port->id); ++ ++ /* ++ * PCMs are not registered during device probe, so don't ++ * report jack here. It will be done in usermode mux ++ * control select. ++ */ ++ if (pcm) { ++ hdac_hdmi_jack_report(pcm, port, false); ++ schedule_work(&port->dapm_work); ++ } ++ ++ return; ++ } ++ ++ if (port->eld.monitor_present && port->eld.eld_valid) { ++ if (pcm) { ++ hdac_hdmi_jack_report(pcm, port, true); ++ schedule_work(&port->dapm_work); ++ } ++ ++ print_hex_dump_debug("ELD: ", DUMP_PREFIX_OFFSET, 16, 1, ++ port->eld.eld_buffer, port->eld.eld_size, false); ++ } + } +- mutex_unlock(&hdmi->pin_mutex); + + if (eld_changed && pcm) + snd_ctl_notify(hdmi->card, +@@ -1795,13 +1793,12 @@ static void hdac_hdmi_set_chmap(struct hdac_device *hdev, int pcm_idx, + if (list_empty(&pcm->port_list)) + return; + +- mutex_lock(&pcm->lock); ++ guard(mutex)(&pcm->lock); + pcm->chmap_set = true; + memcpy(pcm->chmap, chmap, ARRAY_SIZE(pcm->chmap)); + list_for_each_entry(port, &pcm->port_list, head) + if (prepared) + hdac_hdmi_setup_audio_infoframe(hdev, pcm, port); +- mutex_unlock(&pcm->lock); + } + + static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdev, int pcm_idx) +@@ -1866,8 +1863,10 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) + snd_hdac_ext_bus_link_get(hdev->bus, hlink); + + hdmi_priv = devm_kzalloc(&hdev->dev, sizeof(*hdmi_priv), GFP_KERNEL); +- if (hdmi_priv == NULL) ++ if (hdmi_priv == NULL) { ++ snd_hdac_ext_bus_link_put(hdev->bus, hlink); + return -ENOMEM; ++ } + + snd_hdac_register_chmap_ops(hdev, &hdmi_priv->chmap); + hdmi_priv->chmap.ops.get_chmap = hdac_hdmi_get_chmap; +@@ -1876,8 +1875,10 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) + hdmi_priv->chmap.ops.get_spk_alloc = hdac_hdmi_get_spk_alloc; + hdmi_priv->hdev = hdev; + +- if (!hdac_id) ++ if (!hdac_id) { ++ snd_hdac_ext_bus_link_put(hdev->bus, hlink); + return -ENODEV; ++ } + + if (hdac_id->driver_data) + hdmi_priv->drv_data = +@@ -1902,6 +1903,8 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) + if (ret < 0) { + dev_err(&hdev->dev, + "Failed in parse and map nid with err: %d\n", ret); ++ snd_hdac_ext_bus_link_put(hdev->bus, hlink); ++ snd_hdac_display_power(hdev->bus, hdev->addr, false); + return ret; + } + snd_hdac_refresh_widgets(hdev); +diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c +--- a/sound/soc/codecs/hdmi-codec.c ++++ b/sound/soc/codecs/hdmi-codec.c +@@ -4,6 +4,7 @@ + * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ + * Author: Jyri Sarha + */ ++#include + #include + #include + #include +@@ -456,31 +457,30 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream, + if (!((has_playback && tx) || (has_capture && !tx))) + return 0; + +- mutex_lock(&hcp->lock); ++ guard(mutex)(&hcp->lock); + if (hcp->busy) { + dev_err(dai->dev, "Only one simultaneous stream supported!\n"); +- mutex_unlock(&hcp->lock); + return -EINVAL; + } + + if (hcp->hcd.ops->audio_startup) { + ret = hcp->hcd.ops->audio_startup(dai->dev->parent, hcp->hcd.data); + if (ret) +- goto err; ++ return ret; + } + + if (tx && hcp->hcd.ops->get_eld) { + ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->hcd.data, + hcp->eld, sizeof(hcp->eld)); + if (ret) +- goto err; ++ return ret; + + snd_parse_eld(dai->dev, &hcp->eld_parsed, + hcp->eld, sizeof(hcp->eld)); + + ret = snd_pcm_hw_constraint_eld(substream->runtime, hcp->eld); + if (ret) +- goto err; ++ return ret; + + /* Select chmap supported */ + hdmi_codec_eld_chmap(hcp); +@@ -488,8 +488,6 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream, + + hcp->busy = true; + +-err: +- mutex_unlock(&hcp->lock); + return ret; + } + +@@ -507,9 +505,8 @@ static void hdmi_codec_shutdown(struct snd_pcm_substream *substream, + hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; + hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data); + +- mutex_lock(&hcp->lock); ++ guard(mutex)(&hcp->lock); + hcp->busy = false; +- mutex_unlock(&hcp->lock); + } + + static int hdmi_codec_fill_codec_params(struct snd_soc_dai *dai, +diff --git a/sound/soc/codecs/idt821034.c b/sound/soc/codecs/idt821034.c +--- a/sound/soc/codecs/idt821034.c ++++ b/sound/soc/codecs/idt821034.c +@@ -7,6 +7,7 @@ + // Author: Herve Codina + + #include ++#include + #include + #include + #include +@@ -413,12 +414,12 @@ static int idt821034_kctrl_gain_get(struct snd_kcontrol *kcontrol, + + ch = IDT821034_ID_GET_CHAN(mc->reg); + +- mutex_lock(&idt821034->mutex); +- if (IDT821034_ID_IS_OUT(mc->reg)) +- val = idt821034->amps.ch[ch].amp_out.gain; +- else +- val = idt821034->amps.ch[ch].amp_in.gain; +- mutex_unlock(&idt821034->mutex); ++ scoped_guard(mutex, &idt821034->mutex) { ++ if (IDT821034_ID_IS_OUT(mc->reg)) ++ val = idt821034->amps.ch[ch].amp_out.gain; ++ else ++ val = idt821034->amps.ch[ch].amp_in.gain; ++ } + + ucontrol->value.integer.value[0] = val & mask; + if (invert) +@@ -456,7 +457,7 @@ static int idt821034_kctrl_gain_put(struct snd_kcontrol *kcontrol, + + ch = IDT821034_ID_GET_CHAN(mc->reg); + +- mutex_lock(&idt821034->mutex); ++ guard(mutex)(&idt821034->mutex); + + if (IDT821034_ID_IS_OUT(mc->reg)) { + amp = &idt821034->amps.ch[ch].amp_out; +@@ -466,22 +467,18 @@ static int idt821034_kctrl_gain_put(struct snd_kcontrol *kcontrol, + gain_type = IDT821034_GAIN_TX; + } + +- if (amp->gain == val) { +- ret = 0; +- goto end; +- } ++ if (amp->gain == val) ++ return 0; + + if (!amp->is_muted) { + ret = idt821034_set_gain_channel(idt821034, ch, gain_type, val); + if (ret) +- goto end; ++ return ret; + } + + amp->gain = val; +- ret = 1; /* The value changed */ +-end: +- mutex_unlock(&idt821034->mutex); +- return ret; ++ ++ return 1; + } + + static int idt821034_kctrl_mute_get(struct snd_kcontrol *kcontrol, +@@ -495,11 +492,11 @@ static int idt821034_kctrl_mute_get(struct snd_kcontrol *kcontrol, + + ch = IDT821034_ID_GET_CHAN(id); + +- mutex_lock(&idt821034->mutex); +- is_muted = IDT821034_ID_IS_OUT(id) ? +- idt821034->amps.ch[ch].amp_out.is_muted : +- idt821034->amps.ch[ch].amp_in.is_muted; +- mutex_unlock(&idt821034->mutex); ++ scoped_guard(mutex, &idt821034->mutex) { ++ is_muted = IDT821034_ID_IS_OUT(id) ? ++ idt821034->amps.ch[ch].amp_out.is_muted : ++ idt821034->amps.ch[ch].amp_in.is_muted; ++ } + + ucontrol->value.integer.value[0] = !is_muted; + +@@ -521,7 +518,7 @@ static int idt821034_kctrl_mute_put(struct snd_kcontrol *kcontrol, + ch = IDT821034_ID_GET_CHAN(id); + is_mute = !ucontrol->value.integer.value[0]; + +- mutex_lock(&idt821034->mutex); ++ guard(mutex)(&idt821034->mutex); + + if (IDT821034_ID_IS_OUT(id)) { + amp = &idt821034->amps.ch[ch].amp_out; +@@ -531,21 +528,17 @@ static int idt821034_kctrl_mute_put(struct snd_kcontrol *kcontrol, + gain_type = IDT821034_GAIN_TX; + } + +- if (amp->is_muted == is_mute) { +- ret = 0; +- goto end; +- } ++ if (amp->is_muted == is_mute) ++ return 0; + + ret = idt821034_set_gain_channel(idt821034, ch, gain_type, + is_mute ? 0 : amp->gain); + if (ret) +- goto end; ++ return ret; + + amp->is_muted = is_mute; +- ret = 1; /* The value changed */ +-end: +- mutex_unlock(&idt821034->mutex); +- return ret; ++ ++ return 1; + } + + static const DECLARE_TLV_DB_LINEAR(idt821034_gain_in, -300, 1300); +@@ -623,24 +616,20 @@ static int idt821034_power_event(struct snd_soc_dapm_widget *w, + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component); + unsigned int id = w->shift; + u8 power, mask; +- int ret; + u8 ch; + + ch = IDT821034_ID_GET_CHAN(id); + mask = IDT821034_ID_IS_OUT(id) ? IDT821034_CONF_PWRUP_RX : IDT821034_CONF_PWRUP_TX; + +- mutex_lock(&idt821034->mutex); ++ guard(mutex)(&idt821034->mutex); + + power = idt821034_get_channel_power(idt821034, ch); + if (SND_SOC_DAPM_EVENT_ON(event)) + power |= mask; + else + power &= ~mask; +- ret = idt821034_set_channel_power(idt821034, ch, power); + +- mutex_unlock(&idt821034->mutex); +- +- return ret; ++ return idt821034_set_channel_power(idt821034, ch, power); + } + + static const struct snd_soc_dapm_widget idt821034_dapm_widgets[] = { +@@ -717,9 +706,9 @@ static int idt821034_dai_set_tdm_slot(struct snd_soc_dai *dai, + ch = 0; + while (mask && ch < IDT821034_NB_CHANNEL) { + if (mask & 0x1) { +- mutex_lock(&idt821034->mutex); +- ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_RX, slot); +- mutex_unlock(&idt821034->mutex); ++ scoped_guard(mutex, &idt821034->mutex) ++ ret = idt821034_set_channel_ts(idt821034, ch, ++ IDT821034_CH_RX, slot); + if (ret) { + dev_err(dai->dev, "ch%u set tx tdm slot failed (%d)\n", + ch, ret); +@@ -742,9 +731,9 @@ static int idt821034_dai_set_tdm_slot(struct snd_soc_dai *dai, + ch = 0; + while (mask && ch < IDT821034_NB_CHANNEL) { + if (mask & 0x1) { +- mutex_lock(&idt821034->mutex); +- ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_TX, slot); +- mutex_unlock(&idt821034->mutex); ++ scoped_guard(mutex, &idt821034->mutex) ++ ret = idt821034_set_channel_ts(idt821034, ch, ++ IDT821034_CH_TX, slot); + if (ret) { + dev_err(dai->dev, "ch%u set rx tdm slot failed (%d)\n", + ch, ret); +@@ -769,9 +758,8 @@ static int idt821034_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) + { + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component); + u8 conf; +- int ret; + +- mutex_lock(&idt821034->mutex); ++ guard(mutex)(&idt821034->mutex); + + conf = idt821034_get_codec_conf(idt821034); + +@@ -785,13 +773,10 @@ static int idt821034_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) + default: + dev_err(dai->dev, "Unsupported DAI format 0x%x\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); +- ret = -EINVAL; +- goto end; ++ return -EINVAL; + } +- ret = idt821034_set_codec_conf(idt821034, conf); +-end: +- mutex_unlock(&idt821034->mutex); +- return ret; ++ ++ return idt821034_set_codec_conf(idt821034, conf); + } + + static int idt821034_dai_hw_params(struct snd_pcm_substream *substream, +@@ -800,9 +785,8 @@ static int idt821034_dai_hw_params(struct snd_pcm_substream *substream, + { + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component); + u8 conf; +- int ret; + +- mutex_lock(&idt821034->mutex); ++ guard(mutex)(&idt821034->mutex); + + conf = idt821034_get_codec_conf(idt821034); + +@@ -816,13 +800,10 @@ static int idt821034_dai_hw_params(struct snd_pcm_substream *substream, + default: + dev_err(dai->dev, "Unsupported PCM format 0x%x\n", + params_format(params)); +- ret = -EINVAL; +- goto end; ++ return -EINVAL; + } +- ret = idt821034_set_codec_conf(idt821034, conf); +-end: +- mutex_unlock(&idt821034->mutex); +- return ret; ++ ++ return idt821034_set_codec_conf(idt821034, conf); + } + + static const unsigned int idt821034_sample_bits[] = {8}; +@@ -897,11 +878,11 @@ static int idt821034_reset_audio(struct idt821034 *idt821034) + int ret; + u8 i; + +- mutex_lock(&idt821034->mutex); ++ guard(mutex)(&idt821034->mutex); + + ret = idt821034_set_codec_conf(idt821034, 0); + if (ret) +- goto end; ++ return ret; + + for (i = 0; i < IDT821034_NB_CHANNEL; i++) { + idt821034->amps.ch[i].amp_out.gain = IDT821034_GAIN_OUT_INIT_RAW; +@@ -909,24 +890,21 @@ static int idt821034_reset_audio(struct idt821034 *idt821034) + ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_RX, + idt821034->amps.ch[i].amp_out.gain); + if (ret) +- goto end; ++ return ret; + + idt821034->amps.ch[i].amp_in.gain = IDT821034_GAIN_IN_INIT_RAW; + idt821034->amps.ch[i].amp_in.is_muted = false; + ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_TX, + idt821034->amps.ch[i].amp_in.gain); + if (ret) +- goto end; ++ return ret; + + ret = idt821034_set_channel_power(idt821034, i, 0); + if (ret) +- goto end; ++ return ret; + } + +- ret = 0; +-end: +- mutex_unlock(&idt821034->mutex); +- return ret; ++ return 0; + } + + static int idt821034_component_probe(struct snd_soc_component *component) +@@ -965,7 +943,7 @@ static int idt821034_chip_gpio_set(struct gpio_chip *c, unsigned int offset, + u8 slic_raw; + int ret; + +- mutex_lock(&idt821034->mutex); ++ guard(mutex)(&idt821034->mutex); + + slic_raw = idt821034_get_written_slic_raw(idt821034, ch); + if (val) +@@ -974,8 +952,6 @@ static int idt821034_chip_gpio_set(struct gpio_chip *c, unsigned int offset, + slic_raw &= ~mask; + ret = idt821034_write_slic_raw(idt821034, ch, slic_raw); + +- mutex_unlock(&idt821034->mutex); +- + if (ret) + dev_err(&idt821034->spi->dev, "set gpio %d (%u, 0x%x) failed (%d)\n", + offset, ch, mask, ret); +@@ -991,9 +967,8 @@ static int idt821034_chip_gpio_get(struct gpio_chip *c, unsigned int offset) + u8 slic_raw; + int ret; + +- mutex_lock(&idt821034->mutex); +- ret = idt821034_read_slic_raw(idt821034, ch, &slic_raw); +- mutex_unlock(&idt821034->mutex); ++ scoped_guard(mutex, &idt821034->mutex) ++ ret = idt821034_read_slic_raw(idt821034, ch, &slic_raw); + if (ret) { + dev_err(&idt821034->spi->dev, "get gpio %d (%u, 0x%x) failed (%d)\n", + offset, ch, mask, ret); +@@ -1015,9 +990,8 @@ static int idt821034_chip_get_direction(struct gpio_chip *c, unsigned int offset + struct idt821034 *idt821034 = gpiochip_get_data(c); + u8 slic_dir; + +- mutex_lock(&idt821034->mutex); ++ guard(mutex)(&idt821034->mutex); + slic_dir = idt821034_get_slic_conf(idt821034, ch); +- mutex_unlock(&idt821034->mutex); + + return slic_dir & mask ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT; + } +@@ -1034,7 +1008,7 @@ static int idt821034_chip_direction_input(struct gpio_chip *c, unsigned int offs + if (mask & ~(IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN)) + return -EPERM; + +- mutex_lock(&idt821034->mutex); ++ guard(mutex)(&idt821034->mutex); + + slic_conf = idt821034_get_slic_conf(idt821034, ch) | mask; + +@@ -1044,7 +1018,6 @@ static int idt821034_chip_direction_input(struct gpio_chip *c, unsigned int offs + offset, ch, mask, ret); + } + +- mutex_unlock(&idt821034->mutex); + return ret; + } + +@@ -1060,7 +1033,7 @@ static int idt821034_chip_direction_output(struct gpio_chip *c, unsigned int off + if (ret) + return ret; + +- mutex_lock(&idt821034->mutex); ++ guard(mutex)(&idt821034->mutex); + + slic_conf = idt821034_get_slic_conf(idt821034, ch) & ~mask; + +@@ -1070,7 +1043,6 @@ static int idt821034_chip_direction_output(struct gpio_chip *c, unsigned int off + offset, ch, mask, ret); + } + +- mutex_unlock(&idt821034->mutex); + return ret; + } + +@@ -1079,24 +1051,22 @@ static int idt821034_reset_gpio(struct idt821034 *idt821034) + int ret; + u8 i; + +- mutex_lock(&idt821034->mutex); ++ guard(mutex)(&idt821034->mutex); + + /* IO0 and IO1 as input for all channels and output IO set to 0 */ + for (i = 0; i < IDT821034_NB_CHANNEL; i++) { + ret = idt821034_set_slic_conf(idt821034, i, + IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN); + if (ret) +- goto end; ++ return ret; + + ret = idt821034_write_slic_raw(idt821034, i, 0); + if (ret) +- goto end; ++ return ret; + + } +- ret = 0; +-end: +- mutex_unlock(&idt821034->mutex); +- return ret; ++ ++ return 0; + } + + static int idt821034_gpio_init(struct idt821034 *idt821034) +diff --git a/sound/soc/codecs/lochnagar-sc.c b/sound/soc/codecs/lochnagar-sc.c +--- a/sound/soc/codecs/lochnagar-sc.c ++++ b/sound/soc/codecs/lochnagar-sc.c +@@ -137,15 +137,23 @@ static int lochnagar_sc_set_usb_fmt(struct snd_soc_dai *dai, unsigned int fmt) + return lochnagar_sc_check_fmt(dai, fmt, SND_SOC_DAIFMT_CBP_CFP); + } + ++static const u64 lochnagar_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF; ++ + static const struct snd_soc_dai_ops lochnagar_sc_line_ops = { + .startup = lochnagar_sc_line_startup, + .shutdown = lochnagar_sc_line_shutdown, + .set_fmt = lochnagar_sc_set_line_fmt, ++ .auto_selectable_formats = &lochnagar_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static const struct snd_soc_dai_ops lochnagar_sc_usb_ops = { + .startup = lochnagar_sc_startup, + .set_fmt = lochnagar_sc_set_usb_fmt, ++ .auto_selectable_formats = &lochnagar_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static struct snd_soc_dai_driver lochnagar_sc_dai[] = { +diff --git a/sound/soc/codecs/lpass-macro-common.c b/sound/soc/codecs/lpass-macro-common.c +--- a/sound/soc/codecs/lpass-macro-common.c ++++ b/sound/soc/codecs/lpass-macro-common.c +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: GPL-2.0-only + // Copyright (c) 2022, The Linux Foundation. All rights reserved. + ++#include + #include + #include + #include +@@ -71,21 +72,16 @@ EXPORT_SYMBOL_GPL(lpass_macro_pds_exit); + + void lpass_macro_set_codec_version(enum lpass_codec_version version) + { +- mutex_lock(&lpass_codec_mutex); ++ guard(mutex)(&lpass_codec_mutex); + lpass_codec_version = version; +- mutex_unlock(&lpass_codec_mutex); + } + EXPORT_SYMBOL_GPL(lpass_macro_set_codec_version); + + enum lpass_codec_version lpass_macro_get_codec_version(void) + { +- enum lpass_codec_version ver; ++ guard(mutex)(&lpass_codec_mutex); + +- mutex_lock(&lpass_codec_mutex); +- ver = lpass_codec_version; +- mutex_unlock(&lpass_codec_mutex); +- +- return ver; ++ return lpass_codec_version; + } + EXPORT_SYMBOL_GPL(lpass_macro_get_codec_version); + +diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c +--- a/sound/soc/codecs/lpass-rx-macro.c ++++ b/sound/soc/codecs/lpass-rx-macro.c +@@ -6,6 +6,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -2030,9 +2031,10 @@ static struct snd_soc_dai_driver rx_macro_dai[] = { + }, + }; + +-static void rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable) ++static int rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable) + { + struct regmap *regmap = rx->regmap; ++ int ret; + + if (mclk_enable) { + if (rx->rx_mclk_users == 0) { +@@ -2047,14 +2049,16 @@ static void rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable) + CDC_RX_FS_MCLK_CNT_EN_MASK, + CDC_RX_FS_MCLK_CNT_ENABLE); + regcache_mark_dirty(regmap); +- regcache_sync(regmap); ++ ret = regcache_sync(regmap); ++ if (ret) ++ return ret; + } + rx->rx_mclk_users++; + } else { + if (rx->rx_mclk_users <= 0) { + dev_err(rx->dev, "%s: clock already disabled\n", __func__); + rx->rx_mclk_users = 0; +- return; ++ return 0; + } + rx->rx_mclk_users--; + if (rx->rx_mclk_users == 0) { +@@ -2068,6 +2072,8 @@ static void rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable) + CDC_RX_CLK_MCLK2_EN_MASK, 0x0); + } + } ++ ++ return 0; + } + + static int rx_macro_mclk_event(struct snd_soc_dapm_widget *w, +@@ -2079,11 +2085,9 @@ static int rx_macro_mclk_event(struct snd_soc_dapm_widget *w, + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: +- rx_macro_mclk_enable(rx, true); +- break; ++ return rx_macro_mclk_enable(rx, true); + case SND_SOC_DAPM_POST_PMD: +- rx_macro_mclk_enable(rx, false); +- break; ++ return rx_macro_mclk_enable(rx, false); + default: + dev_err(component->dev, "%s: invalid DAPM event %d\n", __func__, event); + ret = -EINVAL; +@@ -3671,14 +3675,16 @@ static int swclk_gate_enable(struct clk_hw *hw) + struct rx_macro *rx = to_rx_macro(hw); + int ret; + +- ret = clk_prepare_enable(rx->mclk); ++ ret = pm_runtime_resume_and_get(rx->dev); ++ if (ret < 0) ++ return ret; ++ ++ ret = rx_macro_mclk_enable(rx, true); + if (ret) { +- dev_err(rx->dev, "unable to prepare mclk\n"); ++ clk_disable_unprepare(rx->mclk); + return ret; + } + +- rx_macro_mclk_enable(rx, true); +- + regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, + CDC_RX_SWR_CLK_EN_MASK, 1); + +@@ -3693,7 +3699,7 @@ static void swclk_gate_disable(struct clk_hw *hw) + CDC_RX_SWR_CLK_EN_MASK, 0); + + rx_macro_mclk_enable(rx, false); +- clk_disable_unprepare(rx->mclk); ++ pm_runtime_put_autosuspend(rx->dev); + } + + static int swclk_gate_is_enabled(struct clk_hw *hw) +@@ -3864,28 +3870,31 @@ static int rx_macro_probe(struct platform_device *pdev) + rx->dev = dev; + + /* set MCLK and NPL rates */ +- clk_set_rate(rx->mclk, MCLK_FREQ); +- clk_set_rate(rx->npl, MCLK_FREQ); +- +- ret = clk_prepare_enable(rx->macro); ++ ret = clk_set_rate(rx->mclk, MCLK_FREQ); + if (ret) + return ret; + +- ret = clk_prepare_enable(rx->dcodec); ++ ret = clk_set_rate(rx->npl, MCLK_FREQ); + if (ret) +- goto err_dcodec; ++ return ret; + +- ret = clk_prepare_enable(rx->mclk); ++ ret = devm_pm_clk_create(dev); + if (ret) +- goto err_mclk; ++ return ret; + +- ret = clk_prepare_enable(rx->npl); +- if (ret) +- goto err_npl; ++ ret = of_pm_clk_add_clks(dev); ++ if (ret < 0) ++ return ret; + +- ret = clk_prepare_enable(rx->fsgen); ++ pm_runtime_set_autosuspend_delay(dev, 100); ++ pm_runtime_use_autosuspend(dev); ++ ret = devm_pm_runtime_enable(dev); + if (ret) +- goto err_fsgen; ++ return ret; ++ ++ ret = pm_runtime_resume_and_get(dev); ++ if (ret) ++ return ret; + + /* reset swr block */ + regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, +@@ -3902,46 +3911,25 @@ static int rx_macro_probe(struct platform_device *pdev) + rx_macro_dai, + ARRAY_SIZE(rx_macro_dai)); + if (ret) +- goto err_clkout; +- +- +- pm_runtime_set_autosuspend_delay(dev, 3000); +- pm_runtime_use_autosuspend(dev); +- pm_runtime_mark_last_busy(dev); +- pm_runtime_set_active(dev); +- pm_runtime_enable(dev); ++ goto err_rpm_put; + + ret = rx_macro_register_mclk_output(rx); + if (ret) +- goto err_clkout; ++ goto err_rpm_put; ++ ++ ret = pm_runtime_put_autosuspend(dev); ++ if (ret < 0) ++ dev_warn(dev, "runtime PM put failed after probe: %d\n", ret); + + return 0; + +-err_clkout: +- clk_disable_unprepare(rx->fsgen); +-err_fsgen: +- clk_disable_unprepare(rx->npl); +-err_npl: +- clk_disable_unprepare(rx->mclk); +-err_mclk: +- clk_disable_unprepare(rx->dcodec); +-err_dcodec: +- clk_disable_unprepare(rx->macro); ++err_rpm_put: ++ if (pm_runtime_put_sync_suspend(dev) < 0) ++ dev_warn(dev, "runtime PM sync suspend failed in probe unwind\n"); + + return ret; + } + +-static void rx_macro_remove(struct platform_device *pdev) +-{ +- struct rx_macro *rx = dev_get_drvdata(&pdev->dev); +- +- clk_disable_unprepare(rx->mclk); +- clk_disable_unprepare(rx->npl); +- clk_disable_unprepare(rx->fsgen); +- clk_disable_unprepare(rx->macro); +- clk_disable_unprepare(rx->dcodec); +-} +- + static const struct of_device_id rx_macro_dt_match[] = { + { + .compatible = "qcom,sc7280-lpass-rx-macro", +@@ -3969,13 +3957,17 @@ MODULE_DEVICE_TABLE(of, rx_macro_dt_match); + static int rx_macro_runtime_suspend(struct device *dev) + { + struct rx_macro *rx = dev_get_drvdata(dev); ++ int ret; + + regcache_cache_only(rx->regmap, true); +- regcache_mark_dirty(rx->regmap); + +- clk_disable_unprepare(rx->fsgen); +- clk_disable_unprepare(rx->npl); +- clk_disable_unprepare(rx->mclk); ++ ret = pm_clk_suspend(dev); ++ if (ret) { ++ regcache_cache_only(rx->regmap, false); ++ return ret; ++ } ++ ++ regcache_mark_dirty(rx->regmap); + + return 0; + } +@@ -3985,33 +3977,23 @@ static int rx_macro_runtime_resume(struct device *dev) + struct rx_macro *rx = dev_get_drvdata(dev); + int ret; + +- ret = clk_prepare_enable(rx->mclk); ++ ret = pm_clk_resume(dev); + if (ret) { +- dev_err(dev, "unable to prepare mclk\n"); ++ regcache_cache_only(rx->regmap, true); ++ regcache_mark_dirty(rx->regmap); + return ret; + } + +- ret = clk_prepare_enable(rx->npl); +- if (ret) { +- dev_err(dev, "unable to prepare mclkx2\n"); +- goto err_npl; +- } +- +- ret = clk_prepare_enable(rx->fsgen); +- if (ret) { +- dev_err(dev, "unable to prepare fsgen\n"); +- goto err_fsgen; +- } + regcache_cache_only(rx->regmap, false); +- regcache_sync(rx->regmap); ++ ret = regcache_sync(rx->regmap); ++ if (ret) { ++ regcache_cache_only(rx->regmap, true); ++ regcache_mark_dirty(rx->regmap); ++ pm_clk_suspend(dev); ++ return ret; ++ } + + return 0; +-err_fsgen: +- clk_disable_unprepare(rx->npl); +-err_npl: +- clk_disable_unprepare(rx->mclk); +- +- return ret; + } + + static const struct dev_pm_ops rx_macro_pm_ops = { +@@ -4026,7 +4008,6 @@ static struct platform_driver rx_macro_driver = { + .pm = pm_ptr(&rx_macro_pm_ops), + }, + .probe = rx_macro_probe, +- .remove = rx_macro_remove, + }; + + module_platform_driver(rx_macro_driver); +diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c +--- a/sound/soc/codecs/lpass-tx-macro.c ++++ b/sound/soc/codecs/lpass-tx-macro.c +@@ -6,6 +6,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -614,6 +615,7 @@ static int tx_macro_mclk_enable(struct tx_macro *tx, + bool mclk_enable) + { + struct regmap *regmap = tx->regmap; ++ int ret; + + if (mclk_enable) { + if (tx->tx_mclk_users == 0) { +@@ -626,7 +628,9 @@ static int tx_macro_mclk_enable(struct tx_macro *tx, + CDC_TX_FS_CNT_EN_MASK, + CDC_TX_FS_CNT_ENABLE); + regcache_mark_dirty(regmap); +- regcache_sync(regmap); ++ ret = regcache_sync(regmap); ++ if (ret) ++ return ret; + } + tx->tx_mclk_users++; + } else { +@@ -739,11 +743,9 @@ static int tx_macro_mclk_event(struct snd_soc_dapm_widget *w, + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: +- tx_macro_mclk_enable(tx, true); +- break; ++ return tx_macro_mclk_enable(tx, true); + case SND_SOC_DAPM_POST_PMD: +- tx_macro_mclk_enable(tx, false); +- break; ++ return tx_macro_mclk_enable(tx, false); + default: + break; + } +@@ -2149,17 +2151,20 @@ static int swclk_gate_enable(struct clk_hw *hw) + struct regmap *regmap = tx->regmap; + int ret; + +- ret = clk_prepare_enable(tx->mclk); ++ ret = pm_runtime_resume_and_get(tx->dev); ++ if (ret < 0) ++ return ret; ++ ++ ret = tx_macro_mclk_enable(tx, true); + if (ret) { +- dev_err(tx->dev, "failed to enable mclk\n"); ++ pm_runtime_put_autosuspend(tx->dev); + return ret; + } + +- tx_macro_mclk_enable(tx, true); +- + regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, + CDC_TX_SWR_CLK_EN_MASK, + CDC_TX_SWR_CLK_ENABLE); ++ + return 0; + } + +@@ -2172,7 +2177,7 @@ static void swclk_gate_disable(struct clk_hw *hw) + CDC_TX_SWR_CLK_EN_MASK, 0x0); + + tx_macro_mclk_enable(tx, false); +- clk_disable_unprepare(tx->mclk); ++ pm_runtime_put_autosuspend(tx->dev); + } + + static int swclk_gate_is_enabled(struct clk_hw *hw) +@@ -2315,28 +2320,31 @@ static int tx_macro_probe(struct platform_device *pdev) + tx->active_decimator[TX_MACRO_AIF3_CAP] = -1; + + /* set MCLK and NPL rates */ +- clk_set_rate(tx->mclk, MCLK_FREQ); +- clk_set_rate(tx->npl, MCLK_FREQ); +- +- ret = clk_prepare_enable(tx->macro); ++ ret = clk_set_rate(tx->mclk, MCLK_FREQ); + if (ret) + goto err; + +- ret = clk_prepare_enable(tx->dcodec); ++ ret = clk_set_rate(tx->npl, MCLK_FREQ); + if (ret) +- goto err_dcodec; ++ goto err; + +- ret = clk_prepare_enable(tx->mclk); ++ ret = devm_pm_clk_create(dev); + if (ret) +- goto err_mclk; ++ goto err; + +- ret = clk_prepare_enable(tx->npl); +- if (ret) +- goto err_npl; ++ ret = of_pm_clk_add_clks(dev); ++ if (ret < 0) ++ goto err; + +- ret = clk_prepare_enable(tx->fsgen); ++ pm_runtime_set_autosuspend_delay(dev, 100); ++ pm_runtime_use_autosuspend(dev); ++ ret = devm_pm_runtime_enable(dev); + if (ret) +- goto err_fsgen; ++ goto err; ++ ++ ret = pm_runtime_resume_and_get(dev); ++ if (ret < 0) ++ goto err; + + + /* reset soundwire block */ +@@ -2356,30 +2364,21 @@ static int tx_macro_probe(struct platform_device *pdev) + tx_macro_dai, + ARRAY_SIZE(tx_macro_dai)); + if (ret) +- goto err_clkout; +- +- pm_runtime_set_autosuspend_delay(dev, 3000); +- pm_runtime_use_autosuspend(dev); +- pm_runtime_mark_last_busy(dev); +- pm_runtime_set_active(dev); +- pm_runtime_enable(dev); ++ goto err_rpm_put; + + ret = tx_macro_register_mclk_output(tx); + if (ret) +- goto err_clkout; ++ goto err_rpm_put; ++ ++ ret = pm_runtime_put_autosuspend(dev); ++ if (ret < 0) ++ dev_warn(dev, "runtime PM put failed after probe: %d\n", ret); + + return 0; + +-err_clkout: +- clk_disable_unprepare(tx->fsgen); +-err_fsgen: +- clk_disable_unprepare(tx->npl); +-err_npl: +- clk_disable_unprepare(tx->mclk); +-err_mclk: +- clk_disable_unprepare(tx->dcodec); +-err_dcodec: +- clk_disable_unprepare(tx->macro); ++err_rpm_put: ++ if (pm_runtime_put_sync_suspend(dev) < 0) ++ dev_warn(dev, "runtime PM sync suspend failed in probe unwind\n"); + err: + lpass_macro_pds_exit(tx->pds); + +@@ -2390,25 +2389,23 @@ static void tx_macro_remove(struct platform_device *pdev) + { + struct tx_macro *tx = dev_get_drvdata(&pdev->dev); + +- clk_disable_unprepare(tx->macro); +- clk_disable_unprepare(tx->dcodec); +- clk_disable_unprepare(tx->mclk); +- clk_disable_unprepare(tx->npl); +- clk_disable_unprepare(tx->fsgen); +- + lpass_macro_pds_exit(tx->pds); + } + + static int tx_macro_runtime_suspend(struct device *dev) + { + struct tx_macro *tx = dev_get_drvdata(dev); ++ int ret; + + regcache_cache_only(tx->regmap, true); +- regcache_mark_dirty(tx->regmap); + +- clk_disable_unprepare(tx->fsgen); +- clk_disable_unprepare(tx->npl); +- clk_disable_unprepare(tx->mclk); ++ ret = pm_clk_suspend(dev); ++ if (ret) { ++ regcache_cache_only(tx->regmap, false); ++ return ret; ++ } ++ ++ regcache_mark_dirty(tx->regmap); + + return 0; + } +@@ -2418,34 +2415,23 @@ static int tx_macro_runtime_resume(struct device *dev) + struct tx_macro *tx = dev_get_drvdata(dev); + int ret; + +- ret = clk_prepare_enable(tx->mclk); ++ ret = pm_clk_resume(dev); + if (ret) { +- dev_err(dev, "unable to prepare mclk\n"); ++ regcache_cache_only(tx->regmap, true); ++ regcache_mark_dirty(tx->regmap); + return ret; + } + +- ret = clk_prepare_enable(tx->npl); +- if (ret) { +- dev_err(dev, "unable to prepare npl\n"); +- goto err_npl; +- } +- +- ret = clk_prepare_enable(tx->fsgen); +- if (ret) { +- dev_err(dev, "unable to prepare fsgen\n"); +- goto err_fsgen; +- } +- + regcache_cache_only(tx->regmap, false); +- regcache_sync(tx->regmap); ++ ret = regcache_sync(tx->regmap); ++ if (ret) { ++ regcache_cache_only(tx->regmap, true); ++ regcache_mark_dirty(tx->regmap); ++ pm_clk_suspend(dev); ++ return ret; ++ } + + return 0; +-err_fsgen: +- clk_disable_unprepare(tx->npl); +-err_npl: +- clk_disable_unprepare(tx->mclk); +- +- return ret; + } + + static const struct dev_pm_ops tx_macro_pm_ops = { +diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c +--- a/sound/soc/codecs/lpass-va-macro.c ++++ b/sound/soc/codecs/lpass-va-macro.c +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1351,34 +1352,43 @@ static int fsgen_gate_enable(struct clk_hw *hw) + { + struct va_macro *va = to_va_macro(hw); + struct regmap *regmap = va->regmap; +- int ret; ++ int ret, rpm_ret; + +- if (va->has_swr_master) { +- ret = clk_prepare_enable(va->mclk); +- if (ret) +- return ret; +- } ++ ret = pm_runtime_resume_and_get(va->dev); ++ if (ret < 0) ++ return ret; + + ret = va_macro_mclk_enable(va, true); ++ if (ret) { ++ rpm_ret = pm_runtime_put_autosuspend(va->dev); ++ if (rpm_ret < 0) ++ dev_warn(va->dev, ++ "runtime PM put failed in fsgen enable unwind: %d\n", ++ rpm_ret); ++ return ret; ++ } + if (va->has_swr_master) + regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, + CDC_VA_SWR_CLK_EN_MASK, CDC_VA_SWR_CLK_ENABLE); + +- return ret; ++ return 0; + } + + static void fsgen_gate_disable(struct clk_hw *hw) + { + struct va_macro *va = to_va_macro(hw); + struct regmap *regmap = va->regmap; ++ int ret; + + if (va->has_swr_master) + regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, + CDC_VA_SWR_CLK_EN_MASK, 0x0); + + va_macro_mclk_enable(va, false); +- if (va->has_swr_master) +- clk_disable_unprepare(va->mclk); ++ ++ ret = pm_runtime_put_autosuspend(va->dev); ++ if (ret < 0) ++ dev_warn(va->dev, "runtime PM put failed in fsgen disable: %d\n", ret); + } + + static int fsgen_gate_is_enabled(struct clk_hw *hw) +@@ -1539,6 +1549,7 @@ static int va_macro_probe(struct platform_device *pdev) + void __iomem *base; + u32 sample_rate = 0; + int ret; ++ int rpm_ret; + + va = devm_kzalloc(dev, sizeof(*va), GFP_KERNEL); + if (!va) +@@ -1594,7 +1605,9 @@ static int va_macro_probe(struct platform_device *pdev) + va->has_npl_clk = data->has_npl_clk; + + /* mclk rate */ +- clk_set_rate(va->mclk, 2 * VA_MACRO_MCLK_FREQ); ++ ret = clk_set_rate(va->mclk, 2 * VA_MACRO_MCLK_FREQ); ++ if (ret) ++ goto err; + + if (va->has_npl_clk) { + va->npl = devm_clk_get(dev, "npl"); +@@ -1603,25 +1616,29 @@ static int va_macro_probe(struct platform_device *pdev) + goto err; + } + +- clk_set_rate(va->npl, 2 * VA_MACRO_MCLK_FREQ); ++ ret = clk_set_rate(va->npl, 2 * VA_MACRO_MCLK_FREQ); ++ if (ret) ++ goto err; + } + +- ret = clk_prepare_enable(va->macro); ++ ret = devm_pm_clk_create(dev); + if (ret) + goto err; + +- ret = clk_prepare_enable(va->dcodec); +- if (ret) +- goto err_dcodec; ++ ret = of_pm_clk_add_clks(dev); ++ if (ret < 0) ++ goto err; + +- ret = clk_prepare_enable(va->mclk); ++ pm_runtime_set_autosuspend_delay(dev, 100); ++ pm_runtime_use_autosuspend(dev); ++ ret = devm_pm_runtime_enable(dev); + if (ret) +- goto err_mclk; ++ goto err; + +- if (va->has_npl_clk) { +- ret = clk_prepare_enable(va->npl); +- if (ret) +- goto err_npl; ++ rpm_ret = pm_runtime_resume_and_get(dev); ++ if (rpm_ret < 0) { ++ ret = rpm_ret; ++ goto err; + } + + /** +@@ -1634,7 +1651,7 @@ static int va_macro_probe(struct platform_device *pdev) + /* read version from register */ + ret = va_macro_set_lpass_codec_version(va); + if (ret) +- goto err_clkout; ++ goto err_rpm_put; + } + + if (va->has_swr_master) { +@@ -1664,35 +1681,28 @@ static int va_macro_probe(struct platform_device *pdev) + va_macro_dais, + ARRAY_SIZE(va_macro_dais)); + if (ret) +- goto err_clkout; +- +- pm_runtime_set_autosuspend_delay(dev, 3000); +- pm_runtime_use_autosuspend(dev); +- pm_runtime_mark_last_busy(dev); +- pm_runtime_set_active(dev); +- pm_runtime_enable(dev); ++ goto err_rpm_put; + + ret = va_macro_register_fsgen_output(va); + if (ret) +- goto err_clkout; ++ goto err_rpm_put; + + va->fsgen = devm_clk_hw_get_clk(dev, &va->hw, "fsgen"); + if (IS_ERR(va->fsgen)) { + ret = PTR_ERR(va->fsgen); +- goto err_clkout; ++ goto err_rpm_put; + } + ++ rpm_ret = pm_runtime_put_autosuspend(dev); ++ if (rpm_ret < 0) ++ dev_warn(dev, "runtime PM put failed after probe: %d\n", rpm_ret); ++ + return 0; + +-err_clkout: +- if (va->has_npl_clk) +- clk_disable_unprepare(va->npl); +-err_npl: +- clk_disable_unprepare(va->mclk); +-err_mclk: +- clk_disable_unprepare(va->dcodec); +-err_dcodec: +- clk_disable_unprepare(va->macro); ++err_rpm_put: ++ rpm_ret = pm_runtime_put_sync_suspend(dev); ++ if (rpm_ret < 0) ++ dev_warn(dev, "runtime PM sync suspend failed in probe unwind: %d\n", rpm_ret); + err: + lpass_macro_pds_exit(va->pds); + +@@ -1703,53 +1713,52 @@ static void va_macro_remove(struct platform_device *pdev) + { + struct va_macro *va = dev_get_drvdata(&pdev->dev); + +- if (va->has_npl_clk) +- clk_disable_unprepare(va->npl); +- +- clk_disable_unprepare(va->mclk); +- clk_disable_unprepare(va->dcodec); +- clk_disable_unprepare(va->macro); +- + lpass_macro_pds_exit(va->pds); + } + + static int va_macro_runtime_suspend(struct device *dev) + { + struct va_macro *va = dev_get_drvdata(dev); ++ int ret; + + regcache_cache_only(va->regmap, true); ++ ++ ret = pm_clk_suspend(dev); ++ if (ret) { ++ regcache_cache_only(va->regmap, false); ++ return ret; ++ } ++ + regcache_mark_dirty(va->regmap); + +- if (va->has_npl_clk) +- clk_disable_unprepare(va->npl); +- +- clk_disable_unprepare(va->mclk); +- + return 0; + } + + static int va_macro_runtime_resume(struct device *dev) + { + struct va_macro *va = dev_get_drvdata(dev); +- int ret; ++ int ret, sret; + +- ret = clk_prepare_enable(va->mclk); ++ ret = pm_clk_resume(dev); + if (ret) { +- dev_err(va->dev, "unable to prepare mclk\n"); ++ regcache_cache_only(va->regmap, true); ++ regcache_mark_dirty(va->regmap); + return ret; + } + +- if (va->has_npl_clk) { +- ret = clk_prepare_enable(va->npl); +- if (ret) { +- clk_disable_unprepare(va->mclk); +- dev_err(va->dev, "unable to prepare npl\n"); +- return ret; +- } +- } +- + regcache_cache_only(va->regmap, false); +- regcache_sync(va->regmap); ++ ++ ret = regcache_sync(va->regmap); ++ if (ret) { ++ regcache_cache_only(va->regmap, true); ++ regcache_mark_dirty(va->regmap); ++ sret = pm_clk_suspend(dev); ++ if (sret) ++ dev_err(va->dev, ++ "failed to suspend clocks after regcache sync failure: %d\n", ++ sret); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c +--- a/sound/soc/codecs/lpass-wsa-macro.c ++++ b/sound/soc/codecs/lpass-wsa-macro.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -2529,15 +2530,13 @@ static const struct snd_soc_dapm_route wsa_audio_map[] = { + static int wsa_swrm_clock(struct wsa_macro *wsa, bool enable) + { + struct regmap *regmap = wsa->regmap; ++ int ret; + + if (enable) { +- int ret; +- +- ret = clk_prepare_enable(wsa->mclk); +- if (ret) { +- dev_err(wsa->dev, "failed to enable mclk\n"); ++ ret = pm_runtime_resume_and_get(wsa->dev); ++ if (ret < 0) + return ret; +- } ++ + wsa_macro_mclk_enable(wsa, true); + + regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, +@@ -2548,7 +2547,10 @@ static int wsa_swrm_clock(struct wsa_macro *wsa, bool enable) + regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, + CDC_WSA_SWR_CLK_EN_MASK, 0); + wsa_macro_mclk_enable(wsa, false); +- clk_disable_unprepare(wsa->mclk); ++ ++ ret = pm_runtime_put_autosuspend(wsa->dev); ++ if (ret < 0) ++ dev_warn(wsa->dev, "runtime PM put failed: %d\n", ret); + } + + return 0; +@@ -2656,7 +2658,7 @@ static int wsa_macro_register_mclk_output(struct wsa_macro *wsa) + init.num_parents = 1; + wsa->hw.init = &init; + hw = &wsa->hw; +- ret = clk_hw_register(wsa->dev, hw); ++ ret = devm_clk_hw_register(wsa->dev, hw); + if (ret) + return ret; + +@@ -2771,28 +2773,31 @@ static int wsa_macro_probe(struct platform_device *pdev) + wsa->dev = dev; + + /* set MCLK and NPL rates */ +- clk_set_rate(wsa->mclk, WSA_MACRO_MCLK_FREQ); +- clk_set_rate(wsa->npl, WSA_MACRO_MCLK_FREQ); +- +- ret = clk_prepare_enable(wsa->macro); ++ ret = clk_set_rate(wsa->mclk, WSA_MACRO_MCLK_FREQ); + if (ret) +- goto err; ++ return ret; + +- ret = clk_prepare_enable(wsa->dcodec); ++ ret = clk_set_rate(wsa->npl, WSA_MACRO_MCLK_FREQ); + if (ret) +- goto err_dcodec; ++ return ret; + +- ret = clk_prepare_enable(wsa->mclk); ++ ret = devm_pm_clk_create(dev); + if (ret) +- goto err_mclk; ++ return ret; + +- ret = clk_prepare_enable(wsa->npl); +- if (ret) +- goto err_npl; ++ ret = of_pm_clk_add_clks(dev); ++ if (ret < 0) ++ return ret; + +- ret = clk_prepare_enable(wsa->fsgen); ++ pm_runtime_set_autosuspend_delay(dev, 100); ++ pm_runtime_use_autosuspend(dev); ++ ret = devm_pm_runtime_enable(dev); + if (ret) +- goto err_fsgen; ++ return ret; ++ ++ ret = pm_runtime_resume_and_get(dev); ++ if (ret < 0) ++ return ret; + + /* reset swr ip */ + regmap_update_bits(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, +@@ -2809,56 +2814,37 @@ static int wsa_macro_probe(struct platform_device *pdev) + wsa_macro_dai, + ARRAY_SIZE(wsa_macro_dai)); + if (ret) +- goto err_clkout; +- +- pm_runtime_set_autosuspend_delay(dev, 3000); +- pm_runtime_use_autosuspend(dev); +- pm_runtime_mark_last_busy(dev); +- pm_runtime_set_active(dev); +- pm_runtime_enable(dev); ++ goto err_rpm_put; + + ret = wsa_macro_register_mclk_output(wsa); + if (ret) +- goto err_clkout; ++ goto err_rpm_put; ++ ++ ret = pm_runtime_put_autosuspend(dev); ++ if (ret < 0) ++ dev_warn(dev, "runtime PM put failed after probe: %d\n", ret); + + return 0; +- +-err_clkout: +- clk_disable_unprepare(wsa->fsgen); +-err_fsgen: +- clk_disable_unprepare(wsa->npl); +-err_npl: +- clk_disable_unprepare(wsa->mclk); +-err_mclk: +- clk_disable_unprepare(wsa->dcodec); +-err_dcodec: +- clk_disable_unprepare(wsa->macro); +-err: ++err_rpm_put: ++ if (pm_runtime_put_sync_suspend(dev) < 0) ++ dev_warn(dev, "runtime PM sync suspend failed in probe unwind\n"); + return ret; +- +-} +- +-static void wsa_macro_remove(struct platform_device *pdev) +-{ +- struct wsa_macro *wsa = dev_get_drvdata(&pdev->dev); +- +- clk_disable_unprepare(wsa->macro); +- clk_disable_unprepare(wsa->dcodec); +- clk_disable_unprepare(wsa->mclk); +- clk_disable_unprepare(wsa->npl); +- clk_disable_unprepare(wsa->fsgen); + } + + static int wsa_macro_runtime_suspend(struct device *dev) + { + struct wsa_macro *wsa = dev_get_drvdata(dev); ++ int ret; + + regcache_cache_only(wsa->regmap, true); +- regcache_mark_dirty(wsa->regmap); + +- clk_disable_unprepare(wsa->fsgen); +- clk_disable_unprepare(wsa->npl); +- clk_disable_unprepare(wsa->mclk); ++ ret = pm_clk_suspend(dev); ++ if (ret) { ++ regcache_cache_only(wsa->regmap, false); ++ return ret; ++ } ++ ++ regcache_mark_dirty(wsa->regmap); + + return 0; + } +@@ -2866,36 +2852,29 @@ static int wsa_macro_runtime_suspend(struct device *dev) + static int wsa_macro_runtime_resume(struct device *dev) + { + struct wsa_macro *wsa = dev_get_drvdata(dev); +- int ret; ++ int ret, sret; + +- ret = clk_prepare_enable(wsa->mclk); ++ ret = pm_clk_resume(dev); + if (ret) { +- dev_err(dev, "unable to prepare mclk\n"); ++ regcache_cache_only(wsa->regmap, true); ++ regcache_mark_dirty(wsa->regmap); ++ return ret; ++ } ++ regcache_cache_only(wsa->regmap, false); ++ ++ ret = regcache_sync(wsa->regmap); ++ if (ret) { ++ regcache_cache_only(wsa->regmap, true); ++ regcache_mark_dirty(wsa->regmap); ++ sret = pm_clk_suspend(dev); ++ if (sret) ++ dev_err(dev, ++ "failed to suspend clocks after regcache sync failure: %d\n", ++ sret); + return ret; + } + +- ret = clk_prepare_enable(wsa->npl); +- if (ret) { +- dev_err(dev, "unable to prepare mclkx2\n"); +- goto err_npl; +- } +- +- ret = clk_prepare_enable(wsa->fsgen); +- if (ret) { +- dev_err(dev, "unable to prepare fsgen\n"); +- goto err_fsgen; +- } +- +- regcache_cache_only(wsa->regmap, false); +- regcache_sync(wsa->regmap); +- + return 0; +-err_fsgen: +- clk_disable_unprepare(wsa->npl); +-err_npl: +- clk_disable_unprepare(wsa->mclk); +- +- return ret; + } + + static const struct dev_pm_ops wsa_macro_pm_ops = { +@@ -2929,7 +2908,6 @@ static struct platform_driver wsa_macro_driver = { + .pm = pm_ptr(&wsa_macro_pm_ops), + }, + .probe = wsa_macro_probe, +- .remove = wsa_macro_remove, + }; + + module_platform_driver(wsa_macro_driver); +diff --git a/sound/soc/codecs/madera.c b/sound/soc/codecs/madera.c +--- a/sound/soc/codecs/madera.c ++++ b/sound/soc/codecs/madera.c +@@ -6,6 +6,7 @@ + // Cirrus Logic International Semiconductor Ltd. + // + ++#include + #include + #include + #include +@@ -513,7 +514,7 @@ int madera_domain_clk_ev(struct snd_soc_dapm_widget *w, + * We can't rely on the DAPM mutex for locking because we need a lock + * that can safely be called in hw_params + */ +- mutex_lock(&priv->rate_lock); ++ guard(mutex)(&priv->rate_lock); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: +@@ -532,8 +533,6 @@ int madera_domain_clk_ev(struct snd_soc_dapm_widget *w, + + madera_debug_dump_domain_groups(priv); + +- mutex_unlock(&priv->rate_lock); +- + return 0; + } + EXPORT_SYMBOL_GPL(madera_domain_clk_ev); +@@ -875,9 +874,8 @@ static int madera_adsp_rate_get(struct snd_kcontrol *kcontrol, + const int adsp_num = e->shift_l; + int item; + +- mutex_lock(&priv->rate_lock); +- cached_rate = priv->adsp_rate_cache[adsp_num]; +- mutex_unlock(&priv->rate_lock); ++ scoped_guard(mutex, &priv->rate_lock) ++ cached_rate = priv->adsp_rate_cache[adsp_num]; + + item = snd_soc_enum_val_to_item(e, cached_rate); + ucontrol->value.enumerated.item[0] = item; +@@ -893,7 +891,6 @@ static int madera_adsp_rate_put(struct snd_kcontrol *kcontrol, + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + const int adsp_num = e->shift_l; + const unsigned int item = ucontrol->value.enumerated.item[0]; +- int ret = 0; + + if (item >= e->items) + return -EINVAL; +@@ -903,22 +900,20 @@ static int madera_adsp_rate_put(struct snd_kcontrol *kcontrol, + * maintain consistent behaviour that rate domains cannot be changed + * while in use since this is a hardware requirement + */ +- mutex_lock(&priv->rate_lock); ++ guard(mutex)(&priv->rate_lock); + + if (!madera_can_change_grp_rate(priv, priv->adsp[adsp_num].cs_dsp.base)) { + dev_warn(priv->madera->dev, + "Cannot change '%s' while in use by active audio paths\n", + kcontrol->id.name); +- ret = -EBUSY; ++ return -EBUSY; + } else if (priv->adsp_rate_cache[adsp_num] != e->values[item]) { + /* Volatile register so defer until the codec is powered up */ + priv->adsp_rate_cache[adsp_num] = e->values[item]; +- ret = 1; ++ return 1; + } + +- mutex_unlock(&priv->rate_lock); +- +- return ret; ++ return 0; + } + + static const struct soc_enum madera_adsp_rate_enum[] = { +@@ -1061,15 +1056,13 @@ int madera_rate_put(struct snd_kcontrol *kcontrol, + * Prevent the domain powering up while we're checking whether it's + * safe to change rate domain + */ +- mutex_lock(&priv->rate_lock); ++ guard(mutex)(&priv->rate_lock); + + val = snd_soc_component_read(component, e->reg); + val >>= e->shift_l; + val &= e->mask; +- if (snd_soc_enum_item_to_val(e, item) == val) { +- ret = 0; +- goto out; +- } ++ if (snd_soc_enum_item_to_val(e, item) == val) ++ return 0; + + if (!madera_can_change_grp_rate(priv, e->reg)) { + dev_warn(priv->madera->dev, +@@ -1082,8 +1075,6 @@ int madera_rate_put(struct snd_kcontrol *kcontrol, + ret = snd_soc_put_enum_double(kcontrol, ucontrol); + madera_spin_sysclk(priv); + } +-out: +- mutex_unlock(&priv->rate_lock); + + return ret; + } +@@ -3041,12 +3032,11 @@ static int madera_hw_params_rate(struct snd_pcm_substream *substream, + if ((cur & MADERA_AIF1_RATE_MASK) == (tar & MADERA_AIF1_RATE_MASK)) + return 0; + +- mutex_lock(&priv->rate_lock); ++ guard(mutex)(&priv->rate_lock); + + if (!madera_can_change_grp_rate(priv, base + MADERA_AIF_RATE_CTRL)) { + madera_aif_warn(dai, "Cannot change rate while active\n"); +- ret = -EBUSY; +- goto out; ++ return -EBUSY; + } + + /* Guard the rate change with SYSCLK cycles */ +@@ -3055,9 +3045,6 @@ static int madera_hw_params_rate(struct snd_pcm_substream *substream, + MADERA_AIF1_RATE_MASK, tar); + madera_spin_sysclk(priv); + +-out: +- mutex_unlock(&priv->rate_lock); +- + return ret; + } + +@@ -3345,6 +3332,16 @@ static int madera_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + return 0; + } + ++static const u64 madera_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_B | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_NB_IF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_IF; ++ + const struct snd_soc_dai_ops madera_dai_ops = { + .startup = &madera_startup, + .set_fmt = &madera_set_fmt, +@@ -3352,6 +3349,8 @@ const struct snd_soc_dai_ops madera_dai_ops = { + .hw_params = &madera_hw_params, + .set_sysclk = &madera_dai_set_sysclk, + .set_tristate = &madera_set_tristate, ++ .auto_selectable_formats = &madera_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + EXPORT_SYMBOL_GPL(madera_dai_ops); + +diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c +--- a/sound/soc/codecs/max98090.c ++++ b/sound/soc/codecs/max98090.c +@@ -2341,6 +2341,7 @@ static irqreturn_t max98090_interrupt(int irq, void *data) + * + * @component: MAX98090 component + * @jack: jack to report detection events on ++ * @data: can be used if codec driver need extra data for configuring jack + * + * Enable microphone detection via IRQ on the MAX98090. If GPIOs are + * being used to bring out signals to the processor then only platform +@@ -2646,12 +2647,18 @@ static void max98090_i2c_remove(struct i2c_client *client) + static int max98090_runtime_resume(struct device *dev) + { + struct max98090_priv *max98090 = dev_get_drvdata(dev); ++ int ret; + + regcache_cache_only(max98090->regmap, false); + + max98090_reset(max98090); + +- regcache_sync(max98090->regmap); ++ ret = regcache_sync(max98090->regmap); ++ if (ret < 0) { ++ regcache_cache_only(max98090->regmap, true); ++ regcache_mark_dirty(max98090->regmap); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c +--- a/sound/soc/codecs/max98095.c ++++ b/sound/soc/codecs/max98095.c +@@ -5,6 +5,7 @@ + * Copyright 2011 Maxim Integrated Products + */ + ++#include + #include + #include + #include +@@ -1532,15 +1533,17 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol, + regsave = snd_soc_component_read(component, M98095_088_CFG_LEVEL); + snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, 0); + +- mutex_lock(&max98095->lock); +- snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG); +- m98095_eq_band(component, channel, 0, coef_set->band1); +- m98095_eq_band(component, channel, 1, coef_set->band2); +- m98095_eq_band(component, channel, 2, coef_set->band3); +- m98095_eq_band(component, channel, 3, coef_set->band4); +- m98095_eq_band(component, channel, 4, coef_set->band5); +- snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, 0); +- mutex_unlock(&max98095->lock); ++ scoped_guard(mutex, &max98095->lock) { ++ snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, ++ M98095_SEG, M98095_SEG); ++ m98095_eq_band(component, channel, 0, coef_set->band1); ++ m98095_eq_band(component, channel, 1, coef_set->band2); ++ m98095_eq_band(component, channel, 2, coef_set->band3); ++ m98095_eq_band(component, channel, 3, coef_set->band4); ++ m98095_eq_band(component, channel, 4, coef_set->band5); ++ snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, ++ M98095_SEG, 0); ++ } + + /* Restore the original on/off state */ + snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, regsave); +@@ -1683,12 +1686,14 @@ static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol, + regsave = snd_soc_component_read(component, M98095_088_CFG_LEVEL); + snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, 0); + +- mutex_lock(&max98095->lock); +- snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG); +- m98095_biquad_band(component, channel, 0, coef_set->band1); +- m98095_biquad_band(component, channel, 1, coef_set->band2); +- snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, 0); +- mutex_unlock(&max98095->lock); ++ scoped_guard(mutex, &max98095->lock) { ++ snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, ++ M98095_SEG, M98095_SEG); ++ m98095_biquad_band(component, channel, 0, coef_set->band1); ++ m98095_biquad_band(component, channel, 1, coef_set->band2); ++ snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, ++ M98095_SEG, 0); ++ } + + /* Restore the original on/off state */ + snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, regsave); +diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c +--- a/sound/soc/codecs/max98363.c ++++ b/sound/soc/codecs/max98363.c +@@ -100,7 +100,12 @@ static int max98363_resume(struct device *dev) + return ret; + + regcache_cache_only(max98363->regmap, false); +- regcache_sync(max98363->regmap); ++ ret = regcache_sync(max98363->regmap); ++ if (ret) { ++ regcache_cache_only(max98363->regmap, true); ++ regcache_mark_dirty(max98363->regmap); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/max98373-i2c.c b/sound/soc/codecs/max98373-i2c.c +--- a/sound/soc/codecs/max98373-i2c.c ++++ b/sound/soc/codecs/max98373-i2c.c +@@ -488,10 +488,17 @@ static int max98373_suspend(struct device *dev) + static int max98373_resume(struct device *dev) + { + struct max98373_priv *max98373 = dev_get_drvdata(dev); ++ int ret; + + regcache_cache_only(max98373->regmap, false); + max98373_reset(max98373, dev); +- regcache_sync(max98373->regmap); ++ ret = regcache_sync(max98373->regmap); ++ if (ret) { ++ regcache_cache_only(max98373->regmap, true); ++ regcache_mark_dirty(max98373->regmap); ++ return ret; ++ } ++ + return 0; + } + +diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c +--- a/sound/soc/codecs/max98373-sdw.c ++++ b/sound/soc/codecs/max98373-sdw.c +@@ -277,7 +277,12 @@ static int max98373_resume(struct device *dev) + } + + regcache_cache_only(max98373->regmap, false); +- regcache_sync(max98373->regmap); ++ ret = regcache_sync(max98373->regmap); ++ if (ret) { ++ regcache_cache_only(max98373->regmap, true); ++ regcache_mark_dirty(max98373->regmap); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/max98388.c b/sound/soc/codecs/max98388.c +--- a/sound/soc/codecs/max98388.c ++++ b/sound/soc/codecs/max98388.c +@@ -863,10 +863,16 @@ static int max98388_suspend(struct device *dev) + static int max98388_resume(struct device *dev) + { + struct max98388_priv *max98388 = dev_get_drvdata(dev); ++ int ret; + + regcache_cache_only(max98388->regmap, false); + max98388_reset(max98388, dev); +- regcache_sync(max98388->regmap); ++ ret = regcache_sync(max98388->regmap); ++ if (ret) { ++ regcache_cache_only(max98388->regmap, true); ++ regcache_mark_dirty(max98388->regmap); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c +--- a/sound/soc/codecs/max98390.c ++++ b/sound/soc/codecs/max98390.c +@@ -788,7 +788,6 @@ static int max98390_dsm_init(struct snd_soc_component *component) + const char *vendor, *product; + struct max98390_priv *max98390 = + snd_soc_component_get_drvdata(component); +- const struct firmware *fw; + char *dsm_param; + + vendor = dmi_get_system_info(DMI_SYS_VENDOR); +@@ -805,6 +804,8 @@ static int max98390_dsm_init(struct snd_soc_component *component) + snprintf(filename, sizeof(filename), "%s", + max98390->dsm_param_name); + } ++ ++ const struct firmware *fw __free(firmware) = NULL; + ret = request_firmware(&fw, filename, component->dev); + if (ret) { + ret = request_firmware(&fw, "dsm_param.bin", component->dev); +@@ -812,7 +813,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) + ret = request_firmware(&fw, "dsmparam.bin", + component->dev); + if (ret) +- goto err; ++ return ret; + } + } + +@@ -822,8 +823,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) + if (fw->size < MAX98390_DSM_PARAM_MIN_SIZE) { + dev_err(component->dev, + "param fw is invalid.\n"); +- ret = -EINVAL; +- goto err_alloc; ++ return -EINVAL; + } + dsm_param = (char *)fw->data; + param_start_addr = (dsm_param[0] & 0xff) | (dsm_param[1] & 0xff) << 8; +@@ -833,8 +833,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) + fw->size < param_size + MAX98390_DSM_PAYLOAD_OFFSET) { + dev_err(component->dev, + "param fw is invalid.\n"); +- ret = -EINVAL; +- goto err_alloc; ++ return -EINVAL; + } + regmap_write(max98390->regmap, MAX98390_R203A_AMP_EN, 0x80); + dsm_param += MAX98390_DSM_PAYLOAD_OFFSET; +@@ -842,10 +841,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) + dsm_param, param_size); + regmap_write(max98390->regmap, MAX98390_R23E1_DSP_GLOBAL_EN, 0x01); + +-err_alloc: +- release_firmware(fw); +-err: +- return ret; ++ return 0; + } + + static void max98390_init_regs(struct snd_soc_component *component) +@@ -952,11 +948,17 @@ static int max98390_suspend(struct device *dev) + static int max98390_resume(struct device *dev) + { + struct max98390_priv *max98390 = dev_get_drvdata(dev); ++ int ret; + + dev_dbg(dev, "%s:Enter\n", __func__); + + regcache_cache_only(max98390->regmap, false); +- regcache_sync(max98390->regmap); ++ ret = regcache_sync(max98390->regmap); ++ if (ret) { ++ regcache_cache_only(max98390->regmap, true); ++ regcache_mark_dirty(max98390->regmap); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c +--- a/sound/soc/codecs/max98396.c ++++ b/sound/soc/codecs/max98396.c +@@ -1600,19 +1600,37 @@ static int max98396_resume(struct device *dev) + if (max98396->pvdd) { + ret = regulator_enable(max98396->pvdd); + if (ret < 0) +- return ret; ++ goto err_core_supplies; + } + + if (max98396->vbat) { + ret = regulator_enable(max98396->vbat); + if (ret < 0) +- return ret; ++ goto err_pvdd; + } + + regcache_cache_only(max98396->regmap, false); + max98396_reset(max98396, dev); +- regcache_sync(max98396->regmap); ++ ret = regcache_sync(max98396->regmap); ++ if (ret < 0) { ++ regcache_cache_only(max98396->regmap, true); ++ regcache_mark_dirty(max98396->regmap); ++ goto err_vbat; ++ } ++ + return 0; ++ ++err_vbat: ++ if (max98396->vbat) ++ regulator_disable(max98396->vbat); ++err_pvdd: ++ if (max98396->pvdd) ++ regulator_disable(max98396->pvdd); ++err_core_supplies: ++ regulator_bulk_disable(MAX98396_NUM_CORE_SUPPLIES, ++ max98396->core_supplies); ++ ++ return ret; + } + + static const struct dev_pm_ops max98396_pm = { +diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c +--- a/sound/soc/codecs/max98927.c ++++ b/sound/soc/codecs/max98927.c +@@ -742,11 +742,17 @@ static int max98927_suspend(struct device *dev) + static int max98927_resume(struct device *dev) + { + struct max98927_priv *max98927 = dev_get_drvdata(dev); ++ int ret; + + regmap_write(max98927->regmap, MAX98927_R0100_SOFT_RESET, + MAX98927_SOFT_RESET); + regcache_cache_only(max98927->regmap, false); +- regcache_sync(max98927->regmap); ++ ret = regcache_sync(max98927->regmap); ++ if (ret) { ++ regcache_cache_only(max98927->regmap, true); ++ regcache_mark_dirty(max98927->regmap); ++ return ret; ++ } + return 0; + } + +diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c +--- a/sound/soc/codecs/msm8916-wcd-analog.c ++++ b/sound/soc/codecs/msm8916-wcd-analog.c +@@ -217,9 +217,11 @@ + #define CDC_A_RX_HPH_BIAS_LDO_OCP (0xf195) + #define CDC_A_RX_HPH_BIAS_CNP (0xf196) + #define CDC_A_RX_HPH_CNP_EN (0xf197) ++#define CDC_A_RX_HPH_L_TEST (0xf19A) + #define CDC_A_RX_HPH_L_PA_DAC_CTL (0xf19B) + #define RX_HPA_L_PA_DAC_CTL_DATA_RESET_MASK BIT(1) + #define RX_HPA_L_PA_DAC_CTL_DATA_RESET_RESET BIT(1) ++#define CDC_A_RX_HPH_R_TEST (0xf19C) + #define CDC_A_RX_HPH_R_PA_DAC_CTL (0xf19D) + #define RX_HPH_R_PA_DAC_CTL_DATA_RESET BIT(1) + #define RX_HPH_R_PA_DAC_CTL_DATA_RESET_MASK BIT(1) +@@ -232,6 +234,8 @@ + #define RX_EAR_CTL_PA_SEL_MASK BIT(7) + #define RX_EAR_CTL_PA_SEL BIT(7) + ++#define CDC_A_RX_EAR_STATUS (0xf1A1) ++ + #define CDC_A_SPKR_DAC_CTL (0xf1B0) + #define SPKR_DAC_CTL_DAC_RESET_MASK BIT(4) + #define SPKR_DAC_CTL_DAC_RESET_NORMAL 0 +@@ -250,6 +254,7 @@ + SPKR_DRV_CAL_EN | SPKR_DRV_SETTLE_EN | \ + SPKR_DRV_FW_EN | SPKR_DRV_BOOST_SET | \ + SPKR_DRV_CMFB_SET | SPKR_DRV_GAIN_SET) ++#define CDC_A_SPKR_ANA_BIAS_SET (0xf1B3) + #define CDC_A_SPKR_OCP_CTL (0xf1B4) + #define CDC_A_SPKR_PWRSTG_CTL (0xf1B5) + #define SPKR_PWRSTG_CTL_DAC_EN_MASK BIT(0) +@@ -264,6 +269,7 @@ + + #define CDC_A_SPKR_DRV_DBG (0xf1B7) + #define CDC_A_CURRENT_LIMIT (0xf1C0) ++#define CDC_A_BYPASS_MODE (0xf1C2) + #define CDC_A_BOOST_EN_CTL (0xf1C3) + #define CDC_A_SLOPE_COMP_IP_ZERO (0xf1C4) + #define CDC_A_SEC_ACCESS (0xf1D0) +@@ -286,6 +292,11 @@ static const char * const supply_names[] = { + + #define MBHC_MAX_BUTTONS (5) + ++struct wcd_reg_seq { ++ const struct reg_default *seq; ++ int seq_size; ++}; ++ + struct pm8916_wcd_analog_priv { + u16 pmic_rev; + u16 codec_version; +@@ -696,6 +707,59 @@ static int pm8916_wcd_analog_enable_ear_pa(struct snd_soc_dapm_widget *w, + return 0; + } + ++static int pm8916_wcd_analog_enable_hphl_pa(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, ++ int event) ++{ ++ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); ++ struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev); ++ ++ /* This quirk is not required for revisions prior to CAJON_2_0 */ ++ if (priv->codec_version < 4) ++ return 0; ++ ++ switch (event) { ++ case SND_SOC_DAPM_POST_PMU: ++ usleep_range(7000, 7100); ++ snd_soc_component_update_bits(component, CDC_A_RX_HPH_L_TEST, ++ 0x04, 0x04); ++ break; ++ case SND_SOC_DAPM_POST_PMD: ++ /* wait 20 ms after the digital codec has powered down */ ++ msleep(20); ++ snd_soc_component_update_bits(component, CDC_A_RX_HPH_L_TEST, ++ 0x04, 0x00); ++ break; ++ } ++ return 0; ++} ++ ++static int pm8916_wcd_analog_enable_hphr_pa(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, ++ int event) ++{ ++ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); ++ struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev); ++ ++ /* This quirk is not required for revisions prior to CAJON_2_0 */ ++ if (priv->codec_version < 4) ++ return 0; ++ ++ switch (event) { ++ case SND_SOC_DAPM_POST_PMU: ++ usleep_range(7000, 7100); ++ snd_soc_component_update_bits(component, CDC_A_RX_HPH_R_TEST, ++ 0x04, 0x04); ++ break; ++ case SND_SOC_DAPM_POST_PMD: ++ msleep(20); ++ snd_soc_component_update_bits(component, CDC_A_RX_HPH_R_TEST, ++ 0x04, 0x00); ++ break; ++ } ++ return 0; ++} ++ + static const struct reg_default wcd_reg_defaults_2_0[] = { + {CDC_A_RX_COM_OCP_CTL, 0xD1}, + {CDC_A_RX_COM_OCP_COUNT, 0xFF}, +@@ -715,9 +779,69 @@ static const struct reg_default wcd_reg_defaults_2_0[] = { + {CDC_A_MASTER_BIAS_CTL, 0x30}, + }; + ++static const struct wcd_reg_seq pm8916_data = { ++ .seq = wcd_reg_defaults_2_0, ++ .seq_size = ARRAY_SIZE(wcd_reg_defaults_2_0), ++}; ++ ++static const struct reg_default wcd_reg_defaults_pm8950[] = { ++ {CDC_A_RX_COM_OCP_CTL, 0xd1}, ++ {CDC_A_RX_COM_OCP_COUNT, 0xff}, ++ {CDC_D_SEC_ACCESS, 0xa5}, ++ {CDC_D_PERPH_RESET_CTL3, 0x0f}, ++ {CDC_A_TX_1_2_OPAMP_BIAS, 0x4c}, ++ {CDC_A_NCP_FBCTRL, 0xa8}, ++ {CDC_A_NCP_VCTRL, 0xa4}, ++ {CDC_A_SPKR_DRV_CTL, 0x69}, ++ {CDC_A_SPKR_DRV_DBG, 0x01}, ++ {CDC_A_SEC_ACCESS, 0xa5}, ++ {CDC_A_PERPH_RESET_CTL3, 0x0f}, ++ {CDC_A_CURRENT_LIMIT, 0x82}, ++ {CDC_A_SPKR_ANA_BIAS_SET, 0x41}, ++ {CDC_A_SPKR_DAC_CTL, 0x03}, ++ {CDC_A_SPKR_OCP_CTL, 0xe1}, ++ {CDC_A_RX_HPH_BIAS_PA, 0xfa}, ++ {CDC_A_MASTER_BIAS_CTL, 0x30}, ++ {CDC_A_MICB_1_INT_RBIAS, 0x00}, ++}; ++ ++static const struct wcd_reg_seq pm8950_data = { ++ .seq = wcd_reg_defaults_pm8950, ++ .seq_size = ARRAY_SIZE(wcd_reg_defaults_pm8950), ++}; ++ ++static const struct reg_default wcd_reg_defaults_pm8953[] = { ++ {CDC_A_RX_COM_OCP_CTL, 0xd1}, ++ {CDC_A_RX_COM_OCP_COUNT, 0xff}, ++ {CDC_D_SEC_ACCESS, 0xa5}, ++ {CDC_D_PERPH_RESET_CTL3, 0x0f}, ++ {CDC_A_TX_1_2_OPAMP_BIAS, 0x4c}, ++ {CDC_A_NCP_FBCTRL, 0xa8}, ++ {CDC_A_NCP_VCTRL, 0xa4}, ++ {CDC_A_SPKR_DRV_CTL, 0x69}, ++ {CDC_A_SPKR_DRV_DBG, 0x01}, ++ {CDC_A_SEC_ACCESS, 0xa5}, ++ {CDC_A_PERPH_RESET_CTL3, 0x0f}, ++ {CDC_A_CURRENT_LIMIT, 0xa2}, ++ {CDC_A_BYPASS_MODE, 0x18}, ++ {CDC_A_SPKR_ANA_BIAS_SET, 0x41}, ++ {CDC_A_SPKR_DAC_CTL, 0x03}, ++ {CDC_A_SPKR_OCP_CTL, 0xe1}, ++ {CDC_A_RX_HPH_BIAS_PA, 0xfa}, ++ {CDC_A_RX_EAR_STATUS, 0x10}, ++ {CDC_A_MASTER_BIAS_CTL, 0x30}, ++ {CDC_A_MICB_1_INT_RBIAS, 0x00}, ++}; ++ ++static const struct wcd_reg_seq pm8953_data = { ++ .seq = wcd_reg_defaults_pm8953, ++ .seq_size = ARRAY_SIZE(wcd_reg_defaults_pm8953), ++}; ++ + static int pm8916_wcd_analog_probe(struct snd_soc_component *component) + { + struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev); ++ const struct wcd_reg_seq *wcd_reg_init_data; + int err, reg; + + err = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); +@@ -738,9 +862,11 @@ static int pm8916_wcd_analog_probe(struct snd_soc_component *component) + snd_soc_component_write(component, CDC_D_PERPH_RESET_CTL4, 0x01); + snd_soc_component_write(component, CDC_A_PERPH_RESET_CTL4, 0x01); + +- for (reg = 0; reg < ARRAY_SIZE(wcd_reg_defaults_2_0); reg++) +- snd_soc_component_write(component, wcd_reg_defaults_2_0[reg].reg, +- wcd_reg_defaults_2_0[reg].def); ++ wcd_reg_init_data = of_device_get_match_data(component->dev); ++ ++ for (reg = 0; reg < wcd_reg_init_data->seq_size; reg++) ++ snd_soc_component_write(component, wcd_reg_init_data->seq[reg].reg, ++ wcd_reg_init_data->seq[reg].def); + + priv->component = component; + +@@ -883,11 +1009,15 @@ static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = { + SND_SOC_DAPM_MUX("EAR_S", SND_SOC_NOPM, 0, 0, &ear_mux), + SND_SOC_DAPM_SUPPLY("EAR CP", CDC_A_NCP_EN, 4, 0, NULL, 0), + +- SND_SOC_DAPM_PGA("HPHL PA", CDC_A_RX_HPH_CNP_EN, 5, 0, NULL, 0), ++ SND_SOC_DAPM_PGA_E("HPHL PA", CDC_A_RX_HPH_CNP_EN, 5, 0, NULL, 0, ++ pm8916_wcd_analog_enable_hphl_pa, ++ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("HPHL", SND_SOC_NOPM, 0, 0, &hphl_mux), + SND_SOC_DAPM_MIXER("HPHL DAC", CDC_A_RX_HPH_L_PA_DAC_CTL, 3, 0, NULL, + 0), +- SND_SOC_DAPM_PGA("HPHR PA", CDC_A_RX_HPH_CNP_EN, 4, 0, NULL, 0), ++ SND_SOC_DAPM_PGA_E("HPHR PA", CDC_A_RX_HPH_CNP_EN, 4, 0, NULL, 0, ++ pm8916_wcd_analog_enable_hphr_pa, ++ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("HPHR", SND_SOC_NOPM, 0, 0, &hphr_mux), + SND_SOC_DAPM_MIXER("HPHR DAC", CDC_A_RX_HPH_R_PA_DAC_CTL, 3, 0, NULL, + 0), +@@ -1259,7 +1389,9 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev) + } + + static const struct of_device_id pm8916_wcd_analog_spmi_match_table[] = { +- { .compatible = "qcom,pm8916-wcd-analog-codec", }, ++ { .compatible = "qcom,pm8916-wcd-analog-codec", .data = &pm8916_data }, ++ { .compatible = "qcom,pm8950-wcd-analog-codec", .data = &pm8950_data }, ++ { .compatible = "qcom,pm8953-wcd-analog-codec", .data = &pm8953_data }, + { } + }; + +diff --git a/sound/soc/codecs/mt6357.c b/sound/soc/codecs/mt6357.c +--- a/sound/soc/codecs/mt6357.c ++++ b/sound/soc/codecs/mt6357.c +@@ -1834,8 +1834,8 @@ static int mt6357_platform_driver_probe(struct platform_device *pdev) + } + + static const struct platform_device_id mt6357_platform_ids[] = { +- {"mt6357-sound", 0}, +- { /* sentinel */ }, ++ { .name = "mt6357-sound" }, ++ { /* sentinel */ } + }; + MODULE_DEVICE_TABLE(platform, mt6357_platform_ids); + +diff --git a/sound/soc/codecs/mt6359-accdet.c b/sound/soc/codecs/mt6359-accdet.c +--- a/sound/soc/codecs/mt6359-accdet.c ++++ b/sound/soc/codecs/mt6359-accdet.c +@@ -6,6 +6,7 @@ + // Author: Argus Lin + // + ++#include + #include + #include + #include +@@ -398,14 +399,13 @@ static void mt6359_accdet_work(struct work_struct *work) + struct mt6359_accdet *priv = + container_of(work, struct mt6359_accdet, accdet_work); + +- mutex_lock(&priv->res_lock); ++ guard(mutex)(&priv->res_lock); + priv->pre_accdet_status = priv->accdet_status; + check_jack_btn_type(priv); + + if (priv->jack_plugged && + priv->pre_accdet_status != priv->accdet_status) + mt6359_accdet_jack_report(priv); +- mutex_unlock(&priv->res_lock); + } + + static void mt6359_accdet_jd_work(struct work_struct *work) +@@ -416,7 +416,7 @@ static void mt6359_accdet_jd_work(struct work_struct *work) + struct mt6359_accdet *priv = + container_of(work, struct mt6359_accdet, jd_work); + +- mutex_lock(&priv->res_lock); ++ guard(mutex)(&priv->res_lock); + if (priv->jd_sts == M_PLUG_IN) { + priv->jack_plugged = true; + +@@ -450,7 +450,6 @@ static void mt6359_accdet_jd_work(struct work_struct *work) + + if (priv->caps & ACCDET_PMIC_EINT_IRQ) + recover_eint_setting(priv); +- mutex_unlock(&priv->res_lock); + } + + static irqreturn_t mt6359_accdet_irq(int irq, void *data) +@@ -459,7 +458,7 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) + unsigned int irq_val = 0, val = 0, value = 0; + int ret; + +- mutex_lock(&priv->res_lock); ++ guard(mutex)(&priv->res_lock); + regmap_read(priv->regmap, ACCDET_IRQ_ADDR, &irq_val); + + if (irq_val & ACCDET_IRQ_MASK_SFT) { +@@ -474,7 +473,6 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) + 1000); + if (ret) { + dev_err(priv->dev, "%s(), ret %d\n", __func__, ret); +- mutex_unlock(&priv->res_lock); + return IRQ_NONE; + } + regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, +@@ -498,7 +496,6 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) + if (ret) { + dev_err(priv->dev, "%s(), ret %d\n", __func__, + ret); +- mutex_unlock(&priv->res_lock); + return IRQ_NONE; + } + regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, +@@ -521,7 +518,6 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) + if (ret) { + dev_err(priv->dev, "%s(), ret %d\n", __func__, + ret); +- mutex_unlock(&priv->res_lock); + return IRQ_NONE; + } + regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, +@@ -540,7 +536,6 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) + + queue_work(priv->jd_workqueue, &priv->jd_work); + } +- mutex_unlock(&priv->res_lock); + + return IRQ_HANDLED; + } +diff --git a/sound/soc/codecs/ntpfw.c b/sound/soc/codecs/ntpfw.c +--- a/sound/soc/codecs/ntpfw.c ++++ b/sound/soc/codecs/ntpfw.c +@@ -89,7 +89,7 @@ int ntpfw_load(struct i2c_client *i2c, const char *name, u32 magic) + { + struct device *dev = &i2c->dev; + const struct ntpfw_chunk *chunk; +- const struct firmware *fw; ++ const struct firmware *fw __free(firmware) = NULL; + const u8 *data; + size_t leftover; + int ret; +@@ -101,10 +101,8 @@ int ntpfw_load(struct i2c_client *i2c, const char *name, u32 magic) + return ret; + } + +- if (!ntpfw_verify(dev, fw->data, fw->size, magic)) { +- ret = -EINVAL; +- goto done; +- } ++ if (!ntpfw_verify(dev, fw->data, fw->size, magic)) ++ return -EINVAL; + + data = fw->data + sizeof(struct ntpfw_header); + leftover = fw->size - sizeof(struct ntpfw_header); +@@ -112,23 +110,18 @@ int ntpfw_load(struct i2c_client *i2c, const char *name, u32 magic) + while (leftover) { + chunk = (struct ntpfw_chunk *)data; + +- if (!ntpfw_verify_chunk(dev, chunk, leftover)) { +- ret = -EINVAL; +- goto done; +- } ++ if (!ntpfw_verify_chunk(dev, chunk, leftover)) ++ return -EINVAL; + + ret = ntpfw_send_chunk(i2c, chunk); + if (ret) +- goto done; ++ return ret; + + data += be16_to_cpu(chunk->length) + sizeof(*chunk); + leftover -= be16_to_cpu(chunk->length) + sizeof(*chunk); + } + +-done: +- release_firmware(fw); +- +- return ret; ++ return 0; + } + EXPORT_SYMBOL_GPL(ntpfw_load); + +diff --git a/sound/soc/codecs/ntpfw.h b/sound/soc/codecs/ntpfw.h +--- a/sound/soc/codecs/ntpfw.h ++++ b/sound/soc/codecs/ntpfw.h +@@ -1,5 +1,5 @@ + /* SPDX-License-Identifier: GPL-2.0-only */ +-/** ++/* + * ntpfw.h - Firmware helper functions for Neofidelity codecs + * + * Copyright (c) 2024, SaluteDevices. All Rights Reserved. +@@ -13,10 +13,11 @@ + /** + * ntpfw_load - load firmware to amplifier over i2c interface. + * +- * @i2c Pointer to amplifier's I2C client. +- * @name Firmware file name. +- * @magic Magic number to validate firmware. +- * @return 0 or error code upon error. ++ * @i2c: Pointer to amplifier's I2C client. ++ * @name: Firmware file name. ++ * @magic: Magic number to validate firmware. ++ * ++ * Returns: 0 or error code upon error. + */ + int ntpfw_load(struct i2c_client *i2c, const char *name, const u32 magic); + +diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c +--- a/sound/soc/codecs/pcm1681.c ++++ b/sound/soc/codecs/pcm1681.c +@@ -199,10 +199,17 @@ static int pcm1681_hw_params(struct snd_pcm_substream *substream, + return pcm1681_set_deemph(component); + } + ++static const u64 pcm1681_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J; ++ + static const struct snd_soc_dai_ops pcm1681_dai_ops = { + .set_fmt = pcm1681_set_dai_fmt, + .hw_params = pcm1681_hw_params, + .mute_stream = pcm1681_mute, ++ .auto_selectable_formats = &pcm1681_selectable_formats, ++ .num_auto_selectable_formats = 1, + .no_capture_mute = 1, + }; + +diff --git a/sound/soc/codecs/pcm1754.c b/sound/soc/codecs/pcm1754.c +--- a/sound/soc/codecs/pcm1754.c ++++ b/sound/soc/codecs/pcm1754.c +@@ -78,10 +78,16 @@ static int pcm1754_mute_stream(struct snd_soc_dai *dai, int mute, int stream) + return 0; + } + ++static const u64 pcm1754_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J; ++ + static const struct snd_soc_dai_ops pcm1754_dai_ops = { + .set_fmt = pcm1754_set_dai_fmt, + .hw_params = pcm1754_hw_params, + .mute_stream = pcm1754_mute_stream, ++ .auto_selectable_formats = &pcm1754_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static const struct snd_soc_dai_driver pcm1754_dai = { +diff --git a/sound/soc/codecs/pcm1789.c b/sound/soc/codecs/pcm1789.c +--- a/sound/soc/codecs/pcm1789.c ++++ b/sound/soc/codecs/pcm1789.c +@@ -164,11 +164,18 @@ static int pcm1789_trigger(struct snd_pcm_substream *substream, int cmd, + return ret; + } + ++static const u64 pcm1789_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J; ++ + static const struct snd_soc_dai_ops pcm1789_dai_ops = { + .set_fmt = pcm1789_set_dai_fmt, + .hw_params = pcm1789_hw_params, + .mute_stream = pcm1789_mute, + .trigger = pcm1789_trigger, ++ .auto_selectable_formats = &pcm1789_selectable_formats, ++ .num_auto_selectable_formats = 1, + .no_capture_mute = 1, + }; + +diff --git a/sound/soc/codecs/pcm179x.c b/sound/soc/codecs/pcm179x.c +--- a/sound/soc/codecs/pcm179x.c ++++ b/sound/soc/codecs/pcm179x.c +@@ -142,10 +142,16 @@ static int pcm179x_hw_params(struct snd_pcm_substream *substream, + return 0; + } + ++static const u64 pcm179x_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J; ++ + static const struct snd_soc_dai_ops pcm179x_dai_ops = { + .set_fmt = pcm179x_set_dai_fmt, + .hw_params = pcm179x_hw_params, + .mute_stream = pcm179x_mute, ++ .auto_selectable_formats = &pcm179x_selectable_formats, ++ .num_auto_selectable_formats = 1, + .no_capture_mute = 1, + }; + +diff --git a/sound/soc/codecs/pcm186x.c b/sound/soc/codecs/pcm186x.c +--- a/sound/soc/codecs/pcm186x.c ++++ b/sound/soc/codecs/pcm186x.c +@@ -473,11 +473,20 @@ static int pcm186x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + return 0; + } + ++static const u64 pcm186x_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_B | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF; ++ + static const struct snd_soc_dai_ops pcm186x_dai_ops = { + .set_sysclk = pcm186x_set_dai_sysclk, + .set_tdm_slot = pcm186x_set_tdm_slot, + .set_fmt = pcm186x_set_fmt, + .hw_params = pcm186x_hw_params, ++ .auto_selectable_formats = &pcm186x_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static struct snd_soc_dai_driver pcm1863_dai = { +diff --git a/sound/soc/codecs/pcm3060.c b/sound/soc/codecs/pcm3060.c +--- a/sound/soc/codecs/pcm3060.c ++++ b/sound/soc/codecs/pcm3060.c +@@ -164,10 +164,18 @@ static int pcm3060_hw_params(struct snd_pcm_substream *substream, + return 0; + } + ++static const u64 pcm3060_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF; ++ + static const struct snd_soc_dai_ops pcm3060_dai_ops = { + .set_sysclk = pcm3060_set_sysclk, + .set_fmt = pcm3060_set_fmt, + .hw_params = pcm3060_hw_params, ++ .auto_selectable_formats = &pcm3060_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + #define PCM3060_DAI_RATES_ADC (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | \ +diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c +--- a/sound/soc/codecs/pcm512x.c ++++ b/sound/soc/codecs/pcm512x.c +@@ -6,7 +6,7 @@ + * Copyright 2014 Linaro Ltd + */ + +- ++#include + #include + #include + #include +@@ -399,10 +399,9 @@ static int pcm512x_digital_playback_switch_get(struct snd_kcontrol *kcontrol, + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + +- mutex_lock(&pcm512x->mutex); ++ guard(mutex)(&pcm512x->mutex); + ucontrol->value.integer.value[0] = !(pcm512x->mute & 0x4); + ucontrol->value.integer.value[1] = !(pcm512x->mute & 0x2); +- mutex_unlock(&pcm512x->mutex); + + return 0; + } +@@ -414,7 +413,7 @@ static int pcm512x_digital_playback_switch_put(struct snd_kcontrol *kcontrol, + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + int ret, changed = 0; + +- mutex_lock(&pcm512x->mutex); ++ guard(mutex)(&pcm512x->mutex); + + if ((pcm512x->mute & 0x4) == (ucontrol->value.integer.value[0] << 2)) { + pcm512x->mute ^= 0x4; +@@ -430,13 +429,10 @@ static int pcm512x_digital_playback_switch_put(struct snd_kcontrol *kcontrol, + if (ret != 0) { + dev_err(component->dev, + "Failed to update digital mute: %d\n", ret); +- mutex_unlock(&pcm512x->mutex); + return ret; + } + } + +- mutex_unlock(&pcm512x->mutex); +- + return changed; + } + +@@ -1465,7 +1461,7 @@ static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) + int ret; + unsigned int mute_det; + +- mutex_lock(&pcm512x->mutex); ++ guard(mutex)(&pcm512x->mutex); + + if (mute) { + pcm512x->mute |= 0x1; +@@ -1475,7 +1471,7 @@ static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) + if (ret != 0) { + dev_err(component->dev, + "Failed to set digital mute: %d\n", ret); +- goto unlock; ++ return ret; + } + + regmap_read_poll_timeout(pcm512x->regmap, +@@ -1488,7 +1484,7 @@ static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) + if (ret != 0) { + dev_err(component->dev, + "Failed to update digital mute: %d\n", ret); +- goto unlock; ++ return ret; + } + + regmap_read_poll_timeout(pcm512x->regmap, +@@ -1499,18 +1495,24 @@ static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) + 200, 10000); + } + +-unlock: +- mutex_unlock(&pcm512x->mutex); +- + return ret; + } + ++static const u64 pcm512x_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_B; ++ + static const struct snd_soc_dai_ops pcm512x_dai_ops = { + .startup = pcm512x_dai_startup, + .hw_params = pcm512x_hw_params, + .set_fmt = pcm512x_set_fmt, + .mute_stream = pcm512x_mute, + .set_bclk_ratio = pcm512x_set_bclk_ratio, ++ .auto_selectable_formats = &pcm512x_selectable_formats, ++ .num_auto_selectable_formats = 1, + .no_capture_mute = 1, + }; + +diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c +--- a/sound/soc/codecs/pcm6240.c ++++ b/sound/soc/codecs/pcm6240.c +@@ -12,6 +12,7 @@ + // Author: Shenghao Ding + // + ++#include + #include + #include + #include +@@ -605,7 +606,7 @@ static int pcmdev_get_volsw(struct snd_kcontrol *kcontrol, + unsigned int reg = mc->reg; + unsigned int val; + +- mutex_lock(&pcm_dev->codec_lock); ++ guard(mutex)(&pcm_dev->codec_lock); + + if (pcm_dev->chip_id == PCM1690) { + ret = pcmdev_dev_read(pcm_dev, dev_no, PCM1690_REG_MODE_CTRL, +@@ -613,18 +614,18 @@ static int pcmdev_get_volsw(struct snd_kcontrol *kcontrol, + if (ret) { + dev_err(pcm_dev->dev, "%s: read mode err=%d\n", + __func__, ret); +- goto out; ++ return ret; + } + val &= PCM1690_REG_MODE_CTRL_DAMS_MSK; + /* Set to wide-range mode, before using vol ctrl. */ + if (!val && vol_ctrl_type == PCMDEV_PCM1690_VOL_CTRL) { + ucontrol->value.integer.value[0] = -25500; +- goto out; ++ return ret; + } + /* Set to fine mode, before using fine vol ctrl. */ + if (val && vol_ctrl_type == PCMDEV_PCM1690_FINE_VOL_CTRL) { + ucontrol->value.integer.value[0] = -12750; +- goto out; ++ return ret; + } + } + +@@ -632,15 +633,14 @@ static int pcmdev_get_volsw(struct snd_kcontrol *kcontrol, + if (ret) { + dev_err(pcm_dev->dev, "%s: read err=%d\n", + __func__, ret); +- goto out; ++ return ret; + } + + val = (val >> shift) & mask; + val = (val > max) ? max : val; + val = mc->invert ? max - val : val; + ucontrol->value.integer.value[0] = val; +-out: +- mutex_unlock(&pcm_dev->codec_lock); ++ + return ret; + } + +@@ -678,7 +678,7 @@ static int pcmdev_put_volsw(struct snd_kcontrol *kcontrol, + unsigned int val, val_mask; + unsigned int reg = mc->reg; + +- mutex_lock(&pcm_dev->codec_lock); ++ guard(mutex)(&pcm_dev->codec_lock); + val = ucontrol->value.integer.value[0] & mask; + val = (val > max) ? max : val; + val = mc->invert ? max - val : val; +@@ -702,7 +702,7 @@ static int pcmdev_put_volsw(struct snd_kcontrol *kcontrol, + __func__, rc); + else + rc = 1; +- mutex_unlock(&pcm_dev->codec_lock); ++ + return rc; + } + +@@ -1577,10 +1577,10 @@ static int pcmdevice_comp_probe(struct snd_soc_component *comp) + { + struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(comp); + struct i2c_adapter *adap = pcm_dev->client->adapter; +- const struct firmware *fw_entry = NULL; ++ const struct firmware *fw_entry __free(firmware) = NULL; + int ret, i, j; + +- mutex_lock(&pcm_dev->codec_lock); ++ guard(mutex)(&pcm_dev->codec_lock); + + pcm_dev->component = comp; + +@@ -1588,7 +1588,7 @@ static int pcmdevice_comp_probe(struct snd_soc_component *comp) + for (j = 0; j < 2; j++) { + ret = pcmdev_gain_ctrl_add(pcm_dev, i, j); + if (ret < 0) +- goto out; ++ return ret; + } + } + +@@ -1621,21 +1621,17 @@ static int pcmdevice_comp_probe(struct snd_soc_component *comp) + if (ret) { + dev_err(pcm_dev->dev, "%s: request %s err = %d\n", __func__, + pcm_dev->bin_name, ret); +- goto out; ++ return ret; + } + + ret = pcmdev_regbin_ready(fw_entry, pcm_dev); + if (ret) { + dev_err(pcm_dev->dev, "%s: %s parse err = %d\n", __func__, + pcm_dev->bin_name, ret); +- goto out; ++ return ret; + } +- ret = pcmdev_profile_ctrl_add(pcm_dev); +-out: +- release_firmware(fw_entry); + +- mutex_unlock(&pcm_dev->codec_lock); +- return ret; ++ return pcmdev_profile_ctrl_add(pcm_dev); + } + + +@@ -1645,9 +1641,8 @@ static void pcmdevice_comp_remove(struct snd_soc_component *codec) + + if (!pcm_dev) + return; +- mutex_lock(&pcm_dev->codec_lock); ++ guard(mutex)(&pcm_dev->codec_lock); + pcmdevice_config_info_remove(pcm_dev); +- mutex_unlock(&pcm_dev->codec_lock); + } + + static const struct snd_soc_dapm_widget pcmdevice_dapm_widgets[] = { +@@ -1890,9 +1885,9 @@ static int pcmdevice_mute(struct snd_soc_dai *dai, int mute, int stream) + else + block_type = PCMDEVICE_BIN_BLK_PRE_POWER_UP; + +- mutex_lock(&pcm_dev->codec_lock); ++ guard(mutex)(&pcm_dev->codec_lock); + pcmdevice_select_cfg_blk(pcm_dev, pcm_dev->cur_conf, block_type); +- mutex_unlock(&pcm_dev->codec_lock); ++ + return 0; + } + +diff --git a/sound/soc/codecs/peb2466.c b/sound/soc/codecs/peb2466.c +--- a/sound/soc/codecs/peb2466.c ++++ b/sound/soc/codecs/peb2466.c +@@ -6,6 +6,7 @@ + // + // Author: Herve Codina + ++#include + #include + #include + #include +@@ -1537,17 +1538,14 @@ static int peb2466_fw_parse(struct snd_soc_component *component, + + static int peb2466_load_coeffs(struct snd_soc_component *component, const char *fw_name) + { +- const struct firmware *fw; ++ const struct firmware *fw __free(firmware) = NULL; + int ret; + + ret = request_firmware(&fw, fw_name, component->dev); + if (ret) + return ret; + +- ret = peb2466_fw_parse(component, fw->data, fw->size); +- release_firmware(fw); +- +- return ret; ++ return peb2466_fw_parse(component, fw->data, fw->size); + } + + static int peb2466_component_probe(struct snd_soc_component *component) +@@ -1704,13 +1702,11 @@ static int peb2466_chip_gpio_update_bits(struct peb2466 *peb2466, unsigned int x + * So, a specific cache value is used. + */ + +- mutex_lock(&peb2466->gpio.lock); ++ guard(mutex)(&peb2466->gpio.lock); + + cache = peb2466_chip_gpio_get_cache(peb2466, xr_reg); +- if (!cache) { +- ret = -EINVAL; +- goto end; +- } ++ if (!cache) ++ return -EINVAL; + + tmp = *cache; + tmp &= ~mask; +@@ -1718,14 +1714,11 @@ static int peb2466_chip_gpio_update_bits(struct peb2466 *peb2466, unsigned int x + + ret = regmap_write(peb2466->regmap, xr_reg, tmp); + if (ret) +- goto end; ++ return ret; + + *cache = tmp; +- ret = 0; + +-end: +- mutex_unlock(&peb2466->gpio.lock); +- return ret; ++ return 0; + } + + static int peb2466_chip_gpio_set(struct gpio_chip *c, unsigned int offset, +diff --git a/sound/soc/codecs/pm4125-sdw.c b/sound/soc/codecs/pm4125-sdw.c +--- a/sound/soc/codecs/pm4125-sdw.c ++++ b/sound/soc/codecs/pm4125-sdw.c +@@ -464,10 +464,16 @@ static int __maybe_unused pm4125_sdw_runtime_suspend(struct device *dev) + static int __maybe_unused pm4125_sdw_runtime_resume(struct device *dev) + { + struct pm4125_sdw_priv *priv = dev_get_drvdata(dev); ++ int ret; + + if (priv->regmap) { + regcache_cache_only(priv->regmap, false); +- regcache_sync(priv->regmap); ++ ret = regcache_sync(priv->regmap); ++ if (ret) { ++ regcache_cache_only(priv->regmap, true); ++ regcache_mark_dirty(priv->regmap); ++ return ret; ++ } + } + + return 0; +diff --git a/sound/soc/codecs/pm4125.c b/sound/soc/codecs/pm4125.c +--- a/sound/soc/codecs/pm4125.c ++++ b/sound/soc/codecs/pm4125.c +@@ -5,6 +5,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1295,7 +1296,7 @@ static const struct irq_domain_ops pm4125_domain_ops = { + + static int pm4125_irq_init(struct pm4125_priv *pm4125, struct device *dev) + { +- pm4125->virq = irq_domain_add_linear(NULL, 1, &pm4125_domain_ops, NULL); ++ pm4125->virq = irq_domain_create_linear(NULL, 1, &pm4125_domain_ops, NULL); + if (!(pm4125->virq)) { + dev_err(dev, "%s: Failed to add IRQ domain\n", __func__); + return -EINVAL; +diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c +--- a/sound/soc/codecs/rt1017-sdca-sdw.c ++++ b/sound/soc/codecs/rt1017-sdca-sdw.c +@@ -784,7 +784,12 @@ static int rt1017_sdca_dev_resume(struct device *dev) + } + + regcache_cache_only(rt1017->regmap, false); +- regcache_sync(rt1017->regmap); ++ ret = regcache_sync(rt1017->regmap); ++ if (ret) { ++ regcache_cache_only(rt1017->regmap, true); ++ regcache_mark_dirty(rt1017->regmap); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c +--- a/sound/soc/codecs/rt1316-sdw.c ++++ b/sound/soc/codecs/rt1316-sdw.c +@@ -756,7 +756,12 @@ static int rt1316_dev_resume(struct device *dev) + } + + regcache_cache_only(rt1316->regmap, false); +- regcache_sync(rt1316->regmap); ++ ret = regcache_sync(rt1316->regmap); ++ if (ret) { ++ regcache_cache_only(rt1316->regmap, true); ++ regcache_mark_dirty(rt1316->regmap); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c +--- a/sound/soc/codecs/rt1318-sdw.c ++++ b/sound/soc/codecs/rt1318-sdw.c +@@ -830,7 +830,12 @@ static int rt1318_dev_resume(struct device *dev) + return ret; + + regcache_cache_only(rt1318->regmap, false); +- regcache_sync(rt1318->regmap); ++ ret = regcache_sync(rt1318->regmap); ++ if (ret) { ++ regcache_cache_only(rt1318->regmap, true); ++ regcache_mark_dirty(rt1318->regmap); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c +--- a/sound/soc/codecs/rt1320-sdw.c ++++ b/sound/soc/codecs/rt1320-sdw.c +@@ -410,6 +410,402 @@ static const struct reg_sequence rt1321_blind_write[] = { + { 0x41001988, 0x03 }, + }; + ++static const struct reg_sequence rt1321_va1_blind_write[] = { ++ { 0x0000c003, 0xf0 }, ++ { 0x0000c01b, 0xfc }, ++ { 0x0000c5c3, 0xf2 }, ++ { 0x0000c5c2, 0x00 }, ++ { 0x0000c5c1, 0x10 }, ++ { 0x0000c5c0, 0x04 }, ++ { 0x0000c5c7, 0x03 }, ++ { 0x0000c5c6, 0x10 }, ++ { 0x0000c526, 0x47 }, ++ { 0x0000c5c4, 0x12 }, ++ { 0x0000c5c5, 0x60 }, ++ { 0x0000c520, 0x10 }, ++ { 0x0000c521, 0x32 }, ++ { 0x0000c5c7, 0x00 }, ++ { 0x0000c5c8, 0x03 }, ++ { 0x0000c5d3, 0x08 }, ++ { 0x0000c5d2, 0x0a }, ++ { 0x0000c5d1, 0x49 }, ++ { 0x0000c5d0, 0x0f }, ++ { 0x0000c580, 0x10 }, ++ { 0x0000c581, 0x32 }, ++ { 0x0000c582, 0x01 }, ++ { 0x0000c682, 0x60 }, ++ { 0x0000c019, 0x10 }, ++ { 0x0000c5f0, 0x01 }, ++ { 0x0000c5f7, 0x22 }, ++ { 0x0000c5f6, 0x22 }, ++ { 0x0000c057, 0x51 }, ++ { 0x0000c054, 0x55 }, ++ { 0x0000c053, 0x55 }, ++ { 0x0000c052, 0x55 }, ++ { 0x0000c051, 0x01 }, ++ { 0x0000c050, 0x15 }, ++ { 0x0000c060, 0x99 }, ++ { 0x0000c030, 0x55 }, ++ { 0x0000c061, 0x55 }, ++ { 0x0000c063, 0x55 }, ++ { 0x0000c065, 0xa5 }, ++ { 0x0000c06b, 0x0a }, ++ { 0x0000ca05, 0xd6 }, ++ { 0x0000ca06, 0x11 }, ++ { 0x0000ca07, 0x1e }, ++ { 0x0000ca25, 0xd6 }, ++ { 0x0000ca26, 0x11 }, ++ { 0x0000ca27, 0x1e }, ++ { 0x0000cd00, 0x05 }, ++ { 0x0000cd81, 0x49 }, ++ { 0x0000cd82, 0x49 }, ++ { 0x0000c604, 0x40 }, ++ { 0x0000c609, 0x40 }, ++ { 0x0000c046, 0xf7 }, ++ { 0x0000c045, 0xff }, ++ { 0x0000c044, 0xff }, ++ { 0x0000c043, 0xff }, ++ { 0x0000c042, 0xff }, ++ { 0x0000c041, 0xff }, ++ { 0x0000c040, 0xff }, ++ { 0x0000c049, 0xff }, ++ { 0x0000c028, 0x3f }, ++ { 0x0000c020, 0x3f }, ++ { 0x0000c032, 0x13 }, ++ { 0x0000c033, 0x01 }, ++ { 0x0000cc10, 0x01 }, ++ { 0x0000dc20, 0x03 }, ++ { 0x0000de03, 0x05 }, ++ { 0x0000dc00, 0x00 }, ++ { 0x0000c700, 0xf0 }, ++ { 0x0000c701, 0x13 }, ++ { 0x0000c900, 0xc3 }, ++ { 0x0000c570, 0x08 }, ++ { 0x0000c086, 0x02 }, ++ { 0x0000c085, 0x7f }, ++ { 0x0000c084, 0x00 }, ++ { 0x0000c081, 0xff }, ++ { 0x0000f084, 0x0f }, ++ { 0x0000f083, 0xff }, ++ { 0x0000f082, 0xff }, ++ { 0x0000f081, 0xff }, ++ { 0x0000f080, 0xff }, ++ { 0x20003003, 0x3f }, ++ { 0x20005818, 0x81 }, ++ { 0x20009018, 0x81 }, ++ { 0x2000301c, 0x81 }, ++ { 0x0000c003, 0xc0 }, ++ { 0x0000c047, 0x80 }, ++ { 0x0000d541, 0x80 }, ++ { 0x0000d487, 0x0b }, ++ { 0x0000d487, 0x3b }, ++ { 0x0000d486, 0xc3 }, ++ { 0x0000d470, 0x89 }, ++ { 0x0000d471, 0x3a }, ++ { 0x0000d472, 0x3d }, ++ { 0x0000d474, 0x11 }, ++ { 0x0000d475, 0x32 }, ++ { 0x0000d476, 0x64 }, ++ { 0x0000d477, 0x10 }, ++ { 0x0000d478, 0xff }, ++ { 0x0000d479, 0x20 }, ++ { 0x0000d47a, 0x10 }, ++ { 0x0000d73c, 0xb7 }, ++ { 0x0000d73d, 0xd7 }, ++ { 0x0000d73e, 0x00 }, ++ { 0x0000d73f, 0x10 }, ++ { 0x1000cd56, 0x00 }, ++ { 0x3fc2dfc0, 0x03 }, ++ { 0x3fc2dfc1, 0x00 }, ++ { 0x3fc2dfc2, 0x00 }, ++ { 0x3fc2dfc3, 0x00 }, ++ { 0x3fc2dfc4, 0x01 }, ++ { 0x3fc2dfc5, 0x00 }, ++ { 0x3fc2dfc6, 0x00 }, ++ { 0x3fc2dfc7, 0x00 }, ++ { 0x3fc2df80, 0x00 }, ++ { 0x3fc2df81, 0x00 }, ++ { 0x3fc2df82, 0x00 }, ++ { 0x3fc2df83, 0x00 }, ++ { 0x0000d541, 0x40 }, ++ { 0x0000d486, 0x43 }, ++ { 0x1000db00, 0x04 }, ++ { 0x1000db01, 0x00 }, ++ { 0x1000db02, 0x10 }, ++ { 0x1000db03, 0x00 }, ++ { 0x1000db04, 0x00 }, ++ { 0x1000db05, 0x45 }, ++ { 0x1000db06, 0x0d }, ++ { 0x1000db07, 0x01 }, ++ { 0x1000db08, 0x00 }, ++ { 0x1000db09, 0x00 }, ++ { 0x1000db0a, 0xbf }, ++ { 0x1000db0b, 0x0b }, ++ { 0x1000db0c, 0x11 }, ++ { 0x1000db0d, 0x00 }, ++ { 0x1000db0e, 0x00 }, ++ { 0x1000db0f, 0x00 }, ++ { 0x1000db10, 0x2c }, ++ { 0x1000db11, 0xfa }, ++ { 0x1000db12, 0x00 }, ++ { 0x1000db13, 0x00 }, ++ { 0x1000db14, 0x09 }, ++ { 0x0000d540, 0x21 }, ++ { 0x0000c570, 0x08 }, ++ { 0x0000d714, 0x17 }, ++ { 0x0000c5c3, 0xf2 }, ++ { 0x0000c5c8, 0x03 }, ++ { 0x20009012, 0x00 }, ++ { 0x0000dd08, 0x17 }, ++ { 0x0000dd09, 0x0e }, ++ { 0x0000dd0a, 0x17 }, ++ { 0x0000dd0b, 0x0e }, ++ { 0x0000c570, 0x08 }, ++ { 0x0000d471, 0x3a }, ++ { 0x0000db00, 0x00 }, ++ { 0x0000db01, 0x00 }, ++ { 0x0000db02, 0x73 }, ++ { 0x0000db03, 0x00 }, ++ { 0x0000db04, 0x00 }, ++ { 0x0000db05, 0x00 }, ++ { 0x0000db06, 0x00 }, ++ { 0x0000db07, 0x00 }, ++ { 0x0000db08, 0x7f }, ++ { 0x0000db09, 0x00 }, ++ { 0x0000db1a, 0x00 }, ++ { 0x0000db1b, 0x00 }, ++ { 0x0000db19, 0x00 }, ++}; ++ ++static const struct reg_sequence rt1321_va2_blind_write[] = { ++ { 0x0000c003, 0xf0 }, ++ { 0x0000c01b, 0xfc }, ++ { 0x0000c5c3, 0xf2 }, ++ { 0x0000c5c2, 0x00 }, ++ { 0x0000c5c1, 0x10 }, ++ { 0x0000c5c0, 0x04 }, ++ { 0x0000c5c7, 0x03 }, ++ { 0x0000c5c6, 0x10 }, ++ { 0x0000c526, 0x47 }, ++ { 0x0000c5c4, 0x12 }, ++ { 0x0000c5c5, 0x60 }, ++ { 0x0000c520, 0x10 }, ++ { 0x0000c521, 0x32 }, ++ { 0x0000c5c7, 0x00 }, ++ { 0x0000c5c8, 0x03 }, ++ { 0x0000c5d3, 0x08 }, ++ { 0x0000c5d2, 0x0a }, ++ { 0x0000c5d1, 0x49 }, ++ { 0x0000c5d0, 0x0f }, ++ { 0x0000c580, 0x10 }, ++ { 0x0000c581, 0x32 }, ++ { 0x0000c582, 0x01 }, ++ { 0x0000c682, 0x60 }, ++ { 0x0000c019, 0x10 }, ++ { 0x0000c5f0, 0x01 }, ++ { 0x0000c5f7, 0x22 }, ++ { 0x0000c5f6, 0x22 }, ++ { 0x0000c057, 0x51 }, ++ { 0x0000c054, 0x55 }, ++ { 0x0000c053, 0x55 }, ++ { 0x0000c052, 0x55 }, ++ { 0x0000c051, 0x01 }, ++ { 0x0000c050, 0x15 }, ++ { 0x0000c060, 0x99 }, ++ { 0x0000c030, 0x55 }, ++ { 0x0000c061, 0x55 }, ++ { 0x0000c063, 0x55 }, ++ { 0x0000c065, 0xa5 }, ++ { 0x0000c06b, 0x0a }, ++ { 0x0000ca05, 0xd6 }, ++ { 0x0000ca06, 0x11 }, ++ { 0x0000ca07, 0x1e }, ++ { 0x0000ca25, 0xd6 }, ++ { 0x0000ca26, 0x11 }, ++ { 0x0000ca27, 0x1e }, ++ { 0x0000cd00, 0x05 }, ++ { 0x0000cd81, 0x49 }, ++ { 0x0000cd82, 0x49 }, ++ { 0x0000c604, 0x40 }, ++ { 0x0000c609, 0x40 }, ++ { 0x0000c046, 0xf7 }, ++ { 0x0000c045, 0xff }, ++ { 0x0000c044, 0xff }, ++ { 0x0000c043, 0xff }, ++ { 0x0000c042, 0xff }, ++ { 0x0000c041, 0xff }, ++ { 0x0000c040, 0xff }, ++ { 0x0000c049, 0xff }, ++ { 0x0000c028, 0x3f }, ++ { 0x0000c020, 0x3f }, ++ { 0x0000c032, 0x13 }, ++ { 0x0000c033, 0x01 }, ++ { 0x0000cc10, 0x01 }, ++ { 0x0000dc20, 0x03 }, ++ { 0x0000de03, 0x05 }, ++ { 0x0000dc00, 0x00 }, ++ { 0x0000c700, 0xf0 }, ++ { 0x0000c701, 0x13 }, ++ { 0x0000c900, 0xc3 }, ++ { 0x0000c570, 0x08 }, ++ { 0x0000c086, 0x02 }, ++ { 0x0000c085, 0x7f }, ++ { 0x0000c084, 0x00 }, ++ { 0x0000c081, 0xff }, ++ { 0x0000f084, 0x0f }, ++ { 0x0000f083, 0xff }, ++ { 0x0000f082, 0xff }, ++ { 0x0000f081, 0xff }, ++ { 0x0000f080, 0xff }, ++ { 0x20003003, 0x3f }, ++ { 0x20005818, 0x81 }, ++ { 0x20009018, 0x81 }, ++ { 0x2000301c, 0x81 }, ++ { 0x0000c003, 0xc0 }, ++ { 0x0000c047, 0x80 }, ++ { 0x0000d541, 0x80 }, ++ { 0x0000d487, 0x0b }, ++ { 0x0000d487, 0x3b }, ++ { 0x0000d486, 0xc3 }, ++ { 0x0000d470, 0x89 }, ++ { 0x0000d471, 0x3a }, ++ { 0x0000d472, 0x3d }, ++ { 0x0000d474, 0x11 }, ++ { 0x0000d475, 0x32 }, ++ { 0x0000d476, 0x64 }, ++ { 0x0000d477, 0x10 }, ++ { 0x0000d478, 0xff }, ++ { 0x0000d479, 0x20 }, ++ { 0x0000d47a, 0x10 }, ++ { 0x10008000, 0x67 }, ++ { 0x10008001, 0x80 }, ++ { 0x10008002, 0x00 }, ++ { 0x10008003, 0x00 }, ++ { 0x1000cd56, 0x00 }, ++ { 0x0000d486, 0x43 }, ++ { 0x3fc2dfc3, 0x00 }, ++ { 0x3fc2dfc2, 0x00 }, ++ { 0x3fc2dfc1, 0x00 }, ++ { 0x3fc2dfc0, 0x03 }, ++ { 0x3fc2dfc7, 0x00 }, ++ { 0x3fc2dfc6, 0x00 }, ++ { 0x3fc2dfc5, 0x00 }, ++ { 0x3fc2dfc4, 0x01 }, ++ { 0x3fc2dfa3, 0x00 }, ++ { 0x3fc2dfa2, 0x00 }, ++ { 0x3fc2dfa1, 0x00 }, ++ { 0x3fc2dfa0, 0x00 }, ++ { 0x3fc2df80, 0x10 }, ++ { 0x3fc2df81, 0x20 }, ++ { 0x3fc2df82, 0x00 }, ++ { 0x3fc2df83, 0x00 }, ++ { 0x3fc2df84, 0x50 }, ++ { 0x3fc2df85, 0x19 }, ++ { 0x3fc2df86, 0x00 }, ++ { 0x3fc2df87, 0x00 }, ++ { 0x3fc2df88, 0x52 }, ++ { 0x3fc2df89, 0x23 }, ++ { 0x3fc2df8a, 0x00 }, ++ { 0x3fc2df8b, 0x00 }, ++ { 0x3fc2df8c, 0xe0 }, ++ { 0x3fc2df8d, 0x2e }, ++ { 0x3fc2df8e, 0x00 }, ++ { 0x3fc2df8f, 0x00 }, ++ { 0x3fc2df90, 0xe0 }, ++ { 0x3fc2df91, 0x2e }, ++ { 0x3fc2df92, 0x00 }, ++ { 0x3fc2df93, 0x00 }, ++ { 0x3fc2df94, 0x01 }, ++ { 0x3fc2df95, 0x08 }, ++ { 0x3fc2df96, 0x00 }, ++ { 0x3fc2df97, 0x00 }, ++ { 0x3fc2df40, 0x80 }, ++ { 0x3fc2df41, 0xbb }, ++ { 0x3fc2df42, 0x00 }, ++ { 0x3fc2df43, 0x00 }, ++ { 0x3fc2df44, 0xc0 }, ++ { 0x3fc2df45, 0x99 }, ++ { 0x3fc2df46, 0x01 }, ++ { 0x3fc2df47, 0x00 }, ++ { 0x3fc2df48, 0x00 }, ++ { 0x3fc2df49, 0x00 }, ++ { 0x3fc2df4a, 0x00 }, ++ { 0x3fc2df4b, 0x00 }, ++ { 0x3fc2df4c, 0x00 }, ++ { 0x3fc2df4d, 0x00 }, ++ { 0x3fc2df4e, 0x00 }, ++ { 0x3fc2df4f, 0x00 }, ++ { 0x3fc2df50, 0x01 }, ++ { 0x3fc2df51, 0x00 }, ++ { 0x3fc2df52, 0x00 }, ++ { 0x3fc2df53, 0x00 }, ++ { 0x3fc2df54, 0x01 }, ++ { 0x3fc2df55, 0x00 }, ++ { 0x3fc2df56, 0x00 }, ++ { 0x3fc2df57, 0x00 }, ++ { 0x3fc2df58, 0x00 }, ++ { 0x3fc2df59, 0x00 }, ++ { 0x3fc2df5a, 0x00 }, ++ { 0x3fc2df5b, 0x00 }, ++ { 0x3fc2df5c, 0x01 }, ++ { 0x3fc2df5d, 0x00 }, ++ { 0x3fc2df5e, 0x00 }, ++ { 0x3fc2df5f, 0x00 }, ++ { 0x3fc2df60, 0x00 }, ++ { 0x3fc2df61, 0x00 }, ++ { 0x3fc2df62, 0x00 }, ++ { 0x3fc2df63, 0x00 }, ++ { 0x3fc2df64, 0x00 }, ++ { 0x3fc2df65, 0x00 }, ++ { 0x3fc2df66, 0x00 }, ++ { 0x3fc2df67, 0x10 }, ++ { 0x3fc2df68, 0x01 }, ++ { 0x3fc2df69, 0x00 }, ++ { 0x3fc2df6a, 0x00 }, ++ { 0x3fc2df6b, 0x00 }, ++ { 0x3fc2df6c, 0x01 }, ++ { 0x3fc2df6d, 0x00 }, ++ { 0x3fc2df6e, 0x00 }, ++ { 0x3fc2df6f, 0x00 }, ++ { 0x3fc2df70, 0x04 }, ++ { 0x3fc2df71, 0x00 }, ++ { 0x3fc2df72, 0x00 }, ++ { 0x3fc2df73, 0x00 }, ++ { 0x3fc2df74, 0x01 }, ++ { 0x3fc2df75, 0x00 }, ++ { 0x3fc2df76, 0x00 }, ++ { 0x3fc2df77, 0x00 }, ++ { 0x1000db00, 0x04 }, ++ { 0x1000db01, 0x00 }, ++ { 0x1000db02, 0x10 }, ++ { 0x1000db03, 0x00 }, ++ { 0x1000db04, 0x00 }, ++ { 0x1000db05, 0x45 }, ++ { 0x1000db06, 0x0d }, ++ { 0x1000db07, 0x01 }, ++ { 0x1000db08, 0x00 }, ++ { 0x1000db09, 0x00 }, ++ { 0x1000db0a, 0xbf }, ++ { 0x1000db0b, 0x0b }, ++ { 0x1000db0c, 0x11 }, ++ { 0x1000db0d, 0x00 }, ++ { 0x1000db0e, 0x00 }, ++ { 0x1000db0f, 0x00 }, ++ { 0x1000db10, 0x2c }, ++ { 0x1000db11, 0xfa }, ++ { 0x1000db12, 0x00 }, ++ { 0x1000db13, 0x00 }, ++ { 0x1000db14, 0x09 }, ++ { 0x0000d540, 0x21 }, ++ { 0x0000d714, 0x17 }, ++ { 0x0000dd0b, 0x0d }, ++ { 0x0000dd0a, 0xff }, ++ { 0x0000dd09, 0x0d }, ++ { 0x0000dd08, 0xff }, ++ { 0x0000c5fb, 0x12 }, ++ { 0x0000c570, 0x08 }, ++}; ++ + static const struct reg_default rt1320_reg_defaults[] = { + { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, +@@ -440,6 +836,7 @@ static const struct reg_default rt1320_mbq_defaults[] = { + static bool rt1320_readable_register(struct device *dev, unsigned int reg) + { + switch (reg) { ++ case 0x004d: + case 0xc000 ... 0xc086: + case 0xc400 ... 0xc409: + case 0xc480 ... 0xc48f: +@@ -497,10 +894,15 @@ static bool rt1320_readable_register(struct device *dev, unsigned int reg) + case RT1321_PATCH_MAIN_VER ... RT1321_PATCH_BETA_VER: + case 0x1000f008: + case 0x1000f021: +- case 0x2000300f: ++ case 0x20003000 ... 0x2000300f: + case 0x2000301c: +- case 0x2000900f: ++ case 0x20003040 ... 0x20003059: ++ case 0x20005800 ... 0x2000580f: ++ case 0x20005818: ++ case 0x20005840 ... 0x20005859: ++ case 0x20009000 ... 0x2000900f: + case 0x20009018: ++ case 0x20009040 ... 0x20009059: + case 0x3fc000c0 ... 0x3fc2dfc8: + case 0x3fe00000 ... 0x3fe36fff: + /* 0x40801508/0x40801809/0x4080180a/0x40801909/0x4080190a */ +@@ -545,6 +947,7 @@ static bool rt1320_readable_register(struct device *dev, unsigned int reg) + static bool rt1320_volatile_register(struct device *dev, unsigned int reg) + { + switch (reg) { ++ case 0x004d: + case 0xc000: + case 0xc003: + case 0xc081: +@@ -596,15 +999,20 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg) + case 0x1000c000 ... 0x1000dfff: + case 0x1000f008: + case 0x1000f021: +- case 0x2000300f: ++ case 0x2000300e ... 0x2000300f: + case 0x2000301c: +- case 0x2000900f: ++ case 0x20003040 ... 0x20003059: ++ case 0x2000580e ... 0x2000580f: ++ case 0x20005818: ++ case 0x20005840 ... 0x20005859: ++ case 0x2000900e ... 0x2000900f: + case 0x20009018: ++ case 0x20009040 ... 0x20009059: + case 0x3fc2ab80 ... 0x3fc2ac4c: + case 0x3fc2b780: + case 0x3fc2bf80 ... 0x3fc2bf83: + case 0x3fc2bfc0 ... 0x3fc2bfc8: +- case 0x3fc2d300 ... 0x3fc2d354: ++ case 0x3fc2d300 ... 0x3fc2d3cc: + case 0x3fc2dfc0 ... 0x3fc2dfc8: + case 0x3fe2e000 ... 0x3fe2e003: + case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0): +@@ -892,6 +1300,42 @@ static int rt1320_check_fw_ready(struct rt1320_sdw_priv *rt1320) + return 0; + } + ++static int rt1320_dspfw_status(struct rt1320_sdw_priv *rt1320) ++{ ++ struct device *dev = &rt1320->sdw_slave->dev; ++ unsigned int fw_status_addr, fw_ready; ++ unsigned int dspfw_run; ++ ++ switch (rt1320->dev_id) { ++ case RT1320_DEV_ID: ++ fw_status_addr = RT1320_DSPFW_STATUS_ADDR; ++ break; ++ case RT1321_DEV_ID: ++ fw_status_addr = RT1321_DSPFW_STATUS_ADDR; ++ break; ++ default: ++ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); ++ return -EINVAL; ++ } ++ ++ regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); ++ fw_ready &= 0x1; ++ ++ if (rt1320->dev_id == RT1321_DEV_ID) { ++ regmap_read(rt1320->regmap, 0xf01e, &dspfw_run); ++ dspfw_run &= 0x1; ++ fw_ready = (!dspfw_run && fw_ready); ++ } ++ ++ if (fw_ready) { ++ dev_dbg(dev, "%s, DSP FW was already\n", __func__); ++ return 1; ++ } ++ ++ dev_dbg(dev, "%s, DSP FW is NOT ready. Please load DSP FW first\n", __func__); ++ return 0; ++} ++ + static int rt1320_check_power_state_ready(struct rt1320_sdw_priv *rt1320, enum rt1320_power_state ps) + { + struct device *dev = &rt1320->sdw_slave->dev; +@@ -982,6 +1426,9 @@ static int rt1320_fw_param_protocol(struct rt1320_sdw_priv *rt1320, enum rt1320_ + if (!tempbuf) + return -ENOMEM; + ++ if (rt1320->dev_id == RT1321_DEV_ID && rt1320->version_id == RT1321_VA2) ++ paramid += 0x0bff0000; ++ + paramhr.moudleid = 1; + paramhr.commandtype = cmdid; + /* 8 is "sizeof(paramid) + sizeof(paramlength)" */ +@@ -1120,45 +1567,73 @@ static void rt1320_calc_r0(struct rt1320_sdw_priv *rt1320) + l_calir0, l_calir0_lo, r_calir0, r_calir0_lo); + } + ++static int rt1320_pilot_tone_output(struct rt1320_sdw_priv *rt1320) ++{ ++ struct device *dev = &rt1320->sdw_slave->dev; ++ int l_targetpostgain, r_targetpostgain; ++ unsigned long long factor = (1 << 12); ++ int l_pilotgain[9], r_pilotgain[9]; ++ const int postgain_step = 234; ++ int targetGain; ++ ++ switch (rt1320->dev_id) { ++ case RT1320_DEV_ID: ++ targetGain = -320000; ++ break; ++ case RT1321_DEV_ID: ++ targetGain = -420000; ++ if (rt1320->version_id == RT1321_VA0) ++ targetGain = -320000; ++ break; ++ default: ++ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); ++ return -EINVAL; ++ } ++ ++ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 70, &l_pilotgain[0], sizeof(l_pilotgain)); ++ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 71, &r_pilotgain[0], sizeof(r_pilotgain)); ++ dev_dbg(dev, "%s, LR pilotgain %d, %d\n", __func__, l_pilotgain[2], r_pilotgain[2]); ++ ++ /* calculate pilot tone gain */ ++ l_pilotgain[2] = (l_pilotgain[2] * 10000) / factor; ++ r_pilotgain[2] = (r_pilotgain[2] * 10000) / factor; ++ ++ /* calculate post gain to meet target gain */ ++ l_targetpostgain = abs(targetGain - l_pilotgain[2]) / postgain_step; ++ r_targetpostgain = abs(targetGain - r_pilotgain[2]) / postgain_step; ++ l_targetpostgain = 0xfff - l_targetpostgain; ++ r_targetpostgain = 0xfff - r_targetpostgain; ++ dev_dbg(dev, "%s, LR targetpostgain=0x%x, 0x%x\n", __func__, l_targetpostgain, r_targetpostgain); ++ ++ regmap_write(rt1320->regmap, 0xdd0b, (l_targetpostgain & 0xf00) >> 8); ++ regmap_write(rt1320->regmap, 0xdd0a, l_targetpostgain & 0xff); ++ regmap_write(rt1320->regmap, 0xdd09, (r_targetpostgain & 0xf00) >> 8); ++ regmap_write(rt1320->regmap, 0xdd08, r_targetpostgain & 0xff); ++ ++ return 0; ++} ++ + static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) + { + struct device *dev = &rt1320->sdw_slave->dev; + struct rt1320_datafixpoint audfixpoint[2]; + unsigned int reg_c5fb, reg_c570, reg_cd00; +- unsigned int vol_reg[4], fw_ready; ++ unsigned int vol_reg[4]; + unsigned long long l_meanr0, r_meanr0; +- unsigned int fw_status_addr; + int l_re[5], r_re[5]; + int ret, tmp; + unsigned long long factor = (1 << 27); + unsigned short l_advancegain, r_advancegain; + unsigned int delay_s = 7; /* delay seconds for the calibration */ ++ int dspfw_status; + + if (!rt1320->component) + return; + +- switch (rt1320->dev_id) { +- case RT1320_DEV_ID: +- fw_status_addr = RT1320_DSPFW_STATUS_ADDR; +- break; +- case RT1321_DEV_ID: +- fw_status_addr = RT1321_DSPFW_STATUS_ADDR; +- break; +- default: +- dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); +- return; +- } +- +- /* set volume 0dB */ + regmap_read(rt1320->regmap, 0xdd0b, &vol_reg[3]); + regmap_read(rt1320->regmap, 0xdd0a, &vol_reg[2]); + regmap_read(rt1320->regmap, 0xdd09, &vol_reg[1]); + regmap_read(rt1320->regmap, 0xdd08, &vol_reg[0]); +- regmap_write(rt1320->regmap, 0xdd0b, 0x0f); +- regmap_write(rt1320->regmap, 0xdd0a, 0xff); +- regmap_write(rt1320->regmap, 0xdd09, 0x0f); +- regmap_write(rt1320->regmap, 0xdd08, 0xff); +- + regmap_read(rt1320->regmap, 0xc5fb, ®_c5fb); + regmap_read(rt1320->regmap, 0xc570, ®_c570); + regmap_read(rt1320->regmap, 0xcd00, ®_cd00); +@@ -1171,9 +1646,8 @@ static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) + goto _finish_; + } + +- regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); +- fw_ready &= 0x1; +- if (!fw_ready) { ++ dspfw_status = rt1320_dspfw_status(rt1320); ++ if (dspfw_status <= 0) { + dev_dbg(dev, "%s, DSP FW is NOT ready. Please load DSP FW first\n", __func__); + goto _finish_; + } +@@ -1184,8 +1658,19 @@ static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) + goto _finish_; + } + +- if (rt1320->dev_id == RT1320_DEV_ID) +- regmap_write(rt1320->regmap, 0xc5fb, 0x00); ++ /* fine tune pilot tone output */ ++ ret = rt1320_pilot_tone_output(rt1320); ++ if (ret < 0) { ++ dev_dbg(dev, "%s, Failed to tune pilot tone output\n", __func__); ++ goto _finish_; ++ } ++ ++ if (rt1320->dev_id == RT1321_DEV_ID) { ++ regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x00); ++ regmap_write(rt1320->regmap, 0xc5c4, 0x12); ++ } ++ ++ regmap_write(rt1320->regmap, 0xc5fb, 0x00); + regmap_write(rt1320->regmap, 0xc570, 0x0b); + regmap_write(rt1320->regmap, 0xcd00, 0xc5); + +@@ -1246,6 +1731,11 @@ static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03); + rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x03); + ++ if (rt1320->dev_id == RT1321_DEV_ID) { ++ regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x80); ++ regmap_write(rt1320->regmap, 0xc5c4, 0x10); ++ } ++ + /* advance gain will be set when R0 load, not here */ + regmap_write(rt1320->regmap, 0xdd0b, vol_reg[3]); + regmap_write(rt1320->regmap, 0xdd0a, vol_reg[2]); +@@ -1296,7 +1786,7 @@ static int rt1320_r0_cali_put(struct snd_kcontrol *kcontrol, + static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320) + { + struct sdw_slave *slave = rt1320->sdw_slave; +- const struct firmware *patch; ++ const struct firmware *patch __free(firmware) = NULL; + const char *filename; + unsigned int addr, val, min_addr, max_addr; + const unsigned char *ptr; +@@ -1312,6 +1802,9 @@ static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320) + max_addr = 0x10007fff; + break; + case RT1321_DEV_ID: ++ if (rt1320->version_id == RT1321_VA2) ++ return; ++ + filename = RT1321_VA_MCU_PATCH; + min_addr = 0x10008000; + max_addr = 0x10008fff; +@@ -1347,17 +1840,15 @@ static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320) + + if (addr > max_addr || addr < min_addr) { + dev_err(&slave->dev, "%s: the address 0x%x is wrong", __func__, addr); +- goto _exit_; ++ return; + } + if (val > 0xff) { + dev_err(&slave->dev, "%s: the value 0x%x is wrong", __func__, val); +- goto _exit_; ++ return; + } + regmap_write(rt1320->regmap, addr, val); + } + } +-_exit_: +- release_firmware(patch); + } + } + +@@ -1382,30 +1873,17 @@ static void rt1320_vab_preset(struct rt1320_sdw_priv *rt1320) + static void rt1320_t0_load(struct rt1320_sdw_priv *rt1320, unsigned int l_t0, unsigned int r_t0) + { + struct device *dev = &rt1320->sdw_slave->dev; +- unsigned int factor = (1 << 22), fw_ready; ++ unsigned int factor = (1 << 22); + int l_t0_data[38], r_t0_data[38]; +- unsigned int fw_status_addr; +- +- switch (rt1320->dev_id) { +- case RT1320_DEV_ID: +- fw_status_addr = RT1320_DSPFW_STATUS_ADDR; +- break; +- case RT1321_DEV_ID: +- fw_status_addr = RT1321_DSPFW_STATUS_ADDR; +- break; +- default: +- dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); +- return; +- } ++ int dspfw_status; + + regmap_write(rt1320->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, + RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00); + rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00); + +- regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); +- fw_ready &= 0x1; +- if (!fw_ready) { ++ dspfw_status = rt1320_dspfw_status(rt1320); ++ if (dspfw_status <= 0) { + dev_warn(dev, "%s, DSP FW is NOT ready\n", __func__); + goto _exit_; + } +@@ -1444,7 +1922,7 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) + struct device *dev = &rt1320->sdw_slave->dev; + static const char func_tag[] = "FUNC"; + static const char xu_tag[] = "XU"; +- const struct firmware *rae_fw = NULL; ++ const struct firmware *rae_fw __free(firmware) = NULL; + unsigned int fw_offset; + unsigned char *fw_data; + unsigned char *param_data; +@@ -1483,22 +1961,46 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) + request_firmware(&rae_fw, rae_filename, dev); + if (rae_fw) { + +- /* RAE CRC clear */ +- regmap_write(rt1320->regmap, 0xe80b, 0x0f); +- +- /* RAE stop & CRC disable */ +- regmap_update_bits(rt1320->regmap, 0xe803, 0xbc, 0x00); +- +- while (--retry) { +- regmap_read(rt1320->regmap, 0xe83f, &value); +- if (value & 0x40) +- break; +- usleep_range(1000, 1100); +- } +- if (!retry && !(value & 0x40)) { +- dev_err(dev, "%s: RAE is not ready to load\n", __func__); +- release_firmware(rae_fw); +- return -ETIMEDOUT; ++ switch (rt1320->dev_id) { ++ case RT1320_DEV_ID: ++ /* RAE CRC clear */ ++ regmap_write(rt1320->regmap, 0xe80b, 0x0f); ++ /* RAE stop & CRC disable */ ++ regmap_update_bits(rt1320->regmap, 0xe803, 0xbc, 0x00); ++ while (--retry) { ++ regmap_read(rt1320->regmap, 0xe83f, &value); ++ if (value & 0x40) ++ break; ++ usleep_range(1000, 1100); ++ } ++ if (!retry && !(value & 0x40)) { ++ dev_err(dev, "%s: RAE is not ready to load\n", __func__); ++ return -ETIMEDOUT; ++ } ++ break; ++ case RT1321_DEV_ID: ++ /* RAE CRC clear */ ++ regmap_write(rt1320->regmap, 0x2000300e, 0xc0); ++ regmap_write(rt1320->regmap, 0x2000300f, 0x0f); ++ /* RAE stop & Phase sync & CRC disable */ ++ regmap_update_bits(rt1320->regmap, 0x20003003, 0xfe, 0x00); ++ regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x00); ++ regmap_update_bits(rt1320->regmap, 0x2000301c, 0x01, 0x00); ++ /* check whether write state is ready */ ++ while (--retry) { ++ regmap_read(rt1320->regmap, 0x20003043, &value); ++ if (value & 0x40) ++ break; ++ usleep_range(1000, 1100); ++ } ++ if (!retry && !(value & 0x40)) { ++ dev_err(dev, "%s: RAE is not ready to load\n", __func__); ++ return -ETIMEDOUT; ++ } ++ break; ++ default: ++ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); ++ return -EINVAL; + } + + dev_dbg(dev, "%s, rae_fw size=0x%zx\n", __func__, rae_fw->size); +@@ -1551,7 +2053,6 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) + } + + regcache_cache_bypass(rt1320->regmap, false); +- release_firmware(rae_fw); + + } else { + dev_err(dev, "%s: Failed to load %s firmware\n", __func__, rae_filename); +@@ -1559,18 +2060,34 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) + goto _exit_; + } + +- /* RAE CRC enable */ +- regmap_update_bits(rt1320->regmap, 0xe803, 0x0c, 0x0c); +- +- /* RAE update */ +- regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x00); +- regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x80); +- +- /* RAE run */ +- regmap_update_bits(rt1320->regmap, 0xe803, 0x80, 0x80); +- +- regmap_read(rt1320->regmap, 0xe80b, &value); +- dev_dbg(dev, "%s: CAE run => 0xe80b reg = 0x%x\n", __func__, value); ++ switch (rt1320->dev_id) { ++ case RT1320_DEV_ID: ++ /* RAE CRC enable */ ++ regmap_update_bits(rt1320->regmap, 0xe803, 0x0c, 0x0c); ++ /* RAE update */ ++ regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x00); ++ regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x80); ++ /* RAE run */ ++ regmap_update_bits(rt1320->regmap, 0xe803, 0x80, 0x80); ++ regmap_read(rt1320->regmap, 0xe80b, &value); ++ dev_dbg(dev, "%s: CAE run => 0xe80b reg = 0x%x\n", __func__, value); ++ break; ++ case RT1321_DEV_ID: ++ /* RAE CRC enable */ ++ regmap_update_bits(rt1320->regmap, 0x20003003, 0x30, 0x30); ++ /* RAE update */ ++ regmap_update_bits(rt1320->regmap, 0x2000301c, 0x80, 0x00); ++ regmap_update_bits(rt1320->regmap, 0x2000301c, 0x80, 0x80); ++ regmap_update_bits(rt1320->regmap, 0x20009018, 0x80, 0x00); ++ regmap_update_bits(rt1320->regmap, 0x20009018, 0x80, 0x80); ++ regmap_update_bits(rt1320->regmap, 0x20005818, 0x80, 0x00); ++ regmap_update_bits(rt1320->regmap, 0x20005818, 0x80, 0x80); ++ /* RAE run */ ++ regmap_update_bits(rt1320->regmap, 0x2000301c, 0x01, 0x01); ++ /* Phase sync eanble */ ++ regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x80); ++ break; ++ } + + rt1320->rae_update_done = true; + +@@ -1598,32 +2115,21 @@ struct rt1320_dspfwheader { + + struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt1320->component); + struct device *dev = &rt1320->sdw_slave->dev; +- unsigned int val, i, fw_offset, fw_ready; +- unsigned int fw_status_addr; ++ unsigned int val, i, fw_offset; + struct rt1320_dspfwheader *fwheader; + struct rt1320_imageinfo *ptr_img; + struct sdw_bpt_section sec[10]; +- const struct firmware *fw = NULL; ++ const struct firmware *fw __free(firmware) = NULL; + unsigned char *fw_data; + bool dev_fw_match = false; + static const char hdr_sig[] = "AFX"; + unsigned int hdr_size = 0; + const char *dmi_vendor, *dmi_product, *dmi_sku; + int len_vendor, len_product, len_sku; ++ unsigned char boot_mode = 0; /* 0: from RAM; 1: from ROM */ ++ unsigned char has_0x3fc00000 = 0; + char filename[512]; + +- switch (rt1320->dev_id) { +- case RT1320_DEV_ID: +- fw_status_addr = RT1320_DSPFW_STATUS_ADDR; +- break; +- case RT1321_DEV_ID: +- fw_status_addr = RT1321_DSPFW_STATUS_ADDR; +- break; +- default: +- dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); +- return; +- } +- + dmi_vendor = dmi_get_system_info(DMI_SYS_VENDOR); + dmi_product = dmi_get_system_info(DMI_PRODUCT_NAME); + dmi_sku = dmi_get_system_info(DMI_PRODUCT_SKU); +@@ -1653,17 +2159,12 @@ struct rt1320_dspfwheader { + RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00); + rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00); + +- regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); +- fw_ready &= 0x1; +- if (fw_ready) { ++ if (rt1320_dspfw_status(rt1320)) { + dev_dbg(dev, "%s, DSP FW was already\n", __func__); + rt1320->fw_load_done = true; + goto _exit_; + } + +- /* change to IRAM */ +- regmap_update_bits(rt1320->regmap, 0xf01e, 0x80, 0x00); +- + request_firmware(&fw, filename, dev); + if (fw) { + fwheader = (struct rt1320_dspfwheader *)fw->data; +@@ -1672,7 +2173,6 @@ struct rt1320_dspfwheader { + + if (fwheader->sync != 0x0a1c5679) { + dev_err(dev, "%s: FW sync error\n", __func__); +- release_firmware(fw); + goto _exit_; + } + +@@ -1709,9 +2209,11 @@ struct rt1320_dspfwheader { + dev_fw_match = true; + break; + case RT1321_DEV_ID: +- if (ptr_img->addr == 0x3fc00000) +- if (fw_data[9] == '1') ++ if (ptr_img->addr == 0x3fc00000) { ++ if (fw_data[7] == '1') + dev_fw_match = true; ++ has_0x3fc00000 = 1; ++ } + break; + default: + dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); +@@ -1721,6 +2223,22 @@ struct rt1320_dspfwheader { + fw_offset += ptr_img->size; + } + ++ if (rt1320->dev_id == RT1321_DEV_ID && rt1320->version_id == RT1321_VA2) { ++ /* ++ * For 1321 VA2, if the FW doesn't include the section for address 0x3fc00000, ++ * it means the FW will boot from ROM and force dev_fw_match to true to download FW by BRA. ++ */ ++ if (!has_0x3fc00000) { ++ boot_mode = 1; ++ dev_fw_match = true; ++ } ++ dev_dbg(dev, "%s: Boot from %s for VA2\n", __func__, (boot_mode ? "ROM" : "RAM")); ++ } ++ ++ /* change to IRAM */ ++ if (!boot_mode) ++ regmap_update_bits(rt1320->regmap, 0xf01e, 0x80, 0x00); ++ + if (dev_fw_match) { + dev_dbg(dev, "%s, starting BRA downloading FW..\n", __func__); + rt1320->bra_msg.dev_num = rt1320->sdw_slave->dev_num; +@@ -1732,7 +2250,6 @@ struct rt1320_dspfwheader { + } + + regcache_cache_bypass(rt1320->regmap, false); +- release_firmware(fw); + + if (!dev_fw_match) { + dev_err(dev, "%s: FW file doesn't match to device\n", __func__); +@@ -1743,24 +2260,39 @@ struct rt1320_dspfwheader { + goto _exit_; + } + +- /* run RAM code */ +- regmap_read(rt1320->regmap, 0x3fc2bfc0, &val); +- val |= 0x8; +- regmap_write(rt1320->regmap, 0x3fc2bfc0, val); +- +- /* clear frame counter */ + switch (rt1320->dev_id) { + case RT1320_DEV_ID: ++ /* run RAM code */ ++ regmap_read(rt1320->regmap, 0x3fc2bfc0, &val); ++ val |= 0x8; ++ regmap_write(rt1320->regmap, 0x3fc2bfc0, val); ++ /* clear frame counter */ + regmap_write(rt1320->regmap, 0x3fc2bfcb, 0x00); + regmap_write(rt1320->regmap, 0x3fc2bfca, 0x00); + regmap_write(rt1320->regmap, 0x3fc2bfc9, 0x00); + regmap_write(rt1320->regmap, 0x3fc2bfc8, 0x00); + break; + case RT1321_DEV_ID: ++ if (!boot_mode) { ++ /* run RAM code */ ++ regmap_read(rt1320->regmap, 0x3fc2dfc0, &val); ++ val |= 0x8; ++ regmap_write(rt1320->regmap, 0x3fc2dfc0, val); ++ } ++ /* clear frame counter */ + regmap_write(rt1320->regmap, 0x3fc2dfcb, 0x00); + regmap_write(rt1320->regmap, 0x3fc2dfca, 0x00); + regmap_write(rt1320->regmap, 0x3fc2dfc9, 0x00); + regmap_write(rt1320->regmap, 0x3fc2dfc8, 0x00); ++ /* enable handshake */ ++ regmap_write(rt1320->regmap, 0x3fc2dfc4, 0x00); ++ regmap_write(rt1320->regmap, 0xd470, 0xad); ++ /* minimum phase settings */ ++ regmap_write(rt1320->regmap, 0xc5c4, 0x10); ++ regmap_write(rt1320->regmap, 0x20003003, 0x31); ++ regmap_update_bits(rt1320->regmap, 0x20003002, 0x40, 0x00); ++ regmap_write(rt1320->regmap, 0xc5b3, 0x01); ++ regmap_write(rt1320->regmap, 0xc052, 0x11); + break; + } + +@@ -1843,14 +2375,35 @@ static void rt1320_vc_preset(struct rt1320_sdw_priv *rt1320) + + static void rt1321_preset(struct rt1320_sdw_priv *rt1320) + { ++ const struct reg_sequence *blindwrite; + unsigned int i, reg, val, delay; ++ unsigned int array_size; + +- for (i = 0; i < ARRAY_SIZE(rt1321_blind_write); i++) { +- reg = rt1321_blind_write[i].reg; +- val = rt1321_blind_write[i].def; +- delay = rt1321_blind_write[i].delay_us; ++ switch (rt1320->version_id) { ++ case RT1321_VA0: ++ blindwrite = rt1321_blind_write; ++ array_size = ARRAY_SIZE(rt1321_blind_write); ++ break; ++ case RT1321_VA1: ++ blindwrite = rt1321_va1_blind_write; ++ array_size = ARRAY_SIZE(rt1321_va1_blind_write); ++ break; ++ case RT1321_VA2: ++ blindwrite = rt1321_va2_blind_write; ++ array_size = ARRAY_SIZE(rt1321_va2_blind_write); ++ break; ++ default: ++ dev_err(&rt1320->sdw_slave->dev, "%s: Unknown version ID %d\n", ++ __func__, rt1320->version_id); ++ return; ++ } + +- if (reg == 0x3fc2dfc3) ++ for (i = 0; i < array_size; i++) { ++ reg = blindwrite[i].reg; ++ val = blindwrite[i].def; ++ delay = blindwrite[i].delay_us; ++ ++ if (reg == 0x1000cd56) + rt1320_load_mcu_patch(rt1320); + + regmap_write(rt1320->regmap, reg, val); +@@ -1892,6 +2445,24 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave) + regmap_read(rt1320->regmap, RT1320_DEV_ID_0, &val); + regmap_read(rt1320->regmap, RT1320_DEV_ID_1, &tmp); + rt1320->dev_id = (val << 8) | tmp; ++ ++ /* This is a workaround that reads the value twice to obtain the correct result. */ ++ rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_0, &val); ++ rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_1, &tmp); ++ rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_0, &val); ++ rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_1, &tmp); ++ val = (val << 8) | tmp; ++ ++ if (rt1320->dev_id == RT1321_DEV_ID) { ++ if (rt1320->version_id == 0x01) ++ rt1320->version_id = RT1321_VA2; ++ else if (val == RT1321_DEV_HV_VA0_ID) ++ rt1320->version_id = RT1321_VA0; ++ else if (val == RT1321_DEV_HV_VA1_ID) ++ rt1320->version_id = RT1321_VA1; ++ else ++ dev_err(dev, "%s: Unknown version ID 0x%x for RT1321\n", __func__, rt1320->version_id); ++ } + } + + regmap_read(rt1320->regmap, +@@ -2326,25 +2897,12 @@ static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); + static int rt1320_r0_load(struct rt1320_sdw_priv *rt1320) + { + struct device *dev = regmap_get_device(rt1320->regmap); +- unsigned int fw_status_addr; +- unsigned int fw_ready; ++ int dspfw_status; + int ret = 0; + + if (!rt1320->r0_l_reg || !rt1320->r0_r_reg) + return -EINVAL; + +- switch (rt1320->dev_id) { +- case RT1320_DEV_ID: +- fw_status_addr = RT1320_DSPFW_STATUS_ADDR; +- break; +- case RT1321_DEV_ID: +- fw_status_addr = RT1321_DSPFW_STATUS_ADDR; +- break; +- default: +- dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); +- return -EINVAL; +- } +- + regmap_write(rt1320->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00); + ret = rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00); +@@ -2353,9 +2911,8 @@ static int rt1320_r0_load(struct rt1320_sdw_priv *rt1320) + goto _timeout_; + } + +- regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); +- fw_ready &= 0x1; +- if (!fw_ready) { ++ dspfw_status = rt1320_dspfw_status(rt1320); ++ if (dspfw_status <= 0) { + dev_dbg(dev, "%s, DSP FW is NOT ready\n", __func__); + goto _timeout_; + } +@@ -2458,7 +3015,7 @@ static int rt1320_dspfw_load_put(struct snd_kcontrol *kcontrol, + if (!rt1320->hw_init) + return 0; + +- ret = pm_runtime_resume(component->dev); ++ ret = pm_runtime_resume_and_get(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + +@@ -2469,6 +3026,8 @@ static int rt1320_dspfw_load_put(struct snd_kcontrol *kcontrol, + if (!ucontrol->value.integer.value[0]) + rt1320->fw_load_done = false; + ++ pm_runtime_mark_last_busy(component->dev); ++ pm_runtime_put_autosuspend(component->dev); + return 0; + } + +@@ -2819,6 +3378,37 @@ static int rt1320_sdw_pcm_hw_free(struct snd_pcm_substream *substream, + return 0; + } + ++static int rt1320_bus_config(struct sdw_slave *slave, ++ struct sdw_bus_params *params) ++{ ++ struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(&slave->dev); ++ unsigned int clk_base; ++ ++ regmap_read(rt1320->regmap, 0x004d, &clk_base); ++ dev_dbg(&rt1320->sdw_slave->dev, "%s clk_base=%x", __func__, clk_base); ++ ++ switch (clk_base) { ++ case RT1320_CLK_FREQ_19_2_MHZ: ++ case RT1320_CLK_FREQ_24MHZ: ++ regmap_write(rt1320->regmap, 0xc600, 0x04); ++ regmap_write(rt1320->regmap, 0xc601, 0x83); ++ regmap_write(rt1320->regmap, 0xc602, 0x1f); ++ regmap_write(rt1320->regmap, 0xc603, 0x40); ++ break; ++ case RT1320_CLK_FREQ_24_576MHZ: ++ case RT1320_CLK_FREQ_22_5792MHZ: ++ regmap_write(rt1320->regmap, 0xc600, 0x07); ++ regmap_write(rt1320->regmap, 0xc601, 0x80); ++ regmap_write(rt1320->regmap, 0xc602, 0x0f); ++ regmap_write(rt1320->regmap, 0xc603, 0x40); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ + /* + * slave_ops: callbacks for get_clock_stop_mode, clock_stop and + * port_prep are not defined for now +@@ -2826,12 +3416,13 @@ static int rt1320_sdw_pcm_hw_free(struct snd_pcm_substream *substream, + static const struct sdw_slave_ops rt1320_slave_ops = { + .read_prop = rt1320_read_prop, + .update_status = rt1320_update_status, ++ .bus_config = rt1320_bus_config, + }; + + static int rt1320_sdw_component_probe(struct snd_soc_component *component) + { +- int ret; + struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); ++ int ret; + + rt1320->component = component; + +@@ -3062,10 +3653,23 @@ static int rt1320_dev_resume(struct device *dev) + return ret; + + regcache_cache_only(rt1320->regmap, false); +- regcache_sync(rt1320->regmap); ++ ret = regcache_sync(rt1320->regmap); ++ if (ret) ++ goto err_sync; ++ + regcache_cache_only(rt1320->mbq_regmap, false); +- regcache_sync(rt1320->mbq_regmap); ++ ret = regcache_sync(rt1320->mbq_regmap); ++ if (ret) ++ goto err_sync; ++ + return 0; ++ ++err_sync: ++ regcache_cache_only(rt1320->regmap, true); ++ regcache_cache_only(rt1320->mbq_regmap, true); ++ regcache_mark_dirty(rt1320->regmap); ++ regcache_mark_dirty(rt1320->mbq_regmap); ++ return ret; + } + + static const struct dev_pm_ops rt1320_pm = { +diff --git a/sound/soc/codecs/rt1320-sdw.h b/sound/soc/codecs/rt1320-sdw.h +--- a/sound/soc/codecs/rt1320-sdw.h ++++ b/sound/soc/codecs/rt1320-sdw.h +@@ -17,12 +17,17 @@ + + #define RT1320_DEV_ID 0x6981 + #define RT1321_DEV_ID 0x7045 ++#define RT1321_DEV_HV_VA0_ID 0x6997 ++#define RT1321_DEV_HV_VA1_ID 0x7071 + + /* imp-defined registers */ + #define RT1320_DEV_VERSION_ID_1 0xc404 + #define RT1320_DEV_ID_1 0xc405 + #define RT1320_DEV_ID_0 0xc406 + ++#define RT1320_HV_DEV_ID_0 0xf622 ++#define RT1320_HV_DEV_ID_1 0xf623 ++ + #define RT1320_POWER_STATE 0xc560 + + #define RT1321_PATCH_MAIN_VER 0x1000cffe +@@ -94,6 +99,12 @@ enum rt1320_version_id { + RT1320_VC, + }; + ++enum rt1321_version_id { ++ RT1321_VA0, ++ RT1321_VA1, ++ RT1321_VA2, ++}; ++ + #define RT1320_VER_B_ID 0x07392238 + #define RT1320_VAB_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vab.bin" + #define RT1320_VC_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vc.bin" +@@ -121,6 +132,7 @@ struct rt1320_datafixpoint { + int invrs; + }; + ++/* FW parameter id 1300 */ + typedef struct FwPara_HwSwGain { + unsigned int SwAdvGain; + unsigned int SwBasGain; +@@ -163,6 +175,13 @@ enum rt1320_rw_type { + RT1320_PARAM_READ = 3, + }; + ++enum { ++ RT1320_CLK_FREQ_19_2_MHZ = 1, ++ RT1320_CLK_FREQ_24MHZ = 2, ++ RT1320_CLK_FREQ_24_576MHZ = 3, ++ RT1320_CLK_FREQ_22_5792MHZ = 4, ++}; ++ + struct rt1320_sdw_priv { + struct snd_soc_component *component; + struct regmap *regmap; +diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c +--- a/sound/soc/codecs/rt5514-spi.c ++++ b/sound/soc/codecs/rt5514-spi.c +@@ -6,6 +6,7 @@ + * Author: Oder Chiou + */ + ++#include + #include + #include + #include +@@ -79,17 +80,17 @@ static void rt5514_spi_copy_work(struct work_struct *work) + unsigned int cur_wp, remain_data; + u8 buf[8]; + +- mutex_lock(&rt5514_dsp->dma_lock); ++ guard(mutex)(&rt5514_dsp->dma_lock); + if (!rt5514_dsp->substream) { + dev_err(rt5514_dsp->dev, "No pcm substream\n"); +- goto done; ++ return; + } + + runtime = rt5514_dsp->substream->runtime; + period_bytes = snd_pcm_lib_period_bytes(rt5514_dsp->substream); + if (!period_bytes) { + schedule_delayed_work(&rt5514_dsp->copy_work, 5); +- goto done; ++ return; + } + + if (rt5514_dsp->buf_size % period_bytes) +@@ -111,7 +112,7 @@ static void rt5514_spi_copy_work(struct work_struct *work) + + if (remain_data < period_bytes) { + schedule_delayed_work(&rt5514_dsp->copy_work, 5); +- goto done; ++ return; + } + } + +@@ -146,9 +147,6 @@ static void rt5514_spi_copy_work(struct work_struct *work) + snd_pcm_period_elapsed(rt5514_dsp->substream); + + schedule_delayed_work(&rt5514_dsp->copy_work, 5); +- +-done: +- mutex_unlock(&rt5514_dsp->dma_lock); + } + + static void rt5514_schedule_copy(struct rt5514_dsp *rt5514_dsp) +@@ -216,7 +214,7 @@ static int rt5514_spi_hw_params(struct snd_soc_component *component, + snd_soc_component_get_drvdata(component); + u8 buf[8]; + +- mutex_lock(&rt5514_dsp->dma_lock); ++ guard(mutex)(&rt5514_dsp->dma_lock); + rt5514_dsp->substream = substream; + rt5514_dsp->dma_offset = 0; + +@@ -225,8 +223,6 @@ static int rt5514_spi_hw_params(struct snd_soc_component *component, + if (buf[0] & RT5514_IRQ_STATUS_BIT) + rt5514_schedule_copy(rt5514_dsp); + +- mutex_unlock(&rt5514_dsp->dma_lock); +- + return 0; + } + +@@ -236,9 +232,8 @@ static int rt5514_spi_hw_free(struct snd_soc_component *component, + struct rt5514_dsp *rt5514_dsp = + snd_soc_component_get_drvdata(component); + +- mutex_lock(&rt5514_dsp->dma_lock); +- rt5514_dsp->substream = NULL; +- mutex_unlock(&rt5514_dsp->dma_lock); ++ scoped_guard(mutex, &rt5514_dsp->dma_lock) ++ rt5514_dsp->substream = NULL; + + cancel_delayed_work_sync(&rt5514_dsp->copy_work); + +diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c +--- a/sound/soc/codecs/rt5514.c ++++ b/sound/soc/codecs/rt5514.c +@@ -1073,12 +1073,18 @@ static int rt5514_set_bias_level(struct snd_soc_component *component, + * settings to make sure recording properly. + */ + if (rt5514->dsp_enabled) { +- rt5514->dsp_enabled = 0; +- regmap_multi_reg_write(rt5514->i2c_regmap, +- rt5514_i2c_patch, +- ARRAY_SIZE(rt5514_i2c_patch)); ++ ret = regmap_multi_reg_write(rt5514->i2c_regmap, ++ rt5514_i2c_patch, ++ ARRAY_SIZE(rt5514_i2c_patch)); ++ if (ret) ++ return ret; ++ + regcache_mark_dirty(rt5514->regmap); +- regcache_sync(rt5514->regmap); ++ ret = regcache_sync(rt5514->regmap); ++ if (ret) ++ return ret; ++ ++ rt5514->dsp_enabled = 0; + } + } + break; +diff --git a/sound/soc/codecs/rt5575-spi.c b/sound/soc/codecs/rt5575-spi.c +--- a/sound/soc/codecs/rt5575-spi.c ++++ b/sound/soc/codecs/rt5575-spi.c +@@ -93,7 +93,6 @@ static void rt5575_spi_burst_write(struct spi_device *spi, u32 addr, const u8 *t + int rt5575_spi_fw_load(struct spi_device *spi) + { + struct device *dev = &spi->dev; +- const struct firmware *firmware; + int i, ret; + static const char * const fw_path[] = { + "realtek/rt5575/rt5575_fw1.bin", +@@ -104,6 +103,7 @@ int rt5575_spi_fw_load(struct spi_device *spi) + static const u32 fw_addr[] = { 0x5f400000, 0x5f600000, 0x5f7fe000, 0x5f7ff000 }; + + for (i = 0; i < ARRAY_SIZE(fw_addr); i++) { ++ const struct firmware *firmware __free(firmware) = NULL; + ret = request_firmware(&firmware, fw_path[i], dev); + if (ret) { + dev_err(dev, "Request firmware failure: %d\n", ret); +@@ -111,7 +111,6 @@ int rt5575_spi_fw_load(struct spi_device *spi) + } + + rt5575_spi_burst_write(spi, fw_addr[i], firmware->data, firmware->size); +- release_firmware(firmware); + } + + return 0; +diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c +--- a/sound/soc/codecs/rt5645.c ++++ b/sound/soc/codecs/rt5645.c +@@ -6,6 +6,7 @@ + * Author: Bard Liao + */ + ++#include + #include + #include + #include +@@ -3323,92 +3324,89 @@ static void rt5645_jack_detect_work(struct work_struct *work) + if (!rt5645->component) + return; + +- mutex_lock(&rt5645->jd_mutex); +- +- switch (rt5645->pdata.jd_mode) { +- case 0: /* Not using rt5645 JD */ +- if (rt5645->gpiod_hp_det) { +- gpio_state = gpiod_get_value(rt5645->gpiod_hp_det); +- if (rt5645->pdata.inv_hp_pol) +- gpio_state ^= 1; +- dev_dbg(rt5645->component->dev, "gpio_state = %d\n", +- gpio_state); +- report = rt5645_jack_detect(rt5645->component, gpio_state); +- } +- snd_soc_jack_report(rt5645->hp_jack, +- report, SND_JACK_HEADPHONE); +- snd_soc_jack_report(rt5645->mic_jack, +- report, SND_JACK_MICROPHONE); +- mutex_unlock(&rt5645->jd_mutex); +- return; +- case 4: +- val = snd_soc_component_read(rt5645->component, RT5645_A_JD_CTRL1) & 0x0020; +- break; +- default: /* read rt5645 jd1_1 status */ +- val = snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x1000; +- break; +- +- } +- +- if (!val && (rt5645->jack_type == 0)) { /* jack in */ +- report = rt5645_jack_detect(rt5645->component, 1); +- } else if (!val && rt5645->jack_type == SND_JACK_HEADSET) { +- /* for push button and jack out */ +- btn_type = 0; +- if (snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x4) { +- /* button pressed */ +- report = SND_JACK_HEADSET; +- btn_type = rt5645_button_detect(rt5645->component); +- /* rt5650 can report three kinds of button behavior, +- one click, double click and hold. However, +- currently we will report button pressed/released +- event. So all the three button behaviors are +- treated as button pressed. */ +- switch (btn_type) { +- case 0x8000: +- case 0x4000: +- case 0x2000: +- report |= SND_JACK_BTN_0; +- break; +- case 0x1000: +- case 0x0800: +- case 0x0400: +- report |= SND_JACK_BTN_1; +- break; +- case 0x0200: +- case 0x0100: +- case 0x0080: +- report |= SND_JACK_BTN_2; +- break; +- case 0x0040: +- case 0x0020: +- case 0x0010: +- report |= SND_JACK_BTN_3; +- break; +- case 0x0000: /* unpressed */ +- break; +- default: +- dev_err(rt5645->component->dev, +- "Unexpected button code 0x%04x\n", +- btn_type); +- break; ++ scoped_guard(mutex, &rt5645->jd_mutex) { ++ switch (rt5645->pdata.jd_mode) { ++ case 0: /* Not using rt5645 JD */ ++ if (rt5645->gpiod_hp_det) { ++ gpio_state = gpiod_get_value(rt5645->gpiod_hp_det); ++ if (rt5645->pdata.inv_hp_pol) ++ gpio_state ^= 1; ++ dev_dbg(rt5645->component->dev, "gpio_state = %d\n", ++ gpio_state); ++ report = rt5645_jack_detect(rt5645->component, gpio_state); + } ++ snd_soc_jack_report(rt5645->hp_jack, ++ report, SND_JACK_HEADPHONE); ++ snd_soc_jack_report(rt5645->mic_jack, ++ report, SND_JACK_MICROPHONE); ++ return; ++ case 4: ++ val = snd_soc_component_read(rt5645->component, RT5645_A_JD_CTRL1) & 0x0020; ++ break; ++ default: /* read rt5645 jd1_1 status */ ++ val = snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x1000; ++ break; + } +- if (btn_type == 0)/* button release */ +- report = rt5645->jack_type; +- else { +- mod_timer(&rt5645->btn_check_timer, +- msecs_to_jiffies(100)); +- } +- } else { +- /* jack out */ +- report = 0; +- snd_soc_component_update_bits(rt5645->component, +- RT5645_INT_IRQ_ST, 0x1, 0x0); +- rt5645_jack_detect(rt5645->component, 0); +- } + +- mutex_unlock(&rt5645->jd_mutex); ++ if (!val && rt5645->jack_type == 0) { /* jack in */ ++ report = rt5645_jack_detect(rt5645->component, 1); ++ } else if (!val && rt5645->jack_type == SND_JACK_HEADSET) { ++ /* for push button and jack out */ ++ btn_type = 0; ++ if (snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x4) { ++ /* button pressed */ ++ report = SND_JACK_HEADSET; ++ btn_type = rt5645_button_detect(rt5645->component); ++ /* ++ * rt5650 can report three kinds of button behavior, ++ * one click, double click and hold. However, ++ * currently we will report button pressed/released ++ * event. So all the three button behaviors are ++ * treated as button pressed. ++ */ ++ switch (btn_type) { ++ case 0x8000: ++ case 0x4000: ++ case 0x2000: ++ report |= SND_JACK_BTN_0; ++ break; ++ case 0x1000: ++ case 0x0800: ++ case 0x0400: ++ report |= SND_JACK_BTN_1; ++ break; ++ case 0x0200: ++ case 0x0100: ++ case 0x0080: ++ report |= SND_JACK_BTN_2; ++ break; ++ case 0x0040: ++ case 0x0020: ++ case 0x0010: ++ report |= SND_JACK_BTN_3; ++ break; ++ case 0x0000: /* unpressed */ ++ break; ++ default: ++ dev_err(rt5645->component->dev, ++ "Unexpected button code 0x%04x\n", ++ btn_type); ++ break; ++ } ++ } ++ if (btn_type == 0)/* button release */ ++ report = rt5645->jack_type; ++ else ++ mod_timer(&rt5645->btn_check_timer, ++ msecs_to_jiffies(100)); ++ } else { ++ /* jack out */ ++ report = 0; ++ snd_soc_component_update_bits(rt5645->component, ++ RT5645_INT_IRQ_ST, 0x1, 0x0); ++ rt5645_jack_detect(rt5645->component, 0); ++ } ++ } + + snd_soc_jack_report(rt5645->hp_jack, report, SND_JACK_HEADPHONE); + snd_soc_jack_report(rt5645->mic_jack, report, SND_JACK_MICROPHONE); +@@ -3522,9 +3520,15 @@ static int rt5645_suspend(struct snd_soc_component *component) + static int rt5645_resume(struct snd_soc_component *component) + { + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); ++ int ret; + + regcache_cache_only(rt5645->regmap, false); +- regcache_sync(rt5645->regmap); ++ ret = regcache_sync(rt5645->regmap); ++ if (ret) { ++ regcache_cache_only(rt5645->regmap, true); ++ regcache_mark_dirty(rt5645->regmap); ++ return ret; ++ } + + return 0; + } +@@ -4335,9 +4339,15 @@ static int rt5645_sys_suspend(struct device *dev) + static int rt5645_sys_resume(struct device *dev) + { + struct rt5645_priv *rt5645 = dev_get_drvdata(dev); ++ int ret; + + regcache_cache_only(rt5645->regmap, false); +- regcache_sync(rt5645->regmap); ++ ret = regcache_sync(rt5645->regmap); ++ if (ret) { ++ regcache_cache_only(rt5645->regmap, true); ++ regcache_mark_dirty(rt5645->regmap); ++ return ret; ++ } + + if (rt5645->hp_jack) { + rt5645->jack_type = 0; +diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c +--- a/sound/soc/codecs/rt5665.c ++++ b/sound/soc/codecs/rt5665.c +@@ -6,6 +6,7 @@ + * Author: Bard Liao + */ + ++#include + #include + #include + #include +@@ -1208,7 +1209,7 @@ static void rt5665_jack_detect_handler(struct work_struct *work) + usleep_range(10000, 15000); + } + +- mutex_lock(&rt5665->calibrate_mutex); ++ guard(mutex)(&rt5665->calibrate_mutex); + + val = snd_soc_component_read(rt5665->component, RT5665_AJD1_CTRL) & 0x0010; + if (!val) { +@@ -1274,8 +1275,6 @@ static void rt5665_jack_detect_handler(struct work_struct *work) + schedule_delayed_work(&rt5665->jd_check_work, 0); + else + cancel_delayed_work_sync(&rt5665->jd_check_work); +- +- mutex_unlock(&rt5665->calibrate_mutex); + } + + static const char * const rt5665_clk_sync[] = { +@@ -4564,7 +4563,7 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665) + { + int value, count; + +- mutex_lock(&rt5665->calibrate_mutex); ++ guard(mutex)(&rt5665->calibrate_mutex); + + regcache_cache_bypass(rt5665->regmap, true); + +@@ -4601,7 +4600,8 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665) + pr_err("HP Calibration Failure\n"); + regmap_write(rt5665->regmap, RT5665_RESET, 0); + regcache_cache_bypass(rt5665->regmap, false); +- goto out_unlock; ++ rt5665->calibration_done = true; ++ return; + } + + count++; +@@ -4620,7 +4620,8 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665) + pr_err("MONO Calibration Failure\n"); + regmap_write(rt5665->regmap, RT5665_RESET, 0); + regcache_cache_bypass(rt5665->regmap, false); +- goto out_unlock; ++ rt5665->calibration_done = true; ++ return; + } + + count++; +@@ -4635,9 +4636,7 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665) + regmap_write(rt5665->regmap, RT5665_BIAS_CUR_CTRL_8, 0xa602); + regmap_write(rt5665->regmap, RT5665_ASRC_8, 0x0120); + +-out_unlock: + rt5665->calibration_done = true; +- mutex_unlock(&rt5665->calibrate_mutex); + } + + static void rt5665_calibrate_handler(struct work_struct *work) +diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c +--- a/sound/soc/codecs/rt5668.c ++++ b/sound/soc/codecs/rt5668.c +@@ -6,6 +6,7 @@ + * Author: Bard Liao + */ + ++#include + #include + #include + #include +@@ -986,7 +987,7 @@ static void rt5668_jack_detect_handler(struct work_struct *work) + return; + } + +- mutex_lock(&rt5668->calibrate_mutex); ++ guard(mutex)(&rt5668->calibrate_mutex); + + val = snd_soc_component_read(rt5668->component, RT5668_AJD1_CTRL) + & RT5668_JDH_RS_MASK; +@@ -1053,8 +1054,6 @@ static void rt5668_jack_detect_handler(struct work_struct *work) + schedule_delayed_work(&rt5668->jd_check_work, 0); + else + cancel_delayed_work_sync(&rt5668->jd_check_work); +- +- mutex_unlock(&rt5668->calibrate_mutex); + } + + static const struct snd_kcontrol_new rt5668_snd_controls[] = { +@@ -2356,7 +2355,7 @@ static void rt5668_calibrate(struct rt5668_priv *rt5668) + { + int value, count; + +- mutex_lock(&rt5668->calibrate_mutex); ++ guard(mutex)(&rt5668->calibrate_mutex); + + rt5668_reset(rt5668->regmap); + regmap_write(rt5668->regmap, RT5668_PWR_ANLG_1, 0xa2bf); +@@ -2400,9 +2399,6 @@ static void rt5668_calibrate(struct rt5668_priv *rt5668) + /* restore settings */ + regmap_write(rt5668->regmap, RT5668_STO1_ADC_MIXER, 0xc0c4); + regmap_write(rt5668->regmap, RT5668_PWR_DIG_1, 0x0000); +- +- mutex_unlock(&rt5668->calibrate_mutex); +- + } + + static int rt5668_i2c_probe(struct i2c_client *i2c) +diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c +--- a/sound/soc/codecs/rt5677-spi.c ++++ b/sound/soc/codecs/rt5677-spi.c +@@ -6,6 +6,7 @@ + * Author: Oder Chiou + */ + ++#include + #include + #include + #include +@@ -133,9 +134,8 @@ static int rt5677_spi_hw_params( + struct rt5677_dsp *rt5677_dsp = + snd_soc_component_get_drvdata(component); + +- mutex_lock(&rt5677_dsp->dma_lock); ++ guard(mutex)(&rt5677_dsp->dma_lock); + rt5677_dsp->substream = substream; +- mutex_unlock(&rt5677_dsp->dma_lock); + + return 0; + } +@@ -147,9 +147,8 @@ static int rt5677_spi_hw_free( + struct rt5677_dsp *rt5677_dsp = + snd_soc_component_get_drvdata(component); + +- mutex_lock(&rt5677_dsp->dma_lock); ++ guard(mutex)(&rt5677_dsp->dma_lock); + rt5677_dsp->substream = NULL; +- mutex_unlock(&rt5677_dsp->dma_lock); + + return 0; + } +@@ -311,17 +310,17 @@ static void rt5677_spi_copy_work(struct work_struct *work) + int ret = 0; + + /* Ensure runtime->dma_area buffer does not go away while copying. */ +- mutex_lock(&rt5677_dsp->dma_lock); ++ guard(mutex)(&rt5677_dsp->dma_lock); + if (!rt5677_dsp->substream) { + dev_err(rt5677_dsp->dev, "No pcm substream\n"); +- goto done; ++ return; + } + + runtime = rt5677_dsp->substream->runtime; + + if (rt5677_spi_mic_write_offset(&mic_write_offset)) { + dev_err(rt5677_dsp->dev, "No mic_write_offset\n"); +- goto done; ++ return; + } + + /* If this is the first time that we've asked for streaming data after +@@ -355,7 +354,7 @@ static void rt5677_spi_copy_work(struct work_struct *work) + ret = rt5677_spi_copy(rt5677_dsp, copy_bytes); + if (ret) { + dev_err(rt5677_dsp->dev, "Copy failed %d\n", ret); +- goto done; ++ return; + } + rt5677_dsp->avail_bytes += copy_bytes; + if (rt5677_dsp->avail_bytes >= period_bytes) { +@@ -367,8 +366,6 @@ static void rt5677_spi_copy_work(struct work_struct *work) + + delay = bytes_to_frames(runtime, period_bytes) / runtime->rate; + schedule_delayed_work(&rt5677_dsp->copy_work, secs_to_jiffies(delay)); +-done: +- mutex_unlock(&rt5677_dsp->dma_lock); + } + + static int rt5677_spi_pcm_new(struct snd_soc_component *component, +@@ -507,10 +504,8 @@ int rt5677_spi_read(u32 addr, void *rxbuf, size_t len) + header[3] = ((addr + offset) & 0x0000ff00) >> 8; + header[4] = ((addr + offset) & 0x000000ff) >> 0; + +- mutex_lock(&spi_mutex); +- status |= spi_sync(g_spi, &m); +- mutex_unlock(&spi_mutex); +- ++ scoped_guard(mutex, &spi_mutex) ++ status |= spi_sync(g_spi, &m); + + /* Copy data back to caller buffer */ + rt5677_spi_reverse(cb + offset, len - offset, body, t[1].len); +@@ -564,9 +559,8 @@ int rt5677_spi_write(u32 addr, const void *txbuf, size_t len) + offset += t.len; + t.len += RT5677_SPI_HEADER + 1; + +- mutex_lock(&spi_mutex); +- status |= spi_sync(g_spi, &m); +- mutex_unlock(&spi_mutex); ++ scoped_guard(mutex, &spi_mutex) ++ status |= spi_sync(g_spi, &m); + } + return status; + } +@@ -591,10 +585,10 @@ void rt5677_spi_hotword_detected(void) + return; + } + +- mutex_lock(&rt5677_dsp->dma_lock); +- dev_info(rt5677_dsp->dev, "Hotword detected\n"); +- rt5677_dsp->new_hotword = true; +- mutex_unlock(&rt5677_dsp->dma_lock); ++ scoped_guard(mutex, &rt5677_dsp->dma_lock) { ++ dev_info(rt5677_dsp->dev, "Hotword detected\n"); ++ rt5677_dsp->new_hotword = true; ++ } + + schedule_delayed_work(&rt5677_dsp->copy_work, 0); + } +diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c +--- a/sound/soc/codecs/rt5677.c ++++ b/sound/soc/codecs/rt5677.c +@@ -6,6 +6,8 @@ + * Author: Oder Chiou + */ + ++#include ++#include + #include + #include + #include +@@ -563,46 +565,43 @@ static int rt5677_dsp_mode_i2c_write_addr(struct rt5677_priv *rt5677, + struct snd_soc_component *component = rt5677->component; + int ret; + +- mutex_lock(&rt5677->dsp_cmd_lock); ++ guard(mutex)(&rt5677->dsp_cmd_lock); + + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB, + addr >> 16); + if (ret < 0) { + dev_err(component->dev, "Failed to set addr msb value: %d\n", ret); +- goto err; ++ return ret; + } + + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB, + addr & 0xffff); + if (ret < 0) { + dev_err(component->dev, "Failed to set addr lsb value: %d\n", ret); +- goto err; ++ return ret; + } + + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_MSB, + value >> 16); + if (ret < 0) { + dev_err(component->dev, "Failed to set data msb value: %d\n", ret); +- goto err; ++ return ret; + } + + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_LSB, + value & 0xffff); + if (ret < 0) { + dev_err(component->dev, "Failed to set data lsb value: %d\n", ret); +- goto err; ++ return ret; + } + + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE, + opcode); + if (ret < 0) { + dev_err(component->dev, "Failed to set op code value: %d\n", ret); +- goto err; ++ return ret; + } + +-err: +- mutex_unlock(&rt5677->dsp_cmd_lock); +- + return ret; + } + +@@ -622,36 +621,33 @@ static int rt5677_dsp_mode_i2c_read_addr( + int ret; + unsigned int msb, lsb; + +- mutex_lock(&rt5677->dsp_cmd_lock); ++ guard(mutex)(&rt5677->dsp_cmd_lock); + + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB, + addr >> 16); + if (ret < 0) { + dev_err(component->dev, "Failed to set addr msb value: %d\n", ret); +- goto err; ++ return ret; + } + + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB, + addr & 0xffff); + if (ret < 0) { + dev_err(component->dev, "Failed to set addr lsb value: %d\n", ret); +- goto err; ++ return ret; + } + + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE, + 0x0002); + if (ret < 0) { + dev_err(component->dev, "Failed to set op code value: %d\n", ret); +- goto err; ++ return ret; + } + + regmap_read(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_MSB, &msb); + regmap_read(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_LSB, &lsb); + *value = (msb << 16) | lsb; + +-err: +- mutex_unlock(&rt5677->dsp_cmd_lock); +- + return ret; + } + +@@ -853,11 +849,11 @@ static int rt5677_parse_and_load_dsp(struct rt5677_priv *rt5677, const u8 *buf, + + static int rt5677_load_dsp_from_file(struct rt5677_priv *rt5677) + { +- const struct firmware *fwp; + struct device *dev = rt5677->component->dev; +- int ret = 0; ++ int ret; + + /* Load dsp firmware from rt5677_elf_vad file */ ++ const struct firmware *fwp __free(firmware) = NULL; + ret = request_firmware(&fwp, "rt5677_elf_vad", dev); + if (ret) { + dev_err(dev, "Request rt5677_elf_vad failed %d\n", ret); +@@ -865,9 +861,7 @@ static int rt5677_load_dsp_from_file(struct rt5677_priv *rt5677) + } + dev_info(dev, "Requested rt5677_elf_vad (%zu)\n", fwp->size); + +- ret = rt5677_parse_and_load_dsp(rt5677, fwp->data, fwp->size); +- release_firmware(fwp); +- return ret; ++ return rt5677_parse_and_load_dsp(rt5677, fwp->data, fwp->size); + } + + static int rt5677_set_dsp_vad(struct snd_soc_component *component, bool on) +@@ -940,21 +934,20 @@ static void rt5677_dsp_work(struct work_struct *work) + activity = false; + + /* Don't turn off the DSP while handling irqs */ +- mutex_lock(&rt5677->irq_lock); +- /* Set DSP CPU to Stop */ +- regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, +- RT5677_PWR_DSP_CPU, RT5677_PWR_DSP_CPU); ++ scoped_guard(mutex, &rt5677->irq_lock) { ++ /* Set DSP CPU to Stop */ ++ regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, ++ RT5677_PWR_DSP_CPU, RT5677_PWR_DSP_CPU); + +- rt5677_set_dsp_mode(rt5677, false); ++ rt5677_set_dsp_mode(rt5677, false); + +- /* Disable and clear VAD interrupt */ +- regmap_write(rt5677->regmap, RT5677_VAD_CTRL1, 0x2184); ++ /* Disable and clear VAD interrupt */ ++ regmap_write(rt5677->regmap, RT5677_VAD_CTRL1, 0x2184); + +- /* Set GPIO1 pin back to be IRQ output for jack detect */ +- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, +- RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ); +- +- mutex_unlock(&rt5677->irq_lock); ++ /* Set GPIO1 pin back to be IRQ output for jack detect */ ++ regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, ++ RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ); ++ } + } + } + +@@ -4767,6 +4760,21 @@ static int rt5677_gpio_direction_in(struct gpio_chip *chip, unsigned offset) + return rt5677_update_gpio_bits(rt5677, offset, m, v); + } + ++static int rt5677_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) ++{ ++ struct rt5677_priv *rt5677 = gpiochip_get_data(chip); ++ unsigned int shift = RT5677_GPIOx_DIR_SFT + (offset % 5) * 3; ++ unsigned int bank = offset / 5; ++ unsigned int reg = bank ? RT5677_GPIO_CTRL3 : RT5677_GPIO_CTRL2; ++ int ret; ++ ++ ret = regmap_test_bits(rt5677->regmap, reg, BIT(shift)); ++ if (ret < 0) ++ return ret; ++ ++ return ret ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; ++} ++ + /* + * Configures the GPIO as + * 0 - floating +@@ -4834,6 +4842,7 @@ static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset) + static const struct gpio_chip rt5677_template_chip = { + .label = RT5677_DRV_NAME, + .owner = THIS_MODULE, ++ .get_direction = rt5677_gpio_get_direction, + .direction_output = rt5677_gpio_direction_out, + .set = rt5677_gpio_set, + .direction_input = rt5677_gpio_direction_in, +@@ -4980,11 +4989,11 @@ static int rt5677_read(void *context, unsigned int reg, unsigned int *val) + + if (rt5677->is_dsp_mode) { + if (reg > 0xff) { +- mutex_lock(&rt5677->dsp_pri_lock); +- rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, +- reg & 0xff); +- rt5677_dsp_mode_i2c_read(rt5677, RT5677_PRIV_DATA, val); +- mutex_unlock(&rt5677->dsp_pri_lock); ++ scoped_guard(mutex, &rt5677->dsp_pri_lock) { ++ rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, ++ reg & 0xff); ++ rt5677_dsp_mode_i2c_read(rt5677, RT5677_PRIV_DATA, val); ++ } + } else { + rt5677_dsp_mode_i2c_read(rt5677, reg, val); + } +@@ -5002,12 +5011,12 @@ static int rt5677_write(void *context, unsigned int reg, unsigned int val) + + if (rt5677->is_dsp_mode) { + if (reg > 0xff) { +- mutex_lock(&rt5677->dsp_pri_lock); +- rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, +- reg & 0xff); +- rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_DATA, +- val); +- mutex_unlock(&rt5677->dsp_pri_lock); ++ scoped_guard(mutex, &rt5677->dsp_pri_lock) { ++ rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, ++ reg & 0xff); ++ rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_DATA, ++ val); ++ } + } else { + rt5677_dsp_mode_i2c_write(rt5677, reg, val); + } +@@ -5399,7 +5408,7 @@ static void rt5677_resume_irq_check(struct work_struct *work) + * Without this explicit check, unplug the headset right after suspend + * starts, then after resume the headset is still shown as plugged in. + */ +- mutex_lock(&rt5677->irq_lock); ++ guard(mutex)(&rt5677->irq_lock); + for (i = 0; i < RT5677_IRQ_NUM; i++) { + if (rt5677->irq_en & rt5677_irq_descs[i].enable_mask) { + virq = irq_find_mapping(rt5677->domain, i); +@@ -5407,7 +5416,6 @@ static void rt5677_resume_irq_check(struct work_struct *work) + handle_nested_irq(virq); + } + } +- mutex_unlock(&rt5677->irq_lock); + } + + static void rt5677_irq_bus_lock(struct irq_data *data) +diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c +--- a/sound/soc/codecs/rt5682-sdw.c ++++ b/sound/soc/codecs/rt5682-sdw.c +@@ -6,6 +6,7 @@ + // Author: Oder Chiou + // + ++#include + #include + #include + #include +@@ -410,7 +411,9 @@ static int rt5682_io_init(struct device *dev, struct sdw_slave *slave) + if (rt5682->first_hw_init) { + regcache_cache_bypass(rt5682->regmap, false); + regcache_mark_dirty(rt5682->regmap); +- regcache_sync(rt5682->regmap); ++ ret = regcache_sync(rt5682->regmap); ++ if (ret) ++ goto err_sync; + + /* volatile registers */ + regmap_update_bits(rt5682->regmap, RT5682_CBJ_CTRL_2, +@@ -472,8 +475,16 @@ static int rt5682_io_init(struct device *dev, struct sdw_slave *slave) + /* Mark Slave initialization complete */ + rt5682->hw_init = true; + rt5682->first_hw_init = true; ++ goto out; ++ ++err_sync: ++ regcache_cache_bypass(rt5682->regmap, false); ++ regcache_cache_only(rt5682->sdw_regmap, true); ++ regcache_cache_only(rt5682->regmap, true); ++ regcache_mark_dirty(rt5682->regmap); + + err_nodev: ++out: + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete: %d\n", __func__, ret); +@@ -660,12 +671,11 @@ static int rt5682_interrupt_callback(struct sdw_slave *slave, + dev_dbg(&slave->dev, + "%s control_port_stat=%x", __func__, status->control_port); + +- mutex_lock(&rt5682->disable_irq_lock); ++ guard(mutex)(&rt5682->disable_irq_lock); + if (status->control_port & 0x4 && !rt5682->disable_irq) { + mod_delayed_work(system_power_efficient_wq, + &rt5682->jack_detect_work, msecs_to_jiffies(rt5682->irq_work_delay_time)); + } +- mutex_unlock(&rt5682->disable_irq_lock); + + return 0; + } +@@ -736,11 +746,11 @@ static int rt5682_dev_system_suspend(struct device *dev) + * deferred work completes and before the parent disables + * interrupts on the link + */ +- mutex_lock(&rt5682->disable_irq_lock); +- rt5682->disable_irq = true; +- ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, +- SDW_SCP_INT1_IMPL_DEF, 0); +- mutex_unlock(&rt5682->disable_irq_lock); ++ scoped_guard(mutex, &rt5682->disable_irq_lock) { ++ rt5682->disable_irq = true; ++ ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, ++ SDW_SCP_INT1_IMPL_DEF, 0); ++ } + + if (ret < 0) { + /* log but don't prevent suspend from happening */ +@@ -760,12 +770,12 @@ static int rt5682_dev_resume(struct device *dev) + return 0; + + if (!slave->unattach_request) { +- mutex_lock(&rt5682->disable_irq_lock); +- if (rt5682->disable_irq == true) { +- sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); +- rt5682->disable_irq = false; ++ scoped_guard(mutex, &rt5682->disable_irq_lock) { ++ if (rt5682->disable_irq) { ++ sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); ++ rt5682->disable_irq = false; ++ } + } +- mutex_unlock(&rt5682->disable_irq_lock); + } + + ret = sdw_slave_wait_for_init(slave, RT5682_PROBE_TIMEOUT); +@@ -776,7 +786,13 @@ static int rt5682_dev_resume(struct device *dev) + + regcache_cache_only(rt5682->sdw_regmap, false); + regcache_cache_only(rt5682->regmap, false); +- regcache_sync(rt5682->regmap); ++ ret = regcache_sync(rt5682->regmap); ++ if (ret) { ++ regcache_cache_only(rt5682->sdw_regmap, true); ++ regcache_cache_only(rt5682->regmap, true); ++ regcache_mark_dirty(rt5682->regmap); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c +--- a/sound/soc/codecs/rt5682.c ++++ b/sound/soc/codecs/rt5682.c +@@ -6,6 +6,7 @@ + // Author: Bard Liao + // + ++#include + #include + #include + #include +@@ -3125,7 +3126,7 @@ void rt5682_calibrate(struct rt5682_priv *rt5682) + { + int value, count; + +- mutex_lock(&rt5682->calibrate_mutex); ++ guard(mutex)(&rt5682->calibrate_mutex); + + rt5682_reset(rt5682); + regmap_write(rt5682->regmap, RT5682_I2C_CTRL, 0x000f); +@@ -3175,8 +3176,6 @@ void rt5682_calibrate(struct rt5682_priv *rt5682) + regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x2005); + regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0xc0c4); + regmap_write(rt5682->regmap, RT5682_CAL_REC, 0x0c0c); +- +- mutex_unlock(&rt5682->calibrate_mutex); + } + EXPORT_SYMBOL_GPL(rt5682_calibrate); + +diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c +--- a/sound/soc/codecs/rt5682s.c ++++ b/sound/soc/codecs/rt5682s.c +@@ -6,6 +6,7 @@ + // Author: Derek Fang + // + ++#include + #include + #include + #include +@@ -648,7 +649,7 @@ static void rt5682s_sar_power_mode(struct snd_soc_component *component, int mode + { + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + +- mutex_lock(&rt5682s->sar_mutex); ++ guard(mutex)(&rt5682s->sar_mutex); + + switch (mode) { + case SAR_PWR_SAVING: +@@ -695,8 +696,6 @@ static void rt5682s_sar_power_mode(struct snd_soc_component *component, int mode + dev_err(component->dev, "Invalid SAR Power mode: %d\n", mode); + break; + } +- +- mutex_unlock(&rt5682s->sar_mutex); + } + + static void rt5682s_enable_push_button_irq(struct snd_soc_component *component) +@@ -2534,7 +2533,7 @@ static int rt5682s_wclk_prepare(struct clk_hw *hw) + if (!rt5682s_clk_check(rt5682s)) + return -EINVAL; + +- mutex_lock(&rt5682s->wclk_mutex); ++ guard(mutex)(&rt5682s->wclk_mutex); + + snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1, + RT5682S_PWR_VREF2 | RT5682S_PWR_FV2 | RT5682S_PWR_MB, +@@ -2556,8 +2555,6 @@ static int rt5682s_wclk_prepare(struct clk_hw *hw) + + rt5682s->wclk_enabled = 1; + +- mutex_unlock(&rt5682s->wclk_mutex); +- + return 0; + } + +@@ -2570,7 +2567,7 @@ static void rt5682s_wclk_unprepare(struct clk_hw *hw) + if (!rt5682s_clk_check(rt5682s)) + return; + +- mutex_lock(&rt5682s->wclk_mutex); ++ guard(mutex)(&rt5682s->wclk_mutex); + + if (!rt5682s->jack_type) + snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1, +@@ -2585,8 +2582,6 @@ static void rt5682s_wclk_unprepare(struct clk_hw *hw) + rt5682s_set_pllb_power(rt5682s, 0); + + rt5682s->wclk_enabled = 0; +- +- mutex_unlock(&rt5682s->wclk_mutex); + } + + static unsigned long rt5682s_wclk_recalc_rate(struct clk_hw *hw, +@@ -2997,7 +2992,7 @@ static void rt5682s_calibrate(struct rt5682s_priv *rt5682s) + { + unsigned int count, value; + +- mutex_lock(&rt5682s->calibrate_mutex); ++ guard(mutex)(&rt5682s->calibrate_mutex); + + regmap_write(rt5682s->regmap, RT5682S_PWR_ANLG_1, 0xaa80); + usleep_range(15000, 20000); +@@ -3034,8 +3029,6 @@ static void rt5682s_calibrate(struct rt5682s_priv *rt5682s) + regmap_write(rt5682s->regmap, RT5682S_PWR_DIG_1, 0x00c0); + regmap_write(rt5682s->regmap, RT5682S_PWR_ANLG_1, 0x0800); + regmap_write(rt5682s->regmap, RT5682S_GLB_CLK, 0x0000); +- +- mutex_unlock(&rt5682s->calibrate_mutex); + } + + static const struct regmap_config rt5682s_regmap = { +diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c +--- a/sound/soc/codecs/rt700-sdw.c ++++ b/sound/soc/codecs/rt700-sdw.c +@@ -6,6 +6,7 @@ + // + // + ++#include + #include + #include + #include +@@ -415,12 +416,11 @@ static int rt700_interrupt_callback(struct sdw_slave *slave, + dev_dbg(&slave->dev, + "%s control_port_stat=%x", __func__, status->control_port); + +- mutex_lock(&rt700->disable_irq_lock); ++ guard(mutex)(&rt700->disable_irq_lock); + if (status->control_port & 0x4 && !rt700->disable_irq) { + mod_delayed_work(system_power_efficient_wq, + &rt700->jack_detect_work, msecs_to_jiffies(250)); + } +- mutex_unlock(&rt700->disable_irq_lock); + + return 0; + } +@@ -499,11 +499,11 @@ static int rt700_dev_system_suspend(struct device *dev) + * deferred work completes and before the parent disables + * interrupts on the link + */ +- mutex_lock(&rt700->disable_irq_lock); +- rt700->disable_irq = true; +- ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, +- SDW_SCP_INT1_IMPL_DEF, 0); +- mutex_unlock(&rt700->disable_irq_lock); ++ scoped_guard(mutex, &rt700->disable_irq_lock) { ++ rt700->disable_irq = true; ++ ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, ++ SDW_SCP_INT1_IMPL_DEF, 0); ++ } + + if (ret < 0) { + /* log but don't prevent suspend from happening */ +diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c +--- a/sound/soc/codecs/rt711-sdca-sdw.c ++++ b/sound/soc/codecs/rt711-sdca-sdw.c +@@ -6,6 +6,7 @@ + // + // + ++#include + #include + #include + #include +@@ -415,13 +416,13 @@ static int rt711_sdca_dev_system_suspend(struct device *dev) + * deferred work completes and before the parent disables + * interrupts on the link + */ +- mutex_lock(&rt711_sdca->disable_irq_lock); +- rt711_sdca->disable_irq = true; +- ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, +- SDW_SCP_SDCA_INTMASK_SDCA_0, 0); +- ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, +- SDW_SCP_SDCA_INTMASK_SDCA_8, 0); +- mutex_unlock(&rt711_sdca->disable_irq_lock); ++ scoped_guard(mutex, &rt711_sdca->disable_irq_lock) { ++ rt711_sdca->disable_irq = true; ++ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, ++ SDW_SCP_SDCA_INTMASK_SDCA_0, 0); ++ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, ++ SDW_SCP_SDCA_INTMASK_SDCA_8, 0); ++ } + + if (ret1 < 0 || ret2 < 0) { + /* log but don't prevent suspend from happening */ +@@ -443,13 +444,15 @@ static int rt711_sdca_dev_resume(struct device *dev) + return 0; + + if (!slave->unattach_request) { +- mutex_lock(&rt711->disable_irq_lock); +- if (rt711->disable_irq == true) { +- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); +- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); +- rt711->disable_irq = false; ++ scoped_guard(mutex, &rt711->disable_irq_lock) { ++ if (rt711->disable_irq) { ++ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, ++ SDW_SCP_SDCA_INTMASK_SDCA_0); ++ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, ++ SDW_SCP_SDCA_INTMASK_SDCA_8); ++ rt711->disable_irq = false; ++ } + } +- mutex_unlock(&rt711->disable_irq_lock); + } + + ret = sdw_slave_wait_for_init(slave, RT711_PROBE_TIMEOUT); +diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c +--- a/sound/soc/codecs/rt711-sdca.c ++++ b/sound/soc/codecs/rt711-sdca.c +@@ -6,6 +6,7 @@ + // + // + ++#include + #include + #include + #include +@@ -450,7 +451,7 @@ static void rt711_sdca_btn_check_handler(struct work_struct *work) + + static void rt711_sdca_jack_init(struct rt711_sdca_priv *rt711) + { +- mutex_lock(&rt711->calibrate_mutex); ++ guard(mutex)(&rt711->calibrate_mutex); + + if (rt711->hs_jack) { + /* Enable HID1 event & set button RTC mode */ +@@ -515,8 +516,6 @@ static void rt711_sdca_jack_init(struct rt711_sdca_priv *rt711) + + dev_dbg(&rt711->slave->dev, "in %s disable\n", __func__); + } +- +- mutex_unlock(&rt711->calibrate_mutex); + } + + static int rt711_sdca_set_jack_detect(struct snd_soc_component *component, +diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c +--- a/sound/soc/codecs/rt711-sdw.c ++++ b/sound/soc/codecs/rt711-sdw.c +@@ -6,6 +6,7 @@ + // + // + ++#include + #include + #include + #include +@@ -422,12 +423,11 @@ static int rt711_interrupt_callback(struct sdw_slave *slave, + dev_dbg(&slave->dev, + "%s control_port_stat=%x", __func__, status->control_port); + +- mutex_lock(&rt711->disable_irq_lock); ++ guard(mutex)(&rt711->disable_irq_lock); + if (status->control_port & 0x4 && !rt711->disable_irq) { + mod_delayed_work(system_power_efficient_wq, + &rt711->jack_detect_work, msecs_to_jiffies(250)); + } +- mutex_unlock(&rt711->disable_irq_lock); + + return 0; + } +@@ -509,11 +509,11 @@ static int rt711_dev_system_suspend(struct device *dev) + * deferred work completes and before the parent disables + * interrupts on the link + */ +- mutex_lock(&rt711->disable_irq_lock); +- rt711->disable_irq = true; +- ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, +- SDW_SCP_INT1_IMPL_DEF, 0); +- mutex_unlock(&rt711->disable_irq_lock); ++ scoped_guard(mutex, &rt711->disable_irq_lock) { ++ rt711->disable_irq = true; ++ ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, ++ SDW_SCP_INT1_IMPL_DEF, 0); ++ } + + if (ret < 0) { + /* log but don't prevent suspend from happening */ +@@ -535,12 +535,12 @@ static int rt711_dev_resume(struct device *dev) + return 0; + + if (!slave->unattach_request) { +- mutex_lock(&rt711->disable_irq_lock); +- if (rt711->disable_irq == true) { +- sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); +- rt711->disable_irq = false; ++ scoped_guard(mutex, &rt711->disable_irq_lock) { ++ if (rt711->disable_irq) { ++ sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); ++ rt711->disable_irq = false; ++ } + } +- mutex_unlock(&rt711->disable_irq_lock); + } + + ret = sdw_slave_wait_for_init(slave, RT711_PROBE_TIMEOUT); +diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c +--- a/sound/soc/codecs/rt711.c ++++ b/sound/soc/codecs/rt711.c +@@ -6,6 +6,7 @@ + // + // + ++#include + #include + #include + #include +@@ -89,24 +90,24 @@ static int rt711_calibration(struct rt711_priv *rt711) + struct regmap *regmap = rt711->regmap; + int ret = 0; + +- mutex_lock(&rt711->calibrate_mutex); ++ guard(mutex)(&rt711->calibrate_mutex); + regmap_write(rt711->regmap, +- RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); ++ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + + dev = regmap_get_device(regmap); + + /* Calibration manual mode */ + rt711_index_update_bits(regmap, RT711_VENDOR_REG, RT711_FSM_CTL, +- 0xf, 0x0); ++ 0xf, 0x0); + + /* trigger */ + rt711_index_update_bits(regmap, RT711_VENDOR_CALI, +- RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_CALI_TRIGGER, +- RT711_DAC_DC_CALI_TRIGGER); ++ RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_CALI_TRIGGER, ++ RT711_DAC_DC_CALI_TRIGGER); + + /* wait for calibration process */ + rt711_index_read(regmap, RT711_VENDOR_CALI, +- RT711_DAC_DC_CALI_CTL1, &val); ++ RT711_DAC_DC_CALI_CTL1, &val); + + while (val & RT711_DAC_DC_CALI_TRIGGER) { + if (loop >= 500) { +@@ -119,16 +120,15 @@ static int rt711_calibration(struct rt711_priv *rt711) + + usleep_range(10000, 11000); + rt711_index_read(regmap, RT711_VENDOR_CALI, +- RT711_DAC_DC_CALI_CTL1, &val); ++ RT711_DAC_DC_CALI_CTL1, &val); + } + + /* depop mode */ + rt711_index_update_bits(regmap, RT711_VENDOR_REG, +- RT711_FSM_CTL, 0xf, RT711_DEPOP_CTL); ++ RT711_FSM_CTL, 0xf, RT711_DEPOP_CTL); + + regmap_write(rt711->regmap, +- RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); +- mutex_unlock(&rt711->calibrate_mutex); ++ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + + dev_dbg(dev, "%s calibration complete, ret=%d\n", __func__, ret); + return ret; +@@ -362,24 +362,24 @@ static void rt711_jack_init(struct rt711_priv *rt711) + { + struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt711->component); + +- mutex_lock(&rt711->calibrate_mutex); ++ guard(mutex)(&rt711->calibrate_mutex); + /* power on */ + if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY) + regmap_write(rt711->regmap, +- RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); ++ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + + if (rt711->hs_jack) { + /* unsolicited response & IRQ control */ + regmap_write(rt711->regmap, +- RT711_SET_MIC2_UNSOLICITED_ENABLE, 0x82); ++ RT711_SET_MIC2_UNSOLICITED_ENABLE, 0x82); + regmap_write(rt711->regmap, +- RT711_SET_HP_UNSOLICITED_ENABLE, 0x81); ++ RT711_SET_HP_UNSOLICITED_ENABLE, 0x81); + regmap_write(rt711->regmap, +- RT711_SET_INLINE_UNSOLICITED_ENABLE, 0x83); ++ RT711_SET_INLINE_UNSOLICITED_ENABLE, 0x83); + rt711_index_write(rt711->regmap, RT711_VENDOR_REG, +- 0x10, 0x2420); ++ 0x10, 0x2420); + rt711_index_write(rt711->regmap, RT711_VENDOR_REG, +- 0x19, 0x2e11); ++ 0x19, 0x2e11); + + switch (rt711->jd_src) { + case RT711_JD1: +@@ -449,8 +449,7 @@ static void rt711_jack_init(struct rt711_priv *rt711) + /* power off */ + if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY) + regmap_write(rt711->regmap, +- RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); +- mutex_unlock(&rt711->calibrate_mutex); ++ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + } + + static int rt711_set_jack_detect(struct snd_soc_component *component, +@@ -511,7 +510,7 @@ static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol, + unsigned int read_ll, read_rl; + int i; + +- mutex_lock(&rt711->calibrate_mutex); ++ guard(mutex)(&rt711->calibrate_mutex); + + /* Can't use update bit function, so read the original value first */ + addr_h = mc->reg; +@@ -599,7 +598,6 @@ static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol, + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + +- mutex_unlock(&rt711->calibrate_mutex); + return 0; + } + +@@ -908,11 +906,11 @@ static int rt711_set_bias_level(struct snd_soc_component *component, + break; + + case SND_SOC_BIAS_STANDBY: +- mutex_lock(&rt711->calibrate_mutex); +- regmap_write(rt711->regmap, +- RT711_SET_AUDIO_POWER_STATE, +- AC_PWRST_D3); +- mutex_unlock(&rt711->calibrate_mutex); ++ scoped_guard(mutex, &rt711->calibrate_mutex) { ++ regmap_write(rt711->regmap, ++ RT711_SET_AUDIO_POWER_STATE, ++ AC_PWRST_D3); ++ } + break; + + default: +diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c +--- a/sound/soc/codecs/rt712-sdca-dmic.c ++++ b/sound/soc/codecs/rt712-sdca-dmic.c +@@ -912,10 +912,23 @@ static int rt712_sdca_dmic_dev_resume(struct device *dev) + } + + regcache_cache_only(rt712->regmap, false); +- regcache_sync(rt712->regmap); ++ ret = regcache_sync(rt712->regmap); ++ if (ret) ++ goto err_sync; ++ + regcache_cache_only(rt712->mbq_regmap, false); +- regcache_sync(rt712->mbq_regmap); ++ ret = regcache_sync(rt712->mbq_regmap); ++ if (ret) ++ goto err_sync; ++ + return 0; ++ ++err_sync: ++ regcache_cache_only(rt712->regmap, true); ++ regcache_cache_only(rt712->mbq_regmap, true); ++ regcache_mark_dirty(rt712->regmap); ++ regcache_mark_dirty(rt712->mbq_regmap); ++ return ret; + } + + static const struct dev_pm_ops rt712_sdca_dmic_pm = { +diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c +--- a/sound/soc/codecs/rt712-sdca-sdw.c ++++ b/sound/soc/codecs/rt712-sdca-sdw.c +@@ -6,6 +6,7 @@ + // + // + ++#include + #include + #include + #include +@@ -427,13 +428,13 @@ static int rt712_sdca_dev_system_suspend(struct device *dev) + * deferred work completes and before the parent disables + * interrupts on the link + */ +- mutex_lock(&rt712_sdca->disable_irq_lock); +- rt712_sdca->disable_irq = true; +- ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, +- SDW_SCP_SDCA_INTMASK_SDCA_0, 0); +- ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, +- SDW_SCP_SDCA_INTMASK_SDCA_8, 0); +- mutex_unlock(&rt712_sdca->disable_irq_lock); ++ scoped_guard(mutex, &rt712_sdca->disable_irq_lock) { ++ rt712_sdca->disable_irq = true; ++ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, ++ SDW_SCP_SDCA_INTMASK_SDCA_0, 0); ++ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, ++ SDW_SCP_SDCA_INTMASK_SDCA_8, 0); ++ } + + if (ret1 < 0 || ret2 < 0) { + /* log but don't prevent suspend from happening */ +@@ -455,14 +456,15 @@ static int rt712_sdca_dev_resume(struct device *dev) + return 0; + + if (!slave->unattach_request) { +- mutex_lock(&rt712->disable_irq_lock); +- if (rt712->disable_irq == true) { +- +- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); +- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); +- rt712->disable_irq = false; ++ scoped_guard(mutex, &rt712->disable_irq_lock) { ++ if (rt712->disable_irq) { ++ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, ++ SDW_SCP_SDCA_INTMASK_SDCA_0); ++ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, ++ SDW_SCP_SDCA_INTMASK_SDCA_8); ++ rt712->disable_irq = false; ++ } + } +- mutex_unlock(&rt712->disable_irq_lock); + } + + ret = sdw_slave_wait_for_init(slave, RT712_PROBE_TIMEOUT); +@@ -472,9 +474,20 @@ static int rt712_sdca_dev_resume(struct device *dev) + } + + regcache_cache_only(rt712->regmap, false); +- regcache_sync(rt712->regmap); ++ ret = regcache_sync(rt712->regmap); ++ if (ret) { ++ regcache_cache_only(rt712->regmap, true); ++ return ret; ++ } ++ + regcache_cache_only(rt712->mbq_regmap, false); +- regcache_sync(rt712->mbq_regmap); ++ ret = regcache_sync(rt712->mbq_regmap); ++ if (ret) { ++ regcache_cache_only(rt712->mbq_regmap, true); ++ regcache_cache_only(rt712->regmap, true); ++ return ret; ++ } ++ + return 0; + } + +diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c +--- a/sound/soc/codecs/rt712-sdca.c ++++ b/sound/soc/codecs/rt712-sdca.c +@@ -7,6 +7,7 @@ + // + + #include ++#include + #include + #include + #include +@@ -403,7 +404,7 @@ static void rt712_sdca_btn_check_handler(struct work_struct *work) + + static void rt712_sdca_jack_init(struct rt712_sdca_priv *rt712) + { +- mutex_lock(&rt712->calibrate_mutex); ++ guard(mutex)(&rt712->calibrate_mutex); + + if (rt712->hs_jack) { + /* Enable HID1 event & set button RTC mode */ +@@ -450,8 +451,6 @@ static void rt712_sdca_jack_init(struct rt712_sdca_priv *rt712) + + dev_dbg(&rt712->slave->dev, "in %s disable\n", __func__); + } +- +- mutex_unlock(&rt712->calibrate_mutex); + } + + static int rt712_sdca_set_jack_detect(struct snd_soc_component *component, +diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c +--- a/sound/soc/codecs/rt721-sdca-sdw.c ++++ b/sound/soc/codecs/rt721-sdca-sdw.c +@@ -6,6 +6,7 @@ + // + // + ++#include + #include + #include + #include +@@ -466,13 +467,13 @@ static int rt721_sdca_dev_system_suspend(struct device *dev) + * deferred work completes and before the parent disables + * interrupts on the link + */ +- mutex_lock(&rt721_sdca->disable_irq_lock); +- rt721_sdca->disable_irq = true; +- ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, +- SDW_SCP_SDCA_INTMASK_SDCA_0, 0); +- ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, +- SDW_SCP_SDCA_INTMASK_SDCA_8, 0); +- mutex_unlock(&rt721_sdca->disable_irq_lock); ++ scoped_guard(mutex, &rt721_sdca->disable_irq_lock) { ++ rt721_sdca->disable_irq = true; ++ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, ++ SDW_SCP_SDCA_INTMASK_SDCA_0, 0); ++ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, ++ SDW_SCP_SDCA_INTMASK_SDCA_8, 0); ++ } + + if (ret1 < 0 || ret2 < 0) { + /* log but don't prevent suspend from happening */ +@@ -494,13 +495,15 @@ static int rt721_sdca_dev_resume(struct device *dev) + return 0; + + if (!slave->unattach_request) { +- mutex_lock(&rt721->disable_irq_lock); +- if (rt721->disable_irq == true) { +- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); +- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); +- rt721->disable_irq = false; ++ scoped_guard(mutex, &rt721->disable_irq_lock) { ++ if (rt721->disable_irq) { ++ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, ++ SDW_SCP_SDCA_INTMASK_SDCA_0); ++ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, ++ SDW_SCP_SDCA_INTMASK_SDCA_8); ++ rt721->disable_irq = false; ++ } + } +- mutex_unlock(&rt721->disable_irq_lock); + } + + ret = sdw_slave_wait_for_init(slave, RT721_PROBE_TIMEOUT); +@@ -510,9 +513,20 @@ static int rt721_sdca_dev_resume(struct device *dev) + } + + regcache_cache_only(rt721->regmap, false); +- regcache_sync(rt721->regmap); ++ ret = regcache_sync(rt721->regmap); ++ if (ret) { ++ regcache_cache_only(rt721->regmap, true); ++ return ret; ++ } ++ + regcache_cache_only(rt721->mbq_regmap, false); +- regcache_sync(rt721->mbq_regmap); ++ ret = regcache_sync(rt721->mbq_regmap); ++ if (ret) { ++ regcache_cache_only(rt721->mbq_regmap, true); ++ regcache_cache_only(rt721->regmap, true); ++ return ret; ++ } ++ + return 0; + } + +diff --git a/sound/soc/codecs/rt721-sdca.c b/sound/soc/codecs/rt721-sdca.c +--- a/sound/soc/codecs/rt721-sdca.c ++++ b/sound/soc/codecs/rt721-sdca.c +@@ -5,7 +5,7 @@ + // Copyright(c) 2024 Realtek Semiconductor Corp. + // + // +- ++#include + #include + #include + #include +@@ -289,7 +289,7 @@ static void rt721_sdca_jack_preset(struct rt721_sdca_priv *rt721) + + static void rt721_sdca_jack_init(struct rt721_sdca_priv *rt721) + { +- mutex_lock(&rt721->calibrate_mutex); ++ guard(mutex)(&rt721->calibrate_mutex); + if (rt721->hs_jack) { + sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0); +@@ -309,7 +309,6 @@ static void rt721_sdca_jack_init(struct rt721_sdca_priv *rt721) + rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT, + RT721_GE_REL_CTRL1, 0x4000, 0x4000); + } +- mutex_unlock(&rt721->calibrate_mutex); + } + + static int rt721_sdca_set_jack_detect(struct snd_soc_component *component, +diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c +--- a/sound/soc/codecs/rt722-sdca-sdw.c ++++ b/sound/soc/codecs/rt722-sdca-sdw.c +@@ -6,6 +6,7 @@ + // + // + ++#include + #include + #include + #include +@@ -517,13 +518,13 @@ static int rt722_sdca_dev_system_suspend(struct device *dev) + * deferred work completes and before the parent disables + * interrupts on the link + */ +- mutex_lock(&rt722_sdca->disable_irq_lock); +- rt722_sdca->disable_irq = true; +- ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, +- SDW_SCP_SDCA_INTMASK_SDCA_0, 0); +- ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, +- SDW_SCP_SDCA_INTMASK_SDCA_8, 0); +- mutex_unlock(&rt722_sdca->disable_irq_lock); ++ scoped_guard(mutex, &rt722_sdca->disable_irq_lock) { ++ rt722_sdca->disable_irq = true; ++ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, ++ SDW_SCP_SDCA_INTMASK_SDCA_0, 0); ++ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, ++ SDW_SCP_SDCA_INTMASK_SDCA_8, 0); ++ } + + if (ret1 < 0 || ret2 < 0) { + /* log but don't prevent suspend from happening */ +@@ -545,13 +546,15 @@ static int rt722_sdca_dev_resume(struct device *dev) + return 0; + + if (!slave->unattach_request) { +- mutex_lock(&rt722->disable_irq_lock); +- if (rt722->disable_irq == true) { +- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); +- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); +- rt722->disable_irq = false; ++ scoped_guard(mutex, &rt722->disable_irq_lock) { ++ if (rt722->disable_irq) { ++ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, ++ SDW_SCP_SDCA_INTMASK_SDCA_0); ++ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, ++ SDW_SCP_SDCA_INTMASK_SDCA_8); ++ rt722->disable_irq = false; ++ } + } +- mutex_unlock(&rt722->disable_irq_lock); + } + + ret = sdw_slave_wait_for_init(slave, RT722_PROBE_TIMEOUT); +@@ -561,7 +564,13 @@ static int rt722_sdca_dev_resume(struct device *dev) + } + + regcache_cache_only(rt722->regmap, false); +- regcache_sync(rt722->regmap); ++ ret = regcache_sync(rt722->regmap); ++ if (ret) { ++ regcache_cache_only(rt722->regmap, true); ++ regcache_mark_dirty(rt722->regmap); ++ return ret; ++ } ++ + return 0; + } + +diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c +--- a/sound/soc/codecs/rt722-sdca.c ++++ b/sound/soc/codecs/rt722-sdca.c +@@ -6,6 +6,7 @@ + // + // + ++#include + #include + #include + #include +@@ -294,7 +295,7 @@ static void rt722_sdca_btn_check_handler(struct work_struct *work) + + static void rt722_sdca_jack_init(struct rt722_sdca_priv *rt722) + { +- mutex_lock(&rt722->calibrate_mutex); ++ guard(mutex)(&rt722->calibrate_mutex); + if (rt722->hs_jack) { + /* set SCP_SDCA_IntMask1[0]=1 */ + sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK1, +@@ -317,7 +318,6 @@ static void rt722_sdca_jack_init(struct rt722_sdca_priv *rt722) + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_HDA_CTL, + RT722_GE_RELATED_CTL2, 0x4000, 0x4000); + } +- mutex_unlock(&rt722->calibrate_mutex); + } + + static int rt722_sdca_set_jack_detect(struct snd_soc_component *component, +@@ -352,8 +352,6 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) + static const char func_tag[] = "FUNC"; + static const char xu_tag[] = "XU"; + const char *dmi_vendor, *dmi_product, *dmi_sku; +- char *cae_filename; +- const struct firmware *cae_fw = NULL; + unsigned int cae_st_spk, cae_st_hp, cae_st_mic; + unsigned int func, value; + unsigned int combined_val; +@@ -385,7 +383,8 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) + space = strchr(dmi_sku, ' '); + s_len = space ? space - dmi_sku : strlen(dmi_sku); + +- cae_filename = kasprintf(GFP_KERNEL, ++ char *cae_filename __free(kfree) = ++ kasprintf(GFP_KERNEL, + "realtek/rt722/rt722_RAE_%.*s_%.*s_%.*s.dat", + v_len, dmi_vendor, + p_len, dmi_product, +@@ -399,8 +398,8 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) + regmap_write(rt722->regmap, RT722_MIC_CAE_PARAM39, 0x5f); + usleep_range(50000, 60000); + ++ const struct firmware *cae_fw __free(firmware) = NULL; + request_firmware(&cae_fw, cae_filename, dev); +- kfree(cae_filename); + if (!cae_fw) { + dev_err(dev, "%s: Failed to load CAE firmware\n", __func__); + return -ENOENT; +@@ -555,7 +554,6 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) + regcache_cache_bypass(rt722->regmap, false); + rt722->cae_update_done = 1; + dev_dbg(dev, "%s: CAE FW update done.\n", __func__); +- release_firmware(cae_fw); + return 0; + + verify_abort: +@@ -565,7 +563,6 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) + out_release: + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG, + RT722_MISC_CTRL1, 0x8000, 0x0000); +- release_firmware(cae_fw); + dev_err(dev, "%s: CAE FW update aborted (ret=%d).\n", __func__, ret); + return ret; + } +@@ -1906,6 +1903,9 @@ int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave) + rt722_sdca_amp_preset(rt722); + rt722_sdca_jack_preset(rt722); + ++ if (rt722->hs_jack && (!rt722->first_hw_init)) ++ rt722_sdca_jack_init(rt722); ++ + if (rt722->first_hw_init) { + regcache_cache_bypass(rt722->regmap, false); + regcache_mark_dirty(rt722->regmap); +diff --git a/sound/soc/codecs/rt766-sdca-sdw.c b/sound/soc/codecs/rt766-sdca-sdw.c +new file mode 100644 +--- /dev/null ++++ b/sound/soc/codecs/rt766-sdca-sdw.c +@@ -0,0 +1,339 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++// ++// rt766-sdca-sdw.c -- rt766 SDCA ALSA SoC audio driver ++// ++// Copyright(c) 2026 Realtek Semiconductor Corp. ++// ++// ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "rt766-sdca.h" ++#include "rt766-sdca-sdw.h" ++ ++#define RT766_PROBE_TIMEOUT 5000 ++ ++static bool rt766_sdca_readable_register(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case SDW_SCP_SDCA_INT1 ... SDW_SCP_SDCA_INTMASK4: ++ case RT766_VERSION_ID ... RT766_BOND_LATCH_ID: ++ case 0xc344 ... 0xc345: ++ case 0xc900: ++ case 0xc920: ++ case 0xd540 ... 0xd542: ++ case 0xf01e: ++ case RT766_HP_POWER_STATE ... RT766_HP_FSM_CTL2_1: ++ case 0x310100: ++ case RT766_MCU_PATCH_ADDR1_START ... RT766_MCU_PATCH_ADDR1_END: ++ case RT766_MCU_PATCH_ADDR2_START ... RT766_MCU_PATCH_ADDR2_END: ++ case RT766_MUTE_REG(UAJ, USER_FU41, 1): ++ case RT766_MUTE_REG(UAJ, USER_FU41, 2): ++ case RT766_VOLUME_REG(UAJ, USER_FU41, 1): ++ case RT766_VOLUME_REG(UAJ, USER_FU41, 2): ++ case RT766_MUTE_REG(UAJ, USER_FU36, 1): ++ case RT766_MUTE_REG(UAJ, USER_FU36, 2): ++ case RT766_VOLUME_REG(UAJ, USER_FU36, 1): ++ case RT766_VOLUME_REG(UAJ, USER_FU36, 2): ++ case RT766_PDE_REQ_REG(UAJ, PDE47): ++ case RT766_PDE_REQ_REG(UAJ, PDE34): ++ case RT766_SDCA_CTL(UAJ, CS41, SDCA_CTL_CS_SAMPLERATEINDEX): ++ case RT766_SDCA_CTL(UAJ, CS36, SDCA_CTL_CS_SAMPLERATEINDEX): ++ case RT766_FUNC_STATUS_REG(UAJ): /* 0x40480000 */ ++ case RT766_PDE_ACTUAL_REG(UAJ, PDE47): /* 0x40481400 */ ++ case RT766_PDE_ACTUAL_REG(UAJ, PDE34): /* 0x40481480 */ ++ case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1): ++ case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2): ++ case RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_SELECTED_MODE): ++ case RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_DETECTED_MODE): /* 0x40600490 */ ++ case RT766_PDE_REQ_REG(MIC, PDE11): ++ case RT766_MUTE_REG(MIC, USER_FU113, 1): ++ case RT766_MUTE_REG(MIC, USER_FU113, 2): ++ case RT766_MUTE_REG(MIC, USER_FU113, 3): ++ case RT766_MUTE_REG(MIC, USER_FU113, 4): ++ case RT766_VOLUME_REG(MIC, USER_FU113, 1): ++ case RT766_VOLUME_REG(MIC, USER_FU113, 2): ++ case RT766_VOLUME_REG(MIC, USER_FU113, 3): ++ case RT766_VOLUME_REG(MIC, USER_FU113, 4): ++ case RT766_FUNC_STATUS_REG(MIC): /* 0x40880000 */ ++ case RT766_SDCA_CTL(MIC, CS113, SDCA_CTL_CS_SAMPLERATEINDEX): ++ case RT766_PDE_ACTUAL_REG(MIC, PDE11): /* 0x40881500 */ ++ case RT766_FUNC_STATUS_REG(HID): /* 0x40c80000 */ ++ /* 0x40c80080 - 0x40c80098 */ ++ case RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_CURRENTOWNER) ... ++ RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_MESSAGELENGTH): ++ case RT766_MUTE_REG(AMP, USER_FU21, 1): ++ case RT766_MUTE_REG(AMP, USER_FU21, 2): ++ case RT766_VOLUME_REG(AMP, USER_FU21, 1): ++ case RT766_VOLUME_REG(AMP, USER_FU21, 2): ++ case RT766_PDE_REQ_REG(AMP, PDE23): ++ case RT766_FUNC_STATUS_REG(AMP): /* 0x41080000 */ ++ case RT766_SDCA_CTL(AMP, PPU21, SDCA_CTL_PPU_POSTURENUMBER): ++ case RT766_SDCA_CTL(AMP, CS21, SDCA_CTL_CS_SAMPLERATEINDEX): ++ case RT766_PDE_ACTUAL_REG(AMP, PDE23): /* 0x41081980 */ ++ case RT766_BUF_ADDR_HID1 ... RT766_BUF_ADDR_HID2: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static bool rt766_sdca_volatile_register(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case SDW_SCP_SDCA_INT1 ... SDW_SCP_SDCA_INTMASK4: ++ case RT766_VERSION_ID ... RT766_BOND_LATCH_ID: ++ case 0xc344 ... 0xc345: ++ case 0xc900: ++ case 0xc920: ++ case 0xd540 ... 0xd542: ++ case 0xf01e: ++ case RT766_HP_POWER_STATE ... RT766_HP_FSM_CTL2_1: ++ case 0x310100: ++ case RT766_MCU_PATCH_ADDR1_START ... RT766_MCU_PATCH_ADDR1_END: ++ case RT766_MCU_PATCH_ADDR2_START ... RT766_MCU_PATCH_ADDR2_END: ++ case RT766_FUNC_STATUS_REG(UAJ): ++ case RT766_PDE_ACTUAL_REG(UAJ, PDE47): ++ case RT766_PDE_ACTUAL_REG(UAJ, PDE34): ++ case RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_DETECTED_MODE): ++ case RT766_FUNC_STATUS_REG(MIC): ++ case RT766_PDE_ACTUAL_REG(MIC, PDE11): ++ case RT766_FUNC_STATUS_REG(HID): ++ case RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_CURRENTOWNER) ... ++ RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_MESSAGELENGTH): ++ case RT766_FUNC_STATUS_REG(AMP): ++ case RT766_PDE_ACTUAL_REG(AMP, PDE23): ++ case RT766_BUF_ADDR_HID1 ... RT766_BUF_ADDR_HID2: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static int rt766_sdca_mbq_size(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case RT766_VOLUME_REG(UAJ, USER_FU41, 1): ++ case RT766_VOLUME_REG(UAJ, USER_FU41, 2): ++ case RT766_VOLUME_REG(UAJ, USER_FU36, 1): ++ case RT766_VOLUME_REG(UAJ, USER_FU36, 2): ++ case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1): ++ case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2): ++ case RT766_VOLUME_REG(MIC, USER_FU113, 1): ++ case RT766_VOLUME_REG(MIC, USER_FU113, 2): ++ case RT766_VOLUME_REG(MIC, USER_FU113, 3): ++ case RT766_VOLUME_REG(MIC, USER_FU113, 4): ++ case RT766_VOLUME_REG(AMP, USER_FU21, 1): ++ case RT766_VOLUME_REG(AMP, USER_FU21, 2): ++ return 2; ++ default: ++ return 1; ++ } ++} ++ ++static const struct regmap_sdw_mbq_cfg rt766_sdca_mbq_cfg = { ++ .mbq_size = rt766_sdca_mbq_size, ++}; ++ ++static const struct regmap_config rt766_sdca_regmap = { ++ .reg_bits = 32, ++ .val_bits = 16, ++ .readable_reg = rt766_sdca_readable_register, ++ .volatile_reg = rt766_sdca_volatile_register, ++ .reg_defaults = rt766_sdca_defaults, ++ .num_reg_defaults = ARRAY_SIZE(rt766_sdca_defaults), ++ .max_register = SDW_SDCA_MAX_REGISTER, ++ .cache_type = REGCACHE_MAPLE, ++ .use_single_read = true, ++ .use_single_write = true, ++}; ++ ++static int rt766_sdca_update_status(struct sdw_slave *slave, ++ enum sdw_slave_status status) ++{ ++ struct rt766_sdca_priv *rt766 = dev_get_drvdata(&slave->dev); ++ ++ if (status == SDW_SLAVE_UNATTACHED) ++ rt766->hw_init = false; ++ ++ if (status == SDW_SLAVE_ATTACHED) { ++ if (rt766->hs_jack) { ++ /* ++ * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then ++ * if the device attached again, we will need to set the setting back. ++ * It could avoid losing the jack detection interrupt. ++ * This also could sync with the cache value as the rt766_sdca_jack_init set. ++ */ ++ sdw_write_no_pm(rt766->slave, SDW_SCP_SDCA_INTMASK3, ++ SDW_SCP_SDCA_INTMASK_SDCA_16); ++ sdw_write_no_pm(rt766->slave, SDW_SCP_SDCA_INTMASK4, ++ SDW_SCP_SDCA_INTMASK_SDCA_24); ++ } ++ } ++ ++ /* ++ * Perform initialization only if slave status is present and ++ * hw_init flag is false ++ */ ++ if (rt766->hw_init || status != SDW_SLAVE_ATTACHED) ++ return 0; ++ ++ /* perform I/O transfers required for Slave initialization */ ++ return rt766_sdca_io_init(&slave->dev, slave); ++} ++ ++static int rt766_sdca_read_prop(struct sdw_slave *slave) ++{ ++ struct sdw_slave_prop *prop = &slave->prop; ++ int ret; ++ ++ ret = sdw_slave_read_prop(slave); ++ if (ret < 0) ++ return ret; ++ ++ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; ++ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; ++ /* ++ * SDCA interrupts are routed through SoundWire domain IRQ. ++ */ ++ prop->use_domain_irq = true; ++ ++ return 0; ++} ++ ++static const struct sdw_slave_ops rt766_sdca_slave_ops = { ++ .read_prop = rt766_sdca_read_prop, ++ .update_status = rt766_sdca_update_status, ++}; ++ ++static int rt766_sdca_sdw_probe(struct sdw_slave *slave, ++ const struct sdw_device_id *id) ++{ ++ struct regmap *regmap; ++ ++ /* Regmap Initialization */ ++ regmap = devm_regmap_init_sdw_mbq_cfg(&slave->dev, slave, ++ &rt766_sdca_regmap, &rt766_sdca_mbq_cfg); ++ if (IS_ERR(regmap)) ++ return PTR_ERR(regmap); ++ ++ return rt766_sdca_init(&slave->dev, regmap, slave); ++} ++ ++static void rt766_sdca_sdw_remove(struct sdw_slave *slave) ++{ ++ pm_runtime_disable(&slave->dev); ++} ++ ++static const struct sdw_device_id rt766_sdca_id[] = { ++ SDW_SLAVE_ENTRY_EXT(0x025d, 0x766, 0x3, 0x1, 0), ++ SDW_SLAVE_ENTRY_EXT(0x025d, 0x767, 0x3, 0x1, 0), ++ {}, ++}; ++MODULE_DEVICE_TABLE(sdw, rt766_sdca_id); ++ ++static int rt766_sdca_dev_suspend(struct device *dev) ++{ ++ struct rt766_sdca_priv *rt766 = dev_get_drvdata(dev); ++ ++ if (!rt766->hw_init) ++ return 0; ++ ++ regcache_cache_only(rt766->regmap, true); ++ return 0; ++} ++ ++static int rt766_sdca_dev_system_suspend(struct device *dev) ++{ ++ struct rt766_sdca_priv *rt766 = dev_get_drvdata(dev); ++ struct sdw_slave *slave = dev_to_sdw_dev(dev); ++ int ret1, ret2; ++ ++ if (!rt766->hw_init) ++ return 0; ++ ++ /* ++ * prevent new interrupts from being handled after the ++ * deferred work completes and before the parent disables ++ * interrupts on the link ++ */ ++ mutex_lock(&rt766->disable_irq_lock); ++ rt766->disable_irq = true; ++ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK3, ++ SDW_SCP_SDCA_INTMASK_SDCA_16, 0); ++ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK4, ++ SDW_SCP_SDCA_INTMASK_SDCA_24, 0); ++ mutex_unlock(&rt766->disable_irq_lock); ++ ++ if (ret1 < 0 || ret2 < 0) { ++ /* log but don't prevent suspend from happening */ ++ dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__); ++ } ++ ++ return rt766_sdca_dev_suspend(dev); ++} ++ ++static int rt766_sdca_dev_resume(struct device *dev) ++{ ++ struct sdw_slave *slave = dev_to_sdw_dev(dev); ++ struct rt766_sdca_priv *rt766 = dev_get_drvdata(dev); ++ int ret; ++ ++ if (!rt766->first_hw_init) ++ return 0; ++ ++ if (!slave->unattach_request) { ++ mutex_lock(&rt766->disable_irq_lock); ++ if (rt766->disable_irq == true) { ++ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK3, SDW_SCP_SDCA_INTMASK_SDCA_16); ++ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK4, SDW_SCP_SDCA_INTMASK_SDCA_24); ++ rt766->disable_irq = false; ++ } ++ mutex_unlock(&rt766->disable_irq_lock); ++ goto regmap_sync; ++ } ++ ++ ret = sdw_slave_wait_for_init(slave, RT766_PROBE_TIMEOUT); ++ if (ret) { ++ sdw_show_ping_status(slave->bus, true); ++ return ret; ++ } ++ ++regmap_sync: ++ regcache_cache_only(rt766->regmap, false); ++ ret = regcache_sync(rt766->regmap); ++ if (ret) { ++ regcache_cache_only(rt766->regmap, true); ++ regcache_mark_dirty(rt766->regmap); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops rt766_sdca_pm = { ++ SYSTEM_SLEEP_PM_OPS(rt766_sdca_dev_system_suspend, rt766_sdca_dev_resume) ++ RUNTIME_PM_OPS(rt766_sdca_dev_suspend, rt766_sdca_dev_resume, NULL) ++}; ++ ++static struct sdw_driver rt766_sdca_sdw_driver = { ++ .driver = { ++ .name = "rt766-sdca", ++ .pm = pm_ptr(&rt766_sdca_pm), ++ }, ++ .probe = rt766_sdca_sdw_probe, ++ .remove = rt766_sdca_sdw_remove, ++ .ops = &rt766_sdca_slave_ops, ++ .id_table = rt766_sdca_id, ++}; ++module_sdw_driver(rt766_sdca_sdw_driver); ++ ++MODULE_DESCRIPTION("ASoC RT766 SDCA SDW driver"); ++MODULE_AUTHOR("Shuming Fan "); ++MODULE_LICENSE("GPL"); ++MODULE_IMPORT_NS("SND_SOC_SDCA"); +diff --git a/sound/soc/codecs/rt766-sdca-sdw.h b/sound/soc/codecs/rt766-sdca-sdw.h +new file mode 100644 +--- /dev/null ++++ b/sound/soc/codecs/rt766-sdca-sdw.h +@@ -0,0 +1,61 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * rt766-sdca-sdw.h -- RT766 SDCA ALSA SoC audio driver header ++ * ++ * Copyright(c) 2026 Realtek Semiconductor Corp. ++ */ ++ ++#ifndef __RT766_SDW_H__ ++#define __RT766_SDW_H__ ++ ++#include ++#include ++ ++static const struct reg_default rt766_sdca_defaults[] = { ++ /* 0x40400289 - 0x4040028a */ ++ { RT766_MUTE_REG(UAJ, USER_FU41, 1), 0x01 }, ++ { RT766_MUTE_REG(UAJ, USER_FU41, 2), 0x01 }, ++ /* 0x40400291 - 0x40400292 */ ++ { RT766_VOLUME_REG(UAJ, USER_FU41, 1), 0x0000 }, ++ { RT766_VOLUME_REG(UAJ, USER_FU41, 2), 0x0000 }, ++ /* 0x40400789 - 0x4040078a */ ++ { RT766_MUTE_REG(UAJ, USER_FU36, 1), 0x01 }, ++ { RT766_MUTE_REG(UAJ, USER_FU36, 2), 0x01 }, ++ /* 0x40400791 - 0x40400792 */ ++ { RT766_VOLUME_REG(UAJ, USER_FU36, 1), 0x0000 }, ++ { RT766_VOLUME_REG(UAJ, USER_FU36, 2), 0x0000 }, ++ { RT766_PDE_REQ_REG(UAJ, PDE47), 0x03 }, /* 0x40401408 */ ++ { RT766_PDE_REQ_REG(UAJ, PDE34), 0x03 }, /* 0x40401488 */ ++ { RT766_SDCA_CTL(UAJ, CS41, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 }, /* 0x40480080 */ ++ { RT766_SDCA_CTL(UAJ, CS36, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 }, /* 0x40480880 */ ++ /* 0x40600259 - 0x4060025a */ ++ { RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1), 0xfe00 }, ++ { RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2), 0xfe00 }, ++ { RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_SELECTED_MODE), 0x00 }, /* 0x40600488 */ ++ ++ { RT766_PDE_REQ_REG(MIC, PDE11), 0x03 }, /* 0x40801508 */ ++ /* 0x40801809 - 0x4080180c */ ++ { RT766_MUTE_REG(MIC, USER_FU113, 1), 0x01 }, ++ { RT766_MUTE_REG(MIC, USER_FU113, 2), 0x01 }, ++ { RT766_MUTE_REG(MIC, USER_FU113, 3), 0x01 }, ++ { RT766_MUTE_REG(MIC, USER_FU113, 4), 0x01 }, ++ /* 0x40801811 - 0x40801814 */ ++ { RT766_VOLUME_REG(MIC, USER_FU113, 1), 0x0000 }, ++ { RT766_VOLUME_REG(MIC, USER_FU113, 2), 0x0000 }, ++ { RT766_VOLUME_REG(MIC, USER_FU113, 3), 0x0000 }, ++ { RT766_VOLUME_REG(MIC, USER_FU113, 4), 0x0000 }, ++ { RT766_SDCA_CTL(MIC, CS113, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 }, /* 0x40880900 */ ++ ++ /* 0x41000189 - 0x4100018a */ ++ { RT766_MUTE_REG(AMP, USER_FU21, 1), 0x01 }, ++ { RT766_MUTE_REG(AMP, USER_FU21, 2), 0x01 }, ++ /* 0x41000191 - 0x41000192 */ ++ { RT766_VOLUME_REG(AMP, USER_FU21, 1), 0x0000 }, ++ { RT766_VOLUME_REG(AMP, USER_FU21, 2), 0x0000 }, ++ { RT766_PDE_REQ_REG(AMP, PDE23), 0x03 }, /* 0x41001988 */ ++ { RT766_SDCA_CTL(AMP, PPU21, SDCA_CTL_PPU_POSTURENUMBER), 0x00 }, /* 0x41080200 */ ++ { RT766_SDCA_CTL(AMP, CS21, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 }, /* 0x41081080 */ ++ ++}; ++ ++#endif /* __RT766_SDW_H__ */ +diff --git a/sound/soc/codecs/rt766-sdca.c b/sound/soc/codecs/rt766-sdca.c +new file mode 100644 +--- /dev/null ++++ b/sound/soc/codecs/rt766-sdca.c +@@ -0,0 +1,1378 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++// ++// rt766-sdca.c -- rt766 SDCA ALSA SoC audio driver ++// ++// Copyright(c) 2026 Realtek Semiconductor Corp. ++// ++// ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "rt766-sdca.h" ++#include "rt-sdw-common.h" ++ ++static int rt766_sdca_btn_detect(struct sdca_interrupt *interrupt) ++{ ++ struct rt766_sdca_priv *rt766 = interrupt->priv; ++ unsigned char *buf = NULL; ++ unsigned int offset, owner, length; ++ unsigned int det_mode, idx, val; ++ int ret; ++ ++ ret = regmap_read(rt766->regmap, ++ RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_DETECTED_MODE), ++ &det_mode); ++ if (ret < 0) ++ goto io_error; ++ ++ /* get current UMP message owner */ ++ ret = regmap_read(rt766->regmap, ++ RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_CURRENTOWNER), ++ &owner); ++ if (ret < 0) ++ goto io_error; ++ ++ /* if owner is device then there is no button event from device */ ++ if (owner == 1) ++ return 0; ++ ++ if (det_mode) { ++ /* read UMP message length */ ++ ret = regmap_read(rt766->regmap, ++ RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_MESSAGELENGTH), ++ &length); ++ if (ret < 0) ++ goto _end_btn_det_; ++ ++ /* read UMP message offset */ ++ ret = regmap_read(rt766->regmap, ++ RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_MESSAGEOFFSET), ++ &offset); ++ if (ret < 0) ++ goto _end_btn_det_; ++ ++ buf = devm_kzalloc(&rt766->slave->dev, length, GFP_KERNEL); ++ if (!buf) { ++ dev_err(&rt766->slave->dev, "%s: alloc buf failed\n", __func__); ++ goto _end_btn_det_; ++ } ++ ++ for (idx = 0; idx < length; idx++) { ++ ret = regmap_read(rt766->regmap, ++ RT766_BUF_ADDR_HID1 + offset + idx, &val); ++ if (ret < 0) ++ goto _end_btn_det_; ++ buf[idx] = val & 0xff; ++ } ++ ++ if (rt766->hid) ++ hid_input_report(rt766->hid, HID_INPUT_REPORT, ++ buf, length, 1); ++ } ++ ++_end_btn_det_: ++ if (buf) ++ devm_kfree(&rt766->slave->dev, buf); ++ ++ /* Host is owner, so set back to device */ ++ if (owner == 0) { ++ regmap_write(rt766->regmap, ++ RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_CURRENTOWNER), 0x01); ++ } ++ ++ return 0; ++ ++io_error: ++ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); ++ return ret; ++} ++ ++static irqreturn_t rt766_sdca_irq_btn_handler(int irq, void *data) ++{ ++ struct sdca_interrupt *interrupt = data; ++ struct rt766_sdca_priv *rt766 = interrupt->priv; ++ ++ if (!rt766->hs_jack) ++ return IRQ_HANDLED; ++ ++ if (!rt766->component->card || !rt766->component->card->instantiated) ++ return IRQ_HANDLED; ++ ++ mutex_lock(&rt766->disable_irq_lock); ++ if (!rt766->disable_irq) ++ rt766_sdca_btn_detect(interrupt); ++ mutex_unlock(&rt766->disable_irq_lock); ++ return IRQ_HANDLED; ++} ++ ++static int rt766_sdca_headset_detect(struct rt766_sdca_priv *rt766) ++{ ++ unsigned int det_mode; ++ int ret; ++ ++ /* get detected_mode */ ++ ret = regmap_read(rt766->regmap, ++ RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_DETECTED_MODE), ++ &det_mode); ++ if (ret < 0) ++ goto io_error; ++ ++ switch (det_mode) { ++ case 0x00: ++ rt766->jack_type = 0; ++ break; ++ case 0x03: ++ rt766->jack_type = SND_JACK_HEADPHONE; ++ break; ++ case 0x05: ++ rt766->jack_type = SND_JACK_HEADSET; ++ break; ++ } ++ ++ /* write selected_mode */ ++ if (det_mode) { ++ ret = regmap_write(rt766->regmap, ++ RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_SELECTED_MODE), ++ det_mode); ++ if (ret < 0) ++ goto io_error; ++ } ++ ++ dev_dbg(&rt766->slave->dev, ++ "%s, detected_mode=0x%x\n", __func__, det_mode); ++ ++ return 0; ++ ++io_error: ++ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); ++ return ret; ++} ++ ++static irqreturn_t rt766_sdca_irq_jd_handler(int irq, void *data) ++{ ++ struct sdca_interrupt *interrupt = data; ++ struct rt766_sdca_priv *rt766 = interrupt->priv; ++ ++ if (!rt766->hs_jack) ++ return IRQ_HANDLED; ++ ++ if (!rt766->component->card || !rt766->component->card->instantiated) ++ return IRQ_HANDLED; ++ ++ mutex_lock(&rt766->disable_irq_lock); ++ if (!rt766->disable_irq) ++ rt766_sdca_headset_detect(rt766); ++ mutex_unlock(&rt766->disable_irq_lock); ++ ++ dev_dbg(&rt766->slave->dev, ++ "in %s, jack_type=%d\n", __func__, rt766->jack_type); ++ ++ snd_soc_jack_report(rt766->hs_jack, rt766->jack_type, SND_JACK_HEADSET); ++ return IRQ_HANDLED; ++} ++ ++static void rt766_sdca_destroy_hid_device(struct sdca_interrupt *interrupt) ++{ ++ struct rt766_sdca_priv *rt766 = interrupt->priv; ++ ++ hid_destroy_device(rt766->hid); ++} ++ ++static int rt766_sdca_irq_ctl(struct rt766_sdca_priv *rt766, ++ struct sdca_function_data *function, ++ struct snd_soc_component *component, ++ struct sdca_interrupt_info *info, ++ bool enabled) ++{ ++ struct device *dev = &rt766->slave->dev; ++ struct sdca_interrupt *interrupt; ++ struct sdca_control *control; ++ struct sdca_entity *entity; ++ irq_handler_t handler; ++ int i, j, irq, ret; ++ ++ for (i = 0; i < function->num_entities; i++) { ++ entity = &function->entities[i]; ++ ++ for (j = 0; j < entity->num_controls; j++) { ++ control = &entity->controls[j]; ++ irq = control->interrupt_position; ++ ++ switch (SDCA_CTL_TYPE(entity->type, control->sel)) { ++ case SDCA_CTL_TYPE_S(GE, DETECTED_MODE): ++ handler = rt766_sdca_irq_jd_handler; ++ break; ++ case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER): ++ handler = rt766_sdca_irq_btn_handler; ++ break; ++ default: ++ continue; ++ } ++ ++ interrupt = &info->irqs[irq]; ++ ++ if (enabled) { ++ ret = sdca_irq_data_populate(dev, rt766->regmap, component, ++ function, entity, control, ++ interrupt); ++ if (ret) ++ return ret; ++ ++ if (handler == rt766_sdca_irq_btn_handler) { ++ ret = sdca_add_hid_device(interrupt); ++ if (ret) ++ return ret; ++ ++ interrupt->free_priv = rt766_sdca_destroy_hid_device; ++ rt766->hid = interrupt->priv; ++ } ++ ++ interrupt->priv = rt766; ++ ret = sdca_irq_request(dev, info, irq, interrupt->name, ++ handler, interrupt); ++ if (ret) { ++ dev_err(dev, "failed to request irq %s: %d\n", ++ interrupt->name, ret); ++ sdca_irq_cleanup_late(dev, function, info); ++ return ret; ++ } ++ dev_dbg(dev, "Requesting IRQ %d InterruptName=%s\n", irq, interrupt->name); ++ } else { ++ sdca_irq_cleanup_late(dev, function, info); ++ dev_dbg(dev, "Freeing IRQ %d\n", irq); ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++static int rt766_sdca_set_jack_detect(struct snd_soc_component *component, ++ struct snd_soc_jack *hs_jack, void *data) ++{ ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ int ret; ++ ++ if (!rt766->uaj_func_data) { ++ dev_err(&rt766->slave->dev, "The SDCA UAJ function is not supported.\n"); ++ return -EINVAL; ++ } ++ ++ rt766->hs_jack = hs_jack; ++ ++ if (!rt766->first_hw_init) ++ return 0; ++ ++ ret = pm_runtime_resume_and_get(component->dev); ++ if (ret < 0) { ++ if (ret != -EACCES) { ++ dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret); ++ return ret; ++ } ++ ++ /* pm_runtime not enabled yet */ ++ dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__); ++ return 0; ++ } ++ ++ /* disable interrupts if hs_jack is not set */ ++ if (!rt766->hs_jack) { ++ if (rt766->uaj_func_data) ++ rt766_sdca_irq_ctl(rt766, rt766->uaj_func_data, ++ rt766->component, rt766->irq_info, false); ++ ++ if (rt766->hid_func_data) ++ rt766_sdca_irq_ctl(rt766, rt766->hid_func_data, ++ rt766->component, rt766->irq_info, false); ++ } ++ ++ pm_runtime_put_autosuspend(component->dev); ++ ++ return 0; ++} ++ ++static int rt766_sdca_set_fu_ctl(struct rt766_sdca_priv *rt766, int func_num, int fu_num) ++{ ++ unsigned int fu01_reg, fu02_reg; ++ unsigned int ch_01, ch_02; ++ unsigned int ch_mute; ++ unsigned int fu_reg; ++ int err, i; ++ ++ switch (fu_num) { ++ case RT766_SDCA_ENT_USER_FU41: ++ ch_01 = (rt766->fu41_dapm_mute || rt766->fu41_mixer_l_mute) ? 0x01 : 0x00; ++ ch_02 = (rt766->fu41_dapm_mute || rt766->fu41_mixer_r_mute) ? 0x01 : 0x00; ++ break; ++ case RT766_SDCA_ENT_USER_FU36: ++ ch_01 = (rt766->fu36_dapm_mute || rt766->fu36_mixer_l_mute) ? 0x01 : 0x00; ++ ch_02 = (rt766->fu36_dapm_mute || rt766->fu36_mixer_r_mute) ? 0x01 : 0x00; ++ break; ++ case RT766_SDCA_ENT_USER_FU21: ++ ch_01 = (rt766->fu21_dapm_mute || rt766->fu21_mixer_l_mute) ? 0x01 : 0x00; ++ ch_02 = (rt766->fu21_dapm_mute || rt766->fu21_mixer_r_mute) ? 0x01 : 0x00; ++ break; ++ case RT766_SDCA_ENT_USER_FU113: ++ for (i = 0; i < ARRAY_SIZE(rt766->fu113_mixer_mute); i++) { ++ ch_mute = (rt766->fu113_dapm_mute || rt766->fu113_mixer_mute[i]) ? 0x01 : 0x00; ++ fu_reg = SDW_SDCA_CTL(func_num, fu_num, SDCA_CTL_FU_MUTE, 1) + i; ++ err = regmap_write(rt766->regmap, fu_reg, ch_mute); ++ if (err < 0) ++ return err; ++ } ++ return 0; ++ } ++ ++ fu01_reg = SDW_SDCA_CTL(func_num, fu_num, SDCA_CTL_FU_MUTE, 1); ++ fu02_reg = SDW_SDCA_CTL(func_num, fu_num, SDCA_CTL_FU_MUTE, 2); ++ err = regmap_write(rt766->regmap, fu01_reg, ch_01); ++ if (err < 0) ++ return err; ++ err = regmap_write(rt766->regmap, fu02_reg, ch_02); ++ if (err < 0) ++ return err; ++ ++ return 0; ++} ++ ++static int rt766_sdca_fu41_playback_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ ++ ucontrol->value.integer.value[0] = !rt766->fu41_mixer_l_mute; ++ ucontrol->value.integer.value[1] = !rt766->fu41_mixer_r_mute; ++ return 0; ++} ++ ++static int rt766_sdca_fu41_playback_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ int err; ++ ++ if (rt766->fu41_mixer_l_mute == !ucontrol->value.integer.value[0] && ++ rt766->fu41_mixer_r_mute == !ucontrol->value.integer.value[1]) ++ return 0; ++ ++ rt766->fu41_mixer_l_mute = !ucontrol->value.integer.value[0]; ++ rt766->fu41_mixer_r_mute = !ucontrol->value.integer.value[1]; ++ ++ err = rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU41); ++ if (err < 0) ++ return err; ++ ++ return 1; ++} ++ ++static int rt766_sdca_fu36_capture_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ ++ ucontrol->value.integer.value[0] = !rt766->fu36_mixer_l_mute; ++ ucontrol->value.integer.value[1] = !rt766->fu36_mixer_r_mute; ++ return 0; ++} ++ ++static int rt766_sdca_fu36_capture_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ int err; ++ ++ if (rt766->fu36_mixer_l_mute == !ucontrol->value.integer.value[0] && ++ rt766->fu36_mixer_r_mute == !ucontrol->value.integer.value[1]) ++ return 0; ++ ++ rt766->fu36_mixer_l_mute = !ucontrol->value.integer.value[0]; ++ rt766->fu36_mixer_r_mute = !ucontrol->value.integer.value[1]; ++ err = rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU36); ++ if (err < 0) ++ return err; ++ ++ return 1; ++} ++ ++static int rt766_sdca_fu21_playback_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ ++ ucontrol->value.integer.value[0] = !rt766->fu21_mixer_l_mute; ++ ucontrol->value.integer.value[1] = !rt766->fu21_mixer_r_mute; ++ return 0; ++} ++ ++static int rt766_sdca_fu21_playback_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ int err; ++ ++ if (rt766->fu21_mixer_l_mute == !ucontrol->value.integer.value[0] && ++ rt766->fu21_mixer_r_mute == !ucontrol->value.integer.value[1]) ++ return 0; ++ ++ rt766->fu21_mixer_l_mute = !ucontrol->value.integer.value[0]; ++ rt766->fu21_mixer_r_mute = !ucontrol->value.integer.value[1]; ++ ++ err = rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_AMP, RT766_SDCA_ENT_USER_FU21); ++ if (err < 0) ++ return err; ++ ++ return 1; ++} ++ ++static int rt766_sdca_fu113_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, int event) ++{ ++ struct snd_soc_component *component = ++ snd_soc_dapm_to_component(w->dapm); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ ++ switch (event) { ++ case SND_SOC_DAPM_POST_PMU: ++ rt766->fu113_dapm_mute = false; ++ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_MIC, RT766_SDCA_ENT_USER_FU113); ++ break; ++ case SND_SOC_DAPM_PRE_PMD: ++ rt766->fu113_dapm_mute = true; ++ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_MIC, RT766_SDCA_ENT_USER_FU113); ++ break; ++ } ++ return 0; ++} ++ ++static int rt766_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ struct rt_sdca_dmic_kctrl_priv *p = ++ (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value; ++ const unsigned int interval_offset = 0xc0; ++ unsigned int regvalue, ctl, i; ++ ++ /* check all channels */ ++ for (i = 0; i < p->count; i++) { ++ regmap_read(rt766->regmap, p->reg_base + i, ®value); ++ ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset); ++ ++ ucontrol->value.integer.value[i] = ctl; ++ } ++ ++ return 0; ++} ++ ++static int rt766_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct rt_sdca_dmic_kctrl_priv *p = ++ (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value; ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ const unsigned int interval_offset = 0xc0; ++ unsigned int gain_val[4]; ++ unsigned int i, changed = 0; ++ unsigned int regvalue[4]; ++ int err; ++ ++ /* check all channels */ ++ for (i = 0; i < p->count; i++) { ++ regmap_read(rt766->regmap, p->reg_base + i, ®value[i]); ++ ++ gain_val[i] = ucontrol->value.integer.value[i]; ++ if (gain_val[i] > p->max) ++ gain_val[i] = p->max; ++ ++ gain_val[i] = 0x1e00 - ((p->max - gain_val[i]) * interval_offset); ++ gain_val[i] &= 0xffff; ++ ++ if (regvalue[i] != gain_val[i]) ++ changed = 1; ++ } ++ ++ if (!changed) ++ return 0; ++ ++ for (i = 0; i < p->count; i++) { ++ err = regmap_write(rt766->regmap, p->reg_base + i, gain_val[i]); ++ if (err < 0) ++ dev_err(&rt766->slave->dev, "0x%08x can't be set\n", p->reg_base + i); ++ } ++ ++ return changed; ++} ++ ++static int rt766_sdca_dmic_fu113_capture_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ unsigned int i; ++ ++ for (i = 0; i < 4; i++) ++ ucontrol->value.integer.value[i] = !rt766->fu113_mixer_mute[i]; ++ return 0; ++} ++ ++static int rt766_sdca_dmic_fu113_capture_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ int err, changed = 0, i; ++ ++ for (i = 0; i < 4; i++) { ++ if (rt766->fu113_mixer_mute[i] != !ucontrol->value.integer.value[i]) ++ changed = 1; ++ rt766->fu113_mixer_mute[i] = !ucontrol->value.integer.value[i]; ++ } ++ ++ err = rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_MIC, RT766_SDCA_ENT_USER_FU113); ++ if (err < 0) ++ return err; ++ return changed; ++} ++ ++static int rt766_sdca_fu41_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, int event) ++{ ++ struct snd_soc_component *component = ++ snd_soc_dapm_to_component(w->dapm); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ ++ switch (event) { ++ case SND_SOC_DAPM_POST_PMU: ++ rt766->fu41_dapm_mute = false; ++ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU41); ++ break; ++ case SND_SOC_DAPM_PRE_PMD: ++ rt766->fu41_dapm_mute = true; ++ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU41); ++ break; ++ } ++ return 0; ++} ++ ++static int rt766_sdca_pde_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, int event, int func_num, int pde_num, const char *pde_ent) ++{ ++ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ struct sdca_function_data *func_data; ++ unsigned char ps0 = 0x0, ps3 = 0x3; ++ const struct sdca_entity *entity; ++ unsigned int pde_req_reg; ++ int from_ps, to_ps; ++ int ret; ++ ++ pde_req_reg = SDW_SDCA_CTL(func_num, pde_num, SDCA_CTL_PDE_REQUESTED_PS, 0); ++ ++ switch (func_num) { ++ case RT766_FUNC_NUM_UAJ: ++ func_data = rt766->uaj_func_data; ++ break; ++ case RT766_FUNC_NUM_AMP: ++ func_data = rt766->sa_func_data; ++ break; ++ case RT766_FUNC_NUM_MIC: ++ func_data = rt766->sm_func_data; ++ break; ++ default: ++ dev_err(component->dev, "%s: unsupported func_num %d\n", ++ __func__, func_num); ++ return -EINVAL; ++ } ++ ++ switch (event) { ++ case SND_SOC_DAPM_POST_PMU: ++ regmap_write(rt766->regmap, pde_req_reg, ps0); ++ from_ps = ps3; ++ to_ps = ps0; ++ break; ++ case SND_SOC_DAPM_PRE_PMD: ++ regmap_write(rt766->regmap, pde_req_reg, ps3); ++ from_ps = ps0; ++ to_ps = ps3; ++ break; ++ } ++ ++ entity = sdca_find_entity_by_label(func_data, pde_ent); ++ if (!entity) { ++ dev_err(component->dev, "%s: failed to find entity %s\n", ++ __func__, pde_ent); ++ return -EINVAL; ++ } ++ ++ ret = sdca_asoc_pde_poll_actual_ps(rt766->regmap, ++ func_num, ++ pde_num, ++ from_ps, to_ps, ++ entity->pde.max_delay, ++ entity->pde.num_max_delay); ++ if (ret) ++ dev_err(component->dev, "%s: PDE transition %x -> %x failed, err=%d\n", ++ __func__, from_ps, to_ps, ret); ++ ++ return ret; ++} ++ ++static int rt766_sdca_pde47_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, int event) ++{ ++ return rt766_sdca_pde_event(w, kcontrol, event, ++ RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_PDE47, "PDE 47"); ++} ++ ++static int rt766_sdca_fu36_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, int event) ++{ ++ struct snd_soc_component *component = ++ snd_soc_dapm_to_component(w->dapm); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ ++ switch (event) { ++ case SND_SOC_DAPM_POST_PMU: ++ rt766->fu36_dapm_mute = false; ++ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU36); ++ break; ++ case SND_SOC_DAPM_PRE_PMD: ++ rt766->fu36_dapm_mute = true; ++ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU36); ++ break; ++ } ++ return 0; ++} ++ ++static int rt766_sdca_pde34_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, int event) ++{ ++ return rt766_sdca_pde_event(w, kcontrol, event, ++ RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_PDE34, "PDE 34"); ++} ++ ++static int rt766_sdca_fu21_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, int event) ++{ ++ struct snd_soc_component *component = ++ snd_soc_dapm_to_component(w->dapm); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ ++ switch (event) { ++ case SND_SOC_DAPM_POST_PMU: ++ rt766->fu21_dapm_mute = false; ++ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_AMP, RT766_SDCA_ENT_USER_FU21); ++ break; ++ case SND_SOC_DAPM_PRE_PMD: ++ rt766->fu21_dapm_mute = true; ++ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_AMP, RT766_SDCA_ENT_USER_FU21); ++ break; ++ } ++ return 0; ++} ++ ++static int rt766_sdca_pde23_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, int event) ++{ ++ return rt766_sdca_pde_event(w, kcontrol, event, ++ RT766_FUNC_NUM_AMP, RT766_SDCA_ENT_PDE23, "PDE 23"); ++} ++ ++static int rt766_sdca_pde11_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, int event) ++{ ++ return rt766_sdca_pde_event(w, kcontrol, event, ++ RT766_FUNC_NUM_MIC, RT766_SDCA_ENT_PDE11, "PDE 11"); ++} ++ ++static int rt766_dmic_fu_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ struct rt_sdca_dmic_kctrl_priv *p = ++ (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value; ++ ++ if (p->max == 1) ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; ++ else ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = p->count; ++ uinfo->value.integer.min = 0; ++ uinfo->value.integer.max = p->max; ++ return 0; ++} ++ ++static const char * const rt766_rx_data_ch_select[] = { ++ "L,R", ++ "R,L", ++ "L,L", ++ "R,R", ++ "L,L+R", ++ "R,L+R", ++ "L+R,L", ++ "L+R,R", ++ "L+R,L+R", ++}; ++ ++static SOC_ENUM_SINGLE_DECL(rt766_rx_data_ch_enum, ++ RT766_SDCA_CTL(AMP, PPU21, SDCA_CTL_PPU_POSTURENUMBER), 0, ++ rt766_rx_data_ch_select); ++ ++static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -9525, 75, 0); ++static const DECLARE_TLV_DB_SCALE(spk_vol_tlv, -6525, 75, 0); ++static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -1725, 75, 0); ++static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, -200, 200, 0); ++ ++#define RT766_P75DB_STEP 0xC0 /* 0.75 dB in Q7.8 format */ ++#define RT766_2DB_STEP 0x200 /* 2 dB in Q7.8 format */ ++#define RT766_HP_VOL_MIN (-127) /* -95.25 dB / 0.75 dB step */ ++#define RT766_HS_VOL_MIN (-23) /* -17.25 dB / 0.75 dB step */ ++#define RT766_HS_BOOST_VOL_MIN (-1) /* -2 dB / 2 dB step */ ++#define RT766_SPK_VOL_MIN (-87) /* -65.25 dB / 0.75 dB step */ ++#define RT766_P_VOL_MAX 0 /* 0 dB / 0.75 dB step */ ++#define RT766_HS_VOL_MAX 40 /* 30 dB / 0.75 dB step */ ++#define RT766_HS_BOOST_VOL_MAX 20 /* 40 dB / 2 dB step */ ++ ++static const struct snd_kcontrol_new rt766_sdca_controls[] = { ++ SOC_DOUBLE_EXT("FU41 Playback Switch", SND_SOC_NOPM, 0, 1, 1, 0, ++ rt766_sdca_fu41_playback_get, rt766_sdca_fu41_playback_put), ++ SDCA_DOUBLE_Q78_TLV("FU41 Playback Volume", ++ RT766_VOLUME_REG(UAJ, USER_FU41, 1), ++ RT766_VOLUME_REG(UAJ, USER_FU41, 2), ++ RT766_HP_VOL_MIN, RT766_P_VOL_MAX, RT766_P75DB_STEP, hp_vol_tlv), ++ SOC_DOUBLE_EXT("FU36 Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0, ++ rt766_sdca_fu36_capture_get, rt766_sdca_fu36_capture_put), ++ SDCA_DOUBLE_Q78_TLV("FU36 Capture Volume", ++ RT766_VOLUME_REG(UAJ, USER_FU36, 1), ++ RT766_VOLUME_REG(UAJ, USER_FU36, 2), ++ RT766_HS_VOL_MIN, RT766_HS_VOL_MAX, RT766_P75DB_STEP, mic_vol_tlv), ++ SDCA_DOUBLE_Q78_TLV("FU33 Boost Volume", ++ RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1), ++ RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2), ++ RT766_HS_BOOST_VOL_MIN, RT766_HS_BOOST_VOL_MAX, RT766_2DB_STEP, boost_vol_tlv), ++ ++ SOC_DOUBLE_EXT("FU21 Playback Switch", SND_SOC_NOPM, 0, 1, 1, 0, ++ rt766_sdca_fu21_playback_get, rt766_sdca_fu21_playback_put), ++ SDCA_DOUBLE_Q78_TLV("FU21 Playback Volume", ++ RT766_VOLUME_REG(AMP, USER_FU21, 1), ++ RT766_VOLUME_REG(AMP, USER_FU21, 2), ++ RT766_SPK_VOL_MIN, RT766_P_VOL_MAX, RT766_P75DB_STEP, spk_vol_tlv), ++ SOC_ENUM("RX Channel Select", rt766_rx_data_ch_enum), ++ ++ RT_SDCA_FU_CTRL("FU113 Capture Switch", ++ RT766_MUTE_REG(MIC, USER_FU113, 1), 1, 1, 4, rt766_dmic_fu_info, ++ rt766_sdca_dmic_fu113_capture_get, rt766_sdca_dmic_fu113_capture_put), ++ RT_SDCA_EXT_TLV("FU113 Capture Volume", ++ RT766_VOLUME_REG(MIC, USER_FU113, 1), ++ rt766_sdca_dmic_set_gain_get, rt766_sdca_dmic_set_gain_put, ++ 4, 0x3f, mic_vol_tlv, rt766_dmic_fu_info), ++}; ++ ++static const struct snd_soc_dapm_widget rt766_sdca_dapm_widgets[] = { ++ /* UAJ */ ++ SND_SOC_DAPM_OUTPUT("HP"), ++ SND_SOC_DAPM_INPUT("MIC2"), ++ SND_SOC_DAPM_SUPPLY("PDE 47", SND_SOC_NOPM, 0, 0, ++ rt766_sdca_pde47_event, ++ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), ++ SND_SOC_DAPM_SUPPLY("PDE 34", SND_SOC_NOPM, 0, 0, ++ rt766_sdca_pde34_event, ++ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), ++ SND_SOC_DAPM_DAC_E("FU 41", NULL, SND_SOC_NOPM, 0, 0, ++ rt766_sdca_fu41_event, ++ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), ++ SND_SOC_DAPM_ADC_E("FU 36", NULL, SND_SOC_NOPM, 0, 0, ++ rt766_sdca_fu36_event, ++ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), ++ SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_OUT("DP12TX", "DP12 Capture", 0, SND_SOC_NOPM, 0, 0), ++ ++ /* AMP */ ++ SND_SOC_DAPM_OUTPUT("SPOL"), ++ SND_SOC_DAPM_OUTPUT("SPOR"), ++ SND_SOC_DAPM_DAC_E("FU 21", NULL, SND_SOC_NOPM, 0, 0, ++ rt766_sdca_fu21_event, ++ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), ++ SND_SOC_DAPM_SUPPLY("PDE 23", SND_SOC_NOPM, 0, 0, ++ rt766_sdca_pde23_event, ++ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), ++ SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0), ++ ++ /* DMIC */ ++ SND_SOC_DAPM_INPUT("DMIC1"), ++ SND_SOC_DAPM_INPUT("DMIC2"), ++ SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0, ++ rt766_sdca_pde11_event, ++ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), ++ SND_SOC_DAPM_ADC_E("FU 113", NULL, SND_SOC_NOPM, 0, 0, ++ rt766_sdca_fu113_event, ++ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), ++ SND_SOC_DAPM_AIF_OUT("DP8TX", "DP8 Capture", 0, SND_SOC_NOPM, 0, 0), ++}; ++ ++static const struct snd_soc_dapm_route rt766_sdca_audio_map[] = { ++ { "FU 41", NULL, "DP3RX" }, ++ { "DP12TX", NULL, "FU 36" }, ++ { "FU 36", NULL, "PDE 34" }, ++ { "FU 36", NULL, "MIC2" }, ++ { "HP", NULL, "PDE 47" }, ++ { "HP", NULL, "FU 41" }, ++ ++ { "FU 21", NULL, "DP1RX" }, ++ { "FU 21", NULL, "PDE 23" }, ++ { "SPOL", NULL, "FU 21" }, ++ { "SPOR", NULL, "FU 21" }, ++ ++ {"DP8TX", NULL, "FU 113"}, ++ {"FU 113", NULL, "PDE 11"}, ++ {"FU 113", NULL, "DMIC1"}, ++ {"FU 113", NULL, "DMIC2"}, ++}; ++ ++static int rt766_sdca_probe(struct snd_soc_component *component) ++{ ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ struct device *dev = &rt766->slave->dev; ++ int ret; ++ ++ rt766->component = component; ++ ++ ret = pm_runtime_resume(component->dev); ++ if (ret < 0 && ret != -EACCES) ++ return ret; ++ ++ if (rt766->uaj_func_data) { ++ dev_dbg(dev, "%s : irq %d\n", __func__, rt766->slave->irq); ++ ++ rt766->irq_info = devm_sdca_irq_allocate(dev, rt766->regmap, rt766->slave->irq); ++ if (IS_ERR(rt766->irq_info)) ++ return PTR_ERR(rt766->irq_info); ++ ++ ret = rt766_sdca_irq_ctl(rt766, rt766->uaj_func_data, ++ component, rt766->irq_info, true); ++ if (ret < 0) { ++ dev_err(dev, "Failed to request UAJ SDCA IRQ: %d\n", ret); ++ return ret; ++ } ++ ++ if (rt766->hid_func_data) { ++ ret = rt766_sdca_irq_ctl(rt766, rt766->hid_func_data, ++ component, rt766->irq_info, true); ++ if (ret < 0) { ++ dev_err(dev, "Failed to request HID SDCA IRQ: %d\n", ret); ++ return ret; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++static void rt766_sdca_remove(struct snd_soc_component *component) ++{ ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ ++ sdca_irq_cleanup_late(component->dev, rt766->uaj_func_data, rt766->irq_info); ++ sdca_irq_cleanup_late(component->dev, rt766->hid_func_data, rt766->irq_info); ++} ++ ++static const struct snd_soc_component_driver soc_sdca_dev_rt766 = { ++ .probe = rt766_sdca_probe, ++ .remove = rt766_sdca_remove, ++ .controls = rt766_sdca_controls, ++ .num_controls = ARRAY_SIZE(rt766_sdca_controls), ++ .dapm_widgets = rt766_sdca_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(rt766_sdca_dapm_widgets), ++ .dapm_routes = rt766_sdca_audio_map, ++ .num_dapm_routes = ARRAY_SIZE(rt766_sdca_audio_map), ++ .set_jack = rt766_sdca_set_jack_detect, ++ .endianness = 1, ++}; ++ ++static int rt766_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, ++ int direction) ++{ ++ snd_soc_dai_dma_data_set(dai, direction, sdw_stream); ++ ++ return 0; ++} ++ ++static void rt766_sdca_shutdown(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ snd_soc_dai_set_dma_data(dai, substream, NULL); ++} ++ ++static int rt766_sdca_pcm_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ struct sdw_stream_config stream_config; ++ struct sdw_port_config port_config; ++ enum sdw_data_direction direction; ++ struct sdw_stream_runtime *sdw_stream; ++ unsigned int sampling_rate; ++ int retval, port; ++ ++ dev_dbg(dai->dev, "%s %s id %d", __func__, dai->name, dai->id); ++ sdw_stream = snd_soc_dai_get_dma_data(dai, substream); ++ ++ if (!sdw_stream) ++ return -EINVAL; ++ ++ if (!rt766->slave) ++ return -EINVAL; ++ ++ /* SoundWire specific configuration */ ++ snd_sdw_params_to_config(substream, params, &stream_config, &port_config); ++ ++ /* SoundWire specific configuration */ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ direction = SDW_DATA_DIR_RX; ++ if (dai->id == RT766_AIF1) ++ port = 3; ++ else if (dai->id == RT766_AIF2) ++ port = 1; ++ else ++ return -EINVAL; ++ } else { ++ direction = SDW_DATA_DIR_TX; ++ if (dai->id == RT766_AIF1) ++ port = 12; ++ else if (dai->id == RT766_AIF3) ++ port = 8; ++ else ++ return -EINVAL; ++ } ++ ++ port_config.num = port; ++ retval = sdw_stream_add_slave(rt766->slave, &stream_config, ++ &port_config, 1, sdw_stream); ++ if (retval) { ++ dev_err(dai->dev, "%s: Unable to configure port\n", __func__); ++ return retval; ++ } ++ ++ if (params_channels(params) > 16) { ++ dev_err(component->dev, "%s: Unsupported channels %d\n", ++ __func__, params_channels(params)); ++ return -EINVAL; ++ } ++ ++ /* sampling rate configuration */ ++ switch (params_rate(params)) { ++ case 44100: ++ sampling_rate = RT766_SDCA_RATE_44100HZ; ++ break; ++ case 48000: ++ sampling_rate = RT766_SDCA_RATE_48000HZ; ++ break; ++ case 96000: ++ sampling_rate = RT766_SDCA_RATE_96000HZ; ++ break; ++ case 192000: ++ sampling_rate = RT766_SDCA_RATE_192000HZ; ++ break; ++ default: ++ dev_err(component->dev, "%s: Rate %d is not supported\n", ++ __func__, params_rate(params)); ++ return -EINVAL; ++ } ++ ++ /* set sampling frequency */ ++ switch (dai->id) { ++ case RT766_AIF1: ++ regmap_write(rt766->regmap, ++ RT766_SDCA_CTL(UAJ, CS41, SDCA_CTL_CS_SAMPLERATEINDEX), ++ sampling_rate); ++ regmap_write(rt766->regmap, ++ RT766_SDCA_CTL(UAJ, CS36, SDCA_CTL_CS_SAMPLERATEINDEX), ++ sampling_rate); ++ break; ++ case RT766_AIF2: ++ regmap_write(rt766->regmap, ++ RT766_SDCA_CTL(AMP, CS21, SDCA_CTL_CS_SAMPLERATEINDEX), ++ sampling_rate); ++ break; ++ case RT766_AIF3: ++ regmap_write(rt766->regmap, ++ RT766_SDCA_CTL(MIC, CS113, SDCA_CTL_CS_SAMPLERATEINDEX), ++ sampling_rate); ++ break; ++ default: ++ dev_err(component->dev, "%s: Wrong DAI id\n", __func__); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int rt766_sdca_pcm_hw_free(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ struct sdw_stream_runtime *sdw_stream = ++ snd_soc_dai_get_dma_data(dai, substream); ++ ++ if (!rt766->slave) ++ return -EINVAL; ++ ++ sdw_stream_remove_slave(rt766->slave, sdw_stream); ++ return 0; ++} ++ ++#define RT766_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \ ++ SNDRV_PCM_RATE_192000) ++#define RT766_DAC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) ++#define RT766_ADC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ ++ SNDRV_PCM_FMTBIT_S32_LE) ++ ++static const struct snd_soc_dai_ops rt766_sdca_ops = { ++ .hw_params = rt766_sdca_pcm_hw_params, ++ .hw_free = rt766_sdca_pcm_hw_free, ++ .set_stream = rt766_sdca_set_sdw_stream, ++ .shutdown = rt766_sdca_shutdown, ++}; ++ ++static struct snd_soc_dai_driver rt766_sdca_dai[] = { ++ { ++ .name = "rt766-sdca-aif1", ++ .id = RT766_AIF1, ++ .playback = { ++ .stream_name = "DP3 Playback", ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = RT766_STEREO_RATES, ++ .formats = RT766_DAC_FORMATS, ++ }, ++ .capture = { ++ .stream_name = "DP12 Capture", ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = RT766_STEREO_RATES, ++ .formats = RT766_ADC_FORMATS, ++ }, ++ .ops = &rt766_sdca_ops, ++ .symmetric_rate = 1, ++ }, ++ { ++ .name = "rt766-sdca-aif2", ++ .id = RT766_AIF2, ++ .playback = { ++ .stream_name = "DP1 Playback", ++ .channels_min = 1, ++ .channels_max = 4, ++ .rates = RT766_STEREO_RATES, ++ .formats = RT766_DAC_FORMATS, ++ }, ++ .ops = &rt766_sdca_ops, ++ }, ++ { ++ .name = "rt766-sdca-aif3", ++ .id = RT766_AIF3, ++ .capture = { ++ .stream_name = "DP8 Capture", ++ .channels_min = 1, ++ .channels_max = 4, ++ .rates = RT766_STEREO_RATES, ++ .formats = RT766_ADC_FORMATS, ++ }, ++ .ops = &rt766_sdca_ops, ++ } ++}; ++ ++static unsigned int rt766_find_dt_rates(struct device *dev, struct sdca_function_data *function, ++ const char *label) ++{ ++ struct snd_soc_pcm_stream stream; ++ struct sdca_entity *entity; ++ int i, ret; ++ ++ for (i = 0; i < function->num_entities; i++) { ++ entity = &function->entities[i]; ++ ++ if (strcmp(entity->label, label)) ++ continue; ++ ++ /* Can't check earlier as only terminals have an iot member. */ ++ if (!entity->iot.is_dataport) ++ continue; ++ ++ ret = sdca_asoc_populate_rate_format(dev, function, entity, &stream); ++ if (ret < 0) { ++ dev_dbg(dev, "%s: failed to parse rates for entity %s\n", ++ __func__, entity->label); ++ return 0; ++ } ++ ++ dev_dbg(dev, "%s: %s supports rates 0x%08x\n", __func__, entity->label, stream.rates); ++ } ++ ++ return stream.rates; ++} ++ ++int rt766_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave *slave) ++{ ++ struct sdca_function_data *func_data_ptr; ++ struct snd_soc_dai_driver *dai_drv; ++ struct rt766_sdca_priv *rt766; ++ unsigned int rates; ++ int ret; ++ int i; ++ ++ rt766 = devm_kzalloc(dev, sizeof(*rt766), GFP_KERNEL); ++ if (!rt766) ++ return -ENOMEM; ++ ++ dev_set_drvdata(dev, rt766); ++ rt766->slave = slave; ++ rt766->regmap = regmap; ++ ++ regcache_cache_only(rt766->regmap, true); ++ ++ ret = devm_mutex_init(dev, &rt766->disable_irq_lock); ++ if (ret < 0) { ++ dev_err(dev, "Failed to initialize mutex\n"); ++ return ret; ++ } ++ ++ /* ++ * Mark hw_init to false ++ * HW init will be performed when device reports present ++ */ ++ rt766->hw_init = false; ++ rt766->first_hw_init = false; ++ rt766->fu41_dapm_mute = true; ++ rt766->fu41_mixer_l_mute = rt766->fu41_mixer_r_mute = false; ++ rt766->fu36_dapm_mute = true; ++ rt766->fu36_mixer_l_mute = rt766->fu36_mixer_r_mute = true; ++ rt766->fu21_dapm_mute = true; ++ rt766->fu21_mixer_l_mute = rt766->fu21_mixer_r_mute = false; ++ rt766->fu113_dapm_mute = true; ++ rt766->fu113_mixer_mute[0] = rt766->fu113_mixer_mute[1] = ++ rt766->fu113_mixer_mute[2] = rt766->fu113_mixer_mute[3] = true; ++ ++ dai_drv = devm_kzalloc(dev, sizeof(struct snd_soc_dai_driver) * ARRAY_SIZE(rt766_sdca_dai), GFP_KERNEL); ++ if (!dai_drv) { ++ dev_err(dev, "Failed to allocate memory for DAI driver\n"); ++ ret = -ENOMEM; ++ goto _sdw_init_err_; ++ } ++ memcpy(dai_drv, rt766_sdca_dai, sizeof(struct snd_soc_dai_driver) * ARRAY_SIZE(rt766_sdca_dai)); ++ ++ /* get SDCA function data */ ++ dev_dbg(dev, "SDCA functions found: %d", slave->sdca_data.num_functions); ++ for (i = 0; i < slave->sdca_data.num_functions; i++) { ++ func_data_ptr = devm_kzalloc(dev, sizeof(*func_data_ptr), GFP_KERNEL); ++ if (!func_data_ptr) { ++ dev_err(dev, "Failed to allocate memory for function data\n"); ++ ret = -ENOMEM; ++ goto _free_dai_drv_; ++ } ++ ++ func_data_ptr->desc = &slave->sdca_data.function[i]; ++ ret = sdca_parse_function(dev, func_data_ptr); ++ if (ret) { ++ devm_kfree(dev, func_data_ptr); ++ goto _free_dai_drv_; ++ } ++ dev_dbg(dev, "Function type=%d, num_entities=%d", ++ slave->sdca_data.function[i].type, func_data_ptr->num_entities); ++ ++ switch (slave->sdca_data.function[i].type) { ++ case SDCA_FUNCTION_TYPE_UAJ: ++ rt766->uaj_func_data = func_data_ptr; ++ /* ++ * Some machines may only support a subset of the sample rates supported by the codec. ++ * Therefore, we need to parse the supported sample rates from the DisCo table and ++ * configure them in the DAI. If the DisCo table does not provide sample rate information, ++ * we will fall back to the default supported rates defined in the codec driver. ++ */ ++ rates = rt766_find_dt_rates(dev, func_data_ptr, "IT 41"); ++ if (rates) ++ dai_drv[RT766_DAI_UAJ].playback.rates = rates; ++ ++ rates = rt766_find_dt_rates(dev, func_data_ptr, "OT 36"); ++ if (rates) ++ dai_drv[RT766_DAI_UAJ].capture.rates = rates; ++ break; ++ case SDCA_FUNCTION_TYPE_SMART_AMP: ++ rt766->sa_func_data = func_data_ptr; ++ rates = rt766_find_dt_rates(dev, func_data_ptr, "IT 21"); ++ if (rates) ++ dai_drv[RT766_DAI_AMP].playback.rates = rates; ++ break; ++ case SDCA_FUNCTION_TYPE_SMART_MIC: ++ rt766->sm_func_data = func_data_ptr; ++ rates = rt766_find_dt_rates(dev, func_data_ptr, "OT 113"); ++ if (rates) ++ dai_drv[RT766_DAI_MIC].capture.rates = rates; ++ break; ++ case SDCA_FUNCTION_TYPE_HID: ++ rt766->hid_func_data = func_data_ptr; ++ break; ++ default: ++ dev_dbg(dev, "Unexpected SDCA function type found: %d", ++ slave->sdca_data.function[i].type); ++ } ++ } ++ ++ ret = devm_snd_soc_register_component(dev, ++ &soc_sdca_dev_rt766, dai_drv, ARRAY_SIZE(rt766_sdca_dai)); ++ if (ret < 0) ++ goto _free_dai_drv_; ++ ++ /* set autosuspend parameters */ ++ pm_runtime_set_autosuspend_delay(dev, 3000); ++ pm_runtime_use_autosuspend(dev); ++ ++ /* make sure the device does not suspend immediately */ ++ pm_runtime_mark_last_busy(dev); ++ ++ pm_runtime_enable(dev); ++ dev_dbg(dev, "%s\n", __func__); ++ return 0; ++ ++_free_dai_drv_: ++ if (dai_drv) ++ devm_kfree(dev, dai_drv); ++ ++_sdw_init_err_: ++ return ret; ++} ++ ++static int rt766_func_initialize(struct rt766_sdca_priv *rt766, struct sdca_function_data *func_data) ++{ ++ struct device *dev = &rt766->slave->dev; ++ unsigned int func_status_reg; ++ unsigned int func_status; ++ int ret; ++ ++ switch (func_data->desc->type) { ++ case SDCA_FUNCTION_TYPE_UAJ: ++ func_status_reg = RT766_FUNC_STATUS_REG(UAJ); ++ break; ++ case SDCA_FUNCTION_TYPE_SMART_AMP: ++ func_status_reg = RT766_FUNC_STATUS_REG(AMP); ++ break; ++ case SDCA_FUNCTION_TYPE_SMART_MIC: ++ func_status_reg = RT766_FUNC_STATUS_REG(MIC); ++ break; ++ case SDCA_FUNCTION_TYPE_HID: ++ func_status_reg = RT766_FUNC_STATUS_REG(HID); ++ break; ++ default: ++ dev_dbg(dev, "Unexpected SDCA function type found: %d", ++ func_data->desc->type); ++ return -EINVAL; ++ } ++ ++ regmap_read(rt766->regmap, func_status_reg, &func_status); ++ dev_dbg(dev, "%s, %s func_status=0x%x\n", __func__, func_data->desc->name, func_status); ++ ++ if ((func_status & SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION) || (!rt766->first_hw_init)) { ++ ret = sdca_regmap_write_init(dev, rt766->regmap, func_data); ++ if (ret) { ++ dev_err(dev, "%s initialization table update failed\n", func_data->desc->name); ++ goto _func_init_err_; ++ } ++ ++ regmap_write(rt766->regmap, func_status_reg, ++ SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION); ++ } ++ ++ return 0; ++ ++_func_init_err_: ++ dev_err(dev, "%s: %s init writes failed, err=%d", __func__, func_data->desc->name, ret); ++ return ret; ++} ++ ++int rt766_sdca_io_init(struct device *dev, struct sdw_slave *slave) ++{ ++ struct rt766_sdca_priv *rt766 = dev_get_drvdata(dev); ++ unsigned int val; ++ ++ rt766->disable_irq = false; ++ ++ if (rt766->hw_init) ++ return 0; ++ ++ regcache_cache_only(rt766->regmap, false); ++ if (rt766->first_hw_init) { ++ regcache_cache_bypass(rt766->regmap, true); ++ } else { ++ /* ++ * PM runtime status is marked as 'active' only when a Slave reports as Attached ++ */ ++ ++ /* update count of parent 'active' children */ ++ pm_runtime_set_active(&slave->dev); ++ } ++ ++ pm_runtime_get_noresume(&slave->dev); ++ ++ regmap_read(rt766->regmap, RT766_BOND_LATCH_ID, &val); ++ dev_dbg(&slave->dev, "%s bond ID=0x%x (%s)\n", __func__, val, (val == 0x1) ? "RT767" : "RT766"); ++ ++ /* check function status and initialize if needed */ ++ if (rt766->uaj_func_data) ++ rt766_func_initialize(rt766, rt766->uaj_func_data); ++ if (rt766->sa_func_data) ++ rt766_func_initialize(rt766, rt766->sa_func_data); ++ if (rt766->sm_func_data) ++ rt766_func_initialize(rt766, rt766->sm_func_data); ++ if (rt766->hid_func_data) ++ rt766_func_initialize(rt766, rt766->hid_func_data); ++ ++ if (rt766->first_hw_init) { ++ regcache_cache_bypass(rt766->regmap, false); ++ regcache_mark_dirty(rt766->regmap); ++ } else { ++ rt766->first_hw_init = true; ++ } ++ ++ /* Mark Slave initialization complete */ ++ rt766->hw_init = true; ++ ++ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); ++ ++ pm_runtime_put_autosuspend(&slave->dev); ++ ++ return 0; ++} ++ ++MODULE_DESCRIPTION("ASoC RT766 SDCA SDW driver"); ++MODULE_AUTHOR("Shuming Fan "); ++MODULE_LICENSE("GPL"); ++MODULE_IMPORT_NS("SND_SOC_SDCA"); +diff --git a/sound/soc/codecs/rt766-sdca.h b/sound/soc/codecs/rt766-sdca.h +new file mode 100644 +--- /dev/null ++++ b/sound/soc/codecs/rt766-sdca.h +@@ -0,0 +1,140 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * rt766-sdca.h -- RT766 SDCA ALSA SoC audio driver header ++ * ++ * Copyright(c) 2026 Realtek Semiconductor Corp. ++ */ ++ ++#ifndef __RT766_H__ ++#define __RT766_H__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct rt766_sdca_priv { ++ struct regmap *regmap; ++ struct snd_soc_component *component; ++ struct sdw_slave *slave; ++ bool hw_init; ++ bool first_hw_init; ++ struct snd_soc_jack *hs_jack; ++ struct mutex disable_irq_lock; /* SDCA irq lock protection */ ++ bool disable_irq; ++ int jack_type; ++ bool fu41_dapm_mute; ++ bool fu41_mixer_l_mute; ++ bool fu41_mixer_r_mute; ++ bool fu113_dapm_mute; ++ bool fu113_mixer_mute[4]; ++ bool fu21_dapm_mute; ++ bool fu21_mixer_l_mute; ++ bool fu21_mixer_r_mute; ++ bool fu36_dapm_mute; ++ bool fu36_mixer_l_mute; ++ bool fu36_mixer_r_mute; ++ struct sdca_function_data *uaj_func_data; ++ struct sdca_function_data *sm_func_data; ++ struct sdca_function_data *sa_func_data; ++ struct sdca_function_data *hid_func_data; ++ struct sdca_interrupt_info *irq_info; ++ struct hid_device *hid; ++}; ++ ++/* vendor registers */ ++#define RT766_VERSION_ID 0xc404 ++#define RT766_DEV_ID1 0xc405 ++#define RT766_DEV_ID0 0xc406 ++#define RT766_BOND_LATCH_ID 0xc407 ++ ++#define RT766_HP_POWER_STATE 0x1000004 ++#define RT766_HP_FSM_CTL2_1 0x100000d ++ ++/* MCU Patch address */ ++#define RT766_MCU_PATCH_ADDR1_START 0x10010000 ++#define RT766_MCU_PATCH_ADDR1_END 0x10011fff ++#define RT766_MCU_PATCH_ADDR2_START 0x10020000 ++#define RT766_MCU_PATCH_ADDR2_END 0x10023fff ++ ++/* Buffer address for HID */ ++#define RT766_BUF_ADDR_HID1 0x44030000 ++#define RT766_BUF_ADDR_HID2 0x44030020 ++ ++/* SDCA (Channel) */ ++#define RT766_CH_1 0x01 ++#define RT766_CH_2 0x02 ++#define RT766_CH_3 0x03 ++#define RT766_CH_4 0x04 ++ ++/* RT766 SDCA Control - function number */ ++#define RT766_FUNC_NUM_UAJ 0x01 ++#define RT766_FUNC_NUM_MIC 0x02 ++#define RT766_FUNC_NUM_HID 0x03 ++#define RT766_FUNC_NUM_AMP 0x04 ++ ++/* RT766 SDCA entity */ ++#define RT766_SDCA_ENT_0 0x00 ++#define RT766_SDCA_ENT_HID101 0x01 ++#define RT766_SDCA_ENT_GE49 0x49 ++#define RT766_SDCA_ENT_USER_FU41 0x05 ++#define RT766_SDCA_ENT_USER_FU36 0x0f ++#define RT766_SDCA_ENT_USER_FU21 0x03 ++#define RT766_SDCA_ENT_USER_FU113 0x30 ++#define RT766_SDCA_ENT_PDE23 0x33 ++#define RT766_SDCA_ENT_PDE47 0x28 ++#define RT766_SDCA_ENT_PDE11 0x2a ++#define RT766_SDCA_ENT_PDE34 0x29 ++#define RT766_SDCA_ENT_CS41 0x01 ++#define RT766_SDCA_ENT_CS36 0x11 ++#define RT766_SDCA_ENT_CS113 0x12 ++#define RT766_SDCA_ENT_CS21 0x21 ++#define RT766_SDCA_ENT_PLATFORM_FU33 0x44 ++#define RT766_SDCA_ENT_PPU21 0x04 ++ ++/* sample frequency index */ ++#define RT766_SDCA_RATE_44100HZ 0x08 ++#define RT766_SDCA_RATE_48000HZ 0x09 ++#define RT766_SDCA_RATE_96000HZ 0x0b ++#define RT766_SDCA_RATE_192000HZ 0x0d ++ ++/* SDCA Register macros */ ++#define RT766_MUTE_REG(func, fu, ch) \ ++ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##fu, SDCA_CTL_FU_MUTE, RT766_CH_##ch) ++ ++#define RT766_VOLUME_REG(func, fu, ch) \ ++ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##fu, SDCA_CTL_FU_CHANNEL_VOLUME, RT766_CH_##ch) ++ ++#define RT766_GAIN_REG(func, fu, ch) \ ++ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##fu, SDCA_CTL_FU_GAIN, RT766_CH_##ch) ++ ++#define RT766_PDE_REQ_REG(func, pde) \ ++ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##pde, SDCA_CTL_PDE_REQUESTED_PS, 0) ++ ++#define RT766_PDE_ACTUAL_REG(func, pde) \ ++ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##pde, SDCA_CTL_PDE_ACTUAL_PS, 0) ++ ++#define RT766_FUNC_STATUS_REG(func) \ ++ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_0, SDCA_CTL_ENTITY_0_FUNCTION_STATUS, 0) ++ ++#define RT766_SDCA_CTL(func, ent, ctl) \ ++ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##ent, ctl, 0) ++ ++enum { ++ RT766_AIF1, ++ RT766_AIF2, ++ RT766_AIF3, ++}; ++ ++enum { ++ RT766_DAI_UAJ, ++ RT766_DAI_AMP, ++ RT766_DAI_MIC, ++}; ++ ++int rt766_sdca_io_init(struct device *dev, struct sdw_slave *slave); ++int rt766_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave *slave); ++#endif /* __RT766_H__ */ +diff --git a/sound/soc/codecs/rt9120.c b/sound/soc/codecs/rt9120.c +--- a/sound/soc/codecs/rt9120.c ++++ b/sound/soc/codecs/rt9120.c +@@ -606,12 +606,19 @@ static int rt9120_runtime_suspend(struct device *dev) + static int rt9120_runtime_resume(struct device *dev) + { + struct rt9120_data *data = dev_get_drvdata(dev); ++ int ret; + + if (data->pwdnn_gpio) { + gpiod_set_value(data->pwdnn_gpio, 1); + msleep(RT9120_CHIPON_WAITMS); + regcache_cache_only(data->regmap, false); +- regcache_sync(data->regmap); ++ ret = regcache_sync(data->regmap); ++ if (ret) { ++ regcache_cache_only(data->regmap, true); ++ regcache_mark_dirty(data->regmap); ++ gpiod_set_value(data->pwdnn_gpio, 0); ++ return ret; ++ } + } + + return 0; +diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c +--- a/sound/soc/codecs/sigmadsp.c ++++ b/sound/soc/codecs/sigmadsp.c +@@ -5,6 +5,7 @@ + * Copyright 2009-2014 Analog Devices Inc. + */ + ++#include + #include + #include + #include +@@ -135,7 +136,7 @@ static int sigmadsp_ctrl_put(struct snd_kcontrol *kcontrol, + uint8_t *data; + int ret = 0; + +- mutex_lock(&sigmadsp->lock); ++ guard(mutex)(&sigmadsp->lock); + + data = ucontrol->value.bytes.data; + +@@ -148,8 +149,6 @@ static int sigmadsp_ctrl_put(struct snd_kcontrol *kcontrol, + ctrl->cached = true; + } + +- mutex_unlock(&sigmadsp->lock); +- + return ret; + } + +@@ -160,7 +159,7 @@ static int sigmadsp_ctrl_get(struct snd_kcontrol *kcontrol, + struct sigmadsp *sigmadsp = snd_kcontrol_chip(kcontrol); + int ret = 0; + +- mutex_lock(&sigmadsp->lock); ++ guard(mutex)(&sigmadsp->lock); + + if (!ctrl->cached) { + ret = sigmadsp_read(sigmadsp, ctrl->addr, ctrl->cache, +@@ -174,8 +173,6 @@ static int sigmadsp_ctrl_get(struct snd_kcontrol *kcontrol, + ctrl->num_bytes); + } + +- mutex_unlock(&sigmadsp->lock); +- + return ret; + } + +@@ -487,7 +484,7 @@ static void devm_sigmadsp_release(struct device *dev, void *res) + static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) + { + const struct sigma_firmware_header *ssfw_head; +- const struct firmware *fw; ++ const struct firmware *fw __free(firmware) = NULL; + int ret; + u32 crc; + +@@ -495,7 +492,7 @@ static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) + ret = request_firmware(&fw, name, sigmadsp->dev); + if (ret) { + pr_debug("%s: request_firmware() failed with %i\n", __func__, ret); +- goto done; ++ return ret; + } + + /* then verify the header */ +@@ -509,13 +506,13 @@ static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) + */ + if (fw->size < sizeof(*ssfw_head) || fw->size >= 0x4000000) { + dev_err(sigmadsp->dev, "Failed to load firmware: Invalid size\n"); +- goto done; ++ return -EINVAL; + } + + ssfw_head = (void *)fw->data; + if (memcmp(ssfw_head->magic, SIGMA_MAGIC, ARRAY_SIZE(ssfw_head->magic))) { + dev_err(sigmadsp->dev, "Failed to load firmware: Invalid magic\n"); +- goto done; ++ return -EINVAL; + } + + crc = crc32(0, fw->data + sizeof(*ssfw_head), +@@ -524,7 +521,7 @@ static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) + if (crc != le32_to_cpu(ssfw_head->crc)) { + dev_err(sigmadsp->dev, "Failed to load firmware: Wrong crc checksum: expected %x got %x\n", + le32_to_cpu(ssfw_head->crc), crc); +- goto done; ++ return -EINVAL; + } + + switch (ssfw_head->version) { +@@ -545,9 +542,6 @@ static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) + if (ret) + sigmadsp_firmware_release(sigmadsp); + +-done: +- release_firmware(fw); +- + return ret; + } + +@@ -677,10 +671,10 @@ static void sigmadsp_activate_ctrl(struct sigmadsp *sigmadsp, + return; + changed = snd_ctl_activate_id(card, &ctrl->kcontrol->id, active); + if (active && changed > 0) { +- mutex_lock(&sigmadsp->lock); +- if (ctrl->cached) +- sigmadsp_ctrl_write(sigmadsp, ctrl, ctrl->cache); +- mutex_unlock(&sigmadsp->lock); ++ scoped_guard(mutex, &sigmadsp->lock) { ++ if (ctrl->cached) ++ sigmadsp_ctrl_write(sigmadsp, ctrl, ctrl->cache); ++ } + } + } + +diff --git a/sound/soc/codecs/sma1303.c b/sound/soc/codecs/sma1303.c +--- a/sound/soc/codecs/sma1303.c ++++ b/sound/soc/codecs/sma1303.c +@@ -1777,6 +1777,8 @@ static void sma1303_i2c_remove(struct i2c_client *client) + struct sma1303_priv *sma1303 = + (struct sma1303_priv *) i2c_get_clientdata(client); + ++ if (sma1303->attr_grp) ++ sysfs_remove_group(sma1303->kobj, sma1303->attr_grp); + cancel_delayed_work_sync(&sma1303->check_fault_work); + } + +diff --git a/sound/soc/codecs/sma1307.c b/sound/soc/codecs/sma1307.c +--- a/sound/soc/codecs/sma1307.c ++++ b/sound/soc/codecs/sma1307.c +@@ -1690,7 +1690,7 @@ static void sma1307_check_fault_worker(struct work_struct *work) + + static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *file) + { +- const struct firmware *fw; ++ const struct firmware *fw __free(firmware) = NULL; + int size, offset, num_mode; + int ret; + +@@ -1703,22 +1703,18 @@ static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *fil + return; + } else if ((fw->size) < SMA1307_SETTING_HEADER_SIZE) { + dev_err(sma1307->dev, "%s: Invalid file\n", __func__); +- release_firmware(fw); + sma1307->set.status = false; + return; + } + + int *data __free(kfree) = kzalloc(fw->size, GFP_KERNEL); + if (!data) { +- release_firmware(fw); + sma1307->set.status = false; + return; + } + size = fw->size >> 2; + memcpy(data, fw->data, fw->size); + +- release_firmware(fw); +- + /* HEADER */ + sma1307->set.header_size = SMA1307_SETTING_HEADER_SIZE; + sma1307->set.checksum = data[sma1307->set.header_size - 2]; +diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c +--- a/sound/soc/codecs/sta32x.c ++++ b/sound/soc/codecs/sta32x.c +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -269,9 +270,9 @@ static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol, + int numcoef = kcontrol->private_value >> 16; + int index = kcontrol->private_value & 0xffff; + unsigned int cfud, val; +- int i, ret = 0; ++ int i; + +- mutex_lock(&sta32x->coeff_lock); ++ guard(mutex)(&sta32x->coeff_lock); + + /* preserve reserved bits in STA32X_CFUD */ + regmap_read(sta32x->regmap, STA32X_CFUD, &cfud); +@@ -283,24 +284,20 @@ static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol, + regmap_write(sta32x->regmap, STA32X_CFUD, cfud); + + regmap_write(sta32x->regmap, STA32X_CFADDR2, index); +- if (numcoef == 1) { ++ if (numcoef == 1) + regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x04); +- } else if (numcoef == 5) { ++ else if (numcoef == 5) + regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x08); +- } else { +- ret = -EINVAL; +- goto exit_unlock; +- } ++ else ++ return -EINVAL; ++ + + for (i = 0; i < 3 * numcoef; i++) { + regmap_read(sta32x->regmap, STA32X_B1CF1 + i, &val); + ucontrol->value.bytes.data[i] = val; + } + +-exit_unlock: +- mutex_unlock(&sta32x->coeff_lock); +- +- return ret; ++ return 0; + } + + static int sta32x_coefficient_put(struct snd_kcontrol *kcontrol, +diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c +--- a/sound/soc/codecs/sta350.c ++++ b/sound/soc/codecs/sta350.c +@@ -16,6 +16,7 @@ + + #define pr_fmt(fmt) KBUILD_MODNAME ":%s:%d: " fmt, __func__, __LINE__ + ++#include + #include + #include + #include +@@ -306,9 +307,9 @@ static int sta350_coefficient_get(struct snd_kcontrol *kcontrol, + int numcoef = kcontrol->private_value >> 16; + int index = kcontrol->private_value & 0xffff; + unsigned int cfud, val; +- int i, ret = 0; ++ int i; + +- mutex_lock(&sta350->coeff_lock); ++ guard(mutex)(&sta350->coeff_lock); + + /* preserve reserved bits in STA350_CFUD */ + regmap_read(sta350->regmap, STA350_CFUD, &cfud); +@@ -320,24 +321,19 @@ static int sta350_coefficient_get(struct snd_kcontrol *kcontrol, + regmap_write(sta350->regmap, STA350_CFUD, cfud); + + regmap_write(sta350->regmap, STA350_CFADDR2, index); +- if (numcoef == 1) { ++ if (numcoef == 1) + regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x04); +- } else if (numcoef == 5) { ++ else if (numcoef == 5) + regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x08); +- } else { +- ret = -EINVAL; +- goto exit_unlock; +- } ++ else ++ return -EINVAL; + + for (i = 0; i < 3 * numcoef; i++) { + regmap_read(sta350->regmap, STA350_B1CF1 + i, &val); + ucontrol->value.bytes.data[i] = val; + } + +-exit_unlock: +- mutex_unlock(&sta350->coeff_lock); +- +- return ret; ++ return 0; + } + + static int sta350_coefficient_put(struct snd_kcontrol *kcontrol, +diff --git a/sound/soc/codecs/tac5xx2-sdw.c b/sound/soc/codecs/tac5xx2-sdw.c +--- a/sound/soc/codecs/tac5xx2-sdw.c ++++ b/sound/soc/codecs/tac5xx2-sdw.c +@@ -88,6 +88,9 @@ + #define TAC_FW_FILE_HDR 20 + #define TAC_MAX_FW_CHUNKS 512 + ++#define TAC_UAJ_PREP_CONNECTED 0xff ++#define TAC_UAJ_PREP_DISCONNECTED 0xdf ++ + struct tac_fw_hdr { + u32 size; + u32 version_offset; +@@ -138,6 +141,7 @@ struct tac5xx2_prv { + bool hw_init; + bool first_hw_init_done; + u32 part_id; ++ u32 rev_id; + struct snd_soc_jack *hs_jack; + int jack_type; + /* Custom fw binary. UMP File Download is not used. */ +@@ -261,7 +265,6 @@ static const struct reg_default tac_reg_default[] = { + {TAC_REG_SDW(0, 0, 0x78), 0x0}, + {TAC_REG_SDW(0, 0, 0x7b), 0x0}, + {TAC_REG_SDW(0, 0, 0x7c), 0xd0}, +- {TAC_REG_SDW(0, 0, 0x7d), 0x0}, + {TAC_REG_SDW(0, 0, 0x7e), 0x0}, + {TAC_REG_SDW(0, 1, 0x1), 0x0}, + {TAC_REG_SDW(0, 1, 0x2), 0x0}, +@@ -507,18 +510,6 @@ static const struct regmap_config tac_regmap = { + .use_single_write = true, + }; + +-/* Check if device has DSP algo that needs status monitoring */ +-static bool tac_has_dsp_algo(struct tac5xx2_prv *tac_dev) +-{ +- switch (tac_dev->part_id) { +- case 0x5682: +- case 0x2883: +- return true; +- default: +- return false; +- } +-} +- + /* Check if device has UAJ (Universal Audio Jack) support */ + static bool tac_has_uaj_support(struct tac5xx2_prv *tac_dev) + { +@@ -800,7 +791,7 @@ static int tac_sdw_hw_params(struct snd_pcm_substream *substream, + return ret; + } + +- ret = sdca_asoc_pde_poll_actual_ps(tac_dev->dev, tac_dev->regmap, function_id, pde_entity, ++ ret = sdca_asoc_pde_poll_actual_ps(tac_dev->regmap, function_id, pde_entity, + SDCA_PDE_PS3, SDCA_PDE_PS0, NULL, 0); + if (ret) + dev_err(tac_dev->dev, "failed to transition func %d, pde %d from PS3 -> PS0, err=%d\n", +@@ -847,7 +838,7 @@ static int tac_sdw_pcm_hw_free(struct snd_pcm_substream *substream, + return ret; + } + +- ret = sdca_asoc_pde_poll_actual_ps(tac_dev->dev, tac_dev->regmap, function_id, ++ ret = sdca_asoc_pde_poll_actual_ps(tac_dev->regmap, function_id, + pde_entity, SDCA_PDE_PS0, SDCA_PDE_PS3, + NULL, 0); + if (ret) +@@ -942,6 +933,7 @@ static int tac5xx2_sdca_button_detect(struct tac5xx2_prv *tac_dev) + static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev) + { + int val, ret; ++ u8 jack_prep; + + ret = regmap_read(tac_dev->regmap, + SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_GE35, +@@ -951,6 +943,8 @@ static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev) + return ret; + } + ++ jack_prep = TAC_UAJ_PREP_CONNECTED; ++ + switch (val) { + case 4: + tac_dev->jack_type = SND_JACK_MICROPHONE; +@@ -964,6 +958,7 @@ static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev) + case 0: + default: + tac_dev->jack_type = 0; ++ jack_prep = TAC_UAJ_PREP_DISCONNECTED; + break; + } + +@@ -973,26 +968,44 @@ static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev) + if (ret) + dev_err(tac_dev->dev, "Failed to update the jack type to device"); + ++ /* ++ * When uaj is uplugged and booted, we end up with the channel prepare ++ * timeout error for the uaj ports. This writes allows the channel prepare ++ * to succeed when the uaj is not plugged in. ++ */ ++ if (tac_dev->rev_id >= 0x30) { ++ ret = regmap_write(tac_dev->regmap, TAC_REG_SDW(0, 3, 127), jack_prep); ++ if (ret) ++ dev_warn(tac_dev->dev, "Failed to write jack_prep register: %d\n", ret); ++ } ++ + return 0; + } + + static int tac5xx2_jack_init(struct tac5xx2_prv *tac_dev) + { ++ u32 jd_int_mask, hid_int_mask; + int ret = 0; + ++ if (tac_dev->rev_id >= 0x30) { ++ jd_int_mask = SDW_SCP_SDCA_INTMASK_SDCA_12; ++ hid_int_mask = SDW_SCP_SDCA_INTMASK_SDCA_17; ++ } else { ++ jd_int_mask = SDW_SCP_SDCA_INTMASK_SDCA_11; ++ hid_int_mask = SDW_SCP_SDCA_INTMASK_SDCA_16; ++ } ++ + if (!tac_dev->hs_jack) + goto disable_interrupts; + +- ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK2, +- SDW_SCP_SDCA_INTMASK_SDCA_11); ++ ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK2, jd_int_mask); + if (ret) { + dev_err(tac_dev->dev, + "Failed to register jack detection interrupt: %d\n", ret); + goto disable_interrupts; + } + +- ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK3, +- SDW_SCP_SDCA_INTMASK_SDCA_16); ++ ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK3, hid_int_mask); + if (ret) { + dev_err(tac_dev->dev, + "Failed to register for button detect interrupt: %d\n", ret); +@@ -1051,6 +1064,7 @@ static int tac_interrupt_callback(struct sdw_slave *slave, + unsigned int sdca_int2, sdca_int3, jack_report_mask = 0; + struct tac5xx2_prv *tac_dev = dev_get_drvdata(&slave->dev); + struct device *dev = &slave->dev; ++ u32 headset_detect_chk, hid_detect_chk; + int btn_type = 0; + int ret = 0; + +@@ -1081,14 +1095,22 @@ static int tac_interrupt_callback(struct sdw_slave *slave, + dev_dbg(dev, "SDCA_INT2: 0x%02x, SDCA_INT3: 0x%02x\n", + sdca_int2, sdca_int3); + +- if (sdca_int2 & SDW_SCP_SDCA_INT_SDCA_11) { ++ if (tac_dev->rev_id >= 0x30) { ++ headset_detect_chk = SDW_SCP_SDCA_INT_SDCA_12; ++ hid_detect_chk = SDW_SCP_SDCA_INT_SDCA_17; ++ } else { ++ headset_detect_chk = SDW_SCP_SDCA_INT_SDCA_11; ++ hid_detect_chk = SDW_SCP_SDCA_INT_SDCA_16; ++ } ++ ++ if (sdca_int2 & headset_detect_chk) { + ret = tac5xx2_sdca_headset_detect(tac_dev); + if (ret < 0) + goto clear; + jack_report_mask |= SND_JACK_HEADSET; + } + +- if (sdca_int3 & SDW_SCP_SDCA_INT_SDCA_16) { ++ if (sdca_int3 & hid_detect_chk) { + btn_type = tac5xx2_sdca_button_detect(tac_dev); + if (btn_type < 0) + btn_type = 0; +@@ -1695,6 +1717,15 @@ static int tac_io_init(struct device *dev, struct sdw_slave *slave, bool first) + goto io_init_err; + } + ++ if (!tac_dev->rev_id) { ++ ret = regmap_read(tac_dev->regmap, TAC_REV_ID, &tac_dev->rev_id); ++ if (ret) { ++ dev_err(tac_dev->dev, "failed to rev id, err=%d\n", ret); ++ goto io_init_err; ++ } ++ dev_dbg(tac_dev->dev, "detected rev_id 0x%x", tac_dev->rev_id); ++ } ++ + if (tac_dev->fw_files && tac_dev->fw_file_cnt > 0) { + ret = tac_download_fw_to_hw(tac_dev); + if (ret) { +@@ -1940,7 +1971,7 @@ static s32 tac_sdw_probe(struct sdw_slave *peripheral, + "failed to allocate %s function data", + func_name); + function_data->desc = &peripheral->sdca_data.function[i]; +- ret = sdca_parse_function(dev, peripheral, function_data); ++ ret = sdca_parse_function(dev, function_data); + if (!ret) + *func_ptr = function_data; + else +@@ -1959,6 +1990,7 @@ static s32 tac_sdw_probe(struct sdw_slave *peripheral, + tac_dev->hw_init = false; + tac_dev->first_hw_init_done = false; + tac_dev->part_id = id->part_id; ++ tac_dev->rev_id = 0x0; + dev_set_drvdata(dev, tac_dev); + + regmap = devm_regmap_init_sdw_mbq_cfg(&peripheral->dev, peripheral, +@@ -1972,17 +2004,13 @@ static s32 tac_sdw_probe(struct sdw_slave *peripheral, + tac_dev->jack_type = 0; + init_completion(&tac_dev->fw_caching_complete); + +- if (tac_has_dsp_algo(tac_dev)) { +- tac_generate_fw_name(peripheral, tac_dev->fw_binaryname, +- sizeof(tac_dev->fw_binaryname)); ++ tac_generate_fw_name(peripheral, tac_dev->fw_binaryname, ++ sizeof(tac_dev->fw_binaryname)); + +- ret = tac_load_and_cache_firmware_async(tac_dev); +- if (ret) { +- complete_all(&tac_dev->fw_caching_complete); +- dev_dbg(dev, "failed to load fw: %d, use rom mode\n", ret); +- } +- } else { ++ ret = tac_load_and_cache_firmware_async(tac_dev); ++ if (ret) { + complete_all(&tac_dev->fw_caching_complete); ++ dev_dbg(dev, "failed to load fw: %d, use rom mode\n", ret); + } + + ret = tac_init(tac_dev); +diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c +--- a/sound/soc/codecs/tas2552.c ++++ b/sound/soc/codecs/tas2552.c +@@ -495,13 +495,21 @@ static int tas2552_runtime_suspend(struct device *dev) + static int tas2552_runtime_resume(struct device *dev) + { + struct tas2552_data *tas2552 = dev_get_drvdata(dev); ++ int ret; + + gpiod_set_value_cansleep(tas2552->enable_gpio, 1); + + tas2552_sw_shutdown(tas2552, 0); + + regcache_cache_only(tas2552->regmap, false); +- regcache_sync(tas2552->regmap); ++ ret = regcache_sync(tas2552->regmap); ++ if (ret) { ++ regcache_cache_only(tas2552->regmap, true); ++ regcache_mark_dirty(tas2552->regmap); ++ tas2552_sw_shutdown(tas2552, 1); ++ gpiod_set_value_cansleep(tas2552->enable_gpio, 0); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/tas2781-comlib-i2c.c b/sound/soc/codecs/tas2781-comlib-i2c.c +--- a/sound/soc/codecs/tas2781-comlib-i2c.c ++++ b/sound/soc/codecs/tas2781-comlib-i2c.c +@@ -6,6 +6,7 @@ + // + // Author: Shenghao Ding + ++#include + #include + #include + #include +@@ -342,7 +343,7 @@ int tascodec_init(struct tasdevice_priv *tas_priv, void *codec, + /* Codec Lock Hold to ensure that codec_probe and firmware parsing and + * loading do not simultaneously execute. + */ +- mutex_lock(&tas_priv->codec_lock); ++ guard(mutex)(&tas_priv->codec_lock); + + if (tas_priv->name_prefix) + scnprintf(tas_priv->rca_binaryname, 64, "%s-%sRCA%d.bin", +@@ -360,8 +361,6 @@ int tascodec_init(struct tasdevice_priv *tas_priv, void *codec, + dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n", + ret); + +- /* Codec Lock Release*/ +- mutex_unlock(&tas_priv->codec_lock); + return ret; + } + EXPORT_SYMBOL_GPL(tascodec_init); +diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c +--- a/sound/soc/codecs/tas2781-fmwlib.c ++++ b/sound/soc/codecs/tas2781-fmwlib.c +@@ -2253,7 +2253,7 @@ int tas2781_load_calibration(void *context, char *file_name, + { + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; + struct tasdevice *tasdev = &(tas_priv->tasdevice[i]); +- const struct firmware *fw_entry = NULL; ++ const struct firmware *fw_entry __free(firmware) = NULL; + struct tasdevice_fw *tas_fmw; + struct firmware fmw; + int offset = 0; +@@ -2263,60 +2263,50 @@ int tas2781_load_calibration(void *context, char *file_name, + if (ret) { + dev_err(tas_priv->dev, "%s: Request firmware %s failed\n", + __func__, file_name); +- goto out; ++ return ret; + } + + if (!fw_entry->size) { + dev_err(tas_priv->dev, "%s: file read error: size = %lu\n", + __func__, (unsigned long)fw_entry->size); +- ret = -EINVAL; +- goto out; ++ return -EINVAL; + } + fmw.size = fw_entry->size; + fmw.data = fw_entry->data; + + tas_fmw = tasdev->cali_data_fmw = kzalloc_obj(struct tasdevice_fw); +- if (!tasdev->cali_data_fmw) { +- ret = -ENOMEM; +- goto out; +- } ++ if (!tasdev->cali_data_fmw) ++ return -ENOMEM; ++ + tas_fmw->dev = tas_priv->dev; + offset = fw_parse_header(tas_priv, tas_fmw, &fmw, offset); + if (offset == -EINVAL) { + dev_err(tas_priv->dev, "fw_parse_header EXIT!\n"); +- ret = offset; +- goto out; ++ return -EINVAL; + } + offset = fw_parse_variable_hdr_cal(tas_priv, tas_fmw, &fmw, offset); + if (offset == -EINVAL) { + dev_err(tas_priv->dev, + "%s: fw_parse_variable_header_cal EXIT!\n", __func__); +- ret = offset; +- goto out; ++ return -EINVAL; + } + offset = fw_parse_program_data(tas_priv, tas_fmw, &fmw, offset); + if (offset < 0) { + dev_err(tas_priv->dev, "fw_parse_program_data EXIT!\n"); +- ret = offset; +- goto out; ++ return offset; + } + offset = fw_parse_configuration_data(tas_priv, tas_fmw, &fmw, offset); + if (offset < 0) { + dev_err(tas_priv->dev, "fw_parse_configuration_data EXIT!\n"); +- ret = offset; +- goto out; ++ return offset; + } + offset = fw_parse_calibration_data(tas_priv, tas_fmw, &fmw, offset); + if (offset < 0) { + dev_err(tas_priv->dev, "fw_parse_calibration_data EXIT!\n"); +- ret = offset; +- goto out; ++ return offset; + } + +-out: +- release_firmware(fw_entry); +- +- return ret; ++ return 0; + } + EXPORT_SYMBOL_NS_GPL(tas2781_load_calibration, "SND_SOC_TAS2781_FMWLIB"); + +@@ -2409,7 +2399,7 @@ static int tasdevice_dspfw_ready(const struct firmware *fmw, + int tasdevice_dsp_parser(void *context) + { + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; +- const struct firmware *fw_entry; ++ const struct firmware *fw_entry __free(firmware) = NULL; + int ret; + + ret = request_firmware(&fw_entry, tas_priv->coef_binaryname, +@@ -2417,15 +2407,10 @@ int tasdevice_dsp_parser(void *context) + if (ret) { + dev_err(tas_priv->dev, "%s: load %s error\n", __func__, + tas_priv->coef_binaryname); +- goto out; ++ return ret; + } + +- ret = tasdevice_dspfw_ready(fw_entry, tas_priv); +- release_firmware(fw_entry); +- fw_entry = NULL; +- +-out: +- return ret; ++ return tasdevice_dspfw_ready(fw_entry, tas_priv); + } + EXPORT_SYMBOL_NS_GPL(tasdevice_dsp_parser, "SND_SOC_TAS2781_FMWLIB"); + +@@ -2807,11 +2792,12 @@ int tasdevice_prmg_load(void *context, int prm_no) + } + EXPORT_SYMBOL_NS_GPL(tasdevice_prmg_load, "SND_SOC_TAS2781_FMWLIB"); + +-void tasdevice_tuning_switch(void *context, int state) ++void tasdevice_tuning_switch(void *context, int state, bool is_cap) + { + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_fw *tas_fmw = tas_priv->fmw; +- int profile_cfg_id = tas_priv->rcabin.profile_cfg_id; ++ int profile_cfg_id = is_cap ? tas_priv->rcabin.capture_profile_id : ++ tas_priv->rcabin.profile_cfg_id; + + /* + * Only RCA-based Playback can still work with no dsp program running +@@ -2828,7 +2814,6 @@ void tasdevice_tuning_switch(void *context, int state) + if (state == 0) { + if (tas_fmw && tas_priv->cur_prog < tas_fmw->nr_programs) { + /* dsp mode or tuning mode */ +- profile_cfg_id = tas_priv->rcabin.profile_cfg_id; + tasdevice_select_tuningprm_cfg(tas_priv, + tas_priv->cur_prog, tas_priv->cur_conf, + profile_cfg_id); +diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c +--- a/sound/soc/codecs/tas2781-i2c.c ++++ b/sound/soc/codecs/tas2781-i2c.c +@@ -13,6 +13,7 @@ + // Author: Kevin Lu + // + ++#include + #include + #include + #include +@@ -617,6 +618,7 @@ static int tasdev_calib_stop_put(struct snd_kcontrol *kcontrol, + { + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp); ++ int i; + + guard(mutex)(&priv->codec_lock); + if (priv->chip_id == TAS2563) +@@ -624,6 +626,14 @@ static int tasdev_calib_stop_put(struct snd_kcontrol *kcontrol, + else + tas2781_calib_stop_put(priv); + ++ /* ++ * Set reloading-firmware flag after calibration, the flag will work ++ * during next playback, then set to the program id after reloading ++ * firmware. ++ */ ++ for (i = 0; i < priv->ndev; i++) ++ priv->tasdevice[i].cur_prog = -1; ++ + return 1; + } + +@@ -843,12 +853,12 @@ static int tasdevice_digital_gain_get( + unsigned char data[4]; + int ret; + +- mutex_lock(&tas_dev->codec_lock); ++ guard(mutex)(&tas_dev->codec_lock); + /* Read the primary device */ + ret = tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4); + if (ret) { + dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__); +- goto out; ++ return ret; + } + + target = get_unaligned_be32(&data[0]); +@@ -868,8 +878,7 @@ static int tasdevice_digital_gain_get( + /* find out the member same as or closer to the current volume */ + ucontrol->value.integer.value[0] = + abs(target - ar_l) <= abs(target - ar_r) ? l : r; +-out: +- mutex_unlock(&tas_dev->codec_lock); ++ + return 0; + } + +@@ -882,29 +891,26 @@ static int tasdevice_digital_gain_put( + struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol); + struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec); + int vol = ucontrol->value.integer.value[0]; +- int status = 0, max = mc->max, rc = 1; ++ int status = 0, max = mc->max; + int i, ret; + unsigned int reg = mc->reg; + unsigned int volrd, volwr; + unsigned char data[4]; + + vol = clamp(vol, 0, max); +- mutex_lock(&tas_dev->codec_lock); ++ guard(mutex)(&tas_dev->codec_lock); + /* Read the primary device */ + ret = tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4); + if (ret) { + dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__); +- rc = -1; +- goto out; ++ return -1; + } + + volrd = get_unaligned_be32(&data[0]); + volwr = get_unaligned_be32(tas_dev->dvc_tlv_table[vol]); + +- if (volrd == volwr) { +- rc = 0; +- goto out; +- } ++ if (volrd == volwr) ++ return 0; + + for (i = 0; i < tas_dev->ndev; i++) { + ret = tasdevice_dev_bulk_write(tas_dev, i, reg, +@@ -918,10 +924,9 @@ static int tasdevice_digital_gain_put( + } + + if (status) +- rc = -1; +-out: +- mutex_unlock(&tas_dev->codec_lock); +- return rc; ++ return -1; ++ ++ return 1; + } + + static const struct snd_kcontrol_new tasdevice_cali_controls[] = { +@@ -995,6 +1000,57 @@ static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol, + return ret; + } + ++/** ++ * tasdevice_set_capture_profile_id - Set runtime capture profile index via ++ * ALSA control ++ * @kcontrol: ALSA kcontrol handle that triggers this operation ++ * @ucontrol: User space control value carrying the new profile index ++ * ++ * This mixer control handler validates the user-provided capture profile ID ++ * against the maximum valid index parsed from the loaded DSP firmware, ++ * then updates the runtime stored capture profile ID only if the new value ++ * differs from the current active one. It will immediately return -EINVAL ++ * if the submitted profile ID falls outside the valid range, including the ++ * edge case that no valid configuration blocks are detected in firmware. ++ * ++ * No actual DSP register write is performed in this handler. The updated ++ * profile ID will be applied to the hardware when the next ALSA capture ++ * stream starts up. Caller does not need to take extra codec lock here, ++ * as the ALSA control core already guarantees serialized execution. ++ * ++ * Return: 1 if profile ID value was changed, 0 if no modification needed, ++ * -EINVAL if the input profile ID is out of valid range ++ */ ++static int tasdevice_set_capture_profile_id(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol); ++ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); ++ unsigned int user_prof_id = ucontrol->value.integer.value[0]; ++ unsigned int max_valid_id; ++ int ret = 0; ++ ++ /* ++ * Align valid range with the bound defined in ++ * tasdevice_info_profile() ++ */ ++ max_valid_id = tas_priv->rcabin.ncfgs - 1; ++ ++ /* ++ * Reject invalid input including zero total configuration edge ++ * case ++ */ ++ if (tas_priv->rcabin.ncfgs == 0 || user_prof_id > max_valid_id) ++ return -EINVAL; ++ ++ if (tas_priv->rcabin.capture_profile_id != user_prof_id) { ++ tas_priv->rcabin.capture_profile_id = user_prof_id; ++ ret = 1; ++ } ++ ++ return ret; ++} ++ + static int tasdevice_info_active_num(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) + { +@@ -1075,6 +1131,41 @@ static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol, + return 0; + } + ++/** ++ * tasdevice_get_capture_profile_id - Report current active capture profile ++ * ID to user space ++ * @kcontrol: ALSA kcontrol structure passed from ALSA core ++ * @ucontrol: User-space control element value buffer to write the result back ++ * ++ * This function ensures the returned profile ID is always clamped inside the ++ * valid range advertised by the info callback, preventing accidental invalid ++ * values from being exposed to applications even if internal driver state is ++ * temporarily inconsistent. ++ * ++ * Returns 0 on successful fill of the control value, no error conditions ++ * are defined for this getter callback. ++ */ ++static int tasdevice_get_capture_profile_id(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol); ++ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); ++ unsigned int max_valid_id, current_prof_id; ++ ++ max_valid_id = tas_priv->rcabin.ncfgs > 0 ? ++ (tas_priv->rcabin.ncfgs - 1U) : 0; ++ ++ /* ++ * Cast current profile id to unsigned to match type with max_valid_id, ++ * avoid signedness mismatch; ++ */ ++ current_prof_id = (unsigned int)tas_priv->rcabin.capture_profile_id; ++ /* Prevent underflow when there are no loaded capture profiles. */ ++ ucontrol->value.integer.value[0] = min(current_prof_id, max_valid_id); ++ ++ return 0; ++} ++ + static int tasdevice_get_chip_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) + { +@@ -1117,6 +1208,41 @@ static int tasdevice_create_control(struct tasdevice_priv *tas_priv) + ret = snd_soc_add_component_controls(tas_priv->codec, + prof_ctrls, nr_controls < mix_index ? nr_controls : mix_index); + ++ mix_index = 0; ++ switch (tas_priv->chip_id) { ++ case TAS2563: ++ case TAS2568: ++ case TAS2570: ++ case TAS2572: ++ case TAS2573: ++ case TAS2574: ++ case TAS2781: ++ prof_ctrls = devm_kcalloc(tas_priv->dev, nr_controls, ++ sizeof(prof_ctrls[0]), GFP_KERNEL); ++ if (!prof_ctrls) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ /* Create a mixer item for selecting the capture profile */ ++ name = devm_kstrdup(tas_priv->dev, "Speaker Capture Profile Id", ++ GFP_KERNEL); ++ if (!name) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ prof_ctrls[mix_index].name = name; ++ prof_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; ++ prof_ctrls[mix_index].info = tasdevice_info_profile; ++ prof_ctrls[mix_index].get = tasdevice_get_capture_profile_id; ++ prof_ctrls[mix_index].put = tasdevice_set_capture_profile_id; ++ mix_index++; ++ ++ ret = snd_soc_add_component_controls(tas_priv->codec, ++ prof_ctrls, nr_controls < mix_index ? nr_controls : mix_index); ++ break; ++ } ++ + out: + return ret; + } +@@ -1330,6 +1456,43 @@ static void alpa_cali_update(struct bulk_reg_val *p, + p->val_len = 4; + } + ++static int create_tas2781_cali_start_ktrl(struct tasdevice_priv ++ *priv, struct snd_kcontrol_new *cali_ctrl) ++{ ++ struct soc_bytes_ext *ext_cali_start; ++ char *cali_start_name; ++ ++ ext_cali_start = devm_kzalloc(priv->dev, ++ sizeof(*ext_cali_start), GFP_KERNEL); ++ if (!ext_cali_start) ++ return -ENOMEM; ++ ++ cali_start_name = devm_kstrdup(priv->dev, ++ "Calibration Start", GFP_KERNEL); ++ if (!cali_start_name) ++ return -ENOMEM; ++ /* ++ * package structure for tas2781 ftc start: ++ * Pkg len (1 byte) ++ * Reg id (1 byte, constant 'r') ++ * book, page, register for pilot threshold, pilot tone ++ * and sine gain (12 bytes) ++ * for (i = 0; i < Device-Sum; i++) { ++ * Device #i index_info (1 byte) ++ * Sine gain for Device #i (8 bytes) ++ * } ++ */ ++ ext_cali_start->max = 14 + priv->ndev * 9; ++ cali_ctrl->name = cali_start_name; ++ cali_ctrl->iface = SNDRV_CTL_ELEM_IFACE_MIXER; ++ cali_ctrl->info = snd_soc_bytes_info_ext; ++ cali_ctrl->put = tas2781_calib_start_put; ++ cali_ctrl->get = tasdev_nop_get; ++ cali_ctrl->private_value = (unsigned long)ext_cali_start; ++ ++ return 0; ++} ++ + static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv) + { + struct calidata *cali_data = &priv->cali_data; +@@ -1448,37 +1611,11 @@ static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv) + */ + cali_data->data[0] = 0xff; + if (priv->chip_id == TAS2781) { +- struct soc_bytes_ext *ext_cali_start; +- char *cali_start_name; +- +- ext_cali_start = devm_kzalloc(priv->dev, +- sizeof(*ext_cali_start), GFP_KERNEL); +- if (!ext_cali_start) +- return -ENOMEM; +- +- cali_start_name = devm_kstrdup(priv->dev, +- "Calibration Start", GFP_KERNEL); +- if (!cali_start_name) +- return -ENOMEM; +- /* +- * package structure for tas2781 ftc start: +- * Pkg len (1 byte) +- * Reg id (1 byte, constant 'r') +- * book, page, register for pilot threshold, pilot tone +- * and sine gain (12 bytes) +- * for (i = 0; i < Device-Sum; i++) { +- * Device #i index_info (1 byte) +- * Sine gain for Device #i (8 bytes) +- * } +- */ +- ext_cali_start->max = 14 + priv->ndev * 9; +- cali_ctrls[i].name = cali_start_name; +- cali_ctrls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; +- cali_ctrls[i].info = snd_soc_bytes_info_ext; +- cali_ctrls[i].put = tas2781_calib_start_put; +- cali_ctrls[i].get = tasdev_nop_get; +- cali_ctrls[i].private_value = (unsigned long)ext_cali_start; ++ rc = create_tas2781_cali_start_ktrl(priv, &cali_ctrls[i]); ++ if (rc != 0) ++ return rc; + i++; ++ + } + + return snd_soc_add_component_controls(priv->codec, cali_ctrls, +@@ -1765,13 +1902,25 @@ static int tasdevice_dapm_event(struct snd_soc_dapm_widget *w, + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + int state = 0; + +- /* Codec Lock Hold */ +- mutex_lock(&tas_priv->codec_lock); ++ guard(mutex)(&tas_priv->codec_lock); + if (event == SND_SOC_DAPM_PRE_PMD) + state = 1; +- tasdevice_tuning_switch(tas_priv, state); +- /* Codec Lock Release*/ +- mutex_unlock(&tas_priv->codec_lock); ++ tasdevice_tuning_switch(tas_priv, state, false); ++ ++ return 0; ++} ++ ++static int tasdevice_capture_dapm_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, int event) ++{ ++ struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); ++ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); ++ int state = 0; ++ ++ guard(mutex)(&tas_priv->codec_lock); ++ if (event == SND_SOC_DAPM_PRE_PMD) ++ state = 1; ++ tasdevice_tuning_switch(tas_priv, state, true); + + return 0; + } +@@ -1779,7 +1928,7 @@ static int tasdevice_dapm_event(struct snd_soc_dapm_widget *w, + static const struct snd_soc_dapm_widget tasdevice_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("ASI", "ASI Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT_E("ASI OUT", "ASI Capture", 0, SND_SOC_NOPM, +- 0, 0, tasdevice_dapm_event, ++ 0, 0, tasdevice_capture_dapm_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SPK("SPK", tasdevice_dapm_event), + SND_SOC_DAPM_OUTPUT("OUT"), +diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c +--- a/sound/soc/codecs/tas2783-sdw.c ++++ b/sound/soc/codecs/tas2783-sdw.c +@@ -12,6 +12,7 @@ + // Author: Baojun Xu + // Author: Kevin Lu + ++#include + #include + #include + #include +@@ -494,6 +495,56 @@ static bool tas2783_readable_register(struct device *dev, unsigned int reg) + return tas2783_sdca_mbq_size(dev, reg) > 0; + } + ++static bool tas2783_writeable_register(struct device *dev, unsigned int reg) ++{ ++ /* ++ * The Latency Control of every Entity, together with the Power Domain ++ * actual state and the protection status, is read-only. They are ++ * listed in tas2783_reg_default[] with a placeholder value, so without ++ * this a regcache_sync() would try to write them back and the ++ * peripheral would reject the transaction, aborting the sync. ++ */ ++ switch (reg) { ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU21, 0x10, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU23, 0x10, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU26, 0x10, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_XU22, 0x06, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_XU22, 0x07, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_XU22, 0x08, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS24, 0x02, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS21, 0x02, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS25, 0x02, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS26, 0x02, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS28, 0x02, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_PDE23, 0x10, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_UDMPU23, 0x06, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_SAPU29, 0x05, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_SAPU29, 0x11, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_PPU21, 0x06, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_PPU26, 0x06, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT21, 0x08, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT29, 0x08, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT26, 0x08, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT28, 0x08, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT24, 0x08, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT23, 0x08, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT25, 0x08, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT28, 0x08, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MU26, 0x06, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT127, 0x08, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU127, 0x10, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS127, 0x02, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU21, 0x08, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU21, 0x04, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU26, 0x08, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU26, 0x04, 0): ++ return false; ++ ++ default: ++ return tas2783_sdca_mbq_size(dev, reg) > 0; ++ } ++} ++ + static bool tas2783_volatile_register(struct device *dev, u32 reg) + { + switch (reg) { +@@ -515,6 +566,7 @@ static const struct regmap_config tas_regmap = { + .reg_bits = 32, + .val_bits = 8, + .readable_reg = tas2783_readable_register, ++ .writeable_reg = tas2783_writeable_register, + .volatile_reg = tas2783_volatile_register, + .reg_defaults = tas2783_reg_default, + .num_reg_defaults = ARRAY_SIZE(tas2783_reg_default), +@@ -692,12 +744,12 @@ static s32 tas2783_update_calibdata(struct tas2783_prv *tas_dev) + return 0; + } + +- mutex_lock(&tas_dev->calib_lock); +- ret = tas2783_validate_calibdata(tas_dev, tas_dev->cali_data.data, +- tas_dev->cali_data.read_sz); +- if (!ret) +- tas2783_set_calib_params_to_device(tas_dev, tmp_val); +- mutex_unlock(&tas_dev->calib_lock); ++ scoped_guard(mutex, &tas_dev->calib_lock) { ++ ret = tas2783_validate_calibdata(tas_dev, tas_dev->cali_data.data, ++ tas_dev->cali_data.read_sz); ++ if (!ret) ++ tas2783_set_calib_params_to_device(tas_dev, tmp_val); ++ } + + return ret; + } +@@ -926,22 +978,23 @@ static s32 tas_sdw_hw_params(struct snd_pcm_substream *substream, + dev_err(tas_dev->dev, + "clear latch failed, err=%d", ret); + +- mutex_lock(&tas_dev->pde_lock); +- /* +- * Sometimes, there is error returned during power on. +- * So added retry logic to ensure power on so that +- * port prepare succeeds +- */ +- do { +- ret = regmap_write(tas_dev->regmap, +- SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, +- TAS2783_SDCA_CTL_REQ_POW_STATE, 0), +- TAS2783_SDCA_POW_STATE_ON); +- if (!ret) +- break; +- usleep_range(2000, 2200); +- } while (retry--); +- mutex_unlock(&tas_dev->pde_lock); ++ scoped_guard(mutex, &tas_dev->pde_lock) { ++ /* ++ * Sometimes, there is error returned during power on. ++ * So added retry logic to ensure power on so that ++ * port prepare succeeds ++ */ ++ do { ++ ret = regmap_write(tas_dev->regmap, ++ SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, ++ TAS2783_SDCA_CTL_REQ_POW_STATE, 0), ++ TAS2783_SDCA_POW_STATE_ON); ++ if (!ret) ++ break; ++ usleep_range(2000, 2200); ++ } while (retry--); ++ } ++ + if (ret) + return ret; + +@@ -965,7 +1018,6 @@ static s32 tas_sdw_hw_params(struct snd_pcm_substream *substream, + static s32 tas_sdw_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + { +- s32 ret; + struct snd_soc_component *component = dai->component; + struct tas2783_prv *tas_dev = + snd_soc_component_get_drvdata(component); +@@ -974,14 +1026,11 @@ static s32 tas_sdw_pcm_hw_free(struct snd_pcm_substream *substream, + + sdw_stream_remove_slave(tas_dev->sdw_peripheral, sdw_stream); + +- mutex_lock(&tas_dev->pde_lock); +- ret = regmap_write(tas_dev->regmap, +- SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, +- TAS2783_SDCA_CTL_REQ_POW_STATE, 0), +- TAS2783_SDCA_POW_STATE_OFF); +- mutex_unlock(&tas_dev->pde_lock); +- +- return ret; ++ guard(mutex)(&tas_dev->pde_lock); ++ return regmap_write(tas_dev->regmap, ++ SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, ++ TAS2783_SDCA_CTL_REQ_POW_STATE, 0), ++ TAS2783_SDCA_POW_STATE_OFF); + } + + static const struct snd_soc_dai_ops tas_dai_ops = { +@@ -1349,7 +1398,7 @@ static s32 tas_sdw_probe(struct sdw_slave *peripheral, + function_data->desc = &peripheral->sdca_data.function[i]; + + /* Parse the function */ +- ret = sdca_parse_function(dev, peripheral, function_data); ++ ret = sdca_parse_function(dev, function_data); + if (!ret) + tas_dev->sa_func_data = function_data; + else +diff --git a/sound/soc/codecs/tas5805m.c b/sound/soc/codecs/tas5805m.c +--- a/sound/soc/codecs/tas5805m.c ++++ b/sound/soc/codecs/tas5805m.c +@@ -12,6 +12,7 @@ + // + // It has been simplified a little and reworked for the 5.x ALSA SoC API. + ++#include + #include + #include + #include +@@ -230,10 +231,9 @@ static int tas5805m_vol_get(struct snd_kcontrol *kcontrol, + struct tas5805m_priv *tas5805m = + snd_soc_component_get_drvdata(component); + +- mutex_lock(&tas5805m->lock); ++ guard(mutex)(&tas5805m->lock); + ucontrol->value.integer.value[0] = tas5805m->vol[0]; + ucontrol->value.integer.value[1] = tas5805m->vol[1]; +- mutex_unlock(&tas5805m->lock); + + return 0; + } +@@ -249,13 +249,12 @@ static int tas5805m_vol_put(struct snd_kcontrol *kcontrol, + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct tas5805m_priv *tas5805m = + snd_soc_component_get_drvdata(component); +- int ret = 0; + + if (!(volume_is_valid(ucontrol->value.integer.value[0]) && + volume_is_valid(ucontrol->value.integer.value[1]))) + return -EINVAL; + +- mutex_lock(&tas5805m->lock); ++ guard(mutex)(&tas5805m->lock); + if (tas5805m->vol[0] != ucontrol->value.integer.value[0] || + tas5805m->vol[1] != ucontrol->value.integer.value[1]) { + tas5805m->vol[0] = ucontrol->value.integer.value[0]; +@@ -265,11 +264,10 @@ static int tas5805m_vol_put(struct snd_kcontrol *kcontrol, + tas5805m->is_powered); + if (tas5805m->is_powered) + tas5805m_refresh(tas5805m); +- ret = 1; ++ return 1; + } +- mutex_unlock(&tas5805m->lock); + +- return ret; ++ return 0; + } + + static const struct snd_kcontrol_new tas5805m_snd_controls[] = { +@@ -332,7 +330,7 @@ static void do_work(struct work_struct *work) + + dev_dbg(&tas5805m->i2c->dev, "DSP startup\n"); + +- mutex_lock(&tas5805m->lock); ++ guard(mutex)(&tas5805m->lock); + /* We mustn't issue any I2C transactions until the I2S + * clock is stable. Furthermore, we must allow a 5ms + * delay after the first set of register writes to +@@ -345,7 +343,6 @@ static void do_work(struct work_struct *work) + + tas5805m->is_powered = true; + tas5805m_refresh(tas5805m); +- mutex_unlock(&tas5805m->lock); + } + + static int tas5805m_dac_event(struct snd_soc_dapm_widget *w, +@@ -362,7 +359,7 @@ static int tas5805m_dac_event(struct snd_soc_dapm_widget *w, + dev_dbg(component->dev, "DSP shutdown\n"); + cancel_work_sync(&tas5805m->work); + +- mutex_lock(&tas5805m->lock); ++ guard(mutex)(&tas5805m->lock); + if (tas5805m->is_powered) { + tas5805m->is_powered = false; + +@@ -379,7 +376,6 @@ static int tas5805m_dac_event(struct snd_soc_dapm_widget *w, + + regmap_write(rm, REG_DEVICE_CTRL_2, DCTRL2_MODE_HIZ); + } +- mutex_unlock(&tas5805m->lock); + } + + return 0; +@@ -414,14 +410,13 @@ static int tas5805m_mute(struct snd_soc_dai *dai, int mute, int direction) + struct tas5805m_priv *tas5805m = + snd_soc_component_get_drvdata(component); + +- mutex_lock(&tas5805m->lock); ++ guard(mutex)(&tas5805m->lock); + dev_dbg(component->dev, "set mute=%d (is_powered=%d)\n", + mute, tas5805m->is_powered); + + tas5805m->is_muted = mute; + if (tas5805m->is_powered) + tas5805m_refresh(tas5805m); +- mutex_unlock(&tas5805m->lock); + + return 0; + } +@@ -462,7 +457,6 @@ static int tas5805m_i2c_probe(struct i2c_client *i2c) + struct tas5805m_priv *tas5805m; + char filename[128]; + const char *config_name; +- const struct firmware *fw; + int ret; + + regmap = devm_regmap_init_i2c(i2c, &tas5805m_regmap); +@@ -507,24 +501,20 @@ static int tas5805m_i2c_probe(struct i2c_client *i2c) + + snprintf(filename, sizeof(filename), "tas5805m_dsp_%s.bin", + config_name); ++ const struct firmware *fw __free(firmware) = NULL; + ret = request_firmware(&fw, filename, dev); + if (ret) + return ret; + + if ((fw->size < 2) || (fw->size & 1)) { + dev_err(dev, "firmware is invalid\n"); +- release_firmware(fw); + return -EINVAL; + } + + tas5805m->dsp_cfg_len = fw->size; + tas5805m->dsp_cfg_data = devm_kmemdup(dev, fw->data, fw->size, GFP_KERNEL); +- if (!tas5805m->dsp_cfg_data) { +- release_firmware(fw); ++ if (!tas5805m->dsp_cfg_data) + return -ENOMEM; +- } +- +- release_firmware(fw); + + /* Do the first part of the power-on here, while we can expect + * the I2S interface to be quiet. We must raise PDN# and then +diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c +--- a/sound/soc/codecs/tlv320aic26.c ++++ b/sound/soc/codecs/tlv320aic26.c +@@ -320,8 +320,14 @@ static int aic26_probe(struct snd_soc_component *component) + return 0; + } + ++static void aic26_remove(struct snd_soc_component *component) ++{ ++ device_remove_file(component->dev, &dev_attr_keyclick); ++} ++ + static const struct snd_soc_component_driver aic26_soc_component_dev = { + .probe = aic26_probe, ++ .remove = aic26_remove, + .controls = aic26_snd_controls, + .num_controls = ARRAY_SIZE(aic26_snd_controls), + .dapm_widgets = tlv320aic26_dapm_widgets, +diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c +--- a/sound/soc/codecs/tlv320aic31xx.c ++++ b/sound/soc/codecs/tlv320aic31xx.c +@@ -1720,18 +1720,14 @@ static int tlv320dac3100_fw_load(struct aic31xx_priv *aic31xx, + static int tlv320dac3100_load_coeffs(struct aic31xx_priv *aic31xx, + const char *fw_name) + { +- const struct firmware *fw; ++ const struct firmware *fw __free(firmware) = NULL; + int ret; + + ret = request_firmware(&fw, fw_name, aic31xx->dev); + if (ret) + return ret; + +- ret = tlv320dac3100_fw_load(aic31xx, fw->data, fw->size); +- +- release_firmware(fw); +- +- return ret; ++ return tlv320dac3100_fw_load(aic31xx, fw->data, fw->size); + } + + static int aic31xx_i2c_probe(struct i2c_client *i2c) +diff --git a/sound/soc/codecs/tlv320aic32x4-clk.c b/sound/soc/codecs/tlv320aic32x4-clk.c +--- a/sound/soc/codecs/tlv320aic32x4-clk.c ++++ b/sound/soc/codecs/tlv320aic32x4-clk.c +@@ -1,5 +1,5 @@ +-/* SPDX-License-Identifier: GPL-2.0 +- * ++// SPDX-License-Identifier: GPL-2.0 ++/* + * Clock Tree for the Texas Instruments TLV320AIC32x4 + * + * Copyright 2019 Annaliese McDermond +@@ -9,6 +9,7 @@ + + #include + #include ++#include + #include + #include + +@@ -57,7 +58,7 @@ static void clk_aic32x4_pll_unprepare(struct clk_hw *hw) + struct clk_aic32x4 *pll = to_clk_aic32x4(hw); + + regmap_update_bits(pll->regmap, AIC32X4_PLLPR, +- AIC32X4_PLLEN, 0); ++ AIC32X4_PLLEN, 0); + } + + static int clk_aic32x4_pll_is_prepared(struct clk_hw *hw) +@@ -75,7 +76,7 @@ static int clk_aic32x4_pll_is_prepared(struct clk_hw *hw) + } + + static int clk_aic32x4_pll_get_muldiv(struct clk_aic32x4 *pll, +- struct clk_aic32x4_pll_muldiv *settings) ++ struct clk_aic32x4_pll_muldiv *settings) + { + /* Change to use regmap_bulk_read? */ + unsigned int val; +@@ -106,19 +107,19 @@ static int clk_aic32x4_pll_get_muldiv(struct clk_aic32x4 *pll, + } + + static int clk_aic32x4_pll_set_muldiv(struct clk_aic32x4 *pll, +- struct clk_aic32x4_pll_muldiv *settings) ++ struct clk_aic32x4_pll_muldiv *settings) + { + int ret; + /* Change to use regmap_bulk_write for some if not all? */ + + ret = regmap_update_bits(pll->regmap, AIC32X4_PLLPR, +- AIC32X4_PLL_R_MASK, settings->r); ++ AIC32X4_PLL_R_MASK, settings->r); + if (ret < 0) + return ret; + + ret = regmap_update_bits(pll->regmap, AIC32X4_PLLPR, +- AIC32X4_PLL_P_MASK, +- settings->p << AIC32X4_PLL_P_SHIFT); ++ AIC32X4_PLL_P_MASK, ++ settings->p << AIC32X4_PLL_P_SHIFT); + if (ret < 0) + return ret; + +@@ -136,23 +137,21 @@ static int clk_aic32x4_pll_set_muldiv(struct clk_aic32x4 *pll, + return 0; + } + +-static unsigned long clk_aic32x4_pll_calc_rate( +- struct clk_aic32x4_pll_muldiv *settings, +- unsigned long parent_rate) ++static unsigned long clk_aic32x4_pll_calc_rate(struct clk_aic32x4_pll_muldiv *settings, ++ unsigned long parent_rate) + { + u64 rate; + /* + * We scale j by 10000 to account for the decimal part of P and divide + * it back out later. + */ +- rate = (u64) parent_rate * settings->r * +- ((settings->j * 10000) + settings->d); ++ rate = (u64)parent_rate * settings->r * ((settings->j * 10000) + settings->d); + +- return (unsigned long) DIV_ROUND_UP_ULL(rate, settings->p * 10000); ++ return (unsigned long)DIV_ROUND_UP_ULL(rate, settings->p * 10000); + } + + static int clk_aic32x4_pll_calc_muldiv(struct clk_aic32x4_pll_muldiv *settings, +- unsigned long rate, unsigned long parent_rate) ++ unsigned long rate, unsigned long parent_rate) + { + u64 multiplier; + +@@ -165,14 +164,14 @@ static int clk_aic32x4_pll_calc_muldiv(struct clk_aic32x4_pll_muldiv *settings, + * of the multiplier. This is because we can't do floating point + * math in the kernel. + */ +- multiplier = (u64) rate * settings->p * 10000; ++ multiplier = (u64)rate * settings->p * 10000; + do_div(multiplier, parent_rate); + + /* + * J can't be over 64, so R can scale this. + * R can't be greater than 4. + */ +- settings->r = ((u32) multiplier / 640000) + 1; ++ settings->r = ((u32)multiplier / 640000) + 1; + if (settings->r > 4) + return -1; + do_div(multiplier, settings->r); +@@ -184,14 +183,14 @@ static int clk_aic32x4_pll_calc_muldiv(struct clk_aic32x4_pll_muldiv *settings, + return -1; + + /* Figure out the integer part, J, and the fractional part, D. */ +- settings->j = (u32) multiplier / 10000; +- settings->d = (u32) multiplier % 10000; ++ settings->j = (u32)multiplier / 10000; ++ settings->d = (u32)multiplier % 10000; + + return 0; + } + + static unsigned long clk_aic32x4_pll_recalc_rate(struct clk_hw *hw, +- unsigned long parent_rate) ++ unsigned long parent_rate) + { + struct clk_aic32x4 *pll = to_clk_aic32x4(hw); + struct clk_aic32x4_pll_muldiv settings; +@@ -220,8 +219,8 @@ static int clk_aic32x4_pll_determine_rate(struct clk_hw *hw, + } + + static int clk_aic32x4_pll_set_rate(struct clk_hw *hw, +- unsigned long rate, +- unsigned long parent_rate) ++ unsigned long rate, ++ unsigned long parent_rate) + { + struct clk_aic32x4 *pll = to_clk_aic32x4(hw); + struct clk_aic32x4_pll_muldiv settings; +@@ -236,7 +235,7 @@ static int clk_aic32x4_pll_set_rate(struct clk_hw *hw, + return ret; + + /* 10ms is the delay to wait before the clocks are stable */ +- msleep(10); ++ usleep_range(10000, 20000); + + return 0; + } +@@ -261,7 +260,6 @@ static u8 clk_aic32x4_pll_get_parent(struct clk_hw *hw) + return (val & AIC32X4_PLL_CLKIN_MASK) >> AIC32X4_PLL_CLKIN_SHIFT; + } + +- + static const struct clk_ops aic32x4_pll_ops = { + .prepare = clk_aic32x4_pll_prepare, + .unprepare = clk_aic32x4_pll_unprepare, +@@ -311,11 +309,11 @@ static void clk_aic32x4_div_unprepare(struct clk_hw *hw) + struct clk_aic32x4 *div = to_clk_aic32x4(hw); + + regmap_update_bits(div->regmap, div->reg, +- AIC32X4_DIVEN, 0); ++ AIC32X4_DIVEN, 0); + } + + static int clk_aic32x4_div_set_rate(struct clk_hw *hw, unsigned long rate, +- unsigned long parent_rate) ++ unsigned long parent_rate) + { + struct clk_aic32x4 *div = to_clk_aic32x4(hw); + u8 divisor; +@@ -342,7 +340,7 @@ static int clk_aic32x4_div_determine_rate(struct clk_hw *hw, + } + + static unsigned long clk_aic32x4_div_recalc_rate(struct clk_hw *hw, +- unsigned long parent_rate) ++ unsigned long parent_rate) + { + struct clk_aic32x4 *div = to_clk_aic32x4(hw); + unsigned int val; +@@ -399,7 +397,7 @@ static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { + { + .name = "pll", + .parent_names = +- (const char* []) { "mclk", "bclk", "gpio", "din" }, ++ (const char *[]) { "mclk", "bclk", "gpio", "din" }, + .num_parents = 4, + .ops = &aic32x4_pll_ops, + .reg = 0, +@@ -414,28 +412,28 @@ static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { + }, + { + .name = "ndac", +- .parent_names = (const char * []) { "codec_clkin" }, ++ .parent_names = (const char *[]) { "codec_clkin" }, + .num_parents = 1, + .ops = &aic32x4_div_ops, + .reg = AIC32X4_NDAC, + }, + { + .name = "mdac", +- .parent_names = (const char * []) { "ndac" }, ++ .parent_names = (const char *[]) { "ndac" }, + .num_parents = 1, + .ops = &aic32x4_div_ops, + .reg = AIC32X4_MDAC, + }, + { + .name = "nadc", +- .parent_names = (const char * []) { "codec_clkin" }, ++ .parent_names = (const char *[]) { "codec_clkin" }, + .num_parents = 1, + .ops = &aic32x4_div_ops, + .reg = AIC32X4_NADC, + }, + { + .name = "madc", +- .parent_names = (const char * []) { "nadc" }, ++ .parent_names = (const char *[]) { "nadc" }, + .num_parents = 1, + .ops = &aic32x4_div_ops, + .reg = AIC32X4_MADC, +@@ -451,7 +449,7 @@ static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { + }; + + static struct clk *aic32x4_register_clk(struct device *dev, +- struct aic32x4_clkdesc *desc) ++ struct aic32x4_clkdesc *desc) + { + struct clk_init_data init; + struct clk_aic32x4 *priv; +@@ -464,8 +462,8 @@ static struct clk *aic32x4_register_clk(struct device *dev, + init.flags = 0; + + priv = devm_kzalloc(dev, sizeof(struct clk_aic32x4), GFP_KERNEL); +- if (priv == NULL) +- return (struct clk *) -ENOMEM; ++ if (!priv) ++ return ERR_PTR(-ENOMEM); + + priv->dev = dev; + priv->hw.init = &init; +diff --git a/sound/soc/codecs/tlv320aic32x4-i2c.c b/sound/soc/codecs/tlv320aic32x4-i2c.c +--- a/sound/soc/codecs/tlv320aic32x4-i2c.c ++++ b/sound/soc/codecs/tlv320aic32x4-i2c.c +@@ -1,11 +1,10 @@ +-/* SPDX-License-Identifier: GPL-2.0 +- * ++// SPDX-License-Identifier: GPL-2.0 ++/* + * Copyright 2011-2019 NW Digital Radio + * + * Author: Annaliese McDermond + * + * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27. +- * + */ + + #include +@@ -16,17 +15,23 @@ + + #include "tlv320aic32x4.h" + ++static const struct regmap_config aic32x4_i2c_regmap_config = { ++ .reg_bits = 8, ++ .val_bits = 8, ++ .max_register = AIC32X4_REFPOWERUP, ++ .ranges = aic32x4_regmap_pages, ++ .num_ranges = 1, ++}; ++ + static int aic32x4_i2c_probe(struct i2c_client *i2c) + { + struct regmap *regmap; +- struct regmap_config config; + enum aic32x4_type type; + +- config = aic32x4_regmap_config; +- config.reg_bits = 8; +- config.val_bits = 8; ++ regmap = devm_regmap_init_i2c(i2c, &aic32x4_i2c_regmap_config); ++ if (IS_ERR(regmap)) ++ return PTR_ERR(regmap); + +- regmap = devm_regmap_init_i2c(i2c, &config); + type = (uintptr_t)i2c_get_match_data(i2c); + + return aic32x4_probe(&i2c->dev, regmap, type); +diff --git a/sound/soc/codecs/tlv320aic32x4-spi.c b/sound/soc/codecs/tlv320aic32x4-spi.c +--- a/sound/soc/codecs/tlv320aic32x4-spi.c ++++ b/sound/soc/codecs/tlv320aic32x4-spi.c +@@ -1,11 +1,10 @@ +-/* SPDX-License-Identifier: GPL-2.0 +- * ++// SPDX-License-Identifier: GPL-2.0 ++/* + * Copyright 2011-2019 NW Digital Radio + * + * Author: Annaliese McDermond + * + * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27. +- * + */ + + #include +@@ -16,19 +15,25 @@ + + #include "tlv320aic32x4.h" + ++static const struct regmap_config aic32x4_spi_regmap_config = { ++ .reg_bits = 7, ++ .pad_bits = 1, ++ .val_bits = 8, ++ .read_flag_mask = 0x01, ++ .max_register = AIC32X4_REFPOWERUP, ++ .ranges = aic32x4_regmap_pages, ++ .num_ranges = 1, ++}; ++ + static int aic32x4_spi_probe(struct spi_device *spi) + { + struct regmap *regmap; +- struct regmap_config config; + enum aic32x4_type type; + +- config = aic32x4_regmap_config; +- config.reg_bits = 7; +- config.pad_bits = 1; +- config.val_bits = 8; +- config.read_flag_mask = 0x01; ++ regmap = devm_regmap_init_spi(spi, &aic32x4_spi_regmap_config); ++ if (IS_ERR(regmap)) ++ return PTR_ERR(regmap); + +- regmap = devm_regmap_init_spi(spi, &config); + type = (uintptr_t)spi_get_device_match_data(spi); + + return aic32x4_probe(&spi->dev, regmap, type); +diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c +--- a/sound/soc/codecs/tlv320aic32x4.c ++++ b/sound/soc/codecs/tlv320aic32x4.c +@@ -1,7 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0-or-later + /* +- * linux/sound/soc/codecs/tlv320aic32x4.c +- * + * Copyright 2011 Vista Silicon S.L. + * + * Author: Javier Martin +@@ -28,7 +26,6 @@ + #include + #include + #include +-#include + + #include "tlv320aic32x4.h" + +@@ -45,7 +42,8 @@ struct aic32x4_priv { + struct regulator *supply_dv; + struct regulator *supply_av; + +- struct aic32x4_setup_data *setup; ++ unsigned int gpio_func[5]; ++ + struct device *dev; + enum aic32x4_type type; + +@@ -75,7 +73,7 @@ static int aic32x4_reset_adc(struct snd_soc_dapm_widget *w, + }; + + static int mic_bias_event(struct snd_soc_dapm_widget *w, +- struct snd_kcontrol *kcontrol, int event) ++ struct snd_kcontrol *kcontrol, int event) + { + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + +@@ -83,25 +81,23 @@ static int mic_bias_event(struct snd_soc_dapm_widget *w, + case SND_SOC_DAPM_POST_PMU: + /* Change Mic Bias Registor */ + snd_soc_component_update_bits(component, AIC32X4_MICBIAS, +- AIC32x4_MICBIAS_MASK, +- AIC32X4_MICBIAS_LDOIN | +- AIC32X4_MICBIAS_2075V); +- printk(KERN_DEBUG "%s: Mic Bias will be turned ON\n", __func__); ++ AIC32x4_MICBIAS_MASK, ++ AIC32X4_MICBIAS_LDOIN | ++ AIC32X4_MICBIAS_2075V); ++ dev_dbg(component->dev, "Mic Bias will be turned ON\n"); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, AIC32X4_MICBIAS, +- AIC32x4_MICBIAS_MASK, 0); +- printk(KERN_DEBUG "%s: Mic Bias will be turned OFF\n", +- __func__); ++ AIC32x4_MICBIAS_MASK, 0); ++ dev_dbg(component->dev, "Mic Bias will be turned OFF\n"); + break; + } + + return 0; + } + +- + static int aic32x4_get_mfp1_gpio(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) ++ struct snd_ctl_elem_value *ucontrol) + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + u8 val; +@@ -114,7 +110,7 @@ static int aic32x4_get_mfp1_gpio(struct snd_kcontrol *kcontrol, + }; + + static int aic32x4_set_mfp2_gpio(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) ++ struct snd_ctl_elem_value *ucontrol) + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + u8 val; +@@ -123,8 +119,7 @@ static int aic32x4_set_mfp2_gpio(struct snd_kcontrol *kcontrol, + val = snd_soc_component_read(component, AIC32X4_DOUTCTL); + gpio_check = (val & AIC32X4_MFP_GPIO_ENABLED); + if (gpio_check != AIC32X4_MFP_GPIO_ENABLED) { +- printk(KERN_ERR "%s: MFP2 is not configure as a GPIO output\n", +- __func__); ++ dev_err(component->dev, "MFP2 is not configure as a GPIO output\n"); + return -EINVAL; + } + +@@ -142,7 +137,7 @@ static int aic32x4_set_mfp2_gpio(struct snd_kcontrol *kcontrol, + }; + + static int aic32x4_get_mfp3_gpio(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) ++ struct snd_ctl_elem_value *ucontrol) + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + u8 val; +@@ -155,7 +150,7 @@ static int aic32x4_get_mfp3_gpio(struct snd_kcontrol *kcontrol, + }; + + static int aic32x4_set_mfp4_gpio(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) ++ struct snd_ctl_elem_value *ucontrol) + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + u8 val; +@@ -164,8 +159,7 @@ static int aic32x4_set_mfp4_gpio(struct snd_kcontrol *kcontrol, + val = snd_soc_component_read(component, AIC32X4_MISOCTL); + gpio_check = (val & AIC32X4_MFP_GPIO_ENABLED); + if (gpio_check != AIC32X4_MFP_GPIO_ENABLED) { +- printk(KERN_ERR "%s: MFP4 is not configure as a GPIO output\n", +- __func__); ++ dev_err(component->dev, "MFP4 is not configure as a GPIO output\n"); + return -EINVAL; + } + +@@ -183,7 +177,7 @@ static int aic32x4_set_mfp4_gpio(struct snd_kcontrol *kcontrol, + }; + + static int aic32x4_get_mfp5_gpio(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) ++ struct snd_ctl_elem_value *ucontrol) + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + u8 val; +@@ -195,7 +189,7 @@ static int aic32x4_get_mfp5_gpio(struct snd_kcontrol *kcontrol, + }; + + static int aic32x4_set_mfp5_gpio(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) ++ struct snd_ctl_elem_value *ucontrol) + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + u8 val; +@@ -204,8 +198,7 @@ static int aic32x4_set_mfp5_gpio(struct snd_kcontrol *kcontrol, + val = snd_soc_component_read(component, AIC32X4_GPIOCTL); + gpio_check = (val & AIC32X4_MFP5_GPIO_OUTPUT); + if (gpio_check != AIC32X4_MFP5_GPIO_OUTPUT) { +- printk(KERN_ERR "%s: MFP5 is not configure as a GPIO output\n", +- __func__); ++ dev_err(component->dev, "MFP5 is not configure as a GPIO output\n"); + return -EINVAL; + } + +@@ -222,25 +215,31 @@ static int aic32x4_set_mfp5_gpio(struct snd_kcontrol *kcontrol, + return 0; + }; + +-static const struct snd_kcontrol_new aic32x4_mfp1[] = { +- SOC_SINGLE_BOOL_EXT("MFP1 GPIO", 0, aic32x4_get_mfp1_gpio, NULL), +-}; +- +-static const struct snd_kcontrol_new aic32x4_mfp2[] = { +- SOC_SINGLE_BOOL_EXT("MFP2 GPIO", 0, NULL, aic32x4_set_mfp2_gpio), +-}; +- +-static const struct snd_kcontrol_new aic32x4_mfp3[] = { +- SOC_SINGLE_BOOL_EXT("MFP3 GPIO", 0, aic32x4_get_mfp3_gpio, NULL), +-}; +- +-static const struct snd_kcontrol_new aic32x4_mfp4[] = { +- SOC_SINGLE_BOOL_EXT("MFP4 GPIO", 0, NULL, aic32x4_set_mfp4_gpio), +-}; +- +-static const struct snd_kcontrol_new aic32x4_mfp5[] = { +- SOC_SINGLE_BOOL_EXT("MFP5 GPIO", 0, aic32x4_get_mfp5_gpio, +- aic32x4_set_mfp5_gpio), ++static const struct { ++ unsigned int reg; ++ struct snd_kcontrol_new ctrl; ++} aic32x4_mfp_cfg[] = { ++ { ++ .reg = AIC32X4_DINCTL, ++ .ctrl = SOC_SINGLE_BOOL_EXT("MFP1 GPIO", 0, aic32x4_get_mfp1_gpio, NULL), ++ }, ++ { ++ .reg = AIC32X4_DOUTCTL, ++ .ctrl = SOC_SINGLE_BOOL_EXT("MFP2 GPIO", 0, NULL, aic32x4_set_mfp2_gpio), ++ }, ++ { ++ .reg = AIC32X4_SCLKCTL, ++ .ctrl = SOC_SINGLE_BOOL_EXT("MFP3 GPIO", 0, aic32x4_get_mfp3_gpio, NULL), ++ }, ++ { ++ .reg = AIC32X4_MISOCTL, ++ .ctrl = SOC_SINGLE_BOOL_EXT("MFP4 GPIO", 0, NULL, aic32x4_set_mfp4_gpio), ++ }, ++ { ++ .reg = AIC32X4_GPIOCTL, ++ .ctrl = SOC_SINGLE_BOOL_EXT("MFP5 GPIO", 0, aic32x4_get_mfp5_gpio, ++ aic32x4_set_mfp5_gpio), ++ }, + }; + + /* 0dB min, 0.5dB steps */ +@@ -270,53 +269,42 @@ static SOC_ENUM_SINGLE_DECL(r_ptm_enum, AIC32X4_RPLAYBACK, 2, ptm_text); + + static const struct snd_kcontrol_new aic32x4_snd_controls[] = { + SOC_DOUBLE_R_S_TLV("PCM Playback Volume", AIC32X4_LDACVOL, +- AIC32X4_RDACVOL, 0, -0x7f, 0x30, 7, 0, tlv_pcm), ++ AIC32X4_RDACVOL, 0, -0x7f, 0x30, 7, 0, tlv_pcm), + SOC_ENUM("DAC Left Playback PowerTune Switch", l_ptm_enum), + SOC_ENUM("DAC Right Playback PowerTune Switch", r_ptm_enum), + SOC_DOUBLE_R_S_TLV("HP Driver Gain Volume", AIC32X4_HPLGAIN, +- AIC32X4_HPRGAIN, 0, -0x6, 0x1d, 5, 0, +- tlv_driver_gain), ++ AIC32X4_HPRGAIN, 0, -0x6, 0x1d, 5, 0, tlv_driver_gain), + SOC_DOUBLE_R_S_TLV("LO Driver Gain Volume", AIC32X4_LOLGAIN, +- AIC32X4_LORGAIN, 0, -0x6, 0x1d, 5, 0, +- tlv_driver_gain), ++ AIC32X4_LORGAIN, 0, -0x6, 0x1d, 5, 0, tlv_driver_gain), + SOC_DOUBLE_R("HP DAC Playback Switch", AIC32X4_HPLGAIN, +- AIC32X4_HPRGAIN, 6, 0x01, 1), ++ AIC32X4_HPRGAIN, 6, 0x01, 1), + SOC_DOUBLE_R("LO DAC Playback Switch", AIC32X4_LOLGAIN, +- AIC32X4_LORGAIN, 6, 0x01, 1), ++ AIC32X4_LORGAIN, 6, 0x01, 1), + SOC_ENUM("LO Playback Common Mode Switch", lo_cm_enum), + SOC_DOUBLE_R("Mic PGA Switch", AIC32X4_LMICPGAVOL, +- AIC32X4_RMICPGAVOL, 7, 0x01, 1), ++ AIC32X4_RMICPGAVOL, 7, 0x01, 1), + + SOC_SINGLE("ADCFGA Left Mute Switch", AIC32X4_ADCFGA, 7, 1, 0), + SOC_SINGLE("ADCFGA Right Mute Switch", AIC32X4_ADCFGA, 3, 1, 0), + + SOC_DOUBLE_R_S_TLV("ADC Level Volume", AIC32X4_LADCVOL, +- AIC32X4_RADCVOL, 0, -0x18, 0x28, 6, 0, tlv_adc_vol), ++ AIC32X4_RADCVOL, 0, -0x18, 0x28, 6, 0, tlv_adc_vol), + SOC_DOUBLE_R_TLV("PGA Level Volume", AIC32X4_LMICPGAVOL, +- AIC32X4_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5), ++ AIC32X4_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5), + + SOC_SINGLE("Auto-mute Switch", AIC32X4_DACMUTE, 4, 7, 0), + + SOC_SINGLE("AGC Left Switch", AIC32X4_LAGC1, 7, 1, 0), + SOC_SINGLE("AGC Right Switch", AIC32X4_RAGC1, 7, 1, 0), +- SOC_DOUBLE_R("AGC Target Level", AIC32X4_LAGC1, AIC32X4_RAGC1, +- 4, 0x07, 0), +- SOC_DOUBLE_R("AGC Gain Hysteresis", AIC32X4_LAGC1, AIC32X4_RAGC1, +- 0, 0x03, 0), +- SOC_DOUBLE_R("AGC Hysteresis", AIC32X4_LAGC2, AIC32X4_RAGC2, +- 6, 0x03, 0), +- SOC_DOUBLE_R("AGC Noise Threshold", AIC32X4_LAGC2, AIC32X4_RAGC2, +- 1, 0x1F, 0), +- SOC_DOUBLE_R("AGC Max PGA", AIC32X4_LAGC3, AIC32X4_RAGC3, +- 0, 0x7F, 0), +- SOC_DOUBLE_R("AGC Attack Time", AIC32X4_LAGC4, AIC32X4_RAGC4, +- 3, 0x1F, 0), +- SOC_DOUBLE_R("AGC Decay Time", AIC32X4_LAGC5, AIC32X4_RAGC5, +- 3, 0x1F, 0), +- SOC_DOUBLE_R("AGC Noise Debounce", AIC32X4_LAGC6, AIC32X4_RAGC6, +- 0, 0x1F, 0), +- SOC_DOUBLE_R("AGC Signal Debounce", AIC32X4_LAGC7, AIC32X4_RAGC7, +- 0, 0x0F, 0), ++ SOC_DOUBLE_R("AGC Target Level", AIC32X4_LAGC1, AIC32X4_RAGC1, 4, 0x07, 0), ++ SOC_DOUBLE_R("AGC Gain Hysteresis", AIC32X4_LAGC1, AIC32X4_RAGC1, 0, 0x03, 0), ++ SOC_DOUBLE_R("AGC Hysteresis", AIC32X4_LAGC2, AIC32X4_RAGC2, 6, 0x03, 0), ++ SOC_DOUBLE_R("AGC Noise Threshold", AIC32X4_LAGC2, AIC32X4_RAGC2, 1, 0x1F, 0), ++ SOC_DOUBLE_R("AGC Max PGA", AIC32X4_LAGC3, AIC32X4_RAGC3, 0, 0x7F, 0), ++ SOC_DOUBLE_R("AGC Attack Time", AIC32X4_LAGC4, AIC32X4_RAGC4, 3, 0x1F, 0), ++ SOC_DOUBLE_R("AGC Decay Time", AIC32X4_LAGC5, AIC32X4_RAGC5, 3, 0x1F, 0), ++ SOC_DOUBLE_R("AGC Noise Debounce", AIC32X4_LAGC6, AIC32X4_RAGC6, 0, 0x1F, 0), ++ SOC_DOUBLE_R("AGC Signal Debounce", AIC32X4_LAGC7, AIC32X4_RAGC7, 0, 0x0F, 0), + }; + + static const struct snd_kcontrol_new hpl_output_mixer_controls[] = { +@@ -354,21 +342,27 @@ static SOC_ENUM_SINGLE_DECL(in3r_lpga_n_enum, AIC32X4_LMICPGANIN, 2, resistor_te + static const struct snd_kcontrol_new in1l_to_lmixer_controls[] = { + SOC_DAPM_ENUM("IN1_L L+ Switch", in1l_lpga_p_enum), + }; ++ + static const struct snd_kcontrol_new in2l_to_lmixer_controls[] = { + SOC_DAPM_ENUM("IN2_L L+ Switch", in2l_lpga_p_enum), + }; ++ + static const struct snd_kcontrol_new in3l_to_lmixer_controls[] = { + SOC_DAPM_ENUM("IN3_L L+ Switch", in3l_lpga_p_enum), + }; ++ + static const struct snd_kcontrol_new in1r_to_lmixer_controls[] = { + SOC_DAPM_ENUM("IN1_R L+ Switch", in1r_lpga_p_enum), + }; ++ + static const struct snd_kcontrol_new cml_to_lmixer_controls[] = { + SOC_DAPM_ENUM("CM_L L- Switch", cml_lpga_n_enum), + }; ++ + static const struct snd_kcontrol_new in2r_to_lmixer_controls[] = { + SOC_DAPM_ENUM("IN2_R L- Switch", in2r_lpga_n_enum), + }; ++ + static const struct snd_kcontrol_new in3r_to_lmixer_controls[] = { + SOC_DAPM_ENUM("IN3_R L- Switch", in3r_lpga_n_enum), + }; +@@ -385,21 +379,27 @@ static SOC_ENUM_SINGLE_DECL(in3l_rpga_n_enum, AIC32X4_RMICPGANIN, 2, resistor_te + static const struct snd_kcontrol_new in1r_to_rmixer_controls[] = { + SOC_DAPM_ENUM("IN1_R R+ Switch", in1r_rpga_p_enum), + }; ++ + static const struct snd_kcontrol_new in2r_to_rmixer_controls[] = { + SOC_DAPM_ENUM("IN2_R R+ Switch", in2r_rpga_p_enum), + }; ++ + static const struct snd_kcontrol_new in3r_to_rmixer_controls[] = { + SOC_DAPM_ENUM("IN3_R R+ Switch", in3r_rpga_p_enum), + }; ++ + static const struct snd_kcontrol_new in2l_to_rmixer_controls[] = { + SOC_DAPM_ENUM("IN2_L R+ Switch", in2l_rpga_p_enum), + }; ++ + static const struct snd_kcontrol_new cmr_to_rmixer_controls[] = { + SOC_DAPM_ENUM("CM_R R- Switch", cmr_rpga_n_enum), + }; ++ + static const struct snd_kcontrol_new in1l_to_rmixer_controls[] = { + SOC_DAPM_ENUM("IN1_L R- Switch", in1l_rpga_n_enum), + }; ++ + static const struct snd_kcontrol_new in3l_to_rmixer_controls[] = { + SOC_DAPM_ENUM("IN3_L R- Switch", in3l_rpga_n_enum), + }; +@@ -428,38 +428,38 @@ static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = { + + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", AIC32X4_ADCSETUP, 6, 0), + SND_SOC_DAPM_MUX("IN1_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, +- in1r_to_rmixer_controls), ++ in1r_to_rmixer_controls), + SND_SOC_DAPM_MUX("IN2_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, +- in2r_to_rmixer_controls), ++ in2r_to_rmixer_controls), + SND_SOC_DAPM_MUX("IN3_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, +- in3r_to_rmixer_controls), ++ in3r_to_rmixer_controls), + SND_SOC_DAPM_MUX("IN2_L to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, +- in2l_to_rmixer_controls), ++ in2l_to_rmixer_controls), + SND_SOC_DAPM_MUX("CM_R to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, +- cmr_to_rmixer_controls), ++ cmr_to_rmixer_controls), + SND_SOC_DAPM_MUX("IN1_L to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, +- in1l_to_rmixer_controls), ++ in1l_to_rmixer_controls), + SND_SOC_DAPM_MUX("IN3_L to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, +- in3l_to_rmixer_controls), ++ in3l_to_rmixer_controls), + + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", AIC32X4_ADCSETUP, 7, 0), + SND_SOC_DAPM_MUX("IN1_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, +- in1l_to_lmixer_controls), ++ in1l_to_lmixer_controls), + SND_SOC_DAPM_MUX("IN2_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, +- in2l_to_lmixer_controls), ++ in2l_to_lmixer_controls), + SND_SOC_DAPM_MUX("IN3_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, +- in3l_to_lmixer_controls), ++ in3l_to_lmixer_controls), + SND_SOC_DAPM_MUX("IN1_R to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, +- in1r_to_lmixer_controls), ++ in1r_to_lmixer_controls), + SND_SOC_DAPM_MUX("CM_L to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, +- cml_to_lmixer_controls), ++ cml_to_lmixer_controls), + SND_SOC_DAPM_MUX("IN2_R to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, +- in2r_to_lmixer_controls), ++ in2r_to_lmixer_controls), + SND_SOC_DAPM_MUX("IN3_R to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, +- in3r_to_lmixer_controls), ++ in3r_to_lmixer_controls), + + SND_SOC_DAPM_SUPPLY("Mic Bias", AIC32X4_MICBIAS, 6, 0, mic_bias_event, +- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), ++ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_POST("ADC Reset", aic32x4_reset_adc), + +@@ -575,7 +575,7 @@ static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = { + {"IN3_R to Left Mixer Negative Resistor", "40 kOhm", "IN3_R"}, + }; + +-static const struct regmap_range_cfg aic32x4_regmap_pages[] = { ++const struct regmap_range_cfg aic32x4_regmap_pages[] = { + { + .selector_reg = 0, + .selector_mask = 0xff, +@@ -585,13 +585,7 @@ static const struct regmap_range_cfg aic32x4_regmap_pages[] = { + .range_max = AIC32X4_REFPOWERUP, + }, + }; +- +-const struct regmap_config aic32x4_regmap_config = { +- .max_register = AIC32X4_REFPOWERUP, +- .ranges = aic32x4_regmap_pages, +- .num_ranges = ARRAY_SIZE(aic32x4_regmap_pages), +-}; +-EXPORT_SYMBOL(aic32x4_regmap_config); ++EXPORT_SYMBOL_GPL(aic32x4_regmap_pages); + + static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +@@ -624,7 +618,7 @@ static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: +- printk(KERN_ERR "aic32x4: invalid clock provider\n"); ++ dev_err(component->dev, "invalid clock provider\n"); + return -EINVAL; + } + +@@ -651,19 +645,20 @@ static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) + AIC32X4_IFACE1_DATATYPE_SHIFT); + break; + default: +- printk(KERN_ERR "aic32x4: invalid DAI interface format\n"); ++ dev_err(component->dev, "invalid DAI interface format\n"); + return -EINVAL; + } + + aic32x4->fmt = fmt; + + snd_soc_component_update_bits(component, AIC32X4_IFACE1, +- AIC32X4_IFACE1_DATATYPE_MASK | +- AIC32X4_IFACE1_MASTER_MASK, iface_reg_1); ++ AIC32X4_IFACE1_DATATYPE_MASK | ++ AIC32X4_IFACE1_MASTER_MASK, ++ iface_reg_1); + snd_soc_component_update_bits(component, AIC32X4_IFACE2, +- AIC32X4_DATA_OFFSET_MASK, iface_reg_2); ++ AIC32X4_DATA_OFFSET_MASK, iface_reg_2); + snd_soc_component_update_bits(component, AIC32X4_IFACE3, +- AIC32X4_BCLKINV_MASK, iface_reg_3); ++ AIC32X4_BCLKINV_MASK, iface_reg_3); + + return 0; + } +@@ -676,14 +671,13 @@ static int aic32x4_set_aosr(struct snd_soc_component *component, u8 aosr) + static int aic32x4_set_dosr(struct snd_soc_component *component, u16 dosr) + { + snd_soc_component_write(component, AIC32X4_DOSRMSB, dosr >> 8); +- snd_soc_component_write(component, AIC32X4_DOSRLSB, +- (dosr & 0xff)); ++ snd_soc_component_write(component, AIC32X4_DOSRLSB, dosr & 0xff); + + return 0; + } + + static int aic32x4_set_processing_blocks(struct snd_soc_component *component, +- u8 r_block, u8 p_block) ++ u8 r_block, u8 p_block) + { + struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); + +@@ -703,6 +697,45 @@ static int aic32x4_set_processing_blocks(struct snd_soc_component *component, + return 0; + } + ++static int aic32x4_configure_rate(struct snd_soc_component *component, ++ unsigned int rate, u8 *aosr, u8 *adc_rc, ++ u8 *dac_rc, u8 *dosr_inc) ++{ ++ struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); ++ u8 prb_rx, prb_tx; ++ ++ if (rate <= 48000) { ++ *aosr = 128; ++ *adc_rc = 6; ++ *dac_rc = 8; ++ *dosr_inc = 8; ++ prb_rx = 1; ++ prb_tx = 1; ++ } else if (rate <= 96000) { ++ *aosr = 64; ++ *adc_rc = 6; ++ *dac_rc = 8; ++ *dosr_inc = 4; ++ prb_rx = 1; ++ prb_tx = (aic32x4->type == AIC32X4_TYPE_TAS2505) ? 1 : 9; ++ } else if (rate == 192000) { ++ *aosr = 32; ++ *adc_rc = 3; ++ *dac_rc = 4; ++ *dosr_inc = 2; ++ prb_rx = 13; ++ prb_tx = (aic32x4->type == AIC32X4_TYPE_TAS2505) ? 1 : 19; ++ } else { ++ dev_err(component->dev, "Sampling rate %u not supported\n", rate); ++ return -EINVAL; ++ } ++ ++ if (aic32x4->type == AIC32X4_TYPE_TAS2505) ++ prb_rx = 0; ++ ++ return aic32x4_set_processing_blocks(component, prb_rx, prb_tx); ++} ++ + static int aic32x4_setup_clocks(struct snd_soc_component *component, + unsigned int sample_rate, unsigned int channels, + unsigned int bit_depth) +@@ -717,7 +750,7 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, + unsigned long adc_clock_rate, dac_clock_rate; + int ret; + +- static struct clk_bulk_data clocks[] = { ++ struct clk_bulk_data clocks[] = { + { .id = "pll" }, + { .id = "nadc" }, + { .id = "madc" }, +@@ -729,37 +762,11 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, + if (ret) + return ret; + +- if (sample_rate <= 48000) { +- aosr = 128; +- adc_resource_class = 6; +- dac_resource_class = 8; +- dosr_increment = 8; +- if (aic32x4->type == AIC32X4_TYPE_TAS2505) +- aic32x4_set_processing_blocks(component, 0, 1); +- else +- aic32x4_set_processing_blocks(component, 1, 1); +- } else if (sample_rate <= 96000) { +- aosr = 64; +- adc_resource_class = 6; +- dac_resource_class = 8; +- dosr_increment = 4; +- if (aic32x4->type == AIC32X4_TYPE_TAS2505) +- aic32x4_set_processing_blocks(component, 0, 1); +- else +- aic32x4_set_processing_blocks(component, 1, 9); +- } else if (sample_rate == 192000) { +- aosr = 32; +- adc_resource_class = 3; +- dac_resource_class = 4; +- dosr_increment = 2; +- if (aic32x4->type == AIC32X4_TYPE_TAS2505) +- aic32x4_set_processing_blocks(component, 0, 1); +- else +- aic32x4_set_processing_blocks(component, 13, 19); +- } else { +- dev_err(component->dev, "Sampling rate not supported\n"); +- return -EINVAL; +- } ++ ret = aic32x4_configure_rate(component, sample_rate, &aosr, ++ &adc_resource_class, &dac_resource_class, ++ &dosr_increment); ++ if (ret) ++ return ret; + + /* PCM over I2S is always 2-channel */ + if ((aic32x4->fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S) +@@ -781,50 +788,39 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, + (min_mdac * dosr * sample_rate); + for (mdac = min_mdac; mdac <= 128; ++mdac) { + for (ndac = max_ndac; ndac > 0; --ndac) { +- dac_clock_rate = ndac * mdac * dosr * +- sample_rate; +- if (dac_clock_rate == adc_clock_rate) { +- if (clk_round_rate(clocks[0].clk, dac_clock_rate) == 0) +- continue; ++ dac_clock_rate = ndac * mdac * dosr * sample_rate; ++ if (dac_clock_rate != adc_clock_rate) ++ continue; + +- clk_set_rate(clocks[0].clk, +- dac_clock_rate); ++ if (clk_round_rate(clocks[0].clk, dac_clock_rate) == 0) ++ continue; + +- clk_set_rate(clocks[1].clk, +- sample_rate * aosr * +- madc); +- clk_set_rate(clocks[2].clk, +- sample_rate * aosr); +- aic32x4_set_aosr(component, +- aosr); ++ clk_set_rate(clocks[0].clk, dac_clock_rate); + +- clk_set_rate(clocks[3].clk, +- sample_rate * dosr * +- mdac); +- clk_set_rate(clocks[4].clk, +- sample_rate * dosr); +- aic32x4_set_dosr(component, +- dosr); ++ clk_set_rate(clocks[1].clk, sample_rate * aosr * madc); ++ clk_set_rate(clocks[2].clk, sample_rate * aosr); ++ aic32x4_set_aosr(component, aosr); + +- clk_set_rate(clocks[5].clk, +- sample_rate * channels * +- bit_depth); ++ clk_set_rate(clocks[3].clk, sample_rate * dosr * mdac); ++ clk_set_rate(clocks[4].clk, sample_rate * dosr); ++ aic32x4_set_dosr(component, dosr); + +- return 0; +- } ++ clk_set_rate(clocks[5].clk, ++ sample_rate * channels * bit_depth); ++ ++ return 0; + } + } + } + } + +- dev_err(component->dev, +- "Could not set clocks to support sample rate.\n"); ++ dev_err(component->dev, "Could not set clocks to support sample rate.\n"); + return -EINVAL; + } + + static int aic32x4_hw_params(struct snd_pcm_substream *substream, +- struct snd_pcm_hw_params *params, +- struct snd_soc_dai *dai) ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) + { + struct snd_soc_component *component = dai->component; + struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); +@@ -837,24 +833,20 @@ static int aic32x4_hw_params(struct snd_pcm_substream *substream, + + switch (params_physical_width(params)) { + case 16: +- iface1_reg |= (AIC32X4_WORD_LEN_16BITS << +- AIC32X4_IFACE1_DATALEN_SHIFT); ++ iface1_reg |= (AIC32X4_WORD_LEN_16BITS << AIC32X4_IFACE1_DATALEN_SHIFT); + break; + case 20: +- iface1_reg |= (AIC32X4_WORD_LEN_20BITS << +- AIC32X4_IFACE1_DATALEN_SHIFT); ++ iface1_reg |= (AIC32X4_WORD_LEN_20BITS << AIC32X4_IFACE1_DATALEN_SHIFT); + break; + case 24: +- iface1_reg |= (AIC32X4_WORD_LEN_24BITS << +- AIC32X4_IFACE1_DATALEN_SHIFT); ++ iface1_reg |= (AIC32X4_WORD_LEN_24BITS << AIC32X4_IFACE1_DATALEN_SHIFT); + break; + case 32: +- iface1_reg |= (AIC32X4_WORD_LEN_32BITS << +- AIC32X4_IFACE1_DATALEN_SHIFT); ++ iface1_reg |= (AIC32X4_WORD_LEN_32BITS << AIC32X4_IFACE1_DATALEN_SHIFT); + break; + } + snd_soc_component_update_bits(component, AIC32X4_IFACE1, +- AIC32X4_IFACE1_DATALEN_MASK, iface1_reg); ++ AIC32X4_IFACE1_DATALEN_MASK, iface1_reg); + + if (params_channels(params) == 1) { + dacsetup_reg = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2LCHN; +@@ -865,7 +857,7 @@ static int aic32x4_hw_params(struct snd_pcm_substream *substream, + dacsetup_reg = AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN; + } + snd_soc_component_update_bits(component, AIC32X4_DACSETUP, +- AIC32X4_DAC_CHAN_MASK, dacsetup_reg); ++ AIC32X4_DAC_CHAN_MASK, dacsetup_reg); + + return 0; + } +@@ -875,7 +867,7 @@ static int aic32x4_mute(struct snd_soc_dai *dai, int mute, int direction) + struct snd_soc_component *component = dai->component; + + snd_soc_component_update_bits(component, AIC32X4_DACMUTE, +- AIC32X4_MUTEON, mute ? AIC32X4_MUTEON : 0); ++ AIC32X4_MUTEON, mute ? AIC32X4_MUTEON : 0); + + return 0; + } +@@ -886,7 +878,7 @@ static int aic32x4_set_bias_level(struct snd_soc_component *component, + struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); + int ret; + +- static struct clk_bulk_data clocks[] = { ++ struct clk_bulk_data clocks[] = { + { .id = "madc" }, + { .id = "mdac" }, + { .id = "bdiv" }, +@@ -955,44 +947,14 @@ static void aic32x4_setup_gpios(struct snd_soc_component *component) + struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); + + /* setup GPIO functions */ +- /* MFP1 */ +- if (aic32x4->setup->gpio_func[0] != AIC32X4_MFPX_DEFAULT_VALUE) { +- snd_soc_component_write(component, AIC32X4_DINCTL, +- aic32x4->setup->gpio_func[0]); +- snd_soc_add_component_controls(component, aic32x4_mfp1, +- ARRAY_SIZE(aic32x4_mfp1)); +- } ++ BUILD_BUG_ON(ARRAY_SIZE(aic32x4->gpio_func) != ARRAY_SIZE(aic32x4_mfp_cfg)); ++ for (int i = 0; i < ARRAY_SIZE(aic32x4->gpio_func); i++) { ++ if (aic32x4->gpio_func[i] == AIC32X4_MFPX_DEFAULT_VALUE) ++ continue; + +- /* MFP2 */ +- if (aic32x4->setup->gpio_func[1] != AIC32X4_MFPX_DEFAULT_VALUE) { +- snd_soc_component_write(component, AIC32X4_DOUTCTL, +- aic32x4->setup->gpio_func[1]); +- snd_soc_add_component_controls(component, aic32x4_mfp2, +- ARRAY_SIZE(aic32x4_mfp2)); +- } +- +- /* MFP3 */ +- if (aic32x4->setup->gpio_func[2] != AIC32X4_MFPX_DEFAULT_VALUE) { +- snd_soc_component_write(component, AIC32X4_SCLKCTL, +- aic32x4->setup->gpio_func[2]); +- snd_soc_add_component_controls(component, aic32x4_mfp3, +- ARRAY_SIZE(aic32x4_mfp3)); +- } +- +- /* MFP4 */ +- if (aic32x4->setup->gpio_func[3] != AIC32X4_MFPX_DEFAULT_VALUE) { +- snd_soc_component_write(component, AIC32X4_MISOCTL, +- aic32x4->setup->gpio_func[3]); +- snd_soc_add_component_controls(component, aic32x4_mfp4, +- ARRAY_SIZE(aic32x4_mfp4)); +- } +- +- /* MFP5 */ +- if (aic32x4->setup->gpio_func[4] != AIC32X4_MFPX_DEFAULT_VALUE) { +- snd_soc_component_write(component, AIC32X4_GPIOCTL, +- aic32x4->setup->gpio_func[4]); +- snd_soc_add_component_controls(component, aic32x4_mfp5, +- ARRAY_SIZE(aic32x4_mfp5)); ++ snd_soc_component_write(component, aic32x4_mfp_cfg[i].reg, ++ aic32x4->gpio_func[i]); ++ snd_soc_add_component_controls(component, &aic32x4_mfp_cfg[i].ctrl, 1); + } + } + +@@ -1002,7 +964,7 @@ static int aic32x4_component_probe(struct snd_soc_component *component) + u32 tmp_reg; + int ret; + +- static struct clk_bulk_data clocks[] = { ++ struct clk_bulk_data clocks[] = { + { .id = "codec_clkin" }, + { .id = "pll" }, + { .id = "bdiv" }, +@@ -1013,8 +975,7 @@ static int aic32x4_component_probe(struct snd_soc_component *component) + if (ret) + return ret; + +- if (aic32x4->setup) +- aic32x4_setup_gpios(component); ++ aic32x4_setup_gpios(component); + + clk_set_parent(clocks[0].clk, clocks[1].clk); + clk_set_parent(clocks[2].clk, clocks[3].clk); +@@ -1022,7 +983,7 @@ static int aic32x4_component_probe(struct snd_soc_component *component) + /* Power platform configuration */ + if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) { + snd_soc_component_write(component, AIC32X4_MICBIAS, +- AIC32X4_MICBIAS_LDOIN | AIC32X4_MICBIAS_2075V); ++ AIC32X4_MICBIAS_LDOIN | AIC32X4_MICBIAS_2075V); + } + if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE) + snd_soc_component_write(component, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE); +@@ -1041,16 +1002,16 @@ static int aic32x4_component_probe(struct snd_soc_component *component) + /* Mic PGA routing */ + if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K) + snd_soc_component_write(component, AIC32X4_LMICPGANIN, +- AIC32X4_LMICPGANIN_IN2R_10K); ++ AIC32X4_LMICPGANIN_IN2R_10K); + else + snd_soc_component_write(component, AIC32X4_LMICPGANIN, +- AIC32X4_LMICPGANIN_CM1L_10K); ++ AIC32X4_LMICPGANIN_CM1L_10K); + if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K) + snd_soc_component_write(component, AIC32X4_RMICPGANIN, +- AIC32X4_RMICPGANIN_IN1L_10K); ++ AIC32X4_RMICPGANIN_IN1L_10K); + else + snd_soc_component_write(component, AIC32X4_RMICPGANIN, +- AIC32X4_RMICPGANIN_CM1R_10K); ++ AIC32X4_RMICPGANIN_CM1R_10K); + + /* + * Workaround: for an unknown reason, the ADC needs to be powered up +@@ -1058,8 +1019,8 @@ static int aic32x4_component_probe(struct snd_soc_component *component) + * a HW BUG or some kind of behavior not documented in the datasheet. + */ + tmp_reg = snd_soc_component_read(component, AIC32X4_ADCSETUP); +- snd_soc_component_write(component, AIC32X4_ADCSETUP, tmp_reg | +- AIC32X4_LADC_EN | AIC32X4_RADC_EN); ++ snd_soc_component_write(component, AIC32X4_ADCSETUP, ++ tmp_reg | AIC32X4_LADC_EN | AIC32X4_RADC_EN); + snd_soc_component_write(component, AIC32X4_ADCSETUP, tmp_reg); + + /* +@@ -1102,13 +1063,13 @@ static const struct snd_kcontrol_new aic32x4_tas2505_snd_controls[] = { + SOC_ENUM("DAC Playback PowerTune Switch", l_ptm_enum), + + SOC_SINGLE_TLV("HP Driver Gain Volume", +- AIC32X4_HPLGAIN, 0, 0x74, 1, tlv_tas_driver_gain), ++ AIC32X4_HPLGAIN, 0, 0x74, 1, tlv_tas_driver_gain), + SOC_SINGLE("HP DAC Playback Switch", AIC32X4_HPLGAIN, 6, 1, 1), + + SOC_SINGLE_TLV("Speaker Driver Playback Volume", +- TAS2505_SPKVOL1, 0, 0x74, 1, tlv_tas_driver_gain), ++ TAS2505_SPKVOL1, 0, 0x74, 1, tlv_tas_driver_gain), + SOC_SINGLE_TLV("Speaker Amplifier Playback Volume", +- TAS2505_SPKVOL2, 4, 5, 0, tlv_amp_vol), ++ TAS2505_SPKVOL2, 4, 5, 0, tlv_amp_vol), + + SOC_SINGLE("Auto-mute Switch", AIC32X4_DACMUTE, 4, 7, 0), + }; +@@ -1144,11 +1105,12 @@ static const struct snd_soc_dapm_route aic32x4_tas2505_dapm_routes[] = { + static struct snd_soc_dai_driver aic32x4_tas2505_dai = { + .name = "tas2505-hifi", + .playback = { +- .stream_name = "Playback", +- .channels_min = 1, +- .channels_max = 2, +- .rates = SNDRV_PCM_RATE_8000_96000, +- .formats = AIC32X4_FORMATS,}, ++ .stream_name = "Playback", ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_96000, ++ .formats = AIC32X4_FORMATS, ++ }, + .ops = &aic32x4_ops, + .symmetric_rate = 1, + }; +@@ -1159,7 +1121,7 @@ static int aic32x4_tas2505_component_probe(struct snd_soc_component *component) + u32 tmp_reg; + int ret; + +- static struct clk_bulk_data clocks[] = { ++ struct clk_bulk_data clocks[] = { + { .id = "codec_clkin" }, + { .id = "pll" }, + { .id = "bdiv" }, +@@ -1170,8 +1132,7 @@ static int aic32x4_tas2505_component_probe(struct snd_soc_component *component) + if (ret) + return ret; + +- if (aic32x4->setup) +- aic32x4_setup_gpios(component); ++ aic32x4_setup_gpios(component); + + clk_set_parent(clocks[0].clk, clocks[1].clk); + clk_set_parent(clocks[2].clk, clocks[3].clk); +@@ -1192,7 +1153,7 @@ static int aic32x4_tas2505_component_probe(struct snd_soc_component *component) + snd_soc_component_write(component, AIC32X4_CMMODE, tmp_reg); + + /* +- * Enable the fast charging feature and ensure the needed 40ms ellapsed ++ * Enable the fast charging feature and ensure the needed 40ms elapsed + * before using the analog circuits. + */ + snd_soc_component_write(component, TAS2505_REFPOWERUP, +@@ -1218,17 +1179,10 @@ static const struct snd_soc_component_driver soc_component_dev_aic32x4_tas2505 = + .endianness = 1, + }; + +-static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, +- struct device_node *np) ++static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, struct device_node *np) + { +- struct aic32x4_setup_data *aic32x4_setup; + int ret; + +- aic32x4_setup = devm_kzalloc(aic32x4->dev, sizeof(*aic32x4_setup), +- GFP_KERNEL); +- if (!aic32x4_setup) +- return -ENOMEM; +- + ret = of_property_match_string(np, "clock-names", "mclk"); + if (ret < 0) + return -EINVAL; +@@ -1245,9 +1199,11 @@ static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, + gpiod_set_consumer_name(aic32x4->rstn_gpio, "tlv320aic32x4_rstn"); + } + +- if (of_property_read_u32_array(np, "aic32x4-gpio-func", +- aic32x4_setup->gpio_func, 5) >= 0) +- aic32x4->setup = aic32x4_setup; ++ for (int i = 0; i < ARRAY_SIZE(aic32x4->gpio_func); i++) ++ aic32x4->gpio_func[i] = AIC32X4_MFPX_DEFAULT_VALUE; ++ of_property_read_u32_array(np, "aic32x4-gpio-func", ++ aic32x4->gpio_func, ARRAY_SIZE(aic32x4->gpio_func)); ++ + return 0; + } + +@@ -1266,7 +1222,7 @@ static void aic32x4_disable_regulators(struct aic32x4_priv *aic32x4) + } + + static int aic32x4_setup_regulators(struct device *dev, +- struct aic32x4_priv *aic32x4) ++ struct aic32x4_priv *aic32x4) + { + int ret = 0; + +@@ -1356,12 +1312,8 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap, + struct device_node *np = dev->of_node; + int ret; + +- if (IS_ERR(regmap)) +- return PTR_ERR(regmap); +- +- aic32x4 = devm_kzalloc(dev, sizeof(struct aic32x4_priv), +- GFP_KERNEL); +- if (aic32x4 == NULL) ++ aic32x4 = devm_kzalloc(dev, sizeof(struct aic32x4_priv), GFP_KERNEL); ++ if (!aic32x4) + return -ENOMEM; + + aic32x4->dev = dev; +@@ -1405,11 +1357,13 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap, + switch (aic32x4->type) { + case AIC32X4_TYPE_TAS2505: + ret = devm_snd_soc_register_component(dev, +- &soc_component_dev_aic32x4_tas2505, &aic32x4_tas2505_dai, 1); ++ &soc_component_dev_aic32x4_tas2505, ++ &aic32x4_tas2505_dai, 1); + break; + default: + ret = devm_snd_soc_register_component(dev, +- &soc_component_dev_aic32x4, &aic32x4_dai, 1); ++ &soc_component_dev_aic32x4, ++ &aic32x4_dai, 1); + } + + if (ret) { +diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h +--- a/sound/soc/codecs/tlv320aic32x4.h ++++ b/sound/soc/codecs/tlv320aic32x4.h +@@ -3,7 +3,6 @@ + * tlv320aic32x4.h + */ + +- + #ifndef _TLV320AIC32X4_H + #define _TLV320AIC32X4_H + +@@ -16,7 +15,7 @@ enum aic32x4_type { + AIC32X4_TYPE_TAS2505, + }; + +-extern const struct regmap_config aic32x4_regmap_config; ++extern const struct regmap_range_cfg aic32x4_regmap_pages[]; + int aic32x4_probe(struct device *dev, struct regmap *regmap, + enum aic32x4_type type); + void aic32x4_remove(struct device *dev); +@@ -24,7 +23,7 @@ int aic32x4_register_clocks(struct device *dev, const char *mclk_name); + + /* tlv320aic32x4 register space (in decimal to match datasheet) */ + +-#define AIC32X4_REG(page, reg) ((page * 128) + reg) ++#define AIC32X4_REG(page, reg) (((page) * 128) + (reg)) + + #define AIC32X4_PSEL AIC32X4_REG(0, 0) + +@@ -234,4 +233,31 @@ int aic32x4_register_clocks(struct device *dev, const char *mclk_name); + #define AIC32X4_MAX_CODEC_CLKIN_FREQ 110000000 + #define AIC32X4_MAX_PLL_CLKIN 20000000 + ++#define AIC32X4_PWR_MICBIAS_2075_LDOIN 0x00000001 ++#define AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE 0x00000002 ++#define AIC32X4_PWR_AIC32X4_LDO_ENABLE 0x00000004 ++#define AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36 0x00000008 ++#define AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED 0x00000010 ++ ++#define AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K 0x00000001 ++#define AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K 0x00000002 ++ ++/* GPIO API */ ++#define AIC32X4_MFPX_DEFAULT_VALUE 0xff ++ ++#define AIC32X4_MFP1_DIN_DISABLED 0 ++#define AIC32X4_MFP1_DIN_ENABLED 0x2 ++#define AIC32X4_MFP1_GPIO_IN 0x4 ++ ++#define AIC32X4_MFP2_GPIO_OUT_LOW 0x0 ++#define AIC32X4_MFP2_GPIO_OUT_HIGH 0x1 ++ ++#define AIC32X4_MFP_GPIO_ENABLED 0x4 ++ ++#define AIC32X4_MFP5_GPIO_DISABLED 0x0 ++#define AIC32X4_MFP5_GPIO_INPUT 0x8 ++#define AIC32X4_MFP5_GPIO_OUTPUT 0xc ++#define AIC32X4_MFP5_GPIO_OUT_LOW 0x0 ++#define AIC32X4_MFP5_GPIO_OUT_HIGH 0x1 ++ + #endif /* _TLV320AIC32X4_H */ +diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c +--- a/sound/soc/codecs/tlv320dac33.c ++++ b/sound/soc/codecs/tlv320dac33.c +@@ -7,6 +7,7 @@ + * Copyright: (C) 2009 Nokia Corporation + */ + ++#include + #include + #include + #include +@@ -236,13 +237,10 @@ static int dac33_write_locked(struct snd_soc_component *component, unsigned int + unsigned int value) + { + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); +- int ret; + +- mutex_lock(&dac33->mutex); +- ret = dac33_write(component, reg, value); +- mutex_unlock(&dac33->mutex); ++ guard(mutex)(&dac33->mutex); + +- return ret; ++ return dac33_write(component, reg, value); + } + + #define DAC33_I2C_ADDR_AUTOINC 0x80 +@@ -365,13 +363,13 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); + int ret = 0; + +- mutex_lock(&dac33->mutex); ++ guard(mutex)(&dac33->mutex); + + /* Safety check */ + if (unlikely(power == dac33->chip_power)) { + dev_dbg(component->dev, "Trying to set the same power state: %s\n", + power ? "ON" : "OFF"); +- goto exit; ++ return ret; + } + + if (power) { +@@ -380,7 +378,7 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) + if (ret != 0) { + dev_err(component->dev, + "Failed to enable supplies: %d\n", ret); +- goto exit; ++ return ret; + } + + if (dac33->reset_gpiod) { +@@ -388,7 +386,7 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) + if (ret < 0) { + dev_err(&dac33->i2c->dev, + "Failed to set reset GPIO: %d\n", ret); +- goto exit; ++ return ret; + } + } + +@@ -400,7 +398,7 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) + if (ret < 0) { + dev_err(&dac33->i2c->dev, + "Failed to set reset GPIO: %d\n", ret); +- goto exit; ++ return ret; + } + } + +@@ -409,14 +407,12 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) + if (ret != 0) { + dev_err(component->dev, + "Failed to disable supplies: %d\n", ret); +- goto exit; ++ return ret; + } + + dac33->chip_power = 0; + } + +-exit: +- mutex_unlock(&dac33->mutex); + return ret; + } + +@@ -659,7 +655,6 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) + { + struct snd_soc_component *component = dac33->component; + unsigned int delay; +- unsigned long flags; + + switch (dac33->fifo_mode) { + case DAC33_FIFO_MODE1: +@@ -667,10 +662,10 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) + DAC33_THRREG(dac33->nsample)); + + /* Take the timestamps */ +- spin_lock_irqsave(&dac33->lock, flags); +- dac33->t_stamp2 = ktime_to_us(ktime_get()); +- dac33->t_stamp1 = dac33->t_stamp2; +- spin_unlock_irqrestore(&dac33->lock, flags); ++ scoped_guard(spinlock_irqsave, &dac33->lock) { ++ dac33->t_stamp2 = ktime_to_us(ktime_get()); ++ dac33->t_stamp1 = dac33->t_stamp2; ++ } + + dac33_write16(component, DAC33_PREFILL_MSB, + DAC33_THRREG(dac33->alarm_threshold)); +@@ -682,11 +677,11 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) + break; + case DAC33_FIFO_MODE7: + /* Take the timestamp */ +- spin_lock_irqsave(&dac33->lock, flags); +- dac33->t_stamp1 = ktime_to_us(ktime_get()); +- /* Move back the timestamp with drain time */ +- dac33->t_stamp1 -= dac33->mode7_us_to_lthr; +- spin_unlock_irqrestore(&dac33->lock, flags); ++ scoped_guard(spinlock_irqsave, &dac33->lock) { ++ dac33->t_stamp1 = ktime_to_us(ktime_get()); ++ /* Move back the timestamp with drain time */ ++ dac33->t_stamp1 -= dac33->mode7_us_to_lthr; ++ } + + dac33_write16(component, DAC33_PREFILL_MSB, + DAC33_THRREG(DAC33_MODE7_MARGIN)); +@@ -704,14 +699,12 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) + static inline void dac33_playback_handler(struct tlv320dac33_priv *dac33) + { + struct snd_soc_component *component = dac33->component; +- unsigned long flags; + + switch (dac33->fifo_mode) { + case DAC33_FIFO_MODE1: + /* Take the timestamp */ +- spin_lock_irqsave(&dac33->lock, flags); +- dac33->t_stamp2 = ktime_to_us(ktime_get()); +- spin_unlock_irqrestore(&dac33->lock, flags); ++ scoped_guard(spinlock_irqsave, &dac33->lock) ++ dac33->t_stamp2 = ktime_to_us(ktime_get()); + + dac33_write16(component, DAC33_NSAMPLE_MSB, + DAC33_THRREG(dac33->nsample)); +@@ -735,7 +728,7 @@ static void dac33_work(struct work_struct *work) + dac33 = container_of(work, struct tlv320dac33_priv, work); + component = dac33->component; + +- mutex_lock(&dac33->mutex); ++ guard(mutex)(&dac33->mutex); + switch (dac33->state) { + case DAC33_PREFILL: + dac33->state = DAC33_PLAYBACK; +@@ -757,18 +750,15 @@ static void dac33_work(struct work_struct *work) + dac33_write(component, DAC33_FIFO_CTRL_A, reg); + break; + } +- mutex_unlock(&dac33->mutex); + } + + static irqreturn_t dac33_interrupt_handler(int irq, void *dev) + { + struct snd_soc_component *component = dev; + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); +- unsigned long flags; + +- spin_lock_irqsave(&dac33->lock, flags); +- dac33->t_stamp1 = ktime_to_us(ktime_get()); +- spin_unlock_irqrestore(&dac33->lock, flags); ++ scoped_guard(spinlock_irqsave, &dac33->lock) ++ dac33->t_stamp1 = ktime_to_us(ktime_get()); + + /* Do not schedule the workqueue in Mode7 */ + if (dac33->fifo_mode != DAC33_FIFO_MODE7) +@@ -902,14 +892,13 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream, + return -EINVAL; + } + +- mutex_lock(&dac33->mutex); ++ guard(mutex)(&dac33->mutex); + + if (!dac33->chip_power) { + /* + * Chip is not powered yet. + * Do the init in the dac33_set_bias_level later. + */ +- mutex_unlock(&dac33->mutex); + return 0; + } + +@@ -1053,8 +1042,6 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream, + break; + } + +- mutex_unlock(&dac33->mutex); +- + return 0; + } + +@@ -1156,21 +1143,20 @@ static snd_pcm_sframes_t dac33_dai_delay( + unsigned int time_delta, uthr; + int samples_out, samples_in, samples; + snd_pcm_sframes_t delay = 0; +- unsigned long flags; + + switch (dac33->fifo_mode) { + case DAC33_FIFO_BYPASS: + break; + case DAC33_FIFO_MODE1: +- spin_lock_irqsave(&dac33->lock, flags); +- t0 = dac33->t_stamp1; +- t1 = dac33->t_stamp2; +- spin_unlock_irqrestore(&dac33->lock, flags); ++ scoped_guard(spinlock_irqsave, &dac33->lock) { ++ t0 = dac33->t_stamp1; ++ t1 = dac33->t_stamp2; ++ } + t_now = ktime_to_us(ktime_get()); + + /* We have not started to fill the FIFO yet, delay is 0 */ + if (!t1) +- goto out; ++ return 0; + + if (t0 > t1) { + /* +@@ -1230,23 +1216,22 @@ static snd_pcm_sframes_t dac33_dai_delay( + } + break; + case DAC33_FIFO_MODE7: +- spin_lock_irqsave(&dac33->lock, flags); +- t0 = dac33->t_stamp1; +- uthr = dac33->uthr; +- spin_unlock_irqrestore(&dac33->lock, flags); ++ scoped_guard(spinlock_irqsave, &dac33->lock) { ++ t0 = dac33->t_stamp1; ++ uthr = dac33->uthr; ++ } + t_now = ktime_to_us(ktime_get()); + + /* We have not started to fill the FIFO yet, delay is 0 */ + if (!t0) +- goto out; ++ return 0; + + if (t_now <= t0) { + /* + * Either the timestamps are messed or equal. Report + * maximum delay + */ +- delay = uthr; +- goto out; ++ return uthr; + } + + time_delta = t_now - t0; +@@ -1287,7 +1272,7 @@ static snd_pcm_sframes_t dac33_dai_delay( + dac33->fifo_mode); + break; + } +-out: ++ + return delay; + } + +diff --git a/sound/soc/codecs/tscs42xx.c b/sound/soc/codecs/tscs42xx.c +--- a/sound/soc/codecs/tscs42xx.c ++++ b/sound/soc/codecs/tscs42xx.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -210,25 +211,21 @@ static int power_up_audio_plls(struct snd_soc_component *component) + return ret; + } + +- mutex_lock(&tscs42xx->pll_lock); ++ guard(mutex)(&tscs42xx->pll_lock); + + ret = snd_soc_component_update_bits(component, R_PLLCTL1C, mask, val); + if (ret < 0) { + dev_err(component->dev, "Failed to turn PLL on (%d)\n", ret); +- goto exit; ++ return ret; + } + + if (!plls_locked(component)) { + dev_err(component->dev, "Failed to lock plls\n"); + ret = -ENOMSG; +- goto exit; ++ return ret; + } + +- ret = 0; +-exit: +- mutex_unlock(&tscs42xx->pll_lock); +- +- return ret; ++ return 0; + } + + static int power_down_audio_plls(struct snd_soc_component *component) +@@ -236,28 +233,24 @@ static int power_down_audio_plls(struct snd_soc_component *component) + struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); + int ret; + +- mutex_lock(&tscs42xx->pll_lock); ++ guard(mutex)(&tscs42xx->pll_lock); + + ret = snd_soc_component_update_bits(component, R_PLLCTL1C, + RM_PLLCTL1C_PDB_PLL1, + RV_PLLCTL1C_PDB_PLL1_DISABLE); + if (ret < 0) { + dev_err(component->dev, "Failed to turn PLL off (%d)\n", ret); +- goto exit; ++ return ret; + } + ret = snd_soc_component_update_bits(component, R_PLLCTL1C, + RM_PLLCTL1C_PDB_PLL2, + RV_PLLCTL1C_PDB_PLL2_DISABLE); + if (ret < 0) { + dev_err(component->dev, "Failed to turn PLL off (%d)\n", ret); +- goto exit; ++ return ret; + } + +- ret = 0; +-exit: +- mutex_unlock(&tscs42xx->pll_lock); +- +- return ret; ++ return 0; + } + + static int coeff_ram_get(struct snd_kcontrol *kcontrol, +@@ -269,13 +262,11 @@ static int coeff_ram_get(struct snd_kcontrol *kcontrol, + (struct coeff_ram_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + +- mutex_lock(&tscs42xx->coeff_ram_lock); ++ guard(mutex)(&tscs42xx->coeff_ram_lock); + + memcpy(ucontrol->value.bytes.data, + &tscs42xx->coeff_ram[ctl->addr * COEFF_SIZE], params->max); + +- mutex_unlock(&tscs42xx->coeff_ram_lock); +- + return 0; + } + +@@ -290,14 +281,14 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol, + unsigned int coeff_cnt = params->max / COEFF_SIZE; + int ret; + +- mutex_lock(&tscs42xx->coeff_ram_lock); ++ guard(mutex)(&tscs42xx->coeff_ram_lock); + + tscs42xx->coeff_ram_synced = false; + + memcpy(&tscs42xx->coeff_ram[ctl->addr * COEFF_SIZE], + ucontrol->value.bytes.data, params->max); + +- mutex_lock(&tscs42xx->pll_lock); ++ guard(mutex)(&tscs42xx->pll_lock); + + if (plls_locked(component)) { + ret = write_coeff_ram(component, tscs42xx->coeff_ram, +@@ -305,18 +296,12 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol, + if (ret < 0) { + dev_err(component->dev, + "Failed to flush coeff ram cache (%d)\n", ret); +- goto exit; ++ return ret; + } + tscs42xx->coeff_ram_synced = true; + } + +- ret = 0; +-exit: +- mutex_unlock(&tscs42xx->pll_lock); +- +- mutex_unlock(&tscs42xx->coeff_ram_lock); +- +- return ret; ++ return 0; + } + + /* Input L Capture Route */ +@@ -385,21 +370,17 @@ static int dac_event(struct snd_soc_dapm_widget *w, + struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); + int ret; + +- mutex_lock(&tscs42xx->coeff_ram_lock); ++ guard(mutex)(&tscs42xx->coeff_ram_lock); + + if (!tscs42xx->coeff_ram_synced) { + ret = write_coeff_ram(component, tscs42xx->coeff_ram, 0x00, + COEFF_RAM_COEFF_COUNT); + if (ret < 0) +- goto exit; ++ return ret; + tscs42xx->coeff_ram_synced = true; + } + +- ret = 0; +-exit: +- mutex_unlock(&tscs42xx->coeff_ram_lock); +- +- return ret; ++ return 0; + } + + static const struct snd_soc_dapm_widget tscs42xx_dapm_widgets[] = { +@@ -926,12 +907,10 @@ static int setup_sample_rate(struct snd_soc_component *component, + return ret; + } + +- mutex_lock(&tscs42xx->audio_params_lock); ++ guard(mutex)(&tscs42xx->audio_params_lock); + + tscs42xx->samplerate = rate; + +- mutex_unlock(&tscs42xx->audio_params_lock); +- + return 0; + } + +@@ -1253,12 +1232,10 @@ static int tscs42xx_set_dai_bclk_ratio(struct snd_soc_dai *codec_dai, + return ret; + } + +- mutex_lock(&tscs42xx->audio_params_lock); ++ guard(mutex)(&tscs42xx->audio_params_lock); + + tscs42xx->bclk_ratio = ratio; + +- mutex_unlock(&tscs42xx->audio_params_lock); +- + return 0; + } + +diff --git a/sound/soc/codecs/tscs454.c b/sound/soc/codecs/tscs454.c +--- a/sound/soc/codecs/tscs454.c ++++ b/sound/soc/codecs/tscs454.c +@@ -4,6 +4,7 @@ + // Author: Steven Eckhoff + + #include ++#include + #include + #include + #include +@@ -329,12 +330,10 @@ static int coeff_ram_get(struct snd_kcontrol *kcontrol, + return -EINVAL; + } + +- mutex_lock(coeff_ram_lock); +- +- memcpy(ucontrol->value.bytes.data, +- &coeff_ram[ctl->addr * COEFF_SIZE], params->max); +- +- mutex_unlock(coeff_ram_lock); ++ scoped_guard(mutex, coeff_ram_lock) { ++ memcpy(ucontrol->value.bytes.data, ++ &coeff_ram[ctl->addr * COEFF_SIZE], params->max); ++ } + + return 0; + } +@@ -428,15 +427,15 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol, + return -EINVAL; + } + +- mutex_lock(coeff_ram_lock); ++ guard(mutex)(coeff_ram_lock); + + *coeff_ram_synced = false; + + memcpy(&coeff_ram[ctl->addr * COEFF_SIZE], + ucontrol->value.bytes.data, params->max); + +- mutex_lock(&tscs454->pll1.lock); +- mutex_lock(&tscs454->pll2.lock); ++ guard(mutex)(&tscs454->pll1.lock); ++ guard(mutex)(&tscs454->pll2.lock); + + val = snd_soc_component_read(component, R_PLLSTAT); + if (val) { /* PLLs locked */ +@@ -446,18 +445,12 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol, + if (ret < 0) { + dev_err(component->dev, + "Failed to flush coeff ram cache (%d)\n", ret); +- goto exit; ++ return ret; + } + *coeff_ram_synced = true; + } + +- ret = 0; +-exit: +- mutex_unlock(&tscs454->pll2.lock); +- mutex_unlock(&tscs454->pll1.lock); +- mutex_unlock(coeff_ram_lock); +- +- return ret; ++ return 0; + } + + static inline int coeff_ram_sync(struct snd_soc_component *component, +@@ -465,41 +458,35 @@ static inline int coeff_ram_sync(struct snd_soc_component *component, + { + int ret; + +- mutex_lock(&tscs454->dac_ram.lock); +- if (!tscs454->dac_ram.synced) { +- ret = write_coeff_ram(component, tscs454->dac_ram.cache, +- R_DACCRS, R_DACCRADD, R_DACCRWDL, +- 0x00, COEFF_RAM_COEFF_COUNT); +- if (ret < 0) { +- mutex_unlock(&tscs454->dac_ram.lock); +- return ret; ++ scoped_guard(mutex, &tscs454->dac_ram.lock) { ++ if (!tscs454->dac_ram.synced) { ++ ret = write_coeff_ram(component, tscs454->dac_ram.cache, ++ R_DACCRS, R_DACCRADD, R_DACCRWDL, ++ 0x00, COEFF_RAM_COEFF_COUNT); ++ if (ret < 0) ++ return ret; + } + } +- mutex_unlock(&tscs454->dac_ram.lock); + +- mutex_lock(&tscs454->spk_ram.lock); +- if (!tscs454->spk_ram.synced) { +- ret = write_coeff_ram(component, tscs454->spk_ram.cache, +- R_SPKCRS, R_SPKCRADD, R_SPKCRWDL, +- 0x00, COEFF_RAM_COEFF_COUNT); +- if (ret < 0) { +- mutex_unlock(&tscs454->spk_ram.lock); +- return ret; ++ scoped_guard(mutex, &tscs454->spk_ram.lock) { ++ if (!tscs454->spk_ram.synced) { ++ ret = write_coeff_ram(component, tscs454->spk_ram.cache, ++ R_SPKCRS, R_SPKCRADD, R_SPKCRWDL, ++ 0x00, COEFF_RAM_COEFF_COUNT); ++ if (ret < 0) ++ return ret; + } + } +- mutex_unlock(&tscs454->spk_ram.lock); + +- mutex_lock(&tscs454->sub_ram.lock); +- if (!tscs454->sub_ram.synced) { +- ret = write_coeff_ram(component, tscs454->sub_ram.cache, +- R_SUBCRS, R_SUBCRADD, R_SUBCRWDL, +- 0x00, COEFF_RAM_COEFF_COUNT); +- if (ret < 0) { +- mutex_unlock(&tscs454->sub_ram.lock); +- return ret; ++ scoped_guard(mutex, &tscs454->sub_ram.lock) { ++ if (!tscs454->sub_ram.synced) { ++ ret = write_coeff_ram(component, tscs454->sub_ram.cache, ++ R_SUBCRS, R_SUBCRADD, R_SUBCRWDL, ++ 0x00, COEFF_RAM_COEFF_COUNT); ++ if (ret < 0) ++ return ret; + } + } +- mutex_unlock(&tscs454->sub_ram.lock); + + return 0; + } +@@ -658,16 +645,14 @@ static int set_sysclk(struct snd_soc_component *component) + + static inline void reserve_pll(struct pll *pll) + { +- mutex_lock(&pll->lock); ++ guard(mutex)(&pll->lock); + pll->users++; +- mutex_unlock(&pll->lock); + } + + static inline void free_pll(struct pll *pll) + { +- mutex_lock(&pll->lock); ++ guard(mutex)(&pll->lock); + pll->users--; +- mutex_unlock(&pll->lock); + } + + static int pll_connected(struct snd_soc_dapm_widget *source, +@@ -679,15 +664,13 @@ static int pll_connected(struct snd_soc_dapm_widget *source, + int users; + + if (strstr(source->name, "PLL 1")) { +- mutex_lock(&tscs454->pll1.lock); +- users = tscs454->pll1.users; +- mutex_unlock(&tscs454->pll1.lock); ++ scoped_guard(mutex, &tscs454->pll1.lock) ++ users = tscs454->pll1.users; + dev_dbg(component->dev, "%s(): PLL 1 users = %d\n", __func__, + users); + } else { +- mutex_lock(&tscs454->pll2.lock); +- users = tscs454->pll2.users; +- mutex_unlock(&tscs454->pll2.lock); ++ scoped_guard(mutex, &tscs454->pll2.lock) ++ users = tscs454->pll2.users; + dev_dbg(component->dev, "%s(): PLL 2 users = %d\n", __func__, + users); + } +@@ -806,7 +789,7 @@ static inline int aif_free(struct snd_soc_component *component, + { + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + +- mutex_lock(&tscs454->aifs_status_lock); ++ guard(mutex)(&tscs454->aifs_status_lock); + + dev_dbg(component->dev, "%s(): aif %d\n", __func__, aif->id); + +@@ -829,8 +812,6 @@ static inline int aif_free(struct snd_soc_component *component, + free_pll(tscs454->internal_rate.pll); + } + +- mutex_unlock(&tscs454->aifs_status_lock); +- + return 0; + } + +@@ -3174,7 +3155,7 @@ static int tscs454_hw_params(struct snd_pcm_substream *substream, + unsigned int val; + int ret; + +- mutex_lock(&tscs454->aifs_status_lock); ++ guard(mutex)(&tscs454->aifs_status_lock); + + dev_dbg(component->dev, "%s(): aif %d fs = %u\n", __func__, + aif->id, fs); +@@ -3207,14 +3188,14 @@ static int tscs454_hw_params(struct snd_pcm_substream *substream, + ret = set_aif_fs(component, aif->id, fs); + if (ret < 0) { + dev_err(component->dev, "Failed to set aif fs (%d)\n", ret); +- goto exit; ++ return ret; + } + + ret = set_aif_sample_format(component, params_format(params), aif->id); + if (ret < 0) { + dev_err(component->dev, + "Failed to set aif sample format (%d)\n", ret); +- goto exit; ++ return ret; + } + + set_aif_status_active(&tscs454->aifs_status, aif->id, +@@ -3223,11 +3204,7 @@ static int tscs454_hw_params(struct snd_pcm_substream *substream, + dev_dbg(component->dev, "Set aif %d active. Streams status is 0x%x\n", + aif->id, tscs454->aifs_status.streams); + +- ret = 0; +-exit: +- mutex_unlock(&tscs454->aifs_status_lock); +- +- return ret; ++ return 0; + } + + static int tscs454_hw_free(struct snd_pcm_substream *substream, +diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c +--- a/sound/soc/codecs/twl6040.c ++++ b/sound/soc/codecs/twl6040.c +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -273,7 +274,7 @@ static void twl6040_hs_jack_report(struct snd_soc_component *component, + struct twl6040_data *priv = snd_soc_component_get_drvdata(component); + int status; + +- mutex_lock(&priv->mutex); ++ guard(mutex)(&priv->mutex); + + /* Sync status */ + status = twl6040_read(component, TWL6040_REG_STATUS); +@@ -281,8 +282,6 @@ static void twl6040_hs_jack_report(struct snd_soc_component *component, + snd_soc_jack_report(jack, report, report); + else + snd_soc_jack_report(jack, 0, report); +- +- mutex_unlock(&priv->mutex); + } + + void twl6040_hs_jack_detect(struct snd_soc_component *component, +diff --git a/sound/soc/codecs/uda1342.c b/sound/soc/codecs/uda1342.c +--- a/sound/soc/codecs/uda1342.c ++++ b/sound/soc/codecs/uda1342.c +@@ -308,9 +308,16 @@ static int uda1342_suspend(struct device *dev) + static int uda1342_resume(struct device *dev) + { + struct uda1342_priv *uda1342 = dev_get_drvdata(dev); ++ int ret; + ++ regcache_cache_only(uda1342->regmap, false); + regcache_mark_dirty(uda1342->regmap); +- regcache_sync(uda1342->regmap); ++ ret = regcache_sync(uda1342->regmap); ++ if (ret) { ++ regcache_cache_only(uda1342->regmap, true); ++ regcache_mark_dirty(uda1342->regmap); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c +--- a/sound/soc/codecs/wcd9335.c ++++ b/sound/soc/codecs/wcd9335.c +@@ -5010,9 +5010,9 @@ static int wcd9335_power_on_reset(struct wcd9335_codec *wcd) + */ + usleep_range(600, 650); + +- gpiod_set_value(wcd->reset_gpio, 1); ++ gpiod_set_value_cansleep(wcd->reset_gpio, 1); + msleep(20); +- gpiod_set_value(wcd->reset_gpio, 0); ++ gpiod_set_value_cansleep(wcd->reset_gpio, 0); + msleep(20); + + return 0; +diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c +--- a/sound/soc/codecs/wcd934x.c ++++ b/sound/soc/codecs/wcd934x.c +@@ -1265,13 +1265,10 @@ static int wcd934x_set_sido_input_src(struct wcd934x_codec *wcd, int sido_src) + + static int wcd934x_enable_ana_bias_and_sysclk(struct wcd934x_codec *wcd) + { +- mutex_lock(&wcd->sysclk_mutex); +- +- if (++wcd->sysclk_users != 1) { +- mutex_unlock(&wcd->sysclk_mutex); +- return 0; ++ scoped_guard(mutex, &wcd->sysclk_mutex) { ++ if (++wcd->sysclk_users != 1) ++ return 0; + } +- mutex_unlock(&wcd->sysclk_mutex); + + regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS, + WCD934X_ANA_BIAS_EN_MASK, +@@ -1328,12 +1325,10 @@ static int wcd934x_enable_ana_bias_and_sysclk(struct wcd934x_codec *wcd) + + static int wcd934x_disable_ana_bias_and_syclk(struct wcd934x_codec *wcd) + { +- mutex_lock(&wcd->sysclk_mutex); +- if (--wcd->sysclk_users != 0) { +- mutex_unlock(&wcd->sysclk_mutex); +- return 0; ++ scoped_guard(mutex, &wcd->sysclk_mutex) { ++ if (--wcd->sysclk_users != 0) ++ return 0; + } +- mutex_unlock(&wcd->sysclk_mutex); + + regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG, + WCD934X_EXT_CLK_BUF_EN_MASK | +@@ -2384,7 +2379,7 @@ static int wcd934x_micbias_control(struct snd_soc_component *component, + __func__, micb_num); + return -EINVAL; + } +- mutex_lock(&wcd934x->micb_lock); ++ guard(mutex)(&wcd934x->micb_lock); + + switch (req) { + case MICB_PULLUP_ENABLE: +@@ -2446,8 +2441,6 @@ static int wcd934x_micbias_control(struct snd_soc_component *component, + break; + } + +- mutex_unlock(&wcd934x->micb_lock); +- + return 0; + } + +@@ -2488,7 +2481,7 @@ static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + int req_volt, int micb_num) + { + struct wcd934x_codec *wcd934x = snd_soc_component_get_drvdata(component); +- int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0; ++ int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en; + + switch (micb_num) { + case MIC_BIAS_1: +@@ -2506,7 +2499,7 @@ static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + default: + return -EINVAL; + } +- mutex_lock(&wcd934x->micb_lock); ++ guard(mutex)(&wcd934x->micb_lock); + /* + * If requested micbias voltage is same as current micbias + * voltage, then just return. Otherwise, adjust voltage as +@@ -2521,15 +2514,11 @@ static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + WCD934X_MICB_VAL_MASK); + + req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt); +- if (req_vout_ctl < 0) { +- ret = -EINVAL; +- goto exit; +- } ++ if (req_vout_ctl < 0) ++ return -EINVAL; + +- if (cur_vout_ctl == req_vout_ctl) { +- ret = 0; +- goto exit; +- } ++ if (cur_vout_ctl == req_vout_ctl) ++ return 0; + + if (micb_en == WCD934X_MICB_ENABLE) + snd_soc_component_write_field(component, micb_reg, +@@ -2550,9 +2539,8 @@ static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + */ + usleep_range(2000, 2100); + } +-exit: +- mutex_unlock(&wcd934x->micb_lock); +- return ret; ++ ++ return 0; + } + + static int wcd934x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, +@@ -5899,10 +5887,8 @@ static int wcd934x_codec_probe(struct platform_device *pdev) + } + + static const struct platform_device_id wcd934x_driver_id[] = { +- { +- .name = "wcd934x-codec", +- }, +- {}, ++ { .name = "wcd934x-codec" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, wcd934x_driver_id); + +diff --git a/sound/soc/codecs/wcd937x-sdw.c b/sound/soc/codecs/wcd937x-sdw.c +--- a/sound/soc/codecs/wcd937x-sdw.c ++++ b/sound/soc/codecs/wcd937x-sdw.c +@@ -1084,10 +1084,16 @@ static int wcd937x_sdw_runtime_suspend(struct device *dev) + static int wcd937x_sdw_runtime_resume(struct device *dev) + { + struct wcd937x_sdw_priv *wcd = dev_get_drvdata(dev); ++ int ret; + + if (wcd->regmap) { + regcache_cache_only(wcd->regmap, false); +- regcache_sync(wcd->regmap); ++ ret = regcache_sync(wcd->regmap); ++ if (ret) { ++ regcache_cache_only(wcd->regmap, true); ++ regcache_mark_dirty(wcd->regmap); ++ return ret; ++ } + } + + return 0; +diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c +--- a/sound/soc/codecs/wcd937x.c ++++ b/sound/soc/codecs/wcd937x.c +@@ -2,6 +2,7 @@ + // Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved. + + #include ++#include + #include + #include + #include +@@ -1056,7 +1057,7 @@ static int wcd937x_micbias_control(struct snd_soc_component *component, + return -EINVAL; + } + +- mutex_lock(&wcd937x->micb_lock); ++ guard(mutex)(&wcd937x->micb_lock); + switch (req) { + case MICB_PULLUP_ENABLE: + wcd937x->pullup_ref[micb_index]++; +@@ -1136,7 +1137,6 @@ static int wcd937x_micbias_control(struct snd_soc_component *component, + } + break; + } +- mutex_unlock(&wcd937x->micb_lock); + + return 0; + } +@@ -1460,7 +1460,7 @@ static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + int req_volt, int micb_num) + { + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); +- int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0; ++ int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en; + + switch (micb_num) { + case MIC_BIAS_1: +@@ -1475,7 +1475,7 @@ static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + default: + return -EINVAL; + } +- mutex_lock(&wcd937x->micb_lock); ++ guard(mutex)(&wcd937x->micb_lock); + /* + * If requested micbias voltage is same as current micbias + * voltage, then just return. Otherwise, adjust voltage as +@@ -1490,15 +1490,11 @@ static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + WCD937X_MICB_VOUT_MASK); + + req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt); +- if (req_vout_ctl < 0) { +- ret = -EINVAL; +- goto exit; +- } ++ if (req_vout_ctl < 0) ++ return -EINVAL; + +- if (cur_vout_ctl == req_vout_ctl) { +- ret = 0; +- goto exit; +- } ++ if (cur_vout_ctl == req_vout_ctl) ++ return 0; + + if (micb_en == WCD937X_MICB_ENABLE) + snd_soc_component_write_field(component, micb_reg, +@@ -1519,9 +1515,8 @@ static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + */ + usleep_range(2000, 2100); + } +-exit: +- mutex_unlock(&wcd937x->micb_lock); +- return ret; ++ ++ return 0; + } + + static int wcd937x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, +diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c +--- a/sound/soc/codecs/wcd938x-sdw.c ++++ b/sound/soc/codecs/wcd938x-sdw.c +@@ -1245,10 +1245,16 @@ static int wcd938x_sdw_runtime_suspend(struct device *dev) + static int wcd938x_sdw_runtime_resume(struct device *dev) + { + struct wcd938x_sdw_priv *wcd = dev_get_drvdata(dev); ++ int ret; + + if (wcd->regmap) { + regcache_cache_only(wcd->regmap, false); +- regcache_sync(wcd->regmap); ++ ret = regcache_sync(wcd->regmap); ++ if (ret) { ++ regcache_cache_only(wcd->regmap, true); ++ regcache_mark_dirty(wcd->regmap); ++ return ret; ++ } + } + + pm_runtime_mark_last_busy(dev); +diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c +--- a/sound/soc/codecs/wcd938x.c ++++ b/sound/soc/codecs/wcd938x.c +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: GPL-2.0-only + // Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + ++#include + #include + #include + #include +@@ -1976,7 +1977,7 @@ static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + int req_volt, int micb_num) + { + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); +- int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0; ++ int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en; + + switch (micb_num) { + case MIC_BIAS_1: +@@ -1994,7 +1995,7 @@ static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + default: + return -EINVAL; + } +- mutex_lock(&wcd938x->micb_lock); ++ guard(mutex)(&wcd938x->micb_lock); + /* + * If requested micbias voltage is same as current micbias + * voltage, then just return. Otherwise, adjust voltage as +@@ -2009,15 +2010,11 @@ static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + WCD938X_MICB_VOUT_MASK); + + req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt); +- if (req_vout_ctl < 0) { +- ret = -EINVAL; +- goto exit; +- } ++ if (req_vout_ctl < 0) ++ return -EINVAL; + +- if (cur_vout_ctl == req_vout_ctl) { +- ret = 0; +- goto exit; +- } ++ if (cur_vout_ctl == req_vout_ctl) ++ return 0; + + if (micb_en == WCD938X_MICB_ENABLE) + snd_soc_component_write_field(component, micb_reg, +@@ -2038,9 +2035,8 @@ static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + */ + usleep_range(2000, 2100); + } +-exit: +- mutex_unlock(&wcd938x->micb_lock); +- return ret; ++ ++ return 0; + } + + static int wcd938x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, +diff --git a/sound/soc/codecs/wcd939x-sdw.c b/sound/soc/codecs/wcd939x-sdw.c +--- a/sound/soc/codecs/wcd939x-sdw.c ++++ b/sound/soc/codecs/wcd939x-sdw.c +@@ -1431,10 +1431,16 @@ static int wcd939x_sdw_runtime_suspend(struct device *dev) + static int wcd939x_sdw_runtime_resume(struct device *dev) + { + struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev); ++ int ret; + + if (wcd->regmap) { + regcache_cache_only(wcd->regmap, false); +- regcache_sync(wcd->regmap); ++ ret = regcache_sync(wcd->regmap); ++ if (ret) { ++ regcache_cache_only(wcd->regmap, true); ++ regcache_mark_dirty(wcd->regmap); ++ return ret; ++ } + } + + return 0; +diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c +--- a/sound/soc/codecs/wcd939x.c ++++ b/sound/soc/codecs/wcd939x.c +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1923,7 +1924,6 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + unsigned int micb_reg, cur_vout_ctl, micb_en; + int req_vout_ctl; +- int ret = 0; + + switch (micb_num) { + case MIC_BIAS_1: +@@ -1941,7 +1941,7 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + default: + return -EINVAL; + } +- mutex_lock(&wcd939x->micb_lock); ++ guard(mutex)(&wcd939x->micb_lock); + + /* + * If requested micbias voltage is same as current micbias +@@ -1957,15 +1957,11 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + WCD939X_MICB_VOUT_CTL); + + req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt); +- if (req_vout_ctl < 0) { +- ret = req_vout_ctl; +- goto exit; +- } ++ if (req_vout_ctl < 0) ++ return req_vout_ctl; + +- if (cur_vout_ctl == req_vout_ctl) { +- ret = 0; +- goto exit; +- } ++ if (cur_vout_ctl == req_vout_ctl) ++ return 0; + + dev_dbg(component->dev, "%s: micb_num: %d, cur_mv: %d, req_mv: %d, micb_en: %d\n", + __func__, micb_num, WCD_VOUT_CTL_TO_MICB(cur_vout_ctl), +@@ -1990,9 +1986,7 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + usleep_range(2000, 2100); + } + +-exit: +- mutex_unlock(&wcd939x->micb_lock); +- return ret; ++ return 0; + } + + static int wcd939x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, +diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c +--- a/sound/soc/codecs/wm0010.c ++++ b/sound/soc/codecs/wm0010.c +@@ -9,6 +9,7 @@ + * Scott Ling + */ + ++#include + #include + #include + #include +@@ -148,13 +149,11 @@ static const char *wm0010_state_to_str(enum wm0010_state state) + static void wm0010_halt(struct snd_soc_component *component) + { + struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); +- unsigned long flags; + enum wm0010_state state; + + /* Fetch the wm0010 state */ +- spin_lock_irqsave(&wm0010->irq_lock, flags); +- state = wm0010->state; +- spin_unlock_irqrestore(&wm0010->irq_lock, flags); ++ scoped_guard(spinlock_irqsave, &wm0010->irq_lock) ++ state = wm0010->state; + + switch (state) { + case WM0010_POWER_OFF: +@@ -173,9 +172,8 @@ static void wm0010_halt(struct snd_soc_component *component) + break; + } + +- spin_lock_irqsave(&wm0010->irq_lock, flags); +- wm0010->state = WM0010_POWER_OFF; +- spin_unlock_irqrestore(&wm0010->irq_lock, flags); ++ scoped_guard(spinlock_irqsave, &wm0010->irq_lock) ++ wm0010->state = WM0010_POWER_OFF; + } + + struct wm0010_boot_xfer { +@@ -190,11 +188,9 @@ struct wm0010_boot_xfer { + static void wm0010_mark_boot_failure(struct wm0010_priv *wm0010) + { + enum wm0010_state state; +- unsigned long flags; + +- spin_lock_irqsave(&wm0010->irq_lock, flags); +- state = wm0010->state; +- spin_unlock_irqrestore(&wm0010->irq_lock, flags); ++ scoped_guard(spinlock_irqsave, &wm0010->irq_lock) ++ state = wm0010->state; + + dev_err(wm0010->dev, "Failed to transition from `%s' state to `%s' state\n", + wm0010_state_to_str(state), wm0010_state_to_str(state + 1)); +@@ -337,7 +333,6 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp + struct wm0010_boot_xfer *xfer; + int ret; + DECLARE_COMPLETION_ONSTACK(done); +- const struct firmware *fw; + const struct dfw_binrec *rec; + const struct dfw_inforec *inforec; + u64 *img; +@@ -346,6 +341,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp + + INIT_LIST_HEAD(&xfer_list); + ++ const struct firmware *fw __free(firmware) = NULL; + ret = request_firmware(&fw, name, component->dev); + if (ret != 0) { + dev_err(component->dev, "Failed to request application(%s): %d\n", +@@ -364,16 +360,14 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp + /* First record should be INFO */ + if (rec->command != DFW_CMD_INFO) { + dev_err(component->dev, "First record not INFO\r\n"); +- ret = -EINVAL; +- goto abort; ++ return -EINVAL; + } + + if (inforec->info_version != INFO_VERSION) { + dev_err(component->dev, + "Unsupported version (%02d) of INFO record\r\n", + inforec->info_version); +- ret = -EINVAL; +- goto abort; ++ return -EINVAL; + } + + dev_dbg(component->dev, "Version v%02d INFO record found\r\n", +@@ -382,8 +376,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp + /* Check it's a DSP file */ + if (dsp != DEVICE_ID_WM0010) { + dev_err(component->dev, "Not a WM0010 firmware file.\r\n"); +- ret = -EINVAL; +- goto abort; ++ return -EINVAL; + } + + /* Skip the info record as we don't need to send it */ +@@ -408,14 +401,14 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp + out = kzalloc(len, GFP_KERNEL | GFP_DMA); + if (!out) { + ret = -ENOMEM; +- goto abort1; ++ goto abort; + } + xfer->t.rx_buf = out; + + img = kzalloc(len, GFP_KERNEL | GFP_DMA); + if (!img) { + ret = -ENOMEM; +- goto abort1; ++ goto abort; + } + xfer->t.tx_buf = img; + +@@ -453,13 +446,13 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp + ret = spi_async(spi, &xfer->m); + if (ret != 0) { + dev_err(component->dev, "Write failed: %d\n", ret); +- goto abort1; ++ goto abort; + } + + if (wm0010->boot_failed) { + dev_dbg(component->dev, "Boot fail!\n"); + ret = -EINVAL; +- goto abort1; ++ goto abort; + } + } + +@@ -467,7 +460,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp + + ret = 0; + +-abort1: ++abort: + while (!list_empty(&xfer_list)) { + xfer = list_first_entry(&xfer_list, struct wm0010_boot_xfer, + list); +@@ -477,8 +470,6 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp + kfree(xfer); + } + +-abort: +- release_firmware(fw); + return ret; + } + +@@ -486,14 +477,12 @@ static int wm0010_stage2_load(struct snd_soc_component *component) + { + struct spi_device *spi = to_spi_device(component->dev); + struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); +- const struct firmware *fw; + struct spi_message m; + struct spi_transfer t; +- u32 *img; +- u8 *out; + int i; + int ret = 0; + ++ const struct firmware *fw __free(firmware) = NULL; + ret = request_firmware(&fw, "wm0010_stage2.bin", component->dev); + if (ret != 0) { + dev_err(component->dev, "Failed to request stage2 loader: %d\n", +@@ -504,17 +493,15 @@ static int wm0010_stage2_load(struct snd_soc_component *component) + dev_dbg(component->dev, "Downloading %zu byte stage 2 loader\n", fw->size); + + /* Copy to local buffer first as vmalloc causes problems for dma */ +- img = kmemdup(&fw->data[0], fw->size, GFP_KERNEL | GFP_DMA); +- if (!img) { +- ret = -ENOMEM; +- goto abort2; +- } ++ u32 *img __free(kfree) = ++ kmemdup(&fw->data[0], fw->size, GFP_KERNEL | GFP_DMA); ++ if (!img) ++ return -ENOMEM; + +- out = kzalloc(fw->size, GFP_KERNEL | GFP_DMA); +- if (!out) { +- ret = -ENOMEM; +- goto abort1; +- } ++ u8 *out __free(kfree) = ++ kzalloc(fw->size, GFP_KERNEL | GFP_DMA); ++ if (!out) ++ return -ENOMEM; + + spi_message_init(&m); + memset(&t, 0, sizeof(t)); +@@ -531,7 +518,7 @@ static int wm0010_stage2_load(struct snd_soc_component *component) + ret = spi_sync(spi, &m); + if (ret != 0) { + dev_err(component->dev, "Initial download failed: %d\n", ret); +- goto abort; ++ return ret; + } + + /* Look for errors from the boot ROM */ +@@ -540,18 +527,11 @@ static int wm0010_stage2_load(struct snd_soc_component *component) + dev_err(component->dev, "Boot ROM error: %x in %d\n", + out[i], i); + wm0010_mark_boot_failure(wm0010); +- ret = -EBUSY; +- goto abort; ++ return -EBUSY; + } + } +-abort: +- kfree(out); +-abort1: +- kfree(img); +-abort2: +- release_firmware(fw); + +- return ret; ++ return 0; + } + + static int wm0010_boot(struct snd_soc_component *component) +@@ -734,9 +714,8 @@ static int wm0010_set_bias_level(struct snd_soc_component *component, + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_PREPARE) { +- mutex_lock(&wm0010->lock); +- wm0010_halt(component); +- mutex_unlock(&wm0010->lock); ++ scoped_guard(mutex, &wm0010->lock) ++ wm0010_halt(component); + } + break; + case SND_SOC_BIAS_OFF: +@@ -832,9 +811,8 @@ static irqreturn_t wm0010_irq(int irq, void *data) + case WM0010_OUT_OF_RESET: + case WM0010_BOOTROM: + case WM0010_STAGE2: +- spin_lock(&wm0010->irq_lock); +- complete(&wm0010->boot_completion); +- spin_unlock(&wm0010->irq_lock); ++ scoped_guard(spinlock, &wm0010->irq_lock) ++ complete(&wm0010->boot_completion); + return IRQ_HANDLED; + default: + return IRQ_NONE; +diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c +--- a/sound/soc/codecs/wm2000.c ++++ b/sound/soc/codecs/wm2000.c +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -612,20 +613,15 @@ static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol, + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); + unsigned int anc_active = ucontrol->value.integer.value[0]; +- int ret; + + if (anc_active > 1) + return -EINVAL; + +- mutex_lock(&wm2000->lock); ++ guard(mutex)(&wm2000->lock); + + wm2000->anc_active = anc_active; + +- ret = wm2000_anc_set_mode(wm2000); +- +- mutex_unlock(&wm2000->lock); +- +- return ret; ++ return wm2000_anc_set_mode(wm2000); + } + + static int wm2000_speaker_get(struct snd_kcontrol *kcontrol, +@@ -645,20 +641,15 @@ static int wm2000_speaker_put(struct snd_kcontrol *kcontrol, + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); + unsigned int val = ucontrol->value.integer.value[0]; +- int ret; + + if (val > 1) + return -EINVAL; + +- mutex_lock(&wm2000->lock); ++ guard(mutex)(&wm2000->lock); + + wm2000->spk_ena = val; + +- ret = wm2000_anc_set_mode(wm2000); +- +- mutex_unlock(&wm2000->lock); +- +- return ret; ++ return wm2000_anc_set_mode(wm2000); + } + + static const struct snd_kcontrol_new wm2000_controls[] = { +@@ -676,9 +667,8 @@ static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w, + { + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); +- int ret; + +- mutex_lock(&wm2000->lock); ++ guard(mutex)(&wm2000->lock); + + if (SND_SOC_DAPM_EVENT_ON(event)) + wm2000->anc_eng_ena = 1; +@@ -686,11 +676,7 @@ static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w, + if (SND_SOC_DAPM_EVENT_OFF(event)) + wm2000->anc_eng_ena = 0; + +- ret = wm2000_anc_set_mode(wm2000); +- +- mutex_unlock(&wm2000->lock); +- +- return ret; ++ return wm2000_anc_set_mode(wm2000); + } + + static const struct snd_soc_dapm_widget wm2000_dapm_widgets[] = { +@@ -810,7 +796,7 @@ static int wm2000_i2c_probe(struct i2c_client *i2c) + struct wm2000_priv *wm2000; + struct wm2000_platform_data *pdata; + const char *filename; +- const struct firmware *fw = NULL; ++ const struct firmware *fw __free(firmware) = NULL; + int ret, i; + unsigned int reg; + u16 id; +@@ -828,7 +814,7 @@ static int wm2000_i2c_probe(struct i2c_client *i2c) + ret = PTR_ERR(wm2000->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); +- goto out; ++ return ret; + } + + for (i = 0; i < WM2000_NUM_SUPPLIES; i++) +@@ -922,9 +908,6 @@ static int wm2000_i2c_probe(struct i2c_client *i2c) + + err_supplies: + regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); +- +-out: +- release_firmware(fw); + return ret; + } + +diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c +--- a/sound/soc/codecs/wm2200.c ++++ b/sound/soc/codecs/wm2200.c +@@ -2461,7 +2461,15 @@ static int wm2200_runtime_resume(struct device *dev) + } + + regcache_cache_only(wm2200->regmap, false); +- regcache_sync(wm2200->regmap); ++ ret = regcache_sync(wm2200->regmap); ++ if (ret) { ++ regcache_cache_only(wm2200->regmap, true); ++ regcache_mark_dirty(wm2200->regmap); ++ gpiod_set_value_cansleep(wm2200->ldo_ena, 0); ++ regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies), ++ wm2200->core_supplies); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c +--- a/sound/soc/codecs/wm5100.c ++++ b/sound/soc/codecs/wm5100.c +@@ -2659,7 +2659,15 @@ static int wm5100_runtime_resume(struct device *dev) + } + + regcache_cache_only(wm5100->regmap, false); +- regcache_sync(wm5100->regmap); ++ ret = regcache_sync(wm5100->regmap); ++ if (ret) { ++ regcache_cache_only(wm5100->regmap, true); ++ regcache_mark_dirty(wm5100->regmap); ++ gpiod_set_value_cansleep(wm5100->ldo_ena, 0); ++ regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies), ++ wm5100->core_supplies); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c +--- a/sound/soc/codecs/wm5102.c ++++ b/sound/soc/codecs/wm5102.c +@@ -7,6 +7,7 @@ + * Author: Mark Brown + */ + ++#include + #include + #include + #include +@@ -667,10 +668,9 @@ static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol, + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct arizona *arizona = dev_get_drvdata(component->dev->parent); + +- mutex_lock(&arizona->dac_comp_lock); ++ guard(mutex)(&arizona->dac_comp_lock); + put_unaligned_be16(arizona->dac_comp_coeff, + ucontrol->value.bytes.data); +- mutex_unlock(&arizona->dac_comp_lock); + + return 0; + } +@@ -681,16 +681,14 @@ static int wm5102_out_comp_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct arizona *arizona = dev_get_drvdata(component->dev->parent); + uint16_t dac_comp_coeff = get_unaligned_be16(ucontrol->value.bytes.data); +- int ret = 0; + +- mutex_lock(&arizona->dac_comp_lock); ++ guard(mutex)(&arizona->dac_comp_lock); + if (arizona->dac_comp_coeff != dac_comp_coeff) { + arizona->dac_comp_coeff = dac_comp_coeff; +- ret = 1; ++ return 1; + } +- mutex_unlock(&arizona->dac_comp_lock); + +- return ret; ++ return 0; + } + + static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol, +@@ -699,9 +697,8 @@ static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol, + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct arizona *arizona = dev_get_drvdata(component->dev->parent); + +- mutex_lock(&arizona->dac_comp_lock); ++ guard(mutex)(&arizona->dac_comp_lock); + ucontrol->value.integer.value[0] = arizona->dac_comp_enabled; +- mutex_unlock(&arizona->dac_comp_lock); + + return 0; + } +@@ -712,19 +709,17 @@ static int wm5102_out_comp_switch_put(struct snd_kcontrol *kcontrol, + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct arizona *arizona = dev_get_drvdata(component->dev->parent); + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; +- int ret = 0; + + if (ucontrol->value.integer.value[0] > mc->max) + return -EINVAL; + +- mutex_lock(&arizona->dac_comp_lock); ++ guard(mutex)(&arizona->dac_comp_lock); + if (arizona->dac_comp_enabled != ucontrol->value.integer.value[0]) { + arizona->dac_comp_enabled = ucontrol->value.integer.value[0]; +- ret = 1; ++ return 1; + } +- mutex_unlock(&arizona->dac_comp_lock); + +- return ret; ++ return 0; + } + + static const char * const wm5102_osr_text[] = { +diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c +--- a/sound/soc/codecs/wm8731.c ++++ b/sound/soc/codecs/wm8731.c +@@ -10,6 +10,7 @@ + * Based on wm8753.c by Liam Girdwood + */ + ++#include + #include + #include + #include +@@ -110,22 +111,20 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component); + unsigned int deemph = ucontrol->value.integer.value[0]; +- int ret = 0; + + if (deemph > 1) + return -EINVAL; + +- mutex_lock(&wm8731->lock); ++ guard(mutex)(&wm8731->lock); + if (wm8731->deemph != deemph) { + wm8731->deemph = deemph; + + wm8731_set_deemph(component); + +- ret = 1; ++ return 1; + } +- mutex_unlock(&wm8731->lock); + +- return ret; ++ return 0; + } + + static const DECLARE_TLV_DB_SCALE(in_tlv, -3450, 150, 0); +diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c +--- a/sound/soc/codecs/wm8903.c ++++ b/sound/soc/codecs/wm8903.c +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -458,22 +459,20 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component); + unsigned int deemph = ucontrol->value.integer.value[0]; +- int ret = 0; + + if (deemph > 1) + return -EINVAL; + +- mutex_lock(&wm8903->lock); ++ guard(mutex)(&wm8903->lock); + if (wm8903->deemph != deemph) { + wm8903->deemph = deemph; + + wm8903_set_deemph(component); + +- ret = 1; ++ return 1; + } +- mutex_unlock(&wm8903->lock); + +- return ret; ++ return 0; + } + + /* ALSA can only do steps of .01dB */ +diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c +--- a/sound/soc/codecs/wm8940.c ++++ b/sound/soc/codecs/wm8940.c +@@ -333,7 +333,7 @@ static const struct snd_soc_dapm_route wm8940_dapm_routes[] = { + {"ADC", NULL, "Boost Mixer"}, + }; + +-#define wm8940_reset(c) snd_soc_component_write(c, WM8940_SOFTRESET, 0); ++#define wm8940_reset(c) snd_soc_component_write(c, WM8940_SOFTRESET, 0) + + static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c +--- a/sound/soc/codecs/wm8958-dsp2.c ++++ b/sound/soc/codecs/wm8958-dsp2.c +@@ -7,6 +7,7 @@ + * Author: Mark Brown + */ + ++#include + #include + #include + #include +@@ -864,9 +865,8 @@ static void wm8958_enh_eq_loaded(const struct firmware *fw, void *context) + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + if (fw && (wm8958_dsp2_fw(component, "ENH_EQ", fw, true) == 0)) { +- mutex_lock(&wm8994->fw_lock); ++ guard(mutex)(&wm8994->fw_lock); + wm8994->enh_eq = fw; +- mutex_unlock(&wm8994->fw_lock); + } + } + +@@ -876,9 +876,8 @@ static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context) + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + if (fw && (wm8958_dsp2_fw(component, "MBC+VSS", fw, true) == 0)) { +- mutex_lock(&wm8994->fw_lock); ++ guard(mutex)(&wm8994->fw_lock); + wm8994->mbc_vss = fw; +- mutex_unlock(&wm8994->fw_lock); + } + } + +@@ -888,9 +887,8 @@ static void wm8958_mbc_loaded(const struct firmware *fw, void *context) + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + if (fw && (wm8958_dsp2_fw(component, "MBC", fw, true) == 0)) { +- mutex_lock(&wm8994->fw_lock); ++ guard(mutex)(&wm8994->fw_lock); + wm8994->mbc = fw; +- mutex_unlock(&wm8994->fw_lock); + } + } + +diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c +--- a/sound/soc/codecs/wm8962.c ++++ b/sound/soc/codecs/wm8962.c +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1564,11 +1565,10 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol, + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); + int old = wm8962->dsp2_ena; +- int ret = 0; + int dsp2_running = snd_soc_component_read(component, WM8962_DSP2_POWER_MANAGEMENT) & + WM8962_DSP2_ENA; + +- mutex_lock(&wm8962->dsp2_ena_lock); ++ guard(mutex)(&wm8962->dsp2_ena_lock); + + if (ucontrol->value.integer.value[0]) + wm8962->dsp2_ena |= 1 << shift; +@@ -1576,9 +1576,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol, + wm8962->dsp2_ena &= ~(1 << shift); + + if (wm8962->dsp2_ena == old) +- goto out; +- +- ret = 1; ++ return 0; + + if (dsp2_running) { + if (wm8962->dsp2_ena) +@@ -1587,10 +1585,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol, + wm8962_dsp2_stop(component); + } + +-out: +- mutex_unlock(&wm8962->dsp2_ena_lock); +- +- return ret; ++ return 1; + } + + /* The VU bits for the headphones are in a different register to the mute +@@ -3937,7 +3932,9 @@ static int wm8962_runtime_resume(struct device *dev) + WM8962_OSC_ENA | WM8962_PLL2_ENA | WM8962_PLL3_ENA, + 0); + +- regcache_sync(wm8962->regmap); ++ ret = regcache_sync(wm8962->regmap); ++ if (ret) ++ goto cache_sync_err; + + regmap_update_bits(wm8962->regmap, WM8962_ANTI_POP, + WM8962_STARTUP_BIAS_ENA | WM8962_VMID_BUF_ENA, +@@ -3955,6 +3952,11 @@ static int wm8962_runtime_resume(struct device *dev) + + return 0; + ++cache_sync_err: ++ regcache_cache_only(wm8962->regmap, true); ++ regcache_mark_dirty(wm8962->regmap); ++ regulator_bulk_disable(ARRAY_SIZE(wm8962->supplies), ++ wm8962->supplies); + disable_clock: + clk_disable_unprepare(wm8962->pdata.mclk); + return ret; +diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c +--- a/sound/soc/codecs/wm8994.c ++++ b/sound/soc/codecs/wm8994.c +@@ -7,6 +7,7 @@ + * Author: Mark Brown + */ + ++#include + #include + #include + #include +@@ -766,7 +767,7 @@ static void active_reference(struct snd_soc_component *component) + { + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + +- mutex_lock(&wm8994->accdet_lock); ++ guard(mutex)(&wm8994->accdet_lock); + + wm8994->active_refcount++; + +@@ -775,8 +776,6 @@ static void active_reference(struct snd_soc_component *component) + + /* If we're using jack detection go into audio mode */ + wm1811_jackdet_set_mode(component, WM1811_JACKDET_MODE_AUDIO); +- +- mutex_unlock(&wm8994->accdet_lock); + } + + static void active_dereference(struct snd_soc_component *component) +@@ -784,7 +783,7 @@ static void active_dereference(struct snd_soc_component *component) + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + u16 mode; + +- mutex_lock(&wm8994->accdet_lock); ++ guard(mutex)(&wm8994->accdet_lock); + + wm8994->active_refcount--; + +@@ -800,8 +799,6 @@ static void active_dereference(struct snd_soc_component *component) + + wm1811_jackdet_set_mode(component, mode); + } +- +- mutex_unlock(&wm8994->accdet_lock); + } + + static int clk_sys_event(struct snd_soc_dapm_widget *w, +@@ -3704,7 +3701,7 @@ static void wm8958_open_circuit_work(struct work_struct *work) + open_circuit_work.work); + struct device *dev = wm8994->wm8994->dev; + +- mutex_lock(&wm8994->accdet_lock); ++ guard(mutex)(&wm8994->accdet_lock); + + wm1811_micd_stop(wm8994->hubs.component); + +@@ -3718,8 +3715,6 @@ static void wm8958_open_circuit_work(struct work_struct *work) + snd_soc_jack_report(wm8994->micdet[0].jack, 0, + wm8994->btn_mask | + SND_JACK_HEADSET); +- +- mutex_unlock(&wm8994->accdet_lock); + } + + static void wm8958_mic_id(void *data, u16 status) +@@ -3777,7 +3772,7 @@ static void wm1811_mic_work(struct work_struct *work) + struct snd_soc_component *component = wm8994->hubs.component; + struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); + +- pm_runtime_get_sync(component->dev); ++ guard(pm_runtime_active)(component->dev); + + /* If required for an external cap force MICBIAS on */ + if (control->pdata.jd_ext_cap) { +@@ -3785,7 +3780,7 @@ static void wm1811_mic_work(struct work_struct *work) + snd_soc_dapm_sync(dapm); + } + +- mutex_lock(&wm8994->accdet_lock); ++ guard(mutex)(&wm8994->accdet_lock); + + dev_dbg(component->dev, "Starting mic detection\n"); + +@@ -3803,10 +3798,6 @@ static void wm1811_mic_work(struct work_struct *work) + snd_soc_component_update_bits(component, WM8958_MIC_DETECT_1, + WM8958_MICD_ENA, WM8958_MICD_ENA); + } +- +- mutex_unlock(&wm8994->accdet_lock); +- +- pm_runtime_put(component->dev); + } + + static irqreturn_t wm1811_jackdet_irq(int irq, void *data) +@@ -4026,15 +4017,10 @@ static void wm8958_mic_work(struct work_struct *work) + mic_complete_work.work); + struct snd_soc_component *component = wm8994->hubs.component; + +- pm_runtime_get_sync(component->dev); +- +- mutex_lock(&wm8994->accdet_lock); ++ guard(pm_runtime_active)(component->dev); ++ guard(mutex)(&wm8994->accdet_lock); + + wm8994->mic_id_cb(wm8994->mic_id_cb_data, wm8994->mic_status); +- +- mutex_unlock(&wm8994->accdet_lock); +- +- pm_runtime_put(component->dev); + } + + static irqreturn_t wm8958_mic_irq(int irq, void *data) +diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c +--- a/sound/soc/codecs/wm9712.c ++++ b/sound/soc/codecs/wm9712.c +@@ -6,6 +6,7 @@ + * Author: Liam Girdwood + */ + ++#include + #include + #include + #include +@@ -229,7 +230,7 @@ static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol, + shift = mc->shift & 0xff; + mask = 1 << shift; + +- mutex_lock(&wm9712->lock); ++ guard(mutex)(&wm9712->lock); + old = wm9712->hp_mixer[mixer]; + if (ucontrol->value.integer.value[0]) + wm9712->hp_mixer[mixer] |= mask; +@@ -251,8 +252,6 @@ static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol, + &update); + } + +- mutex_unlock(&wm9712->lock); +- + return change; + } + +diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c +--- a/sound/soc/codecs/wm9713.c ++++ b/sound/soc/codecs/wm9713.c +@@ -11,6 +11,7 @@ + * o Support for DAPM + */ + ++#include + #include + #include + #include +@@ -238,7 +239,7 @@ static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol, + shift = mc->shift & 0xff; + mask = (1 << shift); + +- mutex_lock(&wm9713->lock); ++ guard(mutex)(&wm9713->lock); + old = wm9713->hp_mixer[mixer]; + if (ucontrol->value.integer.value[0]) + wm9713->hp_mixer[mixer] |= mask; +@@ -260,8 +261,6 @@ static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol, + &update); + } + +- mutex_unlock(&wm9713->lock); +- + return change; + } + +diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c +--- a/sound/soc/codecs/wm_adsp.c ++++ b/sound/soc/codecs/wm_adsp.c +@@ -348,7 +348,6 @@ int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); +- int ret = 1; + + if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw) + return 0; +@@ -356,16 +355,14 @@ int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, + if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW) + return -EINVAL; + +- mutex_lock(&dsp[e->shift_l].cs_dsp.pwr_lock); ++ guard(mutex)(&dsp[e->shift_l].cs_dsp.pwr_lock); + + if (dsp[e->shift_l].cs_dsp.booted || !list_empty(&dsp[e->shift_l].compr_list)) +- ret = -EBUSY; ++ return -EBUSY; + else + dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; + +- mutex_unlock(&dsp[e->shift_l].cs_dsp.pwr_lock); +- +- return ret; ++ return 1; + } + EXPORT_SYMBOL_GPL(wm_adsp_fw_put); + +@@ -450,15 +447,13 @@ static int wm_coeff_put_acked(struct snd_kcontrol *kctl, + if (val == 0) + return 0; /* 0 means no event */ + +- mutex_lock(&cs_ctl->dsp->pwr_lock); ++ guard(mutex)(&cs_ctl->dsp->pwr_lock); + + if (cs_ctl->enabled) + ret = cs_dsp_coeff_write_acked_control(cs_ctl, val); + else + ret = -EPERM; + +- mutex_unlock(&cs_ctl->dsp->pwr_lock); +- + if (ret < 0) + return ret; + +@@ -486,15 +481,13 @@ static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, + struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; + int ret = 0; + +- mutex_lock(&cs_ctl->dsp->pwr_lock); ++ guard(mutex)(&cs_ctl->dsp->pwr_lock); + + ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, cs_ctl->cache, size); + + if (!ret && copy_to_user(bytes, cs_ctl->cache, size)) + ret = -EFAULT; + +- mutex_unlock(&cs_ctl->dsp->pwr_lock); +- + return ret; + } + +@@ -694,10 +687,9 @@ int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, + struct cs_dsp_coeff_ctl *cs_ctl; + int ret; + +- mutex_lock(&dsp->cs_dsp.pwr_lock); ++ guard(mutex)(&dsp->cs_dsp.pwr_lock); + cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg); + ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len); +- mutex_unlock(&dsp->cs_dsp.pwr_lock); + + if (ret < 0) + return ret; +@@ -709,14 +701,10 @@ EXPORT_SYMBOL_GPL(wm_adsp_write_ctl); + int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type, + unsigned int alg, void *buf, size_t len) + { +- int ret; ++ guard(mutex)(&dsp->cs_dsp.pwr_lock); + +- mutex_lock(&dsp->cs_dsp.pwr_lock); +- ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg), ++ return cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg), + 0, buf, len); +- mutex_unlock(&dsp->cs_dsp.pwr_lock); +- +- return ret; + } + EXPORT_SYMBOL_GPL(wm_adsp_read_ctl); + +@@ -1270,38 +1258,32 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) + { + struct wm_adsp_compr *compr, *tmp; + struct snd_soc_pcm_runtime *rtd = stream->private_data; +- int ret = 0; + +- mutex_lock(&dsp->cs_dsp.pwr_lock); ++ guard(mutex)(&dsp->cs_dsp.pwr_lock); + + if (wm_adsp_fw[dsp->fw].num_caps == 0) { + adsp_err(dsp, "%s: Firmware does not support compressed API\n", + snd_soc_rtd_to_codec(rtd, 0)->name); +- ret = -ENXIO; +- goto out; ++ return -ENXIO; + } + + if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { + adsp_err(dsp, "%s: Firmware does not support stream direction\n", + snd_soc_rtd_to_codec(rtd, 0)->name); +- ret = -EINVAL; +- goto out; ++ return -EINVAL; + } + + list_for_each_entry(tmp, &dsp->compr_list, list) { + if (!strcmp(tmp->name, snd_soc_rtd_to_codec(rtd, 0)->name)) { + adsp_err(dsp, "%s: Only a single stream supported per dai\n", + snd_soc_rtd_to_codec(rtd, 0)->name); +- ret = -EBUSY; +- goto out; ++ return -EBUSY; + } + } + + compr = kzalloc_obj(*compr); +- if (!compr) { +- ret = -ENOMEM; +- goto out; +- } ++ if (!compr) ++ return -ENOMEM; + + compr->dsp = dsp; + compr->stream = stream; +@@ -1311,10 +1293,7 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) + + stream->runtime->private_data = compr; + +-out: +- mutex_unlock(&dsp->cs_dsp.pwr_lock); +- +- return ret; ++ return 0; + } + EXPORT_SYMBOL_GPL(wm_adsp_compr_open); + +@@ -1324,7 +1303,7 @@ int wm_adsp_compr_free(struct snd_soc_component *component, + struct wm_adsp_compr *compr = stream->runtime->private_data; + struct wm_adsp *dsp = compr->dsp; + +- mutex_lock(&dsp->cs_dsp.pwr_lock); ++ guard(mutex)(&dsp->cs_dsp.pwr_lock); + + wm_adsp_compr_detach(compr); + list_del(&compr->list); +@@ -1332,8 +1311,6 @@ int wm_adsp_compr_free(struct snd_soc_component *component, + kfree(compr->raw_buf); + kfree(compr); + +- mutex_unlock(&dsp->cs_dsp.pwr_lock); +- + return 0; + } + EXPORT_SYMBOL_GPL(wm_adsp_compr_free); +@@ -1741,7 +1718,7 @@ int wm_adsp_compr_trigger(struct snd_soc_component *component, + + compr_dbg(compr, "Trigger: %d\n", cmd); + +- mutex_lock(&dsp->cs_dsp.pwr_lock); ++ guard(mutex)(&dsp->cs_dsp.pwr_lock); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: +@@ -1777,8 +1754,6 @@ int wm_adsp_compr_trigger(struct snd_soc_component *component, + break; + } + +- mutex_unlock(&dsp->cs_dsp.pwr_lock); +- + return ret; + } + EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger); +@@ -1907,21 +1882,20 @@ int wm_adsp_compr_pointer(struct snd_soc_component *component, + + compr_dbg(compr, "Pointer request\n"); + +- mutex_lock(&dsp->cs_dsp.pwr_lock); ++ guard(mutex)(&dsp->cs_dsp.pwr_lock); + + buf = compr->buf; + + if (dsp->fatal_error || !buf || buf->error) { + snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN); +- ret = -EIO; +- goto out; ++ return -EIO; + } + + if (buf->avail < wm_adsp_compr_frag_words(compr)) { + ret = wm_adsp_buffer_update_avail(buf); + if (ret < 0) { + compr_err(compr, "Error reading avail: %d\n", ret); +- goto out; ++ return ret; + } + + /* +@@ -1934,14 +1908,14 @@ int wm_adsp_compr_pointer(struct snd_soc_component *component, + if (buf->error) + snd_compr_stop_error(stream, + SNDRV_PCM_STATE_XRUN); +- goto out; ++ return ret; + } + + ret = wm_adsp_buffer_reenable_irq(buf); + if (ret < 0) { + compr_err(compr, "Failed to re-enable buffer IRQ: %d\n", + ret); +- goto out; ++ return ret; + } + } + } +@@ -1950,9 +1924,6 @@ int wm_adsp_compr_pointer(struct snd_soc_component *component, + tstamp->copied_total += buf->avail * CS_DSP_DATA_WORD_SIZE; + tstamp->sampling_rate = compr->sample_rate; + +-out: +- mutex_unlock(&dsp->cs_dsp.pwr_lock); +- + return ret; + } + EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer); +@@ -2063,15 +2034,13 @@ int wm_adsp_compr_copy(struct snd_soc_component *component, + struct wm_adsp *dsp = compr->dsp; + int ret; + +- mutex_lock(&dsp->cs_dsp.pwr_lock); ++ guard(mutex)(&dsp->cs_dsp.pwr_lock); + + if (stream->direction == SND_COMPRESS_CAPTURE) + ret = wm_adsp_compr_read(compr, buf, count); + else + ret = -ENOTSUPP; + +- mutex_unlock(&dsp->cs_dsp.pwr_lock); +- + return ret; + } + EXPORT_SYMBOL_GPL(wm_adsp_compr_copy); +diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c +--- a/sound/soc/codecs/wsa881x.c ++++ b/sound/soc/codecs/wsa881x.c +@@ -1178,7 +1178,13 @@ static int wsa881x_runtime_resume(struct device *dev) + } + + regcache_cache_only(regmap, false); +- regcache_sync(regmap); ++ ret = regcache_sync(regmap); ++ if (ret) { ++ regcache_cache_only(regmap, true); ++ regcache_mark_dirty(regmap); ++ gpiod_direction_output(wsa881x->sd_n, 1); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c +--- a/sound/soc/codecs/wsa883x.c ++++ b/sound/soc/codecs/wsa883x.c +@@ -4,6 +4,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -1237,9 +1238,8 @@ static int wsa883x_spkr_event(struct snd_soc_dapm_widget *w, + + switch (event) { + case SND_SOC_DAPM_POST_PMU: +- mutex_lock(&wsa883x->sp_lock); +- wsa883x->pa_on = true; +- mutex_unlock(&wsa883x->sp_lock); ++ scoped_guard(mutex, &wsa883x->sp_lock) ++ wsa883x->pa_on = true; + + switch (wsa883x->dev_mode) { + case RECEIVER: +@@ -1290,9 +1290,8 @@ static int wsa883x_spkr_event(struct snd_soc_dapm_widget *w, + WSA883X_GLOBAL_PA_EN_MASK, 0); + snd_soc_component_write_field(component, WSA883X_PDM_WD_CTL, + WSA883X_PDM_EN_MASK, 0); +- mutex_lock(&wsa883x->sp_lock); +- wsa883x->pa_on = false; +- mutex_unlock(&wsa883x->sp_lock); ++ scoped_guard(mutex, &wsa883x->sp_lock) ++ wsa883x->pa_on = false; + break; + } + return 0; +@@ -1696,9 +1695,15 @@ static int wsa883x_runtime_suspend(struct device *dev) + static int wsa883x_runtime_resume(struct device *dev) + { + struct regmap *regmap = dev_get_regmap(dev, NULL); ++ int ret; + + regcache_cache_only(regmap, false); +- regcache_sync(regmap); ++ ret = regcache_sync(regmap); ++ if (ret) { ++ regcache_cache_only(regmap, true); ++ regcache_mark_dirty(regmap); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c +--- a/sound/soc/codecs/wsa884x.c ++++ b/sound/soc/codecs/wsa884x.c +@@ -1701,9 +1701,8 @@ static int wsa884x_spkr_event(struct snd_soc_dapm_widget *w, + + switch (event) { + case SND_SOC_DAPM_POST_PMU: +- mutex_lock(&wsa884x->sp_lock); +- wsa884x->pa_on = true; +- mutex_unlock(&wsa884x->sp_lock); ++ scoped_guard(mutex, &wsa884x->sp_lock) ++ wsa884x->pa_on = true; + + wsa884x_spkr_post_pmu(component, wsa884x); + +@@ -1717,9 +1716,8 @@ static int wsa884x_spkr_event(struct snd_soc_dapm_widget *w, + WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK, + 0x0); + +- mutex_lock(&wsa884x->sp_lock); +- wsa884x->pa_on = false; +- mutex_unlock(&wsa884x->sp_lock); ++ scoped_guard(mutex, &wsa884x->sp_lock) ++ wsa884x->pa_on = false; + break; + } + +@@ -2147,9 +2145,15 @@ static int wsa884x_runtime_suspend(struct device *dev) + static int wsa884x_runtime_resume(struct device *dev) + { + struct regmap *regmap = dev_get_regmap(dev, NULL); ++ int ret; + + regcache_cache_only(regmap, false); +- regcache_sync(regmap); ++ ret = regcache_sync(regmap); ++ if (ret) { ++ regcache_cache_only(regmap, true); ++ regcache_mark_dirty(regmap); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/wsa885x.c b/sound/soc/codecs/wsa885x.c +new file mode 100644 +--- /dev/null ++++ b/sound/soc/codecs/wsa885x.c +@@ -0,0 +1,1533 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. ++ */ ++ ++/* WSA885X codec driver */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Control Registers - Audio Processing */ ++#define WSA885X_SMP_AMP_CTRL_STEREO_STEREO_SMP_AMP_CTRL_I2S 0x0000 ++#define WSA885X_SMP_AMP_CTRL_STEREO_CMT_GRP_MASK 0x0004 ++#define WSA885X_SMP_AMP_CTRL_STEREO_IT21_CLUSERINDEX 0x0140 ++#define WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID 0x0208 ++#define WSA885X_SMP_AMP_CTRL_STEREO_CS21_SAMPLERATEINDEX 0x0240 ++#define WSA885X_SMP_AMP_CTRL_STEREO_PPU21_POSTURENUMBER 0x0340 ++#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0 0x4405 ++#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1 0x4406 ++#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB 0x4409 ++#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_MSB 0x6409 ++#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB 0x440a ++#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_MSB 0x640a ++#define WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS 0x0a04 ++#define WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS 0x0a40 ++#define WSA885X_SMP_AMP_CTRL_STEREO_OT23_USAGE 0x0b10 ++#define WSA885X_SMP_AMP_CTRL_STEREO_CS24_SAMPLERATEINDEX 0x0e40 ++ ++/* Analog Top Registers - Power and Clock Control */ ++#define WSA885X_ANA_TOP_PON_CKSK_CTL_0 0x800d ++#define WSA885X_ANA_TOP_BG_TVP_UVLO1_PROG 0x8024 ++#define WSA885X_ANA_TOP_BG_TVP_UVLO2_PROG 0x8025 ++#define WSA885X_ANA_TOP_BG_TVP_OVRD_CTL 0x8034 ++ ++/* Analog PLL Registers */ ++#define WSA885X_ANA_PLL_DIV_CTL_0 0x8090 ++#define WSA885X_ANA_PLL_DIV_CTL_1 0x8091 ++#define WSA885X_ANA_TOP_PLL_VCO_CTL 0x8092 ++#define WSA885X_ANA_TOP_PLL_LOOPFILT_0 0x8093 ++#define WSA885X_ANA_TOP_PLL_OVRD_CTL 0x8098 ++#define WSA885X_ANA_TOP_PLL_STATUS_0 0x809a ++#define WSA885X_ANA_TOP_PLL_STATUS_1 0x809b ++ ++/* Analog Boost Control Registers */ ++#define WSA885X_ANA_TOP_BOOST_STB_CTRL2 0x805b ++#define WSA885X_ANA_TOP_BOOST_STB_CTRL3 0x805c ++#define WSA885X_ANA_TOP_BOOST_BYP_CTRL2 0x805e ++#define WSA885X_ANA_TOP_BOOST_BYP_CTRL3 0x805f ++#define WSA885X_ANA_TOP_BOOST_MISC 0x8063 ++#define WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL2 0x8065 ++#define WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL4 0x8067 ++ ++/* Analog IV Sense ADC Registers */ ++#define WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL2 0x80ca ++#define WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3 0x80cb ++#define WSA885X_ANA_TOP_IVSENSE_ADC_REF_CTL 0x80cc ++#define WSA885X_ANA_TOP_IVSENSE_ADC_CDAC_CAL_CTL2 0x80d0 ++ ++/* Analog Speaker Power Stage Registers */ ++#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_CTRL3 0x8108 ++#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_TUNE3 0x810b ++#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_CTRL3 0x810e ++#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_TUNE3 0x8111 ++#define WSA885X_ANA_TOP_SPK_TOP_SPARE3 0x813c ++#define WSA885X_SPK_TOP_LF_CH1_CTRL11 0x811c ++#define WSA885X_SPK_TOP_LF_CH1_TUNE1 0x811d ++#define WSA885X_SPK_TOP_LF_CH2_TUNE1 0x8129 ++#define WSA885X_SPK_TOP_LF_CH1_CTRL9 0x811a ++#define WSA885X_SPK_TOP_LF_CH2_CTRL9 0x8126 ++#define WSA885X_SPK_TOP_LF_CH2_CTRL11 0x8128 ++#define WSA885X_SPK_TOP_COMMON_CTRL2 0x8102 ++#define WSA885X_SPK_TOP_COMMON_TUNE1 0x8103 ++#define WSA885X_IVSENSE_VSNS_ISNS_CTL_CH1 0x80ba ++#define WSA885X_DIG_CTRL0_TOP_CLK_CFG 0x8418 ++#define WSA885X_DIG_CTRL0_SDCA_COMMIT 0x8419 ++#define WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE 0x841a ++#define WSA885X_DIG_CTRL0_SYS_CLK_SEL 0x841b ++#define WSA885X_DIG_CTRL0_CDC_CLK_CTL 0x841c ++#define WSA885X_DIG_CTRL0_PA_FSM_CTL 0x8420 ++#define WSA885X_DIG_CTRL0_POWER_FSM_CTL0 0x8423 ++#define WSA885X_DIG_CTRL0_POWER_FSM_CTL1 0x8424 ++#define WSA885X_DIG_CTRL0_PA0_FSM_CTL1 0x842b ++#define WSA885X_DIG_CTRL0_PA1_FSM_CTL1 0x8435 ++#define WSA885X_DIG_CTRL0_VBAT_THRM_FLT_CTL 0x8458 ++#define WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL 0x8470 ++#define WSA885X_DIG_CTRL0_GAIN_RAMP0_CTL1 0x84b4 ++#define WSA885X_DIG_CTRL0_GAIN_RAMP1_CTL1 0x84b7 ++#define WSA885X_DIG_CTRL0_PCM_DATA_WD0_CTL1 0x84A0 ++#define WSA885X_DIG_CTRL0_PCM_DATA_WD1_CTL1 0x84A4 ++ ++/* Digital Control 1 Registers - I2S/TDM Interface */ ++#define WSA885X_DIG_CTRL1_I2S_CTL0 0x85A0 ++#define WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX 0x85A2 ++#define WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX 0x85A3 ++#define WSA885X_DIG_CTRL1_I2S_TDM_CTL0 0x85A7 ++#define WSA885X_DIG_CTRL1_I2S_TDM_CTL1 0x85A9 ++#define WSA885X_DIG_CTRL1_I2S_TDM_CH_RX 0x85AA ++#define WSA885X_DIG_CTRL1_I2S_TDM_CH_TX 0x85AB ++#define WSA885X_DIG_CTRL1_I2S_RESET_CTL 0x85AE ++ ++/* CDC RX Path Registers - Audio Data Path */ ++#define WSA885X_CDC_RX0_RX_PATH_CFG0 0x8601 ++#define WSA885X_CDC_RX0_RX_PATH_CFG1 0x8602 ++#define WSA885X_CDC_RX0_RX_PATH_CTL 0x8606 ++#define WSA885X_RX0_RX_PATH_DSMDEM_CTL 0x8613 ++#define WSA885X_CDC_RX1_RX_PATH_CFG0 0x8621 ++#define WSA885X_CDC_RX1_RX_PATH_CFG1 0x8622 ++#define WSA885X_CDC_RX1_RX_PATH_CTL 0x8626 ++#define WSA885X_RX1_RX_PATH_DSMDEM_CTL 0x8633 ++ ++/* CDC Compander Registers - Dynamic Range Control */ ++#define WSA885X_CDC_COMPANDER0_CTL0 0x8640 ++#define WSA885X_CDC_COMPANDER0_CTL7 0x8647 ++#define WSA885X_CDC_COMPANDER1_CTL0 0x8660 ++#define WSA885X_CDC_COMPANDER1_CTL7 0x8667 ++ ++/* CDC Speaker Protection Registers - IV Sense */ ++#define WSA885X_CDC_VSENSE0_SPKR_PROT_PATH_CTL 0x86A1 ++#define WSA885X_CDC_VSENSE1_SPKR_PROT_PATH_CTL 0x86B1 ++#define WSA885X_CDC_ISENSE0_SPKR_PROT_PATH_CTL 0x86A9 ++#define WSA885X_CDC_ISENSE1_SPKR_PROT_PATH_CTL 0x86B9 ++ ++/* CDC Class-H Registers - Headroom Control */ ++#define WSA885X_CDC_CLSH_V1P8_BP_CTL1 0x86CD ++#define WSA885X_CDC_CLSH_V1P8_BP_CTL0 0x86CC ++#define WSA885X_CDC_CLSH_CLSH_SIG_DP_CTL0 0x86C7 ++#define WSA885X_CDC_CLSH_CLSH_V_HD_PA 0x86C3 ++#define WSA885X_CDC_CLSH_V1P8_BP_CTL2 0x86CE ++ ++/* Driver Constants */ ++#define WSA885X_CLK_RATE_FIXED 73728000 ++#define WSA885X_NUM_REGS 0x03 ++ ++/* Interrupt Registers */ ++#define WSA885X_INTR_STATUS0 0x8584 ++#define WSA885X_INTR_MASK0 0x8581 ++#define WSA885X_INTR_CLEAR0 0x8587 ++ ++/* Power and PA FSM Control Registers */ ++#define WSA885X_PA0_FSM_CTL0 0x842A ++#define WSA885X_PA1_FSM_CTL0 0x8434 ++ ++/* Digital Control GPIO and Interrupt Registers */ ++#define WSA885X_DIG_CTRL1_PIN_CT 0x8510 ++#define WSA885X_DIG_CTRL1_SPMI_PAD_GPIO2_CTL 0x8518 ++#define WSA885X_DIG_CTRL1_INTR_MODE 0x8580 ++ ++#define WSA885X_I2S_CTL0_PCM_RATE_MASK GENMASK(4, 1) ++#define WSA885X_I2S_CTL0_ENABLE_MASK BIT(0) ++#define WSA885X_I2S_CTL0_PCM_RATE(v) \ ++ FIELD_PREP(WSA885X_I2S_CTL0_PCM_RATE_MASK, (v)) ++#define WSA885X_I2S_CTL0_PCM_RATE_8KHZ 0x0 ++#define WSA885X_I2S_CTL0_PCM_RATE_16KHZ 0x1 ++#define WSA885X_I2S_CTL0_PCM_RATE_32KHZ 0x2 ++#define WSA885X_I2S_CTL0_PCM_RATE_48_OR_44KHZ 0x3 ++#define WSA885X_I2S_CTL0_PCM_RATE_96_OR_88KHZ 0x4 ++#define WSA885X_I2S_CTL0_PCM_RATE_192_OR_176KHZ 0x5 ++#define WSA885X_I2S_CTL0_PCM_RATE_384_OR_352KHZ 0x6 ++#define WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK GENMASK(2, 0) ++#define WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK GENMASK(6, 4) ++#define WSA885X_I2S_CFG0_TDM_TX_SLOT0(v) \ ++ FIELD_PREP_CONST(WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK, (v)) ++#define WSA885X_I2S_CFG0_TDM_TX_SLOT1(v) \ ++ FIELD_PREP_CONST(WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK, (v)) ++#define WSA885X_I2S_CFG1_TDM_TX_SLOT2_MASK GENMASK(2, 0) ++#define WSA885X_I2S_CFG1_TDM_TX_SLOT3_MASK GENMASK(6, 4) ++#define WSA885X_I2S_CFG1_TDM_TX_SLOT2(v) \ ++ FIELD_PREP_CONST(WSA885X_I2S_CFG1_TDM_TX_SLOT2_MASK, (v)) ++#define WSA885X_I2S_CFG1_TDM_TX_SLOT3(v) \ ++ FIELD_PREP_CONST(WSA885X_I2S_CFG1_TDM_TX_SLOT3_MASK, (v)) ++#define WSA885X_I2S_TDM_CTL0_I2S_TDM_EN_MASK BIT(0) ++#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK GENMASK(3, 2) ++#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_2 \ ++ FIELD_PREP_CONST(WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, 0) ++#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_4 \ ++ FIELD_PREP_CONST(WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, 1) ++#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_8 \ ++ FIELD_PREP_CONST(WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, 3) ++#define WSA885X_I2S_TDM_CH_TX_CH0_EN BIT(0) ++#define WSA885X_I2S_TDM_CH_TX_CH1_EN BIT(1) ++#define WSA885X_I2S_TDM_CH_TX_CH2_EN BIT(2) ++#define WSA885X_I2S_TDM_CH_TX_CH3_EN BIT(3) ++#define WSA885X_I2S_TDM_CH_RX_CH0_EN BIT(0) ++#define WSA885X_I2S_TDM_CH_RX_CH3_EN BIT(3) ++#define WSA885X_I2S_RESET_CTL_RESET_MASK BIT(0) ++#define WSA885X_PCM_DATA_WD_CTL1_PCM_DATA_WD_EN_MASK BIT(2) ++#define WSA885X_POWER_FSM_CTL0_CLEAR_ERROR_MASK BIT(3) ++#define WSA885X_PA_FSM_CTL0_CLEAR_ERROR_MASK BIT(2) ++ ++#define WSA885X_I2S_TX_SLOT_ISENSE0 0x1 ++#define WSA885X_I2S_TX_SLOT_ISENSE1 0x2 ++#define WSA885X_I2S_TX_SLOT_CUR_SENSE0 0x5 ++#define WSA885X_I2S_TX_SLOT_CUR_SENSE1 0x6 ++ ++/* RX Sample Rate Index Values - Audio Playback Path */ ++#define WSA885X_RX_RATE_8000HZ 0x00 ++#define WSA885X_RX_RATE_16000HZ 0x01 ++#define WSA885X_RX_RATE_32000HZ 0x02 ++#define WSA885X_RX_RATE_44100HZ 0x03 ++#define WSA885X_RX_RATE_48000HZ 0x04 ++#define WSA885X_RX_RATE_96000HZ 0x05 ++#define WSA885X_RX_RATE_192000HZ 0x06 ++#define WSA885X_RX_RATE_384000HZ 0x07 ++ ++/* VI Sample Rate Index Values - Voltage/Current Sensing Path */ ++#define WSA885X_VI_RATE_8000HZ 0x00 ++#define WSA885X_VI_RATE_16000HZ 0x01 ++#define WSA885X_VI_RATE_44100HZ 0x02 ++#define WSA885X_VI_RATE_48000HZ 0x03 ++#define WSA885X_VI_RATE_96000HZ 0x04 ++#define WSA885X_VI_RATE_22050HZ 0x05 ++#define WSA885X_VI_RATE_24000HZ 0x06 ++#define WSA885X_VI_RATE_192000HZ 0x07 ++#define WSA885X_VI_RATE_384000HZ 0x08 ++ ++/* Channel Configuration Masks */ ++#define WSA885X_CHANNEL_STEREO 0x03 ++#define WSA885X_CHANNEL_MONO_LEFT 0x01 ++#define WSA885X_CHANNEL_MONO_RIGHT 0x02 ++ ++#define WSA885X_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \ ++ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | \ ++ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) ++ ++#define WSA885X_PLL_LOCK_BIT BIT(0) ++ ++#define WSA885X_FU21_VOL_STEPS 124 ++#define WSA885X_USAGE_MODE_MAX 8 ++static const DECLARE_TLV_DB_SCALE(wsa885x_fu21_digital_gain, -8400, 100, 0); ++ ++static bool wsa885x_is_valid_rx_slot_mask(u32 mask) ++{ ++ return mask == WSA885X_CHANNEL_MONO_LEFT || ++ mask == WSA885X_CHANNEL_MONO_RIGHT || ++ mask == WSA885X_CHANNEL_STEREO; ++} ++ ++static const char *const wsa885x_supply_name[] = { ++ "vdd-1p8", ++ "vdd-io", ++}; ++ ++enum { ++ WSA885X_BATT_1S = 1, ++ WSA885X_BATT_2S, ++}; ++ ++enum { ++ WSA885X_IRQ_INT_SAF2WAR = 0, ++ WSA885X_IRQ_INT_WAR2SAF, ++ WSA885X_IRQ_INT_DISABLE, ++ WSA885X_IRQ_INT_PA0_OCP, ++ WSA885X_IRQ_INT_PA1_OCP, ++ WSA885X_IRQ_INT_CLIP0, ++ WSA885X_IRQ_INT_CLIP1, ++ WSA885X_IRQ_INT_CLK_WD, ++ WSA885X_IRQ_INT_INTR_GPIO1_PIN, ++ WSA885X_IRQ_INT_INTR_GPIO2_PIN, ++ WSA885X_IRQ_INT_UVLO, ++ WSA885X_IRQ_INT_BOP, ++ WSA885X_IRQ_INT_PA0_FSM_ERR, ++ WSA885X_IRQ_INT_PA1_FSM_ERR, ++ WSA885X_IRQ_INT_MAIN_FSM_ERR, ++ WSA885X_IRQ_INT_PCM_DATA0_WD, ++ WSA885X_IRQ_INT_PCM_DATA1_WD, ++ WSA885X_IRQ_INT_PCM_DATA0_DC, ++ WSA885X_IRQ_INT_PCM_DATA1_DC, ++ WSA885X_IRQ_INT_PLL_UNLOCKED, ++ WSA885X_IRQ_INT_PROT_MODE_CHANGE, ++ WSA885X_IRQ_INT_PB_CLOCK_VALID, ++ WSA885X_IRQ_INT_SENSE_CLOCK_VALID, ++ WSA885X_IRQ_MAX, ++}; ++ ++struct wsa885x_priv { ++ struct i2c_client *client; ++ struct regmap *regmap; ++ struct device *dev; ++ struct snd_soc_component *component; ++ struct gpio_desc *sd_n; ++ struct reset_control *sd_reset; ++ u32 usage_mode; ++ u32 rx_slot_mask; ++ u32 batt_conf; ++ int stereo_vol_db; ++ struct mutex state_lock; /* protects mutable control state */ ++}; ++ ++struct wsa885x_reg_update { ++ unsigned int reg; ++ unsigned int mask; ++ unsigned int val; ++}; ++ ++static const struct regmap_range_cfg wsa885x_regmap_ranges[] = { ++ { ++ .range_min = 0, ++ .range_max = 0x88ff, ++ .selector_reg = 0x0, ++ .selector_mask = 0xFF, ++ .selector_shift = 0, ++ .window_start = 0, ++ .window_len = 0x100, ++ }, ++}; ++ ++static const struct reg_default wsa885x_codec_reg_defaults[] = { ++ {WSA885X_SMP_AMP_CTRL_STEREO_STEREO_SMP_AMP_CTRL_I2S, 0x00}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_IT21_CLUSERINDEX, 0x01}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_CMT_GRP_MASK, 0x00}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_OT23_USAGE, 0x00}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID, 0x00}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_CS21_SAMPLERATEINDEX, 0x04}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_PPU21_POSTURENUMBER, 0x01}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0, 0x01}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1, 0x01}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_MSB, 0xac}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB, 0x00}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_MSB, 0xac}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB, 0x00}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS, 0x03}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS, 0x03}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_CS24_SAMPLERATEINDEX, 0x03}, ++ {WSA885X_ANA_TOP_PON_CKSK_CTL_0, 0x00}, ++ {WSA885X_ANA_TOP_BG_TVP_UVLO1_PROG, 0x19}, ++ {WSA885X_ANA_TOP_BG_TVP_UVLO2_PROG, 0x22}, ++ {WSA885X_ANA_PLL_DIV_CTL_0, 0x0c}, ++ {WSA885X_ANA_PLL_DIV_CTL_1, 0x50}, ++ {WSA885X_ANA_TOP_PLL_VCO_CTL, 0x00}, ++ {WSA885X_ANA_TOP_PLL_LOOPFILT_0, 0xb4}, ++ {WSA885X_ANA_TOP_PLL_OVRD_CTL, 0x00}, ++ {WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x00}, ++ {WSA885X_ANA_TOP_BOOST_STB_CTRL2, 0x03}, ++ {WSA885X_ANA_TOP_BOOST_STB_CTRL3, 0x3c}, ++ {WSA885X_ANA_TOP_BOOST_BYP_CTRL2, 0xc5}, ++ {WSA885X_ANA_TOP_BOOST_BYP_CTRL3, 0x13}, ++ {WSA885X_ANA_TOP_BOOST_MISC, 0x79}, ++ {WSA885X_ANA_TOP_SPK_TOP_SPARE3, 0x00}, ++ {WSA885X_SPK_TOP_COMMON_CTRL2, 0x08}, ++ {WSA885X_SPK_TOP_LF_CH1_CTRL11, 0x09}, ++ {WSA885X_SPK_TOP_LF_CH1_TUNE1, 0x00}, ++ {WSA885X_SPK_TOP_LF_CH2_TUNE1, 0x00}, ++ {WSA885X_SPK_TOP_LF_CH1_CTRL9, 0x00}, ++ {WSA885X_SPK_TOP_LF_CH2_CTRL9, 0x00}, ++ {WSA885X_SPK_TOP_LF_CH2_CTRL11, 0x09}, ++ {WSA885X_SPK_TOP_COMMON_TUNE1, 0x03}, ++ {WSA885X_IVSENSE_VSNS_ISNS_CTL_CH1, 0x00}, ++ {WSA885X_DIG_CTRL0_CDC_CLK_CTL, 0x0e}, ++ {WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL2, 0x40}, ++ {WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL4, 0xff}, ++ {WSA885X_ANA_TOP_PLL_STATUS_0, 0x00}, ++ {WSA885X_ANA_TOP_PLL_STATUS_1, 0x00}, ++ {WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL2, 0x84}, ++ {WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3, 0x02}, ++ {WSA885X_ANA_TOP_IVSENSE_ADC_REF_CTL, 0x00}, ++ {WSA885X_ANA_TOP_IVSENSE_ADC_CDAC_CAL_CTL2, 0xe0}, ++ {WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_CTRL3, 0xa4}, ++ {WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_TUNE3, 0xc9}, ++ {WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_CTRL3, 0xa4}, ++ {WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_TUNE3, 0xc9}, ++ {WSA885X_DIG_CTRL0_TOP_CLK_CFG, 0x00}, ++ {WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x00}, ++ {WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE, 0x00}, ++ {WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x00}, ++ {WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00}, ++ {WSA885X_DIG_CTRL0_POWER_FSM_CTL0, 0x05}, ++ {WSA885X_DIG_CTRL0_POWER_FSM_CTL1, 0x00}, ++ {WSA885X_DIG_CTRL0_PA0_FSM_CTL1, 0x45}, ++ {WSA885X_DIG_CTRL0_PA1_FSM_CTL1, 0x45}, ++ {WSA885X_DIG_CTRL0_VBAT_THRM_FLT_CTL, 0x7f}, ++ {WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x00}, ++ {WSA885X_DIG_CTRL0_GAIN_RAMP0_CTL1, 0x01}, ++ {WSA885X_DIG_CTRL0_GAIN_RAMP1_CTL1, 0x01}, ++ {WSA885X_DIG_CTRL1_I2S_CTL0, 0x06}, ++ {WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, 0x00}, ++ {WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX, 0x00}, ++ {WSA885X_DIG_CTRL1_I2S_TDM_CTL0, 0x00}, ++ {WSA885X_DIG_CTRL1_I2S_TDM_CTL1, 0x05}, ++ {WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, 0x00}, ++ {WSA885X_DIG_CTRL1_I2S_RESET_CTL, 0x00}, ++ {WSA885X_DIG_CTRL1_I2S_TDM_CH_RX, WSA885X_I2S_TDM_CH_RX_CH3_EN}, ++ {WSA885X_CDC_RX0_RX_PATH_CFG0, 0x89}, ++ {WSA885X_CDC_RX0_RX_PATH_CFG1, 0x64}, ++ {WSA885X_CDC_RX0_RX_PATH_CTL, 0x24}, ++ {WSA885X_RX0_RX_PATH_DSMDEM_CTL, 0x01}, ++ {WSA885X_CDC_RX1_RX_PATH_CFG0, 0x89}, ++ {WSA885X_CDC_RX1_RX_PATH_CFG1, 0x64}, ++ {WSA885X_CDC_RX1_RX_PATH_CTL, 0x04}, ++ {WSA885X_RX1_RX_PATH_DSMDEM_CTL, 0x01}, ++ {WSA885X_CDC_COMPANDER0_CTL0, 0x01}, ++ {WSA885X_CDC_COMPANDER0_CTL7, 0x2a}, ++ {WSA885X_CDC_COMPANDER1_CTL0, 0x01}, ++ {WSA885X_CDC_COMPANDER1_CTL7, 0x2a}, ++ {WSA885X_CDC_VSENSE0_SPKR_PROT_PATH_CTL, 0x14}, ++ {WSA885X_CDC_VSENSE1_SPKR_PROT_PATH_CTL, 0x14}, ++ {WSA885X_CDC_ISENSE0_SPKR_PROT_PATH_CTL, 0x14}, ++ {WSA885X_CDC_ISENSE1_SPKR_PROT_PATH_CTL, 0x14}, ++ {WSA885X_CDC_CLSH_V1P8_BP_CTL1, 0x50}, ++ {WSA885X_CDC_CLSH_V1P8_BP_CTL0, 0x6c}, ++ {WSA885X_CDC_CLSH_CLSH_SIG_DP_CTL0, 0x0d}, ++ {WSA885X_CDC_CLSH_CLSH_V_HD_PA, 0x03}, ++ {WSA885X_CDC_CLSH_V1P8_BP_CTL2, 0x05}, ++}; ++ ++static void wsa885x_multi_update_bits(struct regmap *regmap, ++ const struct wsa885x_reg_update *updates, ++ size_t num_updates) ++{ ++ size_t i; ++ ++ for (i = 0; i < num_updates; i++) ++ regmap_update_bits(regmap, updates[i].reg, ++ updates[i].mask, updates[i].val); ++} ++ ++static void wsa885x_toggle_irq_bit(struct wsa885x_priv *wsa885x, ++ unsigned int reg, unsigned int mask) ++{ ++ regmap_update_bits(wsa885x->regmap, reg, mask, 0); ++ regmap_update_bits(wsa885x->regmap, reg, mask, mask); ++} ++ ++static void wsa885x_pulse_irq_bit(struct wsa885x_priv *wsa885x, ++ unsigned int reg, unsigned int mask) ++{ ++ regmap_update_bits(wsa885x->regmap, reg, mask, 0); ++ regmap_update_bits(wsa885x->regmap, reg, mask, mask); ++ regmap_update_bits(wsa885x->regmap, reg, mask, 0); ++} ++ ++static int wsa885x_tdm_ctl0_slot_num_val(int slots, unsigned int *slot_num_val) ++{ ++ if (!slot_num_val) ++ return -EINVAL; ++ ++ switch (slots) { ++ case 2: ++ *slot_num_val = WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_2; ++ return 0; ++ case 4: ++ *slot_num_val = WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_4; ++ return 0; ++ case 8: ++ *slot_num_val = WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_8; ++ return 0; ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int wsa885x_reg_update_sequence(struct regmap *regmap, int slots) ++{ ++ static const struct reg_sequence regs[] = { ++ { WSA885X_DIG_CTRL1_I2S_TDM_CTL1, 0x15 }, ++ { WSA885X_DIG_CTRL1_I2S_TDM_CTL1, 0x11 }, ++ }; ++ unsigned int slot_num_val; ++ int ret; ++ ++ if (!regmap) ++ return -EINVAL; ++ ++ ret = wsa885x_tdm_ctl0_slot_num_val(slots, &slot_num_val); ++ if (ret) ++ return ret; ++ ++ regmap_multi_reg_write(regmap, regs, ARRAY_SIZE(regs)); ++ ++ regmap_update_bits(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CTL0, ++ WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, ++ slot_num_val); ++ regmap_update_bits(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CTL0, ++ WSA885X_I2S_TDM_CTL0_I2S_TDM_EN_MASK, ++ WSA885X_I2S_TDM_CTL0_I2S_TDM_EN_MASK); ++ regmap_write(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, ++ WSA885X_I2S_TDM_CH_TX_CH0_EN); ++ regmap_update_bits(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, ++ WSA885X_I2S_TDM_CH_TX_CH1_EN, ++ WSA885X_I2S_TDM_CH_TX_CH1_EN); ++ ++ return 0; ++} ++ ++static int wsa885x_wait_for_pll_lock(struct wsa885x_priv *wsa885x) ++{ ++ unsigned int status = 0; ++ int cnt = 0, ret = 0; ++ ++ do { ++ usleep_range(1000, 1100); ++ ret = regmap_read(wsa885x->regmap, WSA885X_ANA_TOP_PLL_STATUS_0, &status); ++ if (ret) { ++ dev_err(wsa885x->dev, "PLL status read failed: %d\n", ret); ++ return ret; ++ } ++ ++ if (status & WSA885X_PLL_LOCK_BIT) ++ return 0; ++ } while (++cnt < 20); ++ ++ dev_warn(wsa885x->dev, "PLL lock timeout after 20ms, status=0x%x\n", status); ++ return -ETIMEDOUT; ++} ++ ++static int wsa885x_2s_conf(struct wsa885x_priv *wsa885x) ++{ ++ static const struct reg_sequence regs[] = { ++ { WSA885X_SPK_TOP_COMMON_TUNE1, 0x26 }, ++ { WSA885X_SPK_TOP_LF_CH1_CTRL11, 0x0d }, ++ { WSA885X_SPK_TOP_LF_CH2_CTRL11, 0x0d }, ++ { WSA885X_CDC_CLSH_V1P8_BP_CTL1, 0x71 }, ++ { WSA885X_CDC_CLSH_V1P8_BP_CTL0, 0xAA }, ++ }; ++ ++ return regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs)); ++} ++ ++static const struct reg_sequence wsa885x_reg_init[] = { ++ { WSA885X_CDC_RX0_RX_PATH_CTL, 0x24 }, ++ { WSA885X_CDC_RX1_RX_PATH_CTL, 0x24 }, ++ { WSA885X_RX0_RX_PATH_DSMDEM_CTL, 0x01 }, ++ { WSA885X_RX1_RX_PATH_DSMDEM_CTL, 0x01 }, ++ { WSA885X_CDC_COMPANDER0_CTL0, 0x01 }, ++ { WSA885X_CDC_COMPANDER1_CTL0, 0x01 }, ++ { WSA885X_CDC_VSENSE0_SPKR_PROT_PATH_CTL, 0x14 }, ++ { WSA885X_CDC_VSENSE1_SPKR_PROT_PATH_CTL, 0x14 }, ++ { WSA885X_CDC_ISENSE0_SPKR_PROT_PATH_CTL, 0x14 }, ++ { WSA885X_CDC_ISENSE1_SPKR_PROT_PATH_CTL, 0x14 }, ++ { WSA885X_DIG_CTRL0_CDC_CLK_CTL, 0x0f }, ++ { WSA885X_DIG_CTRL0_CDC_CLK_CTL, 0x4f }, ++ { WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x02 }, ++ { WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x00 }, ++ { WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x01 }, ++ { WSA885X_SMP_AMP_CTRL_STEREO_CMT_GRP_MASK, 0x01 }, ++ { WSA885X_CDC_RX0_RX_PATH_CFG1, 0x60 }, ++ { WSA885X_CDC_RX1_RX_PATH_CFG1, 0x60 }, ++ { WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_CTRL3, 0xa5 }, ++ { WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_CTRL3, 0xa5 }, ++ { WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL2, 0x85 }, ++ { WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3, 0x0c }, ++ { WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3, 0x0e }, ++ { WSA885X_ANA_TOP_IVSENSE_ADC_REF_CTL, 0x0c }, ++ { WSA885X_DIG_CTRL0_GAIN_RAMP0_CTL1, 0x01 }, ++ { WSA885X_DIG_CTRL0_GAIN_RAMP1_CTL1, 0x01 }, ++ { WSA885X_CDC_RX0_RX_PATH_CFG0, 0x88 }, ++ { WSA885X_CDC_RX0_RX_PATH_CFG0, 0x89 }, ++ { WSA885X_CDC_RX1_RX_PATH_CFG0, 0x88 }, ++ { WSA885X_CDC_RX1_RX_PATH_CFG0, 0x89 }, ++ { WSA885X_ANA_TOP_BOOST_STB_CTRL2, 0x82 }, ++ { WSA885X_ANA_TOP_BOOST_STB_CTRL3, 0x34 }, ++ { WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL2, 0x41 }, ++ { WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL4, 0x7f }, ++ { WSA885X_CDC_CLSH_V1P8_BP_CTL1, 0x50 }, ++ { WSA885X_CDC_CLSH_V1P8_BP_CTL0, 0x6c }, ++ { WSA885X_CDC_CLSH_CLSH_SIG_DP_CTL0, 0x0d }, ++ { WSA885X_CDC_CLSH_CLSH_V_HD_PA, 0x03 }, ++ { WSA885X_DIG_CTRL0_POWER_FSM_CTL0, 0x05 }, ++ { WSA885X_ANA_TOP_PON_CKSK_CTL_0, 0x20 }, ++ { WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_TUNE3, 0x45 }, ++ { WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_TUNE3, 0x45 }, ++ { WSA885X_CDC_CLSH_V1P8_BP_CTL2, 0x05 }, ++ { WSA885X_ANA_TOP_BG_TVP_UVLO1_PROG, 0x35 }, ++ { WSA885X_ANA_TOP_BG_TVP_UVLO2_PROG, 0x21 }, ++ { WSA885X_ANA_TOP_BOOST_BYP_CTRL2, 0xc7 }, ++ { WSA885X_ANA_TOP_BOOST_BYP_CTRL3, 0x11 }, ++ { WSA885X_ANA_TOP_IVSENSE_ADC_CDAC_CAL_CTL2, 0x80 }, ++ { WSA885X_ANA_TOP_SPK_TOP_SPARE3, 0x08 }, ++ { WSA885X_DIG_CTRL0_PA0_FSM_CTL1, 0x47 }, ++ { WSA885X_DIG_CTRL0_PA1_FSM_CTL1, 0x47 }, ++ { WSA885X_CDC_COMPANDER0_CTL7, 0x34 }, ++ { WSA885X_CDC_COMPANDER1_CTL7, 0x34 }, ++ { WSA885X_DIG_CTRL0_VBAT_THRM_FLT_CTL, 0x79 }, ++}; ++ ++static int wsa885x_hw_init(struct wsa885x_priv *wsa885x) ++{ ++ static const struct reg_sequence regs[] = { ++ { WSA885X_DIG_CTRL1_SPMI_PAD_GPIO2_CTL, 0x2e }, ++ { WSA885X_DIG_CTRL1_INTR_MODE, 0x01 }, ++ { WSA885X_DIG_CTRL1_PIN_CT, 0x04 }, ++ }; ++ int ret; ++ ++ ret = regmap_multi_reg_write(wsa885x->regmap, wsa885x_reg_init, ++ ARRAY_SIZE(wsa885x_reg_init)); ++ if (ret) ++ return ret; ++ ++ if (wsa885x->batt_conf == WSA885X_BATT_2S) { ++ ret = wsa885x_2s_conf(wsa885x); ++ if (ret) ++ return ret; ++ } ++ ++ return regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs)); ++} ++ ++static int wsa885x_unmask_interrupts(struct wsa885x_priv *wsa885x) ++{ ++ static const struct reg_sequence regs[] = { ++ { WSA885X_INTR_MASK0, 0x00 }, ++ { WSA885X_INTR_MASK0 + 1, 0x00 }, ++ { WSA885X_INTR_MASK0 + 2, 0xf8 }, ++ }; ++ ++ return regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs)); ++} ++ ++static int wsa885x_wait_for_pde_state(struct wsa885x_priv *wsa885x, int ps) ++{ ++ unsigned int act_ps = 0, clock_valid = 0; ++ int rc = 0, cnt = 0; ++ ++ if (ps < 0 || ps > 3) ++ return -EINVAL; ++ ++ do { ++ usleep_range(1000, 1500); ++ rc = regmap_read(wsa885x->regmap, ++ WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS, ++ &act_ps); ++ if (rc) { ++ dev_err(wsa885x->dev, "PDE state read failed: %d\n", rc); ++ return rc; ++ } ++ if (act_ps == ps) ++ return 0; ++ } while (++cnt < 5); ++ ++ if (regmap_read(wsa885x->regmap, ++ WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID, ++ &clock_valid)) ++ dev_err(wsa885x->dev, ++ "PDE power state %d request failed, actual_ps %d, clock_valid read failed\n", ++ ps, act_ps); ++ else ++ dev_err(wsa885x->dev, ++ "PDE power state %d request failed, actual_ps %d, clock_valid:%d\n", ++ ps, act_ps, clock_valid); ++ ++ return -ETIMEDOUT; ++} ++ ++static void wsa885x_program_stereo_volume(struct wsa885x_priv *wsa885x, ++ int stereo_vol_db, bool commit) ++{ ++ regmap_write(wsa885x->regmap, ++ WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_MSB, ++ (u8)(s8)stereo_vol_db); ++ regmap_write(wsa885x->regmap, ++ WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB, 0x00); ++ regmap_write(wsa885x->regmap, ++ WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_MSB, ++ (u8)(s8)stereo_vol_db); ++ regmap_write(wsa885x->regmap, ++ WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB, 0x00); ++ ++ if (commit) ++ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01); ++} ++ ++static int wsa885x_codec_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ struct wsa885x_priv *wsa885x; ++ u8 pcm_rate, cs21_sample_rate_idx, cs24_sample_rate_idx; ++ ++ wsa885x = snd_soc_component_get_drvdata(dai->component); ++ ++ switch (params_rate(params)) { ++ case 8000: ++ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_8KHZ; ++ cs21_sample_rate_idx = WSA885X_RX_RATE_8000HZ; ++ cs24_sample_rate_idx = WSA885X_VI_RATE_8000HZ; ++ break; ++ case 16000: ++ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_16KHZ; ++ cs21_sample_rate_idx = WSA885X_RX_RATE_16000HZ; ++ cs24_sample_rate_idx = WSA885X_VI_RATE_16000HZ; ++ break; ++ case 32000: ++ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_32KHZ; ++ cs21_sample_rate_idx = WSA885X_RX_RATE_32000HZ; ++ cs24_sample_rate_idx = WSA885X_VI_RATE_48000HZ; ++ break; ++ case 44100: ++ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_48_OR_44KHZ; ++ cs21_sample_rate_idx = WSA885X_RX_RATE_44100HZ; ++ cs24_sample_rate_idx = WSA885X_VI_RATE_44100HZ; ++ break; ++ case 48000: ++ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_48_OR_44KHZ; ++ cs21_sample_rate_idx = WSA885X_RX_RATE_48000HZ; ++ cs24_sample_rate_idx = WSA885X_VI_RATE_48000HZ; ++ break; ++ case 88200: ++ case 96000: ++ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_96_OR_88KHZ; ++ cs21_sample_rate_idx = WSA885X_RX_RATE_96000HZ; ++ cs24_sample_rate_idx = WSA885X_VI_RATE_96000HZ; ++ break; ++ case 176400: ++ case 192000: ++ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_192_OR_176KHZ; ++ cs21_sample_rate_idx = WSA885X_RX_RATE_192000HZ; ++ cs24_sample_rate_idx = WSA885X_VI_RATE_192000HZ; ++ break; ++ case 352800: ++ case 384000: ++ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_384_OR_352KHZ; ++ cs21_sample_rate_idx = WSA885X_RX_RATE_384000HZ; ++ cs24_sample_rate_idx = WSA885X_VI_RATE_384000HZ; ++ break; ++ default: ++ dev_err(wsa885x->dev, "sampling rate %d is not supported\n", params_rate(params)); ++ return -EINVAL; ++ } ++ ++ regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_CTL0, ++ WSA885X_I2S_CTL0_PCM_RATE_MASK | ++ WSA885X_I2S_CTL0_ENABLE_MASK, ++ WSA885X_I2S_CTL0_PCM_RATE(pcm_rate) | ++ WSA885X_I2S_CTL0_ENABLE_MASK); ++ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_RESET_CTL, 0x00); ++ regmap_write(wsa885x->regmap, WSA885X_SMP_AMP_CTRL_STEREO_CS21_SAMPLERATEINDEX, ++ cs21_sample_rate_idx); ++ regmap_write(wsa885x->regmap, WSA885X_SMP_AMP_CTRL_STEREO_CS24_SAMPLERATEINDEX, ++ cs24_sample_rate_idx); ++ ++ mutex_lock(&wsa885x->state_lock); ++ wsa885x_program_stereo_volume(wsa885x, wsa885x->stereo_vol_db, false); ++ mutex_unlock(&wsa885x->state_lock); ++ ++ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01); ++ ++ return 0; ++} ++ ++static int wsa885x_codec_set_tdm_slot(struct snd_soc_dai *dai, ++ unsigned int tx_slot_mask, ++ unsigned int rx_slot_mask, int slots, ++ int slot_width) ++{ ++ static const struct wsa885x_reg_update stereo_updates[] = { ++ { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK, ++ WSA885X_I2S_CFG0_TDM_TX_SLOT0(WSA885X_I2S_TX_SLOT_ISENSE0) }, ++ { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK, ++ WSA885X_I2S_CFG0_TDM_TX_SLOT1(WSA885X_I2S_TX_SLOT_ISENSE1) }, ++ { WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX, WSA885X_I2S_CFG1_TDM_TX_SLOT2_MASK, ++ WSA885X_I2S_CFG1_TDM_TX_SLOT2(WSA885X_I2S_TX_SLOT_CUR_SENSE0) }, ++ { WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX, WSA885X_I2S_CFG1_TDM_TX_SLOT3_MASK, ++ WSA885X_I2S_CFG1_TDM_TX_SLOT3(WSA885X_I2S_TX_SLOT_CUR_SENSE1) }, ++ }; ++ static const struct wsa885x_reg_update mono_left_updates[] = { ++ { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK, ++ WSA885X_I2S_CFG0_TDM_TX_SLOT0(WSA885X_I2S_TX_SLOT_ISENSE0) }, ++ { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK, ++ WSA885X_I2S_CFG0_TDM_TX_SLOT1(WSA885X_I2S_TX_SLOT_CUR_SENSE0) }, ++ }; ++ static const struct wsa885x_reg_update mono_right_updates[] = { ++ { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK, ++ WSA885X_I2S_CFG0_TDM_TX_SLOT0(WSA885X_I2S_TX_SLOT_ISENSE1) }, ++ { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK, ++ WSA885X_I2S_CFG0_TDM_TX_SLOT1(WSA885X_I2S_TX_SLOT_CUR_SENSE1) }, ++ }; ++ struct wsa885x_priv *wsa885x; ++ unsigned int slot_num_val; ++ u32 mask; ++ int ret; ++ ++ wsa885x = snd_soc_component_get_drvdata(dai->component); ++ ++ ret = wsa885x_tdm_ctl0_slot_num_val(slots, &slot_num_val); ++ if (ret) { ++ dev_err(wsa885x->dev, "%s: unsupported slot count %d\n", ++ __func__, slots); ++ return ret; ++ } ++ ++ if (rx_slot_mask && !wsa885x_is_valid_rx_slot_mask(rx_slot_mask)) { ++ dev_err(wsa885x->dev, ++ "%s: unsupported rx_slot_mask 0x%x\n", ++ __func__, rx_slot_mask); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&wsa885x->state_lock); ++ if (rx_slot_mask) ++ wsa885x->rx_slot_mask = rx_slot_mask; ++ else if (!wsa885x_is_valid_rx_slot_mask(wsa885x->rx_slot_mask)) ++ wsa885x->rx_slot_mask = WSA885X_CHANNEL_STEREO; ++ mask = wsa885x->rx_slot_mask; ++ ++ regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_RESET_CTL, ++ WSA885X_I2S_RESET_CTL_RESET_MASK, ++ WSA885X_I2S_RESET_CTL_RESET_MASK); ++ ++ if (mask == WSA885X_CHANNEL_STEREO) { ++ wsa885x_multi_update_bits(wsa885x->regmap, stereo_updates, ++ ARRAY_SIZE(stereo_updates)); ++ ret = wsa885x_reg_update_sequence(wsa885x->regmap, slots); ++ if (ret) ++ goto exit_unlock; ++ regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, ++ WSA885X_I2S_TDM_CH_TX_CH2_EN, ++ WSA885X_I2S_TDM_CH_TX_CH2_EN); ++ regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, ++ WSA885X_I2S_TDM_CH_TX_CH3_EN, ++ WSA885X_I2S_TDM_CH_TX_CH3_EN); ++ } else if (mask == WSA885X_CHANNEL_MONO_LEFT) { ++ wsa885x_multi_update_bits(wsa885x->regmap, mono_left_updates, ++ ARRAY_SIZE(mono_left_updates)); ++ ret = wsa885x_reg_update_sequence(wsa885x->regmap, slots); ++ if (ret) ++ goto exit_unlock; ++ } else if (mask == WSA885X_CHANNEL_MONO_RIGHT) { ++ wsa885x_multi_update_bits(wsa885x->regmap, mono_right_updates, ++ ARRAY_SIZE(mono_right_updates)); ++ ret = wsa885x_reg_update_sequence(wsa885x->regmap, slots); ++ if (ret) ++ goto exit_unlock; ++ } ++ ++ regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_CTL0, ++ WSA885X_I2S_CTL0_ENABLE_MASK, ++ WSA885X_I2S_CTL0_ENABLE_MASK); ++ regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_RESET_CTL, ++ WSA885X_I2S_RESET_CTL_RESET_MASK, 0); ++ ++ ret = 0; ++ ++exit_unlock: ++ mutex_unlock(&wsa885x->state_lock); ++ ++ return ret; ++} ++ ++static int wsa885x_codec_set_sysclk(struct snd_soc_dai *dai, int clk_id, ++ unsigned int freq, int dir) ++{ ++ static const struct reg_sequence pll_prep[] = { ++ { WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x03 }, ++ { WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x04 }, ++ { WSA885X_ANA_TOP_PLL_LOOPFILT_0, 0xB4 }, ++ { WSA885X_ANA_TOP_PLL_VCO_CTL, 0x00 }, ++ { WSA885X_ANA_TOP_PLL_OVRD_CTL, 0x00 }, ++ }; ++ static const struct reg_sequence pll_cleanup[] = { ++ { WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE, 0x00 }, ++ { WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x00 }, ++ { WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x00 }, ++ }; ++ struct wsa885x_priv *wsa885x; ++ u32 pll_div; ++ int ret = 0; ++ ++ wsa885x = snd_soc_component_get_drvdata(dai->component); ++ ++ if (!freq) ++ return -EINVAL; ++ if (WSA885X_CLK_RATE_FIXED % freq) ++ return -EINVAL; ++ pll_div = WSA885X_CLK_RATE_FIXED / freq; ++ if (pll_div > 0xff) ++ return -EINVAL; ++ ++ regmap_multi_reg_write(wsa885x->regmap, pll_prep, ARRAY_SIZE(pll_prep)); ++ regmap_write(wsa885x->regmap, WSA885X_ANA_PLL_DIV_CTL_0, pll_div); ++ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE, 0x02); ++ ++ ret = wsa885x_wait_for_pll_lock(wsa885x); ++ if (ret) { ++ dev_err(wsa885x->dev, "PLL lock failed, aborting sysclk configuration\n"); ++ regmap_multi_reg_write(wsa885x->regmap, pll_cleanup, ++ ARRAY_SIZE(pll_cleanup)); ++ return ret; ++ } ++ ++ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x00); ++ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_POWER_FSM_CTL1, 0x01); ++ regmap_write(wsa885x->regmap, WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x00); ++ ++ return 0; ++} ++ ++static int wsa885x_codec_mute_stream(struct snd_soc_dai *dai, int mute, int stream) ++{ ++ static const struct reg_sequence mute_regs[] = { ++ { WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00 }, ++ { WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS, 0x03 }, ++ }; ++ static const struct reg_sequence mute_commit_regs[] = { ++ { WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0, 0x01 }, ++ { WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1, 0x01 }, ++ { WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01 }, ++ }; ++ static const struct reg_sequence unmute_prep_head_regs[] = { ++ { WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00 }, ++ }; ++ static const struct reg_sequence unmute_prep_tail_regs[] = { ++ { WSA885X_SMP_AMP_CTRL_STEREO_IT21_CLUSERINDEX, 0x01 }, ++ { WSA885X_SMP_AMP_CTRL_STEREO_PPU21_POSTURENUMBER, 0x01 }, ++ }; ++ static const struct reg_sequence unmute_volume_regs[] = { ++ { WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB, 0x00 }, ++ { WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB, 0x00 }, ++ }; ++ static const struct reg_sequence unmute_commit_regs[] = { ++ { WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01 }, ++ { WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS, 0x00 }, ++ }; ++ static const struct reg_sequence unmute_finish_regs[] = { ++ { WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0, 0x00 }, ++ { WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1, 0x00 }, ++ { WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01 }, ++ }; ++ struct wsa885x_priv *wsa885x; ++ int ret = 0, ps0 = 0, ps3 = 3; ++ ++ wsa885x = snd_soc_component_get_drvdata(dai->component); ++ ++ if (stream != SNDRV_PCM_STREAM_PLAYBACK) ++ return 0; ++ ++ mutex_lock(&wsa885x->state_lock); ++ ++ if (wsa885x->usage_mode > WSA885X_USAGE_MODE_MAX) { ++ ret = -EINVAL; ++ goto exit_unlock; ++ } ++ ++ if (!wsa885x_is_valid_rx_slot_mask(wsa885x->rx_slot_mask)) ++ wsa885x->rx_slot_mask = WSA885X_CHANNEL_STEREO; ++ ++ if (mute) { ++ regmap_multi_reg_write(wsa885x->regmap, mute_regs, ++ ARRAY_SIZE(mute_regs)); ++ ret = wsa885x_wait_for_pde_state(wsa885x, ps3); ++ if (ret) { ++ dev_err(wsa885x->dev, ++ "PS3 transition failed: %d\n", ret); ++ } else { ++ regmap_multi_reg_write(wsa885x->regmap, mute_commit_regs, ++ ARRAY_SIZE(mute_commit_regs)); ++ } ++ } else { ++ regmap_multi_reg_write(wsa885x->regmap, unmute_prep_head_regs, ++ ARRAY_SIZE(unmute_prep_head_regs)); ++ regmap_write(wsa885x->regmap, WSA885X_SMP_AMP_CTRL_STEREO_OT23_USAGE, ++ wsa885x->usage_mode); ++ regmap_multi_reg_write(wsa885x->regmap, unmute_prep_tail_regs, ++ ARRAY_SIZE(unmute_prep_tail_regs)); ++ wsa885x_program_stereo_volume(wsa885x, wsa885x->stereo_vol_db, false); ++ regmap_multi_reg_write(wsa885x->regmap, unmute_volume_regs, ++ ARRAY_SIZE(unmute_volume_regs)); ++ regmap_multi_reg_write(wsa885x->regmap, unmute_commit_regs, ++ ARRAY_SIZE(unmute_commit_regs)); ++ ret = wsa885x_wait_for_pde_state(wsa885x, ps0); ++ if (ret) ++ goto exit_unlock; ++ ++ if (wsa885x->rx_slot_mask == WSA885X_CHANNEL_STEREO) { ++ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_RX, ++ WSA885X_I2S_TDM_CH_RX_CH0_EN | ++ WSA885X_I2S_TDM_CH_RX_CH3_EN); ++ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x03); ++ } else if (wsa885x->rx_slot_mask == WSA885X_CHANNEL_MONO_LEFT) { ++ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_RX, ++ WSA885X_I2S_TDM_CH_RX_CH3_EN); ++ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x01); ++ } else if (wsa885x->rx_slot_mask == WSA885X_CHANNEL_MONO_RIGHT) { ++ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_RX, ++ WSA885X_I2S_TDM_CH_RX_CH0_EN); ++ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x02); ++ } ++ ++ regmap_multi_reg_write(wsa885x->regmap, unmute_finish_regs, ++ ARRAY_SIZE(unmute_finish_regs)); ++ } ++ ++exit_unlock: ++ mutex_unlock(&wsa885x->state_lock); ++ ++ return ret; ++} ++ ++static int wsa885x_codec_hw_free(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ static const struct reg_sequence regs[] = { ++ { WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00 }, ++ }; ++ struct wsa885x_priv *wsa885x; ++ ++ wsa885x = snd_soc_component_get_drvdata(dai->component); ++ ++ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) ++ return 0; ++ ++ mutex_lock(&wsa885x->state_lock); ++ regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs)); ++ mutex_unlock(&wsa885x->state_lock); ++ ++ return 0; ++} ++ ++static const struct snd_soc_dai_ops wsa885x_dai_ops = { ++ .hw_params = wsa885x_codec_hw_params, ++ .set_tdm_slot = wsa885x_codec_set_tdm_slot, ++ .set_sysclk = wsa885x_codec_set_sysclk, ++ .mute_stream = wsa885x_codec_mute_stream, ++ .hw_free = wsa885x_codec_hw_free, ++}; ++ ++static struct snd_soc_dai_driver wsa885x_dai[] = { ++ { ++ .name = "wsa885x_dai_drv", ++ .playback = { ++ .stream_name = "WSA885X TDM Playback", ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = WSA885X_RATES, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | ++ SNDRV_PCM_FMTBIT_S32_LE, ++ }, ++ .ops = &wsa885x_dai_ops, ++ }, ++}; ++ ++static void wsa885x_reset_assert(void *data) ++{ ++ struct wsa885x_priv *wsa885x = data; ++ ++ if (wsa885x->sd_reset) ++ reset_control_assert(wsa885x->sd_reset); ++ else ++ gpiod_direction_output(wsa885x->sd_n, 1); ++} ++ ++static void wsa885x_reset_deassert(struct wsa885x_priv *wsa885x) ++{ ++ if (wsa885x->sd_reset) ++ reset_control_deassert(wsa885x->sd_reset); ++ else ++ gpiod_direction_output(wsa885x->sd_n, 0); ++} ++ ++static int wsa885x_get_reset(struct device *dev, struct wsa885x_priv *wsa885x) ++{ ++ wsa885x->sd_reset = devm_reset_control_get_optional_shared(dev, NULL); ++ if (IS_ERR(wsa885x->sd_reset)) ++ return dev_err_probe(dev, PTR_ERR(wsa885x->sd_reset), ++ "Failed to get reset\n"); ++ else if (wsa885x->sd_reset) ++ return 0; ++ ++ wsa885x->sd_n = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); ++ if (IS_ERR(wsa885x->sd_n)) ++ return dev_err_probe(dev, PTR_ERR(wsa885x->sd_n), ++ "Shutdown Control GPIO not found\n"); ++ ++ return 0; ++} ++ ++static bool wsa885x_volatile_register(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case WSA885X_ANA_TOP_PLL_STATUS_0: ++ case WSA885X_ANA_TOP_PLL_STATUS_1: ++ case WSA885X_DIG_CTRL0_SDCA_COMMIT: ++ case WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS: ++ case WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID: ++ case WSA885X_INTR_STATUS0: ++ case WSA885X_INTR_STATUS0 + 1: ++ case WSA885X_INTR_STATUS0 + 2: ++ case WSA885X_INTR_CLEAR0: ++ case WSA885X_INTR_CLEAR0 + 1: ++ case WSA885X_INTR_CLEAR0 + 2: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static bool wsa885x_readable_register(struct device *dev, unsigned int reg) ++{ ++ if (reg == WSA885X_INTR_CLEAR0 || ++ reg == WSA885X_INTR_CLEAR0 + 1 || ++ reg == WSA885X_INTR_CLEAR0 + 2) ++ return false; ++ return reg <= 0x88ff; ++} ++ ++static bool wsa885x_writeable_register(struct device *dev, unsigned int reg) ++{ ++ if (reg > 0x88ff) ++ return false; ++ ++ switch (reg) { ++ case WSA885X_ANA_TOP_PLL_STATUS_0: ++ case WSA885X_ANA_TOP_PLL_STATUS_1: ++ case WSA885X_INTR_STATUS0: ++ case WSA885X_INTR_STATUS0 + 1: ++ case WSA885X_INTR_STATUS0 + 2: ++ case WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS: ++ case WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID: ++ return false; ++ default: ++ return true; ++ } ++} ++ ++static const struct regmap_config wsa885x_regmap_cfg = { ++ .reg_bits = 8, ++ .val_bits = 8, ++ .max_register = 0x88FF, ++ .ranges = wsa885x_regmap_ranges, ++ .num_ranges = ARRAY_SIZE(wsa885x_regmap_ranges), ++ .reg_defaults = wsa885x_codec_reg_defaults, ++ .num_reg_defaults = ARRAY_SIZE(wsa885x_codec_reg_defaults), ++ .volatile_reg = wsa885x_volatile_register, ++ .writeable_reg = wsa885x_writeable_register, ++ .readable_reg = wsa885x_readable_register, ++ .cache_type = REGCACHE_MAPLE, ++ .use_single_read = true, ++ .use_single_write = true, ++}; ++ ++static int wsa885x_component_probe(struct snd_soc_component *component) ++{ ++ struct wsa885x_priv *wsa885x = ++ snd_soc_component_get_drvdata(component); ++ int ret; ++ ++ wsa885x->component = component; ++ snd_soc_component_init_regmap(component, wsa885x->regmap); ++ ++ ret = wsa885x_hw_init(wsa885x); ++ if (ret) ++ return ret; ++ ++ return wsa885x_unmask_interrupts(wsa885x); ++} ++ ++static int wsa885x_stereo_gain_offset_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); ++ int val; ++ ++ mutex_lock(&wsa885x->state_lock); ++ val = wsa885x->stereo_vol_db + 84; ++ mutex_unlock(&wsa885x->state_lock); ++ if (val < 0 || val > WSA885X_FU21_VOL_STEPS) ++ return -ERANGE; ++ ++ ucontrol->value.integer.value[0] = val; ++ return 0; ++} ++ ++static int wsa885x_stereo_gain_offset_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); ++ long val; ++ int stereo_vol_db; ++ ++ val = ucontrol->value.integer.value[0]; ++ ++ if (val < 0 || val > WSA885X_FU21_VOL_STEPS) { ++ dev_err(component->dev, "%s: Invalid range, Val: %ld\n", __func__, val); ++ return -EINVAL; ++ } ++ ++ stereo_vol_db = (int)val - 84; ++ ++ mutex_lock(&wsa885x->state_lock); ++ if (wsa885x->stereo_vol_db == stereo_vol_db) { ++ mutex_unlock(&wsa885x->state_lock); ++ return 0; ++ } ++ ++ wsa885x_program_stereo_volume(wsa885x, stereo_vol_db, true); ++ wsa885x->stereo_vol_db = stereo_vol_db; ++ mutex_unlock(&wsa885x->state_lock); ++ ++ return 1; ++} ++ ++static int wsa885x_usage_modes_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); ++ ++ mutex_lock(&wsa885x->state_lock); ++ if (wsa885x->usage_mode > WSA885X_USAGE_MODE_MAX) { ++ mutex_unlock(&wsa885x->state_lock); ++ return -ERANGE; ++ } ++ ++ ucontrol->value.integer.value[0] = wsa885x->usage_mode; ++ mutex_unlock(&wsa885x->state_lock); ++ ++ return 0; ++} ++ ++static int wsa885x_usage_modes_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); ++ u32 val = ucontrol->value.integer.value[0]; ++ ++ if (val > WSA885X_USAGE_MODE_MAX) ++ return -EINVAL; ++ ++ mutex_lock(&wsa885x->state_lock); ++ if (wsa885x->usage_mode == val) { ++ mutex_unlock(&wsa885x->state_lock); ++ return 0; ++ } ++ ++ wsa885x->usage_mode = val; ++ mutex_unlock(&wsa885x->state_lock); ++ ++ return 1; ++} ++ ++static int wsa885x_rx_slot_mask_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); ++ u32 mask; ++ ++ mutex_lock(&wsa885x->state_lock); ++ mask = wsa885x->rx_slot_mask; ++ mutex_unlock(&wsa885x->state_lock); ++ if (!wsa885x_is_valid_rx_slot_mask(mask)) ++ return -ERANGE; ++ ++ ucontrol->value.integer.value[0] = mask; ++ ++ return 0; ++} ++ ++static int wsa885x_rx_slot_mask_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); ++ u32 mask = ucontrol->value.integer.value[0]; ++ ++ if (!wsa885x_is_valid_rx_slot_mask(mask)) ++ return -EINVAL; ++ ++ mutex_lock(&wsa885x->state_lock); ++ if (wsa885x->rx_slot_mask == mask) { ++ mutex_unlock(&wsa885x->state_lock); ++ return 0; ++ } ++ ++ wsa885x->rx_slot_mask = mask; ++ mutex_unlock(&wsa885x->state_lock); ++ ++ return 1; ++} ++ ++static const struct snd_kcontrol_new wsa885x_snd_controls[] = { ++ SOC_SINGLE_EXT("Usage Mode", SND_SOC_NOPM, 0, WSA885X_USAGE_MODE_MAX, 0, ++ wsa885x_usage_modes_get, ++ wsa885x_usage_modes_put), ++ ++ SOC_SINGLE_EXT_TLV("Speaker Volume", SND_SOC_NOPM, ++ 0, WSA885X_FU21_VOL_STEPS, 0, ++ wsa885x_stereo_gain_offset_get, ++ wsa885x_stereo_gain_offset_put, ++ wsa885x_fu21_digital_gain), ++ ++ SOC_SINGLE_EXT("Rx Slot Mask", SND_SOC_NOPM, 0, 3, 0, ++ wsa885x_rx_slot_mask_get, ++ wsa885x_rx_slot_mask_put), ++}; ++ ++static const struct snd_soc_component_driver wsa885x_component = { ++ .name = "wsa885x", ++ .probe = wsa885x_component_probe, ++ .controls = wsa885x_snd_controls, ++ .num_controls = ARRAY_SIZE(wsa885x_snd_controls), ++}; ++ ++static irqreturn_t wsa885x_handle_irq(int irq_idx, void *data) ++{ ++ struct wsa885x_priv *wsa885x = data; ++ ++ if (irq_idx < 0 || irq_idx >= WSA885X_IRQ_MAX) ++ return IRQ_NONE; ++ ++ switch (irq_idx) { ++ case WSA885X_IRQ_INT_SAF2WAR: ++ case WSA885X_IRQ_INT_WAR2SAF: ++ case WSA885X_IRQ_INT_DISABLE: ++ case WSA885X_IRQ_INT_INTR_GPIO1_PIN: ++ case WSA885X_IRQ_INT_INTR_GPIO2_PIN: ++ case WSA885X_IRQ_INT_PA0_OCP: ++ case WSA885X_IRQ_INT_PA1_OCP: ++ case WSA885X_IRQ_INT_CLIP0: ++ case WSA885X_IRQ_INT_CLIP1: ++ case WSA885X_IRQ_INT_CLK_WD: ++ case WSA885X_IRQ_INT_BOP: ++ case WSA885X_IRQ_INT_UVLO: ++ case WSA885X_IRQ_INT_PCM_DATA0_DC: ++ case WSA885X_IRQ_INT_PCM_DATA1_DC: ++ case WSA885X_IRQ_INT_PLL_UNLOCKED: ++ case WSA885X_IRQ_INT_PROT_MODE_CHANGE: ++ case WSA885X_IRQ_INT_PB_CLOCK_VALID: ++ case WSA885X_IRQ_INT_SENSE_CLOCK_VALID: ++ break; ++ case WSA885X_IRQ_INT_PCM_DATA0_WD: ++ case WSA885X_IRQ_INT_PCM_DATA1_WD: ++ if (irq_idx == WSA885X_IRQ_INT_PCM_DATA0_WD) ++ wsa885x_toggle_irq_bit(wsa885x, WSA885X_DIG_CTRL0_PCM_DATA_WD0_CTL1, ++ WSA885X_PCM_DATA_WD_CTL1_PCM_DATA_WD_EN_MASK); ++ else ++ wsa885x_toggle_irq_bit(wsa885x, WSA885X_DIG_CTRL0_PCM_DATA_WD1_CTL1, ++ WSA885X_PCM_DATA_WD_CTL1_PCM_DATA_WD_EN_MASK); ++ break; ++ case WSA885X_IRQ_INT_PA0_FSM_ERR: ++ case WSA885X_IRQ_INT_PA1_FSM_ERR: ++ case WSA885X_IRQ_INT_MAIN_FSM_ERR: ++ if (irq_idx == WSA885X_IRQ_INT_MAIN_FSM_ERR) { ++ wsa885x_pulse_irq_bit(wsa885x, WSA885X_DIG_CTRL0_POWER_FSM_CTL0, ++ WSA885X_POWER_FSM_CTL0_CLEAR_ERROR_MASK); ++ } else if (irq_idx == WSA885X_IRQ_INT_PA0_FSM_ERR) { ++ wsa885x_pulse_irq_bit(wsa885x, WSA885X_PA0_FSM_CTL0, ++ WSA885X_PA_FSM_CTL0_CLEAR_ERROR_MASK); ++ } else if (irq_idx == WSA885X_IRQ_INT_PA1_FSM_ERR) { ++ wsa885x_pulse_irq_bit(wsa885x, WSA885X_PA1_FSM_CTL0, ++ WSA885X_PA_FSM_CTL0_CLEAR_ERROR_MASK); ++ } ++ break; ++ default: ++ break; ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t wsa885x_interrupt_handler(int irq, void *data) ++{ ++ static const unsigned int status_reg[WSA885X_NUM_REGS] = { ++ WSA885X_INTR_STATUS0, ++ WSA885X_INTR_STATUS0 + 1, ++ WSA885X_INTR_STATUS0 + 2, ++ }; ++ static const unsigned int clear_reg[WSA885X_NUM_REGS] = { ++ WSA885X_INTR_CLEAR0, ++ WSA885X_INTR_CLEAR0 + 1, ++ WSA885X_INTR_CLEAR0 + 2, ++ }; ++ unsigned int status[WSA885X_NUM_REGS] = { 0 }; ++ struct wsa885x_priv *wsa885x = data; ++ irqreturn_t handled = IRQ_NONE; ++ irqreturn_t irq_ret; ++ int i, bit, ret, irq_num; ++ ++ for (i = 0; i < WSA885X_NUM_REGS; i++) { ++ ret = regmap_read(wsa885x->regmap, status_reg[i], &status[i]); ++ if (ret) { ++ dev_err(wsa885x->dev, ++ "Failed to read status_reg[%d] (0x%x): %d\n", ++ i, status_reg[i], ret); ++ status[i] = 0; ++ continue; ++ } ++ } ++ ++ for (i = 0; i < WSA885X_NUM_REGS; i++) { ++ for (bit = 0; bit < 8; bit++) { ++ if (status[i] & BIT(bit)) { ++ irq_num = i * 8 + bit; ++ regmap_write(wsa885x->regmap, clear_reg[i], BIT(bit)); ++ regmap_write(wsa885x->regmap, clear_reg[i], 0); ++ if (irq_num >= WSA885X_IRQ_MAX) { ++ dev_warn_ratelimited(wsa885x->dev, ++ "Unexpected IRQ bit %d (reg %d)\n", ++ bit, i); ++ handled = IRQ_HANDLED; ++ continue; ++ } ++ irq_ret = wsa885x_handle_irq(irq_num, wsa885x); ++ if (irq_ret == IRQ_HANDLED) ++ handled = IRQ_HANDLED; ++ } ++ } ++ } ++ return handled; ++} ++ ++static int wsa885x_register_irq(struct wsa885x_priv *wsa885x) ++{ ++ if (!wsa885x->client->irq) ++ return dev_err_probe(wsa885x->dev, -EINVAL, ++ "IRQ is not configured\n"); ++ ++ return devm_request_threaded_irq(wsa885x->dev, wsa885x->client->irq, NULL, ++ wsa885x_interrupt_handler, ++ IRQF_ONESHOT, ++ dev_name(wsa885x->dev), wsa885x); ++} ++ ++static int wsa885x_probe(struct i2c_client *client) ++{ ++ struct wsa885x_priv *wsa885x; ++ const struct snd_soc_component_driver *component_driver = &wsa885x_component; ++ const char *battery_config; ++ unsigned int i; ++ int ret; ++ struct device *dev = &client->dev; ++ ++ wsa885x = devm_kzalloc(dev, sizeof(*wsa885x), GFP_KERNEL); ++ if (!wsa885x) ++ return -ENOMEM; ++ ++ wsa885x->client = client; ++ wsa885x->dev = dev; ++ wsa885x->stereo_vol_db = -84; ++ wsa885x->rx_slot_mask = WSA885X_CHANNEL_STEREO; ++ mutex_init(&wsa885x->state_lock); ++ wsa885x->regmap = devm_regmap_init_i2c(client, &wsa885x_regmap_cfg); ++ ++ if (IS_ERR(wsa885x->regmap)) ++ return PTR_ERR(wsa885x->regmap); ++ ++ ret = device_property_read_string(dev, "qcom,battery-config", ++ &battery_config); ++ if (ret) { ++ wsa885x->batt_conf = WSA885X_BATT_1S; ++ } else if (!strcmp(battery_config, "1s")) { ++ wsa885x->batt_conf = WSA885X_BATT_1S; ++ } else if (!strcmp(battery_config, "2s")) { ++ wsa885x->batt_conf = WSA885X_BATT_2S; ++ } else { ++ return dev_err_probe(dev, -EINVAL, ++ "Invalid battery config %s (expected 1s or 2s)\n", ++ battery_config); ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(wsa885x_supply_name); i++) { ++ ret = devm_regulator_get_enable(dev, wsa885x_supply_name[i]); ++ if (ret) ++ return dev_err_probe(dev, ret, ++ "Failed to enable regulator %s\n", ++ wsa885x_supply_name[i]); ++ } ++ ++ ret = wsa885x_get_reset(dev, wsa885x); ++ if (ret) ++ return ret; ++ ++ wsa885x_reset_deassert(wsa885x); ++ usleep_range(5000, 5500); ++ ++ ret = devm_add_action_or_reset(dev, wsa885x_reset_assert, wsa885x); ++ if (ret) ++ return dev_err_probe(dev, ret, "devm_add_action_or_reset failed\n"); ++ ++ i2c_set_clientdata(client, wsa885x); ++ ++ ret = wsa885x_register_irq(wsa885x); ++ if (ret) ++ return dev_err_probe(dev, ret, "wsa885x irq registration failed\n"); ++ ++ ret = devm_snd_soc_register_component(dev, component_driver, ++ wsa885x_dai, ++ ARRAY_SIZE(wsa885x_dai)); ++ if (ret) ++ return dev_err_probe(dev, ret, "Codec component registration failed\n"); ++ ++ return 0; ++} ++ ++static const struct of_device_id wsa885x_dt_match[] = { ++ { ++ .compatible = "qcom,wsa8855", ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, wsa885x_dt_match); ++ ++static const struct i2c_device_id wsa885x_id[] = { ++ { ++ .name = "wsa885x", ++ .driver_data = 0, ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(i2c, wsa885x_id); ++ ++static struct i2c_driver wsa885x_driver = { ++ .driver = { ++ .name = "wsa885x", ++ .of_match_table = wsa885x_dt_match, ++ }, ++ .probe = wsa885x_probe, ++ .id_table = wsa885x_id, ++}; ++ ++module_i2c_driver(wsa885x_driver); ++ ++MODULE_DESCRIPTION("ASoC WSA885X Stereo Smart PA Codec Driver"); ++MODULE_LICENSE("GPL"); +diff --git a/sound/soc/codecs/zl38060.c b/sound/soc/codecs/zl38060.c +--- a/sound/soc/codecs/zl38060.c ++++ b/sound/soc/codecs/zl38060.c +@@ -162,7 +162,7 @@ static int zl38_fw_send_xaddr(struct regmap *regmap, const void *data) + static int zl38_load_firmware(struct device *dev, struct regmap *regmap) + { + const struct ihex_binrec *rec; +- const struct firmware *fw; ++ const struct firmware *fw __free(firmware) = NULL; + u32 addr; + u16 len; + int err; +@@ -180,7 +180,7 @@ static int zl38_load_firmware(struct device *dev, struct regmap *regmap) + return err; + err = zl38_fw_enter_boot_mode(regmap); + if (err) +- goto out; ++ return err; + rec = (const struct ihex_binrec *)fw->data; + while (rec) { + addr = be32_to_cpu(rec->addr); +@@ -195,15 +195,12 @@ static int zl38_load_firmware(struct device *dev, struct regmap *regmap) + err = -EINVAL; + } + if (err) +- goto out; ++ return err; + /* next ! */ + rec = ihex_next_binrec(rec); + } +- err = zl38_fw_go(regmap); + +-out: +- release_firmware(fw); +- return err; ++ return zl38_fw_go(regmap); + } + + +diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c +--- a/sound/soc/dwc/dwc-i2s.c ++++ b/sound/soc/dwc/dwc-i2s.c +@@ -955,7 +955,9 @@ static int dw_i2s_probe(struct platform_device *pdev) + } + + irq = platform_get_irq_optional(pdev, 0); +- if (irq >= 0) { ++ if (irq == -EPROBE_DEFER) ++ return irq; ++ if (irq > 0) { + ret = devm_request_irq(&pdev->dev, irq, i2s_irq_handler, 0, + pdev->name, dev); + if (ret < 0) { +diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c +--- a/sound/soc/fsl/fsl-asoc-card.c ++++ b/sound/soc/fsl/fsl-asoc-card.c +@@ -69,7 +69,6 @@ static const struct snd_pcm_hw_constraint_list cs42888_channel_constraints = { + + /** + * struct codec_priv - CODEC private data +- * @mclk: Main clock of the CODEC + * @mclk_freq: Clock rate of MCLK + * @free_freq: Clock rate of MCLK for hw_free() + * @mclk_id: MCLK (or main clock) id for set_sysclk() +@@ -80,7 +79,6 @@ static const struct snd_pcm_hw_constraint_list cs42888_channel_constraints = { + * to stay within PLL frequency limits + */ + struct codec_priv { +- struct clk *mclk; + unsigned long mclk_freq; + unsigned long free_freq; + u32 mclk_id; +@@ -109,12 +107,58 @@ struct cpu_priv { + u32 slot_num; + }; + ++struct fsl_asoc_card_priv; ++ ++/* ++ * struct fsl_asoc_card_pdata - per-compatible static card description ++ * @sysclk_dir: initial CPU SYSCLK direction override (0 = leave default IN) ++ * @sysclk_ratio: SYSCLK ratio on sample rate (0 = not used) ++ * @slot_width: TDM slot width (0 = not TDM) ++ * @codec_dai_name: name of the codec DAI ++ * @codec_mclk_id: MCLK id passed to set_sysclk() for the codec ++ * @codec_fll_id: FLL id; only valid when has_pll is true ++ * @codec_pll_id: PLL id; only valid when has_pll is true ++ * @codec_pll_ratio_s24: PLL output ratio for S24_LE ++ * @has_pll: codec uses PLL/FLL; codec_fll_id and codec_pll_id are valid ++ * @dai_fmt: DAI format flags ++ * @playback_only: restrict card to playback direction ++ * @capture_only: restrict card to capture direction ++ * @dapm_routes: DAPM route table override ++ * @num_dapm_routes: number of entries in dapm_routes ++ * @exclude_format: PCM format bitmask excluded (for SAI + WM8960/WM8962) ++ * @codec_init: codec-specific init run after mclk_freq is populated ++ * @probe_init: optional DT-driven init run at end of probe() (e.g. SPDIF codec discovery) ++ */ ++struct fsl_asoc_card_pdata { ++ u32 sysclk_dir[2]; ++ u32 sysclk_ratio[2]; ++ u32 slot_width; ++ const char *codec_dai_name; ++ u32 codec_mclk_id; ++ int codec_fll_id; ++ int codec_pll_id; ++ int codec_pll_ratio_s24; ++ bool has_pll; ++ bool playback_only; ++ bool capture_only; ++ u32 dai_fmt; ++ const struct snd_soc_dapm_route *dapm_routes; ++ int num_dapm_routes; ++ u64 exclude_format; ++ int (*codec_init)(struct fsl_asoc_card_priv *priv); ++ int (*probe_init)(struct device_node *codec_np[], ++ struct device_node *cpu_np, ++ const char *codec_dai_name[], ++ struct fsl_asoc_card_priv *priv); ++}; ++ + /** + * struct fsl_asoc_card_priv - Freescale Generic ASOC card private data + * @dai_link: DAI link structure including normal one and DPCM link + * @hp_jack: Headphone Jack structure + * @mic_jack: Microphone Jack structure + * @pdev: platform device pointer ++ * @pdata: pointer to the per-compatible card platform data + * @codec_priv: CODEC private data + * @cpu_priv: CPU private data + * @card: ASoC card structure +@@ -135,6 +179,7 @@ struct fsl_asoc_card_priv { + struct simple_util_jack hp_jack; + struct simple_util_jack mic_jack; + struct platform_device *pdev; ++ const struct fsl_asoc_card_pdata *pdata; + struct codec_priv codec_priv[2]; + struct cpu_priv cpu_priv; + struct snd_soc_card card; +@@ -609,6 +654,192 @@ static int fsl_asoc_card_spdif_init(struct device_node *codec_np[], + return 0; + } + ++static int fsl_asoc_card_cs42888_codec_init(struct fsl_asoc_card_priv *priv) ++{ ++ unsigned long mclk_freq = priv->codec_priv[0].mclk_freq; ++ ++ /* ++ * Set CPU sysclk frequency from codec MCLK only if not already ++ * set by the CPU DAI init (e.g. ESAI extal clock takes precedence). ++ */ ++ if (!priv->cpu_priv.sysclk_freq[TX]) ++ priv->cpu_priv.sysclk_freq[TX] = mclk_freq; ++ if (!priv->cpu_priv.sysclk_freq[RX]) ++ priv->cpu_priv.sysclk_freq[RX] = mclk_freq; ++ ++ priv->constraint_channels = &cs42888_channel_constraints; ++ if (mclk_freq % 12288000 == 0) ++ priv->constraint_rates = &cs42888_rate_48k_constraints; ++ else if (mclk_freq % 11289600 == 0) ++ priv->constraint_rates = &cs42888_rate_44k_constraints; ++ else ++ dev_warn(&priv->pdev->dev, ++ "Unknown MCLK frequency %lu, no rate constraints\n", ++ mclk_freq); ++ ++ return 0; ++} ++ ++static int fsl_asoc_card_wm8958_codec_init(struct fsl_asoc_card_priv *priv) ++{ ++ priv->codec_priv[0].free_freq = priv->codec_priv[0].mclk_freq; ++ return 0; ++} ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_cs42888_pdata = { ++ .codec_dai_name = "cs42888", ++ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBC_CFC, ++ .sysclk_dir = { SND_SOC_CLOCK_OUT, SND_SOC_CLOCK_OUT }, ++ .slot_width = 32, ++ .dapm_routes = audio_map, ++ .num_dapm_routes = ARRAY_SIZE(audio_map), ++ .codec_init = fsl_asoc_card_cs42888_codec_init, ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_cs427x_pdata = { ++ .codec_dai_name = "cs4271-hifi", ++ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, ++ .codec_mclk_id = CS427x_SYSCLK_MCLK, ++ .dapm_routes = audio_map, ++ .num_dapm_routes = ARRAY_SIZE(audio_map), ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_sgtl5000_pdata = { ++ .codec_dai_name = "sgtl5000", ++ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, ++ .codec_mclk_id = SGTL5000_SYSCLK, ++ .dapm_routes = audio_map, ++ .num_dapm_routes = ARRAY_SIZE(audio_map), ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_tlv320aic32x4_pdata = { ++ .codec_dai_name = "tlv320aic32x4-hifi", ++ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, ++ .dapm_routes = audio_map, ++ .num_dapm_routes = ARRAY_SIZE(audio_map), ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_tlv320aic31xx_pdata = { ++ .codec_dai_name = "tlv320dac31xx-hifi", ++ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBC_CFC, ++ .sysclk_dir = { SND_SOC_CLOCK_OUT, SND_SOC_CLOCK_OUT }, ++ .playback_only = true, ++ .dapm_routes = audio_map_tx, ++ .num_dapm_routes = ARRAY_SIZE(audio_map_tx), ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_wm8962_pdata = { ++ .codec_dai_name = "wm8962", ++ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, ++ .codec_mclk_id = WM8962_SYSCLK_MCLK, ++ .has_pll = true, ++ .codec_fll_id = WM8962_SYSCLK_FLL, ++ .codec_pll_id = WM8962_FLL, ++ /* ++ * WM8962 has same BCLK generation limitations as WM8960. ++ * See WM8960 section for detailed explanation. ++ */ ++ .exclude_format = SNDRV_PCM_FMTBIT_S20_3LE, ++ .dapm_routes = audio_map, ++ .num_dapm_routes = ARRAY_SIZE(audio_map), ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_wm8960_pdata = { ++ .codec_dai_name = "wm8960-hifi", ++ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, ++ .has_pll = true, ++ .codec_fll_id = WM8960_SYSCLK_AUTO, ++ .codec_pll_id = WM8960_SYSCLK_AUTO, ++ /* ++ * WM8960 in master mode cannot generate exact 1.92 MHz BCLK ++ * required for S20_3LE (48kHz x 2ch x 20bit). Closest available ++ * is 2.048 MHz (SYSCLK/6), which causes right channel corruption. ++ * ++ * In SAI master mode, SAI derive BCLK from MCLK using integer ++ * dividers only. S20_3LE requires non-integer divider ratios ++ * with standard MCLK frequencies. For example, 48kHz stereo ++ * needs 1.920 MHz BCLK, which requires a divider of 6.4 from ++ * 12.288 MHz MCLK (not an integer). ++ */ ++ .exclude_format = SNDRV_PCM_FMTBIT_S20_3LE, ++ .dapm_routes = audio_map, ++ .num_dapm_routes = ARRAY_SIZE(audio_map), ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_ac97_pdata = { ++ .codec_dai_name = "ac97-hifi", ++ .dai_fmt = SND_SOC_DAIFMT_AC97, ++ .dapm_routes = audio_map_ac97, ++ .num_dapm_routes = ARRAY_SIZE(audio_map_ac97), ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_mqs_pdata = { ++ .codec_dai_name = "fsl-mqs-dai", ++ .dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBC_CFC | ++ SND_SOC_DAIFMT_NB_NF, ++ .playback_only = true, ++ .dapm_routes = audio_map_tx, ++ .num_dapm_routes = ARRAY_SIZE(audio_map_tx), ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_wm8524_pdata = { ++ .codec_dai_name = "wm8524-hifi", ++ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBC_CFC, ++ /* RX=0, TX=1: set TX (index 1) to CLOCK_OUT, RX stays at default IN */ ++ .sysclk_dir = { 0, SND_SOC_CLOCK_OUT }, ++ .sysclk_ratio = { 0, 256 }, ++ .slot_width = 32, ++ .playback_only = true, ++ .dapm_routes = audio_map_tx, ++ .num_dapm_routes = ARRAY_SIZE(audio_map_tx), ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_si476x_pdata = { ++ .codec_dai_name = "si476x-codec", ++ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBC_CFC, ++ .dapm_routes = audio_map_rx, ++ .num_dapm_routes = ARRAY_SIZE(audio_map_rx), ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_wm8958_pdata = { ++ .codec_dai_name = "wm8994-aif1", ++ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, ++ .codec_mclk_id = WM8994_FLL_SRC_MCLK1, ++ .has_pll = true, ++ .codec_fll_id = WM8994_SYSCLK_FLL1, ++ .codec_pll_id = WM8994_FLL1, ++ .codec_init = fsl_asoc_card_wm8958_codec_init, ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_nau8822_pdata = { ++ .codec_dai_name = "nau8822-hifi", ++ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, ++ .codec_mclk_id = NAU8822_CLK_MCLK, ++ .has_pll = true, ++ .codec_fll_id = NAU8822_CLK_PLL, ++ .codec_pll_id = NAU8822_CLK_PLL, ++ .dapm_routes = audio_map, ++ .num_dapm_routes = ARRAY_SIZE(audio_map), ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_wm8904_pdata = { ++ .codec_dai_name = "wm8904-hifi", ++ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, ++ .codec_mclk_id = WM8904_FLL_MCLK, ++ .has_pll = true, ++ .codec_fll_id = WM8904_CLK_FLL, ++ .codec_pll_id = WM8904_FLL_MCLK, ++ .codec_pll_ratio_s24 = 192, ++ .dapm_routes = audio_map, ++ .num_dapm_routes = ARRAY_SIZE(audio_map), ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_spdif_pdata = { ++ .codec_dai_name = "spdif", ++ .dai_fmt = DAI_FMT_BASE, ++ .probe_init = fsl_asoc_card_spdif_init, ++}; ++ + static int hp_jack_event(struct notifier_block *nb, unsigned long event, + void *data) + { +@@ -643,17 +874,173 @@ static struct notifier_block mic_jack_nb = { + .notifier_call = mic_jack_event, + }; + +-static int fsl_asoc_card_late_probe(struct snd_soc_card *card) ++/* ++ * fsl_asoc_card_init_cpu - configure CPU DAI-specific settings. ++ * ++ * Called from late_probe() when the CPU DAI component is guaranteed bound. ++ */ ++static int fsl_asoc_card_init_cpu(struct snd_soc_card *card, ++ struct snd_soc_pcm_runtime *rtd) + { + struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); +- struct snd_soc_pcm_runtime *rtd = list_first_entry( +- &card->rtd_list, struct snd_soc_pcm_runtime, list); ++ struct device_node *np = priv->pdev->dev.of_node; ++ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); ++ const char *comp_drv_name = cpu_dai->component->driver->name; ++ struct device *dev = card->dev; ++ int ret; ++ ++ if (!strcmp(comp_drv_name, "fsl-ssi")) { ++ /* Only SSI needs to configure AUDMUX */ ++ ret = fsl_asoc_card_audmux_init(np, priv); ++ if (ret) { ++ dev_err(dev, "failed to init audmux\n"); ++ return ret; ++ } ++ } else if (!strcmp(comp_drv_name, "fsl-esai")) { ++ struct clk *esai_clk = clk_get(cpu_dai->dev, "extal"); ++ ++ if (!IS_ERR(esai_clk)) { ++ priv->cpu_priv.sysclk_freq[TX] = clk_get_rate(esai_clk); ++ priv->cpu_priv.sysclk_freq[RX] = clk_get_rate(esai_clk); ++ clk_put(esai_clk); ++ } else { ++ dev_warn(dev, "failed to get ESAI extal clock: %ld\n", PTR_ERR(esai_clk)); ++ } ++ ++ priv->cpu_priv.sysclk_id[TX] = ESAI_HCKT_EXTAL; ++ priv->cpu_priv.sysclk_id[RX] = ESAI_HCKR_EXTAL; ++ } else if (!strcmp(comp_drv_name, "fsl-sai")) { ++ priv->cpu_priv.sysclk_id[TX] = FSL_SAI_CLK_MAST1; ++ priv->cpu_priv.sysclk_id[RX] = FSL_SAI_CLK_MAST1; ++ ++ if (priv->pdata->exclude_format) ++ priv->exclude_format = priv->pdata->exclude_format; ++ } ++ ++ return 0; ++} ++ ++/* ++ * fsl_asoc_card_init_codecs - read codec MCLK rates and set codec sysclk. ++ * ++ * Called from late_probe() after all components are bound. ++ */ ++static int fsl_asoc_card_init_codecs(struct snd_soc_card *card, ++ struct snd_soc_pcm_runtime *rtd) ++{ ++ struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); ++ const struct fsl_asoc_card_pdata *pdata = priv->pdata; + struct snd_soc_dai *codec_dai; + struct codec_priv *codec_priv; + struct device *dev = card->dev; + int codec_idx; + int ret; + ++ /* Read MCLK rate from each bound codec component */ ++ for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { ++ struct clk *codec_clk = clk_get(codec_dai->component->dev, NULL); ++ ++ codec_priv = &priv->codec_priv[codec_idx]; ++ if (!IS_ERR(codec_clk)) { ++ codec_priv->mclk_freq = clk_get_rate(codec_clk); ++ clk_put(codec_clk); ++ } ++ } ++ ++ if (pdata->codec_init) { ++ ret = pdata->codec_init(priv); ++ if (ret) ++ return ret; ++ } ++ ++ for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { ++ codec_priv = &priv->codec_priv[codec_idx]; ++ ++ ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, ++ codec_priv->mclk_freq, SND_SOC_CLOCK_IN); ++ if (ret && ret != -ENOTSUPP) { ++ dev_err(dev, "failed to set sysclk in %s\n", __func__); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static void fsl_asoc_card_free_jack(struct snd_soc_card *card) ++{ ++ struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); ++ ++ if (priv->hp_jack.gpio.desc) { ++ snd_soc_jack_notifier_unregister(&priv->hp_jack.jack, &hp_jack_nb); ++ snd_soc_jack_free_gpios(&priv->hp_jack.jack, 1, &priv->hp_jack.gpio); ++ priv->hp_jack.gpio.desc = NULL; ++ } ++ ++ if (priv->mic_jack.gpio.desc) { ++ snd_soc_jack_notifier_unregister(&priv->mic_jack.jack, &mic_jack_nb); ++ snd_soc_jack_free_gpios(&priv->mic_jack.jack, 1, &priv->mic_jack.gpio); ++ priv->mic_jack.gpio.desc = NULL; ++ } ++} ++ ++/* ++ * fsl_asoc_card_init_jack - register optional headphone and mic jacks. ++ * ++ * Called from late_probe() once per card bind cycle. ++ */ ++static int fsl_asoc_card_init_jack(struct snd_soc_card *card) ++{ ++ struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); ++ struct device_node *np = priv->pdev->dev.of_node; ++ int ret; ++ ++ /* ++ * Properties "hp-det-gpios" and "mic-det-gpios" are optional. ++ * simple_util_init_jack() checks for the GPIO property and ++ * does nothing if it is absent. ++ */ ++ if (of_property_present(np, "hp-det-gpios") || ++ of_property_present(np, "hp-det-gpio") /* deprecated */) { ++ ret = simple_util_init_jack(card, &priv->hp_jack, ++ 1, NULL, "Headphone Jack"); ++ if (ret) ++ return ret; ++ ++ snd_soc_jack_notifier_register(&priv->hp_jack.jack, &hp_jack_nb); ++ } ++ ++ if (of_property_present(np, "mic-det-gpios") || ++ of_property_present(np, "mic-det-gpio") /* deprecated */) { ++ ret = simple_util_init_jack(card, &priv->mic_jack, ++ 0, NULL, "Mic Jack"); ++ if (ret) ++ return ret; ++ ++ snd_soc_jack_notifier_register(&priv->mic_jack.jack, &mic_jack_nb); ++ } ++ ++ return 0; ++} ++ ++static int fsl_asoc_card_late_probe(struct snd_soc_card *card) ++{ ++ struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); ++ struct snd_soc_pcm_runtime *rtd; ++ int ret; ++ ++ /* Use the first rtd which carries the CPU+codec DAIs */ ++ rtd = list_first_entry(&card->rtd_list, ++ struct snd_soc_pcm_runtime, list); ++ ++ ret = fsl_asoc_card_init_jack(card); ++ if (ret) ++ goto jack_fail; ++ ++ ret = fsl_asoc_card_init_cpu(card, rtd); ++ if (ret) ++ goto jack_fail; ++ + if (fsl_asoc_card_is_ac97(priv)) { + #if IS_ENABLED(CONFIG_SND_AC97_CODEC) + struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component; +@@ -671,19 +1058,20 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card) + return 0; + } + +- for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { +- codec_priv = &priv->codec_priv[codec_idx]; ++ ret = fsl_asoc_card_init_codecs(card, rtd); ++ if (ret) ++ goto jack_fail; + +- ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, +- codec_priv->mclk_freq, SND_SOC_CLOCK_IN); +- if (ret && ret != -ENOTSUPP) { +- dev_err(dev, "failed to set sysclk in %s\n", __func__); +- return ret; +- } ++ return 0; + +- if (!IS_ERR_OR_NULL(codec_priv->mclk)) +- clk_prepare_enable(codec_priv->mclk); +- } ++jack_fail: ++ fsl_asoc_card_free_jack(card); ++ return ret; ++} ++ ++static int fsl_asoc_card_card_remove(struct snd_soc_card *card) ++{ ++ fsl_asoc_card_free_jack(card); + + return 0; + } +@@ -697,12 +1085,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) + struct platform_device *asrc_pdev = NULL; + struct device_node *bitclkprovider = NULL; + struct device_node *frameprovider = NULL; +- struct platform_device *cpu_pdev; + struct fsl_asoc_card_priv *priv; +- struct device *codec_dev[2] = { NULL, NULL }; ++ const struct fsl_asoc_card_pdata *pdata; + struct snd_soc_dai_link_component *dlc; +- const char *codec_dai_name[2]; +- const char *codec_dev_name[2]; ++ const char *codec_dai_name[2] = { NULL, NULL }; + u32 asrc_fmt = 0; + int codec_idx; + u32 width; +@@ -714,6 +1100,13 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) + + priv->pdev = pdev; + ++ pdata = of_device_get_match_data(&pdev->dev); ++ if (!pdata) { ++ dev_err(&pdev->dev, "unknown Device Tree compatible\n"); ++ return -EINVAL; ++ } ++ priv->pdata = pdata; ++ + cpu_np = of_parse_phandle(np, "audio-cpu", 0); + /* Give a chance to old DT bindings */ + if (!cpu_np) +@@ -726,52 +1119,13 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) + goto fail; + } + +- cpu_pdev = of_find_device_by_node(cpu_np); +- if (!cpu_pdev) { +- ret = dev_err_probe(&pdev->dev, -EPROBE_DEFER, +- "failed to find CPU DAI device\n"); +- goto fail; +- } +- + codec_np[0] = of_parse_phandle(np, "audio-codec", 0); + codec_np[1] = of_parse_phandle(np, "audio-codec", 1); + +- for (codec_idx = 0; codec_idx < 2; codec_idx++) { +- if (codec_np[codec_idx]) { +- struct platform_device *codec_pdev; +- struct i2c_client *codec_i2c; +- +- codec_i2c = of_find_i2c_device_by_node(codec_np[codec_idx]); +- if (codec_i2c) { +- codec_dev[codec_idx] = &codec_i2c->dev; +- codec_dev_name[codec_idx] = codec_i2c->name; +- } +- if (!codec_dev[codec_idx]) { +- codec_pdev = of_find_device_by_node(codec_np[codec_idx]); +- if (codec_pdev) { +- codec_dev[codec_idx] = &codec_pdev->dev; +- codec_dev_name[codec_idx] = codec_pdev->name; +- } +- } +- } +- } +- + asrc_np = of_parse_phandle(np, "audio-asrc", 0); + if (asrc_np) + asrc_pdev = of_find_device_by_node(asrc_np); + +- /* Get the MCLK rate only, and leave it controlled by CODEC drivers */ +- for (codec_idx = 0; codec_idx < 2; codec_idx++) { +- if (codec_dev[codec_idx]) { +- struct clk *codec_clk = clk_get(codec_dev[codec_idx], NULL); +- +- if (!IS_ERR(codec_clk)) { +- priv->codec_priv[codec_idx].mclk_freq = clk_get_rate(codec_clk); +- clk_put(codec_clk); +- } +- } +- } +- + /* Default sample rate and format, will be updated in hw_params() */ + priv->sample_rate = 44100; + priv->sample_format = SNDRV_PCM_FORMAT_S16_LE; +@@ -823,133 +1177,40 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) + } + + /* Diversify the card configurations */ +- if (of_device_is_compatible(np, "fsl,imx-audio-cs42888")) { +- codec_dai_name[0] = "cs42888"; +- priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv[0].mclk_freq; +- priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv[0].mclk_freq; +- priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; +- priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT; +- priv->cpu_priv.slot_width = 32; +- priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; +- priv->constraint_channels = &cs42888_channel_constraints; +- if (priv->codec_priv[0].mclk_freq % 12288000 == 0) +- priv->constraint_rates = &cs42888_rate_48k_constraints; +- else if (priv->codec_priv[0].mclk_freq % 11289600 == 0) +- priv->constraint_rates = &cs42888_rate_44k_constraints; +- else +- dev_warn(&pdev->dev, "Unknown MCLK frequency %lu, no rate constraints\n", +- priv->codec_priv[0].mclk_freq); +- } else if (of_device_is_compatible(np, "fsl,imx-audio-cs427x")) { +- codec_dai_name[0] = "cs4271-hifi"; +- priv->codec_priv[0].mclk_id = CS427x_SYSCLK_MCLK; +- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; +- } else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) { +- codec_dai_name[0] = "sgtl5000"; +- priv->codec_priv[0].mclk_id = SGTL5000_SYSCLK; +- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; +- } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic32x4")) { +- codec_dai_name[0] = "tlv320aic32x4-hifi"; +- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; +- } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic31xx")) { +- codec_dai_name[0] = "tlv320dac31xx-hifi"; +- priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; ++ priv->cpu_priv.sysclk_dir[TX] = pdata->sysclk_dir[TX]; ++ priv->cpu_priv.sysclk_dir[RX] = pdata->sysclk_dir[RX]; ++ priv->cpu_priv.sysclk_ratio[TX] = pdata->sysclk_ratio[TX]; ++ priv->cpu_priv.sysclk_ratio[RX] = pdata->sysclk_ratio[RX]; ++ priv->cpu_priv.slot_width = pdata->slot_width; ++ ++ codec_dai_name[0] = pdata->codec_dai_name; ++ priv->codec_priv[0].mclk_id = pdata->codec_mclk_id; ++ if (pdata->has_pll) { ++ priv->codec_priv[0].fll_id = pdata->codec_fll_id; ++ priv->codec_priv[0].pll_id = pdata->codec_pll_id; ++ } ++ if (pdata->codec_pll_ratio_s24) ++ priv->codec_priv[0].pll_ratio_s24 = pdata->codec_pll_ratio_s24; ++ ++ if (pdata->playback_only) { + priv->dai_link[1].playback_only = 1; + priv->dai_link[2].playback_only = 1; +- priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; +- priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT; +- priv->card.dapm_routes = audio_map_tx; +- priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); +- } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) { +- codec_dai_name[0] = "wm8962"; +- priv->codec_priv[0].mclk_id = WM8962_SYSCLK_MCLK; +- priv->codec_priv[0].fll_id = WM8962_SYSCLK_FLL; +- priv->codec_priv[0].pll_id = WM8962_FLL; +- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; +- /* +- * WM8962 has same BCLK generation limitations as WM8960. +- * See WM8960 section for detailed explanation. +- */ +- if (of_node_name_eq(cpu_np, "sai")) +- priv->exclude_format = SNDRV_PCM_FMTBIT_S20_3LE; +- } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8960")) { +- codec_dai_name[0] = "wm8960-hifi"; +- priv->codec_priv[0].fll_id = WM8960_SYSCLK_AUTO; +- priv->codec_priv[0].pll_id = WM8960_SYSCLK_AUTO; +- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; +- /* +- * WM8960 in master mode cannot generate exact 1.92 MHz BCLK +- * required for S20_3LE (48kHz × 2ch × 20bit). Closest available +- * is 2.048 MHz (SYSCLK/6), which causes right channel corruption. +- * +- * In SAI master mode, SAI derive BCLK from MCLK using integer +- * dividers only. S20_3LE requires non-integer divider ratios +- * with standard MCLK frequencies. For example, 48kHz stereo +- * needs 1.920 MHz BCLK, which requires a divider of 6.4 from +- * 12.288 MHz MCLK (not an integer). +- */ +- if (of_node_name_eq(cpu_np, "sai")) +- priv->exclude_format = SNDRV_PCM_FMTBIT_S20_3LE; +- } else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) { +- codec_dai_name[0] = "ac97-hifi"; +- priv->dai_fmt = SND_SOC_DAIFMT_AC97; +- priv->card.dapm_routes = audio_map_ac97; +- priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_ac97); +- } else if (of_device_is_compatible(np, "fsl,imx-audio-mqs")) { +- codec_dai_name[0] = "fsl-mqs-dai"; +- priv->dai_fmt = SND_SOC_DAIFMT_LEFT_J | +- SND_SOC_DAIFMT_CBC_CFC | +- SND_SOC_DAIFMT_NB_NF; +- priv->dai_link[1].playback_only = 1; +- priv->dai_link[2].playback_only = 1; +- priv->card.dapm_routes = audio_map_tx; +- priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); +- } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8524")) { +- codec_dai_name[0] = "wm8524-hifi"; +- priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; +- priv->dai_link[1].playback_only = 1; +- priv->dai_link[2].playback_only = 1; +- priv->cpu_priv.slot_width = 32; +- priv->card.dapm_routes = audio_map_tx; +- priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); +- priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; +- priv->cpu_priv.sysclk_ratio[TX] = 256; +- } else if (of_device_is_compatible(np, "fsl,imx-audio-si476x")) { +- codec_dai_name[0] = "si476x-codec"; +- priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; +- priv->card.dapm_routes = audio_map_rx; +- priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_rx); +- } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8958")) { +- codec_dai_name[0] = "wm8994-aif1"; +- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; +- priv->codec_priv[0].mclk_id = WM8994_FLL_SRC_MCLK1; +- priv->codec_priv[0].fll_id = WM8994_SYSCLK_FLL1; +- priv->codec_priv[0].pll_id = WM8994_FLL1; +- priv->codec_priv[0].free_freq = priv->codec_priv[0].mclk_freq; +- priv->card.dapm_routes = NULL; +- priv->card.num_dapm_routes = 0; +- } else if (of_device_is_compatible(np, "fsl,imx-audio-nau8822")) { +- codec_dai_name[0] = "nau8822-hifi"; +- priv->codec_priv[0].mclk_id = NAU8822_CLK_MCLK; +- priv->codec_priv[0].fll_id = NAU8822_CLK_PLL; +- priv->codec_priv[0].pll_id = NAU8822_CLK_PLL; +- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; +- if (codec_dev[0]) +- priv->codec_priv[0].mclk = devm_clk_get(codec_dev[0], NULL); +- } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8904")) { +- codec_dai_name[0] = "wm8904-hifi"; +- priv->codec_priv[0].mclk_id = WM8904_FLL_MCLK; +- priv->codec_priv[0].fll_id = WM8904_CLK_FLL; +- priv->codec_priv[0].pll_id = WM8904_FLL_MCLK; +- priv->codec_priv[0].pll_ratio_s24 = 192; +- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; +- } else if (of_device_is_compatible(np, "fsl,imx-audio-spdif")) { +- ret = fsl_asoc_card_spdif_init(codec_np, cpu_np, codec_dai_name, priv); ++ } ++ if (pdata->capture_only) { ++ priv->dai_link[1].capture_only = 1; ++ priv->dai_link[2].capture_only = 1; ++ } ++ ++ priv->dai_fmt = pdata->dai_fmt; ++ ++ priv->card.dapm_routes = pdata->dapm_routes; ++ priv->card.num_dapm_routes = pdata->num_dapm_routes; ++ ++ if (pdata->probe_init) { ++ ret = pdata->probe_init(codec_np, cpu_np, ++ codec_dai_name, priv); + if (ret) + goto asrc_fail; +- } else { +- dev_err(&pdev->dev, "unknown Device Tree compatible\n"); +- ret = -EINVAL; +- goto asrc_fail; + } + + /* +@@ -995,51 +1256,21 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) + of_node_put(bitclkprovider); + of_node_put(frameprovider); + +- if (!fsl_asoc_card_is_ac97(priv) && !codec_dev[0] +- && codec_dai_name[0] != snd_soc_dummy_dlc.dai_name) { +- dev_dbg(&pdev->dev, "failed to find codec device\n"); +- ret = -EPROBE_DEFER; +- goto asrc_fail; +- } +- +- /* Common settings for corresponding Freescale CPU DAI driver */ +- if (of_node_name_eq(cpu_np, "ssi")) { +- /* Only SSI needs to configure AUDMUX */ +- ret = fsl_asoc_card_audmux_init(np, priv); +- if (ret) { +- dev_err(&pdev->dev, "failed to init audmux\n"); +- goto asrc_fail; +- } +- } else if (of_node_name_eq(cpu_np, "esai")) { +- struct clk *esai_clk = clk_get(&cpu_pdev->dev, "extal"); +- +- if (!IS_ERR(esai_clk)) { +- priv->cpu_priv.sysclk_freq[TX] = clk_get_rate(esai_clk); +- priv->cpu_priv.sysclk_freq[RX] = clk_get_rate(esai_clk); +- clk_put(esai_clk); +- } else if (PTR_ERR(esai_clk) == -EPROBE_DEFER) { +- ret = -EPROBE_DEFER; +- goto asrc_fail; +- } +- +- priv->cpu_priv.sysclk_id[1] = ESAI_HCKT_EXTAL; +- priv->cpu_priv.sysclk_id[0] = ESAI_HCKR_EXTAL; +- } else if (of_node_name_eq(cpu_np, "sai")) { +- priv->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1; +- priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1; +- } +- + /* Initialize sound card */ + priv->card.dev = &pdev->dev; + priv->card.owner = THIS_MODULE; + ret = snd_soc_of_parse_card_name(&priv->card, "model"); + if (ret) { +- snprintf(priv->name, sizeof(priv->name), "%s-audio", +- fsl_asoc_card_is_ac97(priv) ? "ac97" : codec_dev_name[0]); +- priv->card.name = priv->name; ++ /* ++ * "model" is required by the DT binding. Enforce it here so ++ * the driver fails with a clear message. ++ */ ++ dev_err(&pdev->dev, "Error parsing card name: %d\n", ret); ++ goto asrc_fail; + } + priv->card.dai_link = priv->dai_link; + priv->card.late_probe = fsl_asoc_card_late_probe; ++ priv->card.remove = fsl_asoc_card_card_remove; + priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets; + priv->card.num_dapm_widgets = ARRAY_SIZE(fsl_asoc_card_dapm_widgets); + +@@ -1146,39 +1377,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) + goto asrc_fail; + } + +- /* +- * Properties "hp-det-gpios" and "mic-det-gpios" are optional, and +- * simple_util_init_jack() uses these properties for creating +- * Headphone Jack and Microphone Jack. +- * +- * The notifier is initialized in snd_soc_card_jack_new(), then +- * snd_soc_jack_notifier_register can be called. +- */ +- if (of_property_present(np, "hp-det-gpios") || +- of_property_present(np, "hp-det-gpio") /* deprecated */) { +- ret = simple_util_init_jack(&priv->card, &priv->hp_jack, +- 1, NULL, "Headphone Jack"); +- if (ret) +- goto asrc_fail; +- +- snd_soc_jack_notifier_register(&priv->hp_jack.jack, &hp_jack_nb); +- } +- +- if (of_property_present(np, "mic-det-gpios") || +- of_property_present(np, "mic-det-gpio") /* deprecated */) { +- ret = simple_util_init_jack(&priv->card, &priv->mic_jack, +- 0, NULL, "Mic Jack"); +- if (ret) +- goto asrc_fail; +- +- snd_soc_jack_notifier_register(&priv->mic_jack.jack, &mic_jack_nb); +- } +- + asrc_fail: + of_node_put(asrc_np); + of_node_put(codec_np[0]); + of_node_put(codec_np[1]); +- put_device(&cpu_pdev->dev); + fail: + of_node_put(cpu_np); + +@@ -1186,21 +1388,21 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) + } + + static const struct of_device_id fsl_asoc_card_dt_ids[] = { +- { .compatible = "fsl,imx-audio-ac97", }, +- { .compatible = "fsl,imx-audio-cs42888", }, +- { .compatible = "fsl,imx-audio-cs427x", }, +- { .compatible = "fsl,imx-audio-tlv320aic32x4", }, +- { .compatible = "fsl,imx-audio-tlv320aic31xx", }, +- { .compatible = "fsl,imx-audio-sgtl5000", }, +- { .compatible = "fsl,imx-audio-wm8962", }, +- { .compatible = "fsl,imx-audio-wm8960", }, +- { .compatible = "fsl,imx-audio-mqs", }, +- { .compatible = "fsl,imx-audio-wm8524", }, +- { .compatible = "fsl,imx-audio-si476x", }, +- { .compatible = "fsl,imx-audio-wm8958", }, +- { .compatible = "fsl,imx-audio-nau8822", }, +- { .compatible = "fsl,imx-audio-wm8904", }, +- { .compatible = "fsl,imx-audio-spdif", }, ++ { .compatible = "fsl,imx-audio-ac97", .data = &fsl_asoc_ac97_pdata }, ++ { .compatible = "fsl,imx-audio-cs42888", .data = &fsl_asoc_cs42888_pdata }, ++ { .compatible = "fsl,imx-audio-cs427x", .data = &fsl_asoc_cs427x_pdata }, ++ { .compatible = "fsl,imx-audio-tlv320aic32x4", .data = &fsl_asoc_tlv320aic32x4_pdata }, ++ { .compatible = "fsl,imx-audio-tlv320aic31xx", .data = &fsl_asoc_tlv320aic31xx_pdata }, ++ { .compatible = "fsl,imx-audio-sgtl5000", .data = &fsl_asoc_sgtl5000_pdata }, ++ { .compatible = "fsl,imx-audio-wm8962", .data = &fsl_asoc_wm8962_pdata }, ++ { .compatible = "fsl,imx-audio-wm8960", .data = &fsl_asoc_wm8960_pdata }, ++ { .compatible = "fsl,imx-audio-mqs", .data = &fsl_asoc_mqs_pdata }, ++ { .compatible = "fsl,imx-audio-wm8524", .data = &fsl_asoc_wm8524_pdata }, ++ { .compatible = "fsl,imx-audio-si476x", .data = &fsl_asoc_si476x_pdata }, ++ { .compatible = "fsl,imx-audio-wm8958", .data = &fsl_asoc_wm8958_pdata }, ++ { .compatible = "fsl,imx-audio-nau8822", .data = &fsl_asoc_nau8822_pdata }, ++ { .compatible = "fsl,imx-audio-wm8904", .data = &fsl_asoc_wm8904_pdata }, ++ { .compatible = "fsl,imx-audio-spdif", .data = &fsl_asoc_spdif_pdata }, + {} + }; + MODULE_DEVICE_TABLE(of, fsl_asoc_card_dt_ids); +diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c +--- a/sound/soc/fsl/fsl_asrc.c ++++ b/sound/soc/fsl/fsl_asrc.c +@@ -222,10 +222,9 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair) + enum asrc_pair_index index = ASRC_INVALID_PAIR; + struct fsl_asrc *asrc = pair->asrc; + struct device *dev = &asrc->pdev->dev; +- unsigned long lock_flags; + int i, ret = 0; + +- spin_lock_irqsave(&asrc->lock, lock_flags); ++ guard(spinlock_irqsave)(&asrc->lock); + + for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) { + if (asrc->pair[i] != NULL) +@@ -250,8 +249,6 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair) + pair->index = index; + } + +- spin_unlock_irqrestore(&asrc->lock, lock_flags); +- + return ret; + } + +@@ -265,19 +262,16 @@ static void fsl_asrc_release_pair(struct fsl_asrc_pair *pair) + { + struct fsl_asrc *asrc = pair->asrc; + enum asrc_pair_index index = pair->index; +- unsigned long lock_flags; + + /* Make sure the pair is disabled */ + regmap_update_bits(asrc->regmap, REG_ASRCTR, + ASRCTR_ASRCEi_MASK(index), 0); + +- spin_lock_irqsave(&asrc->lock, lock_flags); ++ guard(spinlock_irqsave)(&asrc->lock); + + asrc->channel_avail += pair->channels; + asrc->pair[index] = NULL; + pair->error = 0; +- +- spin_unlock_irqrestore(&asrc->lock, lock_flags); + } + + /** +diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h +--- a/sound/soc/fsl/fsl_asrc.h ++++ b/sound/soc/fsl/fsl_asrc.h +@@ -444,7 +444,7 @@ struct dma_block { + }; + + /** +- * fsl_asrc_soc_data: soc specific data ++ * struct fsl_asrc_soc_data - soc specific data + * + * @use_edma: using edma as dma device or not + * @channel_bits: width of ASRCNCR register for each pair +@@ -457,7 +457,7 @@ struct fsl_asrc_soc_data { + }; + + /** +- * fsl_asrc_pair_priv: ASRC Pair private data ++ * struct fsl_asrc_pair_priv - ASRC Pair private data + * + * @config: configuration profile + */ +@@ -466,7 +466,7 @@ struct fsl_asrc_pair_priv { + }; + + /** +- * fsl_asrc_priv: ASRC private data ++ * struct fsl_asrc_priv - ASRC private data + * + * @asrck_clk: clock sources to driver ASRC internal logic + * @soc: soc specific data +diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c +--- a/sound/soc/fsl/fsl_asrc_dma.c ++++ b/sound/soc/fsl/fsl_asrc_dma.c +@@ -219,7 +219,9 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component, + */ + component_be = snd_soc_lookup_component_nolocked(dev_be, SND_DMAENGINE_PCM_DRV_NAME); + if (component_be) { +- be_chan = soc_component_to_pcm(component_be)->chan[substream->stream]; ++ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component_be); ++ ++ be_chan = pcm->chan[substream->stream]; + tmp_chan = be_chan; + } + if (!tmp_chan) { +diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c +--- a/sound/soc/fsl/fsl_audmix.c ++++ b/sound/soc/fsl/fsl_audmix.c +@@ -286,7 +286,6 @@ static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) + { + struct fsl_audmix *priv = snd_soc_dai_get_drvdata(dai); +- unsigned long lock_flags; + + /* Capture stream shall not be handled */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) +@@ -296,16 +295,14 @@ static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd, + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: +- spin_lock_irqsave(&priv->lock, lock_flags); +- priv->tdms |= BIT(dai->driver->id); +- spin_unlock_irqrestore(&priv->lock, lock_flags); ++ scoped_guard(spinlock_irqsave, &priv->lock) ++ priv->tdms |= BIT(dai->driver->id); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: +- spin_lock_irqsave(&priv->lock, lock_flags); +- priv->tdms &= ~BIT(dai->driver->id); +- spin_unlock_irqrestore(&priv->lock, lock_flags); ++ scoped_guard(spinlock_irqsave, &priv->lock) ++ priv->tdms &= ~BIT(dai->driver->id); + break; + default: + return -EINVAL; +diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c +--- a/sound/soc/fsl/fsl_dma.c ++++ b/sound/soc/fsl/fsl_dma.c +@@ -18,8 +18,6 @@ + #include + #include + #include +-#include +-#include + #include + #include + +@@ -824,9 +822,34 @@ static int fsl_soc_dma_probe(struct platform_device *pdev) + struct device_node *np = pdev->dev.of_node; + struct device_node *ssi_np; + struct resource res; ++ void __iomem *channel; + const uint32_t *iprop; ++ int irq; + int ret; + ++ channel = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(channel)) ++ return PTR_ERR(channel); ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) ++ return irq; ++ ++ dma = devm_kzalloc(&pdev->dev, sizeof(*dma), GFP_KERNEL); ++ if (!dma) ++ return -ENOMEM; ++ ++ dma->dai.name = DRV_NAME; ++ dma->dai.open = fsl_dma_open; ++ dma->dai.close = fsl_dma_close; ++ dma->dai.hw_params = fsl_dma_hw_params; ++ dma->dai.hw_free = fsl_dma_hw_free; ++ dma->dai.pointer = fsl_dma_pointer; ++ dma->dai.pcm_new = fsl_dma_new; ++ ++ dma->channel = channel; ++ dma->irq = irq; ++ + /* Find the SSI node that points to us. */ + ssi_np = find_ssi_node(np); + if (!ssi_np) { +@@ -842,55 +865,19 @@ static int fsl_soc_dma_probe(struct platform_device *pdev) + return ret; + } + +- dma = kzalloc_obj(*dma); +- if (!dma) { +- of_node_put(ssi_np); +- return -ENOMEM; +- } +- +- dma->dai.name = DRV_NAME; +- dma->dai.open = fsl_dma_open; +- dma->dai.close = fsl_dma_close; +- dma->dai.hw_params = fsl_dma_hw_params; +- dma->dai.hw_free = fsl_dma_hw_free; +- dma->dai.pointer = fsl_dma_pointer; +- dma->dai.pcm_new = fsl_dma_new; +- + /* Store the SSI-specific information that we need */ + dma->ssi_stx_phys = res.start + REG_SSI_STX0; + dma->ssi_srx_phys = res.start + REG_SSI_SRX0; + + iprop = of_get_property(ssi_np, "fsl,fifo-depth", NULL); ++ of_node_put(ssi_np); + if (iprop) + dma->ssi_fifo_depth = be32_to_cpup(iprop); + else + /* Older 8610 DTs didn't have the fifo-depth property */ + dma->ssi_fifo_depth = 8; + +- of_node_put(ssi_np); +- +- ret = devm_snd_soc_register_component(&pdev->dev, &dma->dai, NULL, 0); +- if (ret) { +- dev_err(&pdev->dev, "could not register platform\n"); +- kfree(dma); +- return ret; +- } +- +- dma->channel = of_iomap(np, 0); +- dma->irq = irq_of_parse_and_map(np, 0); +- +- dev_set_drvdata(&pdev->dev, dma); +- +- return 0; +-} +- +-static void fsl_soc_dma_remove(struct platform_device *pdev) +-{ +- struct dma_object *dma = dev_get_drvdata(&pdev->dev); +- +- iounmap(dma->channel); +- irq_dispose_mapping(dma->irq); +- kfree(dma); ++ return devm_snd_soc_register_component(&pdev->dev, &dma->dai, NULL, 0); + } + + static const struct of_device_id fsl_soc_dma_ids[] = { +@@ -905,7 +892,6 @@ static struct platform_driver fsl_soc_dma_driver = { + .of_match_table = fsl_soc_dma_ids, + }, + .probe = fsl_soc_dma_probe, +- .remove = fsl_soc_dma_remove, + }; + + module_platform_driver(fsl_soc_dma_driver); +diff --git a/sound/soc/fsl/fsl_dma.h b/sound/soc/fsl/fsl_dma.h +--- a/sound/soc/fsl/fsl_dma.h ++++ b/sound/soc/fsl/fsl_dma.h +@@ -92,7 +92,7 @@ static inline u32 CCSR_DMA_ECLNDAR_ADDR(u64 x) + #define CCSR_DMA_ATR_SNOOP 0x00050000 + #define CCSR_DMA_ATR_ESAD_MASK 0x0000000F + +-/** ++/* + * List Descriptor for extended chaining mode DMA operations. + * + * The CLSDAR register points to the first (in a linked-list) List +@@ -107,7 +107,7 @@ struct fsl_dma_list_descriptor { + u8 res[8]; /* Reserved */ + } __attribute__ ((aligned(32), packed)); + +-/** ++/* + * Link Descriptor for basic and extended chaining mode DMA operations. + * + * A Link Descriptor points to a single DMA buffer. Each link descriptor +diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c +--- a/sound/soc/fsl/fsl_easrc.c ++++ b/sound/soc/fsl/fsl_easrc.c +@@ -1025,7 +1025,6 @@ static int fsl_easrc_config_context(struct fsl_asrc *easrc, unsigned int ctx_id) + struct fsl_easrc_ctx_priv *ctx_priv; + struct fsl_asrc_pair *ctx; + struct device *dev; +- unsigned long lock_flags; + int ret; + + if (!easrc) +@@ -1053,9 +1052,8 @@ static int fsl_easrc_config_context(struct fsl_asrc *easrc, unsigned int ctx_id) + if (ret) + return ret; + +- spin_lock_irqsave(&easrc->lock, lock_flags); +- ret = fsl_easrc_config_slot(easrc, ctx->index); +- spin_unlock_irqrestore(&easrc->lock, lock_flags); ++ scoped_guard(spinlock_irqsave, &easrc->lock) ++ ret = fsl_easrc_config_slot(easrc, ctx->index); + if (ret) + return ret; + +@@ -1301,13 +1299,12 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx) + enum asrc_pair_index index = ASRC_INVALID_PAIR; + struct fsl_asrc *easrc = ctx->asrc; + struct device *dev; +- unsigned long lock_flags; + int ret = 0; + int i; + + dev = &easrc->pdev->dev; + +- spin_lock_irqsave(&easrc->lock, lock_flags); ++ guard(spinlock_irqsave)(&easrc->lock); + + for (i = ASRC_PAIR_A; i < EASRC_CTX_MAX_NUM; i++) { + if (easrc->pair[i]) +@@ -1331,8 +1328,6 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx) + easrc->channel_avail -= channels; + } + +- spin_unlock_irqrestore(&easrc->lock, lock_flags); +- + return ret; + } + +@@ -1343,7 +1338,6 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx) + */ + static void fsl_easrc_release_context(struct fsl_asrc_pair *ctx) + { +- unsigned long lock_flags; + struct fsl_asrc *easrc; + + if (!ctx) +@@ -1351,14 +1345,12 @@ static void fsl_easrc_release_context(struct fsl_asrc_pair *ctx) + + easrc = ctx->asrc; + +- spin_lock_irqsave(&easrc->lock, lock_flags); ++ guard(spinlock_irqsave)(&easrc->lock); + + fsl_easrc_release_slot(easrc, ctx->index); + + easrc->channel_avail += ctx->channels; + easrc->pair[ctx->index] = NULL; +- +- spin_unlock_irqrestore(&easrc->lock, lock_flags); + } + + /* +@@ -2292,15 +2284,13 @@ static int fsl_easrc_runtime_suspend(struct device *dev) + { + struct fsl_asrc *easrc = dev_get_drvdata(dev); + struct fsl_easrc_priv *easrc_priv = easrc->private; +- unsigned long lock_flags; + + regcache_cache_only(easrc->regmap, true); + + clk_disable_unprepare(easrc->mem_clk); + +- spin_lock_irqsave(&easrc->lock, lock_flags); +- easrc_priv->firmware_loaded = 0; +- spin_unlock_irqrestore(&easrc->lock, lock_flags); ++ scoped_guard(spinlock_irqsave, &easrc->lock) ++ easrc_priv->firmware_loaded = 0; + + return 0; + } +@@ -2311,7 +2301,6 @@ static int fsl_easrc_runtime_resume(struct device *dev) + struct fsl_easrc_priv *easrc_priv = easrc->private; + struct fsl_easrc_ctx_priv *ctx_priv; + struct fsl_asrc_pair *ctx; +- unsigned long lock_flags; + int ret; + int i; + +@@ -2323,13 +2312,11 @@ static int fsl_easrc_runtime_resume(struct device *dev) + regcache_mark_dirty(easrc->regmap); + regcache_sync(easrc->regmap); + +- spin_lock_irqsave(&easrc->lock, lock_flags); +- if (easrc_priv->firmware_loaded) { +- spin_unlock_irqrestore(&easrc->lock, lock_flags); +- goto skip_load; ++ scoped_guard(spinlock_irqsave, &easrc->lock) { ++ if (easrc_priv->firmware_loaded) ++ return 0; ++ easrc_priv->firmware_loaded = 1; + } +- easrc_priv->firmware_loaded = 1; +- spin_unlock_irqrestore(&easrc->lock, lock_flags); + + ret = fsl_easrc_get_firmware(easrc); + if (ret) { +@@ -2377,9 +2364,7 @@ static int fsl_easrc_runtime_resume(struct device *dev) + goto disable_mem_clk; + } + +-skip_load: + return 0; +- + disable_mem_clk: + clk_disable_unprepare(easrc->mem_clk); + return ret; +diff --git a/sound/soc/fsl/fsl_easrc.h b/sound/soc/fsl/fsl_easrc.h +--- a/sound/soc/fsl/fsl_easrc.h ++++ b/sound/soc/fsl/fsl_easrc.h +@@ -584,7 +584,7 @@ struct fsl_easrc_slot { + }; + + /** +- * fsl_easrc_ctx_priv: EASRC context private data ++ * struct fsl_easrc_ctx_priv - EASRC context private data + * + * @in_params: input parameter + * @out_params: output parameter +@@ -625,7 +625,7 @@ struct fsl_easrc_ctx_priv { + }; + + /** +- * fsl_easrc_priv: EASRC private data ++ * struct fsl_easrc_priv - EASRC private data + * + * @slot: slot setting + * @firmware_hdr: the header of firmware +diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c +--- a/sound/soc/fsl/fsl_esai.c ++++ b/sound/soc/fsl/fsl_esai.c +@@ -709,10 +709,9 @@ static void fsl_esai_hw_reset(struct work_struct *work) + { + struct fsl_esai *esai_priv = container_of(work, struct fsl_esai, work); + bool tx = true, rx = false, enabled[2]; +- unsigned long lock_flags; + u32 tfcr, rfcr; + +- spin_lock_irqsave(&esai_priv->lock, lock_flags); ++ guard(spinlock_irqsave)(&esai_priv->lock); + /* Save the registers */ + regmap_read(esai_priv->regmap, REG_ESAI_TFCR, &tfcr); + regmap_read(esai_priv->regmap, REG_ESAI_RFCR, &rfcr); +@@ -750,8 +749,6 @@ static void fsl_esai_hw_reset(struct work_struct *work) + fsl_esai_trigger_start(esai_priv, tx); + if (enabled[rx]) + fsl_esai_trigger_start(esai_priv, rx); +- +- spin_unlock_irqrestore(&esai_priv->lock, lock_flags); + } + + static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, +@@ -759,7 +756,6 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, + { + struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; +- unsigned long lock_flags; + + esai_priv->channels[tx] = substream->runtime->channels; + +@@ -767,16 +763,14 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: +- spin_lock_irqsave(&esai_priv->lock, lock_flags); +- fsl_esai_trigger_start(esai_priv, tx); +- spin_unlock_irqrestore(&esai_priv->lock, lock_flags); ++ scoped_guard(spinlock_irqsave, &esai_priv->lock) ++ fsl_esai_trigger_start(esai_priv, tx); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: +- spin_lock_irqsave(&esai_priv->lock, lock_flags); +- fsl_esai_trigger_stop(esai_priv, tx); +- spin_unlock_irqrestore(&esai_priv->lock, lock_flags); ++ scoped_guard(spinlock_irqsave, &esai_priv->lock) ++ fsl_esai_trigger_stop(esai_priv, tx); + break; + default: + return -EINVAL; +diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c +--- a/sound/soc/fsl/fsl_spdif.c ++++ b/sound/soc/fsl/fsl_spdif.c +@@ -853,17 +853,15 @@ static int fsl_spdif_subcode_get(struct snd_kcontrol *kcontrol, + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; +- unsigned long flags; + int ret = -EAGAIN; + +- spin_lock_irqsave(&ctrl->ctl_lock, flags); ++ guard(spinlock_irqsave)(&ctrl->ctl_lock); + if (ctrl->ready_buf) { + int idx = (ctrl->ready_buf - 1) * SPDIF_UBITS_SIZE; + memcpy(&ucontrol->value.iec958.subcode[0], + &ctrl->subcode[idx], SPDIF_UBITS_SIZE); + ret = 0; + } +- spin_unlock_irqrestore(&ctrl->ctl_lock, flags); + + return ret; + } +@@ -885,17 +883,15 @@ static int fsl_spdif_qget(struct snd_kcontrol *kcontrol, + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; +- unsigned long flags; + int ret = -EAGAIN; + +- spin_lock_irqsave(&ctrl->ctl_lock, flags); ++ guard(spinlock_irqsave)(&ctrl->ctl_lock); + if (ctrl->ready_buf) { + int idx = (ctrl->ready_buf - 1) * SPDIF_QSUB_SIZE; + memcpy(&ucontrol->value.bytes.data[0], + &ctrl->qsub[idx], SPDIF_QSUB_SIZE); + ret = 0; + } +- spin_unlock_irqrestore(&ctrl->ctl_lock, flags); + + return ret; + } +diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c +--- a/sound/soc/fsl/fsl_ssi.c ++++ b/sound/soc/fsl/fsl_ssi.c +@@ -1218,13 +1218,13 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, + if (reg > 0x7f) + return; + +- mutex_lock(&fsl_ac97_data->ac97_reg_lock); ++ guard(mutex)(&fsl_ac97_data->ac97_reg_lock); + + ret = clk_prepare_enable(fsl_ac97_data->clk); + if (ret) { + pr_err("ac97 write clk_prepare_enable failed: %d\n", + ret); +- goto ret_unlock; ++ return; + } + + lreg = reg << 12; +@@ -1238,9 +1238,6 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, + udelay(100); + + clk_disable_unprepare(fsl_ac97_data->clk); +- +-ret_unlock: +- mutex_unlock(&fsl_ac97_data->ac97_reg_lock); + } + + static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, +@@ -1252,12 +1249,12 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, + unsigned int lreg; + int ret; + +- mutex_lock(&fsl_ac97_data->ac97_reg_lock); ++ guard(mutex)(&fsl_ac97_data->ac97_reg_lock); + + ret = clk_prepare_enable(fsl_ac97_data->clk); + if (ret) { + pr_err("ac97 read clk_prepare_enable failed: %d\n", ret); +- goto ret_unlock; ++ return val; + } + + lreg = (reg & 0x7f) << 12; +@@ -1272,8 +1269,6 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, + + clk_disable_unprepare(fsl_ac97_data->clk); + +-ret_unlock: +- mutex_unlock(&fsl_ac97_data->ac97_reg_lock); + return val; + } + +diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c +--- a/sound/soc/fsl/fsl_xcvr.c ++++ b/sound/soc/fsl/fsl_xcvr.c +@@ -797,10 +797,9 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, + { + struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; +- unsigned long lock_flags; + int ret = 0; + +- spin_lock_irqsave(&xcvr->lock, lock_flags); ++ guard(spinlock_irqsave)(&xcvr->lock); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: +@@ -812,7 +811,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, + FSL_XCVR_EXT_CTRL_DPTH_RESET(tx)); + if (ret < 0) { + dev_err(dai->dev, "Failed to set DPATH RESET: %d\n", ret); +- goto release_lock; ++ return ret; + } + + if (tx) { +@@ -824,7 +823,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, + FSL_XCVR_ISR_CMDC_TX_EN); + if (ret < 0) { + dev_err(dai->dev, "err updating isr %d\n", ret); +- goto release_lock; ++ return ret; + } + fallthrough; + case FSL_XCVR_MODE_SPDIF: +@@ -833,7 +832,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, + FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX); + if (ret < 0) { + dev_err(dai->dev, "Failed to start DATA_TX: %d\n", ret); +- goto release_lock; ++ return ret; + } + break; + } +@@ -844,14 +843,14 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, + FSL_XCVR_EXT_CTRL_DMA_DIS(tx), 0); + if (ret < 0) { + dev_err(dai->dev, "Failed to enable DMA: %d\n", ret); +- goto release_lock; ++ return ret; + } + + ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0, + FSL_XCVR_IRQ_EARC_ALL, FSL_XCVR_IRQ_EARC_ALL); + if (ret < 0) { + dev_err(dai->dev, "Error while setting IER0: %d\n", ret); +- goto release_lock; ++ return ret; + } + + /* clear DPATH RESET */ +@@ -860,7 +859,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, + 0); + if (ret < 0) { + dev_err(dai->dev, "Failed to clear DPATH RESET: %d\n", ret); +- goto release_lock; ++ return ret; + } + + break; +@@ -873,14 +872,14 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, + FSL_XCVR_EXT_CTRL_DMA_DIS(tx)); + if (ret < 0) { + dev_err(dai->dev, "Failed to disable DMA: %d\n", ret); +- goto release_lock; ++ return ret; + } + + ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0, + FSL_XCVR_IRQ_EARC_ALL, 0); + if (ret < 0) { + dev_err(dai->dev, "Failed to clear IER0: %d\n", ret); +- goto release_lock; ++ return ret; + } + + if (tx) { +@@ -891,7 +890,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, + FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX); + if (ret < 0) { + dev_err(dai->dev, "Failed to stop DATA_TX: %d\n", ret); +- goto release_lock; ++ return ret; + } + if (xcvr->soc_data->spdif_only) + break; +@@ -905,7 +904,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, + if (ret < 0) { + dev_err(dai->dev, + "Err updating ISR %d\n", ret); +- goto release_lock; ++ return ret; + } + break; + } +@@ -916,18 +915,16 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, + break; + } + +-release_lock: +- spin_unlock_irqrestore(&xcvr->lock, lock_flags); + return ret; + } + + static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) + { + struct device *dev = &xcvr->pdev->dev; +- const struct firmware *fw; + int ret = 0, rem, off, out, page = 0, size = FSL_XCVR_REG_OFFSET; + u32 mask, val; + ++ const struct firmware *fw __free(firmware) = NULL; + ret = request_firmware(&fw, xcvr->soc_data->fw_name, dev); + if (ret) { + dev_err(dev, "failed to request firmware.\n"); +@@ -939,7 +936,6 @@ static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) + /* RAM is 20KiB = 16KiB code + 4KiB data => max 10 pages 2KiB each */ + if (rem > 16384) { + dev_err(dev, "FW size %d is bigger than 16KiB.\n", rem); +- release_firmware(fw); + return -ENOMEM; + } + +@@ -950,7 +946,7 @@ static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) + if (ret < 0) { + dev_err(dev, "FW: failed to set page %d, err=%d\n", + page, ret); +- goto err_firmware; ++ return ret; + } + + off = page * size; +@@ -971,11 +967,6 @@ static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) + } + } + +-err_firmware: +- release_firmware(fw); +- if (ret < 0) +- return ret; +- + /* configure watermarks */ + mask = FSL_XCVR_EXT_CTRL_RX_FWM_MASK | FSL_XCVR_EXT_CTRL_TX_FWM_MASK; + val = FSL_XCVR_EXT_CTRL_RX_FWM(FSL_XCVR_FIFO_WMK_RX); +@@ -1448,11 +1439,10 @@ static void reset_rx_work(struct work_struct *work) + { + struct fsl_xcvr *xcvr = container_of(work, struct fsl_xcvr, work_rst); + struct device *dev = &xcvr->pdev->dev; +- unsigned long lock_flags; + u32 ext_ctrl; + + dev_dbg(dev, "reset rx path\n"); +- spin_lock_irqsave(&xcvr->lock, lock_flags); ++ guard(spinlock_irqsave)(&xcvr->lock); + regmap_read(xcvr->regmap, FSL_XCVR_EXT_CTRL, &ext_ctrl); + + if (!(ext_ctrl & FSL_XCVR_EXT_CTRL_DMA_RD_DIS)) { +@@ -1469,7 +1459,6 @@ static void reset_rx_work(struct work_struct *work) + FSL_XCVR_EXT_CTRL_RX_DPTH_RESET, + 0); + } +- spin_unlock_irqrestore(&xcvr->lock, lock_flags); + } + + static irqreturn_t irq0_isr(int irq, void *devid) +diff --git a/sound/soc/fsl/imx-audio-rpmsg.c b/sound/soc/fsl/imx-audio-rpmsg.c +--- a/sound/soc/fsl/imx-audio-rpmsg.c ++++ b/sound/soc/fsl/imx-audio-rpmsg.c +@@ -22,7 +22,6 @@ static int imx_audio_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, + struct rpmsg_r_msg *r_msg = (struct rpmsg_r_msg *)data; + struct rpmsg_info *info; + struct rpmsg_msg *msg; +- unsigned long flags; + + if (!rpmsg->rpmsg_pdev) + return 0; +@@ -37,21 +36,21 @@ static int imx_audio_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, + /* TYPE C is notification from M core */ + switch (r_msg->header.cmd) { + case TX_PERIOD_DONE: +- spin_lock_irqsave(&info->lock[TX], flags); +- msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM]; +- msg->r_msg.param.buffer_tail = +- r_msg->param.buffer_tail; +- msg->r_msg.param.buffer_tail %= info->num_period[TX]; +- spin_unlock_irqrestore(&info->lock[TX], flags); ++ scoped_guard(spinlock_irqsave, &info->lock[TX]) { ++ msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM]; ++ msg->r_msg.param.buffer_tail = ++ r_msg->param.buffer_tail; ++ msg->r_msg.param.buffer_tail %= info->num_period[TX]; ++ } + info->callback[TX](info->callback_param[TX]); + break; + case RX_PERIOD_DONE: +- spin_lock_irqsave(&info->lock[RX], flags); +- msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM]; +- msg->r_msg.param.buffer_tail = +- r_msg->param.buffer_tail; +- msg->r_msg.param.buffer_tail %= info->num_period[1]; +- spin_unlock_irqrestore(&info->lock[RX], flags); ++ scoped_guard(spinlock_irqsave, &info->lock[RX]) { ++ msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM]; ++ msg->r_msg.param.buffer_tail = ++ r_msg->param.buffer_tail; ++ msg->r_msg.param.buffer_tail %= info->num_period[1]; ++ } + info->callback[RX](info->callback_param[RX]); + break; + default: +diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c +--- a/sound/soc/fsl/imx-pcm-rpmsg.c ++++ b/sound/soc/fsl/imx-pcm-rpmsg.c +@@ -39,10 +39,9 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, + struct rpmsg_device *rpdev = info->rpdev; + int ret = 0; + +- mutex_lock(&info->msg_lock); ++ guard(mutex)(&info->msg_lock); + if (!rpdev) { + dev_err(info->dev, "rpmsg channel not ready\n"); +- mutex_unlock(&info->msg_lock); + return -EINVAL; + } + +@@ -55,15 +54,12 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, + sizeof(struct rpmsg_s_msg)); + if (ret) { + dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret); +- mutex_unlock(&info->msg_lock); + return ret; + } + + /* No receive msg for TYPE_C command */ +- if (msg->s_msg.header.type == MSG_TYPE_C) { +- mutex_unlock(&info->msg_lock); ++ if (msg->s_msg.header.type == MSG_TYPE_C) + return 0; +- } + + /* wait response from rpmsg */ + ret = wait_for_completion_timeout(&info->cmd_complete, +@@ -71,7 +67,6 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, + if (!ret) { + dev_err(&rpdev->dev, "rpmsg_send cmd %d timeout!\n", + msg->s_msg.header.cmd); +- mutex_unlock(&info->msg_lock); + return -ETIMEDOUT; + } + +@@ -100,8 +95,6 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, + dev_dbg(&rpdev->dev, "cmd:%d, resp %d\n", msg->s_msg.header.cmd, + info->r_msg.param.resp); + +- mutex_unlock(&info->msg_lock); +- + return 0; + } + +@@ -109,14 +102,13 @@ static int imx_rpmsg_insert_workqueue(struct snd_pcm_substream *substream, + struct rpmsg_msg *msg, + struct rpmsg_info *info) + { +- unsigned long flags; + int ret = 0; + + /* + * Queue the work to workqueue. + * If the queue is full, drop the message. + */ +- spin_lock_irqsave(&info->wq_lock, flags); ++ guard(spinlock_irqsave)(&info->wq_lock); + if (info->work_write_index != info->work_read_index) { + int index = info->work_write_index; + +@@ -130,7 +122,6 @@ static int imx_rpmsg_insert_workqueue(struct snd_pcm_substream *substream, + info->msg_drop_count[substream->stream]++; + ret = -EPIPE; + } +- spin_unlock_irqrestore(&info->wq_lock, flags); + + return ret; + } +@@ -523,7 +514,6 @@ static int imx_rpmsg_pcm_ack(struct snd_soc_component *component, + snd_pcm_sframes_t avail; + struct timer_list *timer; + struct rpmsg_msg *msg; +- unsigned long flags; + int buffer_tail = 0; + int written_num; + +@@ -553,11 +543,11 @@ static int imx_rpmsg_pcm_ack(struct snd_soc_component *component, + msg->s_msg.param.buffer_tail = buffer_tail; + + /* The notification message is updated to latest */ +- spin_lock_irqsave(&info->lock[substream->stream], flags); +- memcpy(&info->notify[substream->stream], msg, +- sizeof(struct rpmsg_s_msg)); +- info->notify_updated[substream->stream] = true; +- spin_unlock_irqrestore(&info->lock[substream->stream], flags); ++ scoped_guard(spinlock_irqsave, &info->lock[substream->stream]) { ++ memcpy(&info->notify[substream->stream], msg, ++ sizeof(struct rpmsg_s_msg)); ++ info->notify_updated[substream->stream] = true; ++ } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + avail = snd_pcm_playback_hw_avail(runtime); +@@ -641,7 +631,7 @@ static void imx_rpmsg_pcm_work(struct work_struct *work) + bool is_notification = false; + struct rpmsg_info *info; + struct rpmsg_msg msg; +- unsigned long flags; ++ bool updated; + + work_of_rpmsg = container_of(work, struct work_of_rpmsg, work); + info = work_of_rpmsg->info; +@@ -652,25 +642,26 @@ static void imx_rpmsg_pcm_work(struct work_struct *work) + * enough data in M core side, need to let M core know + * data is updated immediately. + */ +- spin_lock_irqsave(&info->lock[TX], flags); +- if (info->notify_updated[TX]) { +- memcpy(&msg, &info->notify[TX], sizeof(struct rpmsg_s_msg)); +- info->notify_updated[TX] = false; +- spin_unlock_irqrestore(&info->lock[TX], flags); +- info->send_message(&msg, info); +- } else { +- spin_unlock_irqrestore(&info->lock[TX], flags); ++ scoped_guard(spinlock_irqsave, &info->lock[TX]) { ++ updated = info->notify_updated[TX]; ++ if (updated) { ++ memcpy(&msg, &info->notify[TX], sizeof(struct rpmsg_s_msg)); ++ info->notify_updated[TX] = false; ++ } + } ++ if (updated) ++ info->send_message(&msg, info); + +- spin_lock_irqsave(&info->lock[RX], flags); +- if (info->notify_updated[RX]) { +- memcpy(&msg, &info->notify[RX], sizeof(struct rpmsg_s_msg)); +- info->notify_updated[RX] = false; +- spin_unlock_irqrestore(&info->lock[RX], flags); +- info->send_message(&msg, info); +- } else { +- spin_unlock_irqrestore(&info->lock[RX], flags); ++ scoped_guard(spinlock_irqsave, &info->lock[RX]) { ++ updated = info->notify_updated[RX]; ++ if (updated) { ++ memcpy(&msg, &info->notify[RX], sizeof(struct rpmsg_s_msg)); ++ info->notify_updated[RX] = false; ++ } + } ++ if (updated) ++ info->send_message(&msg, info); ++ + + /* Skip the notification message for it has been processed above */ + if (work_of_rpmsg->msg.s_msg.header.type == MSG_TYPE_C && +@@ -682,10 +673,10 @@ static void imx_rpmsg_pcm_work(struct work_struct *work) + info->send_message(&work_of_rpmsg->msg, info); + + /* update read index */ +- spin_lock_irqsave(&info->wq_lock, flags); +- info->work_read_index++; +- info->work_read_index %= WORK_MAX_NUM; +- spin_unlock_irqrestore(&info->wq_lock, flags); ++ scoped_guard(spinlock_irqsave, &info->wq_lock) { ++ info->work_read_index++; ++ info->work_read_index %= WORK_MAX_NUM; ++ } + } + + static int imx_rpmsg_pcm_probe(struct platform_device *pdev) +@@ -819,9 +810,9 @@ static const struct dev_pm_ops imx_rpmsg_pcm_pm_ops = { + }; + + static const struct platform_device_id imx_rpmsg_pcm_id_table[] = { +- { .name = "rpmsg-audio-channel" }, +- { .name = "rpmsg-micfil-channel" }, +- { }, ++ { .name = "rpmsg-audio-channel" }, ++ { .name = "rpmsg-micfil-channel" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, imx_rpmsg_pcm_id_table); + +diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c +--- a/sound/soc/fsl/mpc5200_dma.c ++++ b/sound/soc/fsl/mpc5200_dma.c +@@ -77,18 +77,20 @@ static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream) + { + struct psc_dma_stream *s = _psc_dma_stream; + +- spin_lock(&s->psc_dma->lock); +- /* For each finished period, dequeue the completed period buffer +- * and enqueue a new one in it's place. */ +- while (bcom_buffer_done(s->bcom_task)) { +- bcom_retrieve_buffer(s->bcom_task, NULL, NULL); ++ scoped_guard(spinlock, &s->psc_dma->lock) { ++ /* ++ * For each finished period, dequeue the completed period buffer ++ * and enqueue a new one in its place ++ */ ++ while (bcom_buffer_done(s->bcom_task)) { ++ bcom_retrieve_buffer(s->bcom_task, NULL, NULL); + +- s->period_current = (s->period_current+1) % s->runtime->periods; +- s->period_count++; ++ s->period_current = (s->period_current+1) % s->runtime->periods; ++ s->period_count++; + +- psc_dma_bcom_enqueue_next_buffer(s); ++ psc_dma_bcom_enqueue_next_buffer(s); ++ } + } +- spin_unlock(&s->psc_dma->lock); + + /* If the stream is active, then also inform the PCM middle layer + * of the period finished event. */ +@@ -116,7 +118,6 @@ static int psc_dma_trigger(struct snd_soc_component *component, + struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); + struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; + u16 imr; +- unsigned long flags; + int i; + + switch (cmd) { +@@ -135,19 +136,18 @@ static int psc_dma_trigger(struct snd_soc_component *component, + /* Fill up the bestcomm bd queue and enable DMA. + * This will begin filling the PSC's fifo. + */ +- spin_lock_irqsave(&psc_dma->lock, flags); ++ scoped_guard(spinlock_irqsave, &psc_dma->lock) { ++ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) ++ bcom_gen_bd_rx_reset(s->bcom_task); ++ else ++ bcom_gen_bd_tx_reset(s->bcom_task); + +- if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) +- bcom_gen_bd_rx_reset(s->bcom_task); +- else +- bcom_gen_bd_tx_reset(s->bcom_task); ++ for (i = 0; i < runtime->periods; i++) ++ if (!bcom_queue_full(s->bcom_task)) ++ psc_dma_bcom_enqueue_next_buffer(s); + +- for (i = 0; i < runtime->periods; i++) +- if (!bcom_queue_full(s->bcom_task)) +- psc_dma_bcom_enqueue_next_buffer(s); +- +- bcom_enable(s->bcom_task); +- spin_unlock_irqrestore(&psc_dma->lock, flags); ++ bcom_enable(s->bcom_task); ++ } + + out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); + +@@ -158,13 +158,13 @@ static int psc_dma_trigger(struct snd_soc_component *component, + substream->pstr->stream, s->period_count); + s->active = 0; + +- spin_lock_irqsave(&psc_dma->lock, flags); +- bcom_disable(s->bcom_task); +- if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) +- bcom_gen_bd_rx_reset(s->bcom_task); +- else +- bcom_gen_bd_tx_reset(s->bcom_task); +- spin_unlock_irqrestore(&psc_dma->lock, flags); ++ scoped_guard(spinlock_irqsave, &psc_dma->lock) { ++ bcom_disable(s->bcom_task); ++ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) ++ bcom_gen_bd_rx_reset(s->bcom_task); ++ else ++ bcom_gen_bd_tx_reset(s->bcom_task); ++ } + + break; + +@@ -314,35 +314,29 @@ int mpc5200_audio_dma_create(struct platform_device *op) + { + phys_addr_t fifo; + struct psc_dma *psc_dma; +- struct resource res; ++ struct resource *res; + int size, irq, rc; + const __be32 *prop; + void __iomem *regs; +- int ret; ++ ++ regs = devm_platform_get_and_ioremap_resource(op, 0, &res); ++ if (IS_ERR(regs)) ++ return PTR_ERR(regs); + + /* Fetch the registers and IRQ of the PSC */ +- irq = irq_of_parse_and_map(op->dev.of_node, 0); +- if (of_address_to_resource(op->dev.of_node, 0, &res)) { +- dev_err(&op->dev, "Missing reg property\n"); +- return -ENODEV; +- } +- regs = devm_ioremap(&op->dev, res.start, resource_size(&res)); +- if (!regs) { +- dev_err(&op->dev, "Could not map registers\n"); +- return -ENODEV; +- } ++ irq = platform_get_irq(op, 0); ++ if (irq < 0) ++ return irq; + + /* Allocate and initialize the driver private data */ +- psc_dma = kzalloc_obj(*psc_dma); ++ psc_dma = devm_kzalloc(&op->dev, sizeof(*psc_dma), GFP_KERNEL); + if (!psc_dma) + return -ENOMEM; + + /* Get the PSC ID */ + prop = of_get_property(op->dev.of_node, "cell-index", &size); +- if (!prop || size < sizeof *prop) { +- ret = -ENODEV; +- goto out_free; +- } ++ if (!prop || size < sizeof *prop) ++ return -ENODEV; + + spin_lock_init(&psc_dma->lock); + mutex_init(&psc_dma->mutex); +@@ -357,7 +351,7 @@ int mpc5200_audio_dma_create(struct platform_device *op) + + /* Find the address of the fifo data registers and setup the + * DMA tasks */ +- fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32); ++ fifo = res->start + offsetof(struct mpc52xx_psc, buffer.buffer_32); + psc_dma->capture.bcom_task = + bcom_psc_gen_bd_rx_init(psc_dma->id, 10, fifo, 512); + psc_dma->playback.bcom_task = +@@ -365,8 +359,7 @@ int mpc5200_audio_dma_create(struct platform_device *op) + if (!psc_dma->capture.bcom_task || + !psc_dma->playback.bcom_task) { + dev_err(&op->dev, "Could not allocate bestcomm tasks\n"); +- ret = -ENODEV; +- goto out_free; ++ return -ENODEV; + } + + /* Disable all interrupts and reset the PSC */ +@@ -399,16 +392,14 @@ int mpc5200_audio_dma_create(struct platform_device *op) + psc_dma->capture.irq = + bcom_get_task_irq(psc_dma->capture.bcom_task); + +- rc = request_irq(psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED, ++ rc = devm_request_irq(&op->dev, psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED, + "psc-dma-status", psc_dma); +- rc |= request_irq(psc_dma->capture.irq, &psc_dma_bcom_irq, IRQF_SHARED, ++ rc |= devm_request_irq(&op->dev, psc_dma->capture.irq, &psc_dma_bcom_irq, IRQF_SHARED, + "psc-dma-capture", &psc_dma->capture); +- rc |= request_irq(psc_dma->playback.irq, &psc_dma_bcom_irq, IRQF_SHARED, ++ rc |= devm_request_irq(&op->dev, psc_dma->playback.irq, &psc_dma_bcom_irq, IRQF_SHARED, + "psc-dma-playback", &psc_dma->playback); +- if (rc) { +- ret = -ENODEV; +- goto out_irq; +- } ++ if (rc) ++ return -ENODEV; + + /* Save what we've done so it can be found again later */ + dev_set_drvdata(&op->dev, psc_dma); +@@ -416,13 +407,6 @@ int mpc5200_audio_dma_create(struct platform_device *op) + /* Tell the ASoC OF helpers about it */ + return devm_snd_soc_register_component(&op->dev, + &mpc5200_audio_dma_component, NULL, 0); +-out_irq: +- free_irq(psc_dma->irq, psc_dma); +- free_irq(psc_dma->capture.irq, &psc_dma->capture); +- free_irq(psc_dma->playback.irq, &psc_dma->playback); +-out_free: +- kfree(psc_dma); +- return ret; + } + EXPORT_SYMBOL_GPL(mpc5200_audio_dma_create); + +@@ -435,12 +419,6 @@ int mpc5200_audio_dma_destroy(struct platform_device *op) + bcom_gen_bd_rx_release(psc_dma->capture.bcom_task); + bcom_gen_bd_tx_release(psc_dma->playback.bcom_task); + +- /* Release irqs */ +- free_irq(psc_dma->irq, psc_dma); +- free_irq(psc_dma->capture.irq, &psc_dma->capture); +- free_irq(psc_dma->playback.irq, &psc_dma->playback); +- +- kfree(psc_dma); + dev_set_drvdata(&op->dev, NULL); + + return 0; +diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c +--- a/sound/soc/fsl/mpc5200_psc_ac97.c ++++ b/sound/soc/fsl/mpc5200_psc_ac97.c +@@ -30,14 +30,13 @@ static unsigned short psc_ac97_read(struct snd_ac97 *ac97, unsigned short reg) + int status; + unsigned int val; + +- mutex_lock(&psc_dma->mutex); ++ guard(mutex)(&psc_dma->mutex); + + /* Wait for command send status zero = ready */ + status = spin_event_timeout(!(in_be16(&psc_dma->psc_regs->sr_csr.status) & + MPC52xx_PSC_SR_CMDSEND), 100, 0); + if (status == 0) { + pr_err("timeout on ac97 bus (rdy)\n"); +- mutex_unlock(&psc_dma->mutex); + return -ENODEV; + } + +@@ -53,19 +52,16 @@ static unsigned short psc_ac97_read(struct snd_ac97 *ac97, unsigned short reg) + if (status == 0) { + pr_err("timeout on ac97 read (val) %x\n", + in_be16(&psc_dma->psc_regs->sr_csr.status)); +- mutex_unlock(&psc_dma->mutex); + return -ENODEV; + } + /* Get the data */ + val = in_be32(&psc_dma->psc_regs->ac97_data); + if (((val >> 24) & 0x7f) != reg) { + pr_err("reg echo error on ac97 read\n"); +- mutex_unlock(&psc_dma->mutex); + return -ENODEV; + } + val = (val >> 8) & 0xffff; + +- mutex_unlock(&psc_dma->mutex); + return (unsigned short) val; + } + +@@ -74,52 +70,46 @@ static void psc_ac97_write(struct snd_ac97 *ac97, + { + int status; + +- mutex_lock(&psc_dma->mutex); ++ guard(mutex)(&psc_dma->mutex); + + /* Wait for command status zero = ready */ + status = spin_event_timeout(!(in_be16(&psc_dma->psc_regs->sr_csr.status) & + MPC52xx_PSC_SR_CMDSEND), 100, 0); + if (status == 0) { + pr_err("timeout on ac97 bus (write)\n"); +- goto out; ++ return; + } + /* Write data */ + out_be32(&psc_dma->psc_regs->ac97_cmd, + ((reg & 0x7f) << 24) | (val << 8)); +- +- out: +- mutex_unlock(&psc_dma->mutex); + } + + static void psc_ac97_warm_reset(struct snd_ac97 *ac97) + { + struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; + +- mutex_lock(&psc_dma->mutex); ++ guard(mutex)(&psc_dma->mutex); + + out_be32(®s->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_AWR); + udelay(3); + out_be32(®s->sicr, psc_dma->sicr); +- +- mutex_unlock(&psc_dma->mutex); + } + + static void psc_ac97_cold_reset(struct snd_ac97 *ac97) + { + struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; + +- mutex_lock(&psc_dma->mutex); +- dev_dbg(psc_dma->dev, "cold reset\n"); ++ scoped_guard(mutex, &psc_dma->mutex) { ++ dev_dbg(psc_dma->dev, "cold reset\n"); + +- mpc5200_psc_ac97_gpio_reset(psc_dma->id); ++ mpc5200_psc_ac97_gpio_reset(psc_dma->id); + +- /* Notify the PSC that a reset has occurred */ +- out_be32(®s->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_ACRB); ++ /* Notify the PSC that a reset has occurred */ ++ out_be32(®s->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_ACRB); + +- /* Re-enable RX and TX */ +- out_8(®s->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); +- +- mutex_unlock(&psc_dma->mutex); ++ /* Re-enable RX and TX */ ++ out_8(®s->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); ++ } + + usleep_range(1000, 2000); + psc_ac97_warm_reset(ac97); +@@ -286,7 +276,7 @@ static int psc_ac97_of_probe(struct platform_device *op) + return rc; + } + +- rc = snd_soc_register_component(&op->dev, &psc_ac97_component, ++ rc = devm_snd_soc_register_component(&op->dev, &psc_ac97_component, + psc_ac97_dai, ARRAY_SIZE(psc_ac97_dai)); + if (rc != 0) { + dev_err(&op->dev, "Failed to register DAI\n"); +@@ -312,8 +302,6 @@ static int psc_ac97_of_probe(struct platform_device *op) + static void psc_ac97_of_remove(struct platform_device *op) + { + mpc5200_audio_dma_destroy(op); +- snd_soc_unregister_component(&op->dev); +- snd_soc_set_ac97_ops(NULL); + } + + /* Match table for of_platform binding */ +diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c +--- a/sound/soc/fsl/mpc5200_psc_i2s.c ++++ b/sound/soc/fsl/mpc5200_psc_i2s.c +@@ -79,9 +79,12 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream, + * and we don't care about the frequency. Return an error if the direction + * is not SND_SOC_CLOCK_IN. + * ++ * @cpu_dai: DAI runtime data pointer + * @clk_id: reserved, should be zero + * @freq: the frequency of the given clock ID, currently ignored + * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master) ++ * ++ * Returns: %0 on success or %-EINVAL on failure. + */ + static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +@@ -101,7 +104,10 @@ static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, + * This driver only supports I2S mode. Return an error if the format is + * not SND_SOC_DAIFMT_I2S. + * ++ * @cpu_dai: DAI runtime data pointer + * @format: one of SND_SOC_DAIFMT_xxx ++ * ++ * Returns: %0 on success or %-EINVAL on failure. + */ + static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) + { +@@ -119,7 +125,7 @@ static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) + */ + + /** +- * psc_i2s_dai_template: template CPU Digital Audio Interface ++ * var psc_i2s_dai_ops - template CPU Digital Audio Interface + */ + static const struct snd_soc_dai_ops psc_i2s_dai_ops = { + .hw_params = psc_i2s_hw_params, +@@ -166,7 +172,7 @@ static int psc_i2s_of_probe(struct platform_device *op) + if (rc != 0) + return rc; + +- rc = snd_soc_register_component(&op->dev, &psc_i2s_component, ++ rc = devm_snd_soc_register_component(&op->dev, &psc_i2s_component, + psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai)); + if (rc != 0) { + pr_err("Failed to register DAI\n"); +@@ -213,7 +219,6 @@ static int psc_i2s_of_probe(struct platform_device *op) + static void psc_i2s_of_remove(struct platform_device *op) + { + mpc5200_audio_dma_destroy(op); +- snd_soc_unregister_component(&op->dev); + } + + /* Match table for of_platform binding */ +diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c +--- a/sound/soc/fsl/p1022_ds.c ++++ b/sound/soc/fsl/p1022_ds.c +@@ -73,6 +73,8 @@ struct machine_data { + char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */ + }; + ++#define card_to_mdata(_card) container_of(_card, struct machine_data, card) ++ + /** + * p1022_ds_machine_probe: initialize the board + * +@@ -82,8 +84,7 @@ struct machine_data { + */ + static int p1022_ds_machine_probe(struct snd_soc_card *card) + { +- struct machine_data *mdata = +- container_of(card, struct machine_data, card); ++ struct machine_data *mdata = card_to_mdata(card); + struct ccsr_guts __iomem *guts; + + guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); +@@ -122,8 +123,7 @@ static int p1022_ds_machine_probe(struct snd_soc_card *card) + static int p1022_ds_startup(struct snd_pcm_substream *substream) + { + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); +- struct machine_data *mdata = +- container_of(rtd->card, struct machine_data, card); ++ struct machine_data *mdata = card_to_mdata(rtd->card); + struct device *dev = rtd->card->dev; + int ret = 0; + +@@ -156,8 +156,7 @@ static int p1022_ds_startup(struct snd_pcm_substream *substream) + */ + static int p1022_ds_machine_remove(struct snd_soc_card *card) + { +- struct machine_data *mdata = +- container_of(card, struct machine_data, card); ++ struct machine_data *mdata = card_to_mdata(card); + struct ccsr_guts __iomem *guts; + + guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); +@@ -399,8 +398,7 @@ static int p1022_ds_probe(struct platform_device *pdev) + static void p1022_ds_remove(struct platform_device *pdev) + { + struct snd_soc_card *card = platform_get_drvdata(pdev); +- struct machine_data *mdata = +- container_of(card, struct machine_data, card); ++ struct machine_data *mdata = card_to_mdata(card); + + snd_soc_unregister_card(card); + kfree(mdata); +diff --git a/sound/soc/fsl/p1022_rdk.c b/sound/soc/fsl/p1022_rdk.c +--- a/sound/soc/fsl/p1022_rdk.c ++++ b/sound/soc/fsl/p1022_rdk.c +@@ -79,6 +79,8 @@ struct machine_data { + char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */ + }; + ++#define card_to_mdata(_card) container_of(_card, struct machine_data, card) ++ + /** + * p1022_rdk_machine_probe - initialize the board + * @card: ASoC card instance +@@ -91,8 +93,7 @@ struct machine_data { + */ + static int p1022_rdk_machine_probe(struct snd_soc_card *card) + { +- struct machine_data *mdata = +- container_of(card, struct machine_data, card); ++ struct machine_data *mdata = card_to_mdata(card); + struct ccsr_guts __iomem *guts; + + guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); +@@ -134,8 +135,7 @@ static int p1022_rdk_machine_probe(struct snd_soc_card *card) + static int p1022_rdk_startup(struct snd_pcm_substream *substream) + { + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); +- struct machine_data *mdata = +- container_of(rtd->card, struct machine_data, card); ++ struct machine_data *mdata = card_to_mdata(rtd->card); + struct device *dev = rtd->card->dev; + int ret = 0; + +@@ -169,8 +169,7 @@ static int p1022_rdk_startup(struct snd_pcm_substream *substream) + */ + static int p1022_rdk_machine_remove(struct snd_soc_card *card) + { +- struct machine_data *mdata = +- container_of(card, struct machine_data, card); ++ struct machine_data *mdata = card_to_mdata(card); + struct ccsr_guts __iomem *guts; + + guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); +@@ -361,8 +360,7 @@ static int p1022_rdk_probe(struct platform_device *pdev) + static void p1022_rdk_remove(struct platform_device *pdev) + { + struct snd_soc_card *card = platform_get_drvdata(pdev); +- struct machine_data *mdata = +- container_of(card, struct machine_data, card); ++ struct machine_data *mdata = card_to_mdata(card); + + snd_soc_unregister_card(card); + kfree(mdata); +diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c +--- a/sound/soc/generic/audio-graph-card.c ++++ b/sound/soc/generic/audio-graph-card.c +@@ -579,11 +579,11 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) + goto end; + } + +- ret = simple_util_parse_widgets(card, NULL); ++ ret = simple_util_parse_widgets(priv, NULL); + if (ret < 0) + goto end; + +- ret = simple_util_parse_routing(card, NULL); ++ ret = simple_util_parse_routing(priv, NULL); + if (ret < 0) + goto end; + +@@ -594,6 +594,7 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) + if (ret < 0) + goto err; + ++ /* Card name should be set after graph_for_each_link() */ + ret = simple_util_parse_card_name(priv, NULL); + if (ret < 0) + goto err; +@@ -603,14 +604,13 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) + simple_util_debug_info(priv); + + ret = devm_snd_soc_register_card(dev, card); +- if (ret < 0) +- goto err; +- +- return 0; + err: +- simple_util_clean_reference(card); ++ if (ret < 0) { ++ simple_util_clean_reference(priv); ++ return dev_err_probe(dev, ret, "parse error\n"); ++ } + end: +- return dev_err_probe(dev, ret, "parse error\n"); ++ return graph_ret(priv, ret); + } + EXPORT_SYMBOL_GPL(audio_graph_parse_of); + +diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c +--- a/sound/soc/generic/audio-graph-card2.c ++++ b/sound/soc/generic/audio-graph-card2.c +@@ -1303,11 +1303,11 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, + struct graph2_custom_hooks *hooks) + { + struct snd_soc_card *card = simple_priv_to_card(priv); +- int ret; ++ int ret = -ENOMEM; + + struct link_info *li __free(kfree) = kzalloc_obj(*li); + if (!li) +- return -ENOMEM; ++ goto end; + + card->probe = graph_util_card_probe; + card->owner = THIS_MODULE; +@@ -1316,31 +1316,35 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, + if ((hooks) && (hooks)->hook_pre) { + ret = (hooks)->hook_pre(priv); + if (ret < 0) +- goto err; ++ goto end; + } + + ret = graph_for_each_link(priv, hooks, li, graph_count); + if (!li->link) + ret = -EINVAL; + if (ret < 0) +- goto err; ++ goto end; + + ret = simple_util_init_priv(priv, li); + if (ret < 0) +- goto err; ++ goto end; + + priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW); + if (IS_ERR(priv->pa_gpio)) { + ret = PTR_ERR(priv->pa_gpio); + dev_err(dev, "failed to get amplifier gpio: %d\n", ret); +- goto err; ++ goto end; + } + +- ret = simple_util_parse_widgets(card, NULL); ++ ret = simple_util_parse_widgets(priv, NULL); + if (ret < 0) +- goto err; ++ goto end; + +- ret = simple_util_parse_routing(card, NULL); ++ ret = simple_util_parse_routing(priv, NULL); ++ if (ret < 0) ++ goto end; ++ ++ ret = simple_util_parse_aux_devs(priv, NULL); + if (ret < 0) + goto err; + +@@ -1349,6 +1353,7 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, + if (ret < 0) + goto err; + ++ /* Card name should be set after graph_for_each_link() */ + ret = simple_util_parse_card_name(priv, NULL); + if (ret < 0) + goto err; +@@ -1363,15 +1368,13 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, + + simple_util_debug_info(priv); + +- ret = snd_soc_of_parse_aux_devs(card, "aux-devs"); +- if (ret < 0) +- goto err; +- + ret = devm_snd_soc_register_card(dev, card); + err: +- if (ret < 0) +- dev_err_probe(dev, ret, "parse error\n"); +- ++ if (ret < 0) { ++ simple_util_clean_reference(priv); ++ return dev_err_probe(dev, ret, "parse error\n"); ++ } ++end: + return graph_ret(priv, ret); + } + EXPORT_SYMBOL_GPL(audio_graph2_parse_of); +diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c +--- a/sound/soc/generic/simple-card-utils.c ++++ b/sound/soc/generic/simple-card-utils.c +@@ -216,6 +216,27 @@ int simple_util_set_dailink_name(struct simple_util_priv *priv, + } + EXPORT_SYMBOL_GPL(simple_util_set_dailink_name); + ++int simple_util_parse_property(struct simple_util_priv *priv, ++ int (*func)(struct snd_soc_card *card, const char *propname), ++ char *prefix, char *property) ++{ ++ struct snd_soc_card *card = simple_priv_to_card(priv); ++ struct device_node *node = card->dev->of_node; ++ char prop[128]; ++ ++ if (!prefix) ++ prefix = ""; ++ ++ snprintf(prop, sizeof(prop), "%s%s", prefix, property); ++ ++ /* no property is not error */ ++ if (!of_property_present(node, prop)) ++ return 0; ++ ++ return func(card, prop); ++} ++EXPORT_SYMBOL_GPL(simple_util_parse_property); ++ + int simple_util_parse_card_name(struct simple_util_priv *priv, + char *prefix) + { +@@ -731,11 +752,12 @@ void simple_util_canonicalize_cpu(struct snd_soc_dai_link_component *cpus, + } + EXPORT_SYMBOL_GPL(simple_util_canonicalize_cpu); + +-void simple_util_clean_reference(struct snd_soc_card *card) ++void simple_util_clean_reference(struct simple_util_priv *priv) + { + struct snd_soc_dai_link *dai_link; + struct snd_soc_dai_link_component *cpu; + struct snd_soc_dai_link_component *codec; ++ struct snd_soc_card *card = simple_priv_to_card(priv); + int i, j; + + for_each_card_prelinks(card, i, dai_link) { +@@ -747,57 +769,6 @@ void simple_util_clean_reference(struct snd_soc_card *card) + } + EXPORT_SYMBOL_GPL(simple_util_clean_reference); + +-int simple_util_parse_routing(struct snd_soc_card *card, +- char *prefix) +-{ +- struct device_node *node = card->dev->of_node; +- char prop[128]; +- +- if (!prefix) +- prefix = ""; +- +- snprintf(prop, sizeof(prop), "%s%s", prefix, "routing"); +- +- if (!of_property_present(node, prop)) +- return 0; +- +- return snd_soc_of_parse_audio_routing(card, prop); +-} +-EXPORT_SYMBOL_GPL(simple_util_parse_routing); +- +-int simple_util_parse_widgets(struct snd_soc_card *card, +- char *prefix) +-{ +- struct device_node *node = card->dev->of_node; +- char prop[128]; +- +- if (!prefix) +- prefix = ""; +- +- snprintf(prop, sizeof(prop), "%s%s", prefix, "widgets"); +- +- if (of_property_present(node, prop)) +- return snd_soc_of_parse_audio_simple_widgets(card, prop); +- +- /* no widgets is not error */ +- return 0; +-} +-EXPORT_SYMBOL_GPL(simple_util_parse_widgets); +- +-int simple_util_parse_pin_switches(struct snd_soc_card *card, +- char *prefix) +-{ +- char prop[128]; +- +- if (!prefix) +- prefix = ""; +- +- snprintf(prop, sizeof(prop), "%s%s", prefix, "pin-switches"); +- +- return snd_soc_of_parse_pin_switches(card, prop); +-} +-EXPORT_SYMBOL_GPL(simple_util_parse_pin_switches); +- + int simple_util_init_jack(struct snd_soc_card *card, + struct simple_util_jack *sjack, + int is_hp, char *prefix, +@@ -854,9 +825,9 @@ int simple_util_init_jack(struct snd_soc_card *card, + } + EXPORT_SYMBOL_GPL(simple_util_init_jack); + +-int simple_util_init_aux_jacks(struct simple_util_priv *priv, char *prefix) ++int simple_util_init_aux_jacks(struct snd_soc_card *card, char *prefix) + { +- struct snd_soc_card *card = simple_priv_to_card(priv); ++ struct simple_util_priv *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component; + int found_jack_index = 0; + int type = 0; +@@ -1026,8 +997,9 @@ EXPORT_SYMBOL_GPL(simple_util_init_priv); + void simple_util_remove(struct platform_device *pdev) + { + struct snd_soc_card *card = platform_get_drvdata(pdev); ++ struct simple_util_priv *priv = snd_soc_card_get_drvdata(card); + +- simple_util_clean_reference(card); ++ simple_util_clean_reference(priv); + } + EXPORT_SYMBOL_GPL(simple_util_remove); + +diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c +--- a/sound/soc/generic/simple-card.c ++++ b/sound/soc/generic/simple-card.c +@@ -517,44 +517,6 @@ static int simple_populate_aux(struct simple_util_priv *priv) + return simple_ret(priv, ret); + } + +-static int simple_parse_of(struct simple_util_priv *priv, struct link_info *li) +-{ +- struct snd_soc_card *card = simple_priv_to_card(priv); +- int ret; +- +- ret = simple_util_parse_widgets(card, PREFIX); +- if (ret < 0) +- goto end; +- +- ret = simple_util_parse_routing(card, PREFIX); +- if (ret < 0) +- goto end; +- +- ret = simple_util_parse_pin_switches(card, PREFIX); +- if (ret < 0) +- goto end; +- +- /* Single/Muti DAI link(s) & New style of DT node */ +- memset(li, 0, sizeof(*li)); +- ret = simple_for_each_link(priv, li, +- simple_dai_link_of, +- simple_dai_link_of_dpcm); +- if (ret < 0) +- goto end; +- +- ret = simple_util_parse_card_name(priv, PREFIX); +- if (ret < 0) +- goto end; +- +- ret = simple_populate_aux(priv); +- if (ret < 0) +- goto end; +- +- ret = snd_soc_of_parse_aux_devs(card, PREFIX "aux-devs"); +-end: +- return simple_ret(priv, ret); +-} +- + static int simple_count_noml(struct simple_util_priv *priv, + struct device_node *np, + struct device_node *codec, +@@ -699,28 +661,19 @@ static int simple_soc_probe(struct snd_soc_card *card) + if (ret < 0) + goto end; + +- ret = simple_util_init_aux_jacks(priv, PREFIX); ++ ret = simple_util_init_aux_jacks(card, PREFIX); + end: + return simple_ret(priv, ret); + } + +-static int simple_probe(struct platform_device *pdev) ++static int simple_parse_of(struct simple_util_priv *priv) + { +- struct simple_util_priv *priv; +- struct device *dev = &pdev->dev; +- struct snd_soc_card *card; +- int ret; ++ struct snd_soc_card *card = simple_priv_to_card(priv); ++ struct device *dev = card->dev; ++ int ret = -EINVAL; + +- /* Allocate the private data and the DAI link array */ +- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- card = simple_priv_to_card(priv); +- card->owner = THIS_MODULE; +- card->dev = dev; +- card->probe = simple_soc_probe; +- card->driver_name = "simple-card"; ++ if (!dev) ++ return simple_ret(priv, ret); + + ret = -ENOMEM; + struct link_info *li __free(kfree) = kzalloc_obj(*li); +@@ -739,25 +692,71 @@ static int simple_probe(struct platform_device *pdev) + if (ret < 0) + goto end; + +- ret = simple_parse_of(priv, li); +- if (ret < 0) { +- dev_err_probe(dev, ret, "parse error\n"); ++ ret = simple_util_parse_widgets(priv, PREFIX); ++ if (ret < 0) ++ goto end; ++ ++ ret = simple_util_parse_routing(priv, PREFIX); ++ if (ret < 0) ++ goto end; ++ ++ ret = simple_util_parse_pin_switches(priv, PREFIX); ++ if (ret < 0) ++ goto end; ++ ++ ret = simple_util_parse_aux_devs(priv, PREFIX); ++ if (ret < 0) ++ goto err; ++ ++ /* Single/Muti DAI link(s) & New style of DT node */ ++ memset(li, 0, sizeof(*li)); ++ ret = simple_for_each_link(priv, li, ++ simple_dai_link_of, ++ simple_dai_link_of_dpcm); ++ if (ret < 0) ++ goto err; ++ ++ /* Card name should be set after simple_for_each_link() */ ++ ret = simple_util_parse_card_name(priv, PREFIX); ++ if (ret < 0) ++ goto err; ++ ++ ret = simple_populate_aux(priv); ++ if (ret < 0) + goto err; +- } + + snd_soc_card_set_drvdata(card, priv); + + simple_util_debug_info(priv); + + ret = devm_snd_soc_register_card(dev, card); +- if (ret < 0) +- goto err; +- +- return 0; + err: +- simple_util_clean_reference(card); ++ if (ret < 0) { ++ simple_util_clean_reference(priv); ++ return dev_err_probe(dev, ret, "parse error\n"); ++ } + end: +- return dev_err_probe(dev, ret, "parse error\n"); ++ return simple_ret(priv, ret); ++} ++ ++static int simple_probe(struct platform_device *pdev) ++{ ++ struct simple_util_priv *priv; ++ struct device *dev = &pdev->dev; ++ struct snd_soc_card *card; ++ ++ /* Allocate the private data and the DAI link array */ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ card = simple_priv_to_card(priv); ++ card->owner = THIS_MODULE; ++ card->dev = dev; ++ card->probe = simple_soc_probe; ++ card->driver_name = "simple-card"; ++ ++ return simple_parse_of(priv); + } + + static const struct of_device_id simple_of_match[] = { +diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c +--- a/sound/soc/intel/atom/sst-atom-controls.c ++++ b/sound/soc/intel/atom/sst-atom-controls.c +@@ -14,6 +14,7 @@ + */ + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + ++#include + #include + #include + #include +@@ -73,14 +74,10 @@ static int sst_fill_and_send_cmd(struct sst_data *drv, + u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id, + void *cmd_data, u16 len) + { +- int ret; ++ guard(mutex)(&drv->lock); + +- mutex_lock(&drv->lock); +- ret = sst_fill_and_send_cmd_unlocked(drv, ipc_msg, block, +- task_id, pipe_id, cmd_data, len); +- mutex_unlock(&drv->lock); +- +- return ret; ++ return sst_fill_and_send_cmd_unlocked(drv, ipc_msg, block, ++ task_id, pipe_id, cmd_data, len); + } + + /* +@@ -167,7 +164,7 @@ static int sst_slot_get(struct snd_kcontrol *kcontrol, + unsigned int val, mux; + u8 *map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map; + +- mutex_lock(&drv->lock); ++ guard(mutex)(&drv->lock); + val = 1 << ctl_no; + /* search which slot/channel has this bit set - there should be only one */ + for (mux = e->max; mux > 0; mux--) +@@ -175,7 +172,6 @@ static int sst_slot_get(struct snd_kcontrol *kcontrol, + break; + + ucontrol->value.enumerated.item[0] = mux; +- mutex_unlock(&drv->lock); + + dev_dbg(c->dev, "%s - %s map = %#x\n", + is_tx ? "tx channel" : "rx slot", +@@ -235,7 +231,7 @@ static int sst_slot_put(struct snd_kcontrol *kcontrol, + if (mux > e->max - 1) + return -EINVAL; + +- mutex_lock(&drv->lock); ++ guard(mutex)(&drv->lock); + /* first clear all registers of this bit */ + for (i = 0; i < e->max; i++) + map[i] &= ~val; +@@ -244,7 +240,6 @@ static int sst_slot_put(struct snd_kcontrol *kcontrol, + /* kctl set to 'none' and we reset the bits so send IPC */ + ret = sst_check_and_send_slot_map(drv, kcontrol); + +- mutex_unlock(&drv->lock); + return ret; + } + +@@ -258,7 +253,6 @@ static int sst_slot_put(struct snd_kcontrol *kcontrol, + + ret = sst_check_and_send_slot_map(drv, kcontrol); + +- mutex_unlock(&drv->lock); + return ret; + } + +@@ -354,13 +348,12 @@ static int sst_algo_control_set(struct snd_kcontrol *kcontrol, + struct sst_algo_control *bc = (void *)kcontrol->private_value; + + dev_dbg(cmpnt->dev, "control_name=%s\n", kcontrol->id.name); +- mutex_lock(&drv->lock); ++ guard(mutex)(&drv->lock); + switch (bc->type) { + case SST_ALGO_PARAMS: + memcpy(bc->params, ucontrol->value.bytes.data, bc->max); + break; + default: +- mutex_unlock(&drv->lock); + dev_err(cmpnt->dev, "Invalid Input- algo type:%d\n", + bc->type); + return -EINVAL; +@@ -368,7 +361,6 @@ static int sst_algo_control_set(struct snd_kcontrol *kcontrol, + /*if pipe is enabled, need to send the algo params from here*/ + if (bc->w && bc->w->power) + ret = sst_send_algo_cmd(drv, bc); +- mutex_unlock(&drv->lock); + + return ret; + } +@@ -475,7 +467,7 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, + struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value; + struct sst_gain_value *gv = mc->gain_val; + +- mutex_lock(&drv->lock); ++ guard(mutex)(&drv->lock); + + switch (mc->type) { + case SST_GAIN_TLV: +@@ -497,7 +489,6 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, + break; + + default: +- mutex_unlock(&drv->lock); + dev_err(cmpnt->dev, "Invalid Input- gain type:%d\n", + mc->type); + return -EINVAL; +@@ -506,7 +497,6 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, + if (mc->w && mc->w->power) + ret = sst_send_gain_cmd(drv, gv, mc->task_id, + mc->pipe_id | mc->instance_id, mc->module_id, 0); +- mutex_unlock(&drv->lock); + + return ret; + } +@@ -521,10 +511,9 @@ static int sst_send_pipe_module_params(struct snd_soc_dapm_widget *w, + struct sst_data *drv = snd_soc_component_get_drvdata(c); + struct sst_ids *ids = w->priv; + +- mutex_lock(&drv->lock); ++ guard(mutex)(&drv->lock); + sst_find_and_send_pipe_algo(drv, w->name, ids); + sst_set_pipe_gain(ids, drv, 0); +- mutex_unlock(&drv->lock); + + return 0; + } +@@ -761,27 +750,29 @@ int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable) + return ret; + } + +- mutex_lock(&drv->lock); +- if (enable) +- timer_usage++; +- else +- timer_usage--; +- +- /* +- * Send the command only if this call is the first enable or last +- * disable +- */ +- if ((enable && (timer_usage == 1)) || +- (!enable && (timer_usage == 0))) { +- ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD, +- SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd, +- sizeof(cmd.header) + cmd.header.length); +- if (ret && enable) { ++ scoped_guard(mutex, &drv->lock) { ++ if (enable) ++ timer_usage++; ++ else + timer_usage--; +- enable = false; ++ ++ /* ++ * Send the command only if this call is the first enable or last ++ * disable ++ */ ++ if ((enable && timer_usage == 1) || ++ (!enable && timer_usage == 0)) { ++ ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD, ++ SST_FLAG_BLOCKED, ++ SST_TASK_SBA, 0, &cmd, ++ sizeof(cmd.header) + ++ cmd.header.length); ++ if (ret && enable) { ++ timer_usage--; ++ enable = false; ++ } + } + } +- mutex_unlock(&drv->lock); + + if (!enable) + sst->ops->power(sst->dev, false); +diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c +--- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c ++++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c +@@ -11,6 +11,7 @@ + */ + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + ++#include + #include + #include + #include +@@ -32,7 +33,7 @@ int sst_register_dsp(struct sst_device *dev) + return -EINVAL; + if (!try_module_get(dev->dev->driver->owner)) + return -ENODEV; +- mutex_lock(&sst_lock); ++ guard(mutex)(&sst_lock); + if (sst) { + dev_err(dev->dev, "we already have a device %s\n", sst->name); + module_put(dev->dev->driver->owner); +@@ -41,7 +42,7 @@ int sst_register_dsp(struct sst_device *dev) + } + dev_dbg(dev->dev, "registering device %s\n", dev->name); + sst = dev; +- mutex_unlock(&sst_lock); ++ + return 0; + } + EXPORT_SYMBOL_GPL(sst_register_dsp); +@@ -53,17 +54,15 @@ int sst_unregister_dsp(struct sst_device *dev) + if (dev != sst) + return -EINVAL; + +- mutex_lock(&sst_lock); ++ guard(mutex)(&sst_lock); + +- if (!sst) { +- mutex_unlock(&sst_lock); ++ if (!sst) + return -EIO; +- } + + module_put(sst->dev->driver->owner); + dev_dbg(dev->dev, "unreg %s\n", sst->name); + sst = NULL; +- mutex_unlock(&sst_lock); ++ + return 0; + } + EXPORT_SYMBOL_GPL(sst_unregister_dsp); +@@ -103,21 +102,14 @@ static int sst_media_digital_mute(struct snd_soc_dai *dai, int mute, int stream) + void sst_set_stream_status(struct sst_runtime_stream *stream, + int state) + { +- unsigned long flags; +- spin_lock_irqsave(&stream->status_lock, flags); ++ guard(spinlock_irqsave)(&stream->status_lock); + stream->stream_status = state; +- spin_unlock_irqrestore(&stream->status_lock, flags); + } + + static inline int sst_get_stream_status(struct sst_runtime_stream *stream) + { +- int state; +- unsigned long flags; +- +- spin_lock_irqsave(&stream->status_lock, flags); +- state = stream->stream_status; +- spin_unlock_irqrestore(&stream->status_lock, flags); +- return state; ++ guard(spinlock_irqsave)(&stream->status_lock); ++ return stream->stream_status; + } + + static void sst_fill_alloc_params(struct snd_pcm_substream *substream, +@@ -244,12 +236,7 @@ static int sst_platform_alloc_stream(struct snd_pcm_substream *substream, + + stream->stream_info.str_id = str_params.stream_id; + +- ret_val = stream->ops->open(sst->dev, &str_params); +- if (ret_val <= 0) +- return ret_val; +- +- +- return ret_val; ++ return stream->ops->open(sst->dev, &str_params); + } + + static void sst_period_elapsed(void *arg) +@@ -304,7 +291,7 @@ static int sst_media_open(struct snd_pcm_substream *substream, + { + int ret_val = 0; + struct snd_pcm_runtime *runtime = substream->runtime; +- struct sst_runtime_stream *stream; ++ struct sst_runtime_stream *stream __free(kfree) = NULL; + + stream = kzalloc_obj(*stream); + if (!stream) +@@ -312,15 +299,14 @@ static int sst_media_open(struct snd_pcm_substream *substream, + spin_lock_init(&stream->status_lock); + + /* get the sst ops */ +- mutex_lock(&sst_lock); +- if (!sst || +- !try_module_get(sst->dev->driver->owner)) { +- dev_err(dai->dev, "no device available to run\n"); +- ret_val = -ENODEV; +- goto out_ops; ++ scoped_guard(mutex, &sst_lock) { ++ if (!sst || ++ !try_module_get(sst->dev->driver->owner)) { ++ dev_err(dai->dev, "no device available to run\n"); ++ return -ENODEV; ++ } ++ stream->ops = sst->ops; + } +- stream->ops = sst->ops; +- mutex_unlock(&sst_lock); + + stream->stream_info.str_id = 0; + +@@ -330,7 +316,7 @@ static int sst_media_open(struct snd_pcm_substream *substream, + + ret_val = power_up_sst(stream); + if (ret_val < 0) +- goto out_power_up; ++ return ret_val; + + /* + * Make sure the period to be multiple of 1ms to align the +@@ -347,12 +333,14 @@ static int sst_media_open(struct snd_pcm_substream *substream, + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIODS, 2); + +- return snd_pcm_hw_constraint_integer(runtime, +- SNDRV_PCM_HW_PARAM_PERIODS); +-out_ops: +- mutex_unlock(&sst_lock); +-out_power_up: +- kfree(stream); ++ ret_val = snd_pcm_hw_constraint_integer(runtime, ++ SNDRV_PCM_HW_PARAM_PERIODS); ++ ++ if (ret_val < 0) ++ return ret_val; ++ ++ stream = NULL; ++ + return ret_val; + } + +diff --git a/sound/soc/intel/atom/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c +--- a/sound/soc/intel/atom/sst/sst_ipc.c ++++ b/sound/soc/intel/atom/sst/sst_ipc.c +@@ -11,6 +11,7 @@ + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ ++#include + #include + #include + #include +@@ -180,9 +181,8 @@ void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx) + union interrupt_reg_mrfld isr; + union interrupt_reg_mrfld imr; + union ipc_header_mrfld clear_ipc; +- unsigned long irq_flags; + +- spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags); ++ guard(spinlock_irqsave)(&sst_drv_ctx->ipc_spin_lock); + imr.full = sst_shim_read64(sst_drv_ctx->shim, SST_IMRX); + isr.full = sst_shim_read64(sst_drv_ctx->shim, SST_ISRX); + +@@ -200,7 +200,6 @@ void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx) + /* un mask busy interrupt */ + imr.part.busy_interrupt = 0; + sst_shim_write64(sst_drv_ctx->shim, SST_IMRX, imr.full); +- spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); + } + + +diff --git a/sound/soc/intel/atom/sst/sst_pvt.c b/sound/soc/intel/atom/sst/sst_pvt.c +--- a/sound/soc/intel/atom/sst/sst_pvt.c ++++ b/sound/soc/intel/atom/sst/sst_pvt.c +@@ -11,6 +11,7 @@ + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ ++#include + #include + #include + #include +@@ -64,9 +65,8 @@ u64 sst_shim_read64(void __iomem *addr, int offset) + void sst_set_fw_state_locked( + struct intel_sst_drv *sst_drv_ctx, int sst_state) + { +- mutex_lock(&sst_drv_ctx->sst_lock); ++ guard(mutex)(&sst_drv_ctx->sst_lock); + sst_drv_ctx->sst_state = sst_state; +- mutex_unlock(&sst_drv_ctx->sst_lock); + } + + /* +@@ -302,18 +302,17 @@ int sst_assign_pvt_id(struct intel_sst_drv *drv) + { + int local; + +- spin_lock(&drv->block_lock); ++ guard(spinlock)(&drv->block_lock); + /* find first zero index from lsb */ + local = ffz(drv->pvt_id); + dev_dbg(drv->dev, "pvt_id assigned --> %d\n", local); + if (local >= SST_MAX_BLOCKS){ +- spin_unlock(&drv->block_lock); + dev_err(drv->dev, "PVT _ID error: no free id blocks "); + return -EINVAL; + } + /* toggle the index */ + change_bit(local, &drv->pvt_id); +- spin_unlock(&drv->block_lock); ++ + return local; + } + +diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c +--- a/sound/soc/intel/avs/apl.c ++++ b/sound/soc/intel/avs/apl.c +@@ -6,6 +6,7 @@ + // Amadeusz Slawinski + // + ++#include + #include + #include + #include +@@ -190,7 +191,7 @@ static bool avs_apl_lp_streaming(struct avs_dev *adev) + { + struct avs_path *path; + +- spin_lock(&adev->path_list_lock); ++ guard(spinlock)(&adev->path_list_lock); + /* Any gateway without buffer allocated in LP area disqualifies D0IX. */ + list_for_each_entry(path, &adev->path_list, node) { + struct avs_path_pipeline *ppl; +@@ -210,14 +211,11 @@ static bool avs_apl_lp_streaming(struct avs_dev *adev) + if (cfg->copier.dma_type == INVALID_OBJECT_ID) + continue; + +- if (!mod->gtw_attrs.lp_buffer_alloc) { +- spin_unlock(&adev->path_list_lock); ++ if (!mod->gtw_attrs.lp_buffer_alloc) + return false; +- } + } + } + } +- spin_unlock(&adev->path_list_lock); + + return true; + } +diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h +--- a/sound/soc/intel/avs/avs.h ++++ b/sound/soc/intel/avs/avs.h +@@ -336,14 +336,13 @@ int avs_icl_load_basefw(struct avs_dev *adev, struct firmware *fw); + /* Soc component members */ + + struct avs_soc_component { +- struct snd_soc_component base; ++ struct snd_soc_component *base; + struct avs_tplg *tplg; + + struct list_head node; + }; + +-#define to_avs_soc_component(comp) \ +- container_of(comp, struct avs_soc_component, base) ++#define to_avs_soc_component(comp) snd_soc_component_to_priv(comp) + + extern const struct snd_soc_dai_ops avs_dai_fe_ops; + +diff --git a/sound/soc/intel/avs/boards/da7219.c b/sound/soc/intel/avs/boards/da7219.c +--- a/sound/soc/intel/avs/boards/da7219.c ++++ b/sound/soc/intel/avs/boards/da7219.c +@@ -175,7 +175,6 @@ static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot, + if (!dl || !platform) + return -ENOMEM; + +- dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port); + dl->name = devm_kasprintf(dev, GFP_KERNEL, + AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot)); + dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL); +@@ -259,10 +258,8 @@ static int avs_da7219_probe(struct platform_device *pdev) + } + + static const struct platform_device_id avs_da7219_driver_ids[] = { +- { +- .name = "avs_da7219", +- }, +- {}, ++ { .name = "avs_da7219" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_da7219_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/dmic.c b/sound/soc/intel/avs/boards/dmic.c +--- a/sound/soc/intel/avs/boards/dmic.c ++++ b/sound/soc/intel/avs/boards/dmic.c +@@ -104,10 +104,8 @@ static int avs_dmic_probe(struct platform_device *pdev) + } + + static const struct platform_device_id avs_dmic_driver_ids[] = { +- { +- .name = "avs_dmic", +- }, +- {}, ++ { .name = "avs_dmic" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_dmic_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/es8336.c b/sound/soc/intel/avs/boards/es8336.c +--- a/sound/soc/intel/avs/boards/es8336.c ++++ b/sound/soc/intel/avs/boards/es8336.c +@@ -309,10 +309,8 @@ static int avs_es8336_probe(struct platform_device *pdev) + } + + static const struct platform_device_id avs_es8336_driver_ids[] = { +- { +- .name = "avs_es8336", +- }, +- {}, ++ { .name = "avs_es8336" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_es8336_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/hdaudio.c b/sound/soc/intel/avs/boards/hdaudio.c +--- a/sound/soc/intel/avs/boards/hdaudio.c ++++ b/sound/soc/intel/avs/boards/hdaudio.c +@@ -231,10 +231,8 @@ static int avs_hdaudio_probe(struct platform_device *pdev) + } + + static const struct platform_device_id avs_hdaudio_driver_ids[] = { +- { +- .name = "avs_hdaudio", +- }, +- {}, ++ { .name = "avs_hdaudio" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_hdaudio_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/i2s_test.c b/sound/soc/intel/avs/boards/i2s_test.c +--- a/sound/soc/intel/avs/boards/i2s_test.c ++++ b/sound/soc/intel/avs/boards/i2s_test.c +@@ -107,10 +107,8 @@ static int avs_i2s_test_probe(struct platform_device *pdev) + } + + static const struct platform_device_id avs_i2s_test_driver_ids[] = { +- { +- .name = "avs_i2s_test", +- }, +- {}, ++ { .name = "avs_i2s_test" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_i2s_test_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/max98357a.c b/sound/soc/intel/avs/boards/max98357a.c +--- a/sound/soc/intel/avs/boards/max98357a.c ++++ b/sound/soc/intel/avs/boards/max98357a.c +@@ -136,10 +136,8 @@ static int avs_max98357a_probe(struct platform_device *pdev) + } + + static const struct platform_device_id avs_max98357a_driver_ids[] = { +- { +- .name = "avs_max98357a", +- }, +- {}, ++ { .name = "avs_max98357a" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_max98357a_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/max98373.c b/sound/soc/intel/avs/boards/max98373.c +--- a/sound/soc/intel/avs/boards/max98373.c ++++ b/sound/soc/intel/avs/boards/max98373.c +@@ -191,10 +191,8 @@ static int avs_max98373_probe(struct platform_device *pdev) + } + + static const struct platform_device_id avs_max98373_driver_ids[] = { +- { +- .name = "avs_max98373", +- }, +- {}, ++ { .name = "avs_max98373" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_max98373_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/max98927.c b/sound/soc/intel/avs/boards/max98927.c +--- a/sound/soc/intel/avs/boards/max98927.c ++++ b/sound/soc/intel/avs/boards/max98927.c +@@ -188,10 +188,8 @@ static int avs_max98927_probe(struct platform_device *pdev) + } + + static const struct platform_device_id avs_max98927_driver_ids[] = { +- { +- .name = "avs_max98927", +- }, +- {}, ++ { .name = "avs_max98927" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_max98927_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/nau8825.c b/sound/soc/intel/avs/boards/nau8825.c +--- a/sound/soc/intel/avs/boards/nau8825.c ++++ b/sound/soc/intel/avs/boards/nau8825.c +@@ -293,10 +293,8 @@ static int avs_nau8825_probe(struct platform_device *pdev) + } + + static const struct platform_device_id avs_nau8825_driver_ids[] = { +- { +- .name = "avs_nau8825", +- }, +- {}, ++ { .name = "avs_nau8825" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_nau8825_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/pcm3168a.c b/sound/soc/intel/avs/boards/pcm3168a.c +--- a/sound/soc/intel/avs/boards/pcm3168a.c ++++ b/sound/soc/intel/avs/boards/pcm3168a.c +@@ -132,10 +132,8 @@ static int avs_pcm3168a_probe(struct platform_device *pdev) + } + + static const struct platform_device_id avs_pcm3168a_driver_ids[] = { +- { +- .name = "avs_pcm3168a", +- }, +- {}, ++ { .name = "avs_pcm3168a" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_pcm3168a_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/probe.c b/sound/soc/intel/avs/boards/probe.c +--- a/sound/soc/intel/avs/boards/probe.c ++++ b/sound/soc/intel/avs/boards/probe.c +@@ -64,7 +64,7 @@ static const struct platform_device_id avs_probe_mb_driver_ids[] = { + { + .name = "avs_probe_mb", + }, +- {}, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_probe_mb_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/rt274.c b/sound/soc/intel/avs/boards/rt274.c +--- a/sound/soc/intel/avs/boards/rt274.c ++++ b/sound/soc/intel/avs/boards/rt274.c +@@ -261,7 +261,7 @@ static const struct platform_device_id avs_rt274_driver_ids[] = { + { + .name = "avs_rt274", + }, +- {}, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_rt274_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/rt286.c b/sound/soc/intel/avs/boards/rt286.c +--- a/sound/soc/intel/avs/boards/rt286.c ++++ b/sound/soc/intel/avs/boards/rt286.c +@@ -231,7 +231,7 @@ static const struct platform_device_id avs_rt286_driver_ids[] = { + { + .name = "avs_rt286", + }, +- {}, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_rt286_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/rt298.c b/sound/soc/intel/avs/boards/rt298.c +--- a/sound/soc/intel/avs/boards/rt298.c ++++ b/sound/soc/intel/avs/boards/rt298.c +@@ -250,7 +250,7 @@ static const struct platform_device_id avs_rt298_driver_ids[] = { + { + .name = "avs_rt298", + }, +- {}, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_rt298_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/rt5514.c b/sound/soc/intel/avs/boards/rt5514.c +--- a/sound/soc/intel/avs/boards/rt5514.c ++++ b/sound/soc/intel/avs/boards/rt5514.c +@@ -178,7 +178,7 @@ static const struct platform_device_id avs_rt5514_driver_ids[] = { + { + .name = "avs_rt5514", + }, +- {}, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_rt5514_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/rt5640.c b/sound/soc/intel/avs/boards/rt5640.c +--- a/sound/soc/intel/avs/boards/rt5640.c ++++ b/sound/soc/intel/avs/boards/rt5640.c +@@ -252,7 +252,7 @@ static const struct platform_device_id avs_rt5640_driver_ids[] = { + { + .name = "avs_rt5640", + }, +- {}, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_rt5640_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/rt5663.c b/sound/soc/intel/avs/boards/rt5663.c +--- a/sound/soc/intel/avs/boards/rt5663.c ++++ b/sound/soc/intel/avs/boards/rt5663.c +@@ -249,7 +249,7 @@ static const struct platform_device_id avs_rt5663_driver_ids[] = { + { + .name = "avs_rt5663", + }, +- {}, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_rt5663_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/rt5682.c b/sound/soc/intel/avs/boards/rt5682.c +--- a/sound/soc/intel/avs/boards/rt5682.c ++++ b/sound/soc/intel/avs/boards/rt5682.c +@@ -325,7 +325,7 @@ static const struct platform_device_id avs_rt5682_driver_ids[] = { + { + .name = "avs_rt5682", + }, +- {}, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_rt5682_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/ssm4567.c b/sound/soc/intel/avs/boards/ssm4567.c +--- a/sound/soc/intel/avs/boards/ssm4567.c ++++ b/sound/soc/intel/avs/boards/ssm4567.c +@@ -180,7 +180,7 @@ static const struct platform_device_id avs_ssm4567_driver_ids[] = { + { + .name = "avs_ssm4567", + }, +- {}, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_ssm4567_driver_ids); + +diff --git a/sound/soc/intel/avs/control.c b/sound/soc/intel/avs/control.c +--- a/sound/soc/intel/avs/control.c ++++ b/sound/soc/intel/avs/control.c +@@ -27,7 +27,7 @@ static struct avs_path_module *avs_get_volume_module(struct avs_dev *adev, u32 i + struct avs_path_pipeline *ppl; + struct avs_path_module *mod; + +- spin_lock(&adev->path_list_lock); ++ guard(spinlock)(&adev->path_list_lock); + list_for_each_entry(path, &adev->path_list, node) { + list_for_each_entry(ppl, &path->ppl_list, node) { + list_for_each_entry(mod, &ppl->mod_list, node) { +@@ -35,14 +35,11 @@ static struct avs_path_module *avs_get_volume_module(struct avs_dev *adev, u32 i + + if ((guid_equal(type, &AVS_PEAKVOL_MOD_UUID) || + guid_equal(type, &AVS_GAIN_MOD_UUID)) && +- mod->template->ctl_id == id) { +- spin_unlock(&adev->path_list_lock); ++ mod->template->ctl_id == id) + return mod; +- } + } + } + } +- spin_unlock(&adev->path_list_lock); + + return NULL; + } +diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c +--- a/sound/soc/intel/avs/core.c ++++ b/sound/soc/intel/avs/core.c +@@ -14,6 +14,7 @@ + // foundation of this driver + // + ++#include + #include + #include + #include +@@ -273,7 +274,7 @@ static irqreturn_t avs_hda_interrupt(struct hdac_bus *bus) + if (snd_hdac_bus_handle_stream_irq(bus, status, hdac_update_stream)) + ret = IRQ_HANDLED; + +- spin_lock_irq(&bus->reg_lock); ++ guard(spinlock_irq)(&bus->reg_lock); + /* Clear RIRB interrupt. */ + status = snd_hdac_chip_readb(bus, RIRBSTS); + if (status & RIRB_INT_MASK) { +@@ -283,7 +284,6 @@ static irqreturn_t avs_hda_interrupt(struct hdac_bus *bus) + ret = IRQ_HANDLED; + } + +- spin_unlock_irq(&bus->reg_lock); + return ret; + } + +diff --git a/sound/soc/intel/avs/debug.h b/sound/soc/intel/avs/debug.h +--- a/sound/soc/intel/avs/debug.h ++++ b/sound/soc/intel/avs/debug.h +@@ -9,6 +9,7 @@ + #ifndef __SOUND_SOC_INTEL_AVS_DEBUG_H + #define __SOUND_SOC_INTEL_AVS_DEBUG_H + ++#include + #include "messages.h" + #include "registers.h" + +@@ -26,14 +27,9 @@ struct avs_dev; + + static inline int avs_log_buffer_status_locked(struct avs_dev *adev, union avs_notify_msg *msg) + { +- unsigned long flags; +- int ret; ++ guard(spinlock_irqsave)(&adev->trace_lock); + +- spin_lock_irqsave(&adev->trace_lock, flags); +- ret = avs_dsp_op(adev, log_buffer_status, msg); +- spin_unlock_irqrestore(&adev->trace_lock, flags); +- +- return ret; ++ return avs_dsp_op(adev, log_buffer_status, msg); + } + + struct avs_apl_log_buffer_layout { +diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c +--- a/sound/soc/intel/avs/debugfs.c ++++ b/sound/soc/intel/avs/debugfs.c +@@ -6,9 +6,9 @@ + // Amadeusz Slawinski + // + ++#include + #include + #include +-#include + #include + #include + #include +@@ -236,20 +236,15 @@ static int strace_open(struct inode *inode, struct file *file) + if (!try_module_get(adev->dev->driver->owner)) + return -ENODEV; + +- if (kfifo_initialized(&adev->trace_fifo)) { +- ret = -EBUSY; +- goto err; +- } ++ if (kfifo_initialized(&adev->trace_fifo)) ++ return -EBUSY; + + ret = kfifo_alloc(&adev->trace_fifo, PAGE_SIZE, GFP_KERNEL); + if (ret < 0) +- goto err; ++ return ret; + + file->private_data = adev; + return 0; +-err: +- module_put(adev->dev->driver->owner); +- return ret; + } + + static int strace_release(struct inode *inode, struct file *file) +@@ -257,24 +252,22 @@ static int strace_release(struct inode *inode, struct file *file) + union avs_notify_msg msg = AVS_NOTIFICATION(LOG_BUFFER_STATUS); + struct avs_dev *adev = file->private_data; + unsigned long resource_mask; +- unsigned long flags, i; ++ unsigned long i; + u32 num_cores; + + resource_mask = adev->logged_resources; + num_cores = adev->hw_cfg.dsp_cores; + +- spin_lock_irqsave(&adev->trace_lock, flags); ++ scoped_guard(spinlock_irqsave, &adev->trace_lock) { ++ /* Gather any remaining logs. */ ++ for_each_set_bit(i, &resource_mask, num_cores) { ++ msg.log.core = i; ++ avs_dsp_op(adev, log_buffer_status, &msg); ++ } + +- /* Gather any remaining logs. */ +- for_each_set_bit(i, &resource_mask, num_cores) { +- msg.log.core = i; +- avs_dsp_op(adev, log_buffer_status, &msg); ++ kfifo_free(&adev->trace_fifo); + } + +- kfifo_free(&adev->trace_fifo); +- +- spin_unlock_irqrestore(&adev->trace_lock, flags); +- + module_put(adev->dev->driver->owner); + return 0; + } +diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c +--- a/sound/soc/intel/avs/ipc.c ++++ b/sound/soc/intel/avs/ipc.c +@@ -6,6 +6,7 @@ + // Amadeusz Slawinski + // + ++#include + #include + #include + #include +@@ -99,39 +100,39 @@ static void avs_dsp_recovery(struct avs_dev *adev) + unsigned int core_mask; + int ret; + +- mutex_lock(&adev->comp_list_mutex); +- /* disconnect all running streams */ +- list_for_each_entry(acomp, &adev->comp_list, node) { +- struct snd_soc_pcm_runtime *rtd; +- struct snd_soc_card *card; ++ scoped_guard(mutex, &adev->comp_list_mutex) { ++ /* disconnect all running streams */ ++ list_for_each_entry(acomp, &adev->comp_list, node) { ++ struct snd_soc_pcm_runtime *rtd; ++ struct snd_soc_card *card; + +- card = acomp->base.card; +- if (!card) +- continue; +- +- for_each_card_rtds(card, rtd) { +- struct snd_pcm *pcm; +- int dir; +- +- pcm = rtd->pcm; +- if (!pcm || rtd->dai_link->no_pcm) ++ card = acomp->base->card; ++ if (!card) + continue; + +- for_each_pcm_streams(dir) { +- struct snd_pcm_substream *substream; ++ for_each_card_rtds(card, rtd) { ++ struct snd_pcm *pcm; ++ int dir; + +- substream = pcm->streams[dir].substream; +- if (!substream || !substream->runtime) ++ pcm = rtd->pcm; ++ if (!pcm || rtd->dai_link->no_pcm) + continue; + +- /* No need for _irq() as we are in nonatomic context. */ +- snd_pcm_stream_lock(substream); +- snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); +- snd_pcm_stream_unlock(substream); ++ for_each_pcm_streams(dir) { ++ struct snd_pcm_substream *substream; ++ ++ substream = pcm->streams[dir].substream; ++ if (!substream || !substream->runtime) ++ continue; ++ ++ /* No need for _irq() as we are in nonatomic context. */ ++ snd_pcm_stream_lock(substream); ++ snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); ++ snd_pcm_stream_unlock(substream); ++ } + } + } + } +- mutex_unlock(&adev->comp_list_mutex); + + /* forcibly shutdown all cores */ + core_mask = GENMASK(adev->hw_cfg.dsp_cores - 1, 0); +@@ -397,7 +398,7 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request + if (!ipc->ready) + return -EPERM; + +- mutex_lock(&ipc->msg_mutex); ++ guard(mutex)(&ipc->msg_mutex); + + spin_lock(&ipc->rx_lock); + avs_ipc_msg_init(ipc, reply); +@@ -412,7 +413,7 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request + /* Same treatment as on exception, just stack_dump=0. */ + avs_dsp_exception_caught(adev, &msg); + } +- goto exit; ++ return ret; + } + + ret = ipc->rx.rsp.status; +@@ -436,8 +437,6 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request + memcpy(reply->data, ipc->rx.data, reply->size); + } + +-exit: +- mutex_unlock(&ipc->msg_mutex); + return ret; + } + +@@ -501,7 +500,7 @@ static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *req + struct avs_ipc *ipc = adev->ipc; + int ret; + +- mutex_lock(&ipc->msg_mutex); ++ guard(mutex)(&ipc->msg_mutex); + + spin_lock(&ipc->rx_lock); + avs_ipc_msg_init(ipc, NULL); +@@ -522,8 +521,6 @@ static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *req + dev_err(adev->dev, "%s (0x%08x 0x%08x) failed: %d\n", + name, request->glb.primary, request->glb.ext.val, ret); + +- mutex_unlock(&ipc->msg_mutex); +- + return ret; + } + +diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c +--- a/sound/soc/intel/avs/loader.c ++++ b/sound/soc/intel/avs/loader.c +@@ -6,6 +6,7 @@ + // Amadeusz Slawinski + // + ++#include + #include + #include + #include +@@ -630,15 +631,15 @@ static int avs_load_firmware(struct avs_dev *adev, bool purge) + if (ret) + goto reenable_gating; + +- mutex_lock(&adev->comp_list_mutex); +- list_for_each_entry(acomp, &adev->comp_list, node) { +- struct avs_tplg *tplg = acomp->tplg; ++ scoped_guard(mutex, &adev->comp_list_mutex) { ++ list_for_each_entry(acomp, &adev->comp_list, node) { ++ struct avs_tplg *tplg = acomp->tplg; + +- ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs); +- if (ret < 0) +- break; ++ ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs); ++ if (ret < 0) ++ break; ++ } + } +- mutex_unlock(&adev->comp_list_mutex); + + reenable_gating: + avs_hda_l1sen_enable(adev, true); +diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c +--- a/sound/soc/intel/avs/path.c ++++ b/sound/soc/intel/avs/path.c +@@ -6,6 +6,7 @@ + // Amadeusz Slawinski + // + ++#include + #include + #include + #include +@@ -69,16 +70,13 @@ avs_path_find_path(struct avs_dev *adev, const char *name, u32 template_id) + if (!template) + return NULL; + +- spin_lock(&adev->path_list_lock); ++ guard(spinlock)(&adev->path_list_lock); + /* Only one variant of given path template may be instantiated at a time. */ + list_for_each_entry(path, &adev->path_list, node) { +- if (path->template->owner == template) { +- spin_unlock(&adev->path_list_lock); ++ if (path->template->owner == template) + return path; +- } + } + +- spin_unlock(&adev->path_list_lock); + return NULL; + } + +@@ -1300,7 +1298,7 @@ void avs_path_free(struct avs_path *path) + struct avs_path *cpath, *csave; + struct avs_dev *adev = path->owner; + +- mutex_lock(&adev->path_mutex); ++ guard(mutex)(&adev->path_mutex); + + /* Free all condpaths this path spawned. */ + list_for_each_entry_safe(cpath, csave, &path->source_list, source_node) +@@ -1309,8 +1307,6 @@ void avs_path_free(struct avs_path *path) + avs_condpath_free(path->owner, cpath); + + avs_path_free_unlocked(path); +- +- mutex_unlock(&adev->path_mutex); + } + + struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id, +@@ -1329,13 +1325,13 @@ struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id, + } + + /* Serialize path and its components creation. */ +- mutex_lock(&adev->path_mutex); ++ guard(mutex)(&adev->path_mutex); + /* Satisfy needs of avs_path_find_tplg(). */ +- mutex_lock(&adev->comp_list_mutex); ++ guard(mutex)(&adev->comp_list_mutex); + + path = avs_path_create_unlocked(adev, dma_id, variant); + if (IS_ERR(path)) +- goto exit; ++ return path; + + ret = avs_condpaths_walk_all(adev, path); + if (ret) { +@@ -1343,10 +1339,6 @@ struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id, + path = ERR_PTR(ret); + } + +-exit: +- mutex_unlock(&adev->comp_list_mutex); +- mutex_unlock(&adev->path_mutex); +- + return path; + } + +@@ -1491,15 +1483,13 @@ static void avs_condpaths_pause(struct avs_dev *adev, struct avs_path *path) + { + struct avs_path *cpath; + +- mutex_lock(&adev->path_mutex); ++ guard(mutex)(&adev->path_mutex); + + /* If either source or sink stops, so do the attached conditional paths. */ + list_for_each_entry(cpath, &path->source_list, source_node) + avs_condpath_pause(adev, cpath); + list_for_each_entry(cpath, &path->sink_list, sink_node) + avs_condpath_pause(adev, cpath); +- +- mutex_unlock(&adev->path_mutex); + } + + int avs_path_pause(struct avs_path *path) +@@ -1555,7 +1545,7 @@ static void avs_condpaths_run(struct avs_dev *adev, struct avs_path *path, int t + { + struct avs_path *cpath; + +- mutex_lock(&adev->path_mutex); ++ guard(mutex)(&adev->path_mutex); + + /* Run conditional paths only if source and sink are both running. */ + list_for_each_entry(cpath, &path->source_list, source_node) +@@ -1567,8 +1557,6 @@ static void avs_condpaths_run(struct avs_dev *adev, struct avs_path *path, int t + if (cpath->source->state == AVS_PPL_STATE_RUNNING && + cpath->sink->state == AVS_PPL_STATE_RUNNING) + avs_condpath_run(adev, cpath, trigger); +- +- mutex_unlock(&adev->path_mutex); + } + + int avs_path_run(struct avs_path *path, int trigger) +diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c +--- a/sound/soc/intel/avs/pcm.c ++++ b/sound/soc/intel/avs/pcm.c +@@ -959,7 +959,7 @@ static const struct file_operations topology_name_fops = { + static int avs_component_load_libraries(struct avs_soc_component *acomp) + { + struct avs_tplg *tplg = acomp->tplg; +- struct avs_dev *adev = to_avs_dev(acomp->base.dev); ++ struct avs_dev *adev = to_avs_dev(acomp->base->dev); + int ret; + + if (!tplg->num_libs) +@@ -1393,25 +1393,28 @@ int avs_register_component(struct device *dev, const char *name, + struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais) + { + struct avs_soc_component *acomp; +- int ret; ++ const char *comp_name; + + acomp = devm_kzalloc(dev, sizeof(*acomp), GFP_KERNEL); + if (!acomp) + return -ENOMEM; + +- acomp->base.name = devm_kstrdup(dev, name, GFP_KERNEL); +- if (!acomp->base.name) ++ acomp->base = snd_soc_component_alloc(dev); ++ if (!acomp->base) ++ return -ENOMEM; ++ ++ comp_name = devm_kstrdup(dev, name, GFP_KERNEL); ++ if (!comp_name) + return -ENOMEM; + + INIT_LIST_HEAD(&acomp->node); + + drv->use_dai_pcm_id = !obsolete_card_names; + +- ret = snd_soc_component_initialize(&acomp->base, drv, dev); +- if (ret < 0) +- return ret; ++ snd_soc_component_set_name(acomp->base, comp_name); ++ snd_soc_component_set_priv(acomp->base, acomp); + +- return snd_soc_add_component(&acomp->base, cpu_dais, num_cpu_dais); ++ return snd_soc_register_component(acomp->base, drv, cpu_dais, num_cpu_dais); + } + + static struct snd_soc_dai_driver dmic_cpu_dais[] = { +diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c +--- a/sound/soc/intel/avs/probes.c ++++ b/sound/soc/intel/avs/probes.c +@@ -296,19 +296,19 @@ static const struct snd_soc_component_driver avs_probe_component_driver = { + int avs_register_probe_component(struct avs_dev *adev, const char *name) + { + struct snd_soc_component *component; +- int ret; ++ const char *comp_name; + +- component = devm_kzalloc(adev->dev, sizeof(*component), GFP_KERNEL); ++ component = snd_soc_component_alloc(adev->dev); + if (!component) + return -ENOMEM; + +- component->name = devm_kstrdup(adev->dev, name, GFP_KERNEL); +- if (!component->name) ++ comp_name = devm_kstrdup(adev->dev, name, GFP_KERNEL); ++ if (!comp_name) + return -ENOMEM; + +- ret = snd_soc_component_initialize(component, &avs_probe_component_driver, adev->dev); +- if (ret) +- return ret; ++ snd_soc_component_set_name(component, comp_name); + +- return snd_soc_add_component(component, probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais)); ++ return snd_soc_register_component(component, ++ &avs_probe_component_driver, ++ probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais)); + } +diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c +--- a/sound/soc/intel/avs/topology.c ++++ b/sound/soc/intel/avs/topology.c +@@ -2229,7 +2229,7 @@ struct avs_tplg *avs_tplg_new(struct snd_soc_component *comp) + + int avs_load_topology(struct snd_soc_component *comp, const char *filename) + { +- const struct firmware *fw; ++ const struct firmware *fw __free(firmware) = NULL; + int ret; + + ret = request_firmware(&fw, filename, comp->dev); +@@ -2242,7 +2242,6 @@ int avs_load_topology(struct snd_soc_component *comp, const char *filename) + if (ret < 0) + dev_err(comp->dev, "load topology \"%s\" failed: %d\n", filename, ret); + +- release_firmware(fw); + return ret; + } + +diff --git a/sound/soc/intel/avs/utils.c b/sound/soc/intel/avs/utils.c +--- a/sound/soc/intel/avs/utils.c ++++ b/sound/soc/intel/avs/utils.c +@@ -6,6 +6,7 @@ + // Amadeusz Slawinski + // + ++#include + #include + #include + #include +@@ -48,13 +49,12 @@ int avs_get_module_entry(struct avs_dev *adev, const guid_t *uuid, struct avs_mo + { + int idx; + +- mutex_lock(&adev->modres_mutex); ++ guard(mutex)(&adev->modres_mutex); + + idx = avs_module_entry_index(adev, uuid); + if (idx >= 0) + memcpy(entry, &adev->mods_info->entries[idx], sizeof(*entry)); + +- mutex_unlock(&adev->modres_mutex); + return (idx < 0) ? idx : 0; + } + +@@ -62,13 +62,12 @@ int avs_get_module_id_entry(struct avs_dev *adev, u32 module_id, struct avs_modu + { + int idx; + +- mutex_lock(&adev->modres_mutex); ++ guard(mutex)(&adev->modres_mutex); + + idx = avs_module_id_entry_index(adev, module_id); + if (idx >= 0) + memcpy(entry, &adev->mods_info->entries[idx], sizeof(*entry)); + +- mutex_unlock(&adev->modres_mutex); + return (idx < 0) ? idx : 0; + } + +@@ -86,13 +85,12 @@ bool avs_is_module_ida_empty(struct avs_dev *adev, u32 module_id) + bool ret = false; + int idx; + +- mutex_lock(&adev->modres_mutex); ++ guard(mutex)(&adev->modres_mutex); + + idx = avs_module_id_entry_index(adev, module_id); + if (idx >= 0) + ret = ida_is_empty(adev->mod_idas[idx]); + +- mutex_unlock(&adev->modres_mutex); + return ret; + } + +@@ -163,68 +161,57 @@ int avs_module_info_init(struct avs_dev *adev, bool purge) + if (ret) + return AVS_IPC_RET(ret); + +- mutex_lock(&adev->modres_mutex); ++ guard(mutex)(&adev->modres_mutex); + + ret = avs_module_ida_alloc(adev, info, purge); + if (ret < 0) { + dev_err(adev->dev, "initialize module idas failed: %d\n", ret); +- goto exit; ++ return ret; + } + + /* Refresh current information with newly received table. */ + kfree(adev->mods_info); + adev->mods_info = info; + +-exit: +- mutex_unlock(&adev->modres_mutex); + return ret; + } + + void avs_module_info_free(struct avs_dev *adev) + { +- mutex_lock(&adev->modres_mutex); ++ guard(mutex)(&adev->modres_mutex); + + avs_module_ida_destroy(adev); + kfree(adev->mods_info); + adev->mods_info = NULL; +- +- mutex_unlock(&adev->modres_mutex); + } + + int avs_module_id_alloc(struct avs_dev *adev, u16 module_id) + { +- int ret, idx, max_id; ++ int idx, max_id; + +- mutex_lock(&adev->modres_mutex); ++ guard(mutex)(&adev->modres_mutex); + + idx = avs_module_id_entry_index(adev, module_id); + if (idx == -ENOENT) { + dev_err(adev->dev, "invalid module id: %d", module_id); +- ret = -EINVAL; +- goto exit; ++ return -EINVAL; + } + max_id = adev->mods_info->entries[idx].instance_max_count - 1; +- ret = ida_alloc_max(adev->mod_idas[idx], max_id, GFP_KERNEL); +-exit: +- mutex_unlock(&adev->modres_mutex); +- return ret; ++ ++ return ida_alloc_max(adev->mod_idas[idx], max_id, GFP_KERNEL); + } + + void avs_module_id_free(struct avs_dev *adev, u16 module_id, u8 instance_id) + { + int idx; + +- mutex_lock(&adev->modres_mutex); ++ guard(mutex)(&adev->modres_mutex); + + idx = avs_module_id_entry_index(adev, module_id); +- if (idx == -ENOENT) { ++ if (idx == -ENOENT) + dev_err(adev->dev, "invalid module id: %d", module_id); +- goto exit; +- } +- +- ida_free(adev->mod_idas[idx], instance_id); +-exit: +- mutex_unlock(&adev->modres_mutex); ++ else ++ ida_free(adev->mod_idas[idx], instance_id); + } + + /* +diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c +--- a/sound/soc/intel/boards/sof_sdw.c ++++ b/sound/soc/intel/boards/sof_sdw.c +@@ -1483,12 +1483,12 @@ static int mc_probe(struct platform_device *pdev) + dmi_check_system(sof_sdw_quirk_table); + + if (quirk_override != -1) { +- dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n", ++ dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n", + sof_sdw_quirk, quirk_override); + sof_sdw_quirk = quirk_override; + } + +- log_quirks(card->dev); ++ log_quirks(&pdev->dev); + + ctx->mc_quirk = sof_sdw_quirk; + /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ +@@ -1507,13 +1507,13 @@ static int mc_probe(struct platform_device *pdev) + for (i = 0; i < ctx->codec_info_list_count; i++) + amp_num += codec_info_list[i].amp_num; + +- card->components = devm_kasprintf(card->dev, GFP_KERNEL, ++ card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, + " cfg-amp:%d", amp_num); + if (!card->components) + return -ENOMEM; + + if (mach->mach_params.dmic_num) { +- card->components = devm_kasprintf(card->dev, GFP_KERNEL, ++ card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, + "%s mic:dmic cfg-mics:%d", + card->components, + mach->mach_params.dmic_num); +@@ -1524,7 +1524,7 @@ static int mc_probe(struct platform_device *pdev) + /* Register the card */ + ret = devm_snd_soc_register_card(card->dev, card); + if (ret) { +- dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret); ++ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed %d\n", ret); + asoc_sdw_mc_dailink_exit_loop(card); + return ret; + } +@@ -1542,8 +1542,8 @@ static void mc_remove(struct platform_device *pdev) + } + + static const struct platform_device_id mc_id_table[] = { +- { "sof_sdw", }, +- {} ++ { .name = "sof_sdw" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, mc_id_table); + +diff --git a/sound/soc/intel/catpt/core.h b/sound/soc/intel/catpt/core.h +--- a/sound/soc/intel/catpt/core.h ++++ b/sound/soc/intel/catpt/core.h +@@ -139,9 +139,7 @@ int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request, + + int catpt_first_boot_firmware(struct catpt_dev *cdev); + int catpt_boot_firmware(struct catpt_dev *cdev, bool restore); +-int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan); +-int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan); +-int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan); ++int catpt_store_firmware_context(struct catpt_dev *cdev); + int catpt_coredump(struct catpt_dev *cdev); + + #include +diff --git a/sound/soc/intel/catpt/device.c b/sound/soc/intel/catpt/device.c +--- a/sound/soc/intel/catpt/device.c ++++ b/sound/soc/intel/catpt/device.c +@@ -28,44 +28,17 @@ + static int catpt_do_suspend(struct device *dev) + { + struct catpt_dev *cdev = dev_get_drvdata(dev); +- struct dma_chan *chan; + int ret; + +- chan = catpt_dma_request_config_chan(cdev); +- if (IS_ERR(chan)) +- return PTR_ERR(chan); +- + memset(&cdev->dx_ctx, 0, sizeof(cdev->dx_ctx)); + ret = catpt_ipc_enter_dxstate(cdev, CATPT_DX_STATE_D3, &cdev->dx_ctx); +- if (ret) { +- ret = CATPT_IPC_RET(ret); +- goto release_dma_chan; +- } +- +- ret = catpt_dsp_stall(cdev, true); + if (ret) +- goto release_dma_chan; ++ return CATPT_IPC_RET(ret); + +- ret = catpt_store_memdumps(cdev, chan); +- if (ret) { +- dev_err(cdev->dev, "store memdumps failed: %d\n", ret); +- goto release_dma_chan; +- } +- +- ret = catpt_store_module_states(cdev, chan); +- if (ret) { +- dev_err(cdev->dev, "store module states failed: %d\n", ret); +- goto release_dma_chan; +- } +- +- ret = catpt_store_streams_context(cdev, chan); +- if (ret) +- dev_err(cdev->dev, "store streams ctx failed: %d\n", ret); +- +-release_dma_chan: +- dma_release_channel(chan); ++ ret = catpt_store_firmware_context(cdev); + if (ret) + return ret; ++ + return catpt_dsp_power_down(cdev); + } + +diff --git a/sound/soc/intel/catpt/loader.c b/sound/soc/intel/catpt/loader.c +--- a/sound/soc/intel/catpt/loader.c ++++ b/sound/soc/intel/catpt/loader.c +@@ -26,7 +26,7 @@ struct catpt_fw_hdr { + u32 reserved[4]; + } __packed; + +-struct catpt_fw_mod_hdr { ++struct catpt_fw_module_hdr { + char signature[FW_SIGNATURE_SIZE]; + u32 mod_size; + u32 blocks; +@@ -81,7 +81,7 @@ catpt_request_region(struct resource *root, resource_size_t size) + return __request_region(root, addr, size, NULL, 0); + } + +-int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) ++static int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) + { + struct catpt_stream_runtime *stream; + +@@ -108,7 +108,7 @@ int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) + return 0; + } + +-int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan) ++static int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan) + { + int i; + +@@ -138,7 +138,7 @@ int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan) + return 0; + } + +-int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) ++static int catpt_store_dram_data(struct catpt_dev *cdev, struct dma_chan *chan) + { + int i; + +@@ -171,6 +171,39 @@ int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) + return 0; + } + ++int catpt_store_firmware_context(struct catpt_dev *cdev) ++{ ++ struct dma_chan *chan; ++ int ret; ++ ++ chan = catpt_dma_request_config_chan(cdev); ++ if (IS_ERR(chan)) ++ return PTR_ERR(chan); ++ ++ ret = catpt_dsp_stall(cdev, true); ++ if (ret) ++ goto exit; ++ ++ ret = catpt_store_dram_data(cdev, chan); ++ if (ret) { ++ dev_err(cdev->dev, "store memdumps failed: %d\n", ret); ++ goto exit; ++ } ++ ++ ret = catpt_store_module_states(cdev, chan); ++ if (ret) { ++ dev_err(cdev->dev, "store module states failed: %d\n", ret); ++ goto exit; ++ } ++ ++ ret = catpt_store_streams_context(cdev, chan); ++ if (ret) ++ dev_err(cdev->dev, "store streams ctx failed: %d\n", ret); ++exit: ++ dma_release_channel(chan); ++ return ret; ++} ++ + static int + catpt_restore_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) + { +@@ -199,7 +232,7 @@ catpt_restore_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) + return 0; + } + +-static int catpt_restore_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) ++static int catpt_restore_dram_data(struct catpt_dev *cdev, struct dma_chan *chan) + { + int i; + +@@ -234,9 +267,9 @@ static int catpt_restore_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) + return 0; + } + +-static int catpt_restore_fwimage(struct catpt_dev *cdev, +- struct dma_chan *chan, dma_addr_t paddr, +- struct catpt_fw_block_hdr *blk) ++static int catpt_restore_dram_rodata(struct catpt_dev *cdev, ++ struct dma_chan *chan, dma_addr_t paddr, ++ struct catpt_fw_block_hdr *blk) + { + struct resource r1 = {}; + int i; +@@ -324,28 +357,26 @@ static int catpt_load_block(struct catpt_dev *cdev, + + static int catpt_restore_basefw(struct catpt_dev *cdev, + struct dma_chan *chan, dma_addr_t paddr, +- struct catpt_fw_mod_hdr *basefw) ++ struct catpt_fw_module_hdr *basefw) + { +- u32 offset = sizeof(*basefw); ++ u32 off = sizeof(*basefw); + int ret, i; + + print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, + basefw, sizeof(*basefw), false); + +- /* restore basefw image */ ++ /* Restore IRAM and .rodata for DRAM based on the firmware image. */ + for (i = 0; i < basefw->blocks; i++) { + struct catpt_fw_block_hdr *blk; + +- blk = (struct catpt_fw_block_hdr *)((u8 *)basefw + offset); ++ blk = (struct catpt_fw_block_hdr *)((u8 *)basefw + off); + + switch (blk->ram_type) { + case CATPT_RAM_TYPE_IRAM: +- ret = catpt_load_block(cdev, chan, paddr + offset, +- blk, false); ++ ret = catpt_load_block(cdev, chan, paddr + off, blk, false); + break; + default: +- ret = catpt_restore_fwimage(cdev, chan, paddr + offset, +- blk); ++ ret = catpt_restore_dram_rodata(cdev, chan, paddr + off, blk); + break; + } + +@@ -354,11 +385,11 @@ static int catpt_restore_basefw(struct catpt_dev *cdev, + return ret; + } + +- offset += sizeof(*blk) + blk->size; ++ off += sizeof(*blk) + blk->size; + } + +- /* then proceed with memory dumps */ +- ret = catpt_restore_memdumps(cdev, chan); ++ /* Then proceed with DRAM .data saved before D3. */ ++ ret = catpt_restore_dram_data(cdev, chan); + if (ret) + dev_err(cdev->dev, "restore memdumps failed: %d\n", ret); + +@@ -367,9 +398,9 @@ static int catpt_restore_basefw(struct catpt_dev *cdev, + + static int catpt_restore_module(struct catpt_dev *cdev, + struct dma_chan *chan, dma_addr_t paddr, +- struct catpt_fw_mod_hdr *mod) ++ struct catpt_fw_module_hdr *mod) + { +- u32 offset = sizeof(*mod); ++ u32 off = sizeof(*mod); + int i; + + print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, +@@ -379,7 +410,7 @@ static int catpt_restore_module(struct catpt_dev *cdev, + struct catpt_fw_block_hdr *blk; + int ret; + +- blk = (struct catpt_fw_block_hdr *)((u8 *)mod + offset); ++ blk = (struct catpt_fw_block_hdr *)((u8 *)mod + off); + + switch (blk->ram_type) { + case CATPT_RAM_TYPE_INSTANCE: +@@ -390,7 +421,7 @@ static int catpt_restore_module(struct catpt_dev *cdev, + ALIGN(blk->size, 4)); + break; + default: +- ret = catpt_load_block(cdev, chan, paddr + offset, ++ ret = catpt_load_block(cdev, chan, paddr + off, + blk, false); + break; + } +@@ -400,7 +431,7 @@ static int catpt_restore_module(struct catpt_dev *cdev, + return ret; + } + +- offset += sizeof(*blk) + blk->size; ++ off += sizeof(*blk) + blk->size; + } + + return 0; +@@ -408,10 +439,10 @@ static int catpt_restore_module(struct catpt_dev *cdev, + + static int catpt_load_module(struct catpt_dev *cdev, + struct dma_chan *chan, dma_addr_t paddr, +- struct catpt_fw_mod_hdr *mod) ++ struct catpt_fw_module_hdr *mod) + { + struct catpt_module_type *type; +- u32 offset = sizeof(*mod); ++ u32 off = sizeof(*mod); + int i; + + print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, +@@ -423,9 +454,9 @@ static int catpt_load_module(struct catpt_dev *cdev, + struct catpt_fw_block_hdr *blk; + int ret; + +- blk = (struct catpt_fw_block_hdr *)((u8 *)mod + offset); ++ blk = (struct catpt_fw_block_hdr *)((u8 *)mod + off); + +- ret = catpt_load_block(cdev, chan, paddr + offset, blk, true); ++ ret = catpt_load_block(cdev, chan, paddr + off, blk, true); + if (ret) { + dev_err(cdev->dev, "load block failed: %d\n", ret); + return ret; +@@ -440,7 +471,7 @@ static int catpt_load_module(struct catpt_dev *cdev, + type->state_size = blk->size; + } + +- offset += sizeof(*blk) + blk->size; ++ off += sizeof(*blk) + blk->size; + } + + /* init module type static info */ +@@ -457,17 +488,17 @@ static int catpt_restore_firmware(struct catpt_dev *cdev, + struct dma_chan *chan, dma_addr_t paddr, + struct catpt_fw_hdr *fw) + { +- u32 offset = sizeof(*fw); ++ u32 off = sizeof(*fw); + int i; + + print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, + fw, sizeof(*fw), false); + + for (i = 0; i < fw->modules; i++) { +- struct catpt_fw_mod_hdr *mod; ++ struct catpt_fw_module_hdr *mod; + int ret; + +- mod = (struct catpt_fw_mod_hdr *)((u8 *)fw + offset); ++ mod = (struct catpt_fw_module_hdr *)((u8 *)fw + off); + if (strncmp(fw->signature, mod->signature, + FW_SIGNATURE_SIZE)) { + dev_err(cdev->dev, "module signature mismatch\n"); +@@ -479,12 +510,10 @@ static int catpt_restore_firmware(struct catpt_dev *cdev, + + switch (mod->module_id) { + case CATPT_MODID_BASE_FW: +- ret = catpt_restore_basefw(cdev, chan, paddr + offset, +- mod); ++ ret = catpt_restore_basefw(cdev, chan, paddr + off, mod); + break; + default: +- ret = catpt_restore_module(cdev, chan, paddr + offset, +- mod); ++ ret = catpt_restore_module(cdev, chan, paddr + off, mod); + break; + } + +@@ -493,7 +522,7 @@ static int catpt_restore_firmware(struct catpt_dev *cdev, + return ret; + } + +- offset += sizeof(*mod) + mod->mod_size; ++ off += sizeof(*mod) + mod->mod_size; + } + + return 0; +@@ -503,17 +532,17 @@ static int catpt_load_firmware(struct catpt_dev *cdev, + struct dma_chan *chan, dma_addr_t paddr, + struct catpt_fw_hdr *fw) + { +- u32 offset = sizeof(*fw); ++ u32 off = sizeof(*fw); + int i; + + print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, + fw, sizeof(*fw), false); + + for (i = 0; i < fw->modules; i++) { +- struct catpt_fw_mod_hdr *mod; ++ struct catpt_fw_module_hdr *mod; + int ret; + +- mod = (struct catpt_fw_mod_hdr *)((u8 *)fw + offset); ++ mod = (struct catpt_fw_module_hdr *)((u8 *)fw + off); + if (strncmp(fw->signature, mod->signature, + FW_SIGNATURE_SIZE)) { + dev_err(cdev->dev, "module signature mismatch\n"); +@@ -523,44 +552,40 @@ static int catpt_load_firmware(struct catpt_dev *cdev, + if (mod->module_id > CATPT_MODID_LAST) + return -EINVAL; + +- ret = catpt_load_module(cdev, chan, paddr + offset, mod); ++ ret = catpt_load_module(cdev, chan, paddr + off, mod); + if (ret) { + dev_err(cdev->dev, "load module failed: %d\n", ret); + return ret; + } + +- offset += sizeof(*mod) + mod->mod_size; ++ off += sizeof(*mod) + mod->mod_size; + } + + return 0; + } + +-static int catpt_load_image(struct catpt_dev *cdev, struct dma_chan *chan, +- const char *name, const char *signature, +- bool restore) ++static int catpt_request_load_firmware(struct catpt_dev *cdev, struct dma_chan *chan, ++ const char *name, bool restore) + { + struct catpt_fw_hdr *fw; +- struct firmware *img; + dma_addr_t paddr; + void *vaddr; + int ret; + +- ret = request_firmware((const struct firmware **)&img, name, cdev->dev); ++ const struct firmware *img __free(firmware) = NULL; ++ ret = request_firmware(&img, name, cdev->dev); + if (ret) + return ret; + + fw = (struct catpt_fw_hdr *)img->data; +- if (strncmp(fw->signature, signature, FW_SIGNATURE_SIZE)) { ++ if (strncmp(fw->signature, FW_SIGNATURE, FW_SIGNATURE_SIZE)) { + dev_err(cdev->dev, "firmware signature mismatch\n"); +- ret = -EINVAL; +- goto release_fw; ++ return -EINVAL; + } + + vaddr = dma_alloc_coherent(cdev->dev, img->size, &paddr, GFP_KERNEL); +- if (!vaddr) { +- ret = -ENOMEM; +- goto release_fw; +- } ++ if (!vaddr) ++ return -ENOMEM; + + memcpy(vaddr, img->data, img->size); + fw = (struct catpt_fw_hdr *)vaddr; +@@ -570,12 +595,10 @@ static int catpt_load_image(struct catpt_dev *cdev, struct dma_chan *chan, + ret = catpt_load_firmware(cdev, chan, paddr, fw); + + dma_free_coherent(cdev->dev, img->size, vaddr, paddr); +-release_fw: +- release_firmware(img); + return ret; + } + +-static int catpt_load_images(struct catpt_dev *cdev, bool restore) ++static int catpt_request_dma_load_firmware(struct catpt_dev *cdev, bool restore) + { + struct dma_chan *chan; + int ret; +@@ -584,8 +607,7 @@ static int catpt_load_images(struct catpt_dev *cdev, bool restore) + if (IS_ERR(chan)) + return PTR_ERR(chan); + +- ret = catpt_load_image(cdev, chan, cdev->spec->fw_name, +- FW_SIGNATURE, restore); ++ ret = catpt_request_load_firmware(cdev, chan, cdev->spec->fw_name, restore); + if (ret) + goto release_dma_chan; + +@@ -605,7 +627,7 @@ int catpt_boot_firmware(struct catpt_dev *cdev, bool restore) + + catpt_dsp_stall(cdev, true); + +- ret = catpt_load_images(cdev, restore); ++ ret = catpt_request_dma_load_firmware(cdev, restore); + if (ret) { + dev_err(cdev->dev, "load binaries failed: %d\n", ret); + return ret; +@@ -619,10 +641,10 @@ int catpt_boot_firmware(struct catpt_dev *cdev, bool restore) + if (!ret) { + dev_err(cdev->dev, "firmware ready timeout\n"); + return -ETIMEDOUT; +- /* Wake up does not mean FW is ready, an exception could occur. */ +- } else if (!cdev->ipc.ready) { +- return -EREMOTEIO; + } ++ /* Wake up does not mean FW is ready, an exception could occur. */ ++ if (!cdev->ipc.ready) ++ return -EREMOTEIO; + + /* update sram pg & clock once done booting */ + catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask); +diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c +--- a/sound/soc/intel/catpt/pcm.c ++++ b/sound/soc/intel/catpt/pcm.c +@@ -75,8 +75,8 @@ static struct catpt_stream_template *catpt_topology[] = { + static struct catpt_stream_template * + catpt_get_stream_template(struct snd_pcm_substream *substream) + { +- struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream); +- struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtm, 0); ++ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); ++ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + enum catpt_stream_type type; + + type = cpu_dai->driver->id; +@@ -159,11 +159,11 @@ static void catpt_stream_read_position(struct catpt_dev *cdev, + static void catpt_arrange_page_table(struct snd_pcm_substream *substream, + struct snd_dma_buffer *pgtbl) + { +- struct snd_pcm_runtime *rtm = substream->runtime; ++ struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_dma_buffer *databuf = snd_pcm_get_dma_buf(substream); + int i, pages; + +- pages = snd_sgbuf_aligned_pages(rtm->dma_bytes); ++ pages = snd_sgbuf_aligned_pages(runtime->dma_bytes); + + for (i = 0; i < pages; i++) { + u32 pfn, offset; +@@ -386,7 +386,7 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) + { +- struct snd_pcm_runtime *rtm = substream->runtime; ++ struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_dma_buffer *dmab; + struct catpt_stream_runtime *stream; + struct catpt_audio_format afmt; +@@ -412,8 +412,8 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream, + + memset(&rinfo, 0, sizeof(rinfo)); + rinfo.page_table_addr = stream->pgtbl.addr; +- rinfo.num_pages = DIV_ROUND_UP(rtm->dma_bytes, PAGE_SIZE); +- rinfo.size = rtm->dma_bytes; ++ rinfo.num_pages = DIV_ROUND_UP(runtime->dma_bytes, PAGE_SIZE); ++ rinfo.size = runtime->dma_bytes; + rinfo.offset = 0; + rinfo.ring_first_page_pfn = PFN_DOWN(snd_sgbuf_get_addr(dmab, 0)); + +@@ -544,11 +544,11 @@ void catpt_stream_update_position(struct catpt_dev *cdev, + struct catpt_notify_position *pos) + { + struct snd_pcm_substream *substream = stream->substream; +- struct snd_pcm_runtime *r = substream->runtime; ++ struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_uframes_t dsppos, newpos; + int ret; + +- dsppos = bytes_to_frames(r, pos->stream_position); ++ dsppos = bytes_to_frames(runtime, pos->stream_position); + + if (!stream->prepared) + goto exit; +@@ -556,8 +556,8 @@ void catpt_stream_update_position(struct catpt_dev *cdev, + if (stream->template->type != CATPT_STRM_TYPE_RENDER) + goto exit; + +- if (dsppos >= r->buffer_size / 2) +- newpos = r->buffer_size / 2; ++ if (dsppos >= runtime->buffer_size / 2) ++ newpos = runtime->buffer_size / 2; + else + newpos = 0; + /* +@@ -565,7 +565,7 @@ void catpt_stream_update_position(struct catpt_dev *cdev, + * (buffer half consumed) update wp to allow stream progression. + */ + ret = catpt_ipc_set_write_pos(cdev, stream->info.stream_hw_id, +- frames_to_bytes(r, newpos), ++ frames_to_bytes(runtime, newpos), + false, false); + if (ret) { + dev_err(cdev->dev, "update position for stream %d failed: %d\n", +@@ -600,11 +600,11 @@ static const struct snd_pcm_hardware catpt_pcm_hardware = { + }; + + static int catpt_component_pcm_new(struct snd_soc_component *component, +- struct snd_soc_pcm_runtime *rtm) ++ struct snd_soc_pcm_runtime *rtd) + { + struct catpt_dev *cdev = dev_get_drvdata(component->dev); + +- snd_pcm_set_managed_buffer_all(rtm->pcm, SNDRV_DMA_TYPE_DEV_SG, ++ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV_SG, + cdev->dev, + catpt_pcm_hardware.buffer_bytes_max, + catpt_pcm_hardware.buffer_bytes_max); +@@ -615,9 +615,9 @@ static int catpt_component_pcm_new(struct snd_soc_component *component, + static int catpt_component_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) + { +- struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream); ++ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + +- if (!rtm->dai_link->no_pcm) ++ if (!rtd->dai_link->no_pcm) + snd_soc_set_runtime_hwparams(substream, &catpt_pcm_hardware); + return 0; + } +@@ -626,13 +626,13 @@ static snd_pcm_uframes_t + catpt_component_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) + { +- struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream); +- struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtm, 0); ++ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); ++ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(component->dev); + u32 pos; + +- if (rtm->dai_link->no_pcm) ++ if (rtd->dai_link->no_pcm) + return 0; + + stream = snd_soc_dai_get_dma_data(cpu_dai, substream); +@@ -650,10 +650,10 @@ static const struct snd_soc_dai_ops catpt_fe_dai_ops = { + .trigger = catpt_dai_trigger, + }; + +-static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtm, ++static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_dai *dai) + { +- struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtm, 0); ++ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); + struct catpt_ssp_device_format devfmt; + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); + int ret; +@@ -871,8 +871,7 @@ static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol) + return CATPT_IPC_RET(ret); + } + +-static int catpt_volume_info(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_info *uinfo) ++static int catpt_volume_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) + { + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = CATPT_CHANNELS_MAX; +@@ -1073,18 +1072,14 @@ int catpt_arm_stream_templates(struct catpt_dev *cdev) + int catpt_register_plat_component(struct catpt_dev *cdev) + { + struct snd_soc_component *component; +- int ret; + +- component = devm_kzalloc(cdev->dev, sizeof(*component), GFP_KERNEL); ++ component = snd_soc_component_alloc(cdev->dev); + if (!component) + return -ENOMEM; + +- ret = snd_soc_component_initialize(component, &catpt_comp_driver, +- cdev->dev); +- if (ret) +- return ret; ++ snd_soc_component_set_name(component, catpt_comp_driver.name); + +- component->name = catpt_comp_driver.name; +- return snd_soc_add_component(component, dai_drivers, +- ARRAY_SIZE(dai_drivers)); ++ return snd_soc_register_component(component, ++ &catpt_comp_driver, ++ dai_drivers, ARRAY_SIZE(dai_drivers)); + } +diff --git a/sound/soc/intel/common/soc-acpi-intel-arl-match.c b/sound/soc/intel/common/soc-acpi-intel-arl-match.c +--- a/sound/soc/intel/common/soc-acpi-intel-arl-match.c ++++ b/sound/soc/intel/common/soc-acpi-intel-arl-match.c +@@ -572,6 +572,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = { + .links = arl_rt711_l0_rt1316_l3, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-arl-rt711-l0-rt1316-l3.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0) | BIT(3), +@@ -600,12 +601,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = { + .links = arl_rvp, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-arl-rt711.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = 0x1, /* link0 required */ + .links = arl_sdca_rvp, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-arl-rt711-l0.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(2), +diff --git a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c +--- a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c ++++ b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c +@@ -717,24 +717,28 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { + .links = lnl_3_in_1_sdca, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-rt711-l0-rt1316-l23-rt714-l1.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0) | BIT(2) | BIT(3), + .links = lnl_cs42l43_l0_cs35l56_l23, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-cs42l43-l0-cs35l56-l23.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(1) | BIT(2) | BIT(3), + .links = lnl_cs42l43_l2_cs35l56x6_l13, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-cs42l43-l2-cs35l56x6-l13.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0) | BIT(3), + .links = lnl_cs42l43_l0_cs35l56_l3, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-cs42l43-l0-cs35l56-l3.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0), +@@ -748,12 +752,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { + .links = lnl_rvp, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-rt711.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(2) | BIT(3), + .links = lnl_712_only, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-rt712-l2-rt1712-l3.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0), +@@ -766,19 +772,22 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { + .link_mask = GENMASK(2, 0), + .links = lnl_sdw_rt1318_l12_rt714_l0, + .drv_name = "sof_sdw", +- .sof_tplg_filename = "sof-lnl-rt1318-l12-rt714-l0.tplg" ++ .sof_tplg_filename = "sof-lnl-rt1318-l12-rt714-l0.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = GENMASK(2, 0), + .links = lnl_sdw_rt1320_l12_rt714_l0, + .drv_name = "sof_sdw", +- .sof_tplg_filename = "sof-lnl-rt1320-l12-rt714-l0.tplg" ++ .sof_tplg_filename = "sof-lnl-rt1320-l12-rt714-l0.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0) | BIT(1), + .links = lnl_sdw_rt713_l0_rt1318_l1, + .drv_name = "sof_sdw", +- .sof_tplg_filename = "sof-lnl-rt713-l0-rt1318-l1.tplg" ++ .sof_tplg_filename = "sof-lnl-rt713-l0-rt1318-l1.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(1) | BIT(2), +diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c +--- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c ++++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c +@@ -1321,60 +1321,70 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { + .links = tac5572_l0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-tac5572.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0), + .links = tac5672_l0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-tac5672.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0), + .links = tac5682_l0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-tac5682.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0), + .links = tas2783_link0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-tas2783.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0), + .links = tas2883_l0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-tas2883.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = GENMASK(3, 0), + .links = mtl_rt713_l0_rt1316_l12_rt1713_l3, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-rt713-l0-rt1316-l12-rt1713-l3.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = GENMASK(3, 0), + .links = mtl_rt713_l0_rt1318_l12_rt1713_l3, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-rt713-l0-rt1318-l12-rt1713-l3.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0) | BIT(1) | BIT(3), + .links = mtl_rt713_l0_rt1318_l1_rt1713_l3, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-rt713-l0-rt1318-l1-rt1713-l3.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = GENMASK(2, 0), + .links = mtl_rt713_l0_rt1316_l12, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-rt713-l0-rt1316-l12.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(3) | BIT(0), + .links = mtl_712_l0_1712_l3, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-rt712-l0-rt1712-l3.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0), +@@ -1395,7 +1405,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { + .link_mask = GENMASK(2, 0), + .links = mtl_sdw_rt1318_l12_rt714_l0, + .drv_name = "sof_sdw", +- .sof_tplg_filename = "sof-mtl-rt1318-l12-rt714-l0.tplg" ++ .sof_tplg_filename = "sof-mtl-rt1318-l12-rt714-l0.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0) | BIT(2) | BIT(3), +@@ -1455,12 +1466,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { + .links = mtl_3_in_1_sdca, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-rt711-l0-rt1316-l23-rt714-l1.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = 0x9, /* 2 active links required */ + .links = mtl_rt711_l0_rt1316_l3, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-rt711-l0-rt1316-l3.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0), +@@ -1474,18 +1487,21 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { + .links = mtl_rvp, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-rt711.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0) | BIT(2), + .links = rt5682_link2_max98373_link0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-sdw-rt5682-l2-max98373-l0.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0) | BIT(2), + .links = cs42l42_link0_max98363_link2, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-sdw-cs42l42-l0-max98363-l2.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + {}, + }; +diff --git a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c +--- a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c ++++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c +@@ -492,7 +492,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = { + .links = ptl_rt722_l0_rt1320_l23, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-ptl-rt722-l0-rt1320-l23.tplg", +- .get_function_tplg_files = sof_sdw_get_tplg_files, ++ .machine_check = snd_soc_acpi_intel_no_function_topology, + }, + { + .link_mask = BIT(1) | BIT(2), +@@ -522,14 +522,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = { + .links = ptl_rvp, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-ptl-rt711.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(3), + .links = ptl_sdw_rt712_vb_l3_rt1320_l3, + .drv_name = "sof_sdw", +- .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, ++ .machine_check = snd_soc_acpi_intel_rt712_vb_no_function_topology, + .sof_tplg_filename = "sof-ptl-rt712-l3-rt1320-l3.tplg", +- .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + {}, + }; +diff --git a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c +--- a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c ++++ b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c +@@ -53,6 +53,14 @@ bool snd_soc_acpi_intel_no_function_topology(void *arg) + } + EXPORT_SYMBOL_NS(snd_soc_acpi_intel_no_function_topology, "SND_SOC_ACPI_INTEL_SDCA_QUIRKS"); + ++bool snd_soc_acpi_intel_rt712_vb_no_function_topology(void *arg) ++{ ++ return snd_soc_acpi_intel_sdca_is_device_rt712_vb(arg) && ++ snd_soc_acpi_intel_no_function_topology(arg); ++} ++EXPORT_SYMBOL_NS(snd_soc_acpi_intel_rt712_vb_no_function_topology, ++ "SND_SOC_ACPI_INTEL_SDCA_QUIRKS"); ++ + MODULE_DESCRIPTION("ASoC ACPI Intel SDCA quirks"); + MODULE_LICENSE("GPL"); + MODULE_IMPORT_NS("SND_SOC_SDCA"); +diff --git a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h +--- a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h ++++ b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h +@@ -11,5 +11,6 @@ + + bool snd_soc_acpi_intel_sdca_is_device_rt712_vb(void *arg); + bool snd_soc_acpi_intel_no_function_topology(void *arg); ++bool snd_soc_acpi_intel_rt712_vb_no_function_topology(void *arg); + + #endif +diff --git a/sound/soc/intel/keembay/kmb_platform.c b/sound/soc/intel/keembay/kmb_platform.c +--- a/sound/soc/intel/keembay/kmb_platform.c ++++ b/sound/soc/intel/keembay/kmb_platform.c +@@ -873,6 +873,8 @@ static int kmb_plat_dai_probe(struct platform_device *pdev) + + if (kmb_i2s->use_pio) { + irq = platform_get_irq_optional(pdev, 0); ++ if (irq == -EPROBE_DEFER) ++ return irq; + if (irq > 0) { + ret = devm_request_irq(dev, irq, kmb_i2s_irq_handler, 0, + pdev->name, kmb_i2s); +diff --git a/sound/soc/loongson/loongson_card.c b/sound/soc/loongson/loongson_card.c +--- a/sound/soc/loongson/loongson_card.c ++++ b/sound/soc/loongson/loongson_card.c +@@ -2,24 +2,131 @@ + // + // Loongson ASoC Audio Machine driver + // +-// Copyright (C) 2023 Loongson Technology Corporation Limited ++// Copyright (C) 2023-2026 Loongson Technology Corporation Limited + // Author: Yingkun Meng ++// Binbin Zhou + // + ++#include ++#include + #include ++#include ++#include ++#include + #include + #include +-#include +-#include +-#include + + static char codec_name[SND_ACPI_I2C_ID_LEN]; + + struct loongson_card_data { + struct snd_soc_card snd_card; + unsigned int mclk_fs; ++ struct gpio_desc *gpiod_hp_det; ++ struct gpio_desc *gpiod_hp_ctl; ++ struct gpio_desc *gpiod_spkr_en; ++ const struct loongson_card_config *cfg; + }; + ++struct loongson_card_config { ++ unsigned int fmt; ++ bool add_hp_jack; ++ bool add_dapm_widgets; ++ bool add_dapm_routes; ++}; ++ ++static const struct loongson_card_config ls2k1000_card_config = { ++ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBC_CFC, ++ .add_hp_jack = false, ++ .add_dapm_widgets = false, ++ .add_dapm_routes = false, ++}; ++ ++static const struct loongson_card_config ls2k0300_forever_pi_card_config = { ++ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC, ++ .add_hp_jack = false, ++ .add_dapm_widgets = false, ++ .add_dapm_routes = false, ++}; ++ ++static const struct loongson_card_config ls2k0300_dl2k0300b_card_config = { ++ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC, ++ .add_hp_jack = true, ++ .add_dapm_widgets = true, ++ .add_dapm_routes = true, ++}; ++ ++static int tegra_machine_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *k, int event) ++{ ++ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm); ++ struct loongson_card_data *priv = snd_soc_card_get_drvdata(card); ++ ++ if (!snd_soc_dapm_widget_name_cmp(w, "Speaker")) ++ gpiod_set_value_cansleep(priv->gpiod_spkr_en, ++ SND_SOC_DAPM_EVENT_ON(event)); ++ ++ if (!snd_soc_dapm_widget_name_cmp(w, "Headphone")) ++ gpiod_set_value_cansleep(priv->gpiod_hp_ctl, ++ SND_SOC_DAPM_EVENT_ON(event)); ++ ++ return 0; ++} ++ ++static const struct snd_soc_dapm_widget loongson_aosc_dapm_widgets[] = { ++ SND_SOC_DAPM_HP("Headphone", tegra_machine_event), ++ SND_SOC_DAPM_SPK("Speaker", tegra_machine_event), ++}; ++ ++/* Headphones Jack */ ++ ++static struct snd_soc_jack loongson_asoc_hp_jack; ++ ++static struct snd_soc_jack_pin loongson_asoc_hp_jack_pins[] = { ++ { ++ .pin = "Headphone", ++ .mask = SND_JACK_HEADPHONE ++ }, ++ { ++ .pin = "Speaker", ++ .mask = SND_JACK_HEADPHONE, ++ .invert = 1 ++ }, ++}; ++ ++static struct snd_soc_jack_gpio loongson_asoc_hp_jack_gpio = { ++ .name = "Headphones detection", ++ .report = SND_JACK_HEADPHONE, ++ .debounce_time = 150, ++}; ++ ++static int loongson_asoc_machine_init(struct snd_soc_pcm_runtime *rtd) ++{ ++ struct snd_soc_card *card = rtd->card; ++ struct loongson_card_data *ls_priv = snd_soc_card_get_drvdata(card); ++ int ret = 0; ++ ++ if (!ls_priv->cfg->add_hp_jack || !ls_priv->gpiod_hp_det) ++ return 0; ++ ++ ret = snd_soc_card_jack_new_pins(card, "Headphones Jack", ++ SND_JACK_HEADPHONE, ++ &loongson_asoc_hp_jack, ++ loongson_asoc_hp_jack_pins, ++ ARRAY_SIZE(loongson_asoc_hp_jack_pins)); ++ if (ret) { ++ dev_err(rtd->dev, "Headphones Jack creation failed: %d\n", ret); ++ return ret; ++ } ++ ++ loongson_asoc_hp_jack_gpio.desc = ls_priv->gpiod_hp_det; ++ ++ ret = snd_soc_jack_add_gpios(&loongson_asoc_hp_jack, 1, &loongson_asoc_hp_jack_gpio); ++ if (ret) ++ dev_err(rtd->dev, "Headphone GPIO not added: %d\n", ret); ++ ++ return ret; ++} ++ + static int loongson_card_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) + { +@@ -45,7 +152,7 @@ static int loongson_card_hw_params(struct snd_pcm_substream *substream, + return ret; + } + +- return 0; ++ return snd_soc_runtime_set_dai_fmt(rtd, ls_card->cfg->fmt); + } + + static const struct snd_soc_ops loongson_ops = { +@@ -61,8 +168,7 @@ static struct snd_soc_dai_link loongson_dai_links[] = { + { + .name = "Loongson Audio Port", + .stream_name = "Loongson Audio", +- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_NF +- | SND_SOC_DAIFMT_CBC_CFC, ++ .init = loongson_asoc_machine_init, + SND_SOC_DAILINK_REG(analog), + .ops = &loongson_ops, + }, +@@ -123,16 +229,35 @@ static int loongson_card_parse_acpi(struct loongson_card_data *data) + + static int loongson_card_parse_of(struct loongson_card_data *data) + { +- struct device_node *cpu, *codec; + struct snd_soc_card *card = &data->snd_card; ++ struct device_node *cpu, *codec; + struct device *dev = card->dev; + int ret, i; + ++ data->gpiod_hp_det = devm_gpiod_get_optional(dev, "hp-det", GPIOD_IN); ++ if (IS_ERR(data->gpiod_hp_det)) ++ return PTR_ERR(data->gpiod_hp_det); ++ ++ data->gpiod_hp_ctl = devm_gpiod_get_optional(dev, "hp-ctl", GPIOD_OUT_LOW); ++ if (IS_ERR(data->gpiod_hp_ctl)) ++ return PTR_ERR(data->gpiod_hp_ctl); ++ ++ data->gpiod_spkr_en = devm_gpiod_get_optional(dev, "spkr-en", GPIOD_OUT_LOW); ++ if (IS_ERR(data->gpiod_spkr_en)) ++ return PTR_ERR(data->gpiod_spkr_en); ++ ++ if (data->cfg->add_dapm_routes) { ++ ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); ++ if (ret) ++ return ret; ++ } ++ + cpu = of_get_child_by_name(dev->of_node, "cpu"); + if (!cpu) { + dev_err(dev, "platform property missing or invalid\n"); + return -EINVAL; + } ++ + codec = of_get_child_by_name(dev->of_node, "codec"); + if (!codec) { + dev_err(dev, "audio-codec property missing or invalid\n"); +@@ -177,12 +302,22 @@ static int loongson_asoc_card_probe(struct platform_device *pdev) + if (!ls_priv) + return -ENOMEM; + ++ ls_priv->cfg = (const struct loongson_card_config *)device_get_match_data(dev); ++ if (!ls_priv->cfg) ++ return -EINVAL; ++ + card = &ls_priv->snd_card; + + card->dev = dev; + card->owner = THIS_MODULE; + card->dai_link = loongson_dai_links; + card->num_links = ARRAY_SIZE(loongson_dai_links); ++ ++ if (ls_priv->cfg->add_dapm_widgets) { ++ card->dapm_widgets = loongson_aosc_dapm_widgets; ++ card->num_dapm_widgets = ARRAY_SIZE(loongson_aosc_dapm_widgets); ++ } ++ + snd_soc_card_set_drvdata(card, ls_priv); + + ret = device_property_read_string(dev, "model", &card->name); +@@ -202,7 +337,19 @@ static int loongson_asoc_card_probe(struct platform_device *pdev) + } + + static const struct of_device_id loongson_asoc_dt_ids[] = { +- { .compatible = "loongson,ls-audio-card" }, ++ /* Loongson-2K1000/Loongson-2K2000/LS7A */ ++ { ++ .compatible = "loongson,ls-audio-card", ++ .data = &ls2k1000_card_config ++ }, ++ { ++ .compatible = "loongson,ls2k0300-forever-pi-audio-card", ++ .data = &ls2k0300_forever_pi_card_config ++ }, ++ { ++ .compatible = "loongson,ls2k0300-dl2k0300b-audio-card", ++ .data = &ls2k0300_dl2k0300b_card_config ++ }, + { /* sentinel */ }, + }; + MODULE_DEVICE_TABLE(of, loongson_asoc_dt_ids); +diff --git a/sound/soc/loongson/loongson_i2s_plat.c b/sound/soc/loongson/loongson_i2s_plat.c +--- a/sound/soc/loongson/loongson_i2s_plat.c ++++ b/sound/soc/loongson/loongson_i2s_plat.c +@@ -2,7 +2,7 @@ + // + // Loongson I2S controller master mode dirver(platform device) + // +-// Copyright (C) 2023-2024 Loongson Technology Corporation Limited ++// Copyright (C) 2023-2026 Loongson Technology Corporation Limited + // + // Author: Yingkun Meng + // Binbin Zhou +@@ -21,6 +21,7 @@ + #include "loongson_i2s.h" + #include "loongson_dma.h" + ++/* Loongson-2K1000 APBDMA routing */ + #define LOONGSON_I2S_RX_DMA_OFFSET 21 + #define LOONGSON_I2S_TX_DMA_OFFSET 18 + +@@ -30,6 +31,11 @@ + #define LOONGSON_DMA3_CONF 0x3 + #define LOONGSON_DMA4_CONF 0x4 + ++struct loongson_i2s_plat_config { ++ int rev_id; ++ int (*i2s_dma_config)(struct platform_device *pdev); ++}; ++ + static int loongson_i2s_apbdma_config(struct platform_device *pdev) + { + int val; +@@ -47,8 +53,18 @@ static int loongson_i2s_apbdma_config(struct platform_device *pdev) + return 0; + } + ++static const struct loongson_i2s_plat_config ls2k0300_i2s_plat_config = { ++ .rev_id = 1, ++}; ++ ++static const struct loongson_i2s_plat_config ls2k1000_i2s_plat_config = { ++ .rev_id = 0, ++ .i2s_dma_config = loongson_i2s_apbdma_config, ++}; ++ + static int loongson_i2s_plat_probe(struct platform_device *pdev) + { ++ const struct loongson_i2s_plat_config *plat_config; + struct device *dev = &pdev->dev; + struct loongson_i2s *i2s; + struct resource *res; +@@ -59,12 +75,17 @@ static int loongson_i2s_plat_probe(struct platform_device *pdev) + if (!i2s) + return -ENOMEM; + +- ret = loongson_i2s_apbdma_config(pdev); +- if (ret) +- return ret; ++ plat_config = device_get_match_data(dev); ++ if (!plat_config) ++ return -EINVAL; + +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- i2s->reg_base = devm_ioremap_resource(&pdev->dev, res); ++ if (plat_config->i2s_dma_config) { ++ ret = plat_config->i2s_dma_config(pdev); ++ if (ret) ++ return ret; ++ } ++ ++ i2s->reg_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(i2s->reg_base)) + return dev_err_probe(dev, PTR_ERR(i2s->reg_base), + "devm_ioremap_resource failed\n"); +@@ -87,11 +108,17 @@ static int loongson_i2s_plat_probe(struct platform_device *pdev) + if (IS_ERR(i2s_clk)) + return dev_err_probe(dev, PTR_ERR(i2s_clk), "clock property invalid\n"); + i2s->clk_rate = clk_get_rate(i2s_clk); ++ i2s->rev_id = plat_config->rev_id; + + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + dev_set_name(dev, LS_I2S_DRVNAME); + dev_set_drvdata(dev, i2s); + ++ if (i2s->rev_id == 1) { ++ regmap_update_bits(i2s->regmap, LS_I2S_CTRL, I2S_CTRL_RESET, I2S_CTRL_RESET); ++ fsleep(200); ++ } ++ + ret = devm_snd_soc_register_component(dev, &loongson_i2s_edma_component, + &loongson_i2s_dai, 1); + if (ret) +@@ -102,7 +129,8 @@ static int loongson_i2s_plat_probe(struct platform_device *pdev) + } + + static const struct of_device_id loongson_i2s_ids[] = { +- { .compatible = "loongson,ls2k1000-i2s" }, ++ { .compatible = "loongson,ls2k0300-i2s", .data = &ls2k0300_i2s_plat_config }, ++ { .compatible = "loongson,ls2k1000-i2s", .data = &ls2k1000_i2s_plat_config }, + { /* sentinel */ }, + }; + MODULE_DEVICE_TABLE(of, loongson_i2s_ids); +diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig +--- a/sound/soc/mediatek/Kconfig ++++ b/sound/soc/mediatek/Kconfig +@@ -7,7 +7,7 @@ config SND_SOC_MEDIATEK + + config SND_SOC_MT2701 + tristate "ASoC support for Mediatek MT2701 chip" +- depends on ARCH_MEDIATEK ++ depends on ARCH_MEDIATEK || COMPILE_TEST + select SND_SOC_MEDIATEK + help + This adds ASoC driver for Mediatek MT2701 boards +diff --git a/sound/soc/mediatek/common/mtk-dsp-sof-common.c b/sound/soc/mediatek/common/mtk-dsp-sof-common.c +--- a/sound/soc/mediatek/common/mtk-dsp-sof-common.c ++++ b/sound/soc/mediatek/common/mtk-dsp-sof-common.c +@@ -232,6 +232,7 @@ int mtk_sof_dailink_parse_of(struct device *dev, struct snd_soc_card *card, + const char *propname) + { + struct device_node *np = dev->of_node; ++ struct snd_soc_dai_link *dai_link; + struct snd_soc_dai_link *parsed_dai_link; + const char *dai_name = NULL; + int i, j, ret, num_links, parsed_num_links = 0; +@@ -254,9 +255,9 @@ int mtk_sof_dailink_parse_of(struct device *dev, struct snd_soc_card *card, + return ret; + } + dev_dbg(dev, "ASoC: Property get dai_name:%s\n", dai_name); +- for (j = 0; j < card->num_links; j++) { +- if (!strcmp(dai_name, card->dai_link[j].name)) { +- memcpy(&parsed_dai_link[parsed_num_links++], &card->dai_link[j], ++ for_each_card_prelinks(card, j, dai_link) { ++ if (!strcmp(dai_name, dai_link->name)) { ++ memcpy(&parsed_dai_link[parsed_num_links++], dai_link, + sizeof(struct snd_soc_dai_link)); + break; + } +diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c +--- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c ++++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c +@@ -315,7 +315,7 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev) + &mt8173_rt5650_priv.pll_from); + if (ret) { + dev_err(&pdev->dev, +- "%s snd_soc_register_card fail %d\n", ++ "%s device_property_read_u32() fail %d\n", + __func__, ret); + } + } +diff --git a/sound/soc/mediatek/mt8189/mt8189-dai-adda.c b/sound/soc/mediatek/mt8189/mt8189-dai-adda.c +--- a/sound/soc/mediatek/mt8189/mt8189-dai-adda.c ++++ b/sound/soc/mediatek/mt8189/mt8189-dai-adda.c +@@ -385,8 +385,6 @@ static int mtk_adda_pad_top_event(struct snd_soc_dapm_widget *w, + if (event == SND_SOC_DAPM_PRE_PMU) { + if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2_CLK_P2) + regmap_write(afe->regmap, AFE_AUD_PAD_TOP_CFG0, 0xB8); +- else if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2) +- regmap_write(afe->regmap, AFE_AUD_PAD_TOP_CFG0, 0xB0); + else + regmap_write(afe->regmap, AFE_AUD_PAD_TOP_CFG0, 0xB0); + } +diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c +--- a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c ++++ b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c +@@ -1019,7 +1019,7 @@ static struct snd_soc_card mt8192_mt6359_rt1015p_rt5682x_card = { + .num_dapm_routes = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682x_routes), + }; + +-static int mt8192_mt6359_card_set_be_link(struct snd_soc_card *card, ++static int mt8192_mt6359_card_set_be_link(struct device *dev, + struct snd_soc_dai_link *link, + struct device_node *node, + char *link_name) +@@ -1027,9 +1027,9 @@ static int mt8192_mt6359_card_set_be_link(struct snd_soc_card *card, + int ret; + + if (node && strcmp(link->name, link_name) == 0) { +- ret = snd_soc_of_get_dai_link_codecs(card->dev, node, link); ++ ret = snd_soc_of_get_dai_link_codecs(dev, node, link); + if (ret < 0) { +- dev_err_probe(card->dev, ret, "get dai link codecs fail\n"); ++ dev_err_probe(dev, ret, "get dai link codecs fail\n"); + return ret; + } + } +@@ -1065,21 +1065,21 @@ static int mt8192_mt6359_legacy_probe(struct mtk_soc_card_data *soc_card_data) + } + + for_each_card_prelinks(card, i, dai_link) { +- ret = mt8192_mt6359_card_set_be_link(card, dai_link, speaker_codec, "I2S3"); ++ ret = mt8192_mt6359_card_set_be_link(dev, dai_link, speaker_codec, "I2S3"); + if (ret) { + dev_err_probe(dev, ret, "%s set speaker_codec fail\n", + dai_link->name); + break; + } + +- ret = mt8192_mt6359_card_set_be_link(card, dai_link, headset_codec, "I2S8"); ++ ret = mt8192_mt6359_card_set_be_link(dev, dai_link, headset_codec, "I2S8"); + if (ret) { + dev_err_probe(dev, ret, "%s set headset_codec fail\n", + dai_link->name); + break; + } + +- ret = mt8192_mt6359_card_set_be_link(card, dai_link, headset_codec, "I2S9"); ++ ret = mt8192_mt6359_card_set_be_link(dev, dai_link, headset_codec, "I2S9"); + if (ret) { + dev_err_probe(dev, ret, "%s set headset_codec fail\n", + dai_link->name); +diff --git a/sound/soc/mediatek/mt8196/mt8196-nau8825.c b/sound/soc/mediatek/mt8196/mt8196-nau8825.c +--- a/sound/soc/mediatek/mt8196/mt8196-nau8825.c ++++ b/sound/soc/mediatek/mt8196/mt8196-nau8825.c +@@ -104,8 +104,6 @@ static const struct snd_kcontrol_new mt8196_nau8825_controls[] = { + + #define EXT_SPK_AMP_W_NAME "Ext_Speaker_Amp" + +-static struct snd_soc_card mt8196_nau8825_soc_card; +- + static const struct snd_soc_dapm_widget mt8196_nau8825_card_widgets[] = { + /* SOF Uplink */ + SND_SOC_DAPM_MIXER("SOF_DMA_UL0", SND_SOC_NOPM, 0, 0, NULL, 0), +diff --git a/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c b/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c +--- a/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c ++++ b/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c +@@ -2120,19 +2120,15 @@ static int mt8365_afe_pcm_dev_probe(struct platform_device *pdev) + spin_lock_init(&afe_priv->afe_ctrl_lock); + mutex_init(&afe_priv->afe_clk_mutex); + +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- afe->base_addr = devm_ioremap_resource(&pdev->dev, res); ++ afe->base_addr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(afe->base_addr)) + return PTR_ERR(afe->base_addr); + +- res = platform_get_resource(pdev, IORESOURCE_MEM, 1); +- if (res) { +- afe_priv->afe_sram_vir_addr = +- devm_ioremap_resource(&pdev->dev, res); +- if (!IS_ERR(afe_priv->afe_sram_vir_addr)) { +- afe_priv->afe_sram_phy_addr = res->start; +- afe_priv->afe_sram_size = resource_size(res); +- } ++ afe_priv->afe_sram_vir_addr = ++ devm_platform_get_and_ioremap_resource(pdev, 1, &res); ++ if (!IS_ERR(afe_priv->afe_sram_vir_addr)) { ++ afe_priv->afe_sram_phy_addr = res->start; ++ afe_priv->afe_sram_size = resource_size(res); + } + + /* initial audio related clock */ +diff --git a/sound/soc/mediatek/mt8365/mt8365-mt6357.c b/sound/soc/mediatek/mt8365/mt8365-mt6357.c +--- a/sound/soc/mediatek/mt8365/mt8365-mt6357.c ++++ b/sound/soc/mediatek/mt8365/mt8365-mt6357.c +@@ -70,7 +70,8 @@ static const struct snd_soc_dapm_route mt8365_mt6357_routes[] = { + static int mt8365_mt6357_int_adda_startup(struct snd_pcm_substream *substream) + { + struct snd_soc_pcm_runtime *rtd = substream->private_data; +- struct mt8365_mt6357_priv *priv = snd_soc_card_get_drvdata(rtd->card); ++ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card); ++ struct mt8365_mt6357_priv *priv = soc_card_data->mach_priv; + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +@@ -101,7 +102,8 @@ static int mt8365_mt6357_int_adda_startup(struct snd_pcm_substream *substream) + static void mt8365_mt6357_int_adda_shutdown(struct snd_pcm_substream *substream) + { + struct snd_soc_pcm_runtime *rtd = substream->private_data; +- struct mt8365_mt6357_priv *priv = snd_soc_card_get_drvdata(rtd->card); ++ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card); ++ struct mt8365_mt6357_priv *priv = soc_card_data->mach_priv; + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +@@ -245,7 +247,8 @@ static struct snd_soc_dai_link mt8365_mt6357_dais[] = { + + static int mt8365_mt6357_gpio_probe(struct snd_soc_card *card) + { +- struct mt8365_mt6357_priv *priv = snd_soc_card_get_drvdata(card); ++ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card); ++ struct mt8365_mt6357_priv *priv = soc_card_data->mach_priv; + struct device *dev = card->dev; + int ret, i; + +@@ -294,7 +297,6 @@ static int mt8365_mt6357_dev_probe(struct mtk_soc_card_data *soc_card_data, bool + struct mt8365_mt6357_priv *mach_priv; + int ret; + +- card->dev = dev; + ret = parse_dai_link_info(card); + if (ret) + goto err; +diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile +--- a/sound/soc/meson/Makefile ++++ b/sound/soc/meson/Makefile +@@ -4,6 +4,8 @@ snd-soc-meson-aiu-y := aiu.o + snd-soc-meson-aiu-y += aiu-acodec-ctrl.o + snd-soc-meson-aiu-y += aiu-codec-ctrl.o + snd-soc-meson-aiu-y += aiu-encoder-i2s.o ++snd-soc-meson-aiu-y += gx-formatter.o ++snd-soc-meson-aiu-y += aiu-formatter-i2s.o + snd-soc-meson-aiu-y += aiu-encoder-spdif.o + snd-soc-meson-aiu-y += aiu-fifo.o + snd-soc-meson-aiu-y += aiu-fifo-i2s.o +diff --git a/sound/soc/meson/aiu-encoder-i2s.c b/sound/soc/meson/aiu-encoder-i2s.c +--- a/sound/soc/meson/aiu-encoder-i2s.c ++++ b/sound/soc/meson/aiu-encoder-i2s.c +@@ -10,14 +10,11 @@ + #include + + #include "aiu.h" ++#include "gx-formatter.h" ++#include "gx-interface.h" + +-#define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0) +-#define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5) +-#define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9) + #define AIU_I2S_SOURCE_DESC_MODE_SPLIT BIT(11) +-#define AIU_RST_SOFT_I2S_FAST BIT(0) + +-#define AIU_I2S_DAC_CFG_MSB_FIRST BIT(2) + #define AIU_CLK_CTRL_I2S_DIV_EN BIT(0) + #define AIU_CLK_CTRL_I2S_DIV GENMASK(3, 2) + #define AIU_CLK_CTRL_AOCLK_INVERT BIT(6) +@@ -35,49 +32,6 @@ static void aiu_encoder_i2s_divider_enable(struct snd_soc_component *component, + enable ? AIU_CLK_CTRL_I2S_DIV_EN : 0); + } + +-static int aiu_encoder_i2s_setup_desc(struct snd_soc_component *component, +- struct snd_pcm_hw_params *params) +-{ +- /* Always operate in split (classic interleaved) mode */ +- unsigned int desc = AIU_I2S_SOURCE_DESC_MODE_SPLIT; +- +- /* Reset required to update the pipeline */ +- snd_soc_component_write(component, AIU_RST_SOFT, AIU_RST_SOFT_I2S_FAST); +- snd_soc_component_read(component, AIU_I2S_SYNC); +- +- switch (params_physical_width(params)) { +- case 16: /* Nothing to do */ +- break; +- +- case 32: +- desc |= (AIU_I2S_SOURCE_DESC_MODE_24BIT | +- AIU_I2S_SOURCE_DESC_MODE_32BIT); +- break; +- +- default: +- return -EINVAL; +- } +- +- switch (params_channels(params)) { +- case 2: /* Nothing to do */ +- break; +- case 8: +- desc |= AIU_I2S_SOURCE_DESC_MODE_8CH; +- break; +- default: +- return -EINVAL; +- } +- +- snd_soc_component_update_bits(component, AIU_I2S_SOURCE_DESC, +- AIU_I2S_SOURCE_DESC_MODE_8CH | +- AIU_I2S_SOURCE_DESC_MODE_24BIT | +- AIU_I2S_SOURCE_DESC_MODE_32BIT | +- AIU_I2S_SOURCE_DESC_MODE_SPLIT, +- desc); +- +- return 0; +-} +- + static int aiu_encoder_i2s_set_legacy_div(struct snd_soc_component *component, + struct snd_pcm_hw_params *params, + unsigned int bs) +@@ -108,6 +62,32 @@ static int aiu_encoder_i2s_set_legacy_div(struct snd_soc_component *component, + return 0; + } + ++/* ++ * Return true if the given combination of channels and sample width requires ++ * the bs quirk. Return false otherwise. ++ */ ++static bool aiu_encoder_is_bs_quirk(unsigned int channels, int width) ++{ ++ return (channels == 8) && (width == 16); ++} ++ ++static int aiu_encoder_check_bs_quirk(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ struct gx_stream *other_stream = snd_soc_dai_dma_data_get(dai, !substream->stream); ++ ++ /* Nothing to do if the other stream doesn't exist or it's not configured yet. */ ++ if (!other_stream || !other_stream->channels) ++ return 0; ++ ++ if (aiu_encoder_is_bs_quirk(other_stream->channels, other_stream->width) != ++ aiu_encoder_is_bs_quirk(params_channels(params), params_width(params))) ++ return -EINVAL; ++ ++ return 0; ++} ++ + static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component, + struct snd_pcm_hw_params *params, + unsigned int bs) +@@ -119,7 +99,7 @@ static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component, + * increased by 50% to get the correct output rate. + * No idea why ! + */ +- if (params_width(params) == 16 && params_channels(params) == 8) { ++ if (aiu_encoder_is_bs_quirk(params_channels(params), params_width(params))) { + if (bs % 2) { + dev_err(component->dev, + "Cannot increase i2s divider by 50%%\n"); +@@ -141,25 +121,23 @@ static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component, + return 0; + } + +-static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component, +- struct snd_pcm_hw_params *params) ++static int aiu_encoder_i2s_set_clocks(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) + { ++ struct snd_soc_component *component = dai->component; + struct aiu *aiu = snd_soc_component_get_drvdata(component); ++ struct gx_iface *iface = &aiu->i2s.iface; + unsigned int srate = params_rate(params); + unsigned int fs, bs; + int ret; + + /* Get the oversampling factor */ +- fs = DIV_ROUND_CLOSEST(clk_get_rate(aiu->i2s.clks[MCLK].clk), srate); ++ fs = DIV_ROUND_CLOSEST(iface->mclk_rate, srate); + +- if (fs % 64) ++ if ((fs % 64) || (fs == 0)) + return -EINVAL; + +- /* Send data MSB first */ +- snd_soc_component_update_bits(component, AIU_I2S_DAC_CFG, +- AIU_I2S_DAC_CFG_MSB_FIRST, +- AIU_I2S_DAC_CFG_MSB_FIRST); +- + /* Set bclk to lrlck ratio */ + snd_soc_component_update_bits(component, AIU_CODEC_DAC_LRCLK_CTRL, + AIU_CODEC_DAC_LRCLK_CTRL_DIV, +@@ -168,10 +146,22 @@ static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component, + + bs = fs / 64; + +- if (aiu->platform->has_clk_ctrl_more_i2s_div) ++ if (aiu->platform->has_clk_ctrl_more_i2s_div) { ++ /* ++ * The hw rules added in startup() make this unreachable in the ++ * sequential case, but both streams may be refined concurrently ++ * before either commits its config, since only ops->hw_params ++ * runs under the card's pcm_mutex. Re-check against the committed ++ * state of the other stream, which is stable under that mutex. ++ */ ++ if (aiu_encoder_check_bs_quirk(substream, params, dai)) { ++ dev_err(dai->dev, "bclk requirements incompatible with other stream\n"); ++ return -EINVAL; ++ } + ret = aiu_encoder_i2s_set_more_div(component, params, bs); +- else ++ } else { + ret = aiu_encoder_i2s_set_legacy_div(component, params, bs); ++ } + + if (ret) + return ret; +@@ -188,23 +178,37 @@ static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) + { ++ struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); ++ int ret; ++ ++ ret = aiu_encoder_i2s_set_clocks(substream, params, dai); ++ if (ret) { ++ dev_err(dai->dev, "setting i2s clocks failed: %d\n", ret); ++ return ret; ++ } ++ ++ ts->physical_width = params_physical_width(params); ++ ts->width = params_width(params); ++ ts->channels = params_channels(params); ++ ++ return 0; ++} ++ ++static int aiu_encoder_i2s_prepare(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); + struct snd_soc_component *component = dai->component; + int ret; + +- /* Disable the clock while changing the settings */ +- aiu_encoder_i2s_divider_enable(component, false); ++ if (ts->clk_enabled) ++ return 0; + +- ret = aiu_encoder_i2s_setup_desc(component, params); +- if (ret) { +- dev_err(dai->dev, "setting i2s desc failed\n"); ++ ret = clk_prepare_enable(ts->iface->mclk); ++ if (ret) + return ret; +- } + +- ret = aiu_encoder_i2s_set_clocks(component, params); +- if (ret) { +- dev_err(dai->dev, "setting i2s clocks failed\n"); +- return ret; +- } ++ ts->clk_enabled = true; + + aiu_encoder_i2s_divider_enable(component, true); + +@@ -214,9 +218,24 @@ static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream, + static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + { ++ struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); + struct snd_soc_component *component = dai->component; + +- aiu_encoder_i2s_divider_enable(component, false); ++ /* ++ * If this is the last substream being closed then disable the i2s ++ * clock divider. ++ */ ++ if (snd_soc_dai_active(dai) <= 1) ++ aiu_encoder_i2s_divider_enable(component, 0); ++ ++ if (ts->clk_enabled) { ++ clk_disable_unprepare(ts->iface->mclk); ++ ts->clk_enabled = false; ++ } ++ ++ ts->channels = 0; ++ ts->width = 0; ++ ts->physical_width = 0; + + return 0; + } +@@ -224,6 +243,8 @@ static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream, + static int aiu_encoder_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) + { + struct snd_soc_component *component = dai->component; ++ struct aiu *aiu = snd_soc_component_get_drvdata(component); ++ struct gx_iface *iface = &aiu->i2s.iface; + unsigned int inv = fmt & SND_SOC_DAIFMT_INV_MASK; + unsigned int val = 0; + unsigned int skew; +@@ -255,9 +276,12 @@ static int aiu_encoder_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) + skew = 0; + break; + default: ++ dev_err(dai->dev, "unsupported dai format\n"); + return -EINVAL; + } + ++ iface->fmt = fmt; ++ + val |= FIELD_PREP(AIU_CLK_CTRL_LRCLK_SKEW, skew); + snd_soc_component_update_bits(component, AIU_CLK_CTRL, + AIU_CLK_CTRL_LRCLK_INVERT | +@@ -272,6 +296,7 @@ static int aiu_encoder_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) + { + struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); ++ struct gx_iface *iface = &aiu->i2s.iface; + int ret; + + if (WARN_ON(clk_id != 0)) +@@ -280,11 +305,15 @@ static int aiu_encoder_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, + if (dir == SND_SOC_CLOCK_IN) + return 0; + +- ret = clk_set_rate(aiu->i2s.clks[MCLK].clk, freq); +- if (ret) +- dev_err(dai->dev, "Failed to set sysclk to %uHz", freq); ++ ret = clk_set_rate(iface->mclk, freq); ++ if (ret) { ++ dev_err(dai->dev, "Failed to set sysclk to %uHz: %d", freq, ret); ++ return ret; ++ } + +- return ret; ++ iface->mclk_rate = freq; ++ ++ return 0; + } + + static const unsigned int hw_channels[] = {2, 8}; +@@ -294,10 +323,45 @@ static const struct snd_pcm_hw_constraint_list hw_channel_constraints = { + .mask = 0, + }; + ++static int aiu_encoder_i2s_pcm_hw_rule(struct snd_pcm_hw_params *params, ++ struct snd_pcm_hw_rule *rule) ++{ ++ struct gx_stream *other = rule->private; ++ struct snd_interval *ch = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); ++ /* ++ * The quirk is technically based on the significant bits whereas here ++ * we're using the physical width for simplicity. This works because ++ * S16_LE is the only format supported by this encoder that has: ++ * significant bits = physical width = 16-bits ++ */ ++ struct snd_interval *phys_width = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); ++ struct snd_interval new_i; ++ ++ if (other->channels == 0) ++ return 0; ++ ++ snd_interval_any(&new_i); ++ ++ if (rule->var == SNDRV_PCM_HW_PARAM_CHANNELS) { ++ if (aiu_encoder_is_bs_quirk(other->channels, other->width)) ++ new_i.min = new_i.max = 8; ++ else if (snd_interval_single(phys_width) && phys_width->min == 16) ++ new_i.max = 2; /* Force 2ch */ ++ } else { /* SNDRV_PCM_HW_PARAM_SAMPLE_BITS */ ++ if (aiu_encoder_is_bs_quirk(other->channels, other->width)) ++ new_i.min = new_i.max = 16; ++ else if (snd_interval_single(ch) && ch->min == 8) ++ new_i.min = 17; /* Request physical width > 16 bits */ ++ } ++ ++ return snd_interval_refine(hw_param_interval(params, rule->var), &new_i); ++} ++ + static int aiu_encoder_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + { + struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); ++ struct gx_stream *other_stream = snd_soc_dai_dma_data_get(dai, !substream->stream); + int ret; + + /* Make sure the encoder gets either 2 or 8 channels */ +@@ -305,15 +369,79 @@ static int aiu_encoder_i2s_startup(struct snd_pcm_substream *substream, + SNDRV_PCM_HW_PARAM_CHANNELS, + &hw_channel_constraints); + if (ret) { +- dev_err(dai->dev, "adding channels constraints failed\n"); ++ dev_err(dai->dev, "adding channels constraints failed: %d\n", ret); + return ret; + } + +- ret = clk_bulk_prepare_enable(aiu->i2s.clk_num, aiu->i2s.clks); +- if (ret) +- dev_err(dai->dev, "failed to enable i2s clocks\n"); ++ /* ++ * If DAI supports both playback and capture streams ensure the bs-quirk is ++ * handled correctly. ++ * This is only valid for GX platforms (has_clk_ctrl_more_i2s_div=true). ++ */ ++ if (aiu->platform->has_clk_ctrl_more_i2s_div && other_stream) { ++ ret = snd_pcm_hw_rule_add(substream->runtime, 0, ++ SNDRV_PCM_HW_PARAM_CHANNELS, ++ aiu_encoder_i2s_pcm_hw_rule, ++ other_stream, ++ SNDRV_PCM_HW_PARAM_CHANNELS, ++ SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); ++ if (ret) ++ return ret; + +- return ret; ++ ret = snd_pcm_hw_rule_add(substream->runtime, 0, ++ SNDRV_PCM_HW_PARAM_SAMPLE_BITS, ++ aiu_encoder_i2s_pcm_hw_rule, ++ other_stream, ++ SNDRV_PCM_HW_PARAM_CHANNELS, ++ SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); ++ if (ret) ++ return ret; ++ } ++ ++ /* ++ * Enable only clocks which are required for the interface internal ++ * logic. MCLK is enabled/disabled from the formatter and the I2S ++ * divider is enabled/disabled in "hw_params"/"hw_free", respectively. ++ */ ++ ret = clk_prepare_enable(aiu->i2s.clks[PCLK].clk); ++ if (ret) { ++ dev_err(dai->dev, "failed to enable PCLK: %d\n", ret); ++ return ret; ++ } ++ ret = clk_prepare_enable(aiu->i2s.clks[MIXER].clk); ++ if (ret) { ++ dev_err(dai->dev, "failed to enable MIXER: %d\n", ret); ++ clk_disable_unprepare(aiu->i2s.clks[PCLK].clk); ++ return ret; ++ } ++ ret = clk_prepare_enable(aiu->i2s.clks[AOCLK].clk); ++ if (ret) { ++ dev_err(dai->dev, "failed to enable AOCLK: %d\n", ret); ++ clk_disable_unprepare(aiu->i2s.clks[MIXER].clk); ++ clk_disable_unprepare(aiu->i2s.clks[PCLK].clk); ++ return ret; ++ } ++ ++ /* ++ * We're always operating in split mode for the playback stream. ++ * ++ * This setting arguably belong to the 'aiu-formatter', but it's kept ++ * here for backward compatibility reason. At reset the I2S encoder ++ * operates in normal mode which would only support 8ch, but by default ++ * only 2ch are enabled. If a playback stream is started without ++ * changing to split mode, then the I2S encoder doesn't consume audio ++ * samples and the playback fails. ++ * Moving this to 'aiu-formatter' would cause the split mode to be set ++ * only when the formatter is enabled, which doesn't happen at boot as ++ * the default value for "HDMI CTRL SRC" is "DISABLED". ++ */ ++ ret = snd_soc_component_update_bits(dai->component, AIU_I2S_SOURCE_DESC, ++ AIU_I2S_SOURCE_DESC_MODE_SPLIT, ++ AIU_I2S_SOURCE_DESC_MODE_SPLIT); ++ if (ret < 0) ++ dev_err(dai->dev, "failed to update AIU_I2S_SOURCE_DESC: %d", ret); ++ ++ return 0; + } + + static void aiu_encoder_i2s_shutdown(struct snd_pcm_substream *substream, +@@ -321,14 +449,89 @@ static void aiu_encoder_i2s_shutdown(struct snd_pcm_substream *substream, + { + struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); + +- clk_bulk_disable_unprepare(aiu->i2s.clk_num, aiu->i2s.clks); ++ clk_disable_unprepare(aiu->i2s.clks[AOCLK].clk); ++ clk_disable_unprepare(aiu->i2s.clks[MIXER].clk); ++ clk_disable_unprepare(aiu->i2s.clks[PCLK].clk); ++} ++ ++static int aiu_encoder_i2s_trigger(struct snd_pcm_substream *substream, ++ int cmd, ++ struct snd_soc_dai *dai) ++{ ++ struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); ++ int ret; ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ ret = gx_stream_start(ts); ++ break; ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ case SNDRV_PCM_TRIGGER_STOP: ++ gx_stream_stop(ts); ++ ret = 0; ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static int aiu_encoder_i2s_remove_dai(struct snd_soc_dai *dai) ++{ ++ int stream; ++ ++ for_each_pcm_streams(stream) { ++ struct gx_stream *ts; ++ ++ ts = snd_soc_dai_dma_data_get(dai, stream); ++ if (ts) ++ gx_stream_free(ts); ++ ++ snd_soc_dai_dma_data_set(dai, stream, NULL); ++ } ++ ++ return 0; ++} ++ ++static int aiu_encoder_i2s_probe_dai(struct snd_soc_dai *dai) ++{ ++ struct aiu *aiu = snd_soc_dai_get_drvdata(dai); ++ struct gx_iface *iface = &aiu->i2s.iface; ++ int stream; ++ ++ for_each_pcm_streams(stream) { ++ struct gx_stream *ts; ++ ++ if (!snd_soc_dai_get_widget(dai, stream)) ++ continue; ++ ++ ts = gx_stream_alloc(iface); ++ if (!ts) { ++ aiu_encoder_i2s_remove_dai(dai); ++ return -ENOMEM; ++ } ++ snd_soc_dai_dma_data_set(dai, stream, ts); ++ } ++ ++ iface->mclk = aiu->i2s.clks[MCLK].clk; ++ iface->mclk_rate = clk_get_rate(iface->mclk); ++ ++ return 0; + } + + const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops = { ++ .probe = aiu_encoder_i2s_probe_dai, ++ .remove = aiu_encoder_i2s_remove_dai, + .hw_params = aiu_encoder_i2s_hw_params, ++ .prepare = aiu_encoder_i2s_prepare, + .hw_free = aiu_encoder_i2s_hw_free, + .set_fmt = aiu_encoder_i2s_set_fmt, + .set_sysclk = aiu_encoder_i2s_set_sysclk, + .startup = aiu_encoder_i2s_startup, + .shutdown = aiu_encoder_i2s_shutdown, ++ .trigger = aiu_encoder_i2s_trigger, + }; +diff --git a/sound/soc/meson/aiu-formatter-i2s.c b/sound/soc/meson/aiu-formatter-i2s.c +new file mode 100644 +--- /dev/null ++++ b/sound/soc/meson/aiu-formatter-i2s.c +@@ -0,0 +1,103 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// ++// Copyright (c) 2026 BayLibre, SAS. ++// Author: Valerio Setti ++ ++#include ++#include ++#include ++ ++#include "aiu.h" ++#include "gx-formatter.h" ++ ++#define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0) ++#define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5) ++#define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9) ++ ++#define AIU_I2S_DAC_CFG_MSB_FIRST BIT(2) ++ ++static struct snd_soc_dai * ++aiu_formatter_i2s_get_be(struct snd_soc_dapm_widget *w) ++{ ++ struct snd_soc_dapm_path *p; ++ struct snd_soc_dai *be; ++ ++ snd_soc_dapm_widget_for_each_sink_path(w, p) { ++ if (!p->connect) ++ continue; ++ ++ if (p->sink->id == snd_soc_dapm_dai_in) ++ return (struct snd_soc_dai *)p->sink->priv; ++ ++ be = aiu_formatter_i2s_get_be(p->sink); ++ if (be) ++ return be; ++ } ++ ++ return NULL; ++} ++ ++static struct gx_stream * ++aiu_formatter_i2s_get_stream(struct snd_soc_dapm_widget *w) ++{ ++ struct snd_soc_dai *be = aiu_formatter_i2s_get_be(w); ++ ++ if (!be) ++ return NULL; ++ ++ return snd_soc_dai_dma_data_get_playback(be); ++} ++ ++static int aiu_formatter_i2s_prepare(struct regmap *map, ++ const struct gx_formatter_hw *quirks, ++ struct gx_stream *ts) ++{ ++ /* Always operate in split (classic interleaved) mode */ ++ unsigned int desc = 0; ++ ++ /* ++ * Pipeline reset is already implemented in aiu_fifo_i2s_trigger() at ++ * trigger time. ++ */ ++ ++ switch (ts->physical_width) { ++ case 16: /* Nothing to do */ ++ break; ++ ++ case 32: ++ desc |= (AIU_I2S_SOURCE_DESC_MODE_24BIT | ++ AIU_I2S_SOURCE_DESC_MODE_32BIT); ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ switch (ts->channels) { ++ case 2: /* Nothing to do */ ++ break; ++ case 8: ++ desc |= AIU_I2S_SOURCE_DESC_MODE_8CH; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ regmap_update_bits(map, AIU_I2S_SOURCE_DESC, ++ AIU_I2S_SOURCE_DESC_MODE_8CH | ++ AIU_I2S_SOURCE_DESC_MODE_24BIT | ++ AIU_I2S_SOURCE_DESC_MODE_32BIT, ++ desc); ++ ++ /* Send data MSB first */ ++ regmap_update_bits(map, AIU_I2S_DAC_CFG, ++ AIU_I2S_DAC_CFG_MSB_FIRST, ++ AIU_I2S_DAC_CFG_MSB_FIRST); ++ ++ return 0; ++} ++ ++const struct gx_formatter_ops aiu_formatter_i2s_ops = { ++ .get_stream = aiu_formatter_i2s_get_stream, ++ .prepare = aiu_formatter_i2s_prepare, ++}; +diff --git a/sound/soc/meson/aiu.c b/sound/soc/meson/aiu.c +--- a/sound/soc/meson/aiu.c ++++ b/sound/soc/meson/aiu.c +@@ -29,13 +29,22 @@ static SOC_ENUM_SINGLE_DECL(aiu_spdif_encode_sel_enum, AIU_I2S_MISC, + static const struct snd_kcontrol_new aiu_spdif_encode_mux = + SOC_DAPM_ENUM("SPDIF Buffer Src", aiu_spdif_encode_sel_enum); + +-static const struct snd_soc_dapm_widget aiu_cpu_dapm_widgets[] = { +- SND_SOC_DAPM_MUX("SPDIF SRC SEL", SND_SOC_NOPM, 0, 0, +- &aiu_spdif_encode_mux), ++#define AIU_WIDGET_SPDIF_SRC_SEL 0 ++#define AIU_WIDGET_I2S_FORMATTER 1 ++ ++static struct snd_soc_dapm_widget aiu_cpu_dapm_widgets[] = { ++ [AIU_WIDGET_SPDIF_SRC_SEL] = ++ SND_SOC_DAPM_MUX("SPDIF SRC SEL", SND_SOC_NOPM, 0, 0, ++ &aiu_spdif_encode_mux), ++ [AIU_WIDGET_I2S_FORMATTER] = ++ SND_SOC_DAPM_PGA_E("I2S Formatter", SND_SOC_NOPM, 0, 0, NULL, 0, ++ gx_formatter_event, ++ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD)), + }; + + static const struct snd_soc_dapm_route aiu_cpu_dapm_routes[] = { +- { "I2S Encoder Playback", NULL, "I2S FIFO Playback" }, ++ { "I2S Formatter", NULL, "I2S FIFO Playback" }, ++ { "I2S Encoder Playback", NULL, "I2S Formatter" }, + { "SPDIF SRC SEL", "SPDIF", "SPDIF FIFO Playback" }, + { "SPDIF SRC SEL", "I2S", "I2S FIFO Playback" }, + { "SPDIF Encoder Playback", NULL, "SPDIF SRC SEL" }, +@@ -145,6 +154,7 @@ static struct snd_soc_dai_driver aiu_cpu_dai_drv[] = { + .formats = AIU_FORMATS, + }, + .ops = &aiu_encoder_i2s_dai_ops, ++ .symmetric_rate = 1, + }, + [CPU_SPDIF_ENCODER] = { + .name = "SPDIF Encoder", +@@ -172,6 +182,11 @@ static const struct regmap_config aiu_regmap_cfg = { + .max_register = 0x2ac, + }; + ++static const struct gx_formatter_driver aiu_formatter_i2s_drv = { ++ .regmap_cfg = &aiu_regmap_cfg, ++ .ops = &aiu_formatter_i2s_ops, ++}; ++ + static int aiu_clk_bulk_get(struct device *dev, + const char * const *ids, + unsigned int num, +@@ -282,6 +297,14 @@ static int aiu_probe(struct platform_device *pdev) + if (ret) + return ret; + ++ /* Allocate the aiu-formatter into its widget */ ++ ret = gx_formatter_create(dev, &aiu_cpu_dapm_widgets[AIU_WIDGET_I2S_FORMATTER], ++ &aiu_formatter_i2s_drv, map); ++ if (ret) { ++ dev_err(dev, "Failed to allocate aiu formatter\n"); ++ goto err; ++ } ++ + /* Register the cpu component of the aiu */ + ret = snd_soc_register_component(dev, &aiu_cpu_component, + aiu_cpu_dai_drv, +@@ -310,12 +333,14 @@ static int aiu_probe(struct platform_device *pdev) + + return 0; + err: ++ gx_formatter_free(&aiu_cpu_dapm_widgets[AIU_WIDGET_I2S_FORMATTER]); + snd_soc_unregister_component(dev); + return ret; + } + + static void aiu_remove(struct platform_device *pdev) + { ++ gx_formatter_free(&aiu_cpu_dapm_widgets[AIU_WIDGET_I2S_FORMATTER]); + snd_soc_unregister_component(&pdev->dev); + } + +diff --git a/sound/soc/meson/aiu.h b/sound/soc/meson/aiu.h +--- a/sound/soc/meson/aiu.h ++++ b/sound/soc/meson/aiu.h +@@ -7,6 +7,8 @@ + #ifndef _MESON_AIU_H + #define _MESON_AIU_H + ++#include "gx-formatter.h" ++ + struct clk; + struct clk_bulk_data; + struct device; +@@ -25,6 +27,7 @@ struct aiu_interface { + struct clk_bulk_data *clks; + unsigned int clk_num; + int irq; ++ struct gx_iface iface; + }; + + struct aiu_platform_data { +@@ -58,6 +61,7 @@ extern const struct snd_soc_dai_ops aiu_fifo_i2s_dai_ops; + extern const struct snd_soc_dai_ops aiu_fifo_spdif_dai_ops; + extern const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops; + extern const struct snd_soc_dai_ops aiu_encoder_spdif_dai_ops; ++extern const struct gx_formatter_ops aiu_formatter_i2s_ops; + + #define AIU_IEC958_BPF 0x000 + #define AIU_958_MISC 0x010 +diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c +--- a/sound/soc/meson/axg-card.c ++++ b/sound/soc/meson/axg-card.c +@@ -107,6 +107,7 @@ static int axg_card_add_tdm_loopback(struct snd_soc_card *card, + struct snd_soc_dai_link *pad; + struct snd_soc_dai_link *lb; + struct snd_soc_dai_link_component *dlc; ++ struct device *dev = card->dev; + int ret; + + /* extend links */ +@@ -117,11 +118,11 @@ static int axg_card_add_tdm_loopback(struct snd_soc_card *card, + pad = &card->dai_link[*index]; + lb = &card->dai_link[*index + 1]; + +- lb->name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-lb", pad->name); ++ lb->name = devm_kasprintf(dev, GFP_KERNEL, "%s-lb", pad->name); + if (!lb->name) + return -ENOMEM; + +- dlc = devm_kzalloc(card->dev, sizeof(*dlc), GFP_KERNEL); ++ dlc = devm_kzalloc(dev, sizeof(*dlc), GFP_KERNEL); + if (!dlc) + return -ENOMEM; + +@@ -158,13 +159,14 @@ static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card, + struct device_node *node, + struct axg_dai_link_tdm_data *be) + { ++ struct device *dev = card->dev; + char propname[32]; + u32 tx, rx; + int i; + +- be->tx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES, ++ be->tx_mask = devm_kcalloc(dev, AXG_TDM_NUM_LANES, + sizeof(*be->tx_mask), GFP_KERNEL); +- be->rx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES, ++ be->rx_mask = devm_kcalloc(dev, AXG_TDM_NUM_LANES, + sizeof(*be->rx_mask), GFP_KERNEL); + if (!be->tx_mask || !be->rx_mask) + return -ENOMEM; +@@ -191,7 +193,7 @@ static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card, + + /* ... but the interface should at least have one direction */ + if (!tx && !rx) { +- dev_err(card->dev, "tdm link has no cpu slots\n"); ++ dev_err(dev, "tdm link has no cpu slots\n"); + return -EINVAL; + } + +@@ -207,7 +209,7 @@ static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card, + * Error if the slots can't accommodate the largest mask or + * if it is just too big + */ +- dev_err(card->dev, "bad slot number\n"); ++ dev_err(dev, "bad slot number\n"); + return -EINVAL; + } + +@@ -222,8 +224,9 @@ static int axg_card_parse_codecs_masks(struct snd_soc_card *card, + struct axg_dai_link_tdm_data *be) + { + struct axg_dai_link_tdm_mask *codec_mask; ++ struct device *dev = card->dev; + +- codec_mask = devm_kcalloc(card->dev, link->num_codecs, ++ codec_mask = devm_kcalloc(dev, link->num_codecs, + sizeof(*codec_mask), GFP_KERNEL); + if (!codec_mask) + return -ENOMEM; +@@ -249,10 +252,11 @@ static int axg_card_parse_tdm(struct snd_soc_card *card, + struct meson_card *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_dai_link *link = &card->dai_link[*index]; + struct axg_dai_link_tdm_data *be; ++ struct device *dev = card->dev; + int ret; + + /* Allocate tdm link parameters */ +- be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL); ++ be = devm_kzalloc(dev, sizeof(*be), GFP_KERNEL); + if (!be) + return -ENOMEM; + priv->link_data[*index] = be; +@@ -266,7 +270,7 @@ static int axg_card_parse_tdm(struct snd_soc_card *card, + + ret = axg_card_parse_cpu_tdm_slots(card, link, node, be); + if (ret) { +- dev_err(card->dev, "error parsing tdm link slots\n"); ++ dev_err(dev, "error parsing tdm link slots\n"); + return ret; + } + +@@ -310,9 +314,10 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np, + { + struct snd_soc_dai_link *dai_link = &card->dai_link[*index]; + struct snd_soc_dai_link_component *cpu; ++ struct device *dev = card->dev; + int ret; + +- cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL); ++ cpu = devm_kzalloc(dev, sizeof(*cpu), GFP_KERNEL); + if (!cpu) + return -ENOMEM; + +diff --git a/sound/soc/meson/g12a-toacodec.c b/sound/soc/meson/g12a-toacodec.c +--- a/sound/soc/meson/g12a-toacodec.c ++++ b/sound/soc/meson/g12a-toacodec.c +@@ -312,7 +312,7 @@ static int g12a_toacodec_probe(struct platform_device *pdev) + + ret = device_reset(dev); + if (ret) +- return ret; ++ return dev_err_probe(dev, ret, "failed to reset device\n"); + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) +diff --git a/sound/soc/meson/g12a-tohdmitx.c b/sound/soc/meson/g12a-tohdmitx.c +--- a/sound/soc/meson/g12a-tohdmitx.c ++++ b/sound/soc/meson/g12a-tohdmitx.c +@@ -251,7 +251,7 @@ static int g12a_tohdmitx_probe(struct platform_device *pdev) + + ret = device_reset(dev); + if (ret) +- return ret; ++ return dev_err_probe(dev, ret, "failed to reset device\n"); + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) +diff --git a/sound/soc/meson/gx-card.c b/sound/soc/meson/gx-card.c +--- a/sound/soc/meson/gx-card.c ++++ b/sound/soc/meson/gx-card.c +@@ -48,9 +48,10 @@ static int gx_card_parse_i2s(struct snd_soc_card *card, + struct meson_card *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_dai_link *link = &card->dai_link[*index]; + struct gx_dai_link_i2s_data *be; ++ struct device *dev = card->dev; + + /* Allocate i2s link parameters */ +- be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL); ++ be = devm_kzalloc(dev, sizeof(*be), GFP_KERNEL); + if (!be) + return -ENOMEM; + priv->link_data[*index] = be; +@@ -81,9 +82,10 @@ static int gx_card_add_link(struct snd_soc_card *card, struct device_node *np, + { + struct snd_soc_dai_link *dai_link = &card->dai_link[*index]; + struct snd_soc_dai_link_component *cpu; ++ struct device *dev = card->dev; + int ret; + +- cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL); ++ cpu = devm_kzalloc(dev, sizeof(*cpu), GFP_KERNEL); + if (!cpu) + return -ENOMEM; + +diff --git a/sound/soc/meson/gx-formatter.c b/sound/soc/meson/gx-formatter.c +new file mode 100644 +--- /dev/null ++++ b/sound/soc/meson/gx-formatter.c +@@ -0,0 +1,282 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++// ++// Copyright (c) 2026 BayLibre, SAS. ++// Author: Valerio Setti ++ ++#include ++#include ++#include ++#include ++ ++#include "gx-formatter.h" ++ ++struct gx_formatter { ++ struct list_head list; ++ struct gx_stream *stream; ++ const struct gx_formatter_driver *drv; ++ bool enabled; ++ struct regmap *map; ++}; ++ ++static int gx_formatter_enable(struct gx_formatter *formatter) ++{ ++ int ret; ++ ++ /* Do nothing if the formatter is already enabled */ ++ if (formatter->enabled) ++ return 0; ++ ++ /* Setup the stream parameter in the formatter */ ++ if (formatter->drv->ops->prepare) { ++ ret = formatter->drv->ops->prepare(formatter->map, ++ formatter->drv->quirks, ++ formatter->stream); ++ if (ret) ++ return ret; ++ } ++ ++ /* Finally, actually enable the formatter */ ++ if (formatter->drv->ops->enable) ++ formatter->drv->ops->enable(formatter->map); ++ ++ formatter->enabled = true; ++ ++ return 0; ++} ++ ++static void gx_formatter_disable(struct gx_formatter *formatter) ++{ ++ /* Do nothing if the formatter is already disabled */ ++ if (!formatter->enabled) ++ return; ++ ++ if (formatter->drv->ops->disable) ++ formatter->drv->ops->disable(formatter->map); ++ ++ formatter->enabled = false; ++} ++ ++static int gx_formatter_attach(struct gx_formatter *formatter) ++{ ++ struct gx_stream *ts = formatter->stream; ++ int ret = 0; ++ ++ mutex_lock(&ts->lock); ++ ++ /* Catch up if the stream is already running when we attach */ ++ if (ts->ready) { ++ ret = gx_formatter_enable(formatter); ++ if (ret) { ++ pr_err("failed to enable formatter\n"); ++ goto out; ++ } ++ } ++ ++ list_add_tail(&formatter->list, &ts->formatter_list); ++out: ++ mutex_unlock(&ts->lock); ++ return ret; ++} ++ ++static void gx_formatter_detach(struct gx_formatter *formatter) ++{ ++ struct gx_stream *ts = formatter->stream; ++ ++ if (!ts) ++ return; ++ ++ mutex_lock(&ts->lock); ++ list_del(&formatter->list); ++ mutex_unlock(&ts->lock); ++ ++ gx_formatter_disable(formatter); ++} ++ ++static int gx_formatter_power_up(struct gx_formatter *formatter, ++ struct snd_soc_dapm_widget *w) ++{ ++ struct gx_stream *ts = formatter->drv->ops->get_stream(w); ++ int ret; ++ ++ /* ++ * If we don't get a stream at this stage, it would mean that the ++ * widget is powering up but is not attached to any backend DAI. ++ * It should not happen, ever ! ++ */ ++ if (WARN_ON(!ts)) ++ return -ENODEV; ++ ++ formatter->stream = ts; ++ INIT_LIST_HEAD(&formatter->list); ++ ret = gx_formatter_attach(formatter); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static void gx_formatter_power_down(struct gx_formatter *formatter) ++{ ++ gx_formatter_detach(formatter); ++ formatter->stream = NULL; ++} ++ ++int gx_formatter_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *control, ++ int event) ++{ ++ struct snd_soc_component *c; ++ struct gx_formatter *formatter; ++ int ret = 0; ++ ++ c = snd_soc_dapm_to_component(w->dapm); ++ ++ if (w->priv) ++ formatter = w->priv; ++ else ++ formatter = snd_soc_component_get_drvdata(c); ++ ++ switch (event) { ++ case SND_SOC_DAPM_PRE_PMU: ++ ret = gx_formatter_power_up(formatter, w); ++ break; ++ ++ case SND_SOC_DAPM_PRE_PMD: ++ gx_formatter_power_down(formatter); ++ break; ++ ++ default: ++ dev_err(c->dev, "Unexpected event %d\n", event); ++ return -EINVAL; ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(gx_formatter_event); ++ ++int gx_formatter_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ const struct gx_formatter_driver *drv; ++ struct gx_formatter *formatter; ++ void __iomem *regs; ++ ++ drv = of_device_get_match_data(dev); ++ if (!drv) { ++ dev_err(dev, "failed to match device\n"); ++ return -ENODEV; ++ } ++ ++ formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL); ++ if (!formatter) ++ return -ENOMEM; ++ platform_set_drvdata(pdev, formatter); ++ formatter->drv = drv; ++ ++ regs = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(regs)) ++ return PTR_ERR(regs); ++ ++ formatter->map = devm_regmap_init_mmio(dev, regs, drv->regmap_cfg); ++ if (IS_ERR(formatter->map)) { ++ dev_err(dev, "failed to init regmap: %ld\n", ++ PTR_ERR(formatter->map)); ++ return PTR_ERR(formatter->map); ++ } ++ ++ return devm_snd_soc_register_component(dev, drv->component_drv, ++ NULL, 0); ++} ++EXPORT_SYMBOL_GPL(gx_formatter_probe); ++ ++int gx_formatter_create(struct device *dev, ++ struct snd_soc_dapm_widget *w, ++ const struct gx_formatter_driver *drv, ++ struct regmap *regmap) ++{ ++ struct gx_formatter *formatter; ++ ++ formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL); ++ if (!formatter) ++ return -ENOMEM; ++ ++ formatter->drv = drv; ++ formatter->map = regmap; ++ ++ w->priv = formatter; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(gx_formatter_create); ++ ++int gx_stream_start(struct gx_stream *ts) ++{ ++ struct gx_formatter *formatter; ++ int ret = 0; ++ ++ mutex_lock(&ts->lock); ++ ++ /* Start all the formatters attached to the stream */ ++ list_for_each_entry(formatter, &ts->formatter_list, list) { ++ ret = gx_formatter_enable(formatter); ++ if (ret) { ++ pr_err("failed to enable formatter\n"); ++ goto out; ++ } ++ } ++ ++ ts->ready = true; ++ ++out: ++ mutex_unlock(&ts->lock); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(gx_stream_start); ++ ++void gx_stream_stop(struct gx_stream *ts) ++{ ++ struct gx_formatter *formatter; ++ ++ mutex_lock(&ts->lock); ++ ts->ready = false; ++ ++ /* Stop all the formatters attached to the stream */ ++ list_for_each_entry(formatter, &ts->formatter_list, list) { ++ gx_formatter_disable(formatter); ++ } ++ ++ mutex_unlock(&ts->lock); ++} ++EXPORT_SYMBOL_GPL(gx_stream_stop); ++ ++struct gx_stream *gx_stream_alloc(struct gx_iface *iface) ++{ ++ struct gx_stream *ts; ++ ++ ts = kzalloc(sizeof(*ts), GFP_KERNEL); ++ if (ts) { ++ INIT_LIST_HEAD(&ts->formatter_list); ++ mutex_init(&ts->lock); ++ ts->iface = iface; ++ } ++ ++ return ts; ++} ++EXPORT_SYMBOL_GPL(gx_stream_alloc); ++ ++void gx_stream_free(struct gx_stream *ts) ++{ ++ /* ++ * If the list is not empty, it would mean that one of the formatter ++ * widget is still powered and attached to the interface while we ++ * are removing the TDM DAI. It should not be possible ++ */ ++ WARN_ON(!list_empty(&ts->formatter_list)); ++ mutex_destroy(&ts->lock); ++ kfree(ts); ++} ++EXPORT_SYMBOL_GPL(gx_stream_free); ++ ++MODULE_DESCRIPTION("Amlogic GX formatter driver"); ++MODULE_AUTHOR("Valerio Setti "); ++MODULE_LICENSE("GPL"); +diff --git a/sound/soc/meson/gx-formatter.h b/sound/soc/meson/gx-formatter.h +new file mode 100644 +--- /dev/null ++++ b/sound/soc/meson/gx-formatter.h +@@ -0,0 +1,56 @@ ++/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ ++/* ++ * Copyright (c) 2026 Baylibre SAS. ++ * Author: Valerio Setti ++ */ ++ ++#ifndef _MESON_GX_FORMATTER_H ++#define _MESON_GX_FORMATTER_H ++ ++#include "gx-interface.h" ++ ++struct platform_device; ++struct regmap; ++struct snd_soc_dapm_widget; ++struct snd_kcontrol; ++ ++struct gx_formatter_hw { ++ unsigned int skew_offset; ++}; ++ ++struct gx_formatter_ops { ++ struct gx_stream *(*get_stream)(struct snd_soc_dapm_widget *w); ++ void (*enable)(struct regmap *map); ++ void (*disable)(struct regmap *map); ++ int (*prepare)(struct regmap *map, ++ const struct gx_formatter_hw *quirks, ++ struct gx_stream *ts); ++}; ++ ++struct gx_formatter_driver { ++ const struct snd_soc_component_driver *component_drv; ++ const struct regmap_config *regmap_cfg; ++ const struct gx_formatter_ops *ops; ++ const struct gx_formatter_hw *quirks; ++}; ++ ++int gx_formatter_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *control, ++ int event); ++int gx_formatter_probe(struct platform_device *pdev); ++ ++int gx_formatter_create(struct device *dev, ++ struct snd_soc_dapm_widget *w, ++ const struct gx_formatter_driver *drv, ++ struct regmap *regmap); ++ ++/* ++ * Formatter data is already freed when the associated device is removed, ++ * so we just need to remove the pointer from the widget. ++ */ ++static inline void gx_formatter_free(struct snd_soc_dapm_widget *w) ++{ ++ w->priv = NULL; ++} ++ ++#endif /* _MESON_GX_FORMATTER_H */ +diff --git a/sound/soc/meson/gx-interface.h b/sound/soc/meson/gx-interface.h +new file mode 100644 +--- /dev/null ++++ b/sound/soc/meson/gx-interface.h +@@ -0,0 +1,42 @@ ++/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ ++/* ++ * Copyright (c) 2026 Baylibre SAS. ++ * Author: Valerio Setti ++ */ ++ ++#ifndef _MESON_GX_INTERFACE_H ++#define _MESON_GX_INTERFACE_H ++ ++#include ++#include ++#include ++#include ++#include ++ ++struct gx_iface { ++ struct clk *mclk; ++ unsigned long mclk_rate; ++ ++ /* format is common to all the DAIs of the iface */ ++ unsigned int fmt; ++}; ++ ++struct gx_stream { ++ struct gx_iface *iface; ++ struct list_head formatter_list; ++ struct mutex lock; ++ unsigned int channels; ++ unsigned int width; ++ unsigned int physical_width; ++ bool ready; ++ ++ /* For continuous clock tracking */ ++ bool clk_enabled; ++}; ++ ++struct gx_stream *gx_stream_alloc(struct gx_iface *iface); ++void gx_stream_free(struct gx_stream *ts); ++int gx_stream_start(struct gx_stream *ts); ++void gx_stream_stop(struct gx_stream *ts); ++ ++#endif /* _MESON_GX_INTERFACE_H */ +diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c +--- a/sound/soc/meson/meson-card-utils.c ++++ b/sound/soc/meson/meson-card-utils.c +@@ -71,6 +71,7 @@ int meson_card_parse_dai(struct snd_soc_card *card, + struct device_node *node, + struct snd_soc_dai_link_component *dlc) + { ++ struct device *dev = card->dev; + int ret; + + if (!dlc || !node) +@@ -78,7 +79,7 @@ int meson_card_parse_dai(struct snd_soc_card *card, + + ret = snd_soc_of_get_dlc(node, NULL, dlc, 0); + if (ret) +- return dev_err_probe(card->dev, ret, "can't parse dai\n"); ++ return dev_err_probe(dev, ret, "can't parse dai\n"); + + return ret; + } +@@ -89,7 +90,8 @@ static int meson_card_set_link_name(struct snd_soc_card *card, + struct device_node *node, + const char *prefix) + { +- char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s", ++ struct device *dev = card->dev; ++ char *name = devm_kasprintf(dev, GFP_KERNEL, "%s.%s", + prefix, node->full_name); + if (!name) + return -ENOMEM; +@@ -132,16 +134,17 @@ int meson_card_set_be_link(struct snd_soc_card *card, + struct device_node *node) + { + struct snd_soc_dai_link_component *codec; ++ struct device *dev = card->dev; + int ret, num_codecs; + + num_codecs = of_get_child_count(node); + if (!num_codecs) { +- dev_err(card->dev, "be link %s has no codec\n", ++ dev_err(dev, "be link %s has no codec\n", + node->full_name); + return -EINVAL; + } + +- codec = devm_kcalloc(card->dev, num_codecs, sizeof(*codec), GFP_KERNEL); ++ codec = devm_kcalloc(dev, num_codecs, sizeof(*codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + +@@ -158,7 +161,7 @@ int meson_card_set_be_link(struct snd_soc_card *card, + + ret = meson_card_set_link_name(card, link, node, "be"); + if (ret) +- dev_err(card->dev, "error setting %pOFn link name\n", node); ++ dev_err(dev, "error setting %pOFn link name\n", node); + + return ret; + } +@@ -189,12 +192,13 @@ EXPORT_SYMBOL_GPL(meson_card_set_fe_link); + static int meson_card_add_links(struct snd_soc_card *card) + { + struct meson_card *priv = snd_soc_card_get_drvdata(card); +- struct device_node *node = card->dev->of_node; ++ struct device *dev = card->dev; ++ struct device_node *node = dev->of_node; + int num, i, ret; + + num = of_get_child_count(node); + if (!num) { +- dev_err(card->dev, "card has no links\n"); ++ dev_err(dev, "card has no links\n"); + return -EINVAL; + } + +@@ -219,8 +223,10 @@ static int meson_card_parse_of_optional(struct snd_soc_card *card, + int (*func)(struct snd_soc_card *c, + const char *p)) + { ++ struct device *dev = card->dev; ++ + /* If property is not provided, don't fail ... */ +- if (!of_property_present(card->dev->of_node, propname)) ++ if (!of_property_present(dev->of_node, propname)) + return 0; + + /* ... but do fail if it is provided and the parsing fails */ +diff --git a/sound/soc/meson/t9015.c b/sound/soc/meson/t9015.c +--- a/sound/soc/meson/t9015.c ++++ b/sound/soc/meson/t9015.c +@@ -78,8 +78,14 @@ static int t9015_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) + return 0; + } + ++static const u64 t9015_dai_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J; ++ + static const struct snd_soc_dai_ops t9015_dai_ops = { + .set_fmt = t9015_dai_set_fmt, ++ .auto_selectable_formats = &t9015_dai_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static struct snd_soc_dai_driver t9015_dai = { +@@ -265,10 +271,8 @@ static int t9015_probe(struct platform_device *pdev) + return dev_err_probe(dev, PTR_ERR(priv->avdd), "failed to AVDD\n"); + + ret = device_reset(dev); +- if (ret) { +- dev_err(dev, "reset failed\n"); +- return ret; +- } ++ if (ret) ++ return dev_err_probe(dev, ret, "failed to reset device\n"); + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) { +diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c +--- a/sound/soc/mxs/mxs-saif.c ++++ b/sound/soc/mxs/mxs-saif.c +@@ -826,12 +826,9 @@ static int mxs_saif_probe(struct platform_device *pdev) + mxs_saif[saif->id] = saif; + + saif->clk = devm_clk_get(&pdev->dev, NULL); +- if (IS_ERR(saif->clk)) { +- ret = PTR_ERR(saif->clk); +- dev_err(&pdev->dev, "Cannot get the clock: %d\n", +- ret); +- return ret; +- } ++ if (IS_ERR(saif->clk)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(saif->clk), ++ "Cannot get the clock\n"); + + saif->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(saif->base)) +@@ -844,10 +841,8 @@ static int mxs_saif_probe(struct platform_device *pdev) + saif->dev = &pdev->dev; + ret = devm_request_irq(&pdev->dev, irq, mxs_saif_irq, 0, + dev_name(&pdev->dev), saif); +- if (ret) { +- dev_err(&pdev->dev, "failed to request irq\n"); ++ if (ret) + return ret; +- } + + platform_set_drvdata(pdev, saif); + +@@ -860,16 +855,12 @@ static int mxs_saif_probe(struct platform_device *pdev) + + ret = devm_snd_soc_register_component(&pdev->dev, &mxs_saif_component, + &mxs_saif_dai, 1); +- if (ret) { +- dev_err(&pdev->dev, "register DAI failed\n"); ++ if (ret) + return ret; +- } + + ret = mxs_pcm_platform_register(&pdev->dev); +- if (ret) { +- dev_err(&pdev->dev, "register PCM failed: %d\n", ret); ++ if (ret) + return ret; +- } + + return 0; + } +diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c +--- a/sound/soc/mxs/mxs-sgtl5000.c ++++ b/sound/soc/mxs/mxs-sgtl5000.c +@@ -155,8 +155,6 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev) + + ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); + if (ret) { +- dev_err(&pdev->dev, "failed to parse audio-routing (%d)\n", +- ret); + mxs_saif_put_mclk(0); + return ret; + } +diff --git a/sound/soc/pxa/pxa2xx-ac97-lib.c b/sound/soc/pxa/pxa2xx-ac97-lib.c +--- a/sound/soc/pxa/pxa2xx-ac97-lib.c ++++ b/sound/soc/pxa/pxa2xx-ac97-lib.c +@@ -316,10 +316,8 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev) + int irq; + + ac97_reg_base = devm_platform_ioremap_resource(dev, 0); +- if (IS_ERR(ac97_reg_base)) { +- dev_err(&dev->dev, "Missing MMIO resource\n"); ++ if (IS_ERR(ac97_reg_base)) + return PTR_ERR(ac97_reg_base); +- } + + if (cpu_is_pxa27x()) { + /* Assert reset using GPIOD_OUT_HIGH, because reset is GPIO_ACTIVE_LOW */ +diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c +--- a/sound/soc/qcom/common.c ++++ b/sound/soc/qcom/common.c +@@ -23,6 +23,161 @@ static const struct snd_soc_dapm_widget qcom_jack_snd_widgets[] = { + SND_SOC_DAPM_SPK("DP7 Jack", NULL), + }; + ++static struct device_node *qcom_snd_get_link_node(struct snd_soc_pcm_runtime *rtd) ++{ ++ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); ++ struct snd_soc_card *card = rtd->card; ++ struct of_phandle_args args; ++ int ret; ++ ++ if (!card->dev || !card->dev->of_node) ++ return NULL; ++ ++ for_each_available_child_of_node_scoped(card->dev->of_node, np) { ++ struct device_node *cpu_np __free(device_node) = ++ of_get_child_by_name(np, "cpu"); ++ ++ if (!cpu_np) ++ continue; ++ ++ ret = of_parse_phandle_with_args(cpu_np, "sound-dai", "#sound-dai-cells", 0, ++ &args); ++ if (ret) ++ continue; ++ ++ if (args.np == rtd->dai_link->cpus[0].of_node && ++ args.args_count == 1 && args.args[0] == cpu_dai->id) { ++ of_node_put(args.np); ++ return of_node_get(np); ++ } ++ ++ of_node_put(args.np); ++ } ++ ++ return NULL; ++} ++ ++static int qcom_snd_parse_tdm_slot(struct device_node *np, ++ struct qcom_snd_tdm_slot_cfg *cfg) ++{ ++ memset(cfg, 0, sizeof(*cfg)); ++ ++ return snd_soc_of_parse_tdm_slot(np, &cfg->tx_mask, &cfg->rx_mask, ++ &cfg->slots, &cfg->slot_width); ++} ++ ++static int qcom_snd_normalize_tdm_slots(struct qcom_snd_tdm_slot_cfg *cpu_cfg, ++ struct qcom_snd_tdm_slot_cfg *codec_cfg) ++{ ++ unsigned int slots; ++ unsigned int slot_width; ++ ++ if (cpu_cfg->slots && codec_cfg->slots && cpu_cfg->slots != codec_cfg->slots) ++ return -EINVAL; ++ ++ if (cpu_cfg->slot_width && codec_cfg->slot_width && ++ cpu_cfg->slot_width != codec_cfg->slot_width) ++ return -EINVAL; ++ ++ slots = cpu_cfg->slots ?: codec_cfg->slots; ++ if (!slots) ++ return 0; ++ ++ slot_width = cpu_cfg->slot_width ?: codec_cfg->slot_width; ++ if (!slot_width) ++ return -EINVAL; ++ ++ cpu_cfg->slots = slots; ++ codec_cfg->slots = slots; ++ cpu_cfg->slot_width = slot_width; ++ codec_cfg->slot_width = slot_width; ++ ++ return 0; ++} ++ ++static int qcom_snd_parse_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, ++ struct qcom_snd_tdm_slot_cfg *cpu_cfg, ++ struct qcom_snd_tdm_slot_cfg *codec_cfg) ++{ ++ struct device_node *link_np __free(device_node) = qcom_snd_get_link_node(rtd); ++ int ret; ++ ++ if (!link_np) ++ return -EINVAL; ++ ++ struct device_node *cpu_np __free(device_node) = ++ of_get_child_by_name(link_np, "cpu"); ++ struct device_node *codec_np __free(device_node) = ++ of_get_child_by_name(link_np, "codec"); ++ if (!cpu_np || !codec_np) ++ return -EINVAL; ++ ++ ret = qcom_snd_parse_tdm_slot(cpu_np, cpu_cfg); ++ if (ret) ++ return ret; ++ ++ return qcom_snd_parse_tdm_slot(codec_np, codec_cfg); ++} ++ ++int qcom_snd_get_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, ++ struct qcom_snd_tdm_slot_cfg *cpu_cfg, ++ struct qcom_snd_tdm_slot_cfg *codec_cfg) ++{ ++ int ret; ++ ++ ret = qcom_snd_parse_dai_tdm_slots(rtd, cpu_cfg, codec_cfg); ++ if (ret) ++ return ret; ++ ++ return qcom_snd_normalize_tdm_slots(cpu_cfg, codec_cfg); ++} ++EXPORT_SYMBOL_GPL(qcom_snd_get_dai_tdm_slots); ++ ++int qcom_snd_apply_dai_tdm_slots_cfg(struct snd_soc_pcm_runtime *rtd, ++ const struct qcom_snd_tdm_slot_cfg *cpu_cfg, ++ const struct qcom_snd_tdm_slot_cfg *codec_cfg) ++{ ++ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); ++ struct snd_soc_dai *codec_dai; ++ int i; ++ int ret; ++ ++ if (!cpu_cfg->slots) ++ return 0; ++ ++ ret = snd_soc_dai_set_tdm_slot(cpu_dai, cpu_cfg->tx_mask, cpu_cfg->rx_mask, ++ cpu_cfg->slots, cpu_cfg->slot_width); ++ if (ret) ++ return ret; ++ ++ for_each_rtd_codec_dais(rtd, i, codec_dai) { ++ ret = snd_soc_dai_set_tdm_slot(codec_dai, ++ codec_cfg->tx_mask, ++ codec_cfg->rx_mask, ++ codec_cfg->slots, ++ codec_cfg->slot_width); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(qcom_snd_apply_dai_tdm_slots_cfg); ++ ++int qcom_snd_apply_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd) ++{ ++ struct qcom_snd_tdm_slot_cfg cpu_cfg; ++ struct qcom_snd_tdm_slot_cfg codec_cfg; ++ int ret; ++ ++ ret = qcom_snd_get_dai_tdm_slots(rtd, &cpu_cfg, &codec_cfg); ++ if (ret) ++ return ret == -EINVAL ? 0 : ret; ++ ++ return qcom_snd_apply_dai_tdm_slots_cfg(rtd, &cpu_cfg, &codec_cfg); ++} ++EXPORT_SYMBOL_GPL(qcom_snd_apply_dai_tdm_slots); ++ + int qcom_snd_parse_of(struct snd_soc_card *card) + { + struct device *dev = card->dev; +@@ -91,7 +246,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) + + ret = of_property_read_string(np, "link-name", &link->name); + if (ret) { +- dev_err(card->dev, "error getting codec dai_link name\n"); ++ dev_err(dev, "error getting codec dai_link name\n"); + return ret; + } + +@@ -109,7 +264,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) + + ret = snd_soc_of_get_dlc(cpu, &args, link->cpus, 0); + if (ret) { +- dev_err_probe(card->dev, ret, ++ dev_err_probe(dev, ret, + "%s: error getting cpu dai name\n", link->name); + return ret; + } +@@ -126,7 +281,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) + "sound-dai", + 0); + if (!link->platforms->of_node) { +- dev_err(card->dev, "%s: platform dai not found\n", link->name); ++ dev_err(dev, "%s: platform dai not found\n", link->name); + return -EINVAL; + } + } else { +@@ -136,7 +291,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) + if (codec) { + ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); + if (ret < 0) { +- dev_err_probe(card->dev, ret, ++ dev_err_probe(dev, ret, + "%s: codec dai not found\n", link->name); + return ret; + } +@@ -215,6 +370,7 @@ int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd, + } + + switch (cpu_dai->id) { ++ case LPI_MI2S_RX_0: + case TX_CODEC_DMA_TX_0: + case TX_CODEC_DMA_TX_1: + case TX_CODEC_DMA_TX_2: +diff --git a/sound/soc/qcom/common.h b/sound/soc/qcom/common.h +--- a/sound/soc/qcom/common.h ++++ b/sound/soc/qcom/common.h +@@ -7,9 +7,23 @@ + #include + #include + +-#define LPASS_MAX_PORT (SENARY_MI2S_TX + 1) ++#define LPASS_MAX_PORT (LPI_MI2S_TX_6 + 1) ++ ++struct qcom_snd_tdm_slot_cfg { ++ unsigned int tx_mask; ++ unsigned int rx_mask; ++ unsigned int slots; ++ unsigned int slot_width; ++}; + + int qcom_snd_parse_of(struct snd_soc_card *card); ++int qcom_snd_get_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, ++ struct qcom_snd_tdm_slot_cfg *cpu_cfg, ++ struct qcom_snd_tdm_slot_cfg *codec_cfg); ++int qcom_snd_apply_dai_tdm_slots_cfg(struct snd_soc_pcm_runtime *rtd, ++ const struct qcom_snd_tdm_slot_cfg *cpu_cfg, ++ const struct qcom_snd_tdm_slot_cfg *codec_cfg); ++int qcom_snd_apply_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd); + int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_jack *jack, bool *jack_setup); + int qcom_snd_dp_jack_setup(struct snd_soc_pcm_runtime *rtd, +diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c +--- a/sound/soc/qcom/qdsp6/audioreach.c ++++ b/sound/soc/qcom/qdsp6/audioreach.c +@@ -152,6 +152,13 @@ struct apm_i2s_module_intf_cfg { + + #define APM_I2S_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_i2s_module_intf_cfg), 8) + ++struct apm_audio_if_module_intf_cfg { ++ struct apm_module_param_data param_data; ++ struct param_id_audio_if_intf_cfg cfg; ++} __packed; ++ ++#define APM_AUDIO_IF_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_audio_if_module_intf_cfg), 8) ++ + struct apm_module_hw_ep_mf_cfg { + struct apm_module_param_data param_data; + struct param_id_hw_ep_mf mf; +@@ -168,6 +175,13 @@ struct apm_module_frame_size_factor_cfg { + + #define APM_FS_CFG_PSIZE ALIGN(sizeof(struct apm_module_frame_size_factor_cfg), 8) + ++struct apm_module_hw_ep_frame_duration_cfg { ++ struct apm_module_param_data param_data; ++ struct param_id_hw_ep_frame_duration frame_duration; ++} __packed; ++ ++#define APM_HW_EP_FRAME_DURATION_PSIZE ALIGN(sizeof(struct apm_module_hw_ep_frame_duration_cfg), 8) ++ + struct apm_module_hw_ep_power_mode_cfg { + struct apm_module_param_data param_data; + struct param_id_hw_ep_power_mode_cfg power_mode; +@@ -703,6 +717,7 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph, + int pm_sz = APM_HW_EP_PMODE_CFG_PSIZE; + int size = ic_sz + ep_sz + fs_sz + pm_sz; + void *p; ++ int i; + + struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0); + if (IS_ERR(pkt)) +@@ -741,7 +756,12 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph, + + intf_cfg->cfg.lpaif_type = module->hw_interface_type; + intf_cfg->cfg.intf_index = module->hw_interface_idx; +- intf_cfg->cfg.active_channels_mask = (1 << cfg->num_channels) - 1; ++ intf_cfg->cfg.active_channels_mask = 0; ++ /* Convert the physical channel mapping into a bit field */ ++ for (i = 0; i < AR_PCM_MAX_NUM_CHANNEL; i++) ++ if (cfg->channel_map[i]) ++ intf_cfg->cfg.active_channels_mask |= BIT(i); ++ + p += ic_sz; + + pm_cfg = p; +@@ -840,7 +860,7 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, + uint32_t num_channels = cfg->num_channels; + int payload_size = APM_MFC_CFG_PSIZE(media_format, num_channels) + + APM_MODULE_PARAM_DATA_SIZE; +- int i; ++ int i, j; + void *p; + + struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); +@@ -860,8 +880,12 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, + media_format->sample_rate = cfg->sample_rate; + media_format->bit_width = cfg->bit_width; + media_format->num_channels = cfg->num_channels; +- for (i = 0; i < num_channels; i++) +- media_format->channel_mapping[i] = cfg->channel_map[i]; ++ /* Convert the physical mapping to a logical mapping of the channels */ ++ for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < cfg->num_channels; i++) { ++ if (!cfg->channel_map[i]) ++ continue; ++ media_format->channel_mapping[j++] = cfg->channel_map[i]; ++ } + + return q6apm_send_cmd_sync(graph->apm, pkt, 0); + } +@@ -1042,6 +1066,81 @@ static int audioreach_i2s_set_media_format(struct q6apm_graph *graph, + return q6apm_send_cmd_sync(graph->apm, pkt, 0); + } + ++static int audioreach_audio_if_set_media_format(struct q6apm_graph *graph, ++ const struct audioreach_module *module, ++ const struct audioreach_module_config *cfg) ++{ ++ struct apm_module_hw_ep_frame_duration_cfg *fd_cfg; ++ struct apm_module_param_data *param_data; ++ struct apm_audio_if_module_intf_cfg *intf_cfg; ++ struct apm_module_hw_ep_mf_cfg *hw_cfg; ++ int ic_sz = APM_AUDIO_IF_INTF_CFG_PSIZE; ++ int ep_sz = APM_HW_EP_CFG_PSIZE; ++ int fd_sz = APM_HW_EP_FRAME_DURATION_PSIZE; ++ int size = ic_sz + ep_sz + fd_sz; ++ u32 slot_mask = cfg->slot_mask ? cfg->slot_mask : module->slot_mask; ++ u16 nslots_per_frame = cfg->nslots_per_frame ? ++ (u16)cfg->nslots_per_frame : module->nslots_per_frame; ++ u16 slot_width = cfg->slot_width ? (u16)cfg->slot_width : module->slot_width; ++ void *p; ++ ++ struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0); ++ if (IS_ERR(pkt)) ++ return PTR_ERR(pkt); ++ ++ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; ++ intf_cfg = p; ++ ++ param_data = &intf_cfg->param_data; ++ param_data->module_instance_id = module->instance_id; ++ param_data->error_code = 0; ++ param_data->param_id = PARAM_ID_AUDIO_IF_INTF_CFG; ++ param_data->param_size = ic_sz - APM_MODULE_PARAM_DATA_SIZE; ++ intf_cfg->cfg.qaif_type = module->qaif_type; ++ intf_cfg->cfg.intf_idx = (u16)module->hw_interface_idx; ++ intf_cfg->cfg.intf_mode = module->intf_mode; ++ intf_cfg->cfg.ctrl_data_out_enable = module->ctrl_data_out_enable; ++ intf_cfg->cfg.active_slot_mask = slot_mask; ++ intf_cfg->cfg.nslots_per_frame = nslots_per_frame; ++ intf_cfg->cfg.slot_width = slot_width; ++ intf_cfg->cfg.active_lane_mask = module->active_lane_mask; ++ intf_cfg->cfg.frame_sync_rate = module->frame_sync_rate; ++ intf_cfg->cfg.frame_sync_src = module->sync_src; ++ intf_cfg->cfg.frame_sync_mode = module->sync_mode; ++ intf_cfg->cfg.invert_frame_sync_pulse = module->ctrl_invert_sync_pulse; ++ intf_cfg->cfg.frame_sync_data_delay = module->ctrl_sync_data_delay; ++ intf_cfg->cfg.bit_clk_type = module->bit_clk_type; ++ intf_cfg->cfg.inv_int_bit_clk = module->inv_int_bit_clk; ++ intf_cfg->cfg.inv_ext_bit_clk = module->inv_ext_bit_clk; ++ ++ p += ic_sz; ++ hw_cfg = p; ++ param_data = &hw_cfg->param_data; ++ param_data->module_instance_id = module->instance_id; ++ param_data->error_code = 0; ++ param_data->param_id = PARAM_ID_HW_EP_MF_CFG; ++ param_data->param_size = ep_sz - APM_MODULE_PARAM_DATA_SIZE; ++ ++ hw_cfg->mf.sample_rate = cfg->sample_rate; ++ hw_cfg->mf.bit_width = cfg->bit_width; ++ hw_cfg->mf.num_channels = cfg->num_channels; ++ hw_cfg->mf.data_format = module->data_format; ++ ++ p += ep_sz; ++ fd_cfg = p; ++ param_data = &fd_cfg->param_data; ++ param_data->module_instance_id = module->instance_id; ++ param_data->error_code = 0; ++ param_data->param_id = PARAM_ID_HW_EP_FRAME_DURATION; ++ param_data->param_size = fd_sz - APM_MODULE_PARAM_DATA_SIZE; ++ fd_cfg->frame_duration.frame_duration_in_us = AUDIO_IF_FRAME_DURATION_US; ++ fd_cfg->frame_duration.allow_frame_duration_normalization = AUDIO_IF_FRAME_DURATION_NORMALIZATION_ENABLE; ++ fd_cfg->frame_duration.min_normalized_frame_dur_us = AUDIO_IF_FRAME_DURATION_MIN_US; ++ fd_cfg->frame_duration.max_normalized_frame_dur_us = AUDIO_IF_FRAME_DURATION_MAX_US; ++ ++ return q6apm_send_cmd_sync(graph->apm, pkt, 0); ++} ++ + static int audioreach_logging_set_media_format(struct q6apm_graph *graph, + const struct audioreach_module *module) + { +@@ -1080,6 +1179,7 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph, + struct apm_pcm_module_media_fmt_cmd *cfg; + struct apm_module_param_data *param_data; + int payload_size; ++ int i, j; + + if (num_channels > 4) { + dev_err(graph->dev, "Error: Invalid channels (%d)!\n", num_channels); +@@ -1113,7 +1213,12 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph, + media_cfg->num_channels = mcfg->num_channels; + media_cfg->q_factor = mcfg->bit_width - 1; + media_cfg->bits_per_sample = mcfg->bit_width; +- memcpy(media_cfg->channel_mapping, mcfg->channel_map, mcfg->num_channels); ++ /* Convert the physical mapping to a logical mapping of the channels */ ++ for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < mcfg->num_channels; i++) { ++ if (!mcfg->channel_map[i]) ++ continue; ++ media_cfg->channel_mapping[j++] = mcfg->channel_map[i]; ++ } + + return q6apm_send_cmd_sync(graph->apm, pkt, 0); + } +@@ -1163,6 +1268,7 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph, + struct payload_media_fmt_pcm *cfg; + struct media_format *header; + int rc, payload_size; ++ int i, j; + void *p; + + if (num_channels > 4) { +@@ -1202,7 +1308,12 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph, + cfg->q_factor = mcfg->bit_width - 1; + cfg->endianness = PCM_LITTLE_ENDIAN; + cfg->num_channels = mcfg->num_channels; +- memcpy(cfg->channel_mapping, mcfg->channel_map, mcfg->num_channels); ++ /* Convert the physical mapping to a logical mapping of the channels */ ++ for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < cfg->num_channels; i++) { ++ if (!mcfg->channel_map[i]) ++ continue; ++ cfg->channel_mapping[j++] = mcfg->channel_map[i]; ++ } + } else { + rc = audioreach_set_compr_media_format(header, p, mcfg); + if (rc) +@@ -1279,7 +1390,7 @@ static int audioreach_speaker_protection_vi(struct q6apm_graph *graph, + struct apm_module_sp_vi_ex_mode_cfg *ex_cfg; + int op_sz, cm_sz, ex_sz; + struct apm_module_param_data *param_data; +- int rc, i, payload_size; ++ int rc, i, payload_size, j; + struct gpr_pkt *pkt; + void *p; + +@@ -1320,14 +1431,19 @@ static int audioreach_speaker_protection_vi(struct q6apm_graph *graph, + param_data->param_size = cm_sz - APM_MODULE_PARAM_DATA_SIZE; + + cm_cfg->cfg.num_channels = num_channels * 2; +- for (i = 0; i < num_channels; i++) { ++ /* Convert the physical mapping to a logical mapping of the channels */ ++ for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < num_channels; i++) { ++ if (!mcfg->channel_map[i]) ++ continue; + /* + * Map speakers into Vsense and then Isense of each channel. + * E.g. for PCM_CHANNEL_FL and PCM_CHANNEL_FR to: + * [1, 2, 3, 4] + */ +- cm_cfg->cfg.channel_mapping[2 * i] = (mcfg->channel_map[i] - 1) * 2 + 1; +- cm_cfg->cfg.channel_mapping[2 * i + 1] = (mcfg->channel_map[i] - 1) * 2 + 2; ++ cm_cfg->cfg.channel_mapping[2 * j] = (mcfg->channel_map[i] - 1) * 2 + 1; ++ cm_cfg->cfg.channel_mapping[2 * j + 1] = (mcfg->channel_map[i] - 1) * 2 + 2; ++ ++ ++j; + } + + p += cm_sz; +@@ -1411,6 +1527,10 @@ int audioreach_set_media_format(struct q6apm_graph *graph, + if (!rc) + rc = audioreach_module_enable(graph, module, true); + break; ++ case MODULE_ID_AUDIO_IF_SOURCE: ++ case MODULE_ID_AUDIO_IF_SINK: ++ rc = audioreach_audio_if_set_media_format(graph, module, cfg); ++ break; + + default: + rc = 0; +diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h +--- a/sound/soc/qcom/qdsp6/audioreach.h ++++ b/sound/soc/qcom/qdsp6/audioreach.h +@@ -36,6 +36,8 @@ struct q6apm_graph; + #define MODULE_ID_SPEAKER_PROTECTION 0x070010E2 + #define MODULE_ID_SPEAKER_PROTECTION_VI 0x070010E3 + #define MODULE_ID_OPUS_DEC 0x07001174 ++#define MODULE_ID_AUDIO_IF_SINK 0x0700117C ++#define MODULE_ID_AUDIO_IF_SOURCE 0x0700117D + + #define APM_CMD_GET_SPF_STATE 0x01001021 + #define APM_CMD_RSP_GET_SPF_STATE 0x02001007 +@@ -544,6 +546,74 @@ struct param_id_i2s_intf_cfg { + #define PORT_ID_I2S_OUPUT 1 + #define I2S_STACK_SIZE 2048 + ++#define PARAM_ID_AUDIO_IF_INTF_CFG 0x08001B11 ++ ++/* ++ * struct param_id_audio_if_intf_cfg - Audio interface configuration ++ * @qaif_type: Audio interface type (e.g. QAIF, QAIF_VA) ++ * @intf_idx: Interface instance index ++ * @intf_mode: Interface operating mode (TDM/PCM/I2S) ++ * @ctrl_data_out_enable: Enable sharing of data-out signal with other masters ++ * @active_slot_mask: Bitmask indicating active slots ++ * @nslots_per_frame: Number of slots per audio frame ++ * @slot_width: Width of each slot in bits ++ * @active_lane_mask: Bitmask of active data lanes ++ * @frame_sync_rate: Frame sync rate in Hz ++ * @frame_sync_src: Frame sync source selection ++ * @frame_sync_mode: Frame sync mode configuration ++ * @invert_frame_sync_pulse: Invert frame sync polarity when set ++ * @frame_sync_data_delay: Data delay from frame sync in bit clocks ++ * @bit_clk_type: Bit clock type (internal / external) ++ * @inv_int_bit_clk: Invert internal bit clock when set ++ * @inv_ext_bit_clk: Invert external bit clock when set ++ * ++ * This structure defines configuration parameters for the Qualcomm ++ * Audio Interface (QAIF) block. It is used to program interface ++ * characteristics such as slot configuration, clocking and frame ++ * synchronization behaviour. ++ */ ++struct param_id_audio_if_intf_cfg { ++ uint16_t qaif_type; ++ uint16_t intf_idx; ++ uint16_t intf_mode; ++ uint16_t ctrl_data_out_enable; ++ uint32_t active_slot_mask; ++ uint16_t nslots_per_frame; ++ uint16_t slot_width; ++ uint32_t active_lane_mask; ++ uint32_t frame_sync_rate; ++ uint16_t frame_sync_src; ++ uint16_t frame_sync_mode; ++ uint16_t invert_frame_sync_pulse; ++ uint16_t frame_sync_data_delay; ++ uint16_t bit_clk_type; ++ uint8_t inv_int_bit_clk; ++ uint8_t inv_ext_bit_clk; ++} __packed; ++ ++#define PARAM_ID_HW_EP_FRAME_DURATION 0x08001B2F ++#define AUDIO_IF_FRAME_DURATION_US 1000 ++#define AUDIO_IF_FRAME_DURATION_NORMALIZATION_ENABLE 1 ++#define AUDIO_IF_FRAME_DURATION_MIN_US 1 ++#define AUDIO_IF_FRAME_DURATION_MAX_US 100000 ++ ++/** ++ * struct param_id_hw_ep_frame_duration - Hardware endpoint frame duration ++ * @frame_duration_in_us: Frame duration in microseconds. ++ * @allow_frame_duration_normalization: Permit SPF to normalize frame duration. ++ * @min_normalized_frame_dur_us: Minimum normalized frame duration in microseconds. ++ * @max_normalized_frame_dur_us: Maximum normalized frame duration in microseconds. ++ * ++ * This structure configures the frame duration for the Audio IF hardware ++ * endpoint and, when enabled, the allowed normalization range. ++ */ ++struct param_id_hw_ep_frame_duration { ++ uint32_t frame_duration_in_us; ++ uint32_t allow_frame_duration_normalization; ++ uint32_t min_normalized_frame_dur_us; ++ uint32_t max_normalized_frame_dur_us; ++} __packed; ++ + #define PARAM_ID_DISPLAY_PORT_INTF_CFG 0x08001154 + + struct param_id_display_port_intf_cfg { +@@ -877,6 +947,23 @@ struct audioreach_module { + uint32_t data_format; + uint32_t hw_interface_type; + ++ /* Audio IF module (TDM/PCM/I2S) */ ++ u32 slot_mask; ++ u32 active_lane_mask; ++ u32 frame_sync_rate; ++ u16 qaif_type; ++ u16 sync_src; ++ u16 ctrl_data_out_enable; ++ u16 nslots_per_frame; ++ u16 slot_width; ++ u16 intf_mode; ++ u16 sync_mode; ++ u16 ctrl_invert_sync_pulse; ++ u16 ctrl_sync_data_delay; ++ u16 bit_clk_type; ++ u8 inv_int_bit_clk; ++ u8 inv_ext_bit_clk; ++ + /* PCM module specific */ + uint32_t interleave_type; + +@@ -907,6 +994,9 @@ struct audioreach_module_config { + u32 channel_allocation; + u32 sd_line_mask; + int fmt; ++ u32 slot_mask; ++ u16 nslots_per_frame; ++ u16 slot_width; + struct snd_codec codec; + u8 channel_map[AR_PCM_MAX_NUM_CHANNEL]; + }; +diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c +--- a/sound/soc/qcom/qdsp6/q6afe-dai.c ++++ b/sound/soc/qcom/qdsp6/q6afe-dai.c +@@ -412,6 +412,8 @@ static int q6afe_dai_prepare(struct snd_pcm_substream *substream, + case SENARY_MI2S_RX ... SENARY_MI2S_TX: + case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: + case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: ++ case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: ++ case LPI_MI2S_RX_5 ... LPI_MI2S_TX_6: + rc = q6afe_i2s_port_prepare(dai_data->port[dai->id], + &dai_data->port_config[dai->id].i2s_cfg); + if (rc < 0) { +@@ -665,6 +667,21 @@ static const struct snd_soc_dapm_route q6afe_dapm_routes[] = { + + /* USB playback AFE port receives data for playback, hence use the RX port */ + {"USB Playback", NULL, "USB_RX"}, ++ ++ {"LPI RX0 MI2S Playback", NULL, "LPI_MI2S_RX_0"}, ++ {"LPI_MI2S_TX_0", NULL, "LPI TX0 MI2S Capture"}, ++ {"LPI RX1 MI2S Playback", NULL, "LPI_MI2S_RX_1"}, ++ {"LPI_MI2S_TX_1", NULL, "LPI TX1 MI2S Capture"}, ++ {"LPI RX2 MI2S Playback", NULL, "LPI_MI2S_RX_2"}, ++ {"LPI_MI2S_TX_2", NULL, "LPI TX2 MI2S Capture"}, ++ {"LPI RX3 MI2S Playback", NULL, "LPI_MI2S_RX_3"}, ++ {"LPI_MI2S_TX_3", NULL, "LPI TX3 MI2S Capture"}, ++ {"LPI RX4 MI2S Playback", NULL, "LPI_MI2S_RX_4"}, ++ {"LPI_MI2S_TX_4", NULL, "LPI TX4 MI2S Capture"}, ++ {"LPI RX5 MI2S Playback", NULL, "LPI_MI2S_RX_5"}, ++ {"LPI_MI2S_TX_5", NULL, "LPI TX5 MI2S Capture"}, ++ {"LPI RX6 MI2S Playback", NULL, "LPI_MI2S_RX_6"}, ++ {"LPI_MI2S_TX_6", NULL, "LPI TX6 MI2S Capture"}, + }; + + static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai) +@@ -1011,6 +1028,35 @@ static const struct snd_soc_dapm_widget q6afe_dai_widgets[] = { + 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("USB_RX", NULL, 0, SND_SOC_NOPM, 0, 0), ++ ++ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_0", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_0", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_1", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_1", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_2", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_2", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_3", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_3", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_4", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_4", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_5", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_5", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_6", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_6", NULL, ++ 0, SND_SOC_NOPM, 0, 0), + }; + + static const struct snd_soc_component_driver q6afe_dai_component = { +@@ -1045,6 +1091,8 @@ static void of_q6afe_parse_dai_data(struct device *dev, + case SENARY_MI2S_RX ... SENARY_MI2S_TX: + case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: + case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: ++ case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: ++ case LPI_MI2S_RX_5 ... LPI_MI2S_TX_6: + priv = &data->priv[id]; + ret = of_property_read_variable_u32_array(node, + "qcom,sd-lines", +diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c +--- a/sound/soc/qcom/qdsp6/q6afe.c ++++ b/sound/soc/qcom/qdsp6/q6afe.c +@@ -132,6 +132,20 @@ + #define AFE_PORT_ID_QUINARY_MI2S_TX 0x1017 + #define AFE_PORT_ID_SENARY_MI2S_RX 0x1018 + #define AFE_PORT_ID_SENARY_MI2S_TX 0x1019 ++#define AFE_PORT_ID_INT0_MI2S_RX 0x102e ++#define AFE_PORT_ID_INT0_MI2S_TX 0x102f ++#define AFE_PORT_ID_INT1_MI2S_RX 0x1030 ++#define AFE_PORT_ID_INT1_MI2S_TX 0x1031 ++#define AFE_PORT_ID_INT2_MI2S_RX 0x1032 ++#define AFE_PORT_ID_INT2_MI2S_TX 0x1033 ++#define AFE_PORT_ID_INT3_MI2S_RX 0x1034 ++#define AFE_PORT_ID_INT3_MI2S_TX 0x1035 ++#define AFE_PORT_ID_INT4_MI2S_RX 0x1036 ++#define AFE_PORT_ID_INT4_MI2S_TX 0x1037 ++#define AFE_PORT_ID_INT5_MI2S_RX 0x1038 ++#define AFE_PORT_ID_INT5_MI2S_TX 0x1039 ++#define AFE_PORT_ID_INT6_MI2S_RX 0x103a ++#define AFE_PORT_ID_INT6_MI2S_TX 0x103b + + /* Start of the range of port IDs for TDM devices. */ + #define AFE_PORT_ID_TDM_PORT_RANGE_START 0x9000 +@@ -931,6 +945,34 @@ static struct afe_port_map port_maps[AFE_PORT_MAX] = { + [RX_CODEC_DMA_RX_7] = { AFE_PORT_ID_RX_CODEC_DMA_RX_7, + RX_CODEC_DMA_RX_7, 1, 1}, + [USB_RX] = { AFE_PORT_ID_USB_RX, USB_RX, 1, 1}, ++ [LPI_MI2S_RX_0] = { AFE_PORT_ID_INT0_MI2S_RX, ++ LPI_MI2S_RX_0, 1, 1}, ++ [LPI_MI2S_TX_0] = { AFE_PORT_ID_INT0_MI2S_TX, ++ LPI_MI2S_TX_0, 0, 1}, ++ [LPI_MI2S_RX_1] = { AFE_PORT_ID_INT1_MI2S_RX, ++ LPI_MI2S_RX_1, 1, 1}, ++ [LPI_MI2S_TX_1] = { AFE_PORT_ID_INT1_MI2S_TX, ++ LPI_MI2S_TX_1, 0, 1}, ++ [LPI_MI2S_RX_2] = { AFE_PORT_ID_INT2_MI2S_RX, ++ LPI_MI2S_RX_2, 1, 1}, ++ [LPI_MI2S_TX_2] = { AFE_PORT_ID_INT2_MI2S_TX, ++ LPI_MI2S_TX_2, 0, 1}, ++ [LPI_MI2S_RX_3] = { AFE_PORT_ID_INT3_MI2S_RX, ++ LPI_MI2S_RX_3, 1, 1}, ++ [LPI_MI2S_TX_3] = { AFE_PORT_ID_INT3_MI2S_TX, ++ LPI_MI2S_TX_3, 0, 1}, ++ [LPI_MI2S_RX_4] = { AFE_PORT_ID_INT4_MI2S_RX, ++ LPI_MI2S_RX_4, 1, 1}, ++ [LPI_MI2S_TX_4] = { AFE_PORT_ID_INT4_MI2S_TX, ++ LPI_MI2S_TX_4, 0, 1}, ++ [LPI_MI2S_RX_5] = { AFE_PORT_ID_INT5_MI2S_RX, ++ LPI_MI2S_RX_5, 1, 1}, ++ [LPI_MI2S_TX_5] = { AFE_PORT_ID_INT5_MI2S_TX, ++ LPI_MI2S_TX_5, 0, 1}, ++ [LPI_MI2S_RX_6] = { AFE_PORT_ID_INT6_MI2S_RX, ++ LPI_MI2S_RX_6, 1, 1}, ++ [LPI_MI2S_TX_6] = { AFE_PORT_ID_INT6_MI2S_TX, ++ LPI_MI2S_TX_6, 0, 1}, + }; + + static void q6afe_port_free(struct kref *ref) +@@ -1785,6 +1827,20 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id) + case AFE_PORT_ID_QUINARY_MI2S_TX: + case AFE_PORT_ID_SENARY_MI2S_RX: + case AFE_PORT_ID_SENARY_MI2S_TX: ++ case AFE_PORT_ID_INT0_MI2S_RX: ++ case AFE_PORT_ID_INT0_MI2S_TX: ++ case AFE_PORT_ID_INT1_MI2S_RX: ++ case AFE_PORT_ID_INT1_MI2S_TX: ++ case AFE_PORT_ID_INT2_MI2S_RX: ++ case AFE_PORT_ID_INT2_MI2S_TX: ++ case AFE_PORT_ID_INT3_MI2S_RX: ++ case AFE_PORT_ID_INT3_MI2S_TX: ++ case AFE_PORT_ID_INT4_MI2S_RX: ++ case AFE_PORT_ID_INT4_MI2S_TX: ++ case AFE_PORT_ID_INT5_MI2S_RX: ++ case AFE_PORT_ID_INT5_MI2S_TX: ++ case AFE_PORT_ID_INT6_MI2S_RX: ++ case AFE_PORT_ID_INT6_MI2S_TX: + cfg_type = AFE_PARAM_ID_I2S_CONFIG; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX ... AFE_PORT_ID_QUINARY_TDM_TX_7: +diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +--- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c ++++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +@@ -2,10 +2,12 @@ + // Copyright (c) 2021, Linaro Limited + + #include ++#include + #include + #include + #include + #include ++#include + #include + #include + #include +@@ -15,15 +17,54 @@ + #include "q6dsp-common.h" + #include "audioreach.h" + #include "q6apm.h" ++#include "q6prm.h" + + #define AUDIOREACH_BE_PCM_BASE 16 + ++struct q6apm_dai_priv_data { ++ struct clk *mclk; ++ struct clk *bclk; ++ bool mclk_enabled, bclk_enabled; ++}; ++ + struct q6apm_lpass_dai_data { + struct q6apm_graph *graph[APM_PORT_MAX]; + bool is_port_started[APM_PORT_MAX]; + struct audioreach_module_config module_config[APM_PORT_MAX]; ++ struct q6apm_dai_priv_data priv[APM_PORT_MAX]; + }; + ++static void q6apm_lpass_dai_disable_clocks(struct q6apm_lpass_dai_data *dai_data, int id) ++{ ++ if (dai_data->priv[id].mclk_enabled) { ++ clk_disable_unprepare(dai_data->priv[id].mclk); ++ dai_data->priv[id].mclk_enabled = false; ++ } ++ ++ if (dai_data->priv[id].bclk_enabled) { ++ clk_disable_unprepare(dai_data->priv[id].bclk); ++ dai_data->priv[id].bclk_enabled = false; ++ } ++} ++ ++static void q6apm_lpass_dai_put_clocks(struct q6apm_lpass_dai_data *dai_data) ++{ ++ int i; ++ ++ for (i = 0; i < APM_PORT_MAX; i++) { ++ q6apm_lpass_dai_disable_clocks(dai_data, i); ++ ++ if (dai_data->priv[i].mclk) { ++ clk_put(dai_data->priv[i].mclk); ++ dai_data->priv[i].mclk = NULL; ++ } ++ if (dai_data->priv[i].bclk) { ++ clk_put(dai_data->priv[i].bclk); ++ dai_data->priv[i].bclk = NULL; ++ } ++ } ++} ++ + static int q6dma_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, + const unsigned int *tx_ch_mask, +@@ -251,6 +292,62 @@ static int q6apm_lpass_dai_startup(struct snd_pcm_substream *substream, struct s + return 0; + } + ++static int q6i2s_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) ++{ ++ return q6apm_lpass_dai_startup(substream, dai); ++} ++ ++static void q6i2s_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) ++{ ++ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); ++ ++ q6apm_lpass_dai_shutdown(substream, dai); ++ q6apm_lpass_dai_disable_clocks(dai_data, dai->id); ++} ++ ++static int q6i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) ++{ ++ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); ++ struct clk *sysclk = NULL; ++ bool *enabled = NULL; ++ int ret = 0; ++ ++ switch (clk_id) { ++ case LPAIF_MI2S_MCLK: ++ sysclk = dai_data->priv[dai->id].mclk; ++ enabled = &dai_data->priv[dai->id].mclk_enabled; ++ break; ++ case LPAIF_MI2S_BCLK: ++ sysclk = dai_data->priv[dai->id].bclk; ++ enabled = &dai_data->priv[dai->id].bclk_enabled; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (sysclk) { ++ ret = clk_set_rate(sysclk, freq); ++ if (ret) { ++ dev_err(dai->dev, "Error, Unable to set rate (%d) for sysclk %d\n", ++ freq, clk_id); ++ return ret; ++ } ++ ++ if (*enabled) ++ return 0; ++ ++ ret = clk_prepare_enable(sysclk); ++ if (ret) { ++ dev_err(dai->dev, "Error, Unable to prepare (%d) sysclk\n", clk_id); ++ return ret; ++ } ++ ++ *enabled = true; ++ } ++ ++ return ret; ++} ++ + static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) + { + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); +@@ -261,6 +358,53 @@ static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) + return 0; + } + ++static int q6tdm_set_tdm_slot(struct snd_soc_dai *dai, ++ unsigned int tx_mask, ++ unsigned int rx_mask, ++ int slots, int slot_width) ++{ ++ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); ++ struct audioreach_module_config *cfg = &dai_data->module_config[dai->id]; ++ unsigned int cap_mask, slot_mask; ++ ++ if (slot_width != 16 && slot_width != 32) { ++ dev_err(dai->dev, "%s: invalid slot_width %d\n", __func__, slot_width); ++ return -EINVAL; ++ } ++ ++ switch (slots) { ++ case 2: ++ case 4: ++ case 8: ++ case 16: ++ cap_mask = GENMASK(slots - 1, 0); ++ break; ++ default: ++ dev_err(dai->dev, "%s: invalid slots %d\n", __func__, slots); ++ return -EINVAL; ++ } ++ ++ switch (dai->id) { ++ case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: ++ slot_mask = (dai->id & 0x1) ? tx_mask : rx_mask; ++ if (slot_mask & ~cap_mask) { ++ dev_err(dai->dev, "%s: invalid slot mask 0x%x for %d slots\n", ++ __func__, slot_mask, slots); ++ return -EINVAL; ++ } ++ ++ cfg->nslots_per_frame = slots; ++ cfg->slot_width = slot_width; ++ cfg->slot_mask = slot_mask; ++ break; ++ default: ++ dev_err(dai->dev, "%s: invalid dai id 0x%x\n", __func__, dai->id); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ + static const struct snd_soc_dai_ops q6dma_ops = { + .prepare = q6apm_lpass_dai_prepare, + .startup = q6apm_lpass_dai_startup, +@@ -272,11 +416,12 @@ static const struct snd_soc_dai_ops q6dma_ops = { + + static const struct snd_soc_dai_ops q6i2s_ops = { + .prepare = q6apm_lpass_dai_prepare, +- .startup = q6apm_lpass_dai_startup, +- .shutdown = q6apm_lpass_dai_shutdown, ++ .startup = q6i2s_dai_startup, ++ .shutdown = q6i2s_lpass_dai_shutdown, + .set_channel_map = q6dma_set_channel_map, + .hw_params = q6dma_hw_params, + .set_fmt = q6i2s_set_fmt, ++ .set_sysclk = q6i2s_set_sysclk, + .trigger = q6apm_lpass_dai_trigger, + }; + +@@ -289,6 +434,17 @@ static const struct snd_soc_dai_ops q6hdmi_ops = { + .trigger = q6apm_lpass_dai_trigger, + }; + ++static const struct snd_soc_dai_ops q6tdm_ops = { ++ .prepare = q6apm_lpass_dai_prepare, ++ .startup = q6apm_lpass_dai_startup, ++ .shutdown = q6i2s_lpass_dai_shutdown, ++ .set_tdm_slot = q6tdm_set_tdm_slot, ++ .hw_params = q6dma_hw_params, ++ .set_fmt = q6i2s_set_fmt, ++ .set_sysclk = q6i2s_set_sysclk, ++ .trigger = q6apm_lpass_dai_trigger, ++}; ++ + static const struct snd_soc_component_driver q6apm_lpass_dai_component = { + .name = "q6apm-be-dai-component", + .of_xlate_dai_name = q6dsp_audio_ports_of_xlate_dai_name, +@@ -297,6 +453,65 @@ static const struct snd_soc_component_driver q6apm_lpass_dai_component = { + .remove_order = SND_SOC_COMP_ORDER_FIRST, + }; + ++static int of_q6apm_parse_dai_data(struct device *dev, ++ struct q6apm_lpass_dai_data *data) ++{ ++ int ret; ++ ++ for_each_child_of_node_scoped(dev->of_node, node) { ++ struct q6apm_dai_priv_data *priv; ++ int id; ++ ++ ret = of_property_read_u32(node, "reg", &id); ++ if (ret || id < 0 || id >= APM_PORT_MAX) { ++ dev_err(dev, "valid dai id not found:%d\n", ret); ++ continue; ++ } ++ ++ switch (id) { ++ /* MI2S specific properties */ ++ case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: ++ case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: ++ case SENARY_MI2S_RX ... SENARY_MI2S_TX: ++ case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: ++ priv = &data->priv[id]; ++ priv->mclk = of_clk_get_by_name(node, "mclk"); ++ if (IS_ERR(priv->mclk)) { ++ int err = PTR_ERR(priv->mclk); ++ ++ priv->mclk = NULL; ++ if (err == -EPROBE_DEFER) { ++ q6apm_lpass_dai_put_clocks(data); ++ return dev_err_probe(dev, err, ++ "unable to get mi2s mclk\n"); ++ } ++ } ++ ++ priv->bclk = of_clk_get_by_name(node, "bclk"); ++ if (IS_ERR(priv->bclk)) { ++ int err = PTR_ERR(priv->bclk); ++ ++ priv->bclk = NULL; ++ if (err == -EPROBE_DEFER) { ++ q6apm_lpass_dai_put_clocks(data); ++ return dev_err_probe(dev, err, ++ "unable to get mi2s bclk\n"); ++ } ++ } ++ break; ++ default: ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++static void q6apm_lpass_dai_clocks_action(void *data) ++{ ++ q6apm_lpass_dai_put_clocks(data); ++} ++ + static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) + { + struct q6dsp_audio_port_dai_driver_config cfg; +@@ -304,17 +519,26 @@ static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) + struct snd_soc_dai_driver *dais; + struct device *dev = &pdev->dev; + int num_dais; ++ int ret; + + dai_data = devm_kzalloc(dev, sizeof(*dai_data), GFP_KERNEL); + if (!dai_data) + return -ENOMEM; + + dev_set_drvdata(dev, dai_data); ++ ret = of_q6apm_parse_dai_data(dev, dai_data); ++ if (ret) ++ return ret; ++ ++ ret = devm_add_action_or_reset(dev, q6apm_lpass_dai_clocks_action, dai_data); ++ if (ret) ++ return ret; + + memset(&cfg, 0, sizeof(cfg)); + cfg.q6i2s_ops = &q6i2s_ops; + cfg.q6dma_ops = &q6dma_ops; + cfg.q6hdmi_ops = &q6hdmi_ops; ++ cfg.q6tdm_ops = &q6tdm_ops; + dais = q6dsp_audio_ports_set_config(dev, &cfg, &num_dais); + + return devm_snd_soc_register_component(dev, &q6apm_lpass_dai_component, dais, num_dais); +diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c +--- a/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c ++++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c +@@ -12,7 +12,7 @@ + #include + #include "q6dsp-lpass-clocks.h" + +-#define Q6DSP_MAX_CLK_ID 104 ++#define Q6DSP_MAX_CLK_ID 105 + #define Q6DSP_LPASS_CLK_ROOT_DEFAULT 0 + + +diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c +--- a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c ++++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c +@@ -553,11 +553,15 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { + Q6AFE_MI2S_RX_DAI("LPI RX2", LPI_MI2S_RX_2), + Q6AFE_MI2S_RX_DAI("LPI RX3", LPI_MI2S_RX_3), + Q6AFE_MI2S_RX_DAI("LPI RX4", LPI_MI2S_RX_4), ++ Q6AFE_MI2S_RX_DAI("LPI RX5", LPI_MI2S_RX_5), ++ Q6AFE_MI2S_RX_DAI("LPI RX6", LPI_MI2S_RX_6), + Q6AFE_MI2S_TX_DAI("LPI TX0", LPI_MI2S_TX_0), + Q6AFE_MI2S_TX_DAI("LPI TX1", LPI_MI2S_TX_1), + Q6AFE_MI2S_TX_DAI("LPI TX2", LPI_MI2S_TX_2), + Q6AFE_MI2S_TX_DAI("LPI TX3", LPI_MI2S_TX_3), + Q6AFE_MI2S_TX_DAI("LPI TX4", LPI_MI2S_TX_4), ++ Q6AFE_MI2S_TX_DAI("LPI TX5", LPI_MI2S_TX_5), ++ Q6AFE_MI2S_TX_DAI("LPI TX6", LPI_MI2S_TX_6), + Q6AFE_TDM_PB_DAI("Primary", 0, PRIMARY_TDM_RX_0), + Q6AFE_TDM_PB_DAI("Primary", 1, PRIMARY_TDM_RX_1), + Q6AFE_TDM_PB_DAI("Primary", 2, PRIMARY_TDM_RX_2), +@@ -712,6 +716,7 @@ struct snd_soc_dai_driver *q6dsp_audio_ports_set_config(struct device *dev, + case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: + case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: + case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: ++ case LPI_MI2S_RX_5 ... LPI_MI2S_TX_6: + q6dsp_audio_fe_dais[i].ops = cfg->q6i2s_ops; + break; + case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: +diff --git a/sound/soc/qcom/qdsp6/q6prm-clocks.c b/sound/soc/qcom/qdsp6/q6prm-clocks.c +--- a/sound/soc/qcom/qdsp6/q6prm-clocks.c ++++ b/sound/soc/qcom/qdsp6/q6prm-clocks.c +@@ -42,6 +42,11 @@ static const struct q6dsp_clk_init q6prm_clks[] = { + Q6PRM_CLK(LPASS_CLK_ID_INT5_MI2S_IBIT), + Q6PRM_CLK(LPASS_CLK_ID_INT6_MI2S_IBIT), + Q6PRM_CLK(LPASS_CLK_ID_QUI_MI2S_OSR), ++ Q6PRM_CLK(LPASS_CLK_ID_MCLK_1), ++ Q6PRM_CLK(LPASS_CLK_ID_MCLK_2), ++ Q6PRM_CLK(LPASS_CLK_ID_MCLK_3), ++ Q6PRM_CLK(LPASS_CLK_ID_MCLK_4), ++ Q6PRM_CLK(LPASS_CLK_ID_MCLK_5), + Q6PRM_CLK(LPASS_CLK_ID_WSA_CORE_MCLK), + Q6PRM_CLK(LPASS_CLK_ID_WSA_CORE_NPL_MCLK), + Q6PRM_CLK(LPASS_CLK_ID_VA_CORE_MCLK), +@@ -59,10 +64,40 @@ static const struct q6dsp_clk_init q6prm_clks[] = { + Q6PRM_CLK(LPASS_CLK_ID_WSA2_CORE_TX_MCLK), + Q6PRM_CLK(LPASS_CLK_ID_WSA2_CORE_TX_2X_MCLK), + Q6PRM_CLK(LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF0_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF0_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF1_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF1_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF2_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF2_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF3_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF3_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF4_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF4_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF5_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF5_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF6_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF6_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF7_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF7_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF8_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF8_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF9_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF9_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF10_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF10_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF11_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF11_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF12_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF12_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_VA_QAIF_IF0_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_VA_QAIF_IF0_EBIT), + Q6DSP_VOTE_CLK(LPASS_HW_MACRO_VOTE, Q6PRM_HW_CORE_ID_LPASS, + "LPASS_HW_MACRO"), + Q6DSP_VOTE_CLK(LPASS_HW_DCODEC_VOTE, Q6PRM_HW_CORE_ID_DCODEC, + "LPASS_HW_DCODEC"), ++ Q6DSP_VOTE_CLK(LPASS_HW_LPR_VOTE, Q6PRM_HW_LPR_VOTE, ++ "LPASS_HW_LPR_VOTE"), + }; + + static const struct q6dsp_clk_desc q6dsp_clk_q6prm __maybe_unused = { +diff --git a/sound/soc/qcom/qdsp6/q6prm.c b/sound/soc/qcom/qdsp6/q6prm.c +--- a/sound/soc/qcom/qdsp6/q6prm.c ++++ b/sound/soc/qcom/qdsp6/q6prm.c +@@ -31,10 +31,16 @@ struct q6prm { + #define PARAM_ID_RSC_HW_CORE 0x08001032 + #define PARAM_ID_RSC_LPASS_CORE 0x0800102B + #define PARAM_ID_RSC_AUDIO_HW_CLK 0x0800102C ++#define PARAM_ID_RSC_CPU_LPR 0x08001A6E ++ ++#define LPR_CPU_SS_SLEEP_DISABLE 0x1 + + struct prm_cmd_request_hw_core { + struct apm_module_param_data param_data; +- uint32_t hw_clk_id; ++ union { ++ u32 hw_clk_id; ++ u32 lpr_state; ++ }; + } __packed; + + struct prm_cmd_request_rsc { +@@ -62,6 +68,7 @@ static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool + struct prm_cmd_request_hw_core *req; + gpr_device_t *gdev = prm->gdev; + uint32_t opcode, rsp_opcode; ++ bool lpr_req = (hw_block_id == Q6PRM_HW_LPR_VOTE); + + if (enable) { + opcode = PRM_CMD_REQUEST_HW_RSC; +@@ -82,10 +89,13 @@ static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool + + param_data->module_instance_id = GPR_PRM_MODULE_IID; + param_data->error_code = 0; +- param_data->param_id = PARAM_ID_RSC_HW_CORE; ++ param_data->param_id = lpr_req ? PARAM_ID_RSC_CPU_LPR : PARAM_ID_RSC_HW_CORE; + param_data->param_size = sizeof(*req) - APM_MODULE_PARAM_DATA_SIZE; + +- req->hw_clk_id = hw_block_id; ++ if (lpr_req) ++ req->lpr_state = LPR_CPU_SS_SLEEP_DISABLE; ++ else ++ req->hw_clk_id = hw_block_id; + + return q6prm_send_cmd_sync(prm, pkt, rsp_opcode); + } +diff --git a/sound/soc/qcom/qdsp6/q6prm.h b/sound/soc/qcom/qdsp6/q6prm.h +--- a/sound/soc/qcom/qdsp6/q6prm.h ++++ b/sound/soc/qcom/qdsp6/q6prm.h +@@ -3,6 +3,9 @@ + #ifndef __Q6PRM_H__ + #define __Q6PRM_H__ + ++#define LPAIF_MI2S_MCLK 1 ++#define LPAIF_MI2S_BCLK 2 ++ + /* Clock ID for Primary I2S IBIT */ + #define Q6PRM_LPASS_CLK_ID_PRI_MI2S_IBIT 0x100 + /* Clock ID for Primary I2S EBIT */ +@@ -52,6 +55,17 @@ + /* Clock ID for QUINARY MI2S OSR CLK */ + #define Q6PRM_LPASS_CLK_ID_QUI_MI2S_OSR 0x116 + ++/* Clock ID for MCLK1 */ ++#define Q6PRM_LPASS_CLK_ID_MCLK_1 0x300 ++/* Clock ID for MCLK2 */ ++#define Q6PRM_LPASS_CLK_ID_MCLK_2 0x301 ++/* Clock ID for MCLK3 */ ++#define Q6PRM_LPASS_CLK_ID_MCLK_3 0x302 ++/* Clock ID for MCLK4 */ ++#define Q6PRM_LPASS_CLK_ID_MCLK_4 0x303 ++/* Clock ID for MCLK5 */ ++#define Q6PRM_LPASS_CLK_ID_MCLK_5 0x304 ++ + #define Q6PRM_LPASS_CLK_ID_WSA_CORE_MCLK 0x305 + #define Q6PRM_LPASS_CLK_ID_WSA_CORE_NPL_MCLK 0x306 + +@@ -83,10 +97,40 @@ + /* Clock ID for RX CORE MCLK2 2X MCLK */ + #define Q6PRM_LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK 0x318 + ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF0_IBIT 0x500 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF0_EBIT 0x501 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF1_IBIT 0x502 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF1_EBIT 0x503 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF2_IBIT 0x504 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF2_EBIT 0x505 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF3_IBIT 0x506 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF3_EBIT 0x507 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF4_IBIT 0x508 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF4_EBIT 0x509 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF5_IBIT 0x50A ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF5_EBIT 0x50B ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF6_IBIT 0x50C ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF6_EBIT 0x50D ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF7_IBIT 0x50E ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF7_EBIT 0x50F ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF8_IBIT 0x510 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF8_EBIT 0x511 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF9_IBIT 0x512 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF9_EBIT 0x513 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF10_IBIT 0x514 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF10_EBIT 0x515 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF11_IBIT 0x516 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF11_EBIT 0x517 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF12_IBIT 0x518 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF12_EBIT 0x519 ++#define Q6PRM_LAPSS_CLK_ID_VA_QAIF_IF0_IBIT 0x550 ++#define Q6PRM_LAPSS_CLK_ID_VA_QAIF_IF0_EBIT 0x551 ++ + #define Q6PRM_LPASS_CLK_SRC_INTERNAL 1 + #define Q6PRM_LPASS_CLK_ROOT_DEFAULT 0 + #define Q6PRM_HW_CORE_ID_LPASS 1 + #define Q6PRM_HW_CORE_ID_DCODEC 2 ++#define Q6PRM_HW_LPR_VOTE 3 + + int q6prm_set_lpass_clock(struct device *dev, int clk_id, int clk_attr, + int clk_root, unsigned int freq); +diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c +--- a/sound/soc/qcom/qdsp6/q6routing.c ++++ b/sound/soc/qcom/qdsp6/q6routing.c +@@ -127,7 +127,14 @@ + { mix_name, "TX_CODEC_DMA_TX_2", "TX_CODEC_DMA_TX_2"}, \ + { mix_name, "TX_CODEC_DMA_TX_3", "TX_CODEC_DMA_TX_3"}, \ + { mix_name, "TX_CODEC_DMA_TX_4", "TX_CODEC_DMA_TX_4"}, \ +- { mix_name, "TX_CODEC_DMA_TX_5", "TX_CODEC_DMA_TX_5"} ++ { mix_name, "TX_CODEC_DMA_TX_5", "TX_CODEC_DMA_TX_5"}, \ ++ { mix_name, "LPI_MI2S_TX_0", "LPI_MI2S_TX_0" }, \ ++ { mix_name, "LPI_MI2S_TX_1", "LPI_MI2S_TX_1" }, \ ++ { mix_name, "LPI_MI2S_TX_2", "LPI_MI2S_TX_2" }, \ ++ { mix_name, "LPI_MI2S_TX_3", "LPI_MI2S_TX_3" }, \ ++ { mix_name, "LPI_MI2S_TX_4", "LPI_MI2S_TX_4" }, \ ++ { mix_name, "LPI_MI2S_TX_5", "LPI_MI2S_TX_5" }, \ ++ { mix_name, "LPI_MI2S_TX_6", "LPI_MI2S_TX_6" } + + #define Q6ROUTING_TX_MIXERS(id) \ + SOC_SINGLE_EXT("PRI_MI2S_TX", PRIMARY_MI2S_TX, \ +@@ -320,6 +327,27 @@ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TX_CODEC_DMA_TX_5", TX_CODEC_DMA_TX_5, \ ++ id, 1, 0, msm_routing_get_audio_mixer, \ ++ msm_routing_put_audio_mixer), \ ++ SOC_SINGLE_EXT("LPI_MI2S_TX_0", LPI_MI2S_TX_0, \ ++ id, 1, 0, msm_routing_get_audio_mixer, \ ++ msm_routing_put_audio_mixer), \ ++ SOC_SINGLE_EXT("LPI_MI2S_TX_1", LPI_MI2S_TX_1, \ ++ id, 1, 0, msm_routing_get_audio_mixer, \ ++ msm_routing_put_audio_mixer), \ ++ SOC_SINGLE_EXT("LPI_MI2S_TX_2", LPI_MI2S_TX_2, \ ++ id, 1, 0, msm_routing_get_audio_mixer, \ ++ msm_routing_put_audio_mixer), \ ++ SOC_SINGLE_EXT("LPI_MI2S_TX_3", LPI_MI2S_TX_3, \ ++ id, 1, 0, msm_routing_get_audio_mixer, \ ++ msm_routing_put_audio_mixer), \ ++ SOC_SINGLE_EXT("LPI_MI2S_TX_4", LPI_MI2S_TX_4, \ ++ id, 1, 0, msm_routing_get_audio_mixer, \ ++ msm_routing_put_audio_mixer), \ ++ SOC_SINGLE_EXT("LPI_MI2S_TX_5", LPI_MI2S_TX_5, \ ++ id, 1, 0, msm_routing_get_audio_mixer, \ ++ msm_routing_put_audio_mixer), \ ++ SOC_SINGLE_EXT("LPI_MI2S_TX_6", LPI_MI2S_TX_6, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), + +@@ -709,6 +737,26 @@ static const struct snd_kcontrol_new rxcodec_dma_rx_6_mixer_controls[] = { + static const struct snd_kcontrol_new rx_codec_dma_rx_7_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_7) }; + ++static const struct snd_kcontrol_new lpi_mi2s_rx_0_mixer_controls[] = { ++ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_0) }; ++ ++static const struct snd_kcontrol_new lpi_mi2s_rx_1_mixer_controls[] = { ++ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_1) }; ++ ++static const struct snd_kcontrol_new lpi_mi2s_rx_2_mixer_controls[] = { ++ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_2) }; ++ ++static const struct snd_kcontrol_new lpi_mi2s_rx_3_mixer_controls[] = { ++ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_3) }; ++ ++static const struct snd_kcontrol_new lpi_mi2s_rx_4_mixer_controls[] = { ++ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_4) }; ++ ++static const struct snd_kcontrol_new lpi_mi2s_rx_5_mixer_controls[] = { ++ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_5) }; ++ ++static const struct snd_kcontrol_new lpi_mi2s_rx_6_mixer_controls[] = { ++ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_6) }; + + static const struct snd_kcontrol_new mmul1_mixer_controls[] = { + Q6ROUTING_TX_MIXERS(MSM_FRONTEND_DAI_MULTIMEDIA1) }; +@@ -938,6 +986,27 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { + SND_SOC_DAPM_MIXER("USB_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + usb_rx_mixer_controls, + ARRAY_SIZE(usb_rx_mixer_controls)), ++ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, ++ lpi_mi2s_rx_0_mixer_controls, ++ ARRAY_SIZE(lpi_mi2s_rx_0_mixer_controls)), ++ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, ++ lpi_mi2s_rx_1_mixer_controls, ++ ARRAY_SIZE(lpi_mi2s_rx_1_mixer_controls)), ++ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0, ++ lpi_mi2s_rx_2_mixer_controls, ++ ARRAY_SIZE(lpi_mi2s_rx_2_mixer_controls)), ++ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0, ++ lpi_mi2s_rx_3_mixer_controls, ++ ARRAY_SIZE(lpi_mi2s_rx_3_mixer_controls)), ++ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0, ++ lpi_mi2s_rx_4_mixer_controls, ++ ARRAY_SIZE(lpi_mi2s_rx_4_mixer_controls)), ++ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_5 Audio Mixer", SND_SOC_NOPM, 0, 0, ++ lpi_mi2s_rx_5_mixer_controls, ++ ARRAY_SIZE(lpi_mi2s_rx_5_mixer_controls)), ++ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_6 Audio Mixer", SND_SOC_NOPM, 0, 0, ++ lpi_mi2s_rx_6_mixer_controls, ++ ARRAY_SIZE(lpi_mi2s_rx_6_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0, + mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia2 Mixer", SND_SOC_NOPM, 0, 0, +@@ -1031,6 +1100,13 @@ static const struct snd_soc_dapm_route intercon[] = { + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_6 Audio Mixer", "RX_CODEC_DMA_RX_6"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_7 Audio Mixer", "RX_CODEC_DMA_RX_7"), + Q6ROUTING_RX_DAPM_ROUTE("USB_RX Audio Mixer", "USB_RX"), ++ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_0 Audio Mixer", "LPI_MI2S_RX_0"), ++ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_1 Audio Mixer", "LPI_MI2S_RX_1"), ++ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_2 Audio Mixer", "LPI_MI2S_RX_2"), ++ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_3 Audio Mixer", "LPI_MI2S_RX_3"), ++ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_4 Audio Mixer", "LPI_MI2S_RX_4"), ++ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_5 Audio Mixer", "LPI_MI2S_RX_5"), ++ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_6 Audio Mixer", "LPI_MI2S_RX_6"), + Q6ROUTING_TX_DAPM_ROUTE("MultiMedia1 Mixer"), + Q6ROUTING_TX_DAPM_ROUTE("MultiMedia2 Mixer"), + Q6ROUTING_TX_DAPM_ROUTE("MultiMedia3 Mixer"), +diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c +--- a/sound/soc/qcom/qdsp6/topology.c ++++ b/sound/soc/qcom/qdsp6/topology.c +@@ -753,6 +753,103 @@ static int audioreach_widget_i2s_module_load(struct audioreach_module *mod, + return 0; + } + ++static int audioreach_widget_audio_if_module_load(struct audioreach_module *mod, ++ const struct snd_soc_tplg_vendor_array *mod_array) ++{ ++ const struct snd_soc_tplg_vendor_value_elem *mod_elem; ++ int tkn_count = 0; ++ u32 val; ++ ++ mod_elem = mod_array->value; ++ ++ while (tkn_count < le32_to_cpu(mod_array->num_elems)) { ++ val = le32_to_cpu(mod_elem->value); ++ switch (le32_to_cpu(mod_elem->token)) { ++ case AR_TKN_U32_MODULE_HW_IF_IDX: ++ mod->hw_interface_idx = val; ++ break; ++ case AR_TKN_U32_MODULE_FMT_DATA: ++ mod->data_format = val; ++ break; ++ case AR_TKN_U16_MODULE_SYNC_SRC: ++ if (val > U16_MAX) ++ return -EINVAL; ++ mod->sync_src = (u16)val; ++ break; ++ case AR_TKN_U16_MODULE_CTRL_DATA_OUT_ENABLE: ++ if (val > U16_MAX) ++ return -EINVAL; ++ mod->ctrl_data_out_enable = (u16)val; ++ break; ++ case AR_TKN_U32_MODULE_SLOT_MASK: ++ mod->slot_mask = val; ++ break; ++ case AR_TKN_U16_MODULE_NSLOTS_PER_FRAME: ++ if (val > U16_MAX) ++ return -EINVAL; ++ mod->nslots_per_frame = (u16)val; ++ break; ++ case AR_TKN_U16_MODULE_SLOT_WIDTH: ++ if (val > U16_MAX) ++ return -EINVAL; ++ mod->slot_width = (u16)val; ++ break; ++ case AR_TKN_U16_MODULE_INTF_MODE: ++ if (val > U16_MAX) ++ return -EINVAL; ++ mod->intf_mode = (u16)val; ++ break; ++ case AR_TKN_U16_MODULE_SYNC_MODE: ++ if (val > U16_MAX) ++ return -EINVAL; ++ mod->sync_mode = (u16)val; ++ break; ++ case AR_TKN_U16_MODULE_CTRL_INVERT_SYNC_PULSE: ++ if (val > U16_MAX) ++ return -EINVAL; ++ mod->ctrl_invert_sync_pulse = (u16)val; ++ break; ++ case AR_TKN_U16_MODULE_CTRL_SYNC_DATA_DELAY: ++ if (val > U16_MAX) ++ return -EINVAL; ++ mod->ctrl_sync_data_delay = (u16)val; ++ break; ++ case AR_TKN_U16_MODULE_QAIF_TYPE: ++ if (val > U16_MAX) ++ return -EINVAL; ++ mod->qaif_type = (u16)val; ++ break; ++ case AR_TKN_U32_MODULE_ACTIVE_LANE_MASK: ++ mod->active_lane_mask = val; ++ break; ++ case AR_TKN_U32_MODULE_FRAME_SYNC_RATE: ++ mod->frame_sync_rate = val; ++ break; ++ case AR_TKN_U16_MODULE_BIT_CLK_TYPE: ++ if (val > U16_MAX) ++ return -EINVAL; ++ mod->bit_clk_type = (u16)val; ++ break; ++ case AR_TKN_U8_MODULE_INV_INT_BIT_CLK: ++ if (val > U8_MAX) ++ return -EINVAL; ++ mod->inv_int_bit_clk = (u8)val; ++ break; ++ case AR_TKN_U8_MODULE_INV_EXT_BIT_CLK: ++ if (val > U8_MAX) ++ return -EINVAL; ++ mod->inv_ext_bit_clk = (u8)val; ++ break; ++ default: ++ break; ++ } ++ tkn_count++; ++ mod_elem++; ++ } ++ ++ return 0; ++} ++ + static int audioreach_widget_dp_module_load(struct audioreach_module *mod, + const struct snd_soc_tplg_vendor_array *mod_array) + { +@@ -806,6 +903,12 @@ static int audioreach_widget_load_buffer(struct snd_soc_component *component, + case MODULE_ID_I2S_SOURCE: + audioreach_widget_i2s_module_load(mod, mod_array); + break; ++ case MODULE_ID_AUDIO_IF_SINK: ++ case MODULE_ID_AUDIO_IF_SOURCE: ++ ret = audioreach_widget_audio_if_module_load(mod, mod_array); ++ if (ret) ++ return ret; ++ break; + case MODULE_ID_DISPLAY_PORT_SINK: + audioreach_widget_dp_module_load(mod, mod_array); + break; +@@ -1313,7 +1416,6 @@ int audioreach_tplg_init(struct snd_soc_component *component) + { + struct snd_soc_card *card = component->card; + struct device *dev = component->dev; +- const struct firmware *fw; + int ret; + + /* Inline with Qualcomm UCM configs and linux-firmware path */ +@@ -1323,6 +1425,7 @@ int audioreach_tplg_init(struct snd_soc_component *component) + if (!tplg_fw_name) + return -ENOMEM; + ++ const struct firmware *fw __free(firmware) = NULL; + ret = request_firmware(&fw, tplg_fw_name, dev); + if (ret < 0) { + dev_err(dev, "tplg firmware loading %s failed %d\n", tplg_fw_name, ret); +@@ -1335,8 +1438,6 @@ int audioreach_tplg_init(struct snd_soc_component *component) + dev_err(dev, "tplg component load failed: %d\n", ret); + } + +- release_firmware(fw); +- + return ret; + } + EXPORT_SYMBOL_GPL(audioreach_tplg_init); +diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c +--- a/sound/soc/qcom/sc8280xp.c ++++ b/sound/soc/qcom/sc8280xp.c +@@ -12,17 +12,157 @@ + #include + #include + #include "qdsp6/q6afe.h" ++#include "qdsp6/q6apm.h" ++#include "qdsp6/q6prm.h" ++#include "qdsp6/q6dsp-common.h" + #include "common.h" + #include "sdw.h" + ++#define I2S_MCLKFS 256 ++ ++#define I2S_MCLK_RATE(rate) \ ++ ((rate) * (I2S_MCLKFS)) ++#define I2S_BIT_RATE(rate, channels, format) \ ++ ((rate) * (channels) * (format)) ++ ++static struct snd_soc_dapm_widget sc8280xp_dapm_widgets[] = { ++ SND_SOC_DAPM_HP("Headphone Jack", NULL), ++ SND_SOC_DAPM_MIC("Mic Jack", NULL), ++ SND_SOC_DAPM_SPK("DP0 Jack", NULL), ++ SND_SOC_DAPM_SPK("DP1 Jack", NULL), ++ SND_SOC_DAPM_SPK("DP2 Jack", NULL), ++ SND_SOC_DAPM_SPK("DP3 Jack", NULL), ++ SND_SOC_DAPM_SPK("DP4 Jack", NULL), ++ SND_SOC_DAPM_SPK("DP5 Jack", NULL), ++ SND_SOC_DAPM_SPK("DP6 Jack", NULL), ++ SND_SOC_DAPM_SPK("DP7 Jack", NULL), ++}; ++ ++static const struct snd_kcontrol_new max98090_controls[] = { ++ SOC_DAPM_PIN_SWITCH("Headset Mic12"), ++ SOC_DAPM_PIN_SWITCH("Headphone"), ++ SOC_DAPM_PIN_SWITCH("Headset Mic56"), ++ SOC_DAPM_PIN_SWITCH("Speaker"), ++ SOC_DAPM_PIN_SWITCH("Receiver"), ++ SOC_DAPM_PIN_SWITCH("Int Mic"), ++}; ++ ++static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = { ++ SND_SOC_DAPM_HP("Headphone Jack", NULL), ++ SND_SOC_DAPM_MIC("Mic Jack", NULL), ++ SND_SOC_DAPM_HP("Headphone", NULL), ++ SND_SOC_DAPM_MIC("Headset Mic12", NULL), ++ SND_SOC_DAPM_MIC("Headset Mic56", NULL), ++ SND_SOC_DAPM_MIC("Int Mic", NULL), ++ SND_SOC_DAPM_SPK("Receiver", NULL), ++ SND_SOC_DAPM_SPK("Speaker", NULL), ++}; ++ ++struct qcom_snd_soc_common { ++ const char *driver_name; ++ const struct snd_soc_dapm_widget *dapm_widgets; ++ int num_dapm_widgets; ++ const struct snd_soc_dapm_route *dapm_routes; ++ int num_dapm_routes; ++ const struct snd_kcontrol_new *controls; ++ int num_controls; ++ unsigned int codec_dai_fmt; ++ bool codec_sysclk_set; ++ bool mi2s_mclk_enable; ++ bool mi2s_bclk_enable; ++ bool wcd_jack; ++ int (*snd_prepare)(struct snd_pcm_substream *substream); ++}; ++ + struct sc8280xp_snd_data { + bool stream_prepared[AFE_PORT_MAX]; + struct snd_soc_card *card; + struct snd_soc_jack jack; + struct snd_soc_jack dp_jack[8]; ++ const struct qcom_snd_soc_common *priv; + bool jack_setup; + }; + ++static inline int sc8280xp_get_mclk_freq(struct snd_pcm_hw_params *params) ++{ ++ int rate = params_rate(params); ++ ++ switch (rate) { ++ case 11025: ++ case 44100: ++ case 88200: ++ return I2S_MCLK_RATE(44100); ++ default: ++ break; ++ } ++ ++ return I2S_MCLK_RATE(rate); ++} ++ ++static inline int sc8280xp_get_bclk_freq(struct snd_pcm_hw_params *params) ++{ ++ return I2S_BIT_RATE(params_rate(params), ++ params_channels(params), ++ snd_pcm_format_width(params_format(params))); ++} ++ ++static int sc8280xp_tdm_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); ++ struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); ++ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); ++ struct snd_soc_dai *codec_dai; ++ struct qcom_snd_tdm_slot_cfg cpu_cfg; ++ struct qcom_snd_tdm_slot_cfg codec_cfg; ++ unsigned int bclk_freq; ++ int ret; ++ int i; ++ ++ ret = qcom_snd_get_dai_tdm_slots(rtd, &cpu_cfg, &codec_cfg); ++ if (ret) ++ return ret == -EINVAL ? 0 : ret; ++ ++ if (!cpu_cfg.slots) ++ return 0; ++ ++ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); ++ if (ret) ++ return ret; ++ ++ ret = qcom_snd_apply_dai_tdm_slots_cfg(rtd, &cpu_cfg, &codec_cfg); ++ if (ret) ++ return ret; ++ ++ bclk_freq = snd_soc_tdm_params_to_bclk(params, cpu_cfg.slot_width, cpu_cfg.slots, 1); ++ if (!bclk_freq) ++ return -EINVAL; ++ ++ if (data->priv->mi2s_bclk_enable) { ++ ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_MI2S_BCLK, bclk_freq, ++ SND_SOC_CLOCK_IN); ++ if (ret) { ++ dev_err(rtd->dev, "%s: failed to set cpu sysclk: %d\n", ++ __func__, ret); ++ return ret; ++ } ++ } ++ ++ if (data->priv->codec_sysclk_set) { ++ for_each_rtd_codec_dais(rtd, i, codec_dai) { ++ ret = snd_soc_dai_set_sysclk(codec_dai, 0, bclk_freq, ++ SND_SOC_CLOCK_IN); ++ if (ret) { ++ dev_err(rtd->dev, "%s: failed to set codec sysclk on %s: %d\n", ++ __func__, codec_dai->name, ret); ++ return ret; ++ } ++ } ++ } ++ ++ return 0; ++} ++ + static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) + { + struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); +@@ -32,10 +172,6 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) + int dp_pcm_id = 0; + + switch (cpu_dai->id) { +- case PRIMARY_MI2S_RX...QUATERNARY_MI2S_TX: +- case QUINARY_MI2S_RX...QUINARY_MI2S_TX: +- snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); +- break; + case WSA_CODEC_DMA_RX_0: + case WSA_CODEC_DMA_RX_1: + /* +@@ -64,7 +200,10 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) + if (dp_jack) + return qcom_snd_dp_jack_setup(rtd, dp_jack, dp_pcm_id); + +- return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); ++ if (data->priv->wcd_jack) ++ return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); ++ ++ return 0; + } + + static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, +@@ -96,12 +235,118 @@ static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + return 0; + } + ++static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); ++ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); ++ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); ++ struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); ++ int mclk_freq = sc8280xp_get_mclk_freq(params); ++ int bclk_freq = sc8280xp_get_bclk_freq(params); ++ int ret; ++ ++ switch (cpu_dai->id) { ++ case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: ++ case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: ++ case SENARY_MI2S_RX ... SENARY_MI2S_TX: ++ case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: ++ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); ++ if (ret && ret != -ENOTSUPP) ++ return ret; ++ ++ if (data->priv->codec_dai_fmt) { ++ ret = snd_soc_dai_set_fmt(codec_dai, ++ data->priv->codec_dai_fmt); ++ if (ret && ret != -ENOTSUPP) ++ return ret; ++ } ++ ++ if (data->priv->mi2s_mclk_enable) { ++ ret = snd_soc_dai_set_sysclk(cpu_dai, ++ LPAIF_MI2S_MCLK, mclk_freq, ++ SND_SOC_CLOCK_OUT); ++ if (ret) ++ return ret; ++ } ++ ++ if (data->priv->mi2s_bclk_enable) { ++ ret = snd_soc_dai_set_sysclk(cpu_dai, ++ LPAIF_MI2S_BCLK, bclk_freq, ++ SND_SOC_CLOCK_OUT); ++ if (ret) ++ return ret; ++ } ++ ++ if (data->priv->codec_sysclk_set) { ++ ret = snd_soc_dai_set_sysclk(codec_dai, ++ 0, mclk_freq, ++ SND_SOC_CLOCK_IN); ++ if (ret && ret != -ENOTSUPP) ++ return ret; ++ } ++ break; ++ case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: ++ return sc8280xp_tdm_hw_params(substream, params); ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ ++/* ++ * WSA and WSA2 are handled as a single interface with the ++ * following channels mask: ++ * __________________________________________________ ++ * | Bits | 3 | 2 | 1 | 0 | ++ * --------------------------------------------------- ++ * | Line | WSA2 Ch2 | WSA2 Ch1 | WSA Ch2 | WSA Ch1 | ++ * --------------------------------------------------- ++ * ++ * The Ayaneo Pocket S2 speakers are connected only to ++ * the WSA2 interface and the WSA interface is not enabled. ++ * ++ * Set the channel mapping on the WSA2 channels only. ++ */ ++static const unsigned int ayaneo_ps2_channels_mapping[] = { ++ 0, /* WSA Ch1 */ ++ 0, /* WSA Ch2 */ ++ PCM_CHANNEL_FL, /* WSA2 Ch1 */ ++ PCM_CHANNEL_FR /* WSA2 Ch2 */ ++}; ++ ++static int ayaneo_ps2_snd_prepare(struct snd_pcm_substream *substream) ++{ ++ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); ++ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); ++ unsigned int channels = substream->runtime->channels; ++ ++ if (cpu_dai->id != WSA_CODEC_DMA_RX_0) ++ return 0; ++ ++ if (channels != 2) ++ return -EINVAL; ++ ++ return snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, ++ ARRAY_SIZE(ayaneo_ps2_channels_mapping), ++ ayaneo_ps2_channels_mapping); ++} ++ + static int sc8280xp_snd_prepare(struct snd_pcm_substream *substream) + { + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + ++ if (data->priv->snd_prepare) { ++ int ret; ++ ++ ret = data->priv->snd_prepare(substream); ++ if (ret) ++ return ret; ++ } ++ + return qcom_snd_sdw_prepare(substream, &data->stream_prepared[cpu_dai->id]); + } + +@@ -117,6 +362,7 @@ static int sc8280xp_snd_hw_free(struct snd_pcm_substream *substream) + static const struct snd_soc_ops sc8280xp_be_ops = { + .startup = qcom_snd_sdw_startup, + .shutdown = qcom_snd_sdw_shutdown, ++ .hw_params = sc8280xp_snd_hw_params, + .hw_free = sc8280xp_snd_hw_free, + .prepare = sc8280xp_snd_prepare, + }; +@@ -145,38 +391,182 @@ static int sc8280xp_platform_probe(struct platform_device *pdev) + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; +- card->owner = THIS_MODULE; ++ + /* Allocate the private data */ + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + ++ data->priv = of_device_get_match_data(dev); ++ if (!data->priv) ++ return -ENODEV; ++ ++ card->owner = THIS_MODULE; + card->dev = dev; + dev_set_drvdata(dev, card); + snd_soc_card_set_drvdata(card, data); ++ card->dapm_widgets = data->priv->dapm_widgets; ++ card->num_dapm_widgets = data->priv->num_dapm_widgets; ++ card->dapm_routes = data->priv->dapm_routes; ++ card->num_dapm_routes = data->priv->num_dapm_routes; ++ card->controls = data->priv->controls; ++ card->num_controls = data->priv->num_controls; ++ + ret = qcom_snd_parse_of(card); + if (ret) + return ret; + +- card->driver_name = of_device_get_match_data(dev); ++ card->driver_name = data->priv->driver_name; + sc8280xp_add_be_ops(card); + return devm_snd_soc_register_card(dev, card); + } + ++static struct qcom_snd_soc_common ayaneo_ps2_priv_data = { ++ .driver_name = "ayaneo-ps2", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .snd_prepare = ayaneo_ps2_snd_prepare, ++ .wcd_jack = true, ++}; ++ ++static const struct qcom_snd_soc_common eliza_priv_data = { ++ .driver_name = "eliza", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .wcd_jack = true, ++}; ++ ++static const struct qcom_snd_soc_common hawi_priv_data = { ++ .driver_name = "hawi", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .codec_sysclk_set = true, ++ .mi2s_bclk_enable = true, ++ .wcd_jack = true, ++}; ++ ++static const struct qcom_snd_soc_common kaanapali_priv_data = { ++ .driver_name = "kaanapali", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .wcd_jack = true, ++}; ++ ++static const struct qcom_snd_soc_common qcs9100_priv_data = { ++ .driver_name = "sa8775p", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++}; ++ ++static const struct qcom_snd_soc_common qcs615_priv_data = { ++ .driver_name = "qcs615", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .codec_sysclk_set = true, ++}; ++ ++static const struct qcom_snd_soc_common qcm6490_priv_data = { ++ .driver_name = "qcm6490", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .wcd_jack = true, ++}; ++ ++static const struct qcom_snd_soc_common qcs6490_priv_data = { ++ .driver_name = "qcs6490", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .wcd_jack = true, ++}; ++ ++static const struct qcom_snd_soc_common qcs8275_priv_data = { ++ .driver_name = "qcs8300", ++ .dapm_widgets = max98090_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(max98090_dapm_widgets), ++ .controls = max98090_controls, ++ .num_controls = ARRAY_SIZE(max98090_controls), ++ .codec_sysclk_set = true, ++ .codec_dai_fmt = SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_BC_FC, ++}; ++ ++static const struct qcom_snd_soc_common sc8280xp_priv_data = { ++ .driver_name = "sc8280xp", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .wcd_jack = true, ++}; ++ ++static const struct qcom_snd_soc_common sm8450_priv_data = { ++ .driver_name = "sm8450", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .wcd_jack = true, ++ /* I2S Connected to HDMI */ ++ .mi2s_mclk_enable = true, ++ .mi2s_bclk_enable = true, ++ .codec_dai_fmt = SND_SOC_DAIFMT_BC_FC | ++ SND_SOC_DAIFMT_NB_NF | ++ SND_SOC_DAIFMT_I2S, ++}; ++ ++static const struct qcom_snd_soc_common sm8475_priv_data = { ++ .driver_name = "sm8475", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .wcd_jack = true, ++}; ++ ++static const struct qcom_snd_soc_common sm8550_priv_data = { ++ .driver_name = "sm8550", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .wcd_jack = true, ++ /* I2S Connected to HDMI */ ++ .mi2s_mclk_enable = true, ++ .mi2s_bclk_enable = true, ++ .codec_dai_fmt = SND_SOC_DAIFMT_BC_FC | ++ SND_SOC_DAIFMT_NB_NF | ++ SND_SOC_DAIFMT_I2S, ++}; ++ ++static const struct qcom_snd_soc_common sm8650_priv_data = { ++ .driver_name = "sm8650", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .wcd_jack = true, ++ /* I2S Connected to HDMI */ ++ .mi2s_mclk_enable = true, ++ .mi2s_bclk_enable = true, ++ .codec_dai_fmt = SND_SOC_DAIFMT_BC_FC | ++ SND_SOC_DAIFMT_NB_NF | ++ SND_SOC_DAIFMT_I2S, ++}; ++ ++static const struct qcom_snd_soc_common sm8750_priv_data = { ++ .driver_name = "sm8750", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .wcd_jack = true, ++}; ++ + static const struct of_device_id snd_sc8280xp_dt_match[] = { +- {.compatible = "qcom,eliza-sndcard", "eliza"}, +- {.compatible = "qcom,kaanapali-sndcard", "kaanapali"}, +- {.compatible = "qcom,qcm6490-idp-sndcard", "qcm6490"}, +- {.compatible = "qcom,qcs615-sndcard", "qcs615"}, +- {.compatible = "qcom,qcs6490-rb3gen2-sndcard", "qcs6490"}, +- {.compatible = "qcom,qcs8275-sndcard", "qcs8300"}, +- {.compatible = "qcom,qcs9075-sndcard", "sa8775p"}, +- {.compatible = "qcom,qcs9100-sndcard", "sa8775p"}, +- {.compatible = "qcom,sc8280xp-sndcard", "sc8280xp"}, +- {.compatible = "qcom,sm8450-sndcard", "sm8450"}, +- {.compatible = "qcom,sm8550-sndcard", "sm8550"}, +- {.compatible = "qcom,sm8650-sndcard", "sm8650"}, +- {.compatible = "qcom,sm8750-sndcard", "sm8750"}, ++ { .compatible = "ayaneo,pocket-s2-sndcard", .data = &ayaneo_ps2_priv_data }, ++ { .compatible = "qcom,eliza-sndcard", .data = &eliza_priv_data }, ++ { .compatible = "qcom,hawi-sndcard", .data = &hawi_priv_data }, ++ { .compatible = "qcom,kaanapali-sndcard", .data = &kaanapali_priv_data }, ++ { .compatible = "qcom,maili-sndcard", .data = &hawi_priv_data }, ++ { .compatible = "qcom,qcm6490-idp-sndcard", .data = &qcm6490_priv_data }, ++ { .compatible = "qcom,qcs615-sndcard", .data = &qcs615_priv_data }, ++ { .compatible = "qcom,qcs6490-rb3gen2-sndcard", .data = &qcs6490_priv_data }, ++ { .compatible = "qcom,qcs8275-sndcard", .data = &qcs8275_priv_data }, ++ { .compatible = "qcom,qcs9075-sndcard", .data = &qcs9100_priv_data }, ++ { .compatible = "qcom,qcs9100-sndcard", .data = &qcs9100_priv_data }, ++ { .compatible = "qcom,sc8280xp-sndcard", .data = &sc8280xp_priv_data }, ++ { .compatible = "qcom,sm8450-sndcard", .data = &sm8450_priv_data }, ++ { .compatible = "qcom,sm8475-sndcard", .data = &sm8475_priv_data }, ++ { .compatible = "qcom,sm8550-sndcard", .data = &sm8550_priv_data }, ++ { .compatible = "qcom,sm8650-sndcard", .data = &sm8650_priv_data }, ++ { .compatible = "qcom,sm8750-sndcard", .data = &sm8750_priv_data }, + {} + }; + +diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c +--- a/sound/soc/qcom/sdm845.c ++++ b/sound/soc/qcom/sdm845.c +@@ -36,7 +36,6 @@ struct sdm845_snd_data { + bool jack_setup; + bool slim_port_setup; + bool stream_prepared[AFE_PORT_MAX]; +- struct snd_soc_card *card; + uint32_t pri_mi2s_clk_count; + uint32_t sec_mi2s_clk_count; + uint32_t quat_tdm_clk_count; +@@ -564,7 +563,6 @@ static int sdm845_snd_platform_probe(struct platform_device *pdev) + if (ret) + return ret; + +- data->card = card; + snd_soc_card_set_drvdata(card, data); + + sdm845_add_ops(card); +diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c +--- a/sound/soc/qcom/sm8250.c ++++ b/sound/soc/qcom/sm8250.c +@@ -112,6 +112,22 @@ static int sm8250_snd_startup(struct snd_pcm_substream *substream) + snd_soc_dai_set_fmt(cpu_dai, fmt); + snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); + break; ++ case LPI_MI2S_RX_0: ++ codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S; ++ snd_soc_dai_set_sysclk(cpu_dai, ++ Q6AFE_LPASS_CLK_ID_INT0_MI2S_IBIT, ++ MI2S_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); ++ snd_soc_dai_set_fmt(cpu_dai, fmt); ++ snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); ++ break; ++ case LPI_MI2S_TX_3: ++ codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S; ++ snd_soc_dai_set_sysclk(cpu_dai, ++ Q6AFE_LPASS_CLK_ID_INT3_MI2S_IBIT, ++ MI2S_BCLK_RATE, SNDRV_PCM_STREAM_CAPTURE); ++ snd_soc_dai_set_fmt(cpu_dai, fmt); ++ snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); ++ break; + default: + break; + } +@@ -194,6 +210,7 @@ static const struct of_device_id snd_sm8250_dt_match[] = { + { .compatible = "qcom,qrb2210-sndcard", .data = "qcm2290" }, + { .compatible = "qcom,qrb4210-rb2-sndcard", .data = "sm4250" }, + { .compatible = "qcom,qrb5165-rb5-sndcard", .data = "sm8250" }, ++ { .compatible = "qcom,sdm660-sndcard", .data = "sdm660" }, + { .compatible = "qcom,sm8250-sndcard", .data = "sm8250" }, + {} + }; +diff --git a/sound/soc/qcom/storm.c b/sound/soc/qcom/storm.c +--- a/sound/soc/qcom/storm.c ++++ b/sound/soc/qcom/storm.c +@@ -64,21 +64,21 @@ static struct snd_soc_dai_link storm_dai_link = { + SND_SOC_DAILINK_REG(hifi), + }; + +-static int storm_parse_of(struct snd_soc_card *card) ++static int storm_parse_of(struct device *dev) + { +- struct snd_soc_dai_link *dai_link = card->dai_link; +- struct device_node *np = card->dev->of_node; ++ struct snd_soc_dai_link *dai_link = &storm_dai_link; ++ struct device_node *np = dev->of_node; + + dai_link->cpus->of_node = of_parse_phandle(np, "cpu", 0); + if (!dai_link->cpus->of_node) { +- dev_err(card->dev, "error getting cpu phandle\n"); ++ dev_err(dev, "error getting cpu phandle\n"); + return -EINVAL; + } + dai_link->platforms->of_node = dai_link->cpus->of_node; + + dai_link->codecs->of_node = of_parse_phandle(np, "codec", 0); + if (!dai_link->codecs->of_node) { +- dev_err(card->dev, "error getting codec phandle\n"); ++ dev_err(dev, "error getting codec phandle\n"); + return -EINVAL; + } + +@@ -106,7 +106,7 @@ static int storm_platform_probe(struct platform_device *pdev) + card->dai_link = &storm_dai_link; + card->num_links = 1; + +- ret = storm_parse_of(card); ++ ret = storm_parse_of(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "error resolving dai links: %d\n", ret); + return ret; +diff --git a/sound/soc/renesas/fsi.c b/sound/soc/renesas/fsi.c +--- a/sound/soc/renesas/fsi.c ++++ b/sound/soc/renesas/fsi.c +@@ -1979,8 +1979,8 @@ static const struct of_device_id fsi_of_match[] = { + MODULE_DEVICE_TABLE(of, fsi_of_match); + + static const struct platform_device_id fsi_id_table[] = { +- { "sh_fsi", (kernel_ulong_t)&fsi1_core }, +- {}, ++ /* an array with no valid entry prevents matching on driver name */ ++ { } + }; + MODULE_DEVICE_TABLE(platform, fsi_id_table); + +@@ -1992,7 +1992,7 @@ static int fsi_probe(struct platform_device *pdev) + const struct fsi_core *core; + struct fsi_priv *fsi; + struct resource *res; +- unsigned int irq; ++ int irq; + int ret; + + memset(&info, 0, sizeof(info)); +@@ -2007,12 +2007,15 @@ static int fsi_probe(struct platform_device *pdev) + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- irq = platform_get_irq(pdev, 0); +- if (!res || (int)irq <= 0) { ++ if (!res) { + dev_err(&pdev->dev, "Not enough FSI platform resources.\n"); + return -ENODEV; + } + ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) ++ return irq; ++ + master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL); + if (!master) + return -ENOMEM; +diff --git a/sound/soc/renesas/rcar/adg.c b/sound/soc/renesas/rcar/adg.c +--- a/sound/soc/renesas/rcar/adg.c ++++ b/sound/soc/renesas/rcar/adg.c +@@ -565,7 +565,7 @@ static struct clk *rsnd_adg_create_null_clk(struct rsnd_priv *priv, + struct clk *clk; + + clk = clk_register_fixed_rate(dev, name, parent, 0, 0); +- if (IS_ERR_OR_NULL(clk)) { ++ if (IS_ERR(clk)) { + dev_err(dev, "create null clk error\n"); + return ERR_CAST(clk); + } +@@ -618,7 +618,7 @@ static int rsnd_adg_get_clkin(struct rsnd_priv *priv) + * No "adg" is not error + */ + clk = devm_clk_get(dev, "adg"); +- if (IS_ERR_OR_NULL(clk)) ++ if (IS_ERR(clk)) + clk = rsnd_adg_null_clk_get(priv); + adg->adg = clk; + +@@ -626,9 +626,9 @@ static int rsnd_adg_get_clkin(struct rsnd_priv *priv) + for (i = 0; i < clkin_size; i++) { + clk = devm_clk_get(dev, clkin_name[i]); + +- if (IS_ERR_OR_NULL(clk)) ++ if (IS_ERR(clk)) + clk = rsnd_adg_null_clk_get(priv); +- if (IS_ERR_OR_NULL(clk)) ++ if (IS_ERR(clk)) + goto err; + + adg->clkin[i] = clk; +diff --git a/sound/soc/renesas/siu_dai.c b/sound/soc/renesas/siu_dai.c +--- a/sound/soc/renesas/siu_dai.c ++++ b/sound/soc/renesas/siu_dai.c +@@ -715,7 +715,6 @@ static struct snd_soc_dai_driver siu_i2s_dai = { + + static int siu_probe(struct platform_device *pdev) + { +- const struct firmware *fw_entry; + struct resource *res, *region; + struct siu_info *info; + int ret; +@@ -726,6 +725,7 @@ static int siu_probe(struct platform_device *pdev) + siu_i2s_data = info; + info->dev = &pdev->dev; + ++ const struct firmware *fw_entry __free(firmware) = NULL; + ret = request_firmware(&fw_entry, "siu_spb.bin", &pdev->dev); + if (ret) + return ret; +@@ -736,8 +736,6 @@ static int siu_probe(struct platform_device *pdev) + */ + memcpy(&info->fw, fw_entry->data, fw_entry->size); + +- release_firmware(fw_entry); +- + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; +diff --git a/sound/soc/rockchip/rk3288_hdmi_analog.c b/sound/soc/rockchip/rk3288_hdmi_analog.c +--- a/sound/soc/rockchip/rk3288_hdmi_analog.c ++++ b/sound/soc/rockchip/rk3288_hdmi_analog.c +@@ -185,10 +185,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) + gpiod_set_consumer_name(machine->gpio_hp_en, "hp_en"); + + ret = snd_soc_of_parse_card_name(card, "rockchip,model"); +- if (ret) { +- dev_err(card->dev, "SoC parse card name failed %d\n", ret); ++ if (ret) + return ret; +- } + + rk_dailink.codecs[0].of_node = of_parse_phandle(np, + "rockchip,audio-codec", +@@ -207,10 +205,9 @@ static int snd_rk_mc_probe(struct platform_device *pdev) + } + + ret = snd_soc_get_dai_name(&args, &rk_dailink.codecs[0].dai_name); +- if (ret) { +- dev_err(&pdev->dev, "Unable to get codec_dai_name\n"); +- return ret; +- } ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, ++ "Unable to get codec_dai_name\n"); + + rk_dailink.cpus->of_node = of_parse_phandle(np, "rockchip,i2s-controller", + 0); +@@ -223,11 +220,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) + rk_dailink.platforms->of_node = rk_dailink.cpus->of_node; + + ret = snd_soc_of_parse_audio_routing(card, "rockchip,routing"); +- if (ret) { +- dev_err(&pdev->dev, +- "Unable to parse 'rockchip,routing' property\n"); ++ if (ret) + return ret; +- } + + snd_soc_card_set_drvdata(card, machine); + +diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c +--- a/sound/soc/rockchip/rockchip_i2s.c ++++ b/sound/soc/rockchip/rockchip_i2s.c +@@ -767,16 +767,14 @@ static int rockchip_i2s_probe(struct platform_device *pdev) + + /* try to prepare related clocks */ + i2s->hclk = devm_clk_get_enabled(&pdev->dev, "i2s_hclk"); +- if (IS_ERR(i2s->hclk)) { +- dev_err(&pdev->dev, "Can't retrieve i2s bus clock\n"); +- return PTR_ERR(i2s->hclk); +- } ++ if (IS_ERR(i2s->hclk)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->hclk), ++ "Can't retrieve i2s bus clock\n"); + + i2s->mclk = devm_clk_get(&pdev->dev, "i2s_clk"); +- if (IS_ERR(i2s->mclk)) { +- dev_err(&pdev->dev, "Can't retrieve i2s master clock\n"); +- return PTR_ERR(i2s->mclk); +- } ++ if (IS_ERR(i2s->mclk)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->mclk), ++ "Can't retrieve i2s master clock\n"); + + regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(regs)) +@@ -792,7 +790,11 @@ static int rockchip_i2s_probe(struct platform_device *pdev) + + i2s->bclk_ratio = 64; + i2s->pinctrl = devm_pinctrl_get(&pdev->dev); +- if (!IS_ERR(i2s->pinctrl)) { ++ if (IS_ERR(i2s->pinctrl)) { ++ if (PTR_ERR(i2s->pinctrl) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ dev_dbg(&pdev->dev, "failed to find i2s pinctrl\n"); ++ } else { + i2s->bclk_on = pinctrl_lookup_state(i2s->pinctrl, "bclk_on"); + if (!IS_ERR_OR_NULL(i2s->bclk_on)) { + i2s->bclk_off = pinctrl_lookup_state(i2s->pinctrl, "bclk_off"); +@@ -801,8 +803,6 @@ static int rockchip_i2s_probe(struct platform_device *pdev) + return -EINVAL; + } + } +- } else { +- dev_dbg(&pdev->dev, "failed to find i2s pinctrl\n"); + } + + i2s_pinctrl_select_bclk_off(i2s); +@@ -831,16 +831,12 @@ static int rockchip_i2s_probe(struct platform_device *pdev) + &rockchip_i2s_component, + dai, 1); + +- if (ret) { +- dev_err(&pdev->dev, "Could not register DAI\n"); ++ if (ret) + return ret; +- } + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); +- if (ret) { +- dev_err(&pdev->dev, "Could not register PCM\n"); ++ if (ret) + return ret; +- } + + return 0; + } +diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c +--- a/sound/soc/rockchip/rockchip_i2s_tdm.c ++++ b/sound/soc/rockchip/rockchip_i2s_tdm.c +@@ -1263,16 +1263,14 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) + i2s_tdm->tx_reset = devm_reset_control_get_optional_exclusive(&pdev->dev, + "tx-m"); + if (IS_ERR(i2s_tdm->tx_reset)) { +- ret = PTR_ERR(i2s_tdm->tx_reset); +- return dev_err_probe(i2s_tdm->dev, ret, ++ return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->tx_reset), + "Error in tx-m reset control\n"); + } + + i2s_tdm->rx_reset = devm_reset_control_get_optional_exclusive(&pdev->dev, + "rx-m"); + if (IS_ERR(i2s_tdm->rx_reset)) { +- ret = PTR_ERR(i2s_tdm->rx_reset); +- return dev_err_probe(i2s_tdm->dev, ret, ++ return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->rx_reset), + "Error in rx-m reset control\n"); + } + +@@ -1363,17 +1361,12 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) + ret = devm_snd_soc_register_component(&pdev->dev, + &rockchip_i2s_tdm_component, + i2s_tdm->dai, 1); +- +- if (ret) { +- dev_err(&pdev->dev, "Could not register DAI\n"); ++ if (ret) + goto err_suspend; +- } + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); +- if (ret) { +- dev_err(&pdev->dev, "Could not register PCM\n"); ++ if (ret) + goto err_suspend; +- } + + return 0; + +diff --git a/sound/soc/rockchip/rockchip_max98090.c b/sound/soc/rockchip/rockchip_max98090.c +--- a/sound/soc/rockchip/rockchip_max98090.c ++++ b/sound/soc/rockchip/rockchip_max98090.c +@@ -428,11 +428,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) + + /* Parse card name. */ + ret = snd_soc_of_parse_card_name(card, "rockchip,model"); +- if (ret) { +- dev_err(&pdev->dev, +- "Soc parse card name failed %d\n", ret); ++ if (ret) + return ret; +- } + + /* register the soc card */ + ret = devm_snd_soc_register_card(&pdev->dev, card); +diff --git a/sound/soc/rockchip/rockchip_pdm.c b/sound/soc/rockchip/rockchip_pdm.c +--- a/sound/soc/rockchip/rockchip_pdm.c ++++ b/sound/soc/rockchip/rockchip_pdm.c +@@ -630,10 +630,8 @@ static int rockchip_pdm_probe(struct platform_device *pdev) + &rockchip_pdm_component, + &rockchip_pdm_dai, 1); + +- if (ret) { +- dev_err(&pdev->dev, "could not register dai: %d\n", ret); ++ if (ret) + goto err_suspend; +- } + + rockchip_pdm_rxctrl(pdm, 0); + +@@ -642,10 +640,8 @@ static int rockchip_pdm_probe(struct platform_device *pdev) + goto err_suspend; + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); +- if (ret) { +- dev_err(&pdev->dev, "could not register pcm: %d\n", ret); ++ if (ret) + goto err_suspend; +- } + + return 0; + +diff --git a/sound/soc/rockchip/rockchip_rt5645.c b/sound/soc/rockchip/rockchip_rt5645.c +--- a/sound/soc/rockchip/rockchip_rt5645.c ++++ b/sound/soc/rockchip/rockchip_rt5645.c +@@ -191,11 +191,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) + rk_dailink.platforms->of_node = rk_dailink.cpus->of_node; + + ret = snd_soc_of_parse_card_name(card, "rockchip,model"); +- if (ret) { +- dev_err(&pdev->dev, +- "Soc parse card name failed %d\n", ret); ++ if (ret) + goto put_cpu_of_node; +- } + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) { +diff --git a/sound/soc/rockchip/rockchip_sai.c b/sound/soc/rockchip/rockchip_sai.c +--- a/sound/soc/rockchip/rockchip_sai.c ++++ b/sound/soc/rockchip/rockchip_sai.c +@@ -1428,12 +1428,13 @@ static int rockchip_sai_probe(struct platform_device *pdev) + "Failed to initialize regmap\n"); + + irq = platform_get_irq_optional(pdev, 0); ++ if (irq == -EPROBE_DEFER) ++ return irq; + if (irq > 0) { + ret = devm_request_irq(&pdev->dev, irq, rockchip_sai_isr, + IRQF_SHARED, node->name, sai); + if (ret) +- return dev_err_probe(&pdev->dev, ret, +- "Failed to request irq %d\n", irq); ++ return ret; + } else { + dev_dbg(&pdev->dev, "Asked for an IRQ but got %d\n", irq); + } +@@ -1456,7 +1457,7 @@ static int rockchip_sai_probe(struct platform_device *pdev) + + ret = rockchip_sai_parse_paths(sai, node); + if (ret) +- return dev_err_probe(&pdev->dev, ret, "Failed to parse paths\n"); ++ return ret; + + /* + * From here on, all register accesses need to be wrapped in +@@ -1471,18 +1472,14 @@ static int rockchip_sai_probe(struct platform_device *pdev) + return dev_err_probe(&pdev->dev, ret, "Failed to resume device\n"); + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); +- if (ret) { +- dev_err(&pdev->dev, "Failed to register PCM: %d\n", ret); ++ if (ret) + goto err_runtime_suspend; +- } + + ret = devm_snd_soc_register_component(&pdev->dev, + &rockchip_sai_component, + dai, 1); +- if (ret) { +- dev_err(&pdev->dev, "Failed to register component: %d\n", ret); ++ if (ret) + goto err_runtime_suspend; +- } + + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_put(&pdev->dev); +diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c +--- a/sound/soc/rockchip/rockchip_spdif.c ++++ b/sound/soc/rockchip/rockchip_spdif.c +@@ -396,13 +396,13 @@ static int rk_spdif_probe(struct platform_device *pdev) + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) +- return dev_err_probe(&pdev->dev, ret, "Could not register PCM\n"); ++ return ret; + + ret = devm_snd_soc_register_component(&pdev->dev, + &rk_spdif_component, + &rk_spdif_dai, 1); + if (ret) +- return dev_err_probe(&pdev->dev, ret, "Could not register DAI\n"); ++ return ret; + + return 0; + } +diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c +--- a/sound/soc/samsung/i2s.c ++++ b/sound/soc/samsung/i2s.c +@@ -1116,15 +1116,31 @@ static const struct snd_soc_dapm_widget samsung_i2s_widgets[] = { + + static const struct snd_soc_dapm_route samsung_i2s_dapm_routes[] = { + { "Playback Mixer", NULL, "Primary Playback" }, +- { "Playback Mixer", NULL, "Secondary Playback" }, +- + { "Mixer DAI TX", NULL, "Playback Mixer" }, + { "Primary Capture", NULL, "Mixer DAI RX" }, + }; + ++static const struct snd_soc_dapm_route samsung_i2s_dapm_routes_sec_play[] = { ++ { "Playback Mixer", NULL, "Secondary Playback" }, ++}; ++ ++static int samsung_i2s_component_probe(struct snd_soc_component *component) ++{ ++ struct samsung_i2s_priv *priv = snd_soc_component_get_drvdata(component); ++ ++ if (priv->quirks & QUIRK_SEC_DAI) ++ snd_soc_dapm_add_routes(snd_soc_component_to_dapm(component), ++ samsung_i2s_dapm_routes_sec_play, ++ ARRAY_SIZE(samsung_i2s_dapm_routes_sec_play)); ++ ++ return 0; ++} ++ + static const struct snd_soc_component_driver samsung_i2s_component = { + .name = "samsung-i2s", + ++ .probe = samsung_i2s_component_probe, ++ + .dapm_widgets = samsung_i2s_widgets, + .num_dapm_widgets = ARRAY_SIZE(samsung_i2s_widgets), + +@@ -1441,10 +1457,10 @@ static int samsung_i2s_probe(struct platform_device *pdev) + regs_base = res->start; + + priv->clk = devm_clk_get(&pdev->dev, "iis"); +- if (IS_ERR(priv->clk)) { +- dev_err(&pdev->dev, "Failed to get iis clock\n"); +- return PTR_ERR(priv->clk); +- } ++ if (IS_ERR(priv->clk)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk), ++ "Failed to get iis clock\n"); ++ + + ret = clk_prepare_enable(priv->clk); + if (ret != 0) { +@@ -1650,8 +1666,7 @@ static const struct samsung_i2s_dai_data i2sv6_dai_type __maybe_unused = { + }; + + static const struct samsung_i2s_dai_data i2sv7_dai_type __maybe_unused = { +- .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | +- QUIRK_SUPPORTS_TDM, ++ .quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR | QUIRK_SUPPORTS_TDM, + .pcm_rates = SNDRV_PCM_RATE_8000_192000, + .i2s_variant_regs = &i2sv7_regs, + }; +@@ -1675,7 +1690,7 @@ static const struct platform_device_id samsung_i2s_driver_ids[] = { + .name = "samsung-i2s", + .driver_data = (kernel_ulong_t)&i2sv3_dai_type, + }, +- {}, ++ { } + }; + MODULE_DEVICE_TABLE(platform, samsung_i2s_driver_ids); + +diff --git a/sound/soc/samsung/smdk_spdif.c b/sound/soc/samsung/smdk_spdif.c +--- a/sound/soc/samsung/smdk_spdif.c ++++ b/sound/soc/samsung/smdk_spdif.c +@@ -130,12 +130,8 @@ static int smdk_hw_params(struct snd_pcm_substream *substream, + return ret; + + /* Set S/PDIF uses internal source clock */ +- ret = snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK, +- rclk_rate, SND_SOC_CLOCK_IN); +- if (ret < 0) +- return ret; +- +- return ret; ++ return snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK, ++ rclk_rate, SND_SOC_CLOCK_IN); + } + + static const struct snd_soc_ops smdk_spdif_ops = { +diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c +--- a/sound/soc/samsung/spdif.c ++++ b/sound/soc/samsung/spdif.c +@@ -380,8 +380,8 @@ static int spdif_probe(struct platform_device *pdev) + + spdif->pclk = devm_clk_get(&pdev->dev, "spdif"); + if (IS_ERR(spdif->pclk)) { +- dev_err(&pdev->dev, "failed to get peri-clock\n"); +- ret = -ENOENT; ++ ret = dev_err_probe(&pdev->dev, PTR_ERR(spdif->pclk), ++ "failed to get peri-clock\n"); + goto err0; + } + ret = clk_prepare_enable(spdif->pclk); +@@ -390,8 +390,8 @@ static int spdif_probe(struct platform_device *pdev) + + spdif->sclk = devm_clk_get(&pdev->dev, "sclk_spdif"); + if (IS_ERR(spdif->sclk)) { +- dev_err(&pdev->dev, "failed to get internal source clock\n"); +- ret = -ENOENT; ++ ret = dev_err_probe(&pdev->dev, PTR_ERR(spdif->sclk), ++ "failed to get internal source clock\n"); + goto err1; + } + ret = clk_prepare_enable(spdif->sclk); +diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c +--- a/sound/soc/sdca/sdca_asoc.c ++++ b/sound/soc/sdca/sdca_asoc.c +@@ -364,7 +364,6 @@ static int entity_parse_ot(struct device *dev, + + /** + * sdca_asoc_pde_poll_actual_ps - Verify PDE power state reached target state +- * @dev: Pointer to the device for error logging. + * @regmap: Register map for reading ACTUAL_PS register. + * @function_id: SDCA function identifier. + * @entity_id: SDCA entity identifier for the power domain. +@@ -389,11 +388,11 @@ static int entity_parse_ot(struct device *dev, + * polling times out before reaching the target state, or a negative error code if + * a register read fails. + */ +-int sdca_asoc_pde_poll_actual_ps(struct device *dev, struct regmap *regmap, ++int sdca_asoc_pde_poll_actual_ps(struct regmap *regmap, + int function_id, int entity_id, +- int from_ps, int to_ps, +- const struct sdca_pde_delay *pde_delays, +- int num_delays) ++ int from_ps, int to_ps, ++ const struct sdca_pde_delay *pde_delays, ++ int num_delays) + { + static const int polls = 100; + static const int default_poll_us = 1000; +@@ -451,14 +450,14 @@ static int entity_pde_event(struct snd_soc_dapm_widget *widget, + return 0; + } + +- ret = sdca_asoc_pde_poll_actual_ps(component->dev, component->regmap, ++ ret = sdca_asoc_pde_poll_actual_ps(component->regmap, + SDW_SDCA_CTL_FUNC(widget->reg), + SDW_SDCA_CTL_ENT(widget->reg), + from, to, + entity->pde.max_delay, + entity->pde.num_max_delay); + if (ret) +- dev_err(component->dev, "%s: PDE transition %x -> %x failed, err=%d\n", ++ dev_err(component->dev, "%s: pde transition %x -> %x failed: %d\n", + entity->label, from, to, ret); + + return ret; +@@ -1231,7 +1230,7 @@ static u64 width_find_mask(unsigned int bits) + } + } + +-static int populate_rate_format(struct device *dev, ++int sdca_asoc_populate_rate_format(struct device *dev, + struct sdca_function_data *function, + struct sdca_entity *entity, + struct snd_soc_pcm_stream *stream) +@@ -1292,6 +1291,7 @@ static int populate_rate_format(struct device *dev, + + return 0; + } ++EXPORT_SYMBOL_NS(sdca_asoc_populate_rate_format, "SND_SOC_SDCA"); + + /** + * sdca_asoc_populate_dais - fill in an array of DAI drivers for a Function +@@ -1344,7 +1344,7 @@ int sdca_asoc_populate_dais(struct device *dev, struct sdca_function_data *funct + stream->channels_min = 1; + stream->channels_max = SDCA_MAX_CHANNEL_COUNT; + +- ret = populate_rate_format(dev, function, entity, stream); ++ ret = sdca_asoc_populate_rate_format(dev, function, entity, stream); + if (ret) + return ret; + +diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c +--- a/sound/soc/sdca/sdca_class_function.c ++++ b/sound/soc/sdca/sdca_class_function.c +@@ -329,7 +329,7 @@ static int class_function_probe(struct auxiliary_device *auxdev, + drv->core = core; + drv->function = &sdev->function; + +- ret = sdca_parse_function(dev, core->sdw, drv->function); ++ ret = sdca_parse_function(dev, drv->function); + if (ret) + return ret; + +diff --git a/sound/soc/sdca/sdca_fdl.c b/sound/soc/sdca/sdca_fdl.c +--- a/sound/soc/sdca/sdca_fdl.c ++++ b/sound/soc/sdca/sdca_fdl.c +@@ -195,7 +195,6 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, + { + struct device *dev = interrupt->dev; + struct sdca_fdl_data *fdl_data = &interrupt->function->fdl_data; +- const struct firmware *firmware = NULL; + struct acpi_sw_file *swf = NULL, *tmp; + struct sdca_fdl_file *fdl_file; + char *disk_filename; +@@ -230,6 +229,7 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, + + dev_dbg(dev, "FDL disk filename: %s\n", disk_filename); + ++ const struct firmware *firmware __free(firmware) = NULL; + ret = firmware_request_nowarn(&firmware, disk_filename, dev); + kfree(disk_filename); + if (ret) { +@@ -258,8 +258,7 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, + + if (!swf) { + dev_err(dev, "failed to locate SWF\n"); +- ret = -ENOENT; +- goto error; ++ return -ENOENT; + } + + dev_info(dev, "loading SWF: %x-%x-%x\n", +@@ -271,9 +270,6 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, + SDCA_CTL_XU_FDL_MESSAGEOFFSET, fdl_file->fdl_offset, + SDCA_CTL_XU_FDL_MESSAGELENGTH, swf->data, + swf->file_length - offsetof(struct acpi_sw_file, data)); +- +-error: +- release_firmware(firmware); + return ret; + } + +diff --git a/sound/soc/sdca/sdca_function_device.c b/sound/soc/sdca/sdca_function_device.c +--- a/sound/soc/sdca/sdca_function_device.c ++++ b/sound/soc/sdca/sdca_function_device.c +@@ -32,7 +32,8 @@ static void sdca_dev_release(struct device *dev) + + /* alloc, init and add link devices */ + static struct sdca_dev *sdca_dev_register(struct device *parent, +- struct sdca_function_desc *function_desc) ++ struct sdca_function_desc *function_desc, ++ struct acpi_table_swft *swft) + { + struct sdca_dev *sdev; + struct auxiliary_device *auxdev; +@@ -50,6 +51,7 @@ static struct sdca_dev *sdca_dev_register(struct device *parent, + auxdev->dev.release = sdca_dev_release; + + sdev->function.desc = function_desc; ++ sdev->function.fdl_data.swft = swft; + + rc = ida_alloc(&sdca_function_ida, GFP_KERNEL); + if (rc < 0) { +@@ -99,7 +101,8 @@ int sdca_dev_register_functions(struct sdw_slave *slave) + struct sdca_dev *func_dev; + + func_dev = sdca_dev_register(&slave->dev, +- &sdca_data->function[i]); ++ &sdca_data->function[i], ++ sdca_data->swft); + if (IS_ERR(func_dev)) { + ret = PTR_ERR(func_dev); + /* +diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c +--- a/sound/soc/sdca/sdca_functions.c ++++ b/sound/soc/sdca/sdca_functions.c +@@ -7,6 +7,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -18,7 +19,6 @@ + #include + #include + #include +-#include + + /* + * Should be long enough to encompass all the MIPI DisCo properties. +@@ -1365,90 +1365,76 @@ static int find_sdca_entity_ge(struct device *dev, + return -EINVAL; + } + +-static int +-find_sdca_entity_hide(struct device *dev, struct sdw_slave *sdw, +- struct fwnode_handle *function_node, +- struct fwnode_handle *entity_node, struct sdca_entity *entity) ++static int find_sdca_entity_hide(struct device *dev, ++ struct fwnode_handle *entity_node, ++ struct sdca_entity *entity) + { + struct sdca_entity_hide *hide = &entity->hide; +- unsigned int delay, *af_list = hide->af_number_list; +- int nval, ret; +- unsigned char *report_desc = NULL; ++ int num_reports, ret; ++ unsigned int delay; + + ret = fwnode_property_read_u32(entity_node, +- "mipi-sdca-RxUMP-ownership-transition-max-delay", &delay); ++ "mipi-sdca-RxUMP-ownership-transition-max-delay", ++ &delay); + if (!ret) + hide->max_delay = delay; + +- nval = fwnode_property_count_u32(entity_node, "mipi-sdca-HIDTx-supported-report-ids"); +- if (nval > 0) { +- hide->num_hidtx_ids = nval; ++ num_reports = fwnode_property_count_u32(entity_node, ++ "mipi-sdca-HIDTx-supported-report-ids"); ++ if (num_reports < 0 && num_reports != -EINVAL) { ++ dev_err(dev, "%pfwP: failed to read hid tx ids: %d\n", ++ entity_node, num_reports); ++ return num_reports; ++ } else if (num_reports > 0) { ++ hide->num_hidtx_ids = num_reports; + hide->hidtx_ids = devm_kcalloc(dev, hide->num_hidtx_ids, + sizeof(*hide->hidtx_ids), GFP_KERNEL); + if (!hide->hidtx_ids) + return -ENOMEM; + +- ret = fwnode_property_read_u32_array(entity_node, +- "mipi-sdca-HIDTx-supported-report-ids", +- hide->hidtx_ids, +- hide->num_hidtx_ids); +- if (ret < 0) +- return ret; ++ fwnode_property_read_u32_array(entity_node, ++ "mipi-sdca-HIDTx-supported-report-ids", ++ hide->hidtx_ids, hide->num_hidtx_ids); + } + +- nval = fwnode_property_count_u32(entity_node, "mipi-sdca-HIDRx-supported-report-ids"); +- if (nval > 0) { +- hide->num_hidrx_ids = nval; ++ num_reports = fwnode_property_count_u32(entity_node, ++ "mipi-sdca-HIDRx-supported-report-ids"); ++ if (num_reports < 0 && num_reports != -EINVAL) { ++ dev_err(dev, "%pfwP: failed to read hid rx ids: %d\n", ++ entity_node, num_reports); ++ return num_reports; ++ } else if (num_reports > 0) { ++ hide->num_hidrx_ids = num_reports; + hide->hidrx_ids = devm_kcalloc(dev, hide->num_hidrx_ids, + sizeof(*hide->hidrx_ids), GFP_KERNEL); + if (!hide->hidrx_ids) + return -ENOMEM; + +- ret = fwnode_property_read_u32_array(entity_node, +- "mipi-sdca-HIDRx-supported-report-ids", +- hide->hidrx_ids, +- hide->num_hidrx_ids); +- if (ret < 0) +- return ret; ++ fwnode_property_read_u32_array(entity_node, ++ "mipi-sdca-HIDRx-supported-report-ids", ++ hide->hidrx_ids, hide->num_hidrx_ids); + } + +- nval = fwnode_property_count_u32(entity_node, "mipi-sdca-hide-related-audio-function-list"); +- if (nval <= 0) { ++ /* ++ * FIXME: This should probably link to the actual sdca_function_data pointer, ++ * but updating to do so should probably wait until we have a user. ++ */ ++ num_reports = fwnode_property_count_u32(entity_node, ++ "mipi-sdca-hide-related-audio-function-list"); ++ if (num_reports <= 0) { + dev_err(dev, "%pfwP: audio function numbers list missing: %d\n", +- entity_node, nval); ++ entity_node, num_reports); + return -EINVAL; +- } else if (nval > SDCA_MAX_FUNCTION_COUNT) { +- dev_err(dev, "%pfwP: maximum number of audio function exceeded\n", entity_node); ++ } else if (num_reports > ARRAY_SIZE(hide->af_number_list)) { ++ dev_err(dev, "%pfwP: maximum number of audio function exceeded\n", ++ entity_node); + return -EINVAL; + } + +- hide->hide_reside_function_num = nval; ++ hide->hide_reside_function_num = num_reports; + fwnode_property_read_u32_array(entity_node, +- "mipi-sdca-hide-related-audio-function-list", af_list, nval); +- +- nval = fwnode_property_count_u8(function_node, "mipi-sdca-hid-descriptor"); +- if (nval) +- fwnode_property_read_u8_array(function_node, "mipi-sdca-hid-descriptor", +- (u8 *)&hide->hid_desc, nval); +- +- if (hide->hid_desc.bNumDescriptors) { +- nval = fwnode_property_count_u8(function_node, "mipi-sdca-report-descriptor"); +- if (nval) { +- report_desc = devm_kzalloc(dev, nval, GFP_KERNEL); +- if (!report_desc) +- return -ENOMEM; +- hide->hid_report_desc = report_desc; +- fwnode_property_read_u8_array(function_node, "mipi-sdca-report-descriptor", +- report_desc, nval); +- +- /* add HID device */ +- ret = sdca_add_hid_device(dev, sdw, entity); +- if (ret) { +- dev_err(dev, "%pfwP: failed to add HID device: %d\n", entity_node, ret); +- return ret; +- } +- } +- } ++ "mipi-sdca-hide-related-audio-function-list", ++ hide->af_number_list, num_reports); + + return 0; + } +@@ -1475,8 +1461,7 @@ static int find_sdca_entity_xu(struct device *dev, + return 0; + } + +-static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw, +- struct sdca_function_data *function, ++static int find_sdca_entity(struct device *dev, struct sdca_function_data *function, + struct fwnode_handle *function_node, + struct fwnode_handle *entity_node, + struct sdca_entity *entity) +@@ -1528,8 +1513,7 @@ static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw, + ret = find_sdca_entity_ge(dev, entity_node, entity); + break; + case SDCA_ENTITY_TYPE_HIDE: +- ret = find_sdca_entity_hide(dev, sdw, function_node, +- entity_node, entity); ++ ret = find_sdca_entity_hide(dev, entity_node, entity); + break; + default: + break; +@@ -1544,8 +1528,7 @@ static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw, + return 0; + } + +-static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw, +- struct fwnode_handle *function_node, ++static int find_sdca_entities(struct device *dev, struct fwnode_handle *function_node, + struct sdca_function_data *function) + { + struct sdca_entity *entities; +@@ -1596,7 +1579,7 @@ static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw, + return -EINVAL; + } + +- ret = find_sdca_entity(dev, sdw, function, function_node, ++ ret = find_sdca_entity(dev, function, function_node, + entity_node, &entities[i]); + fwnode_handle_put(entity_node); + if (ret) +@@ -1619,7 +1602,7 @@ static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw, + return 0; + } + +-static struct sdca_entity *find_sdca_entity_by_label(struct sdca_function_data *function, ++struct sdca_entity *sdca_find_entity_by_label(struct sdca_function_data *function, + const char *entity_label) + { + struct sdca_entity *entity = NULL; +@@ -1648,6 +1631,7 @@ static struct sdca_entity *find_sdca_entity_by_label(struct sdca_function_data * + + return NULL; + } ++EXPORT_SYMBOL_NS(sdca_find_entity_by_label, "SND_SOC_SDCA"); + + static struct sdca_entity *find_sdca_entity_by_id(struct sdca_function_data *function, + const int id) +@@ -1688,7 +1672,7 @@ static int find_sdca_entity_connection_iot(struct device *dev, + return ret; + } + +- clock_entity = find_sdca_entity_by_label(function, clock_label); ++ clock_entity = sdca_find_entity_by_label(function, clock_label); + if (!clock_entity) { + dev_err(dev, "%s: failed to find clock with label %s\n", + entity->label, clock_label); +@@ -1873,7 +1857,7 @@ static int find_sdca_entity_connection(struct device *dev, + return ret; + } + +- connected_entity = find_sdca_entity_by_label(function, connected_label); ++ connected_entity = sdca_find_entity_by_label(function, connected_label); + if (!connected_entity) { + dev_err(dev, "%s: failed to find entity with label %s\n", + entity->label, connected_label); +@@ -2088,8 +2072,7 @@ static int find_sdca_clusters(struct device *dev, + return 0; + } + +-static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, +- struct fwnode_handle *function_node, ++static int find_sdca_filesets(struct device *dev, struct fwnode_handle *function_node, + struct sdca_function_data *function) + { + static const int mult_fileset = 3; +@@ -2171,23 +2154,65 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, + set->files = files; + } + +- function->fdl_data.swft = sdw->sdca_data.swft; + function->fdl_data.num_sets = num_sets; + function->fdl_data.sets = sets; + + return 0; + } + ++static int find_sdca_hid(struct device *dev, struct fwnode_handle *function_node, ++ struct sdca_function_data *function) ++{ ++ int num_desc; ++ ++ num_desc = fwnode_property_count_u8(function_node, "mipi-sdca-hid-descriptor"); ++ if (!num_desc) { ++ return 0; ++ } else if (num_desc < 0) { ++ dev_err(dev, "%pfwP: failed to read hid descriptor: %d\n", ++ function_node, num_desc); ++ return num_desc; ++ } else if (num_desc > sizeof(function->hid.desc)) { ++ dev_err(dev, "%pfwP: hid descriptor too large: %d\n", ++ function_node, num_desc); ++ return -EINVAL; ++ } ++ ++ fwnode_property_read_u8_array(function_node, "mipi-sdca-hid-descriptor", ++ (u8 *)&function->hid.desc, num_desc); ++ ++ if (!function->hid.desc.bNumDescriptors) ++ return 0; ++ ++ num_desc = fwnode_property_count_u8(function_node, "mipi-sdca-report-descriptor"); ++ if (num_desc <= 0) { ++ dev_err(dev, "%pfwP: failed to read report descriptor: %d\n", ++ function_node, num_desc); ++ ++ if (!num_desc) ++ return -EINVAL; ++ ++ return num_desc; ++ } ++ ++ function->hid.report_desc = devm_kzalloc(dev, num_desc, GFP_KERNEL); ++ if (!function->hid.report_desc) ++ return -ENOMEM; ++ ++ fwnode_property_read_u8_array(function_node, "mipi-sdca-report-descriptor", ++ function->hid.report_desc, num_desc); ++ ++ return 0; ++} ++ + /** + * sdca_parse_function - parse ACPI DisCo for a Function + * @dev: Pointer to device against which function data will be allocated. +- * @sdw: SoundWire slave device to be processed. + * @function: Pointer to the Function information, to be populated. + * + * Return: Returns 0 for success. + */ +-int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, +- struct sdca_function_data *function) ++int sdca_parse_function(struct device *dev, struct sdca_function_data *function) + { + struct fwnode_handle *node = function->desc->node; + u32 tmp; +@@ -2213,7 +2238,7 @@ int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, + if (ret) + return ret; + +- ret = find_sdca_entities(dev, sdw, node, function); ++ ret = find_sdca_entities(dev, node, function); + if (ret) + return ret; + +@@ -2225,10 +2250,20 @@ int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, + if (ret < 0) + return ret; + +- ret = find_sdca_filesets(dev, sdw, node, function); ++ ret = find_sdca_filesets(dev, node, function); + if (ret) + return ret; + ++ switch (function->desc->type) { ++ case SDCA_FUNCTION_TYPE_HID: ++ ret = find_sdca_hid(dev, node, function); ++ if (ret) ++ return ret; ++ break; ++ default: ++ break; ++ } ++ + return 0; + } + EXPORT_SYMBOL_NS(sdca_parse_function, "SND_SOC_SDCA"); +diff --git a/sound/soc/sdca/sdca_hid.c b/sound/soc/sdca/sdca_hid.c +--- a/sound/soc/sdca/sdca_hid.c ++++ b/sound/soc/sdca/sdca_hid.c +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -23,18 +24,18 @@ + + static int sdwhid_parse(struct hid_device *hid) + { +- struct sdca_entity *entity = hid->driver_data; ++ struct sdca_function_data *function = hid->driver_data; + unsigned int rsize; + int ret; + +- rsize = le16_to_cpu(entity->hide.hid_desc.rpt_desc.wDescriptorLength); ++ rsize = le16_to_cpu(function->hid.desc.rpt_desc.wDescriptorLength); + + if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { + dev_err(&hid->dev, "invalid size of report descriptor (%u)\n", rsize); + return -EINVAL; + } + +- ret = hid_parse_report(hid, entity->hide.hid_report_desc, rsize); ++ ret = hid_parse_report(hid, function->hid.report_desc, rsize); + + if (!ret) + return 0; +@@ -85,10 +86,16 @@ static const struct hid_ll_driver sdw_hid_driver = { + .raw_request = sdwhid_raw_request, + }; + +-int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, +- struct sdca_entity *entity) ++/** ++ * sdca_add_hid_device - create a new SDCA HID device ++ * @interrupt: Pointer to the SDCA interrupt information structure. ++ * ++ * Return: Zero on success, and a negative error code on failure. ++ */ ++int sdca_add_hid_device(struct sdca_interrupt *interrupt) + { +- struct sdw_bus *bus = sdw->bus; ++ struct device *dev = interrupt->dev; ++ struct sdca_function_data *function = interrupt->function; + struct hid_device *hid; + int ret; + +@@ -100,16 +107,13 @@ int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, + + hid->dev.parent = dev; + hid->bus = BUS_SDW; +- hid->version = le16_to_cpu(entity->hide.hid_desc.bcdHID); ++ hid->version = le16_to_cpu(function->hid.desc.bcdHID); + +- snprintf(hid->name, sizeof(hid->name), +- "HID sdw:%01x:%01x:%04x:%04x:%02x", +- bus->controller_id, bus->link_id, sdw->id.mfg_id, +- sdw->id.part_id, sdw->id.class_id); ++ strscpy(hid->phys, dev_name(dev)); ++ snprintf(hid->name, sizeof(hid->name), "SDCA %s:%02x", ++ function->desc->name, function->desc->adr); + +- snprintf(hid->phys, sizeof(hid->phys), "%s", dev->bus->name); +- +- hid->driver_data = entity; ++ hid->driver_data = function; + + ret = hid_add_device(hid); + if (ret && ret != -ENODEV) { +@@ -118,12 +122,24 @@ int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, + return ret; + } + +- entity->hide.hid = hid; ++ interrupt->priv = hid; + + return 0; + } + EXPORT_SYMBOL_NS(sdca_add_hid_device, "SND_SOC_SDCA"); + ++/** ++ * sdca_destroy_hid_device - destroy the HID device ++ * @interrupt: Pointer to the SDCA interrupt information structure. ++ */ ++void sdca_destroy_hid_device(struct sdca_interrupt *interrupt) ++{ ++ struct hid_device *hid = interrupt->priv; ++ ++ hid_destroy_device(hid); ++} ++EXPORT_SYMBOL_NS(sdca_destroy_hid_device, "SND_SOC_SDCA"); ++ + /** + * sdca_hid_process_report - read a HID event from the device and report + * @interrupt: Pointer to the SDCA interrupt information structure. +@@ -133,7 +149,7 @@ EXPORT_SYMBOL_NS(sdca_add_hid_device, "SND_SOC_SDCA"); + int sdca_hid_process_report(struct sdca_interrupt *interrupt) + { + struct device *dev = interrupt->dev; +- struct hid_device *hid = interrupt->entity->hide.hid; ++ struct hid_device *hid = interrupt->priv; + void *val __free(kfree) = NULL; + int len, ret; + +diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c +--- a/sound/soc/sdca/sdca_interrupts.c ++++ b/sound/soc/sdca/sdca_interrupts.c +@@ -487,6 +487,12 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *regmap, + } + break; + case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER): ++ interrupt->free_priv = sdca_destroy_hid_device; ++ ++ ret = sdca_add_hid_device(interrupt); ++ if (ret) ++ return ret; ++ + interrupt->handler = hid_handler; + break; + default: +diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c +--- a/sound/soc/sdw_utils/soc_sdw_utils.c ++++ b/sound/soc/sdw_utils/soc_sdw_utils.c +@@ -834,6 +834,35 @@ struct asoc_sdw_codec_info codec_info_list[] = { + }, + .dai_num = 2, + }, ++ { ++ .vendor_id = 0x01fa, ++ .part_id = 0x3562, ++ .name_prefix = "AMP", ++ .is_amp = true, ++ .dais = { ++ { ++ .direction = {true, false}, ++ .dai_name = "cs35l56-sdw1", ++ .component_name = "cs35l56", ++ .dai_type = SOC_SDW_DAI_TYPE_AMP, ++ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, ++ .init = asoc_sdw_cs_amp_init, ++ .rtd_init = asoc_sdw_cs_spk_rtd_init, ++ .controls = generic_spk_controls, ++ .num_controls = ARRAY_SIZE(generic_spk_controls), ++ .widgets = generic_spk_widgets, ++ .num_widgets = ARRAY_SIZE(generic_spk_widgets), ++ }, ++ { ++ .direction = {false, true}, ++ .dai_name = "cs35l56-sdw1c", ++ .dai_type = SOC_SDW_DAI_TYPE_AMP, ++ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, ++ .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, ++ }, ++ }, ++ .dai_num = 2, ++ }, + { + .vendor_id = 0x01fa, + .part_id = 0x3563, +@@ -1789,9 +1818,7 @@ static int is_sdca_aux_dev_present(struct device *dev, + int adr_index) + { + struct sdw_slave *slave; +- struct device *sdw_dev; + const char *sdw_codec_name; +- int ret = 0; + int i; + + if (!aux_codec_name) +@@ -1801,7 +1828,8 @@ static int is_sdca_aux_dev_present(struct device *dev, + if (!sdw_codec_name) + return -ENOMEM; + +- sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); ++ struct device *sdw_dev __free(put_device) = ++ bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); + if (!sdw_dev) { + dev_err(dev, "codec %s not found\n", sdw_codec_name); + return -EINVAL; +@@ -1811,24 +1839,19 @@ static int is_sdca_aux_dev_present(struct device *dev, + + if (!slave->sdca_data.interface_revision) { + dev_warn(dev, "No SDCA properties, assuming aux '%s' present\n", aux_codec_name); +- ret = 1; +- goto put_dev; ++ return 1; + } + + for (i = 0; i < slave->sdca_data.num_functions; i++) { + const char *fname = slave->sdca_data.function[i].name; + +- if (fname && strstr(aux_codec_name, fname)) { +- ret = 1; +- goto put_dev; +- } ++ if (fname && strstr(aux_codec_name, fname)) ++ return 1; + } + + dev_dbg(dev, "SDCA function for aux '%s' NOT FOUND on slave, skipping\n", aux_codec_name); + +-put_dev: +- put_device(sdw_dev); +- return ret; ++ return 0; + } + + int asoc_sdw_count_sdw_endpoints(struct snd_soc_card *card, +@@ -1928,9 +1951,8 @@ static int is_sdca_endpoint_present(struct device *dev, + const struct snd_soc_acpi_endpoint *adr_end; + const struct asoc_sdw_dai_info *dai_info; + struct sdw_slave *slave; +- struct device *sdw_dev; + const char *sdw_codec_name; +- int ret, i; ++ int i; + + adr_end = &adr_dev->endpoints[end_index]; + dai_info = &codec_info->dais[adr_end->num]; +@@ -1939,7 +1961,8 @@ static int is_sdca_endpoint_present(struct device *dev, + if (!sdw_codec_name) + return -ENOMEM; + +- sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); ++ struct device *sdw_dev __free(put_device) = ++ bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); + if (!sdw_dev) { + dev_err(dev, "codec %s not found\n", sdw_codec_name); + return -EINVAL; +@@ -1950,8 +1973,7 @@ static int is_sdca_endpoint_present(struct device *dev, + /* Make sure BIOS provides SDCA properties */ + if (!slave->sdca_data.interface_revision) { + dev_warn(&slave->dev, "SDCA properties not found in the BIOS\n"); +- ret = 1; +- goto put_device; ++ return 1; + } + + for (i = 0; i < slave->sdca_data.num_functions; i++) { +@@ -1960,8 +1982,7 @@ static int is_sdca_endpoint_present(struct device *dev, + if (dai_type == dai_info->dai_type) { + dev_dbg(&slave->dev, "DAI type %d sdca function %s found\n", + dai_type, slave->sdca_data.function[i].name); +- ret = 1; +- goto put_device; ++ return 1; + } + } + +@@ -1969,11 +1990,7 @@ static int is_sdca_endpoint_present(struct device *dev, + "SDCA device function for DAI type %d not supported, skip endpoint\n", + dai_info->dai_type); + +- ret = 0; +- +-put_device: +- put_device(sdw_dev); +- return ret; ++ return 0; + } + + int asoc_sdw_parse_sdw_endpoints(struct device *dev, +diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c +--- a/sound/soc/soc-component.c ++++ b/sound/soc/soc-component.c +@@ -29,17 +29,42 @@ static inline int _soc_component_ret_reg_rw(struct snd_soc_component *component, + func, component->name, reg); + } + +-static inline int soc_component_field_shift(struct snd_soc_component *component, +- unsigned int mask) ++struct snd_soc_component *snd_soc_component_alloc(struct device *dev) + { +- if (!mask) { +- dev_err(component->dev, "ASoC: error field mask is zero for %s\n", +- component->name); +- return 0; +- } ++ struct snd_soc_component *component = devm_kzalloc(dev, sizeof(*component), GFP_KERNEL); + +- return (ffs(mask) - 1); ++ if (!component) ++ return NULL; ++ ++ component->dev = dev; ++ ++ return component; + } ++EXPORT_SYMBOL_GPL(snd_soc_component_alloc); ++ ++void snd_soc_component_set_name(struct snd_soc_component *component, const char *name) ++{ ++ component->name = name; ++} ++EXPORT_SYMBOL_GPL(snd_soc_component_set_name); ++ ++const char *snd_soc_component_name(struct snd_soc_component *component) ++{ ++ return component->name; ++} ++EXPORT_SYMBOL_GPL(snd_soc_component_name); ++ ++void snd_soc_component_set_priv(struct snd_soc_component *component, void *priv) ++{ ++ component->priv = priv; ++} ++EXPORT_SYMBOL_GPL(snd_soc_component_set_priv); ++ ++void *snd_soc_component_to_priv(struct snd_soc_component *component) ++{ ++ return component->priv; ++} ++EXPORT_SYMBOL_GPL(snd_soc_component_to_priv); + + /* + * We might want to check substream by using list. +@@ -820,6 +845,18 @@ int snd_soc_component_update_bits_async(struct snd_soc_component *component, + } + EXPORT_SYMBOL_GPL(snd_soc_component_update_bits_async); + ++static inline int soc_component_field_shift(struct snd_soc_component *component, ++ unsigned int mask) ++{ ++ if (!mask) { ++ dev_err(component->dev, "ASoC: error field mask is zero for %s\n", ++ component->name); ++ return 0; ++ } ++ ++ return (ffs(mask) - 1); ++} ++ + /** + * snd_soc_component_read_field() - Read register field value + * @component: Component to read from +diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c +--- a/sound/soc/soc-core.c ++++ b/sound/soc/soc-core.c +@@ -2707,10 +2707,11 @@ static void snd_soc_del_component_unlocked(struct snd_soc_component *component) + list_del(&component->list); + } + +-int snd_soc_component_initialize(struct snd_soc_component *component, +- const struct snd_soc_component_driver *driver, +- struct device *dev) ++static int soc_component_initialize(struct snd_soc_component *component, ++ const struct snd_soc_component_driver *driver) + { ++ struct device *dev = component->dev; ++ + component->dapm = snd_soc_dapm_alloc(dev); + if (!component->dapm) + return -ENOMEM; +@@ -2730,16 +2731,14 @@ int snd_soc_component_initialize(struct snd_soc_component *component, + } + } + +- component->dev = dev; + component->driver = driver; + + return 0; + } +-EXPORT_SYMBOL_GPL(snd_soc_component_initialize); + +-int snd_soc_add_component(struct snd_soc_component *component, +- struct snd_soc_dai_driver *dai_drv, +- int num_dai) ++static int soc_component_add(struct snd_soc_component *component, ++ struct snd_soc_dai_driver *dai_drv, ++ int num_dai) + { + struct snd_soc_card *card, *c; + int ret; +@@ -2778,27 +2777,36 @@ int snd_soc_add_component(struct snd_soc_component *component, + + return ret; + } +-EXPORT_SYMBOL_GPL(snd_soc_add_component); + +-int snd_soc_register_component(struct device *dev, ++int snd_soc_register_component_c(struct snd_soc_component *component, + const struct snd_soc_component_driver *component_driver, + struct snd_soc_dai_driver *dai_drv, + int num_dai) + { +- struct snd_soc_component *component; + int ret; + +- component = devm_kzalloc(dev, sizeof(*component), GFP_KERNEL); +- if (!component) +- return -ENOMEM; +- +- ret = snd_soc_component_initialize(component, component_driver, dev); ++ ret = soc_component_initialize(component, component_driver); + if (ret < 0) + return ret; + +- return snd_soc_add_component(component, dai_drv, num_dai); ++ return soc_component_add(component, dai_drv, num_dai); + } +-EXPORT_SYMBOL_GPL(snd_soc_register_component); ++EXPORT_SYMBOL_GPL(snd_soc_register_component_c); ++ ++int snd_soc_register_component_d(struct device *dev, ++ const struct snd_soc_component_driver *component_driver, ++ struct snd_soc_dai_driver *dai_drv, ++ int num_dai) ++{ ++ struct snd_soc_component *component; ++ ++ component = snd_soc_component_alloc(dev); ++ if (!component) ++ return -ENOMEM; ++ ++ return snd_soc_register_component_c(component, component_driver, dai_drv, num_dai); ++} ++EXPORT_SYMBOL_GPL(snd_soc_register_component_d); + + /** + * snd_soc_unregister_component_by_driver - Unregister component using a given driver +diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c +--- a/sound/soc/soc-dapm.c ++++ b/sound/soc/soc-dapm.c +@@ -1203,6 +1203,8 @@ static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w, + case snd_soc_dapm_pga: + case snd_soc_dapm_effect: + case snd_soc_dapm_out_drv: ++ case snd_soc_dapm_encoder: ++ case snd_soc_dapm_decoder: + wname_in_long_name = true; + kcname_in_long_name = true; + break; +@@ -2785,6 +2787,8 @@ static ssize_t dapm_widget_show_component(struct snd_soc_component *component, + case snd_soc_dapm_pga: + case snd_soc_dapm_effect: + case snd_soc_dapm_out_drv: ++ case snd_soc_dapm_encoder: ++ case snd_soc_dapm_decoder: + case snd_soc_dapm_mixer: + case snd_soc_dapm_mixer_named_ctl: + case snd_soc_dapm_supply: +@@ -3367,6 +3371,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) + case snd_soc_dapm_pga: + case snd_soc_dapm_effect: + case snd_soc_dapm_out_drv: ++ case snd_soc_dapm_encoder: ++ case snd_soc_dapm_decoder: + dapm_new_pga(w); + break; + case snd_soc_dapm_dai_link: +diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c +--- a/sound/soc/soc-generic-dmaengine-pcm.c ++++ b/sound/soc/soc-generic-dmaengine-pcm.c +@@ -3,6 +3,7 @@ + // Copyright (C) 2013, Analog Devices Inc. + // Author: Lars-Peter Clausen + ++#include + #include + #include + #include +@@ -77,7 +78,7 @@ static int dmaengine_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) + { +- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); ++ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); + struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); + struct dma_slave_config slave_config; + int ret; +@@ -99,7 +100,7 @@ dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component, + struct snd_pcm_substream *substream) + { + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); +- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); ++ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); + struct device *dma_dev = dmaengine_dma_dev(pcm, substream); + struct dma_chan *chan = pcm->chan[substream->stream]; + struct snd_dmaengine_dai_dma_data *dma_data; +@@ -148,7 +149,7 @@ dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component, + static int dmaengine_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) + { +- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); ++ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); + struct dma_chan *chan = pcm->chan[substream->stream]; + int ret; + +@@ -176,7 +177,7 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel( + struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream) + { +- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); ++ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); + struct snd_dmaengine_dai_dma_data *dma_data; + + if (rtd->dai_link->num_cpus > 1) { +@@ -219,7 +220,7 @@ static bool dmaengine_pcm_can_report_residue(struct device *dev, + static int dmaengine_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) + { +- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); ++ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); + const struct snd_dmaengine_pcm_config *config = pcm->config; + struct device *dev = component->dev; + size_t prealloc_buffer_size; +@@ -279,7 +280,7 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer( + struct snd_soc_component *component, + struct snd_pcm_substream *substream) + { +- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); ++ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); + + if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE) + return snd_dmaengine_pcm_pointer_no_residue(substream); +@@ -293,7 +294,7 @@ static int dmaengine_copy(struct snd_soc_component *component, + struct iov_iter *iter, unsigned long bytes) + { + struct snd_pcm_runtime *runtime = substream->runtime; +- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); ++ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); + int (*process)(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + unsigned long bytes) = pcm->config->process; +@@ -395,6 +396,27 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, + */ + if (PTR_ERR(chan) == -EPROBE_DEFER) + return -EPROBE_DEFER; ++ ++ bool has_fw_node = dev->of_node || is_acpi_device_node(dev->fwnode); ++ bool name_exists_in_fw = false; ++ ++ if (has_fw_node) ++ name_exists_in_fw = device_property_match_string(dev, ++ "dma-names", ++ name) >= 0; ++ ++ if (has_fw_node && name_exists_in_fw) ++ dev_warn(dev, "DTS/ACPI DMA channel '%s' request failed (%ld)\n", ++ name, PTR_ERR(chan)); ++ ++ if (has_fw_node && !name_exists_in_fw) ++ dev_warn(dev, "DTS/ACPI name '%s' not found, legacy failed (%ld)\n", ++ name, PTR_ERR(chan)); ++ ++ if (!has_fw_node) ++ dev_warn(dev, "Legacy DMA channel '%s' request failed (%ld)\n", ++ name, PTR_ERR(chan)); ++ + pcm->chan[i] = NULL; + } else { + pcm->chan[i] = chan; +@@ -406,6 +428,12 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, + if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) + pcm->chan[1] = pcm->chan[0]; + ++ if (!pcm->chan[0] && ++ !pcm->chan[1]) { ++ dev_err(dev, "no DMA channel found for either playback or capture\n"); ++ return -ENODEV; ++ } ++ + return 0; + } + +@@ -435,10 +463,15 @@ static const struct snd_dmaengine_pcm_config snd_dmaengine_pcm_default_config = + int snd_dmaengine_pcm_register(struct device *dev, + const struct snd_dmaengine_pcm_config *config, unsigned int flags) + { ++ struct snd_soc_component *component; + const struct snd_soc_component_driver *driver; + struct dmaengine_pcm *pcm; + int ret; + ++ component = snd_soc_component_alloc(dev); ++ if (!component) ++ return -ENOMEM; ++ + pcm = kzalloc_obj(*pcm); + if (!pcm) + return -ENOMEM; +@@ -449,7 +482,8 @@ int snd_dmaengine_pcm_register(struct device *dev, + pcm->flags = flags; + + if (config->name) +- pcm->component.name = config->name; ++ snd_soc_component_set_name(component, config->name); ++ snd_soc_component_set_priv(component, pcm); + + ret = dmaengine_pcm_request_chan_of(pcm, dev, config); + if (ret) +@@ -460,11 +494,7 @@ int snd_dmaengine_pcm_register(struct device *dev, + else + driver = &dmaengine_pcm_component; + +- ret = snd_soc_component_initialize(&pcm->component, driver, dev); +- if (ret) +- goto err_free_dma; +- +- ret = snd_soc_add_component(&pcm->component, NULL, 0); ++ ret = snd_soc_register_component(component, driver, NULL, 0); + if (ret) + goto err_free_dma; + +@@ -493,7 +523,7 @@ void snd_dmaengine_pcm_unregister(struct device *dev) + if (!component) + return; + +- pcm = soc_component_to_pcm(component); ++ pcm = snd_soc_component_to_priv(component); + + snd_soc_unregister_component_by_driver(dev, component->driver); + dmaengine_pcm_release_chan(pcm); +diff --git a/sound/soc/soc-topology-test.c b/sound/soc/soc-topology-test.c +--- a/sound/soc/soc-topology-test.c ++++ b/sound/soc/soc-topology-test.c +@@ -45,15 +45,13 @@ static void snd_soc_tplg_test_exit(struct kunit *test) + struct kunit_soc_component { + struct kunit *kunit; + int expect; /* what result we expect when loading topology */ +- struct snd_soc_component comp; + struct snd_soc_card card; + struct firmware fw; + }; + + static int d_probe(struct snd_soc_component *component) + { +- struct kunit_soc_component *kunit_comp = +- container_of(component, struct kunit_soc_component, comp); ++ struct kunit_soc_component *kunit_comp = snd_soc_component_to_priv(component); + int ret; + + ret = snd_soc_tplg_component_load(component, NULL, &kunit_comp->fw); +@@ -65,8 +63,7 @@ static int d_probe(struct snd_soc_component *component) + + static void d_remove(struct snd_soc_component *component) + { +- struct kunit_soc_component *kunit_comp = +- container_of(component, struct kunit_soc_component, comp); ++ struct kunit_soc_component *kunit_comp = snd_soc_component_to_priv(component); + int ret; + + ret = snd_soc_tplg_component_remove(component); +@@ -214,8 +211,7 @@ static struct tplg_tmpl_002 tplg_tmpl_with_pcm = { + */ + static int d_probe_null_comp(struct snd_soc_component *component) + { +- struct kunit_soc_component *kunit_comp = +- container_of(component, struct kunit_soc_component, comp); ++ struct kunit_soc_component *kunit_comp = snd_soc_component_to_priv(component); + int ret; + + /* instead of passing component pointer as first argument, pass NULL here */ +@@ -234,6 +230,7 @@ static const struct snd_soc_component_driver test_component_null_comp = { + static void snd_soc_tplg_test_load_with_null_comp(struct kunit *test) + { + struct kunit_soc_component *kunit_comp; ++ struct snd_soc_component *component; + int ret; + + /* prepare */ +@@ -249,15 +246,17 @@ static void snd_soc_tplg_test_load_with_null_comp(struct kunit *test) + kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); + kunit_comp->card.fully_routed = true; + ++ component = snd_soc_component_alloc(test_dev); ++ KUNIT_ASSERT_NOT_NULL(test, component); ++ ++ snd_soc_component_set_priv(component, kunit_comp); ++ + /* run test */ + ret = snd_soc_register_card(&kunit_comp->card); + if (ret != 0 && ret != -EPROBE_DEFER) + KUNIT_FAIL(test, "Failed to register card"); + +- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component_null_comp, test_dev); +- KUNIT_EXPECT_EQ(test, 0, ret); +- +- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); ++ ret = snd_soc_register_component(component, &test_component_null_comp, NULL, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* cleanup */ +@@ -276,6 +275,7 @@ static void snd_soc_tplg_test_load_with_null_comp(struct kunit *test) + static void snd_soc_tplg_test_load_with_null_ops(struct kunit *test) + { + struct kunit_soc_component *kunit_comp; ++ struct snd_soc_component *component; + int ret; + + /* prepare */ +@@ -291,15 +291,17 @@ static void snd_soc_tplg_test_load_with_null_ops(struct kunit *test) + kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); + kunit_comp->card.fully_routed = true; + ++ component = snd_soc_component_alloc(test_dev); ++ KUNIT_ASSERT_NOT_NULL(test, component); ++ ++ snd_soc_component_set_priv(component, kunit_comp); ++ + /* run test */ + ret = snd_soc_register_card(&kunit_comp->card); + if (ret != 0 && ret != -EPROBE_DEFER) + KUNIT_FAIL(test, "Failed to register card"); + +- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); +- KUNIT_EXPECT_EQ(test, 0, ret); +- +- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); ++ ret = snd_soc_register_component(component, &test_component, NULL, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* cleanup */ +@@ -318,8 +320,7 @@ static void snd_soc_tplg_test_load_with_null_ops(struct kunit *test) + */ + static int d_probe_null_fw(struct snd_soc_component *component) + { +- struct kunit_soc_component *kunit_comp = +- container_of(component, struct kunit_soc_component, comp); ++ struct kunit_soc_component *kunit_comp = snd_soc_component_to_priv(component); + int ret; + + /* instead of passing fw pointer as third argument, pass NULL here */ +@@ -338,6 +339,7 @@ static const struct snd_soc_component_driver test_component_null_fw = { + static void snd_soc_tplg_test_load_with_null_fw(struct kunit *test) + { + struct kunit_soc_component *kunit_comp; ++ struct snd_soc_component *component; + int ret; + + /* prepare */ +@@ -353,15 +355,17 @@ static void snd_soc_tplg_test_load_with_null_fw(struct kunit *test) + kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); + kunit_comp->card.fully_routed = true; + ++ component = snd_soc_component_alloc(test_dev); ++ KUNIT_ASSERT_NOT_NULL(test, component); ++ ++ snd_soc_component_set_priv(component, kunit_comp); ++ + /* run test */ + ret = snd_soc_register_card(&kunit_comp->card); + if (ret != 0 && ret != -EPROBE_DEFER) + KUNIT_FAIL(test, "Failed to register card"); + +- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component_null_fw, test_dev); +- KUNIT_EXPECT_EQ(test, 0, ret); +- +- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); ++ ret = snd_soc_register_component(component, &test_component_null_fw, NULL, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* cleanup */ +@@ -375,6 +379,7 @@ static void snd_soc_tplg_test_load_with_null_fw(struct kunit *test) + static void snd_soc_tplg_test_load_empty_tplg(struct kunit *test) + { + struct kunit_soc_component *kunit_comp; ++ struct snd_soc_component *component; + struct tplg_tmpl_001 *data; + int size; + int ret; +@@ -401,15 +406,17 @@ static void snd_soc_tplg_test_load_empty_tplg(struct kunit *test) + kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); + kunit_comp->card.fully_routed = true; + ++ component = snd_soc_component_alloc(test_dev); ++ KUNIT_ASSERT_NOT_NULL(test, component); ++ ++ snd_soc_component_set_priv(component, kunit_comp); ++ + /* run test */ + ret = snd_soc_register_card(&kunit_comp->card); + if (ret != 0 && ret != -EPROBE_DEFER) + KUNIT_FAIL(test, "Failed to register card"); + +- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); +- KUNIT_EXPECT_EQ(test, 0, ret); +- +- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); ++ ret = snd_soc_register_component(component, &test_component, NULL, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* cleanup */ +@@ -425,6 +432,7 @@ static void snd_soc_tplg_test_load_empty_tplg(struct kunit *test) + static void snd_soc_tplg_test_load_empty_tplg_bad_magic(struct kunit *test) + { + struct kunit_soc_component *kunit_comp; ++ struct snd_soc_component *component; + struct tplg_tmpl_001 *data; + int size; + int ret; +@@ -456,15 +464,17 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_magic(struct kunit *test) + kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); + kunit_comp->card.fully_routed = true; + ++ component = snd_soc_component_alloc(test_dev); ++ KUNIT_ASSERT_NOT_NULL(test, component); ++ ++ snd_soc_component_set_priv(component, kunit_comp); ++ + /* run test */ + ret = snd_soc_register_card(&kunit_comp->card); + if (ret != 0 && ret != -EPROBE_DEFER) + KUNIT_FAIL(test, "Failed to register card"); + +- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); +- KUNIT_EXPECT_EQ(test, 0, ret); +- +- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); ++ ret = snd_soc_register_component(component, &test_component, NULL, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* cleanup */ +@@ -480,6 +490,7 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_magic(struct kunit *test) + static void snd_soc_tplg_test_load_empty_tplg_bad_abi(struct kunit *test) + { + struct kunit_soc_component *kunit_comp; ++ struct snd_soc_component *component; + struct tplg_tmpl_001 *data; + int size; + int ret; +@@ -511,15 +522,17 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_abi(struct kunit *test) + kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); + kunit_comp->card.fully_routed = true; + ++ component = snd_soc_component_alloc(test_dev); ++ KUNIT_ASSERT_NOT_NULL(test, component); ++ ++ snd_soc_component_set_priv(component, kunit_comp); ++ + /* run test */ + ret = snd_soc_register_card(&kunit_comp->card); + if (ret != 0 && ret != -EPROBE_DEFER) + KUNIT_FAIL(test, "Failed to register card"); + +- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); +- KUNIT_EXPECT_EQ(test, 0, ret); +- +- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); ++ ret = snd_soc_register_component(component, &test_component, NULL, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* cleanup */ +@@ -535,6 +548,7 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_abi(struct kunit *test) + static void snd_soc_tplg_test_load_empty_tplg_bad_size(struct kunit *test) + { + struct kunit_soc_component *kunit_comp; ++ struct snd_soc_component *component; + struct tplg_tmpl_001 *data; + int size; + int ret; +@@ -566,15 +580,17 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_size(struct kunit *test) + kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); + kunit_comp->card.fully_routed = true; + ++ component = snd_soc_component_alloc(test_dev); ++ KUNIT_ASSERT_NOT_NULL(test, component); ++ ++ snd_soc_component_set_priv(component, kunit_comp); ++ + /* run test */ + ret = snd_soc_register_card(&kunit_comp->card); + if (ret != 0 && ret != -EPROBE_DEFER) + KUNIT_FAIL(test, "Failed to register card"); + +- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); +- KUNIT_EXPECT_EQ(test, 0, ret); +- +- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); ++ ret = snd_soc_register_component(component, &test_component, NULL, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* cleanup */ +@@ -590,6 +606,7 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_size(struct kunit *test) + static void snd_soc_tplg_test_load_empty_tplg_bad_payload_size(struct kunit *test) + { + struct kunit_soc_component *kunit_comp; ++ struct snd_soc_component *component; + struct tplg_tmpl_001 *data; + int size; + int ret; +@@ -622,15 +639,17 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_payload_size(struct kunit *tes + kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); + kunit_comp->card.fully_routed = true; + ++ component = snd_soc_component_alloc(test_dev); ++ KUNIT_ASSERT_NOT_NULL(test, component); ++ ++ snd_soc_component_set_priv(component, kunit_comp); ++ + /* run test */ + ret = snd_soc_register_card(&kunit_comp->card); + if (ret != 0 && ret != -EPROBE_DEFER) + KUNIT_FAIL(test, "Failed to register card"); + +- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); +- KUNIT_EXPECT_EQ(test, 0, ret); +- +- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); ++ ret = snd_soc_register_component(component, &test_component, NULL, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* cleanup */ +@@ -644,6 +663,7 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_payload_size(struct kunit *tes + static void snd_soc_tplg_test_load_pcm_tplg(struct kunit *test) + { + struct kunit_soc_component *kunit_comp; ++ struct snd_soc_component *component; + u8 *data; + int size; + int ret; +@@ -670,15 +690,17 @@ static void snd_soc_tplg_test_load_pcm_tplg(struct kunit *test) + kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); + kunit_comp->card.fully_routed = true; + ++ component = snd_soc_component_alloc(test_dev); ++ KUNIT_ASSERT_NOT_NULL(test, component); ++ ++ snd_soc_component_set_priv(component, kunit_comp); ++ + /* run test */ + ret = snd_soc_register_card(&kunit_comp->card); + if (ret != 0 && ret != -EPROBE_DEFER) + KUNIT_FAIL(test, "Failed to register card"); + +- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); +- KUNIT_EXPECT_EQ(test, 0, ret); +- +- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); ++ ret = snd_soc_register_component(component, &test_component, NULL, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + + snd_soc_unregister_component(test_dev); +@@ -693,6 +715,7 @@ static void snd_soc_tplg_test_load_pcm_tplg(struct kunit *test) + static void snd_soc_tplg_test_load_pcm_tplg_reload_comp(struct kunit *test) + { + struct kunit_soc_component *kunit_comp; ++ struct snd_soc_component *component; + u8 *data; + int size; + int ret; +@@ -720,16 +743,19 @@ static void snd_soc_tplg_test_load_pcm_tplg_reload_comp(struct kunit *test) + kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); + kunit_comp->card.fully_routed = true; + ++ component = snd_soc_component_alloc(test_dev); ++ KUNIT_ASSERT_NOT_NULL(test, component); ++ ++ snd_soc_component_set_priv(component, kunit_comp); ++ + /* run test */ + ret = snd_soc_register_card(&kunit_comp->card); + if (ret != 0 && ret != -EPROBE_DEFER) + KUNIT_FAIL(test, "Failed to register card"); + + for (i = 0; i < 100; i++) { +- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); +- KUNIT_EXPECT_EQ(test, 0, ret); + +- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); ++ ret = snd_soc_register_component(component, &test_component, NULL, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + + snd_soc_unregister_component(test_dev); +@@ -745,6 +771,7 @@ static void snd_soc_tplg_test_load_pcm_tplg_reload_comp(struct kunit *test) + static void snd_soc_tplg_test_load_pcm_tplg_reload_card(struct kunit *test) + { + struct kunit_soc_component *kunit_comp; ++ struct snd_soc_component *component; + u8 *data; + int size; + int ret; +@@ -772,11 +799,13 @@ static void snd_soc_tplg_test_load_pcm_tplg_reload_card(struct kunit *test) + kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); + kunit_comp->card.fully_routed = true; + +- /* run test */ +- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); +- KUNIT_EXPECT_EQ(test, 0, ret); ++ component = snd_soc_component_alloc(test_dev); ++ KUNIT_ASSERT_NOT_NULL(test, component); + +- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); ++ snd_soc_component_set_priv(component, kunit_comp); ++ ++ /* run test */ ++ ret = snd_soc_register_component(component, &test_component, NULL, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + + for (i = 0; i < 100; i++) { +diff --git a/sound/soc/sof/amd/Kconfig b/sound/soc/sof/amd/Kconfig +--- a/sound/soc/sof/amd/Kconfig ++++ b/sound/soc/sof/amd/Kconfig +@@ -104,4 +104,15 @@ config SND_SOC_SOF_AMD_ACP70 + AMD ACP7.0/ACP7.1 version based platforms. + Say Y if you want to enable SOF on ACP7.0/ACP7.1 based platforms. + ++config SND_SOC_SOF_AMD_ACP7X ++ tristate "SOF support for ACP7.B/7.F platforms" ++ depends on SND_SOC_SOF_PCI ++ depends on AMD_NODE ++ select SND_SOC_SOF_AMD_COMMON ++ help ++ Select this option for SOF support on AMD ACP7.B and ACP7.F PCI ++ revision based platforms. ++ Say Y if you want to enable SOF on ACP7.B/7.F based platforms. ++ If unsure select "N". ++ + endif +diff --git a/sound/soc/sof/amd/Makefile b/sound/soc/sof/amd/Makefile +--- a/sound/soc/sof/amd/Makefile ++++ b/sound/soc/sof/amd/Makefile +@@ -11,6 +11,7 @@ snd-sof-amd-rembrandt-y := pci-rmb.o rembrandt.o + snd-sof-amd-vangogh-y := pci-vangogh.o vangogh.o + snd-sof-amd-acp63-y := pci-acp63.o acp63.o + snd-sof-amd-acp70-y := pci-acp70.o acp70.o ++snd-sof-amd-acp7x-y := pci-acp7x.o acp7x.o + + obj-$(CONFIG_SND_SOC_SOF_AMD_COMMON) += snd-sof-amd-acp.o + obj-$(CONFIG_SND_SOC_SOF_AMD_RENOIR) += snd-sof-amd-renoir.o +@@ -18,3 +19,4 @@ obj-$(CONFIG_SND_SOC_SOF_AMD_REMBRANDT) += snd-sof-amd-rembrandt.o + obj-$(CONFIG_SND_SOC_SOF_AMD_VANGOGH) += snd-sof-amd-vangogh.o + obj-$(CONFIG_SND_SOC_SOF_AMD_ACP63) += snd-sof-amd-acp63.o + obj-$(CONFIG_SND_SOC_SOF_AMD_ACP70) += snd-sof-amd-acp70.o ++obj-$(CONFIG_SND_SOC_SOF_AMD_ACP7X) += snd-sof-amd-acp7x.o +diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h +--- a/sound/soc/sof/amd/acp-dsp-offset.h ++++ b/sound/soc/sof/amd/acp-dsp-offset.h +@@ -68,12 +68,14 @@ + #define ACP5X_PGFSM_BASE 0x1424 + #define ACP6X_PGFSM_BASE 0x1024 + #define ACP70_PGFSM_BASE ACP6X_PGFSM_BASE ++#define ACP7X_PGFSM_BASE ACP6X_PGFSM_BASE + #define PGFSM_CONTROL_OFFSET 0x0 + #define PGFSM_STATUS_OFFSET 0x4 + #define ACP3X_CLKMUX_SEL 0x1424 + #define ACP5X_CLKMUX_SEL 0x142C + #define ACP6X_CLKMUX_SEL 0x102C + #define ACP70_CLKMUX_SEL ACP6X_CLKMUX_SEL ++#define ACP7X_CLKMUX_SEL ACP6X_CLKMUX_SEL + + /* Registers from ACP_INTR block */ + #define ACP3X_EXT_INTR_STAT 0x1808 +@@ -86,21 +88,31 @@ + #define ACP70_EXTERNAL_INTR_CNTL ACP6X_EXTERNAL_INTR_CNTL + #define ACP70_EXT_INTR_STAT ACP6X_EXT_INTR_STAT + #define ACP70_EXT_INTR_STAT1 ACP6X_EXT_INTR_STAT1 ++#define ACP7X_EXTERNAL_INTR_ENB ACP6X_EXTERNAL_INTR_ENB ++#define ACP7X_EXTERNAL_INTR_CNTL 0x1A04 ++#define ACP7X_EXT_INTR_STAT 0x1A1C ++#define ACP7X_EXTERNAL_INTR_CNTL1 0x1A08 ++#define ACP7X_EXT_INTR_STAT1 0x1A20 + + #define ACP3X_DSP_SW_INTR_BASE 0x1814 + #define ACP5X_DSP_SW_INTR_BASE 0x1814 + #define ACP6X_DSP_SW_INTR_BASE 0x1808 + #define ACP70_DSP_SW_INTR_BASE ACP6X_DSP_SW_INTR_BASE ++#define ACP7X_DSP_SW_INTR_BASE 0x1860 + #define DSP_SW_INTR_CNTL_OFFSET 0x0 + #define DSP_SW_INTR_STAT_OFFSET 0x4 ++#define ACP7X_DSP_SW_INTR_STAT (ACP7X_DSP_SW_INTR_BASE + DSP_SW_INTR_STAT_OFFSET) + #define DSP_SW_INTR_TRIG_OFFSET 0x8 ++#define ACP7X_DSP_SW_INTR_TRIG_OFFSET 0x30 + #define ACP3X_ERROR_STATUS 0x18C4 + #define ACP6X_ERROR_STATUS 0x1A4C + #define ACP70_ERROR_STATUS ACP6X_ERROR_STATUS ++#define ACP7X_ERROR_STATUS 0x1A88 + #define ACP3X_AXI2DAGB_SEM_0 0x1880 + #define ACP5X_AXI2DAGB_SEM_0 0x1884 + #define ACP6X_AXI2DAGB_SEM_0 0x1874 + #define ACP70_AXI2DAGB_SEM_0 ACP6X_AXI2DAGB_SEM_0 ++#define ACP7X_AXI2DAGB_SEM_0 0x18F4 + + /* ACP common registers to report errors related to I2S & SoundWire interfaces */ + #define ACP3X_SW_I2S_ERROR_REASON 0x18C8 +@@ -123,6 +135,7 @@ + #define ACP_SCRATCH_REG_0 0x10000 + #define ACP6X_DSP_FUSION_RUNSTALL 0x0644 + #define ACP70_DSP_FUSION_RUNSTALL ACP6X_DSP_FUSION_RUNSTALL ++#define ACP7X_DSP_FUSION_RUNSTALL ACP6X_DSP_FUSION_RUNSTALL + + /* Cache window registers */ + #define ACP_DSP0_CACHE_OFFSET0 0x0420 +@@ -139,5 +152,9 @@ + #define ACP70_SDW1_HOST_WAKE_STAT BIT(25) + #define ACP70_SDW0_PME_STAT BIT(26) + #define ACP70_SDW1_PME_STAT BIT(27) ++#define ACP7X_DSP0_IDMA_ERROR_MASK 0x4B0 ++#define ACP7X_IDMA_ERROR_MASK 0x1FF9FF ++#define ACP7X_ZSC_DSP_CTRL 0x001014 ++#define ACP7X_PME_EN ACP70_PME_EN + + #endif +diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c +--- a/sound/soc/sof/amd/acp-ipc.c ++++ b/sound/soc/sof/amd/acp-ipc.c +@@ -32,11 +32,20 @@ static void acpbus_trigger_host_to_dsp_swintr(struct acp_dev_data *adata) + struct snd_sof_dev *sdev = adata->dev; + const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); + u32 swintr_trigger; ++ unsigned int swintr_trigger_reg_offset; + ++ switch (adata->pci_rev) { ++ case ACP7B_PCI_ID: ++ case ACP7F_PCI_ID: ++ swintr_trigger_reg_offset = ACP7X_DSP_SW_INTR_TRIG_OFFSET; ++ break; ++ default: ++ swintr_trigger_reg_offset = DSP_SW_INTR_TRIG_OFFSET; ++ } + swintr_trigger = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->dsp_intr_base + +- DSP_SW_INTR_TRIG_OFFSET); ++ swintr_trigger_reg_offset); + swintr_trigger |= 0x01; +- snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->dsp_intr_base + DSP_SW_INTR_TRIG_OFFSET, ++ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->dsp_intr_base + swintr_trigger_reg_offset, + swintr_trigger); + } + +diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c +--- a/sound/soc/sof/amd/acp-loader.c ++++ b/sound/soc/sof/amd/acp-loader.c +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + + #include "../ops.h" + #include "acp-dsp-offset.h" +@@ -173,10 +174,31 @@ int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev) + + adata = sdev->pdata->hw_pdata; + +- if (adata->quirks && adata->quirks->signed_fw_image) ++ if (adata->pci_rev >= ACP7B_PCI_ID) { ++ if (adata->acp_sof_signed_firmware_image) { ++ if (adata->fw_bin_size <= ACP_IMAGE_HEADER_SIZE) { ++ dev_err(sdev->dev, "Invalid signed firmware size %u\n", ++ adata->fw_bin_size); ++ return -EINVAL; ++ } ++ size_fw = get_unaligned_le32(adata->bin_buf + ++ ACP_IMAGE_HDR_SIZE_FW_SIGNED_OFF); ++ if (!size_fw || ++ size_fw > adata->fw_bin_size - ACP_IMAGE_HEADER_SIZE) { ++ dev_err(sdev->dev, ++ "Invalid signed firmware payload size %u (max %u)\n", ++ size_fw, adata->fw_bin_size - ACP_IMAGE_HEADER_SIZE); ++ return -EINVAL; ++ } ++ size_fw += ACP_IMAGE_HEADER_SIZE; ++ } else { ++ size_fw = adata->fw_bin_size; ++ } ++ } else if (adata->quirks && adata->quirks->signed_fw_image) { + size_fw = adata->fw_bin_size - ACP_FIRMWARE_SIGNATURE; +- else ++ } else { + size_fw = adata->fw_bin_size; ++ } + + page_count = PAGE_ALIGN(size_fw) >> PAGE_SHIFT; + adata->fw_bin_page_count = page_count; +@@ -312,9 +334,14 @@ int acp_sof_load_signed_firmware(struct snd_sof_dev *sdev) + } + kfree(fw_filename); + +- ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_DRAM, 0, +- (void *)adata->fw_dbin->data, +- adata->fw_dbin->size); ++ if (adata->pci_rev >= ACP7B_PCI_ID) ++ ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_SRAM, 0, ++ (void *)adata->fw_dbin->data, ++ adata->fw_dbin->size); ++ else ++ ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_DRAM, 0, ++ (void *)adata->fw_dbin->data, ++ adata->fw_dbin->size); + return ret; + } + EXPORT_SYMBOL_NS(acp_sof_load_signed_firmware, "SND_SOC_SOF_AMD_COMMON"); +diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c +--- a/sound/soc/sof/amd/acp.c ++++ b/sound/soc/sof/amd/acp.c +@@ -12,6 +12,7 @@ + * Hardware interface for generic AMD ACP processor + */ + ++#include + #include + #include + #include +@@ -60,6 +61,8 @@ static void init_dma_descriptor(struct acp_dev_data *adata) + case ACP70_PCI_ID: + case ACP71_PCI_ID: + case ACP72_PCI_ID: ++ case ACP7B_PCI_ID: ++ case ACP7F_PCI_ID: + acp_dma_desc_base_addr = ACP70_DMA_DESC_BASE_ADDR; + acp_dma_desc_max_num_dscr = ACP70_DMA_DESC_MAX_NUM_DSCR; + break; +@@ -101,6 +104,8 @@ static int config_dma_channel(struct acp_dev_data *adata, unsigned int ch, + case ACP70_PCI_ID: + case ACP71_PCI_ID: + case ACP72_PCI_ID: ++ case ACP7B_PCI_ID: ++ case ACP7F_PCI_ID: + acp_dma_cntl_0 = ACP70_DMA_CNTL_0; + acp_dma_ch_rst_sts = ACP70_DMA_CH_RST_STS; + acp_dma_dscr_err_sts_0 = ACP70_DMA_ERR_STS_0; +@@ -283,7 +288,8 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr, + } + } + +- if (adata->quirks && adata->quirks->signed_fw_image) ++ if ((adata->quirks && adata->quirks->signed_fw_image) || ++ adata->acp_sof_signed_firmware_image) + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_INCLUDE_HDR, ACP_SHA_HEADER); + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_STRT_ADDR, start_addr); +@@ -342,6 +348,8 @@ int acp_dma_status(struct acp_dev_data *adata, unsigned char ch) + case ACP70_PCI_ID: + case ACP71_PCI_ID: + case ACP72_PCI_ID: ++ case ACP7B_PCI_ID: ++ case ACP7F_PCI_ID: + acp_dma_ch_sts = ACP70_DMA_CH_STS; + break; + default: +@@ -534,8 +542,17 @@ static irqreturn_t acp_irq_handler(int irq, void *dev_id) + snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP_ERROR_IRQ_MASK); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_sw0_i2s_err_reason, 0); + /* ACP_SW1_I2S_ERROR_REASON is newly added register from rmb platform onwards */ +- if (adata->pci_rev >= ACP_RMB_PCI_ID) ++ switch (adata->pci_rev) { ++ case ACP_RMB_PCI_ID: ++ case ACP63_PCI_ID: ++ case ACP70_PCI_ID: ++ case ACP71_PCI_ID: ++ case ACP72_PCI_ID: + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SW1_I2S_ERROR_REASON, 0); ++ break; ++ default: ++ break; ++ } + snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_error_stat, 0); + irq_flag = 1; + } +@@ -564,6 +581,35 @@ static irqreturn_t acp_irq_handler(int irq, void *dev_id) + return IRQ_NONE; + } + ++static irqreturn_t acp7x_irq_handler(int irq, void *dev_id) ++{ ++ struct snd_sof_dev *sdev = dev_id; ++ const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); ++ unsigned int base = desc->dsp_intr_base; ++ unsigned int val; ++ unsigned int ext_intr_stat; ++ int irq_flag = 0; ++ ++ val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET); ++ if (val & ACP_DSP_TO_HOST_IRQ) { ++ snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET, ++ ACP_DSP_TO_HOST_IRQ); ++ return IRQ_WAKE_THREAD; ++ } ++ ++ ext_intr_stat = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat); ++ if (ext_intr_stat & ACP_ERROR_IRQ_MASK) { ++ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP_ERROR_IRQ_MASK); ++ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_error_stat, 0); ++ irq_flag = 1; ++ } ++ ++ if (irq_flag) ++ return IRQ_HANDLED; ++ ++ return IRQ_NONE; ++} ++ + static int acp_power_on(struct snd_sof_dev *sdev) + { + const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); +@@ -571,6 +617,7 @@ static int acp_power_on(struct snd_sof_dev *sdev) + unsigned int base = desc->pgfsm_base; + unsigned int val; + unsigned int acp_pgfsm_status_mask, acp_pgfsm_cntl_mask; ++ bool use_masked_status = false; + int ret; + + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + PGFSM_STATUS_OFFSET); +@@ -595,6 +642,12 @@ static int acp_power_on(struct snd_sof_dev *sdev) + acp_pgfsm_status_mask = ACP70_PGFSM_STATUS_MASK; + acp_pgfsm_cntl_mask = ACP70_PGFSM_CNTL_POWER_ON_MASK; + break; ++ case ACP7B_PCI_ID: ++ case ACP7F_PCI_ID: ++ acp_pgfsm_status_mask = ACP7X_PGFSM_STATUS_MASK; ++ acp_pgfsm_cntl_mask = ACP7X_PGFSM_CNTL_POWER_ON_MASK; ++ use_masked_status = true; ++ break; + default: + return -EINVAL; + } +@@ -603,8 +656,17 @@ static int acp_power_on(struct snd_sof_dev *sdev) + snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + PGFSM_CONTROL_OFFSET, + acp_pgfsm_cntl_mask); + +- ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, base + PGFSM_STATUS_OFFSET, val, +- !val, ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US); ++ if (use_masked_status) ++ ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ++ base + PGFSM_STATUS_OFFSET, val, ++ !(val & acp_pgfsm_status_mask), ++ ACP_REG_POLL_INTERVAL, ++ ACP_REG_POLL_TIMEOUT_US); ++ else ++ ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ++ base + PGFSM_STATUS_OFFSET, val, ++ !val, ACP_REG_POLL_INTERVAL, ++ ACP_REG_POLL_TIMEOUT_US); + if (ret < 0) + dev_err(sdev->dev, "timeout in ACP_PGFSM_STATUS read\n"); + +@@ -703,6 +765,13 @@ static int acp_init(struct snd_sof_dev *sdev) + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP70_PME_EN, 1); + break; ++ case ACP7B_PCI_ID: ++ case ACP7F_PCI_ID: ++ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_ZSC_DSP_CTRL, 0); ++ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_PME_EN, 1); ++ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_DSP0_IDMA_ERROR_MASK, ++ ACP7X_IDMA_ERROR_MASK); ++ break; + } + return 0; + } +@@ -749,6 +818,8 @@ int amd_sof_acp_suspend(struct snd_sof_dev *sdev, u32 target_state) + case ACP72_PCI_ID: + enable = true; + break; ++ default: ++ break; + } + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_CONTROL, enable); + +@@ -990,6 +1061,194 @@ void amd_sof_acp_remove(struct snd_sof_dev *sdev) + } + EXPORT_SYMBOL_NS(amd_sof_acp_remove, "SND_SOC_SOF_AMD_COMMON"); + ++int amd_sof_acp7x_probe(struct snd_sof_dev *sdev) ++{ ++ struct pci_dev *pci = to_pci_dev(sdev->dev); ++ struct acp_dev_data *adata; ++ const struct sof_amd_acp_desc *chip; ++ const union acpi_object *obj; ++ struct acpi_device *adev; ++ unsigned int addr; ++ unsigned int irqflags; ++ int ret; ++ ++ chip = get_chip_info(sdev->pdata); ++ if (!chip) { ++ dev_err(sdev->dev, "no such device supported, chip id:%x\n", pci->device); ++ return -EIO; ++ } ++ adata = devm_kzalloc(sdev->dev, sizeof(struct acp_dev_data), GFP_KERNEL); ++ if (!adata) ++ return -ENOMEM; ++ ++ adata->dev = sdev; ++ adata->dmic_dev = platform_device_register_data(sdev->dev, "dmic-codec", ++ PLATFORM_DEVID_NONE, NULL, 0); ++ if (IS_ERR(adata->dmic_dev)) { ++ dev_err(sdev->dev, "failed to register platform for dmic codec\n"); ++ return PTR_ERR(adata->dmic_dev); ++ } ++ ++ addr = pci_resource_start(pci, ACP_DSP_BAR); ++ sdev->bar[ACP_DSP_BAR] = devm_ioremap(sdev->dev, addr, pci_resource_len(pci, ACP_DSP_BAR)); ++ if (!sdev->bar[ACP_DSP_BAR]) { ++ dev_err(sdev->dev, "ioremap error\n"); ++ ret = -ENXIO; ++ goto unregister_dev; ++ } ++ ++ pci_set_master(pci); ++ adata->addr = addr; ++ adata->reg_range = chip->reg_end_addr - chip->reg_start_addr; ++ adata->pci_rev = pci->revision; ++ mutex_init(&adata->acp_lock); ++ sdev->pdata->hw_pdata = adata; ++ ++ ret = acp_init(sdev); ++ if (ret < 0) ++ goto unregister_dev; ++ ++ adev = ACPI_COMPANION(&pci->dev); ++ ++ sdev->ipc_irq = pci->irq; ++ irqflags = IRQF_SHARED; ++ ++ ret = request_threaded_irq(pci->irq, acp7x_irq_handler, acp_irq_thread, ++ irqflags, "AudioDSP", sdev); ++ if (ret < 0) { ++ dev_err(sdev->dev, "failed to register IRQ %d\n", sdev->ipc_irq); ++ goto unregister_dev; ++ } ++ ++ if (adev) { ++ if (!acpi_dev_get_property(adev, "acp-sof-signed-firmware-image", ++ ACPI_TYPE_INTEGER, &obj)) ++ adata->acp_sof_signed_firmware_image = obj->integer.value; ++ } ++ ++ sdev->dsp_box.offset = 0; ++ sdev->dsp_box.size = BOX_SIZE_512; ++ ++ sdev->host_box.offset = sdev->dsp_box.offset + sdev->dsp_box.size; ++ sdev->host_box.size = BOX_SIZE_512; ++ ++ sdev->debug_box.offset = sdev->host_box.offset + sdev->host_box.size; ++ sdev->debug_box.size = BOX_SIZE_1024; ++ ++ if (adata->acp_sof_signed_firmware_image) { ++ adata->fw_code_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, ++ "sof-%s-code.bin", chip->name); ++ if (!adata->fw_code_bin) { ++ ret = -ENOMEM; ++ goto free_ipc_irq; ++ } ++ adata->fw_data_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, ++ "sof-%s-data.bin", chip->name); ++ if (!adata->fw_data_bin) { ++ ret = -ENOMEM; ++ goto free_ipc_irq; ++ } ++ } ++ ++ adata->enable_fw_debug = enable_fw_debug; ++ acp_memory_init(sdev); ++ acp_dsp_stream_init(sdev); ++ ++ return 0; ++ ++free_ipc_irq: ++ free_irq(sdev->ipc_irq, sdev); ++unregister_dev: ++ platform_device_unregister(adata->dmic_dev); ++ return ret; ++} ++EXPORT_SYMBOL_NS(amd_sof_acp7x_probe, "SND_SOC_SOF_AMD_COMMON"); ++ ++void amd_sof_acp7x_remove(struct snd_sof_dev *sdev) ++{ ++ struct acp_dev_data *adata = sdev->pdata->hw_pdata; ++ ++ if (sdev->ipc_irq) ++ free_irq(sdev->ipc_irq, sdev); ++ ++ if (adata->dmic_dev) ++ platform_device_unregister(adata->dmic_dev); ++ ++ acp_reset(sdev); ++} ++EXPORT_SYMBOL_NS(amd_sof_acp7x_remove, "SND_SOC_SOF_AMD_COMMON"); ++ ++int amd_sof_acp7x_suspend(struct snd_sof_dev *sdev, u32 target_state) ++{ ++ struct acp_dev_data *acp_data; ++ int ret; ++ bool enable = false; ++ ++ acp_data = sdev->pdata->hw_pdata; ++ ++ ret = acp_reset(sdev); ++ if (ret) { ++ dev_err(sdev->dev, "ACP Reset failed\n"); ++ return ret; ++ } ++ switch (acp_data->pci_rev) { ++ case ACP7B_PCI_ID: ++ case ACP7F_PCI_ID: ++ enable = true; ++ break; ++ default: ++ break; ++ } ++ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_CONTROL, enable); ++ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_ZSC_DSP_CTRL, 1); ++ ++ return 0; ++} ++EXPORT_SYMBOL_NS(amd_sof_acp7x_suspend, "SND_SOC_SOF_AMD_COMMON"); ++ ++int amd_sof_acp7x_resume(struct snd_sof_dev *sdev) ++{ ++ struct acp_dev_data *acp_data; ++ int ret; ++ ++ acp_data = sdev->pdata->hw_pdata; ++ ++ ret = acp_init(sdev); ++ if (ret) { ++ dev_err(sdev->dev, "ACP Init failed\n"); ++ return ret; ++ } ++ ret = acp_memory_init(sdev); ++ if (ret) { ++ dev_err(sdev->dev, "ACP Memory init failed\n"); ++ return ret; ++ } ++ ++ switch (acp_data->pci_rev) { ++ case ACP7B_PCI_ID: ++ case ACP7F_PCI_ID: ++ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_PME_EN, 1); ++ break; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_NS(amd_sof_acp7x_resume, "SND_SOC_SOF_AMD_COMMON"); ++ ++int amd_sof_acp7x_suspend_runtime(struct snd_sof_dev *sdev) ++{ ++ return amd_sof_acp7x_suspend(sdev, 0); ++} ++EXPORT_SYMBOL_NS(amd_sof_acp7x_suspend_runtime, "SND_SOC_SOF_AMD_COMMON"); ++ ++int amd_sof_acp7x_resume_runtime(struct snd_sof_dev *sdev) ++{ ++ return amd_sof_acp7x_resume(sdev); ++} ++EXPORT_SYMBOL_NS(amd_sof_acp7x_resume_runtime, "SND_SOC_SOF_AMD_COMMON"); ++ + MODULE_LICENSE("Dual BSD/GPL"); + MODULE_DESCRIPTION("AMD ACP sof driver"); + MODULE_IMPORT_NS("SOUNDWIRE_AMD_INIT"); +diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h +--- a/sound/soc/sof/amd/acp.h ++++ b/sound/soc/sof/amd/acp.h +@@ -76,6 +76,12 @@ + #define ACP70_PCI_ID 0x70 + #define ACP71_PCI_ID 0x71 + #define ACP72_PCI_ID 0x72 ++#define ACP7B_PCI_ID 0x7B ++#define ACP7F_PCI_ID 0x7F ++ ++#define ACP7X_PGFSM_CNTL_POWER_ON_MASK 0x7F ++#define ACP7X_PGFSM_STATUS_MASK 0xFFF ++#define ACP7X_SRAM_PTE_OFFSET ACP6X_SRAM_PTE_OFFSET + + #define HOST_BRIDGE_CZN 0x1630 + #define HOST_BRIDGE_VGH 0x1645 +@@ -107,6 +113,9 @@ + #define PROBE_STATUS_BIT BIT(31) + + #define ACP_FIRMWARE_SIGNATURE 0x100 ++#define ACP_IMAGE_HEADER_SIZE ACP_FIRMWARE_SIGNATURE ++#define ACP_IMAGE_HDR_SIZE_FW_SIGNED_OFF 0x14 ++ + #define ACP_ERROR_IRQ_MASK BIT(29) + #define ACP_SDW0_IRQ_MASK BIT(21) + #define ACP_SDW1_IRQ_MASK BIT(2) +@@ -271,6 +280,7 @@ struct acp_dev_data { + /* acp70_sdw1_wake_event flag set to true when wake irq asserted for SW1 instance */ + bool acp70_sdw1_wake_event; + unsigned int pci_rev; ++ int acp_sof_signed_firmware_image; + }; + + void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes); +@@ -344,6 +354,16 @@ int sof_acp63_ops_init(struct snd_sof_dev *sdev); + extern struct snd_sof_dsp_ops sof_acp70_ops; + int sof_acp70_ops_init(struct snd_sof_dev *sdev); + ++extern struct snd_sof_dsp_ops sof_acp7x_ops; ++int sof_acp7x_ops_init(struct snd_sof_dev *sdev); ++ ++int amd_sof_acp7x_probe(struct snd_sof_dev *sdev); ++void amd_sof_acp7x_remove(struct snd_sof_dev *sdev); ++int amd_sof_acp7x_suspend(struct snd_sof_dev *sdev, u32 target_state); ++int amd_sof_acp7x_resume(struct snd_sof_dev *sdev); ++int amd_sof_acp7x_suspend_runtime(struct snd_sof_dev *sdev); ++int amd_sof_acp7x_resume_runtime(struct snd_sof_dev *sdev); ++ + struct snd_soc_acpi_mach *amd_sof_machine_select(struct snd_sof_dev *sdev); + /* Machine configuration */ + int snd_amd_acp_find_config(struct pci_dev *pci); +diff --git a/sound/soc/sof/amd/acp7x.c b/sound/soc/sof/amd/acp7x.c +new file mode 100644 +--- /dev/null ++++ b/sound/soc/sof/amd/acp7x.c +@@ -0,0 +1,185 @@ ++// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) ++// ++// This file is provided under a dual BSD/GPLv2 license. When using or ++// redistributing this file, you may do so under either license. ++// ++// Copyright(c) 2025 Advanced Micro Devices, Inc. ++// ++// Authors: Vijendar Mukunda ++ ++/* ++ * Hardware interface for Audio DSP on ACP7.B/7.F platforms ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "../ops.h" ++#include "../sof-audio.h" ++#include "acp.h" ++#include "acp-dsp-offset.h" ++ ++#define I2S_TDM0_INSTANCE 0 ++#define I2S_TDM1_INSTANCE 1 ++#define I2S_TDM2_INSTANCE 2 ++#define PDM0_DMIC_INSTANCE 3 ++#define PDM1_DMIC_INSTANCE 4 ++ ++static struct snd_soc_dai_driver acp7x_sof_dai[] = { ++ [I2S_TDM0_INSTANCE] = { ++ .id = I2S_TDM0_INSTANCE, ++ .name = "acp-sof-i2s0", ++ .playback = { ++ .rates = SNDRV_PCM_RATE_8000_96000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | ++ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, ++ .channels_min = 2, ++ .channels_max = 8, ++ .rate_min = 8000, ++ .rate_max = 96000, ++ }, ++ .capture = { ++ .rates = SNDRV_PCM_RATE_8000_48000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | ++ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, ++ /* Supporting only stereo for I2S HS controller capture */ ++ .channels_min = 2, ++ .channels_max = 2, ++ .rate_min = 8000, ++ .rate_max = 48000, ++ }, ++ }, ++ ++ [I2S_TDM1_INSTANCE] = { ++ .id = I2S_TDM1_INSTANCE, ++ .name = "acp-sof-i2s1", ++ .playback = { ++ .rates = SNDRV_PCM_RATE_8000_96000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | ++ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, ++ .channels_min = 2, ++ .channels_max = 8, ++ .rate_min = 8000, ++ .rate_max = 96000, ++ }, ++ .capture = { ++ .rates = SNDRV_PCM_RATE_8000_48000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | ++ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, ++ /* Supporting only stereo for I2S BT controller capture */ ++ .channels_min = 2, ++ .channels_max = 2, ++ .rate_min = 8000, ++ .rate_max = 48000, ++ }, ++ }, ++ ++ [I2S_TDM2_INSTANCE] = { ++ .id = I2S_TDM2_INSTANCE, ++ .name = "acp-sof-i2s2", ++ .playback = { ++ .rates = SNDRV_PCM_RATE_8000_96000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | ++ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, ++ .channels_min = 2, ++ .channels_max = 8, ++ .rate_min = 8000, ++ .rate_max = 96000, ++ }, ++ .capture = { ++ .rates = SNDRV_PCM_RATE_8000_48000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | ++ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, ++ /* Supporting only stereo for I2S SP controller capture */ ++ .channels_min = 2, ++ .channels_max = 2, ++ .rate_min = 8000, ++ .rate_max = 48000, ++ }, ++ }, ++ ++ [PDM0_DMIC_INSTANCE] = { ++ .id = PDM0_DMIC_INSTANCE, ++ .name = "acp-sof-dmic0", ++ .capture = { ++ .rates = SNDRV_PCM_RATE_8000_48000, ++ .formats = SNDRV_PCM_FMTBIT_S32_LE, ++ .channels_min = 2, ++ .channels_max = 4, ++ .rate_min = 8000, ++ .rate_max = 48000, ++ }, ++ }, ++ ++ [PDM1_DMIC_INSTANCE] = { ++ .id = PDM1_DMIC_INSTANCE, ++ .name = "acp-sof-dmic1", ++ .capture = { ++ .rates = SNDRV_PCM_RATE_8000_96000, ++ .formats = SNDRV_PCM_FMTBIT_S32_LE, ++ .channels_min = 2, ++ .channels_max = 4, ++ .rate_min = 8000, ++ .rate_max = 96000, ++ }, ++ }, ++}; ++ ++static int sof_acp7x_post_fw_run_delay(struct snd_sof_dev *sdev) ++{ ++ /* ++ * Resuming from suspend in some cases may cause the DSP firmware ++ * to enter an unrecoverable faulty state. Delaying a bit any host ++ * to DSP transmission right after firmware boot completion seems ++ * to resolve the issue. ++ */ ++ if (!sdev->first_boot) ++ usleep_range(100, 150); ++ ++ return 0; ++} ++ ++struct snd_sof_dsp_ops sof_acp7x_ops; ++EXPORT_SYMBOL_NS(sof_acp7x_ops, "SND_SOC_SOF_AMD_COMMON"); ++ ++int sof_acp7x_ops_init(struct snd_sof_dev *sdev) ++{ ++ struct acpi_device *adev = ACPI_COMPANION(&to_pci_dev(sdev->dev)->dev); ++ const union acpi_object *obj; ++ int acp_sof_signed_firmware_image = 0; ++ int acp_sof_post_fw_run_delay = 0; ++ ++ /* common defaults */ ++ memcpy(&sof_acp7x_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops)); ++ ++ sof_acp7x_ops.drv = acp7x_sof_dai; ++ sof_acp7x_ops.num_drv = ARRAY_SIZE(acp7x_sof_dai); ++ sof_acp7x_ops.probe = amd_sof_acp7x_probe; ++ sof_acp7x_ops.remove = amd_sof_acp7x_remove; ++ ++ if (adev) { ++ if (!acpi_dev_get_property(adev, "acp-sof-signed-firmware-image", ++ ACPI_TYPE_INTEGER, &obj)) ++ acp_sof_signed_firmware_image = obj->integer.value; ++ ++ if (!acpi_dev_get_property(adev, "acp-sof-post_fw_run_delay", ++ ACPI_TYPE_INTEGER, &obj)) ++ acp_sof_post_fw_run_delay = obj->integer.value; ++ } ++ ++ if (acp_sof_signed_firmware_image) ++ sof_acp7x_ops.load_firmware = acp_sof_load_signed_firmware; ++ ++ if (acp_sof_post_fw_run_delay) ++ sof_acp7x_ops.post_fw_run = sof_acp7x_post_fw_run_delay; ++ ++ sof_acp7x_ops.suspend = amd_sof_acp7x_suspend; ++ sof_acp7x_ops.resume = amd_sof_acp7x_resume; ++ sof_acp7x_ops.runtime_suspend = amd_sof_acp7x_suspend_runtime; ++ sof_acp7x_ops.runtime_resume = amd_sof_acp7x_resume_runtime; ++ ++ return 0; ++} +diff --git a/sound/soc/sof/amd/pci-acp7x.c b/sound/soc/sof/amd/pci-acp7x.c +new file mode 100644 +--- /dev/null ++++ b/sound/soc/sof/amd/pci-acp7x.c +@@ -0,0 +1,116 @@ ++// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) ++// ++// This file is provided under a dual BSD/GPLv2 license. When using or ++// redistributing this file, you may do so under either license. ++// ++// Copyright(c) 2025 Advanced Micro Devices, Inc. All rights reserved. ++// ++// Authors: Vijendar Mukunda ++ ++/* ++ * PCI interface for ACP7.B/7.F devices ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "../ops.h" ++#include "../sof-pci-dev.h" ++#include "../../amd/mach-config.h" ++#include "acp.h" ++#include "acp-dsp-offset.h" ++ ++#define ACP7X_FUTURE_REG_ACLK_0 0x18e0 ++#define ACP7X_REG_START 0x1240000 ++#define ACP7X_REG_END 0x125C000 ++ ++static const struct sof_amd_acp_desc acp7x_chip_info = { ++ .name = "acp7x", ++ .pgfsm_base = ACP7X_PGFSM_BASE, ++ .ext_intr_enb = ACP6X_EXTERNAL_INTR_ENB, ++ .ext_intr_cntl = ACP7X_EXTERNAL_INTR_CNTL, ++ .ext_intr_stat = ACP7X_EXT_INTR_STAT, ++ .ext_intr_stat1 = ACP7X_EXT_INTR_STAT1, ++ .dsp_intr_base = ACP7X_DSP_SW_INTR_BASE, ++ .acp_error_stat = ACP7X_ERROR_STATUS, ++ .sram_pte_offset = ACP7X_SRAM_PTE_OFFSET, ++ .hw_semaphore_offset = ACP7X_AXI2DAGB_SEM_0, ++ .fusion_dsp_offset = ACP7X_DSP_FUSION_RUNSTALL, ++ .probe_reg_offset = ACP7X_FUTURE_REG_ACLK_0, ++ .reg_start_addr = ACP7X_REG_START, ++ .reg_end_addr = ACP7X_REG_END, ++}; ++ ++static const struct sof_dev_desc acp7x_desc = { ++ .machines = snd_soc_acpi_amd_acp7x_sof_machines, ++ .resindex_lpe_base = 0, ++ .resindex_pcicfg_base = -1, ++ .resindex_imr_base = -1, ++ .irqindex_host_ipc = -1, ++ .chip_info = &acp7x_chip_info, ++ .ipc_supported_mask = BIT(SOF_IPC_TYPE_3), ++ .ipc_default = SOF_IPC_TYPE_3, ++ .default_fw_path = { ++ [SOF_IPC_TYPE_3] = "amd/sof", ++ }, ++ .default_tplg_path = { ++ [SOF_IPC_TYPE_3] = "amd/sof-tplg", ++ }, ++ .default_fw_filename = { ++ [SOF_IPC_TYPE_3] = "sof-acp7x.ri", ++ }, ++ .nocodec_tplg_filename = "sof-acp.tplg", ++ .ops = &sof_acp7x_ops, ++ .ops_init = sof_acp7x_ops_init, ++}; ++ ++static int acp7x_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) ++{ ++ unsigned int flag; ++ ++ switch (pci->revision) { ++ case ACP7B_PCI_ID: ++ case ACP7F_PCI_ID: ++ break; ++ default: ++ return -ENODEV; ++ } ++ ++ flag = snd_amd_acp_find_config(pci); ++ if (flag != FLAG_AMD_SOF && flag != FLAG_AMD_SOF_ONLY_DMIC) ++ return -ENODEV; ++ ++ return sof_pci_probe(pci, pci_id); ++} ++ ++static void acp7x_pci_remove(struct pci_dev *pci) ++{ ++ sof_pci_remove(pci); ++} ++ ++/* PCI IDs */ ++static const struct pci_device_id acp7x_pci_ids[] = { ++ { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID), ++ .driver_data = (unsigned long)&acp7x_desc}, ++ { 0, } ++}; ++MODULE_DEVICE_TABLE(pci, acp7x_pci_ids); ++ ++/* pci_driver definition */ ++static struct pci_driver snd_sof_pci_amd_acp7x_driver = { ++ .name = KBUILD_MODNAME, ++ .id_table = acp7x_pci_ids, ++ .probe = acp7x_pci_probe, ++ .remove = acp7x_pci_remove, ++ .driver = { ++ .pm = pm_ptr(&sof_pci_pm), ++ }, ++}; ++module_pci_driver(snd_sof_pci_amd_acp7x_driver); ++ ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_DESCRIPTION("ACP7X SOF Driver"); ++MODULE_IMPORT_NS("SND_SOC_SOF_AMD_COMMON"); ++MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV"); +diff --git a/sound/soc/sof/fw-file-profile.c b/sound/soc/sof/fw-file-profile.c +--- a/sound/soc/sof/fw-file-profile.c ++++ b/sound/soc/sof/fw-file-profile.c +@@ -16,20 +16,19 @@ static int sof_test_firmware_file(struct device *dev, + enum sof_ipc_type *ipc_type_to_adjust) + { + enum sof_ipc_type fw_ipc_type; +- const struct firmware *fw; +- const char *fw_filename; + const u32 *magic; + int ret; + +- fw_filename = kasprintf(GFP_KERNEL, "%s/%s", profile->fw_path, +- profile->fw_name); ++ const char *fw_filename __free(kfree) = ++ kasprintf(GFP_KERNEL, "%s/%s", profile->fw_path, ++ profile->fw_name); + if (!fw_filename) + return -ENOMEM; + ++ const struct firmware *fw __free(firmware) = NULL; + ret = firmware_request_nowarn(&fw, fw_filename, dev); + if (ret < 0) { + dev_dbg(dev, "Failed to open firmware file: %s\n", fw_filename); +- kfree(fw_filename); + return ret; + } + +@@ -44,8 +43,7 @@ static int sof_test_firmware_file(struct device *dev, + break; + default: + dev_err(dev, "Invalid firmware magic: %#x\n", *magic); +- ret = -EINVAL; +- goto out; ++ return -EINVAL; + } + + if (ipc_type_to_adjust) { +@@ -54,13 +52,10 @@ static int sof_test_firmware_file(struct device *dev, + dev_err(dev, + "ipc type mismatch between %s and expected: %d vs %d\n", + fw_filename, fw_ipc_type, profile->ipc_type); +- ret = -EINVAL; ++ return -EINVAL; + } +-out: +- release_firmware(fw); +- kfree(fw_filename); + +- return ret; ++ return 0; + } + + static int sof_test_topology_file(struct device *dev, +diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c +--- a/sound/soc/sof/intel/hda-ctrl.c ++++ b/sound/soc/sof/intel/hda-ctrl.c +@@ -186,6 +186,7 @@ EXPORT_SYMBOL_NS(hda_dsp_ctrl_clock_power_gating, "SND_SOC_SOF_INTEL_HDA_COMMON" + int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec) + { + struct hdac_bus *bus = sof_to_bus(sdev); ++ struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); + struct hdac_stream *stream; + int sd_offset, ret = 0; + u32 gctl; +@@ -193,6 +194,16 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec) + if (bus->chip_init) + return 0; + ++ /* ++ * The controller reset clears the ACE2+ link DMA stream allocation ++ * constraints; reset the masks to reflect this. ++ */ ++ memset(sof_hda->link_dma_active_sdw_mask, 0, ++ sizeof(sof_hda->link_dma_active_sdw_mask)); ++ memset(sof_hda->link_dma_active_multi_mask, 0, ++ sizeof(sof_hda->link_dma_active_multi_mask)); ++ sof_hda->link_dma_out_hda_used_mask = 0; ++ + hda_codec_set_codec_wakeup(sdev, true); + + hda_dsp_ctrl_misc_clock_gating(sdev, false); +@@ -223,6 +234,14 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec) + /* Accept unsolicited responses */ + snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL); + ++ /* Perform a one-time enumeration of the Multi-Link capability */ ++ ret = hda_bus_ml_init(bus); ++ if (ret < 0) { ++ dev_err(sdev->dev, "%s: failed to enumerate multi-links\n", ++ __func__); ++ goto err; ++ } ++ + if (detect_codec) + hda_codec_detect_mask(sdev); + +diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c +--- a/sound/soc/sof/intel/hda-dai-ops.c ++++ b/sound/soc/sof/intel/hda-dai-ops.c +@@ -20,7 +20,7 @@ + /* These ops are only applicable for the HDA DAI's in their current form */ + #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK) + /* +- * This function checks if the host dma channel corresponding ++ * This function checks if the host DMA stream corresponding + * to the link DMA stream_tag argument is assigned to one + * of the FEs connected to the BE DAI. + */ +@@ -42,23 +42,53 @@ static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd, + } + + static struct hdac_ext_stream * +-hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream) ++hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream, ++ enum hda_bus_ml_link_type link_type) + { + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); ++ struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); + struct sof_intel_hda_stream *hda_stream; + const struct sof_intel_dsp_desc *chip; + struct snd_sof_dev *sdev; + struct hdac_ext_stream *res = NULL; + struct hdac_stream *hstream = NULL; +- + int stream_dir = substream->stream; ++ bool is_multi = link_type == HDA_BUS_ML_LINK_HDA || link_type == HDA_BUS_ML_LINK_UAOL; ++ bool is_play = stream_dir == SNDRV_PCM_STREAM_PLAYBACK; ++ bool is_sdw = link_type == HDA_BUS_ML_LINK_SDW; ++ bool is_hda = link_type == HDA_BUS_ML_LINK_HDA; ++ u32 concur_block_mask = 0; ++ u32 seq_block_mask = 0; ++ unsigned int stream_idx; + + if (!bus->ppcap) { + dev_err(bus->dev, "stream type not supported\n"); + return NULL; + } + ++ /* ++ * On ACE2+ the link DMA stream allocator must avoid two HW errata, ++ * see the comment on struct sof_intel_hda_dev. ++ * ++ * - Concurrent cross-direction: SoundWire conflicts with HDA, iDisp ++ * and UAOL on the same physical stream index; SSP and DMIC are safe. ++ * - Sequential playback: a stream index previously used by an HDA/iDisp ++ * link cannot drive any non-HDA/iDisp link in the same direction ++ * until the next controller reset. ++ * ++ * The masks are protected by bus->reg_lock; sample them inside the ++ * lock together with the stream walk to keep the decision atomic ++ * with concurrent allocations and releases. ++ */ + guard(spinlock_irq)(&bus->reg_lock); ++ ++ if (is_sdw) ++ concur_block_mask = sof_hda->link_dma_active_multi_mask[!stream_dir]; ++ else if (is_multi) ++ concur_block_mask = sof_hda->link_dma_active_sdw_mask[!stream_dir]; ++ if (is_play && !is_hda) ++ seq_block_mask = sof_hda->link_dma_out_hda_used_mask; ++ + list_for_each_entry(hstream, &bus->stream_list, list) { + struct hdac_ext_stream *hext_stream = + stream_to_hdac_ext_stream(hstream); +@@ -69,6 +99,12 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream + sdev = hda_stream->sdev; + chip = get_chip_info(sdev->pdata); + ++ stream_idx = hstream->stream_tag - 1; ++ ++ /* skip streams blocked by the ACE2+ allocator constraints */ ++ if ((concur_block_mask | seq_block_mask) & BIT(stream_idx)) ++ continue; ++ + /* check if link is available */ + if (!hext_stream->link_locked) { + /* +@@ -95,7 +131,7 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream + + /* + * This must be a hostless stream. +- * So reserve the host DMA channel. ++ * So reserve the host DMA stream. + */ + hda_stream->host_reserved = 1; + break; +@@ -109,6 +145,16 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream + + res->link_locked = 1; + res->link_substream = substream; ++ ++ stream_idx = res->hstream.stream_tag - 1; ++ if (is_sdw) ++ sof_hda->link_dma_active_sdw_mask[stream_dir] |= BIT(stream_idx); ++ else if (is_multi) ++ sof_hda->link_dma_active_multi_mask[stream_dir] |= BIT(stream_idx); ++ ++ /* persistent OUT HDA/iDisp shadow, cleared only on CRST# */ ++ if (is_hda && is_play) ++ sof_hda->link_dma_out_hda_used_mask |= BIT(stream_idx); + } + + return res; +@@ -143,11 +189,13 @@ static struct hdac_ext_stream *hda_ipc4_get_hext_stream(struct snd_sof_dev *sdev + + static struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev, + struct snd_soc_dai *cpu_dai, +- struct snd_pcm_substream *substream) ++ struct snd_pcm_substream *substream, ++ struct hdac_ext_link *hlink) + { + struct hdac_ext_stream *hext_stream; ++ enum hda_bus_ml_link_type link_type = hda_bus_ml_link_get_type(hlink); + +- hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream); ++ hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream, link_type); + if (!hext_stream) + return NULL; + +@@ -160,6 +208,22 @@ static void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai + struct snd_pcm_substream *substream) + { + struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream); ++ struct sof_intel_hda_dev *sof_hda = sdev->pdata->hw_pdata; ++ struct hdac_bus *bus = sof_to_bus(sdev); ++ int dir = substream->stream; ++ unsigned int stream_idx = hext_stream->hstream.stream_tag - 1; ++ ++ /* ++ * Drop the stream index from the per-direction active concurrency masks. ++ * The two masks are mutually exclusive for a given stream/direction ++ * (and a stream of the SSP/DMIC kind appears in neither), so a blind ++ * clear of both is safe and lets us avoid having to remember the ++ * link type at allocation time. ++ */ ++ scoped_guard(spinlock_irq, &bus->reg_lock) { ++ sof_hda->link_dma_active_sdw_mask[dir] &= ~BIT(stream_idx); ++ sof_hda->link_dma_active_multi_mask[dir] &= ~BIT(stream_idx); ++ } + + snd_soc_dai_set_dma_data(cpu_dai, substream, NULL); + snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK); +diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c +--- a/sound/soc/sof/intel/hda-dai.c ++++ b/sound/soc/sof/intel/hda-dai.c +@@ -188,7 +188,7 @@ static int hda_link_dma_hw_params(struct snd_pcm_substream *substream, + + if (!hext_stream) { + if (ops->assign_hext_stream) +- hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream); ++ hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream, hlink); + } + + if (!hext_stream) +diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c +--- a/sound/soc/sof/intel/hda-dsp.c ++++ b/sound/soc/sof/intel/hda-dsp.c +@@ -1114,11 +1114,7 @@ static int hda_dsp_s5_quirk(struct snd_sof_dev *sdev) + usleep_range(500, 1000); + + /* Restore state for shutdown, back to reset */ +- ret = hda_dsp_ctrl_link_reset(sdev, true); +- if (ret < 0) +- return ret; +- +- return ret; ++ return hda_dsp_ctrl_link_reset(sdev, true); + } + + int hda_dsp_shutdown_dma_flush(struct snd_sof_dev *sdev) +diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c +--- a/sound/soc/sof/intel/hda-loader.c ++++ b/sound/soc/sof/intel/hda-loader.c +@@ -216,9 +216,10 @@ int hda_cl_trigger(struct device *dev, struct hdac_ext_stream *hext_stream, int + EXPORT_SYMBOL_NS(hda_cl_trigger, "SND_SOC_SOF_INTEL_HDA_COMMON"); + + int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab, +- bool persistent_buffer, struct hdac_ext_stream *hext_stream) ++ bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool is_iccmax) + { +- return hda_data_stream_cleanup(dev, dmab, persistent_buffer, hext_stream, false); ++ return hda_data_stream_cleanup(dev, dmab, persistent_buffer, hext_stream, ++ is_iccmax, false); + } + EXPORT_SYMBOL_NS(hda_cl_cleanup, "SND_SOC_SOF_INTEL_HDA_COMMON"); + +@@ -302,7 +303,7 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev) + * If the cleanup also fails, we return the initial error + */ + ret1 = hda_cl_cleanup(sdev->dev, &hda->iccmax_dmab, +- persistent_cl_buffer, iccmax_stream); ++ persistent_cl_buffer, iccmax_stream, true); + if (ret1 < 0) { + dev_err(sdev->dev, "error: ICCMAX stream cleanup failed\n"); + +@@ -458,7 +459,7 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) + * If the cleanup also fails, we return the initial error + */ + ret1 = hda_cl_cleanup(sdev->dev, &hda->cl_dmab, +- persistent_cl_buffer, hext_stream); ++ persistent_cl_buffer, hext_stream, false); + if (ret1 < 0) { + dev_err(sdev->dev, "error: Code loader DSP cleanup failed\n"); + +@@ -587,7 +588,7 @@ int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev, + cleanup: + /* clean up even in case of error and return the first error */ + ret1 = hda_cl_cleanup(sdev->dev, &hda->cl_dmab, persistent_cl_buffer, +- hext_stream); ++ hext_stream, false); + if (ret1 < 0) { + dev_err(sdev->dev, "%s: Code loader DSP cleanup failed\n", __func__); + +diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c +--- a/sound/soc/sof/intel/hda-mlink.c ++++ b/sound/soc/sof/intel/hda-mlink.c +@@ -432,6 +432,10 @@ int hda_bus_ml_init(struct hdac_bus *bus) + if (!bus->mlcap) + return 0; + ++ /* Enumeration is a one time operation, skip if already done */ ++ if (!list_empty(&bus->hlink_list)) ++ return 0; ++ + link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1; + + dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count); +@@ -880,19 +884,6 @@ int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, + return 0; + } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_map_stream_ch, "SND_SOC_SOF_HDA_MLINK"); + +-void hda_bus_ml_put_all(struct hdac_bus *bus) +-{ +- struct hdac_ext_link *hlink; +- +- list_for_each_entry(hlink, &bus->hlink_list, list) { +- struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); +- +- if (!h2link->alt) +- snd_hdac_ext_bus_link_put(bus, hlink); +- } +-} +-EXPORT_SYMBOL_NS(hda_bus_ml_put_all, "SND_SOC_SOF_HDA_MLINK"); +- + void hda_bus_ml_reset_losidv(struct hdac_bus *bus) + { + struct hdac_ext_link *hlink; +@@ -903,6 +894,24 @@ void hda_bus_ml_reset_losidv(struct hdac_bus *bus) + } + EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, "SND_SOC_SOF_HDA_MLINK"); + ++enum hda_bus_ml_link_type hda_bus_ml_link_get_type(struct hdac_ext_link *hlink) ++{ ++ struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); ++ ++ if (!h2link->alt) ++ return HDA_BUS_ML_LINK_HDA; ++ ++ switch (h2link->elid) { ++ case AZX_REG_ML_LEPTR_ID_SDW: ++ return HDA_BUS_ML_LINK_SDW; ++ case AZX_REG_ML_LEPTR_ID_INTEL_UAOL: ++ return HDA_BUS_ML_LINK_UAOL; ++ default: ++ return HDA_BUS_ML_LINK_OTHER; ++ } ++} ++EXPORT_SYMBOL_NS(hda_bus_ml_link_get_type, "SND_SOC_SOF_HDA_MLINK"); ++ + int hda_bus_ml_resume(struct hdac_bus *bus) + { + struct hdac_ext_link *hlink; +diff --git a/sound/soc/sof/intel/hda-sdw-bpt.c b/sound/soc/sof/intel/hda-sdw-bpt.c +--- a/sound/soc/sof/intel/hda-sdw-bpt.c ++++ b/sound/soc/sof/intel/hda-sdw-bpt.c +@@ -163,7 +163,7 @@ static int hda_sdw_bpt_dma_deprepare(struct device *dev, struct hdac_ext_stream + u32 mask; + int ret; + +- ret = hda_data_stream_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream, true); ++ ret = hda_data_stream_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream, false, true); + if (ret < 0) { + dev_err(sdev->dev, "%s: SDW BPT DMA cleanup failed\n", + __func__); +diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c +--- a/sound/soc/sof/intel/hda-stream.c ++++ b/sound/soc/sof/intel/hda-stream.c +@@ -198,13 +198,18 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, + + mask = (1 << hstream->index); + ++ /* Reset the spib_addr before disabling SPIB */ ++ if (!enable) ++ sof_io_write(sdev, hstream->spib_addr, 0); ++ + /* enable/disable SPIB for the stream */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_SPIB_BAR, + SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCCTL, mask, + enable << hstream->index); + + /* set the SPIB value */ +- sof_io_write(sdev, hstream->spib_addr, size); ++ if (enable) ++ sof_io_write(sdev, hstream->spib_addr, size); + + return 0; + } +@@ -608,6 +613,9 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, + return ret; + } + ++ /* Host DMA is not running */ ++ hstream->running = false; ++ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + sd_offset + SOF_HDA_ADSP_REG_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK, +@@ -1323,16 +1331,18 @@ hda_data_stream_prepare(struct device *dev, unsigned int format, unsigned int si + EXPORT_SYMBOL_NS(hda_data_stream_prepare, "SND_SOC_SOF_INTEL_HDA_COMMON"); + + int hda_data_stream_cleanup(struct device *dev, struct snd_dma_buffer *dmab, +- bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool pair) ++ bool persistent_buffer, struct hdac_ext_stream *hext_stream, ++ bool is_iccmax, bool pair) + { + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + struct hdac_stream *hstream = hdac_stream(hext_stream); + int sd_offset = SOF_STREAM_SD_OFFSET(hstream); + int ret = 0; + +- if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK) ++ if (!is_iccmax) + ret = hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0); +- else ++ ++ if (hstream->direction == SNDRV_PCM_STREAM_CAPTURE) + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, + SOF_HDA_SD_CTL_DMA_START, 0); + +diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c +--- a/sound/soc/sof/intel/hda.c ++++ b/sound/soc/sof/intel/hda.c +@@ -421,6 +421,20 @@ static inline void hda_dsp_sdw_process_mic_privacy(struct snd_sof_dev *sdev) { } + /* pre fw run operations */ + int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev) + { ++ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; ++ const struct sof_intel_dsp_desc *chip = hda->desc; ++ int ret; ++ ++ /* Power down DSP if left enabled to ensure a clean boot state. */ ++ if (hda_dsp_core_is_enabled(sdev, chip->host_managed_cores_mask)) { ++ dev_dbg(sdev->dev, "DSP core enabled, power down DSP first\n"); ++ ++ ret = chip->power_down_dsp(sdev); ++ if (ret < 0) ++ dev_warn(sdev->dev, ++ "%s: failed to power down already-enabled DSP\n", __func__); ++ } ++ + /* disable clock gating and power gating */ + return hda_dsp_ctrl_clock_power_gating(sdev, false); + } +@@ -625,8 +639,6 @@ static int hda_init_caps(struct snd_sof_dev *sdev) + return ret; + } + +- hda_bus_ml_init(bus); +- + /* Skip SoundWire if it is not supported */ + if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH))) + goto skip_soundwire; +@@ -670,8 +682,6 @@ static int hda_init_caps(struct snd_sof_dev *sdev) + if (!HDA_IDISP_CODEC(bus->codec_mask)) + hda_codec_i915_display_power(sdev, false); + +- hda_bus_ml_put_all(bus); +- + return 0; + } + +diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h +--- a/sound/soc/sof/intel/hda.h ++++ b/sound/soc/sof/intel/hda.h +@@ -523,6 +523,29 @@ struct sof_intel_hda_dev { + /* the maximum number of streams (playback + capture) supported */ + u32 stream_max; + ++ /* ++ * ACE2+ link DMA stream allocation constraints (stream index = ++ * stream_tag - 1, shared between input and output directions). All ++ * masks are cleared by hda_dsp_ctrl_init_chip() on controller reset ++ * (CRST#). ++ * ++ * - Concurrent (cross-direction) constraint: a SoundWire stream and ++ * a HDA/iDisp/UAOL stream cannot share a physical stream index ++ * across directions, the resulting LLP/timestamp values are wrong. ++ * link_dma_active_sdw_mask and link_dma_active_multi_mask ++ * (indexed by SNDRV_PCM_STREAM_*) track currently allocated ++ * streams per direction in each of the conflicting groups; SSP ++ * and DMIC do not participate. Bits are cleared on stream release. ++ * ++ * - Sequential (playback only) constraint: once a HDA/iDisp link ++ * has used a playback stream index, that index cannot drive a ++ * non-HDA/iDisp link in the same direction until the next CRST#. ++ * link_dma_out_hda_used_mask records this. ++ */ ++ u32 link_dma_active_sdw_mask[SNDRV_PCM_STREAM_LAST + 1]; ++ u32 link_dma_active_multi_mask[SNDRV_PCM_STREAM_LAST + 1]; ++ u32 link_dma_out_hda_used_mask; ++ + /* PM related */ + bool l1_disabled;/* is DMI link L1 disabled? */ + +@@ -739,7 +762,7 @@ struct hdac_ext_stream *hda_cl_prepare(struct device *dev, unsigned int format, + int hda_cl_trigger(struct device *dev, struct hdac_ext_stream *hext_stream, int cmd); + + int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab, +- bool persistent_buffer, struct hdac_ext_stream *hext_stream); ++ bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool is_iccmax); + int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot); + #define HDA_CL_STREAM_FORMAT 0x40 + +@@ -911,7 +934,8 @@ hda_data_stream_prepare(struct device *dev, unsigned int format, unsigned int si + bool is_iccmax, bool pair); + + int hda_data_stream_cleanup(struct device *dev, struct snd_dma_buffer *dmab, +- bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool pair); ++ bool persistent_buffer, struct hdac_ext_stream *hext_stream, ++ bool is_iccmax, bool pair); + + /* common dai driver */ + extern struct snd_soc_dai_driver skl_dai[]; +@@ -1030,7 +1054,8 @@ struct hda_dai_widget_dma_ops { + struct snd_pcm_substream *substream); + struct hdac_ext_stream *(*assign_hext_stream)(struct snd_sof_dev *sdev, + struct snd_soc_dai *cpu_dai, +- struct snd_pcm_substream *substream); ++ struct snd_pcm_substream *substream, ++ struct hdac_ext_link *hlink); + void (*release_hext_stream)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, + struct snd_pcm_substream *substream); + void (*setup_hext_stream)(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream, +diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c +--- a/sound/soc/sof/intel/icl.c ++++ b/sound/soc/sof/intel/icl.c +@@ -9,10 +9,14 @@ + * Hardware interface for audio DSP on IceLake. + */ + +-#include +-#include +-#include ++#include + #include ++#include ++#include ++#include ++#include ++#include ++ + #include "../ipc4-priv.h" + #include "../ops.h" + #include "hda.h" +diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c +--- a/sound/soc/sof/intel/mtl.c ++++ b/sound/soc/sof/intel/mtl.c +@@ -236,6 +236,17 @@ int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable) + } + EXPORT_SYMBOL_NS(mtl_enable_interrupts, "SND_SOC_SOF_INTEL_MTL"); + ++static bool mtl_dsp_is_enabled(struct snd_sof_dev *sdev) ++{ ++ int val; ++ ++ val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_HFDSSCS); ++ if (val & MTL_HFDSSCS_CPA_MASK) ++ return true; ++ ++ return false; ++} ++ + /* pre fw run operations */ + static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev) + { +@@ -249,6 +260,18 @@ static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev) + u32 dsppwrsts; + const struct sof_intel_dsp_desc *chip; + ++ /* Power down the DSP if it is left enabled to ensure clean boot state */ ++ if (mtl_dsp_is_enabled(sdev)) { ++ dev_dbg(sdev->dev, "powering down DSP first\n"); ++ ++ ret = mtl_power_down_dsp(sdev); ++ if (ret < 0) { ++ dev_warn(sdev->dev, ++ "%s: failed to power down already-enabled DSP\n", __func__); ++ /* Continue anyway to attempt recovery */ ++ } ++ } ++ + chip = get_chip_info(sdev->pdata); + if (chip->hw_ip_version > SOF_INTEL_ACE_2_0) { + dsppwrctl = PTL_HFPWRCTL2; +diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c +--- a/sound/soc/sof/ipc3-pcm.c ++++ b/sound/soc/sof/ipc3-pcm.c +@@ -421,6 +421,12 @@ static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, + dev_dbg(component->dev, "AMD_SDW channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; ++ case SOF_DAI_AMD_I2S: ++ rate->min = private->dai_config->acp_i2s.fsync_rate; ++ rate->max = private->dai_config->acp_i2s.fsync_rate; ++ channels->min = private->dai_config->acp_i2s.tdm_slots; ++ channels->max = private->dai_config->acp_i2s.tdm_slots; ++ break; + default: + dev_err(component->dev, "Invalid DAI type %d\n", private->dai_config->type); + break; +diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c +--- a/sound/soc/sof/ipc3-topology.c ++++ b/sound/soc/sof/ipc3-topology.c +@@ -281,7 +281,10 @@ static const struct sof_topology_token acpdmic_tokens[] = { + offsetof(struct sof_ipc_dai_acpdmic_params, pdm_ch)}, + }; + +-/* ACPI2S */ ++/* ++ * ACPI2S tokens fill struct sof_ipc_dai_acp_params; SOF_DAI_AMD_I2S (ACPTDM ++ * on ACP7.B/7.F) reuses this tuple group rather than defining a parallel set. ++ */ + static const struct sof_topology_token acpi2s_tokens[] = { + {SOF_TKN_AMD_ACPI2S_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_acp_params, fsync_rate)}, +@@ -289,6 +292,8 @@ static const struct sof_topology_token acpi2s_tokens[] = { + offsetof(struct sof_ipc_dai_acp_params, tdm_slots)}, + {SOF_TKN_AMD_ACPI2S_TDM_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_acp_params, tdm_mode)}, ++ {SOF_TKN_AMD_ACPI2S_FORMAT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, ++ offsetof(struct sof_ipc_dai_acp_params, format)}, + }; + + /* MICFIL PDM */ +@@ -1363,6 +1368,35 @@ static int sof_link_acp_sdw_load(struct snd_soc_component *scomp, struct snd_sof + return 0; + } + ++static int sof_link_acp_i2s_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, ++ struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) ++{ ++ struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; ++ struct sof_dai_private_data *private = dai->private; ++ u32 size = sizeof(*config); ++ int ret; ++ ++ /* handle master/slave and inverted clocks */ ++ sof_dai_set_format(hw_config, config); ++ ++ /* init IPC */ ++ memset(&config->acp_i2s, 0, sizeof(config->acp_i2s)); ++ config->hdr.size = size; ++ ++ ret = sof_update_ipc_object(scomp, &config->acp_i2s, SOF_ACPI2S_TOKENS, slink->tuples, ++ slink->num_tuples, size, slink->num_hw_configs); ++ if (ret < 0) ++ return ret; ++ ++ dai->number_configs = 1; ++ dai->current_config = 0; ++ private->dai_config = kmemdup(config, size, GFP_KERNEL); ++ if (!private->dai_config) ++ return -ENOMEM; ++ ++ return 0; ++} ++ + static int sof_link_afe_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, + struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) + { +@@ -1692,6 +1726,9 @@ static int sof_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget) + case SOF_DAI_AMD_SDW: + ret = sof_link_acp_sdw_load(scomp, slink, config, dai); + break; ++ case SOF_DAI_AMD_I2S: ++ ret = sof_link_acp_i2s_load(scomp, slink, config, dai); ++ break; + default: + break; + } +diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c +--- a/sound/soc/sof/ipc4-topology.c ++++ b/sound/soc/sof/ipc4-topology.c +@@ -163,11 +163,11 @@ static const struct sof_topology_token comp_ext_tokens[] = { + {SOF_TKN_COMP_SCHED_DOMAIN, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_domain, + offsetof(struct snd_sof_widget, comp_domain)}, + {SOF_TKN_COMP_DOMAIN_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, +- offsetof(struct snd_sof_widget, dp_domain_id)}, ++ offsetof(struct snd_sof_widget, domain_id)}, + {SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, +- offsetof(struct snd_sof_widget, dp_heap_bytes)}, ++ offsetof(struct snd_sof_widget, heap_bytes)}, + {SOF_TKN_COMP_STACK_BYTES_REQUIREMENT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, +- offsetof(struct snd_sof_widget, dp_stack_bytes)}, ++ offsetof(struct snd_sof_widget, stack_bytes)}, + }; + + static const struct sof_topology_token gain_tokens[] = { +@@ -406,6 +406,39 @@ sof_ipc4_get_input_pin_audio_fmt(struct snd_sof_widget *swidget, int pin_index) + return NULL; + } + ++static void ++sof_ipc4_evaluate_params_change(struct sof_ipc4_available_audio_format *available_fmt) ++{ ++ struct sof_ipc4_audio_format *fmt; ++ u32 in_rate, in_channels, in_valid_bits; ++ u32 out_rate, out_channels, out_valid_bits; ++ u32 changed_params = 0; ++ int i, j; ++ ++ for (i = 0; i < available_fmt->num_input_formats; i++) { ++ fmt = &available_fmt->input_pin_fmts[i].audio_fmt; ++ in_rate = fmt->sampling_frequency; ++ in_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg); ++ in_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); ++ ++ for (j = 0; j < available_fmt->num_output_formats; j++) { ++ fmt = &available_fmt->output_pin_fmts[j].audio_fmt; ++ out_rate = fmt->sampling_frequency; ++ out_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg); ++ out_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); ++ ++ if (in_rate != out_rate) ++ changed_params |= BIT(SNDRV_PCM_HW_PARAM_RATE); ++ if (in_channels != out_channels) ++ changed_params |= BIT(SNDRV_PCM_HW_PARAM_CHANNELS); ++ if (in_valid_bits != out_valid_bits) ++ changed_params |= BIT(SNDRV_PCM_HW_PARAM_FORMAT); ++ } ++ } ++ ++ available_fmt->changed_params = changed_params; ++} ++ + /** + * sof_ipc4_get_audio_fmt - get available audio formats from swidget->tuples + * @scomp: pointer to pointer to SOC component +@@ -497,6 +530,8 @@ static int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp, + available_fmt->num_output_formats); + } + ++ sof_ipc4_evaluate_params_change(available_fmt); ++ + return 0; + + err_out: +@@ -661,6 +696,9 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget) + if (ret) + goto free_copier; + ++ /* Copier can only change format */ ++ available_fmt->changed_params &= BIT(SNDRV_PCM_HW_PARAM_FORMAT); ++ + /* + * This callback is used by host copier and module-to-module copier, + * and only host copier needs to set gtw_cfg. +@@ -789,6 +827,9 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) + if (ret) + goto free_copier; + ++ /* Copier can only change format */ ++ available_fmt->changed_params &= BIT(SNDRV_PCM_HW_PARAM_FORMAT); ++ + ret = sof_update_ipc_object(scomp, &node_type, + SOF_COPIER_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(node_type), 1); +@@ -1371,6 +1412,22 @@ sof_ipc4_update_resource_usage(struct snd_sof_dev *sdev, struct snd_sof_widget * + pipeline = pipe_widget->private; + pipeline->mem_usage += total; + ++ /* ++ * If this is not a Data Processing module instance, add the ++ * required heap sizes to the sum of all module instances belonging ++ * to the same pipeline, and find the maximum stack requirement ++ * among all module instances belonging to the same pipeline. ++ */ ++ if (swidget->comp_domain != SOF_COMP_DOMAIN_DP) { ++ pipe_widget->heap_bytes += swidget->heap_bytes; ++ if (pipe_widget->stack_bytes < swidget->stack_bytes) ++ pipe_widget->stack_bytes = swidget->stack_bytes; ++ ++ dev_dbg(sdev->dev, "%s mem reqs to %s heap %u stack %u", ++ swidget->widget->name, pipe_widget->widget->name, ++ pipe_widget->heap_bytes, pipe_widget->stack_bytes); ++ } ++ + /* Update base_config->cpc from the module manifest */ + sof_ipc4_update_cpc_from_manifest(sdev, fw_module, base_config); + +@@ -1688,6 +1745,8 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget) + pipe_widget = swidget->spipe->pipe_widget; + pipeline = pipe_widget->private; + pipeline->mem_usage = 0; ++ pipe_widget->heap_bytes = 0; ++ pipe_widget->stack_bytes = 0; + + if (WIDGET_IS_AIF(swidget->id) || swidget->id == snd_soc_dapm_buffer) { + if (pipeline->use_chain_dma) { +@@ -2065,10 +2124,57 @@ static void sof_ipc4_host_config(struct snd_sof_dev *sdev, struct snd_sof_widget + } + + static int +-sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, +- struct snd_pcm_hw_params *fe_params, +- struct snd_sof_platform_stream_params *platform_params, +- struct snd_pcm_hw_params *pipeline_params, int dir) ++sof_ipc4_copier_module_update_params(struct snd_sof_widget *swidget, ++ struct snd_pcm_hw_params *pipeline_params) ++{ ++ struct snd_soc_component *scomp = swidget->scomp; ++ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); ++ struct sof_ipc4_copier_data *copier_data; ++ struct sof_ipc4_copier *ipc4_copier; ++ ++ switch (swidget->id) { ++ case snd_soc_dapm_aif_in: ++ case snd_soc_dapm_aif_out: ++ case snd_soc_dapm_buffer: ++ ipc4_copier = swidget->private; ++ copier_data = &ipc4_copier->data; ++ break; ++ case snd_soc_dapm_dai_in: ++ case snd_soc_dapm_dai_out: ++ { ++ struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; ++ struct sof_ipc4_pipeline *pipeline = pipe_widget->private; ++ struct snd_sof_dai *dai; ++ ++ if (pipeline->use_chain_dma) ++ return 0; ++ ++ dai = swidget->private; ++ ++ ipc4_copier = (struct sof_ipc4_copier *)dai->private; ++ copier_data = &ipc4_copier->data; ++ ++ break; ++ } ++ default: ++ dev_err(sdev->dev, "unsupported type %d for copier %s", ++ swidget->id, swidget->widget->name); ++ return -EINVAL; ++ } ++ ++ /* modify the input params for the next widget */ ++ return sof_ipc4_update_hw_params(sdev, pipeline_params, ++ &copier_data->out_format, ++ BIT(SNDRV_PCM_HW_PARAM_FORMAT) | ++ BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | ++ BIT(SNDRV_PCM_HW_PARAM_RATE)); ++} ++ ++static int ++_sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, ++ struct snd_pcm_hw_params *fe_params, ++ struct snd_sof_platform_stream_params *platform_params, ++ struct snd_pcm_hw_params *pipeline_params, int dir) + { + struct sof_ipc4_available_audio_format *available_fmt; + struct snd_soc_component *scomp = swidget->scomp; +@@ -2540,6 +2646,21 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, + return 0; + } + ++static int ++sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, ++ struct snd_pcm_hw_params *fe_params, ++ struct snd_sof_platform_stream_params *platform_params, ++ struct snd_pcm_hw_params *pipeline_params, int dir) ++{ ++ if (swidget->prepared) ++ return sof_ipc4_copier_module_update_params(swidget, ++ pipeline_params); ++ ++ return _sof_ipc4_prepare_copier_module(swidget, fe_params, ++ platform_params, pipeline_params, ++ dir); ++} ++ + static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, + struct snd_pcm_hw_params *fe_params, + struct snd_sof_platform_stream_params *platform_params, +@@ -2553,6 +2674,10 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, + u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type; + int input_fmt_index, output_fmt_index; + ++ /* This cannot happen */ ++ if (unlikely(swidget->prepared)) ++ return 0; ++ + input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, + &gain->data.base_config, + pipeline_params, +@@ -2598,6 +2723,10 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget, + u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type; + int input_fmt_index, output_fmt_index; + ++ /* Already prepared, nothing to do */ ++ if (swidget->prepared) ++ return 0; ++ + input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, + &mixer->base_config, + pipeline_params, +@@ -2644,6 +2773,10 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, + u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type; + int output_fmt_index, input_fmt_index; + ++ /* This cannot happen */ ++ if (unlikely(swidget->prepared)) ++ return 0; ++ + input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, + &src->data.base_config, + pipeline_params, +@@ -2810,6 +2943,18 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, + int ret; + + if (available_fmt->num_input_formats) { ++ if (swidget->prepared) { ++ if (!available_fmt->num_output_formats) ++ return 0; ++ ++ /* modify the pipeline params with the output format */ ++ return sof_ipc4_update_hw_params(sdev, pipeline_params, ++ &process->output_format, ++ BIT(SNDRV_PCM_HW_PARAM_FORMAT) | ++ BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | ++ BIT(SNDRV_PCM_HW_PARAM_RATE)); ++ } ++ + input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, + &process->base_config, + pipeline_params, +@@ -2822,39 +2967,69 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, + if (available_fmt->num_output_formats) { + struct sof_ipc4_audio_format *in_fmt; + struct sof_ipc4_pin_format *pin_fmt; +- u32 out_ref_rate, out_ref_channels; +- int out_ref_valid_bits, out_ref_type; ++ u32 ref_rate, ref_channels; ++ int ref_valid_bits, ref_type; + + if (available_fmt->num_input_formats) { ++ /* ++ * The process module can change parameters and their operation ++ * depends on the direction: ++ * Playback: typically they have single output format. This is ++ * to 'force' the conversion from input to output. ++ * Use the input format as reference since the single ++ * format is going to be picked. ++ * Capture: typically they have multiple output formats to ++ * convert from dai (input) to FE (output) parameters. ++ * Use the input format as base and replace the param ++ * which is changed by the module with the FE parameter ++ * Reason: we can have module which changes the ++ * parameters in path, we cannot use the full ++ * FE param set for the module output lookup. ++ */ + in_fmt = &available_fmt->input_pin_fmts[input_fmt_index].audio_fmt; + +- out_ref_rate = in_fmt->sampling_frequency; +- out_ref_channels = ++ ref_rate = in_fmt->sampling_frequency; ++ ref_channels = + SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg); +- out_ref_valid_bits = ++ ref_valid_bits = + SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); +- out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg); ++ ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg); + } else { + /* for modules without input formats, use FE params as reference */ +- out_ref_rate = params_rate(fe_params); +- out_ref_channels = params_channels(fe_params); ++ ref_rate = params_rate(fe_params); ++ ref_channels = params_channels(fe_params); + ret = sof_ipc4_get_sample_type(sdev, fe_params); + if (ret < 0) + return ret; +- out_ref_type = (u32)ret; ++ ref_type = (u32)ret; + +- out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); +- if (out_ref_valid_bits < 0) +- return out_ref_valid_bits; ++ ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); ++ if (ref_valid_bits < 0) ++ return ref_valid_bits; + } + ++ if (dir == SNDRV_PCM_STREAM_CAPTURE) { ++ if (available_fmt->changed_params & BIT(SNDRV_PCM_HW_PARAM_RATE)) ++ ref_rate = params_rate(fe_params); ++ if (available_fmt->changed_params & BIT(SNDRV_PCM_HW_PARAM_CHANNELS)) ++ ref_channels = params_channels(fe_params); ++ if (available_fmt->changed_params & BIT(SNDRV_PCM_HW_PARAM_FORMAT)) { ++ ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); ++ if (ref_valid_bits < 0) ++ return ref_valid_bits; ++ ++ ref_type = sof_ipc4_get_sample_type(sdev, fe_params); ++ if (ref_type < 0) ++ return ref_type; ++ } ++ } + output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget, + &process->base_config, + available_fmt, +- out_ref_rate, +- out_ref_channels, +- out_ref_valid_bits, +- out_ref_type); ++ ref_rate, ++ ref_channels, ++ ref_valid_bits, ++ ref_type); + if (output_fmt_index < 0) + return output_fmt_index; + +@@ -2868,9 +3043,7 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, + /* modify the pipeline params with the output format */ + ret = sof_ipc4_update_hw_params(sdev, pipeline_params, + &process->output_format, +- BIT(SNDRV_PCM_HW_PARAM_FORMAT) | +- BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | +- BIT(SNDRV_PCM_HW_PARAM_RATE)); ++ available_fmt->changed_params); + if (ret) + return ret; + } +@@ -3085,27 +3258,47 @@ static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr + return 0; + } + +-static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev, +- struct snd_sof_widget *swidget, +- struct sof_ipc4_msg *msg, +- void *ipc_data, u32 ipc_size, +- void **new_data) ++static void sof_ipc4_add_init_ext_dp_memory_data(struct snd_sof_dev *sdev, ++ struct snd_sof_widget *swidget, ++ u32 *payload, u32 *ext_pos, ++ struct sof_ipc4_module_init_ext_object **hdr) ++{ ++ /* Add memory_data if comp_domain indicates DP */ ++ if (swidget->comp_domain == SOF_COMP_DOMAIN_DP) { ++ struct sof_ipc4_mod_init_ext_dp_memory_data *dp_mem_data; ++ ++ *hdr = (struct sof_ipc4_module_init_ext_object *)&payload[*ext_pos]; ++ (*hdr)->header = ++ SOF_IPC4_MOD_INIT_EXT_OBJ_ID(SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA) | ++ SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS(DIV_ROUND_UP(sizeof(*dp_mem_data), ++ sizeof(u32))); ++ *ext_pos += DIV_ROUND_UP(sizeof(**hdr), sizeof(u32)); ++ dp_mem_data = (struct sof_ipc4_mod_init_ext_dp_memory_data *)&payload[*ext_pos]; ++ dp_mem_data->domain_id = swidget->domain_id; ++ dp_mem_data->stack_bytes = swidget->stack_bytes; ++ dp_mem_data->heap_bytes = swidget->heap_bytes; ++ *ext_pos += DIV_ROUND_UP(sizeof(*dp_mem_data), sizeof(u32)); ++ } ++} ++ ++static int sof_ipc4_widget_mod_init_msg_payload(struct snd_sof_dev *sdev, ++ struct snd_sof_widget *swidget, ++ struct sof_ipc4_msg *msg, ++ void *ipc_data, u32 ipc_size, ++ void **new_data) + { +- struct sof_ipc4_mod_init_ext_dp_memory_data *dp_mem_data; + struct sof_ipc4_module_init_ext_init *ext_init; +- struct sof_ipc4_module_init_ext_object *hdr; ++ struct sof_ipc4_module_init_ext_object *hdr = NULL; + int new_size; + u32 *payload; + u32 ext_pos; + +- /* For the moment the only reason for adding init_ext_init payload is DP +- * memory data. If both stack and heap size are 0 (= use default), then +- * there is no need for init_ext_init payload. ++ /* ++ * Only DP widgets currently add init-ext objects here. Avoid allocating ++ * a max-sized payload buffer for widgets that will immediately return 0. + */ +- if (swidget->comp_domain != SOF_COMP_DOMAIN_DP) { +- msg->extension &= ~SOF_IPC4_MOD_EXT_EXTENDED_INIT_MASK; ++ if (swidget->comp_domain != SOF_COMP_DOMAIN_DP) + return 0; +- } + + payload = kzalloc(sdev->ipc->max_payload_size, GFP_KERNEL); + if (!payload) +@@ -3113,27 +3306,26 @@ static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev, + + /* Add ext_init first and set objects array flag to 1 */ + ext_init = (struct sof_ipc4_module_init_ext_init *)payload; +- ext_init->word0 |= SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_MASK; + ext_pos = DIV_ROUND_UP(sizeof(*ext_init), sizeof(u32)); + + /* Add object array objects after ext_init */ + +- /* Add dp_memory_data if comp_domain indicates DP */ +- if (swidget->comp_domain == SOF_COMP_DOMAIN_DP) { +- hdr = (struct sof_ipc4_module_init_ext_object *)&payload[ext_pos]; +- hdr->header = SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_MASK | +- SOF_IPC4_MOD_INIT_EXT_OBJ_ID(SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA) | +- SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS(DIV_ROUND_UP(sizeof(*dp_mem_data), +- sizeof(u32))); +- ext_pos += DIV_ROUND_UP(sizeof(*hdr), sizeof(u32)); +- dp_mem_data = (struct sof_ipc4_mod_init_ext_dp_memory_data *)&payload[ext_pos]; +- dp_mem_data->domain_id = swidget->dp_domain_id; +- dp_mem_data->stack_bytes = swidget->dp_stack_bytes; +- dp_mem_data->heap_bytes = swidget->dp_heap_bytes; +- ext_pos += DIV_ROUND_UP(sizeof(*dp_mem_data), sizeof(u32)); ++ sof_ipc4_add_init_ext_dp_memory_data(sdev, swidget, payload, &ext_pos, &hdr); ++ ++ /* Add following object array items here */ ++ ++ if (!hdr) { ++ /* ++ * NOTE: Remove this early bail out, when struct ++ * sof_ipc4_module_init_ext_init alone has some ++ * function. ++ */ ++ kfree(payload); ++ return 0; + } + +- /* If another array object is added, remember clear previous OBJ_LAST bit */ ++ ext_init->word0 |= SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_MASK; ++ hdr->header |= SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_MASK; + + /* Calculate final size and check that it fits to max payload size */ + new_size = ext_pos * sizeof(u32) + ipc_size; +@@ -3156,6 +3348,69 @@ static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev, + return new_size; + } + ++static void sof_ipc4_widget_pipe_ext_obj_memory_data(struct snd_sof_dev *sdev, ++ struct snd_sof_widget *swidget, ++ u32 *payload, u32 *ext_pos, ++ struct sof_ipc4_glb_pipe_ext_object **hdr) ++{ ++ struct sof_ipc4_glb_pipe_ext_obj_memory_data *mem_data; ++ ++ *hdr = (struct sof_ipc4_glb_pipe_ext_object *)&payload[*ext_pos]; ++ (*hdr)->header = ++ SOF_IPC4_GLB_PIPE_EXT_OBJ_ID(SOF_IPC4_GLB_PIPE_DATA_ID_MEM_DATA) | ++ SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS(DIV_ROUND_UP(sizeof(*mem_data), ++ sizeof(u32))); ++ *ext_pos += DIV_ROUND_UP(sizeof(**hdr), sizeof(u32)); ++ mem_data = (struct sof_ipc4_glb_pipe_ext_obj_memory_data *)&payload[*ext_pos]; ++ mem_data->domain_id = swidget->domain_id; ++ mem_data->stack_bytes = swidget->stack_bytes; ++ mem_data->heap_bytes = swidget->heap_bytes; ++ *ext_pos += DIV_ROUND_UP(sizeof(*mem_data), sizeof(u32)); ++ ++ dev_dbg(sdev->dev, ++ "%s; domain_id %u stack %u heap %u bytes", ++ swidget->widget->name, mem_data->domain_id, mem_data->stack_bytes, ++ mem_data->heap_bytes); ++} ++ ++static int sof_ipc4_widget_pipe_create_msg_payload(struct snd_sof_dev *sdev, ++ struct snd_sof_widget *swidget, ++ struct sof_ipc4_msg *msg, ++ void **new_data) ++{ ++ struct sof_ipc4_glb_pipe_payload *payload_hdr; ++ struct sof_ipc4_glb_pipe_ext_object *hdr = NULL; ++ u32 *payload; ++ u32 ext_pos; ++ ++ payload = kzalloc(sdev->ipc->max_payload_size, GFP_KERNEL); ++ if (!payload) ++ return -ENOMEM; ++ ++ /* Add sof_ipc4_glb_pipe_payload and set array bit to 1 */ ++ payload_hdr = (struct sof_ipc4_glb_pipe_payload *)payload; ++ payload_hdr->word0 |= SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_MASK; ++ ext_pos = DIV_ROUND_UP(sizeof(*payload_hdr), sizeof(u32)); ++ ++ sof_ipc4_widget_pipe_ext_obj_memory_data(sdev, swidget, payload, &ext_pos, &hdr); ++ /* Add following array objects here */ ++ ++ /* Mark end of object array */ ++ hdr->header |= SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_MASK; ++ ++ /* Put total payload size in words to the payload header */ ++ payload_hdr->word0 |= SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS(ext_pos); ++ *new_data = payload; ++ ++ /* Update msg extension bits according to the payload changes */ ++ msg->extension |= SOF_IPC4_GLB_PIPE_PAYLOAD_MASK; ++ ++ dev_dbg(sdev->dev, "%s: payload word0 %#x", swidget->widget->name, ++ payload_hdr->word0); ++ ++ return ext_pos * sizeof(int32_t); ++} ++ + static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) + { + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; +@@ -3342,8 +3597,8 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget + swidget->widget->name, swidget->pipeline_id, module_id, + swidget->instance_id, swidget->core); + +- ret = sof_ipc4_widget_setup_msg_payload(sdev, swidget, msg, ipc_data, ipc_size, +- &ext_data); ++ ret = sof_ipc4_widget_mod_init_msg_payload(sdev, swidget, msg, ipc_data, ipc_size, ++ &ext_data); + if (ret < 0) + goto fail; + +@@ -3355,6 +3610,17 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget + dev_dbg(sdev->dev, "Create pipeline %s (pipe %d) - instance %d, core %d\n", + swidget->widget->name, swidget->pipeline_id, + swidget->instance_id, swidget->core); ++ ++ msg->extension &= ~SOF_IPC4_GLB_PIPE_PAYLOAD_MASK; ++ ret = sof_ipc4_widget_pipe_create_msg_payload(sdev, swidget, msg, ++ &ext_data); ++ if (ret < 0) ++ goto fail; ++ ++ if (ret > 0) { ++ ipc_size = ret; ++ ipc_data = ext_data; ++ } + } + + msg->data_size = ipc_size; +@@ -3412,6 +3678,8 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget + swidget->widget->name); + + pipeline->mem_usage = 0; ++ swidget->heap_bytes = 0; ++ swidget->stack_bytes = 0; + pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED; + ida_free(&pipeline_ida, swidget->instance_id); + swidget->instance_id = -EINVAL; +diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h +--- a/sound/soc/sof/ipc4-topology.h ++++ b/sound/soc/sof/ipc4-topology.h +@@ -167,7 +167,7 @@ struct sof_ipc4_pipeline { + }; + + /** +- * struct sof_ipc4_multi_pipeline_data - multi pipeline trigger IPC data ++ * struct ipc4_pipeline_set_state_data - multi pipeline trigger IPC data + * @count: Number of pipelines to be triggered + * @pipeline_instance_ids: Flexible array of IDs of the pipelines to be triggered + */ +@@ -197,12 +197,15 @@ struct sof_ipc4_pin_format { + * @input_pin_fmts: Available input pin formats + * @num_input_formats: Number of input pin formats + * @num_output_formats: Number of output pin formats ++ * @changed_params: Mask of changed params by the module instance between it's ++ * input and output formts (rate, channels, depth) + */ + struct sof_ipc4_available_audio_format { + struct sof_ipc4_pin_format *output_pin_fmts; + struct sof_ipc4_pin_format *input_pin_fmts; + u32 num_input_formats; + u32 num_output_formats; ++ u32 changed_params; + }; + + /** +@@ -210,7 +213,7 @@ struct sof_ipc4_available_audio_format { + * @node_id: ID of Gateway Node + * @dma_buffer_size: Preferred Gateway DMA buffer size (in bytes) + * @config_length: Length of gateway node configuration blob specified in #config_data +- * config_data: Gateway node configuration blob ++ * @config_data: Gateway node configuration blob + */ + struct sof_copier_gateway_cfg { + uint32_t node_id; +@@ -285,7 +288,9 @@ struct sof_ipc4_dma_stream_ch_map { + struct sof_ipc4_dma_config { + uint8_t dma_method; + uint8_t pre_allocated_by_host; ++ /* private: */ + uint16_t rsvd; ++ /* public: */ + uint32_t dma_channel_id; + uint32_t stream_id; + struct sof_ipc4_dma_stream_ch_map dma_stream_channel_map; +@@ -296,7 +301,7 @@ struct sof_ipc4_dma_config { + #define SOF_IPC4_GTW_DMA_CONFIG_ID 0x1000 + + /** +- * struct sof_ipc4_dma_config: DMA configuration ++ * struct sof_ipc4_dma_config_tlv - DMA configuration + * @type: set to SOF_IPC4_GTW_DMA_CONFIG_ID + * @length: sizeof(struct sof_ipc4_dma_config) + dma_config.dma_priv_config_size + * @dma_config: actual DMA configuration +@@ -321,6 +326,7 @@ struct sof_ipc4_alh_configuration_blob { + * @data: IPC copier data + * @copier_config: Copier + blob + * @ipc_config_size: Size of copier_config ++ * @ipc_config_data: Copier module config data + * @available_fmt: Available audio format + * @frame_fmt: frame format + * @msg: message structure for copier +diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c +--- a/sound/soc/sof/ipc4.c ++++ b/sound/soc/sof/ipc4.c +@@ -289,6 +289,89 @@ static void sof_ipc4_dump_payload(struct snd_sof_dev *sdev, + 16, 4, ipc_data, size, false); + } + ++static const char *sof_ipc4_resource_type_str(u32 type) ++{ ++ switch (type) { ++ case SOF_IPC4_MODULE_INSTANCE: ++ return "resource: MODULE_INSTANCE"; ++ case SOF_IPC4_PIPELINE: ++ return "resource: PIPELINE"; ++ case SOF_IPC4_GATEWAY: ++ return "resource: GATEWAY"; ++ case SOF_IPC4_EDF_TASK: ++ return "resource: EDF_TASK"; ++ case SOF_IPC4_INVALID_RESOURCE_TYPE: ++ return "Resource is invalid"; ++ default: ++ return "Unknown resource type"; ++ } ++} ++ ++static const char *sof_ipc4_resource_event_type_str(u32 event_type) ++{ ++ switch (event_type) { ++ case SOF_IPC4_MIXER_UNDERRUN_DETECTED: ++ return "event: MIXER_UNDERRUN_DETECTED"; ++ case SOF_IPC4_PROCESS_DATA_ERROR: ++ return "event: PROCESS_DATA_ERROR"; ++ case SOF_IPC4_GATEWAY_UNDERRUN_DETECTED: ++ return "event: GATEWAY_UNDERRUN_DETECTED"; ++ case SOF_IPC4_GATEWAY_OVERRUN_DETECTED: ++ return "event: GATEWAY_OVERRUN_DETECTED"; ++ default: ++ return "Unknown event type"; ++ } ++} ++ ++static void sof_ipc4_resource_event_handler(struct snd_sof_dev *sdev, ++ struct sof_ipc4_msg *ipc4_msg) ++{ ++ struct sof_ipc4_notify_resource_data *data = ipc4_msg->data_ptr; ++ ++ /* Print event details */ ++ switch (data->event_type) { ++ case SOF_IPC4_MIXER_UNDERRUN_DETECTED: ++ dev_dbg(sdev->dev, "%s (%u): eos %u, mixed %u, expected %u\n", ++ sof_ipc4_resource_event_type_str(data->event_type), ++ data->event_type, data->data.mixer_underrun.eos_flag, ++ data->data.mixer_underrun.data_mixed, ++ data->data.mixer_underrun.expected_data_mixed); ++ break; ++ case SOF_IPC4_PROCESS_DATA_ERROR: ++ dev_dbg(sdev->dev, "%s (%u): error_code %#x\n", ++ sof_ipc4_resource_event_type_str(data->event_type), ++ data->event_type, data->data.process_data_error.error_code); ++ break; ++ case SOF_IPC4_GATEWAY_UNDERRUN_DETECTED: ++ case SOF_IPC4_GATEWAY_OVERRUN_DETECTED: ++ dev_dbg(sdev->dev, "%s (%u)\n", ++ sof_ipc4_resource_event_type_str(data->event_type), ++ data->event_type); ++ break; ++ default: ++ dev_dbg(sdev->dev, "%s (%u): raw dws %#x %#x %#x %#x %#x %#x\n", ++ sof_ipc4_resource_event_type_str(data->event_type), ++ data->event_type, ++ data->data.dws[0], data->data.dws[1], data->data.dws[2], ++ data->data.dws[3], data->data.dws[4], data->data.dws[5]); ++ break; ++ } ++ ++ /* Print resource details */ ++ if (data->resource_type == SOF_IPC4_MODULE_INSTANCE) { ++ u32 module_id = SOF_IPC4_MOD_ID_GET(data->resource_id); ++ u32 instance_id = SOF_IPC4_MOD_INSTANCE_GET(data->resource_id); ++ ++ dev_dbg(sdev->dev, "%s (%u), module_id %u, instance_id %u\n", ++ sof_ipc4_resource_type_str(data->resource_type), ++ data->resource_type, module_id, instance_id); ++ } else if (data->resource_type != SOF_IPC4_INVALID_RESOURCE_TYPE) { ++ dev_dbg(sdev->dev, "%s (%u), id %u\n", ++ sof_ipc4_resource_type_str(data->resource_type), ++ data->resource_type, data->resource_id); ++ } ++} ++ + static int sof_ipc4_get_reply(struct snd_sof_dev *sdev) + { + struct snd_sof_ipc_msg *msg = sdev->msg; +@@ -734,6 +817,7 @@ static void sof_ipc4_rx_msg(struct snd_sof_dev *sdev) + break; + case SOF_IPC4_NOTIFY_RESOURCE_EVENT: + data_size = sizeof(struct sof_ipc4_notify_resource_data); ++ handler_func = sof_ipc4_resource_event_handler; + break; + case SOF_IPC4_NOTIFY_LOG_BUFFER_STATUS: + sof_ipc4_mtrace_update_pos(sdev, SOF_IPC4_LOG_CORE_GET(ipc4_msg->primary)); +diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c +--- a/sound/soc/sof/nocodec.c ++++ b/sound/soc/sof/nocodec.c +@@ -21,12 +21,13 @@ static struct snd_soc_card sof_nocodec_card = { + static int sof_nocodec_bes_setup(struct device *dev, + struct snd_soc_dai_driver *drv, + struct snd_soc_dai_link *links, +- int link_num, struct snd_soc_card *card) ++ int link_num) + { ++ struct snd_soc_card *card = &sof_nocodec_card; + struct snd_soc_dai_link_component *dlc; + int i; + +- if (!drv || !links || !card) ++ if (!drv || !links) + return -EINVAL; + + /* set up BE dai_links */ +@@ -67,10 +68,10 @@ static int sof_nocodec_bes_setup(struct device *dev, + return 0; + } + +-static int sof_nocodec_setup(struct device *dev, +- u32 num_dai_drivers, +- struct snd_soc_dai_driver *dai_drivers) ++static int sof_nocodec_setup(struct device *dev, struct snd_soc_acpi_mach *mach) + { ++ u32 num_dai_drivers = mach->mach_params.num_dai_drivers; ++ struct snd_soc_dai_driver *dai_drivers = mach->mach_params.dai_drivers; + struct snd_soc_dai_link *links; + + /* create dummy BE dai_links */ +@@ -78,7 +79,7 @@ static int sof_nocodec_setup(struct device *dev, + if (!links) + return -ENOMEM; + +- return sof_nocodec_bes_setup(dev, dai_drivers, links, num_dai_drivers, &sof_nocodec_card); ++ return sof_nocodec_bes_setup(dev, dai_drivers, links, num_dai_drivers); + } + + static int sof_nocodec_probe(struct platform_device *pdev) +@@ -92,8 +93,7 @@ static int sof_nocodec_probe(struct platform_device *pdev) + + snd_soc_card_set_topology_name(card, "sof"); + +- ret = sof_nocodec_setup(card->dev, mach->mach_params.num_dai_drivers, +- mach->mach_params.dai_drivers); ++ ret = sof_nocodec_setup(card->dev, mach); + if (ret < 0) + return ret; + +diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c +--- a/sound/soc/sof/pcm.c ++++ b/sound/soc/sof/pcm.c +@@ -62,7 +62,7 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream) + * To avoid sending IPC before the previous IPC is handled, we + * schedule delayed work here to call the snd_pcm_period_elapsed(). + */ +- schedule_work(&spcm->stream[substream->stream].period_elapsed_work); ++ queue_work(system_highpri_wq, &spcm->stream[substream->stream].period_elapsed_work); + } + EXPORT_SYMBOL(snd_sof_pcm_period_elapsed); + +diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c +--- a/sound/soc/sof/sof-audio.c ++++ b/sound/soc/sof/sof-audio.c +@@ -501,9 +501,8 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget + !sof_widget_in_same_direction(swidget, dir)) + return 0; + +- /* skip widgets already prepared or aggregated DAI widgets*/ +- if (!widget_ops[widget->id].ipc_prepare || swidget->prepared || +- is_aggregated_dai(swidget)) ++ /* skip widgets aggregated DAI widgets */ ++ if (!widget_ops[widget->id].ipc_prepare || is_aggregated_dai(swidget)) + goto sink_prepare; + + /* prepare the source widget */ +diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h +--- a/sound/soc/sof/sof-audio.h ++++ b/sound/soc/sof/sof-audio.h +@@ -459,10 +459,10 @@ struct snd_sof_widget { + /* Scheduling domain (enum sof_comp_domain), unset, Low Latency, or Data Processing */ + u32 comp_domain; + +- /* The values below are added to mod_init pay load if comp_domain indicates DP component */ +- u32 dp_domain_id; /* DP process userspace domain ID */ +- u32 dp_stack_bytes; /* DP process stack size requirement in bytes */ +- u32 dp_heap_bytes; /* DP process heap size requirement in bytes */ ++ /* Module instance's memory configuration. */ ++ u32 domain_id; /* Module instance's userspace domain ID */ ++ u32 stack_bytes; /* Module instance's stack size requirement */ ++ u32 heap_bytes; /* Module instance's heap size requirement */ + + struct snd_soc_dapm_widget *widget; + struct list_head list; /* list in sdev widget list */ +diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c +--- a/sound/soc/sof/topology.c ++++ b/sound/soc/sof/topology.c +@@ -309,7 +309,7 @@ static const struct sof_dai_types sof_dais[] = { + {"ACPHS_VIRTUAL", SOF_DAI_AMD_HS_VIRTUAL}, + {"MICFIL", SOF_DAI_IMX_MICFIL}, + {"ACP_SDW", SOF_DAI_AMD_SDW}, +- ++ {"ACPTDM", SOF_DAI_AMD_I2S}, + }; + + static enum sof_ipc_dai_type find_dai(const char *name) +@@ -1108,7 +1108,7 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, + + full = NULL; + partial = NULL; +- list_for_each_entry(rtd, &card->rtd_list, list) { ++ for_each_card_rtds(card, rtd) { + /* does stream match DAI link ? */ + if (rtd->dai_link->stream_name) { + if (!strcmp(rtd->dai_link->stream_name, w->sname)) { +@@ -1173,7 +1173,7 @@ static void sof_disconnect_dai_widget(struct snd_soc_component *scomp, + else + return; + +- list_for_each_entry(rtd, &card->rtd_list, list) { ++ for_each_card_rtds(card, rtd) { + /* does stream match DAI link ? */ + if (!rtd->dai_link->stream_name || + !strstr(rtd->dai_link->stream_name, sname)) +@@ -1949,8 +1949,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ + private->array, le32_to_cpu(private->size)); + if (ret < 0) { + dev_err(scomp->dev, "Failed tp parse common DAI link tokens\n"); +- kfree(slink); +- return ret; ++ goto free_slink; + } + + token_list = tplg_ops ? tplg_ops->token_list : NULL; +@@ -2001,6 +2000,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ + case SOF_DAI_AMD_HS: + case SOF_DAI_AMD_SP_VIRTUAL: + case SOF_DAI_AMD_HS_VIRTUAL: ++ case SOF_DAI_AMD_I2S: + token_id = SOF_ACPI2S_TOKENS; + num_tuples += token_list[SOF_ACPI2S_TOKENS].count; + break; +@@ -2019,8 +2019,8 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ + /* allocate memory for tuples array */ + slink->tuples = kzalloc_objs(*slink->tuples, num_tuples); + if (!slink->tuples) { +- kfree(slink); +- return -ENOMEM; ++ ret = -ENOMEM; ++ goto free_slink; + } + + if (token_list[SOF_DAI_LINK_TOKENS].tokens) { +@@ -2076,6 +2076,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ + + err: + kfree(slink->tuples); ++free_slink: + kfree(slink); + + return ret; +@@ -2511,13 +2512,12 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_pdata *sof_pdata = sdev->pdata; + const char *tplg_filename_prefix = sof_pdata->tplg_filename_prefix; +- const struct firmware *fw; +- const char **tplg_files; + int tplg_cnt = 0; + int ret; + int i; + +- tplg_files = kcalloc(scomp->card->num_links, sizeof(char *), GFP_KERNEL); ++ const char **tplg_files __free(kfree) = ++ kcalloc(scomp->card->num_links, sizeof(char *), GFP_KERNEL); + if (!tplg_files) + return -ENOMEM; + +@@ -2543,10 +2543,8 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) + tplg_filename_prefix, + &tplg_files, + no_fallback); +- if (tplg_cnt < 0) { +- kfree(tplg_files); ++ if (tplg_cnt < 0) + return tplg_cnt; +- } + } + + /* +@@ -2557,8 +2555,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) + if (strstr(file, "dummy")) { + dev_err(scomp->dev, + "Function topology is required, please upgrade sof-firmware\n"); +- +- kfree(tplg_files); + return -EINVAL; + } + tplg_files[0] = file; +@@ -2573,6 +2569,7 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) + if (tplg_files[0] != file) + dev_info(scomp->dev, "loading topology %d: %s\n", i, tplg_files[i]); + ++ const struct firmware *fw __free(firmware) = NULL; + ret = request_firmware(&fw, tplg_files[i], scomp->dev); + if (ret < 0) { + /* +@@ -2591,8 +2588,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) + else + ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw); + +- release_firmware(fw); +- + if (ret < 0) { + dev_err(scomp->dev, "tplg %s component load failed %d\n", + tplg_files[i], ret); +@@ -2611,6 +2606,8 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) + goto out; + } + dev_info(scomp->dev, "loading feature topology %d: %s\n", i, feature_topology); ++ ++ const struct firmware *fw __free(firmware) = NULL; + ret = request_firmware(&fw, feature_topology, scomp->dev); + if (ret < 0) { + /* +@@ -2635,8 +2632,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) + else + ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw); + +- release_firmware(fw); +- + if (ret < 0) { + dev_err(scomp->dev, "feature tplg %s component load failed %d\n", + feature_topologies[i], ret); +@@ -2655,8 +2650,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) + if (ret >= 0 && sdev->led_present) + ret = snd_ctl_led_request(); + +- kfree(tplg_files); +- + return ret; + } + EXPORT_SYMBOL(snd_sof_load_topology); +diff --git a/sound/soc/sophgo/cv1800b-tdm.c b/sound/soc/sophgo/cv1800b-tdm.c +--- a/sound/soc/sophgo/cv1800b-tdm.c ++++ b/sound/soc/sophgo/cv1800b-tdm.c +@@ -558,7 +558,7 @@ static const struct snd_soc_dai_ops cv1800b_i2s_dai_ops = { + .set_sysclk = cv1800b_i2s_dai_set_sysclk, + }; + +-static const struct snd_soc_dai_driver cv1800b_i2s_dai_template = { ++static struct snd_soc_dai_driver cv1800b_i2s_dai_template = { + .name = "cv1800b-i2s", + .playback = { + .stream_name = "Playback", +@@ -636,7 +636,6 @@ static int cv1800b_i2s_probe(struct platform_device *pdev) + struct cv1800b_i2s *i2s; + struct resource *res; + void __iomem *regs; +- struct snd_soc_dai_driver *dai; + int ret; + + i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL); +@@ -666,21 +665,14 @@ static int cv1800b_i2s_probe(struct platform_device *pdev) + platform_set_drvdata(pdev, i2s); + cv1800b_i2s_setup_tdm(i2s); + +- dai = devm_kmemdup(dev, &cv1800b_i2s_dai_template, sizeof(*dai), +- GFP_KERNEL); +- if (!dai) +- return -ENOMEM; +- +- ret = devm_snd_soc_register_component(dev, &cv1800b_i2s_component, dai, +- 1); ++ ret = devm_snd_soc_register_component(dev, &cv1800b_i2s_component, ++ &cv1800b_i2s_dai_template, 1); + if (ret) + return ret; + + ret = devm_snd_dmaengine_pcm_register(dev, &cv1800b_i2s_pcm_config, 0); +- if (ret) { +- dev_err(dev, "dmaengine_pcm_register failed: %d\n", ret); ++ if (ret) + return ret; +- } + + return 0; + } +diff --git a/sound/soc/spacemit/k1_i2s.c b/sound/soc/spacemit/k1_i2s.c +--- a/sound/soc/spacemit/k1_i2s.c ++++ b/sound/soc/spacemit/k1_i2s.c +@@ -335,6 +335,11 @@ static int spacemit_i2s_dai_remove(struct snd_soc_dai *dai) + return 0; + } + ++static const u64 spacemit_i2s_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_B; ++ + static const struct snd_soc_dai_ops spacemit_i2s_dai_ops = { + .probe = spacemit_i2s_dai_probe, + .remove = spacemit_i2s_dai_remove, +@@ -343,6 +348,8 @@ static const struct snd_soc_dai_ops spacemit_i2s_dai_ops = { + .set_sysclk = spacemit_i2s_set_sysclk, + .set_fmt = spacemit_i2s_set_fmt, + .trigger = spacemit_i2s_trigger, ++ .auto_selectable_formats = &spacemit_i2s_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static struct snd_soc_dai_driver spacemit_i2s_dai = { +@@ -459,7 +466,7 @@ static int spacemit_i2s_probe(struct platform_device *pdev) + + i2s->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(i2s->base)) +- return dev_err_probe(i2s->dev, PTR_ERR(i2s->base), "failed to map registers\n"); ++ return PTR_ERR(i2s->base); + + i2s->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(i2s->reset)) +@@ -476,7 +483,7 @@ static int spacemit_i2s_probe(struct platform_device *pdev) + &spacemit_i2s_component, + dai, 1); + if (ret) +- return dev_err_probe(i2s->dev, ret, "failed to register component"); ++ return ret; + + return devm_snd_dmaengine_pcm_register(&pdev->dev, &spacemit_dmaengine_pcm_config, 0); + } +diff --git a/sound/soc/spear/spdif_in.c b/sound/soc/spear/spdif_in.c +--- a/sound/soc/spear/spdif_in.c ++++ b/sound/soc/spear/spdif_in.c +@@ -55,7 +55,7 @@ static int spdif_in_dai_probe(struct snd_soc_dai *dai) + struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai); + + host->dma_params_rx.filter_data = &host->dma_params; +- dai->capture_dma_data = &host->dma_params_rx; ++ snd_soc_dai_dma_data_set_capture(dai, &host->dma_params_rx); + + return 0; + } +@@ -150,12 +150,12 @@ static int spdif_in_trigger(struct snd_pcm_substream *substream, int cmd, + + static const struct snd_soc_dai_ops spdif_in_dai_ops = { + .shutdown = spdif_in_shutdown, ++ .probe = spdif_in_dai_probe, + .trigger = spdif_in_trigger, + .hw_params = spdif_in_hw_params, + }; + + static struct snd_soc_dai_driver spdif_in_dai = { +- .probe = spdif_in_dai_probe, + .capture = { + .channels_min = 2, + .channels_max = 2, +@@ -218,10 +218,8 @@ static int spdif_in_probe(struct platform_device *pdev) + + host->io_base = io_base; + host->irq = platform_get_irq(pdev, 0); +- if (host->irq < 0) { +- dev_warn(&pdev->dev, "failed to get IRQ: %d\n", host->irq); ++ if (host->irq < 0) + return host->irq; +- } + + host->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(host->clk)) +@@ -243,10 +241,8 @@ static int spdif_in_probe(struct platform_device *pdev) + + ret = devm_request_irq(&pdev->dev, host->irq, spdif_in_irq, 0, + "spdif-in", host); +- if (ret) { +- dev_warn(&pdev->dev, "request_irq failed\n"); ++ if (ret) + return ret; +- } + + ret = devm_snd_soc_register_component(&pdev->dev, &spdif_in_component, + &spdif_in_dai, 1); +diff --git a/sound/soc/spear/spdif_out.c b/sound/soc/spear/spdif_out.c +--- a/sound/soc/spear/spdif_out.c ++++ b/sound/soc/spear/spdif_out.c +@@ -249,6 +249,7 @@ static int spdif_soc_dai_probe(struct snd_soc_dai *dai) + } + + static const struct snd_soc_dai_ops spdif_out_dai_ops = { ++ .probe = spdif_soc_dai_probe, + .mute_stream = spdif_mute, + .startup = spdif_out_startup, + .shutdown = spdif_out_shutdown, +@@ -266,7 +267,6 @@ static struct snd_soc_dai_driver spdif_out_dai = { + SNDRV_PCM_RATE_192000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +- .probe = spdif_soc_dai_probe, + .ops = &spdif_out_dai_ops, + }; + +diff --git a/sound/soc/sprd/sprd-mcdt.c b/sound/soc/sprd/sprd-mcdt.c +--- a/sound/soc/sprd/sprd-mcdt.c ++++ b/sound/soc/sprd/sprd-mcdt.c +@@ -931,10 +931,8 @@ static int sprd_mcdt_probe(struct platform_device *pdev) + + ret = devm_request_irq(&pdev->dev, irq, sprd_mcdt_irq_handler, + 0, "sprd-mcdt", mcdt); +- if (ret) { +- dev_err(&pdev->dev, "Failed to request MCDT IRQ\n"); ++ if (ret) + return ret; +- } + + sprd_mcdt_init_chans(mcdt, res); + +diff --git a/sound/soc/sprd/sprd-pcm-dma.c b/sound/soc/sprd/sprd-pcm-dma.c +--- a/sound/soc/sprd/sprd-pcm-dma.c ++++ b/sound/soc/sprd/sprd-pcm-dma.c +@@ -469,8 +469,6 @@ static int sprd_soc_platform_probe(struct platform_device *pdev) + + ret = devm_snd_soc_register_component(&pdev->dev, &sprd_soc_component, + NULL, 0); +- if (ret) +- dev_err(&pdev->dev, "could not register platform:%d\n", ret); + + return ret; + } +diff --git a/sound/soc/starfive/jh7110_pwmdac.c b/sound/soc/starfive/jh7110_pwmdac.c +--- a/sound/soc/starfive/jh7110_pwmdac.c ++++ b/sound/soc/starfive/jh7110_pwmdac.c +@@ -477,25 +477,22 @@ static int jh7110_pwmdac_probe(struct platform_device *pdev) + &jh7110_pwmdac_component, + &jh7110_pwmdac_dai, 1); + if (ret) +- return dev_err_probe(&pdev->dev, ret, "failed to register dai\n"); ++ return ret; + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) +- return dev_err_probe(&pdev->dev, ret, "failed to register pcm\n"); ++ return ret; + + pm_runtime_enable(dev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = jh7110_pwmdac_runtime_resume(&pdev->dev); +- if (ret) +- goto err_pm_disable; ++ if (ret) { ++ pm_runtime_disable(&pdev->dev); ++ return ret; ++ } + } + + return 0; +- +-err_pm_disable: +- pm_runtime_disable(&pdev->dev); +- +- return ret; + } + + static void jh7110_pwmdac_remove(struct platform_device *pdev) +diff --git a/sound/soc/starfive/jh7110_tdm.c b/sound/soc/starfive/jh7110_tdm.c +--- a/sound/soc/starfive/jh7110_tdm.c ++++ b/sound/soc/starfive/jh7110_tdm.c +@@ -559,10 +559,8 @@ static int jh7110_tdm_clk_reset_get(struct platform_device *pdev, + tdm->clks[5].id = "tdm"; + + ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(tdm->clks), tdm->clks); +- if (ret) { +- dev_err(&pdev->dev, "Failed to get tdm clocks\n"); ++ if (ret) + return ret; +- } + + tdm->resets = devm_reset_control_array_get_exclusive(&pdev->dev); + if (IS_ERR(tdm->resets)) { +@@ -589,42 +587,33 @@ static int jh7110_tdm_probe(struct platform_device *pdev) + tdm->dev = &pdev->dev; + + ret = jh7110_tdm_clk_reset_get(pdev, tdm); +- if (ret) { +- dev_err(&pdev->dev, "Failed to enable audio-tdm clock\n"); ++ if (ret) + return ret; +- } + + jh7110_tdm_init_params(tdm); + + dev_set_drvdata(&pdev->dev, tdm); + ret = devm_snd_soc_register_component(&pdev->dev, &jh7110_tdm_component, + &jh7110_tdm_dai, 1); +- if (ret) { +- dev_err(&pdev->dev, "Failed to register dai\n"); ++ if (ret) + return ret; +- } + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, + &jh7110_dmaengine_pcm_config, + SND_DMAENGINE_PCM_FLAG_COMPAT); +- if (ret) { +- dev_err(&pdev->dev, "Could not register pcm: %d\n", ret); ++ if (ret) + return ret; +- } + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = jh7110_tdm_runtime_resume(&pdev->dev); +- if (ret) +- goto err_pm_disable; ++ if (ret) { ++ pm_runtime_disable(&pdev->dev); ++ return ret; ++ } + } + + return 0; +- +-err_pm_disable: +- pm_runtime_disable(&pdev->dev); +- +- return ret; + } + + static void jh7110_tdm_dev_remove(struct platform_device *pdev) +diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c +--- a/sound/soc/stm/stm32_adfsdm.c ++++ b/sound/soc/stm/stm32_adfsdm.c +@@ -360,11 +360,8 @@ static int stm32_adfsdm_probe(struct platform_device *pdev) + ret = devm_snd_soc_register_component(&pdev->dev, + &stm32_adfsdm_soc_platform, + NULL, 0); +- if (ret < 0) { +- dev_err(&pdev->dev, "%s: Failed to register PCM platform\n", +- __func__); ++ if (ret < 0) + return ret; +- } + + pm_runtime_enable(&pdev->dev); + +diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c +--- a/sound/soc/stm/stm32_i2s.c ++++ b/sound/soc/stm/stm32_i2s.c +@@ -1238,10 +1238,8 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev, + + ret = devm_request_irq(&pdev->dev, irq, stm32_i2s_isr, 0, + dev_name(&pdev->dev), i2s); +- if (ret) { +- dev_err(&pdev->dev, "irq request returned %d\n", ret); ++ if (ret) + return ret; +- } + + /* Reset */ + rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); +@@ -1295,7 +1293,7 @@ static int stm32_i2s_probe(struct platform_device *pdev) + + ret = snd_dmaengine_pcm_register(&pdev->dev, &stm32_i2s_pcm_config, 0); + if (ret) +- return dev_err_probe(&pdev->dev, ret, "PCM DMA register error\n"); ++ return ret; + + ret = snd_soc_register_component(&pdev->dev, &stm32_i2s_component, + i2s->dai_drv, 1); +diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c +--- a/sound/soc/stm/stm32_sai_sub.c ++++ b/sound/soc/stm/stm32_sai_sub.c +@@ -1696,19 +1696,15 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) + + ret = devm_request_irq(&pdev->dev, sai->pdata->irq, stm32_sai_isr, + IRQF_SHARED, dev_name(&pdev->dev), sai); +- if (ret) { +- dev_err(&pdev->dev, "IRQ request returned %d\n", ret); ++ if (ret) + goto err_unprepare_pclk; +- } + + if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) + conf = &stm32_sai_pcm_config_spdif; + + ret = snd_dmaengine_pcm_register(&pdev->dev, conf, 0); +- if (ret) { +- ret = dev_err_probe(&pdev->dev, ret, "Could not register pcm dma\n"); ++ if (ret) + goto err_unprepare_pclk; +- } + + ret = snd_soc_register_component(&pdev->dev, &stm32_component, + &sai->cpu_dai_drv, 1); +diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c +--- a/sound/soc/stm/stm32_spdifrx.c ++++ b/sound/soc/stm/stm32_spdifrx.c +@@ -970,10 +970,8 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) + + ret = devm_request_irq(&pdev->dev, spdifrx->irq, stm32_spdifrx_isr, 0, + dev_name(&pdev->dev), spdifrx); +- if (ret) { +- dev_err(&pdev->dev, "IRQ request returned %d\n", ret); ++ if (ret) + return ret; +- } + + rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); + if (IS_ERR(rst)) +@@ -987,7 +985,7 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) + pcm_config = &stm32_spdifrx_pcm_config; + ret = snd_dmaengine_pcm_register(&pdev->dev, pcm_config, 0); + if (ret) +- return dev_err_probe(&pdev->dev, ret, "PCM DMA register error\n"); ++ return ret; + + ret = snd_soc_register_component(&pdev->dev, + &stm32_spdifrx_component, +diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c +--- a/sound/soc/sunxi/sun4i-codec.c ++++ b/sound/soc/sunxi/sun4i-codec.c +@@ -22,13 +22,13 @@ + #include + + #include ++#include ++#include + #include + #include + #include + #include + #include +-#include +-#include + + /* Codec DAC digital controls and FIFO registers */ + #define SUN4I_CODEC_DAC_DPC (0x00) +@@ -2317,67 +2317,54 @@ static int sun4i_codec_probe(struct platform_device *pdev) + + scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base, + quirks->regmap_config); +- if (IS_ERR(scodec->regmap)) { +- dev_err(&pdev->dev, "Failed to create our regmap\n"); +- return PTR_ERR(scodec->regmap); +- } ++ if (IS_ERR(scodec->regmap)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->regmap), ++ "Failed to create our regmap\n"); + + /* Get the clocks from the DT */ + scodec->clk_apb = devm_clk_get_enabled(&pdev->dev, "apb"); +- if (IS_ERR(scodec->clk_apb)) { +- dev_err(&pdev->dev, "Failed to get the APB clock\n"); +- return PTR_ERR(scodec->clk_apb); +- } ++ if (IS_ERR(scodec->clk_apb)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->clk_apb), ++ "Failed to get the APB clock\n"); + + scodec->clk_module = devm_clk_get(&pdev->dev, "codec"); +- if (IS_ERR(scodec->clk_module)) { +- dev_err(&pdev->dev, "Failed to get the module clock\n"); +- return PTR_ERR(scodec->clk_module); +- } ++ if (IS_ERR(scodec->clk_module)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->clk_module), ++ "Failed to get the module clock\n"); + + if (quirks->has_reset) { + scodec->rst = devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL); +- if (IS_ERR(scodec->rst)) { +- dev_err(&pdev->dev, "Failed to get reset control\n"); +- return PTR_ERR(scodec->rst); +- } ++ if (IS_ERR(scodec->rst)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->rst), ++ "Failed to get reset control\n"); + } + + scodec->gpio_pa = devm_gpiod_get_optional(&pdev->dev, "allwinner,pa", + GPIOD_OUT_LOW); +- if (IS_ERR(scodec->gpio_pa)) { +- ret = PTR_ERR(scodec->gpio_pa); +- dev_err_probe(&pdev->dev, ret, "Failed to get pa gpio\n"); +- return ret; +- } ++ if (IS_ERR(scodec->gpio_pa)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->gpio_pa), ++ "Failed to get pa gpio\n"); ++ + + scodec->gpio_hp = devm_gpiod_get_optional(&pdev->dev, "hp-det", GPIOD_IN); +- if (IS_ERR(scodec->gpio_hp)) { +- ret = PTR_ERR(scodec->gpio_hp); +- dev_err_probe(&pdev->dev, ret, "Failed to get hp-det gpio\n"); +- return ret; +- } ++ if (IS_ERR(scodec->gpio_hp)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->gpio_hp), ++ "Failed to get hp-det gpio\n"); + + /* reg_field setup */ + scodec->reg_adc_fifoc = devm_regmap_field_alloc(&pdev->dev, + scodec->regmap, + quirks->reg_adc_fifoc); +- if (IS_ERR(scodec->reg_adc_fifoc)) { +- ret = PTR_ERR(scodec->reg_adc_fifoc); +- dev_err(&pdev->dev, "Failed to create regmap fields: %d\n", +- ret); +- return ret; +- } ++ if (IS_ERR(scodec->reg_adc_fifoc)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->reg_adc_fifoc), ++ "Failed to create regmap fields\n"); + + scodec->reg_dac_fifoc = devm_regmap_field_alloc(&pdev->dev, + scodec->regmap, + quirks->reg_dac_fifoc); +- if (IS_ERR(scodec->reg_dac_fifoc)) { +- ret = PTR_ERR(scodec->reg_dac_fifoc); +- dev_err(&pdev->dev, "Failed to create regmap fields: %d\n", +- ret); +- return ret; +- } ++ if (IS_ERR(scodec->reg_dac_fifoc)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->reg_dac_fifoc), ++ "Failed to create regmap fields\n"); + + /* DMA configuration for TX FIFO */ + scodec->playback_dma_data.addr = res->start + quirks->reg_dac_txdata; +@@ -2394,39 +2381,29 @@ static int sun4i_codec_probe(struct platform_device *pdev) + + ret = devm_snd_soc_register_component(&pdev->dev, quirks->codec, + &sun4i_codec_dai, 1); +- if (ret) { +- dev_err(&pdev->dev, "Failed to register our codec\n"); ++ if (ret) + return ret; +- } + + ret = devm_snd_soc_register_component(&pdev->dev, + &sun4i_codec_component, + &dummy_cpu_dai, 1); +- if (ret) { +- dev_err(&pdev->dev, "Failed to register our DAI\n"); ++ if (ret) + return ret; +- } + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); +- if (ret) { +- dev_err(&pdev->dev, "Failed to register against DMAEngine\n"); ++ if (ret) + return ret; +- } + + card = quirks->create_card(&pdev->dev); +- if (IS_ERR(card)) { +- ret = PTR_ERR(card); +- dev_err(&pdev->dev, "Failed to create our card\n"); +- return ret; +- } ++ if (IS_ERR(card)) ++ return PTR_ERR(card); + + snd_soc_card_set_drvdata(card, scodec); + + ret = snd_soc_register_card(card); +- if (ret) { +- dev_err_probe(&pdev->dev, ret, "Failed to register our card\n"); +- return ret; +- } ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, ++ "Failed to register our card\n"); + + return 0; + } +diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c +--- a/sound/soc/sunxi/sun4i-i2s.c ++++ b/sound/soc/sunxi/sun4i-i2s.c +@@ -1550,30 +1550,26 @@ static int sun4i_i2s_probe(struct platform_device *pdev) + } + + i2s->bus_clk = devm_clk_get(&pdev->dev, "apb"); +- if (IS_ERR(i2s->bus_clk)) { +- dev_err(&pdev->dev, "Can't get our bus clock\n"); +- return PTR_ERR(i2s->bus_clk); +- } ++ if (IS_ERR(i2s->bus_clk)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->bus_clk), ++ "Can't get our bus clock\n"); + + i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs, + i2s->variant->sun4i_i2s_regmap); +- if (IS_ERR(i2s->regmap)) { +- dev_err(&pdev->dev, "Regmap initialisation failed\n"); +- return PTR_ERR(i2s->regmap); +- } ++ if (IS_ERR(i2s->regmap)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->regmap), ++ "Regmap initialisation failed\n"); + + i2s->mod_clk = devm_clk_get(&pdev->dev, "mod"); +- if (IS_ERR(i2s->mod_clk)) { +- dev_err(&pdev->dev, "Can't get our mod clock\n"); +- return PTR_ERR(i2s->mod_clk); +- } ++ if (IS_ERR(i2s->mod_clk)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->mod_clk), ++ "Can't get our mod clock\n"); + + if (i2s->variant->has_reset) { + i2s->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); +- if (IS_ERR(i2s->rst)) { +- dev_err(&pdev->dev, "Failed to get reset control\n"); +- return PTR_ERR(i2s->rst); +- } ++ if (IS_ERR(i2s->rst)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->rst), ++ "Failed to get reset control\n"); + } + + if (!IS_ERR(i2s->rst)) { +diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c +--- a/sound/soc/sunxi/sun4i-spdif.c ++++ b/sound/soc/sunxi/sun4i-spdif.c +@@ -684,26 +684,23 @@ static int sun4i_spdif_probe(struct platform_device *pdev) + + host->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &sun4i_spdif_regmap_config); +- if (IS_ERR(host->regmap)) { +- dev_err(&pdev->dev, "failed to initialise regmap.\n"); +- return PTR_ERR(host->regmap); +- } ++ if (IS_ERR(host->regmap)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(host->regmap), ++ "failed to initialise regmap.\n"); + + /* Clocks */ + host->apb_clk = devm_clk_get(&pdev->dev, "apb"); +- if (IS_ERR(host->apb_clk)) { +- dev_err(&pdev->dev, "failed to get a apb clock.\n"); +- return PTR_ERR(host->apb_clk); +- } ++ if (IS_ERR(host->apb_clk)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(host->apb_clk), ++ "failed to get a apb clock.\n"); + + if (quirks->tx_clk_name) + tx_clk_name = quirks->tx_clk_name; + host->spdif_clk = devm_clk_get(&pdev->dev, tx_clk_name); +- if (IS_ERR(host->spdif_clk)) { +- dev_err(&pdev->dev, "failed to get the \"%s\" clock.\n", +- tx_clk_name); +- return PTR_ERR(host->spdif_clk); +- } ++ if (IS_ERR(host->spdif_clk)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(host->spdif_clk), ++ "failed to get the \"%s\" clock.\n", ++ tx_clk_name); + + host->dma_params_tx.addr = res->start + quirks->reg_dac_txdata; + host->dma_params_tx.maxburst = 8; +diff --git a/sound/soc/sunxi/sun50i-codec-analog.c b/sound/soc/sunxi/sun50i-codec-analog.c +--- a/sound/soc/sunxi/sun50i-codec-analog.c ++++ b/sound/soc/sunxi/sun50i-codec-analog.c +@@ -551,16 +551,13 @@ static int sun50i_codec_analog_probe(struct platform_device *pdev) + bool enable; + + base = devm_platform_ioremap_resource(pdev, 0); +- if (IS_ERR(base)) { +- dev_err(&pdev->dev, "Failed to map the registers\n"); ++ if (IS_ERR(base)) + return PTR_ERR(base); +- } + + regmap = sun8i_adda_pr_regmap_init(&pdev->dev, base); +- if (IS_ERR(regmap)) { +- dev_err(&pdev->dev, "Failed to create regmap\n"); +- return PTR_ERR(regmap); +- } ++ if (IS_ERR(regmap)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(regmap), ++ "Failed to create regmap\n"); + + enable = device_property_read_bool(&pdev->dev, + "allwinner,internal-bias-resistor"); +diff --git a/sound/soc/sunxi/sun8i-codec-analog.c b/sound/soc/sunxi/sun8i-codec-analog.c +--- a/sound/soc/sunxi/sun8i-codec-analog.c ++++ b/sound/soc/sunxi/sun8i-codec-analog.c +@@ -822,16 +822,13 @@ static int sun8i_codec_analog_probe(struct platform_device *pdev) + void __iomem *base; + + base = devm_platform_ioremap_resource(pdev, 0); +- if (IS_ERR(base)) { +- dev_err(&pdev->dev, "Failed to map the registers\n"); ++ if (IS_ERR(base)) + return PTR_ERR(base); +- } + + regmap = sun8i_adda_pr_regmap_init(&pdev->dev, base); +- if (IS_ERR(regmap)) { +- dev_err(&pdev->dev, "Failed to create regmap\n"); +- return PTR_ERR(regmap); +- } ++ if (IS_ERR(regmap)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(regmap), ++ "Failed to create regmap\n"); + + return devm_snd_soc_register_component(&pdev->dev, + &sun8i_codec_analog_cmpnt_drv, +diff --git a/sound/soc/tegra/tegra20_das.c b/sound/soc/tegra/tegra20_das.c +--- a/sound/soc/tegra/tegra20_das.c ++++ b/sound/soc/tegra/tegra20_das.c +@@ -167,10 +167,9 @@ static int tegra20_das_probe(struct platform_device *pdev) + + das->regmap = devm_regmap_init_mmio(&pdev->dev, regs, + &tegra20_das_regmap_config); +- if (IS_ERR(das->regmap)) { +- dev_err(&pdev->dev, "regmap init failed\n"); +- return PTR_ERR(das->regmap); +- } ++ if (IS_ERR(das->regmap)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(das->regmap), ++ "regmap init failed\n"); + + tegra20_das_connect_dap_to_dac(das, TEGRA20_DAS_DAP_ID_1, + TEGRA20_DAS_DAP_SEL_DAC1); +diff --git a/sound/soc/tegra/tegra210_adx.c b/sound/soc/tegra/tegra210_adx.c +--- a/sound/soc/tegra/tegra210_adx.c ++++ b/sound/soc/tegra/tegra210_adx.c +@@ -496,16 +496,12 @@ static struct snd_kcontrol_new tegra264_adx_controls[] = { + static int tegra210_adx_component_probe(struct snd_soc_component *component) + { + struct tegra210_adx *adx = snd_soc_component_get_drvdata(component); +- int err = 0; + +- if (adx->soc_data->num_controls) { +- err = snd_soc_add_component_controls(component, adx->soc_data->controls, +- adx->soc_data->num_controls); +- if (err) +- dev_err(component->dev, "can't add ADX controls, err: %d\n", err); +- } ++ if (adx->soc_data->num_controls) ++ return snd_soc_add_component_controls(component, adx->soc_data->controls, ++ adx->soc_data->num_controls); + +- return err; ++ return 0; + } + + static const struct snd_soc_component_driver tegra210_adx_cmpnt = { +diff --git a/sound/soc/tegra/tegra210_amx.c b/sound/soc/tegra/tegra210_amx.c +--- a/sound/soc/tegra/tegra210_amx.c ++++ b/sound/soc/tegra/tegra210_amx.c +@@ -504,16 +504,12 @@ static struct snd_kcontrol_new tegra264_amx_controls[] = { + static int tegra210_amx_component_probe(struct snd_soc_component *component) + { + struct tegra210_amx *amx = snd_soc_component_get_drvdata(component); +- int err = 0; + +- if (amx->soc_data->num_controls) { +- err = snd_soc_add_component_controls(component, amx->soc_data->controls, +- amx->soc_data->num_controls); +- if (err) +- dev_err(component->dev, "can't add AMX controls, err: %d\n", err); +- } ++ if (amx->soc_data->num_controls) ++ return snd_soc_add_component_controls(component, amx->soc_data->controls, ++ amx->soc_data->num_controls); + +- return err; ++ return 0; + } + + static const struct snd_soc_component_driver tegra210_amx_cmpnt = { +diff --git a/sound/soc/tegra/tegra210_i2s.c b/sound/soc/tegra/tegra210_i2s.c +--- a/sound/soc/tegra/tegra210_i2s.c ++++ b/sound/soc/tegra/tegra210_i2s.c +@@ -1090,13 +1090,13 @@ static int tegra210_i2s_probe(struct platform_device *pdev) + "can't retrieve I2S bit clock\n"); + + /* +- * Not an error, as this clock is needed only when some other I/O +- * requires input clock from current I2S instance, which is +- * configurable from DT. ++ * This clock is optional and is only needed when another I/O uses ++ * the current I2S instance as its input clock, as configured in DT. + */ +- i2s->clk_sync_input = devm_clk_get(dev, "sync_input"); ++ i2s->clk_sync_input = devm_clk_get_optional(dev, "sync_input"); + if (IS_ERR(i2s->clk_sync_input)) +- dev_dbg(dev, "can't retrieve I2S sync input clock\n"); ++ return dev_err_probe(dev, PTR_ERR(i2s->clk_sync_input), ++ "can't retrieve I2S sync input clock\n"); + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) +diff --git a/sound/soc/ti/ams-delta.c b/sound/soc/ti/ams-delta.c +--- a/sound/soc/ti/ams-delta.c ++++ b/sound/soc/ti/ams-delta.c +@@ -562,21 +562,21 @@ static int ams_delta_probe(struct platform_device *pdev) + + card->dev = &pdev->dev; + +- handset_mute = devm_gpiod_get(card->dev, "handset_mute", ++ handset_mute = devm_gpiod_get(&pdev->dev, "handset_mute", + GPIOD_OUT_HIGH); + if (IS_ERR(handset_mute)) + return PTR_ERR(handset_mute); + +- handsfree_mute = devm_gpiod_get(card->dev, "handsfree_mute", ++ handsfree_mute = devm_gpiod_get(&pdev->dev, "handsfree_mute", + GPIOD_OUT_HIGH); + if (IS_ERR(handsfree_mute)) + return PTR_ERR(handsfree_mute); + + ret = snd_soc_register_card(card); + if (ret) { +- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + card->dev = NULL; +- return ret; ++ return dev_err_probe(&pdev->dev, ret, ++ "snd_soc_register_card() failed\n"); + } + return 0; + } +diff --git a/sound/soc/ti/davinci-evm.c b/sound/soc/ti/davinci-evm.c +--- a/sound/soc/ti/davinci-evm.c ++++ b/sound/soc/ti/davinci-evm.c +@@ -245,9 +245,8 @@ static int davinci_evm_probe(struct platform_device *pdev) + + snd_soc_card_set_drvdata(&evm_soc_card, drvdata); + ret = devm_snd_soc_register_card(&pdev->dev, &evm_soc_card); +- + if (ret) { +- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); ++ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); + goto err_put; + } + +diff --git a/sound/soc/ti/j721e-evm.c b/sound/soc/ti/j721e-evm.c +--- a/sound/soc/ti/j721e-evm.c ++++ b/sound/soc/ti/j721e-evm.c +@@ -870,10 +870,9 @@ static int j721e_soc_probe(struct platform_device *pdev) + card->num_dapm_routes = ARRAY_SIZE(j721e_cpb_dapm_routes); + card->fully_routed = 1; + +- if (snd_soc_of_parse_card_name(card, "model")) { +- dev_err(&pdev->dev, "Card name is not provided\n"); +- return -ENODEV; +- } ++ ret = snd_soc_of_parse_card_name(card, "model"); ++ if (ret) ++ return ret; + + link_cnt = 0; + conf_cnt = 0; +diff --git a/sound/soc/ti/omap-abe-twl6040.c b/sound/soc/ti/omap-abe-twl6040.c +--- a/sound/soc/ti/omap-abe-twl6040.c ++++ b/sound/soc/ti/omap-abe-twl6040.c +@@ -234,16 +234,13 @@ static int omap_abe_probe(struct platform_device *pdev) + card->dapm_routes = audio_map; + card->num_dapm_routes = ARRAY_SIZE(audio_map); + +- if (snd_soc_of_parse_card_name(card, "ti,model")) { +- dev_err(&pdev->dev, "Card name is not provided\n"); +- return -ENODEV; +- } ++ ret = snd_soc_of_parse_card_name(card, "ti,model"); ++ if (ret) ++ return ret; + + ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing"); +- if (ret) { +- dev_err(&pdev->dev, "Error while parsing DAPM routing\n"); ++ if (ret) + return ret; +- } + + dai_node = of_parse_phandle(node, "ti,mcpdm", 0); + if (!dai_node) { +@@ -299,8 +296,8 @@ static int omap_abe_probe(struct platform_device *pdev) + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) +- dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n", +- ret); ++ dev_err_probe(&pdev->dev, ret, ++ "devm_snd_soc_register_card() failed\n"); + + return ret; + } +diff --git a/sound/soc/ti/omap-dmic.c b/sound/soc/ti/omap-dmic.c +--- a/sound/soc/ti/omap-dmic.c ++++ b/sound/soc/ti/omap-dmic.c +@@ -465,10 +465,9 @@ static int asoc_dmic_probe(struct platform_device *pdev) + mutex_init(&dmic->mutex); + + dmic->fclk = devm_clk_get(dmic->dev, "fck"); +- if (IS_ERR(dmic->fclk)) { +- dev_err(dmic->dev, "can't get fck\n"); +- return -ENODEV; +- } ++ if (IS_ERR(dmic->fclk)) ++ return dev_err_probe(dmic->dev, PTR_ERR(dmic->fclk), ++ "can't get fck\n"); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); + if (!res) { +diff --git a/sound/soc/ti/omap-hdmi.c b/sound/soc/ti/omap-hdmi.c +--- a/sound/soc/ti/omap-hdmi.c ++++ b/sound/soc/ti/omap-hdmi.c +@@ -24,8 +24,6 @@ + #define DRV_NAME "omap-hdmi-audio" + + struct hdmi_audio_data { +- struct snd_soc_card *card; +- + const struct omap_hdmi_audio_ops *ops; + struct device *dssdev; + struct snd_dmaengine_dai_dma_data dma_data; +@@ -375,12 +373,9 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev) + card->dev = dev; + + ret = devm_snd_soc_register_card(dev, card); +- if (ret) { +- dev_err(dev, "snd_soc_register_card failed (%d)\n", ret); +- return ret; +- } ++ if (ret) ++ return dev_err_probe(dev, ret, "snd_soc_register_card() failed\n"); + +- ad->card = card; + snd_soc_card_set_drvdata(card, ad); + + dev_set_drvdata(dev, ad); +diff --git a/sound/soc/ti/omap-twl4030.c b/sound/soc/ti/omap-twl4030.c +--- a/sound/soc/ti/omap-twl4030.c ++++ b/sound/soc/ti/omap-twl4030.c +@@ -17,7 +17,6 @@ + */ + + #include +-#include + #include + #include + +@@ -29,6 +28,7 @@ + #include "omap-mcbsp.h" + + struct omap_twl4030 { ++ struct snd_soc_jack_gpio hs_jack_gpio; + struct snd_soc_jack hs_jack; + }; + +@@ -124,38 +124,18 @@ static struct snd_soc_jack_pin hs_jack_pins[] = { + }, + }; + +-/* Headset jack detection gpios */ +-static struct snd_soc_jack_gpio hs_jack_gpios[] = { +- { +- .name = "ti,jack-det", +- .report = SND_JACK_HEADSET, +- .debounce_time = 200, +- }, +-}; +- +-static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm, +- int connected, char *pin) +-{ +- if (!connected) +- snd_soc_dapm_disable_pin(dapm, pin); +-} +- + static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd) + { + struct snd_soc_card *card = rtd->card; +- struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card); +- struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev); ++ + struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card); +- int ret = 0; ++ int ret; + + /* + * This is a bit of a hack, but the GPIO is optional so we + * only want to add the jack detection if the GPIO is there. + */ + if (of_property_present(card->dev->of_node, "ti,jack-det-gpio")) { +- hs_jack_gpios[0].gpiod_dev = card->dev; +- hs_jack_gpios[0].idx = 0; +- + ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack", + SND_JACK_HEADSET, + &priv->hs_jack, hs_jack_pins, +@@ -163,36 +143,19 @@ static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd) + if (ret) + return ret; + +- ret = snd_soc_jack_add_gpios(&priv->hs_jack, +- ARRAY_SIZE(hs_jack_gpios), +- hs_jack_gpios); ++ priv->hs_jack_gpio.name = "ti,jack-det"; ++ priv->hs_jack_gpio.report = SND_JACK_HEADSET; ++ priv->hs_jack_gpio.debounce_time = 200; ++ priv->hs_jack_gpio.gpiod_dev = card->dev; ++ priv->hs_jack_gpio.idx = 0; ++ ++ ret = snd_soc_jack_add_gpios(&priv->hs_jack, 1, ++ &priv->hs_jack_gpio); + if (ret) + return ret; + } + +- /* +- * NULL pdata means we booted with DT. In this case the routing is +- * provided and the card is fully routed, no need to mark pins. +- */ +- if (!pdata || !pdata->custom_routing) +- return ret; +- +- /* Disable not connected paths if not used */ +- twl4030_disconnect_pin(dapm, pdata->has_ear, "Earpiece Spk"); +- twl4030_disconnect_pin(dapm, pdata->has_hf, "Handsfree Spk"); +- twl4030_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone"); +- twl4030_disconnect_pin(dapm, pdata->has_predriv, "Ext Spk"); +- twl4030_disconnect_pin(dapm, pdata->has_carkit, "Carkit Spk"); +- +- twl4030_disconnect_pin(dapm, pdata->has_mainmic, "Main Mic"); +- twl4030_disconnect_pin(dapm, pdata->has_submic, "Sub Mic"); +- twl4030_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic"); +- twl4030_disconnect_pin(dapm, pdata->has_carkitmic, "Carkit Mic"); +- twl4030_disconnect_pin(dapm, pdata->has_digimic0, "Digital0 Mic"); +- twl4030_disconnect_pin(dapm, pdata->has_digimic1, "Digital1 Mic"); +- twl4030_disconnect_pin(dapm, pdata->has_linein, "Line In"); +- +- return ret; ++ return 0; + } + + /* Digital audio interface glue - connects codec <--> CPU */ +@@ -237,11 +200,15 @@ static struct snd_soc_card omap_twl4030_card = { + + static int omap_twl4030_probe(struct platform_device *pdev) + { +- struct omap_tw4030_pdata *pdata = dev_get_platdata(&pdev->dev); +- struct device_node *node = pdev->dev.of_node; + struct snd_soc_card *card = &omap_twl4030_card; ++ struct device_node *node, *dai_node; + struct omap_twl4030 *priv; +- int ret = 0; ++ struct property *prop; ++ int ret; ++ ++ node = pdev->dev.of_node; ++ if (!node) ++ return -ENODEV; + + card->dev = &pdev->dev; + +@@ -249,69 +216,52 @@ static int omap_twl4030_probe(struct platform_device *pdev) + if (priv == NULL) + return -ENOMEM; + +- if (node) { +- struct device_node *dai_node; +- struct property *prop; ++ ret = snd_soc_of_parse_card_name(card, "ti,model"); ++ if (ret) ++ return ret; + +- if (snd_soc_of_parse_card_name(card, "ti,model")) { +- dev_err(&pdev->dev, "Card name is not provided\n"); +- return -ENODEV; +- } +- +- dai_node = of_parse_phandle(node, "ti,mcbsp", 0); +- if (!dai_node) { +- dev_err(&pdev->dev, "McBSP node is not provided\n"); +- return -EINVAL; +- } +- omap_twl4030_dai_links[0].cpus->dai_name = NULL; +- omap_twl4030_dai_links[0].cpus->of_node = dai_node; +- +- omap_twl4030_dai_links[0].platforms->name = NULL; +- omap_twl4030_dai_links[0].platforms->of_node = dai_node; +- +- dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0); +- if (!dai_node) { +- card->num_links = 1; +- } else { +- omap_twl4030_dai_links[1].cpus->dai_name = NULL; +- omap_twl4030_dai_links[1].cpus->of_node = dai_node; +- +- omap_twl4030_dai_links[1].platforms->name = NULL; +- omap_twl4030_dai_links[1].platforms->of_node = dai_node; +- } +- +- /* Optional: audio routing can be provided */ +- prop = of_find_property(node, "ti,audio-routing", NULL); +- if (prop) { +- ret = snd_soc_of_parse_audio_routing(card, +- "ti,audio-routing"); +- if (ret) +- return ret; +- +- card->fully_routed = 1; +- } +- } else if (pdata) { +- if (pdata->card_name) { +- card->name = pdata->card_name; +- } else { +- dev_err(&pdev->dev, "Card name is not provided\n"); +- return -ENODEV; +- } +- +- if (!pdata->voice_connected) +- card->num_links = 1; +- } else { +- dev_err(&pdev->dev, "Missing pdata\n"); ++ if (!card->name) { ++ dev_err(&pdev->dev, "Card name is not provided\n"); + return -ENODEV; + } + ++ dai_node = of_parse_phandle(node, "ti,mcbsp", 0); ++ if (!dai_node) { ++ dev_err(&pdev->dev, "McBSP node is not provided\n"); ++ return -EINVAL; ++ } ++ omap_twl4030_dai_links[0].cpus->dai_name = NULL; ++ omap_twl4030_dai_links[0].cpus->of_node = dai_node; ++ ++ omap_twl4030_dai_links[0].platforms->name = NULL; ++ omap_twl4030_dai_links[0].platforms->of_node = dai_node; ++ ++ dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0); ++ if (!dai_node) { ++ card->num_links = 1; ++ } else { ++ omap_twl4030_dai_links[1].cpus->dai_name = NULL; ++ omap_twl4030_dai_links[1].cpus->of_node = dai_node; ++ ++ omap_twl4030_dai_links[1].platforms->name = NULL; ++ omap_twl4030_dai_links[1].platforms->of_node = dai_node; ++ } ++ ++ /* Optional: audio routing can be provided */ ++ prop = of_find_property(node, "ti,audio-routing", NULL); ++ if (prop) { ++ ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing"); ++ if (ret) ++ return ret; ++ ++ card->fully_routed = 1; ++ } ++ + snd_soc_card_set_drvdata(card, priv); + ret = devm_snd_soc_register_card(&pdev->dev, card); +- if (ret) { +- dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n", +- ret); +- return ret; +- } ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, ++ "devm_snd_soc_register_card() failed\n"); + + return 0; + } +diff --git a/sound/soc/ti/rx51.c b/sound/soc/ti/rx51.c +--- a/sound/soc/ti/rx51.c ++++ b/sound/soc/ti/rx51.c +@@ -358,21 +358,22 @@ static struct snd_soc_card rx51_sound_card = { + static int rx51_soc_probe(struct platform_device *pdev) + { + struct rx51_audio_pdata *pdata; +- struct device_node *np = pdev->dev.of_node; ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; + struct snd_soc_card *card = &rx51_sound_card; + int err; + + if (!of_machine_is_compatible("nokia,omap3-n900")) + return -ENODEV; + +- card->dev = &pdev->dev; ++ card->dev = dev; + + if (np) { + struct device_node *dai_node; + + dai_node = of_parse_phandle(np, "nokia,cpu-dai", 0); + if (!dai_node) { +- dev_err(card->dev, "McBSP node is not provided\n"); ++ dev_err(dev, "McBSP node is not provided\n"); + return -EINVAL; + } + rx51_dai[0].cpus->dai_name = NULL; +@@ -382,7 +383,7 @@ static int rx51_soc_probe(struct platform_device *pdev) + + dai_node = of_parse_phandle(np, "nokia,audio-codec", 0); + if (!dai_node) { +- dev_err(card->dev, "Codec node is not provided\n"); ++ dev_err(dev, "Codec node is not provided\n"); + return -EINVAL; + } + rx51_dai[0].codecs->name = NULL; +@@ -390,7 +391,7 @@ static int rx51_soc_probe(struct platform_device *pdev) + + dai_node = of_parse_phandle(np, "nokia,audio-codec", 1); + if (!dai_node) { +- dev_err(card->dev, "Auxiliary Codec node is not provided\n"); ++ dev_err(dev, "Auxiliary Codec node is not provided\n"); + return -EINVAL; + } + rx51_aux_dev[0].dlc.name = NULL; +@@ -400,7 +401,7 @@ static int rx51_soc_probe(struct platform_device *pdev) + + dai_node = of_parse_phandle(np, "nokia,headphone-amplifier", 0); + if (!dai_node) { +- dev_err(card->dev, "Headphone amplifier node is not provided\n"); ++ dev_err(dev, "Headphone amplifier node is not provided\n"); + return -EINVAL; + } + rx51_aux_dev[1].dlc.name = NULL; +@@ -409,40 +410,36 @@ static int rx51_soc_probe(struct platform_device *pdev) + rx51_codec_conf[1].dlc.of_node = dai_node; + } + +- pdata = devm_kzalloc(card->dev, sizeof(*pdata), GFP_KERNEL); ++ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (pdata == NULL) + return -ENOMEM; + + snd_soc_card_set_drvdata(card, pdata); + +- pdata->tvout_selection_gpio = devm_gpiod_get(card->dev, ++ pdata->tvout_selection_gpio = devm_gpiod_get(dev, + "tvout-selection", + GPIOD_OUT_LOW); +- if (IS_ERR(pdata->tvout_selection_gpio)) { +- dev_err(card->dev, "could not get tvout selection gpio\n"); +- return PTR_ERR(pdata->tvout_selection_gpio); +- } ++ if (IS_ERR(pdata->tvout_selection_gpio)) ++ return dev_err_probe(dev, PTR_ERR(pdata->tvout_selection_gpio), ++ "could not get tvout selection gpio\n"); + +- pdata->eci_sw_gpio = devm_gpiod_get(card->dev, "eci-switch", ++ pdata->eci_sw_gpio = devm_gpiod_get(dev, "eci-switch", + GPIOD_OUT_HIGH); +- if (IS_ERR(pdata->eci_sw_gpio)) { +- dev_err(card->dev, "could not get eci switch gpio\n"); +- return PTR_ERR(pdata->eci_sw_gpio); +- } ++ if (IS_ERR(pdata->eci_sw_gpio)) ++ return dev_err_probe(dev, PTR_ERR(pdata->eci_sw_gpio), ++ "could not get eci switch gpio\n"); + +- pdata->speaker_amp_gpio = devm_gpiod_get(card->dev, ++ pdata->speaker_amp_gpio = devm_gpiod_get(dev, + "speaker-amplifier", + GPIOD_OUT_LOW); +- if (IS_ERR(pdata->speaker_amp_gpio)) { +- dev_err(card->dev, "could not get speaker enable gpio\n"); +- return PTR_ERR(pdata->speaker_amp_gpio); +- } ++ if (IS_ERR(pdata->speaker_amp_gpio)) ++ return dev_err_probe(dev, PTR_ERR(pdata->speaker_amp_gpio), ++ "could not get speaker enable gpio\n"); + +- err = devm_snd_soc_register_card(card->dev, card); +- if (err) { +- dev_err(card->dev, "snd_soc_register_card failed (%d)\n", err); +- return err; +- } ++ err = devm_snd_soc_register_card(dev, card); ++ if (err) ++ return dev_err_probe(dev, err, ++ "snd_soc_register_card() failed\n"); + + return 0; + } +diff --git a/sound/soc/uniphier/aio-cpu.c b/sound/soc/uniphier/aio-cpu.c +--- a/sound/soc/uniphier/aio-cpu.c ++++ b/sound/soc/uniphier/aio-cpu.c +@@ -795,16 +795,12 @@ int uniphier_aio_probe(struct platform_device *pdev) + ret = devm_snd_soc_register_component(dev, &uniphier_aio_component, + chip->chip_spec->dais, + chip->chip_spec->num_dais); +- if (ret) { +- dev_err(dev, "Register component failed.\n"); ++ if (ret) + goto err_out_reset; +- } + + ret = uniphier_aiodma_soc_register_platform(pdev); +- if (ret) { +- dev_err(dev, "Register platform failed.\n"); ++ if (ret) + goto err_out_reset; +- } + + return 0; + +diff --git a/sound/soc/uniphier/aio.h b/sound/soc/uniphier/aio.h +--- a/sound/soc/uniphier/aio.h ++++ b/sound/soc/uniphier/aio.h +@@ -156,7 +156,7 @@ struct uniphier_aio_selector { + int hw; + }; + +-/** ++/* + * 'SoftWare MAPping' setting of UniPhier AIO registers. + * + * We have to setup 'virtual' register maps to access 'real' registers of AIO. +diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c +--- a/sound/soc/ux500/mop500.c ++++ b/sound/soc/ux500/mop500.c +@@ -68,10 +68,11 @@ static void mop500_of_node_put(void) + of_node_put(mop500_dai_links[0].codecs->of_node); + } + +-static int mop500_of_probe(struct platform_device *pdev, +- struct device_node *np) ++static int mop500_of_probe(struct snd_soc_card *card) + { ++ struct device *dev = card->dev; + struct device_node *codec_np, *msp_np[2]; ++ struct device_node *np = dev->of_node; + int i; + + msp_np[0] = of_parse_phandle(np, "stericsson,cpu-dai", 0); +@@ -79,7 +80,7 @@ static int mop500_of_probe(struct platform_device *pdev, + codec_np = of_parse_phandle(np, "stericsson,audio-codec", 0); + + if (!(msp_np[0] && msp_np[1] && codec_np)) { +- dev_err(&pdev->dev, "Phandle missing or invalid\n"); ++ dev_err(dev, "Phandle missing or invalid\n"); + for (i = 0; i < 2; i++) + of_node_put(msp_np[i]); + of_node_put(codec_np); +@@ -95,21 +96,20 @@ static int mop500_of_probe(struct platform_device *pdev, + mop500_dai_links[i].codecs->name = NULL; + } + +- snd_soc_of_parse_card_name(&mop500_card, "stericsson,card-name"); ++ snd_soc_of_parse_card_name(card, "stericsson,card-name"); + + return 0; + } + + static int mop500_probe(struct platform_device *pdev) + { +- struct device_node *np = pdev->dev.of_node; + int ret; + + dev_dbg(&pdev->dev, "%s: Enter.\n", __func__); + + mop500_card.dev = &pdev->dev; + +- ret = mop500_of_probe(pdev, np); ++ ret = mop500_of_probe(&mop500_card); + if (ret) + return ret; + +diff --git a/sound/soc/ux500/mop500_ab8500.c b/sound/soc/ux500/mop500_ab8500.c +--- a/sound/soc/ux500/mop500_ab8500.c ++++ b/sound/soc/ux500/mop500_ab8500.c +@@ -433,5 +433,5 @@ void mop500_ab8500_remove(struct snd_soc_card *card) + clk_put(drvdata->clk_ptr_ulpclk); + clk_put(drvdata->clk_ptr_intclk); + +- snd_soc_card_set_drvdata(card, drvdata); ++ snd_soc_card_set_drvdata(card, NULL); + } +diff --git a/sound/soc/xilinx/xlnx_formatter_pcm.c b/sound/soc/xilinx/xlnx_formatter_pcm.c +--- a/sound/soc/xilinx/xlnx_formatter_pcm.c ++++ b/sound/soc/xilinx/xlnx_formatter_pcm.c +@@ -9,8 +9,6 @@ + #include + #include + #include +-#include +-#include + #include + + #include +diff --git a/sound/soc/xilinx/xlnx_i2s.c b/sound/soc/xilinx/xlnx_i2s.c +--- a/sound/soc/xilinx/xlnx_i2s.c ++++ b/sound/soc/xilinx/xlnx_i2s.c +@@ -9,9 +9,8 @@ + + #include + #include +-#include +-#include + #include ++#include + #include + #include + +@@ -174,7 +173,6 @@ static int xlnx_i2s_probe(struct platform_device *pdev) + int ret; + u32 format; + struct device *dev = &pdev->dev; +- struct device_node *node = dev->of_node; + + drv_data = devm_kzalloc(&pdev->dev, sizeof(*drv_data), GFP_KERNEL); + if (!drv_data) +@@ -184,18 +182,16 @@ static int xlnx_i2s_probe(struct platform_device *pdev) + if (IS_ERR(drv_data->base)) + return PTR_ERR(drv_data->base); + +- ret = of_property_read_u32(node, "xlnx,num-channels", &drv_data->channels); +- if (ret < 0) { +- dev_err(dev, "cannot get supported channels\n"); +- return ret; +- } ++ ret = device_property_read_u32(dev, "xlnx,num-channels", &drv_data->channels); ++ if (ret < 0) ++ return dev_err_probe(dev, ret, "cannot get supported channels\n"); ++ + drv_data->channels *= 2; + +- ret = of_property_read_u32(node, "xlnx,dwidth", &drv_data->data_width); +- if (ret < 0) { +- dev_err(dev, "cannot get data width\n"); +- return ret; +- } ++ ret = device_property_read_u32(dev, "xlnx,dwidth", &drv_data->data_width); ++ if (ret < 0) ++ return dev_err_probe(dev, ret, "cannot get data width\n"); ++ + switch (drv_data->data_width) { + case 16: + format = SNDRV_PCM_FMTBIT_S16_LE; +@@ -207,7 +203,7 @@ static int xlnx_i2s_probe(struct platform_device *pdev) + return -EINVAL; + } + +- if (of_device_is_compatible(node, "xlnx,i2s-transmitter-1.0")) { ++ if (device_is_compatible(dev, "xlnx,i2s-transmitter-1.0")) { + drv_data->dai_drv.name = "xlnx_i2s_playback"; + drv_data->dai_drv.playback.stream_name = "Playback"; + drv_data->dai_drv.playback.formats = format; +@@ -215,7 +211,7 @@ static int xlnx_i2s_probe(struct platform_device *pdev) + drv_data->dai_drv.playback.channels_max = drv_data->channels; + drv_data->dai_drv.playback.rates = SNDRV_PCM_RATE_8000_192000; + drv_data->dai_drv.ops = &xlnx_i2s_dai_ops; +- } else if (of_device_is_compatible(node, "xlnx,i2s-receiver-1.0")) { ++ } else if (device_is_compatible(dev, "xlnx,i2s-receiver-1.0")) { + drv_data->dai_drv.name = "xlnx_i2s_capture"; + drv_data->dai_drv.capture.stream_name = "Capture"; + drv_data->dai_drv.capture.formats = format; +@@ -233,10 +229,8 @@ static int xlnx_i2s_probe(struct platform_device *pdev) + + ret = devm_snd_soc_register_component(&pdev->dev, &xlnx_i2s_component, + &drv_data->dai_drv, 1); +- if (ret) { +- dev_err(&pdev->dev, "i2s component registration failed\n"); ++ if (ret) + return ret; +- } + + dev_info(&pdev->dev, "%s DAI registered\n", drv_data->dai_drv.name); + +diff --git a/sound/soc/xilinx/xlnx_spdif.c b/sound/soc/xilinx/xlnx_spdif.c +--- a/sound/soc/xilinx/xlnx_spdif.c ++++ b/sound/soc/xilinx/xlnx_spdif.c +@@ -10,8 +10,7 @@ + #include + #include + #include +-#include +-#include ++#include + #include + #include + #include +@@ -242,28 +241,24 @@ static int xlnx_spdif_probe(struct platform_device *pdev) + struct spdif_dev_data *ctx; + + struct device *dev = &pdev->dev; +- struct device_node *node = dev->of_node; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->axi_clk = devm_clk_get_enabled(dev, "s_axi_aclk"); +- if (IS_ERR(ctx->axi_clk)) { +- ret = PTR_ERR(ctx->axi_clk); +- dev_err(dev, "failed to get s_axi_aclk(%d)\n", ret); +- return ret; +- } ++ if (IS_ERR(ctx->axi_clk)) ++ return dev_err_probe(dev, PTR_ERR(ctx->axi_clk), ++ "failed to get s_axi_aclk\n"); + + ctx->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ctx->base)) + return PTR_ERR(ctx->base); + +- ret = of_property_read_u32(node, "xlnx,spdif-mode", &ctx->mode); +- if (ret < 0) { +- dev_err(dev, "cannot get SPDIF mode\n"); +- return ret; +- } ++ ret = device_property_read_u32(dev, "xlnx,spdif-mode", &ctx->mode); ++ if (ret < 0) ++ return dev_err_probe(dev, ret, "cannot get SPDIF mode\n"); ++ + if (ctx->mode) { + dai_drv = &xlnx_spdif_tx_dai; + } else { +@@ -274,29 +269,23 @@ static int xlnx_spdif_probe(struct platform_device *pdev) + ret = devm_request_irq(dev, ret, + xlnx_spdifrx_irq_handler, + 0, "XLNX_SPDIF_RX", ctx); +- if (ret) { +- dev_err(dev, "spdif rx irq request failed\n"); +- return -ENODEV; +- } ++ if (ret) ++ return ret; + + init_waitqueue_head(&ctx->chsts_q); + dai_drv = &xlnx_spdif_rx_dai; + } + +- ret = of_property_read_u32(node, "xlnx,aud_clk_i", &ctx->aclk); +- if (ret < 0) { +- dev_err(dev, "cannot get aud_clk_i value\n"); +- return ret; +- } ++ ret = device_property_read_u32(dev, "xlnx,aud_clk_i", &ctx->aclk); ++ if (ret < 0) ++ return dev_err_probe(dev, ret, "cannot get aud_clk_i value\n"); + + dev_set_drvdata(dev, ctx); + + ret = devm_snd_soc_register_component(dev, &xlnx_spdif_component, + dai_drv, 1); +- if (ret) { +- dev_err(dev, "SPDIF component registration failed\n"); ++ if (ret) + return ret; +- } + + writel(XSPDIF_SOFT_RESET_VALUE, ctx->base + XSPDIF_SOFT_RESET_REG); + dev_info(dev, "%s DAI registered\n", dai_drv->name); +diff --git a/sound/soc/xtensa/xtfpga-i2s.c b/sound/soc/xtensa/xtfpga-i2s.c +--- a/sound/soc/xtensa/xtfpga-i2s.c ++++ b/sound/soc/xtensa/xtfpga-i2s.c +@@ -533,34 +533,25 @@ static int xtfpga_i2s_probe(struct platform_device *pdev) + int err, irq; + + i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); +- if (!i2s) { +- err = -ENOMEM; +- goto err; +- } ++ if (!i2s) ++ return -ENOMEM; ++ + platform_set_drvdata(pdev, i2s); + i2s->dev = &pdev->dev; + dev_dbg(&pdev->dev, "dev: %p, i2s: %p\n", &pdev->dev, i2s); + + i2s->regs = devm_platform_ioremap_resource(pdev, 0); +- if (IS_ERR(i2s->regs)) { +- err = PTR_ERR(i2s->regs); +- goto err; +- } ++ if (IS_ERR(i2s->regs)) ++ return PTR_ERR(i2s->regs); + + i2s->regmap = devm_regmap_init_mmio(&pdev->dev, i2s->regs, + &xtfpga_i2s_regmap_config); +- if (IS_ERR(i2s->regmap)) { +- dev_err(&pdev->dev, "regmap init failed\n"); +- err = PTR_ERR(i2s->regmap); +- goto err; +- } ++ if (IS_ERR(i2s->regmap)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->regmap), "regmap init failed\n"); + + i2s->clk = devm_clk_get(&pdev->dev, NULL); +- if (IS_ERR(i2s->clk)) { +- dev_err(&pdev->dev, "couldn't get clock\n"); +- err = PTR_ERR(i2s->clk); +- goto err; +- } ++ if (IS_ERR(i2s->clk)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->clk), "couldn't get clock\n"); + + regmap_write(i2s->regmap, XTFPGA_I2S_CONFIG, + (0x1 << XTFPGA_I2S_CONFIG_CHANNEL_BASE)); +@@ -568,41 +559,34 @@ static int xtfpga_i2s_probe(struct platform_device *pdev) + regmap_write(i2s->regmap, XTFPGA_I2S_INT_MASK, XTFPGA_I2S_INT_UNDERRUN); + + irq = platform_get_irq(pdev, 0); +- if (irq < 0) { +- err = irq; +- goto err; +- } ++ if (irq < 0) ++ return irq; ++ + err = devm_request_threaded_irq(&pdev->dev, irq, NULL, + xtfpga_i2s_threaded_irq_handler, + IRQF_SHARED | IRQF_ONESHOT, + pdev->name, i2s); +- if (err < 0) { +- dev_err(&pdev->dev, "request_irq failed\n"); +- goto err; +- } ++ if (err < 0) ++ return err; + + err = devm_snd_soc_register_component(&pdev->dev, + &xtfpga_i2s_component, + xtfpga_i2s_dai, + ARRAY_SIZE(xtfpga_i2s_dai)); +- if (err < 0) { +- dev_err(&pdev->dev, "couldn't register component\n"); +- goto err; +- } ++ if (err < 0) ++ return err; ++ + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + err = xtfpga_i2s_runtime_resume(&pdev->dev); +- if (err) +- goto err_pm_disable; ++ if (err) { ++ pm_runtime_disable(&pdev->dev); ++ return err; ++ } + } +- return 0; + +-err_pm_disable: +- pm_runtime_disable(&pdev->dev); +-err: +- dev_err(&pdev->dev, "%s: err = %d\n", __func__, err); +- return err; ++ return 0; + } + + static void xtfpga_i2s_remove(struct platform_device *pdev) diff --git a/pkgbuilds/linux-omarchy-bore/0511-asoc-updates.patch.sig b/pkgbuilds/linux-omarchy-bore/0511-asoc-updates.patch.sig new file mode 100644 index 0000000..a7f9893 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0511-asoc-updates.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0511-sound-updates-fixes.patch.sig b/pkgbuilds/linux-omarchy-bore/0511-sound-updates-fixes.patch.sig deleted file mode 100644 index 09b8d69..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/0511-sound-updates-fixes.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/0512-sound-fixes-1.patch b/pkgbuilds/linux-omarchy-bore/0512-sound-fixes-1.patch new file mode 100644 index 0000000..7de337b --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0512-sound-fixes-1.patch @@ -0,0 +1,661 @@ +diff --git a/include/sound/rawmidi.h b/include/sound/rawmidi.h +--- a/include/sound/rawmidi.h ++++ b/include/sound/rawmidi.h +@@ -179,7 +179,8 @@ int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info + int snd_rawmidi_kernel_open_nested(struct snd_rawmidi *rmidi, int subdevice, + int mode, struct snd_rawmidi_file *rfile, + int depth); +-int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile); ++int snd_rawmidi_kernel_release_nested(struct snd_rawmidi_file *rfile, ++ int depth); + int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream, + struct snd_rawmidi_params *params); + int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream, +@@ -201,6 +202,11 @@ static inline int snd_rawmidi_kernel_open(struct snd_rawmidi *rmidi, + return snd_rawmidi_kernel_open_nested(rmidi, subdevice, mode, rfile, 0); + } + ++static inline int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile) ++{ ++ return snd_rawmidi_kernel_release_nested(rfile, 0); ++} ++ + /* set up the tied devices */ + static inline void snd_rawmidi_tie_devices(struct snd_rawmidi *r1, + struct snd_rawmidi *r2) +diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c +--- a/sound/aoa/soundbus/i2sbus/core.c ++++ b/sound/aoa/soundbus/i2sbus/core.c +@@ -240,6 +240,8 @@ static int i2sbus_add_dev(struct macio_dev *macio, + } + for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) { + int irq = irq_of_parse_and_map(np, i); ++ if (!irq) ++ goto err; + if (request_irq(irq, ints[i], 0, dev->rnames[i], dev)) + goto err; + dev->interrupts[i] = irq; +diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c +--- a/sound/core/rawmidi.c ++++ b/sound/core/rawmidi.c +@@ -571,7 +571,6 @@ static void rawmidi_release_priv(struct snd_rawmidi_file *rfile) + struct snd_rawmidi *rmidi; + + rmidi = rfile->rmidi; +- guard(mutex)(&rmidi->open_mutex); + if (rfile->input) { + close_substream(rmidi, rfile->input, 1); + rfile->input = NULL; +@@ -585,7 +584,8 @@ static void rawmidi_release_priv(struct snd_rawmidi_file *rfile) + } + + /* called from sound/core/seq/seq_midi.c */ +-int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile) ++int snd_rawmidi_kernel_release_nested(struct snd_rawmidi_file *rfile, ++ int depth) + { + struct snd_rawmidi *rmidi; + +@@ -593,11 +593,13 @@ int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile) + return -ENXIO; + + rmidi = rfile->rmidi; ++ mutex_lock_nested(&rmidi->open_mutex, depth); + rawmidi_release_priv(rfile); ++ mutex_unlock(&rmidi->open_mutex); + module_put(rmidi->card->module); + return 0; + } +-EXPORT_SYMBOL(snd_rawmidi_kernel_release); ++EXPORT_SYMBOL(snd_rawmidi_kernel_release_nested); + + static int snd_rawmidi_release(struct inode *inode, struct file *file) + { +@@ -607,7 +609,8 @@ static int snd_rawmidi_release(struct inode *inode, struct file *file) + + rfile = file->private_data; + rmidi = rfile->rmidi; +- rawmidi_release_priv(rfile); ++ scoped_guard(mutex, &rmidi->open_mutex) ++ rawmidi_release_priv(rfile); + kfree(rfile); + module = rmidi->card->module; + snd_card_file_remove(rmidi->card, file); +diff --git a/sound/core/ump.c b/sound/core/ump.c +--- a/sound/core/ump.c ++++ b/sound/core/ump.c +@@ -1184,7 +1184,8 @@ static int snd_ump_legacy_close(struct snd_rawmidi_substream *substream) + ump->legacy_substreams[dir][group] = NULL; + if (dir == SNDRV_RAWMIDI_STREAM_OUTPUT) { + if (!--ump->legacy_out_opens) +- snd_rawmidi_kernel_release(&ump->legacy_out_rfile); ++ snd_rawmidi_kernel_release_nested(&ump->legacy_out_rfile, ++ SINGLE_DEPTH_NESTING); + } + return 0; + } +diff --git a/sound/hda/codecs/conexant.c b/sound/hda/codecs/conexant.c +--- a/sound/hda/codecs/conexant.c ++++ b/sound/hda/codecs/conexant.c +@@ -207,7 +207,7 @@ static void cx_remove(struct hda_codec *codec) + snd_hda_gen_remove(codec); + } + +-static void cx_process_headset_plugin(struct hda_codec *codec) ++static void cx_process_headset_detect_plug_type(struct hda_codec *codec) + { + unsigned int val; + unsigned int count = 0; +@@ -223,11 +223,9 @@ static void cx_process_headset_plugin(struct hda_codec *codec) + count++; + } while (count < 3); + val = snd_hda_codec_read(codec, 0x1c, 0, 0xcb0, 0x0); +- if (val & 0x800) { +- codec_dbg(codec, "headset plugin, type is CTIA\n"); +- snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24); +- } else if (val & 0x400) { +- codec_dbg(codec, "headset plugin, type is OMTP\n"); ++ if (val & 0xc00) { ++ codec_dbg(codec, "headset plugin, type is %s\n", ++ val & 0x800 ? "CTIA" : "OMTP"); + snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24); + } else { + codec_dbg(codec, "headphone plugin\n"); +@@ -243,10 +241,12 @@ static void cx_update_headset_mic_vref(struct hda_codec *codec, struct hda_jack_ + * Check hp&mic tag to process headset plugin & plugout. + */ + mic_present = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0x0); +- if (!(mic_present & AC_PINSENSE_PRESENCE)) /* mic plugout */ ++ if (!(mic_present & AC_PINSENSE_PRESENCE)) { /* mic plugout */ + snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20); +- else +- cx_process_headset_plugin(codec); ++ } else { ++ cx_process_headset_detect_plug_type(codec); ++ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24); ++ } + } + + static int cx_suspend(struct hda_codec *codec) +diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c +--- a/sound/hda/codecs/realtek/alc269.c ++++ b/sound/hda/codecs/realtek/alc269.c +@@ -1370,6 +1370,61 @@ static void alc269_fixup_hp_gpio_led(struct hda_codec *codec, + alc_fixup_hp_gpio_led(codec, action, 0x08, 0x10); + } + ++/* ++ * HP Laptop 15-fd0xxx (SSID 103c:8bb6) Speaker Mute LED fix ++ * ++ * The speaker mute LED is controlled via VREF100 on NID 0x1a. ++ * This pin must remain powered (D0) even during suspend, otherwise ++ * the LED state is lost and the pin defaults to Hi-Z on resume, ++ * causing the LED to stop responding to mute toggles. ++ * Windows keeps this pin powered unconditionally, so matching that ++ * behavior ensures consistent LED operation across suspend/resume. ++ * The mic-mute LED is controlled via GPIO 0 with active-low polarity. ++ */ ++static unsigned int hp_8bb6_power_filter(struct hda_codec *codec, ++ hda_nid_t nid, ++ unsigned int power_state) ++{ ++ if (nid == 0x1a) ++ return AC_PWRST_D0; ++ return snd_hda_gen_path_power_filter(codec, nid, power_state); ++} ++ ++static int hp_8bb6_speaker_mute_led_set(struct led_classdev *led_cdev, ++ enum led_brightness brightness) ++{ ++ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); ++ unsigned int val = (brightness == LED_OFF) ? PIN_IN : PIN_VREF100; ++ ++ snd_hda_set_pin_ctl_cache(codec, 0x1a, val); ++ return 0; ++} ++ ++static void alc236_fixup_hp_15_fd0xxx(struct hda_codec *codec, ++ const struct hda_fixup *fix, ++ int action) ++{ ++ struct alc_spec *spec = codec->spec; ++ ++ switch (action) { ++ case HDA_FIXUP_ACT_PRE_PROBE: ++ spec->micmute_led_polarity = 1; ++ alc_fixup_hp_gpio_led(codec, action, 0x00, 0x01); ++ ++ spec->mute_led_polarity = 0; ++ snd_hda_gen_add_mute_led_cdev(codec, hp_8bb6_speaker_mute_led_set); ++ ++ codec->power_filter = hp_8bb6_power_filter; ++ spec->no_shutup_pins = 1; ++ break; ++ ++ case HDA_FIXUP_ACT_INIT: ++ if (spec->gen.vmaster_mute.hook) ++ snd_hda_sync_vmaster_hook(&spec->gen.vmaster_mute); ++ break; ++ } ++} ++ + static void alc285_fixup_hp_gpio_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) + { +@@ -4089,6 +4144,7 @@ enum { + ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE, + ALC225_FIXUP_S3_POP_NOISE, + ALC700_FIXUP_INTEL_REFERENCE, ++ ALC700_FIXUP_INTEL_HADES_CANYON, + ALC274_FIXUP_DELL_BIND_DACS, + ALC274_FIXUP_DELL_AIO_LINEOUT_VERB, + ALC298_FIXUP_TPT470_DOCK_FIX, +@@ -4152,6 +4208,7 @@ enum { + ALC236_FIXUP_HP_GPIO_LED, + ALC236_FIXUP_HP_MUTE_LED, + ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF, ++ ALC236_FIXUP_HP_15_FD0XXX, + ALC236_FIXUP_LENOVO_INV_DMIC, + ALC298_FIXUP_SAMSUNG_AMP, + ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS, +@@ -4297,6 +4354,7 @@ enum { + ALC287_FIXUP_LENOVO_LEGION_AW88399, + ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN_HEADSET, + ALC285_LENOVO_DAC_RENAME, ++ ALC287_FIXUP_YOGA9_SPEAKER2_TO_DAC1, + }; + + /* A special fixup for Lenovo C940 and Yoga Duet 7; +@@ -4333,6 +4391,19 @@ static void alc287_fixup_lenovo_yoga_book_9i(struct hda_codec *codec, + __snd_hda_apply_fixup(codec, id, action, 0); + } + ++static void alc700_fixup_intel_hades_canyon(struct hda_codec *codec, ++ const struct hda_fixup *fix, ++ int action) ++{ ++ /* ++ * Windows may leave coef 0x1b bit 0x0400 cleared, causing broken ++ * analog playback after rebooting into Linux. Restore the bit ++ * during codec initialization. ++ */ ++ if (action == HDA_FIXUP_ACT_INIT) ++ alc_update_coef_idx(codec, 0x1b, 0x0400, 0x0400); ++} ++ + static const struct hda_fixup alc269_fixups[] = { + [ALC298_FIXUP_RAZER_BLADE16_2025_PINS] = { + .type = HDA_FIXUP_PINS, +@@ -5411,6 +5482,10 @@ static const struct hda_fixup alc269_fixups[] = { + {} + } + }, ++ [ALC700_FIXUP_INTEL_HADES_CANYON] = { ++ .type = HDA_FIXUP_FUNC, ++ .v.func = alc700_fixup_intel_hades_canyon, ++ }, + [ALC274_FIXUP_DELL_BIND_DACS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc274_fixup_bind_dacs, +@@ -5913,6 +5988,10 @@ static const struct hda_fixup alc269_fixups[] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc236_fixup_hp_mute_led_micmute_gpio, + }, ++ [ALC236_FIXUP_HP_15_FD0XXX] = { ++ .type = HDA_FIXUP_FUNC, ++ .v.func = alc236_fixup_hp_15_fd0xxx, ++ }, + [ALC236_FIXUP_LENOVO_INV_DMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_inv_dmic, +@@ -7010,6 +7089,12 @@ static const struct hda_fixup alc269_fixups[] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_lenovo_dac_rename, + }, ++ [ALC287_FIXUP_YOGA9_SPEAKER2_TO_DAC1] = { ++ .type = HDA_FIXUP_FUNC, ++ .v.func = alc285_fixup_speaker2_to_dac1, ++ .chained = true, ++ .chain_id = ALC287_FIXUP_TXNW2781_I2C, ++ }, + }; + + static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7448,6 +7533,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8bb3, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8bb4, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), ++ SND_PCI_QUIRK(0x103c, 0x8bb6, "HP Laptop 15-fd0039nt", ALC236_FIXUP_HP_15_FD0XXX), + SND_PCI_QUIRK(0x103c, 0x8bbe, "HP Victus 16-r0xxx (MB 8BBE)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8bc8, "HP Victus 15-fa1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8bcd, "HP Omen 16-xd0xxx", ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT), +@@ -8031,6 +8117,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x17aa, 0x3834, "Lenovo IdeaPad Slim 9i 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x383d, "Legion Y9000X 2019", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x3843, "Lenovo Yoga 9i / Yoga Book 9i", ALC287_FIXUP_LENOVO_YOGA_BOOK_9I), ++ SND_PCI_QUIRK(0x17aa, 0x3846, "Lenovo Yoga Pro 9 16IAH10", ALC287_FIXUP_YOGA9_SPEAKER2_TO_DAC1), + /* Yoga Pro 7 14IMH9 shares PCI SSID 17aa:3847 with Legion 7 16ACHG6; + * use codec SSID to distinguish them + */ +@@ -8044,14 +8131,15 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x17aa, 0x3852, "Lenovo Yoga 7 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x3853, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x3855, "Legion 7 16ITHG6", ALC287_FIXUP_LEGION_16ITHG6), ++ SND_PCI_QUIRK(0x17aa, 0x3862, "Lenovo IdeaPad Slim 3 15ABR8", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x3865, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x3866, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x3869, "Lenovo Yoga7 14IAL7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), + HDA_CODEC_QUIRK(0x17aa, 0x386a, "Lenovo Yoga 7 16IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), + HDA_CODEC_QUIRK(0x17aa, 0x386e, "Legion Y9000X 2022 IAH7", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x386e, "Yoga Pro 7 14ARP8", ALC285_FIXUP_YOGA_SPEAKER2_TO_DAC1), +- HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ + HDA_CODEC_QUIRK(0x17aa, 0x38a7, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ ++ HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ + SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x3870, "Lenovo Yoga 7 14ARB7", ALC287_FIXUP_YOGA7_14ARB7_I2C), + SND_PCI_QUIRK(0x17aa, 0x3874, "Legion 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), +@@ -8231,6 +8319,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x2782, 0xa128, "Positivo N15RPE-S", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x2782, 0xa212, "Lunnen Ground 14", ALC269VC_FIXUP_LUNNEN_GROUND_14), + SND_PCI_QUIRK(0x7017, 0x2014, "Star Labs StarFighter", ALC233_FIXUP_STARLABS_STARFIGHTER), ++ SND_PCI_QUIRK(0x8086, 0x2073, "Intel NUC 8 Hades Canyon", ++ ALC700_FIXUP_INTEL_HADES_CANYON), + SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), + SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED), + SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", ALC256_FIXUP_INTEL_NUC10), +diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c +--- a/sound/usb/mixer.c ++++ b/sound/usb/mixer.c +@@ -1237,21 +1237,27 @@ static void init_cur_mix_raw(struct usb_mixer_elem_info *cval, int ch, int idx) + } + + /* +- * Additional checks for sticky mixers ++ * Additional checks for sticky GET_CUR + * +- * Some devices' volume control mixers are sticky, which accept SET_CUR but +- * do absolutely nothing. ++ * Some devices' volume control mixers have sticky GET_CUR, which implies either ++ * stubbed SET_CUR or broken GET_CUR. For the former case, the mixer accepts ++ * SET_CUR but do absolutely nothing, so falling back to soft mixer is the only ++ * way to control the volume. For the latter case, the mixer has effective ++ * SET_CUR despite GET_CUR being constant, and the mixer is usable as long as we ++ * always provide mixer value from the ceche. + * + * Check the return values of GET_CUR with different SET_CUR values. Consider +- * the mixer as sticky if GET_CUR always returns a constant value. ++ * GET_CUR as sticky if GET_CUR always returns a constant value. + * +- * Some devices have effective SET_CUR despite GET_CUR being constant. Do not +- * consider the mixer as sticky if a quirk flag indicates that. ++ * Unfortunately, we can't distinguish between stubbed SET_CUR and broken ++ * GET_CUR with simple read-back tests. Disabling the mixer regardless and ++ * forcing userspace to use soft mixer instead can lead to audible distortion at ++ * low volume on some wireless headphones, probably due to their poorly- ++ * performed lossy codec. + * +- * Gate the registration of sticky mixers to prevent confusing userspace, so +- * that they won't cause ineffective volume control. However, for mixers with +- * effective SET_CUR but broken GET_CUR, the registration can continue normally +- * but further GET_CUR requests will be gated. ++ * Instead, mark GET_CUR as broken regardless and only provide mixer value from ++ * the cache. Users may opt into soft mixer in userspace audio stack if they ++ * need it. + */ + static int check_sticky_volume_control(struct usb_mixer_elem_info *cval, + int channel, int saved) +@@ -1271,24 +1277,13 @@ static int check_sticky_volume_control(struct usb_mixer_elem_info *cval, + return 0; + } + +- if (cval->head.mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_GET_CUR_BROKEN) { +- usb_audio_info(cval->head.mixer->chip, +- "%d:%d: broken mixer GET_CUR (%d/%d/%d => %d)\n", +- cval->head.id, mixer_ctrl_intf(cval->head.mixer), +- cval->min, cval->max, cval->res, saved); +- +- cval->get_cur_broken = 1; +- return -ENXIO; +- } +- +- usb_audio_err(cval->head.mixer->chip, +- "%d:%d: sticky mixer values (%d/%d/%d => %d), disabling\n", +- cval->head.id, mixer_ctrl_intf(cval->head.mixer), +- cval->min, cval->max, cval->res, saved); + usb_audio_info(cval->head.mixer->chip, +- "check MIXER_GET_CUR_BROKEN if you believe the mixer is non-sticky"); ++ "%d:%d: broken mixer GET_CUR (%d/%d/%d => %d)\n", ++ cval->head.id, mixer_ctrl_intf(cval->head.mixer), ++ cval->min, cval->max, cval->res, saved); + +- return -ENODEV; ++ cval->get_cur_broken = 1; ++ return -ENXIO; + } + + /* +@@ -1385,8 +1380,6 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, + goto no_checks; + + ret = check_sticky_volume_control(cval, minchn, saved); +- if (ret == -ENODEV) +- goto sticky; + if (ret) + goto no_checks; + +@@ -1454,34 +1447,15 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, + } + } + +- return 0; +- +-sticky: + /* +- * It makes no sense to restore the saved value for a sticky mixer, +- * since setting any value is a no-op. +- * +- * However, in some rare cases, SET_CUR is effective despite GET_CUR +- * always returns a constant value. These mixers are not sticky, but +- * there's no way to distinguish them. Without any additional +- * information, the best thing we can do is to set the mixer value to +- * the maximum before bailing out, so that a soft mixer can still reach +- * the maximum hardware volume if the mixer turns out to be non-sticky. +- * Meanwhile, all channels must be synchronized to prevent imbalance +- * volume. ++ * When GET_CUR is sticky, the saved value is bogus, so mixer values set ++ * by the sanity checks must be discarded through init_cur_mix_raw(). ++ * After that, we can clear the flag as per QUIRK_FLAG_MIXER_GET_CUR_OK. + */ +- if (!cval->cmask) { +- snd_usb_set_cur_mix_value(cval, 0, 0, cval->max); +- } else { +- idx = 0; +- for (i = 0; i < MAX_CHANNELS; i++) { +- if (cval->cmask & BIT(i)) { +- snd_usb_set_cur_mix_value(cval, i + 1, idx, cval->max); +- idx++; +- } +- } +- } +- return ret; ++ if (cval->head.mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_GET_CUR_OK) ++ cval->get_cur_broken = 0; ++ ++ return 0; + } + + #define get_min_max(cval, def) get_min_max_with_quirks(cval, def, NULL) +diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c +--- a/sound/usb/mixer_maps.c ++++ b/sound/usb/mixer_maps.c +@@ -505,13 +505,13 @@ static const struct usbmix_connector_map gigabyte_b450_connector_map[] = { + {} + }; + +-/* Audient iD14: FU 12 advertises Volume on only 4 of its 6 logical channels +- * and sits on the monitor mixer branch, but it is traced through to the +- * Speaker output terminal and gets named "Speaker Playback Volume". Userspace +- * then adopts it as the stream's hardware volume, and any setting below 0 dB +- * attenuates some channels but not others (20 dB imbalance at 80%). Give it a +- * non-standard name so that it is no longer taken for the stream's master +- * volume, while remaining reachable for anyone who wants the monitor gain. ++/* Audient iD14 MkI and MkII: FU 12 sits on the monitor mixer branch but is ++ * traced through to the Speaker output terminal, so it is named "Speaker ++ * Playback Volume". On MkII it controls only 4 of 6 playback channels. MkI ++ * testing found asymmetric attenuation within the main stereo pair. Userspace ++ * adopts this control as the stream's hardware volume, causing imbalance below ++ * 0 dB. Give it a non-standard name so that userspace no longer treats it as ++ * the stream master, while keeping the monitor gain reachable. + */ + static const struct usbmix_name_map audient_id14_map[] = { + { 12, "Monitor Mix Playback" }, /* FU, partial coverage */ +@@ -602,7 +602,12 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { + .map = maya44_map, + }, + { +- /* Audient iD14 */ ++ /* Audient iD14 MkI */ ++ .id = USB_ID(0x2708, 0x0002), ++ .map = audient_id14_map, ++ }, ++ { ++ /* Audient iD14 MkII */ + .id = USB_ID(0x2708, 0x0008), + .map = audient_id14_map, + }, +diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h +--- a/sound/usb/quirks-table.h ++++ b/sound/usb/quirks-table.h +@@ -1814,6 +1814,28 @@ YAMAHA_DEVICE(0x7010, "UB99"), + } + } + }, ++{ ++ /* ++ * M-Audio Venom ++ * ++ * The AudioControl interface times out on every GET_CUR request, ++ * which adds around 47 seconds to the card registration and ++ * freezes the device, blocking streaming. ++ * Using an explicit composite quirk to skip the mixer entirely. ++ */ ++ USB_DEVICE_VENDOR_SPEC(0x0763, 0x2084), ++ QUIRK_DRIVER_INFO { ++ .vendor_name = "M-Audio", ++ .product_name = "Venom", ++ QUIRK_DATA_COMPOSITE { ++ { QUIRK_DATA_IGNORE(0) }, ++ { QUIRK_DATA_STANDARD_AUDIO(1) }, ++ { QUIRK_DATA_STANDARD_AUDIO(2) }, ++ { QUIRK_DATA_STANDARD_MIDI(3) }, ++ QUIRK_COMPOSITE_END ++ } ++ } ++}, + + /* Casio devices */ + { +diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c +--- a/sound/usb/quirks.c ++++ b/sound/usb/quirks.c +@@ -2215,10 +2215,10 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), + DEVICE_FLG(0x03f0, 0x654a, /* HP 320 FHD Webcam */ + QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), +- DEVICE_FLG(0x040b, 0x0897, /* Weltrend Semiconductor, sold as Redragon H510-PRO Wireless headset */ +- QUIRK_FLAG_MIXER_GET_CUR_BROKEN), + DEVICE_FLG(0x041e, 0x3000, /* Creative SB Extigy */ + QUIRK_FLAG_IGNORE_CTL_ERROR), ++ DEVICE_FLG(0x041e, 0x324d, /* Creative Sound Blaster Play! 3 */ ++ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), + DEVICE_FLG(0x041e, 0x4080, /* Creative Live Cam VF0610 */ + QUIRK_FLAG_GET_SAMPLE_RATE), + DEVICE_FLG(0x045e, 0x083c, /* MS USB Link headset */ +@@ -2256,8 +2256,9 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + DEVICE_FLG(0x046d, 0x0a8f, /* Logitech H390 headset */ + QUIRK_FLAG_CTL_MSG_DELAY_1M | + QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), +- DEVICE_FLG(0x046d, 0x0af7, /* Logitech PRO X 2 LIGHTSPEED */ +- QUIRK_FLAG_MIXER_GET_CUR_BROKEN), ++ DEVICE_FLG(0x046d, 0x0aba, /* Logitech PRO X Wireless */ ++ QUIRK_FLAG_MIXER_GET_CUR_OK | ++ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), + DEVICE_FLG(0x0499, 0x1506, /* Yamaha THR5 */ + QUIRK_FLAG_GENERIC_IMPLICIT_FB), + DEVICE_FLG(0x0499, 0x1509, /* Steinberg UR22 */ +@@ -2329,9 +2330,11 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + DEVICE_FLG(0x0763, 0x2031, /* M-Audio Fast Track C600 */ + QUIRK_FLAG_GENERIC_IMPLICIT_FB), + DEVICE_FLG(0x0763, 0x2080, /* M-Audio Fast Track Ultra */ +- QUIRK_FLAG_MIXER_GET_CUR_BROKEN | QUIRK_FLAG_GENERIC_IMPLICIT_FB), ++ QUIRK_FLAG_GENERIC_IMPLICIT_FB), + DEVICE_FLG(0x0763, 0x2081, /* M-Audio Fast Track Ultra */ +- QUIRK_FLAG_MIXER_GET_CUR_BROKEN | QUIRK_FLAG_GENERIC_IMPLICIT_FB), ++ QUIRK_FLAG_GENERIC_IMPLICIT_FB), ++ DEVICE_FLG(0x0763, 0x2084, /* M-Audio Venom */ ++ QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_DISABLE_AUTOSUSPEND), + DEVICE_FLG(0x07fd, 0x000b, /* MOTU M Series 2nd hardware revision */ + QUIRK_FLAG_CTL_MSG_DELAY_1M), + DEVICE_FLG(0x08bb, 0x2702, /* LineX FM Transmitter */ +@@ -2370,8 +2373,6 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER), + DEVICE_FLG(0x1038, 0x1294, /* SteelSeries Arctis Pro Wireless */ + QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), +- DEVICE_FLG(0x1038, 0x2232, /* SteelSeries Arctis Nova 5 */ +- QUIRK_FLAG_MIXER_GET_CUR_BROKEN), + DEVICE_FLG(0x1101, 0x0003, /* Audioengine D1 */ + QUIRK_FLAG_GET_SAMPLE_RATE), + DEVICE_FLG(0x12d1, 0x3a07, /* HUAWEI USB-C HEADSET */ +@@ -2379,8 +2380,6 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), + DEVICE_FLG(0x1224, 0x2a25, /* Jieli Technology USB PHY 2.0 */ + QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), +- DEVICE_FLG(0x1377, 0x6004, /* Sennheiser MOMENTUM 3 */ +- QUIRK_FLAG_MIXER_GET_CUR_BROKEN), + DEVICE_FLG(0x1395, 0x740a, /* Sennheiser DECT */ + QUIRK_FLAG_GET_SAMPLE_RATE), + DEVICE_FLG(0x1397, 0x0507, /* Behringer UMC202HD */ +@@ -2393,6 +2392,9 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + QUIRK_FLAG_IFB_SILENCE_ON_EMPTY), + DEVICE_FLG(0x13e5, 0x0001, /* Serato Phono */ + QUIRK_FLAG_IGNORE_CTL_ERROR), ++ DEVICE_FLG(0x152a, 0x85dd, /* SMSL USB DAC */ ++ QUIRK_FLAG_DSD_RAW | QUIRK_FLAG_DISABLE_AUTOSUSPEND | ++ QUIRK_FLAG_SKIP_IFACE_SETUP), + DEVICE_FLG(0x152a, 0x880a, /* NeuralDSP Quad Cortex */ + 0), /* Doesn't have the vendor quirk which would otherwise apply */ + DEVICE_FLG(0x1532, 0x055e, /* Razer Nommo V2 X */ +@@ -2505,8 +2507,6 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + QUIRK_FLAG_CTL_MSG_DELAY_1M), + DEVICE_FLG(0x2d99, 0x0026, /* HECATE G2 GAMING HEADSET */ + QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), +- DEVICE_FLG(0x2d99, 0xa024, /* Edifier MF200 */ +- QUIRK_FLAG_MIXER_GET_CUR_BROKEN), + DEVICE_FLG(0x2fc6, 0xf06b, /* MOONDROP Moonriver2 Ti */ + QUIRK_FLAG_CTL_MSG_DELAY), + DEVICE_FLG(0x2fc6, 0xf0b5, /* iBasso DC-Elite */ +@@ -2647,7 +2647,7 @@ static const char *const snd_usb_audio_quirk_flag_names[] = { + QUIRK_STRING_ENTRY(MIXER_PLAYBACK_LINEAR_VOL), + QUIRK_STRING_ENTRY(MIXER_CAPTURE_LINEAR_VOL), + QUIRK_STRING_ENTRY(IFB_SILENCE_ON_EMPTY), +- QUIRK_STRING_ENTRY(MIXER_GET_CUR_BROKEN), ++ QUIRK_STRING_ENTRY(MIXER_GET_CUR_OK), + QUIRK_STRING_ENTRY(PLAYBACK_URB_FIXUP), + QUIRK_STRING_ENTRY(ALWAYS_SET_RATE), + NULL +diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h +--- a/sound/usb/usbaudio.h ++++ b/sound/usb/usbaudio.h +@@ -243,24 +243,18 @@ extern bool snd_usb_skip_validation; + * from snd_usb_handle_sync_urb. Instead fall through and enqueue a + * packet_info containing only size-0 packets, so the OUT ring keeps + * moving (emits silence). Needed by Behringer Flow 8 (1397:050c). +- * QUIRK_FLAG_MIXER_GET_CUR_BROKEN +- * Some mixers are sticky, which means that setting their current volume is a +- * no-op, and reading the current volume returns a constant value. The sticky +- * check disables these mixers to prevent confusing userspace. However, some +- * devices do have a tunable volume despite the reported current volume being +- * constant. As the sticky check can't distinguish between the two categories, +- * setting this flag tells that the device should fall into the second +- * category when GET_CUR returns a constant value, resulting in the sticky +- * check being non-fatal and only disabling GET_CUR instead of the whole mixer. +- * The current volume will then be provided by the internal cache that stores +- * the last set volume ++ * QUIRK_FLAG_MIXER_GET_CUR_OK ++ * On some devices, whether their GET_CUR being sticky depends on whether ++ * hotpluggable components are present. When the hotpluggable components are ++ * missing on probe, their GET_CUR behavior is classified as broken. Set the ++ * flag to prevent the heuristics from gating GET_CUR. + * QUIRK_FLAG_PLAYBACK_URB_FIXUP + * Set URB_ISO_ASAP flag for isochronous URBs and force nurbs to MAX_URBS. + * This is needed for devices that exhibit boot-time audio stuttering due + * to insufficient buffer depth combined with xHCI scheduling variability. + * The larger buffer (MAX_URBS = 12, ~64ms) absorbs system scheduling + * jitter during boot, while URB_ISO_ASAP ensures consistent xHCI scheduling. +- * QUIRK_FLAG_ALWAYS_SET_RATE: ++ * QUIRK_FLAG_ALWAYS_SET_RATE + * Issue SET_CUR for the sample rate even when the clock already reports the + * requested rate. A device advertising a single rate is otherwise never sent + * the request at all, and some require it before streaming will start. +@@ -297,10 +291,10 @@ enum { + QUIRK_TYPE_MIXER_PLAYBACK_LINEAR_VOL = 27, + QUIRK_TYPE_MIXER_CAPTURE_LINEAR_VOL = 28, + QUIRK_TYPE_IFB_SILENCE_ON_EMPTY = 29, +- QUIRK_TYPE_MIXER_GET_CUR_BROKEN = 30, ++ QUIRK_TYPE_MIXER_GET_CUR_OK = 30, + QUIRK_TYPE_PLAYBACK_URB_FIXUP = 31, + QUIRK_TYPE_ALWAYS_SET_RATE = 32, +-/* Please also edit snd_usb_audio_quirk_flag_names */ ++/* Please also edit snd_usb_audio_quirk_flag_names and alsa-configuration.rst */ + }; + + #define QUIRK_FLAG(x) BIT_U64(QUIRK_TYPE_ ## x) +@@ -335,7 +329,7 @@ enum { + #define QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL QUIRK_FLAG(MIXER_PLAYBACK_LINEAR_VOL) + #define QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL QUIRK_FLAG(MIXER_CAPTURE_LINEAR_VOL) + #define QUIRK_FLAG_IFB_SILENCE_ON_EMPTY QUIRK_FLAG(IFB_SILENCE_ON_EMPTY) +-#define QUIRK_FLAG_MIXER_GET_CUR_BROKEN QUIRK_FLAG(MIXER_GET_CUR_BROKEN) ++#define QUIRK_FLAG_MIXER_GET_CUR_OK QUIRK_FLAG(MIXER_GET_CUR_OK) + #define QUIRK_FLAG_PLAYBACK_URB_FIXUP QUIRK_FLAG(PLAYBACK_URB_FIXUP) + #define QUIRK_FLAG_ALWAYS_SET_RATE QUIRK_FLAG(ALWAYS_SET_RATE) + diff --git a/pkgbuilds/linux-omarchy-bore/0512-sound-fixes-1.patch.sig b/pkgbuilds/linux-omarchy-bore/0512-sound-fixes-1.patch.sig new file mode 100644 index 0000000..6645ee8 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0512-sound-fixes-1.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0512-sound-fixes.patch.sig b/pkgbuilds/linux-omarchy-bore/0512-sound-fixes.patch.sig deleted file mode 100644 index c02c1df..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/0512-sound-fixes.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/0511-sound-updates-fixes.patch b/pkgbuilds/linux-omarchy-bore/0513-asoc-fixes-1.patch similarity index 51% rename from pkgbuilds/linux-omarchy-bore/0511-sound-updates-fixes.patch rename to pkgbuilds/linux-omarchy-bore/0513-asoc-fixes-1.patch index c32b5c3..49c1eb3 100644 --- a/pkgbuilds/linux-omarchy-bore/0511-sound-updates-fixes.patch +++ b/pkgbuilds/linux-omarchy-bore/0513-asoc-fixes-1.patch @@ -64,400 +64,9 @@ diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bin #define LPASS_HW_AVTIMER_VOTE 101 #define LPASS_HW_MACRO_VOTE 102 -diff --git a/include/sound/rawmidi.h b/include/sound/rawmidi.h ---- a/include/sound/rawmidi.h -+++ b/include/sound/rawmidi.h -@@ -179,7 +179,8 @@ int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info - int snd_rawmidi_kernel_open_nested(struct snd_rawmidi *rmidi, int subdevice, - int mode, struct snd_rawmidi_file *rfile, - int depth); --int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile); -+int snd_rawmidi_kernel_release_nested(struct snd_rawmidi_file *rfile, -+ int depth); - int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream, - struct snd_rawmidi_params *params); - int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream, -@@ -201,6 +202,11 @@ static inline int snd_rawmidi_kernel_open(struct snd_rawmidi *rmidi, - return snd_rawmidi_kernel_open_nested(rmidi, subdevice, mode, rfile, 0); - } - -+static inline int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile) -+{ -+ return snd_rawmidi_kernel_release_nested(rfile, 0); -+} -+ - /* set up the tied devices */ - static inline void snd_rawmidi_tie_devices(struct snd_rawmidi *r1, - struct snd_rawmidi *r2) -diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c ---- a/sound/aoa/soundbus/i2sbus/core.c -+++ b/sound/aoa/soundbus/i2sbus/core.c -@@ -240,6 +240,8 @@ static int i2sbus_add_dev(struct macio_dev *macio, - } - for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) { - int irq = irq_of_parse_and_map(np, i); -+ if (!irq) -+ goto err; - if (request_irq(irq, ints[i], 0, dev->rnames[i], dev)) - goto err; - dev->interrupts[i] = irq; -diff --git a/sound/core/control_led.c b/sound/core/control_led.c ---- a/sound/core/control_led.c -+++ b/sound/core/control_led.c -@@ -255,6 +255,8 @@ static int snd_ctl_led_set_id(int card_number, struct snd_ctl_elem_id *id, - kctl = snd_ctl_find_id(card, id); - if (!kctl) - return -ENOENT; -+ if (!kctl->info || !kctl->get) -+ return -EINVAL; - ioff = snd_ctl_get_ioff(kctl, id); - vd = &kctl->vd[ioff]; - access = vd->access & SNDRV_CTL_ELEM_ACCESS_LED_MASK; -diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c ---- a/sound/core/rawmidi.c -+++ b/sound/core/rawmidi.c -@@ -571,7 +571,6 @@ static void rawmidi_release_priv(struct snd_rawmidi_file *rfile) - struct snd_rawmidi *rmidi; - - rmidi = rfile->rmidi; -- guard(mutex)(&rmidi->open_mutex); - if (rfile->input) { - close_substream(rmidi, rfile->input, 1); - rfile->input = NULL; -@@ -585,7 +584,8 @@ static void rawmidi_release_priv(struct snd_rawmidi_file *rfile) - } - - /* called from sound/core/seq/seq_midi.c */ --int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile) -+int snd_rawmidi_kernel_release_nested(struct snd_rawmidi_file *rfile, -+ int depth) - { - struct snd_rawmidi *rmidi; - -@@ -593,11 +593,13 @@ int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile) - return -ENXIO; - - rmidi = rfile->rmidi; -+ mutex_lock_nested(&rmidi->open_mutex, depth); - rawmidi_release_priv(rfile); -+ mutex_unlock(&rmidi->open_mutex); - module_put(rmidi->card->module); - return 0; - } --EXPORT_SYMBOL(snd_rawmidi_kernel_release); -+EXPORT_SYMBOL(snd_rawmidi_kernel_release_nested); - - static int snd_rawmidi_release(struct inode *inode, struct file *file) - { -@@ -607,7 +609,8 @@ static int snd_rawmidi_release(struct inode *inode, struct file *file) - - rfile = file->private_data; - rmidi = rfile->rmidi; -- rawmidi_release_priv(rfile); -+ scoped_guard(mutex, &rmidi->open_mutex) -+ rawmidi_release_priv(rfile); - kfree(rfile); - module = rmidi->card->module; - snd_card_file_remove(rmidi->card, file); -diff --git a/sound/core/ump.c b/sound/core/ump.c ---- a/sound/core/ump.c -+++ b/sound/core/ump.c -@@ -1184,7 +1184,8 @@ static int snd_ump_legacy_close(struct snd_rawmidi_substream *substream) - ump->legacy_substreams[dir][group] = NULL; - if (dir == SNDRV_RAWMIDI_STREAM_OUTPUT) { - if (!--ump->legacy_out_opens) -- snd_rawmidi_kernel_release(&ump->legacy_out_rfile); -+ snd_rawmidi_kernel_release_nested(&ump->legacy_out_rfile, -+ SINGLE_DEPTH_NESTING); - } - return 0; - } -diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c ---- a/sound/drivers/mtpav.c -+++ b/sound/drivers/mtpav.c -@@ -642,9 +642,7 @@ static void snd_mtpav_free(struct snd_card *card) - { - struct mtpav *crd = card->private_data; - -- guard(spinlock_irqsave)(&crd->spinlock); -- if (crd->istimer > 0) -- snd_mtpav_remove_output_timer(crd); -+ timer_shutdown_sync(&crd->timer); - } - - /* -diff --git a/sound/hda/codecs/conexant.c b/sound/hda/codecs/conexant.c ---- a/sound/hda/codecs/conexant.c -+++ b/sound/hda/codecs/conexant.c -@@ -207,7 +207,7 @@ static void cx_remove(struct hda_codec *codec) - snd_hda_gen_remove(codec); - } - --static void cx_process_headset_plugin(struct hda_codec *codec) -+static void cx_process_headset_detect_plug_type(struct hda_codec *codec) - { - unsigned int val; - unsigned int count = 0; -@@ -223,11 +223,9 @@ static void cx_process_headset_plugin(struct hda_codec *codec) - count++; - } while (count < 3); - val = snd_hda_codec_read(codec, 0x1c, 0, 0xcb0, 0x0); -- if (val & 0x800) { -- codec_dbg(codec, "headset plugin, type is CTIA\n"); -- snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24); -- } else if (val & 0x400) { -- codec_dbg(codec, "headset plugin, type is OMTP\n"); -+ if (val & 0xc00) { -+ codec_dbg(codec, "headset plugin, type is %s\n", -+ val & 0x800 ? "CTIA" : "OMTP"); - snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24); - } else { - codec_dbg(codec, "headphone plugin\n"); -@@ -243,10 +241,12 @@ static void cx_update_headset_mic_vref(struct hda_codec *codec, struct hda_jack_ - * Check hp&mic tag to process headset plugin & plugout. - */ - mic_present = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0x0); -- if (!(mic_present & AC_PINSENSE_PRESENCE)) /* mic plugout */ -+ if (!(mic_present & AC_PINSENSE_PRESENCE)) { /* mic plugout */ - snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20); -- else -- cx_process_headset_plugin(codec); -+ } else { -+ cx_process_headset_detect_plug_type(codec); -+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24); -+ } - } - - static int cx_suspend(struct hda_codec *codec) -diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c ---- a/sound/hda/codecs/realtek/alc269.c -+++ b/sound/hda/codecs/realtek/alc269.c -@@ -1370,6 +1370,61 @@ static void alc269_fixup_hp_gpio_led(struct hda_codec *codec, - alc_fixup_hp_gpio_led(codec, action, 0x08, 0x10); - } - -+/* -+ * HP Laptop 15-fd0xxx (SSID 103c:8bb6) Speaker Mute LED fix -+ * -+ * The speaker mute LED is controlled via VREF100 on NID 0x1a. -+ * This pin must remain powered (D0) even during suspend, otherwise -+ * the LED state is lost and the pin defaults to Hi-Z on resume, -+ * causing the LED to stop responding to mute toggles. -+ * Windows keeps this pin powered unconditionally, so matching that -+ * behavior ensures consistent LED operation across suspend/resume. -+ * The mic-mute LED is controlled via GPIO 0 with active-low polarity. -+ */ -+static unsigned int hp_8bb6_power_filter(struct hda_codec *codec, -+ hda_nid_t nid, -+ unsigned int power_state) -+{ -+ if (nid == 0x1a) -+ return AC_PWRST_D0; -+ return snd_hda_gen_path_power_filter(codec, nid, power_state); -+} -+ -+static int hp_8bb6_speaker_mute_led_set(struct led_classdev *led_cdev, -+ enum led_brightness brightness) -+{ -+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); -+ unsigned int val = (brightness == LED_OFF) ? PIN_IN : PIN_VREF100; -+ -+ snd_hda_set_pin_ctl_cache(codec, 0x1a, val); -+ return 0; -+} -+ -+static void alc236_fixup_hp_15_fd0xxx(struct hda_codec *codec, -+ const struct hda_fixup *fix, -+ int action) -+{ -+ struct alc_spec *spec = codec->spec; -+ -+ switch (action) { -+ case HDA_FIXUP_ACT_PRE_PROBE: -+ spec->micmute_led_polarity = 1; -+ alc_fixup_hp_gpio_led(codec, action, 0x00, 0x01); -+ -+ spec->mute_led_polarity = 0; -+ snd_hda_gen_add_mute_led_cdev(codec, hp_8bb6_speaker_mute_led_set); -+ -+ codec->power_filter = hp_8bb6_power_filter; -+ spec->no_shutup_pins = 1; -+ break; -+ -+ case HDA_FIXUP_ACT_INIT: -+ if (spec->gen.vmaster_mute.hook) -+ snd_hda_sync_vmaster_hook(&spec->gen.vmaster_mute); -+ break; -+ } -+} -+ - static void alc285_fixup_hp_gpio_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) - { -@@ -4089,6 +4144,7 @@ enum { - ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE, - ALC225_FIXUP_S3_POP_NOISE, - ALC700_FIXUP_INTEL_REFERENCE, -+ ALC700_FIXUP_INTEL_HADES_CANYON, - ALC274_FIXUP_DELL_BIND_DACS, - ALC274_FIXUP_DELL_AIO_LINEOUT_VERB, - ALC298_FIXUP_TPT470_DOCK_FIX, -@@ -4152,6 +4208,7 @@ enum { - ALC236_FIXUP_HP_GPIO_LED, - ALC236_FIXUP_HP_MUTE_LED, - ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF, -+ ALC236_FIXUP_HP_15_FD0XXX, - ALC236_FIXUP_LENOVO_INV_DMIC, - ALC298_FIXUP_SAMSUNG_AMP, - ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS, -@@ -4297,6 +4354,7 @@ enum { - ALC287_FIXUP_LENOVO_LEGION_AW88399, - ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN_HEADSET, - ALC285_LENOVO_DAC_RENAME, -+ ALC287_FIXUP_YOGA9_SPEAKER2_TO_DAC1, - }; - - /* A special fixup for Lenovo C940 and Yoga Duet 7; -@@ -4333,6 +4391,19 @@ static void alc287_fixup_lenovo_yoga_book_9i(struct hda_codec *codec, - __snd_hda_apply_fixup(codec, id, action, 0); - } - -+static void alc700_fixup_intel_hades_canyon(struct hda_codec *codec, -+ const struct hda_fixup *fix, -+ int action) -+{ -+ /* -+ * Windows may leave coef 0x1b bit 0x0400 cleared, causing broken -+ * analog playback after rebooting into Linux. Restore the bit -+ * during codec initialization. -+ */ -+ if (action == HDA_FIXUP_ACT_INIT) -+ alc_update_coef_idx(codec, 0x1b, 0x0400, 0x0400); -+} -+ - static const struct hda_fixup alc269_fixups[] = { - [ALC298_FIXUP_RAZER_BLADE16_2025_PINS] = { - .type = HDA_FIXUP_PINS, -@@ -5411,6 +5482,10 @@ static const struct hda_fixup alc269_fixups[] = { - {} - } - }, -+ [ALC700_FIXUP_INTEL_HADES_CANYON] = { -+ .type = HDA_FIXUP_FUNC, -+ .v.func = alc700_fixup_intel_hades_canyon, -+ }, - [ALC274_FIXUP_DELL_BIND_DACS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc274_fixup_bind_dacs, -@@ -5913,6 +5988,10 @@ static const struct hda_fixup alc269_fixups[] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc236_fixup_hp_mute_led_micmute_gpio, - }, -+ [ALC236_FIXUP_HP_15_FD0XXX] = { -+ .type = HDA_FIXUP_FUNC, -+ .v.func = alc236_fixup_hp_15_fd0xxx, -+ }, - [ALC236_FIXUP_LENOVO_INV_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic, -@@ -7010,6 +7089,12 @@ static const struct hda_fixup alc269_fixups[] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_lenovo_dac_rename, - }, -+ [ALC287_FIXUP_YOGA9_SPEAKER2_TO_DAC1] = { -+ .type = HDA_FIXUP_FUNC, -+ .v.func = alc285_fixup_speaker2_to_dac1, -+ .chained = true, -+ .chain_id = ALC287_FIXUP_TXNW2781_I2C, -+ }, - }; - - static const struct hda_quirk alc269_fixup_tbl[] = { -@@ -7448,6 +7533,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8bb3, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8bb4, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), -+ SND_PCI_QUIRK(0x103c, 0x8bb6, "HP Laptop 15-fd0039nt", ALC236_FIXUP_HP_15_FD0XXX), - SND_PCI_QUIRK(0x103c, 0x8bbe, "HP Victus 16-r0xxx (MB 8BBE)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), - SND_PCI_QUIRK(0x103c, 0x8bc8, "HP Victus 15-fa1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), - SND_PCI_QUIRK(0x103c, 0x8bcd, "HP Omen 16-xd0xxx", ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT), -@@ -8031,6 +8117,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { - SND_PCI_QUIRK(0x17aa, 0x3834, "Lenovo IdeaPad Slim 9i 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x383d, "Legion Y9000X 2019", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x3843, "Lenovo Yoga 9i / Yoga Book 9i", ALC287_FIXUP_LENOVO_YOGA_BOOK_9I), -+ SND_PCI_QUIRK(0x17aa, 0x3846, "Lenovo Yoga Pro 9 16IAH10", ALC287_FIXUP_YOGA9_SPEAKER2_TO_DAC1), - /* Yoga Pro 7 14IMH9 shares PCI SSID 17aa:3847 with Legion 7 16ACHG6; - * use codec SSID to distinguish them - */ -@@ -8044,14 +8131,15 @@ static const struct hda_quirk alc269_fixup_tbl[] = { - SND_PCI_QUIRK(0x17aa, 0x3852, "Lenovo Yoga 7 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x3853, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x3855, "Legion 7 16ITHG6", ALC287_FIXUP_LEGION_16ITHG6), -+ SND_PCI_QUIRK(0x17aa, 0x3862, "Lenovo IdeaPad Slim 3 15ABR8", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x17aa, 0x3865, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x17aa, 0x3866, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x17aa, 0x3869, "Lenovo Yoga7 14IAL7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), - HDA_CODEC_QUIRK(0x17aa, 0x386a, "Lenovo Yoga 7 16IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), - HDA_CODEC_QUIRK(0x17aa, 0x386e, "Legion Y9000X 2022 IAH7", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x17aa, 0x386e, "Yoga Pro 7 14ARP8", ALC285_FIXUP_YOGA_SPEAKER2_TO_DAC1), -- HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ - HDA_CODEC_QUIRK(0x17aa, 0x38a7, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ -+ HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ - SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x17aa, 0x3870, "Lenovo Yoga 7 14ARB7", ALC287_FIXUP_YOGA7_14ARB7_I2C), - SND_PCI_QUIRK(0x17aa, 0x3874, "Legion 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), -@@ -8231,6 +8319,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { - SND_PCI_QUIRK(0x2782, 0xa128, "Positivo N15RPE-S", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x2782, 0xa212, "Lunnen Ground 14", ALC269VC_FIXUP_LUNNEN_GROUND_14), - SND_PCI_QUIRK(0x7017, 0x2014, "Star Labs StarFighter", ALC233_FIXUP_STARLABS_STARFIGHTER), -+ SND_PCI_QUIRK(0x8086, 0x2073, "Intel NUC 8 Hades Canyon", -+ ALC700_FIXUP_INTEL_HADES_CANYON), - SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), - SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED), - SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", ALC256_FIXUP_INTEL_NUC10), -diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c ---- a/sound/pci/ice1712/ice1712.c -+++ b/sound/pci/ice1712/ice1712.c -@@ -2523,7 +2523,7 @@ static int snd_ice1712_probe(struct pci_dev *pci, - const struct pci_device_id *pci_id) - { - static int dev; -- struct snd_card *card; -+ struct snd_card *card __free(snd_card_unref) = NULL; - struct snd_ice1712 *ice; - int pcm_dev = 0, err; - const struct snd_ice1712_card_info * const *tbl, *c; -@@ -2535,8 +2535,8 @@ static int snd_ice1712_probe(struct pci_dev *pci, - return -ENOENT; - } - -- err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, -- sizeof(*ice), &card); -+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, -+ sizeof(*ice), &card); - if (err < 0) - return err; - ice = card->private_data; -@@ -2640,6 +2640,7 @@ static int snd_ice1712_probe(struct pci_dev *pci, - if (err < 0) - return err; - pci_set_drvdata(pci, card); -+ card = NULL; /* probe succeeded, don't release as error */ - dev++; - return 0; - } diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c --- a/sound/soc/amd/acp-config.c +++ b/sound/soc/amd/acp-config.c -@@ -54,7 +54,7 @@ static const struct dmi_system_id acp70_acpi_flag_override_table[] = { - { - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC"), -- DMI_MATCH(DMI_PRODUCT_NAME, "TUF Gaming A14 FA401EA"), -+ DMI_MATCH(DMI_PRODUCT_NAME, "FA401EA"), - }, - }, - { @@ -70,6 +70,13 @@ static const struct dmi_system_id acp70_acpi_flag_override_table[] = { DMI_MATCH(DMI_BOARD_NAME, "8EA1"), }, @@ -816,51 +425,6 @@ diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c ret = sdw_write_no_pm(slave, addr, prep_ch->ch_mask); if (ret) dev_err(dev, "prep failed for port %d, err=%d\n", -diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig ---- a/sound/soc/fsl/Kconfig -+++ b/sound/soc/fsl/Kconfig -@@ -244,7 +244,7 @@ config SND_SOC_MPC5200_AC97 - config SND_MPC52xx_SOC_PCM030 - tristate "SoC AC97 Audio support for Phytec pcm030 and WM9712" - depends on PPC_MPC5200_SIMPLE -- select SND_SOC_MPC5200_AC97 -+ depends on SND_SOC_MPC5200_AC97 - select SND_SOC_WM9712 - help - Say Y if you want to add support for sound on the Phytec pcm030 -@@ -253,7 +253,7 @@ config SND_MPC52xx_SOC_PCM030 - config SND_MPC52xx_SOC_EFIKA - tristate "SoC AC97 Audio support for bbplan Efika and STAC9766" - depends on PPC_EFIKA -- select SND_SOC_MPC5200_AC97 -+ depends on SND_SOC_MPC5200_AC97 - select SND_SOC_STAC9766 - help - Say Y if you want to add support for sound on the Efika. -diff --git a/sound/soc/fsl/efika-audio-fabric.c b/sound/soc/fsl/efika-audio-fabric.c ---- a/sound/soc/fsl/efika-audio-fabric.c -+++ b/sound/soc/fsl/efika-audio-fabric.c -@@ -22,8 +22,6 @@ - #include - #include - --#include "mpc5200_dma.h" -- - #define DRV_NAME "efika-audio-fabric" - - SND_SOC_DAILINK_DEFS(analog, -diff --git a/sound/soc/fsl/pcm030-audio-fabric.c b/sound/soc/fsl/pcm030-audio-fabric.c ---- a/sound/soc/fsl/pcm030-audio-fabric.c -+++ b/sound/soc/fsl/pcm030-audio-fabric.c -@@ -13,8 +13,6 @@ - - #include - --#include "mpc5200_dma.h" -- - #define DRV_NAME "pcm030-audio-fabric" - - struct pcm030_audio_data { diff --git a/sound/soc/loongson/loongson_card.c b/sound/soc/loongson/loongson_card.c --- a/sound/soc/loongson/loongson_card.c +++ b/sound/soc/loongson/loongson_card.c @@ -1275,18 +839,6 @@ diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_u ret = sdw_enable_stream(sdw_stream); break; -diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c ---- a/sound/soc/soc-dapm.c -+++ b/sound/soc/soc-dapm.c -@@ -3614,7 +3614,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, - val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; - mask = e->mask << e->shift_l; - if (e->shift_l != e->shift_r) { -- if (item[1] > e->items) -+ if (item[1] >= e->items) - return -EINVAL; - val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r; - mask |= e->mask << e->shift_r; diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -1317,339 +869,3 @@ diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaen name, PTR_ERR(chan)); pcm->chan[i] = NULL; -diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c ---- a/sound/usb/mixer.c -+++ b/sound/usb/mixer.c -@@ -1237,21 +1237,27 @@ static void init_cur_mix_raw(struct usb_mixer_elem_info *cval, int ch, int idx) - } - - /* -- * Additional checks for sticky mixers -+ * Additional checks for sticky GET_CUR - * -- * Some devices' volume control mixers are sticky, which accept SET_CUR but -- * do absolutely nothing. -+ * Some devices' volume control mixers have sticky GET_CUR, which implies either -+ * stubbed SET_CUR or broken GET_CUR. For the former case, the mixer accepts -+ * SET_CUR but do absolutely nothing, so falling back to soft mixer is the only -+ * way to control the volume. For the latter case, the mixer has effective -+ * SET_CUR despite GET_CUR being constant, and the mixer is usable as long as we -+ * always provide mixer value from the ceche. - * - * Check the return values of GET_CUR with different SET_CUR values. Consider -- * the mixer as sticky if GET_CUR always returns a constant value. -+ * GET_CUR as sticky if GET_CUR always returns a constant value. - * -- * Some devices have effective SET_CUR despite GET_CUR being constant. Do not -- * consider the mixer as sticky if a quirk flag indicates that. -+ * Unfortunately, we can't distinguish between stubbed SET_CUR and broken -+ * GET_CUR with simple read-back tests. Disabling the mixer regardless and -+ * forcing userspace to use soft mixer instead can lead to audible distortion at -+ * low volume on some wireless headphones, probably due to their poorly- -+ * performed lossy codec. - * -- * Gate the registration of sticky mixers to prevent confusing userspace, so -- * that they won't cause ineffective volume control. However, for mixers with -- * effective SET_CUR but broken GET_CUR, the registration can continue normally -- * but further GET_CUR requests will be gated. -+ * Instead, mark GET_CUR as broken regardless and only provide mixer value from -+ * the cache. Users may opt into soft mixer in userspace audio stack if they -+ * need it. - */ - static int check_sticky_volume_control(struct usb_mixer_elem_info *cval, - int channel, int saved) -@@ -1271,24 +1277,13 @@ static int check_sticky_volume_control(struct usb_mixer_elem_info *cval, - return 0; - } - -- if (cval->head.mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_GET_CUR_BROKEN) { -- usb_audio_info(cval->head.mixer->chip, -- "%d:%d: broken mixer GET_CUR (%d/%d/%d => %d)\n", -- cval->head.id, mixer_ctrl_intf(cval->head.mixer), -- cval->min, cval->max, cval->res, saved); -- -- cval->get_cur_broken = 1; -- return -ENXIO; -- } -- -- usb_audio_err(cval->head.mixer->chip, -- "%d:%d: sticky mixer values (%d/%d/%d => %d), disabling\n", -- cval->head.id, mixer_ctrl_intf(cval->head.mixer), -- cval->min, cval->max, cval->res, saved); - usb_audio_info(cval->head.mixer->chip, -- "check MIXER_GET_CUR_BROKEN if you believe the mixer is non-sticky"); -+ "%d:%d: broken mixer GET_CUR (%d/%d/%d => %d)\n", -+ cval->head.id, mixer_ctrl_intf(cval->head.mixer), -+ cval->min, cval->max, cval->res, saved); - -- return -ENODEV; -+ cval->get_cur_broken = 1; -+ return -ENXIO; - } - - /* -@@ -1385,8 +1380,6 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, - goto no_checks; - - ret = check_sticky_volume_control(cval, minchn, saved); -- if (ret == -ENODEV) -- goto sticky; - if (ret) - goto no_checks; - -@@ -1454,34 +1447,15 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, - } - } - -- return 0; -- --sticky: - /* -- * It makes no sense to restore the saved value for a sticky mixer, -- * since setting any value is a no-op. -- * -- * However, in some rare cases, SET_CUR is effective despite GET_CUR -- * always returns a constant value. These mixers are not sticky, but -- * there's no way to distinguish them. Without any additional -- * information, the best thing we can do is to set the mixer value to -- * the maximum before bailing out, so that a soft mixer can still reach -- * the maximum hardware volume if the mixer turns out to be non-sticky. -- * Meanwhile, all channels must be synchronized to prevent imbalance -- * volume. -+ * When GET_CUR is sticky, the saved value is bogus, so mixer values set -+ * by the sanity checks must be discarded through init_cur_mix_raw(). -+ * After that, we can clear the flag as per QUIRK_FLAG_MIXER_GET_CUR_OK. - */ -- if (!cval->cmask) { -- snd_usb_set_cur_mix_value(cval, 0, 0, cval->max); -- } else { -- idx = 0; -- for (i = 0; i < MAX_CHANNELS; i++) { -- if (cval->cmask & BIT(i)) { -- snd_usb_set_cur_mix_value(cval, i + 1, idx, cval->max); -- idx++; -- } -- } -- } -- return ret; -+ if (cval->head.mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_GET_CUR_OK) -+ cval->get_cur_broken = 0; -+ -+ return 0; - } - - #define get_min_max(cval, def) get_min_max_with_quirks(cval, def, NULL) -diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c ---- a/sound/usb/mixer_maps.c -+++ b/sound/usb/mixer_maps.c -@@ -505,13 +505,13 @@ static const struct usbmix_connector_map gigabyte_b450_connector_map[] = { - {} - }; - --/* Audient iD14: FU 12 advertises Volume on only 4 of its 6 logical channels -- * and sits on the monitor mixer branch, but it is traced through to the -- * Speaker output terminal and gets named "Speaker Playback Volume". Userspace -- * then adopts it as the stream's hardware volume, and any setting below 0 dB -- * attenuates some channels but not others (20 dB imbalance at 80%). Give it a -- * non-standard name so that it is no longer taken for the stream's master -- * volume, while remaining reachable for anyone who wants the monitor gain. -+/* Audient iD14 MkI and MkII: FU 12 sits on the monitor mixer branch but is -+ * traced through to the Speaker output terminal, so it is named "Speaker -+ * Playback Volume". On MkII it controls only 4 of 6 playback channels. MkI -+ * testing found asymmetric attenuation within the main stereo pair. Userspace -+ * adopts this control as the stream's hardware volume, causing imbalance below -+ * 0 dB. Give it a non-standard name so that userspace no longer treats it as -+ * the stream master, while keeping the monitor gain reachable. - */ - static const struct usbmix_name_map audient_id14_map[] = { - { 12, "Monitor Mix Playback" }, /* FU, partial coverage */ -@@ -602,7 +602,12 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { - .map = maya44_map, - }, - { -- /* Audient iD14 */ -+ /* Audient iD14 MkI */ -+ .id = USB_ID(0x2708, 0x0002), -+ .map = audient_id14_map, -+ }, -+ { -+ /* Audient iD14 MkII */ - .id = USB_ID(0x2708, 0x0008), - .map = audient_id14_map, - }, -diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h ---- a/sound/usb/quirks-table.h -+++ b/sound/usb/quirks-table.h -@@ -1814,6 +1814,28 @@ YAMAHA_DEVICE(0x7010, "UB99"), - } - } - }, -+{ -+ /* -+ * M-Audio Venom -+ * -+ * The AudioControl interface times out on every GET_CUR request, -+ * which adds around 47 seconds to the card registration and -+ * freezes the device, blocking streaming. -+ * Using an explicit composite quirk to skip the mixer entirely. -+ */ -+ USB_DEVICE_VENDOR_SPEC(0x0763, 0x2084), -+ QUIRK_DRIVER_INFO { -+ .vendor_name = "M-Audio", -+ .product_name = "Venom", -+ QUIRK_DATA_COMPOSITE { -+ { QUIRK_DATA_IGNORE(0) }, -+ { QUIRK_DATA_STANDARD_AUDIO(1) }, -+ { QUIRK_DATA_STANDARD_AUDIO(2) }, -+ { QUIRK_DATA_STANDARD_MIDI(3) }, -+ QUIRK_COMPOSITE_END -+ } -+ } -+}, - - /* Casio devices */ - { -diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c ---- a/sound/usb/quirks.c -+++ b/sound/usb/quirks.c -@@ -2215,10 +2215,10 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { - QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), - DEVICE_FLG(0x03f0, 0x654a, /* HP 320 FHD Webcam */ - QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), -- DEVICE_FLG(0x040b, 0x0897, /* Weltrend Semiconductor, sold as Redragon H510-PRO Wireless headset */ -- QUIRK_FLAG_MIXER_GET_CUR_BROKEN), - DEVICE_FLG(0x041e, 0x3000, /* Creative SB Extigy */ - QUIRK_FLAG_IGNORE_CTL_ERROR), -+ DEVICE_FLG(0x041e, 0x324d, /* Creative Sound Blaster Play! 3 */ -+ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), - DEVICE_FLG(0x041e, 0x4080, /* Creative Live Cam VF0610 */ - QUIRK_FLAG_GET_SAMPLE_RATE), - DEVICE_FLG(0x045e, 0x083c, /* MS USB Link headset */ -@@ -2256,8 +2256,9 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { - DEVICE_FLG(0x046d, 0x0a8f, /* Logitech H390 headset */ - QUIRK_FLAG_CTL_MSG_DELAY_1M | - QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), -- DEVICE_FLG(0x046d, 0x0af7, /* Logitech PRO X 2 LIGHTSPEED */ -- QUIRK_FLAG_MIXER_GET_CUR_BROKEN), -+ DEVICE_FLG(0x046d, 0x0aba, /* Logitech PRO X Wireless */ -+ QUIRK_FLAG_MIXER_GET_CUR_OK | -+ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), - DEVICE_FLG(0x0499, 0x1506, /* Yamaha THR5 */ - QUIRK_FLAG_GENERIC_IMPLICIT_FB), - DEVICE_FLG(0x0499, 0x1509, /* Steinberg UR22 */ -@@ -2329,9 +2330,11 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { - DEVICE_FLG(0x0763, 0x2031, /* M-Audio Fast Track C600 */ - QUIRK_FLAG_GENERIC_IMPLICIT_FB), - DEVICE_FLG(0x0763, 0x2080, /* M-Audio Fast Track Ultra */ -- QUIRK_FLAG_MIXER_GET_CUR_BROKEN | QUIRK_FLAG_GENERIC_IMPLICIT_FB), -+ QUIRK_FLAG_GENERIC_IMPLICIT_FB), - DEVICE_FLG(0x0763, 0x2081, /* M-Audio Fast Track Ultra */ -- QUIRK_FLAG_MIXER_GET_CUR_BROKEN | QUIRK_FLAG_GENERIC_IMPLICIT_FB), -+ QUIRK_FLAG_GENERIC_IMPLICIT_FB), -+ DEVICE_FLG(0x0763, 0x2084, /* M-Audio Venom */ -+ QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_DISABLE_AUTOSUSPEND), - DEVICE_FLG(0x07fd, 0x000b, /* MOTU M Series 2nd hardware revision */ - QUIRK_FLAG_CTL_MSG_DELAY_1M), - DEVICE_FLG(0x08bb, 0x2702, /* LineX FM Transmitter */ -@@ -2370,8 +2373,6 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { - QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER), - DEVICE_FLG(0x1038, 0x1294, /* SteelSeries Arctis Pro Wireless */ - QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), -- DEVICE_FLG(0x1038, 0x2232, /* SteelSeries Arctis Nova 5 */ -- QUIRK_FLAG_MIXER_GET_CUR_BROKEN), - DEVICE_FLG(0x1101, 0x0003, /* Audioengine D1 */ - QUIRK_FLAG_GET_SAMPLE_RATE), - DEVICE_FLG(0x12d1, 0x3a07, /* HUAWEI USB-C HEADSET */ -@@ -2379,8 +2380,6 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { - QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), - DEVICE_FLG(0x1224, 0x2a25, /* Jieli Technology USB PHY 2.0 */ - QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), -- DEVICE_FLG(0x1377, 0x6004, /* Sennheiser MOMENTUM 3 */ -- QUIRK_FLAG_MIXER_GET_CUR_BROKEN), - DEVICE_FLG(0x1395, 0x740a, /* Sennheiser DECT */ - QUIRK_FLAG_GET_SAMPLE_RATE), - DEVICE_FLG(0x1397, 0x0507, /* Behringer UMC202HD */ -@@ -2393,6 +2392,9 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { - QUIRK_FLAG_IFB_SILENCE_ON_EMPTY), - DEVICE_FLG(0x13e5, 0x0001, /* Serato Phono */ - QUIRK_FLAG_IGNORE_CTL_ERROR), -+ DEVICE_FLG(0x152a, 0x85dd, /* SMSL USB DAC */ -+ QUIRK_FLAG_DSD_RAW | QUIRK_FLAG_DISABLE_AUTOSUSPEND | -+ QUIRK_FLAG_SKIP_IFACE_SETUP), - DEVICE_FLG(0x152a, 0x880a, /* NeuralDSP Quad Cortex */ - 0), /* Doesn't have the vendor quirk which would otherwise apply */ - DEVICE_FLG(0x1532, 0x055e, /* Razer Nommo V2 X */ -@@ -2505,8 +2507,6 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { - QUIRK_FLAG_CTL_MSG_DELAY_1M), - DEVICE_FLG(0x2d99, 0x0026, /* HECATE G2 GAMING HEADSET */ - QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), -- DEVICE_FLG(0x2d99, 0xa024, /* Edifier MF200 */ -- QUIRK_FLAG_MIXER_GET_CUR_BROKEN), - DEVICE_FLG(0x2fc6, 0xf06b, /* MOONDROP Moonriver2 Ti */ - QUIRK_FLAG_CTL_MSG_DELAY), - DEVICE_FLG(0x2fc6, 0xf0b5, /* iBasso DC-Elite */ -@@ -2647,7 +2647,7 @@ static const char *const snd_usb_audio_quirk_flag_names[] = { - QUIRK_STRING_ENTRY(MIXER_PLAYBACK_LINEAR_VOL), - QUIRK_STRING_ENTRY(MIXER_CAPTURE_LINEAR_VOL), - QUIRK_STRING_ENTRY(IFB_SILENCE_ON_EMPTY), -- QUIRK_STRING_ENTRY(MIXER_GET_CUR_BROKEN), -+ QUIRK_STRING_ENTRY(MIXER_GET_CUR_OK), - QUIRK_STRING_ENTRY(PLAYBACK_URB_FIXUP), - QUIRK_STRING_ENTRY(ALWAYS_SET_RATE), - NULL -diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h ---- a/sound/usb/usbaudio.h -+++ b/sound/usb/usbaudio.h -@@ -243,24 +243,18 @@ extern bool snd_usb_skip_validation; - * from snd_usb_handle_sync_urb. Instead fall through and enqueue a - * packet_info containing only size-0 packets, so the OUT ring keeps - * moving (emits silence). Needed by Behringer Flow 8 (1397:050c). -- * QUIRK_FLAG_MIXER_GET_CUR_BROKEN -- * Some mixers are sticky, which means that setting their current volume is a -- * no-op, and reading the current volume returns a constant value. The sticky -- * check disables these mixers to prevent confusing userspace. However, some -- * devices do have a tunable volume despite the reported current volume being -- * constant. As the sticky check can't distinguish between the two categories, -- * setting this flag tells that the device should fall into the second -- * category when GET_CUR returns a constant value, resulting in the sticky -- * check being non-fatal and only disabling GET_CUR instead of the whole mixer. -- * The current volume will then be provided by the internal cache that stores -- * the last set volume -+ * QUIRK_FLAG_MIXER_GET_CUR_OK -+ * On some devices, whether their GET_CUR being sticky depends on whether -+ * hotpluggable components are present. When the hotpluggable components are -+ * missing on probe, their GET_CUR behavior is classified as broken. Set the -+ * flag to prevent the heuristics from gating GET_CUR. - * QUIRK_FLAG_PLAYBACK_URB_FIXUP - * Set URB_ISO_ASAP flag for isochronous URBs and force nurbs to MAX_URBS. - * This is needed for devices that exhibit boot-time audio stuttering due - * to insufficient buffer depth combined with xHCI scheduling variability. - * The larger buffer (MAX_URBS = 12, ~64ms) absorbs system scheduling - * jitter during boot, while URB_ISO_ASAP ensures consistent xHCI scheduling. -- * QUIRK_FLAG_ALWAYS_SET_RATE: -+ * QUIRK_FLAG_ALWAYS_SET_RATE - * Issue SET_CUR for the sample rate even when the clock already reports the - * requested rate. A device advertising a single rate is otherwise never sent - * the request at all, and some require it before streaming will start. -@@ -297,10 +291,10 @@ enum { - QUIRK_TYPE_MIXER_PLAYBACK_LINEAR_VOL = 27, - QUIRK_TYPE_MIXER_CAPTURE_LINEAR_VOL = 28, - QUIRK_TYPE_IFB_SILENCE_ON_EMPTY = 29, -- QUIRK_TYPE_MIXER_GET_CUR_BROKEN = 30, -+ QUIRK_TYPE_MIXER_GET_CUR_OK = 30, - QUIRK_TYPE_PLAYBACK_URB_FIXUP = 31, - QUIRK_TYPE_ALWAYS_SET_RATE = 32, --/* Please also edit snd_usb_audio_quirk_flag_names */ -+/* Please also edit snd_usb_audio_quirk_flag_names and alsa-configuration.rst */ - }; - - #define QUIRK_FLAG(x) BIT_U64(QUIRK_TYPE_ ## x) -@@ -335,7 +329,7 @@ enum { - #define QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL QUIRK_FLAG(MIXER_PLAYBACK_LINEAR_VOL) - #define QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL QUIRK_FLAG(MIXER_CAPTURE_LINEAR_VOL) - #define QUIRK_FLAG_IFB_SILENCE_ON_EMPTY QUIRK_FLAG(IFB_SILENCE_ON_EMPTY) --#define QUIRK_FLAG_MIXER_GET_CUR_BROKEN QUIRK_FLAG(MIXER_GET_CUR_BROKEN) -+#define QUIRK_FLAG_MIXER_GET_CUR_OK QUIRK_FLAG(MIXER_GET_CUR_OK) - #define QUIRK_FLAG_PLAYBACK_URB_FIXUP QUIRK_FLAG(PLAYBACK_URB_FIXUP) - #define QUIRK_FLAG_ALWAYS_SET_RATE QUIRK_FLAG(ALWAYS_SET_RATE) - diff --git a/pkgbuilds/linux-omarchy-bore/0513-asoc-fixes-1.patch.sig b/pkgbuilds/linux-omarchy-bore/0513-asoc-fixes-1.patch.sig new file mode 100644 index 0000000..2c6d29a Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0513-asoc-fixes-1.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0512-sound-fixes.patch b/pkgbuilds/linux-omarchy-bore/0514-sound-fixes-2.patch similarity index 74% rename from pkgbuilds/linux-omarchy/0512-sound-fixes.patch rename to pkgbuilds/linux-omarchy-bore/0514-sound-fixes-2.patch index 9566376..043c34e 100644 --- a/pkgbuilds/linux-omarchy/0512-sound-fixes.patch +++ b/pkgbuilds/linux-omarchy-bore/0514-sound-fixes-2.patch @@ -1,92 +1,3 @@ -diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c ---- a/sound/core/pcm_native.c -+++ b/sound/core/pcm_native.c -@@ -4023,20 +4023,33 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, - return -EINVAL; - } - runtime = substream->runtime; -- if (runtime->state == SNDRV_PCM_STATE_OPEN) -- return -EBADFD; -- if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) -- return -ENXIO; -+ /* don't race with buffer reallocation in hw_params/hw_free */ -+ if (!atomic_inc_unless_negative(&runtime->buffer_accessing)) -+ return -EBUSY; -+ if (runtime->state == SNDRV_PCM_STATE_OPEN) { -+ err = -EBADFD; -+ goto out; -+ } -+ if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) { -+ err = -ENXIO; -+ goto out; -+ } - if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || -- runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) -- return -EINVAL; -+ runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) { -+ err = -EINVAL; -+ goto out; -+ } - size = area->vm_end - area->vm_start; - offset = area->vm_pgoff << PAGE_SHIFT; - dma_bytes = PAGE_ALIGN(runtime->dma_bytes); -- if ((size_t)size > dma_bytes) -- return -EINVAL; -- if (offset > dma_bytes - size) -- return -EINVAL; -+ if ((size_t)size > dma_bytes) { -+ err = -EINVAL; -+ goto out; -+ } -+ if (offset > dma_bytes - size) { -+ err = -EINVAL; -+ goto out; -+ } - - area->vm_ops = &snd_pcm_vm_ops_data; - area->vm_private_data = substream; -@@ -4046,6 +4059,8 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, - err = snd_pcm_lib_default_mmap(substream, area); - if (!err) - atomic_inc(&substream->mmap_count); -+out: -+ atomic_dec(&runtime->buffer_accessing); - return err; - } - EXPORT_SYMBOL(snd_pcm_mmap_data); -diff --git a/sound/core/ump.c b/sound/core/ump.c ---- a/sound/core/ump.c -+++ b/sound/core/ump.c -@@ -1335,6 +1335,8 @@ static void update_legacy_names(struct snd_ump_endpoint *ump) - { - struct snd_rawmidi *rmidi = ump->legacy_rmidi; - -+ if (!rmidi) -+ return; - update_legacy_substreams(ump, rmidi, SNDRV_RAWMIDI_STREAM_INPUT); - update_legacy_substreams(ump, rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT); - } -@@ -1343,6 +1345,8 @@ static void ump_legacy_set_rawmidi_name(struct snd_ump_endpoint *ump) - { - struct snd_rawmidi *rmidi = ump->legacy_rmidi; - -+ if (!rmidi) -+ return; - snprintf(rmidi->name, sizeof(rmidi->name), "%.68s (MIDI 1.0)", - ump->core.name); - } -diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c ---- a/sound/drivers/dummy.c -+++ b/sound/drivers/dummy.c -@@ -807,7 +807,7 @@ static int snd_dummy_capsrc_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el - left = ucontrol->value.integer.value[0] & 1; - right = ucontrol->value.integer.value[1] & 1; - guard(spinlock_irq)(&dummy->mixer_lock); -- change = dummy->capture_source[addr][0] != left && -+ change = dummy->capture_source[addr][0] != left || - dummy->capture_source[addr][1] != right; - dummy->capture_source[addr][0] = left; - dummy->capture_source[addr][1] = right; diff --git a/sound/hda/codecs/cirrus/cs420x.c b/sound/hda/codecs/cirrus/cs420x.c --- a/sound/hda/codecs/cirrus/cs420x.c +++ b/sound/hda/codecs/cirrus/cs420x.c @@ -233,62 +144,6 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 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/hda/core/device.c b/sound/hda/core/device.c ---- a/sound/hda/core/device.c -+++ b/sound/hda/core/device.c -@@ -404,6 +404,7 @@ static void setup_fg_nodes(struct hdac_device *codec) - */ - int snd_hdac_refresh_widgets(struct hdac_device *codec) - { -+ hda_nid_t fg = codec->afg ? codec->afg : codec->mfg; - hda_nid_t start_nid; - int nums, err = 0; - -@@ -412,10 +413,10 @@ int snd_hdac_refresh_widgets(struct hdac_device *codec) - * widgets array. - */ - guard(mutex)(&codec->widget_lock); -- nums = snd_hdac_get_sub_nodes(codec, codec->afg, &start_nid); -+ nums = snd_hdac_get_sub_nodes(codec, fg, &start_nid); - if (!start_nid || nums <= 0 || nums >= 0xff) { - dev_err(&codec->dev, "cannot read sub nodes for FG 0x%02x\n", -- codec->afg); -+ fg); - return -EINVAL; - } - -diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c ---- a/sound/usb/caiaq/audio.c -+++ b/sound/usb/caiaq/audio.c -@@ -828,16 +828,13 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *cdev) - - cdev->data_urbs_in = alloc_urbs(cdev, SNDRV_PCM_STREAM_CAPTURE, &ret); - if (ret < 0) { -- kfree(cdev->data_cb_info); -- free_urbs(cdev->data_urbs_in); -+ snd_usb_caiaq_audio_free(cdev); - return ret; - } - - cdev->data_urbs_out = alloc_urbs(cdev, SNDRV_PCM_STREAM_PLAYBACK, &ret); - if (ret < 0) { -- kfree(cdev->data_cb_info); -- free_urbs(cdev->data_urbs_in); -- free_urbs(cdev->data_urbs_out); -+ snd_usb_caiaq_audio_free(cdev); - return ret; - } - -@@ -858,6 +855,9 @@ void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *cdev) - - dev_dbg(dev, "%s(%p)\n", __func__, cdev); - free_urbs(cdev->data_urbs_in); -+ cdev->data_urbs_in = NULL; - free_urbs(cdev->data_urbs_out); -+ cdev->data_urbs_out = NULL; - kfree(cdev->data_cb_info); -+ cdev->data_cb_info = NULL; - } diff --git a/sound/usb/fcp.c b/sound/usb/fcp.c --- a/sound/usb/fcp.c +++ b/sound/usb/fcp.c diff --git a/pkgbuilds/linux-omarchy-bore/0514-sound-fixes-2.patch.sig b/pkgbuilds/linux-omarchy-bore/0514-sound-fixes-2.patch.sig new file mode 100644 index 0000000..fce1ac9 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0514-sound-fixes-2.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0513-xps13-sof-quirk.patch b/pkgbuilds/linux-omarchy-bore/0515-xps13-sof-quirk.patch similarity index 100% rename from pkgbuilds/linux-omarchy-bore/0513-xps13-sof-quirk.patch rename to pkgbuilds/linux-omarchy-bore/0515-xps13-sof-quirk.patch diff --git a/pkgbuilds/linux-omarchy-bore/0513-xps13-sof-quirk.patch.sig b/pkgbuilds/linux-omarchy-bore/0515-xps13-sof-quirk.patch.sig similarity index 100% rename from pkgbuilds/linux-omarchy-bore/0513-xps13-sof-quirk.patch.sig rename to pkgbuilds/linux-omarchy-bore/0515-xps13-sof-quirk.patch.sig diff --git a/pkgbuilds/linux-omarchy-bore/0514-rt766-stream-config-type.patch b/pkgbuilds/linux-omarchy-bore/0516-rt766-stream-config-type.patch similarity index 100% rename from pkgbuilds/linux-omarchy-bore/0514-rt766-stream-config-type.patch rename to pkgbuilds/linux-omarchy-bore/0516-rt766-stream-config-type.patch diff --git a/pkgbuilds/linux-omarchy-bore/0514-rt766-stream-config-type.patch.sig b/pkgbuilds/linux-omarchy-bore/0516-rt766-stream-config-type.patch.sig similarity index 100% rename from pkgbuilds/linux-omarchy-bore/0514-rt766-stream-config-type.patch.sig rename to pkgbuilds/linux-omarchy-bore/0516-rt766-stream-config-type.patch.sig diff --git a/pkgbuilds/linux-omarchy-bore/0516-hda-realtek-rog-strix-g733zw-speakers.patch b/pkgbuilds/linux-omarchy-bore/0517-hda-realtek-rog-strix-g733zw-speakers.patch similarity index 100% rename from pkgbuilds/linux-omarchy-bore/0516-hda-realtek-rog-strix-g733zw-speakers.patch rename to pkgbuilds/linux-omarchy-bore/0517-hda-realtek-rog-strix-g733zw-speakers.patch diff --git a/pkgbuilds/linux-omarchy-bore/0516-hda-realtek-rog-strix-g733zw-speakers.patch.sig b/pkgbuilds/linux-omarchy-bore/0517-hda-realtek-rog-strix-g733zw-speakers.patch.sig similarity index 100% rename from pkgbuilds/linux-omarchy-bore/0516-hda-realtek-rog-strix-g733zw-speakers.patch.sig rename to pkgbuilds/linux-omarchy-bore/0517-hda-realtek-rog-strix-g733zw-speakers.patch.sig diff --git a/pkgbuilds/linux-omarchy-bore/0517-asoc-amd-yc-acer-aspire-a314-23p.patch b/pkgbuilds/linux-omarchy-bore/0518-asoc-amd-yc-acer-aspire-a314-23p.patch similarity index 100% rename from pkgbuilds/linux-omarchy-bore/0517-asoc-amd-yc-acer-aspire-a314-23p.patch rename to pkgbuilds/linux-omarchy-bore/0518-asoc-amd-yc-acer-aspire-a314-23p.patch diff --git a/pkgbuilds/linux-omarchy-bore/0517-asoc-amd-yc-acer-aspire-a314-23p.patch.sig b/pkgbuilds/linux-omarchy-bore/0518-asoc-amd-yc-acer-aspire-a314-23p.patch.sig similarity index 100% rename from pkgbuilds/linux-omarchy-bore/0517-asoc-amd-yc-acer-aspire-a314-23p.patch.sig rename to pkgbuilds/linux-omarchy-bore/0518-asoc-amd-yc-acer-aspire-a314-23p.patch.sig diff --git a/pkgbuilds/linux-omarchy-bore/0522-sound-fixes-3.patch b/pkgbuilds/linux-omarchy-bore/0522-sound-fixes-3.patch new file mode 100644 index 0000000..48c9872 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0522-sound-fixes-3.patch @@ -0,0 +1,432 @@ +diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c +--- a/sound/hda/codecs/realtek/alc269.c ++++ b/sound/hda/codecs/realtek/alc269.c +@@ -7465,6 +7465,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x887c, "HP Laptop 14s-fq1xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x888a, "HP ENVY x360 Convertible 15-eu0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS), + SND_PCI_QUIRK(0x103c, 0x888d, "HP ZBook Power 15.6 inch G8 Mobile Workstation PC", ALC236_FIXUP_HP_GPIO_LED), ++ SND_PCI_QUIRK(0x103c, 0x8890, "HP Elite Dragonfly Max G2 Notebook PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), + SND_PCI_QUIRK(0x103c, 0x8895, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8896, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8898, "HP EliteBook 845 G8 Notebook PC", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST), +@@ -7580,6 +7581,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x8b92, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8b96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), ++ SND_PCI_QUIRK(0x103c, 0x8ba9, "HP Omen 16-wd0xxx", ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8bb3, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8bb4, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8bb6, "HP Laptop 15-fd0039nt", ALC236_FIXUP_HP_15_FD0XXX), +diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c +--- a/sound/pci/rme9652/hdspm.c ++++ b/sound/pci/rme9652/hdspm.c +@@ -1071,14 +1071,8 @@ struct hdspm { + + + static const struct pci_device_id snd_hdspm_ids[] = { +- { +- .vendor = PCI_VENDOR_ID_XILINX, +- .device = PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI, +- .subvendor = PCI_ANY_ID, +- .subdevice = PCI_ANY_ID, +- .class = 0, +- .class_mask = 0, +- .driver_data = 0}, ++ { PCI_DEVICE(PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI) }, ++ { PCI_DEVICE(0x1d18, 0x3fc6) }, /* RME HDSPe AIO PCI express audio */ + {0,} + }; + +diff --git a/sound/usb/hiface/pcm.c b/sound/usb/hiface/pcm.c +--- a/sound/usb/hiface/pcm.c ++++ b/sound/usb/hiface/pcm.c +@@ -24,7 +24,7 @@ + struct pcm_urb { + struct hiface_chip *chip; + +- struct urb instance; ++ struct urb *instance; + struct usb_anchor submitted; + u8 *buffer; + }; +@@ -193,7 +193,7 @@ static void hiface_pcm_stream_stop(struct pcm_runtime *rt) + if (!time) + usb_kill_anchored_urbs( + &rt->out_urbs[i].submitted); +- usb_kill_urb(&rt->out_urbs[i].instance); ++ usb_kill_urb(rt->out_urbs[i].instance); + } + + rt->stream_state = STREAM_DISABLED; +@@ -215,9 +215,9 @@ static int hiface_pcm_stream_start(struct pcm_runtime *rt) + rt->stream_state = STREAM_STARTING; + for (i = 0; i < PCM_N_URBS; i++) { + memset(rt->out_urbs[i].buffer, 0, PCM_PACKET_SIZE); +- usb_anchor_urb(&rt->out_urbs[i].instance, ++ usb_anchor_urb(rt->out_urbs[i].instance, + &rt->out_urbs[i].submitted); +- ret = usb_submit_urb(&rt->out_urbs[i].instance, ++ ret = usb_submit_urb(rt->out_urbs[i].instance, + GFP_ATOMIC); + if (ret) { + hiface_pcm_stream_stop(rt); +@@ -334,7 +334,7 @@ static void hiface_pcm_out_urb_handler(struct urb *usb_urb) + if (do_period_elapsed) + snd_pcm_period_elapsed(sub->instance); + +- ret = usb_submit_urb(&out_urb->instance, GFP_ATOMIC); ++ ret = usb_submit_urb(out_urb->instance, GFP_ATOMIC); + if (ret < 0) + goto out_fail; + +@@ -492,16 +492,18 @@ static int hiface_pcm_init_urb(struct pcm_urb *urb, + void (*handler)(struct urb *)) + { + urb->chip = chip; +- usb_init_urb(&urb->instance); ++ urb->instance = usb_alloc_urb(0, GFP_KERNEL); ++ if (!urb->instance) ++ return -ENOMEM; + + urb->buffer = kzalloc(PCM_PACKET_SIZE, GFP_KERNEL); + if (!urb->buffer) + return -ENOMEM; + +- usb_fill_bulk_urb(&urb->instance, chip->dev, ++ usb_fill_bulk_urb(urb->instance, chip->dev, + usb_sndbulkpipe(chip->dev, ep), (void *)urb->buffer, + PCM_PACKET_SIZE, handler, urb); +- if (usb_urb_ep_type_check(&urb->instance)) ++ if (usb_urb_ep_type_check(urb->instance)) + return -EINVAL; + init_usb_anchor(&urb->submitted); + +@@ -520,24 +522,26 @@ void hiface_pcm_abort(struct hiface_chip *chip) + } + } + +-static void hiface_pcm_destroy(struct hiface_chip *chip) ++static void hiface_pcm_destroy(struct pcm_runtime *rt) + { +- struct pcm_runtime *rt = chip->pcm; + int i; + +- for (i = 0; i < PCM_N_URBS; i++) +- kfree(rt->out_urbs[i].buffer); ++ if (!rt) ++ return; + +- kfree(chip->pcm); +- chip->pcm = NULL; ++ if (rt->chip) ++ rt->chip->pcm = NULL; ++ ++ for (i = 0; i < PCM_N_URBS; i++) { ++ usb_free_urb(rt->out_urbs[i].instance); ++ kfree(rt->out_urbs[i].buffer); ++ } ++ kfree(rt); + } + + static void hiface_pcm_free(struct snd_pcm *pcm) + { +- struct pcm_runtime *rt = pcm->private_data; +- +- if (rt) +- hiface_pcm_destroy(rt->chip); ++ hiface_pcm_destroy(pcm->private_data); + } + + int hiface_pcm_init(struct hiface_chip *chip, u8 extra_freq) +@@ -587,8 +591,6 @@ int hiface_pcm_init(struct hiface_chip *chip, u8 extra_freq) + return 0; + + error: +- for (i = 0; i < PCM_N_URBS; i++) +- kfree(rt->out_urbs[i].buffer); +- kfree(rt); ++ hiface_pcm_destroy(rt); + return ret; + } +diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c +--- a/sound/usb/misc/ua101.c ++++ b/sound/usb/misc/ua101.c +@@ -109,10 +109,10 @@ struct ua101 { + unsigned int buffer_pos; + unsigned int queue_length; + struct ua101_urb { +- struct urb urb; +- struct usb_iso_packet_descriptor iso_frame_desc[1]; ++ struct urb *urb; + struct list_head ready_list; +- } *urbs[MAX_QUEUE_LENGTH]; ++ struct ua101 *ua; ++ } urbs[MAX_QUEUE_LENGTH]; + struct { + unsigned int size; + void *addr; +@@ -167,15 +167,15 @@ static void abort_usb_playback(struct ua101 *ua) + wake_up(&ua->alsa_playback_wait); + } + +-static void playback_urb_complete(struct urb *usb_urb) ++static void playback_urb_complete(struct urb *urb) + { +- struct ua101_urb *urb = (struct ua101_urb *)usb_urb; +- struct ua101 *ua = urb->urb.context; ++ struct ua101_urb *ua_urb = urb->context; ++ struct ua101 *ua = ua_urb->ua; + +- if (unlikely(urb->urb.status == -ENOENT || /* unlinked */ +- urb->urb.status == -ENODEV || /* device removed */ +- urb->urb.status == -ECONNRESET || /* unlinked */ +- urb->urb.status == -ESHUTDOWN)) { /* device disabled */ ++ if (unlikely(urb->status == -ENOENT || /* unlinked */ ++ urb->status == -ENODEV || /* device removed */ ++ urb->status == -ECONNRESET || /* unlinked */ ++ urb->status == -ESHUTDOWN)) { /* device disabled */ + abort_usb_playback(ua); + abort_alsa_playback(ua); + return; +@@ -184,18 +184,19 @@ static void playback_urb_complete(struct urb *usb_urb) + if (test_bit(USB_PLAYBACK_RUNNING, &ua->states)) { + /* append URB to FIFO */ + guard(spinlock_irqsave)(&ua->lock); +- list_add_tail(&urb->ready_list, &ua->ready_playback_urbs); ++ list_add_tail(&ua_urb->ready_list, &ua->ready_playback_urbs); + if (ua->rate_feedback_count > 0) + queue_work(system_highpri_wq, &ua->playback_work); + ua->playback.substream->runtime->delay -= +- urb->urb.iso_frame_desc[0].length / ++ urb->iso_frame_desc[0].length / + ua->playback.frame_bytes; + } + } + + static void first_playback_urb_complete(struct urb *urb) + { +- struct ua101 *ua = urb->context; ++ struct ua101_urb *ua_urb = urb->context; ++ struct ua101 *ua = ua_urb->ua; + + urb->complete = playback_urb_complete; + playback_urb_complete(urb); +@@ -248,7 +249,8 @@ static void playback_work(struct work_struct *work) + { + struct ua101 *ua = container_of(work, struct ua101, playback_work); + unsigned int frames; +- struct ua101_urb *urb; ++ struct ua101_urb *ua_urb; ++ struct urb *urb; + bool do_period_elapsed = false; + int err; + +@@ -275,23 +277,24 @@ static void playback_work(struct work_struct *work) + ua->rate_feedback_count--; + + /* take URB out of FIFO */ +- urb = list_first_entry(&ua->ready_playback_urbs, +- struct ua101_urb, ready_list); +- list_del(&urb->ready_list); ++ ua_urb = list_first_entry(&ua->ready_playback_urbs, ++ struct ua101_urb, ready_list); ++ list_del(&ua_urb->ready_list); ++ urb = ua_urb->urb; + + /* fill packet with data or silence */ +- urb->urb.iso_frame_desc[0].length = ++ urb->iso_frame_desc[0].length = + frames * ua->playback.frame_bytes; + if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states)) + do_period_elapsed |= copy_playback_data(&ua->playback, +- &urb->urb, ++ urb, + frames); + else +- memset(urb->urb.transfer_buffer, 0, +- urb->urb.iso_frame_desc[0].length); ++ memset(urb->transfer_buffer, 0, ++ urb->iso_frame_desc[0].length); + + /* and off you go ... */ +- err = usb_submit_urb(&urb->urb, GFP_ATOMIC); ++ err = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(err < 0)) { + abort_usb_playback(ua); + abort_alsa_playback(ua); +@@ -342,7 +345,8 @@ static bool copy_capture_data(struct ua101_stream *stream, struct urb *urb, + + static void capture_urb_complete(struct urb *urb) + { +- struct ua101 *ua = urb->context; ++ struct ua101_urb *ua_urb = urb->context; ++ struct ua101 *ua = ua_urb->ua; + struct ua101_stream *stream = &ua->capture; + unsigned int frames, write_ptr; + bool do_period_elapsed; +@@ -413,7 +417,8 @@ static void capture_urb_complete(struct urb *urb) + + static void first_capture_urb_complete(struct urb *urb) + { +- struct ua101 *ua = urb->context; ++ struct ua101_urb *ua_urb = urb->context; ++ struct ua101 *ua = ua_urb->ua; + + urb->complete = capture_urb_complete; + capture_urb_complete(urb); +@@ -427,7 +432,7 @@ static int submit_stream_urbs(struct ua101 *ua, struct ua101_stream *stream) + unsigned int i; + + for (i = 0; i < stream->queue_length; ++i) { +- int err = usb_submit_urb(&stream->urbs[i]->urb, GFP_KERNEL); ++ int err = usb_submit_urb(stream->urbs[i].urb, GFP_KERNEL); + if (err < 0) { + dev_err(&ua->dev->dev, "USB request error %d: %s\n", + err, usb_error_string(err)); +@@ -442,8 +447,8 @@ static void kill_stream_urbs(struct ua101_stream *stream) + unsigned int i; + + for (i = 0; i < stream->queue_length; ++i) +- if (stream->urbs[i]) +- usb_kill_urb(&stream->urbs[i]->urb); ++ if (stream->urbs[i].urb) ++ usb_kill_urb(stream->urbs[i].urb); + } + + static int enable_iso_interface(struct ua101 *ua, unsigned int intf_index) +@@ -508,7 +513,7 @@ static int start_usb_capture(struct ua101 *ua) + return err; + + clear_bit(CAPTURE_URB_COMPLETED, &ua->states); +- ua->capture.urbs[0]->urb.complete = first_capture_urb_complete; ++ ua->capture.urbs[0].urb->complete = first_capture_urb_complete; + ua->rate_feedback_start = 0; + ua->rate_feedback_count = 0; + +@@ -550,7 +555,7 @@ static int start_usb_playback(struct ua101 *ua) + return err; + + clear_bit(PLAYBACK_URB_COMPLETED, &ua->states); +- ua->playback.urbs[0]->urb.complete = ++ ua->playback.urbs[0].urb->complete = + first_playback_urb_complete; + scoped_guard(spinlock_irq, &ua->lock) { + INIT_LIST_HEAD(&ua->ready_playback_urbs); +@@ -580,7 +585,7 @@ static int start_usb_playback(struct ua101 *ua) + add_with_wraparound(ua, &ua->rate_feedback_start, 1); + ua->rate_feedback_count--; + } +- urb = &ua->playback.urbs[i]->urb; ++ urb = ua->playback.urbs[i].urb; + urb->iso_frame_desc[0].length = + frames * ua->playback.frame_bytes; + memset(urb->transfer_buffer, 0, +@@ -1059,7 +1064,7 @@ static int alloc_stream_urbs(struct ua101 *ua, struct ua101_stream *stream, + void (*urb_complete)(struct urb *)) + { + unsigned max_packet_size = stream->max_packet_bytes; +- struct ua101_urb *urb; ++ struct urb *urb; + unsigned int b, u = 0; + + for (b = 0; b < ARRAY_SIZE(stream->buffers); ++b) { +@@ -1070,23 +1075,24 @@ static int alloc_stream_urbs(struct ua101 *ua, struct ua101_stream *stream, + while (size >= max_packet_size) { + if (u >= stream->queue_length) + goto bufsize_error; +- urb = kmalloc_obj(*urb); ++ urb = usb_alloc_urb(1, GFP_KERNEL); + if (!urb) + return -ENOMEM; +- usb_init_urb(&urb->urb); +- urb->urb.dev = ua->dev; +- urb->urb.pipe = stream->usb_pipe; +- urb->urb.transfer_flags = URB_NO_TRANSFER_DMA_MAP; +- urb->urb.transfer_buffer = addr; +- urb->urb.transfer_dma = dma; +- urb->urb.transfer_buffer_length = max_packet_size; +- urb->urb.number_of_packets = 1; +- urb->urb.interval = 1; +- urb->urb.context = ua; +- urb->urb.complete = urb_complete; +- urb->urb.iso_frame_desc[0].offset = 0; +- urb->urb.iso_frame_desc[0].length = max_packet_size; +- stream->urbs[u++] = urb; ++ urb->dev = ua->dev; ++ urb->pipe = stream->usb_pipe; ++ urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; ++ urb->transfer_buffer = addr; ++ urb->transfer_dma = dma; ++ urb->transfer_buffer_length = max_packet_size; ++ urb->number_of_packets = 1; ++ urb->interval = 1; ++ urb->context = &stream->urbs[u]; ++ urb->complete = urb_complete; ++ urb->iso_frame_desc[0].offset = 0; ++ urb->iso_frame_desc[0].length = max_packet_size; ++ stream->urbs[u].ua = ua; ++ stream->urbs[u].urb = urb; ++ u++; + size -= max_packet_size; + addr += max_packet_size; + dma += max_packet_size; +@@ -1104,8 +1110,8 @@ static void free_stream_urbs(struct ua101_stream *stream) + unsigned int i; + + for (i = 0; i < stream->queue_length; ++i) { +- kfree(stream->urbs[i]); +- stream->urbs[i] = NULL; ++ usb_free_urb(stream->urbs[i].urb); ++ stream->urbs[i].urb = NULL; + } + } + +diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c +--- a/sound/usb/quirks.c ++++ b/sound/usb/quirks.c +@@ -849,6 +849,27 @@ static int snd_usb_accessmusic_boot_quirk(struct usb_device *dev) + return 0; + } + ++/* ++ * A post configuration device descriptor read is needed to make the CM1A ++ * operational after reenumeration. ++ */ ++static int snd_usb_cm1a_boot_quirk(struct usb_device *dev) ++{ ++ struct usb_device_descriptor *desc __free(kfree) = kmalloc_obj(*desc); ++ int err; ++ ++ if (!desc) ++ return -ENOMEM; ++ ++ err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, sizeof(*desc)); ++ if (err < 0) { ++ dev_err(&dev->dev, "failed to read device descriptor: %d\n", err); ++ return err; ++ } ++ ++ return 0; ++} ++ + /* + * Some sound cards from Native Instruments are in fact compliant to the USB + * audio standard of version 2 and other approved USB standards, even though +@@ -1681,6 +1702,8 @@ int snd_usb_apply_boot_quirk_once(struct usb_device *dev, + switch (id) { + case USB_ID(0x07fd, 0x0008): /* MOTU M Series, 1st hardware version */ + return snd_usb_motu_m_series_boot_quirk(dev); ++ case USB_ID(0x1397, 0x1234): /* Behringer CM1A */ ++ return snd_usb_cm1a_boot_quirk(dev); + } + + return 0; +@@ -2390,6 +2413,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + QUIRK_FLAG_PLAYBACK_FIRST | QUIRK_FLAG_GENERIC_IMPLICIT_FB), + DEVICE_FLG(0x1397, 0x050c, /* Behringer Flow 8 */ + QUIRK_FLAG_IFB_SILENCE_ON_EMPTY), ++ DEVICE_FLG(0x1397, 0x0510, /* Behringer UV1 */ ++ QUIRK_FLAG_PLAYBACK_FIRST | QUIRK_FLAG_GENERIC_IMPLICIT_FB), + DEVICE_FLG(0x13e5, 0x0001, /* Serato Phono */ + QUIRK_FLAG_IGNORE_CTL_ERROR), + DEVICE_FLG(0x152a, 0x85dd, /* SMSL USB DAC */ diff --git a/pkgbuilds/linux-omarchy-bore/0522-sound-fixes-3.patch.sig b/pkgbuilds/linux-omarchy-bore/0522-sound-fixes-3.patch.sig new file mode 100644 index 0000000..2f7746c Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0522-sound-fixes-3.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0523-asoc-fixes-2.patch b/pkgbuilds/linux-omarchy-bore/0523-asoc-fixes-2.patch new file mode 100644 index 0000000..b65c5d4 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0523-asoc-fixes-2.patch @@ -0,0 +1,1040 @@ +diff --git a/sound/hda/core/ext/controller.c b/sound/hda/core/ext/controller.c +--- a/sound/hda/core/ext/controller.c ++++ b/sound/hda/core/ext/controller.c +@@ -90,8 +90,10 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_bus *bus) + + for (idx = 0; idx < link_count; idx++) { + hlink = kzalloc_obj(*hlink); +- if (!hlink) ++ if (!hlink) { ++ snd_hdac_ext_link_free_all(bus); + return -ENOMEM; ++ } + hlink->index = idx; + hlink->bus = bus; + hlink->ml_addr = bus->mlcap + AZX_ML_BASE + +diff --git a/sound/hda/core/ext/stream.c b/sound/hda/core/ext/stream.c +--- a/sound/hda/core/ext/stream.c ++++ b/sound/hda/core/ext/stream.c +@@ -102,8 +102,10 @@ int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx, + + for (i = 0; i < num_stream; i++) { + struct hdac_ext_stream *hext_stream = kzalloc_obj(*hext_stream); +- if (!hext_stream) ++ if (!hext_stream) { ++ snd_hdac_ext_stream_free_all(bus); + return -ENOMEM; ++ } + tag = ++stream_tag; + snd_hdac_ext_stream_init(bus, hext_stream, idx, dir, tag); + idx++; +@@ -111,7 +113,6 @@ int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx, + } + + return 0; +- + } + EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all); + +diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c +--- a/sound/soc/amd/yc/acp6x-mach.c ++++ b/sound/soc/amd/yc/acp6x-mach.c +@@ -388,6 +388,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { + DMI_MATCH(DMI_PRODUCT_NAME, "Nitro ANV15-41"), + } + }, ++ { ++ .driver_data = &acp6x_card, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "RB"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Nitro AN17-41"), ++ } ++ }, + { + .driver_data = &acp6x_card, + .matches = { +@@ -899,6 +906,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { + DMI_MATCH(DMI_BOARD_NAME, "TM2423"), + } + }, ++ { ++ .driver_data = &acp6x_card, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "HP 255R 15.6 inch G10 Notebook PC"), ++ } ++ }, + {} + }; + +diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c +--- a/sound/soc/codecs/ab8500-codec.c ++++ b/sound/soc/codecs/ab8500-codec.c +@@ -14,17 +14,14 @@ + * for ST-Ericsson. + */ + +-#include + #include + #include + #include + #include + #include + #include +-#include + #include + #include +-#include + #include + #include + #include +@@ -54,32 +51,9 @@ + /* Macrocell register definitions */ + #define AB8500_GPIO_DIR4_REG 0x13 /* Bank AB8500_MISC */ + +-/* Nr of FIR/IIR-coeff banks in ANC-block */ +-#define AB8500_NR_OF_ANC_COEFF_BANKS 2 +- +-/* Minimum duration to keep ANC IIR Init bit high or +-low before proceeding with the configuration sequence */ +-#define AB8500_ANC_SM_DELAY 2000 +- +-/* Sidetone states */ +-static const char * const enum_sid_state[] = { +- "Unconfigured", +- "Apply FIR", +- "FIR is configured", +-}; +-enum sid_state { +- SID_UNCONFIGURED = 0, +- SID_APPLY_FIR = 1, +- SID_FIR_CONFIGURED = 2, +-}; +- + /* Private data for AB8500 device-driver */ + struct ab8500_codec_drvdata { + struct regmap *regmap; +- struct mutex ctrl_lock; +- +- /* Sidetone */ +- enum sid_state sid_status; + }; + + static inline const char *amic_micbias_str(enum amic_micbias micbias) +@@ -642,9 +616,6 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = { + NULL, 0), + /* Acoustical Noise Cancellation path */ + +- SND_SOC_DAPM_INPUT("ANC Configure Input"), +- SND_SOC_DAPM_OUTPUT("ANC Configure Output"), +- + SND_SOC_DAPM_MUX("ANC Source", + SND_SOC_NOPM, 0, 0, + dapm_anc_in_select), +@@ -687,10 +658,6 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { + {"Main Supply", NULL, "Audio Power"}, + {"Main Supply", NULL, "Audio Analog Power"}, + +- /* ANC Configure */ +- {"ANC Configure Input", NULL, "Main Supply"}, +- {"ANC Configure Output", NULL, "ANC Configure Input"}, +- + /* Powerup charge pump if DA1/2 is in use */ + + {"DA_IN1", NULL, "ab8500_0p"}, +@@ -972,75 +939,6 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes_mic2_vamicx[] = { + {"MIC2 V-AMICx Enable", NULL, "V-AMIC2"}, + }; + +-/* +- * Control-events +- */ +- +-static int sid_status_control_get(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); +- struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev); +- +- guard(mutex)(&drvdata->ctrl_lock); +- ucontrol->value.enumerated.item[0] = drvdata->sid_status; +- +- return 0; +-} +- +-/* Write sidetone FIR-coefficients configuration sequence */ +-static int sid_status_control_put(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); +- struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev); +- unsigned int param, sidconf; +- int status = 1; +- +- dev_dbg(component->dev, "%s: Enter\n", __func__); +- +- if (ucontrol->value.enumerated.item[0] != SID_APPLY_FIR) { +- dev_err(component->dev, +- "%s: ERROR: This control supports '%s' only!\n", +- __func__, enum_sid_state[SID_APPLY_FIR]); +- return -EIO; +- } +- +- guard(mutex)(&drvdata->ctrl_lock); +- +- sidconf = snd_soc_component_read(component, AB8500_SIDFIRCONF); +- if (((sidconf & BIT(AB8500_SIDFIRCONF_FIRSIDBUSY)) != 0)) { +- if ((sidconf & BIT(AB8500_SIDFIRCONF_ENFIRSIDS)) == 0) { +- dev_err(component->dev, "%s: Sidetone busy while off!\n", +- __func__); +- status = -EPERM; +- } else { +- status = -EBUSY; +- } +- dev_dbg(component->dev, "%s: Exit\n", __func__); +- return status; +- } +- +- snd_soc_component_write(component, AB8500_SIDFIRADR, 0); +- +- for (param = 0; param < AB8500_SID_FIR_COEFFS; param++) { +- snd_soc_component_write(component, AB8500_SIDFIRCOEF1, 0); +- snd_soc_component_write(component, AB8500_SIDFIRCOEF2, 0); +- } +- +- snd_soc_component_update_bits(component, AB8500_SIDFIRADR, +- BIT(AB8500_SIDFIRADR_FIRSIDSET), +- BIT(AB8500_SIDFIRADR_FIRSIDSET)); +- snd_soc_component_update_bits(component, AB8500_SIDFIRADR, +- BIT(AB8500_SIDFIRADR_FIRSIDSET), 0); +- +- drvdata->sid_status = SID_FIR_CONFIGURED; +- +- dev_dbg(component->dev, "%s: Exit\n", __func__); +- +- return status; +-} +- + /* + * Controls - Non-DAPM ASoC + */ +@@ -1324,9 +1222,6 @@ static SOC_ENUM_SINGLE_DECL(soc_enum_bfifomast, + AB8500_FIFOCONF3, AB8500_FIFOCONF3_BFIFOMAST_SHIFT, + enum_slavemaster); + +-/* Sidetone */ +-static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_sidstate, enum_sid_state); +- + /* ANC */ + + static struct snd_kcontrol_new ab8500_ctrls[] = { +@@ -1617,8 +1512,6 @@ static struct snd_kcontrol_new ab8500_ctrls[] = { + AB8500_ANC_WARP_DELAY_MIN, AB8500_ANC_WARP_DELAY_MAX, 0), + + /* Sidetone */ +- SOC_ENUM_EXT("Sidetone Status", soc_enum_sidstate, +- sid_status_control_get, sid_status_control_put), + SOC_SINGLE_STROBE("Sidetone Reset", + AB8500_SIDFIRADR, AB8500_SIDFIRADR_FIRSIDSET, 0), + }; +@@ -2148,10 +2041,8 @@ static void ab8500_codec_of_probe(struct device *dev, struct device_node *np, + + static int ab8500_codec_probe(struct snd_soc_component *component) + { +- struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); + struct device *dev = component->dev; + struct device_node *np = dev->of_node; +- struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(dev); + struct ab8500_codec_platform_data codec_pdata; + int status; + +@@ -2184,10 +2075,6 @@ static int ab8500_codec_probe(struct snd_soc_component *component) + snd_soc_component_write(component, AB8500_SHORTCIRCONF, + BIT(AB8500_SHORTCIRCONF_HSZCDDIS)); + +- snd_soc_dapm_disable_pin(dapm, "ANC Configure Input"); +- +- mutex_init(&drvdata->ctrl_lock); +- + return status; + } + +@@ -2216,7 +2103,6 @@ static int ab8500_codec_driver_probe(struct platform_device *pdev) + GFP_KERNEL); + if (!drvdata) + return -ENOMEM; +- drvdata->sid_status = SID_UNCONFIGURED; + dev_set_drvdata(&pdev->dev, drvdata); + + drvdata->regmap = devm_regmap_init(&pdev->dev, NULL, &pdev->dev, +diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c +--- a/sound/soc/codecs/cs35l56-sdw.c ++++ b/sound/soc/codecs/cs35l56-sdw.c +@@ -386,11 +386,8 @@ static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev) + { + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + +- if (!cs35l56->base.init_done) +- return 0; +- +- /* runtime_resume unmasks the interrupt */ +- cs35l56_mask_soundwire_interrupts(cs35l56); ++ if (cs35l56->sdw_attached) ++ cs35l56_mask_soundwire_interrupts(cs35l56); + + return cs35l56_system_suspend(dev); + } +diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c +--- a/sound/soc/codecs/cs35l56.c ++++ b/sound/soc/codecs/cs35l56.c +@@ -18,9 +18,11 @@ + #include + #include + #include ++#include + #include + #include + #include ++#include + #include + #include + #include +@@ -37,6 +39,13 @@ + #include "wm_adsp.h" + #include "cs35l56.h" + ++/* ++ * snd_soc_register_component() can call component_probe() on all instances ++ * in a card, so deferred registration must be protected across all instances. ++ */ ++static DEFINE_MUTEX(cs35l56_component_register_lock); ++static bool cs35l56_shutting_down; ++ + void cs35l56_mask_soundwire_interrupts(struct cs35l56_private *cs35l56) + { + /* +@@ -1957,6 +1966,11 @@ static void cs35l56_component_register_work(struct work_struct *work) + component_register_work); + int ret; + ++ guard(mutex)(&cs35l56_component_register_lock); ++ ++ if (cs35l56_shutting_down) ++ return; ++ + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(cs35l56->base.dev, pm_err); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm_err); + if (ret) { +@@ -2217,6 +2231,37 @@ EXPORT_NS_GPL_DEV_PM_OPS(cs35l56_pm_ops_i2c_spi, SND_SOC_CS35L56_CORE) = { + }; + #endif + ++static int cs35l56_reboot_notify(struct notifier_block *nb, ++ unsigned long action, void *data) ++{ ++ guard(mutex)(&cs35l56_component_register_lock); ++ cs35l56_shutting_down = true; ++ ++ return NOTIFY_DONE; ++} ++ ++static struct notifier_block cs35l56_reboot_notifier = { ++ .notifier_call = cs35l56_reboot_notify, ++}; ++ ++static int __init cs35l56_modinit(void) ++{ ++ /* ++ * Use reboot notifier to prevent race between shutdown and ++ * snd_soc_register_component(). Driver shutdown() callback would ++ * run too late, after device_shutdown() is already walking the ++ * device list that component registration can modify. ++ */ ++ return register_reboot_notifier(&cs35l56_reboot_notifier); ++} ++module_init(cs35l56_modinit); ++ ++static void __exit cs35l56_modexit(void) ++{ ++ unregister_reboot_notifier(&cs35l56_reboot_notifier); ++} ++module_exit(cs35l56_modexit); ++ + MODULE_DESCRIPTION("ASoC CS35L56 driver"); + MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED"); + MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB"); +diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h +--- a/sound/soc/codecs/cs35l56.h ++++ b/sound/soc/codecs/cs35l56.h +@@ -32,6 +32,7 @@ struct sdw_slave; + struct cs35l56_private { + struct wm_adsp dsp; /* must be first member */ + struct cs35l56_base base; ++ struct work_struct component_register_work; + struct work_struct dsp_work; + struct workqueue_struct *dsp_wq; + struct snd_soc_component *component; +@@ -41,6 +42,7 @@ struct cs35l56_private { + const char *fallback_fw_suffix; + bool soft_resetting; + bool sdw_attached; ++ bool component_registered; + struct completion init_completion; + + int speaker_id; +diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c +--- a/sound/soc/codecs/es8326.c ++++ b/sound/soc/codecs/es8326.c +@@ -26,6 +26,7 @@ struct es8326_priv { + struct snd_soc_component *component; + struct delayed_work jack_detect_work; + struct delayed_work button_press_work; ++ struct delayed_work capture_pop_work; + struct snd_soc_jack *jack; + int irq; + /* The lock protects the situation that an irq is generated +@@ -628,6 +629,7 @@ static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction) + regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, + 0x30, 0x00); + } else { ++ cancel_delayed_work_sync(&es8326->capture_pop_work); + regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE, + 0x0F, 0x0F); + if (es8326->version > ES8326_VERSION_B) { +@@ -666,8 +668,9 @@ static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction) + regmap_update_bits(es8326->regmap, ES8326_ANA_MICBIAS, 0x70, 0x70); + regmap_update_bits(es8326->regmap, ES8326_VMIDSEL, 0x40, 0x00); + } +- regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE, +- 0x0F, 0x00); ++ ++ queue_delayed_work(system_dfl_wq, &es8326->capture_pop_work, ++ msecs_to_jiffies(40)); + } + } + return 0; +@@ -773,6 +776,15 @@ static void es8326_disable_micbias(struct snd_soc_component *component) + snd_soc_dapm_mutex_unlock(dapm); + } + ++static void es8326_capture_pop_handler(struct work_struct *work) ++{ ++ struct es8326_priv *es8326 = ++ container_of(work, struct es8326_priv, capture_pop_work.work); ++ ++ regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE, ++ 0x0F, 0x00); ++} ++ + /* + * For button detection, set the following in soundcard + * snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); +@@ -1140,6 +1152,7 @@ static int es8326_suspend(struct snd_soc_component *component) + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + + cancel_delayed_work_sync(&es8326->jack_detect_work); ++ cancel_delayed_work_sync(&es8326->capture_pop_work); + es8326_disable_micbias(component); + es8326->calibrated = false; + regmap_write(es8326->regmap, ES8326_CLK_MUX, 0x2d); +@@ -1291,6 +1304,8 @@ static int es8326_i2c_probe(struct i2c_client *i2c) + es8326_jack_detect_handler); + INIT_DELAYED_WORK(&es8326->button_press_work, + es8326_jack_button_handler); ++ INIT_DELAYED_WORK(&es8326->capture_pop_work, ++ es8326_capture_pop_handler); + /* ES8316 is level-based while ES8326 is edge-based */ + ret = devm_request_threaded_irq(&i2c->dev, es8326->irq, NULL, es8326_irq, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, +diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c +--- a/sound/soc/codecs/es8389.c ++++ b/sound/soc/codecs/es8389.c +@@ -812,6 +812,23 @@ static int es8389_pcm_hw_free(struct snd_pcm_substream *substream, + return 0; + } + ++static void es8389_standby(struct snd_soc_component *component) ++{ ++ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); ++ ++ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x04); ++ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x04); ++ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xD4); ++ usleep_range(70000, 72000); ++ regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x59); ++ regmap_write(es8389->regmap, ES8389_ADC_EN, 0x00); ++ regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0x00); ++ regmap_write(es8389->regmap, ES8389_RESET, 0x3E); ++ regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x80); ++ usleep_range(8000, 8500); ++ regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x00); ++} ++ + static int es8389_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) + { +@@ -834,18 +851,7 @@ static int es8389_set_bias_level(struct snd_soc_component *component, + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: +- regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x04); +- regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x04); +- regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xD4); +- usleep_range(70000, 72000); +- regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x59); +- regmap_write(es8389->regmap, ES8389_ADC_EN, 0x00); +- regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0x00); +- regmap_write(es8389->regmap, ES8389_RESET, 0x3E); +- regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x80); +- usleep_range(8000, 8500); +- regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x00); +- ++ es8389_standby(component); + clk_disable_unprepare(es8389->mclk); + break; + case SND_SOC_BIAS_OFF: +@@ -1015,7 +1021,7 @@ static int es8389_suspend(struct snd_soc_component *component) + { + struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); + +- es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY); ++ es8389_standby(component); + regcache_cache_only(es8389->regmap, true); + regcache_mark_dirty(es8389->regmap); + +@@ -1084,7 +1090,7 @@ static int es8389_probe(struct snd_soc_component *component) + + es8389->hpf_freq = ES8389_HPF_DEFAULT; + es8389_init(component); +- es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY); ++ es8389_standby(component); + + return 0; + } +diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c +--- a/sound/soc/codecs/rt1318-sdw.c ++++ b/sound/soc/codecs/rt1318-sdw.c +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + #include + #include "rt1318-sdw.h" + +@@ -564,11 +565,10 @@ static int rt1318_sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_soc_component *component = dai->component; + struct rt1318_sdw_priv *rt1318 = + snd_soc_component_get_drvdata(component); +- struct sdw_stream_config stream_config; ++ struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config; +- enum sdw_data_direction direction; + struct sdw_stream_runtime *sdw_stream; +- int retval, port, num_channels, ch_mask; ++ int retval, port; + unsigned int sampling_rate; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); +@@ -582,23 +582,13 @@ static int rt1318_sdw_hw_params(struct snd_pcm_substream *substream, + + /* SoundWire specific configuration */ + /* port 1 for playback */ +- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +- direction = SDW_DATA_DIR_RX; ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + port = 1; +- } else { +- direction = SDW_DATA_DIR_TX; ++ else + port = 2; +- } + +- num_channels = params_channels(params); +- ch_mask = (1 << num_channels) - 1; +- +- stream_config.frame_rate = params_rate(params); +- stream_config.ch_count = num_channels; +- stream_config.bps = snd_pcm_format_width(params_format(params)); +- stream_config.direction = direction; +- +- port_config.ch_mask = ch_mask; ++ /* SoundWire specific configuration */ ++ snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + port_config.num = port; + + retval = sdw_stream_add_slave(rt1318->sdw_slave, &stream_config, +diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c +--- a/sound/soc/codecs/rt1320-sdw.c ++++ b/sound/soc/codecs/rt1320-sdw.c +@@ -3246,7 +3246,7 @@ static int rt1320_sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_soc_component *component = dai->component; + struct rt1320_sdw_priv *rt1320 = + snd_soc_component_get_drvdata(component); +- struct sdw_stream_config stream_config; ++ struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config; + struct sdw_port_config dmic_port_config[2]; + struct sdw_stream_runtime *sdw_stream; +diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c +--- a/sound/soc/codecs/rt712-sdca.c ++++ b/sound/soc/codecs/rt712-sdca.c +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + #include + #include "rt712-sdca.h" + +@@ -1449,11 +1450,10 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + { + struct snd_soc_component *component = dai->component; + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); +- struct sdw_stream_config stream_config; ++ struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config; +- enum sdw_data_direction direction; + struct sdw_stream_runtime *sdw_stream; +- int retval, port, num_channels; ++ int retval, port; + unsigned int sampling_rate; + + dev_dbg(dai->dev, "%s %s id %d", __func__, dai->name, dai->id); +@@ -1471,7 +1471,6 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + + /* SoundWire specific configuration */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +- direction = SDW_DATA_DIR_RX; + if (dai->id == RT712_AIF1) + port = 1; + else if (dai->id == RT712_AIF2) +@@ -1479,7 +1478,6 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + else + return -EINVAL; + } else { +- direction = SDW_DATA_DIR_TX; + if (dai->id == RT712_AIF1) + port = 4; + else if (dai->id == RT712_AIF3) +@@ -1488,13 +1486,8 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + return -EINVAL; + } + +- stream_config.frame_rate = params_rate(params); +- stream_config.ch_count = params_channels(params); +- stream_config.bps = snd_pcm_format_width(params_format(params)); +- stream_config.direction = direction; +- +- num_channels = params_channels(params); +- port_config.ch_mask = GENMASK(num_channels - 1, 0); ++ /* SoundWire specific configuration */ ++ snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + port_config.num = port; + + retval = sdw_stream_add_slave(rt712->slave, &stream_config, +diff --git a/sound/soc/codecs/rt721-sdca.c b/sound/soc/codecs/rt721-sdca.c +--- a/sound/soc/codecs/rt721-sdca.c ++++ b/sound/soc/codecs/rt721-sdca.c +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + #include + + #include "rt721-sdca.h" +@@ -206,6 +207,7 @@ static void rt721_sdca_amp_preset(struct rt721_sdca_priv *rt721) + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55, + RT721_SDCA_CTL_FU_MUTE, CH_02), 0x00); ++ regmap_write(rt721->regmap, 0x2f5d, 0x1); + } + + static void rt721_sdca_jack_preset(struct rt721_sdca_priv *rt721) +@@ -1268,11 +1270,10 @@ static int rt721_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + { + struct snd_soc_component *component = dai->component; + struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); +- struct sdw_stream_config stream_config; ++ struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config; +- enum sdw_data_direction direction; + struct sdw_stream_runtime *sdw_stream; +- int retval, port, num_channels; ++ int retval, port; + unsigned int sampling_rate; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); +@@ -1291,7 +1292,6 @@ static int rt721_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + * RT721_AIF3 with port = 6 for digital-mic capture + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +- direction = SDW_DATA_DIR_RX; + if (dai->id == RT721_AIF1) + port = 1; + else if (dai->id == RT721_AIF2) +@@ -1299,7 +1299,6 @@ static int rt721_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + else + return -EINVAL; + } else { +- direction = SDW_DATA_DIR_TX; + if (dai->id == RT721_AIF1) + port = 2; + else if (dai->id == RT721_AIF3) +@@ -1307,13 +1306,9 @@ static int rt721_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + else + return -EINVAL; + } +- stream_config.frame_rate = params_rate(params); +- stream_config.ch_count = params_channels(params); +- stream_config.bps = snd_pcm_format_width(params_format(params)); +- stream_config.direction = direction; + +- num_channels = params_channels(params); +- port_config.ch_mask = GENMASK(num_channels - 1, 0); ++ /* SoundWire specific configuration */ ++ snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + port_config.num = port; + + retval = sdw_stream_add_slave(rt721->slave, &stream_config, +diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c +--- a/sound/soc/codecs/rt722-sdca.c ++++ b/sound/soc/codecs/rt722-sdca.c +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + #include + + #include "rt722-sdca.h" +@@ -1442,11 +1443,10 @@ static int rt722_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + { + struct snd_soc_component *component = dai->component; + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); +- struct sdw_stream_config stream_config; ++ struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config; +- enum sdw_data_direction direction; + struct sdw_stream_runtime *sdw_stream; +- int retval, port, num_channels; ++ int retval, port; + unsigned int sampling_rate; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); +@@ -1465,7 +1465,6 @@ static int rt722_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + * RT722_AIF3 with port = 6 for digital-mic capture + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +- direction = SDW_DATA_DIR_RX; + if (dai->id == RT722_AIF1) + port = 1; + else if (dai->id == RT722_AIF2) +@@ -1473,7 +1472,6 @@ static int rt722_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + else + return -EINVAL; + } else { +- direction = SDW_DATA_DIR_TX; + if (dai->id == RT722_AIF1) + port = 2; + else if (dai->id == RT722_AIF3) +@@ -1481,13 +1479,9 @@ static int rt722_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + else + return -EINVAL; + } +- stream_config.frame_rate = params_rate(params); +- stream_config.ch_count = params_channels(params); +- stream_config.bps = snd_pcm_format_width(params_format(params)); +- stream_config.direction = direction; + +- num_channels = params_channels(params); +- port_config.ch_mask = GENMASK(num_channels - 1, 0); ++ /* SoundWire specific configuration */ ++ snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + port_config.num = port; + + retval = sdw_stream_add_slave(rt722->slave, &stream_config, +diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c +--- a/sound/soc/codecs/tas2783-sdw.c ++++ b/sound/soc/codecs/tas2783-sdw.c +@@ -1001,6 +1001,31 @@ static s32 tas_sdw_hw_params(struct snd_pcm_substream *substream, + /* SoundWire specific configuration */ + snd_sdw_params_to_config(substream, params, + &stream_config, &port_config); ++ ++ /* ++ * The two mono amps each render one channel of the stereo stream: ++ * snd_sdw_params_to_config() hands every codec the full mask for ++ * playback, which leaves the pair in mirror mode and one channel ++ * unreproduced. Claim a single channel instead, keyed off the ++ * machine-assigned component prefix rather than the SoundWire ++ * address, which is board-specific: soc_sdw_ti_amp.c names the amps ++ * tas2783-1..4. ++ * ++ * Which side an amp then renders does not follow from the bit that ++ * is set - sdw_compute_slave_ports() advances the payload offset by ++ * the popcount of ch_mask and never looks at which bit it is - but ++ * from the amp's position in the codec order of the DAI link, which ++ * on these boards matches the prefix numbering. ++ */ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && ++ params_channels(params) == 2 && component->name_prefix) { ++ const char *idx_str = strrchr(component->name_prefix, '-'); ++ unsigned long idx; ++ ++ if (idx_str && !kstrtoul(idx_str + 1, 10, &idx) && idx) ++ port_config.ch_mask = (idx & 1) ? BIT(0) : BIT(1); ++ } ++ + /* port 1 for playback */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + port_config.num = 1; +diff --git a/sound/soc/intel/avs/boards/hdaudio.c b/sound/soc/intel/avs/boards/hdaudio.c +--- a/sound/soc/intel/avs/boards/hdaudio.c ++++ b/sound/soc/intel/avs/boards/hdaudio.c +@@ -15,6 +15,22 @@ + #include "../../../codecs/hda.h" + #include "../utils.h" + ++static int avs_link_startup(struct snd_pcm_substream *substream) ++{ ++ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); ++ const struct snd_soc_pcm_stream *stream_info; ++ struct snd_soc_dai *codec_dai; ++ ++ codec_dai = snd_soc_rtd_to_codec(rtd, 0); ++ stream_info = snd_soc_dai_get_pcm_stream(codec_dai, substream->stream); ++ ++ return snd_pcm_hw_constraint_msbits(substream->runtime, 0, 0, stream_info->sig_bits); ++} ++ ++static const struct snd_soc_ops avs_link_ops = { ++ .startup = avs_link_startup, ++}; ++ + static int avs_create_dai_links(struct device *dev, struct hda_codec *codec, int pcm_count, + struct snd_soc_dai_link **links) + { +@@ -43,6 +59,7 @@ static int avs_create_dai_links(struct device *dev, struct hda_codec *codec, int + dl[i].platforms = platform; + dl[i].num_platforms = 1; + dl[i].ignore_pmdown_time = 1; ++ dl[i].ops = &avs_link_ops; + + dl[i].codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL); + dl[i].cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL); +diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c +--- a/sound/soc/intel/avs/core.c ++++ b/sound/soc/intel/avs/core.c +@@ -92,16 +92,28 @@ static int avs_hdac_bus_init_streams(struct hdac_bus *bus) + { + unsigned int cp_streams, pb_streams; + unsigned int gcap; ++ int ret; + + gcap = snd_hdac_chip_readw(bus, GCAP); + cp_streams = (gcap >> 8) & 0x0F; + pb_streams = (gcap >> 12) & 0x0F; + bus->num_streams = cp_streams + pb_streams; + +- snd_hdac_ext_stream_init_all(bus, 0, cp_streams, SNDRV_PCM_STREAM_CAPTURE); +- snd_hdac_ext_stream_init_all(bus, cp_streams, pb_streams, SNDRV_PCM_STREAM_PLAYBACK); ++ ret = snd_hdac_ext_stream_init_all(bus, 0, cp_streams, SNDRV_PCM_STREAM_CAPTURE); ++ if (ret) ++ return ret; ++ ret = snd_hdac_ext_stream_init_all(bus, cp_streams, pb_streams, SNDRV_PCM_STREAM_PLAYBACK); ++ if (ret) ++ goto err; + +- return snd_hdac_bus_alloc_stream_pages(bus); ++ ret = snd_hdac_bus_alloc_stream_pages(bus); ++ if (ret) ++ goto err; ++ ++ return 0; ++err: ++ snd_hdac_ext_stream_free_all(bus); ++ return ret; + } + + static bool avs_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset) +@@ -383,6 +395,18 @@ static int avs_bus_init(struct avs_dev *adev, struct pci_dev *pci, const struct + struct device *dev = &pci->dev; + int ret; + ++ ipc = devm_kzalloc(dev, sizeof(*ipc), GFP_KERNEL); ++ if (!ipc) ++ return -ENOMEM; ++ ++ adev->modcfg_buf = devm_kzalloc(dev, AVS_MAILBOX_SIZE, GFP_KERNEL); ++ if (!adev->modcfg_buf) ++ return -ENOMEM; ++ ++ ret = avs_ipc_init(ipc, dev); ++ if (ret < 0) ++ return ret; ++ + ret = snd_hdac_ext_bus_init(&bus->core, dev, NULL, &soc_hda_ext_bus_ops); + if (ret < 0) + return ret; +@@ -394,17 +418,6 @@ static int avs_bus_init(struct avs_dev *adev, struct pci_dev *pci, const struct + bus->mixer_assigned = -1; + mutex_init(&bus->prepare_mutex); + +- ipc = devm_kzalloc(dev, sizeof(*ipc), GFP_KERNEL); +- if (!ipc) +- return -ENOMEM; +- ret = avs_ipc_init(ipc, dev); +- if (ret < 0) +- return ret; +- +- adev->modcfg_buf = devm_kzalloc(dev, AVS_MAILBOX_SIZE, GFP_KERNEL); +- if (!adev->modcfg_buf) +- return -ENOMEM; +- + adev->dev = dev; + adev->spec = (const struct avs_spec *)id->driver_data; + adev->ipc = ipc; +@@ -456,13 +469,14 @@ static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) + + ret = pcim_request_all_regions(pci, "AVS HDAudio"); + if (ret < 0) +- return ret; ++ goto err_request_regions; + + bus->addr = pci_resource_start(pci, 0); + bus->remap_addr = pci_ioremap_bar(pci, 0); + if (!bus->remap_addr) { + dev_err(bus->dev, "ioremap error\n"); +- return -ENXIO; ++ ret = -ENXIO; ++ goto err_request_regions; + } + + adev->dsp_ba = pci_ioremap_bar(pci, 4); +@@ -526,6 +540,8 @@ static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) + iounmap(adev->dsp_ba); + err_remap_bar4: + iounmap(bus->remap_addr); ++err_request_regions: ++ snd_hdac_ext_bus_exit(bus); + return ret; + } + +diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c +--- a/sound/soc/intel/avs/debugfs.c ++++ b/sound/soc/intel/avs/debugfs.c +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -236,15 +237,20 @@ static int strace_open(struct inode *inode, struct file *file) + if (!try_module_get(adev->dev->driver->owner)) + return -ENODEV; + +- if (kfifo_initialized(&adev->trace_fifo)) +- return -EBUSY; ++ if (kfifo_initialized(&adev->trace_fifo)) { ++ ret = -EBUSY; ++ goto err; ++ } + + ret = kfifo_alloc(&adev->trace_fifo, PAGE_SIZE, GFP_KERNEL); + if (ret < 0) +- return ret; ++ goto err; + + file->private_data = adev; + return 0; ++err: ++ module_put(adev->dev->driver->owner); ++ return ret; + } + + static int strace_release(struct inode *inode, struct file *file) +diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c +--- a/sound/soc/intel/avs/ipc.c ++++ b/sound/soc/intel/avs/ipc.c +@@ -172,7 +172,7 @@ static void avs_dsp_exception_caught(struct avs_dev *adev, union avs_notify_msg + + /* Avoid deadlock as the exception may be the response to SET_D0IX. */ + if (current_work() != &ipc->d0ix_work.work) +- cancel_delayed_work_sync(&ipc->d0ix_work); ++ cancel_delayed_work(&ipc->d0ix_work); + ipc->in_d0ix = false; + /* Re-enabled on recovery completion. */ + pm_runtime_disable(adev->dev); +@@ -395,11 +395,11 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request + struct avs_ipc *ipc = adev->ipc; + int ret; + ++ guard(mutex)(&ipc->msg_mutex); ++ + if (!ipc->ready) + return -EPERM; + +- guard(mutex)(&ipc->msg_mutex); +- + spin_lock(&ipc->rx_lock); + avs_ipc_msg_init(ipc, reply); + avs_dsp_send_tx(adev, request, true); +diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig +--- a/sound/soc/intel/boards/Kconfig ++++ b/sound/soc/intel/boards/Kconfig +@@ -533,12 +533,14 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH + select SND_SOC_CS35L56_SPI + select SND_SOC_CS35L56_SDW + select SND_SOC_ES9356 ++ imply SND_SOC_TAC5XX2_SDW + select SND_SOC_DMIC + select SND_SOC_INTEL_HDA_DSP_COMMON + imply SND_SOC_SDW_MOCKUP + help + Add support for Intel SoundWire-based platforms connected to +- MAX98373, RT700, RT711, RT1308 and RT715 ++ MAX98373, RT700, RT711, RT1308, RT715, TAC5XX2_SDW family (including ++ TAC5572, TAC5682). + If unsure select "N". + + endif +diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c +--- a/sound/soc/intel/boards/sof_rt5682.c ++++ b/sound/soc/intel/boards/sof_rt5682.c +@@ -908,6 +908,14 @@ static const struct platform_device_id board_ids[] = { + SOF_SSP_PORT_BT_OFFLOAD(2) | + SOF_BT_OFFLOAD_PRESENT), + }, ++ { ++ .name = "nvl_rt5682_def", ++ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | ++ SOF_SSP_PORT_CODEC(0) | ++ SOF_SSP_PORT_AMP(1) | ++ SOF_SSP_PORT_BT_OFFLOAD(2) | ++ SOF_BT_OFFLOAD_PRESENT), ++ }, + { + .name = "ptl_rt5682_c1_h02", + .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | +diff --git a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c +--- a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c ++++ b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c +@@ -51,10 +51,10 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_nvl_machines[] = { + }, + { + .comp_ids = &nvl_rt5682_rt5682s_hp, +- .drv_name = "sof_rt5682", +- .sof_tplg_filename = "sof-nvl-rt5682", /* the tplg suffix is added at run time */ +- .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER | +- SND_SOC_ACPI_TPLG_INTEL_SSP_MSB, ++ .drv_name = "nvl_rt5682_def", ++ .sof_tplg_filename = "sof-nvl", /* the tplg suffix is added at run time */ ++ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_AMP_NAME | ++ SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME, + }, + /* place amp/hdmi-in only boards in the end of table */ + { diff --git a/pkgbuilds/linux-omarchy-bore/0523-asoc-fixes-2.patch.sig b/pkgbuilds/linux-omarchy-bore/0523-asoc-fixes-2.patch.sig new file mode 100644 index 0000000..906bd76 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0523-asoc-fixes-2.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0524-sound-fixes-4.patch b/pkgbuilds/linux-omarchy-bore/0524-sound-fixes-4.patch new file mode 100644 index 0000000..39c0ebf --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0524-sound-fixes-4.patch @@ -0,0 +1,135 @@ +diff --git a/sound/core/init.c b/sound/core/init.c +--- a/sound/core/init.c ++++ b/sound/core/init.c +@@ -364,7 +364,7 @@ static int snd_card_init(struct snd_card *card, struct device *parent, + sound_debugfs_root); + #endif + #ifdef CONFIG_SND_CTL_DEBUG +- card->value_buf = kmalloc(sizeof(*card->value_buf), GFP_KERNEL); ++ card->value_buf = kmalloc_obj(*card->value_buf); + if (!card->value_buf) + return -ENOMEM; + #endif +diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c +--- a/sound/hda/codecs/realtek/alc269.c ++++ b/sound/hda/codecs/realtek/alc269.c +@@ -4304,6 +4304,7 @@ enum { + ALC287_FIXUP_LEGION_16ACHG6, + ALC287_FIXUP_CS35L41_I2C_2, + ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED, ++ ALC287_FIXUP_CS35L41_I2C_2_HP_MUTE_LEDS, + ALC287_FIXUP_CS35L41_I2C_4, + ALC245_FIXUP_CS35L41_SPI_1, + ALC245_FIXUP_CS35L41_SPI_2, +@@ -6615,6 +6616,12 @@ static const struct hda_fixup alc269_fixups[] = { + .chained = true, + .chain_id = ALC285_FIXUP_HP_MUTE_LED, + }, ++ [ALC287_FIXUP_CS35L41_I2C_2_HP_MUTE_LEDS] = { ++ .type = HDA_FIXUP_FUNC, ++ .v.func = cs35l41_fixup_i2c_two, ++ .chained = true, ++ .chain_id = ALC245_FIXUP_HP_X360_MUTE_LEDS, ++ }, + [ALC287_FIXUP_CS35L41_I2C_4] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l41_fixup_i2c_four, +@@ -7372,6 +7379,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x8158, "HP", ALC256_FIXUP_HP_HEADSET_MIC), + SND_PCI_QUIRK(0x103c, 0x820d, "HP Pavilion 15", ALC295_FIXUP_HP_X360), + SND_PCI_QUIRK(0x103c, 0x8256, "HP", ALC221_FIXUP_HP_FRONT_MIC), ++ SND_PCI_QUIRK(0x103c, 0x8259, "HP OMEN 15-ax202nf", ALC269_FIXUP_HP_MUTE_LED_MIC3), + SND_PCI_QUIRK(0x103c, 0x827e, "HP x360", ALC295_FIXUP_HP_X360), + SND_PCI_QUIRK(0x103c, 0x827f, "HP x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), + SND_PCI_QUIRK(0x103c, 0x82bf, "HP G3 mini", ALC221_FIXUP_HP_MIC_NO_PRESENCE), +@@ -7582,6 +7590,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x8b96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8ba9, "HP Omen 16-wd0xxx", ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT), ++ SND_PCI_QUIRK(0x103c, 0x8bb1, "HP Victus 15-fa1xxx (MB 8BB1)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8bb3, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8bb4, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8bb6, "HP Laptop 15-fd0039nt", ALC236_FIXUP_HP_15_FD0XXX), +@@ -7674,7 +7683,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x8d92, "HP ZBook Firefly 16 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8dcd, "HP Victus 15-fa2xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8d9b, "HP 17 Turbine OmniBook 7 UMA", ALC287_FIXUP_CS35L41_I2C_2), +- SND_PCI_QUIRK(0x103c, 0x8d9c, "HP 17 Turbine OmniBook 7 DIS", ALC287_FIXUP_CS35L41_I2C_2), ++ SND_PCI_QUIRK(0x103c, 0x8d9c, "HP 17 Turbine OmniBook 7 DIS", ALC287_FIXUP_CS35L41_I2C_2_HP_MUTE_LEDS), + SND_PCI_QUIRK(0x103c, 0x8d9d, "HP 17 Turbine OmniBook X UMA", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8d9e, "HP 17 Turbine OmniBook X DIS", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8d9f, "HP 14 Cadet (x360)", ALC287_FIXUP_CS35L41_I2C_2), +diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c +--- a/sound/usb/endpoint.c ++++ b/sound/usb/endpoint.c +@@ -449,7 +449,9 @@ static void push_back_to_ready_list(struct snd_usb_endpoint *ep, + struct snd_urb_ctx *ctx) + { + guard(spinlock_irqsave)(&ep->lock); +- list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs); ++ /* ctx may still be linked: a stale completion racing a stop/restart. */ ++ if (list_empty(&ctx->ready_list)) ++ list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs); + } + + /* +@@ -1037,6 +1039,7 @@ void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep) + */ + static int stop_urbs(struct snd_usb_endpoint *ep, bool force, bool keep_pending) + { ++ struct snd_urb_ctx *ctx, *n; + unsigned int i; + + if (!force && atomic_read(&ep->running)) +@@ -1046,7 +1049,9 @@ static int stop_urbs(struct snd_usb_endpoint *ep, bool force, bool keep_pending) + return 0; + + scoped_guard(spinlock_irqsave, &ep->lock) { +- INIT_LIST_HEAD(&ep->ready_playback_urbs); ++ /* Unlink each ctx; INIT_LIST_HEAD() alone would leave them looking linked. */ ++ list_for_each_entry_safe(ctx, n, &ep->ready_playback_urbs, ready_list) ++ list_del_init(&ctx->ready_list); + ep->next_packet_head = 0; + ep->next_packet_queued = 0; + } +diff --git a/sound/usb/implicit.c b/sound/usb/implicit.c +--- a/sound/usb/implicit.c ++++ b/sound/usb/implicit.c +@@ -76,6 +76,7 @@ static const struct snd_usb_implicit_fb_match playback_implicit_fb_quirks[] = { + + /* Implicit feedback quirk table for capture: only FIXED type */ + static const struct snd_usb_implicit_fb_match capture_implicit_fb_quirks[] = { ++ IMPLICIT_FB_FIXED_DEV(0x1397, 0x0004, 0x01, 1), /* Behringer FCA1616 */ + {} /* terminator */ + }; + +diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c +--- a/sound/usb/mixer_maps.c ++++ b/sound/usb/mixer_maps.c +@@ -531,6 +531,15 @@ static const struct usbmix_name_map audient_id24_map[] = { + {} + }; + ++/* ++ * The GC553Pro returns no data for GET_CUR on its advertised mute control. ++ * SET_CUR succeeds but does not mute capture, so skip the control entirely. ++ */ ++static const struct usbmix_name_map avermedia_gc553pro_map[] = { ++ { 3, NULL, UAC_FU_MUTE }, ++ {} ++}; ++ + /* + * Control map entries + */ +@@ -577,6 +586,10 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { + .id = USB_ID(0x0763, 0x2031), + .selector_map = c400_selectors, + }, ++ { ++ .id = USB_ID(0x07ca, 0x1553), ++ .map = avermedia_gc553pro_map, ++ }, + { + .id = USB_ID(0x08bb, 0x2702), + .map = linex_map, diff --git a/pkgbuilds/linux-omarchy-bore/0524-sound-fixes-4.patch.sig b/pkgbuilds/linux-omarchy-bore/0524-sound-fixes-4.patch.sig new file mode 100644 index 0000000..ef2122d Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0524-sound-fixes-4.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0525-asoc-fixes-3.patch b/pkgbuilds/linux-omarchy-bore/0525-asoc-fixes-3.patch new file mode 100644 index 0000000..843a983 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0525-asoc-fixes-3.patch @@ -0,0 +1,549 @@ +diff --git a/include/sound/soc_sdw_utils.h b/include/sound/soc_sdw_utils.h +--- a/include/sound/soc_sdw_utils.h ++++ b/include/sound/soc_sdw_utils.h +@@ -250,8 +250,6 @@ int asoc_sdw_cs_amp_init(struct snd_soc_card *card, + struct snd_soc_dai_link *dai_links, + struct asoc_sdw_codec_info *info, + bool playback); +-int asoc_sdw_cs_spk_feedback_rtd_init(struct snd_soc_pcm_runtime *rtd, +- struct snd_soc_dai *dai); + int asoc_sdw_cs35l56_volume_limit(struct snd_soc_card *card, const char *name_prefix); + + /* MAXIM codec support */ +diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c +--- a/sound/soc/amd/acp/acp-sdw-sof-mach.c ++++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c +@@ -314,7 +314,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) + if (!sof_aux) + return -ENOMEM; + +- ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_devs); ++ ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_confs); + if (ret < 0) + return ret; + +@@ -390,7 +390,7 @@ static int mc_probe(struct platform_device *pdev) + ctx->private = amd_ctx; + card = &ctx->card; + card->dev = &pdev->dev; +- card->name = "amd-soundwire"; ++ card->name = "amd-sdw"; + card->owner = THIS_MODULE; + card->late_probe = asoc_sdw_card_late_probe; + +diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -524,13 +524,13 @@ config SND_SOC_ADAU1977 + tristate + + config SND_SOC_ADAU1977_SPI +- tristate ++ tristate "Analog Devices ADAU1977/ADAU1978/ADAU1979 CODEC - SPI" + depends on SPI_MASTER + select SND_SOC_ADAU1977 + select REGMAP_SPI + + config SND_SOC_ADAU1977_I2C +- tristate ++ tristate "Analog Devices ADAU1977/ADAU1978/ADAU1979 CODEC - I2C" + depends on I2C + select SND_SOC_ADAU1977 + select REGMAP_I2C +diff --git a/sound/soc/codecs/adau1977-i2c.c b/sound/soc/codecs/adau1977-i2c.c +--- a/sound/soc/codecs/adau1977-i2c.c ++++ b/sound/soc/codecs/adau1977-i2c.c +@@ -34,9 +34,18 @@ static const struct i2c_device_id adau1977_i2c_ids[] = { + }; + MODULE_DEVICE_TABLE(i2c, adau1977_i2c_ids); + ++static const struct of_device_id adau1977_i2c_of_match[] = { ++ { .compatible = "adi,adau1977" }, ++ { .compatible = "adi,adau1978" }, ++ { .compatible = "adi,adau1979" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, adau1977_i2c_of_match); ++ + static struct i2c_driver adau1977_i2c_driver = { + .driver = { + .name = "adau1977", ++ .of_match_table = adau1977_i2c_of_match, + }, + .probe = adau1977_i2c_probe, + .id_table = adau1977_i2c_ids, +diff --git a/sound/soc/codecs/adau1977-spi.c b/sound/soc/codecs/adau1977-spi.c +--- a/sound/soc/codecs/adau1977-spi.c ++++ b/sound/soc/codecs/adau1977-spi.c +@@ -53,18 +53,18 @@ static const struct spi_device_id adau1977_spi_ids[] = { + }; + MODULE_DEVICE_TABLE(spi, adau1977_spi_ids); + +-static const struct of_device_id adau1977_spi_of_match[] __maybe_unused = { +- { .compatible = "adi,adau1977" }, +- { .compatible = "adi,adau1978" }, +- { .compatible = "adi,adau1979" }, +- { }, ++static const struct of_device_id adau1977_spi_of_match[] = { ++ { .compatible = "adi,adau1977" }, ++ { .compatible = "adi,adau1978" }, ++ { .compatible = "adi,adau1979" }, ++ { }, + }; + MODULE_DEVICE_TABLE(of, adau1977_spi_of_match); + + static struct spi_driver adau1977_spi_driver = { + .driver = { + .name = "adau1977", +- .of_match_table = of_match_ptr(adau1977_spi_of_match), ++ .of_match_table = adau1977_spi_of_match, + }, + .probe = adau1977_spi_probe, + .id_table = adau1977_spi_ids, +diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c +--- a/sound/soc/codecs/rt712-sdca-sdw.c ++++ b/sound/soc/codecs/rt712-sdca-sdw.c +@@ -18,12 +18,16 @@ + static bool rt712_sdca_readable_register(struct device *dev, unsigned int reg) + { + switch (reg) { ++ case 0x004d: + case 0x201a ... 0x201f: + case 0x2029 ... 0x202a: + case 0x202d ... 0x2034: + case 0x2230 ... 0x2232: + case 0x2f01 ... 0x2f0a: + case 0x2f35 ... 0x2f36: ++ case 0x2f3a: ++ case 0x2f3d: ++ case 0x2f41: + case 0x2f50: + case 0x2f54: + case 0x2f58 ... 0x2f5d: +@@ -48,6 +52,7 @@ static bool rt712_sdca_readable_register(struct device *dev, unsigned int reg) + static bool rt712_sdca_volatile_register(struct device *dev, unsigned int reg) + { + switch (reg) { ++ case 0x004d: + case 0x201b: + case 0x201c: + case 0x201d: +@@ -56,6 +61,9 @@ static bool rt712_sdca_volatile_register(struct device *dev, unsigned int reg) + case 0x2230: + case 0x2f01: + case 0x2f35: ++ case 0x2f3a: ++ case 0x2f3d: ++ case 0x2f41: + case 0x320c: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0): + case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... +diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c +--- a/sound/soc/codecs/rt712-sdca.c ++++ b/sound/soc/codecs/rt712-sdca.c +@@ -73,14 +73,57 @@ static int rt712_sdca_index_update_bits(struct rt712_sdca_priv *rt712, + return rt712_sdca_index_write(rt712, nid, reg, tmp); + } + ++static void rt712_sdca_clk_patch(struct rt712_sdca_priv *rt712) ++{ ++ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0000); ++ regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x03); ++ usleep_range(1000, 1100); ++ regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x02); ++ usleep_range(1000, 1100); ++ regmap_update_bits(rt712->regmap, RT712_PLL2_CONF2, 0x0080, 0x0000); ++ regmap_update_bits(rt712->regmap, RT712_PLL2_CONF2, 0x001f, 0x0017); ++ regmap_update_bits(rt712->regmap, RT712_PLL2_CONF3, 0x0010, 0x0000); ++ regmap_update_bits(rt712->regmap, RT712_PLL2_CONF1, 0x0081, 0x0001); ++ regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x03); ++ usleep_range(1000, 1100); ++ regmap_update_bits(rt712->regmap, RT712_PLL2_CONF1, 0x0081, 0x0081); ++ regmap_update_bits(rt712->regmap, RT712_PLL2_CONF2, 0x0080, 0x0080); ++ regmap_update_bits(rt712->regmap, RT712_PLL2_CONF2, 0x001f, 0x0000); ++ regmap_update_bits(rt712->regmap, RT712_PLL2_CONF3, 0x0010, 0x0010); ++ usleep_range(1000, 1100); ++ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0081); ++} ++ ++static void rt712_sdca_clk_patch2(struct rt712_sdca_priv *rt712) ++{ ++ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_REG, 0x49, 0x0800, ++ 0x0000); ++ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_REG, 0x49, 0xf000, ++ 0x0000); ++ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0000); ++ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_ANALOG_CTL, 0x0c, 0xc000, ++ 0xc000); ++ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_ANALOG_CTL, 0x00, 0xc000, ++ 0xc000); ++ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0081); ++ regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x02); ++ usleep_range(1000, 1100); ++ regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x03); ++ usleep_range(1000, 1100); ++ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0000); ++} ++ + static int rt712_sdca_calibration(struct rt712_sdca_priv *rt712) + { + unsigned int val, loop_rc = 0, loop_dc = 0; + struct device *dev; + struct regmap *regmap = rt712->regmap; ++ unsigned int clk_base; + int chk_cnt = 100; + int ret = 0; + ++ regmap_read(rt712->regmap, RT712_SDW_ROOT_CLK, &clk_base); ++ + mutex_lock(&rt712->calibrate_mutex); + dev = regmap_get_device(regmap); + +@@ -109,8 +152,35 @@ static int rt712_sdca_calibration(struct rt712_sdca_priv *rt712) + if (ret < 0) + goto _cali_fail_; + } +- if (loop_dc == chk_cnt) +- dev_err(dev, "%s, calibration time-out!\n", __func__); ++ ++ if (loop_dc == chk_cnt) { ++ if (clk_base == RT712_CLK_FREQ_24_576MHZ) { ++ rt712_sdca_clk_patch(rt712); ++ rt712_sdca_clk_patch2(rt712); ++ } ++ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_FSM_CTL, 0x4100); ++ rt712_sdca_index_write(rt712, RT712_VENDOR_CALI, ++ RT712_DAC_DC_CALI_CTL1, 0x7883); ++ rt712_sdca_index_write(rt712, RT712_VENDOR_CALI, ++ RT712_DAC_DC_CALI_CTL1, 0xf893); ++ rt712_sdca_index_read(rt712, RT712_VENDOR_CALI, ++ RT712_DAC_DC_CALI_CTL1, &val); ++ ++ for (loop_dc = 0; loop_dc < chk_cnt && ++ (val & RT712_DAC_DC_CALI_TRIGGER); loop_dc++) { ++ usleep_range(10000, 11000); ++ ret = rt712_sdca_index_read(rt712, RT712_VENDOR_CALI, ++ RT712_DAC_DC_CALI_CTL1, &val); ++ ++ if (ret < 0) ++ goto _cali_fail_; ++ } ++ ++ if (loop_dc == chk_cnt) ++ dev_err(dev, "%s, calibration time-out!\n", __func__); ++ else ++ dev_dbg(dev, "%s, calibration success!\n", __func__); ++ } + + if (loop_dc == chk_cnt || loop_rc == chk_cnt) + ret = -ETIMEDOUT; +@@ -1759,9 +1829,13 @@ static void rt712_sdca_va_io_init(struct rt712_sdca_priv *rt712) + + static void rt712_sdca_vb_io_init(struct rt712_sdca_priv *rt712) + { +- int ret = 0; + unsigned int jack_func_status, mic_func_status, amp_func_status; + struct device *dev = &rt712->slave->dev; ++ unsigned int clk_base; ++ int ret = 0; ++ ++ regmap_read(rt712->regmap, RT712_SDW_ROOT_CLK, &clk_base); ++ dev_dbg(dev, "%s clk_base=%x", __func__, clk_base); + + regmap_read(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), &jack_func_status); +@@ -1773,6 +1847,12 @@ static void rt712_sdca_vb_io_init(struct rt712_sdca_priv *rt712) + __func__, jack_func_status, mic_func_status, amp_func_status); + + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_JD_CTL3, 0x7778); ++ ++ if (clk_base == RT712_CLK_FREQ_24_576MHZ) { ++ rt712_sdca_clk_patch(rt712); ++ rt712_sdca_clk_patch2(rt712); ++ } ++ + /* DMIC */ + if ((mic_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt712->first_hw_init)) { + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_DMIC2_FU_IT_FLOAT_CTL, 0x1526); +diff --git a/sound/soc/codecs/rt712-sdca.h b/sound/soc/codecs/rt712-sdca.h +--- a/sound/soc/codecs/rt712-sdca.h ++++ b/sound/soc/codecs/rt712-sdca.h +@@ -162,6 +162,16 @@ struct rt712_dmic_kctrl_priv { + #define RT712_EAPD_HIGH 0x2 + #define RT712_EAPD_LOW 0x0 + ++/* SDW clock root frequency */ ++#define RT712_SDW_ROOT_CLK 0x004d ++#define RT712_SDW_SCALE_CLK0 0x0062 ++#define RT712_SDW_SCALE_CLK1 0x0072 ++ ++/* PLL2 config */ ++#define RT712_PLL2_CONF1 0x2f3a ++#define RT712_PLL2_CONF2 0x2f3d ++#define RT712_PLL2_CONF3 0x2f41 ++ + /* RC Calibration register */ + #define RT712_RC_CAL 0x3201 + +@@ -254,6 +264,14 @@ enum rt712_sdca_version { + RT712_VB, + }; + ++enum { ++ RT712_CLK_FREQ_19_2_MHZ = 1, ++ RT712_CLK_FREQ_24MHZ = 2, ++ RT712_CLK_FREQ_24_576MHZ = 3, ++ RT712_CLK_FREQ_22_5792MHZ = 4, ++}; ++ ++ + int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave); + int rt712_sdca_init(struct device *dev, struct regmap *regmap, + struct regmap *mbq_regmap, struct sdw_slave *slave); +diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c +--- a/sound/soc/codecs/rt721-sdca-sdw.c ++++ b/sound/soc/codecs/rt721-sdca-sdw.c +@@ -70,6 +70,7 @@ static bool rt721_sdca_mbq_readable_register(struct device *dev, unsigned int re + case 0x0310100: + case 0x2000000 ... 0x2000003: + case 0x2000013: ++ case 0x2000026: + case 0x200002c: + case 0x200003c: + case 0x2000046: +@@ -142,6 +143,7 @@ static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int re + case 0x200000d: + case 0x2000019: + case 0x2000020: ++ case 0x2000026: + case 0x200002c: + case 0x2000030: + case 0x2000046: +@@ -155,6 +157,7 @@ static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int re + case 0x5810039: + case 0x5b10018: + case 0x5b10019: ++ case 0x6100006: + return true; + default: + return false; +diff --git a/sound/soc/codecs/rt721-sdca.c b/sound/soc/codecs/rt721-sdca.c +--- a/sound/soc/codecs/rt721-sdca.c ++++ b/sound/soc/codecs/rt721-sdca.c +@@ -1497,6 +1497,15 @@ int rt721_sdca_init(struct device *dev, struct regmap *regmap, + &soc_sdca_dev_rt721, rt721_sdca_dai, ARRAY_SIZE(rt721_sdca_dai)); + } + ++static void rt721_sdca_reset(struct rt721_sdca_priv *rt721) ++{ ++ rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_VENDOR_REG, ++ RT721_VD_HIDDEN_CTRL, RT721_HIDDEN_REG_SW_RESET, ++ RT721_HIDDEN_REG_SW_RESET); ++ rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT, ++ RT721_HDA_LEGACY_RESET_CTL, 0x1, 0x1); ++} ++ + int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave) + { + struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev); +@@ -1530,9 +1539,17 @@ int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave) + } + + pm_runtime_get_noresume(&slave->dev); ++ ++ if (!rt721->first_hw_init) ++ rt721_sdca_reset(rt721); ++ + rt721_sdca_dmic_preset(rt721); + rt721_sdca_amp_preset(rt721); + rt721_sdca_jack_preset(rt721); ++ ++ if (rt721->hs_jack && (!rt721->first_hw_init)) ++ rt721_sdca_jack_init(rt721); ++ + if (rt721->first_hw_init) { + regcache_cache_bypass(rt721->regmap, false); + regcache_mark_dirty(rt721->regmap); +diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c +--- a/sound/soc/codecs/wm_adsp.c ++++ b/sound/soc/codecs/wm_adsp.c +@@ -775,9 +775,12 @@ static int wm_adsp_request_firmware_file(struct wm_adsp *dsp, + s++; + } + ++ adsp_dbg(dsp, "Try '%s'\n", fw->filename); + ret = wm_adsp_firmware_request(&fw->firmware, fw->filename, cs_dsp->dev); + if (ret < 0) { +- adsp_dbg(dsp, "Failed to request '%s': %d\n", fw->filename, ret); ++ if (ret != -ENOENT) ++ adsp_dbg(dsp, "Failed to request '%s': %d\n", fw->filename, ret); ++ + kfree(fw->filename); + fw->filename = NULL; + if (ret != -ENOENT) +diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c +--- a/sound/soc/intel/boards/sof_es8336.c ++++ b/sound/soc/intel/boards/sof_es8336.c +@@ -360,6 +360,15 @@ static const struct dmi_system_id sof_es8336_quirk_table[] = { + .driver_data = (void *)(SOF_ES8336_HEADPHONE_GPIO | + SOC_ES8336_HEADSET_MIC1) + }, ++ { ++ .callback = sof_es8336_quirk_cb, ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "NDZ-WXX9"), ++ }, ++ .driver_data = (void *)(SOF_ES8336_HEADPHONE_GPIO | ++ SOC_ES8336_HEADSET_MIC1) ++ }, + {} + }; + +diff --git a/sound/soc/sdw_utils/soc_sdw_cs_amp.c b/sound/soc/sdw_utils/soc_sdw_cs_amp.c +--- a/sound/soc/sdw_utils/soc_sdw_cs_amp.c ++++ b/sound/soc/sdw_utils/soc_sdw_cs_amp.c +@@ -14,7 +14,6 @@ + #include + #include + +-#define CS_AMP_CHANNELS_PER_AMP 4 + #define CS35L56_SPK_VOLUME_0DB 400 /* 0dB Max */ + + int asoc_sdw_cs35l56_volume_limit(struct snd_soc_card *card, const char *name_prefix) +@@ -64,51 +63,6 @@ int asoc_sdw_cs_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai + } + EXPORT_SYMBOL_NS(asoc_sdw_cs_spk_rtd_init, "SND_SOC_SDW_UTILS"); + +-int asoc_sdw_cs_spk_feedback_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) +-{ +- const struct snd_soc_dai_link *dai_link = rtd->dai_link; +- const struct snd_soc_dai_link_ch_map *ch_map; +- const struct snd_soc_dai_link_component *codec_dlc; +- struct snd_soc_dai *codec_dai; +- u8 ch_slot[8] = {}; +- unsigned int amps_per_bus, ch_per_amp, mask; +- int i, ret; +- +- WARN_ON(dai_link->num_cpus > ARRAY_SIZE(ch_slot)); +- +- /* +- * CS35L56 has 4 TX channels. When the capture is aggregated the +- * same bus slots will be allocated to all the amps on a bus. Only +- * one amp on that bus can be transmitting in each slot so divide +- * the available 4 slots between all the amps on a bus. +- */ +- amps_per_bus = dai_link->num_codecs / dai_link->num_cpus; +- if ((amps_per_bus == 0) || (amps_per_bus > CS_AMP_CHANNELS_PER_AMP)) { +- dev_err(rtd->card->dev, "Illegal num_codecs:%u / num_cpus:%u\n", +- dai_link->num_codecs, dai_link->num_cpus); +- return -EINVAL; +- } +- +- ch_per_amp = CS_AMP_CHANNELS_PER_AMP / amps_per_bus; +- +- for_each_rtd_ch_maps(rtd, i, ch_map) { +- codec_dlc = snd_soc_link_to_codec(rtd->dai_link, i); +- codec_dai = snd_soc_find_dai(codec_dlc); +- mask = GENMASK(ch_per_amp - 1, 0) << ch_slot[ch_map->cpu]; +- +- ret = snd_soc_dai_set_tdm_slot(codec_dai, 0, mask, 4, 32); +- if (ret < 0) { +- dev_err(rtd->card->dev, "Failed to set TDM slot:%d\n", ret); +- return ret; +- } +- +- ch_slot[ch_map->cpu] += ch_per_amp; +- } +- +- return 0; +-} +-EXPORT_SYMBOL_NS(asoc_sdw_cs_spk_feedback_rtd_init, "SND_SOC_SDW_UTILS"); +- + int asoc_sdw_cs_amp_init(struct snd_soc_card *card, + struct snd_soc_dai_link *dai_links, + struct asoc_sdw_codec_info *info, +diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c +--- a/sound/soc/sdw_utils/soc_sdw_utils.c ++++ b/sound/soc/sdw_utils/soc_sdw_utils.c +@@ -811,7 +811,6 @@ struct asoc_sdw_codec_info codec_info_list[] = { + .dai_name = "cs35l56-sdw1c", + .dai_type = SOC_SDW_DAI_TYPE_AMP, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, +- .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, + }, + }, + .dai_num = 2, +@@ -840,7 +839,6 @@ struct asoc_sdw_codec_info codec_info_list[] = { + .dai_name = "cs35l56-sdw1c", + .dai_type = SOC_SDW_DAI_TYPE_AMP, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, +- .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, + }, + }, + .dai_num = 2, +@@ -869,7 +867,6 @@ struct asoc_sdw_codec_info codec_info_list[] = { + .dai_name = "cs35l56-sdw1c", + .dai_type = SOC_SDW_DAI_TYPE_AMP, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, +- .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, + }, + }, + .dai_num = 2, +@@ -898,7 +895,6 @@ struct asoc_sdw_codec_info codec_info_list[] = { + .dai_name = "cs35l56-sdw1c", + .dai_type = SOC_SDW_DAI_TYPE_AMP, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, +- .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, + }, + }, + .dai_num = 2, +@@ -1565,7 +1561,7 @@ int asoc_sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai_link_ch_map *ch_maps; + int ch = params_channels(params); +- unsigned int ch_mask; ++ unsigned int cpu_ch_mask, codec_ch_mask; + int num_codecs; + int step; + int i; +@@ -1575,8 +1571,9 @@ int asoc_sdw_hw_params(struct snd_pcm_substream *substream, + + /* Identical data will be sent to all codecs in playback */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +- ch_mask = GENMASK(ch - 1, 0); ++ cpu_ch_mask = GENMASK(ch - 1, 0); + step = 0; ++ codec_ch_mask = 0; + } else { + num_codecs = rtd->dai_link->num_codecs; + +@@ -1586,17 +1583,24 @@ int asoc_sdw_hw_params(struct snd_pcm_substream *substream, + return -EINVAL; + } + +- ch_mask = GENMASK(ch / num_codecs - 1, 0); +- step = hweight_long(ch_mask); ++ cpu_ch_mask = GENMASK(ch / num_codecs - 1, 0); ++ step = hweight_long(cpu_ch_mask); ++ codec_ch_mask = cpu_ch_mask; + } + + /* + * The captured data will be combined from each cpu DAI if the dai + * link has more than one codec DAIs. Set codec channel mask and + * ASoC will set the corresponding channel numbers for each cpu dai. ++ * ++ * sdw_stream_add_slave() assigns different payload offsets to each ++ * codec in a capture stream, so that the same channels on each ++ * codec map to different channels on the CPU. + */ +- for_each_link_ch_maps(rtd->dai_link, i, ch_maps) +- ch_maps->cpu_ch_mask = ch_mask << (i * step); ++ for_each_link_ch_maps(rtd->dai_link, i, ch_maps) { ++ ch_maps->cpu_ch_mask = cpu_ch_mask << (i * step); ++ ch_maps->codec_ch_mask = codec_ch_mask; ++ } + + return 0; + } diff --git a/pkgbuilds/linux-omarchy-bore/0525-asoc-fixes-3.patch.sig b/pkgbuilds/linux-omarchy-bore/0525-asoc-fixes-3.patch.sig new file mode 100644 index 0000000..e53ea2a Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0525-asoc-fixes-3.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0526-sof-amd-acp7x-suspend-unknown-revision.patch b/pkgbuilds/linux-omarchy-bore/0526-sof-amd-acp7x-suspend-unknown-revision.patch new file mode 100644 index 0000000..3b71148 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0526-sof-amd-acp7x-suspend-unknown-revision.patch @@ -0,0 +1,13 @@ +diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c +--- a/sound/soc/sof/amd/acp.c ++++ b/sound/soc/sof/amd/acp.c +@@ -1197,7 +1197,8 @@ int amd_sof_acp7x_suspend(struct snd_sof_dev *sdev, u32 target_state) + enable = true; + break; + default: +- break; ++ dev_err(sdev->dev, "Unexpected PCI revision: 0x%x\n", acp_data->pci_rev); ++ return -EINVAL; + } + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_CONTROL, enable); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_ZSC_DSP_CTRL, 1); diff --git a/pkgbuilds/linux-omarchy-bore/0526-sof-amd-acp7x-suspend-unknown-revision.patch.sig b/pkgbuilds/linux-omarchy-bore/0526-sof-amd-acp7x-suspend-unknown-revision.patch.sig new file mode 100644 index 0000000..bb0d2f1 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0526-sof-amd-acp7x-suspend-unknown-revision.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0527-sound-fixes-5.patch b/pkgbuilds/linux-omarchy-bore/0527-sound-fixes-5.patch new file mode 100644 index 0000000..f9378d4 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0527-sound-fixes-5.patch @@ -0,0 +1,513 @@ +diff --git a/include/sound/core.h b/include/sound/core.h +--- a/include/sound/core.h ++++ b/include/sound/core.h +@@ -340,6 +340,15 @@ static inline void snd_card_unref(struct snd_card *card) + + DEFINE_FREE(snd_card_unref, struct snd_card *, if (_T) snd_card_unref(_T)) + ++/* ++ * For automatic error handling at probe time, assign the card like: ++ * struct snd_card *card __free(snd_card_free) = NULL; ++ * then create and process as usual. ++ * But, don't forget to clear to NULL at successful return for avoiding ++ * the unexpected cleanup! ++ */ ++DEFINE_FREE(snd_card_free, struct snd_card *, if (_T) snd_card_free(_T)) ++ + #define snd_card_set_dev(card, devptr) ((card)->dev = (devptr)) + + /* device.c */ +diff --git a/sound/core/seq/seq_compat.c b/sound/core/seq/seq_compat.c +--- a/sound/core/seq/seq_compat.c ++++ b/sound/core/seq/seq_compat.c +@@ -44,7 +44,9 @@ static int snd_seq_call_port_info_ioctl(struct snd_seq_client *client, unsigned + return -EFAULT; + data->kernel = NULL; + +- err = snd_seq_kernel_client_ctl(client->number, cmd, data); ++ scoped_guard(mutex, &client->ioctl_mutex) { ++ err = snd_seq_kernel_client_ctl(client->number, cmd, data); ++ } + if (err < 0) + return err; + +diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c +--- a/sound/hda/codecs/realtek/alc269.c ++++ b/sound/hda/codecs/realtek/alc269.c +@@ -4025,6 +4025,7 @@ enum { + ALC269_FIXUP_SONY_VAIO, + ALC275_FIXUP_SONY_VAIO_GPIO2, + ALC269_FIXUP_DELL_M101Z, ++ ALC256_FIXUP_ASUS_PM3406CHA, + ALC269_FIXUP_SKU_IGNORE, + ALC269_FIXUP_ASUS_G73JW, + ALC269_FIXUP_ASUS_N7601ZM_PINS, +@@ -4124,6 +4125,7 @@ enum { + ALC285_FIXUP_HP_SPECTRE_X360_EB1, + ALC285_FIXUP_HP_SPECTRE_X360_DF1, + ALC285_FIXUP_HP_ENVY_X360, ++ ALC285_FIXUP_HP_ENVY_13AQ, + ALC288_FIXUP_DELL_HEADSET_MODE, + ALC288_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC288_FIXUP_DELL_XPS_13, +@@ -4199,6 +4201,7 @@ enum { + ALC225_FIXUP_DELL_WYSE_AIO_MIC_NO_PRESENCE, + ALC225_FIXUP_WYSE_AUTO_MUTE, + ALC225_FIXUP_WYSE_DISABLE_MIC_VREF, ++ ALC225_FIXUP_HP_AIO_SPK, + ALC286_FIXUP_ACER_AIO_HEADSET_MIC, + ALC256_FIXUP_ASUS_HEADSET_MIC, + ALC256_FIXUP_ASUS_MIC_NO_PRESENCE, +@@ -4385,6 +4388,7 @@ enum { + ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN_HEADSET, + ALC285_LENOVO_DAC_RENAME, + ALC287_FIXUP_YOGA9_SPEAKER2_TO_DAC1, ++ ALC256_FIXUP_IPASON_SMARTBOOK_S1, + }; + + /* A special fixup for Lenovo C940 and Yoga Duet 7; +@@ -4482,6 +4486,14 @@ static const struct hda_fixup alc269_fixups[] = { + {} + } + }, ++ [ALC256_FIXUP_ASUS_PM3406CHA] = { ++ .type = HDA_FIXUP_VERBS, ++ .v.verbs = (const struct hda_verb[]) { ++ { 0x20, AC_VERB_SET_COEF_INDEX, 0x10 }, ++ { 0x20, AC_VERB_SET_PROC_COEF, 0x7f20 }, ++ {} ++ } ++ }, + [ALC269_FIXUP_SKU_IGNORE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_sku_ignore, +@@ -5720,6 +5732,14 @@ static const struct hda_fixup alc269_fixups[] = { + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC + }, ++ [ALC225_FIXUP_HP_AIO_SPK] = { ++ .type = HDA_FIXUP_VERBS, ++ .v.verbs = (const struct hda_verb[]) { ++ { 0x20, AC_VERB_SET_COEF_INDEX, 0x10 }, ++ { 0x20, AC_VERB_SET_PROC_COEF, 0x0220 }, ++ { } ++ }, ++ }, + [ALC286_FIXUP_ACER_AIO_HEADSET_MIC] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { +@@ -6401,6 +6421,15 @@ static const struct hda_fixup alc269_fixups[] = { + .chained = true, + .chain_id = ALC285_FIXUP_HP_GPIO_AMP_INIT, + }, ++ [ALC285_FIXUP_HP_ENVY_13AQ] = { ++ .type = HDA_FIXUP_PINS, ++ .v.pins = (const struct hda_pintbl[]) { ++ { 0x14, 0x90170150 }, /* front speakers */ ++ { } ++ }, ++ .chained = true, ++ .chain_id = ALC285_FIXUP_HP_GPIO_AMP_INIT, ++ }, + [ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_ideapad_s740_coef, +@@ -7088,6 +7117,8 @@ static const struct hda_fixup alc269_fixups[] = { + [ALC236_FIXUP_DELL_HP_POP_NOISE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_invalidate_dacs, ++ .chained = true, ++ .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE + }, + [ALC274_FIXUP_HP_89E9_GPIO] = { + .type = HDA_FIXUP_FUNC, +@@ -7150,6 +7181,13 @@ static const struct hda_fixup alc269_fixups[] = { + { } + } + }, ++ [ALC256_FIXUP_IPASON_SMARTBOOK_S1] = { ++ .type = HDA_FIXUP_PINS, ++ .v.pins = (const struct hda_pintbl[]) { ++ { 0x1b, 0x90170110 }, /* the real internal speaker */ ++ { } ++ }, ++ }, + }; + + static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7197,6 +7235,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x142b, "Acer Swift SF314-42", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1025, 0x1430, "Acer TravelMate B311R-31", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1025, 0x1466, "Acer Aspire A515-56", ALC255_FIXUP_ACER_HEADPHONE_AND_MIC), ++ SND_PCI_QUIRK(0x1025, 0x151e, "Acer Nitro AN515-45", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x1534, "Acer Predator PH315-54", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1025, 0x1539, "Acer Nitro 5 AN515-57", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x159c, "Acer Nitro 5 AN515-58", ALC287_FIXUP_ACER_MICMUTE_LED), +@@ -7210,8 +7249,10 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x1679, "Acer Nitro 16 AN16-41", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x169a, "Acer Swift SFG16", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), + SND_PCI_QUIRK(0x1025, 0x171e, "Acer Nitro ANV15-51", ALC245_FIXUP_ACER_MICMUTE_LED), ++ SND_PCI_QUIRK(0x1025, 0x172c, "Acer Nitro ANV16-41", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x1731, "Acer Predator PHN16-72", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x173a, "Acer Swift SFG14-73", ALC245_FIXUP_ACER_MICMUTE_LED), ++ SND_PCI_QUIRK(0x1025, 0x173b, "Acer Swift SFG16-72", ALC245_FIXUP_ACER_MICMUTE_LED), + SND_PCI_QUIRK(0x1025, 0x1758, "Acer Nitro ANV15-41", ALC245_FIXUP_ACER_MICMUTE_LED), + SND_PCI_QUIRK(0x1025, 0x1826, "Acer Helios ZPC", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1025, 0x182c, "Acer Helios ZPD", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), +@@ -7317,6 +7358,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), + SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED), ++ SND_PCI_QUIRK(0x103c, 0x2164, "HP Pavilion 15 Notebook PC", ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x218b, "HP", ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x21f9, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x2210, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), +@@ -7399,9 +7441,11 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x854a, "HP EliteBook 830 G6", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x854d, "HP EliteBook 840 G6", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x856a, "HP Pavilion 15-cs1xxx", ALC295_FIXUP_HP_PAVILION_MUTE_LED_1B), ++ HDA_CODEC_QUIRK(0x103c, 0x85ba, "HP Pavilion AiO 24", ALC225_FIXUP_HP_AIO_SPK), + SND_PCI_QUIRK(0x103c, 0x85c6, "HP Pavilion x360 Convertible 14-dy1xxx", ALC295_FIXUP_HP_MUTE_LED_COEFBIT11), + SND_PCI_QUIRK(0x103c, 0x85de, "HP Envy x360 13-ar0xxx", ALC285_FIXUP_HP_ENVY_X360), + SND_PCI_QUIRK(0x103c, 0x85f0, "HP Laptop 15-dw0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), ++ SND_PCI_QUIRK(0x103c, 0x85f1, "HP Laptop 15-dw1xxx", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + SND_PCI_QUIRK(0x103c, 0x85f3, "HP 250 G8 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x8603, "HP Omen 17-cb0xxx", ALC285_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x860c, "HP ZBook 17 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT), +@@ -7409,6 +7453,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x861f, "HP Elite Dragonfly G1", ALC285_FIXUP_HP_GPIO_AMP_INIT), + SND_PCI_QUIRK(0x103c, 0x864f, "HP Laptop 15-dy0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED), ++ SND_PCI_QUIRK(0x103c, 0x86ad, "HP ENVY 13-aq1xxx", ALC285_FIXUP_HP_ENVY_13AQ), + SND_PCI_QUIRK(0x103c, 0x86c1, "HP Laptop 15-da3001TU", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x86c7, "HP Envy AiO 32", ALC274_FIXUP_HP_ENVY_GPIO), + SND_PCI_QUIRK(0x103c, 0x86c8, "HP Laptop 14s-dr1xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), +@@ -7437,6 +7482,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x8786, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8787, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8788, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED), ++ SND_PCI_QUIRK(0x103c, 0x87b2, "HP Pavilion Gaming Laptop 15-ec1xxx", ALC285_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x87b7, "HP Laptop 14-fq0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x87c8, "HP", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x87cb, "HP Pavilion 15-eg0xxx", ALC287_FIXUP_HP_GPIO_LED), +@@ -7497,6 +7543,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x8975, "HP EliteBook x360 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x897d, "HP mt440 Mobile Thin Client U74", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8981, "HP Elite Dragonfly G3", ALC245_FIXUP_CS35L41_SPI_4), ++ SND_PCI_QUIRK(0x103c, 0x8987, "HP Pavilion 14-dv1001TU", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x898a, "HP Pavilion 15-eg100", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x898e, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x898f, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2), +@@ -7534,7 +7581,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x8a2d, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a2e, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a30, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), +- SND_PCI_QUIRK(0x103c, 0x8a31, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), ++ SND_PCI_QUIRK(0x103c, 0x8a31, "HP ENVY x360 2-in-1 Laptop 15-ey0xxx", ALC287_FIXUP_CS35L41_I2C_2_HP_MUTE_LEDS), + SND_PCI_QUIRK(0x103c, 0x8a34, "HP Pavilion x360 2-in-1 Laptop 14-ek0xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8a36, "HP Pavilion Plus 14-eh0xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8a3d, "HP Victus 15-fb0xxx (MB 8A3D)", ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT), +@@ -7595,6 +7642,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x8bb4, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8bb6, "HP Laptop 15-fd0039nt", ALC236_FIXUP_HP_15_FD0XXX), + SND_PCI_QUIRK(0x103c, 0x8bbe, "HP Victus 16-r0xxx (MB 8BBE)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), ++ SND_PCI_QUIRK(0x103c, 0x8bc7, "HP Pavilion 15-eh3174nw", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8bc8, "HP Victus 15-fa1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8bcd, "HP Omen 16-xd0xxx", ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8bd4, "HP Victus 16-s0xxx (MB 8BD4)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), +@@ -7617,6 +7665,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x8c17, "HP Spectre x360 2-in-1 Laptop 16-aa0xxx", ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX), + SND_PCI_QUIRK(0x103c, 0x8c21, "HP Pavilion Plus Laptop 14-ey0XXX", ALC245_FIXUP_HP_X360_MUTE_LEDS), + SND_PCI_QUIRK(0x103c, 0x8c2d, "HP Victus 15-fa1xxx (MB 8C2D)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), ++ SND_PCI_QUIRK(0x103c, 0x8c2f, "HP Victus 15-fb2xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8c30, "HP Victus 15-fb1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8c3f, "HP Victus 15-fa1xxx (MB 8C3F)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8c46, "HP EliteBook 830 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), +@@ -7726,6 +7775,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x8e37, "HP 16 Piston OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8e3a, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8e3b, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2), ++ SND_PCI_QUIRK(0x103c, 0x8e43, "HP Laptop 15-fd2xxx", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + SND_PCI_QUIRK(0x103c, 0x8e60, "HP OmniBook 7 Laptop 16-bh0xxx", ALC245_FIXUP_CS35L41_I2C_2_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8e61, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8e62, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), +@@ -7829,6 +7879,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x1652, "ASUS ROG Zephyrus Do 15 SE", ALC289_FIXUP_ASUS_ZEPHYRUS_DUAL_SPK), + SND_PCI_QUIRK(0x1043, 0x1662, "ASUS GV301QH", ALC294_FIXUP_ASUS_DUAL_SPK), + SND_PCI_QUIRK(0x1043, 0x1663, "ASUS GU603ZI/ZJ/ZQ/ZU/ZV", ALC285_FIXUP_ASUS_HEADSET_MIC), ++ SND_PCI_QUIRK(0x1043, 0x1664, "ASUS ExpertBook PM3406CHA", ALC256_FIXUP_ASUS_PM3406CHA), + SND_PCI_QUIRK(0x1043, 0x1683, "ASUS UM3402YAR", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS UX3402VA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x16b2, "ASUS GU603", ALC289_FIXUP_ASUS_GA401), +@@ -7931,7 +7982,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x3ef0, "ASUS Strix G635LR_LW_LX", ALC287_FIXUP_TXNW2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3f00, "ASUS Strix G815LH_LM_LP", ALC287_FIXUP_TXNW2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3f10, "ASUS Strix G835LR_LW_LX", ALC287_FIXUP_TXNW2781_I2C), +- SND_PCI_QUIRK(0x1043, 0x3f20, "ASUS Strix G615LR_LW", ALC287_FIXUP_TXNW2781_I2C), ++ SND_PCI_QUIRK(0x1043, 0x3f20, "ASUS Strix G615LR_LW", ALC287_FIXUP_TXNW2781_I2C_ASUS), + SND_PCI_QUIRK(0x1043, 0x3f30, "ASUS Strix G815LR_LW", ALC287_FIXUP_TXNW2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3fd0, "ASUS B3605CVA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x3ff0, "ASUS B5405CVA", ALC245_FIXUP_CS35L41_SPI_2), +@@ -7954,6 +8005,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x10cf, 0x1757, "Lifebook E752", ALC269_FIXUP_LIFEBOOK_HP_PIN), + SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC), + SND_PCI_QUIRK(0x10ec, 0x10f2, "Intel Reference board", ALC700_FIXUP_INTEL_REFERENCE), ++ SND_PCI_QUIRK(0x10ec, 0x111e, "Higole F9B", ALC290_FIXUP_MONO_SPEAKERS), + SND_PCI_QUIRK(0x10ec, 0x118c, "Medion EE4254 MD62100", ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE), + SND_PCI_QUIRK(0x10ec, 0x119e, "Positivo SU C1400", ALC269_FIXUP_ASPIRE_HEADSET_MIC), + SND_PCI_QUIRK(0x10ec, 0x11bc, "VAIO VJFE-IL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), +@@ -8179,7 +8231,6 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x17aa, 0x3834, "Lenovo IdeaPad Slim 9i 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x383d, "Legion Y9000X 2019", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x3843, "Lenovo Yoga 9i / Yoga Book 9i", ALC287_FIXUP_LENOVO_YOGA_BOOK_9I), +- SND_PCI_QUIRK(0x17aa, 0x3846, "Lenovo Yoga Pro 9 16IAH10", ALC287_FIXUP_YOGA9_SPEAKER2_TO_DAC1), + /* Yoga Pro 7 14IMH9 shares PCI SSID 17aa:3847 with Legion 7 16ACHG6; + * use codec SSID to distinguish them + */ +@@ -8204,6 +8255,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ + SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x3870, "Lenovo Yoga 7 14ARB7", ALC287_FIXUP_YOGA7_14ARB7_I2C), ++ SND_PCI_QUIRK(0x17aa, 0x3871, "Lenovo ThinkBook 16p Gen 3 ARH", ALC287_FIXUP_YOGA7_14ARB7_I2C), + SND_PCI_QUIRK(0x17aa, 0x3874, "Legion 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x3877, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x3878, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2), +@@ -8254,6 +8306,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x17aa, 0x38d5, "Yoga S990-16 Pro IMH YC Quad", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38d6, "Yoga S990-16 Pro IMH VECO Quad", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38d7, "Lenovo Yoga 9 14IMH9", ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN), ++ SND_PCI_QUIRK(0x17aa, 0x38dc, "Lenovo ThinkBook G8", ALC285_FIXUP_SPEAKER2_TO_DAC1), + SND_PCI_QUIRK(0x17aa, 0x38df, "Yoga Y990 Intel YC Dual", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38e0, "Yoga Y990 Intel VECO Dual", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38f8, "Yoga Book 9i", ALC287_FIXUP_TAS2781_I2C), +@@ -8273,7 +8326,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x17aa, 0x3912, "Lenovo Xiaoxin 14 GT", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), + SND_PCI_QUIRK(0x17aa, 0x3913, "Lenovo 145", ALC236_FIXUP_LENOVO_INV_DMIC), + SND_PCI_QUIRK(0x17aa, 0x391f, "Yoga S990-16 pro Quad YC Quad", ALC287_FIXUP_TXNW2781_I2C), +- SND_PCI_QUIRK(0x17aa, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_TXNW2781_I2C), ++ SND_PCI_QUIRK(0x17aa, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_YOGA9_SPEAKER2_TO_DAC1), + SND_PCI_QUIRK(0x17aa, 0x3929, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x392b, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + HDA_CODEC_QUIRK(0x17aa, 0x3936, "Legion R9000P ADR10H", ALC287_FIXUP_LENOVO_LEGION_AW88399), +@@ -8369,6 +8422,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x2014, 0x800a, "Positivo ARN50", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x2039, 0x0001, "Inspur S14-G1", ALC295_FIXUP_CHROME_BOOK), + SND_PCI_QUIRK(0x2145, 0x0001, "Star Labs StarFighter", ALC233_FIXUP_STARLABS_STARFIGHTER), ++ SND_PCI_QUIRK(0x2782, 0x0206, "IPASON SmartBook S1", ALC256_FIXUP_IPASON_SMARTBOOK_S1), + SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x2782, 0x0228, "Infinix ZERO BOOK 13", ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13), + SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO), +diff --git a/sound/hda/codecs/realtek/realtek.c b/sound/hda/codecs/realtek/realtek.c +--- a/sound/hda/codecs/realtek/realtek.c ++++ b/sound/hda/codecs/realtek/realtek.c +@@ -1292,6 +1292,7 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec) + } + + switch (codec->core.vendor_id) { ++ case 0x10ec0235: + case 0x10ec0255: + alc_process_coef_fw(codec, coef0255); + break; +@@ -1407,6 +1408,7 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, + }; + + switch (codec->core.vendor_id) { ++ case 0x10ec0235: + case 0x10ec0255: + alc_write_coef_idx(codec, 0x45, 0xc489); + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); +@@ -1562,6 +1564,7 @@ static void alc_headset_mode_default(struct hda_codec *codec) + alc_process_coef_fw(codec, coef0225); + alc_hp_enable_unmute(codec, 75); + break; ++ case 0x10ec0235: + case 0x10ec0255: + alc_process_coef_fw(codec, coef0255); + break; +@@ -1663,6 +1666,7 @@ static void alc_headset_mode_ctia(struct hda_codec *codec) + }; + + switch (codec->core.vendor_id) { ++ case 0x10ec0235: + case 0x10ec0255: + alc_process_coef_fw(codec, coef0255); + break; +@@ -1780,6 +1784,7 @@ static void alc_headset_mode_omtp(struct hda_codec *codec) + }; + + switch (codec->core.vendor_id) { ++ case 0x10ec0235: + case 0x10ec0255: + alc_process_coef_fw(codec, coef0255); + break; +@@ -1881,6 +1886,7 @@ static void alc_determine_headset_type(struct hda_codec *codec) + } + + switch (codec->core.vendor_id) { ++ case 0x10ec0235: + case 0x10ec0255: + alc_process_coef_fw(codec, coef0255); + msleep(300); +diff --git a/sound/hda/controllers/tegra.c b/sound/hda/controllers/tegra.c +--- a/sound/hda/controllers/tegra.c ++++ b/sound/hda/controllers/tegra.c +@@ -374,7 +374,8 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) + * in powers of 2, next available ratio is 16 which can be + * used as a limiting factor here. + */ +- if (of_device_is_compatible(np, "nvidia,tegra30-hda")) ++ if (of_device_is_compatible(np, "nvidia,tegra30-hda") || ++ of_device_is_compatible(np, "nvidia,tegra194-hda")) + chip->bus.core.sdo_limit = 16; + + /* codec detection */ +diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c +--- a/sound/pci/ctxfi/cthw20k2.c ++++ b/sound/pci/ctxfi/cthw20k2.c +@@ -1895,6 +1895,7 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) + + if (hw->model == CTSB1270) { + /* Set up the PCM4220 ADC on Titanium HD */ ++ data = hw_read_20kx(hw, GPIO_DATA); + data &= ~0x0C; + if (1 == info->msr) + data |= 0x00; /* Single Speed Mode 32-50kHz */ +@@ -1903,6 +1904,7 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) + else + data |= 0x04; /* Quad Speed Mode 108kHz-216kHz */ + hw_write_20kx(hw, GPIO_DATA, data); ++ usleep_range(10000, 11000); + } + + hw_adc_start(hw); +diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c +--- a/sound/pci/ice1712/ice1712.c ++++ b/sound/pci/ice1712/ice1712.c +@@ -2523,7 +2523,7 @@ static int snd_ice1712_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) + { + static int dev; +- struct snd_card *card __free(snd_card_unref) = NULL; ++ struct snd_card *card __free(snd_card_free) = NULL; + struct snd_ice1712 *ice; + int pcm_dev = 0, err; + const struct snd_ice1712_card_info * const *tbl, *c; +diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c +--- a/sound/usb/caiaq/device.c ++++ b/sound/usb/caiaq/device.c +@@ -562,6 +562,9 @@ static int snd_probe(struct usb_interface *intf, + ret = init_card(caiaqdev(card)); + if (ret < 0) { + dev_err(&usb_dev->dev, "unable to init card! (ret=%d)\n", ret); ++#ifdef CONFIG_SND_USB_CAIAQ_INPUT ++ snd_usb_caiaq_input_disconnect(caiaqdev(card)); ++#endif + snd_card_free(card); + return ret; + } +diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c +--- a/sound/usb/line6/playback.c ++++ b/sound/usb/line6/playback.c +@@ -182,6 +182,13 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) + + fsize *= bytes_per_frame; + ++ if (fsize > line6pcm->max_packet_size_out) { ++ dev_err(line6pcm->line6->ifcdev, ++ "playback packet too large: %d > %d\n", ++ fsize, line6pcm->max_packet_size_out); ++ return -EMSGSIZE; ++ } ++ + fout->offset = urb_size; + fout->length = fsize; + urb_size += fsize; +diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c +--- a/sound/usb/misc/ua101.c ++++ b/sound/usb/misc/ua101.c +@@ -1007,6 +1007,15 @@ static int detect_usb_format(struct ua101 *ua) + } + ua->playback.usb_pipe = usb_sndisocpipe(ua->dev, usb_endpoint_num(epd)); + ua->playback.max_packet_bytes = usb_endpoint_maxp(epd); ++ ++ if (ua->capture.max_packet_bytes / ua->capture.frame_bytes * ++ ua->playback.frame_bytes > ua->playback.max_packet_bytes) { ++ dev_err(&ua->dev->dev, ++ "playback packet size %u too small for %u capture frames\n", ++ ua->playback.max_packet_bytes, ++ ua->capture.max_packet_bytes / ua->capture.frame_bytes); ++ return -ENXIO; ++ } + return 0; + } + +diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c +--- a/sound/usb/mixer_maps.c ++++ b/sound/usb/mixer_maps.c +@@ -749,6 +749,10 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { + .id = USB_ID(0x0db0, 0xa073), + .map = msi_mpg_x570s_carbon_max_wifi_alc4080_map, + }, ++ { /* MSI MAG B850M MORTAR WIFI */ ++ .id = USB_ID(0x0db0, 0xcc78), ++ .map = msi_mpg_x570s_carbon_max_wifi_alc4080_map, ++ }, + { /* MSI TRX40 */ + .id = USB_ID(0x0db0, 0x543d), + .map = trx40_mobo_map, +diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c +--- a/sound/usb/quirks.c ++++ b/sound/usb/quirks.c +@@ -1702,10 +1702,12 @@ int snd_usb_apply_boot_quirk_once(struct usb_device *dev, + switch (id) { + case USB_ID(0x07fd, 0x0008): /* MOTU M Series, 1st hardware version */ + return snd_usb_motu_m_series_boot_quirk(dev); +- case USB_ID(0x1397, 0x1234): /* Behringer CM1A */ +- return snd_usb_cm1a_boot_quirk(dev); + } + ++ /* Behringer devices may need explicit device descriptor read at boot */ ++ if (USB_ID_VENDOR(id) == 0x1397) ++ return snd_usb_cm1a_boot_quirk(dev); ++ + return 0; + } + +@@ -2366,6 +2368,10 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + QUIRK_FLAG_CTL_MSG_DELAY_1M), + DEVICE_FLG(0x0a73, 0x003a, /* Mackie DLZ Creator XS */ + QUIRK_FLAG_ALWAYS_SET_RATE), ++ DEVICE_FLG(0x0b05, 0x1826, /* ASUS SupremeFX Hi-Fi */ ++ QUIRK_FLAG_DISABLE_AUTOSUSPEND), ++ DEVICE_FLG(0x0b05, 0x1827, /* ASUS SupremeFX Hi-Fi */ ++ QUIRK_FLAG_DISABLE_AUTOSUSPEND), + DEVICE_FLG(0x0b05, 0x18a6, /* ASUSTek Computer, Inc. */ + QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE), + DEVICE_FLG(0x0b0e, 0x0349, /* Jabra 550a */ +@@ -2510,8 +2516,6 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), + DEVICE_FLG(0x262a, 0x9302, /* ddHiFi TC44C */ + QUIRK_FLAG_DSD_RAW), +- DEVICE_FLG(0x2708, 0x0002, /* Audient iD14 */ +- QUIRK_FLAG_IGNORE_CTL_ERROR), + DEVICE_FLG(0x2772, 0x0502, /* Musical Fidelity M6s DAC */ + 0), /* for avoiding QUIRK_FLAG_DSD_RAW with vendor match */ + DEVICE_FLG(0x2912, 0x30c8, /* Audioengine D1 */ +@@ -2542,6 +2546,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + QUIRK_FLAG_IGNORE_CTL_ERROR), + DEVICE_FLG(0x3255, 0x0000, /* Luxman D-10X */ + QUIRK_FLAG_ITF_USB_DSD_DAC | QUIRK_FLAG_CTL_MSG_DELAY), ++ DEVICE_FLG(0x32bb, 0x0004, /* HiBy FC4 */ ++ QUIRK_FLAG_DSD_RAW), + DEVICE_FLG(0x3302, 0x17c2, /* TTGK Technology USB-C Audio */ + QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), + DEVICE_FLG(0x339b, 0x3a07, /* Synaptics HONOR USB-C HEADSET */ +@@ -2608,6 +2614,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + QUIRK_FLAG_DSD_RAW), + VENDOR_FLG(0x2622, /* IAG Limited devices */ + QUIRK_FLAG_DSD_RAW), ++ VENDOR_FLG(0x2708, /* Audient devices */ ++ QUIRK_FLAG_IGNORE_CTL_ERROR), + VENDOR_FLG(0x2772, /* Musical Fidelity devices */ + QUIRK_FLAG_DSD_RAW), + VENDOR_FLG(0x278b, /* Rotel? */ diff --git a/pkgbuilds/linux-omarchy-bore/0527-sound-fixes-5.patch.sig b/pkgbuilds/linux-omarchy-bore/0527-sound-fixes-5.patch.sig new file mode 100644 index 0000000..04aa2c3 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0527-sound-fixes-5.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0528-asoc-fixes-4.patch b/pkgbuilds/linux-omarchy-bore/0528-asoc-fixes-4.patch new file mode 100644 index 0000000..2290ae4 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0528-asoc-fixes-4.patch @@ -0,0 +1,523 @@ +diff --git a/drivers/spi/spi-cs42l43.c b/drivers/spi/spi-cs42l43.c +--- a/drivers/spi/spi-cs42l43.c ++++ b/drivers/spi/spi-cs42l43.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -215,12 +216,108 @@ static size_t cs42l43_spi_max_length(struct spi_device *spi) + return CS42L43_SPI_MAX_LENGTH; + } + ++/* ++ * Workaround needed for two speaker ID pins in one ACPI GpioIo() but ++ * the Linux-specific _DSD property only contains one pin. ++ * Create a temporary acpi_gpio_mapping pointing at both pins. ++ */ ++static const struct acpi_gpio_params cs42l43_2bit_speaker_id_from_one_gpioio_params[] = { ++ [0] = { ++ .crs_entry_index = 0, ++ .line_index = 0, ++ }, ++ [1] = { ++ .crs_entry_index = 0, ++ .line_index = 1, ++ }, ++}; ++ ++static const struct acpi_gpio_mapping cs42l43_2bit_speaker_id_from_one_gpioio_mapping[] = { ++ { ++ .name = "spk-id-quirk-gpios", ++ .data = cs42l43_2bit_speaker_id_from_one_gpioio_params, ++ .size = ARRAY_SIZE(cs42l43_2bit_speaker_id_from_one_gpioio_params), ++ }, ++ { } ++}; ++ ++static int cs42l43_get_2bit_speaker_id_from_one_gpioio(struct cs42l43_spi *priv, int *result) ++{ ++ struct fwnode_reference_args args; ++ struct acpi_device *adev; ++ struct gpio_desc *desc; ++ u32 spkid = 0; ++ int i, ret; ++ ++ /* Use the _DSD property to get the node containing the GpioIo() */ ++ ret = fwnode_property_get_reference_args(dev_fwnode(priv->dev), "spk-id-gpios", ++ NULL, 3, 0, &args); ++ if (ret) ++ return ret; ++ ++ struct fwnode_handle *fwnode __free(fwnode_handle) = args.fwnode; ++ ++ /* An acpi_gpio_mapping must be added to the node that contains the GpioIo() */ ++ adev = to_acpi_device_node(fwnode); ++ if (!adev) ++ return -EINVAL; ++ ++ ret = acpi_dev_add_driver_gpios(adev, cs42l43_2bit_speaker_id_from_one_gpioio_mapping); ++ if (ret) ++ return ret; ++ ++ /* gpiod_get_array() can't read from a mapping in a child node */ ++ for (i = 0; i < ARRAY_SIZE(cs42l43_2bit_speaker_id_from_one_gpioio_params); i++) { ++ desc = fwnode_gpiod_get_index(fwnode, "spk-id-quirk", i, GPIOD_IN, ++ dev_name(priv->dev)); ++ if (IS_ERR(desc)) { ++ ret = PTR_ERR(desc); ++ goto out; ++ } ++ ++ ret = gpiod_get_value_cansleep(desc); ++ gpiod_put(desc); ++ if (ret < 0) ++ goto out; ++ ++ spkid |= (u32)ret << i; ++ } ++ ++ dev_dbg(priv->dev, "spk-id-gpios = %u\n", spkid); ++ *result = spkid; ++ ret = 0; ++out: ++ acpi_dev_remove_driver_gpios(adev); ++ ++ return ret; ++} ++ ++static const struct dmi_system_id cs42l43_spk_id_quirks[] = { ++ { ++ .ident = "Dell XPS 13 DX13260", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), ++ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0E53"), ++ }, ++ .driver_data = cs42l43_get_2bit_speaker_id_from_one_gpioio, ++ }, ++ { } ++}; ++ + static int cs42l43_get_speaker_id_gpios(struct cs42l43_spi *priv, int *result) + { ++ const struct dmi_system_id *dmi_id; + struct gpio_descs *descs; + u32 spkid; + int i, ret; + ++ dmi_id = dmi_first_match(cs42l43_spk_id_quirks); ++ if (dmi_id) { ++ int (*get_speaker_id)(struct cs42l43_spi *priv, int *result) = dmi_id->driver_data; ++ ++ return get_speaker_id(priv, result); ++ } ++ + descs = gpiod_get_array_optional(priv->dev, "spk-id", GPIOD_IN); + if (!descs) + return 0; +diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c +--- a/sound/soc/amd/acp-config.c ++++ b/sound/soc/amd/acp-config.c +@@ -51,6 +51,13 @@ static const struct dmi_system_id acp70_acpi_flag_override_table[] = { + DMI_MATCH(DMI_PRODUCT_NAME, "83V9"), + }, + }, ++ { ++ /* Lenovo Yoga Pro 7 14ASP9 (Strix Point, ACP 7.0) */ ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "83HN"), ++ }, ++ }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC"), +@@ -63,6 +70,13 @@ static const struct dmi_system_id acp70_acpi_flag_override_table[] = { + DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook 18 M1807GA"), + }, + }, ++ { ++ /* ASUS Zenbook S14 UM5406GA (Strix Point, ACP 7.0) */ ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Zenbook S14 UM5406GA"), ++ }, ++ }, + { + /* HP OmniBook X Flip 14-kc0xxx (Strix Point, ACP 7.2) */ + .matches = { +diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig +--- a/sound/soc/amd/acp/Kconfig ++++ b/sound/soc/amd/acp/Kconfig +@@ -181,6 +181,7 @@ config SND_SOC_AMD_LEGACY_SDW_MACH + select SND_SOC_RT715_SDW + select SND_SOC_RT715_SDCA_SDW + select SND_SOC_RT722_SDCA_SDW ++ select SND_SOC_TAS2783_SDW + help + This option enables Legacy(No DSP) sound card support for SoundWire + enabled AMD platforms along with ACP PDM controller. +diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c +--- a/sound/soc/amd/acp/amd-acp70-acpi-match.c ++++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c +@@ -748,7 +748,27 @@ static const struct snd_soc_acpi_link_adr acp70_rt721_only[] = { + {} + }; + ++static const struct snd_soc_acpi_link_adr acp70_tas2783a_l0_rt712_vb_l1[] = { ++ { ++ .mask = BIT(0), ++ .num_adr = ARRAY_SIZE(tas2783_2_adr), ++ .adr_d = tas2783_2_adr, ++ }, ++ { ++ .mask = BIT(1), ++ .num_adr = ARRAY_SIZE(rt712_vb_1_group1_adr), ++ .adr_d = rt712_vb_1_group1_adr, ++ }, ++ {} ++}; ++ + struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = { ++ { ++ .link_mask = BIT(0) | BIT(1), ++ .links = acp70_tas2783a_l0_rt712_vb_l1, ++ .machine_check = snd_soc_acpi_amd_sdca_is_device_rt712_vb, ++ .drv_name = "amd_sdw", ++ }, + { + .link_mask = BIT(0), + .links = acp70_tas2783_2, +diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c +--- a/sound/soc/amd/yc/acp6x-mach.c ++++ b/sound/soc/amd/yc/acp6x-mach.c +@@ -248,6 +248,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { + DMI_MATCH(DMI_PRODUCT_NAME, "21J6"), + } + }, ++ { ++ .driver_data = &acp6x_card, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "21JN"), ++ } ++ }, + { + .driver_data = &acp6x_card, + .matches = { +@@ -381,6 +388,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { + DMI_MATCH(DMI_PRODUCT_NAME, "83Q3"), + } + }, ++ { ++ .driver_data = &acp6x_card, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "83UU"), ++ } ++ }, + { + .driver_data = &acp6x_card, + .matches = { +diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -1989,9 +1989,7 @@ config SND_SOC_RT715_SDCA_SDW + config SND_SOC_RT766_SDCA_SDW + tristate "Realtek RT766 SDCA Codec - SDW" + depends on SOUNDWIRE +- depends on SND_SOC_SDCA +- select SND_SOC_SDCA_HID +- select SND_SOC_SDCA_IRQ ++ depends on SND_SOC_SDCA_CLASS + select REGMAP_SOUNDWIRE + select REGMAP_SOUNDWIRE_MBQ + +diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c +--- a/sound/soc/codecs/lpass-wsa-macro.c ++++ b/sound/soc/codecs/lpass-wsa-macro.c +@@ -1841,6 +1841,7 @@ static int wsa_macro_enable_interpolator(struct snd_soc_dapm_widget *w, + int event) + { + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); ++ unsigned int gain; + u16 gain_reg; + u16 reg; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); +@@ -1882,6 +1883,8 @@ static int wsa_macro_enable_interpolator(struct snd_soc_dapm_widget *w, + CDC_WSA_RX_PGA_HALF_DB_MASK, + CDC_WSA_RX_PGA_HALF_DB_ENABLE); + } ++ gain = snd_soc_component_read(component, gain_reg); ++ snd_soc_component_write(component, gain_reg, gain); + wsa_macro_config_ear_spkr_gain(component, wsa, + event, gain_reg); + break; +diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c +--- a/sound/soc/codecs/max98363.c ++++ b/sound/soc/codecs/max98363.c +@@ -200,7 +200,7 @@ static int max98363_sdw_dai_hw_params(struct snd_pcm_substream *substream, + struct max98363_priv *max98363 = + snd_soc_component_get_drvdata(component); + +- struct sdw_stream_config stream_config; ++ struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_runtime *stream; +diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c +--- a/sound/soc/codecs/rt1017-sdca-sdw.c ++++ b/sound/soc/codecs/rt1017-sdca-sdw.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -573,11 +574,10 @@ static int rt1017_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + { + struct snd_soc_component *component = dai->component; + struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component); +- struct sdw_stream_config stream_config; ++ struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config; +- enum sdw_data_direction direction; + struct sdw_stream_runtime *sdw_stream; +- int retval, port, num_channels, ch_mask; ++ int retval, port; + unsigned int sampling_rate; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); +@@ -591,28 +591,17 @@ static int rt1017_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + + /* SoundWire specific configuration */ + /* port 1 for playback */ +- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +- direction = SDW_DATA_DIR_RX; ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + port = 1; +- } else { +- direction = SDW_DATA_DIR_TX; ++ else + port = 2; +- } + +- num_channels = params_channels(params); +- ch_mask = (1 << num_channels) - 1; +- +- stream_config.frame_rate = params_rate(params); +- stream_config.ch_count = num_channels; +- stream_config.bps = snd_pcm_format_width(params_format(params)); +- stream_config.direction = direction; +- +- port_config.ch_mask = ch_mask; ++ snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + port_config.num = port; + + dev_dbg(dai->dev, "frame_rate %d, ch_count %d, bps %d, direction %d, ch_mask %d, port: %d\n", +- params_rate(params), num_channels, snd_pcm_format_width(params_format(params)), +- direction, ch_mask, port); ++ stream_config.frame_rate, stream_config.ch_count, stream_config.bps, ++ stream_config.direction, port_config.ch_mask, port); + + retval = sdw_stream_add_slave(rt1017->sdw_slave, &stream_config, + &port_config, 1, sdw_stream); +diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c +--- a/sound/soc/codecs/rt274.c ++++ b/sound/soc/codecs/rt274.c +@@ -619,9 +619,11 @@ static int rt274_hw_params(struct snd_pcm_substream *substream, + unsigned int val = 0; + int d_len_code = 0, c_len_code = 0; + ++ /* bit 14 Sample Rate Base, 0: 48kHz 1: 44.1kHz */ + switch (params_rate(params)) { +- /* bit 14 0:48K 1:44.1K */ + case 44100: ++ val |= BIT(14); ++ break; + case 48000: + break; + default: +diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c +--- a/sound/soc/codecs/rt286.c ++++ b/sound/soc/codecs/rt286.c +@@ -953,7 +953,7 @@ static int rt286_probe(struct snd_soc_component *component) + + if (rt286->i2c->irq) + schedule_delayed_work(&rt286->jack_detect_work, +- msecs_to_jiffies(50)); ++ msecs_to_jiffies(1250)); + return 0; + } + +diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c +--- a/sound/soc/codecs/wm8903.c ++++ b/sound/soc/codecs/wm8903.c +@@ -676,7 +676,7 @@ SOC_ENUM("DRC Decay Rate", drc_decay), + SOC_ENUM("DRC FF Delay", drc_ff_delay), + SOC_SINGLE("DRC Anticlip Switch", WM8903_DRC_0, 1, 1, 0), + SOC_SINGLE("DRC QR Switch", WM8903_DRC_0, 2, 1, 0), +-SOC_SINGLE_TLV("DRC QR Threshold Volume", WM8903_DRC_0, 6, 3, 0, drc_tlv_max), ++SOC_SINGLE_TLV("DRC QR Threshold Volume", WM8903_DRC_1, 6, 3, 0, drc_tlv_max), + SOC_ENUM("DRC QR Decay Rate", drc_qr_decay), + SOC_SINGLE("DRC Smoothing Switch", WM8903_DRC_0, 3, 1, 0), + SOC_SINGLE("DRC Smoothing Hysteresis Switch", WM8903_DRC_0, 0, 1, 0), +diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c +--- a/sound/soc/intel/boards/bytcr_rt5651.c ++++ b/sound/soc/intel/boards/bytcr_rt5651.c +@@ -410,6 +410,23 @@ static const struct dmi_system_id byt_rt5651_quirk_table[] = { + BYT_RT5651_HP_LR_SWAPPED | + BYT_RT5651_MONO_SPEAKER), + }, ++ { ++ /* Chuwi Hi8 (CWI513) with a BIOS using generic DMI strings */ ++ .callback = byt_rt5651_quirk_cb, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"), ++ DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"), ++ /* Above matches are too generic, add bios-date match */ ++ DMI_MATCH(DMI_BIOS_DATE, "05/21/2016"), ++ }, ++ .driver_data = (void *)(BYT_RT5651_MCLK_EN | ++ BYT_RT5651_JD1_1 | ++ BYT_RT5651_OVCD_TH_2000UA | ++ BYT_RT5651_OVCD_SF_1P0 | ++ BYT_RT5651_IN2_MAP | ++ BYT_RT5651_HP_LR_SWAPPED | ++ BYT_RT5651_MONO_SPEAKER), ++ }, + { + /* Chuwi Vi8 Plus (CWI519) */ + .callback = byt_rt5651_quirk_cb, +diff --git a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c +--- a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c ++++ b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c +@@ -745,6 +745,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { + .links = lnl_cs42l43_l0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-cs42l43-l0.tplg", ++ .machine_check = snd_soc_acpi_intel_force_function_topology, + .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { +diff --git a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c +--- a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c ++++ b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c +@@ -53,6 +53,22 @@ bool snd_soc_acpi_intel_no_function_topology(void *arg) + } + EXPORT_SYMBOL_NS(snd_soc_acpi_intel_no_function_topology, "SND_SOC_ACPI_INTEL_SDCA_QUIRKS"); + ++static const struct dmi_system_id force_function_topology_quirk_table[] = { ++ { ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "83R0"), ++ }, ++ }, ++ {} ++}; ++ ++bool snd_soc_acpi_intel_force_function_topology(void *arg) ++{ ++ return !dmi_check_system(force_function_topology_quirk_table); ++} ++EXPORT_SYMBOL_NS(snd_soc_acpi_intel_force_function_topology, "SND_SOC_ACPI_INTEL_SDCA_QUIRKS"); ++ + bool snd_soc_acpi_intel_rt712_vb_no_function_topology(void *arg) + { + return snd_soc_acpi_intel_sdca_is_device_rt712_vb(arg) && +diff --git a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h +--- a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h ++++ b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h +@@ -11,6 +11,7 @@ + + bool snd_soc_acpi_intel_sdca_is_device_rt712_vb(void *arg); + bool snd_soc_acpi_intel_no_function_topology(void *arg); ++bool snd_soc_acpi_intel_force_function_topology(void *arg); + bool snd_soc_acpi_intel_rt712_vb_no_function_topology(void *arg); + + #endif +diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c +--- a/sound/soc/qcom/lpass-cpu.c ++++ b/sound/soc/qcom/lpass-cpu.c +@@ -1234,8 +1234,8 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) + } + if (drvdata->mi2s_playback_sd_mode[dai_id] == + LPAIF_I2SCTL_MODE_QUAD01) { +- variant->dai_driver[dai_id].playback.channels_min = 4; +- variant->dai_driver[dai_id].playback.channels_max = 4; ++ variant->dai_driver[i].playback.channels_min = 4; ++ variant->dai_driver[i].playback.channels_max = 4; + } + } + +diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c +--- a/sound/soc/sdca/sdca_class_function.c ++++ b/sound/soc/sdca/sdca_class_function.c +@@ -388,20 +388,27 @@ static int class_function_probe(struct auxiliary_device *auxdev, + + ret = devm_pm_runtime_enable(dev); + if (ret) +- return ret; ++ goto err_pm; + + ret = class_function_boot(drv); + if (ret) +- return ret; ++ goto err_pm; + + ret = devm_snd_soc_register_component(dev, cmp_drv, dais, num_dais); +- if (ret) +- return dev_err_probe(dev, ret, "failed to register component\n"); ++ if (ret) { ++ dev_err_probe(dev, ret, "failed to register component\n"); ++ goto err_pm; ++ } + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return 0; ++ ++err_pm: ++ pm_runtime_put_sync(dev); ++ ++ return ret; + } + + static void class_function_remove(struct auxiliary_device *auxdev) +@@ -490,8 +497,6 @@ static int class_function_suspend(struct device *dev) + struct class_function_drv *drv = auxiliary_get_drvdata(auxdev); + int ret; + +- drv->suspended = true; +- + /* Ensure runtime resume runs on resume */ + ret = pm_runtime_resume_and_get(dev); + if (ret) { +@@ -499,6 +504,8 @@ static int class_function_suspend(struct device *dev) + return ret; + } + ++ drv->suspended = true; ++ + sdca_irq_disable(drv->function, drv->core->irq_info); + + ret = pm_runtime_force_suspend(dev); +diff --git a/sound/soc/tegra/tegra186_asrc.c b/sound/soc/tegra/tegra186_asrc.c +--- a/sound/soc/tegra/tegra186_asrc.c ++++ b/sound/soc/tegra/tegra186_asrc.c +@@ -828,7 +828,7 @@ static const struct snd_kcontrol_new tegra186_asrc_controls[] = { + tegra186_asrc_put_input_threshold), + + SOC_SINGLE_EXT("Stream6 Input Threshold", +- ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 4), 0, 3, 0, ++ ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 5), 0, 3, 0, + tegra186_asrc_get_input_threshold, + tegra186_asrc_put_input_threshold), + diff --git a/pkgbuilds/linux-omarchy-bore/0528-asoc-fixes-4.patch.sig b/pkgbuilds/linux-omarchy-bore/0528-asoc-fixes-4.patch.sig new file mode 100644 index 0000000..e560a52 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0528-asoc-fixes-4.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0540-media-ipu-bridge-ivsc-no-cvs-lookup.patch b/pkgbuilds/linux-omarchy-bore/0540-media-ipu-bridge-ivsc-no-cvs-lookup.patch deleted file mode 100644 index 3762b2e..0000000 --- a/pkgbuilds/linux-omarchy-bore/0540-media-ipu-bridge-ivsc-no-cvs-lookup.patch +++ /dev/null @@ -1,41 +0,0 @@ -diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c ---- a/drivers/media/pci/intel/ipu-bridge.c -+++ b/drivers/media/pci/intel/ipu-bridge.c -@@ -173,6 +173,19 @@ static const struct acpi_device_id ivsc_acpi_ids[] = { - { "INTC10E1" }, /* PTL */ - }; - -+/* -+ * The subset of ivsc_acpi_ids[] which are IVSC, rather than CVS, devices. The -+ * CVS IDs are deliberately not listed here: new ones keep being added, whereas -+ * this list is complete. -+ */ -+static const struct acpi_device_id ivsc_only_acpi_ids[] = { -+ { "INTC1059" }, -+ { "INTC1095" }, -+ { "INTC100A" }, -+ { "INTC10CF" }, -+ { } -+}; -+ - static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev) - { - unsigned int i; -@@ -224,6 +237,17 @@ static struct device *ipu_bridge_get_ivsc_csi_dev(struct acpi_device *adev) - return csi_dev; - } - -+ /* -+ * The lookups below match on the ACPI companion alone. That is fine for -+ * CVS, which binds a driver to that very device, but not for IVSC: there -+ * the ACPI device also has a driverless platform device, which would be -+ * returned instead of the mei-csi client. Return NULL for IVSC so that -+ * the caller fails and the probe is retried once the IVSC device shows -+ * up. -+ */ -+ if (!acpi_match_device_ids(adev, ivsc_only_acpi_ids)) -+ return NULL; -+ - /* Try to locate CVS device on the I2C bus */ - csi_dev = bus_find_device_by_acpi_dev(&i2c_bus_type, adev); - if (csi_dev) diff --git a/pkgbuilds/linux-omarchy-bore/0540-media-ipu-bridge-ivsc-no-cvs-lookup.patch.sig b/pkgbuilds/linux-omarchy-bore/0540-media-ipu-bridge-ivsc-no-cvs-lookup.patch.sig deleted file mode 100644 index e516289..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/0540-media-ipu-bridge-ivsc-no-cvs-lookup.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/0541-cvs-nova-lake-acpi-id.patch b/pkgbuilds/linux-omarchy-bore/0541-cvs-nova-lake-acpi-id.patch index 39ccf4d..2e401f7 100644 --- a/pkgbuilds/linux-omarchy-bore/0541-cvs-nova-lake-acpi-id.patch +++ b/pkgbuilds/linux-omarchy-bore/0541-cvs-nova-lake-acpi-id.patch @@ -1,7 +1,7 @@ diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c -@@ -862,6 +862,7 @@ static const char * const acpi_honor_dep_ids[] = { +@@ -867,6 +867,7 @@ static const char * const acpi_honor_dep_ids[] = { "INTC10DE", /* CVS (LNL) driver must be loaded to allow camera streaming */ "INTC10E0", /* CVS (ARL) driver must be loaded to allow camera streaming */ "INTC10E1", /* CVS (PTL) driver must be loaded to allow camera streaming */ diff --git a/pkgbuilds/linux-omarchy-bore/0541-cvs-nova-lake-acpi-id.patch.sig b/pkgbuilds/linux-omarchy-bore/0541-cvs-nova-lake-acpi-id.patch.sig index 92c4d7b..818b577 100644 Binary files a/pkgbuilds/linux-omarchy-bore/0541-cvs-nova-lake-acpi-id.patch.sig and b/pkgbuilds/linux-omarchy-bore/0541-cvs-nova-lake-acpi-id.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0543-media-laptop-cameras.patch b/pkgbuilds/linux-omarchy-bore/0543-media-laptop-cameras.patch new file mode 100644 index 0000000..9ffdd20 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0543-media-laptop-cameras.patch @@ -0,0 +1,3293 @@ +diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig +--- a/drivers/media/i2c/Kconfig ++++ b/drivers/media/i2c/Kconfig +@@ -259,6 +259,7 @@ config VIDEO_IMX335 + + config VIDEO_IMX355 + tristate "Sony IMX355 sensor support" ++ select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor driver for the Sony + IMX355 camera. +@@ -287,6 +288,16 @@ config VIDEO_IMX415 + To compile this driver as a module, choose M here: the + module will be called imx415. + ++config VIDEO_IMX471 ++ tristate "Sony IMX471 sensor support" ++ select V4L2_CCI_I2C ++ help ++ This is a Video4Linux2 sensor driver for the Sony ++ IMX471 camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called imx471. ++ + config VIDEO_MAX9271_LIB + tristate + +diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile +--- a/drivers/media/i2c/Makefile ++++ b/drivers/media/i2c/Makefile +@@ -61,6 +61,7 @@ obj-$(CONFIG_VIDEO_IMX335) += imx335.o + obj-$(CONFIG_VIDEO_IMX355) += imx355.o + obj-$(CONFIG_VIDEO_IMX412) += imx412.o + obj-$(CONFIG_VIDEO_IMX415) += imx415.o ++obj-$(CONFIG_VIDEO_IMX471) += imx471.o + obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o + obj-$(CONFIG_VIDEO_ISL7998X) += isl7998x.o + obj-$(CONFIG_VIDEO_KS0127) += ks0127.o +diff --git a/drivers/media/i2c/cvs/core.c b/drivers/media/i2c/cvs/core.c +--- a/drivers/media/i2c/cvs/core.c ++++ b/drivers/media/i2c/cvs/core.c +@@ -31,6 +31,7 @@ + + #define PCI_DEVICE_ID_INTEL_IPU7 0x645d /* MTL / LNL */ + #define PCI_DEVICE_ID_INTEL_IPU7P5 0xb05d /* ARL / PTL */ ++#define PCI_DEVICE_ID_INTEL_IPU8 0xd719 /* NVL */ + + /* + * IPU7 PCI device IDs not covered by ipu6_pci_tbl in ipu6-pci-table.h. +@@ -39,6 +40,7 @@ + static const struct pci_device_id icvs_ipu7_tbl[] = { + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU7) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU7P5) }, ++ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU8) }, + { } + }; + +diff --git a/drivers/media/i2c/cvs/icvs.h b/drivers/media/i2c/cvs/icvs.h +--- a/drivers/media/i2c/cvs/icvs.h ++++ b/drivers/media/i2c/cvs/icvs.h +@@ -432,7 +432,6 @@ enum icvs_state { + * @freq_ctrl: (future) frequency control pointer + * @pads: Local media pads (sink/source) + * @nr_of_lanes: Active CSI-2 lane count +- * @link_freq: Current link frequency (Hz) + * @ipu_link: PM runtime device link (IPU consumer, CVS supplier) + * @res: Resource capability (light/full) + * @caps: Reported device protocol capabilities +@@ -458,7 +457,6 @@ struct icvs { + struct v4l2_ctrl *freq_ctrl; + struct media_pad pads[ICVS_CSI_NUM_PADS]; + u32 nr_of_lanes; +- u64 link_freq; + struct device_link *ipu_link; + enum icvs_resources res; + struct icvs_dev_capabilities caps; +diff --git a/drivers/media/i2c/cvs/v4l2.c b/drivers/media/i2c/cvs/v4l2.c +--- a/drivers/media/i2c/cvs/v4l2.c ++++ b/drivers/media/i2c/cvs/v4l2.c +@@ -46,6 +46,7 @@ static const struct v4l2_mbus_framefmt cvs_csi_format_mbus_default = { + /** + * csi_set_link_cfg - Program default CSI-2 link parameters + * @ctx: CVS device context ++ * @link_freq: Link frequency (Hz) + * + * Populates a HOST_SET_MIPI_CONFIG command using current lane count and + * link frequency, then submits it to the device. +@@ -53,12 +54,12 @@ static const struct v4l2_mbus_framefmt cvs_csi_format_mbus_default = { + * + * Return: 0 on success or negative errno. + */ +-static int csi_set_link_cfg(struct icvs *ctx) ++static int csi_set_link_cfg(struct icvs *ctx, u64 link_freq) + { + struct icvs_cmd cmd = { + .cmd_id = cpu_to_be16(ICVS_HOST_SET_MIPI_CONFIG), + .param.conf.nr_of_lanes = ctx->nr_of_lanes, +- .param.conf.link_freq = ctx->link_freq, ++ .param.conf.link_freq = link_freq, + }; + size_t cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.conf); + +@@ -91,7 +92,7 @@ static int cvs_csi_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev *remote_sd = + media_entity_to_v4l2_subdev(ctx->remote->entity); + struct device *dev = cvs_dev(ctx); +- s64 freq; ++ s64 link_freq; + int ret; + + /* cvs_set_link_owner(ICVS_CSI_LINK_HOST) */ +@@ -99,15 +100,14 @@ static int cvs_csi_enable_streams(struct v4l2_subdev *sd, + if (ret < 0) + return ret; + +- freq = v4l2_get_link_freq(ctx->remote, 0, 0); +- if (freq < 0) { +- ret = freq; ++ link_freq = v4l2_get_link_freq(ctx->remote, 0, 0); ++ if (link_freq < 0) { ++ ret = link_freq; + goto err_rpm_put; + } +- ctx->link_freq = freq; + + if (ctx->i2c_client) { +- ret = csi_set_link_cfg(ctx); ++ ret = csi_set_link_cfg(ctx, link_freq); + if (ret < 0) + goto err_rpm_put_sync; + } +@@ -345,7 +345,6 @@ static int cvs_csi_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad, + if (freq < 0) + return -EINVAL; + +- ctx->link_freq = freq; + cfg->link_freq = freq; + + return 0; +diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c +--- a/drivers/media/i2c/imx355.c ++++ b/drivers/media/i2c/imx355.c +@@ -9,74 +9,97 @@ + #include + #include + #include ++#include + #include + #include + ++#include + #include + #include + #include + #include + +-#define IMX355_REG_MODE_SELECT 0x0100 ++#define IMX355_REG_MODE_SELECT CCI_REG8(0x0100) + #define IMX355_MODE_STANDBY 0x00 + #define IMX355_MODE_STREAMING 0x01 + + /* Chip ID */ +-#define IMX355_REG_CHIP_ID 0x0016 ++#define IMX355_REG_CHIP_ID CCI_REG16(0x0016) + #define IMX355_CHIP_ID 0x0355 + ++#define IMX355_REG_LANE_SEL CCI_REG8(0x0114) ++ ++/* PLL registers that depend on the external clock frequency */ ++#define IMX355_REG_EXTCLK_FREQ CCI_REG16(0x0136) ++#define IMX355_REG_PLL_OP_PREDIV CCI_REG8(0x030d) ++#define IMX355_REG_PLL_OP_MUL CCI_REG16(0x030e) ++#define IMX355_REG_PLL_IVT_PCK_DIV CCI_REG8(0x0301) ++#define IMX355_REG_PLL_IVT_SYSCK_DIV CCI_REG8(0x0303) ++#define IMX355_PLL_OP_PREDIV 2 ++#define IMX355_PLL_IVT_PCK_DIV 5 ++ + /* V_TIMING internal */ +-#define IMX355_REG_FLL 0x0340 ++#define IMX355_REG_FLL CCI_REG16(0x0340) + #define IMX355_FLL_MAX 0xffff ++#define IMX355_VBLANK_MIN 20 ++ ++#define IMX355_REG_LLP CCI_REG16(0x0342) ++#define IMX355_LLP_MAX 0xffff ++ ++#define IMX355_REG_X_ADD_START CCI_REG16(0x0344) ++#define IMX355_REG_Y_ADD_START CCI_REG16(0x0346) ++#define IMX355_REG_X_ADD_END CCI_REG16(0x0348) ++#define IMX355_REG_Y_ADD_END CCI_REG16(0x034a) ++#define IMX355_REG_X_OUT_SIZE CCI_REG16(0x034c) ++#define IMX355_REG_Y_OUT_SIZE CCI_REG16(0x034e) + + /* Exposure control */ +-#define IMX355_REG_EXPOSURE 0x0202 ++#define IMX355_REG_EXPOSURE CCI_REG16(0x0202) + #define IMX355_EXPOSURE_MIN 1 + #define IMX355_EXPOSURE_STEP 1 + #define IMX355_EXPOSURE_DEFAULT 0x0282 ++#define IMX355_EXPOSURE_OFFSET 10 + + /* Analog gain control */ +-#define IMX355_REG_ANALOG_GAIN 0x0204 ++#define IMX355_REG_ANALOG_GAIN CCI_REG16(0x0204) + #define IMX355_ANA_GAIN_MIN 0 + #define IMX355_ANA_GAIN_MAX 960 + #define IMX355_ANA_GAIN_STEP 1 + #define IMX355_ANA_GAIN_DEFAULT 0 + + /* Digital gain control */ +-#define IMX355_REG_DPGA_USE_GLOBAL_GAIN 0x3070 +-#define IMX355_REG_DIG_GAIN_GLOBAL 0x020e ++#define IMX355_REG_DPGA_USE_GLOBAL_GAIN CCI_REG8(0x3070) ++#define IMX355_REG_DIG_GAIN_GLOBAL CCI_REG16(0x020e) + #define IMX355_DGTL_GAIN_MIN 256 + #define IMX355_DGTL_GAIN_MAX 4095 + #define IMX355_DGTL_GAIN_STEP 1 + #define IMX355_DGTL_GAIN_DEFAULT 256 + + /* Test Pattern Control */ +-#define IMX355_REG_TEST_PATTERN 0x0600 ++#define IMX355_REG_TEST_PATTERN CCI_REG16(0x0600) + #define IMX355_TEST_PATTERN_DISABLED 0 + #define IMX355_TEST_PATTERN_SOLID_COLOR 1 + #define IMX355_TEST_PATTERN_COLOR_BARS 2 + #define IMX355_TEST_PATTERN_GRAY_COLOR_BARS 3 + #define IMX355_TEST_PATTERN_PN9 4 + ++#define IMX355_REG_REQ_LINK_BIT_RATE CCI_REG16(0x0820) ++ ++#define IMX355_REG_BINNING_MODE CCI_REG8(0x0900) ++#define IMX355_REG_BINNING_TYPE CCI_REG8(0x0901) ++#define IMX355_REG_BINNING_WEIGHTING CCI_REG8(0x0902) ++ + /* Flip Control */ +-#define IMX355_REG_ORIENTATION 0x0101 ++#define IMX355_REG_ORIENTATION CCI_REG8(0x0101) + +-/* default link frequency and external clock */ +-#define IMX355_LINK_FREQ_DEFAULT 360000000LL +-#define IMX355_EXT_CLK 19200000 +-#define IMX355_LINK_FREQ_INDEX 0 +- +-/* number of data lanes */ +-#define IMX355_DATA_LANES 4 +- +-struct imx355_reg { +- u16 address; +- u8 val; +-}; ++#define IMX355_PIXEL_ARRAY_TOP 0 ++#define IMX355_PIXEL_ARRAY_LEFT 0 ++#define IMX355_PIXEL_ARRAY_WIDTH 3280 ++#define IMX355_PIXEL_ARRAY_HEIGHT 2464 + + struct imx355_reg_list { + u32 num_of_regs; +- const struct imx355_reg *regs; ++ const struct cci_reg_sequence *regs; + }; + + /* Mode : resolution and related config&values */ +@@ -85,28 +108,58 @@ struct imx355_mode { + u32 width; + /* Frame height */ + u32 height; ++ struct v4l2_rect crop; + + /* V-timing */ + u32 fll_def; +- u32 fll_min; + + /* H-timing */ + u32 llp; + +- /* index of link frequency */ +- u32 link_freq_index; +- + /* Default register values */ + struct imx355_reg_list reg_list; + }; + ++struct imx355_clk_params { ++ u32 ext_clk; ++ u16 extclk_freq; /* External clock (MHz) in 8.8 fixed point) */ ++ u16 pll_op_mpy[2]; /* OP system PLL multiplier */ ++ u8 pll_op_prediv[2]; /* OP system pre PLL d */ ++}; ++ ++/* ++ * The clock tree is in single PLL mode, so PREDIV_VT and MPY_IVT do nothing. ++ * In 4 lane mode the MIPI rate is 360Mhz (720Mbit/s) and pixel rate is ++ * 288MPix/s. ++ * In 2 lane mode the MIPI rate is 444MHz (888Mbit/s) and pixel rate ++ * 177.6MPix/s with a 24MHz clock, and 441.6MHz (883.2Mbit/s) and 176.6MPix/s ++ * with a 19.2MHz clock. ++ */ ++static const struct imx355_clk_params imx355_clk_params[] = { ++ { ++ .ext_clk = 19200000, ++ .extclk_freq = 0x1333, ++ .pll_op_mpy = { 75, 92 }, ++ .pll_op_prediv = { 2, 2 } ++ }, ++ { ++ .ext_clk = 24000000, ++ .extclk_freq = 0x1800, ++ .pll_op_mpy = { 60, 111 }, ++ .pll_op_prediv = { 2, 3 } ++ }, ++}; ++ + struct imx355_hwcfg { ++ s64 link_freq_menu; + unsigned long link_freq_bitmap; ++ unsigned int num_lanes; + }; + + struct imx355 { + struct device *dev; + struct clk *clk; ++ struct regmap *regmap; + + struct v4l2_subdev sd; + struct media_pad pad; +@@ -114,24 +167,14 @@ struct imx355 { + struct v4l2_ctrl_handler ctrl_handler; + /* V4L2 Controls */ + struct v4l2_ctrl *link_freq; +- struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hflip; + +- /* Current mode */ +- const struct imx355_mode *cur_mode; +- + struct imx355_hwcfg *hwcfg; +- +- /* +- * Mutex for serialized access: +- * Protect sensor set pad format and start/stop streaming safely. +- * Protect access to sensor v4l2 controls. +- */ +- struct mutex mutex; ++ const struct imx355_clk_params *clk_params; + + struct gpio_desc *reset_gpio; + struct regulator_bulk_data *supplies; +@@ -143,750 +186,147 @@ static const struct regulator_bulk_data imx355_supplies[] = { + { .supply = "dovdd" }, + }; + +-static const struct imx355_reg imx355_global_regs[] = { +- { 0x0136, 0x13 }, +- { 0x0137, 0x33 }, +- { 0x304e, 0x03 }, +- { 0x4348, 0x16 }, +- { 0x4350, 0x19 }, +- { 0x4408, 0x0a }, +- { 0x440c, 0x0b }, +- { 0x4411, 0x5f }, +- { 0x4412, 0x2c }, +- { 0x4623, 0x00 }, +- { 0x462c, 0x0f }, +- { 0x462d, 0x00 }, +- { 0x462e, 0x00 }, +- { 0x4684, 0x54 }, +- { 0x480a, 0x07 }, +- { 0x4908, 0x07 }, +- { 0x4909, 0x07 }, +- { 0x490d, 0x0a }, +- { 0x491e, 0x0f }, +- { 0x4921, 0x06 }, +- { 0x4923, 0x28 }, +- { 0x4924, 0x28 }, +- { 0x4925, 0x29 }, +- { 0x4926, 0x29 }, +- { 0x4927, 0x1f }, +- { 0x4928, 0x20 }, +- { 0x4929, 0x20 }, +- { 0x492a, 0x20 }, +- { 0x492c, 0x05 }, +- { 0x492d, 0x06 }, +- { 0x492e, 0x06 }, +- { 0x492f, 0x06 }, +- { 0x4930, 0x03 }, +- { 0x4931, 0x04 }, +- { 0x4932, 0x04 }, +- { 0x4933, 0x05 }, +- { 0x595e, 0x01 }, +- { 0x5963, 0x01 }, +- { 0x3030, 0x01 }, +- { 0x3031, 0x01 }, +- { 0x3045, 0x01 }, +- { 0x4010, 0x00 }, +- { 0x4011, 0x00 }, +- { 0x4012, 0x00 }, +- { 0x4013, 0x01 }, +- { 0x68a8, 0xfe }, +- { 0x68a9, 0xff }, +- { 0x6888, 0x00 }, +- { 0x6889, 0x00 }, +- { 0x68b0, 0x00 }, +- { 0x3058, 0x00 }, +- { 0x305a, 0x00 }, ++static const struct cci_reg_sequence imx355_global_regs[] = { ++ { CCI_REG8(0x304e), 0x03 }, ++ { CCI_REG8(0x4348), 0x16 }, ++ { CCI_REG8(0x4350), 0x19 }, ++ { CCI_REG8(0x4408), 0x0a }, ++ { CCI_REG8(0x440c), 0x0b }, ++ { CCI_REG8(0x4411), 0x5f }, ++ { CCI_REG8(0x4412), 0x2c }, ++ { CCI_REG8(0x4623), 0x00 }, ++ { CCI_REG8(0x462c), 0x0f }, ++ { CCI_REG8(0x462d), 0x00 }, ++ { CCI_REG8(0x462e), 0x00 }, ++ { CCI_REG8(0x4684), 0x54 }, ++ { CCI_REG8(0x480a), 0x07 }, ++ { CCI_REG8(0x4908), 0x07 }, ++ { CCI_REG8(0x4909), 0x07 }, ++ { CCI_REG8(0x490d), 0x0a }, ++ { CCI_REG8(0x491e), 0x0f }, ++ { CCI_REG8(0x4921), 0x06 }, ++ { CCI_REG8(0x4923), 0x28 }, ++ { CCI_REG8(0x4924), 0x28 }, ++ { CCI_REG8(0x4925), 0x29 }, ++ { CCI_REG8(0x4926), 0x29 }, ++ { CCI_REG8(0x4927), 0x1f }, ++ { CCI_REG8(0x4928), 0x20 }, ++ { CCI_REG8(0x4929), 0x20 }, ++ { CCI_REG8(0x492a), 0x20 }, ++ { CCI_REG8(0x492c), 0x05 }, ++ { CCI_REG8(0x492d), 0x06 }, ++ { CCI_REG8(0x492e), 0x06 }, ++ { CCI_REG8(0x492f), 0x06 }, ++ { CCI_REG8(0x4930), 0x03 }, ++ { CCI_REG8(0x4931), 0x04 }, ++ { CCI_REG8(0x4932), 0x04 }, ++ { CCI_REG8(0x4933), 0x05 }, ++ { CCI_REG8(0x595e), 0x01 }, ++ { CCI_REG8(0x5963), 0x01 }, ++ { CCI_REG8(0x3030), 0x01 }, ++ { CCI_REG8(0x3031), 0x01 }, ++ { CCI_REG8(0x3045), 0x01 }, ++ { CCI_REG8(0x4010), 0x00 }, ++ { CCI_REG8(0x4011), 0x00 }, ++ { CCI_REG8(0x4012), 0x00 }, ++ { CCI_REG8(0x4013), 0x01 }, ++ { CCI_REG8(0x68a8), 0xfe }, ++ { CCI_REG8(0x68a9), 0xff }, ++ { CCI_REG8(0x6888), 0x00 }, ++ { CCI_REG8(0x6889), 0x00 }, ++ { CCI_REG8(0x68b0), 0x00 }, ++ { CCI_REG8(0x3058), 0x00 }, ++ { CCI_REG8(0x305a), 0x00 }, ++ { CCI_REG8(0x0112), 0x0a }, ++ { CCI_REG8(0x0113), 0x0a }, ++ { IMX355_REG_PLL_IVT_PCK_DIV, IMX355_PLL_IVT_PCK_DIV }, ++ { CCI_REG8(0x0303), 0x01 }, ++ { CCI_REG8(0x0305), 0x02 }, ++ { CCI_REG8(0x0306), 0x00 }, ++ { CCI_REG8(0x0307), 0x78 }, ++ { CCI_REG8(0x030b), 0x01 }, ++ { IMX355_REG_PLL_OP_PREDIV, IMX355_PLL_OP_PREDIV }, ++ { CCI_REG8(0x0310), 0x00 }, ++ { CCI_REG8(0x0220), 0x00 }, ++ { CCI_REG8(0x0222), 0x01 }, ++ { CCI_REG8(0x3088), 0x04 }, ++ { CCI_REG8(0x6813), 0x02 }, ++ { CCI_REG8(0x6835), 0x07 }, ++ { CCI_REG8(0x6836), 0x01 }, ++ { CCI_REG8(0x6837), 0x04 }, ++ { CCI_REG8(0x684d), 0x07 }, ++ { CCI_REG8(0x684e), 0x01 }, ++ { CCI_REG8(0x684f), 0x04 }, + }; + +-static const struct imx355_reg_list imx355_global_setting = { +- .num_of_regs = ARRAY_SIZE(imx355_global_regs), +- .regs = imx355_global_regs, ++static const struct cci_reg_sequence mode_3268x2448_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_3268x2448_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x0e }, +- { 0x0343, 0x58 }, +- { 0x0340, 0x0a }, +- { 0x0341, 0x37 }, +- { 0x0344, 0x00 }, +- { 0x0345, 0x08 }, +- { 0x0346, 0x00 }, +- { 0x0347, 0x08 }, +- { 0x0348, 0x0c }, +- { 0x0349, 0xcb }, +- { 0x034a, 0x09 }, +- { 0x034b, 0x97 }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x00 }, +- { 0x0901, 0x11 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x0c }, +- { 0x034d, 0xc4 }, +- { 0x034e, 0x09 }, +- { 0x034f, 0x90 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_3264x2448_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_3264x2448_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x0e }, +- { 0x0343, 0x58 }, +- { 0x0340, 0x0a }, +- { 0x0341, 0x37 }, +- { 0x0344, 0x00 }, +- { 0x0345, 0x08 }, +- { 0x0346, 0x00 }, +- { 0x0347, 0x08 }, +- { 0x0348, 0x0c }, +- { 0x0349, 0xc7 }, +- { 0x034a, 0x09 }, +- { 0x034b, 0x97 }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x00 }, +- { 0x0901, 0x11 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x0c }, +- { 0x034d, 0xc0 }, +- { 0x034e, 0x09 }, +- { 0x034f, 0x90 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_3280x2464_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_3280x2464_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x0e }, +- { 0x0343, 0x58 }, +- { 0x0340, 0x0a }, +- { 0x0341, 0x37 }, +- { 0x0344, 0x00 }, +- { 0x0345, 0x00 }, +- { 0x0346, 0x00 }, +- { 0x0347, 0x00 }, +- { 0x0348, 0x0c }, +- { 0x0349, 0xcf }, +- { 0x034a, 0x09 }, +- { 0x034b, 0x9f }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x00 }, +- { 0x0901, 0x11 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x0c }, +- { 0x034d, 0xd0 }, +- { 0x034e, 0x09 }, +- { 0x034f, 0xa0 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_1940x1096_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_1940x1096_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x0e }, +- { 0x0343, 0x58 }, +- { 0x0340, 0x05 }, +- { 0x0341, 0x1a }, +- { 0x0344, 0x02 }, +- { 0x0345, 0xa0 }, +- { 0x0346, 0x02 }, +- { 0x0347, 0xac }, +- { 0x0348, 0x0a }, +- { 0x0349, 0x33 }, +- { 0x034a, 0x06 }, +- { 0x034b, 0xf3 }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x00 }, +- { 0x0901, 0x11 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x07 }, +- { 0x034d, 0x94 }, +- { 0x034e, 0x04 }, +- { 0x034f, 0x48 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_1936x1096_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_1936x1096_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x0e }, +- { 0x0343, 0x58 }, +- { 0x0340, 0x05 }, +- { 0x0341, 0x1a }, +- { 0x0344, 0x02 }, +- { 0x0345, 0xa0 }, +- { 0x0346, 0x02 }, +- { 0x0347, 0xac }, +- { 0x0348, 0x0a }, +- { 0x0349, 0x2f }, +- { 0x034a, 0x06 }, +- { 0x034b, 0xf3 }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x00 }, +- { 0x0901, 0x11 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x07 }, +- { 0x034d, 0x90 }, +- { 0x034e, 0x04 }, +- { 0x034f, 0x48 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_1924x1080_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_1924x1080_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x0e }, +- { 0x0343, 0x58 }, +- { 0x0340, 0x05 }, +- { 0x0341, 0x1a }, +- { 0x0344, 0x02 }, +- { 0x0345, 0xa8 }, +- { 0x0346, 0x02 }, +- { 0x0347, 0xb4 }, +- { 0x0348, 0x0a }, +- { 0x0349, 0x2b }, +- { 0x034a, 0x06 }, +- { 0x034b, 0xeb }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x00 }, +- { 0x0901, 0x11 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x07 }, +- { 0x034d, 0x84 }, +- { 0x034e, 0x04 }, +- { 0x034f, 0x38 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_1920x1080_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_1920x1080_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x0e }, +- { 0x0343, 0x58 }, +- { 0x0340, 0x05 }, +- { 0x0341, 0x1a }, +- { 0x0344, 0x02 }, +- { 0x0345, 0xa8 }, +- { 0x0346, 0x02 }, +- { 0x0347, 0xb4 }, +- { 0x0348, 0x0a }, +- { 0x0349, 0x27 }, +- { 0x034a, 0x06 }, +- { 0x034b, 0xeb }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x00 }, +- { 0x0901, 0x11 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x07 }, +- { 0x034d, 0x80 }, +- { 0x034e, 0x04 }, +- { 0x034f, 0x38 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_1640x1232_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_1640x1232_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x07 }, +- { 0x0343, 0x2c }, +- { 0x0340, 0x05 }, +- { 0x0341, 0x1a }, +- { 0x0344, 0x00 }, +- { 0x0345, 0x00 }, +- { 0x0346, 0x00 }, +- { 0x0347, 0x00 }, +- { 0x0348, 0x0c }, +- { 0x0349, 0xcf }, +- { 0x034a, 0x09 }, +- { 0x034b, 0x9f }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x01 }, +- { 0x0901, 0x22 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x06 }, +- { 0x034d, 0x68 }, +- { 0x034e, 0x04 }, +- { 0x034f, 0xd0 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_1640x922_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_1640x922_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x07 }, +- { 0x0343, 0x2c }, +- { 0x0340, 0x05 }, +- { 0x0341, 0x1a }, +- { 0x0344, 0x00 }, +- { 0x0345, 0x00 }, +- { 0x0346, 0x01 }, +- { 0x0347, 0x30 }, +- { 0x0348, 0x0c }, +- { 0x0349, 0xcf }, +- { 0x034a, 0x08 }, +- { 0x034b, 0x63 }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x01 }, +- { 0x0901, 0x22 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x06 }, +- { 0x034d, 0x68 }, +- { 0x034e, 0x03 }, +- { 0x034f, 0x9a }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_1300x736_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_1300x736_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x07 }, +- { 0x0343, 0x2c }, +- { 0x0340, 0x05 }, +- { 0x0341, 0x1a }, +- { 0x0344, 0x01 }, +- { 0x0345, 0x58 }, +- { 0x0346, 0x01 }, +- { 0x0347, 0xf0 }, +- { 0x0348, 0x0b }, +- { 0x0349, 0x7f }, +- { 0x034a, 0x07 }, +- { 0x034b, 0xaf }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x01 }, +- { 0x0901, 0x22 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x05 }, +- { 0x034d, 0x14 }, +- { 0x034e, 0x02 }, +- { 0x034f, 0xe0 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_1296x736_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_1296x736_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x07 }, +- { 0x0343, 0x2c }, +- { 0x0340, 0x05 }, +- { 0x0341, 0x1a }, +- { 0x0344, 0x01 }, +- { 0x0345, 0x58 }, +- { 0x0346, 0x01 }, +- { 0x0347, 0xf0 }, +- { 0x0348, 0x0b }, +- { 0x0349, 0x77 }, +- { 0x034a, 0x07 }, +- { 0x034b, 0xaf }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x01 }, +- { 0x0901, 0x22 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x05 }, +- { 0x034d, 0x10 }, +- { 0x034e, 0x02 }, +- { 0x034f, 0xe0 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_1284x720_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_1284x720_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x07 }, +- { 0x0343, 0x2c }, +- { 0x0340, 0x05 }, +- { 0x0341, 0x1a }, +- { 0x0344, 0x01 }, +- { 0x0345, 0x68 }, +- { 0x0346, 0x02 }, +- { 0x0347, 0x00 }, +- { 0x0348, 0x0b }, +- { 0x0349, 0x6f }, +- { 0x034a, 0x07 }, +- { 0x034b, 0x9f }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x01 }, +- { 0x0901, 0x22 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x05 }, +- { 0x034d, 0x04 }, +- { 0x034e, 0x02 }, +- { 0x034f, 0xd0 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_1280x720_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_1280x720_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x07 }, +- { 0x0343, 0x2c }, +- { 0x0340, 0x05 }, +- { 0x0341, 0x1a }, +- { 0x0344, 0x01 }, +- { 0x0345, 0x68 }, +- { 0x0346, 0x02 }, +- { 0x0347, 0x00 }, +- { 0x0348, 0x0b }, +- { 0x0349, 0x67 }, +- { 0x034a, 0x07 }, +- { 0x034b, 0x9f }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x01 }, +- { 0x0901, 0x22 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x05 }, +- { 0x034d, 0x00 }, +- { 0x034e, 0x02 }, +- { 0x034f, 0xd0 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, +-}; +- +-static const struct imx355_reg mode_820x616_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x0e }, +- { 0x0343, 0x58 }, +- { 0x0340, 0x02 }, +- { 0x0341, 0x8c }, +- { 0x0344, 0x00 }, +- { 0x0345, 0x00 }, +- { 0x0346, 0x00 }, +- { 0x0347, 0x00 }, +- { 0x0348, 0x0c }, +- { 0x0349, 0xcf }, +- { 0x034a, 0x09 }, +- { 0x034b, 0x9f }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x01 }, +- { 0x0901, 0x44 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x03 }, +- { 0x034d, 0x34 }, +- { 0x034e, 0x02 }, +- { 0x034f, 0x68 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x02 }, +- { 0x0701, 0x78 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_820x616_regs[] = { ++ { CCI_REG8(0x0700), 0x02 }, ++ { CCI_REG8(0x0701), 0x78 }, + }; + + static const char * const imx355_test_pattern_menu[] = { +@@ -897,23 +337,19 @@ static const char * const imx355_test_pattern_menu[] = { + "Pseudorandom Sequence (PN9)", + }; + +-/* +- * When adding more than the one below, make sure the disallowed ones will +- * actually be disabled in the LINK_FREQ control. +- */ +-static const s64 link_freq_menu_items[] = { +- IMX355_LINK_FREQ_DEFAULT, +-}; +- + /* Mode configs */ + static const struct imx355_mode supported_modes[] = { + { + .width = 3280, + .height = 2464, ++ .crop = { ++ .width = 3280, ++ .height = 2464, ++ .left = 0, ++ .top = 0, ++ }, + .fll_def = 2615, +- .fll_min = 2615, + .llp = 3672, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs), + .regs = mode_3280x2464_regs, +@@ -922,10 +358,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 3268, + .height = 2448, ++ .crop = { ++ .width = 3268, ++ .height = 2448, ++ .left = 8, ++ .top = 8, ++ }, + .fll_def = 2615, +- .fll_min = 2615, + .llp = 3672, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3268x2448_regs), + .regs = mode_3268x2448_regs, +@@ -934,10 +374,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 3264, + .height = 2448, ++ .crop = { ++ .width = 3264, ++ .height = 2448, ++ .left = 8, ++ .top = 8, ++ }, + .fll_def = 2615, +- .fll_min = 2615, + .llp = 3672, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3264x2448_regs), + .regs = mode_3264x2448_regs, +@@ -946,10 +390,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 1940, + .height = 1096, ++ .crop = { ++ .width = 1940, ++ .height = 1096, ++ .left = 672, ++ .top = 684, ++ }, + .fll_def = 1306, +- .fll_min = 1306, + .llp = 3672, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1940x1096_regs), + .regs = mode_1940x1096_regs, +@@ -958,10 +406,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 1936, + .height = 1096, ++ .crop = { ++ .width = 1936, ++ .height = 1096, ++ .left = 672, ++ .top = 684, ++ }, + .fll_def = 1306, +- .fll_min = 1306, + .llp = 3672, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1936x1096_regs), + .regs = mode_1936x1096_regs, +@@ -970,10 +422,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 1924, + .height = 1080, ++ .crop = { ++ .width = 1924, ++ .height = 1080, ++ .left = 680, ++ .top = 692, ++ }, + .fll_def = 1306, +- .fll_min = 1306, + .llp = 3672, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1924x1080_regs), + .regs = mode_1924x1080_regs, +@@ -982,10 +438,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 1920, + .height = 1080, ++ .crop = { ++ .width = 1920, ++ .height = 1080, ++ .left = 680, ++ .top = 692, ++ }, + .fll_def = 1306, +- .fll_min = 1306, + .llp = 3672, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1920x1080_regs), + .regs = mode_1920x1080_regs, +@@ -994,10 +454,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 1640, + .height = 1232, ++ .crop = { ++ .width = 3280, ++ .height = 2464, ++ .left = 0, ++ .top = 0, ++ }, + .fll_def = 1306, +- .fll_min = 1306, + .llp = 1836, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1640x1232_regs), + .regs = mode_1640x1232_regs, +@@ -1006,10 +470,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 1640, + .height = 922, ++ .crop = { ++ .width = 3280, ++ .height = 1844, ++ .left = 0, ++ .top = 304, ++ }, + .fll_def = 1306, +- .fll_min = 1306, + .llp = 1836, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1640x922_regs), + .regs = mode_1640x922_regs, +@@ -1018,10 +486,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 1300, + .height = 736, ++ .crop = { ++ .width = 2600, ++ .height = 1472, ++ .left = 344, ++ .top = 496, ++ }, + .fll_def = 1306, +- .fll_min = 1306, + .llp = 1836, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1300x736_regs), + .regs = mode_1300x736_regs, +@@ -1030,10 +502,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 1296, + .height = 736, ++ .crop = { ++ .width = 2592, ++ .height = 1472, ++ .left = 344, ++ .top = 496, ++ }, + .fll_def = 1306, +- .fll_min = 1306, + .llp = 1836, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1296x736_regs), + .regs = mode_1296x736_regs, +@@ -1042,10 +518,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 1284, + .height = 720, ++ .crop = { ++ .width = 2568, ++ .height = 1440, ++ .left = 360, ++ .top = 512, ++ }, + .fll_def = 1306, +- .fll_min = 1306, + .llp = 1836, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1284x720_regs), + .regs = mode_1284x720_regs, +@@ -1054,10 +534,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 1280, + .height = 720, ++ .crop = { ++ .width = 2560, ++ .height = 1440, ++ .left = 360, ++ .top = 512, ++ }, + .fll_def = 1306, +- .fll_min = 1306, + .llp = 1836, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1280x720_regs), + .regs = mode_1280x720_regs, +@@ -1066,10 +550,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 820, + .height = 616, ++ .crop = { ++ .width = 3280, ++ .height = 2464, ++ .left = 0, ++ .top = 0, ++ }, + .fll_def = 652, +- .fll_min = 652, + .llp = 3672, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_820x616_regs), + .regs = mode_820x616_regs, +@@ -1095,116 +583,28 @@ static u32 imx355_get_format_code(struct imx355 *imx355) + { MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SBGGR10_1X10, }, + }; + +- lockdep_assert_held(&imx355->mutex); + code = codes[imx355->vflip->val][imx355->hflip->val]; + + return code; + } + +-/* Read registers up to 4 at a time */ +-static int imx355_read_reg(struct imx355 *imx355, u16 reg, u32 len, u32 *val) +-{ +- struct i2c_client *client = v4l2_get_subdevdata(&imx355->sd); +- struct i2c_msg msgs[2]; +- u8 addr_buf[2]; +- u8 data_buf[4] = { 0 }; +- int ret; +- +- if (len > 4) +- return -EINVAL; +- +- put_unaligned_be16(reg, addr_buf); +- /* Write register address */ +- msgs[0].addr = client->addr; +- msgs[0].flags = 0; +- msgs[0].len = ARRAY_SIZE(addr_buf); +- msgs[0].buf = addr_buf; +- +- /* Read data from register */ +- msgs[1].addr = client->addr; +- msgs[1].flags = I2C_M_RD; +- msgs[1].len = len; +- msgs[1].buf = &data_buf[4 - len]; +- +- ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); +- if (ret != ARRAY_SIZE(msgs)) +- return -EIO; +- +- *val = get_unaligned_be32(data_buf); +- +- return 0; +-} +- +-/* Write registers up to 4 at a time */ +-static int imx355_write_reg(struct imx355 *imx355, u16 reg, u32 len, u32 val) +-{ +- struct i2c_client *client = v4l2_get_subdevdata(&imx355->sd); +- u8 buf[6]; +- +- if (len > 4) +- return -EINVAL; +- +- put_unaligned_be16(reg, buf); +- put_unaligned_be32(val << (8 * (4 - len)), buf + 2); +- if (i2c_master_send(client, buf, len + 2) != len + 2) +- return -EIO; +- +- return 0; +-} +- +-/* Write a list of registers */ +-static int imx355_write_regs(struct imx355 *imx355, +- const struct imx355_reg *regs, u32 len) +-{ +- int ret; +- u32 i; +- +- for (i = 0; i < len; i++) { +- ret = imx355_write_reg(imx355, regs[i].address, 1, regs[i].val); +- if (ret) { +- dev_err_ratelimited(imx355->dev, +- "write reg 0x%4.4x return err %d", +- regs[i].address, ret); +- +- return ret; +- } +- } +- +- return 0; +-} +- +-/* Open sub-device */ +-static int imx355_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +-{ +- struct imx355 *imx355 = to_imx355(sd); +- struct v4l2_mbus_framefmt *try_fmt = +- v4l2_subdev_state_get_format(fh->state, 0); +- +- mutex_lock(&imx355->mutex); +- +- /* Initialize try_fmt */ +- try_fmt->width = imx355->cur_mode->width; +- try_fmt->height = imx355->cur_mode->height; +- try_fmt->code = imx355_get_format_code(imx355); +- try_fmt->field = V4L2_FIELD_NONE; +- +- mutex_unlock(&imx355->mutex); +- +- return 0; +-} +- + static int imx355_set_ctrl(struct v4l2_ctrl *ctrl) + { + struct imx355 *imx355 = container_of(ctrl->handler, + struct imx355, ctrl_handler); ++ const struct v4l2_mbus_framefmt *format = NULL; ++ struct v4l2_subdev_state *state; + s64 max; + int ret; + ++ state = v4l2_subdev_get_locked_active_state(&imx355->sd); ++ format = v4l2_subdev_state_get_format(state, 0); ++ + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ +- max = imx355->cur_mode->height + ctrl->val - 10; ++ max = format->height + ctrl->val - IMX355_EXPOSURE_OFFSET; + __v4l2_ctrl_modify_range(imx355->exposure, + imx355->exposure->minimum, + max, imx355->exposure->step, max); +@@ -1221,31 +621,31 @@ static int imx355_set_ctrl(struct v4l2_ctrl *ctrl) + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + /* Analog gain = 1024/(1024 - ctrl->val) times */ +- ret = imx355_write_reg(imx355, IMX355_REG_ANALOG_GAIN, 2, +- ctrl->val); ++ ret = cci_write(imx355->regmap, IMX355_REG_ANALOG_GAIN, ++ ctrl->val, NULL); + break; + case V4L2_CID_DIGITAL_GAIN: +- ret = imx355_write_reg(imx355, IMX355_REG_DIG_GAIN_GLOBAL, 2, +- ctrl->val); ++ ret = cci_write(imx355->regmap, IMX355_REG_DIG_GAIN_GLOBAL, ++ ctrl->val, NULL); + break; + case V4L2_CID_EXPOSURE: +- ret = imx355_write_reg(imx355, IMX355_REG_EXPOSURE, 2, +- ctrl->val); ++ ret = cci_write(imx355->regmap, IMX355_REG_EXPOSURE, ++ ctrl->val, NULL); + break; + case V4L2_CID_VBLANK: + /* Update FLL that meets expected vertical blanking */ +- ret = imx355_write_reg(imx355, IMX355_REG_FLL, 2, +- imx355->cur_mode->height + ctrl->val); ++ ret = cci_write(imx355->regmap, IMX355_REG_FLL, ++ format->height + ctrl->val, NULL); + break; + case V4L2_CID_TEST_PATTERN: +- ret = imx355_write_reg(imx355, IMX355_REG_TEST_PATTERN, +- 2, ctrl->val); ++ ret = cci_write(imx355->regmap, IMX355_REG_TEST_PATTERN, ++ ctrl->val, NULL); + break; + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: +- ret = imx355_write_reg(imx355, IMX355_REG_ORIENTATION, 1, +- imx355->hflip->val | +- imx355->vflip->val << 1); ++ ret = cci_write(imx355->regmap, IMX355_REG_ORIENTATION, ++ imx355->hflip->val | imx355->vflip->val << 1, ++ NULL); + break; + default: + ret = -EINVAL; +@@ -1272,9 +672,7 @@ static int imx355_enum_mbus_code(struct v4l2_subdev *sd, + if (code->index > 0) + return -EINVAL; + +- mutex_lock(&imx355->mutex); + code->code = imx355_get_format_code(imx355); +- mutex_unlock(&imx355->mutex); + + return 0; + } +@@ -1288,12 +686,9 @@ static int imx355_enum_frame_size(struct v4l2_subdev *sd, + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + +- mutex_lock(&imx355->mutex); + if (fse->code != imx355_get_format_code(imx355)) { +- mutex_unlock(&imx355->mutex); + return -EINVAL; + } +- mutex_unlock(&imx355->mutex); + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; +@@ -1311,36 +706,10 @@ static void imx355_update_pad_format(struct imx355 *imx355, + fmt->format.height = mode->height; + fmt->format.code = imx355_get_format_code(imx355); + fmt->format.field = V4L2_FIELD_NONE; +-} +- +-static int imx355_do_get_pad_format(struct imx355 *imx355, +- struct v4l2_subdev_state *sd_state, +- struct v4l2_subdev_format *fmt) +-{ +- struct v4l2_mbus_framefmt *framefmt; +- +- if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +- framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); +- fmt->format = *framefmt; +- } else { +- imx355_update_pad_format(imx355, imx355->cur_mode, fmt); +- } +- +- return 0; +-} +- +-static int imx355_get_pad_format(struct v4l2_subdev *sd, +- struct v4l2_subdev_state *sd_state, +- struct v4l2_subdev_format *fmt) +-{ +- struct imx355 *imx355 = to_imx355(sd); +- int ret; +- +- mutex_lock(&imx355->mutex); +- ret = imx355_do_get_pad_format(imx355, sd_state, fmt); +- mutex_unlock(&imx355->mutex); +- +- return ret; ++ fmt->format.colorspace = V4L2_COLORSPACE_RAW; ++ fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_601; ++ fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE; ++ fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; + } + + static int +@@ -1351,13 +720,8 @@ imx355_set_pad_format(struct v4l2_subdev *sd, + struct imx355 *imx355 = to_imx355(sd); + const struct imx355_mode *mode; + struct v4l2_mbus_framefmt *framefmt; +- s32 vblank_def; +- s32 vblank_min; ++ struct v4l2_rect *crop; + s64 h_blank; +- u64 pixel_rate; +- u32 height; +- +- mutex_lock(&imx355->mutex); + + /* + * Only one bayer order is supported. +@@ -1370,23 +734,25 @@ imx355_set_pad_format(struct v4l2_subdev *sd, + width, height, + fmt->format.width, fmt->format.height); + imx355_update_pad_format(imx355, mode, fmt); +- if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +- framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); +- *framefmt = fmt->format; +- } else { +- imx355->cur_mode = mode; +- pixel_rate = IMX355_LINK_FREQ_DEFAULT * 2 * 4; +- do_div(pixel_rate, 10); +- __v4l2_ctrl_s_ctrl_int64(imx355->pixel_rate, pixel_rate); ++ framefmt = v4l2_subdev_state_get_format(sd_state, 0); ++ ++ *framefmt = fmt->format; ++ ++ crop = v4l2_subdev_state_get_crop(sd_state, 0); ++ crop->width = mode->crop.width; ++ crop->height = mode->crop.height; ++ crop->left = mode->crop.left; ++ crop->top = mode->crop.top; ++ ++ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + /* Update limits and set FPS to default */ +- height = imx355->cur_mode->height; +- vblank_def = imx355->cur_mode->fll_def - height; +- vblank_min = imx355->cur_mode->fll_min - height; +- height = IMX355_FLL_MAX - height; +- __v4l2_ctrl_modify_range(imx355->vblank, vblank_min, height, 1, +- vblank_def); +- __v4l2_ctrl_s_ctrl(imx355->vblank, vblank_def); +- h_blank = mode->llp - imx355->cur_mode->width; ++ __v4l2_ctrl_modify_range(imx355->vblank, IMX355_VBLANK_MIN, ++ IMX355_FLL_MAX - mode->height, 1, ++ mode->fll_def - mode->height); ++ __v4l2_ctrl_s_ctrl(imx355->vblank, mode->fll_def - mode->height); ++ ++ h_blank = mode->llp - mode->width; ++ + /* + * Currently hblank is not changeable. + * So FPS control is done only by vblank. +@@ -1395,7 +761,42 @@ imx355_set_pad_format(struct v4l2_subdev *sd, + h_blank, 1, h_blank); + } + +- mutex_unlock(&imx355->mutex); ++ return 0; ++} ++ ++static int imx355_get_selection(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_selection *sel) ++{ ++ switch (sel->target) { ++ case V4L2_SEL_TGT_CROP: ++ sel->r = *v4l2_subdev_state_get_crop(sd_state, 0); ++ return 0; ++ case V4L2_SEL_TGT_CROP_DEFAULT: ++ case V4L2_SEL_TGT_CROP_BOUNDS: ++ case V4L2_SEL_TGT_NATIVE_SIZE: ++ sel->r.top = IMX355_PIXEL_ARRAY_TOP; ++ sel->r.left = IMX355_PIXEL_ARRAY_LEFT; ++ sel->r.width = IMX355_PIXEL_ARRAY_WIDTH; ++ sel->r.height = IMX355_PIXEL_ARRAY_HEIGHT; ++ ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++static int imx355_entity_init_state(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_state *sd_state) ++{ ++ struct v4l2_subdev_format fmt = { }; ++ ++ fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; ++ fmt.format.code = MEDIA_BUS_FMT_SRGGB10_1X10; ++ fmt.format.width = supported_modes[0].width; ++ fmt.format.height = supported_modes[0].height; ++ ++ imx355_set_pad_format(subdev, sd_state, &fmt); + + return 0; + } +@@ -1403,52 +804,98 @@ imx355_set_pad_format(struct v4l2_subdev *sd, + /* Start streaming */ + static int imx355_start_streaming(struct imx355 *imx355) + { +- const struct imx355_reg_list *reg_list; +- int ret; ++ const struct v4l2_mbus_framefmt *fmt; ++ struct v4l2_subdev_state *state; ++ const struct imx355_mode *mode; ++ int lane_idx = imx355->hwcfg->num_lanes == 4 ? 0 : 1; ++ struct v4l2_rect *crop; ++ u64 link_bitrate; ++ u8 binning_mode; ++ int ret = 0; + + /* Global Setting */ +- reg_list = &imx355_global_setting; +- ret = imx355_write_regs(imx355, reg_list->regs, reg_list->num_of_regs); +- if (ret) { +- dev_err(imx355->dev, "failed to set global settings"); +- return ret; +- } ++ cci_multi_reg_write(imx355->regmap, imx355_global_regs, ++ ARRAY_SIZE(imx355_global_regs), &ret); + +- /* Apply default values of current mode */ +- reg_list = &imx355->cur_mode->reg_list; +- ret = imx355_write_regs(imx355, reg_list->regs, reg_list->num_of_regs); +- if (ret) { +- dev_err(imx355->dev, "failed to set mode"); +- return ret; +- } ++ /* Apply values of current mode */ ++ state = v4l2_subdev_get_locked_active_state(&imx355->sd); ++ fmt = v4l2_subdev_state_get_format(state, 0); ++ crop = v4l2_subdev_state_get_crop(state, 0); ++ mode = v4l2_find_nearest_size(supported_modes, ++ ARRAY_SIZE(supported_modes), ++ width, height, fmt->width, fmt->height); ++ cci_multi_reg_write(imx355->regmap, mode->reg_list.regs, ++ mode->reg_list.num_of_regs, &ret); ++ ++ /* Set readout crop and size registers */ ++ cci_write(imx355->regmap, IMX355_REG_X_ADD_START, crop->left, ++ &ret); ++ cci_write(imx355->regmap, IMX355_REG_Y_ADD_START, crop->top, &ret); ++ cci_write(imx355->regmap, IMX355_REG_X_ADD_END, ++ crop->width + crop->left - 1, &ret); ++ cci_write(imx355->regmap, IMX355_REG_Y_ADD_END, ++ crop->height + crop->top - 1, &ret); ++ cci_write(imx355->regmap, IMX355_REG_X_OUT_SIZE, fmt->width, &ret); ++ cci_write(imx355->regmap, IMX355_REG_Y_OUT_SIZE, fmt->height, &ret); ++ ++ binning_mode = ((crop->width / fmt->width) << 4) | ++ (crop->height / fmt->height); ++ cci_write(imx355->regmap, IMX355_REG_BINNING_MODE, ++ binning_mode == 0x11 ? 0x00 : 0x01, &ret); ++ cci_write(imx355->regmap, IMX355_REG_BINNING_TYPE, binning_mode, &ret); ++ cci_write(imx355->regmap, IMX355_REG_BINNING_WEIGHTING, 0x00, &ret); ++ ++ /* Set PLL registers for the external clock frequency */ ++ cci_write(imx355->regmap, IMX355_REG_EXTCLK_FREQ, ++ imx355->clk_params->extclk_freq, &ret); ++ cci_write(imx355->regmap, IMX355_REG_PLL_OP_MUL, ++ imx355->clk_params->pll_op_mpy[lane_idx], &ret); ++ cci_write(imx355->regmap, IMX355_REG_PLL_OP_PREDIV, ++ imx355->clk_params->pll_op_prediv[lane_idx], &ret); ++ cci_write(imx355->regmap, IMX355_REG_PLL_IVT_SYSCK_DIV, ++ lane_idx ? 2 : 1, &ret); ++ ++ /* Set MIPI configuration */ ++ cci_write(imx355->regmap, IMX355_REG_LANE_SEL, ++ imx355->hwcfg->num_lanes - 1, &ret); ++ ++ link_bitrate = imx355->link_freq->qmenu_int[imx355->link_freq->val] * ++ imx355->hwcfg->num_lanes * 2; ++ do_div(link_bitrate, 1000000); ++ cci_write(imx355->regmap, IMX355_REG_REQ_LINK_BIT_RATE, link_bitrate, ++ &ret); + + /* set digital gain control to all color mode */ +- ret = imx355_write_reg(imx355, IMX355_REG_DPGA_USE_GLOBAL_GAIN, 1, 1); +- if (ret) +- return ret; ++ cci_write(imx355->regmap, IMX355_REG_DPGA_USE_GLOBAL_GAIN, 1, &ret); ++ ++ /* set line length */ ++ cci_write(imx355->regmap, IMX355_REG_LLP, ++ imx355->hblank->val + fmt->width, &ret); + + /* Apply customized values from user */ +- ret = __v4l2_ctrl_handler_setup(imx355->sd.ctrl_handler); +- if (ret) +- return ret; ++ if (!ret) ++ ret = __v4l2_ctrl_handler_setup(imx355->sd.ctrl_handler); + +- return imx355_write_reg(imx355, IMX355_REG_MODE_SELECT, +- 1, IMX355_MODE_STREAMING); ++ cci_write(imx355->regmap, IMX355_REG_MODE_SELECT, IMX355_MODE_STREAMING, ++ &ret); ++ ++ return ret; + } + + /* Stop streaming */ + static int imx355_stop_streaming(struct imx355 *imx355) + { +- return imx355_write_reg(imx355, IMX355_REG_MODE_SELECT, +- 1, IMX355_MODE_STANDBY); ++ return cci_write(imx355->regmap, IMX355_REG_MODE_SELECT, ++ IMX355_MODE_STANDBY, NULL); + } + + static int imx355_set_stream(struct v4l2_subdev *sd, int enable) + { + struct imx355 *imx355 = to_imx355(sd); ++ struct v4l2_subdev_state *state; + int ret = 0; + +- mutex_lock(&imx355->mutex); ++ state = v4l2_subdev_lock_and_get_active_state(sd); + + if (enable) { + ret = pm_runtime_resume_and_get(imx355->dev); +@@ -1464,21 +911,21 @@ static int imx355_set_stream(struct v4l2_subdev *sd, int enable) + goto err_rpm_put; + } else { + imx355_stop_streaming(imx355); +- pm_runtime_put(imx355->dev); ++ pm_runtime_put_autosuspend(imx355->dev); + } + + /* vflip and hflip cannot change during streaming */ + __v4l2_ctrl_grab(imx355->vflip, enable); + __v4l2_ctrl_grab(imx355->hflip, enable); + +- mutex_unlock(&imx355->mutex); ++ v4l2_subdev_unlock_state(state); + + return ret; + + err_rpm_put: +- pm_runtime_put(imx355->dev); ++ pm_runtime_put_autosuspend(imx355->dev); + err_unlock: +- mutex_unlock(&imx355->mutex); ++ v4l2_subdev_unlock_state(state); + + return ret; + } +@@ -1487,14 +934,14 @@ static int imx355_set_stream(struct v4l2_subdev *sd, int enable) + static int imx355_identify_module(struct imx355 *imx355) + { + int ret; +- u32 val; ++ u64 val; + +- ret = imx355_read_reg(imx355, IMX355_REG_CHIP_ID, 2, &val); ++ ret = cci_read(imx355->regmap, IMX355_REG_CHIP_ID, &val, NULL); + if (ret) + return ret; + + if (val != IMX355_CHIP_ID) { +- dev_err(imx355->dev, "chip id mismatch: %x!=%x", ++ dev_err(imx355->dev, "chip id mismatch: %x!=%llx", + IMX355_CHIP_ID, val); + return -EIO; + } +@@ -1512,9 +959,10 @@ static const struct v4l2_subdev_video_ops imx355_video_ops = { + + static const struct v4l2_subdev_pad_ops imx355_pad_ops = { + .enum_mbus_code = imx355_enum_mbus_code, +- .get_fmt = imx355_get_pad_format, ++ .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = imx355_set_pad_format, + .enum_frame_size = imx355_enum_frame_size, ++ .get_selection = imx355_get_selection, + }; + + static const struct v4l2_subdev_ops imx355_subdev_ops = { +@@ -1528,7 +976,7 @@ static const struct media_entity_operations imx355_subdev_entity_ops = { + }; + + static const struct v4l2_subdev_internal_ops imx355_internal_ops = { +- .open = imx355_open, ++ .init_state = imx355_entity_init_state, + }; + + static int imx355_power_off(struct device *dev) +@@ -1582,13 +1030,11 @@ static int imx355_init_controls(struct imx355 *imx355) + { + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl_handler *ctrl_hdlr; ++ const struct imx355_mode *mode = &supported_modes[0]; + s64 exposure_max; + s64 vblank_def; +- s64 vblank_min; + s64 hblank; + u64 pixel_rate; +- const struct imx355_mode *mode; +- u32 max; + int ret; + + ctrl_hdlr = &imx355->ctrl_handler; +@@ -1596,40 +1042,34 @@ static int imx355_init_controls(struct imx355 *imx355) + if (ret) + return ret; + +- ctrl_hdlr->lock = &imx355->mutex; +- max = ARRAY_SIZE(link_freq_menu_items) - 1; + imx355->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx355_ctrl_ops, +- V4L2_CID_LINK_FREQ, max, 0, +- link_freq_menu_items); ++ V4L2_CID_LINK_FREQ, 0, 0, ++ &imx355->hwcfg->link_freq_menu); + if (imx355->link_freq) + imx355->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + /* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ +- pixel_rate = IMX355_LINK_FREQ_DEFAULT * 2 * 4; ++ pixel_rate = imx355->hwcfg->link_freq_menu * 2 * imx355->hwcfg->num_lanes; + do_div(pixel_rate, 10); +- /* By default, PIXEL_RATE is read only */ +- imx355->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, +- V4L2_CID_PIXEL_RATE, pixel_rate, +- pixel_rate, 1, pixel_rate); ++ ++ v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, V4L2_CID_PIXEL_RATE, ++ pixel_rate, pixel_rate, 1, pixel_rate); + + /* Initialize vblank/hblank/exposure parameters based on current mode */ +- mode = imx355->cur_mode; + vblank_def = mode->fll_def - mode->height; +- vblank_min = mode->fll_min - mode->height; + imx355->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, +- V4L2_CID_VBLANK, vblank_min, ++ V4L2_CID_VBLANK, IMX355_VBLANK_MIN, + IMX355_FLL_MAX - mode->height, + 1, vblank_def); + + hblank = mode->llp - mode->width; +- imx355->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, +- V4L2_CID_HBLANK, hblank, hblank, +- 1, hblank); ++ imx355->hblank = v4l2_ctrl_new_std(ctrl_hdlr, NULL, V4L2_CID_HBLANK, ++ hblank, hblank, 1, hblank); + if (imx355->hblank) + imx355->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + /* fll >= exposure time + adjust parameter (default value is 10) */ +- exposure_max = mode->fll_def - 10; ++ exposure_max = mode->fll_def - IMX355_EXPOSURE_OFFSET; + imx355->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, + V4L2_CID_EXPOSURE, + IMX355_EXPOSURE_MIN, exposure_max, +@@ -1683,14 +1123,17 @@ static int imx355_init_controls(struct imx355 *imx355) + return ret; + } + +-static struct imx355_hwcfg *imx355_get_hwcfg(struct device *dev) ++static struct imx355_hwcfg *imx355_get_hwcfg(struct imx355 *imx355) + { ++ struct device *dev = imx355->dev; + struct imx355_hwcfg *cfg; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; ++ const struct imx355_clk_params *clk = imx355->clk_params; + struct fwnode_handle *ep; + struct fwnode_handle *fwnode = dev_fwnode(dev); ++ int lane_idx; + int ret; + + if (!fwnode) +@@ -1708,13 +1151,18 @@ static struct imx355_hwcfg *imx355_get_hwcfg(struct device *dev) + if (!cfg) + goto out_err; + +- if (bus_cfg.bus.mipi_csi2.num_data_lanes != IMX355_DATA_LANES) ++ if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2 && ++ bus_cfg.bus.mipi_csi2.num_data_lanes != 4) + goto out_err; + ++ cfg->num_lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; ++ ++ lane_idx = cfg->num_lanes == 4 ? 0 : 1; ++ cfg->link_freq_menu = (clk->ext_clk * clk->pll_op_mpy[lane_idx]) / ++ (clk->pll_op_prediv[lane_idx] * 2); + ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, +- link_freq_menu_items, +- ARRAY_SIZE(link_freq_menu_items), ++ &cfg->link_freq_menu, 1, + &cfg->link_freq_bitmap); + if (ret) + goto out_err; +@@ -1741,7 +1189,10 @@ static int imx355_probe(struct i2c_client *client) + + imx355->dev = &client->dev; + +- mutex_init(&imx355->mutex); ++ imx355->regmap = devm_cci_regmap_init_i2c(client, 16); ++ if (IS_ERR(imx355->regmap)) ++ return dev_err_probe(imx355->dev, PTR_ERR(imx355->regmap), ++ "Unable to initialize I2C\n"); + + imx355->clk = devm_v4l2_sensor_clk_get(imx355->dev, NULL); + if (IS_ERR(imx355->clk)) +@@ -1749,7 +1200,13 @@ static int imx355_probe(struct i2c_client *client) + "failed to get clock\n"); + + freq = clk_get_rate(imx355->clk); +- if (freq != IMX355_EXT_CLK) ++ for (unsigned int i = 0; i < ARRAY_SIZE(imx355_clk_params); i++) { ++ if (freq == imx355_clk_params[i].ext_clk) { ++ imx355->clk_params = &imx355_clk_params[i]; ++ break; ++ } ++ } ++ if (!imx355->clk_params) + return dev_err_probe(imx355->dev, -EINVAL, + "external clock %lu is not supported\n", + freq); +@@ -1760,7 +1217,7 @@ static int imx355_probe(struct i2c_client *client) + &imx355->supplies); + if (ret) { + dev_err_probe(imx355->dev, ret, "could not get regulators"); +- goto error_probe; ++ return ret; + } + + imx355->reset_gpio = devm_gpiod_get_optional(imx355->dev, "reset", +@@ -1768,22 +1225,21 @@ static int imx355_probe(struct i2c_client *client) + if (IS_ERR(imx355->reset_gpio)) { + ret = dev_err_probe(imx355->dev, PTR_ERR(imx355->reset_gpio), + "failed to get gpios"); +- goto error_probe; ++ return ret; + } + + /* Initialize subdev */ + v4l2_i2c_subdev_init(&imx355->sd, client, &imx355_subdev_ops); + +- imx355->hwcfg = imx355_get_hwcfg(imx355->dev); ++ imx355->hwcfg = imx355_get_hwcfg(imx355); + if (!imx355->hwcfg) { + dev_err(imx355->dev, "failed to get hwcfg"); +- ret = -ENODEV; +- goto error_probe; ++ return -ENODEV; + } + + ret = imx355_power_on(imx355->dev); + if (ret) +- goto error_probe; ++ return ret; + + /* Check module identity */ + ret = imx355_identify_module(imx355); +@@ -1792,9 +1248,6 @@ static int imx355_probe(struct i2c_client *client) + goto error_power_off; + } + +- /* Set default mode to max resolution */ +- imx355->cur_mode = &supported_modes[0]; +- + ret = imx355_init_controls(imx355); + if (ret) { + dev_err(imx355->dev, "failed to init controls: %d", ret); +@@ -1816,24 +1269,36 @@ static int imx355_probe(struct i2c_client *client) + goto error_handler_free; + } + ++ imx355->sd.state_lock = imx355->ctrl_handler.lock; ++ ret = v4l2_subdev_init_finalize(&imx355->sd); ++ if (ret < 0) { ++ dev_err_probe(imx355->dev, ret, "subdev init error\n"); ++ goto error_media_entity_free; ++ } ++ + /* + * Device is already turned on by i2c-core with ACPI domain PM. + * Enable runtime PM and turn off the device. + */ + pm_runtime_set_active(imx355->dev); + pm_runtime_enable(imx355->dev); ++ pm_runtime_set_autosuspend_delay(imx355->dev, 1000); ++ pm_runtime_use_autosuspend(imx355->dev); + + ret = v4l2_async_register_subdev_sensor(&imx355->sd); + if (ret < 0) +- goto error_media_entity_runtime_pm; ++ goto error_subdev_cleanup_runtime_pm; + + pm_runtime_idle(imx355->dev); + + return 0; + +-error_media_entity_runtime_pm: ++error_subdev_cleanup_runtime_pm: + pm_runtime_disable(imx355->dev); + pm_runtime_set_suspended(imx355->dev); ++ pm_runtime_dont_use_autosuspend(imx355->dev); ++ v4l2_subdev_cleanup(&imx355->sd); ++error_media_entity_free: + media_entity_cleanup(&imx355->sd.entity); + + error_handler_free: +@@ -1842,9 +1307,6 @@ static int imx355_probe(struct i2c_client *client) + error_power_off: + imx355_power_off(imx355->dev); + +-error_probe: +- mutex_destroy(&imx355->mutex); +- + return ret; + } + +@@ -1854,6 +1316,7 @@ static void imx355_remove(struct i2c_client *client) + struct imx355 *imx355 = to_imx355(sd); + + v4l2_async_unregister_subdev(sd); ++ v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + +@@ -1864,7 +1327,7 @@ static void imx355_remove(struct i2c_client *client) + pm_runtime_set_suspended(imx355->dev); + } + +- mutex_destroy(&imx355->mutex); ++ pm_runtime_dont_use_autosuspend(imx355->dev); + } + + static const struct acpi_device_id imx355_acpi_ids[] __maybe_unused = { +diff --git a/drivers/media/i2c/imx471.c b/drivers/media/i2c/imx471.c +new file mode 100644 +--- /dev/null ++++ b/drivers/media/i2c/imx471.c +@@ -0,0 +1,957 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * imx471.c - imx471 sensor driver ++ * ++ * Copyright (C) 2025 Intel Corporation ++ * Copyright (C) 2026 Kate Hsuan ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define IMX471_REG_MODE_SELECT CCI_REG8(0x0100) ++#define IMX471_MODE_STANDBY 0x00 ++#define IMX471_MODE_STREAMING 0x01 ++ ++/* Chip ID */ ++#define IMX471_REG_CHIP_ID CCI_REG16(0x0016) ++#define IMX471_CHIP_ID 0x0471 ++ ++/* V_TIMING internal */ ++#define IMX471_REG_FLL CCI_REG16(0x0340) ++#define IMX471_FLL_MAX 0xffff ++ ++/* Exposure control */ ++#define IMX471_REG_EXPOSURE CCI_REG16(0x0202) ++#define IMX471_EXPOSURE_MIN 1 ++#define IMX471_EXPOSURE_STEP 1 ++#define IMX471_EXPOSURE_DEFAULT 1270 ++ ++/* Default exposure margin */ ++#define IMX471_EXPOSURE_MARGIN 18 ++ ++/* Analog gain control */ ++#define IMX471_REG_ANALOG_GAIN CCI_REG16(0x0204) ++#define IMX471_ANA_GAIN_MIN 0 ++#define IMX471_ANA_GAIN_MAX 800 ++#define IMX471_ANA_GAIN_STEP 1 ++#define IMX471_ANA_GAIN_DEFAULT 0 ++ ++/* Digital gain control */ ++#define IMX471_REG_DPGA_USE_GLOBAL_GAIN CCI_REG16(0x3ff9) ++#define IMX471_REG_DIG_GAIN_GLOBAL CCI_REG16(0x020e) ++#define IMX471_DGTL_GAIN_MIN 256 ++#define IMX471_DGTL_GAIN_MAX 4095 ++#define IMX471_DGTL_GAIN_STEP 1 ++#define IMX471_DGTL_GAIN_DEFAULT 256 ++ ++/* HFLIP and VFLIP control */ ++#define IMX471_REG_ORIENTATION CCI_REG8(0x0101) ++ ++/* Test Pattern Control */ ++#define IMX471_REG_TEST_PATTERN CCI_REG8(0x0600) ++ ++/* default link frequency and external clock */ ++#define IMX471_LINK_FREQ_DEFAULT 200000000LL ++#define IMX471_EXT_CLK 19200000 ++ ++/* PLL */ ++#define IMX471_REG_VTPXCK_DIV CCI_REG8(0x0301) ++#define IMX471_REG_VTSYCK_DIV CCI_REG8(0x0303) ++#define IMX471_REG_PREPLLCK_VT_DIV CCI_REG8(0x0305) ++#define IMX471_REG_PLL_VT_MPY CCI_REG16(0x0306) ++#define IMX471_REG_OPPXCK_DIV CCI_REG8(0x0309) ++#define IMX471_REG_OPSYCK_DIV CCI_REG8(0x030b) ++#define IMX471_REG_PLL_MULT_DRIV CCI_REG8(0x0310) ++#define IMX471_PLL_SINGLE 0 ++#define IMX471_PLL_DUAL 1 ++ ++/* IMX471 native and active pixel array size */ ++#define IMX471_NATIVE_WIDTH 4672 ++#define IMX471_NATIVE_HEIGHT 3512 ++#define IMX471_PIXEL_ARRAY_LEFT 8 ++#define IMX471_PIXEL_ARRAY_TOP 8 ++#define IMX471_PIXEL_ARRAY_WIDTH 4656 ++#define IMX471_PIXEL_ARRAY_HEIGHT 3496 ++ ++#define IMX471_REG_EXCK_FREQ CCI_REG16(0x0136) ++ ++#define IMX471_REG_CSI_DATA_FORMAT CCI_REG16(0x0112) ++#define IMX471_CSI_DATA_FORMAT_RAW10 0x0a0a ++ ++#define IMX471_REG_CSI_LANE_MODE CCI_REG8(0x0114) ++#define IMX471_CSI_2_LANE_MODE 1 ++#define IMX471_CSI_4_LANE_MODE 3 ++ ++#define IMX471_REG_X_ADD_STA CCI_REG16(0x0344) ++#define IMX471_REG_Y_ADD_STA CCI_REG16(0x0346) ++#define IMX471_REG_X_ADD_END CCI_REG16(0x0348) ++#define IMX471_REG_Y_ADD_END CCI_REG16(0x034a) ++#define IMX471_REG_X_OUTPUT_SIZE CCI_REG16(0x034c) ++#define IMX471_REG_Y_OUTPUT_SIZE CCI_REG16(0x034e) ++#define IMX471_REG_X_EVEN_INC CCI_REG8(0x0381) ++#define IMX471_REG_X_ODD_INC CCI_REG8(0x0383) ++#define IMX471_REG_Y_EVEN_INC CCI_REG8(0x0385) ++#define IMX471_REG_Y_ODD_INC CCI_REG8(0x0387) ++ ++#define IMX471_REG_DIG_CROP_X_OFFSET CCI_REG16(0x0408) ++#define IMX471_REG_DIG_CROP_Y_OFFSET CCI_REG16(0x040a) ++#define IMX471_REG_DIG_CROP_WIDTH CCI_REG16(0x040c) ++#define IMX471_REG_DIG_CROP_HEIGHT CCI_REG16(0x040e) ++ ++/* Binning mode */ ++#define IMX471_REG_BINNING_MODE CCI_REG8(0x0900) ++#define IMX471_BINNING_NONE 0 ++#define IMX471_BINNING_ENABLE 1 ++#define IMX471_REG_BINNING_TYPE CCI_REG8(0x0901) ++#define IMX471_REG_BINNING_WEIGHTING CCI_REG8(0x0902) ++ ++#define to_imx471(_sd) container_of_const(_sd, struct imx471, sd) ++ ++static const char * const imx471_supply_name[] = { ++ "vana", ++}; ++ ++struct imx471_mode { ++ u32 width; ++ u32 height; ++ ++ /* V-timing */ ++ u32 fll_def; ++ u32 fll_min; ++ ++ /* H-timing */ ++ u32 llp; ++ ++ const struct cci_reg_sequence *default_mode_regs; ++ unsigned int default_mode_regs_length; ++}; ++ ++struct imx471 { ++ struct v4l2_subdev sd; ++ struct media_pad pad; ++ ++ struct v4l2_ctrl_handler ctrl_handler; ++ struct v4l2_ctrl *vblank; ++ struct v4l2_ctrl *hblank; ++ struct v4l2_ctrl *vflip; ++ struct v4l2_ctrl *hflip; ++ struct v4l2_ctrl *exposure; ++ ++ struct gpio_desc *reset_gpio; ++ struct regulator_bulk_data supplies[ARRAY_SIZE(imx471_supply_name)]; ++ struct clk *img_clk; ++ ++ struct device *dev; ++ struct regmap *regmap; ++}; ++ ++static const struct cci_reg_sequence imx471_global_regs[] = { ++ { IMX471_REG_EXCK_FREQ, 0x1333 }, ++ { CCI_REG8(0x3c7e), 0x08 }, ++ { CCI_REG8(0x3c7f), 0x05 }, ++ { CCI_REG8(0x3e35), 0x00 }, ++ { CCI_REG8(0x3e36), 0x00 }, ++ { CCI_REG8(0x3e37), 0x00 }, ++ { CCI_REG8(0x3f7f), 0x01 }, ++ { CCI_REG8(0x4431), 0x04 }, ++ { CCI_REG8(0x531c), 0x01 }, ++ { CCI_REG8(0x531d), 0x02 }, ++ { CCI_REG8(0x531e), 0x04 }, ++ { CCI_REG8(0x5928), 0x00 }, ++ { CCI_REG8(0x5929), 0x2f }, ++ { CCI_REG8(0x592a), 0x00 }, ++ { CCI_REG8(0x592b), 0x85 }, ++ { CCI_REG8(0x592c), 0x00 }, ++ { CCI_REG8(0x592d), 0x32 }, ++ { CCI_REG8(0x592e), 0x00 }, ++ { CCI_REG8(0x592f), 0x88 }, ++ { CCI_REG8(0x5930), 0x00 }, ++ { CCI_REG8(0x5931), 0x3d }, ++ { CCI_REG8(0x5932), 0x00 }, ++ { CCI_REG8(0x5933), 0x93 }, ++ { CCI_REG8(0x5938), 0x00 }, ++ { CCI_REG8(0x5939), 0x24 }, ++ { CCI_REG8(0x593a), 0x00 }, ++ { CCI_REG8(0x593b), 0x7a }, ++ { CCI_REG8(0x593c), 0x00 }, ++ { CCI_REG8(0x593d), 0x24 }, ++ { CCI_REG8(0x593e), 0x00 }, ++ { CCI_REG8(0x593f), 0x7a }, ++ { CCI_REG8(0x5940), 0x00 }, ++ { CCI_REG8(0x5941), 0x2f }, ++ { CCI_REG8(0x5942), 0x00 }, ++ { CCI_REG8(0x5943), 0x85 }, ++ { CCI_REG8(0x5f0e), 0x6e }, ++ { CCI_REG8(0x5f11), 0xc6 }, ++ { CCI_REG8(0x5f17), 0x5e }, ++ { CCI_REG8(0x7990), 0x01 }, ++ { CCI_REG8(0x7993), 0x5d }, ++ { CCI_REG8(0x7994), 0x5d }, ++ { CCI_REG8(0x7995), 0xa1 }, ++ { CCI_REG8(0x799a), 0x01 }, ++ { CCI_REG8(0x799d), 0x00 }, ++ { CCI_REG8(0x8169), 0x01 }, ++ { CCI_REG8(0x8359), 0x01 }, ++ { CCI_REG8(0x9302), 0x1e }, ++ { CCI_REG8(0x9306), 0x1f }, ++ { CCI_REG8(0x930a), 0x26 }, ++ { CCI_REG8(0x930e), 0x23 }, ++ { CCI_REG8(0x9312), 0x23 }, ++ { CCI_REG8(0x9316), 0x2c }, ++ { CCI_REG8(0x9317), 0x19 }, ++ { CCI_REG8(0xb046), 0x01 }, ++ { CCI_REG8(0xb048), 0x01 }, ++}; ++ ++static const struct cci_reg_sequence mode_1928x1088_regs[] = { ++ { IMX471_REG_CSI_DATA_FORMAT, IMX471_CSI_DATA_FORMAT_RAW10 }, ++ { IMX471_REG_CSI_LANE_MODE, IMX471_CSI_4_LANE_MODE }, ++ { IMX471_REG_X_ADD_STA, 8 }, ++ { IMX471_REG_Y_ADD_STA, 408 }, ++ { IMX471_REG_X_ADD_END, 4647 }, ++ { IMX471_REG_Y_ADD_END, 3051 }, ++ { IMX471_REG_X_EVEN_INC, 1 }, ++ { IMX471_REG_X_ODD_INC, 1 }, ++ { IMX471_REG_Y_EVEN_INC, 1 }, ++ { IMX471_REG_Y_ODD_INC, 1 }, ++ { IMX471_REG_BINNING_MODE, IMX471_BINNING_ENABLE }, ++ { IMX471_REG_BINNING_TYPE, 0x22 }, ++ { IMX471_REG_BINNING_WEIGHTING, 0x08 }, ++ { IMX471_REG_DIG_CROP_X_OFFSET, 208 }, ++ { IMX471_REG_DIG_CROP_Y_OFFSET, 108 }, ++ { IMX471_REG_DIG_CROP_WIDTH, 1928 }, ++ { IMX471_REG_DIG_CROP_HEIGHT, 1088 }, ++ { IMX471_REG_X_OUTPUT_SIZE, 1928 }, ++ { IMX471_REG_Y_OUTPUT_SIZE, 1088 }, ++ { IMX471_REG_VTPXCK_DIV, 0x06 }, ++ { IMX471_REG_VTSYCK_DIV, 0x02 }, ++ { IMX471_REG_PREPLLCK_VT_DIV, 0x02 }, ++ { IMX471_REG_PLL_VT_MPY, 0x0079 }, ++ { IMX471_REG_OPSYCK_DIV, 0x01 }, ++ { CCI_REG8(0x030d), 0x02 }, ++ { CCI_REG8(0x030e), 0x00 }, ++ { CCI_REG8(0x030f), 0x53 }, ++ { IMX471_REG_PLL_MULT_DRIV, IMX471_PLL_DUAL }, ++ { CCI_REG8(0x3f4c), 0x81 }, ++ { CCI_REG8(0x3f4d), 0x81 }, ++ { CCI_REG8(0x3f78), 0x01 }, ++ { CCI_REG8(0x3f79), 0x31 }, ++ { CCI_REG8(0x3ffe), 0x00 }, ++ { CCI_REG8(0x3fff), 0x8a }, ++ { CCI_REG8(0x5f0a), 0xb6 }, ++}; ++ ++static const char * const imx471_test_pattern_menu[] = { ++ "Disabled", ++ "Solid Colour", ++ "Eight Vertical Colour Bars", ++ "Colour Bars With Fade to Grey", ++ "Pseudorandom Sequence (PN9)", ++}; ++ ++static const s64 link_freq_menu_items[] = { ++ IMX471_LINK_FREQ_DEFAULT, ++}; ++ ++/* ++ * The Bayer formats for the flipping. ++ * - no flip ++ * - h flip ++ * - v flip ++ * - h and v flips ++ */ ++static const u32 imx471_hv_flips_bayer_order[] = { ++ MEDIA_BUS_FMT_SRGGB10_1X10, ++ MEDIA_BUS_FMT_SGRBG10_1X10, ++ MEDIA_BUS_FMT_SGBRG10_1X10, ++ MEDIA_BUS_FMT_SBGGR10_1X10, ++}; ++ ++static const struct imx471_mode imx471_modes[] = { ++ { ++ .width = 1928, ++ .height = 1088, ++ .fll_def = 1308, ++ .fll_min = 1308, ++ .llp = 2328, ++ .default_mode_regs = mode_1928x1088_regs, ++ .default_mode_regs_length = ARRAY_SIZE(mode_1928x1088_regs), ++ }, ++}; ++ ++static int imx471_get_regulators(struct device *dev, struct imx471 *sensor) ++{ ++ for (unsigned int i = 0; i < ARRAY_SIZE(imx471_supply_name); i++) ++ sensor->supplies[i].supply = imx471_supply_name[i]; ++ ++ return devm_regulator_bulk_get(dev, ARRAY_SIZE(imx471_supply_name), ++ sensor->supplies); ++} ++ ++static int imx471_set_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct imx471 *sensor = container_of_const(ctrl->handler, ++ struct imx471, ++ ctrl_handler); ++ struct v4l2_subdev_state *state = ++ v4l2_subdev_get_locked_active_state(&sensor->sd); ++ const struct v4l2_mbus_framefmt *format = ++ v4l2_subdev_state_get_format(state, 0); ++ int ret; ++ ++ if (ctrl->id == V4L2_CID_VBLANK) { ++ s64 exposure_max = format->height + ctrl->val - ++ IMX471_EXPOSURE_MARGIN; ++ ret = __v4l2_ctrl_modify_range(sensor->exposure, ++ sensor->exposure->minimum, ++ exposure_max, ++ sensor->exposure->step, ++ exposure_max); ++ if (ret) ++ return ret; ++ } ++ ++ if (!pm_runtime_get_if_in_use(sensor->dev)) ++ return 0; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_ANALOGUE_GAIN: ++ ret = cci_write(sensor->regmap, IMX471_REG_ANALOG_GAIN, ++ ctrl->val, NULL); ++ break; ++ case V4L2_CID_DIGITAL_GAIN: ++ ret = cci_write(sensor->regmap, IMX471_REG_DIG_GAIN_GLOBAL, ++ ctrl->val, NULL); ++ break; ++ case V4L2_CID_EXPOSURE: ++ ret = cci_write(sensor->regmap, IMX471_REG_EXPOSURE, ++ ctrl->val, NULL); ++ break; ++ case V4L2_CID_VBLANK: ++ /* Update FLL that meets expected vertical blanking */ ++ ret = cci_write(sensor->regmap, IMX471_REG_FLL, ++ format->height + ctrl->val, NULL); ++ break; ++ case V4L2_CID_TEST_PATTERN: ++ ret = cci_write(sensor->regmap, IMX471_REG_TEST_PATTERN, ++ ctrl->val, NULL); ++ break; ++ case V4L2_CID_HFLIP: ++ case V4L2_CID_VFLIP: ++ ret = cci_write(sensor->regmap, IMX471_REG_ORIENTATION, ++ sensor->hflip->val | sensor->vflip->val << 1, ++ NULL); ++ break; ++ default: ++ ret = -EINVAL; ++ dev_err(sensor->dev, "ctrl(id:0x%x,val:0x%x) is not handled\n", ++ ctrl->id, ctrl->val); ++ break; ++ } ++ ++ pm_runtime_put(sensor->dev); ++ ++ return ret; ++} ++ ++static const struct v4l2_ctrl_ops imx471_ctrl_ops = { ++ .s_ctrl = imx471_set_ctrl, ++}; ++ ++static u32 imx471_get_format_code(struct imx471 *sensor) ++{ ++ unsigned int i; ++ ++ i = (sensor->vflip->val ? 2 : 0) | (sensor->hflip->val ? 1 : 0); ++ ++ return imx471_hv_flips_bayer_order[i]; ++} ++ ++static int imx471_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ struct imx471 *sensor = to_imx471(sd); ++ ++ if (code->index >= (ARRAY_SIZE(imx471_hv_flips_bayer_order) / 4)) ++ return -EINVAL; ++ ++ code->code = imx471_get_format_code(sensor); ++ ++ return 0; ++} ++ ++static int imx471_enum_frame_size(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_frame_size_enum *fse) ++{ ++ if (fse->index >= ARRAY_SIZE(imx471_modes)) ++ return -EINVAL; ++ ++ fse->min_width = imx471_modes[fse->index].width; ++ fse->max_width = fse->min_width; ++ fse->min_height = imx471_modes[fse->index].height; ++ fse->max_height = fse->min_height; ++ ++ return 0; ++} ++ ++static void imx471_update_pad_format(struct imx471 *sensor, ++ const struct imx471_mode *mode, ++ struct v4l2_subdev_format *fmt) ++{ ++ fmt->format.code = imx471_get_format_code(sensor); ++ fmt->format.width = mode->width; ++ fmt->format.height = mode->height; ++ fmt->format.field = V4L2_FIELD_NONE; ++} ++ ++static int imx471_set_pad_format(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct imx471 *sensor = to_imx471(sd); ++ const struct imx471_mode *mode; ++ int h_blank, ret; ++ ++ mode = v4l2_find_nearest_size(imx471_modes, ARRAY_SIZE(imx471_modes), ++ width, height, fmt->format.width, ++ fmt->format.height); ++ ++ imx471_update_pad_format(sensor, mode, fmt); ++ ++ *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; ++ ++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) ++ return 0; ++ ++ if (media_entity_is_streaming(&sensor->sd.entity)) ++ return -EBUSY; ++ ++ ret = __v4l2_ctrl_modify_range(sensor->vblank, ++ mode->fll_min - mode->height, ++ IMX471_FLL_MAX - mode->height, ++ 1, ++ mode->fll_def - mode->height); ++ if (ret) ++ return ret; ++ ++ h_blank = mode->llp - mode->width; ++ /* ++ * Currently hblank is not changeable. ++ * So FPS control is done only by vblank. ++ */ ++ return __v4l2_ctrl_modify_range(sensor->hblank, h_blank, ++ h_blank, 1, h_blank); ++} ++ ++static int imx471_get_selection(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_selection *sel) ++{ ++ switch (sel->target) { ++ case V4L2_SEL_TGT_CROP: ++ sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); ++ break; ++ ++ case V4L2_SEL_TGT_NATIVE_SIZE: ++ sel->r.top = 0; ++ sel->r.left = 0; ++ sel->r.width = IMX471_NATIVE_WIDTH; ++ sel->r.height = IMX471_NATIVE_HEIGHT; ++ return 0; ++ ++ case V4L2_SEL_TGT_CROP_DEFAULT: ++ case V4L2_SEL_TGT_CROP_BOUNDS: ++ sel->r.top = IMX471_PIXEL_ARRAY_TOP; ++ sel->r.left = IMX471_PIXEL_ARRAY_LEFT; ++ sel->r.width = IMX471_PIXEL_ARRAY_WIDTH; ++ sel->r.height = IMX471_PIXEL_ARRAY_HEIGHT; ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++static int imx471_init_state(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state) ++{ ++ struct v4l2_subdev_format fmt = { ++ .which = V4L2_SUBDEV_FORMAT_TRY, ++ .format = { ++ .code = MEDIA_BUS_FMT_SRGGB10_1X10, ++ .width = imx471_modes[0].width, ++ .height = imx471_modes[0].height, ++ }, ++ }; ++ ++ return imx471_set_pad_format(sd, sd_state, &fmt); ++} ++ ++static int imx471_identify_module(struct imx471 *sensor) ++{ ++ int ret; ++ u64 val; ++ ++ ret = cci_read(sensor->regmap, IMX471_REG_CHIP_ID, &val, NULL); ++ if (ret) ++ return dev_err_probe(sensor->dev, ret, ++ "failed to read chip id\n"); ++ ++ if (val != IMX471_CHIP_ID) ++ return dev_err_probe(sensor->dev, -EIO, ++ "chip id mismatch: %x!=%llx\n", ++ IMX471_CHIP_ID, val); ++ ++ return 0; ++} ++ ++static int imx471_power_off(struct device *dev) ++{ ++ struct v4l2_subdev *sd = dev_get_drvdata(dev); ++ struct imx471 *sensor = to_imx471(sd); ++ ++ clk_disable_unprepare(sensor->img_clk); ++ gpiod_set_value_cansleep(sensor->reset_gpio, 1); ++ ++ regulator_bulk_disable(ARRAY_SIZE(imx471_supply_name), ++ sensor->supplies); ++ ++ return 0; ++} ++ ++static int imx471_power_on(struct device *dev) ++{ ++ struct v4l2_subdev *sd = dev_get_drvdata(dev); ++ struct imx471 *sensor = to_imx471(sd); ++ int ret; ++ ++ ret = regulator_bulk_enable(ARRAY_SIZE(imx471_supply_name), ++ sensor->supplies); ++ if (ret < 0) { ++ dev_err(dev, "failed to enable regulators: %d\n", ret); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(sensor->img_clk); ++ if (ret < 0) { ++ regulator_bulk_disable(ARRAY_SIZE(imx471_supply_name), ++ sensor->supplies); ++ dev_err(dev, "failed to enable imaging clock: %d\n", ret); ++ return ret; ++ } ++ ++ gpiod_set_value_cansleep(sensor->reset_gpio, 0); ++ ++ usleep_range(10000, 15000); ++ ++ return 0; ++} ++ ++static int imx471_enable_stream(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *state, ++ u32 pad, u64 streams_mask) ++{ ++ struct imx471 *sensor = to_imx471(sd); ++ const struct imx471_mode *mode; ++ struct v4l2_mbus_framefmt *fmt; ++ int ret; ++ ++ ret = pm_runtime_resume_and_get(sensor->dev); ++ if (ret) ++ return ret; ++ ++ ret = imx471_identify_module(sensor); ++ if (ret) ++ goto error_powerdown; ++ ++ ret = cci_multi_reg_write(sensor->regmap, imx471_global_regs, ++ ARRAY_SIZE(imx471_global_regs), NULL); ++ if (ret) { ++ dev_err(sensor->dev, "failed to set global settings: %d\n", ++ ret); ++ goto error_powerdown; ++ } ++ ++ fmt = v4l2_subdev_state_get_format(state, 0); ++ mode = v4l2_find_nearest_size(imx471_modes, ARRAY_SIZE(imx471_modes), ++ width, height, fmt->width, fmt->height); ++ ++ ret = cci_multi_reg_write(sensor->regmap, mode->default_mode_regs, ++ mode->default_mode_regs_length, NULL); ++ if (ret) { ++ dev_err(sensor->dev, "failed to set mode: %d\n", ret); ++ goto error_powerdown; ++ } ++ ++ ret = cci_write(sensor->regmap, IMX471_REG_DPGA_USE_GLOBAL_GAIN, 1, ++ NULL); ++ if (ret) ++ goto error_powerdown; ++ ++ ret = __v4l2_ctrl_handler_setup(&sensor->ctrl_handler); ++ if (ret) ++ goto error_powerdown; ++ ++ ret = cci_write(sensor->regmap, IMX471_REG_MODE_SELECT, ++ IMX471_MODE_STREAMING, NULL); ++ if (ret) ++ goto error_powerdown; ++ ++ __v4l2_ctrl_grab(sensor->vflip, true); ++ __v4l2_ctrl_grab(sensor->hflip, true); ++ ++ return ret; ++ ++error_powerdown: ++ pm_runtime_put(sensor->dev); ++ ++ return ret; ++} ++ ++static int imx471_disable_stream(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *state, ++ u32 pad, u64 streams_mask) ++{ ++ struct imx471 *sensor = to_imx471(sd); ++ int ret; ++ ++ ret = cci_write(sensor->regmap, IMX471_REG_MODE_SELECT, ++ IMX471_MODE_STANDBY, NULL); ++ pm_runtime_put(sensor->dev); ++ ++ if (ret) ++ dev_err(sensor->dev, ++ "failed to disable stream with return value: %d\n", ++ ret); ++ ++ __v4l2_ctrl_grab(sensor->vflip, false); ++ __v4l2_ctrl_grab(sensor->hflip, false); ++ ++ return 0; ++} ++ ++static const struct v4l2_subdev_video_ops imx471_video_ops = { ++ .s_stream = v4l2_subdev_s_stream_helper, ++}; ++ ++static const struct v4l2_subdev_pad_ops imx471_pad_ops = { ++ .enum_mbus_code = imx471_enum_mbus_code, ++ .get_fmt = v4l2_subdev_get_fmt, ++ .set_fmt = imx471_set_pad_format, ++ .get_selection = imx471_get_selection, ++ .enum_frame_size = imx471_enum_frame_size, ++ .enable_streams = imx471_enable_stream, ++ .disable_streams = imx471_disable_stream, ++}; ++ ++static const struct v4l2_subdev_ops imx471_subdev_ops = { ++ .video = &imx471_video_ops, ++ .pad = &imx471_pad_ops, ++}; ++ ++static const struct v4l2_subdev_internal_ops imx471_internal_ops = { ++ .init_state = imx471_init_state, ++}; ++ ++static int imx471_init_controls(struct imx471 *sensor) ++{ ++ const struct imx471_mode *mode = &imx471_modes[0]; ++ struct v4l2_fwnode_device_properties props; ++ struct v4l2_ctrl_handler *ctrl_hdlr; ++ struct v4l2_ctrl *link_freq; ++ s64 exposure_max, hblank; ++ u64 pixel_rate; ++ int ret; ++ ++ ret = v4l2_fwnode_device_parse(sensor->dev, &props); ++ if (ret) { ++ dev_err(sensor->dev, "failed to parse fwnode: %d\n", ret); ++ return ret; ++ } ++ ++ ctrl_hdlr = &sensor->ctrl_handler; ++ v4l2_ctrl_handler_init(ctrl_hdlr, 12); ++ ++ v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx471_ctrl_ops, &props); ++ ++ link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, ++ &imx471_ctrl_ops, ++ V4L2_CID_LINK_FREQ, ++ ARRAY_SIZE(link_freq_menu_items) - 1, ++ 0, ++ link_freq_menu_items); ++ ++ /* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ ++ pixel_rate = div_u64(IMX471_LINK_FREQ_DEFAULT * 2 * 4, 10); ++ ++ v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, ++ V4L2_CID_PIXEL_RATE, pixel_rate, ++ pixel_rate, 1, pixel_rate); ++ ++ sensor->vblank = v4l2_ctrl_new_std(ctrl_hdlr, ++ &imx471_ctrl_ops, ++ V4L2_CID_VBLANK, ++ mode->fll_min - mode->height, ++ IMX471_FLL_MAX - mode->height, ++ 1, ++ mode->fll_def - mode->height); ++ ++ hblank = mode->llp - mode->width; ++ sensor->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, ++ V4L2_CID_HBLANK, hblank, hblank, ++ 1, hblank); ++ ++ /* fll >= exposure time + adjust parameter (default value is 18) */ ++ exposure_max = mode->fll_def - IMX471_EXPOSURE_MARGIN; ++ sensor->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, ++ V4L2_CID_EXPOSURE, ++ IMX471_EXPOSURE_MIN, exposure_max, ++ IMX471_EXPOSURE_STEP, ++ IMX471_EXPOSURE_DEFAULT); ++ ++ v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, ++ IMX471_ANA_GAIN_MIN, IMX471_ANA_GAIN_MAX, ++ IMX471_ANA_GAIN_STEP, IMX471_ANA_GAIN_DEFAULT); ++ ++ v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, V4L2_CID_DIGITAL_GAIN, ++ IMX471_DGTL_GAIN_MIN, IMX471_DGTL_GAIN_MAX, ++ IMX471_DGTL_GAIN_STEP, IMX471_DGTL_GAIN_DEFAULT); ++ ++ v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx471_ctrl_ops, ++ V4L2_CID_TEST_PATTERN, ++ ARRAY_SIZE(imx471_test_pattern_menu) - 1, ++ 0, 0, imx471_test_pattern_menu); ++ ++ sensor->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 0); ++ ++ sensor->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, ++ V4L2_CID_VFLIP, 0, 1, 1, 0); ++ ++ if (ctrl_hdlr->error) { ++ dev_err(sensor->dev, "%s control init failed: %d\n", ++ __func__, ctrl_hdlr->error); ++ goto error; ++ } ++ ++ link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; ++ sensor->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; ++ sensor->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; ++ sensor->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; ++ ++ sensor->sd.ctrl_handler = ctrl_hdlr; ++ ++ return 0; ++ ++error: ++ v4l2_ctrl_handler_free(ctrl_hdlr); ++ ++ return ctrl_hdlr->error; ++} ++ ++static int imx471_check_hwcfg(struct imx471 *sensor) ++{ ++ struct v4l2_fwnode_endpoint bus_cfg = { ++ .bus_type = V4L2_MBUS_CSI2_DPHY, ++ }; ++ struct fwnode_handle *ep, *fwnode = dev_fwnode(sensor->dev); ++ unsigned long link_freq_bitmap; ++ struct clk *clk; ++ int ret; ++ ++ clk = devm_v4l2_sensor_clk_get(sensor->dev, NULL); ++ if (IS_ERR(clk)) ++ return dev_err_probe(sensor->dev, PTR_ERR(clk), ++ "can't get clock frequency\n"); ++ ++ if (clk_get_rate(clk) != IMX471_EXT_CLK) ++ return dev_err_probe(sensor->dev, -EINVAL, ++ "external clock %lu is not supported\n", ++ clk_get_rate(clk)); ++ ++ ep = fwnode_graph_get_endpoint_by_id(fwnode, 0, 0, 0); ++ ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); ++ fwnode_handle_put(ep); ++ if (ret) ++ return dev_err_probe(sensor->dev, ret, ++ "parsing endpoint failed\n"); ++ ++ if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) { ++ ret = dev_err_probe(sensor->dev, -EINVAL, ++ "number of CSI2 data lanes %u is not supported\n", ++ bus_cfg.bus.mipi_csi2.num_data_lanes); ++ goto done_endpoint_free; ++ } ++ ++ ret = v4l2_link_freq_to_bitmap(sensor->dev, bus_cfg.link_frequencies, ++ bus_cfg.nr_of_link_frequencies, ++ link_freq_menu_items, ++ ARRAY_SIZE(link_freq_menu_items), ++ &link_freq_bitmap); ++ ++done_endpoint_free: ++ v4l2_fwnode_endpoint_free(&bus_cfg); ++ ++ return ret; ++} ++ ++static int imx471_probe(struct i2c_client *client) ++{ ++ struct imx471 *sensor; ++ int ret; ++ ++ sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL); ++ if (!sensor) ++ return dev_err_probe(&client->dev, -ENOMEM, ++ "failed to allocate memory\n"); ++ ++ sensor->dev = &client->dev; ++ ++ ret = imx471_check_hwcfg(sensor); ++ if (ret) ++ return dev_err_probe(sensor->dev, ret, ++ "failed to check hwcfg: %d\n", ret); ++ ++ ret = imx471_get_regulators(sensor->dev, sensor); ++ if (ret) ++ return dev_err_probe(sensor->dev, ret, ++ "failed to get regulators\n"); ++ ++ sensor->reset_gpio = devm_gpiod_get_optional(sensor->dev, "reset", ++ GPIOD_OUT_HIGH); ++ if (IS_ERR(sensor->reset_gpio)) ++ return dev_err_probe(sensor->dev, PTR_ERR(sensor->reset_gpio), ++ "failed to get reset gpio\n"); ++ ++ sensor->img_clk = devm_v4l2_sensor_clk_get(sensor->dev, NULL); ++ if (IS_ERR(sensor->img_clk)) ++ return dev_err_probe(sensor->dev, PTR_ERR(sensor->img_clk), ++ "failed to get imaging clock\n"); ++ ++ v4l2_i2c_subdev_init(&sensor->sd, client, &imx471_subdev_ops); ++ ++ sensor->regmap = devm_cci_regmap_init_i2c(client, 16); ++ if (IS_ERR(sensor->regmap)) ++ return dev_err_probe(sensor->dev, PTR_ERR(sensor->regmap), ++ "failed to initialize CCI\n"); ++ ++ ret = imx471_power_on(sensor->dev); ++ if (ret) ++ return dev_err_probe(sensor->dev, ret, ++ "failed to power on\n"); ++ ++ ret = imx471_identify_module(sensor); ++ if (ret) { ++ dev_err_probe(sensor->dev, ret, "failed to find sensor: %d\n", ++ ret); ++ goto error_power_off; ++ } ++ ++ ret = imx471_init_controls(sensor); ++ if (ret) { ++ dev_err_probe(sensor->dev, ret, "failed to init controls: %d\n", ++ ret); ++ goto error_power_off; ++ } ++ ++ sensor->sd.internal_ops = &imx471_internal_ops; ++ sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ sensor->pad.flags = MEDIA_PAD_FL_SOURCE; ++ sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; ++ ++ ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad); ++ if (ret) { ++ dev_err_probe(sensor->dev, ret, ++ "failed to init entity pads: %d\n", ret); ++ goto error_v4l2_ctrl_handler_free; ++ } ++ ++ sensor->sd.state_lock = sensor->ctrl_handler.lock; ++ ret = v4l2_subdev_init_finalize(&sensor->sd); ++ if (ret < 0) { ++ dev_err_probe(sensor->dev, ret, "failed to init subdev: %d\n", ++ ret); ++ goto error_media_entity_pm; ++ } ++ ++ pm_runtime_set_active(sensor->dev); ++ pm_runtime_enable(sensor->dev); ++ ++ ret = v4l2_async_register_subdev_sensor(&sensor->sd); ++ if (ret < 0) ++ goto error_v4l2_subdev_cleanup; ++ ++ pm_runtime_idle(sensor->dev); ++ ++ return 0; ++ ++error_v4l2_subdev_cleanup: ++ pm_runtime_disable(sensor->dev); ++ pm_runtime_set_suspended(sensor->dev); ++ v4l2_subdev_cleanup(&sensor->sd); ++ ++error_media_entity_pm: ++ media_entity_cleanup(&sensor->sd.entity); ++ ++error_v4l2_ctrl_handler_free: ++ v4l2_ctrl_handler_free(sensor->sd.ctrl_handler); ++ ++error_power_off: ++ imx471_power_off(sensor->dev); ++ ++ return ret; ++} ++ ++static void imx471_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ ++ v4l2_async_unregister_subdev(sd); ++ v4l2_subdev_cleanup(sd); ++ media_entity_cleanup(&sd->entity); ++ v4l2_ctrl_handler_free(sd->ctrl_handler); ++ ++ pm_runtime_disable(&client->dev); ++ ++ if (!pm_runtime_status_suspended(&client->dev)) { ++ imx471_power_off(&client->dev); ++ pm_runtime_set_suspended(&client->dev); ++ } ++} ++ ++static DEFINE_RUNTIME_DEV_PM_OPS(imx471_pm_ops, imx471_power_off, ++ imx471_power_on, NULL); ++ ++static const struct acpi_device_id imx471_acpi_ids[] __maybe_unused = { ++ { "SONY471A" }, ++ { "TBE20A0" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(acpi, imx471_acpi_ids); ++ ++static struct i2c_driver imx471_i2c_driver = { ++ .driver = { ++ .name = "imx471", ++ .acpi_match_table = ACPI_PTR(imx471_acpi_ids), ++ .pm = pm_sleep_ptr(&imx471_pm_ops), ++ }, ++ .probe = imx471_probe, ++ .remove = imx471_remove, ++}; ++module_i2c_driver(imx471_i2c_driver); ++ ++MODULE_AUTHOR("Jimmy Su "); ++MODULE_AUTHOR("Serin Yeh "); ++MODULE_AUTHOR("Kate Hsuan "); ++MODULE_DESCRIPTION("Sony imx471 sensor driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c +--- a/drivers/media/i2c/ov2740.c ++++ b/drivers/media/i2c/ov2740.c +@@ -1241,41 +1241,42 @@ static int ov2740_nvmem_read(void *priv, unsigned int off, void *val, + return ret; + } + ++static const struct regmap_config regmap_config = { ++ .val_bits = 8, ++ .reg_bits = 16, ++ .disable_locking = true, ++}; ++ + static int ov2740_register_nvmem(struct i2c_client *client, + struct ov2740 *ov2740) + { + struct nvm_data *nvm; +- struct regmap_config regmap_config = { }; +- struct nvmem_config nvmem_config = { }; + struct regmap *regmap; + struct device *dev = ov2740->dev; ++ struct nvmem_config nvmem_config = { ++ .name = dev_name(dev), ++ .dev = dev, ++ .read_only = true, ++ .root_only = true, ++ .owner = THIS_MODULE, ++ .compat = true, ++ .base_dev = dev, ++ .reg_read = ov2740_nvmem_read, ++ .stride = 1, ++ .word_size = 1, ++ .size = CUSTOMER_USE_OTP_SIZE, ++ }; + + nvm = devm_kzalloc(dev, sizeof(*nvm), GFP_KERNEL); + if (!nvm) + return -ENOMEM; + +- regmap_config.val_bits = 8; +- regmap_config.reg_bits = 16; +- regmap_config.disable_locking = true; + regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + nvm->regmap = regmap; +- +- nvmem_config.name = dev_name(dev); +- nvmem_config.dev = dev; +- nvmem_config.read_only = true; +- nvmem_config.root_only = true; +- nvmem_config.owner = THIS_MODULE; +- nvmem_config.compat = true; +- nvmem_config.base_dev = dev; +- nvmem_config.reg_read = ov2740_nvmem_read; +- nvmem_config.reg_write = NULL; + nvmem_config.priv = nvm; +- nvmem_config.stride = 1; +- nvmem_config.word_size = 1; +- nvmem_config.size = CUSTOMER_USE_OTP_SIZE; + + nvm->nvmem = devm_nvmem_register(dev, &nvmem_config); + if (IS_ERR(nvm->nvmem)) +diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c +--- a/drivers/media/pci/intel/ipu-bridge.c ++++ b/drivers/media/pci/intel/ipu-bridge.c +@@ -49,6 +49,8 @@ + * Please keep the list sorted by ACPI HID. + */ + static const struct ipu_sensor_config ipu_supported_sensors[] = { ++ /* Himax HM1092 */ ++ IPU_SENSOR_CONFIG("HIMX1092", 2, 180000000, 180480000), + /* Himax HM11B1 */ + IPU_SENSOR_CONFIG("HIMX11B1", 1, 384000000), + /* Himax HM2170 */ +@@ -97,6 +99,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { + IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000), + /* Sony IMX471 */ + IPU_SENSOR_CONFIG("SONY471A", 1, 200000000), ++ /* Sony IMX471 (found on Lenovo X1 Carbon G14) */ ++ IPU_SENSOR_CONFIG("TBE20A0", 1, 200000000), + /* Toshiba T4KA3 */ + IPU_SENSOR_CONFIG("XMCC0003", 1, 321468000), + }; +@@ -194,8 +198,8 @@ static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev + for (i = 0; i < ARRAY_SIZE(ivsc_acpi_ids); i++) { + const struct acpi_device_id *acpi_id = &ivsc_acpi_ids[i]; + struct acpi_device *consumer, *ivsc_adev; +- + acpi_handle handle = acpi_device_handle(ACPI_PTR(adev)); ++ + for_each_acpi_dev_match(ivsc_adev, acpi_id->id, NULL, -1) + /* camera sensor depends on IVSC in DSDT if exist */ + for_each_acpi_consumer_dev(ivsc_adev, consumer) diff --git a/pkgbuilds/linux-omarchy-bore/0543-media-laptop-cameras.patch.sig b/pkgbuilds/linux-omarchy-bore/0543-media-laptop-cameras.patch.sig new file mode 100644 index 0000000..afd9deb Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0543-media-laptop-cameras.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0544-ipu-bridge-lenovo-x9-upside-down.patch b/pkgbuilds/linux-omarchy-bore/0544-ipu-bridge-lenovo-x9-upside-down.patch new file mode 100644 index 0000000..abda19b --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0544-ipu-bridge-lenovo-x9-upside-down.patch @@ -0,0 +1,49 @@ +diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c +--- a/drivers/media/pci/intel/ipu-bridge.c ++++ b/drivers/media/pci/intel/ipu-bridge.c +@@ -138,6 +138,45 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = { + }, + .driver_data = "OVTI02C1", + }, ++ /* ++ * The first four characters of DMI_BOARD_NAME identify the Lenovo ++ * machine type/model. For example, a DMI_BOARD_NAME starting with ++ * "21Q6" indicates a ThinkPad X9-15. ++ * ++ * Reference: https://psref.lenovo.com/ ++ */ ++ { ++ /* Lenovo X9-14 */ ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_BOARD_NAME, "21QA"), ++ }, ++ .driver_data = "SONY471A", ++ }, ++ { ++ /* Lenovo X9-14 */ ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_BOARD_NAME, "21QB"), ++ }, ++ .driver_data = "SONY471A", ++ }, ++ { ++ /* Lenovo X9-15 */ ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_BOARD_NAME, "21Q6"), ++ }, ++ .driver_data = "SONY471A", ++ }, ++ { ++ /* Lenovo X9-15 */ ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_BOARD_NAME, "21Q7"), ++ }, ++ .driver_data = "SONY471A", ++ }, + {} /* Terminating entry */ + }; + diff --git a/pkgbuilds/linux-omarchy-bore/0544-ipu-bridge-lenovo-x9-upside-down.patch.sig b/pkgbuilds/linux-omarchy-bore/0544-ipu-bridge-lenovo-x9-upside-down.patch.sig new file mode 100644 index 0000000..844b9fe Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0544-ipu-bridge-lenovo-x9-upside-down.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0545-ipu-bridge-dell-16-premium-da16250-upside-down.patch b/pkgbuilds/linux-omarchy-bore/0545-ipu-bridge-dell-16-premium-da16250-upside-down.patch new file mode 100644 index 0000000..7c15eb7 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0545-ipu-bridge-dell-16-premium-da16250-upside-down.patch @@ -0,0 +1,17 @@ +diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c +--- a/drivers/media/pci/intel/ipu-bridge.c ++++ b/drivers/media/pci/intel/ipu-bridge.c +@@ -138,6 +138,13 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = { + }, + .driver_data = "OVTI02C1", + }, ++ { ++ .matches = { ++ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), ++ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 16 (Dell 16 Premium) DA16250"), ++ }, ++ .driver_data = "OVTI02C1", ++ }, + /* + * The first four characters of DMI_BOARD_NAME identify the Lenovo + * machine type/model. For example, a DMI_BOARD_NAME starting with diff --git a/pkgbuilds/linux-omarchy-bore/0545-ipu-bridge-dell-16-premium-da16250-upside-down.patch.sig b/pkgbuilds/linux-omarchy-bore/0545-ipu-bridge-dell-16-premium-da16250-upside-down.patch.sig new file mode 100644 index 0000000..4e6cc4f Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0545-ipu-bridge-dell-16-premium-da16250-upside-down.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0546-ipu-bridge-samsung-galaxy-book5-pro-360-upside-down.patch b/pkgbuilds/linux-omarchy-bore/0546-ipu-bridge-samsung-galaxy-book5-pro-360-upside-down.patch new file mode 100644 index 0000000..a42d186 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0546-ipu-bridge-samsung-galaxy-book5-pro-360-upside-down.patch @@ -0,0 +1,18 @@ +diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c +--- a/drivers/media/pci/intel/ipu-bridge.c ++++ b/drivers/media/pci/intel/ipu-bridge.c +@@ -184,6 +184,14 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = { + }, + .driver_data = "SONY471A", + }, ++ { ++ /* Samsung Galaxy Book5 Pro 360 */ ++ .matches = { ++ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), ++ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "960QHA"), ++ }, ++ .driver_data = "OVTI02E1", ++ }, + {} /* Terminating entry */ + }; + diff --git a/pkgbuilds/linux-omarchy-bore/0546-ipu-bridge-samsung-galaxy-book5-pro-360-upside-down.patch.sig b/pkgbuilds/linux-omarchy-bore/0546-ipu-bridge-samsung-galaxy-book5-pro-360-upside-down.patch.sig new file mode 100644 index 0000000..18998fb Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0546-ipu-bridge-samsung-galaxy-book5-pro-360-upside-down.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0547-int3472-imx471-vana-power-enable.patch b/pkgbuilds/linux-omarchy-bore/0547-int3472-imx471-vana-power-enable.patch new file mode 100644 index 0000000..db38adc --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0547-int3472-imx471-vana-power-enable.patch @@ -0,0 +1,28 @@ +diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c +--- a/drivers/platform/x86/intel/int3472/discrete.c ++++ b/drivers/platform/x86/intel/int3472/discrete.c +@@ -164,6 +164,24 @@ static const struct int3472_gpio_map int3472_gpio_map[] = { + .con_id = "dvdd", + .enable_time_us = 45 * USEC_PER_MSEC, + }, ++ { /* imx471 expects "vana" as con_id for power enable */ ++ .hid = "SONY471A", ++ .type_from = INT3472_GPIO_TYPE_POWER_ENABLE, ++ .type_to = INT3472_GPIO_TYPE_POWER_ENABLE, ++ .con_id = "vana", ++ .enable_time_us = GPIO_REGULATOR_ENABLE_TIME, ++ }, ++ { ++ /* ++ * imx471 (on Lenovo ThinkPads X1 G14) expects "vana" as con_id ++ * for power enable ++ */ ++ .hid = "TBE20A0", ++ .type_from = INT3472_GPIO_TYPE_POWER_ENABLE, ++ .type_to = INT3472_GPIO_TYPE_POWER_ENABLE, ++ .con_id = "vana", ++ .enable_time_us = GPIO_REGULATOR_ENABLE_TIME, ++ }, + }; + + static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3472, u8 *type, diff --git a/pkgbuilds/linux-omarchy-bore/0547-int3472-imx471-vana-power-enable.patch.sig b/pkgbuilds/linux-omarchy-bore/0547-int3472-imx471-vana-power-enable.patch.sig new file mode 100644 index 0000000..fbd699c Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0547-int3472-imx471-vana-power-enable.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0548-media-cvs-sensor-node-in-bound-callback.patch b/pkgbuilds/linux-omarchy-bore/0548-media-cvs-sensor-node-in-bound-callback.patch new file mode 100644 index 0000000..b041d90 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0548-media-cvs-sensor-node-in-bound-callback.patch @@ -0,0 +1,37 @@ +diff --git a/drivers/media/i2c/cvs/v4l2.c b/drivers/media/i2c/cvs/v4l2.c +--- a/drivers/media/i2c/cvs/v4l2.c ++++ b/drivers/media/i2c/cvs/v4l2.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -400,7 +401,7 @@ static int cvs_csi_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_async_connection *asc) + { + struct icvs *ctx = notifier_to_csi(notifier); +- int pad; ++ int pad, ret; + + pad = media_entity_get_fwnode_pad(&sd->entity, asc->match.fwnode, + MEDIA_PAD_FL_SOURCE); +@@ -409,9 +410,13 @@ static int cvs_csi_notify_bound(struct v4l2_async_notifier *notifier, + + ctx->remote = &sd->entity.pads[pad]; + +- return media_create_pad_link(&sd->entity, pad, &ctx->subdev.entity, +- ICVS_CSI_PAD_SINK, MEDIA_LNK_FL_ENABLED | +- MEDIA_LNK_FL_IMMUTABLE); ++ ret = media_create_pad_link(&sd->entity, pad, &ctx->subdev.entity, ++ ICVS_CSI_PAD_SINK, MEDIA_LNK_FL_ENABLED | ++ MEDIA_LNK_FL_IMMUTABLE); ++ if (ret) ++ return ret; ++ ++ return v4l2_device_register_subdev_nodes(sd->v4l2_dev); + } + + /** diff --git a/pkgbuilds/linux-omarchy-bore/0548-media-cvs-sensor-node-in-bound-callback.patch.sig b/pkgbuilds/linux-omarchy-bore/0548-media-cvs-sensor-node-in-bound-callback.patch.sig new file mode 100644 index 0000000..5355a32 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0548-media-cvs-sensor-node-in-bound-callback.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0549-media-imx471-analog-gain-max-960.patch b/pkgbuilds/linux-omarchy-bore/0549-media-imx471-analog-gain-max-960.patch new file mode 100644 index 0000000..a11c4f2 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0549-media-imx471-analog-gain-max-960.patch @@ -0,0 +1,12 @@ +diff --git a/drivers/media/i2c/imx471.c b/drivers/media/i2c/imx471.c +--- a/drivers/media/i2c/imx471.c ++++ b/drivers/media/i2c/imx471.c +@@ -41,7 +41,7 @@ + /* Analog gain control */ + #define IMX471_REG_ANALOG_GAIN CCI_REG16(0x0204) + #define IMX471_ANA_GAIN_MIN 0 +-#define IMX471_ANA_GAIN_MAX 800 ++#define IMX471_ANA_GAIN_MAX 960 + #define IMX471_ANA_GAIN_STEP 1 + #define IMX471_ANA_GAIN_DEFAULT 0 + diff --git a/pkgbuilds/linux-omarchy-bore/0549-media-imx471-analog-gain-max-960.patch.sig b/pkgbuilds/linux-omarchy-bore/0549-media-imx471-analog-gain-max-960.patch.sig new file mode 100644 index 0000000..bd5334b Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0549-media-imx471-analog-gain-max-960.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0560-input-updates.patch b/pkgbuilds/linux-omarchy-bore/0560-input-updates.patch new file mode 100644 index 0000000..d8548c7 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0560-input-updates.patch @@ -0,0 +1,3869 @@ +diff --git a/drivers/input/input.c b/drivers/input/input.c +--- a/drivers/input/input.c ++++ b/drivers/input/input.c +@@ -318,7 +318,7 @@ static int input_get_disposition(struct input_dev *dev, + static void input_event_dispose(struct input_dev *dev, int disposition, + unsigned int type, unsigned int code, int value) + { +- if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event) ++ if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event && dev->ready) + dev->event(dev, type, code, value); + + if (disposition & INPUT_PASS_TO_HANDLERS) { +@@ -568,6 +568,75 @@ void input_release_device(struct input_handle *handle) + } + EXPORT_SYMBOL(input_release_device); + ++#define INPUT_DO_TOGGLE(dev, type, bits, on) \ ++ do { \ ++ int i; \ ++ bool active; \ ++ \ ++ if (!test_bit(EV_##type, dev->evbit)) \ ++ break; \ ++ \ ++ for_each_set_bit(i, dev->bits##bit, type##_CNT) { \ ++ active = test_bit(i, dev->bits); \ ++ if (!active && !on) \ ++ continue; \ ++ \ ++ dev->event(dev, EV_##type, i, on ? active : 0); \ ++ } \ ++ } while (0) ++ ++/* ++ * Iterate through the logical state of the input device (LEDs, sounds, ++ * auto-repeat) and explicitly push that state down to the hardware ++ * via dev->event() to match the current logical state (if activate is true), ++ * or forcibly turn off all feedback like LEDs and sounds during teardown ++ * or suspend (if activate is false). ++ * ++ * Primarily used as a state-replay mechanism after a device is opened ++ * or uninhibited, as events might have been dropped by the core while the ++ * hardware was not marked as ready. ++ */ ++static void input_dev_toggle(struct input_dev *dev, bool activate) ++{ ++ if (!dev->event || !dev->ready) ++ return; ++ ++ INPUT_DO_TOGGLE(dev, LED, led, activate); ++ INPUT_DO_TOGGLE(dev, SND, snd, activate); ++ ++ if (activate && test_bit(EV_REP, dev->evbit)) { ++ dev->event(dev, EV_REP, REP_PERIOD, dev->rep[REP_PERIOD]); ++ dev->event(dev, EV_REP, REP_DELAY, dev->rep[REP_DELAY]); ++ } ++} ++ ++static int input_start_device(struct input_dev *dev) ++{ ++ int error; ++ ++ lockdep_assert_held(&dev->mutex); ++ ++ if (dev->users++ == 0 && !dev->inhibited) { ++ if (dev->open) { ++ error = dev->open(dev); ++ if (error) { ++ dev->users--; ++ return error; ++ } ++ } ++ ++ scoped_guard(spinlock_irq, &dev->event_lock) { ++ dev->ready = true; ++ input_dev_toggle(dev, true); ++ } ++ ++ if (dev->poller) ++ input_dev_poller_start(dev->poller); ++ } ++ ++ return 0; ++} ++ + /** + * input_open_device - open input device + * @handle: handle through which device is being accessed +@@ -586,21 +655,9 @@ int input_open_device(struct input_handle *handle) + + handle->open++; + +- if (handle->handler->passive_observer) +- return 0; +- +- if (dev->users++ || dev->inhibited) { +- /* +- * Device is already opened and/or inhibited, +- * so we can exit immediately and report success. +- */ +- return 0; +- } +- +- if (dev->open) { +- error = dev->open(dev); ++ if (!handle->handler->passive_observer) { ++ error = input_start_device(dev); + if (error) { +- dev->users--; + handle->open--; + /* + * Make sure we are not delivering any more +@@ -611,8 +668,8 @@ int input_open_device(struct input_handle *handle) + } + } + +- if (dev->poller) +- input_dev_poller_start(dev->poller); ++ if (handle->open == 1 && handle->handler->start) ++ handle->handler->start(handle); + } + + return 0; +@@ -651,6 +708,12 @@ void input_close_device(struct input_handle *handle) + if (!--dev->users && !dev->inhibited) { + if (dev->poller) + input_dev_poller_stop(dev->poller); ++ ++ scoped_guard(spinlock_irq, &dev->event_lock) { ++ input_dev_toggle(dev, false); ++ dev->ready = false; ++ } ++ + if (dev->close) + dev->close(dev); + } +@@ -1702,37 +1765,6 @@ static int input_dev_uevent(const struct device *device, struct kobj_uevent_env + return 0; + } + +-#define INPUT_DO_TOGGLE(dev, type, bits, on) \ +- do { \ +- int i; \ +- bool active; \ +- \ +- if (!test_bit(EV_##type, dev->evbit)) \ +- break; \ +- \ +- for_each_set_bit(i, dev->bits##bit, type##_CNT) { \ +- active = test_bit(i, dev->bits); \ +- if (!active && !on) \ +- continue; \ +- \ +- dev->event(dev, EV_##type, i, on ? active : 0); \ +- } \ +- } while (0) +- +-static void input_dev_toggle(struct input_dev *dev, bool activate) +-{ +- if (!dev->event) +- return; +- +- INPUT_DO_TOGGLE(dev, LED, led, activate); +- INPUT_DO_TOGGLE(dev, SND, snd, activate); +- +- if (activate && test_bit(EV_REP, dev->evbit)) { +- dev->event(dev, EV_REP, REP_PERIOD, dev->rep[REP_PERIOD]); +- dev->event(dev, EV_REP, REP_DELAY, dev->rep[REP_DELAY]); +- } +-} +- + /** + * input_reset_device() - reset/restore the state of input device + * @dev: input device whose state needs to be reset +@@ -1756,52 +1788,73 @@ static int input_inhibit_device(struct input_dev *dev) + { + guard(mutex)(&dev->mutex); + ++ if (dev->going_away) ++ return -ENODEV; ++ + if (dev->inhibited) + return 0; + + if (dev->users) { +- if (dev->close) +- dev->close(dev); + if (dev->poller) + input_dev_poller_stop(dev->poller); ++ ++ scoped_guard(spinlock_irq, &dev->event_lock) { ++ input_dev_toggle(dev, false); ++ dev->ready = false; ++ } ++ ++ if (dev->close) ++ dev->close(dev); + } + + scoped_guard(spinlock_irq, &dev->event_lock) { + input_mt_release_slots(dev); + input_dev_release_keys(dev); + input_handle_event(dev, EV_SYN, SYN_REPORT, 1); +- input_dev_toggle(dev, false); ++ dev->inhibited = true; + } + +- dev->inhibited = true; +- + return 0; + } + + static int input_uninhibit_device(struct input_dev *dev) + { ++ struct input_handle *handle; + int error; + + guard(mutex)(&dev->mutex); + ++ if (dev->going_away) ++ return -ENODEV; ++ + if (!dev->inhibited) + return 0; + ++ dev->inhibited = false; ++ + if (dev->users) { + if (dev->open) { + error = dev->open(dev); +- if (error) ++ if (error) { ++ dev->inhibited = true; + return error; ++ } + } +- if (dev->poller) +- input_dev_poller_start(dev->poller); ++ scoped_guard(spinlock_irq, &dev->event_lock) ++ dev->ready = true; + } + +- dev->inhibited = false; +- + scoped_guard(spinlock_irq, &dev->event_lock) + input_dev_toggle(dev, true); + ++ if (dev->users && dev->poller) ++ input_dev_poller_start(dev->poller); ++ ++ list_for_each_entry(handle, &dev->h_list, d_node) { ++ if (handle->open && handle->handler->start) ++ handle->handler->start(handle); ++ } ++ + return 0; + } + +@@ -2655,9 +2708,6 @@ int input_register_handle(struct input_handle *handle) + */ + list_add_tail_rcu(&handle->h_node, &handler->h_list); + +- if (handler->start) +- handler->start(handle); +- + return 0; + } + EXPORT_SYMBOL(input_register_handle); +diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig +--- a/drivers/input/keyboard/Kconfig ++++ b/drivers/input/keyboard/Kconfig +@@ -119,44 +119,6 @@ config KEYBOARD_ATKBD + To compile this driver as a module, choose M here: the + module will be called atkbd. + +-config KEYBOARD_ATKBD_HP_KEYCODES +- bool "Use HP keyboard scancodes" +- depends on PARISC && KEYBOARD_ATKBD +- default y +- help +- Say Y here if you have a PA-RISC machine and want to use an AT or +- PS/2 keyboard, and your keyboard uses keycodes that are specific to +- PA-RISC keyboards. +- +- Say N if you use a standard keyboard. +- +-config KEYBOARD_ATKBD_RDI_KEYCODES +- bool "Use PrecisionBook keyboard scancodes" +- depends on KEYBOARD_ATKBD_HP_KEYCODES +- default n +- help +- If you have an RDI PrecisionBook, say Y here if you want to use its +- built-in keyboard (as opposed to an external keyboard). +- +- The PrecisionBook has five keys that conflict with those used by most +- AT and PS/2 keyboards. These are as follows: +- +- PrecisionBook Standard AT or PS/2 +- +- F1 F12 +- Left Ctrl Left Alt +- Caps Lock Left Ctrl +- Right Ctrl Caps Lock +- Left 102nd key (the key to the right of Left Shift) +- +- If you say N here, and use the PrecisionBook keyboard, then each key +- in the left-hand column will be interpreted as the corresponding key +- in the right-hand column. +- +- If you say Y here, and use an external keyboard, then each key in the +- right-hand column will be interpreted as the key shown in the +- left-hand column. +- + config KEYBOARD_QT1050 + tristate "Microchip AT42QT1050 Touch Sensor Chip" + depends on I2C +diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c +--- a/drivers/input/keyboard/atkbd.c ++++ b/drivers/input/keyboard/atkbd.c +@@ -73,13 +73,6 @@ MODULE_PARM_DESC(terminal, "Enable break codes on an IBM Terminal keyboard conne + #define ATKBD_KEYMAP_SIZE 512 + + static const unsigned short atkbd_set2_keycode[ATKBD_KEYMAP_SIZE] = { +-#ifdef CONFIG_KEYBOARD_ATKBD_HP_KEYCODES +- +-/* XXX: need a more general approach */ +- +-#include "hpps2atkbd.h" /* include the keyboard scancodes */ +- +-#else + 0, 67, 65, 63, 61, 59, 60, 88,183, 68, 66, 64, 62, 15, 41,117, + 184, 56, 42, 93, 29, 16, 2, 0,185, 0, 44, 31, 30, 17, 3, 0, + 186, 46, 45, 32, 18, 5, 4, 95,187, 57, 47, 33, 20, 19, 6,183, +@@ -99,7 +92,6 @@ static const unsigned short atkbd_set2_keycode[ATKBD_KEYMAP_SIZE] = { + 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119, 0, + + 0, 0, 0, 65, 99, +-#endif + }; + + static const unsigned short atkbd_set3_keycode[ATKBD_KEYMAP_SIZE] = { +diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c +--- a/drivers/input/keyboard/cap11xx.c ++++ b/drivers/input/keyboard/cap11xx.c +@@ -5,6 +5,8 @@ + * (c) 2014 Daniel Mack + */ + ++#include ++#include + #include + #include + #include +@@ -16,63 +18,43 @@ + #include + #include + ++#define CAP1114_REG_BUTTON_STATUS1 0x03 ++#define CAP1114_REG_BUTTON_STATUS2 0x04 ++#define CAP1114_REG_CONFIG2 0x40 ++#define CAP1114_REG_CONFIG2_VOL_UP_DOWN BIT(1) ++#define CAP1114_REG_LED_OUTPUT_CONTROL1 0x73 ++ + #define CAP11XX_REG_MAIN_CONTROL 0x00 + #define CAP11XX_REG_MAIN_CONTROL_GAIN_SHIFT (6) + #define CAP11XX_REG_MAIN_CONTROL_GAIN_MASK (0xc0) + #define CAP11XX_REG_MAIN_CONTROL_DLSEEP BIT(4) +-#define CAP11XX_REG_GENERAL_STATUS 0x02 + #define CAP11XX_REG_SENSOR_INPUT 0x03 +-#define CAP11XX_REG_NOISE_FLAG_STATUS 0x0a ++#define CAP1114_REG_BUTTON_STATUS2 0x04 + #define CAP11XX_REG_SENOR_DELTA(X) (0x10 + (X)) + #define CAP11XX_REG_SENSITIVITY_CONTROL 0x1f + #define CAP11XX_REG_SENSITIVITY_CONTROL_DELTA_SENSE_MASK 0x70 +-#define CAP11XX_REG_CONFIG 0x20 +-#define CAP11XX_REG_SENSOR_ENABLE 0x21 +-#define CAP11XX_REG_SENSOR_CONFIG 0x22 +-#define CAP11XX_REG_SENSOR_CONFIG2 0x23 +-#define CAP11XX_REG_SAMPLING_CONFIG 0x24 +-#define CAP11XX_REG_CALIBRATION 0x26 +-#define CAP11XX_REG_INT_ENABLE 0x27 + #define CAP11XX_REG_REPEAT_RATE 0x28 + #define CAP11XX_REG_SIGNAL_GUARD_ENABLE 0x29 +-#define CAP11XX_REG_MT_CONFIG 0x2a +-#define CAP11XX_REG_MT_PATTERN_CONFIG 0x2b +-#define CAP11XX_REG_MT_PATTERN 0x2d +-#define CAP11XX_REG_RECALIB_CONFIG 0x2f + #define CAP11XX_REG_SENSOR_THRESH(X) (0x30 + (X)) +-#define CAP11XX_REG_SENSOR_NOISE_THRESH 0x38 +-#define CAP11XX_REG_STANDBY_CHANNEL 0x40 +-#define CAP11XX_REG_STANDBY_CONFIG 0x41 +-#define CAP11XX_REG_STANDBY_SENSITIVITY 0x42 +-#define CAP11XX_REG_STANDBY_THRESH 0x43 + #define CAP11XX_REG_CONFIG2 0x44 + #define CAP11XX_REG_CONFIG2_ALT_POL BIT(6) +-#define CAP11XX_REG_SENSOR_BASE_CNT(X) (0x50 + (X)) +-#define CAP11XX_REG_LED_POLARITY 0x73 + #define CAP11XX_REG_LED_OUTPUT_CONTROL 0x74 + #define CAP11XX_REG_CALIB_SENSITIVITY_CONFIG 0x80 + #define CAP11XX_REG_CALIB_SENSITIVITY_CONFIG2 0x81 +- +-#define CAP11XX_REG_LED_DUTY_CYCLE_1 0x90 +-#define CAP11XX_REG_LED_DUTY_CYCLE_2 0x91 +-#define CAP11XX_REG_LED_DUTY_CYCLE_3 0x92 + #define CAP11XX_REG_LED_DUTY_CYCLE_4 0x93 + +-#define CAP11XX_REG_LED_DUTY_MIN_MASK (0x0f) +-#define CAP11XX_REG_LED_DUTY_MIN_MASK_SHIFT (0) + #define CAP11XX_REG_LED_DUTY_MAX_MASK (0xf0) +-#define CAP11XX_REG_LED_DUTY_MAX_MASK_SHIFT (4) + #define CAP11XX_REG_LED_DUTY_MAX_VALUE (15) + +-#define CAP11XX_REG_SENSOR_CALIB (0xb1 + (X)) +-#define CAP11XX_REG_SENSOR_CALIB_LSB1 0xb9 +-#define CAP11XX_REG_SENSOR_CALIB_LSB2 0xba + #define CAP11XX_REG_PRODUCT_ID 0xfd + #define CAP11XX_REG_MANUFACTURER_ID 0xfe + #define CAP11XX_REG_REVISION 0xff + + #define CAP11XX_MANUFACTURER_ID 0x5d + ++#define CAP11XX_T_RST_FILT_MIN_US 10000 ++#define CAP11XX_T_RST_ON_MIN_MS 400 ++ + #ifdef CONFIG_LEDS_CLASS + struct cap11xx_led { + struct cap11xx_priv *priv; +@@ -85,6 +67,7 @@ struct cap11xx_priv { + struct regmap *regmap; + struct device *dev; + struct input_dev *idev; ++ struct gpio_desc *reset_gpio; + const struct cap11xx_hw_model *model; + + struct cap11xx_led *leds; +@@ -101,47 +84,32 @@ struct cap11xx_priv { + + struct cap11xx_hw_model { + u8 product_id; ++ u8 led_output_control_reg_base; ++ u8 sensor_input_reg_base; + unsigned int num_channels; + unsigned int num_leds; ++ unsigned int num_sensor_thresholds; + bool has_gain; ++ bool has_grouped_sensors; + bool has_irq_config; ++ bool has_repeat_en; + bool has_sensitivity_control; + bool has_signal_guard; + }; + + static const struct reg_default cap11xx_reg_defaults[] = { + { CAP11XX_REG_MAIN_CONTROL, 0x00 }, +- { CAP11XX_REG_GENERAL_STATUS, 0x00 }, +- { CAP11XX_REG_SENSOR_INPUT, 0x00 }, +- { CAP11XX_REG_NOISE_FLAG_STATUS, 0x00 }, + { CAP11XX_REG_SENSITIVITY_CONTROL, 0x2f }, +- { CAP11XX_REG_CONFIG, 0x20 }, +- { CAP11XX_REG_SENSOR_ENABLE, 0x3f }, +- { CAP11XX_REG_SENSOR_CONFIG, 0xa4 }, +- { CAP11XX_REG_SENSOR_CONFIG2, 0x07 }, +- { CAP11XX_REG_SAMPLING_CONFIG, 0x39 }, +- { CAP11XX_REG_CALIBRATION, 0x00 }, +- { CAP11XX_REG_INT_ENABLE, 0x3f }, + { CAP11XX_REG_REPEAT_RATE, 0x3f }, +- { CAP11XX_REG_MT_CONFIG, 0x80 }, +- { CAP11XX_REG_MT_PATTERN_CONFIG, 0x00 }, +- { CAP11XX_REG_MT_PATTERN, 0x3f }, +- { CAP11XX_REG_RECALIB_CONFIG, 0x8a }, + { CAP11XX_REG_SENSOR_THRESH(0), 0x40 }, + { CAP11XX_REG_SENSOR_THRESH(1), 0x40 }, + { CAP11XX_REG_SENSOR_THRESH(2), 0x40 }, + { CAP11XX_REG_SENSOR_THRESH(3), 0x40 }, + { CAP11XX_REG_SENSOR_THRESH(4), 0x40 }, + { CAP11XX_REG_SENSOR_THRESH(5), 0x40 }, +- { CAP11XX_REG_SENSOR_NOISE_THRESH, 0x01 }, +- { CAP11XX_REG_STANDBY_CHANNEL, 0x00 }, +- { CAP11XX_REG_STANDBY_CONFIG, 0x39 }, +- { CAP11XX_REG_STANDBY_SENSITIVITY, 0x02 }, +- { CAP11XX_REG_STANDBY_THRESH, 0x40 }, ++ { CAP11XX_REG_SENSOR_THRESH(6), 0x40 }, ++ { CAP11XX_REG_SENSOR_THRESH(7), 0x40 }, + { CAP11XX_REG_CONFIG2, 0x40 }, +- { CAP11XX_REG_LED_POLARITY, 0x00 }, +- { CAP11XX_REG_SENSOR_CALIB_LSB1, 0x00 }, +- { CAP11XX_REG_SENSOR_CALIB_LSB2, 0x00 }, + }; + + static bool cap11xx_volatile_reg(struct device *dev, unsigned int reg) +@@ -149,6 +117,12 @@ static bool cap11xx_volatile_reg(struct device *dev, unsigned int reg) + switch (reg) { + case CAP11XX_REG_MAIN_CONTROL: + case CAP11XX_REG_SENSOR_INPUT: ++ /* ++ * CAP1114_REG_BUTTON_STATUS1 (CAP11XX_REG_SENSOR_INPUT) and ++ * CAP1114_REG_BUTTON_STATUS2 is volatile for the CAP1114, ++ * which supports more than 8 touch channels. ++ */ ++ case CAP1114_REG_BUTTON_STATUS2: + case CAP11XX_REG_SENOR_DELTA(0): + case CAP11XX_REG_SENOR_DELTA(1): + case CAP11XX_REG_SENOR_DELTA(2): +@@ -257,8 +231,8 @@ static int cap11xx_init_keys(struct cap11xx_priv *priv) + } + + if (!of_property_read_u32_array(node, "microchip,input-threshold", +- priv->thresholds, priv->model->num_channels)) { +- for (i = 0; i < priv->model->num_channels; i++) { ++ priv->thresholds, priv->model->num_sensor_thresholds)) { ++ for (i = 0; i < priv->model->num_sensor_thresholds; i++) { + if (priv->thresholds[i] > 127) { + dev_err(dev, "Invalid input-threshold value %u\n", + priv->thresholds[i]); +@@ -273,10 +247,13 @@ static int cap11xx_init_keys(struct cap11xx_priv *priv) + } + } + +- if (!of_property_read_u32_array(node, "microchip,calib-sensitivity", +- priv->calib_sensitivities, +- priv->model->num_channels)) { +- if (priv->model->has_sensitivity_control) { ++ if (of_property_present(node, "microchip,calib-sensitivity")) { ++ if (!priv->model->has_sensitivity_control) { ++ dev_warn(dev, ++ "This model doesn't support 'calib-sensitivity'\n"); ++ } else if (!of_property_read_u32_array(node, "microchip,calib-sensitivity", ++ priv->calib_sensitivities, ++ priv->model->num_channels)) { + for (i = 0; i < priv->model->num_channels; i++) { + if (!is_power_of_2(priv->calib_sensitivities[i]) || + priv->calib_sensitivities[i] > 4) { +@@ -296,32 +273,31 @@ static int cap11xx_init_keys(struct cap11xx_priv *priv) + if (error) + return error; + } +- } else { +- dev_warn(dev, +- "This model doesn't support 'calib-sensitivity'\n"); + } + } + +- for (i = 0; i < priv->model->num_channels; i++) { +- if (!of_property_read_u32_index(node, "microchip,signal-guard", +- i, &u32_val)) { +- if (u32_val > 1) +- return -EINVAL; +- if (u32_val) +- priv->signal_guard_inputs_mask |= 0x01 << i; +- } +- } +- +- if (priv->signal_guard_inputs_mask) { +- if (priv->model->has_signal_guard) { +- error = regmap_write(priv->regmap, +- CAP11XX_REG_SIGNAL_GUARD_ENABLE, +- priv->signal_guard_inputs_mask); +- if (error) +- return error; +- } else { ++ if (of_property_present(node, "microchip,signal-guard")) { ++ if (!priv->model->has_signal_guard) { + dev_warn(dev, + "This model doesn't support 'signal-guard'\n"); ++ } else { ++ for (i = 0; i < priv->model->num_channels; i++) { ++ if (!of_property_read_u32_index(node, "microchip,signal-guard", ++ i, &u32_val)) { ++ if (u32_val > 1) ++ return -EINVAL; ++ if (u32_val) ++ priv->signal_guard_inputs_mask |= 0x01 << i; ++ } ++ } ++ ++ if (priv->signal_guard_inputs_mask) { ++ error = regmap_write(priv->regmap, ++ CAP11XX_REG_SIGNAL_GUARD_ENABLE, ++ priv->signal_guard_inputs_mask); ++ if (error) ++ return error; ++ } + } + } + +@@ -332,10 +308,23 @@ static int cap11xx_init_keys(struct cap11xx_priv *priv) + of_property_read_u32_array(node, "linux,keycodes", + priv->keycodes, priv->model->num_channels); + +- /* Disable autorepeat. The Linux input system has its own handling. */ +- error = regmap_write(priv->regmap, CAP11XX_REG_REPEAT_RATE, 0); +- if (error) +- return error; ++ /* ++ * CAP1114 needs dedicated configuration to split ++ * grouped sensors into independent inputs. ++ */ ++ if (priv->model->has_grouped_sensors) { ++ error = regmap_set_bits(priv->regmap, CAP1114_REG_CONFIG2, ++ CAP1114_REG_CONFIG2_VOL_UP_DOWN); ++ if (error) ++ return error; ++ } ++ ++ if (priv->model->has_repeat_en) { ++ /* Disable autorepeat. The Linux input system has its own handling. */ ++ error = regmap_write(priv->regmap, CAP11XX_REG_REPEAT_RATE, 0); ++ if (error) ++ return error; ++ } + + return 0; + } +@@ -354,10 +343,25 @@ static irqreturn_t cap11xx_thread_func(int irq_num, void *data) + if (ret < 0) + goto out; + +- ret = regmap_read(priv->regmap, CAP11XX_REG_SENSOR_INPUT, &status); ++ ret = regmap_read(priv->regmap, priv->model->sensor_input_reg_base, &status); + if (ret < 0) + goto out; + ++ if (priv->model->num_channels > 8) { ++ unsigned int status2; ++ ++ ret = regmap_read(priv->regmap, priv->model->sensor_input_reg_base + 1, &status2); ++ if (ret < 0) ++ goto out; ++ ++ /* ++ * CAP1114 STATUS1 register only contains data for the first 6 channels. ++ * the remaining channels is stored in STATUS2. ++ */ ++ status &= GENMASK(5, 0); ++ status |= FIELD_PREP(GENMASK(13, 6), status2); ++ } ++ + for (i = 0; i < priv->idev->keycodemax; i++) + input_report_key(priv->idev, priv->keycodes[i], + status & (1 << i)); +@@ -407,10 +411,16 @@ static int cap11xx_led_set(struct led_classdev *cdev, + * limitation. Brightness levels per LED are either + * 0 (OFF) and 1 (ON). + */ +- return regmap_update_bits(priv->regmap, +- CAP11XX_REG_LED_OUTPUT_CONTROL, +- BIT(led->reg), +- value ? BIT(led->reg) : 0); ++ if (led->reg >= 8) ++ return regmap_update_bits(priv->regmap, ++ priv->model->led_output_control_reg_base + 1, ++ BIT(led->reg - 8), ++ value ? BIT(led->reg - 8) : 0); ++ else ++ return regmap_update_bits(priv->regmap, ++ priv->model->led_output_control_reg_base, ++ BIT(led->reg), ++ value ? BIT(led->reg) : 0); + } + + static int cap11xx_init_leds(struct device *dev, +@@ -420,6 +430,7 @@ static int cap11xx_init_leds(struct device *dev, + struct cap11xx_led *led; + int cnt = of_get_child_count(node); + int error; ++ u32 duty_val; + + if (!num_leds || !cnt) + return 0; +@@ -433,15 +444,26 @@ static int cap11xx_init_leds(struct device *dev, + + priv->leds = led; + ++ /* Set all LEDs to off */ + error = regmap_update_bits(priv->regmap, +- CAP11XX_REG_LED_OUTPUT_CONTROL, 0xff, 0); ++ priv->model->led_output_control_reg_base, ++ GENMASK(min(num_leds, 8) - 1, 0), 0); + if (error) + return error; + ++ if (num_leds > 8) { ++ error = regmap_update_bits(priv->regmap, ++ priv->model->led_output_control_reg_base + 1, ++ GENMASK(num_leds - 8 - 1, 0), 0); ++ if (error) ++ return error; ++ } ++ ++ duty_val = FIELD_PREP(CAP11XX_REG_LED_DUTY_MAX_MASK, ++ CAP11XX_REG_LED_DUTY_MAX_VALUE); ++ + error = regmap_update_bits(priv->regmap, CAP11XX_REG_LED_DUTY_CYCLE_4, +- CAP11XX_REG_LED_DUTY_MAX_MASK, +- CAP11XX_REG_LED_DUTY_MAX_VALUE << +- CAP11XX_REG_LED_DUTY_MAX_MASK_SHIFT); ++ CAP11XX_REG_LED_DUTY_MAX_MASK, duty_val); + if (error) + return error; + +@@ -510,9 +532,20 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client) + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + ++ priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); ++ if (IS_ERR(priv->reset_gpio)) ++ return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), ++ "Failed to get 'reset' GPIO\n"); ++ ++ if (priv->reset_gpio) { ++ usleep_range(CAP11XX_T_RST_FILT_MIN_US, CAP11XX_T_RST_FILT_MIN_US * 2); ++ gpiod_set_value_cansleep(priv->reset_gpio, 0); ++ msleep(CAP11XX_T_RST_ON_MIN_MS); ++ } ++ + error = regmap_read(priv->regmap, CAP11XX_REG_PRODUCT_ID, &val); + if (error) +- return error; ++ return dev_err_probe(dev, error, "Failed to read product ID\n"); + + if (val != cap->product_id) { + dev_err(dev, "Product ID: Got 0x%02x, expected 0x%02x\n", +@@ -522,7 +555,7 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client) + + error = regmap_read(priv->regmap, CAP11XX_REG_MANUFACTURER_ID, &val); + if (error) +- return error; ++ return dev_err_probe(dev, error, "Failed to read manufacturer ID\n"); + + if (val != CAP11XX_MANUFACTURER_ID) { + dev_err(dev, "Manufacturer ID: Got 0x%02x, expected 0x%02x\n", +@@ -531,11 +564,8 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client) + } + + error = regmap_read(priv->regmap, CAP11XX_REG_REVISION, &rev); +- if (error < 0) +- return error; +- +- dev_info(dev, "CAP11XX detected, model %s, revision 0x%02x\n", +- id->name, rev); ++ if (error) ++ return dev_err_probe(dev, error, "Failed to read revision\n"); + + priv->model = cap; + +@@ -599,47 +629,79 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client) + } + + static const struct cap11xx_hw_model cap1106_model = { +- .product_id = 0x55, .num_channels = 6, .num_leds = 0, ++ .product_id = 0x55, ++ .num_channels = 6, .num_leds = 0, .num_sensor_thresholds = 6, ++ .sensor_input_reg_base = CAP11XX_REG_SENSOR_INPUT, + .has_gain = true, + .has_irq_config = true, ++ .has_repeat_en = true, ++}; ++ ++static const struct cap11xx_hw_model cap1114_model = { ++ .product_id = 0x3a, ++ .num_channels = 14, .num_leds = 11, .num_sensor_thresholds = 8, ++ .led_output_control_reg_base = CAP1114_REG_LED_OUTPUT_CONTROL1, ++ .sensor_input_reg_base = CAP1114_REG_BUTTON_STATUS1, ++ .has_grouped_sensors = true, + }; + + static const struct cap11xx_hw_model cap1126_model = { +- .product_id = 0x53, .num_channels = 6, .num_leds = 2, ++ .product_id = 0x53, ++ .num_channels = 6, .num_leds = 2, .num_sensor_thresholds = 6, ++ .led_output_control_reg_base = CAP11XX_REG_LED_OUTPUT_CONTROL, ++ .sensor_input_reg_base = CAP11XX_REG_SENSOR_INPUT, + .has_gain = true, + .has_irq_config = true, ++ .has_repeat_en = true, + }; + + static const struct cap11xx_hw_model cap1188_model = { +- .product_id = 0x50, .num_channels = 8, .num_leds = 8, ++ .product_id = 0x50, ++ .num_channels = 8, .num_leds = 8, .num_sensor_thresholds = 8, ++ .led_output_control_reg_base = CAP11XX_REG_LED_OUTPUT_CONTROL, ++ .sensor_input_reg_base = CAP11XX_REG_SENSOR_INPUT, + .has_gain = true, + .has_irq_config = true, ++ .has_repeat_en = true, + }; + + static const struct cap11xx_hw_model cap1203_model = { +- .product_id = 0x6d, .num_channels = 3, .num_leds = 0, ++ .product_id = 0x6d, ++ .num_channels = 3, .num_leds = 0, .num_sensor_thresholds = 3, ++ .sensor_input_reg_base = CAP11XX_REG_SENSOR_INPUT, ++ .has_repeat_en = true, + }; + + static const struct cap11xx_hw_model cap1206_model = { +- .product_id = 0x67, .num_channels = 6, .num_leds = 0, ++ .product_id = 0x67, ++ .num_channels = 6, .num_leds = 0, .num_sensor_thresholds = 6, ++ .sensor_input_reg_base = CAP11XX_REG_SENSOR_INPUT, ++ .has_repeat_en = true, + }; + + static const struct cap11xx_hw_model cap1293_model = { +- .product_id = 0x6f, .num_channels = 3, .num_leds = 0, ++ .product_id = 0x6f, ++ .num_channels = 3, .num_leds = 0, .num_sensor_thresholds = 3, ++ .sensor_input_reg_base = CAP11XX_REG_SENSOR_INPUT, + .has_gain = true, ++ .has_repeat_en = true, + .has_sensitivity_control = true, + .has_signal_guard = true, + }; + + static const struct cap11xx_hw_model cap1298_model = { +- .product_id = 0x71, .num_channels = 8, .num_leds = 0, ++ .product_id = 0x71, ++ .num_channels = 8, .num_leds = 0, .num_sensor_thresholds = 8, ++ .sensor_input_reg_base = CAP11XX_REG_SENSOR_INPUT, + .has_gain = true, ++ .has_repeat_en = true, + .has_sensitivity_control = true, + .has_signal_guard = true, + }; + + static const struct of_device_id cap11xx_dt_ids[] = { + { .compatible = "microchip,cap1106", .data = &cap1106_model }, ++ { .compatible = "microchip,cap1114", .data = &cap1114_model }, + { .compatible = "microchip,cap1126", .data = &cap1126_model }, + { .compatible = "microchip,cap1188", .data = &cap1188_model }, + { .compatible = "microchip,cap1203", .data = &cap1203_model }, +@@ -652,6 +714,7 @@ MODULE_DEVICE_TABLE(of, cap11xx_dt_ids); + + static const struct i2c_device_id cap11xx_i2c_ids[] = { + { .name = "cap1106", .driver_data = (kernel_ulong_t)&cap1106_model }, ++ { .name = "cap1114", .driver_data = (kernel_ulong_t)&cap1114_model }, + { .name = "cap1126", .driver_data = (kernel_ulong_t)&cap1126_model }, + { .name = "cap1188", .driver_data = (kernel_ulong_t)&cap1188_model }, + { .name = "cap1203", .driver_data = (kernel_ulong_t)&cap1203_model }, +diff --git a/drivers/input/keyboard/charlieplex_keypad.c b/drivers/input/keyboard/charlieplex_keypad.c +--- a/drivers/input/keyboard/charlieplex_keypad.c ++++ b/drivers/input/keyboard/charlieplex_keypad.c +@@ -77,7 +77,9 @@ static int charlieplex_keypad_scan_line(struct charlieplex_keypad *keypad, + int err; + + /* Activate only one line as output at a time. */ +- gpiod_direction_output(line_gpios->desc[oline], 1); ++ err = gpiod_direction_output(line_gpios->desc[oline], 1); ++ if (err) ++ return err; + + if (keypad->settling_time_us) + fsleep(keypad->settling_time_us); +diff --git a/drivers/input/keyboard/hpps2atkbd.h b/drivers/input/keyboard/hpps2atkbd.h +deleted file mode 100644 +--- a/drivers/input/keyboard/hpps2atkbd.h ++++ /dev/null +@@ -1,110 +0,0 @@ +-/* +- * drivers/input/keyboard/hpps2atkbd.h +- * +- * Copyright (c) 2004 Helge Deller +- * Copyright (c) 2002 Laurent Canet +- * Copyright (c) 2002 Thibaut Varene +- * Copyright (c) 2000 Xavier Debacker +- * +- * HP PS/2 AT-compatible Keyboard, found in PA/RISC Workstations & Laptops +- * +- * This file is subject to the terms and conditions of the GNU General Public +- * License. See the file "COPYING" in the main directory of this archive +- * for more details. +- */ +- +- +-/* Is the keyboard an RDI PrecisionBook? */ +-#ifndef CONFIG_KEYBOARD_ATKBD_RDI_KEYCODES +-# define CONFLICT(x,y) x +-#else +-# define CONFLICT(x,y) y +-#endif +- +-/* sadly RDI (Tadpole) decided to ship a different keyboard layout +- than HP for their PS/2 laptop keyboard which leads to conflicting +- keycodes between a normal HP PS/2 keyboard and a RDI Precisionbook. +- HP: RDI: */ +-#define C_07 CONFLICT( KEY_F12, KEY_F1 ) +-#define C_11 CONFLICT( KEY_LEFTALT, KEY_LEFTCTRL ) +-#define C_14 CONFLICT( KEY_LEFTCTRL, KEY_CAPSLOCK ) +-#define C_58 CONFLICT( KEY_CAPSLOCK, KEY_RIGHTCTRL ) +-#define C_61 CONFLICT( KEY_102ND, KEY_LEFT ) +- +-/* Raw SET 2 scancode table */ +- +-/* 00 */ KEY_RESERVED, KEY_F9, KEY_RESERVED, KEY_F5, KEY_F3, KEY_F1, KEY_F2, C_07, +-/* 08 */ KEY_ESC, KEY_F10, KEY_F8, KEY_F6, KEY_F4, KEY_TAB, KEY_GRAVE, KEY_F2, +-/* 10 */ KEY_RESERVED, C_11, KEY_LEFTSHIFT, KEY_RESERVED, C_14, KEY_Q, KEY_1, KEY_F3, +-/* 18 */ KEY_RESERVED, KEY_LEFTALT, KEY_Z, KEY_S, KEY_A, KEY_W, KEY_2, KEY_F4, +-/* 20 */ KEY_RESERVED, KEY_C, KEY_X, KEY_D, KEY_E, KEY_4, KEY_3, KEY_F5, +-/* 28 */ KEY_RESERVED, KEY_SPACE, KEY_V, KEY_F, KEY_T, KEY_R, KEY_5, KEY_F6, +-/* 30 */ KEY_RESERVED, KEY_N, KEY_B, KEY_H, KEY_G, KEY_Y, KEY_6, KEY_F7, +-/* 38 */ KEY_RESERVED, KEY_RIGHTALT, KEY_M, KEY_J, KEY_U, KEY_7, KEY_8, KEY_F8, +-/* 40 */ KEY_RESERVED, KEY_COMMA, KEY_K, KEY_I, KEY_O, KEY_0, KEY_9, KEY_F9, +-/* 48 */ KEY_RESERVED, KEY_DOT, KEY_SLASH, KEY_L, KEY_SEMICOLON, KEY_P, KEY_MINUS, KEY_F10, +-/* 50 */ KEY_RESERVED, KEY_RESERVED, KEY_APOSTROPHE,KEY_RESERVED, KEY_LEFTBRACE, KEY_EQUAL, KEY_F11, KEY_SYSRQ, +-/* 58 */ C_58, KEY_RIGHTSHIFT,KEY_ENTER, KEY_RIGHTBRACE,KEY_BACKSLASH, KEY_BACKSLASH,KEY_F12, KEY_SCROLLLOCK, +-/* 60 */ KEY_DOWN, C_61, KEY_PAUSE, KEY_UP, KEY_DELETE, KEY_END, KEY_BACKSPACE, KEY_INSERT, +-/* 68 */ KEY_RESERVED, KEY_KP1, KEY_RIGHT, KEY_KP4, KEY_KP7, KEY_PAGEDOWN, KEY_HOME, KEY_PAGEUP, +-/* 70 */ KEY_KP0, KEY_KPDOT, KEY_KP2, KEY_KP5, KEY_KP6, KEY_KP8, KEY_ESC, KEY_NUMLOCK, +-/* 78 */ KEY_F11, KEY_KPPLUS, KEY_KP3, KEY_KPMINUS, KEY_KPASTERISK,KEY_KP9, KEY_SCROLLLOCK,KEY_102ND, +-/* 80 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 88 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 90 */ KEY_RESERVED, KEY_RIGHTALT, 255, KEY_RESERVED, KEY_RIGHTCTRL, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 98 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_CAPSLOCK, KEY_RESERVED, KEY_LEFTMETA, +-/* a0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RIGHTMETA, +-/* a8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_COMPOSE, +-/* b0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* b8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* c0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* c8 */ KEY_RESERVED, KEY_RESERVED, KEY_KPSLASH, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* d0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* d8 */ KEY_RESERVED, KEY_RESERVED, KEY_KPENTER, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* e0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* e8 */ KEY_RESERVED, KEY_END, KEY_RESERVED, KEY_LEFT, KEY_HOME, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* f0 */ KEY_INSERT, KEY_DELETE, KEY_DOWN, KEY_RESERVED, KEY_RIGHT, KEY_UP, KEY_RESERVED, KEY_PAUSE, +-/* f8 */ KEY_RESERVED, KEY_RESERVED, KEY_PAGEDOWN, KEY_RESERVED, KEY_SYSRQ, KEY_PAGEUP, KEY_RESERVED, KEY_RESERVED, +- +-/* These are offset for escaped keycodes: */ +- +-/* 00 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_F7, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 08 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 10 */ KEY_RESERVED, KEY_RIGHTALT, KEY_RESERVED, KEY_RESERVED, KEY_RIGHTCTRL, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 18 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 20 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 28 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 30 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 38 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 40 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 48 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 50 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 58 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 60 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 68 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 70 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 78 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 80 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 88 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 90 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 98 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* a0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* a8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* b0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* b8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* c0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* c8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* d0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* d8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* e0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* e8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* f0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* f8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED +- +-#undef CONFLICT +-#undef C_07 +-#undef C_11 +-#undef C_14 +-#undef C_58 +-#undef C_61 +- +diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c +--- a/drivers/input/keyboard/matrix_keypad.c ++++ b/drivers/input/keyboard/matrix_keypad.c +@@ -16,7 +16,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c +--- a/drivers/input/keyboard/pmic8xxx-keypad.c ++++ b/drivers/input/keyboard/pmic8xxx-keypad.c +@@ -462,15 +462,9 @@ static int pmic8xxx_kp_enable(struct pmic8xxx_kp *kp) + + static int pmic8xxx_kp_disable(struct pmic8xxx_kp *kp) + { +- int rc; +- + kp->ctrl_reg &= ~KEYP_CTRL_KEYP_EN; + +- rc = regmap_write(kp->regmap, KEYP_CTRL, kp->ctrl_reg); +- if (rc < 0) +- return rc; +- +- return rc; ++ return regmap_write(kp->regmap, KEYP_CTRL, kp->ctrl_reg); + } + + static int pmic8xxx_kp_open(struct input_dev *dev) +diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c +--- a/drivers/input/keyboard/samsung-keypad.c ++++ b/drivers/input/keyboard/samsung-keypad.c +@@ -142,7 +142,7 @@ static irqreturn_t samsung_keypad_irq(int irq, void *dev_id) + unsigned int row_state[SAMSUNG_MAX_COLS]; + bool key_down; + +- pm_runtime_get_sync(&keypad->pdev->dev); ++ guard(pm_runtime_active)(&keypad->pdev->dev); + + do { + readl(keypad->base + SAMSUNG_KEYIFSTSCLR); +@@ -158,8 +158,6 @@ static irqreturn_t samsung_keypad_irq(int irq, void *dev_id) + + } while (key_down && !keypad->stopped); + +- pm_runtime_put(&keypad->pdev->dev); +- + return IRQ_HANDLED; + } + +@@ -167,7 +165,7 @@ static void samsung_keypad_start(struct samsung_keypad *keypad) + { + unsigned int val; + +- pm_runtime_get_sync(&keypad->pdev->dev); ++ guard(pm_runtime_active)(&keypad->pdev->dev); + + /* Tell IRQ thread that it may poll the device. */ + keypad->stopped = false; +@@ -182,14 +180,14 @@ static void samsung_keypad_start(struct samsung_keypad *keypad) + /* KEYIFCOL reg clear. */ + writel(0, keypad->base + SAMSUNG_KEYIFCOL); + +- pm_runtime_put(&keypad->pdev->dev); ++ enable_irq(keypad->irq); + } + + static void samsung_keypad_stop(struct samsung_keypad *keypad) + { + unsigned int val; + +- pm_runtime_get_sync(&keypad->pdev->dev); ++ guard(pm_runtime_active)(&keypad->pdev->dev); + + /* Signal IRQ thread to stop polling and disable the handler. */ + keypad->stopped = true; +@@ -205,14 +203,6 @@ static void samsung_keypad_stop(struct samsung_keypad *keypad) + writel(val, keypad->base + SAMSUNG_KEYIFCON); + + clk_disable(keypad->clk); +- +- /* +- * Now that chip should not generate interrupts we can safely +- * re-enable the handler. +- */ +- enable_irq(keypad->irq); +- +- pm_runtime_put(&keypad->pdev->dev); + } + + static int samsung_keypad_open(struct input_dev *input_dev) +@@ -412,7 +402,8 @@ static int samsung_keypad_probe(struct platform_device *pdev) + } + + error = devm_request_threaded_irq(&pdev->dev, keypad->irq, NULL, +- samsung_keypad_irq, IRQF_ONESHOT, ++ samsung_keypad_irq, ++ IRQF_ONESHOT | IRQF_NO_AUTOEN, + dev_name(&pdev->dev), keypad); + if (error) { + dev_err(&pdev->dev, "failed to register keypad interrupt\n"); +@@ -492,15 +483,17 @@ static void samsung_keypad_toggle_wakeup(struct samsung_keypad *keypad, + + val = readl(keypad->base + SAMSUNG_KEYIFCON); + if (enable) { ++ enable_irq_wake(keypad->irq); + val |= SAMSUNG_KEYIFCON_WAKEUPEN; +- if (device_may_wakeup(&keypad->pdev->dev)) +- enable_irq_wake(keypad->irq); ++ writel(val, keypad->base + SAMSUNG_KEYIFCON); + } else { + val &= ~SAMSUNG_KEYIFCON_WAKEUPEN; +- if (device_may_wakeup(&keypad->pdev->dev)) +- disable_irq_wake(keypad->irq); ++ writel(val, keypad->base + SAMSUNG_KEYIFCON); ++ disable_irq_wake(keypad->irq); ++ ++ if (!input_device_enabled(keypad->input_dev)) ++ writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR); + } +- writel(val, keypad->base + SAMSUNG_KEYIFCON); + + clk_disable(keypad->clk); + } +@@ -516,7 +509,8 @@ static int samsung_keypad_suspend(struct device *dev) + if (input_device_enabled(input_dev)) + samsung_keypad_stop(keypad); + +- samsung_keypad_toggle_wakeup(keypad, true); ++ if (device_may_wakeup(dev)) ++ samsung_keypad_toggle_wakeup(keypad, true); + + return 0; + } +@@ -529,7 +523,8 @@ static int samsung_keypad_resume(struct device *dev) + + guard(mutex)(&input_dev->mutex); + +- samsung_keypad_toggle_wakeup(keypad, false); ++ if (device_may_wakeup(dev)) ++ samsung_keypad_toggle_wakeup(keypad, false); + + if (input_device_enabled(input_dev)) + samsung_keypad_start(keypad); +diff --git a/drivers/input/keyboard/snvs_pwrkey.c b/drivers/input/keyboard/snvs_pwrkey.c +--- a/drivers/input/keyboard/snvs_pwrkey.c ++++ b/drivers/input/keyboard/snvs_pwrkey.c +@@ -39,6 +39,9 @@ struct pwrkey_drv_data { + int keycode; + int keystate; /* 1:pressed */ + int wakeup; ++ bool suspended; /* Track suspend state */ ++ bool pending_press; /* Key pressed during suspend, report from timer callback */ ++ spinlock_t lock; /* Protects keystate, suspended and pending_press */ + struct timer_list check_timer; + struct input_dev *input; + u8 minor_rev; +@@ -49,14 +52,38 @@ static void imx_imx_snvs_check_for_events(struct timer_list *t) + struct pwrkey_drv_data *pdata = timer_container_of(pdata, t, + check_timer); + struct input_dev *input = pdata->input; ++ bool state_changed = false; ++ bool pending_press; + u32 state; + + regmap_read(pdata->snvs, SNVS_HPSR_REG, &state); + state = state & SNVS_HPSR_BTN ? 1 : 0; + +- /* only report new event if status changed */ +- if (state ^ pdata->keystate) { +- pdata->keystate = state; ++ scoped_guard(spinlock_irqsave, &pdata->lock) { ++ pending_press = pdata->pending_press; ++ if (pending_press) { ++ pdata->pending_press = false; ++ pdata->keystate = 1; ++ } ++ /* only report new event if status changed */ ++ if (state ^ pdata->keystate) { ++ pdata->keystate = state; ++ state_changed = true; ++ } ++ } ++ ++ /* ++ * Report a press event latched during suspend. If the key is still ++ * held, state_changed will be 0 (keystate already set to 1 above), ++ * so no duplicate press is reported. If already released, ++ * state_changed will fire next to report the release. ++ */ ++ if (pending_press) { ++ input_report_key(input, pdata->keycode, 1); ++ input_sync(input); ++ } ++ ++ if (state_changed) { + input_event(input, EV_KEY, pdata->keycode, state); + input_sync(input); + pm_relax(pdata->input->dev.parent); +@@ -92,8 +119,17 @@ static irqreturn_t imx_snvs_pwrkey_interrupt(int irq, void *dev_id) + input_sync(input); + pm_relax(input->dev.parent); + } else { ++ /* ++ * If the key is pressed during suspend, latch it so ++ * the timer callback can report the press event in ++ * softirq context, avoiding out-of-order events. ++ */ ++ scoped_guard(spinlock_irqsave, &pdata->lock) { ++ if (pdata->suspended) ++ pdata->pending_press = true; ++ } + mod_timer(&pdata->check_timer, +- jiffies + msecs_to_jiffies(DEBOUNCE_TIME)); ++ jiffies + msecs_to_jiffies(DEBOUNCE_TIME)); + } + } + +@@ -112,6 +148,7 @@ static void imx_snvs_pwrkey_act(void *pdata) + + static int imx_snvs_pwrkey_probe(struct platform_device *pdev) + { ++ struct device *dev = &pdev->dev; + struct pwrkey_drv_data *pdata; + struct input_dev *input; + struct device_node *np; +@@ -122,37 +159,35 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev) + u32 vid; + + /* Get SNVS register Page */ +- np = pdev->dev.of_node; ++ np = dev->of_node; + if (!np) +- return -ENODEV; ++ return dev_err_probe(dev, -ENODEV, "Device tree node not found\n"); + +- pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); ++ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->snvs = syscon_regmap_lookup_by_phandle(np, "regmap"); +- if (IS_ERR(pdata->snvs)) { +- dev_err(&pdev->dev, "Can't get snvs syscon\n"); +- return PTR_ERR(pdata->snvs); +- } ++ if (IS_ERR(pdata->snvs)) ++ return dev_err_probe(dev, PTR_ERR(pdata->snvs), "Can't get snvs syscon\n"); + + if (of_property_read_u32(np, "linux,keycode", &pdata->keycode)) { + pdata->keycode = KEY_POWER; +- dev_warn(&pdev->dev, "KEY_POWER without setting in dts\n"); ++ dev_warn(dev, "KEY_POWER without setting in dts\n"); + } + +- clk = devm_clk_get_optional_enabled(&pdev->dev, NULL); +- if (IS_ERR(clk)) { +- dev_err(&pdev->dev, "Failed to get snvs clock (%pe)\n", clk); +- return PTR_ERR(clk); +- } ++ clk = devm_clk_get_optional_enabled(dev, NULL); ++ if (IS_ERR(clk)) ++ return dev_err_probe(dev, PTR_ERR(clk), ++ "Failed to get snvs clock (%pe)\n", clk); + + pdata->wakeup = of_property_read_bool(np, "wakeup-source"); + + pdata->irq = platform_get_irq(pdev, 0); + if (pdata->irq < 0) +- return -EINVAL; ++ return pdata->irq; + ++ spin_lock_init(&pdata->lock); + error = of_property_read_u32(np, "power-off-time-sec", &val); + if (!error) { + switch (val) { +@@ -165,9 +200,8 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev) + bpt = (val / 5) - 1; + break; + default: +- dev_err(&pdev->dev, +- "power-off-time-sec %d out of range\n", val); +- return -EINVAL; ++ return dev_err_probe(dev, -EINVAL, ++ "power-off-time-sec %d out of range\n", val); + } + + regmap_update_bits(pdata->snvs, SNVS_LPCR_REG, SNVS_LPCR_BPT_MASK, +@@ -184,11 +218,9 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev) + + timer_setup(&pdata->check_timer, imx_imx_snvs_check_for_events, 0); + +- input = devm_input_allocate_device(&pdev->dev); +- if (!input) { +- dev_err(&pdev->dev, "failed to allocate the input device\n"); +- return -ENOMEM; +- } ++ input = devm_input_allocate_device(dev); ++ if (!input) ++ return dev_err_probe(dev, -ENOMEM, "failed to allocate the input device\n"); + + input->name = pdev->name; + input->phys = "snvs-pwrkey/input0"; +@@ -197,37 +229,57 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev) + input_set_capability(input, EV_KEY, pdata->keycode); + + /* input customer action to cancel release timer */ +- error = devm_add_action(&pdev->dev, imx_snvs_pwrkey_act, pdata); +- if (error) { +- dev_err(&pdev->dev, "failed to register remove action\n"); +- return error; +- } ++ error = devm_add_action(dev, imx_snvs_pwrkey_act, pdata); ++ if (error) ++ return dev_err_probe(dev, error, "failed to register remove action\n"); + + pdata->input = input; + platform_set_drvdata(pdev, pdata); + +- error = devm_request_irq(&pdev->dev, pdata->irq, +- imx_snvs_pwrkey_interrupt, +- 0, pdev->name, pdev); +- if (error) { +- dev_err(&pdev->dev, "interrupt not available.\n"); +- return error; +- } ++ error = devm_request_irq(dev, pdata->irq, ++ imx_snvs_pwrkey_interrupt, ++ 0, pdev->name, pdev); ++ if (error) ++ return dev_err_probe(dev, error, "interrupt not available.\n"); + + error = input_register_device(input); +- if (error < 0) { +- dev_err(&pdev->dev, "failed to register input device\n"); +- return error; +- } ++ if (error < 0) ++ return dev_err_probe(dev, error, "failed to register input device\n"); + +- device_init_wakeup(&pdev->dev, pdata->wakeup); +- error = dev_pm_set_wake_irq(&pdev->dev, pdata->irq); ++ device_init_wakeup(dev, pdata->wakeup); ++ error = dev_pm_set_wake_irq(dev, pdata->irq); + if (error) +- dev_err(&pdev->dev, "irq wake enable failed.\n"); ++ dev_err(dev, "irq wake enable failed.\n"); + + return 0; + } + ++static int imx_snvs_pwrkey_suspend(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev); ++ ++ guard(spinlock_irq)(&pdata->lock); ++ pdata->suspended = true; ++ ++ return 0; ++} ++ ++static int imx_snvs_pwrkey_resume(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev); ++ ++ guard(spinlock_irq)(&pdata->lock); ++ pdata->suspended = false; ++ ++ return 0; ++} ++ ++static DEFINE_SIMPLE_DEV_PM_OPS(imx_snvs_pwrkey_pm_ops, ++ imx_snvs_pwrkey_suspend, ++ imx_snvs_pwrkey_resume); ++ + static const struct of_device_id imx_snvs_pwrkey_ids[] = { + { .compatible = "fsl,sec-v4.0-pwrkey" }, + { /* sentinel */ } +@@ -238,6 +290,7 @@ static struct platform_driver imx_snvs_pwrkey_driver = { + .driver = { + .name = "snvs_pwrkey", + .of_match_table = imx_snvs_pwrkey_ids, ++ .pm = pm_ptr(&imx_snvs_pwrkey_pm_ops), + }, + .probe = imx_snvs_pwrkey_probe, + }; +diff --git a/drivers/input/keyboard/tca8418_keypad.c b/drivers/input/keyboard/tca8418_keypad.c +--- a/drivers/input/keyboard/tca8418_keypad.c ++++ b/drivers/input/keyboard/tca8418_keypad.c +@@ -254,7 +254,8 @@ static int tca8418_configure(struct tca8418_keypad *keypad_data, + return error; + + error = tca8418_write_byte(keypad_data, REG_CFG, +- CFG_INT_CFG | CFG_OVR_FLOW_IEN | CFG_KE_IEN); ++ CFG_INT_CFG | CFG_OVR_FLOW_IEN | ++ CFG_OVR_FLOW_M | CFG_KE_IEN); + + return error; + } +diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig +--- a/drivers/input/misc/Kconfig ++++ b/drivers/input/misc/Kconfig +@@ -842,6 +842,18 @@ config INPUT_IQS7222 + To compile this driver as a module, choose M here: the + module will be called iqs7222. + ++config INPUT_ISA1200_HAPTIC ++ tristate "Imagis ISA1200 haptic feedback unit" ++ depends on I2C ++ select INPUT_FF_MEMLESS ++ select REGMAP_I2C ++ help ++ Say Y to enable support for the Imagis ISA1200 haptic ++ feedback unit. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called isa1200. ++ + config INPUT_CMA3000 + tristate "VTI CMA3000 Tri-axis accelerometer" + help +diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile +--- a/drivers/input/misc/Makefile ++++ b/drivers/input/misc/Makefile +@@ -49,6 +49,7 @@ obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o + obj-$(CONFIG_INPUT_IQS269A) += iqs269a.o + obj-$(CONFIG_INPUT_IQS626A) += iqs626a.o + obj-$(CONFIG_INPUT_IQS7222) += iqs7222.o ++obj-$(CONFIG_INPUT_ISA1200_HAPTIC) += isa1200.o + obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o + obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o + obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o +diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c +--- a/drivers/input/misc/ims-pcu.c ++++ b/drivers/input/misc/ims-pcu.c +@@ -2234,6 +2234,7 @@ static const struct usb_device_id ims_pcu_id_table[] = { + }, + { } + }; ++MODULE_DEVICE_TABLE(usb, ims_pcu_id_table); + + static const struct attribute_group *ims_pcu_sysfs_groups[] = { + &ims_pcu_attr_group, +diff --git a/drivers/input/misc/isa1200.c b/drivers/input/misc/isa1200.c +new file mode 100644 +--- /dev/null ++++ b/drivers/input/misc/isa1200.c +@@ -0,0 +1,533 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * System control (LDO regulator) ++ * ++ * LDO voltage to register mapping is linear, but it is split in two parts: ++ * 2.3V - 3.0V map to 0x08 - 0x0f; 3.1V - 3.8V map to 0x00 - 0x7 ++ */ ++ ++#define ISA1200_SCTRL 0x00 ++#define ISA1200_LDO_VOLTAGE_BASE 0x08 ++#define ISA1200_LDO_VOLTAGE_STEP 100000 ++#define ISA1200_LDO_VOLTAGE_2V3 23 ++#define ISA1200_LDO_VOLTAGE_3V1 31 ++#define ISA1200_LDO_VOLTAGE_MIN 2300000 ++#define ISA1200_LDO_VOLTAGE_MAX 3800000 ++ ++/* ++ * The output frequency is calculated with this formula: ++ * ++ * base clock frequency ++ * fout = ----------------------------------------- ++ * (128 - PWM_FREQ) * 2 * PLLDIV * PWM_PERIOD ++ * ++ * The base clock frequency is the clock frequency provided on the ++ * clock input to the chip, divided by the value in HCTRL0 ++ * ++ * PWM_FREQ is configured in register HCTRL4, it is common to set this ++ * to 0 to get only two variables to calculate. ++ * ++ * PLLDIV is configured in register HCTRL3 (bits 7..4, so 0..15) ++ * PWM_PERIOD is configured in register HCTRL6 ++ * Further the duty cycle can be configured in HCTRL5 ++ */ ++ ++/* ++ * HCTRL0 configures clock or PWM input and selects the divider for ++ * the clock input. ++ */ ++#define ISA1200_HCTRL0 0x30 ++#define ISA1200_HCTRL0_HAP_ENABLE BIT(7) ++#define ISA1200_HCTRL0_PWM_GEN_MODE BIT(4) ++#define ISA1200_HCTRL0_PWM_INPUT_MODE BIT(3) ++#define ISA1200_HCTRL0_CLKDIV_128 128 ++ ++/* ++ * HCTRL1 configures the motor type and clock sourse ++ */ ++#define ISA1200_HCTRL1 0x31 ++#define ISA1200_HCTRL1_EXT_CLOCK BIT(7) ++#define ISA1200_HCTRL1_DAC_INVERT BIT(6) ++#define ISA1200_HCTRL1_MODE(n) (((n) & 1) << 5) ++ ++/* HCTRL2 controls software reset of the chip */ ++#define ISA1200_HCTRL2 0x32 ++#define ISA1200_HCTRL2_SW_RESET BIT(0) ++ ++/* ++ * HCTRL3 controls the PLL divisor ++ * ++ * Bits [0,1] are always set to 1 (we don't know what they are ++ * used for) and bit 4 and upward control the PLL divisor. ++ */ ++#define ISA1200_HCTRL3 0x33 ++#define ISA1200_HCTRL3_DEFAULT 0x03 ++#define ISA1200_HCTRL3_PLLDIV(n) (((n) & 0xf) << 4) ++ ++/* HCTRL4 controls the PWM frequency of external channel */ ++#define ISA1200_HCTRL4 0x34 ++ ++/* HCTRL5 controls the PWM high duty cycle of internal channel */ ++#define ISA1200_HCTRL5 0x35 ++ ++/* HCTRL6 controls the PWM period of internal channel */ ++#define ISA1200_HCTRL6 0x36 ++#define ISA1200_HCTRL6_PERIOD_SCALE 100 ++ ++/* The use for these registers is unknown but they exist */ ++#define ISA1200_HCTRL7 0x37 ++#define ISA1200_HCTRL8 0x38 ++#define ISA1200_HCTRL9 0x39 ++#define ISA1200_HCTRLA 0x3a ++#define ISA1200_HCTRLB 0x3b ++#define ISA1200_HCTRLC 0x3c ++#define ISA1200_HCTRLD 0x3d ++ ++#define ISA1200_EN_PINS_MAX 2 ++ ++static const struct regulator_bulk_data isa1200_supplies[] = { ++ { .supply = "vdd" }, { .supply = "vddp" }, ++}; ++ ++struct isa1200_config { ++ u32 ldo_voltage; ++ u32 mode; ++ u32 clkdiv; ++ u32 plldiv; ++ u32 freq; ++ u32 period; ++ u32 duty; ++}; ++ ++struct isa1200 { ++ struct input_dev *input; ++ struct regmap *map; ++ ++ struct clk *clk; ++ struct pwm_device *pwm; ++ struct gpio_descs *enable_gpios; ++ struct regulator_bulk_data *supplies; ++ ++ struct work_struct play_work; ++ struct isa1200_config config; ++ ++ int level; ++ bool suspended; ++ bool active; ++}; ++ ++static const struct regmap_config isa1200_regmap_config = { ++ .reg_bits = 8, ++ .val_bits = 8, ++ .max_register = ISA1200_HCTRLD, ++}; ++ ++static void isa1200_start(struct isa1200 *isa) ++{ ++ struct isa1200_config *config = &isa->config; ++ struct device *dev = &isa->input->dev; ++ struct pwm_state state; ++ u8 hctrl0 = 0, hctrl1 = 0; ++ DECLARE_BITMAP(values, ISA1200_EN_PINS_MAX); ++ int err; ++ ++ if (!isa->active) { ++ err = regulator_bulk_enable(ARRAY_SIZE(isa1200_supplies), ++ isa->supplies); ++ if (err) { ++ dev_err(dev, "failed to enable supplies (%d)\n", err); ++ return; ++ } ++ ++ err = clk_prepare_enable(isa->clk); ++ if (err) { ++ dev_err(dev, "failed to enable clock (%d)\n", err); ++ regulator_bulk_disable(ARRAY_SIZE(isa1200_supplies), ++ isa->supplies); ++ return; ++ } ++ ++ bitmap_fill(values, ISA1200_EN_PINS_MAX); ++ gpiod_multi_set_value_cansleep(isa->enable_gpios, values); ++ ++ usleep_range(200, 300); ++ } ++ ++ regmap_write(isa->map, ISA1200_SCTRL, config->ldo_voltage); ++ ++ if (isa->clk) { ++ hctrl0 = ISA1200_HCTRL0_PWM_GEN_MODE; ++ hctrl1 = ISA1200_HCTRL1_EXT_CLOCK; ++ } ++ ++ if (isa->pwm) { ++ hctrl0 = ISA1200_HCTRL0_PWM_INPUT_MODE; ++ hctrl1 = 0; ++ } ++ ++ hctrl0 |= __ffs(config->clkdiv / ISA1200_HCTRL0_CLKDIV_128); ++ hctrl1 |= ISA1200_HCTRL1_DAC_INVERT; ++ hctrl1 |= ISA1200_HCTRL1_MODE(config->mode); ++ ++ regmap_write(isa->map, ISA1200_HCTRL0, hctrl0); ++ regmap_write(isa->map, ISA1200_HCTRL1, hctrl1); ++ ++ /* Make sure to de-assert software reset */ ++ regmap_write(isa->map, ISA1200_HCTRL2, 0x00); ++ ++ /* PLL divisor */ ++ regmap_write(isa->map, ISA1200_HCTRL3, ++ ISA1200_HCTRL3_PLLDIV(config->plldiv) | ++ ISA1200_HCTRL3_DEFAULT); ++ ++ /* Frequency */ ++ regmap_write(isa->map, ISA1200_HCTRL4, config->freq); ++ /* Duty cycle */ ++ regmap_write(isa->map, ISA1200_HCTRL5, config->period >> 1); ++ /* Period */ ++ regmap_write(isa->map, ISA1200_HCTRL6, config->period); ++ ++ hctrl0 |= ISA1200_HCTRL0_HAP_ENABLE; ++ regmap_write(isa->map, ISA1200_HCTRL0, hctrl0); ++ ++ if (isa->clk) ++ regmap_write(isa->map, ISA1200_HCTRL5, config->duty); ++ ++ if (isa->pwm) { ++ pwm_get_state(isa->pwm, &state); ++ state.duty_cycle = config->duty; ++ state.enabled = true; ++ pwm_apply_might_sleep(isa->pwm, &state); ++ } ++ ++ isa->active = true; ++} ++ ++static void isa1200_stop(struct isa1200 *isa) ++{ ++ struct pwm_state state; ++ DECLARE_BITMAP(values, ISA1200_EN_PINS_MAX); ++ ++ if (!isa->active) ++ return; ++ ++ if (isa->pwm) { ++ pwm_get_state(isa->pwm, &state); ++ state.duty_cycle = 0; ++ state.enabled = false; ++ pwm_apply_might_sleep(isa->pwm, &state); ++ } ++ ++ regmap_write(isa->map, ISA1200_HCTRL0, 0x00); ++ ++ bitmap_zero(values, ISA1200_EN_PINS_MAX); ++ gpiod_multi_set_value_cansleep(isa->enable_gpios, values); ++ ++ clk_disable_unprepare(isa->clk); ++ regulator_bulk_disable(ARRAY_SIZE(isa1200_supplies), ++ isa->supplies); ++ ++ isa->active = false; ++} ++ ++static void isa1200_play_work(struct work_struct *work) ++{ ++ struct isa1200 *isa = container_of(work, struct isa1200, play_work); ++ ++ if (!READ_ONCE(isa->suspended)) { ++ if (isa->level) ++ isa1200_start(isa); ++ else ++ isa1200_stop(isa); ++ } ++} ++ ++static int isa1200_vibrator_play_effect(struct input_dev *input, void *data, ++ struct ff_effect *effect) ++{ ++ struct isa1200 *isa = input_get_drvdata(input); ++ int level; ++ ++ /* ++ * TODO: we currently only support rumble. ++ * The ISA1200 can control two motors and some devices ++ * also have two motors mounted. ++ */ ++ level = effect->u.rumble.strong_magnitude; ++ if (!level) ++ level = effect->u.rumble.weak_magnitude; ++ ++ dev_dbg(&input->dev, "FF effect type %d level %d\n", ++ effect->type, level); ++ ++ if (isa->level != level) { ++ isa->level = level; ++ if (!READ_ONCE(isa->suspended)) ++ schedule_work(&isa->play_work); ++ } ++ ++ return 0; ++} ++ ++static void isa1200_vibrator_close(struct input_dev *input) ++{ ++ struct isa1200 *isa = input_get_drvdata(input); ++ ++ cancel_work_sync(&isa->play_work); ++ isa1200_stop(isa); ++ isa->level = 0; ++} ++ ++static int isa1200_of_probe(struct i2c_client *client) ++{ ++ struct isa1200 *isa = i2c_get_clientdata(client); ++ struct isa1200_config *config = &isa->config; ++ struct device *dev = &client->dev; ++ struct fwnode_handle *ldo_node; ++ int err; ++ ++ isa->clk = devm_clk_get_optional(dev, NULL); ++ if (IS_ERR(isa->clk)) ++ return dev_err_probe(dev, PTR_ERR(isa->clk), ++ "failed to get clock\n"); ++ ++ isa->pwm = devm_pwm_get(dev, NULL); ++ if (IS_ERR(isa->pwm)) { ++ err = PTR_ERR(isa->pwm); ++ if (err == -ENODEV || err == -EINVAL) ++ isa->pwm = NULL; ++ else ++ return dev_err_probe(dev, err, "getting PWM\n"); ++ } ++ ++ if (!isa->clk && !isa->pwm) ++ return dev_err_probe(dev, -EINVAL, ++ "clock or PWM are required, none were provided\n"); ++ ++ err = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(isa1200_supplies), ++ isa1200_supplies, &isa->supplies); ++ if (err) ++ return dev_err_probe(dev, err, "failed to get supplies\n"); ++ ++ isa->enable_gpios = devm_gpiod_get_array_optional(dev, "control", ++ GPIOD_OUT_LOW); ++ if (IS_ERR(isa->enable_gpios)) ++ return dev_err_probe(dev, PTR_ERR(isa->enable_gpios), ++ "failed to get enable gpios\n"); ++ ++ if (isa->enable_gpios && isa->enable_gpios->ndescs > ISA1200_EN_PINS_MAX) ++ return dev_err_probe(dev, -EINVAL, "too many enable gpios\n"); ++ ++ ldo_node = device_get_named_child_node(dev, "ldo"); ++ if (!ldo_node) ++ return dev_err_probe(dev, -ENODEV, ++ "failed to get embedded LDO node\n"); ++ ++ err = fwnode_property_read_u32(ldo_node, "regulator-min-microvolt", ++ &config->ldo_voltage); ++ fwnode_handle_put(ldo_node); ++ if (err) ++ return dev_err_probe(dev, err, ++ "failed to get ldo voltage\n"); ++ ++ config->ldo_voltage = clamp(config->ldo_voltage, ++ ISA1200_LDO_VOLTAGE_MIN, ++ ISA1200_LDO_VOLTAGE_MAX); ++ ++ config->ldo_voltage /= ISA1200_LDO_VOLTAGE_STEP; ++ if (config->ldo_voltage < ISA1200_LDO_VOLTAGE_3V1) ++ config->ldo_voltage = config->ldo_voltage - ++ ISA1200_LDO_VOLTAGE_2V3 + ++ ISA1200_LDO_VOLTAGE_BASE; ++ else ++ config->ldo_voltage -= ISA1200_LDO_VOLTAGE_3V1; ++ ++ config->mode = 0; /* LRA_MODE */ ++ device_property_read_u32(dev, "imagis,mode", &config->mode); ++ ++ config->clkdiv = ISA1200_HCTRL0_CLKDIV_128; ++ device_property_read_u32(dev, "imagis,clk-div", &config->clkdiv); ++ if (!config->clkdiv) ++ return dev_err_probe(dev, -EINVAL, "clk-div cannot be zero\n"); ++ ++ config->clkdiv = clamp(config->clkdiv, ISA1200_HCTRL0_CLKDIV_128, ++ ISA1200_HCTRL0_CLKDIV_128 << 3); ++ ++ err = device_property_read_u32(dev, "imagis,pll-div", &config->plldiv); ++ if (err || !config->plldiv) ++ config->plldiv = 1; ++ ++ config->period = 0; ++ config->freq = 0; ++ config->duty = 0; ++ ++ if (isa->clk) { ++ err = device_property_read_u32(dev, "imagis,period-ns", ++ &config->period); ++ if (err) ++ return dev_err_probe(dev, err, ++ "failed to get period\n"); ++ ++ /* ++ * TODO: The scale value is arbitrary, but it fits observations ++ * quite well, and the exact conversion method is unknown. ++ * The period property value returned above is the HCTRL6 ++ * register value set by the vendor code, multiplied by 100. ++ */ ++ config->period /= ISA1200_HCTRL6_PERIOD_SCALE; ++ config->duty = config->period >> 1; ++ } ++ ++ if (isa->pwm) { ++ struct pwm_state state; ++ ++ pwm_init_state(isa->pwm, &state); ++ ++ if (!state.period) ++ return dev_err_probe(dev, -EINVAL, ++ "PWM period cannot be zero\n"); ++ ++ config->freq = div64_u64(NANO, state.period * config->clkdiv); ++ config->duty = state.period >> 1; ++ ++ err = pwm_apply_might_sleep(isa->pwm, &state); ++ if (err) ++ return dev_err_probe(dev, err, ++ "failed to apply initial PWM state\n"); ++ } ++ ++ /* ++ * TODO: If device is using a clock, this property should return the ++ * value written to the HCTRL5 register by downstrem code. It likely ++ * needs to be converted into a meaningful duty cycle value, though ++ * unfortunately the exact conversion mechanism is unknown. If the ++ * device uses PWM, this property will return the correct duty cycle ++ * in nanoseconds. ++ */ ++ device_property_read_u32(dev, "imagis,duty-cycle-ns", &config->duty); ++ ++ return 0; ++} ++ ++static int isa1200_probe(struct i2c_client *client) ++{ ++ struct isa1200 *isa; ++ struct device *dev = &client->dev; ++ int err; ++ ++ isa = devm_kzalloc(dev, sizeof(*isa), GFP_KERNEL); ++ if (!isa) ++ return -ENOMEM; ++ ++ isa->input = devm_input_allocate_device(dev); ++ if (!isa->input) ++ return -ENOMEM; ++ ++ i2c_set_clientdata(client, isa); ++ ++ err = isa1200_of_probe(client); ++ if (err) ++ return err; ++ ++ isa->map = devm_regmap_init_i2c(client, &isa1200_regmap_config); ++ if (IS_ERR(isa->map)) ++ return dev_err_probe(dev, PTR_ERR(isa->map), ++ "failed to initialize register map\n"); ++ ++ INIT_WORK(&isa->play_work, isa1200_play_work); ++ ++ isa->input->name = "isa1200-haptic"; ++ isa->input->id.bustype = BUS_I2C; ++ isa->input->close = isa1200_vibrator_close; ++ ++ isa->active = false; ++ ++ input_set_drvdata(isa->input, isa); ++ ++ /* TODO: this hardware can likely support more than rumble */ ++ input_set_capability(isa->input, EV_FF, FF_RUMBLE); ++ ++ err = input_ff_create_memless(isa->input, NULL, ++ isa1200_vibrator_play_effect); ++ if (err) ++ return dev_err_probe(dev, err, "failed to create FF dev\n"); ++ ++ err = input_register_device(isa->input); ++ if (err) ++ return dev_err_probe(dev, err, "failed to register input dev\n"); ++ ++ return 0; ++} ++ ++static int isa1200_suspend(struct device *dev) ++{ ++ struct isa1200 *isa = dev_get_drvdata(dev); ++ ++ guard(mutex)(&isa->input->mutex); ++ ++ if (input_device_enabled(isa->input)) { ++ WRITE_ONCE(isa->suspended, true); ++ cancel_work_sync(&isa->play_work); ++ isa1200_stop(isa); ++ } ++ ++ return 0; ++} ++ ++static int isa1200_resume(struct device *dev) ++{ ++ struct isa1200 *isa = dev_get_drvdata(dev); ++ ++ guard(mutex)(&isa->input->mutex); ++ ++ if (input_device_enabled(isa->input)) { ++ WRITE_ONCE(isa->suspended, false); ++ if (isa->level) ++ schedule_work(&isa->play_work); ++ } ++ ++ return 0; ++} ++ ++static DEFINE_SIMPLE_DEV_PM_OPS(isa1200_pm_ops, isa1200_suspend, isa1200_resume); ++ ++static const struct of_device_id isa1200_of_match[] = { ++ { .compatible = "imagis,isa1200" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, isa1200_of_match); ++ ++static struct i2c_driver isa1200_i2c_driver = { ++ .driver = { ++ .name = "isa1200", ++ .of_match_table = isa1200_of_match, ++ .pm = pm_sleep_ptr(&isa1200_pm_ops), ++ }, ++ .probe = isa1200_probe, ++}; ++module_i2c_driver(isa1200_i2c_driver); ++ ++MODULE_AUTHOR("Linus Walleij "); ++MODULE_AUTHOR("Svyatoslav Ryhel "); ++MODULE_DESCRIPTION("Imagis ISA1200 haptic feedback unit"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/input/mouse/elan_i2c.h b/drivers/input/mouse/elan_i2c.h +--- a/drivers/input/mouse/elan_i2c.h ++++ b/drivers/input/mouse/elan_i2c.h +@@ -102,7 +102,8 @@ struct elan_transport_ops { + int (*prepare_fw_update)(struct i2c_client *client, u16 ic_type, + u8 iap_version, u16 fw_page_size); + int (*write_fw_block)(struct i2c_client *client, u16 fw_page_size, +- const u8 *page, u16 checksum, int idx); ++ u16 fw_page_delay, const u8 *page, u16 checksum, ++ int idx); + int (*finish_fw_update)(struct i2c_client *client, + struct completion *reset_done); + +diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c +--- a/drivers/input/mouse/elan_i2c_core.c ++++ b/drivers/input/mouse/elan_i2c_core.c +@@ -16,27 +16,27 @@ + */ + + #include ++#include + #include + #include + #include + #include + #include ++#include + #include + #include + #include +-#include +-#include +-#include +-#include +-#include +-#include +-#include + #include +-#include ++#include ++#include + #include + #include + #include + #include ++#include ++#include ++#include ++#include + #include + + #include "elan_i2c.h" +@@ -86,6 +86,7 @@ struct elan_tp_data { + u16 ic_type; + u16 fw_validpage_count; + u16 fw_page_size; ++ u16 fw_page_delay; + u32 fw_signature_address; + + u8 min_baseline; +@@ -127,8 +128,11 @@ static u32 elan_i2c_lookup_quirks(u16 ic_type, u16 product_id) + } + + static int elan_get_fwinfo(u16 ic_type, u8 iap_version, u16 *validpage_count, +- u32 *signature_address, u16 *page_size) ++ u32 *signature_address, u16 *page_size, ++ u16 *page_delay) + { ++ *page_delay = 30; ++ + switch (ic_type) { + case 0x00: + case 0x06: +@@ -164,6 +168,7 @@ static int elan_get_fwinfo(u16 ic_type, u8 iap_version, u16 *validpage_count, + break; + case 0x19: + *validpage_count = 2032; ++ *page_delay = 10; + break; + default: + /* unknown ic type clear value */ +@@ -179,6 +184,7 @@ static int elan_get_fwinfo(u16 ic_type, u8 iap_version, u16 *validpage_count, + if ((ic_type == 0x14 || ic_type == 0x15) && iap_version >= 2) { + *validpage_count /= 8; + *page_size = ETP_FW_PAGE_SIZE_512; ++ *page_delay = 50; + } else if (ic_type >= 0x0D && iap_version >= 1) { + *validpage_count /= 2; + *page_size = ETP_FW_PAGE_SIZE_128; +@@ -368,7 +374,8 @@ static int elan_query_device_info(struct elan_tp_data *data) + error = elan_get_fwinfo(data->ic_type, data->iap_version, + &data->fw_validpage_count, + &data->fw_signature_address, +- &data->fw_page_size); ++ &data->fw_page_size, ++ &data->fw_page_delay); + if (error) + dev_warn(&data->client->dev, + "unexpected iap version %#04x (ic type: %#04x), firmware update will not work\n", +@@ -479,14 +486,16 @@ static int elan_query_device_parameters(struct elan_tp_data *data) + ********************************************************** + */ + static int elan_write_fw_block(struct elan_tp_data *data, u16 page_size, +- const u8 *page, u16 checksum, int idx) ++ u16 page_delay, const u8 *page, u16 checksum, ++ int idx) + { + int retry = ETP_RETRY_COUNT; + int error; + + do { + error = data->ops->write_fw_block(data->client, page_size, +- page, checksum, idx); ++ page_delay, page, checksum, ++ idx); + if (!error) + return 0; + +@@ -525,7 +534,8 @@ static int __elan_update_firmware(struct elan_tp_data *data, + checksum += ((page[j + 1] << 8) | page[j]); + + error = elan_write_fw_block(data, data->fw_page_size, +- page, checksum, i); ++ data->fw_page_delay, page, checksum, ++ i); + if (error) { + dev_err(dev, "write page %d fail: %d\n", i, error); + return error; +diff --git a/drivers/input/mouse/elan_i2c_i2c.c b/drivers/input/mouse/elan_i2c_i2c.c +--- a/drivers/input/mouse/elan_i2c_i2c.c ++++ b/drivers/input/mouse/elan_i2c_i2c.c +@@ -625,7 +625,8 @@ static int elan_i2c_prepare_fw_update(struct i2c_client *client, u16 ic_type, + } + + static int elan_i2c_write_fw_block(struct i2c_client *client, u16 fw_page_size, +- const u8 *page, u16 checksum, int idx) ++ u16 fw_page_delay, const u8 *page, u16 checksum, ++ int idx) + { + struct device *dev = &client->dev; + u8 val[3]; +@@ -650,7 +651,7 @@ static int elan_i2c_write_fw_block(struct i2c_client *client, u16 fw_page_size, + } + + /* Wait for F/W to update one page ROM data. */ +- msleep(fw_page_size == ETP_FW_PAGE_SIZE_512 ? 50 : 35); ++ msleep(fw_page_delay); + + error = elan_i2c_read_cmd(client, ETP_I2C_IAP_CTRL_CMD, val); + if (error) { +diff --git a/drivers/input/mouse/elan_i2c_smbus.c b/drivers/input/mouse/elan_i2c_smbus.c +--- a/drivers/input/mouse/elan_i2c_smbus.c ++++ b/drivers/input/mouse/elan_i2c_smbus.c +@@ -416,7 +416,8 @@ static int elan_smbus_prepare_fw_update(struct i2c_client *client, u16 ic_type, + + + static int elan_smbus_write_fw_block(struct i2c_client *client, u16 fw_page_size, +- const u8 *page, u16 checksum, int idx) ++ u16 fw_page_delay, const u8 *page, u16 checksum, ++ int idx) + { + struct device *dev = &client->dev; + int error; +diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c +--- a/drivers/input/mouse/focaltech.c ++++ b/drivers/input/mouse/focaltech.c +@@ -78,8 +78,8 @@ struct focaltech_finger_state { + * Absolute position (from the bottom left corner) of the + * finger. + */ +- unsigned int x; +- unsigned int y; ++ int x; ++ int y; + }; + + /* +@@ -108,7 +108,7 @@ struct focaltech_hw_state { + }; + + struct focaltech_data { +- unsigned int x_max, y_max; ++ int x_max, y_max; + struct focaltech_hw_state state; + }; + +@@ -126,17 +126,16 @@ static void focaltech_report_state(struct psmouse *psmouse) + input_mt_slot(dev, i); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); + if (active) { +- unsigned int clamped_x, clamped_y; + /* + * The touchpad might report invalid data, so we clamp + * the resulting values so that we do not confuse +- * userspace. ++ * userspace or accumulate coordinate wind-up. + */ +- clamped_x = clamp(finger->x, 0U, priv->x_max); +- clamped_y = clamp(finger->y, 0U, priv->y_max); +- input_report_abs(dev, ABS_MT_POSITION_X, clamped_x); ++ finger->x = clamp(finger->x, 0, priv->x_max); ++ finger->y = clamp(finger->y, 0, priv->y_max); ++ input_report_abs(dev, ABS_MT_POSITION_X, finger->x); + input_report_abs(dev, ABS_MT_POSITION_Y, +- priv->y_max - clamped_y); ++ priv->y_max - finger->y); + input_report_abs(dev, ABS_TOOL_WIDTH, state->width); + } + } +diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c +--- a/drivers/input/mouse/psmouse-base.c ++++ b/drivers/input/mouse/psmouse-base.c +@@ -256,9 +256,8 @@ static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_sta + */ + void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) + { +- serio_pause_rx(psmouse->ps2dev.serio); ++ guard(serio_pause_rx)(psmouse->ps2dev.serio); + __psmouse_set_state(psmouse, new_state); +- serio_continue_rx(psmouse->ps2dev.serio); + } + + /* +@@ -267,7 +266,15 @@ void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) + */ + static int psmouse_handle_byte(struct psmouse *psmouse) + { +- psmouse_ret_t rc = psmouse->protocol_handler(psmouse); ++ psmouse_ret_t rc; ++ ++ /* protocol_handler is NULL when device is being disconnected */ ++ if (unlikely(!psmouse->protocol_handler)) { ++ psmouse->pktcnt = 0; ++ return 0; ++ } ++ ++ rc = psmouse->protocol_handler(psmouse); + + switch (rc) { + case PSMOUSE_BAD_DATA: +@@ -484,13 +491,16 @@ static int psmouse_poll(struct psmouse *psmouse) + PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)); + } + +-static bool psmouse_check_pnp_id(const char *id, const char * const ids[]) ++static bool psmouse_check_pnp_id(const char *p, const char * const ids[]) + { +- int i; ++ const char * const *id; ++ size_t len; + +- for (i = 0; ids[i]; i++) +- if (!strcasecmp(id, ids[i])) ++ for (id = ids; *id; id++) { ++ len = strlen(*id); ++ if (!strncasecmp(p, *id, len) && (p[len] == ' ' || p[len] == '\0')) + return true; ++ } + + return false; + } +@@ -501,28 +511,26 @@ static bool psmouse_check_pnp_id(const char *id, const char * const ids[]) + bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[]) + { + struct serio *serio = psmouse->ps2dev.serio; +- char *p, *fw_id_copy, *save_ptr; +- bool found = false; ++ const char *p = serio->firmware_id; + +- if (strncmp(serio->firmware_id, "PNP: ", 5)) ++ if (!strstarts(p, "PNP: ")) + return false; + +- fw_id_copy = kstrndup(&serio->firmware_id[5], +- sizeof(serio->firmware_id) - 5, +- GFP_KERNEL); +- if (!fw_id_copy) +- return false; ++ p += 5; ++ while (*p) { ++ p = skip_spaces(p); ++ if (!*p) ++ break; + +- save_ptr = fw_id_copy; +- while ((p = strsep(&fw_id_copy, " ")) != NULL) { +- if (psmouse_check_pnp_id(p, ids)) { +- found = true; ++ if (psmouse_check_pnp_id(p, ids)) ++ return true; ++ ++ p = strchr(p, ' '); ++ if (!p) + break; +- } + } + +- kfree(save_ptr); +- return found; ++ return false; + } + + /* +@@ -1311,10 +1319,10 @@ static void psmouse_resync(struct work_struct *work) + bool failed = false, enabled = false; + int i; + +- mutex_lock(&psmouse_mutex); ++ guard(mutex)(&psmouse_mutex); + + if (psmouse->state != PSMOUSE_RESYNCING) +- goto out; ++ return; + + if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { + parent = psmouse_from_serio(serio->parent); +@@ -1392,8 +1400,6 @@ static void psmouse_resync(struct work_struct *work) + + if (parent) + psmouse_activate(parent); +- out: +- mutex_unlock(&psmouse_mutex); + } + + /* +@@ -1404,7 +1410,7 @@ static void psmouse_cleanup(struct serio *serio) + struct psmouse *psmouse = psmouse_from_serio(serio); + struct psmouse *parent = NULL; + +- mutex_lock(&psmouse_mutex); ++ guard(mutex)(&psmouse_mutex); + + if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { + parent = psmouse_from_serio(serio->parent); +@@ -1440,8 +1446,6 @@ static void psmouse_cleanup(struct serio *serio) + + psmouse_activate(parent); + } +- +- mutex_unlock(&psmouse_mutex); + } + + /* +@@ -1452,20 +1456,20 @@ static void psmouse_disconnect(struct serio *serio) + struct psmouse *psmouse = psmouse_from_serio(serio); + struct psmouse *parent = NULL; + +- mutex_lock(&psmouse_mutex); ++ disable_work_sync(&psmouse->resync_work); ++ ++ guard(mutex)(&psmouse_mutex); + + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + +- /* make sure we don't have a resync in progress */ +- mutex_unlock(&psmouse_mutex); +- disable_work_sync(&psmouse->resync_work); +- mutex_lock(&psmouse_mutex); +- + if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { + parent = psmouse_from_serio(serio->parent); + psmouse_deactivate(parent); + } + ++ scoped_guard(serio_pause_rx, serio) ++ psmouse->protocol_handler = NULL; ++ + if (psmouse->disconnect) + psmouse->disconnect(psmouse); + +@@ -1484,8 +1488,6 @@ static void psmouse_disconnect(struct serio *serio) + + if (parent) + psmouse_activate(parent); +- +- mutex_unlock(&psmouse_mutex); + } + + static int psmouse_switch_protocol(struct psmouse *psmouse, +@@ -1654,14 +1656,12 @@ static int __psmouse_reconnect(struct serio *serio, bool fast_reconnect) + enum psmouse_type type; + int rc = -1; + +- mutex_lock(&psmouse_mutex); ++ lockdep_assert_held(&psmouse_mutex); + + if (fast_reconnect) { + reconnect_handler = psmouse->fast_reconnect; +- if (!reconnect_handler) { +- rc = -ENOENT; +- goto out_unlock; +- } ++ if (!reconnect_handler) ++ return -ENOENT; + } else { + reconnect_handler = psmouse->reconnect; + } +@@ -1713,18 +1713,20 @@ static int __psmouse_reconnect(struct serio *serio, bool fast_reconnect) + if (parent) + psmouse_activate(parent); + +-out_unlock: +- mutex_unlock(&psmouse_mutex); + return rc; + } + + static int psmouse_reconnect(struct serio *serio) + { ++ guard(mutex)(&psmouse_mutex); ++ + return __psmouse_reconnect(serio, false); + } + + static int psmouse_fast_reconnect(struct serio *serio) + { ++ guard(mutex)(&psmouse_mutex); ++ + return __psmouse_reconnect(serio, true); + } + +diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c +--- a/drivers/input/mouse/synaptics.c ++++ b/drivers/input/mouse/synaptics.c +@@ -164,6 +164,7 @@ static const char * const topbuttonpad_pnp_ids[] = { + #ifdef CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS + static const char * const smbus_pnp_ids[] = { + /* all of the topbuttonpad_pnp_ids are valid, we just add some extras */ ++ "DLL0597", /* Dell Inspiron 3521 */ + "DLL060d", /* Dell Precision M3800 */ + "LEN0048", /* X1 Carbon 3 */ + "LEN0046", /* X250 */ +diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c +--- a/drivers/input/mouse/synaptics_i2c.c ++++ b/drivers/input/mouse/synaptics_i2c.c +@@ -261,7 +261,7 @@ static s32 synaptics_i2c_reg_set(struct i2c_client *client, u16 reg, u8 val) + if (error) + return error; + +- return error; ++ return 0; + } + + static s32 synaptics_i2c_word_get(struct i2c_client *client, u16 reg) +diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c +--- a/drivers/input/rmi4/rmi_f12.c ++++ b/drivers/input/rmi4/rmi_f12.c +@@ -155,6 +155,10 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12) + offset += 4; + } + ++ /* When platform data are provided, we're done */ ++ if (sensor->x_mm && sensor->y_mm) ++ return 0; ++ + /* + * Use the Query DPM feature when the resolution query register + * exists. +@@ -171,8 +175,10 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12) + } + dpm_resolution = buf[0]; + +- sensor->x_mm = sensor->max_x / dpm_resolution; +- sensor->y_mm = sensor->max_y / dpm_resolution; ++ if (!sensor->x_mm) ++ sensor->x_mm = sensor->max_x / dpm_resolution; ++ if (!sensor->y_mm) ++ sensor->y_mm = sensor->max_y / dpm_resolution; + } else { + if (rmi_register_desc_has_subpacket(item, 3)) { + rx_receivers = buf[offset]; +@@ -184,8 +190,10 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12) + if (rmi_register_desc_has_subpacket(item, 4)) + offset += 1; + +- sensor->x_mm = (pitch_x * rx_receivers) >> 12; +- sensor->y_mm = (pitch_y * tx_receivers) >> 12; ++ if (!sensor->x_mm) ++ sensor->x_mm = (pitch_x * rx_receivers) >> 12; ++ if (!sensor->y_mm) ++ sensor->y_mm = (pitch_y * tx_receivers) >> 12; + } + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: x_mm: %d y_mm: %d\n", __func__, +diff --git a/drivers/input/rmi4/rmi_smbus.c b/drivers/input/rmi4/rmi_smbus.c +--- a/drivers/input/rmi4/rmi_smbus.c ++++ b/drivers/input/rmi4/rmi_smbus.c +@@ -177,13 +177,8 @@ static int smb_block_read(struct rmi_transport_dev *xport, + struct rmi_smb_xport *rmi_smb = + container_of(xport, struct rmi_smb_xport, xport); + struct i2c_client *client = rmi_smb->client; +- int retval; + +- retval = i2c_smbus_read_block_data(client, commandcode, buf); +- if (retval < 0) +- return retval; +- +- return retval; ++ return i2c_smbus_read_block_data(client, commandcode, buf); + } + + static int rmi_smb_read_block(struct rmi_transport_dev *xport, u16 rmiaddr, +diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig +--- a/drivers/input/serio/Kconfig ++++ b/drivers/input/serio/Kconfig +@@ -106,6 +106,33 @@ config SERIO_GSCPS2 + To compile this driver as a module, choose M here: the + module will be called gscps2. + ++config SERIO_GSCPS2_RDI_KEYCODES ++ bool "Use PrecisionBook keyboard scancodes" ++ depends on SERIO_GSCPS2 ++ default n ++ help ++ If you have an RDI PrecisionBook, say Y here if you want to use its ++ built-in keyboard (as opposed to an external keyboard). ++ ++ The PrecisionBook has five keys that conflict with those used by most ++ AT and PS/2 keyboards. These are as follows: ++ ++ PrecisionBook Standard AT or PS/2 ++ ++ F1 F12 ++ Left Ctrl Left Alt ++ Caps Lock Left Ctrl ++ Right Ctrl Caps Lock ++ Left 102nd key (the key to the right of Left Shift) ++ ++ If you say N here, and use the PrecisionBook keyboard, then each key ++ in the left-hand column will be interpreted as the corresponding key ++ in the right-hand column. ++ ++ If you say Y here, and use an external keyboard, then each key in the ++ right-hand column will be interpreted as the key shown in the ++ left-hand column. ++ + config HP_SDC + tristate "HP System Device Controller i8042 Support" + depends on (GSC || HP300) && SERIO +diff --git a/drivers/input/serio/gscps2.c b/drivers/input/serio/gscps2.c +--- a/drivers/input/serio/gscps2.c ++++ b/drivers/input/serio/gscps2.c +@@ -22,18 +22,15 @@ + * was usable/enabled ?) + */ + +-#include +-#include +-#include +-#include ++#include + #include + #include +-#include +-#include ++#include + #include ++#include ++#include + + #include +-#include + #include + + MODULE_AUTHOR("Laurent Canet , Thibaut Varene , Helge Deller "); +@@ -80,6 +77,116 @@ MODULE_LICENSE("GPL"); + #define GSC_ID_KEYBOARD 0 /* device ID values */ + #define GSC_ID_MOUSE 1 + ++#ifndef CONFIG_SERIO_GSCPS2_RDI_KEYCODES ++# define CONFLICT(x, y) x ++#else ++# define CONFLICT(x, y) y ++#endif ++ ++/* ++ * Sadly RDI (Tadpole) decided to ship a different keyboard layout ++ * than HP for their PS/2 laptop keyboard which leads to conflicting ++ * keycodes between a normal HP PS/2 keyboard and a RDI PrecisionBook. ++ * HP: RDI: ++ */ ++#define C_07 CONFLICT(KEY_F12, KEY_F1) ++#define C_11 CONFLICT(KEY_LEFTALT, KEY_LEFTCTRL) ++#define C_14 CONFLICT(KEY_LEFTCTRL, KEY_CAPSLOCK) ++#define C_58 CONFLICT(KEY_CAPSLOCK, KEY_RIGHTCTRL) ++#define C_61 CONFLICT(KEY_102ND, KEY_LEFT) ++ ++/* ++ * Special keycode value recognized by atkbd (ATKBD_KEY_NULL) to silently ++ * discard scancodes without generating input events or "unknown key" warnings. ++ */ ++#define KEY_NULL 255 ++ ++#define KEYMAP_ENTRY(scancode, keycode) (((scancode) << 16) | (keycode)) ++ ++static const u32 gscps2_keymap[] = { ++ KEYMAP_ENTRY(0x01, KEY_F9), KEYMAP_ENTRY(0x03, KEY_F5), ++ KEYMAP_ENTRY(0x04, KEY_F3), KEYMAP_ENTRY(0x05, KEY_F1), ++ KEYMAP_ENTRY(0x06, KEY_F2), KEYMAP_ENTRY(0x07, C_07), ++ KEYMAP_ENTRY(0x08, KEY_ESC), KEYMAP_ENTRY(0x09, KEY_F10), ++ KEYMAP_ENTRY(0x0a, KEY_F8), KEYMAP_ENTRY(0x0b, KEY_F6), ++ KEYMAP_ENTRY(0x0c, KEY_F4), KEYMAP_ENTRY(0x0d, KEY_TAB), ++ KEYMAP_ENTRY(0x0e, KEY_GRAVE), KEYMAP_ENTRY(0x0f, KEY_F2), ++ KEYMAP_ENTRY(0x11, C_11), KEYMAP_ENTRY(0x12, KEY_LEFTSHIFT), ++ KEYMAP_ENTRY(0x14, C_14), KEYMAP_ENTRY(0x15, KEY_Q), ++ KEYMAP_ENTRY(0x16, KEY_1), KEYMAP_ENTRY(0x17, KEY_F3), ++ KEYMAP_ENTRY(0x19, KEY_LEFTALT), KEYMAP_ENTRY(0x1a, KEY_Z), ++ KEYMAP_ENTRY(0x1b, KEY_S), KEYMAP_ENTRY(0x1c, KEY_A), ++ KEYMAP_ENTRY(0x1d, KEY_W), KEYMAP_ENTRY(0x1e, KEY_2), ++ KEYMAP_ENTRY(0x1f, KEY_F4), KEYMAP_ENTRY(0x21, KEY_C), ++ KEYMAP_ENTRY(0x22, KEY_X), KEYMAP_ENTRY(0x23, KEY_D), ++ KEYMAP_ENTRY(0x24, KEY_E), KEYMAP_ENTRY(0x25, KEY_4), ++ KEYMAP_ENTRY(0x26, KEY_3), KEYMAP_ENTRY(0x27, KEY_F5), ++ KEYMAP_ENTRY(0x29, KEY_SPACE), KEYMAP_ENTRY(0x2a, KEY_V), ++ KEYMAP_ENTRY(0x2b, KEY_F), KEYMAP_ENTRY(0x2c, KEY_T), ++ KEYMAP_ENTRY(0x2d, KEY_R), KEYMAP_ENTRY(0x2e, KEY_5), ++ KEYMAP_ENTRY(0x2f, KEY_F6), KEYMAP_ENTRY(0x31, KEY_N), ++ KEYMAP_ENTRY(0x32, KEY_B), KEYMAP_ENTRY(0x33, KEY_H), ++ KEYMAP_ENTRY(0x34, KEY_G), KEYMAP_ENTRY(0x35, KEY_Y), ++ KEYMAP_ENTRY(0x36, KEY_6), KEYMAP_ENTRY(0x37, KEY_F7), ++ KEYMAP_ENTRY(0x39, KEY_RIGHTALT), KEYMAP_ENTRY(0x3a, KEY_M), ++ KEYMAP_ENTRY(0x3b, KEY_J), KEYMAP_ENTRY(0x3c, KEY_U), ++ KEYMAP_ENTRY(0x3d, KEY_7), KEYMAP_ENTRY(0x3e, KEY_8), ++ KEYMAP_ENTRY(0x3f, KEY_F8), KEYMAP_ENTRY(0x41, KEY_COMMA), ++ KEYMAP_ENTRY(0x42, KEY_K), KEYMAP_ENTRY(0x43, KEY_I), ++ KEYMAP_ENTRY(0x44, KEY_O), KEYMAP_ENTRY(0x45, KEY_0), ++ KEYMAP_ENTRY(0x46, KEY_9), KEYMAP_ENTRY(0x47, KEY_F9), ++ KEYMAP_ENTRY(0x49, KEY_DOT), KEYMAP_ENTRY(0x4a, KEY_SLASH), ++ KEYMAP_ENTRY(0x4b, KEY_L), KEYMAP_ENTRY(0x4c, KEY_SEMICOLON), ++ KEYMAP_ENTRY(0x4d, KEY_P), KEYMAP_ENTRY(0x4e, KEY_MINUS), ++ KEYMAP_ENTRY(0x4f, KEY_F10), KEYMAP_ENTRY(0x52, KEY_APOSTROPHE), ++ KEYMAP_ENTRY(0x54, KEY_LEFTBRACE), KEYMAP_ENTRY(0x55, KEY_EQUAL), ++ KEYMAP_ENTRY(0x56, KEY_F11), KEYMAP_ENTRY(0x57, KEY_SYSRQ), ++ KEYMAP_ENTRY(0x58, C_58), KEYMAP_ENTRY(0x59, KEY_RIGHTSHIFT), ++ KEYMAP_ENTRY(0x5a, KEY_ENTER), KEYMAP_ENTRY(0x5b, KEY_RIGHTBRACE), ++ KEYMAP_ENTRY(0x5c, KEY_BACKSLASH), KEYMAP_ENTRY(0x5d, KEY_BACKSLASH), ++ KEYMAP_ENTRY(0x5e, KEY_F12), KEYMAP_ENTRY(0x5f, KEY_SCROLLLOCK), ++ KEYMAP_ENTRY(0x60, KEY_DOWN), KEYMAP_ENTRY(0x61, C_61), ++ KEYMAP_ENTRY(0x62, KEY_PAUSE), KEYMAP_ENTRY(0x63, KEY_UP), ++ KEYMAP_ENTRY(0x64, KEY_DELETE), KEYMAP_ENTRY(0x65, KEY_END), ++ KEYMAP_ENTRY(0x66, KEY_BACKSPACE), KEYMAP_ENTRY(0x67, KEY_INSERT), ++ KEYMAP_ENTRY(0x69, KEY_KP1), KEYMAP_ENTRY(0x6a, KEY_RIGHT), ++ KEYMAP_ENTRY(0x6b, KEY_KP4), KEYMAP_ENTRY(0x6c, KEY_KP7), ++ KEYMAP_ENTRY(0x6d, KEY_PAGEDOWN), KEYMAP_ENTRY(0x6e, KEY_HOME), ++ KEYMAP_ENTRY(0x6f, KEY_PAGEUP), KEYMAP_ENTRY(0x70, KEY_KP0), ++ KEYMAP_ENTRY(0x71, KEY_KPDOT), KEYMAP_ENTRY(0x72, KEY_KP2), ++ KEYMAP_ENTRY(0x73, KEY_KP5), KEYMAP_ENTRY(0x74, KEY_KP6), ++ KEYMAP_ENTRY(0x75, KEY_KP8), KEYMAP_ENTRY(0x76, KEY_ESC), ++ KEYMAP_ENTRY(0x77, KEY_NUMLOCK), KEYMAP_ENTRY(0x78, KEY_F11), ++ KEYMAP_ENTRY(0x79, KEY_KPPLUS), KEYMAP_ENTRY(0x7a, KEY_KP3), ++ KEYMAP_ENTRY(0x7b, KEY_KPMINUS), KEYMAP_ENTRY(0x7c, KEY_KPASTERISK), ++ KEYMAP_ENTRY(0x7d, KEY_KP9), KEYMAP_ENTRY(0x7e, KEY_SCROLLLOCK), ++ KEYMAP_ENTRY(0x7f, KEY_102ND), KEYMAP_ENTRY(0x91, KEY_RIGHTALT), ++ KEYMAP_ENTRY(0x92, KEY_NULL), KEYMAP_ENTRY(0x94, KEY_RIGHTCTRL), ++ KEYMAP_ENTRY(0x9d, KEY_CAPSLOCK), KEYMAP_ENTRY(0x9f, KEY_LEFTMETA), ++ KEYMAP_ENTRY(0xa7, KEY_RIGHTMETA), KEYMAP_ENTRY(0xaf, KEY_COMPOSE), ++ KEYMAP_ENTRY(0xca, KEY_KPSLASH), KEYMAP_ENTRY(0xda, KEY_KPENTER), ++ KEYMAP_ENTRY(0xe9, KEY_END), KEYMAP_ENTRY(0xeb, KEY_LEFT), ++ KEYMAP_ENTRY(0xec, KEY_HOME), KEYMAP_ENTRY(0xf0, KEY_INSERT), ++ KEYMAP_ENTRY(0xf1, KEY_DELETE), KEYMAP_ENTRY(0xf2, KEY_DOWN), ++ KEYMAP_ENTRY(0xf4, KEY_RIGHT), KEYMAP_ENTRY(0xf5, KEY_UP), ++ KEYMAP_ENTRY(0xf7, KEY_PAUSE), KEYMAP_ENTRY(0xfa, KEY_PAGEDOWN), ++ KEYMAP_ENTRY(0xfc, KEY_SYSRQ), KEYMAP_ENTRY(0xfd, KEY_PAGEUP), ++ ++ /* Escaped keycodes */ ++ KEYMAP_ENTRY(0x103, KEY_F7), KEYMAP_ENTRY(0x10b, KEY_LEFTMETA), ++ KEYMAP_ENTRY(0x10c, KEY_RIGHTMETA), KEYMAP_ENTRY(0x111, KEY_RIGHTALT), ++ KEYMAP_ENTRY(0x114, KEY_RIGHTCTRL), ++}; ++ ++static const struct property_entry gscps2_props[] = { ++ PROPERTY_ENTRY_U32_ARRAY("linux,keymap", gscps2_keymap), ++ { } ++}; ++ ++static const struct software_node gscps2_keyboard_node = { ++ .name = "gscps2-keyboard", ++ .properties = gscps2_props, ++}; + + static irqreturn_t gscps2_interrupt(int irq, void *dev); + +@@ -398,6 +505,17 @@ static int __init gscps2_probe(struct parisc_device *dev) + goto fail; + #endif + ++ if (ps2port->id == GSC_ID_KEYBOARD) { ++ ret = device_add_software_node(&serio->dev, ++ &gscps2_keyboard_node); ++ if (ret) { ++ dev_err(&dev->dev, ++ "failed to add software node for keyboard: %d\n", ++ ret); ++ goto fail; ++ } ++ } ++ + pr_info("serio: %s port at 0x%08lx irq %d @ %s\n", + ps2port->port->name, + hpa, +@@ -411,11 +529,16 @@ static int __init gscps2_probe(struct parisc_device *dev) + return 0; + + fail: ++ if (ps2port->id == GSC_ID_KEYBOARD) ++ device_remove_software_node(&serio->dev); ++ + free_irq(dev->irq, ps2port); + + fail_miserably: + iounmap(ps2port->addr); ++#if 0 + release_mem_region(dev->hpa.start, GSC_STATUS + 4); ++#endif + + fail_nomem: + kfree(ps2port); +@@ -434,6 +557,9 @@ static void __exit gscps2_remove(struct parisc_device *dev) + { + struct gscps2port *ps2port = dev_get_drvdata(&dev->dev); + ++ if (ps2port->id == GSC_ID_KEYBOARD) ++ device_remove_software_node(&ps2port->port->dev); ++ + serio_unregister_port(ps2port->port); + free_irq(dev->irq, ps2port); + gscps2_flush(ps2port); +@@ -465,16 +591,25 @@ static struct parisc_driver parisc_ps2_driver __refdata = { + + static int __init gscps2_init(void) + { +- register_parisc_driver(&parisc_ps2_driver); +- return 0; ++ int error; ++ ++ error = software_node_register(&gscps2_keyboard_node); ++ if (error) ++ return error; ++ ++ error = register_parisc_driver(&parisc_ps2_driver); ++ if (error) ++ software_node_unregister(&gscps2_keyboard_node); ++ ++ return error; + } + + static void __exit gscps2_exit(void) + { + unregister_parisc_driver(&parisc_ps2_driver); ++ software_node_unregister(&gscps2_keyboard_node); + } + + + module_init(gscps2_init); + module_exit(gscps2_exit); +- +diff --git a/drivers/input/serio/i8042-acpipnpio.h b/drivers/input/serio/i8042-acpipnpio.h +--- a/drivers/input/serio/i8042-acpipnpio.h ++++ b/drivers/input/serio/i8042-acpipnpio.h +@@ -3,6 +3,7 @@ + #define _I8042_ACPIPNPIO_H + + #include ++#include + + #ifdef CONFIG_X86 + #include +@@ -1486,17 +1487,21 @@ static char i8042_pnp_aux_name[32]; + + static void i8042_pnp_id_to_string(struct pnp_id *id, char *dst, int dst_size) + { +- strscpy(dst, "PNP:", dst_size); ++ struct seq_buf sb; ++ ++ seq_buf_init(&sb, dst, dst_size); ++ seq_buf_printf(&sb, "PNP:"); + + while (id) { +- strlcat(dst, " ", dst_size); +- strlcat(dst, id->id, dst_size); ++ seq_buf_printf(&sb, " %s", id->id); + id = id->next; + } + } + + static int i8042_pnp_kbd_probe(struct pnp_dev *dev, const struct pnp_device_id *did) + { ++ const char *name = pnp_dev_name(dev); ++ + if (pnp_port_valid(dev, 0) && pnp_port_len(dev, 0) == 1) + i8042_pnp_data_reg = pnp_port_start(dev,0); + +@@ -1506,11 +1511,8 @@ static int i8042_pnp_kbd_probe(struct pnp_dev *dev, const struct pnp_device_id * + if (pnp_irq_valid(dev,0)) + i8042_pnp_kbd_irq = pnp_irq(dev, 0); + +- strscpy(i8042_pnp_kbd_name, did->id, sizeof(i8042_pnp_kbd_name)); +- if (strlen(pnp_dev_name(dev))) { +- strlcat(i8042_pnp_kbd_name, ":", sizeof(i8042_pnp_kbd_name)); +- strlcat(i8042_pnp_kbd_name, pnp_dev_name(dev), sizeof(i8042_pnp_kbd_name)); +- } ++ scnprintf(i8042_pnp_kbd_name, sizeof(i8042_pnp_kbd_name), "%s%s%s", ++ did->id, strlen(name) ? ":" : "", name); + i8042_pnp_id_to_string(dev->id, i8042_kbd_firmware_id, + sizeof(i8042_kbd_firmware_id)); + i8042_kbd_fwnode = dev_fwnode(&dev->dev); +@@ -1524,6 +1526,8 @@ static int i8042_pnp_kbd_probe(struct pnp_dev *dev, const struct pnp_device_id * + + static int i8042_pnp_aux_probe(struct pnp_dev *dev, const struct pnp_device_id *did) + { ++ const char *name = pnp_dev_name(dev); ++ + if (pnp_port_valid(dev, 0) && pnp_port_len(dev, 0) == 1) + i8042_pnp_data_reg = pnp_port_start(dev,0); + +@@ -1533,11 +1537,8 @@ static int i8042_pnp_aux_probe(struct pnp_dev *dev, const struct pnp_device_id * + if (pnp_irq_valid(dev, 0)) + i8042_pnp_aux_irq = pnp_irq(dev, 0); + +- strscpy(i8042_pnp_aux_name, did->id, sizeof(i8042_pnp_aux_name)); +- if (strlen(pnp_dev_name(dev))) { +- strlcat(i8042_pnp_aux_name, ":", sizeof(i8042_pnp_aux_name)); +- strlcat(i8042_pnp_aux_name, pnp_dev_name(dev), sizeof(i8042_pnp_aux_name)); +- } ++ scnprintf(i8042_pnp_aux_name, sizeof(i8042_pnp_aux_name), "%s%s%s", ++ did->id, strlen(name) ? ":" : "", name); + i8042_pnp_id_to_string(dev->id, i8042_aux_firmware_id, + sizeof(i8042_aux_firmware_id)); + +diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c +--- a/drivers/input/touchscreen/elo.c ++++ b/drivers/input/touchscreen/elo.c +@@ -257,7 +257,7 @@ static int elo_command_10(struct elo *elo, unsigned char *packet) + + static int elo_setup_10(struct elo *elo) + { +- static const char *elo_types[] = { "Accu", "Dura", "Intelli", "Carroll" }; ++ static const char * const elo_types[] = { "Accu", "Dura", "Intelli", "Carroll" }; + struct input_dev *dev = elo->dev; + unsigned char packet[ELO10_PACKET_LEN] = { ELO10_ID_CMD }; + +@@ -273,7 +273,7 @@ static int elo_setup_10(struct elo *elo) + + dev_info(&elo->serio->dev, + "%sTouch touchscreen, fw: %02x.%02x, features: 0x%02x, controller: 0x%02x\n", +- elo_types[(packet[1] -'0') & 0x03], ++ elo_types[(packet[1] - '0') & 0x03], + packet[5], packet[4], packet[3], packet[7]); + + return 0; +diff --git a/drivers/input/touchscreen/inexio.c b/drivers/input/touchscreen/inexio.c +--- a/drivers/input/touchscreen/inexio.c ++++ b/drivers/input/touchscreen/inexio.c +@@ -81,7 +81,9 @@ static irqreturn_t inexio_interrupt(struct serio *serio, + if (INEXIO_RESPONSE_BEGIN_BYTE&pinexio->data[0]) + inexio_process_data(pinexio); + else +- printk(KERN_DEBUG "inexio.c: unknown/unsynchronized data from device, byte %x\n",pinexio->data[0]); ++ dev_dbg(&serio->dev, ++ "unknown/unsynchronized data from device, byte %x\n", ++ pinexio->data[0]); + + return IRQ_HANDLED; + } +diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c +--- a/drivers/input/touchscreen/iqs5xx.c ++++ b/drivers/input/touchscreen/iqs5xx.c +@@ -785,7 +785,8 @@ static int iqs5xx_fw_file_parse(struct i2c_client *client, + switch (rec_type) { + case IQS5XX_REC_TYPE_DATA: + if (rec_addr < IQS5XX_CHKSM || +- rec_addr > IQS5XX_PMAP_END) { ++ rec_addr > IQS5XX_PMAP_END || ++ rec_len > IQS5XX_PMAP_END + 1 - rec_addr) { + dev_err(&client->dev, + "Invalid address at record %u\n", + rec_num); +diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c +--- a/drivers/input/touchscreen/mms114.c ++++ b/drivers/input/touchscreen/mms114.c +@@ -4,6 +4,8 @@ + // Copyright (c) 2012 Samsung Electronics Co., Ltd. + // Author: Joonyoung Shim + ++#include ++#include + #include + #include + #include +@@ -52,21 +54,13 @@ + #define MMS114_TYPE_TOUCHSCREEN 1 + #define MMS114_TYPE_TOUCHKEY 2 + +-enum mms_type { +- TYPE_MMS114 = 114, +- TYPE_MMS134S = 134, +- TYPE_MMS136 = 136, +- TYPE_MMS152 = 152, +- TYPE_MMS345L = 345, +-}; +- + struct mms114_data { ++ const struct mms_chip *chip; + struct i2c_client *client; + struct input_dev *input_dev; + struct regulator *core_reg; + struct regulator *io_reg; + struct touchscreen_properties props; +- enum mms_type type; + unsigned int contact_threshold; + unsigned int moving_threshold; + +@@ -77,9 +71,23 @@ struct mms114_data { + u8 cache_mode_control; + }; + ++struct mms_chip { ++ const char *name; ++ int event_size; ++ bool has_config_regs; ++ int (*get_version)(struct mms114_data *data); ++}; ++ ++#define MMS114_FLAGS_ID_MASK GENMASK(3, 0) ++#define MMS114_FLAGS_TYPE_MASK GENMASK(6, 5) ++#define MMS114_FLAGS_PRESSED_MASK BIT(7) ++ ++#define MMS114_XY_HI_X_MASK GENMASK(3, 0) ++#define MMS114_XY_HI_Y_MASK GENMASK(7, 4) ++ + struct mms114_touch { +- u8 id:4, reserved_bit4:1, type:2, pressed:1; +- u8 x_hi:4, y_hi:4; ++ u8 flags; ++ u8 xy_hi; + u8 x_lo; + u8 y_lo; + u8 width; +@@ -87,16 +95,16 @@ struct mms114_touch { + u8 reserved[2]; + } __packed; + +-static int __mms114_read_reg(struct mms114_data *data, unsigned int reg, +- unsigned int len, u8 *val) ++static int __mms114_read_reg(struct mms114_data *data, u8 reg, ++ unsigned int len, void *val) + { + struct i2c_client *client = data->client; + struct i2c_msg xfer[2]; +- u8 buf = reg & 0xff; ++ u8 buf = reg; + int error; + +- if (reg <= MMS114_MODE_CONTROL && reg + len > MMS114_MODE_CONTROL) +- BUG(); ++ if (WARN_ON(reg <= MMS114_MODE_CONTROL && reg + len > MMS114_MODE_CONTROL)) ++ return -EINVAL; + + /* Write register */ + xfer[0].addr = client->addr; +@@ -116,12 +124,12 @@ static int __mms114_read_reg(struct mms114_data *data, unsigned int reg, + "%s: i2c transfer failed (%d)\n", __func__, error); + return error < 0 ? error : -EIO; + } +- udelay(MMS114_I2C_DELAY); ++ usleep_range(MMS114_I2C_DELAY, MMS114_I2C_DELAY + 50); + + return 0; + } + +-static int mms114_read_reg(struct mms114_data *data, unsigned int reg) ++static int mms114_read_reg(struct mms114_data *data, u8 reg) + { + u8 val; + int error; +@@ -133,15 +141,14 @@ static int mms114_read_reg(struct mms114_data *data, unsigned int reg) + return error < 0 ? error : val; + } + +-static int mms114_write_reg(struct mms114_data *data, unsigned int reg, +- unsigned int val) ++static int mms114_write_reg(struct mms114_data *data, u8 reg, u8 val) + { + struct i2c_client *client = data->client; + u8 buf[2]; + int error; + +- buf[0] = reg & 0xff; +- buf[1] = val & 0xff; ++ buf[0] = reg; ++ buf[1] = val; + + error = i2c_master_send(client, buf, 2); + if (error != 2) { +@@ -149,7 +156,7 @@ static int mms114_write_reg(struct mms114_data *data, unsigned int reg, + "%s: i2c send failed (%d)\n", __func__, error); + return error < 0 ? error : -EIO; + } +- udelay(MMS114_I2C_DELAY); ++ usleep_range(MMS114_I2C_DELAY, MMS114_I2C_DELAY + 50); + + if (reg == MMS114_MODE_CONTROL) + data->cache_mode_control = val; +@@ -157,32 +164,119 @@ static int mms114_write_reg(struct mms114_data *data, unsigned int reg, + return 0; + } + ++static int mms114_get_version(struct mms114_data *data) ++{ ++ struct device *dev = &data->client->dev; ++ u8 buf[6]; ++ int error; ++ ++ error = __mms114_read_reg(data, MMS114_TSP_REV, 6, buf); ++ if (error) ++ return error; ++ ++ dev_info(dev, "TSP Rev: 0x%x, HW Rev: 0x%x, Firmware Ver: 0x%x\n", ++ buf[0], buf[1], buf[3]); ++ return 0; ++} ++ ++static int mms152_get_version(struct mms114_data *data) ++{ ++ struct device *dev = &data->client->dev; ++ u8 buf[3]; ++ int group; ++ int error; ++ ++ error = __mms114_read_reg(data, MMS152_FW_REV, 3, buf); ++ if (error) ++ return error; ++ ++ group = i2c_smbus_read_byte_data(data->client, MMS152_COMPAT_GROUP); ++ if (group < 0) ++ return group; ++ ++ dev_info(dev, "TSP FW Rev: bootloader 0x%x / core 0x%x / config 0x%x, Compat group: %c\n", ++ buf[0], buf[1], buf[2], group); ++ return 0; ++} ++ ++static int mms345l_get_version(struct mms114_data *data) ++{ ++ struct device *dev = &data->client->dev; ++ u8 buf[3]; ++ int error; ++ ++ error = __mms114_read_reg(data, MMS152_FW_REV, 3, buf); ++ if (error) ++ return error; ++ ++ dev_info(dev, "TSP FW Rev: bootloader 0x%x / core 0x%x / config 0x%x\n", ++ buf[0], buf[1], buf[2]); ++ return 0; ++} ++ ++static const struct mms_chip mms114_descriptor = { ++ .name = "MMS114", ++ .event_size = MMS114_EVENT_SIZE, ++ .has_config_regs = true, ++ .get_version = mms114_get_version, ++}; ++ ++static const struct mms_chip mms134s_descriptor = { ++ .name = "MMS134S", ++ .event_size = MMS136_EVENT_SIZE, ++ .has_config_regs = true, ++ .get_version = mms114_get_version, ++}; ++ ++static const struct mms_chip mms136_descriptor = { ++ .name = "MMS136", ++ .event_size = MMS136_EVENT_SIZE, ++ .has_config_regs = true, ++ .get_version = mms114_get_version, ++}; ++ ++static const struct mms_chip mms152_descriptor = { ++ .name = "MMS152", ++ .event_size = MMS114_EVENT_SIZE, ++ .has_config_regs = false, ++ .get_version = mms152_get_version, ++}; ++ ++static const struct mms_chip mms345l_descriptor = { ++ .name = "MMS345L", ++ .event_size = MMS114_EVENT_SIZE, ++ .has_config_regs = false, ++ .get_version = mms345l_get_version, ++}; ++ + static void mms114_process_mt(struct mms114_data *data, struct mms114_touch *touch) + { + struct i2c_client *client = data->client; + struct input_dev *input_dev = data->input_dev; +- unsigned int id; ++ unsigned int id = FIELD_GET(MMS114_FLAGS_ID_MASK, touch->flags); ++ unsigned int type = FIELD_GET(MMS114_FLAGS_TYPE_MASK, touch->flags); ++ bool pressed = FIELD_GET(MMS114_FLAGS_PRESSED_MASK, touch->flags); + unsigned int x; + unsigned int y; + +- if (touch->id == 0 || touch->id > MMS114_MAX_TOUCH) { +- dev_err(&client->dev, "Wrong touch id (%d)\n", touch->id); ++ if (id == 0 || id > MMS114_MAX_TOUCH) { ++ dev_err(&client->dev, "Wrong touch id (%d)\n", id); + return; + } + +- id = touch->id - 1; +- x = touch->x_lo | touch->x_hi << 8; +- y = touch->y_lo | touch->y_hi << 8; ++ id--; ++ x = touch->x_lo | FIELD_GET(MMS114_XY_HI_X_MASK, touch->xy_hi) << 8; ++ y = touch->y_lo | FIELD_GET(MMS114_XY_HI_Y_MASK, touch->xy_hi) << 8; + + dev_dbg(&client->dev, + "id: %d, type: %d, pressed: %d, x: %d, y: %d, width: %d, strength: %d\n", +- id, touch->type, touch->pressed, ++ id, type, pressed, + x, y, touch->width, touch->strength); + + input_mt_slot(input_dev, id); +- input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, touch->pressed); ++ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, pressed); + +- if (touch->pressed) { ++ if (pressed) { + touchscreen_report_pos(input_dev, &data->props, x, y, true); + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, touch->width); + input_report_abs(input_dev, ABS_MT_PRESSURE, touch->strength); +@@ -195,21 +289,23 @@ static void mms114_process_touchkey(struct mms114_data *data, + struct i2c_client *client = data->client; + struct input_dev *input_dev = data->input_dev; + unsigned int keycode_id; ++ unsigned int id = FIELD_GET(MMS114_FLAGS_ID_MASK, touch->flags); ++ bool pressed = FIELD_GET(MMS114_FLAGS_PRESSED_MASK, touch->flags); + +- if (touch->id == 0) ++ if (id == 0) + return; + +- if (touch->id > data->num_keycodes) { ++ if (id > data->num_keycodes) { + dev_err(&client->dev, "Wrong touch id for touchkey (%d)\n", +- touch->id); ++ id); + return; + } + +- keycode_id = touch->id - 1; ++ keycode_id = id - 1; + dev_dbg(&client->dev, "keycode id: %d, pressed: %d\n", keycode_id, +- touch->pressed); ++ pressed); + +- input_report_key(input_dev, data->keycodes[keycode_id], touch->pressed); ++ input_report_key(input_dev, data->keycodes[keycode_id], pressed); + } + + static irqreturn_t mms114_interrupt(int irq, void *dev_id) +@@ -218,8 +314,8 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id) + struct i2c_client *client = data->client; + struct mms114_touch touch[MMS114_MAX_TOUCH]; + struct mms114_touch *t; ++ int event_size = data->chip->event_size; + int packet_size; +- int event_size; + int touch_size; + int index; + int error; +@@ -234,23 +330,17 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id) + goto out; + } + +- /* MMS136 has slightly different event size */ +- if (data->type == TYPE_MMS134S || data->type == TYPE_MMS136) +- event_size = MMS136_EVENT_SIZE; +- else +- event_size = MMS114_EVENT_SIZE; +- + touch_size = packet_size / event_size; + +- error = __mms114_read_reg(data, MMS114_INFORMATION, packet_size, +- (u8 *)touch); +- if (error < 0) ++ error = __mms114_read_reg(data, MMS114_INFORMATION, packet_size, touch); ++ if (error) + goto out; + + for (index = 0; index < touch_size; index++) { + t = (struct mms114_touch *)((u8 *)touch + index * event_size); ++ unsigned int type = FIELD_GET(MMS114_FLAGS_TYPE_MASK, t->flags); + +- switch (t->type) { ++ switch (type) { + case MMS114_TYPE_TOUCHSCREEN: + mms114_process_mt(data, t); + break; +@@ -261,7 +351,7 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id) + + default: + dev_err(&client->dev, "Wrong touch type (%d)\n", +- t->type); ++ type); + break; + } + } +@@ -290,65 +380,17 @@ static int mms114_set_active(struct mms114_data *data, bool active) + return mms114_write_reg(data, MMS114_MODE_CONTROL, val); + } + +-static int mms114_get_version(struct mms114_data *data) +-{ +- struct device *dev = &data->client->dev; +- u8 buf[6]; +- int group; +- int error; +- +- switch (data->type) { +- case TYPE_MMS345L: +- error = __mms114_read_reg(data, MMS152_FW_REV, 3, buf); +- if (error) +- return error; +- +- dev_info(dev, "TSP FW Rev: bootloader 0x%x / core 0x%x / config 0x%x\n", +- buf[0], buf[1], buf[2]); +- break; +- +- case TYPE_MMS152: +- error = __mms114_read_reg(data, MMS152_FW_REV, 3, buf); +- if (error) +- return error; +- +- group = i2c_smbus_read_byte_data(data->client, +- MMS152_COMPAT_GROUP); +- if (group < 0) +- return group; +- +- dev_info(dev, "TSP FW Rev: bootloader 0x%x / core 0x%x / config 0x%x, Compat group: %c\n", +- buf[0], buf[1], buf[2], group); +- break; +- +- case TYPE_MMS114: +- case TYPE_MMS134S: +- case TYPE_MMS136: +- error = __mms114_read_reg(data, MMS114_TSP_REV, 6, buf); +- if (error) +- return error; +- +- dev_info(dev, "TSP Rev: 0x%x, HW Rev: 0x%x, Firmware Ver: 0x%x\n", +- buf[0], buf[1], buf[3]); +- break; +- } +- +- return 0; +-} +- + static int mms114_setup_regs(struct mms114_data *data) + { + const struct touchscreen_properties *props = &data->props; + int val; + int error; + +- error = mms114_get_version(data); +- if (error < 0) ++ error = data->chip->get_version(data); ++ if (error) + return error; + +- /* MMS114, MMS134S and MMS136 have configuration and power on registers */ +- if (data->type != TYPE_MMS114 && data->type != TYPE_MMS134S && +- data->type != TYPE_MMS136) ++ if (!data->chip->has_config_regs) + return 0; + + error = mms114_set_active(data, true); +@@ -366,21 +408,21 @@ static int mms114_setup_regs(struct mms114_data *data) + if (error < 0) + return error; + +- val = props->max_x & 0xff; ++ val = props->max_y & 0xff; + error = mms114_write_reg(data, MMS114_Y_RESOLUTION, val); + if (error < 0) + return error; + + if (data->contact_threshold) { + error = mms114_write_reg(data, MMS114_CONTACT_THRESHOLD, +- data->contact_threshold); ++ data->contact_threshold); + if (error < 0) + return error; + } + + if (data->moving_threshold) { + error = mms114_write_reg(data, MMS114_MOVING_THRESHOLD, +- data->moving_threshold); ++ data->moving_threshold); + if (error < 0) + return error; + } +@@ -466,9 +508,9 @@ static int mms114_parse_legacy_bindings(struct mms114_data *data) + } + + device_property_read_u32(dev, "contact-threshold", +- &data->contact_threshold); ++ &data->contact_threshold); + device_property_read_u32(dev, "moving-threshold", +- &data->moving_threshold); ++ &data->moving_threshold); + + if (device_property_read_bool(dev, "x-invert")) + props->invert_x = true; +@@ -484,7 +526,6 @@ static int mms114_probe(struct i2c_client *client) + { + struct mms114_data *data; + struct input_dev *input_dev; +- const void *match_data; + int error; + int i; + +@@ -504,12 +545,10 @@ static int mms114_probe(struct i2c_client *client) + data->client = client; + data->input_dev = input_dev; + +- match_data = device_get_match_data(&client->dev); +- if (!match_data) ++ data->chip = i2c_get_match_data(client); ++ if (!data->chip) + return -EINVAL; + +- data->type = (enum mms_type)match_data; +- + data->num_keycodes = device_property_count_u32(&client->dev, + "linux,keycodes"); + if (data->num_keycodes == -EINVAL) { +@@ -521,7 +560,7 @@ static int mms114_probe(struct i2c_client *client) + return data->num_keycodes; + } else if (data->num_keycodes > MMS114_MAX_TOUCHKEYS) { + dev_warn(&client->dev, +- "Found %d linux,keycodes but max is %d, ignoring the rest\n", ++ "Found %d linux,keycodes but max is %d, ignoring the rest\n", + data->num_keycodes, MMS114_MAX_TOUCHKEYS); + data->num_keycodes = MMS114_MAX_TOUCHKEYS; + } +@@ -566,8 +605,7 @@ static int mms114_probe(struct i2c_client *client) + 0, data->props.max_y, 0, 0); + } + +- if (data->type == TYPE_MMS114 || data->type == TYPE_MMS134S || +- data->type == TYPE_MMS136) { ++ if (data->chip->has_config_regs) { + /* + * The firmware handles movement and pressure fuzz, so + * don't duplicate that in software. +@@ -582,8 +620,8 @@ static int mms114_probe(struct i2c_client *client) + } + + input_dev->name = devm_kasprintf(&client->dev, GFP_KERNEL, +- "MELFAS MMS%d Touchscreen", +- data->type); ++ "MELFAS %s Touchscreen", ++ data->chip->name); + if (!input_dev->name) + return -ENOMEM; + +@@ -679,29 +717,22 @@ static int mms114_resume(struct device *dev) + static DEFINE_SIMPLE_DEV_PM_OPS(mms114_pm_ops, mms114_suspend, mms114_resume); + + static const struct i2c_device_id mms114_id[] = { +- { .name = "mms114" }, ++ { .name = "mms114", .driver_data = (kernel_ulong_t)&mms114_descriptor }, ++ { .name = "mms134s", .driver_data = (kernel_ulong_t)&mms134s_descriptor }, ++ { .name = "mms136", .driver_data = (kernel_ulong_t)&mms136_descriptor }, ++ { .name = "mms152", .driver_data = (kernel_ulong_t)&mms152_descriptor }, ++ { .name = "mms345l", .driver_data = (kernel_ulong_t)&mms345l_descriptor }, + { } + }; + MODULE_DEVICE_TABLE(i2c, mms114_id); + + #ifdef CONFIG_OF + static const struct of_device_id mms114_dt_match[] = { +- { +- .compatible = "melfas,mms114", +- .data = (void *)TYPE_MMS114, +- }, { +- .compatible = "melfas,mms134s", +- .data = (void *)TYPE_MMS134S, +- }, { +- .compatible = "melfas,mms136", +- .data = (void *)TYPE_MMS136, +- }, { +- .compatible = "melfas,mms152", +- .data = (void *)TYPE_MMS152, +- }, { +- .compatible = "melfas,mms345l", +- .data = (void *)TYPE_MMS345L, +- }, ++ { .compatible = "melfas,mms114", .data = &mms114_descriptor }, ++ { .compatible = "melfas,mms134s", .data = &mms134s_descriptor }, ++ { .compatible = "melfas,mms136", .data = &mms136_descriptor }, ++ { .compatible = "melfas,mms152", .data = &mms152_descriptor }, ++ { .compatible = "melfas,mms345l", .data = &mms345l_descriptor }, + { } + }; + MODULE_DEVICE_TABLE(of, mms114_dt_match); +@@ -722,4 +753,4 @@ module_i2c_driver(mms114_driver); + /* Module information */ + MODULE_AUTHOR("Joonyoung Shim "); + MODULE_DESCRIPTION("MELFAS mms114 Touchscreen driver"); +-MODULE_LICENSE("GPL v2"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c +--- a/drivers/input/touchscreen/sur40.c ++++ b/drivers/input/touchscreen/sur40.c +@@ -128,8 +128,8 @@ struct sur40_image_header { + /* polling interval (ms) */ + #define POLL_INTERVAL 1 + +-/* maximum number of contacts FIXME: this is a guess? */ +-#define MAX_CONTACTS 64 ++/* maximum number of contacts */ ++#define MAX_CONTACTS 52 + + /* control commands */ + #define SUR40_GET_VERSION 0xb0 /* 12 bytes string */ +diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c +--- a/drivers/input/touchscreen/wacom_w8001.c ++++ b/drivers/input/touchscreen/wacom_w8001.c +@@ -417,6 +417,13 @@ static int w8001_detect(struct w8001 *w8001) + return 0; + } + ++static void w8001_append_suffix(char *dest, const char *suffix, size_t dest_sz) ++{ ++ size_t used = strnlen(dest, dest_sz); ++ ++ strscpy(dest + used, suffix, dest_sz - used); ++} ++ + static int w8001_setup_pen(struct w8001 *w8001, char *basename, + size_t basename_sz) + { +@@ -453,7 +460,7 @@ static int w8001_setup_pen(struct w8001 *w8001, char *basename, + } + + w8001->id = 0x90; +- strlcat(basename, " Penabled", basename_sz); ++ w8001_append_suffix(basename, " Penabled", basename_sz); + + return 0; + } +@@ -503,14 +510,14 @@ static int w8001_setup_touch(struct w8001 *w8001, char *basename, + case 2: + w8001->pktlen = W8001_PKTLEN_TOUCH93; + w8001->id = 0x93; +- strlcat(basename, " 1FG", basename_sz); ++ w8001_append_suffix(basename, " 1FG", basename_sz); + break; + + case 1: + case 3: + case 4: + w8001->pktlen = W8001_PKTLEN_TOUCH9A; +- strlcat(basename, " 1FG", basename_sz); ++ w8001_append_suffix(basename, " 1FG", basename_sz); + w8001->id = 0x9a; + break; + +@@ -534,7 +541,7 @@ static int w8001_setup_touch(struct w8001 *w8001, char *basename, + input_abs_set_res(dev, ABS_MT_POSITION_X, touch.panel_res); + input_abs_set_res(dev, ABS_MT_POSITION_Y, touch.panel_res); + +- strlcat(basename, " 2FG", basename_sz); ++ w8001_append_suffix(basename, " 2FG", basename_sz); + if (w8001->max_pen_x && w8001->max_pen_y) + w8001->id = 0xE3; + else +@@ -542,7 +549,7 @@ static int w8001_setup_touch(struct w8001 *w8001, char *basename, + break; + } + +- strlcat(basename, " Touchscreen", basename_sz); ++ w8001_append_suffix(basename, " Touchscreen", basename_sz); + + return 0; + } +diff --git a/drivers/mfd/rohm-bd71828.c b/drivers/mfd/rohm-bd71828.c +--- a/drivers/mfd/rohm-bd71828.c ++++ b/drivers/mfd/rohm-bd71828.c +@@ -5,7 +5,8 @@ + * ROHM BD718[15/28/79] and BD72720 PMIC driver + */ + +-#include ++#include ++#include + #include + #include + #include +@@ -18,6 +19,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -37,19 +39,6 @@ + }, \ + } + +-static struct gpio_keys_button button = { +- .code = KEY_POWER, +- .gpio = -1, +- .type = EV_KEY, +- .wakeup = 1, +-}; +- +-static const struct gpio_keys_platform_data bd71828_powerkey_data = { +- .buttons = &button, +- .nbuttons = 1, +- .name = "bd71828-pwrkey", +-}; +- + static const struct resource bd71815_rtc_irqs[] = { + DEFINE_RES_IRQ_NAMED(BD71815_INT_RTC0, "bd70528-rtc-alm-0"), + DEFINE_RES_IRQ_NAMED(BD71815_INT_RTC1, "bd70528-rtc-alm-1"), +@@ -174,10 +163,6 @@ static struct mfd_cell bd71828_mfd_cells[] = { + .name = "bd71828-rtc", + .resources = bd71828_rtc_irqs, + .num_resources = ARRAY_SIZE(bd71828_rtc_irqs), +- }, { +- .name = "gpio-keys", +- .platform_data = &bd71828_powerkey_data, +- .pdata_size = sizeof(bd71828_powerkey_data), + }, + }; + +@@ -242,11 +227,8 @@ static const struct mfd_cell bd72720_mfd_cells[] = { + .name = "bd72720-rtc", + .resources = bd72720_rtc_irqs, + .num_resources = ARRAY_SIZE(bd72720_rtc_irqs), +- }, { +- .name = "gpio-keys", +- .platform_data = &bd71828_powerkey_data, +- .pdata_size = sizeof(bd71828_powerkey_data), + }, ++ /* Power button is registered separately */ + }; + + static const struct regmap_range bd71815_volatile_ranges[] = { +@@ -877,6 +859,84 @@ static int set_clk_mode(struct device *dev, struct regmap *regmap, + OUT32K_MODE_CMOS); + } + ++static const struct property_entry bd71828_powerkey_parent_props[] = { ++ PROPERTY_ENTRY_STRING("label", "bd71828-pwrkey"), ++ { } ++}; ++ ++static const struct property_entry bd71828_powerkey_props[] = { ++ PROPERTY_ENTRY_U32("linux,code", KEY_POWER), ++ PROPERTY_ENTRY_BOOL("wakeup-source"), ++ { } ++}; ++ ++#define GPIO_KEYS 0 /* Node corresponding to gpio-keys device itself */ ++#define PWRON_KEY 1 /* Node describing power button in gpio-keys */ ++ ++static int bd71828_i2c_register_swnodes(const struct software_node *nodes) ++{ ++ const struct software_node * const node_group[] = { ++ &nodes[GPIO_KEYS], &nodes[PWRON_KEY], NULL ++ }; ++ ++ return software_node_register_node_group(node_group); ++} ++ ++static void bd71828_i2c_unregister_swnodes(void *data) ++{ ++ const struct software_node *nodes = data; ++ const struct software_node * const node_group[] = { ++ &nodes[GPIO_KEYS], &nodes[PWRON_KEY], NULL ++ }; ++ ++ software_node_unregister_node_group(node_group); ++} ++ ++static int bd71828_i2c_register_pwrbutton(struct device *dev, int button_irq, ++ struct irq_domain *irq_domain) ++{ ++ const struct resource res[] = { ++ DEFINE_RES_IRQ_NAMED(button_irq, "bd71828-pwrkey"), ++ }; ++ struct mfd_cell gpio_keys_cell = { ++ .name = "gpio-keys", ++ .resources = res, ++ .num_resources = ARRAY_SIZE(res), ++ }; ++ struct software_node *nodes; ++ int ret; ++ ++ nodes = devm_kcalloc(dev, 2, sizeof(*nodes), GFP_KERNEL); ++ if (!nodes) ++ return -ENOMEM; ++ ++ nodes[GPIO_KEYS].name = devm_kasprintf(dev, GFP_KERNEL, "%s-power-key", dev_name(dev)); ++ if (!nodes[GPIO_KEYS].name) ++ return -ENOMEM; ++ ++ nodes[GPIO_KEYS].properties = bd71828_powerkey_parent_props; ++ ++ nodes[PWRON_KEY].parent = &nodes[GPIO_KEYS]; ++ nodes[PWRON_KEY].properties = bd71828_powerkey_props; ++ ++ ret = bd71828_i2c_register_swnodes(nodes); ++ if (ret) ++ return ret; ++ ++ ret = devm_add_action_or_reset(dev, bd71828_i2c_unregister_swnodes, nodes); ++ if (ret) ++ return ret; ++ ++ gpio_keys_cell.swnode = &nodes[GPIO_KEYS]; ++ ++ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, &gpio_keys_cell, 1, ++ NULL, 0, irq_domain); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to register power-button"); ++ ++ return 0; ++} ++ + static struct i2c_client *bd71828_dev; + static void bd71828_power_off(void) + { +@@ -929,6 +989,7 @@ static struct regmap *bd72720_do_regmaps(struct i2c_client *i2c) + static int bd71828_i2c_probe(struct i2c_client *i2c) + { + struct regmap_irq_chip_data *irq_data; ++ struct irq_domain *irq_domain; + int ret; + struct regmap *regmap = NULL; + const struct regmap_config *regmap_config; +@@ -1022,23 +1083,23 @@ static int bd71828_i2c_probe(struct i2c_client *i2c) + "Failed to enable main level IRQs\n"); + } + } +- if (button_irq) { +- ret = regmap_irq_get_virq(irq_data, button_irq); +- if (ret < 0) +- return dev_err_probe(&i2c->dev, ret, +- "Failed to get the power-key IRQ\n"); +- +- button.irq = ret; +- } + + ret = set_clk_mode(&i2c->dev, regmap, clkmode_reg); + if (ret) + return ret; + ++ irq_domain = regmap_irq_get_domain(irq_data); ++ + ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO, mfd, cells, +- NULL, 0, regmap_irq_get_domain(irq_data)); ++ NULL, 0, irq_domain); + if (ret) +- return dev_err_probe(&i2c->dev, ret, "Failed to create subdevices\n"); ++ return dev_err_probe(&i2c->dev, ret, "Failed to create subdevices\n"); ++ ++ if (button_irq) { ++ ret = bd71828_i2c_register_pwrbutton(&i2c->dev, button_irq, irq_domain); ++ if (ret) ++ return ret; ++ } + + if (of_device_is_system_power_controller(i2c->dev.of_node) && + chip_type == ROHM_CHIP_TYPE_BD71828) { +diff --git a/drivers/mfd/rohm-bd718x7.c b/drivers/mfd/rohm-bd718x7.c +--- a/drivers/mfd/rohm-bd718x7.c ++++ b/drivers/mfd/rohm-bd718x7.c +@@ -7,7 +7,8 @@ + // Datasheet for BD71837MWV available from + // https://www.rohm.com/datasheet/BD71837MWV/bd71837mwv-e + +-#include ++#include ++#include + #include + #include + #include +@@ -15,37 +16,16 @@ + #include + #include + #include ++#include + #include + #include + +-static struct gpio_keys_button button = { +- .code = KEY_POWER, +- .gpio = -1, +- .type = EV_KEY, +-}; +- +-static struct gpio_keys_platform_data bd718xx_powerkey_data = { +- .buttons = &button, +- .nbuttons = 1, +- .name = "bd718xx-pwrkey", +-}; +- + static struct mfd_cell bd71837_mfd_cells[] = { +- { +- .name = "gpio-keys", +- .platform_data = &bd718xx_powerkey_data, +- .pdata_size = sizeof(bd718xx_powerkey_data), +- }, + { .name = "bd71837-clk", }, + { .name = "bd71837-pmic", }, + }; + + static struct mfd_cell bd71847_mfd_cells[] = { +- { +- .name = "gpio-keys", +- .platform_data = &bd718xx_powerkey_data, +- .pdata_size = sizeof(bd718xx_powerkey_data), +- }, + { .name = "bd71847-clk", }, + { .name = "bd71847-pmic", }, + }; +@@ -125,10 +105,89 @@ static int bd718xx_init_press_duration(struct regmap *regmap, + return 0; + } + ++static const struct property_entry bd718xx_powerkey_parent_props[] = { ++ PROPERTY_ENTRY_STRING("label", "bd718xx-pwrkey"), ++ { } ++}; ++ ++static const struct property_entry bd718xx_powerkey_props[] = { ++ PROPERTY_ENTRY_U32("linux,code", KEY_POWER), ++ { } ++}; ++ ++static const struct resource bd718xx_powerkey_resources[] = { ++ DEFINE_RES_IRQ_NAMED(BD718XX_INT_PWRBTN_S, "bd718xx-pwrkey"), ++}; ++ ++#define GPIO_KEYS 0 /* Node corresponding to gpio-keys device itself */ ++#define PWRON_KEY 1 /* Node describing power button in gpio-keys */ ++ ++static int bd718xx_i2c_register_swnodes(const struct software_node *nodes) ++{ ++ const struct software_node * const node_group[] = { ++ &nodes[GPIO_KEYS], &nodes[PWRON_KEY], NULL ++ }; ++ ++ return software_node_register_node_group(node_group); ++} ++ ++static void bd718xx_i2c_unregister_swnodes(void *data) ++{ ++ const struct software_node *nodes = data; ++ const struct software_node * const node_group[] = { ++ &nodes[GPIO_KEYS], &nodes[PWRON_KEY], NULL ++ }; ++ ++ software_node_unregister_node_group(node_group); ++} ++ ++static int bd718xx_i2c_register_pwrbutton(struct device *dev, ++ struct irq_domain *irq_domain) ++{ ++ struct mfd_cell gpio_keys_cell = { ++ .name = "gpio-keys", ++ .resources = bd718xx_powerkey_resources, ++ .num_resources = ARRAY_SIZE(bd718xx_powerkey_resources), ++ }; ++ struct software_node *nodes; ++ int ret; ++ ++ nodes = devm_kcalloc(dev, 2, sizeof(*nodes), GFP_KERNEL); ++ if (!nodes) ++ return -ENOMEM; ++ ++ nodes[GPIO_KEYS].name = devm_kasprintf(dev, GFP_KERNEL, "%s-power-key", dev_name(dev)); ++ if (!nodes[GPIO_KEYS].name) ++ return -ENOMEM; ++ ++ nodes[GPIO_KEYS].properties = bd718xx_powerkey_parent_props; ++ ++ nodes[PWRON_KEY].parent = &nodes[GPIO_KEYS]; ++ nodes[PWRON_KEY].properties = bd718xx_powerkey_props; ++ ++ ret = bd718xx_i2c_register_swnodes(nodes); ++ if (ret) ++ return ret; ++ ++ ret = devm_add_action_or_reset(dev, bd718xx_i2c_unregister_swnodes, nodes); ++ if (ret) ++ return ret; ++ ++ gpio_keys_cell.swnode = &nodes[GPIO_KEYS]; ++ ++ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, &gpio_keys_cell, 1, ++ NULL, 0, irq_domain); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to register power-button"); ++ ++ return 0; ++} ++ + static int bd718xx_i2c_probe(struct i2c_client *i2c) + { + struct regmap *regmap; + struct regmap_irq_chip_data *irq_data; ++ struct irq_domain *irq_domain; + int ret; + unsigned int chip_type; + struct mfd_cell *mfd; +@@ -169,20 +228,18 @@ static int bd718xx_i2c_probe(struct i2c_client *i2c) + if (ret) + return ret; + +- ret = regmap_irq_get_virq(irq_data, BD718XX_INT_PWRBTN_S); +- +- if (ret < 0) +- return dev_err_probe(&i2c->dev, ret, "Failed to get the IRQ\n"); +- +- button.irq = ret; ++ irq_domain = regmap_irq_get_domain(irq_data); + + ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO, +- mfd, cells, NULL, 0, +- regmap_irq_get_domain(irq_data)); ++ mfd, cells, NULL, 0, irq_domain); + if (ret) +- dev_err_probe(&i2c->dev, ret, "Failed to create subdevices\n"); ++ return dev_err_probe(&i2c->dev, ret, "Failed to create subdevices\n"); + +- return ret; ++ ret = bd718xx_i2c_register_pwrbutton(&i2c->dev, irq_domain); ++ if (ret) ++ return ret; ++ ++ return 0; + } + + static const struct of_device_id bd718xx_of_match[] = { +diff --git a/include/linux/input.h b/include/linux/input.h +--- a/include/linux/input.h ++++ b/include/linux/input.h +@@ -117,7 +117,8 @@ enum input_clock_type { + * user opens device and dev->close() is called when the very + * last user closes the device + * @going_away: marks devices that are in a middle of unregistering and +- * causes input_open_device*() fail with -ENODEV. ++ * causes input_open_device() and input_inhibit/uninhibit_device() ++ * to fail with -ENODEV. + * @dev: driver model's view of this device + * @h_list: list of input handles associated with the device. When + * accessing the list dev->mutex must be held +@@ -128,11 +129,14 @@ enum input_clock_type { + * @devres_managed: indicates that devices is managed with devres framework + * and needs not be explicitly unregistered or freed. + * @timestamp: storage for a timestamp set by input_set_timestamp called +- * by a driver ++ * by a driver + * @inhibited: indicates that the input device is inhibited. If that is +- * the case then input core ignores any events generated by the device. +- * Device's close() is called when it is being inhibited and its open() +- * is called when it is being uninhibited. ++ * the case then input core ignores any events generated by the device. ++ * Device's close() is called when it is being inhibited and its open() ++ * is called when it is being uninhibited. ++ * @ready: indicates that the device has been successfully opened and is ++ * prepared to process events (like LEDs or sounds) sent from the ++ * input core. + */ + struct input_dev { + const char *name; +@@ -209,6 +213,7 @@ struct input_dev { + ktime_t timestamp[INPUT_CLK_MAX]; + + bool inhibited; ++ bool ready; + }; + #define to_input_dev(d) container_of(d, struct input_dev, dev) + +@@ -284,8 +289,9 @@ struct input_handle; + * @connect: called when attaching a handler to an input device + * @disconnect: disconnects a handler from input device + * @start: starts handler for given handle. This function is called by +- * input core right after connect() method and also when a process +- * that "grabbed" a device releases it ++ * input core when device is open and ready to process events, ++ * and also when device is uninhibited or when a process that "grabbed" ++ * a device releases it + * @passive_observer: set to %true by drivers only interested in observing + * data stream from devices if there are other users present. Such + * drivers will not result in starting underlying hardware device +diff --git a/include/linux/input/elan-i2c-ids.h b/include/linux/input/elan-i2c-ids.h +--- a/include/linux/input/elan-i2c-ids.h ++++ b/include/linux/input/elan-i2c-ids.h +@@ -18,7 +18,7 @@ + #ifndef __ELAN_I2C_IDS_H + #define __ELAN_I2C_IDS_H + +-#include ++#include + + static const struct acpi_device_id elan_acpi_id[] = { + { "ELAN0000", 0 }, diff --git a/pkgbuilds/linux-omarchy-bore/0560-input-updates.patch.sig b/pkgbuilds/linux-omarchy-bore/0560-input-updates.patch.sig new file mode 100644 index 0000000..594f27d Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0560-input-updates.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0560-input.patch b/pkgbuilds/linux-omarchy-bore/0560-input.patch deleted file mode 100644 index 0d58569..0000000 --- a/pkgbuilds/linux-omarchy-bore/0560-input.patch +++ /dev/null @@ -1,76 +0,0 @@ -diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c ---- a/drivers/input/mouse/focaltech.c -+++ b/drivers/input/mouse/focaltech.c -@@ -78,8 +78,8 @@ struct focaltech_finger_state { - * Absolute position (from the bottom left corner) of the - * finger. - */ -- unsigned int x; -- unsigned int y; -+ int x; -+ int y; - }; - - /* -@@ -108,7 +108,7 @@ struct focaltech_hw_state { - }; - - struct focaltech_data { -- unsigned int x_max, y_max; -+ int x_max, y_max; - struct focaltech_hw_state state; - }; - -@@ -126,17 +126,16 @@ static void focaltech_report_state(struct psmouse *psmouse) - input_mt_slot(dev, i); - input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); - if (active) { -- unsigned int clamped_x, clamped_y; - /* - * The touchpad might report invalid data, so we clamp - * the resulting values so that we do not confuse -- * userspace. -+ * userspace or accumulate coordinate wind-up. - */ -- clamped_x = clamp(finger->x, 0U, priv->x_max); -- clamped_y = clamp(finger->y, 0U, priv->y_max); -- input_report_abs(dev, ABS_MT_POSITION_X, clamped_x); -+ finger->x = clamp(finger->x, 0, priv->x_max); -+ finger->y = clamp(finger->y, 0, priv->y_max); -+ input_report_abs(dev, ABS_MT_POSITION_X, finger->x); - input_report_abs(dev, ABS_MT_POSITION_Y, -- priv->y_max - clamped_y); -+ priv->y_max - finger->y); - input_report_abs(dev, ABS_TOOL_WIDTH, state->width); - } - } -diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c ---- a/drivers/input/mouse/psmouse-base.c -+++ b/drivers/input/mouse/psmouse-base.c -@@ -267,7 +267,15 @@ void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) - */ - static int psmouse_handle_byte(struct psmouse *psmouse) - { -- psmouse_ret_t rc = psmouse->protocol_handler(psmouse); -+ psmouse_ret_t rc; -+ -+ /* protocol_handler is NULL when device is being disconnected */ -+ if (unlikely(!psmouse->protocol_handler)) { -+ psmouse->pktcnt = 0; -+ return 0; -+ } -+ -+ rc = psmouse->protocol_handler(psmouse); - - switch (rc) { - case PSMOUSE_BAD_DATA: -@@ -1466,6 +1474,9 @@ static void psmouse_disconnect(struct serio *serio) - psmouse_deactivate(parent); - } - -+ scoped_guard(serio_pause_rx, serio) -+ psmouse->protocol_handler = NULL; -+ - if (psmouse->disconnect) - psmouse->disconnect(psmouse); - diff --git a/pkgbuilds/linux-omarchy-bore/0560-input.patch.sig b/pkgbuilds/linux-omarchy-bore/0560-input.patch.sig deleted file mode 100644 index 409e402..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/0560-input.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/0561-input-fixes.patch b/pkgbuilds/linux-omarchy-bore/0561-input-fixes.patch new file mode 100644 index 0000000..b067aaa --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0561-input-fixes.patch @@ -0,0 +1,22 @@ +diff --git a/drivers/input/touchscreen/tsc2007_core.c b/drivers/input/touchscreen/tsc2007_core.c +--- a/drivers/input/touchscreen/tsc2007_core.c ++++ b/drivers/input/touchscreen/tsc2007_core.c +@@ -221,7 +221,6 @@ static int tsc2007_get_pendown_state_gpio(struct device *dev) + static int tsc2007_probe_properties(struct device *dev, struct tsc2007 *ts) + { + u32 val32; +- u64 val64; + + if (!device_property_read_u32(dev, "ti,max-rt", &val32)) + ts->max_rt = val32; +@@ -237,8 +236,8 @@ static int tsc2007_probe_properties(struct device *dev, struct tsc2007 *ts) + if (!device_property_read_u32(dev, "ti,fuzzz", &val32)) + ts->fuzzz = val32; + +- if (!device_property_read_u64(dev, "ti,poll-period", &val64)) +- ts->poll_period = msecs_to_jiffies(val64); ++ if (!device_property_read_u32(dev, "ti,poll-period", &val32)) ++ ts->poll_period = msecs_to_jiffies(val32); + else + ts->poll_period = msecs_to_jiffies(1); + diff --git a/pkgbuilds/linux-omarchy-bore/0561-input-fixes.patch.sig b/pkgbuilds/linux-omarchy-bore/0561-input-fixes.patch.sig new file mode 100644 index 0000000..609b474 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0561-input-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0562-hid-fixes.patch b/pkgbuilds/linux-omarchy-bore/0562-hid-fixes.patch new file mode 100644 index 0000000..47e03fa --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0562-hid-fixes.patch @@ -0,0 +1,483 @@ +diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_common.h b/drivers/hid/amd-sfh-hid/amd_sfh_common.h +--- a/drivers/hid/amd-sfh-hid/amd_sfh_common.h ++++ b/drivers/hid/amd-sfh-hid/amd_sfh_common.h +@@ -12,11 +12,15 @@ + + #include + #include ++#include + #include "amd_sfh_hid.h" + + #define PCI_DEVICE_ID_AMD_MP2 0x15E4 + #define PCI_DEVICE_ID_AMD_MP2_1_1 0x164A + ++/* The BAR 2 size must cover the highest register offset (0x10958) */ ++#define AMD_SFH_MIN_BAR_SIZE SZ_128K ++ + #define AMD_C2P_MSG(regno) (0x10500 + ((regno) * 4)) + #define AMD_P2C_MSG(regno) (0x10680 + ((regno) * 4)) + +diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c +--- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c ++++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c +@@ -451,6 +451,16 @@ static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i + if (rc) + return rc; + ++ if (!(pci_resource_flags(pdev, 2) & IORESOURCE_MEM)) { ++ dev_err(&pdev->dev, "BAR 2 is not IORESOURCE_MEM\n"); ++ return -ENODEV; ++ } ++ ++ if (pci_resource_len(pdev, 2) < AMD_SFH_MIN_BAR_SIZE) { ++ dev_err(&pdev->dev, "BAR 2 is too small\n"); ++ return -EINVAL; ++ } ++ + rc = pcim_iomap_regions(pdev, BIT(2), DRIVER_NAME); + if (rc) + return rc; +diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c +--- a/drivers/hid/bpf/hid_bpf_dispatch.c ++++ b/drivers/hid/bpf/hid_bpf_dispatch.c +@@ -359,7 +359,7 @@ hid_bpf_release_context(struct hid_bpf_ctx *ctx) + + static int + __hid_bpf_hw_check_params(struct hid_bpf_ctx *ctx, __u8 *buf, size_t *buf__sz, +- enum hid_report_type rtype) ++ enum hid_report_type rtype, bool hw_request) + { + struct hid_report_enum *report_enum; + struct hid_report *report; +@@ -388,6 +388,10 @@ __hid_bpf_hw_check_params(struct hid_bpf_ctx *ctx, __u8 *buf, size_t *buf__sz, + + report_len = hid_report_len(report); + ++ /* unnumbered reports need to have a report ID reserved in the first byte */ ++ if (hw_request && report_enum->numbered == 0) ++ report_len += 1; ++ + if (*buf__sz > report_len) + *buf__sz = report_len; + +@@ -420,7 +424,7 @@ hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz, + return -EDEADLOCK; + + /* check arguments */ +- ret = __hid_bpf_hw_check_params(ctx, buf, &size, rtype); ++ ret = __hid_bpf_hw_check_params(ctx, buf, &size, rtype, true); + if (ret) + return ret; + +@@ -480,7 +484,7 @@ hid_bpf_hw_output_report(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz) + return -EDEADLOCK; + + /* check arguments */ +- ret = __hid_bpf_hw_check_params(ctx, buf, &size, HID_OUTPUT_REPORT); ++ ret = __hid_bpf_hw_check_params(ctx, buf, &size, HID_OUTPUT_REPORT, true); + if (ret) + return ret; + +@@ -506,7 +510,7 @@ __hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *b + return -EDEADLOCK; + + /* check arguments */ +- ret = __hid_bpf_hw_check_params(ctx, buf, &size, type); ++ ret = __hid_bpf_hw_check_params(ctx, buf, &size, type, false); + if (ret) + return ret; + +diff --git a/drivers/hid/hid-alps.c b/drivers/hid/hid-alps.c +--- a/drivers/hid/hid-alps.c ++++ b/drivers/hid/hid-alps.c +@@ -407,6 +407,8 @@ static int u1_raw_event(struct alps_dev *hdata, u8 *data, int size) + return 1; + + case U1_SP_ABSOLUTE_REPORT_ID: ++ if (!hdata->input2) ++ return 0; + sp_x = get_unaligned_le16(data+2); + sp_y = get_unaligned_le16(data+4); + +@@ -738,7 +740,6 @@ static int alps_input_configured(struct hid_device *hdev, struct hid_input *hi) + goto exit; + } + +- data->input2 = input2; + input2->phys = input->phys; + input2->name = "DualPoint Stick"; + input2->id.bustype = BUS_I2C; +@@ -762,11 +763,12 @@ static int alps_input_configured(struct hid_device *hdev, struct hid_input *hi) + __set_bit(INPUT_PROP_POINTER, input2->propbit); + __set_bit(INPUT_PROP_POINTING_STICK, input2->propbit); + +- if (input_register_device(data->input2)) { ++ if (input_register_device(input2)) { + input_free_device(input2); + ret = -ENOENT; + goto exit; + } ++ data->input2 = input2; + } + + exit: +@@ -823,6 +825,24 @@ static int alps_probe(struct hid_device *hdev, const struct hid_device_id *id) + return 0; + } + ++static void alps_remove(struct hid_device *hdev) ++{ ++ struct alps_dev *data = hid_get_drvdata(hdev); ++ ++ /* ++ * input2 ("DualPoint Stick") is allocated separately and is not ++ * tracked in hdev->inputs, so the default remove path ++ * (hid_hw_stop -> hidinput_disconnect) does not unregister it. ++ * ++ * Stop the device first so that no URB callback can touch input2 ++ * while it is being unregistered, then drop it explicitly. ++ */ ++ hid_hw_stop(hdev); ++ ++ if (data->input2) ++ input_unregister_device(data->input2); ++} ++ + static const struct hid_device_id alps_id[] = { + { HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, + USB_VENDOR_ID_ALPS_JP, HID_DEVICE_ID_ALPS_U1_DUAL) }, +@@ -845,6 +865,7 @@ static struct hid_driver alps_driver = { + .input_configured = alps_input_configured, + .resume = pm_ptr(alps_post_resume), + .reset_resume = pm_ptr(alps_post_reset), ++ .remove = alps_remove, + }; + + module_hid_driver(alps_driver); +diff --git a/drivers/hid/hid-corsair-void.c b/drivers/hid/hid-corsair-void.c +--- a/drivers/hid/hid-corsair-void.c ++++ b/drivers/hid/hid-corsair-void.c +@@ -51,20 +51,23 @@ + /* Receiver report information: (ID 102) */ + /* -------------------------------------------------------------------------- */ + /* +- * When queried, the recevier responds with 4 bytes to describe the firmware +- * The first 2 bytes are for the receiver, the second 2 are the headset ++ * When queried, the receiver responds with 5 bytes to describe the firmware ++ * The first byte is the ID, then 2 bytes for the receiver, then 2 for the headset + * The headset firmware version will be 0 if no headset is connected + * +- * 0: Recevier firmware major version ++ * 0: Report ID ++ * 102 for the firmware packet ++ * ++ * 1: Receiver firmware major version + * Major version of the receiver's firmware + * +- * 1: Recevier firmware minor version ++ * 2: Receiver firmware minor version + * Minor version of the receiver's firmware + * +- * 2: Headset firmware major version ++ * 3: Headset firmware major version + * Major version of the headset's firmware + * +- * 3: Headset firmware minor version ++ * 4: Headset firmware minor version + * Minor version of the headset's firmware + */ + /* -------------------------------------------------------------------------- */ +diff --git a/drivers/hid/hid-elecom.c b/drivers/hid/hid-elecom.c +--- a/drivers/hid/hid-elecom.c ++++ b/drivers/hid/hid-elecom.c +@@ -79,7 +79,7 @@ static const __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc, + case USB_DEVICE_ID_ELECOM_M_XT3URBK_00FB: + case USB_DEVICE_ID_ELECOM_M_XT3URBK_018F: + case USB_DEVICE_ID_ELECOM_M_XT3DRBK_00FC: +- case USB_DEVICE_ID_ELECOM_M_XT4DRBK: ++ case USB_DEVICE_ID_ELECOM_M_XT4DRBK_00FD: + /* + * Report descriptor format: + * 12: button bit count +@@ -104,6 +104,7 @@ static const __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc, + mouse_button_fixup(hdev, rdesc, *rsize, 12, 30, 14, 20, 8); + break; + case USB_DEVICE_ID_ELECOM_M_XT3DRBK_018C: ++ case USB_DEVICE_ID_ELECOM_M_XT4DRBK_018E: + /* + * Report descriptor format: + * 22: button bit count +@@ -148,7 +149,8 @@ static const struct hid_device_id elecom_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK_018F) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK_00FC) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK_018C) }, +- { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK_00FD) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK_018E) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1URBK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1DRBK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT2DRBK) }, +diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h +--- a/drivers/hid/hid-ids.h ++++ b/drivers/hid/hid-ids.h +@@ -472,7 +472,8 @@ + #define USB_DEVICE_ID_ELECOM_M_XT3URBK_018F 0x018f + #define USB_DEVICE_ID_ELECOM_M_XT3DRBK_00FC 0x00fc + #define USB_DEVICE_ID_ELECOM_M_XT3DRBK_018C 0x018c +-#define USB_DEVICE_ID_ELECOM_M_XT4DRBK 0x00fd ++#define USB_DEVICE_ID_ELECOM_M_XT4DRBK_00FD 0x00fd ++#define USB_DEVICE_ID_ELECOM_M_XT4DRBK_018E 0x018e + #define USB_DEVICE_ID_ELECOM_M_DT1URBK 0x00fe + #define USB_DEVICE_ID_ELECOM_M_DT1DRBK 0x00ff + #define USB_DEVICE_ID_ELECOM_M_DT2DRBK 0x018d +@@ -759,6 +760,7 @@ + #define I2C_DEVICE_ID_ITE_LENOVO_LEGION_Y720 0x837a + #define USB_DEVICE_ID_ITE_LENOVO_YOGA900 0x8396 + #define I2C_DEVICE_ID_ITE_LENOVO_YOGA_SLIM_7X_KEYBOARD 0x8987 ++#define I2C_DEVICE_ID_ITE_LENOVO_YOGA_SLIM_7X_G11_KEYBOARD 0x83db + #define USB_DEVICE_ID_ITE8595 0x8595 + #define USB_DEVICE_ID_ITE_MEDION_E1239T 0xce50 + +@@ -1283,6 +1285,9 @@ + #define USB_DEVICE_ID_SAMSUNG_WIRELESS_UNIVERSAL_KBD 0xa006 + #define USB_DEVICE_ID_SAMSUNG_WIRELESS_MULTI_HOGP_KBD 0xa064 + ++#define USB_VENDOR_ID_SDINNOVATION 0x36ae ++#define USB_DEVICE_ID_SDINNOVATION_GAMING_KBD 0xfeab ++ + #define USB_VENDOR_ID_SEMICO 0x1a2c + #define USB_DEVICE_ID_SEMICO_USB_KEYKOARD 0x0023 + #define USB_DEVICE_ID_SEMICO_USB_KEYKOARD2 0x0027 +diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c +--- a/drivers/hid/hid-logitech-hidpp.c ++++ b/drivers/hid/hid-logitech-hidpp.c +@@ -4656,6 +4656,8 @@ static const struct hid_device_id hidpp_devices[] = { + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC082) }, + { /* Logitech G502 Lightspeed Wireless Gaming Mouse over USB */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC08D) }, ++ { /* Logitech G502 X Lightspeed Wireless Gaming Mouse over USB */ ++ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC098) }, + { /* Logitech G703 Gaming Mouse over USB */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC087) }, + { /* Logitech G703 Hero Gaming Mouse over USB */ +diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c +--- a/drivers/hid/hid-multitouch.c ++++ b/drivers/hid/hid-multitouch.c +@@ -79,6 +79,7 @@ MODULE_LICENSE("GPL"); + #define MT_QUIRK_APPLE_TOUCHBAR BIT(23) + #define MT_QUIRK_YOGABOOK9I BIT(24) + #define MT_QUIRK_KEEP_LATENCY_ON_CLOSE BIT(25) ++#define MT_QUIRK_IGNORE_FEATURE_ID_MISMATCH BIT(26) + + #define MT_INPUTMODE_TOUCHSCREEN 0x02 + #define MT_INPUTMODE_TOUCHPAD 0x03 +@@ -235,6 +236,7 @@ static void mt_post_parse(struct mt_device *td, struct mt_application *app); + #define MT_CLS_APPLE_TOUCHBAR 0x0114 + #define MT_CLS_YOGABOOK9I 0x0115 + #define MT_CLS_EGALAX_P80H84 0x0116 ++#define MT_CLS_ASUS_ROG_Z13_FOLIO 0x0117 + #define MT_CLS_SIS 0x0457 + + #define MT_DEFAULT_MAXCONTACT 10 +@@ -405,6 +407,16 @@ static const struct mt_class mt_classes[] = { + .quirks = MT_QUIRK_ALWAYS_VALID | + MT_QUIRK_CONTACT_CNT_ACCURATE | + MT_QUIRK_ASUS_CUSTOM_UP }, ++ { .name = MT_CLS_ASUS_ROG_Z13_FOLIO, ++ .quirks = MT_QUIRK_ALWAYS_VALID | ++ MT_QUIRK_IGNORE_DUPLICATES | ++ MT_QUIRK_HOVERING | ++ MT_QUIRK_CONTACT_CNT_ACCURATE | ++ MT_QUIRK_STICKY_FINGERS | ++ MT_QUIRK_WIN8_PTP_BUTTONS | ++ MT_QUIRK_CONFIDENCE | ++ MT_QUIRK_IGNORE_FEATURE_ID_MISMATCH, ++ .export_all_inputs = true }, + { .name = MT_CLS_VTL, + .quirks = MT_QUIRK_ALWAYS_VALID | + MT_QUIRK_CONTACT_CNT_ACCURATE | +@@ -507,6 +519,7 @@ static const struct attribute_group mt_attribute_group = { + + static void mt_get_feature(struct hid_device *hdev, struct hid_report *report) + { ++ struct mt_device *td = hid_get_drvdata(hdev); + int ret; + u32 size = hid_report_len(report); + u8 *buf; +@@ -528,8 +541,14 @@ static void mt_get_feature(struct hid_device *hdev, struct hid_report *report) + dev_warn(&hdev->dev, "failed to fetch feature %d\n", + report->id); + } else { +- /* The report ID in the request and the response should match */ +- if (report->id != buf[0]) { ++ /* ++ * The report ID in the request and the response should match. ++ * Some firmware (e.g. the ASUS ROG Z13 Folio ++ * touchpad) returns a mismatched ID on this specific fetch; ++ * tolerate it only for devices explicitly flagged as such. ++ */ ++ if (report->id != buf[0] && ++ !(td->mtclass.quirks & MT_QUIRK_IGNORE_FEATURE_ID_MISMATCH)) { + hid_err(hdev, "Returned feature report did not match the request\n"); + goto free; + } +@@ -2726,6 +2745,12 @@ static const struct hid_device_id mt_devices[] = { + HID_DEVICE(BUS_I2C, HID_GROUP_MULTITOUCH_WIN_8, + I2C_VENDOR_ID_HANTICK, I2C_PRODUCT_ID_HANTICK_5288) }, + ++ /* Asus ROG Flow Z13 (2025) GZ302EA keyboard-cover touchpad */ ++ { .driver_data = MT_CLS_ASUS_ROG_Z13_FOLIO, ++ HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8, ++ USB_VENDOR_ID_ASUSTEK, ++ USB_DEVICE_ID_ASUSTEK_ROG_Z13_FOLIO) }, ++ + /* Generic MT device */ + { HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH, HID_ANY_ID, HID_ANY_ID) }, + +diff --git a/drivers/hid/hid-oxp.c b/drivers/hid/hid-oxp.c +--- a/drivers/hid/hid-oxp.c ++++ b/drivers/hid/hid-oxp.c +@@ -1552,9 +1552,9 @@ static int oxp_hid_probe(struct hid_device *hdev, + + static void oxp_hid_remove(struct hid_device *hdev) + { +- cancel_delayed_work(&drvdata.oxp_rgb_queue); +- cancel_delayed_work(&drvdata.oxp_btn_queue); +- cancel_delayed_work(&drvdata.oxp_mcu_init); ++ cancel_delayed_work_sync(&drvdata.oxp_rgb_queue); ++ cancel_delayed_work_sync(&drvdata.oxp_btn_queue); ++ cancel_delayed_work_sync(&drvdata.oxp_mcu_init); + hid_hw_close(hdev); + hid_hw_stop(hdev); + } +diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c +--- a/drivers/hid/hid-quirks.c ++++ b/drivers/hid/hid-quirks.c +@@ -183,6 +183,7 @@ static const struct hid_device_id hid_quirks[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_X52_2), HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE }, + { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_X52_PRO), HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE }, + { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_X65), HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SDINNOVATION, USB_DEVICE_ID_SDINNOVATION_GAMING_KBD), HID_QUIRK_ALWAYS_POLL }, + { HID_USB_DEVICE(USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD2), HID_QUIRK_NO_INIT_REPORTS }, + { HID_USB_DEVICE(USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD), HID_QUIRK_NO_INIT_REPORTS }, + { HID_USB_DEVICE(USB_VENDOR_ID_SENNHEISER, USB_DEVICE_ID_SENNHEISER_BTD500USB), HID_QUIRK_NOGET }, +@@ -422,13 +423,14 @@ static const struct hid_device_id hid_have_special_driver[] = { + #endif + #if IS_ENABLED(CONFIG_HID_ELECOM) + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, +- { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XGL20DLBK) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XGL20DLBK) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1MRBK_01AC) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK_00FB) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK_018F) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK_00FC) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK_018C) }, +- { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK_00FD) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK_018E) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1URBK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1DRBK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT2DRBK) }, +diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c +--- a/drivers/hid/hid-roccat.c ++++ b/drivers/hid/hid-roccat.c +@@ -344,8 +344,6 @@ int roccat_connect(const struct class *klass, struct hid_device *hid, int report + return temp; + } + +- mutex_unlock(&devices_lock); +- + init_waitqueue_head(&device->wait); + INIT_LIST_HEAD(&device->readers); + mutex_init(&device->readers_lock); +@@ -356,6 +354,7 @@ int roccat_connect(const struct class *klass, struct hid_device *hid, int report + device->cbuf_end = 0; + device->report_size = report_size; + ++ mutex_unlock(&devices_lock); + return minor; + } + EXPORT_SYMBOL_GPL(roccat_connect); +@@ -369,15 +368,12 @@ void roccat_disconnect(int minor) + + mutex_lock(&devices_lock); + device = devices[minor]; +- mutex_unlock(&devices_lock); + + device->exist = 0; /* TODO exist maybe not needed */ + + device_destroy(device->dev->class, MKDEV(roccat_major, minor)); + +- mutex_lock(&devices_lock); + devices[minor] = NULL; +- mutex_unlock(&devices_lock); + + if (device->open) { + hid_hw_close(device->hid); +@@ -385,6 +381,8 @@ void roccat_disconnect(int minor) + } else { + roccat_free_device(device); + } ++ ++ mutex_unlock(&devices_lock); + } + EXPORT_SYMBOL_GPL(roccat_disconnect); + +diff --git a/drivers/hid/hid-winwing.c b/drivers/hid/hid-winwing.c +--- a/drivers/hid/hid-winwing.c ++++ b/drivers/hid/hid-winwing.c +@@ -315,7 +315,8 @@ static void winwing_haptic_rumble_cb(struct work_struct *work) + static int winwing_play_effect(struct input_dev *dev, void *context, + struct ff_effect *effect) + { +- struct winwing_drv_data *data = (struct winwing_drv_data *) context; ++ struct hid_device *hdev = input_get_drvdata(dev); ++ struct winwing_drv_data *data = hid_get_drvdata(hdev); + + if (effect->type != FF_RUMBLE) + return 0; +@@ -342,7 +343,12 @@ static int winwing_init_ff(struct hid_device *hdev, struct hid_input *hidinput) + + input_set_capability(hidinput->input, EV_FF, FF_RUMBLE); + +- return input_ff_create_memless(hidinput->input, data, ++ /* ++ * input_ff_create_memless() takes ownership of the context pointer ++ * and frees it on teardown; do not hand it the devm-managed drvdata. ++ * winwing_play_effect() fetches it from the input device instead. ++ */ ++ return input_ff_create_memless(hidinput->input, NULL, + winwing_play_effect); + } + +diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c +--- a/drivers/hid/i2c-hid/i2c-hid-core.c ++++ b/drivers/hid/i2c-hid/i2c-hid-core.c +@@ -123,9 +123,11 @@ static const struct i2c_hid_quirks { + __u32 quirks; + } i2c_hid_quirks[] = { + { I2C_VENDOR_ID_HANTICK, I2C_PRODUCT_ID_HANTICK_5288, +- I2C_HID_QUIRK_NO_IRQ_AFTER_RESET }, ++ I2C_HID_QUIRK_NO_IRQ_AFTER_RESET | I2C_HID_QUIRK_BAD_INPUT_SIZE }, + { I2C_VENDOR_ID_ITE, I2C_DEVICE_ID_ITE_VOYO_WINPAD_A15, + I2C_HID_QUIRK_NO_IRQ_AFTER_RESET }, ++ { USB_VENDOR_ID_ITE, I2C_DEVICE_ID_ITE_LENOVO_YOGA_SLIM_7X_G11_KEYBOARD, ++ I2C_HID_QUIRK_NO_IRQ_AFTER_RESET }, + { I2C_VENDOR_ID_RAYDIUM, I2C_PRODUCT_ID_RAYDIUM_3118, + I2C_HID_QUIRK_NO_IRQ_AFTER_RESET }, + { USB_VENDOR_ID_ALPS_JP, HID_ANY_ID, +diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c +--- a/drivers/hid/wacom_sys.c ++++ b/drivers/hid/wacom_sys.c +@@ -113,8 +113,9 @@ static int wacom_wac_pen_serial_enforce(struct hid_device *hdev, + + /* Queue events which have invalid tool type or serial number */ + for (i = 0; i < report->maxfield; i++) { +- for (j = 0; j < report->field[i]->maxusage; j++) { +- struct hid_field *field = report->field[i]; ++ struct hid_field *field = report->field[i]; ++ ++ for (j = 0; j < field->report_count; j++) { + struct hid_usage *usage = &field->usage[j]; + unsigned int equivalent_usage = wacom_equivalent_usage(usage->hid); + unsigned int offset; diff --git a/pkgbuilds/linux-omarchy-bore/0562-hid-fixes.patch.sig b/pkgbuilds/linux-omarchy-bore/0562-hid-fixes.patch.sig new file mode 100644 index 0000000..b890b57 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0562-hid-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0563-input-fixes-2.patch b/pkgbuilds/linux-omarchy-bore/0563-input-fixes-2.patch new file mode 100644 index 0000000..1682da2 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0563-input-fixes-2.patch @@ -0,0 +1,88 @@ +diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c +--- a/drivers/input/keyboard/atkbd.c ++++ b/drivers/input/keyboard/atkbd.c +@@ -1938,6 +1938,14 @@ static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = { + }, + .callback = atkbd_deactivate_fixup, + }, ++ { ++ /* Lenovo IdeaPad Slim 3 15IWC11 (83RR) */ ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "83RR"), ++ }, ++ .callback = atkbd_deactivate_fixup, ++ }, + { + /* Xiaomi Book Pro 14 (TM2424) */ + .matches = { +diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c +--- a/drivers/input/mouse/synaptics.c ++++ b/drivers/input/mouse/synaptics.c +@@ -192,6 +192,7 @@ static const char * const smbus_pnp_ids[] = { + "LEN2054", /* E480 */ + "LEN2055", /* E580 */ + "LEN2058", /* E490 */ ++ "LEN205b", /* T490 */ + "LEN2068", /* T14 Gen 1 */ + "SYN1221", /* TUXEDO InfinityBook Pro 14 v5 */ + "SYN3003", /* HP EliteBook 850 G1 */ +@@ -1839,8 +1840,10 @@ static int synaptics_setup_intertouch(struct psmouse *psmouse, + return -ENXIO; + } + +- /* Disable intertouch on known-broken board revisions */ +- if (info->board_id == 2722) { ++ /* Disable intertouch on known-broken T440p board revisions */ ++ if (info->board_id == 2722 && ++ psmouse_matches_pnp_id(psmouse, ++ (const char * const []){"LEN0036", NULL})) { + psmouse_info(psmouse, + "Disabling intertouch for board id %u\n", + info->board_id); +diff --git a/drivers/input/serio/i8042-acpipnpio.h b/drivers/input/serio/i8042-acpipnpio.h +--- a/drivers/input/serio/i8042-acpipnpio.h ++++ b/drivers/input/serio/i8042-acpipnpio.h +@@ -671,6 +671,15 @@ static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = { + }, + .driver_data = (void *)(SERIO_QUIRK_NOAUX) + }, ++ { ++ /* Fujitsu LIFEBOOK U7410 */ ++ /* Mux mode also disturbs the EC keyboard, causing spurious ACKs */ ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU CLIENT COMPUTING LIMITED"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U7410"), ++ }, ++ .driver_data = (void *)(SERIO_QUIRK_NOMUX) ++ }, + { + /* Gigabyte M912 */ + .matches = { +diff --git a/drivers/input/touchscreen/s6sy761.c b/drivers/input/touchscreen/s6sy761.c +--- a/drivers/input/touchscreen/s6sy761.c ++++ b/drivers/input/touchscreen/s6sy761.c +@@ -500,10 +500,22 @@ static int s6sy761_suspend(struct device *dev) + static int s6sy761_resume(struct device *dev) + { + struct s6sy761_data *sdata = dev_get_drvdata(dev); ++ int err; ++ ++ err = s6sy761_power_on(sdata); ++ if (err) ++ return err; + + enable_irq(sdata->client->irq); + +- return s6sy761_power_on(sdata); ++ guard(mutex)(&sdata->input->mutex); ++ ++ if (!input_device_enabled(sdata->input)) ++ return 0; ++ ++ err = i2c_smbus_write_byte(sdata->client, S6SY761_SENSE_ON); ++ ++ return err; + } + + static const struct dev_pm_ops s6sy761_pm_ops = { diff --git a/pkgbuilds/linux-omarchy-bore/0563-input-fixes-2.patch.sig b/pkgbuilds/linux-omarchy-bore/0563-input-fixes-2.patch.sig new file mode 100644 index 0000000..36b52ad Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0563-input-fixes-2.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch b/pkgbuilds/linux-omarchy-bore/0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch index e569f4c..d7870a2 100644 --- a/pkgbuilds/linux-omarchy-bore/0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch +++ b/pkgbuilds/linux-omarchy-bore/0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch @@ -1,7 +1,7 @@ diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c -@@ -1294,12 +1294,14 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id) +@@ -1480,12 +1480,14 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id) return ret; } diff --git a/pkgbuilds/linux-omarchy-bore/0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch.sig b/pkgbuilds/linux-omarchy-bore/0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch.sig index f1805db..42cf5b7 100644 Binary files a/pkgbuilds/linux-omarchy-bore/0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch.sig and b/pkgbuilds/linux-omarchy-bore/0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0600-usb4stream-fixes.patch b/pkgbuilds/linux-omarchy-bore/0600-usb4stream-fixes.patch deleted file mode 100644 index 5fe836e..0000000 --- a/pkgbuilds/linux-omarchy-bore/0600-usb4stream-fixes.patch +++ /dev/null @@ -1,47 +0,0 @@ -diff --git a/drivers/thunderbolt/stream.c b/drivers/thunderbolt/stream.c -index 4cc86d8d6491..43d29d69dcdb 100644 ---- a/drivers/thunderbolt/stream.c -+++ b/drivers/thunderbolt/stream.c -@@ -512,8 +512,10 @@ tbstream_dev_alloc_tx(struct tbstream_dev *sdev, enum tbstream_frame_pdf pdf, - dma_sync_single_for_cpu(dma_dev, sf->frame.buffer_phy, size, - DMA_TO_DEVICE); - if (pdf == TBSTREAM_DATA) { -- if (copy_page_from_iter(sf->page, 0, size, from) != size) -+ if (copy_page_from_iter(sf->page, 0, size, from) != size) { -+ sdev->tx_ring.cons--; - return ERR_PTR(-EFAULT); -+ } - } else { - memset(page_address(sf->page), 0, size); - } -@@ -671,7 +673,7 @@ tbstream_dev_fops_read_iter(struct kiocb *kiocb, struct iov_iter *to) - } - - nbytes = 0; -- while (nbytes < iov_iter_count(to)) { -+ while (iov_iter_count(to)) { - struct tbstream_frame *sf; - size_t size, sf_size; - -@@ -693,7 +695,7 @@ tbstream_dev_fops_read_iter(struct kiocb *kiocb, struct iov_iter *to) - } - - sf_size = tb_ring_frame_size(&sf->frame); -- size = min(iov_iter_count(to) - nbytes, sf_size); -+ size = min(iov_iter_count(to), sf_size); - - if (copy_page_to_iter(sf->page, sf->offset, size, to) != size) { - ret = -EFAULT; -@@ -763,10 +765,10 @@ tbstream_dev_fops_write_iter(struct kiocb *kiocb, struct iov_iter *from) - } - - nbytes = 0; -- while (nbytes < iov_iter_count(from)) { -+ while (iov_iter_count(from)) { - size_t size; - -- size = min(iov_iter_count(from) - nbytes, TB_MAX_FRAME_SIZE); -+ size = min(iov_iter_count(from), TB_MAX_FRAME_SIZE); - ret = tbstream_dev_send_data(sdev, from, size); - if (ret) { - /* diff --git a/pkgbuilds/linux-omarchy-bore/0600-usb4stream-fixes.patch.sig b/pkgbuilds/linux-omarchy-bore/0600-usb4stream-fixes.patch.sig deleted file mode 100644 index 33208c2..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/0600-usb4stream-fixes.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/0601-thunderbolt-updates.patch b/pkgbuilds/linux-omarchy-bore/0601-thunderbolt-updates.patch new file mode 100644 index 0000000..7a0d9ca --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0601-thunderbolt-updates.patch @@ -0,0 +1,1131 @@ +diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c +--- a/drivers/net/thunderbolt/main.c ++++ b/drivers/net/thunderbolt/main.c +@@ -1362,7 +1362,7 @@ static void tbnet_generate_mac(struct net_device *dev) + dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; + } + +-static int tbnet_probe(struct tb_service *svc, const struct tb_service_id *id) ++static int tbnet_probe(struct tb_service *svc) + { + struct tb_xdomain *xd = tb_service_parent(svc); + struct net_device *dev; +@@ -1482,7 +1482,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(tbnet_pm_ops, tbnet_suspend, tbnet_resume); + + static const struct tb_service_id tbnet_ids[] = { + { TB_SERVICE("network", 1) }, +- { }, ++ { } + }; + MODULE_DEVICE_TABLE(tbsvc, tbnet_ids); + +diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c +--- a/drivers/thunderbolt/debugfs.c ++++ b/drivers/thunderbolt/debugfs.c +@@ -136,13 +136,13 @@ static void *validate_and_copy_from_user(const void __user *user_buf, + if (!access_ok(user_buf, *count)) + return ERR_PTR(-EFAULT); + +- buf = (void *)get_zeroed_page(GFP_KERNEL); ++ buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return ERR_PTR(-ENOMEM); + + nbytes = min_t(size_t, *count, PAGE_SIZE); + if (copy_from_user(buf, user_buf, nbytes)) { +- free_page((unsigned long)buf); ++ kfree(buf); + return ERR_PTR(-EFAULT); + } + +@@ -265,7 +265,7 @@ static ssize_t regs_write(struct tb_switch *sw, struct tb_port *port, + out: + pm_runtime_mark_last_busy(&sw->dev); + pm_runtime_put_autosuspend(&sw->dev); +- free_page((unsigned long)buf); ++ kfree(buf); + + return ret < 0 ? ret : count; + } +@@ -406,7 +406,7 @@ static ssize_t port_sb_regs_write(struct file *file, const char __user *user_buf + out: + pm_runtime_mark_last_busy(&sw->dev); + pm_runtime_put_autosuspend(&sw->dev); +- free_page((unsigned long)buf); ++ kfree(buf); + + return ret < 0 ? ret : count; + } +@@ -439,7 +439,7 @@ static ssize_t retimer_sb_regs_write(struct file *file, + out: + pm_runtime_mark_last_busy(&rt->dev); + pm_runtime_put_autosuspend(&rt->dev); +- free_page((unsigned long)buf); ++ kfree(buf); + + return ret < 0 ? ret : count; + } +@@ -652,7 +652,7 @@ margining_ber_level_write(struct file *file, const char __user *user_buf, + margining->ber_level = val; + + out_free: +- free_page((unsigned long)buf); ++ kfree(buf); + out_unlock: + mutex_unlock(&tb->lock); + +@@ -829,7 +829,7 @@ margining_lanes_write(struct file *file, const char __user *user_buf, + } + } + +- free_page((unsigned long)buf); ++ kfree(buf); + + if (lane == -1) + return -EINVAL; +@@ -958,7 +958,7 @@ margining_error_counter_write(struct file *file, const char __user *user_buf, + else + goto err_free; + +- free_page((unsigned long)buf); ++ kfree(buf); + + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { + if (!margining->software) +@@ -970,7 +970,7 @@ margining_error_counter_write(struct file *file, const char __user *user_buf, + return count; + + err_free: +- free_page((unsigned long)buf); ++ kfree(buf); + return -EINVAL; + } + +@@ -1116,7 +1116,7 @@ static ssize_t margining_mode_write(struct file *file, + mutex_unlock(&tb->lock); + + out_free: +- free_page((unsigned long)buf); ++ kfree(buf); + return ret ? ret : count; + } + +@@ -1503,7 +1503,7 @@ static ssize_t margining_test_write(struct file *file, + mutex_unlock(&tb->lock); + + out_free: +- free_page((unsigned long)buf); ++ kfree(buf); + return ret ? ret : count; + } + +@@ -1569,7 +1569,7 @@ static ssize_t margining_margin_write(struct file *file, + mutex_unlock(&tb->lock); + + out_free: +- free_page((unsigned long)buf); ++ kfree(buf); + return ret ? ret : count; + } + +@@ -1624,7 +1624,7 @@ static ssize_t margining_eye_write(struct file *file, + ret = -EINVAL; + } + +- free_page((unsigned long)buf); ++ kfree(buf); + return ret ? ret : count; + } + +@@ -1934,7 +1934,7 @@ static ssize_t counters_write(struct file *file, const char __user *user_buf, + out: + pm_runtime_mark_last_busy(&sw->dev); + pm_runtime_put_autosuspend(&sw->dev); +- free_page((unsigned long)buf); ++ kfree(buf); + + return ret < 0 ? ret : count; + } +diff --git a/drivers/thunderbolt/dma_test.c b/drivers/thunderbolt/dma_test.c +--- a/drivers/thunderbolt/dma_test.c ++++ b/drivers/thunderbolt/dma_test.c +@@ -636,7 +636,7 @@ static void dma_test_debugfs_init(struct tb_service *svc) + debugfs_create_file("test", 0200, debugfs_dir, svc, &test_fops); + } + +-static int dma_test_probe(struct tb_service *svc, const struct tb_service_id *id) ++static int dma_test_probe(struct tb_service *svc) + { + struct tb_xdomain *xd = tb_service_parent(svc); + struct dma_test *dt; +@@ -689,7 +689,7 @@ static const struct dev_pm_ops dma_test_pm_ops = { + + static const struct tb_service_id dma_test_ids[] = { + { TB_SERVICE("dma_test", 1) }, +- { }, ++ { } + }; + MODULE_DEVICE_TABLE(tbsvc, dma_test_ids); + +diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c +--- a/drivers/thunderbolt/domain.c ++++ b/drivers/thunderbolt/domain.c +@@ -77,12 +77,10 @@ static int tb_service_probe(struct device *dev) + { + struct tb_service *svc = tb_to_service(dev); + struct tb_service_driver *driver; +- const struct tb_service_id *id; + + driver = container_of(dev->driver, struct tb_service_driver, driver); +- id = __tb_service_match(dev, &driver->driver); + +- return driver->probe(svc, id); ++ return driver->probe(svc); + } + + static void tb_service_remove(struct device *dev) +@@ -790,6 +788,21 @@ int tb_domain_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, + transmit_ring, receive_path, receive_ring); + } + ++static void tb_domain_reset_interface(struct tb *tb) ++{ ++ struct tb_nhi *nhi = tb->nhi; ++ ++ if (!nhi->ops->reset_interface) ++ return; ++ ++ guard(mutex)(&tb->lock); ++ ++ /* The reset clears the ring state so stop the control channel */ ++ tb_ctl_stop(tb->ctl); ++ nhi->ops->reset_interface(nhi); ++ tb_ctl_start(tb->ctl); ++} ++ + /** + * tb_domain_disconnect_xdomain_paths() - Disable DMA paths for XDomain + * @tb: Domain disabling the DMA paths +@@ -812,11 +825,20 @@ int tb_domain_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, + int transmit_path, int transmit_ring, + int receive_path, int receive_ring) + { ++ int ret; ++ + if (!tb->cm_ops->disconnect_xdomain_paths) + return -ENOTSUPP; + +- return tb->cm_ops->disconnect_xdomain_paths(tb, xd, transmit_path, ++ ret = tb->cm_ops->disconnect_xdomain_paths(tb, xd, transmit_path, + transmit_ring, receive_path, receive_ring); ++ if (ret) ++ return ret; ++ ++ if (tb->nhi->quirks & QUIRK_RESET_DMA_ON_TEARDOWN) ++ tb_domain_reset_interface(tb); ++ ++ return 0; + } + + static int disconnect_xdomain(struct device *dev, void *data) +diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c +--- a/drivers/thunderbolt/nhi.c ++++ b/drivers/thunderbolt/nhi.c +@@ -235,6 +235,12 @@ static void ring_write_descriptors(struct tb_ring *ring) + { + struct ring_frame *frame, *n; + struct ring_desc *descriptor; ++ u32 flags; ++ ++ flags = RING_DESC_POSTED; ++ if (!(ring->flags & RING_FLAG_NO_INTERRUPT)) ++ flags |= RING_DESC_INTERRUPT; ++ + list_for_each_entry_safe(frame, n, &ring->queue, list) { + if (ring_full(ring)) + break; +@@ -242,7 +248,7 @@ static void ring_write_descriptors(struct tb_ring *ring) + descriptor = &ring->descriptors[ring->head]; + descriptor->phys = frame->buffer_phy; + descriptor->time = 0; +- descriptor->flags = RING_DESC_POSTED | RING_DESC_INTERRUPT; ++ descriptor->flags = flags; + if (ring->is_tx) { + descriptor->length = frame->size; + descriptor->eof = frame->eof; +@@ -339,8 +345,9 @@ EXPORT_SYMBOL_GPL(__tb_ring_enqueue); + * @ring: Ring to poll + * + * This function can be called when @start_poll callback of the @ring +- * has been called. It will read one completed frame from the ring and +- * return it to the caller. ++ * has been called or the ring is created with %RING_FLAG_NO_INTERRUPT. ++ * It will read one completed frame from the ring and return it to the ++ * caller. + * + * Return: Pointer to &struct ring_frame, %NULL if there is no more + * completed frames. +@@ -538,6 +545,12 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, + dev_dbg(nhi->dev, "allocating %s ring %d of size %d\n", + transmit ? "TX" : "RX", hop, size); + ++ if ((flags & RING_FLAG_NO_INTERRUPT) && start_poll) { ++ dev_WARN(nhi->dev, ++ "start_poll() and NO_INTERRUPT cannot be used at the same time\n"); ++ return NULL; ++ } ++ + ring = kzalloc_obj(*ring); + if (!ring) + return NULL; +@@ -568,7 +581,7 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, + if (!ring->descriptors) + goto err_free_ring; + +- if (nhi->ops->request_ring_irq) { ++ if (!(flags & RING_FLAG_NO_INTERRUPT) && nhi->ops->request_ring_irq) { + if (nhi->ops->request_ring_irq(ring, flags & RING_FLAG_NO_SUSPEND)) + goto err_free_descs; + } +@@ -701,7 +714,8 @@ void tb_ring_start(struct tb_ring *ring) + ring_iowrite32options(ring, flags, 0); + } + +- ring_interrupt_active(ring, true); ++ if (!(ring->flags & RING_FLAG_NO_INTERRUPT)) ++ ring_interrupt_active(ring, true); + ring->running = true; + err: + spin_unlock(&ring->lock); +@@ -761,7 +775,8 @@ void tb_ring_stop(struct tb_ring *ring) + RING_TYPE(ring), ring->hop); + goto err; + } +- ring_interrupt_active(ring, false); ++ if (!(ring->flags & RING_FLAG_NO_INTERRUPT)) ++ ring_interrupt_active(ring, false); + + ring_iowrite32options(ring, 0, 0); + ring_iowrite64desc(ring, 0, 0); +@@ -1160,6 +1175,32 @@ static void nhi_reset(struct tb_nhi *nhi) + dev_warn(nhi->dev, "timeout resetting host router\n"); + } + ++/** ++ * nhi_reset_interface() - Reset the host interface ++ * @nhi: Host interface to reset ++ * ++ * Brings the registers in the memory BAR back to their default state and ++ * clears the End-to-End Flow Control state. The caller is responsible for ++ * stopping the control channel over the reset because it clears the ring ++ * state as well. ++ */ ++void nhi_reset_interface(struct tb_nhi *nhi) ++{ ++ u32 val; ++ ++ val = ioread32(nhi->iobase + REG_CAPS); ++ /* Only v1 host interfaces implement the reset */ ++ if (FIELD_GET(REG_CAPS_VERSION_MASK, val) >= REG_CAPS_VERSION_2) ++ return; ++ ++ dev_dbg(nhi->dev, "issuing host interface reset\n"); ++ ++ iowrite32(REG_HOST_INTERFACE_RESET_RST, ++ nhi->iobase + REG_HOST_INTERFACE_RESET); ++ /* Wait for tHIReset (10 ms) to complete */ ++ usleep_range(10000, 20000); ++} ++ + static struct tb *nhi_select_cm(struct tb_nhi *nhi) + { + struct tb *tb; +@@ -1235,6 +1276,8 @@ int nhi_probe(struct tb_nhi *nhi) + + dev_dbg(dev, "NHI initialized, starting thunderbolt\n"); + ++ nhi->host_reset = host_reset; ++ + res = tb_domain_add(tb, host_reset); + if (res) { + /* +diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h +--- a/drivers/thunderbolt/nhi.h ++++ b/drivers/thunderbolt/nhi.h +@@ -36,6 +36,8 @@ irqreturn_t nhi_msi(int irq, void *data); + irqreturn_t ring_msix(int irq, void *data); + int nhi_probe(struct tb_nhi *nhi); + void nhi_shutdown(struct tb_nhi *nhi); ++void nhi_reset_interface(struct tb_nhi *nhi); ++ + extern const struct dev_pm_ops nhi_pm_ops; + + /** +@@ -52,6 +54,7 @@ extern const struct dev_pm_ops nhi_pm_ops; + * @release_ring_irq: NHI specific interrupt release hook + * @is_present: Whether the device is currently present on the parent bus + * @init_interrupts: NHI specific interrupt initialization hook ++ * @reset_interface: Resets the host interface + */ + struct tb_nhi_ops { + int (*init)(struct tb_nhi *nhi); +@@ -66,6 +69,7 @@ struct tb_nhi_ops { + void (*release_ring_irq)(struct tb_ring *ring); + bool (*is_present)(struct tb_nhi *nhi); + int (*init_interrupts)(struct tb_nhi *nhi); ++ void (*reset_interface)(struct tb_nhi *nhi); + }; + + /* +@@ -116,11 +120,24 @@ struct tb_nhi_ops { + #define PCI_DEVICE_ID_INTEL_PTL_P_NHI0 0xe433 + #define PCI_DEVICE_ID_INTEL_PTL_P_NHI1 0xe434 + ++#define PCI_DEVICE_ID_AMD_1AH_M60H_NHI0 0x1120 ++#define PCI_DEVICE_ID_AMD_1AH_M60H_NHI1 0x1121 ++#define PCI_DEVICE_ID_AMD_1AH_M68H_NHI0 0x113b ++#define PCI_DEVICE_ID_AMD_1AH_M68H_NHI1 0x113c ++#define PCI_DEVICE_ID_AMD_1AH_M80H_NHI0 0x1155 ++#define PCI_DEVICE_ID_AMD_1AH_M80H_NHI1 0x1158 ++#define PCI_DEVICE_ID_AMD_1AH_M80H_NHI2 0x1159 ++#define PCI_DEVICE_ID_AMD_1AH_M24H_NHI0 0x151c ++#define PCI_DEVICE_ID_AMD_1AH_M24H_NHI1 0x151d ++#define PCI_DEVICE_ID_AMD_1AH_M70H_NHI0 0x158d ++#define PCI_DEVICE_ID_AMD_1AH_M70H_NHI1 0x158e ++ + #define PCI_CLASS_SERIAL_USB_USB4 0x0c0340 + + /* Host interface quirks */ +-#define QUIRK_AUTO_CLEAR_INT BIT(0) +-#define QUIRK_E2E BIT(1) ++#define QUIRK_AUTO_CLEAR_INT BIT(0) ++#define QUIRK_E2E BIT(1) ++#define QUIRK_RESET_DMA_ON_TEARDOWN BIT(2) + + /* + * Minimal number of vectors when we use MSI-X. Two for control channel +diff --git a/drivers/thunderbolt/nhi_regs.h b/drivers/thunderbolt/nhi_regs.h +--- a/drivers/thunderbolt/nhi_regs.h ++++ b/drivers/thunderbolt/nhi_regs.h +@@ -115,6 +115,10 @@ struct ring_desc { + #define REG_CAPS_VERSION_MASK GENMASK(23, 16) + #define REG_CAPS_VERSION_2 0x40 + ++/* Host Interface Reset - resets TX/RX rings and E2E flow control counters */ ++#define REG_HOST_INTERFACE_RESET 0x39858 ++#define REG_HOST_INTERFACE_RESET_RST BIT(0) ++ + #define REG_DMA_MISC 0x39864 + #define REG_DMA_MISC_INT_AUTO_CLEAR BIT(2) + #define REG_DMA_MISC_DISABLE_AUTO_CLEAR BIT(17) +diff --git a/drivers/thunderbolt/pci.c b/drivers/thunderbolt/pci.c +--- a/drivers/thunderbolt/pci.c ++++ b/drivers/thunderbolt/pci.c +@@ -62,6 +62,27 @@ static void nhi_pci_check_quirks(struct tb_nhi_pci *nhi_pci) + nhi->quirks |= QUIRK_E2E; + break; + } ++ } else if (pdev->vendor == PCI_VENDOR_ID_AMD) { ++ switch (pdev->device) { ++ case PCI_DEVICE_ID_AMD_1AH_M60H_NHI0: ++ case PCI_DEVICE_ID_AMD_1AH_M60H_NHI1: ++ case PCI_DEVICE_ID_AMD_1AH_M68H_NHI0: ++ case PCI_DEVICE_ID_AMD_1AH_M68H_NHI1: ++ case PCI_DEVICE_ID_AMD_1AH_M80H_NHI0: ++ case PCI_DEVICE_ID_AMD_1AH_M80H_NHI1: ++ case PCI_DEVICE_ID_AMD_1AH_M80H_NHI2: ++ case PCI_DEVICE_ID_AMD_1AH_M24H_NHI0: ++ case PCI_DEVICE_ID_AMD_1AH_M24H_NHI1: ++ case PCI_DEVICE_ID_AMD_1AH_M70H_NHI0: ++ case PCI_DEVICE_ID_AMD_1AH_M70H_NHI1: ++ /* ++ * These AMD hosts may hang the Tx ring when the ++ * DMA paths are torn down so they need the host ++ * interface reset after each teardown. ++ */ ++ nhi->quirks |= QUIRK_RESET_DMA_ON_TEARDOWN; ++ break; ++ } + } + } + +@@ -112,7 +133,6 @@ static int nhi_pci_init_msi(struct tb_nhi *nhi) + { + struct tb_nhi_pci *nhi_pci = nhi_to_pci(nhi); + struct pci_dev *pdev = to_pci_dev(nhi->dev); +- struct device *dev = &pdev->dev; + int res, irq, nvec; + + ida_init(&nhi_pci->msix_ida); +@@ -139,7 +159,7 @@ static int nhi_pci_init_msi(struct tb_nhi *nhi) + res = devm_request_irq(&pdev->dev, irq, nhi_msi, + IRQF_NO_SUSPEND, "thunderbolt", nhi); + if (res) +- return dev_err_probe(dev, res, "request_irq failed, aborting\n"); ++ return res; + } + + return 0; +@@ -230,7 +250,7 @@ static void nhi_pci_ring_release_msix(struct tb_ring *ring) + ring->irq = 0; + } + +-static void nhi_pci_shutdown(struct tb_nhi *nhi) ++static void nhi_pci_release_irq(struct tb_nhi *nhi) + { + struct tb_nhi_pci *nhi_pci = nhi_to_pci(nhi); + struct pci_dev *pdev = to_pci_dev(nhi->dev); +@@ -256,9 +276,10 @@ static const struct tb_nhi_ops pci_nhi_default_ops = { + .post_nvm_auth = nhi_pci_complete_dma_port, + .request_ring_irq = nhi_pci_ring_request_msix, + .release_ring_irq = nhi_pci_ring_release_msix, +- .shutdown = nhi_pci_shutdown, ++ .shutdown = nhi_pci_release_irq, + .is_present = nhi_pci_is_present, + .init_interrupts = nhi_pci_init_msi, ++ .reset_interface = nhi_reset_interface, + }; + + /* Ice Lake specific NHI operations */ +@@ -424,7 +445,7 @@ static int icl_nhi_resume(struct tb_nhi *nhi) + + static void icl_nhi_shutdown(struct tb_nhi *nhi) + { +- nhi_pci_shutdown(nhi); ++ nhi_pci_release_irq(nhi); + + icl_nhi_force_power(nhi, false); + } +@@ -442,6 +463,7 @@ static const struct tb_nhi_ops icl_nhi_ops = { + .release_ring_irq = nhi_pci_ring_release_msix, + .is_present = nhi_pci_is_present, + .init_interrupts = nhi_pci_init_msi, ++ .reset_interface = nhi_reset_interface, + }; + + static int nhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +@@ -479,11 +501,19 @@ static int nhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) + return nhi_probe(&nhi_pci->nhi); + } + +-static void nhi_pci_remove(struct pci_dev *pdev) ++static void nhi_pci_do_remove(struct pci_dev *pdev, bool reset) + { + struct tb *tb = pci_get_drvdata(pdev); + struct tb_nhi *nhi = tb->nhi; + ++ /* ++ * On system shutdown/reboot force a host router reset so the ++ * connection manager asserts DPR on connected Thunderbolt 3 devices ++ * before the router tree is removed (see tb_stop()). ++ */ ++ if (reset) ++ nhi->host_reset = true; ++ + pm_runtime_get_sync(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_forbid(&pdev->dev); +@@ -493,6 +523,16 @@ static void nhi_pci_remove(struct pci_dev *pdev) + nhi_shutdown(nhi); + } + ++static void nhi_pci_remove(struct pci_dev *pdev) ++{ ++ nhi_pci_do_remove(pdev, false); ++} ++ ++static void nhi_pci_shutdown(struct pci_dev *pdev) ++{ ++ nhi_pci_do_remove(pdev, true); ++} ++ + static struct pci_device_id nhi_ids[] = { + /* + * We have to specify class, the TB bridges use the same device and +@@ -593,7 +633,7 @@ static struct pci_driver nhi_driver = { + .id_table = nhi_ids, + .probe = nhi_pci_probe, + .remove = nhi_pci_remove, +- .shutdown = nhi_pci_remove, ++ .shutdown = nhi_pci_shutdown, + .driver.pm = &nhi_pm_ops, + }; + +diff --git a/drivers/thunderbolt/stream.c b/drivers/thunderbolt/stream.c +--- a/drivers/thunderbolt/stream.c ++++ b/drivers/thunderbolt/stream.c +@@ -9,10 +9,12 @@ + + #define pr_fmt(fmt) "tbstream: " fmt + ++#include + #include + #include + #include + #include ++#include + #include + #include + #include +@@ -128,6 +130,7 @@ struct tbstream_ring { + * @out_hopid: Out HopID + * @ring_size: Size of the rings + * @throttling: Interrupt throttling rate in ns ++ * @busy_poll: Instead of interrupts, busy poll the rings + * @users: Number of times @cdev has been opened + * @closed: CLOSE packet was received + * @removed: Userspace removed the ConfigFS group underneath. +@@ -147,6 +150,7 @@ struct tbstream_dev { + int out_hopid; + unsigned int ring_size; + unsigned int throttling; ++ bool busy_poll; + int users; + bool closed; + bool removed; +@@ -536,10 +540,39 @@ tbstream_dev_send_data(struct tbstream_dev *sdev, struct iov_iter *from, + return tb_ring_tx(sdev->tx_ring.ring, &sf->frame); + } + ++static void ++tbstream_dev_poll_ring(struct tbstream_dev *sdev, struct tbstream_ring *ring) ++{ ++ struct ring_frame *frame; ++ ++ if (!sdev->busy_poll) ++ return; ++ ++ while ((frame = tb_ring_poll(ring->ring))) ++ frame->callback(ring->ring, frame, false); ++} ++ + static int tbstream_dev_send_close(struct tbstream_dev *sdev) + { + struct tbstream_frame *sf; + ++ if (sdev->busy_poll) { ++ /* ++ * When busy polling it's the write(2) path that ++ * advances the completions so it is possible that the ++ * ring is full at this point. Advance the ring here so ++ * that there is room for the CLOSE packet to be sent. ++ */ ++ ktime_t timeout = ktime_add_ms(ktime_get(), 500); ++ ++ do { ++ if (tbstream_ring_available(&sdev->tx_ring)) ++ break; ++ tbstream_dev_poll_ring(sdev, &sdev->tx_ring); ++ fsleep(15); ++ } while (ktime_before(ktime_get(), timeout)); ++ } ++ + sf = tbstream_dev_alloc_tx(sdev, TBSTREAM_CLOSE, NULL, SZ_256); + if (IS_ERR(sf)) + return PTR_ERR(sf); +@@ -549,12 +582,15 @@ static int tbstream_dev_send_close(struct tbstream_dev *sdev) + static int tbstream_dev_start(struct tbstream_dev *sdev) + { + struct tb_xdomain *xd = tbstream_dev_xdomain(sdev); ++ unsigned int flags = RING_FLAG_FRAME | RING_FLAG_E2E; + u16 sof_mask, eof_mask; + struct tb_ring *ring; + int ret, e2e_tx_hop; + +- ring = tb_ring_alloc_tx(xd->tb->nhi, -1, sdev->ring_size, +- RING_FLAG_FRAME | RING_FLAG_E2E); ++ if (sdev->busy_poll) ++ flags |= RING_FLAG_NO_INTERRUPT; ++ ++ ring = tb_ring_alloc_tx(xd->tb->nhi, -1, sdev->ring_size, flags); + if (!ring) + return -ENOMEM; + sdev->tx_ring.ring = ring; +@@ -567,9 +603,8 @@ static int tbstream_dev_start(struct tbstream_dev *sdev) + sof_mask = BIT(TBSTREAM_FRAME_START); + eof_mask = BIT(TBSTREAM_DATA) | BIT(TBSTREAM_CLOSE); + +- ring = tb_ring_alloc_rx(xd->tb->nhi, -1, sdev->ring_size, +- RING_FLAG_FRAME | RING_FLAG_E2E, e2e_tx_hop, +- sof_mask, eof_mask, NULL, NULL); ++ ring = tb_ring_alloc_rx(xd->tb->nhi, -1, sdev->ring_size, flags, ++ e2e_tx_hop, sof_mask, eof_mask, NULL, NULL); + if (!ring) { + ret = -ENOMEM; + goto err_free_tx_buffers; +@@ -607,15 +642,43 @@ static int tbstream_dev_start(struct tbstream_dev *sdev) + return ret; + } + ++static bool tbstream_dev_tx_drained(const struct tbstream_dev *sdev) ++{ ++ const struct tbstream_ring *ring = &sdev->tx_ring; ++ ++ /* ++ * Everything is completed when number of free TX slots is back ++ * to the maximum. ++ */ ++ return ring->prod - ring->cons == tb_ring_size(ring->ring) - 1; ++} ++ + static void tbstream_dev_stop(struct tbstream_dev *sdev) + { + struct tb_xdomain *xd; + +- /* Wait for the ring to complete any outstanding frames */ +- tb_ring_flush(sdev->tx_ring.ring, 500); +- tb_ring_stop(sdev->tx_ring.ring); +- tb_ring_flush(sdev->rx_ring.ring, 500); +- tb_ring_stop(sdev->rx_ring.ring); ++ if (sdev->busy_poll) { ++ /* ++ * When busy polling we must advance the ring ourselves ++ * to push all outstanding frames on the wire. ++ */ ++ ktime_t timeout = ktime_add_ms(ktime_get(), 500); ++ ++ do { ++ if (tbstream_dev_tx_drained(sdev)) ++ break; ++ tbstream_dev_poll_ring(sdev, &sdev->tx_ring); ++ fsleep(15); ++ } while (ktime_before(ktime_get(), timeout)); ++ ++ tb_ring_stop(sdev->tx_ring.ring); ++ tb_ring_stop(sdev->rx_ring.ring); ++ } else { ++ tb_ring_flush(sdev->tx_ring.ring, 500); ++ tb_ring_stop(sdev->tx_ring.ring); ++ tb_ring_flush(sdev->rx_ring.ring, 500); ++ tb_ring_stop(sdev->rx_ring.ring); ++ } + + xd = tbstream_dev_xdomain(sdev); + if (xd) { +@@ -633,10 +696,24 @@ static void tbstream_dev_stop(struct tbstream_dev *sdev) + sdev->tx_ring.ring = NULL; + } + ++/* Use only with read_iter/write_iter() to handle nowait */ ++static int tbstream_dev_lock(struct tbstream_dev *sdev, bool nowait) ++{ ++ if (nowait) { ++ if (!mutex_trylock(&sdev->lock)) ++ return -EAGAIN; ++ } else { ++ if (mutex_lock_interruptible(&sdev->lock)) ++ return -ERESTARTSYS; ++ } ++ return 0; ++} ++ + static ssize_t + tbstream_dev_fops_read_iter(struct kiocb *kiocb, struct iov_iter *to) + { + struct file *file = kiocb->ki_filp; ++ bool nowait = file->f_flags & O_NONBLOCK || kiocb->ki_flags & IOCB_NOWAIT; + struct tbstream_dev *sdev = to_tbstream_dev(file->private_data); + size_t nbytes; + int ret; +@@ -645,31 +722,50 @@ tbstream_dev_fops_read_iter(struct kiocb *kiocb, struct iov_iter *to) + if (ret) + return ret; + +- if (mutex_lock_interruptible(&sdev->lock)) +- return -ERESTARTSYS; ++ ret = tbstream_dev_lock(sdev, nowait); ++ if (ret) ++ return ret; + +- while (!tbstream_ring_available(&sdev->rx_ring)) { +- mutex_unlock(&sdev->lock); +- +- if (file->f_flags & O_NONBLOCK) +- return -EAGAIN; +- ret = wait_event_interruptible(sdev->wait, +- tbstream_ring_available(&sdev->rx_ring) || +- tbstream_dev_valid(sdev) != 0 || +- tbstream_dev_closed(sdev) || +- tbstream_dev_removed(sdev)); +- if (ret) +- return ret; ++ for (;;) { ++ /* When busy polling, advance any completions manually */ ++ tbstream_dev_poll_ring(sdev, &sdev->rx_ring); + + ret = tbstream_dev_valid(sdev); ++ if (ret) { ++ mutex_unlock(&sdev->lock); ++ return ret; ++ } ++ ++ if (tbstream_dev_closed(sdev) || tbstream_dev_removed(sdev)) { ++ mutex_unlock(&sdev->lock); ++ return 0; ++ } ++ ++ if (tbstream_ring_available(&sdev->rx_ring)) ++ break; ++ ++ mutex_unlock(&sdev->lock); ++ ++ if (nowait) ++ return -EAGAIN; ++ ++ if (sdev->busy_poll) { ++ if (signal_pending(current)) ++ return -ERESTARTSYS; ++ cond_resched(); ++ } else { ++ ret = wait_event_interruptible(sdev->wait, ++ tbstream_ring_available(&sdev->rx_ring) || ++ tbstream_dev_valid(sdev) != 0 || ++ tbstream_dev_closed(sdev) || ++ tbstream_dev_removed(sdev)); ++ if (ret) ++ return ret; ++ } ++ ++ ret = tbstream_dev_lock(sdev, nowait); + if (ret) + return ret; +- +- if (tbstream_dev_closed(sdev) || tbstream_dev_removed(sdev)) +- return 0; +- +- if (mutex_lock_interruptible(&sdev->lock)) +- return -ERESTARTSYS; + } + + nbytes = 0; +@@ -729,6 +825,7 @@ static ssize_t + tbstream_dev_fops_write_iter(struct kiocb *kiocb, struct iov_iter *from) + { + struct file *file = kiocb->ki_filp; ++ bool nowait = file->f_flags & O_NONBLOCK || kiocb->ki_flags & IOCB_NOWAIT; + struct tbstream_dev *sdev = to_tbstream_dev(file->private_data); + size_t nbytes; + int ret; +@@ -737,31 +834,49 @@ tbstream_dev_fops_write_iter(struct kiocb *kiocb, struct iov_iter *from) + if (ret) + return ret; + +- if (mutex_lock_interruptible(&sdev->lock)) +- return -ERESTARTSYS; ++ ret = tbstream_dev_lock(sdev, nowait); ++ if (ret) ++ return ret; + +- while (!tbstream_ring_available(&sdev->tx_ring)) { +- mutex_unlock(&sdev->lock); +- +- if (file->f_flags & O_NONBLOCK) +- return -EAGAIN; +- ret = wait_event_interruptible(sdev->wait, +- tbstream_ring_available(&sdev->tx_ring) || +- tbstream_dev_valid(sdev) != 0 || +- tbstream_dev_closed(sdev) || +- tbstream_dev_removed(sdev)); +- if (ret) +- return ret; ++ for (;;) { ++ tbstream_dev_poll_ring(sdev, &sdev->tx_ring); + + ret = tbstream_dev_valid(sdev); ++ if (ret) { ++ mutex_unlock(&sdev->lock); ++ return ret; ++ } ++ ++ if (tbstream_dev_closed(sdev) || tbstream_dev_removed(sdev)) { ++ mutex_unlock(&sdev->lock); ++ return -ENXIO; ++ } ++ ++ if (tbstream_ring_available(&sdev->tx_ring)) ++ break; ++ ++ mutex_unlock(&sdev->lock); ++ ++ if (nowait) ++ return -EAGAIN; ++ ++ if (sdev->busy_poll) { ++ if (signal_pending(current)) ++ return -ERESTARTSYS; ++ cond_resched(); ++ } else { ++ ret = wait_event_interruptible(sdev->wait, ++ tbstream_ring_available(&sdev->tx_ring) || ++ tbstream_dev_valid(sdev) != 0 || ++ tbstream_dev_closed(sdev) || ++ tbstream_dev_removed(sdev)); ++ if (ret) ++ return ret; ++ } ++ ++ ret = tbstream_dev_lock(sdev, nowait); + if (ret) + return ret; +- +- if (tbstream_dev_closed(sdev) || tbstream_dev_removed(sdev)) +- return -ENXIO; +- +- if (mutex_lock_interruptible(&sdev->lock)) +- return -ERESTARTSYS; + } + + nbytes = 0; +@@ -795,6 +910,13 @@ tbstream_dev_fops_poll(struct file *file, struct poll_table_struct *wait) + struct tbstream_dev *sdev = to_tbstream_dev(file->private_data); + __poll_t mask = 0; + ++ /* ++ * Without interrupts there is nothing that can wake us up so ++ * return failure instead. ++ */ ++ if (sdev->busy_poll) ++ return EPOLLERR; ++ + poll_wait(file, &sdev->wait, wait); + guard(mutex)(&sdev->lock); + if (tbstream_dev_valid(sdev) != 0) { +@@ -904,6 +1026,35 @@ tbstream_dev_from_group(struct config_group *group) + return container_of(group, struct tbstream_dev, group); + } + ++static ssize_t tbstream_dev_busy_poll_show(struct config_item *item, char *buf) ++{ ++ struct config_group *group = to_config_group(item); ++ struct tbstream_dev *sdev = tbstream_dev_from_group(group); ++ ++ return sysfs_emit(buf, "%u\n", sdev->busy_poll); ++} ++ ++static ssize_t ++tbstream_dev_busy_poll_store(struct config_item *item, const char *buf, ++ size_t count) ++{ ++ struct config_group *group = to_config_group(item); ++ struct tbstream_dev *sdev = tbstream_dev_from_group(group); ++ bool busy_poll; ++ int ret; ++ ++ ret = kstrtobool(buf, &busy_poll); ++ if (ret) ++ return ret; ++ ++ guard(mutex)(&sdev->lock); ++ if (sdev->users) ++ return -EBUSY; ++ sdev->busy_poll = busy_poll; ++ return count; ++} ++CONFIGFS_ATTR(tbstream_dev_, busy_poll); ++ + static ssize_t tbstream_dev_index_show(struct config_item *item, char *buf) + { + struct config_group *group = to_config_group(item); +@@ -1210,6 +1361,7 @@ tbstream_dev_throttling_store(struct config_item *item, const char *buf, + CONFIGFS_ATTR(tbstream_dev_, throttling); + + static struct configfs_attribute *tbstream_dev_attrs[] = { ++ &tbstream_dev_attr_busy_poll, + &tbstream_dev_attr_index, + &tbstream_dev_attr_in_hopid, + &tbstream_dev_attr_out_hopid, +@@ -1542,7 +1694,7 @@ static void tbstream_group_detach_stream(struct tbstream *stream) + config_group_put(&sg->group); + } + +-static int tbstream_probe(struct tb_service *svc, const struct tb_service_id *id) ++static int tbstream_probe(struct tb_service *svc) + { + struct tbstream *stream; + +@@ -1632,7 +1784,7 @@ static const struct dev_pm_ops tbstream_pm_ops = { + + static const struct tb_service_id tbstream_ids[] = { + { TB_SERVICE("stream", 1) }, +- { }, ++ { } + }; + MODULE_DEVICE_TABLE(tbsvc, tbstream_ids); + +diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c +--- a/drivers/thunderbolt/switch.c ++++ b/drivers/thunderbolt/switch.c +@@ -682,7 +682,16 @@ int tb_port_disable(struct tb_port *port) + return __tb_port_enable(port, false); + } + +-static int tb_port_reset(struct tb_port *port) ++/** ++ * tb_port_reset() - Reset the port ++ * @port: Port to reset ++ * ++ * Resets @port. For USB4 ports this issues a USB4 port reset and for ++ * legacy ports the link controller port is reset. ++ * ++ * Return: %0 on success, negative errno otherwise. ++ */ ++int tb_port_reset(struct tb_port *port) + { + if (tb_switch_is_usb4(port->sw)) + return port->cap_usb4 ? usb4_port_reset(port) : 0; +diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c +--- a/drivers/thunderbolt/tb.c ++++ b/drivers/thunderbolt/tb.c +@@ -2941,7 +2941,9 @@ static void tb_handle_event(struct tb *tb, enum tb_cfg_pkg_type type, + static void tb_stop(struct tb *tb) + { + struct tb_cm *tcm = tb_priv(tb); ++ struct tb_nhi *nhi = tb->nhi; + struct tb_tunnel *tunnel; ++ struct tb_port *port; + struct tb_tunnel *n; + + cancel_delayed_work(&tcm->remove_work); +@@ -2956,6 +2958,25 @@ static void tb_stop(struct tb *tb) + tb_tunnel_deactivate(tunnel); + tb_tunnel_put(tunnel); + } ++ /* ++ * Signal disconnect to connected devices before the router tree is ++ * removed below. A Thunderbolt 3 device directly connected to a USB4 ++ * host otherwise never receives a disconnect indication, leaving ++ * firmware to poll the dead link for up to ~60 s which on some ++ * platforms turns the shutdown into a warm reset. Asserting ++ * PORT_CS_19.DPR drives SBTX low (USB4 spec section 6.9) so the device ++ * detects SBRX low and goes to Uninitialized Unplugged immediately. ++ */ ++ if (nhi->host_reset) { ++ tb_switch_for_each_port(tb->root_switch, port) { ++ if (!tb_port_is_null(port) || !tb_port_has_remote(port)) ++ continue; ++ if (tb_switch_is_usb4(port->remote->sw)) ++ continue; ++ if (tb_port_reset(port)) ++ tb_port_dbg(port, "downstream port reset failed, continuing\n"); ++ } ++ } + tb_switch_remove(tb->root_switch); + tb->root_switch = NULL; + tcm->hotplug_active = false; /* signal tb_handle_hotplug to quit */ +diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h +--- a/drivers/thunderbolt/tb.h ++++ b/drivers/thunderbolt/tb.h +@@ -1103,6 +1103,7 @@ int tb_port_clear_counter(struct tb_port *port, int counter); + int tb_port_unlock(struct tb_port *port); + int tb_port_enable(struct tb_port *port); + int tb_port_disable(struct tb_port *port); ++int tb_port_reset(struct tb_port *port); + int tb_port_alloc_in_hopid(struct tb_port *port, int hopid, int max_hopid); + void tb_port_release_in_hopid(struct tb_port *port, int hopid); + int tb_port_alloc_out_hopid(struct tb_port *port, int hopid, int max_hopid); +diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c +--- a/drivers/thunderbolt/tunnel.c ++++ b/drivers/thunderbolt/tunnel.c +@@ -48,6 +48,9 @@ + #define TB_DP_AUX_PRIORITY 2 + #define TB_DP_AUX_WEIGHT 1 + ++/* struct tb_regs_hop::initial_credits is 7 bits wide */ ++#define TB_MAX_CREDITS 127 ++ + /* Minimum number of credits needed for PCIe path */ + #define TB_MIN_PCIE_CREDITS 6U + /* +@@ -1778,8 +1781,7 @@ static int tb_dma_reserve_credits(struct tb_path_hop *hop, unsigned int credits) + if (available < TB_MIN_DMA_CREDITS) + return -ENOSPC; + +- while (credits > available) +- credits--; ++ credits = min(credits, available); + + tb_port_dbg(port, "reserving %u credits for DMA path\n", + credits); +@@ -1908,7 +1910,7 @@ struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi, + struct tb_tunnel *tunnel; + size_t npaths = 0, i = 0; + struct tb_path *path; +- int credits; ++ unsigned int credits; + + /* Ring 0 is reserved for control channel */ + if (WARN_ON(!receive_ring || !transmit_ring)) +@@ -1931,6 +1933,11 @@ struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi, + tunnel->destroy = tb_dma_destroy; + + credits = min_not_zero(dma_credits, nhi->sw->max_dma_credits); ++ if (credits > TB_MAX_CREDITS) { ++ tb_tunnel_dbg(tunnel, "%u credits do not fit a hop, using %u\n", ++ credits, TB_MAX_CREDITS); ++ credits = TB_MAX_CREDITS; ++ } + + if (receive_ring > 0) { + path = tb_path_alloc(tb, dst, receive_path, nhi, receive_ring, 0, +diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c +--- a/drivers/thunderbolt/xdomain.c ++++ b/drivers/thunderbolt/xdomain.c +@@ -968,6 +968,9 @@ tb_xdp_schedule_request(struct tb *tb, const struct tb_xdp_header *hdr, + */ + int tb_register_service_driver(struct tb_service_driver *drv) + { ++ if (!drv->probe) ++ return -EINVAL; ++ + drv->driver.bus = &tb_bus_type; + return driver_register(&drv->driver); + } +@@ -1811,6 +1814,7 @@ static void tb_xdomain_state_work(struct work_struct *work) + tb_xdomain_failed(xd); + } else { + xd->state = XDOMAIN_STATE_ENUMERATED; ++ tb_xdomain_queue_properties_changed(xd); + } + break; + +diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h +--- a/include/linux/thunderbolt.h ++++ b/include/linux/thunderbolt.h +@@ -465,7 +465,7 @@ static inline struct tb_service *tb_to_service(struct device *dev) + */ + struct tb_service_driver { + struct device_driver driver; +- int (*probe)(struct tb_service *svc, const struct tb_service_id *id); ++ int (*probe)(struct tb_service *svc); + void (*remove)(struct tb_service *svc); + void (*shutdown)(struct tb_service *svc); + const struct tb_service_id *id_table; +@@ -514,6 +514,11 @@ void tb_service_properties_changed(struct tb_service *svc); + * @hop_count: Number of rings (end point hops) supported by NHI. + * @quirks: NHI specific quirks if any + * @domain_released: Completed when domain has been fully released ++ * @host_reset: Host router was reset on driver load, or forced on system ++ * shutdown/reboot. When set, tb_stop() asserts DPR on connected ++ * downstream ports to signal disconnect before tearing down the ++ * router tree. Only Thunderbolt 3 devices are reset; USB4 ++ * routers are skipped. + */ + struct tb_nhi { + spinlock_t lock; +@@ -528,6 +533,7 @@ struct tb_nhi { + u32 hop_count; + unsigned long quirks; + struct completion domain_released; ++ bool host_reset; + }; + + /** +@@ -592,6 +598,8 @@ struct tb_ring { + #define RING_FLAG_FRAME BIT(1) + /* Enable end-to-end flow control */ + #define RING_FLAG_E2E BIT(2) ++/* Do not enable interrupt for the ring */ ++#define RING_FLAG_NO_INTERRUPT BIT(3) + + struct ring_frame; + typedef void (*ring_cb)(struct tb_ring *, struct ring_frame *, bool canceled); diff --git a/pkgbuilds/linux-omarchy-bore/0601-thunderbolt-updates.patch.sig b/pkgbuilds/linux-omarchy-bore/0601-thunderbolt-updates.patch.sig new file mode 100644 index 0000000..89bd5ac Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0601-thunderbolt-updates.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0601-usb4stream-busy-poll.patch b/pkgbuilds/linux-omarchy-bore/0601-usb4stream-busy-poll.patch deleted file mode 100644 index 0cb7d2a..0000000 --- a/pkgbuilds/linux-omarchy-bore/0601-usb4stream-busy-poll.patch +++ /dev/null @@ -1,477 +0,0 @@ -diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c -index 35e3c119d5ee..a4816db5cacd 100644 ---- a/drivers/thunderbolt/nhi.c -+++ b/drivers/thunderbolt/nhi.c -@@ -235,6 +235,12 @@ static void ring_write_descriptors(struct tb_ring *ring) - { - struct ring_frame *frame, *n; - struct ring_desc *descriptor; -+ u32 flags; -+ -+ flags = RING_DESC_POSTED; -+ if (!(ring->flags & RING_FLAG_NO_INTERRUPT)) -+ flags |= RING_DESC_INTERRUPT; -+ - list_for_each_entry_safe(frame, n, &ring->queue, list) { - if (ring_full(ring)) - break; -@@ -242,7 +248,7 @@ static void ring_write_descriptors(struct tb_ring *ring) - descriptor = &ring->descriptors[ring->head]; - descriptor->phys = frame->buffer_phy; - descriptor->time = 0; -- descriptor->flags = RING_DESC_POSTED | RING_DESC_INTERRUPT; -+ descriptor->flags = flags; - if (ring->is_tx) { - descriptor->length = frame->size; - descriptor->eof = frame->eof; -@@ -339,8 +345,9 @@ EXPORT_SYMBOL_GPL(__tb_ring_enqueue); - * @ring: Ring to poll - * - * This function can be called when @start_poll callback of the @ring -- * has been called. It will read one completed frame from the ring and -- * return it to the caller. -+ * has been called or the ring is created with %RING_FLAG_NO_INTERRUPT. -+ * It will read one completed frame from the ring and return it to the -+ * caller. - * - * Return: Pointer to &struct ring_frame, %NULL if there is no more - * completed frames. -@@ -538,6 +545,12 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, - dev_dbg(nhi->dev, "allocating %s ring %d of size %d\n", - transmit ? "TX" : "RX", hop, size); - -+ if ((flags & RING_FLAG_NO_INTERRUPT) && start_poll) { -+ dev_WARN(nhi->dev, -+ "start_poll() and NO_INTERRUPT cannot be used at the same time\n"); -+ return NULL; -+ } -+ - ring = kzalloc_obj(*ring); - if (!ring) - return NULL; -@@ -568,7 +581,7 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, - if (!ring->descriptors) - goto err_free_ring; - -- if (nhi->ops->request_ring_irq) { -+ if (!(flags & RING_FLAG_NO_INTERRUPT) && nhi->ops->request_ring_irq) { - if (nhi->ops->request_ring_irq(ring, flags & RING_FLAG_NO_SUSPEND)) - goto err_free_descs; - } -@@ -701,7 +714,8 @@ void tb_ring_start(struct tb_ring *ring) - ring_iowrite32options(ring, flags, 0); - } - -- ring_interrupt_active(ring, true); -+ if (!(ring->flags & RING_FLAG_NO_INTERRUPT)) -+ ring_interrupt_active(ring, true); - ring->running = true; - err: - spin_unlock(&ring->lock); -@@ -761,7 +775,8 @@ void tb_ring_stop(struct tb_ring *ring) - RING_TYPE(ring), ring->hop); - goto err; - } -- ring_interrupt_active(ring, false); -+ if (!(ring->flags & RING_FLAG_NO_INTERRUPT)) -+ ring_interrupt_active(ring, false); - - ring_iowrite32options(ring, 0, 0); - ring_iowrite64desc(ring, 0, 0); -diff --git a/drivers/thunderbolt/stream.c b/drivers/thunderbolt/stream.c -index 43d29d69dcdb..a7d9fda66875 100644 ---- a/drivers/thunderbolt/stream.c -+++ b/drivers/thunderbolt/stream.c -@@ -9,10 +9,12 @@ - - #define pr_fmt(fmt) "tbstream: " fmt - -+#include - #include - #include - #include - #include -+#include - #include - #include - #include -@@ -128,6 +130,7 @@ struct tbstream_ring { - * @out_hopid: Out HopID - * @ring_size: Size of the rings - * @throttling: Interrupt throttling rate in ns -+ * @busy_poll: Instead of interrupts, busy poll the rings - * @users: Number of times @cdev has been opened - * @closed: CLOSE packet was received - * @removed: Userspace removed the ConfigFS group underneath. -@@ -147,6 +150,7 @@ struct tbstream_dev { - int out_hopid; - unsigned int ring_size; - unsigned int throttling; -+ bool busy_poll; - int users; - bool closed; - bool removed; -@@ -536,10 +540,39 @@ tbstream_dev_send_data(struct tbstream_dev *sdev, struct iov_iter *from, - return tb_ring_tx(sdev->tx_ring.ring, &sf->frame); - } - -+static void -+tbstream_dev_poll_ring(struct tbstream_dev *sdev, struct tbstream_ring *ring) -+{ -+ struct ring_frame *frame; -+ -+ if (!sdev->busy_poll) -+ return; -+ -+ while ((frame = tb_ring_poll(ring->ring))) -+ frame->callback(ring->ring, frame, false); -+} -+ - static int tbstream_dev_send_close(struct tbstream_dev *sdev) - { - struct tbstream_frame *sf; - -+ if (sdev->busy_poll) { -+ /* -+ * When busy polling it's the write(2) path that -+ * advances the completions so it is possible that the -+ * ring is full at this point. Advance the ring here so -+ * that there is room for the CLOSE packet to be sent. -+ */ -+ ktime_t timeout = ktime_add_ms(ktime_get(), 500); -+ -+ do { -+ if (tbstream_ring_available(&sdev->tx_ring)) -+ break; -+ tbstream_dev_poll_ring(sdev, &sdev->tx_ring); -+ fsleep(15); -+ } while (ktime_before(ktime_get(), timeout)); -+ } -+ - sf = tbstream_dev_alloc_tx(sdev, TBSTREAM_CLOSE, NULL, SZ_256); - if (IS_ERR(sf)) - return PTR_ERR(sf); -@@ -549,12 +582,15 @@ static int tbstream_dev_send_close(struct tbstream_dev *sdev) - static int tbstream_dev_start(struct tbstream_dev *sdev) - { - struct tb_xdomain *xd = tbstream_dev_xdomain(sdev); -+ unsigned int flags = RING_FLAG_FRAME | RING_FLAG_E2E; - u16 sof_mask, eof_mask; - struct tb_ring *ring; - int ret, e2e_tx_hop; - -- ring = tb_ring_alloc_tx(xd->tb->nhi, -1, sdev->ring_size, -- RING_FLAG_FRAME | RING_FLAG_E2E); -+ if (sdev->busy_poll) -+ flags |= RING_FLAG_NO_INTERRUPT; -+ -+ ring = tb_ring_alloc_tx(xd->tb->nhi, -1, sdev->ring_size, flags); - if (!ring) - return -ENOMEM; - sdev->tx_ring.ring = ring; -@@ -567,9 +603,8 @@ static int tbstream_dev_start(struct tbstream_dev *sdev) - sof_mask = BIT(TBSTREAM_FRAME_START); - eof_mask = BIT(TBSTREAM_DATA) | BIT(TBSTREAM_CLOSE); - -- ring = tb_ring_alloc_rx(xd->tb->nhi, -1, sdev->ring_size, -- RING_FLAG_FRAME | RING_FLAG_E2E, e2e_tx_hop, -- sof_mask, eof_mask, NULL, NULL); -+ ring = tb_ring_alloc_rx(xd->tb->nhi, -1, sdev->ring_size, flags, -+ e2e_tx_hop, sof_mask, eof_mask, NULL, NULL); - if (!ring) { - ret = -ENOMEM; - goto err_free_tx_buffers; -@@ -607,15 +642,43 @@ static int tbstream_dev_start(struct tbstream_dev *sdev) - return ret; - } - -+static bool tbstream_dev_tx_drained(const struct tbstream_dev *sdev) -+{ -+ const struct tbstream_ring *ring = &sdev->tx_ring; -+ -+ /* -+ * Everything is completed when number of free TX slots is back -+ * to the maximum. -+ */ -+ return ring->prod - ring->cons == tb_ring_size(ring->ring) - 1; -+} -+ - static void tbstream_dev_stop(struct tbstream_dev *sdev) - { - struct tb_xdomain *xd; - -- /* Wait for the ring to complete any outstanding frames */ -- tb_ring_flush(sdev->tx_ring.ring, 500); -- tb_ring_stop(sdev->tx_ring.ring); -- tb_ring_flush(sdev->rx_ring.ring, 500); -- tb_ring_stop(sdev->rx_ring.ring); -+ if (sdev->busy_poll) { -+ /* -+ * When busy polling we must advance the ring ourselves -+ * to push all outstanding frames on the wire. -+ */ -+ ktime_t timeout = ktime_add_ms(ktime_get(), 500); -+ -+ do { -+ if (tbstream_dev_tx_drained(sdev)) -+ break; -+ tbstream_dev_poll_ring(sdev, &sdev->tx_ring); -+ fsleep(15); -+ } while (ktime_before(ktime_get(), timeout)); -+ -+ tb_ring_stop(sdev->tx_ring.ring); -+ tb_ring_stop(sdev->rx_ring.ring); -+ } else { -+ tb_ring_flush(sdev->tx_ring.ring, 500); -+ tb_ring_stop(sdev->tx_ring.ring); -+ tb_ring_flush(sdev->rx_ring.ring, 500); -+ tb_ring_stop(sdev->rx_ring.ring); -+ } - - xd = tbstream_dev_xdomain(sdev); - if (xd) { -@@ -633,10 +696,24 @@ static void tbstream_dev_stop(struct tbstream_dev *sdev) - sdev->tx_ring.ring = NULL; - } - -+/* Use only with read_iter/write_iter() to handle nowait */ -+static int tbstream_dev_lock(struct tbstream_dev *sdev, bool nowait) -+{ -+ if (nowait) { -+ if (!mutex_trylock(&sdev->lock)) -+ return -EAGAIN; -+ } else { -+ if (mutex_lock_interruptible(&sdev->lock)) -+ return -ERESTARTSYS; -+ } -+ return 0; -+} -+ - static ssize_t - tbstream_dev_fops_read_iter(struct kiocb *kiocb, struct iov_iter *to) - { - struct file *file = kiocb->ki_filp; -+ bool nowait = file->f_flags & O_NONBLOCK || kiocb->ki_flags & IOCB_NOWAIT; - struct tbstream_dev *sdev = to_tbstream_dev(file->private_data); - size_t nbytes; - int ret; -@@ -645,31 +722,50 @@ tbstream_dev_fops_read_iter(struct kiocb *kiocb, struct iov_iter *to) - if (ret) - return ret; - -- if (mutex_lock_interruptible(&sdev->lock)) -- return -ERESTARTSYS; -+ ret = tbstream_dev_lock(sdev, nowait); -+ if (ret) -+ return ret; - -- while (!tbstream_ring_available(&sdev->rx_ring)) { -- mutex_unlock(&sdev->lock); -- -- if (file->f_flags & O_NONBLOCK) -- return -EAGAIN; -- ret = wait_event_interruptible(sdev->wait, -- tbstream_ring_available(&sdev->rx_ring) || -- tbstream_dev_valid(sdev) != 0 || -- tbstream_dev_closed(sdev) || -- tbstream_dev_removed(sdev)); -- if (ret) -- return ret; -+ for (;;) { -+ /* When busy polling, advance any completions manually */ -+ tbstream_dev_poll_ring(sdev, &sdev->rx_ring); - - ret = tbstream_dev_valid(sdev); -+ if (ret) { -+ mutex_unlock(&sdev->lock); -+ return ret; -+ } -+ -+ if (tbstream_dev_closed(sdev) || tbstream_dev_removed(sdev)) { -+ mutex_unlock(&sdev->lock); -+ return 0; -+ } -+ -+ if (tbstream_ring_available(&sdev->rx_ring)) -+ break; -+ -+ mutex_unlock(&sdev->lock); -+ -+ if (nowait) -+ return -EAGAIN; -+ -+ if (sdev->busy_poll) { -+ if (signal_pending(current)) -+ return -ERESTARTSYS; -+ cond_resched(); -+ } else { -+ ret = wait_event_interruptible(sdev->wait, -+ tbstream_ring_available(&sdev->rx_ring) || -+ tbstream_dev_valid(sdev) != 0 || -+ tbstream_dev_closed(sdev) || -+ tbstream_dev_removed(sdev)); -+ if (ret) -+ return ret; -+ } -+ -+ ret = tbstream_dev_lock(sdev, nowait); - if (ret) - return ret; -- -- if (tbstream_dev_closed(sdev) || tbstream_dev_removed(sdev)) -- return 0; -- -- if (mutex_lock_interruptible(&sdev->lock)) -- return -ERESTARTSYS; - } - - nbytes = 0; -@@ -729,6 +825,7 @@ static ssize_t - tbstream_dev_fops_write_iter(struct kiocb *kiocb, struct iov_iter *from) - { - struct file *file = kiocb->ki_filp; -+ bool nowait = file->f_flags & O_NONBLOCK || kiocb->ki_flags & IOCB_NOWAIT; - struct tbstream_dev *sdev = to_tbstream_dev(file->private_data); - size_t nbytes; - int ret; -@@ -737,31 +834,49 @@ tbstream_dev_fops_write_iter(struct kiocb *kiocb, struct iov_iter *from) - if (ret) - return ret; - -- if (mutex_lock_interruptible(&sdev->lock)) -- return -ERESTARTSYS; -+ ret = tbstream_dev_lock(sdev, nowait); -+ if (ret) -+ return ret; - -- while (!tbstream_ring_available(&sdev->tx_ring)) { -- mutex_unlock(&sdev->lock); -- -- if (file->f_flags & O_NONBLOCK) -- return -EAGAIN; -- ret = wait_event_interruptible(sdev->wait, -- tbstream_ring_available(&sdev->tx_ring) || -- tbstream_dev_valid(sdev) != 0 || -- tbstream_dev_closed(sdev) || -- tbstream_dev_removed(sdev)); -- if (ret) -- return ret; -+ for (;;) { -+ tbstream_dev_poll_ring(sdev, &sdev->tx_ring); - - ret = tbstream_dev_valid(sdev); -+ if (ret) { -+ mutex_unlock(&sdev->lock); -+ return ret; -+ } -+ -+ if (tbstream_dev_closed(sdev) || tbstream_dev_removed(sdev)) { -+ mutex_unlock(&sdev->lock); -+ return -ENXIO; -+ } -+ -+ if (tbstream_ring_available(&sdev->tx_ring)) -+ break; -+ -+ mutex_unlock(&sdev->lock); -+ -+ if (nowait) -+ return -EAGAIN; -+ -+ if (sdev->busy_poll) { -+ if (signal_pending(current)) -+ return -ERESTARTSYS; -+ cond_resched(); -+ } else { -+ ret = wait_event_interruptible(sdev->wait, -+ tbstream_ring_available(&sdev->tx_ring) || -+ tbstream_dev_valid(sdev) != 0 || -+ tbstream_dev_closed(sdev) || -+ tbstream_dev_removed(sdev)); -+ if (ret) -+ return ret; -+ } -+ -+ ret = tbstream_dev_lock(sdev, nowait); - if (ret) - return ret; -- -- if (tbstream_dev_closed(sdev) || tbstream_dev_removed(sdev)) -- return -ENXIO; -- -- if (mutex_lock_interruptible(&sdev->lock)) -- return -ERESTARTSYS; - } - - nbytes = 0; -@@ -795,6 +910,13 @@ tbstream_dev_fops_poll(struct file *file, struct poll_table_struct *wait) - struct tbstream_dev *sdev = to_tbstream_dev(file->private_data); - __poll_t mask = 0; - -+ /* -+ * Without interrupts there is nothing that can wake us up so -+ * return failure instead. -+ */ -+ if (sdev->busy_poll) -+ return EPOLLERR; -+ - poll_wait(file, &sdev->wait, wait); - guard(mutex)(&sdev->lock); - if (tbstream_dev_valid(sdev) != 0) { -@@ -904,6 +1026,35 @@ tbstream_dev_from_group(struct config_group *group) - return container_of(group, struct tbstream_dev, group); - } - -+static ssize_t tbstream_dev_busy_poll_show(struct config_item *item, char *buf) -+{ -+ struct config_group *group = to_config_group(item); -+ struct tbstream_dev *sdev = tbstream_dev_from_group(group); -+ -+ return sysfs_emit(buf, "%u\n", sdev->busy_poll); -+} -+ -+static ssize_t -+tbstream_dev_busy_poll_store(struct config_item *item, const char *buf, -+ size_t count) -+{ -+ struct config_group *group = to_config_group(item); -+ struct tbstream_dev *sdev = tbstream_dev_from_group(group); -+ bool busy_poll; -+ int ret; -+ -+ ret = kstrtobool(buf, &busy_poll); -+ if (ret) -+ return ret; -+ -+ guard(mutex)(&sdev->lock); -+ if (sdev->users) -+ return -EBUSY; -+ sdev->busy_poll = busy_poll; -+ return count; -+} -+CONFIGFS_ATTR(tbstream_dev_, busy_poll); -+ - static ssize_t tbstream_dev_index_show(struct config_item *item, char *buf) - { - struct config_group *group = to_config_group(item); -@@ -1210,6 +1361,7 @@ tbstream_dev_throttling_store(struct config_item *item, const char *buf, - CONFIGFS_ATTR(tbstream_dev_, throttling); - - static struct configfs_attribute *tbstream_dev_attrs[] = { -+ &tbstream_dev_attr_busy_poll, - &tbstream_dev_attr_index, - &tbstream_dev_attr_in_hopid, - &tbstream_dev_attr_out_hopid, -diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h -index 557288c0274b..8367d106d81b 100644 ---- a/include/linux/thunderbolt.h -+++ b/include/linux/thunderbolt.h -@@ -592,6 +592,8 @@ struct tb_ring { - #define RING_FLAG_FRAME BIT(1) - /* Enable end-to-end flow control */ - #define RING_FLAG_E2E BIT(2) -+/* Do not enable interrupt for the ring */ -+#define RING_FLAG_NO_INTERRUPT BIT(3) - - struct ring_frame; - typedef void (*ring_cb)(struct tb_ring *, struct ring_frame *, bool canceled); diff --git a/pkgbuilds/linux-omarchy-bore/0601-usb4stream-busy-poll.patch.sig b/pkgbuilds/linux-omarchy-bore/0601-usb4stream-busy-poll.patch.sig deleted file mode 100644 index 8f8f07d..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/0601-usb4stream-busy-poll.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/0602-thunderbolt-fixes.patch b/pkgbuilds/linux-omarchy-bore/0602-thunderbolt-fixes.patch new file mode 100644 index 0000000..f2e51c0 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0602-thunderbolt-fixes.patch @@ -0,0 +1,557 @@ +diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c +--- a/drivers/thunderbolt/ctl.c ++++ b/drivers/thunderbolt/ctl.c +@@ -73,7 +73,6 @@ struct tb_ctl { + #define tb_ctl_dbg_once(ctl, format, arg...) \ + dev_dbg_once((ctl)->nhi->dev, format, ## arg) + +-static DECLARE_WAIT_QUEUE_HEAD(tb_cfg_request_cancel_queue); + /* Serializes access to request kref_get/put */ + static DEFINE_MUTEX(tb_cfg_request_lock); + +@@ -133,41 +132,42 @@ void tb_cfg_request_put(struct tb_cfg_request *req) + static int tb_cfg_request_enqueue(struct tb_ctl *ctl, + struct tb_cfg_request *req) + { ++ tb_cfg_request_get(req); ++ + WARN_ON(test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags)); + WARN_ON(req->ctl); + +- mutex_lock(&ctl->request_queue_lock); ++ guard(mutex)(&ctl->request_queue_lock); + if (!ctl->running) { +- mutex_unlock(&ctl->request_queue_lock); ++ tb_cfg_request_put(req); + return -ENOTCONN; + } + req->ctl = ctl; + list_add_tail(&req->list, &ctl->request_queue); + set_bit(TB_CFG_REQUEST_ACTIVE, &req->flags); +- mutex_unlock(&ctl->request_queue_lock); + return 0; + } + ++static bool tb_cfg_request_is_active(struct tb_cfg_request *req) ++{ ++ return test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags); ++} ++ ++static bool tb_cfg_request_is_canceled(struct tb_cfg_request *req) ++{ ++ return test_bit(TB_CFG_REQUEST_CANCELED, &req->flags); ++} ++ + static void tb_cfg_request_dequeue(struct tb_cfg_request *req) + { + struct tb_ctl *ctl = req->ctl; + +- mutex_lock(&ctl->request_queue_lock); +- if (!test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags)) { +- mutex_unlock(&ctl->request_queue_lock); +- return; ++ guard(mutex)(&ctl->request_queue_lock); ++ if (tb_cfg_request_is_active(req)) { ++ list_del(&req->list); ++ clear_bit(TB_CFG_REQUEST_ACTIVE, &req->flags); ++ tb_cfg_request_put(req); + } +- +- list_del(&req->list); +- clear_bit(TB_CFG_REQUEST_ACTIVE, &req->flags); +- if (test_bit(TB_CFG_REQUEST_CANCELED, &req->flags)) +- wake_up(&tb_cfg_request_cancel_queue); +- mutex_unlock(&ctl->request_queue_lock); +-} +- +-static bool tb_cfg_request_is_active(struct tb_cfg_request *req) +-{ +- return test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags); + } + + static struct tb_cfg_request * +@@ -178,7 +178,7 @@ tb_cfg_request_find(struct tb_ctl *ctl, struct ctl_pkg *pkg) + mutex_lock(&pkg->ctl->request_queue_lock); + list_for_each_entry(iter, &pkg->ctl->request_queue, list) { + tb_cfg_request_get(iter); +- if (iter->match(iter, pkg)) { ++ if (!tb_cfg_request_is_canceled(iter) && iter->match(iter, pkg)) { + req = iter; + break; + } +@@ -512,8 +512,11 @@ static void tb_ctl_rx_callback(struct tb_ring *ring, struct ring_frame *frame, + trace_tb_rx(pkg->ctl->index, frame->eof, pkg->buffer, frame->size, !req); + + if (req) { +- if (req->copy(req, pkg)) +- schedule_work(&req->work); ++ scoped_guard(mutex, &pkg->ctl->request_queue_lock) { ++ if (!tb_cfg_request_is_canceled(req) && ++ req->copy(req, pkg)) ++ schedule_work(&req->work); ++ } + tb_cfg_request_put(req); + } + +@@ -525,11 +528,10 @@ static void tb_cfg_request_work(struct work_struct *work) + { + struct tb_cfg_request *req = container_of(work, typeof(*req), work); + +- if (!test_bit(TB_CFG_REQUEST_CANCELED, &req->flags)) ++ if (!tb_cfg_request_is_canceled(req)) + req->callback(req->callback_data); + + tb_cfg_request_dequeue(req); +- tb_cfg_request_put(req); + } + + /** +@@ -555,10 +557,9 @@ int tb_cfg_request(struct tb_ctl *ctl, struct tb_cfg_request *req, + INIT_WORK(&req->work, tb_cfg_request_work); + INIT_LIST_HEAD(&req->list); + +- tb_cfg_request_get(req); + ret = tb_cfg_request_enqueue(ctl, req); + if (ret) +- goto err_put; ++ return ret; + + ret = tb_ctl_tx(ctl, req->request, req->request_size, + req->request_type); +@@ -572,9 +573,6 @@ int tb_cfg_request(struct tb_ctl *ctl, struct tb_cfg_request *req, + + err_dequeue: + tb_cfg_request_dequeue(req); +-err_put: +- tb_cfg_request_put(req); +- + return ret; + } + +@@ -588,9 +586,10 @@ int tb_cfg_request(struct tb_ctl *ctl, struct tb_cfg_request *req, + */ + void tb_cfg_request_cancel(struct tb_cfg_request *req, int err) + { +- set_bit(TB_CFG_REQUEST_CANCELED, &req->flags); +- schedule_work(&req->work); +- wait_event(tb_cfg_request_cancel_queue, !tb_cfg_request_is_active(req)); ++ scoped_guard(mutex, &req->ctl->request_queue_lock) ++ set_bit(TB_CFG_REQUEST_CANCELED, &req->flags); ++ cancel_work_sync(&req->work); ++ tb_cfg_request_dequeue(req); + req->result.err = err; + } + +diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c +--- a/drivers/thunderbolt/nhi.c ++++ b/drivers/thunderbolt/nhi.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -560,6 +561,8 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, + INIT_LIST_HEAD(&ring->in_flight); + INIT_WORK(&ring->work, ring_work); + init_waitqueue_head(&ring->wait); ++ lockdep_register_key(&ring->lock_key); ++ lockdep_init_map(&ring->work.lockdep_map, "ring.work", &ring->lock_key, 0); + + ring->nhi = nhi; + ring->hop = hop; +@@ -599,6 +602,7 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, + ring->size * sizeof(*ring->descriptors), + ring->descriptors, ring->descriptors_dma); + err_free_ring: ++ lockdep_unregister_key(&ring->lock_key); + kfree(ring); + + return NULL; +@@ -848,6 +852,7 @@ void tb_ring_free(struct tb_ring *ring) + * to finish before freeing the ring. + */ + flush_work(&ring->work); ++ lockdep_unregister_key(&ring->lock_key); + kfree(ring); + } + EXPORT_SYMBOL_GPL(tb_ring_free); +diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c +--- a/drivers/thunderbolt/switch.c ++++ b/drivers/thunderbolt/switch.c +@@ -774,6 +774,7 @@ static int tb_port_alloc_hopid(struct tb_port *port, bool in, int min_hopid, + { + int port_max_hopid; + struct ida *ida; ++ int ret; + + if (in) { + port_max_hopid = port->config.max_in_hop_id; +@@ -793,7 +794,11 @@ static int tb_port_alloc_hopid(struct tb_port *port, bool in, int min_hopid, + if (max_hopid < 0 || max_hopid > port_max_hopid) + max_hopid = port_max_hopid; + +- return ida_alloc_range(ida, min_hopid, max_hopid, GFP_KERNEL); ++ ret = ida_alloc_range(ida, min_hopid, max_hopid, GFP_KERNEL); ++ if (ret >= 0) ++ tb_switch_get(port->sw); ++ ++ return ret; + } + + /** +@@ -832,6 +837,7 @@ int tb_port_alloc_out_hopid(struct tb_port *port, int min_hopid, int max_hopid) + void tb_port_release_in_hopid(struct tb_port *port, int hopid) + { + ida_free(&port->in_hopids, hopid); ++ tb_switch_put(port->sw); + } + + /** +@@ -842,6 +848,7 @@ void tb_port_release_in_hopid(struct tb_port *port, int hopid) + void tb_port_release_out_hopid(struct tb_port *port, int hopid) + { + ida_free(&port->out_hopids, hopid); ++ tb_switch_put(port->sw); + } + + static inline bool tb_switch_is_reachable(const struct tb_switch *parent, +diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c +--- a/drivers/thunderbolt/tb.c ++++ b/drivers/thunderbolt/tb.c +@@ -89,6 +89,7 @@ static void tb_dp_resource_unavailable(struct tb *tb, struct tb_port *port, + const char *reason); + static void tb_queue_dp_bandwidth_request(struct tb *tb, u64 route, u8 port, + int retry, unsigned long delay); ++static void tb_dp_tunnel_active(struct tb_tunnel *tunnel, void *data); + + static void tb_queue_hotplug(struct tb *tb, u64 route, u8 port, bool unplug) + { +@@ -385,7 +386,8 @@ static void tb_switch_discover_tunnels(struct tb_switch *sw, + + switch (port->config.type) { + case TB_TYPE_DP_HDMI_IN: +- tunnel = tb_tunnel_discover_dp(tb, port, alloc_hopids); ++ tunnel = tb_tunnel_discover_dp(tb, port, alloc_hopids, ++ tb_dp_tunnel_active, tb); + tb_increase_tmu_accuracy(tunnel); + break; + +@@ -1910,6 +1912,18 @@ static void tb_dp_tunnel_active(struct tb_tunnel *tunnel, void *data) + struct tb *tb = data; + + mutex_lock(&tb->lock); ++ ++ /* ++ * If the DPRX read was canceled the tunnel is already being torn ++ * down by whoever canceled it. Do not touch the adapters here ++ * because the routers may be gone by now. ++ */ ++ if (tunnel->dprx_canceled) { ++ tb_tunnel_dbg(tunnel, "DPRX read canceled, not activating\n"); ++ mutex_unlock(&tb->lock); ++ return; ++ } ++ + if (tb_tunnel_is_active(tunnel)) { + int consumed_up, consumed_down, ret; + +@@ -1964,8 +1978,6 @@ static void tb_dp_tunnel_active(struct tb_tunnel *tunnel, void *data) + tb_dp_resource_unavailable(tb, in, "DPRX negotiation failed"); + } + mutex_unlock(&tb->lock); +- +- tb_domain_put(tb); + } + + static void tb_tunnel_one_dp(struct tb *tb, struct tb_port *in, +@@ -2026,8 +2038,7 @@ static void tb_tunnel_one_dp(struct tb *tb, struct tb_port *in, + available_up, available_down); + + tunnel = tb_tunnel_alloc_dp(tb, in, out, link_nr, available_up, +- available_down, tb_dp_tunnel_active, +- tb_domain_get(tb)); ++ available_down, tb_dp_tunnel_active, tb); + if (!tunnel) { + tb_port_dbg(out, "could not allocate DP tunnel\n"); + goto err_reclaim_usb; +@@ -2048,7 +2059,6 @@ static void tb_tunnel_one_dp(struct tb *tb, struct tb_port *in, + tb_tunnel_put(tunnel); + err_reclaim_usb: + tb_reclaim_usb3_bandwidth(tb, in, out); +- tb_domain_put(tb); + err_detach_group: + tb_detach_bandwidth_group(in); + err_dealloc_dp: +@@ -2950,11 +2960,12 @@ static void tb_stop(struct tb *tb) + /* tunnels are only present after everything has been initialized */ + list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) { + /* +- * DMA tunnels require the driver to be functional so we +- * tear them down. Other protocol tunnels can be left +- * intact. ++ * DMA tunnels and DP tunnels which are not yet active require ++ * the driver to be functional so we tear them down. ++ * Other protocol tunnels can be left intact. + */ +- if (tb_tunnel_is_dma(tunnel)) ++ if (tb_tunnel_is_dma(tunnel) || ++ (tb_tunnel_is_dp(tunnel) && !tb_tunnel_is_active(tunnel))) + tb_tunnel_deactivate(tunnel); + tb_tunnel_put(tunnel); + } +@@ -3274,11 +3285,11 @@ static void tb_remove_work(struct work_struct *work) + struct tb *tb = tcm_to_tb(tcm); + + mutex_lock(&tb->lock); +- if (tb->root_switch) ++ if (tb->root_switch) { + tb_free_unplugged_children(tb->root_switch); ++ tb_free_unplugged_xdomains(tb->root_switch); ++ } + mutex_unlock(&tb->lock); +- +- tb_free_unplugged_xdomains(tb->root_switch); + } + + static int tb_runtime_resume(struct tb *tb) +diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c +--- a/drivers/thunderbolt/tunnel.c ++++ b/drivers/thunderbolt/tunnel.c +@@ -510,6 +510,7 @@ struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down, + goto err_deactivate; + } + ++ tb_tunnel_set_active(tunnel, true); + tb_tunnel_dbg(tunnel, "discovered\n"); + return tunnel; + +@@ -1093,8 +1094,14 @@ static void tb_dp_dprx_work(struct work_struct *work) + struct tb_tunnel *tunnel = container_of(work, typeof(*tunnel), dprx_work.work); + struct tb *tb = tunnel->tb; + ++ /* ++ * The DPRX read can be canceled while this work is waiting for ++ * tb->lock. Check the flag only once it is held: while the lock is ++ * held the tunnel cannot be torn down under us and the adapters are ++ * safe to access. ++ */ ++ mutex_lock(&tb->lock); + if (!tunnel->dprx_canceled) { +- mutex_lock(&tb->lock); + if (tb_dp_is_usb4(tunnel->src_port->sw) && + tb_dp_wait_dprx(tunnel, TB_DPRX_WAIT_TIMEOUT)) { + if (ktime_before(ktime_get(), tunnel->dprx_timeout)) { +@@ -1106,41 +1113,42 @@ static void tb_dp_dprx_work(struct work_struct *work) + } else { + tb_tunnel_set_active(tunnel, true); + } +- mutex_unlock(&tb->lock); + } ++ mutex_unlock(&tb->lock); + +- if (tunnel->callback) +- tunnel->callback(tunnel, tunnel->callback_data); ++ tunnel->callback(tunnel, tunnel->callback_data); + tb_tunnel_put(tunnel); ++ tb_domain_put(tb); + } + + static int tb_dp_dprx_start(struct tb_tunnel *tunnel) + { + /* +- * Bump up the reference to keep the tunnel around. It will be +- * dropped in tb_dp_dprx_stop() once the tunnel is deactivated. ++ * Bump up the references to keep the tunnel and the domain around ++ * until the work has run or has been canceled. + */ + tb_tunnel_get(tunnel); ++ tb_domain_get(tunnel->tb); + + tunnel->dprx_started = true; ++ tunnel->dprx_canceled = false; ++ tunnel->dprx_timeout = dprx_timeout_to_ktime(dprx_timeout); ++ queue_delayed_work(tunnel->tb->wq, &tunnel->dprx_work, 0); + +- if (tunnel->callback) { +- tunnel->dprx_timeout = dprx_timeout_to_ktime(dprx_timeout); +- queue_delayed_work(tunnel->tb->wq, &tunnel->dprx_work, 0); +- return -EINPROGRESS; +- } +- +- return tb_dp_is_usb4(tunnel->src_port->sw) ? +- tb_dp_wait_dprx(tunnel, dprx_timeout) : 0; ++ return -EINPROGRESS; + } + + static void tb_dp_dprx_stop(struct tb_tunnel *tunnel) + { ++ struct tb *tb = tunnel->tb; ++ + if (tunnel->dprx_started) { + tunnel->dprx_started = false; + tunnel->dprx_canceled = true; +- if (cancel_delayed_work(&tunnel->dprx_work)) ++ if (cancel_delayed_work(&tunnel->dprx_work)) { + tb_tunnel_put(tunnel); ++ tb_domain_put(tb); ++ } + } + } + +@@ -1582,20 +1590,28 @@ static void tb_dp_dump(struct tb_tunnel *tunnel) + * @tb: Pointer to the domain structure + * @in: DP in adapter + * @alloc_hopid: Allocate HopIDs from visited ports ++ * @callback: Callback that is called when the DP tunnel is fully ++ * activated (or there is an error) ++ * @callback_data: Data for @callback + * + * If @in adapter is active, follows the tunnel to the DP out adapter + * and back. Returns the discovered tunnel or %NULL if there was no +- * tunnel. ++ * tunnel. See tb_tunnel_alloc_dp() for @callback. + * + * Return: Pointer to &struct tb_tunnel or %NULL if no tunnel found. + */ + struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in, +- bool alloc_hopid) ++ bool alloc_hopid, ++ void (*callback)(struct tb_tunnel *, void *), ++ void *callback_data) + { + struct tb_tunnel *tunnel; + struct tb_port *port; + struct tb_path *path; + ++ if (WARN_ON(!callback)) ++ return NULL; ++ + if (!tb_dp_port_is_enabled(in)) + return NULL; + +@@ -1611,6 +1627,9 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in, + tunnel->alloc_bandwidth = tb_dp_alloc_bandwidth; + tunnel->consumed_bandwidth = tb_dp_consumed_bandwidth; + tunnel->src_port = in; ++ tunnel->callback = callback; ++ tunnel->callback_data = callback_data; ++ INIT_DELAYED_WORK(&tunnel->dprx_work, tb_dp_dprx_work); + + path = tb_path_discover(in, TB_DP_VIDEO_HOPID, NULL, -1, + &tunnel->dst_port, "Video", alloc_hopid); +@@ -1656,6 +1675,7 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in, + + tb_dp_dump(tunnel); + ++ tb_tunnel_set_active(tunnel, true); + tb_tunnel_dbg(tunnel, "discovered\n"); + return tunnel; + +@@ -1677,16 +1697,16 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in, + * %0 if no available bandwidth. + * @max_down: Maximum available downstream bandwidth for the DP tunnel. + * %0 if no available bandwidth. +- * @callback: Optional callback that is called when the DP tunnel is +- * fully activated (or there is an error) +- * @callback_data: Optional data for @callback ++ * @callback: Callback that is called when the DP tunnel is fully ++ * activated (or there is an error) ++ * @callback_data: Data for @callback + * + * Allocates a tunnel between @in and @out that is capable of tunneling +- * Display Port traffic. If @callback is not %NULL it will be called +- * after tb_tunnel_activate() once the tunnel has been fully activated. +- * It can call tb_tunnel_is_active() to check if activation was +- * successful (or if it returns %false there was some sort of issue). +- * The @callback is called without @tb->lock held. ++ * Display Port traffic. The @callback is called after tb_tunnel_activate() ++ * once the tunnel has been fully activated. It can call ++ * tb_tunnel_is_active() to check if activation was successful (or if it ++ * returns %false there was some sort of issue). The @callback is called ++ * without @tb->lock held. + * + * Return: Pointer to @struct tb_tunnel or %NULL in case of failure. + */ +@@ -1701,7 +1721,7 @@ struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in, + struct tb_path *path; + bool pm_support; + +- if (WARN_ON(!in->cap_adap || !out->cap_adap)) ++ if (WARN_ON(!in->cap_adap || !out->cap_adap || !callback)) + return NULL; + + tunnel = tb_tunnel_alloc(tb, 3, TB_TUNNEL_DP); +@@ -2288,6 +2308,7 @@ struct tb_tunnel *tb_tunnel_discover_usb3(struct tb *tb, struct tb_port *down, + tb_usb3_reclaim_available_bandwidth; + } + ++ tb_tunnel_set_active(tunnel, true); + tb_tunnel_dbg(tunnel, "discovered\n"); + return tunnel; + +diff --git a/drivers/thunderbolt/tunnel.h b/drivers/thunderbolt/tunnel.h +--- a/drivers/thunderbolt/tunnel.h ++++ b/drivers/thunderbolt/tunnel.h +@@ -66,8 +66,8 @@ enum tb_tunnel_state { + * @dprx_canceled: Was DPRX capabilities read poll canceled + * @dprx_timeout: If set DPRX capabilities read poll work will timeout after this passes + * @dprx_work: Worker that is scheduled to poll completion of DPRX capabilities read +- * @callback: Optional callback called when DP tunnel is fully activated +- * @callback_data: Optional data for @callback ++ * @callback: Callback called when DP tunnel is fully activated ++ * @callback_data: Data for @callback + * @paths: All paths required by the tunnel + */ + struct tb_tunnel { +@@ -117,7 +117,9 @@ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up, + bool tb_tunnel_reserved_pci(struct tb_port *port, int *reserved_up, + int *reserved_down); + struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in, +- bool alloc_hopid); ++ bool alloc_hopid, ++ void (*callback)(struct tb_tunnel *, void *), ++ void *callback_data); + struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in, + struct tb_port *out, int link_nr, + int max_up, int max_down, +diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c +--- a/drivers/thunderbolt/xdomain.c ++++ b/drivers/thunderbolt/xdomain.c +@@ -1814,7 +1814,6 @@ static void tb_xdomain_state_work(struct work_struct *work) + tb_xdomain_failed(xd); + } else { + xd->state = XDOMAIN_STATE_ENUMERATED; +- tb_xdomain_queue_properties_changed(xd); + } + break; + +diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h +--- a/include/linux/thunderbolt.h ++++ b/include/linux/thunderbolt.h +@@ -22,6 +22,7 @@ struct device; + #include + #include + #include ++#include + #include + #include + #include +@@ -565,6 +566,7 @@ struct tb_nhi { + * @interval_nsec: Interval counter if interrupt throttling is to be + * used with this ring (in ns) + * @wait: Used to signal that the ring may be empty now ++ * @lock_key: Lock validator class key per-ring + */ + struct tb_ring { + spinlock_t lock; +@@ -590,6 +592,7 @@ struct tb_ring { + void *poll_data; + unsigned int interval_nsec; + wait_queue_head_t wait; ++ struct lock_class_key lock_key; + }; + + /* Leave ring interrupt enabled on suspend */ diff --git a/pkgbuilds/linux-omarchy-bore/0602-thunderbolt-fixes.patch.sig b/pkgbuilds/linux-omarchy-bore/0602-thunderbolt-fixes.patch.sig new file mode 100644 index 0000000..bc9e5e4 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0602-thunderbolt-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0603-usb-updates.patch b/pkgbuilds/linux-omarchy-bore/0603-usb-updates.patch new file mode 100644 index 0000000..171a4b4 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0603-usb-updates.patch @@ -0,0 +1,5629 @@ +diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c +--- a/drivers/power/supply/power_supply_core.c ++++ b/drivers/power/supply/power_supply_core.c +@@ -477,6 +477,159 @@ struct power_supply *power_supply_get_by_name(const char *name) + } + EXPORT_SYMBOL_GPL(power_supply_get_by_name); + ++static bool power_supply_is_system_battery(struct power_supply *psy) ++{ ++ union power_supply_propval val; ++ ++ if (psy->desc->type != POWER_SUPPLY_TYPE_BATTERY) ++ return false; ++ ++ if (!power_supply_get_property_direct(psy, POWER_SUPPLY_PROP_SCOPE, ++ &val)) ++ if (val.intval == POWER_SUPPLY_SCOPE_DEVICE) ++ return false; ++ ++ return true; ++} ++ ++static int __power_supply_get_num_system_batteries(struct power_supply *epsy, ++ void *data) ++{ ++ int *count = data; ++ ++ if (power_supply_is_system_battery(epsy)) ++ (*count)++; ++ ++ return 0; ++} ++ ++static int power_supply_get_num_system_batteries(struct device *dev) ++{ ++ int ret, count = 0; ++ ++ ret = power_supply_for_each_psy(&count, ++ __power_supply_get_num_system_batteries); ++ ++ dev_dbg(dev, "%s: count: %d ret %d\n", __func__, count, ret); ++ ++ if (ret) ++ return ret; ++ ++ return count; ++} ++ ++struct psy_get_supplies_data { ++ int cnt; ++ int size; ++ struct power_supply **psys; ++}; ++ ++static int ++__power_supply_populate_system_batteries_array(struct power_supply *epsy, ++ void *_data) ++{ ++ struct psy_get_supplies_data *data = _data; ++ ++ if (power_supply_is_system_battery(epsy)) { ++ if (data->size <= data->cnt) ++ return -EOVERFLOW; ++ ++ get_device(&epsy->dev); ++ data->psys[data->cnt] = epsy; ++ atomic_inc(&epsy->use_cnt); ++ data->cnt++; ++ } ++ ++ return 0; ++} ++ ++static int ++power_supply_populate_system_batteries_array(struct device *dev, int size, ++ struct power_supply **batteries) ++{ ++ int ret; ++ ++ struct psy_get_supplies_data data = { ++ .cnt = 0, ++ .size = size, ++ .psys = batteries, ++ }; ++ ++ ret = power_supply_for_each_psy(&data, ++ __power_supply_populate_system_batteries_array); ++ ++ dev_dbg(dev, "%s Found %d batteries with array size %d ret %d\n", ++ __func__, data.cnt, data.size, ret); ++ ++ if (ret < 0 || !data.cnt) { ++ power_supply_put_system_batteries(batteries, data.cnt); ++ return ret; ++ } ++ ++ return data.cnt; ++} ++ ++/** ++ * power_supply_get_system_batteries() - Fetches references to battery type ++ * power supplies in the system. ++ * @dev: Pointer to device requesting the power supply refs. ++ * @psys: Pointer to an array of power supply refs. ++ * ++ * Helper function to get handles to battery type power supplies in the system. ++ * If acquiring a ref to a power supply fails, then the search for battery type ++ * power supplies will abort and the acquired power supply references will be ++ * released. ++ * ++ * Return: Indicates the number of battery type power supplies returned on ++ * success or a negative error code on failure. ++ * ++ * Call power_supply_put_system_batteries() after use to cleanup resources. ++ */ ++int __must_check power_supply_get_system_batteries(struct device *dev, ++ struct power_supply ***psys) ++{ ++ int ret; ++ ++ if (!psys) ++ return -EINVAL; ++ ++ ret = power_supply_get_num_system_batteries(dev); ++ if (ret <= 0) { ++ *psys = NULL; ++ return ret; ++ } ++ ++ *psys = kzalloc_objs(**psys, ret); ++ if (!*psys) ++ return -ENOMEM; ++ ++ ret = power_supply_populate_system_batteries_array(dev, ret, *psys); ++ if (ret <= 0) ++ *psys = NULL; ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(power_supply_get_system_batteries); ++ ++/** ++ * power_supply_put_system_batteries() - Cleanup resources allocated by ++ * power_supply_get_system_batteries() ++ * @psys: Array of power supply references to release and free. ++ * @count: Number of elements in the array. ++ */ ++void power_supply_put_system_batteries(struct power_supply **psys, int count) ++{ ++ int i; ++ ++ for (i = 0; i < count; i++) { ++ if (psys[i]) ++ power_supply_put(psys[i]); ++ } ++ ++ kfree(psys); ++} ++EXPORT_SYMBOL_GPL(power_supply_put_system_batteries); ++ + /** + * power_supply_put() - Drop reference obtained with power_supply_get_by_name + * @psy: Reference to put +diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c +--- a/drivers/usb/cdns3/cdns3-plat.c ++++ b/drivers/usb/cdns3/cdns3-plat.c +@@ -147,12 +147,12 @@ static int cdns3_plat_probe(struct platform_device *pdev) + cdns->usb2_phy = devm_phy_optional_get(dev, "cdns3,usb2-phy"); + if (IS_ERR(cdns->usb2_phy)) + return dev_err_probe(dev, PTR_ERR(cdns->usb2_phy), +- "Failed to get cdn3,usb2-phy\n"); ++ "Failed to get cdns3,usb2-phy\n"); + + cdns->usb3_phy = devm_phy_optional_get(dev, "cdns3,usb3-phy"); + if (IS_ERR(cdns->usb3_phy)) + return dev_err_probe(dev, PTR_ERR(cdns->usb3_phy), +- "Failed to get cdn3,usb3-phy\n"); ++ "Failed to get cdns3,usb3-phy\n"); + + ret = phy_init(cdns->usb2_phy); + if (ret) +diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c +--- a/drivers/usb/core/config.c ++++ b/drivers/usb/core/config.c +@@ -151,7 +151,7 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, + usb_endpoint_xfer_int(&ep->desc)) && + desc->bmAttributes != 0) { + dev_notice(ddev, "%s endpoint with bmAttributes = %d in config %d interface %d altsetting %d ep 0x%X: setting to zero\n", +- usb_endpoint_xfer_control(&ep->desc) ? "Control" : "Bulk", ++ usb_endpoint_xfer_control(&ep->desc) ? "Control" : "Interrupt", + desc->bmAttributes, + cfgno, inum, asnum, ep->desc.bEndpointAddress); + ep->ss_ep_comp.bmAttributes = 0; +diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c +--- a/drivers/usb/core/devices.c ++++ b/drivers/usb/core/devices.c +@@ -37,6 +37,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -408,7 +409,7 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, + return 0; + /* allocate 2^1 pages = 8K (on i386); + * should be more than enough for one device */ +- pages_start = (char *)__get_free_pages(GFP_NOIO, 1); ++ pages_start = kmalloc(PAGE_SIZE << 1, GFP_NOIO); + if (!pages_start) + return -ENOMEM; + +@@ -479,7 +480,7 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, + if (length > *nbytes) + length = *nbytes; + if (copy_to_user(*buffer, pages_start + *skip_bytes, length)) { +- free_pages((unsigned long)pages_start, 1); ++ kfree(pages_start); + return -EFAULT; + } + *nbytes -= length; +@@ -490,7 +491,7 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, + } else + *skip_bytes -= length; + +- free_pages((unsigned long)pages_start, 1); ++ kfree(pages_start); + + /* Now look at all of this device's children. */ + usb_hub_for_each_child(usbdev, chix, childdev) { +diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c +--- a/drivers/usb/core/devio.c ++++ b/drivers/usb/core/devio.c +@@ -2329,6 +2329,13 @@ static int proc_ioctl(struct usb_dev_state *ps, struct usbdevfs_ioctl *ctl) + if (!connected(ps)) + return -ENODEV; + ++ if (ps->dev->state != USB_STATE_CONFIGURED) ++ return -EHOSTUNREACH; ++ ++ intf = usb_ifnum_to_if(ps->dev, ctl->ifno); ++ if (!intf) ++ return -EINVAL; ++ + /* alloc buffer */ + size = _IOC_SIZE(ctl->ioctl_code); + if (size > 0) { +@@ -2345,11 +2352,7 @@ static int proc_ioctl(struct usb_dev_state *ps, struct usbdevfs_ioctl *ctl) + } + } + +- if (ps->dev->state != USB_STATE_CONFIGURED) +- retval = -EHOSTUNREACH; +- else if (!(intf = usb_ifnum_to_if(ps->dev, ctl->ifno))) +- retval = -EINVAL; +- else switch (ctl->ioctl_code) { ++ switch (ctl->ioctl_code) { + + /* disconnect kernel driver from interface */ + case USBDEVFS_DISCONNECT: +diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c +--- a/drivers/usb/core/hub.c ++++ b/drivers/usb/core/hub.c +@@ -623,11 +623,11 @@ static int hub_ext_port_status(struct usb_hub *hub, int port1, int type, + mutex_lock(&hub->status_mutex); + ret = get_port_status(hub->hdev, port1, &hub->status->port, type, len); + if (ret < len) { +- if (ret != -ENODEV) +- dev_err(hub->intfdev, +- "%s failed (err = %d)\n", __func__, ret); + if (ret >= 0) + ret = -EIO; ++ if (ret != -ENODEV) ++ dev_dbg(hub->intfdev, ++ "get_port_status failed: err = %d\n", ret); + } else { + *status = le16_to_cpu(hub->status->port.wPortStatus); + *change = le16_to_cpu(hub->status->port.wPortChange); +@@ -995,11 +995,11 @@ static int hub_hub_status(struct usb_hub *hub, + mutex_lock(&hub->status_mutex); + ret = get_hub_status(hub->hdev, &hub->status->hub); + if (ret < (int)sizeof(hub->status->hub)) { +- if (ret != -ENODEV) +- dev_err(hub->intfdev, +- "%s failed (err = %d)\n", __func__, ret); + if (ret >= 0) + ret = -EIO; ++ if (ret != -ENODEV) ++ dev_dbg(hub->intfdev, ++ "get_hub_status failed: err = %d\n", ret); + } else { + *status = le16_to_cpu(hub->status->hub.wHubStatus); + *change = le16_to_cpu(hub->status->hub.wHubChange); +diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c +--- a/drivers/usb/dwc2/core_intr.c ++++ b/drivers/usb/dwc2/core_intr.c +@@ -334,9 +334,8 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) + + /** + * dwc2_wakeup_from_lpm_l1 - Exit the device from LPM L1 state +- * + * @hsotg: Programming view of DWC_otg controller +- * ++ * @remotewakeup: Whether this is a remote wakeup + */ + void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg, bool remotewakeup) + { +diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -2188,22 +2188,89 @@ static void dwc3_vbus_draw_work(struct work_struct *work) + ret, dwc->current_limit); + } + +-static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc) ++static int dwc3_psy_notifier(struct notifier_block *nb, ++ unsigned long event, void *data) + { +- struct power_supply *usb_psy; +- const char *usb_psy_name; ++ struct dwc3 *dwc = container_of(nb, struct dwc3, psy_nb); ++ struct power_supply *psy = data; ++ unsigned long flags; ++ ++ if (dwc->usb_psy) ++ return NOTIFY_DONE; ++ ++ if (strcmp(psy->desc->name, dwc->usb_psy_name) != 0) ++ return NOTIFY_DONE; ++ ++ /* Explicitly get the reference for this psy */ ++ psy = power_supply_get_by_name(dwc->usb_psy_name); ++ if (!psy) ++ return NOTIFY_DONE; ++ ++ spin_lock_irqsave(&dwc->lock, flags); ++ /* ++ * The USB power_supply may already be set. This can happen if notifier ++ * callbacks for the USB power_supply race, or if a previous notifier ++ * callback has already successfully fetched and associated the instance. ++ * In such cases, release the newly acquired reference and ignore ++ * subsequent notifications until the notifier is unregistered. ++ */ ++ if (dwc->usb_psy) { ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ power_supply_put(psy); ++ return NOTIFY_DONE; ++ } ++ ++ dwc->usb_psy = psy; ++ if (dwc->current_limit != DWC3_CURRENT_UNSPECIFIED) ++ schedule_work(&dwc->vbus_draw_work); ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ ++ return NOTIFY_OK; ++} ++ ++static void dwc3_get_usb_power_supply(struct dwc3 *dwc) ++{ ++ struct power_supply *psy; ++ unsigned long flags; + int ret; + +- ret = device_property_read_string(dwc->dev, "usb-psy-name", &usb_psy_name); ++ ret = device_property_read_string(dwc->dev, "usb-psy-name", &dwc->usb_psy_name); + if (ret < 0) +- return NULL; +- +- usb_psy = power_supply_get_by_name(usb_psy_name); +- if (!usb_psy) +- return ERR_PTR(-EPROBE_DEFER); ++ return; + + INIT_WORK(&dwc->vbus_draw_work, dwc3_vbus_draw_work); +- return usb_psy; ++ ++ dwc->current_limit = DWC3_CURRENT_UNSPECIFIED; ++ dwc->psy_nb.notifier_call = dwc3_psy_notifier; ++ ret = power_supply_reg_notifier(&dwc->psy_nb); ++ if (ret) { ++ dev_err(dwc->dev, "Failed to register power supply notifier: %d\n", ret); ++ dwc->psy_nb.notifier_call = NULL; ++ return; ++ } ++ ++ psy = power_supply_get_by_name(dwc->usb_psy_name); ++ if (!psy) ++ return; ++ ++ /* Unregister the notifier now that we have the power supply */ ++ power_supply_unreg_notifier(&dwc->psy_nb); ++ dwc->psy_nb.notifier_call = NULL; ++ ++ spin_lock_irqsave(&dwc->lock, flags); ++ /* ++ * It is possible that the notifier callback ran before we reached here ++ * and successfully fetched the power supply. In that case we need to ++ * release the above reference. ++ */ ++ if (dwc->usb_psy) { ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ power_supply_put(psy); ++ return; ++ } ++ ++ dwc->usb_psy = psy; ++ spin_unlock_irqrestore(&dwc->lock, flags); + } + + int dwc3_core_probe(const struct dwc3_probe_data *data) +@@ -2251,9 +2318,9 @@ int dwc3_core_probe(const struct dwc3_probe_data *data) + + dwc3_get_software_properties(dwc, &data->properties); + +- dwc->usb_psy = dwc3_get_usb_power_supply(dwc); +- if (IS_ERR(dwc->usb_psy)) +- return dev_err_probe(dev, PTR_ERR(dwc->usb_psy), "couldn't get usb power supply\n"); ++ spin_lock_init(&dwc->lock); ++ ++ dwc3_get_usb_power_supply(dwc); + + if (!data->ignore_clocks_and_resets) { + dwc->reset = devm_reset_control_array_get_optional_shared(dev); +@@ -2305,7 +2372,6 @@ int dwc3_core_probe(const struct dwc3_probe_data *data) + dwc->num_usb3_ports = 1; + } + +- spin_lock_init(&dwc->lock); + mutex_init(&dwc->mutex); + + pm_runtime_get_noresume(dev); +@@ -2373,6 +2439,8 @@ int dwc3_core_probe(const struct dwc3_probe_data *data) + err_assert_reset: + reset_control_assert(dwc->reset); + err_put_psy: ++ if (dwc->psy_nb.notifier_call) ++ power_supply_unreg_notifier(&dwc->psy_nb); + if (dwc->usb_psy) + power_supply_put(dwc->usb_psy); + +@@ -2429,6 +2497,9 @@ void dwc3_core_remove(struct dwc3 *dwc) + + dwc3_free_event_buffers(dwc); + ++ if (dwc->psy_nb.notifier_call) ++ power_supply_unreg_notifier(&dwc->psy_nb); ++ + if (dwc->usb_psy) { + cancel_work_sync(&dwc->vbus_draw_work); + power_supply_put(dwc->usb_psy); +diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h +--- a/drivers/usb/dwc3/core.h ++++ b/drivers/usb/dwc3/core.h +@@ -722,7 +722,6 @@ struct dwc3_event_buffer { + * @cancelled_list: list of cancelled requests for this endpoint + * @pending_list: list of pending requests for this endpoint + * @started_list: list of started requests on this endpoint +- * @regs: pointer to first endpoint register + * @trb_pool: array of transaction buffers + * @trb_pool_dma: dma address of @trb_pool + * @trb_enqueue: enqueue 'pointer' into TRB array +@@ -1059,6 +1058,8 @@ struct dwc3_glue_ops { + * @role_switch_default_mode: default operation mode of controller while + * usb role is USB_ROLE_NONE. + * @usb_psy: pointer to power supply interface. ++ * @usb_psy_name: name of the USB power supply ++ * @psy_nb: power supply notifier block + * @vbus_draw_work: Work to set the vbus drawing limit + * @current_limit: How much current to draw from vbus, in milliAmperes. + * @usb2_phy: pointer to USB2 PHY +@@ -1251,9 +1252,13 @@ struct dwc3 { + enum usb_dr_mode role_switch_default_mode; + + struct power_supply *usb_psy; ++ const char *usb_psy_name; ++ struct notifier_block psy_nb; + struct work_struct vbus_draw_work; + unsigned int current_limit; + ++#define DWC3_CURRENT_UNSPECIFIED UINT_MAX ++ + u32 fladj; + u32 ref_clk_per; + u32 irq_gadget; +diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c +--- a/drivers/usb/dwc3/dwc3-am62.c ++++ b/drivers/usb/dwc3/dwc3-am62.c +@@ -205,7 +205,9 @@ static int dwc3_ti_init(struct dwc3_am62 *am62) + + dwc3_ti_writel(am62, USBSS_PHY_CONFIG, reg); + +- clk_prepare_enable(am62->usb2_refclk); ++ ret = clk_prepare_enable(am62->usb2_refclk); ++ if (ret) ++ return ret; + + /* Set mode valid bit to indicate role is valid */ + reg = dwc3_ti_readl(am62, USBSS_MODE_CONTROL); +@@ -361,14 +363,19 @@ static int dwc3_ti_resume_common(struct device *dev) + { + struct dwc3_am62 *am62 = dev_get_drvdata(dev); + u32 reg; ++ int ret; + + reg = dwc3_ti_readl(am62, USBSS_DEBUG_CFG); + if (reg != USBSS_DEBUG_CFG_DISABLED) { + /* lost power/context */ +- dwc3_ti_init(am62); ++ ret = dwc3_ti_init(am62); ++ if (ret) ++ return ret; + } else { + dwc3_ti_writel(am62, USBSS_DEBUG_CFG, USBSS_DEBUG_CFG_OFF); +- clk_prepare_enable(am62->usb2_refclk); ++ ret = clk_prepare_enable(am62->usb2_refclk); ++ if (ret) ++ return ret; + } + + if (device_may_wakeup(dev)) { +diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c +--- a/drivers/usb/dwc3/dwc3-qcom.c ++++ b/drivers/usb/dwc3/dwc3-qcom.c +@@ -602,7 +602,7 @@ static void dwc3_qcom_run_stop_notifier(struct dwc3 *dwc, bool is_on) + pm_runtime_mark_last_busy(qcom->dev); + } + +-struct dwc3_glue_ops dwc3_qcom_glue_ops = { ++static struct dwc3_glue_ops dwc3_qcom_glue_ops = { + .pre_set_role = dwc3_qcom_set_role_notifier, + .pre_run_stop = dwc3_qcom_run_stop_notifier, + }; +diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c +--- a/drivers/usb/dwc3/dwc3-xilinx.c ++++ b/drivers/usb/dwc3/dwc3-xilinx.c +@@ -98,18 +98,10 @@ static int dwc3_xlnx_init_versal(struct dwc3_xlnx *priv_data) + + dwc3_xlnx_mask_phy_rst(priv_data, false); + +- /* Assert and De-assert reset */ +- ret = reset_control_assert(crst); +- if (ret < 0) { +- dev_err_probe(dev, ret, "failed to assert Reset\n"); +- return ret; +- } +- +- ret = reset_control_deassert(crst); +- if (ret < 0) { +- dev_err_probe(dev, ret, "failed to De-assert Reset\n"); +- return ret; +- } ++ /* assert and deassert reset */ ++ ret = reset_control_reset(crst); ++ if (ret) ++ return dev_err_probe(dev, ret, "failed to assert and deassert reset\n"); + + dwc3_xlnx_mask_phy_rst(priv_data, true); + dwc3_xlnx_set_coherency(priv_data, XLNX_USB2_TRAFFIC_ROUTE_CONFIG); +@@ -194,7 +186,7 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) + } + + if (priv_data->usb3_phy) { +- /* Set PIPE Power Present signal in FPD Power Present Register*/ ++ /* Set PIPE Power Present signal in FPD Power Present Register */ + writel(FPD_POWER_PRSNT_OPTION, priv_data->regs + XLNX_USB_FPD_POWER_PRSNT); + /* Set the PIPE Clock Select bit in FPD PIPE Clock register */ + writel(PIPE_CLK_SELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK); +diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c +--- a/drivers/usb/dwc3/gadget.c ++++ b/drivers/usb/dwc3/gadget.c +@@ -3129,15 +3129,26 @@ static void dwc3_gadget_set_ssp_rate(struct usb_gadget *g, + static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA) + { + struct dwc3 *dwc = gadget_to_dwc(g); ++ unsigned long flags; + + if (dwc->usb2_phy) + return usb_phy_set_power(dwc->usb2_phy, mA); + +- if (!dwc->usb_psy) +- return -EOPNOTSUPP; ++ spin_lock_irqsave(&dwc->lock, flags); ++ if (!dwc->usb_psy) { ++ if (!dwc->psy_nb.notifier_call) { ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ return -EOPNOTSUPP; ++ } ++ dwc->current_limit = mA; ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ dev_dbg(dwc->dev, "Stored VBUS draw: %u mA (power supply not ready)\n", mA); ++ return 0; ++ } + + dwc->current_limit = mA; + schedule_work(&dwc->vbus_draw_work); ++ spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; + } +diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c +--- a/drivers/usb/dwc3/host.c ++++ b/drivers/usb/dwc3/host.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + + #include "../host/xhci-port.h" + #include "../host/xhci-ext-caps.h" +@@ -46,9 +47,9 @@ static void dwc3_power_off_all_roothub_ports(struct dwc3 *dwc) + return; + } + +- op_regs_base = HC_LENGTH(readl(xhci_regs)); ++ op_regs_base = FIELD_GET(HC_LENGTH, readl(xhci_regs)); + reg = readl(xhci_regs + XHCI_HCSPARAMS1); +- port_num = HCS_MAX_PORTS(reg); ++ port_num = FIELD_GET(HCS_MAX_PORTS, reg); + + for (i = 1; i <= port_num; i++) { + offset = op_regs_base + XHCI_PORTSC_BASE + 0x10 * (i - 1); +diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c +--- a/drivers/usb/gadget/configfs.c ++++ b/drivers/usb/gadget/configfs.c +@@ -125,11 +125,11 @@ static int usb_string_copy(const char *s, char **s_copy) + if (copy) { + str = copy; + } else { +- str = kmalloc(USB_MAX_STRING_WITH_NULL_LEN, GFP_KERNEL); ++ str = kzalloc(USB_MAX_STRING_WITH_NULL_LEN, GFP_KERNEL); + if (!str) + return -ENOMEM; + } +- strcpy(str, s); ++ memcpy(str, s, ret + 1); + if (str[ret - 1] == '\n') + str[ret - 1] = '\0'; + *s_copy = str; +diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c +--- a/drivers/usb/gadget/function/f_fs.c ++++ b/drivers/usb/gadget/function/f_fs.c +@@ -159,7 +159,9 @@ struct ffs_epfile { + struct mutex mutex; + + struct ffs_data *ffs; +- struct ffs_ep *ep; /* P: ffs->eps_lock */ ++ struct ffs_ep *ep; /* P: ffs->eps_lock */ ++ struct ffs_epfile *epfile_in; /* P: ffs->eps_lock */ ++ struct ffs_epfile *epfile_out; /* P: ffs->eps_lock */ + + /* + * Buffer for holding data from partial reads which may happen since +@@ -219,12 +221,13 @@ struct ffs_epfile { + struct ffs_buffer *read_buffer; + #define READ_BUFFER_DROP ((struct ffs_buffer *)ERR_PTR(-ESHUTDOWN)) + +- char name[5]; ++ char name[8]; + + unsigned char in; /* P: ffs->eps_lock */ + unsigned char isoc; /* P: ffs->eps_lock */ + +- unsigned char _pad; ++ u8 zlp_enabled; /* P: ffs->eps_lock */ ++ bool is_rw_proxy; + + /* Protects dmabufs */ + struct mutex dmabufs_mutex; +@@ -991,9 +994,8 @@ static ssize_t __ffs_epfile_read_data(struct ffs_epfile *epfile, + return ret; + } + +-static struct ffs_ep *ffs_epfile_wait_ep(struct file *file) ++static struct ffs_ep *ffs_epfile_wait_ep(struct ffs_epfile *epfile, struct file *file) + { +- struct ffs_epfile *epfile = file->private_data; + struct ffs_ep *ep; + int ret; + +@@ -1020,17 +1022,22 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) + char *data = NULL; + ssize_t ret, data_len = -EINVAL; + int halt; ++ bool is_rw_proxy = epfile->is_rw_proxy; + + /* Are we still active? */ + if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) + return -ENODEV; + +- ep = ffs_epfile_wait_ep(file); ++ /* Proxy to base endpoint if rw_proxy */ ++ if (is_rw_proxy) ++ epfile = io_data->read ? epfile->epfile_out : epfile->epfile_in; ++ ++ ep = ffs_epfile_wait_ep(epfile, file); + if (IS_ERR(ep)) + return PTR_ERR(ep); + + /* Do we halt? */ +- halt = (!io_data->read == !epfile->in); ++ halt = is_rw_proxy ? 0 : (!io_data->read == !epfile->in); + if (halt && epfile->isoc) + return -EINVAL; + +@@ -1127,6 +1134,8 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) + req->buf = data; + req->num_sgs = 0; + } ++ ++ req->zero = !io_data->read ? epfile->zlp_enabled : 0; + req->length = data_len; + + io_data->buf = data; +@@ -1178,6 +1187,8 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) + req->buf = data; + req->num_sgs = 0; + } ++ ++ req->zero = !io_data->read ? epfile->zlp_enabled : 0; + req->length = data_len; + + io_data->buf = data; +@@ -1666,7 +1677,7 @@ static int ffs_dmabuf_transfer(struct file *file, + + priv = attach->importer_priv; + +- ep = ffs_epfile_wait_ep(file); ++ ep = ffs_epfile_wait_ep(epfile, file); + if (IS_ERR(ep)) { + ret = PTR_ERR(ep); + goto err_attachment_put; +@@ -1732,6 +1743,7 @@ static int ffs_dmabuf_transfer(struct file *file, + + /* Now that the dma_fence is in place, queue the transfer. */ + ++ usb_req->zero = epfile->zlp_enabled; + usb_req->length = req->length; + usb_req->buf = NULL; + usb_req->sg = priv->sgt->sgl; +@@ -1779,10 +1791,14 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, + struct ffs_epfile *epfile = file->private_data; + struct ffs_ep *ep; + int ret; ++ __u32 enable_zlp = 0; + + if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) + return -ENODEV; + ++ if (epfile->is_rw_proxy) ++ return -ENOTTY; ++ + switch (code) { + case FUNCTIONFS_DMABUF_ATTACH: + { +@@ -1811,12 +1827,29 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, + + return ffs_dmabuf_transfer(file, &req); + } ++ /* ++ * We handle this IOCTL before ffs_epfile_wait_ep() to allow userspace ++ * to configure ZLP behavior immediately without blocking indefinitely ++ * while waiting for the USB host to connect and enable the endpoint. ++ */ ++ case FUNCTIONFS_ENDPOINT_ENABLE_ZLP: ++ if (!epfile->in) ++ return -EINVAL; ++ ++ if (copy_from_user(&enable_zlp, (void __user *)value, sizeof(enable_zlp))) ++ return -EFAULT; ++ ++ spin_lock_irq(&epfile->ffs->eps_lock); ++ epfile->zlp_enabled = !!enable_zlp; ++ spin_unlock_irq(&epfile->ffs->eps_lock); ++ ++ return 0; + default: + break; + } + + /* Wait for endpoint to be enabled */ +- ep = ffs_epfile_wait_ep(file); ++ ep = ffs_epfile_wait_ep(epfile, file); + if (IS_ERR(ep)) + return PTR_ERR(ep); + +@@ -2214,7 +2247,7 @@ static void ffs_data_closed(struct ffs_data *ffs) + + if (epfiles) + ffs_epfiles_destroy(ffs->sb, epfiles, +- ffs->eps_count); ++ ffs->epfiles_count); + + if (ffs->setup_state == FFS_SETUP_PENDING) + __ffs_ep0_stall(ffs); +@@ -2273,7 +2306,7 @@ static void ffs_data_clear(struct ffs_data *ffs) + * copy of epfile will save us from use-after-free. + */ + if (epfiles) { +- ffs_epfiles_destroy(ffs->sb, epfiles, ffs->eps_count); ++ ffs_epfiles_destroy(ffs->sb, epfiles, ffs->epfiles_count); + ffs->epfiles = NULL; + } + +@@ -2371,11 +2404,16 @@ static void functionfs_unbind(struct ffs_data *ffs) + static int ffs_epfiles_create(struct ffs_data *ffs) + { + struct ffs_epfile *epfile, *epfiles; +- unsigned i, count; ++ unsigned int i, count, epfiles_count; + int err; + + count = ffs->eps_count; +- epfiles = kzalloc_objs(*epfiles, count); ++ epfiles_count = count; ++ if (ffs->user_flags & FUNCTIONFS_RW_PROXY_EPS) ++ epfiles_count += count / 2; ++ ffs->epfiles_count = epfiles_count; ++ ++ epfiles = kzalloc_objs(*epfiles, epfiles_count); + if (!epfiles) + return -ENOMEM; + +@@ -2398,6 +2436,32 @@ static int ffs_epfiles_create(struct ffs_data *ffs) + } + } + ++ if (ffs->user_flags & FUNCTIONFS_RW_PROXY_EPS) { ++ struct ffs_epfile *comp = epfiles + count; ++ ++ for (i = 0; i < count; i += 2, ++comp) { ++ struct ffs_epfile *ep1 = &epfiles[i]; ++ struct ffs_epfile *ep2 = &epfiles[i + 1]; ++ bool ep1_in = ffs->eps_addrmap[i + 1] & USB_ENDPOINT_DIR_MASK; ++ ++ comp->ffs = ffs; ++ comp->is_rw_proxy = true; ++ comp->epfile_in = ep1_in ? ep1 : ep2; ++ comp->epfile_out = ep1_in ? ep2 : ep1; ++ mutex_init(&comp->mutex); ++ mutex_init(&comp->dmabufs_mutex); ++ INIT_LIST_HEAD(&comp->dmabufs); ++ snprintf(comp->name, sizeof(comp->name), "%s_rw", ++ epfiles[i].name); ++ err = ffs_sb_create_file(ffs->sb, comp->name, ++ comp, &ffs_epfile_operations); ++ if (err) { ++ ffs_epfiles_destroy(ffs->sb, epfiles, count + (i / 2)); ++ return err; ++ } ++ } ++ } ++ + ffs->epfiles = epfiles; + return 0; + } +@@ -2975,7 +3039,8 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, + FUNCTIONFS_VIRTUAL_ADDR | + FUNCTIONFS_EVENTFD | + FUNCTIONFS_ALL_CTRL_RECIP | +- FUNCTIONFS_CONFIG0_SETUP)) { ++ FUNCTIONFS_CONFIG0_SETUP | ++ FUNCTIONFS_RW_PROXY_EPS)) { + ret = -ENOSYS; + goto error; + } +@@ -3063,6 +3128,21 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, + goto error; + } + ++ if (ffs->user_flags & FUNCTIONFS_RW_PROXY_EPS) { ++ if (ffs->eps_count % 2) { ++ ret = -EINVAL; ++ goto error; ++ } ++ ++ for (i = 1; i < ffs->eps_count; i += 2) { ++ if ((ffs->eps_addrmap[i] & USB_ENDPOINT_DIR_MASK) == ++ (ffs->eps_addrmap[i + 1] & USB_ENDPOINT_DIR_MASK)) { ++ ret = -EINVAL; ++ goto error; ++ } ++ } ++ } ++ + ffs->raw_descs_data = _data; + ffs->raw_descs = raw_descs; + ffs->raw_descs_length = data - raw_descs; +@@ -3358,7 +3438,7 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, + struct usb_request *req; + struct usb_ep *ep; + u8 bEndpointAddress; +- u16 wMaxPacketSize; ++ __le16 wMaxPacketSize; + + /* + * We back up bEndpointAddress because autoconfig overwrites +diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c +--- a/drivers/usb/gadget/function/f_mass_storage.c ++++ b/drivers/usb/gadget/function/f_mass_storage.c +@@ -203,7 +203,6 @@ + /*------------------------------------------------------------------------*/ + + #define FSG_DRIVER_DESC "Mass Storage Function" +-#define FSG_DRIVER_VERSION "2009/09/11" + + static const char fsg_string_interface[] = "Mass Storage"; + +@@ -3516,8 +3515,6 @@ static struct usb_function_instance *fsg_alloc_inst(void) + if (rc) + goto release_common; + +- pr_info(FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n"); +- + memset(&config, 0, sizeof(config)); + config.removable = true; + rc = fsg_common_create_lun(opts->common, &config, 0, "lun.0", +diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c +--- a/drivers/usb/gadget/function/u_audio.c ++++ b/drivers/usb/gadget/function/u_audio.c +@@ -641,15 +641,15 @@ int u_audio_start_capture(struct g_audio *audio_dev) + ret = config_ep_by_speed(gadget, &audio_dev->func, ep_fback); + if (ret < 0) { + dev_err(dev, "config_ep_by_speed in_ep_fback failed (%d)\n", ret); +- return ret; // TODO: Clean up out_ep ++ goto err_out_ep; + } + +- prm->fb_ep_enabled = true; + ret = usb_ep_enable(ep_fback); + if (ret < 0) { + dev_err(dev, "usb_ep_enable failed for in_ep_fback (%d)\n", ret); +- return ret; // TODO: Clean up out_ep ++ goto err_out_ep; + } ++ prm->fb_ep_enabled = true; + req_len = ep_fback->maxpacket; + + req_fback = usb_ep_alloc_request(ep_fback, GFP_ATOMIC); +@@ -680,6 +680,12 @@ int u_audio_start_capture(struct g_audio *audio_dev) + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); + + return 0; ++ ++err_out_ep: ++ set_active(prm, false); ++ free_ep(prm, ep); ++ ++ return ret; + } + EXPORT_SYMBOL_GPL(u_audio_start_capture); + +diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h +--- a/drivers/usb/gadget/function/u_fs.h ++++ b/drivers/usb/gadget/function/u_fs.h +@@ -252,8 +252,14 @@ struct ffs_data { + + unsigned short strings_count; + unsigned short interfaces_count; ++ ++ /* ++ * eps_count tracks the number of underlying hardware endpoints. ++ * epfiles_count tracks the total number of VFS endpoint files. ++ * When companion endpoints are active, epfiles_count > eps_count. ++ */ + unsigned short eps_count; +- unsigned short _pad1; ++ unsigned short epfiles_count; + + /* filled by __ffs_data_got_strings() */ + /* ids in stringtabs are set in functionfs_bind() */ +diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c +--- a/drivers/usb/gadget/legacy/inode.c ++++ b/drivers/usb/gadget/legacy/inode.c +@@ -471,11 +471,17 @@ static void ep_user_copy_worker(struct work_struct *work) + struct kiocb *iocb = priv->iocb; + size_t ret; + +- kthread_use_mm(mm); +- ret = copy_to_iter(priv->buf, priv->actual, &priv->to); +- kthread_unuse_mm(mm); +- if (!ret) ++ if (mmget_not_zero(mm)) { ++ kthread_use_mm(mm); ++ ret = copy_to_iter(priv->buf, priv->actual, &priv->to); ++ kthread_unuse_mm(mm); ++ mmput(mm); ++ if (!ret) ++ ret = -EFAULT; ++ } else { + ret = -EFAULT; ++ } ++ mmdrop(mm); + + /* completing the iocb can drop the ctx and mm, don't touch mm after */ + iocb->ki_complete(iocb, ret); +@@ -501,6 +507,7 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) + * complete the aio request immediately. + */ + if (priv->to_free == NULL || unlikely(req->actual == 0)) { ++ mmdrop(priv->mm); + kfree(req->buf); + kfree(priv->to_free); + kfree(priv); +@@ -541,6 +548,7 @@ static ssize_t ep_aio(struct kiocb *iocb, + priv->epdata = epdata; + priv->actual = 0; + priv->mm = current->mm; /* mm teardown waits for iocbs in exit_aio() */ ++ mmgrab(priv->mm); + + /* each kiocb is coupled to one usb_request, but we can't + * allocate or submit those if the host disconnected. +@@ -570,6 +578,7 @@ static ssize_t ep_aio(struct kiocb *iocb, + + fail: + spin_unlock_irq(&epdata->dev->lock); ++ mmdrop(priv->mm); + kfree(priv->to_free); + kfree(priv); + put_ep(epdata); +diff --git a/drivers/usb/gadget/legacy/nokia.c b/drivers/usb/gadget/legacy/nokia.c +--- a/drivers/usb/gadget/legacy/nokia.c ++++ b/drivers/usb/gadget/legacy/nokia.c +@@ -50,7 +50,7 @@ static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS; + */ + #define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS + +-#endif /* CONFIG_USB_DEBUG */ ++#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ + + FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); + +diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig +--- a/drivers/usb/gadget/udc/Kconfig ++++ b/drivers/usb/gadget/udc/Kconfig +@@ -90,7 +90,7 @@ config USB_BCM63XX_UDC + + config USB_FSL_USB2 + tristate "Freescale Highspeed USB DR Peripheral Controller" +- depends on FSL_SOC ++ depends on FSL_SOC || COMPILE_TEST + help + Some of Freescale PowerPC and i.MX processors have a High Speed + Dual-Role(DR) USB controller, which supports device mode. +diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c +--- a/drivers/usb/gadget/udc/fsl_qe_udc.c ++++ b/drivers/usb/gadget/udc/fsl_qe_udc.c +@@ -1945,6 +1945,7 @@ static void ch9getstatus(struct qe_udc *udc, u8 request_type, u16 value, + u16 index, u16 length) + { + u16 usb_status = 0; ++ struct usb_request *usb_req; + struct qe_req *req; + struct qe_ep *ep; + int status = 0; +@@ -1983,8 +1984,11 @@ static void ch9getstatus(struct qe_udc *udc, u8 request_type, u16 value, + } + } + +- req = container_of(qe_alloc_request(&ep->ep, GFP_KERNEL), +- struct qe_req, req); ++ usb_req = qe_alloc_request(&ep->ep, GFP_KERNEL); ++ if (!usb_req) ++ goto stall; ++ ++ req = container_of(usb_req, struct qe_req, req); + req->req.length = 2; + req->req.buf = udc->statusbuf; + *(u16 *)req->req.buf = cpu_to_le16(usb_status); +diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c +--- a/drivers/usb/gadget/udc/pch_udc.c ++++ b/drivers/usb/gadget/udc/pch_udc.c +@@ -320,7 +320,6 @@ struct pch_vbus_gpio_data { + * @lock: protects all state + * @stall: stall requested + * @prot_stall: protcol stall requested +- * @registered: driver registered with system + * @suspended: driver in suspended state + * @connected: gadget driver associated + * @vbus_session: required vbus_session state +diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c +--- a/drivers/usb/host/fsl-mph-dr-of.c ++++ b/drivers/usb/host/fsl-mph-dr-of.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + + struct fsl_usb2_dev_data { + char *dr_mode; /* controller mode */ +@@ -183,7 +184,7 @@ static int fsl_usb2_mph_dr_of_probe(struct platform_device *ofdev) + const struct of_device_id *match; + const unsigned char *prop; + static unsigned int idx; +- int i; ++ int i, err; + + if (!of_device_is_available(np)) + return -ENODEV; +@@ -246,6 +247,10 @@ static int fsl_usb2_mph_dr_of_probe(struct platform_device *ofdev) + } + } + ++ err = devm_regulator_get_enable_optional(&ofdev->dev, "vbus"); ++ if (err) ++ return dev_err_probe(&ofdev->dev, err, "failed to get vbus regulator\n"); ++ + for (i = 0; i < ARRAY_SIZE(dev_data->drivers); i++) { + if (!dev_data->drivers[i]) + continue; +diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c +--- a/drivers/usb/host/ohci-dbg.c ++++ b/drivers/usb/host/ohci-dbg.c +@@ -683,7 +683,7 @@ static int fill_buffer(struct debug_buffer *buf) + int ret; + + if (!buf->page) +- buf->page = (char *)get_zeroed_page(GFP_KERNEL); ++ buf->page = kzalloc(PAGE_SIZE, GFP_KERNEL); + + if (!buf->page) { + ret = -ENOMEM; +@@ -729,11 +729,8 @@ static int debug_close(struct inode *inode, struct file *file) + { + struct debug_buffer *buf = file->private_data; + +- if (buf) { +- if (buf->page) +- free_page((unsigned long)buf->page); +- kfree(buf); +- } ++ kfree(buf->page); ++ kfree(buf); + + return 0; + } +diff --git a/drivers/usb/host/xhci-caps.h b/drivers/usb/host/xhci-caps.h +--- a/drivers/usb/host/xhci-caps.h ++++ b/drivers/usb/host/xhci-caps.h +@@ -5,22 +5,23 @@ + */ + + #include ++#include + + /* hc_capbase - bitmasks */ + /* bits 7:0 - Capability Registers Length */ +-#define HC_LENGTH(p) ((p) & 0xff) ++#define HC_LENGTH GENMASK(7, 0) + /* bits 15:8 - Rsvd */ + /* bits 31:16 - Host Controller Interface Version Number */ +-#define HC_VERSION(p) (((p) >> 16) & 0xffff) ++#define HC_VERSION GENMASK(31, 16) + + /* HCSPARAMS1 - hcs_params1 - bitmasks */ + /* bits 7:0 - Number of Device Slots */ +-#define HCS_MAX_SLOTS(p) (((p) >> 0) & 0xff) +-#define HCS_SLOTS_MASK 0xff +-/* bits 18:8 - Number of Interrupters, max values is 1024 */ +-#define HCS_MAX_INTRS(p) (((p) >> 8) & 0x7ff) +-/* bits 31:24, Max Ports - max value is 255 */ +-#define HCS_MAX_PORTS(p) (((p) >> 24) & 0xff) ++#define HCS_SLOTS_MASK GENMASK(7, 0) ++/* bits 18:8 - Number of Interrupters, max values is 1024 */ ++#define HCS_MAX_INTRS GENMASK(18, 8) ++/* bits 23:19 - Rsvd */ ++/* bits 31:24 - Max Ports, max values is 255 */ ++#define HCS_MAX_PORTS GENMASK(31, 24) + + /* HCSPARAMS2 - hcs_params2 - bitmasks */ + /* +@@ -33,24 +34,25 @@ + * Note: 1 Frame = 8 Microframes + * xHCI specification section 5.3.4. + */ +-#define HCS_IST_VALUE(p) ((p) & 0x7) ++#define HCS_IST_VALUE GENMASK(2, 0) + #define HCS_IST_UNIT BIT(3) + /* bits 7:4 - Event Ring Segment Table Max, 2^(n) */ +-#define HCS_ERST_MAX(p) (((p) >> 4) & 0xf) ++#define HCS_ERST_MAX GENMASK(7, 4) + /* bits 20:8 - Rsvd */ + /* bits 25:21 - Max Scratchpad Buffers (Hi), 5 Most significant bits */ +-#define HCS_MAX_SP_HI(p) (((p) >> 21) & 0x1f) ++#define HCS_MAX_SP_HI GENMASK(25, 21) + /* bit 26 - Scratchpad restore, for save/restore HW state */ + /* bits 31:27 - Max Scratchpad Buffers (Lo), 5 Least significant bits */ +-#define HCS_MAX_SP_LO(p) (((p) >> 27) & 0x1f) +-#define HCS_MAX_SCRATCHPAD(p) (HCS_MAX_SP_HI(p) << 5 | HCS_MAX_SP_LO(p)) ++#define HCS_MAX_SP_LO GENMASK(31, 27) ++#define HCS_MAX_SCRATCHPAD(p) (FIELD_GET(HCS_MAX_SP_HI, (p)) << 5 | \ ++ FIELD_GET(HCS_MAX_SP_LO, (p))) + + /* HCSPARAMS3 - hcs_params3 - bitmasks */ + /* bits 7:0 - U1 Device Exit Latency, Max U1 to U0 latency for the roothub ports */ +-#define HCS_U1_LATENCY(p) (((p) >> 0) & 0xff) ++#define HCS_U1_LATENCY GENMASK(7, 0) + /* bits 15:8 - Rsvd */ + /* bits 31:16 - U2 Device Exit Latency, Max U2 to U0 latency for the roothub ports */ +-#define HCS_U2_LATENCY(p) (((p) >> 16) & 0xffff) ++#define HCS_U2_LATENCY GENMASK(31, 16) + + /* HCCPARAMS1 - hcc_params - bitmasks */ + /* bit 0 - 64-bit Addressing Capability */ +@@ -77,19 +79,20 @@ + /* bit 11 - Contiguous Frame ID Capability */ + #define HCC_CFC BIT(11) + /* bits 15:12 - Max size for Primary Stream Arrays, 2^(n+1) */ +-#define HCC_MAX_PSA(p) (1 << ((((p) >> 12) & 0xf) + 1)) ++#define HCC_MAX_PSA GENMASK(15, 12) ++#define GET_MAX_PSA_SIZE(p) (1 << (FIELD_GET(HCC_MAX_PSA, (p)) + 1)) + /* bits 31:16 - xHCI Extended Capabilities Pointer, from PCI base: 2^(n) */ +-#define HCC_EXT_CAPS(p) (((p) >> 16) & 0xffff) ++#define HCC_EXT_CAPS GENMASK(31, 16) + + /* DBOFF - db_off - bitmasks */ + /* bits 1:0 - Rsvd */ + /* bits 31:2 - Doorbell Array Offset */ +-#define DBOFF_MASK (0xfffffffc) ++#define DBOFF_MASK GENMASK(31, 2) + + /* RTSOFF - run_regs_off - bitmasks */ + /* bits 4:0 - Rsvd */ + /* bits 31:5 - Runtime Register Space Offse */ +-#define RTSOFF_MASK (~0x1f) ++#define RTSOFF_MASK GENMASK(31, 5) + + /* HCCPARAMS2 - hcc_params2 - bitmasks */ + /* bit 0 - U3 Entry Capability */ +diff --git a/drivers/usb/host/xhci-dbgtty.c b/drivers/usb/host/xhci-dbgtty.c +--- a/drivers/usb/host/xhci-dbgtty.c ++++ b/drivers/usb/host/xhci-dbgtty.c +@@ -665,11 +665,11 @@ int dbc_tty_init(void) + + void dbc_tty_exit(void) + { +- if (dbc_tty_driver) { +- tty_unregister_driver(dbc_tty_driver); +- tty_driver_kref_put(dbc_tty_driver); +- dbc_tty_driver = NULL; +- } ++ if (IS_ERR_OR_NULL(dbc_tty_driver)) ++ return; + ++ tty_unregister_driver(dbc_tty_driver); ++ tty_driver_kref_put(dbc_tty_driver); + idr_destroy(&dbc_tty_minors); ++ dbc_tty_driver = NULL; + } +diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c +--- a/drivers/usb/host/xhci-debugfs.c ++++ b/drivers/usb/host/xhci-debugfs.c +@@ -9,6 +9,7 @@ + + #include + #include ++#include + + #include "xhci.h" + #include "xhci-debugfs.h" +@@ -791,7 +792,7 @@ void xhci_debugfs_init(struct xhci_hcd *xhci) + xhci->debugfs_root, "reg-cap"); + + xhci_debugfs_regset(xhci, +- HC_LENGTH(readl(&xhci->cap_regs->hc_capbase)), ++ FIELD_GET(HC_LENGTH, readl(&xhci->cap_regs->hc_capbase)), + xhci_op_regs, ARRAY_SIZE(xhci_op_regs), + xhci->debugfs_root, "reg-op"); + +diff --git a/drivers/usb/host/xhci-histb.c b/drivers/usb/host/xhci-histb.c +--- a/drivers/usb/host/xhci-histb.c ++++ b/drivers/usb/host/xhci-histb.c +@@ -276,7 +276,7 @@ static int xhci_histb_probe(struct platform_device *pdev) + if (ret) + goto put_usb3_hcd; + +- if (HCC_MAX_PSA(xhci->hcc_params) >= 4) ++ if (GET_MAX_PSA_SIZE(xhci->hcc_params) >= 4) + xhci->shared_hcd->can_do_streams = 1; + + ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED); +diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c +--- a/drivers/usb/host/xhci-hub.c ++++ b/drivers/usb/host/xhci-hub.c +@@ -115,8 +115,8 @@ static int xhci_create_usb3x_bos_desc(struct xhci_hcd *xhci, char *buf, + + if ((xhci->quirks & XHCI_LPM_SUPPORT)) { + reg = readl(&xhci->cap_regs->hcs_params3); +- ss_cap->bU1devExitLat = HCS_U1_LATENCY(reg); +- ss_cap->bU2DevExitLat = cpu_to_le16(HCS_U2_LATENCY(reg)); ++ ss_cap->bU1devExitLat = FIELD_GET(HCS_U1_LATENCY, reg); ++ ss_cap->bU2DevExitLat = cpu_to_le16(FIELD_GET(HCS_U2_LATENCY, reg)); + } + + if (wLength < le16_to_cpu(bos->wTotalLength)) +@@ -1295,7 +1295,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + } + /* In spec software should not attempt to suspend + * a port unless the port reports that it is in the +- * enabled (PED = ‘1’,PLS < ‘3’) state. ++ * enabled (PED = '1',PLS < '3') state. + */ + portsc = xhci_portsc_readl(port); + if ((portsc & PORT_PE) == 0 || (portsc & PORT_RESET) || +diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c +--- a/drivers/usb/host/xhci-mem.c ++++ b/drivers/usb/host/xhci-mem.c +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + + #include "xhci.h" + #include "xhci-trace.h" +@@ -1001,7 +1002,6 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, + for (i = 0; i < 31; i++) { + dev->eps[i].ep_index = i; + dev->eps[i].vdev = dev; +- dev->eps[i].xhci = xhci; + INIT_LIST_HEAD(&dev->eps[i].cancelled_td_list); + INIT_LIST_HEAD(&dev->eps[i].bw_endpoint_list); + } +@@ -1493,6 +1493,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, + return -ENOMEM; + + virt_dev->eps[ep_index].skip = false; ++ virt_dev->eps[ep_index].next_uframe = -1; + ep_ring = virt_dev->eps[ep_index].new_ring; + xhci_ring_init(xhci, ep_ring); + +@@ -2083,7 +2084,7 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, + addr, port_offset, port_count, major_revision); + /* Port count includes the current port offset */ + if (port_offset == 0 || (port_offset + port_count - 1) > num_ports) +- /* WTF? "Valid values are ‘1’ to MaxPorts" */ ++ /* WTF? "Valid values are '1' to MaxPorts" */ + return; + + port_cap = &xhci->port_caps[xhci->num_port_caps++]; +@@ -2300,7 +2301,7 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int segs, gfp_t flags) + if (!segs) + segs = ERST_DEFAULT_SEGS; + +- max_segs = BIT(HCS_ERST_MAX(xhci->hcs_params2)); ++ max_segs = FIELD_GET(HCS_ERST_MAX, xhci->hcs_params2) << 2; + segs = min(segs, max_segs); + + ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev)); +diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c +--- a/drivers/usb/host/xhci-mtk.c ++++ b/drivers/usb/host/xhci-mtk.c +@@ -468,7 +468,7 @@ static void xhci_mtk_quirks(struct device *dev, struct xhci_hcd *xhci) + * MTK xHCI 0.96: PSA is 1 by default even if doesn't support stream, + * and it's 3 when support it. + */ +- if (xhci->hci_version < 0x100 && HCC_MAX_PSA(xhci->hcc_params) == 4) ++ if (xhci->hci_version < 0x100 && GET_MAX_PSA_SIZE(xhci->hcc_params) == 4) + xhci->quirks |= XHCI_BROKEN_STREAMS; + } + +@@ -650,7 +650,7 @@ static int xhci_mtk_probe(struct platform_device *pdev) + } + + usb3_hcd = xhci_get_usb3_hcd(xhci); +- if (usb3_hcd && HCC_MAX_PSA(xhci->hcc_params) >= 4 && ++ if (usb3_hcd && GET_MAX_PSA_SIZE(xhci->hcc_params) >= 4 && + !(xhci->quirks & XHCI_BROKEN_STREAMS)) + usb3_hcd->can_do_streams = 1; + +diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c +--- a/drivers/usb/host/xhci-pci.c ++++ b/drivers/usb/host/xhci-pci.c +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + + #include "xhci.h" + #include "xhci-trace.h" +@@ -662,7 +663,8 @@ int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id) + } + + usb3_hcd = xhci_get_usb3_hcd(xhci); +- if (usb3_hcd && !(xhci->quirks & XHCI_BROKEN_STREAMS) && HCC_MAX_PSA(xhci->hcc_params) >= 4) ++ if (usb3_hcd && !(xhci->quirks & XHCI_BROKEN_STREAMS) && ++ GET_MAX_PSA_SIZE(xhci->hcc_params) >= 4) + usb3_hcd->can_do_streams = 1; + + /* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */ +diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c +--- a/drivers/usb/host/xhci-plat.c ++++ b/drivers/usb/host/xhci-plat.c +@@ -340,7 +340,7 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s + } + + usb3_hcd = xhci_get_usb3_hcd(xhci); +- if (usb3_hcd && HCC_MAX_PSA(xhci->hcc_params) >= 4 && ++ if (usb3_hcd && GET_MAX_PSA_SIZE(xhci->hcc_params) >= 4 && + !(xhci->quirks & XHCI_BROKEN_STREAMS)) + usb3_hcd->can_do_streams = 1; + +diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -57,6 +57,8 @@ + #include + #include + #include ++#include ++ + #include "xhci.h" + #include "xhci-trace.h" + +@@ -561,8 +563,8 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, + * pointer command pending because the device can choose to start any + * stream once the endpoint is on the HW schedule. + */ +- if ((ep_state & EP_STOP_CMD_PENDING) || (ep_state & SET_DEQ_PENDING) || +- (ep_state & EP_HALTED) || (ep_state & EP_CLEARING_TT)) ++ if (ep_state & (EP_STOP_CMD_PENDING | SET_DEQ_PENDING | EP_HALTED | ++ EP_CLEARING_TT | EP_DROP_PENDING)) + return; + + trace_xhci_ring_ep_doorbell(slot_id, DB_VALUE(ep_index, stream_id)); +@@ -573,9 +575,8 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, + } + + /* Ring the doorbell for any rings with pending URBs */ +-static void ring_doorbell_for_active_rings(struct xhci_hcd *xhci, +- unsigned int slot_id, +- unsigned int ep_index) ++void xhci_ring_doorbell_for_active_rings(struct xhci_hcd *xhci, unsigned int slot_id, ++ unsigned int ep_index) + { + unsigned int stream_id; + struct xhci_virt_ep *ep; +@@ -598,13 +599,6 @@ static void ring_doorbell_for_active_rings(struct xhci_hcd *xhci, + } + } + +-void xhci_ring_doorbell_for_active_rings(struct xhci_hcd *xhci, +- unsigned int slot_id, +- unsigned int ep_index) +-{ +- ring_doorbell_for_active_rings(xhci, slot_id, ep_index); +-} +- + static struct xhci_virt_ep *xhci_get_virt_ep(struct xhci_hcd *xhci, + unsigned int slot_id, + unsigned int ep_index) +@@ -686,112 +680,100 @@ static u64 xhci_get_hw_deq(struct xhci_hcd *xhci, struct xhci_virt_device *vdev, + return le64_to_cpu(ep_ctx->deq); + } + +-static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, +- unsigned int slot_id, unsigned int ep_index, +- unsigned int stream_id, struct xhci_td *td) ++/* ++ * Move the endpoint dequeue pointer to the next queued TD on ring->td_list or ++ * to enqueue if no TDs are queued (empty ring) ++ * All cancelled TDs on ring->td_list should be moved to ep->cancelled_td_list ++ * before calling this function ++ */ ++static int xhci_move_deq_to_next_td(struct xhci_hcd *xhci, ++ struct xhci_virt_ep *ep, ++ unsigned int stream_id) + { +- struct xhci_virt_device *dev = xhci->devs[slot_id]; +- struct xhci_virt_ep *ep = &dev->eps[ep_index]; +- struct xhci_ring *ep_ring; + struct xhci_command *cmd; +- struct xhci_segment *new_seg; +- union xhci_trb *new_deq; +- int new_cycle; ++ struct xhci_ring *ring; ++ struct xhci_td *td; + dma_addr_t addr; +- u64 hw_dequeue; +- bool hw_dequeue_found = false; +- bool td_last_trb_found = false; ++ int new_cycle; + u32 trb_sct = 0; +- int ret; ++ int ret = 0; + +- ep_ring = xhci_triad_to_transfer_ring(xhci, slot_id, +- ep_index, stream_id); +- if (!ep_ring) { ++ ring = xhci_virt_ep_to_ring(xhci, ep, stream_id); ++ if (!ring) { + xhci_warn(xhci, "WARN can't find new dequeue, invalid stream ID %u\n", + stream_id); + return -ENODEV; + } + +- hw_dequeue = xhci_get_hw_deq(xhci, dev, ep_index, stream_id) & TR_DEQ_PTR_MASK; +- new_seg = ep_ring->deq_seg; +- new_deq = ep_ring->dequeue; +- new_cycle = le32_to_cpu(td->end_trb->generic.field[3]) & TRB_CYCLE; +- +- /* +- * Walk the ring until both the next TRB and hw_dequeue are found (don't +- * move hw_dequeue back if it went forward due to a HW bug). Cycle state +- * is loaded from a known good TRB, track later toggles to maintain it. +- */ +- do { +- if (!hw_dequeue_found && xhci_trb_virt_to_dma(new_seg, new_deq) +- == (dma_addr_t)hw_dequeue) { +- hw_dequeue_found = true; +- if (td_last_trb_found) +- break; +- } +- if (new_deq == td->end_trb) +- td_last_trb_found = true; +- +- if (td_last_trb_found && trb_is_link(new_deq) && +- link_trb_toggles_cycle(new_deq)) +- new_cycle ^= 0x1; +- +- next_trb(&new_seg, &new_deq); +- +- /* Search wrapped around, bail out */ +- if (new_deq == ep->ring->dequeue) { +- xhci_err(xhci, "Error: Failed finding new dequeue state\n"); +- return -EINVAL; +- } +- +- } while (!hw_dequeue_found || !td_last_trb_found); +- +- /* Don't update the ring cycle state for the producer (us). */ +- addr = xhci_trb_virt_to_dma(new_seg, new_deq); +- if (addr == 0) { +- xhci_warn(xhci, "Can't find dma of new dequeue ptr\n"); +- xhci_warn(xhci, "deq seg = %p, deq ptr = %p\n", new_seg, new_deq); +- return -EINVAL; +- } +- + if ((ep->ep_state & SET_DEQ_PENDING)) { +- xhci_warn(xhci, "Set TR Deq already pending, don't submit for %pad\n", +- &addr); ++ xhci_warn(xhci, "Set TR Deq already pending\n"); + return -EBUSY; + } + + /* This function gets called from contexts where it cannot sleep */ + cmd = xhci_alloc_command(xhci, false, GFP_ATOMIC); + if (!cmd) { +- xhci_warn(xhci, "Can't alloc Set TR Deq cmd %pad\n", &addr); ++ xhci_warn(xhci, "Can't alloc Set TR Deq cmd\n"); + return -ENOMEM; + } + ++ /* ++ * Move dequeue to the beginning of next td, or to enqueue if ring is ++ * empty. Avoid moving dequeue to a link trb (empty ring) as it causes ++ * issues on some hosts. In that case advance the enqueue to next segment ++ * before moving dequeue to it ++ */ ++ ++ if (list_empty(&ring->td_list)) { ++ if (trb_is_link(ring->enqueue)) ++ inc_enq_past_link(xhci, ring, 0); ++ ep->queued_deq_seg = ring->enq_seg; ++ ep->queued_deq_ptr = ring->enqueue; ++ new_cycle = ring->cycle_state; ++ } else { ++ td = list_first_entry(&ring->td_list, struct xhci_td, td_list); ++ ep->queued_deq_seg = td->start_seg; ++ ep->queued_deq_ptr = td->start_trb; ++ new_cycle = le32_to_cpu(td->start_trb->generic.field[3]) & TRB_CYCLE; ++ } ++ ++ addr = xhci_trb_virt_to_dma(ep->queued_deq_seg, ep->queued_deq_ptr); ++ if (addr == 0) { ++ xhci_warn(xhci, "Can't find new dequeue dma of seg %p, ptr %p\n", ++ ep->queued_deq_seg, ep->queued_deq_ptr); ++ ret = -EINVAL; ++ goto err_out; ++ } ++ + if (stream_id) + trb_sct = SCT_FOR_TRB(SCT_PRI_TR); + ret = queue_command(xhci, cmd, + lower_32_bits(addr) | trb_sct | new_cycle, + upper_32_bits(addr), +- STREAM_ID_FOR_TRB(stream_id), SLOT_ID_FOR_TRB(slot_id) | +- EP_INDEX_FOR_TRB(ep_index) | TRB_TYPE(TRB_SET_DEQ), false); +- if (ret < 0) { +- xhci_free_command(xhci, cmd); +- return ret; +- } +- ep->queued_deq_seg = new_seg; +- ep->queued_deq_ptr = new_deq; ++ STREAM_ID_FOR_TRB(stream_id), SLOT_ID_FOR_TRB(ep->vdev->slot_id) | ++ EP_INDEX_FOR_TRB(ep->ep_index) | TRB_TYPE(TRB_SET_DEQ), false); ++ if (ret < 0) ++ goto err_out; + + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Set TR Deq ptr 0x%llx, cycle %u\n", addr, new_cycle); + +- /* Stop the TD queueing code from ringing the doorbell until +- * this command completes. The HC won't set the dequeue pointer +- * if the ring is running, and ringing the doorbell starts the +- * ring running. ++ /* ++ * Stop the TD queueing code from ringing the doorbell until this ++ * command completes. The HC won't set the dequeue pointer if the ring ++ * is running, and ringing the doorbell starts the ring. + */ + ep->ep_state |= SET_DEQ_PENDING; + xhci_ring_cmd_db(xhci); ++ + return 0; ++ ++err_out: ++ xhci_free_command(xhci, cmd); ++ ep->queued_deq_seg = NULL; ++ ep->queued_deq_ptr = NULL; ++ ++ return ret; + } + + /* flip_cycle means flip the cycle bit of all but the first and last TRB. +@@ -950,7 +932,7 @@ static void xhci_dequeue_td(struct xhci_hcd *xhci, struct xhci_td *td, struct xh + } + + /* Complete the cancelled URBs we unlinked from td_list. */ +-static void xhci_giveback_invalidated_tds(struct xhci_virt_ep *ep) ++static void xhci_giveback_invalidated_tds(struct xhci_hcd *xhci, struct xhci_virt_ep *ep) + { + struct xhci_ring *ring; + struct xhci_td *td, *tmp_td; +@@ -958,17 +940,17 @@ static void xhci_giveback_invalidated_tds(struct xhci_virt_ep *ep) + list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, + cancelled_td_list) { + +- ring = xhci_urb_to_transfer_ring(ep->xhci, td->urb); ++ ring = xhci_urb_to_transfer_ring(xhci, td->urb); + + if (td->cancel_status == TD_CLEARED) { +- xhci_dbg(ep->xhci, "%s: Giveback cancelled URB %p TD\n", ++ xhci_dbg(xhci, "%s: Giveback cancelled URB %p TD\n", + __func__, td->urb); +- xhci_td_cleanup(ep->xhci, td, ring, td->status); ++ xhci_td_cleanup(xhci, td, ring, td->status); + } else { +- xhci_dbg(ep->xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n", ++ xhci_dbg(xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n", + __func__, td->urb, td->cancel_status); + } +- if (ep->xhci->xhc_state & XHCI_STATE_DYING) ++ if (xhci->xhc_state & XHCI_STATE_DYING) + return; + } + } +@@ -1002,15 +984,19 @@ static int xhci_handle_halted_endpoint(struct xhci_hcd *xhci, + struct xhci_td *td, + enum xhci_ep_reset_type reset_type) + { ++ struct xhci_port *rhub_port = ep->vdev->rhub_port; + unsigned int slot_id = ep->vdev->slot_id; + int err; + + /* +- * Avoid resetting endpoint if link is inactive. Can cause host hang. +- * Device will be reset soon to recover the link so don't do anything ++ * Avoid resetting endpoint if link is inactive or device disonnected. ++ * Can cause host hang. ++ * Device will be reset to recover an inactive link, so don't do anything + */ +- if (ep->vdev->flags & VDEV_PORT_ERROR) ++ if (rhub_port->link_inactive || !rhub_port->connected) { ++ ep->ep_state |= EP_DROP_PENDING; + return -ENODEV; ++ } + + /* add td to cancelled list and let reset ep handler take care of it */ + if (reset_type == EP_HARD_RESET) { +@@ -1047,15 +1033,13 @@ static int xhci_handle_halted_endpoint(struct xhci_hcd *xhci, + * only call this when ring is not in a running state + */ + +-static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) ++static int xhci_invalidate_cancelled_tds(struct xhci_hcd *xhci, struct xhci_virt_ep *ep) + { +- struct xhci_hcd *xhci; + struct xhci_td *td = NULL; + struct xhci_td *tmp_td = NULL; + struct xhci_td *cached_td = NULL; + struct xhci_ring *ring; + u64 hw_deq; +- unsigned int slot_id = ep->vdev->slot_id; + int err; + + /* +@@ -1065,8 +1049,6 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) + if (ep->ep_state & SET_DEQ_PENDING) + return 0; + +- xhci = ep->xhci; +- + list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, cancelled_td_list) { + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Removing canceled TD starting at 0x%llx (dma) in stream %u URB %p", +@@ -1080,6 +1062,13 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) + td->urb, td->urb->stream_id); + continue; + } ++ ++ /* device disconnected or link error, ep will be dropped */ ++ if (ep->ep_state & EP_DROP_PENDING) { ++ td->cancel_status = TD_CLEARED; ++ continue; ++ } ++ + /* + * If a ring stopped on the TD we need to cancel then we have to + * move the xHC endpoint ring dequeue pointer past this TD. +@@ -1131,9 +1120,8 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) + if (!cached_td) + return 0; + +- err = xhci_move_dequeue_past_td(xhci, slot_id, ep->ep_index, +- cached_td->urb->stream_id, +- cached_td); ++ err = xhci_move_deq_to_next_td(xhci, ep, cached_td->urb->stream_id); ++ + if (err) { + /* Failed to move past cached td, just set cached TDs to no-op */ + list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, cancelled_td_list) { +@@ -1161,23 +1149,23 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) + * + * Call under xhci->lock on a stopped endpoint. + */ +-void xhci_process_cancelled_tds(struct xhci_virt_ep *ep) ++void xhci_process_cancelled_tds(struct xhci_hcd *xhci, struct xhci_virt_ep *ep) + { +- xhci_invalidate_cancelled_tds(ep); +- xhci_giveback_invalidated_tds(ep); ++ xhci_invalidate_cancelled_tds(xhci, ep); ++ xhci_giveback_invalidated_tds(xhci, ep); + } + + /* + * Returns the TD the endpoint ring halted on. + * Only call for non-running rings without streams. + */ +-static struct xhci_td *find_halted_td(struct xhci_virt_ep *ep) ++static struct xhci_td *find_halted_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep) + { + struct xhci_td *td; + u64 hw_deq; + + if (!list_empty(&ep->ring->td_list)) { /* Not streams compatible */ +- hw_deq = xhci_get_hw_deq(ep->xhci, ep->vdev, ep->ep_index, 0); ++ hw_deq = xhci_get_hw_deq(xhci, ep->vdev, ep->ep_index, 0); + hw_deq &= TR_DEQ_PTR_MASK; + td = list_first_entry(&ep->ring->td_list, struct xhci_td, td_list); + if (trb_in_td(td, hw_deq)) +@@ -1252,7 +1240,7 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id, + reset_type = EP_SOFT_RESET; + } else { + reset_type = EP_HARD_RESET; +- td = find_halted_td(ep); ++ td = find_halted_td(xhci, ep); + if (td) + td->status = -EPROTO; + } +@@ -1310,13 +1298,17 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id, + } + } + ++ /* link is inactive or disconnected, ep is not running and shouldn't be restarted */ ++ if (ep->vdev->rhub_port->link_inactive || !ep->vdev->rhub_port->connected) ++ ep->ep_state |= EP_DROP_PENDING; ++ + /* will queue a set TR deq if stopped on a cancelled, uncleared TD */ +- xhci_invalidate_cancelled_tds(ep); ++ xhci_invalidate_cancelled_tds(xhci, ep); + ep->ep_state &= ~EP_STOP_CMD_PENDING; + + /* Otherwise ring the doorbell(s) to restart queued transfers */ +- xhci_giveback_invalidated_tds(ep); +- ring_doorbell_for_active_rings(xhci, slot_id, ep_index); ++ xhci_giveback_invalidated_tds(xhci, ep); ++ xhci_ring_doorbell_for_active_rings(xhci, slot_id, ep_index); + } + + static void xhci_kill_ring_urbs(struct xhci_hcd *xhci, struct xhci_ring *ring) +@@ -1537,14 +1529,14 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, + /* HW cached TDs cleared from cache, give them back */ + list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, + cancelled_td_list) { +- ep_ring = xhci_urb_to_transfer_ring(ep->xhci, td->urb); ++ ep_ring = xhci_urb_to_transfer_ring(xhci, td->urb); + if (td->cancel_status == TD_CLEARING_CACHE) { + td->cancel_status = TD_CLEARED; +- xhci_dbg(ep->xhci, "%s: Giveback cancelled URB %p TD\n", ++ xhci_dbg(xhci, "%s: Giveback cancelled URB %p TD\n", + __func__, td->urb); +- xhci_td_cleanup(ep->xhci, td, ep_ring, td->status); ++ xhci_td_cleanup(xhci, td, ep_ring, td->status); + } else { +- xhci_dbg(ep->xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n", ++ xhci_dbg(xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n", + __func__, td->urb, td->cancel_status); + } + } +@@ -1555,17 +1547,17 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, + + /* Check for deferred or newly cancelled TDs */ + if (!list_empty(&ep->cancelled_td_list)) { +- xhci_dbg(ep->xhci, "%s: Pending TDs to clear, continuing with invalidation\n", ++ xhci_dbg(xhci, "%s: Pending TDs to clear, continuing with invalidation\n", + __func__); +- xhci_invalidate_cancelled_tds(ep); ++ xhci_invalidate_cancelled_tds(xhci, ep); + /* Try to restart the endpoint if all is done */ +- ring_doorbell_for_active_rings(xhci, slot_id, ep_index); ++ xhci_ring_doorbell_for_active_rings(xhci, slot_id, ep_index); + /* Start giving back any TDs invalidated above */ +- xhci_giveback_invalidated_tds(ep); ++ xhci_giveback_invalidated_tds(xhci, ep); + } else { + /* Restart any rings with pending URBs */ +- xhci_dbg(ep->xhci, "%s: All TDs cleared, ring doorbell\n", __func__); +- ring_doorbell_for_active_rings(xhci, slot_id, ep_index); ++ xhci_dbg(xhci, "%s: All TDs cleared, ring doorbell\n", __func__); ++ xhci_ring_doorbell_for_active_rings(xhci, slot_id, ep_index); + } + } + +@@ -1591,16 +1583,16 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id, + "Ignoring reset ep completion code of %u", cmd_comp_code); + + /* Cleanup cancelled TDs as ep is stopped. May queue a Set TR Deq cmd */ +- xhci_invalidate_cancelled_tds(ep); ++ xhci_invalidate_cancelled_tds(xhci, ep); + + /* Clear our internal halted state */ + ep->ep_state &= ~EP_HALTED; + +- xhci_giveback_invalidated_tds(ep); ++ xhci_giveback_invalidated_tds(xhci, ep); + + /* if this was a soft reset, then restart */ + if ((le32_to_cpu(trb->generic.field[3])) & TRB_TSP) +- ring_doorbell_for_active_rings(xhci, slot_id, ep_index); ++ xhci_ring_doorbell_for_active_rings(xhci, slot_id, ep_index); + } + + static void xhci_handle_cmd_enable_slot(int slot_id, struct xhci_command *command, +@@ -2008,13 +2000,15 @@ static void xhci_cavium_reset_phy_quirk(struct xhci_hcd *xhci) + static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) + { + struct xhci_virt_device *vdev = NULL; +- struct usb_hcd *hcd; +- u32 port_id; +- u32 portsc, cmd_reg; +- unsigned int hcd_portnum; + struct xhci_bus_state *bus_state; +- bool bogus_port_status = false; + struct xhci_port *port; ++ struct usb_hcd *hcd; ++ bool bogus_port_status = false; ++ unsigned int hcd_portnum; ++ u32 cmd_reg; ++ u32 port_id; ++ u32 portsc; ++ u32 pls; + + /* Port status change events always have a successful completion code */ + if (GET_COMP_CODE(le32_to_cpu(event->generic.field[2])) != COMP_SUCCESS) +@@ -2051,6 +2045,7 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) + bus_state = &port->rhub->bus_state; + hcd_portnum = port->hcd_portnum; + portsc = xhci_portsc_readl(port); ++ pls = portsc & PORT_PLS_MASK; + + xhci_dbg(xhci, "Port change event, %d-%d, id %d, portsc: 0x%x\n", + hcd->self.busnum, hcd_portnum + 1, port_id, portsc); +@@ -2062,11 +2057,13 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) + usb_hcd_resume_root_hub(hcd); + } + +- if (vdev && (portsc & PORT_PLS_MASK) == XDEV_INACTIVE) { +- if (!(portsc & PORT_RESET)) +- vdev->flags |= VDEV_PORT_ERROR; +- } else if (vdev && portsc & PORT_RC) { +- vdev->flags &= ~VDEV_PORT_ERROR; ++ /* ++ * Tag broken links to avoid retries while hub driver sorts it out. ++ * Link status is not relible while port is in reset. ++ */ ++ if (!(portsc & PORT_RESET)) { ++ port->link_inactive = (pls == XDEV_INACTIVE); ++ port->connected = !!(portsc & PORT_CONNECT); + } + + if ((portsc & PORT_PLC) && (portsc & PORT_PLS_MASK) == XDEV_RESUME) { +@@ -3978,88 +3975,102 @@ static unsigned int xhci_get_last_burst_packet_count(struct xhci_hcd *xhci, + /* Returns the Isochronous Scheduling Threshold in Microframes. 1 Frame is 8 Microframes. */ + static int xhci_ist_microframes(struct xhci_hcd *xhci) + { +- int ist = HCS_IST_VALUE(xhci->hcs_params2); ++ int ist = FIELD_GET(HCS_IST_VALUE, xhci->hcs_params2); + + if (xhci->hcs_params2 & HCS_IST_UNIT) + ist *= 8; + return ist; + } + +-/* +- * Calculates Frame ID field of the isochronous TRB identifies the +- * target frame that the Interval associated with this Isochronous +- * Transfer Descriptor will start on. Refer to 4.11.2.5 in 1.1 spec. +- * +- * Returns actual frame id on success, negative value on error. +- */ +-static int xhci_get_isoc_frame_id(struct xhci_hcd *xhci, +- struct urb *urb, int index) ++ ++static bool xhci_isoc_td_uses_frame_id(struct xhci_hcd *xhci, struct urb *urb, ++ struct xhci_virt_ep *ep, int i) + { +- int start_frame, ist, ret = 0; +- int start_frame_id, end_frame_id, current_frame_id; ++ if (urb->transfer_flags & URB_ISO_ASAP) ++ return false; + +- if (urb->dev->speed == USB_SPEED_LOW || +- urb->dev->speed == USB_SPEED_FULL) +- start_frame = urb->start_frame + index * urb->interval; +- else +- start_frame = (urb->start_frame + index * urb->interval) >> 3; ++ if (xhci->hcc_params & HCC_CFC) ++ return true; + ++ /* set frame id for first TD of first URB in stream */ ++ if (ep->next_uframe == -1 && i == 0) ++ return true; ++ ++ return false; ++} ++ ++/* ++ * Check if frame is in the valid frame window, including start and end. ++ * If start > end then assume window wrapped around at a limit the frame ++ * value won't exceed. ++ */ ++static bool xhci_frame_in_range(u32 frame, u32 start, u32 end) ++{ ++ /* frame window end wrapped around */ ++ if (start > end) ++ return frame >= start || frame <= end; ++ ++ return frame >= start && frame <= end; ++} ++ ++/* ++ * Set the urb->start_frame of the URB. ++ * ++ * Returns microframe index of first TD ++ */ ++static int xhci_get_isoc_start_frame(struct xhci_hcd *xhci, struct urb *urb, ++ struct xhci_virt_ep *ep) ++{ ++ u32 curr_frame, start_uframe; ++ u32 urb_start, urb_end; ++ u32 win_start, win_end; ++ bool frame_unit; ++ int uinterval; ++ u32 mfindex; ++ int ist; ++ ++ /* check if urb uses frame units instead of microframes */ ++ frame_unit = (urb->dev->speed == USB_SPEED_FULL || ++ urb->dev->speed == USB_SPEED_LOW); ++ ++ uinterval = urb->interval; ++ if (frame_unit) ++ uinterval *= 8; ++ ++ /* get current microframe index and isoc scheduling threshold */ ++ mfindex = readl(&xhci->run_regs->microframe_index); + ist = xhci_ist_microframes(xhci); + +- /* Software shall not schedule an Isoch TD with a Frame ID value that +- * is less than the Start Frame ID or greater than the End Frame ID, +- * where: +- * +- * End Frame ID = (Current MFINDEX register value + 895 ms.) MOD 2048 +- * Start Frame ID = (Current MFINDEX register value + IST + 1) MOD 2048 +- * +- * Both the End Frame ID and Start Frame ID values are calculated +- * in microframes. When software determines the valid Frame ID value; +- * The End Frame ID value should be rounded down to the nearest Frame +- * boundary, and the Start Frame ID value should be rounded up to the +- * nearest Frame boundary. +- */ +- current_frame_id = readl(&xhci->run_regs->microframe_index); +- start_frame_id = roundup(current_frame_id + ist + 1, 8); +- end_frame_id = rounddown(current_frame_id + 895 * 8, 8); ++ /* calculate valid frame window, in frame units, see xhci 4.11.2.5 */ ++ curr_frame = MFINDEX_TO_FRAME(mfindex); ++ win_start = (curr_frame + DIV_ROUND_UP_POW2(ist, 8) + 1) % MAX_FRAMES; ++ win_end = (curr_frame + 895) % MAX_FRAMES; + +- start_frame &= 0x7ff; +- start_frame_id = (start_frame_id >> 3) & 0x7ff; +- end_frame_id = (end_frame_id >> 3) & 0x7ff; +- +- if (start_frame_id < end_frame_id) { +- if (start_frame > end_frame_id || +- start_frame < start_frame_id) +- ret = -EINVAL; +- } else if (start_frame_id > end_frame_id) { +- if ((start_frame > end_frame_id && +- start_frame < start_frame_id)) +- ret = -EINVAL; ++ /* Is this the first URB starting the whole isoc data flow? */ ++ if (ep->next_uframe < 0) { ++ /* align first URB to next interval boundary, or at last to full frame */ ++ start_uframe = mfindex + ist + XHCI_CFC_DELAY; ++ start_uframe = roundup(start_uframe, 8); ++ start_uframe = roundup(start_uframe, uinterval) % MAX_UFRAMES; + } else { +- ret = -EINVAL; +- } ++ /* URB is mid stream and expected to handle the next frame */ ++ start_uframe = ep->next_uframe; ++ urb_start = start_uframe / 8; ++ urb_end = (start_uframe + urb->number_of_packets * uinterval) / 8; ++ urb_end %= MAX_FRAMES; + +- if (index == 0) { +- if (ret == -EINVAL || start_frame == start_frame_id) { +- start_frame = start_frame_id + 1; +- if (urb->dev->speed == USB_SPEED_LOW || +- urb->dev->speed == USB_SPEED_FULL) +- urb->start_frame = start_frame; +- else +- urb->start_frame = start_frame << 3; +- ret = 0; +- } +- } ++ if (!xhci_frame_in_range(urb_start, win_start, win_end)) ++ xhci_dbg(xhci, "Ill-timed isoc URB %p for start frame %d, range %d-%d\n", ++ urb, urb_start, win_start, win_end); + +- if (ret) { +- xhci_warn(xhci, "Frame ID %d (reg %d, index %d) beyond range (%d, %d)\n", +- start_frame, current_frame_id, index, +- start_frame_id, end_frame_id); +- xhci_warn(xhci, "Ignore frame ID field, use SIA bit instead\n"); +- return ret; ++ if (!xhci_frame_in_range(urb_end, win_start, win_end)) ++ xhci_dbg(xhci, "Ill-timed isoc URB %p for end frame %d, range %d-%d\n", ++ urb, urb_start, win_start, win_end); + } ++ /* set urb->start_frame */ ++ urb->start_frame = frame_unit ? start_uframe / 8 : start_uframe; + +- return start_frame; ++ return start_uframe; + } + + /* Check if we should generate event interrupt for a TD in an isoc URB */ +@@ -4099,7 +4110,8 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, + int i, j; + bool more_trbs_coming; + struct xhci_virt_ep *xep; +- int frame_id; ++ int uinterval = urb->interval; ++ int start_uframe; + + xep = &xhci->devs[slot_id]->eps[ep_index]; + ep_ring = xhci->devs[slot_id]->eps[ep_index].ring; +@@ -4115,6 +4127,12 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, + start_cycle = ep_ring->cycle_state; + + urb_priv = urb->hcpriv; ++ ++ if (urb->dev->speed == USB_SPEED_FULL || urb->dev->speed == USB_SPEED_LOW) ++ uinterval = urb->interval * 8; ++ ++ start_uframe = xhci_get_isoc_start_frame(xhci, urb, xep); ++ + /* Queue the TRBs for each TD, even if they are zero-length */ + for (i = 0; i < num_tds; i++) { + unsigned int total_pkt_count, max_pkt; +@@ -4146,14 +4164,16 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, + goto cleanup; + } + td = &urb_priv->td[i]; +- /* use SIA as default, if frame id is used overwrite it */ +- sia_frame_id = TRB_SIA; +- if (!(urb->transfer_flags & URB_ISO_ASAP) && +- (xhci->hcc_params & HCC_CFC)) { +- frame_id = xhci_get_isoc_frame_id(xhci, urb, i); +- if (frame_id >= 0) +- sia_frame_id = TRB_FRAME_ID(frame_id); ++ ++ ++ /* Choose SIA or frame ID based scheduling for this TD */ ++ if (xhci_isoc_td_uses_frame_id(xhci, urb, xep, i)) { ++ sia_frame_id = (start_uframe + i * uinterval) / 8; ++ sia_frame_id = TRB_FRAME_ID(sia_frame_id % MAX_FRAMES); ++ } else { ++ sia_frame_id = TRB_SIA; + } ++ + /* + * Set isoc specific data for the first TRB in a TD. + * Prevent HW from getting the TRBs by keeping the cycle state +@@ -4232,9 +4252,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, + } + } + +- /* store the next frame id */ +- if (xhci->hcc_params & HCC_CFC) +- xep->next_frame_id = urb->start_frame + num_tds * urb->interval; ++ xep->next_uframe = (start_uframe + num_tds * uinterval) % MAX_UFRAMES; + + if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs == 0) { + if (xhci->quirks & XHCI_AMD_PLL_FIX) +@@ -4281,11 +4299,9 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, + struct xhci_virt_device *xdev; + struct xhci_ring *ep_ring; + struct xhci_ep_ctx *ep_ctx; +- int start_frame; ++ struct xhci_virt_ep *xep; + int num_tds, num_trbs, i; + int ret; +- struct xhci_virt_ep *xep; +- int ist; + + xdev = xhci->devs[slot_id]; + xep = &xhci->devs[slot_id]->eps[ep_index]; +@@ -4311,38 +4327,12 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, + */ + check_interval(urb, ep_ctx); + +- /* Calculate the start frame and put it in urb->start_frame. */ +- if ((xhci->hcc_params & HCC_CFC) && !list_empty(&ep_ring->td_list)) { +- if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_RUNNING) { +- urb->start_frame = xep->next_frame_id; +- goto skip_start_over; +- } +- } +- +- start_frame = readl(&xhci->run_regs->microframe_index); +- start_frame &= 0x3fff; + /* +- * Round up to the next frame and consider the time before trb really +- * gets scheduled by hardare. ++ * Check if this starts the isoc data flow. Relies on hw setting ep ctx ++ * state after doorbell ring. Consider adding list_empty(td_list) check + */ +- ist = xhci_ist_microframes(xhci); +- start_frame += ist + XHCI_CFC_DELAY; +- start_frame = roundup(start_frame, 8); +- +- /* +- * Round up to the next ESIT (Endpoint Service Interval Time) if ESIT +- * is greate than 8 microframes. +- */ +- if (urb->dev->speed == USB_SPEED_LOW || +- urb->dev->speed == USB_SPEED_FULL) { +- start_frame = roundup(start_frame, urb->interval << 3); +- urb->start_frame = start_frame >> 3; +- } else { +- start_frame = roundup(start_frame, urb->interval); +- urb->start_frame = start_frame; +- } +- +-skip_start_over: ++ if (GET_EP_CTX_STATE(ep_ctx) != EP_STATE_RUNNING) ++ xep->next_uframe = -1; + + return xhci_queue_isoc_tx(xhci, mem_flags, urb, slot_id, ep_index); + } +diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c +--- a/drivers/usb/host/xhci-tegra.c ++++ b/drivers/usb/host/xhci-tegra.c +@@ -6,6 +6,7 @@ + * Copyright (C) 2014 Google, Inc. + */ + ++#include + #include + #include + #include +@@ -993,7 +994,7 @@ static int tegra_xusb_wait_for_falcon(struct tegra_xusb *tegra) + u32 value; + + cap_regs = tegra->regs; +- op_regs = tegra->regs + HC_LENGTH(readl(&cap_regs->hc_capbase)); ++ op_regs = tegra->regs + FIELD_GET(HC_LENGTH, readl(&cap_regs->hc_capbase)), + + ret = readl_poll_timeout(&op_regs->status, value, !(value & STS_CNR), 1000, 200000); + +@@ -1895,7 +1896,7 @@ static int tegra_xusb_probe(struct platform_device *pdev) + goto remove_usb2; + } + +- if (HCC_MAX_PSA(xhci->hcc_params) >= 4) ++ if (GET_MAX_PSA_SIZE(xhci->hcc_params) >= 4) + xhci->shared_hcd->can_do_streams = 1; + + err = usb_add_hcd(xhci->shared_hcd, tegra->xhci_irq, IRQF_SHARED); +diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c +--- a/drivers/usb/host/xhci.c ++++ b/drivers/usb/host/xhci.c +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + + #include "xhci.h" + #include "xhci-trace.h" +@@ -1664,7 +1665,7 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag + goto free_priv; + } + +- if (xhci->devs[slot_id]->flags & VDEV_PORT_ERROR) { ++ if (xhci->devs[slot_id]->rhub_port->link_inactive) { + xhci_dbg(xhci, "Can't queue urb, port error, link inactive\n"); + ret = -ENODEV; + goto free_priv; +@@ -1852,11 +1853,11 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) + } + + /* In this case no commands are pending but the endpoint is stopped */ +- if (ep->ep_state & EP_CLEARING_TT) { ++ if (ep->ep_state & (EP_CLEARING_TT | EP_DROP_PENDING)) { + /* and cancelled TDs can be given back right away */ + xhci_dbg(xhci, "Invalidating TDs instantly on slot %d ep %d in state 0x%x\n", + urb->dev->slot_id, ep_index, ep->ep_state); +- xhci_process_cancelled_tds(ep); ++ xhci_process_cancelled_tds(xhci, ep); + } else { + /* Otherwise, queue a new Stop Endpoint command */ + command = xhci_alloc_command(xhci, false, GFP_ATOMIC); +@@ -3507,7 +3508,7 @@ static void xhci_calculate_streams_entries(struct xhci_hcd *xhci, + * level page entries), but that's an optional feature for xHCI host + * controllers. xHCs must support at least 4 stream IDs. + */ +- max_streams = HCC_MAX_PSA(xhci->hcc_params); ++ max_streams = GET_MAX_PSA_SIZE(xhci->hcc_params); + if (*num_stream_ctxs > max_streams) { + xhci_dbg(xhci, "xHCI HW only supports %u stream ctx entries.\n", + max_streams); +@@ -3637,7 +3638,7 @@ static int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, + + /* MaxPSASize value 0 (2 streams) means streams are not supported */ + if ((xhci->quirks & XHCI_BROKEN_STREAMS) || +- HCC_MAX_PSA(xhci->hcc_params) < 4) { ++ GET_MAX_PSA_SIZE(xhci->hcc_params) < 4) { + xhci_dbg(xhci, "xHCI controller does not support streams.\n"); + return -ENOSYS; + } +@@ -4035,7 +4036,6 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd, + xhci_get_slot_state(xhci, virt_dev->out_ctx)); + xhci_dbg(xhci, "Not freeing device rings.\n"); + /* Don't treat this as an error. May change my mind later. */ +- virt_dev->flags = 0; + ret = 0; + goto command_cleanup; + case COMP_SUCCESS: +@@ -4087,10 +4087,12 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd, + } + /* If necessary, update the number of active TTs on this root port */ + xhci_update_tt_active_eps(xhci, virt_dev, old_active_eps); +- virt_dev->flags = 0; + ret = 0; + + command_cleanup: ++ for (i = 0; i < EP_CTX_PER_DEV; i++) ++ virt_dev->eps[i].ep_state &= ~EP_DROP_PENDING; ++ + xhci_free_command(xhci, reset_device_cmd); + return ret; + } +@@ -4607,7 +4609,7 @@ static int xhci_calculate_hird_besl(struct xhci_hcd *xhci, + int besl_device = 0; + u32 field; + +- u2del = HCS_U2_LATENCY(xhci->hcs_params3); ++ u2del = FIELD_GET(HCS_U2_LATENCY, xhci->hcs_params3); + field = le32_to_cpu(udev->bos->ext_cap->bmAttributes); + + if (field & USB_BESL_SUPPORT) { +@@ -5459,26 +5461,28 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) + xhci_warn(xhci, "Host controller not accessible, removed?\n"); + return -ENODEV; + } +- xhci->op_regs = hcd->regs + HC_LENGTH(hc_capbase); ++ xhci->op_regs = hcd->regs + FIELD_GET(HC_LENGTH, hc_capbase); ++ + xhci->run_regs = hcd->regs + + (readl(&xhci->cap_regs->run_regs_off) & RTSOFF_MASK); + /* Cache read-only capability registers */ + hcs_params1 = readl(&xhci->cap_regs->hcs_params1); + xhci->hcs_params2 = readl(&xhci->cap_regs->hcs_params2); + xhci->hcs_params3 = readl(&xhci->cap_regs->hcs_params3); +- xhci->hci_version = HC_VERSION(hc_capbase); ++ xhci->hci_version = FIELD_GET(HC_VERSION, hc_capbase); + xhci->hcc_params = readl(&xhci->cap_regs->hcc_params); + if (xhci->hci_version > 0x100) + xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2); + + xhci->dma_mask_bits = 64; +- xhci->max_slots = min(HCS_MAX_SLOTS(hcs_params1), MAX_HC_SLOTS); +- xhci->max_ports = min(HCS_MAX_PORTS(hcs_params1), MAX_HC_PORTS); ++ xhci->max_slots = min(FIELD_GET(HCS_SLOTS_MASK, hcs_params1), MAX_HC_SLOTS); ++ xhci->max_ports = min(FIELD_GET(HCS_MAX_PORTS, hcs_params1), MAX_HC_PORTS); ++ + /* xhci-plat or xhci-pci might have set max_interrupters already */ + if (!xhci->max_interrupters) +- xhci->max_interrupters = min(HCS_MAX_INTRS(hcs_params1), MAX_HC_INTRS); +- else if (xhci->max_interrupters > HCS_MAX_INTRS(hcs_params1)) +- xhci->max_interrupters = HCS_MAX_INTRS(hcs_params1); ++ xhci->max_interrupters = min(FIELD_GET(HCS_MAX_INTRS, hcs_params1), MAX_HC_INTRS); ++ else if (xhci->max_interrupters > FIELD_GET(HCS_MAX_INTRS, hcs_params1)) ++ xhci->max_interrupters = FIELD_GET(HCS_MAX_INTRS, hcs_params1); + + xhci->quirks |= quirks; + +@@ -5519,7 +5523,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) + * DMA_BIT_MASK(32)) in this xhci_gen_setup(). + */ + if (xhci->quirks & XHCI_NO_64BIT_SUPPORT) +- xhci->hcc_params &= ~BIT(0); ++ xhci->hcc_params &= ~HCC_64BIT_ADDR; + + /* + * Set dma_mask and coherent_dma_mask to 64-bits if xHC supports +diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h +--- a/drivers/usb/host/xhci.h ++++ b/drivers/usb/host/xhci.h +@@ -290,6 +290,11 @@ struct xhci_run_regs { + struct xhci_intr_reg ir_set[1024]; + }; + ++/* Bits [13:3] of the microframe index equals the 1ms frame index */ ++#define MFINDEX_TO_FRAME(p) (((p) >> 3) & 0x7ff) ++#define MAX_FRAMES 2048 ++#define MAX_UFRAMES (MAX_FRAMES * 8) ++ + /** + * struct doorbell_array + * +@@ -677,9 +682,9 @@ struct xhci_virt_ep { + #define EP_SOFT_CLEAR_TOGGLE BIT(7) + /* usb_hub_clear_tt_buffer is in progress */ + #define EP_CLEARING_TT BIT(8) ++#define EP_DROP_PENDING BIT(9) /* port disconnect or link error, don't restart */ + /* ---- Related to URB cancellation ---- */ + struct list_head cancelled_td_list; +- struct xhci_hcd *xhci; + /* Dequeue pointer and dequeue segment for a submitted Set TR Dequeue + * command. We'll need to update the ring's dequeue segment and dequeue + * pointer after the command completes. +@@ -699,7 +704,7 @@ struct xhci_virt_ep { + struct list_head bw_endpoint_list; + unsigned long stop_time; + /* Isoch Frame ID checking storage */ +- int next_frame_id; ++ int next_uframe; + /* Use new Isoch TRB layout needed for extended TBC support */ + bool use_extended_tbc; + /* set if this endpoint is controlled via sideband access*/ +@@ -753,14 +758,6 @@ struct xhci_virt_device { + struct xhci_port *rhub_port; + struct xhci_interval_bw_table *bw_table; + struct xhci_tt_bw_info *tt_info; +- /* +- * flags for state tracking based on events and issued commands. +- * Software can not rely on states from output contexts because of +- * latency between events and xHC updating output context values. +- * See xhci 1.1 section 4.8.3 for more details +- */ +- unsigned long flags; +-#define VDEV_PORT_ERROR BIT(0) /* Port error, link inactive */ + + /* The current max exit latency for the enabled USB3 link states. */ + u16 current_mel; +@@ -1480,6 +1477,8 @@ struct xhci_port { + int hcd_portnum; + struct xhci_hub *rhub; + struct xhci_port_cap *port_cap; ++ unsigned int link_inactive:1; ++ unsigned int connected:1; + unsigned int lpm_incapable:1; + unsigned long resume_timestamp; + bool rexit_active; +@@ -1961,7 +1960,7 @@ unsigned int count_trbs(u64 addr, u64 len); + unsigned int xhci_num_trbs_free(struct xhci_ring *ring); + int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, + int suspend, gfp_t gfp_flags); +-void xhci_process_cancelled_tds(struct xhci_virt_ep *ep); ++void xhci_process_cancelled_tds(struct xhci_hcd *xhci, struct xhci_virt_ep *ep); + void xhci_update_erst_dequeue(struct xhci_hcd *xhci, + struct xhci_interrupter *ir, + bool clear_ehb); +diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c +--- a/drivers/usb/misc/cypress_cy7c63.c ++++ b/drivers/usb/misc/cypress_cy7c63.c +@@ -89,8 +89,11 @@ static int vendor_command(struct cypress *dev, unsigned char request, + address, data, iobuf, CYPRESS_MAX_REQSIZE, + USB_CTRL_GET_TIMEOUT); + /* we must not process garbage */ +- if (retval < 2) ++ if (retval < 2) { ++ if (retval >= 0) ++ retval = -EIO; + goto err_buf; ++ } + + /* store returned data (more READs to be added) */ + switch (request) { +@@ -175,8 +178,9 @@ static ssize_t read_port(struct device *dev, struct device_attribute *attr, + dev_dbg(&cyp->udev->dev, "READ_PORT%d called\n", port_num); + + result = vendor_command(cyp, CYPRESS_READ_PORT, read_id, 0); +- + dev_dbg(&cyp->udev->dev, "Result of vendor_command: %d\n\n", result); ++ if (result < 0) ++ return result; + + return sprintf(buf, "%d", cyp->port[port_num]); + } +diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c +--- a/drivers/usb/misc/uss720.c ++++ b/drivers/usb/misc/uss720.c +@@ -223,6 +223,7 @@ static int get_1284_register(struct parport *pp, unsigned char reg, unsigned cha + } + printk(KERN_WARNING "get_1284_register timeout\n"); + kill_all_async_requests_priv(priv); ++ kref_put(&rq->ref_count, destroy_async); + return -EIO; + } + +diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c +--- a/drivers/usb/mtu3/mtu3_host.c ++++ b/drivers/usb/mtu3/mtu3_host.c +@@ -46,6 +46,14 @@ + #define WC1_IS_P_95 BIT(12) + #define WC1_IS_EN_P0_95 BIT(6) + ++/* mt8196 */ ++#define PERI_WK_CTRL0_8196 0x08 ++#define WC0_IS_EN_P0_96 BIT(0) ++#define WC0_IS_EN_P1_96 BIT(7) ++ ++#define PERI_WK_CTRL1_8196 0x10 ++#define WC1_IS_EN_P2_96 BIT(0) ++ + /* mt2712 etc */ + #define PERI_SSUSB_SPM_CTRL 0x0 + #define SSC_IP_SLEEP_EN BIT(4) +@@ -59,6 +67,9 @@ enum ssusb_uwk_vers { + SSUSB_UWK_V1_3, /* mt8195 IP0 */ + SSUSB_UWK_V1_5 = 105, /* mt8195 IP2 */ + SSUSB_UWK_V1_6, /* mt8195 IP3 */ ++ SSUSB_UWK_V1_7, /* mt8196 IP0 */ ++ SSUSB_UWK_V1_8, /* mt8196 IP1 */ ++ SSUSB_UWK_V1_9, /* mt8196 IP2 */ + }; + + /* +@@ -100,6 +111,21 @@ static void ssusb_wakeup_ip_sleep_set(struct ssusb_mtk *ssusb, bool enable) + msk = WC0_IS_EN_P3_95 | WC0_IS_C_95(0x7) | WC0_IS_P_95; + val = enable ? (WC0_IS_EN_P3_95 | WC0_IS_C_95(0x1)) : 0; + break; ++ case SSUSB_UWK_V1_7: ++ reg = ssusb->uwk_reg_base + PERI_WK_CTRL0_8196; ++ msk = WC0_IS_EN_P0_96; ++ val = enable ? msk : 0; ++ break; ++ case SSUSB_UWK_V1_8: ++ reg = ssusb->uwk_reg_base + PERI_WK_CTRL0_8196; ++ msk = WC0_IS_EN_P1_96; ++ val = enable ? msk : 0; ++ break; ++ case SSUSB_UWK_V1_9: ++ reg = ssusb->uwk_reg_base + PERI_WK_CTRL1_8196; ++ msk = WC1_IS_EN_P2_96; ++ val = enable ? msk : 0; ++ break; + case SSUSB_UWK_V2: + reg = ssusb->uwk_reg_base + PERI_SSUSB_SPM_CTRL; + msk = SSC_IP_SLEEP_EN | SSC_SPM_INT_EN; +diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c +--- a/drivers/usb/mtu3/mtu3_plat.c ++++ b/drivers/usb/mtu3/mtu3_plat.c +@@ -544,7 +544,8 @@ static int mtu3_suspend_common(struct device *dev, pm_message_t msg) + + ssusb_phy_power_off(ssusb); + clk_bulk_disable_unprepare(BULK_CLKS_CNT, ssusb->clks); +- ssusb_wakeup_set(ssusb, true); ++ if (device_may_wakeup(dev) && (ssusb->is_host || PMSG_IS_AUTO(msg))) ++ ssusb_wakeup_set(ssusb, true); + return 0; + + sleep_err: +diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig +--- a/drivers/usb/musb/Kconfig ++++ b/drivers/usb/musb/Kconfig +@@ -116,7 +116,6 @@ config USB_MUSB_POLARFIRE_SOC + tristate "Microchip PolarFire SoC platforms" + depends on ARCH_MICROCHIP_POLARFIRE || COMPILE_TEST + depends on NOP_USB_XCEIV +- select USB_MUSB_DUAL_ROLE + help + Say Y here to enable support for USB on Microchip's PolarFire SoC. + +diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c +--- a/drivers/usb/musb/musb_host.c ++++ b/drivers/usb/musb/musb_host.c +@@ -1774,7 +1774,8 @@ void musb_host_rx(struct musb *musb, u8 epnum) + status = -EPIPE; + + } else if (rx_csr & MUSB_RXCSR_H_ERROR) { +- dev_err(musb->controller, "ep%d RX three-strikes error", epnum); ++ dev_err_ratelimited(musb->controller, ++ "ep%d RX three-strikes error\n", epnum); + + /* + * The three-strikes error could only happen when the USB +@@ -1788,6 +1789,17 @@ void musb_host_rx(struct musb *musb, u8 epnum) + rx_csr &= ~MUSB_RXCSR_H_ERROR; + musb_writew(epio, MUSB_RXCSR, rx_csr); + ++ /* ++ * Unplugging a USB-Ethernet adapter while it is busy can make ++ * the controller keep re-asserting the three-strikes error for ++ * this endpoint before the disconnect is processed. That floods ++ * the log and can wedge the host port until reboot. Drop the ++ * stale pending RX interrupt on platforms that support it (e.g. ++ * AM335x/DSPS) to break the storm; the transfer is still ++ * aborted below via the fault path. ++ */ ++ musb_platform_clear_ep_rxintr(musb, epnum); ++ + } else if (rx_csr & MUSB_RXCSR_DATAERROR) { + + if (USB_ENDPOINT_XFER_ISOC != qh->type) { +diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c +--- a/drivers/usb/serial/digi_acceleport.c ++++ b/drivers/usb/serial/digi_acceleport.c +@@ -32,10 +32,6 @@ + /* so we can be sure to send the full buffer in one urb */ + #define DIGI_OUT_BUF_SIZE 8 + +-/* port input buffer length -- must be >= transfer buffer length - 3 */ +-/* so we can be sure to hold at least one full buffer from one urb */ +-#define DIGI_IN_BUF_SIZE 64 +- + /* retry timeout while sleeping */ + #define DIGI_RETRY_TIMEOUT (HZ/10) + +@@ -176,10 +172,10 @@ + /* Structures */ + + struct digi_serial { +- spinlock_t ds_serial_lock; ++ struct mutex open_mutex; + struct usb_serial_port *ds_oob_port; /* out-of-band port */ + int ds_oob_port_num; /* index of out-of-band port */ +- int ds_device_started; ++ int open_count; + }; + + struct digi_port { +@@ -194,7 +190,6 @@ struct digi_port { + int dp_throttled; + int dp_throttle_restart; + wait_queue_head_t dp_flush_wait; +- wait_queue_head_t dp_close_wait; /* wait queue for close */ + wait_queue_head_t write_wait; + struct usb_serial_port *dp_port; + }; +@@ -227,9 +222,7 @@ static unsigned int digi_chars_in_buffer(struct tty_struct *tty); + static int digi_open(struct tty_struct *tty, struct usb_serial_port *port); + static void digi_close(struct usb_serial_port *port); + static void digi_dtr_rts(struct usb_serial_port *port, int on); +-static int digi_startup_device(struct usb_serial *serial); + static int digi_startup(struct usb_serial *serial); +-static void digi_disconnect(struct usb_serial *serial); + static void digi_release(struct usb_serial *serial); + static int digi_port_probe(struct usb_serial_port *port); + static void digi_port_remove(struct usb_serial_port *port); +@@ -272,7 +265,7 @@ static struct usb_serial_driver digi_acceleport_2_device = { + .dtr_rts = digi_dtr_rts, + .write = digi_write, + .write_room = digi_write_room, +- .write_bulk_callback = digi_write_bulk_callback, ++ .write_bulk_callback = digi_write_bulk_callback, + .read_bulk_callback = digi_read_bulk_callback, + .chars_in_buffer = digi_chars_in_buffer, + .throttle = digi_rx_throttle, +@@ -282,7 +275,6 @@ static struct usb_serial_driver digi_acceleport_2_device = { + .tiocmget = digi_tiocmget, + .tiocmset = digi_tiocmset, + .attach = digi_startup, +- .disconnect = digi_disconnect, + .release = digi_release, + .port_probe = digi_port_probe, + .port_remove = digi_port_remove, +@@ -301,7 +293,7 @@ static struct usb_serial_driver digi_acceleport_4_device = { + .close = digi_close, + .write = digi_write, + .write_room = digi_write_room, +- .write_bulk_callback = digi_write_bulk_callback, ++ .write_bulk_callback = digi_write_bulk_callback, + .read_bulk_callback = digi_read_bulk_callback, + .chars_in_buffer = digi_chars_in_buffer, + .throttle = digi_rx_throttle, +@@ -311,7 +303,6 @@ static struct usb_serial_driver digi_acceleport_4_device = { + .tiocmget = digi_tiocmget, + .tiocmset = digi_tiocmset, + .attach = digi_startup, +- .disconnect = digi_disconnect, + .release = digi_release, + .port_probe = digi_port_probe, + .port_remove = digi_port_remove, +@@ -335,7 +326,6 @@ static struct usb_serial_driver * const serial_drivers[] = { + * interruptible_sleep_on_timeout is deprecated and has been replaced + * with the equivalent code. + */ +- + static long cond_wait_interruptible_timeout_irqrestore( + wait_queue_head_t *q, long timeout, + spinlock_t *lock, unsigned long flags) +@@ -351,6 +341,13 @@ __releases(lock) + return timeout; + } + ++static struct usb_serial_port *digi_get_oob_port(struct usb_serial *serial) ++{ ++ struct digi_serial *serial_priv = usb_get_serial_data(serial); ++ ++ return serial_priv->ds_oob_port; ++} ++ + /* + * Digi Write OOB Command + * +@@ -361,15 +358,14 @@ __releases(lock) + * the interruptible flag is true, or a negative error + * returned by usb_submit_urb. + */ +- + static int digi_write_oob_command(struct usb_serial_port *port, + unsigned char *buf, int count, int interruptible) + { +- int ret = 0; +- int len; +- struct usb_serial_port *oob_port = (struct usb_serial_port *)((struct digi_serial *)(usb_get_serial_data(port->serial)))->ds_oob_port; ++ struct usb_serial_port *oob_port = digi_get_oob_port(port->serial); + struct digi_port *oob_priv = usb_get_serial_port_data(oob_port); + unsigned long flags; ++ int ret = 0; ++ int len; + + dev_dbg(&port->dev, + "digi_write_oob_command: TOP: port=%d, count=%d\n", +@@ -406,10 +402,8 @@ static int digi_write_oob_command(struct usb_serial_port *port, + dev_err(&port->dev, "%s: usb_submit_urb failed, ret=%d\n", + __func__, ret); + return ret; +- + } + +- + /* + * Digi Write In Band Command + * +@@ -421,16 +415,15 @@ static int digi_write_oob_command(struct usb_serial_port *port, + * timeout ticks. Returns 0 if successful, or a negative + * error returned by digi_write. + */ +- + static int digi_write_inb_command(struct usb_serial_port *port, + unsigned char *buf, int count, unsigned long timeout) + { +- int ret = 0; +- int len; + struct digi_port *priv = usb_get_serial_port_data(port); + unsigned char *data = port->write_urb->transfer_buffer; + unsigned long expire; + unsigned long flags; ++ int ret = 0; ++ int len; + + dev_dbg(&port->dev, "digi_write_inb_command: TOP: port=%d, count=%d\n", + priv->dp_port_num, count); +@@ -459,7 +452,7 @@ static int digi_write_inb_command(struct usb_serial_port *port, + /* len must be a multiple of 4 and small enough to */ + /* guarantee the write will send buffered data first, */ + /* so commands are in order with data and not split */ +- len = min(count, port->bulk_out_size-2-priv->dp_out_buf_len); ++ len = min(count, port->bulk_out_size - 2 - priv->dp_out_buf_len); + if (len > 4) + len &= ~3; + +@@ -467,11 +460,10 @@ static int digi_write_inb_command(struct usb_serial_port *port, + if (priv->dp_out_buf_len > 0) { + data[0] = DIGI_CMD_SEND_DATA; + data[1] = priv->dp_out_buf_len; +- memcpy(data + 2, priv->dp_out_buf, +- priv->dp_out_buf_len); ++ memcpy(data + 2, priv->dp_out_buf, priv->dp_out_buf_len); + memcpy(data + 2 + priv->dp_out_buf_len, buf, len); +- port->write_urb->transfer_buffer_length +- = priv->dp_out_buf_len + 2 + len; ++ port->write_urb->transfer_buffer_length = ++ priv->dp_out_buf_len + 2 + len; + } else { + memcpy(data, buf, len); + port->write_urb->transfer_buffer_length = len; +@@ -484,7 +476,6 @@ static int digi_write_inb_command(struct usb_serial_port *port, + count -= len; + buf += len; + } +- + } + spin_unlock_irqrestore(&priv->dp_port_lock, flags); + +@@ -495,7 +486,6 @@ static int digi_write_inb_command(struct usb_serial_port *port, + return ret; + } + +- + /* + * Digi Set Modem Signals + * +@@ -505,17 +495,15 @@ static int digi_write_inb_command(struct usb_serial_port *port, + * -EINTR if interrupted while sleeping, or a non-zero error + * returned by usb_submit_urb. + */ +- + static int digi_set_modem_signals(struct usb_serial_port *port, + unsigned int modem_signals, int interruptible) + { +- +- int ret; + struct digi_port *port_priv = usb_get_serial_port_data(port); +- struct usb_serial_port *oob_port = (struct usb_serial_port *) ((struct digi_serial *)(usb_get_serial_data(port->serial)))->ds_oob_port; ++ struct usb_serial_port *oob_port = digi_get_oob_port(port->serial); + struct digi_port *oob_priv = usb_get_serial_port_data(oob_port); + unsigned char *data = oob_port->write_urb->transfer_buffer; + unsigned long flags; ++ int ret; + + dev_dbg(&port->dev, + "digi_set_modem_signals: TOP: port=%d, modem_signals=0x%x\n", +@@ -573,14 +561,13 @@ static int digi_set_modem_signals(struct usb_serial_port *port, + * is only called from close, and only one process can be in close on a + * port at a time, so its ok. + */ +- + static int digi_transmit_idle(struct usb_serial_port *port, + unsigned long timeout) + { +- int ret; +- unsigned char buf[2]; + struct digi_port *priv = usb_get_serial_port_data(port); ++ unsigned char buf[2]; + unsigned long flags; ++ int ret; + + spin_lock_irqsave(&priv->dp_port_lock, flags); + priv->dp_transmit_idle = 0; +@@ -607,16 +594,15 @@ static int digi_transmit_idle(struct usb_serial_port *port, + } + priv->dp_transmit_idle = 0; + spin_unlock_irqrestore(&priv->dp_port_lock, flags); ++ + return 0; +- + } + +- + static void digi_rx_throttle(struct tty_struct *tty) + { +- unsigned long flags; + struct usb_serial_port *port = tty->driver_data; + struct digi_port *priv = usb_get_serial_port_data(port); ++ unsigned long flags; + + /* stop receiving characters by not resubmitting the read urb */ + spin_lock_irqsave(&priv->dp_port_lock, flags); +@@ -625,13 +611,12 @@ static void digi_rx_throttle(struct tty_struct *tty) + spin_unlock_irqrestore(&priv->dp_port_lock, flags); + } + +- + static void digi_rx_unthrottle(struct tty_struct *tty) + { +- int ret = 0; +- unsigned long flags; + struct usb_serial_port *port = tty->driver_data; + struct digi_port *priv = usb_get_serial_port_data(port); ++ unsigned long flags; ++ int ret = 0; + + spin_lock_irqsave(&priv->dp_port_lock, flags); + +@@ -651,7 +636,6 @@ static void digi_rx_unthrottle(struct tty_struct *tty) + __func__, ret, priv->dp_port_num); + } + +- + static void digi_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, + const struct ktermios *old_termios) +@@ -762,7 +746,6 @@ static void digi_set_termios(struct tty_struct *tty, + + /* set stop bits */ + if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) { +- + if ((cflag & CSTOPB)) + arg = DIGI_STOP_BITS_2; + else +@@ -772,7 +755,6 @@ static void digi_set_termios(struct tty_struct *tty, + buf[i++] = priv->dp_port_num; + buf[i++] = arg; + buf[i++] = 0; +- + } + + /* set input flow control */ +@@ -841,7 +823,6 @@ static void digi_set_termios(struct tty_struct *tty, + tty_encode_baud_rate(tty, baud, baud); + } + +- + static int digi_break_ctl(struct tty_struct *tty, int break_state) + { + struct usb_serial_port *port = tty->driver_data; +@@ -855,50 +836,48 @@ static int digi_break_ctl(struct tty_struct *tty, int break_state) + return digi_write_inb_command(port, buf, 4, 0); + } + +- + static int digi_tiocmget(struct tty_struct *tty) + { + struct usb_serial_port *port = tty->driver_data; + struct digi_port *priv = usb_get_serial_port_data(port); +- unsigned int val; + unsigned long flags; ++ unsigned int val; + + spin_lock_irqsave(&priv->dp_port_lock, flags); + val = priv->dp_modem_signals; + spin_unlock_irqrestore(&priv->dp_port_lock, flags); ++ + return val; + } + +- + static int digi_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) + { + struct usb_serial_port *port = tty->driver_data; + struct digi_port *priv = usb_get_serial_port_data(port); +- unsigned int val; + unsigned long flags; ++ unsigned int val; + + spin_lock_irqsave(&priv->dp_port_lock, flags); + val = (priv->dp_modem_signals & ~clear) | set; + spin_unlock_irqrestore(&priv->dp_port_lock, flags); ++ + return digi_set_modem_signals(port, val, 1); + } + +- + static int digi_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) + { +- +- int ret, data_len, new_len; + struct digi_port *priv = usb_get_serial_port_data(port); + unsigned char *data = port->write_urb->transfer_buffer; ++ int ret, data_len, new_len; + unsigned long flags; + + dev_dbg(&port->dev, "digi_write: TOP: port=%d, count=%d\n", + priv->dp_port_num, count); + + /* copy user data (which can sleep) before getting spin lock */ +- count = min(count, port->bulk_out_size-2); ++ count = min(count, port->bulk_out_size - 2); + count = min(64, count); + + /* be sure only one write proceeds at a time */ +@@ -920,7 +899,7 @@ static int digi_write(struct tty_struct *tty, struct usb_serial_port *port, + + /* allow space for any buffered data and for new data, up to */ + /* transfer buffer size - 2 (for command and length bytes) */ +- new_len = min(count, port->bulk_out_size-2-priv->dp_out_buf_len); ++ new_len = min(count, port->bulk_out_size - 2 - priv->dp_out_buf_len); + data_len = new_len + priv->dp_out_buf_len; + + if (data_len == 0) { +@@ -928,7 +907,7 @@ static int digi_write(struct tty_struct *tty, struct usb_serial_port *port, + return 0; + } + +- port->write_urb->transfer_buffer_length = data_len+2; ++ port->write_urb->transfer_buffer_length = data_len + 2; + + *data++ = DIGI_CMD_SEND_DATA; + *data++ = data_len; +@@ -954,39 +933,22 @@ static int digi_write(struct tty_struct *tty, struct usb_serial_port *port, + "%s: usb_submit_urb failed, ret=%d, port=%d\n", + __func__, ret, priv->dp_port_num); + dev_dbg(&port->dev, "digi_write: returning %d\n", ret); +- return ret; + ++ return ret; + } + + static void digi_write_bulk_callback(struct urb *urb) + { +- + struct usb_serial_port *port = urb->context; +- struct usb_serial *serial; +- struct digi_port *priv; +- struct digi_serial *serial_priv; ++ struct digi_serial *serial_priv = usb_get_serial_data(port->serial); ++ struct digi_port *priv = usb_get_serial_port_data(port); ++ unsigned char *data = urb->transfer_buffer; + unsigned long flags; +- int ret = 0; +- int status = urb->status; + bool wakeup; +- +- /* port and serial sanity check */ +- if (port == NULL || (priv = usb_get_serial_port_data(port)) == NULL) { +- pr_err("%s: port or port->private is NULL, status=%d\n", +- __func__, status); +- return; +- } +- serial = port->serial; +- if (serial == NULL || (serial_priv = usb_get_serial_data(serial)) == NULL) { +- dev_err(&port->dev, +- "%s: serial or serial->private is NULL, status=%d\n", +- __func__, status); +- return; +- } ++ int ret = 0; + + /* handle oob callback */ + if (priv->dp_port_num == serial_priv->ds_oob_port_num) { +- dev_dbg(&port->dev, "digi_write_bulk_callback: oob callback\n"); + spin_lock_irqsave(&priv->dp_port_lock, flags); + priv->dp_write_urb_in_use = 0; + wake_up_interruptible(&priv->write_wait); +@@ -999,15 +961,13 @@ static void digi_write_bulk_callback(struct urb *urb) + spin_lock_irqsave(&priv->dp_port_lock, flags); + priv->dp_write_urb_in_use = 0; + if (priv->dp_out_buf_len > 0) { +- *((unsigned char *)(port->write_urb->transfer_buffer)) +- = (unsigned char)DIGI_CMD_SEND_DATA; +- *((unsigned char *)(port->write_urb->transfer_buffer) + 1) +- = (unsigned char)priv->dp_out_buf_len; +- port->write_urb->transfer_buffer_length = +- priv->dp_out_buf_len + 2; +- memcpy(port->write_urb->transfer_buffer + 2, priv->dp_out_buf, +- priv->dp_out_buf_len); +- ret = usb_submit_urb(port->write_urb, GFP_ATOMIC); ++ data[0] = DIGI_CMD_SEND_DATA; ++ data[1] = priv->dp_out_buf_len; ++ memcpy(data + 2, priv->dp_out_buf, priv->dp_out_buf_len); ++ ++ urb->transfer_buffer_length = priv->dp_out_buf_len + 2; ++ ++ ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret == 0) { + priv->dp_write_urb_in_use = 1; + priv->dp_out_buf_len = 0; +@@ -1041,8 +1001,8 @@ static unsigned int digi_write_room(struct tty_struct *tty) + + spin_unlock_irqrestore(&priv->dp_port_lock, flags); + dev_dbg(&port->dev, "digi_write_room: port=%d, room=%u\n", priv->dp_port_num, room); +- return room; + ++ return room; + } + + static unsigned int digi_chars_in_buffer(struct tty_struct *tty) +@@ -1070,17 +1030,53 @@ static void digi_dtr_rts(struct usb_serial_port *port, int on) + digi_set_modem_signals(port, on * (TIOCM_DTR | TIOCM_RTS), 1); + } + ++static int digi_open_oob_port(struct usb_serial *serial) ++{ ++ struct digi_serial *serial_priv = usb_get_serial_data(serial); ++ struct usb_serial_port *oob_port = serial_priv->ds_oob_port; ++ int ret = 0; ++ ++ mutex_lock(&serial_priv->open_mutex); ++ ++ if (serial_priv->open_count++ == 0) { ++ ret = usb_submit_urb(oob_port->read_urb, GFP_KERNEL); ++ if (ret) { ++ dev_err(&serial->interface->dev, "failed to submit OOB read urb: %d\n", ++ ret); ++ serial_priv->open_count--; ++ } ++ } ++ ++ mutex_unlock(&serial_priv->open_mutex); ++ ++ return ret; ++} ++ ++static void digi_close_oob_port(struct usb_serial *serial) ++{ ++ struct digi_serial *serial_priv = usb_get_serial_data(serial); ++ struct usb_serial_port *oob_port = serial_priv->ds_oob_port; ++ ++ mutex_lock(&serial_priv->open_mutex); ++ ++ if (serial_priv->open_count-- == 1) { ++ usb_kill_urb(oob_port->read_urb); ++ usb_kill_urb(oob_port->write_urb); ++ } ++ ++ mutex_unlock(&serial_priv->open_mutex); ++} ++ + static int digi_open(struct tty_struct *tty, struct usb_serial_port *port) + { +- int ret; +- unsigned char buf[32]; + struct digi_port *priv = usb_get_serial_port_data(port); + struct ktermios not_termios; +- int throttled; ++ unsigned char buf[32]; ++ int ret; + +- /* be sure the device is started up */ +- if (digi_startup_device(port->serial) != 0) +- return -ENXIO; ++ ret = digi_open_oob_port(port->serial); ++ if (ret) ++ return ret; + + /* read modem signals automatically whenever they change */ + buf[0] = DIGI_CMD_READ_INPUT_SIGNALS; +@@ -1106,32 +1102,34 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port) + } + + spin_lock_irq(&priv->dp_port_lock); +- throttled = priv->dp_throttle_restart; + priv->dp_throttled = 0; + priv->dp_throttle_restart = 0; + spin_unlock_irq(&priv->dp_port_lock); + +- if (throttled) { +- ret = usb_submit_urb(port->read_urb, GFP_KERNEL); +- if (ret) { +- dev_err(&port->dev, "failed to submit read urb: %d\n", ret); +- return ret; +- } ++ ret = usb_submit_urb(port->read_urb, GFP_KERNEL); ++ if (ret) { ++ dev_err(&port->dev, "failed to submit read urb: %d\n", ret); ++ goto err_close_oob; + } + + return 0; +-} + ++err_close_oob: ++ digi_close_oob_port(port->serial); ++ ++ return ret; ++} + + static void digi_close(struct usb_serial_port *port) + { ++ struct digi_port *priv = usb_get_serial_port_data(port); ++ unsigned char buf[32]; + DEFINE_WAIT(wait); + int ret; +- unsigned char buf[32]; +- struct digi_port *priv = usb_get_serial_port_data(port); ++ ++ usb_kill_urb(port->read_urb); + + mutex_lock(&port->serial->disc_mutex); +- /* if disconnected, just clear flags */ + if (port->serial->disconnected) + goto exit; + +@@ -1177,57 +1175,18 @@ static void digi_close(struct usb_serial_port *port) + TASK_INTERRUPTIBLE); + schedule_timeout(DIGI_CLOSE_TIMEOUT); + finish_wait(&priv->dp_flush_wait, &wait); ++exit: ++ mutex_unlock(&port->serial->disc_mutex); + + /* shutdown any outstanding bulk writes */ + usb_kill_urb(port->write_urb); +-exit: +- spin_lock_irq(&priv->dp_port_lock); +- priv->dp_write_urb_in_use = 0; +- wake_up_interruptible(&priv->dp_close_wait); +- spin_unlock_irq(&priv->dp_port_lock); +- mutex_unlock(&port->serial->disc_mutex); +-} + +- +-/* +- * Digi Startup Device +- * +- * Starts reads on all ports. Must be called AFTER startup, with +- * urbs initialized. Returns 0 if successful, non-zero error otherwise. +- */ +- +-static int digi_startup_device(struct usb_serial *serial) +-{ +- int i, ret = 0; +- struct digi_serial *serial_priv = usb_get_serial_data(serial); +- struct usb_serial_port *port; +- +- /* be sure this happens exactly once */ +- spin_lock(&serial_priv->ds_serial_lock); +- if (serial_priv->ds_device_started) { +- spin_unlock(&serial_priv->ds_serial_lock); +- return 0; +- } +- serial_priv->ds_device_started = 1; +- spin_unlock(&serial_priv->ds_serial_lock); +- +- /* start reading from each bulk in endpoint for the device */ +- /* set USB_DISABLE_SPD flag for write bulk urbs */ +- for (i = 0; i < serial->type->num_ports + 1; i++) { +- port = serial->port[i]; +- ret = usb_submit_urb(port->read_urb, GFP_KERNEL); +- if (ret != 0) { +- dev_err(&port->dev, +- "%s: usb_submit_urb failed, ret=%d, port=%d\n", +- __func__, ret, i); +- break; +- } +- } +- return ret; ++ digi_close_oob_port(port->serial); + } + + static int digi_port_init(struct usb_serial_port *port, unsigned port_num) + { ++ struct digi_serial *serial_priv = usb_get_serial_data(port->serial); + struct digi_port *priv; + + priv = kzalloc_obj(*priv); +@@ -1235,10 +1194,13 @@ static int digi_port_init(struct usb_serial_port *port, unsigned port_num) + return -ENOMEM; + + spin_lock_init(&priv->dp_port_lock); ++ ++ if (port == serial_priv->ds_oob_port) ++ lockdep_set_subclass(&priv->dp_port_lock, SINGLE_DEPTH_NESTING); ++ + priv->dp_port_num = port_num; + init_waitqueue_head(&priv->dp_transmit_idle_wait); + init_waitqueue_head(&priv->dp_flush_wait); +- init_waitqueue_head(&priv->dp_close_wait); + init_waitqueue_head(&priv->write_wait); + priv->dp_port = port; + +@@ -1275,10 +1237,13 @@ static int digi_startup(struct usb_serial *serial) + if (!serial_priv) + return -ENOMEM; + +- spin_lock_init(&serial_priv->ds_serial_lock); ++ mutex_init(&serial_priv->open_mutex); ++ + serial_priv->ds_oob_port_num = oob_port_num; + serial_priv->ds_oob_port = serial->port[oob_port_num]; + ++ usb_set_serial_data(serial, serial_priv); ++ + ret = digi_port_init(serial_priv->ds_oob_port, + serial_priv->ds_oob_port_num); + if (ret) { +@@ -1286,24 +1251,9 @@ static int digi_startup(struct usb_serial *serial) + return ret; + } + +- usb_set_serial_data(serial, serial_priv); +- + return 0; + } + +- +-static void digi_disconnect(struct usb_serial *serial) +-{ +- int i; +- +- /* stop reads and writes on all ports */ +- for (i = 0; i < serial->type->num_ports + 1; i++) { +- usb_kill_urb(serial->port[i]->read_urb); +- usb_kill_urb(serial->port[i]->write_urb); +- } +-} +- +- + static void digi_release(struct usb_serial *serial) + { + struct digi_serial *serial_priv; +@@ -1333,30 +1283,25 @@ static void digi_port_remove(struct usb_serial_port *port) + static void digi_read_bulk_callback(struct urb *urb) + { + struct usb_serial_port *port = urb->context; +- struct digi_port *priv; +- struct digi_serial *serial_priv; +- int ret; ++ struct usb_serial *serial = port->serial; ++ struct digi_serial *serial_priv = usb_get_serial_data(serial); ++ struct digi_port *priv = usb_get_serial_port_data(port); + int status = urb->status; +- +- /* port sanity check, do not resubmit if port is not valid */ +- if (port == NULL) +- return; +- priv = usb_get_serial_port_data(port); +- if (priv == NULL) { +- dev_err(&port->dev, "%s: port->private is NULL, status=%d\n", +- __func__, status); +- return; +- } +- if (port->serial == NULL || +- (serial_priv = usb_get_serial_data(port->serial)) == NULL) { +- dev_err(&port->dev, "%s: serial is bad or serial->private " +- "is NULL, status=%d\n", __func__, status); +- return; +- } ++ int ret; + + /* do not resubmit urb if it has any status error */ +- if (status) { +- dev_err(&port->dev, ++ switch (status) { ++ case 0: ++ break; ++ case -ENOENT: ++ case -ECONNRESET: ++ case -ESHUTDOWN: ++ dev_err(&serial->interface->dev, ++ "%s: nonzero read bulk status: status=%d, port=%d\n", ++ __func__, status, priv->dp_port_num); ++ return; ++ default: ++ dev_err(&serial->interface->dev, + "%s: nonzero read bulk status: status=%d, port=%d\n", + __func__, status, priv->dp_port_num); + return; +@@ -1374,11 +1319,10 @@ static void digi_read_bulk_callback(struct urb *urb) + /* continue read */ + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret != 0 && ret != -EPERM) { +- dev_err(&port->dev, ++ dev_err(&serial->interface->dev, + "%s: failed resubmitting urb, ret=%d, port=%d\n", + __func__, ret, priv->dp_port_num); + } +- + } + + /* +@@ -1390,7 +1334,6 @@ static void digi_read_bulk_callback(struct urb *urb) + * It returns 0 if successful, 1 if successful but the port is + * throttled, and -1 if the sanity checks failed. + */ +- + static int digi_read_inb_callback(struct urb *urb) + { + struct usb_serial_port *port = urb->context; +@@ -1468,10 +1411,8 @@ static int digi_read_inb_callback(struct urb *urb) + dev_dbg(&port->dev, "%s: unknown opcode: %d\n", __func__, opcode); + + return throttled ? 1 : 0; +- + } + +- + /* + * Digi Read OOB Callback + * +@@ -1480,10 +1421,8 @@ static int digi_read_inb_callback(struct urb *urb) + * the port->serial is valid. It returns 0 if successful, and + * -1 if the sanity checks failed. + */ +- + static int digi_read_oob_callback(struct urb *urb) + { +- + struct usb_serial_port *port = urb->context; + struct usb_serial *serial = port->serial; + struct tty_struct *tty; +@@ -1491,8 +1430,8 @@ static int digi_read_oob_callback(struct urb *urb) + unsigned char *buf = urb->transfer_buffer; + int opcode, line, status, val; + unsigned long flags; +- int i; + unsigned int rts; ++ int i; + + if (urb->actual_length < 4) + return -1; +@@ -1504,7 +1443,8 @@ static int digi_read_oob_callback(struct urb *urb) + status = buf[i + 2]; + val = buf[i + 3]; + +- dev_dbg(&port->dev, "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d\n", ++ dev_dbg(&serial->interface->dev, ++ "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d\n", + opcode, line, status, val); + + if (status != 0 || line >= serial->type->num_ports) +@@ -1562,8 +1502,8 @@ static int digi_read_oob_callback(struct urb *urb) + } + tty_kref_put(tty); + } +- return 0; + ++ return 0; + } + + module_usb_serial_driver(serial_drivers, id_table_combined); +diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c +--- a/drivers/usb/serial/keyspan_pda.c ++++ b/drivers/usb/serial/keyspan_pda.c +@@ -31,7 +31,6 @@ + struct keyspan_pda_private { + int tx_room; + struct work_struct unthrottle_work; +- struct usb_serial *serial; + struct usb_serial_port *port; + bool throttled; + bool throttle_req; +diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c +--- a/drivers/usb/serial/metro-usb.c ++++ b/drivers/usb/serial/metro-usb.c +@@ -36,6 +36,7 @@ + struct metrousb_private { + spinlock_t lock; + int throttled; ++ int throttle_req; + unsigned long control_state; + }; + +@@ -108,7 +109,7 @@ static void metrousb_read_int_callback(struct urb *urb) + unsigned char *data = urb->transfer_buffer; + unsigned long flags; + int throttled = 0; +- int result = 0; ++ int result; + + dev_dbg(&port->dev, "%s\n", __func__); + +@@ -143,7 +144,10 @@ static void metrousb_read_int_callback(struct urb *urb) + + /* Set any port variables. */ + spin_lock_irqsave(&metro_priv->lock, flags); +- throttled = metro_priv->throttled; ++ if (metro_priv->throttle_req) { ++ metro_priv->throttled = 1; ++ throttled = 1; ++ } + spin_unlock_irqrestore(&metro_priv->lock, flags); + + if (throttled) +@@ -169,12 +173,13 @@ static int metrousb_open(struct tty_struct *tty, struct usb_serial_port *port) + struct usb_serial *serial = port->serial; + struct metrousb_private *metro_priv = usb_get_serial_port_data(port); + unsigned long flags; +- int result = 0; ++ int result; + + /* Set the private data information for the port. */ + spin_lock_irqsave(&metro_priv->lock, flags); + metro_priv->control_state = 0; + metro_priv->throttled = 0; ++ metro_priv->throttle_req = 0; + spin_unlock_irqrestore(&metro_priv->lock, flags); + + /* Clear the urb pipe. */ +@@ -269,7 +274,7 @@ static void metrousb_throttle(struct tty_struct *tty) + + /* Set the private information for the port to stop reading data. */ + spin_lock_irqsave(&metro_priv->lock, flags); +- metro_priv->throttled = 1; ++ metro_priv->throttle_req = 1; + spin_unlock_irqrestore(&metro_priv->lock, flags); + } + +@@ -321,19 +326,23 @@ static void metrousb_unthrottle(struct tty_struct *tty) + struct usb_serial_port *port = tty->driver_data; + struct metrousb_private *metro_priv = usb_get_serial_port_data(port); + unsigned long flags; +- int result = 0; ++ int throttled; ++ int result; + + /* Set the private information for the port to resume reading data. */ + spin_lock_irqsave(&metro_priv->lock, flags); ++ throttled = metro_priv->throttled; + metro_priv->throttled = 0; ++ metro_priv->throttle_req = 0; + spin_unlock_irqrestore(&metro_priv->lock, flags); + +- /* Submit the urb to read from the port. */ +- result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); +- if (result) +- dev_err(&port->dev, +- "failed submitting interrupt in urb error code=%d\n", +- result); ++ if (throttled) { ++ result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); ++ if (result) { ++ dev_err(&port->dev, "failed to submit interrupt in urb: %d\n", ++ result); ++ } ++ } + } + + static struct usb_serial_driver metrousb_device = { +diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c +--- a/drivers/usb/serial/pl2303.c ++++ b/drivers/usb/serial/pl2303.c +@@ -51,6 +51,7 @@ static const struct usb_device_id id_table[] = { + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GL) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GE) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GS) }, ++ { USB_DEVICE(PL2303_VENDOR_ID, PL256X_PRODUCT_ID_4P) }, + { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) }, + { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) }, + { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID), +@@ -141,10 +142,15 @@ MODULE_DEVICE_TABLE(usb, id_table); + #define VENDOR_WRITE_REQUEST_TYPE 0x40 + #define VENDOR_WRITE_REQUEST 0x01 + #define VENDOR_WRITE_NREQUEST 0x80 ++#define VENDOR_WRITE_MPREQUEST 0x80 + + #define VENDOR_READ_REQUEST_TYPE 0xc0 + #define VENDOR_READ_REQUEST 0x01 + #define VENDOR_READ_NREQUEST 0x81 ++#define VENDOR_READ_MPREQUEST 0x80 ++ ++#define PL256X_RESET_REQUEST_TYPE 0x40 ++#define PL256X_RESET_REQUEST 0x96 + + #define UART_STATE_INDEX 8 + #define UART_STATE_MSR_MASK 0x8b +@@ -172,6 +178,16 @@ MODULE_DEVICE_TABLE(usb, id_table); + #define PL2303_HXN_FLOWCTRL_RTS_CTS 0x18 + #define PL2303_HXN_FLOWCTRL_XON_XOFF 0x0c + ++#define PL256X_PORT_A_FLOWCTRL_REG 0xc005 ++#define PL256X_PORT_B_FLOWCTRL_REG 0xd005 ++#define PL256X_PORT_C_FLOWCTRL_REG 0xe005 ++#define PL256X_PORT_D_FLOWCTRL_REG 0xf005 ++ ++#define PL256X_FLOWCTRL_MASK 0x43 ++#define PL256X_FLOWCTRL_XON_XOFF 0x40 ++#define PL256X_FLOWCTRL_RTS_CTS 0x03 ++#define PL256X_FLOWCTRL_NONE 0x00 ++ + static int pl2303_set_break(struct usb_serial_port *port, bool enable); + + enum pl2303_type { +@@ -181,6 +197,7 @@ enum pl2303_type { + TYPE_TB, + TYPE_HXD, + TYPE_HXN, ++ TYPE_MP, + TYPE_COUNT + }; + +@@ -196,6 +213,8 @@ struct pl2303_type_data { + struct pl2303_serial_private { + const struct pl2303_type_data *type; + unsigned long quirks; ++ u16 interface_num; ++ u16 flowctrl_reg; + }; + + struct pl2303_private { +@@ -236,8 +255,30 @@ static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = { + .max_baud_rate = 12000000, + .no_divisors = true, + }, ++ [TYPE_MP] = { ++ .name = "MP", ++ .max_baud_rate = 24000000, ++ .no_divisors = true, ++ }, + }; + ++static int pl256x_uart_reset(struct usb_serial *serial) ++{ ++ struct pl2303_serial_private *spriv = usb_get_serial_data(serial); ++ struct device *dev = &serial->interface->dev; ++ int res; ++ ++ res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), ++ PL256X_RESET_REQUEST, PL256X_RESET_REQUEST_TYPE, ++ 0, spriv->interface_num, NULL, 0, 100); ++ if (res) { ++ dev_err(dev, "failed to reset device: %d\n", res); ++ return res; ++ } ++ ++ return 0; ++} ++ + static int pl2303_vendor_read(struct usb_serial *serial, u16 value, + unsigned char buf[1]) + { +@@ -248,6 +289,8 @@ static int pl2303_vendor_read(struct usb_serial *serial, u16 value, + + if (spriv->type == &pl2303_type_data[TYPE_HXN]) + request = VENDOR_READ_NREQUEST; ++ else if (spriv->type == &pl2303_type_data[TYPE_MP]) ++ request = VENDOR_READ_MPREQUEST; + else + request = VENDOR_READ_REQUEST; + +@@ -279,6 +322,8 @@ static int pl2303_vendor_write(struct usb_serial *serial, u16 value, u16 index) + + if (spriv->type == &pl2303_type_data[TYPE_HXN]) + request = VENDOR_WRITE_NREQUEST; ++ else if (spriv->type == &pl2303_type_data[TYPE_MP]) ++ request = VENDOR_WRITE_MPREQUEST; + else + request = VENDOR_WRITE_REQUEST; + +@@ -304,10 +349,12 @@ static int pl2303_update_reg(struct usb_serial *serial, u8 reg, u8 mask, u8 val) + if (!buf) + return -ENOMEM; + +- if (spriv->type == &pl2303_type_data[TYPE_HXN]) ++ if (spriv->type == &pl2303_type_data[TYPE_HXN] || ++ spriv->type == &pl2303_type_data[TYPE_MP]) { + ret = pl2303_vendor_read(serial, reg, buf); +- else ++ } else { + ret = pl2303_vendor_read(serial, reg | 0x80, buf); ++ } + + if (ret) + goto out_free; +@@ -458,6 +505,14 @@ static int pl2303_detect_type(struct usb_serial *serial) + case 0x905: /* GT-2AB */ + case 0x1005: /* GC-Q20 */ + return TYPE_HXN; ++ case 0x3302: /* PL2533 VC 2 Port */ ++ case 0x3304: /* PL2533 VC 4 Port */ ++ case 0x4302: /* PL2543 VC 2 Port */ ++ case 0x4304: /* PL2543 VC 4 Port */ ++ case 0x6502: /* PL2565 VC 2 Port */ ++ case 0x6504: /* PL2565 VC 4 Port */ ++ case 0x6506: /* PL2565 VC 4 Port QFN64 package */ ++ return TYPE_MP; + } + break; + } +@@ -491,6 +546,7 @@ static int pl2303_startup(struct usb_serial *serial) + struct pl2303_serial_private *spriv; + enum pl2303_type type; + unsigned char *buf; ++ unsigned int ifnum; + int ret; + + ret = pl2303_detect_type(serial); +@@ -500,20 +556,43 @@ static int pl2303_startup(struct usb_serial *serial) + type = ret; + dev_dbg(&serial->interface->dev, "device type: %s\n", pl2303_type_data[type].name); + ++ ifnum = serial->interface->altsetting->desc.bInterfaceNumber; ++ + spriv = kzalloc_obj(*spriv); + if (!spriv) + return -ENOMEM; + ++ if (type == TYPE_MP) { ++ switch (ifnum) { ++ case 0: ++ spriv->flowctrl_reg = PL256X_PORT_A_FLOWCTRL_REG; ++ break; ++ case 1: ++ spriv->flowctrl_reg = PL256X_PORT_B_FLOWCTRL_REG; ++ break; ++ case 2: ++ spriv->flowctrl_reg = PL256X_PORT_C_FLOWCTRL_REG; ++ break; ++ case 3: ++ spriv->flowctrl_reg = PL256X_PORT_D_FLOWCTRL_REG; ++ break; ++ default: ++ kfree(spriv); ++ return -ENODEV; ++ } ++ } ++ + spriv->type = &pl2303_type_data[type]; + spriv->quirks = (unsigned long)usb_get_serial_data(serial); + spriv->quirks |= spriv->type->quirks; ++ spriv->interface_num = ifnum; + + if (type == TYPE_HXD && pl2303_is_hxd_clone(serial)) + spriv->quirks |= PL2303_QUIRK_NO_BREAK_GETLINE; + + usb_set_serial_data(serial, spriv); + +- if (type != TYPE_HXN) { ++ if (type != TYPE_HXN && type != TYPE_MP) { + buf = kmalloc(1, GFP_KERNEL); + if (!buf) { + kfree(spriv); +@@ -575,13 +654,15 @@ static void pl2303_port_remove(struct usb_serial_port *port) + static int pl2303_set_control_lines(struct usb_serial_port *port, u8 value) + { + struct usb_device *dev = port->serial->dev; ++ struct usb_serial *serial = port->serial; ++ struct pl2303_serial_private *spriv = usb_get_serial_data(serial); + int retval; + + dev_dbg(&port->dev, "%s - %02x\n", __func__, value); + + retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE, +- value, 0, NULL, 0, 100); ++ value, spriv->interface_num, NULL, 0, 100); + if (retval) + dev_err(&port->dev, "%s - failed: %d\n", __func__, retval); + +@@ -761,7 +842,7 @@ static int pl2303_get_line_request(struct usb_serial_port *port, + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, +- 0, 0, buf, 7, 100); ++ 0, spriv->interface_num, buf, 7, 100); + if (ret != 7) { + dev_err(&port->dev, "%s - failed: %d\n", __func__, ret); + +@@ -780,11 +861,13 @@ static int pl2303_set_line_request(struct usb_serial_port *port, + unsigned char buf[7]) + { + struct usb_device *udev = port->serial->dev; ++ struct usb_serial *serial = port->serial; ++ struct pl2303_serial_private *spriv = usb_get_serial_data(serial); + int ret; + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE, +- 0, 0, buf, 7, 100); ++ 0, spriv->interface_num, buf, 7, 100); + if (ret < 0) { + dev_err(&port->dev, "%s - failed: %d\n", __func__, ret); + return ret; +@@ -939,6 +1022,9 @@ static void pl2303_set_termios(struct tty_struct *tty, + pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG, + PL2303_HXN_FLOWCTRL_MASK, + PL2303_HXN_FLOWCTRL_RTS_CTS); ++ } else if (spriv->type == &pl2303_type_data[TYPE_MP]) { ++ pl2303_vendor_write(serial, spriv->flowctrl_reg, ++ PL256X_FLOWCTRL_RTS_CTS); + } else { + pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0x60); + } +@@ -947,6 +1033,9 @@ static void pl2303_set_termios(struct tty_struct *tty, + pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG, + PL2303_HXN_FLOWCTRL_MASK, + PL2303_HXN_FLOWCTRL_XON_XOFF); ++ } else if (spriv->type == &pl2303_type_data[TYPE_MP]) { ++ pl2303_vendor_write(serial, spriv->flowctrl_reg, ++ PL256X_FLOWCTRL_XON_XOFF); + } else { + pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0xc0); + } +@@ -955,6 +1044,9 @@ static void pl2303_set_termios(struct tty_struct *tty, + pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG, + PL2303_HXN_FLOWCTRL_MASK, + PL2303_HXN_FLOWCTRL_NONE); ++ } else if (spriv->type == &pl2303_type_data[TYPE_MP]) { ++ pl2303_vendor_write(serial, spriv->flowctrl_reg, ++ PL256X_FLOWCTRL_NONE); + } else { + pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0); + } +@@ -1002,6 +1094,8 @@ static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port) + pl2303_vendor_write(serial, PL2303_HXN_RESET_REG, + PL2303_HXN_RESET_UPSTREAM_PIPE | + PL2303_HXN_RESET_DOWNSTREAM_PIPE); ++ } else if (spriv->type == &pl2303_type_data[TYPE_MP]) { ++ pl256x_uart_reset(serial); + } else { + pl2303_vendor_write(serial, 8, 0); + pl2303_vendor_write(serial, 9, 0); +@@ -1112,7 +1206,7 @@ static int pl2303_set_break(struct usb_serial_port *port, bool enable) + + result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + BREAK_REQUEST, BREAK_REQUEST_TYPE, state, +- 0, NULL, 0, 100); ++ spriv->interface_num, NULL, 0, 100); + if (result) { + dev_err(&port->dev, "error sending break = %d\n", result); + return result; +diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h +--- a/drivers/usb/serial/pl2303.h ++++ b/drivers/usb/serial/pl2303.h +@@ -26,7 +26,7 @@ + #define PL2303_PRODUCT_ID_HCR331 0x331a + #define PL2303_PRODUCT_ID_MOTOROLA 0x0307 + #define PL2303_PRODUCT_ID_ZTEK 0xe1f1 +- ++#define PL256X_PRODUCT_ID_4P 0x2533 + + #define ATEN_VENDOR_ID 0x0557 + #define ATEN_VENDOR_ID2 0x0547 +diff --git a/drivers/usb/typec/altmodes/Kconfig b/drivers/usb/typec/altmodes/Kconfig +--- a/drivers/usb/typec/altmodes/Kconfig ++++ b/drivers/usb/typec/altmodes/Kconfig +@@ -5,6 +5,7 @@ menu "USB Type-C Alternate Mode drivers" + config TYPEC_DP_ALTMODE + tristate "DisplayPort Alternate Mode driver" + depends on DRM ++ select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF + help + DisplayPort USB Type-C Alternate Mode allows DisplayPort + displays and adapters to be attached to the USB Type-C +diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c +--- a/drivers/usb/typec/altmodes/displayport.c ++++ b/drivers/usb/typec/altmodes/displayport.c +@@ -764,6 +764,7 @@ int dp_altmode_probe(struct typec_altmode *alt) + struct typec_altmode *plug = typec_altmode_get_plug(alt, TYPEC_PLUG_SOP_P); + struct fwnode_handle *fwnode; + struct dp_altmode *dp; ++ u32 cap = DP_CAP_CAPABILITY(alt->vdo); + + /* Port can only be DFP_U. */ + if (typec_altmode_get_data_role(alt) != TYPEC_HOST) +@@ -778,6 +779,18 @@ int dp_altmode_probe(struct typec_altmode *alt) + return -ENODEV; + } + ++ /* ++ * Make sure the DisplayPort VDO is valid (VESA DPAM v2.1a, Section ++ * 5.4.1, Table 5-6, DP Capabilities VDO). A device exposing DP on a ++ * USB-C receptacle must advertise at least one pin assignment for the ++ * capability it claims, otherwise Alt Mode can never be configured. ++ */ ++ if ((cap == DP_CAP_DFP_D && !DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo)) || ++ (cap == DP_CAP_UFP_D && !DP_CAP_PIN_ASSIGN_UFP_D(alt->vdo))) { ++ typec_altmode_put_plug(plug); ++ return -ENODEV; ++ } ++ + dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL); + if (!dp) { + typec_altmode_put_plug(plug); +@@ -790,7 +803,6 @@ int dp_altmode_probe(struct typec_altmode *alt) + dp->alt = alt; + + alt->desc = "DisplayPort"; +- typec_altmode_set_ops(alt, &dp_altmode_ops); + + if (plug) { + plug->desc = "Displayport"; +@@ -811,6 +823,10 @@ int dp_altmode_probe(struct typec_altmode *alt) + if (plug) + typec_altmode_set_drvdata(plug, dp); + ++ if ((alt->vdo & DP_CAP_RECEPTACLE) && typec_cable_altmode_unsupported(alt)) ++ return 0; ++ ++ typec_altmode_set_ops(alt, &dp_altmode_ops); + if (!alt->mode_selection) { + dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER; + schedule_work(&dp->work); +diff --git a/drivers/usb/typec/altmodes/thunderbolt.c b/drivers/usb/typec/altmodes/thunderbolt.c +--- a/drivers/usb/typec/altmodes/thunderbolt.c ++++ b/drivers/usb/typec/altmodes/thunderbolt.c +@@ -284,6 +284,10 @@ static int tbt_altmode_probe(struct typec_altmode *alt) + + alt->desc = "Thunderbolt3"; + typec_altmode_set_drvdata(alt, tbt); ++ ++ if (typec_cable_altmode_unsupported(alt)) ++ return 0; ++ + typec_altmode_set_ops(alt, &tbt_altmode_ops); + + if (!alt->mode_selection && tbt_ready(alt)) { +diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c +--- a/drivers/usb/typec/class.c ++++ b/drivers/usb/typec/class.c +@@ -1429,6 +1429,77 @@ int typec_cable_is_active(struct typec_cable *cable) + } + EXPORT_SYMBOL_GPL(typec_cable_is_active); + ++enum typec_cable_altmode_support { ++ CABLE_SUPPORT_UNKNOWN, ++ CABLE_SUPPORTED, ++ CABLE_NOT_SUPPORTED, ++}; ++ ++static enum typec_cable_altmode_support ++typec_cable_check_altmode_support(struct typec_cable *cable, ++ struct typec_altmode *alt) ++{ ++ struct typec_altmode *plug; ++ u32 speed; ++ ++ /* ++ * Check if the cable has an e-marker, supports modal operation, and the ++ * SOP' altmode nodes are created. ++ */ ++ plug = typec_altmode_get_plug(alt, TYPEC_PLUG_SOP_P); ++ if (plug) { ++ typec_altmode_put_plug(plug); ++ return CABLE_SUPPORTED; ++ } ++ ++ /* The identity is not specified */ ++ if (!cable->identity) ++ return CABLE_SUPPORT_UNKNOWN; ++ ++ /* Non-e-marked cable */ ++ if (!cable->identity->id_header) ++ return CABLE_NOT_SUPPORTED; ++ ++ switch (PD_IDH_PTYPE(cable->identity->id_header)) { ++ case IDH_PTYPE_PCABLE: ++ speed = VDO_TYPEC_CABLE_SPEED(cable->identity->vdo[0]); ++ if (speed == CABLE_USB2_ONLY) ++ return CABLE_NOT_SUPPORTED; ++ return CABLE_SUPPORTED; ++ case IDH_PTYPE_ACABLE: ++ /* ++ * Active cables must establish an SOP' communication ++ * node. Since that check failed at the beginning of ++ * this function, this active cable does not support ++ * this specific altmode. ++ */ ++ return CABLE_NOT_SUPPORTED; ++ } ++ ++ return CABLE_SUPPORT_UNKNOWN; ++} ++ ++/** ++ * typec_cable_altmode_unsupported - Check if a cable restricts altmode ++ * @alt: The Alternate Mode to evaluate ++ * ++ * Returns true if the connected cable is incapable of handling the altmode. ++ */ ++bool typec_cable_altmode_unsupported(struct typec_altmode *alt) ++{ ++ enum typec_cable_altmode_support support = CABLE_SUPPORT_UNKNOWN; ++ struct typec_cable *cable; ++ ++ cable = typec_cable_get(typec_altmode2port(alt)); ++ if (cable) { ++ support = typec_cable_check_altmode_support(cable, alt); ++ typec_cable_put(cable); ++ } ++ ++ return support == CABLE_NOT_SUPPORTED; ++} ++EXPORT_SYMBOL_GPL(typec_cable_altmode_unsupported); ++ + /** + * typec_cable_set_identity - Report result from Discover Identity command + * @cable: The cable updated identity values +diff --git a/drivers/usb/typec/mux/tusb1046.c b/drivers/usb/typec/mux/tusb1046.c +--- a/drivers/usb/typec/mux/tusb1046.c ++++ b/drivers/usb/typec/mux/tusb1046.c +@@ -178,6 +178,7 @@ static const struct of_device_id tusb1046_match_table[] = { + {.compatible = "ti,tusb1046"}, + {}, + }; ++MODULE_DEVICE_TABLE(of, tusb1046_match_table); + + static struct i2c_driver tusb1046_driver = { + .driver = { +diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c +--- a/drivers/usb/typec/tcpm/tcpm.c ++++ b/drivers/usb/typec/tcpm/tcpm.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -212,6 +213,24 @@ static const char * const tcpm_ams_str[] = { + FOREACH_AMS(GENERATE_STRING) + }; + ++#define FOREACH_VDM_DISCOVERY(S) \ ++ S(VDM_DISCOVERY_UNKNOWN), \ ++ S(VDM_DISCOVERY_PARTNER_IDENT), \ ++ S(VDM_DISCOVERY_CABLE_IDENT), \ ++ S(VDM_DISCOVERY_PARTNER_SVIDS), \ ++ S(VDM_DISCOVERY_PARTNER_MODES), \ ++ S(VDM_DISCOVERY_CABLE_SVIDS), \ ++ S(VDM_DISCOVERY_CABLE_MODES), \ ++ S(VDM_DISCOVERY_COMPLETE) ++ ++enum vdm_discovery_states { ++ FOREACH_VDM_DISCOVERY(GENERATE_ENUM) ++}; ++ ++static const char * const vdm_discovery_state_strings[] = { ++ FOREACH_VDM_DISCOVERY(GENERATE_STRING) ++}; ++ + enum vdm_states { + VDM_STATE_ERR_BUSY = -3, + VDM_STATE_ERR_SEND = -2, +@@ -232,7 +251,9 @@ enum pd_msg_request { + PD_MSG_DATA_SINK_CAP, + PD_MSG_DATA_SOURCE_CAP, + PD_MSG_DATA_REV, +- PD_MSG_EXT_SINK_CAP_EXT ++ PD_MSG_EXT_SINK_CAP_EXT, ++ PD_MSG_DATA_BATT_STATUS, ++ PD_MSG_EXT_BATT_CAP, + }; + + enum adev_actions { +@@ -274,7 +295,7 @@ enum frs_typec_current { + #define ALTMODE_DISCOVERY_MAX (SVID_DISCOVERY_MAX * MODE_DISCOVERY_MAX) + + #define GET_SINK_CAP_RETRY_MS 100 +-#define SEND_DISCOVER_RETRY_MS 100 ++#define SEND_DISCOVERY_VDM_RETRY_MS 100 + + struct pd_mode_data { + int svid_index; /* current SVID index */ +@@ -387,7 +408,15 @@ struct pd_timings { + }; + + /* Convert microwatt to watt */ +-#define UW_TO_W(pow) ((pow) / 1000000) ++#define UW_TO_W(pow) (div_u64((pow), 1000000)) ++ ++/* ++ * As per USB PD Spec Rev 3.18 (Sec. 6.5.13.11), the number of fixed batteries ++ * that a port can be queried is restricted to 4. ++ */ ++#define MAX_NUM_FIXED_BATT 4 ++ ++#define BATTERY_PROPERTY_UNKNOWN 0xffff + + /* + * struct pd_identifier - Contains info about PD identifiers +@@ -483,8 +512,6 @@ struct tcpm_port { + bool vbus_source; + bool vbus_charge; + +- /* Set to true when Discover_Identity Command is expected to be sent in Ready states. */ +- bool send_discover; + bool op_vsafe5v; + + int try_role; +@@ -510,8 +537,8 @@ struct tcpm_port { + struct kthread_work vdm_state_machine; + struct hrtimer enable_frs_timer; + struct kthread_work enable_frs; +- struct hrtimer send_discover_timer; +- struct kthread_work send_discover_work; ++ struct hrtimer vdm_discovery_timer; ++ struct kthread_work vdm_discovery_work; + bool state_machine_running; + /* Set to true when VDM State Machine has following actions. */ + bool vdm_sm_running; +@@ -578,6 +605,9 @@ struct tcpm_port { + + u32 bist_request; + ++ /* VDM Discovery State to determine message sent */ ++ enum vdm_discovery_states vdm_discovery_state; ++ + /* PD state for Vendor Defined Messages */ + enum vdm_states vdm_state; + u32 vdm_retries; +@@ -641,12 +671,6 @@ struct tcpm_port { + bool potential_contaminant; + + /* SOP* Related Fields */ +- /* +- * Flag to determine if SOP' Discover Identity is available. The flag +- * is set if Discover Identity on SOP' does not immediately follow +- * Discover Identity on SOP. +- */ +- bool send_discover_prime; + /* + * tx_sop_type determines which SOP* a message is being sent on. + * For messages that are queued and not sent immediately such as in +@@ -683,6 +707,9 @@ struct tcpm_port { + + struct pd_identifier pd_ident; + struct sink_caps_ext_data sink_caps_ext; ++ struct power_supply **fixed_batt; ++ u32 fixed_batt_cnt; ++ u32 batt_request_id; + #ifdef CONFIG_DEBUG_FS + struct dentry *dentry; + struct mutex logbuffer_lock; /* log buffer access lock */ +@@ -771,6 +798,9 @@ static const char * const pd_rev[] = { + #define tcpm_wait_for_discharge(port) \ + (((port)->auto_vbus_discharge_enabled && !(port)->vbus_vsafe0v) ? PD_T_SAFE_0V : 0) + ++#define tcpm_can_send_vdm(state) \ ++ ((state == SRC_READY || state == SNK_READY || state == SRC_VDM_IDENTITY_REQUEST)) ++ + static enum tcpm_state tcpm_default_state(struct tcpm_port *port) + { + if (port->port_type == TYPEC_PORT_DRP) { +@@ -1470,6 +1500,20 @@ static int tcpm_pd_send_sink_caps(struct tcpm_port *port) + return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); + } + ++static void tcpm_get_fixed_batt(struct tcpm_port *port) ++{ ++ int ret; ++ ++ if (!port->self_powered || port->fixed_batt_cnt > 0) ++ return; ++ ++ ret = power_supply_get_system_batteries(port->dev, &port->fixed_batt); ++ if (ret < 0) ++ tcpm_log(port, "Failed to get battery array, ret=%d", ret); ++ else ++ port->fixed_batt_cnt = ret; ++} ++ + static int tcpm_pd_send_sink_cap_ext(struct tcpm_port *port) + { + u16 operating_snk_watt = port->operating_snk_mw / 1000; +@@ -1482,6 +1526,8 @@ static int tcpm_pd_send_sink_cap_ext(struct tcpm_port *port) + if (!port->self_powered) + data->spr_op_pdp = operating_snk_watt; + ++ tcpm_get_fixed_batt(port); ++ + /* + * SPR Sink Minimum PDP indicates the minimum power required to operate + * a sink device in its lowest level of functionality without requiring +@@ -1507,6 +1553,7 @@ static int tcpm_pd_send_sink_cap_ext(struct tcpm_port *port) + skedb.load_step = data->load_step; + skedb.load_char = cpu_to_le16(data->load_char); + skedb.compliance = data->compliance; ++ skedb.batt_info = min(port->fixed_batt_cnt, MAX_NUM_FIXED_BATT); + skedb.modes = data->modes; + skedb.spr_min_pdp = data->spr_min_pdp; + skedb.spr_op_pdp = data->spr_op_pdp; +@@ -1525,6 +1572,153 @@ static int tcpm_pd_send_sink_cap_ext(struct tcpm_port *port) + port->message_id, + data_obj_cnt, + 1 /* Denotes if ext header */)); ++ ++ return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); ++} ++ ++static u16 tcpm_charge_to_energy(int charge, int voltage) ++{ ++ u64 energy = div_u64((u64)charge * voltage, 1000000); ++ ++ /* Battery telemetry is reported in increments of 0.1Wh */ ++ return (u16)UW_TO_W(energy * 10); ++} ++ ++static int tcpm_pd_send_batt_status(struct tcpm_port *port) ++{ ++ u16 present_charge = BATTERY_PROPERTY_UNKNOWN; ++ bool batt_present = false, invalid_ref = true; ++ u32 batt_id = port->batt_request_id; ++ union power_supply_propval val; ++ struct power_supply *batt; ++ u8 charging_status = 0; ++ struct pd_message msg; ++ int ret, charge_now; ++ u32 bsdo; ++ ++ tcpm_get_fixed_batt(port); ++ memset(&msg, 0, sizeof(msg)); ++ ++ if (batt_id >= port->fixed_batt_cnt || batt_id >= MAX_NUM_FIXED_BATT) ++ goto send_status; ++ ++ invalid_ref = false; ++ batt = port->fixed_batt[batt_id]; ++ ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_PRESENT, &val); ++ if (ret) ++ tcpm_log(port, ++ "Failed to fetch power_supply_prop_present ret %d", ++ ret); ++ else ++ batt_present = val.intval > 0; ++ ++ ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_CHARGE_NOW, ++ &val); ++ if (!ret) { ++ charge_now = val.intval; ++ ret = power_supply_get_property(batt, ++ POWER_SUPPLY_PROP_VOLTAGE_AVG, ++ &val); ++ if (!ret) ++ present_charge = tcpm_charge_to_energy(charge_now, ++ val.intval); ++ } ++ ++ ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_STATUS, &val); ++ if (!ret) { ++ switch (val.intval) { ++ case POWER_SUPPLY_STATUS_CHARGING: ++ charging_status = BSDO_BATTERY_INFO_CHARGING; ++ break; ++ case POWER_SUPPLY_STATUS_DISCHARGING: ++ charging_status = BSDO_BATTERY_INFO_DISCHARGING; ++ break; ++ case POWER_SUPPLY_STATUS_NOT_CHARGING: ++ case POWER_SUPPLY_STATUS_FULL: ++ charging_status = BSDO_BATTERY_INFO_IDLE; ++ break; ++ default: ++ charging_status = BSDO_BATTERY_INFO_RSVD; ++ break; ++ } ++ } ++ ++send_status: ++ ++ bsdo = BSDO(present_charge, charging_status, batt_present, invalid_ref); ++ msg.payload[0] = cpu_to_le32(bsdo); ++ msg.header = PD_HEADER_LE(PD_DATA_BATT_STATUS, ++ port->pwr_role, ++ port->data_role, ++ port->negotiated_rev, ++ port->message_id, ++ 1); ++ ++ return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); ++} ++ ++static int tcpm_pd_send_batt_cap(struct tcpm_port *port) ++{ ++ u16 design_cap = BATTERY_PROPERTY_UNKNOWN; ++ u16 charge_cap = BATTERY_PROPERTY_UNKNOWN; ++ u32 batt_id = port->batt_request_id; ++ union power_supply_propval val; ++ struct batt_cap_ext_msg bcdb; ++ struct power_supply *batt; ++ bool invalid_ref = true; ++ struct pd_message msg; ++ u8 data_obj_cnt; ++ int ret, vol; ++ ++ tcpm_get_fixed_batt(port); ++ memset(&msg, 0, sizeof(msg)); ++ ++ if (batt_id >= port->fixed_batt_cnt || batt_id >= MAX_NUM_FIXED_BATT) ++ goto send_cap; ++ ++ invalid_ref = false; ++ batt = port->fixed_batt[batt_id]; ++ ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_VOLTAGE_AVG, ++ &val); ++ if (!ret) { ++ vol = val.intval; ++ ret = power_supply_get_property(batt, ++ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, ++ &val); ++ if (!ret) ++ design_cap = tcpm_charge_to_energy(val.intval, vol); ++ ++ ret = power_supply_get_property(batt, ++ POWER_SUPPLY_PROP_CHARGE_FULL, ++ &val); ++ if (!ret) ++ charge_cap = tcpm_charge_to_energy(val.intval, vol); ++ } ++ ++send_cap: ++ ++ /* ++ * As per the USB PD Rev3.1 v1.8 spec, if a battery VID (assigned by the ++ * USB-IF) does not exist or an invalid battery reference is made by the ++ * requestor, then set the VID field to 0xffff. If the VID field is ++ * 0xffff, set the PID field to 0. ++ */ ++ bcdb.vid = BATTERY_PROPERTY_UNKNOWN; ++ bcdb.pid = 0; ++ bcdb.batt_design_cap = cpu_to_le16(design_cap); ++ bcdb.batt_last_chg_cap = cpu_to_le16(charge_cap); ++ bcdb.batt_type = invalid_ref ? BATT_CAP_BATT_TYPE_INVALID_REF : 0; ++ memcpy(msg.ext_msg.data, &bcdb, sizeof(bcdb)); ++ msg.ext_msg.header = PD_EXT_HDR_LE(sizeof(bcdb), ++ 0, /* Denotes if request chunk */ ++ 0, /* Chunk number */ ++ 1 /* Chunked */); ++ ++ data_obj_cnt = count_chunked_data_objs(sizeof(bcdb)); ++ msg.header = PD_HEADER_EXT_LE(PD_EXT_BATT_CAP, port->pwr_role, ++ port->data_role, port->negotiated_rev, ++ port->message_id, data_obj_cnt); ++ + return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); + } + +@@ -1559,13 +1753,19 @@ static void mod_enable_frs_delayed_work(struct tcpm_port *port, unsigned int del + } + } + +-static void mod_send_discover_delayed_work(struct tcpm_port *port, unsigned int delay_ms) ++static void mod_vdm_discovery_cancel_delayed_work(struct tcpm_port *port) ++{ ++ hrtimer_cancel(&port->vdm_discovery_timer); ++ kthread_cancel_work_sync(&port->vdm_discovery_work); ++} ++ ++static void mod_vdm_discovery_delayed_work(struct tcpm_port *port, unsigned int delay_ms) + { + if (delay_ms) { +- hrtimer_start(&port->send_discover_timer, ms_to_ktime(delay_ms), HRTIMER_MODE_REL); ++ hrtimer_start(&port->vdm_discovery_timer, ms_to_ktime(delay_ms), HRTIMER_MODE_REL); + } else { +- hrtimer_cancel(&port->send_discover_timer); +- kthread_queue_work(port->wq, &port->send_discover_work); ++ hrtimer_cancel(&port->vdm_discovery_timer); ++ kthread_queue_work(port->wq, &port->vdm_discovery_work); + } + } + +@@ -1773,16 +1973,11 @@ static void tcpm_queue_vdm(struct tcpm_port *port, const u32 header, + WARN_ON(!mutex_is_locked(&port->lock)); + + /* If is sending discover_identity, handle received message first */ +- if (PD_VDO_SVDM(vdo_hdr) && PD_VDO_CMD(vdo_hdr) == CMD_DISCOVER_IDENT) { +- if (tx_sop_type == TCPC_TX_SOP_PRIME) +- port->send_discover_prime = true; +- else +- port->send_discover = true; +- mod_send_discover_delayed_work(port, SEND_DISCOVER_RETRY_MS); +- } else { ++ if (PD_VDO_SVDM(vdo_hdr) && PD_VDO_CMD(vdo_hdr) == CMD_DISCOVER_IDENT) ++ mod_vdm_discovery_delayed_work(port, SEND_DISCOVERY_VDM_RETRY_MS); ++ else + /* Make sure we are not still processing a previous VDM packet */ + WARN_ON(port->vdm_state > VDM_STATE_DONE); +- } + + port->vdo_count = cnt + 1; + port->vdo_data[0] = header; +@@ -1805,8 +2000,7 @@ static void tcpm_queue_vdm_work(struct kthread_work *work) + struct tcpm_port *port = event->port; + + mutex_lock(&port->lock); +- if (port->state != SRC_READY && port->state != SNK_READY && +- port->state != SRC_VDM_IDENTITY_REQUEST) { ++ if (!tcpm_can_send_vdm(port->state)) { + tcpm_log_force(port, "dropping altmode_vdm_event"); + goto port_unlock; + } +@@ -2034,7 +2228,7 @@ static void tcpm_register_partner_altmodes(struct tcpm_port *port) + if (!port->partner) + return; + +- for (i = 0; i < modep->altmodes; i++) { ++ for (i = 0; i < modep->altmodes && i < ALTMODE_DISCOVERY_MAX; i++) { + altmode = typec_partner_register_altmode(port->partner, + &modep->altmode_desc[i]); + if (IS_ERR(altmode)) { +@@ -2052,9 +2246,10 @@ static void tcpm_register_plug_altmodes(struct tcpm_port *port) + struct typec_altmode *altmode; + int i; + +- typec_plug_set_num_altmodes(port->plug_prime, modep->altmodes); ++ typec_plug_set_num_altmodes(port->plug_prime, ++ min(modep->altmodes, ALTMODE_DISCOVERY_MAX)); + +- for (i = 0; i < modep->altmodes; i++) { ++ for (i = 0; i < modep->altmodes && i < ALTMODE_DISCOVERY_MAX; i++) { + altmode = typec_plug_register_altmode(port->plug_prime, + &modep->altmode_desc[i]); + if (IS_ERR(altmode)) { +@@ -2154,6 +2349,19 @@ static bool tcpm_cable_vdm_supported(struct tcpm_port *port) + tcpm_can_communicate_sop_prime(port); + } + ++static void tcpm_update_vdm_discovery_state(struct tcpm_port *port, ++ enum vdm_discovery_states new_state) ++{ ++ enum vdm_discovery_states old_state = port->vdm_discovery_state; ++ ++ if (old_state != new_state) ++ tcpm_log_force(port, "vdm discovery state changed: %s -> %s", ++ vdm_discovery_state_strings[old_state], ++ vdm_discovery_state_strings[new_state]); ++ ++ port->vdm_discovery_state = new_state; ++} ++ + static int tcpm_handle_discover_mode(struct tcpm_port *port, u32 *response, + enum tcpm_transmit_type rx_sop_type, + enum tcpm_transmit_type *response_tx_sop_type) +@@ -2171,6 +2379,7 @@ static int tcpm_handle_discover_mode(struct tcpm_port *port, u32 *response, + response[0] = VDO(svid, 1, + typec_get_negotiated_svdm_version(typec), + CMD_DISCOVER_MODES); ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_PARTNER_MODES); + return 1; + } + +@@ -2179,10 +2388,12 @@ static int tcpm_handle_discover_mode(struct tcpm_port *port, u32 *response, + response[0] = VDO(USB_SID_PD, 1, + typec_get_cable_svdm_version(typec), + CMD_DISCOVER_SVID); ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_PARTNER_MODES); + return 1; + } + + tcpm_register_partner_altmodes(port); ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); + } else if (rx_sop_type == TCPC_TX_SOP_PRIME) { + modep = &port->mode_data_prime; + modep->svid_index++; +@@ -2193,11 +2404,13 @@ static int tcpm_handle_discover_mode(struct tcpm_port *port, u32 *response, + response[0] = VDO(svid, 1, + typec_get_cable_svdm_version(typec), + CMD_DISCOVER_MODES); ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_CABLE_MODES); + return 1; + } + + tcpm_register_plug_altmodes(port); + tcpm_register_partner_altmodes(port); ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); + } + + return 0; +@@ -2376,18 +2589,18 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, + typec_cable_set_svdm_version(port->cable, + svdm_version); + } ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_PARTNER_IDENT); ++ + /* 6.4.4.3.1 */ + svdm_consume_identity(port, p, cnt); + /* Attempt Vconn swap, delay SOP' discovery if necessary */ + if (tcpm_attempt_vconn_swap_discovery(port)) { +- port->send_discover_prime = true; + port->upcoming_state = VCONN_SWAP_SEND; + ret = tcpm_ams_start(port, VCONN_SWAP); + if (!ret) + return 0; + /* Cannot perform Vconn swap */ + port->upcoming_state = INVALID_STATE; +- port->send_discover_prime = false; + } + + /* +@@ -2398,7 +2611,6 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, + if (IS_ERR_OR_NULL(port->cable) && + tcpm_can_communicate_sop_prime(port)) { + *response_tx_sop_type = TCPC_TX_SOP_PRIME; +- port->send_discover_prime = true; + response[0] = VDO(USB_SID_PD, 1, + typec_get_negotiated_svdm_version(typec), + CMD_DISCOVER_IDENT); +@@ -2426,6 +2638,7 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, + tcpm_set_state(port, SRC_SEND_CAPABILITIES, 0); + return 0; + } ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_CABLE_IDENT); + + *response_tx_sop_type = TCPC_TX_SOP; + response[0] = VDO(USB_SID_PD, 1, +@@ -2445,16 +2658,27 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, + rlen = 1; + } else { + if (rx_sop_type == TCPC_TX_SOP) { ++ tcpm_update_vdm_discovery_state(port, ++ VDM_DISCOVERY_PARTNER_SVIDS); + if (modep->nsvids && supports_modal(port)) { + response[0] = VDO(modep->svids[0], 1, svdm_version, + CMD_DISCOVER_MODES); + rlen = 1; ++ } else { ++ tcpm_update_vdm_discovery_state(port, ++ VDM_DISCOVERY_COMPLETE); + } + } else if (rx_sop_type == TCPC_TX_SOP_PRIME) { ++ tcpm_update_vdm_discovery_state(port, ++ VDM_DISCOVERY_CABLE_SVIDS); + if (modep_prime->nsvids) { + response[0] = VDO(modep_prime->svids[0], 1, + svdm_version, CMD_DISCOVER_MODES); + rlen = 1; ++ } else { ++ tcpm_register_partner_altmodes(port); ++ tcpm_update_vdm_discovery_state(port, ++ VDM_DISCOVERY_COMPLETE); + } + } + } +@@ -2505,8 +2729,13 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, + case CMDT_RSP_NAK: + tcpm_ams_finish(port); + switch (cmd) { ++ /* ++ * The cable is not allowed to respond with NAK so this must've happened over SOP ++ */ + case CMD_DISCOVER_IDENT: + case CMD_DISCOVER_SVID: ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); ++ break; + case VDO_CMD_VENDOR(0) ... VDO_CMD_VENDOR(15): + break; + case CMD_DISCOVER_MODES: +@@ -2686,44 +2915,6 @@ static void tcpm_handle_vdm_request(struct tcpm_port *port, + port->vdm_sm_running = false; + } + +-static void tcpm_send_vdm(struct tcpm_port *port, u32 vid, int cmd, +- const u32 *data, int count, enum tcpm_transmit_type tx_sop_type) +-{ +- int svdm_version; +- u32 header; +- +- switch (tx_sop_type) { +- case TCPC_TX_SOP_PRIME: +- /* +- * If the port partner is discovered, then the port partner's +- * SVDM Version will be returned +- */ +- svdm_version = typec_get_cable_svdm_version(port->typec_port); +- if (svdm_version < 0) +- svdm_version = SVDM_VER_MAX; +- break; +- case TCPC_TX_SOP: +- svdm_version = typec_get_negotiated_svdm_version(port->typec_port); +- if (svdm_version < 0) +- return; +- break; +- default: +- svdm_version = typec_get_negotiated_svdm_version(port->typec_port); +- if (svdm_version < 0) +- return; +- break; +- } +- +- if (WARN_ON(count > VDO_MAX_SIZE - 1)) +- count = VDO_MAX_SIZE - 1; +- +- /* set VDM header with VID & CMD */ +- header = VDO(vid, ((vid & USB_SID_PD) == USB_SID_PD) ? +- 1 : (PD_VDO_CMD(cmd) <= CMD_ATTENTION), +- svdm_version, cmd); +- tcpm_queue_vdm(port, header, data, count, tx_sop_type); +-} +- + static unsigned int vdm_ready_timeout(u32 vdm_hdr) + { + unsigned int timeout; +@@ -2769,8 +2960,7 @@ static void vdm_run_state_machine(struct tcpm_port *port) + * if there's traffic or we're not in PDO ready state don't send + * a VDM. + */ +- if (port->state != SRC_READY && port->state != SNK_READY && +- port->state != SRC_VDM_IDENTITY_REQUEST) { ++ if (!tcpm_can_send_vdm(port->state)) { + port->vdm_sm_running = false; + break; + } +@@ -2780,22 +2970,10 @@ static void vdm_run_state_machine(struct tcpm_port *port) + switch (PD_VDO_CMD(vdo_hdr)) { + case CMD_DISCOVER_IDENT: + res = tcpm_ams_start(port, DISCOVER_IDENTITY); +- if (res == 0) { +- switch (port->tx_sop_type) { +- case TCPC_TX_SOP_PRIME: +- port->send_discover_prime = false; +- break; +- case TCPC_TX_SOP: +- port->send_discover = false; +- break; +- default: +- port->send_discover = false; +- break; +- } +- } else if (res == -EAGAIN) { ++ if (res == -EAGAIN) { + port->vdo_data[0] = 0; +- mod_send_discover_delayed_work(port, +- SEND_DISCOVER_RETRY_MS); ++ mod_vdm_discovery_delayed_work(port, ++ SEND_DISCOVERY_VDM_RETRY_MS); + } + break; + case CMD_DISCOVER_SVID: +@@ -2853,6 +3031,7 @@ static void vdm_run_state_machine(struct tcpm_port *port) + */ + if (port->state == SRC_VDM_IDENTITY_REQUEST) { + tcpm_ams_finish(port); ++ port->vdo_data[0] = 0; + port->vdm_state = VDM_STATE_DONE; + tcpm_set_state(port, SRC_SEND_CAPABILITIES, 0); + /* +@@ -2869,6 +3048,7 @@ static void vdm_run_state_machine(struct tcpm_port *port) + tcpm_ams_finish(port); + } else { + tcpm_ams_finish(port); ++ port->vdo_data[0] = 0; + if (port->tx_sop_type == TCPC_TX_SOP) + break; + /* Handle SOP' Transmission Errors */ +@@ -2878,11 +3058,11 @@ static void vdm_run_state_machine(struct tcpm_port *port) + * discovery process on SOP only. + */ + case CMD_DISCOVER_IDENT: +- port->vdo_data[0] = 0; + response[0] = VDO(USB_SID_PD, 1, + typec_get_negotiated_svdm_version( + port->typec_port), + CMD_DISCOVER_SVID); ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_CABLE_IDENT); + tcpm_queue_vdm(port, response[0], &response[1], + 0, TCPC_TX_SOP); + break; +@@ -2892,9 +3072,11 @@ static void vdm_run_state_machine(struct tcpm_port *port) + */ + case CMD_DISCOVER_SVID: + tcpm_register_partner_altmodes(port); ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); + break; + case CMD_DISCOVER_MODES: + tcpm_register_partner_altmodes(port); ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); + break; + default: + break; +@@ -3807,7 +3989,8 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, + PD_MSG_CTRL_NOT_SUPP, + NONE_AMS); + } else { +- if (port->send_discover && port->negotiated_rev < PD_REV30) { ++ if (port->vdm_discovery_state == VDM_DISCOVERY_UNKNOWN && ++ port->negotiated_rev < PD_REV30) { + tcpm_queue_message(port, PD_MSG_CTRL_WAIT); + break; + } +@@ -3823,7 +4006,8 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, + PD_MSG_CTRL_NOT_SUPP, + NONE_AMS); + } else { +- if (port->send_discover && port->negotiated_rev < PD_REV30) { ++ if (port->vdm_discovery_state == VDM_DISCOVERY_UNKNOWN && ++ port->negotiated_rev < PD_REV30) { + tcpm_queue_message(port, PD_MSG_CTRL_WAIT); + break; + } +@@ -3832,7 +4016,8 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, + } + break; + case PD_CTRL_VCONN_SWAP: +- if (port->send_discover && port->negotiated_rev < PD_REV30) { ++ if (port->vdm_discovery_state == VDM_DISCOVERY_UNKNOWN && ++ port->negotiated_rev < PD_REV30) { + tcpm_queue_message(port, PD_MSG_CTRL_WAIT); + break; + } +@@ -3891,6 +4076,7 @@ static void tcpm_pd_ext_msg_request(struct tcpm_port *port, + { + enum pd_ext_msg_type type = pd_header_type_le(msg->header); + unsigned int data_size = pd_ext_header_data_size_le(msg->ext_msg.header); ++ const struct pd_chunked_ext_message_data *ext_msg = &msg->ext_msg; + + /* stopping VDM state machine if interrupted by other Messages */ + if (tcpm_vdm_ams(port)) { +@@ -3899,7 +4085,7 @@ static void tcpm_pd_ext_msg_request(struct tcpm_port *port, + mod_vdm_delayed_work(port, 0); + } + +- if (!(le16_to_cpu(msg->ext_msg.header) & PD_EXT_HDR_CHUNKED)) { ++ if (!(le16_to_cpu(ext_msg->header) & PD_EXT_HDR_CHUNKED)) { + tcpm_pd_handle_msg(port, PD_MSG_CTRL_NOT_SUPP, NONE_AMS); + tcpm_log(port, "Unchunked extended messages unsupported"); + return; +@@ -3924,9 +4110,25 @@ static void tcpm_pd_ext_msg_request(struct tcpm_port *port, + NONE_AMS, 0); + } + break; +- case PD_EXT_SOURCE_CAP_EXT: +- case PD_EXT_GET_BATT_CAP: + case PD_EXT_GET_BATT_STATUS: ++ if (data_size >= 1) { ++ port->batt_request_id = ext_msg->data[0]; ++ tcpm_pd_handle_msg(port, PD_MSG_DATA_BATT_STATUS, ++ GETTING_BATTERY_STATUS); ++ } else { ++ tcpm_set_state(port, SOFT_RESET_SEND, 0); ++ } ++ break; ++ case PD_EXT_GET_BATT_CAP: ++ if (data_size >= 1) { ++ port->batt_request_id = ext_msg->data[0]; ++ tcpm_pd_handle_msg(port, PD_MSG_EXT_BATT_CAP, ++ GETTING_BATTERY_CAPABILITIES); ++ } else { ++ tcpm_set_state(port, SOFT_RESET_SEND, 0); ++ } ++ break; ++ case PD_EXT_SOURCE_CAP_EXT: + case PD_EXT_BATT_CAP: + case PD_EXT_GET_MANUFACTURER_INFO: + case PD_EXT_MANUFACTURER_INFO: +@@ -4137,6 +4339,22 @@ static bool tcpm_send_queued_message(struct tcpm_port *port) + ret); + tcpm_ams_finish(port); + break; ++ case PD_MSG_DATA_BATT_STATUS: ++ ret = tcpm_pd_send_batt_status(port); ++ if (ret) ++ tcpm_log(port, ++ "Failed to send battery status ret=%d", ++ ret); ++ tcpm_ams_finish(port); ++ break; ++ case PD_MSG_EXT_BATT_CAP: ++ ret = tcpm_pd_send_batt_cap(port); ++ if (ret) ++ tcpm_log(port, ++ "Failed to send battery cap ret=%d", ++ ret); ++ tcpm_ams_finish(port); ++ break; + default: + break; + } +@@ -4853,8 +5071,7 @@ static int tcpm_src_attach(struct tcpm_port *port) + port->partner = NULL; + + port->attached = true; +- port->send_discover = true; +- port->send_discover_prime = false; ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_UNKNOWN); + + return 0; + +@@ -4896,11 +5113,11 @@ static void tcpm_unregister_altmodes(struct tcpm_port *port) + struct pd_mode_data *modep_prime = &port->mode_data_prime; + int i; + +- for (i = 0; i < modep->altmodes; i++) { ++ for (i = 0; i < modep->altmodes && i < ALTMODE_DISCOVERY_MAX; i++) { + typec_unregister_altmode(port->partner_altmode[i]); + port->partner_altmode[i] = NULL; + } +- for (i = 0; i < modep_prime->altmodes; i++) { ++ for (i = 0; i < modep_prime->altmodes && i < ALTMODE_DISCOVERY_MAX; i++) { + typec_unregister_altmode(port->plug_prime_altmode[i]); + port->plug_prime_altmode[i] = NULL; + } +@@ -4931,6 +5148,8 @@ static void tcpm_reset_port(struct tcpm_port *port) + port->in_ams = false; + port->ams = NONE_AMS; + port->vdm_sm_running = false; ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_UNKNOWN); ++ mod_vdm_discovery_cancel_delayed_work(port); + tcpm_unregister_altmodes(port); + tcpm_typec_disconnect(port); + port->attached = false; +@@ -5014,8 +5233,7 @@ static int tcpm_snk_attach(struct tcpm_port *port) + port->partner = NULL; + + port->attached = true; +- port->send_discover = true; +- port->send_discover_prime = false; ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_UNKNOWN); + + return 0; + } +@@ -5422,16 +5640,11 @@ static void run_state_machine(struct tcpm_port *port) + * as well. + */ + if (port->explicit_contract) { +- if (port->send_discover_prime) { +- port->tx_sop_type = TCPC_TX_SOP_PRIME; +- } else { +- port->tx_sop_type = TCPC_TX_SOP; ++ if (port->vdm_discovery_state == VDM_DISCOVERY_UNKNOWN) + tcpm_set_initial_svdm_version(port); +- } +- mod_send_discover_delayed_work(port, 0); ++ mod_vdm_discovery_delayed_work(port, 0); + } else { +- port->send_discover = false; +- port->send_discover_prime = false; ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); + } + + /* +@@ -5814,16 +6027,11 @@ static void run_state_machine(struct tcpm_port *port) + * as well. + */ + if (port->explicit_contract) { +- if (port->send_discover_prime) { +- port->tx_sop_type = TCPC_TX_SOP_PRIME; +- } else { +- port->tx_sop_type = TCPC_TX_SOP; ++ if (port->vdm_discovery_state == VDM_DISCOVERY_UNKNOWN) + tcpm_set_initial_svdm_version(port); +- } +- mod_send_discover_delayed_work(port, 0); ++ mod_vdm_discovery_delayed_work(port, 0); + } else { +- port->send_discover = false; +- port->send_discover_prime = false; ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); + } + + power_supply_changed(port->psy); +@@ -5869,8 +6077,8 @@ static void run_state_machine(struct tcpm_port *port) + port->tcpc->set_pd_rx(port->tcpc, false); + tcpm_unregister_altmodes(port); + port->nr_sink_caps = 0; +- port->send_discover = true; +- port->send_discover_prime = false; ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_UNKNOWN); ++ mod_vdm_discovery_cancel_delayed_work(port); + if (port->pwr_role == TYPEC_SOURCE) + tcpm_set_state(port, SRC_HARD_RESET_VBUS_OFF, + PD_T_PS_HARD_RESET); +@@ -6017,25 +6225,15 @@ static void run_state_machine(struct tcpm_port *port) + /* DR_Swap states */ + case DR_SWAP_SEND: + tcpm_pd_send_control(port, PD_CTRL_DR_SWAP, TCPC_TX_SOP); +- if (port->data_role == TYPEC_DEVICE || port->negotiated_rev > PD_REV20) { +- port->send_discover = true; +- port->send_discover_prime = false; +- } + tcpm_set_state_cond(port, DR_SWAP_SEND_TIMEOUT, + PD_T_SENDER_RESPONSE); + break; + case DR_SWAP_ACCEPT: + tcpm_pd_send_control(port, PD_CTRL_ACCEPT, TCPC_TX_SOP); +- if (port->data_role == TYPEC_DEVICE || port->negotiated_rev > PD_REV20) { +- port->send_discover = true; +- port->send_discover_prime = false; +- } + tcpm_set_state_cond(port, DR_SWAP_CHANGE_DR, 0); + break; + case DR_SWAP_SEND_TIMEOUT: + tcpm_swap_complete(port, -ETIMEDOUT); +- port->send_discover = false; +- port->send_discover_prime = false; + tcpm_ams_finish(port); + tcpm_set_state(port, ready_state(port), 0); + break; +@@ -6047,6 +6245,8 @@ static void run_state_machine(struct tcpm_port *port) + else + tcpm_set_roles(port, true, TYPEC_STATE_USB, port->pwr_role, + TYPEC_HOST); ++ if (port->data_role == TYPEC_HOST || port->negotiated_rev > PD_REV20) ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_UNKNOWN); + tcpm_ams_finish(port); + tcpm_set_state(port, ready_state(port), 0); + break; +@@ -6330,9 +6530,7 @@ static void run_state_machine(struct tcpm_port *port) + + /* Cable states */ + case SRC_VDM_IDENTITY_REQUEST: +- port->send_discover_prime = true; +- port->tx_sop_type = TCPC_TX_SOP_PRIME; +- mod_send_discover_delayed_work(port, 0); ++ mod_vdm_discovery_delayed_work(port, 0); + port->upcoming_state = SRC_SEND_CAPABILITIES; + break; + +@@ -7052,8 +7250,8 @@ static void tcpm_enable_frs_work(struct kthread_work *work) + goto unlock; + + /* Send when the state machine is idle */ +- if (port->state != SNK_READY || port->vdm_sm_running || port->send_discover || +- port->send_discover_prime) ++ if (port->state != SNK_READY || port->vdm_sm_running || ++ port->vdm_discovery_state == VDM_DISCOVERY_UNKNOWN) + goto resched; + + port->upcoming_state = GET_SINK_CAP; +@@ -7070,29 +7268,160 @@ static void tcpm_enable_frs_work(struct kthread_work *work) + mutex_unlock(&port->lock); + } + +-static void tcpm_send_discover_work(struct kthread_work *work) ++static void tcpm_vdm_discovery_work(struct kthread_work *work) + { +- struct tcpm_port *port = container_of(work, struct tcpm_port, send_discover_work); ++ struct tcpm_port *port = container_of(work, struct tcpm_port, vdm_discovery_work); ++ enum tcpm_transmit_type tx_sop_type = TCPC_TX_SOP; ++ struct typec_port *typec = port->typec_port; ++ struct pd_mode_data *modep, *modep_prime; ++ u32 msg[2] = { }; ++ int svdm_version; + + mutex_lock(&port->lock); +- /* No need to send DISCOVER_IDENTITY anymore */ +- if (!port->send_discover && !port->send_discover_prime) +- goto unlock; + +- if (port->data_role == TYPEC_DEVICE && port->negotiated_rev < PD_REV30) { +- port->send_discover = false; +- port->send_discover_prime = false; ++ tcpm_log_force(port, "%s state [%s]", __func__, ++ vdm_discovery_state_strings[port->vdm_discovery_state]); ++ ++ /* No need to perform work if Discovery process is complete */ ++ if (port->vdm_discovery_state == VDM_DISCOVERY_COMPLETE) + goto unlock; +- } + + /* Retry if the port is not idle */ +- if ((port->state != SRC_READY && port->state != SNK_READY && +- port->state != SRC_VDM_IDENTITY_REQUEST) || port->vdm_sm_running) { +- mod_send_discover_delayed_work(port, SEND_DISCOVER_RETRY_MS); ++ if (!tcpm_can_send_vdm(port->state) || port->vdm_sm_running) { ++ mod_vdm_discovery_delayed_work(port, SEND_DISCOVERY_VDM_RETRY_MS); + goto unlock; + } + +- tcpm_send_vdm(port, USB_SID_PD, CMD_DISCOVER_IDENT, NULL, 0, port->tx_sop_type); ++ modep = &port->mode_data; ++ modep_prime = &port->mode_data_prime; ++ ++ svdm_version = typec_get_negotiated_svdm_version(typec); ++ ++ switch (port->vdm_discovery_state) { ++ /* ++ * The port has not received a Discover Identity response from the port partner. ++ * ++ * 1. The port will send Discover Identity to the partner over SOP in the SRC_READY and ++ * SNK_READY states if there is an explicit contract ++ * 2. The port will send Discover Identity to the cable over SOP' in the ++ * SRC_VDM_IDENTITY_REQUEST state if capable of doing so. ++ */ ++ case VDM_DISCOVERY_UNKNOWN: ++ /* Can't send Discover Identity, VDM discovery is complete */ ++ if (port->data_role == TYPEC_DEVICE && port->negotiated_rev < PD_REV30) { ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); ++ goto unlock; ++ } ++ ++ if (port->state == SRC_VDM_IDENTITY_REQUEST) { ++ tx_sop_type = TCPC_TX_SOP_PRIME; ++ svdm_version = SVDM_VER_MAX; ++ } ++ ++ msg[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_IDENT); ++ break; ++ /* ++ * The port has received a Discover Identity ACK from the port partner. ++ * ++ * 1. The port will send Discover Identity to the cable over SOP' in the SRC_READY and ++ * SNK_READY states if it did not previously discover the cable but is capable of doing ++ * so. ++ * 2. The port will send Discover SVIDs to the partner over SOP in the SRC_READY and ++ * SNK_READY states otherwise. ++ */ ++ case VDM_DISCOVERY_PARTNER_IDENT: ++ if (tcpm_can_communicate_sop_prime(port) && !port->cable) { ++ tx_sop_type = TCPC_TX_SOP_PRIME; ++ msg[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_IDENT); ++ } else { ++ if (tcpm_can_communicate_sop_prime(port)) ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_CABLE_IDENT); ++ msg[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_SVID); ++ } ++ break; ++ /* ++ * The port has received a Discover Identity ACK from the cable. ++ * ++ * 1. The port will send Discover SVIDs to the partner over SOP. ++ */ ++ case VDM_DISCOVERY_CABLE_IDENT: ++ msg[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_SVID); ++ break; ++ /* ++ * The port has received a Discover SVIDs ACK from the partner or the last SVIDs supported ++ * by the partner. ++ * ++ * 1. The port will send Discover Modes for the first SVID over SOP if the partner supports ++ * modal operation and valid SVIDs were registered. ++ * 2. The vdm_discovery_state will move to VDM_DISCOVERY_COMPLETE otherwise. ++ */ ++ case VDM_DISCOVERY_PARTNER_SVIDS: ++ if (modep->nsvids && supports_modal(port)) { ++ msg[0] = VDO(modep->svids[0], 1, svdm_version, CMD_DISCOVER_MODES); ++ } else { ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); ++ goto unlock; ++ } ++ break; ++ /* ++ * The port has received a Discover Modes ACK from the partner for any mode. ++ * ++ * 1. The port will send Discover Modes for the next SVID that has not been discovered to ++ * the port partner over SOP. ++ * 2. The port will send Discover SVIDs over SOP' if the port can communicate over SOP' ++ * and the cable supports VDMs. ++ * 3. The vdm_discovery_state will move to VDM_DISCOVERY_COMPLETE otherwise. ++ */ ++ case VDM_DISCOVERY_PARTNER_MODES: ++ /* Not all modes have been discovered yet */ ++ if (modep->svid_index < modep->nsvids) { ++ msg[0] = VDO(modep_prime->svids[modep->svid_index], 1, svdm_version, ++ CMD_DISCOVER_MODES); ++ } else if (tcpm_can_communicate_sop_prime(port) && tcpm_cable_vdm_supported(port)) { ++ tx_sop_type = TCPC_TX_SOP_PRIME; ++ svdm_version = typec_get_cable_svdm_version(typec); ++ msg[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_SVID); ++ } else { ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); ++ goto unlock; ++ } ++ break; ++ /* ++ * The port has received a Discover SVIDs ACK from the cable over SOP'. ++ * ++ * 1. The port will send Discover Modes for the first SVID over SOP'. ++ */ ++ case VDM_DISCOVERY_CABLE_SVIDS: ++ if (modep_prime->nsvids) { ++ tx_sop_type = TCPC_TX_SOP_PRIME; ++ svdm_version = typec_get_cable_svdm_version(typec); ++ msg[0] = VDO(modep_prime->svids[0], 1, svdm_version, CMD_DISCOVER_MODES); ++ } else { ++ goto unlock; ++ } ++ break; ++ /* ++ * The port has received a Discover Modes ACK from the cable for any mode. ++ * ++ * 1. The port will send Discover Modes for the next SVID that has not been discovered to ++ * the cable over SOP'. ++ */ ++ case VDM_DISCOVERY_CABLE_MODES: ++ if (modep_prime->svid_index < modep_prime->nsvids) { ++ tx_sop_type = TCPC_TX_SOP_PRIME; ++ svdm_version = typec_get_cable_svdm_version(typec); ++ msg[0] = VDO(modep_prime->svids[modep->svid_index], 1, svdm_version, ++ CMD_DISCOVER_MODES); ++ } else { ++ goto unlock; ++ } ++ break; ++ default: ++ goto unlock; ++ } ++ ++ if (svdm_version >= 0) ++ tcpm_queue_vdm(port, msg[0], &msg[1], 0, tx_sop_type); + + unlock: + mutex_unlock(&port->lock); +@@ -8508,12 +8837,12 @@ static enum hrtimer_restart enable_frs_timer_handler(struct hrtimer *timer) + return HRTIMER_NORESTART; + } + +-static enum hrtimer_restart send_discover_timer_handler(struct hrtimer *timer) ++static enum hrtimer_restart vdm_discovery_timer_handler(struct hrtimer *timer) + { +- struct tcpm_port *port = container_of(timer, struct tcpm_port, send_discover_timer); ++ struct tcpm_port *port = container_of(timer, struct tcpm_port, vdm_discovery_timer); + + if (port->registered) +- kthread_queue_work(port->wq, &port->send_discover_work); ++ kthread_queue_work(port->wq, &port->vdm_discovery_work); + return HRTIMER_NORESTART; + } + +@@ -8547,14 +8876,14 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) + kthread_init_work(&port->vdm_state_machine, vdm_state_machine_work); + kthread_init_work(&port->event_work, tcpm_pd_event_handler); + kthread_init_work(&port->enable_frs, tcpm_enable_frs_work); +- kthread_init_work(&port->send_discover_work, tcpm_send_discover_work); ++ kthread_init_work(&port->vdm_discovery_work, tcpm_vdm_discovery_work); + hrtimer_setup(&port->state_machine_timer, state_machine_timer_handler, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + hrtimer_setup(&port->vdm_state_machine_timer, vdm_state_machine_timer_handler, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hrtimer_setup(&port->enable_frs_timer, enable_frs_timer_handler, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); +- hrtimer_setup(&port->send_discover_timer, send_discover_timer_handler, CLOCK_MONOTONIC, ++ hrtimer_setup(&port->vdm_discovery_timer, vdm_discovery_timer_handler, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + + spin_lock_init(&port->pd_event_lock); +@@ -8649,11 +8978,12 @@ void tcpm_unregister_port(struct tcpm_port *port) + port->registered = false; + kthread_destroy_worker(port->wq); + +- hrtimer_cancel(&port->send_discover_timer); ++ hrtimer_cancel(&port->vdm_discovery_timer); + hrtimer_cancel(&port->enable_frs_timer); + hrtimer_cancel(&port->vdm_state_machine_timer); + hrtimer_cancel(&port->state_machine_timer); + ++ power_supply_put_system_batteries(port->fixed_batt, port->fixed_batt_cnt); + tcpm_reset_port(port); + + tcpm_port_unregister_pd(port); +diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c +--- a/drivers/usb/typec/tipd/core.c ++++ b/drivers/usb/typec/tipd/core.c +@@ -129,6 +129,7 @@ enum { + TPS_MODE_BIST, + TPS_MODE_DISC, + TPS_MODE_PTCH, ++ TPS_MODE_APP1, + }; + + static const char *const modes[] = { +@@ -137,6 +138,7 @@ static const char *const modes[] = { + [TPS_MODE_BIST] = "BIST", + [TPS_MODE_DISC] = "DISC", + [TPS_MODE_PTCH] = "PTCH", ++ [TPS_MODE_APP1] = "APP1", + }; + + /* Unrecognized commands will be replaced with "!CMD" */ +@@ -158,6 +160,7 @@ struct tipd_data { + int (*init)(struct tps6598x *tps); + int (*switch_power_state)(struct tps6598x *tps, u8 target_state); + bool (*read_data_status)(struct tps6598x *tps); ++ bool (*read_power_status)(struct tps6598x *tps); + int (*reset)(struct tps6598x *tps); + int (*connect)(struct tps6598x *tps, u32 status); + }; +@@ -629,6 +632,34 @@ static bool tps6598x_read_power_status(struct tps6598x *tps) + return true; + } + ++/* ++ * TPS66993 deprecated Power_Status register (0x3F). BC1.2 is not supported ++ * and the remaining bits are redundant with STATUS register (0x1A). ++ * Synthesize pwr_status from the already-read STATUS register. ++ */ ++static bool tps66993_read_power_status(struct tps6598x *tps) ++{ ++ u16 pwr_status = 0; ++ ++ /* Same masks as TPS_POWER_STATUS_CONNECTION() / SOURCESINK() / PWROPMODE() in tps6598x.h */ ++ if (tps->status & TPS_STATUS_PLUG_PRESENT) ++ pwr_status |= FIELD_PREP(TPS_POWER_STATUS_CONNECTION_MASK, 1); ++ ++ /* SOURCESINK: 1=sink; STATUS.PortRole 1=source, opposite convention */ ++ if (!TPS_STATUS_TO_TYPEC_PORTROLE(tps->status)) ++ pwr_status |= FIELD_PREP(TPS_POWER_STATUS_SOURCESINK_MASK, 1); ++ ++ if (TPS_STATUS_VBUS_STATUS(tps->status) == TPS_STATUS_VBUS_STATUS_PD) ++ pwr_status |= FIELD_PREP(TPS_POWER_STATUS_TYPEC_CURRENT_MASK, ++ TPS_POWER_STATUS_TYPEC_CURRENT_PD); ++ ++ tps->pwr_status = pwr_status; ++ ++ tps->data->trace_power_status(pwr_status); ++ ++ return true; ++} ++ + static void tps6598x_handle_plug_event(struct tps6598x *tps, u32 status) + { + int ret; +@@ -906,7 +937,7 @@ static irqreturn_t cd321x_interrupt(int irq, void *data) + goto err_unlock; + + if (event & APPLE_CD_REG_INT_POWER_STATUS_UPDATE) { +- if (!tps6598x_read_power_status(tps)) ++ if (!tps->data->read_power_status(tps)) + goto err_unlock; + if (TPS_POWER_STATUS_PWROPMODE(tps->pwr_status) == TYPEC_PWR_MODE_PD) { + if (tps6598x_read_partner_identity(tps)) { +@@ -961,7 +992,7 @@ static irqreturn_t tps25750_interrupt(int irq, void *data) + goto err_clear_ints; + + if (event[0] & TPS_REG_INT_POWER_STATUS_UPDATE) +- if (!tps6598x_read_power_status(tps)) ++ if (!tps->data->read_power_status(tps)) + goto err_clear_ints; + + if (event[0] & TPS_REG_INT_DATA_STATUS_UPDATE) +@@ -1034,17 +1065,25 @@ static irqreturn_t tps6598x_interrupt(int irq, void *data) + if (!tps6598x_read_status(tps, &status)) + goto err_unlock; + ++ tps->status = status; ++ + if ((event1[0] | event2[0]) & TPS_REG_INT_POWER_STATUS_UPDATE) +- if (!tps6598x_read_power_status(tps)) ++ if (!tps->data->read_power_status(tps)) + goto err_unlock; + + if ((event1[0] | event2[0]) & TPS_REG_INT_DATA_STATUS_UPDATE) + if (!tps->data->read_data_status(tps)) + goto err_unlock; + +- /* Handle plug insert or removal */ +- if ((event1[0] | event2[0]) & TPS_REG_INT_PLUG_EVENT) ++ /* ++ * Refresh power status before connect - needed for TPS66993 which ++ * synthesizes pwr_status from STATUS and never gets POWER_STATUS_UPDATE. ++ */ ++ if ((event1[0] | event2[0]) & TPS_REG_INT_PLUG_EVENT) { ++ if (!tps->data->read_power_status(tps)) ++ goto err_unlock; + tps6598x_handle_plug_event(tps, status); ++ } + + err_unlock: + mutex_unlock(&tps->lock); +@@ -1080,6 +1119,7 @@ static int tps6598x_check_mode(struct tps6598x *tps) + + switch (ret) { + case TPS_MODE_APP: ++ case TPS_MODE_APP1: + case TPS_MODE_PTCH: + return ret; + case TPS_MODE_BOOT: +@@ -1753,7 +1793,7 @@ static int tps6598x_probe(struct i2c_client *client) + struct tps6598x *tps; + struct fwnode_handle *fwnode; + u32 status; +- u32 vid; ++ u32 vid = 0; + int ret; + + data = i2c_get_match_data(client); +@@ -1781,8 +1821,11 @@ static int tps6598x_probe(struct i2c_client *client) + + if (!device_is_compatible(tps->dev, "ti,tps25750")) { + ret = tps6598x_read32(tps, TPS_REG_VID, &vid); +- if (ret < 0 || !vid) ++ if (ret < 0 || !vid) { ++ dev_err(tps->dev, "failed to read vendor ID: %d, vid: %#x\n", ++ ret, vid); + return -ENODEV; ++ } + } + + /* +@@ -1818,6 +1861,8 @@ static int tps6598x_probe(struct i2c_client *client) + goto err_clear_mask; + } + ++ tps->status = status; ++ + /* + * This fwnode has a "compatible" property, but is never populated as a + * struct device. Instead we simply parse it to read the properties. +@@ -1845,7 +1890,7 @@ static int tps6598x_probe(struct i2c_client *client) + + if (status & TPS_STATUS_PLUG_PRESENT) { + ret = -EINVAL; +- if (!tps6598x_read_power_status(tps)) ++ if (!tps->data->read_power_status(tps)) + goto err_unregister_port; + if (!tps->data->read_data_status(tps)) + goto err_unregister_port; +@@ -1860,7 +1905,7 @@ static int tps6598x_probe(struct i2c_client *client) + IRQF_SHARED | IRQF_ONESHOT, + dev_name(&client->dev), tps); + } else { +- dev_warn(tps->dev, "Unable to find the interrupt, switching to polling\n"); ++ dev_dbg(tps->dev, "no IRQ specified, using polling mode\n"); + INIT_DELAYED_WORK(&tps->wq_poll, tps6598x_poll_work); + queue_delayed_work(system_power_efficient_wq, &tps->wq_poll, + msecs_to_jiffies(POLL_INTERVAL)); +@@ -1987,6 +2032,7 @@ static const struct tipd_data cd321x_data = { + .trace_status = trace_tps6598x_status, + .init = cd321x_init, + .read_data_status = cd321x_read_data_status, ++ .read_power_status = tps6598x_read_power_status, + .reset = cd321x_reset, + .switch_power_state = cd321x_switch_power_state, + .connect = cd321x_connect, +@@ -2006,6 +2052,25 @@ static const struct tipd_data tps6598x_data = { + .apply_patch = tps6598x_apply_patch, + .init = tps6598x_init, + .read_data_status = tps6598x_read_data_status, ++ .read_power_status = tps6598x_read_power_status, ++ .reset = tps6598x_reset, ++ .connect = tps6598x_connect, ++}; ++ ++static const struct tipd_data tps66993_data = { ++ .irq_handler = tps6598x_interrupt, ++ .irq_mask1 = TPS_REG_INT_DATA_STATUS_UPDATE | ++ TPS_REG_INT_PLUG_EVENT, ++ .tps_struct_size = sizeof(struct tps6598x), ++ .register_port = tps6598x_register_port, ++ .unregister_port = tps6598x_unregister_port, ++ .trace_data_status = trace_tps6598x_data_status, ++ .trace_power_status = trace_tps6598x_power_status, ++ .trace_status = trace_tps6598x_status, ++ .apply_patch = tps6598x_apply_patch, ++ .init = tps6598x_init, ++ .read_data_status = tps6598x_read_data_status, ++ .read_power_status = tps66993_read_power_status, + .reset = tps6598x_reset, + .connect = tps6598x_connect, + }; +@@ -2024,12 +2089,14 @@ static const struct tipd_data tps25750_data = { + .apply_patch = tps25750_apply_patch, + .init = tps25750_init, + .read_data_status = tps6598x_read_data_status, ++ .read_power_status = tps6598x_read_power_status, + .reset = tps25750_reset, + .connect = tps6598x_connect, + }; + + static const struct of_device_id tps6598x_of_match[] = { + { .compatible = "ti,tps6598x", &tps6598x_data}, ++ { .compatible = "ti,tps66993", &tps66993_data}, + { .compatible = "apple,cd321x", &cd321x_data}, + { .compatible = "ti,tps25750", &tps25750_data}, + {} +diff --git a/drivers/usb/typec/tipd/tps6598x.h b/drivers/usb/typec/tipd/tps6598x.h +--- a/drivers/usb/typec/tipd/tps6598x.h ++++ b/drivers/usb/typec/tipd/tps6598x.h +@@ -142,9 +142,13 @@ + #define TPS_SYSTEM_POWER_STATE_S4 0x04 + #define TPS_SYSTEM_POWER_STATE_S5 0x05 + +-/* TPS_REG_POWER_STATUS bits */ +-#define TPS_POWER_STATUS_CONNECTION(x) TPS_FIELD_GET(BIT(0), (x)) +-#define TPS_POWER_STATUS_SOURCESINK(x) TPS_FIELD_GET(BIT(1), (x)) ++/* TPS_REG_POWER_STATUS bits (masks shared by TPS_FIELD_GET accessors and FIELD_PREP) */ ++#define TPS_POWER_STATUS_CONNECTION_MASK BIT(0) ++#define TPS_POWER_STATUS_SOURCESINK_MASK BIT(1) ++#define TPS_POWER_STATUS_CONNECTION(x) \ ++ TPS_FIELD_GET(TPS_POWER_STATUS_CONNECTION_MASK, (x)) ++#define TPS_POWER_STATUS_SOURCESINK(x) \ ++ TPS_FIELD_GET(TPS_POWER_STATUS_SOURCESINK_MASK, (x)) + #define TPS_POWER_STATUS_BC12_DET(x) TPS_FIELD_GET(BIT(2), (x)) + + #define TPS_POWER_STATUS_TYPEC_CURRENT_MASK GENMASK(3, 2) +diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c +--- a/drivers/usb/typec/ucsi/debugfs.c ++++ b/drivers/usb/typec/ucsi/debugfs.c +@@ -82,8 +82,8 @@ static int ucsi_resp_show(struct seq_file *s, void *not_used) + if (ucsi->debugfs->status) + return ucsi->debugfs->status; + +- seq_printf(s, "0x%016llx%016llx\n", ucsi->debugfs->response.high, +- ucsi->debugfs->response.low); ++ seq_printf(s, "0x%016llx%016llx%016llx\n", ucsi->debugfs->response.ext, ++ ucsi->debugfs->response.high, ucsi->debugfs->response.low); + return 0; + } + DEFINE_SHOW_ATTRIBUTE(ucsi_resp); +diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c +--- a/drivers/usb/typec/ucsi/ucsi.c ++++ b/drivers/usb/typec/ucsi/ucsi.c +@@ -1102,8 +1102,8 @@ static int ucsi_register_plug(struct ucsi_connector *con) + plug = typec_register_plug(con->cable, &desc); + if (IS_ERR(plug)) { + dev_err(con->ucsi->dev, +- "con%d: failed to register plug (%ld)\n", con->num, +- PTR_ERR(plug)); ++ "con%d: failed to register plug (%pe)\n", con->num, ++ plug); + return PTR_ERR(plug); + } + +@@ -1161,8 +1161,8 @@ static int ucsi_register_cable(struct ucsi_connector *con) + cable = typec_register_cable(con->port, &desc); + if (IS_ERR(cable)) { + dev_err(con->ucsi->dev, +- "con%d: failed to register cable (%ld)\n", con->num, +- PTR_ERR(cable)); ++ "con%d: failed to register cable (%pe)\n", con->num, ++ cable); + return PTR_ERR(cable); + } + +@@ -1291,8 +1291,8 @@ static int ucsi_register_partner(struct ucsi_connector *con) + partner = typec_register_partner(con->port, &desc); + if (IS_ERR(partner)) { + dev_err(con->ucsi->dev, +- "con%d: failed to register partner (%ld)\n", con->num, +- PTR_ERR(partner)); ++ "con%d: failed to register partner (%pe)\n", con->num, ++ partner); + return PTR_ERR(partner); + } + +@@ -2221,7 +2221,7 @@ static void ucsi_init_work(struct work_struct *work) + return; + } + +- queue_delayed_work(system_long_wq, &ucsi->work, ++ queue_delayed_work(system_dfl_long_wq, &ucsi->work, + UCSI_ROLE_SWITCH_INTERVAL); + } + } +@@ -2345,7 +2345,7 @@ int ucsi_register(struct ucsi *ucsi) + UCSI_BCD_GET_MINOR(ucsi->version), + UCSI_BCD_GET_SUBMINOR(ucsi->version)); + +- queue_delayed_work(system_long_wq, &ucsi->work, 0); ++ queue_delayed_work(system_dfl_long_wq, &ucsi->work, 0); + + ucsi_debugfs_register(ucsi); + return 0; +diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h +--- a/drivers/usb/typec/ucsi/ucsi.h ++++ b/drivers/usb/typec/ucsi/ucsi.h +@@ -466,6 +466,7 @@ struct ucsi_debugfs_entry { + struct ucsi_data { + u64 low; + u64 high; ++ u64 ext; + } response; + int status; + u8 message_out[MESSAGE_OUT_MAX_LEN]; +diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h +--- a/include/linux/power_supply.h ++++ b/include/linux/power_supply.h +@@ -806,11 +806,26 @@ extern int power_supply_reg_notifier(struct notifier_block *nb); + extern void power_supply_unreg_notifier(struct notifier_block *nb); + #if IS_ENABLED(CONFIG_POWER_SUPPLY) + extern struct power_supply *power_supply_get_by_name(const char *name); ++extern int __must_check power_supply_get_system_batteries(struct device *dev, ++ struct power_supply ***psys); ++extern void power_supply_put_system_batteries(struct power_supply **psys, int count); + extern void power_supply_put(struct power_supply *psy); + #else + static inline void power_supply_put(struct power_supply *psy) {} + static inline struct power_supply *power_supply_get_by_name(const char *name) + { return NULL; } ++static inline int __must_check power_supply_get_system_batteries(struct device *dev, ++ struct power_supply ***psys) ++{ ++ if (psys) ++ *psys = NULL; ++ return 0; ++} ++ ++static inline void power_supply_put_system_batteries(struct power_supply **psys, ++ int count) ++{ ++} + #endif + extern struct power_supply *power_supply_get_by_reference(struct fwnode_handle *fwnode, + const char *property); +diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h +--- a/include/linux/usb/pd.h ++++ b/include/linux/usb/pd.h +@@ -106,6 +106,9 @@ enum pd_ext_msg_type { + #define PD_HEADER_LE(type, pwr, data, rev, id, cnt) \ + cpu_to_le16(PD_HEADER((type), (pwr), (data), (rev), (id), (cnt), (0))) + ++#define PD_HEADER_EXT_LE(type, pwr, data, rev, id, cnt) \ ++ cpu_to_le16(PD_HEADER((type), (pwr), (data), (rev), (id), (cnt), (1))) ++ + static inline unsigned int pd_header_cnt(u16 header) + { + return (header >> PD_HEADER_CNT_SHIFT) & PD_HEADER_CNT_MASK; +@@ -219,6 +222,25 @@ static inline u8 count_chunked_data_objs(u32 size) + return ((size / 4) + (size % 4 ? 1 : 0)); + } + ++/** ++ * batt_cap_ext_msg - Battery capability extended PD message ++ * @vid: Battery Vendor ID (assigned by USB-IF) ++ * @pid: Battery Product ID (assigned by battery or device vendor) ++ * @batt_design_cap: Battery design capacity in 0.1Wh ++ * @batt_last_chg_cap: Battery last full charge capacity in 0.1Wh ++ * @batt_type: Battery Type. bit0 when set indicates invalid battery reference. ++ * Rest of the bits are reserved. ++ */ ++struct batt_cap_ext_msg { ++ __le16 vid; ++ __le16 pid; ++ __le16 batt_design_cap; ++ __le16 batt_last_chg_cap; ++ u8 batt_type; ++} __packed; ++ ++#define BATT_CAP_BATT_TYPE_INVALID_REF BIT(0) ++ + /* Sink Caps Extended Data Block Version */ + #define SKEDB_VER_1_0 1 + +@@ -724,4 +746,33 @@ void usb_power_delivery_unlink_device(struct usb_power_delivery *pd, struct devi + + #endif /* CONFIG_TYPEC */ + ++/* Battery Status Data Object */ ++#define BSDO_PRESENT_CAPACITY GENMASK(31, 16) ++#define BSDO_CHG_STATUS GENMASK(11, 10) ++#define BSDO_BATTERY_PRESENT BIT(9) ++#define BSDO_INVALID_BATTERY_REFERENCE BIT(8) ++ ++/* ++ * Battery Charge Status: Battery Charging Status Values as defined in ++ * "USB PD Spec Rev3.1 Ver1.8", "Table 6-46 Battery Status Data Object (BSDO)". ++ */ ++#define BSDO_BATTERY_INFO_CHARGING 0x0 ++#define BSDO_BATTERY_INFO_DISCHARGING 0x1 ++#define BSDO_BATTERY_INFO_IDLE 0x2 ++#define BSDO_BATTERY_INFO_RSVD 0x3 ++ ++/** ++ * BSDO() - Pack data into Battery Status Data Object format. ++ * @batt_charge: Battery's present state of charge in 0.1WH increment. ++ * @chg_status: Battery charge status. ++ * @batt_present: Indicates that battery is present/attached when set else absent when unset. ++ * @invalid_ref: Indicates that an invalid battery reference was made in the Get_Battery_Status ++ * request. ++ */ ++#define BSDO(batt_charge, chg_status, batt_present, invalid_ref) \ ++ ((FIELD_PREP(BSDO_PRESENT_CAPACITY, batt_charge)) | \ ++ (FIELD_PREP(BSDO_CHG_STATUS, chg_status)) | \ ++ ((batt_present) ? BSDO_BATTERY_PRESENT : 0) | \ ++ ((invalid_ref) ? BSDO_INVALID_BATTERY_REFERENCE : 0)) ++ + #endif /* __LINUX_USB_PD_H */ +diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h +--- a/include/linux/usb/typec.h ++++ b/include/linux/usb/typec.h +@@ -337,6 +337,7 @@ void typec_unregister_cable(struct typec_cable *cable); + struct typec_cable *typec_cable_get(struct typec_port *port); + void typec_cable_put(struct typec_cable *cable); + int typec_cable_is_active(struct typec_cable *cable); ++bool typec_cable_altmode_unsupported(struct typec_altmode *alt); + + struct typec_plug *typec_register_plug(struct typec_cable *cable, + struct typec_plug_desc *desc); +diff --git a/include/uapi/linux/usb/functionfs.h b/include/uapi/linux/usb/functionfs.h +--- a/include/uapi/linux/usb/functionfs.h ++++ b/include/uapi/linux/usb/functionfs.h +@@ -25,6 +25,7 @@ enum functionfs_flags { + FUNCTIONFS_EVENTFD = 32, + FUNCTIONFS_ALL_CTRL_RECIP = 64, + FUNCTIONFS_CONFIG0_SETUP = 128, ++ FUNCTIONFS_RW_PROXY_EPS = 256, + }; + + /* Descriptor of an non-audio endpoint */ +@@ -414,4 +415,27 @@ struct usb_functionfs_event { + #define FUNCTIONFS_DMABUF_TRANSFER _IOW('g', 133, \ + struct usb_ffs_dmabuf_transfer_req) + ++/* ++ * Enable or disable automatic zero-length packet (ZLP) appending for the ++ * endpoint. The argument is a pointer to a __u32: 0 to disable, non-zero to ++ * enable. ++ * ++ * When enabled, the kernel will automatically append a ZLP at the end of ++ * a transfer if the payload length is an exact multiple of the endpoint's ++ * max packet size. ++ * ++ * This is useful for compatibility with legacy protocols which require ++ * automatic ZLP appending to data written from userspace. ++ * ++ * This ioctl can only be used on IN endpoints. It can be called at any time ++ * after the FunctionFS instance is active, even before the host has connected ++ * or enabled the endpoint. ++ * ++ * Returns zero on success, or a negative errno value on error: ++ * -ENODEV: The FunctionFS instance is not active. ++ * -EINVAL: The endpoint is not an IN endpoint. ++ * -EFAULT: Invalid user space pointer for the argument. ++ */ ++#define FUNCTIONFS_ENDPOINT_ENABLE_ZLP _IOW('g', 134, __u32) ++ + #endif /* _UAPI__LINUX_FUNCTIONFS_H__ */ +diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs +--- a/rust/kernel/usb.rs ++++ b/rust/kernel/usb.rs +@@ -24,11 +24,8 @@ + }; + use core::{ + marker::PhantomData, +- mem::{ +- offset_of, +- MaybeUninit, // +- }, +- ptr::NonNull, ++ mem::offset_of, ++ ptr::NonNull, // + }; + + /// An adapter for the registration of USB drivers. +@@ -130,8 +127,7 @@ pub const fn from_id(vendor: u16, product: u16) -> Self { + match_flags: bindings::USB_DEVICE_ID_MATCH_DEVICE as u16, + idVendor: vendor, + idProduct: product, +- // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`. +- ..unsafe { MaybeUninit::zeroed().assume_init() } ++ ..pin_init::zeroed() + }) + } + +@@ -143,8 +139,7 @@ pub const fn from_device_ver(vendor: u16, product: u16, bcd_lo: u16, bcd_hi: u16 + idProduct: product, + bcdDevice_lo: bcd_lo, + bcdDevice_hi: bcd_hi, +- // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`. +- ..unsafe { MaybeUninit::zeroed().assume_init() } ++ ..pin_init::zeroed() + }) + } + +@@ -155,8 +150,7 @@ pub const fn from_device_info(class: u8, subclass: u8, protocol: u8) -> Self { + bDeviceClass: class, + bDeviceSubClass: subclass, + bDeviceProtocol: protocol, +- // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`. +- ..unsafe { MaybeUninit::zeroed().assume_init() } ++ ..pin_init::zeroed() + }) + } + +@@ -167,8 +161,7 @@ pub const fn from_interface_info(class: u8, subclass: u8, protocol: u8) -> Self + bInterfaceClass: class, + bInterfaceSubClass: subclass, + bInterfaceProtocol: protocol, +- // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`. +- ..unsafe { MaybeUninit::zeroed().assume_init() } ++ ..pin_init::zeroed() + }) + } + +@@ -180,8 +173,7 @@ pub const fn from_device_interface_class(vendor: u16, product: u16, class: u8) - + idVendor: vendor, + idProduct: product, + bInterfaceClass: class, +- // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`. +- ..unsafe { MaybeUninit::zeroed().assume_init() } ++ ..pin_init::zeroed() + }) + } + +@@ -193,8 +185,7 @@ pub const fn from_device_interface_protocol(vendor: u16, product: u16, protocol: + idVendor: vendor, + idProduct: product, + bInterfaceProtocol: protocol, +- // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`. +- ..unsafe { MaybeUninit::zeroed().assume_init() } ++ ..pin_init::zeroed() + }) + } + +@@ -206,8 +197,7 @@ pub const fn from_device_interface_number(vendor: u16, product: u16, number: u8) + idVendor: vendor, + idProduct: product, + bInterfaceNumber: number, +- // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`. +- ..unsafe { MaybeUninit::zeroed().assume_init() } ++ ..pin_init::zeroed() + }) + } + +@@ -227,8 +217,7 @@ pub const fn from_device_and_interface_info( + bInterfaceClass: class, + bInterfaceSubClass: subclass, + bInterfaceProtocol: protocol, +- // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`. +- ..unsafe { MaybeUninit::zeroed().assume_init() } ++ ..pin_init::zeroed() + }) + } + } +@@ -393,6 +382,7 @@ fn as_ref(&self) -> &Device { + + // SAFETY: Instances of `Interface` are always reference-counted. + unsafe impl AlwaysRefCounted for Interface { ++ #[inline] + fn inc_ref(&self) { + // SAFETY: The invariants of `Interface` guarantee that `self.as_raw()` + // returns a valid `struct usb_interface` pointer, for which we will +@@ -400,6 +390,7 @@ fn inc_ref(&self) { + unsafe { bindings::usb_get_intf(self.as_raw()) }; + } + ++ #[inline] + unsafe fn dec_ref(obj: NonNull) { + // SAFETY: The safety requirements guarantee that the refcount is non-zero. + unsafe { bindings::usb_put_intf(obj.cast().as_ptr()) } +@@ -444,6 +435,7 @@ fn as_raw(&self) -> *mut bindings::usb_device { + + // SAFETY: Instances of `Device` are always reference-counted. + unsafe impl AlwaysRefCounted for Device { ++ #[inline] + fn inc_ref(&self) { + // SAFETY: The invariants of `Device` guarantee that `self.as_raw()` + // returns a valid `struct usb_device` pointer, for which we will +@@ -451,6 +443,7 @@ fn inc_ref(&self) { + unsafe { bindings::usb_get_dev(self.as_raw()) }; + } + ++ #[inline] + unsafe fn dec_ref(obj: NonNull) { + // SAFETY: The safety requirements guarantee that the refcount is non-zero. + unsafe { bindings::usb_put_dev(obj.cast().as_ptr()) } diff --git a/pkgbuilds/linux-omarchy-bore/0603-usb-updates.patch.sig b/pkgbuilds/linux-omarchy-bore/0603-usb-updates.patch.sig new file mode 100644 index 0000000..a76936f Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0603-usb-updates.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0604-xhci-fixes.patch b/pkgbuilds/linux-omarchy-bore/0604-xhci-fixes.patch new file mode 100644 index 0000000..8545f24 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0604-xhci-fixes.patch @@ -0,0 +1,35 @@ +diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c +--- a/drivers/usb/host/xhci-mem.c ++++ b/drivers/usb/host/xhci-mem.c +@@ -2301,7 +2301,7 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int segs, gfp_t flags) + if (!segs) + segs = ERST_DEFAULT_SEGS; + +- max_segs = FIELD_GET(HCS_ERST_MAX, xhci->hcs_params2) << 2; ++ max_segs = BIT(FIELD_GET(HCS_ERST_MAX, xhci->hcs_params2)); + segs = min(segs, max_segs); + + ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev)); +diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -4328,11 +4328,16 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, + check_interval(urb, ep_ctx); + + /* +- * Check if this starts the isoc data flow. Relies on hw setting ep ctx +- * state after doorbell ring. Consider adding list_empty(td_list) check ++ * Schedule the URB discontiguously if all previous URBs have completed. ++ * XXX core can't tell if completions are pending but not running yet. + */ +- if (GET_EP_CTX_STATE(ep_ctx) != EP_STATE_RUNNING) ++ if (list_empty(&ep_ring->td_list) && ++ !hcd_periodic_completion_in_progress(xhci_to_hcd(xhci), urb->ep)) { ++ if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_RUNNING) ++ xhci_dbg(xhci, "Unexpected running ring at isoc stream start, uframe: %d\n", ++ xep->next_uframe); + xep->next_uframe = -1; ++ } + + return xhci_queue_isoc_tx(xhci, mem_flags, urb, slot_id, ep_index); + } diff --git a/pkgbuilds/linux-omarchy-bore/0604-xhci-fixes.patch.sig b/pkgbuilds/linux-omarchy-bore/0604-xhci-fixes.patch.sig new file mode 100644 index 0000000..7d0570f Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0604-xhci-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0605-thunderbolt-pcie-rescan-after-tunnel.patch b/pkgbuilds/linux-omarchy-bore/0605-thunderbolt-pcie-rescan-after-tunnel.patch new file mode 100644 index 0000000..a0b7740 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0605-thunderbolt-pcie-rescan-after-tunnel.patch @@ -0,0 +1,186 @@ +diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c +--- a/drivers/thunderbolt/domain.c ++++ b/drivers/thunderbolt/domain.c +@@ -909,6 +909,10 @@ int tb_domain_init(void) + { + int ret; + ++ ret = tb_pci_rescan_init(); ++ if (ret) ++ return ret; ++ + tb_configfs_init(); + tb_debugfs_init(); + tb_acpi_init(); +@@ -927,6 +931,7 @@ err_xdomain: + err_acpi: + tb_acpi_exit(); + tb_debugfs_exit(); ++ tb_pci_rescan_exit(); + + return ret; + } +@@ -940,4 +945,5 @@ void tb_domain_exit(void) + tb_acpi_exit(); + tb_debugfs_exit(); + tb_configfs_exit(); ++ tb_pci_rescan_exit(); + } +diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c +--- a/drivers/thunderbolt/tb.c ++++ b/drivers/thunderbolt/tb.c +@@ -9,7 +9,9 @@ + #include + #include + #include ++#include + #include ++#include + #include + + #include "tb.h" +@@ -18,6 +20,7 @@ + + #define TB_TIMEOUT 100 /* ms */ + #define TB_RELEASE_BW_TIMEOUT 10000 /* ms */ ++#define TB_PCI_RESCAN_DELAY 300 /* ms */ + + /* + * How many time bandwidth allocation request from graphics driver is +@@ -49,6 +52,13 @@ MODULE_PARM_DESC(asym_threshold, + "threshold (Mb/s) when to Gen 4 switch link symmetry. 0 disables. (default: " + __MODULE_STRING(TB_ASYM_THRESHOLD) ")"); + ++struct tb_pci_rescan { ++ struct delayed_work work; ++ struct kref kref; ++ struct pci_bus *bus; ++ bool stopped; ++}; ++ + /** + * struct tb_cm - Simple Thunderbolt connection manager + * @tunnel_list: List of active tunnels +@@ -67,8 +77,11 @@ struct tb_cm { + bool hotplug_active; + struct delayed_work remove_work; + struct tb_bandwidth_group groups[MAX_GROUPS]; ++ struct tb_pci_rescan *pci_rescan; + }; + ++static struct workqueue_struct *tb_pci_rescan_wq; ++ + static inline struct tb *tcm_to_tb(struct tb_cm *tcm) + { + return ((void *)tcm - sizeof(struct tb)); +@@ -2282,6 +2295,82 @@ static int tb_disconnect_pci(struct tb *tb, struct tb_switch *sw) + return 0; + } + ++static void tb_pci_rescan_release(struct kref *kref) ++{ ++ struct tb_pci_rescan *rescan = container_of(kref, typeof(*rescan), kref); ++ ++ put_device(&rescan->bus->dev); ++ kfree(rescan); ++} ++ ++static void tb_pci_rescan_work_fn(struct work_struct *work) ++{ ++ struct tb_pci_rescan *rescan = ++ container_of(to_delayed_work(work), typeof(*rescan), work); ++ ++ pci_lock_rescan_remove(); ++ if (!READ_ONCE(rescan->stopped)) ++ pci_rescan_bus(rescan->bus); ++ pci_unlock_rescan_remove(); ++ kref_put(&rescan->kref, tb_pci_rescan_release); ++} ++ ++static void tb_pci_rescan_schedule(struct tb *tb) ++{ ++ struct tb_cm *tcm = tb_priv(tb); ++ struct tb_pci_rescan *rescan = tcm->pci_rescan; ++ ++ if (!rescan) { ++ struct pci_bus *bus; ++ ++ if (!dev_is_pci(tb->nhi->dev)) ++ return; ++ bus = to_pci_dev(tb->nhi->dev)->bus; ++ if (bus->parent) ++ bus = bus->parent; ++ ++ rescan = kzalloc_obj(*rescan); ++ if (!rescan) ++ return; ++ INIT_DELAYED_WORK(&rescan->work, tb_pci_rescan_work_fn); ++ kref_init(&rescan->kref); ++ get_device(&bus->dev); ++ rescan->bus = bus; ++ tcm->pci_rescan = rescan; ++ } ++ ++ kref_get(&rescan->kref); ++ if (mod_delayed_work(tb_pci_rescan_wq, &rescan->work, ++ msecs_to_jiffies(TB_PCI_RESCAN_DELAY))) ++ kref_put(&rescan->kref, tb_pci_rescan_release); ++} ++ ++static void tb_pci_rescan_stop(struct tb_cm *tcm) ++{ ++ struct tb_pci_rescan *rescan = tcm->pci_rescan; ++ ++ if (!rescan) ++ return; ++ ++ WRITE_ONCE(rescan->stopped, true); ++ if (cancel_delayed_work(&rescan->work)) ++ kref_put(&rescan->kref, tb_pci_rescan_release); ++ kref_put(&rescan->kref, tb_pci_rescan_release); ++ tcm->pci_rescan = NULL; ++} ++ ++int tb_pci_rescan_init(void) ++{ ++ tb_pci_rescan_wq = alloc_workqueue("thunderbolt_pci_rescan", ++ WQ_FREEZABLE | WQ_UNBOUND, 0); ++ return tb_pci_rescan_wq ? 0 : -ENOMEM; ++} ++ ++void tb_pci_rescan_exit(void) ++{ ++ destroy_workqueue(tb_pci_rescan_wq); ++} ++ + static int tb_tunnel_pci(struct tb *tb, struct tb_switch *sw) + { + struct tb_port *up, *down, *port; +@@ -2323,6 +2412,7 @@ static int tb_tunnel_pci(struct tb *tb, struct tb_switch *sw) + tb_sw_warn(sw, "failed to connect xHCI\n"); + + list_add_tail(&tunnel->list, &tcm->tunnel_list); ++ tb_pci_rescan_schedule(tb); + return 0; + } + +@@ -2957,6 +3047,7 @@ static void tb_stop(struct tb *tb) + struct tb_tunnel *n; + + cancel_delayed_work(&tcm->remove_work); ++ tb_pci_rescan_stop(tcm); + /* tunnels are only present after everything has been initialized */ + list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) { + /* +diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h +--- a/drivers/thunderbolt/tb.h ++++ b/drivers/thunderbolt/tb.h +@@ -759,6 +759,8 @@ static inline int tb_port_write(struct tb_port *port, const void *buffer, + + struct tb *icm_probe(struct tb_nhi *nhi); + struct tb *tb_probe(struct tb_nhi *nhi); ++int tb_pci_rescan_init(void); ++void tb_pci_rescan_exit(void); + + extern const struct device_type tb_domain_type; + extern const struct device_type tb_retimer_type; diff --git a/pkgbuilds/linux-omarchy-bore/0605-thunderbolt-pcie-rescan-after-tunnel.patch.sig b/pkgbuilds/linux-omarchy-bore/0605-thunderbolt-pcie-rescan-after-tunnel.patch.sig new file mode 100644 index 0000000..68db430 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0605-thunderbolt-pcie-rescan-after-tunnel.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0606-thunderbolt-fixes-2.patch b/pkgbuilds/linux-omarchy-bore/0606-thunderbolt-fixes-2.patch new file mode 100644 index 0000000..0d85afa --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0606-thunderbolt-fixes-2.patch @@ -0,0 +1,239 @@ +diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c +--- a/drivers/thunderbolt/domain.c ++++ b/drivers/thunderbolt/domain.c +@@ -788,21 +788,6 @@ int tb_domain_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, + transmit_ring, receive_path, receive_ring); + } + +-static void tb_domain_reset_interface(struct tb *tb) +-{ +- struct tb_nhi *nhi = tb->nhi; +- +- if (!nhi->ops->reset_interface) +- return; +- +- guard(mutex)(&tb->lock); +- +- /* The reset clears the ring state so stop the control channel */ +- tb_ctl_stop(tb->ctl); +- nhi->ops->reset_interface(nhi); +- tb_ctl_start(tb->ctl); +-} +- + /** + * tb_domain_disconnect_xdomain_paths() - Disable DMA paths for XDomain + * @tb: Domain disabling the DMA paths +@@ -825,20 +810,11 @@ int tb_domain_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, + int transmit_path, int transmit_ring, + int receive_path, int receive_ring) + { +- int ret; +- + if (!tb->cm_ops->disconnect_xdomain_paths) + return -ENOTSUPP; + +- ret = tb->cm_ops->disconnect_xdomain_paths(tb, xd, transmit_path, ++ return tb->cm_ops->disconnect_xdomain_paths(tb, xd, transmit_path, + transmit_ring, receive_path, receive_ring); +- if (ret) +- return ret; +- +- if (tb->nhi->quirks & QUIRK_RESET_DMA_ON_TEARDOWN) +- tb_domain_reset_interface(tb); +- +- return 0; + } + + static int disconnect_xdomain(struct device *dev, void *data) +diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c +--- a/drivers/thunderbolt/nhi.c ++++ b/drivers/thunderbolt/nhi.c +@@ -1180,32 +1180,6 @@ static void nhi_reset(struct tb_nhi *nhi) + dev_warn(nhi->dev, "timeout resetting host router\n"); + } + +-/** +- * nhi_reset_interface() - Reset the host interface +- * @nhi: Host interface to reset +- * +- * Brings the registers in the memory BAR back to their default state and +- * clears the End-to-End Flow Control state. The caller is responsible for +- * stopping the control channel over the reset because it clears the ring +- * state as well. +- */ +-void nhi_reset_interface(struct tb_nhi *nhi) +-{ +- u32 val; +- +- val = ioread32(nhi->iobase + REG_CAPS); +- /* Only v1 host interfaces implement the reset */ +- if (FIELD_GET(REG_CAPS_VERSION_MASK, val) >= REG_CAPS_VERSION_2) +- return; +- +- dev_dbg(nhi->dev, "issuing host interface reset\n"); +- +- iowrite32(REG_HOST_INTERFACE_RESET_RST, +- nhi->iobase + REG_HOST_INTERFACE_RESET); +- /* Wait for tHIReset (10 ms) to complete */ +- usleep_range(10000, 20000); +-} +- + static struct tb *nhi_select_cm(struct tb_nhi *nhi) + { + struct tb *tb; +diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h +--- a/drivers/thunderbolt/nhi.h ++++ b/drivers/thunderbolt/nhi.h +@@ -36,8 +36,6 @@ irqreturn_t nhi_msi(int irq, void *data); + irqreturn_t ring_msix(int irq, void *data); + int nhi_probe(struct tb_nhi *nhi); + void nhi_shutdown(struct tb_nhi *nhi); +-void nhi_reset_interface(struct tb_nhi *nhi); +- + extern const struct dev_pm_ops nhi_pm_ops; + + /** +@@ -54,7 +52,6 @@ extern const struct dev_pm_ops nhi_pm_ops; + * @release_ring_irq: NHI specific interrupt release hook + * @is_present: Whether the device is currently present on the parent bus + * @init_interrupts: NHI specific interrupt initialization hook +- * @reset_interface: Resets the host interface + */ + struct tb_nhi_ops { + int (*init)(struct tb_nhi *nhi); +@@ -69,7 +66,6 @@ struct tb_nhi_ops { + void (*release_ring_irq)(struct tb_ring *ring); + bool (*is_present)(struct tb_nhi *nhi); + int (*init_interrupts)(struct tb_nhi *nhi); +- void (*reset_interface)(struct tb_nhi *nhi); + }; + + /* +@@ -120,24 +116,11 @@ struct tb_nhi_ops { + #define PCI_DEVICE_ID_INTEL_PTL_P_NHI0 0xe433 + #define PCI_DEVICE_ID_INTEL_PTL_P_NHI1 0xe434 + +-#define PCI_DEVICE_ID_AMD_1AH_M60H_NHI0 0x1120 +-#define PCI_DEVICE_ID_AMD_1AH_M60H_NHI1 0x1121 +-#define PCI_DEVICE_ID_AMD_1AH_M68H_NHI0 0x113b +-#define PCI_DEVICE_ID_AMD_1AH_M68H_NHI1 0x113c +-#define PCI_DEVICE_ID_AMD_1AH_M80H_NHI0 0x1155 +-#define PCI_DEVICE_ID_AMD_1AH_M80H_NHI1 0x1158 +-#define PCI_DEVICE_ID_AMD_1AH_M80H_NHI2 0x1159 +-#define PCI_DEVICE_ID_AMD_1AH_M24H_NHI0 0x151c +-#define PCI_DEVICE_ID_AMD_1AH_M24H_NHI1 0x151d +-#define PCI_DEVICE_ID_AMD_1AH_M70H_NHI0 0x158d +-#define PCI_DEVICE_ID_AMD_1AH_M70H_NHI1 0x158e +- + #define PCI_CLASS_SERIAL_USB_USB4 0x0c0340 + + /* Host interface quirks */ +-#define QUIRK_AUTO_CLEAR_INT BIT(0) +-#define QUIRK_E2E BIT(1) +-#define QUIRK_RESET_DMA_ON_TEARDOWN BIT(2) ++#define QUIRK_AUTO_CLEAR_INT BIT(0) ++#define QUIRK_E2E BIT(1) + + /* + * Minimal number of vectors when we use MSI-X. Two for control channel +diff --git a/drivers/thunderbolt/nhi_regs.h b/drivers/thunderbolt/nhi_regs.h +--- a/drivers/thunderbolt/nhi_regs.h ++++ b/drivers/thunderbolt/nhi_regs.h +@@ -115,10 +115,6 @@ struct ring_desc { + #define REG_CAPS_VERSION_MASK GENMASK(23, 16) + #define REG_CAPS_VERSION_2 0x40 + +-/* Host Interface Reset - resets TX/RX rings and E2E flow control counters */ +-#define REG_HOST_INTERFACE_RESET 0x39858 +-#define REG_HOST_INTERFACE_RESET_RST BIT(0) +- + #define REG_DMA_MISC 0x39864 + #define REG_DMA_MISC_INT_AUTO_CLEAR BIT(2) + #define REG_DMA_MISC_DISABLE_AUTO_CLEAR BIT(17) +diff --git a/drivers/thunderbolt/pci.c b/drivers/thunderbolt/pci.c +--- a/drivers/thunderbolt/pci.c ++++ b/drivers/thunderbolt/pci.c +@@ -62,27 +62,6 @@ static void nhi_pci_check_quirks(struct tb_nhi_pci *nhi_pci) + nhi->quirks |= QUIRK_E2E; + break; + } +- } else if (pdev->vendor == PCI_VENDOR_ID_AMD) { +- switch (pdev->device) { +- case PCI_DEVICE_ID_AMD_1AH_M60H_NHI0: +- case PCI_DEVICE_ID_AMD_1AH_M60H_NHI1: +- case PCI_DEVICE_ID_AMD_1AH_M68H_NHI0: +- case PCI_DEVICE_ID_AMD_1AH_M68H_NHI1: +- case PCI_DEVICE_ID_AMD_1AH_M80H_NHI0: +- case PCI_DEVICE_ID_AMD_1AH_M80H_NHI1: +- case PCI_DEVICE_ID_AMD_1AH_M80H_NHI2: +- case PCI_DEVICE_ID_AMD_1AH_M24H_NHI0: +- case PCI_DEVICE_ID_AMD_1AH_M24H_NHI1: +- case PCI_DEVICE_ID_AMD_1AH_M70H_NHI0: +- case PCI_DEVICE_ID_AMD_1AH_M70H_NHI1: +- /* +- * These AMD hosts may hang the Tx ring when the +- * DMA paths are torn down so they need the host +- * interface reset after each teardown. +- */ +- nhi->quirks |= QUIRK_RESET_DMA_ON_TEARDOWN; +- break; +- } + } + } + +@@ -279,7 +258,6 @@ static const struct tb_nhi_ops pci_nhi_default_ops = { + .shutdown = nhi_pci_release_irq, + .is_present = nhi_pci_is_present, + .init_interrupts = nhi_pci_init_msi, +- .reset_interface = nhi_reset_interface, + }; + + /* Ice Lake specific NHI operations */ +@@ -463,7 +441,6 @@ static const struct tb_nhi_ops icl_nhi_ops = { + .release_ring_irq = nhi_pci_ring_release_msix, + .is_present = nhi_pci_is_present, + .init_interrupts = nhi_pci_init_msi, +- .reset_interface = nhi_reset_interface, + }; + + static int nhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +diff --git a/drivers/thunderbolt/quirks.c b/drivers/thunderbolt/quirks.c +--- a/drivers/thunderbolt/quirks.c ++++ b/drivers/thunderbolt/quirks.c +@@ -99,6 +99,9 @@ static const struct tb_quirk tb_quirks[] = { + quirk_usb3_maximum_bandwidth }, + { 0x8087, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HUB_40G_BRIDGE, 0x0000, 0x0000, + quirk_usb3_maximum_bandwidth }, ++ /* CLx is unstable on the Anker Prime TB5 dock (DROM 01ea:83b5) */ ++ { 0x8087, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HUB_80G_BRIDGE, 0x01ea, 0x83b5, ++ quirk_clx_disable }, + /* + * Block Runtime PM in DP redrive mode for Intel Barlow Ridge host + * controllers. +diff --git a/drivers/thunderbolt/stream.c b/drivers/thunderbolt/stream.c +--- a/drivers/thunderbolt/stream.c ++++ b/drivers/thunderbolt/stream.c +@@ -980,6 +980,9 @@ static int tbstream_dev_fops_open(struct inode *inode, struct file *file) + } + + mutex_unlock(&sdev->lock); ++ ++ /* Stream handles IOCB_NOWAIT just fine */ ++ file->f_mode |= FMODE_NOWAIT; + return 0; + + err_unlock: +diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c +--- a/drivers/thunderbolt/xdomain.c ++++ b/drivers/thunderbolt/xdomain.c +@@ -377,6 +377,10 @@ static int tb_xdp_properties_request(struct tb_ctl *ctl, u64 route, + + len += sizeof(res->hdr.xd_hdr) / 4; + len -= sizeof(*res) / 4; ++ if (len > TB_XDP_PROPERTIES_MAX_DATA_LENGTH) { ++ ret = -EINVAL; ++ goto err; ++ } + + if (res->offset != req.offset) { + ret = -EINVAL; diff --git a/pkgbuilds/linux-omarchy-bore/0606-thunderbolt-fixes-2.patch.sig b/pkgbuilds/linux-omarchy-bore/0606-thunderbolt-fixes-2.patch.sig new file mode 100644 index 0000000..4a16c2a Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0606-thunderbolt-fixes-2.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0607-usb-fixes.patch b/pkgbuilds/linux-omarchy-bore/0607-usb-fixes.patch new file mode 100644 index 0000000..2e3ea13 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0607-usb-fixes.patch @@ -0,0 +1,1673 @@ +diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c +--- a/drivers/usb/cdns3/cdns3-gadget.c ++++ b/drivers/usb/cdns3/cdns3-gadget.c +@@ -3269,6 +3269,10 @@ static void cdns3_gadget_exit(struct cdns *cdns) + usb_del_gadget(&priv_dev->gadget); + devm_free_irq(cdns->dev, cdns->dev_irq, priv_dev); + ++ /* The works can still be queued until the IRQ is freed. */ ++ cancel_work_sync(&priv_dev->pending_status_wq); ++ cancel_work_sync(&priv_dev->aligned_buf_wq); ++ + cdns3_free_all_eps(priv_dev); + + while (!list_empty(&priv_dev->aligned_buf_list)) { +diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c +--- a/drivers/usb/cdns3/cdns3-pci-wrap.c ++++ b/drivers/usb/cdns3/cdns3-pci-wrap.c +@@ -96,6 +96,11 @@ static int cdns3_pci_probe(struct pci_dev *pdev, + + if (pci_is_enabled(func)) { + wrap = pci_get_drvdata(func); ++ if (!wrap) { ++ dev_err(&pdev->dev, ++ "second function not initialized, retrying\n"); ++ return -EPROBE_DEFER; ++ } + } else { + wrap = kzalloc_obj(*wrap); + if (!wrap) +diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c +--- a/drivers/usb/chipidea/ci_hdrc_tegra.c ++++ b/drivers/usb/chipidea/ci_hdrc_tegra.c +@@ -306,8 +306,10 @@ static int tegra_usb_probe(struct platform_device *pdev) + + pm_runtime_enable(&pdev->dev); + err = pm_runtime_resume_and_get(&pdev->dev); +- if (err) ++ if (err) { ++ pm_runtime_disable(&pdev->dev); + return err; ++ } + + if (device_property_present(&pdev->dev, "nvidia,needs-double-reset")) + usb->needs_double_reset = true; +diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c +--- a/drivers/usb/class/cdc-acm.c ++++ b/drivers/usb/class/cdc-acm.c +@@ -331,7 +331,6 @@ static void acm_process_notification(struct acm *acm, unsigned char *buf) + + spin_lock_irqsave(&acm->read_lock, flags); + acm->ctrlin = newctrl; +- acm->oldcount = acm->iocount; + + if (difference & USB_CDC_SERIAL_STATE_DSR) + acm->iocount.dsr++; +@@ -788,6 +787,9 @@ static void acm_port_shutdown(struct tty_port *port) + usb_autopm_put_interface_async(acm->control); + } + ++ if (acm->disconnected) ++ return; ++ + acm_unpoison_urbs(acm); + + if (acm->quirks & ALWAYS_POLL_CTRL) { +@@ -1027,11 +1029,16 @@ static int wait_serial_change(struct acm *acm, unsigned long arg) + DECLARE_WAITQUEUE(wait, current); + struct async_icount old, new; + +- do { ++ spin_lock_irq(&acm->read_lock); ++ old = acm->iocount; ++ spin_unlock_irq(&acm->read_lock); ++ ++ add_wait_queue(&acm->wioctl, &wait); ++ for (;;) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ + spin_lock_irq(&acm->read_lock); +- old = acm->oldcount; + new = acm->iocount; +- acm->oldcount = new; + spin_unlock_irq(&acm->read_lock); + + if ((arg & TIOCM_DSR) && +@@ -1044,22 +1051,20 @@ static int wait_serial_change(struct acm *acm, unsigned long arg) + old.rng != new.rng) + break; + +- add_wait_queue(&acm->wioctl, &wait); +- set_current_state(TASK_INTERRUPTIBLE); +- schedule(); +- remove_wait_queue(&acm->wioctl, &wait); + if (acm->disconnected) { +- if (arg & TIOCM_CD) +- break; +- else +- rv = -ENODEV; +- } else { +- if (signal_pending(current)) +- rv = -ERESTARTSYS; ++ rv = -ENODEV; ++ break; + } +- } while (!rv); + +- ++ schedule(); ++ ++ if (signal_pending(current)) { ++ rv = -ERESTARTSYS; ++ break; ++ } ++ } ++ __set_current_state(TASK_RUNNING); ++ remove_wait_queue(&acm->wioctl, &wait); + + return rv; + } +diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h +--- a/drivers/usb/class/cdc-acm.h ++++ b/drivers/usb/class/cdc-acm.h +@@ -90,7 +90,6 @@ struct acm { + unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ + unsigned int ctrlout; /* output control lines (DTR, RTS) */ + struct async_icount iocount; /* counters for control line changes */ +- struct async_icount oldcount; /* for comparison of counter */ + wait_queue_head_t wioctl; /* for ioctl */ + unsigned int writesize; /* max packet size for the output bulk endpoint */ + unsigned int readsize,ctrlsize; /* buffer sizes for freeing */ +diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c +--- a/drivers/usb/core/hub.c ++++ b/drivers/usb/core/hub.c +@@ -73,8 +73,11 @@ + /* + * Give SS hubs 200ms time after wake to train downstream links before + * assuming no port activity and allowing hub to runtime suspend back. ++ * Root hubs have no upstream hub whose wake propagation needs to be ++ * accounted for, so they need less time, use 120ms for them. + */ + #define USB_SS_PORT_U0_WAKE_TIME 200 /* ms */ ++#define USB_SS_RH_PORT_U0_WAKE_TIME 120 /* ms */ + + /* Protect struct usb_device->state and ->children members + * Note: Both are also protected by ->dev.sem, except that ->state can +@@ -1358,7 +1361,9 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) + + queue_delayed_work(system_power_efficient_wq, + &hub->post_resume_work, +- msecs_to_jiffies(USB_SS_PORT_U0_WAKE_TIME)); ++ msecs_to_jiffies(hdev->parent ? ++ USB_SS_PORT_U0_WAKE_TIME : ++ USB_SS_RH_PORT_U0_WAKE_TIME)); + return; + } + +diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c +--- a/drivers/usb/core/message.c ++++ b/drivers/usb/core/message.c +@@ -1337,7 +1337,8 @@ static void remove_intf_ep_devs(struct usb_interface *intf) + * + * Disables the endpoint for URB submission and nukes all pending URBs. + * If @reset_hardware is set then also deallocates hcd/hardware state +- * for the endpoint. ++ * for the endpoint (clearing both ep_in and ep_out pointers for ++ * bidirectional non-ep0 control endpoints). + */ + void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr, + bool reset_hardware) +@@ -1358,6 +1359,11 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr, + dev->ep_in[epnum] = NULL; + } + if (ep) { ++ if (reset_hardware && epnum != 0 && ++ usb_endpoint_xfer_control(&ep->desc)) { ++ dev->ep_out[epnum] = NULL; ++ dev->ep_in[epnum] = NULL; ++ } + ep->enabled = 0; + usb_hcd_flush_endpoint(dev, ep); + if (reset_hardware) +diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c +--- a/drivers/usb/dwc2/hcd.c ++++ b/drivers/usb/dwc2/hcd.c +@@ -5082,14 +5082,13 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg) + } + + cancel_work_sync(&hsotg->phy_reset_work); +- +- timer_delete(&hsotg->wkp_timer); + } + + static void dwc2_hcd_release(struct dwc2_hsotg *hsotg) + { + /* Turn off all host-specific interrupts */ + dwc2_disable_host_interrupts(hsotg); ++ timer_shutdown_sync(&hsotg->wkp_timer); + + dwc2_hcd_free(hsotg); + } +diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h +--- a/drivers/usb/dwc3/core.h ++++ b/drivers/usb/dwc3/core.h +@@ -49,6 +49,7 @@ + #define DWC3_ENDPOINTS_NUM 32 + #define DWC3_XHCI_RESOURCES_NUM 2 + #define DWC3_ISOC_MAX_RETRIES 5 ++#define DWC3_ERR_RECOVERY_MAX 3 + + #define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */ + #define DWC3_EVENT_BUFFERS_SIZE 4096 +@@ -841,6 +842,12 @@ enum dwc3_link_state { + DWC3_LINK_STATE_MASK = 0x0f, + }; + ++enum dwc3_err_state { ++ DWC3_ERR_NONE = 0, ++ DWC3_ERR_RECOVERY, ++ DWC3_ERR_UNRECOVERABLE, ++}; ++ + /* TRB Length, PCM and Status */ + #define DWC3_TRB_SIZE_MASK (0x00ffffff) + #define DWC3_TRB_SIZE_LENGTH(n) ((n) & DWC3_TRB_SIZE_MASK) +@@ -1004,6 +1011,7 @@ struct dwc3_glue_ops { + /** + * struct dwc3 - representation of our controller + * @drd_work: workqueue used for role swapping ++ * @err_recovery_work: workqueue used for controller error recovery + * @ep0_trb: trb which is used for the ctrl_req + * @bounce: address of bounce buffer + * @setup_buf: used while precessing STD USB requests +@@ -1013,6 +1021,7 @@ struct dwc3_glue_ops { + * @ep0_in_setup: one control transfer is completed and enter setup phase + * @lock: for synchronizing + * @mutex: for mode switching ++ * @connect_mutex: for the pull-up and err_recovery_work + * @dev: pointer to our struct device + * @sysdev: pointer to the DMA-capable device + * @xhci: pointer to our xHCI child +@@ -1079,6 +1088,9 @@ struct dwc3_glue_ops { + * @ep0_next_event: hold the next expected event + * @ep0state: state of endpoint zero + * @link_state: link state ++ * @err_state: current error recovery state. ++ * @err_recovery_count: number of consecutive error recovery attempts until ++ * confirmed healthy and reset to 0 on reset event. + * @speed: device speed (super, high, full, low) + * @hwparams: copy of hwparams registers + * @regset: debugfs pointer to regdump file +@@ -1189,6 +1201,7 @@ struct dwc3_glue_ops { + */ + struct dwc3 { + struct work_struct drd_work; ++ struct work_struct err_recovery_work; + struct dwc3_trb *ep0_trb; + void *bounce; + u8 *setup_buf; +@@ -1203,6 +1216,9 @@ struct dwc3 { + /* mode switching lock */ + struct mutex mutex; + ++ /* serializes pull-up run/stop vs error recovery */ ++ struct mutex connect_mutex; ++ + struct device *dev; + struct device *sysdev; + +@@ -1332,6 +1348,9 @@ struct dwc3 { + enum dwc3_ep0_next ep0_next_event; + enum dwc3_ep0_state ep0state; + enum dwc3_link_state link_state; ++ enum dwc3_err_state err_state; ++ ++ u32 err_recovery_count; + + u16 u2sel; + u16 u2pel; +diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c +--- a/drivers/usb/dwc3/dwc3-am62.c ++++ b/drivers/usb/dwc3/dwc3-am62.c +@@ -299,6 +299,7 @@ static int dwc3_ti_probe(struct platform_device *pdev) + + err_pm_disable: + clk_disable_unprepare(am62->usb2_refclk); ++ pm_runtime_put_noidle(dev); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + return ret; +diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c +--- a/drivers/usb/dwc3/ep0.c ++++ b/drivers/usb/dwc3/ep0.c +@@ -200,6 +200,11 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, + int ret; + + spin_lock_irqsave(&dwc->lock, flags); ++ if (dwc->err_state != DWC3_ERR_NONE) { ++ ret = -ESHUTDOWN; ++ goto out; ++ } ++ + if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) { + dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n", + dep->name); +@@ -271,6 +276,10 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value) + int ret; + + spin_lock_irqsave(&dwc->lock, flags); ++ if (dwc->err_state != DWC3_ERR_NONE) { ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ return -ESHUTDOWN; ++ } + ret = __dwc3_gadget_ep0_set_halt(ep, value); + spin_unlock_irqrestore(&dwc->lock, flags); + +diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c +--- a/drivers/usb/dwc3/gadget.c ++++ b/drivers/usb/dwc3/gadget.c +@@ -1149,6 +1149,10 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep, + return 0; + + spin_lock_irqsave(&dwc->lock, flags); ++ if (dwc->err_state != DWC3_ERR_NONE) { ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ return -ESHUTDOWN; ++ } + ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT); + spin_unlock_irqrestore(&dwc->lock, flags); + +@@ -2060,6 +2064,10 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, + int ret; + + spin_lock_irqsave(&dwc->lock, flags); ++ if (dwc->err_state != DWC3_ERR_NONE) { ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ return -ESHUTDOWN; ++ } + ret = __dwc3_gadget_ep_queue(dep, req); + spin_unlock_irqrestore(&dwc->lock, flags); + +@@ -2292,6 +2300,10 @@ static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value) + int ret; + + spin_lock_irqsave(&dwc->lock, flags); ++ if (dwc->err_state != DWC3_ERR_NONE) { ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ return -ESHUTDOWN; ++ } + ret = __dwc3_gadget_ep_set_halt(dep, value, false); + spin_unlock_irqrestore(&dwc->lock, flags); + +@@ -2306,6 +2318,10 @@ static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep) + int ret; + + spin_lock_irqsave(&dwc->lock, flags); ++ if (dwc->err_state != DWC3_ERR_NONE) { ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ return -ESHUTDOWN; ++ } + dep->flags |= DWC3_EP_WEDGE; + + if (dep->number == 0 || dep->number == 1) +@@ -2437,6 +2453,10 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g) + } + + spin_lock_irqsave(&dwc->lock, flags); ++ if (dwc->err_state != DWC3_ERR_NONE) { ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ return -ESHUTDOWN; ++ } + if (!dwc->gadget->wakeup_armed) { + dev_err(dwc->dev, "not armed for remote wakeup\n"); + spin_unlock_irqrestore(&dwc->lock, flags); +@@ -2464,6 +2484,10 @@ static int dwc3_gadget_func_wakeup(struct usb_gadget *g, int intf_id) + } + + spin_lock_irqsave(&dwc->lock, flags); ++ if (dwc->err_state != DWC3_ERR_NONE) { ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ return -ESHUTDOWN; ++ } + /* + * If the link is in U3, signal for remote wakeup and wait for the + * link to transition to U0 before sending device notification. +@@ -2833,10 +2857,13 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) + + synchronize_irq(dwc->irq_gadget); + ++ /* Serialize against dwc3_err_recovery_work() */ ++ mutex_lock(&dwc->connect_mutex); + if (!is_on) + ret = dwc3_gadget_soft_disconnect(dwc); + else + ret = dwc3_gadget_soft_connect(dwc); ++ mutex_unlock(&dwc->connect_mutex); + + pm_runtime_put(dwc->dev); + +@@ -4153,6 +4180,10 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) + + dwc->suspended = false; + ++ /* The controller is recovered. */ ++ if (dwc->err_state == DWC3_ERR_NONE) ++ dwc->err_recovery_count = 0; ++ + /* + * Ideally, dwc3_reset_gadget() would trigger the function + * drivers to stop any active transfers through ep disable. +@@ -4640,6 +4671,12 @@ static irqreturn_t dwc3_thread_interrupt(int irq, void *_evt) + return ret; + } + ++static void dwc3_schedule_err_recovery(struct dwc3 *dwc) ++{ ++ dwc->err_state = DWC3_ERR_RECOVERY; ++ schedule_work(&dwc->err_recovery_work); ++} ++ + static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt) + { + struct dwc3 *dwc = evt->dwc; +@@ -4673,9 +4710,21 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt) + return IRQ_NONE; + + if (count > evt->length) { +- dev_err_ratelimited(dwc->dev, "invalid count(%u) > evt->length(%u)\n", ++ dev_err(dwc->dev, "invalid count(%u) > evt->length(%u)\n", + count, evt->length); +- return IRQ_NONE; ++ /* ++ * This is a fatal error - the driver and controller are out of ++ * sync on which event has been consumed. Reinitializing the ++ * controller is required to recover. Write the bogus count back ++ * to GEVNTCOUNT to clear the IRQ source, consistent with the ++ * stale event clearing in dwc3_event_buffers_setup(), then ++ * schedule error recovery. ++ */ ++ spin_lock(&dwc->lock); ++ dwc3_schedule_err_recovery(dwc); ++ spin_unlock(&dwc->lock); ++ dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), count); ++ return IRQ_HANDLED; + } + + evt->count = count; +@@ -4735,6 +4784,57 @@ static void dwc_gadget_release(struct device *dev) + kfree(gadget); + } + ++static void dwc3_err_recovery_work(struct work_struct *work) ++{ ++ struct dwc3 *dwc = container_of(work, struct dwc3, err_recovery_work); ++ unsigned long flags; ++ int ret; ++ ++ /* serializes against dwc3_gadget_pullup() */ ++ mutex_lock(&dwc->connect_mutex); ++ ++ ret = dwc3_gadget_soft_disconnect(dwc); ++ ++ dwc3_disconnect_gadget_sleepable(dwc); ++ ++ if (ret) ++ goto err_unrecoverable; ++ ++ if (dwc->softconnect) { ++ u32 count; ++ /* ++ * Wait irq to finish before soft_connect resets evt->lpos and ++ * the event buffer registers to avoid racing with ++ * dwc3_process_event_buf(). ++ */ ++ synchronize_irq(dwc->irq_gadget); ++ ++ spin_lock_irqsave(&dwc->lock, flags); ++ count = ++dwc->err_recovery_count; ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ ++ if (count > DWC3_ERR_RECOVERY_MAX) ++ goto err_unrecoverable; ++ ++ ret = dwc3_gadget_soft_connect(dwc); ++ if (ret) ++ goto err_unrecoverable; ++ } ++ mutex_unlock(&dwc->connect_mutex); ++ ++ spin_lock_irqsave(&dwc->lock, flags); ++ dwc->err_state = DWC3_ERR_NONE; ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ return; ++ ++err_unrecoverable: ++ dev_err(dwc->dev, "Unable to recover the controller\n"); ++ mutex_unlock(&dwc->connect_mutex); ++ spin_lock_irqsave(&dwc->lock, flags); ++ dwc->err_state = DWC3_ERR_UNRECOVERABLE; ++ spin_unlock_irqrestore(&dwc->lock, flags); ++} ++ + /** + * dwc3_gadget_init - initializes gadget related registers + * @dwc: pointer to our controller context structure +@@ -4778,6 +4878,8 @@ int dwc3_gadget_init(struct dwc3 *dwc) + } + + init_completion(&dwc->ep0_in_setup); ++ INIT_WORK(&dwc->err_recovery_work, dwc3_err_recovery_work); ++ mutex_init(&dwc->connect_mutex); + dwc->gadget = kzalloc_obj(struct usb_gadget); + if (!dwc->gadget) { + ret = -ENOMEM; +@@ -4874,6 +4976,8 @@ void dwc3_gadget_exit(struct dwc3 *dwc) + if (!dwc->gadget) + return; + ++ cancel_work_sync(&dwc->err_recovery_work); ++ mutex_destroy(&dwc->connect_mutex); + dwc3_enable_susphy(dwc, true); + usb_del_gadget(dwc->gadget); + dwc3_gadget_free_endpoints(dwc); +diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c +--- a/drivers/usb/gadget/function/f_fs.c ++++ b/drivers/usb/gadget/function/f_fs.c +@@ -1877,8 +1877,9 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, + break; + case FUNCTIONFS_ENDPOINT_DESC: + { ++ const struct usb_endpoint_descriptor *desc; ++ struct usb_endpoint_descriptor desc1; + int desc_idx; +- struct usb_endpoint_descriptor desc1, *desc; + + switch (epfile->ffs->gadget->speed) { + case USB_SPEED_SUPER: +@@ -1892,7 +1893,17 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, + desc_idx = 0; + } + +- desc = epfile->ep->descs[desc_idx]; ++ do { ++ desc = epfile->ep->descs[desc_idx]; ++ } while (!desc && --desc_idx >= 0); ++ ++ if (!desc) ++ desc = epfile->ep->ep->desc; ++ if (!desc) { ++ ret = -EINVAL; ++ break; ++ } ++ + memcpy(&desc1, desc, desc->bLength); + + spin_unlock_irq(&epfile->ffs->eps_lock); +diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c +--- a/drivers/usb/gadget/function/f_midi2.c ++++ b/drivers/usb/gadget/function/f_midi2.c +@@ -1633,8 +1633,8 @@ struct f_midi2_usb_config { + + /* MIDI 1.0 jacks */ + unsigned char jack_in, jack_out, jack_id; +- struct usb_midi_in_jack_descriptor jack_ins[MAX_CABLES]; +- struct usb_midi_out_jack_descriptor_1 jack_outs[MAX_CABLES]; ++ struct usb_midi_in_jack_descriptor jack_ins[MAX_CABLES * 2]; ++ struct usb_midi_out_jack_descriptor_1 jack_outs[MAX_CABLES * 2]; + }; + + static int append_config(struct f_midi2_usb_config *config, void *d) +diff --git a/drivers/usb/gadget/function/f_uac1_legacy.c b/drivers/usb/gadget/function/f_uac1_legacy.c +--- a/drivers/usb/gadget/function/f_uac1_legacy.c ++++ b/drivers/usb/gadget/function/f_uac1_legacy.c +@@ -797,6 +797,9 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f) + + static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value) + { ++ if (cmd >= ARRAY_SIZE(con->data)) ++ return -EINVAL; ++ + con->data[cmd] = value; + + return 0; +@@ -804,6 +807,9 @@ static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value) + + static int generic_get_cmd(struct usb_audio_control *con, u8 cmd) + { ++ if (cmd >= ARRAY_SIZE(con->data)) ++ return -EINVAL; ++ + return con->data[cmd]; + } + +diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c +--- a/drivers/usb/gadget/udc/aspeed-vhub/core.c ++++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c +@@ -267,6 +267,9 @@ static void ast_vhub_remove(struct platform_device *pdev) + for (i = 0; i < vhub->max_ports; i++) + ast_vhub_del_dev(&vhub->ports[i].dev); + ++ /* Final drain; the worker takes vhub->lock, so stay outside of it */ ++ cancel_work_sync(&vhub->wake_work); ++ + spin_lock_irqsave(&vhub->lock, flags); + + /* Mask & ack all interrupts */ +@@ -328,6 +331,7 @@ static int ast_vhub_probe(struct platform_device *pdev) + return -ENOMEM; + + spin_lock_init(&vhub->lock); ++ INIT_WORK(&vhub->wake_work, ast_vhub_wake_work); + vhub->pdev = pdev; + vhub->port_irq_mask = GENMASK(VHUB_IRQ_DEV1_BIT + vhub->max_ports - 1, + VHUB_IRQ_DEV1_BIT); +diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c +--- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c ++++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c +@@ -280,7 +280,7 @@ static int ast_vhub_udc_wakeup(struct usb_gadget* gadget) + int rc = -EINVAL; + + spin_lock_irqsave(&d->vhub->lock, flags); +- if (!d->wakeup_en) ++ if (!d->wakeup_en || !d->registered) + goto err; + + DDBG(d, "Device initiated wakeup\n"); +diff --git a/drivers/usb/gadget/udc/aspeed-vhub/hub.c b/drivers/usb/gadget/udc/aspeed-vhub/hub.c +--- a/drivers/usb/gadget/udc/aspeed-vhub/hub.c ++++ b/drivers/usb/gadget/udc/aspeed-vhub/hub.c +@@ -558,7 +558,7 @@ void ast_vhub_device_connect(struct ast_vhub *vhub, + ast_vhub_send_host_wakeup(vhub); + } + +-static void ast_vhub_wake_work(struct work_struct *work) ++void ast_vhub_wake_work(struct work_struct *work) + { + struct ast_vhub *vhub = container_of(work, + struct ast_vhub, +@@ -588,6 +588,8 @@ static void ast_vhub_wake_work(struct work_struct *work) + + void ast_vhub_hub_wake_all(struct ast_vhub *vhub) + { ++ lockdep_assert_held(&vhub->lock); ++ + /* + * A device is trying to wake the world, because this + * can recurse into the device, we break the call chain +@@ -1076,7 +1078,6 @@ static int ast_vhub_init_desc(struct ast_vhub *vhub) + int ast_vhub_init_hub(struct ast_vhub *vhub) + { + vhub->speed = USB_SPEED_UNKNOWN; +- INIT_WORK(&vhub->wake_work, ast_vhub_wake_work); + + return ast_vhub_init_desc(vhub); + } +diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h +--- a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h ++++ b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h +@@ -547,6 +547,7 @@ void ast_vhub_hub_suspend(struct ast_vhub *vhub); + void ast_vhub_hub_resume(struct ast_vhub *vhub); + void ast_vhub_hub_reset(struct ast_vhub *vhub); + void ast_vhub_hub_wake_all(struct ast_vhub *vhub); ++void ast_vhub_wake_work(struct work_struct *work); + + /* dev.c */ + int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx); +diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c +--- a/drivers/usb/gadget/udc/dummy_hcd.c ++++ b/drivers/usb/gadget/udc/dummy_hcd.c +@@ -343,9 +343,11 @@ static void dummy_giveback(struct dummy *dum, struct usb_ep *_ep, + { + bool fifo = req == &dum->fifo_req; + ++ ++dum->callback_usage; + spin_unlock(&dum->lock); + usb_gadget_giveback_request(_ep, &req->req); + spin_lock(&dum->lock); ++ --dum->callback_usage; + if (fifo) + dum->fifo_req_busy = 0; + } +@@ -759,11 +761,13 @@ static int dummy_queue(struct usb_ep *_ep, struct usb_request *_req, + req->req.complete = fifo_complete; + + list_add_tail(&req->queue, &ep->queue); ++ ++dum->callback_usage; + spin_unlock(&dum->lock); + _req->actual = _req->length; + _req->status = 0; + usb_gadget_giveback_request(_ep, _req); + spin_lock(&dum->lock); ++ --dum->callback_usage; + } else + list_add_tail(&req->queue, &ep->queue); + spin_unlock_irqrestore(&dum->lock, flags); +diff --git a/drivers/usb/host/octeon-hcd.c b/drivers/usb/host/octeon-hcd.c +--- a/drivers/usb/host/octeon-hcd.c ++++ b/drivers/usb/host/octeon-hcd.c +@@ -378,8 +378,22 @@ struct octeon_hcd { + struct cvmx_usb_transaction *active_split; + struct cvmx_usb_tx_fifo periodic; + struct cvmx_usb_tx_fifo nonperiodic; ++ struct hrtimer sof_timer; + }; + ++static int cvmx_usb_poll(struct octeon_hcd *usb); ++ ++static enum hrtimer_restart octeon_usb_sof_timer(struct hrtimer *t) ++{ ++ struct octeon_hcd *usb = container_of(t, struct octeon_hcd, sof_timer); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&usb->lock, flags); ++ cvmx_usb_poll(usb); ++ spin_unlock_irqrestore(&usb->lock, flags); ++ return HRTIMER_NORESTART; ++} ++ + /* + * This macro logically sets a single field in a CSR. It does the sequence + * read, modify, and write +@@ -578,7 +592,7 @@ static int cvmx_wait_tx_rx(struct octeon_hcd *usb, int fflsh_type) + int result; + u64 address = CVMX_USBCX_GRSTCTL(usb->index); + u64 done = cvmx_get_cycle() + 100 * +- (u64)octeon_get_clock_rate / 1000000; ++ (u64)octeon_get_clock_rate() / 1000000; + union cvmx_usbcx_grstctl c; + + while (1) { +@@ -853,6 +867,14 @@ static int cvmx_usb_initialize(struct device *dev, + * USBC_GAHBCFG[PTXFEMPLVL] + * Global interrupt mask, USBC_GAHBCFG[GLBLINTRMSK] = 1 + */ ++ usbcx_gahbcfg.u32 = cvmx_usb_read_csr32(usb, ++ CVMX_USBCX_GHWCFG3(usb->index)); ++ if (usbcx_gahbcfg.u32 == 0xffffffff || usbcx_gahbcfg.u32 == 0) { ++ dev_err(dev, "USB core is not responding (GHWCFG3=0x%08x)\n", ++ usbcx_gahbcfg.u32); ++ return -ENODEV; ++ } ++ + usbcx_gahbcfg.u32 = 0; + usbcx_gahbcfg.s.dmaen = !(usb->init_flags & + CVMX_USB_INITIALIZE_FLAGS_NO_DMA); +@@ -1900,6 +1922,7 @@ static void cvmx_usb_schedule(struct octeon_hcd *usb, int is_sof) + int channel; + struct cvmx_usb_pipe *pipe; + int need_sof; ++ u64 min_due; + enum cvmx_usb_transfer ttype; + + if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA) { +@@ -1940,15 +1963,33 @@ static void cvmx_usb_schedule(struct octeon_hcd *usb, int is_sof) + * future that might need to be scheduled + */ + need_sof = 0; ++ min_due = ~0ull; + for (ttype = CVMX_USB_TRANSFER_CONTROL; + ttype <= CVMX_USB_TRANSFER_INTERRUPT; ttype++) { + list_for_each_entry(pipe, &usb->active_pipes[ttype], node) { +- if (pipe->next_tx_frame > usb->frame_number) { +- need_sof = 1; +- break; +- } ++ if (pipe->next_tx_frame > usb->frame_number && ++ pipe->next_tx_frame < min_due) ++ min_due = pipe->next_tx_frame; + } + } ++ if (min_due != ~0ull) { ++ u64 delta = min_due - usb->frame_number; ++ ++ /* ++ * frame_number is resynced from HFNUM on every poll, so a ++ * deadline that is many frames away does not need an ++ * interrupt on every SOF to count them down - sleep on the ++ * timer instead and keep SOF interrupts for deadlines within ++ * a few frames. Stay well below the 16383-frame wrap of ++ * HFNUM. One (micro)frame is 125us in high-speed mode. ++ */ ++ if (delta <= 4 || delta > 8000) ++ need_sof = 1; ++ else ++ hrtimer_start(&usb->sof_timer, ++ ns_to_ktime((delta - 2) * 125000), ++ HRTIMER_MODE_REL); ++ } + USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), + cvmx_usbcx_gintmsk, sofmsk, need_sof); + } +@@ -3638,6 +3679,8 @@ static int octeon_usb_probe(struct platform_device *pdev) + usb = (struct octeon_hcd *)hcd->hcd_priv; + + spin_lock_init(&usb->lock); ++ hrtimer_setup(&usb->sof_timer, octeon_usb_sof_timer, CLOCK_MONOTONIC, ++ HRTIMER_MODE_REL); + + usb->init_flags = initialize_flags; + +@@ -3688,6 +3731,7 @@ static void octeon_usb_remove(struct platform_device *pdev) + unsigned long flags; + + usb_remove_hcd(hcd); ++ hrtimer_cancel(&usb->sof_timer); + spin_lock_irqsave(&usb->lock, flags); + status = cvmx_usb_shutdown(usb); + spin_unlock_irqrestore(&usb->lock, flags); +diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c +--- a/drivers/usb/host/ohci-da8xx.c ++++ b/drivers/usb/host/ohci-da8xx.c +@@ -447,6 +447,7 @@ static int ohci_da8xx_probe(struct platform_device *pdev) + + err_remove_hcd: + usb_remove_hcd(hcd); ++ device_wakeup_disable(hcd->self.controller); + err: + usb_put_hcd(hcd); + return error; +@@ -457,6 +458,7 @@ static void ohci_da8xx_remove(struct platform_device *pdev) + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + usb_remove_hcd(hcd); ++ device_wakeup_disable(hcd->self.controller); + usb_put_hcd(hcd); + } + +diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c +--- a/drivers/usb/host/ohci-s3c2410.c ++++ b/drivers/usb/host/ohci-s3c2410.c +@@ -335,6 +335,7 @@ ohci_hcd_s3c2410_remove(struct platform_device *dev) + struct usb_hcd *hcd = platform_get_drvdata(dev); + + usb_remove_hcd(hcd); ++ device_wakeup_disable(hcd->self.controller); + s3c2410_stop_hc(dev); + usb_put_hcd(hcd); + } +diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c +--- a/drivers/usb/host/ohci-spear.c ++++ b/drivers/usb/host/ohci-spear.c +@@ -103,6 +103,7 @@ static void spear_ohci_hcd_drv_remove(struct platform_device *pdev) + struct spear_ohci *sohci_p = to_spear_ohci(hcd); + + usb_remove_hcd(hcd); ++ device_wakeup_disable(hcd->self.controller); + clk_disable_unprepare(sohci_p->clk); + + usb_put_hcd(hcd); +diff --git a/drivers/usb/host/ohci-st.c b/drivers/usb/host/ohci-st.c +--- a/drivers/usb/host/ohci-st.c ++++ b/drivers/usb/host/ohci-st.c +@@ -235,6 +235,7 @@ static void st_ohci_platform_remove(struct platform_device *dev) + int clk; + + usb_remove_hcd(hcd); ++ device_wakeup_disable(hcd->self.controller); + + if (pdata->power_off) + pdata->power_off(dev); +diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c +--- a/drivers/usb/serial/bus.c ++++ b/drivers/usb/serial/bus.c +@@ -165,7 +165,11 @@ int usb_serial_bus_register(struct usb_serial_driver *driver) + + void usb_serial_bus_deregister(struct usb_serial_driver *driver) + { +- free_dynids(driver); + driver_unregister(&driver->driver); ++ free_dynids(driver); + } + ++void usb_serial_bus_remove_new_id(struct usb_serial_driver *driver) ++{ ++ driver_remove_file(&driver->driver, &driver_attr_new_id); ++} +diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c +--- a/drivers/usb/serial/cp210x.c ++++ b/drivers/usb/serial/cp210x.c +@@ -223,6 +223,7 @@ static const struct usb_device_id id_table[] = { + { USB_DEVICE(0x19CF, 0x3000) }, /* Parrot NMEA GPS Flight Recorder */ + { USB_DEVICE(0x1ADB, 0x0001) }, /* Schweitzer Engineering C662 Cable */ + { USB_DEVICE(0x1B1C, 0x1C00) }, /* Corsair USB Dongle */ ++ { USB_DEVICE(0x1B1C, 0x1C02) }, /* Corsair AX1500i Power Supply */ + { USB_DEVICE(0x1B93, 0x1013) }, /* Phoenix Contact UPS Device */ + { USB_DEVICE(0x1BA4, 0x0002) }, /* Silicon Labs 358x factory default */ + { USB_DEVICE(0x1BE3, 0x07A6) }, /* WAGO 750-923 USB Service Cable */ +diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c +--- a/drivers/usb/serial/generic.c ++++ b/drivers/usb/serial/generic.c +@@ -266,6 +266,7 @@ EXPORT_SYMBOL_GPL(usb_serial_generic_chars_in_buffer); + void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout) + { + struct usb_serial_port *port = tty->driver_data; ++ struct tty_port *tport = &port->port; + unsigned int bps; + unsigned long period; + unsigned long expire; +@@ -285,7 +286,14 @@ void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout) + __func__, jiffies_to_msecs(timeout), + jiffies_to_msecs(period)); + expire = jiffies + timeout; +- while (!port->serial->type->tx_empty(port)) { ++ for (;;) { ++ mutex_lock(&tport->mutex); ++ if (tty_io_error(tty) || port->serial->type->tx_empty(port)) { ++ mutex_unlock(&tport->mutex); ++ break; ++ } ++ mutex_unlock(&tport->mutex); ++ + schedule_timeout_interruptible(period); + if (signal_pending(current)) + break; +diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c +--- a/drivers/usb/serial/option.c ++++ b/drivers/usb/serial/option.c +@@ -260,6 +260,7 @@ static void option_instat_callback(struct urb *urb); + #define QUECTEL_PRODUCT_EM060K_12a 0x012a + #define QUECTEL_PRODUCT_EM060K_12b 0x012b + #define QUECTEL_PRODUCT_EM060K_12c 0x012c ++#define QUECTEL_PRODUCT_RG660QB 0x013d + #define QUECTEL_PRODUCT_EG91 0x0191 + #define QUECTEL_PRODUCT_EG95 0x0195 + #define QUECTEL_PRODUCT_BG96 0x0296 +@@ -280,6 +281,7 @@ static void option_instat_callback(struct urb *urb); + #define QUECTEL_PRODUCT_EC200U 0x0901 + #define QUECTEL_PRODUCT_EG912Y 0x6001 + #define QUECTEL_PRODUCT_EC200S_CN 0x6002 ++#define QUECTEL_PRODUCT_EG060W 0x6004 + #define QUECTEL_PRODUCT_EC200A 0x6005 + #define QUECTEL_PRODUCT_EG916Q 0x6007 + #define QUECTEL_PRODUCT_EM061K_LWW 0x6008 +@@ -1268,12 +1270,15 @@ static const struct usb_device_id option_ids[] = { + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200A, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200U, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200S_CN, 0xff, 0, 0) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG060W, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200T, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG912Y, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG916Q, 0xff, 0x00, 0x00) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500K, 0xff, 0x00, 0x00) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG650V, 0xff, 0xff, 0x30) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG650V, 0xff, 0, 0) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG660QB, 0xff, 0xff, 0x30) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG660QB, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0xff, 0x30) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0xff, 0x40) }, +@@ -2168,6 +2173,8 @@ static const struct usb_device_id option_ids[] = { + { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9003, 0xff) }, /* Simcom SIM7500/SIM7600 MBIM mode */ + { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9011, 0xff), /* Simcom SIM7500/SIM7600 RNDIS mode */ + .driver_info = RSVD(7) }, ++ { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x902b, 0xff), ++ .driver_info = RSVD(4) }, + { USB_DEVICE(0x1e0e, 0x9071), /* Simcom SIM8230 RMNET mode */ + .driver_info = RSVD(3) | RSVD(4) }, + { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9078, 0xff), /* Simcom SIM8230 ECM mode */ +@@ -2429,6 +2436,13 @@ static const struct usb_device_id option_ids[] = { + .driver_info = RSVD(5) }, + { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe167, 0xff), /* Foxconn T99W640 MBIM */ + .driver_info = RSVD(3) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x01) }, /* Compal EXC-T1 */ ++ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x02) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x03) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x04) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x30) }, /* Compal EXM-G1x */ ++ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x40) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x60) }, + { USB_DEVICE(0x1508, 0x1001), /* Fibocom NL668 (IOT version) */ + .driver_info = RSVD(4) | RSVD(5) | RSVD(6) }, + { USB_DEVICE(0x1782, 0x4d10) }, /* Fibocom L610 (AT mode) */ +diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c +--- a/drivers/usb/serial/quatech2.c ++++ b/drivers/usb/serial/quatech2.c +@@ -52,6 +52,7 @@ + #define QT2_XMIT_FLUSH 0x05 /* no following info */ + #define QT2_CONTROL_ESCAPE 0xff /* pass through previous 2 control bytes */ + ++#define MIN_BAUD_RATE 50 + #define MAX_BAUD_RATE 921600 + #define DEFAULT_BAUD_RATE 9600 + +@@ -156,7 +157,7 @@ static inline int calc_baud_divisor(int baudrate) + + static inline int qt2_set_port_config(struct usb_device *dev, + unsigned char port_number, +- u16 baudrate, u16 lcr) ++ speed_t baudrate, u16 lcr) + { + int divisor = calc_baud_divisor(baudrate); + u16 index = ((u16) (lcr << 8) | (u16) (port_number)); +@@ -257,9 +258,9 @@ static void qt2_set_termios(struct tty_struct *tty, + struct usb_device *dev = port->serial->dev; + struct qt2_port_private *port_priv; + struct ktermios *termios = &tty->termios; +- u16 baud; + unsigned int cflag = termios->c_cflag; + u16 new_lcr = 0; ++ speed_t baud; + int status; + + port_priv = usb_get_serial_port_data(port); +@@ -277,6 +278,18 @@ static void qt2_set_termios(struct tty_struct *tty, + if (!baud) + baud = 9600; + ++ if (baud < MIN_BAUD_RATE || baud > MAX_BAUD_RATE) { ++ if (old_termios) ++ baud = tty_termios_baud_rate(old_termios); ++ else ++ baud = clamp(baud, MIN_BAUD_RATE, MAX_BAUD_RATE); ++ ++ tty_encode_baud_rate(tty, baud, baud); ++ ++ if (!baud) ++ baud = 9600; ++ } ++ + status = qt2_set_port_config(dev, port_priv->device_port, baud, + new_lcr); + if (status < 0) +@@ -373,7 +386,7 @@ static int qt2_open(struct tty_struct *tty, struct usb_serial_port *port) + port_priv->device_port = (u8) device_port; + + if (tty) +- qt2_set_termios(tty, port, &tty->termios); ++ qt2_set_termios(tty, port, NULL); + + return 0; + +diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c +--- a/drivers/usb/serial/ssu100.c ++++ b/drivers/usb/serial/ssu100.c +@@ -32,6 +32,7 @@ + + #define SERIAL_EVEN_PARITY (UART_LCR_PARITY | UART_LCR_EPAR) + ++#define MIN_BAUD_RATE 50 + #define MAX_BAUD_RATE 460800 + + #define ATC_DISABLED 0x00 +@@ -217,9 +218,10 @@ static void ssu100_set_termios(struct tty_struct *tty, + { + struct usb_device *dev = port->serial->dev; + struct ktermios *termios = &tty->termios; +- u16 baud, divisor, remainder; ++ speed_t baud, remainder; + unsigned int cflag = termios->c_cflag; + u16 urb_value = 0; /* will hold the new flags */ ++ u16 divisor; + int result; + + if (cflag & PARENB) { +@@ -235,6 +237,18 @@ static void ssu100_set_termios(struct tty_struct *tty, + if (!baud) + baud = 9600; + ++ if (baud < MIN_BAUD_RATE || baud > MAX_BAUD_RATE) { ++ if (old_termios) ++ baud = tty_termios_baud_rate(old_termios); ++ else ++ baud = clamp(baud, MIN_BAUD_RATE, MAX_BAUD_RATE); ++ ++ tty_encode_baud_rate(tty, baud, baud); ++ ++ if (!baud) ++ baud = 9600; ++ } ++ + dev_dbg(&port->dev, "%s - got baud = %d\n", __func__, baud); + + +@@ -310,7 +324,7 @@ static int ssu100_open(struct tty_struct *tty, struct usb_serial_port *port) + dev_dbg(&port->dev, "%s - set uart failed\n", __func__); + + if (tty) +- ssu100_set_termios(tty, port, &tty->termios); ++ ssu100_set_termios(tty, port, NULL); + + return usb_serial_generic_open(tty, port); + } +diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c +--- a/drivers/usb/serial/usb-serial.c ++++ b/drivers/usb/serial/usb-serial.c +@@ -400,17 +400,13 @@ static unsigned int serial_chars_in_buffer(struct tty_struct *tty) + static void serial_wait_until_sent(struct tty_struct *tty, int timeout) + { + struct usb_serial_port *port = tty->driver_data; +- struct usb_serial *serial = port->serial; + + dev_dbg(&port->dev, "%s\n", __func__); + + if (!port->serial->type->wait_until_sent) + return; + +- mutex_lock(&serial->disc_mutex); +- if (!serial->disconnected) +- port->serial->type->wait_until_sent(tty, timeout); +- mutex_unlock(&serial->disc_mutex); ++ port->serial->type->wait_until_sent(tty, timeout); + } + + static void serial_throttle(struct tty_struct *tty) +@@ -438,8 +434,13 @@ static int serial_get_serial(struct tty_struct *tty, struct serial_struct *ss) + struct usb_serial_port *port = tty->driver_data; + struct tty_port *tport = &port->port; + unsigned int close_delay, closing_wait; ++ int ret = 0; + + mutex_lock(&tport->mutex); ++ if (tty_io_error(tty)) { ++ ret = -EIO; ++ goto out_unlock; ++ } + + close_delay = jiffies_to_msecs(tport->close_delay) / 10; + closing_wait = tport->closing_wait; +@@ -452,10 +453,10 @@ static int serial_get_serial(struct tty_struct *tty, struct serial_struct *ss) + + if (port->serial->type->get_serial) + port->serial->type->get_serial(tty, ss); +- ++out_unlock: + mutex_unlock(&tport->mutex); + +- return 0; ++ return ret; + } + + static int serial_set_serial(struct tty_struct *tty, struct serial_struct *ss) +@@ -471,6 +472,10 @@ static int serial_set_serial(struct tty_struct *tty, struct serial_struct *ss) + closing_wait = msecs_to_jiffies(closing_wait * 10); + + mutex_lock(&tport->mutex); ++ if (tty_io_error(tty)) { ++ ret = -EIO; ++ goto out_unlock; ++ } + + if (!capable(CAP_SYS_ADMIN)) { + if (close_delay != tport->close_delay || +@@ -498,6 +503,7 @@ static int serial_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) + { + struct usb_serial_port *port = tty->driver_data; ++ struct tty_port *tport = &port->port; + int retval = -ENOIOCTLCMD; + + dev_dbg(&port->dev, "%s - cmd 0x%04x\n", __func__, cmd); +@@ -508,8 +514,21 @@ static int serial_ioctl(struct tty_struct *tty, + retval = port->serial->type->tiocmiwait(tty, arg); + break; + default: +- if (port->serial->type->ioctl) ++ if (!port->serial->type->ioctl) ++ break; ++ ++ if (cmd == TIOCSRS485) ++ down_write(&tty->termios_rwsem); ++ ++ mutex_lock(&tport->mutex); ++ if (tty_io_error(tty)) ++ retval = -EIO; ++ else + retval = port->serial->type->ioctl(tty, cmd, arg); ++ mutex_unlock(&tport->mutex); ++ ++ if (cmd == TIOCSRS485) ++ up_write(&tty->termios_rwsem); + } + + return retval; +@@ -519,25 +538,40 @@ static void serial_set_termios(struct tty_struct *tty, + const struct ktermios *old) + { + struct usb_serial_port *port = tty->driver_data; ++ struct tty_port *tport = &port->port; + + dev_dbg(&port->dev, "%s\n", __func__); + +- if (port->serial->type->set_termios) +- port->serial->type->set_termios(tty, port, old); +- else ++ if (!port->serial->type->set_termios) { + tty_termios_copy_hw(&tty->termios, old); ++ return; ++ } ++ ++ mutex_lock(&tport->mutex); ++ if (!tty_io_error(tty)) ++ port->serial->type->set_termios(tty, port, old); ++ mutex_unlock(&tport->mutex); + } + + static int serial_break(struct tty_struct *tty, int break_state) + { + struct usb_serial_port *port = tty->driver_data; ++ struct tty_port *tport = &port->port; ++ int ret; + + dev_dbg(&port->dev, "%s\n", __func__); + +- if (port->serial->type->break_ctl) +- return port->serial->type->break_ctl(tty, break_state); ++ if (!port->serial->type->break_ctl) ++ return -ENOTTY; + +- return -ENOTTY; ++ mutex_lock(&tport->mutex); ++ if (tty_io_error(tty)) ++ ret = -EIO; ++ else ++ ret = port->serial->type->break_ctl(tty, break_state); ++ mutex_unlock(&tport->mutex); ++ ++ return ret; + } + + static int serial_proc_show(struct seq_file *m, void *v) +@@ -578,24 +612,44 @@ static int serial_proc_show(struct seq_file *m, void *v) + static int serial_tiocmget(struct tty_struct *tty) + { + struct usb_serial_port *port = tty->driver_data; ++ struct tty_port *tport = &port->port; ++ int ret; + + dev_dbg(&port->dev, "%s\n", __func__); + +- if (port->serial->type->tiocmget) +- return port->serial->type->tiocmget(tty); +- return -ENOTTY; ++ if (!port->serial->type->tiocmget) ++ return -ENOTTY; ++ ++ mutex_lock(&tport->mutex); ++ if (tty_io_error(tty)) ++ ret = -EIO; ++ else ++ ret = port->serial->type->tiocmget(tty); ++ mutex_unlock(&tport->mutex); ++ ++ return ret; + } + + static int serial_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) + { + struct usb_serial_port *port = tty->driver_data; ++ struct tty_port *tport = &port->port; ++ int ret; + + dev_dbg(&port->dev, "%s\n", __func__); + +- if (port->serial->type->tiocmset) +- return port->serial->type->tiocmset(tty, set, clear); +- return -ENOTTY; ++ if (!port->serial->type->tiocmset) ++ return -ENOTTY; ++ ++ mutex_lock(&tport->mutex); ++ if (tty_io_error(tty)) ++ ret = -EIO; ++ else ++ ret = port->serial->type->tiocmset(tty, set, clear); ++ mutex_unlock(&tport->mutex); ++ ++ return ret; + } + + static int serial_get_icount(struct tty_struct *tty, +@@ -1191,11 +1245,16 @@ static void usb_serial_disconnect(struct usb_interface *interface) + usb_serial_port_poison_urbs(port); + wake_up_interruptible(&port->port.delta_msr_wait); + cancel_work_sync(&port->work); ++ } ++ ++ if (serial->type->disconnect) ++ serial->type->disconnect(serial); ++ ++ for (i = 0; i < serial->num_ports; ++i) { ++ port = serial->port[i]; + if (device_is_registered(&port->dev)) + device_del(&port->dev); + } +- if (serial->type->disconnect) +- serial->type->disconnect(serial); + + release_sibling(serial, interface); + +@@ -1464,7 +1523,7 @@ int __usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers + { + int rc; + struct usb_driver *udriver; +- struct usb_serial_driver * const *sd; ++ struct usb_serial_driver * const *sd, * const *s; + + /* + * udriver must be registered before any of the serial drivers, +@@ -1517,9 +1576,11 @@ int __usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers + return 0; + + err_deregister_drivers: ++ for (s = serial_drivers; s < sd; ++s) ++ usb_serial_bus_remove_new_id(*s); ++ usb_deregister(udriver); + while (sd-- > serial_drivers) + usb_serial_deregister(*sd); +- usb_deregister(udriver); + err_free_driver: + kfree(udriver); + return rc; +@@ -1537,10 +1598,23 @@ EXPORT_SYMBOL_GPL(__usb_serial_register_drivers); + void usb_serial_deregister_drivers(struct usb_serial_driver *const serial_drivers[]) + { + struct usb_driver *udriver = (*serial_drivers)->usb_driver; ++ struct usb_serial_driver * const *sd; ++ ++ /* ++ * udriver must be deregistered before the serial drivers so that ++ * I/O is stopped before unbinding the ports. ++ * ++ * Remove the new_id attributes to prevent ids from being added and ++ * triggering a probe of udriver after it has been deregistered. ++ */ ++ for (sd = serial_drivers; *sd; ++sd) ++ usb_serial_bus_remove_new_id(*sd); + +- for (; *serial_drivers; ++serial_drivers) +- usb_serial_deregister(*serial_drivers); + usb_deregister(udriver); ++ ++ for (sd = serial_drivers; *sd; ++sd) ++ usb_serial_deregister(*sd); ++ + kfree(udriver); + } + EXPORT_SYMBOL_GPL(usb_serial_deregister_drivers); +diff --git a/drivers/usb/serial/xr_serial.c b/drivers/usb/serial/xr_serial.c +--- a/drivers/usb/serial/xr_serial.c ++++ b/drivers/usb/serial/xr_serial.c +@@ -850,12 +850,9 @@ static int xr_get_rs485_config(struct tty_struct *tty, + struct usb_serial_port *port = tty->driver_data; + struct xr_data *data = usb_get_serial_port_data(port); + +- down_read(&tty->termios_rwsem); +- if (copy_to_user(argp, &data->rs485, sizeof(data->rs485))) { +- up_read(&tty->termios_rwsem); ++ /* core holds port mutex */ ++ if (copy_to_user(argp, &data->rs485, sizeof(data->rs485))) + return -EFAULT; +- } +- up_read(&tty->termios_rwsem); + + return 0; + } +@@ -871,10 +868,9 @@ static int xr_set_rs485_config(struct tty_struct *tty, + return -EFAULT; + xr_sanitize_serial_rs485(&rs485); + +- down_write(&tty->termios_rwsem); ++ /* core holds termios rwsem and port mutex */ + data->rs485 = rs485; + xr_set_flow_mode(tty, port, NULL); +- up_write(&tty->termios_rwsem); + + if (copy_to_user(argp, &rs485, sizeof(rs485))) + return -EFAULT; +diff --git a/drivers/usb/storage/sierra_ms.c b/drivers/usb/storage/sierra_ms.c +--- a/drivers/usb/storage/sierra_ms.c ++++ b/drivers/usb/storage/sierra_ms.c +@@ -77,6 +77,13 @@ static int sierra_get_swoc_info(struct usb_device *udev, + sizeof(struct swoc_info), /* __u16 size */ + USB_CTRL_SET_TIMEOUT); /* int timeout */ + ++ /* ++ * A short IN transfer leaves the tail of swocInfo uninitialized; ++ * only a full transfer is valid. ++ */ ++ if (result != sizeof(struct swoc_info)) ++ return -EIO; ++ + swocInfo->LinuxSKU = le16_to_cpu(swocInfo->LinuxSKU); + swocInfo->LinuxVer = le16_to_cpu(swocInfo->LinuxVer); + return result; +diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c +--- a/drivers/usb/typec/anx7411.c ++++ b/drivers/usb/typec/anx7411.c +@@ -1566,8 +1566,11 @@ static void anx7411_i2c_remove(struct i2c_client *client) + anx7411_partner_unregister_altmode(plat); + anx7411_unregister_partner(plat); + +- if (plat->workqueue) ++ if (plat->workqueue) { ++ disable_irq(plat->intp_irq); ++ cancel_work_sync(&plat->work); + destroy_workqueue(plat->workqueue); ++ } + + i2c_unregister_device(plat->spi_client); + +diff --git a/drivers/usb/typec/port-mapper.c b/drivers/usb/typec/port-mapper.c +--- a/drivers/usb/typec/port-mapper.c ++++ b/drivers/usb/typec/port-mapper.c +@@ -42,6 +42,23 @@ static int usb4_port_compare(struct device *dev, void *fwnode) + return usb4_usb3_port_match(dev, fwnode); + } + ++static bool typec_has_usb4_host_interface(const struct fwnode_handle *fwnode) ++{ ++ if (!IS_REACHABLE(CONFIG_USB4)) ++ return false; ++ ++ struct fwnode_handle *nhi_fwnode __free(fwnode_handle) = ++ fwnode_find_reference(fwnode, "usb4-host-interface", 0); ++ ++ /* ++ * The USB4 port can only appear if the host interface is enabled in ++ * the firmware and has been enumerated as a device. The latter is ++ * the same check as in usb_acpi_add_usb4_devlink(). ++ */ ++ return !IS_ERR(nhi_fwnode) && fwnode_device_is_available(nhi_fwnode) && ++ nhi_fwnode->dev; ++} ++ + static int typec_port_compare(struct device *dev, void *fwnode) + { + return device_match_fwnode(dev, fwnode); +@@ -64,11 +81,11 @@ static int typec_port_match(struct device *dev, void *data) + adev_fwnode); + + /* +- * If dev is USB 3.x port, it may have reference to the ++ * If dev is USB 3.x port, it may have reference to an available + * USB4 host interface in which case we can also link the + * Type-C port with the USB4 port. + */ +- if (fwnode_property_present(adev_fwnode, "usb4-host-interface")) ++ if (typec_has_usb4_host_interface(adev_fwnode)) + component_match_add(&arg->port->dev, &arg->match, + usb4_port_compare, adev_fwnode); + } +diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c +--- a/drivers/usb/typec/tcpm/tcpm.c ++++ b/drivers/usb/typec/tcpm/tcpm.c +@@ -1756,7 +1756,8 @@ static void mod_enable_frs_delayed_work(struct tcpm_port *port, unsigned int del + static void mod_vdm_discovery_cancel_delayed_work(struct tcpm_port *port) + { + hrtimer_cancel(&port->vdm_discovery_timer); +- kthread_cancel_work_sync(&port->vdm_discovery_work); ++ if (port->wq) ++ kthread_cancel_work_sync(&port->vdm_discovery_work); + } + + static void mod_vdm_discovery_delayed_work(struct tcpm_port *port, unsigned int delay_ms) +@@ -7113,7 +7114,7 @@ static void tcpm_pd_event_handler(struct kthread_work *work) + port->upcoming_state = FR_SWAP_SEND; + ret = tcpm_ams_start(port, FAST_ROLE_SWAP); + if (ret == -EAGAIN) +- port->upcoming_state = INVALID_STATE; ++ tcpm_set_state(port, ERROR_RECOVERY, 0); + } else { + tcpm_log(port, "Discarding FRS_SIGNAL! Not in sink ready"); + } +@@ -8977,6 +8978,7 @@ void tcpm_unregister_port(struct tcpm_port *port) + + port->registered = false; + kthread_destroy_worker(port->wq); ++ port->wq = NULL; + + hrtimer_cancel(&port->vdm_discovery_timer); + hrtimer_cancel(&port->enable_frs_timer); +diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c +--- a/drivers/usb/typec/tipd/core.c ++++ b/drivers/usb/typec/tipd/core.c +@@ -343,7 +343,7 @@ static void tps6598x_set_data_role(struct tps6598x *tps, + + static int tps6598x_connect(struct tps6598x *tps, u32 status) + { +- struct typec_partner_desc desc; ++ struct typec_partner_desc desc = { }; + enum typec_pwr_opmode mode; + int ret; + +@@ -354,7 +354,6 @@ static int tps6598x_connect(struct tps6598x *tps, u32 status) + + desc.usb_pd = mode == TYPEC_PWR_MODE_PD; + desc.accessory = TYPEC_ACCESSORY_NONE; /* XXX: handle accessories */ +- desc.identity = NULL; + + if (desc.usb_pd) { + ret = tps6598x_read_partner_identity(tps); +@@ -850,11 +849,10 @@ static void cd321x_update_work(struct work_struct *work) + + /* Set up partner if we were previously disconnected (or changed). */ + if (!tps->partner) { +- struct typec_partner_desc desc; ++ struct typec_partner_desc desc = { }; + + desc.usb_pd = is_pd; + desc.accessory = TYPEC_ACCESSORY_NONE; /* XXX: handle accessories */ +- desc.identity = NULL; + + if (desc.usb_pd) + desc.identity = &st.partner_identity; +@@ -1792,6 +1790,7 @@ static int tps6598x_probe(struct i2c_client *client) + const struct tipd_data *data; + struct tps6598x *tps; + struct fwnode_handle *fwnode; ++ bool patch_loaded = false; + u32 status; + u32 vid = 0; + int ret; +@@ -1847,6 +1846,7 @@ static int tps6598x_probe(struct i2c_client *client) + return ret; + + if (ret == TPS_MODE_PTCH) { ++ patch_loaded = true; + ret = tps->data->init(tps); + if (ret) + return ret; +@@ -1937,7 +1937,8 @@ static int tps6598x_probe(struct i2c_client *client) + tps6598x_write64(tps, TPS_REG_INT_MASK1, 0); + err_reset_controller: + /* Reset PD controller to remove any applied patch */ +- tps->data->reset(tps); ++ if (patch_loaded) ++ tps->data->reset(tps); + + return ret; + } +diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c +--- a/drivers/usb/typec/ucsi/displayport.c ++++ b/drivers/usb/typec/ucsi/displayport.c +@@ -71,11 +71,14 @@ static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo) + if (ret < 0) { + if (ucsi->version > 0x0100) + goto err_unlock; +- cur = 0xff; + } + +- if (cur < UCSI_MAX_ALTMODES) { +- ret = dp->con->port_altmode[cur] == alt ? 0 : -EBUSY; ++ if (cur != 0xff) { ++ if (cur < UCSI_MAX_ALTMODES) ++ ret = dp->con->port_altmode[cur] == alt ? 0 : -EBUSY; ++ else ++ ret = -EINVAL; ++ + goto err_unlock; + } + +diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c +--- a/drivers/usb/typec/ucsi/ucsi.c ++++ b/drivers/usb/typec/ucsi/ucsi.c +@@ -1801,10 +1801,21 @@ static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con) + { + struct fwnode_handle *fwnode; + int i = 1; ++ int ret; ++ u32 port; + +- device_for_each_child_node(con->ucsi->dev, fwnode) +- if (i++ == con->num) +- return fwnode; ++ device_for_each_child_node(con->ucsi->dev, fwnode) { ++ ret = fwnode_property_read_u32(fwnode, "reg", &port); ++ if (ret < 0) { ++ if (i == con->num) ++ return fwnode; ++ } else { ++ if (port == con->num - 1) ++ return fwnode; ++ } ++ ++ i++; ++ } + return NULL; + } + +diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c +--- a/drivers/usb/typec/ucsi/ucsi_acpi.c ++++ b/drivers/usb/typec/ucsi/ucsi_acpi.c +@@ -121,6 +121,33 @@ static const struct ucsi_operations ucsi_acpi_ops = { + .async_control = ucsi_acpi_async_control + }; + ++static int ucsi_acer_read_version(struct ucsi *ucsi, u16 *version) ++{ ++ struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); ++ int ret; ++ ++ ret = ucsi_acpi_read_version(ucsi, version); ++ if (ret) ++ return ret; ++ ++ if (!*version) { ++ dev_warn(ua->dev, "UCSI version is zero, assuming 1.2\n"); ++ *version = UCSI_VERSION_1_2; ++ } ++ ++ return 0; ++} ++ ++static const struct ucsi_operations ucsi_acer_ops = { ++ .read_version = ucsi_acer_read_version, ++ .read_cci = ucsi_acpi_read_cci, ++ .poll_cci = ucsi_acpi_poll_cci, ++ .read_message_in = ucsi_acpi_read_message_in, ++ .write_message_out = ucsi_acpi_write_message_out, ++ .sync_control = ucsi_sync_control_common, ++ .async_control = ucsi_acpi_async_control ++}; ++ + static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command, u32 *cci, + void *val, size_t len, void *msg_out, + size_t msg_out_size) +@@ -162,6 +189,13 @@ static const struct ucsi_operations ucsi_gram_ops = { + }; + + static const struct dmi_system_id ucsi_acpi_quirks[] = { ++ { ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Nitro ANV15-41"), ++ }, ++ .driver_data = (void *)&ucsi_acer_ops, ++ }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"), +diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c +--- a/drivers/usb/typec/ucsi/ucsi_glink.c ++++ b/drivers/usb/typec/ucsi/ucsi_glink.c +@@ -362,6 +362,10 @@ static void pmic_glink_ucsi_destroy(void *data) + { + struct pmic_glink_ucsi *ucsi = data; + ++ /* Drain the work items before ucsi_destroy() frees the ucsi instance */ ++ cancel_work_sync(&ucsi->notify_work); ++ cancel_work_sync(&ucsi->register_work); ++ + /* Protect to make sure we're not in a middle of a transaction from a glink callback */ + mutex_lock(&ucsi->lock); + ucsi_destroy(ucsi->ucsi); +@@ -467,8 +471,15 @@ static void pmic_glink_ucsi_remove(struct auxiliary_device *adev) + { + struct pmic_glink_ucsi *ucsi = dev_get_drvdata(&adev->dev); + +- /* Unregister first to stop having read & writes */ +- ucsi_unregister(ucsi->ucsi); ++ /* Callbacks can queue work until devres releases the client */ ++ disable_work_sync(&ucsi->notify_work); ++ disable_work_sync(&ucsi->register_work); ++ ++ /* register_work may have unregistered the instance already */ ++ if (ucsi->ucsi_registered) { ++ ucsi->ucsi_registered = false; ++ ucsi_unregister(ucsi->ucsi); ++ } + } + + static const struct auxiliary_device_id pmic_glink_ucsi_id_table[] = { +diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h +--- a/include/linux/usb/serial.h ++++ b/include/linux/usb/serial.h +@@ -381,6 +381,7 @@ void usb_serial_handle_dcd_change(struct usb_serial_port *usb_port, + + int usb_serial_bus_register(struct usb_serial_driver *device); + void usb_serial_bus_deregister(struct usb_serial_driver *device); ++void usb_serial_bus_remove_new_id(struct usb_serial_driver *driver); + + extern const struct bus_type usb_serial_bus_type; + extern struct tty_driver *usb_serial_tty_driver; diff --git a/pkgbuilds/linux-omarchy-bore/0607-usb-fixes.patch.sig b/pkgbuilds/linux-omarchy-bore/0607-usb-fixes.patch.sig new file mode 100644 index 0000000..182c484 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0607-usb-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0610-typec-cable-altmode-check.patch b/pkgbuilds/linux-omarchy-bore/0610-typec-cable-altmode-check.patch deleted file mode 100644 index 3db7549..0000000 --- a/pkgbuilds/linux-omarchy-bore/0610-typec-cable-altmode-check.patch +++ /dev/null @@ -1,132 +0,0 @@ -diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c -index 263a89c5f324..5ee33a69b9cf 100644 ---- a/drivers/usb/typec/altmodes/displayport.c -+++ b/drivers/usb/typec/altmodes/displayport.c -@@ -790,7 +790,6 @@ int dp_altmode_probe(struct typec_altmode *alt) - dp->alt = alt; - - alt->desc = "DisplayPort"; -- typec_altmode_set_ops(alt, &dp_altmode_ops); - - if (plug) { - plug->desc = "Displayport"; -@@ -811,6 +810,10 @@ int dp_altmode_probe(struct typec_altmode *alt) - if (plug) - typec_altmode_set_drvdata(plug, dp); - -+ if ((alt->vdo & DP_CAP_RECEPTACLE) && typec_cable_altmode_unsupported(alt)) -+ return 0; -+ -+ typec_altmode_set_ops(alt, &dp_altmode_ops); - if (!alt->mode_selection) { - dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER; - schedule_work(&dp->work); -diff --git a/drivers/usb/typec/altmodes/thunderbolt.c b/drivers/usb/typec/altmodes/thunderbolt.c -index 32250b94262a..2eccdddf1b1f 100644 ---- a/drivers/usb/typec/altmodes/thunderbolt.c -+++ b/drivers/usb/typec/altmodes/thunderbolt.c -@@ -284,6 +284,10 @@ static int tbt_altmode_probe(struct typec_altmode *alt) - - alt->desc = "Thunderbolt3"; - typec_altmode_set_drvdata(alt, tbt); -+ -+ if (typec_cable_altmode_unsupported(alt)) -+ return 0; -+ - typec_altmode_set_ops(alt, &tbt_altmode_ops); - - if (!alt->mode_selection && tbt_ready(alt)) { -diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c -index 0595e8cb83aa..54c7810b563b 100644 ---- a/drivers/usb/typec/class.c -+++ b/drivers/usb/typec/class.c -@@ -1429,6 +1429,77 @@ int typec_cable_is_active(struct typec_cable *cable) - } - EXPORT_SYMBOL_GPL(typec_cable_is_active); - -+enum typec_cable_altmode_support { -+ CABLE_SUPPORT_UNKNOWN, -+ CABLE_SUPPORTED, -+ CABLE_NOT_SUPPORTED, -+}; -+ -+static enum typec_cable_altmode_support -+typec_cable_check_altmode_support(struct typec_cable *cable, -+ struct typec_altmode *alt) -+{ -+ struct typec_altmode *plug; -+ u32 speed; -+ -+ /* -+ * Check if the cable has an e-marker, supports modal operation, and the -+ * SOP' altmode nodes are created. -+ */ -+ plug = typec_altmode_get_plug(alt, TYPEC_PLUG_SOP_P); -+ if (plug) { -+ typec_altmode_put_plug(plug); -+ return CABLE_SUPPORTED; -+ } -+ -+ /* The identity is not specified */ -+ if (!cable->identity) -+ return CABLE_SUPPORT_UNKNOWN; -+ -+ /* Non-e-marked cable */ -+ if (!cable->identity->id_header) -+ return CABLE_NOT_SUPPORTED; -+ -+ switch (PD_IDH_PTYPE(cable->identity->id_header)) { -+ case IDH_PTYPE_PCABLE: -+ speed = VDO_TYPEC_CABLE_SPEED(cable->identity->vdo[0]); -+ if (speed == CABLE_USB2_ONLY) -+ return CABLE_NOT_SUPPORTED; -+ return CABLE_SUPPORTED; -+ case IDH_PTYPE_ACABLE: -+ /* -+ * Active cables must establish an SOP' communication -+ * node. Since that check failed at the beginning of -+ * this function, this active cable does not support -+ * this specific altmode. -+ */ -+ return CABLE_NOT_SUPPORTED; -+ } -+ -+ return CABLE_SUPPORT_UNKNOWN; -+} -+ -+/** -+ * typec_cable_altmode_unsupported - Check if a cable restricts altmode -+ * @alt: The Alternate Mode to evaluate -+ * -+ * Returns true if the connected cable is incapable of handling the altmode. -+ */ -+bool typec_cable_altmode_unsupported(struct typec_altmode *alt) -+{ -+ enum typec_cable_altmode_support support = CABLE_SUPPORT_UNKNOWN; -+ struct typec_cable *cable; -+ -+ cable = typec_cable_get(typec_altmode2port(alt)); -+ if (cable) { -+ support = typec_cable_check_altmode_support(cable, alt); -+ typec_cable_put(cable); -+ } -+ -+ return support == CABLE_NOT_SUPPORTED; -+} -+EXPORT_SYMBOL_GPL(typec_cable_altmode_unsupported); -+ - /** - * typec_cable_set_identity - Report result from Discover Identity command - * @cable: The cable updated identity values -diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h -index d61ec38216fa..10a783b738ef 100644 ---- a/include/linux/usb/typec.h -+++ b/include/linux/usb/typec.h -@@ -337,6 +337,7 @@ void typec_unregister_cable(struct typec_cable *cable); - struct typec_cable *typec_cable_get(struct typec_port *port); - void typec_cable_put(struct typec_cable *cable); - int typec_cable_is_active(struct typec_cable *cable); -+bool typec_cable_altmode_unsupported(struct typec_altmode *alt); - - struct typec_plug *typec_register_plug(struct typec_cable *cable, - struct typec_plug_desc *desc); diff --git a/pkgbuilds/linux-omarchy-bore/0610-typec-cable-altmode-check.patch.sig b/pkgbuilds/linux-omarchy-bore/0610-typec-cable-altmode-check.patch.sig deleted file mode 100644 index fd0bd60..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/0610-typec-cable-altmode-check.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/0660-btusb-mediatek-mt7922-13d3-3625.patch b/pkgbuilds/linux-omarchy-bore/0660-btusb-mediatek-mt7922-13d3-3625.patch index 8789452..3b51649 100644 --- a/pkgbuilds/linux-omarchy-bore/0660-btusb-mediatek-mt7922-13d3-3625.patch +++ b/pkgbuilds/linux-omarchy-bore/0660-btusb-mediatek-mt7922-13d3-3625.patch @@ -1,7 +1,7 @@ diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c -@@ -801,6 +801,8 @@ static const struct usb_device_id quirks_table[] = { +@@ -819,6 +819,8 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3613), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH }, diff --git a/pkgbuilds/linux-omarchy-bore/0660-btusb-mediatek-mt7922-13d3-3625.patch.sig b/pkgbuilds/linux-omarchy-bore/0660-btusb-mediatek-mt7922-13d3-3625.patch.sig index ceec933..61318b8 100644 Binary files a/pkgbuilds/linux-omarchy-bore/0660-btusb-mediatek-mt7922-13d3-3625.patch.sig and b/pkgbuilds/linux-omarchy-bore/0660-btusb-mediatek-mt7922-13d3-3625.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0661-rtw89-command-offload-source.patch b/pkgbuilds/linux-omarchy-bore/0661-rtw89-command-offload-source.patch deleted file mode 100644 index 539fe9a..0000000 --- a/pkgbuilds/linux-omarchy-bore/0661-rtw89-command-offload-source.patch +++ /dev/null @@ -1,32 +0,0 @@ -diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c ---- a/drivers/net/wireless/realtek/rtw89/fw.c -+++ b/drivers/net/wireless/realtek/rtw89/fw.c -@@ -11780,7 +11780,7 @@ static void rtw89_fw_cmd_ofld_write_rf(struct rtw89_dev *rtwdev, - static void rtw89_fw_cmd_ofld_udelay(struct rtw89_dev *rtwdev, u32 us) - { - struct rtw89_fw_cmd_ofld_arg cmd = { -- .src = RTW89_FW_CMD_OFLD_SRC_OTHER, -+ .src = RTW89_FW_CMD_OFLD_SRC_BB, - .type = RTW89_FW_CMD_OFLD_DELAY, - .value = us, - }; -@@ -11794,7 +11794,7 @@ static void rtw89_fw_cmd_ofld_udelay(struct rtw89_dev *rtwdev, u32 us) - static void rtw89_fw_cmd_ofld_mdelay(struct rtw89_dev *rtwdev, u32 ms) - { - struct rtw89_fw_cmd_ofld_arg cmd = { -- .src = RTW89_FW_CMD_OFLD_SRC_OTHER, -+ .src = RTW89_FW_CMD_OFLD_SRC_BB, - .type = RTW89_FW_CMD_OFLD_DELAY, - .value = ms * 1000, - }; -diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h ---- a/drivers/net/wireless/realtek/rtw89/fw.h -+++ b/drivers/net/wireless/realtek/rtw89/fw.h -@@ -3142,7 +3142,6 @@ enum rtw89_fw_cmd_ofld_arg_src { - RTW89_FW_CMD_OFLD_SRC_RF, - RTW89_FW_CMD_OFLD_SRC_MAC, - RTW89_FW_CMD_OFLD_SRC_RF_DDIE, -- RTW89_FW_CMD_OFLD_SRC_OTHER, - }; - - enum rtw89_fw_cmd_ofld_arg_type { diff --git a/pkgbuilds/linux-omarchy-bore/0661-rtw89-command-offload-source.patch.sig b/pkgbuilds/linux-omarchy-bore/0661-rtw89-command-offload-source.patch.sig deleted file mode 100644 index a9714f1..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/0661-rtw89-command-offload-source.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/0663-btintel-pcie-serialize-reset-type.patch b/pkgbuilds/linux-omarchy-bore/0663-btintel-pcie-serialize-reset-type.patch new file mode 100644 index 0000000..2464f58 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0663-btintel-pcie-serialize-reset-type.patch @@ -0,0 +1,145 @@ +diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c +--- a/drivers/bluetooth/btintel_pcie.c ++++ b/drivers/bluetooth/btintel_pcie.c +@@ -2499,8 +2499,6 @@ static void btintel_pcie_inc_recovery_count(struct pci_dev *pdev, + } + } + +-static void btintel_pcie_reset(struct hci_dev *hdev); +- + static int btintel_pcie_acpi_reset_method(struct btintel_pcie_data *data) + { + union acpi_object *obj, argv4; +@@ -2693,56 +2691,86 @@ static void btintel_pcie_reset_work(struct work_struct *wk) + pci_unlock_rescan_remove(); + } + +-static void btintel_pcie_reset(struct hci_dev *hdev) ++/* Schedule a device reset of the requested type. ++ * ++ * BTINTEL_PCIE_RECOVERY_IN_PROGRESS serializes all reset requesters ++ * (sysfs reset attribute, hci_cmd_timeout(), hw_error, resume error ++ * path, etc.) so that: ++ * ++ * - dev_data->reset_type is written by exactly one caller (the ++ * thread that wins test_and_set_bit), eliminating the race where ++ * a second hw_error could clobber an already-scheduled reset's ++ * type; ++ * - the write happens AFTER the bit is set, so reset_work observes ++ * it through schedule_work()'s memory ordering; ++ * - losers return without touching reset_type or scheduling the ++ * work, so concurrent triggers are silently coalesced into the ++ * in-flight one (whose recovery will reinitialize the device ++ * regardless of the dropped trigger's variant). ++ * ++ * The bit is cleared only by .remove() / re-probe via fresh devm ++ * allocation, which is the intended one-shot semantics: a reset ++ * tears down and re-probes 'data', so there is no "in-flight" ++ * reset to follow up after device_reprobe() succeeds. ++ */ ++static void btintel_pcie_request_reset(struct btintel_pcie_data *data, ++ enum btintel_pcie_reset_type type) + { +- struct btintel_pcie_data *data; +- +- data = hci_get_drvdata(hdev); +- + if (!test_bit(BTINTEL_PCIE_SETUP_DONE, &data->flags)) + return; + + if (test_and_set_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags)) + return; + ++ data->reset_type = type; ++ + pci_dev_get(data->pdev); + schedule_work(&data->reset_work); + } + ++static void btintel_pcie_hci_reset(struct hci_dev *hdev) ++{ ++ struct btintel_pcie_data *data = hci_get_drvdata(hdev); ++ ++ btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR); ++} ++ + static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code) + { +- struct btintel_pcie_dev_recovery *data; ++ struct btintel_pcie_dev_recovery *rec; + struct btintel_pcie_data *dev_data = hci_get_drvdata(hdev); + struct pci_dev *pdev = dev_data->pdev; ++ enum btintel_pcie_reset_type type; + time64_t retry_window; + ++ if (test_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &dev_data->flags)) ++ return; ++ + btintel_pcie_dump_debug_registers(hdev); + +- data = btintel_pcie_get_recovery(pdev, &hdev->dev); +- if (!data) ++ rec = btintel_pcie_get_recovery(pdev, &hdev->dev); ++ if (!rec) + return; + +- if (code == 0x13) +- dev_data->reset_type = BTINTEL_PCIE_IOSF_PRR_PLDR; +- else +- dev_data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR; ++ type = (code == 0x13) ? BTINTEL_PCIE_IOSF_PRR_PLDR ++ : BTINTEL_PCIE_IOSF_PRR_FLR; + + bt_dev_err(hdev, "Encountered exception err:0x%x triggering: %s", code, +- dev_data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR ? "PLDR" : "FLR"); +- retry_window = ktime_get_boottime_seconds() - data->last_error; ++ type == BTINTEL_PCIE_IOSF_PRR_PLDR ? "PLDR" : "FLR"); ++ retry_window = ktime_get_boottime_seconds() - rec->last_error; + + if (retry_window < BTINTEL_PCIE_RESET_WINDOW_SECS && +- data->count >= BTINTEL_PCIE_FLR_MAX_RETRY) { ++ rec->count >= BTINTEL_PCIE_FLR_MAX_RETRY) { + bt_dev_err(hdev, "Exhausted maximum: %d recovery attempts: %d", +- BTINTEL_PCIE_FLR_MAX_RETRY, data->count); ++ BTINTEL_PCIE_FLR_MAX_RETRY, rec->count); + bt_dev_dbg(hdev, "Boot time: %lld seconds", + ktime_get_boottime_seconds()); + bt_dev_dbg(hdev, "last error at: %lld seconds", +- data->last_error); ++ rec->last_error); + return; + } + btintel_pcie_inc_recovery_count(pdev, &hdev->dev); +- btintel_pcie_reset(hdev); ++ btintel_pcie_request_reset(dev_data, type); + } + + static bool btintel_pcie_wakeup(struct hci_dev *hdev) +@@ -2832,7 +2860,7 @@ static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data) + hdev->hw_error = btintel_pcie_hw_error; + hdev->set_diag = btintel_set_diag; + hdev->set_bdaddr = btintel_set_bdaddr; +- hdev->reset = btintel_pcie_reset; ++ hdev->reset = btintel_pcie_hci_reset; + hdev->wakeup = btintel_pcie_wakeup; + hdev->hci_drv = &btintel_pcie_hci_drv; + +@@ -3124,8 +3152,7 @@ static int btintel_pcie_resume(struct device *dev) + if (data->pm_sx_event == PM_EVENT_FREEZE || + data->pm_sx_event == PM_EVENT_HIBERNATE) { + set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags); +- data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR; +- btintel_pcie_reset(data->hdev); ++ btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR); + return 0; + } + +@@ -3156,7 +3183,7 @@ static int btintel_pcie_resume(struct device *dev) + queue_work(data->coredump_workqueue, &data->coredump_work); + } + set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags); +- btintel_pcie_reset(data->hdev); ++ btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR); + } + return err; + } diff --git a/pkgbuilds/linux-omarchy-bore/0663-btintel-pcie-serialize-reset-type.patch.sig b/pkgbuilds/linux-omarchy-bore/0663-btintel-pcie-serialize-reset-type.patch.sig new file mode 100644 index 0000000..6469de5 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0663-btintel-pcie-serialize-reset-type.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0664-btintel-pcie-stale-cache-in-dxstate-fallback.patch b/pkgbuilds/linux-omarchy-bore/0664-btintel-pcie-stale-cache-in-dxstate-fallback.patch new file mode 100644 index 0000000..54219c0 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0664-btintel-pcie-stale-cache-in-dxstate-fallback.patch @@ -0,0 +1,30 @@ +diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c +--- a/drivers/bluetooth/btintel_pcie.c ++++ b/drivers/bluetooth/btintel_pcie.c +@@ -3069,10 +3069,12 @@ static int btintel_pcie_set_dxstate(struct btintel_pcie_data *data, u32 dxstate) + BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES, + BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0); + +- /* A hardware bug may cause the alive interrupt to be missed. +- * Check if the controller reached the expected state and retry +- * the operation only if it hasn't. ++ /* A hardware bug may cause the alive interrupt to be missed. Refresh ++ * boot_stage_cache from hardware, since only the interrupt handler ++ * updates it. Finally retry only if the state check still fails. + */ ++ data->boot_stage_cache = btintel_pcie_rd_reg32(data, ++ BTINTEL_PCIE_CSR_BOOT_STAGE_REG); + if (dxstate == BTINTEL_PCIE_STATE_D0) { + if (btintel_pcie_in_d0(data)) + return 0; +diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h +--- a/drivers/bluetooth/btintel_pcie.h ++++ b/drivers/bluetooth/btintel_pcie.h +@@ -50,6 +50,7 @@ + #define BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_HALTED (BIT(14)) + #define BTINTEL_PCIE_CSR_BOOT_STAGE_MAC_ACCESS_ON (BIT(16)) + #define BTINTEL_PCIE_CSR_BOOT_STAGE_ALIVE (BIT(23)) ++/* Reflects live D-state. Updated by hardware on every D-state transition. */ + #define BTINTEL_PCIE_CSR_BOOT_STAGE_D3_STATE_READY (BIT(24)) + + /* Registers for MSI-X */ diff --git a/pkgbuilds/linux-omarchy-bore/0664-btintel-pcie-stale-cache-in-dxstate-fallback.patch.sig b/pkgbuilds/linux-omarchy-bore/0664-btintel-pcie-stale-cache-in-dxstate-fallback.patch.sig new file mode 100644 index 0000000..216c5d1 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0664-btintel-pcie-stale-cache-in-dxstate-fallback.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0665-btintel-pcie-pm-flow-s0ix-s3-s4.patch b/pkgbuilds/linux-omarchy-bore/0665-btintel-pcie-pm-flow-s0ix-s3-s4.patch new file mode 100644 index 0000000..2ec0e80 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0665-btintel-pcie-pm-flow-s0ix-s3-s4.patch @@ -0,0 +1,145 @@ +diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c +--- a/drivers/bluetooth/btintel_pcie.c ++++ b/drivers/bluetooth/btintel_pcie.c +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -3046,7 +3047,8 @@ static void btintel_pcie_coredump(struct device *dev) + + static int btintel_pcie_set_dxstate(struct btintel_pcie_data *data, u32 dxstate) + { +- int retry = 0, status; ++ int retry = 0; ++ long status; + u32 dx_intr_timeout_ms = 200; + + do { +@@ -3098,18 +3100,23 @@ static int btintel_pcie_suspend_late(struct device *dev, pm_message_t mesg) + + data = pci_get_drvdata(pdev); + +- dxstate = (mesg.event == PM_EVENT_SUSPEND ? +- BTINTEL_PCIE_STATE_D3_HOT : BTINTEL_PCIE_STATE_D3_COLD); +- +- data->pm_sx_event = mesg.event; ++ /* S0ix (s2idle) uses D3_HOT; S3, freeze and hibernate use D3_COLD. */ ++ if (mesg.event == PM_EVENT_SUSPEND && ++ pm_suspend_target_state == PM_SUSPEND_TO_IDLE) ++ dxstate = BTINTEL_PCIE_STATE_D3_HOT; ++ else ++ dxstate = BTINTEL_PCIE_STATE_D3_COLD; + + start = ktime_get(); + + /* Refer: 6.4.11.7 -> Platform power management */ + err = btintel_pcie_set_dxstate(data, dxstate); + +- if (err) ++ if (err) { ++ bt_dev_err(data->hdev, "Failed to set dxstate:%u (%d)", ++ dxstate, err); + return err; ++ } + + bt_dev_dbg(data->hdev, + "device entered into d3 state from d0 in %lld us", +@@ -3132,7 +3139,7 @@ static int btintel_pcie_freeze(struct device *dev) + return btintel_pcie_suspend_late(dev, PMSG_FREEZE); + } + +-static int btintel_pcie_resume(struct device *dev) ++static int btintel_pcie_resume_event(struct device *dev, pm_message_t mesg) + { + struct pci_dev *pdev = to_pci_dev(dev); + struct btintel_pcie_data *data; +@@ -3140,19 +3147,15 @@ static int btintel_pcie_resume(struct device *dev) + int err; + + data = pci_get_drvdata(pdev); +- data->gp0_received = false; + + start = ktime_get(); + +- /* When the system enters S4 (hibernate) mode, bluetooth device loses +- * power, which results in the erasure of its loaded firmware. +- * Consequently, function level reset (flr) is required on system +- * resume to bring the controller back into an operational state by +- * initiating a new firmware download. ++ /* S3 and S4 may cut power, erasing the firmware. Force FLR to recover ++ * instead of a normal D0 transition. + */ +- +- if (data->pm_sx_event == PM_EVENT_FREEZE || +- data->pm_sx_event == PM_EVENT_HIBERNATE) { ++ if (mesg.event == PM_EVENT_RESTORE || ++ (mesg.event == PM_EVENT_RESUME && ++ pm_suspend_target_state == PM_SUSPEND_MEM)) { + set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags); + btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR); + return 0; +@@ -3161,7 +3164,9 @@ static int btintel_pcie_resume(struct device *dev) + /* Refer: 6.4.11.7 -> Platform power management */ + err = btintel_pcie_set_dxstate(data, BTINTEL_PCIE_STATE_D0); + +- if (err == 0) { ++ if (err) { ++ bt_dev_err(data->hdev, "Failed to set D0 state (%d)", err); ++ } else { + bt_dev_dbg(data->hdev, + "device entered into d0 state from d3 in %lld us", + ktime_to_us(ktime_get() - start)); +@@ -3190,13 +3195,28 @@ static int btintel_pcie_resume(struct device *dev) + return err; + } + ++static int btintel_pcie_resume(struct device *dev) ++{ ++ return btintel_pcie_resume_event(dev, PMSG_RESUME); ++} ++ ++static int btintel_pcie_restore(struct device *dev) ++{ ++ return btintel_pcie_resume_event(dev, PMSG_RESTORE); ++} ++ ++static int btintel_pcie_thaw(struct device *dev) ++{ ++ return btintel_pcie_resume_event(dev, PMSG_THAW); ++} ++ + static const struct dev_pm_ops btintel_pcie_pm_ops = { + .suspend = btintel_pcie_suspend, + .resume = btintel_pcie_resume, + .freeze = btintel_pcie_freeze, +- .thaw = btintel_pcie_resume, ++ .thaw = btintel_pcie_thaw, + .poweroff = btintel_pcie_hibernate, +- .restore = btintel_pcie_resume, ++ .restore = btintel_pcie_restore, + }; + + static struct pci_driver btintel_pcie_driver = { +diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h +--- a/drivers/bluetooth/btintel_pcie.h ++++ b/drivers/bluetooth/btintel_pcie.h +@@ -478,7 +478,6 @@ struct btintel_pcie_dump_header { + * @txq: TX Queue struct + * @rxq: RX Queue struct + * @alive_intr_ctxt: Alive interrupt context +- * @pm_sx_event: PM event on which system got suspended + */ + struct btintel_pcie_data { + struct pci_dev *pdev; +@@ -533,7 +532,6 @@ struct btintel_pcie_data { + enum btintel_pcie_reset_type reset_type; + struct btintel_pcie_dbgc dbgc; + struct btintel_pcie_dump_header dmp_hdr; +- u8 pm_sx_event; + u32 debug_evt_addr; + u32 debug_evt_size; + }; diff --git a/pkgbuilds/linux-omarchy-bore/0665-btintel-pcie-pm-flow-s0ix-s3-s4.patch.sig b/pkgbuilds/linux-omarchy-bore/0665-btintel-pcie-pm-flow-s0ix-s3-s4.patch.sig new file mode 100644 index 0000000..ee77a45 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0665-btintel-pcie-pm-flow-s0ix-s3-s4.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0666-btintel-pcie-verify-serialize-dstate.patch b/pkgbuilds/linux-omarchy-bore/0666-btintel-pcie-verify-serialize-dstate.patch new file mode 100644 index 0000000..b1305d3 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0666-btintel-pcie-verify-serialize-dstate.patch @@ -0,0 +1,113 @@ +diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c +--- a/drivers/bluetooth/btintel_pcie.c ++++ b/drivers/bluetooth/btintel_pcie.c +@@ -1012,6 +1012,10 @@ static void btintel_pcie_msix_gp0_handler(struct btintel_pcie_data *data) + submit_rx = false; + signal_waitq = false; + ++ /* Serialize the alive_intr_ctxt claim against set_dxstate()'s ++ * register-based fallback, which can run concurrently. ++ */ ++ spin_lock(&data->irq_lock); + switch (data->alive_intr_ctxt) { + case BTINTEL_PCIE_ROM: + data->alive_intr_ctxt = BTINTEL_PCIE_FW_DL; +@@ -1067,6 +1071,7 @@ static void btintel_pcie_msix_gp0_handler(struct btintel_pcie_data *data) + data->alive_intr_ctxt); + break; + } ++ spin_unlock(&data->irq_lock); + + if (submit_rx) { + btintel_pcie_reset_ia(data); +@@ -3047,7 +3052,7 @@ static void btintel_pcie_coredump(struct device *dev) + + static int btintel_pcie_set_dxstate(struct btintel_pcie_data *data, u32 dxstate) + { +- int retry = 0; ++ int retry = 0, err; + long status; + u32 dx_intr_timeout_ms = 200; + +@@ -3059,30 +3064,66 @@ static int btintel_pcie_set_dxstate(struct btintel_pcie_data *data, u32 dxstate) + status = wait_event_timeout(data->gp0_wait_q, data->gp0_received, + msecs_to_jiffies(dx_intr_timeout_ms)); + +- if (status) +- return 0; ++ if (!status) { ++ bt_dev_warn(data->hdev, ++ "Timeout (%u ms) on alive interrupt for D%d entry, retry count %d", ++ dx_intr_timeout_ms, dxstate, retry); + +- bt_dev_warn(data->hdev, +- "Timeout (%u ms) on alive interrupt for D%d entry, retry count %d", +- dx_intr_timeout_ms, dxstate, retry); ++ /* clear gp0 cause */ ++ btintel_pcie_clr_reg_bits(data, ++ BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES, ++ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0); ++ } + +- /* clear gp0 cause */ +- btintel_pcie_clr_reg_bits(data, +- BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES, +- BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0); +- +- /* A hardware bug may cause the alive interrupt to be missed. Refresh +- * boot_stage_cache from hardware, since only the interrupt handler +- * updates it. Finally retry only if the state check still fails. ++ /* gp0_received is set at the top of the handler, before the switch on ++ * alive_intr_ctxt. Error and lockdown are filtered out above it, but a ++ * matched case can still complete without doing anything - D3 breaks ++ * unchanged while the controller has not reached D0 - and a hardware ++ * bug may drop the interrupt outright. Either way the flag says a gp0 ++ * was handled, not that the transition completed, and only the register ++ * knows. Refresh the cache here and retry only if the state check still ++ * fails. + */ + data->boot_stage_cache = btintel_pcie_rd_reg32(data, + BTINTEL_PCIE_CSR_BOOT_STAGE_REG); + if (dxstate == BTINTEL_PCIE_STATE_D0) { +- if (btintel_pcie_in_d0(data)) ++ if (btintel_pcie_in_d0(data)) { ++ bool armed; ++ ++ /* Claim the transition under irq_lock so a ++ * concurrent gp0 handler run cannot also arm RX. ++ */ ++ spin_lock(&data->irq_lock); ++ armed = data->alive_intr_ctxt == BTINTEL_PCIE_D0; ++ if (!armed) ++ data->alive_intr_ctxt = BTINTEL_PCIE_D0; ++ spin_unlock(&data->irq_lock); ++ ++ if (armed) ++ return 0; ++ ++ /* Do what the handler's D3 -> D0 branch ++ * would have done, unless it already has. ++ */ ++ btintel_pcie_reset_ia(data); ++ err = btintel_pcie_start_rx(data); ++ if (err) ++ return err; ++ + return 0; ++ } + } else { +- if (btintel_pcie_in_d3(data)) ++ if (btintel_pcie_in_d3(data)) { ++ /* Do what the handler's D0 -> D3 branch ++ * would have done, unless it already has. ++ */ ++ spin_lock(&data->irq_lock); ++ if (data->alive_intr_ctxt != BTINTEL_PCIE_D3) ++ data->alive_intr_ctxt = BTINTEL_PCIE_D3; ++ spin_unlock(&data->irq_lock); ++ + return 0; ++ } + } + + } while (++retry < BTINTEL_PCIE_DX_TRANSITION_MAX_RETRIES); diff --git a/pkgbuilds/linux-omarchy-bore/0666-btintel-pcie-verify-serialize-dstate.patch.sig b/pkgbuilds/linux-omarchy-bore/0666-btintel-pcie-verify-serialize-dstate.patch.sig new file mode 100644 index 0000000..11d5690 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0666-btintel-pcie-verify-serialize-dstate.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0667-btintel-pcie-gp0-cause-w1c-write.patch b/pkgbuilds/linux-omarchy-bore/0667-btintel-pcie-gp0-cause-w1c-write.patch new file mode 100644 index 0000000..9927412 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0667-btintel-pcie-gp0-cause-w1c-write.patch @@ -0,0 +1,20 @@ +diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c +--- a/drivers/bluetooth/btintel_pcie.c ++++ b/drivers/bluetooth/btintel_pcie.c +@@ -3069,10 +3069,12 @@ static int btintel_pcie_set_dxstate(struct btintel_pcie_data *data, u32 dxstate) + "Timeout (%u ms) on alive interrupt for D%d entry, retry count %d", + dx_intr_timeout_ms, dxstate, retry); + +- /* clear gp0 cause */ +- btintel_pcie_clr_reg_bits(data, +- BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES, +- BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0); ++ /* MSIX_HW_INT_CAUSES is W1C. Write only GP0 so other ++ * pending causes are not acknowledged here. ++ */ ++ btintel_pcie_wr_reg32(data, ++ BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES, ++ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0); + } + + /* gp0_received is set at the top of the handler, before the switch on diff --git a/pkgbuilds/linux-omarchy-bore/0667-btintel-pcie-gp0-cause-w1c-write.patch.sig b/pkgbuilds/linux-omarchy-bore/0667-btintel-pcie-gp0-cause-w1c-write.patch.sig new file mode 100644 index 0000000..0b81f4e Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0667-btintel-pcie-gp0-cause-w1c-write.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0668-btintel-pcie-dstate-races.patch b/pkgbuilds/linux-omarchy-bore/0668-btintel-pcie-dstate-races.patch new file mode 100644 index 0000000..d25c602 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0668-btintel-pcie-dstate-races.patch @@ -0,0 +1,86 @@ +diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c +--- a/drivers/bluetooth/btintel_pcie.c ++++ b/drivers/bluetooth/btintel_pcie.c +@@ -1016,6 +1016,8 @@ static void btintel_pcie_msix_gp0_handle + * register-based fallback, which can run concurrently. + */ + spin_lock(&data->irq_lock); ++ data->boot_stage_cache = btintel_pcie_rd_reg32(data, ++ BTINTEL_PCIE_CSR_BOOT_STAGE_REG); + switch (data->alive_intr_ctxt) { + case BTINTEL_PCIE_ROM: + data->alive_intr_ctxt = BTINTEL_PCIE_FW_DL; +@@ -1071,12 +1073,12 @@ static void btintel_pcie_msix_gp0_handle + data->alive_intr_ctxt); + break; + } +- spin_unlock(&data->irq_lock); + + if (submit_rx) { + btintel_pcie_reset_ia(data); + btintel_pcie_start_rx(data); + } ++ spin_unlock(&data->irq_lock); + + if (signal_waitq) { + bt_dev_dbg(data->hdev, "wake up gp0 wait_q"); +@@ -3086,40 +3088,34 @@ static int btintel_pcie_set_dxstate(stru + * knows. Refresh the cache here and retry only if the state check still + * fails. + */ ++ spin_lock(&data->irq_lock); + data->boot_stage_cache = btintel_pcie_rd_reg32(data, + BTINTEL_PCIE_CSR_BOOT_STAGE_REG); + if (dxstate == BTINTEL_PCIE_STATE_D0) { + if (btintel_pcie_in_d0(data)) { +- bool armed; ++ err = 0; + + /* Claim the transition under irq_lock so a + * concurrent gp0 handler run cannot also arm RX. + */ +- spin_lock(&data->irq_lock); +- armed = data->alive_intr_ctxt == BTINTEL_PCIE_D0; +- if (!armed) ++ if (data->alive_intr_ctxt != BTINTEL_PCIE_D0) { + data->alive_intr_ctxt = BTINTEL_PCIE_D0; +- spin_unlock(&data->irq_lock); + +- if (armed) +- return 0; +- +- /* Do what the handler's D3 -> D0 branch +- * would have done, unless it already has. +- */ +- btintel_pcie_reset_ia(data); +- err = btintel_pcie_start_rx(data); +- if (err) +- return err; ++ /* Do what the handler's D3 -> D0 branch ++ * would have done, unless it already has. ++ */ ++ btintel_pcie_reset_ia(data); ++ err = btintel_pcie_start_rx(data); ++ } ++ spin_unlock(&data->irq_lock); + +- return 0; ++ return err; + } + } else { + if (btintel_pcie_in_d3(data)) { + /* Do what the handler's D0 -> D3 branch + * would have done, unless it already has. + */ +- spin_lock(&data->irq_lock); + if (data->alive_intr_ctxt != BTINTEL_PCIE_D3) + data->alive_intr_ctxt = BTINTEL_PCIE_D3; + spin_unlock(&data->irq_lock); +@@ -3127,6 +3123,7 @@ static int btintel_pcie_set_dxstate(stru + return 0; + } + } ++ spin_unlock(&data->irq_lock); + + } while (++retry < BTINTEL_PCIE_DX_TRANSITION_MAX_RETRIES); + diff --git a/pkgbuilds/linux-omarchy-bore/0668-btintel-pcie-dstate-races.patch.sig b/pkgbuilds/linux-omarchy-bore/0668-btintel-pcie-dstate-races.patch.sig new file mode 100644 index 0000000..f910b39 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0668-btintel-pcie-dstate-races.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0669-btintel-version-tlv-parse-errors.patch b/pkgbuilds/linux-omarchy-bore/0669-btintel-version-tlv-parse-errors.patch new file mode 100644 index 0000000..164dbb6 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0669-btintel-version-tlv-parse-errors.patch @@ -0,0 +1,24 @@ +diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c +--- a/drivers/bluetooth/btintel.c ++++ b/drivers/bluetooth/btintel.c +@@ -720,6 +720,7 @@ static int btintel_read_version_tlv(struct hci_dev *hdev, + { + struct sk_buff *skb; + const u8 param[1] = { 0xFF }; ++ int err; + + if (!version) + return -EINVAL; +@@ -738,10 +739,10 @@ static int btintel_read_version_tlv(struct hci_dev *hdev, + return -EIO; + } + +- btintel_parse_version_tlv(hdev, version, skb); ++ err = btintel_parse_version_tlv(hdev, version, skb); + + kfree_skb(skb); +- return 0; ++ return err; + } + + /* ------- REGMAP IBT SUPPORT ------- */ diff --git a/pkgbuilds/linux-omarchy-bore/0669-btintel-version-tlv-parse-errors.patch.sig b/pkgbuilds/linux-omarchy-bore/0669-btintel-version-tlv-parse-errors.patch.sig new file mode 100644 index 0000000..b8e7e45 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0669-btintel-version-tlv-parse-errors.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0670-bluetooth-fixes.patch b/pkgbuilds/linux-omarchy-bore/0670-bluetooth-fixes.patch new file mode 100644 index 0000000..14623cd --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0670-bluetooth-fixes.patch @@ -0,0 +1,485 @@ +diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c +--- a/drivers/bluetooth/btintel.c ++++ b/drivers/bluetooth/btintel.c +@@ -288,7 +288,8 @@ void btintel_hw_error(struct hci_dev *hdev, u8 code) + goto unlock; + } + +- bt_dev_err(hdev, "Exception info %s", (char *)(skb->data + 1)); ++ bt_dev_err(hdev, "Exception info %.*s", (int)(skb->len - 1), ++ (char *)(skb->data + 1)); + + kfree_skb(skb); + +@@ -407,8 +408,16 @@ int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name) + /* DDC file contains one or more DDC structure which has + * Length (1 byte), DDC ID (2 bytes), and DDC value (Length - 2). + */ +- while (fw->size > fw_ptr - fw->data) { +- u8 cmd_plen = fw_ptr[0] + sizeof(u8); ++ while (fw->size > (size_t)(fw_ptr - fw->data)) { ++ size_t remaining = fw->size - (fw_ptr - fw->data); ++ unsigned int cmd_plen = fw_ptr[0] + 1U; ++ ++ if (cmd_plen < 3 || cmd_plen > U8_MAX || cmd_plen > remaining) { ++ bt_dev_err(hdev, "Malformed DDC record (plen=%u, remaining=%zu)", ++ cmd_plen, remaining); ++ release_firmware(fw); ++ return -EINVAL; ++ } + + skb = __hci_cmd_sync(hdev, 0xfc8b, cmd_plen, fw_ptr, + HCI_INIT_TIMEOUT); +diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c +--- a/drivers/bluetooth/btintel_pcie.c ++++ b/drivers/bluetooth/btintel_pcie.c +@@ -1200,7 +1200,7 @@ static int btintel_pcie_recv_frame(struct btintel_pcie_data *data, + { + int ret; + u8 pkt_type; +- u16 plen; ++ u32 plen; + u32 pcie_pkt_type; + void *pdata; + struct hci_dev *hdev = data->hdev; +@@ -2149,6 +2149,16 @@ static int btintel_pcie_send_frame(struct hci_dev *hdev, + if (test_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags)) + return -ENODEV; + ++ /* Account for the 4-byte PCIe type header prepended before the ++ * DMA copy. Written as a subtraction to avoid wrap-around on ++ * attacker-controlled skb->len. ++ */ ++ if (skb->len > BTINTEL_PCIE_BUFFER_SIZE - BTINTEL_PCIE_HCI_TYPE_LEN) { ++ bt_dev_err(hdev, "Packet too large: %u > %u", skb->len, ++ BTINTEL_PCIE_BUFFER_SIZE - BTINTEL_PCIE_HCI_TYPE_LEN); ++ return -EMSGSIZE; ++ } ++ + /* Due to the fw limitation, the type header of the packet should be + * 4 bytes unlike 1 byte for UART. In UART, the firmware can read + * the first byte to get the packet type and redirect the rest of data +diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h +--- a/include/net/bluetooth/hci_core.h ++++ b/include/net/bluetooth/hci_core.h +@@ -62,7 +62,7 @@ struct inquiry_entry { + NAME_PENDING, + NAME_KNOWN, + } name_state; +- __u32 timestamp; ++ unsigned long timestamp; + struct inquiry_data data; + }; + +@@ -78,7 +78,7 @@ struct discovery_state { + struct list_head all; /* All devices found during inquiry */ + struct list_head unknown; /* Name state not known */ + struct list_head resolve; /* Name needs to be resolved */ +- __u32 timestamp; ++ unsigned long timestamp; + bdaddr_t last_adv_addr; + u8 last_adv_addr_type; + s8 last_adv_rssi; +@@ -561,6 +561,7 @@ struct hci_dev { + struct list_head link_keys; + struct list_head long_term_keys; + struct list_head identity_resolving_keys; ++ struct mutex remote_oob_lock; + struct list_head remote_oob_data; + struct list_head le_accept_list; + struct list_head le_resolv_list; +diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c +--- a/net/bluetooth/hci_conn.c ++++ b/net/bluetooth/hci_conn.c +@@ -302,11 +302,13 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data) + cp.tx_bandwidth = cpu_to_le32(0x00001f40); + cp.rx_bandwidth = cpu_to_le32(0x00001f40); + ++ hci_dev_lock(hdev); ++ + switch (conn->codec.id) { + case BT_CODEC_MSBC: + if (!find_next_esco_param(conn, esco_param_msbc, + ARRAY_SIZE(esco_param_msbc))) +- return -EINVAL; ++ goto unlock; + + param = &esco_param_msbc[conn->attempt - 1]; + cp.tx_coding_format.id = 0x05; +@@ -332,7 +334,7 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data) + case BT_CODEC_TRANSPARENT: + if (!find_next_esco_param(conn, esco_param_msbc, + ARRAY_SIZE(esco_param_msbc))) +- return -EINVAL; ++ goto unlock; + + param = &esco_param_msbc[conn->attempt - 1]; + cp.tx_coding_format.id = 0x03; +@@ -359,11 +361,11 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data) + if (conn->parent && lmp_esco_capable(conn->parent)) { + if (!find_next_esco_param(conn, esco_param_cvsd, + ARRAY_SIZE(esco_param_cvsd))) +- return -EINVAL; ++ goto unlock; + param = &esco_param_cvsd[conn->attempt - 1]; + } else { + if (conn->attempt > ARRAY_SIZE(sco_param_cvsd)) +- return -EINVAL; ++ goto unlock; + param = &sco_param_cvsd[conn->attempt - 1]; + } + cp.tx_coding_format.id = 2; +@@ -386,9 +388,11 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data) + cp.out_transport_unit_size = 16; + break; + default: +- return -EINVAL; ++ goto unlock; + } + ++ hci_dev_unlock(hdev); ++ + cp.retrans_effort = param->retrans_effort; + cp.pkt_type = __cpu_to_le16(param->pkt_type); + cp.max_latency = __cpu_to_le16(param->max_latency); +@@ -397,6 +401,10 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data) + return -EIO; + + return 0; ++ ++unlock: ++ hci_dev_unlock(hdev); ++ return -EINVAL; + } + + static bool hci_setup_sync_conn(struct hci_conn *conn, __u16 handle) +diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c +--- a/net/bluetooth/hci_core.c ++++ b/net/bluetooth/hci_core.c +@@ -1486,8 +1486,10 @@ int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, + + BT_DBG("%s removing %pMR (%u)", hdev->name, bdaddr, bdaddr_type); + ++ mutex_lock(&hdev->remote_oob_lock); + list_del(&data->list); + kfree(data); ++ mutex_unlock(&hdev->remote_oob_lock); + + return 0; + } +@@ -1496,10 +1498,12 @@ void hci_remote_oob_data_clear(struct hci_dev *hdev) + { + struct oob_data *data, *n; + ++ mutex_lock(&hdev->remote_oob_lock); + list_for_each_entry_safe(data, n, &hdev->remote_oob_data, list) { + list_del(&data->list); + kfree(data); + } ++ mutex_unlock(&hdev->remote_oob_lock); + } + + int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, +@@ -1508,11 +1512,14 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, + { + struct oob_data *data; + ++ mutex_lock(&hdev->remote_oob_lock); + data = hci_find_remote_oob_data(hdev, bdaddr, bdaddr_type); + if (!data) { + data = kmalloc_obj(*data); +- if (!data) ++ if (!data) { ++ mutex_unlock(&hdev->remote_oob_lock); + return -ENOMEM; ++ } + + bacpy(&data->bdaddr, bdaddr); + data->bdaddr_type = bdaddr_type; +@@ -1545,6 +1552,8 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, + + BT_DBG("%s for %pMR", hdev->name, bdaddr); + ++ mutex_unlock(&hdev->remote_oob_lock); ++ + return 0; + } + +@@ -2482,6 +2491,7 @@ struct hci_dev *hci_alloc_dev_priv(int sizeof_priv) + mutex_init(&hdev->lock); + mutex_init(&hdev->req_lock); + mutex_init(&hdev->mgmt_pending_lock); ++ mutex_init(&hdev->remote_oob_lock); + + ida_init(&hdev->unset_handle_ida); + +@@ -2554,8 +2564,10 @@ int hci_register_dev(struct hci_dev *hdev) + return id; + + error = dev_set_name(&hdev->dev, "hci%u", id); +- if (error) ++ if (error) { ++ ida_free(&hci_index_ida, id); + return error; ++ } + + hdev->name = dev_name(&hdev->dev); + hdev->id = id; +@@ -3323,6 +3335,8 @@ static void hci_queue_iso(struct hci_conn *conn, struct sk_buff_head *queue, + + skb_shinfo(skb)->frag_list = NULL; + ++ spin_lock_bh(&queue->lock); ++ + __skb_queue_tail(queue, skb); + + do { +@@ -3337,6 +3351,8 @@ static void hci_queue_iso(struct hci_conn *conn, struct sk_buff_head *queue, + + __skb_queue_tail(queue, skb); + } while (list); ++ ++ spin_unlock_bh(&queue->lock); + } + + bt_dev_dbg(hdev, "hcon %p queued %d", conn, skb_queue_len(queue)); +@@ -3400,9 +3416,6 @@ static struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, + struct hci_conn *conn = NULL, *c; + unsigned int num = 0, min = ~0; + +- /* We don't have to lock device here. Connections are always +- * added and removed with TX task disabled. */ +- + rcu_read_lock(); + + list_for_each_entry_rcu(c, &h->list, list) { +@@ -3607,13 +3620,15 @@ static void __check_timeout(struct hci_dev *hdev, unsigned int cnt, u8 type) + } + + /* Schedule SCO */ +-static void hci_sched_sco(struct hci_dev *hdev, __u8 type) ++static void __hci_sched_sco(struct hci_dev *hdev, __u8 type) + { + struct hci_conn *conn; + struct sk_buff *skb; + int quote, *cnt; + unsigned int pkts = hdev->sco_pkts; + ++ lockdep_assert_held(&hdev->lock); ++ + bt_dev_dbg(hdev, "type %u", type); + + if (!hci_conn_num(hdev, type) || !pkts) +@@ -3648,6 +3663,13 @@ static void hci_sched_sco(struct hci_dev *hdev, __u8 type) + queue_work(hdev->workqueue, &hdev->tx_work); + } + ++static void hci_sched_sco(struct hci_dev *hdev, __u8 type) ++{ ++ hci_dev_lock(hdev); ++ __hci_sched_sco(hdev, type); ++ hci_dev_unlock(hdev); ++} ++ + static void hci_sched_acl_pkt(struct hci_dev *hdev) + { + unsigned int cnt = hdev->acl_cnt; +@@ -3657,6 +3679,8 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev) + + __check_timeout(hdev, cnt, ACL_LINK); + ++ hci_dev_lock(hdev); ++ + while (hdev->acl_cnt && + (chan = hci_chan_sent(hdev, ACL_LINK, "e))) { + u32 priority = (skb_peek(&chan->data_q))->priority; +@@ -3681,13 +3705,15 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev) + chan->conn->sent++; + + /* Send pending SCO packets right away */ +- hci_sched_sco(hdev, SCO_LINK); +- hci_sched_sco(hdev, ESCO_LINK); ++ __hci_sched_sco(hdev, SCO_LINK); ++ __hci_sched_sco(hdev, ESCO_LINK); + } + } + + if (cnt != hdev->acl_cnt) + hci_prio_recalculate(hdev, ACL_LINK); ++ ++ hci_dev_unlock(hdev); + } + + static void hci_sched_acl(struct hci_dev *hdev) +@@ -3716,6 +3742,8 @@ static void hci_sched_le(struct hci_dev *hdev) + + __check_timeout(hdev, *cnt, LE_LINK); + ++ hci_dev_lock(hdev); ++ + tmp = *cnt; + while (*cnt && (chan = hci_chan_sent(hdev, LE_LINK, "e))) { + u32 priority = (skb_peek(&chan->data_q))->priority; +@@ -3737,13 +3765,15 @@ static void hci_sched_le(struct hci_dev *hdev) + chan->conn->sent++; + + /* Send pending SCO packets right away */ +- hci_sched_sco(hdev, SCO_LINK); +- hci_sched_sco(hdev, ESCO_LINK); ++ __hci_sched_sco(hdev, SCO_LINK); ++ __hci_sched_sco(hdev, ESCO_LINK); + } + } + + if (*cnt != tmp) + hci_prio_recalculate(hdev, LE_LINK); ++ ++ hci_dev_unlock(hdev); + } + + /* Schedule iso */ +@@ -3762,6 +3792,8 @@ static void hci_sched_iso(struct hci_dev *hdev, __u8 type) + + __check_timeout(hdev, *cnt, type); + ++ hci_dev_lock(hdev); ++ + while (*cnt && (conn = hci_low_sent(hdev, type, "e))) { + while (quote-- && (skb = skb_dequeue(&conn->data_q))) { + BT_DBG("skb %p len %d", skb, skb->len); +@@ -3775,6 +3807,8 @@ static void hci_sched_iso(struct hci_dev *hdev, __u8 type) + (*cnt)--; + } + } ++ ++ hci_dev_unlock(hdev); + } + + static void hci_tx_work(struct work_struct *work) +diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c +--- a/net/bluetooth/hci_debugfs.c ++++ b/net/bluetooth/hci_debugfs.c +@@ -364,7 +364,7 @@ static int inquiry_cache_show(struct seq_file *f, void *p) + + list_for_each_entry(e, &cache->all, all) { + struct inquiry_data *data = &e->data; +- seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n", ++ seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %lu\n", + &data->bdaddr, + data->pscan_rep_mode, data->pscan_period_mode, + data->pscan_mode, data->dev_class[2], +diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c +--- a/net/bluetooth/hci_sync.c ++++ b/net/bluetooth/hci_sync.c +@@ -312,6 +312,10 @@ static void hci_cmd_sync_work(struct work_struct *work) + while (1) { + struct hci_cmd_sync_work_entry *entry; + ++ /* Leave the backlog to hci_cmd_sync_clear() */ ++ if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) ++ break; ++ + mutex_lock(&hdev->cmd_sync_work_lock); + entry = list_first_entry_or_null(&hdev->cmd_sync_work_list, + struct hci_cmd_sync_work_entry, +@@ -658,6 +662,8 @@ void hci_cmd_sync_clear(struct hci_dev *hdev) + { + struct hci_cmd_sync_work_entry *entry, *tmp; + ++ /* cmd_work is disabled, the pending request can only time out */ ++ hci_cmd_sync_cancel_sync(hdev, ENODEV); + cancel_work_sync(&hdev->cmd_sync_work); + cancel_work_sync(&hdev->reenable_adv_work); + +@@ -5678,6 +5684,7 @@ int hci_stop_discovery_sync(struct hci_dev *hdev) + { + struct discovery_state *d = &hdev->discovery; + struct inquiry_entry *e; ++ bdaddr_t addr; + int err; + + bt_dev_dbg(hdev, "state %u", hdev->discovery.state); +@@ -5713,15 +5720,21 @@ int hci_stop_discovery_sync(struct hci_dev *hdev) + return 0; + + if (d->state == DISCOVERY_RESOLVING || d->state == DISCOVERY_STOPPING) { ++ hci_dev_lock(hdev); + e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, + NAME_PENDING); +- if (!e) ++ if (!e) { ++ hci_dev_unlock(hdev); + return 0; ++ } ++ ++ bacpy(&addr, &e->data.bdaddr); ++ hci_dev_unlock(hdev); + + /* Ignore cancel errors since it should interfere with stopping + * of the discovery. + */ +- hci_remote_name_cancel_sync(hdev, &e->data.bdaddr); ++ hci_remote_name_cancel_sync(hdev, &addr); + } + + return 0; +@@ -7151,6 +7164,7 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) + bacpy(&cp.bdaddr, &conn->dst); + cp.pscan_rep_mode = 0x02; + ++ hci_dev_lock(hdev); + ie = hci_inquiry_cache_lookup(hdev, &conn->dst); + if (ie) { + if (inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) { +@@ -7162,6 +7176,7 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) + + memcpy(conn->dev_class, ie->data.dev_class, 3); + } ++ hci_dev_unlock(hdev); + + cp.pkt_type = cpu_to_le16(conn->pkt_type); + if (lmp_rswitch_capable(hdev) && !(hdev->link_mode & HCI_LM_MASTER)) +diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c +--- a/net/bluetooth/rfcomm/tty.c ++++ b/net/bluetooth/rfcomm/tty.c +@@ -462,7 +462,7 @@ static int __rfcomm_release_dev(void __user *arg) + /* Shut down TTY synchronously before freeing rfcomm_dev */ + tty_port_tty_vhangup(&dev->port); + +- if (!test_bit(RFCOMM_TTY_OWNED, &dev->status)) ++ if (!test_and_set_bit(RFCOMM_TTY_OWNED, &dev->status)) + tty_port_put(&dev->port); + + tty_port_put(&dev->port); +@@ -724,10 +724,9 @@ static int rfcomm_tty_install(struct tty_driver *driver, struct tty_struct *tty) + * when the last process closes the tty. The behaviour is expected by + * userspace. + */ +- if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) { +- set_bit(RFCOMM_TTY_OWNED, &dev->status); ++ if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags) && ++ !test_and_set_bit(RFCOMM_TTY_OWNED, &dev->status)) + tty_port_put(&dev->port); +- } + + return 0; + } +diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c +--- a/net/bluetooth/smp.c ++++ b/net/bluetooth/smp.c +@@ -661,6 +661,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn, + else + bdaddr_type = BDADDR_LE_RANDOM; + ++ mutex_lock(&hdev->remote_oob_lock); + oob_data = hci_find_remote_oob_data(hdev, &hcon->dst, + bdaddr_type); + if (oob_data && oob_data->present) { +@@ -671,6 +672,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn, + SMP_DBG("OOB Remote Confirmation: %16phN", smp->pcnf); + SMP_DBG("OOB Remote Random: %16phN", smp->rr); + } ++ mutex_unlock(&hdev->remote_oob_lock); + + } else { + authreq &= ~SMP_AUTH_SC; diff --git a/pkgbuilds/linux-omarchy-bore/0670-bluetooth-fixes.patch.sig b/pkgbuilds/linux-omarchy-bore/0670-bluetooth-fixes.patch.sig new file mode 100644 index 0000000..a680e2e Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0670-bluetooth-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0701-pci-amd-enhanced-atomics.patch b/pkgbuilds/linux-omarchy-bore/0701-pci-amd-enhanced-atomics.patch new file mode 100644 index 0000000..c76cb96 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0701-pci-amd-enhanced-atomics.patch @@ -0,0 +1,109 @@ +diff --git a/arch/x86/pci/fixup.c b/arch/x86/pci/fixup.c +--- a/arch/x86/pci/fixup.c ++++ b/arch/x86/pci/fixup.c +@@ -886,6 +886,105 @@ static void quirk_clear_strap_no_soft_reset_dev2_f0(struct pci_dev *dev) + } + } + DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, 0x15b8, quirk_clear_strap_no_soft_reset_dev2_f0); ++ ++/* ++ * Enhanced atomic operations can cause corruption with 64-bit IOVA ++ * on these devices. ++ */ ++#define RX_ENH_ATOMIC_EN BIT(8) ++ ++static const u32 nbio_7_7_pcie_smn_addrs[] = { ++ 0x111401d0, ++ 0x111411d0, ++ 0x111421d0, ++ 0x111431d0, ++ 0x111441d0, ++ 0x112401d0, ++ 0x112411d0, ++ 0x112421d0, ++ 0x112431d0, ++ 0x112441d0, ++ 0x112451d0, ++ 0x113401d0, ++ 0x114401d0, ++}; ++ ++static const u32 nbio_7_11_pcie_smn_addrs[] = { ++ 0x112401d0, ++ 0x112411d0, ++ 0x112421d0, ++ 0x112431d0, ++ 0x112441d0, ++ 0x112451d0, ++ 0x113401d0, ++ 0x113411d0, ++ 0x113421d0, ++ 0x113431d0, ++ 0x113441d0, ++ 0x113451d0, ++}; ++ ++static void quirk_amd_nbio_enhanced_atomic(struct pci_dev *host_bridge, ++ const u32 *smn_addrs, ++ size_t nr_smn_addrs) ++{ ++ bool changed = false; ++ size_t i; ++ u32 data; ++ int ret; ++ ++ for (i = 0; i < nr_smn_addrs; i++) { ++ ret = amd_smn_read(0, smn_addrs[i], &data); ++ if (ret) ++ continue; ++ if (!(data & RX_ENH_ATOMIC_EN)) ++ continue; ++ data = data & ~RX_ENH_ATOMIC_EN; ++ ret = amd_smn_write(0, smn_addrs[i], data); ++ if (ret) ++ continue; ++ if (changed) ++ continue; ++ ret = amd_smn_read(0, smn_addrs[i], &data); ++ if (ret) ++ continue; ++ if (data & RX_ENH_ATOMIC_EN) ++ continue; ++ changed = true; ++ } ++ ++ if (changed) ++ pci_info(host_bridge, "enhanced atomics disabled\n"); ++} ++ ++static void quirk_amd_nbio_7_7_disable_enhanced_atomic(struct pci_dev *dev) ++{ ++ quirk_amd_nbio_enhanced_atomic(dev, nbio_7_7_pcie_smn_addrs, ++ ARRAY_SIZE(nbio_7_7_pcie_smn_addrs)); ++} ++ ++static void quirk_amd_nbio_7_11_disable_enhanced_atomic(struct pci_dev *dev) ++{ ++ quirk_amd_nbio_enhanced_atomic(dev, nbio_7_11_pcie_smn_addrs, ++ ARRAY_SIZE(nbio_7_11_pcie_smn_addrs)); ++} ++ ++/* Phoenix, Hawk Point (NBIO 7.7) */ ++DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, 0x14E8, ++ quirk_amd_nbio_7_7_disable_enhanced_atomic); ++DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, 0x14E8, ++ quirk_amd_nbio_7_7_disable_enhanced_atomic); ++ ++/* Strix, Krackan, Strix Halo (NBIO 7.11) */ ++DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, 0x1507, ++ quirk_amd_nbio_7_11_disable_enhanced_atomic); ++DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, 0x1507, ++ quirk_amd_nbio_7_11_disable_enhanced_atomic); ++DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, 0x1122, ++ quirk_amd_nbio_7_11_disable_enhanced_atomic); ++DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, 0x1122, ++ quirk_amd_nbio_7_11_disable_enhanced_atomic); ++ + #endif + + /* diff --git a/pkgbuilds/linux-omarchy-bore/0701-pci-amd-enhanced-atomics.patch.sig b/pkgbuilds/linux-omarchy-bore/0701-pci-amd-enhanced-atomics.patch.sig new file mode 100644 index 0000000..f5360e4 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0701-pci-amd-enhanced-atomics.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0702-ahci-jmb585-32bit-dma-revert.patch b/pkgbuilds/linux-omarchy-bore/0702-ahci-jmb585-32bit-dma-revert.patch new file mode 100644 index 0000000..9794028 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0702-ahci-jmb585-32bit-dma-revert.patch @@ -0,0 +1,42 @@ +diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c +--- a/drivers/ata/ahci.c ++++ b/drivers/ata/ahci.c +@@ -68,7 +68,6 @@ enum board_ids { + /* board IDs for specific chipsets in alphabetical order */ + board_ahci_al, + board_ahci_avn, +- board_ahci_jmb585, + board_ahci_mcp65, + board_ahci_mcp77, + board_ahci_mcp89, +@@ -213,15 +212,6 @@ static const struct ata_port_info ahci_port_info[] = { + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_avn_ops, + }, +- /* JMicron JMB582/585: 64-bit DMA is broken, force 32-bit */ +- [board_ahci_jmb585] = { +- AHCI_HFLAGS (AHCI_HFLAG_IGN_IRQ_IF_ERR | +- AHCI_HFLAG_32BIT_ONLY), +- .flags = AHCI_FLAG_COMMON, +- .pio_mask = ATA_PIO4, +- .udma_mask = ATA_UDMA6, +- .port_ops = &ahci_ops, +- }, + [board_ahci_mcp65] = { + AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP | + AHCI_HFLAG_YES_NCQ), +@@ -954,14 +944,6 @@ static const struct pci_device_id ahci_pci_tbl[] = { + /* Elkhart Lake AHCI */ + PCI_VDEVICE(INTEL, 0x4b63), + .driver_data = board_ahci_pcs_quirk, +- }, { +- /* JMicron JMB582/585: force 32-bit DMA (broken 64-bit implementation) */ +- PCI_VDEVICE(JMICRON, 0x0582), +- .driver_data = board_ahci_jmb585, +- +- }, { +- PCI_VDEVICE(JMICRON, 0x0585), +- .driver_data = board_ahci_jmb585, + }, { + /* JMicron 360/1/3/5/6, match class to avoid IDE function */ + PCI_DEVICE(PCI_VENDOR_ID_JMICRON, PCI_ANY_ID), diff --git a/pkgbuilds/linux-omarchy-bore/0702-ahci-jmb585-32bit-dma-revert.patch.sig b/pkgbuilds/linux-omarchy-bore/0702-ahci-jmb585-32bit-dma-revert.patch.sig new file mode 100644 index 0000000..b59d6ba Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0702-ahci-jmb585-32bit-dma-revert.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0703-pci-vmd-scan-bios-enumerated-devices.patch b/pkgbuilds/linux-omarchy-bore/0703-pci-vmd-scan-bios-enumerated-devices.patch new file mode 100644 index 0000000..0fe6c04 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0703-pci-vmd-scan-bios-enumerated-devices.patch @@ -0,0 +1,297 @@ +diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c +--- a/drivers/pci/controller/vmd.c ++++ b/drivers/pci/controller/vmd.c +@@ -37,6 +37,12 @@ + #define MB2_SHADOW_OFFSET 0x2000 + #define MB2_SHADOW_SIZE 16 + ++/* DMR BAR4 register offsets */ ++#define SHADOW_MEMBAR1_28C1 0x2818 /* MEMBAR1 physical address */ ++#define SHADOW_MEMBAR2_28C1 0x2820 /* MEMBAR2 physical address */ ++#define BASE_ID_REG_28C1 0x2840 ++#define MEMBAR2_OFFSET_28C1 0x30d0 ++ + enum vmd_features { + /* + * Device may contain registers which hint the physical location of the +@@ -77,6 +83,15 @@ enum vmd_features { + * proper power management of the SoC. + */ + VMD_FEAT_BIOS_PM_QUIRK = (1 << 5), ++ ++ /* ++ * Newer VMD with device ID 0x28C1 has unique settings compared to its ++ * predecessor where BIOS enumerates the entire VMD device tree and ++ * stores respective configurations including bus start range and ++ * shadow registers in VMD MMIO space in VMD BAR4/BAR5, otherwise ++ * referred to as MEMBAR2 or MSI-X BAR. ++ */ ++ VMD_FEAT_USE_BIOS_INFO = (1 << 6), + }; + + #define VMD_BIOS_PM_QUIRK_LTR 0x1003 /* 3145728 ns */ +@@ -142,6 +157,7 @@ struct vmd_dev { + u8 first_vec; + char *name; + int instance; ++ unsigned long features; + }; + + static inline struct vmd_dev *vmd_from_bus(struct pci_bus *bus) +@@ -366,6 +382,9 @@ static void vmd_set_msi_remapping(struct vmd_dev *vmd, bool enable) + { + u16 reg; + ++ if (!!(vmd->features & VMD_FEAT_USE_BIOS_INFO)) ++ return; ++ + pci_read_config_word(vmd->dev, PCI_REG_VMCONFIG, ®); + reg = enable ? (reg & ~VMCONFIG_MSI_REMAP) : + (reg | VMCONFIG_MSI_REMAP); +@@ -389,11 +408,22 @@ static void vmd_remove_irq_domain(struct vmd_dev *vmd) + } + } + ++static unsigned int vmd_bus_to_ecam(struct vmd_dev *vmd, unsigned int busnr) ++{ ++ if (!!(vmd->features & VMD_FEAT_USE_BIOS_INFO)) ++ return busnr; ++ ++ return busnr - vmd->busn_start; ++} ++ + static void __iomem *vmd_cfg_addr(struct vmd_dev *vmd, struct pci_bus *bus, + unsigned int devfn, int reg, int len) + { +- unsigned int busnr_ecam = bus->number - vmd->busn_start; +- u32 offset = PCIE_ECAM_OFFSET(busnr_ecam, devfn, reg); ++ unsigned int busnr_ecam; ++ u32 offset; ++ ++ busnr_ecam = vmd_bus_to_ecam(vmd, bus->number); ++ offset = PCIE_ECAM_OFFSET(busnr_ecam, devfn, reg); + + if (offset + len >= resource_size(&vmd->dev->resource[VMD_CFGBAR])) + return NULL; +@@ -518,22 +548,37 @@ static inline void vmd_acpi_begin(void) { } + static inline void vmd_acpi_end(void) { } + #endif /* CONFIG_ACPI */ + ++static resource_size_t vmd_cfgbar_ecam_space(struct vmd_dev *vmd) ++{ ++ resource_size_t cfgbar_buses; ++ unsigned int ecam_start; ++ ++ cfgbar_buses = resource_size(&vmd->dev->resource[VMD_CFGBAR]) >> 20; ++ ecam_start = vmd_bus_to_ecam(vmd, vmd->resources[0].start); ++ if (ecam_start >= cfgbar_buses) ++ return 0; ++ ++ return cfgbar_buses - ecam_start; ++} + static void vmd_domain_reset(struct vmd_dev *vmd) + { + u16 bus, max_buses = resource_size(&vmd->resources[0]); + u8 dev, functions, fn, hdr_type; ++ unsigned int ecam_bus; + char __iomem *base; + ++ max_buses = min_t(u16, max_buses, vmd_cfgbar_ecam_space(vmd)); + for (bus = 0; bus < max_buses; bus++) { ++ ecam_bus = vmd_bus_to_ecam(vmd, vmd->resources[0].start + bus); + for (dev = 0; dev < 32; dev++) { +- base = vmd->cfgbar + PCIE_ECAM_OFFSET(bus, ++ base = vmd->cfgbar + PCIE_ECAM_OFFSET(ecam_bus, + PCI_DEVFN(dev, 0), 0); + + hdr_type = readb(base + PCI_HEADER_TYPE); + + functions = (hdr_type & PCI_HEADER_TYPE_MFD) ? 8 : 1; + for (fn = 0; fn < functions; fn++) { +- base = vmd->cfgbar + PCIE_ECAM_OFFSET(bus, ++ base = vmd->cfgbar + PCIE_ECAM_OFFSET(ecam_bus, + PCI_DEVFN(dev, fn), 0); + + hdr_type = readb(base + PCI_HEADER_TYPE) & +@@ -661,6 +706,46 @@ static int vmd_get_bus_number_start(struct vmd_dev *vmd) + return 0; + } + ++static int vmd_get_bus_info_from_bar4(struct vmd_dev *vmd, ++ resource_size_t *offset1, ++ resource_size_t *offset2) ++{ ++ u64 phys1, phys2, bar4_2840; ++ void __iomem *bar4; ++ u32 base_id; ++ u8 base_bus; ++ ++ bar4 = pci_ioremap_bar(vmd->dev, 4); ++ if (!bar4) ++ return -ENOMEM; ++ ++ /* Read shadow registers for MEMBAR1 and MEMBAR2 physical addresses */ ++ phys1 = readq(bar4 + SHADOW_MEMBAR1_28C1); ++ phys2 = readq(bar4 + SHADOW_MEMBAR2_28C1); ++ ++ /* ++ * Read and set bus start number from Base ID register. 24-bit Base ID ++ * register is part of 64-bit shadowed reqid hide range register and ++ * holds segment, bus, device and function. ++ */ ++ bar4_2840 = readq(bar4 + BASE_ID_REG_28C1); ++ base_id = bar4_2840 & 0xFFFFFF; ++ base_bus = base_id >> 8; ++ vmd->busn_start = base_bus; ++ ++ /* Calculate offsets like vmd_get_phys_offsets() does */ ++ if (phys1) ++ *offset1 = vmd->dev->resource[VMD_MEMBAR1].start - ++ (phys1 & PCI_BASE_ADDRESS_MEM_MASK); ++ if (phys2) ++ *offset2 = vmd->dev->resource[VMD_MEMBAR2].start - ++ (phys2 & PCI_BASE_ADDRESS_MEM_MASK); ++ ++ pci_iounmap(vmd->dev, bar4); ++ ++ return 0; ++} ++ + static irqreturn_t vmd_irq(int irq, void *data) + { + struct vmd_irq_list *irqs = data; +@@ -711,6 +796,52 @@ static int vmd_alloc_irqs(struct vmd_dev *vmd) + return 0; + } + ++static int vmd_prepare_offsets_and_bus(struct vmd_dev *vmd, ++ unsigned long features, ++ resource_size_t *membar2_offset, ++ resource_size_t *offset1, ++ resource_size_t *offset2) ++{ ++ int ret; ++ ++ /* ++ * Shadow registers may exist in certain VMD device IDs which allow ++ * guests to correctly assign host physical addresses to the root ports ++ * and child devices. These registers will either return the host value ++ * or 0, depending on an enable bit in the VMD device. ++ * ++ * For certain VMD devices (i.e. 0x28C1), BIOS places device info ++ * in BAR4 shadow registers to determine the base bus number and memory ++ * offsets. ++ */ ++ if (features & VMD_FEAT_USE_BIOS_INFO) { ++ *membar2_offset = MEMBAR2_OFFSET_28C1; ++ ret = vmd_get_bus_info_from_bar4(vmd, offset1, offset2); ++ if (ret) ++ return ret; ++ } else if (features & VMD_FEAT_HAS_MEMBAR_SHADOW) { ++ *membar2_offset = MB2_SHADOW_OFFSET + MB2_SHADOW_SIZE; ++ ret = vmd_get_phys_offsets(vmd, true, offset1, offset2); ++ if (ret) ++ return ret; ++ } else if (features & VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP) { ++ ret = vmd_get_phys_offsets(vmd, false, offset1, offset2); ++ if (ret) ++ return ret; ++ } ++ ++ /* ++ * Certain VMD devices may have a root port configuration option which ++ * limits the bus range to between 0-127, 128-255, or 224-255. ++ */ ++ if (features & VMD_FEAT_HAS_BUS_RESTRICTIONS) { ++ ret = vmd_get_bus_number_start(vmd); ++ if (ret) ++ return ret; ++ } ++ return 0; ++} ++ + /* + * Since VMD is an aperture to regular PCIe root ports, only allow it to + * control features that the OS is allowed to control on the physical PCI bus. +@@ -780,42 +911,24 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) + LIST_HEAD(resources); + resource_size_t offset[2] = {0}; + resource_size_t membar2_offset = 0x2000; ++ resource_size_t busn_end; + struct pci_bus *child; + struct pci_dev *dev; + int ret; + +- /* +- * Shadow registers may exist in certain VMD device ids which allow +- * guests to correctly assign host physical addresses to the root ports +- * and child devices. These registers will either return the host value +- * or 0, depending on an enable bit in the VMD device. +- */ +- if (features & VMD_FEAT_HAS_MEMBAR_SHADOW) { +- membar2_offset = MB2_SHADOW_OFFSET + MB2_SHADOW_SIZE; +- ret = vmd_get_phys_offsets(vmd, true, &offset[0], &offset[1]); +- if (ret) +- return ret; +- } else if (features & VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP) { +- ret = vmd_get_phys_offsets(vmd, false, &offset[0], &offset[1]); +- if (ret) +- return ret; +- } +- +- /* +- * Certain VMD devices may have a root port configuration option which +- * limits the bus range to between 0-127, 128-255, or 224-255 +- */ +- if (features & VMD_FEAT_HAS_BUS_RESTRICTIONS) { +- ret = vmd_get_bus_number_start(vmd); +- if (ret) +- return ret; +- } ++ ret = vmd_prepare_offsets_and_bus(vmd, features, &membar2_offset, ++ &offset[0], &offset[1]); ++ if (ret) ++ return ret; + ++ /* Do not let resource[0] end go out of bounds */ + res = &vmd->dev->resource[VMD_CFGBAR]; ++ busn_end = vmd->busn_start + (resource_size(res) >> 20) - 1; ++ busn_end = min_t(resource_size_t, busn_end, 0xff); + vmd->resources[0] = (struct resource) { + .name = "VMD CFGBAR", + .start = vmd->busn_start, +- .end = vmd->busn_start + (resource_size(res) >> 20) - 1, ++ .end = busn_end, + .flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED, + }; + +@@ -998,6 +1111,7 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id) + + vmd->dev = dev; + vmd->sysdata.domain = PCI_DOMAIN_NR_NOT_SET; ++ vmd->features = features; + vmd->instance = ida_alloc(&vmd_instance_ida, GFP_KERNEL); + if (vmd->instance < 0) + return vmd->instance; +@@ -1114,6 +1228,10 @@ static const struct pci_device_id vmd_ids[] = { + .driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW | + VMD_FEAT_HAS_BUS_RESTRICTIONS | + VMD_FEAT_CAN_BYPASS_MSI_REMAP,}, ++ {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_VMD_28C1), ++ .driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW | ++ VMD_FEAT_CAN_BYPASS_MSI_REMAP | ++ VMD_FEAT_USE_BIOS_INFO,}, + {PCI_VDEVICE(INTEL, 0x467f), + .driver_data = VMD_FEATS_CLIENT,}, + {PCI_VDEVICE(INTEL, 0x4c3d), +diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h +--- a/include/linux/pci_ids.h ++++ b/include/linux/pci_ids.h +@@ -2892,6 +2892,7 @@ + #define PCI_DEVICE_ID_INTEL_HDA_ICH8 0x284b + #define PCI_DEVICE_ID_INTEL_ICH8_6 0x2850 + #define PCI_DEVICE_ID_INTEL_VMD_28C0 0x28c0 ++#define PCI_DEVICE_ID_INTEL_VMD_28C1 0x28c1 + #define PCI_DEVICE_ID_INTEL_ICH9_0 0x2910 + #define PCI_DEVICE_ID_INTEL_ICH9_2 0x2912 + #define PCI_DEVICE_ID_INTEL_ICH9_3 0x2913 diff --git a/pkgbuilds/linux-omarchy-bore/0703-pci-vmd-scan-bios-enumerated-devices.patch.sig b/pkgbuilds/linux-omarchy-bore/0703-pci-vmd-scan-bios-enumerated-devices.patch.sig new file mode 100644 index 0000000..5969dea Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0703-pci-vmd-scan-bios-enumerated-devices.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0704-pci-vmd-meteor-lake-mtl016-flush.patch b/pkgbuilds/linux-omarchy-bore/0704-pci-vmd-meteor-lake-mtl016-flush.patch new file mode 100644 index 0000000..22be8d3 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0704-pci-vmd-meteor-lake-mtl016-flush.patch @@ -0,0 +1,120 @@ +diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c +--- a/drivers/pci/controller/vmd.c ++++ b/drivers/pci/controller/vmd.c +@@ -92,6 +92,22 @@ enum vmd_features { + * referred to as MEMBAR2 or MSI-X BAR. + */ + VMD_FEAT_USE_BIOS_INFO = (1 << 6), ++ ++ /* ++ * Meteor Lake VMD (device ID 0x7d0b) is affected by erratum MTL016: ++ * the VMD may signal its MSI before the posted writes that carry the ++ * child device's DMA data have landed in memory. The demuxed handler ++ * then runs against a not-yet-coherent completion queue and misses the ++ * completion entirely, so the I/O is only recovered when the block ++ * layer timeout fires and polls the queue ("timeout, completion ++ * polled"). Intel's documented workaround is to issue a dummy read ++ * to the MSI initiator (the child device) before handling the ++ * interrupt: the read completion cannot pass the device's earlier ++ * posted writes, so it pulls them into memory per PCIe ordering ++ * rules. A read that terminates at the VMD itself does not order ++ * against the child's writes and is not sufficient (measured). ++ */ ++ VMD_FEAT_INTERRUPT_QUIRK = (1 << 7), + }; + + #define VMD_BIOS_PM_QUIRK_LTR 0x1003 /* 3145728 ns */ +@@ -114,6 +130,9 @@ static DEFINE_RAW_SPINLOCK(list_lock); + * @irq: back pointer to parent. + * @enabled: true if driver enabled IRQ + * @virq: the virtual IRQ value provided to the requesting driver. ++ * @flush_addr: config space address of the initiating device, read before ++ * demuxing to flush its posted writes (MTL016); NULL if the ++ * VMD is not affected. + * + * Every MSI/MSI-X IRQ requested for a device in a VMD domain will be mapped to + * a VMD IRQ using this structure. +@@ -123,8 +142,14 @@ struct vmd_irq { + struct vmd_irq_list *irq; + bool enabled; + unsigned int virq; ++ void __iomem *flush_addr; + }; + ++struct vmd_dev; ++ ++static void __iomem *vmd_cfg_addr(struct vmd_dev *vmd, struct pci_bus *bus, ++ unsigned int devfn, int reg, int len); ++ + /** + * struct vmd_irq_list - list of driver requested IRQs mapping to a VMD vector + * @irq_list: the list of irq's the VMD one demuxes to. +@@ -132,12 +157,15 @@ struct vmd_irq { + * @count: number of child IRQs assigned to this vector; used to track + * sharing. + * @virq: The underlying VMD Linux interrupt number ++ * @vmd: back pointer to the owning VMD device; serializes the MTL016 ++ * flush read against other config space access. + */ + struct vmd_irq_list { + struct list_head irq_list; + struct srcu_struct srcu; + unsigned int count; + unsigned int virq; ++ struct vmd_dev *vmd; + }; + + struct vmd_dev { +@@ -295,6 +323,13 @@ static int vmd_msi_alloc(struct irq_domain *domain, unsigned int virq, + INIT_LIST_HEAD(&vmdirq->node); + vmdirq->irq = vmd_next_irq(vmd, desc); + vmdirq->virq = virq + i; ++ if (vmd->features & VMD_FEAT_INTERRUPT_QUIRK) { ++ struct pci_dev *pdev = msi_desc_to_pci_dev(desc); ++ ++ vmdirq->flush_addr = vmd_cfg_addr(vmd, pdev->bus, ++ pdev->devfn, ++ PCI_VENDOR_ID, 2); ++ } + + irq_domain_set_info(domain, virq + i, vmdirq->irq->virq, + &vmd_msi_controller, vmdirq, +@@ -753,8 +788,20 @@ static irqreturn_t vmd_irq(int irq, void *data) + int idx; + + idx = srcu_read_lock(&irqs->srcu); +- list_for_each_entry_rcu(vmdirq, &irqs->irq_list, node) ++ list_for_each_entry_rcu(vmdirq, &irqs->irq_list, node) { ++ /* ++ * MTL016: the MSI may have outrun the initiating device's ++ * posted writes (e.g. its NVMe completion entry). A read ++ * that completes at the initiator flushes them, so the ++ * demuxed handler observes a coherent completion queue. ++ * The value is discarded; only the ordering matters. ++ */ ++ if (vmdirq->flush_addr) { ++ guard(raw_spinlock)(&irqs->vmd->cfg_lock); ++ readw(vmdirq->flush_addr); ++ } + generic_handle_irq(vmdirq->virq); ++ } + srcu_read_unlock(&irqs->srcu, idx); + + return IRQ_HANDLED; +@@ -785,6 +832,7 @@ static int vmd_alloc_irqs(struct vmd_dev *vmd) + return err; + + INIT_LIST_HEAD(&vmd->irqs[i].irq_list); ++ vmd->irqs[i].vmd = vmd; + vmd->irqs[i].virq = pci_irq_vector(dev, i); + err = devm_request_irq(&dev->dev, vmd->irqs[i].virq, + vmd_irq, IRQF_NO_THREAD, +@@ -1239,7 +1287,7 @@ static const struct pci_device_id vmd_ids[] = { + {PCI_VDEVICE(INTEL, 0xa77f), + .driver_data = VMD_FEATS_CLIENT,}, + {PCI_VDEVICE(INTEL, 0x7d0b), +- .driver_data = VMD_FEATS_CLIENT,}, ++ .driver_data = VMD_FEATS_CLIENT | VMD_FEAT_INTERRUPT_QUIRK,}, + {PCI_VDEVICE(INTEL, 0xad0b), + .driver_data = VMD_FEATS_CLIENT,}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_VMD_9A0B), diff --git a/pkgbuilds/linux-omarchy-bore/0704-pci-vmd-meteor-lake-mtl016-flush.patch.sig b/pkgbuilds/linux-omarchy-bore/0704-pci-vmd-meteor-lake-mtl016-flush.patch.sig new file mode 100644 index 0000000..63aecd3 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0704-pci-vmd-meteor-lake-mtl016-flush.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0705-pci-toshiba-ahci-ssd-dma-alias-quirk.patch b/pkgbuilds/linux-omarchy-bore/0705-pci-toshiba-ahci-ssd-dma-alias-quirk.patch new file mode 100644 index 0000000..49806cf --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0705-pci-toshiba-ahci-ssd-dma-alias-quirk.patch @@ -0,0 +1,13 @@ +diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c +--- a/drivers/pci/quirks.c ++++ b/drivers/pci/quirks.c +@@ -4370,6 +4370,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_JMICRON, + DECLARE_PCI_FIXUP_HEADER(0x1c28, /* Lite-On */ + 0x0122, /* Plextor M6E (Marvell 88SS9183)*/ + quirk_dma_func1_alias); ++/* Toshiba AHCI SSD controller in MacBookAir6,1 and MacBookAir7,1 */ ++DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TOSHIBA, 0x010b, ++ quirk_dma_func1_alias); + + /* + * Some devices DMA with the wrong devfn, not just the wrong function. diff --git a/pkgbuilds/linux-omarchy-bore/0705-pci-toshiba-ahci-ssd-dma-alias-quirk.patch.sig b/pkgbuilds/linux-omarchy-bore/0705-pci-toshiba-ahci-ssd-dma-alias-quirk.patch.sig new file mode 100644 index 0000000..678cdb3 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0705-pci-toshiba-ahci-ssd-dma-alias-quirk.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0740-hwmon-updates.patch b/pkgbuilds/linux-omarchy-bore/0740-hwmon-updates.patch new file mode 100644 index 0000000..a66a783 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0740-hwmon-updates.patch @@ -0,0 +1,7419 @@ +diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig +--- a/drivers/hwmon/Kconfig ++++ b/drivers/hwmon/Kconfig +@@ -224,6 +224,7 @@ config SENSORS_ADT7462 + config SENSORS_ADT7470 + tristate "Analog Devices ADT7470" + depends on I2C ++ depends on PWM || PWM=n + select REGMAP_I2C + help + If you say yes here you get support for the Analog Devices +@@ -292,11 +293,11 @@ config SENSORS_ASC7621 + will be called asc7621. + + config SENSORS_ASUS_ROG_RYUJIN +- tristate "ASUS ROG RYUJIN II 360 hardware monitoring driver" ++ tristate "ASUS ROG RYUJIN hardware monitoring driver" + depends on HID + help + If you say yes here you get support for the fans and sensors of +- the ASUS ROG RYUJIN II 360 AIO CPU liquid cooler. ++ supported ASUS ROG RYUJIN II and III AIO CPU liquid coolers. + + This driver can also be built as a module. If so, the module + will be called asus_rog_ryujin. +@@ -335,6 +336,17 @@ config SENSORS_K10TEMP + This driver can also be built as a module. If so, the module + will be called k10temp. + ++config SENSORS_KB9002 ++ tristate "Kandou KB9002 PCIe retimer" ++ depends on I2C ++ help ++ If you say yes here you get support for the integrated ++ temperature sensor and firmware version readout on Kandou ++ KB9002 PCIe 5.0 retimers, accessed over SMBus. ++ ++ This driver can also be built as a module. If so, the module ++ will be called kb9002. ++ + config SENSORS_KBATT + tristate "KEBA battery controller support" + depends on KEBA_CP500 +@@ -403,7 +415,6 @@ config SENSORS_ARCTIC_FAN_CONTROLLER + config SENSORS_ARM_SCMI + tristate "ARM SCMI Sensors" + depends on ARM_SCMI_PROTOCOL +- depends on THERMAL || !THERMAL_OF + help + This driver provides support for temperature, voltage, current + and power sensors available on SCMI based platforms. The actual +@@ -415,7 +426,6 @@ config SENSORS_ARM_SCMI + config SENSORS_ARM_SCPI + tristate "ARM SCPI Sensors" + depends on ARM_SCPI_PROTOCOL +- depends on THERMAL || !THERMAL_OF + help + This driver provides support for temperature, voltage, current + and power sensors available on ARM Ltd's SCP based platforms. The +@@ -435,7 +445,6 @@ config SENSORS_ASB100 + config SENSORS_ASPEED + tristate "ASPEED AST2400/AST2500 PWM and Fan tach driver" + depends on ARCH_ASPEED || COMPILE_TEST +- depends on THERMAL || THERMAL=n + select REGMAP + help + This driver provides support for ASPEED AST2400/AST2500 PWM +@@ -506,8 +515,8 @@ config SENSORS_CORSAIR_PSU + If you say yes here you get support for Corsair PSUs with a HID + interface. + Currently this driver supports the (RM/HX)550i, (RM/HX)650i, +- (RM/HX)750i, (RM/HX)850i, (RM/HX)1000i and HX1200i power supplies +- by Corsair. ++ (RM/HX)750i, (RM/HX)850i, (RM/HX)1000i, HX1200i and HX1500i power ++ supplies by Corsair. + + This driver can also be built as a module. If so, the module + will be called corsair-psu. +@@ -788,7 +797,6 @@ config SENSORS_G762 + config SENSORS_GPIO_FAN + tristate "GPIO fan" + depends on GPIOLIB || COMPILE_TEST +- depends on THERMAL || THERMAL=n + help + If you say yes here you get support for fans connected to GPIO lines. + +@@ -802,7 +810,7 @@ config SENSORS_GXP_FAN_CTRL + If you say yes here you get support for GXP fan control functionality. + + The GXP controls fan function via the CPLD through the use of PWM +- registers. This driver reports status and pwm setting of the fans. ++ registers. This driver enables pwm setting of the fans. + + config SENSORS_HIH6130 + tristate "Honeywell Humidicon HIH-6130 humidity/temperature sensor" +@@ -942,6 +950,19 @@ config SENSORS_JC42 + This driver can also be built as a module. If so, the module + will be called jc42. + ++config SENSORS_POLARFIRE_SOC_TVS ++ tristate "PolarFire SoC (MPFS) temperature and voltage sensor" ++ depends on POLARFIRE_SOC_SYSCONS || COMPILE_TEST ++ depends on MFD_SYSCON ++ help ++ This driver adds support for the PolarFire SoC (MPFS) Temperature and ++ Voltage Sensor. ++ ++ To compile this driver as a module, choose M here. the ++ module will be called tvs-mpfs. ++ ++ If unsure, say N. ++ + config SENSORS_POWERZ + tristate "ChargerLAB POWER-Z USB-C tester" + depends on USB +@@ -1357,7 +1378,6 @@ config SENSORS_MAX6639 + config SENSORS_MAX6650 + tristate "Maxim MAX6650 sensor chip" + depends on I2C +- depends on THERMAL || THERMAL=n + help + If you say yes here you get support for the MAX6650 / MAX6651 + sensor chips. +@@ -1732,7 +1752,6 @@ config SENSORS_PC87427 + config SENSORS_NTC_THERMISTOR + tristate "NTC thermistor support" + depends on IIO +- depends on THERMAL || !THERMAL_OF + help + This driver supports NTC thermistors sensor reading and its + interpretation. The driver can also monitor the temperature and +@@ -1933,7 +1952,6 @@ config SENSORS_PT5161L + config SENSORS_PWM_FAN + tristate "PWM fan" + depends on PWM || COMPILE_TEST +- depends on THERMAL || THERMAL=n + help + If you say yes here you get support for fans connected to PWM lines. + The driver uses the generic PWM interface, thus it will work on a +@@ -1945,7 +1963,6 @@ config SENSORS_PWM_FAN + config SENSORS_QNAP_MCU_HWMON + tristate "QNAP MCU hardware monitoring" + depends on MFD_QNAP_MCU +- depends on THERMAL || THERMAL=n + help + Say yes here to enable support for fan and temperature sensor + connected to a QNAP MCU, as found in a number of QNAP network +@@ -2010,8 +2027,9 @@ config SENSORS_SHT3x + depends on I2C + select CRC8 + help +- If you say yes here you get support for the Sensiron SHT30, SHT31 and +- SHT85 humidity and temperature sensors. ++ If you say yes here you get support for the Sensiron SHT30, SHT31, ++ SHT85 and the compatible GXCAS GXHT30 humidity and temperature ++ sensors. + + This driver can also be built as a module. If so, the module + will be called sht3x. +@@ -2070,6 +2088,17 @@ config SENSORS_DME1737 + This driver can also be built as a module. If so, the module + will be called dme1737. + ++config SENSORS_EIC7700_PVT ++ tristate "Eswin EIC7700 Voltage, Temperature sensor driver" ++ depends on ARCH_ESWIN || COMPILE_TEST ++ select POLYNOMIAL ++ help ++ If you say yes here you get support for Eswin EIC7700 PVT sensor ++ embedded into the SoC. ++ ++ This driver can also be built as a module. If so, the module will be ++ called eic7700-pvt. ++ + config SENSORS_EMC1403 + tristate "SMSC EMC1403/23 thermal sensor" + depends on I2C +diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile +--- a/drivers/hwmon/Makefile ++++ b/drivers/hwmon/Makefile +@@ -72,6 +72,7 @@ obj-$(CONFIG_SENSORS_DME1737) += dme1737.o + obj-$(CONFIG_SENSORS_DRIVETEMP) += drivetemp.o + obj-$(CONFIG_SENSORS_DS620) += ds620.o + obj-$(CONFIG_SENSORS_DS1621) += ds1621.o ++obj-$(CONFIG_SENSORS_EIC7700_PVT) += eic7700-pvt.o + obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o + obj-$(CONFIG_SENSORS_EMC1812) += emc1812.o + obj-$(CONFIG_SENSORS_EMC2103) += emc2103.o +@@ -112,6 +113,7 @@ obj-$(CONFIG_SENSORS_IT87) += it87.o + obj-$(CONFIG_SENSORS_JC42) += jc42.o + obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o + obj-$(CONFIG_SENSORS_K10TEMP) += k10temp.o ++obj-$(CONFIG_SENSORS_KB9002) += kb9002.o + obj-$(CONFIG_SENSORS_KBATT) += kbatt.o + obj-$(CONFIG_SENSORS_KFAN) += kfan.o + obj-$(CONFIG_SENSORS_LAN966X) += lan966x-hwmon.o +@@ -197,6 +199,7 @@ obj-$(CONFIG_SENSORS_NZXT_SMART2) += nzxt-smart2.o + obj-$(CONFIG_SENSORS_PC87360) += pc87360.o + obj-$(CONFIG_SENSORS_PC87427) += pc87427.o + obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o ++obj-$(CONFIG_SENSORS_POLARFIRE_SOC_TVS) += tvs-mpfs.o + obj-$(CONFIG_SENSORS_POWERZ) += powerz.o + obj-$(CONFIG_SENSORS_POWR1220) += powr1220.o + obj-$(CONFIG_SENSORS_PROM21_XHCI) += prom21-xhci.o +diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c +--- a/drivers/hwmon/acpi_power_meter.c ++++ b/drivers/hwmon/acpi_power_meter.c +@@ -21,8 +21,6 @@ + #include + + #define ACPI_POWER_METER_NAME "power_meter" +-#define ACPI_POWER_METER_DEVICE_NAME "Power Meter" +-#define ACPI_POWER_METER_CLASS "pwr_meter_resource" + + #define NUM_SENSORS 17 + +@@ -877,7 +875,7 @@ static void acpi_power_meter_notify(acpi_handle handle, u32 event, void *data) + break; + } + +- acpi_bus_generate_netlink_event(ACPI_POWER_METER_CLASS, ++ acpi_bus_generate_netlink_event("pwr_meter_resource", + dev_name(&resource->acpi_dev->dev), + event, 0); + } +@@ -899,8 +897,6 @@ static int acpi_power_meter_probe(struct platform_device *pdev) + resource->sensors_valid = 0; + resource->acpi_dev = device; + mutex_init(&resource->lock); +- strscpy(acpi_device_name(device), ACPI_POWER_METER_DEVICE_NAME); +- strscpy(acpi_device_class(device), ACPI_POWER_METER_CLASS); + + platform_set_drvdata(pdev, resource); + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -22,6 +22,9 @@ + #include + #include + #include ++#include ++#include ++#include + + /* Addresses to scan */ + static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; +@@ -100,6 +103,7 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; + #define ADT7470_REG_FAN_MAX(x) (ADT7470_REG_FAN_MAX_BASE_ADDR + ((x) * 2)) + + #define ADT7470_PWM_COUNT 4 ++#define ADT7470_PWM_MAX 255 + #define ADT7470_REG_PWM(x) (ADT7470_REG_PWM_BASE_ADDR + (x)) + #define ADT7470_REG_PWM_MAX(x) (ADT7470_REG_PWM_MAX_BASE_ADDR + (x)) + #define ADT7470_REG_PWM_MIN(x) (ADT7470_REG_PWM_MIN_BASE_ADDR + (x)) +@@ -163,6 +167,7 @@ struct adt7470_data { + char limits_valid; + unsigned long sensors_last_updated; /* In jiffies */ + unsigned long limits_last_updated; /* In jiffies */ ++ bool use_pwm_framework; + + int num_temp_sensors; /* -1 = probe */ + int temperatures_probed; +@@ -850,7 +855,7 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val + + switch (attr) { + case hwmon_pwm_input: +- val = clamp_val(val, 0, 255); ++ val = clamp_val(val, 0, ADT7470_PWM_MAX); + mutex_lock(&data->lock); + err = regmap_write(data->regmap, ADT7470_REG_PWM(channel), + val); +@@ -886,6 +891,133 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val + return err; + } + ++struct adt7470_pwm_wfhw { ++ u8 val; ++}; ++ ++static int adt7470_pwm_round_waveform_tohw(struct pwm_chip *chip, ++ struct pwm_device *pwm, ++ const struct pwm_waveform *wf, ++ void *_wfhw) ++{ ++ struct adt7470_data *data = pwmchip_get_drvdata(chip); ++ struct adt7470_pwm_wfhw *wfhw = _wfhw; ++ u64 actual_period; ++ ++ if (wf->duty_length_ns == 0) { ++ wfhw->val = 0; ++ return 0; ++ } ++ ++ /* ++ * The PWM frequency (period) is a single chip-wide setting shared by ++ * all 4 channels, so it cannot be changed on a per-pwm_device basis ++ * through this API. The duty cycle is rounded against the currently ++ * configured hardware period rather than the period requested in ++ * @wf; round_waveform_fromhw() reports the actual resulting ++ * waveform back so the core/consumer can detect a mismatch. ++ */ ++ actual_period = DIV_ROUND_UP_ULL(NSEC_PER_SEC, data->pwm_freq); ++ ++ if (actual_period > wf->period_length_ns) ++ /* period too short */ ++ return 1; ++ ++ if (wf->duty_length_ns >= actual_period) { ++ wfhw->val = ADT7470_PWM_MAX; ++ } else { ++ wfhw->val = mul_u64_u64_div_u64(wf->duty_length_ns, ++ ADT7470_PWM_MAX, ++ actual_period); ++ } ++ ++ return 0; ++} ++ ++static int adt7470_pwm_round_waveform_fromhw(struct pwm_chip *chip, ++ struct pwm_device *pwm, ++ const void *_wfhw, ++ struct pwm_waveform *wf) ++{ ++ struct adt7470_data *data = pwmchip_get_drvdata(chip); ++ const struct adt7470_pwm_wfhw *wfhw = _wfhw; ++ ++ wf->period_length_ns = DIV_ROUND_UP_ULL(NSEC_PER_SEC, data->pwm_freq); ++ wf->duty_offset_ns = 0; ++ wf->duty_length_ns = DIV_ROUND_UP_ULL((u64)wfhw->val * wf->period_length_ns, ++ ADT7470_PWM_MAX); ++ return 0; ++} ++ ++static int adt7470_pwm_read_waveform(struct pwm_chip *chip, ++ struct pwm_device *pwm, ++ void *_wfhw) ++{ ++ struct adt7470_data *data = pwmchip_get_drvdata(chip); ++ struct device *dev = regmap_get_device(data->regmap); ++ struct adt7470_pwm_wfhw *wfhw = _wfhw; ++ ++ data = adt7470_update_device(dev); ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ ++ /* ++ * No lock needed: like the other hwmon_ops read callbacks in this ++ * driver (e.g. adt7470_pwm_read()), this only does a single byte ++ * read from the cache populated by adt7470_update_device(). ++ */ ++ wfhw->val = data->pwm[pwm->hwpwm]; ++ ++ return 0; ++} ++ ++static int adt7470_pwm_write_waveform(struct pwm_chip *chip, ++ struct pwm_device *pwm, ++ const void *_wfhw) ++{ ++ struct adt7470_data *data = pwmchip_get_drvdata(chip); ++ const struct adt7470_pwm_wfhw *wfhw = _wfhw; ++ unsigned int pwm_auto_reg_mask; ++ int err; ++ ++ if (pwm->hwpwm % 2) ++ pwm_auto_reg_mask = ADT7470_PWM2_AUTO_MASK; ++ else ++ pwm_auto_reg_mask = ADT7470_PWM1_AUTO_MASK; ++ ++ guard(mutex)(&data->lock); ++ ++ if (data->pwm[pwm->hwpwm] == wfhw->val && ++ data->pwm_automatic[pwm->hwpwm] == 0) ++ return 0; ++ ++ /* Put the PWM channel in manual mode before updating it. */ ++ err = regmap_update_bits(data->regmap, ++ ADT7470_REG_PWM_CFG(pwm->hwpwm), ++ pwm_auto_reg_mask, 0); ++ if (err < 0) ++ return err; ++ ++ data->pwm_automatic[pwm->hwpwm] = 0; ++ ++ err = regmap_write(data->regmap, ++ ADT7470_REG_PWM(pwm->hwpwm), wfhw->val); ++ if (err < 0) ++ return err; ++ ++ data->pwm[pwm->hwpwm] = wfhw->val; ++ ++ return 0; ++} ++ ++static const struct pwm_ops adt7470_pwm_ops = { ++ .sizeof_wfhw = sizeof(struct adt7470_pwm_wfhw), ++ .round_waveform_tohw = adt7470_pwm_round_waveform_tohw, ++ .round_waveform_fromhw = adt7470_pwm_round_waveform_fromhw, ++ .read_waveform = adt7470_pwm_read_waveform, ++ .write_waveform = adt7470_pwm_write_waveform, ++}; ++ + static ssize_t pwm_max_show(struct device *dev, + struct device_attribute *devattr, char *buf) + { +@@ -910,7 +1042,7 @@ static ssize_t pwm_max_store(struct device *dev, + if (kstrtol(buf, 10, &temp)) + return -EINVAL; + +- temp = clamp_val(temp, 0, 255); ++ temp = clamp_val(temp, 0, ADT7470_PWM_MAX); + + mutex_lock(&data->lock); + data->pwm_max[attr->index] = temp; +@@ -945,7 +1077,7 @@ static ssize_t pwm_min_store(struct device *dev, + if (kstrtol(buf, 10, &temp)) + return -EINVAL; + +- temp = clamp_val(temp, 0, 255); ++ temp = clamp_val(temp, 0, ADT7470_PWM_MAX); + + mutex_lock(&data->lock); + data->pwm_min[attr->index] = temp; +@@ -1106,6 +1238,10 @@ static struct attribute *adt7470_attrs[] = { + &dev_attr_alarm_mask.attr, + &dev_attr_num_temp_sensors.attr, + &dev_attr_auto_update_interval.attr, ++ NULL ++}; ++ ++static struct attribute *adt7470_pwm_attrs[] = { + &sensor_dev_attr_force_pwm_max.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, +@@ -1130,7 +1266,32 @@ static struct attribute *adt7470_attrs[] = { + NULL + }; + +-ATTRIBUTE_GROUPS(adt7470); ++static const struct attribute_group adt7470_group = { ++ .attrs = adt7470_attrs, ++}; ++ ++static umode_t adt7470_pwm_is_visible(struct kobject *kobj, ++ struct attribute *attr, int index) ++{ ++ struct device *dev = kobj_to_dev(kobj); ++ struct adt7470_data *data = dev_get_drvdata(dev); ++ ++ if (data->use_pwm_framework) ++ return 0; ++ ++ return attr->mode; ++} ++ ++static const struct attribute_group adt7470_pwm_group = { ++ .attrs = adt7470_pwm_attrs, ++ .is_visible = adt7470_pwm_is_visible, ++}; ++ ++static const struct attribute_group *adt7470_groups[] = { ++ &adt7470_group, ++ &adt7470_pwm_group, ++ NULL, ++}; + + static int adt7470_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long *val) +@@ -1165,6 +1326,7 @@ static int adt7470_write(struct device *dev, enum hwmon_sensor_types type, u32 a + static umode_t adt7470_is_visible(const void *_data, enum hwmon_sensor_types type, + u32 attr, int channel) + { ++ const struct adt7470_data *data = _data; + umode_t mode = 0; + + switch (type) { +@@ -1197,6 +1359,14 @@ static umode_t adt7470_is_visible(const void *_data, enum hwmon_sensor_types typ + } + break; + case hwmon_pwm: ++ /* Hide all pwm attributes if this device is exposed to the PWM ++ * framework ++ */ ++ if (data->use_pwm_framework) { ++ mode = 0; ++ break; ++ } ++ + switch (attr) { + case hwmon_pwm_input: + case hwmon_pwm_enable: +@@ -1226,6 +1396,8 @@ static const struct hwmon_ops adt7470_hwmon_ops = { + }; + + static const struct hwmon_channel_info * const adt7470_info[] = { ++ HWMON_CHANNEL_INFO(chip, ++ HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, +@@ -1327,6 +1499,29 @@ static int adt7470_probe(struct i2c_client *client) + + data->pwm_freq = (u32)freq_val; + ++ data->use_pwm_framework = false; ++ ++ if (device_property_present(dev, "#pwm-cells")) { ++ if (IS_REACHABLE(CONFIG_PWM)) { ++ struct pwm_chip *chip; ++ ++ chip = devm_pwmchip_alloc(dev, ADT7470_PWM_COUNT, 0); ++ if (IS_ERR(chip)) ++ return PTR_ERR(chip); ++ ++ chip->ops = &adt7470_pwm_ops; ++ pwmchip_set_drvdata(chip, data); ++ ++ err = devm_pwmchip_add(dev, chip); ++ if (err) ++ return dev_err_probe(dev, err, "failed to register PWM chip\n"); ++ ++ data->use_pwm_framework = true; ++ } else { ++ dev_warn(dev, "#pwm-cells present but CONFIG_PWM disabled. HWMON PWM attributes not hidden.\n"); ++ } ++ } ++ + /* Register sysfs hooks */ + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + &adt7470_chip_info, +diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c +--- a/drivers/hwmon/applesmc.c ++++ b/drivers/hwmon/applesmc.c +@@ -33,6 +33,7 @@ + #include + #include + #include ++#include + + /* data port used by Apple SMC */ + #define APPLESMC_DATA_PORT 0x300 +@@ -133,6 +134,7 @@ static struct applesmc_registers { + bool init_complete; /* true when fully initialized */ + struct applesmc_entry *cache; /* cached key entries */ + const char **index; /* temperature key index */ ++ char fan_positions[10][17]; /* cached fan position labels */ + } smcreg = { + .mutex = __MUTEX_INITIALIZER(smcreg.mutex), + }; +@@ -143,8 +145,8 @@ static s16 rest_x; + static s16 rest_y; + static u8 backlight_state[2]; + +-static struct device *hwmon_dev; + static struct input_dev *applesmc_idev; ++static struct device *hwmon_dev; + + /* + * Last index written to key_at_index sysfs file, and value to use for all other +@@ -372,7 +374,8 @@ static const struct applesmc_entry *applesmc_get_entry_by_index(int index) + __be32 be; + int ret = 0; + +- if (cache->valid) ++ /* Pairs with smp_store_release() to ensure cache contents are visible */ ++ if (smp_load_acquire(&cache->valid)) + return cache; + + mutex_lock(&smcreg.mutex); +@@ -391,7 +394,8 @@ static const struct applesmc_entry *applesmc_get_entry_by_index(int index) + cache->len = info[0]; + memcpy(cache->type, &info[1], 4); + cache->flags = info[5]; +- cache->valid = true; ++ /* Pairs with smp_load_acquire() to commit cache contents before setting valid */ ++ smp_store_release(&cache->valid, true); + + out: + mutex_unlock(&smcreg.mutex); +@@ -566,7 +570,7 @@ static int applesmc_init_smcreg_try(void) + { + struct applesmc_registers *s = &smcreg; + bool left_light_sensor = false, right_light_sensor = false; +- unsigned int count; ++ unsigned int count, i; + u8 tmp[1]; + int ret; + +@@ -597,6 +601,16 @@ static int applesmc_init_smcreg_try(void) + if (s->fan_count > 10) + s->fan_count = 10; + ++ for (i = 0; i < s->fan_count; i++) { ++ char newkey[5]; ++ ++ scnprintf(newkey, sizeof(newkey), FAN_ID_FMT, i); ++ ret = applesmc_read_key(newkey, s->fan_positions[i], 16); ++ s->fan_positions[i][16] = 0; ++ if (ret) ++ scnprintf(s->fan_positions[i], 17, " Fan %d", i); ++ } ++ + ret = applesmc_get_lower_bound(&s->temp_begin, "T"); + if (ret) + return ret; +@@ -808,33 +822,6 @@ static ssize_t applesmc_light_show(struct device *dev, + return sysfs_emit(sysfsbuf, "(%d,%d)\n", left, right); + } + +-/* Displays sensor key as label */ +-static ssize_t applesmc_show_sensor_label(struct device *dev, +- struct device_attribute *devattr, char *sysfsbuf) +-{ +- const char *key = smcreg.index[to_index(devattr)]; +- +- return sysfs_emit(sysfsbuf, "%s\n", key); +-} +- +-/* Displays degree Celsius * 1000 */ +-static ssize_t applesmc_show_temperature(struct device *dev, +- struct device_attribute *devattr, char *sysfsbuf) +-{ +- const char *key = smcreg.index[to_index(devattr)]; +- int ret; +- s16 value; +- int temp; +- +- ret = applesmc_read_s16(key, &value); +- if (ret) +- return ret; +- +- temp = 250 * (value >> 6); +- +- return sysfs_emit(sysfsbuf, "%d\n", temp); +-} +- + static ssize_t applesmc_show_fan_speed(struct device *dev, + struct device_attribute *attr, char *sysfsbuf) + { +@@ -854,99 +841,6 @@ static ssize_t applesmc_show_fan_speed(struct device *dev, + return sysfs_emit(sysfsbuf, "%u\n", speed); + } + +-static ssize_t applesmc_store_fan_speed(struct device *dev, +- struct device_attribute *attr, +- const char *sysfsbuf, size_t count) +-{ +- int ret; +- unsigned long speed; +- char newkey[5]; +- u8 buffer[2]; +- +- if (kstrtoul(sysfsbuf, 10, &speed) < 0 || speed >= 0x4000) +- return -EINVAL; /* Bigger than a 14-bit value */ +- +- scnprintf(newkey, sizeof(newkey), fan_speed_fmt[to_option(attr)], +- to_index(attr)); +- +- buffer[0] = (speed >> 6) & 0xff; +- buffer[1] = (speed << 2) & 0xff; +- ret = applesmc_write_key(newkey, buffer, 2); +- +- if (ret) +- return ret; +- else +- return count; +-} +- +-static ssize_t applesmc_show_fan_manual(struct device *dev, +- struct device_attribute *attr, char *sysfsbuf) +-{ +- int ret; +- u16 manual = 0; +- u8 buffer[2]; +- +- ret = applesmc_read_key(FANS_MANUAL, buffer, 2); +- if (ret) +- return ret; +- +- manual = ((buffer[0] << 8 | buffer[1]) >> to_index(attr)) & 0x01; +- return sysfs_emit(sysfsbuf, "%d\n", manual); +-} +- +-static ssize_t applesmc_store_fan_manual(struct device *dev, +- struct device_attribute *attr, +- const char *sysfsbuf, size_t count) +-{ +- int ret; +- u8 buffer[2]; +- unsigned long input; +- u16 val; +- +- if (kstrtoul(sysfsbuf, 10, &input) < 0) +- return -EINVAL; +- +- ret = applesmc_read_key(FANS_MANUAL, buffer, 2); +- if (ret) +- goto out; +- +- val = (buffer[0] << 8 | buffer[1]); +- +- if (input) +- val = val | (0x01 << to_index(attr)); +- else +- val = val & ~(0x01 << to_index(attr)); +- +- buffer[0] = (val >> 8) & 0xFF; +- buffer[1] = val & 0xFF; +- +- ret = applesmc_write_key(FANS_MANUAL, buffer, 2); +- +-out: +- if (ret) +- return ret; +- else +- return count; +-} +- +-static ssize_t applesmc_show_fan_position(struct device *dev, +- struct device_attribute *attr, char *sysfsbuf) +-{ +- int ret; +- char newkey[5]; +- u8 buffer[17]; +- +- scnprintf(newkey, sizeof(newkey), FAN_ID_FMT, to_index(attr)); +- +- ret = applesmc_read_key(newkey, buffer, 16); +- buffer[16] = 0; +- +- if (ret) +- return ret; +- +- return sysfs_emit(sysfsbuf, "%s\n", buffer + 4); +-} +- + static ssize_t applesmc_calibrate_show(struct device *dev, + struct device_attribute *attr, char *sysfsbuf) + { +@@ -1094,22 +988,7 @@ static struct applesmc_node_group light_sensor_group[] = { + { } + }; + +-static struct applesmc_node_group fan_group[] = { +- { "fan%d_label", applesmc_show_fan_position }, +- { "fan%d_input", applesmc_show_fan_speed, NULL, 0 }, +- { "fan%d_min", applesmc_show_fan_speed, applesmc_store_fan_speed, 1 }, +- { "fan%d_max", applesmc_show_fan_speed, NULL, 2 }, +- { "fan%d_safe", applesmc_show_fan_speed, NULL, 3 }, +- { "fan%d_output", applesmc_show_fan_speed, applesmc_store_fan_speed, 4 }, +- { "fan%d_manual", applesmc_show_fan_manual, applesmc_store_fan_manual }, +- { } +-}; + +-static struct applesmc_node_group temp_group[] = { +- { "temp%d_label", applesmc_show_sensor_label }, +- { "temp%d_input", applesmc_show_temperature }, +- { } +-}; + + /* Module stuff */ + +@@ -1312,8 +1191,238 @@ static const struct dmi_system_id applesmc_whitelist[] __initconst = { + }; + MODULE_DEVICE_TABLE(dmi, applesmc_whitelist); + ++static struct applesmc_dev_attr *fan_safe_attrs; ++static struct attribute **fan_safe_attr_list; ++static struct attribute_group fan_safe_group; ++static const struct attribute_group *applesmc_extra_groups[2]; ++ ++static u32 *applesmc_temp_config; ++static u32 *applesmc_fan_config; ++static u32 *applesmc_pwm_config; ++static struct hwmon_channel_info *applesmc_info_temp; ++static struct hwmon_channel_info *applesmc_info_fan; ++static struct hwmon_channel_info *applesmc_info_pwm; ++static const struct hwmon_channel_info **applesmc_info_arr; ++static struct hwmon_chip_info *applesmc_chip; ++ ++static void applesmc_free_hwmon(void) ++{ ++ kfree(applesmc_temp_config); ++ kfree(applesmc_fan_config); ++ kfree(applesmc_pwm_config); ++ kfree(applesmc_info_temp); ++ kfree(applesmc_info_fan); ++ kfree(applesmc_info_pwm); ++ kfree(applesmc_info_arr); ++ kfree(applesmc_chip); ++ kfree(fan_safe_attrs); ++ kfree(fan_safe_attr_list); ++} ++ ++static umode_t applesmc_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type, ++ u32 attr, int channel) ++{ ++ switch (type) { ++ case hwmon_temp: ++ if (attr == hwmon_temp_input || attr == hwmon_temp_label) ++ return 0444; ++ break; ++ case hwmon_fan: ++ switch (attr) { ++ case hwmon_fan_input: ++ case hwmon_fan_label: ++ case hwmon_fan_max: ++ return 0444; ++ case hwmon_fan_min: ++ case hwmon_fan_target: ++ return 0644; ++ default: ++ break; ++ } ++ break; ++ case hwmon_pwm: ++ if (attr == hwmon_pwm_enable) ++ return 0644; ++ break; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++static int applesmc_hwmon_read(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, long *val) ++{ ++ int ret; ++ ++ switch (type) { ++ case hwmon_temp: ++ if (attr == hwmon_temp_input) { ++ const char *key = smcreg.index[channel]; ++ s16 value; ++ ++ ret = applesmc_read_s16(key, &value); ++ if (ret) ++ return ret; ++ *val = 250 * (value >> 6); ++ return 0; ++ } ++ break; ++ case hwmon_fan: ++ switch (attr) { ++ case hwmon_fan_input: { ++ char key[5]; ++ u8 buffer[2]; ++ ++ scnprintf(key, sizeof(key), "F%dAc", channel); ++ ret = applesmc_read_key(key, buffer, 2); ++ if (ret) ++ return ret; ++ *val = ((buffer[0] << 8 | buffer[1]) >> 2); ++ return 0; ++ } ++ case hwmon_fan_min: { ++ char key[5]; ++ u8 buffer[2]; ++ ++ scnprintf(key, sizeof(key), "F%dMn", channel); ++ ret = applesmc_read_key(key, buffer, 2); ++ if (ret) ++ return ret; ++ *val = ((buffer[0] << 8 | buffer[1]) >> 2); ++ return 0; ++ } ++ case hwmon_fan_max: { ++ char key[5]; ++ u8 buffer[2]; ++ ++ scnprintf(key, sizeof(key), "F%dMx", channel); ++ ret = applesmc_read_key(key, buffer, 2); ++ if (ret) ++ return ret; ++ *val = ((buffer[0] << 8 | buffer[1]) >> 2); ++ return 0; ++ } ++ case hwmon_fan_target: { ++ char key[5]; ++ u8 buffer[2]; ++ ++ scnprintf(key, sizeof(key), "F%dTg", channel); ++ ret = applesmc_read_key(key, buffer, 2); ++ if (ret) ++ return ret; ++ *val = ((buffer[0] << 8 | buffer[1]) >> 2); ++ return 0; ++ } ++ default: ++ break; ++ } ++ break; ++ case hwmon_pwm: ++ if (attr == hwmon_pwm_enable) { ++ u8 buffer[2]; ++ ++ ret = applesmc_read_key(FANS_MANUAL, buffer, 2); ++ if (ret) ++ return ret; ++ *val = (((buffer[0] << 8 | buffer[1]) >> channel) & 0x01) ? 1 : 2; ++ return 0; ++ } ++ break; ++ default: ++ break; ++ } ++ return -EOPNOTSUPP; ++} ++ ++static int applesmc_hwmon_write(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, long val) ++{ ++ int ret; ++ ++ switch (type) { ++ case hwmon_fan: ++ if (attr == hwmon_fan_min || attr == hwmon_fan_target) { ++ char key[5]; ++ u8 buffer[2]; ++ const char *fmt = (attr == hwmon_fan_min) ? "F%dMn" : "F%dTg"; ++ ++ if (val < 0 || val >= 0x4000) ++ return -EINVAL; ++ scnprintf(key, sizeof(key), fmt, channel); ++ buffer[0] = (val >> 6) & 0xff; ++ buffer[1] = (val << 2) & 0xff; ++ return applesmc_write_key(key, buffer, 2); ++ } ++ break; ++ case hwmon_pwm: ++ if (attr == hwmon_pwm_enable) { ++ u8 buffer[2]; ++ u16 manual_val; ++ const struct applesmc_entry *entry; ++ ++ if (val != 1 && val != 2) ++ return -EINVAL; ++ ++ entry = applesmc_get_entry_by_key(FANS_MANUAL); ++ if (IS_ERR(entry)) ++ return PTR_ERR(entry); ++ ++ mutex_lock(&smcreg.mutex); ++ ret = read_smc(APPLESMC_READ_CMD, entry->key, buffer, 2); ++ if (ret) ++ goto out_unlock; ++ manual_val = (buffer[0] << 8 | buffer[1]); ++ if (val == 1) ++ manual_val |= (0x01 << channel); ++ else ++ manual_val &= ~(0x01 << channel); ++ buffer[0] = (manual_val >> 8) & 0xff; ++ buffer[1] = manual_val & 0xff; ++ ret = write_smc(APPLESMC_WRITE_CMD, entry->key, buffer, 2); ++out_unlock: ++ mutex_unlock(&smcreg.mutex); ++ return ret; ++ } ++ break; ++ default: ++ break; ++ } ++ return -EOPNOTSUPP; ++} ++ ++static int applesmc_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, const char **str) ++{ ++ switch (type) { ++ case hwmon_temp: ++ if (attr == hwmon_temp_label) { ++ *str = smcreg.index[channel]; ++ return 0; ++ } ++ break; ++ case hwmon_fan: ++ if (attr == hwmon_fan_label) { ++ *str = smcreg.fan_positions[channel] + 4; ++ return 0; ++ } ++ break; ++ default: ++ break; ++ } ++ return -EOPNOTSUPP; ++} ++ ++static const struct hwmon_ops applesmc_hwmon_ops = { ++ .is_visible = applesmc_hwmon_is_visible, ++ .read = applesmc_hwmon_read, ++ .write = applesmc_hwmon_write, ++ .read_string = applesmc_hwmon_read_string, ++}; ++ + static int __init applesmc_init(void) + { ++ int i; + int ret; + + if (!dmi_check_system(applesmc_whitelist)) { +@@ -1348,17 +1457,97 @@ static int __init applesmc_init(void) + if (ret) + goto out_smcreg; + +- ret = applesmc_create_nodes(fan_group, smcreg.fan_count); +- if (ret) ++ /* allocate hwmon channel configs */ ++ applesmc_temp_config = kcalloc(smcreg.index_count + 1, ++ sizeof(*applesmc_temp_config), GFP_KERNEL); ++ applesmc_fan_config = kcalloc(smcreg.fan_count + 1, ++ sizeof(*applesmc_fan_config), GFP_KERNEL); ++ applesmc_pwm_config = kcalloc(smcreg.fan_count + 1, ++ sizeof(*applesmc_pwm_config), GFP_KERNEL); ++ if (!applesmc_temp_config || !applesmc_fan_config || !applesmc_pwm_config) { ++ ret = -ENOMEM; + goto out_info; ++ } + +- ret = applesmc_create_nodes(temp_group, smcreg.index_count); +- if (ret) +- goto out_fans; ++ for (i = 0; i < smcreg.index_count; i++) ++ applesmc_temp_config[i] = HWMON_T_INPUT | HWMON_T_LABEL; ++ applesmc_temp_config[smcreg.index_count] = 0; ++ ++ for (i = 0; i < smcreg.fan_count; i++) { ++ applesmc_fan_config[i] = HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | ++ HWMON_F_MAX | HWMON_F_TARGET; ++ applesmc_pwm_config[i] = HWMON_PWM_ENABLE; ++ } ++ applesmc_fan_config[smcreg.fan_count] = 0; ++ applesmc_pwm_config[smcreg.fan_count] = 0; ++ ++ applesmc_info_temp = kzalloc_obj(*applesmc_info_temp, GFP_KERNEL); ++ applesmc_info_fan = kzalloc_obj(*applesmc_info_fan, GFP_KERNEL); ++ applesmc_info_pwm = kzalloc_obj(*applesmc_info_pwm, GFP_KERNEL); ++ if (!applesmc_info_temp || !applesmc_info_fan || !applesmc_info_pwm) { ++ ret = -ENOMEM; ++ goto out_info; ++ } ++ ++ applesmc_info_temp->type = hwmon_temp; ++ applesmc_info_temp->config = applesmc_temp_config; ++ ++ applesmc_info_fan->type = hwmon_fan; ++ applesmc_info_fan->config = applesmc_fan_config; ++ ++ applesmc_info_pwm->type = hwmon_pwm; ++ applesmc_info_pwm->config = applesmc_pwm_config; ++ ++ applesmc_info_arr = kcalloc(4, sizeof(*applesmc_info_arr), GFP_KERNEL); ++ if (!applesmc_info_arr) { ++ ret = -ENOMEM; ++ goto out_info; ++ } ++ ++ applesmc_info_arr[0] = applesmc_info_temp; ++ applesmc_info_arr[1] = applesmc_info_fan; ++ applesmc_info_arr[2] = applesmc_info_pwm; ++ applesmc_info_arr[3] = NULL; ++ ++ applesmc_chip = kzalloc_obj(*applesmc_chip, GFP_KERNEL); ++ if (!applesmc_chip) { ++ ret = -ENOMEM; ++ goto out_info; ++ } ++ ++ applesmc_chip->ops = &applesmc_hwmon_ops; ++ applesmc_chip->info = applesmc_info_arr; ++ ++ /* Create non-standard fanX_safe attributes group */ ++ fan_safe_attrs = kcalloc(smcreg.fan_count, ++ sizeof(*fan_safe_attrs), GFP_KERNEL); ++ fan_safe_attr_list = kcalloc(smcreg.fan_count + 1, ++ sizeof(*fan_safe_attr_list), GFP_KERNEL); ++ if (!fan_safe_attrs || !fan_safe_attr_list) { ++ ret = -ENOMEM; ++ goto out_info; ++ } ++ ++ for (i = 0; i < smcreg.fan_count; i++) { ++ struct applesmc_dev_attr *node = &fan_safe_attrs[i]; ++ ++ scnprintf(node->name, sizeof(node->name), "fan%d_safe", i + 1); ++ node->sda.index = (3 << 16) | (i & 0xffff); /* Option 3 (safe speed) */ ++ node->sda.dev_attr.show = applesmc_show_fan_speed; ++ node->sda.dev_attr.store = NULL; ++ sysfs_attr_init(&node->sda.dev_attr.attr); ++ node->sda.dev_attr.attr.name = node->name; ++ node->sda.dev_attr.attr.mode = 0444; ++ fan_safe_attr_list[i] = &node->sda.dev_attr.attr; ++ } ++ fan_safe_attr_list[smcreg.fan_count] = NULL; ++ fan_safe_group.attrs = fan_safe_attr_list; ++ applesmc_extra_groups[0] = &fan_safe_group; ++ applesmc_extra_groups[1] = NULL; + + ret = applesmc_create_accelerometer(); + if (ret) +- goto out_temperature; ++ goto out_info; + + ret = applesmc_create_light_sensor(); + if (ret) +@@ -1368,7 +1557,8 @@ static int __init applesmc_init(void) + if (ret) + goto out_light_sysfs; + +- hwmon_dev = hwmon_device_register(&pdev->dev); ++ hwmon_dev = hwmon_device_register_with_info(&pdev->dev, "applesmc", NULL, ++ applesmc_chip, applesmc_extra_groups); + if (IS_ERR(hwmon_dev)) { + ret = PTR_ERR(hwmon_dev); + goto out_light_ledclass; +@@ -1382,11 +1572,8 @@ static int __init applesmc_init(void) + applesmc_release_light_sensor(); + out_accelerometer: + applesmc_release_accelerometer(); +-out_temperature: +- applesmc_destroy_nodes(temp_group); +-out_fans: +- applesmc_destroy_nodes(fan_group); + out_info: ++ applesmc_free_hwmon(); + applesmc_destroy_nodes(info_group); + out_smcreg: + applesmc_destroy_smcreg(); +@@ -1407,13 +1594,12 @@ static void __exit applesmc_exit(void) + applesmc_release_key_backlight(); + applesmc_release_light_sensor(); + applesmc_release_accelerometer(); +- applesmc_destroy_nodes(temp_group); +- applesmc_destroy_nodes(fan_group); + applesmc_destroy_nodes(info_group); + applesmc_destroy_smcreg(); + platform_device_unregister(pdev); + platform_driver_unregister(&applesmc_driver); + release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS); ++ applesmc_free_hwmon(); + } + + module_init(applesmc_init); +diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c +--- a/drivers/hwmon/asus-ec-sensors.c ++++ b/drivers/hwmon/asus-ec-sensors.c +@@ -185,6 +185,20 @@ enum ec_sensors { + #define SENSOR_TEMP_SENSOR_EXTRA_2 BIT(ec_sensor_temp_sensor_extra_2) + #define SENSOR_TEMP_SENSOR_EXTRA_3 BIT(ec_sensor_temp_sensor_extra_3) + ++/* ++ * The values for temperature sensor readings without physical sensors connected. ++ * The value varies across generations and is seemingly defined by the EC chip ++ * used in the given board. ++ */ ++static const s32 temperature_blank_values[] = {-62, -60, -40}; ++ ++static const s32 environment_temp_sensors = ++ SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_T_SENSOR_ALT1 | ++ SENSOR_TEMP_WATER_IN | SENSOR_TEMP_WATER_OUT | ++ SENSOR_TEMP_WATER_BLOCK_IN | SENSOR_TEMP_WATER_BLOCK_OUT | ++ SENSOR_TEMP_T_SENSOR_2 | SENSOR_TEMP_SENSOR_EXTRA_1 | ++ SENSOR_TEMP_SENSOR_EXTRA_2 | SENSOR_TEMP_SENSOR_EXTRA_3; ++ + enum board_family { + family_unknown, + family_amd_400_series, +@@ -386,6 +400,8 @@ static const struct ec_sensor_info sensors_family_intel_600[] = { + [ec_sensor_temp_t_sensor] = + EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d), + [ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e), ++ [ec_sensor_fan_cpu_opt] = ++ EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0), + [ec_sensor_fan_water_flow] = + EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xbe), + [ec_sensor_temp_water_in] = +@@ -487,6 +503,14 @@ static const struct ec_board_info board_info_crosshair_x670e_hero = { + .family = family_amd_600_series, + }; + ++static const struct ec_board_info board_info_crosshair_x870e_hero = { ++ .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | ++ SENSOR_TEMP_MB | SENSOR_TEMP_VRM | ++ SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CPU_OPT, ++ .mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0, ++ .family = family_amd_800_series, ++}; ++ + static const struct ec_board_info board_info_maximus_vi_hero = { + .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | + SENSOR_TEMP_T_SENSOR | +@@ -527,6 +551,14 @@ static const struct ec_board_info board_info_maximus_z790_extreme = { + .family = family_intel_700_series, + }; + ++static const struct ec_board_info board_info_maximus_z790_hero = { ++ .sensors = SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM | ++ SENSOR_SET_TEMP_WATER | SENSOR_FAN_WATER_FLOW | ++ SENSOR_FAN_CPU_OPT, ++ .mutex_path = ASUS_HW_ACCESS_MUTEX_RMTW_ASMX, ++ .family = family_intel_700_series, ++}; ++ + static const struct ec_board_info board_info_prime_x470_pro = { + .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | + SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM | +@@ -590,6 +622,13 @@ static const struct ec_board_info board_info_pro_art_x870E_creator_wifi = { + .family = family_amd_800_series, + }; + ++static const struct ec_board_info board_info_pro_art_z690_creator_wifi = { ++ .sensors = SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM | ++ SENSOR_FAN_CPU_OPT, ++ .mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PC00_LPCB_SIO1_MUT0, ++ .family = family_intel_600_series, ++}; ++ + static const struct ec_board_info board_info_pro_ws_trx50_sage_wifi = { + /* Board also has a nct6798 */ + .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | SENSOR_TEMP_VRME | +@@ -743,7 +782,7 @@ static const struct ec_board_info board_info_strix_x870_i_gaming_wifi = { + + static const struct ec_board_info board_info_strix_x870e_e_gaming_wifi = { + .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | +- SENSOR_TEMP_MB | SENSOR_TEMP_VRM | ++ SENSOR_TEMP_MB | SENSOR_TEMP_VRM | SENSOR_TEMP_T_SENSOR | + SENSOR_FAN_CPU_OPT, + .mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0, + .family = family_amd_800_series, +@@ -859,6 +898,8 @@ static const struct dmi_system_id dmi_table[] = { + &board_info_pro_art_x670E_creator_wifi), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt X870E-CREATOR WIFI", + &board_info_pro_art_x870E_creator_wifi), ++ DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt Z690-CREATOR WIFI", ++ &board_info_pro_art_z690_creator_wifi), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("Pro WS TRX50-SAGE WIFI", + &board_info_pro_ws_trx50_sage_wifi), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("Pro WS TRX50-SAGE WIFI A", +@@ -883,6 +924,8 @@ static const struct dmi_system_id dmi_table[] = { + &board_info_crosshair_x670e_gene), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR X670E HERO", + &board_info_crosshair_x670e_hero), ++ DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR X870E HERO", ++ &board_info_crosshair_x870e_hero), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS XI HERO", + &board_info_maximus_xi_hero), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS XI HERO (WI-FI)", +@@ -893,6 +936,8 @@ static const struct dmi_system_id dmi_table[] = { + &board_info_maximus_z690_formula), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS Z790 EXTREME", + &board_info_maximus_z790_extreme), ++ DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS Z790 HERO", ++ &board_info_maximus_z790_hero), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-E GAMING", + &board_info_strix_b550_e_gaming), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-I GAMING", +@@ -927,8 +972,12 @@ static const struct dmi_system_id dmi_table[] = { + &board_info_strix_x870_i_gaming_wifi), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X870E-E GAMING WIFI", + &board_info_strix_x870e_e_gaming_wifi), ++ DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X870E-E GAMING WIFI7 R2", ++ &board_info_strix_x870e_e_gaming_wifi), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X870E-H GAMING WIFI7", + &board_info_strix_x870e_h_gaming_wifi7), ++ DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z390-E GAMING", ++ &board_info_strix_z390_f_gaming), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z390-F GAMING", + &board_info_strix_z390_f_gaming), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z490-F GAMING", +@@ -955,6 +1004,7 @@ static const struct dmi_system_id dmi_table[] = { + }; + + struct ec_sensor { ++ /* this is ec_sensors enum value */ + unsigned int info_index; + s32 cached_value; + }; +@@ -1047,6 +1097,12 @@ get_sensor_info(const struct ec_sensors_data *state, int index) + return state->sensors_info + state->sensors[index].info_index; + } + ++static enum ec_sensors ++get_ec_sensor_type(const struct ec_sensors_data *state, int index) ++{ ++ return state->sensors[index].info_index; ++} ++ + static int find_ec_sensor_index(const struct ec_sensors_data *ec, + enum hwmon_sensor_types type, int channel) + { +@@ -1290,6 +1346,17 @@ static int get_cached_value_or_update(const struct device *dev, + return 0; + } + ++static bool is_blank_temperature_value(s32 value) ++{ ++ size_t i; ++ ++ for (i = 0; i < ARRAY_SIZE(temperature_blank_values); ++i) { ++ if (value == temperature_blank_values[i]) ++ return true; ++ } ++ return false; ++} ++ + /* + * Now follow the functions that implement the hwmon interface + */ +@@ -1297,6 +1364,8 @@ static int get_cached_value_or_update(const struct device *dev, + static int asus_ec_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) + { ++ const struct ec_sensor_info *sensor_info; ++ enum ec_sensors ec_sensor; + int ret; + s32 value = 0; + +@@ -1308,12 +1377,19 @@ static int asus_ec_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + } + + ret = get_cached_value_or_update(dev, sidx, state, &value); +- if (!ret) { +- *val = scale_sensor_value(value, +- get_sensor_info(state, sidx)->type); +- } ++ if (ret) ++ return ret; + +- return ret; ++ sensor_info = get_sensor_info(state, sidx); ++ if (sensor_info->type == hwmon_temp) { ++ ec_sensor = get_ec_sensor_type(state, sidx); ++ if ((environment_temp_sensors & BIT(ec_sensor)) && ++ is_blank_temperature_value(value)) ++ return -ENODATA; ++ } ++ *val = scale_sensor_value(value, sensor_info->type); ++ ++ return 0; + } + + static int asus_ec_hwmon_read_string(struct device *dev, +diff --git a/drivers/hwmon/asus_rog_ryujin.c b/drivers/hwmon/asus_rog_ryujin.c +--- a/drivers/hwmon/asus_rog_ryujin.c ++++ b/drivers/hwmon/asus_rog_ryujin.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0+ + /* +- * hwmon driver for Asus ROG Ryujin II 360 AIO cooler. ++ * hwmon driver for Asus ROG Ryujin AIO coolers. + * + * Copyright 2024 Aleksa Savic + */ +@@ -17,6 +17,9 @@ + + #define USB_VENDOR_ID_ASUS_ROG 0x0b05 + #define USB_PRODUCT_ID_RYUJIN_AIO 0x1988 /* ASUS ROG RYUJIN II 360 */ ++#define USB_PRODUCT_ID_RYUJIN_III_EXTREME 0x1bcb ++#define USB_PRODUCT_ID_RYUJIN_III_EVA 0x1ade ++#define USB_PRODUCT_ID_RYUJIN_III_WHITE 0x1ada + + struct rog_ryujin_device_info { + u8 temp_offset; +@@ -34,6 +37,14 @@ static const struct rog_ryujin_device_info rog_ryujin_ii_360_info = { + .has_controller = true, + }; + ++static const struct rog_ryujin_device_info rog_ryujin_iii_info = { ++ .temp_offset = 5, ++ .pump_speed_offset = 7, ++ .fan_speed_offset = 10, ++ .duty_channel = 1, ++ .has_controller = false, ++}; ++ + #define STATUS_VALIDITY 1500 /* ms */ + #define MAX_REPORT_LENGTH 65 + +@@ -173,7 +184,6 @@ static int rog_ryujin_write_expanded(struct rog_ryujin_data *priv, const u8 *cmd + static int rog_ryujin_execute_cmd(struct rog_ryujin_data *priv, const u8 *cmd, int cmd_length, + struct completion *status_completion) + { +- unsigned long flags; + int ret; + + /* +@@ -181,9 +191,9 @@ static int rog_ryujin_execute_cmd(struct rog_ryujin_data *priv, const u8 *cmd, i + * completion. Reinit is done because hidraw could have triggered + * the raw event parsing and marked the passed in completion as done. + */ +- spin_lock_irqsave(&priv->status_report_request_lock, flags); ++ spin_lock_bh(&priv->status_report_request_lock); + reinit_completion(status_completion); +- spin_unlock_irqrestore(&priv->status_report_request_lock, flags); ++ spin_unlock_bh(&priv->status_report_request_lock); + + /* Send command for getting data */ + ret = rog_ryujin_write_expanded(priv, cmd, cmd_length); +@@ -411,18 +421,15 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + int size) + { + struct rog_ryujin_data *priv = hid_get_drvdata(hdev); +- unsigned long flags; + + if (size < 2 || data[0] != RYUJIN_CMD_PREFIX) + return 0; + +- spin_lock_irqsave(&priv->status_report_request_lock, flags); +- + if (data[1] == RYUJIN_GET_COOLER_STATUS_CMD_RESPONSE) { + if (size <= priv->info->temp_offset + 1 || + size <= priv->info->pump_speed_offset + 1 || + size <= priv->info->fan_speed_offset + 1) +- goto unlock; ++ return 0; + + /* Received coolant temp and speeds of pump and internal fan */ + priv->temp_input[0] = data[priv->info->temp_offset] * 1000 + +@@ -436,7 +443,7 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + complete_all(&priv->cooler_status_received); + } else if (data[1] == RYUJIN_GET_CONTROLLER_SPEED_CMD_RESPONSE) { + if (size <= RYUJIN_CONTROLLER_SPEED_3 + 1) +- goto unlock; ++ return 0; + + /* Received speeds of four fans attached to the controller */ + priv->speed_input[2] = get_unaligned_le16(data + RYUJIN_CONTROLLER_SPEED_1); +@@ -448,7 +455,7 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + complete_all(&priv->controller_status_received); + } else if (data[1] == RYUJIN_GET_COOLER_DUTY_CMD_RESPONSE) { + if (size <= RYUJIN_INTERNAL_FAN_DUTY) +- goto unlock; ++ return 0; + + /* Received report for pump and internal fan duties (in %) */ + if (data[RYUJIN_PUMP_DUTY] == 0 && data[RYUJIN_INTERNAL_FAN_DUTY] == 0) { +@@ -467,7 +474,7 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + * We're expecting a report, so parse it. + */ + goto read_cooler_duty; +- goto unlock; ++ return 0; + } + read_cooler_duty: + priv->duty_input[0] = rog_ryujin_percent_to_pwm(data[RYUJIN_PUMP_DUTY]); +@@ -477,7 +484,7 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + complete_all(&priv->cooler_duty_received); + } else if (data[1] == RYUJIN_GET_CONTROLLER_DUTY_CMD_RESPONSE) { + if (size <= RYUJIN_CONTROLLER_DUTY) +- goto unlock; ++ return 0; + + /* Received report for controller duty for fans (in PWM) */ + if (data[RYUJIN_CONTROLLER_DUTY] == 0) { +@@ -496,7 +503,7 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + * We're expecting a report, so parse it. + */ + goto read_controller_duty; +- goto unlock; ++ return 0; + } + read_controller_duty: + priv->duty_input[2] = data[RYUJIN_CONTROLLER_DUTY]; +@@ -505,8 +512,6 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + complete_all(&priv->controller_duty_received); + } + +-unlock: +- spin_unlock_irqrestore(&priv->status_report_request_lock, flags); + return 0; + } + +@@ -596,6 +601,12 @@ static void rog_ryujin_remove(struct hid_device *hdev) + static const struct hid_device_id rog_ryujin_table[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ASUS_ROG, USB_PRODUCT_ID_RYUJIN_AIO), + .driver_data = (kernel_ulong_t)&rog_ryujin_ii_360_info }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ASUS_ROG, USB_PRODUCT_ID_RYUJIN_III_EXTREME), ++ .driver_data = (kernel_ulong_t)&rog_ryujin_iii_info }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ASUS_ROG, USB_PRODUCT_ID_RYUJIN_III_EVA), ++ .driver_data = (kernel_ulong_t)&rog_ryujin_iii_info }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ASUS_ROG, USB_PRODUCT_ID_RYUJIN_III_WHITE), ++ .driver_data = (kernel_ulong_t)&rog_ryujin_iii_info }, + { } + }; + +@@ -625,4 +636,4 @@ module_exit(rog_ryujin_exit); + + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Aleksa Savic "); +-MODULE_DESCRIPTION("Hwmon driver for Asus ROG Ryujin II 360 AIO cooler"); ++MODULE_DESCRIPTION("Hwmon driver for Asus ROG Ryujin AIO coolers"); +diff --git a/drivers/hwmon/axi-fan-control.c b/drivers/hwmon/axi-fan-control.c +--- a/drivers/hwmon/axi-fan-control.c ++++ b/drivers/hwmon/axi-fan-control.c +@@ -508,8 +508,7 @@ static int axi_fan_control_probe(struct platform_device *pdev) + IRQF_ONESHOT | IRQF_TRIGGER_HIGH, + NULL, ctl); + if (ret) +- return dev_err_probe(&pdev->dev, ret, +- "failed to request an irq\n"); ++ return ret; + + return 0; + } +diff --git a/drivers/hwmon/chipcap2.c b/drivers/hwmon/chipcap2.c +--- a/drivers/hwmon/chipcap2.c ++++ b/drivers/hwmon/chipcap2.c +@@ -22,6 +22,8 @@ + #include + #include + #include ++#include ++#include + + #define CC2_START_CM 0xA0 + #define CC2_START_NOM 0x80 +@@ -83,6 +85,7 @@ struct cc2_data { + struct i2c_client *client; + struct regulator *regulator; + const char *name; ++ const char *label; + int irq_ready; + int irq_low; + int irq_high; +@@ -444,6 +447,8 @@ static umode_t cc2_is_visible(const void *data, enum hwmon_sensor_types type, + switch (attr) { + case hwmon_humidity_input: + return 0444; ++ case hwmon_humidity_label: ++ return cc2->label ? 0444 : 0; + case hwmon_humidity_min_alarm: + return cc2->rh_alarm.low_alarm_visible ? 0444 : 0; + case hwmon_humidity_max_alarm: +@@ -461,6 +466,8 @@ static umode_t cc2_is_visible(const void *data, enum hwmon_sensor_types type, + switch (attr) { + case hwmon_temp_input: + return 0444; ++ case hwmon_temp_label: ++ return cc2->label ? 0444 : 0; + default: + return 0; + } +@@ -547,6 +554,16 @@ static int cc2_humidity_max_alarm_status(struct cc2_data *data, long *val) + return 0; + } + ++static int cc2_read_string(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, const char **str) ++{ ++ struct cc2_data *data = dev_get_drvdata(dev); ++ ++ *str = data->label; ++ ++ return 0; ++} ++ + static int cc2_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long *val) + { +@@ -665,8 +682,9 @@ static int cc2_request_alarm_irqs(struct cc2_data *data, struct device *dev) + } + + static const struct hwmon_channel_info *cc2_info[] = { +- HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), +- HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT | HWMON_H_MIN | HWMON_H_MAX | ++ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL), ++ HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT | HWMON_H_LABEL | ++ HWMON_H_MIN | HWMON_H_MAX | + HWMON_H_MIN_HYST | HWMON_H_MAX_HYST | + HWMON_H_MIN_ALARM | HWMON_H_MAX_ALARM), + NULL +@@ -675,6 +693,7 @@ static const struct hwmon_channel_info *cc2_info[] = { + static const struct hwmon_ops cc2_hwmon_ops = { + .is_visible = cc2_is_visible, + .read = cc2_read, ++ .read_string = cc2_read_string, + .write = cc2_write, + }; + +@@ -705,6 +724,8 @@ static int cc2_probe(struct i2c_client *client) + return dev_err_probe(dev, PTR_ERR(data->regulator), + "Failed to get regulator\n"); + ++ device_property_read_string(dev, "label", &data->label); ++ + ret = cc2_request_ready_irq(data, dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to request ready irq\n"); +diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c +--- a/drivers/hwmon/coretemp.c ++++ b/drivers/hwmon/coretemp.c +@@ -68,7 +68,7 @@ enum coretemp_attr_index { + * This value is passed as "id" field to rdmsr/wrmsr functions. + * @status_reg: One of IA32_THERM_STATUS or IA32_PACKAGE_THERM_STATUS, + * from where the temperature values should be read. +- * @attr_size: Total number of pre-core attrs displayed in the sysfs. ++ * @attr_size: Number of sysfs attributes in this temperature data group. + */ + struct temp_data { + int temp; +diff --git a/drivers/hwmon/corsair-psu.c b/drivers/hwmon/corsair-psu.c +--- a/drivers/hwmon/corsair-psu.c ++++ b/drivers/hwmon/corsair-psu.c +@@ -884,7 +884,7 @@ static const struct hid_device_id corsairpsu_idtable[] = { + { HID_USB_DEVICE(0x1b1c, 0x1c0c) }, /* Corsair RM850i */ + { HID_USB_DEVICE(0x1b1c, 0x1c0d) }, /* Corsair RM1000i */ + { HID_USB_DEVICE(0x1b1c, 0x1c1e) }, /* Corsair HX1000i Series 2023 */ +- { HID_USB_DEVICE(0x1b1c, 0x1c1f) }, /* Corsair HX1500i Legacy and Series 2023 */ ++ { HID_USB_DEVICE(0x1b1c, 0x1c1f) }, /* Corsair HX1500i Legacy, Series 2023 and 2025 */ + { HID_USB_DEVICE(0x1b1c, 0x1c23) }, /* Corsair HX1200i Series 2023 */ + { HID_USB_DEVICE(0x1b1c, 0x1c27) }, /* Corsair HX1200i Series 2025 */ + { }, +diff --git a/drivers/hwmon/cros_ec_hwmon.c b/drivers/hwmon/cros_ec_hwmon.c +--- a/drivers/hwmon/cros_ec_hwmon.c ++++ b/drivers/hwmon/cros_ec_hwmon.c +@@ -148,9 +148,17 @@ static bool cros_ec_hwmon_is_error_temp(u8 temp) + temp == EC_TEMP_SENSOR_NOT_CALIBRATED; + } + ++/* This differs slightly from the variant in units.h to avoid rounding inconsistencies. */ ++#define CROS_EC_HWMON_ABSOLUTE_ZERO_MILLICELSIUS (-273000) ++ ++static long cros_ec_hwmon_kelvin_to_millicelsius(long t) ++{ ++ return t * MILLIDEGREE_PER_DEGREE + CROS_EC_HWMON_ABSOLUTE_ZERO_MILLICELSIUS; ++} ++ + static long cros_ec_hwmon_temp_to_millicelsius(u8 temp) + { +- return kelvin_to_millicelsius((((long)temp) + EC_TEMP_SENSOR_OFFSET)); ++ return cros_ec_hwmon_kelvin_to_millicelsius((((long)temp) + EC_TEMP_SENSOR_OFFSET)); + } + + static bool cros_ec_hwmon_attr_is_temp_threshold(u32 attr) +@@ -228,8 +236,13 @@ static int cros_ec_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + ret = cros_ec_hwmon_read_temp_threshold(priv->cros_ec, channel, + cros_ec_hwmon_attr_to_thres(attr), + &threshold); +- if (ret == 0) +- *val = kelvin_to_millicelsius(threshold); ++ if (ret == 0) { ++ /* Limit to sensible, non-overflowing values. */ ++ if (threshold > 255 + 273) ++ *val = 255000; ++ else ++ *val = cros_ec_hwmon_kelvin_to_millicelsius(threshold); ++ } + } + } + +diff --git a/drivers/hwmon/da9055-hwmon.c b/drivers/hwmon/da9055-hwmon.c +--- a/drivers/hwmon/da9055-hwmon.c ++++ b/drivers/hwmon/da9055-hwmon.c +@@ -256,11 +256,8 @@ static int da9055_hwmon_probe(struct platform_device *pdev) + NULL, da9055_auxadc_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "adc-irq", hwmon); +- if (ret != 0) { +- dev_err(hwmon->da9055->dev, "DA9055 ADC IRQ failed ret=%d\n", +- ret); ++ if (ret != 0) + return ret; +- } + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, "da9055", + hwmon, +diff --git a/drivers/hwmon/eic7700-pvt.c b/drivers/hwmon/eic7700-pvt.c +new file mode 100644 +--- /dev/null ++++ b/drivers/hwmon/eic7700-pvt.c +@@ -0,0 +1,509 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * ESWIN EIC7700 Voltage, Temperature sensor driver ++ * ++ * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd. ++ * ++ * Authors: ++ * Yulin Lu ++ * Huan He ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "eic7700-pvt.h" ++ ++static const struct pvt_sensor_info pvt_info[] = { ++ PVT_SENSOR_INFO(0, "Temperature", hwmon_temp, TEMP), ++ PVT_SENSOR_INFO(0, "Voltage", hwmon_in, VOLT), ++}; ++ ++static const char * const pvt_clk_names[PVT_CLK_NUM] = {"enable", "apb"}; ++ ++/* ++ * The original translation formulae of the temperature (in degrees of Celsius) ++ * to PVT data and vice-versa are following: ++ * N = 6.0818e-8*(T^4) +1.2873e-5*(T^3) + 7.2244e-3*(T^2) + 3.6484*(T^1) + ++ * 1.6198e2, ++ * T = -1.8439e-11*(N^4) + 8.0705e-8*(N^3) + -1.8501e-4*(N^2) + ++ * 3.2843e-1*(N^1) - 4.8690e1, ++ * where T = [-40, 125]C and N = [27, 771]. ++ * They must be accordingly altered to be suitable for the integer arithmetics. ++ * The technique is called 'factor redistribution', which just makes sure the ++ * multiplications and divisions are made so to have a result of the operations ++ * within the integer numbers limit. In addition we need to translate the ++ * formulae to accept millidegrees of Celsius. Here what they look like after ++ * the alterations: ++ * N = (60818e-20*(T^4) + 12873e-14*(T^3) + 72244e-9*(T^2) + 36484e-3*T + ++ * 16198e2) / 1e4, ++ * T = -18439e-12*(N^4) + 80705e-9*(N^3) - 185010e-6*(N^2) + 328430e-3*N - ++ * 48690, ++ * where T = [-40000, 125000] mC and N = [27, 771]. ++ */ ++static const struct polynomial poly_N_to_temp = { ++ .total_divider = 1, ++ .terms = { ++ {4, -18439, 1000, 1}, ++ {3, 80705, 1000, 1}, ++ {2, -185010, 1000, 1}, ++ {1, 328430, 1000, 1}, ++ {0, -48690, 1, 1} ++ } ++}; ++ ++/* ++ * Similar alterations are performed for the voltage conversion equations. ++ * The original formulae are: ++ * N = 1.3905e3*V - 5.7685e2, ++ * V = (N + 5.7685e2) / 1.3905e3, ++ * where V = [0.72, 0.88] V and N = [424, 646]. ++ * After the optimization they looks as follows: ++ * N = (13905e-3*V - 5768.5) / 10, ++ * V = (N * 10^5 / 13905 + 57685 * 10^3 / 13905) / 10. ++ * where V = [720, 880] mV and N = [424, 646]. ++ */ ++static const struct polynomial poly_N_to_volt = { ++ .total_divider = 10, ++ .terms = { ++ {1, 100000, 13905, 1}, ++ {0, 57685000, 1, 13905} ++ } ++}; ++ ++static inline u32 eic7700_pvt_update(void __iomem *reg, u32 mask, u32 data) ++{ ++ u32 old; ++ ++ old = readl_relaxed(reg); ++ writel((old & ~mask) | (data & mask), reg); ++ ++ return old & mask; ++} ++ ++static inline void eic7700_pvt_set_mode(struct pvt_hwmon *pvt, u32 mode) ++{ ++ u32 old; ++ ++ mode = FIELD_PREP(PVT_MODE_MASK, mode); ++ ++ old = eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, 0); ++ eic7700_pvt_update(pvt->regs + PVT_MODE, PVT_MODE_MASK, mode); ++ eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, old); ++} ++ ++static inline void eic7700_pvt_set_trim(struct pvt_hwmon *pvt, u32 val) ++{ ++ u32 old; ++ ++ old = eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, 0); ++ writel(val, pvt->regs + PVT_TRIM); ++ eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, old); ++} ++ ++static irqreturn_t eic7700_pvt_hard_isr(int irq, void *data) ++{ ++ struct pvt_hwmon *pvt = data; ++ u32 stat, val; ++ int active; ++ ++ if (IS_ENABLED(CONFIG_PM)) { ++ active = pm_runtime_get_if_active(pvt->dev); ++ if (active <= 0) ++ return IRQ_NONE; ++ } ++ ++ stat = readl(pvt->regs + PVT_INT); ++ if (!(stat & PVT_INT_STAT)) { ++ if (IS_ENABLED(CONFIG_PM)) ++ pm_runtime_put(pvt->dev); ++ return IRQ_NONE; ++ } ++ ++ eic7700_pvt_update(pvt->regs + PVT_INT, PVT_INT_CLR, PVT_INT_CLR); ++ /* ++ * Read the data, update the cache and notify a waiter of this event. ++ */ ++ val = readl(pvt->regs + PVT_DATA); ++ WRITE_ONCE(pvt->data_cache, FIELD_GET(PVT_DATA_OUT, val)); ++ complete(&pvt->conversion); ++ ++ if (IS_ENABLED(CONFIG_PM)) ++ pm_runtime_put(pvt->dev); ++ ++ return IRQ_HANDLED; ++} ++ ++static int eic7700_pvt_read_data(struct pvt_hwmon *pvt, ++ enum pvt_sensor_type type, long *val) ++{ ++ unsigned long timeout; ++ u32 data; ++ int ret; ++ ++ /* ++ * Wait for PVT conversion to complete and update the data cache. The ++ * data read procedure is following: set the requested PVT sensor mode, ++ * enable conversion, wait until conversion is finished, then disable ++ * conversion and IRQ, and read the cached data. ++ */ ++ reinit_completion(&pvt->conversion); ++ ++ eic7700_pvt_set_mode(pvt, pvt_info[type].mode); ++ eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, PVT_ENA_EN); ++ ++ /* ++ * Wait with timeout since in case if the sensor is suddenly powered ++ * down the request won't be completed and the caller will hang up on ++ * this procedure until the power is back up again. Multiply the ++ * timeout by the factor of two to prevent a false timeout. ++ */ ++ timeout = 2 * usecs_to_jiffies(ktime_to_us(pvt->timeout)); ++ ret = wait_for_completion_timeout(&pvt->conversion, timeout); ++ ++ eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, 0); ++ eic7700_pvt_update(pvt->regs + PVT_INT, PVT_INT_CLR, PVT_INT_CLR); ++ ++ if (!ret) ++ synchronize_irq(pvt->irq); ++ ++ data = READ_ONCE(pvt->data_cache); ++ ++ if (!ret) ++ return -ETIMEDOUT; ++ ++ if (type == PVT_TEMP) ++ *val = polynomial_calc(&poly_N_to_temp, data); ++ else ++ *val = polynomial_calc(&poly_N_to_volt, data); ++ ++ return 0; ++} ++ ++static const struct hwmon_channel_info *pvt_channel_info[] = { ++ HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), ++ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL), ++ HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LABEL), ++ NULL ++}; ++ ++static umode_t eic7700_pvt_hwmon_is_visible(const void *data, ++ enum hwmon_sensor_types type, ++ u32 attr, int ch) ++{ ++ switch (type) { ++ case hwmon_temp: ++ switch (attr) { ++ case hwmon_temp_input: ++ case hwmon_temp_label: ++ return 0444; ++ } ++ break; ++ case hwmon_in: ++ switch (attr) { ++ case hwmon_in_input: ++ case hwmon_in_label: ++ return 0444; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ ++static int eic7700_pvt_hwmon_read(struct device *dev, ++ enum hwmon_sensor_types type, u32 attr, ++ int ch, long *val) ++{ ++ struct pvt_hwmon *pvt = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = pm_runtime_get_sync(pvt->dev); ++ if (ret < 0) { ++ dev_err(pvt->dev, "Failed to resume PVT device: %d\n", ret); ++ pm_runtime_put_noidle(pvt->dev); ++ return ret; ++ } ++ ++ switch (type) { ++ case hwmon_temp: ++ switch (attr) { ++ case hwmon_temp_input: ++ ret = eic7700_pvt_read_data(pvt, ch, val); ++ break; ++ default: ++ ret = -EOPNOTSUPP; ++ } ++ break; ++ case hwmon_in: ++ if (attr == hwmon_in_input) ++ ret = eic7700_pvt_read_data(pvt, PVT_VOLT + ch, val); ++ else ++ ret = -EOPNOTSUPP; ++ break; ++ default: ++ ret = -EOPNOTSUPP; ++ } ++ ++ pm_runtime_mark_last_busy(pvt->dev); ++ pm_runtime_put_autosuspend(pvt->dev); ++ return ret; ++} ++ ++static int eic7700_pvt_hwmon_read_string(struct device *dev, ++ enum hwmon_sensor_types type, u32 attr, ++ int ch, const char **str) ++{ ++ switch (type) { ++ case hwmon_temp: ++ if (attr == hwmon_temp_label) { ++ *str = pvt_info[ch].label; ++ return 0; ++ } ++ break; ++ case hwmon_in: ++ if (attr == hwmon_in_label) { ++ *str = pvt_info[PVT_VOLT + ch].label; ++ return 0; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ return -EOPNOTSUPP; ++} ++ ++static const struct hwmon_ops pvt_hwmon_ops = { ++ .is_visible = eic7700_pvt_hwmon_is_visible, ++ .read = eic7700_pvt_hwmon_read, ++ .read_string = eic7700_pvt_hwmon_read_string ++}; ++ ++static const struct hwmon_chip_info pvt_hwmon_info = { ++ .ops = &pvt_hwmon_ops, ++ .info = pvt_channel_info ++}; ++ ++static struct pvt_hwmon *eic7700_pvt_create_data(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct pvt_hwmon *pvt; ++ ++ pvt = devm_kzalloc(dev, sizeof(*pvt), GFP_KERNEL); ++ if (!pvt) ++ return ERR_PTR(-ENOMEM); ++ ++ pvt->dev = dev; ++ init_completion(&pvt->conversion); ++ ++ return pvt; ++} ++ ++static int eic7700_pvt_init_iface(struct pvt_hwmon *pvt) ++{ ++ /* ++ * Make sure controller are disabled so not to accidentally have ISR ++ * executed before the driver data is fully initialized. Clear the IRQ ++ * status as well. ++ */ ++ eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, 0); ++ eic7700_pvt_update(pvt->regs + PVT_INT, PVT_INT_CLR, PVT_INT_CLR); ++ readl(pvt->regs + PVT_INT); ++ readl(pvt->regs + PVT_DATA); ++ ++ /* Setup default sensor mode and temperature trim. */ ++ eic7700_pvt_set_mode(pvt, pvt_info[PVT_TEMP].mode); ++ ++ /* ++ * Max conversion latency (~333 µs) derived from PVT spec: ++ * maximum sampling rate = 3000 samples/sec. ++ */ ++ pvt->timeout = ns_to_ktime(PVT_TOUT_MIN); ++ ++ eic7700_pvt_set_trim(pvt, PVT_TRIM_DEF); ++ ++ return 0; ++} ++ ++static int eic7700_pvt_request_irq(struct pvt_hwmon *pvt) ++{ ++ struct platform_device *pdev = to_platform_device(pvt->dev); ++ int ret; ++ ++ pvt->irq = platform_get_irq(pdev, 0); ++ if (pvt->irq < 0) ++ return pvt->irq; ++ ++ ret = devm_request_threaded_irq(pvt->dev, pvt->irq, ++ eic7700_pvt_hard_isr, NULL, ++ IRQF_TRIGGER_HIGH, "pvt", pvt); ++ if (ret) { ++ dev_err(pvt->dev, "Couldn't request PVT IRQ\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int eic7700_pvt_create_hwmon(struct pvt_hwmon *pvt) ++{ ++ pvt->hwmon = devm_hwmon_device_register_with_info(pvt->dev, "pvt", ++ pvt, &pvt_hwmon_info, ++ NULL); ++ if (IS_ERR(pvt->hwmon)) { ++ dev_err(pvt->dev, "Couldn't create hwmon device\n"); ++ return PTR_ERR(pvt->hwmon); ++ } ++ ++ return 0; ++} ++ ++static void eic7700_pvt_disable_pm_runtime(void *data) ++{ ++ struct pvt_hwmon *pvt = data; ++ ++ pm_runtime_dont_use_autosuspend(pvt->dev); ++ pm_runtime_disable(pvt->dev); ++ ++ if (!pm_runtime_status_suspended(pvt->dev)) { ++ clk_bulk_disable_unprepare(PVT_CLK_NUM, pvt->clks); ++ pm_runtime_set_suspended(pvt->dev); ++ } ++} ++ ++static int eic7700_pvt_probe(struct platform_device *pdev) ++{ ++ struct reset_control *rst; ++ struct pvt_hwmon *pvt; ++ int i, ret; ++ ++ pvt = eic7700_pvt_create_data(pdev); ++ if (IS_ERR(pvt)) ++ return PTR_ERR(pvt); ++ ++ platform_set_drvdata(pdev, pvt); ++ ++ pvt->regs = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(pvt->regs)) ++ return PTR_ERR(pvt->regs); ++ ++ for (i = 0; i < PVT_CLK_NUM; i++) ++ pvt->clks[i].id = pvt_clk_names[i]; ++ ++ ret = devm_clk_bulk_get(&pdev->dev, PVT_CLK_NUM, pvt->clks); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, ++ "Couldn't get clock descriptors\n"); ++ ++ rst = devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL); ++ if (IS_ERR(rst)) ++ return dev_err_probe(pvt->dev, PTR_ERR(rst), ++ "Couldn't get reset control\n"); ++ ++ ret = clk_bulk_prepare_enable(PVT_CLK_NUM, pvt->clks); ++ if (ret) ++ return dev_err_probe(pvt->dev, ret, ++ "Failed to enable clocks\n"); ++ ++ ret = eic7700_pvt_init_iface(pvt); ++ if (ret) { ++ clk_bulk_disable_unprepare(PVT_CLK_NUM, pvt->clks); ++ return ret; ++ } ++ ++ if (IS_ENABLED(CONFIG_PM)) ++ clk_bulk_disable_unprepare(PVT_CLK_NUM, pvt->clks); ++ ++ pm_runtime_enable(&pdev->dev); ++ pm_runtime_set_autosuspend_delay(&pdev->dev, 3000); ++ pm_runtime_use_autosuspend(&pdev->dev); ++ pm_runtime_get_noresume(&pdev->dev); ++ ++ ret = devm_add_action_or_reset(pvt->dev, eic7700_pvt_disable_pm_runtime, ++ pvt); ++ if (ret) { ++ pm_runtime_put_noidle(&pdev->dev); ++ return dev_err_probe(&pdev->dev, ret, ++ "Can't register PM cleanup\n"); ++ } ++ ++ ret = eic7700_pvt_request_irq(pvt); ++ if (ret) ++ goto err_put_pm_runtime; ++ ++ ret = eic7700_pvt_create_hwmon(pvt); ++ if (ret) ++ goto err_put_pm_runtime; ++ ++ pm_runtime_put_autosuspend(&pdev->dev); ++ ++ return 0; ++ ++err_put_pm_runtime: ++ pm_runtime_put_noidle(&pdev->dev); ++ return ret; ++} ++ ++static int __maybe_unused eic7700_pvt_runtime_resume(struct device *dev) ++{ ++ struct pvt_hwmon *pvt = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = clk_bulk_prepare_enable(PVT_CLK_NUM, pvt->clks); ++ if (ret) { ++ dev_err(dev, "Failed to enable clocks: %d\n", ret); ++ return ret; ++ } ++ ++ eic7700_pvt_set_trim(pvt, PVT_TRIM_DEF); ++ ++ return 0; ++} ++ ++static int __maybe_unused eic7700_pvt_runtime_suspend(struct device *dev) ++{ ++ struct pvt_hwmon *pvt = dev_get_drvdata(dev); ++ ++ clk_bulk_disable_unprepare(PVT_CLK_NUM, pvt->clks); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops eic7700_pvt_pm_ops = { ++ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) ++ RUNTIME_PM_OPS(eic7700_pvt_runtime_suspend, eic7700_pvt_runtime_resume, ++ NULL) ++}; ++ ++static const struct of_device_id pvt_of_match[] = { ++ { .compatible = "eswin,eic7700-pvt"}, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, pvt_of_match); ++ ++static struct platform_driver pvt_driver = { ++ .probe = eic7700_pvt_probe, ++ .driver = { ++ .name = "eic7700-pvt", ++ .of_match_table = pvt_of_match, ++ .pm = pm_ptr(&eic7700_pvt_pm_ops), ++ }, ++}; ++module_platform_driver(pvt_driver); ++ ++MODULE_AUTHOR("Yulin Lu "); ++MODULE_AUTHOR("Huan He "); ++MODULE_DESCRIPTION("Eswin eic7700 PVT driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/hwmon/eic7700-pvt.h b/drivers/hwmon/eic7700-pvt.h +new file mode 100644 +--- /dev/null ++++ b/drivers/hwmon/eic7700-pvt.h +@@ -0,0 +1,99 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * ESWIN EIC7700 Voltage, Temperature sensor driver ++ * ++ * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd. ++ */ ++#ifndef __HWMON_EIC7700_PVT_H__ ++#define __HWMON_EIC7700_PVT_H__ ++ ++#include ++#include ++#include ++#include ++ ++/* ESWIN EIC7700 PVT registers and their bitfields */ ++#define PVT_TRIM 0x04 ++#define PVT_MODE 0x08 ++#define PVT_MODE_MASK GENMASK(2, 0) ++#define PVT_CTRL_MODE_TEMP 0x0 ++#define PVT_CTRL_MODE_VOLT 0x4 ++#define PVT_ENA 0x0c ++#define PVT_ENA_EN BIT(0) ++#define PVT_INT 0x10 ++#define PVT_INT_STAT BIT(0) ++#define PVT_INT_CLR BIT(1) ++#define PVT_DATA 0x14 ++#define PVT_DATA_OUT GENMASK(9, 0) ++ ++/* ++ * PVT sensors-related limits and default values ++ * @PVT_TEMP_CHS: Number of temperature hwmon channels. ++ * @PVT_VOLT_CHS: Number of voltage hwmon channels. ++ * @PVT_TRIM_DEF: Default temperature sensor trim value (set a proper value ++ * when one is determined for ESWIN EIC7700 SoC). ++ * @PVT_TOUT_MIN: Minimal timeout between samples in nanoseconds. ++ */ ++#define PVT_TEMP_CHS 1 ++#define PVT_VOLT_CHS 1 ++#define PVT_TRIM_DEF 0 ++#define PVT_TOUT_MIN (NSEC_PER_SEC / 3000) ++ ++/* ++ * enum pvt_sensor_type - ESWIN EIC7700 PVT sensor types (correspond to each PVT ++ * sampling mode) ++ * @PVT_TEMP: PVT Temperature sensor. ++ * @PVT_VOLT: PVT Voltage sensor. ++ */ ++enum pvt_sensor_type { ++ PVT_TEMP = 0, ++ PVT_VOLT ++}; ++ ++#define PVT_CLK_NUM 2 ++ ++/* ++ * struct pvt_sensor_info - ESWIN EIC7700 PVT sensor informational structure ++ * @channel: Sensor channel ID. ++ * @label: hwmon sensor label. ++ * @mode: PVT mode corresponding to the channel. ++ * @type: Sensor type. ++ */ ++struct pvt_sensor_info { ++ int channel; ++ const char *label; ++ u32 mode; ++ enum hwmon_sensor_types type; ++}; ++ ++#define PVT_SENSOR_INFO(_ch, _label, _type, _mode) \ ++ { \ ++ .channel = _ch, \ ++ .label = _label, \ ++ .mode = PVT_CTRL_MODE_ ##_mode, \ ++ .type = _type, \ ++ } ++ ++/* ++ * struct pvt_hwmon - Eswin EIC7700 PVT private data ++ * @dev: device structure of the PVT platform device. ++ * @hwmon: hwmon device structure. ++ * @regs: pointer to the Eswin EIC7700 PVT registers region. ++ * @irq: PVT events IRQ number. ++ * @clks: PVT clock descriptors. ++ * @data_cache: data cache in raw format. ++ * @conversion: data conversion completion. ++ * @timeout: conversion timeout. ++ */ ++struct pvt_hwmon { ++ struct device *dev; ++ struct device *hwmon; ++ void __iomem *regs; ++ int irq; ++ struct clk_bulk_data clks[PVT_CLK_NUM]; ++ u32 data_cache; ++ struct completion conversion; ++ ktime_t timeout; ++}; ++ ++#endif /* __HWMON_EIC7700_PVT_H__ */ +diff --git a/drivers/hwmon/emc1403.c b/drivers/hwmon/emc1403.c +--- a/drivers/hwmon/emc1403.c ++++ b/drivers/hwmon/emc1403.c +@@ -18,6 +18,7 @@ + #include + #include + #include ++#include + #include + + #define THERMAL_PID_REG 0xfd +@@ -654,14 +655,20 @@ static int emc1403_probe(struct i2c_client *client) + { + struct thermal_data *data; + struct device *hwmon_dev; +- const struct i2c_device_id *id = i2c_match_id(emc1403_idtable, client); ++ int ret; ++ ++ ret = devm_regulator_get_enable(&client->dev, "vdd"); ++ if (ret) ++ return dev_err_probe(&client->dev, ret, ++ "Failed to enable regulator\n"); + + data = devm_kzalloc(&client->dev, sizeof(struct thermal_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + +- data->chip = id->driver_data; ++ data->chip = (uintptr_t)i2c_get_match_data(client); ++ + data->regmap = devm_regmap_init_i2c(client, &emc1403_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); +@@ -677,10 +684,20 @@ static const unsigned short emc1403_address_list[] = { + 0x18, 0x1c, 0x29, 0x3c, 0x4c, 0x4d, 0x5c, I2C_CLIENT_END + }; + ++static const struct of_device_id emc1403_of_match[] = { ++ { .compatible = "smsc,emc1402", .data = (void *)emc1402 }, ++ { .compatible = "smsc,emc1403", .data = (void *)emc1403 }, ++ { .compatible = "smsc,emc1404", .data = (void *)emc1404 }, ++ { .compatible = "smsc,emc1428", .data = (void *)emc1428 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, emc1403_of_match); ++ + static struct i2c_driver sensor_emc1403 = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "emc1403", ++ .of_match_table = emc1403_of_match, + }, + .detect = emc1403_detect, + .probe = emc1403_probe, +diff --git a/drivers/hwmon/gxp-fan-ctrl.c b/drivers/hwmon/gxp-fan-ctrl.c +--- a/drivers/hwmon/gxp-fan-ctrl.c ++++ b/drivers/hwmon/gxp-fan-ctrl.c +@@ -1,59 +1,16 @@ + // SPDX-License-Identifier: GPL-2.0-only +-/* Copyright (C) 2022 Hewlett-Packard Enterprise Development Company, L.P. */ ++/* Copyright (C) 2023 Hewlett-Packard Enterprise Development Company, L.P. */ + +-#include + #include + #include + #include + #include + #include + +-#define OFS_FAN_INST 0 /* Is 0 because plreg base will be set at INST */ +-#define OFS_FAN_FAIL 2 /* Is 2 bytes after base */ +-#define OFS_SEVSTAT 0 /* Is 0 because fn2 base will be set at SEVSTAT */ +-#define POWER_BIT 24 +- + struct gxp_fan_ctrl_drvdata { + void __iomem *base; +- void __iomem *plreg; +- void __iomem *fn2; + }; + +-static bool fan_installed(struct device *dev, int fan) +-{ +- struct gxp_fan_ctrl_drvdata *drvdata = dev_get_drvdata(dev); +- u8 val; +- +- val = readb(drvdata->plreg + OFS_FAN_INST); +- +- return !!(val & BIT(fan)); +-} +- +-static long fan_failed(struct device *dev, int fan) +-{ +- struct gxp_fan_ctrl_drvdata *drvdata = dev_get_drvdata(dev); +- u8 val; +- +- val = readb(drvdata->plreg + OFS_FAN_FAIL); +- +- return !!(val & BIT(fan)); +-} +- +-static long fan_enabled(struct device *dev, int fan) +-{ +- struct gxp_fan_ctrl_drvdata *drvdata = dev_get_drvdata(dev); +- u32 val; +- +- /* +- * Check the power status as if the platform is off the value +- * reported for the PWM will be incorrect. Report fan as +- * disabled. +- */ +- val = readl(drvdata->fn2 + OFS_SEVSTAT); +- +- return !!((val & BIT(POWER_BIT)) && fan_installed(dev, fan)); +-} +- + static int gxp_pwm_write(struct device *dev, u32 attr, int channel, long val) + { + struct gxp_fan_ctrl_drvdata *drvdata = dev_get_drvdata(dev); +@@ -80,37 +37,11 @@ static int gxp_fan_ctrl_write(struct device *dev, enum hwmon_sensor_types type, + } + } + +-static int gxp_fan_read(struct device *dev, u32 attr, int channel, long *val) +-{ +- switch (attr) { +- case hwmon_fan_enable: +- *val = fan_enabled(dev, channel); +- return 0; +- case hwmon_fan_fault: +- *val = fan_failed(dev, channel); +- return 0; +- default: +- return -EOPNOTSUPP; +- } +-} +- + static int gxp_pwm_read(struct device *dev, u32 attr, int channel, long *val) + { + struct gxp_fan_ctrl_drvdata *drvdata = dev_get_drvdata(dev); +- u32 reg; + +- /* +- * Check the power status of the platform. If the platform is off +- * the value reported for the PWM will be incorrect. In this case +- * report a PWM of zero. +- */ +- +- reg = readl(drvdata->fn2 + OFS_SEVSTAT); +- +- if (reg & BIT(POWER_BIT)) +- *val = fan_installed(dev, channel) ? readb(drvdata->base + channel) : 0; +- else +- *val = 0; ++ *val = readb(drvdata->base + channel); + + return 0; + } +@@ -119,8 +50,6 @@ static int gxp_fan_ctrl_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) + { + switch (type) { +- case hwmon_fan: +- return gxp_fan_read(dev, attr, channel, val); + case hwmon_pwm: + return gxp_pwm_read(dev, attr, channel, val); + default: +@@ -135,16 +64,6 @@ static umode_t gxp_fan_ctrl_is_visible(const void *_data, + umode_t mode = 0; + + switch (type) { +- case hwmon_fan: +- switch (attr) { +- case hwmon_fan_enable: +- case hwmon_fan_fault: +- mode = 0444; +- break; +- default: +- break; +- } +- break; + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: +@@ -168,15 +87,6 @@ static const struct hwmon_ops gxp_fan_ctrl_ops = { + }; + + static const struct hwmon_channel_info * const gxp_fan_ctrl_info[] = { +- HWMON_CHANNEL_INFO(fan, +- HWMON_F_FAULT | HWMON_F_ENABLE, +- HWMON_F_FAULT | HWMON_F_ENABLE, +- HWMON_F_FAULT | HWMON_F_ENABLE, +- HWMON_F_FAULT | HWMON_F_ENABLE, +- HWMON_F_FAULT | HWMON_F_ENABLE, +- HWMON_F_FAULT | HWMON_F_ENABLE, +- HWMON_F_FAULT | HWMON_F_ENABLE, +- HWMON_F_FAULT | HWMON_F_ENABLE), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, +@@ -211,18 +121,6 @@ static int gxp_fan_ctrl_probe(struct platform_device *pdev) + return dev_err_probe(dev, PTR_ERR(drvdata->base), + "failed to map base\n"); + +- drvdata->plreg = devm_platform_ioremap_resource_byname(pdev, +- "pl"); +- if (IS_ERR(drvdata->plreg)) +- return dev_err_probe(dev, PTR_ERR(drvdata->plreg), +- "failed to map plreg\n"); +- +- drvdata->fn2 = devm_platform_ioremap_resource_byname(pdev, +- "fn2"); +- if (IS_ERR(drvdata->fn2)) +- return dev_err_probe(dev, PTR_ERR(drvdata->fn2), +- "failed to map fn2\n"); +- + hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, + "hpe_gxp_fan_ctrl", + drvdata, +diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c +--- a/drivers/hwmon/hwmon.c ++++ b/drivers/hwmon/hwmon.c +@@ -57,8 +57,8 @@ struct hwmon_device_attribute { + }; + + #define to_hwmon_attr(d) \ +- container_of(d, struct hwmon_device_attribute, dev_attr) +-#define to_dev_attr(a) container_of(a, struct device_attribute, attr) ++ container_of_const(d, struct hwmon_device_attribute, dev_attr) ++#define to_dev_attr(a) container_of_const(a, struct device_attribute, attr) + + /* + * Thermal zone information +@@ -71,27 +71,27 @@ struct hwmon_thermal_data { + }; + + static ssize_t +-name_show(struct device *dev, struct device_attribute *attr, char *buf) ++name_show(struct device *dev, const struct device_attribute *attr, char *buf) + { + return sysfs_emit(buf, "%s\n", to_hwmon_device(dev)->name); + } +-static DEVICE_ATTR_RO(name); ++static const DEVICE_ATTR_RO(name); + + static ssize_t +-label_show(struct device *dev, struct device_attribute *attr, char *buf) ++label_show(struct device *dev, const struct device_attribute *attr, char *buf) + { + return sysfs_emit(buf, "%s\n", to_hwmon_device(dev)->label); + } +-static DEVICE_ATTR_RO(label); ++static const DEVICE_ATTR_RO(label); + +-static struct attribute *hwmon_dev_attrs[] = { ++static const struct attribute *const hwmon_dev_attrs[] = { + &dev_attr_name.attr, + &dev_attr_label.attr, + NULL + }; + + static umode_t hwmon_dev_attr_is_visible(struct kobject *kobj, +- struct attribute *attr, int n) ++ const struct attribute *attr, int n) + { + struct device *dev = kobj_to_dev(kobj); + struct hwmon_device *hdev = to_hwmon_device(dev); +@@ -106,8 +106,8 @@ static umode_t hwmon_dev_attr_is_visible(struct kobject *kobj, + } + + static const struct attribute_group hwmon_dev_attr_group = { +- .attrs = hwmon_dev_attrs, +- .is_visible = hwmon_dev_attr_is_visible, ++ .attrs_const = hwmon_dev_attrs, ++ .is_visible_const = hwmon_dev_attr_is_visible, + }; + + static const struct attribute_group *hwmon_dev_attr_groups[] = { +@@ -115,13 +115,13 @@ static const struct attribute_group *hwmon_dev_attr_groups[] = { + NULL + }; + +-static void hwmon_free_attrs(struct attribute **attrs) ++static void hwmon_free_attrs(const struct attribute *const *attrs) + { + int i; + + for (i = 0; attrs[i]; i++) { +- struct device_attribute *dattr = to_dev_attr(attrs[i]); +- struct hwmon_device_attribute *hattr = to_hwmon_attr(dattr); ++ const struct device_attribute *dattr = to_dev_attr(attrs[i]); ++ const struct hwmon_device_attribute *hattr = to_hwmon_attr(dattr); + + kfree(hattr); + } +@@ -132,8 +132,8 @@ static void hwmon_dev_release(struct device *dev) + { + struct hwmon_device *hwdev = to_hwmon_device(dev); + +- if (hwdev->group.attrs) +- hwmon_free_attrs(hwdev->group.attrs); ++ if (hwdev->group.attrs_const) ++ hwmon_free_attrs(hwdev->group.attrs_const); + kfree(hwdev->groups); + kfree(hwdev->label); + kfree(hwdev); +@@ -341,7 +341,7 @@ static int hwmon_match_device(struct device *dev, const void *data) + return dev->class == &hwmon_class; + } + +-static ssize_t pec_show(struct device *dev, struct device_attribute *dummy, ++static ssize_t pec_show(struct device *dev, const struct device_attribute *dummy, + char *buf) + { + struct i2c_client *client = to_i2c_client(dev); +@@ -349,7 +349,7 @@ static ssize_t pec_show(struct device *dev, struct device_attribute *dummy, + return sysfs_emit(buf, "%d\n", !!(client->flags & I2C_CLIENT_PEC)); + } + +-static ssize_t pec_store(struct device *dev, struct device_attribute *devattr, ++static ssize_t pec_store(struct device *dev, const struct device_attribute *devattr, + const char *buf, size_t count) + { + struct i2c_client *client = to_i2c_client(dev); +@@ -389,7 +389,7 @@ static ssize_t pec_store(struct device *dev, struct device_attribute *devattr, + return err; + } + +-static DEVICE_ATTR_RW(pec); ++static const DEVICE_ATTR_RW(pec); + + static void hwmon_remove_pec(void *dev) + { +@@ -424,9 +424,9 @@ static int hwmon_pec_register(struct device *hdev) + /* sysfs attribute management */ + + static ssize_t hwmon_attr_show(struct device *dev, +- struct device_attribute *devattr, char *buf) ++ const struct device_attribute *devattr, char *buf) + { +- struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr); ++ const struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr); + struct hwmon_device *hwdev = to_hwmon_device(dev); + s64 val64; + long val; +@@ -449,10 +449,10 @@ static ssize_t hwmon_attr_show(struct device *dev, + } + + static ssize_t hwmon_attr_show_string(struct device *dev, +- struct device_attribute *devattr, ++ const struct device_attribute *devattr, + char *buf) + { +- struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr); ++ const struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr); + struct hwmon_device *hwdev = to_hwmon_device(dev); + enum hwmon_sensor_types type = hattr->type; + const char *s; +@@ -472,10 +472,10 @@ static ssize_t hwmon_attr_show_string(struct device *dev, + } + + static ssize_t hwmon_attr_store(struct device *dev, +- struct device_attribute *devattr, ++ const struct device_attribute *devattr, + const char *buf, size_t count) + { +- struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr); ++ const struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr); + struct hwmon_device *hwdev = to_hwmon_device(dev); + long val; + int ret; +@@ -509,12 +509,12 @@ static bool is_string_attr(enum hwmon_sensor_types type, u32 attr) + (type == hwmon_fan && attr == hwmon_fan_label); + } + +-static struct attribute *hwmon_genattr(const void *drvdata, +- enum hwmon_sensor_types type, +- u32 attr, +- int index, +- const char *template, +- const struct hwmon_ops *ops) ++static const struct attribute *hwmon_genattr(const void *drvdata, ++ enum hwmon_sensor_types type, ++ u32 attr, ++ int index, ++ const char *template, ++ const struct hwmon_ops *ops) + { + struct hwmon_device_attribute *hattr; + struct device_attribute *dattr; +@@ -551,8 +551,8 @@ static struct attribute *hwmon_genattr(const void *drvdata, + hattr->ops = ops; + + dattr = &hattr->dev_attr; +- dattr->show = is_string ? hwmon_attr_show_string : hwmon_attr_show; +- dattr->store = hwmon_attr_store; ++ dattr->show_const = is_string ? hwmon_attr_show_string : hwmon_attr_show; ++ dattr->store_const = hwmon_attr_store; + + a = &dattr->attr; + sysfs_attr_init(a); +@@ -830,7 +830,7 @@ static int hwmon_num_channel_attrs(const struct hwmon_channel_info *info) + } + + static int hwmon_genattrs(const void *drvdata, +- struct attribute **attrs, ++ const struct attribute **attrs, + const struct hwmon_ops *ops, + const struct hwmon_channel_info *info) + { +@@ -849,7 +849,7 @@ static int hwmon_genattrs(const void *drvdata, + u32 attr; + + while (attr_mask) { +- struct attribute *a; ++ const struct attribute *a; + + attr = __ffs(attr_mask); + attr_mask &= ~BIT(attr); +@@ -868,11 +868,11 @@ static int hwmon_genattrs(const void *drvdata, + return aindex; + } + +-static struct attribute ** ++static const struct attribute ** + __hwmon_create_attrs(const void *drvdata, const struct hwmon_chip_info *chip) + { + int ret, i, aindex = 0, nattrs = 0; +- struct attribute **attrs; ++ const struct attribute **attrs; + + for (i = 0; chip->info[i]; i++) + nattrs += hwmon_num_channel_attrs(chip->info[i]); +@@ -927,7 +927,7 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata, + hdev = &hwdev->dev; + + if (chip) { +- struct attribute **attrs; ++ const struct attribute **attrs; + int ngroups = 2; /* terminating NULL plus &hwdev->groups */ + + if (groups) +@@ -946,7 +946,7 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata, + goto free_hwmon; + } + +- hwdev->group.attrs = attrs; ++ hwdev->group.attrs_const = attrs; + ngroups = 0; + hwdev->groups[ngroups++] = &hwdev->group; + +diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c +--- a/drivers/hwmon/ina2xx.c ++++ b/drivers/hwmon/ina2xx.c +@@ -124,6 +124,7 @@ static const struct regmap_config ina2xx_regmap_config = { + enum ina2xx_ids { + ina219, + ina226, ++ ina232, + ina234, + ina260, + sy24655 +@@ -210,6 +211,20 @@ static const struct ina2xx_config ina2xx_config[] = { + .current_shift = 4, + .has_update_interval = true, + }, ++ [ina232] = { ++ .config_default = INA226_CONFIG_DEFAULT, ++ .calibration_value = 2048, ++ .shunt_div = 400, ++ .shunt_voltage_shift = 0, ++ .bus_voltage_shift = 0, ++ .bus_voltage_lsb = 1600, ++ .power_lsb_factor = 32, ++ .has_alerts = true, ++ .has_ishunt = false, ++ .has_power_average = false, ++ .current_shift = 0, ++ .has_update_interval = true, ++ }, + [ina260] = { + .config_default = INA260_CONFIG_DEFAULT, + .shunt_div = 400, +@@ -1123,6 +1138,7 @@ static const struct i2c_device_id ina2xx_id[] = { + { .name = "ina226", .driver_data = ina226 }, + { .name = "ina230", .driver_data = ina226 }, + { .name = "ina231", .driver_data = ina226 }, ++ { .name = "ina232", .driver_data = ina232 }, + { .name = "ina234", .driver_data = ina234 }, + { .name = "ina260", .driver_data = ina260 }, + { .name = "sy24655", .driver_data = sy24655 }, +@@ -1155,6 +1171,10 @@ static const struct of_device_id __maybe_unused ina2xx_of_match[] = { + .compatible = "ti,ina231", + .data = (void *)ina226 + }, ++ { ++ .compatible = "ti,ina232", ++ .data = (void *)ina232 ++ }, + { + .compatible = "ti,ina234", + .data = (void *)ina234 +diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c +--- a/drivers/hwmon/k10temp.c ++++ b/drivers/hwmon/k10temp.c +@@ -207,6 +207,10 @@ static const char *k10temp_temp_label[] = { + "Tccd10", + "Tccd11", + "Tccd12", ++ "Tccd13", ++ "Tccd14", ++ "Tccd15", ++ "Tccd16", + }; + + static int k10temp_read_labels(struct device *dev, +@@ -243,7 +247,7 @@ static int k10temp_read_temp(struct device *dev, u32 attr, int channel, + if (*val < 0 && !data->disp_negative) + *val = 0; + break; +- case 2 ... 13: /* Tccd{1-12} */ ++ case 2 ... 17: /* Tccd{1-16} */ + ret = read_ccd_temp_reg(data, channel - 2, ®val); + + if (ret) +@@ -384,6 +388,10 @@ static const struct hwmon_channel_info * const k10temp_info[] = { + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, ++ HWMON_T_INPUT | HWMON_T_LABEL, ++ HWMON_T_INPUT | HWMON_T_LABEL, ++ HWMON_T_INPUT | HWMON_T_LABEL, ++ HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL), + NULL + }; +@@ -515,6 +523,10 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) + } + } else if (boot_cpu_data.x86 == 0x1a) { + switch (boot_cpu_data.x86_model) { ++ case 0x00 ... 0x2f: /* Zen5 Turin */ ++ data->ccd_offset = 0x1F0; ++ k10temp_get_ccd_support(data, 16); ++ break; + case 0x40 ... 0x4f: /* Zen5 Ryzen Desktop */ + data->ccd_offset = 0x308; + k10temp_get_ccd_support(data, 8); +diff --git a/drivers/hwmon/kb9002.c b/drivers/hwmon/kb9002.c +new file mode 100644 +--- /dev/null ++++ b/drivers/hwmon/kb9002.c +@@ -0,0 +1,460 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Kandou KB9002 PCIe 5.0 retimer hwmon driver. ++ * ++ * The retimer exposes a system management bus (SMBus 3.0 with PEC) ++ * target for firmware-managed status registers. This driver assumes ++ * the chip is strapped to SMBus mode and exports the aggregated ++ * maximum die temperature as hwmon temp1_input (millidegrees Celsius) ++ * plus the firmware version and boot status under debugfs. ++ * ++ * The raw-I2C path (kb9002_i2c_read/write, used only at probe to switch ++ * the host interface) carries the 32-bit register address and data ++ * big-endian. The SMBus path (kb9002_fw_read/kb9002_smbus_hw_read) ++ * carries the register address and returned data little-endian. ++ * ++ * Datasheet: Kandou KB9002 PCIe retimer (KA-015171-PD). ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define KB9002_DEV_NAME "kb9002" ++ ++/* ++ * SMBus read command codes. Each read is a two-phase PEC-protected ++ * transaction: prime writes the target address, data reads it back with ++ * the register contents. FW reads use a 16-bit address, HW reads 32-bit. ++ */ ++#define KB9002_CC_FW_READ_PRIME 0x82 ++#define KB9002_CC_FW_READ_DATA 0x81 ++#define KB9002_CC_HW_READ_PRIME 0x8a ++#define KB9002_CC_HW_READ_DATA 0x89 ++ ++/* Firmware register offsets (16-bit). */ ++#define KB9002_FW_REG_VID 0x0004 ++#define KB9002_FW_REG_FW_VERSION 0x0500 ++#define KB9002_FW_REG_TEMP_MAXIMUM 0x0550 ++ ++#define KB9002_VID_MASK GENMASK(31, 16) ++#define KB9002_VID_KANDOU 0x1e6f ++ ++/* Firmware boot status: 0xe8 in the top byte means init completed OK. */ ++#define KB9002_HW_REG_FW_BOOT_STATUS 0xe0090008 ++#define KB9002_FW_BOOT_STATUS_OK_MSB 0xe8 ++ ++/* ++ * Hardware registers reached over raw I2C (32-bit addressing). The ++ * host-interface bit selects SMBus (set) vs raw-I2C target; parts ++ * strapped to raw I2C need it set before SMBus access works. ++ */ ++#define KB9002_HW_REG_REVID 0x00480004 ++#define KB9002_HW_REG_HOST_IF 0x00480008 ++#define KB9002_HOST_IF_SMBUS BIT(1) ++ ++#define KB9002_REVID_MASK GENMASK(7, 0) ++#define KB9002_REVID_B0 0x10 ++#define KB9002_REVID_B1 0x11 ++ ++/* Retries to drain a stray leading 0xff from the raw-I2C FIFO. */ ++#define KB9002_REVID_READ_RETRIES 16 ++ ++/* Temperature: 32-bit Q16.16 absolute Kelvin. */ ++#define KB9002_TEMP_FRAC_BITS 16 ++#define KB9002_ABS_ZERO_MILLI_C (-273150) ++ ++/* Firmware takes up to ~2s to respond after a host-interface change. */ ++#define KB9002_FW_READY_POLL_US (25 * USEC_PER_MSEC) ++#define KB9002_FW_READY_TIMEOUT_US (2 * USEC_PER_SEC) ++ ++struct kb9002_data { ++ struct i2c_client *client; ++ struct device *hwmon_dev; ++}; ++ ++/* Raw-I2C read: write the 32-bit BE address, then read 4 BE data bytes. */ ++static int kb9002_i2c_read(struct i2c_client *client, u32 reg, u32 *val) ++{ ++ u8 addr[4]; ++ u8 rbuf[4]; ++ struct i2c_msg msgs[2] = { ++ { ++ .addr = client->addr, ++ .flags = 0, ++ .len = sizeof(addr), ++ .buf = addr, ++ }, ++ { ++ .addr = client->addr, ++ .flags = I2C_M_RD, ++ .len = sizeof(rbuf), ++ .buf = rbuf, ++ }, ++ }; ++ int ret; ++ ++ put_unaligned_be32(reg, addr); ++ ++ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); ++ if (ret < 0) ++ return ret; ++ if (ret != ARRAY_SIZE(msgs)) ++ return -EIO; ++ ++ *val = get_unaligned_be32(rbuf); ++ return 0; ++} ++ ++/* Raw-I2C write: 4 BE address bytes followed by 4 BE data bytes. */ ++static int kb9002_i2c_write(struct i2c_client *client, u32 reg, u32 val) ++{ ++ u8 buf[8]; ++ struct i2c_msg msg = { ++ .addr = client->addr, ++ .flags = 0, ++ .len = sizeof(buf), ++ .buf = buf, ++ }; ++ int ret; ++ ++ put_unaligned_be32(reg, &buf[0]); ++ put_unaligned_be32(val, &buf[4]); ++ ++ ret = i2c_transfer(client->adapter, &msg, 1); ++ if (ret < 0) ++ return ret; ++ if (ret != 1) ++ return -EIO; ++ ++ return 0; ++} ++ ++/* ++ * Read the silicon revision ID. A fresh FIFO may start with a stray ++ * 0xff that shifts the result, so drain one byte between retries until ++ * the top byte is no longer 0xff. ++ */ ++static int kb9002_read_revid(struct i2c_client *client, u32 *revid) ++{ ++ u8 dummy; ++ int ret; ++ int i; ++ ++ for (i = 0; i < KB9002_REVID_READ_RETRIES; i++) { ++ ret = kb9002_i2c_read(client, KB9002_HW_REG_REVID, revid); ++ if (ret) ++ return ret; ++ if ((*revid >> 24) != 0xff) ++ return 0; ++ /* Drain one byte from the chip to re-align the I2C FIFO. */ ++ i2c_master_recv(client, &dummy, 1); ++ } ++ ++ return -EIO; ++} ++ ++/* ++ * Read a 32-bit firmware register over SMBus: block-write the 16-bit LE ++ * address, then block-read the echoed address plus 4 LE data bytes. ++ */ ++static int kb9002_fw_read(struct kb9002_data *data, u16 reg, u32 *val) ++{ ++ struct i2c_client *client = data->client; ++ u8 addr[2]; ++ u8 rbuf[I2C_SMBUS_BLOCK_MAX]; ++ int ret; ++ ++ put_unaligned_le16(reg, addr); ++ ++ ret = i2c_smbus_write_block_data(client, KB9002_CC_FW_READ_PRIME, ++ sizeof(addr), addr); ++ if (ret < 0) ++ return ret; ++ ++ ret = i2c_smbus_read_block_data(client, KB9002_CC_FW_READ_DATA, rbuf); ++ if (ret < 0) ++ return ret; ++ if (ret < (int)(sizeof(addr) + sizeof(*val))) ++ return -EIO; ++ ++ *val = get_unaligned_le32(&rbuf[sizeof(addr)]); ++ return 0; ++} ++ ++/* Like kb9002_fw_read but for a hardware register (32-bit LE address). */ ++static int kb9002_smbus_hw_read(struct kb9002_data *data, u32 reg, u32 *val) ++{ ++ struct i2c_client *client = data->client; ++ u8 addr[4]; ++ u8 rbuf[I2C_SMBUS_BLOCK_MAX]; ++ int ret; ++ ++ put_unaligned_le32(reg, addr); ++ ++ ret = i2c_smbus_write_block_data(client, KB9002_CC_HW_READ_PRIME, ++ sizeof(addr), addr); ++ if (ret < 0) ++ return ret; ++ ++ ret = i2c_smbus_read_block_data(client, KB9002_CC_HW_READ_DATA, rbuf); ++ if (ret < 0) ++ return ret; ++ if (ret < (int)(sizeof(addr) + sizeof(*val))) ++ return -EIO; ++ ++ *val = get_unaligned_le32(&rbuf[sizeof(addr)]); ++ return 0; ++} ++ ++/* ++ * Switch the host interface from raw-I2C to SMBus and wait for firmware ++ * to come back up. Called only when SMBus access failed in probe, i.e. ++ * the chip is strapped to raw-I2C mode. Confirms the revision, sets the ++ * SMBus-mode bit, then polls until firmware responds again. ++ */ ++static int kb9002_enable_smbus_target(struct kb9002_data *data) ++{ ++ struct i2c_client *client = data->client; ++ u32 revid; ++ u32 val; ++ int op_ret; ++ int ret; ++ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) ++ return dev_err_probe(&client->dev, -ENODEV, ++ "raw I2C required to switch to SMBus mode\n"); ++ ++ ret = kb9002_read_revid(client, &revid); ++ if (ret) ++ return dev_err_probe(&client->dev, ret, ++ "revision ID read failed\n"); ++ ++ switch (FIELD_GET(KB9002_REVID_MASK, revid)) { ++ case KB9002_REVID_B0: ++ case KB9002_REVID_B1: ++ break; ++ default: ++ return dev_err_probe(&client->dev, -ENODEV, ++ "unsupported revision ID 0x%08x\n", revid); ++ } ++ ++ ret = kb9002_i2c_read(client, KB9002_HW_REG_HOST_IF, &val); ++ if (ret) ++ return dev_err_probe(&client->dev, ret, ++ "host interface read failed\n"); ++ ++ val |= KB9002_HOST_IF_SMBUS; ++ ++ ret = kb9002_i2c_write(client, KB9002_HW_REG_HOST_IF, val); ++ if (ret) ++ return dev_err_probe(&client->dev, ret, ++ "host interface write failed\n"); ++ ++ /* Wait until firmware re-initialisation completes. */ ++ ret = read_poll_timeout(kb9002_fw_read, op_ret, op_ret == 0, ++ KB9002_FW_READY_POLL_US, ++ KB9002_FW_READY_TIMEOUT_US, true, ++ data, KB9002_FW_REG_VID, &val); ++ if (ret) ++ return dev_err_probe(&client->dev, ret, ++ "firmware not responding over SMBus\n"); ++ ++ return 0; ++} ++ ++/* Convert Q16.16 absolute Kelvin to millidegrees Celsius. */ ++static long kb9002_temp_to_milli_c(u32 raw) ++{ ++ s64 milli_k = ((s64)raw * 1000) >> KB9002_TEMP_FRAC_BITS; ++ ++ return (long)milli_k + KB9002_ABS_ZERO_MILLI_C; ++} ++ ++static int kb9002_read_temp(struct kb9002_data *data, long *val) ++{ ++ u32 raw; ++ int ret; ++ ++ ret = kb9002_fw_read(data, KB9002_FW_REG_TEMP_MAXIMUM, &raw); ++ if (ret) ++ return ret; ++ ++ *val = kb9002_temp_to_milli_c(raw); ++ return 0; ++} ++ ++static umode_t kb9002_is_visible(const void *drvdata, ++ enum hwmon_sensor_types type, ++ u32 attr, int channel) ++{ ++ return 0444; ++} ++ ++static int kb9002_read(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, long *val) ++{ ++ struct kb9002_data *data = dev_get_drvdata(dev); ++ ++ if (type == hwmon_temp && attr == hwmon_temp_input) ++ return kb9002_read_temp(data, val); ++ ++ return -EOPNOTSUPP; ++} ++ ++static const struct hwmon_ops kb9002_hwmon_ops = { ++ .is_visible = kb9002_is_visible, ++ .read = kb9002_read, ++}; ++ ++static const struct hwmon_channel_info * const kb9002_hwmon_info[] = { ++ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), ++ NULL, ++}; ++ ++static const struct hwmon_chip_info kb9002_chip_info = { ++ .ops = &kb9002_hwmon_ops, ++ .info = kb9002_hwmon_info, ++}; ++ ++static int kb9002_fw_version_show(struct seq_file *s, void *unused) ++{ ++ struct kb9002_data *data = s->private; ++ u32 ver; ++ int ret; ++ ++ guard(hwmon_lock)(data->hwmon_dev); ++ ++ ret = kb9002_fw_read(data, KB9002_FW_REG_FW_VERSION, &ver); ++ if (ret) ++ return ret; ++ ++ seq_printf(s, "%u.%02u.%02u.%u\n", ++ (ver >> 24) & 0xff, (ver >> 16) & 0xff, ++ (ver >> 8) & 0xff, (ver >> 0) & 0xff); ++ return 0; ++} ++DEFINE_SHOW_ATTRIBUTE(kb9002_fw_version); ++ ++static int kb9002_fw_load_status_show(struct seq_file *s, void *unused) ++{ ++ struct kb9002_data *data = s->private; ++ u32 status; ++ int ret; ++ ++ guard(hwmon_lock)(data->hwmon_dev); ++ ++ ret = kb9002_smbus_hw_read(data, KB9002_HW_REG_FW_BOOT_STATUS, &status); ++ if (ret) ++ return ret; ++ ++ seq_printf(s, "%s\n", ++ (status >> 24) == KB9002_FW_BOOT_STATUS_OK_MSB ? ++ "normal" : "abnormal"); ++ return 0; ++} ++DEFINE_SHOW_ATTRIBUTE(kb9002_fw_load_status); ++ ++static void kb9002_debugfs_init(struct kb9002_data *data) ++{ ++ struct dentry *dir = data->client->debugfs; ++ ++ debugfs_create_file("fw_ver", 0444, dir, data, ++ &kb9002_fw_version_fops); ++ debugfs_create_file("fw_load_status", 0444, dir, data, ++ &kb9002_fw_load_status_fops); ++} ++ ++static int kb9002_probe(struct i2c_client *client) ++{ ++ struct device *dev = &client->dev; ++ struct kb9002_data *data; ++ struct device *hwmon_dev; ++ u32 vid; ++ int ret; ++ ++ if (!i2c_check_functionality(client->adapter, ++ I2C_FUNC_SMBUS_BLOCK_DATA | ++ I2C_FUNC_SMBUS_PEC)) ++ return -ENODEV; ++ ++ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ data->client = client; ++ ++ /* All firmware register accesses are PEC-protected. */ ++ client->flags |= I2C_CLIENT_PEC; ++ ++ i2c_set_clientdata(client, data); ++ ++ /* ++ * Try SMBus first. If the chip is strapped to raw-I2C mode it ++ * will not respond to SMBus framing, so fall back to switching ++ * the host interface over raw I2C and retry. ++ */ ++ ret = kb9002_fw_read(data, KB9002_FW_REG_VID, &vid); ++ if (ret) { ++ dev_dbg(dev, "SMBus probe failed (%d), trying raw-I2C host-interface switch\n", ++ ret); ++ ret = kb9002_enable_smbus_target(data); ++ if (ret) ++ return ret; ++ ret = kb9002_fw_read(data, KB9002_FW_REG_VID, &vid); ++ if (ret) ++ return dev_err_probe(dev, ret, ++ "VID read failed after host-interface switch\n"); ++ } ++ if (FIELD_GET(KB9002_VID_MASK, vid) != KB9002_VID_KANDOU) ++ return dev_err_probe(dev, -ENODEV, ++ "unexpected VID 0x%08x\n", vid); ++ ++ hwmon_dev = devm_hwmon_device_register_with_info(dev, KB9002_DEV_NAME, ++ data, ++ &kb9002_chip_info, ++ NULL); ++ if (IS_ERR(hwmon_dev)) ++ return PTR_ERR(hwmon_dev); ++ ++ data->hwmon_dev = hwmon_dev; ++ kb9002_debugfs_init(data); ++ return 0; ++} ++ ++static const struct i2c_device_id kb9002_id[] = { ++ { .name = KB9002_DEV_NAME }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, kb9002_id); ++ ++static const struct of_device_id kb9002_of_match[] = { ++ { .compatible = "kandou,kb9002" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, kb9002_of_match); ++ ++static struct i2c_driver kb9002_driver = { ++ .driver = { ++ .name = KB9002_DEV_NAME, ++ .of_match_table = kb9002_of_match, ++ }, ++ .probe = kb9002_probe, ++ .id_table = kb9002_id, ++}; ++module_i2c_driver(kb9002_driver); ++ ++MODULE_AUTHOR("Andy Chung "); ++MODULE_DESCRIPTION("Kandou KB9002 PCIe retimer hwmon driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c +--- a/drivers/hwmon/lm90.c ++++ b/drivers/hwmon/lm90.c +@@ -101,6 +101,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -108,7 +109,7 @@ + #include + #include + #include +-#include ++#include + #include + #include + #include +@@ -295,7 +296,7 @@ static const struct i2c_device_id lm90_id[] = { + }; + MODULE_DEVICE_TABLE(i2c, lm90_id); + +-static const struct of_device_id __maybe_unused lm90_of_match[] = { ++static const struct of_device_id lm90_of_match[] = { + { + .compatible = "adi,adm1032", + .data = (void *)adm1032 +@@ -2602,7 +2603,6 @@ static void lm90_stop_work(void *_data) + + static int lm90_init_client(struct i2c_client *client, struct lm90_data *data) + { +- struct device_node *np = client->dev.of_node; + int config, convrate; + + if (data->flags & LM90_HAVE_CONVRATE) { +@@ -2626,7 +2626,7 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data) + + /* Check Temperature Range Select */ + if (data->flags & LM90_HAVE_EXTENDED_TEMP) { +- if (of_property_read_bool(np, "ti,extended-range-enable")) ++ if (device_property_read_bool(&client->dev, "ti,extended-range-enable")) + config |= 0x04; + if (!(config & 0x04)) + data->flags &= ~LM90_HAVE_EXTENDED_TEMP; +@@ -2692,36 +2692,41 @@ static irqreturn_t lm90_irq_thread(int irq, void *dev_id) + return IRQ_NONE; + } + +-static int lm90_probe_channel_from_dt(struct i2c_client *client, +- struct device_node *child, +- struct lm90_data *data) ++static int lm90_probe_channel(struct i2c_client *client, ++ struct fwnode_handle *child, ++ struct lm90_data *data) + { + u32 id; + s32 val; + int err; + struct device *dev = &client->dev; + +- err = of_property_read_u32(child, "reg", &id); ++ err = fwnode_property_read_u32(child, "reg", &id); + if (err) { +- dev_err(dev, "missing reg property of %pOFn\n", child); ++ dev_err(dev, "missing reg property of %pfw\n", child); + return err; + } + + if (id >= MAX_CHANNELS) { +- dev_err(dev, "invalid reg property value %d in %pOFn\n", id, child); ++ dev_err(dev, "invalid reg property value %d in %pfw\n", id, child); + return -EINVAL; + } + +- err = of_property_read_string(child, "label", &data->channel_label[id]); ++ err = fwnode_property_read_string(child, "label", &data->channel_label[id]); + if (err == -ENODATA || err == -EILSEQ) { +- dev_err(dev, "invalid label property in %pOFn\n", child); ++ dev_err(dev, "invalid label property in %pfw\n", child); + return err; + } + + if (data->channel_label[id]) + data->channel_config[id] |= HWMON_T_LABEL; + +- err = of_property_read_s32(child, "temperature-offset-millicelsius", &val); ++ /* ++ * fwnode_property_read_u32() has no signed equivalent. ++ * temperature-offset-millicelsius is signed, so read and reinterpret it as s32 to ++ * preserve negative offsets values (same behavior as the old of_property_read_s32()). ++ */ ++ err = fwnode_property_read_u32(child, "temperature-offset-millicelsius", (u32 *)&val); + if (!err) { + if (id == 0) { + dev_err(dev, "temperature-offset-millicelsius can't be set for internal channel\n"); +@@ -2739,18 +2744,17 @@ static int lm90_probe_channel_from_dt(struct i2c_client *client, + return 0; + } + +-static int lm90_parse_dt_channel_info(struct i2c_client *client, +- struct lm90_data *data) ++static int lm90_parse_channel_info(struct i2c_client *client, ++ struct lm90_data *data) + { + int err; + struct device *dev = &client->dev; +- const struct device_node *np = dev->of_node; + +- for_each_child_of_node_scoped(np, child) { +- if (strcmp(child->name, "channel")) ++ device_for_each_child_node_scoped(dev, child) { ++ if (!fwnode_name_eq(child, "channel")) + continue; + +- err = lm90_probe_channel_from_dt(client, child, data); ++ err = lm90_probe_channel(client, child, data); + if (err) + return err; + } +@@ -2887,12 +2891,10 @@ static int lm90_probe(struct i2c_client *client) + /* Set maximum conversion rate */ + data->max_convrate = lm90_params[data->kind].max_convrate; + +- /* Parse device-tree channel information */ +- if (client->dev.of_node) { +- err = lm90_parse_dt_channel_info(client, data); +- if (err) +- return err; +- } ++ /* Parse channel information */ ++ err = lm90_parse_channel_info(client, data); ++ if (err) ++ return err; + + /* Initialize the LM90 chip */ + err = lm90_init_client(client, data); +@@ -2918,10 +2920,8 @@ static int lm90_probe(struct i2c_client *client) + err = devm_request_threaded_irq(dev, client->irq, + NULL, lm90_irq_thread, + IRQF_ONESHOT, "lm90", client); +- if (err < 0) { +- dev_err(dev, "cannot request IRQ %d\n", client->irq); ++ if (err < 0) + return err; +- } + } + + return 0; +@@ -2985,7 +2985,7 @@ static struct i2c_driver lm90_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "lm90", +- .of_match_table = of_match_ptr(lm90_of_match), ++ .of_match_table = lm90_of_match, + .pm = pm_sleep_ptr(&lm90_pm_ops), + }, + .probe = lm90_probe, +diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c +--- a/drivers/hwmon/nct6775-core.c ++++ b/drivers/hwmon/nct6775-core.c +@@ -33,6 +33,7 @@ + * (0xd451) + * nct6798d 14 7 7 2+6 0xd428 0xc1 0x5ca3 + * (0xd429) ++ * nct5585d 14 7 7 2+6 0xd428 0xc1 0x5ca3 + * nct6796d-s 18 7 7 6+2 0xd801 0xc1 0x5ca3 + * nct6799d-r 18 7 7 6+2 0xd802 0xc1 0x5ca3 + * +diff --git a/drivers/hwmon/nct6775-platform.c b/drivers/hwmon/nct6775-platform.c +--- a/drivers/hwmon/nct6775-platform.c ++++ b/drivers/hwmon/nct6775-platform.c +@@ -34,7 +34,7 @@ static const char * const nct6775_sio_names[] __initconst = { + [nct6795] = "NCT6795D", + [nct6796] = "NCT6796D", + [nct6797] = "NCT6797D", +- [nct6798] = "NCT6798D", ++ [nct6798] = "NCT6798D/NCT5585D", + [nct6799] = "NCT6796D-S/NCT6799D-R", + }; + +diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c +--- a/drivers/hwmon/npcm750-pwm-fan.c ++++ b/drivers/hwmon/npcm750-pwm-fan.c +@@ -996,10 +996,8 @@ static int npcm7xx_pwm_fan_probe(struct platform_device *pdev) + sprintf(name, "NPCM7XX-FAN-MD%d", i); + ret = devm_request_irq(dev, data->fan_irq[i], npcm7xx_fan_isr, + 0, name, (void *)data); +- if (ret) { +- dev_err(dev, "register IRQ fan%d failed\n", i); ++ if (ret) + return ret; +- } + } + + for_each_child_of_node_scoped(np, child) { +diff --git a/drivers/hwmon/nzxt-kraken3.c b/drivers/hwmon/nzxt-kraken3.c +--- a/drivers/hwmon/nzxt-kraken3.c ++++ b/drivers/hwmon/nzxt-kraken3.c +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: GPL-2.0+ + /* +- * hwmon driver for NZXT Kraken X53/X63/X73, Z53/Z63/Z73 and 2023/2023 Elite all in one coolers. ++ * hwmon driver for NZXT Kraken X53/X63/X73, Z53/Z63/Z73, 2023/2023 Elite, and ++ * 2024 Elite all in one coolers. + * X53 and Z53 in code refer to all models in their respective series (shortened for brevity). + * 2023 models use the Z53 code paths. + * +@@ -25,6 +26,7 @@ + #define USB_PRODUCT_ID_Z53 0x3008 + #define USB_PRODUCT_ID_KRAKEN2023 0x300E + #define USB_PRODUCT_ID_KRAKEN2023_ELITE 0x300C ++#define USB_PRODUCT_ID_KRAKEN2024_ELITE 0x3012 + + enum kinds { X53, Z53, KRAKEN2023 } __packed; + enum pwm_enable { off, manual, curve } __packed; +@@ -927,6 +929,10 @@ static int kraken3_probe(struct hid_device *hdev, const struct hid_device_id *id + priv->kind = KRAKEN2023; + device_name = "kraken2023elite"; + break; ++ case USB_PRODUCT_ID_KRAKEN2024_ELITE: ++ priv->kind = KRAKEN2023; ++ device_name = "kraken2024elite"; ++ break; + default: + ret = -ENODEV; + goto fail_and_close; +@@ -994,6 +1000,7 @@ static const struct hid_device_id kraken3_table[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_NZXT, USB_PRODUCT_ID_Z53) }, + { HID_USB_DEVICE(USB_VENDOR_ID_NZXT, USB_PRODUCT_ID_KRAKEN2023) }, + { HID_USB_DEVICE(USB_VENDOR_ID_NZXT, USB_PRODUCT_ID_KRAKEN2023_ELITE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_NZXT, USB_PRODUCT_ID_KRAKEN2024_ELITE) }, + { } + }; + +diff --git a/drivers/hwmon/peci/common.h b/drivers/hwmon/peci/common.h +--- a/drivers/hwmon/peci/common.h ++++ b/drivers/hwmon/peci/common.h +@@ -31,7 +31,7 @@ struct peci_sensor_data { + + /** + * peci_sensor_need_update() - check whether sensor update is needed or not +- * @sensor: pointer to sensor data struct ++ * @state: pointer to sensor state struct + * + * Return: true if update is needed, false if not. + */ +@@ -44,7 +44,7 @@ static inline bool peci_sensor_need_update(struct peci_sensor_state *state) + + /** + * peci_sensor_mark_updated() - mark the sensor is updated +- * @sensor: pointer to sensor data struct ++ * @state: pointer to sensor state struct + */ + static inline void peci_sensor_mark_updated(struct peci_sensor_state *state) + { +diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig +--- a/drivers/hwmon/pmbus/Kconfig ++++ b/drivers/hwmon/pmbus/Kconfig +@@ -52,8 +52,8 @@ config SENSORS_ADM1275 + help + If you say yes here you get hardware monitoring support for Analog + Devices ADM1075, ADM1272, ADM1273, ADM1275, ADM1276, ADM1278, ADM1281, +- ADM1293, ADM1294 and SQ24905C Hot-Swap Controller and +- Digital Power Monitors. ++ ADM1293, ADM1294, ROHM BD12780, ROHM BD12790, and SQ24905C ++ Hot-Swap Controller and Digital Power Monitors. + + This driver can also be built as a module. If so, the module will + be called adm1275. +@@ -394,10 +394,10 @@ config SENSORS_MAX20751 + be called max20751. + + config SENSORS_MAX20830 +- tristate "Analog Devices MAX20830" ++ tristate "Analog Devices MAX20830 and compatibles" + help + If you say yes here you get hardware monitoring support for Analog +- Devices MAX20830. ++ Devices MAX20830, MAX20830C, and MAX20840C. + + This driver can also be built as a module. If so, the module will + be called max20830. +@@ -434,8 +434,9 @@ config SENSORS_MAX34440 + tristate "Maxim MAX34440 and compatibles" + help + If you say yes here you get hardware monitoring support for Maxim +- MAX34440, MAX34441, MAX34446, MAX34451, MAX34460, and MAX34461. +- Other compatible are ADPM12160, and ADPM12200. ++ MAX34440, MAX34441, MAX34446, MAX34451, MAX34452, MAX34460, and ++ MAX34461. Other compatible devices are ADPM12160, ADPM12200, and ++ ADPM12250. + + This driver can also be built as a module. If so, the module will + be called max34440. +@@ -607,6 +608,15 @@ config SENSORS_MPQ7932 + This driver can also be built as a module. If so, the module will + be called mpq7932. + ++config SENSORS_MPQ82D00 ++ tristate "MPS MPQ82D00" ++ help ++ If you say yes here you get hardware monitoring functionality support ++ for power management IC MPS MPQ82D00. ++ ++ This driver can also be built as a module. If so, the module will ++ be called mpq82d00. ++ + config SENSORS_MPQ8785 + tristate "MPS MPQ8785" + help +@@ -616,6 +626,37 @@ config SENSORS_MPQ8785 + This driver can also be built as a module. If so, the module will + be called mpq8785. + ++config SENSORS_MPQ8646 ++ tristate "MPS MPQ8646" ++ depends on REGULATOR || !REGULATOR ++ depends on NVMEM || !NVMEM ++ help ++ If you say yes here you get hardware monitoring support for the ++ Monolithic Power Systems MPQ8646. ++ ++ This driver can also be built as a module. If so, the module ++ will be called mpq8646. ++ ++config SENSORS_MPQ8646_DEBUG_UNSAFE ++ bool "MPQ8646 unsafe write/provisioning debugfs (DANGEROUS)" ++ depends on SENSORS_MPQ8646 && DEBUG_FS ++ default n ++ help ++ Expose additional WRITE-able debugfs files in the client's ++ pmbus debugfs directory, ++ /sys/kernel/debug/i2c/i2c-/-/ ++ ++ These are provisioning and bring-up aids. On many designs the ++ MPQ8646 powers the SoC core rail, so a wrong write can brown out ++ or permanently mis-provision the board. ++ ++ WARNING: Wrong register writes can and likely will physically ++ damage or destroy the chip and/or the board. ++ ++ It shall never be set in a production, shipping, or default configuration. ++ ++ If unsure, say N. ++ + config SENSORS_PIM4328 + tristate "Flex PIM4328 and compatibles" + help +@@ -677,6 +718,25 @@ config SENSORS_STEF48H28 + This driver can also be built as a module. If so, the module will + be called stef48h28. + ++config SENSORS_SQ24860 ++ tristate "Silergy SQ24860" ++ help ++ If you say yes here you get hardware monitoring support for Silergy ++ SQ24860 eFuse. ++ ++ This driver can also be built as a module. If so, the module will ++ be called sq24860. ++ ++config SENSORS_SQ24860_REGULATOR ++ bool "Regulator support for SQ24860" ++ depends on SENSORS_SQ24860 && REGULATOR ++ default SENSORS_SQ24860 ++ help ++ If you say yes here you get regulator support for Silergy SQ24860. ++ The regulator is registered through the PMBus regulator framework and ++ can be used to control the output exposed by the device. ++ This option is only useful if regulator framework support is needed. ++ + config SENSORS_STPDDC60 + tristate "ST STPDDC60" + help +@@ -767,6 +827,15 @@ config SENSORS_UCD9200 + This driver can also be built as a module. If so, the module will + be called ucd9200. + ++config SENSORS_VT7505 ++ tristate "Analog Devices MAX16545, MAX16550 and Volterra VT7505" ++ help ++ If you say yes here you get hardware monitoring support for Analog ++ Devices MAX16545, MAX16550 and Volterra VT7505 PMBus controllers. ++ ++ This driver can also be built as a module. If so, the module will ++ be called vt7505. ++ + config SENSORS_XDP710 + tristate "Infineon XDP710 family" + help +@@ -803,6 +872,16 @@ config SENSORS_XDPE1A2G7B + This driver can also be built as a module. If so, the module will + be called xdpe1a2g7b. + ++config SENSORS_XDPE1A2G7B_REGULATOR ++ bool "Regulator support for XDPE1A2G7B and compatibles" ++ depends on SENSORS_XDPE1A2G7B && REGULATOR ++ help ++ If you say yes here you get regulator support for Infineon ++ XDPE1A2G5B and XDPE1A2G7B multi-phase digital controllers. ++ ++ This enables the controllers to be used as regulator devices, ++ providing voltage control through the regulator framework. ++ + config SENSORS_XDPE122 + tristate "Infineon XDPE122 family" + help +diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile +--- a/drivers/hwmon/pmbus/Makefile ++++ b/drivers/hwmon/pmbus/Makefile +@@ -59,12 +59,15 @@ obj-$(CONFIG_SENSORS_MP5990) += mp5990.o + obj-$(CONFIG_SENSORS_MP9941) += mp9941.o + obj-$(CONFIG_SENSORS_MP9945) += mp9945.o + obj-$(CONFIG_SENSORS_MPQ7932) += mpq7932.o ++obj-$(CONFIG_SENSORS_MPQ82D00) += mpq82d00.o + obj-$(CONFIG_SENSORS_MPQ8785) += mpq8785.o ++obj-$(CONFIG_SENSORS_MPQ8646) += mpq8646.o + obj-$(CONFIG_SENSORS_PLI1209BC) += pli1209bc.o + obj-$(CONFIG_SENSORS_PM6764TR) += pm6764tr.o + obj-$(CONFIG_SENSORS_PXE1610) += pxe1610.o + obj-$(CONFIG_SENSORS_Q54SJ108A2) += q54sj108a2.o + obj-$(CONFIG_SENSORS_STEF48H28) += stef48h28.o ++obj-$(CONFIG_SENSORS_SQ24860) += sq24860.o + obj-$(CONFIG_SENSORS_STPDDC60) += stpddc60.o + obj-$(CONFIG_SENSORS_TDA38640) += tda38640.o + obj-$(CONFIG_SENSORS_TPS25990) += tps25990.o +@@ -73,6 +76,7 @@ obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o + obj-$(CONFIG_SENSORS_TPS546D24) += tps546d24.o + obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o + obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o ++obj-$(CONFIG_SENSORS_VT7505) += vt7505.o + obj-$(CONFIG_SENSORS_XDP710) += xdp710.o + obj-$(CONFIG_SENSORS_XDP720) += xdp720.o + obj-$(CONFIG_SENSORS_XDPE122) += xdpe12284.o +diff --git a/drivers/hwmon/pmbus/acbel-fsg032.c b/drivers/hwmon/pmbus/acbel-fsg032.c +--- a/drivers/hwmon/pmbus/acbel-fsg032.c ++++ b/drivers/hwmon/pmbus/acbel-fsg032.c +@@ -52,6 +52,7 @@ static const struct i2c_device_id acbel_fsg032_id[] = { + { .name = "acbel_fsg032" }, + { } + }; ++MODULE_DEVICE_TABLE(i2c, acbel_fsg032_id); + + static struct pmbus_driver_info acbel_fsg032_info = { + .pages = 1, +diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c +--- a/drivers/hwmon/pmbus/adm1275.c ++++ b/drivers/hwmon/pmbus/adm1275.c +@@ -19,7 +19,7 @@ + #include "pmbus.h" + + enum chips { adm1075, adm1272, adm1273, adm1275, adm1276, adm1278, adm1281, +- adm1293, adm1294, sq24905c }; ++ adm1293, adm1294, bd12780, bd12790, sq24905c }; + + #define ADM1275_MFR_STATUS_IOUT_WARN2 BIT(0) + #define ADM1293_MFR_STATUS_VAUX_UV_WARN BIT(5) +@@ -47,6 +47,8 @@ enum chips { adm1075, adm1272, adm1273, adm1275, adm1276, adm1278, adm1281, + #define ADM1278_VOUT_EN BIT(1) + + #define ADM1278_PMON_DEFCONFIG (ADM1278_VOUT_EN | ADM1278_TEMP1_EN | ADM1278_TSFILT) ++/* The BD127[89]0 data sheets mark TSFILT bit as reserved. */ ++#define BD12780_PMON_DEFCONFIG (ADM1278_VOUT_EN | ADM1278_TEMP1_EN) + + #define ADM1293_IRANGE_25 0 + #define ADM1293_IRANGE_50 BIT(6) +@@ -134,6 +136,30 @@ static const struct coefficients adm1272_coefficients[] = { + + }; + ++/* ++ * BD12790 coefficients derived from preliminary datasheet, Table 1 (p.18) ++ * and the PMBus direct-format relationship X = (Y * 10^(-R) - b) / m. ++ * ++ * Voltage: V[V] = 14.77e-3 * code (60V) / 24.62e-3 * code (100V) ++ * -> m = 6770, R=-2 / m = 4062, R=-2 ++ * Current: code = I[A] * RS * 132802.1 + 2048 (15mV) / * 66401.06 + 2048 (30mV) ++ * -> m = 1328, b = 2048 * 10^(-R) = 20480, R=-1 / m = 664, same b and R ++ * Power: code = k * RS * PIN, k = 35119.94 / 17559.97 / 21071.44 / 10535.72 ++ * -> m = round(k * 10^(-3-R)), R=-2 for 60V/15mV, R=-3 for the other three ++ * Temperature: code = 4.2 * T + 3188 -> m = 42, b = 3188 * 10 = 31880, R=-1 ++ */ ++static const struct coefficients bd12790_coefficients[] = { ++ [0] = { 6770, 0, -2 }, /* voltage, vrange 60V */ ++ [1] = { 4062, 0, -2 }, /* voltage, vrange 100V */ ++ [2] = { 1328, 20480, -1 }, /* current, vsense range 15mV */ ++ [3] = { 664, 20480, -1 }, /* current, vsense range 30mV */ ++ [4] = { 3512, 0, -2 }, /* power, vrange 60V, irange 15mV */ ++ [5] = { 21071, 0, -3 }, /* power, vrange 100V, irange 15mV */ ++ [6] = { 17560, 0, -3 }, /* power, vrange 60V, irange 30mV */ ++ [7] = { 10536, 0, -3 }, /* power, vrange 100V, irange 30mV */ ++ [8] = { 42, 31880, -1 }, /* temperature */ ++}; ++ + static const struct coefficients adm1275_coefficients[] = { + [0] = { 19199, 0, -2 }, /* voltage, vrange set */ + [1] = { 6720, 0, -1 }, /* voltage, vrange not set */ +@@ -487,6 +513,8 @@ static const struct i2c_device_id adm1275_id[] = { + { .name = "adm1281", .driver_data = adm1281 }, + { .name = "adm1293", .driver_data = adm1293 }, + { .name = "adm1294", .driver_data = adm1294 }, ++ { .name = "bd12780", .driver_data = bd12780 }, ++ { .name = "bd12790", .driver_data = bd12790 }, + { .name = "mc09c", .driver_data = sq24905c }, + { } + }; +@@ -494,12 +522,13 @@ MODULE_DEVICE_TABLE(i2c, adm1275_id); + + /* Enable VOUT & TEMP1 if not enabled (disabled by default) */ + static int adm1275_enable_vout_temp(struct adm1275_data *data, +- struct i2c_client *client, int config) ++ struct i2c_client *client, int config, ++ u16 defconfig) + { + int ret; + +- if ((config & ADM1278_PMON_DEFCONFIG) != ADM1278_PMON_DEFCONFIG) { +- config |= ADM1278_PMON_DEFCONFIG; ++ if ((config & defconfig) != defconfig) { ++ config |= defconfig; + ret = adm1275_write_pmon_config(data, client, config); + if (ret < 0) { + dev_err(&client->dev, "Failed to enable VOUT/TEMP1 monitoring\n"); +@@ -524,23 +553,19 @@ static int adm1275_probe(struct i2c_client *client) + u32 shunt; + u32 avg; + +- if (!i2c_check_functionality(client->adapter, +- I2C_FUNC_SMBUS_READ_BYTE_DATA +- | I2C_FUNC_SMBUS_BLOCK_DATA)) +- return -ENODEV; +- +- ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, block_buffer); ++ ret = pmbus_read_smbus_i2c_block_data(client, PMBUS_MFR_ID, block_buffer); + if (ret < 0) { + dev_err(&client->dev, "Failed to read Manufacturer ID\n"); + return ret; + } + if ((ret != 3 || strncmp(block_buffer, "ADI", 3)) && +- (ret != 2 || strncmp(block_buffer, "SY", 2))) { ++ (ret != 2 || strncmp(block_buffer, "SY", 2)) && ++ (ret != 4 || strncmp(block_buffer, "ROHM", 4))) { + dev_err(&client->dev, "Unsupported Manufacturer ID\n"); + return -ENODEV; + } + +- ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, block_buffer); ++ ret = pmbus_read_smbus_i2c_block_data(client, PMBUS_MFR_MODEL, block_buffer); + if (ret < 0) { + dev_err(&client->dev, "Failed to read Manufacturer Model\n"); + return ret; +@@ -562,6 +587,7 @@ static int adm1275_probe(struct i2c_client *client) + if (mid->driver_data == adm1272 || mid->driver_data == adm1273 || + mid->driver_data == adm1278 || mid->driver_data == adm1281 || + mid->driver_data == adm1293 || mid->driver_data == adm1294 || ++ mid->driver_data == bd12780 || mid->driver_data == bd12790 || + mid->driver_data == sq24905c) + config_read_fn = i2c_smbus_read_word_data; + else +@@ -642,6 +668,7 @@ static int adm1275_probe(struct i2c_client *client) + data->have_power_sampling = true; + + coefficients = adm1272_coefficients; ++ + vindex = (config & ADM1275_VRANGE) ? 1 : 0; + cindex = (config & ADM1272_IRANGE) ? 3 : 2; + /* pindex depends on the combination of the above */ +@@ -666,7 +693,53 @@ static int adm1275_probe(struct i2c_client *client) + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + +- ret = adm1275_enable_vout_temp(data, client, config); ++ ret = adm1275_enable_vout_temp(data, client, config, ++ ADM1278_PMON_DEFCONFIG); ++ if (ret) ++ return ret; ++ ++ if (config & ADM1278_VIN_EN) ++ info->func[0] |= PMBUS_HAVE_VIN; ++ break; ++ ++ /* ++ * The BD12790 is almost identical to the adm1272. Only the defconfig ++ * and coefficients have minor differences. ++ */ ++ case bd12790: ++ data->have_vout = true; ++ data->have_pin_max = true; ++ data->have_temp_max = true; ++ data->have_power_sampling = true; ++ ++ coefficients = bd12790_coefficients; ++ ++ vindex = (config & ADM1275_VRANGE) ? 1 : 0; ++ cindex = (config & ADM1272_IRANGE) ? 3 : 2; ++ /* pindex depends on the combination of the above */ ++ switch (config & (ADM1275_VRANGE | ADM1272_IRANGE)) { ++ case 0: ++ default: ++ pindex = 4; ++ break; ++ case ADM1275_VRANGE: ++ pindex = 5; ++ break; ++ case ADM1272_IRANGE: ++ pindex = 6; ++ break; ++ case ADM1275_VRANGE | ADM1272_IRANGE: ++ pindex = 7; ++ break; ++ } ++ tindex = 8; ++ ++ info->func[0] |= PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT | ++ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | ++ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; ++ ++ ret = adm1275_enable_vout_temp(data, client, config, ++ BD12780_PMON_DEFCONFIG); + if (ret) + return ret; + +@@ -728,13 +801,45 @@ static int adm1275_probe(struct i2c_client *client) + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + +- ret = adm1275_enable_vout_temp(data, client, config); ++ ret = adm1275_enable_vout_temp(data, client, config, ++ ADM1278_PMON_DEFCONFIG); + if (ret) + return ret; + + if (config & ADM1278_VIN_EN) + info->func[0] |= PMBUS_HAVE_VIN; + break; ++ ++ /* ++ * The BD12780 is almost functionally identical with the adm1278 above. ++ * Only differences visible to the driver are lack of TSFILT bits and ++ * different identification register contents. ++ */ ++ case bd12780: ++ data->have_vout = true; ++ data->have_pin_max = true; ++ data->have_temp_max = true; ++ data->have_power_sampling = true; ++ ++ coefficients = adm1278_coefficients; ++ vindex = 0; ++ cindex = 1; ++ pindex = 2; ++ tindex = 3; ++ ++ info->func[0] |= PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT | ++ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | ++ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; ++ ++ ret = adm1275_enable_vout_temp(data, client, config, ++ BD12780_PMON_DEFCONFIG); ++ if (ret) ++ return ret; ++ ++ if (config & ADM1278_VIN_EN) ++ info->func[0] |= PMBUS_HAVE_VIN; ++ ++ break; + case adm1293: + case adm1294: + data->have_iout_min = true; +@@ -870,9 +975,27 @@ static int adm1275_probe(struct i2c_client *client) + return pmbus_do_probe(client, info); + } + ++static const struct of_device_id adm1275_of_match[] = { ++ { .compatible = "adi,adm1075", }, ++ { .compatible = "adi,adm1272", }, ++ { .compatible = "adi,adm1273", }, ++ { .compatible = "adi,adm1275", }, ++ { .compatible = "adi,adm1276", }, ++ { .compatible = "adi,adm1278", }, ++ { .compatible = "adi,adm1281", }, ++ { .compatible = "adi,adm1293", }, ++ { .compatible = "adi,adm1294", }, ++ { .compatible = "rohm,bd12780", }, ++ { .compatible = "rohm,bd12790", }, ++ { .compatible = "silergy,mc09c", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, adm1275_of_match); ++ + static struct i2c_driver adm1275_driver = { + .driver = { + .name = "adm1275", ++ .of_match_table = adm1275_of_match, + }, + .probe = adm1275_probe, + .id_table = adm1275_id, +diff --git a/drivers/hwmon/pmbus/ir36021.c b/drivers/hwmon/pmbus/ir36021.c +--- a/drivers/hwmon/pmbus/ir36021.c ++++ b/drivers/hwmon/pmbus/ir36021.c +@@ -31,21 +31,13 @@ static int ir36021_probe(struct i2c_client *client) + u8 buf[I2C_SMBUS_BLOCK_MAX]; + int ret; + +- if (!i2c_check_functionality(client->adapter, +- I2C_FUNC_SMBUS_READ_BYTE_DATA +- | I2C_FUNC_SMBUS_READ_WORD_DATA +- | I2C_FUNC_SMBUS_READ_BLOCK_DATA)) +- return -ENODEV; +- +- ret = i2c_smbus_read_i2c_block_data(client, PMBUS_MFR_MODEL, 2, buf); +- if (ret < 0) { +- dev_err(&client->dev, "Failed to read PMBUS_MFR_MODEL\n"); +- return ret; +- } +- if (ret != 2 || buf[0] != 0x01 || buf[1] != 0x2d) { +- dev_err(&client->dev, "MFR_MODEL unrecognised\n"); +- return -ENODEV; +- } ++ ret = pmbus_read_smbus_i2c_block_data(client, PMBUS_MFR_MODEL, buf); ++ if (ret < 0) ++ return dev_err_probe(&client->dev, ret, ++ "Failed to read PMBUS_MFR_MODEL\n"); ++ if (ret != 1 || buf[0] != 0x2d) ++ return dev_err_probe(&client->dev, -ENODEV, ++ "MFR_MODEL unrecognised\n"); + + return pmbus_do_probe(client, &ir36021_info); + } +diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c +--- a/drivers/hwmon/pmbus/lm25066.c ++++ b/drivers/hwmon/pmbus/lm25066.c +@@ -35,6 +35,7 @@ enum chips { lm25056, lm25066, lm5064, lm5066, lm5066i }; + #define LM25066_READ_AVG_PIN 0xdf + + #define LM25066_DEV_SETUP_CL BIT(4) /* Current limit */ ++#define LM25066_DEV_SETUP_CL_CFG BIT(2) /* Current limit configuration */ + + #define LM25066_SAMPLES_FOR_AVG_MAX 4096 + +@@ -485,6 +486,42 @@ static int lm25066_probe(struct i2c_client *client) + + data->id = (enum chips)(unsigned long)i2c_get_match_data(client); + ++ if (data->id != lm25056) { ++ int config_new = config; ++ const char *cl_setting; ++ int ret; ++ ++ if (!of_property_read_string(client->dev.of_node, ++ "ti,current-range", &cl_setting)) { ++ config_new |= LM25066_DEV_SETUP_CL_CFG; ++ if (strcmp(cl_setting, "high") == 0) { ++ if (data->id == lm25066) ++ config_new |= LM25066_DEV_SETUP_CL; ++ else ++ config_new &= ~LM25066_DEV_SETUP_CL; ++ } else if (strcmp(cl_setting, "low") == 0) { ++ if (data->id == lm25066) ++ config_new &= ~LM25066_DEV_SETUP_CL; ++ else ++ config_new |= LM25066_DEV_SETUP_CL; ++ } else { ++ dev_err(&client->dev, ++ "invalid current-range setting: %s\n", ++ cl_setting); ++ return -EINVAL; ++ } ++ } ++ ++ if (config_new != config) { ++ ret = i2c_smbus_write_byte_data(client, ++ LM25066_DEVICE_SETUP, ++ config_new); ++ if (ret < 0) ++ return ret; ++ config = config_new; ++ } ++ } ++ + info = &data->info; + + info->pages = 1; +diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c +--- a/drivers/hwmon/pmbus/ltc2978.c ++++ b/drivers/hwmon/pmbus/ltc2978.c +@@ -611,17 +611,13 @@ static int ltc2978_get_id(struct i2c_client *client) + u8 buf[I2C_SMBUS_BLOCK_MAX]; + int ret; + +- if (!i2c_check_functionality(client->adapter, +- I2C_FUNC_SMBUS_READ_BLOCK_DATA)) +- return -ENODEV; +- +- ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf); ++ ret = pmbus_read_smbus_i2c_block_data(client, PMBUS_MFR_ID, buf); + if (ret < 0) + return ret; + if (ret < 3 || (strncmp(buf, "LTC", 3) && strncmp(buf, "ADI", 3))) + return -ENODEV; + +- ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf); ++ ret = pmbus_read_smbus_i2c_block_data(client, PMBUS_MFR_MODEL, buf); + if (ret < 0) + return ret; + for (id = <c2978_id[0]; strlen(id->name); id++) { +@@ -637,16 +633,17 @@ static int ltc2978_get_id(struct i2c_client *client) + u8 buf[I2C_SMBUS_BLOCK_MAX]; + int ret; + +- ret = i2c_smbus_read_i2c_block_data(client, PMBUS_IC_DEVICE_ID, +- sizeof(buf), buf); ++ ret = pmbus_read_smbus_i2c_block_data(client, PMBUS_IC_DEVICE_ID, ++ buf); + if (ret < 0) + return ret; + +- if (!strncmp(buf + 1, "LT7170", 6) || +- !strncmp(buf + 1, "LT7170-1", 8)) ++ if (ret < 6) ++ return -ENODEV; ++ ++ if (!strncmp(buf, "LT7170", 6)) + return lt7170; +- if (!strncmp(buf + 1, "LT7171", 6) || +- !strncmp(buf + 1, "LT7171-1", 8)) ++ if (!strncmp(buf, "LT7171", 6)) + return lt7171; + + return -ENODEV; +diff --git a/drivers/hwmon/pmbus/max20830.c b/drivers/hwmon/pmbus/max20830.c +--- a/drivers/hwmon/pmbus/max20830.c ++++ b/drivers/hwmon/pmbus/max20830.c +@@ -7,12 +7,69 @@ + + #include + #include ++#include + #include + #include + #include "pmbus.h" + + #define MAX20830_IC_DEVICE_ID_LENGTH 9 + ++struct max20830_data { ++ struct pmbus_driver_info info; ++ u32 vout_rfb1; ++ u32 vout_rfb2; ++}; ++ ++static const char * const supported_chip_ids[] = { ++ "MAX20830", ++ "MAX20830C", ++ "MAX20840C", ++}; ++ ++/* ++ * MAX20830 only supports READ_VOUT for VOUT monitoring. ++ * ++ * Limit registers (VOUT_OV_WARN_LIMIT, VOUT_OV_FAULT_LIMIT, etc.) are not ++ * supported by this driver and return -ENODATA. This means sysfs attributes ++ * like in1_max, in1_crit, etc. will not be available. Only in1_input (the ++ * scaled output voltage) is supported. ++ * ++ * MAX20830 uses an external resistor divider for voltage sensing: ++ * - VOUT_COMMAND and VOUT_MAX set the reference voltage at the feedback pin ++ * - READ_VOUT reports the feedback voltage, which needs to be scaled for actual ++ * output voltage ++ * ++ * Scaling formula: vout_actual = vout_fb × (1 + RFB1 / RFB2) ++ * ++ * If regulator support is added in the future, some adjustments are needed to ++ * ensure correct feedback voltages are set. ++ */ ++static int max20830_read_word_data(struct i2c_client *client, int page, ++ int phase, int reg) ++{ ++ const struct pmbus_driver_info *info = pmbus_get_driver_info(client); ++ const struct max20830_data *data = container_of(info, struct max20830_data, info); ++ int ret; ++ u64 temp; ++ ++ switch (reg) { ++ case PMBUS_READ_VOUT: ++ ret = pmbus_read_word_data(client, page, phase, reg); ++ if (ret < 0) ++ return ret; ++ ++ /* Apply voltage divider scaling if resistors are non-zero */ ++ if (data->vout_rfb1 && data->vout_rfb2) { ++ temp = (u64)data->vout_rfb1 + (u64)data->vout_rfb2; ++ temp = DIV_ROUND_CLOSEST_ULL((u64)ret * temp, data->vout_rfb2); ++ ret = clamp_val(temp, 0, 0xFFFF); ++ } ++ return ret; ++ default: ++ return -ENODATA; ++ } ++} ++ + static struct pmbus_driver_info max20830_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, +@@ -23,61 +80,53 @@ static struct pmbus_driver_info max20830_info = { + PMBUS_HAVE_TEMP | + PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP, ++ .read_word_data = max20830_read_word_data, ++ .have_pmbus_revision = true, ++ .pmbus_revision = PMBUS_REV_13, + }; + + static int max20830_probe(struct i2c_client *client) + { + u8 buf[I2C_SMBUS_BLOCK_MAX + 1] = {}; +- int ret; ++ struct max20830_data *data; ++ int i, ret; + +- if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BLOCK_DATA) && +- !i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) +- return -ENODEV; ++ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; + +- /* +- * Use i2c_smbus_read_block_data() if supported, otherwise fall back +- * to i2c_smbus_read_i2c_block_data() to support I2C controllers +- * which do not support SMBus block reads. +- */ +- if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BLOCK_DATA)) { +- /* Reads 9 Data bytes from MAX20830 */ +- ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf); +- if (ret < 0) +- return dev_err_probe(&client->dev, ret, +- "Failed to read IC_DEVICE_ID\n"); +- } else { +- /* Reads 1 length byte + 9 Data bytes from MAX20830 */ +- ret = i2c_smbus_read_i2c_block_data(client, PMBUS_IC_DEVICE_ID, +- MAX20830_IC_DEVICE_ID_LENGTH + 1, +- buf); +- if (ret < 0) +- return dev_err_probe(&client->dev, ret, +- "Failed to read IC_DEVICE_ID\n"); +- /* +- * Moves data forward, removing the length byte, this is to +- * match the format of i2c_smbus_read_block_data(). +- * Also adjust return value to reflect length byte removal. +- */ +- memmove(buf, buf + 1, MAX20830_IC_DEVICE_ID_LENGTH); +- ret = ret - 1; +- } ++ data->info = max20830_info; + +- /* +- * MAX20830 IC_DEVICE_ID sends string data "MAX20830\0". +- * Return value should at least be 9 bytes of data. +- */ ++ /* Read optional voltage divider resistor values */ ++ device_property_read_u32(&client->dev, "adi,vout-rfb1-ohms", &data->vout_rfb1); ++ device_property_read_u32(&client->dev, "adi,vout-rfb2-ohms", &data->vout_rfb2); ++ ++ ret = pmbus_read_smbus_i2c_block_data(client, PMBUS_IC_DEVICE_ID, buf); ++ if (ret < 0) ++ return dev_err_probe(&client->dev, ret, ++ "Failed to read IC_DEVICE_ID\n"); ++ ++ /* Verify we read the expected number of bytes */ + if (ret < MAX20830_IC_DEVICE_ID_LENGTH) + return dev_err_probe(&client->dev, -ENODEV, +- "IC_DEVICE_ID too short: expected at least 9 bytes, got %d\n", +- ret); ++ "IC_DEVICE_ID too short: expected %d bytes, got %d\n", ++ MAX20830_IC_DEVICE_ID_LENGTH, ret); + +- /* 9 bytes of data, buf[0]-buf[7] = "MAX20830", buf[8] = '\0' */ +- buf[MAX20830_IC_DEVICE_ID_LENGTH - 1] = '\0'; +- if (strncmp(buf, "MAX20830", MAX20830_IC_DEVICE_ID_LENGTH - 1)) ++ /* Null-terminate the string */ ++ buf[ret] = '\0'; ++ ++ /* Verify the device ID matches what we expect */ ++ for (i = 0; i < ARRAY_SIZE(supported_chip_ids); i++) { ++ if (!strcmp(buf, supported_chip_ids[i])) ++ break; ++ } ++ ++ /* No match found - unsupported device */ ++ if (i == ARRAY_SIZE(supported_chip_ids)) + return dev_err_probe(&client->dev, -ENODEV, +- "Unsupported device: '%s'\n", buf); ++ "Unsupported device: '%*pE'\n", ret, buf); + +- return pmbus_do_probe(client, &max20830_info); ++ return pmbus_do_probe(client, &data->info); + } + + static const struct i2c_device_id max20830_id[] = { +diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c +--- a/drivers/hwmon/pmbus/max34440.c ++++ b/drivers/hwmon/pmbus/max34440.c +@@ -23,6 +23,7 @@ enum chips { + max34441, + max34446, + max34451, ++ max34452, + max34460, + max34461, + }; +@@ -59,6 +60,7 @@ enum chips { + #define MAX34440_IOUT_OC_FAULT_LIMIT 0x4A + + #define MAX34451ETNA6_MFR_REV 0x0012 ++#define MAX34451ETNA8_MFR_REV 0x0014 + + #define MAX34451_MFR_CHANNEL_CONFIG 0xe4 + #define MAX34451_MFR_CHANNEL_CONFIG_SEL_MASK 0x3f +@@ -105,16 +107,21 @@ static int max34440_read_word_data(struct i2c_client *client, int page, + case PMBUS_UT_FAULT_LIMIT: + case PMBUS_MFR_MAX_TEMP_1: + /* +- * MAX34451/ADPM family do not support VIN/IIN limit registers, +- * manufacturer-specific min/max registers, or undercurrent/ +- * undertemperature fault limits. Accessing these triggers CML +- * error and asserts ALERT. ++ * MAX34451/MAX34452/ADPM family do not support VIN/IIN limit ++ * registers, manufacturer-specific min/max registers, or ++ * undercurrent/undertemperature fault limits. Accessing these ++ * triggers CML error and asserts ALERT. + */ +- if (data->id == max34451 || data->id == adpm12160 || +- data->id == adpm12200 || data->id == adpm12250) ++ if (data->id == max34451 || data->id == max34452 || ++ data->id == adpm12160 || data->id == adpm12200 || ++ data->id == adpm12250) + return -ENXIO; + ret = -ENODATA; + break; ++ case PMBUS_VOUT_OV_WARN_LIMIT: ++ if (data->id == max34452) ++ return -ENXIO; ++ return -ENODATA; + case PMBUS_VIRT_READ_VOUT_MIN: + ret = pmbus_read_word_data(client, page, phase, + MAX34440_MFR_VOUT_MIN); +@@ -125,8 +132,8 @@ static int max34440_read_word_data(struct i2c_client *client, int page, + break; + case PMBUS_VIRT_READ_IOUT_AVG: + if (data->id != max34446 && data->id != max34451 && +- data->id != adpm12160 && data->id != adpm12200 && +- data->id != adpm12250) ++ data->id != max34452 && data->id != adpm12160 && ++ data->id != adpm12200 && data->id != adpm12250) + return -ENXIO; + ret = pmbus_read_word_data(client, page, phase, + MAX34446_MFR_IOUT_AVG); +@@ -191,6 +198,10 @@ static int max34440_write_word_data(struct i2c_client *client, int page, + ret = pmbus_write_word_data(client, page, data->iout_oc_warn_limit, + word); + break; ++ case PMBUS_VOUT_OV_WARN_LIMIT: ++ if (data->id == max34452) ++ return -ENXIO; ++ return -ENODATA; + case PMBUS_VIRT_RESET_POUT_HISTORY: + ret = pmbus_write_word_data(client, page, + MAX34446_MFR_POUT_PEAK, 0); +@@ -211,8 +222,8 @@ static int max34440_write_word_data(struct i2c_client *client, int page, + ret = pmbus_write_word_data(client, page, + MAX34440_MFR_IOUT_PEAK, 0); + if (!ret && (data->id == max34446 || data->id == max34451 || +- data->id == adpm12160 || data->id == adpm12200 || +- data->id == adpm12250)) ++ data->id == max34452 || data->id == adpm12160 || ++ data->id == adpm12200 || data->id == adpm12250)) + ret = pmbus_write_word_data(client, page, + MAX34446_MFR_IOUT_AVG, 0); + +@@ -280,12 +291,13 @@ static int max34451_read_byte_data(struct i2c_client *client, int page, int reg) + case PMBUS_STATUS_BYTE: + case PMBUS_STATUS_OTHER: + /* +- * MAX34451/ADPM family do not support STATUS_BYTE or ++ * MAX34451/MAX34452/ADPM family do not support STATUS_BYTE or + * STATUS_OTHER registers. Accessing them triggers CML + * error and asserts ALERT. + */ +- if (data->id == max34451 || data->id == adpm12160 || +- data->id == adpm12200 || data->id == adpm12250) ++ if (data->id == max34451 || data->id == max34452 || ++ data->id == adpm12160 || data->id == adpm12200 || ++ data->id == adpm12250) + return -ENXIO; + return -ENODATA; + default: +@@ -303,12 +315,13 @@ static int max34451_write_byte_data(struct i2c_client *client, int page, + case PMBUS_STATUS_BYTE: + case PMBUS_STATUS_OTHER: + /* +- * MAX34451/ADPM family do not support STATUS_BYTE or ++ * MAX34451/MAX34452/ADPM family do not support STATUS_BYTE or + * STATUS_OTHER registers. Writing to them triggers CML + * error and asserts ALERT. + */ +- if (data->id == max34451 || data->id == adpm12160 || +- data->id == adpm12200 || data->id == adpm12250) ++ if (data->id == max34451 || data->id == max34452 || ++ data->id == adpm12160 || data->id == adpm12200 || ++ data->id == adpm12250) + return -ENXIO; + return -ENODATA; + default: +@@ -339,16 +352,28 @@ static int max34451_set_supported_funcs(struct i2c_client *client, + if (rv < 0) + return rv; + +- if (rv >= MAX34451ETNA6_MFR_REV) { ++ if (data->id == max34451 && rv >= MAX34451ETNA6_MFR_REV) { + max34451_na6 = true; + data->info.format[PSC_VOLTAGE_IN] = direct; + data->info.format[PSC_CURRENT_IN] = direct; +- data->info.m[PSC_VOLTAGE_IN] = 1; ++ + data->info.b[PSC_VOLTAGE_IN] = 0; +- data->info.R[PSC_VOLTAGE_IN] = 3; +- data->info.m[PSC_CURRENT_IN] = 1; + data->info.b[PSC_CURRENT_IN] = 0; +- data->info.R[PSC_CURRENT_IN] = 2; ++ if (rv >= MAX34451ETNA8_MFR_REV) { ++ data->info.m[PSC_VOLTAGE_IN] = 125; ++ data->info.R[PSC_VOLTAGE_IN] = 0; ++ data->info.m[PSC_VOLTAGE_OUT] = 125; ++ data->info.R[PSC_VOLTAGE_OUT] = 0; ++ data->info.m[PSC_CURRENT_IN] = 250; ++ data->info.R[PSC_CURRENT_IN] = -1; ++ data->info.m[PSC_CURRENT_OUT] = 250; ++ data->info.R[PSC_CURRENT_OUT] = -1; ++ } else { ++ data->info.m[PSC_VOLTAGE_IN] = 1; ++ data->info.R[PSC_VOLTAGE_IN] = 3; ++ data->info.m[PSC_CURRENT_IN] = 1; ++ data->info.R[PSC_CURRENT_IN] = 2; ++ } + data->iout_oc_fault_limit = PMBUS_IOUT_OC_FAULT_LIMIT; + data->iout_oc_warn_limit = PMBUS_IOUT_OC_WARN_LIMIT; + } +@@ -665,6 +690,32 @@ static struct pmbus_driver_info max34440_info[] = { + .write_word_data = max34440_write_word_data, + .page_change_delay = MAX34440_PAGE_CHANGE_DELAY, + }, ++ [max34452] = { ++ .pages = 21, ++ .format[PSC_VOLTAGE_OUT] = direct, ++ .format[PSC_TEMPERATURE] = direct, ++ .format[PSC_CURRENT_OUT] = direct, ++ .m[PSC_VOLTAGE_OUT] = 1, ++ .b[PSC_VOLTAGE_OUT] = 0, ++ .R[PSC_VOLTAGE_OUT] = 3, ++ .m[PSC_CURRENT_OUT] = 1, ++ .b[PSC_CURRENT_OUT] = 0, ++ .R[PSC_CURRENT_OUT] = 2, ++ .m[PSC_TEMPERATURE] = 1, ++ .b[PSC_TEMPERATURE] = 0, ++ .R[PSC_TEMPERATURE] = 2, ++ /* func 0-15 is set dynamically before probing */ ++ .func[16] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, ++ .func[17] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, ++ .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, ++ .func[19] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, ++ .func[20] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, ++ .read_byte_data = max34451_read_byte_data, ++ .read_word_data = max34440_read_word_data, ++ .write_byte_data = max34451_write_byte_data, ++ .write_word_data = max34440_write_word_data, ++ .page_change_delay = MAX34440_PAGE_CHANGE_DELAY, ++ }, + [max34460] = { + .pages = 18, + .format[PSC_VOLTAGE_OUT] = direct, +@@ -748,7 +799,7 @@ static int max34440_probe(struct i2c_client *client) + data->iout_oc_fault_limit = MAX34440_IOUT_OC_FAULT_LIMIT; + data->iout_oc_warn_limit = MAX34440_IOUT_OC_WARN_LIMIT; + +- if (data->id == max34451) { ++ if (data->id == max34451 || data->id == max34452) { + rv = max34451_set_supported_funcs(client, data); + if (rv) + return rv; +@@ -769,6 +820,7 @@ static const struct i2c_device_id max34440_id[] = { + { .name = "max34441", .driver_data = max34441 }, + { .name = "max34446", .driver_data = max34446 }, + { .name = "max34451", .driver_data = max34451 }, ++ { .name = "max34452", .driver_data = max34452 }, + { .name = "max34460", .driver_data = max34460 }, + { .name = "max34461", .driver_data = max34461 }, + { } +diff --git a/drivers/hwmon/pmbus/mp2975.c b/drivers/hwmon/pmbus/mp2975.c +--- a/drivers/hwmon/pmbus/mp2975.c ++++ b/drivers/hwmon/pmbus/mp2975.c +@@ -176,7 +176,6 @@ mp2975_vid2direct(int vrf, int val) + static u16 mp2975_data2reg_linear11(s64 val) + { + s16 exponent = 0, mantissa; +- bool negative = false; + + /* simple case */ + if (val == 0) +@@ -196,10 +195,6 @@ static u16 mp2975_data2reg_linear11(s64 val) + /* Convert mantissa from milli-units to units */ + mantissa = clamp_val(DIV_ROUND_CLOSEST_ULL(val, 1000), 0, 0x3ff); + +- /* restore sign */ +- if (negative) +- mantissa = -mantissa; +- + /* Convert to 5 bit exponent, 11 bit mantissa */ + return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800); + } +diff --git a/drivers/hwmon/pmbus/mpq82d00.c b/drivers/hwmon/pmbus/mpq82d00.c +new file mode 100644 +--- /dev/null ++++ b/drivers/hwmon/pmbus/mpq82d00.c +@@ -0,0 +1,249 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Hardware monitoring driver for MPS Synchronous Step-Down Converter(MPQ82D00) ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "pmbus.h" ++ ++#define MPQ82D00_VOUT_DIV 64 ++ ++#define MPQ82D00_PAGE_NUM 1 ++ ++#define MPQ82D00_RAIL1_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | \ ++ PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP | \ ++ PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | \ ++ PMBUS_HAVE_STATUS_VOUT | \ ++ PMBUS_HAVE_STATUS_IOUT | \ ++ PMBUS_HAVE_STATUS_TEMP | \ ++ PMBUS_HAVE_STATUS_INPUT) ++ ++struct mpq82d00_data { ++ struct pmbus_driver_info info; ++ int vout_scale; ++}; ++ ++#define to_mpq82d00_data(x) container_of(x, struct mpq82d00_data, info) ++ ++static u16 mpq82d00_linear_exp_transfer(u16 word, u16 expect_exponent) ++{ ++ s16 exponent, mantissa, target_exponent; ++ ++ exponent = ((s16)word) >> 11; ++ mantissa = ((s16)((word & 0x7ff) << 5)) >> 5; ++ target_exponent = (s16)((expect_exponent & 0x1f) << 11) >> 11; ++ ++ /* ++ * The MPQ82D00 does not support negtive limit value, if a negtive ++ * limit value is written, the limit value will become to 0. And ++ * the maximum positive limit value is limitted to 0x3FF. ++ */ ++ if (mantissa < 0) { ++ mantissa = 0; ++ } else { ++ if (exponent > target_exponent) { ++ mantissa = (1023 >> (exponent - target_exponent)) >= mantissa ? ++ mantissa << (exponent - target_exponent) : ++ 0x3FF; ++ } else { ++ mantissa = clamp_val(mantissa >> (target_exponent - exponent), ++ 0, 0x3FF); ++ } ++ } ++ ++ return mantissa | ((expect_exponent << 11) & 0xf800); ++} ++ ++static int mpq82d00_read_byte_data(struct i2c_client *client, int page, int reg) ++{ ++ int ret; ++ ++ switch (reg) { ++ case PMBUS_VOUT_MODE: ++ /* ++ * The MPQ82D00 does not follow standard PMBus protocol completely, ++ * and the calculation of vout in this driver is based on direct ++ * format. As a result, the format of vout is enforced to direct. ++ */ ++ ret = PB_VOUT_MODE_DIRECT; ++ break; ++ default: ++ /* ++ * These registers are not explicitly handled by the driver, ++ * as a result, return -ENODATA directly. ++ */ ++ ret = -ENODATA; ++ break; ++ } ++ ++ return ret; ++} ++ ++static int mpq82d00_read_word_data(struct i2c_client *client, int page, ++ int phase, int reg) ++{ ++ const struct pmbus_driver_info *info = pmbus_get_driver_info(client); ++ struct mpq82d00_data *data = to_mpq82d00_data(info); ++ int ret; ++ ++ switch (reg) { ++ case PMBUS_STATUS_WORD: ++ case PMBUS_READ_VIN: ++ case PMBUS_READ_IOUT: ++ case PMBUS_READ_POUT: ++ case PMBUS_READ_PIN: ++ case PMBUS_READ_TEMPERATURE_1: ++ case PMBUS_IOUT_OC_FAULT_LIMIT: ++ case PMBUS_OT_FAULT_LIMIT: ++ /* ++ * These registers are not explicitly handled by the driver, ++ * as a result, return -ENODATA directly. ++ */ ++ ret = -ENODATA; ++ break; ++ case PMBUS_READ_VOUT: ++ ret = pmbus_read_word_data(client, page, phase, reg); ++ if (ret < 0) ++ return ret; ++ ++ ret = DIV_ROUND_CLOSEST((ret & GENMASK(11, 0)) * data->vout_scale, ++ MPQ82D00_VOUT_DIV); ++ break; ++ default: ++ /* ++ * The MPQ82D00 do not support other telemetry and limit ++ * value reading, so, return -EINVAL directly. ++ */ ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++static int mpq82d00_write_word_data(struct i2c_client *client, int page, int reg, ++ u16 word) ++{ ++ int ret; ++ ++ switch (reg) { ++ case PMBUS_OT_FAULT_LIMIT: ++ /* ++ * The PMBUS_OT_FAULT_LIMIT of MPQ82D00 is linear11 format, ++ * and the exponent is a constant value(5'b00000), so the ++ * exponent of word parameter should be converted to 5'b00000. ++ */ ++ ret = pmbus_write_word_data(client, page, reg, ++ mpq82d00_linear_exp_transfer(word, 0x00)); ++ break; ++ case PMBUS_IOUT_OC_FAULT_LIMIT: ++ /* ++ * The PMBUS_IOUT_OC_FAULT_LIMIT of MPQ82D00 is linear11 format, ++ * and the exponent is a constant value(5'b11100), so the ++ * exponent of word parameter should be converted to 5'b11100. ++ */ ++ ret = pmbus_write_word_data(client, page, reg, ++ mpq82d00_linear_exp_transfer(word, 0x1C)); ++ break; ++ default: ++ /* ++ * The MPQ82D00 do not support other limit value configuration, ++ * so, return -EINVAL directly. ++ */ ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++static int mpq82d00_identify(struct i2c_client *client, struct pmbus_driver_info *info) ++{ ++ struct mpq82d00_data *data = to_mpq82d00_data(info); ++ int ret; ++ ++ ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); ++ if (ret < 0) ++ return ret; ++ ++ ret = i2c_smbus_read_byte_data(client, PMBUS_VOUT_MODE); ++ if (ret < 0) ++ return ret; ++ ++ /* ++ * The MPQ82D00 supports three vout mode. If PMBUS_VOUT_MODE ++ * bit5 is 1, the vout scale is 5mv/LSB.If PMBUS_VOUT_MODE bit5 ++ * is 0, it is linear mode, the vout scale is 1.953125mv/LSB. ++ */ ++ if (FIELD_GET(GENMASK(5, 5), ret)) ++ data->vout_scale = 320; ++ else ++ data->vout_scale = 125; ++ ++ return 0; ++} ++ ++static struct pmbus_driver_info mpq82d00_info = { ++ .pages = MPQ82D00_PAGE_NUM, ++ .format[PSC_VOLTAGE_IN] = linear, ++ .format[PSC_CURRENT_OUT] = linear, ++ .format[PSC_TEMPERATURE] = linear, ++ .format[PSC_VOLTAGE_OUT] = direct, ++ .format[PSC_POWER] = linear, ++ ++ .m[PSC_VOLTAGE_OUT] = 1, ++ .R[PSC_VOLTAGE_OUT] = 3, ++ .b[PSC_VOLTAGE_OUT] = 0, ++ ++ .func[0] = MPQ82D00_RAIL1_FUNC, ++ .read_word_data = mpq82d00_read_word_data, ++ .read_byte_data = mpq82d00_read_byte_data, ++ .write_word_data = mpq82d00_write_word_data, ++ .identify = mpq82d00_identify, ++}; ++ ++static int mpq82d00_probe(struct i2c_client *client) ++{ ++ struct mpq82d00_data *data; ++ ++ data = devm_kzalloc(&client->dev, sizeof(struct mpq82d00_data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ memcpy(&data->info, &mpq82d00_info, sizeof(mpq82d00_info)); ++ ++ return pmbus_do_probe(client, &data->info); ++} ++ ++static const struct i2c_device_id mpq82d00_id[] = { ++ {.name = "mpq82d00"}, ++ {} ++}; ++MODULE_DEVICE_TABLE(i2c, mpq82d00_id); ++ ++static const struct of_device_id __maybe_unused mpq82d00_of_match[] = { ++ {.compatible = "mps,mpq82d00"}, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, mpq82d00_of_match); ++ ++static struct i2c_driver mpq82d00_driver = { ++ .driver = { ++ .name = "mpq82d00", ++ .of_match_table = mpq82d00_of_match, ++ }, ++ .probe = mpq82d00_probe, ++ .id_table = mpq82d00_id, ++}; ++ ++module_i2c_driver(mpq82d00_driver); ++ ++MODULE_AUTHOR("Wensheng Wang ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "pmbus.h" ++ ++/* Default cadence for the in-driver alarm-poll fallback (ms) */ ++#define MPQ8646_ALARM_POLL_MS_DEFAULT 1000 ++ ++/* MPS vendor-extended command codes (NOT in PMBus 1.3 Part II) */ ++#define MPS_CLEAR_LAST_FAULT 0x08 ++#define MPS_MFR_CFG_EXT 0xF5 ++#define MPS_MFR_CFG_EXT_CLR_LAST_EN BIT(6) ++#define MPS_PROTECTION_LAST 0xFB ++ ++/* ++ * PMBus 1.3 NVM commit / revert commands. MPS equivalent of ++ * STORE_ALL (15h) and RESTORE_ALL (16h). ++ */ ++#define PMBUS_STORE_USER_ALL 0x15 ++#define PMBUS_RESTORE_USER_ALL 0x16 ++ ++/* PMBus 1.3 timing / UVLO command codes */ ++#define PMBUS_VIN_ON 0x35 ++#define PMBUS_VIN_OFF 0x36 ++#define PMBUS_TON_DELAY 0x60 ++#define PMBUS_TON_RISE 0x61 ++#define PMBUS_TOFF_DELAY 0x64 ++#define PMBUS_TOFF_FALL 0x65 ++ ++/* MPS vendor-extended observability / identity registers */ ++#define MPS_MFR_CONFIG_ID 0xC0 ++#define MPS_MFR_CONFIG_CODE_REV 0xC1 ++#define MPS_MFR_PRODUCT_REV_USER 0xC2 ++#define MPS_MFR_SILICON_REV 0xC3 ++#define MPS_MFR_RETRY_TIMES 0xF4 ++#define MPS_MFR_VBOOT_CFG 0xFC ++ ++/* ++ * MPS_MFR_PMBUS_LOCK (EEh): 16-bit WORD whose low two bits gate ++ * subsequent PMBus writes ++ * bits[1:0] = 00 -- unlocked (POR default) ++ * 01 -- lock all writes EXCEPT VOUT_COMMAND (0x21) ++ * so the operator can still DVFS the rail ++ * 11 -- lock all writes ++ * A negative-going PG edge resets these bits to 00, the lock ++ * is operationally reversible without a full chip POR. ++ */ ++#define MPS_MFR_PMBUS_LOCK 0xEE ++ ++/* ++ * Retry parameters for the MFR_CFG_EXT gate-close write after ++ * CLEAR_LAST_FAULT. Bench-observed NVM-busy NACK window on this ++ * silicon is about 1 ms; the datasheet does not have information. ++ */ ++#define MPQ8646_NVM_RETRY_MAX 5 ++#define MPQ8646_NVM_RETRY_DELAY_US_MIN 2000 ++#define MPQ8646_NVM_RETRY_DELAY_US_MAX 4000 ++ ++#define MPQ8646_DEBUG(client, fmt, ...) \ ++ dev_dbg(&(client)->dev, fmt, ##__VA_ARGS__) ++ ++/* ++ * Maximum legal value for VOUT_SCALE_LOOP (0x29) on the MPQ8646 silicon ++ * The chip uses an 11-bit VOUT feedback-divider scale register. ++ */ ++#define MPQ8646_VOUT_SCALE_LOOP_MAX GENMASK(10, 0) ++ ++/* Forward declaration */ ++struct mpq8646_dbg_reg_ctx; ++ ++/* Per-instance state */ ++struct mpq8646_priv { ++ struct pmbus_driver_info info; /* must be first, container_of target */ ++ struct i2c_client *client; ++ ++ /* Serialises CLEAR_LAST_FAULT sequences and PROTECTION_LAST reads */ ++ struct mutex mps_lock; ++ ++ /* Set alarm_poll_interval_ms = 0 to disable. */ ++ struct delayed_work alarm_poll_work; ++ u32 alarm_poll_interval_ms; ++ ++#ifdef CONFIG_DEBUG_FS ++ /* the only debugfs file that can re-arm the poll worker */ ++ struct dentry *dbg_poll; ++ struct mpq8646_dbg_reg_ctx *dbg_reg_ctx; ++#endif ++}; ++ ++static inline struct mpq8646_priv *mpq8646_priv_from_client(struct i2c_client *client) ++{ ++ const struct pmbus_driver_info *info = pmbus_get_driver_info(client); ++ ++ return container_of(info, struct mpq8646_priv, info); ++} ++ ++/* ++ * VID-mode m/b/R coefficients, same values as the mpq8785 driver for ++ * the MPS VID encoding. Per the PMBus Direct formula used by ++ * pmbus_core, X = (Y * 10^-R - b) / m, so m=64 / b=0 / R=1 yields ++ * 1.5625 mV per LSB. ++ */ ++#define MPQ8646_VID_M 64 ++#define MPQ8646_VID_B 0 ++#define MPQ8646_VID_R 1 ++ ++static int mpq8646_identify(struct i2c_client *client, ++ struct pmbus_driver_info *info) ++{ ++ int vout_mode; ++ ++ vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); ++ if (vout_mode < 0) ++ return vout_mode; ++ ++ switch (vout_mode & PB_VOUT_MODE_MODE_MASK) { ++ case PB_VOUT_MODE_LINEAR: ++ info->format[PSC_VOLTAGE_OUT] = linear; ++ break; ++ case PB_VOUT_MODE_VID: ++ case PB_VOUT_MODE_DIRECT: ++ info->format[PSC_VOLTAGE_OUT] = direct; ++ info->m[PSC_VOLTAGE_OUT] = MPQ8646_VID_M; ++ info->b[PSC_VOLTAGE_OUT] = MPQ8646_VID_B; ++ info->R[PSC_VOLTAGE_OUT] = MPQ8646_VID_R; ++ break; ++ default: ++ return -ENODEV; ++ } ++ ++ return 0; ++}; ++ ++static int mpq8646_read_byte_data(struct i2c_client *client, int page, int reg) ++{ ++ int ret; ++ ++ MPQ8646_DEBUG(client, "read_byte_data page=%d reg=0x%02x\n", page, reg); ++ ++ switch (reg) { ++ case PMBUS_VOUT_MODE: ++ ret = pmbus_read_byte_data(client, page, reg); ++ MPQ8646_DEBUG(client, " VOUT_MODE raw=0x%02x ret=%d\n", ret, ret); ++ if (ret < 0) ++ return ret; ++ ++ if ((ret & PB_VOUT_MODE_MODE_MASK) == PB_VOUT_MODE_VID) ++ return PB_VOUT_MODE_DIRECT; ++ ++ return ret; ++ case PMBUS_STATUS_BYTE: ++ case PMBUS_STATUS_CML: ++ case PMBUS_STATUS_OTHER: ++ case PMBUS_STATUS_MFR_SPECIFIC: ++ case PMBUS_STATUS_FAN_12: ++ case PMBUS_STATUS_FAN_34: ++ return -ENXIO; ++ case PMBUS_MFR_LOCATION: ++ case PMBUS_MFR_DATE: ++ case PMBUS_MFR_SERIAL: ++ case PMBUS_IC_DEVICE_ID: ++ case PMBUS_IC_DEVICE_REV: ++ MPQ8646_DEBUG(client, " unsupported mfr-info reg 0x%02x -> ENXIO\n", reg); ++ return -ENXIO; ++ default: ++ return -ENODATA; ++ } ++} ++ ++/* ++ * Reference: ltc2978.c::ltc2978_write_word_data which uses the ++ * same virtual-register channel for its real chip-side peak reset. ++ */ ++static int mpq8646_write_word_data(struct i2c_client *client, int page, ++ int reg, u16 word) ++{ ++ struct mpq8646_priv *priv = mpq8646_priv_from_client(client); ++ int rc; ++ ++ switch (reg) { ++ case PMBUS_VIRT_RESET_VIN_HISTORY: ++ case PMBUS_VIRT_RESET_VOUT_HISTORY: ++ case PMBUS_VIRT_RESET_IOUT_HISTORY: ++ case PMBUS_VIRT_RESET_TEMP_HISTORY: ++ MPQ8646_DEBUG(client, "reset_history virt reg=0x%04x -> CLEAR_FAULTS\n", ++ reg); ++ rc = i2c_smbus_write_byte(priv->client, PMBUS_CLEAR_FAULTS); ++ if (rc < 0) ++ MPQ8646_DEBUG(client, " CLEAR_FAULTS rc=%d\n", rc); ++ return rc < 0 ? rc : 0; ++ default: ++ return -ENODATA; /* let pmbus_core do the direct write */ ++ } ++} ++ ++static int mpq8646_read_word_data(struct i2c_client *client, int page, ++ int phase, int reg) ++{ ++ int rc; ++ ++ MPQ8646_DEBUG(client, "read_word_data page=%d phase=%d reg=0x%02x\n", ++ page, phase, reg); ++ ++ switch (reg) { ++ case PMBUS_READ_VIN: ++ case PMBUS_READ_VOUT: ++ case PMBUS_READ_IOUT: ++ case PMBUS_READ_TEMPERATURE_1: ++ case PMBUS_STATUS_WORD: ++ case PMBUS_VOUT_OV_FAULT_LIMIT: ++ case PMBUS_VOUT_OV_WARN_LIMIT: ++ case PMBUS_VOUT_UV_WARN_LIMIT: ++ case PMBUS_VOUT_UV_FAULT_LIMIT: ++ case PMBUS_IOUT_OC_FAULT_LIMIT: ++ case PMBUS_IOUT_OC_WARN_LIMIT: ++ case PMBUS_OT_FAULT_LIMIT: ++ case PMBUS_OT_WARN_LIMIT: ++ case PMBUS_VIN_OV_FAULT_LIMIT: ++ case PMBUS_VIN_OV_WARN_LIMIT: ++ case PMBUS_VIN_UV_WARN_LIMIT: ++ case PMBUS_VIN_UV_FAULT_LIMIT: ++ case PMBUS_MFR_VIN_MAX: ++ case PMBUS_MFR_VOUT_MAX: ++ case PMBUS_MFR_IOUT_MAX: ++ case PMBUS_MFR_MAX_TEMP_1: ++ break; ++ case PMBUS_VIRT_RESET_VIN_HISTORY: ++ case PMBUS_VIRT_RESET_VOUT_HISTORY: ++ case PMBUS_VIRT_RESET_IOUT_HISTORY: ++ case PMBUS_VIRT_RESET_TEMP_HISTORY: ++ return 0; ++ default: ++ return -ENODATA; ++ } ++ ++ rc = i2c_smbus_read_word_data(client, reg); ++ ++ MPQ8646_DEBUG(client, " reg=0x%02x rc=%d\n", reg, rc); ++ return rc; ++} ++ ++static struct pmbus_driver_info mpq8646_info = { ++ .pages = 1, ++ .format[PSC_VOLTAGE_IN] = direct, ++ .format[PSC_CURRENT_OUT] = direct, ++ .format[PSC_TEMPERATURE] = direct, ++ .m[PSC_VOLTAGE_IN] = 4, ++ .b[PSC_VOLTAGE_IN] = 0, ++ .R[PSC_VOLTAGE_IN] = 1, ++ .m[PSC_CURRENT_OUT] = 16, ++ .b[PSC_CURRENT_OUT] = 0, ++ .R[PSC_CURRENT_OUT] = 0, ++ .m[PSC_TEMPERATURE] = 1, ++ .b[PSC_TEMPERATURE] = 0, ++ .R[PSC_TEMPERATURE] = 0, ++ .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | ++ PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP | ++ PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_VOUT | ++ PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_TEMP, ++}; ++ ++#if IS_ENABLED(CONFIG_REGULATOR) ++static const struct regulator_desc mpq8646_reg_desc[] = { ++ PMBUS_REGULATOR("vout", 0), ++}; ++#endif /* CONFIG_REGULATOR */ ++ ++#if IS_ENABLED(CONFIG_NVMEM) ++/* ++ * Expose using ++ * /sys/bus/nvmem/devices//nvmem ++ * ++ * Layout (16 bytes): ++ * offset 0..1 PROTECTION_LAST (0xFB) word, LE -- NVM ++ * offset 2..3 MFR_RETRY_TIMES (0xF4) word, LE -- NVM ++ * offset 4..5 MFR_CONFIG_ID (0xC0) word, LE -- NVM ++ * offset 6..7 MFR_VBOOT_CFG (0xFC) word, LE -- NVM ++ * offset 8 MFR_SILICON_REV (0xC3) byte -- NVM ++ * offset 9..15 reserved (zero-fill, leaves room for additions) ++ */ ++#define MPQ8646_NVMEM_SIZE 16 ++ ++struct mpq8646_nvmem_entry { ++ unsigned int off; ++ u8 reg; ++ bool is_word; ++}; ++ ++static const struct mpq8646_nvmem_entry mpq8646_nvmem_map[] = { ++ { 0, MPS_PROTECTION_LAST, true }, ++ { 2, MPS_MFR_RETRY_TIMES, true }, ++ { 4, MPS_MFR_CONFIG_ID, true }, ++ { 6, MPS_MFR_VBOOT_CFG, true }, ++ { 8, MPS_MFR_SILICON_REV, false }, ++}; ++ ++static int mpq8646_nvmem_read(void *data, unsigned int offset, void *val, ++ size_t bytes) ++{ ++ struct mpq8646_priv *priv = data; ++ u8 *out = val; ++ size_t i; ++ ++ if (offset >= MPQ8646_NVMEM_SIZE) ++ return -EINVAL; ++ if (offset + bytes > MPQ8646_NVMEM_SIZE) ++ bytes = MPQ8646_NVMEM_SIZE - offset; ++ ++ memset(out, 0, bytes); ++ ++ guard(mutex)(&priv->mps_lock); ++ for (i = 0; i < ARRAY_SIZE(mpq8646_nvmem_map); i++) { ++ const struct mpq8646_nvmem_entry *e = &mpq8646_nvmem_map[i]; ++ unsigned int e_start = e->off; ++ unsigned int e_end = e_start + (e->is_word ? 2 : 1); ++ u8 raw[2]; ++ int rc; ++ unsigned int j; ++ ++ if (e_end <= offset || e_start >= offset + bytes) ++ continue; /* outside requested slice */ ++ ++ if (e->is_word) ++ rc = i2c_smbus_read_word_data(priv->client, e->reg); ++ else ++ rc = i2c_smbus_read_byte_data(priv->client, e->reg); ++ if (rc < 0) ++ continue; /* leave the zero-fill in place */ ++ ++ raw[0] = rc & 0xff; ++ raw[1] = (rc >> 8) & 0xff; ++ ++ for (j = 0; j < e_end - e_start; j++) { ++ unsigned int abs = e_start + j; ++ ++ if (abs >= offset && abs < offset + bytes) ++ out[abs - offset] = raw[j]; ++ } ++ } ++ return 0; ++} ++#endif /* CONFIG_NVMEM */ ++ ++static const struct i2c_device_id mpq8646_id[] = { ++ { .name = "mpq8646" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(i2c, mpq8646_id); ++ ++static const struct of_device_id __maybe_unused mpq8646_of_match[] = { ++ { .compatible = "mps,mpq8646" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, mpq8646_of_match); ++ ++static struct pmbus_platform_data mpq8646_no_pec_pdata = { ++ .flags = PMBUS_NO_CAPABILITY, ++}; ++ ++#ifdef CONFIG_DEBUG_FS ++/* ++ * Read-only decode cases in the client's pmbus debugfs directory: ++ * the MPS-specific STATUS_WORD and PROTECTION_LAST bit decode plus the ++ * identity/timing registers. ++ */ ++ ++struct mpq_status_bit { ++ u16 mask; ++ const char *name; ++}; ++ ++static const struct mpq_status_bit mpq8646_status_word_bits[] = { ++ { PB_STATUS_VOUT, "VOUT" }, ++ { PB_STATUS_IOUT_POUT, "IOUT_POUT" }, ++ { PB_STATUS_INPUT, "INPUT" }, ++ { PB_STATUS_WORD_MFR, "NVM_SUMMARY" }, ++ { PB_STATUS_POWER_GOOD_N, "POWER_GOOD#" }, ++ { PB_STATUS_FANS, "FANS" }, ++ { PB_STATUS_OTHER, "OTHER" }, ++ { PB_STATUS_UNKNOWN, "WATCH_DOG" }, ++ { PB_STATUS_BUSY, "BUSY" }, ++ { PB_STATUS_OFF, "OFF" }, ++ { PB_STATUS_VOUT_OV, "VOUT_OV_FAULT" }, ++ { PB_STATUS_IOUT_OC, "IOUT_OC_FAULT" }, ++ { PB_STATUS_VIN_UV, "VIN_UV_FAULT" }, ++ { PB_STATUS_TEMPERATURE, "TEMP" }, ++ { PB_STATUS_CML, "CML" }, ++ { PB_STATUS_NONE_ABOVE, "DRMOS_FAULT" }, ++ { /* sentinel */ } ++}; ++ ++/* PROTECTION_LAST (0xFB) bit names, it survives chip POR */ ++static const struct mpq_status_bit mpq8646_protection_last_bits[] = { ++ { BIT(15), "INIT_FAULT" }, ++ { BIT(14), "NVM_CRC_ERROR" }, ++ { BIT(13), "NVM_FAULT" }, ++ { BIT(12), "OC_PHASE_FAULT" }, ++ { BIT(11), "OTP_SELF_FAULT" }, ++ { BIT(9), "SWITCH_PRD_FAULT" }, ++ { BIT(8), "VIN_OV_FAULT" }, ++ { BIT(7), "VOUT_OV_FAULT" }, ++ { BIT(6), "VOUT_UV_FAULT" }, ++ { BIT(5), "OC_TOT_FAULT" }, ++ { BIT(4), "VIN_UVLO_FAULT" }, ++ { BIT(3), "DRMOS_OTP" }, ++ { /* sentinel */ } ++}; ++ ++static void mpq8646_print_bits(struct seq_file *s, u16 v, ++ const struct mpq_status_bit *tab) ++{ ++ const struct mpq_status_bit *t; ++ bool first = true; ++ ++ seq_printf(s, "0x%04x", v); ++ if (!v) { ++ seq_puts(s, " [clean]\n"); ++ return; ++ } ++ seq_puts(s, " ["); ++ for (t = tab; t->mask; t++) { ++ if (v & t->mask) { ++ if (!first) ++ seq_putc(s, ' '); ++ seq_puts(s, t->name); ++ first = false; ++ } ++ } ++ seq_puts(s, "]\n"); ++} ++ ++static int mpq8646_dbg_status_decoded_show(struct seq_file *s, void *unused) ++{ ++ struct mpq8646_priv *priv = s->private; ++ int rc; ++ ++ scoped_guard(pmbus_lock, priv->client) ++ rc = i2c_smbus_read_word_data(priv->client, PMBUS_STATUS_WORD); ++ if (rc < 0) { ++ seq_printf(s, "ERROR: STATUS_WORD read failed (%d)\n", rc); ++ return 0; ++ } ++ seq_puts(s, "STATUS_WORD: "); ++ mpq8646_print_bits(s, (u16)rc, mpq8646_status_word_bits); ++ seq_puts(s, "(MPS extensions: bit12=NVM_SUMMARY, bit8=WATCH_DOG, bit0=DRMOS_FAULT)\n"); ++ return 0; ++} ++DEFINE_SHOW_ATTRIBUTE(mpq8646_dbg_status_decoded); ++ ++static int mpq8646_dbg_protection_last_show(struct seq_file *s, void *unused) ++{ ++ struct mpq8646_priv *priv = s->private; ++ int rc; ++ ++ guard(pmbus_lock)(priv->client); ++ scoped_guard(mutex, &priv->mps_lock) ++ rc = i2c_smbus_read_word_data(priv->client, MPS_PROTECTION_LAST); ++ ++ if (rc < 0) { ++ seq_printf(s, "ERROR: PROTECTION_LAST read failed (%d)\n", rc); ++ return 0; ++ } ++ seq_puts(s, "PROTECTION_LAST: "); ++ mpq8646_print_bits(s, (u16)rc, mpq8646_protection_last_bits); ++ seq_puts(s, "(NVM-backed, survives chip POR)\n"); ++ return 0; ++} ++DEFINE_SHOW_ATTRIBUTE(mpq8646_dbg_protection_last); ++ ++struct mpq8646_dbg_reg { ++ u8 reg; ++ bool is_word; ++ const char *name; ++}; ++ ++static const struct mpq8646_dbg_reg mpq8646_dbg_regs[] = { ++ /* MPS vendor extensions (read-only identity / observability) */ ++ { MPS_MFR_CONFIG_ID, true, "mfr_config_id" }, ++ { MPS_MFR_CONFIG_CODE_REV, true, "mfr_config_code_rev" }, ++ { MPS_MFR_SILICON_REV, false, "mfr_silicon_rev" }, ++ { MPS_MFR_RETRY_TIMES, true, "mfr_retry_times" }, ++ { MPS_MFR_VBOOT_CFG, true, "mfr_vboot_cfg" }, ++ /* PMBus 1.3 standard timing / UVLO (read-only introspection) */ ++ { PMBUS_VIN_ON, true, "vin_on" }, ++ { PMBUS_VIN_OFF, true, "vin_off" }, ++ { PMBUS_TON_DELAY, true, "ton_delay" }, ++ { PMBUS_TON_RISE, true, "ton_rise" }, ++ { PMBUS_TOFF_DELAY, true, "toff_delay" }, ++ { PMBUS_TOFF_FALL, true, "toff_fall" }, ++}; ++ ++struct mpq8646_dbg_reg_ctx { ++ struct mpq8646_priv *priv; ++ const struct mpq8646_dbg_reg *desc; ++}; ++ ++static int mpq8646_dbg_reg_show(struct seq_file *s, void *unused) ++{ ++ struct mpq8646_dbg_reg_ctx *ctx = s->private; ++ int rc; ++ ++ guard(pmbus_lock)(ctx->priv->client); ++ if (ctx->desc->is_word) ++ rc = i2c_smbus_read_word_data(ctx->priv->client, ++ ctx->desc->reg); ++ else ++ rc = i2c_smbus_read_byte_data(ctx->priv->client, ++ ctx->desc->reg); ++ ++ if (rc < 0) { ++ seq_printf(s, "ERROR: reg 0x%02x (%s) read failed (%d)\n", ++ ctx->desc->reg, ctx->desc->name, rc); ++ return 0; ++ } ++ seq_printf(s, "0x%0*x\n", ctx->desc->is_word ? 4 : 2, rc); ++ return 0; ++} ++DEFINE_SHOW_ATTRIBUTE(mpq8646_dbg_reg); ++ ++#ifdef CONFIG_SENSORS_MPQ8646_DEBUG_UNSAFE ++/* ++ * Write/provisioning data, disabled by default: NVM commit and ++ * revert, the CLEAR_LAST_FAULT sequences and a small set of named ++ * writable registers. ++ */ ++ ++static void mpq8646_unsafe_banner(struct mpq8646_priv *priv) ++{ ++ dev_warn(&priv->client->dev, ++ "**********************************************************\n" ++ "** WARNING WARNING WARNING WARNING WARNING WARNING **\n" ++ "** **\n" ++ "** The MPQ8646 provisioning debugfs writes are enabled. **\n" ++ "** Wrong register writes can and likely will physically **\n" ++ "** damage or destroy the chip and/or the board. **\n" ++ "** **\n" ++ "** If you see this message and you are not debugging **\n" ++ "** the kernel, report this immediately to your system **\n" ++ "** administrator! **\n" ++ "** **\n" ++ "** WARNING WARNING WARNING WARNING WARNING WARNING **\n" ++ "**********************************************************\n"); ++} ++ ++static int mpq8646_dbg_clear_protection_last(void *data, u64 val) ++{ ++ struct mpq8646_priv *priv = data; ++ int rc; ++ ++ if (!val) ++ return 0; ++ ++ guard(pmbus_lock)(priv->client); ++ scoped_guard(mutex, &priv->mps_lock) ++ rc = i2c_smbus_write_byte(priv->client, MPS_CLEAR_LAST_FAULT); ++ if (rc < 0) ++ dev_warn(&priv->client->dev, ++ "clear_protection_last: CLEAR_LAST_FAULT write failed (%d)\n", ++ rc); ++ return rc; ++} ++DEFINE_DEBUGFS_ATTRIBUTE(mpq8646_dbg_clear_protection_last_fops, ++ NULL, mpq8646_dbg_clear_protection_last, "%llu\n"); ++ ++static int mpq8646_dbg_clear_protection_last_force(void *data, u64 val) ++{ ++ struct mpq8646_priv *priv = data; ++ int rc, ret; ++ int wp_orig, cfg_orig; ++ ++ if (!val) ++ return 0; ++ ++ guard(pmbus_lock)(priv->client); ++ guard(mutex)(&priv->mps_lock); ++ ++ wp_orig = i2c_smbus_read_byte_data(priv->client, PMBUS_WRITE_PROTECT); ++ if (wp_orig < 0) { ++ dev_warn(&priv->client->dev, ++ "clear_protection_last_force: WRITE_PROTECT read failed (%d), aborting\n", ++ wp_orig); ++ return wp_orig; ++ } ++ cfg_orig = i2c_smbus_read_word_data(priv->client, MPS_MFR_CFG_EXT); ++ if (cfg_orig < 0) { ++ dev_warn(&priv->client->dev, ++ "clear_protection_last_force: MFR_CFG_EXT read failed (%d), aborting\n", ++ cfg_orig); ++ return cfg_orig; ++ } ++ ++ if (wp_orig != 0) { ++ rc = i2c_smbus_write_byte_data(priv->client, ++ PMBUS_WRITE_PROTECT, 0); ++ if (rc < 0) { ++ dev_warn(&priv->client->dev, ++ "clear_protection_last_force: WP clear failed (%d), aborting\n", ++ rc); ++ return rc; ++ } ++ } ++ ++ ret = i2c_smbus_write_word_data(priv->client, MPS_MFR_CFG_EXT, ++ (u16)cfg_orig | MPS_MFR_CFG_EXT_CLR_LAST_EN); ++ if (ret < 0) { ++ dev_warn(&priv->client->dev, ++ "clear_protection_last_force: gate open failed (%d)\n", ++ ret); ++ } else { ++ ret = i2c_smbus_write_byte(priv->client, MPS_CLEAR_LAST_FAULT); ++ if (ret < 0) ++ dev_warn(&priv->client->dev, ++ "clear_protection_last_force: CLEAR_LAST_FAULT failed (%d) even with gate open\n", ++ ret); ++ ++ for (int attempt = 0; attempt < MPQ8646_NVM_RETRY_MAX; attempt++) { ++ rc = i2c_smbus_write_word_data(priv->client, ++ MPS_MFR_CFG_EXT, ++ (u16)cfg_orig); ++ if (rc >= 0) ++ break; ++ usleep_range(MPQ8646_NVM_RETRY_DELAY_US_MIN, ++ MPQ8646_NVM_RETRY_DELAY_US_MAX); ++ } ++ if (rc < 0) { ++ dev_warn(&priv->client->dev, ++ "clear_protection_last_force: MFR_CFG_EXT restore failed after retries (%d): gate may stay open until POR\n", ++ rc); ++ if (ret == 0) ++ ret = rc; ++ } ++ } ++ ++ if (wp_orig != 0) { ++ rc = i2c_smbus_write_byte_data(priv->client, ++ PMBUS_WRITE_PROTECT, ++ (u8)wp_orig); ++ if (rc < 0) { ++ dev_warn(&priv->client->dev, ++ "clear_protection_last_force: WP restore failed (%d)\n", ++ rc); ++ if (ret == 0) ++ ret = rc; ++ } ++ } ++ return ret; ++} ++DEFINE_DEBUGFS_ATTRIBUTE(mpq8646_dbg_clear_protection_last_force_fops, ++ NULL, mpq8646_dbg_clear_protection_last_force, "%llu\n"); ++ ++static int mpq8646_dbg_store_all(void *data, u64 val) ++{ ++ struct mpq8646_priv *priv = data; ++ int rc; ++ ++ if (!val) ++ return 0; ++ ++ guard(pmbus_lock)(priv->client); ++ scoped_guard(mutex, &priv->mps_lock) ++ rc = i2c_smbus_write_byte(priv->client, PMBUS_STORE_USER_ALL); ++ if (rc < 0) ++ dev_warn(&priv->client->dev, ++ "store_all: STORE_USER_ALL (0x15) write failed (%d)\n", ++ rc); ++ return rc; ++} ++DEFINE_DEBUGFS_ATTRIBUTE(mpq8646_dbg_store_all_fops, ++ NULL, mpq8646_dbg_store_all, "%llu\n"); ++ ++static int mpq8646_dbg_restore_all(void *data, u64 val) ++{ ++ struct mpq8646_priv *priv = data; ++ int rc; ++ ++ if (!val) ++ return 0; ++ ++ guard(pmbus_lock)(priv->client); ++ scoped_guard(mutex, &priv->mps_lock) ++ rc = i2c_smbus_write_byte(priv->client, PMBUS_RESTORE_USER_ALL); ++ if (rc < 0) ++ dev_warn(&priv->client->dev, ++ "restore_all: RESTORE_USER_ALL (0x16) write failed (%d)\n", ++ rc); ++ return rc; ++} ++DEFINE_DEBUGFS_ATTRIBUTE(mpq8646_dbg_restore_all_fops, ++ NULL, mpq8646_dbg_restore_all, "%llu\n"); ++ ++static const struct mpq8646_dbg_reg mpq8646_dbg_regs_unsafe[] = { ++ /* PMBus 1.3 control / margin */ ++ { PMBUS_ON_OFF_CONFIG, false, "on_off_config" }, ++ { PMBUS_VOUT_MARGIN_HIGH, true, "vout_margin_high" }, ++ { PMBUS_VOUT_MARGIN_LOW, true, "vout_margin_low" }, ++ /* MPS PMBus-level write-protect */ ++ { MPS_MFR_PMBUS_LOCK, true, "mfr_pmbus_lock" }, ++ /* MPS user-writable product revision */ ++ { MPS_MFR_PRODUCT_REV_USER, true, "mfr_product_rev_user" }, ++}; ++ ++static int mpq8646_dbg_reg_get(void *data, u64 *val) ++{ ++ struct mpq8646_dbg_reg_ctx *ctx = data; ++ int rc; ++ ++ guard(pmbus_lock)(ctx->priv->client); ++ if (ctx->desc->is_word) ++ rc = i2c_smbus_read_word_data(ctx->priv->client, ++ ctx->desc->reg); ++ else ++ rc = i2c_smbus_read_byte_data(ctx->priv->client, ++ ctx->desc->reg); ++ if (rc < 0) ++ return rc; ++ *val = rc; ++ return 0; ++} ++ ++static int mpq8646_dbg_reg_set(void *data, u64 val) ++{ ++ struct mpq8646_dbg_reg_ctx *ctx = data; ++ int rc; ++ ++ guard(pmbus_lock)(ctx->priv->client); ++ if (ctx->desc->is_word) ++ rc = i2c_smbus_write_word_data(ctx->priv->client, ++ ctx->desc->reg, (u16)val); ++ else ++ rc = i2c_smbus_write_byte_data(ctx->priv->client, ++ ctx->desc->reg, (u8)val); ++ return rc < 0 ? rc : 0; ++} ++DEFINE_DEBUGFS_ATTRIBUTE(mpq8646_dbg_reg_rw_fops, ++ mpq8646_dbg_reg_get, mpq8646_dbg_reg_set, "0x%llx\n"); ++ ++static void mpq8646_debugfs_register_unsafe(struct mpq8646_priv *priv, ++ struct dentry *root) ++{ ++ struct mpq8646_dbg_reg_ctx *ctx; ++ size_t i; ++ ++ mpq8646_unsafe_banner(priv); ++ ++ debugfs_create_file_unsafe("clear_protection_last", 0200, root, priv, ++ &mpq8646_dbg_clear_protection_last_fops); ++ debugfs_create_file_unsafe("clear_protection_last_force", 0200, root, ++ priv, ++ &mpq8646_dbg_clear_protection_last_force_fops); ++ debugfs_create_file_unsafe("store_all", 0200, root, priv, ++ &mpq8646_dbg_store_all_fops); ++ debugfs_create_file_unsafe("restore_all", 0200, root, priv, ++ &mpq8646_dbg_restore_all_fops); ++ ++ ctx = devm_kcalloc(&priv->client->dev, ++ ARRAY_SIZE(mpq8646_dbg_regs_unsafe), ++ sizeof(*ctx), GFP_KERNEL); ++ if (!ctx) ++ return; ++ for (i = 0; i < ARRAY_SIZE(mpq8646_dbg_regs_unsafe); i++) { ++ ctx[i].priv = priv; ++ ctx[i].desc = &mpq8646_dbg_regs_unsafe[i]; ++ debugfs_create_file_unsafe(mpq8646_dbg_regs_unsafe[i].name, ++ 0600, root, &ctx[i], ++ &mpq8646_dbg_reg_rw_fops); ++ } ++} ++#else ++static inline void mpq8646_debugfs_register_unsafe(struct mpq8646_priv *priv, ++ struct dentry *root) {} ++#endif /* CONFIG_SENSORS_MPQ8646_DEBUG_UNSAFE */ ++ ++static int mpq8646_dbg_poll_interval_get(void *data, u64 *val) ++{ ++ struct mpq8646_priv *priv = data; ++ ++ *val = priv->alarm_poll_interval_ms; ++ return 0; ++} ++ ++static int mpq8646_dbg_poll_interval_set(void *data, u64 val) ++{ ++ struct mpq8646_priv *priv = data; ++ bool was_off = !priv->alarm_poll_interval_ms; ++ ++ priv->alarm_poll_interval_ms = (u32)val; ++ ++ if (val && was_off && !priv->client->irq) ++ schedule_delayed_work(&priv->alarm_poll_work, ++ msecs_to_jiffies((u32)val)); ++ return 0; ++} ++DEFINE_DEBUGFS_ATTRIBUTE(mpq8646_dbg_poll_interval_fops, ++ mpq8646_dbg_poll_interval_get, ++ mpq8646_dbg_poll_interval_set, "%llu\n"); ++ ++static void mpq8646_debugfs_register(struct mpq8646_priv *priv) ++{ ++ struct dentry *root; ++ size_t i; ++ ++ root = pmbus_get_debugfs_dir(priv->client); ++ if (!root) ++ return; ++ ++ /* MPS extensions: status decode + NVM-backed PROTECTION_LAST */ ++ debugfs_create_file("status_decoded", 0400, root, priv, ++ &mpq8646_dbg_status_decoded_fops); ++ debugfs_create_file("protection_last", 0400, root, priv, ++ &mpq8646_dbg_protection_last_fops); ++ priv->dbg_poll = ++ debugfs_create_file_unsafe("alarm_poll_interval_ms", 0600, ++ root, priv, ++ &mpq8646_dbg_poll_interval_fops); ++ ++ priv->dbg_reg_ctx = devm_kcalloc(&priv->client->dev, ++ ARRAY_SIZE(mpq8646_dbg_regs), ++ sizeof(*priv->dbg_reg_ctx), ++ GFP_KERNEL); ++ if (!priv->dbg_reg_ctx) ++ return; ++ for (i = 0; i < ARRAY_SIZE(mpq8646_dbg_regs); i++) { ++ priv->dbg_reg_ctx[i].priv = priv; ++ priv->dbg_reg_ctx[i].desc = &mpq8646_dbg_regs[i]; ++ debugfs_create_file(mpq8646_dbg_regs[i].name, 0400, ++ root, &priv->dbg_reg_ctx[i], ++ &mpq8646_dbg_reg_fops); ++ } ++ ++ mpq8646_debugfs_register_unsafe(priv, root); ++} ++ ++static void mpq8646_debugfs_unregister(struct mpq8646_priv *priv) ++{ ++ debugfs_remove(priv->dbg_poll); ++} ++#else ++static inline void mpq8646_debugfs_register(struct mpq8646_priv *priv) {} ++static inline void mpq8646_debugfs_unregister(struct mpq8646_priv *priv) {} ++#endif /* CONFIG_DEBUG_FS */ ++ ++static void mpq8646_alarm_poll_work(struct work_struct *work) ++{ ++ struct mpq8646_priv *priv = container_of(to_delayed_work(work), ++ struct mpq8646_priv, ++ alarm_poll_work); ++ ++ if (priv->client->irq) ++ return; /* SMBALERT# wired; polling not needed */ ++ ++ if (!priv->alarm_poll_interval_ms) ++ return; /* polling disabled; don't re-arm */ ++ ++ pmbus_check_and_notify_faults(priv->client); ++ ++ schedule_delayed_work(&priv->alarm_poll_work, ++ msecs_to_jiffies(priv->alarm_poll_interval_ms)); ++} ++ ++static int mpq8646_probe(struct i2c_client *client) ++{ ++ struct device *dev = &client->dev; ++ struct pmbus_driver_info *info; ++ struct mpq8646_priv *priv; ++ u32 voltage_scale; ++ int ret; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ priv->client = client; ++ mutex_init(&priv->mps_lock); ++ memcpy(&priv->info, &mpq8646_info, sizeof(priv->info)); ++ info = &priv->info; ++ ++ info->identify = mpq8646_identify; ++ info->read_byte_data = mpq8646_read_byte_data; ++ info->read_word_data = mpq8646_read_word_data; ++ info->write_word_data = mpq8646_write_word_data; ++ dev->platform_data = &mpq8646_no_pec_pdata; ++ ++#if IS_ENABLED(CONFIG_REGULATOR) ++ info->reg_desc = mpq8646_reg_desc; ++ info->num_regulators = ARRAY_SIZE(mpq8646_reg_desc); ++#endif ++ ++ INIT_DELAYED_WORK(&priv->alarm_poll_work, mpq8646_alarm_poll_work); ++ priv->alarm_poll_interval_ms = MPQ8646_ALARM_POLL_MS_DEFAULT; ++ ++ if (!device_property_read_u32(dev, "mps,vout-fb-divider-ratio-permille", ++ &voltage_scale)) { ++ if (voltage_scale > MPQ8646_VOUT_SCALE_LOOP_MAX) ++ return -EINVAL; ++ ++ ret = i2c_smbus_write_word_data(client, PMBUS_VOUT_SCALE_LOOP, ++ voltage_scale); ++ if (ret) ++ return ret; ++ } ++ ++ ret = pmbus_do_probe(client, info); ++ if (ret) ++ return ret; ++ ++ mpq8646_debugfs_register(priv); ++ ++ if (!client->irq) ++ schedule_delayed_work(&priv->alarm_poll_work, ++ msecs_to_jiffies(priv->alarm_poll_interval_ms)); ++ ++#if IS_ENABLED(CONFIG_NVMEM) ++ { ++ struct nvmem_config cfg = { ++ .dev = &client->dev, ++ .name = dev_name(&client->dev), ++ .owner = THIS_MODULE, ++ .read_only = true, ++ .root_only = true, ++ .word_size = 1, ++ .stride = 1, ++ .size = MPQ8646_NVMEM_SIZE, ++ .reg_read = mpq8646_nvmem_read, ++ .priv = priv, ++ }; ++ struct nvmem_device *nv = devm_nvmem_register(&client->dev, &cfg); ++ ++ if (IS_ERR(nv)) ++ dev_warn(&client->dev, ++ "nvmem snapshot register failed (%pe)\n", ++ nv); ++ } ++#endif ++ return 0; ++}; ++ ++static void mpq8646_remove(struct i2c_client *client) ++{ ++ struct mpq8646_priv *priv = mpq8646_priv_from_client(client); ++ ++ mpq8646_debugfs_unregister(priv); ++ cancel_delayed_work_sync(&priv->alarm_poll_work); ++} ++ ++static struct i2c_driver mpq8646_driver = { ++ .driver = { ++ .name = "mpq8646", ++ .of_match_table = of_match_ptr(mpq8646_of_match), ++ }, ++ .probe = mpq8646_probe, ++ .remove = mpq8646_remove, ++ .id_table = mpq8646_id, ++}; ++ ++module_i2c_driver(mpq8646_driver); ++ ++MODULE_AUTHOR("Vincent Jardin "); ++MODULE_DESCRIPTION("PMBus driver for MPS MPQ8646 (extended observability)"); ++MODULE_LICENSE("GPL"); ++MODULE_IMPORT_NS("PMBUS"); +diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h +--- a/drivers/hwmon/pmbus/pmbus.h ++++ b/drivers/hwmon/pmbus/pmbus.h +@@ -421,10 +421,12 @@ enum pmbus_data_format { linear = 0, ieee754, direct, vid }; + enum vrm_version { vr11 = 0, vr12, vr13, imvp9, amd625mv, nvidia195mv }; + + /* PMBus revision identifiers */ +-#define PMBUS_REV_10 0x00 /* PMBus revision 1.0 */ +-#define PMBUS_REV_11 0x11 /* PMBus revision 1.1 */ +-#define PMBUS_REV_12 0x22 /* PMBus revision 1.2 */ +-#define PMBUS_REV_13 0x33 /* PMBus revision 1.3 */ ++#define PMBUS_REV_10 0x00 /* PMBus revision 1.0 */ ++#define PMBUS_REV_11 0x11 /* PMBus revision 1.1 */ ++#define PMBUS_REV_12 0x22 /* PMBus revision 1.2 */ ++#define PMBUS_REV_13 0x33 /* PMBus revision 1.3 */ ++#define PMBUS_REV_131 0x44 /* PMBus revision 1.3.1 */ ++#define PMBUS_REV_14 0x55 /* PMBus revision 1.4 */ + + /* Operation type flags for pmbus_update_ts */ + #define PMBUS_OP_WRITE BIT(0) +@@ -489,6 +491,16 @@ struct pmbus_driver_info { + int access_delay; /* in microseconds */ + int write_delay; /* in microseconds */ + int page_change_delay; /* in microseconds */ ++ ++ /* ++ * Some chips do not support the PMBUS_REVISION command. ++ * Drivers for such chips can report the supported PMBus revision here. ++ * ++ * Drivers must set have_pmbus_revision to true and provide the ++ * supported PMBus version in pmbus_revision. ++ */ ++ bool have_pmbus_revision; /* true if pmbus_revision is valid */ ++ u8 pmbus_revision; /* PMBus revision */ + }; + + /* Regulator ops */ +@@ -550,6 +562,7 @@ void pmbus_set_update(struct i2c_client *client, u8 reg, bool update); + void pmbus_wait(struct i2c_client *client); + void pmbus_update_ts(struct i2c_client *client, int op); + int pmbus_set_page(struct i2c_client *client, int page, int phase); ++int pmbus_read_smbus_i2c_block_data(struct i2c_client *client, u8 reg, char *data_buf); + int pmbus_read_word_data(struct i2c_client *client, int page, int phase, + u8 reg); + int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg, +@@ -561,6 +574,7 @@ int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, + int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, + u8 mask, u8 value); + void pmbus_clear_faults(struct i2c_client *client); ++void pmbus_check_and_notify_faults(struct i2c_client *client); + bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg); + bool pmbus_check_word_register(struct i2c_client *client, int page, int reg); + int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info); +diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c +--- a/drivers/hwmon/pmbus/pmbus_core.c ++++ b/drivers/hwmon/pmbus/pmbus_core.c +@@ -96,7 +96,8 @@ struct pmbus_data { + + u32 flags; /* from platform data */ + +- u8 revision; /* The PMBus revision the device is compliant with */ ++ bool have_pmbus_revision; ++ u8 revision; /* The PMBus revision the device is compliant with */ + + int exponent[PMBUS_PAGES]; + /* linear mode: exponent for output voltages */ +@@ -181,7 +182,12 @@ EXPORT_SYMBOL_NS_GPL(pmbus_set_update, "PMBUS"); + void pmbus_wait(struct i2c_client *client) + { + struct pmbus_data *data = i2c_get_clientdata(client); +- s64 delay = ktime_us_delta(data->next_access_backoff, ktime_get()); ++ s64 delay; ++ ++ if (!data) ++ return; ++ ++ delay = ktime_us_delta(data->next_access_backoff, ktime_get()); + + if (delay > 0) + fsleep(delay); +@@ -192,8 +198,14 @@ EXPORT_SYMBOL_NS_GPL(pmbus_wait, "PMBUS"); + void pmbus_update_ts(struct i2c_client *client, int op) + { + struct pmbus_data *data = i2c_get_clientdata(client); +- const struct pmbus_driver_info *info = data->info; +- int delay = info->access_delay; ++ const struct pmbus_driver_info *info; ++ int delay; ++ ++ if (!data) ++ return; ++ ++ info = data->info; ++ delay = info->access_delay; + + if (op & PMBUS_OP_WRITE) + delay = max(delay, info->write_delay); +@@ -517,6 +529,81 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, + } + EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, "PMBUS"); + ++/** ++ * pmbus_read_smbus_i2c_block_data() - Read SMBus/I2C block data ++ * @client: Handle to slave device ++ * @reg: Byte interpreted by slave ++ * @data_buf: Byte array into which data will be read ++ * Return: Negative errno or number of bytes read ++ * ++ * PMBus internal function to read a SMBus block from a PMBus chip. ++ * ++ * PMBus chips report various properties using SMBus block read operations. ++ * However, not all I2C controllers support this operation. ++ * ++ * Execute SMBus block read if supported. If not supported, but SMBus I2C block ++ * read is supported, use it instead. Note that at most 31 data bytes can be ++ * read from the device if i2c_smbus_read_i2c_block_data() is used to read the ++ * data. This is a SMBUs protocol limit which can not be avoided. ++ * ++ * Return -EOPNOTSUPP if neither I2C_FUNC_SMBUS_READ_BLOCK_DATA nor ++ * I2C_FUNC_SMBUS_READ_I2C_BLOCK is supported. ++ * ++ * Callers must hold pmbus_lock or execute calls from the probe function. ++ */ ++int pmbus_read_smbus_i2c_block_data(struct i2c_client *client, u8 reg, char *data_buf) ++{ ++ u8 buf[I2C_SMBUS_BLOCK_MAX]; ++ int blen, len, ret; ++ ++ if (i2c_check_functionality(client->adapter, ++ I2C_FUNC_SMBUS_READ_BLOCK_DATA)) { ++ pmbus_wait(client); ++ ret = i2c_smbus_read_block_data(client, reg, data_buf); ++ pmbus_update_ts(client, 0); ++ return ret; ++ } ++ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { ++ dev_err_once(&client->dev, "I2C adapter does not support I2C_FUNC_SMBUS_READ_I2C_BLOCK\n"); ++ return -EOPNOTSUPP; ++ } ++ ++ /* ++ * If the returned data is valid SMBus block data, the first byte ++ * must be the data length. ++ * ++ * i2c_smbus_read_i2c_block_data() may return an error if the chip ++ * sends NACK before the number of requested bytes is received. ++ * Handle this by reading the data length first, then reading the ++ * entire message up to I2C_SMBUS_BLOCK_MAX bytes. This ensures ++ * that requested number of bytes never exceeds the number of ++ * bytes sent by the chip. ++ */ ++ pmbus_wait(client); ++ ret = i2c_smbus_read_i2c_block_data(client, reg, 1, buf); ++ pmbus_update_ts(client, 0); ++ if (ret < 0) ++ return ret; ++ ++ len = buf[0]; ++ if (len == 0) ++ return 0; ++ blen = len; ++ if (len >= I2C_SMBUS_BLOCK_MAX) ++ len = I2C_SMBUS_BLOCK_MAX - 1; ++ pmbus_wait(client); ++ ret = i2c_smbus_read_i2c_block_data(client, reg, len + 1, buf); ++ pmbus_update_ts(client, 0); ++ if (ret < 0) ++ return ret; ++ if (buf[0] != blen) ++ return -EIO; ++ memcpy(data_buf, buf + 1, len); ++ return len; ++} ++EXPORT_SYMBOL_NS_GPL(pmbus_read_smbus_i2c_block_data, "PMBUS"); ++ + static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg, + char *data_buf) + { +@@ -526,11 +613,7 @@ static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg, + if (rv < 0) + return rv; + +- pmbus_wait(client); +- rv = i2c_smbus_read_block_data(client, reg, data_buf); +- pmbus_update_ts(client, 0); +- +- return rv; ++ return pmbus_read_smbus_i2c_block_data(client, reg, data_buf); + } + + static struct pmbus_sensor *pmbus_find_sensor(struct pmbus_data *data, int page, +@@ -2849,9 +2932,16 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, + if (!(data->flags & PMBUS_NO_WRITE_PROTECT)) + pmbus_init_wp(client, data); + +- ret = i2c_smbus_read_byte_data(client, PMBUS_REVISION); +- if (ret >= 0) +- data->revision = ret; ++ if (info->have_pmbus_revision) { ++ data->have_pmbus_revision = true; ++ data->revision = info->pmbus_revision; ++ } else { ++ ret = i2c_smbus_read_byte_data(client, PMBUS_REVISION); ++ if (ret >= 0) { ++ data->have_pmbus_revision = true; ++ data->revision = ret; ++ } ++ } + + if (data->info->pages) + pmbus_clear_faults(client); +@@ -3455,10 +3545,9 @@ static int pmbus_write_smbalert_mask(struct i2c_client *client, u8 page, u8 reg, + return ret; + } + +-static irqreturn_t pmbus_fault_handler(int irq, void *pdata) ++void pmbus_check_and_notify_faults(struct i2c_client *client) + { +- struct pmbus_data *data = pdata; +- struct i2c_client *client = to_i2c_client(data->dev); ++ struct pmbus_data *data = i2c_get_clientdata(client); + int i, status, event; + + guard(pmbus_lock)(client); +@@ -3471,6 +3560,15 @@ static irqreturn_t pmbus_fault_handler(int irq, void *pdata) + } + + pmbus_clear_faults(client); ++} ++EXPORT_SYMBOL_NS_GPL(pmbus_check_and_notify_faults, "PMBUS"); ++ ++static irqreturn_t pmbus_fault_handler(int irq, void *pdata) ++{ ++ struct pmbus_data *data = pdata; ++ struct i2c_client *client = to_i2c_client(data->dev); ++ ++ pmbus_check_and_notify_faults(client); + + return IRQ_HANDLED; + } +@@ -3514,10 +3612,8 @@ static int pmbus_irq_setup(struct i2c_client *client, struct pmbus_data *data) + /* Register notifiers */ + err = devm_request_threaded_irq(dev, client->irq, NULL, pmbus_fault_handler, + IRQF_ONESHOT, "pmbus-irq", data); +- if (err) { +- dev_err(dev, "failed to request an irq %d\n", err); ++ if (err) + return err; +- } + + return 0; + } +@@ -3543,6 +3639,17 @@ static int pmbus_debugfs_get(void *data, u64 *val) + DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops, pmbus_debugfs_get, NULL, + "0x%02llx\n"); + ++static int pmbus_debugfs_get_revision(void *data, u64 *val) ++{ ++ struct pmbus_data *pdata = data; ++ ++ *val = pdata->revision; ++ ++ return 0; ++} ++DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_revision_ops, pmbus_debugfs_get_revision, NULL, ++ "0x%02llx\n"); ++ + static int pmbus_debugfs_get_status(void *data, u64 *val) + { + struct pmbus_debugfs_entry *entry = data; +@@ -3698,14 +3805,9 @@ static void pmbus_init_debugfs(struct i2c_client *client, + &entries[idx++], + &pmbus_debugfs_ops); + } +- if (pmbus_check_byte_register(client, 0, PMBUS_REVISION)) { +- entries[idx].client = client; +- entries[idx].page = 0; +- entries[idx].reg = PMBUS_REVISION; +- debugfs_create_file("pmbus_revision", 0444, debugfs, +- &entries[idx++], +- &pmbus_debugfs_ops); +- } ++ if (data->have_pmbus_revision) ++ debugfs_create_file("pmbus_revision", 0444, debugfs, data, ++ &pmbus_debugfs_revision_ops); + + for (i = 0; i < ARRAY_SIZE(pmbus_debugfs_block_data); i++) { + const struct pmbus_debugfs_data *d = &pmbus_debugfs_block_data[i]; +diff --git a/drivers/hwmon/pmbus/sq24860.c b/drivers/hwmon/pmbus/sq24860.c +new file mode 100644 +--- /dev/null ++++ b/drivers/hwmon/pmbus/sq24860.c +@@ -0,0 +1,430 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Author: Ziming Zhu ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "pmbus.h" ++ ++#define SQ24860_IIN_CAL_GAIN 0x38 ++#define SQ24860_READ_VAUX 0xd0 ++#define SQ24860_READ_VIN_MIN 0xd1 ++#define SQ24860_READ_VIN_PEAK 0xd2 ++#define SQ24860_READ_IIN_PEAK 0xd4 ++#define SQ24860_READ_PIN_PEAK 0xd5 ++#define SQ24860_READ_TEMP_AVG 0xd6 ++#define SQ24860_READ_TEMP_PEAK 0xd7 ++#define SQ24860_READ_VOUT_MIN 0xda ++#define SQ24860_READ_VIN_AVG 0xdc ++#define SQ24860_READ_VOUT_AVG 0xdd ++#define SQ24860_READ_IIN_AVG 0xde ++#define SQ24860_READ_PIN_AVG 0xdf ++#define SQ24860_VIREF 0xe0 ++#define SQ24860_PK_MIN_AVG 0xea ++#define PK_MIN_AVG_RST_PEAK BIT(7) ++#define PK_MIN_AVG_RST_AVG BIT(6) ++#define PK_MIN_AVG_RST_MIN BIT(5) ++#define PK_MIN_AVG_AVG_CNT GENMASK(2, 0) ++#define SQ24860_MFR_WRITE_PROTECT 0xf8 ++#define SQ24860_UNLOCKED BIT(7) ++ ++#define SQ24860_8B_SHIFT 2 ++#define SQ24860_IIN_OCF_NUM 1000000 ++#define SQ24860_IIN_OCF_DIV 129278 ++#define SQ24860_IIN_OCF_OFF 165 ++ ++#define PK_MIN_AVG_RST_MASK (PK_MIN_AVG_RST_PEAK | \ ++ PK_MIN_AVG_RST_AVG | \ ++ PK_MIN_AVG_RST_MIN) ++#define SQ24860_MAX_SAMPLES BIT(FIELD_MAX(PK_MIN_AVG_AVG_CNT)) ++/* ++ * Arbitrary default Rimon value: 1.6kOhm ++ */ ++#define SQ24860_DEFAULT_RIMON 1600000000 ++#define SQ24860_GIMON 18180 ++ ++#define SQ24860_VAUX_DIV 20 ++ ++static int sq24860_write_iin_cal_gain(struct i2c_client *client, u32 rimon) ++{ ++ u64 temp = 6400ULL * 1000000000ULL * 1000ULL; ++ u64 denom; ++ u64 word; ++ ++ if (!rimon) ++ return -EINVAL; ++ ++ denom = (u64)rimon * SQ24860_GIMON; ++ word = div64_u64(temp, denom); ++ if (!word || word > U16_MAX) ++ return -EINVAL; ++ ++ return i2c_smbus_write_word_data(client, SQ24860_IIN_CAL_GAIN, ++ (u16)word); ++} ++ ++static int sq24860_mfr_write_protect_set(struct i2c_client *client, ++ u8 protect) ++{ ++ u8 val; ++ ++ switch (protect) { ++ case 0: ++ val = 0xa2; ++ break; ++ case PB_WP_ALL: ++ val = 0x0; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return pmbus_write_byte_data(client, -1, SQ24860_MFR_WRITE_PROTECT, ++ val); ++} ++ ++static int sq24860_mfr_write_protect_get(struct i2c_client *client) ++{ ++ int ret = pmbus_read_byte_data(client, -1, SQ24860_MFR_WRITE_PROTECT); ++ ++ if (ret < 0) ++ return ret; ++ ++ return (ret & SQ24860_UNLOCKED) ? 0 : PB_WP_ALL; ++} ++ ++static int sq24860_read_word_data(struct i2c_client *client, ++ int page, int phase, int reg) ++{ ++ int ret; ++ ++ switch (reg) { ++ case PMBUS_VIRT_READ_VIN_MAX: ++ ret = pmbus_read_word_data(client, page, phase, ++ SQ24860_READ_VIN_PEAK); ++ break; ++ ++ case PMBUS_VIRT_READ_VIN_MIN: ++ ret = pmbus_read_word_data(client, page, phase, ++ SQ24860_READ_VIN_MIN); ++ break; ++ ++ case PMBUS_VIRT_READ_VIN_AVG: ++ ret = pmbus_read_word_data(client, page, phase, ++ SQ24860_READ_VIN_AVG); ++ break; ++ ++ case PMBUS_VIRT_READ_VOUT_MIN: ++ ret = pmbus_read_word_data(client, page, phase, ++ SQ24860_READ_VOUT_MIN); ++ break; ++ ++ case PMBUS_VIRT_READ_VOUT_AVG: ++ ret = pmbus_read_word_data(client, page, phase, ++ SQ24860_READ_VOUT_AVG); ++ break; ++ ++ case PMBUS_VIRT_READ_IIN_AVG: ++ ret = pmbus_read_word_data(client, page, phase, ++ SQ24860_READ_IIN_AVG); ++ break; ++ ++ case PMBUS_VIRT_READ_IIN_MAX: ++ ret = pmbus_read_word_data(client, page, phase, ++ SQ24860_READ_IIN_PEAK); ++ break; ++ ++ case PMBUS_VIRT_READ_TEMP_AVG: ++ ret = pmbus_read_word_data(client, page, phase, ++ SQ24860_READ_TEMP_AVG); ++ break; ++ ++ case PMBUS_VIRT_READ_TEMP_MAX: ++ ret = pmbus_read_word_data(client, page, phase, ++ SQ24860_READ_TEMP_PEAK); ++ break; ++ ++ case PMBUS_VIRT_READ_PIN_AVG: ++ ret = pmbus_read_word_data(client, page, phase, ++ SQ24860_READ_PIN_AVG); ++ break; ++ ++ case PMBUS_VIRT_READ_PIN_MAX: ++ ret = pmbus_read_word_data(client, page, phase, ++ SQ24860_READ_PIN_PEAK); ++ break; ++ ++ case PMBUS_VIRT_READ_VMON: ++ ret = pmbus_read_word_data(client, page, phase, ++ SQ24860_READ_VAUX); ++ if (ret < 0) ++ break; ++ ret = DIV_ROUND_CLOSEST(ret, SQ24860_VAUX_DIV); ++ break; ++ ++ case PMBUS_VIN_UV_WARN_LIMIT: ++ case PMBUS_VIN_UV_FAULT_LIMIT: ++ case PMBUS_VIN_OV_WARN_LIMIT: ++ case PMBUS_VIN_OV_FAULT_LIMIT: ++ case PMBUS_VOUT_UV_WARN_LIMIT: ++ case PMBUS_IIN_OC_WARN_LIMIT: ++ case PMBUS_OT_WARN_LIMIT: ++ case PMBUS_OT_FAULT_LIMIT: ++ case PMBUS_PIN_OP_WARN_LIMIT: ++ /* ++ * These registers provide an 8 bits value instead of a ++ * 10bits one. Just shifting twice the register value is ++ * enough to make the sensor type conversion work, even ++ * if the datasheet provides different m, b and R for ++ * those. ++ */ ++ ret = pmbus_read_word_data(client, page, phase, reg); ++ if (ret < 0) ++ break; ++ ret <<= SQ24860_8B_SHIFT; ++ break; ++ ++ case PMBUS_IIN_OC_FAULT_LIMIT: ++ /* ++ * VIREF directly sets the over-current limit at which the eFuse ++ * will turn the FET off and trigger a fault. Expose it through ++ * this generic property instead of a manufacturer specific one. ++ */ ++ ret = pmbus_read_byte_data(client, page, SQ24860_VIREF); ++ if (ret < 0) ++ break; ++ ret = DIV_ROUND_CLOSEST(ret * SQ24860_IIN_OCF_NUM, ++ SQ24860_IIN_OCF_DIV); ++ ret += SQ24860_IIN_OCF_OFF; ++ break; ++ ++ case PMBUS_VIRT_SAMPLES: ++ ret = pmbus_read_byte_data(client, page, SQ24860_PK_MIN_AVG); ++ if (ret < 0) ++ break; ++ ret = BIT(FIELD_GET(PK_MIN_AVG_AVG_CNT, ret)); ++ break; ++ ++ case PMBUS_VIRT_RESET_TEMP_HISTORY: ++ case PMBUS_VIRT_RESET_VIN_HISTORY: ++ case PMBUS_VIRT_RESET_IIN_HISTORY: ++ case PMBUS_VIRT_RESET_PIN_HISTORY: ++ case PMBUS_VIRT_RESET_VOUT_HISTORY: ++ ret = 0; ++ break; ++ ++ default: ++ ret = -ENODATA; ++ break; ++ } ++ ++ return ret; ++} ++ ++static int sq24860_write_word_data(struct i2c_client *client, ++ int page, int reg, u16 value) ++{ ++ int ret; ++ ++ switch (reg) { ++ case PMBUS_VIN_UV_WARN_LIMIT: ++ case PMBUS_VIN_UV_FAULT_LIMIT: ++ case PMBUS_VIN_OV_WARN_LIMIT: ++ case PMBUS_VIN_OV_FAULT_LIMIT: ++ case PMBUS_VOUT_UV_WARN_LIMIT: ++ case PMBUS_IIN_OC_WARN_LIMIT: ++ case PMBUS_OT_WARN_LIMIT: ++ case PMBUS_OT_FAULT_LIMIT: ++ case PMBUS_PIN_OP_WARN_LIMIT: ++ value = max_t(s16, (s16)value, 0); ++ value >>= SQ24860_8B_SHIFT; ++ value = clamp_val(value, 0, 0xff); ++ ret = pmbus_write_word_data(client, page, reg, value); ++ break; ++ ++ case PMBUS_IIN_OC_FAULT_LIMIT: ++ value = max_t(s16, (s16)value, SQ24860_IIN_OCF_OFF); ++ value -= SQ24860_IIN_OCF_OFF; ++ value = DIV_ROUND_CLOSEST(((unsigned int)value) * SQ24860_IIN_OCF_DIV, ++ SQ24860_IIN_OCF_NUM); ++ value = clamp_val(value, 0, 0x3f); ++ ret = pmbus_write_byte_data(client, page, SQ24860_VIREF, value); ++ break; ++ ++ case PMBUS_VIRT_SAMPLES: ++ value = clamp_val(value, 1, SQ24860_MAX_SAMPLES); ++ value = ilog2(value); ++ ret = pmbus_update_byte_data(client, page, SQ24860_PK_MIN_AVG, ++ PK_MIN_AVG_AVG_CNT, ++ FIELD_PREP(PK_MIN_AVG_AVG_CNT, value)); ++ break; ++ ++ case PMBUS_VIRT_RESET_TEMP_HISTORY: ++ case PMBUS_VIRT_RESET_VIN_HISTORY: ++ case PMBUS_VIRT_RESET_IIN_HISTORY: ++ case PMBUS_VIRT_RESET_PIN_HISTORY: ++ case PMBUS_VIRT_RESET_VOUT_HISTORY: ++ /* ++ * SQ24860 has history resets based on MIN/AVG/PEAK instead of per ++ * sensor type. Exposing this quirk in hwmon is not desirable so ++ * reset MIN, AVG and PEAK together. Even is there effectively only ++ * one reset, which resets everything, expose the 5 entries so ++ * userspace is not required map a sensor type to another to trigger ++ * a reset ++ */ ++ ret = pmbus_update_byte_data(client, 0, SQ24860_PK_MIN_AVG, ++ PK_MIN_AVG_RST_MASK, ++ PK_MIN_AVG_RST_MASK); ++ break; ++ ++ default: ++ ret = -ENODATA; ++ break; ++ } ++ ++ return ret; ++} ++ ++static int sq24860_read_byte_data(struct i2c_client *client, ++ int page, int reg) ++{ ++ int ret; ++ ++ switch (reg) { ++ case PMBUS_WRITE_PROTECT: ++ ret = sq24860_mfr_write_protect_get(client); ++ break; ++ ++ default: ++ ret = -ENODATA; ++ break; ++ } ++ ++ return ret; ++} ++ ++static int sq24860_write_byte_data(struct i2c_client *client, ++ int page, int reg, u8 byte) ++{ ++ int ret; ++ ++ switch (reg) { ++ case PMBUS_WRITE_PROTECT: ++ ret = sq24860_mfr_write_protect_set(client, byte); ++ break; ++ ++ default: ++ ret = -ENODATA; ++ break; ++ } ++ ++ return ret; ++} ++ ++#if IS_ENABLED(CONFIG_SENSORS_SQ24860_REGULATOR) ++static const struct regulator_desc sq24860_reg_desc[] = { ++ PMBUS_REGULATOR_ONE_NODE("vout"), ++}; ++#endif ++ ++static const struct pmbus_driver_info sq24860_base_info = { ++ .pages = 1, ++ .format[PSC_VOLTAGE_IN] = direct, ++ .m[PSC_VOLTAGE_IN] = 64, ++ .b[PSC_VOLTAGE_IN] = 0, ++ .R[PSC_VOLTAGE_IN] = 0, ++ .format[PSC_VOLTAGE_OUT] = direct, ++ .m[PSC_VOLTAGE_OUT] = 64, ++ .b[PSC_VOLTAGE_OUT] = 0, ++ .R[PSC_VOLTAGE_OUT] = 0, ++ .format[PSC_TEMPERATURE] = direct, ++ .m[PSC_TEMPERATURE] = 1, ++ .b[PSC_TEMPERATURE] = 0, ++ .R[PSC_TEMPERATURE] = 0, ++ /* ++ * Current and power measurements depend on the calibration gain ++ * programmed from the board-specific IMON resistor value. ++ */ ++ .format[PSC_CURRENT_IN] = direct, ++ .m[PSC_CURRENT_IN] = 16, ++ .b[PSC_CURRENT_IN] = 0, ++ .R[PSC_CURRENT_IN] = 0, ++ .format[PSC_POWER] = direct, ++ .m[PSC_POWER] = 2, ++ .b[PSC_POWER] = 0, ++ .R[PSC_POWER] = 0, ++ .func[0] = PMBUS_HAVE_VIN | ++ PMBUS_HAVE_VOUT | ++ PMBUS_HAVE_VMON | ++ PMBUS_HAVE_IIN | ++ PMBUS_HAVE_PIN | ++ PMBUS_HAVE_TEMP | ++ PMBUS_HAVE_STATUS_VOUT | ++ PMBUS_HAVE_STATUS_IOUT | ++ PMBUS_HAVE_STATUS_INPUT | ++ PMBUS_HAVE_STATUS_TEMP | ++ PMBUS_HAVE_SAMPLES, ++ .read_word_data = sq24860_read_word_data, ++ .write_word_data = sq24860_write_word_data, ++ .read_byte_data = sq24860_read_byte_data, ++ .write_byte_data = sq24860_write_byte_data, ++ ++#if IS_ENABLED(CONFIG_SENSORS_SQ24860_REGULATOR) ++ .reg_desc = sq24860_reg_desc, ++ .num_regulators = ARRAY_SIZE(sq24860_reg_desc), ++#endif ++}; ++ ++static const struct i2c_device_id sq24860_i2c_id[] = { ++ { "sq24860" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(i2c, sq24860_i2c_id); ++ ++static const struct of_device_id sq24860_of_match[] = { ++ { .compatible = "silergy,sq24860" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, sq24860_of_match); ++ ++static int sq24860_probe(struct i2c_client *client) ++{ ++ struct device *dev = &client->dev; ++ struct pmbus_driver_info *info; ++ u32 rimon; ++ int ret; ++ ++ if (device_property_read_u32(dev, "silergy,rimon-micro-ohms", &rimon)) ++ rimon = SQ24860_DEFAULT_RIMON; ++ ret = sq24860_write_iin_cal_gain(client, rimon); ++ if (ret < 0) ++ return dev_err_probe(&client->dev, ret, ++ "Failed to set gain\n"); ++ info = devm_kmemdup(dev, &sq24860_base_info, sizeof(*info), GFP_KERNEL); ++ if (!info) ++ return -ENOMEM; ++ ++ return pmbus_do_probe(client, info); ++} ++ ++static struct i2c_driver sq24860_driver = { ++ .driver = { ++ .name = "sq24860", ++ .of_match_table = sq24860_of_match, ++ }, ++ .probe = sq24860_probe, ++ .id_table = sq24860_i2c_id, ++}; ++module_i2c_driver(sq24860_driver); ++ ++MODULE_AUTHOR("Ziming Zhu "); ++MODULE_DESCRIPTION("PMBUS driver for SQ24860 eFuse"); ++MODULE_LICENSE("GPL"); ++MODULE_IMPORT_NS("PMBUS"); +diff --git a/drivers/hwmon/pmbus/vt7505.c b/drivers/hwmon/pmbus/vt7505.c +new file mode 100644 +--- /dev/null ++++ b/drivers/hwmon/pmbus/vt7505.c +@@ -0,0 +1,287 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Hardware monitoring driver for Analog Devices MAX16545/MAX16550 and ++ * Volterra VT7505 PMBus controllers. ++ * ++ * Copyright 2026 Hewlett Packard Enterprise Development LP ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "pmbus.h" ++ ++#define VT7505_MFR_CONFIG 0xd0 ++#define VT7505_CFG_OCP_S_FILT_MASK GENMASK(15, 14) ++ ++#define VT7505_MFR_PEAK_VIN 0xd1 ++#define VT7505_MFR_PEAK_IOUT 0xd2 ++#define VT7505_MFR_PEAK_PIN 0xd3 ++#define VT7505_MFR_PEAK_TEMP 0xd4 ++#define VT7505_MFR_CLEAR_PEAKS 0xd5 ++#define VT7505_MFR_PEAK_VOUT 0xfd ++ ++#define VT7505_RLOAD_DEFAULT 4750 ++/* Largest RLOAD for which coeff * rload / 1000 still fits in s32. */ ++#define VT7505_RLOAD_MAX 283383959U ++ ++struct vt7505_chip_data { ++ int temp_m; ++ int temp_b; ++ bool has_ocp_filter; ++}; ++ ++static const struct vt7505_chip_data max16545_data = { ++ .temp_m = 205, ++ .temp_b = 6545, ++}; ++ ++static const struct vt7505_chip_data max16550_data = { ++ .temp_m = 199, ++ .temp_b = 7046, ++ .has_ocp_filter = true, ++}; ++ ++static const struct vt7505_chip_data vt7505_data = { ++ .temp_m = 205, ++ .temp_b = 6545, ++ .has_ocp_filter = true, ++}; ++ ++static int vt7505_read_word_data(struct i2c_client *client, int page, ++ int phase, int reg) ++{ ++ switch (reg) { ++ case PMBUS_VIRT_READ_VIN_MAX: ++ return pmbus_read_word_data(client, page, phase, ++ VT7505_MFR_PEAK_VIN); ++ case PMBUS_VIRT_READ_IOUT_MAX: ++ return pmbus_read_word_data(client, page, phase, ++ VT7505_MFR_PEAK_IOUT); ++ case PMBUS_VIRT_READ_PIN_MAX: ++ return pmbus_read_word_data(client, page, phase, ++ VT7505_MFR_PEAK_PIN); ++ case PMBUS_VIRT_READ_TEMP_MAX: ++ return pmbus_read_word_data(client, page, phase, ++ VT7505_MFR_PEAK_TEMP); ++ case PMBUS_VIRT_READ_VOUT_MAX: ++ return pmbus_read_word_data(client, page, phase, ++ VT7505_MFR_PEAK_VOUT); ++ case PMBUS_VIRT_RESET_VIN_HISTORY: ++ case PMBUS_VIRT_RESET_IOUT_HISTORY: ++ case PMBUS_VIRT_RESET_PIN_HISTORY: ++ case PMBUS_VIRT_RESET_TEMP_HISTORY: ++ case PMBUS_VIRT_RESET_VOUT_HISTORY: ++ return 0; ++ default: ++ return -ENODATA; ++ } ++} ++ ++static int vt7505_write_word_data(struct i2c_client *client, int page, ++ int reg, u16 word) ++{ ++ switch (reg) { ++ /* ++ * A single reset command clears all peak values. CLEAR_PEAKS is a ++ * send-byte command; the device NAKs a word or byte-data write to it. ++ */ ++ case PMBUS_VIRT_RESET_VIN_HISTORY: ++ case PMBUS_VIRT_RESET_IOUT_HISTORY: ++ case PMBUS_VIRT_RESET_PIN_HISTORY: ++ case PMBUS_VIRT_RESET_TEMP_HISTORY: ++ case PMBUS_VIRT_RESET_VOUT_HISTORY: ++ return pmbus_write_byte(client, page, ++ VT7505_MFR_CLEAR_PEAKS); ++ default: ++ return -ENODATA; ++ } ++} ++ ++/* ++ * None of these controllers implement the standard PMBus WRITE_PROTECT ++ * (0x10) register, so tell the core not to access it. ++ */ ++static struct pmbus_platform_data vt7505_pdata = { ++ .flags = PMBUS_NO_WRITE_PROTECT, ++}; ++ ++static int vt7505_set_ocp_filter(struct i2c_client *client) ++{ ++ u32 ocp_us; ++ u8 field; ++ int ret; ++ u16 word; ++ ++ if (of_property_read_u32(client->dev.of_node, "adi,ocp-severe-filter-us", ++ &ocp_us)) ++ return 0; ++ ++ switch (ocp_us) { ++ case 0: ++ field = 0; ++ break; ++ case 1: ++ field = 1; ++ break; ++ case 2: ++ field = 2; ++ break; ++ case 10: ++ field = 3; ++ break; ++ default: ++ return dev_err_probe(&client->dev, -EINVAL, ++ "invalid adi,ocp-severe-filter-us value %u\n", ++ ocp_us); ++ } ++ ++ ret = i2c_smbus_read_word_data(client, VT7505_MFR_CONFIG); ++ if (ret < 0) ++ return dev_err_probe(&client->dev, ret, ++ "failed to read MFR_CONFIG\n"); ++ ++ word = ret & ~VT7505_CFG_OCP_S_FILT_MASK; ++ word |= FIELD_PREP(VT7505_CFG_OCP_S_FILT_MASK, field); ++ ++ ret = i2c_smbus_write_word_data(client, VT7505_MFR_CONFIG, word); ++ if (ret < 0) ++ return dev_err_probe(&client->dev, ret, ++ "failed to write MFR_CONFIG\n"); ++ ++ return 0; ++} ++ ++static void vt7505_set_m(int *m, u32 rload) ++{ ++ u64 val = (u64)*m * rload; ++ ++ /* rload is range-checked in probe, so the result fits in int. */ ++ *m = DIV_ROUND_CLOSEST_ULL(val, 1000); ++} ++ ++static int vt7505_probe(struct i2c_client *client) ++{ ++ struct device *dev = &client->dev; ++ const struct vt7505_chip_data *chip; ++ struct pmbus_driver_info *info; ++ u32 rload; ++ int ret; ++ ++ chip = i2c_get_match_data(client); ++ if (!chip) ++ return -ENODEV; ++ ++ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); ++ if (!info) ++ return -ENOMEM; ++ ++ dev->platform_data = &vt7505_pdata; ++ ++ /* ++ * The m coefficient used in the direct-format current and power ++ * calculations depends on RLOAD, the external current-report resistor ++ * connected between the ILOAD pin and ground. Use the default value if ++ * none is specified. ++ */ ++ if (of_property_read_u32(dev->of_node, "adi,rload-ohms", &rload)) ++ rload = VT7505_RLOAD_DEFAULT; ++ ++ if (!rload || rload > VT7505_RLOAD_MAX) ++ return dev_err_probe(dev, -EINVAL, ++ "adi,rload-ohms must be 1-%u\n", ++ VT7505_RLOAD_MAX); ++ ++ info->pages = 1; ++ info->read_word_data = vt7505_read_word_data; ++ info->write_word_data = vt7505_write_word_data; ++ ++ info->format[PSC_VOLTAGE_IN] = direct; ++ info->format[PSC_VOLTAGE_OUT] = direct; ++ info->format[PSC_CURRENT_IN] = direct; ++ info->format[PSC_CURRENT_OUT] = direct; ++ info->format[PSC_POWER] = direct; ++ info->format[PSC_TEMPERATURE] = direct; ++ ++ /* ++ * Direct data format coefficients from the device datasheet ("PMBus ++ * Equation Parameters"). The current and power m coefficients scale ++ * with RLOAD; vt7505_set_m() applies the 1/1000 factor below, giving ++ * m = 3.824 * RLOAD for current and 0.895 * RLOAD for power. The ++ * temperature coefficients are chip specific (see the chip data). ++ */ ++ info->m[PSC_VOLTAGE_IN] = 7578; ++ info->R[PSC_VOLTAGE_IN] = -2; ++ info->m[PSC_VOLTAGE_OUT] = 7578; ++ info->R[PSC_VOLTAGE_OUT] = -2; ++ info->m[PSC_CURRENT_IN] = 3824; ++ info->b[PSC_CURRENT_IN] = -4300; ++ info->R[PSC_CURRENT_IN] = -3; ++ info->m[PSC_CURRENT_OUT] = 3824; ++ info->b[PSC_CURRENT_OUT] = -4300; ++ info->R[PSC_CURRENT_OUT] = -3; ++ info->m[PSC_POWER] = 895; ++ info->b[PSC_POWER] = -9100; ++ info->R[PSC_POWER] = -2; ++ ++ vt7505_set_m(&info->m[PSC_CURRENT_IN], rload); ++ vt7505_set_m(&info->m[PSC_CURRENT_OUT], rload); ++ vt7505_set_m(&info->m[PSC_POWER], rload); ++ ++ info->m[PSC_TEMPERATURE] = chip->temp_m; ++ info->b[PSC_TEMPERATURE] = chip->temp_b; ++ info->R[PSC_TEMPERATURE] = -2; ++ ++ info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT | ++ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | ++ PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | ++ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | ++ PMBUS_HAVE_IIN | PMBUS_HAVE_PIN; ++ ++ /* ++ * The severe OCP deglitch filter is programmable on the MAX16550 and ++ * the VT7505, but fixed on the MAX16545. ++ */ ++ if (chip->has_ocp_filter) { ++ ret = vt7505_set_ocp_filter(client); ++ if (ret) ++ return ret; ++ } ++ ++ return pmbus_do_probe(client, info); ++} ++ ++static const struct i2c_device_id vt7505_id[] = { ++ { .name = "max16545", .driver_data = (kernel_ulong_t)&max16545_data }, ++ { .name = "max16550", .driver_data = (kernel_ulong_t)&max16550_data }, ++ { .name = "vt7505", .driver_data = (kernel_ulong_t)&vt7505_data }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, vt7505_id); ++ ++static const struct of_device_id vt7505_of_match[] = { ++ { .compatible = "adi,max16545", .data = &max16545_data }, ++ { .compatible = "adi,max16550", .data = &max16550_data }, ++ { .compatible = "adi,vt7505", .data = &vt7505_data }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, vt7505_of_match); ++ ++static struct i2c_driver vt7505_driver = { ++ .driver = { ++ .name = "vt7505", ++ .of_match_table = vt7505_of_match, ++ }, ++ .probe = vt7505_probe, ++ .id_table = vt7505_id, ++}; ++module_i2c_driver(vt7505_driver); ++ ++MODULE_AUTHOR("Georgi Vlaev "); ++MODULE_DESCRIPTION("PMBus driver for Analog Devices MAX16545/MAX16550 and Volterra VT7505"); ++MODULE_LICENSE("GPL"); ++MODULE_IMPORT_NS("PMBUS"); +diff --git a/drivers/hwmon/pmbus/xdpe1a2g7b.c b/drivers/hwmon/pmbus/xdpe1a2g7b.c +--- a/drivers/hwmon/pmbus/xdpe1a2g7b.c ++++ b/drivers/hwmon/pmbus/xdpe1a2g7b.c +@@ -57,6 +57,13 @@ static int xdpe1a2g7b_identify(struct i2c_client *client, + return 0; + } + ++#if IS_ENABLED(CONFIG_SENSORS_XDPE1A2G7B_REGULATOR) ++static const struct regulator_desc xdpe1a2g7b_reg_desc[] = { ++ PMBUS_REGULATOR("vout", 0), ++ PMBUS_REGULATOR("vout", 1), ++}; ++#endif ++ + static struct pmbus_driver_info xdpe1a2g7b_info = { + .pages = XDPE1A2G7B_PAGE_NUM, + .identify = xdpe1a2g7b_identify, +@@ -72,6 +79,10 @@ static struct pmbus_driver_info xdpe1a2g7b_info = { + .func[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | PMBUS_HAVE_STATUS_INPUT, ++#if IS_ENABLED(CONFIG_SENSORS_XDPE1A2G7B_REGULATOR) ++ .num_regulators = XDPE1A2G7B_PAGE_NUM, ++ .reg_desc = xdpe1a2g7b_reg_desc, ++#endif + }; + + static int xdpe1a2g7b_probe(struct i2c_client *client) +diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c +--- a/drivers/hwmon/pwm-fan.c ++++ b/drivers/hwmon/pwm-fan.c +@@ -634,12 +634,8 @@ static int pwm_fan_probe(struct platform_device *pdev) + if (tach->irq > 0) { + ret = devm_request_irq(dev, tach->irq, pulse_handler, + IRQF_NO_THREAD, pdev->name, tach); +- if (ret) { +- dev_err(dev, +- "Failed to request interrupt: %d\n", +- ret); ++ if (ret) + return ret; +- } + } + + if (!ctx->pulses_per_revolution[i]) { +diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c +--- a/drivers/hwmon/sht15.c ++++ b/drivers/hwmon/sht15.c +@@ -981,10 +981,8 @@ static int sht15_probe(struct platform_device *pdev) + IRQF_TRIGGER_FALLING, + "sht15 data", + data); +- if (ret) { +- dev_err(&pdev->dev, "failed to get irq for data line\n"); ++ if (ret) + goto err_release_reg; +- } + disable_irq_nosync(gpiod_to_irq(data->data)); + ret = sht15_connection_reset(data); + if (ret) +diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c +--- a/drivers/hwmon/sht3x.c ++++ b/drivers/hwmon/sht3x.c +@@ -62,7 +62,7 @@ static const unsigned char sht3x_cmd_read_serial_number[] = { 0x37, 0x80 }; + #define SHT3X_MAX_HUMIDITY 100000 + + enum sht3x_chips { +- sht3x, ++ sht3x = 1, + sts3x, + }; + +@@ -940,8 +940,19 @@ static const struct i2c_device_id sht3x_ids[] = { + + MODULE_DEVICE_TABLE(i2c, sht3x_ids); + ++static const struct of_device_id sht3x_of_match[] = { ++ { .compatible = "sensirion,sht30", .data = (void *)(uintptr_t)sht3x }, ++ { .compatible = "sensirion,sts30", .data = (void *)(uintptr_t)sts3x }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(of, sht3x_of_match); ++ + static struct i2c_driver sht3x_i2c_driver = { +- .driver.name = "sht3x", ++ .driver = { ++ .name = "sht3x", ++ .of_match_table = sht3x_of_match, ++ }, + .probe = sht3x_probe, + .id_table = sht3x_ids, + }; +diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c +--- a/drivers/hwmon/tmp102.c ++++ b/drivers/hwmon/tmp102.c +@@ -396,6 +396,8 @@ static DEFINE_SIMPLE_DEV_PM_OPS(tmp102_dev_pm_ops, tmp102_suspend, tmp102_resume + + static const struct i2c_device_id tmp102_id[] = { + { .name = "tmp102" }, ++ { .name = "tmp110" }, ++ { .name = "tmp113" }, + { } + }; + MODULE_DEVICE_TABLE(i2c, tmp102_id); +diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c +--- a/drivers/hwmon/tmp401.c ++++ b/drivers/hwmon/tmp401.c +@@ -695,7 +695,7 @@ static int tmp401_probe(struct i2c_client *client) + info->type = hwmon_chip; + info->config = data->chip_channel_config; + +- data->chip_channel_config[0] = HWMON_C_UPDATE_INTERVAL; ++ data->chip_channel_config[0] = HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL; + + info = &data->temp_info; + info->type = hwmon_temp; +diff --git a/drivers/hwmon/tvs-mpfs.c b/drivers/hwmon/tvs-mpfs.c +new file mode 100644 +--- /dev/null ++++ b/drivers/hwmon/tvs-mpfs.c +@@ -0,0 +1,390 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Author: Lars Randers ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MPFS_TVS_CTRL 0x08 ++#define MPFS_TVS_OUTPUT0 0x24 ++#define MPFS_TVS_OUTPUT1 0x28 ++ ++#define MPFS_TVS_CTRL_TEMP_VALID BIT(19) ++#define MPFS_TVS_CTRL_V2P5_VALID BIT(18) ++#define MPFS_TVS_CTRL_V1P8_VALID BIT(17) ++#define MPFS_TVS_CTRL_V1P05_VALID BIT(16) ++ ++#define MPFS_TVS_CTRL_TEMP_ENABLE BIT(3) ++#define MPFS_TVS_CTRL_V2P5_ENABLE BIT(2) ++#define MPFS_TVS_CTRL_V1P8_ENABLE BIT(1) ++#define MPFS_TVS_CTRL_V1P05_ENABLE BIT(0) ++#define MPFS_TVS_CTRL_ENABLE_ALL GENMASK(3, 0) ++ ++/* ++ * For all of these the value in millivolts is stored in 16 bits, with an upper ++ * sign bit and a lower 3 bits of decimal. These masks discard the sign bit and ++ * decimal places, because if Linux is running these voltages cannot be negative ++ * and so avoid having to convert to two's complement. ++ */ ++#define MPFS_OUTPUT0_V1P8_MASK GENMASK(30, 19) ++#define MPFS_OUTPUT0_V1P05_MASK GENMASK(14, 3) ++#define MPFS_OUTPUT1_V2P5_MASK GENMASK(14, 3) ++ ++/* ++ * The register map claims that the temperature is stored in bits 31:16, but ++ * application note "AN4682: PolarFire FPGA Temperature and Voltage Sensor" ++ * says that 31 is reserved. Temperature is in kelvin, so what's probably a ++ * sign bit has no value anyway. ++ */ ++#define MPFS_OUTPUT1_TEMP_MASK GENMASK(30, 16) ++ ++#define MPFS_TVS_INTERVAL_MASK GENMASK(15, 8) ++#define MPFS_TVS_INTERVAL_OFFSET 8 ++/* The interval register is in increments of 32 us */ ++#define MPFS_TVS_INTERVAL_SCALE 32 ++/* with 254 usable increments of 32 us available, 8 ms is the integer limit */ ++#define MPFS_TVS_INTERVAL_MAX_MS 8 ++ ++/* 273.1875 in 11.4 fixed-point notation */ ++#define MPFS_TVS_K_TO_C 0x1113 ++ ++enum mpfs_tvs_sensors { ++ SENSOR_V1P05 = 0, ++ SENSOR_V1P8, ++ SENSOR_V2P5, ++}; ++ ++static const char * const mpfs_tvs_voltage_labels[] = { "1P05", "1P8", "2P5" }; ++ ++struct mpfs_tvs { ++ struct regmap *regmap; ++}; ++ ++static int mpfs_tvs_voltage_read(struct mpfs_tvs *data, u32 attr, ++ int channel, long *val) ++{ ++ u32 tmp, control; ++ ++ if (attr != hwmon_in_input && attr != hwmon_in_enable) ++ return -EOPNOTSUPP; ++ ++ regmap_read(data->regmap, MPFS_TVS_CTRL, &control); ++ ++ switch (channel) { ++ case SENSOR_V2P5: ++ if (attr == hwmon_in_enable) { ++ *val = FIELD_GET(MPFS_TVS_CTRL_V2P5_ENABLE, control); ++ break; ++ } ++ ++ if (!(control & MPFS_TVS_CTRL_V2P5_VALID)) ++ return -ENODATA; ++ ++ regmap_read(data->regmap, MPFS_TVS_OUTPUT1, &tmp); ++ *val = FIELD_GET(MPFS_OUTPUT1_V2P5_MASK, tmp); ++ break; ++ case SENSOR_V1P8: ++ if (attr == hwmon_in_enable) { ++ *val = FIELD_GET(MPFS_TVS_CTRL_V1P8_ENABLE, control); ++ break; ++ } ++ ++ if (!(control & MPFS_TVS_CTRL_V1P8_VALID)) ++ return -ENODATA; ++ ++ regmap_read(data->regmap, MPFS_TVS_OUTPUT0, &tmp); ++ *val = FIELD_GET(MPFS_OUTPUT0_V1P8_MASK, tmp); ++ break; ++ case SENSOR_V1P05: ++ if (attr == hwmon_in_enable) { ++ *val = FIELD_GET(MPFS_TVS_CTRL_V1P05_ENABLE, control); ++ break; ++ } ++ ++ if (!(control & MPFS_TVS_CTRL_V1P05_VALID)) ++ return -ENODATA; ++ ++ regmap_read(data->regmap, MPFS_TVS_OUTPUT0, &tmp); ++ *val = FIELD_GET(MPFS_OUTPUT0_V1P05_MASK, tmp); ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} ++ ++static int mpfs_tvs_voltage_write(struct mpfs_tvs *data, u32 attr, ++ int channel, long val) ++{ ++ u32 tmp; ++ ++ if (attr != hwmon_in_enable) ++ return -EOPNOTSUPP; ++ ++ if (val > 1 || val < 0) ++ return -EINVAL; ++ ++ switch (channel) { ++ case SENSOR_V2P5: ++ tmp = FIELD_PREP(MPFS_TVS_CTRL_V2P5_ENABLE, val); ++ regmap_update_bits(data->regmap, MPFS_TVS_CTRL, ++ MPFS_TVS_CTRL_V2P5_ENABLE, tmp); ++ break; ++ case SENSOR_V1P8: ++ tmp = FIELD_PREP(MPFS_TVS_CTRL_V1P8_ENABLE, val); ++ regmap_update_bits(data->regmap, MPFS_TVS_CTRL, ++ MPFS_TVS_CTRL_V1P8_ENABLE, tmp); ++ break; ++ case SENSOR_V1P05: ++ tmp = FIELD_PREP(MPFS_TVS_CTRL_V1P05_ENABLE, val); ++ regmap_update_bits(data->regmap, MPFS_TVS_CTRL, ++ MPFS_TVS_CTRL_V1P05_ENABLE, tmp); ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} ++ ++static int mpfs_tvs_temp_read(struct mpfs_tvs *data, u32 attr, long *val) ++{ ++ u32 tmp, control; ++ ++ if (attr != hwmon_temp_input && attr != hwmon_temp_enable) ++ return -EOPNOTSUPP; ++ ++ regmap_read(data->regmap, MPFS_TVS_CTRL, &control); ++ ++ if (attr == hwmon_temp_enable) { ++ *val = FIELD_GET(MPFS_TVS_CTRL_TEMP_ENABLE, control); ++ return 0; ++ } ++ ++ if (!(control & MPFS_TVS_CTRL_TEMP_VALID)) ++ return -ENODATA; ++ ++ regmap_read(data->regmap, MPFS_TVS_OUTPUT1, &tmp); ++ *val = FIELD_GET(MPFS_OUTPUT1_TEMP_MASK, tmp); ++ *val -= MPFS_TVS_K_TO_C; ++ *val = (1000 * *val) >> 4; /* fixed point (11.4) to millidegrees */ ++ ++ return 0; ++} ++ ++static int mpfs_tvs_temp_write(struct mpfs_tvs *data, u32 attr, long val) ++{ ++ u32 tmp; ++ ++ if (attr != hwmon_temp_enable) ++ return -EOPNOTSUPP; ++ ++ if (val > 1 || val < 0) ++ return -EINVAL; ++ ++ tmp = FIELD_PREP(MPFS_TVS_CTRL_TEMP_ENABLE, val); ++ regmap_update_bits(data->regmap, MPFS_TVS_CTRL, ++ MPFS_TVS_CTRL_TEMP_ENABLE, tmp); ++ ++ return 0; ++} ++ ++static int mpfs_tvs_interval_read(struct mpfs_tvs *data, u32 attr, long *val) ++{ ++ u32 tmp; ++ ++ if (attr != hwmon_chip_update_interval) ++ return -EOPNOTSUPP; ++ ++ regmap_read(data->regmap, MPFS_TVS_CTRL, &tmp); ++ *val = FIELD_GET(MPFS_TVS_INTERVAL_MASK, tmp); ++ *val *= MPFS_TVS_INTERVAL_SCALE; ++ *val = roundup(*val, 1000); ++ *val /= 1000; ++ ++ return 0; ++} ++ ++static int mpfs_tvs_interval_write(struct mpfs_tvs *data, u32 attr, long val) ++{ ++ long temp = val; ++ ++ if (attr != hwmon_chip_update_interval) ++ return -EOPNOTSUPP; ++ ++ temp = clamp(temp, 0, MPFS_TVS_INTERVAL_MAX_MS); ++ ++ temp *= 1000; ++ temp /= MPFS_TVS_INTERVAL_SCALE; ++ ++ temp <<= MPFS_TVS_INTERVAL_OFFSET; ++ regmap_update_bits(data->regmap, MPFS_TVS_CTRL, ++ MPFS_TVS_INTERVAL_MASK, temp); ++ ++ return 0; ++} ++ ++static umode_t mpfs_tvs_is_visible(const void *data, ++ enum hwmon_sensor_types type, ++ u32 attr, int channel) ++{ ++ if (type == hwmon_chip && attr == hwmon_chip_update_interval) ++ return 0644; ++ ++ if (type == hwmon_temp) { ++ switch (attr) { ++ case hwmon_temp_enable: ++ return 0644; ++ case hwmon_temp_input: ++ case hwmon_temp_label: ++ return 0444; ++ default: ++ return 0; ++ } ++ } ++ ++ if (type == hwmon_in) { ++ switch (attr) { ++ case hwmon_in_enable: ++ return 0644; ++ case hwmon_in_input: ++ case hwmon_in_label: ++ return 0444; ++ default: ++ return 0; ++ } ++ } ++ ++ return 0; ++} ++ ++static int mpfs_tvs_read(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, long *val) ++{ ++ struct mpfs_tvs *data = dev_get_drvdata(dev); ++ ++ switch (type) { ++ case hwmon_temp: ++ return mpfs_tvs_temp_read(data, attr, val); ++ case hwmon_in: ++ return mpfs_tvs_voltage_read(data, attr, channel, val); ++ case hwmon_chip: ++ return mpfs_tvs_interval_read(data, attr, val); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static int mpfs_tvs_write(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, long val) ++{ ++ struct mpfs_tvs *data = dev_get_drvdata(dev); ++ ++ switch (type) { ++ case hwmon_temp: ++ return mpfs_tvs_temp_write(data, attr, val); ++ case hwmon_in: ++ return mpfs_tvs_voltage_write(data, attr, channel, val); ++ case hwmon_chip: ++ return mpfs_tvs_interval_write(data, attr, val); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static int mpfs_tvs_read_labels(struct device *dev, ++ enum hwmon_sensor_types type, ++ u32 attr, int channel, ++ const char **str) ++{ ++ switch (type) { ++ case hwmon_temp: ++ *str = "Die Temp"; ++ return 0; ++ case hwmon_in: ++ *str = mpfs_tvs_voltage_labels[channel]; ++ return 0; ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static const struct hwmon_ops mpfs_tvs_ops = { ++ .is_visible = mpfs_tvs_is_visible, ++ .read_string = mpfs_tvs_read_labels, ++ .read = mpfs_tvs_read, ++ .write = mpfs_tvs_write, ++}; ++ ++static const struct hwmon_channel_info *mpfs_tvs_info[] = { ++ HWMON_CHANNEL_INFO(chip, ++ HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL), ++ HWMON_CHANNEL_INFO(temp, ++ HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_ENABLE), ++ HWMON_CHANNEL_INFO(in, ++ HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_ENABLE, ++ HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_ENABLE, ++ HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_ENABLE), ++ NULL ++}; ++ ++static const struct hwmon_chip_info mpfs_tvs_chip_info = { ++ .ops = &mpfs_tvs_ops, ++ .info = mpfs_tvs_info, ++}; ++ ++static int mpfs_tvs_probe(struct platform_device *pdev) ++{ ++ struct device *hwmon_dev; ++ struct mpfs_tvs *data; ++ ++ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ data->regmap = device_node_to_regmap(pdev->dev.parent->of_node); ++ if (IS_ERR(data->regmap)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(data->regmap), ++ "Failed to find syscon regmap\n"); ++ ++ /* ++ * It's an MMIO regmap with no resources, there's nothing that can fail ++ * and return an error ++ */ ++ regmap_write(data->regmap, MPFS_TVS_CTRL, MPFS_TVS_CTRL_ENABLE_ALL); ++ ++ hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, "mpfs_tvs", ++ data, ++ &mpfs_tvs_chip_info, ++ NULL); ++ if (IS_ERR(hwmon_dev)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(hwmon_dev), ++ "hwmon device registration failed.\n"); ++ ++ return 0; ++} ++ ++static struct platform_driver mpfs_tvs_driver = { ++ .probe = mpfs_tvs_probe, ++ .driver = { ++ .name = "mpfs-tvs", ++ }, ++}; ++module_platform_driver(mpfs_tvs_driver); ++ ++MODULE_AUTHOR("Lars Randers "); ++MODULE_DESCRIPTION("PolarFire SoC temperature & voltage sensor driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/hwmon/xgene-hwmon.c b/drivers/hwmon/xgene-hwmon.c +--- a/drivers/hwmon/xgene-hwmon.c ++++ b/drivers/hwmon/xgene-hwmon.c +@@ -133,10 +133,6 @@ static int xgene_hwmon_pcc_rd(struct xgene_hwmon_dev *ctx, u32 *msg) + init_completion(&ctx->rd_complete); + ctx->resp_pending = true; + +- /* Write signature for subspace */ +- WRITE_ONCE(generic_comm_base->signature, +- cpu_to_le32(PCC_SIGNATURE | ctx->mbox_idx)); +- + /* Write to the shared command region */ + WRITE_ONCE(generic_comm_base->command, + cpu_to_le16(MSG_TYPE(msg[0]) | PCC_CMD_GENERATE_DB_INTR)); +diff --git a/drivers/hwmon/yogafan.c b/drivers/hwmon/yogafan.c +--- a/drivers/hwmon/yogafan.c ++++ b/drivers/hwmon/yogafan.c +@@ -53,6 +53,7 @@ struct yoga_fan_data { + }; + + /* Specific configurations mapped via DMI */ ++ + static const struct yogafan_config yoga_8bit_fans_cfg = { + .multiplier = 100, + .fan_count = 1, +@@ -71,6 +72,24 @@ static const struct yogafan_config legion_16bit_dual_cfg = { + .paths = { "\\_SB.PCI0.LPC0.EC0.FANS", "\\_SB.PCI0.LPC0.EC0.FA2S" } + }; + ++static const struct yogafan_config loq_15iax9_8bit_dual_cfg = { ++ .multiplier = 100, ++ .fan_count = 2, ++ .paths = { "\\_SB.PC00.LPCB.EC0.FA1S", "\\_SB.PC00.LPCB.EC0.FA2S" } ++}; ++ ++static const struct yogafan_config xiaoxin_8bit_dual_cfg = { ++ .multiplier = 100, ++ .fan_count = 2, ++ .paths = { "\\_SB.PCI0.LPC0.EC0.FANS", "\\_SB.PCI0.LPC0.EC0.FA2S" } ++}; ++ ++static const struct yogafan_config yoga_pro_7_14iah10_cfg = { ++ .multiplier = 100, ++ .fan_count = 1, ++ .paths = { "\\_SB.PC00.LPCB.EC0.FANS", NULL } ++}; ++ + static void apply_rllag_filter(struct yoga_fan_data *data, int idx, long raw_rpm) + { + ktime_t now = ktime_get_boottime(); +@@ -170,6 +189,54 @@ static const struct hwmon_chip_info yoga_fan_chip_info = { + }; + + static const struct dmi_system_id yogafan_quirks[] = { ++ { ++ .ident = "Lenovo LOQ 15IAX9", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_FAMILY, "LOQ 15IAX9"), ++ }, ++ .driver_data = (void *)&loq_15iax9_8bit_dual_cfg, ++ }, ++ { ++ .ident = "Lenovo XiaoXin Pro 13ARE 2020", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_FAMILY, "XiaoXinPro-13ARE 2020"), ++ }, ++ .driver_data = (void *)&xiaoxin_8bit_dual_cfg, ++ }, ++ { ++ .ident = "Lenovo IdeaPad 3 15ALC6", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_FAMILY, "IdeaPad 3 15ALC6"), ++ }, ++ .driver_data = (void *)&ideapad_8bit_fan0_cfg, ++ }, ++ { ++ .ident = "Lenovo Legion Pro 7 16AFR10H", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_FAMILY, "Legion Pro 7 16AFR10H"), ++ }, ++ .driver_data = (void *)&xiaoxin_8bit_dual_cfg, ++ }, ++ { ++ .ident = "Lenovo Yoga Pro 7 14IAH10", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_FAMILY, "Yoga Pro 7 14IAH10"), ++ }, ++ .driver_data = (void *)&yoga_pro_7_14iah10_cfg, ++ }, ++ { ++ .ident = "Lenovo Yoga 7 16ARP8", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_FAMILY, "Yoga 7 16ARP8"), ++ }, ++ .driver_data = (void *)&xiaoxin_8bit_dual_cfg, ++ }, + { + .ident = "Lenovo Yoga", + .matches = { +diff --git a/include/linux/hwmon-sysfs.h b/include/linux/hwmon-sysfs.h +--- a/include/linux/hwmon-sysfs.h ++++ b/include/linux/hwmon-sysfs.h +@@ -15,10 +15,10 @@ struct sensor_device_attribute{ + int index; + }; + #define to_sensor_dev_attr(_dev_attr) \ +- container_of(_dev_attr, struct sensor_device_attribute, dev_attr) ++ container_of_const(_dev_attr, struct sensor_device_attribute, dev_attr) + +-#define SENSOR_ATTR(_name, _mode, _show, _store, _index) \ +- { .dev_attr = __ATTR(_name, _mode, _show, _store), \ ++#define SENSOR_ATTR(_name, _mode, _show, _store, _index) \ ++ { .dev_attr = __DEVICE_ATTR(_name, _mode, _show, _store), \ + .index = _index } + + #define SENSOR_ATTR_RO(_name, _func, _index) \ +@@ -49,11 +49,11 @@ struct sensor_device_attribute_2 { + u8 nr; + }; + #define to_sensor_dev_attr_2(_dev_attr) \ +- container_of(_dev_attr, struct sensor_device_attribute_2, dev_attr) ++ container_of_const(_dev_attr, struct sensor_device_attribute_2, dev_attr) + +-#define SENSOR_ATTR_2(_name, _mode, _show, _store, _nr, _index) \ +- { .dev_attr = __ATTR(_name, _mode, _show, _store), \ +- .index = _index, \ ++#define SENSOR_ATTR_2(_name, _mode, _show, _store, _nr, _index) \ ++ { .dev_attr = __DEVICE_ATTR(_name, _mode, _show, _store), \ ++ .index = _index, \ + .nr = _nr } + + #define SENSOR_ATTR_2_RO(_name, _func, _nr, _index) \ diff --git a/pkgbuilds/linux-omarchy-bore/0740-hwmon-updates.patch.sig b/pkgbuilds/linux-omarchy-bore/0740-hwmon-updates.patch.sig new file mode 100644 index 0000000..00d8812 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0740-hwmon-updates.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0741-hwmon-fixes-1.patch b/pkgbuilds/linux-omarchy-bore/0741-hwmon-fixes-1.patch new file mode 100644 index 0000000..15533e0 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0741-hwmon-fixes-1.patch @@ -0,0 +1,179 @@ +diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c +--- a/drivers/hwmon/applesmc.c ++++ b/drivers/hwmon/applesmc.c +@@ -1481,9 +1481,9 @@ static int __init applesmc_init(void) + applesmc_fan_config[smcreg.fan_count] = 0; + applesmc_pwm_config[smcreg.fan_count] = 0; + +- applesmc_info_temp = kzalloc_obj(*applesmc_info_temp, GFP_KERNEL); +- applesmc_info_fan = kzalloc_obj(*applesmc_info_fan, GFP_KERNEL); +- applesmc_info_pwm = kzalloc_obj(*applesmc_info_pwm, GFP_KERNEL); ++ applesmc_info_temp = kzalloc_obj(*applesmc_info_temp); ++ applesmc_info_fan = kzalloc_obj(*applesmc_info_fan); ++ applesmc_info_pwm = kzalloc_obj(*applesmc_info_pwm); + if (!applesmc_info_temp || !applesmc_info_fan || !applesmc_info_pwm) { + ret = -ENOMEM; + goto out_info; +@@ -1498,7 +1498,7 @@ static int __init applesmc_init(void) + applesmc_info_pwm->type = hwmon_pwm; + applesmc_info_pwm->config = applesmc_pwm_config; + +- applesmc_info_arr = kcalloc(4, sizeof(*applesmc_info_arr), GFP_KERNEL); ++ applesmc_info_arr = kzalloc_objs(*applesmc_info_arr, 4); + if (!applesmc_info_arr) { + ret = -ENOMEM; + goto out_info; +@@ -1509,7 +1509,7 @@ static int __init applesmc_init(void) + applesmc_info_arr[2] = applesmc_info_pwm; + applesmc_info_arr[3] = NULL; + +- applesmc_chip = kzalloc_obj(*applesmc_chip, GFP_KERNEL); ++ applesmc_chip = kzalloc_obj(*applesmc_chip); + if (!applesmc_chip) { + ret = -ENOMEM; + goto out_info; +@@ -1519,10 +1519,9 @@ static int __init applesmc_init(void) + applesmc_chip->info = applesmc_info_arr; + + /* Create non-standard fanX_safe attributes group */ +- fan_safe_attrs = kcalloc(smcreg.fan_count, +- sizeof(*fan_safe_attrs), GFP_KERNEL); +- fan_safe_attr_list = kcalloc(smcreg.fan_count + 1, +- sizeof(*fan_safe_attr_list), GFP_KERNEL); ++ fan_safe_attrs = kzalloc_objs(*fan_safe_attrs, smcreg.fan_count); ++ fan_safe_attr_list = kzalloc_objs(*fan_safe_attr_list, ++ smcreg.fan_count + 1); + if (!fan_safe_attrs || !fan_safe_attr_list) { + ret = -ENOMEM; + goto out_info; +diff --git a/drivers/hwmon/asus_rog_ryujin.c b/drivers/hwmon/asus_rog_ryujin.c +--- a/drivers/hwmon/asus_rog_ryujin.c ++++ b/drivers/hwmon/asus_rog_ryujin.c +@@ -184,6 +184,7 @@ static int rog_ryujin_write_expanded(struct rog_ryujin_data *priv, const u8 *cmd + static int rog_ryujin_execute_cmd(struct rog_ryujin_data *priv, const u8 *cmd, int cmd_length, + struct completion *status_completion) + { ++ unsigned long flags; + int ret; + + /* +@@ -191,9 +192,9 @@ static int rog_ryujin_execute_cmd(struct rog_ryujin_data *priv, const u8 *cmd, i + * completion. Reinit is done because hidraw could have triggered + * the raw event parsing and marked the passed in completion as done. + */ +- spin_lock_bh(&priv->status_report_request_lock); ++ spin_lock_irqsave(&priv->status_report_request_lock, flags); + reinit_completion(status_completion); +- spin_unlock_bh(&priv->status_report_request_lock); ++ spin_unlock_irqrestore(&priv->status_report_request_lock, flags); + + /* Send command for getting data */ + ret = rog_ryujin_write_expanded(priv, cmd, cmd_length); +@@ -421,15 +422,18 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + int size) + { + struct rog_ryujin_data *priv = hid_get_drvdata(hdev); ++ unsigned long flags; + + if (size < 2 || data[0] != RYUJIN_CMD_PREFIX) + return 0; + ++ spin_lock_irqsave(&priv->status_report_request_lock, flags); ++ + if (data[1] == RYUJIN_GET_COOLER_STATUS_CMD_RESPONSE) { + if (size <= priv->info->temp_offset + 1 || + size <= priv->info->pump_speed_offset + 1 || + size <= priv->info->fan_speed_offset + 1) +- return 0; ++ goto unlock; + + /* Received coolant temp and speeds of pump and internal fan */ + priv->temp_input[0] = data[priv->info->temp_offset] * 1000 + +@@ -443,7 +447,7 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + complete_all(&priv->cooler_status_received); + } else if (data[1] == RYUJIN_GET_CONTROLLER_SPEED_CMD_RESPONSE) { + if (size <= RYUJIN_CONTROLLER_SPEED_3 + 1) +- return 0; ++ goto unlock; + + /* Received speeds of four fans attached to the controller */ + priv->speed_input[2] = get_unaligned_le16(data + RYUJIN_CONTROLLER_SPEED_1); +@@ -455,7 +459,7 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + complete_all(&priv->controller_status_received); + } else if (data[1] == RYUJIN_GET_COOLER_DUTY_CMD_RESPONSE) { + if (size <= RYUJIN_INTERNAL_FAN_DUTY) +- return 0; ++ goto unlock; + + /* Received report for pump and internal fan duties (in %) */ + if (data[RYUJIN_PUMP_DUTY] == 0 && data[RYUJIN_INTERNAL_FAN_DUTY] == 0) { +@@ -474,7 +478,7 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + * We're expecting a report, so parse it. + */ + goto read_cooler_duty; +- return 0; ++ goto unlock; + } + read_cooler_duty: + priv->duty_input[0] = rog_ryujin_percent_to_pwm(data[RYUJIN_PUMP_DUTY]); +@@ -484,7 +488,7 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + complete_all(&priv->cooler_duty_received); + } else if (data[1] == RYUJIN_GET_CONTROLLER_DUTY_CMD_RESPONSE) { + if (size <= RYUJIN_CONTROLLER_DUTY) +- return 0; ++ goto unlock; + + /* Received report for controller duty for fans (in PWM) */ + if (data[RYUJIN_CONTROLLER_DUTY] == 0) { +@@ -503,7 +507,7 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + * We're expecting a report, so parse it. + */ + goto read_controller_duty; +- return 0; ++ goto unlock; + } + read_controller_duty: + priv->duty_input[2] = data[RYUJIN_CONTROLLER_DUTY]; +@@ -512,6 +516,8 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + complete_all(&priv->controller_duty_received); + } + ++unlock: ++ spin_unlock_irqrestore(&priv->status_report_request_lock, flags); + return 0; + } + +diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c +--- a/drivers/hwmon/hwmon.c ++++ b/drivers/hwmon/hwmon.c +@@ -318,6 +318,11 @@ static int hwmon_attr_base(enum hwmon_sensor_types type) + return 1; + } + ++static bool is_hwmon_device(struct device *dev) ++{ ++ return dev->class == &hwmon_class; ++} ++ + #if IS_REACHABLE(CONFIG_I2C) + + /* +@@ -338,7 +343,7 @@ static int hwmon_attr_base(enum hwmon_sensor_types type) + + static int hwmon_match_device(struct device *dev, const void *data) + { +- return dev->class == &hwmon_class; ++ return is_hwmon_device(dev); + } + + static ssize_t pec_show(struct device *dev, const struct device_attribute *dummy, +@@ -781,6 +786,9 @@ int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type, + const char *template; + int base; + ++ if (WARN(!is_hwmon_device(dev), "%s is not a hardware monitoring device\n", ++ dev_name(dev))) ++ return -EINVAL; + if (type >= ARRAY_SIZE(__templates)) + return -EINVAL; + if (attr >= __templates_size[type]) diff --git a/pkgbuilds/linux-omarchy-bore/0741-hwmon-fixes-1.patch.sig b/pkgbuilds/linux-omarchy-bore/0741-hwmon-fixes-1.patch.sig new file mode 100644 index 0000000..76f3485 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0741-hwmon-fixes-1.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0742-hwmon-fixes-2.patch b/pkgbuilds/linux-omarchy-bore/0742-hwmon-fixes-2.patch new file mode 100644 index 0000000..9552822 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0742-hwmon-fixes-2.patch @@ -0,0 +1,12 @@ +diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c +--- a/drivers/hwmon/k10temp.c ++++ b/drivers/hwmon/k10temp.c +@@ -523,7 +523,7 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) + } + } else if (boot_cpu_data.x86 == 0x1a) { + switch (boot_cpu_data.x86_model) { +- case 0x00 ... 0x2f: /* Zen5 Turin */ ++ case 0x00 ... 0x1f: /* Zen5 Turin */ + data->ccd_offset = 0x1F0; + k10temp_get_ccd_support(data, 16); + break; diff --git a/pkgbuilds/linux-omarchy-bore/0742-hwmon-fixes-2.patch.sig b/pkgbuilds/linux-omarchy-bore/0742-hwmon-fixes-2.patch.sig new file mode 100644 index 0000000..dcf7ede Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0742-hwmon-fixes-2.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0750-applesmc-cache-race.patch b/pkgbuilds/linux-omarchy-bore/0750-applesmc-cache-race.patch deleted file mode 100644 index bd8dd7c..0000000 --- a/pkgbuilds/linux-omarchy-bore/0750-applesmc-cache-race.patch +++ /dev/null @@ -1,31 +0,0 @@ -diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c ---- a/drivers/hwmon/applesmc.c -+++ b/drivers/hwmon/applesmc.c -@@ -33,6 +33,7 @@ - #include - #include - #include -+#include - - /* data port used by Apple SMC */ - #define APPLESMC_DATA_PORT 0x300 -@@ -372,7 +373,8 @@ static const struct applesmc_entry *applesmc_get_entry_by_index(int index) - __be32 be; - int ret = 0; - -- if (cache->valid) -+ /* Pairs with smp_store_release() to ensure cache contents are visible */ -+ if (smp_load_acquire(&cache->valid)) - return cache; - - mutex_lock(&smcreg.mutex); -@@ -391,7 +393,8 @@ static const struct applesmc_entry *applesmc_get_entry_by_index(int index) - cache->len = info[0]; - memcpy(cache->type, &info[1], 4); - cache->flags = info[5]; -- cache->valid = true; -+ /* Pairs with smp_load_acquire() to commit cache contents before setting valid */ -+ smp_store_release(&cache->valid, true); - - out: - mutex_unlock(&smcreg.mutex); diff --git a/pkgbuilds/linux-omarchy-bore/0750-applesmc-cache-race.patch.sig b/pkgbuilds/linux-omarchy-bore/0750-applesmc-cache-race.patch.sig deleted file mode 100644 index 6a23cb7..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/0750-applesmc-cache-race.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/0751-applesmc-key-backlight-workqueue-leak.patch b/pkgbuilds/linux-omarchy-bore/0751-applesmc-key-backlight-workqueue-leak.patch deleted file mode 100644 index 6eda26b..0000000 --- a/pkgbuilds/linux-omarchy-bore/0751-applesmc-key-backlight-workqueue-leak.patch +++ /dev/null @@ -1,22 +0,0 @@ -diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c ---- a/drivers/hwmon/applesmc.c -+++ b/drivers/hwmon/applesmc.c -@@ -1252,12 +1252,17 @@ static void applesmc_release_light_sensor(void) - - static int applesmc_create_key_backlight(void) - { -+ int ret; -+ - if (!smcreg.has_key_backlight) - return 0; - applesmc_led_wq = create_singlethread_workqueue("applesmc-led"); - if (!applesmc_led_wq) - return -ENOMEM; -- return led_classdev_register(&pdev->dev, &applesmc_backlight); -+ ret = led_classdev_register(&pdev->dev, &applesmc_backlight); -+ if (ret) -+ destroy_workqueue(applesmc_led_wq); -+ return ret; - } - - static void applesmc_release_key_backlight(void) diff --git a/pkgbuilds/linux-omarchy-bore/0751-applesmc-key-backlight-workqueue-leak.patch.sig b/pkgbuilds/linux-omarchy-bore/0751-applesmc-key-backlight-workqueue-leak.patch.sig deleted file mode 100644 index d4cd8fb..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/0751-applesmc-key-backlight-workqueue-leak.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/0800-platform-updates.patch b/pkgbuilds/linux-omarchy-bore/0800-platform-updates.patch index bb6a9de..c6018e3 100644 --- a/pkgbuilds/linux-omarchy-bore/0800-platform-updates.patch +++ b/pkgbuilds/linux-omarchy-bore/0800-platform-updates.patch @@ -364,78 +364,6 @@ diff --git a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.c b/drivers/hid/am pr_warn_once("Undefined laptop placement\n"); } -diff --git a/drivers/platform/arm64/qcom-hamoa-ec.c b/drivers/platform/arm64/qcom-hamoa-ec.c ---- a/drivers/platform/arm64/qcom-hamoa-ec.c -+++ b/drivers/platform/arm64/qcom-hamoa-ec.c -@@ -92,8 +92,10 @@ static int qcom_ec_read(struct qcom_ec *ec, u8 cmd, u8 resp_len, u8 *resp) - return ret; - else if (ret == 0 || ret == 0xff) - return -EOPNOTSUPP; -+ else if (ret != resp_len) -+ return -EIO; - -- if (resp[0] >= resp_len) -+ if (resp[0] != resp_len - 1) - return -EINVAL; - - return 0; -diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c ---- a/drivers/platform/mellanox/mlxbf-bootctl.c -+++ b/drivers/platform/mellanox/mlxbf-bootctl.c -@@ -10,6 +10,7 @@ - - #include - #include -+#include - #include - #include - #include -diff --git a/drivers/platform/mellanox/mlxbf-pmc.c b/drivers/platform/mellanox/mlxbf-pmc.c ---- a/drivers/platform/mellanox/mlxbf-pmc.c -+++ b/drivers/platform/mellanox/mlxbf-pmc.c -@@ -2262,13 +2262,19 @@ static int mlxbf_pmc_map_counters(struct device *dev) - - static int mlxbf_pmc_probe(struct platform_device *pdev) - { -- struct acpi_device *acpi_dev = ACPI_COMPANION(&pdev->dev); -- const char *hid = acpi_device_hid(acpi_dev); - struct device *dev = &pdev->dev; -+ struct acpi_device *acpi_dev; - struct arm_smccc_res res; -+ const char *hid; - guid_t guid; - int ret; - -+ acpi_dev = ACPI_COMPANION(&pdev->dev); -+ if (!acpi_dev) -+ return -ENODEV; -+ -+ hid = acpi_device_hid(acpi_dev); -+ - /* Ensure we have the UUID we expect for this service. */ - arm_smccc_smc(MLXBF_PMC_SIP_SVC_UID, 0, 0, 0, 0, 0, 0, 0, &res); - guid_parse(mlxbf_pmc_svc_uuid_str, &guid); -diff --git a/drivers/platform/surface/surface_acpi_notify.c b/drivers/platform/surface/surface_acpi_notify.c ---- a/drivers/platform/surface/surface_acpi_notify.c -+++ b/drivers/platform/surface/surface_acpi_notify.c -@@ -777,12 +777,16 @@ static int san_consumer_links_setup(struct platform_device *pdev) - - static int san_probe(struct platform_device *pdev) - { -- struct acpi_device *san = ACPI_COMPANION(&pdev->dev); - struct ssam_controller *ctrl; -+ struct acpi_device *san; - struct san_data *data; - acpi_status astatus; - int status; - -+ san = ACPI_COMPANION(&pdev->dev); -+ if (!san) -+ return -ENODEV; -+ - ctrl = ssam_client_bind(&pdev->dev); - if (IS_ERR(ctrl)) - return PTR_ERR(ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ctrl); diff --git a/drivers/platform/surface/surface_aggregator_hub.c b/drivers/platform/surface/surface_aggregator_hub.c --- a/drivers/platform/surface/surface_aggregator_hub.c +++ b/drivers/platform/surface/surface_aggregator_hub.c @@ -520,20 +448,6 @@ diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig depends on INPUT select INPUT_SPARSEKMAP select NEW_LEDS -diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c ---- a/drivers/platform/x86/acer-wmi.c -+++ b/drivers/platform/x86/acer-wmi.c -@@ -1581,7 +1581,9 @@ static int WMI_gaming_execute_u32_u64(u32 method_id, u32 in, u64 *out) - return -EIO; - - obj = result.pointer; -- if (obj && out) { -+ if (!obj && out) { -+ ret = -ENOMSG; -+ } else if (obj && out) { - switch (obj->type) { - case ACPI_TYPE_INTEGER: - *out = obj->integer.value; diff --git a/drivers/platform/x86/amd/Kconfig b/drivers/platform/x86/amd/Kconfig --- a/drivers/platform/x86/amd/Kconfig +++ b/drivers/platform/x86/amd/Kconfig @@ -1795,19 +1709,6 @@ diff --git a/drivers/platform/x86/amd/hsmp/hsmp.h b/drivers/platform/x86/amd/hsm + */ +extern struct rw_semaphore hsmp_sock_rwsem; #endif /* HSMP_H */ -diff --git a/drivers/platform/x86/amd/hsmp/hwmon.c b/drivers/platform/x86/amd/hsmp/hwmon.c ---- a/drivers/platform/x86/amd/hsmp/hwmon.c -+++ b/drivers/platform/x86/amd/hsmp/hwmon.c -@@ -31,6 +31,9 @@ static int hsmp_hwmon_write(struct device *dev, enum hwmon_sensor_types type, - if (attr != hwmon_power_cap) - return -EOPNOTSUPP; - -+ if (val < 0) -+ return -EINVAL; -+ - msg.num_args = 1; - msg.args[0] = val / MICROWATT_PER_MILLIWATT; - msg.msg_id = HSMP_SET_SOCKET_POWER_LIMIT; diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/x86/amd/hsmp/plat.c --- a/drivers/platform/x86/amd/hsmp/plat.c +++ b/drivers/platform/x86/amd/hsmp/plat.c @@ -3763,41 +3664,6 @@ diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-w { KE_KEY, 0x8C, { KEY_SWITCHVIDEOMODE } }, /* SDSP DVI only */ { KE_KEY, 0x8D, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + DVI */ { KE_KEY, 0x8E, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + DVI */ -diff --git a/drivers/platform/x86/asus-wireless.c b/drivers/platform/x86/asus-wireless.c ---- a/drivers/platform/x86/asus-wireless.c -+++ b/drivers/platform/x86/asus-wireless.c -@@ -132,6 +132,10 @@ static int asus_wireless_probe(struct platform_device *pdev) - const struct acpi_device_id *id; - int err; - -+ id = acpi_match_acpi_device(device_ids, adev); -+ if (!id) -+ return -ENODEV; -+ - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; -@@ -139,6 +143,7 @@ static int asus_wireless_probe(struct platform_device *pdev) - platform_set_drvdata(pdev, data); - - data->adev = adev; -+ data->hswc_params = (const struct hswc_params *)id->driver_data; - - data->idev = devm_input_allocate_device(&pdev->dev); - if (!data->idev) -@@ -153,12 +158,6 @@ static int asus_wireless_probe(struct platform_device *pdev) - if (err) - return err; - -- id = acpi_match_acpi_device(device_ids, adev); -- if (!id) -- return 0; -- -- data->hswc_params = (const struct hswc_params *)id->driver_data; -- - data->wq = create_singlethread_workqueue("asus_wireless_workqueue"); - if (!data->wq) - return -ENOMEM; diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -3832,30 +3698,6 @@ diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c /* Throttle thermal policy ****************************************************/ static int throttle_thermal_policy_write(struct asus_wmi *asus) { -@@ -5244,20 +5268,20 @@ static int asus_wmi_add(struct platform_device *pdev) - return 0; - - fail_wmi_handler: -+ asus_screenpad_exit(asus); -+fail_screenpad: - asus_wmi_backlight_exit(asus); - fail_backlight: - asus_wmi_rfkill_exit(asus); --fail_screenpad: -- asus_screenpad_exit(asus); - fail_rfkill: - asus_wmi_led_exit(asus); - fail_leds: -+fail_custom_fan_curve: - fail_hwmon: - asus_wmi_input_exit(asus); - fail_input: - asus_wmi_sysfs_exit(asus->platform_device); - fail_sysfs: --fail_custom_fan_curve: - fail_platform_profile_setup: - fail_fan_boost_mode: - fail_platform: diff --git a/drivers/platform/x86/dell/dell-smbios-base.c b/drivers/platform/x86/dell/dell-smbios-base.c --- a/drivers/platform/x86/dell/dell-smbios-base.c +++ b/drivers/platform/x86/dell/dell-smbios-base.c @@ -4164,43 +4006,6 @@ diff --git a/drivers/platform/x86/dell/dell-smbios.h b/drivers/platform/x86/dell void dell_smbios_unregister_device(struct device *d); int dell_smbios_error(int value); -diff --git a/drivers/platform/x86/dell/dell-wmi-base.c b/drivers/platform/x86/dell/dell-wmi-base.c ---- a/drivers/platform/x86/dell/dell-wmi-base.c -+++ b/drivers/platform/x86/dell/dell-wmi-base.c -@@ -456,7 +456,7 @@ static int dell_wmi_process_key(struct wmi_device *wdev, int type, int code, __l - key++; - used = 1; - } else if (type == 0x0012 && code == 0x000d && remaining > 0) { -- value = (le16_to_cpu(buffer[2]) == 2); -+ value = (le16_to_cpu(buffer[0]) == 2); - used = 1; - } - -@@ -843,9 +843,22 @@ static int __init dell_wmi_init(void) - - err = dell_privacy_register_driver(); - if (err) -- return err; -+ goto out_smbios; - -- return wmi_driver_register(&dell_wmi_driver); -+ err = wmi_driver_register(&dell_wmi_driver); -+ if (err) -+ goto out_privacy; -+ -+ return 0; -+ -+out_privacy: -+ dell_privacy_unregister_driver(); -+ -+out_smbios: -+ if (wmi_requires_smbios_request) -+ dell_wmi_events_set_enabled(false); -+ -+ return err; - } - late_initcall(dell_wmi_init); - diff --git a/drivers/platform/x86/dell/dell-wmi-ddv.c b/drivers/platform/x86/dell/dell-wmi-ddv.c --- a/drivers/platform/x86/dell/dell-wmi-ddv.c +++ b/drivers/platform/x86/dell/dell-wmi-ddv.c @@ -4251,35 +4056,6 @@ diff --git a/drivers/platform/x86/dell/dell-wmi-ddv.c b/drivers/platform/x86/del } static ssize_t dell_wmi_ddv_query_string(struct wmi_device *wdev, enum dell_ddv_method method, -diff --git a/drivers/platform/x86/dell/dell-wmi-privacy.c b/drivers/platform/x86/dell/dell-wmi-privacy.c ---- a/drivers/platform/x86/dell/dell-wmi-privacy.c -+++ b/drivers/platform/x86/dell/dell-wmi-privacy.c -@@ -92,11 +92,11 @@ bool dell_privacy_has_mic_mute(void) - { - struct privacy_wmi_data *priv; - -- mutex_lock(&list_mutex); -+ guard(mutex)(&list_mutex); -+ - priv = list_first_entry_or_null(&wmi_list, - struct privacy_wmi_data, - list); -- mutex_unlock(&list_mutex); - - return priv && (priv->features_present & BIT(DELL_PRIVACY_TYPE_AUDIO)); - } -diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h b/drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h ---- a/drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h -+++ b/drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h -@@ -107,7 +107,7 @@ enum { - static int get_##type##_instance_id(struct kobject *kobj) \ - { \ - int i; \ -- for (i = 0; i <= wmi_priv.type##_instances_count; i++) { \ -+ for (i = 0; i < wmi_priv.type##_instances_count; i++) { \ - if (!(strcmp(kobj->name, wmi_priv.type##_data[i].attribute_name)))\ - return i; \ - } \ diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -4415,21 +4191,6 @@ diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujits if (error) return error; -diff --git a/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c ---- a/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c -+++ b/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c -@@ -353,6 +353,11 @@ static int hp_populate_password_elements_from_package(union acpi_object *passwor - case PSWD_ENCODINGS: - size = min_t(u32, password_data->encodings_size, MAX_ENCODINGS_SIZE); - for (pos_values = 0; pos_values < size; pos_values++) { -+ if (elem + pos_values >= password_obj_count) { -+ pr_err("Error elem-objects package is too small\n"); -+ return -EINVAL; -+ } -+ - ret = hp_convert_hexstr_to_str(password_obj[elem + pos_values].string.pointer, - password_obj[elem + pos_values].string.length, - &str_value, &value_len); diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -4463,22 +4224,17 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c #define zero_if_sup(tmp) (zero_insize_support?0:sizeof(tmp)) // use when zero insize is required -@@ -133,11 +142,52 @@ static const struct thermal_profile_params omen_v1_no_ec_thermal_params = { +@@ -133,24 +142,41 @@ static const struct thermal_profile_params omen_v1_no_ec_thermal_params = { .ec_tp_offset = HP_NO_THERMAL_PROFILE_OFFSET, }; --/* -- * A generic pointer for the currently-active board's thermal profile -- * parameters. -- */ --static struct thermal_profile_params *active_thermal_profile_params; +struct hp_wmi_fan_profile_params { + int (*get_fan_speed)(int fan); + bool fan_table; +}; + -+struct hp_wmi_board_params { -+ const struct thermal_profile_params *thermal_profile; + struct hp_wmi_board_params { + const struct thermal_profile_params *thermal_profile; + const struct hp_wmi_fan_profile_params *fan_profile; +}; + @@ -4487,41 +4243,30 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c +static const struct hp_wmi_fan_profile_params victus_s_fan_profile_params = { + .get_fan_speed = hp_wmi_get_fan_speed_victus_s, + .fan_table = true, -+}; -+ -+static const struct hp_wmi_board_params victus_s_board_params = { -+ .thermal_profile = &victus_s_thermal_params, -+ .fan_profile = &victus_s_fan_profile_params, -+}; -+ -+static const struct hp_wmi_board_params omen_v1_board_params = { -+ .thermal_profile = &omen_v1_thermal_params, -+ .fan_profile = &victus_s_fan_profile_params, -+}; -+ -+static const struct hp_wmi_board_params omen_v1_legacy_board_params = { -+ .thermal_profile = &omen_v1_legacy_thermal_params, -+ .fan_profile = &victus_s_fan_profile_params, -+}; -+ -+static const struct hp_wmi_board_params omen_v1_no_ec_board_params = { -+ .thermal_profile = &omen_v1_no_ec_thermal_params, -+ .fan_profile = &victus_s_fan_profile_params, -+}; -+ -+static const struct hp_wmi_board_params *active_board_params; -+ -+static const struct thermal_profile_params *hp_wmi_thermal_profile(void) -+{ -+ if (!active_board_params) -+ return NULL; -+ -+ return active_board_params->thermal_profile; -+} + }; - /* DMI board names of devices that should use the omen specific path for - * thermal profiles. -@@ -157,7 +207,7 @@ static const char * const omen_thermal_profile_boards[] = { + static const struct hp_wmi_board_params victus_s_board_params = { + .thermal_profile = &victus_s_thermal_params, ++ .fan_profile = &victus_s_fan_profile_params, + }; + + static const struct hp_wmi_board_params omen_v1_board_params = { + .thermal_profile = &omen_v1_thermal_params, ++ .fan_profile = &victus_s_fan_profile_params, + }; + + static const struct hp_wmi_board_params omen_v1_legacy_board_params = { + .thermal_profile = &omen_v1_legacy_thermal_params, ++ .fan_profile = &victus_s_fan_profile_params, + }; + + static const struct hp_wmi_board_params omen_v1_no_ec_board_params = { + .thermal_profile = &omen_v1_no_ec_thermal_params, ++ .fan_profile = &victus_s_fan_profile_params, + }; + + static const struct hp_wmi_board_params *active_board_params; +@@ -181,7 +207,7 @@ static const char * const omen_thermal_profile_boards[] = { "886B", "886C", "88C8", "88CB", "88D1", "88D2", "88F4", "88F5", "88F6", "88F7", "88FD", "88FE", "88FF", "8900", "8901", "8902", "8912", "8917", "8918", "8949", "894A", "89EB", @@ -4530,33 +4275,21 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c "8BAD", "8C58", "8E41", -@@ -187,83 +237,103 @@ static const char * const victus_thermal_profile_boards[] = { - "8A25", - }; - --/* DMI Board names of Victus 16-r and Victus 16-s laptops */ --static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst = { -+/* DMI board-specific feature data for Omen and Victus laptops. */ -+static const struct dmi_system_id hp_wmi_feature_boards[] __initconst = { - { +@@ -217,6 +243,10 @@ static const struct dmi_system_id hp_wmi_feature_boards[] __initconst = { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8902") }, -- .driver_data = (void *)&omen_v1_legacy_thermal_params, -+ .driver_data = (void *)&omen_v1_legacy_board_params, -+ }, + .driver_data = (void *)&omen_v1_legacy_board_params, + }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8A3D") }, + .driver_data = (void *)&victus_s_board_params, - }, ++ }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8A44") }, -- .driver_data = (void *)&omen_v1_legacy_thermal_params, -+ .driver_data = (void *)&omen_v1_legacy_board_params, - }, - { + .driver_data = (void *)&omen_v1_legacy_board_params, +@@ -225,6 +255,14 @@ static const struct dmi_system_id hp_wmi_feature_boards[] __initconst = { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8A4D") }, -- .driver_data = (void *)&omen_v1_legacy_thermal_params, -+ .driver_data = (void *)&omen_v1_legacy_board_params, -+ }, + .driver_data = (void *)&omen_v1_legacy_board_params, + }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BAA") }, + .driver_data = (void *)&omen_v1_board_params, @@ -4564,107 +4297,33 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BA9") }, + .driver_data = (void *)&omen_v1_board_params, - }, ++ }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BAB") }, -- .driver_data = (void *)&omen_v1_thermal_params, -+ .driver_data = (void *)&omen_v1_board_params, - }, - { + .driver_data = (void *)&omen_v1_board_params, +@@ -233,6 +271,10 @@ static const struct dmi_system_id hp_wmi_feature_boards[] __initconst = { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8B2F") }, -- .driver_data = (void *)&victus_s_thermal_params, -+ .driver_data = (void *)&victus_s_board_params, -+ }, + .driver_data = (void *)&victus_s_board_params, + }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BB3") }, + .driver_data = (void *)&omen_v1_no_ec_board_params, - }, ++ }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BBE") }, -- .driver_data = (void *)&victus_s_thermal_params, -+ .driver_data = (void *)&victus_s_board_params, - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BC2") }, -- .driver_data = (void *)&omen_v1_thermal_params, -+ .driver_data = (void *)&omen_v1_board_params, - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BCA") }, -- .driver_data = (void *)&omen_v1_thermal_params, -+ .driver_data = (void *)&omen_v1_board_params, - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BCD") }, -- .driver_data = (void *)&omen_v1_thermal_params, -+ .driver_data = (void *)&omen_v1_board_params, - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BD4") }, -- .driver_data = (void *)&victus_s_thermal_params, -+ .driver_data = (void *)&victus_s_board_params, - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BD5") }, -- .driver_data = (void *)&victus_s_thermal_params, -+ .driver_data = (void *)&victus_s_board_params, - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8C76") }, -- .driver_data = (void *)&omen_v1_thermal_params, -+ .driver_data = (void *)&omen_v1_board_params, - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8C77") }, -- .driver_data = (void *)&omen_v1_thermal_params, -+ .driver_data = (void *)&omen_v1_board_params, - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8C78") }, -- .driver_data = (void *)&omen_v1_thermal_params, -+ .driver_data = (void *)&omen_v1_board_params, - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8C99") }, -- .driver_data = (void *)&victus_s_thermal_params, -+ .driver_data = (void *)&victus_s_board_params, - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8C9C") }, -- .driver_data = (void *)&victus_s_thermal_params, -+ .driver_data = (void *)&victus_s_board_params, - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8D26") }, -- .driver_data = (void *)&omen_v1_legacy_thermal_params, -+ .driver_data = (void *)&omen_v1_legacy_board_params, - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8D41") }, -- .driver_data = (void *)&omen_v1_no_ec_thermal_params, -+ .driver_data = (void *)&omen_v1_no_ec_board_params, - }, - { + .driver_data = (void *)&victus_s_board_params, +@@ -289,6 +331,10 @@ static const struct dmi_system_id hp_wmi_feature_boards[] __initconst = { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8D87") }, -- .driver_data = (void *)&omen_v1_no_ec_thermal_params, -+ .driver_data = (void *)&omen_v1_no_ec_board_params, -+ }, + .driver_data = (void *)&omen_v1_no_ec_board_params, + }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8D88") }, + .driver_data = (void *)&omen_v1_no_ec_board_params, - }, ++ }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8DD6") }, -@@ -271,7 +341,7 @@ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8E35") }, -- .driver_data = (void *)&omen_v1_legacy_thermal_params, -+ .driver_data = (void *)&omen_v1_legacy_board_params, - }, - {}, - }; -@@ -336,6 +406,7 @@ enum hp_wmi_commandtype { + .driver_data = (void *)&omen_v1_no_ec_board_params, +@@ -360,6 +406,7 @@ enum hp_wmi_commandtype { HPWMI_POSTCODEERROR_QUERY = 0x2a, HPWMI_SYSTEM_DEVICE_MODE = 0x40, HPWMI_THERMAL_PROFILE_QUERY = 0x4c, @@ -4672,7 +4331,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c }; struct victus_power_limits { -@@ -504,9 +575,9 @@ struct hp_wmi_hwmon_priv { +@@ -528,9 +575,9 @@ struct hp_wmi_hwmon_priv { struct mutex lock; /* protects mode, pwm */ u8 min_rpm; u8 max_rpm; @@ -4684,7 +4343,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c struct delayed_work keep_alive_dwork; }; -@@ -833,24 +904,20 @@ static int hp_wmi_fan_speed_max_set(int enabled) +@@ -857,24 +904,20 @@ static int hp_wmi_fan_speed_max_set(int enabled) return enabled; } @@ -4719,7 +4378,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c ret = hp_wmi_get_fan_count_userdefine_trigger(); if (ret < 0) -@@ -867,7 +934,9 @@ static int hp_wmi_fan_speed_set(struct hp_wmi_hwmon_priv *priv, u8 speed) +@@ -891,7 +934,9 @@ static int hp_wmi_fan_speed_set(struct hp_wmi_hwmon_priv *priv, u8 speed) static int hp_wmi_fan_speed_reset(struct hp_wmi_hwmon_priv *priv) { @@ -4730,7 +4389,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c } static int hp_wmi_fan_speed_max_reset(struct hp_wmi_hwmon_priv *priv) -@@ -1128,12 +1197,134 @@ static int camera_shutter_input_setup(void) +@@ -1152,12 +1197,134 @@ static int camera_shutter_input_setup(void) return err; } @@ -4865,7 +4524,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c static struct attribute *hp_wmi_attrs[] = { &dev_attr_display.attr, -@@ -1142,6 +1333,7 @@ static struct attribute *hp_wmi_attrs[] = { +@@ -1166,6 +1333,7 @@ static struct attribute *hp_wmi_attrs[] = { &dev_attr_dock.attr, &dev_attr_tablet.attr, &dev_attr_postcode.attr, @@ -4873,7 +4532,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c NULL, }; ATTRIBUTE_GROUPS(hp_wmi); -@@ -1793,6 +1985,38 @@ static bool is_victus_s_thermal_profile(void) +@@ -1817,6 +1985,38 @@ static bool is_victus_s_thermal_profile(void) return is_victus_s_board; } @@ -4912,77 +4571,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c static int victus_s_gpu_thermal_profile_get(bool *ctgp_enable, bool *ppab_enable, u8 *dstate, -@@ -1878,7 +2102,10 @@ static int platform_profile_victus_s_get_ec(enum platform_profile_option *profil - u8 current_dstate, current_gpu_slowdown_temp, tp; - const struct thermal_profile_params *params; - -- params = active_thermal_profile_params; -+ params = hp_wmi_thermal_profile(); -+ if (!params) -+ return -ENODEV; -+ - if (params->ec_tp_offset == HP_EC_OFFSET_UNKNOWN || - params->ec_tp_offset == HP_NO_THERMAL_PROFILE_OFFSET) { - *profile = active_platform_profile; -@@ -1890,10 +2117,10 @@ static int platform_profile_victus_s_get_ec(enum platform_profile_option *profil - return ret; - - /* -- * We cannot use active_thermal_profile_params here, because boards -- * like 8C78 have tp == 0x0 || tp == 0x1 after cold boot, but logically -- * it should have tp == 0x30 || tp == 0x31, as corrected by the Omen -- * Gaming Hub on windows. Hence accept both of these values. -+ * Boards like 8C78 have tp == 0x0 || tp == 0x1 after cold boot, -+ * but logically it should have tp == 0x30 || tp == 0x31, as -+ * corrected by the Omen Gaming Hub on windows. Hence accept both -+ * of these values. - */ - if (tp == victus_s_thermal_params.performance || - tp == omen_v1_thermal_params.performance) { -@@ -1928,12 +2155,12 @@ static int platform_profile_victus_s_get_ec(enum platform_profile_option *profil - - static int platform_profile_victus_s_set_ec(enum platform_profile_option profile) - { -- struct thermal_profile_params *params; -+ const struct thermal_profile_params *params; - bool gpu_ctgp_enable, gpu_ppab_enable; - u8 gpu_dstate; /* Test shows 1 = 100%, 2 = 50%, 3 = 25%, 4 = 12.5% */ - int err, tp; - -- params = active_thermal_profile_params; -+ params = hp_wmi_thermal_profile(); - if (!params) - return -ENODEV; - -@@ -2199,6 +2426,7 @@ static const struct platform_profile_ops hp_wmi_platform_profile_ops = { - static int thermal_profile_setup(struct platform_device *device) - { - const struct platform_profile_ops *ops; -+ const struct thermal_profile_params *params; - int err, tp; - - if (is_omen_thermal_profile()) { -@@ -2230,13 +2458,17 @@ static int thermal_profile_setup(struct platform_device *device) - - ops = &platform_profile_victus_ops; - } else if (is_victus_s_thermal_profile()) { -+ params = hp_wmi_thermal_profile(); -+ if (!params) -+ return -ENODEV; -+ - /* - * For an unknown EC layout board, platform_profile_victus_s_get_ec(), - * behaves like a wrapper around active_platform_profile, to avoid using - * uninitialized data, we default to PLATFORM_PROFILE_BALANCED. - */ -- if (active_thermal_profile_params->ec_tp_offset == HP_EC_OFFSET_UNKNOWN || -- active_thermal_profile_params->ec_tp_offset == HP_NO_THERMAL_PROFILE_OFFSET) { -+ if (params->ec_tp_offset == HP_EC_OFFSET_UNKNOWN || -+ params->ec_tp_offset == HP_NO_THERMAL_PROFILE_OFFSET) { - active_platform_profile = PLATFORM_PROFILE_BALANCED; - } else { - err = platform_profile_victus_s_get_ec(&active_platform_profile); -@@ -2404,7 +2636,7 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv) +@@ -2436,7 +2636,7 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv) switch (priv->mode) { case PWM_MODE_MAX: @@ -4991,7 +4580,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c ret = hp_wmi_get_fan_count_userdefine_trigger(); if (ret < 0) return ret; -@@ -2416,16 +2648,16 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv) +@@ -2448,16 +2648,16 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv) secs_to_jiffies(KEEP_ALIVE_DELAY_SECS)); return 0; case PWM_MODE_MANUAL: @@ -5011,7 +4600,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c ret = hp_wmi_get_fan_count_userdefine_trigger(); if (ret < 0) return ret; -@@ -2449,12 +2681,12 @@ static umode_t hp_wmi_hwmon_is_visible(const void *data, +@@ -2481,12 +2681,12 @@ static umode_t hp_wmi_hwmon_is_visible(const void *data, { switch (type) { case hwmon_pwm: @@ -5027,7 +4616,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c return 0444; } else { if (hp_wmi_get_fan_speed(channel) >= 0) -@@ -2478,8 +2710,8 @@ static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, +@@ -2510,8 +2710,8 @@ static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, priv = dev_get_drvdata(dev); switch (type) { case hwmon_fan: @@ -5038,7 +4627,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c else ret = hp_wmi_get_fan_speed(channel); if (ret < 0) -@@ -2488,10 +2720,10 @@ static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, +@@ -2520,10 +2720,10 @@ static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, return 0; case hwmon_pwm: if (attr == hwmon_pwm_input) { @@ -5051,7 +4640,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c if (rpm < 0) return rpm; *val = rpm_to_pwm(rpm / 100, priv); -@@ -2518,14 +2750,15 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, +@@ -2550,14 +2750,15 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { struct hp_wmi_hwmon_priv *priv; @@ -5069,7 +4658,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c return -EOPNOTSUPP; /* PWM input is invalid when not in manual mode */ if (priv->mode != PWM_MODE_MANUAL) -@@ -2534,7 +2767,10 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, +@@ -2566,7 +2767,10 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, /* ensure PWM input is within valid fan speeds */ rpm = pwm_to_rpm(val, priv); rpm = clamp_val(rpm, priv->min_rpm, priv->max_rpm); @@ -5081,7 +4670,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c return hp_wmi_apply_fan_settings(priv); } switch (val) { -@@ -2542,16 +2778,20 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, +@@ -2574,16 +2778,20 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, priv->mode = PWM_MODE_MAX; return hp_wmi_apply_fan_settings(priv); case PWM_MODE_MANUAL: @@ -5107,7 +4696,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c priv->mode = PWM_MODE_MANUAL; return hp_wmi_apply_fan_settings(priv); case PWM_MODE_AUTO: -@@ -2567,7 +2807,7 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, +@@ -2599,7 +2807,7 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, static const struct hwmon_channel_info * const info[] = { HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT), @@ -5116,7 +4705,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c NULL }; -@@ -2608,14 +2848,14 @@ static int hp_wmi_setup_fan_settings(struct hp_wmi_hwmon_priv *priv) +@@ -2640,14 +2848,14 @@ static int hp_wmi_setup_fan_settings(struct hp_wmi_hwmon_priv *priv) struct victus_s_fan_table *fan_table; u8 min_rpm, max_rpm; u8 cpu_rpm, gpu_rpm, noise_db; @@ -5134,7 +4723,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c return 0; ret = hp_wmi_perform_query(HPWMI_VICTUS_S_GET_FAN_TABLE_QUERY, -@@ -2654,10 +2894,8 @@ static int hp_wmi_setup_fan_settings(struct hp_wmi_hwmon_priv *priv) +@@ -2686,10 +2894,8 @@ static int hp_wmi_setup_fan_settings(struct hp_wmi_hwmon_priv *priv) if (min_rpm == U8_MAX || max_rpm == 0) return -EINVAL; @@ -5145,54 +4734,6 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c return 0; } -@@ -2697,24 +2935,25 @@ static int hp_wmi_hwmon_init(void) - return 0; - } - --static void __init setup_active_thermal_profile_params(void) -+static void __init setup_active_board_params(void) - { - const struct dmi_system_id *id; -+ const struct thermal_profile_params *params; - -- /* -- * Currently only victus_s devices use the -- * active_thermal_profile_params -- */ -- id = dmi_first_match(victus_s_thermal_profile_boards); -+ id = dmi_first_match(hp_wmi_feature_boards); - if (id) { -+ active_board_params = id->driver_data; -+ params = hp_wmi_thermal_profile(); -+ if (!params) -+ return; -+ - /* - * Marking this boolean is required to ensure that - * is_victus_s_thermal_profile() behaves like a valid - * wrapper. - */ - is_victus_s_board = true; -- active_thermal_profile_params = id->driver_data; -- if (active_thermal_profile_params->ec_tp_offset == HP_EC_OFFSET_UNKNOWN) { -+ if (params->ec_tp_offset == HP_EC_OFFSET_UNKNOWN) { - pr_warn("Unknown EC layout for board %s. Thermal profile readback will be disabled. Please report this to platform-driver-x86@vger.kernel.org\n", - dmi_get_system_info(DMI_BOARD_NAME)); - } -@@ -2749,10 +2988,10 @@ static int __init hp_wmi_init(void) - } - - /* -- * Setup active board's thermal profile parameters before -- * starting platform driver probe. -+ * Setup active board feature data before starting platform -+ * driver probe. - */ -- setup_active_thermal_profile_params(); -+ setup_active_board_params(); - err = platform_driver_probe(&hp_wmi_driver, hp_wmi_bios_setup); - if (err) - goto err_unregister_device; diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c --- a/drivers/platform/x86/huawei-wmi.c +++ b/drivers/platform/x86/huawei-wmi.c @@ -6042,7 +5583,7 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c #define SB_GGOV_METHOD "\\_SB.GGOV" #define GOV_TLED 0x2020008 -@@ -78,42 +88,95 @@ MODULE_PARM_DESC(fw_debug, "Enable printing of firmware debug messages"); +@@ -78,26 +88,29 @@ MODULE_PARM_DESC(fw_debug, "Enable printing of firmware debug messages"); #define WMBB_USB_CHARGE 0x10B #define WMBB_BATT_LIMIT 0x10C @@ -6084,8 +5625,8 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c -#define INIT_SPARSE_KEYMAP 0x80 static int battery_limit_use_wmbb; -+static bool kbd_backlight_available; - static struct led_classdev kbd_backlight; + static bool kbd_backlight_available; +@@ -105,16 +118,65 @@ static struct led_classdev kbd_backlight; static enum led_brightness get_kbd_backlight_level(struct device *dev); static const struct key_entry wmi_keymap[] = { @@ -6158,7 +5699,7 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c static int ggov(u32 arg0) { union acpi_object args[1]; -@@ -154,30 +217,97 @@ static int ggov(u32 arg0) +@@ -155,30 +217,97 @@ static int ggov(u32 arg0) return res; } @@ -6273,13 +5814,14 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c } static union acpi_object *lg_wmbb(struct device *dev, u32 method_id, u32 arg1, u32 arg2) -@@ -211,72 +341,11 @@ static union acpi_object *lg_wmbb(struct device *dev, u32 method_id, u32 arg1, u +@@ -212,76 +341,11 @@ static union acpi_object *lg_wmbb(struct device *dev, u32 method_id, u32 arg1, u return (union acpi_object *)buffer.pointer; } -static void wmi_notify(union acpi_object *obj, void *context) -{ - long data = (long)context; +- unsigned int brightness; - - pr_debug("event guid %li\n", data); - if (!obj) @@ -6290,8 +5832,11 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c - struct key_entry *key; - - if (eventcode == 0x10000000) { -- led_classdev_notify_brightness_hw_changed( -- &kbd_backlight, get_kbd_backlight_level(kbd_backlight.dev->parent)); +- if (kbd_backlight_available) { +- brightness = get_kbd_backlight_level(kbd_backlight.dev->parent); +- led_classdev_notify_brightness_hw_changed(&kbd_backlight, +- brightness); +- } - } else { - key = sparse_keymap_entry_from_scancode( - wmi_input_dev, eventcode); @@ -6346,7 +5891,7 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c int ret; ret = kstrtoul(buffer, 10, &value); -@@ -285,10 +354,10 @@ static ssize_t fan_mode_store(struct device *dev, +@@ -290,10 +354,10 @@ static ssize_t fan_mode_store(struct device *dev, if (value >= 3) return -EINVAL; @@ -6361,7 +5906,7 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c return count; } -@@ -296,22 +365,14 @@ static ssize_t fan_mode_store(struct device *dev, +@@ -301,22 +365,14 @@ static ssize_t fan_mode_store(struct device *dev, static ssize_t fan_mode_show(struct device *dev, struct device_attribute *attr, char *buffer) { @@ -6390,7 +5935,7 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c } static ssize_t usb_charge_store(struct device *dev, -@@ -361,41 +422,30 @@ static ssize_t reader_mode_store(struct device *dev, +@@ -366,41 +422,30 @@ static ssize_t reader_mode_store(struct device *dev, const char *buffer, size_t count) { bool value; @@ -6441,7 +5986,7 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c } static ssize_t fn_lock_store(struct device *dev, -@@ -403,40 +453,30 @@ static ssize_t fn_lock_store(struct device *dev, +@@ -408,40 +453,30 @@ static ssize_t fn_lock_store(struct device *dev, const char *buffer, size_t count) { bool value; @@ -6491,7 +6036,7 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c } static ssize_t charge_control_end_threshold_store(struct device *dev, -@@ -453,14 +493,18 @@ static ssize_t charge_control_end_threshold_store(struct device *dev, +@@ -458,14 +493,18 @@ static ssize_t charge_control_end_threshold_store(struct device *dev, if (value == 100 || value == 80) { union acpi_object *r; @@ -6516,7 +6061,7 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c return count; } -@@ -471,8 +515,9 @@ static ssize_t charge_control_end_threshold_show(struct device *device, +@@ -476,8 +515,9 @@ static ssize_t charge_control_end_threshold_show(struct device *device, struct device_attribute *attr, char *buf) { @@ -6527,7 +6072,7 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c if (battery_limit_use_wmbb) { r = lg_wmbb(&pf_device->dev, WMBB_BATT_LIMIT, WM_GET, 0); -@@ -485,19 +530,13 @@ static ssize_t charge_control_end_threshold_show(struct device *device, +@@ -490,19 +530,13 @@ static ssize_t charge_control_end_threshold_show(struct device *device, } status = r->buffer.pointer[0x10]; @@ -6552,7 +6097,7 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c if (status != 80 && status != 100) status = 0; -@@ -563,10 +602,7 @@ static const struct attribute_group dev_attribute_group = { +@@ -568,10 +602,7 @@ static const struct attribute_group dev_attribute_group = { static void tpad_led_set(struct led_classdev *cdev, enum led_brightness brightness) { @@ -6564,7 +6109,7 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c } static enum led_brightness tpad_led_get(struct led_classdev *cdev) -@@ -579,47 +615,50 @@ static LED_DEVICE(tpad_led, 1, 0); +@@ -584,47 +615,50 @@ static LED_DEVICE(tpad_led, 1, 0); static void kbd_backlight_set(struct led_classdev *cdev, enum led_brightness brightness) { @@ -6644,7 +6189,7 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c } static enum led_brightness kbd_backlight_get(struct led_classdev *cdev) -@@ -629,26 +668,163 @@ static enum led_brightness kbd_backlight_get(struct led_classdev *cdev) +@@ -634,26 +668,163 @@ static enum led_brightness kbd_backlight_get(struct led_classdev *cdev) static LED_DEVICE(kbd_backlight, 255, LED_BRIGHT_HW_CHANGED); @@ -6822,42 +6367,36 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c static acpi_status lg_laptop_address_space_write(struct device *dev, acpi_physical_address address, size_t size, u64 value) { -@@ -860,15 +1036,23 @@ static int acpi_probe(struct platform_device *pdev) +@@ -865,10 +1036,6 @@ static int acpi_probe(struct platform_device *pdev) if (year >= 2019) battery_limit_use_wmbb = 1; -+ /* LEDs are optional */ -+ ret = devm_led_classdev_register(&pdev->dev, &kbd_backlight); -+ if (ret < 0) -+ kbd_backlight_available = false; -+ else -+ kbd_backlight_available = true; -+ -+ devm_led_classdev_register(&pdev->dev, &tpad_led); -+ +- ret = sysfs_create_group(&pf_device->dev.kobj, &dev_attribute_group); +- if (ret) +- goto out_platform_device; +- + /* LEDs are optional */ + ret = devm_led_classdev_register(&pdev->dev, &kbd_backlight); + if (ret < 0) +@@ -878,7 +1045,14 @@ static int acpi_probe(struct platform_device *pdev) + + devm_led_classdev_register(&pdev->dev, &tpad_led); + +- wmi_input_setup(); + ret = lg_laptop_input_init(&pdev->dev, device->handle); + if (ret < 0) + goto out_platform_device; + - ret = sysfs_create_group(&pf_device->dev.kobj, &dev_attribute_group); - if (ret) - goto out_platform_device; - -- /* LEDs are optional */ -- led_classdev_register(&pf_device->dev, &kbd_backlight); -- led_classdev_register(&pf_device->dev, &tpad_led); -- -- wmi_input_setup(); ++ ret = sysfs_create_group(&pf_device->dev.kobj, &dev_attribute_group); ++ if (ret) ++ goto out_platform_device; ++ battery_hook_register(&battery_hook); return 0; -@@ -884,11 +1068,7 @@ static void acpi_remove(struct platform_device *pdev) - { +@@ -895,7 +1069,6 @@ static void acpi_remove(struct platform_device *pdev) sysfs_remove_group(&pf_device->dev.kobj, &dev_attribute_group); -- led_classdev_unregister(&tpad_led); -- led_classdev_unregister(&kbd_backlight); -- battery_hook_unregister(&battery_hook); - wmi_input_destroy(); platform_device_unregister(pf_device); diff --git a/pkgbuilds/linux-omarchy-bore/0800-platform-updates.patch.sig b/pkgbuilds/linux-omarchy-bore/0800-platform-updates.patch.sig index 03807cf..58371ed 100644 Binary files a/pkgbuilds/linux-omarchy-bore/0800-platform-updates.patch.sig and b/pkgbuilds/linux-omarchy-bore/0800-platform-updates.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0802-platform-fixes.patch b/pkgbuilds/linux-omarchy-bore/0802-platform-fixes.patch new file mode 100644 index 0000000..b7d82cc --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0802-platform-fixes.patch @@ -0,0 +1,240 @@ +diff --git a/drivers/platform/x86/amd/pmf/spc.c b/drivers/platform/x86/amd/pmf/spc.c +--- a/drivers/platform/x86/amd/pmf/spc.c ++++ b/drivers/platform/x86/amd/pmf/spc.c +@@ -17,7 +17,6 @@ + #include + #include "pmf.h" + +-#ifdef CONFIG_AMD_PMF_DEBUG + u32 amd_pmf_get_ta_custom_bios_inputs(struct ta_pmf_enact_table *in, int index) + { + switch (index) { +@@ -31,6 +30,7 @@ u32 amd_pmf_get_ta_custom_bios_inputs(struct ta_pmf_enact_table *in, int index) + } + EXPORT_SYMBOL(amd_pmf_get_ta_custom_bios_inputs); + ++#ifdef CONFIG_AMD_PMF_DEBUG + void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) + { + int i; +diff --git a/drivers/platform/x86/x86-android-tablets/core.c b/drivers/platform/x86/x86-android-tablets/core.c +--- a/drivers/platform/x86/x86-android-tablets/core.c ++++ b/drivers/platform/x86/x86-android-tablets/core.c +@@ -156,7 +156,6 @@ static struct platform_device **pdevs; + static struct serdev_device **serdevs; + static const struct software_node **gpio_button_swnodes; + static const struct software_node **swnode_group; +-static const struct software_node **gpiochip_node_group; + static void (*exit_handler)(void); + + static __init struct i2c_adapter * +@@ -362,6 +361,15 @@ static const struct software_node *cherryview_gpiochip_node_group[] = { + NULL + }; + ++const struct software_node crystalcove_gpiochip_node = { ++ .name = "INT33FD:00", ++}; ++ ++static const struct software_node *crystalcove_gpiochip_node_group[] = { ++ &crystalcove_gpiochip_node, ++ NULL ++}; ++ + static void gpio_secondary_unset(void *data) + { + struct device *dev = data; +@@ -377,27 +385,28 @@ static void gpio_secondary_unregister_node_group(void *data) + software_node_unregister_node_group(nodes); + } + +-static int gpio_secondary_fwnode_init(struct device *parent) ++static int gpio_secondary_fwnode_init(struct device *parent, ++ const struct software_node * const *node_group) + { + const struct software_node *const *swnode; + struct fwnode_handle *fwnode; + struct device *phys_dev; + int ret; + +- if (!gpiochip_node_group) ++ if (!node_group) + return 0; + +- ret = software_node_register_node_group(gpiochip_node_group); ++ ret = software_node_register_node_group(node_group); + if (ret) + return ret; + + ret = devm_add_action_or_reset(parent, + gpio_secondary_unregister_node_group, +- gpiochip_node_group); ++ (void *)node_group); + if (ret) + return ret; + +- for (swnode = gpiochip_node_group; *swnode; swnode++) { ++ for (swnode = node_group; *swnode; swnode++) { + struct device *dev __free(put_device) = + acpi_bus_find_device_by_name((*swnode)->name); + if (!dev) +@@ -460,6 +469,7 @@ static void x86_android_tablet_remove(struct platform_device *pdev) + + static __init int x86_android_tablet_probe(struct platform_device *pdev) + { ++ const struct software_node * const *gpiochip_node_group; + const struct x86_dev_info *dev_info; + const struct dmi_system_id *id; + int i, ret = 0; +@@ -491,12 +501,20 @@ static __init int x86_android_tablet_probe(struct platform_device *pdev) + break; + } + +- ret = gpio_secondary_fwnode_init(&pdev->dev); ++ ret = gpio_secondary_fwnode_init(&pdev->dev, gpiochip_node_group); + if (ret) { + x86_android_tablet_remove(pdev); + return ret; + } + ++ if (dev_info->has_crystalcove) { ++ ret = gpio_secondary_fwnode_init(&pdev->dev, crystalcove_gpiochip_node_group); ++ if (ret) { ++ x86_android_tablet_remove(pdev); ++ return ret; ++ } ++ } ++ + ret = software_node_register_node_group(dev_info->swnode_group); + if (ret) { + x86_android_tablet_remove(pdev); +diff --git a/drivers/platform/x86/x86-android-tablets/lenovo.c b/drivers/platform/x86/x86-android-tablets/lenovo.c +--- a/drivers/platform/x86/x86-android-tablets/lenovo.c ++++ b/drivers/platform/x86/x86-android-tablets/lenovo.c +@@ -61,13 +61,6 @@ static struct lp855x_platform_data lenovo_lp8557_reg_only_pdata = { + .initial_brightness = 128, + }; + +-static const struct software_node arizona_gpiochip_node = { +- .name = "arizona", +-}; +- +-static const struct software_node crystalcove_gpiochip_node = { +- .name = "gpio_crystalcove", +-}; + + /* Lenovo Yoga Book X90F / X90L's Android factory image has everything hardcoded */ + +@@ -416,15 +409,17 @@ static const struct platform_device_info lenovo_yoga_tab2_830_1050_pdevs[] __ini + + #define LENOVO_YOGA_TAB2_830_1050_CODEC_NAME "spi-10WM5102:00" + ++static const struct software_node lenovo_yoga_tab2_830_1050_wm5102; ++ + static const struct property_entry lenovo_yoga_tab2_830_1050_wm1502_props[] = { + PROPERTY_ENTRY_GPIO("reset-gpios", + &crystalcove_gpiochip_node, 3, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("wlf,ldoena-gpios", + &baytrail_gpiochip_nodes[1], 23, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("wlf,spkvdd-ena-gpios", +- &arizona_gpiochip_node, 2, GPIO_ACTIVE_HIGH), ++ &lenovo_yoga_tab2_830_1050_wm5102, 2, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("wlf,micd-pol-gpios", +- &arizona_gpiochip_node, 4, GPIO_ACTIVE_LOW), ++ &lenovo_yoga_tab2_830_1050_wm5102, 4, GPIO_ACTIVE_LOW), + { } + }; + +@@ -432,14 +427,6 @@ static const struct software_node lenovo_yoga_tab2_830_1050_wm5102 = { + .properties = lenovo_yoga_tab2_830_1050_wm1502_props, + }; + +-static const struct software_node *lenovo_yoga_tab2_830_1050_swnodes[] = { +- &crystalcove_gpiochip_node, +- &arizona_gpiochip_node, +- &lenovo_yoga_tab2_830_1050_wm5102, +- &generic_lipo_hv_4v35_battery_node, +- NULL +-}; +- + static int __init lenovo_yoga_tab2_830_1050_init(struct device *dev); + static void lenovo_yoga_tab2_830_1050_exit(void); + +@@ -455,8 +442,9 @@ const struct x86_dev_info lenovo_yoga_tab2_830_1050_info __initconst = { + .pdev_info = lenovo_yoga_tab2_830_1050_pdevs, + .pdev_count = ARRAY_SIZE(lenovo_yoga_tab2_830_1050_pdevs), + .gpio_button_swnodes = lenovo_yoga_tab2_830_1050_lid_swnodes, +- .swnode_group = lenovo_yoga_tab2_830_1050_swnodes, ++ .swnode_group = generic_lipo_hv_4v35_battery_swnodes, + .modules = lenovo_yoga_tab2_modules, ++ .has_crystalcove = true, + .gpiochip_type = X86_GPIOCHIP_BAYTRAIL, + .init = lenovo_yoga_tab2_830_1050_init, + .exit = lenovo_yoga_tab2_830_1050_exit, +@@ -800,8 +788,9 @@ const struct x86_dev_info lenovo_yoga_tab2_1380_info __initconst = { + .pdev_info = lenovo_yoga_tab2_1380_pdevs, + .pdev_count = ARRAY_SIZE(lenovo_yoga_tab2_1380_pdevs), + .gpio_button_swnodes = lenovo_yoga_tab2_830_1050_lid_swnodes, +- .swnode_group = lenovo_yoga_tab2_830_1050_swnodes, ++ .swnode_group = generic_lipo_hv_4v35_battery_swnodes, + .modules = lenovo_yoga_tab2_modules, ++ .has_crystalcove = true, + .gpiochip_type = X86_GPIOCHIP_BAYTRAIL, + .init = lenovo_yoga_tab2_1380_init, + .exit = lenovo_yoga_tab2_830_1050_exit, +@@ -985,13 +974,15 @@ static struct arizona_pdata lenovo_yt3_wm5102_pdata = { + }, + }; + ++static const struct software_node lenovo_yt3_wm5102; ++ + static const struct property_entry lenovo_yt3_wm1502_props[] = { + PROPERTY_ENTRY_GPIO("wlf,spkvdd-ena-gpios", + &cherryview_gpiochip_nodes[0], 75, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("wlf,ldoena-gpios", + &cherryview_gpiochip_nodes[0], 81, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("reset-gpios", &cherryview_gpiochip_nodes[0], 82, GPIO_ACTIVE_HIGH), +- PROPERTY_ENTRY_GPIO("wlf,micd-pol-gpios", &arizona_gpiochip_node, 2, GPIO_ACTIVE_HIGH), ++ PROPERTY_ENTRY_GPIO("wlf,micd-pol-gpios", &lenovo_yt3_wm5102, 2, GPIO_ACTIVE_HIGH), + { } + }; + +@@ -1000,11 +991,6 @@ static const struct software_node lenovo_yt3_wm5102 = { + .name = "wm5102", + }; + +-static const struct software_node *lenovo_yt3_swnodes[] = { +- &arizona_gpiochip_node, +- &lenovo_yt3_wm5102, +- NULL +-}; + + static const struct x86_spi_dev_info lenovo_yt3_spi_devs[] __initconst = { + { +@@ -1072,7 +1058,6 @@ const struct x86_dev_info lenovo_yt3_info __initconst = { + .i2c_client_count = ARRAY_SIZE(lenovo_yt3_i2c_clients), + .spi_dev_info = lenovo_yt3_spi_devs, + .spi_dev_count = ARRAY_SIZE(lenovo_yt3_spi_devs), +- .swnode_group = lenovo_yt3_swnodes, + .modules = lenovo_yt3_modules, + .gpiochip_type = X86_GPIOCHIP_CHERRYVIEW, + .init = lenovo_yt3_init, +diff --git a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h +--- a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h ++++ b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h +@@ -96,6 +96,7 @@ struct x86_dev_info { + int (*init)(struct device *dev); + void (*exit)(void); + bool use_pci; ++ bool has_crystalcove; + enum x86_gpiochip_type gpiochip_type; + }; + +@@ -107,6 +108,7 @@ int x86_acpi_irq_helper_get(const struct x86_acpi_irq_data *data); + /* Software nodes representing GPIO chips used by various tablets */ + extern const struct software_node baytrail_gpiochip_nodes[]; + extern const struct software_node cherryview_gpiochip_nodes[]; ++extern const struct software_node crystalcove_gpiochip_node; + + /* + * Extern declarations of x86_dev_info structs so there can be a single diff --git a/pkgbuilds/linux-omarchy-bore/0802-platform-fixes.patch.sig b/pkgbuilds/linux-omarchy-bore/0802-platform-fixes.patch.sig new file mode 100644 index 0000000..3e19e33 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0802-platform-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0860-ftrace-module-enable-sorted-symbol-search.patch b/pkgbuilds/linux-omarchy-bore/0860-ftrace-module-enable-sorted-symbol-search.patch new file mode 100644 index 0000000..79ded30 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/0860-ftrace-module-enable-sorted-symbol-search.patch @@ -0,0 +1,170 @@ +diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c +--- a/kernel/trace/ftrace.c ++++ b/kernel/trace/ftrace.c +@@ -18,6 +18,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -4385,6 +4386,109 @@ static int test_for_valid_rec(struct dyn_ftrace *rec) + return 1; + } + ++#if defined(CONFIG_MODULES) && defined(CONFIG_KALLSYMS) ++#define FTRACE_MOD_SYMS ++/* ++ * test_for_valid_rec() resolves an address with kallsyms_lookup(), which ++ * scans the whole symbol table of a module. Calling it for every record of ++ * a module being loaded costs O(records * symbols): several seconds for a ++ * driver as large as amdgpu, all of it under ftrace_lock. Sort the symbol ++ * addresses of the module once instead and binary search them. The module ++ * symbol table itself is left untouched, as livepatch relies on its order. ++ */ ++struct ftrace_mod_syms { ++ unsigned long *addrs; ++ unsigned int nr; ++}; ++ ++static int ftrace_cmp_addr(const void *a, const void *b) ++{ ++ unsigned long x = *(const unsigned long *)a; ++ unsigned long y = *(const unsigned long *)b; ++ ++ return x < y ? -1 : x > y; ++} ++ ++/* Collect the symbols find_kallsyms_symbol() would consider. */ ++static void ftrace_mod_syms_init(struct ftrace_mod_syms *syms, ++ struct module *mod) ++{ ++ /* A coming module cannot have its kallsyms replaced under us. */ ++ struct mod_kallsyms *kallsyms = rcu_dereference_raw(mod->kallsyms); ++ unsigned int i; ++ ++ syms->nr = 0; ++ syms->addrs = kvmalloc_array(kallsyms->num_symtab, ++ sizeof(*syms->addrs), GFP_KERNEL); ++ if (!syms->addrs) ++ return; ++ ++ for (i = 1; i < kallsyms->num_symtab; i++) { ++ const Elf_Sym *sym = &kallsyms->symtab[i]; ++ const char *name = kallsyms->strtab + sym->st_name; ++ ++ if (sym->st_shndx == SHN_UNDEF || *name == '\0' || ++ is_mapping_symbol(name)) ++ continue; ++ syms->addrs[syms->nr++] = kallsyms_symbol_value(sym); ++ } ++ ++ sort(syms->addrs, syms->nr, sizeof(*syms->addrs), ftrace_cmp_addr, NULL); ++} ++ ++/* Same answer as test_for_valid_rec(), using the sorted addresses. */ ++static int test_for_valid_mod_rec(struct ftrace_mod_syms *syms, ++ struct module *mod, struct dyn_ftrace *rec) ++{ ++ unsigned long ip = rec->ip, base, best; ++ unsigned int lo = 0, hi = syms->nr, mid; ++ struct module_memory *mod_mem = NULL; ++ ++ if (!syms->addrs) ++ return test_for_valid_rec(rec); ++ ++ for_each_mod_mem_type(type) { ++#ifndef CONFIG_KALLSYMS_ALL ++ if (!mod_mem_type_is_text(type)) ++ continue; ++#endif ++ if (within_module_mem_type(ip, mod, type)) { ++ mod_mem = &mod->mem[type]; ++ break; ++ } ++ } ++ if (!mod_mem) ++ goto invalid; ++ base = (unsigned long)mod_mem->base; ++ ++ /* Find the last symbol at or below ip. */ ++ while (lo < hi) { ++ mid = lo + (hi - lo) / 2; ++ if (syms->addrs[mid] <= ip) ++ lo = mid + 1; ++ else ++ hi = mid; ++ } ++ if (!lo) ++ goto invalid; ++ best = syms->addrs[lo - 1]; ++ ++ /* Weak functions can cause invalid addresses */ ++ if (best < base || ip - best > FTRACE_MCOUNT_MAX_OFFSET) ++ goto invalid; ++ return 1; ++ ++invalid: ++ rec->flags |= FTRACE_FL_DISABLED; ++ return 0; ++} ++ ++static void ftrace_mod_syms_free(struct ftrace_mod_syms *syms) ++{ ++ kvfree(syms->addrs); ++} ++#endif ++ + static struct workqueue_struct *ftrace_check_wq __initdata; + static struct work_struct ftrace_check_work __initdata; + +@@ -8010,11 +8114,30 @@ void ftrace_release_mod(struct module *mod) + } + } + ++#ifndef FTRACE_MOD_SYMS ++struct ftrace_mod_syms { }; ++ ++static inline void ftrace_mod_syms_init(struct ftrace_mod_syms *syms, ++ struct module *mod) { } ++ ++static inline int test_for_valid_mod_rec(struct ftrace_mod_syms *syms, ++ struct module *mod, ++ struct dyn_ftrace *rec) ++{ ++ return test_for_valid_rec(rec); ++} ++ ++static inline void ftrace_mod_syms_free(struct ftrace_mod_syms *syms) { } ++#endif ++ + void ftrace_module_enable(struct module *mod) + { ++ struct ftrace_mod_syms syms; + struct dyn_ftrace *rec; + struct ftrace_page *pg; + ++ ftrace_mod_syms_init(&syms, mod); ++ + mutex_lock(&ftrace_lock); + + if (ftrace_disabled) +@@ -8050,7 +8173,7 @@ void ftrace_module_enable(struct module *mod) + cond_resched(); + + /* Weak functions should still be ignored */ +- if (!test_for_valid_rec(rec)) { ++ if (!test_for_valid_mod_rec(&syms, mod, rec)) { + /* Clear all other flags. Should not be enabled anyway */ + rec->flags = FTRACE_FL_DISABLED; + continue; +@@ -8087,6 +8210,8 @@ void ftrace_module_enable(struct module *mod) + out_unlock: + mutex_unlock(&ftrace_lock); + ++ ftrace_mod_syms_free(&syms); ++ + process_cached_mods(mod->name); + } + diff --git a/pkgbuilds/linux-omarchy-bore/0860-ftrace-module-enable-sorted-symbol-search.patch.sig b/pkgbuilds/linux-omarchy-bore/0860-ftrace-module-enable-sorted-symbol-search.patch.sig new file mode 100644 index 0000000..709d6ce Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/0860-ftrace-module-enable-sorted-symbol-search.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/8201-xe-shrinker-return-freed-page-count.patch b/pkgbuilds/linux-omarchy-bore/8201-xe-shrinker-return-freed-page-count.patch deleted file mode 100644 index f340536..0000000 --- a/pkgbuilds/linux-omarchy-bore/8201-xe-shrinker-return-freed-page-count.patch +++ /dev/null @@ -1,132 +0,0 @@ -diff --git c/drivers/gpu/drm/xe/xe_shrinker.c i/drivers/gpu/drm/xe/xe_shrinker.c ---- c/drivers/gpu/drm/xe/xe_shrinker.c -+++ i/drivers/gpu/drm/xe/xe_shrinker.c -@@ -54,13 +54,14 @@ xe_shrinker_mod_pages(struct xe_shrinker *shrinker, long shrinkable, long purgea - write_unlock(&shrinker->lock); - } - --static s64 __xe_shrinker_walk(struct xe_device *xe, -+static int __xe_shrinker_walk(struct xe_device *xe, - struct ttm_operation_ctx *ctx, - const struct xe_bo_shrink_flags flags, -- unsigned long to_scan, unsigned long *scanned) -+ unsigned long to_scan, unsigned long *scanned, -+ unsigned long *freed) - { - unsigned int mem_type; -- s64 freed = 0, lret; -+ s64 lret; - - for (mem_type = XE_PL_SYSTEM; mem_type <= XE_PL_TT; ++mem_type) { - struct ttm_resource_manager *man = ttm_manager_type(&xe->ttm, mem_type); -@@ -82,7 +83,7 @@ static s64 __xe_shrinker_walk(struct xe_device *xe, - if (lret < 0) - return lret; - -- freed += lret; -+ *freed += lret; - if (*scanned >= to_scan) - break; - } -@@ -90,7 +91,7 @@ static s64 __xe_shrinker_walk(struct xe_device *xe, - xe_assert(xe, !IS_ERR(ttm_bo)); - } - -- return freed; -+ return 0; - } - - /* -@@ -99,40 +100,35 @@ static s64 __xe_shrinker_walk(struct xe_device *xe, - * add writeback. This avoids stalls and explicit writebacks with light or - * moderate memory pressure. - */ --static s64 xe_shrinker_walk(struct xe_device *xe, -+static int xe_shrinker_walk(struct xe_device *xe, - struct ttm_operation_ctx *ctx, - const struct xe_bo_shrink_flags flags, -- unsigned long to_scan, unsigned long *scanned) -+ unsigned long to_scan, unsigned long *scanned, -+ unsigned long *freed) - { - bool no_wait_gpu = true; - struct xe_bo_shrink_flags save_flags = flags; -- s64 lret, freed; -+ int ret; - - swap(no_wait_gpu, ctx->no_wait_gpu); - save_flags.writeback = false; -- lret = __xe_shrinker_walk(xe, ctx, save_flags, to_scan, scanned); -+ ret = __xe_shrinker_walk(xe, ctx, save_flags, to_scan, scanned, freed); - swap(no_wait_gpu, ctx->no_wait_gpu); -- if (lret < 0 || *scanned >= to_scan) -- return lret; -+ if (ret || *scanned >= to_scan) -+ return ret; - -- freed = lret; - if (!ctx->no_wait_gpu) { -- lret = __xe_shrinker_walk(xe, ctx, save_flags, to_scan, scanned); -- if (lret < 0) -- return lret; -- freed += lret; -- if (*scanned >= to_scan) -- return freed; -+ ret = __xe_shrinker_walk(xe, ctx, save_flags, to_scan, scanned, -+ freed); -+ if (ret || *scanned >= to_scan) -+ return ret; - } - -- if (flags.writeback) { -- lret = __xe_shrinker_walk(xe, ctx, flags, to_scan, scanned); -- if (lret < 0) -- return lret; -- freed += lret; -- } -+ if (flags.writeback) -+ ret = __xe_shrinker_walk(xe, ctx, flags, to_scan, scanned, -+ freed); - -- return freed; -+ return ret; - } - - static unsigned long -@@ -214,7 +210,6 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con - bool runtime_pm; - bool purgeable; - bool can_backup = !!(sc->gfp_mask & __GFP_FS); -- s64 lret; - - nr_to_scan = sc->nr_to_scan; - -@@ -225,12 +220,9 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con - /* Might need runtime PM. Try to wake early if it looks like it. */ - runtime_pm = xe_shrinker_runtime_pm_get(shrinker, false, nr_to_scan, can_backup); - -- if (purgeable && nr_scanned < nr_to_scan) { -- lret = xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags, -- nr_to_scan, &nr_scanned); -- if (lret >= 0) -- freed += lret; -- } -+ if (purgeable && nr_scanned < nr_to_scan) -+ xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags, -+ nr_to_scan, &nr_scanned, &freed); - - sc->nr_scanned = nr_scanned; - if (nr_scanned >= nr_to_scan || !can_backup) -@@ -242,10 +234,8 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con - - shrink_flags.purge = false; - -- lret = xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags, -- nr_to_scan, &nr_scanned); -- if (lret >= 0) -- freed += lret; -+ xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags, -+ nr_to_scan, &nr_scanned, &freed); - - sc->nr_scanned = nr_scanned; - out: diff --git a/pkgbuilds/linux-omarchy-bore/8201-xe-shrinker-return-freed-page-count.patch.sig b/pkgbuilds/linux-omarchy-bore/8201-xe-shrinker-return-freed-page-count.patch.sig deleted file mode 100644 index bbe1ce3..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/8201-xe-shrinker-return-freed-page-count.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/8202-xe-shrinker-runtime-pm-for-non-system-memory.patch b/pkgbuilds/linux-omarchy-bore/8202-xe-shrinker-runtime-pm-for-non-system-memory.patch deleted file mode 100644 index 29ccecf..0000000 --- a/pkgbuilds/linux-omarchy-bore/8202-xe-shrinker-runtime-pm-for-non-system-memory.patch +++ /dev/null @@ -1,149 +0,0 @@ -diff --git c/drivers/gpu/drm/xe/xe_shrinker.c i/drivers/gpu/drm/xe/xe_shrinker.c ---- c/drivers/gpu/drm/xe/xe_shrinker.c -+++ i/drivers/gpu/drm/xe/xe_shrinker.c -@@ -54,13 +54,33 @@ xe_shrinker_mod_pages(struct xe_shrinker *shrinker, long shrinkable, long purgea - write_unlock(&shrinker->lock); - } - --static int __xe_shrinker_walk(struct xe_device *xe, -+static bool __xe_shrinker_runtime_pm_get(struct xe_shrinker *shrinker) -+{ -+ struct xe_device *xe = shrinker->xe; -+ -+ if (xe_pm_runtime_get_if_active(xe)) -+ return true; -+ -+ if (xe_rpm_reclaim_safe(xe) && !ttm_bo_shrink_avoid_wait()) { -+ xe_pm_runtime_get(xe); -+ return true; -+ } -+ -+ queue_work(xe->unordered_wq, &shrinker->pm_worker); -+ -+ return false; -+} -+ -+static int __xe_shrinker_walk(struct xe_shrinker *shrinker, - struct ttm_operation_ctx *ctx, - const struct xe_bo_shrink_flags flags, - unsigned long to_scan, unsigned long *scanned, - unsigned long *freed) - { -+ struct xe_device *xe = shrinker->xe; - unsigned int mem_type; -+ bool rpm = false; -+ int ret = 0; - s64 lret; - - for (mem_type = XE_PL_SYSTEM; mem_type <= XE_PL_TT; ++mem_type) { -@@ -75,23 +95,36 @@ static int __xe_shrinker_walk(struct xe_device *xe, - if (!man || !man->use_tt) - continue; - -+ if (mem_type != XE_PL_SYSTEM && !rpm && -+ xe_device_is_l2_flush_optimized(xe)) { -+ if (!__xe_shrinker_runtime_pm_get(shrinker)) -+ break; -+ rpm = true; -+ } -+ - ttm_bo_lru_for_each_reserved_guarded(&curs, man, &arg, ttm_bo) { - if (!ttm_bo_shrink_suitable(ttm_bo, ctx)) - continue; - - lret = xe_bo_shrink(ctx, ttm_bo, flags, scanned); -- if (lret < 0) -- return lret; -+ if (lret < 0) { -+ ret = lret; -+ goto out; -+ } - - *freed += lret; - if (*scanned >= to_scan) -- break; -+ goto out; - } - /* Trylocks should never error, just fail. */ - xe_assert(xe, !IS_ERR(ttm_bo)); - } - -- return 0; -+out: -+ if (rpm) -+ xe_pm_runtime_put(xe); -+ -+ return ret; - } - - /* -@@ -100,7 +133,7 @@ static int __xe_shrinker_walk(struct xe_device *xe, - * add writeback. This avoids stalls and explicit writebacks with light or - * moderate memory pressure. - */ --static int xe_shrinker_walk(struct xe_device *xe, -+static int xe_shrinker_walk(struct xe_shrinker *shrinker, - struct ttm_operation_ctx *ctx, - const struct xe_bo_shrink_flags flags, - unsigned long to_scan, unsigned long *scanned, -@@ -112,20 +145,21 @@ static int xe_shrinker_walk(struct xe_device *xe, - - swap(no_wait_gpu, ctx->no_wait_gpu); - save_flags.writeback = false; -- ret = __xe_shrinker_walk(xe, ctx, save_flags, to_scan, scanned, freed); -+ ret = __xe_shrinker_walk(shrinker, ctx, save_flags, to_scan, scanned, -+ freed); - swap(no_wait_gpu, ctx->no_wait_gpu); - if (ret || *scanned >= to_scan) - return ret; - - if (!ctx->no_wait_gpu) { -- ret = __xe_shrinker_walk(xe, ctx, save_flags, to_scan, scanned, -+ ret = __xe_shrinker_walk(shrinker, ctx, save_flags, to_scan, scanned, - freed); - if (ret || *scanned >= to_scan) - return ret; - } - - if (flags.writeback) -- ret = __xe_shrinker_walk(xe, ctx, flags, to_scan, scanned, -+ ret = __xe_shrinker_walk(shrinker, ctx, flags, to_scan, scanned, - freed); - - return ret; -@@ -176,16 +210,7 @@ static bool xe_shrinker_runtime_pm_get(struct xe_shrinker *shrinker, bool force, - return false; - } - -- if (!xe_pm_runtime_get_if_active(xe)) { -- if (xe_rpm_reclaim_safe(xe) && !ttm_bo_shrink_avoid_wait()) { -- xe_pm_runtime_get(xe); -- return true; -- } -- queue_work(xe->unordered_wq, &shrinker->pm_worker); -- return false; -- } -- -- return true; -+ return __xe_shrinker_runtime_pm_get(shrinker); - } - - static void xe_shrinker_runtime_pm_put(struct xe_shrinker *shrinker, bool runtime_pm) -@@ -221,7 +246,7 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con - runtime_pm = xe_shrinker_runtime_pm_get(shrinker, false, nr_to_scan, can_backup); - - if (purgeable && nr_scanned < nr_to_scan) -- xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags, -+ xe_shrinker_walk(shrinker, &ctx, shrink_flags, - nr_to_scan, &nr_scanned, &freed); - - sc->nr_scanned = nr_scanned; -@@ -234,7 +259,7 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con - - shrink_flags.purge = false; - -- xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags, -+ xe_shrinker_walk(shrinker, &ctx, shrink_flags, - nr_to_scan, &nr_scanned, &freed); - - sc->nr_scanned = nr_scanned; diff --git a/pkgbuilds/linux-omarchy-bore/8202-xe-shrinker-runtime-pm-for-non-system-memory.patch.sig b/pkgbuilds/linux-omarchy-bore/8202-xe-shrinker-runtime-pm-for-non-system-memory.patch.sig deleted file mode 100644 index 0707c41..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/8202-xe-shrinker-runtime-pm-for-non-system-memory.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/8203-xe-shrinker-release-through-the-put-helper.patch b/pkgbuilds/linux-omarchy-bore/8203-xe-shrinker-release-through-the-put-helper.patch deleted file mode 100644 index 6af456f..0000000 --- a/pkgbuilds/linux-omarchy-bore/8203-xe-shrinker-release-through-the-put-helper.patch +++ /dev/null @@ -1,39 +0,0 @@ -diff --git c/drivers/gpu/drm/xe/xe_shrinker.c i/drivers/gpu/drm/xe/xe_shrinker.c ---- c/drivers/gpu/drm/xe/xe_shrinker.c -+++ i/drivers/gpu/drm/xe/xe_shrinker.c -@@ -71,6 +71,12 @@ static bool __xe_shrinker_runtime_pm_get(struct xe_shrinker *shrinker) - return false; - } - -+static void xe_shrinker_runtime_pm_put(struct xe_shrinker *shrinker, bool runtime_pm) -+{ -+ if (runtime_pm) -+ xe_pm_runtime_put(shrinker->xe); -+} -+ - static int __xe_shrinker_walk(struct xe_shrinker *shrinker, - struct ttm_operation_ctx *ctx, - const struct xe_bo_shrink_flags flags, -@@ -121,8 +127,7 @@ static int __xe_shrinker_walk(struct xe_shrinker *shrinker, - } - - out: -- if (rpm) -- xe_pm_runtime_put(xe); -+ xe_shrinker_runtime_pm_put(shrinker, rpm); - - return ret; - } -@@ -213,12 +218,6 @@ static bool xe_shrinker_runtime_pm_get(struct xe_shrinker *shrinker, bool force, - return __xe_shrinker_runtime_pm_get(shrinker); - } - --static void xe_shrinker_runtime_pm_put(struct xe_shrinker *shrinker, bool runtime_pm) --{ -- if (runtime_pm) -- xe_pm_runtime_put(shrinker->xe); --} -- - static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_control *sc) - { - struct xe_shrinker *shrinker = to_xe_shrinker(shrink); diff --git a/pkgbuilds/linux-omarchy-bore/8203-xe-shrinker-release-through-the-put-helper.patch.sig b/pkgbuilds/linux-omarchy-bore/8203-xe-shrinker-release-through-the-put-helper.patch.sig deleted file mode 100644 index ddd9329..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/8203-xe-shrinker-release-through-the-put-helper.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/8204-mm-opportunistic-compaction.patch b/pkgbuilds/linux-omarchy-bore/8204-mm-opportunistic-compaction.patch index 0a31aa0..353ca7c 100644 --- a/pkgbuilds/linux-omarchy-bore/8204-mm-opportunistic-compaction.patch +++ b/pkgbuilds/linux-omarchy-bore/8204-mm-opportunistic-compaction.patch @@ -100,7 +100,7 @@ diff --git c/mm/internal.h i/mm/internal.h diff --git c/mm/shrinker.c i/mm/shrinker.c --- c/mm/shrinker.c +++ i/mm/shrinker.c -@@ -474,7 +474,7 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, +@@ -476,7 +476,7 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, #ifdef CONFIG_MEMCG static unsigned long shrink_slab_memcg(gfp_t gfp_mask, int nid, @@ -109,7 +109,7 @@ diff --git c/mm/shrinker.c i/mm/shrinker.c { struct shrinker_info *info; unsigned long ret, freed = 0; -@@ -536,6 +536,7 @@ static unsigned long shrink_slab_memcg(gfp_t gfp_mask, int nid, +@@ -538,6 +538,7 @@ static unsigned long shrink_slab_memcg(gfp_t gfp_mask, int nid, .gfp_mask = gfp_mask, .nid = nid, .memcg = memcg, @@ -117,7 +117,7 @@ diff --git c/mm/shrinker.c i/mm/shrinker.c }; struct shrinker *shrinker; int shrinker_id = calc_shrinker_id(index, offset); -@@ -595,7 +596,8 @@ static unsigned long shrink_slab_memcg(gfp_t gfp_mask, int nid, +@@ -597,7 +598,8 @@ static unsigned long shrink_slab_memcg(gfp_t gfp_mask, int nid, } #else /* !CONFIG_MEMCG */ static unsigned long shrink_slab_memcg(gfp_t gfp_mask, int nid, @@ -127,7 +127,7 @@ diff --git c/mm/shrinker.c i/mm/shrinker.c { return 0; } -@@ -607,6 +609,7 @@ static unsigned long shrink_slab_memcg(gfp_t gfp_mask, int nid, +@@ -609,6 +611,7 @@ static unsigned long shrink_slab_memcg(gfp_t gfp_mask, int nid, * @nid: node whose slab caches to target * @memcg: memory cgroup whose slab caches to target * @priority: the reclaim priority @@ -135,7 +135,7 @@ diff --git c/mm/shrinker.c i/mm/shrinker.c * * Call the shrink functions to age shrinkable caches. * -@@ -622,7 +625,7 @@ static unsigned long shrink_slab_memcg(gfp_t gfp_mask, int nid, +@@ -624,7 +627,7 @@ static unsigned long shrink_slab_memcg(gfp_t gfp_mask, int nid, * Returns the number of reclaimed slab objects. */ unsigned long shrink_slab(gfp_t gfp_mask, int nid, struct mem_cgroup *memcg, @@ -144,7 +144,7 @@ diff --git c/mm/shrinker.c i/mm/shrinker.c { unsigned long ret, freed = 0; struct shrinker *shrinker; -@@ -635,7 +638,8 @@ unsigned long shrink_slab(gfp_t gfp_mask, int nid, struct mem_cgroup *memcg, +@@ -637,7 +640,8 @@ unsigned long shrink_slab(gfp_t gfp_mask, int nid, struct mem_cgroup *memcg, * oom. */ if (!mem_cgroup_disabled() && !mem_cgroup_is_root(memcg)) @@ -154,7 +154,7 @@ diff --git c/mm/shrinker.c i/mm/shrinker.c /* * lockless algorithm of global shrink. -@@ -664,6 +668,7 @@ unsigned long shrink_slab(gfp_t gfp_mask, int nid, struct mem_cgroup *memcg, +@@ -666,6 +670,7 @@ unsigned long shrink_slab(gfp_t gfp_mask, int nid, struct mem_cgroup *memcg, .gfp_mask = gfp_mask, .nid = nid, .memcg = memcg, diff --git a/pkgbuilds/linux-omarchy-bore/8204-mm-opportunistic-compaction.patch.sig b/pkgbuilds/linux-omarchy-bore/8204-mm-opportunistic-compaction.patch.sig index 1b113b3..d4744f8 100644 Binary files a/pkgbuilds/linux-omarchy-bore/8204-mm-opportunistic-compaction.patch.sig and b/pkgbuilds/linux-omarchy-bore/8204-mm-opportunistic-compaction.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/8206-mm-carry-order-and-hint-in-one-word.patch b/pkgbuilds/linux-omarchy-bore/8206-mm-carry-order-and-hint-in-one-word.patch index 92b39ef..65dd70b 100644 --- a/pkgbuilds/linux-omarchy-bore/8206-mm-carry-order-and-hint-in-one-word.patch +++ b/pkgbuilds/linux-omarchy-bore/8206-mm-carry-order-and-hint-in-one-word.patch @@ -1,6 +1,6 @@ -diff --git c/include/linux/mmzone.h i/include/linux/mmzone.h ---- c/include/linux/mmzone.h -+++ i/include/linux/mmzone.h +diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h +--- a/include/linux/mmzone.h ++++ b/include/linux/mmzone.h @@ -1498,6 +1498,32 @@ enum kswapd_opportunistic_compaction_type { KSWAPD_OPPORTUNISTIC_COMPACTION, }; @@ -56,10 +56,10 @@ diff --git c/include/linux/mmzone.h i/include/linux/mmzone.h enum zone_type kswapd_highest_zoneidx; atomic_t kswapd_failures; /* Number of 'reclaimed == 0' runs */ -diff --git c/mm/mm_init.c i/mm/mm_init.c ---- c/mm/mm_init.c -+++ i/mm/mm_init.c -@@ -1553,7 +1553,8 @@ void __ref free_area_init_core_hotplug(struct pglist_data *pgdat) +diff --git a/mm/mm_init.c b/mm/mm_init.c +--- a/mm/mm_init.c ++++ b/mm/mm_init.c +@@ -1558,7 +1558,8 @@ int __ref free_area_init_core_hotplug(struct pglist_data *pgdat) * when it starts in the near future. */ pgdat->nr_zones = 0; @@ -69,9 +69,9 @@ diff --git c/mm/mm_init.c i/mm/mm_init.c pgdat->kswapd_highest_zoneidx = 0; pgdat->node_start_pfn = 0; pgdat->node_present_pages = 0; -diff --git c/mm/vmscan.c i/mm/vmscan.c ---- c/mm/vmscan.c -+++ i/mm/vmscan.c +diff --git a/mm/vmscan.c b/mm/vmscan.c +--- a/mm/vmscan.c ++++ b/mm/vmscan.c @@ -7413,6 +7413,49 @@ static enum zone_type kswapd_highest_zoneidx(pg_data_t *pgdat, return curr_idx == MAX_NR_ZONES ? prev_highest_zoneidx : curr_idx; } diff --git a/pkgbuilds/linux-omarchy-bore/8206-mm-carry-order-and-hint-in-one-word.patch.sig b/pkgbuilds/linux-omarchy-bore/8206-mm-carry-order-and-hint-in-one-word.patch.sig index de65251..684949b 100644 Binary files a/pkgbuilds/linux-omarchy-bore/8206-mm-carry-order-and-hint-in-one-word.patch.sig and b/pkgbuilds/linux-omarchy-bore/8206-mm-carry-order-and-hint-in-one-word.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/8208-mm-thp-deferred-split-uses-hint.patch b/pkgbuilds/linux-omarchy-bore/8208-mm-thp-deferred-split-uses-hint.patch deleted file mode 100644 index 8de9983..0000000 --- a/pkgbuilds/linux-omarchy-bore/8208-mm-thp-deferred-split-uses-hint.patch +++ /dev/null @@ -1,21 +0,0 @@ -diff --git a/mm/huge_memory.c b/mm/huge_memory.c ---- a/mm/huge_memory.c -+++ b/mm/huge_memory.c -@@ -4476,6 +4476,17 @@ static unsigned long deferred_split_count(struct shrinker *shrink, - { - unsigned long count; - -+ /* -+ * Splitting a partially mapped folio during an opportunistic -+ * high-order pass frees its unmapped tail pages but shatters the -+ * folio, so the pass works against the contiguous block it is -+ * reclaiming for. Report nothing rather than SHRINK_EMPTY: for a -+ * memcg aware shrinker SHRINK_EMPTY clears the memcg shrinker bit, -+ * which would also skip the next normal pass. -+ */ -+ if (sc->opportunistic_compaction) -+ return 0; -+ - count = list_lru_shrink_count(&deferred_split_lru, sc); - return count ?: SHRINK_EMPTY; - } diff --git a/pkgbuilds/linux-omarchy-bore/8208-mm-thp-deferred-split-uses-hint.patch.sig b/pkgbuilds/linux-omarchy-bore/8208-mm-thp-deferred-split-uses-hint.patch.sig deleted file mode 100644 index 88c17d4..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/8208-mm-thp-deferred-split-uses-hint.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/8211-xe-shrinker-skip-active-vm.patch b/pkgbuilds/linux-omarchy-bore/8211-xe-shrinker-skip-active-vm.patch new file mode 100644 index 0000000..76470c1 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/8211-xe-shrinker-skip-active-vm.patch @@ -0,0 +1,116 @@ +diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c +--- a/drivers/gpu/drm/xe/xe_bo.c ++++ b/drivers/gpu/drm/xe/xe_bo.c +@@ -28,6 +28,7 @@ + #include "xe_ggtt.h" + #include "xe_map.h" + #include "xe_migrate.h" ++#include "xe_module.h" + #include "xe_pat.h" + #include "xe_pm.h" + #include "xe_preempt_fence.h" +@@ -1244,6 +1245,26 @@ xe_bo_eviction_valuable(struct ttm_buffer_object *bo, const struct ttm_place *pl + return true; + } + ++static bool xe_bo_bound_to_active_vm(struct xe_bo *bo) ++{ ++ u64 window = (u64)READ_ONCE(xe_modparam.shrink_active_ms) * NSEC_PER_MSEC; ++ u64 now = ktime_get_ns(); ++ struct drm_gpuvm_bo *vm_bo; ++ ++ if (!window) ++ return false; ++ ++ drm_gem_for_each_gpuvm_bo(vm_bo, &bo->ttm.base) { ++ struct xe_vm *vm = gpuvm_to_vm(vm_bo->vm); ++ u64 last = READ_ONCE(vm->last_exec_ns); ++ ++ if (!xe_vm_in_fault_mode(vm) && last && now - last < window) ++ return true; ++ } ++ ++ return false; ++} ++ + /** + * xe_bo_shrink() - Try to shrink an xe bo. + * @ctx: The struct ttm_operation_ctx used for shrinking. +@@ -1295,6 +1316,10 @@ long xe_bo_shrink(struct ttm_operation_ctx *ctx, struct ttm_buffer_object *bo, + goto out_unref; + } + ++ if (xe_bo_bound_to_active_vm(xe_bo) && ++ try_wait_for_completion(&xe->pm_block)) ++ goto out_unref; ++ + /* System CCS needs gpu copy when moving PL_TT -> PL_SYSTEM */ + needs_rpm = (!IS_DGFX(xe) && bo->resource->mem_type != XE_PL_SYSTEM && + xe_bo_needs_ccs_pages(xe_bo)); +diff --git a/drivers/gpu/drm/xe/xe_defaults.h b/drivers/gpu/drm/xe/xe_defaults.h +--- a/drivers/gpu/drm/xe/xe_defaults.h ++++ b/drivers/gpu/drm/xe/xe_defaults.h +@@ -22,5 +22,6 @@ + #define XE_DEFAULT_WEDGED_MODE XE_WEDGED_MODE_UPON_CRITICAL_ERROR + #define XE_DEFAULT_WEDGED_MODE_STR "upon-critical-error" + #define XE_DEFAULT_SVM_NOTIFIER_SIZE 512 ++#define XE_DEFAULT_SHRINK_ACTIVE_MS 1000 + + #endif +diff --git a/drivers/gpu/drm/xe/xe_exec.c b/drivers/gpu/drm/xe/xe_exec.c +--- a/drivers/gpu/drm/xe/xe_exec.c ++++ b/drivers/gpu/drm/xe/xe_exec.c +@@ -344,6 +344,7 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file) + if (!xe_vm_in_lr_mode(vm)) + xe_exec_queue_last_fence_set(q, vm, &job->drm.s_fence->finished); + xe_sched_job_push(job); ++ WRITE_ONCE(vm->last_exec_ns, ktime_get_ns()); + xe_vm_reactivate_rebind(vm); + + if (!err && !xe_vm_in_lr_mode(vm)) { +diff --git a/drivers/gpu/drm/xe/xe_module.c b/drivers/gpu/drm/xe/xe_module.c +--- a/drivers/gpu/drm/xe/xe_module.c ++++ b/drivers/gpu/drm/xe/xe_module.c +@@ -30,6 +30,7 @@ struct xe_modparam xe_modparam = { + #endif + .wedged_mode = XE_DEFAULT_WEDGED_MODE, + .svm_notifier_size = XE_DEFAULT_SVM_NOTIFIER_SIZE, ++ .shrink_active_ms = XE_DEFAULT_SHRINK_ACTIVE_MS, + /* the rest are 0 by default */ + }; + +@@ -37,6 +38,11 @@ module_param_named(svm_notifier_size, xe_modparam.svm_notifier_size, uint, 0600) + MODULE_PARM_DESC(svm_notifier_size, "Set the svm notifier size in MiB, must be power of 2 " + "[default=" __stringify(XE_DEFAULT_SVM_NOTIFIER_SIZE) "]"); + ++module_param_named(shrink_active_ms, xe_modparam.shrink_active_ms, uint, 0600); ++MODULE_PARM_DESC(shrink_active_ms, "Do not back up a buffer object bound to a VM that submitted work " ++ "within this many ms, 0=disabled " ++ "[default=" __stringify(XE_DEFAULT_SHRINK_ACTIVE_MS) "]"); ++ + module_param_named_unsafe(force_execlist, xe_modparam.force_execlist, bool, 0444); + MODULE_PARM_DESC(force_execlist, "Force Execlist submission"); + +diff --git a/drivers/gpu/drm/xe/xe_module.h b/drivers/gpu/drm/xe/xe_module.h +--- a/drivers/gpu/drm/xe/xe_module.h ++++ b/drivers/gpu/drm/xe/xe_module.h +@@ -25,6 +25,7 @@ struct xe_modparam { + #endif + unsigned int wedged_mode; + u32 svm_notifier_size; ++ unsigned int shrink_active_ms; + }; + + extern struct xe_modparam xe_modparam; +diff --git a/drivers/gpu/drm/xe/xe_vm_types.h b/drivers/gpu/drm/xe/xe_vm_types.h +--- a/drivers/gpu/drm/xe/xe_vm_types.h ++++ b/drivers/gpu/drm/xe/xe_vm_types.h +@@ -245,6 +245,8 @@ struct xe_vm { + /** @lru_bulk_move: Bulk LRU move list for this VM's BOs */ + struct ttm_lru_bulk_move lru_bulk_move; + ++ u64 last_exec_ns; ++ + u64 size; + + struct xe_pt *pt_root[XE_MAX_TILES_PER_DEVICE]; diff --git a/pkgbuilds/linux-omarchy-bore/8211-xe-shrinker-skip-active-vm.patch.sig b/pkgbuilds/linux-omarchy-bore/8211-xe-shrinker-skip-active-vm.patch.sig new file mode 100644 index 0000000..0a4ed41 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/8211-xe-shrinker-skip-active-vm.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/8212-xe-shrinker-backup-in-kswapd-only.patch b/pkgbuilds/linux-omarchy-bore/8212-xe-shrinker-backup-in-kswapd-only.patch new file mode 100644 index 0000000..f77d2f2 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/8212-xe-shrinker-backup-in-kswapd-only.patch @@ -0,0 +1,95 @@ +diff --git a/drivers/gpu/drm/xe/xe_defaults.h b/drivers/gpu/drm/xe/xe_defaults.h +--- a/drivers/gpu/drm/xe/xe_defaults.h ++++ b/drivers/gpu/drm/xe/xe_defaults.h +@@ -23,5 +23,6 @@ + #define XE_DEFAULT_WEDGED_MODE_STR "upon-critical-error" + #define XE_DEFAULT_SVM_NOTIFIER_SIZE 512 + #define XE_DEFAULT_SHRINK_ACTIVE_MS 1000 ++#define XE_DEFAULT_SHRINK_DIRECT_BACKUP false + + #endif +diff --git a/drivers/gpu/drm/xe/xe_module.c b/drivers/gpu/drm/xe/xe_module.c +--- a/drivers/gpu/drm/xe/xe_module.c ++++ b/drivers/gpu/drm/xe/xe_module.c +@@ -31,6 +31,7 @@ struct xe_modparam xe_modparam = { + .wedged_mode = XE_DEFAULT_WEDGED_MODE, + .svm_notifier_size = XE_DEFAULT_SVM_NOTIFIER_SIZE, + .shrink_active_ms = XE_DEFAULT_SHRINK_ACTIVE_MS, ++ .shrink_direct_backup = XE_DEFAULT_SHRINK_DIRECT_BACKUP, + /* the rest are 0 by default */ + }; + +@@ -43,6 +44,10 @@ MODULE_PARM_DESC(shrink_active_ms, "Do not back up a buffer object bound to a VM + "within this many ms, 0=disabled " + "[default=" __stringify(XE_DEFAULT_SHRINK_ACTIVE_MS) "]"); + ++module_param_named(shrink_direct_backup, xe_modparam.shrink_direct_backup, bool, 0600); ++MODULE_PARM_DESC(shrink_direct_backup, "Back up buffer objects in direct reclaim too, not only in kswapd " ++ "[default=" __stringify(XE_DEFAULT_SHRINK_DIRECT_BACKUP) "]"); ++ + module_param_named_unsafe(force_execlist, xe_modparam.force_execlist, bool, 0444); + MODULE_PARM_DESC(force_execlist, "Force Execlist submission"); + +diff --git a/drivers/gpu/drm/xe/xe_module.h b/drivers/gpu/drm/xe/xe_module.h +--- a/drivers/gpu/drm/xe/xe_module.h ++++ b/drivers/gpu/drm/xe/xe_module.h +@@ -26,6 +26,7 @@ struct xe_modparam { + unsigned int wedged_mode; + u32 svm_notifier_size; + unsigned int shrink_active_ms; ++ bool shrink_direct_backup; + }; + + extern struct xe_modparam xe_modparam; +diff --git a/drivers/gpu/drm/xe/xe_shrinker.c b/drivers/gpu/drm/xe/xe_shrinker.c +--- a/drivers/gpu/drm/xe/xe_shrinker.c ++++ b/drivers/gpu/drm/xe/xe_shrinker.c +@@ -4,6 +4,7 @@ + */ + + #include ++#include + + #include + #include +@@ -11,6 +12,7 @@ + #include + + #include "xe_bo.h" ++#include "xe_module.h" + #include "xe_pm.h" + #include "xe_shrinker.h" + +@@ -170,9 +172,12 @@ static int xe_shrinker_walk(struct xe_shrinker *shrinker, + return ret; + } + +-static inline bool xe_shrinker_can_backup(const struct shrink_control *sc) ++static inline bool xe_shrinker_can_backup(struct xe_shrinker *shrinker, ++ const struct shrink_control *sc) + { +- return (sc->gfp_mask & __GFP_FS) && !sc->opportunistic_compaction; ++ return (sc->gfp_mask & __GFP_FS) && !sc->opportunistic_compaction && ++ (current_is_kswapd() || READ_ONCE(xe_modparam.shrink_direct_backup) || ++ !try_wait_for_completion(&shrinker->xe->pm_block)); + } + + static unsigned long +@@ -188,7 +193,7 @@ xe_shrinker_count(struct shrinker *shrink, struct shrink_control *sc) + * wants, and it needs __GFP_FS. + */ + read_lock(&shrinker->lock); +- if (xe_shrinker_can_backup(sc)) ++ if (xe_shrinker_can_backup(shrinker, sc)) + num_pages = min_t(unsigned long, + ttm_backup_bytes_avail() >> PAGE_SHIFT, + shrinker->shrinkable_pages); +@@ -241,7 +246,7 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con + }; + bool runtime_pm; + bool purgeable; +- bool can_backup = xe_shrinker_can_backup(sc); ++ bool can_backup = xe_shrinker_can_backup(shrinker, sc); + + nr_to_scan = sc->nr_to_scan; + diff --git a/pkgbuilds/linux-omarchy-bore/8212-xe-shrinker-backup-in-kswapd-only.patch.sig b/pkgbuilds/linux-omarchy-bore/8212-xe-shrinker-backup-in-kswapd-only.patch.sig new file mode 100644 index 0000000..30e13a0 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/8212-xe-shrinker-backup-in-kswapd-only.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/8213-xe-hibernation-writeback-gated.patch b/pkgbuilds/linux-omarchy-bore/8213-xe-hibernation-writeback-gated.patch new file mode 100644 index 0000000..b639c28 --- /dev/null +++ b/pkgbuilds/linux-omarchy-bore/8213-xe-hibernation-writeback-gated.patch @@ -0,0 +1,112 @@ +diff --git a/drivers/gpu/drm/xe/xe_defaults.h b/drivers/gpu/drm/xe/xe_defaults.h +--- a/drivers/gpu/drm/xe/xe_defaults.h ++++ b/drivers/gpu/drm/xe/xe_defaults.h +@@ -24,5 +24,6 @@ + #define XE_DEFAULT_SVM_NOTIFIER_SIZE 512 + #define XE_DEFAULT_SHRINK_ACTIVE_MS 1000 + #define XE_DEFAULT_SHRINK_DIRECT_BACKUP false ++#define XE_DEFAULT_HIBERNATE_WRITEBACK false + + #endif +diff --git a/drivers/gpu/drm/xe/xe_module.c b/drivers/gpu/drm/xe/xe_module.c +--- a/drivers/gpu/drm/xe/xe_module.c ++++ b/drivers/gpu/drm/xe/xe_module.c +@@ -32,6 +32,7 @@ struct xe_modparam xe_modparam = { + .svm_notifier_size = XE_DEFAULT_SVM_NOTIFIER_SIZE, + .shrink_active_ms = XE_DEFAULT_SHRINK_ACTIVE_MS, + .shrink_direct_backup = XE_DEFAULT_SHRINK_DIRECT_BACKUP, ++ .hibernate_writeback = XE_DEFAULT_HIBERNATE_WRITEBACK, + /* the rest are 0 by default */ + }; + +@@ -48,6 +49,10 @@ module_param_named(shrink_direct_backup, xe_modparam.shrink_direct_backup, bool, + MODULE_PARM_DESC(shrink_direct_backup, "Back up buffer objects in direct reclaim too, not only in kswapd " + "[default=" __stringify(XE_DEFAULT_SHRINK_DIRECT_BACKUP) "]"); + ++module_param_named(hibernate_writeback, xe_modparam.hibernate_writeback, bool, 0600); ++MODULE_PARM_DESC(hibernate_writeback, "Write buffer objects to swap before hibernation, " ++ "integrated GPUs only [default=" __stringify(XE_DEFAULT_HIBERNATE_WRITEBACK) "]"); ++ + module_param_named_unsafe(force_execlist, xe_modparam.force_execlist, bool, 0444); + MODULE_PARM_DESC(force_execlist, "Force Execlist submission"); + +diff --git a/drivers/gpu/drm/xe/xe_module.h b/drivers/gpu/drm/xe/xe_module.h +--- a/drivers/gpu/drm/xe/xe_module.h ++++ b/drivers/gpu/drm/xe/xe_module.h +@@ -27,6 +27,7 @@ struct xe_modparam { + u32 svm_notifier_size; + unsigned int shrink_active_ms; + bool shrink_direct_backup; ++ bool hibernate_writeback; + }; + + extern struct xe_modparam xe_modparam; +diff --git a/drivers/gpu/drm/xe/xe_pm.c b/drivers/gpu/drm/xe/xe_pm.c +--- a/drivers/gpu/drm/xe/xe_pm.c ++++ b/drivers/gpu/drm/xe/xe_pm.c +@@ -26,6 +26,7 @@ + #include "xe_pcode.h" + #include "xe_printk.h" + #include "xe_pxp.h" ++#include "xe_shrinker.h" + #include "xe_sriov_vf_ccs.h" + #include "xe_sysctrl.h" + #include "xe_trace.h" +@@ -450,6 +451,9 @@ static int xe_pm_notifier_callback(struct notifier_block *nb, + if (err) + drm_dbg(&xe->drm, "Notifier evict user failed (%d)\n", err); + ++ if (action == PM_HIBERNATION_PREPARE) ++ xe_shrinker_backup_for_hibernation(xe->mem.shrinker); ++ + err = xe_bo_notifier_prepare_all_pinned(xe); + if (err) + drm_dbg(&xe->drm, "Notifier prepare pin failed (%d)\n", err); +diff --git a/drivers/gpu/drm/xe/xe_shrinker.c b/drivers/gpu/drm/xe/xe_shrinker.c +--- a/drivers/gpu/drm/xe/xe_shrinker.c ++++ b/drivers/gpu/drm/xe/xe_shrinker.c +@@ -172,6 +172,34 @@ static int xe_shrinker_walk(struct xe_shrinker *shrinker, + return ret; + } + ++void xe_shrinker_backup_for_hibernation(struct xe_shrinker *shrinker) ++{ ++ struct ttm_operation_ctx ctx = { ++ .interruptible = false, ++ .no_wait_gpu = false, ++ }; ++ struct xe_bo_shrink_flags flags = { ++ .purge = false, ++ .writeback = true, ++ }; ++ unsigned long to_scan, scanned = 0, freed = 0; ++ ktime_t start = ktime_get(); ++ int ret; ++ ++ if (!READ_ONCE(xe_modparam.hibernate_writeback) || IS_DGFX(shrinker->xe) || ++ !ttm_backup_bytes_avail()) ++ return; ++ ++ read_lock(&shrinker->lock); ++ to_scan = shrinker->shrinkable_pages; ++ read_unlock(&shrinker->lock); ++ ++ ret = __xe_shrinker_walk(shrinker, &ctx, flags, to_scan, &scanned, &freed); ++ drm_info(&shrinker->xe->drm, ++ "Hibernation writeback: %lu of %lu pages scanned, %lu freed in %lld ms (%d)\n", ++ scanned, to_scan, freed, ktime_ms_delta(ktime_get(), start), ret); ++} ++ + static inline bool xe_shrinker_can_backup(struct xe_shrinker *shrinker, + const struct shrink_control *sc) + { +diff --git a/drivers/gpu/drm/xe/xe_shrinker.h b/drivers/gpu/drm/xe/xe_shrinker.h +--- a/drivers/gpu/drm/xe/xe_shrinker.h ++++ b/drivers/gpu/drm/xe/xe_shrinker.h +@@ -13,4 +13,6 @@ void xe_shrinker_mod_pages(struct xe_shrinker *shrinker, long shrinkable, long p + + int xe_shrinker_create(struct xe_device *xe); + ++void xe_shrinker_backup_for_hibernation(struct xe_shrinker *shrinker); ++ + #endif diff --git a/pkgbuilds/linux-omarchy-bore/8213-xe-hibernation-writeback-gated.patch.sig b/pkgbuilds/linux-omarchy-bore/8213-xe-hibernation-writeback-gated.patch.sig new file mode 100644 index 0000000..0a27dd0 Binary files /dev/null and b/pkgbuilds/linux-omarchy-bore/8213-xe-hibernation-writeback-gated.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/9999-bpftool-strip-wformat-bootstrap.patch b/pkgbuilds/linux-omarchy-bore/9999-bpftool-strip-wformat-bootstrap.patch deleted file mode 100644 index ff35b72..0000000 --- a/pkgbuilds/linux-omarchy-bore/9999-bpftool-strip-wformat-bootstrap.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile ---- a/tools/bpf/bpftool/Makefile -+++ b/tools/bpf/bpftool/Makefile -@@ -99,7 +99,7 @@ endif - HOST_LDFLAGS := $(LDFLAGS) - - # Remove warnings for libbpf bootstrap build --LIBBPF_BOOTSTRAP_CFLAGS := $(filter-out -W -Wall -Wextra -Wformat -Wformat-signedness,$(HOST_CFLAGS)) -+LIBBPF_BOOTSTRAP_CFLAGS := $(filter-out -W -Wall -Wextra -Wformat%,$(HOST_CFLAGS)) - - INSTALL ?= install - RM ?= rm -f diff --git a/pkgbuilds/linux-omarchy-bore/9999-bpftool-strip-wformat-bootstrap.patch.sig b/pkgbuilds/linux-omarchy-bore/9999-bpftool-strip-wformat-bootstrap.patch.sig deleted file mode 100644 index 7b95eee..0000000 Binary files a/pkgbuilds/linux-omarchy-bore/9999-bpftool-strip-wformat-bootstrap.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy-bore/PKGBUILD b/pkgbuilds/linux-omarchy-bore/PKGBUILD index d9c5b41..7d7d57b 100644 --- a/pkgbuilds/linux-omarchy-bore/PKGBUILD +++ b/pkgbuilds/linux-omarchy-bore/PKGBUILD @@ -2,8 +2,8 @@ # Adopted from the Arch Linux linux package. pkgbase=linux-omarchy-bore -pkgver=7.2.5 -pkgrel=6 +pkgver=7.2.8 +pkgrel=2 pkgdesc='Omarchy Linux (BORE CPU scheduler, ADIOS I/O scheduler)' url='https://omarchy.org' arch=( @@ -36,14 +36,7 @@ options=( !strip ) _major=${pkgver%%.*} -if [[ $pkgver == *rc* ]]; then - _base=${pkgver%rc*} - _prev=${_base%.*}.$(( ${_base##*.} - 1 )) - _srcname=linux-${_prev%.0} - _rcpatch=patch-${pkgver//rc/-rc} -else - _srcname=linux-$pkgver -fi +_srcname=linux-7.2.8 source=( https://cdn.kernel.org/pub/linux/kernel/v${_major}.x/${_srcname}.tar.{xz,sign} ${_rcpatch:+https://cdn.kernel.org/pub/linux/kernel/v${_major}.x/stable-review/${_rcpatch}.xz} @@ -54,95 +47,144 @@ source=( 0120-tlbpull.patch{,.sig} 0121-smp-preempt.patch{,.sig} 0130-sched-detach-tasks.patch{,.sig} - 0131-sched-avg-idle.patch{,.sig} 0140-sched-always-inline.patch{,.sig} 0141-sched-urgent-fixes.patch{,.sig} - 0142-sched-itmt-no-debugfs-dependency.patch{,.sig} 0143-sched-hybrid-cluster-balancing.patch{,.sig} 0144-sched-nohz-idle-core.patch{,.sig} 0150-adios-3.2.0.patch{,.sig} - 0200-idle.patch{,.sig} - 0210-pstate.patch{,.sig} - 0211-amd-pstate-fixes.patch{,.sig} - 0212-amd-pstate-epp-cache.patch{,.sig} + 0210-pm-updates.patch{,.sig} + 0211-pm-fixes-1.patch{,.sig} + 0212-pm-fixes-2.patch{,.sig} + 0213-pm-fixes-3.patch{,.sig} 0220-x86-amd-zen5-tlb-sizes.patch{,.sig} 0250-zsmalloc.patch{,.sig} 0260-mglru-exec-protect.patch{,.sig} 0270-ksm-rmap-walk.patch{,.sig} 0280-mm-updates.patch{,.sig} + 0281-mm-no-deferred-kernel-page-table-free-at-boot.patch{,.sig} 0290-zstd-bmi2-fallback-aliases.patch{,.sig} 0291-zstd-bmi2-cpu-feature-dispatch.patch{,.sig} 0292-crypto-zstd-defer-cstream-init.patch{,.sig} 0293-crypto-zstd-defer-dstream-init.patch{,.sig} 0295-af-alg-restrict.patch{,.sig} - 0296-x86-mm-pmd-modify-keep-dirty-bit.patch{,.sig} 0300-btrfs.patch{,.sig} - 0301-btrfs-fixes.patch{,.sig} + 0301-btrfs-fixes-1.patch{,.sig} 0302-btrfs-zstd-decompress-direct-to-page.patch{,.sig} + 0303-btrfs-fixes-2.patch{,.sig} + 0304-btrfs-fixes-3.patch{,.sig} 0310-fuse-eof-zeroing.patch{,.sig} 0311-fuse-perf.patch{,.sig} 0312-fuse-writethrough-uptodate.patch{,.sig} 0313-fuse-background-wakeup.patch{,.sig} + 0320-vfs-open-consume-path-reference.patch{,.sig} 0350-drm-edid-populate-monitor-range-from-displayid-adaptive-sync.patch{,.sig} 0360-gpu-mem-cgroup.patch{,.sig} - 0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch{,.sig} 0400-drm-i915-alpm-limit-pr-alpm-to-panel-replay.patch{,.sig} - 0401-drm-i915-psr-exit-panel-replay-for-alpm-lag.patch{,.sig} - 0402-psr2-early-transport-panels.patch{,.sig} + 0401-psr2-early-transport-panels.patch{,.sig} + 0402-drm-i915-quirk-panel-replay-exit-on-flush.patch{,.sig} + 0403-drm-i915-quirk-dell-xps14-ptl-panel-replay-exit-on-flush.patch{,.sig} + 0404-drm-i915-quirk-dell-xps16-ptl-panel-replay-exit-on-flush.patch{,.sig} + 0405-drm-i915-quirk-dell-xps13-dx13260-ptl-psr1-only.patch{,.sig} + 0406-drm-i915-quirk-dell-xps13-dx13260-wcl-panel-replay-exit-on-flush.patch{,.sig} 0411-drm-xe-display-no-stolen-framebuffers.patch{,.sig} 0420-safe-window.patch{,.sig} 0430-fbc.patch{,.sig} 0440-xe3-peak-bandwidth.patch{,.sig} + 0442-drm-i915-psr-clear-stale-sel-fetch-bits.patch{,.sig} + 0443-drm-i915-dsb-delayed-vblank-scanline-shift.patch{,.sig} + 0444-drm-i915-vrr-dc-balance-off-by-default.patch{,.sig} 0450-amd-hdmi-vrr-allm.patch{,.sig} 0451-amd-vtem-tmds-links.patch{,.sig} 0452-amd-hdmi-frl-default.patch{,.sig} + 0453-amd-vtem-null-sink.patch{,.sig} + 0454-amd-hdmi-frl-status-polling.patch{,.sig} + 0455-amd-hdmi-frl-cap-on-nondestructive-verify.patch{,.sig} + 0456-amd-dml-frl-compressed-cap-check.patch{,.sig} 0460-vesa-displayid-dsc-bpp.patch{,.sig} 0461-vesa-dsc-passthru-mode-match-fix.patch{,.sig} 0472-amdgpu-userq-post-reset-error.patch{,.sig} - 0473-i915-ptl-cdclk-sanitize.patch{,.sig} 0474-amd-display-oled-vesa-backlight.patch{,.sig} 0475-revert-drm-i915-dp-as-sdp-vrr-or-pr.patch{,.sig} + 0476-amd-display-ycbcr420-only-modes.patch{,.sig} + 0477-amd-display-irq-atomic-rmw.patch{,.sig} + 0478-amd-freesync-vcp-code-check-hdmi-pcon.patch{,.sig} + 0479-amd-hdmi-allm-hf-vsif-in-vsp-infopacket.patch{,.sig} 0510-sound-updates.patch{,.sig} - 0511-sound-updates-fixes.patch{,.sig} - 0512-sound-fixes.patch{,.sig} - 0513-xps13-sof-quirk.patch{,.sig} - 0514-rt766-stream-config-type.patch{,.sig} - 0516-hda-realtek-rog-strix-g733zw-speakers.patch{,.sig} - 0517-asoc-amd-yc-acer-aspire-a314-23p.patch{,.sig} - 0540-media-ipu-bridge-ivsc-no-cvs-lookup.patch{,.sig} + 0511-asoc-updates.patch{,.sig} + 0512-sound-fixes-1.patch{,.sig} + 0513-asoc-fixes-1.patch{,.sig} + 0514-sound-fixes-2.patch{,.sig} + 0515-xps13-sof-quirk.patch{,.sig} + 0516-rt766-stream-config-type.patch{,.sig} + 0517-hda-realtek-rog-strix-g733zw-speakers.patch{,.sig} + 0518-asoc-amd-yc-acer-aspire-a314-23p.patch{,.sig} + 0522-sound-fixes-3.patch{,.sig} + 0523-asoc-fixes-2.patch{,.sig} + 0524-sound-fixes-4.patch{,.sig} + 0525-asoc-fixes-3.patch{,.sig} + 0526-sof-amd-acp7x-suspend-unknown-revision.patch{,.sig} + 0527-sound-fixes-5.patch{,.sig} + 0528-asoc-fixes-4.patch{,.sig} 0541-cvs-nova-lake-acpi-id.patch{,.sig} 0542-media-cvs-wake-irq-without-claiming-gpio.patch{,.sig} - 0560-input.patch{,.sig} + 0543-media-laptop-cameras.patch{,.sig} + 0544-ipu-bridge-lenovo-x9-upside-down.patch{,.sig} + 0545-ipu-bridge-dell-16-premium-da16250-upside-down.patch{,.sig} + 0546-ipu-bridge-samsung-galaxy-book5-pro-360-upside-down.patch{,.sig} + 0547-int3472-imx471-vana-power-enable.patch{,.sig} + 0548-media-cvs-sensor-node-in-bound-callback.patch{,.sig} + 0549-media-imx471-analog-gain-max-960.patch{,.sig} + 0560-input-updates.patch{,.sig} + 0561-input-fixes.patch{,.sig} + 0562-hid-fixes.patch{,.sig} + 0563-input-fixes-2.patch{,.sig} 0565-i2c-asue140d-touchpad-100khz.patch{,.sig} 0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch{,.sig} - 0600-usb4stream-fixes.patch{,.sig} - 0601-usb4stream-busy-poll.patch{,.sig} - 0610-typec-cable-altmode-check.patch{,.sig} + 0601-thunderbolt-updates.patch{,.sig} + 0602-thunderbolt-fixes.patch{,.sig} + 0603-usb-updates.patch{,.sig} + 0604-xhci-fixes.patch{,.sig} + 0605-thunderbolt-pcie-rescan-after-tunnel.patch{,.sig} + 0606-thunderbolt-fixes-2.patch{,.sig} + 0607-usb-fixes.patch{,.sig} 0620-usb-string-sanitize.patch{,.sig} 0650-wireguard-tstamp-type.patch{,.sig} 0660-btusb-mediatek-mt7922-13d3-3625.patch{,.sig} - 0661-rtw89-command-offload-source.patch{,.sig} 0662-iwlwifi-mld-skip-tx-when-firmware-dead.patch{,.sig} + 0663-btintel-pcie-serialize-reset-type.patch{,.sig} + 0664-btintel-pcie-stale-cache-in-dxstate-fallback.patch{,.sig} + 0665-btintel-pcie-pm-flow-s0ix-s3-s4.patch{,.sig} + 0666-btintel-pcie-verify-serialize-dstate.patch{,.sig} + 0667-btintel-pcie-gp0-cause-w1c-write.patch{,.sig} + 0668-btintel-pcie-dstate-races.patch{,.sig} + 0669-btintel-version-tlv-parse-errors.patch{,.sig} + 0670-bluetooth-fixes.patch{,.sig} 0700-pci-target-speed-quirk.patch{,.sig} - 0750-applesmc-cache-race.patch{,.sig} - 0751-applesmc-key-backlight-workqueue-leak.patch{,.sig} + 0701-pci-amd-enhanced-atomics.patch{,.sig} + 0702-ahci-jmb585-32bit-dma-revert.patch{,.sig} + 0703-pci-vmd-scan-bios-enumerated-devices.patch{,.sig} + 0704-pci-vmd-meteor-lake-mtl016-flush.patch{,.sig} + 0705-pci-toshiba-ahci-ssd-dma-alias-quirk.patch{,.sig} + 0740-hwmon-updates.patch{,.sig} + 0741-hwmon-fixes-1.patch{,.sig} + 0742-hwmon-fixes-2.patch{,.sig} 0770-acpi-pm-acer-swift3-sf314-56g-power-resource.patch{,.sig} 0800-platform-updates.patch{,.sig} 0801-amd-pmf-util-unbind-use-after-free.patch{,.sig} + 0802-platform-fixes.patch{,.sig} 0850-futex-wait-multiple.patch{,.sig} 0851-futex-wait-multiple-fixes.patch{,.sig} 0852-futex-wait-multiple-abi-fixes.patch{,.sig} - 8201-xe-shrinker-return-freed-page-count.patch{,.sig} - 8202-xe-shrinker-runtime-pm-for-non-system-memory.patch{,.sig} - 8203-xe-shrinker-release-through-the-put-helper.patch{,.sig} + 0860-ftrace-module-enable-sorted-symbol-search.patch{,.sig} 8204-mm-opportunistic-compaction.patch{,.sig} 8205-mm-hint-uses-allocation-order.patch{,.sig} 8206-mm-carry-order-and-hint-in-one-word.patch{,.sig} 8207-mm-classify-huge-page-allocations-as-failable.patch{,.sig} - 8208-mm-thp-deferred-split-uses-hint.patch{,.sig} 8209-xe-shrinker-use-opportunistic-hint.patch{,.sig} 8210-xe-shrinker-single-backup-decision.patch{,.sig} - 9999-bpftool-strip-wformat-bootstrap.patch{,.sig} + 8211-xe-shrinker-skip-active-vm.patch{,.sig} + 8212-xe-shrinker-backup-in-kswapd-only.patch{,.sig} + 8213-xe-hibernation-writeback-gated.patch{,.sig} ) source_x86_64=(config.x86_64) validpgpkeys=( @@ -150,41 +192,37 @@ validpgpkeys=( 647F28654894E3BD457199BE38DBBDC86092693E # Greg Kroah-Hartman 12D27D5D8C8E9BF1AABC7C7C7C64768D3DE334E7 # Krzysztof Wilczynski ) -b2sums=('48551bee71cd02815136fb8abe7da4464c2e17c89ef35cb0c0530c8b969fe12127ca97ab6c656ea8e5b29f2c4a8fe4cc143a626f4cca6b972a8105991e4c6905' +b2sums=('5326dde778eb945f282740ef0d8765b46fbd198f1209cdfd7b91e5dac1312fc01f8bf0b2bc67b16a76de736edd8ea21b38b6c91e696ec0c4b39746cd21a15214' 'SKIP' - '6d92fb81077232b8cd1ae500b3aabc71434792e750ea6126dc095e50d4278d3e42b3cfdc3c8bc693ea14b109f6adfa3a1ed095187fc5501962fbe8f049f867bc' + '60eac409f1ba50fe319f6b3aa5b0af81bc7ce5628ca41c1af346a6d5ef0fc9c577527742d1f70ece7e07ea410fcd112b32d64f0a05af5b532f27b62e5e7d837a' 'SKIP' 'cc2a66a097b5567e80c59b7d6c492fef5e667fc71a0390908712c69512c4136dbaf3d150b62d04eff960c796f7a94e6d0915d3b92cb336318b49f5f516a3bba6' 'SKIP' - 'a32edb39b4ee9c0378239f4998f477fe5371e6931c188d660874b360ca6d3bdcfff71cf088bed071365627d87458eca0be625e154ca3d1e8150e5e0bdf64175c' + '9bdc9b93b30431d1b788f8a44cbf7e9030b8ff2b4e0cef9ceb7b8405b497eab1526567f840b0fc643e25626312322fcb008fcfd914363d42e3d3bc3eea6ed6ab' 'SKIP' - 'f6c00ac2400580dffe3605d2693809396e18185f0c59b21b86f1f22a1f8d6529c51e8cc2474bc40f74a96631f415da51521a8b35d5b096e9b9d164c2755fb091' + 'c5a06c5632ba1eeb592f19b7f1581acfb7adf603b098d1d966359a1bb266a330a5707b64aa1f0e0346003e0b3008f46d8af669b621ccacd9f219838fb5e122bc' 'SKIP' '4651980c9d988ed73dc91fd440467de0a1de21efa3efae0fc006524c88af40173182ce6826c212e8b7faaaae1aa512fc755272ec4663c185533e65c936780db3' 'SKIP' - '57f88a64a8ec20794c008c9de70b3f01680ce9023925df8e0f2c7be69f2600e10276d54bd264f5b8b258e04d38cf483cbf83bb79df466f17fe4b6b33fec52a76' + 'c974858b6e01c8db72700a472722d88820efb8b784cb2facb80c5babc8e93f00621b954ceafa22033b9a8cd61a981afcff4a2f759cc4f66ece013a71ce1572d7' 'SKIP' - '050d38240747325e90bb86bb7c47a14573dcb297ee323b3d84f74df9ddf3691cdf32ac9e293bd237c9b09221065b002e297745f4b898a937b9347dacb2de7109' + 'a55d203cef25122f29ae651a8011f15bb3210bb959b459c521dd7d18d3d65a79594196ba3eafffc223114eb640cb41e8c36310600ff451b3cd7d6b852ba6307a' 'SKIP' - 'd21fa7cb609188720365a8d95a98c3286f2f32a772309e66913c6b16bcf871ed28ea1ff01fc15ecbb47a02def1a557512126e39ac0daa956a1ba89878acf5de5' + '28179167d761963a2a988e1aaf2f2aa0724aed94ad6f79232701f2e13d5c5e0a4a8396e2b8c5a56bdd91a5a325f62ad22a14370ab1f717baaeb913abbee2d6eb' 'SKIP' - 'cd744c8f861d2cfdfdc98ea6875419a298dcb5d478444b8e551a52fe7ce5279e2d977f8610783d79144be4927a8bf41b2f80113e6f7aa3ea02f54b9984446017' + 'fc6deeaa32b42416a5f5c92ee507aaead8d088ea9a5795848c96ce58c3d745d1b1f101916ded9d7163b52d3d705e35b5de9a1a8e910fd4cb100a37f53444b6b2' 'SKIP' - '3434a5bbcd2bd8b4e2013400bf4d771ad5c8295ec74f67ff7acaf917954a01d25931679a71ddb8995fd7703b0b9904db4e6fe5f384d3705cf4d7b80dbf5dba55' - 'SKIP' - 'c8b8bb1ec676378d862f64b23445f170f2e995f3d89f4ffc727c377128ace87417e50d58c39e62d62795924ab4c62f3defad64139e3d3b472b75f6431484e2dd' - 'SKIP' - 'c9c1be8f72cef610592b1815a81a34706b54f2ef4a188acbd31d98243129e2939fe0c2b679f428a67eac70b069c8a662c3629c598f89430930f5b8bb602aada8' + '775d7f30c8eaa56989793050ed0e856fae3cfbc698681004f5b12945d90802c46fcf09a400fd2ee1af983ac2987d4dfc771e4542be69ec38352538afc09d07ee' 'SKIP' '4f58fc77bc13fe9492fe4662e0a4b879a8a1616e271c2095959dccdffed9b0a866a4eb9dc3ac74fad9eed11c362e43a7bfa56d8ea6f571572a63ff4550c5dc23' 'SKIP' - 'd1007ee452cdf92967ec7055f1f39a3007a69842040e12f5063a1d2f24fd6d1eeaff2357abf96d0e330541a4781f93fecbcb116a4c73371913582eb72b8f9466' + '921a4b6ca25fb1a10588d7a3df65277cf6acf1115e618741486d230bd8eee16daef3f712518f98c8a49222aed468e706015eb238ba289769a464f5f33a21b21e' 'SKIP' - '0d3309140e496ad552d1a15d4fbf04b5ad03877bc73ae689f57554e91b7458578f2c88b6bbd37dd9fdf822a8d48c2cd6f961ec735ae5887f96fa51d908d203ee' + '486987a57ff24ae8c58953c01db6c8e4f4eca4093a8484b28b9a647790a6757a381631e13d64605bb009613985b686a0f98ef58b2e2f9c9ddc5b4d0de1c62de1' 'SKIP' - '13beae6f8d23a1f075410f50c09bc94f24f505571af1feb3cee58f732c0d649a9ce5939bc11642c4f3c8dfbd967939b2a0a205220abf06f10d559c4c43a946a3' + 'e2d21e10be9239260aaf41ec5584885555d3d5150a817fdc2b04001444ae07cebefd140b20e841c0988843749e850ad52f93b98d005d5b158e158454c2020691' 'SKIP' - '2f24f88d8f960148046c36222fdaaadfed2ca88c60240fa1d111430898192ca45aeaa93a3afa24ad02b11c4dc8c3904a92f17c0284396f2f43352ad934a35056' + 'cd6d3232202e8e6a44e80783becc1b527336027420e308f354b32b6de1f83de210aac78ee0c639ba23030b4a0811cf7728bcaeed08e9dbe11caf3edff380ed6c' 'SKIP' 'e59c4da548b2ea4fd6144886e3e2e765b3e73972121e9e73dbc05c066834b2b35e6ef4c17617e77efc90260566bf7c0910d2138d8d5c1c3b6094eeac9de64cfe' 'SKIP' @@ -194,7 +232,9 @@ b2sums=('48551bee71cd02815136fb8abe7da4464c2e17c89ef35cb0c0530c8b969fe12127ca97a 'SKIP' '31abdd15ae3f9709d6912c90319a0b62d8380e1ae1169575c0e8e419f8ce264a7cf7c72558a85719cd5a1cae7341007adb3e3936268999ebd5215c2ed288f22c' 'SKIP' - '4efb9ab5258aee01b07bc322b160d2f6e3571ff82c4f3e317cab152a919c67522629d56fb79d517da034373cd6924bc3a20c98073197c93590feb1a34e902358' + 'f95af54bcfaf17e80d9c0f60243d946aff2bbb796ae47c92a97a353f598483f03ca87fccefbaa47639b1018b82e7879dae4b8759e8fd9206b33d219560d682a7' + 'SKIP' + 'ff9a3e5df8457880b25caef9de90a7eb513ad8de99296ed503268749a32d8fa79da11f598a5fd80d28231abdd65a247e2aa92ce7c8ccc9f497fa483eee93aa82' 'SKIP' '55951c59286df43f069302b0b1036424812f80b45c5ea13219fce1bfc3e831731432c216288d722e44493c736cbf89a3e44801b5a4f1f242c17b5a89b236a5ef' 'SKIP' @@ -206,65 +246,99 @@ b2sums=('48551bee71cd02815136fb8abe7da4464c2e17c89ef35cb0c0530c8b969fe12127ca97a 'SKIP' 'ad2cfdc3d8de2193110325395e11015577f26ef1081307bb236ed73fbd109822dd44ea026a57c2e17233e1fb7982e78aa986b71c043f42887e6c5d7f51b84ea9' 'SKIP' - '19c3dd32bbe3ed7caec86e90a6af39b07eb99d5a36515cf061bb6979e5a403ca98a0e00e584a9f746330d663e5fc0868ba7fc98ab2df264bc863bdf22ef3dd5e' + '92cadbb0f7c288987838534f523f85a67d07db19ce27fd6c8717f73c02f7ec7b7b47276fea8b2a86593178dbc4f44e774727050f2b46baafdb7296028fb24a0f' 'SKIP' - 'ed2ee4849b715d80ba103fe090669062fc08c6864c64e8f8291f7f21092f0184ca5528bdd3a9318f077ef00cbe8cf28bacd1e7a9cb9f9ae1047becd930a0c42e' - 'SKIP' - 'f09e80ed25fdda9b2c79bd5ea3701985596d478b0b4e6948a1ea8fb789ef944e71640ae200442afcc23a9f0e07b34d83365e223b620baf776bbd864977e633a0' + '0b281a5780efd064a6c98a08a41eadb1286a2aa5a7f1bab33d3adaa82713f4676bd3acf07556f3617ab0649f95fa5ce3a0418da1b6ae48cb580f0d7d5d9752b3' 'SKIP' 'a51e5f2a5e37c350fb4d715ece644c565a508970c3aeb1d28cb6cd03a0c7316a1935e028b64f20a03818926c611b50ae0ffd77d04c676d604d303beed0f96e1f' 'SKIP' - '25dcd2c4a4287f26c01419b7d6430629d2827b6efff473361c68d6ec44e94e6d2bb8ab8215dbae5da25781a668f571cfc28b60c5693eb4ed629bf80fe2f68ccf' + '71e8a5d91d285519367ba73241d01adc3b61a2b7b21f8061ecc3749fbf9d87f5ca414809343fd37530bfd5226f919cc3d87dc3b4034cc5b8be9c720f8b4676b2' 'SKIP' - '8d6eeb2330a3ca7c522cef77bf7336fac9444a0884d5d101c933abd3a31a3711529a31f9a181d4795148ecbcc24425750587314ce28e44c9ab2fd2de8365e853' + '64ca5b3c45b579c51c04f1177c8e6eb25db4c76f17f7b253c5ed959c9688bafaa97da701257435b34338432e88071f04a8d4049ba66895dd3ba804173cb08b28' + 'SKIP' + '4dfbcdb23d7117b75202944547f7ce956dd8fa7405bc5ca4856135e2becc21d950eb6e1aaec082da4290ac6cffa402686c2c9137d4dc23952796ce3334b95ff7' + 'SKIP' + '51a7a4f87d53e4b3d25041a0536b238d4550fcfdef0e7de5aa75c26a7a9217f6f3e260670154b9fa6674470f72e5e09c76571e6224d8be4eb5dd4383fef64a55' 'SKIP' 'fd487cb02212ffdae5d8beb7d895819377686085b95658dcf597a534b017adcb65dfdf06e0276e7322a46dc28118803b05c246c22e7d3c361f1778ed9e12a3c6' 'SKIP' - 'e18e5086c531a37837f6e7df72ce10a38289b02de4cea376772d45cccb5aabc628fe4f3f9607f646903a7cf59b02692703e6b26800604cf958136987df0b4d5d' + '2c58614d716943cc1883dc513c2206e82f49574eca79f3e7dde21f8dd0f02f18611b2b2a444745df820c30c887d8263bdf9786a4a90b7979efee8d86633cbb88' + 'SKIP' + '218d01002d5487e2cb29f479f9ef9a2beb8226d2adccd1a9f932dc162a8502003db19b1bf74395b0239a2a15888131bcdc969b737cf56358dac6ab14d1b4501d' 'SKIP' '1b340a502dcf5a1680caf5320b3aa72aa4317e4cf1d877c1f6425e281a5668d5f092417e1f282371c0a211344f5c187fb3bdaf3f97c7e122951edf7e4c08ca58' 'SKIP' '1124c5c3fd5104c886c841ead1ae9afb42683388b640b1a86523dad9919c6bd59a1f78b07ee5a213e0dca77c636a29676af0592d4ed1735ea35f282f6f23e46f' 'SKIP' - '15855e9c28cb0abaab7ab606904dfc84d426ccab44edae59f0e1f092202b80525929f8d394d04ca1513d1d5dfc2cfc108a66a9c1cb42b9966c6417562ef20689' - 'SKIP' '505bf01a9d6b0926b557b673bb686d1a4ff73ee70539991e52272fbf58aad3ea25cf5ffc5c2d094a2d5e78b18a1a134f2ab530f595073a2d3eba9ff2c1418e1e' 'SKIP' - 'b33b46c37f12f7650a67c3731b87ff84add1ac8623cc96b52223a1ef9946bcaa990ed7c7036674e228335f57ec9ed421ea4d6c1dd72d19b516215d9ac2c7c3ee' - 'SKIP' 'b4bf4c4c0672bad91d7f8d309e741104918dd8ee8688a00d600379eba5828d1b8157953377c73d0b58050b4ff888c43013ede4e9c959be8d9c028a6143bf58ed' 'SKIP' + 'a5ae2f6c29ba850292fcd1cd39d7af6041169ad9d73151d8fcc07ec2e1da473f3ac801eda892082b615d7f19c2a3ac17bceeb1d100dc1e2ff4b30287bbd8fc8b' + 'SKIP' + '3b8e01822f9f97842c5f7d7211df876d46d8477321546bd5b8cb74484550a2c520ffe351ce0ebb7df5659d2745187081883c091574c855ae7361315d153bc557' + 'SKIP' + '90e189032c6a04ca809607a5c9a8e798ff2f3dfa7d82bb510eb608da961196bb4b53f35907bfa9ce4006fe182102900bfa20f4566b146aaddaace4fc19adc305' + 'SKIP' + 'b9f60ea91694fb859543cf895ac9d654edd0aad92a3bce6ba9cfcfc450f413e49bc8cdb08adc6fba1a46ef319c10cdc501e3ffe6b37de222b11cd1c6cf138db9' + 'SKIP' + 'f80573494f330b31af70f1a079100082e00e3c7e185bbc51928d09d2ad3ccabb3134c72a0edcef994d6cc517015190d88c41301cf73c73419c594977f86a2906' + 'SKIP' 'e403bdc300284c4f0f4302e501b8ccfe5e9af04e73a6070eb6615322f5f40ed6edad53af2ed278741f61a4e3811b92faf1af02f113dbcc2a19bde335a55930d7' 'SKIP' '5de29d0eb5264e24ab9228bea2ea4b549b3fe37ea67c045df9f83064f11c2f557b9641e772d28db59f8d258418c77435b66ff471c3695043f0f8275d5e7b1685' 'SKIP' - '3c2cd960103ad97ce35679f906b3c9be79a9744959c5dfd0ea338d8075b698790934694b2f71fde026b164c5246cba71ffe3cd5dfc1464da338136fbbaea2f1c' + '25a4ec87d1c789cab55e8ee6357276eb2e87b5e70eba2cd978b2c898e7b79de3fbc8cf5997fcd660998223b6173c98e440c853ad7c3e116f52a48d38622dfb46' 'SKIP' '2b33b11e48d8dd8d6743f26170fb99eae38541e3665e2448d91cedf729cca7c0278bfba1078ecbe88201eac4407ec5bd0292a97dbab9cfc67e42646712befc1f' 'SKIP' - '71934436987ecf6914e5c559445c2b7ee92a31c467552342094c93a59d32563aa9930e710556cc1e271eeb352705a9df9b3702482f87d045d132233cee4ab48b' + '5ef0d9561a570941752fd1976a2929eb25fb4dc16ff7fad09ce71e0d6881924f1ba8839574f7f877bd8089d116d689e33a54c14e2f177fe797dc86b305a74c0f' 'SKIP' - '9fb1318a3a181644c67b2adcac0cb04d1b88dd0e632751967239d4e8d4698f193795d00194b0f68505964c84572b67168dcbed02ad5bb7abd41e406a62111489' + '0415549ab1bf3ef0f2af4e62130b66e80d31dd3a1d1acda1cfcc2e4c926f25cf9449153736216a564cbd0710c0f0f1294607bf27c26790bad16e0f8956127e32' + 'SKIP' + 'a0a17aa48cdbf60db7a6d3677fae8a3388757b0f1de71043d8eb93028710f5a26aa57eb2011199b84e9689f7e4457843b74a5731c7dc14e4d1fca44b957bf33e' + 'SKIP' + 'ff1f07b3ad829d444b92c93fa63ab2c08277fa512483806d119a4cc75efbbcfc197dc982d9a146b7e9b94a9ca5bac95eed54735f607d7715cd52444ff0039e30' + 'SKIP' + 'a5670a422ae3d084f20800a5ca2c5615efaef4cdc58e466ceb25a8c56597e592cbf3eea7f4b8b28f1e7c79e9ecc7ff29c284d7be41de26fc6291df020fdd3d33' 'SKIP' '353b7e89a7044b7426db730d5b900eb347907aa0c20e307d2317925d6021a969a343649ede7a32e698dad60dd4472eb197c9578fb0e36fc354a60266fd680cd7' 'SKIP' - '803223224f22aca25dfe34d73e2bd49438f0cfa83b71baa545fa6fe93ee1a631b6b62c7324dc1c0d2d2ded94c36185a83af39b4f4a3362f554a1943ec2e2ec3e' + 'd70e2f90fd0630f53b9680a1a935294d0a9e86e52ef9a712905a7799827ced2ff5acfa27a51980cf6f8157d60d9831cc0d62c623f98cf577810a4cab02b334fe' 'SKIP' - 'b0a7ab192066c472f3ebc39bc9afc9fef0528ff7c98bf67c5b9cb679cc6d93eab3d25e91d8d74c9c52f72803949a3ade7940e677b9a3114605b966df94da93bb' + '88a071e8e20be14db31253152064a26961b74c329793b7fd0c81b629973bd39040e9a66189e3dfda31ecc189f82e65781b310603f498e13456b8090662f80b0a' + 'SKIP' + '0cdfeb8a469295f0d7afc27800f5109f5c88c4afda93056d4c4fc9601988334c32d287e1490966ae21bbe80c5e4f8b8da2cdb6a019ec72682521972840247b8c' + 'SKIP' + 'acc6611ef0605b18b3326e2170f32c3daad02b6472b4fd8fd2a8ba4f99a35b319f2d67b6f1f017e9beb6e8cdb8edc081e97382f3cbe7999d8ad57bd5e929a9bb' + 'SKIP' + '29f44ba6256ca05c8f40f0f1b0e813a5b3928f23e37a23cef9e7e148832f41d8ab4ec43b398ef21a06cc8588e4feee24fbf1f3ec4428ef1b22b1d43a776022c8' + 'SKIP' + 'f3437588231ac856c45eee210b6d1e4f31019480665ae31f62e0fcc4b9931cea4bdcd2bb95345cb9dea26dba43931ccc73e9c9ed209ab3281a1618378658ec0d' 'SKIP' '824089f9f45e2ff9e788d874bb9294b9208b4486fd22c56cb59d4d88cb996ff5a81ac22f8ff136ddffa1e40330f02e53ab759ffea7e4535c11d4f8195069966b' 'SKIP' - '696309582c56fad530a0bf2be12bb744dedac11ed69642b14eade0a26baf1ce52b36074b89b963b40c9c6fbc4857cdc71b2a6c6d64afcbb2df716bbb57579986' - 'SKIP' - '14212fca051d3e9a85f75616ff7f95bbd119ef8e5400dafd4447f70db2c4e9c03d971ec4fdfb4db2319d2613062d74f54dfd5d0a100ab0fa27ac6c05e16bd063' + '22e8b6835a86ad485e5b23ba73a4e1515242b06f42063c99d0dda1f9248ce222e003acd43a517dced198a28bbd1812ce5faf66fa589717734558783fa385bb13' 'SKIP' '7228e249d25a99f5106040b8ddb5705979b3a5af21ca60d0d43a9ecaf6eaccef6bde8ea69c896cffbf2b5963ec4f68987e69b184a8885c7f4c98d2b05fd59fbb' 'SKIP' - 'a54ac1c6d43fe88dee08884ed2e5bad0a0b6d11f34a46dc9801e675860afb7641b57384248a144a621e66a73541d73da11f71671116f1cc7f21534526411439a' + '4d5c5e2fbb5d8fe50a899bcfc629645d1bc9ef995c76611ecb38d25070552487d53ea59cb48bb3922d9d9965cc1f7d37ceea4f2ec789ed1bf37d69b831dea6bf' 'SKIP' - '2e486cabad0d45aeb760d031c7946e9887c710b29dbe9c16e5f4bae6a230031dba4d817f1ee25a0bbca849d1c71b2f84052ffb73c4a6c4350a6ea5ee83c5a40e' + '47015b9baebb1d6dd2a68321980c1c97da097825966f480c8569bcc9df5bef0257c51d5f7398960f28016258eaf1fff25bf142a64a01b829e882b4c1dda10612' 'SKIP' - '23e2a5def526fa886d1f1b05607bfe6b1ecbb2d6009d023145adff9eadc3135f9078faca6638966b586e81e4ab9d48d23b5758504d5a043d5f46e7a701d185fa' + '4f3df2be1b52573ef4299934896dd5b4a5b9ebfa1f69c92f125edee1aa2db2f99bd2e6cf67734e1c953e9413bc6936067e8ac619cad66a5fe8dbcfcfc7906b83' + 'SKIP' + 'ee01ba98e614695154ba62789874843e48ce2a4715b3e6ca0304da0526f705672bb29253e0c52dc8f8fd3148c072067232ae6f4e86315dfc27b1563073cb92eb' + 'SKIP' + '0963ee814b6f64903d67757c5c55a93ec1f5b16374af16aeff2e7e494254d19031d4e1422428f4f7a8167592b7750a1db87476871eeb5cba851b377e59911149' + 'SKIP' + 'dcb350ef2987b801e481b5cf32cdfbe47018939f6b43e45b6dcff87eb75cc1a48f25e679caa823f3fe6391048f216ed9d95ae79b9cac237041857458f56e7f7a' + 'SKIP' + '0e4156949673d16caa6ee1405bf33d432ce2632e433d0abea2f4e9bc5b413500573ca06c10b8dfb477d2a6c7b4201f66ebe81ffb05302e3771dd2f0c515fc5da' + 'SKIP' + '9576c977d2c95cc474e84ac8dd7683a71df38d1d6e1ad80065cfa78db8185c7a52bb408af9881a71767a1c7e3f011424a180d0cba574b14ea53f65a567784181' + 'SKIP' + '4834e1cc74f3902e8e88eb28f9fb1f2bc3cdcda6745083092e7fc39c131bb0b1a8b06d74e6112e5d4c97d14e8b56302096691005474e6e5d2720da02463246a9' 'SKIP' '5d82f32f7c04b084530722b9848254aac289c2a754a10e16821083a871ec14065dd9ee83e72b8e9e8ba2915f3fbaf17b0874df724002eb08a5f4f84d1b6aecd8' 'SKIP' @@ -274,112 +348,174 @@ b2sums=('48551bee71cd02815136fb8abe7da4464c2e17c89ef35cb0c0530c8b969fe12127ca97a 'SKIP' 'eb79554ef0d014b94bc72d4d559c2cc5419df96486acb3b6a09d5e8e9dc48db626ce0f90338c67fe873b2cc3c45c3f308c8080532b60f74a07c9e39e01d34197' 'SKIP' - 'f657143cdd9c937d78315f4617392e60f7c46f5cec882e060f740f77a27dd4c045878cb66e568a6d9ae74e9c706ffb3052817db6f327c87b85ff2f46eea678e2' + '89482f70ea83fde5a88554f54c866d11c98baab1cbcb7fa6fe0079187821736b7ad9896246cb3976ec9d471fc0ebcec6dab32b95b4928704e23e3f4ff935f221' 'SKIP' - '0acbd87a09a62af860d14f342d61c82c29ec1711038d4abf4ae473df187fbda6aa49386d88928c5dc4c3379e097478b1d40d9ebe9360e64d02e59161164b2a3f' + '31202d94636ee8a62214952d0d54eb12b8e1e264edd20c374678dd442d5fc9f8b3ffa2e94cdb6d27c58004a660823d62ffe11aa81decb5af166cc6912486b9cc' + 'SKIP' + 'c0551a31a3c63b70966c9f87eb852cbbf43a13f649bb14b6fd2e8ca23f7fb311ba55d94ddb589a78a432eb225cd2336e553d399787071cee9933f2078fe8b5ad' + 'SKIP' + 'e6f6460dd43c5a7e4d943e6545479438286712237c9feec357317b1431aa778684132b734c5c113f1e57180de366411542c29f62507095e7db62e2d9aa6ad9a6' + 'SKIP' + '58899f7c2bb14ee442c6cfedb9a354f729c03cefd325cfe71da23f33d5a548f6083d5ad922231108fb58ee00f7ef2a2d45ce58c5767079e3ae3efaff306924d8' + 'SKIP' + 'acfcf80889e146bde867b12a7c1b4a7b88a71c09603d58974b647d8066cc281b300b7a43c0e1820f22fafb5f7056522091b9c6c0072ae6a4835e603578c5f95f' + 'SKIP' + 'c446acd1c5e498f0972febf76bf6198b4ee72c6e3e4ef285862564f2467d7ad7ad74eee88191fe1fd887a90a19ad61a3945ed9ee22a2ff6902fc5a806788b151' + 'SKIP' + '8bf8f181ffbd249b0af3a8fcc8463d84daa3c4aeb567595065c311f29b2dec2344fdd240d30314eae2c3d4a4daac53dad34e8452df11e386ce0a028267b18c0d' 'SKIP' 'bb06066e90132f0172333d46be54ca816eb7d3230a193a6cae765d8edc50df4a6ba9fb7c0647cd6befc8c3bbbe1d96c285ee202812477d7295c1130ea8cdd6f4' 'SKIP' - 'f1a94986b4b3f310fecf981e3619f9787a0c8cb0b4fb3f39e5fe85ee3400e7e50278d41bf5468f65e5bd890358d2aabe28d3e11ee838ccbda2ac7af16be568d9' + '788488e828ac47a55ab78ad43251f7a2e5ad15bd5b5468bb31d4ee31e5de01a79b5680d5b048cb123eb3b7bde75e6b3d1e366e745009ed4129c8a6585d92b6b1' + 'SKIP' + '3bdf369bc73fad9bcf5b866043ff007365302318f42de7107fee47a7e8404c02c9c8fb5762d07ef10aebd2eb815c5483fc4960b7936e30ea5aa969a55f392474' + 'SKIP' + 'bdc66e50a9ef697e3baeee768f0f2dc56485afeb8a4e9f9ffd45f183d9614c33e0d0e72a8babca87963d1d9d701148eb41990730f2215a1c40e0965cc7f05e78' + 'SKIP' + 'a22e13b9bf0eac5d4190fd42bfde72ad0830cdbde8c0d309b2dfe9b0151e4c85f9f14f595ba3f36af479173ba1274f7700293c3d61dd913f711196a2dae8d6dd' + 'SKIP' + 'ebf13eb83afc051e3b9c84bfff7b326d73de6bac03f5b4d5996485d06a9cc87aca795b5c029646f804d62314a97e4a2b5da0b5f72f71556f33b62fd26529797e' + 'SKIP' + '8c6b5b96a3992c7cfbe50384fc0a4504e6d522c6918739f822b5bab29066eaeee7d1cc525b41a9b5b354ce020b1837e88d9bb940f3d2ba55a034aa9a845d489e' + 'SKIP' + 'aa24ab789db9badbd38db52f1e01fbc9d5abc09d812e2426ff281c869bbc0923cd903842d0c10ed8cf0e1a4695d3508b8ee4650f6a3eac3655247830aebb2c0b' + 'SKIP' + '1cf95a50e985ac37abd0faf86f4138e510cfb33af0ec172a2ca63f9252e6e4cba2f6c43a0babb11a96973bef3b7bf309a0f22ede26dc437673fcc77afef7fc31' + 'SKIP' + 'd5ab07d8a1db9191fc3b0d1f44c0237e93d662fb8bf04dd55385d8cded9589f66c8212a4d37c651c505fcfcb4a26da072c0be71d97fa0774b4724ba925a7af8e' + 'SKIP' + '7da8c7cd405efee8b37aff9916215b2d7052850f734b693656d2b66bae485661484efadc7207fa00dca5aa02d5e1bbaec2df9abc3e583f280097a939fc32096b' + 'SKIP' + '36fa6403c6519e064972afa0905cbff55f88fb89cad7582a89079f657537458da3d93deae474e33e018928d03090e1a373c6246b180d79f43366e16ccee533f7' 'SKIP' '44dcaad2ab326ebee62d3e63207a161dcb1b63bdb21412b5624ca7875bb6e99997528683a633d7dd526dc5ae9408e73bd066e2a1e5b34ba5c829162df2265346' 'SKIP' - '162a2c2104d5f036657ace6eef2ac2d533c0934376fab2521ff1d3fccd704e3d08fa4f6162e9100a844db2aeb57fa3ce214b8058bd227f38b058ab7606ee7816' + '69c1d61ffec58e2d3058d9e9e5fda57a089397d5ad451224f75f1cc2816c7f3fbd650b31bfb5911157fd364f77cb6f720e0825ae3deed8fe9f03576b8f7f1b31' 'SKIP' - 'd50d5a69087a5ba6c416a6391232764c8281a8094645175fdb2cfc1bb011e33335ae4e96d79364681bea61edcb58ba9acb491e08c301d0667481759d0ee1e183' + 'e3d60abe9fbfa046409598588dd23a9b16439474595136526e3169ff47982e011ede263d403211f5f5692ac43cf88af5bbf0d11b8e7e959478305c67687c525d' 'SKIP' - '539b1d0e764f0ace3e572ac49f20a790183155ddc0bcd3a8d040f44c9891dd36b1a0accb8c0288267afbd33e6ceadf6fc3a5e3db40491c695e0c1d3dd64af40c' + '1fb895cb1deb1b227515087e12fb0f7fab70b0db25a1a535fa3c294a5455af4d987d02c7c8ff24aed41359055d1383ea90bf9709999382610ed156dbc4069086' 'SKIP' - 'f5cae6eabf2066d5deef737a15967d5d11340de34b181f3b3d841023507fc58af2b68926eb04bb342e1adb857165400f862f2b35ffec4204a107b3b22a1571e7' + 'a2fb510d537b9afe8719334e5578a79a8ce7b021ff76c59830c749dbd963c38b7dfb60a61422b88f76886ccffb5ad5cb456b307a14ed68a5c86981eefd4fe3d0' + 'SKIP' + '5ecc37fe83f8dad00a82aa40a0c75d7518d6458b2f22d77820a28a34d772f1255cdb2befa5e525344d5deabac8f5dd092434a9ee2a9c27ccca5c5c231790c017' + 'SKIP' + 'bf138fd2ba2618ae9ff64838706c2212b478513e04b54ca3bdd8bf97292a8a310d73e0cd96533be62f139b645d7ac5de22a4c3bc1fd9f67e807bb862e965ea08' + 'SKIP' + '825397852f1c25b18c37d25bc53a7706af1eedd97cb9cf85ac590d1044e2254d06c4639bd54cac06ad42534399081b4ca17b0b10605c152ed41a22674c0ae42d' + 'SKIP' + '42a4c3debfa847f46c26db0d3e7cb1a540d3d15deae359b582eb2c71514c2cdb36c28c8c57b22dc32d0cee4d00ad7519da01c66127446692c81ea7a9178d722e' 'SKIP' 'ab28a1f4f1756b8451840566d8100cb3d0b498b13d006593c72829b9783e94900bae78ab47c576d156e1d08c56602274b3f7d5bd885de17336623b23c3e4f255' 'SKIP' '8481f23c33a84d8efb798f4b83f78011f74cd5a3dec428b177d7e600922836c1eec61c4238724038221d8ca2e2cdf20462c5911085da3461aa315f7dd19d96d8' 'SKIP' - '19ebb39f3e53ce77dc3522d15c88270c5ca3fdd76fa8bd417852d08a1d85b30bc6eed3e11c95392b694dfc59bb8b43e41d43df4954f00593c8fbe50bd9e199cc' - 'SKIP' - '4dc42e9bfb5ee0f952bfd7befc9d333044aec11fc3a3852d4636f2e38ab139eb92c497eb3e30921b6ee8ae7d6e5834dc334ad25419bac2c875a9fd7eb7945f9b' + 'eca2778279d88085b6bf7af742773ad533906f98b146ba2526a9e00136e154913f233be728105765b0d2fc2774068f670a4ab1b49d8a7486d20e04d116f71c0b' 'SKIP' 'fea498b5c5ada6d7f57caf652b68f687081aae8552d2d04cd9e96764efd1b896208671f4f501210bb557710cca3ceb0c02ac2b5da6d4c04c210e87ec2d715384' 'SKIP' + '39650b6ac7f62174d4b24e5267f9c6042c22452f16c649e032570eacf428e83ef8e10959fe54adf8573ac1c84d6082eb713ae5f173165af7d1f687e5d26fe6bc' + 'SKIP' + '3acc8aeadbac6ea2dd5a4fba4c10165dc1e6a4a55b838bff184fa46e827df67ea4e9a8535e17ed8cc80cc5338252306a76049af5956dc767bd591729b2aceb4c' + 'SKIP' + 'd442bf561a4b5dd1058c45a8181157feb268a84da94ec7f260dc6d10f7e0ddb71f1097bef945509f3d212431975ad3197f778bcc1c93ce1829ed2070bcf6bf9c' + 'SKIP' + 'c966ed5918d71acb91e988f3776d84db0b072f42e3e3f05cc178fa92a0d902fa3e34beb790c963042b52bc500d6ab9a2802b84a8411ca02cc1e28d361603bb2d' + 'SKIP' + '60fde203ea68744ea9bb8b81a18aedc8a4de8a985dc65bfd6b2f6b43210f87c2875e53eb61f4498c9b236892abde92da32755b69ee5bdb23f079079f22b529ac' + 'SKIP' + '56420f41da60382143b4df2d1fd58384180ca3a179b9f1b5082b2e4b74976ed13a29725c622e61c698631d923384082b43c9f703ce417d09e99556e4600f981f' + 'SKIP' + '5d880f3cdb32ed6fb7ce26ffbd1774fda56641d9304b5372806b6e559dab31dcac233cd6765a0b64bb6afe99fd9602402fb5e3b8c791cd1a9eaf9af28ca01bee' + 'SKIP' + 'a6098df3b653702e2689ec25934839c8a986b0e2f113b216ff4f86c8c5d530584f61d33bf4a027d1b304deb989406a3b342f13e11a66cc91ec69457c5d2e6f14' + 'SKIP' '4a4af88fc8a8c37ae6d20b35d19e60a17672569d5489be120edbcc62ab3ba4bbc43b78cd0799b9fd73c84b089364320bcbad6fcb7128c2cb06d15ed984d6fc8c' 'SKIP' - '0959ca78ff31299adf1091f4aa1b32cd2e9066aba5e44f0658c91bcb93fe8714f7b671ffa2cd498ecbb6c6e643ac6571bfb193034af206599122fd7c2a3c23a8' + '411fd61157830b68da00e19be0d0db7ed61de97fd99b46a31c3958c5f66885265521e9e099c4f3404975db9de9b178cb698dbbe1399d81bf13ecff79b9ebd8b8' 'SKIP' - '03ca740048e48c5b6948f972f7a826240cf51c9b5c8645d1b1c50366e78cbf45f5b45b7b47f87f231b50aab46ef2f8fd78bc53899ba1c319a19f9f61c7cecb90' + '49adc1fc4c68da5c39d96746007d19107ae2d293518cfb36f3742aeba7723db31660fb1f7b3579e8fa88bb53206dbd1e718934f9b37e87925c6c0b06759e77fd' + 'SKIP' + 'f8a67f01fca5614f0eb3bfef59658913fb9b88b043f57c8bfedf04ad79912ff04d5d356dce8ff453ca001c151ab1bc017dcd7daffd5024e2bc20b087d08ab8ce' + 'SKIP' + '561a635f88fb3729937e1632d736a8981002cffd57cf16edad538381de8bb6a583f48b05e4d2bf1c8b6c03aece2495ac2850c1d66c4b540af2b88e1a9a0d9c2b' + 'SKIP' + 'da96dbbe3b6b4e189cf7bdce14647bba07bbc5599ce28ed385a1c34d237ee7ae3ac595d2552444615f2b15c2b7eda42f40743d5bdebd443e3ec31e5743f59056' + 'SKIP' + 'c25bff6e5be62aca887ae0781899e37a39e1aec90f0eb934ec5394abf560f4d02ad1fb3973a45d00058b46555bbc8d9febfcc8026e210f53a8864a91b3b80a9d' + 'SKIP' + 'e70e0077249bf008e069041a0239c6bb63075044cd0df3218649ff92a6a2d43467026886f2a6a70c70552268f6936764459d7cc0817a103b6a0f74c37c52c72d' + 'SKIP' + 'f1f853a0ef095a53080629a00598b7b1e0023141fb1890622ffd8efc4688afdeb87be4f1a096b76868840d45c10b3c88bb33f44677ff70378d63ec81eb3110e3' 'SKIP' '1b63569b589e994d1869e2f8eaf5d4ee225fe68862ac5c848e46544b827ca5fddb4bf060f5f39001608fd9bb284eec005f5f9b095307d3a008c9ea8957c2fad1' 'SKIP' - '073514dabc88f0206911eb27eb36157e425849221056438340b22ee7bfa6997b58ce7dc4470b09d7ee96f6374f117d91cc19d9032401b2355d1c0aba6fc75771' + 'f6430036af77a31a9ddd16200bd3c2c7b1b7e46475c7917dee91c835766be046a31736123ba8429ca2f0f8d3b93ef60522873bbec45d50b954cbb8319e6f25ba' 'SKIP' 'd7dffb68fd22db7ef41d1ac193dd297fb6150f034f257a44e6cb0a4848dc4a94e328c2e26d5296a72993e2db75d09e3ac46456d337a5bae767fb266082d3cc66' 'SKIP' + '4e4792acaf0455f41cb1541d20d37e7a39104aa2dfb505e9be29dc3e7fecf328557780982b32cd8f67f77594542f5b8104766a4e8b5ce2270c9c601728363b3f' + 'SKIP' 'aca0527f5630671a319e7ecff958a3a64f29df73b427c67c1d29763cc1801fa82e96761c3a61c22d936913943a49261da64d490a0752ba9ffc8e0c1d92f99835' 'SKIP' '803736a8e836560b3e50b7c0c8cf1f9ebb16b8642cc7bc6e5167d19037bd757431565fe4581776e50eb2693c49f778a24fecff41a553612e7e498f755b8f2f67' 'SKIP' '4bc132d9c34deaae9b13d08dcff8ea83aa8f9ce4398531e0e0a80df30da3808815f3a587fe5627f6553b8e9c2aea6cc1749b752250a91c94f1cf8ba4f3eb5e8d' 'SKIP' - '2b007e58c0541c6ae71ac07cc8c0762343a50e1f3eb52db5631ce725c4f074f41c639fbf6d8695daa78ee1d6a8cb9a0c31d0a9d9ea2a3a10c05f7f6421dda184' + 'b76c03e8c59c0d2c06d00130cbe4984932762107f3d8180f90ac6c63bd435d6b8dfd317b9d1e756a89c513a20bdb52dcf4f3887daa6598449e560ca1076c5a1d' 'SKIP' - 'd3b2c0e4fd9f26a0eded375522478f8e9403232799d233eed4668920f8a9942ec6bafbc928f14459e4736f63a9a66d3ae2e9f70dc810145acb5fc522b13ecbd9' - 'SKIP' - 'c7b0be7a05a304e9993fe8ebccefa44944bf9d27dad9f7b3cc7111221bf50b2db71ca51cafc89dc03b237aef3aba7114543c8475cc393d13e61726e68898d3af' - 'SKIP' - '4008c7443bc221a142a939deac86918bef8f09ff1ca99a899586b13120edb8007450daf0b9d143770a9f04d616fe96f527b6aac160be45e5cfa66c0da898039c' + '42703ec988c2b1865fbe4eef67840f4370958e3e874d9f8a03a46c43619bed7656c56cae0ef1d8cca5340648b47ba7ddf813d25d9db3307f10146c45b8e34ade' 'SKIP' '029d9e899dd9e7bdbcacee34ee9b468e2e2a8f726e38d014891c8353f6afcc52a58c7d9289fb0487cce4d90c8d51f578c27b1dbcdab42de7308f50b7baf27877' 'SKIP' - '9ede433f4e01a5a313a133a8029d64250596582b93a33e434b8e2ee5bc53ba7f91947e1be2aaf57776028cc89e7ca62cc90067ee64650b3e503acad858777a8a' + '343b48e982195e3f07aa95dac09d5e09d5c02f823b91cc4f0775fa9529e9a985923a5af48067a1f071b99613acfa25b18e3b083e094f3cd97f6758a495c91c3e' 'SKIP' '9ac15b3542629c5333cc7d7f53b4f2995d807a31331b9e04d3da051be0ddde7e8c3cbc79837ffb49a4d364db1680cdc6d763465fa56bd4d26b16b765985babe1' 'SKIP' - '25fc5f03732a584c864ee2ad20a74e16777998b0b34f880bbe9d646cf1944bac1f276bbd62515475337eaa81c0f398d79888aca6dc18bcd06ebb7cee9e4a1d8e' - 'SKIP' '70e35154f9385156a11280294cc254cb314fd89653daedb46546a6bd64c550e3fc7e7a8df3e653c516687f0317bcc710fce82b4d649472333c501ed85fcd6cf6' 'SKIP' '576fd826f28d945782840865f3e5b7c87d7f6f91f08e473ff0d06a37caa2f89cdd9cb4a7aafef5551363aab9c60e887ee794d5b6292874c12baf88c23ea5668e' 'SKIP' - 'b69cf36cc5633e507866e67f59557646dda7d6ea436b145f3f3356c4b12e1300c94977a7d1214835f2476c94a30dd672076241b920d96d744fbc199539df506f' + '01125c624a4971686d02a23918e3da697645a322e5a7638159636d9a16c82f815efdad1d8e22c507518dc0b92b6842277637cb2d0940f4eaab40c0faa77d8da6' + 'SKIP' + '322e4e255a2b900ad224ff376b00199c40e26fc5407e15dd36c1ca4e51c7ca0431397a69696959d1c1910b02a4bdac8efbaa20f6f6f6dd5424b711c149ea367b' + 'SKIP' + 'ae93d961d0eef41ed39c473fe9478b62fab13ba395333b3c11b34b7787904a3d2684520ec76dbe2603dd96a146a68bcbe98da1b7e4cc1e659246127384bce22c' 'SKIP') -b2sums_x86_64=('f69a306213a769710e3429817239746406e115af29f34f37514119eb49f973c86953379b68d3df89700bef7f86e3ec43eb91d5f5821f2a8eae7f28dba589d265') +b2sums_x86_64=('7bcc8b4bc054268c8aa7aec3c6db22d6773d4bca1322edc157c7d23095998eedb0c6698d042310fced5fd759347297c500d2fa987bd71684c8c043c395ce4fc8') # https://www.kernel.org/pub/linux/kernel/v7.x/sha256sums.asc -sha256sums=('55ddf0df8325d9dad96fcff7bd93977d22e3f50af06527572af59b77c7632b78' +sha256sums=('12e8d5a973d1ad7c5a5c69882e4022b131ed715db7003fdcd760ddf8c3e51941' 'SKIP' - '95f3e9209629044028373af987dc0e270a5a15acb8c8e49c5f05057220c75fe2' + '777e4bbfe44531c6504fa646ee74e95e8ff11f264a3fa281b528e25960e741ad' 'SKIP' '6cf74fc3f28a751354187b6cf610c3e43cf9088b5f730b68dbb575828c3a7478' 'SKIP' - 'daf0aaebff3cf4679d0bb8137ace6c9a2de62f75aa4655a0a02ddf759f3c7f26' + '5de3a8f11e9217c9b7815f9348396e8a35f68c2d440bb4fc0746a3e1401e7ca5' 'SKIP' - 'a9171e731d08a454a50174889af8936fab962b33c37c67169b0cdf21b0b80821' + '58f21f8c48dbbb1295d488815aae50003155ec9ea5633ffbca4f0e17b18e8803' 'SKIP' '413e9994d35bc722cfe45752ab60a1bd8454443170d80987e6a5be6b7b745940' 'SKIP' - '01b9087c0cebf17d0ffbbbdd6025c9937fcdfa00c8672d310786ca3c3ce6daf9' + '19cf60d05f912155ce54ad6af70b933ab63a8277785efa03695851089d87ac29' 'SKIP' - '5c776365d391a15c23afb3969183449852f795f42355629ac563abf7ae5e4735' + 'd7c247f4e9f84e05b482c0294ef9053587cc50e46e29962cabcb27f97aa2277d' 'SKIP' - '3a6aaf3b29eb5b4018c5703626311cd1c7a61bd7100789633efc6a79a9b3247b' + '064d5c5429b491e5b62e29cddc7b31a7cf29a4b66597428ce042a017deb2d2c4' 'SKIP' - 'b28d9b548407f0da4280f4f9a6c0e92db6f3a32a35c7668471b92096eb7b093c' + '574b3e1152b16c27b281c56564044c38fc80a82a591acb41e99fbb23a658b03b' 'SKIP' - '9492d7db11fd327ff06a52cad945bf8ff6dfcabec72c435bf02b4432ee67475e' - 'SKIP' - '66af19c43c068144699183a17b67bcf4df03578937c4cffc5d8b24e2e2828f7b' - 'SKIP' - '267506a02ac723ea37adcc7c851051edbd1ed731c6aaea4c9e590aadc207855c' + '04350da18867666ef240e3a1d2d69281ad9990412154408564e2399bbc96628f' 'SKIP' '07baad05112a47cb0aea81191502c87e496e13b4f23a6ec36ea65bb4219c35a8' 'SKIP' - '720720067a21e8c57d2619f4c0b46846ac060ec6db97088634d62ab8628c70a9' + 'df870910371645a95140a3f92424db43c4e1a92bd493393e598d61c6266f1c7b' 'SKIP' - '3d1299d19aa9fbc96d25f67154009813e05e92340340953ac8355649cff837a4' + '3d5550935087a7d82b527227e8f08ed98c2ffdb830b526c09e30b93137b3e607' 'SKIP' - 'dac26fb42f5df71b30c46b3c5894c6016af5a283c57b45173386767ca53b1bf8' + '2351136c78ad2dfe68e34bf9a70d377724257f6239e68bf8f501637b607af6b2' 'SKIP' - '66eb2c49dbf708cc781d411d0e44ed613480f0ef66d522272929d9cb7bb21e93' + '6c93475fc28d211ad7c15c691ea882deedeea8c0defe153cdde0a2a74488c7a0' 'SKIP' '718b40e15350049e03c0ed281b9257fca5432b8dec2e0e145e8b26507ee8892d' 'SKIP' @@ -389,7 +525,9 @@ sha256sums=('55ddf0df8325d9dad96fcff7bd93977d22e3f50af06527572af59b77c7632b78' 'SKIP' '7c25d0a53a115bc1f072ff0cfdd9aa88858e754ae8b15026a2582656e8bd9c43' 'SKIP' - '79799f12cdf42de1cf6c5d483896b439d77bc2e582e10c7215f5375a76612a72' + '336d72ab830bb33ef2a23bda1869e1fc071af64b24779281e197073ab70c851a' + 'SKIP' + '829300e576f418e64101a20d87f30f7c90798cd6e179bfb72768a83d99fa711e' 'SKIP' '5e3b455024fab2855b590091c9d14b0d12d5363f3d4bf025b31b9aa88b65eba5' 'SKIP' @@ -401,65 +539,99 @@ sha256sums=('55ddf0df8325d9dad96fcff7bd93977d22e3f50af06527572af59b77c7632b78' 'SKIP' 'e1226c836a9c2fb6daff109e56ba21c13fd198e109cbb181de920af04fe153e6' 'SKIP' - '8d60f7dd26ef904419aa966427a9af2f2c9a079bb092ea0b58274ab905f6047a' + '2cdd81f5520b935e1d99629fbd6e1cc9d1351b301396316f37946b95084f1a26' 'SKIP' - '258597006eb96e1fb185ec9303ceb48148eedce95608b57900383f6df59d2989' - 'SKIP' - 'f935409f9aeba3314adfad7db1b3d895557a07029a80d10969804e49c0e270ca' + 'e65c2645b0d01301a91874183faed44a390b7a402d2dffd45171d210034dcc93' 'SKIP' 'd8e64b9da3beab7c33832c019da50d0977b39dc32ec1f649a7c3bc93a2fdb9a5' 'SKIP' - '1780f66b157de6d249301331e31b7505f6e6fa26686fa4c618623567e4b758ae' + '5e2515879d58eba10c861f73318f4c4b478b8dd592680eb58961aa4065d4c3df' 'SKIP' - '2501c98aaea6a53eeaa18d696f60b677e710d0f2d89f7525e47ba806de1878b3' + '874efac3eb32970f3e06275adc072a384b89ff69e2e5b7452d6146a6e89049b0' + 'SKIP' + '43aa4a11d9d48f5094b4c0e807d1d31598e861a50695bdfa52f19819b5ff5285' + 'SKIP' + '2b49e4c98c5d704bee0a0e2b38051d59177161bca7b0188e6c0feb555a95fbf1' 'SKIP' 'e6b770d37e80509ed7ec5d8b59b4d2e5d6dba54ba659d7392de3b567f1eb6e54' 'SKIP' - 'b8596b5b5b546fdc309e10326b0e2ccea5a0538d4919630ed2845cded78580aa' + 'a9b3ef8aa79488d1cd813507e040e0762d4c8c5845b7f4ecc29c6cea1ade8056' + 'SKIP' + '264d49d74eb4006c8eded08e39409045032920d76df035aebe8a1d0939d4b7b7' 'SKIP' '5cc215215fed4247c2d6b9732deff586331a07d54c3b00538a81921093a3a1a1' 'SKIP' 'e303da14a3c8a15fd1cb45394138f03dd16e66d2ac0d1ebcd933977df937d771' 'SKIP' - '503eba8d7a80b978fccdaac798f57cbb6cd2b2b04fd04c46399daa004b6646dc' - 'SKIP' '1f0c958b64ba48b8dc8b5548e1ad1934b8d4903fc16fec15014759e3681ea275' 'SKIP' - '47994d576008a377de612ed77e6b2e7bc6b24ad3a30a6d157646d380ae323142' - 'SKIP' 'd9eb3717ba98e270b0a998f8dd90cbda072f63b6a6ea8f66763bde3ea64e591d' 'SKIP' + 'ce634014dc0131a44c3c95a8e9cb0a9de433f59d73fd99d3e3297adde65fef71' + 'SKIP' + 'd069e594d170adb016c5000d9cb7eac12c3c267a6b60c27afde7f046dba0509b' + 'SKIP' + '78feb2e2fabccaf954fdae5b776a22cec4a3f4135fbd24f79596a5274fc0129b' + 'SKIP' + 'f864c7daeaac0bad482bbf3853583d8a6cbbf2bf264562e397fc26355ffd5504' + 'SKIP' + 'c3a709af1589bd0c467565dc66ec4c01bed9c2b94314dde30c839084e594557b' + 'SKIP' '1e2fddb2e0c5d184a2e8c774886cf9be2f2f292e4f05832b50e5b49c48a7bacc' 'SKIP' 'b9b8771b6a8bd7128f4344ad189e32c155abf72abd3ff646dcf5c78b08eb94c4' 'SKIP' - 'f62837e8ee51070f8139f718e0e80a4ce0c0dea9ce2328c10c63403aca5b3d2e' + '34991cf6cec6c22bc1a55a76bc5f72b405165a2f14ef977a0446df7c18bdfbb4' 'SKIP' 'cae59282e3d1afd69f4f5dc00a180c712ef464c98b8fd7ccc9b03de944dc79b9' 'SKIP' - '5a74c8b2ba11e5876eeeb5ba530ae97e384b48943fa7a31e13396ecb82c5a8ce' + '3b06b5285923c251b0ebaf69911875f796cf7f1d7580ec33bb218509fea313a7' 'SKIP' - '8b954ea37a2190022064c82688bc8cb1cf79a90598f078035be7d4041003cda9' + '15609bef72571868fec6b8971f53b74441e5e93fbdd3c677ede963d0719cbc40' + 'SKIP' + '1231a6e23557936b811ce5dc184c23a7fbd268a20d582b6820c67125df8f07c0' + 'SKIP' + 'ec12a77671afb85f8297c86fc97f8314b4f00a1630acb199af12e3550a9d4ec8' + 'SKIP' + '1b1660064a3dadf2bb3006b140d1c7016a1ad051f27e7a4d6d9f3f40d491fe9c' 'SKIP' '40ac31789399a5964c4fd820d4cdc7214add91109bbcf4a614619e5d8b367441' 'SKIP' - '707460d08c15f451d0a0e567c6e35522f3e1409bbe3bfbcf7160f33af06391b7' + 'ab3b968d8bc2073e8c53478aa5d5afbadc5329a22b1486a014e5d0d84bfcf752' 'SKIP' - 'eebbe50deab379d09b5149ef30769ad6dfa11109696934ee66b108ec406da942' + '2a2a81e647aefda80192d230cd1dbae2aeb611f66b7d6c83694b9f9f8e2befdb' + 'SKIP' + '2772f0733267d12f36ae2a339c9aeaf0a133a20381a6b73e0c26f2ef92996ff3' + 'SKIP' + '3998c774884fdfc94be3da3c4c3641bd4b5aeb1878d8ef13bf0e27cd9f2e9994' + 'SKIP' + '0d5afb79681a948a65943f557d143c15126e2497e71c5ff50ac13ed8619a67e5' + 'SKIP' + 'e95f8fb6d116b30c2b86d9fb51747a9f9b4e5f980c1a3b627a38940db9d0e521' 'SKIP' 'dab92dbc6eac02771c72801d94e6a53aabcf629a9cd79e85e0ce2ba44a65b06b' 'SKIP' - '30e2fc90c950869ab2fe1535b609f35dc1d46d6b6816de4afcbf687dbc224f0a' - 'SKIP' - '1fdf8c4d8ee07c02883b827a11a84c1e5f70570545b8a4a232f6b59dd02049fa' + 'f55bc6366f8b9ffef744b289788a19e8604239e84c1af8b929a6922cf07d223d' 'SKIP' '0057dff07a3ce9c7b7085edd1f912958e01ce8f7400151d55dfb74c0c6a084e2' 'SKIP' - '2762f72620781a672d9a21bcc591b862b04a756b271cd1009a7390eb1b77635e' + '9c4c16b9593449ce556a4d6325e5e2205cb1698f9000a967ba7df118395b1ef0' 'SKIP' - '541325476efee83232ab8a6c62c14cf3c3c957c49cf053ed2869f7780f86d032' + '00afdc277c53dff0898ac86fd44ead020e007373134d7c7c333285a9f1dbf786' 'SKIP' - '8a8eb0934424ab6312f26e03a06be5d0401360ee60a20a87b11f7c3c6590103c' + 'bed39a5e6bbb7cdac3f56a883a4684b2a63f3e8fe2f8cdcdd0b89b374b8d2a21' + 'SKIP' + '75d7fc371ba9b00a78af2e4b0fdb7cf79bf25a6620c983662d6b2e90cf5ce8c0' + 'SKIP' + '0fe32924142868879ac9417c67b0233b6ef8ddb0163183352479f5163bf7b5df' + 'SKIP' + '7444186ad4f6d8e2b52a3729064f649b035af6eae4f4ad46b9768c006419fdbc' + 'SKIP' + '94750a925a726ad6631d0486e7f3cb3796b3941715b188e790fa135d0aa5734d' + 'SKIP' + '143193a175084d65035bcfdcc81e9dfef2e794dd56d637556db2c73b117f708f' + 'SKIP' + 'f18821fa9527c7ee07a49b9c1c983a3584d5058f9734b520b5d51cb485c93f17' 'SKIP' 'f7ff13ea46359217d696c6ccd49bc3d74c441ac5893d5151d95138017b0d10aa' 'SKIP' @@ -469,73 +641,139 @@ sha256sums=('55ddf0df8325d9dad96fcff7bd93977d22e3f50af06527572af59b77c7632b78' 'SKIP' '7245c6e2cb5c0b0ddf800894f087770cbf8ccc6f8b3f53b9a21c704e9dfa42c2' 'SKIP' - '135140bce40644079be7743b51f8618fc829a8868ac53137050f76c45eca4061' + 'd49629fd003900f13418c05d8c2fc37c8dea2fd8a15debb5b863008c9c46021f' 'SKIP' - 'e6ec816ae309445c6c11e3433c04d0c9100e16c0e23268226d13c19e7be49edb' + '411fe33ab7021dcf73b02cdc80a64094a511333c52dd754e54d382fe1145de08' + 'SKIP' + '5104ee45fc11994b1cb44130811435c84fe4db532a2fa9a722c7b03dbbdc8bf1' + 'SKIP' + 'f7617e3c4248e015f6f174e7c8659299d831035d0fb3c89acd1a858a202f91b9' + 'SKIP' + 'e7dd17ccd4b417a1813c21285c738a3fdede1326d8a1445086ea1af294c3710b' + 'SKIP' + '058aa32a18723035b18a770e1bd7f4b4f101bf5618bf0114e1874a398de42e06' + 'SKIP' + '833c5ede23f4a014f428c64b7da550cdcd7ecc939dfeb45dce172b18417888a5' + 'SKIP' + '21a9196887fe8453218e4805e171b19958005f38e5d21886c58a55f8d9b86795' 'SKIP' '29c61ec984bd342113b3c4310a3d5fddfd6d7db83847cfb26b84cc730385320a' 'SKIP' - '53425998005a4115bdf6001b9ffbe62c66bb2db205994b2e13005a3417ea6cbb' + '4b5abcc24f557e83e4d385c8304c781d6ab78fc0a3935db97e01e7d27fc617ea' + 'SKIP' + '0dc5f0f91640715ee1744fb8cfe680de6ac1370f4df8e17d31016744f31d3638' + 'SKIP' + 'cd068c7e199f458356886eb321f759a91894bca374c71ccac242407d8b22a1f5' + 'SKIP' + '618369a173be9a5a2f91fb6044931d897bbf4911586bd94a10b43a25838872a2' + 'SKIP' + 'bd3535f78183d56b133986b814ec31a8c1aa933d71b3a611937e6377ee04af10' + 'SKIP' + 'bb1e2ab62b109e434bd2c2635b5912602b8a8cc0ba24743b7bb5f691dcde2353' + 'SKIP' + '55ddb7c072f3933eef42ac48f3b32f90a5298f5cf90b4ba5b2128a197250494a' + 'SKIP' + '62e446b250e96fb65a3068bac424e1e5fe4482ad6d7ac473fa94cb8ac047c0ab' + 'SKIP' + '8b55dbedf730b0dc675793418c21b909aed5db4311248bd277d3184839d261f0' + 'SKIP' + '5a09e7b8e10fdc61839ada49e9a33a64b240fa54847c1c0e0044518297196f0b' + 'SKIP' + 'b857f4c98ff5998adab1765a06ad307f9334a8d7d9644a3de556f2b0e9cb2b4b' 'SKIP' 'c28fca9ba015e96599627acf42e52737a6a846bdcbfd3d4af4147eda3109e12d' 'SKIP' - '5ce97dadb9eb1a8b0da41a8446ee74e01a5a7e7ab439bbea7a711318f04ae24f' + 'd15cdfc9b86c40a119a4f21f4fbaec72efebc67677a7386df46a61816d4c6e02' 'SKIP' - '44f1c7bd18ea7f8772ce41e24dddd5f314e416ec8dfac6afe1ce8680c1bb4903' + '1b070eef78ccffb3763b464f5a1b0636936f4c33b0be9b44ab116c5dcbadbebe' 'SKIP' - '0bb8ae5b6f23e13c60953eb5bbc8939e8f6c8faf2b216a20203af3b6f0217134' + 'eb0923de01175707151930d28b1a1a912d24bbc171248fa9f45f57a2125d077a' 'SKIP' - '907c92ef681154d10959a5f0cbe2a636688036990ba42e059af00cc2311b4be4' + 'c076ed3752bcc2033e435043668fe007eb1ea1760597416fbb5d90bed4947bec' + 'SKIP' + '0fa9fdc6d6c85435d7b3cb38b51823f458ab273c527fcbe21b945dbd89686304' + 'SKIP' + 'ef66f6c32ac98a311b7437265b21a8c70746f1ea4dfbe28aff655e778d09c28b' + 'SKIP' + 'b45303b559ab8990ecb4e0281e5d529625311e795d6c3a9a52c2c27498141e18' + 'SKIP' + '1728e516153acca3c343f5184c9894fe6f1a1fbc95abe1594f544ef601db39ed' 'SKIP' '534e68a989d36823f14a10299803f294eef39391619b4c329d78c20539d9fcd5' 'SKIP' '516e6ec51ca0330989128a4eb2a8f6785e5d45d930899cbbbf37b8147ddfd854' 'SKIP' - 'bda3d6842f15998ce3f3e33d0e6e420df9c9c2abcba13716af478720c5933753' - 'SKIP' - '3e7e7dba1ca88930c2c3f0d5411ddcee8c2e12fca59d859e2383dccaf9a08022' + '757ccc291d53030b89088802615ecf727df6f18ab2515cc0075008ebbd564e36' 'SKIP' '3a271bc6b0219152047fb2b78d9e695eb791b796ea6d8233096912c77d62a5c0' 'SKIP' + 'fd6bf3ed3d16c788330630fdd15604836ec2a0b6a7db2b04216f756da9117bd2' + 'SKIP' + '4d598b9e00edf7c17d915eec7c8d369fc0ea40badb7f4576cb3dedd92a685d4b' + 'SKIP' + '98ff0d6196470c582b3e3841ef35bf82a63b57ebaef952885bd1905bcb8e627b' + 'SKIP' + 'de2682af1db7f5ae8cd74da4f9020bfb7d2167f030eb15f0afb7bd46ffe49e84' + 'SKIP' + '73dc22b7f97a6cc5a5f35d6b30b9b7b12be2566160a024769550290a0e7e4029' + 'SKIP' + 'f06834dfaa9e976a5778fa4fd07117a85bbff3b788c91e61d8cbc628e1b8bfb3' + 'SKIP' + '11884198740c5ac572cbe87086b1744ce19f0e1f856775e6edccce843ab097a3' + 'SKIP' + 'e6d848172228e98dde3a7ec679e7ea6bb0a42cca0b0105c58c8f9d6d44e33943' + 'SKIP' '4e80f2d05591175d4ed0a1a144509e97260e5593472aae586c07956215d99162' 'SKIP' - '8ebce8f38e38bd66879d040028c2448e5bc7dcde9df0ccee2b4221015591b162' + '1b442ad6f28c684068844ce93616690b84651e35870802b02937351bea078b4a' 'SKIP' - '0b0e05b615ae6eb825bf7d7fa568f551b0dff6c2cee62c9285c1b0d63224ea1c' + '9be4f91e6a4be87791d700f44c228fe599468f06a4f627b79e75718ac020db4d' + 'SKIP' + 'ed635ed3b63cdb61614b8805aa5a9515d5b538522165be625dca89bdb7d6e801' + 'SKIP' + 'e28c29b9f359db85a60bfd49d2f28ba51f4d2272eb79b7d3e19ba8b3d5acdac9' + 'SKIP' + '3175c56fd2935848ce99fe10be1bc348231909f8479c3d3af911c5c7ab7f381b' + 'SKIP' + '945c90d99f9a9fa47d61e471f76d17c0229ffa0fa8cda22de990a2ccc4194bb2' + 'SKIP' + '70ff41b2341836c27c5545a7948258f137d991c2879cbeb29f7af740c11d20e6' + 'SKIP' + 'b4efd997d3f09cbf258fd0f78aca44e4c96694dc4efc5c8c9a677ea67ebf5a54' 'SKIP' '2e1e2a0eb039ce61c1612bf6c7ab5227b99df053bcd5592da7758715dd333fda' 'SKIP' - '17de1f76da3db42dc3c7829a22953cd2c6de916999d809e3fb0bbb741753ec8f' + '6800dd82115ebe71d80dde0bb368fbd8563caa6c29127ffde0d6b5df81b502b0' 'SKIP' 'd90cc6570a47c8754e4c5d45834e0434c0a1fcabda9b8933b1fba84e3dabc083' 'SKIP' + 'a008c052ca657efcd4e27b8f7b819adbc9606e261aac37d41a40cf67c094cff9' + 'SKIP' '028bf538c870465e1752514725372fa9eef6b7178d9b8d1e2ecc15edc5a63cc8' 'SKIP' 'a92a84405ee9845738c6b52d8f0f0eca16eb40afe0ebe0f4e9e1168597675311' 'SKIP' '44a0b10a6dc83465ea86f5fba936b2bfbeffb1b3f0b2e04013bfed99ba4f6c5a' 'SKIP' - '229b28cee6f2b8afb5888eed453ce7f82de033900383bd91957339007c9e6cbf' + 'ab4a43d7b8d56a4405aaebaac00dd33d2019cae407039fad4b0dae9ba1ffaa78' 'SKIP' - 'c5d61db05b19fde06102b0b3dbd76e1989c8f07f3b7d175176bd2d73f1a71e5c' - 'SKIP' - '5988d7a37b4b71a64ed09fea872f08515a3c7029d33fd053a6a1822bb811ddb5' - 'SKIP' - '1ec4fb93700b0696ba2c2a371f218f13b32dcc745523c7ee26a5c3cd7af253fc' + 'f81f6b5769e47716ca318ecf76ab1c0fafa0e165fa858a64251359937bffcf04' 'SKIP' 'c67fee35a42866dd3c9056ff099fc504cdad9530c107d959331b5572b21af818' 'SKIP' - '36cb306751c57de3e61c7a4cd18a7f131eef0dc1de15a4e28751d1e5b08bace8' + 'fdfe62019e6ef5dfba2fabecc6ee5b58400f41ef000883fed135ecaca881b40c' 'SKIP' '07683e522d6dea3d3ce658ee80bf4317f6c3caf248259da960bc4610f4d1e807' 'SKIP' - '66a0933d2d4f2edc8999b6fdf0fe10b5b607646fd6f1a1d534bf26270543f8f1' - 'SKIP' 'c5279468d93e562f509a6b599a2b7ca7ee0032d3fe35314c5769fc6c9a9234ab' 'SKIP' 'c0111614ec44014cbf621eedf5a4ef302a855423bc1633ee692392950e971d0e' 'SKIP' - '1cbe1ddd3b37cd0c7e4bad4af08e681eda2f7a61698004322bb87f320aa95dbb' + 'c2f916fa04d91edf926abf8fed086302fe538ba62d92450af0ddb6d3a33942f1' + 'SKIP' + 'd17774053ab07de92e18abdb0ff4d00352a02b6a97c48e28d8c875954c8eec36' + 'SKIP' + '774f337663b215c5aa8877c06ffce95be44403fe77516c1be9ef9789e2d049e5' 'SKIP') export KBUILD_BUILD_HOST=omarchy @@ -577,7 +815,8 @@ build() { cd $_srcname MAKEFLAGS="-j$(nproc) $MAKEFLAGS" make all - make -C tools/bpf/bpftool vmlinux.h feature-clang-bpf-co-re=1 + env -u CFLAGS -u CXXFLAGS -u LDFLAGS \ + make -C tools/bpf/bpftool vmlinux.h feature-clang-bpf-co-re=1 } _package() { diff --git a/pkgbuilds/linux-omarchy-bore/config.x86_64 b/pkgbuilds/linux-omarchy-bore/config.x86_64 index 27b8b44..24fe461 100644 --- a/pkgbuilds/linux-omarchy-bore/config.x86_64 +++ b/pkgbuilds/linux-omarchy-bore/config.x86_64 @@ -998,9 +998,9 @@ CONFIG_SOFTIRQ_ON_OWN_STACK=y CONFIG_ARCH_HAS_ELF_RANDOMIZE=y CONFIG_HAVE_ARCH_MMAP_RND_BITS=y CONFIG_HAVE_EXIT_THREAD=y -CONFIG_ARCH_MMAP_RND_BITS=32 +CONFIG_ARCH_MMAP_RND_BITS=28 CONFIG_HAVE_ARCH_MMAP_RND_COMPAT_BITS=y -CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS=8 CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES=y CONFIG_HAVE_PAGE_SIZE_4KB=y CONFIG_PAGE_SIZE_4KB=y @@ -2561,7 +2561,7 @@ CONFIG_EFI_CAPSULE_LOADER=m # CONFIG_EFI_TEST is not set CONFIG_EFI_DEV_PATH_PARSER=y CONFIG_APPLE_PROPERTIES=y -CONFIG_RESET_ATTACK_MITIGATION=y +# CONFIG_RESET_ATTACK_MITIGATION is not set CONFIG_EFI_RCI2_TABLE=y # CONFIG_EFI_DISABLE_PCI_DMA is not set CONFIG_EFI_EARLYCON=y @@ -4672,6 +4672,7 @@ CONFIG_INPUT_IMS_PCU=m CONFIG_INPUT_IQS269A=m CONFIG_INPUT_IQS626A=m CONFIG_INPUT_IQS7222=m +CONFIG_INPUT_ISA1200_HAPTIC=m CONFIG_INPUT_CMA3000=m CONFIG_INPUT_CMA3000_I2C=m CONFIG_INPUT_XEN_KBDDEV_FRONTEND=m @@ -5488,6 +5489,7 @@ CONFIG_SENSORS_ASUS_ROG_RYUJIN=m CONFIG_SENSORS_AXI_FAN_CONTROL=m CONFIG_SENSORS_K8TEMP=m CONFIG_SENSORS_K10TEMP=m +CONFIG_SENSORS_KB9002=m CONFIG_SENSORS_KBATT=m CONFIG_SENSORS_KFAN=m CONFIG_SENSORS_FAM15H_POWER=m @@ -5679,7 +5681,10 @@ CONFIG_SENSORS_MP9941=m CONFIG_SENSORS_MP9945=m CONFIG_SENSORS_MPQ7932_REGULATOR=y CONFIG_SENSORS_MPQ7932=m +CONFIG_SENSORS_MPQ82D00=m CONFIG_SENSORS_MPQ8785=m +CONFIG_SENSORS_MPQ8646=m +# CONFIG_SENSORS_MPQ8646_DEBUG_UNSAFE is not set CONFIG_SENSORS_PIM4328=m CONFIG_SENSORS_PLI1209BC=m CONFIG_SENSORS_PLI1209BC_REGULATOR=y @@ -5687,6 +5692,8 @@ CONFIG_SENSORS_PM6764TR=m CONFIG_SENSORS_PXE1610=m CONFIG_SENSORS_Q54SJ108A2=m CONFIG_SENSORS_STEF48H28=m +CONFIG_SENSORS_SQ24860=m +CONFIG_SENSORS_SQ24860_REGULATOR=y CONFIG_SENSORS_STPDDC60=m CONFIG_SENSORS_TDA38640=m CONFIG_SENSORS_TDA38640_REGULATOR=y @@ -5697,10 +5704,12 @@ CONFIG_SENSORS_TPS53679=m CONFIG_SENSORS_TPS546D24=m CONFIG_SENSORS_UCD9000=m CONFIG_SENSORS_UCD9200=m +CONFIG_SENSORS_VT7505=m CONFIG_SENSORS_XDP710=m CONFIG_SENSORS_XDP720=m CONFIG_SENSORS_XDPE152=m CONFIG_SENSORS_XDPE1A2G7B=m +CONFIG_SENSORS_XDPE1A2G7B_REGULATOR=y CONFIG_SENSORS_XDPE122=m CONFIG_SENSORS_XDPE122_REGULATOR=y CONFIG_SENSORS_ZL6100=m @@ -6772,6 +6781,7 @@ CONFIG_VIDEO_IMX290=m CONFIG_VIDEO_IMX296=m CONFIG_VIDEO_IMX319=m CONFIG_VIDEO_IMX355=m +CONFIG_VIDEO_IMX471=m CONFIG_VIDEO_MAX9271_LIB=m CONFIG_VIDEO_MT9M001=m CONFIG_VIDEO_MT9M111=m @@ -10200,7 +10210,7 @@ CONFIG_AMD_IOMMU=y CONFIG_DMAR_TABLE=y CONFIG_INTEL_IOMMU=y CONFIG_INTEL_IOMMU_SVM=y -CONFIG_INTEL_IOMMU_DEFAULT_ON=y +# CONFIG_INTEL_IOMMU_DEFAULT_ON is not set CONFIG_INTEL_IOMMU_FLOPPY_WA=y CONFIG_INTEL_IOMMU_SCALABLE_MODE_DEFAULT_ON=y CONFIG_INTEL_IOMMU_PERF_EVENTS=y @@ -11847,7 +11857,10 @@ CONFIG_INTEGRITY_MACHINE_KEYRING=y # CONFIG_INTEGRITY_CA_MACHINE_KEYRING is not set CONFIG_LOAD_UEFI_KEYS=y CONFIG_INTEGRITY_AUDIT=y -# CONFIG_IMA is not set +CONFIG_IMA=y +CONFIG_IMA_DEFAULT_HASH_SHA256=y +CONFIG_IMA_WRITE_POLICY=y +CONFIG_IMA_READ_POLICY=y # CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT is not set # CONFIG_EVM is not set # CONFIG_DEFAULT_SECURITY_SELINUX is not set @@ -12277,7 +12290,7 @@ CONFIG_NEED_DMA_MAP_STATE=y CONFIG_ARCH_DMA_ADDR_T_64BIT=y CONFIG_ARCH_HAS_FORCE_DMA_UNENCRYPTED=y CONFIG_SWIOTLB=y -CONFIG_SWIOTLB_DYNAMIC=y +# CONFIG_SWIOTLB_DYNAMIC is not set CONFIG_DMA_NEED_SYNC=y CONFIG_DMA_COHERENT_POOL=y CONFIG_DMA_CMA=y diff --git a/pkgbuilds/linux-omarchy/0010-archlinux-base.patch b/pkgbuilds/linux-omarchy/0010-archlinux-base.patch index 55e9379..497422b 100644 --- a/pkgbuilds/linux-omarchy/0010-archlinux-base.patch +++ b/pkgbuilds/linux-omarchy/0010-archlinux-base.patch @@ -1,5 +1,4 @@ diff --git a/kernel/fork.c b/kernel/fork.c -index f0e2e131a9a5..7b611da9a27a 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -127,6 +127,12 @@ @@ -15,7 +14,7 @@ index f0e2e131a9a5..7b611da9a27a 100644 /* * Minimum number of threads to boot the kernel */ -@@ -2093,6 +2099,11 @@ __latent_entropy struct task_struct *copy_process( +@@ -2092,6 +2098,11 @@ __latent_entropy struct task_struct *copy_process( return ERR_PTR(-EPERM); } @@ -27,7 +26,7 @@ index f0e2e131a9a5..7b611da9a27a 100644 /* * Force any signals received before this point to be delivered * before the fork happens. Collect up signals sent to multiple -@@ -3163,6 +3174,10 @@ static int check_unshare_flags(unsigned long unshare_flags) +@@ -3165,6 +3176,10 @@ static int check_unshare_flags(unsigned long unshare_flags) if (!current_is_single_threaded()) return -EINVAL; } @@ -38,7 +37,7 @@ index f0e2e131a9a5..7b611da9a27a 100644 return 0; } -@@ -3398,6 +3413,15 @@ static const struct ctl_table fork_sysctl_table[] = { +@@ -3400,6 +3415,15 @@ static const struct ctl_table fork_sysctl_table[] = { .mode = 0644, .proc_handler = sysctl_max_threads, }, diff --git a/pkgbuilds/linux-omarchy/0010-archlinux-base.patch.sig b/pkgbuilds/linux-omarchy/0010-archlinux-base.patch.sig index c790810..a828924 100644 Binary files a/pkgbuilds/linux-omarchy/0010-archlinux-base.patch.sig and b/pkgbuilds/linux-omarchy/0010-archlinux-base.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0120-tlbpull.patch b/pkgbuilds/linux-omarchy/0120-tlbpull.patch index 712b9b7..14e2d46 100644 --- a/pkgbuilds/linux-omarchy/0120-tlbpull.patch +++ b/pkgbuilds/linux-omarchy/0120-tlbpull.patch @@ -22,19 +22,6 @@ diff --git a/arch/x86/include/asm/pgtable_types.h b/arch/x86/include/asm/pgtable typedef struct page *pgtable_t; extern pteval_t __supported_pte_mask; -diff --git a/arch/x86/include/asm/pkeys.h b/arch/x86/include/asm/pkeys.h ---- a/arch/x86/include/asm/pkeys.h -+++ b/arch/x86/include/asm/pkeys.h -@@ -88,6 +88,9 @@ int mm_pkey_alloc(struct mm_struct *mm) - u16 all_pkeys_mask = ((1U << arch_max_pkey()) - 1); - int ret; - -+ if (!arch_pkeys_enabled()) -+ return -1; -+ - /* - * Are we out of pkeys? We must handle this specially - * because ffz() behavior is undefined if there are no diff --git a/arch/x86/include/asm/tlbflush.h b/arch/x86/include/asm/tlbflush.h --- a/arch/x86/include/asm/tlbflush.h +++ b/arch/x86/include/asm/tlbflush.h @@ -168,35 +155,7 @@ diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c --- a/arch/x86/mm/pat/set_memory.c +++ b/arch/x86/mm/pat/set_memory.c -@@ -62,18 +62,18 @@ enum cpa_warn { - static const int cpa_warn_level = CPA_PROTECT; - - /* -- * Serialize cpa() (for !DEBUG_PAGEALLOC which uses large identity mappings) -- * using cpa_lock. So that we don't allow any other cpu, with stale large tlb -- * entries change the page attribute in parallel to some other cpu -- * splitting a large page entry along with changing the attribute. -+ * Serialize cpa() using cpa_lock so that we don't allow any other cpu, with -+ * stale large tlb entries, to change the page attribute in parallel to some -+ * other cpu splitting a large page entry along with changing the attribute. - */ - static DEFINE_SPINLOCK(cpa_lock); - --#define CPA_FLUSHTLB 1 --#define CPA_ARRAY 2 --#define CPA_PAGES_ARRAY 4 --#define CPA_NO_CHECK_ALIAS 8 /* Do not search for aliases */ --#define CPA_COLLAPSE 16 /* try to collapse large pages */ -+#define CPA_FLUSHTLB 0x01 -+#define CPA_ARRAY 0x02 -+#define CPA_PAGES_ARRAY 0x04 -+#define CPA_NO_CHECK_ALIAS 0x08 /* Do not search for aliases */ -+#define CPA_COLLAPSE 0x10 /* try to collapse large pages */ -+#define CPA_DEBUG_PAGEALLOC 0x20 - - static inline pgprot_t cachemode2pgprot(enum page_cache_mode pcm) - { -@@ -86,9 +86,8 @@ static unsigned long direct_pages_count[PG_LEVEL_NUM]; +@@ -88,9 +88,8 @@ static unsigned long direct_pages_count[PG_LEVEL_NUM]; void update_page_count(int level, unsigned long pages) { /* Protect against CPA */ @@ -207,16 +166,37 @@ diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c } static void split_page_count(int level) -@@ -418,6 +417,8 @@ static void cpa_collapse_large_pages(struct cpa_data *cpa) +@@ -420,7 +419,7 @@ static void __cpa_collapse_large_pages(struct cpa_data *cpa) int collapsed = 0; int i; +- spin_lock(&cpa_lock); + guard(spinlock)(&cpa_lock); -+ + if (cpa->flags & (CPA_PAGES_ARRAY | CPA_ARRAY)) { for (i = 0; i < cpa->numpages; i++) - collapsed += collapse_large_pages(__cpa_addr(cpa, i), -@@ -888,24 +889,23 @@ static void __set_pmd_pte(pte_t *kpte, unsigned long address, pte_t pte) +@@ -435,10 +434,8 @@ static void __cpa_collapse_large_pages(struct cpa_data *cpa) + collapsed += collapse_large_pages(addr, &pgtables); + } + +- if (!collapsed) { +- spin_unlock(&cpa_lock); ++ if (!collapsed) + return; +- } + + flush_tlb_all(); + +@@ -454,8 +451,6 @@ static void __cpa_collapse_large_pages(struct cpa_data *cpa) + else + pagetable_free(ptdesc); + } +- +- spin_unlock(&cpa_lock); + } + + static void cpa_collapse_large_pages(struct cpa_data *cpa) +@@ -916,24 +911,23 @@ static void __set_pmd_pte(pte_t *kpte, unsigned long address, pte_t pte) { /* change init_mm */ set_pte_atomic(kpte, pte); @@ -246,7 +226,7 @@ diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c } static pgprot_t pgprot_clear_protnone_bits(pgprot_t prot) -@@ -1075,16 +1075,11 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, +@@ -1103,16 +1097,11 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, static int should_split_large_page(pte_t *kpte, unsigned long address, struct cpa_data *cpa) { @@ -265,7 +245,7 @@ diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c } static void split_set_pte(struct cpa_data *cpa, pte_t *pte, unsigned long pfn, -@@ -1135,16 +1130,14 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address, +@@ -1162,16 +1151,14 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address, bool nx, rw; pte_t *tmp; @@ -284,7 +264,7 @@ diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c paravirt_alloc_pte(&init_mm, page_to_pfn(base)); -@@ -1177,7 +1170,6 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address, +@@ -1204,7 +1191,6 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address, break; default: @@ -292,7 +272,7 @@ diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c return 1; } -@@ -1225,7 +1217,6 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address, +@@ -1252,7 +1238,6 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address, * just split large page entry. */ flush_tlb_all(); @@ -300,21 +280,7 @@ diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c return 0; } -@@ -1235,11 +1226,9 @@ static int split_large_page(struct cpa_data *cpa, pte_t *kpte, - { - struct ptdesc *ptdesc; - -- if (!debug_pagealloc_enabled()) -- spin_unlock(&cpa_lock); -+ spin_unlock(&cpa_lock); - ptdesc = pagetable_alloc(GFP_KERNEL, 0); -- if (!debug_pagealloc_enabled()) -- spin_lock(&cpa_lock); -+ spin_lock(&cpa_lock); - if (!ptdesc) - return -ENOMEM; - -@@ -1298,11 +1287,11 @@ static int collapse_pmd_page(pmd_t *pmd, unsigned long addr, +@@ -1327,11 +1312,11 @@ static int collapse_pmd_page(pmd_t *pmd, unsigned long addr, list_add(&page_ptdesc(pmd_page(old_pmd))->pt_list, pgtables); if (IS_ENABLED(CONFIG_X86_32)) { @@ -329,7 +295,7 @@ diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c p4d_t *p4d = p4d_offset(pgd, addr); pud_t *pud = pud_offset(p4d, addr); pmd_t *pmd = pmd_offset(pud, addr); -@@ -1376,7 +1365,7 @@ static int collapse_pud_page(pud_t *pud, unsigned long addr, +@@ -1405,7 +1390,7 @@ static int collapse_pud_page(pud_t *pud, unsigned long addr, */ static int collapse_large_pages(unsigned long addr, struct list_head *pgtables) { @@ -338,7 +304,7 @@ diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c pgd_t *pgd; p4d_t *p4d; pud_t *pud; -@@ -1384,26 +1373,24 @@ static int collapse_large_pages(unsigned long addr, struct list_head *pgtables) +@@ -1413,26 +1398,24 @@ static int collapse_large_pages(unsigned long addr, struct list_head *pgtables) addr &= PMD_MASK; @@ -370,153 +336,6 @@ diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c return collapsed; } -@@ -2004,6 +1991,7 @@ static int __change_page_attr_set_clr(struct cpa_data *cpa, int primary) - { - unsigned long numpages = cpa->numpages; - unsigned long rempages = numpages; -+ bool lock = true; - int ret = 0; - - /* -@@ -2013,6 +2001,29 @@ static int __change_page_attr_set_clr(struct cpa_data *cpa, int primary) - !cpa->force_split) - return ret; - -+ /* -+ * DEBUG_PAGEALLOC is special; it is called from any context the -+ * page-allocator is, which violates the normal cpa_lock locking -+ * rules. -+ * -+ * However, since it is part of the page-allocator, things are still -+ * properly serialized by the page-allocator locking and the fact that -+ * when a page is owned by the page-allocator, it isn't owned by -+ * anybody else. That is, you *SHOULD NOT* be calling cpa() on memory -+ * that isn't allocated. -+ * -+ * Additionally, DEBUG_PAGEALLOC ensures (per probe_page_size_mask()) -+ * that the kernel mapping is 4k pages, therefore there are no large -+ * pages to split/collapse. -+ * -+ * Furthermore, the page-allocator strictly manages pages that -+ * *exist*, avoiding pgd_lock. -+ * -+ * Therefore, it is safe to not take cpa_lock. -+ */ -+ if (debug_pagealloc_enabled() && (cpa->flags & CPA_DEBUG_PAGEALLOC)) -+ lock = false; -+ - while (rempages) { - /* - * Store the remaining nr of pages for the large page -@@ -2023,11 +2034,12 @@ static int __change_page_attr_set_clr(struct cpa_data *cpa, int primary) - if (cpa->flags & (CPA_ARRAY | CPA_PAGES_ARRAY)) - cpa->numpages = 1; - -- if (!debug_pagealloc_enabled()) -- spin_lock(&cpa_lock); -- ret = __change_page_attr(cpa, primary); -- if (!debug_pagealloc_enabled()) -- spin_unlock(&cpa_lock); -+ if (lock) { -+ guard(spinlock)(&cpa_lock); -+ ret = __change_page_attr(cpa, primary); -+ } else { -+ ret = __change_page_attr(cpa, primary); -+ } - if (ret) - goto out; - -@@ -2606,7 +2618,7 @@ int set_pages_rw(struct page *page, int numpages) - return set_memory_rw(addr, numpages); - } - --static int __set_pages_p(struct page *page, int numpages) -+static int __set_pages_p(struct page *page, int numpages, unsigned int cpa_flags) - { - unsigned long tempaddr = (unsigned long) page_address(page); - struct cpa_data cpa = { .vaddr = &tempaddr, -@@ -2614,7 +2626,7 @@ static int __set_pages_p(struct page *page, int numpages) - .numpages = numpages, - .mask_set = __pgprot(_PAGE_PRESENT | _PAGE_RW), - .mask_clr = __pgprot(0), -- .flags = CPA_NO_CHECK_ALIAS }; -+ .flags = CPA_NO_CHECK_ALIAS | cpa_flags }; - - /* - * No alias checking needed for setting present flag. otherwise, -@@ -2625,7 +2637,7 @@ static int __set_pages_p(struct page *page, int numpages) - return __change_page_attr_set_clr(&cpa, 1); - } - --static int __set_pages_np(struct page *page, int numpages) -+static int __set_pages_np(struct page *page, int numpages, unsigned int cpa_flags) - { - unsigned long tempaddr = (unsigned long) page_address(page); - struct cpa_data cpa = { .vaddr = &tempaddr, -@@ -2633,7 +2645,7 @@ static int __set_pages_np(struct page *page, int numpages) - .numpages = numpages, - .mask_set = __pgprot(0), - .mask_clr = __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY), -- .flags = CPA_NO_CHECK_ALIAS }; -+ .flags = CPA_NO_CHECK_ALIAS | cpa_flags }; - - /* - * No alias checking needed for setting not present flag. otherwise, -@@ -2646,20 +2658,20 @@ static int __set_pages_np(struct page *page, int numpages) - - int set_direct_map_invalid_noflush(struct page *page) - { -- return __set_pages_np(page, 1); -+ return __set_pages_np(page, 1, 0); - } - - int set_direct_map_default_noflush(struct page *page) - { -- return __set_pages_p(page, 1); -+ return __set_pages_p(page, 1, 0); - } - - int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid) - { - if (valid) -- return __set_pages_p(page, nr); -+ return __set_pages_p(page, nr, 0); - -- return __set_pages_np(page, nr); -+ return __set_pages_np(page, nr, 0); - } - - #ifdef CONFIG_DEBUG_PAGEALLOC -@@ -2678,15 +2690,23 @@ void __kernel_map_pages(struct page *page, int numpages, int enable) - * and hence no memory allocations during large page split. - */ - if (enable) -- __set_pages_p(page, numpages); -+ __set_pages_p(page, numpages, CPA_DEBUG_PAGEALLOC); - else -- __set_pages_np(page, numpages); -+ __set_pages_np(page, numpages, CPA_DEBUG_PAGEALLOC); - - /* -- * We should perform an IPI and flush all tlbs, -- * but that can deadlock->flush only current cpu. -- * Preemption needs to be disabled around __flush_tlb_all() due to -- * CR3 reload in __native_flush_tlb(). -+ * We should perform an IPI and flush all tlbs, but that can -+ * deadlock, settle for a local flush. -+ * -+ * Not doing a global TLB flush means that remote CPUs will retain -+ * stale TLB entries. In case of P->NP (on free) this means the remote -+ * CPUs will not take the faults, making the debug scheme less -+ * reliable. On the NP->P (on alloc) this means the remote CPUs can -+ * take a spurious fault. However spurious_kernel_fault() will observe -+ * *_present() and fix it up. -+ * -+ * Preemption needs to be disabled around __flush_tlb_all() due to CR3 -+ * reload in __native_flush_tlb(). - */ - preempt_disable(); - __flush_tlb_all(); diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c --- a/arch/x86/mm/pgtable.c +++ b/arch/x86/mm/pgtable.c diff --git a/pkgbuilds/linux-omarchy/0120-tlbpull.patch.sig b/pkgbuilds/linux-omarchy/0120-tlbpull.patch.sig index b8fcd47..3f44a63 100644 Binary files a/pkgbuilds/linux-omarchy/0120-tlbpull.patch.sig and b/pkgbuilds/linux-omarchy/0120-tlbpull.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0130-sched-detach-tasks.patch b/pkgbuilds/linux-omarchy/0130-sched-detach-tasks.patch index 296b430..245f6e1 100644 --- a/pkgbuilds/linux-omarchy/0130-sched-detach-tasks.patch +++ b/pkgbuilds/linux-omarchy/0130-sched-detach-tasks.patch @@ -1,7 +1,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c -@@ -13338,12 +13338,15 @@ static int sched_balance_rq(int this_cpu, struct rq *this_rq, +@@ -13350,12 +13350,15 @@ static int sched_balance_rq(int this_cpu, struct rq *this_rq, * still unbalanced. ld_moved simply stays zero, so it is * correctly treated as an imbalance. */ diff --git a/pkgbuilds/linux-omarchy/0130-sched-detach-tasks.patch.sig b/pkgbuilds/linux-omarchy/0130-sched-detach-tasks.patch.sig index 88fd720..b0ccd26 100644 Binary files a/pkgbuilds/linux-omarchy/0130-sched-detach-tasks.patch.sig and b/pkgbuilds/linux-omarchy/0130-sched-detach-tasks.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0131-sched-avg-idle.patch b/pkgbuilds/linux-omarchy/0131-sched-avg-idle.patch deleted file mode 100644 index 5f8bb20..0000000 --- a/pkgbuilds/linux-omarchy/0131-sched-avg-idle.patch +++ /dev/null @@ -1,23 +0,0 @@ -diff --git a/kernel/sched/core.c b/kernel/sched/core.c ---- a/kernel/sched/core.c -+++ b/kernel/sched/core.c -@@ -3743,11 +3743,17 @@ static inline void ttwu_do_wakeup(struct task_struct *p) - - void update_rq_avg_idle(struct rq *rq) - { -- u64 delta = rq_clock(rq) - rq->idle_stamp; -- u64 max = 2*rq->max_idle_balance_cost; -+ u64 idle_stamp = rq->idle_stamp; -+ u64 delta, max; -+ -+ if (!idle_stamp) -+ return; -+ -+ delta = rq_clock(rq) - idle_stamp; - - update_avg(&rq->avg_idle, delta); - -+ max = 2 * rq->max_idle_balance_cost; - if (rq->avg_idle > max) - rq->avg_idle = max; - rq->idle_stamp = 0; diff --git a/pkgbuilds/linux-omarchy/0131-sched-avg-idle.patch.sig b/pkgbuilds/linux-omarchy/0131-sched-avg-idle.patch.sig deleted file mode 100644 index a9f0fa6..0000000 Binary files a/pkgbuilds/linux-omarchy/0131-sched-avg-idle.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/0140-sched-always-inline.patch b/pkgbuilds/linux-omarchy/0140-sched-always-inline.patch index ee0da9b..cfb6c27 100644 --- a/pkgbuilds/linux-omarchy/0140-sched-always-inline.patch +++ b/pkgbuilds/linux-omarchy/0140-sched-always-inline.patch @@ -212,7 +212,7 @@ diff --git a/include/linux/vtime.h b/include/linux/vtime.h diff --git a/kernel/sched/core.c b/kernel/sched/core.c --- a/kernel/sched/core.c +++ b/kernel/sched/core.c -@@ -5091,7 +5091,7 @@ static inline void prepare_task(struct task_struct *next) +@@ -5098,7 +5098,7 @@ static inline void prepare_task(struct task_struct *next) WRITE_ONCE(next->on_cpu, 1); } @@ -221,7 +221,7 @@ diff --git a/kernel/sched/core.c b/kernel/sched/core.c { /* * This must be the very last reference to @prev from this CPU. After -@@ -5135,7 +5135,7 @@ static void zap_balance_callbacks(struct rq *rq) +@@ -5142,7 +5142,7 @@ static void zap_balance_callbacks(struct rq *rq) rq->balance_callback = found ? &balance_push_callback : NULL; } @@ -230,7 +230,7 @@ diff --git a/kernel/sched/core.c b/kernel/sched/core.c { void (*func)(struct rq *rq); struct balance_callback *next; -@@ -5170,7 +5170,7 @@ struct balance_callback balance_push_callback = { +@@ -5177,7 +5177,7 @@ struct balance_callback balance_push_callback = { .func = balance_push, }; @@ -239,7 +239,7 @@ diff --git a/kernel/sched/core.c b/kernel/sched/core.c __splice_balance_callbacks(struct rq *rq, bool split) { struct balance_callback *head = rq->balance_callback; -@@ -5244,7 +5244,7 @@ prepare_lock_switch(struct rq *rq, struct task_struct *next, struct rq_flags *rf +@@ -5251,7 +5251,7 @@ prepare_lock_switch(struct rq *rq, struct task_struct *next, struct rq_flags *rf __acquire(__rq_lockp(this_rq())); } @@ -248,7 +248,7 @@ diff --git a/kernel/sched/core.c b/kernel/sched/core.c __releases(__rq_lockp(rq)) { /* -@@ -5278,7 +5278,7 @@ static inline void kmap_local_sched_out(void) +@@ -5285,7 +5285,7 @@ static inline void kmap_local_sched_out(void) #endif } @@ -257,7 +257,7 @@ diff --git a/kernel/sched/core.c b/kernel/sched/core.c { #ifdef CONFIG_KMAP_LOCAL if (unlikely(current->kmap_ctrl.idx)) -@@ -5332,7 +5332,7 @@ prepare_task_switch(struct rq *rq, struct task_struct *prev, +@@ -5339,7 +5339,7 @@ prepare_task_switch(struct rq *rq, struct task_struct *prev, * past. 'prev == current' is still correct but we need to recalculate this_rq * because prev may have moved to another CPU. */ diff --git a/pkgbuilds/linux-omarchy/0140-sched-always-inline.patch.sig b/pkgbuilds/linux-omarchy/0140-sched-always-inline.patch.sig index 2e7bcd9..ee137b3 100644 Binary files a/pkgbuilds/linux-omarchy/0140-sched-always-inline.patch.sig and b/pkgbuilds/linux-omarchy/0140-sched-always-inline.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0141-sched-urgent-fixes.patch b/pkgbuilds/linux-omarchy/0141-sched-urgent-fixes.patch index 9bc941f..f26a59b 100644 --- a/pkgbuilds/linux-omarchy/0141-sched-urgent-fixes.patch +++ b/pkgbuilds/linux-omarchy/0141-sched-urgent-fixes.patch @@ -1,21 +1,7 @@ -diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c ---- a/kernel/sched/deadline.c -+++ b/kernel/sched/deadline.c -@@ -3026,8 +3026,8 @@ static struct task_struct *pick_next_pushable_dl_task(struct rq *rq) - next_node = rb_first_cached(&rq->dl.pushable_dl_tasks_root); - while (next_node) { - i = __node_2_pdl(next_node); -- /* make sure task isn't on_cpu (possible with proxy-exec) */ -- if (!task_on_cpu(rq, i)) { -+ /* skip tasks that cannot be migrated */ -+ if (!task_on_cpu(rq, i) && !is_migration_disabled(i)) { - p = i; - break; - } diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c -@@ -10565,17 +10565,40 @@ static enum llc_mig can_migrate_llc(int src_cpu, int dst_cpu, +@@ -10575,17 +10575,40 @@ static enum llc_mig can_migrate_llc(int src_cpu, int dst_cpu, return mig_llc; } @@ -58,7 +44,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c mm = p->mm; if (!mm) return mig_unrestricted; -@@ -10632,6 +10655,14 @@ alb_break_llc(struct lb_env *env) +@@ -10642,6 +10665,14 @@ alb_break_llc(struct lb_env *env) unsigned long util = 0; struct task_struct *cur; @@ -73,7 +59,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c if (env->src_rq->nr_running <= 1) return true; -@@ -10639,7 +10670,8 @@ alb_break_llc(struct lb_env *env) +@@ -10649,7 +10680,8 @@ alb_break_llc(struct lb_env *env) if (cur && cur->sched_class == &fair_sched_class) util = task_util(cur); @@ -83,7 +69,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c util, false) == mig_forbid) return true; } -@@ -10679,8 +10711,7 @@ static bool migrate_degrades_llc(struct task_struct *p, struct lb_env *env) +@@ -10689,8 +10721,7 @@ static bool migrate_degrades_llc(struct task_struct *p, struct lb_env *env) READ_ONCE(p->preferred_llc) != llc_id(env->dst_cpu)) return true; @@ -93,7 +79,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c return false; return true; -@@ -11743,6 +11774,15 @@ static inline bool llc_balance(struct lb_env *env, struct sg_lb_stats *sgs, +@@ -11753,6 +11784,15 @@ static inline bool llc_balance(struct lb_env *env, struct sg_lb_stats *sgs, if (env->sd->flags & SD_SHARE_LLC) return false; @@ -109,17 +95,3 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c /* * Skip cache aware tagging if nr_balanced_failed is sufficiently high. * Threshold of cache_nice_tries is set to 1 higher than nr_balance_failed -diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c ---- a/kernel/sched/rt.c -+++ b/kernel/sched/rt.c -@@ -1871,8 +1871,8 @@ static struct task_struct *pick_next_pushable_task(struct rq *rq) - return NULL; - - plist_for_each_entry(i, head, pushable_tasks) { -- /* make sure task isn't on_cpu (possible with proxy-exec) */ -- if (!task_on_cpu(rq, i)) { -+ /* skip tasks that cannot be migrated */ -+ if (!task_on_cpu(rq, i) && !is_migration_disabled(i)) { - p = i; - break; - } diff --git a/pkgbuilds/linux-omarchy/0141-sched-urgent-fixes.patch.sig b/pkgbuilds/linux-omarchy/0141-sched-urgent-fixes.patch.sig index 4983403..0af85fe 100644 Binary files a/pkgbuilds/linux-omarchy/0141-sched-urgent-fixes.patch.sig and b/pkgbuilds/linux-omarchy/0141-sched-urgent-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0142-sched-itmt-no-debugfs-dependency.patch b/pkgbuilds/linux-omarchy/0142-sched-itmt-no-debugfs-dependency.patch deleted file mode 100644 index a0f39ea..0000000 --- a/pkgbuilds/linux-omarchy/0142-sched-itmt-no-debugfs-dependency.patch +++ /dev/null @@ -1,24 +0,0 @@ -diff --git a/arch/x86/kernel/itmt.c b/arch/x86/kernel/itmt.c ---- a/arch/x86/kernel/itmt.c -+++ b/arch/x86/kernel/itmt.c -@@ -110,18 +110,14 @@ int sched_set_itmt_support(void) - arch_debugfs_dir, - &sysctl_sched_itmt_enabled, - &dfs_sched_itmt_fops); -- if (IS_ERR_OR_NULL(dfs_sched_itmt)) { -+ if (IS_ERR(dfs_sched_itmt)) - dfs_sched_itmt = NULL; -- return -ENOMEM; -- } - - dfs_sched_core_prio = debugfs_create_file("sched_core_priority", 0644, - arch_debugfs_dir, NULL, - &sched_core_priority_fops); -- if (IS_ERR_OR_NULL(dfs_sched_core_prio)) { -+ if (IS_ERR(dfs_sched_core_prio)) - dfs_sched_core_prio = NULL; -- return -ENOMEM; -- } - - sched_itmt_capable = true; - diff --git a/pkgbuilds/linux-omarchy/0142-sched-itmt-no-debugfs-dependency.patch.sig b/pkgbuilds/linux-omarchy/0142-sched-itmt-no-debugfs-dependency.patch.sig deleted file mode 100644 index d96b7f9..0000000 Binary files a/pkgbuilds/linux-omarchy/0142-sched-itmt-no-debugfs-dependency.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/0143-sched-hybrid-cluster-balancing.patch b/pkgbuilds/linux-omarchy/0143-sched-hybrid-cluster-balancing.patch index 9c10fcd..137ec28 100644 --- a/pkgbuilds/linux-omarchy/0143-sched-hybrid-cluster-balancing.patch +++ b/pkgbuilds/linux-omarchy/0143-sched-hybrid-cluster-balancing.patch @@ -14,22 +14,22 @@ diff --git a/include/linux/sched/sd_flags.h b/include/linux/sched/sd_flags.h diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c -@@ -11902,10 +11902,25 @@ static inline void update_sg_lb_stats(struct lb_env *env, +@@ -11912,12 +11912,25 @@ static inline void update_sg_lb_stats(struct lb_env *env, continue; if (sd_flags & SD_ASYM_CPUCAPACITY) { - /* Check for a misfit task on the cpu */ - if (sgs->group_misfit_task_load < rq->misfit_task_load) { - sgs->group_misfit_task_load = rq->misfit_task_load; -- *sg_overloaded = 1; +- + if (rq->misfit_task_load) { + /* + * Always mark the root domain overloaded so big + * CPUs can pick up misfit tasks via newly idle + * balance. + */ -+ if (balancing_at_rd) -+ *sg_overloaded = 1; + if (balancing_at_rd) + *sg_overloaded = 1; + + /* + * Only account misfit load if @dst_cpu can @@ -44,43 +44,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c } } else if (env->idle && sched_reduced_capacity(rq, env->sd)) { /* Check for a task running on a CPU with reduced capacity */ -@@ -11984,6 +11999,17 @@ static bool update_sd_pick_busiest(struct lb_env *env, - sds->local_stat.group_type != group_has_spare)) - return false; - -+ /* -+ * Candidate sg has no more than one task per CPU and has higher -+ * per-CPU capacity. Migrating tasks to less capable CPUs may harm -+ * throughput. Maximize throughput, power/energy consequences are not -+ * considered. -+ */ -+ if ((env->sd->flags & SD_ASYM_CPUCAPACITY) && -+ (sgs->group_type <= group_fully_busy) && -+ (capacity_greater(sg->sgc->min_capacity, capacity_of(env->dst_cpu)))) -+ return false; -+ - if (sgs->group_type > busiest->group_type) - return true; - -@@ -12090,17 +12116,6 @@ static bool update_sd_pick_busiest(struct lb_env *env, - break; - } - -- /* -- * Candidate sg has no more than one task per CPU and has higher -- * per-CPU capacity. Migrating tasks to less capable CPUs may harm -- * throughput. Maximize throughput, power/energy consequences are not -- * considered. -- */ -- if ((env->sd->flags & SD_ASYM_CPUCAPACITY) && -- (sgs->group_type <= group_fully_busy) && -- (capacity_greater(sg->sgc->min_capacity, capacity_of(env->dst_cpu)))) -- return false; -- - return true; - } - -@@ -13018,9 +13033,24 @@ static struct rq *sched_balance_find_src_rq(struct lb_env *env, +@@ -13030,9 +13043,24 @@ static struct rq *sched_balance_find_src_rq(struct lb_env *env, * average load. */ if (env->sd->flags & SD_ASYM_CPUCAPACITY && diff --git a/pkgbuilds/linux-omarchy/0143-sched-hybrid-cluster-balancing.patch.sig b/pkgbuilds/linux-omarchy/0143-sched-hybrid-cluster-balancing.patch.sig index b191450..f125b5b 100644 Binary files a/pkgbuilds/linux-omarchy/0143-sched-hybrid-cluster-balancing.patch.sig and b/pkgbuilds/linux-omarchy/0143-sched-hybrid-cluster-balancing.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0144-sched-nohz-idle-core.patch b/pkgbuilds/linux-omarchy/0144-sched-nohz-idle-core.patch index 1fe001c..5af4cbd 100644 --- a/pkgbuilds/linux-omarchy/0144-sched-nohz-idle-core.patch +++ b/pkgbuilds/linux-omarchy/0144-sched-nohz-idle-core.patch @@ -1,7 +1,7 @@ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c -@@ -13924,29 +13924,62 @@ static inline int on_null_domain(struct rq *rq) +@@ -13934,29 +13934,62 @@ static inline int on_null_domain(struct rq *rq) */ static inline int find_new_ilb(void) { diff --git a/pkgbuilds/linux-omarchy/0144-sched-nohz-idle-core.patch.sig b/pkgbuilds/linux-omarchy/0144-sched-nohz-idle-core.patch.sig index 0a059ef..9ac0c5f 100644 Binary files a/pkgbuilds/linux-omarchy/0144-sched-nohz-idle-core.patch.sig and b/pkgbuilds/linux-omarchy/0144-sched-nohz-idle-core.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0200-idle.patch b/pkgbuilds/linux-omarchy/0200-idle.patch deleted file mode 100644 index dc499e5..0000000 --- a/pkgbuilds/linux-omarchy/0200-idle.patch +++ /dev/null @@ -1,61 +0,0 @@ -diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c ---- a/drivers/idle/intel_idle.c -+++ b/drivers/idle/intel_idle.c -@@ -53,6 +53,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -2697,6 +2698,9 @@ static void __init cmdline_table_adjust(struct cpuidle_driver *drv) - pr_info("Failed to adjust C-states with data from 'intel_idle.table'\n"); - } - -+#define INTEL_IDLE_INIT_QOS 20 -+static struct pm_qos_request qos_req __initdata; -+ - static int __init intel_idle_init(void) - { - const struct x86_cpu_id *id; -@@ -2766,6 +2770,13 @@ static int __init intel_idle_init(void) - if (retval) - pr_warn("failed to initialized sysfs"); - -+ /* -+ * Some platforms, in particular the Intel S1200BTL motherboard, have a -+ * problem with using package idle states too early, so prevent that -+ * from taking place until the device_initcall() phase is over. -+ */ -+ cpu_latency_qos_add_request(&qos_req, INTEL_IDLE_INIT_QOS); -+ - retval = cpuidle_register_driver(&intel_idle_driver); - if (retval) { - struct cpuidle_driver *drv = cpuidle_get_driver(); -@@ -2790,6 +2801,9 @@ static int __init intel_idle_init(void) - intel_idle_cpuidle_devices_uninit(); - cpuidle_unregister_driver(&intel_idle_driver); - init_driver_fail: -+ if (cpu_latency_qos_request_active((&qos_req))) -+ cpu_latency_qos_remove_request(&qos_req); -+ - intel_idle_sysfs_uninit(); - free_percpu(intel_idle_cpuidle_devices); - return retval; -@@ -2797,6 +2811,15 @@ static int __init intel_idle_init(void) - } - subsys_initcall_sync(intel_idle_init); - -+static int __init intel_idle_init_complete(void) -+{ -+ if (cpu_latency_qos_request_active((&qos_req))) -+ cpu_latency_qos_remove_request(&qos_req); -+ -+ return 0; -+} -+device_initcall_sync(intel_idle_init_complete); -+ - /* - * We are not really modular, but we used to support that. Meaning we also - * support "intel_idle.max_cstate=..." at boot and also a read-only export of diff --git a/pkgbuilds/linux-omarchy/0200-idle.patch.sig b/pkgbuilds/linux-omarchy/0200-idle.patch.sig deleted file mode 100644 index 5998b39..0000000 Binary files a/pkgbuilds/linux-omarchy/0200-idle.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/0210-pm-updates.patch b/pkgbuilds/linux-omarchy/0210-pm-updates.patch new file mode 100644 index 0000000..5110d06 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0210-pm-updates.patch @@ -0,0 +1,2365 @@ +diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c +--- a/drivers/acpi/acpi_processor.c ++++ b/drivers/acpi/acpi_processor.c +@@ -994,3 +994,357 @@ end: + } + EXPORT_SYMBOL_NS_GPL(acpi_processor_evaluate_cst, "ACPI_PROCESSOR_IDLE"); + #endif /* CONFIG_ACPI_PROCESSOR_CSTATE */ ++ ++#ifdef CONFIG_ACPI_PROCESSOR_IDLE ++struct acpi_lpi_states_array { ++ unsigned int size; ++ unsigned int composite_states_size; ++ struct acpi_lpi_state *entries; ++ struct acpi_lpi_state *composite_states[ACPI_PROCESSOR_MAX_POWER]; ++}; ++ ++static int obj_get_integer(union acpi_object *obj, u32 *value) ++{ ++ if (obj->type != ACPI_TYPE_INTEGER) ++ return -EINVAL; ++ ++ *value = obj->integer.value; ++ return 0; ++} ++ ++#define lpi_state_debug(handle, message, state_idx) \ ++ acpi_handle_debug(handle, message " for _LPI state %u\n", state_idx) ++ ++static void process_lpi_state_package(union acpi_object *lpi_pkg, ++ struct acpi_lpi_state *lpi_state, ++ acpi_handle handle, ++ unsigned int state_idx, bool strict) ++{ ++ union acpi_object *lpi_pkg_elem, *obj; ++ ++ if (lpi_pkg->type != ACPI_TYPE_PACKAGE || lpi_pkg->package.count < 7) ++ return; ++ ++ lpi_pkg_elem = lpi_pkg->package.elements; ++ ++ /* Get the entry method first and skip the state if that fails. */ ++ obj = &lpi_pkg_elem[6]; ++ if (obj->type == ACPI_TYPE_BUFFER) { ++ struct acpi_power_register *reg; ++ ++ if (obj->buffer.length < sizeof(*reg)) { ++ lpi_state_debug(handle, "Invalid register data", state_idx); ++ return; ++ } ++ ++ reg = (struct acpi_power_register *)obj->buffer.pointer; ++ if (reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) { ++ lpi_state_debug(handle, "Unsupported entry method", state_idx); ++ return; ++ } ++ ++ lpi_state->entry_method = ACPI_CSTATE_FFH; ++ lpi_state->address = reg->address; ++ } else if (obj->type == ACPI_TYPE_INTEGER) { ++ lpi_state->entry_method = ACPI_CSTATE_INTEGER; ++ lpi_state->address = obj->integer.value; ++ } else { ++ lpi_state_debug(handle, "Invalid entry method", state_idx); ++ return; ++ } ++ ++ if (obj_get_integer(&lpi_pkg_elem[0], &lpi_state->min_residency)) { ++ if (strict) { ++ lpi_state_debug(handle, "No min. residency", state_idx); ++ return; ++ } ++ ++ lpi_state_debug(handle, "Assuming 10 us min. residency", state_idx); ++ lpi_state->min_residency = 10; ++ } ++ ++ if (obj_get_integer(&lpi_pkg_elem[1], &lpi_state->wake_latency)) { ++ if (strict) { ++ lpi_state_debug(handle, "No wake latency", state_idx); ++ return; ++ } ++ ++ lpi_state_debug(handle, "Assuming 10 us wake latency", state_idx); ++ lpi_state->wake_latency = 10; ++ } ++ ++ if (obj_get_integer(&lpi_pkg_elem[2], &lpi_state->flags)) ++ lpi_state->flags = 0; ++ ++ if (obj_get_integer(&lpi_pkg_elem[3], &lpi_state->arch_flags)) ++ lpi_state->arch_flags = 0; ++ ++ if (obj_get_integer(&lpi_pkg_elem[4], &lpi_state->res_cnt_freq)) ++ lpi_state->res_cnt_freq = 1; ++ ++ if (obj_get_integer(&lpi_pkg_elem[5], &lpi_state->enable_parent_state)) ++ lpi_state->enable_parent_state = 0; ++ ++ /* Skip elements [7-8] i.e. Residency/Usage counters. */ ++ ++ /* ++ * Avoid out-of-bounds access if the size of the package is less than ++ * expected. ++ */ ++ if (lpi_pkg->package.count < 10) ++ return; ++ ++ obj = &lpi_pkg_elem[9]; ++ if (obj->type == ACPI_TYPE_STRING) ++ strscpy(lpi_state->desc, obj->string.pointer, ACPI_CX_DESC_LEN); ++} ++ ++static int acpi_processor_evaluate_lpi(acpi_handle handle, ++ struct acpi_lpi_states_array *info, ++ bool strict) ++{ ++ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; ++ union acpi_object *lpi_data, *lpi_pkg; ++ unsigned int lpi_pkg_count, state_idx; ++ struct acpi_lpi_state *lpi_state; ++ acpi_status status; ++ int ret = 0; ++ ++ status = acpi_evaluate_object(handle, "_LPI", NULL, &buffer); ++ if (ACPI_FAILURE(status)) { ++ acpi_handle_debug(handle, "No _LPI, giving up\n"); ++ return -ENODEV; ++ } ++ ++ lpi_data = buffer.pointer; ++ ++ /* There must be at least 4 elements = 3 elements + 1 package */ ++ if (!lpi_data || lpi_data->type != ACPI_TYPE_PACKAGE || ++ lpi_data->package.count < 4) { ++ acpi_handle_debug(handle, "Not enough elements in _LPI\n"); ++ ret = -ENODATA; ++ goto end; ++ } ++ ++ lpi_pkg_count = lpi_data->package.elements[2].integer.value; ++ ++ /* Validate number of power states. */ ++ if (!lpi_pkg_count || lpi_pkg_count != lpi_data->package.count - 3) { ++ acpi_handle_debug(handle, "Invalid _LPI state count\n"); ++ ret = -ENODATA; ++ goto end; ++ } ++ ++ lpi_state = kzalloc_objs(*lpi_state, lpi_pkg_count); ++ if (!lpi_state) { ++ ret = -ENOMEM; ++ goto end; ++ } ++ ++ info->size = lpi_pkg_count; ++ info->entries = lpi_state; ++ ++ /* _LPI State packages start at index 3. */ ++ lpi_pkg = &lpi_data->package.elements[3]; ++ ++ for (state_idx = 1; state_idx <= lpi_pkg_count; state_idx++) { ++ lpi_state->index = state_idx; ++ process_lpi_state_package(lpi_pkg++, lpi_state++, handle, ++ state_idx, strict); ++ } ++ ++ acpi_handle_debug(handle, "Found %u power states\n", lpi_pkg_count); ++end: ++ kfree(buffer.pointer); ++ return ret; ++} ++ ++/** ++ * combine_lpi_states - combine local and parent LPI states to form a composite LPI state ++ * ++ * @local: local LPI state ++ * @parent: parent LPI state ++ * @result: composite LPI state ++ */ ++static bool combine_lpi_states(struct acpi_lpi_state *local, ++ struct acpi_lpi_state *parent, ++ struct acpi_lpi_state *result) ++{ ++ if (parent->entry_method == ACPI_CSTATE_INTEGER) { ++ if (!parent->address) /* 0 means autopromotable */ ++ return false; ++ result->address = local->address + parent->address; ++ } else { ++ result->address = parent->address; ++ } ++ ++ result->min_residency = max(local->min_residency, parent->min_residency); ++ result->wake_latency = local->wake_latency + parent->wake_latency; ++ result->enable_parent_state = parent->enable_parent_state; ++ result->entry_method = local->entry_method; ++ ++ result->flags = parent->flags; ++ result->arch_flags = parent->arch_flags; ++ result->index = parent->index; ++ ++ scnprintf(result->desc, ACPI_CX_DESC_LEN, "%s+%s", local->desc, parent->desc); ++ return true; ++} ++ ++#define ACPI_LPI_STATE_FLAGS_ENABLED BIT(0) ++ ++static void stash_composite_state(struct acpi_lpi_states_array *curr_level, ++ struct acpi_lpi_state *t) ++{ ++ curr_level->composite_states[curr_level->composite_states_size++] = t; ++} ++ ++static bool too_many_states(acpi_handle handle, unsigned int state_count) ++{ ++ if (state_count < ACPI_PROCESSOR_MAX_POWER) ++ return false; ++ ++ acpi_handle_info(handle, "No space for more _LPI states than %d\n", ++ ACPI_PROCESSOR_MAX_POWER); ++ return true; ++} ++ ++static unsigned int flatten_lpi_states(acpi_handle handle, ++ struct acpi_lpi_state *lpi_states, ++ unsigned int state_count, ++ struct acpi_lpi_states_array *curr, ++ struct acpi_lpi_states_array *prev) ++{ ++ struct acpi_lpi_state *parent_lpi = curr->entries; ++ unsigned int j; ++ ++ /* ++ * Combine each of the "raw" _LPI states from the current (processor ++ * container) level with all of the composite _LPI states from the ++ * previous (processor or processor container) level. ++ */ ++ for (j = 0; j < curr->size; j++, parent_lpi++) { ++ struct acpi_lpi_state *flpi; ++ int i; ++ ++ if (!(parent_lpi->flags & ACPI_LPI_STATE_FLAGS_ENABLED)) ++ continue; ++ ++ if (too_many_states(handle, state_count)) ++ break; ++ ++ flpi = &lpi_states[state_count]; ++ ++ for (i = 0; i < prev->composite_states_size; i++) { ++ struct acpi_lpi_state *local_lpi = prev->composite_states[i]; ++ ++ if (parent_lpi->index > local_lpi->enable_parent_state) ++ continue; ++ ++ if (!combine_lpi_states(local_lpi, parent_lpi, flpi)) ++ continue; ++ ++ stash_composite_state(curr, flpi); ++ state_count++; ++ flpi++; ++ ++ if (state_count >= ACPI_PROCESSOR_MAX_POWER) ++ break; ++ } ++ } ++ ++ return state_count; ++} ++ ++int acpi_processor_extract_lpi_info(acpi_handle pr_handle, ++ struct acpi_processor_power *pr_power, ++ bool strict) ++{ ++ struct acpi_lpi_states_array info[2], *prev, *curr; ++ acpi_handle handle = pr_handle; ++ unsigned int state_count = 0; ++ unsigned int i; ++ int ret; ++ ++ if (!osc_pc_lpi_support_confirmed) ++ return -EOPNOTSUPP; ++ ++ curr = &info[0]; ++ curr->composite_states_size = 0; ++ ++ ret = acpi_processor_evaluate_lpi(handle, curr, strict); ++ if (ret) ++ return ret; ++ ++ /* Copy all of the usable first-level states to power.lpi_states[]. */ ++ for (i = 0; i < curr->size; i++) { ++ struct acpi_lpi_state *lpi = &curr->entries[i]; ++ struct acpi_lpi_state *flpi; ++ ++ /* ++ * Skip states that are not enabled or have an inadequate entry ++ * method for this level. ++ */ ++ if (!(lpi->flags & ACPI_LPI_STATE_FLAGS_ENABLED) || ++ lpi->entry_method == ACPI_CSTATE_INTEGER) ++ continue; ++ ++ if (too_many_states(pr_handle, state_count)) ++ break; ++ ++ flpi = &pr_power->lpi_states[state_count++]; ++ memcpy(flpi, lpi, sizeof(*lpi)); ++ stash_composite_state(curr, flpi); ++ } ++ ++ kfree(curr->entries); ++ ++ /* ++ * If there are no _LPI states at the first level, there are no _LPI ++ * states at all. ++ */ ++ if (!state_count) ++ return -ENODATA; ++ ++ prev = curr; ++ curr = &info[1]; ++ ++ for (;;) { ++ struct acpi_lpi_states_array *tmp; ++ struct acpi_device *d; ++ ++ if (ACPI_FAILURE(acpi_get_parent(handle, &handle))) ++ break; ++ ++ d = acpi_fetch_acpi_dev(handle); ++ if (!d) ++ break; ++ ++ if (strcmp(acpi_device_hid(d), ACPI_PROCESSOR_CONTAINER_HID)) ++ break; ++ ++ curr->composite_states_size = 0; ++ ++ ret = acpi_processor_evaluate_lpi(handle, curr, strict); ++ if (ret) ++ break; ++ ++ /* flatten all the LPI states in this level of hierarchy */ ++ state_count = flatten_lpi_states(pr_handle, pr_power->lpi_states, ++ state_count, curr, prev); ++ ++ kfree(curr->entries); ++ ++ tmp = prev, prev = curr, curr = tmp; ++ } ++ ++ /* reset the index after flattening */ ++ for (i = 0; i < state_count; i++) ++ pr_power->lpi_states[i].index = i; ++ ++ pr_power->count = state_count; ++ ++ return 0; ++} ++EXPORT_SYMBOL_NS_GPL(acpi_processor_extract_lpi_info, "ACPI_PROCESSOR_IDLE"); ++#endif /* CONFIG_ACPI_PROCESSOR_IDLE */ +diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c +--- a/drivers/acpi/processor_idle.c ++++ b/drivers/acpi/processor_idle.c +@@ -853,237 +853,6 @@ static int acpi_processor_setup_cstates(struct acpi_processor *pr) + + #endif /* CONFIG_ACPI_PROCESSOR_CSTATE */ + +-struct acpi_lpi_states_array { +- unsigned int size; +- unsigned int composite_states_size; +- struct acpi_lpi_state *entries; +- struct acpi_lpi_state *composite_states[ACPI_PROCESSOR_MAX_POWER]; +-}; +- +-static int obj_get_integer(union acpi_object *obj, u32 *value) +-{ +- if (obj->type != ACPI_TYPE_INTEGER) +- return -EINVAL; +- +- *value = obj->integer.value; +- return 0; +-} +- +-static int acpi_processor_evaluate_lpi(acpi_handle handle, +- struct acpi_lpi_states_array *info) +-{ +- acpi_status status; +- int ret = 0; +- int pkg_count, state_idx = 1, loop; +- struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; +- union acpi_object *lpi_data; +- struct acpi_lpi_state *lpi_state; +- +- status = acpi_evaluate_object(handle, "_LPI", NULL, &buffer); +- if (ACPI_FAILURE(status)) { +- acpi_handle_debug(handle, "No _LPI, giving up\n"); +- return -ENODEV; +- } +- +- lpi_data = buffer.pointer; +- +- /* There must be at least 4 elements = 3 elements + 1 package */ +- if (!lpi_data || lpi_data->type != ACPI_TYPE_PACKAGE || +- lpi_data->package.count < 4) { +- pr_debug("not enough elements in _LPI\n"); +- ret = -ENODATA; +- goto end; +- } +- +- pkg_count = lpi_data->package.elements[2].integer.value; +- +- /* Validate number of power states. */ +- if (pkg_count < 1 || pkg_count != lpi_data->package.count - 3) { +- pr_debug("count given by _LPI is not valid\n"); +- ret = -ENODATA; +- goto end; +- } +- +- lpi_state = kzalloc_objs(*lpi_state, pkg_count); +- if (!lpi_state) { +- ret = -ENOMEM; +- goto end; +- } +- +- info->size = pkg_count; +- info->entries = lpi_state; +- +- /* LPI States start at index 3 */ +- for (loop = 3; state_idx <= pkg_count; loop++, state_idx++, lpi_state++) { +- union acpi_object *element, *pkg_elem, *obj; +- +- element = &lpi_data->package.elements[loop]; +- if (element->type != ACPI_TYPE_PACKAGE || element->package.count < 7) +- continue; +- +- pkg_elem = element->package.elements; +- +- obj = pkg_elem + 6; +- if (obj->type == ACPI_TYPE_BUFFER) { +- struct acpi_power_register *reg; +- +- if (obj->buffer.length < sizeof(*reg)) { +- acpi_handle_debug(handle, +- "Invalid register data for _LPI state %d\n", +- state_idx); +- continue; +- } +- +- reg = (struct acpi_power_register *)obj->buffer.pointer; +- if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO && +- reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) +- continue; +- +- lpi_state->address = reg->address; +- lpi_state->entry_method = +- reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE ? +- ACPI_CSTATE_FFH : ACPI_CSTATE_SYSTEMIO; +- } else if (obj->type == ACPI_TYPE_INTEGER) { +- lpi_state->entry_method = ACPI_CSTATE_INTEGER; +- lpi_state->address = obj->integer.value; +- } else { +- pr_debug("Entry method of state-%d is invalid, disable it.\n", +- state_idx); +- continue; +- } +- +- lpi_state->index = state_idx; +- if (obj_get_integer(pkg_elem + 0, &lpi_state->min_residency)) { +- pr_debug("No min. residency found, assuming 10 us\n"); +- lpi_state->min_residency = 10; +- } +- +- if (obj_get_integer(pkg_elem + 1, &lpi_state->wake_latency)) { +- pr_debug("No wakeup residency found, assuming 10 us\n"); +- lpi_state->wake_latency = 10; +- } +- +- if (obj_get_integer(pkg_elem + 2, &lpi_state->flags)) +- lpi_state->flags = 0; +- +- if (obj_get_integer(pkg_elem + 3, &lpi_state->arch_flags)) +- lpi_state->arch_flags = 0; +- +- if (obj_get_integer(pkg_elem + 4, &lpi_state->res_cnt_freq)) +- lpi_state->res_cnt_freq = 1; +- +- if (obj_get_integer(pkg_elem + 5, &lpi_state->enable_parent_state)) +- lpi_state->enable_parent_state = 0; +- +- /* Skip elements [7-8] i.e. Residency/Usage counters. */ +- +- /* +- * Avoid out-of-bounds access if the size of the package is less +- * than expected. +- */ +- if (element->package.count < 10) +- continue; +- +- obj = pkg_elem + 9; +- if (obj->type == ACPI_TYPE_STRING) +- strscpy(lpi_state->desc, obj->string.pointer, +- ACPI_CX_DESC_LEN); +- } +- +- acpi_handle_debug(handle, "Found %d power states\n", state_idx); +-end: +- kfree(buffer.pointer); +- return ret; +-} +- +-/** +- * combine_lpi_states - combine local and parent LPI states to form a composite LPI state +- * +- * @local: local LPI state +- * @parent: parent LPI state +- * @result: composite LPI state +- */ +-static bool combine_lpi_states(struct acpi_lpi_state *local, +- struct acpi_lpi_state *parent, +- struct acpi_lpi_state *result) +-{ +- if (parent->entry_method == ACPI_CSTATE_INTEGER) { +- if (!parent->address) /* 0 means autopromotable */ +- return false; +- result->address = local->address + parent->address; +- } else { +- result->address = parent->address; +- } +- +- result->min_residency = max(local->min_residency, parent->min_residency); +- result->wake_latency = local->wake_latency + parent->wake_latency; +- result->enable_parent_state = parent->enable_parent_state; +- result->entry_method = local->entry_method; +- +- result->flags = parent->flags; +- result->arch_flags = parent->arch_flags; +- result->index = parent->index; +- +- scnprintf(result->desc, ACPI_CX_DESC_LEN, "%s+%s", local->desc, parent->desc); +- return true; +-} +- +-#define ACPI_LPI_STATE_FLAGS_ENABLED BIT(0) +- +-static void stash_composite_state(struct acpi_lpi_states_array *curr_level, +- struct acpi_lpi_state *t) +-{ +- curr_level->composite_states[curr_level->composite_states_size++] = t; +-} +- +-static unsigned int flatten_lpi_states(struct acpi_processor *pr, +- unsigned int flat_state_cnt, +- struct acpi_lpi_states_array *curr_level, +- struct acpi_lpi_states_array *prev_level) +-{ +- int i, j, state_count = curr_level->size; +- struct acpi_lpi_state *p, *t = curr_level->entries; +- +- curr_level->composite_states_size = 0; +- for (j = 0; j < state_count; j++, t++) { +- struct acpi_lpi_state *flpi; +- +- if (!(t->flags & ACPI_LPI_STATE_FLAGS_ENABLED)) +- continue; +- +- if (flat_state_cnt >= ACPI_PROCESSOR_MAX_POWER) { +- pr_warn("Limiting number of LPI states to max (%d)\n", +- ACPI_PROCESSOR_MAX_POWER); +- pr_warn("Please increase ACPI_PROCESSOR_MAX_POWER if needed.\n"); +- break; +- } +- +- flpi = &pr->power.lpi_states[flat_state_cnt]; +- +- if (!prev_level) { /* leaf/processor node */ +- memcpy(flpi, t, sizeof(*t)); +- stash_composite_state(curr_level, flpi); +- flat_state_cnt++; +- continue; +- } +- +- for (i = 0; i < prev_level->composite_states_size; i++) { +- p = prev_level->composite_states[i]; +- if (t->index <= p->enable_parent_state && +- combine_lpi_states(p, t, flpi)) { +- stash_composite_state(curr_level, flpi); +- flat_state_cnt++; +- flpi++; +- if (flat_state_cnt >= ACPI_PROCESSOR_MAX_POWER) +- break; +- } +- } +- } +- +- kfree(curr_level->entries); +- return flat_state_cnt; +-} +- + int __weak acpi_processor_ffh_lpi_probe(unsigned int cpu) + { + return -EOPNOTSUPP; +@@ -1091,64 +860,16 @@ int __weak acpi_processor_ffh_lpi_probe(unsigned int cpu) + + static int acpi_processor_get_lpi_info(struct acpi_processor *pr) + { +- int ret, i; +- acpi_status status; +- acpi_handle handle = pr->handle, pr_ahandle; +- struct acpi_device *d = NULL; +- struct acpi_lpi_states_array info[2], *tmp, *prev, *curr; +- unsigned int state_count; ++ int ret; + + /* make sure our architecture has support */ + ret = acpi_processor_ffh_lpi_probe(pr->id); + if (ret == -EOPNOTSUPP) + return ret; + +- if (!osc_pc_lpi_support_confirmed) +- return -EOPNOTSUPP; +- +- if (!acpi_has_method(handle, "_LPI")) +- return -EINVAL; +- +- prev = &info[0]; +- curr = &info[1]; +- handle = pr->handle; +- ret = acpi_processor_evaluate_lpi(handle, prev); ++ ret = acpi_processor_extract_lpi_info(pr->handle, &pr->power, false); + if (ret) + return ret; +- state_count = flatten_lpi_states(pr, 0, prev, NULL); +- +- status = acpi_get_parent(handle, &pr_ahandle); +- while (ACPI_SUCCESS(status)) { +- d = acpi_fetch_acpi_dev(pr_ahandle); +- if (!d) +- break; +- +- handle = pr_ahandle; +- +- if (strcmp(acpi_device_hid(d), ACPI_PROCESSOR_CONTAINER_HID)) +- break; +- +- /* can be optional ? */ +- if (!acpi_has_method(handle, "_LPI")) +- break; +- +- ret = acpi_processor_evaluate_lpi(handle, curr); +- if (ret) +- break; +- +- /* flatten all the LPI states in this level of hierarchy */ +- state_count = flatten_lpi_states(pr, state_count, curr, prev); +- +- tmp = prev, prev = curr, curr = tmp; +- +- status = acpi_get_parent(handle, &pr_ahandle); +- } +- +- /* reset the index after flattening */ +- for (i = 0; i < state_count; i++) +- pr->power.lpi_states[i].index = i; +- +- pr->power.count = state_count; + + /* Tell driver that _LPI is supported. */ + pr->flags.has_lpi = 1; +diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c +--- a/drivers/base/power/main.c ++++ b/drivers/base/power/main.c +@@ -41,6 +41,9 @@ + #include "../base.h" + #include "power.h" + ++#undef MODULE_PARAM_PREFIX ++#define MODULE_PARAM_PREFIX "pm." ++ + typedef int (*pm_callback_t)(struct device *); + + /* +@@ -532,6 +535,11 @@ module_param(dpm_watchdog_all_cpu_backtrace, bool, 0644); + MODULE_PARM_DESC(dpm_watchdog_all_cpu_backtrace, + "Backtrace all CPUs on DPM watchdog timeout"); + ++static bool __read_mostly dpm_watchdog_enabled = ++ IS_ENABLED(CONFIG_DPM_WATCHDOG_ENABLED); ++module_param(dpm_watchdog_enabled, bool, 0644); ++MODULE_PARM_DESC(dpm_watchdog_enabled, "Enable DPM watchdog"); ++ + static unsigned int __read_mostly dpm_watchdog_timeout = CONFIG_DPM_WATCHDOG_TIMEOUT; + static unsigned int __read_mostly dpm_watchdog_warning_timeout = + CONFIG_DPM_WATCHDOG_WARNING_TIMEOUT; +@@ -627,6 +635,9 @@ static void dpm_watchdog_set(struct dpm_watchdog *wd, struct device *dev) + { + struct timer_list *timer = &wd->timer; + ++ if (!dpm_watchdog_enabled) ++ return; ++ + wd->dev = dev; + wd->tsk = current; + wd->fatal = dpm_watchdog_timeout == dpm_watchdog_warning_timeout; +@@ -645,6 +656,9 @@ static void dpm_watchdog_clear(struct dpm_watchdog *wd) + { + struct timer_list *timer = &wd->timer; + ++ if (!dpm_watchdog_enabled) ++ return; ++ + timer_delete_sync(timer); + timer_destroy_on_stack(timer); + } +diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c +--- a/drivers/base/power/runtime.c ++++ b/drivers/base/power/runtime.c +@@ -469,9 +469,6 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev) + if (retval == -EACCES) + retval = -EAGAIN; + +- if (retval != -EAGAIN && retval != -EBUSY) +- dev->power.runtime_error = retval; +- + return retval; + } + +@@ -751,6 +748,9 @@ static int rpm_suspend(struct device *dev, int rpmflags) + dev->power.deferred_resume = false; + wake_up_all(&dev->power.wait_queue); + ++ if (retval != -EAGAIN && retval != -EBUSY) ++ dev->power.runtime_error = retval; ++ + /* + * On transient errors, if the callback routine failed an autosuspend, + * and if the last_busy time has been updated so that there is a new +diff --git a/drivers/cpufreq/amd-pstate-ut.c b/drivers/cpufreq/amd-pstate-ut.c +--- a/drivers/cpufreq/amd-pstate-ut.c ++++ b/drivers/cpufreq/amd-pstate-ut.c +@@ -59,6 +59,7 @@ static int amd_pstate_ut_check_freq(u32 index); + static int amd_pstate_ut_epp(u32 index); + static int amd_pstate_ut_check_driver(u32 index); + static int amd_pstate_ut_check_freq_attrs(u32 index); ++static int amd_pstate_ut_check_floor_freq(u32 index); + + static struct amd_pstate_ut_struct amd_pstate_ut_cases[] = { + {"amd_pstate_ut_acpi_cpc_valid", amd_pstate_ut_acpi_cpc_valid }, +@@ -68,6 +69,7 @@ static struct amd_pstate_ut_struct amd_pstate_ut_cases[] = { + {"amd_pstate_ut_epp", amd_pstate_ut_epp }, + {"amd_pstate_ut_check_driver", amd_pstate_ut_check_driver }, + {"amd_pstate_ut_check_freq_attrs", amd_pstate_ut_check_freq_attrs }, ++ {"amd_pstate_ut_check_floor_freq", amd_pstate_ut_check_floor_freq }, + }; + + static bool test_in_list(const char *list, const char *name) +@@ -275,6 +277,7 @@ static int amd_pstate_set_mode(enum amd_pstate_mode mode) + static int amd_pstate_ut_epp(u32 index) + { + static const char * const epp_strings[] = { ++ "dynamic", + "power", + "balance_power", + "balance_performance", +@@ -282,10 +285,10 @@ static int amd_pstate_ut_epp(u32 index) + }; + char *buf __free(cleanup_page) = NULL; + struct cpufreq_policy *policy = NULL; ++ unsigned long orig_dynamic_epp = 0; + enum amd_pstate_mode orig_mode; + struct amd_cpudata *cpudata; + unsigned long orig_policy; +- bool orig_dynamic_epp; + int ret, cpu = 0; + u16 epp; + int i; +@@ -294,9 +297,11 @@ static int amd_pstate_ut_epp(u32 index) + if (!policy) + return -ENODEV; + +- cpudata = policy->driver_data; + orig_mode = amd_pstate_get_status(); +- orig_dynamic_epp = cpudata->dynamic_epp; ++ if (policy->driver_data) { ++ cpudata = policy->driver_data; ++ orig_dynamic_epp = cpudata->dynamic_epp; ++ } + + /* Drop reference before potential driver change. */ + cpufreq_cpu_put(policy); +@@ -321,16 +326,6 @@ static int amd_pstate_ut_epp(u32 index) + orig_policy = cpudata->policy; + cpudata->policy = CPUFREQ_POLICY_POWERSAVE; + +- /* +- * Disable dynamic EPP before running test. If "orig_dynamic_epp" is +- * true, the driver will do a redundant switch at the end and there +- * is no need for enabling it again at the end of the test. +- */ +- if (cpudata->dynamic_epp) { +- pr_debug("Dynamic EPP is enabled, disabling it\n"); +- amd_pstate_clear_dynamic_epp(policy); +- } +- + for (epp = 0; epp <= U8_MAX; epp++) { + u8 val; + +@@ -367,6 +362,11 @@ static int amd_pstate_ut_epp(u32 index) + if (ret < 0) + goto out; + strreplace(buf, '\n', '\0'); ++ /* ++ * "dynamic" mode reports the EPP as "dynamic(profile:X)" ++ * Trim at "(" and just compare tie the epp string. ++ */ ++ strreplace(buf, '(', '\0'); + + if (strcmp(buf, epp_strings[i])) { + pr_err("String EPP value mismatch: %s != %s\n", buf, epp_strings[i]); +@@ -380,18 +380,23 @@ static int amd_pstate_ut_epp(u32 index) + out: + if (policy) { + cpudata->policy = orig_policy; ++ /* ++ * If the driver had enabled dynamic_epp to begin with, ++ * restore it here before dropping policy reference. ++ */ ++ if (orig_dynamic_epp) { ++ int ret2; ++ ++ ret2 = store_energy_performance_preference(policy, ++ epp_strings[0], ++ strlen(epp_strings[0])); ++ if (!ret && (ret2 < 0)) ++ ret = ret2; ++ } + up_write(&policy->rwsem); + cpufreq_cpu_put(policy); + } + +- if (orig_dynamic_epp) { +- int ret2; +- +- ret2 = amd_pstate_set_mode(AMD_PSTATE_DISABLE); +- if (!ret && ret2) +- ret = ret2; +- } +- + if (orig_mode != amd_pstate_get_status()) { + int ret2; + +@@ -557,6 +562,80 @@ out: + return ret; + } + ++static int amd_pstate_ut_check_floor_freq(u32 index) ++{ ++ struct cpufreq_policy *policy __free(put_cpufreq_policy) = NULL; ++ char *buf __free(cleanup_page) = NULL; ++ unsigned int orig_floor_freq; ++ unsigned int floor_freq; ++ int ret, cpu = 0; ++ ++ if (!cpu_feature_enabled(X86_FEATURE_CPPC_PERF_PRIO)) ++ return -EOPNOTSUPP; ++ ++ policy = cpufreq_cpu_get(cpu); ++ if (!policy) ++ return -ENODEV; ++ ++ buf = (char *)__get_free_page(GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ guard(rwsem_write)(&policy->rwsem); ++ ++ if (!policy->driver_data) ++ return -ENODEV; ++ ++ /* Retrieve original floor frequency */ ++ memset(buf, 0, PAGE_SIZE); ++ ret = show_amd_pstate_floor_freq(policy, buf); ++ if (ret < 0) ++ return ret; ++ ++ ret = kstrtou32(buf, 0, &orig_floor_freq); ++ if (ret) ++ return ret; ++ ++ memset(buf, 0, PAGE_SIZE); ++ snprintf(buf, PAGE_SIZE, "%u", policy->cpuinfo.min_freq); ++ ++ /* Set floor frequency to cpuinfo.min_freq */ ++ ret = store_amd_pstate_floor_freq(policy, buf, strlen(buf)); ++ if (ret < 0) { ++ pr_err("Failed to set floor frequency to %s\n", buf); ++ return ret; ++ } ++ ++ memset(buf, 0, PAGE_SIZE); ++ ret = show_amd_pstate_floor_freq(policy, buf); ++ if (ret < 0) ++ return ret; ++ ++ strreplace(buf, '\n', '\0'); ++ ret = kstrtou32(buf, 0, &floor_freq); ++ if (ret) ++ return ret; ++ ++ /* Confirm sysfs reflects the change correctly. */ ++ if (floor_freq != policy->cpuinfo.min_freq) { ++ pr_err("Floor frequency value mismatch: %u != %u\n", ++ floor_freq, policy->cpuinfo.min_freq); ++ return -EINVAL; ++ } ++ ++ memset(buf, 0, PAGE_SIZE); ++ snprintf(buf, PAGE_SIZE, "%u", orig_floor_freq); ++ ++ /* Restore the original value. */ ++ ret = store_amd_pstate_floor_freq(policy, buf, strlen(buf)); ++ if (ret < 0) { ++ pr_err("Failed to restore floor frequency to %s\n", buf); ++ return ret; ++ } ++ ++ return 0; ++} ++ + static int __init amd_pstate_ut_init(void) + { + u32 i = 0, arr_size = ARRAY_SIZE(amd_pstate_ut_cases); +@@ -575,10 +654,16 @@ static int __init amd_pstate_ut_init(void) + + ret = amd_pstate_ut_cases[i].func(i); + +- if (ret) ++ if (ret) { ++ /* Platform does not support the feature being tested. */ ++ if (ret == -EOPNOTSUPP) { ++ pr_err("%-4d %-20s\t skipped!\n", i+1, amd_pstate_ut_cases[i].name); ++ continue; ++ } + pr_err("%-4d %-20s\t fail: %d!\n", i+1, amd_pstate_ut_cases[i].name, ret); +- else ++ } else { + pr_info("%-4d %-20s\t success!\n", i+1, amd_pstate_ut_cases[i].name); ++ } + } + + return 0; +diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c +--- a/drivers/cpufreq/amd-pstate.c ++++ b/drivers/cpufreq/amd-pstate.c +@@ -75,7 +75,7 @@ const char *amd_pstate_get_mode_string(enum amd_pstate_mode mode) + mode = AMD_PSTATE_UNDEFINED; + return amd_pstate_mode_string[mode]; + } +-EXPORT_SYMBOL_GPL(amd_pstate_get_mode_string); ++EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_get_mode_string); + + struct quirk_entry { + u32 nominal_freq; +@@ -87,7 +87,6 @@ static struct cpufreq_driver amd_pstate_driver; + static struct cpufreq_driver amd_pstate_epp_driver; + static int cppc_state = AMD_PSTATE_UNDEFINED; + static bool amd_pstate_prefcore = true; +-static bool dynamic_epp; + static struct quirk_entry *quirks; + + /* +@@ -106,6 +105,7 @@ static struct quirk_entry *quirks; + * 3 balance_power + * 4 power + * 5 custom (for raw EPP values) ++ * 6 dynamic (platform profile driven selection) + */ + enum energy_perf_value_index { + EPP_INDEX_DEFAULT = 0, +@@ -114,6 +114,7 @@ enum energy_perf_value_index { + EPP_INDEX_BALANCE_POWERSAVE, + EPP_INDEX_POWERSAVE, + EPP_INDEX_CUSTOM, ++ EPP_INDEX_DYNAMIC, + EPP_INDEX_MAX, + }; + +@@ -124,6 +125,7 @@ static const char * const energy_perf_strings[] = { + [EPP_INDEX_BALANCE_POWERSAVE] = "balance_power", + [EPP_INDEX_POWERSAVE] = "power", + [EPP_INDEX_CUSTOM] = "custom", ++ [EPP_INDEX_DYNAMIC] = "dynamic", + }; + static_assert(ARRAY_SIZE(energy_perf_strings) == EPP_INDEX_MAX); + +@@ -134,7 +136,7 @@ static unsigned int epp_values[] = { + [EPP_INDEX_BALANCE_POWERSAVE] = AMD_CPPC_EPP_BALANCE_POWERSAVE, + [EPP_INDEX_POWERSAVE] = AMD_CPPC_EPP_POWERSAVE, + }; +-static_assert(ARRAY_SIZE(epp_values) == EPP_INDEX_MAX - 1); ++static_assert(ARRAY_SIZE(epp_values) == EPP_INDEX_MAX - 2); + + typedef int (*cppc_mode_transition_fn)(int); + +@@ -462,7 +464,6 @@ static int msr_init_perf(struct amd_cpudata *cpudata) + { + union perf_cached perf = READ_ONCE(cpudata->perf); + u64 cap1, numerator, cppc_req; +- u8 min_perf; + + int ret = rdmsrq_safe_on_cpu(cpudata->cpu, MSR_AMD_CPPC_CAP1, + &cap1); +@@ -478,16 +479,6 @@ static int msr_init_perf(struct amd_cpudata *cpudata) + return ret; + + WRITE_ONCE(cpudata->cppc_req_cached, cppc_req); +- min_perf = FIELD_GET(AMD_CPPC_MIN_PERF_MASK, cppc_req); +- +- /* +- * Clear out the min_perf part to check if the rest of the MSR is 0, if yes, this is an +- * indication that the min_perf value is the one specified through the BIOS option +- */ +- cppc_req &= ~(AMD_CPPC_MIN_PERF_MASK); +- +- if (!cppc_req) +- perf.bios_min_perf = min_perf; + + perf.highest_perf = numerator; + perf.max_limit_perf = numerator; +@@ -495,6 +486,7 @@ static int msr_init_perf(struct amd_cpudata *cpudata) + perf.nominal_perf = FIELD_GET(AMD_CPPC_NOMINAL_PERF_MASK, cap1); + perf.lowest_nonlinear_perf = FIELD_GET(AMD_CPPC_LOWNONLIN_PERF_MASK, cap1); + perf.lowest_perf = FIELD_GET(AMD_CPPC_LOWEST_PERF_MASK, cap1); ++ perf.bios_min_perf = FIELD_GET(AMD_CPPC_MIN_PERF_MASK, cppc_req); + WRITE_ONCE(cpudata->perf, perf); + WRITE_ONCE(cpudata->prefcore_ranking, FIELD_GET(AMD_CPPC_HIGHEST_PERF_MASK, cap1)); + WRITE_ONCE(cpudata->floor_perf_cnt, FIELD_GET(AMD_CPPC_FLOOR_PERF_CNT_MASK, cap1)); +@@ -789,6 +781,7 @@ static unsigned int amd_pstate_fast_switch(struct cpufreq_policy *policy, + static void amd_pstate_adjust_perf(struct cpufreq_policy *policy, + unsigned long _min_perf, + unsigned long target_perf, ++ unsigned long _max_perf, + unsigned long capacity) + { + u8 max_perf, min_perf, des_perf, cap_perf; +@@ -1044,6 +1037,13 @@ static int amd_pstate_init_freq(struct amd_cpudata *cpudata) + return -EINVAL; + } + ++ if (perf.bios_min_perf) { ++ u32 bios_min_freq = perf_to_freq(perf, cpudata->nominal_freq, perf.bios_min_perf); ++ ++ pr_debug("Found Requested CPU Min Frequency of %uKHz on CPU%d\n", ++ bios_min_freq, cpudata->cpu); ++ } ++ + return 0; + } + +@@ -1192,6 +1192,24 @@ static int amd_pstate_power_supply_notifier(struct notifier_block *nb, + return NOTIFY_OK; + } + ++static int amd_pstate_get_epp_from_platform_profile(struct cpufreq_policy *policy, ++ enum platform_profile_option profile) ++{ ++ switch (profile) { ++ case PLATFORM_PROFILE_PERFORMANCE: ++ return AMD_CPPC_EPP_PERFORMANCE; ++ case PLATFORM_PROFILE_BALANCED: ++ return amd_pstate_get_balanced_epp(policy); ++ case PLATFORM_PROFILE_LOW_POWER: ++ return AMD_CPPC_EPP_POWERSAVE; ++ default: ++ break; ++ } ++ ++ pr_err("Unknown Platform Profile %d\n", profile); ++ return -EOPNOTSUPP; ++} ++ + static int amd_pstate_profile_probe(void *drvdata, unsigned long *choices) + { + set_bit(PLATFORM_PROFILE_LOW_POWER, choices); +@@ -1217,31 +1235,19 @@ static int amd_pstate_profile_set(struct device *dev, + struct amd_cpudata *cpudata = dev_get_drvdata(dev); + struct cpufreq_policy *policy __free(put_cpufreq_policy) = cpufreq_cpu_get(cpudata->cpu); + int ret; ++ u8 epp; + + if (!policy) + return -ENODEV; + +- switch (profile) { +- case PLATFORM_PROFILE_LOW_POWER: +- ret = amd_pstate_set_epp(policy, AMD_CPPC_EPP_POWERSAVE); +- if (ret) +- return ret; +- break; +- case PLATFORM_PROFILE_BALANCED: +- ret = amd_pstate_set_epp(policy, +- amd_pstate_get_balanced_epp(policy)); +- if (ret) +- return ret; +- break; +- case PLATFORM_PROFILE_PERFORMANCE: +- ret = amd_pstate_set_epp(policy, AMD_CPPC_EPP_PERFORMANCE); +- if (ret) +- return ret; +- break; +- default: +- pr_err("Unknown Platform Profile %d\n", profile); +- return -EOPNOTSUPP; +- } ++ ret = amd_pstate_get_epp_from_platform_profile(policy, profile); ++ if (ret < 0) ++ return ret; ++ ++ epp = (u8)ret; ++ ret = amd_pstate_set_epp(policy, epp); ++ if (ret) ++ return ret; + + cpudata->current_profile = profile; + +@@ -1267,28 +1273,20 @@ void amd_pstate_clear_dynamic_epp(struct cpufreq_policy *policy) + kfree(cpudata->profile_name); + cpudata->dynamic_epp = false; + } +-EXPORT_SYMBOL_GPL(amd_pstate_clear_dynamic_epp); ++EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_clear_dynamic_epp); + + static int amd_pstate_set_dynamic_epp(struct cpufreq_policy *policy) + { + struct amd_cpudata *cpudata = policy->driver_data; ++ u64 prev = READ_ONCE(cpudata->cppc_req_cached); + int ret; + u8 epp; + +- switch (cpudata->current_profile) { +- case PLATFORM_PROFILE_PERFORMANCE: +- epp = AMD_CPPC_EPP_PERFORMANCE; +- break; +- case PLATFORM_PROFILE_LOW_POWER: +- epp = AMD_CPPC_EPP_POWERSAVE; +- break; +- case PLATFORM_PROFILE_BALANCED: +- epp = amd_pstate_get_balanced_epp(policy); +- break; +- default: +- pr_err("Unknown Platform Profile %d\n", cpudata->current_profile); +- return -EOPNOTSUPP; +- } ++ ret = amd_pstate_get_epp_from_platform_profile(policy, cpudata->current_profile); ++ if (ret < 0) ++ return ret; ++ ++ epp = (u8)ret; + ret = amd_pstate_set_epp(policy, epp); + if (ret) + return ret; +@@ -1321,6 +1319,9 @@ static int amd_pstate_set_dynamic_epp(struct cpufreq_policy *policy) + cleanup: + amd_pstate_clear_dynamic_epp(policy); + ++ epp = FIELD_GET(AMD_CPPC_EPP_PERF_MASK, prev); ++ /* Restore previous EPP if toggling Dynamic EPP failed. */ ++ amd_pstate_set_epp(policy, epp); + return ret; + } + +@@ -1391,7 +1392,7 @@ static ssize_t show_amd_pstate_hw_prefcore(struct cpufreq_policy *policy, + static ssize_t show_energy_performance_available_preferences( + struct cpufreq_policy *policy, char *buf) + { +- int offset = 0, i; ++ int i, offset = 0; + struct amd_cpudata *cpudata = policy->driver_data; + + if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) +@@ -1414,11 +1415,6 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy, + bool raw_epp = false; + u8 epp; + +- if (cpudata->dynamic_epp) { +- pr_debug("EPP cannot be set when dynamic EPP is enabled\n"); +- return -EBUSY; +- } +- + /* + * if the value matches a number, use that, otherwise see if + * matches an index in the energy_perf_strings array +@@ -1429,6 +1425,25 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy, + ret = sysfs_match_string(energy_perf_strings, buf); + if (ret < 0 || ret == EPP_INDEX_CUSTOM) + return -EINVAL; ++ ++ if (ret == EPP_INDEX_DYNAMIC) { ++ if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) ++ return -EBUSY; ++ /* ++ * Dynamic EPP was already enabled for this CPU. ++ * Nothing to do. ++ */ ++ if (cpudata->dynamic_epp) ++ return count; ++ ++ cpudata->current_profile = PLATFORM_PROFILE_BALANCED; ++ ret = amd_pstate_set_dynamic_epp(policy); ++ if (ret) ++ return ret; ++ ++ return count; ++ } ++ + if (ret) + epp = epp_values[ret]; + else +@@ -1440,6 +1455,13 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy, + return -EBUSY; + } + ++ /* ++ * Dynamic EPP was enabled previously! ++ * Switch back to the static EPP mode. ++ */ ++ if (cpudata->dynamic_epp) ++ amd_pstate_clear_dynamic_epp(policy); ++ + ret = amd_pstate_set_epp(policy, epp); + if (ret) + return ret; +@@ -1448,7 +1470,7 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy, + + return count; + } +-EXPORT_SYMBOL_GPL(store_energy_performance_preference); ++EXPORT_SYMBOL_FOR_PSTATE_UT(store_energy_performance_preference); + + ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *buf) + { +@@ -1457,7 +1479,7 @@ ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char * + + epp = FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cpudata->cppc_req_cached); + +- if (cpudata->raw_epp) ++ if (!cpudata->dynamic_epp && cpudata->raw_epp) + return sysfs_emit(buf, "%u\n", epp); + + switch (epp) { +@@ -1477,12 +1499,14 @@ ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char * + return -EINVAL; + } + ++ if (cpudata->dynamic_epp) ++ return sysfs_emit(buf, "dynamic(profile:%s)\n", energy_perf_strings[preference]); ++ + return sysfs_emit(buf, "%s\n", energy_perf_strings[preference]); + } +-EXPORT_SYMBOL_GPL(show_energy_performance_preference); ++EXPORT_SYMBOL_FOR_PSTATE_UT(show_energy_performance_preference); + +-static ssize_t store_amd_pstate_floor_freq(struct cpufreq_policy *policy, +- const char *buf, size_t count) ++ssize_t store_amd_pstate_floor_freq(struct cpufreq_policy *policy, const char *buf, size_t count) + { + struct amd_cpudata *cpudata = policy->driver_data; + union perf_cached perf = READ_ONCE(cpudata->perf); +@@ -1505,13 +1529,15 @@ static ssize_t store_amd_pstate_floor_freq(struct cpufreq_policy *policy, + + return ret ?: count; + } ++EXPORT_SYMBOL_FOR_PSTATE_UT(store_amd_pstate_floor_freq); + +-static ssize_t show_amd_pstate_floor_freq(struct cpufreq_policy *policy, char *buf) ++ssize_t show_amd_pstate_floor_freq(struct cpufreq_policy *policy, char *buf) + { + struct amd_cpudata *cpudata = policy->driver_data; + + return sysfs_emit(buf, "%u\n", cpudata->floor_freq); + } ++EXPORT_SYMBOL_FOR_PSTATE_UT(show_amd_pstate_floor_freq); + + static ssize_t show_amd_pstate_floor_count(struct cpufreq_policy *policy, char *buf) + { +@@ -1579,7 +1605,7 @@ struct freq_attr **amd_pstate_get_current_attrs(void) + return NULL; + return current_pstate_driver->attr; + } +-EXPORT_SYMBOL_GPL(amd_pstate_get_current_attrs); ++EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_get_current_attrs); + + static struct freq_attr **get_freq_attrs(void) + { +@@ -1764,7 +1790,7 @@ int amd_pstate_get_status(void) + { + return cppc_state; + } +-EXPORT_SYMBOL_GPL(amd_pstate_get_status); ++EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_get_status); + + int amd_pstate_update_status(const char *buf, size_t size) + { +@@ -1784,7 +1810,7 @@ int amd_pstate_update_status(const char *buf, size_t size) + + return 0; + } +-EXPORT_SYMBOL_GPL(amd_pstate_update_status); ++EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_update_status); + + static ssize_t status_show(struct device *dev, + struct device_attribute *attr, char *buf) +@@ -1812,50 +1838,12 @@ static ssize_t prefcore_show(struct device *dev, + return sysfs_emit(buf, "%s\n", str_enabled_disabled(amd_pstate_prefcore)); + } + +-static ssize_t dynamic_epp_show(struct device *dev, +- struct device_attribute *attr, char *buf) +-{ +- return sysfs_emit(buf, "%s\n", str_enabled_disabled(dynamic_epp)); +-} +- +-static ssize_t dynamic_epp_store(struct device *a, struct device_attribute *b, +- const char *buf, size_t count) +-{ +- bool enabled; +- int ret; +- +- ret = kstrtobool(buf, &enabled); +- if (ret) +- return ret; +- +- guard(mutex)(&amd_pstate_driver_lock); +- +- if (cppc_state != AMD_PSTATE_ACTIVE) { +- pr_debug("dynamic_epp can only be toggled in active mode\n"); +- return -EINVAL; +- } +- +- /* Nothing to do */ +- if (dynamic_epp == enabled) +- return count; +- +- /* reinitialize with desired dynamic EPP value */ +- dynamic_epp = enabled; +- ret = amd_pstate_change_driver_mode(cppc_state); +- if (ret) +- dynamic_epp = false; +- +- return ret ? ret : count; +-} +- + static DEVICE_ATTR_RW(status); + static DEVICE_ATTR_RO(prefcore); +-static DEVICE_ATTR_RW(dynamic_epp); + + static struct attribute *pstate_global_attributes[] = { + &dev_attr_status.attr, + &dev_attr_prefcore.attr, +- &dev_attr_dynamic_epp.attr, + NULL + }; + +@@ -1938,12 +1926,13 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) + + policy->boost_supported = READ_ONCE(cpudata->boost_supported); + +- /* Fetch the firmware programmed default EPP value */ ++ /* Cache the firmware programmed EPP */ + default_epp = amd_pstate_get_epp(cpudata); + if (default_epp < 0) { + ret = default_epp; + goto free_cpudata1; + } ++ FIELD_MODIFY(AMD_CPPC_EPP_PERF_MASK, &cpudata->cppc_req_cached, default_epp); + + /* + * Set the policy to provide a valid fallback value in case +@@ -1961,10 +1950,7 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) + cpudata->current_profile = PLATFORM_PROFILE_BALANCED; + } + +- if (dynamic_epp) +- ret = amd_pstate_set_dynamic_epp(policy); +- else +- ret = amd_pstate_set_epp(policy, cpudata->epp_default_dc); ++ ret = amd_pstate_set_epp(policy, cpudata->epp_default_dc); + if (ret) + goto free_cpudata1; + +@@ -2036,6 +2022,18 @@ static int amd_pstate_epp_set_policy(struct cpufreq_policy *policy) + if (!policy->cpuinfo.max_freq) + return -ENODEV; + ++ /* Must be a switch between PERFORMANCE and POWERSAVE */ ++ if (cpudata->policy != policy->policy) { ++ /* ++ * Disable dynamic_epp when switching ++ * out of CPUFREQ_POLICY_POWERSAVE. ++ */ ++ if (cpudata->dynamic_epp) { ++ WARN_ON_ONCE(cpudata->policy != CPUFREQ_POLICY_POWERSAVE); ++ amd_pstate_clear_dynamic_epp(policy); ++ } ++ } ++ + cpudata->policy = policy->policy; + + ret = amd_pstate_epp_update_limit(policy, true); +@@ -2363,19 +2361,8 @@ static int __init amd_prefcore_param(char *str) + return 0; + } + +-static int __init amd_dynamic_epp_param(char *str) +-{ +- if (!strcmp(str, "disable")) +- dynamic_epp = false; +- if (!strcmp(str, "enable")) +- dynamic_epp = true; +- +- return 0; +-} +- + early_param("amd_pstate", amd_pstate_param); + early_param("amd_prefcore", amd_prefcore_param); +-early_param("amd_dynamic_epp", amd_dynamic_epp_param); + + MODULE_AUTHOR("Huang Rui "); + MODULE_DESCRIPTION("AMD Processor P-state Frequency Driver"); +diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h +--- a/drivers/cpufreq/amd-pstate.h ++++ b/drivers/cpufreq/amd-pstate.h +@@ -11,6 +11,13 @@ + #include + #include + ++#if IS_MODULE(CONFIG_X86_AMD_PSTATE_UT) ++#define EXPORT_SYMBOL_FOR_PSTATE_UT(symbol) \ ++ EXPORT_SYMBOL_FOR_MODULES(symbol, "amd-pstate-ut") ++#else ++#define EXPORT_SYMBOL_FOR_PSTATE_UT(symbol) ++#endif ++ + /********************************************************************* + * AMD P-state INTERFACE * + *********************************************************************/ +@@ -153,6 +160,8 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy, + const char *buf, size_t count); + ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *buf); + void amd_pstate_clear_dynamic_epp(struct cpufreq_policy *policy); ++ssize_t store_amd_pstate_floor_freq(struct cpufreq_policy *policy, const char *buf, size_t count); ++ssize_t show_amd_pstate_floor_freq(struct cpufreq_policy *policy, char *buf); + + struct freq_attr; + +diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c +--- a/drivers/cpufreq/cpufreq.c ++++ b/drivers/cpufreq/cpufreq.c +@@ -2225,14 +2225,17 @@ EXPORT_SYMBOL_GPL(cpufreq_driver_fast_switch); + * @policy: cpufreq policy object of the target CPU. + * @min_perf: Minimum (required) performance level (units of @capacity). + * @target_perf: Target (desired) performance level (units of @capacity). ++ * @max_perf: Maximum (allowed) performance level (units of @capacity). + * @capacity: Capacity of the target CPU. + * +- * Carry out a fast performance level switch of @cpu without sleeping. ++ * Carry out a fast performance level adjustment for the CPU represented by ++ * @policy without sleeping. + * + * The driver's ->adjust_perf() callback invoked by this function must be +- * suitable for being called from within RCU-sched read-side critical sections +- * and it is expected to select a suitable performance level equal to or above +- * @min_perf and preferably equal to or below @target_perf. ++ * suitable for calling from within RCU-sched read-side critical sections and ++ * it is expected to program the processor to select suitable performance ++ * levels between @min_perf and @max_perf inclusive and preferably close to ++ * @target_perf going forward for the CPU represented by @policy. + * + * This function must not be called if policy->fast_switch_enabled is unset. + * +@@ -2244,9 +2247,10 @@ EXPORT_SYMBOL_GPL(cpufreq_driver_fast_switch); + void cpufreq_driver_adjust_perf(struct cpufreq_policy *policy, + unsigned long min_perf, + unsigned long target_perf, ++ unsigned long max_perf, + unsigned long capacity) + { +- cpufreq_driver->adjust_perf(policy, min_perf, target_perf, capacity); ++ cpufreq_driver->adjust_perf(policy, min_perf, target_perf, max_perf, capacity); + } + + /** +diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c +--- a/drivers/cpufreq/intel_pstate.c ++++ b/drivers/cpufreq/intel_pstate.c +@@ -299,7 +299,6 @@ struct pstate_funcs { + static struct pstate_funcs pstate_funcs __read_mostly; + + static bool hwp_active __ro_after_init; +-static int hwp_mode_bdw __ro_after_init; + static bool per_cpu_limits __ro_after_init; + static bool hwp_forced __ro_after_init; + static bool hwp_boost __read_mostly; +@@ -587,21 +586,14 @@ static void intel_pstate_hybrid_hwp_adjust(struct cpudata *cpu) + + hwp_is_hybrid = true; + +- cpu->pstate.turbo_freq = rounddown(cpu->pstate.turbo_pstate * scaling, +- perf_ctl_scaling); +- cpu->pstate.max_freq = rounddown(cpu->pstate.max_pstate * scaling, +- perf_ctl_scaling); +- + freq = perf_ctl_max_phys * perf_ctl_scaling; + cpu->pstate.max_pstate_physical = intel_pstate_freq_to_hwp(cpu, freq); + +- freq = cpu->pstate.min_pstate * perf_ctl_scaling; +- cpu->pstate.min_freq = freq; + /* + * Cast the min P-state value retrieved via pstate_funcs.get_min() to + * the effective range of HWP performance levels. + */ +- cpu->pstate.min_pstate = intel_pstate_freq_to_hwp(cpu, freq); ++ cpu->pstate.min_pstate = intel_pstate_freq_to_hwp(cpu, cpu->pstate.min_freq); + } + + static bool turbo_is_disabled(void) +@@ -979,12 +971,10 @@ static int hybrid_get_cost(struct device *dev, unsigned long freq, + * capacity. Similarly, P-cores start to be populated when E-cores are + * utilized above 60% of the capacity. + */ +- if (hybrid_get_cpu_type(dev->id) == INTEL_CPU_TYPE_ATOM) { +- if (hybrid_has_l3(dev->id)) /* E-core */ +- *cost += 1; +- } else { /* P-core */ ++ if (hybrid_get_cpu_type(dev->id) == INTEL_CPU_TYPE_CORE) /* P-core */ + *cost += 2; +- } ++ else if (hybrid_has_l3(dev->id)) /* E-core */ ++ *cost += 1; + + return 0; + } +@@ -1185,6 +1175,22 @@ static bool hybrid_clear_max_perf_cpu(void) + return ret; + } + ++static void intel_pstate_update_freq_limits(struct cpudata *cpu) ++{ ++ int scaling = cpu->pstate.scaling; ++ unsigned int turbo_freq = cpu->pstate.turbo_pstate * scaling; ++ unsigned int max_freq = cpu->pstate.max_pstate * scaling; ++ int perf_ctl_scaling = cpu->pstate.perf_ctl_scaling; ++ ++ if (scaling != perf_ctl_scaling) { ++ turbo_freq = rounddown(turbo_freq, perf_ctl_scaling); ++ max_freq = rounddown(max_freq, perf_ctl_scaling); ++ } ++ ++ cpu->pstate.turbo_freq = turbo_freq; ++ cpu->pstate.max_freq = max_freq; ++} ++ + static void __intel_pstate_get_hwp_cap(struct cpudata *cpu) + { + u64 cap; +@@ -1197,20 +1203,8 @@ static void __intel_pstate_get_hwp_cap(struct cpudata *cpu) + + static void intel_pstate_get_hwp_cap(struct cpudata *cpu) + { +- int scaling = cpu->pstate.scaling; +- + __intel_pstate_get_hwp_cap(cpu); +- +- cpu->pstate.max_freq = cpu->pstate.max_pstate * scaling; +- cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * scaling; +- if (scaling != cpu->pstate.perf_ctl_scaling) { +- int perf_ctl_scaling = cpu->pstate.perf_ctl_scaling; +- +- cpu->pstate.max_freq = rounddown(cpu->pstate.max_freq, +- perf_ctl_scaling); +- cpu->pstate.turbo_freq = rounddown(cpu->pstate.turbo_freq, +- perf_ctl_scaling); +- } ++ intel_pstate_update_freq_limits(cpu); + } + + static void hybrid_update_capacity(struct cpudata *cpu) +@@ -2299,33 +2293,16 @@ static int hwp_get_cpu_scaling(int cpu) + return intel_pstate_cppc_get_scaling(cpu); + } + +-static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate) +-{ +- trace_cpu_frequency(pstate * cpu->pstate.scaling, cpu->cpu); +- cpu->pstate.current_pstate = pstate; +- /* +- * Generally, there is no guarantee that this code will always run on +- * the CPU being updated, so force the register update to run on the +- * right CPU. +- */ +- wrmsrq_on_cpu(cpu->cpu, MSR_IA32_PERF_CTL, +- pstate_funcs.get_val(cpu, pstate)); +-} +- +-static void intel_pstate_set_min_pstate(struct cpudata *cpu) +-{ +- intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate); +-} +- + static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) + { + int perf_ctl_scaling = pstate_funcs.get_scaling(); + + cpu->pstate.max_pstate_physical = pstate_funcs.get_max_physical(cpu->cpu); + cpu->pstate.min_pstate = pstate_funcs.get_min(cpu->cpu); ++ cpu->pstate.min_freq = cpu->pstate.min_pstate * perf_ctl_scaling; + cpu->pstate.perf_ctl_scaling = perf_ctl_scaling; + +- if (hwp_active && !hwp_mode_bdw) { ++ if (hwp_active) { + __intel_pstate_get_hwp_cap(cpu); + + if (pstate_funcs.get_cpu_scaling) { +@@ -2345,11 +2322,7 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) + cpu->pstate.turbo_pstate = pstate_funcs.get_turbo(cpu->cpu); + } + +- if (cpu->pstate.scaling == perf_ctl_scaling) { +- cpu->pstate.min_freq = cpu->pstate.min_pstate * perf_ctl_scaling; +- cpu->pstate.max_freq = cpu->pstate.max_pstate * perf_ctl_scaling; +- cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * perf_ctl_scaling; +- } ++ intel_pstate_update_freq_limits(cpu); + + if (pstate_funcs.get_aperf_mperf_shift) + cpu->aperf_mperf_shift = pstate_funcs.get_aperf_mperf_shift(); +@@ -2882,8 +2855,22 @@ static void intel_pstate_update_perf_limits(struct cpudata *cpu, + cpu->min_perf_ratio); + } + ++static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate) ++{ ++ trace_cpu_frequency(pstate * cpu->pstate.scaling, cpu->cpu); ++ cpu->pstate.current_pstate = pstate; ++ /* ++ * Generally, there is no guarantee that this code will always run on ++ * the CPU being updated, so force the register update to run on the ++ * right CPU. ++ */ ++ wrmsrq_on_cpu(cpu->cpu, MSR_IA32_PERF_CTL, ++ pstate_funcs.get_val(cpu, pstate)); ++} ++ + static int intel_pstate_set_policy(struct cpufreq_policy *policy) + { ++ unsigned int freq = policy->min; + struct cpudata *cpu; + + if (!policy->cpuinfo.max_freq) +@@ -2899,7 +2886,23 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy) + + intel_pstate_update_perf_limits(cpu, policy->min, policy->max); + +- if (cpu->policy == CPUFREQ_POLICY_PERFORMANCE) { ++ if (hwp_active) { ++ /* ++ * The active mode only requires an update util hook if HWP ++ * boost is used and the policy is not "performance". ++ */ ++ if (hwp_boost && cpu->policy != CPUFREQ_POLICY_PERFORMANCE) { ++ intel_pstate_set_update_util_hook(policy->cpu); ++ } else { ++ intel_pstate_clear_update_util_hook(policy->cpu); ++ if (cpu->policy == CPUFREQ_POLICY_PERFORMANCE) { ++ freq = cpu->max_perf_ratio * cpu->pstate.scaling; ++ if (cpu->pstate.scaling != cpu->pstate.perf_ctl_scaling) ++ freq = rounddown(freq, cpu->pstate.perf_ctl_scaling); ++ } ++ } ++ intel_pstate_hwp_set(policy->cpu); ++ } else if (cpu->policy == CPUFREQ_POLICY_PERFORMANCE) { + int pstate = max(cpu->pstate.min_pstate, cpu->max_perf_ratio); + + /* +@@ -2908,25 +2911,17 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy) + */ + intel_pstate_clear_update_util_hook(policy->cpu); + intel_pstate_set_pstate(cpu, pstate); ++ freq = pstate * cpu->pstate.scaling; + } else { + intel_pstate_set_update_util_hook(policy->cpu); + } +- +- if (hwp_active) { +- /* +- * When hwp_boost was active before and dynamically it +- * was turned off, in that case we need to clear the +- * update util hook. +- */ +- if (!hwp_boost) +- intel_pstate_clear_update_util_hook(policy->cpu); +- intel_pstate_hwp_set(policy->cpu); +- } + /* +- * policy->cur is never updated with the intel_pstate driver, but it +- * is used as a stale frequency value. So, keep it within limits. ++ * policy->cur is never updated in the intel_pstate driver, but it is ++ * used as a stale frequency value, so set it to reflect the actual ++ * requested P-state in the "performance" policy case and to the min ++ * otherwise. + */ +- policy->cur = policy->min; ++ policy->cur = freq; + + mutex_unlock(&intel_pstate_limits_lock); + +@@ -2969,6 +2964,11 @@ static int intel_pstate_verify_policy(struct cpufreq_policy_data *policy) + return 0; + } + ++static void intel_pstate_set_min_pstate(struct cpudata *cpu) ++{ ++ intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate); ++} ++ + static int intel_cpufreq_cpu_offline(struct cpufreq_policy *policy) + { + struct cpudata *cpu = all_cpu_data[policy->cpu]; +@@ -3242,6 +3242,7 @@ static unsigned int intel_cpufreq_fast_switch(struct cpufreq_policy *policy, + static void intel_cpufreq_adjust_perf(struct cpufreq_policy *policy, + unsigned long min_perf, + unsigned long target_perf, ++ unsigned long max_perf, + unsigned long capacity) + { + struct cpudata *cpu = all_cpu_data[policy->cpu]; +@@ -3272,7 +3273,13 @@ static void intel_cpufreq_adjust_perf(struct cpufreq_policy *policy, + if (min_pstate > cpu->max_perf_ratio) + min_pstate = cpu->max_perf_ratio; + +- max_pstate = min(cap_pstate, cpu->max_perf_ratio); ++ max_pstate = cap_pstate; ++ if (max_perf < capacity) ++ max_pstate = DIV_ROUND_UP(cap_pstate * max_perf, capacity); ++ ++ if (max_pstate > cpu->max_perf_ratio) ++ max_pstate = cpu->max_perf_ratio; ++ + if (max_pstate < min_pstate) + max_pstate = min_pstate; + +@@ -3679,14 +3686,14 @@ static inline bool intel_pstate_has_acpi_ppc(void) { return false; } + static inline void intel_pstate_request_control_from_smm(void) {} + #endif /* CONFIG_ACPI */ + +-#define INTEL_PSTATE_HWP_BROADWELL 0x01 ++#define INTEL_PSTATE_HWP_NOT_HYBRID 0x01 + + #define X86_MATCH_HWP(vfm, hwp_mode) \ + X86_MATCH_VFM_FEATURE(vfm, X86_FEATURE_HWP, hwp_mode) + + static const struct x86_cpu_id hwp_support_ids[] __initconst = { +- X86_MATCH_HWP(INTEL_BROADWELL_X, INTEL_PSTATE_HWP_BROADWELL), +- X86_MATCH_HWP(INTEL_BROADWELL_D, INTEL_PSTATE_HWP_BROADWELL), ++ X86_MATCH_HWP(INTEL_BROADWELL_X, INTEL_PSTATE_HWP_NOT_HYBRID), ++ X86_MATCH_HWP(INTEL_BROADWELL_D, INTEL_PSTATE_HWP_NOT_HYBRID), + X86_MATCH_HWP(INTEL_ANY, 0), + {} + }; +@@ -3811,7 +3818,6 @@ static int __init intel_pstate_init(void) + + if (!no_hwp) { + hwp_active = true; +- hwp_mode_bdw = id->driver_data; + intel_pstate.attr = hwp_cpufreq_attrs; + intel_cpufreq.attr = hwp_cpufreq_attrs; + intel_cpufreq.flags |= CPUFREQ_NEED_UPDATE_LIMITS; +@@ -3819,7 +3825,8 @@ static int __init intel_pstate_init(void) + if (!default_driver) + default_driver = &intel_pstate; + +- pstate_funcs.get_cpu_scaling = hwp_get_cpu_scaling; ++ if (!id->driver_data) ++ pstate_funcs.get_cpu_scaling = hwp_get_cpu_scaling; + + goto hwp_cpu_matched; + } +diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c +--- a/drivers/idle/intel_idle.c ++++ b/drivers/idle/intel_idle.c +@@ -1780,6 +1780,7 @@ module_param_named(no_native, no_native, bool, 0444); + MODULE_PARM_DESC(no_native, "Ignore cpu specific (native) idle states in lieu of ACPI idle states"); + + static struct acpi_processor_power acpi_state_table __initdata; ++static bool acpi_lpi_available __initdata; + + /** + * intel_idle_cst_usable - Check if the _CST information can be used. +@@ -1804,18 +1805,37 @@ static bool __init intel_idle_cst_usable(void) + return true; + } + +-static bool __init intel_idle_acpi_cst_extract(void) ++static bool __init intel_idle_acpi_extract_lpi_cstates(void) + { + unsigned int cpu; + +- if (no_acpi) { +- pr_debug("Not allowed to use ACPI _CST\n"); +- return false; ++ for_each_possible_cpu(cpu) { ++ struct acpi_processor *pr; ++ ++ pr = per_cpu(processors, cpu); ++ if (!pr) ++ continue; ++ ++ if (acpi_processor_extract_lpi_info(pr->handle, ++ &acpi_state_table, true)) ++ continue; ++ ++ acpi_lpi_available = true; ++ return true; + } + +- for_each_possible_cpu(cpu) { +- struct acpi_processor *pr = per_cpu(processors, cpu); ++ pr_debug("No ACPI _LPI idle states\n"); ++ return false; ++} + ++static bool __init intel_idle_acpi_extract_cst_cstates(void) ++{ ++ unsigned int cpu; ++ ++ for_each_possible_cpu(cpu) { ++ struct acpi_processor *pr; ++ ++ pr = per_cpu(processors, cpu); + if (!pr) + continue; + +@@ -1827,18 +1847,96 @@ static bool __init intel_idle_acpi_cst_extract(void) + if (!intel_idle_cst_usable()) + continue; + +- if (!acpi_processor_claim_cst_control()) +- break; +- + return true; + } + +- acpi_state_table.count = 0; + pr_debug("ACPI _CST not found or not usable\n"); + return false; + } + +-static void __init intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) ++static bool __init intel_idle_acpi_extract_cstates(void) ++{ ++ if (intel_idle_acpi_extract_lpi_cstates()) ++ return true; ++ ++ if (intel_idle_acpi_extract_cst_cstates()) ++ return true; ++ ++ return false; ++} ++ ++static bool __init intel_idle_acpi_probe(void) ++{ ++ if (no_acpi) { ++ pr_debug("Not allowed to use ACPI for C-states extraction\n"); ++ return false; ++ } ++ ++ if (intel_idle_acpi_extract_cstates() && ++ acpi_processor_claim_cst_control()) ++ return true; ++ ++ acpi_state_table.count = 0; ++ return false; ++} ++ ++static void __init intel_idle_complete_state_init(struct cpuidle_state *state) ++{ ++ if (intel_idle_state_needs_timer_stop(state)) ++ state->flags |= CPUIDLE_FLAG_TIMER_STOP; ++ ++ state->enter = intel_idle; ++ state->enter_dead = intel_idle_enter_dead; ++ state->enter_s2idle = intel_idle_s2idle; ++} ++ ++static void __init intel_idle_init_cstates_acpi_lpi(struct cpuidle_driver *drv) ++{ ++ int index; ++ ++ for (index = 0; index < acpi_state_table.count; index++) { ++ struct acpi_lpi_state *lpi_state; ++ struct cpuidle_state *state; ++ ++ if (intel_idle_max_cstate_reached(index)) ++ break; ++ ++ lpi_state = &acpi_state_table.lpi_states[index]; ++ ++ state = &drv->states[drv->state_count++]; ++ ++ scnprintf(state->name, CPUIDLE_NAME_LEN, "C%d_LPI", index + 1); ++ strscpy(state->desc, lpi_state->desc, CPUIDLE_DESC_LEN); ++ state->exit_latency = lpi_state->wake_latency; ++ state->target_residency = lpi_state->min_residency; ++ state->flags = MWAIT2flg(lpi_state->address); ++ /* ++ * Assume that entering any of the idle states extracted from ++ * _LPI except for the first two will cause the TLB to be ++ * flushed and let the core call leave_mm() for them upfront ++ * to avoid unnecessary wakeups due to TLB shootdowns. ++ */ ++ if (index > 1) ++ state->flags |= CPUIDLE_FLAG_TLB_FLUSHED; ++ ++ if (disabled_states_mask & BIT(index + 1)) ++ state->flags |= CPUIDLE_FLAG_OFF; ++ ++ intel_idle_complete_state_init(state); ++ ++ pr_info("%s: MWAIT hint 0x%x\n", state->name, flg2MWAIT(state->flags)); ++ } ++ ++ /* ++ * Assume the first idle state in the table to be C1 and if any deeper ++ * idle states are exposed while X86_FEATURE_NONSTOP_TSC is unset, mark ++ * the TSC as unstable. ++ */ ++ if (index > 1 && !boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) ++ mark_tsc_unstable("TSC halts in idle"); ++} ++ ++static void __init intel_idle_init_cstates_acpi_cst(struct cpuidle_driver *drv) + { + int cstate, limit = min_t(int, CPUIDLE_STATE_MAX, acpi_state_table.count); + +@@ -1880,49 +1978,76 @@ static void __init intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) + if (disabled_states_mask & BIT(cstate)) + state->flags |= CPUIDLE_FLAG_OFF; + +- if (intel_idle_state_needs_timer_stop(state)) +- state->flags |= CPUIDLE_FLAG_TIMER_STOP; +- + if (cx->type > ACPI_STATE_C1 && !boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) + mark_tsc_unstable("TSC halts in idle"); + +- state->enter = intel_idle; +- state->enter_dead = intel_idle_enter_dead; +- state->enter_s2idle = intel_idle_s2idle; ++ intel_idle_complete_state_init(state); + } + } + +-static bool __init intel_idle_off_by_default(unsigned int flags, u32 mwait_hint) ++static void __init intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) + { +- int cstate, limit; ++ if (acpi_lpi_available) ++ intel_idle_init_cstates_acpi_lpi(drv); ++ else ++ intel_idle_init_cstates_acpi_cst(drv); ++} + +- /* +- * If there are no _CST C-states, do not disable any C-states by +- * default. +- */ +- if (!acpi_state_table.count) +- return false; ++static bool __init intel_idle_acpi_hint_match(unsigned int flags, u32 acpi_hint, ++ u32 table_hint) ++{ ++ if (flags & CPUIDLE_FLAG_PARTIAL_HINT_MATCH) { ++ acpi_hint &= ~MWAIT_SUBSTATE_MASK; ++ table_hint &= ~MWAIT_SUBSTATE_MASK; ++ } ++ return acpi_hint == table_hint; ++} ++ ++static bool __init intel_idle_off_by_default_lpi(unsigned int flags, u32 mwait_hint) ++{ ++ int index; ++ ++ for (index = 0; index < acpi_state_table.count; index++) { ++ u32 acpi_hint = acpi_state_table.lpi_states[index].address; ++ ++ if (intel_idle_acpi_hint_match(flags, acpi_hint, mwait_hint)) ++ return false; ++ } ++ return true; ++} ++ ++static bool __init intel_idle_off_by_default_cst(unsigned int flags, u32 mwait_hint) ++{ ++ int cstate, limit = min_t(int, CPUIDLE_STATE_MAX, acpi_state_table.count); + +- limit = min_t(int, CPUIDLE_STATE_MAX, acpi_state_table.count); + /* + * If limit > 0, intel_idle_cst_usable() has returned 'true', so all of + * the interesting states are ACPI_CSTATE_FFH. + */ + for (cstate = 1; cstate < limit; cstate++) { + u32 acpi_hint = acpi_state_table.states[cstate].address; +- u32 table_hint = mwait_hint; + +- if (flags & CPUIDLE_FLAG_PARTIAL_HINT_MATCH) { +- acpi_hint &= ~MWAIT_SUBSTATE_MASK; +- table_hint &= ~MWAIT_SUBSTATE_MASK; +- } +- +- if (acpi_hint == table_hint) ++ if (intel_idle_acpi_hint_match(flags, acpi_hint, mwait_hint)) + return false; + } + return true; + } + ++static bool __init intel_idle_off_by_default(unsigned int flags, u32 mwait_hint) ++{ ++ /* ++ * If there is no C-states information in the ACPI tables, do not ++ * disable any C-states by default. ++ */ ++ if (!acpi_state_table.count) ++ return false; ++ ++ if (acpi_lpi_available) ++ return intel_idle_off_by_default_lpi(flags, mwait_hint); ++ ++ return intel_idle_off_by_default_cst(flags, mwait_hint); ++} ++ + static inline bool ignore_native(void) + { + return no_native && !no_acpi; +@@ -1930,7 +2055,7 @@ static inline bool ignore_native(void) + #else /* !CONFIG_ACPI_PROCESSOR_CSTATE */ + #define force_use_acpi (false) + +-static inline bool intel_idle_acpi_cst_extract(void) { return false; } ++static inline bool intel_idle_acpi_probe(void) { return false; } + static inline void intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) { } + static inline bool intel_idle_off_by_default(unsigned int flags, u32 mwait_hint) + { +@@ -2745,7 +2870,7 @@ static int __init intel_idle_init(void) + if (icpu) { + if (icpu->state_table) + cpuidle_state_table = icpu->state_table; +- else if (!intel_idle_acpi_cst_extract()) ++ else if (!intel_idle_acpi_probe()) + return -ENODEV; + + auto_demotion_disable_flags = icpu->auto_demotion_disable_flags; +@@ -2754,8 +2879,8 @@ static int __init intel_idle_init(void) + if (icpu->c1_demotion_supported) + c1_demotion_supported = true; + if (icpu->use_acpi || force_use_acpi) +- intel_idle_acpi_cst_extract(); +- } else if (!intel_idle_acpi_cst_extract()) { ++ intel_idle_acpi_probe(); ++ } else if (!intel_idle_acpi_probe()) { + return -ENODEV; + } + +diff --git a/drivers/opp/core.c b/drivers/opp/core.c +--- a/drivers/opp/core.c ++++ b/drivers/opp/core.c +@@ -1581,8 +1581,6 @@ static struct opp_table *_update_opp_table_clk(struct device *dev, + struct opp_table *opp_table, + bool getclk) + { +- int ret; +- + /* + * Return early if we don't need to get clk or we have already done it + * earlier. +@@ -1591,39 +1589,35 @@ static struct opp_table *_update_opp_table_clk(struct device *dev, + opp_table->clks) + return opp_table; + +- /* Find clk for the device */ +- opp_table->clk = clk_get(dev, NULL); ++ /* ++ * There are few platforms which don't want the OPP core to manage ++ * device's clock settings. In such cases neither the platform ++ * provides the clks explicitly to us, nor the DT contains a valid ++ * clk entry. The OPP nodes in DT may still contain "opp-hz" property ++ * though, which we need to parse and allow the platform to find an ++ * OPP based on freq later on. ++ * ++ * This is a simple solution to take care of such corner cases, i.e. ++ * make the clk_count 1, which lets us allocate space for frequency ++ * in opp->rates and also parse the entries in DT. Use ++ * clk_get_optional() instead of clk_get() so opp_table->clk stays ++ * NULL for such devices, instead of holding an ERR_PTR(-ENOENT) that ++ * consumers must remember to special-case. ++ */ ++ opp_table->clk = clk_get_optional(dev, NULL); + +- ret = PTR_ERR_OR_ZERO(opp_table->clk); +- if (!ret) { ++ if (IS_ERR(opp_table->clk)) { ++ dev_pm_opp_put_opp_table(opp_table); ++ dev_err_probe(dev, PTR_ERR(opp_table->clk), "Couldn't find clock\n"); ++ return ERR_CAST(opp_table->clk); ++ } ++ ++ if (opp_table->clk) + opp_table->config_clks = _opp_config_clk_single; +- opp_table->clk_count = 1; +- return opp_table; +- } + +- if (ret == -ENOENT) { +- /* +- * There are few platforms which don't want the OPP core to +- * manage device's clock settings. In such cases neither the +- * platform provides the clks explicitly to us, nor the DT +- * contains a valid clk entry. The OPP nodes in DT may still +- * contain "opp-hz" property though, which we need to parse and +- * allow the platform to find an OPP based on freq later on. +- * +- * This is a simple solution to take care of such corner cases, +- * i.e. make the clk_count 1, which lets us allocate space for +- * frequency in opp->rates and also parse the entries in DT. +- */ +- opp_table->clk_count = 1; ++ opp_table->clk_count = 1; + +- dev_dbg(dev, "%s: Couldn't find clock: %d\n", __func__, ret); +- return opp_table; +- } +- +- dev_pm_opp_put_opp_table(opp_table); +- dev_err_probe(dev, ret, "Couldn't find clock\n"); +- +- return ERR_PTR(ret); ++ return opp_table; + } + + /* +diff --git a/include/linux/acpi.h b/include/linux/acpi.h +--- a/include/linux/acpi.h ++++ b/include/linux/acpi.h +@@ -315,6 +315,19 @@ static inline int acpi_processor_evaluate_cst(acpi_handle handle, u32 cpu, + } + #endif + ++#ifdef CONFIG_ACPI_PROCESSOR_IDLE ++int acpi_processor_extract_lpi_info(acpi_handle pr_handle, ++ struct acpi_processor_power *pr_power, ++ bool strict); ++#else ++static inline int acpi_processor_extract_lpi_info(acpi_handle pr_handle, ++ struct acpi_processor_power *pr_power, ++ bool strict) ++{ ++ return -ENODEV; ++} ++#endif ++ + #ifdef CONFIG_ACPI_HOTPLUG_CPU + /* Arch dependent functions for cpu hotplug support */ + int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, u32 acpi_id, +diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h +--- a/include/linux/cpufreq.h ++++ b/include/linux/cpufreq.h +@@ -379,6 +379,7 @@ struct cpufreq_driver { + void (*adjust_perf)(struct cpufreq_policy *policy, + unsigned long min_perf, + unsigned long target_perf, ++ unsigned long max_perf, + unsigned long capacity); + + /* +@@ -624,6 +625,7 @@ unsigned int cpufreq_driver_fast_switch(struct cpufreq_policy *policy, + void cpufreq_driver_adjust_perf(struct cpufreq_policy *policy, + unsigned long min_perf, + unsigned long target_perf, ++ unsigned long max_perf, + unsigned long capacity); + bool cpufreq_driver_has_adjust_perf(void); + int cpufreq_driver_target(struct cpufreq_policy *policy, +diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig +--- a/kernel/power/Kconfig ++++ b/kernel/power/Kconfig +@@ -268,6 +268,16 @@ config DPM_WATCHDOG + captured in pstore device for inspection in subsequent + boot session. + ++config DPM_WATCHDOG_ENABLED ++ bool "Enable DPM watchdog by default" ++ depends on DPM_WATCHDOG ++ default y ++ help ++ If you say Y here, the DPM watchdog will be enabled by default. ++ If you say N, it will be compiled in but disabled. It can be ++ enabled at boot time via the "pm.dpm_watchdog_enabled" kernel ++ parameter or at runtime via sysfs. ++ + config DPM_WATCHDOG_TIMEOUT + int "Watchdog timeout to panic in seconds" + range 1 120 +diff --git a/kernel/power/swap.c b/kernel/power/swap.c +--- a/kernel/power/swap.c ++++ b/kernel/power/swap.c +@@ -430,19 +430,22 @@ static int swap_write_page(struct swap_map_handle *handle, void *buf, + + if (!handle->cur) + return -EINVAL; +- offset = alloc_swapdev_block(root_swap); +- error = write_page(buf, offset, hb); +- if (error) +- return error; +- handle->cur->entries[handle->k++] = offset; ++ ++ /* ++ * If the current map page is full, allocate and link next one first. ++ * Delaying this until here avoids writing an empty swap map page when ++ * the image size is an exact MAP_PAGE_ENTRIES multiple. ++ */ + if (handle->k >= MAP_PAGE_ENTRIES) { + offset = alloc_swapdev_block(root_swap); + if (!offset) + return -ENOSPC; ++ + handle->cur->next_swap = offset; + error = write_page(handle->cur, handle->cur_swap, hb); + if (error) +- goto out; ++ return error; ++ + clear_page(handle->cur); + handle->cur_swap = offset; + handle->k = 0; +@@ -450,7 +453,7 @@ static int swap_write_page(struct swap_map_handle *handle, void *buf, + if (hb && low_free_pages() <= handle->reqd_free_pages) { + error = hib_wait_io(hb); + if (error) +- goto out; ++ return error; + /* + * Recalculate the number of required free pages, to + * make sure we never take more than half. +@@ -458,14 +461,21 @@ static int swap_write_page(struct swap_map_handle *handle, void *buf, + handle->reqd_free_pages = reqd_free_pages(); + } + } +- out: +- return error; ++ ++ offset = alloc_swapdev_block(root_swap); ++ error = write_page(buf, offset, hb); ++ if (error) ++ return error; ++ handle->cur->entries[handle->k++] = offset; ++ return 0; + } + + static int flush_swap_writer(struct swap_map_handle *handle) + { +- if (handle->cur && handle->cur_swap) ++ if (handle->cur && handle->cur_swap && handle->k) + return write_page(handle->cur, handle->cur_swap, NULL); ++ else if (handle->cur && handle->cur_swap) ++ return 0; + else + return -EINVAL; + } +@@ -739,7 +749,7 @@ static int save_compressed_image(struct swap_map_handle *handle, + + data[thr].cc = crypto_alloc_acomp(hib_comp_algo, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR_OR_NULL(data[thr].cc)) { +- pr_err("Could not allocate comp stream %ld\n", PTR_ERR(data[thr].cc)); ++ pr_err("Could not allocate comp stream %pe\n", data[thr].cc); + ret = -EFAULT; + goto out_clean; + } +@@ -1243,7 +1253,7 @@ static int load_compressed_image(struct swap_map_handle *handle, + + data[thr].cc = crypto_alloc_acomp(hib_comp_algo, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR_OR_NULL(data[thr].cc)) { +- pr_err("Could not allocate comp stream %ld\n", PTR_ERR(data[thr].cc)); ++ pr_err("Could not allocate comp stream %pe\n", data[thr].cc); + ret = -EFAULT; + goto out_clean; + } +diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c +--- a/kernel/sched/cpufreq_schedutil.c ++++ b/kernel/sched/cpufreq_schedutil.c +@@ -50,6 +50,7 @@ struct sugov_cpu { + + unsigned long util; + unsigned long bw_min; ++ unsigned long bw_max; + + /* The field below is for single-CPU policies only: */ + #ifdef CONFIG_NO_HZ_COMMON +@@ -243,6 +244,7 @@ static void sugov_get_util(struct sugov_cpu *sg_cpu, unsigned long boost) + util = effective_cpu_util(sg_cpu->cpu, util, &min, &max); + util = max(util, boost); + sg_cpu->bw_min = min; ++ sg_cpu->bw_max = max; + sg_cpu->util = sugov_effective_cpu_perf(sg_cpu->cpu, util, min, max); + } + +@@ -495,7 +497,7 @@ static void sugov_update_single_perf(struct update_util_data *hook, u64 time, + sg_cpu->util = prev_util; + + cpufreq_driver_adjust_perf(sg_policy->policy, sg_cpu->bw_min, +- sg_cpu->util, max_cap); ++ sg_cpu->util, sg_cpu->bw_max, max_cap); + + sg_policy->need_freq_update = false; + sg_policy->last_freq_update_time = time; +@@ -601,7 +603,7 @@ static ssize_t rate_limit_us_show(struct gov_attr_set *attr_set, char *buf) + { + struct sugov_tunables *tunables = to_sugov_tunables(attr_set); + +- return sprintf(buf, "%u\n", tunables->rate_limit_us); ++ return sysfs_emit(buf, "%u\n", tunables->rate_limit_us); + } + + static ssize_t +diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs +--- a/rust/kernel/cpufreq.rs ++++ b/rust/kernel/cpufreq.rs +@@ -797,7 +797,13 @@ pub trait Driver { + } + + /// Driver's `adjust_perf` callback. +- fn adjust_perf(_policy: &mut Policy, _min_perf: usize, _target_perf: usize, _capacity: usize) { ++ fn adjust_perf( ++ _policy: &mut Policy, ++ _min_perf: usize, ++ _target_perf: usize, ++ _max_perf: usize, ++ _capacity: usize, ++ ) { + build_error!(VTABLE_DEFAULT_ERROR) + } + +@@ -1270,12 +1276,13 @@ impl Registration { + ptr: *mut bindings::cpufreq_policy, + min_perf: c_ulong, + target_perf: c_ulong, ++ max_perf: c_ulong, + capacity: c_ulong, + ) { + // SAFETY: The `ptr` is guaranteed to be valid by the contract with the C code for the + // lifetime of `policy`. + let policy = unsafe { Policy::from_raw_mut(ptr) }; +- T::adjust_perf(policy, min_perf, target_perf, capacity); ++ T::adjust_perf(policy, min_perf, target_perf, max_perf, capacity); + } + + /// Driver's `get_intermediate` callback. diff --git a/pkgbuilds/linux-omarchy/0210-pm-updates.patch.sig b/pkgbuilds/linux-omarchy/0210-pm-updates.patch.sig new file mode 100644 index 0000000..8ffea46 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0210-pm-updates.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0210-pstate.patch b/pkgbuilds/linux-omarchy/0210-pstate.patch deleted file mode 100644 index 3d4eefc..0000000 --- a/pkgbuilds/linux-omarchy/0210-pstate.patch +++ /dev/null @@ -1,480 +0,0 @@ -diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c ---- a/drivers/cpufreq/amd-pstate.c -+++ b/drivers/cpufreq/amd-pstate.c -@@ -782,6 +782,7 @@ static unsigned int amd_pstate_fast_switch(struct cpufreq_policy *policy, - static void amd_pstate_adjust_perf(struct cpufreq_policy *policy, - unsigned long _min_perf, - unsigned long target_perf, -+ unsigned long _max_perf, - unsigned long capacity) - { - u8 max_perf, min_perf, des_perf, cap_perf; -diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c ---- a/drivers/cpufreq/cpufreq.c -+++ b/drivers/cpufreq/cpufreq.c -@@ -2225,14 +2225,17 @@ EXPORT_SYMBOL_GPL(cpufreq_driver_fast_switch); - * @policy: cpufreq policy object of the target CPU. - * @min_perf: Minimum (required) performance level (units of @capacity). - * @target_perf: Target (desired) performance level (units of @capacity). -+ * @max_perf: Maximum (allowed) performance level (units of @capacity). - * @capacity: Capacity of the target CPU. - * -- * Carry out a fast performance level switch of @cpu without sleeping. -+ * Carry out a fast performance level adjustment for the CPU represented by -+ * @policy without sleeping. - * - * The driver's ->adjust_perf() callback invoked by this function must be -- * suitable for being called from within RCU-sched read-side critical sections -- * and it is expected to select a suitable performance level equal to or above -- * @min_perf and preferably equal to or below @target_perf. -+ * suitable for calling from within RCU-sched read-side critical sections and -+ * it is expected to program the processor to select suitable performance -+ * levels between @min_perf and @max_perf inclusive and preferably close to -+ * @target_perf going forward for the CPU represented by @policy. - * - * This function must not be called if policy->fast_switch_enabled is unset. - * -@@ -2244,9 +2247,10 @@ EXPORT_SYMBOL_GPL(cpufreq_driver_fast_switch); - void cpufreq_driver_adjust_perf(struct cpufreq_policy *policy, - unsigned long min_perf, - unsigned long target_perf, -+ unsigned long max_perf, - unsigned long capacity) - { -- cpufreq_driver->adjust_perf(policy, min_perf, target_perf, capacity); -+ cpufreq_driver->adjust_perf(policy, min_perf, target_perf, max_perf, capacity); - } - - /** -diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c ---- a/drivers/cpufreq/intel_pstate.c -+++ b/drivers/cpufreq/intel_pstate.c -@@ -299,7 +299,6 @@ struct pstate_funcs { - static struct pstate_funcs pstate_funcs __read_mostly; - - static bool hwp_active __ro_after_init; --static int hwp_mode_bdw __ro_after_init; - static bool per_cpu_limits __ro_after_init; - static bool hwp_forced __ro_after_init; - static bool hwp_boost __read_mostly; -@@ -587,21 +586,14 @@ static void intel_pstate_hybrid_hwp_adjust(struct cpudata *cpu) - - hwp_is_hybrid = true; - -- cpu->pstate.turbo_freq = rounddown(cpu->pstate.turbo_pstate * scaling, -- perf_ctl_scaling); -- cpu->pstate.max_freq = rounddown(cpu->pstate.max_pstate * scaling, -- perf_ctl_scaling); -- - freq = perf_ctl_max_phys * perf_ctl_scaling; - cpu->pstate.max_pstate_physical = intel_pstate_freq_to_hwp(cpu, freq); - -- freq = cpu->pstate.min_pstate * perf_ctl_scaling; -- cpu->pstate.min_freq = freq; - /* - * Cast the min P-state value retrieved via pstate_funcs.get_min() to - * the effective range of HWP performance levels. - */ -- cpu->pstate.min_pstate = intel_pstate_freq_to_hwp(cpu, freq); -+ cpu->pstate.min_pstate = intel_pstate_freq_to_hwp(cpu, cpu->pstate.min_freq); - } - - static bool turbo_is_disabled(void) -@@ -979,12 +971,10 @@ static int hybrid_get_cost(struct device *dev, unsigned long freq, - * capacity. Similarly, P-cores start to be populated when E-cores are - * utilized above 60% of the capacity. - */ -- if (hybrid_get_cpu_type(dev->id) == INTEL_CPU_TYPE_ATOM) { -- if (hybrid_has_l3(dev->id)) /* E-core */ -- *cost += 1; -- } else { /* P-core */ -+ if (hybrid_get_cpu_type(dev->id) == INTEL_CPU_TYPE_CORE) /* P-core */ - *cost += 2; -- } -+ else if (hybrid_has_l3(dev->id)) /* E-core */ -+ *cost += 1; - - return 0; - } -@@ -1185,6 +1175,22 @@ static bool hybrid_clear_max_perf_cpu(void) - return ret; - } - -+static void intel_pstate_update_freq_limits(struct cpudata *cpu) -+{ -+ int scaling = cpu->pstate.scaling; -+ unsigned int turbo_freq = cpu->pstate.turbo_pstate * scaling; -+ unsigned int max_freq = cpu->pstate.max_pstate * scaling; -+ int perf_ctl_scaling = cpu->pstate.perf_ctl_scaling; -+ -+ if (scaling != perf_ctl_scaling) { -+ turbo_freq = rounddown(turbo_freq, perf_ctl_scaling); -+ max_freq = rounddown(max_freq, perf_ctl_scaling); -+ } -+ -+ cpu->pstate.turbo_freq = turbo_freq; -+ cpu->pstate.max_freq = max_freq; -+} -+ - static void __intel_pstate_get_hwp_cap(struct cpudata *cpu) - { - u64 cap; -@@ -1197,20 +1203,8 @@ static void __intel_pstate_get_hwp_cap(struct cpudata *cpu) - - static void intel_pstate_get_hwp_cap(struct cpudata *cpu) - { -- int scaling = cpu->pstate.scaling; -- - __intel_pstate_get_hwp_cap(cpu); -- -- cpu->pstate.max_freq = cpu->pstate.max_pstate * scaling; -- cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * scaling; -- if (scaling != cpu->pstate.perf_ctl_scaling) { -- int perf_ctl_scaling = cpu->pstate.perf_ctl_scaling; -- -- cpu->pstate.max_freq = rounddown(cpu->pstate.max_freq, -- perf_ctl_scaling); -- cpu->pstate.turbo_freq = rounddown(cpu->pstate.turbo_freq, -- perf_ctl_scaling); -- } -+ intel_pstate_update_freq_limits(cpu); - } - - static void hybrid_update_capacity(struct cpudata *cpu) -@@ -2299,33 +2293,16 @@ static int hwp_get_cpu_scaling(int cpu) - return intel_pstate_cppc_get_scaling(cpu); - } - --static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate) --{ -- trace_cpu_frequency(pstate * cpu->pstate.scaling, cpu->cpu); -- cpu->pstate.current_pstate = pstate; -- /* -- * Generally, there is no guarantee that this code will always run on -- * the CPU being updated, so force the register update to run on the -- * right CPU. -- */ -- wrmsrq_on_cpu(cpu->cpu, MSR_IA32_PERF_CTL, -- pstate_funcs.get_val(cpu, pstate)); --} -- --static void intel_pstate_set_min_pstate(struct cpudata *cpu) --{ -- intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate); --} -- - static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) - { - int perf_ctl_scaling = pstate_funcs.get_scaling(); - - cpu->pstate.max_pstate_physical = pstate_funcs.get_max_physical(cpu->cpu); - cpu->pstate.min_pstate = pstate_funcs.get_min(cpu->cpu); -+ cpu->pstate.min_freq = cpu->pstate.min_pstate * perf_ctl_scaling; - cpu->pstate.perf_ctl_scaling = perf_ctl_scaling; - -- if (hwp_active && !hwp_mode_bdw) { -+ if (hwp_active) { - __intel_pstate_get_hwp_cap(cpu); - - if (pstate_funcs.get_cpu_scaling) { -@@ -2345,19 +2322,13 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) - cpu->pstate.turbo_pstate = pstate_funcs.get_turbo(cpu->cpu); - } - -- if (cpu->pstate.scaling == perf_ctl_scaling) { -- cpu->pstate.min_freq = cpu->pstate.min_pstate * perf_ctl_scaling; -- cpu->pstate.max_freq = cpu->pstate.max_pstate * perf_ctl_scaling; -- cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * perf_ctl_scaling; -- } -+ intel_pstate_update_freq_limits(cpu); - - if (pstate_funcs.get_aperf_mperf_shift) - cpu->aperf_mperf_shift = pstate_funcs.get_aperf_mperf_shift(); - - if (pstate_funcs.get_vid) - pstate_funcs.get_vid(cpu); -- -- intel_pstate_set_min_pstate(cpu); - } - - /* -@@ -2884,8 +2855,22 @@ static void intel_pstate_update_perf_limits(struct cpudata *cpu, - cpu->min_perf_ratio); - } - -+static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate) -+{ -+ trace_cpu_frequency(pstate * cpu->pstate.scaling, cpu->cpu); -+ cpu->pstate.current_pstate = pstate; -+ /* -+ * Generally, there is no guarantee that this code will always run on -+ * the CPU being updated, so force the register update to run on the -+ * right CPU. -+ */ -+ wrmsrq_on_cpu(cpu->cpu, MSR_IA32_PERF_CTL, -+ pstate_funcs.get_val(cpu, pstate)); -+} -+ - static int intel_pstate_set_policy(struct cpufreq_policy *policy) - { -+ unsigned int freq = policy->min; - struct cpudata *cpu; - - if (!policy->cpuinfo.max_freq) -@@ -2901,7 +2886,23 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy) - - intel_pstate_update_perf_limits(cpu, policy->min, policy->max); - -- if (cpu->policy == CPUFREQ_POLICY_PERFORMANCE) { -+ if (hwp_active) { -+ /* -+ * The active mode only requires an update util hook if HWP -+ * boost is used and the policy is not "performance". -+ */ -+ if (hwp_boost && cpu->policy != CPUFREQ_POLICY_PERFORMANCE) { -+ intel_pstate_set_update_util_hook(policy->cpu); -+ } else { -+ intel_pstate_clear_update_util_hook(policy->cpu); -+ if (cpu->policy == CPUFREQ_POLICY_PERFORMANCE) { -+ freq = cpu->max_perf_ratio * cpu->pstate.scaling; -+ if (cpu->pstate.scaling != cpu->pstate.perf_ctl_scaling) -+ freq = rounddown(freq, cpu->pstate.perf_ctl_scaling); -+ } -+ } -+ intel_pstate_hwp_set(policy->cpu); -+ } else if (cpu->policy == CPUFREQ_POLICY_PERFORMANCE) { - int pstate = max(cpu->pstate.min_pstate, cpu->max_perf_ratio); - - /* -@@ -2910,25 +2911,17 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy) - */ - intel_pstate_clear_update_util_hook(policy->cpu); - intel_pstate_set_pstate(cpu, pstate); -+ freq = pstate * cpu->pstate.scaling; - } else { - intel_pstate_set_update_util_hook(policy->cpu); - } -- -- if (hwp_active) { -- /* -- * When hwp_boost was active before and dynamically it -- * was turned off, in that case we need to clear the -- * update util hook. -- */ -- if (!hwp_boost) -- intel_pstate_clear_update_util_hook(policy->cpu); -- intel_pstate_hwp_set(policy->cpu); -- } - /* -- * policy->cur is never updated with the intel_pstate driver, but it -- * is used as a stale frequency value. So, keep it within limits. -+ * policy->cur is never updated in the intel_pstate driver, but it is -+ * used as a stale frequency value, so set it to reflect the actual -+ * requested P-state in the "performance" policy case and to the min -+ * otherwise. - */ -- policy->cur = policy->min; -+ policy->cur = freq; - - mutex_unlock(&intel_pstate_limits_lock); - -@@ -2971,6 +2964,11 @@ static int intel_pstate_verify_policy(struct cpufreq_policy_data *policy) - return 0; - } - -+static void intel_pstate_set_min_pstate(struct cpudata *cpu) -+{ -+ intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate); -+} -+ - static int intel_cpufreq_cpu_offline(struct cpufreq_policy *policy) - { - struct cpudata *cpu = all_cpu_data[policy->cpu]; -@@ -3063,6 +3061,7 @@ static int __intel_pstate_cpu_init(struct cpufreq_policy *policy) - static int intel_pstate_cpu_init(struct cpufreq_policy *policy) - { - int ret = __intel_pstate_cpu_init(policy); -+ struct cpudata *cpu; - - if (ret) - return ret; -@@ -3073,11 +3072,11 @@ static int intel_pstate_cpu_init(struct cpufreq_policy *policy) - */ - policy->policy = CPUFREQ_POLICY_POWERSAVE; - -- if (hwp_active) { -- struct cpudata *cpu = all_cpu_data[policy->cpu]; -- -+ cpu = all_cpu_data[policy->cpu]; -+ if (hwp_active) - cpu->epp_cached = intel_pstate_get_epp(cpu, 0); -- } -+ else -+ intel_pstate_set_min_pstate(cpu); - - return 0; - } -@@ -3243,6 +3242,7 @@ static unsigned int intel_cpufreq_fast_switch(struct cpufreq_policy *policy, - static void intel_cpufreq_adjust_perf(struct cpufreq_policy *policy, - unsigned long min_perf, - unsigned long target_perf, -+ unsigned long max_perf, - unsigned long capacity) - { - struct cpudata *cpu = all_cpu_data[policy->cpu]; -@@ -3273,7 +3273,13 @@ static void intel_cpufreq_adjust_perf(struct cpufreq_policy *policy, - if (min_pstate > cpu->max_perf_ratio) - min_pstate = cpu->max_perf_ratio; - -- max_pstate = min(cap_pstate, cpu->max_perf_ratio); -+ max_pstate = cap_pstate; -+ if (max_perf < capacity) -+ max_pstate = DIV_ROUND_UP(cap_pstate * max_perf, capacity); -+ -+ if (max_pstate > cpu->max_perf_ratio) -+ max_pstate = cpu->max_perf_ratio; -+ - if (max_pstate < min_pstate) - max_pstate = min_pstate; - -@@ -3301,8 +3307,6 @@ static int intel_cpufreq_cpu_init(struct cpufreq_policy *policy) - return ret; - - policy->cpuinfo.transition_latency = INTEL_CPUFREQ_TRANSITION_LATENCY; -- /* This reflects the intel_pstate_get_cpu_pstates() setting. */ -- policy->cur = policy->cpuinfo.min_freq; - - req = kzalloc_objs(*req, 2); - if (!req) { -@@ -3323,9 +3327,15 @@ static int intel_cpufreq_cpu_init(struct cpufreq_policy *policy) - WRITE_ONCE(cpu->hwp_req_cached, value); - - cpu->epp_cached = intel_pstate_get_epp(cpu, value); -+ -+ intel_cpufreq_hwp_update(cpu, cpu->pstate.min_pstate, -+ cpu->pstate.max_pstate, -+ cpu->pstate.min_pstate, false); - } else { - policy->transition_delay_us = INTEL_CPUFREQ_TRANSITION_DELAY; -+ intel_pstate_set_min_pstate(cpu); - } -+ policy->cur = policy->cpuinfo.min_freq; - - freq = DIV_ROUND_UP(cpu->pstate.turbo_freq * global.min_perf_pct, 100); - -@@ -3676,14 +3686,14 @@ static inline bool intel_pstate_has_acpi_ppc(void) { return false; } - static inline void intel_pstate_request_control_from_smm(void) {} - #endif /* CONFIG_ACPI */ - --#define INTEL_PSTATE_HWP_BROADWELL 0x01 -+#define INTEL_PSTATE_HWP_NOT_HYBRID 0x01 - - #define X86_MATCH_HWP(vfm, hwp_mode) \ - X86_MATCH_VFM_FEATURE(vfm, X86_FEATURE_HWP, hwp_mode) - - static const struct x86_cpu_id hwp_support_ids[] __initconst = { -- X86_MATCH_HWP(INTEL_BROADWELL_X, INTEL_PSTATE_HWP_BROADWELL), -- X86_MATCH_HWP(INTEL_BROADWELL_D, INTEL_PSTATE_HWP_BROADWELL), -+ X86_MATCH_HWP(INTEL_BROADWELL_X, INTEL_PSTATE_HWP_NOT_HYBRID), -+ X86_MATCH_HWP(INTEL_BROADWELL_D, INTEL_PSTATE_HWP_NOT_HYBRID), - X86_MATCH_HWP(INTEL_ANY, 0), - {} - }; -@@ -3808,7 +3818,6 @@ static int __init intel_pstate_init(void) - - if (!no_hwp) { - hwp_active = true; -- hwp_mode_bdw = id->driver_data; - intel_pstate.attr = hwp_cpufreq_attrs; - intel_cpufreq.attr = hwp_cpufreq_attrs; - intel_cpufreq.flags |= CPUFREQ_NEED_UPDATE_LIMITS; -@@ -3816,7 +3825,8 @@ static int __init intel_pstate_init(void) - if (!default_driver) - default_driver = &intel_pstate; - -- pstate_funcs.get_cpu_scaling = hwp_get_cpu_scaling; -+ if (!id->driver_data) -+ pstate_funcs.get_cpu_scaling = hwp_get_cpu_scaling; - - goto hwp_cpu_matched; - } -diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h ---- a/include/linux/cpufreq.h -+++ b/include/linux/cpufreq.h -@@ -379,6 +379,7 @@ struct cpufreq_driver { - void (*adjust_perf)(struct cpufreq_policy *policy, - unsigned long min_perf, - unsigned long target_perf, -+ unsigned long max_perf, - unsigned long capacity); - - /* -@@ -624,6 +625,7 @@ unsigned int cpufreq_driver_fast_switch(struct cpufreq_policy *policy, - void cpufreq_driver_adjust_perf(struct cpufreq_policy *policy, - unsigned long min_perf, - unsigned long target_perf, -+ unsigned long max_perf, - unsigned long capacity); - bool cpufreq_driver_has_adjust_perf(void); - int cpufreq_driver_target(struct cpufreq_policy *policy, -diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c ---- a/kernel/sched/cpufreq_schedutil.c -+++ b/kernel/sched/cpufreq_schedutil.c -@@ -50,6 +50,7 @@ struct sugov_cpu { - - unsigned long util; - unsigned long bw_min; -+ unsigned long bw_max; - - /* The field below is for single-CPU policies only: */ - #ifdef CONFIG_NO_HZ_COMMON -@@ -243,6 +244,7 @@ static void sugov_get_util(struct sugov_cpu *sg_cpu, unsigned long boost) - util = effective_cpu_util(sg_cpu->cpu, util, &min, &max); - util = max(util, boost); - sg_cpu->bw_min = min; -+ sg_cpu->bw_max = max; - sg_cpu->util = sugov_effective_cpu_perf(sg_cpu->cpu, util, min, max); - } - -@@ -495,7 +497,7 @@ static void sugov_update_single_perf(struct update_util_data *hook, u64 time, - sg_cpu->util = prev_util; - - cpufreq_driver_adjust_perf(sg_policy->policy, sg_cpu->bw_min, -- sg_cpu->util, max_cap); -+ sg_cpu->util, sg_cpu->bw_max, max_cap); - - sg_policy->need_freq_update = false; - sg_policy->last_freq_update_time = time; -diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs ---- a/rust/kernel/cpufreq.rs -+++ b/rust/kernel/cpufreq.rs -@@ -792,7 +792,13 @@ fn fast_switch(_policy: &mut Policy, _target_freq: u32) -> u32 { - } - - /// Driver's `adjust_perf` callback. -- fn adjust_perf(_policy: &mut Policy, _min_perf: usize, _target_perf: usize, _capacity: usize) { -+ fn adjust_perf( -+ _policy: &mut Policy, -+ _min_perf: usize, -+ _target_perf: usize, -+ _max_perf: usize, -+ _capacity: usize, -+ ) { - build_error!(VTABLE_DEFAULT_ERROR) - } - -@@ -1263,12 +1269,13 @@ impl Registration { - ptr: *mut bindings::cpufreq_policy, - min_perf: c_ulong, - target_perf: c_ulong, -+ max_perf: c_ulong, - capacity: c_ulong, - ) { - // SAFETY: The `ptr` is guaranteed to be valid by the contract with the C code for the - // lifetime of `policy`. - let policy = unsafe { Policy::from_raw_mut(ptr) }; -- T::adjust_perf(policy, min_perf, target_perf, capacity); -+ T::adjust_perf(policy, min_perf, target_perf, max_perf, capacity); - } - - /// Driver's `get_intermediate` callback. diff --git a/pkgbuilds/linux-omarchy/0210-pstate.patch.sig b/pkgbuilds/linux-omarchy/0210-pstate.patch.sig deleted file mode 100644 index 1d30612..0000000 Binary files a/pkgbuilds/linux-omarchy/0210-pstate.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/0211-amd-pstate-fixes.patch b/pkgbuilds/linux-omarchy/0211-amd-pstate-fixes.patch deleted file mode 100644 index 67a6cf4..0000000 --- a/pkgbuilds/linux-omarchy/0211-amd-pstate-fixes.patch +++ /dev/null @@ -1,127 +0,0 @@ -diff --git a/drivers/cpufreq/amd-pstate-ut.c b/drivers/cpufreq/amd-pstate-ut.c ---- a/drivers/cpufreq/amd-pstate-ut.c -+++ b/drivers/cpufreq/amd-pstate-ut.c -@@ -560,6 +560,11 @@ static int amd_pstate_ut_check_freq_attrs(u32 index) - static int __init amd_pstate_ut_init(void) - { - u32 i = 0, arr_size = ARRAY_SIZE(amd_pstate_ut_cases); -+ enum amd_pstate_mode mode = amd_pstate_get_status(); -+ -+ /* don't test if no running amd-pstate driver */ -+ if (mode == AMD_PSTATE_UNDEFINED || mode == AMD_PSTATE_DISABLE) -+ return -EOPNOTSUPP; - - for (i = 0; i < arr_size; i++) { - int ret; -diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c ---- a/drivers/cpufreq/amd-pstate.c -+++ b/drivers/cpufreq/amd-pstate.c -@@ -199,7 +199,7 @@ static inline int get_mode_idx_from_str(const char *str, size_t size) - - static DEFINE_MUTEX(amd_pstate_driver_lock); - --static u8 msr_get_epp(struct amd_cpudata *cpudata) -+static int msr_get_epp(struct amd_cpudata *cpudata) - { - u64 value; - int ret; -@@ -215,12 +215,12 @@ static u8 msr_get_epp(struct amd_cpudata *cpudata) - - DEFINE_STATIC_CALL(amd_pstate_get_epp, msr_get_epp); - --static inline s16 amd_pstate_get_epp(struct amd_cpudata *cpudata) -+static inline int amd_pstate_get_epp(struct amd_cpudata *cpudata) - { - return static_call(amd_pstate_get_epp)(cpudata); - } - --static u8 shmem_get_epp(struct amd_cpudata *cpudata) -+static int shmem_get_epp(struct amd_cpudata *cpudata) - { - u64 epp; - int ret; -@@ -526,9 +526,6 @@ static int shmem_init_perf(struct amd_cpudata *cpudata) - WRITE_ONCE(cpudata->perf, perf); - WRITE_ONCE(cpudata->prefcore_ranking, cppc_perf.highest_perf); - -- if (cppc_state == AMD_PSTATE_ACTIVE) -- return 0; -- - ret = cppc_get_auto_sel(cpudata->cpu, &auto_sel); - if (ret) { - pr_warn("failed to get auto_sel, ret: %d\n", ret); -@@ -1174,6 +1171,9 @@ static int amd_pstate_power_supply_notifier(struct notifier_block *nb, - if (cpudata->current_profile != PLATFORM_PROFILE_BALANCED) - return 0; - -+ if (!policy) -+ return NOTIFY_OK; -+ - epp = amd_pstate_get_balanced_epp(policy); - - ret = amd_pstate_set_epp(policy, epp); -@@ -1209,6 +1209,9 @@ static int amd_pstate_profile_set(struct device *dev, - struct cpufreq_policy *policy __free(put_cpufreq_policy) = cpufreq_cpu_get(cpudata->cpu); - int ret; - -+ if (!policy) -+ return -ENODEV; -+ - switch (profile) { - case PLATFORM_PROFILE_LOW_POWER: - ret = amd_pstate_set_epp(policy, AMD_CPPC_EPP_POWERSAVE); -@@ -1877,6 +1880,7 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) - struct amd_cpudata *cpudata; - union perf_cached perf; - struct device *dev; -+ int default_epp; - int ret; - - /* -@@ -1925,6 +1929,13 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) - - policy->boost_supported = READ_ONCE(cpudata->boost_supported); - -+ /* Fetch the firmware programmed default EPP value */ -+ default_epp = amd_pstate_get_epp(cpudata); -+ if (default_epp < 0) { -+ ret = default_epp; -+ goto free_cpudata1; -+ } -+ - /* - * Set the policy to provide a valid fallback value in case - * the default cpufreq governor is neither powersave nor performance. -@@ -1932,7 +1943,7 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) - if (amd_pstate_acpi_pm_profile_server() || - amd_pstate_acpi_pm_profile_undefined()) { - policy->policy = CPUFREQ_POLICY_PERFORMANCE; -- cpudata->epp_default_ac = cpudata->epp_default_dc = amd_pstate_get_epp(cpudata); -+ cpudata->epp_default_ac = cpudata->epp_default_dc = default_epp; - cpudata->current_profile = PLATFORM_PROFILE_PERFORMANCE; - } else { - policy->policy = CPUFREQ_POLICY_POWERSAVE; -diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h ---- a/drivers/cpufreq/amd-pstate.h -+++ b/drivers/cpufreq/amd-pstate.h -@@ -32,6 +32,7 @@ - * @min_limit_perf: Cached value of the performance corresponding to policy->min - * @max_limit_perf: Cached value of the performance corresponding to policy->max - * @bios_min_perf: Cached perf value corresponding to the "Requested CPU Min Frequency" BIOS option -+ * @val: Raw 64-bit value for atomic access via READ_ONCE()/WRITE_ONCE() - */ - union perf_cached { - struct { -@@ -89,7 +90,12 @@ struct amd_aperf_mperf { - * @epp_default_ac: Default EPP value for AC power source - * @epp_default_dc: Default EPP value for DC power source - * @dynamic_epp: Whether dynamic EPP is enabled -+ * @raw_epp: Whether the last EPP write was a raw numeric value rather than a -+ * named preference - * @power_nb: Notifier block for power events -+ * @current_profile: Currently selected platform profile option -+ * @ppdev: Device registered with the platform profile handler -+ * @profile_name: Name under which @ppdev is registered - * - * The amd_cpudata is key private data for each CPU thread in AMD P-State, and - * represents all the attributes and goals that AMD P-State requests at runtime. diff --git a/pkgbuilds/linux-omarchy/0211-amd-pstate-fixes.patch.sig b/pkgbuilds/linux-omarchy/0211-amd-pstate-fixes.patch.sig deleted file mode 100644 index 7f218b5..0000000 Binary files a/pkgbuilds/linux-omarchy/0211-amd-pstate-fixes.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/0211-pm-fixes-1.patch b/pkgbuilds/linux-omarchy/0211-pm-fixes-1.patch new file mode 100644 index 0000000..ed2d0f5 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0211-pm-fixes-1.patch @@ -0,0 +1,104 @@ +diff --git a/drivers/opp/core.c b/drivers/opp/core.c +--- a/drivers/opp/core.c ++++ b/drivers/opp/core.c +@@ -453,8 +453,8 @@ int dev_pm_opp_get_opp_count(struct device *dev) + _find_opp_table(dev); + + if (IS_ERR(opp_table)) { +- dev_dbg(dev, "%s: OPP table not found (%ld)\n", +- __func__, PTR_ERR(opp_table)); ++ dev_dbg(dev, "%s: OPP table not found (%pe)\n", ++ __func__, opp_table); + return PTR_ERR(opp_table); + } + +@@ -611,8 +611,8 @@ _find_key(struct device *dev, unsigned long *key, int index, bool available, + _find_opp_table(dev); + + if (IS_ERR(opp_table)) { +- dev_err(dev, "%s: OPP table not found (%ld)\n", __func__, +- PTR_ERR(opp_table)); ++ dev_err(dev, "%s: OPP table not found (%pe)\n", __func__, ++ opp_table); + return ERR_CAST(opp_table); + } + +@@ -722,8 +722,8 @@ struct dev_pm_opp *dev_pm_opp_find_key_exact(struct device *dev, + struct opp_table *opp_table __free(put_opp_table) = _find_opp_table(dev); + + if (IS_ERR(opp_table)) { +- dev_err(dev, "%s: OPP table not found (%ld)\n", __func__, +- PTR_ERR(opp_table)); ++ dev_err(dev, "%s: OPP table not found (%pe)\n", __func__, ++ opp_table); + return ERR_CAST(opp_table); + } + +@@ -1036,8 +1036,8 @@ static int _set_opp_voltage(struct device *dev, struct regulator *reg, + + /* Regulator not available for device */ + if (IS_ERR(reg)) { +- dev_dbg(dev, "%s: regulator not available: %ld\n", __func__, +- PTR_ERR(reg)); ++ dev_dbg(dev, "%s: regulator not available: %pe\n", __func__, ++ reg); + return 0; + } + +@@ -1448,8 +1448,8 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) + temp_freq = freq; + opp = _find_freq_ceil(opp_table, &temp_freq); + if (IS_ERR(opp)) { +- dev_err(dev, "%s: failed to find OPP for freq %lu (%ld)\n", +- __func__, freq, PTR_ERR(opp)); ++ dev_err(dev, "%s: failed to find OPP for freq %lu (%pe)\n", ++ __func__, freq, opp); + return PTR_ERR(opp); + } + +@@ -1581,6 +1581,8 @@ static struct opp_table *_update_opp_table_clk(struct device *dev, + struct opp_table *opp_table, + bool getclk) + { ++ int ret; ++ + /* + * Return early if we don't need to get clk or we have already done it + * earlier. +@@ -1607,9 +1609,9 @@ static struct opp_table *_update_opp_table_clk(struct device *dev, + opp_table->clk = clk_get_optional(dev, NULL); + + if (IS_ERR(opp_table->clk)) { ++ ret = dev_err_probe(dev, PTR_ERR(opp_table->clk), "Couldn't find clock\n"); + dev_pm_opp_put_opp_table(opp_table); +- dev_err_probe(dev, PTR_ERR(opp_table->clk), "Couldn't find clock\n"); +- return ERR_CAST(opp_table->clk); ++ return ERR_PTR(ret); + } + + if (opp_table->clk) +@@ -2869,8 +2871,8 @@ static int _opp_set_availability(struct device *dev, unsigned long freq, + struct dev_pm_opp *opp __free(put_opp) = ERR_PTR(-ENODEV), *tmp_opp; + + if (IS_ERR(opp_table)) { +- dev_warn(dev, "%s: Device OPP not found (%ld)\n", __func__, +- PTR_ERR(opp_table)); ++ dev_warn(dev, "%s: Device OPP not found (%pe)\n", __func__, ++ opp_table); + return PTR_ERR(opp_table); + } + +diff --git a/drivers/opp/of.c b/drivers/opp/of.c +--- a/drivers/opp/of.c ++++ b/drivers/opp/of.c +@@ -1345,8 +1345,8 @@ int of_get_required_opp_performance_state(struct device_node *np, int index) + _find_table_of_opp_np(required_np); + + if (IS_ERR(opp_table)) { +- pr_err("%s: Failed to find required OPP table %pOF: %ld\n", +- __func__, np, PTR_ERR(opp_table)); ++ pr_err("%s: Failed to find required OPP table %pOF: %pe\n", ++ __func__, np, opp_table); + return PTR_ERR(opp_table); + } + diff --git a/pkgbuilds/linux-omarchy/0211-pm-fixes-1.patch.sig b/pkgbuilds/linux-omarchy/0211-pm-fixes-1.patch.sig new file mode 100644 index 0000000..b6284eb Binary files /dev/null and b/pkgbuilds/linux-omarchy/0211-pm-fixes-1.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0212-amd-pstate-epp-cache.patch b/pkgbuilds/linux-omarchy/0212-amd-pstate-epp-cache.patch deleted file mode 100644 index 61ea14c..0000000 --- a/pkgbuilds/linux-omarchy/0212-amd-pstate-epp-cache.patch +++ /dev/null @@ -1,18 +0,0 @@ -diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c ---- a/drivers/cpufreq/amd-pstate.c -+++ b/drivers/cpufreq/amd-pstate.c -@@ -1929,12 +1929,13 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) - - policy->boost_supported = READ_ONCE(cpudata->boost_supported); - -- /* Fetch the firmware programmed default EPP value */ -+ /* Cache the firmware programmed EPP */ - default_epp = amd_pstate_get_epp(cpudata); - if (default_epp < 0) { - ret = default_epp; - goto free_cpudata1; - } -+ FIELD_MODIFY(AMD_CPPC_EPP_PERF_MASK, &cpudata->cppc_req_cached, default_epp); - - /* - * Set the policy to provide a valid fallback value in case diff --git a/pkgbuilds/linux-omarchy/0212-amd-pstate-epp-cache.patch.sig b/pkgbuilds/linux-omarchy/0212-amd-pstate-epp-cache.patch.sig deleted file mode 100644 index 7371ce6..0000000 Binary files a/pkgbuilds/linux-omarchy/0212-amd-pstate-epp-cache.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/0212-pm-fixes-2.patch b/pkgbuilds/linux-omarchy/0212-pm-fixes-2.patch new file mode 100644 index 0000000..0e9c073 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0212-pm-fixes-2.patch @@ -0,0 +1,54 @@ +diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c +--- a/kernel/power/hibernate.c ++++ b/kernel/power/hibernate.c +@@ -408,9 +408,18 @@ int hibernation_snapshot(int platform_mode) + if (error) + goto Close; + ++ error = dpm_prepare(PMSG_FREEZE); ++ if (error) ++ goto Complete; ++ ++ /* Preallocate image memory before freezing kernel threads and shutting down devices. */ ++ error = hibernate_preallocate_memory(); ++ if (error) ++ goto Complete; ++ + error = freeze_kernel_threads(); + if (error) +- goto Close; ++ goto Cleanup; + + if (hibernation_test(TEST_FREEZER)) { + +@@ -422,15 +431,6 @@ int hibernation_snapshot(int platform_mode) + goto Thaw; + } + +- error = dpm_prepare(PMSG_FREEZE); +- if (error) +- goto Complete; +- +- /* Preallocate image memory before shutting down devices. */ +- error = hibernate_preallocate_memory(); +- if (error) +- goto Complete; +- + console_suspend_all(); + pm_restrict_gfp_mask(); + +@@ -464,10 +464,12 @@ int hibernation_snapshot(int platform_mode) + platform_end(platform_mode); + return error; + +- Complete: +- dpm_complete(PMSG_RECOVER); + Thaw: + thaw_kernel_threads(); ++ Cleanup: ++ swsusp_free(); ++ Complete: ++ dpm_complete(PMSG_RECOVER); + goto Close; + } + diff --git a/pkgbuilds/linux-omarchy/0212-pm-fixes-2.patch.sig b/pkgbuilds/linux-omarchy/0212-pm-fixes-2.patch.sig new file mode 100644 index 0000000..17b44d0 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0212-pm-fixes-2.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0213-pm-fixes-3.patch b/pkgbuilds/linux-omarchy/0213-pm-fixes-3.patch new file mode 100644 index 0000000..e8c1523 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0213-pm-fixes-3.patch @@ -0,0 +1,61 @@ +diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c +--- a/drivers/cpufreq/cpufreq.c ++++ b/drivers/cpufreq/cpufreq.c +@@ -2590,8 +2590,8 @@ static void cpufreq_update_pressure(struct cpufreq_policy *policy) + + cpu = cpumask_first(policy->related_cpus); + max_freq = arch_scale_freq_ref(cpu); +- if (!max_freq) +- max_freq = policy->cpuinfo.max_freq; ++ if (!max_freq && cpufreq_driver->scale_freq_ref) ++ max_freq = cpufreq_driver->scale_freq_ref(policy); + + capped_freq = policy->max; + +diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c +--- a/drivers/cpufreq/intel_pstate.c ++++ b/drivers/cpufreq/intel_pstate.c +@@ -1175,6 +1175,14 @@ static bool hybrid_clear_max_perf_cpu(void) + return ret; + } + ++static unsigned int intel_pstate_scale_freq_ref(struct cpufreq_policy *policy) ++{ ++ if (READ_ONCE(all_cpu_data[policy->cpu]->capacity_perf)) ++ return policy->cpuinfo.max_freq; ++ ++ return 0; ++} ++ + static void intel_pstate_update_freq_limits(struct cpudata *cpu) + { + int scaling = cpu->pstate.scaling; +@@ -3092,6 +3100,7 @@ static struct cpufreq_driver intel_pstate = { + .offline = intel_pstate_cpu_offline, + .online = intel_pstate_cpu_online, + .update_limits = intel_pstate_update_limits, ++ .scale_freq_ref = intel_pstate_scale_freq_ref, + .name = "intel_pstate", + }; + +@@ -3415,6 +3424,7 @@ static struct cpufreq_driver intel_cpufreq = { + .suspend = intel_cpufreq_suspend, + .resume = intel_pstate_resume, + .update_limits = intel_pstate_update_limits, ++ .scale_freq_ref = intel_pstate_scale_freq_ref, + .name = "intel_cpufreq", + }; + +diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h +--- a/include/linux/cpufreq.h ++++ b/include/linux/cpufreq.h +@@ -420,6 +420,9 @@ struct cpufreq_driver { + /* Will be called after the driver is fully initialized */ + void (*ready)(struct cpufreq_policy *policy); + ++ /* Return the capacity reference frequency for policy. */ ++ unsigned int (*scale_freq_ref)(struct cpufreq_policy *policy); ++ + struct freq_attr **attr; + + /* platform specific boost support code */ diff --git a/pkgbuilds/linux-omarchy/0213-pm-fixes-3.patch.sig b/pkgbuilds/linux-omarchy/0213-pm-fixes-3.patch.sig new file mode 100644 index 0000000..8400915 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0213-pm-fixes-3.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0280-mm-updates.patch b/pkgbuilds/linux-omarchy/0280-mm-updates.patch index 46a53c5..e0bf148 100644 --- a/pkgbuilds/linux-omarchy/0280-mm-updates.patch +++ b/pkgbuilds/linux-omarchy/0280-mm-updates.patch @@ -278,25 +278,7 @@ diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h -@@ -270,10 +270,15 @@ struct mem_cgroup { - #endif - int kmemcg_id; - -- struct memcg_vmstats_percpu __percpu *vmstats_percpu; -- - #ifdef CONFIG_CGROUP_WRITEBACK - struct list_head cgwb_list; -+#endif -+ -+ /* Keep the hot per-CPU stats pointer away from memory event counters. */ -+ struct memcg_vmstats_percpu __percpu *vmstats_percpu -+ ____cacheline_aligned_in_smp; -+ -+#ifdef CONFIG_CGROUP_WRITEBACK - struct wb_domain cgwb_domain; - struct memcg_cgwb_frn cgwb_frn[MEMCG_CGWB_FRN_CNT]; - #endif -@@ -947,6 +952,8 @@ unsigned long memcg_page_state_output(struct mem_cgroup *memcg, int item); +@@ -952,6 +952,8 @@ unsigned long memcg_page_state_output(struct mem_cgroup *memcg, int item); bool memcg_stat_item_valid(int idx); bool memcg_vm_event_item_valid(enum vm_event_item idx); unsigned long lruvec_page_state(struct lruvec *lruvec, enum node_stat_item idx); @@ -305,7 +287,7 @@ diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h unsigned long lruvec_page_state_local(struct lruvec *lruvec, enum node_stat_item idx); -@@ -1399,6 +1406,12 @@ static inline unsigned long lruvec_page_state(struct lruvec *lruvec, +@@ -1404,6 +1406,12 @@ static inline unsigned long lruvec_page_state(struct lruvec *lruvec, return node_page_state(lruvec_pgdat(lruvec), idx); } @@ -576,66 +558,6 @@ diff --git a/lib/maple_tree.c b/lib/maple_tree.c return wr_slot_store; return wr_node_store; -@@ -3793,35 +3825,40 @@ int mas_alloc_cyclic(struct ma_state *mas, unsigned long *startp, - void *entry, unsigned long range_lo, unsigned long range_hi, - unsigned long *next, gfp_t gfp) - { -- unsigned long min = range_lo; -- int ret = 0; -+ int ret; -+ unsigned long min; -+ -+ min = range_lo; -+ do { -+ range_lo = max(min, *next); -+ ret = mas_empty_area(mas, range_lo, range_hi, 1); -+ if (ret < 0 && range_lo > min) { -+ mas_reset(mas); -+ ret = mas_empty_area(mas, min, range_hi, 1); -+ if (ret == 0) -+ ret = 1; -+ } -+ if (ret < 0) -+ goto out; -+ -+ mas_insert(mas, entry); -+ } while (mas_nomem(mas, gfp)); -+ -+ if (mas_is_err(mas)) { -+ ret = xa_err(mas->node); -+ goto out; -+ } - -- range_lo = max(min, *next); -- ret = mas_empty_area(mas, range_lo, range_hi, 1); - if ((mas->tree->ma_flags & MT_FLAGS_ALLOC_WRAPPED) && ret == 0) { - mas->tree->ma_flags &= ~MT_FLAGS_ALLOC_WRAPPED; - ret = 1; - } -- if (ret < 0 && range_lo > min) { -- mas_reset(mas); -- ret = mas_empty_area(mas, min, range_hi, 1); -- if (ret == 0) -- ret = 1; -- } -- if (ret < 0) -- return ret; -- -- do { -- mas_insert(mas, entry); -- } while (mas_nomem(mas, gfp)); -- if (mas_is_err(mas)) -- return xa_err(mas->node); -- - *startp = mas->index; - *next = *startp + 1; - if (*next == 0) - mas->tree->ma_flags |= MT_FLAGS_ALLOC_WRAPPED; - -+out: - mas_destroy(mas); - return ret; - } diff --git a/mm/khugepaged.c b/mm/khugepaged.c --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -898,24 +820,7 @@ diff --git a/mm/swap.c b/mm/swap.c static void lru_activate(struct lruvec *lruvec, struct folio *folio) { long nr_pages = folio_nr_pages(folio); -@@ -1153,7 +1086,16 @@ static void lruvec_reparent_lru(struct lruvec *child_lruvec, - for_each_managed_zone_pgdat(zone, NODE_DATA(nid), zid, MAX_NR_ZONES - 1) { - unsigned long size = mem_cgroup_get_zone_lru_size(child_lruvec, lru, zid); - -+ if (!size) -+ continue; -+ -+ /* -+ * The folios are accounted to the parent from now on, so the -+ * size has to be moved, not just copied. Leaving it behind -+ * makes the dying child describe folios it no longer owns. -+ */ - mem_cgroup_update_lru_size(parent_lruvec, lru, zid, size); -+ mem_cgroup_update_lru_size(child_lruvec, lru, zid, -(long)size); - } - } - -@@ -1164,8 +1106,6 @@ void lru_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, int +@@ -1173,8 +1106,6 @@ void lru_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, int child_lruvec = mem_cgroup_lruvec(memcg, NODE_DATA(nid)); parent_lruvec = mem_cgroup_lruvec(parent, NODE_DATA(nid)); @@ -1129,20 +1034,7 @@ diff --git a/mm/vmscan.c b/mm/vmscan.c pte_t *pte = pvmw->pte; unsigned long addr = pvmw->address; struct vm_area_struct *vma = pvmw->vma; -@@ -4571,7 +4629,12 @@ void lru_gen_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, - for_each_managed_zone_pgdat(zone, NODE_DATA(nid), zid, MAX_NR_ZONES - 1) { - unsigned long size = mem_cgroup_get_zone_lru_size(child_lruvec, lru, zid); - -+ if (!size) -+ continue; -+ -+ /* Move the accounting, do not duplicate it. */ - mem_cgroup_update_lru_size(parent_lruvec, lru, zid, size); -+ mem_cgroup_update_lru_size(child_lruvec, lru, zid, -(long)size); - } - } - } -@@ -4814,7 +4877,8 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, +@@ -4819,7 +4877,8 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, struct reclaim_stat stat; struct lru_gen_mm_walk *walk; int scanned, reclaimed; @@ -1152,7 +1044,7 @@ diff --git a/mm/vmscan.c b/mm/vmscan.c bool skip_retry = false; struct mem_cgroup *memcg = lruvec_memcg(lruvec); struct pglist_data *pgdat = lruvec_pgdat(lruvec); -@@ -4826,6 +4890,7 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, +@@ -4831,6 +4890,7 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, scanned = isolate_folios(nr_to_scan, lruvec, sc, swappiness, &list, &isolated, &type, &type_scanned); @@ -1160,7 +1052,7 @@ diff --git a/mm/vmscan.c b/mm/vmscan.c /* Scanning may have emptied the oldest gen, flush it */ if (scanned) -@@ -4838,6 +4903,7 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, +@@ -4843,6 +4903,7 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, retry: reclaimed = shrink_folio_list(&list, pgdat, sc, &stat, false, memcg); sc->nr_reclaimed += reclaimed; @@ -1168,7 +1060,7 @@ diff --git a/mm/vmscan.c b/mm/vmscan.c /* Retry pass is only meant for clean folios without new isolation */ if (isolated) handle_reclaim_writeback(isolated, pgdat, sc, &stat); -@@ -4887,6 +4953,10 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, +@@ -4892,6 +4953,10 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, goto retry; } diff --git a/pkgbuilds/linux-omarchy/0280-mm-updates.patch.sig b/pkgbuilds/linux-omarchy/0280-mm-updates.patch.sig index 4e1b338..8a29396 100644 Binary files a/pkgbuilds/linux-omarchy/0280-mm-updates.patch.sig and b/pkgbuilds/linux-omarchy/0280-mm-updates.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0281-mm-no-deferred-kernel-page-table-free-at-boot.patch b/pkgbuilds/linux-omarchy/0281-mm-no-deferred-kernel-page-table-free-at-boot.patch new file mode 100644 index 0000000..e8e9809 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0281-mm-no-deferred-kernel-page-table-free-at-boot.patch @@ -0,0 +1,35 @@ +diff --git a/mm/pgtable-generic.c b/mm/pgtable-generic.c +--- a/mm/pgtable-generic.c ++++ b/mm/pgtable-generic.c +@@ -438,12 +438,30 @@ static void kernel_pgtable_work_func(struct work_struct *work) + __pagetable_free(pt); + } + ++static void schedule_kernel_pgtable_free(void) ++{ ++ schedule_work(&kernel_pgtable_work.work); ++} ++ + void pagetable_free_kernel(struct ptdesc *pt) + { + spin_lock(&kernel_pgtable_work.lock); + list_add(&pt->pt_list, &kernel_pgtable_work.list); + spin_unlock(&kernel_pgtable_work.lock); + +- schedule_work(&kernel_pgtable_work.work); ++ /* ++ * The workqueue may not exist yet while the system is booting. ++ * kernel_pgtable_drain_early() schedules the work once it does. ++ */ ++ if (system_state != SYSTEM_BOOTING) ++ schedule_kernel_pgtable_free(); + } ++ ++static int __init kernel_pgtable_drain_early(void) ++{ ++ /* Free the kernel page tables queued while booting. */ ++ schedule_kernel_pgtable_free(); ++ return 0; ++} ++core_initcall(kernel_pgtable_drain_early); + #endif diff --git a/pkgbuilds/linux-omarchy/0281-mm-no-deferred-kernel-page-table-free-at-boot.patch.sig b/pkgbuilds/linux-omarchy/0281-mm-no-deferred-kernel-page-table-free-at-boot.patch.sig new file mode 100644 index 0000000..cf29026 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0281-mm-no-deferred-kernel-page-table-free-at-boot.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0296-x86-mm-pmd-modify-keep-dirty-bit.patch b/pkgbuilds/linux-omarchy/0296-x86-mm-pmd-modify-keep-dirty-bit.patch deleted file mode 100644 index 602ef86..0000000 --- a/pkgbuilds/linux-omarchy/0296-x86-mm-pmd-modify-keep-dirty-bit.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h ---- a/arch/x86/include/asm/pgtable.h -+++ b/arch/x86/include/asm/pgtable.h -@@ -806,7 +806,7 @@ static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot) - pmdval_t val = pmd_val(pmd), oldval = val; - pmd_t pmd_result; - -- val &= (_HPAGE_CHG_MASK & ~_PAGE_DIRTY); -+ val &= _HPAGE_CHG_MASK; - val |= check_pgprot(newprot) & ~_HPAGE_CHG_MASK; - val = flip_protnone_guard(oldval, val, PHYSICAL_PMD_PAGE_MASK); - diff --git a/pkgbuilds/linux-omarchy/0296-x86-mm-pmd-modify-keep-dirty-bit.patch.sig b/pkgbuilds/linux-omarchy/0296-x86-mm-pmd-modify-keep-dirty-bit.patch.sig deleted file mode 100644 index b01f81d..0000000 Binary files a/pkgbuilds/linux-omarchy/0296-x86-mm-pmd-modify-keep-dirty-bit.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/0300-btrfs.patch b/pkgbuilds/linux-omarchy/0300-btrfs.patch index 0cb9f8d..837f7bc 100644 --- a/pkgbuilds/linux-omarchy/0300-btrfs.patch +++ b/pkgbuilds/linux-omarchy/0300-btrfs.patch @@ -1,50 +1,7 @@ -diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c ---- a/fs/btrfs/acl.c -+++ b/fs/btrfs/acl.c -@@ -15,6 +15,7 @@ - #include "xattr.h" - #include "acl.h" - #include "misc.h" -+#include "btrfs_inode.h" - - struct posix_acl *btrfs_get_acl(struct inode *inode, int type, bool rcu) - { -@@ -107,6 +108,9 @@ int btrfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, - struct inode *inode = d_inode(dentry); - umode_t old_mode = inode->i_mode; - -+ if (btrfs_root_readonly(BTRFS_I(inode)->root)) -+ return -EROFS; -+ - if (type == ACL_TYPE_ACCESS && acl) { - ret = posix_acl_update_mode(idmap, inode, - &inode->i_mode, &acl); diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c -@@ -2047,6 +2047,11 @@ static int btrfs_reclaim_block_group(struct btrfs_block_group *bg, int *reclaime - - trace_btrfs_reclaim_block_group(bg); - ret = btrfs_relocate_chunk(fs_info, bg->start, false); -+ if (btrfs_is_zoned(fs_info) && ret == -EAGAIN) { -+ btrfs_dec_block_group_ro(bg); -+ btrfs_debug(fs_info, "deferring reclaim of chunk %llu", bg->start); -+ return ret; -+ } - if (ret) { - btrfs_dec_block_group_ro(bg); - btrfs_err(fs_info, "error relocating chunk %llu", -@@ -2113,7 +2118,8 @@ void btrfs_reclaim_block_groups(struct btrfs_fs_info *fs_info, unsigned int limi - spin_unlock(&fs_info->unused_bgs_lock); - ret = btrfs_reclaim_block_group(bg, &reclaimed); - -- if (ret && !READ_ONCE(space_info->periodic_reclaim)) -+ if ((btrfs_is_zoned(fs_info) && ret == -EAGAIN) || -+ (ret && !READ_ONCE(space_info->periodic_reclaim))) - btrfs_link_bg_list(bg, &retry_list); - btrfs_put_block_group(bg); - -@@ -2624,10 +2630,9 @@ static int fill_dummy_bgs(struct btrfs_fs_info *fs_info) +@@ -2630,10 +2630,9 @@ static int fill_dummy_bgs(struct btrfs_fs_info *fs_info) /* Fill dummy cache as FULL */ bg->length = map->chunk_len; @@ -56,7 +13,7 @@ diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c bg->space_info = btrfs_find_space_info(fs_info, bg->flags); ret = btrfs_add_block_group_cache(bg); /* -@@ -3916,7 +3921,7 @@ int btrfs_update_block_group(struct btrfs_trans_handle *trans, +@@ -3922,7 +3921,7 @@ int btrfs_update_block_group(struct btrfs_trans_handle *trans, old_val += num_bytes; cache->used = old_val; cache->reserved -= num_bytes; @@ -311,43 +268,7 @@ diff --git a/fs/btrfs/defrag.c b/fs/btrfs/defrag.c kfree(entry); } } -@@ -1130,20 +1130,15 @@ static_assert(PAGE_ALIGNED(CLUSTER_SIZE)); - * - * - Extent bits are locked - */ --static int defrag_one_locked_target(struct btrfs_inode *inode, -- struct defrag_target_range *target, -- struct folio **folios, int nr_pages, -- struct extent_state **cached_state) -+static void defrag_one_locked_target(struct btrfs_inode *inode, -+ struct defrag_target_range *target, -+ struct folio **folios, int nr_pages, -+ struct extent_state **cached_state) - { - struct btrfs_fs_info *fs_info = inode->root->fs_info; -- struct extent_changeset *data_reserved = NULL; - const u64 start = target->start; - const u64 len = target->len; -- int ret = 0; - -- ret = btrfs_delalloc_reserve_space(inode, &data_reserved, start, len); -- if (ret < 0) -- return ret; - btrfs_clear_extent_bit(&inode->io_tree, start, start + len - 1, - EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING | - EXTENT_DEFRAG, cached_state); -@@ -1164,10 +1159,6 @@ static int defrag_one_locked_target(struct btrfs_inode *inode, - continue; - btrfs_folio_clamp_set_dirty(fs_info, folio, start, len); - } -- btrfs_delalloc_release_extents(inode, len); -- extent_changeset_free(data_reserved); -- -- return ret; - } - - static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len, -@@ -1178,11 +1169,13 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len, +@@ -1169,7 +1169,7 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len, struct defrag_target_range *entry; struct defrag_target_range *tmp; LIST_HEAD(target_list); @@ -356,69 +277,45 @@ diff --git a/fs/btrfs/defrag.c b/fs/btrfs/defrag.c const u32 sectorsize = inode->root->fs_info->sectorsize; u64 cur = start; const unsigned int nr_pages = ((start + len - 1) >> PAGE_SHIFT) - - (start >> PAGE_SHIFT) + 1; -+ struct extent_changeset *data_reserved = NULL; -+ u64 last_defrag_end = start; - int ret = 0; - - ASSERT(nr_pages <= CLUSTER_SIZE / PAGE_SIZE); -@@ -1192,6 +1185,20 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len, - if (!folios) - return -ENOMEM; - -+ /* -+ * Reserve delalloc space before locking the range and before locking -+ * and dirtying any folios - otherwise we could deadlock, for example -+ * after defrag of one range we dirty folios and keep them locked when -+ * we move to the next range, so reserving delalloc space right before -+ * each range could trigger flushing of delalloc and deadlock on the -+ * extent lock or trigger a transaction commit with flushoncommit, which -+ * can either deadlock on the lock of a folio made dirty in the previous -+ * range or the extent lock. -+ */ -+ ret = btrfs_delalloc_reserve_space(inode, &data_reserved, start, len); +@@ -1196,10 +1196,8 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len, + * range or the extent lock. + */ + ret = btrfs_delalloc_reserve_space(inode, &data_reserved, start, len); +- if (ret < 0) { +- kfree(folios); + if (ret < 0) -+ return ret; -+ + return ret; +- } + /* Prepare all pages */ for (int i = 0; cur < start + len && i < nr_pages; i++) { - folios[i] = defrag_prepare_one_folio(inode, cur >> PAGE_SHIFT); -@@ -1225,15 +1232,12 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len, +@@ -1234,16 +1232,12 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len, if (ret < 0) goto unlock_extent; - list_for_each_entry(entry, &target_list, list) { -- ret = defrag_one_locked_target(inode, entry, folios, nr_pages, -- &cached_state); -- if (ret < 0) -- break; ++ list_for_each_entry_safe(entry, tmp, &target_list, list) { + defrag_one_locked_target(inode, entry, folios, nr_pages, &cached_state); + if (entry->start > last_defrag_end) + btrfs_delalloc_release_space(inode, data_reserved, last_defrag_end, + entry->start - last_defrag_end, true); + last_defrag_end = entry->start + entry->len; - } - - list_for_each_entry_safe(entry, tmp, &target_list, list) { +- list_for_each_entry_safe(entry, tmp, &target_list, list) { - list_del_init(&entry->list); -+ defrag_one_locked_target(inode, entry, folios, nr_pages, &cached_state); -+ if (entry->start > last_defrag_end) -+ btrfs_delalloc_release_space(inode, data_reserved, last_defrag_end, -+ entry->start - last_defrag_end, true); -+ last_defrag_end = entry->start + entry->len; kfree(entry); } unlock_extent: -@@ -1245,7 +1249,12 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len, +@@ -1255,7 +1249,6 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len, folio_unlock(folios[i]); folio_put(folios[i]); } - kfree(folios); -+ btrfs_delalloc_release_extents(inode, len); -+ if (last_defrag_end < start + len) -+ btrfs_delalloc_release_space(inode, data_reserved, last_defrag_end, -+ start + len - last_defrag_end, true); -+ extent_changeset_free(data_reserved); -+ - return ret; - } - -@@ -1310,10 +1319,8 @@ static int defrag_one_cluster(struct btrfs_inode *inode, + btrfs_delalloc_release_extents(inode, len); + if (last_defrag_end < start + len) + btrfs_delalloc_release_space(inode, data_reserved, last_defrag_end, +@@ -1326,10 +1319,8 @@ static int defrag_one_cluster(struct btrfs_inode *inode, inode->root->fs_info->sectorsize_bits; } out: @@ -744,29 +641,7 @@ diff --git a/fs/btrfs/direct-io.c b/fs/btrfs/direct-io.c diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c -@@ -271,14 +271,15 @@ int btree_csum_one_bio(struct btrfs_bio *bbio) - return -EIO; - - /* -- * If an extent_buffer is marked as EXTENT_BUFFER_ZONED_ZEROOUT, don't -- * checksum it but zero-out its content. This is done to preserve -- * ordering of I/O without unnecessarily writing out data. -+ * An extent_buffer marked EXTENT_BUFFER_ZONED_ZEROOUT is written out as -+ * zeros to preserve ordering of I/O without persisting the now -+ * unnecessary block. The bio is fed from the shared zero page (see -+ * write_one_eb()), so there is nothing to checksum here. Crucially, the -+ * buffer's own content is left intact: it may still be referenced, e.g. -+ * btrfs_free_tree_block() reads its header to add a delayed reference. - */ -- if (test_bit(EXTENT_BUFFER_ZONED_ZEROOUT, &eb->bflags)) { -- memzero_extent_buffer(eb, 0, eb->len); -+ if (test_bit(EXTENT_BUFFER_ZONED_ZEROOUT, &eb->bflags)) - return 0; -- } - - if (WARN_ON_ONCE(found_start != eb->start)) - return -EIO; -@@ -590,12 +591,13 @@ static const struct address_space_operations btree_aops = { +@@ -591,12 +591,13 @@ static const struct address_space_operations btree_aops = { struct extent_buffer *btrfs_find_create_tree_block( struct btrfs_fs_info *fs_info, @@ -781,7 +656,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c } /* -@@ -608,12 +610,13 @@ struct extent_buffer *btrfs_find_create_tree_block( +@@ -609,12 +610,13 @@ struct extent_buffer *btrfs_find_create_tree_block( struct extent_buffer *read_tree_block(struct btrfs_fs_info *fs_info, u64 bytenr, struct btrfs_tree_parent_check *check) { @@ -796,7 +671,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c check->level); if (IS_ERR(buf)) return buf; -@@ -666,10 +669,7 @@ static struct btrfs_root *btrfs_alloc_root(struct btrfs_fs_info *fs_info, +@@ -667,10 +669,7 @@ static struct btrfs_root *btrfs_alloc_root(struct btrfs_fs_info *fs_info, init_waitqueue_head(&root->log_commit_wait[1]); INIT_LIST_HEAD(&root->log_ctxs[0]); INIT_LIST_HEAD(&root->log_ctxs[1]); @@ -807,7 +682,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c refcount_set(&root->refs, 1); atomic_set(&root->snapshot_force_cow, 0); atomic_set(&root->nr_swapfiles, 0); -@@ -2052,7 +2052,7 @@ static int btrfs_replay_log(struct btrfs_fs_info *fs_info, +@@ -2053,7 +2052,7 @@ static int btrfs_replay_log(struct btrfs_fs_info *fs_info, if (IS_ERR(log_tree_root->node)) { ret = PTR_ERR(log_tree_root->node); log_tree_root->node = NULL; @@ -816,7 +691,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c btrfs_put_root(log_tree_root); return ret; } -@@ -2062,7 +2062,7 @@ static int btrfs_replay_log(struct btrfs_fs_info *fs_info, +@@ -2063,7 +2062,7 @@ static int btrfs_replay_log(struct btrfs_fs_info *fs_info, btrfs_put_root(log_tree_root); if (unlikely(ret)) { ASSERT(BTRFS_FS_ERROR(fs_info) != 0); @@ -825,7 +700,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c return ret; } -@@ -2303,8 +2303,8 @@ static int btrfs_read_roots(struct btrfs_fs_info *fs_info) +@@ -2304,8 +2303,8 @@ static int btrfs_read_roots(struct btrfs_fs_info *fs_info) return 0; out: @@ -836,7 +711,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c return ret; } -@@ -2395,8 +2395,8 @@ static int validate_sys_chunk_array(const struct btrfs_fs_info *fs_info, +@@ -2400,8 +2399,8 @@ static int validate_sys_chunk_array(const struct btrfs_fs_info *fs_info, int btrfs_validate_super(const struct btrfs_fs_info *fs_info, const struct btrfs_super_block *sb, int mirror_num) { @@ -847,7 +722,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c int ret = 0; const bool ignore_flags = btrfs_test_opt(fs_info, IGNORESUPERFLAGS); -@@ -2438,24 +2438,24 @@ int btrfs_validate_super(const struct btrfs_fs_info *fs_info, +@@ -2443,24 +2442,24 @@ int btrfs_validate_super(const struct btrfs_fs_info *fs_info, */ if (unlikely(!is_power_of_2(sectorsize) || sectorsize < BTRFS_MIN_BLOCKSIZE || sectorsize > BTRFS_MAX_METADATA_BLOCKSIZE)) { @@ -876,7 +751,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c le32_to_cpu(sb->__unused_leafsize), nodesize); ret = -EINVAL; } -@@ -2905,7 +2905,6 @@ void btrfs_init_fs_info(struct btrfs_fs_info *fs_info) +@@ -2910,7 +2909,6 @@ void btrfs_init_fs_info(struct btrfs_fs_info *fs_info) fs_info->nodesize = 4096; fs_info->sectorsize = 4096; fs_info->sectorsize_bits = ilog2(4096); @@ -884,7 +759,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c /* Default compress algorithm when user does -o compress */ fs_info->compress_type = BTRFS_COMPRESS_ZLIB; -@@ -2979,8 +2978,8 @@ static int btrfs_uuid_rescan_kthread(void *data) +@@ -2984,8 +2982,8 @@ static int btrfs_uuid_rescan_kthread(void *data) ret = btrfs_uuid_tree_iterate(fs_info); if (ret < 0) { if (ret != -EINTR) @@ -895,7 +770,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c up(&fs_info->uuid_tree_rescan_sem); return ret; } -@@ -3083,7 +3082,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) +@@ -3088,7 +3086,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) ret = btrfs_rebuild_free_space_tree(fs_info); if (ret) { btrfs_warn(fs_info, @@ -904,7 +779,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c return ret; } } -@@ -3094,7 +3093,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) +@@ -3099,7 +3097,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) ret = btrfs_delete_free_space_tree(fs_info); if (ret) { btrfs_warn(fs_info, @@ -913,7 +788,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c return ret; } } -@@ -3105,7 +3104,8 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) +@@ -3110,7 +3108,8 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) */ ret = btrfs_delete_orphan_free_space_entries(fs_info); if (ret < 0) { @@ -923,7 +798,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c return ret; } /* -@@ -3139,7 +3139,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) +@@ -3144,7 +3143,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) ret = btrfs_recover_relocation(fs_info); mutex_unlock(&fs_info->cleaner_mutex); if (ret < 0) { @@ -932,7 +807,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c return ret; } -@@ -3149,7 +3149,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) +@@ -3154,7 +3153,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) ret = btrfs_create_free_space_tree(fs_info); if (ret) { btrfs_warn(fs_info, @@ -941,7 +816,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c return ret; } } -@@ -3177,7 +3177,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) +@@ -3182,7 +3181,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info) ret = btrfs_create_uuid_tree(fs_info); if (ret) { btrfs_warn(fs_info, @@ -950,46 +825,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c return ret; } } -@@ -3314,6 +3314,8 @@ static void invalidate_and_check_btree_folios(struct btrfs_fs_info *fs_info) - */ - rcu_read_lock(); - xa_for_each(&fs_info->buffer_tree, index, eb) { -+ unsigned int refs; -+ - /* Increase the ref so that the eb won't disappear. */ - if (!refcount_inc_not_zero(&eb->refs)) - continue; -@@ -3323,17 +3325,27 @@ static void invalidate_and_check_btree_folios(struct btrfs_fs_info *fs_info) - if (test_bit(EXTENT_BUFFER_READING, &eb->bflags)) - wait_on_bit_io(&eb->bflags, EXTENT_BUFFER_READING, - TASK_UNINTERRUPTIBLE); -+ /* -+ * We hold the spinlock to make sure above -+ * EXTENT_BUFFER_READING flag is cleared with the held -+ * ref dropped. -+ * Or we can hit a race window and lead to false alerts. -+ */ -+ spin_lock(&eb->refs_lock); -+ refs = refcount_read(&eb->refs); -+ spin_unlock(&eb->refs_lock); -+ - /* - * The refs threshold is 2, one held by us at the beginning - * of the loop, one for the ownership in the buffer tree. - */ -- if (unlikely(refcount_read(&eb->refs) > 2 || extent_buffer_under_io(eb))) { -+ if (unlikely(refs > 2 || extent_buffer_under_io(eb))) { - WARN_ON_ONCE(IS_ENABLED(CONFIG_BTRFS_DEBUG)); - btrfs_warn(fs_info, - "unable to release extent buffer %llu owner %llu gen %llu refs %u flags 0x%lx", - eb->start, btrfs_header_owner(eb), - btrfs_header_generation(eb), -- refcount_read(&eb->refs), eb->bflags); -+ refs, eb->bflags); - } - free_extent_buffer(eb); - rcu_read_lock(); -@@ -3355,7 +3367,6 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device +@@ -3372,7 +3371,6 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device { u32 sectorsize; u32 nodesize; @@ -997,7 +833,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c u64 generation; u16 csum_type; struct btrfs_super_block *disk_super; -@@ -3464,7 +3475,6 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device +@@ -3481,7 +3479,6 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device /* Set up fs_info before parsing mount options */ nodesize = btrfs_super_nodesize(disk_super); sectorsize = btrfs_super_sectorsize(disk_super); @@ -1005,7 +841,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c fs_info->dirty_metadata_batch = nodesize * (1 + ilog2(nr_cpu_ids)); fs_info->delalloc_batch = sectorsize * 512 * (1 + ilog2(nr_cpu_ids)); -@@ -3483,7 +3493,6 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device +@@ -3500,7 +3497,6 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device else fs_info->block_max_order = calc_block_max_order(fs_info->sectorsize_bits); fs_info->csums_per_leaf = BTRFS_MAX_ITEM_SIZE(fs_info) / fs_info->csum_size; @@ -1013,7 +849,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c fs_info->fs_devices->fs_info = fs_info; if (fs_info->sectorsize > PAGE_SIZE) -@@ -3549,7 +3558,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device +@@ -3566,7 +3562,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device ret = btrfs_read_sys_array(fs_info); mutex_unlock(&fs_info->chunk_mutex); if (ret) { @@ -1022,7 +858,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c goto fail_sb_buffer; } -@@ -3568,7 +3577,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device +@@ -3585,7 +3581,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device ret = btrfs_read_chunk_tree(fs_info); if (ret) { @@ -1031,7 +867,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c goto fail_tree_roots; } -@@ -3598,7 +3607,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device +@@ -3615,7 +3611,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device ret = btrfs_get_dev_zone_info_all_devices(fs_info); if (ret) { btrfs_err(fs_info, @@ -1040,7 +876,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c goto fail_block_groups; } -@@ -3621,72 +3630,73 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device +@@ -3638,72 +3634,73 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device ret = btrfs_verify_dev_extents(fs_info); if (ret) { btrfs_err(fs_info, @@ -1129,7 +965,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c goto fail_sysfs; } -@@ -3742,7 +3752,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device +@@ -3759,7 +3756,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device fs_info->fs_root = btrfs_get_fs_root(fs_info, BTRFS_FS_TREE_OBJECTID, true); if (IS_ERR(fs_info->fs_root)) { ret = PTR_ERR(fs_info->fs_root); @@ -1138,7 +974,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c fs_info->fs_root = NULL; goto fail_qgroup; } -@@ -3763,7 +3773,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device +@@ -3780,7 +3777,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device btrfs_info(fs_info, "checking UUID tree"); ret = btrfs_check_uuid_tree(fs_info); if (ret) { @@ -1147,7 +983,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c close_ctree(fs_info); return ret; } -@@ -3879,8 +3889,8 @@ static int write_dev_supers(struct btrfs_device *device, +@@ -3896,8 +3893,8 @@ static int write_dev_supers(struct btrfs_device *device, continue; } else if (ret < 0) { btrfs_err(device->fs_info, @@ -1158,7 +994,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c atomic_inc(&device->sb_write_errors); continue; } -@@ -3898,8 +3908,8 @@ static int write_dev_supers(struct btrfs_device *device, +@@ -3915,8 +3912,8 @@ static int write_dev_supers(struct btrfs_device *device, GFP_NOFS); if (IS_ERR(folio)) { btrfs_err(device->fs_info, @@ -1169,7 +1005,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c atomic_inc(&device->sb_write_errors); continue; } -@@ -4381,6 +4391,21 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info) +@@ -4398,6 +4395,21 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info) */ flush_workqueue(fs_info->fixup_workers); @@ -1191,7 +1027,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c /* * Handle the error fs first, as it will flush and wait for all ordered * extents. This will generate delayed iputs, thus we want to handle -@@ -4511,7 +4536,7 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info) +@@ -4528,7 +4540,7 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info) if (!btrfs_is_shutdown(fs_info)) { ret = btrfs_commit_super(fs_info); if (ret) @@ -1200,7 +1036,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c } } -@@ -4550,6 +4575,13 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info) +@@ -4567,6 +4579,13 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info) free_root_pointers(fs_info, true); btrfs_free_fs_roots(fs_info); @@ -1214,7 +1050,7 @@ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c /* * We must make sure there is not any read request to * submit after we stop all workers. -@@ -4998,6 +5030,7 @@ static int btrfs_cleanup_transaction(struct btrfs_fs_info *fs_info) +@@ -5015,6 +5034,7 @@ static int btrfs_cleanup_transaction(struct btrfs_fs_info *fs_info) btrfs_assert_delayed_root_empty(fs_info); btrfs_destroy_all_delalloc_inodes(fs_info); btrfs_drop_all_logs(fs_info); @@ -1461,198 +1297,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c bio_ctrl->wbc->nr_to_write--; -@@ -2379,14 +2397,17 @@ static struct extent_buffer *find_extent_buffer_nolock( - static void end_bbio_meta_write(struct btrfs_bio *bbio) - { - struct extent_buffer *eb = bbio->private; -- struct folio_iter fi; - - if (bbio->bio.bi_status != BLK_STS_OK) - set_btree_ioerr(eb); - -- bio_for_each_folio_all(fi, &bbio->bio) { -- btrfs_meta_folio_clear_writeback(fi.folio, eb); -- } -+ /* -+ * Clear writeback on the buffer's own folios. The bio may carry the -+ * shared zero page instead (EXTENT_BUFFER_ZONED_ZEROOUT), so iterate -+ * the extent buffer folios rather than the bio folios. -+ */ -+ for (int i = 0; i < num_extent_folios(eb); i++) -+ btrfs_meta_folio_clear_writeback(eb->folios[i], eb); - - buffer_tree_clear_mark(eb, PAGECACHE_TAG_WRITEBACK); - clear_and_wake_up_bit(EXTENT_BUFFER_WRITEBACK, &eb->bflags); -@@ -2427,7 +2448,8 @@ static noinline_for_stack void write_one_eb(struct extent_buffer *eb, - struct btrfs_fs_info *fs_info = eb->fs_info; - struct btrfs_bio *bbio; - -- prepare_eb_write(eb); -+ if (!test_bit(EXTENT_BUFFER_ZONED_ZEROOUT, &eb->bflags)) -+ prepare_eb_write(eb); - - bbio = btrfs_bio_alloc(INLINE_EXTENT_BUFFER_PAGES, - REQ_OP_WRITE | REQ_META | wbc_to_write_flags(wbc), -@@ -2447,8 +2469,21 @@ static noinline_for_stack void write_one_eb(struct extent_buffer *eb, - btrfs_meta_folio_set_writeback(folio, eb); - if (!folio_test_dirty(folio)) - wbc->nr_to_write -= folio_nr_pages(folio); -- bio_add_folio_nofail(&bbio->bio, folio, range_len, -- offset_in_folio(folio, range_start)); -+ if (test_bit(EXTENT_BUFFER_ZONED_ZEROOUT, &eb->bflags)) { -+ u32 off = 0; -+ -+ while (off < range_len) { -+ u32 add = min_t(u32, PAGE_SIZE, range_len - off); -+ -+ bio_add_folio_nofail(&bbio->bio, -+ page_folio(ZERO_PAGE(0)), -+ add, 0); -+ off += add; -+ } -+ } else { -+ bio_add_folio_nofail(&bbio->bio, folio, range_len, -+ offset_in_folio(folio, range_start)); -+ } - wbc_account_cgroup_owner(wbc, folio, range_len); - folio_unlock(folio); - } -@@ -2496,6 +2531,76 @@ void btrfs_btree_wait_writeback_range(struct btrfs_fs_info *fs_info, u64 start, - } - } - -+static int write_meta_extent_buffer(struct btrfs_eb_write_context *ctx, -+ struct writeback_control *wbc) -+{ -+ struct extent_buffer *eb = ctx->eb; -+ int ret; -+ -+ ret = btrfs_check_meta_write_pointer(eb->fs_info, ctx); -+ if (ret) -+ return ret; -+ -+ if (!lock_extent_buffer_for_io(eb, wbc)) -+ return 0; -+ -+ /* Implies write in zoned mode. */ -+ if (ctx->zoned_bg) { -+ /* Mark the last eb in the block group. */ -+ btrfs_schedule_zone_finish_bg(ctx->zoned_bg, eb); -+ ctx->zoned_bg->meta_write_pointer += eb->len; -+ } -+ write_one_eb(eb, wbc); -+ return 0; -+} -+ -+/* -+ * On a zoned filesystem, write out the currently dirty metadata extent buffers -+ * of @bg. Used to flush the active metadata/system block group before the -+ * ascending-address walk in btree_writepages(), so that walk can pivot the -+ * active block group away (finishing it) instead of aborting the commit; see -+ * the caller for details. -+ */ -+static void flush_active_meta_bg(struct address_space *mapping, -+ struct writeback_control *wbc, -+ struct btrfs_eb_write_context *ctx, -+ struct btrfs_block_group *bg) -+{ -+ struct btrfs_fs_info *fs_info = inode_to_fs_info(mapping->host); -+ unsigned long index = bg->start >> fs_info->nodesize_bits; -+ unsigned long end = (btrfs_block_group_end(bg) - 1) >> fs_info->nodesize_bits; -+ struct eb_batch batch; -+ unsigned int nr_ebs; -+ -+ ASSERT(btrfs_is_zoned(fs_info)); -+ lockdep_assert_held(&fs_info->zoned_meta_io_lock); -+ -+ eb_batch_init(&batch); -+ while (index <= end && -+ (nr_ebs = buffer_tree_get_ebs_tag(fs_info, &index, end, -+ PAGECACHE_TAG_DIRTY, &batch))) { -+ struct extent_buffer *eb; -+ -+ while ((eb = eb_batch_next(&batch)) != NULL) { -+ ctx->eb = eb; -+ -+ /* -+ * If the eb is behind the write pointer (-EBUSY, e.g. -+ * already being written by someone else) skip it and -+ * carry on. Only a hole at the write pointer (-EAGAIN) -+ * stops the flush. The main walk in btree_writepages() -+ * then deals with it. -+ */ -+ if (write_meta_extent_buffer(ctx, wbc) == -EAGAIN) { -+ eb_batch_release(&batch); -+ return; -+ } -+ } -+ eb_batch_release(&batch); -+ cond_resched(); -+ } -+} -+ - int btree_writepages(struct address_space *mapping, struct writeback_control *wbc) - { - struct btrfs_eb_write_context ctx = { .wbc = wbc }; -@@ -2531,6 +2636,22 @@ int btree_writepages(struct address_space *mapping, struct writeback_control *wb - else - tag = PAGECACHE_TAG_DIRTY; - btrfs_zoned_meta_io_lock(fs_info); -+ -+ /* -+ * On a zoned filesystem, flush the currently active metadata/system -+ * block group(s) first, under this same lock, so the ascending-address -+ * walk below can pivot the active block group instead of aborting the -+ * transaction commit with -EAGAIN. -+ */ -+ if (btrfs_is_zoned(fs_info) && wbc->sync_mode == WB_SYNC_ALL && -+ !wbc->for_sync) { -+ if (fs_info->active_meta_bg) -+ flush_active_meta_bg(mapping, wbc, &ctx, -+ fs_info->active_meta_bg); -+ if (fs_info->active_system_bg) -+ flush_active_meta_bg(mapping, wbc, &ctx, -+ fs_info->active_system_bg); -+ } - retry: - if (wbc->sync_mode == WB_SYNC_ALL) - buffer_tree_tag_for_writeback(fs_info, index, end); -@@ -2541,28 +2662,13 @@ int btree_writepages(struct address_space *mapping, struct writeback_control *wb - while ((eb = eb_batch_next(&batch)) != NULL) { - ctx.eb = eb; - -- ret = btrfs_check_meta_write_pointer(eb->fs_info, &ctx); -- if (ret) { -- if (ret == -EBUSY) -- ret = 0; -- -- if (ret) { -- done = true; -- break; -- } -- continue; -+ ret = write_meta_extent_buffer(&ctx, wbc); -+ if (ret == -EBUSY) { -+ ret = 0; -+ } else if (ret) { -+ done = true; -+ break; - } -- -- if (!lock_extent_buffer_for_io(eb, wbc)) -- continue; -- -- /* Implies write in zoned mode. */ -- if (ctx.zoned_bg) { -- /* Mark the last eb in the block group. */ -- btrfs_schedule_zone_finish_bg(ctx.zoned_bg, eb); -- ctx.zoned_bg->meta_write_pointer += eb->len; -- } -- write_one_eb(eb, wbc); - } - nr_to_write_done = (wbc->nr_to_write <= 0); - eb_batch_release(&batch); -@@ -2888,13 +2994,25 @@ void btrfs_readahead(struct readahead_control *rac) +@@ -2976,13 +2994,25 @@ void btrfs_readahead(struct readahead_control *rac) struct extent_map *em_cached = NULL; struct fsverity_info *vi = NULL; @@ -1681,123 +1326,8 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c if (em_cached) btrfs_free_extent_map(em_cached); -@@ -3129,47 +3247,71 @@ static inline void btrfs_release_extent_buffer(struct extent_buffer *eb) - kmem_cache_free(extent_buffer_cache, eb); - } - -+/* -+ * Claim a slot to track an extent buffer in, evicting the coldest tracked buffer -+ * when the array is full. -+ * -+ * Slots fill in order until the array is full. After that a CLOCK (second -+ * chance) scan advances the hand, clearing one reference bit per step, until -+ * it lands on an unreferenced slot whose buffer is evicted. Clearing a bit per -+ * step bounds the scan to BTRFS_INHIBITED_EBS_SLOTS iterations. -+ */ -+static int btrfs_inhibit_claim_slot(struct btrfs_trans_handle *trans) -+{ -+ int slot; -+ -+ if (trans->nr_inhibited_ebs < BTRFS_INHIBITED_EBS_SLOTS) -+ return trans->nr_inhibited_ebs++; -+ -+ while (trans->inhibited_ebs_referenced & (1U << trans->inhibited_ebs_hand)) { -+ trans->inhibited_ebs_referenced &= ~(1U << trans->inhibited_ebs_hand); -+ trans->inhibited_ebs_hand = -+ (trans->inhibited_ebs_hand + 1) % BTRFS_INHIBITED_EBS_SLOTS; -+ } -+ slot = trans->inhibited_ebs_hand; -+ trans->inhibited_ebs_hand = (trans->inhibited_ebs_hand + 1) % BTRFS_INHIBITED_EBS_SLOTS; -+ -+ atomic_dec(&trans->inhibited_ebs[slot]->writeback_inhibitors); -+ free_extent_buffer(trans->inhibited_ebs[slot]); -+ -+ return slot; -+} -+ - /* - * Inhibit writeback on buffer during transaction. - * - * @trans: transaction handle that will own the inhibitor - * @eb: extent buffer to inhibit writeback on - * -- * Attempt to track this extent buffer in the transaction's inhibited set. If -- * memory allocation fails, the buffer is simply not tracked. It may be written -- * back and need re-COW, which is the original behavior. This is acceptable -- * since inhibiting writeback is an optimization. -+ * Attempt to track this extent buffer in the transaction's inhibited set. When -+ * the set is full the coldest tracked buffer is evicted instead. An untracked -+ * buffer may be written back and need re-COW, which is the original behavior. -+ * This is acceptable since inhibiting writeback is an optimization. - */ - void btrfs_inhibit_eb_writeback(struct btrfs_trans_handle *trans, struct extent_buffer *eb) - { -- unsigned long index = eb->start >> trans->fs_info->nodesize_bits; -- void *old; -+ int slot; - - lockdep_assert_held(&eb->lock); -- /* Check if already inhibited by this handle. */ -- old = xa_load(&trans->writeback_inhibited_ebs, index); -- if (old == eb) -- return; - -- /* Take reference for the xarray entry. */ -+ /* Already tracked: set its reference bit (second chance) and return. */ -+ for (int i = 0; i < trans->nr_inhibited_ebs; i++) { -+ if (trans->inhibited_ebs[i] == eb) { -+ trans->inhibited_ebs_referenced |= 1U << i; -+ return; -+ } -+ } -+ -+ slot = btrfs_inhibit_claim_slot(trans); -+ -+ /* -+ * Pin the eb while the array holds a raw pointer to it; the counter is -+ * what lock_extent_buffer_for_io() checks. -+ */ - refcount_inc(&eb->refs); -- -- old = xa_store(&trans->writeback_inhibited_ebs, index, eb, GFP_NOFS); -- if (xa_is_err(old)) { -- /* Allocation failed, just skip inhibiting this buffer. */ -- free_extent_buffer(eb); -- return; -- } -- -- /* Handle replacement of different eb at same index. */ -- if (old && old != eb) { -- struct extent_buffer *old_eb = old; -- -- atomic_dec(&old_eb->writeback_inhibitors); -- free_extent_buffer(old_eb); -- } -- - atomic_inc(&eb->writeback_inhibitors); -+ trans->inhibited_ebs[slot] = eb; -+ trans->inhibited_ebs_referenced |= 1U << slot; - } - - /* -@@ -3177,22 +3319,18 @@ void btrfs_inhibit_eb_writeback(struct btrfs_trans_handle *trans, struct extent_ - */ - void btrfs_uninhibit_all_eb_writeback(struct btrfs_trans_handle *trans) - { -- struct extent_buffer *eb; -- unsigned long index; -- -- xa_for_each(&trans->writeback_inhibited_ebs, index, eb) { -- atomic_dec(&eb->writeback_inhibitors); -- free_extent_buffer(eb); -+ for (int i = 0; i < trans->nr_inhibited_ebs; i++) { -+ atomic_dec(&trans->inhibited_ebs[i]->writeback_inhibitors); -+ free_extent_buffer(trans->inhibited_ebs[i]); - } -- xa_destroy(&trans->writeback_inhibited_ebs); -+ trans->nr_inhibited_ebs = 0; -+ trans->inhibited_ebs_referenced = 0; -+ trans->inhibited_ebs_hand = 0; +@@ -3298,12 +3328,9 @@ void btrfs_uninhibit_all_eb_writeback(struct btrfs_trans_handle *trans) + trans->inhibited_ebs_hand = 0; } -static struct extent_buffer *__alloc_extent_buffer(struct btrfs_fs_info *fs_info, @@ -1811,7 +1341,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c eb->start = start; eb->len = fs_info->nodesize; eb->fs_info = fs_info; -@@ -3205,7 +3343,15 @@ static struct extent_buffer *__alloc_extent_buffer(struct btrfs_fs_info *fs_info +@@ -3316,7 +3343,15 @@ static struct extent_buffer *__alloc_extent_buffer(struct btrfs_fs_info *fs_info refcount_set(&eb->refs, 1); ASSERT(eb->len <= BTRFS_MAX_METADATA_BLOCKSIZE); @@ -1827,7 +1357,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c return eb; } -@@ -3502,7 +3648,7 @@ static bool check_eb_alignment(struct btrfs_fs_info *fs_info, u64 start) +@@ -3613,7 +3648,7 @@ static bool check_eb_alignment(struct btrfs_fs_info *fs_info, u64 start) * The caller needs to free the existing folios and retry using the same order. */ static int attach_eb_folio_to_filemap(struct extent_buffer *eb, int i, @@ -1836,7 +1366,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c struct extent_buffer **found_eb_ret) { -@@ -3524,6 +3670,7 @@ static int attach_eb_folio_to_filemap(struct extent_buffer *eb, int i, +@@ -3635,6 +3670,7 @@ static int attach_eb_folio_to_filemap(struct extent_buffer *eb, int i, if (!ret) goto finish; @@ -1844,7 +1374,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c existing_folio = filemap_lock_folio(mapping, index + i); /* The page cache only exists for a very short time, just retry. */ if (IS_ERR(existing_folio)) -@@ -3532,7 +3679,27 @@ static int attach_eb_folio_to_filemap(struct extent_buffer *eb, int i, +@@ -3643,7 +3679,27 @@ static int attach_eb_folio_to_filemap(struct extent_buffer *eb, int i, /* For now, we should only have single-page folios for btree inode. */ ASSERT(folio_nr_pages(existing_folio) == 1); @@ -1872,7 +1402,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c folio_unlock(existing_folio); folio_put(existing_folio); return -EAGAIN; -@@ -3563,8 +3730,10 @@ static int attach_eb_folio_to_filemap(struct extent_buffer *eb, int i, +@@ -3674,8 +3730,10 @@ static int attach_eb_folio_to_filemap(struct extent_buffer *eb, int i, eb->folio_size = folio_size(eb->folios[i]); eb->folio_shift = folio_shift(eb->folios[i]); /* Should not fail, as we have preallocated the memory. */ @@ -1884,7 +1414,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c /* * To inform we have an extra eb under allocation, so that * detach_extent_buffer_page() won't release the folio private when the -@@ -3579,13 +3748,104 @@ static int attach_eb_folio_to_filemap(struct extent_buffer *eb, int i, +@@ -3690,13 +3748,104 @@ static int attach_eb_folio_to_filemap(struct extent_buffer *eb, int i, return 0; } @@ -1990,7 +1520,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c u64 lockdep_owner = owner_root; bool page_contig = true; bool uptodate = true; -@@ -3609,9 +3869,14 @@ struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info, +@@ -3720,9 +3869,14 @@ struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info, if (eb) return eb; @@ -2008,7 +1538,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c /* * The reloc trees are just snapshots, so we need them to appear to be -@@ -3622,66 +3887,18 @@ struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info, +@@ -3733,66 +3887,18 @@ struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info, btrfs_set_buffer_lockdep_class(lockdep_owner, eb, level); @@ -2079,7 +1609,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c attached++; /* -@@ -3758,6 +3975,10 @@ struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info, +@@ -3869,6 +3975,10 @@ struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info, out: WARN_ON(!refcount_dec_and_test(&eb->refs)); @@ -2090,71 +1620,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c /* * Any attached folios need to be detached before we unlock them. This * is because when we're inserting our new folios into the mapping, and -@@ -3842,12 +4063,31 @@ static int release_extent_buffer(struct extent_buffer *eb) - return 0; - } - --void free_extent_buffer(struct extent_buffer *eb) -+static void clear_extent_buffer_reading(struct extent_buffer *eb) -+{ -+ clear_and_wake_up_bit(EXTENT_BUFFER_READING, &eb->bflags); -+} -+ -+static void free_extent_buffer_clear_reading(struct extent_buffer *eb, -+ bool clear_reading) - { - int refs; -+ - if (!eb) - return; - -+ /* -+ * We want to clear EXTENT_BUFFER_READING flag and decrease refs -+ * in the same critical section. -+ * This will make sure invalidate_and_check_btree_folios() won't -+ * see an eb with EXTENT_BUFFER_READING cleared but refs not yet -+ * decreased. -+ */ -+ if (clear_reading) { -+ spin_lock(&eb->refs_lock); -+ clear_extent_buffer_reading(eb); -+ } -+ - refs = refcount_read(&eb->refs); - while (1) { - if (test_bit(EXTENT_BUFFER_UNMAPPED, &eb->bflags)) { -@@ -3858,11 +4098,16 @@ void free_extent_buffer(struct extent_buffer *eb) - } - - /* Optimization to avoid locking eb->refs_lock. */ -- if (atomic_try_cmpxchg(&eb->refs.refs, &refs, refs - 1)) -+ if (atomic_try_cmpxchg(&eb->refs.refs, &refs, refs - 1)) { -+ if (clear_reading) -+ spin_unlock(&eb->refs_lock); - return; -+ } - } - -- spin_lock(&eb->refs_lock); -+ if (!clear_reading) -+ spin_lock(&eb->refs_lock); -+ - if (refcount_read(&eb->refs) == 2 && - test_bit(EXTENT_BUFFER_STALE, &eb->bflags) && - !extent_buffer_under_io(eb) && -@@ -3876,6 +4121,11 @@ void free_extent_buffer(struct extent_buffer *eb) - release_extent_buffer(eb); - } - -+void free_extent_buffer(struct extent_buffer *eb) -+{ -+ return free_extent_buffer_clear_reading(eb, false); -+} -+ - void free_extent_buffer_stale(struct extent_buffer *eb) - { - if (!eb) -@@ -3890,6 +4140,32 @@ void free_extent_buffer_stale(struct extent_buffer *eb) +@@ -4030,6 +4140,32 @@ void free_extent_buffer_stale(struct extent_buffer *eb) release_extent_buffer(eb); } @@ -2187,7 +1653,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c void btrfs_clear_buffer_dirty(struct btrfs_trans_handle *trans, struct extent_buffer *eb) { -@@ -3914,26 +4190,42 @@ void btrfs_clear_buffer_dirty(struct btrfs_trans_handle *trans, +@@ -4054,26 +4190,42 @@ void btrfs_clear_buffer_dirty(struct btrfs_trans_handle *trans, return; } @@ -2246,29 +1712,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c } void set_extent_buffer_dirty(struct extent_buffer *eb) -@@ -4001,11 +4293,6 @@ void set_extent_buffer_uptodate(struct extent_buffer *eb) - btrfs_meta_folio_set_uptodate(eb->folios[i], eb); - } - --static void clear_extent_buffer_reading(struct extent_buffer *eb) --{ -- clear_and_wake_up_bit(EXTENT_BUFFER_READING, &eb->bflags); --} -- - static void end_bbio_meta_read(struct btrfs_bio *bbio) - { - struct extent_buffer *eb = bbio->private; -@@ -4029,8 +4316,7 @@ static void end_bbio_meta_read(struct btrfs_bio *bbio) - else - clear_extent_buffer_uptodate(eb); - -- clear_extent_buffer_reading(eb); -- free_extent_buffer(eb); -+ free_extent_buffer_clear_reading(eb, true); - - bio_put(&bbio->bio); - } -@@ -4789,6 +5075,8 @@ void btrfs_readahead_tree_block(struct btrfs_fs_info *fs_info, +@@ -4923,6 +5075,8 @@ void btrfs_readahead_tree_block(struct btrfs_fs_info *fs_info, .level = level, .transid = gen }; @@ -2277,7 +1721,7 @@ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c struct extent_buffer *eb; int ret; -@@ -4797,7 +5085,7 @@ void btrfs_readahead_tree_block(struct btrfs_fs_info *fs_info, +@@ -4931,7 +5085,7 @@ void btrfs_readahead_tree_block(struct btrfs_fs_info *fs_info, check.has_first_key = true; } @@ -2347,197 +1791,10 @@ diff --git a/fs/btrfs/fiemap.c b/fs/btrfs/fiemap.c bool stopped = false; int ret; -@@ -660,7 +660,7 @@ static int extent_fiemap(struct btrfs_inode *inode, - range_end = round_up(start + len, sectorsize); - prev_extent_end = range_start; - -- btrfs_lock_extent(&inode->io_tree, range_start, range_end, &cached_state); -+ btrfs_lock_extent(&inode->io_tree, range_start, range_end - 1, &cached_state); - - ret = fiemap_find_last_extent_offset(inode, path, &last_extent_end); - if (ret < 0) -@@ -840,7 +840,7 @@ static int extent_fiemap(struct btrfs_inode *inode, - } - - out_unlock: -- btrfs_unlock_extent(&inode->io_tree, range_start, range_end, &cached_state); -+ btrfs_unlock_extent(&inode->io_tree, range_start, range_end - 1, &cached_state); - - if (ret == BTRFS_FIEMAP_FLUSH_CACHE) { - btrfs_release_path(path); diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c -@@ -875,70 +875,64 @@ static noinline int prepare_one_folio(struct inode *inode, struct folio **folio_ - - /* - * Locks the extent and properly waits for data=ordered extents to finish -- * before allowing the folios to be modified if need. -+ * before allowing the folios to be modified. - * - * Return: -- * 1 - the extent is locked -- * 0 - the extent is not locked, and everything is OK -+ * 0 - the extent is locked - * -EAGAIN - need to prepare the folios again - */ - static noinline int --lock_and_cleanup_extent_if_need(struct btrfs_inode *inode, struct folio *folio, -- loff_t pos, size_t write_bytes, -- u64 *lockstart, u64 *lockend, bool nowait, -- struct extent_state **cached_state) -+lock_and_cleanup_extent(struct btrfs_inode *inode, struct folio *folio, -+ loff_t pos, size_t write_bytes, -+ u64 *lockstart, u64 *lockend, bool nowait, -+ struct extent_state **cached_state) - { - struct btrfs_fs_info *fs_info = inode->root->fs_info; -+ struct btrfs_ordered_extent *ordered; - u64 start_pos; - u64 last_pos; -- int ret = 0; - - start_pos = round_down(pos, fs_info->sectorsize); - last_pos = round_up(pos + write_bytes, fs_info->sectorsize) - 1; - -- if (start_pos < inode->vfs_inode.i_size) { -- struct btrfs_ordered_extent *ordered; -- -- if (nowait) { -- if (!btrfs_try_lock_extent(&inode->io_tree, start_pos, -- last_pos, cached_state)) { -- folio_unlock(folio); -- folio_put(folio); -- return -EAGAIN; -- } -- } else { -- btrfs_lock_extent(&inode->io_tree, start_pos, last_pos, -- cached_state); -- } -- -- ordered = btrfs_lookup_ordered_range(inode, start_pos, -- last_pos - start_pos + 1); -- if (ordered && -- ordered->file_offset + ordered->num_bytes > start_pos && -- ordered->file_offset <= last_pos) { -- btrfs_unlock_extent(&inode->io_tree, start_pos, last_pos, -- cached_state); -+ if (nowait) { -+ if (!btrfs_try_lock_extent(&inode->io_tree, start_pos, -+ last_pos, cached_state)) { - folio_unlock(folio); - folio_put(folio); -- btrfs_start_ordered_extent(ordered); -- btrfs_put_ordered_extent(ordered); - return -EAGAIN; - } -- if (ordered) -- btrfs_put_ordered_extent(ordered); -- -- *lockstart = start_pos; -- *lockend = last_pos; -- ret = 1; -+ } else { -+ btrfs_lock_extent(&inode->io_tree, start_pos, last_pos, -+ cached_state); - } - -+ ordered = btrfs_lookup_ordered_range(inode, start_pos, -+ last_pos - start_pos + 1); -+ if (ordered && -+ ordered->file_offset + ordered->num_bytes > start_pos && -+ ordered->file_offset <= last_pos) { -+ btrfs_unlock_extent(&inode->io_tree, start_pos, last_pos, -+ cached_state); -+ folio_unlock(folio); -+ folio_put(folio); -+ btrfs_start_ordered_extent(ordered); -+ btrfs_put_ordered_extent(ordered); -+ return -EAGAIN; -+ } -+ if (ordered) -+ btrfs_put_ordered_extent(ordered); -+ -+ *lockstart = start_pos; -+ *lockend = last_pos; -+ - /* - * We should be called after prepare_one_folio() which should have locked - * all pages in the range. - */ - WARN_ON(!folio_test_locked(folio)); - -- return ret; -+ return 0; - } - - /* -@@ -1195,7 +1189,6 @@ static int copy_one_range(struct btrfs_inode *inode, struct iov_iter *iter, - const u64 reserved_start = round_down(start, fs_info->sectorsize); - u64 reserved_len; - struct folio *folio = NULL; -- int extents_locked; - u64 lockstart; - u64 lockend; - bool only_release_metadata = false; -@@ -1253,18 +1246,16 @@ static int copy_one_range(struct btrfs_inode *inode, struct iov_iter *iter, - reserved_len = last_block - reserved_start; - } - -- extents_locked = lock_and_cleanup_extent_if_need(inode, folio, start, -- write_bytes, &lockstart, -- &lockend, nowait, -- &cached_state); -- if (extents_locked < 0) { -- if (!nowait && extents_locked == -EAGAIN) -+ ret = lock_and_cleanup_extent(inode, folio, start, write_bytes, -+ &lockstart, &lockend, nowait, &cached_state); -+ if (ret < 0) { -+ if (!nowait) - goto again; - - btrfs_delalloc_release_extents(inode, reserved_len); - release_space(inode, *data_reserved, reserved_start, reserved_len, - only_release_metadata); -- return extents_locked; -+ return ret; - } - - copied = copy_folio_from_iter_atomic(folio, offset_in_folio(folio, start), -@@ -1288,11 +1279,8 @@ static int copy_one_range(struct btrfs_inode *inode, struct iov_iter *iter, - - /* No copied bytes, unlock, release reserved space and exit. */ - if (copied == 0) { -- if (extents_locked) -- btrfs_unlock_extent(&inode->io_tree, lockstart, lockend, -- &cached_state); -- else -- btrfs_free_extent_state(cached_state); -+ btrfs_unlock_extent(&inode->io_tree, lockstart, lockend, -+ &cached_state); - btrfs_delalloc_release_extents(inode, reserved_len); - release_space(inode, *data_reserved, reserved_start, reserved_len, - only_release_metadata); -@@ -1311,17 +1299,7 @@ static int copy_one_range(struct btrfs_inode *inode, struct iov_iter *iter, - - ret = btrfs_dirty_folio(inode, folio, start, copied, &cached_state, - only_release_metadata); -- /* -- * If we have not locked the extent range, because the range's start -- * offset is >= i_size, we might still have a non-NULL cached extent -- * state, acquired while marking the extent range as delalloc through -- * btrfs_dirty_page(). Therefore free any possible cached extent state -- * to avoid a memory leak. -- */ -- if (extents_locked) -- btrfs_unlock_extent(&inode->io_tree, lockstart, lockend, &cached_state); -- else -- btrfs_free_extent_state(cached_state); -+ btrfs_unlock_extent(&inode->io_tree, lockstart, lockend, &cached_state); - - btrfs_delalloc_release_extents(inode, reserved_len); - if (ret) { -@@ -1595,8 +1573,6 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) +@@ -1573,8 +1573,6 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) else btrfs_inode_lock(inode, BTRFS_ILOCK_MMAP); @@ -2546,7 +1803,7 @@ diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c /* * Before we acquired the inode's lock and the mmap lock, someone may * have dirtied more pages in the target range. We need to make sure -@@ -1679,8 +1655,6 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) +@@ -1657,8 +1655,6 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) if (ret) goto out_release_extents; @@ -2555,7 +1812,7 @@ diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c if (skip_inode_logging(&ctx)) { /* * We've had everything committed since the last time we were -@@ -2683,8 +2657,8 @@ static int btrfs_punch_hole(struct file *file, loff_t offset, loff_t len) +@@ -2663,8 +2659,8 @@ static int btrfs_punch_hole(struct file *file, loff_t offset, loff_t len) lockstart = round_up(offset, fs_info->sectorsize); lockend = round_down(offset + len, fs_info->sectorsize) - 1; @@ -2566,7 +1823,7 @@ diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c /* * Only do this if we are in the same block and we aren't doing the * entire block. -@@ -2888,7 +2862,7 @@ enum { +@@ -2868,7 +2864,7 @@ enum { static int btrfs_zero_range_check_range_boundary(struct btrfs_inode *inode, u64 offset) { @@ -2575,7 +1832,7 @@ diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c struct extent_map *em; int ret; -@@ -2918,7 +2892,7 @@ static int btrfs_zero_range(struct inode *inode, +@@ -2898,7 +2894,7 @@ static int btrfs_zero_range(struct inode *inode, struct extent_changeset *data_reserved = NULL; int ret; u64 alloc_hint = 0; @@ -2584,7 +1841,7 @@ diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c const u64 orig_start = offset; const u64 orig_end = offset + len - 1; u64 alloc_start = round_down(offset, sectorsize); -@@ -2967,8 +2941,8 @@ static int btrfs_zero_range(struct inode *inode, +@@ -2947,8 +2943,8 @@ static int btrfs_zero_range(struct inode *inode, } btrfs_free_extent_map(em); @@ -2735,7 +1992,7 @@ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c return ret; } -@@ -3050,7 +3051,7 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, +@@ -3065,7 +3066,7 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans, u64 qgroup_reserved) { struct btrfs_root *root = inode->root; @@ -2744,7 +2001,7 @@ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c BTRFS_PATH_AUTO_FREE(path); struct extent_buffer *leaf; struct btrfs_key ins; -@@ -3907,7 +3908,7 @@ int btrfs_orphan_cleanup(struct btrfs_root *root) +@@ -3926,7 +3927,7 @@ int btrfs_orphan_cleanup(struct btrfs_root *root) out: if (ret) @@ -2753,7 +2010,7 @@ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c return ret; } -@@ -4210,8 +4211,8 @@ static int btrfs_read_locked_inode(struct btrfs_inode *inode, struct btrfs_path +@@ -4229,8 +4230,8 @@ static int btrfs_read_locked_inode(struct btrfs_inode *inode, struct btrfs_path ret = btrfs_load_inode_props(inode, path); if (ret) btrfs_err(fs_info, @@ -2764,7 +2021,7 @@ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c } /* -@@ -6825,8 +6826,8 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans, +@@ -6844,8 +6845,8 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans, } if (ret) { btrfs_err(fs_info, @@ -2775,7 +2032,7 @@ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c } /* -@@ -10205,6 +10206,8 @@ static void btrfs_free_swapfile_pins(struct inode *inode) +@@ -10224,6 +10225,8 @@ static void btrfs_free_swapfile_pins(struct inode *inode) struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info; struct btrfs_swapfile_pin *sp; struct rb_node *node, *next; @@ -2784,7 +2041,7 @@ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c spin_lock(&fs_info->swapfile_pins_lock); node = rb_first(&fs_info->swapfile_pins); -@@ -10214,15 +10217,24 @@ static void btrfs_free_swapfile_pins(struct inode *inode) +@@ -10233,15 +10236,24 @@ static void btrfs_free_swapfile_pins(struct inode *inode) if (sp->inode == inode) { rb_erase(&sp->node, &fs_info->swapfile_pins); if (sp->is_block_group) { @@ -2811,7 +2068,7 @@ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c } struct btrfs_swap_info { -@@ -10300,8 +10312,10 @@ static int btrfs_swap_activate(struct swap_info_struct *sis, struct file *file, +@@ -10319,8 +10331,10 @@ static int btrfs_swap_activate(struct swap_info_struct *sis, struct file *file, struct btrfs_backref_share_check_ctx *backref_ctx = NULL; struct btrfs_path *path = NULL; int ret = 0; @@ -2822,7 +2079,7 @@ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c /* * Acquire the inode's mmap lock to prevent races with memory mapped -@@ -10551,6 +10565,9 @@ static int btrfs_swap_activate(struct swap_info_struct *sis, struct file *file, +@@ -10570,6 +10584,9 @@ static int btrfs_swap_activate(struct swap_info_struct *sis, struct file *file, ret = 0; else goto out; @@ -2832,7 +2089,7 @@ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c } if (bsi.block_len && -@@ -10598,6 +10615,14 @@ static int btrfs_swap_activate(struct swap_info_struct *sis, struct file *file, +@@ -10617,6 +10634,14 @@ static int btrfs_swap_activate(struct swap_info_struct *sis, struct file *file, if (ret) return ret; @@ -3215,43 +2472,6 @@ diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c } } -@@ -4339,12 +4339,13 @@ static int qgroup_free_reserved_data(struct btrfs_inode *inode, - struct ulist_node *unode; - struct ulist_iterator uiter; - struct extent_changeset changeset; -+ const u32 sectorsize = root->fs_info->sectorsize; -+ const u64 aligned_start = round_down(start, sectorsize); -+ const u64 aligned_len = round_up(start + len, sectorsize) - aligned_start; - u64 freed = 0; - int ret; - - extent_changeset_init_bytes_only(&changeset); -- len = round_up(start + len, root->fs_info->sectorsize); -- start = round_down(start, root->fs_info->sectorsize); - - ULIST_ITER_INIT(&uiter); - while ((unode = ulist_next(&reserved->range_changed, &uiter))) { -@@ -4356,12 +4357,15 @@ static int qgroup_free_reserved_data(struct btrfs_inode *inode, - - extent_changeset_release(&changeset); - -- /* Only free range in range [start, start + len) */ -- if (range_start >= start + len || -- range_start + range_len <= start) -+ /* -+ * Only free the range within -+ * [aligned_start, aligned_start + aligned_len). -+ */ -+ if (range_start >= aligned_start + aligned_len || -+ range_start + range_len <= aligned_start) - continue; -- free_start = max(range_start, start); -- free_len = min(start + len, range_start + range_len) - -+ free_start = max(range_start, aligned_start); -+ free_len = min(aligned_start + aligned_len, range_start + range_len) - - free_start; - /* - * TODO: To also modify reserved->ranges_reserved to reflect diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c --- a/fs/btrfs/raid56.c +++ b/fs/btrfs/raid56.c @@ -3655,87 +2875,7 @@ diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c return false; } -@@ -1537,6 +1538,33 @@ static void clear_reloc_root(struct btrfs_root *root) - clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE, &root->state); - } - -+/* Drop the reloc trees of a relocation that is being deferred and retried. */ -+static void abort_reloc_roots(struct reloc_control *rc, struct list_head *list) -+{ -+ struct btrfs_fs_info *fs_info = rc->extent_root->fs_info; -+ struct btrfs_root *reloc_root, *tmp; -+ -+ list_for_each_entry_safe(reloc_root, tmp, list, root_list) { -+ struct btrfs_root *root; -+ -+ root = btrfs_get_fs_root(fs_info, reloc_root->root_key.offset, false); -+ if (!IS_ERR(root)) { -+ if (root->reloc_root == reloc_root) { -+ clear_reloc_root(root); -+ btrfs_put_root(reloc_root); -+ } -+ btrfs_put_root(root); -+ } -+ -+ btrfs_set_root_refs(&reloc_root->root_item, 0); -+ memset(&reloc_root->root_item.drop_progress, 0, sizeof(struct btrfs_disk_key)); -+ btrfs_set_root_drop_level(&reloc_root->root_item, 0); -+ -+ list_del_init(&reloc_root->root_list); -+ list_add_tail(&reloc_root->reloc_dirty_list, &rc->dirty_subvol_roots); -+ } -+} -+ - static int clean_dirty_subvols(struct reloc_control *rc) - { - struct btrfs_root *root; -@@ -1876,8 +1904,7 @@ int prepare_to_merge(struct reloc_control *rc, int err) - return err; - } - --static noinline_for_stack --void merge_reloc_roots(struct reloc_control *rc) -+static noinline_for_stack int merge_reloc_roots(struct reloc_control *rc) - { - struct btrfs_fs_info *fs_info = rc->extent_root->fs_info; - struct btrfs_root *root; -@@ -1975,7 +2002,15 @@ void merge_reloc_roots(struct reloc_control *rc) - goto again; - } - out: -- if (ret) { -+ if (btrfs_is_zoned(fs_info) && ret == -EAGAIN) { -+ abort_reloc_roots(rc, &reloc_roots); -+ -+ /* New reloc root may be added. */ -+ mutex_lock(&fs_info->reloc_mutex); -+ list_splice_init(&rc->reloc_roots, &reloc_roots); -+ mutex_unlock(&fs_info->reloc_mutex); -+ abort_reloc_roots(rc, &reloc_roots); -+ } else if (ret) { - btrfs_handle_fs_error(fs_info, ret, NULL); - free_reloc_roots(&reloc_roots); - -@@ -2001,6 +2036,7 @@ void merge_reloc_roots(struct reloc_control *rc) - * - * The remaining nodes will be cleaned up by put_reloc_control(). - */ -+ return ret; - } - - static void free_block_list(struct rb_root *blocks) -@@ -3730,7 +3766,9 @@ static noinline_for_stack int relocate_block_group(struct reloc_control *rc) - */ - err = prepare_to_merge(rc, err); - -- merge_reloc_roots(rc); -+ ret = merge_reloc_roots(rc); -+ if (ret && !err) -+ err = ret; - - rc->merge_reloc_tree = false; - unset_reloc_control(rc); -@@ -4114,10 +4152,10 @@ static int copy_remapped_data(struct btrfs_fs_info *fs_info, u64 old_addr, +@@ -4151,10 +4152,10 @@ static int copy_remapped_data(struct btrfs_fs_info *fs_info, u64 old_addr, u64 new_addr, u64 length) { int ret; @@ -3748,17 +2888,6 @@ diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c pages = kzalloc_objs(struct page *, nr_pages, GFP_NOFS); if (!pages) -@@ -5720,7 +5758,9 @@ int btrfs_recover_relocation(struct btrfs_fs_info *fs_info) - goto out_drop_reloc_refs; - release_recovered_fs_roots(&recovered_roots, false); - -- merge_reloc_roots(rc); -+ ret = merge_reloc_roots(rc); -+ if (ret) -+ goto out_unset; - - unset_reloc_control(rc); - diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c --- a/fs/btrfs/root-tree.c +++ b/fs/btrfs/root-tree.c @@ -4127,7 +3256,7 @@ diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c stripe->logical + (i << fs_info->sectorsize_bits), scrub_repair_read_endio, stripe); -@@ -1245,20 +1227,9 @@ static void scrub_stripe_read_repair_worker(struct work_struct *work) +@@ -1249,20 +1231,9 @@ static void scrub_stripe_read_repair_worker(struct work_struct *work) static void scrub_read_endio(struct btrfs_bio *bbio) { struct scrub_stripe *stripe = bbio->private; @@ -4149,7 +3278,7 @@ diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c if (atomic_dec_and_test(&stripe->pending_io)) { wake_up(&stripe->io_wait); INIT_WORK(&stripe->work, scrub_stripe_read_repair_worker); -@@ -1270,7 +1241,7 @@ static void scrub_write_endio(struct btrfs_bio *bbio) +@@ -1274,7 +1245,7 @@ static void scrub_write_endio(struct btrfs_bio *bbio) { struct scrub_stripe *stripe = bbio->private; struct btrfs_fs_info *fs_info = stripe->bg->fs_info; @@ -4158,7 +3287,7 @@ diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c const u32 bio_size = bio_get_size(&bbio->bio); if (bbio->bio.bi_status) { -@@ -1349,7 +1320,7 @@ static void scrub_write_sectors(struct scrub_ctx *sctx, struct scrub_stripe *str +@@ -1353,7 +1324,7 @@ static void scrub_write_sectors(struct scrub_ctx *sctx, struct scrub_stripe *str bbio = NULL; } if (!bbio) @@ -4167,7 +3296,7 @@ diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c stripe->logical + (sector_nr << fs_info->sectorsize_bits), scrub_write_endio, stripe); scrub_bio_add_sector(bbio, stripe, sector_nr); -@@ -1844,7 +1815,7 @@ static void scrub_submit_extent_sector_read(struct scrub_stripe *stripe) +@@ -1848,7 +1819,7 @@ static void scrub_submit_extent_sector_read(struct scrub_stripe *stripe) continue; } @@ -4176,7 +3305,7 @@ diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c logical, scrub_read_endio, stripe); } -@@ -1869,7 +1840,6 @@ static void scrub_submit_initial_read(struct scrub_ctx *sctx, +@@ -1873,7 +1844,6 @@ static void scrub_submit_initial_read(struct scrub_ctx *sctx, { struct btrfs_fs_info *fs_info = sctx->fs_info; struct btrfs_bio *bbio; @@ -4184,7 +3313,7 @@ diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c unsigned int nr_sectors = stripe_length(stripe) >> fs_info->sectorsize_bits; int mirror = stripe->mirror_num; -@@ -1882,7 +1852,7 @@ static void scrub_submit_initial_read(struct scrub_ctx *sctx, +@@ -1886,7 +1856,7 @@ static void scrub_submit_initial_read(struct scrub_ctx *sctx, return; } @@ -4193,7 +3322,7 @@ diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c stripe->logical, scrub_read_endio, stripe); /* Read the whole range inside the chunk boundary. */ for (unsigned int cur = 0; cur < nr_sectors; cur++) -@@ -2138,7 +2108,7 @@ static int scrub_raid56_cached_parity(struct scrub_ctx *sctx, +@@ -2142,7 +2112,7 @@ static int scrub_raid56_cached_parity(struct scrub_ctx *sctx, for (int i = 0; i < data_stripes; i++) { struct scrub_stripe *stripe = &sctx->raid56_data_stripes[i]; @@ -4202,7 +3331,7 @@ diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c full_stripe_start + (i << BTRFS_STRIPE_LEN_SHIFT)); } raid56_parity_submit_scrub_rbio(rbio); -@@ -3091,14 +3061,6 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start, +@@ -3095,14 +3065,6 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start, /* At mount time we have ensured nodesize is in the range of [4K, 64K]. */ ASSERT(fs_info->nodesize <= BTRFS_STRIPE_LEN); @@ -4436,16 +3565,7 @@ diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c -@@ -698,8 +698,6 @@ start_transaction(struct btrfs_root *root, unsigned int num_items, - goto alloc_fail; - } - -- xa_init(&h->writeback_inhibited_ebs); -- - /* - * If we are JOIN_NOLOCK we're already committing a transaction and - * waiting on this guy, so we don't need to do the sb_start_intwrite -@@ -1519,12 +1517,8 @@ static noinline int commit_fs_roots(struct btrfs_trans_handle *trans) +@@ -1528,12 +1528,8 @@ static noinline int commit_fs_roots(struct btrfs_trans_handle *trans) ASSERT(atomic_read(&root->log_writers) == 0, "atomic_read(&root->log_writers)=%d", atomic_read(&root->log_writers)); @@ -4460,7 +3580,7 @@ diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c radix_tree_tag_clear(&fs_info->fs_roots_radix, (unsigned long)btrfs_root_id(root), -@@ -1642,7 +1636,7 @@ static int qgroup_account_snapshot(struct btrfs_trans_handle *trans, +@@ -1651,7 +1647,7 @@ static int qgroup_account_snapshot(struct btrfs_trans_handle *trans, ret = btrfs_write_and_wait_transaction(trans); if (unlikely(ret)) { btrfs_err(fs_info, @@ -4469,16 +3589,16 @@ diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c return ret; } -@@ -2588,7 +2582,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) +@@ -2597,7 +2593,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) ret = btrfs_write_and_wait_transaction(trans); if (unlikely(ret)) { - btrfs_err(fs_info, "error while writing out transaction: %d", ret); + btrfs_err(fs_info, "error while writing out transaction: %pe", ERR_PTR(ret)); - mutex_unlock(&fs_info->tree_log_mutex); - goto scrub_continue; - } -@@ -2749,8 +2743,8 @@ void __cold __btrfs_abort_transaction(struct btrfs_trans_handle *trans, + /* + * Abort before releasing tree_log_mutex, so a log sync waiting + * on it sees the fs error and skips writing super_for_commit +@@ -2764,8 +2760,8 @@ void __cold __btrfs_abort_transaction(struct btrfs_trans_handle *trans, WRITE_ONCE(trans->transaction->aborted, error); trace_btrfs_transaction_abort(trans); if (first_hit) { @@ -4489,67 +3609,6 @@ diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c if (error == -ENOSPC) btrfs_dump_space_info_for_trans_abort(fs_info); } -diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h ---- a/fs/btrfs/transaction.h -+++ b/fs/btrfs/transaction.h -@@ -7,12 +7,12 @@ - #define BTRFS_TRANSACTION_H - - #include -+#include - #include - #include - #include - #include - #include --#include - #include "btrfs_inode.h" - #include "delayed-ref.h" - -@@ -23,6 +23,7 @@ struct btrfs_fs_info; - struct btrfs_root_item; - struct btrfs_root; - struct btrfs_path; -+struct extent_buffer; - - /* - * Signal that a direct IO write is in progress, to avoid deadlock for sync -@@ -136,6 +137,18 @@ enum { - - #define TRANS_EXTWRITERS (__TRANS_START | __TRANS_ATTACH) - -+/* -+ * Number of extent buffers a transaction handle tracks for writeback -+ * inhibition. The CLOCK reference bits pack into a u32 so this must not exceed -+ * 32, and keeping it a power of two lets the compiler reduce the CLOCK hand -+ * modulo to a mask. -+ */ -+#define BTRFS_INHIBITED_EBS_SLOTS 8 -+ -+static_assert(BTRFS_INHIBITED_EBS_SLOTS <= 32); -+static_assert(BTRFS_INHIBITED_EBS_SLOTS != 0 && -+ (BTRFS_INHIBITED_EBS_SLOTS & (BTRFS_INHIBITED_EBS_SLOTS - 1)) == 0); -+ - struct btrfs_trans_handle { - u64 transid; - u64 bytes_reserved; -@@ -163,8 +176,14 @@ struct btrfs_trans_handle { - struct btrfs_fs_info *fs_info; - struct list_head new_bgs; - struct btrfs_block_rsv delayed_rsv; -- /* Extent buffers with writeback inhibited by this handle. */ -- struct xarray writeback_inhibited_ebs; -+ -+ /* Extent buffers this handle has inhibited writeback on. */ -+ struct extent_buffer *inhibited_ebs[BTRFS_INHIBITED_EBS_SLOTS]; -+ /* CLOCK reference bit per slot. */ -+ u32 inhibited_ebs_referenced; -+ u32 nr_inhibited_ebs; -+ /* CLOCK hand. */ -+ u32 inhibited_ebs_hand; - }; - - /* diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -4811,7 +3870,7 @@ diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c diff --git a/fs/btrfs/verity.c b/fs/btrfs/verity.c --- a/fs/btrfs/verity.c +++ b/fs/btrfs/verity.c -@@ -638,7 +638,7 @@ static int btrfs_end_enable_verity(struct file *filp, const void *desc, +@@ -652,7 +652,7 @@ static int btrfs_end_enable_verity(struct file *filp, const void *desc, rollback_ret = rollback_verity(inode); if (rollback_ret) btrfs_err(inode->root->fs_info, @@ -4820,46 +3879,6 @@ diff --git a/fs/btrfs/verity.c b/fs/btrfs/verity.c return ret; } -@@ -720,14 +720,18 @@ static struct page *btrfs_read_merkle_tree_page(struct inode *inode, - goto out; - - folio_lock(folio); -- /* If it's not uptodate after we have the lock, we got a read error. */ -- if (!folio_test_uptodate(folio)) { -+ /* Folio was truncated from mapping. */ -+ if (!folio->mapping) { - folio_unlock(folio); - folio_put(folio); -- return ERR_PTR(-EIO); -+ goto again; - } -- folio_unlock(folio); -- goto out; -+ /* Another reader may have filled the folio while we waited. */ -+ if (folio_test_uptodate(folio)) { -+ folio_unlock(folio); -+ goto out; -+ } -+ goto read_folio; - } - - folio = filemap_alloc_folio(mapping_gfp_constraint(inode->i_mapping, ~__GFP_FS), -@@ -744,6 +748,7 @@ static struct page *btrfs_read_merkle_tree_page(struct inode *inode, - return ERR_PTR(ret); - } - -+read_folio: - /* - * Merkle item keys are indexed from byte 0 in the merkle tree. - * They have the form: -@@ -753,6 +758,7 @@ static struct page *btrfs_read_merkle_tree_page(struct inode *inode, - ret = read_key_bytes(BTRFS_I(inode), BTRFS_VERITY_MERKLE_ITEM_KEY, off, - folio_address(folio), PAGE_SIZE, folio); - if (ret < 0) { -+ folio_unlock(folio); - folio_put(folio); - return ERR_PTR(ret); - } diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -4914,7 +3933,7 @@ diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c const char *old_name; /* -@@ -4588,7 +4553,7 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_info) +@@ -4592,7 +4557,7 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_info) if (ret == -ENOSPC) { enospc_errors++; } else if (ret == -ETXTBSY) { @@ -4923,7 +3942,7 @@ diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c "skipping relocation of block group %llu due to active swapfile", found_key.offset); ret = 0; -@@ -6049,6 +6014,19 @@ struct btrfs_chunk_map *btrfs_alloc_chunk_map(int num_stripes, gfp_t gfp) +@@ -6053,6 +6018,19 @@ struct btrfs_chunk_map *btrfs_alloc_chunk_map(int num_stripes, gfp_t gfp) return map; } @@ -4943,7 +3962,7 @@ diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c static struct btrfs_block_group *create_chunk(struct btrfs_trans_handle *trans, struct alloc_chunk_ctl *ctl, struct btrfs_device_info *devices_info) -@@ -6067,11 +6045,10 @@ static struct btrfs_block_group *create_chunk(struct btrfs_trans_handle *trans, +@@ -6071,11 +6049,10 @@ static struct btrfs_block_group *create_chunk(struct btrfs_trans_handle *trans, map->start = start; map->chunk_len = ctl->chunk_size; map->stripe_size = ctl->stripe_size; @@ -4957,7 +3976,7 @@ diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c for (int i = 0; i < ctl->ndevs; i++) { for (int j = 0; j < ctl->dev_stripes; j++) { -@@ -6250,7 +6227,7 @@ int btrfs_chunk_alloc_add_chunk_item(struct btrfs_trans_handle *trans, +@@ -6254,7 +6231,7 @@ int btrfs_chunk_alloc_add_chunk_item(struct btrfs_trans_handle *trans, btrfs_set_stack_chunk_length(chunk, bg->length); btrfs_set_stack_chunk_owner(chunk, BTRFS_EXTENT_TREE_OBJECTID); btrfs_set_stack_chunk_stripe_len(chunk, BTRFS_STRIPE_LEN); @@ -4966,7 +3985,7 @@ diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c btrfs_set_stack_chunk_num_stripes(chunk, map->num_stripes); btrfs_set_stack_chunk_io_align(chunk, BTRFS_STRIPE_LEN); btrfs_set_stack_chunk_io_width(chunk, BTRFS_STRIPE_LEN); -@@ -7632,9 +7609,7 @@ static int read_one_chunk(struct btrfs_key *key, struct extent_buffer *leaf, +@@ -7636,9 +7613,7 @@ static int read_one_chunk(struct btrfs_key *key, struct extent_buffer *leaf, map->start = logical; map->chunk_len = length; map->num_stripes = num_stripes; @@ -4977,7 +3996,7 @@ diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c /* * We can't use the sub_stripes value, as for profiles other than * RAID10, they may have 0 as sub_stripes for filesystems created by -@@ -7645,6 +7620,7 @@ static int read_one_chunk(struct btrfs_key *key, struct extent_buffer *leaf, +@@ -7649,6 +7624,7 @@ static int read_one_chunk(struct btrfs_key *key, struct extent_buffer *leaf, */ map->sub_stripes = btrfs_raid_array[index].sub_stripes; map->verified_stripes = 0; diff --git a/pkgbuilds/linux-omarchy/0300-btrfs.patch.sig b/pkgbuilds/linux-omarchy/0300-btrfs.patch.sig index 60dbc9e..9d469a9 100644 Binary files a/pkgbuilds/linux-omarchy/0300-btrfs.patch.sig and b/pkgbuilds/linux-omarchy/0300-btrfs.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0301-btrfs-fixes-1.patch b/pkgbuilds/linux-omarchy/0301-btrfs-fixes-1.patch new file mode 100644 index 0000000..8cc1ec5 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0301-btrfs-fixes-1.patch @@ -0,0 +1,92 @@ +diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c +--- a/fs/btrfs/dev-replace.c ++++ b/fs/btrfs/dev-replace.c +@@ -627,7 +627,7 @@ static int btrfs_dev_replace_start(struct btrfs_fs_info *fs_info, + + ret = mark_block_group_to_copy(fs_info, src_device); + if (ret) +- return ret; ++ goto leave; + + down_write(&dev_replace->rwsem); + dev_replace->replace_task = current; +diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c +--- a/fs/btrfs/ioctl.c ++++ b/fs/btrfs/ioctl.c +@@ -384,6 +384,7 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap, + inode_flags &= ~BTRFS_INODE_COMPRESS; + inode_flags |= BTRFS_INODE_NOCOMPRESS; + } else if (fsflags & FS_COMPR_FL) { ++ enum btrfs_compression_type comp_type; + + if (IS_SWAPFILE(&inode->vfs_inode)) + return -ETXTBSY; +@@ -391,9 +392,23 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap, + inode_flags |= BTRFS_INODE_COMPRESS; + inode_flags &= ~BTRFS_INODE_NOCOMPRESS; + +- comp = btrfs_compress_type2str(fs_info->compress_type); +- if (!comp || comp[0] == 0) +- comp = btrfs_compress_type2str(BTRFS_COMPRESS_ZLIB); ++ /* ++ * Keep the algorithm recorded in the compression property, ++ * otherwise changing an unrelated attribute would reset it to ++ * the mount default, since FS_IOC_SETFLAGS callers write back ++ * the whole flag set they got from FS_IOC_GETFLAGS and that ++ * includes FS_COMPR_FL for any inode carrying the property. ++ * ++ * Inodes with the compress flag set but no property keep using ++ * the mount default, so they behave as before. ++ */ ++ if (inode->prop_compress) ++ comp_type = inode->prop_compress; ++ else if (fs_info->compress_type) ++ comp_type = fs_info->compress_type; ++ else ++ comp_type = BTRFS_COMPRESS_ZLIB; ++ comp = btrfs_compress_type2str(comp_type); + } else { + inode_flags &= ~(BTRFS_INODE_COMPRESS | BTRFS_INODE_NOCOMPRESS); + } +diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c +--- a/fs/btrfs/send.c ++++ b/fs/btrfs/send.c +@@ -6417,6 +6417,13 @@ static int process_extent(struct send_ctx *sctx, + + if (S_ISLNK(sctx->cur_inode_mode)) + return 0; ++ if (unlikely(!S_ISREG(sctx->cur_inode_mode))) { ++ btrfs_crit(sctx->send_root->fs_info, ++ "send: extent for non-regular inode %llu root %llu mode 0%llo", ++ key->objectid, btrfs_root_id(sctx->send_root), ++ sctx->cur_inode_mode & S_IFMT); ++ return -EUCLEAN; ++ } + + if (sctx->parent_root && !sctx->cur_inode_new) { + ret = is_extent_unchanged(sctx, path, key); +diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c +--- a/fs/btrfs/zoned.c ++++ b/fs/btrfs/zoned.c +@@ -2710,6 +2710,7 @@ int btrfs_zone_finish_endio(struct btrfs_fs_info *fs_info, u64 logical, u64 leng + { + struct btrfs_block_group *block_group; + u64 min_alloc_bytes; ++ int ret = 0; + + if (!btrfs_is_zoned(fs_info)) + return 0; +@@ -2729,11 +2730,11 @@ int btrfs_zone_finish_endio(struct btrfs_fs_info *fs_info, u64 logical, u64 leng + block_group->start + block_group->zone_capacity) + goto out; + +- do_zone_finish(block_group, true); ++ ret = do_zone_finish(block_group, true); + + out: + btrfs_put_block_group(block_group); +- return 0; ++ return ret; + } + + static void btrfs_zone_finish_endio_workfn(struct work_struct *work) diff --git a/pkgbuilds/linux-omarchy/0301-btrfs-fixes-1.patch.sig b/pkgbuilds/linux-omarchy/0301-btrfs-fixes-1.patch.sig new file mode 100644 index 0000000..f05ee3b Binary files /dev/null and b/pkgbuilds/linux-omarchy/0301-btrfs-fixes-1.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0301-btrfs-fixes.patch b/pkgbuilds/linux-omarchy/0301-btrfs-fixes.patch deleted file mode 100644 index c5a4318..0000000 --- a/pkgbuilds/linux-omarchy/0301-btrfs-fixes.patch +++ /dev/null @@ -1,386 +0,0 @@ -diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c ---- a/fs/btrfs/dev-replace.c -+++ b/fs/btrfs/dev-replace.c -@@ -626,7 +626,7 @@ static int btrfs_dev_replace_start(struct btrfs_fs_info *fs_info, - - ret = mark_block_group_to_copy(fs_info, src_device); - if (ret) -- return ret; -+ goto leave; - - down_write(&dev_replace->rwsem); - dev_replace->replace_task = current; -diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c ---- a/fs/btrfs/inode.c -+++ b/fs/btrfs/inode.c -@@ -3436,6 +3436,9 @@ int btrfs_finish_one_ordered(struct btrfs_ordered_extent *ordered_extent) - */ - btrfs_remove_ordered_extent(ordered_extent); - -+ /* Cleanup any remaining biocs attached to the OE. */ -+ btrfs_cleanup_ordered_bioc_list(ordered_extent); -+ - /* once for us */ - btrfs_put_ordered_extent(ordered_extent); - /* once for the tree */ -diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c ---- a/fs/btrfs/ioctl.c -+++ b/fs/btrfs/ioctl.c -@@ -384,6 +384,7 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap, - inode_flags &= ~BTRFS_INODE_COMPRESS; - inode_flags |= BTRFS_INODE_NOCOMPRESS; - } else if (fsflags & FS_COMPR_FL) { -+ enum btrfs_compression_type comp_type; - - if (IS_SWAPFILE(&inode->vfs_inode)) - return -ETXTBSY; -@@ -391,9 +392,23 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap, - inode_flags |= BTRFS_INODE_COMPRESS; - inode_flags &= ~BTRFS_INODE_NOCOMPRESS; - -- comp = btrfs_compress_type2str(fs_info->compress_type); -- if (!comp || comp[0] == 0) -- comp = btrfs_compress_type2str(BTRFS_COMPRESS_ZLIB); -+ /* -+ * Keep the algorithm recorded in the compression property, -+ * otherwise changing an unrelated attribute would reset it to -+ * the mount default, since FS_IOC_SETFLAGS callers write back -+ * the whole flag set they got from FS_IOC_GETFLAGS and that -+ * includes FS_COMPR_FL for any inode carrying the property. -+ * -+ * Inodes with the compress flag set but no property keep using -+ * the mount default, so they behave as before. -+ */ -+ if (inode->prop_compress) -+ comp_type = inode->prop_compress; -+ else if (fs_info->compress_type) -+ comp_type = fs_info->compress_type; -+ else -+ comp_type = BTRFS_COMPRESS_ZLIB; -+ comp = btrfs_compress_type2str(comp_type); - } else { - inode_flags &= ~(BTRFS_INODE_COMPRESS | BTRFS_INODE_NOCOMPRESS); - } -diff --git a/fs/btrfs/raid-stripe-tree.c b/fs/btrfs/raid-stripe-tree.c ---- a/fs/btrfs/raid-stripe-tree.c -+++ b/fs/btrfs/raid-stripe-tree.c -@@ -310,8 +310,10 @@ static int update_raid_extent_item(struct btrfs_trans_handle *trans, - - ret = btrfs_search_slot(trans, trans->fs_info->stripe_root, key, path, - 0, 1); -- if (ret) -- return (ret == 1 ? ret : -EINVAL); -+ if (ret > 0) -+ ret = -ENOENT; -+ if (ret < 0) -+ return ret; - - leaf = path->nodes[0]; - slot = path->slots[0]; -@@ -337,7 +339,6 @@ int btrfs_insert_one_raid_extent(struct btrfs_trans_handle *trans, - stripe_extent = kzalloc(item_size, GFP_NOFS); - if (unlikely(!stripe_extent)) { - btrfs_abort_transaction(trans, -ENOMEM); -- btrfs_end_transaction(trans); - return -ENOMEM; - } - -@@ -374,7 +375,7 @@ int btrfs_insert_raid_extent(struct btrfs_trans_handle *trans, - struct btrfs_ordered_extent *ordered_extent) - { - struct btrfs_io_context *bioc; -- int ret; -+ int ret = 0; - - if (!btrfs_fs_incompat(trans->fs_info, RAID_STRIPE_TREE)) - return 0; -@@ -382,17 +383,23 @@ int btrfs_insert_raid_extent(struct btrfs_trans_handle *trans, - list_for_each_entry(bioc, &ordered_extent->bioc_list, rst_ordered_entry) { - ret = btrfs_insert_one_raid_extent(trans, bioc); - if (ret) -- return ret; -+ break; - } - -- while (!list_empty(&ordered_extent->bioc_list)) { -- bioc = list_first_entry(&ordered_extent->bioc_list, -+ btrfs_cleanup_ordered_bioc_list(ordered_extent); -+ return ret; -+} -+ -+void btrfs_cleanup_ordered_bioc_list(struct btrfs_ordered_extent *ordered) -+{ -+ while (!list_empty(&ordered->bioc_list)) { -+ struct btrfs_io_context *bioc; -+ -+ bioc = list_first_entry(&ordered->bioc_list, - typeof(*bioc), rst_ordered_entry); - list_del(&bioc->rst_ordered_entry); - btrfs_put_bioc(bioc); - } -- -- return 0; - } - - int btrfs_get_raid_extent_offset(struct btrfs_fs_info *fs_info, -diff --git a/fs/btrfs/raid-stripe-tree.h b/fs/btrfs/raid-stripe-tree.h ---- a/fs/btrfs/raid-stripe-tree.h -+++ b/fs/btrfs/raid-stripe-tree.h -@@ -28,6 +28,7 @@ int btrfs_get_raid_extent_offset(struct btrfs_fs_info *fs_info, - u32 stripe_index, struct btrfs_io_stripe *stripe); - int btrfs_insert_raid_extent(struct btrfs_trans_handle *trans, - struct btrfs_ordered_extent *ordered_extent); -+void btrfs_cleanup_ordered_bioc_list(struct btrfs_ordered_extent *ordered); - - #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS - int btrfs_insert_one_raid_extent(struct btrfs_trans_handle *trans, -diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c ---- a/fs/btrfs/scrub.c -+++ b/fs/btrfs/scrub.c -@@ -1023,6 +1023,10 @@ static void scrub_stripe_report_errors(struct scrub_ctx *sctx, - - skip: - for_each_set_bit(sector_nr, &extent_bitmap, stripe->nr_sectors) { -+ const u64 sector_logical = stripe->logical + -+ ((u64)sector_nr << fs_info->sectorsize_bits); -+ const u64 sector_physical = physical + -+ ((u64)sector_nr << fs_info->sectorsize_bits); - bool repaired = false; - - if (scrub_bitmap_test_bit_is_metadata(stripe, sector_nr)) { -@@ -1051,12 +1055,12 @@ static void scrub_stripe_report_errors(struct scrub_ctx *sctx, - if (dev) { - btrfs_err_rl(fs_info, - "scrub: fixed up error at logical %llu on dev %s physical %llu", -- stripe->logical, btrfs_dev_name(dev), -- physical); -+ sector_logical, btrfs_dev_name(dev), -+ sector_physical); - } else { - btrfs_err_rl(fs_info, - "scrub: fixed up error at logical %llu on mirror %u", -- stripe->logical, stripe->mirror_num); -+ sector_logical, stripe->mirror_num); - } - continue; - } -@@ -1065,30 +1069,30 @@ static void scrub_stripe_report_errors(struct scrub_ctx *sctx, - if (dev) { - btrfs_err_rl(fs_info, - "scrub: unable to fixup (regular) error at logical %llu on dev %s physical %llu", -- stripe->logical, btrfs_dev_name(dev), -- physical); -+ sector_logical, btrfs_dev_name(dev), -+ sector_physical); - } else { - btrfs_err_rl(fs_info, - "scrub: unable to fixup (regular) error at logical %llu on mirror %u", -- stripe->logical, stripe->mirror_num); -+ sector_logical, stripe->mirror_num); - } - - if (scrub_bitmap_test_bit_io_error(stripe, sector_nr)) - if (__ratelimit(&rs) && dev) - scrub_print_common_warning("i/o error", dev, false, -- stripe->logical, physical); -+ sector_logical, sector_physical); - if (scrub_bitmap_test_bit_csum_error(stripe, sector_nr)) - if (__ratelimit(&rs) && dev) - scrub_print_common_warning("checksum error", dev, false, -- stripe->logical, physical); -+ sector_logical, sector_physical); - if (scrub_bitmap_test_bit_meta_error(stripe, sector_nr)) - if (__ratelimit(&rs) && dev) - scrub_print_common_warning("header error", dev, false, -- stripe->logical, physical); -+ sector_logical, sector_physical); - if (scrub_bitmap_test_bit_meta_gen_error(stripe, sector_nr)) - if (__ratelimit(&rs) && dev) - scrub_print_common_warning("generation error", dev, false, -- stripe->logical, physical); -+ sector_logical, sector_physical); - } - - /* Update the device stats. */ -diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c ---- a/fs/btrfs/send.c -+++ b/fs/btrfs/send.c -@@ -2065,7 +2065,7 @@ static int will_overwrite_ref(struct send_ctx *sctx, u64 dir, u64 dir_gen, - - ret = is_inode_existent(sctx, dir, dir_gen, NULL, &parent_root_dir_gen); - if (ret <= 0) -- return 0; -+ return ret; - - /* - * If we have a parent root we need to verify that the parent dir was -@@ -6417,6 +6417,13 @@ static int process_extent(struct send_ctx *sctx, - - if (S_ISLNK(sctx->cur_inode_mode)) - return 0; -+ if (unlikely(!S_ISREG(sctx->cur_inode_mode))) { -+ btrfs_crit(sctx->send_root->fs_info, -+ "send: extent for non-regular inode %llu root %llu mode 0%llo", -+ key->objectid, btrfs_root_id(sctx->send_root), -+ sctx->cur_inode_mode & S_IFMT); -+ return -EUCLEAN; -+ } - - if (sctx->parent_root && !sctx->cur_inode_new) { - ret = is_extent_unchanged(sctx, path, key); -diff --git a/fs/btrfs/tests/extent-io-tests.c b/fs/btrfs/tests/extent-io-tests.c ---- a/fs/btrfs/tests/extent-io-tests.c -+++ b/fs/btrfs/tests/extent-io-tests.c -@@ -133,14 +133,14 @@ static int test_find_delalloc(u32 sectorsize, u32 nodesize) - if (IS_ERR(root)) { - test_std_err(TEST_ALLOC_ROOT); - ret = PTR_ERR(root); -- goto out; -+ goto out_root_info; - } - - inode = btrfs_new_test_inode(); - if (!inode) { - test_std_err(TEST_ALLOC_INODE); - ret = -ENOMEM; -- goto out; -+ goto out_root_info; - } - tmp = &BTRFS_I(inode)->io_tree; - BTRFS_I(inode)->root = root; -@@ -333,6 +333,7 @@ static int test_find_delalloc(u32 sectorsize, u32 nodesize) - process_page_range(inode, 0, total_dirty - 1, - PROCESS_UNLOCK | PROCESS_RELEASE); - iput(inode); -+out_root_info: - btrfs_free_dummy_root(root); - btrfs_free_dummy_fs_info(fs_info); - return ret; -diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c ---- a/fs/btrfs/transaction.c -+++ b/fs/btrfs/transaction.c -@@ -458,8 +458,19 @@ static int record_root_in_trans(struct btrfs_trans_handle *trans, - * through btrfs_record_root_in_trans without having to take the - * lock. smp_wmb() makes sure that all the writes above are - * done before we pop in the zero below -+ * -+ * If @force is true, it means the call is from -+ * qgroup_account_snapshot(), which only requires radix tree -+ * tracking. -+ * We should not force reloc root creation here, as the root -+ * may have already been modified, and in that case -+ * root->commit_root has already been dropped. -+ * -+ * Using that commit root will cause the reloc root to refer -+ * to a deleted extent, causing extent tree corruption. - */ -- ret = btrfs_init_reloc_root(trans, root); -+ if (!force) -+ ret = btrfs_init_reloc_root(trans, root); - smp_mb__before_atomic(); - clear_bit(BTRFS_ROOT_IN_TRANS_SETUP, &root->state); - } -@@ -2583,6 +2594,12 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) - ret = btrfs_write_and_wait_transaction(trans); - if (unlikely(ret)) { - btrfs_err(fs_info, "error while writing out transaction: %pe", ERR_PTR(ret)); -+ /* -+ * Abort before releasing tree_log_mutex, so a log sync waiting -+ * on it sees the fs error and skips writing super_for_commit -+ * for this failed transaction. See btrfs_sync_log(). -+ */ -+ btrfs_abort_transaction(trans, ret); - mutex_unlock(&fs_info->tree_log_mutex); - goto scrub_continue; - } -diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c ---- a/fs/btrfs/volumes.c -+++ b/fs/btrfs/volumes.c -@@ -3035,7 +3035,11 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path - error_sysfs: - btrfs_sysfs_remove_device(device); - mutex_lock(&fs_info->fs_devices->device_list_mutex); -+ if (seeding_dev) -+ btrfs_assign_next_active_device(device, seed_devices->latest_dev); - mutex_lock(&fs_info->chunk_mutex); -+ if (!list_empty(&device->post_commit_list)) -+ list_del_init(&device->post_commit_list); - list_del_rcu(&device->dev_list); - list_del(&device->dev_alloc_list); - fs_info->fs_devices->num_devices--; -diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c ---- a/fs/btrfs/zoned.c -+++ b/fs/btrfs/zoned.c -@@ -2626,16 +2626,13 @@ static int do_zone_finish(struct btrfs_block_group *block_group, bool fully_writ - down_read(&dev_replace->rwsem); - map = block_group->physical_map; - for (i = 0; i < map->num_stripes; i++) { -- - ret = call_zone_finish(block_group, &map->stripes[i]); -- if (ret) { -- up_read(&dev_replace->rwsem); -- return ret; -- } -+ if (ret) -+ break; - } - up_read(&dev_replace->rwsem); - -- if (!fully_written) -+ if (!ret && !fully_written) - btrfs_dec_block_group_ro(block_group); - - spin_lock(&fs_info->zone_active_bgs_lock); -@@ -2648,7 +2645,7 @@ static int do_zone_finish(struct btrfs_block_group *block_group, bool fully_writ - - clear_and_wake_up_bit(BTRFS_FS_NEED_ZONE_FINISH, &fs_info->flags); - -- return 0; -+ return ret; - } - - int btrfs_zone_finish(struct btrfs_block_group *block_group) -@@ -2713,6 +2710,7 @@ int btrfs_zone_finish_endio(struct btrfs_fs_info *fs_info, u64 logical, u64 leng - { - struct btrfs_block_group *block_group; - u64 min_alloc_bytes; -+ int ret = 0; - - if (!btrfs_is_zoned(fs_info)) - return 0; -@@ -2732,11 +2730,11 @@ int btrfs_zone_finish_endio(struct btrfs_fs_info *fs_info, u64 logical, u64 leng - block_group->start + block_group->zone_capacity) - goto out; - -- do_zone_finish(block_group, true); -+ ret = do_zone_finish(block_group, true); - - out: - btrfs_put_block_group(block_group); -- return 0; -+ return ret; - } - - static void btrfs_zone_finish_endio_workfn(struct work_struct *work) -diff --git a/fs/btrfs/zstd.c b/fs/btrfs/zstd.c ---- a/fs/btrfs/zstd.c -+++ b/fs/btrfs/zstd.c -@@ -307,8 +307,17 @@ struct list_head *zstd_get_workspace(struct btrfs_fs_info *fs_info, int level) - DEFINE_WAIT(wait); - - prepare_to_wait(&zwsm->wait, &wait, TASK_UNINTERRUPTIBLE); -- schedule(); -+ /* -+ * Re-check after being queued: zstd_put_workspace() only wakes -+ * a queue that already has a sleeper, so a workspace returned -+ * since the failed allocation woke nobody. -+ */ -+ ws = zstd_find_workspace(fs_info, level); -+ if (!ws) -+ schedule(); - finish_wait(&zwsm->wait, &wait); -+ if (ws) -+ return ws; - - goto again; - } diff --git a/pkgbuilds/linux-omarchy/0301-btrfs-fixes.patch.sig b/pkgbuilds/linux-omarchy/0301-btrfs-fixes.patch.sig deleted file mode 100644 index 53ac4a1..0000000 Binary files a/pkgbuilds/linux-omarchy/0301-btrfs-fixes.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/0303-btrfs-fixes-2.patch b/pkgbuilds/linux-omarchy/0303-btrfs-fixes-2.patch new file mode 100644 index 0000000..42654e7 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0303-btrfs-fixes-2.patch @@ -0,0 +1,97 @@ +diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c +--- a/fs/btrfs/block-group.c ++++ b/fs/btrfs/block-group.c +@@ -3074,6 +3074,18 @@ struct btrfs_block_group *btrfs_make_block_group(struct btrfs_trans_handle *tran + return ERR_PTR(ret); + } + ++ /* ++ * Ensure the corresponding space_info object is created and ++ * assigned to our block group. We want our bg to be added to the rbtree ++ * with its ->space_info set. ++ * ++ * On a zoned filesystem btrfs_add_new_free_space() ends up in ++ * __btrfs_add_free_space_zoned(), which dereferences ++ * block_group->space_info, so it has to be set beforehand. ++ */ ++ cache->space_info = space_info; ++ ASSERT(cache->space_info); ++ + ret = btrfs_add_new_free_space(cache, chunk_offset, chunk_offset + size, NULL); + btrfs_free_excluded_extents(cache); + if (ret) { +@@ -3081,14 +3093,6 @@ struct btrfs_block_group *btrfs_make_block_group(struct btrfs_trans_handle *tran + return ERR_PTR(ret); + } + +- /* +- * Ensure the corresponding space_info object is created and +- * assigned to our block group. We want our bg to be added to the rbtree +- * with its ->space_info set. +- */ +- cache->space_info = space_info; +- ASSERT(cache->space_info); +- + ret = btrfs_add_block_group_cache(cache); + if (ret) { + btrfs_remove_free_space_cache(cache); +diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c +--- a/fs/btrfs/tree-checker.c ++++ b/fs/btrfs/tree-checker.c +@@ -1909,6 +1909,16 @@ static int check_inode_ref(struct extent_buffer *leaf, + return -EUCLEAN; + } + ++ if (unlikely(btrfs_is_fstree(btrfs_header_owner(leaf)) && ++ (key->offset < BTRFS_FIRST_FREE_OBJECTID || ++ key->offset > BTRFS_LAST_FREE_OBJECTID))) { ++ inode_ref_err(leaf, slot, ++ "invalid offset for ref key, have %llu expect [%llu, %lld]", ++ key->offset, BTRFS_FIRST_FREE_OBJECTID, ++ BTRFS_LAST_FREE_OBJECTID); ++ return -EUCLEAN; ++ } ++ + ptr = btrfs_item_ptr_offset(leaf, slot); + end = ptr + btrfs_item_size(leaf, slot); + while (ptr < end) { +@@ -1952,12 +1962,14 @@ static int check_inode_extref(struct extent_buffer *leaf, + { + unsigned long ptr = btrfs_item_ptr_offset(leaf, slot); + unsigned long end = ptr + btrfs_item_size(leaf, slot); ++ const bool is_fstree = btrfs_is_fstree(btrfs_header_owner(leaf)); + + if (unlikely(!check_prev_ino(leaf, key, slot, prev_key))) + return -EUCLEAN; + + while (ptr < end) { + struct btrfs_inode_extref *extref = (struct btrfs_inode_extref *)ptr; ++ u64 parent; + u16 namelen; + + if (unlikely(ptr + sizeof(*extref) > end)) { +@@ -1967,7 +1979,24 @@ static int check_inode_extref(struct extent_buffer *leaf, + return -EUCLEAN; + } + ++ parent = btrfs_inode_extref_parent(leaf, extref); ++ if (unlikely(is_fstree && (parent < BTRFS_FIRST_FREE_OBJECTID || ++ parent > BTRFS_LAST_FREE_OBJECTID))) { ++ inode_ref_err(leaf, slot, ++ "invalid parent for extref key, have %llu expect [%llu, %lld]", ++ parent, BTRFS_FIRST_FREE_OBJECTID, ++ BTRFS_LAST_FREE_OBJECTID); ++ return -EUCLEAN; ++ } ++ + namelen = btrfs_inode_extref_name_len(leaf, extref); ++ if (unlikely(namelen == 0 || namelen > BTRFS_NAME_LEN)) { ++ inode_ref_err(leaf, slot, ++ "invalid inode extref name length, has %u expect [1, %u]", ++ namelen, BTRFS_NAME_LEN); ++ return -EUCLEAN; ++ } ++ + if (unlikely(ptr + sizeof(*extref) + namelen > end)) { + inode_ref_err(leaf, slot, + "inode extref overflow, ptr %lu end %lu namelen %u", diff --git a/pkgbuilds/linux-omarchy/0303-btrfs-fixes-2.patch.sig b/pkgbuilds/linux-omarchy/0303-btrfs-fixes-2.patch.sig new file mode 100644 index 0000000..c75c8f3 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0303-btrfs-fixes-2.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0304-btrfs-fixes-3.patch b/pkgbuilds/linux-omarchy/0304-btrfs-fixes-3.patch new file mode 100644 index 0000000..7d1ab48 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0304-btrfs-fixes-3.patch @@ -0,0 +1,77 @@ +diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c +--- a/fs/btrfs/tree-checker.c ++++ b/fs/btrfs/tree-checker.c +@@ -2306,7 +2306,7 @@ static int check_free_space_extent(struct extent_buffer *leaf, struct btrfs_key + + if (unlikely(btrfs_item_size(leaf, slot) != 0)) { + generic_err(leaf, slot, +- "invalid item size for free space info, has %u expect 0", ++ "invalid item size for free space extent, has %u expect 0", + btrfs_item_size(leaf, slot)); + return -EUCLEAN; + } +diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c +--- a/fs/btrfs/volumes.c ++++ b/fs/btrfs/volumes.c +@@ -740,6 +740,36 @@ const u8 *btrfs_sb_fsid_ptr(const struct btrfs_super_block *sb) + return has_metadata_uuid ? sb->metadata_uuid : sb->fsid; + } + ++static bool should_rename_device(const struct btrfs_device *dev) ++{ ++ bool ret; ++ const char *old_name; ++ ++ rcu_read_lock(); ++ old_name = rcu_dereference(dev->name); ++ /* ++ * For systems booted without an initramfs, the rootfs has the device ++ * name "/dev/root". ++ * ++ * Although using btrfs without an initramfs is not recommended (if a ++ * new device is added to the rootfs, the system can no longer boot, as ++ * there is no way to register all devices), there is still a minority ++ * of users doing this. ++ * ++ * And after the system is up, a later device scan on the real block ++ * device file will never get this device's name updated, as the ++ * device->devt is still the same. ++ * ++ * Here we add one and only one exception for "/dev/root", to allow the ++ * device name to be updated even if the new path points to the same ++ * block device. ++ */ ++ ret = (strcmp(old_name, "/dev/root") == 0); ++ rcu_read_unlock(); ++ ++ return ret; ++} ++ + /* + * Add new device to list of registered devices + * +@@ -860,7 +890,8 @@ static noinline struct btrfs_device *device_list_add(const char *path, + MAJOR(path_devt), MINOR(path_devt), + current->comm, task_pid_nr(current)); + +- } else if (!device->name || device->devt != path_devt) { ++ } else if (!device->name || device->devt != path_devt || ++ should_rename_device(device)) { + const char *old_name; + + /* +diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c +--- a/fs/btrfs/zoned.c ++++ b/fs/btrfs/zoned.c +@@ -2688,6 +2688,11 @@ bool btrfs_can_activate_zone(struct btrfs_fs_devices *fs_devices, u64 flags) + + switch (flags & BTRFS_BLOCK_GROUP_PROFILE_MASK) { + case 0: /* single */ ++ case BTRFS_BLOCK_GROUP_RAID0: ++ case BTRFS_BLOCK_GROUP_RAID1: ++ case BTRFS_BLOCK_GROUP_RAID1C3: ++ case BTRFS_BLOCK_GROUP_RAID1C4: ++ case BTRFS_BLOCK_GROUP_RAID10: + ret = (atomic_read(&zinfo->active_zones_left) >= (1 + reserved)); + break; + case BTRFS_BLOCK_GROUP_DUP: diff --git a/pkgbuilds/linux-omarchy/0304-btrfs-fixes-3.patch.sig b/pkgbuilds/linux-omarchy/0304-btrfs-fixes-3.patch.sig new file mode 100644 index 0000000..d471f2e Binary files /dev/null and b/pkgbuilds/linux-omarchy/0304-btrfs-fixes-3.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0310-fuse-eof-zeroing.patch b/pkgbuilds/linux-omarchy/0310-fuse-eof-zeroing.patch index b34a909..38a6427 100644 --- a/pkgbuilds/linux-omarchy/0310-fuse-eof-zeroing.patch +++ b/pkgbuilds/linux-omarchy/0310-fuse-eof-zeroing.patch @@ -1,7 +1,7 @@ diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c -@@ -2280,6 +2280,9 @@ int fuse_do_setattr(struct mnt_idmap *idmap, struct dentry *dentry, +@@ -2289,6 +2289,9 @@ int fuse_do_setattr(struct mnt_idmap *idmap, struct dentry *dentry, */ if ((is_truncate || !is_wb) && S_ISREG(inode->i_mode) && oldsize != outarg.attr.size) { @@ -28,7 +28,7 @@ diff --git a/fs/fuse/file.c b/fs/fuse/file.c if (inode->i_size < pos + iov_iter_count(ii)) set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); -@@ -2908,6 +2912,11 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, +@@ -2909,6 +2913,11 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, /* we could have extended the file */ if (!(mode & FALLOC_FL_KEEP_SIZE)) { diff --git a/pkgbuilds/linux-omarchy/0310-fuse-eof-zeroing.patch.sig b/pkgbuilds/linux-omarchy/0310-fuse-eof-zeroing.patch.sig index c65855e..e4796a2 100644 Binary files a/pkgbuilds/linux-omarchy/0310-fuse-eof-zeroing.patch.sig and b/pkgbuilds/linux-omarchy/0310-fuse-eof-zeroing.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0311-fuse-perf.patch b/pkgbuilds/linux-omarchy/0311-fuse-perf.patch index 5162077..ca3c02a 100644 --- a/pkgbuilds/linux-omarchy/0311-fuse-perf.patch +++ b/pkgbuilds/linux-omarchy/0311-fuse-perf.patch @@ -1,7 +1,7 @@ diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c -@@ -216,10 +216,13 @@ EXPORT_SYMBOL_GPL(fuse_req_hash); +@@ -213,10 +213,13 @@ EXPORT_SYMBOL_GPL(fuse_req_hash); /* * A new request is available, wake fiq->waitq */ @@ -17,7 +17,7 @@ diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c kill_fasync(&fiq->fasync, SIGIO, POLL_IN); spin_unlock(&fiq->lock); } -@@ -236,7 +239,7 @@ void fuse_dev_queue_forget(struct fuse_iqueue *fiq, +@@ -233,7 +236,7 @@ void fuse_dev_queue_forget(struct fuse_iqueue *fiq, if (fiq->connected) { fiq->forget_list_tail->next = forget; fiq->forget_list_tail = forget; @@ -26,7 +26,7 @@ diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c } else { kfree(forget); spin_unlock(&fiq->lock); -@@ -258,7 +261,7 @@ void fuse_dev_queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req) +@@ -255,7 +258,7 @@ void fuse_dev_queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req) list_del_init(&req->intr_entry); spin_unlock(&fiq->lock); } else { @@ -35,7 +35,7 @@ diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c } } else { spin_unlock(&fiq->lock); -@@ -288,11 +291,13 @@ EXPORT_SYMBOL_GPL(fuse_request_assign_unique); +@@ -285,11 +288,13 @@ EXPORT_SYMBOL_GPL(fuse_request_assign_unique); static void fuse_dev_queue_req(struct fuse_iqueue *fiq, struct fuse_req *req) { @@ -50,7 +50,7 @@ diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c } else { spin_unlock(&fiq->lock); req->out.h.error = -ENOTCONN; -@@ -755,6 +760,11 @@ static void __fuse_request_send(struct fuse_req *req) +@@ -752,6 +757,11 @@ static void __fuse_request_send(struct fuse_req *req) /* acquire extra reference, since request is still needed after fuse_request_end() */ __fuse_get_request(req); @@ -62,7 +62,7 @@ diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c fuse_send_one(fiq, req); request_wait_answer(req); -@@ -1809,7 +1819,7 @@ void fuse_chan_resend(struct fuse_chan *fch) +@@ -1806,7 +1816,7 @@ void fuse_chan_resend(struct fuse_chan *fch) } /* iq and pq requests are both oldest to newest */ list_splice(&to_queue, &fiq->pending); @@ -94,7 +94,7 @@ diff --git a/fs/fuse/fuse_dev_i.h b/fs/fuse/fuse_dev_i.h diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c -@@ -1417,6 +1417,7 @@ static void process_init_reply(struct fuse_args *args, int error) +@@ -1420,6 +1420,7 @@ static void process_init_reply(struct fuse_args *args, int error) fm->sb->s_bdi->ra_pages = min(fm->sb->s_bdi->ra_pages, ra_pages); diff --git a/pkgbuilds/linux-omarchy/0311-fuse-perf.patch.sig b/pkgbuilds/linux-omarchy/0311-fuse-perf.patch.sig index ce13d31..9bbc4c6 100644 Binary files a/pkgbuilds/linux-omarchy/0311-fuse-perf.patch.sig and b/pkgbuilds/linux-omarchy/0311-fuse-perf.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0313-fuse-background-wakeup.patch b/pkgbuilds/linux-omarchy/0313-fuse-background-wakeup.patch index 77e750a..27fbcdc 100644 --- a/pkgbuilds/linux-omarchy/0313-fuse-background-wakeup.patch +++ b/pkgbuilds/linux-omarchy/0313-fuse-background-wakeup.patch @@ -1,7 +1,7 @@ diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c -@@ -409,7 +409,8 @@ void fuse_chan_max_background_set(struct fuse_chan *fch, unsigned int val) +@@ -406,7 +406,8 @@ void fuse_chan_max_background_set(struct fuse_chan *fch, unsigned int val) fch->max_background = val; fch->blocked = fch->num_background >= fch->max_background; if (!fch->blocked) diff --git a/pkgbuilds/linux-omarchy/0313-fuse-background-wakeup.patch.sig b/pkgbuilds/linux-omarchy/0313-fuse-background-wakeup.patch.sig index e161277..cfd1cd7 100644 Binary files a/pkgbuilds/linux-omarchy/0313-fuse-background-wakeup.patch.sig and b/pkgbuilds/linux-omarchy/0313-fuse-background-wakeup.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0320-vfs-open-consume-path-reference.patch b/pkgbuilds/linux-omarchy/0320-vfs-open-consume-path-reference.patch new file mode 100644 index 0000000..eba1599 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0320-vfs-open-consume-path-reference.patch @@ -0,0 +1,119 @@ +diff --git a/fs/internal.h b/fs/internal.h +--- a/fs/internal.h ++++ b/fs/internal.h +@@ -204,6 +204,7 @@ int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, + int flag); + int chown_common(const struct path *path, uid_t user, gid_t group); + extern int vfs_open(const struct path *, struct file *); ++int vfs_open_consume(struct path *, struct file *); + + /* + * inode.c +diff --git a/fs/namei.c b/fs/namei.c +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -4652,6 +4652,7 @@ static const char *open_last_lookups(struct nameidata *nd, + static int do_open(struct nameidata *nd, + struct file *file, const struct open_flags *op) + { ++ struct vfsmount *mnt; + struct mnt_idmap *idmap; + int open_flag = op->open_flag; + bool do_truncate; +@@ -4693,11 +4694,17 @@ static int do_open(struct nameidata *nd, + error = mnt_want_write(nd->path.mnt); + if (error) + return error; ++ /* ++ * A dedicated reference is needed because after the call to ++ * vfs_open_consume() we no longer own the reference in nd->path.mnt ++ * while we need to undo write acess below. ++ */ ++ mnt = mntget(nd->path.mnt); + do_truncate = true; + } + error = may_open(idmap, &nd->path, acc_mode, open_flag); + if (!error && !(file->f_mode & FMODE_OPENED)) +- error = vfs_open(&nd->path, file); ++ error = vfs_open_consume(&nd->path, file); + if (!error) + error = security_file_post_open(file, op->acc_mode); + if (!error && do_truncate) +@@ -4706,8 +4713,10 @@ static int do_open(struct nameidata *nd, + WARN_ON(1); + error = -EINVAL; + } +- if (do_truncate) +- mnt_drop_write(nd->path.mnt); ++ if (do_truncate) { ++ mnt_drop_write(mnt); ++ mntput(mnt); ++ } + return error; + } + +diff --git a/fs/open.c b/fs/open.c +--- a/fs/open.c ++++ b/fs/open.c +@@ -882,6 +882,11 @@ static inline int file_get_write_access(struct file *f) + return error; + } + ++/* ++ * Populate struct file ++ * ++ * NOTE: it assumes f_path is populated and consumes the caller's reference. ++ */ + static int do_dentry_open(struct file *f, + int (*open)(struct inode *, struct file *)) + { +@@ -889,7 +894,6 @@ static int do_dentry_open(struct file *f, + struct inode *inode = f->f_path.dentry->d_inode; + int error; + +- path_get(&f->f_path); + f->f_inode = inode; + f->f_mapping = inode->i_mapping; + f->f_wb_err = filemap_sample_wb_err(f->f_mapping); +@@ -1006,6 +1010,7 @@ int finish_open(struct file *file, struct dentry *dentry, + BUG_ON(file->f_mode & FMODE_OPENED); /* once it's opened, it's opened */ + + file->__f_path.dentry = dentry; ++ path_get(&file->f_path); + return do_dentry_open(file, open); + } + EXPORT_SYMBOL(finish_open); +@@ -1049,6 +1054,7 @@ int vfs_open(const struct path *path, struct file *file) + int ret; + + file->__f_path = *path; ++ path_get(&file->f_path); + ret = do_dentry_open(file, NULL); + if (!ret) { + /* +@@ -1061,6 +1067,25 @@ int vfs_open(const struct path *path, struct file *file) + return ret; + } + ++/** ++ * vfs_open_consume - open the file at the given path and consume the reference ++ * @path: path to open ++ * @file: newly allocated file with f_flag initialized ++ */ ++int vfs_open_consume(struct path *path, struct file *file) ++{ ++ int ret; ++ ++ file->__f_path = *path; ++ path->mnt = NULL; ++ path->dentry = NULL; ++ ret = do_dentry_open(file, NULL); ++ if (!ret) { ++ fsnotify_open(file); ++ } ++ return ret; ++} ++ + struct file *dentry_open(const struct path *path, int flags, + const struct cred *cred) + { diff --git a/pkgbuilds/linux-omarchy/0320-vfs-open-consume-path-reference.patch.sig b/pkgbuilds/linux-omarchy/0320-vfs-open-consume-path-reference.patch.sig new file mode 100644 index 0000000..c0764ef Binary files /dev/null and b/pkgbuilds/linux-omarchy/0320-vfs-open-consume-path-reference.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch b/pkgbuilds/linux-omarchy/0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch deleted file mode 100644 index 6977a84..0000000 --- a/pkgbuilds/linux-omarchy/0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c ---- a/drivers/gpu/drm/ttm/ttm_bo.c -+++ b/drivers/gpu/drm/ttm/ttm_bo.c -@@ -1343,7 +1343,7 @@ ttm_bo_swapout_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo) - - if (ttm_tt_is_populated(tt)) { - ret = ttm_tt_swapout(bdev, tt, swapout_walk->gfp_flags); -- if (!ret) { -+ if (ret > 0) { - spin_lock(&bdev->lru_lock); - ttm_resource_del_bulk_move_unevictable(bo->resource, bo); - ttm_resource_move_to_lru_tail(bo->resource); diff --git a/pkgbuilds/linux-omarchy/0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch.sig b/pkgbuilds/linux-omarchy/0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch.sig deleted file mode 100644 index 8ca1431..0000000 Binary files a/pkgbuilds/linux-omarchy/0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/0401-drm-i915-psr-exit-panel-replay-for-alpm-lag.patch.sig b/pkgbuilds/linux-omarchy/0401-drm-i915-psr-exit-panel-replay-for-alpm-lag.patch.sig deleted file mode 100644 index 4c3c1a0..0000000 Binary files a/pkgbuilds/linux-omarchy/0401-drm-i915-psr-exit-panel-replay-for-alpm-lag.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/0402-psr2-early-transport-panels.patch b/pkgbuilds/linux-omarchy/0401-psr2-early-transport-panels.patch similarity index 100% rename from pkgbuilds/linux-omarchy/0402-psr2-early-transport-panels.patch rename to pkgbuilds/linux-omarchy/0401-psr2-early-transport-panels.patch diff --git a/pkgbuilds/linux-omarchy/0402-psr2-early-transport-panels.patch.sig b/pkgbuilds/linux-omarchy/0401-psr2-early-transport-panels.patch.sig similarity index 100% rename from pkgbuilds/linux-omarchy/0402-psr2-early-transport-panels.patch.sig rename to pkgbuilds/linux-omarchy/0401-psr2-early-transport-panels.patch.sig diff --git a/pkgbuilds/linux-omarchy-bore/0401-drm-i915-psr-exit-panel-replay-for-alpm-lag.patch b/pkgbuilds/linux-omarchy/0402-drm-i915-quirk-panel-replay-exit-on-flush.patch similarity index 64% rename from pkgbuilds/linux-omarchy-bore/0401-drm-i915-psr-exit-panel-replay-for-alpm-lag.patch rename to pkgbuilds/linux-omarchy/0402-drm-i915-quirk-panel-replay-exit-on-flush.patch index 28a6330..27e9b7d 100644 --- a/pkgbuilds/linux-omarchy-bore/0401-drm-i915-psr-exit-panel-replay-for-alpm-lag.patch +++ b/pkgbuilds/linux-omarchy/0402-drm-i915-quirk-panel-replay-exit-on-flush.patch @@ -1,5 +1,4 @@ diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h -index c21e0c0ef0b1..e62788328bfd 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1780,6 +1780,7 @@ struct intel_psr { @@ -11,10 +10,9 @@ index c21e0c0ef0b1..e62788328bfd 100644 u8 io_wake_lines; diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c -index beaa1d62613d..644ab9b1bee9 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c -@@ -2475,6 +2475,7 @@ void intel_psr_disable(struct intel_dp *intel_dp, +@@ -2481,6 +2481,7 @@ void intel_psr_disable(struct intel_dp *intel_dp, mutex_unlock(&intel_dp->psr.lock); cancel_work_sync(&intel_dp->psr.work); cancel_delayed_work_sync(&intel_dp->psr.dc3co_work); @@ -22,7 +20,7 @@ index beaa1d62613d..644ab9b1bee9 100644 } /** -@@ -2506,6 +2507,7 @@ void intel_psr_pause(struct intel_dp *intel_dp) +@@ -2512,6 +2513,7 @@ void intel_psr_pause(struct intel_dp *intel_dp) cancel_work_sync(&psr->work); cancel_delayed_work_sync(&psr->dc3co_work); @@ -30,48 +28,48 @@ index beaa1d62613d..644ab9b1bee9 100644 } /** -@@ -3539,6 +3541,17 @@ static void intel_psr_handle_irq(struct intel_dp *intel_dp) +@@ -3545,6 +3547,17 @@ static void intel_psr_handle_irq(struct intel_dp *intel_dp) drm_dp_dpcd_writeb(&intel_dp->aux, DP_SET_POWER, DP_SET_POWER_D0); } +#define PANEL_REPLAY_REENABLE_DELAY_MS 50 + -+static bool panel_replay_alpm_cursor_lag_workaround_enabled(struct intel_dp *intel_dp) ++static bool panel_replay_exit_on_flush_enabled(struct intel_dp *intel_dp) +{ + return intel_dp_is_edp(intel_dp) && + intel_dp->psr.panel_replay_enabled && + intel_dp->psr.sel_update_enabled && + intel_has_dpcd_quirk(intel_dp, -+ QUIRK_PANEL_REPLAY_ALPM_CURSOR_LAG); ++ QUIRK_PANEL_REPLAY_EXIT_ON_FLUSH); +} + static void intel_psr_work(struct work_struct *work) { struct intel_dp *intel_dp = -@@ -3557,6 +3570,11 @@ static void intel_psr_work(struct work_struct *work) +@@ -3563,6 +3576,11 @@ static void intel_psr_work(struct work_struct *work) if (intel_dp->psr.pause_counter) goto unlock; -+ /* The dedicated delayed work owns re-entry while the workaround is armed. */ -+ if (panel_replay_alpm_cursor_lag_workaround_enabled(intel_dp) && ++ /* The dedicated delayed work owns re-entry while the quirk is armed. */ ++ if (panel_replay_exit_on_flush_enabled(intel_dp) && + delayed_work_pending(&intel_dp->psr.panel_replay_reenable_work)) + goto unlock; + /* * We have to make sure PSR is ready for re-enable * otherwise it keeps disabled until next full enable/disable cycle. -@@ -3566,6 +3584,10 @@ static void intel_psr_work(struct work_struct *work) +@@ -3572,6 +3590,10 @@ static void intel_psr_work(struct work_struct *work) if (!__psr_wait_for_idle_locked(intel_dp)) goto unlock; -+ if (panel_replay_alpm_cursor_lag_workaround_enabled(intel_dp) && ++ if (panel_replay_exit_on_flush_enabled(intel_dp) && + delayed_work_pending(&intel_dp->psr.panel_replay_reenable_work)) + goto unlock; + /* * The delayed work can race with an invalidate hence we need to * recheck. Since psr_flush first clears this and then reschedules we -@@ -3579,6 +3601,33 @@ static void intel_psr_work(struct work_struct *work) +@@ -3585,6 +3607,33 @@ static void intel_psr_work(struct work_struct *work) mutex_unlock(&intel_dp->psr.lock); } @@ -84,7 +82,7 @@ index beaa1d62613d..644ab9b1bee9 100644 + mutex_lock(&intel_dp->psr.lock); + + if (!intel_dp->psr.enabled || -+ !panel_replay_alpm_cursor_lag_workaround_enabled(intel_dp) || ++ !panel_replay_exit_on_flush_enabled(intel_dp) || + intel_dp->psr.pause_counter || + READ_ONCE(intel_dp->psr.irq_aux_error)) + goto unlock; @@ -105,25 +103,25 @@ index beaa1d62613d..644ab9b1bee9 100644 static void intel_psr_configure_full_frame_update(struct intel_dp *intel_dp) { struct intel_display *display = to_intel_display(intel_dp); -@@ -3650,8 +3699,11 @@ void intel_psr_invalidate(struct intel_display *display, +@@ -3656,8 +3705,11 @@ void intel_psr_invalidate(struct intel_display *display, INTEL_FRONTBUFFER_ALL_MASK(intel_dp->psr.pipe); intel_dp->psr.busy_frontbuffer_bits |= pipe_frontbuffer_bits; - if (pipe_frontbuffer_bits) + if (pipe_frontbuffer_bits) { -+ if (panel_replay_alpm_cursor_lag_workaround_enabled(intel_dp)) ++ if (panel_replay_exit_on_flush_enabled(intel_dp)) + cancel_delayed_work(&intel_dp->psr.panel_replay_reenable_work); _psr_invalidate_handle(intel_dp); + } mutex_unlock(&intel_dp->psr.lock); } -@@ -3767,6 +3819,15 @@ void intel_psr_flush(struct intel_display *display, +@@ -3773,6 +3825,15 @@ void intel_psr_flush(struct intel_display *display, if (intel_dp->psr.pause_counter) goto unlock; + if (pipe_frontbuffer_bits && -+ panel_replay_alpm_cursor_lag_workaround_enabled(intel_dp)) { ++ panel_replay_exit_on_flush_enabled(intel_dp)) { + intel_psr_exit(intel_dp); + mod_delayed_work(display->wq.unordered, + &intel_dp->psr.panel_replay_reenable_work, @@ -134,7 +132,7 @@ index beaa1d62613d..644ab9b1bee9 100644 if (origin == ORIGIN_FLIP || (origin == ORIGIN_CURSOR_UPDATE && !intel_dp->psr.psr2_sel_fetch_enabled)) { -@@ -3830,6 +3891,8 @@ void intel_psr_init(struct intel_dp *intel_dp) +@@ -3836,6 +3897,8 @@ void intel_psr_init(struct intel_dp *intel_dp) INIT_WORK(&intel_dp->psr.work, intel_psr_work); INIT_DELAYED_WORK(&intel_dp->psr.dc3co_work, tgl_dc3co_disable_work); @@ -144,53 +142,31 @@ index beaa1d62613d..644ab9b1bee9 100644 } diff --git a/drivers/gpu/drm/i915/display/intel_quirks.c b/drivers/gpu/drm/i915/display/intel_quirks.c -index 33245f44c0d5..dc6576016400 100644 --- a/drivers/gpu/drm/i915/display/intel_quirks.c +++ b/drivers/gpu/drm/i915/display/intel_quirks.c -@@ -86,12 +86,13 @@ static void quirk_edp_limit_rate_hbr2(struct intel_display *display) - drm_info(display->drm, "Applying eDP Limit rate to HBR2 quirk\n"); - } - --static void quirk_disable_edp_panel_replay(struct intel_dp *intel_dp) -+static void quirk_panel_replay_alpm_cursor_lag(struct intel_dp *intel_dp) - { - struct intel_display *display = to_intel_display(intel_dp); - -- intel_set_dpcd_quirk(intel_dp, QUIRK_DISABLE_EDP_PANEL_REPLAY); -- drm_info(display->drm, "Applying disable Panel Replay quirk\n"); -+ intel_set_dpcd_quirk(intel_dp, QUIRK_PANEL_REPLAY_ALPM_CURSOR_LAG); -+ drm_info(display->drm, -+ "Applying Panel Replay ALPM cursor lag workaround\n"); +@@ -94,6 +94,14 @@ static void quirk_disable_edp_panel_replay(struct intel_dp *intel_dp) + drm_info(display->drm, "Applying disable Panel Replay quirk\n"); } ++static void quirk_panel_replay_exit_on_flush(struct intel_dp *intel_dp) ++{ ++ struct intel_display *display = to_intel_display(intel_dp); ++ ++ intel_set_dpcd_quirk(intel_dp, QUIRK_PANEL_REPLAY_EXIT_ON_FLUSH); ++ drm_info(display->drm, "Applying Panel Replay exit on flush quirk\n"); ++} ++ static void quirk_disable_psr2(struct intel_display *display) -@@ -276,7 +277,7 @@ static const struct intel_dpcd_quirk intel_dpcd_quirks[] = { - .subsystem_vendor = 0x1028, - .subsystem_device = 0x0db9, - .sink_oui = SINK_OUI(0x00, 0x22, 0xb9), -- .hook = quirk_disable_edp_panel_replay, -+ .hook = quirk_panel_replay_alpm_cursor_lag, - }, - /* Dell XPS 16 DA16260 */ - { -@@ -284,7 +285,7 @@ static const struct intel_dpcd_quirk intel_dpcd_quirks[] = { - .subsystem_vendor = 0x1028, - .subsystem_device = 0x0dba, - .sink_oui = SINK_OUI(0x00, 0x22, 0xb9), -- .hook = quirk_disable_edp_panel_replay, -+ .hook = quirk_panel_replay_alpm_cursor_lag, - }, - }; - + { + intel_set_quirk(display, QUIRK_DISABLE_PSR2); diff --git a/drivers/gpu/drm/i915/display/intel_quirks.h b/drivers/gpu/drm/i915/display/intel_quirks.h -index 970a4fe52faf..50ef333b87a9 100644 --- a/drivers/gpu/drm/i915/display/intel_quirks.h +++ b/drivers/gpu/drm/i915/display/intel_quirks.h @@ -23,6 +23,7 @@ enum intel_quirk_id { QUIRK_EDP_LIMIT_RATE_HBR2, QUIRK_DISABLE_EDP_PANEL_REPLAY, QUIRK_DISABLE_PSR2, -+ QUIRK_PANEL_REPLAY_ALPM_CURSOR_LAG, ++ QUIRK_PANEL_REPLAY_EXIT_ON_FLUSH, }; void intel_init_quirks(struct intel_display *display); diff --git a/pkgbuilds/linux-omarchy/0402-drm-i915-quirk-panel-replay-exit-on-flush.patch.sig b/pkgbuilds/linux-omarchy/0402-drm-i915-quirk-panel-replay-exit-on-flush.patch.sig new file mode 100644 index 0000000..196c3ee Binary files /dev/null and b/pkgbuilds/linux-omarchy/0402-drm-i915-quirk-panel-replay-exit-on-flush.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0403-drm-i915-quirk-dell-xps14-ptl-panel-replay-exit-on-flush.patch b/pkgbuilds/linux-omarchy/0403-drm-i915-quirk-dell-xps14-ptl-panel-replay-exit-on-flush.patch new file mode 100644 index 0000000..24c203e --- /dev/null +++ b/pkgbuilds/linux-omarchy/0403-drm-i915-quirk-dell-xps14-ptl-panel-replay-exit-on-flush.patch @@ -0,0 +1,12 @@ +diff --git a/drivers/gpu/drm/i915/display/intel_quirks.c b/drivers/gpu/drm/i915/display/intel_quirks.c +--- a/drivers/gpu/drm/i915/display/intel_quirks.c ++++ b/drivers/gpu/drm/i915/display/intel_quirks.c +@@ -284,7 +284,7 @@ static const struct intel_dpcd_quirk intel_dpcd_quirks[] = { + .subsystem_vendor = 0x1028, + .subsystem_device = 0x0db9, + .sink_oui = SINK_OUI(0x00, 0x22, 0xb9), +- .hook = quirk_disable_edp_panel_replay, ++ .hook = quirk_panel_replay_exit_on_flush, + }, + /* Dell XPS 16 DA16260 */ + { diff --git a/pkgbuilds/linux-omarchy/0403-drm-i915-quirk-dell-xps14-ptl-panel-replay-exit-on-flush.patch.sig b/pkgbuilds/linux-omarchy/0403-drm-i915-quirk-dell-xps14-ptl-panel-replay-exit-on-flush.patch.sig new file mode 100644 index 0000000..0393b90 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0403-drm-i915-quirk-dell-xps14-ptl-panel-replay-exit-on-flush.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0404-drm-i915-quirk-dell-xps16-ptl-panel-replay-exit-on-flush.patch b/pkgbuilds/linux-omarchy/0404-drm-i915-quirk-dell-xps16-ptl-panel-replay-exit-on-flush.patch new file mode 100644 index 0000000..8eb9b65 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0404-drm-i915-quirk-dell-xps16-ptl-panel-replay-exit-on-flush.patch @@ -0,0 +1,12 @@ +diff --git a/drivers/gpu/drm/i915/display/intel_quirks.c b/drivers/gpu/drm/i915/display/intel_quirks.c +--- a/drivers/gpu/drm/i915/display/intel_quirks.c ++++ b/drivers/gpu/drm/i915/display/intel_quirks.c +@@ -292,7 +292,7 @@ static const struct intel_dpcd_quirk intel_dpcd_quirks[] = { + .subsystem_vendor = 0x1028, + .subsystem_device = 0x0dba, + .sink_oui = SINK_OUI(0x00, 0x22, 0xb9), +- .hook = quirk_disable_edp_panel_replay, ++ .hook = quirk_panel_replay_exit_on_flush, + }, + }; + diff --git a/pkgbuilds/linux-omarchy/0404-drm-i915-quirk-dell-xps16-ptl-panel-replay-exit-on-flush.patch.sig b/pkgbuilds/linux-omarchy/0404-drm-i915-quirk-dell-xps16-ptl-panel-replay-exit-on-flush.patch.sig new file mode 100644 index 0000000..095745c Binary files /dev/null and b/pkgbuilds/linux-omarchy/0404-drm-i915-quirk-dell-xps16-ptl-panel-replay-exit-on-flush.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0405-drm-i915-quirk-dell-xps13-dx13260-ptl-psr1-only.patch b/pkgbuilds/linux-omarchy/0405-drm-i915-quirk-dell-xps13-dx13260-ptl-psr1-only.patch new file mode 100644 index 0000000..1da7f71 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0405-drm-i915-quirk-dell-xps13-dx13260-ptl-psr1-only.patch @@ -0,0 +1,28 @@ +diff --git a/drivers/gpu/drm/i915/display/intel_quirks.c b/drivers/gpu/drm/i915/display/intel_quirks.c +--- a/drivers/gpu/drm/i915/display/intel_quirks.c ++++ b/drivers/gpu/drm/i915/display/intel_quirks.c +@@ -267,6 +267,9 @@ static struct intel_quirk intel_quirks[] = { + + /* Xiaomi Book Pro 14 2026 */ + { 0xb081, 0x1d72, 0x2424, quirk_disable_psr2 }, ++ ++ /* Dell XPS 13 DX13260 */ ++ { DEVICE_ID_ANY, 0x1028, 0x0e54, quirk_disable_psr2 }, + }; + + static const struct intel_dpcd_quirk intel_dpcd_quirks[] = { +@@ -294,6 +297,14 @@ static const struct intel_dpcd_quirk intel_dpcd_quirks[] = { + .sink_oui = SINK_OUI(0x00, 0x22, 0xb9), + .hook = quirk_panel_replay_exit_on_flush, + }, ++ /* Dell XPS 13 DX13260 */ ++ { ++ .device = DEVICE_ID_ANY, ++ .subsystem_vendor = 0x1028, ++ .subsystem_device = 0x0e54, ++ .sink_oui = SINK_OUI(0x00, 0x22, 0xb9), ++ .hook = quirk_disable_edp_panel_replay, ++ }, + }; + + void intel_init_quirks(struct intel_display *display) diff --git a/pkgbuilds/linux-omarchy/0405-drm-i915-quirk-dell-xps13-dx13260-ptl-psr1-only.patch.sig b/pkgbuilds/linux-omarchy/0405-drm-i915-quirk-dell-xps13-dx13260-ptl-psr1-only.patch.sig new file mode 100644 index 0000000..16a1fb9 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0405-drm-i915-quirk-dell-xps13-dx13260-ptl-psr1-only.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0406-drm-i915-quirk-dell-xps13-dx13260-wcl-panel-replay-exit-on-flush.patch b/pkgbuilds/linux-omarchy/0406-drm-i915-quirk-dell-xps13-dx13260-wcl-panel-replay-exit-on-flush.patch new file mode 100644 index 0000000..94d0d55 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0406-drm-i915-quirk-dell-xps13-dx13260-wcl-panel-replay-exit-on-flush.patch @@ -0,0 +1,19 @@ +diff --git a/drivers/gpu/drm/i915/display/intel_quirks.c b/drivers/gpu/drm/i915/display/intel_quirks.c +--- a/drivers/gpu/drm/i915/display/intel_quirks.c ++++ b/drivers/gpu/drm/i915/display/intel_quirks.c +@@ -305,6 +305,15 @@ static const struct intel_dpcd_quirk intel_dpcd_quirks[] = { + .sink_oui = SINK_OUI(0x00, 0x22, 0xb9), + .hook = quirk_disable_edp_panel_replay, + }, ++ /* Dell XPS 13 DX13260 (Wildcat Lake), sink "Bamboo" */ ++ { ++ .device = DEVICE_ID_ANY, ++ .subsystem_vendor = 0x1028, ++ .subsystem_device = 0x0e53, ++ .sink_oui = SINK_OUI(0x00, 0x22, 0xb9), ++ .sink_device_id = SINK_DEVICE_ID(0x42, 0x61, 0x6d, 0x62, 0x6f, 0x6f), ++ .hook = quirk_panel_replay_exit_on_flush, ++ }, + }; + + void intel_init_quirks(struct intel_display *display) diff --git a/pkgbuilds/linux-omarchy/0406-drm-i915-quirk-dell-xps13-dx13260-wcl-panel-replay-exit-on-flush.patch.sig b/pkgbuilds/linux-omarchy/0406-drm-i915-quirk-dell-xps13-dx13260-wcl-panel-replay-exit-on-flush.patch.sig new file mode 100644 index 0000000..36a38a4 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0406-drm-i915-quirk-dell-xps13-dx13260-wcl-panel-replay-exit-on-flush.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0430-fbc.patch b/pkgbuilds/linux-omarchy/0430-fbc.patch index 4548866..b64db93 100644 --- a/pkgbuilds/linux-omarchy/0430-fbc.patch +++ b/pkgbuilds/linux-omarchy/0430-fbc.patch @@ -55,7 +55,7 @@ diff --git a/drivers/gpu/drm/i915/display/intel_fbc.c b/drivers/gpu/drm/i915/dis diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c --- a/drivers/gpu/drm/i915/display/skl_universal_plane.c +++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c -@@ -1937,10 +1937,10 @@ static int intel_plane_min_height(struct intel_plane *plane, +@@ -1932,10 +1932,10 @@ static int intel_plane_min_height(struct intel_plane *plane, return 1; } @@ -70,7 +70,7 @@ diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/dr { if (plane->max_width) return plane->max_width(fb, color_plane, rotation); -@@ -1948,10 +1948,10 @@ static int intel_plane_max_width(struct intel_plane *plane, +@@ -1943,10 +1943,10 @@ static int intel_plane_max_width(struct intel_plane *plane, return INT_MAX; } diff --git a/pkgbuilds/linux-omarchy/0430-fbc.patch.sig b/pkgbuilds/linux-omarchy/0430-fbc.patch.sig index f0db1f3..41dee2a 100644 Binary files a/pkgbuilds/linux-omarchy/0430-fbc.patch.sig and b/pkgbuilds/linux-omarchy/0430-fbc.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0442-drm-i915-psr-clear-stale-sel-fetch-bits.patch b/pkgbuilds/linux-omarchy/0442-drm-i915-psr-clear-stale-sel-fetch-bits.patch new file mode 100644 index 0000000..e2e1fac --- /dev/null +++ b/pkgbuilds/linux-omarchy/0442-drm-i915-psr-clear-stale-sel-fetch-bits.patch @@ -0,0 +1,95 @@ +diff --git a/drivers/gpu/drm/i915/display/intel_cursor.c b/drivers/gpu/drm/i915/display/intel_cursor.c +--- a/drivers/gpu/drm/i915/display/intel_cursor.c ++++ b/drivers/gpu/drm/i915/display/intel_cursor.c +@@ -536,7 +536,8 @@ static void i9xx_cursor_disable_sel_fetch_arm(struct intel_dsb *dsb, + struct intel_display *display = to_intel_display(plane); + enum pipe pipe = plane->pipe; + +- if (!crtc_state->enable_psr2_sel_fetch) ++ if (!crtc_state->enable_psr2_sel_fetch && ++ !crtc_state->clear_psr2_sel_fetch) + return; + + intel_de_write_dsb(display, dsb, SEL_FETCH_CUR_CTL(pipe), 0); +@@ -569,8 +570,10 @@ static void i9xx_cursor_update_sel_fetch_arm(struct intel_dsb *dsb, + struct intel_display *display = to_intel_display(plane); + enum pipe pipe = plane->pipe; + +- if (!crtc_state->enable_psr2_sel_fetch) ++ if (!crtc_state->enable_psr2_sel_fetch) { ++ i9xx_cursor_disable_sel_fetch_arm(dsb, plane, crtc_state); + return; ++ } + + if (drm_rect_height(&plane_state->psr2_sel_fetch_area) > 0) { + if (crtc_state->enable_psr2_su_region_et) { +diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h +--- a/drivers/gpu/drm/i915/display/intel_display_types.h ++++ b/drivers/gpu/drm/i915/display/intel_display_types.h +@@ -1177,6 +1177,8 @@ struct intel_crtc_state { + bool has_sel_update; + bool enable_psr2_sel_fetch; + bool enable_psr2_su_region_et; ++ /* Drop the stale selective fetch enable bits as selective fetch is turned off */ ++ bool clear_psr2_sel_fetch; + bool req_psr2_sdp_prior_scanline; + bool has_panel_replay; + bool link_off_after_as_sdp_when_pr_active; +diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c +--- a/drivers/gpu/drm/i915/display/intel_psr.c ++++ b/drivers/gpu/drm/i915/display/intel_psr.c +@@ -2941,6 +2941,8 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state, + struct intel_crtc *crtc) + { + struct intel_display *display = to_intel_display(state); ++ const struct intel_crtc_state *old_crtc_state = ++ intel_atomic_get_old_crtc_state(state, crtc); + struct intel_crtc_state *crtc_state = intel_atomic_get_new_crtc_state(state, crtc); + struct intel_plane_state *new_plane_state, *old_plane_state; + struct intel_plane *plane; +@@ -2953,6 +2955,19 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state, + bool full_update = false, su_area_changed; + int i, ret; + ++ /* ++ * Selective fetch is not always usable, for instance it is dropped ++ * while pipe CRC is active. The planes keep their selective fetch ++ * enable bit set in hardware over that, and a plane disabled while ++ * selective fetch is off never gets the bit cleared. Once selective ++ * fetch comes back the hardware would resume fetching for a plane that ++ * is no longer enabled and keep its DDB range reserved, so have the ++ * plane update drop the bit for every plane of the pipe as selective ++ * fetch is turned off. ++ */ ++ crtc_state->clear_psr2_sel_fetch = old_crtc_state->enable_psr2_sel_fetch && ++ !crtc_state->enable_psr2_sel_fetch; ++ + if (!crtc_state->enable_psr2_sel_fetch) + return 0; + +diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c +--- a/drivers/gpu/drm/i915/display/skl_universal_plane.c ++++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c +@@ -885,7 +885,8 @@ static void icl_plane_disable_sel_fetch_arm(struct intel_dsb *dsb, + struct intel_display *display = to_intel_display(plane); + enum pipe pipe = plane->pipe; + +- if (!crtc_state->enable_psr2_sel_fetch) ++ if (!crtc_state->enable_psr2_sel_fetch && ++ !crtc_state->clear_psr2_sel_fetch) + return; + + intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_CTL(pipe, plane->id), 0); +@@ -1634,10 +1635,8 @@ static void icl_plane_update_sel_fetch_arm(struct intel_dsb *dsb, + struct intel_display *display = to_intel_display(plane); + enum pipe pipe = plane->pipe; + +- if (!crtc_state->enable_psr2_sel_fetch) +- return; +- +- if (drm_rect_height(&plane_state->psr2_sel_fetch_area) > 0) ++ if (crtc_state->enable_psr2_sel_fetch && ++ drm_rect_height(&plane_state->psr2_sel_fetch_area) > 0) + intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_CTL(pipe, plane->id), + SEL_FETCH_PLANE_CTL_ENABLE); + else diff --git a/pkgbuilds/linux-omarchy/0442-drm-i915-psr-clear-stale-sel-fetch-bits.patch.sig b/pkgbuilds/linux-omarchy/0442-drm-i915-psr-clear-stale-sel-fetch-bits.patch.sig new file mode 100644 index 0000000..fa39ab5 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0442-drm-i915-psr-clear-stale-sel-fetch-bits.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0443-drm-i915-dsb-delayed-vblank-scanline-shift.patch b/pkgbuilds/linux-omarchy/0443-drm-i915-dsb-delayed-vblank-scanline-shift.patch new file mode 100644 index 0000000..820d377 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0443-drm-i915-dsb-delayed-vblank-scanline-shift.patch @@ -0,0 +1,22 @@ +diff --git a/drivers/gpu/drm/i915/display/intel_dsb.c b/drivers/gpu/drm/i915/display/intel_dsb.c +--- a/drivers/gpu/drm/i915/display/intel_dsb.c ++++ b/drivers/gpu/drm/i915/display/intel_dsb.c +@@ -902,9 +902,17 @@ void intel_dsb_wait_for_delayed_vblank(struct intel_atomic_state *state, + * the hardware itself guarantees that we're SCL lines + * away from the delayed vblank, and we won't be inside + * the vmin safe window so this extra wait does nothing. ++ * ++ * Experimentally, DSB may observe a slightly stale ++ * PIPEDSL value. When the actual scanline has just reached ++ * safe_window_start, WAIT_DSL_OUT may complete immediately ++ * due to the stale value. ++ * ++ * Shift the start back by one scanline to ensure the wait ++ * window is entered reliably. + */ + intel_dsb_wait_scanline_out(state, dsb, +- intel_vrr_safe_window_start(crtc_state), ++ intel_vrr_safe_window_start(crtc_state) - 1, + intel_vrr_vmin_safe_window_end(crtc_state)); + /* + * When the push is sent during vblank it will trigger diff --git a/pkgbuilds/linux-omarchy/0443-drm-i915-dsb-delayed-vblank-scanline-shift.patch.sig b/pkgbuilds/linux-omarchy/0443-drm-i915-dsb-delayed-vblank-scanline-shift.patch.sig new file mode 100644 index 0000000..cd6b14b Binary files /dev/null and b/pkgbuilds/linux-omarchy/0443-drm-i915-dsb-delayed-vblank-scanline-shift.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0444-drm-i915-vrr-dc-balance-off-by-default.patch b/pkgbuilds/linux-omarchy/0444-drm-i915-vrr-dc-balance-off-by-default.patch new file mode 100644 index 0000000..9efda11 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0444-drm-i915-vrr-dc-balance-off-by-default.patch @@ -0,0 +1,42 @@ +diff --git a/drivers/gpu/drm/i915/display/intel_display_params.c b/drivers/gpu/drm/i915/display/intel_display_params.c +--- a/drivers/gpu/drm/i915/display/intel_display_params.c ++++ b/drivers/gpu/drm/i915/display/intel_display_params.c +@@ -120,6 +120,10 @@ intel_display_param_named_unsafe(enable_psr, int, 0400, + "(0=disabled, 1=enable up to PSR1, 2=enable up to PSR2) " + "Default: -1 (use per-chip default)"); + ++intel_display_param_named_unsafe(enable_dc_balance, bool, 0400, ++ "Enable VRR DC balance (0=disabled, 1=enabled). " ++ "Default: 0 (disabled)"); ++ + intel_display_param_named_unsafe(enable_panel_replay, int, 0400, + "Enable Panel Replay (0=disabled, 1=enabled). Default: -1 (use per-chip default)"); + +diff --git a/drivers/gpu/drm/i915/display/intel_display_params.h b/drivers/gpu/drm/i915/display/intel_display_params.h +--- a/drivers/gpu/drm/i915/display/intel_display_params.h ++++ b/drivers/gpu/drm/i915/display/intel_display_params.h +@@ -46,6 +46,7 @@ struct drm_printer; + param(bool, enable_dp_mst, true, 0600) \ + param(int, enable_fbc, -1, 0600) \ + param(int, enable_psr, -1, 0600) \ ++ param(bool, enable_dc_balance, false, 0600) \ + param(int, enable_panel_replay, -1, 0600) \ + param(bool, psr_safest_params, false, 0400) \ + param(bool, enable_psr2_sel_fetch, true, 0400) \ +diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c +--- a/drivers/gpu/drm/i915/display/intel_vrr.c ++++ b/drivers/gpu/drm/i915/display/intel_vrr.c +@@ -383,10 +383,12 @@ static bool intel_vrr_dc_balance_possible(const struct intel_crtc_state *crtc_st + static void + intel_vrr_dc_balance_compute_config(struct intel_crtc_state *crtc_state) + { ++ struct intel_display *display = to_intel_display(crtc_state); + int guardband_usec, adjustment_usec; + struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; + +- if (!intel_vrr_dc_balance_possible(crtc_state) || !crtc_state->vrr.enable) ++ if (!intel_vrr_dc_balance_possible(crtc_state) || ++ !crtc_state->vrr.enable || !display->params.enable_dc_balance) + return; + + crtc_state->vrr.dc_balance.vmax = crtc_state->vrr.vmax; diff --git a/pkgbuilds/linux-omarchy/0444-drm-i915-vrr-dc-balance-off-by-default.patch.sig b/pkgbuilds/linux-omarchy/0444-drm-i915-vrr-dc-balance-off-by-default.patch.sig new file mode 100644 index 0000000..c8a67fa Binary files /dev/null and b/pkgbuilds/linux-omarchy/0444-drm-i915-vrr-dc-balance-off-by-default.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0450-amd-hdmi-vrr-allm.patch b/pkgbuilds/linux-omarchy/0450-amd-hdmi-vrr-allm.patch index ebe9a36..02991bc 100644 --- a/pkgbuilds/linux-omarchy/0450-amd-hdmi-vrr-allm.patch +++ b/pkgbuilds/linux-omarchy/0450-amd-hdmi-vrr-allm.patch @@ -9,7 +9,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm #include "modules/inc/mod_power.h" #include "modules/power/power_helpers.h" -@@ -10068,6 +10069,9 @@ static void update_freesync_state_on_stream( +@@ -10088,6 +10089,9 @@ static void update_freesync_state_on_stream( &vrr_infopacket, pack_sdp_v1_3); @@ -19,7 +19,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm new_crtc_state->freesync_vrr_info_changed |= (memcmp(&new_crtc_state->vrr_infopacket, &vrr_infopacket, -@@ -10079,6 +10083,36 @@ static void update_freesync_state_on_stream( +@@ -10099,6 +10103,36 @@ static void update_freesync_state_on_stream( new_stream->vrr_infopacket = vrr_infopacket; new_stream->allow_freesync = mod_freesync_get_freesync_enabled(&vrr_params); @@ -56,7 +56,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm if (new_crtc_state->freesync_vrr_info_changed) drm_dbg_kms(adev_to_drm(adev), "VRR packet update: crtc=%u enabled=%d state=%d", new_crtc_state->base.crtc->base.id, -@@ -10645,9 +10679,12 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, +@@ -10665,9 +10699,12 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, } if (acrtc_state->stream) { @@ -70,7 +70,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm } } -@@ -12049,6 +12086,14 @@ static void get_freesync_config_for_crtc( +@@ -12069,6 +12106,14 @@ static void get_freesync_config_for_crtc( } out: new_crtc_state->freesync_config = config; @@ -85,7 +85,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm } static void reset_freesync_config_for_crtc( -@@ -14011,6 +14056,15 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, +@@ -14031,6 +14076,15 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, if (!adev->dm.freesync_module || !dc_supports_vrr(sink->ctx->dce_version)) goto update; @@ -101,7 +101,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm edid = drm_edid_raw(drm_edid); // FIXME: Get rid of drm_edid_raw() /* Some eDP panels only have the refresh rate range info in DisplayID */ -@@ -14036,7 +14090,9 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, +@@ -14056,7 +14110,9 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, amdgpu_dm_connector->as_type = ADAPTIVE_SYNC_TYPE_EDP; } @@ -112,7 +112,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info); if (i >= 0) { amdgpu_dm_connector->vsdb_info = vsdb_info; -@@ -14052,6 +14108,59 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, +@@ -14072,6 +14128,59 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz; } } @@ -172,7 +172,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm } if (amdgpu_dm_connector->dc_link) -@@ -14093,6 +14202,11 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, +@@ -14113,6 +14222,11 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, if (dm_con_state) dm_con_state->freesync_capable = freesync_capable; diff --git a/pkgbuilds/linux-omarchy/0450-amd-hdmi-vrr-allm.patch.sig b/pkgbuilds/linux-omarchy/0450-amd-hdmi-vrr-allm.patch.sig index ee35537..8e70633 100644 Binary files a/pkgbuilds/linux-omarchy/0450-amd-hdmi-vrr-allm.patch.sig and b/pkgbuilds/linux-omarchy/0450-amd-hdmi-vrr-allm.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0451-amd-vtem-tmds-links.patch b/pkgbuilds/linux-omarchy/0451-amd-vtem-tmds-links.patch index f61c400..5bc46ad 100644 --- a/pkgbuilds/linux-omarchy/0451-amd-vtem-tmds-links.patch +++ b/pkgbuilds/linux-omarchy/0451-amd-vtem-tmds-links.patch @@ -1,7 +1,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c -@@ -10069,7 +10069,10 @@ static void update_freesync_state_on_stream( +@@ -10089,7 +10089,10 @@ static void update_freesync_state_on_stream( &vrr_infopacket, pack_sdp_v1_3); diff --git a/pkgbuilds/linux-omarchy/0451-amd-vtem-tmds-links.patch.sig b/pkgbuilds/linux-omarchy/0451-amd-vtem-tmds-links.patch.sig index 7077a0e..29e7f44 100644 Binary files a/pkgbuilds/linux-omarchy/0451-amd-vtem-tmds-links.patch.sig and b/pkgbuilds/linux-omarchy/0451-amd-vtem-tmds-links.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0453-amd-vtem-null-sink.patch b/pkgbuilds/linux-omarchy/0453-amd-vtem-null-sink.patch new file mode 100644 index 0000000..0a3c3d3 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0453-amd-vtem-null-sink.patch @@ -0,0 +1,14 @@ +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +@@ -10090,8 +10090,8 @@ static void update_freesync_state_on_stream( + pack_sdp_v1_3); + + /* Per HDMI 2.1, VTEM is valid on TMDS as well as FRL */ +- if (new_stream->sink->sink_signal == SIGNAL_TYPE_HDMI_FRL || +- (new_stream->sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A && ++ if (new_stream->signal == SIGNAL_TYPE_HDMI_FRL || ++ (new_stream->signal == SIGNAL_TYPE_HDMI_TYPE_A && + aconn && aconn->base.display_info.hdmi.vrr_cap.supported)) + mod_build_infopacket_vtem(new_stream, &vrr_params, 0, &vrr_infopacket); + diff --git a/pkgbuilds/linux-omarchy/0453-amd-vtem-null-sink.patch.sig b/pkgbuilds/linux-omarchy/0453-amd-vtem-null-sink.patch.sig new file mode 100644 index 0000000..795a770 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0453-amd-vtem-null-sink.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0454-amd-hdmi-frl-status-polling.patch b/pkgbuilds/linux-omarchy/0454-amd-hdmi-frl-status-polling.patch new file mode 100644 index 0000000..62ada93 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0454-amd-hdmi-frl-status-polling.patch @@ -0,0 +1,12 @@ +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +@@ -2025,7 +2025,7 @@ static void hdmi_frl_status_polling_work(struct work_struct *work) + if (!dc_is_hdmi_signal(dc_link->connector_signal)) + continue; + +- if (dc_link->connector_signal != SIGNAL_TYPE_HDMI_FRL) ++ if (dc_link->frl_link_settings.frl_link_rate == 0) + continue; + + link_update = dc_link_frl_poll_status_flag(dc_link); diff --git a/pkgbuilds/linux-omarchy/0454-amd-hdmi-frl-status-polling.patch.sig b/pkgbuilds/linux-omarchy/0454-amd-hdmi-frl-status-polling.patch.sig new file mode 100644 index 0000000..786879a Binary files /dev/null and b/pkgbuilds/linux-omarchy/0454-amd-hdmi-frl-status-polling.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0455-amd-hdmi-frl-cap-on-nondestructive-verify.patch b/pkgbuilds/linux-omarchy/0455-amd-hdmi-frl-cap-on-nondestructive-verify.patch new file mode 100644 index 0000000..26db142 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0455-amd-hdmi-frl-cap-on-nondestructive-verify.patch @@ -0,0 +1,12 @@ +diff --git a/drivers/gpu/drm/amd/display/dc/link/link_detection.c b/drivers/gpu/drm/amd/display/dc/link/link_detection.c +--- a/drivers/gpu/drm/amd/display/dc/link/link_detection.c ++++ b/drivers/gpu/drm/amd/display/dc/link/link_detection.c +@@ -882,7 +882,7 @@ static void verify_link_capability_non_destructive(struct dc_link *link) + else + link->verified_link_cap = dp_get_max_link_cap(link); + } else if (dc_is_hdmi_signal(link->local_sink->sink_signal)) { +- link->verified_link_cap = link->reported_link_cap; ++ link->frl_verified_link_cap = link->frl_reported_link_cap; + + if (is_hdmi_frl_in_use(link)) { + link->local_sink->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A; diff --git a/pkgbuilds/linux-omarchy/0455-amd-hdmi-frl-cap-on-nondestructive-verify.patch.sig b/pkgbuilds/linux-omarchy/0455-amd-hdmi-frl-cap-on-nondestructive-verify.patch.sig new file mode 100644 index 0000000..f225581 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0455-amd-hdmi-frl-cap-on-nondestructive-verify.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0456-amd-dml-frl-compressed-cap-check.patch b/pkgbuilds/linux-omarchy/0456-amd-dml-frl-compressed-cap-check.patch new file mode 100644 index 0000000..4531604 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0456-amd-dml-frl-compressed-cap-check.patch @@ -0,0 +1,26 @@ +diff --git a/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c +--- a/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c ++++ b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c +@@ -630,17 +630,17 @@ enum frl_cap_chk_result dml1_frl_cap_chk(struct frl_cap_chk_params *params) + { + struct frl_cap_chk_intermediates inter; + +-#if defined (CONFIG_DRM_AMD_DC_FP) +- if (params->compressed) +- return dml1_frl_cap_chk_compressed(params, &inter); +-#endif +- + return dml1_frl_cap_chk_inter(params, &inter); + } + + enum frl_cap_chk_result dml1_frl_cap_chk_inter(struct frl_cap_chk_params *params, + struct frl_cap_chk_intermediates *inter) + { ++#if defined (CONFIG_DRM_AMD_DC_FP) ++ if (params->compressed) ++ return dml1_frl_cap_chk_compressed(params, inter); ++#endif ++ + return dml1_frl_cap_chk_uncompressed(params, inter); + } + diff --git a/pkgbuilds/linux-omarchy/0456-amd-dml-frl-compressed-cap-check.patch.sig b/pkgbuilds/linux-omarchy/0456-amd-dml-frl-compressed-cap-check.patch.sig new file mode 100644 index 0000000..8297ca2 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0456-amd-dml-frl-compressed-cap-check.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0460-vesa-displayid-dsc-bpp.patch b/pkgbuilds/linux-omarchy/0460-vesa-displayid-dsc-bpp.patch index 1ba6ec7..7597723 100644 --- a/pkgbuilds/linux-omarchy/0460-vesa-displayid-dsc-bpp.patch +++ b/pkgbuilds/linux-omarchy/0460-vesa-displayid-dsc-bpp.patch @@ -1,7 +1,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c -@@ -7096,6 +7096,11 @@ static void fill_stream_properties_from_drm_display_mode( +@@ -7116,6 +7116,11 @@ static void fill_stream_properties_from_drm_display_mode( stream->output_color_space = get_output_color_space(timing_out, connector_state); stream->content_type = get_output_content_type(connector_state); @@ -13,7 +13,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm } static void fill_audio_info(struct audio_info *audio_info, -@@ -7616,6 +7621,7 @@ create_stream_for_sink(struct drm_connector *connector, +@@ -7636,6 +7641,7 @@ create_stream_for_sink(struct drm_connector *connector, struct drm_display_mode mode; struct drm_display_mode saved_mode; struct drm_display_mode *freesync_mode = NULL; @@ -21,7 +21,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm bool native_mode_found = false; bool recalculate_timing = false; bool scale = dm_state->scaling != RMX_OFF; -@@ -7707,6 +7713,16 @@ create_stream_for_sink(struct drm_connector *connector, +@@ -7727,6 +7733,16 @@ create_stream_for_sink(struct drm_connector *connector, } } diff --git a/pkgbuilds/linux-omarchy/0460-vesa-displayid-dsc-bpp.patch.sig b/pkgbuilds/linux-omarchy/0460-vesa-displayid-dsc-bpp.patch.sig index f76e652..922eab7 100644 Binary files a/pkgbuilds/linux-omarchy/0460-vesa-displayid-dsc-bpp.patch.sig and b/pkgbuilds/linux-omarchy/0460-vesa-displayid-dsc-bpp.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0461-vesa-dsc-passthru-mode-match-fix.patch b/pkgbuilds/linux-omarchy/0461-vesa-dsc-passthru-mode-match-fix.patch index e27b493..b826d02 100644 --- a/pkgbuilds/linux-omarchy/0461-vesa-dsc-passthru-mode-match-fix.patch +++ b/pkgbuilds/linux-omarchy/0461-vesa-dsc-passthru-mode-match-fix.patch @@ -1,7 +1,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c -@@ -7622,6 +7622,7 @@ create_stream_for_sink(struct drm_connector *connector, +@@ -7642,6 +7642,7 @@ create_stream_for_sink(struct drm_connector *connector, struct drm_display_mode saved_mode; struct drm_display_mode *freesync_mode = NULL; struct drm_display_mode *dsc_passthru_mode = NULL; @@ -9,7 +9,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm bool native_mode_found = false; bool recalculate_timing = false; bool scale = dm_state->scaling != RMX_OFF; -@@ -7688,6 +7689,22 @@ create_stream_for_sink(struct drm_connector *connector, +@@ -7708,6 +7709,22 @@ create_stream_for_sink(struct drm_connector *connector, mode_refresh = drm_mode_vrefresh(&mode); @@ -32,7 +32,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm if (preferred_mode == NULL) { /* * This may not be an error, the use case is when we have no -@@ -7713,16 +7730,6 @@ create_stream_for_sink(struct drm_connector *connector, +@@ -7733,16 +7750,6 @@ create_stream_for_sink(struct drm_connector *connector, } } diff --git a/pkgbuilds/linux-omarchy/0461-vesa-dsc-passthru-mode-match-fix.patch.sig b/pkgbuilds/linux-omarchy/0461-vesa-dsc-passthru-mode-match-fix.patch.sig index d303e52..cb18d66 100644 Binary files a/pkgbuilds/linux-omarchy/0461-vesa-dsc-passthru-mode-match-fix.patch.sig and b/pkgbuilds/linux-omarchy/0461-vesa-dsc-passthru-mode-match-fix.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0473-i915-ptl-cdclk-sanitize.patch b/pkgbuilds/linux-omarchy/0473-i915-ptl-cdclk-sanitize.patch deleted file mode 100644 index 08d39ec..0000000 --- a/pkgbuilds/linux-omarchy/0473-i915-ptl-cdclk-sanitize.patch +++ /dev/null @@ -1,16 +0,0 @@ -diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c ---- a/drivers/gpu/drm/i915/display/intel_cdclk.c -+++ b/drivers/gpu/drm/i915/display/intel_cdclk.c -@@ -2366,8 +2366,10 @@ static void bxt_sanitize_cdclk(struct intel_display *display) - * dividers both syncing to an active pipe, or asynchronously - * (PIPE_NONE). - */ -- cdctl &= ~bxt_cdclk_cd2x_pipe(display, INVALID_PIPE); -- cdctl |= bxt_cdclk_cd2x_pipe(display, INVALID_PIPE); -+ if (DISPLAY_VER(display) < 30) { -+ cdctl &= ~bxt_cdclk_cd2x_pipe(display, INVALID_PIPE); -+ cdctl |= bxt_cdclk_cd2x_pipe(display, INVALID_PIPE); -+ } - - if (cdctl != expected) { - if (DISPLAY_VER(display) < 20) { diff --git a/pkgbuilds/linux-omarchy/0473-i915-ptl-cdclk-sanitize.patch.sig b/pkgbuilds/linux-omarchy/0473-i915-ptl-cdclk-sanitize.patch.sig deleted file mode 100644 index 4e8c059..0000000 Binary files a/pkgbuilds/linux-omarchy/0473-i915-ptl-cdclk-sanitize.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/0474-amd-display-oled-vesa-backlight.patch b/pkgbuilds/linux-omarchy/0474-amd-display-oled-vesa-backlight.patch index 590d8a0..035758b 100644 --- a/pkgbuilds/linux-omarchy/0474-amd-display-oled-vesa-backlight.patch +++ b/pkgbuilds/linux-omarchy/0474-amd-display-oled-vesa-backlight.patch @@ -1,7 +1,7 @@ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c -@@ -4160,8 +4160,12 @@ static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector) +@@ -4176,8 +4176,12 @@ static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector) else if (!IS_ERR_OR_NULL(panel_backlight_quirk) && panel_backlight_quirk->force_pwm) caps->aux_support = false; diff --git a/pkgbuilds/linux-omarchy/0474-amd-display-oled-vesa-backlight.patch.sig b/pkgbuilds/linux-omarchy/0474-amd-display-oled-vesa-backlight.patch.sig index 14aa526..f64009c 100644 Binary files a/pkgbuilds/linux-omarchy/0474-amd-display-oled-vesa-backlight.patch.sig and b/pkgbuilds/linux-omarchy/0474-amd-display-oled-vesa-backlight.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0476-amd-display-ycbcr420-only-modes.patch b/pkgbuilds/linux-omarchy/0476-amd-display-ycbcr420-only-modes.patch new file mode 100644 index 0000000..8d6c8a4 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0476-amd-display-ycbcr420-only-modes.patch @@ -0,0 +1,25 @@ +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +@@ -7020,16 +7020,11 @@ static void fill_stream_properties_from_drm_display_mode( + timing_out->v_border_top = 0; + timing_out->v_border_bottom = 0; + /* TODO: un-hardcode */ +- if (drm_mode_is_420_only(info, mode_in) +- && (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A || +- stream->signal == SIGNAL_TYPE_HDMI_FRL) +- && aconnector +- && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420) +- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420; +- else if (drm_mode_is_420_also(info, mode_in) +- && aconnector +- && (aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420 +- || aconnector->force_yuv420_output)) ++ if (drm_mode_is_420_only(info, mode_in) || ++ (aconnector && ++ (aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420 || ++ aconnector->force_yuv420_output) && ++ drm_mode_is_420_also(info, mode_in))) + timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420; + else if ((connector->display_info.color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422)) + && aconnector diff --git a/pkgbuilds/linux-omarchy/0476-amd-display-ycbcr420-only-modes.patch.sig b/pkgbuilds/linux-omarchy/0476-amd-display-ycbcr420-only-modes.patch.sig new file mode 100644 index 0000000..2b5da94 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0476-amd-display-ycbcr420-only-modes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0477-amd-display-irq-atomic-rmw.patch b/pkgbuilds/linux-omarchy/0477-amd-display-irq-atomic-rmw.patch new file mode 100644 index 0000000..1b0eb75 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0477-amd-display-irq-atomic-rmw.patch @@ -0,0 +1,303 @@ +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +@@ -3345,7 +3345,7 @@ static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev, + if (acrtc && state->stream_status[i].plane_count != 0 && + amdgpu_ip_version(adev, DCE_HWIP, 0) == 0) { + irq_source = IRQ_TYPE_PFLIP + acrtc->otg_inst; +- rc = dc_interrupt_set(adev->dm.dc, irq_source, enable) ? 0 : -EBUSY; ++ rc = amdgpu_dm_irq_set(adev, irq_source, enable) ? 0 : -EBUSY; + if (rc) + drm_warn(adev_to_drm(adev), "Failed to %s pflip interrupts\n", + enable ? "enable" : "disable"); +@@ -3369,7 +3369,7 @@ static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev, + /* During gpu-reset we disable and then enable vblank irq, so + * don't use amdgpu_irq_get/put() to avoid refcount change. + */ +- if (!dc_interrupt_set(adev->dm.dc, irq_source, enable)) ++ if (!amdgpu_dm_irq_set(adev, irq_source, enable)) + drm_warn(adev_to_drm(adev), "Failed to %sable vblank interrupt\n", enable ? "en" : "dis"); + + } else if (acrtc && state->stream_status[i].plane_count != 0) { +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +@@ -550,6 +550,18 @@ struct amdgpu_display_manager { + struct common_irq_params + vupdate_params[DC_IRQ_SOURCE_VUPDATE6 - DC_IRQ_SOURCE_VUPDATE1 + 1]; + ++ /** ++ * @irq_reg_lock: ++ * ++ * Serializes the read-modify-writes of the HW interrupt control ++ * registers. Several interrupt sources share one register - e.g. the ++ * enable and clear bits of both VSTARTUP (vblank) and VUPDATE_NO_LOCK ++ * live in OTG_GLOBAL_SYNC_STATUS. Therefore, enabling one source must ++ * not race with acking another. Held only across amdgpu_dm_irq_set() ++ * and amdgpu_dm_irq_ack(). ++ */ ++ spinlock_t irq_reg_lock; ++ + /** + * @dmub_trace_params: + * +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c +@@ -31,6 +31,7 @@ + #include "amdgpu_dm_psr.h" + #include "amdgpu_dm_replay.h" + #include "amdgpu_dm_crtc.h" ++#include "amdgpu_dm_irq.h" + #include "amdgpu_dm_plane.h" + #include "amdgpu_dm_trace.h" + #include "amdgpu_dm_debugfs.h" +@@ -87,7 +88,7 @@ int amdgpu_dm_crtc_set_vupdate_irq(struct drm_crtc *crtc, bool enable) + + irq_source = IRQ_TYPE_VUPDATE + acrtc->otg_inst; + +- rc = dc_interrupt_set(adev->dm.dc, irq_source, enable) ? 0 : -EBUSY; ++ rc = amdgpu_dm_irq_set(adev, irq_source, enable) ? 0 : -EBUSY; + + DRM_DEBUG_VBL("crtc %d - vupdate irq %sabling: r=%d\n", + acrtc->crtc_id, enable ? "en" : "dis", rc); +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +@@ -1378,12 +1378,13 @@ void dm_helpers_free_gpu_mem( + + bool dm_helpers_dmub_outbox_interrupt_control(struct dc_context *ctx, bool enable) + { ++ struct amdgpu_device *adev = ctx->driver_context; + enum dc_irq_source irq_source; + bool ret; + + irq_source = DC_IRQ_SOURCE_DMCUB_OUTBOX; + +- ret = dc_interrupt_set(ctx->dc, irq_source, enable); ++ ret = amdgpu_dm_irq_set(adev, irq_source, enable); + + DRM_DEBUG_DRIVER("Dmub trace irq %sabling: r=%d\n", + enable ? "en" : "dis", ret); +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c +@@ -424,6 +424,7 @@ int amdgpu_dm_irq_init(struct amdgpu_device *adev) + DRM_DEBUG_KMS("DM_IRQ\n"); + + spin_lock_init(&adev->dm.irq_handler_list_table_lock); ++ spin_lock_init(&adev->dm.irq_reg_lock); + + for (src = 0; src < DAL_IRQ_SOURCES_NUMBER; src++) { + /* low context handler list init */ +@@ -496,7 +497,7 @@ void amdgpu_dm_irq_suspend(struct amdgpu_device *adev) + hnd_list_l = &adev->dm.irq_handler_list_low_tab[src]; + hnd_list_h = &adev->dm.irq_handler_list_high_tab[src]; + if (!list_empty(hnd_list_l) || !list_empty(hnd_list_h)) +- dc_interrupt_set(adev->dm.dc, src, false); ++ amdgpu_dm_irq_set(adev, src, false); + + DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); + +@@ -533,7 +534,7 @@ void amdgpu_dm_irq_resume_early(struct amdgpu_device *adev) + hnd_list_l = &adev->dm.irq_handler_list_low_tab[src]; + hnd_list_h = &adev->dm.irq_handler_list_high_tab[src]; + if (!list_empty(hnd_list_l) || !list_empty(hnd_list_h)) +- dc_interrupt_set(adev->dm.dc, src, true); ++ amdgpu_dm_irq_set(adev, src, true); + } + + DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); +@@ -558,7 +559,7 @@ void amdgpu_dm_irq_resume_late(struct amdgpu_device *adev) + hnd_list_l = &adev->dm.irq_handler_list_low_tab[src]; + hnd_list_h = &adev->dm.irq_handler_list_high_tab[src]; + if (!list_empty(hnd_list_l) || !list_empty(hnd_list_h)) +- dc_interrupt_set(adev->dm.dc, src, true); ++ amdgpu_dm_irq_set(adev, src, true); + } + + DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); +@@ -645,6 +646,21 @@ static void amdgpu_dm_irq_immediate_work(struct amdgpu_device *adev, + DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); + } + ++bool amdgpu_dm_irq_set(struct amdgpu_device *adev, enum dc_irq_source src, ++ bool enable) ++{ ++ guard(spinlock_irqsave)(&adev->dm.irq_reg_lock); ++ ++ return dc_interrupt_set(adev->dm.dc, src, enable); ++} ++ ++void amdgpu_dm_irq_ack(struct amdgpu_device *adev, enum dc_irq_source src) ++{ ++ guard(spinlock_irqsave)(&adev->dm.irq_reg_lock); ++ ++ dc_interrupt_ack(adev->dm.dc, src); ++} ++ + /** + * amdgpu_dm_irq_handler - Generic DM IRQ handler + * @adev: amdgpu base driver device containing the DM device +@@ -665,7 +681,7 @@ static int amdgpu_dm_irq_handler(struct amdgpu_device *adev, + entry->src_id, + entry->src_data[0]); + +- dc_interrupt_ack(adev->dm.dc, src); ++ amdgpu_dm_irq_ack(adev, src); + + /* Call high irq work immediately */ + amdgpu_dm_irq_immediate_work(adev, src); +@@ -703,7 +719,7 @@ static int amdgpu_dm_set_hpd_irq_state(struct amdgpu_device *adev, + enum dc_irq_source src = amdgpu_dm_hpd_to_dal_irq_source(type); + bool st = (state == AMDGPU_IRQ_STATE_ENABLE); + +- dc_interrupt_set(adev->dm.dc, src, st); ++ amdgpu_dm_irq_set(adev, src, st); + return 0; + } + +@@ -737,7 +753,7 @@ static inline int dm_irq_state(struct amdgpu_device *adev, + if (dc && dc->caps.ips_support && dc->idle_optimizations_allowed) + dc_allow_idle_optimizations(dc, false); + +- dc_interrupt_set(adev->dm.dc, irq_source, st); ++ amdgpu_dm_irq_set(adev, irq_source, st); + return 0; + } + +@@ -791,7 +807,7 @@ static int amdgpu_dm_set_dmub_outbox_irq_state(struct amdgpu_device *adev, + enum dc_irq_source irq_source = DC_IRQ_SOURCE_DMCUB_OUTBOX; + bool st = (state == AMDGPU_IRQ_STATE_ENABLE); + +- dc_interrupt_set(adev->dm.dc, irq_source, st); ++ amdgpu_dm_irq_set(adev, irq_source, st); + return 0; + } + +@@ -817,7 +833,7 @@ static int amdgpu_dm_set_dmub_trace_irq_state(struct amdgpu_device *adev, + enum dc_irq_source irq_source = DC_IRQ_SOURCE_DMCUB_OUTBOX0; + bool st = (state == AMDGPU_IRQ_STATE_ENABLE); + +- dc_interrupt_set(adev->dm.dc, irq_source, st); ++ amdgpu_dm_irq_set(adev, irq_source, st); + return 0; + } + +@@ -881,9 +897,7 @@ void amdgpu_dm_set_irq_funcs(struct amdgpu_device *adev) + } + void amdgpu_dm_outbox_init(struct amdgpu_device *adev) + { +- dc_interrupt_set(adev->dm.dc, +- DC_IRQ_SOURCE_DMCUB_OUTBOX, +- true); ++ amdgpu_dm_irq_set(adev, DC_IRQ_SOURCE_DMCUB_OUTBOX, true); + } + + /** +@@ -905,7 +919,7 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev) + + /* First, clear all hpd and hpdrx interrupts */ + for (i = DC_IRQ_SOURCE_HPD1; i <= DC_IRQ_SOURCE_HPD6RX; i++) { +- if (!dc_interrupt_set(adev->dm.dc, i, false)) ++ if (!amdgpu_dm_irq_set(adev, i, false)) + drm_err(dev, "Failed to clear hpd(rx) source=%d on init\n", + i); + } +@@ -934,7 +948,7 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev) + * of dm. Note that only hpd interrupt types are registered with + * base driver; hpd_rx types aren't. IOW, amdgpu_irq_get/put on + * hpd_rx isn't available. DM currently controls hpd_rx +- * explicitly with dc_interrupt_set() ++ * explicitly with amdgpu_dm_irq_set() + */ + if (dc_link->irq_source_hpd != DC_IRQ_SOURCE_INVALID) { + irq_type = dc_link->irq_source_hpd - DC_IRQ_SOURCE_HPD1; +@@ -943,23 +957,21 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev) + * and what bios reports as the # of connectors with hpd + * sources. Since the # of hpd source types registered + * with base driver == mode_info.num_hpd, we have to +- * fallback to dc_interrupt_set for the remaining types. ++ * fallback to amdgpu_dm_irq_set for the remaining types. + */ + if (irq_type < adev->mode_info.num_hpd) { + if (amdgpu_irq_get(adev, &adev->hpd_irq, irq_type)) + drm_err(dev, "DM_IRQ: Failed get HPD for source=%d)!\n", + dc_link->irq_source_hpd); + } else { +- dc_interrupt_set(adev->dm.dc, +- dc_link->irq_source_hpd, +- true); ++ amdgpu_dm_irq_set(adev, dc_link->irq_source_hpd, ++ true); + } + } + + if (dc_link->irq_source_hpd_rx != DC_IRQ_SOURCE_INVALID) { +- dc_interrupt_set(adev->dm.dc, +- dc_link->irq_source_hpd_rx, +- true); ++ amdgpu_dm_irq_set(adev, dc_link->irq_source_hpd_rx, ++ true); + } + } + drm_connector_list_iter_end(&iter); +@@ -1003,16 +1015,14 @@ void amdgpu_dm_hpd_fini(struct amdgpu_device *adev) + drm_err(dev, "DM_IRQ: Failed put HPD for source=%d!\n", + dc_link->irq_source_hpd); + } else { +- dc_interrupt_set(adev->dm.dc, +- dc_link->irq_source_hpd, +- false); ++ amdgpu_dm_irq_set(adev, dc_link->irq_source_hpd, ++ false); + } + } + + if (dc_link->irq_source_hpd_rx != DC_IRQ_SOURCE_INVALID) { +- dc_interrupt_set(adev->dm.dc, +- dc_link->irq_source_hpd_rx, +- false); ++ amdgpu_dm_irq_set(adev, dc_link->irq_source_hpd_rx, ++ false); + } + } + drm_connector_list_iter_end(&iter); +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h +@@ -81,6 +81,34 @@ void amdgpu_dm_irq_unregister_interrupt(struct amdgpu_device *adev, + enum dc_irq_source irq_source, + void *ih_index); + ++/** ++ * amdgpu_dm_irq_set - enable or disable a DC interrupt source. ++ * ++ * @adev: AMD DRM device ++ * @src: DC interrupt source to toggle ++ * @enable: true to enable the source, false to disable it ++ * ++ * DM-wide replacement for dc_interrupt_set(). As locking is DM's ++ * responsibility, this is a thin wrapper serializes the underlying ++ * read-modify-write against the other interrupt sources sharing HW control ++ * registers with @src, so DM must never call dc_interrupt_set() directly. ++ * ++ * Returns: true if the source was toggled. ++ */ ++bool amdgpu_dm_irq_set(struct amdgpu_device *adev, enum dc_irq_source src, ++ bool enable); ++ ++/** ++ * amdgpu_dm_irq_ack - acknowledge a DC interrupt source. ++ * ++ * @adev: AMD DRM device ++ * @src: DC interrupt source to acknowledge ++ * ++ * DM-wide replacement for dc_interrupt_ack(), serialized the same way as ++ * amdgpu_dm_irq_set(). ++ */ ++void amdgpu_dm_irq_ack(struct amdgpu_device *adev, enum dc_irq_source src); ++ + void amdgpu_dm_set_irq_funcs(struct amdgpu_device *adev); + + void amdgpu_dm_outbox_init(struct amdgpu_device *adev); diff --git a/pkgbuilds/linux-omarchy/0477-amd-display-irq-atomic-rmw.patch.sig b/pkgbuilds/linux-omarchy/0477-amd-display-irq-atomic-rmw.patch.sig new file mode 100644 index 0000000..c0f963e Binary files /dev/null and b/pkgbuilds/linux-omarchy/0477-amd-display-irq-atomic-rmw.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0478-amd-freesync-vcp-code-check-hdmi-pcon.patch b/pkgbuilds/linux-omarchy/0478-amd-freesync-vcp-code-check-hdmi-pcon.patch new file mode 100644 index 0000000..dcdb282 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0478-amd-freesync-vcp-code-check-hdmi-pcon.patch @@ -0,0 +1,16 @@ +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +@@ -14232,6 +14232,12 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, + } + } + ++ /* HDMI/PCON without an EDID FreeSync VCP code is not FreeSync-capable. */ ++ if ((sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A || ++ as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) && ++ !sink->edid_caps.freesync_vcp_code) ++ freesync_capable = false; ++ + /* Handle MCCS */ + if (do_mccs) { + dm_helpers_read_mccs_caps(adev->dm.dc->ctx, amdgpu_dm_connector->dc_link, sink); diff --git a/pkgbuilds/linux-omarchy/0478-amd-freesync-vcp-code-check-hdmi-pcon.patch.sig b/pkgbuilds/linux-omarchy/0478-amd-freesync-vcp-code-check-hdmi-pcon.patch.sig new file mode 100644 index 0000000..eed1a60 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0478-amd-freesync-vcp-code-check-hdmi-pcon.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0479-amd-hdmi-allm-hf-vsif-in-vsp-infopacket.patch b/pkgbuilds/linux-omarchy/0479-amd-hdmi-allm-hf-vsif-in-vsp-infopacket.patch new file mode 100644 index 0000000..13b9043 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0479-amd-hdmi-allm-hf-vsif-in-vsp-infopacket.patch @@ -0,0 +1,40 @@ +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +@@ -10135,20 +10135,19 @@ static void update_freesync_state_on_stream( + */ + if (new_stream->signal == SIGNAL_TYPE_HDMI_TYPE_A || + new_stream->signal == SIGNAL_TYPE_HDMI_FRL) { +- struct dc_info_packet hf_vsif = {0}; ++ struct dc_info_packet vsp_infopacket = {0}; + bool sink_allm = aconn && aconn->base.display_info.hdmi.allm; + bool allm = sink_allm && + (vrr_params.state == VRR_STATE_ACTIVE_VARIABLE || + vrr_params.state == VRR_STATE_ACTIVE_FIXED); + bool allm_changed; + +- if (allm) +- mod_build_hf_vsif_infopacket(new_stream, &hf_vsif, allm, allm); ++ mod_build_hf_vsif_infopacket(new_stream, &vsp_infopacket, allm, allm); + +- allm_changed = memcmp(&new_stream->hfvsif_infopacket, &hf_vsif, +- sizeof(hf_vsif)) != 0; ++ allm_changed = memcmp(&new_stream->vsp_infopacket, &vsp_infopacket, ++ sizeof(vsp_infopacket)) != 0; + new_crtc_state->freesync_vrr_info_changed |= allm_changed; +- new_stream->hfvsif_infopacket = hf_vsif; ++ new_stream->vsp_infopacket = vsp_infopacket; + + if (allm_changed) + drm_dbg_driver(adev_to_drm(adev), +@@ -10727,8 +10726,8 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, + if (acrtc_state->freesync_vrr_info_changed) { + bundle->stream_update.vrr_infopacket = + &acrtc_state->stream->vrr_infopacket; +- bundle->stream_update.hfvsif_infopacket = +- &acrtc_state->stream->hfvsif_infopacket; ++ bundle->stream_update.vsp_infopacket = ++ &acrtc_state->stream->vsp_infopacket; + } + } + } diff --git a/pkgbuilds/linux-omarchy/0479-amd-hdmi-allm-hf-vsif-in-vsp-infopacket.patch.sig b/pkgbuilds/linux-omarchy/0479-amd-hdmi-allm-hf-vsif-in-vsp-infopacket.patch.sig new file mode 100644 index 0000000..37a9ec9 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0479-amd-hdmi-allm-hf-vsif-in-vsp-infopacket.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0510-sound-updates.patch b/pkgbuilds/linux-omarchy/0510-sound-updates.patch index 29d72a3..1368f53 100644 --- a/pkgbuilds/linux-omarchy/0510-sound-updates.patch +++ b/pkgbuilds/linux-omarchy/0510-sound-updates.patch @@ -1,7 +1,7 @@ diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c -@@ -1752,6 +1752,7 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device) +@@ -1757,6 +1757,7 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device) * by the drivers/platform/x86/serial-multi-instantiate.c driver, which * knows which client device id to use for each resource. */ @@ -9,18 +9,6 @@ diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c {"BSG1160", }, {"BSG2150", }, {"CSC3551", }, -diff --git a/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c b/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c ---- a/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c -+++ b/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c -@@ -147,7 +147,7 @@ static const struct reg_default halo_register_defaults[] = { - { 0x2b805c0, 0 }, /* HALO_SCRATCH1 */ - { 0x2b805c8, 0 }, /* HALO_SCRATCH2 */ - { 0x2b805d0, 0 }, /* HALO_SCRATCH3 */ -- { 0x2b805c8, 0 }, /* HALO_SCRATCH4 */ -+ { 0x2b805d8, 0 }, /* HALO_SCRATCH4 */ - { 0x2bc1000, 0 }, /* HALO_CCM_CORE_CONTROL */ - { 0x2bc7000, 0 }, /* HALO_WDT_CONTROL */ - diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c --- a/drivers/platform/x86/serial-multi-instantiate.c +++ b/drivers/platform/x86/serial-multi-instantiate.c @@ -48,132 +36,6 @@ diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/ { "BSG1160", (unsigned long)&bsg1160_data }, { "BSG2150", (unsigned long)&bsg2150_data }, { "CSC3551", (unsigned long)&cs35l41_hda }, -diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c ---- a/drivers/soundwire/bus_type.c -+++ b/drivers/soundwire/bus_type.c -@@ -105,6 +105,9 @@ static int sdw_bus_probe(struct device *dev) - } - slave->index = ret; - -+ /* Create IRQ mapping now so the driver can get it in probe() */ -+ sdw_irq_create_mapping(slave); -+ - ret = drv->probe(slave, id); - if (ret) { - ida_free(&slave->bus->slave_ida, slave->index); -@@ -117,9 +120,6 @@ static int sdw_bus_probe(struct device *dev) - if (drv->ops && drv->ops->read_prop) - drv->ops->read_prop(slave); - -- if (slave->prop.use_domain_irq) -- sdw_irq_create_mapping(slave); -- - /* init the dynamic sysfs attributes we need */ - ret = sdw_slave_sysfs_dpn_init(slave); - if (ret < 0) -diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h ---- a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h -+++ b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h -@@ -152,6 +152,10 @@ - #define LPI_MI2S_TX_4 146 - #define SENARY_MI2S_RX 147 - #define SENARY_MI2S_TX 148 -+#define LPI_MI2S_RX_5 149 -+#define LPI_MI2S_TX_5 150 -+#define LPI_MI2S_RX_6 151 -+#define LPI_MI2S_TX_6 152 - - #define LPASS_CLK_ID_PRI_MI2S_IBIT 1 - #define LPASS_CLK_ID_PRI_MI2S_EBIT 2 -@@ -233,11 +237,39 @@ - /* Clock ID for RX CORE MCLK2 2X MCLK */ - #define LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK 70 - -+#define LAPSS_CLK_ID_QAIF_IF0_IBIT 71 -+#define LAPSS_CLK_ID_QAIF_IF0_EBIT 72 -+#define LAPSS_CLK_ID_QAIF_IF1_IBIT 73 -+#define LAPSS_CLK_ID_QAIF_IF1_EBIT 74 -+#define LAPSS_CLK_ID_QAIF_IF2_IBIT 75 -+#define LAPSS_CLK_ID_QAIF_IF2_EBIT 76 -+#define LAPSS_CLK_ID_QAIF_IF3_IBIT 77 -+#define LAPSS_CLK_ID_QAIF_IF3_EBIT 78 -+#define LAPSS_CLK_ID_QAIF_IF4_IBIT 79 -+#define LAPSS_CLK_ID_QAIF_IF4_EBIT 80 -+#define LAPSS_CLK_ID_QAIF_IF5_IBIT 81 -+#define LAPSS_CLK_ID_QAIF_IF5_EBIT 82 -+#define LAPSS_CLK_ID_QAIF_IF6_IBIT 83 -+#define LAPSS_CLK_ID_QAIF_IF6_EBIT 84 -+#define LAPSS_CLK_ID_QAIF_IF7_IBIT 85 -+#define LAPSS_CLK_ID_QAIF_IF7_EBIT 86 -+#define LAPSS_CLK_ID_QAIF_IF8_IBIT 87 -+#define LAPSS_CLK_ID_QAIF_IF8_EBIT 88 -+#define LAPSS_CLK_ID_QAIF_IF9_IBIT 89 -+#define LAPSS_CLK_ID_QAIF_IF9_EBIT 90 -+#define LAPSS_CLK_ID_QAIF_IF10_IBIT 91 -+#define LAPSS_CLK_ID_QAIF_IF10_EBIT 92 -+#define LAPSS_CLK_ID_QAIF_IF11_IBIT 93 -+#define LAPSS_CLK_ID_QAIF_IF11_EBIT 94 -+#define LAPSS_CLK_ID_QAIF_IF12_IBIT 95 -+#define LAPSS_CLK_ID_QAIF_IF12_EBIT 96 -+#define LAPSS_CLK_ID_VA_QAIF_IF0_IBIT 97 -+#define LAPSS_CLK_ID_VA_QAIF_IF0_EBIT 98 -+ - #define LPASS_HW_AVTIMER_VOTE 101 - #define LPASS_HW_MACRO_VOTE 102 - #define LPASS_HW_DCODEC_VOTE 103 -- --#define Q6AFE_MAX_CLK_ID 104 -+#define LPASS_HW_LPR_VOTE 104 - - #define LPASS_CLK_ATTRIBUTE_INVALID 0x0 - #define LPASS_CLK_ATTRIBUTE_COUPLE_NO 0x1 -diff --git a/include/linux/platform_data/omap-twl4030.h b/include/linux/platform_data/omap-twl4030.h -deleted file mode 100644 ---- a/include/linux/platform_data/omap-twl4030.h -+++ /dev/null -@@ -1,42 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/** -- * omap-twl4030.h - ASoC machine driver for TI SoC based boards with twl4030 -- * codec, header. -- * -- * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com -- * All rights reserved. -- * -- * Author: Peter Ujfalusi -- */ -- --#ifndef _OMAP_TWL4030_H_ --#define _OMAP_TWL4030_H_ -- --/* To select if only one channel is connected in a stereo port */ --#define OMAP_TWL4030_LEFT (1 << 0) --#define OMAP_TWL4030_RIGHT (1 << 1) -- --struct omap_tw4030_pdata { -- const char *card_name; -- /* Voice port is connected to McBSP3 */ -- bool voice_connected; -- -- /* The driver will parse the connection flags if this flag is set */ -- bool custom_routing; -- /* Flags to indicate connected audio ports. */ -- u8 has_hs; -- u8 has_hf; -- u8 has_predriv; -- u8 has_carkit; -- bool has_ear; -- -- bool has_mainmic; -- bool has_submic; -- bool has_hsmic; -- bool has_carkitmic; -- bool has_digimic0; -- bool has_digimic1; -- u8 has_linein; --}; -- --#endif /* _OMAP_TWL4030_H_ */ diff --git a/include/sound/ac97/codec.h b/include/sound/ac97/codec.h --- a/include/sound/ac97/codec.h +++ b/include/sound/ac97/codec.h @@ -868,88 +730,6 @@ diff --git a/include/sound/core.h b/include/sound/core.h struct module *module; /* top-level module */ void *private_data; /* private data for soundcard */ -diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h ---- a/include/sound/cs35l56.h -+++ b/include/sound/cs35l56.h -@@ -417,7 +417,6 @@ void cs35l56_wait_control_port_ready(void); - void cs35l56_wait_min_reset_pulse(void); - void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire); - int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq); --irqreturn_t cs35l56_irq(int irq, void *data); - int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base); - int cs35l56_runtime_suspend_common(struct cs35l56_base *cs35l56_base); - int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_soundwire); -diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h ---- a/include/sound/dmaengine_pcm.h -+++ b/include/sound/dmaengine_pcm.h -@@ -175,12 +175,6 @@ int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream *substream, - struct dmaengine_pcm { - struct dma_chan *chan[SNDRV_PCM_STREAM_LAST + 1]; - const struct snd_dmaengine_pcm_config *config; -- struct snd_soc_component component; - unsigned int flags; - }; -- --static inline struct dmaengine_pcm *soc_component_to_pcm(struct snd_soc_component *p) --{ -- return container_of(p, struct dmaengine_pcm, component); --} - #endif -diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h ---- a/include/sound/hda-mlink.h -+++ b/include/sound/hda-mlink.h -@@ -9,6 +9,22 @@ - struct hdac_bus; - struct hdac_ext_link; - -+/** -+ * enum hda_bus_ml_link_type - mlink link type, used by SOF link DMA -+ * allocator constraints (see struct sof_intel_hda_dev). -+ * -+ * @HDA_BUS_ML_LINK_HDA: non-alt link, i.e. HDA codec or iDisp -+ * @HDA_BUS_ML_LINK_SDW: alt link, SoundWire -+ * @HDA_BUS_ML_LINK_UAOL: alt link, USB Audio Offload -+ * @HDA_BUS_ML_LINK_OTHER: alt link, SSP or DMIC -+ */ -+enum hda_bus_ml_link_type { -+ HDA_BUS_ML_LINK_HDA, -+ HDA_BUS_ML_LINK_SDW, -+ HDA_BUS_ML_LINK_UAOL, -+ HDA_BUS_ML_LINK_OTHER, -+}; -+ - #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK) - - int hda_bus_ml_init(struct hdac_bus *bus); -@@ -49,11 +65,12 @@ int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num); - int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, - int channel_mask, int stream_id, int dir); - --void hda_bus_ml_put_all(struct hdac_bus *bus); - void hda_bus_ml_reset_losidv(struct hdac_bus *bus); - int hda_bus_ml_resume(struct hdac_bus *bus); - int hda_bus_ml_suspend(struct hdac_bus *bus); - -+enum hda_bus_ml_link_type hda_bus_ml_link_get_type(struct hdac_ext_link *hlink); -+ - struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus); - struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus); - struct hdac_ext_link *hdac_bus_eml_sdw_get_hlink(struct hdac_bus *bus); -@@ -169,11 +186,13 @@ hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, - return 0; - } - --static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { } - static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { } - static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; } - static inline int hda_bus_ml_suspend(struct hdac_bus *bus) { return 0; } - -+static inline enum hda_bus_ml_link_type -+hda_bus_ml_link_get_type(struct hdac_ext_link *hlink) { return HDA_BUS_ML_LINK_HDA; } -+ - static inline struct hdac_ext_link * - hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus) { return NULL; } - diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -1319,196 +1099,6 @@ diff --git a/include/sound/rawmidi.h b/include/sound/rawmidi.h /* set up the tied devices */ static inline void snd_rawmidi_tie_devices(struct snd_rawmidi *r1, struct snd_rawmidi *r2) -diff --git a/include/sound/sdca_asoc.h b/include/sound/sdca_asoc.h ---- a/include/sound/sdca_asoc.h -+++ b/include/sound/sdca_asoc.h -@@ -25,6 +25,8 @@ struct snd_soc_dai_driver; - struct snd_soc_dai_ops; - struct snd_soc_dapm_route; - struct snd_soc_dapm_widget; -+struct snd_soc_pcm_stream; -+struct sdca_entity; - - /* convenient macro to handle the mono volume in 7.8 fixed format representation */ - #define SDCA_SINGLE_Q78_TLV(xname, xreg, xmin, xmax, xstep, tlv_array) \ -@@ -82,6 +84,11 @@ int sdca_asoc_populate_component(struct device *dev, - struct snd_soc_dai_driver **dai_drv, int *num_dai_drv, - const struct snd_soc_dai_ops *ops); - -+int sdca_asoc_populate_rate_format(struct device *dev, -+ struct sdca_function_data *function, -+ struct sdca_entity *entity, -+ struct snd_soc_pcm_stream *stream); -+ - int sdca_asoc_set_constraints(struct device *dev, struct regmap *regmap, - struct sdca_function_data *function, - struct snd_pcm_substream *substream, -@@ -100,9 +107,9 @@ int sdca_asoc_q78_put_volsw(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); - int sdca_asoc_q78_get_volsw(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); --int sdca_asoc_pde_poll_actual_ps(struct device *dev, struct regmap *regmap, -+int sdca_asoc_pde_poll_actual_ps(struct regmap *regmap, - int function_id, int entity_id, -- int from_ps, int to_ps, -- const struct sdca_pde_delay *pde_delays, -- int num_delays); -+ int from_ps, int to_ps, -+ const struct sdca_pde_delay *pde_delays, -+ int num_delays); - #endif // __SDCA_ASOC_H__ -diff --git a/include/sound/sdca_fdl.h b/include/sound/sdca_fdl.h ---- a/include/sound/sdca_fdl.h -+++ b/include/sound/sdca_fdl.h -@@ -83,6 +83,10 @@ static inline int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt) - return 0; - } - -+static inline void sdca_fdl_free_state(struct sdca_interrupt *interrupt) -+{ -+} -+ - static inline int sdca_fdl_process(struct sdca_interrupt *interrupt) - { - return 0; -diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h ---- a/include/sound/sdca_function.h -+++ b/include/sound/sdca_function.h -@@ -298,7 +298,7 @@ enum sdca_xu_controls { - }; - - /** -- * enum sdca_set_index_range - Column definitions UMP SetIndex -+ * enum sdca_fdl_set_index_range - Column definitions UMP SetIndex - */ - enum sdca_fdl_set_index_range { - SDCA_FDL_SET_INDEX_SET_NUMBER = 0, -@@ -803,6 +803,8 @@ struct sdca_control_range { - * @mode: Access mode of the Control. - * @layers: Bitmask of access layers of the Control. - * @deferrable: Indicates if the access to the Control can be deferred. -+ * @is_volatile: Indicates the Control registers are forced to be treated -+ * as volatile. - * @has_default: Indicates the Control has a default value to be written. - * @has_reset: Indicates the Control has a defined reset value. - * @has_fixed: Indicates the Control only supports a single value. -@@ -1116,7 +1118,6 @@ struct sdca_entity_ge { - - /** - * struct sdca_entity_hide - information specific to HIDE Entities -- * @hid: HID device structure - * @num_hidtx_ids: number of HIDTx Report ID - * @num_hidrx_ids: number of HIDRx Report ID - * @hidtx_ids: HIDTx Report ID -@@ -1127,11 +1128,8 @@ struct sdca_entity_ge { - * within this Device - * @max_delay: the maximum time in microseconds allowed for the Device - * to change the ownership from Device to Host -- * @hid_report_desc: HID Report Descriptor for the HIDE Entity -- * @hid_desc: HID descriptor for the HIDE Entity - */ - struct sdca_entity_hide { -- struct hid_device *hid; - unsigned int *hidtx_ids; - unsigned int *hidrx_ids; - int num_hidtx_ids; -@@ -1139,12 +1137,10 @@ struct sdca_entity_hide { - unsigned int af_number_list[SDCA_MAX_FUNCTION_COUNT]; - unsigned int hide_reside_function_num; - unsigned int max_delay; -- unsigned char *hid_report_desc; -- struct hid_descriptor hid_desc; - }; - - /** -- * enum sdca_xu_reset_machanism - SDCA FDL Resets -+ * enum sdca_xu_reset_mechanism - SDCA FDL Resets - */ - enum sdca_xu_reset_mechanism { - SDCA_XU_RESET_FUNCTION = 0x0, -@@ -1401,6 +1397,16 @@ struct sdca_fdl_data { - int num_sets; - }; - -+/** -+ * struct sdca_function_hid - information about a function's HID descriptors -+ * @report_desc: HID Report Descriptor for the HID Function -+ * @desc: HID descriptor for the HID Function -+ */ -+struct sdca_function_hid { -+ unsigned char *report_desc; -+ struct hid_descriptor desc; -+}; -+ - /** - * struct sdca_function_data - top-level information for one SDCA function - * @desc: Pointer to short descriptor from initial parsing. -@@ -1415,6 +1421,7 @@ struct sdca_fdl_data { - * @reset_max_delay: Maximum Function reset delay in microseconds, before an - * error should be reported. - * @fdl_data: FDL data for this Function, if available. -+ * @hid: HID data for this Function, if available. - */ - struct sdca_function_data { - struct sdca_function_desc *desc; -@@ -1430,6 +1437,10 @@ struct sdca_function_data { - unsigned int reset_max_delay; - - struct sdca_fdl_data fdl_data; -+ -+ union { -+ struct sdca_function_hid hid; -+ }; - }; - - static inline u32 sdca_range(struct sdca_control_range *range, -@@ -1451,8 +1462,7 @@ static inline u32 sdca_range_search(struct sdca_control_range *range, - return 0; - } - --int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, -- struct sdca_function_data *function); -+int sdca_parse_function(struct device *dev, struct sdca_function_data *function); - - const char *sdca_find_terminal_name(enum sdca_terminal_type type); - -@@ -1469,5 +1479,7 @@ struct sdca_control_range *sdca_selector_find_range(struct device *dev, - struct sdca_cluster *sdca_id_find_cluster(struct device *dev, - struct sdca_function_data *function, - const int id); -+struct sdca_entity *sdca_find_entity_by_label(struct sdca_function_data *function, -+ const char *entity_label); - - #endif -diff --git a/include/sound/sdca_hid.h b/include/sound/sdca_hid.h ---- a/include/sound/sdca_hid.h -+++ b/include/sound/sdca_hid.h -@@ -16,18 +16,21 @@ struct sdca_interrupt; - - #if IS_ENABLED(CONFIG_SND_SOC_SDCA_HID) - --int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, -- struct sdca_entity *entity); -+int sdca_add_hid_device(struct sdca_interrupt *interrupt); -+void sdca_destroy_hid_device(struct sdca_interrupt *interrupt); - int sdca_hid_process_report(struct sdca_interrupt *interrupt); - - #else - --static inline int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, -- struct sdca_entity *entity) -+static inline int sdca_add_hid_device(struct sdca_interrupt *interrupt) - { - return 0; - } - -+static inline void sdca_destroy_hid_device(struct sdca_interrupt *interrupt) -+{ -+} -+ - static inline int sdca_hid_process_report(struct sdca_interrupt *interrupt) - { - return 0; diff --git a/include/sound/seq_virmidi.h b/include/sound/seq_virmidi.h --- a/include/sound/seq_virmidi.h +++ b/include/sound/seq_virmidi.h @@ -1520,571 +1110,6 @@ diff --git a/include/sound/seq_virmidi.h b/include/sound/seq_virmidi.h struct rw_semaphore filelist_sem; struct list_head filelist; }; -diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h ---- a/include/sound/simple_card_utils.h -+++ b/include/sound/simple_card_utils.h -@@ -181,7 +181,7 @@ void simple_util_canonicalize_platform(struct snd_soc_dai_link_component *platfo - void simple_util_canonicalize_cpu(struct snd_soc_dai_link_component *cpus, - int is_single_links); - --void simple_util_clean_reference(struct snd_soc_card *card); -+void simple_util_clean_reference(struct simple_util_priv *priv); - - void simple_util_parse_convert(struct device_node *np, char *prefix, - struct simple_util_data *data); -@@ -189,18 +189,37 @@ bool simple_util_is_convert_required(const struct simple_util_data *data); - - int simple_util_get_sample_fmt(struct simple_util_data *data); - --int simple_util_parse_routing(struct snd_soc_card *card, -- char *prefix); --int simple_util_parse_widgets(struct snd_soc_card *card, -- char *prefix); --int simple_util_parse_pin_switches(struct snd_soc_card *card, -- char *prefix); -+int simple_util_parse_property(struct simple_util_priv *priv, -+ int (*func)(struct snd_soc_card *card, const char *propname), -+ char *prefix, char *property); -+static inline int simple_util_parse_routing(struct simple_util_priv *priv, char *prefix) -+{ -+ return simple_util_parse_property(priv, snd_soc_of_parse_audio_routing, -+ prefix, "routing"); -+} -+ -+static inline int simple_util_parse_widgets(struct simple_util_priv *priv, char *prefix) -+{ -+ return simple_util_parse_property(priv, snd_soc_of_parse_audio_simple_widgets, -+ prefix, "widgets"); -+} -+ -+static inline int simple_util_parse_pin_switches(struct simple_util_priv *priv, char *prefix) -+{ -+ return simple_util_parse_property(priv, snd_soc_of_parse_pin_switches, -+ prefix, "pin-switches"); -+} -+ -+static inline int simple_util_parse_aux_devs(struct simple_util_priv *priv, char *prefix) -+{ -+ return simple_util_parse_property(priv, snd_soc_of_parse_aux_devs, -+ prefix, "aux-devs"); -+} - - int simple_util_init_jack(struct snd_soc_card *card, - struct simple_util_jack *sjack, - int is_hp, char *prefix, char *pin); --int simple_util_init_aux_jacks(struct simple_util_priv *priv, -- char *prefix); -+int simple_util_init_aux_jacks(struct snd_soc_card *card, char *prefix); - int simple_util_init_priv(struct simple_util_priv *priv, - struct link_info *li); - void simple_util_remove(struct platform_device *pdev); -diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h ---- a/include/sound/soc-acpi.h -+++ b/include/sound/soc-acpi.h -@@ -57,7 +57,7 @@ static inline struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg) - #endif - - /** -- * snd_soc_acpi_mach_params: interface for machine driver configuration -+ * struct snd_soc_acpi_mach_params - interface for machine driver configuration - * - * @acpi_ipc_irq_index: used for BYT-CR detection - * @platform: string used for HDAudio codec support -@@ -93,7 +93,7 @@ struct snd_soc_acpi_mach_params { - }; - - /** -- * snd_soc_acpi_endpoint - endpoint descriptor -+ * struct snd_soc_acpi_endpoint - endpoint descriptor - * @num: endpoint number (mandatory, unique per device) - * @aggregated: 0 (independent) or 1 (logically grouped) - * @group_position: zero-based order (only when @aggregated is 1) -@@ -107,7 +107,7 @@ struct snd_soc_acpi_endpoint { - }; - - /** -- * snd_soc_acpi_adr_device - descriptor for _ADR-enumerated device -+ * struct snd_soc_acpi_adr_device - descriptor for _ADR-enumerated device - * @adr: 64 bit ACPI _ADR value - * @num_endpoints: number of endpoints for this device - * @endpoints: array of endpoints -@@ -121,7 +121,7 @@ struct snd_soc_acpi_adr_device { - }; - - /** -- * snd_soc_acpi_link_adr - ACPI-based list of _ADR enumerated devices -+ * struct snd_soc_acpi_link_adr - ACPI-based list of _ADR enumerated devices - * @mask: one bit set indicates the link this list applies to - * @num_adr: ARRAY_SIZE of devices - * @adr_d: array of devices -@@ -167,8 +167,8 @@ struct snd_soc_acpi_link_adr { - #define SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME BIT(4) - - /** -- * snd_soc_acpi_mach: ACPI-based machine descriptor. Most of the fields are -- * related to the hardware, except for the firmware and topology file names. -+ * struct snd_soc_acpi_mach - ACPI-based machine descriptor. Most of the fields -+ * are related to the hardware, except for the firmware and topology file names. - * A platform supported by legacy and Sound Open Firmware (SOF) would expose - * all firmware/topology related fields. - * -@@ -192,6 +192,7 @@ struct snd_soc_acpi_link_adr { - * the initial selection in the snd_soc_acpi_mach table. - * @pdata: intended for platform data or machine specific-ops. This structure - * is not constant since this field may be updated at run-time -+ * @mach_params: machine driver configuration - * @sof_tplg_filename: Sound Open Firmware topology file name, if enabled - * @tplg_quirk_mask: quirks to select different topology files dynamically - * @get_function_tplg_files: This is an optional callback, if specified then instead of -@@ -199,11 +200,11 @@ struct snd_soc_acpi_link_adr { - * files to be loaded. - * Return value: The number of the files or negative ERRNO. 0 means that the single topology - * file should be used, no function topology split can be used on the machine. -- * @card: the pointer of the card -- * @mach: the pointer of the machine driver -- * @prefix: the prefix of the topology file name. Typically, it is the path. -- * @tplg_files: the pointer of the array of the topology file names. -- * @best_effort: ignore non supported links and try to build the card in best effort -+ * card: the pointer of the card -+ * mach: the pointer of the machine driver -+ * prefix: the prefix of the topology file name. Typically, it is the path. -+ * tplg_files: the pointer of the array of the topology file names. -+ * best_effort: ignore non supported links and try to build the card in best effort - * with supported links - */ - /* Descriptor for SST ASoC machine driver */ -diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h ---- a/include/sound/soc-component.h -+++ b/include/sound/soc-component.h -@@ -253,6 +253,9 @@ struct snd_soc_component { - void *mark_pm; - - struct dentry *debugfs_root; -+ -+ /* Component private data */ -+ void *priv; - }; - - #define for_each_component_dais(component, dai)\ -@@ -283,6 +286,14 @@ static inline int snd_soc_component_cache_sync( - return regcache_sync(component->regmap); - } - -+struct snd_soc_component *snd_soc_component_alloc(struct device *dev); -+ -+void snd_soc_component_set_name(struct snd_soc_component *component, const char *name); -+const char *snd_soc_component_name(struct snd_soc_component *component); -+ -+void snd_soc_component_set_priv(struct snd_soc_component *component, void *priv); -+void *snd_soc_component_to_priv(struct snd_soc_component *component); -+ - void snd_soc_component_set_aux(struct snd_soc_component *component, - struct snd_soc_aux_dev *aux); - int snd_soc_component_init(struct snd_soc_component *component); -diff --git a/include/sound/soc.h b/include/sound/soc.h ---- a/include/sound/soc.h -+++ b/include/sound/soc.h -@@ -447,15 +447,16 @@ static inline int snd_soc_resume(struct device *dev) - } - #endif - int snd_soc_poweroff(struct device *dev); --int snd_soc_component_initialize(struct snd_soc_component *component, -- const struct snd_soc_component_driver *driver, -- struct device *dev); --int snd_soc_add_component(struct snd_soc_component *component, -- struct snd_soc_dai_driver *dai_drv, -- int num_dai); --int snd_soc_register_component(struct device *dev, -+int snd_soc_register_component_c(struct snd_soc_component *component, - const struct snd_soc_component_driver *component_driver, - struct snd_soc_dai_driver *dai_drv, int num_dai); -+int snd_soc_register_component_d(struct device *dev, -+ const struct snd_soc_component_driver *component_driver, -+ struct snd_soc_dai_driver *dai_drv, int num_dai); -+#define snd_soc_register_component(x, ...) _Generic((x), \ -+struct device * : snd_soc_register_component_d, \ -+struct snd_soc_component * : snd_soc_register_component_c)(x, __VA_ARGS__) -+ - int devm_snd_soc_register_component(struct device *dev, - const struct snd_soc_component_driver *component_driver, - struct snd_soc_dai_driver *dai_drv, int num_dai); -diff --git a/include/sound/soc_sdw_utils.h b/include/sound/soc_sdw_utils.h ---- a/include/sound/soc_sdw_utils.h -+++ b/include/sound/soc_sdw_utils.h -@@ -44,6 +44,18 @@ - - struct asoc_sdw_codec_info; - -+struct asoc_sdw_mc_private { -+ struct snd_soc_card card; -+ struct snd_soc_jack sdw_headset; -+ struct device *headset_codec_dev; /* only one headset per card */ -+ struct device *amp_dev1, *amp_dev2; -+ bool append_dai_type; -+ bool ignore_internal_dmic; -+ void *private; -+ unsigned long mc_quirk; -+ int codec_info_list_count; -+}; -+ - struct asoc_sdw_dai_info { - const bool direction[2]; /* playback & capture support */ - const char *codec_name; -@@ -88,25 +100,13 @@ struct asoc_sdw_codec_info { - - int (*codec_card_late_probe)(struct snd_soc_card *card); - -- int (*count_sidecar)(struct snd_soc_card *card, -+ int (*count_sidecar)(struct asoc_sdw_mc_private *ctx, - int *num_dais, int *num_devs); - int (*add_sidecar)(struct snd_soc_card *card, - struct snd_soc_dai_link **dai_links, - struct snd_soc_codec_conf **codec_conf); - }; - --struct asoc_sdw_mc_private { -- struct snd_soc_card card; -- struct snd_soc_jack sdw_headset; -- struct device *headset_codec_dev; /* only one headset per card */ -- struct device *amp_dev1, *amp_dev2; -- bool append_dai_type; -- bool ignore_internal_dmic; -- void *private; -- unsigned long mc_quirk; -- int codec_info_list_count; --}; -- - struct asoc_sdw_endpoint { - struct list_head list; - -@@ -182,7 +182,8 @@ struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks - const struct snd_soc_acpi_endpoint *new); - int asoc_sdw_get_dai_type(u32 type); - --int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, -+int asoc_sdw_parse_sdw_endpoints(struct device *dev, -+ struct asoc_sdw_mc_private *ctx, - struct snd_soc_aux_dev *soc_aux, - struct asoc_sdw_dailink *soc_dais, - struct asoc_sdw_endpoint *soc_ends, -@@ -235,7 +236,7 @@ int asoc_sdw_es9356_amp_init(struct snd_soc_card *card, - int asoc_sdw_es9356_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); - - /* CS AMP support */ --int asoc_sdw_bridge_cs35l56_count_sidecar(struct snd_soc_card *card, -+int asoc_sdw_bridge_cs35l56_count_sidecar(struct asoc_sdw_mc_private *ctx, - int *num_dais, int *num_devs); - int asoc_sdw_bridge_cs35l56_add_sidecar(struct snd_soc_card *card, - struct snd_soc_dai_link **dai_links, -diff --git a/include/sound/sof/dai-amd.h b/include/sound/sof/dai-amd.h ---- a/include/sound/sof/dai-amd.h -+++ b/include/sound/sof/dai-amd.h -@@ -18,6 +18,7 @@ struct sof_ipc_dai_acp_params { - uint32_t fsync_rate; /* FSYNC frequency in Hz */ - uint32_t tdm_slots; - uint32_t tdm_mode; -+ uint32_t format; - } __packed; - - /* ACPDMIC Configuration Request - SOF_IPC_DAI_AMD_CONFIG */ -diff --git a/include/sound/sof/dai.h b/include/sound/sof/dai.h ---- a/include/sound/sof/dai.h -+++ b/include/sound/sof/dai.h -@@ -90,6 +90,8 @@ enum sof_ipc_dai_type { - SOF_DAI_AMD_HS_VIRTUAL, /**< AMD ACP HS VIRTUAL */ - SOF_DAI_IMX_MICFIL, /** < i.MX MICFIL PDM */ - SOF_DAI_AMD_SDW, /**< AMD ACP SDW */ -+ SOF_DAI_INTEL_UAOL, /**< Intel UAOL */ -+ SOF_DAI_AMD_I2S, /**< AMD ACP I2S */ - }; - - /* general purpose DAI configuration */ -@@ -121,6 +123,7 @@ struct sof_ipc_dai_config { - struct sof_ipc_dai_mtk_afe_params afe; - struct sof_ipc_dai_micfil_params micfil; - struct sof_ipc_dai_acp_sdw_params acp_sdw; -+ struct sof_ipc_dai_acp_params acp_i2s; - }; - } __packed; - -diff --git a/include/sound/sof/header.h b/include/sound/sof/header.h ---- a/include/sound/sof/header.h -+++ b/include/sound/sof/header.h -@@ -179,7 +179,7 @@ struct sof_ipc_compound_hdr { - uint32_t count; /**< count of 0 means end of compound sequence */ - } __packed; - --/** -+/* - * OOPS header architecture specific data. - */ - struct sof_ipc_dsp_oops_arch_hdr { -@@ -187,7 +187,7 @@ struct sof_ipc_dsp_oops_arch_hdr { - uint32_t totalsize; /* Total size of oops message */ - } __packed; - --/** -+/* - * OOPS header platform specific data. - */ - struct sof_ipc_dsp_oops_plat_hdr { -diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h ---- a/include/sound/sof/ipc4/header.h -+++ b/include/sound/sof/ipc4/header.h -@@ -187,6 +187,10 @@ enum sof_ipc4_pipeline_state { - #define SOF_IPC4_GLB_PIPE_EXT_CORE_ID_MASK GENMASK(23, 20) - #define SOF_IPC4_GLB_PIPE_EXT_CORE_ID(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_CORE_ID_SHIFT) - -+#define SOF_IPC4_GLB_PIPE_PAYLOAD_SHIFT 29 -+#define SOF_IPC4_GLB_PIPE_PAYLOAD_MASK BIT(29) -+#define SOF_IPC4_GLB_PIPE_PAYLOAD(x) ((x) << SOF_IPC4_GLB_PIPE_PAYLOAD_SHIFT) -+ - /* pipeline set state ipc msg */ - #define SOF_IPC4_GLB_PIPE_STATE_ID_SHIFT 16 - #define SOF_IPC4_GLB_PIPE_STATE_ID_MASK GENMASK(23, 16) -@@ -536,12 +540,63 @@ enum sof_ipc4_notification_type { - SOF_IPC4_NOTIFY_TYPE_LAST, - }; - -+enum sof_ipc4_resource_type { -+ SOF_IPC4_MODULE_INSTANCE, -+ SOF_IPC4_PIPELINE, -+ SOF_IPC4_GATEWAY, -+ SOF_IPC4_EDF_TASK, -+ SOF_IPC4_INVALID_RESOURCE_TYPE, -+}; -+ -+enum sof_ipc4_event_type { -+ /* Underrun detected by the Mixer */ -+ SOF_IPC4_MIXER_UNDERRUN_DETECTED = 1, -+ /* Error caught during data processing */ -+ SOF_IPC4_PROCESS_DATA_ERROR = 3, -+ /* Underrun detected by gateway. */ -+ SOF_IPC4_GATEWAY_UNDERRUN_DETECTED = 6, -+ /* Overrun detected by gateway */ -+ SOF_IPC4_GATEWAY_OVERRUN_DETECTED, -+}; -+ -+/** -+ * struct sof_ipc4_process_data_error_event_data - process data error event payload -+ * @error_code: Error code returned by data processing function -+ */ -+struct sof_ipc4_process_data_error_event_data { -+ uint32_t error_code; -+}; -+ -+/** -+ * struct sof_ipc4_mixer_underrun_event_data - mixer underrun event payload -+ * @eos_flag: Indicates EndOfStream -+ * @data_mixed: Data processed by module (in bytes) -+ * @expected_data_mixed: Expected data to be processed (in bytes) -+ */ -+struct sof_ipc4_mixer_underrun_event_data { -+ uint32_t eos_flag; -+ uint32_t data_mixed; -+ uint32_t expected_data_mixed; -+}; -+ -+/** -+ * union sof_ipc4_resource_event_data - resource event specific payload -+ * @dws: Raw event data payload as six dwords -+ * @process_data_error: SOF_IPC4_PROCESS_DATA_ERROR payload -+ * @mixer_underrun: SOF_IPC4_MIXER_UNDERRUN_DETECTED payload -+ */ -+union sof_ipc4_resource_event_data { -+ uint32_t dws[6]; -+ struct sof_ipc4_process_data_error_event_data process_data_error; -+ struct sof_ipc4_mixer_underrun_event_data mixer_underrun; -+}; -+ - struct sof_ipc4_notify_resource_data { - uint32_t resource_type; - uint32_t resource_id; - uint32_t event_type; - uint32_t reserved; -- uint32_t data[6]; -+ union sof_ipc4_resource_event_data data; - } __packed __aligned(4); - - #define SOF_IPC4_DEBUG_DESCRIPTOR_SIZE 12 /* 3 x u32 */ -@@ -654,11 +709,81 @@ enum sof_ipc4_mod_init_ext_obj_id { - SOF_IPC4_MOD_INIT_DATA_ID_MAX = SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA, - }; - --/* DP module memory configuration data object for ext_init object array */ -+/* DP module memory configuration data object for object array */ - struct sof_ipc4_mod_init_ext_dp_memory_data { -+ u32 domain_id; /* userspace domain ID */ -+ u32 stack_bytes; /* required stack size in bytes */ -+ u32 heap_bytes; /* required heap size in bytes */ -+} __packed __aligned(4); -+ -+/* -+ * This set of macros are very similar to the set above, but these are -+ * for building payload to SOF_IPC4_GLB_CREATE_PIPELINE message. -+ * -+ * Macros for creating struct sof_ipc4_glb_pipe_payload payload with -+ * its associated data. struct sof_ipc4_glb_pipe_payload should be the -+ * first piece of payload following SOF_IPC4_GLB_CREATE_PIPELINE msg, -+ * and its existence is indicated with SOF_IPC4_GLB_PIPE_PAYLOAD bit. -+ * -+ * The macros below apply to sof_ipc4_glb_pipe_payload.word0 -+ */ -+#define SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS_SHIFT 0 -+#define SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS_MASK GENMASK(23, 0) -+#define SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS(x) ((x) << SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS_SHIFT) -+ -+#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_SHIFT 24 -+#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_MASK BIT(24) -+#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_SHIFT) -+ -+struct sof_ipc4_glb_pipe_payload { -+ u32 word0; -+ u32 rsvd1; -+ u32 rsvd2; -+} __packed __aligned(4); -+ -+/* -+ * SOF_IPC4_GLB_CREATE_PIPELINE payload may be followed by arbitrary -+ * number of object array objects. SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY-bit -+ * indicates that an array object follows struct -+ * sof_ipc4_glb_pipe_payload. -+ * -+ * The object header's SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST-bit in struct -+ * sof_ipc4_glb_pipe_ext_object indicates if the array is continued -+ * with another object. The header has also fields to identify the -+ * object, SOF_IPC4_GLB_PIPE_EXT_OBJ_ID, and to indicate the object's -+ * size in 32-bit words, SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS, not -+ * including the header itself. -+ * -+ * The macros below apply to sof_ipc4_glb_pipe_ext_object.header -+ */ -+#define SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_SHIFT 0 -+#define SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_MASK BIT(0) -+#define SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_SHIFT) -+ -+#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ID_SHIFT 1 -+#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ID_MASK GENMASK(15, 1) -+#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ID(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_ID_SHIFT) -+ -+#define SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS_SHIFT 16 -+#define SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS_MASK GENMASK(31, 16) -+#define SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS_SHIFT) -+ -+struct sof_ipc4_glb_pipe_ext_object { -+ u32 header; -+ u32 data[]; -+} __packed __aligned(4); -+ -+enum sof_ipc4_glb_pipe_ext_obj_id { -+ SOF_IPC4_GLB_PIPE_DATA_ID_INVALID = 0, -+ SOF_IPC4_GLB_PIPE_DATA_ID_MEM_DATA, -+ SOF_IPC4_GLB_PIPE_DATA_ID_MAX = SOF_IPC4_GLB_PIPE_DATA_ID_MEM_DATA, -+}; -+ -+/* Pipeline memory configuration data object for ext_init object array */ -+struct sof_ipc4_glb_pipe_ext_obj_memory_data { - u32 domain_id; /* userspace domain ID */ -- u32 stack_bytes; /* stack size in bytes, 0 means default size */ -- u32 heap_bytes; /* stack size in bytes, 0 means default size */ -+ u32 stack_bytes; /* stack size in bytes */ -+ u32 heap_bytes; /* heap size in bytes */ - } __packed __aligned(4); - - /** @}*/ -diff --git a/include/sound/tas2781-dsp.h b/include/sound/tas2781-dsp.h ---- a/include/sound/tas2781-dsp.h -+++ b/include/sound/tas2781-dsp.h -@@ -201,6 +201,11 @@ struct tasdevice_rca { - int ncfgs; - struct tasdevice_config_info **cfg_info; - int profile_cfg_id; -+ /* -+ * Used among SmartAMP for PDM microphone recording or IV data -+ * capture. -+ */ -+ int capture_profile_id; - /* - * Since version 0x105, the keyword 'init' was introduced into the - * profile, which is used for chip initialization, particularly to -@@ -222,7 +227,7 @@ void tasdevice_calbin_remove(void *context); - int tasdevice_select_tuningprm_cfg(void *context, int prm, - int cfg_no, int rca_conf_no); - int tasdevice_prmg_load(void *context, int prm_no); --void tasdevice_tuning_switch(void *context, int state); -+void tasdevice_tuning_switch(void *context, int state, bool is_cap); - int tas2781_load_calibration(void *context, char *file_name, - unsigned short i); - -diff --git a/include/sound/tlv320aic32x4.h b/include/sound/tlv320aic32x4.h -deleted file mode 100644 ---- a/include/sound/tlv320aic32x4.h -+++ /dev/null -@@ -1,43 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * tlv320aic32x4.h -- TLV320AIC32X4 Soc Audio driver platform data -- * -- * Copyright 2011 Vista Silicon S.L. -- * -- * Author: Javier Martin -- */ -- --#ifndef _AIC32X4_PDATA_H --#define _AIC32X4_PDATA_H -- --#define AIC32X4_PWR_MICBIAS_2075_LDOIN 0x00000001 --#define AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE 0x00000002 --#define AIC32X4_PWR_AIC32X4_LDO_ENABLE 0x00000004 --#define AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36 0x00000008 --#define AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED 0x00000010 -- --#define AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K 0x00000001 --#define AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K 0x00000002 -- --/* GPIO API */ --#define AIC32X4_MFPX_DEFAULT_VALUE 0xff -- --#define AIC32X4_MFP1_DIN_DISABLED 0 --#define AIC32X4_MFP1_DIN_ENABLED 0x2 --#define AIC32X4_MFP1_GPIO_IN 0x4 -- --#define AIC32X4_MFP2_GPIO_OUT_LOW 0x0 --#define AIC32X4_MFP2_GPIO_OUT_HIGH 0x1 -- --#define AIC32X4_MFP_GPIO_ENABLED 0x4 -- --#define AIC32X4_MFP5_GPIO_DISABLED 0x0 --#define AIC32X4_MFP5_GPIO_INPUT 0x8 --#define AIC32X4_MFP5_GPIO_OUTPUT 0xc --#define AIC32X4_MFP5_GPIO_OUT_LOW 0x0 --#define AIC32X4_MFP5_GPIO_OUT_HIGH 0x1 -- --struct aic32x4_setup_data { -- unsigned int gpio_func[5]; --}; --#endif -diff --git a/include/sound/wm8904.h b/include/sound/wm8904.h ---- a/include/sound/wm8904.h -+++ b/include/sound/wm8904.h -@@ -116,7 +116,7 @@ - #define WM8904_DRC_REGS 4 - #define WM8904_EQ_REGS 24 - --/** -+/* - * DRC configurations are specified with a label and a set of register - * values to write (the enable bits will be ignored). At runtime an - * enumerated control will be presented for each DRC block allowing -@@ -131,7 +131,7 @@ struct wm8904_drc_cfg { - u16 regs[WM8904_DRC_REGS]; - }; - --/** -+/* - * ReTune Mobile configurations are specified with a label, sample - * rate and set of values to write (the enable bits will be ignored). - * diff --git a/include/uapi/sound/asequencer.h b/include/uapi/sound/asequencer.h --- a/include/uapi/sound/asequencer.h +++ b/include/uapi/sound/asequencer.h @@ -2445,649 +1470,6 @@ diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h * expected to be linear value (not logarithm) for audio signal level between 0.0 and +1.0. */ struct snd_firewire_motu_command_dsp_meter { -diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h -deleted file mode 100644 ---- a/include/uapi/sound/skl-tplg-interface.h -+++ /dev/null -@@ -1,168 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ --/* -- * skl-tplg-interface.h - Intel DSP FW private data interface -- * -- * Copyright (C) 2015 Intel Corp -- * Author: Jeeja KP -- * Nilofer, Samreen -- */ -- --#ifndef __HDA_TPLG_INTERFACE_H__ --#define __HDA_TPLG_INTERFACE_H__ -- --#include -- --/* -- * Default types range from 0~12. type can range from 0 to 0xff -- * SST types start at higher to avoid any overlapping in future -- */ --#define SKL_CONTROL_TYPE_BYTE_TLV 0x100 --#define SKL_CONTROL_TYPE_MIC_SELECT 0x102 --#define SKL_CONTROL_TYPE_MULTI_IO_SELECT 0x103 --#define SKL_CONTROL_TYPE_MULTI_IO_SELECT_DMIC 0x104 -- --#define HDA_SST_CFG_MAX 900 /* size of copier cfg*/ --#define MAX_IN_QUEUE 8 --#define MAX_OUT_QUEUE 8 -- --#define SKL_UUID_STR_SZ 40 --/* Event types goes here */ --/* Reserve event type 0 for no event handlers */ --enum skl_event_types { -- SKL_EVENT_NONE = 0, -- SKL_MIXER_EVENT, -- SKL_MUX_EVENT, -- SKL_VMIXER_EVENT, -- SKL_PGA_EVENT --}; -- --/** -- * enum skl_ch_cfg - channel configuration -- * -- * @SKL_CH_CFG_MONO: One channel only -- * @SKL_CH_CFG_STEREO: L & R -- * @SKL_CH_CFG_2_1: L, R & LFE -- * @SKL_CH_CFG_3_0: L, C & R -- * @SKL_CH_CFG_3_1: L, C, R & LFE -- * @SKL_CH_CFG_QUATRO: L, R, Ls & Rs -- * @SKL_CH_CFG_4_0: L, C, R & Cs -- * @SKL_CH_CFG_5_0: L, C, R, Ls & Rs -- * @SKL_CH_CFG_5_1: L, C, R, Ls, Rs & LFE -- * @SKL_CH_CFG_DUAL_MONO: One channel replicated in two -- * @SKL_CH_CFG_I2S_DUAL_STEREO_0: Stereo(L,R) in 4 slots, 1st stream:[ L, R, -, - ] -- * @SKL_CH_CFG_I2S_DUAL_STEREO_1: Stereo(L,R) in 4 slots, 2nd stream:[ -, -, L, R ] -- * @SKL_CH_CFG_INVALID: Invalid -- */ --enum skl_ch_cfg { -- SKL_CH_CFG_MONO = 0, -- SKL_CH_CFG_STEREO = 1, -- SKL_CH_CFG_2_1 = 2, -- SKL_CH_CFG_3_0 = 3, -- SKL_CH_CFG_3_1 = 4, -- SKL_CH_CFG_QUATRO = 5, -- SKL_CH_CFG_4_0 = 6, -- SKL_CH_CFG_5_0 = 7, -- SKL_CH_CFG_5_1 = 8, -- SKL_CH_CFG_DUAL_MONO = 9, -- SKL_CH_CFG_I2S_DUAL_STEREO_0 = 10, -- SKL_CH_CFG_I2S_DUAL_STEREO_1 = 11, -- SKL_CH_CFG_7_1 = 12, -- SKL_CH_CFG_4_CHANNEL = SKL_CH_CFG_7_1, -- SKL_CH_CFG_INVALID --}; -- --enum skl_module_type { -- SKL_MODULE_TYPE_MIXER = 0, -- SKL_MODULE_TYPE_COPIER, -- SKL_MODULE_TYPE_UPDWMIX, -- SKL_MODULE_TYPE_SRCINT, -- SKL_MODULE_TYPE_ALGO, -- SKL_MODULE_TYPE_BASE_OUTFMT, -- SKL_MODULE_TYPE_KPB, -- SKL_MODULE_TYPE_MIC_SELECT, --}; -- --enum skl_core_affinity { -- SKL_AFFINITY_CORE_0 = 0, -- SKL_AFFINITY_CORE_1, -- SKL_AFFINITY_CORE_MAX --}; -- --enum skl_pipe_conn_type { -- SKL_PIPE_CONN_TYPE_NONE = 0, -- SKL_PIPE_CONN_TYPE_FE, -- SKL_PIPE_CONN_TYPE_BE --}; -- --enum skl_hw_conn_type { -- SKL_CONN_NONE = 0, -- SKL_CONN_SOURCE = 1, -- SKL_CONN_SINK = 2 --}; -- --enum skl_dev_type { -- SKL_DEVICE_BT = 0x0, -- SKL_DEVICE_DMIC = 0x1, -- SKL_DEVICE_I2S = 0x2, -- SKL_DEVICE_SLIMBUS = 0x3, -- SKL_DEVICE_HDALINK = 0x4, -- SKL_DEVICE_HDAHOST = 0x5, -- SKL_DEVICE_NONE --}; -- --/** -- * enum skl_interleaving - interleaving style -- * -- * @SKL_INTERLEAVING_PER_CHANNEL: [s1_ch1...s1_chN,...,sM_ch1...sM_chN] -- * @SKL_INTERLEAVING_PER_SAMPLE: [s1_ch1...sM_ch1,...,s1_chN...sM_chN] -- */ --enum skl_interleaving { -- SKL_INTERLEAVING_PER_CHANNEL = 0, -- SKL_INTERLEAVING_PER_SAMPLE = 1, --}; -- --enum skl_sample_type { -- SKL_SAMPLE_TYPE_INT_MSB = 0, -- SKL_SAMPLE_TYPE_INT_LSB = 1, -- SKL_SAMPLE_TYPE_INT_SIGNED = 2, -- SKL_SAMPLE_TYPE_INT_UNSIGNED = 3, -- SKL_SAMPLE_TYPE_FLOAT = 4 --}; -- --enum module_pin_type { -- /* All pins of the module takes same PCM inputs or outputs -- * e.g. mixout -- */ -- SKL_PIN_TYPE_HOMOGENEOUS, -- /* All pins of the module takes different PCM inputs or outputs -- * e.g mux -- */ -- SKL_PIN_TYPE_HETEROGENEOUS, --}; -- --enum skl_module_param_type { -- SKL_PARAM_DEFAULT = 0, -- SKL_PARAM_INIT, -- SKL_PARAM_SET, -- SKL_PARAM_BIND --}; -- --struct skl_dfw_algo_data { -- __u32 set_params:2; -- __u32 rsvd:30; -- __u32 param_id; -- __u32 max; -- char params[]; --} __packed; -- --enum skl_tkn_dir { -- SKL_DIR_IN, -- SKL_DIR_OUT --}; -- --enum skl_tuple_type { -- SKL_TYPE_TUPLE, -- SKL_TYPE_DATA --}; -- --#endif -diff --git a/include/uapi/sound/snd_ar_tokens.h b/include/uapi/sound/snd_ar_tokens.h ---- a/include/uapi/sound/snd_ar_tokens.h -+++ b/include/uapi/sound/snd_ar_tokens.h -@@ -58,7 +58,7 @@ enum ar_event_types { - #define SND_SOC_AR_TPLG_FE_BE_GRAPH_CTL_MIX 256 - #define SND_SOC_AR_TPLG_VOL_CTL 257 - --/** -+/* - * %AR_TKN_U32_SUB_GRAPH_INSTANCE_ID: Sub Graph Instance Id - * - * %AR_TKN_U32_SUB_GRAPH_PERF_MODE: Performance mode of subgraph -@@ -168,6 +168,60 @@ enum ar_event_types { - * LOG_WAIT = 0, - * LOG_IMMEDIATELY = 1 - * -+ * %AR_TKN_U16_MODULE_SYNC_SRC: Frame sync source -+ * AR_AUDIO_IF_SYNC_SRC_EXTERNAL = 0, -+ * AR_AUDIO_IF_SYNC_SRC_INTERNAL = 1 -+ * -+ * %AR_TKN_U16_MODULE_CTRL_DATA_OUT_ENABLE: Enable data-out tri-state control -+ * AR_AUDIO_IF_CTRL_DATA_OE_DISABLE = 0, -+ * AR_AUDIO_IF_CTRL_DATA_OE_ENABLE = 1 -+ * -+ * %AR_TKN_U32_MODULE_SLOT_MASK: Active TDM slot bitmask -+ * -+ * %AR_TKN_U16_MODULE_NSLOTS_PER_FRAME: Number of slots per TDM frame -+ * -+ * %AR_TKN_U16_MODULE_SLOT_WIDTH: Slot width in bits (16 or 32) -+ * -+ * %AR_TKN_U16_MODULE_SYNC_MODE: Frame sync mode -+ * AR_AUDIO_IF_FRAME_SYNC_MODE_SHORT = 0, -+ * AR_AUDIO_IF_FRAME_SYNC_MODE_ONE_SLOT = 1, -+ * AR_AUDIO_IF_FRAME_SYNC_MODE_LONG = 2 -+ * -+ * %AR_TKN_U16_MODULE_CTRL_INVERT_SYNC_PULSE: Invert frame sync pulse polarity -+ * AR_AUDIO_IF_SYNC_NORMAL = 0, -+ * AR_AUDIO_IF_SYNC_INVERTED = 1 -+ * -+ * %AR_TKN_U16_MODULE_CTRL_SYNC_DATA_DELAY: Data delay relative to frame sync -+ * AR_AUDIO_IF_DATA_DELAY_NONE = 0, -+ * AR_AUDIO_IF_DATA_DELAY_1_CYCLE = 1, -+ * AR_AUDIO_IF_DATA_DELAY_2_CYCLE = 2 -+ * -+ * %AR_TKN_U16_MODULE_INTF_MODE: Audio IF interface mode -+ * AR_AUDIO_IF_INTF_MODE_TDM = 0, -+ * AR_AUDIO_IF_INTF_MODE_PCM = 1, -+ * AR_AUDIO_IF_INTF_MODE_I2S = 2 -+ * -+ * %AR_TKN_U16_MODULE_QAIF_TYPE: QAIF hardware port type index -+ * AR_AUDIO_IF_QAIF = 0, -+ * AR_AUDIO_IF_QAIF_VA = 1 -+ * -+ * %AR_TKN_U32_MODULE_ACTIVE_LANE_MASK: Active lane bitmask for multi-lane -+ * -+ * %AR_TKN_U32_MODULE_FRAME_SYNC_RATE: Frame sync rate in Hz -+ * -+ * %AR_TKN_U16_MODULE_BIT_CLK_TYPE: Bit clock type -+ * AR_AUDIO_IF_BIT_CLK_INTERNAL = 0, -+ * AR_AUDIO_IF_BIT_CLK_EXTERNAL = 1, -+ * AR_AUDIO_IF_BIT_CLK_SKIP = 2 -+ * -+ * %AR_TKN_U8_MODULE_INV_INT_BIT_CLK: Invert internal bit clock -+ * AR_AUDIO_IF_CLK_NORMAL = 0, -+ * AR_AUDIO_IF_CLK_INVERTED = 1 -+ * -+ * %AR_TKN_U8_MODULE_INV_EXT_BIT_CLK: Invert external bit clock -+ * AR_AUDIO_IF_CLK_NORMAL = 0, -+ * AR_AUDIO_IF_CLK_INVERTED = 1 -+ * - * %AR_TKN_DAI_INDEX: dai index - * - */ -@@ -240,6 +294,45 @@ enum ar_event_types { - #define AR_TKN_U32_MODULE_LOG_TAP_POINT_ID 260 - #define AR_TKN_U32_MODULE_LOG_MODE 261 - -+#define AR_TKN_U16_MODULE_SYNC_SRC 262 -+#define AR_TKN_U16_MODULE_CTRL_DATA_OUT_ENABLE 263 -+#define AR_TKN_U32_MODULE_SLOT_MASK 264 -+#define AR_TKN_U16_MODULE_NSLOTS_PER_FRAME 265 -+#define AR_TKN_U16_MODULE_SLOT_WIDTH 266 -+#define AR_TKN_U16_MODULE_SYNC_MODE 267 -+#define AR_TKN_U16_MODULE_CTRL_INVERT_SYNC_PULSE 268 -+#define AR_TKN_U16_MODULE_CTRL_SYNC_DATA_DELAY 269 -+#define AR_TKN_U16_MODULE_INTF_MODE 270 -+#define AR_TKN_U16_MODULE_QAIF_TYPE 271 -+#define AR_TKN_U32_MODULE_ACTIVE_LANE_MASK 272 -+#define AR_TKN_U32_MODULE_FRAME_SYNC_RATE 273 -+#define AR_TKN_U16_MODULE_BIT_CLK_TYPE 274 -+#define AR_TKN_U8_MODULE_INV_INT_BIT_CLK 275 -+#define AR_TKN_U8_MODULE_INV_EXT_BIT_CLK 276 -+ -+#define AR_AUDIO_IF_SYNC_SRC_EXTERNAL 0 -+#define AR_AUDIO_IF_SYNC_SRC_INTERNAL 1 -+#define AR_AUDIO_IF_CTRL_DATA_OE_DISABLE 0 -+#define AR_AUDIO_IF_CTRL_DATA_OE_ENABLE 1 -+#define AR_AUDIO_IF_INTF_MODE_TDM 0 -+#define AR_AUDIO_IF_INTF_MODE_PCM 1 -+#define AR_AUDIO_IF_INTF_MODE_I2S 2 -+#define AR_AUDIO_IF_QAIF 0 -+#define AR_AUDIO_IF_QAIF_VA 1 -+#define AR_AUDIO_IF_FRAME_SYNC_MODE_SHORT 0 -+#define AR_AUDIO_IF_FRAME_SYNC_MODE_ONE_SLOT 1 -+#define AR_AUDIO_IF_FRAME_SYNC_MODE_LONG 2 -+#define AR_AUDIO_IF_SYNC_NORMAL 0 -+#define AR_AUDIO_IF_SYNC_INVERTED 1 -+#define AR_AUDIO_IF_DATA_DELAY_NONE 0 -+#define AR_AUDIO_IF_DATA_DELAY_1_CYCLE 1 -+#define AR_AUDIO_IF_DATA_DELAY_2_CYCLE 2 -+#define AR_AUDIO_IF_BIT_CLK_INTERNAL 0 -+#define AR_AUDIO_IF_BIT_CLK_EXTERNAL 1 -+#define AR_AUDIO_IF_BIT_CLK_SKIP 2 -+#define AR_AUDIO_IF_CLK_NORMAL 0 -+#define AR_AUDIO_IF_CLK_INVERTED 1 -+ - #define SND_SOC_AR_TPLG_MODULE_CFG_TYPE 0x01001006 - struct audioreach_module_priv_data { - __le32 size; /* size in bytes of the array, including all elements */ -diff --git a/include/uapi/sound/snd_sst_tokens.h b/include/uapi/sound/snd_sst_tokens.h -deleted file mode 100644 ---- a/include/uapi/sound/snd_sst_tokens.h -+++ /dev/null -@@ -1,324 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ --/* -- * snd_sst_tokens.h - Intel SST tokens definition -- * -- * Copyright (C) 2016 Intel Corp -- * Author: Shreyas NC -- */ --#ifndef __SND_SST_TOKENS_H__ --#define __SND_SST_TOKENS_H__ -- --/** -- * %SKL_TKN_UUID: Module UUID -- * -- * %SKL_TKN_U8_BLOCK_TYPE: Type of the private data block.Can be: -- * tuples, bytes, short and words -- * -- * %SKL_TKN_U8_IN_PIN_TYPE: Input pin type, -- * homogenous=0, heterogenous=1 -- * -- * %SKL_TKN_U8_OUT_PIN_TYPE: Output pin type, -- * homogenous=0, heterogenous=1 -- * %SKL_TKN_U8_DYN_IN_PIN: Configure Input pin dynamically -- * if true -- * -- * %SKL_TKN_U8_DYN_OUT_PIN: Configure Output pin dynamically -- * if true -- * -- * %SKL_TKN_U8_IN_QUEUE_COUNT: Store the number of Input pins -- * -- * %SKL_TKN_U8_OUT_QUEUE_COUNT: Store the number of Output pins -- * -- * %SKL_TKN_U8_TIME_SLOT: TDM slot number -- * -- * %SKL_TKN_U8_CORE_ID: Stores module affinity value.Can take -- * the values: -- * SKL_AFFINITY_CORE_0 = 0, -- * SKL_AFFINITY_CORE_1, -- * SKL_AFFINITY_CORE_MAX -- * -- * %SKL_TKN_U8_MOD_TYPE: Module type value. -- * -- * %SKL_TKN_U8_CONN_TYPE: Module connection type can be a FE, -- * BE or NONE as defined : -- * SKL_PIPE_CONN_TYPE_NONE = 0, -- * SKL_PIPE_CONN_TYPE_FE = 1 (HOST_DMA) -- * SKL_PIPE_CONN_TYPE_BE = 2 (LINK_DMA) -- * -- * %SKL_TKN_U8_DEV_TYPE: Type of device to which the module is -- * connected -- * Can take the values: -- * SKL_DEVICE_BT = 0x0, -- * SKL_DEVICE_DMIC = 0x1, -- * SKL_DEVICE_I2S = 0x2, -- * SKL_DEVICE_SLIMBUS = 0x3, -- * SKL_DEVICE_HDALINK = 0x4, -- * SKL_DEVICE_HDAHOST = 0x5, -- * SKL_DEVICE_NONE -- * -- * %SKL_TKN_U8_HW_CONN_TYPE: Connection type of the HW to which the -- * module is connected -- * SKL_CONN_NONE = 0, -- * SKL_CONN_SOURCE = 1, -- * SKL_CONN_SINK = 2 -- * -- * %SKL_TKN_U16_PIN_INST_ID: Stores the pin instance id -- * -- * %SKL_TKN_U16_MOD_INST_ID: Stores the mdule instance id -- * -- * %SKL_TKN_U32_MAX_MCPS: Module max mcps value -- * -- * %SKL_TKN_U32_MEM_PAGES: Module resource pages -- * -- * %SKL_TKN_U32_OBS: Stores Output Buffer size -- * -- * %SKL_TKN_U32_IBS: Stores input buffer size -- * -- * %SKL_TKN_U32_VBUS_ID: Module VBUS_ID. PDM=0, SSP0=0, -- * SSP1=1,SSP2=2, -- * SSP3=3, SSP4=4, -- * SSP5=5, SSP6=6,INVALID -- * -- * %SKL_TKN_U32_PARAMS_FIXUP: Module Params fixup mask -- * %SKL_TKN_U32_CONVERTER: Module params converter mask -- * %SKL_TKN_U32_PIPE_ID: Stores the pipe id -- * -- * %SKL_TKN_U32_PIPE_CONN_TYPE: Type of the token to which the pipe is -- * connected to. It can be -- * SKL_PIPE_CONN_TYPE_NONE = 0, -- * SKL_PIPE_CONN_TYPE_FE = 1 (HOST_DMA), -- * SKL_PIPE_CONN_TYPE_BE = 2 (LINK_DMA), -- * -- * %SKL_TKN_U32_PIPE_PRIORITY: Pipe priority value -- * %SKL_TKN_U32_PIPE_MEM_PGS: Pipe resource pages -- * -- * %SKL_TKN_U32_DIR_PIN_COUNT: Value for the direction to set input/output -- * formats and the pin count. -- * The first 4 bits have the direction -- * value and the next 4 have -- * the pin count value. -- * SKL_DIR_IN = 0, SKL_DIR_OUT = 1. -- * The input and output formats -- * share the same set of tokens -- * with the distinction between input -- * and output made by reading direction -- * token. -- * -- * %SKL_TKN_U32_FMT_CH: Supported channel count -- * -- * %SKL_TKN_U32_FMT_FREQ: Supported frequency/sample rate -- * -- * %SKL_TKN_U32_FMT_BIT_DEPTH: Supported container size -- * -- * %SKL_TKN_U32_FMT_SAMPLE_SIZE:Number of samples in the container -- * -- * %SKL_TKN_U32_FMT_CH_CONFIG: Supported channel configurations for the -- * input/output. -- * -- * %SKL_TKN_U32_FMT_INTERLEAVE: Interleaving style which can be per -- * channel or per sample. The values can be : -- * SKL_INTERLEAVING_PER_CHANNEL = 0, -- * SKL_INTERLEAVING_PER_SAMPLE = 1, -- * -- * %SKL_TKN_U32_FMT_SAMPLE_TYPE: -- * Specifies the sample type. Can take the -- * values: SKL_SAMPLE_TYPE_INT_MSB = 0, -- * SKL_SAMPLE_TYPE_INT_LSB = 1, -- * SKL_SAMPLE_TYPE_INT_SIGNED = 2, -- * SKL_SAMPLE_TYPE_INT_UNSIGNED = 3, -- * SKL_SAMPLE_TYPE_FLOAT = 4 -- * -- * %SKL_TKN_U32_CH_MAP: Channel map values -- * %SKL_TKN_U32_MOD_SET_PARAMS: It can take these values: -- * SKL_PARAM_DEFAULT, SKL_PARAM_INIT, -- * SKL_PARAM_SET, SKL_PARAM_BIND -- * -- * %SKL_TKN_U32_MOD_PARAM_ID: ID of the module params -- * -- * %SKL_TKN_U32_CAPS_SET_PARAMS: -- * Set params value -- * -- * %SKL_TKN_U32_CAPS_PARAMS_ID: Params ID -- * -- * %SKL_TKN_U32_CAPS_SIZE: Caps size -- * -- * %SKL_TKN_U32_PROC_DOMAIN: Specify processing domain -- * -- * %SKL_TKN_U32_LIB_COUNT: Specifies the number of libraries -- * -- * %SKL_TKN_STR_LIB_NAME: Specifies the library name -- * -- * %SKL_TKN_U32_PMODE: Specifies the power mode for pipe -- * -- * %SKL_TKL_U32_D0I3_CAPS: Specifies the D0i3 capability for module -- * -- * %SKL_TKN_U32_DMA_BUF_SIZE: DMA buffer size in millisec -- * -- * %SKL_TKN_U32_PIPE_DIR: Specifies pipe direction. Can be -- * playback/capture. -- * -- * %SKL_TKN_U32_NUM_CONFIGS: Number of pipe configs -- * -- * %SKL_TKN_U32_PATH_MEM_PGS: Size of memory (in pages) required for pipeline -- * and its data -- * -- * %SKL_TKN_U32_PIPE_CONFIG_ID: Config id for the modules in the pipe -- * and PCM params supported by that pipe -- * config. This is used as index to fill -- * up the pipe config and module config -- * structure. -- * -- * %SKL_TKN_U32_CFG_FREQ: -- * %SKL_TKN_U8_CFG_CHAN: -- * %SKL_TKN_U8_CFG_BPS: PCM params (freq, channels, bits per sample) -- * supported for each of the pipe configs. -- * -- * %SKL_TKN_CFG_MOD_RES_ID: Module's resource index for each of the -- * pipe config -- * -- * %SKL_TKN_CFG_MOD_FMT_ID: Module's interface index for each of the -- * pipe config -- * -- * %SKL_TKN_U8_NUM_MOD: Number of modules in the manifest -- * -- * %SKL_TKN_MM_U8_MOD_IDX: Current index of the module in the manifest -- * -- * %SKL_TKN_MM_U8_NUM_RES: Number of resources for the module -- * -- * %SKL_TKN_MM_U8_NUM_INTF: Number of interfaces for the module -- * -- * %SKL_TKN_MM_U32_RES_ID: Resource index for the resource info to -- * be filled into. -- * A module can support multiple resource -- * configuration and is represnted as a -- * resource table. This index is used to -- * fill information into appropriate index. -- * -- * %SKL_TKN_MM_U32_CPS: DSP cycles per second -- * -- * %SKL_TKN_MM_U32_DMA_SIZE: Allocated buffer size for gateway DMA -- * -- * %SKL_TKN_MM_U32_CPC: DSP cycles allocated per frame -- * -- * %SKL_TKN_MM_U32_RES_PIN_ID: Resource pin index in the module -- * -- * %SKL_TKN_MM_U32_INTF_PIN_ID: Interface index in the module -- * -- * %SKL_TKN_MM_U32_PIN_BUF: Buffer size of the module pin -- * -- * %SKL_TKN_MM_U32_FMT_ID: Format index for each of the interface/ -- * format information to be filled into. -- * -- * %SKL_TKN_MM_U32_NUM_IN_FMT: Number of input formats -- * %SKL_TKN_MM_U32_NUM_OUT_FMT: Number of output formats -- * -- * %SKL_TKN_U32_ASTATE_IDX: Table Index for the A-State entry to be filled -- * with kcps and clock source -- * -- * %SKL_TKN_U32_ASTATE_COUNT: Number of valid entries in A-State table -- * -- * %SKL_TKN_U32_ASTATE_KCPS: Specifies the core load threshold (in kilo -- * cycles per second) below which DSP is clocked -- * from source specified by clock source. -- * -- * %SKL_TKN_U32_ASTATE_CLK_SRC: Clock source for A-State entry -- * -- * %SKL_TKN_U32_FMT_CFG_IDX: Format config index -- * -- * module_id and loadable flags dont have tokens as these values will be -- * read from the DSP FW manifest -- * -- * Tokens defined can be used either in the manifest or widget private data. -- * -- * SKL_TKN_MM is used as a suffix for all tokens that represent -- * module data in the manifest. -- */ --enum SKL_TKNS { -- SKL_TKN_UUID = 1, -- SKL_TKN_U8_NUM_BLOCKS, -- SKL_TKN_U8_BLOCK_TYPE, -- SKL_TKN_U8_IN_PIN_TYPE, -- SKL_TKN_U8_OUT_PIN_TYPE, -- SKL_TKN_U8_DYN_IN_PIN, -- SKL_TKN_U8_DYN_OUT_PIN, -- SKL_TKN_U8_IN_QUEUE_COUNT, -- SKL_TKN_U8_OUT_QUEUE_COUNT, -- SKL_TKN_U8_TIME_SLOT, -- SKL_TKN_U8_CORE_ID, -- SKL_TKN_U8_MOD_TYPE, -- SKL_TKN_U8_CONN_TYPE, -- SKL_TKN_U8_DEV_TYPE, -- SKL_TKN_U8_HW_CONN_TYPE, -- SKL_TKN_U16_MOD_INST_ID, -- SKL_TKN_U16_BLOCK_SIZE, -- SKL_TKN_U32_MAX_MCPS, -- SKL_TKN_U32_MEM_PAGES, -- SKL_TKN_U32_OBS, -- SKL_TKN_U32_IBS, -- SKL_TKN_U32_VBUS_ID, -- SKL_TKN_U32_PARAMS_FIXUP, -- SKL_TKN_U32_CONVERTER, -- SKL_TKN_U32_PIPE_ID, -- SKL_TKN_U32_PIPE_CONN_TYPE, -- SKL_TKN_U32_PIPE_PRIORITY, -- SKL_TKN_U32_PIPE_MEM_PGS, -- SKL_TKN_U32_DIR_PIN_COUNT, -- SKL_TKN_U32_FMT_CH, -- SKL_TKN_U32_FMT_FREQ, -- SKL_TKN_U32_FMT_BIT_DEPTH, -- SKL_TKN_U32_FMT_SAMPLE_SIZE, -- SKL_TKN_U32_FMT_CH_CONFIG, -- SKL_TKN_U32_FMT_INTERLEAVE, -- SKL_TKN_U32_FMT_SAMPLE_TYPE, -- SKL_TKN_U32_FMT_CH_MAP, -- SKL_TKN_U32_PIN_MOD_ID, -- SKL_TKN_U32_PIN_INST_ID, -- SKL_TKN_U32_MOD_SET_PARAMS, -- SKL_TKN_U32_MOD_PARAM_ID, -- SKL_TKN_U32_CAPS_SET_PARAMS, -- SKL_TKN_U32_CAPS_PARAMS_ID, -- SKL_TKN_U32_CAPS_SIZE, -- SKL_TKN_U32_PROC_DOMAIN, -- SKL_TKN_U32_LIB_COUNT, -- SKL_TKN_STR_LIB_NAME, -- SKL_TKN_U32_PMODE, -- SKL_TKL_U32_D0I3_CAPS, /* Typo added at v4.10 */ -- SKL_TKN_U32_D0I3_CAPS = SKL_TKL_U32_D0I3_CAPS, -- SKL_TKN_U32_DMA_BUF_SIZE, -- -- SKL_TKN_U32_PIPE_DIRECTION, -- SKL_TKN_U32_PIPE_CONFIG_ID, -- SKL_TKN_U32_NUM_CONFIGS, -- SKL_TKN_U32_PATH_MEM_PGS, -- -- SKL_TKN_U32_CFG_FREQ, -- SKL_TKN_U8_CFG_CHAN, -- SKL_TKN_U8_CFG_BPS, -- SKL_TKN_CFG_MOD_RES_ID, -- SKL_TKN_CFG_MOD_FMT_ID, -- SKL_TKN_U8_NUM_MOD, -- -- SKL_TKN_MM_U8_MOD_IDX, -- SKL_TKN_MM_U8_NUM_RES, -- SKL_TKN_MM_U8_NUM_INTF, -- SKL_TKN_MM_U32_RES_ID, -- SKL_TKN_MM_U32_CPS, -- SKL_TKN_MM_U32_DMA_SIZE, -- SKL_TKN_MM_U32_CPC, -- SKL_TKN_MM_U32_RES_PIN_ID, -- SKL_TKN_MM_U32_INTF_PIN_ID, -- SKL_TKN_MM_U32_PIN_BUF, -- SKL_TKN_MM_U32_FMT_ID, -- SKL_TKN_MM_U32_NUM_IN_FMT, -- SKL_TKN_MM_U32_NUM_OUT_FMT, -- -- SKL_TKN_U32_ASTATE_IDX, -- SKL_TKN_U32_ASTATE_COUNT, -- SKL_TKN_U32_ASTATE_KCPS, -- SKL_TKN_U32_ASTATE_CLK_SRC, -- -- SKL_TKN_U32_FMT_CFG_IDX = 96, -- SKL_TKN_MAX = SKL_TKN_U32_FMT_CFG_IDX, --}; -- --#endif -diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h ---- a/include/uapi/sound/sof/tokens.h -+++ b/include/uapi/sound/sof/tokens.h -@@ -111,8 +111,8 @@ - - #define SOF_TKN_COMP_SCHED_DOMAIN 418 - #define SOF_TKN_COMP_DOMAIN_ID 419 --#define SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT 420 --#define SOF_TKN_COMP_STACK_BYTES_REQUIREMENT 421 -+#define SOF_TKN_COMP_STACK_BYTES_REQUIREMENT 420 -+#define SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT 421 - - /* SSP */ - #define SOF_TKN_INTEL_SSP_CLKS_CONTROL 500 -@@ -223,6 +223,7 @@ - #define SOF_TKN_AMD_ACPI2S_RATE 1700 - #define SOF_TKN_AMD_ACPI2S_CH 1701 - #define SOF_TKN_AMD_ACPI2S_TDM_MODE 1702 -+#define SOF_TKN_AMD_ACPI2S_FORMAT 1703 - - /* MICFIL PDM */ - #define SOF_TKN_IMX_MICFIL_RATE 2000 diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c --- a/sound/atmel/ac97c.c +++ b/sound/atmel/ac97c.c @@ -3567,15 +1949,7 @@ diff --git a/sound/core/init.c b/sound/core/init.c }; /** -@@ -584,12 +583,17 @@ EXPORT_SYMBOL_GPL(snd_card_disconnect_sync); - - static int snd_card_do_free(struct snd_card *card) - { -+ bool managed = card->managed; -+ - card->releasing = true; - #if IS_ENABLED(CONFIG_SND_MIXER_OSS) - if (snd_mixer_oss_notify_callback) +@@ -592,6 +591,9 @@ static int snd_card_do_free(struct snd_card *card) snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE); #endif snd_device_free_all(card); @@ -3585,16 +1959,7 @@ diff --git a/sound/core/init.c b/sound/core/init.c if (card->private_free) card->private_free(card); #ifdef CONFIG_SND_CTL_DEBUG -@@ -601,7 +605,7 @@ static int snd_card_do_free(struct snd_card *card) - } - if (card->release_completion) - complete(card->release_completion); -- if (!card->managed) -+ if (!managed) - kfree(card); - return 0; - } -@@ -722,7 +726,7 @@ static void snd_card_set_id_no_lock(struct snd_card *card, const char *src, +@@ -725,7 +727,7 @@ static void snd_card_set_id_no_lock(struct snd_card *card, const char *src, int len, loops; bool is_default = false; char *id; @@ -3603,7 +1968,7 @@ diff --git a/sound/core/init.c b/sound/core/init.c copy_valid_id_string(card, src, nid); id = card->id; -@@ -1031,21 +1035,44 @@ int __init snd_card_info_init(void) +@@ -1034,21 +1036,44 @@ int __init snd_card_info_init(void) * * Return: Zero otherwise a negative error code. */ @@ -4227,7 +2592,7 @@ diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c offset = vmf->pgoff << PAGE_SHIFT; dma_bytes = PAGE_ALIGN(runtime->dma_bytes); if (offset > dma_bytes - PAGE_SIZE) -@@ -4241,29 +4245,29 @@ static unsigned long snd_pcm_get_unmapped_area(struct file *file, +@@ -4256,29 +4260,29 @@ static unsigned long snd_pcm_get_unmapped_area(struct file *file, const struct file_operations snd_pcm_f_ops[2] = { { @@ -4512,41 +2877,7 @@ diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c * If the receiver client is a kernel client, the original event is * quoted in SNDRV_SEQ_EVENT_KERNEL_ERROR, since this requires no extra * kmalloc. -@@ -528,7 +540,7 @@ static int bounce_error_event(struct snd_seq_client *client, - struct snd_seq_event *event, - int err, int atomic, int hop) - { -- struct snd_seq_event bounce_ev; -+ struct snd_seq_event bounce_ev, quoted; - int result; - - if (client == NULL || -@@ -548,15 +560,19 @@ static int bounce_error_event(struct snd_seq_client *client, - * For user clients, send SNDRV_SEQ_EVENT_BOUNCE with the - * original event embedded as variable-length data. This - * avoids exposing data.quote.event (a kernel pointer) to -- * userspace. The variable-length path in snd_seq_event_dup() -- * copies the event data from data.ext.ptr into chained cells, -- * and snd_seq_expand_var_event() copies only the data content -- * -- never the pointer -- to userspace. -+ * userspace. Sanitise the embedded copy too - a queued -+ * variable-length event carries the address of its own -+ * extension cell, and the payload goes out verbatim. - */ -+ quoted = *event; -+ if (snd_seq_ev_is_variable("ed)) { -+ quoted.data.ext.len &= ~SNDRV_SEQ_EXT_MASK; -+ quoted.data.ext.ptr = NULL; -+ } - bounce_ev.type = SNDRV_SEQ_EVENT_BOUNCE; - bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE; - bounce_ev.data.ext.len = sizeof(struct snd_seq_event); -- bounce_ev.data.ext.ptr = (char *)event; -+ bounce_ev.data.ext.ptr = (char *)"ed; - } else { - /* - * For kernel clients, quote the event pointer directly. -@@ -720,10 +736,11 @@ static int __deliver_to_subscribers(struct snd_seq_client *client, +@@ -724,10 +736,11 @@ static int __deliver_to_subscribers(struct snd_seq_client *client, /* lock list */ if (atomic) @@ -4560,7 +2891,7 @@ diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c /* both ports ready? */ if (atomic_read(&subs->ref_count) != 2) continue; -@@ -744,7 +761,7 @@ static int __deliver_to_subscribers(struct snd_seq_client *client, +@@ -748,7 +761,7 @@ static int __deliver_to_subscribers(struct snd_seq_client *client, memcpy(event, &event_saved, saved_size); } if (atomic) @@ -4569,7 +2900,7 @@ diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c else up_read(&grp->list_mutex); memcpy(event, &event_saved, saved_size); -@@ -1941,7 +1958,7 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void *arg) +@@ -1945,7 +1958,7 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void *arg) { struct snd_seq_query_subs *subs = arg; struct snd_seq_port_subs_info *group; @@ -4578,7 +2909,7 @@ diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c int i; struct snd_seq_client *cptr __free(snd_seq_client) = -@@ -1968,15 +1985,15 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void *arg) +@@ -1972,15 +1985,15 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void *arg) /* search for the subscriber */ subs->num_subs = group->count; i = 0; @@ -4597,7 +2928,7 @@ diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c subs->addr = s->info.sender; } subs->flags = s->info.flags; -@@ -2532,19 +2549,19 @@ static void snd_seq_info_dump_subscribers(struct snd_info_buffer *buffer, +@@ -2536,19 +2549,19 @@ static void snd_seq_info_dump_subscribers(struct snd_info_buffer *buffer, struct snd_seq_port_subs_info *group, int is_src, char *msg) { @@ -4622,7 +2953,7 @@ diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c if (count++) snd_iprintf(buffer, ", "); snd_iprintf(buffer, "%d:%d", -@@ -2666,16 +2683,15 @@ void snd_seq_info_clients_read(struct snd_info_entry *entry, +@@ -2670,16 +2683,15 @@ void snd_seq_info_clients_read(struct snd_info_entry *entry, * REGISTRATION PART */ @@ -4659,159 +2990,6 @@ diff --git a/sound/core/seq/seq_clientmgr.h b/sound/core/seq/seq_clientmgr.h struct mutex ports_mutex; struct mutex ioctl_mutex; int convert32; /* convert 32->64bit */ -diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c ---- a/sound/core/seq/seq_midi.c -+++ b/sound/core/seq/seq_midi.c -@@ -42,9 +42,11 @@ struct seq_midisynth { - struct snd_rawmidi *rmidi; - int device; - int subdevice; -+ struct snd_rawmidi_substream __rcu *input_substream; -+ snd_use_lock_t input_use_lock; /* in-flight event_input users */ - struct snd_rawmidi_file input_rfile; -- spinlock_t output_lock; /* protects output_rfile publication */ - snd_use_lock_t output_use_lock; /* in-flight event_input users */ -+ struct snd_rawmidi_substream __rcu *output_substream; - struct snd_rawmidi_file output_rfile; - int seq_client; - int seq_port; -@@ -76,6 +78,14 @@ static void snd_midi_input_event(struct snd_rawmidi_substream *substream) - msynth = runtime->private_data; - if (msynth == NULL) - return; -+ -+ scoped_guard(rcu) { -+ if (rcu_dereference(msynth->input_substream) != substream) -+ return; -+ -+ snd_use_lock_use(&msynth->input_use_lock); -+ } -+ - memset(&ev, 0, sizeof(ev)); - while (runtime->avail > 0) { - res = snd_rawmidi_kernel_read(substream, buf, sizeof(buf)); -@@ -95,6 +105,8 @@ static void snd_midi_input_event(struct snd_rawmidi_substream *substream) - memset(&ev, 0, sizeof(ev)); - } - } -+ -+ snd_use_lock_free(&msynth->input_use_lock); - } - - static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, int count) -@@ -134,8 +146,8 @@ static int event_process_midi(struct snd_seq_event *ev, int direct, - if (snd_BUG_ON(!msynth)) - return -EINVAL; - -- scoped_guard(spinlock_irqsave, &msynth->output_lock) { -- substream = msynth->output_rfile.output; -+ scoped_guard(rcu) { -+ substream = rcu_dereference(msynth->output_substream); - if (!substream) - return -ENODEV; - snd_use_lock_use(&msynth->output_use_lock); -@@ -177,7 +189,7 @@ static int snd_seq_midisynth_new(struct seq_midisynth *msynth, - msynth->card = card; - msynth->device = device; - msynth->subdevice = subdevice; -- spin_lock_init(&msynth->output_lock); -+ snd_use_lock_init(&msynth->input_use_lock); - snd_use_lock_init(&msynth->output_use_lock); - return 0; - } -@@ -188,28 +200,31 @@ static int midisynth_subscribe(void *private_data, struct snd_seq_port_subscribe - int err; - struct seq_midisynth *msynth = private_data; - struct snd_rawmidi_runtime *runtime; -+ struct snd_rawmidi_file rfile = {}; - struct snd_rawmidi_params params; - - /* open midi port */ - err = snd_rawmidi_kernel_open(msynth->rmidi, msynth->subdevice, - SNDRV_RAWMIDI_LFLG_INPUT, -- &msynth->input_rfile); -+ &rfile); - if (err < 0) { - pr_debug("ALSA: seq_midi: midi input open failed!!!\n"); - return err; - } -- runtime = msynth->input_rfile.input->runtime; -+ runtime = rfile.input->runtime; - memset(¶ms, 0, sizeof(params)); - params.avail_min = 1; - params.buffer_size = input_buffer_size; -- err = snd_rawmidi_input_params(msynth->input_rfile.input, ¶ms); -+ err = snd_rawmidi_input_params(rfile.input, ¶ms); - if (err < 0) { -- snd_rawmidi_kernel_release(&msynth->input_rfile); -+ snd_rawmidi_kernel_release(&rfile); - return err; - } - snd_midi_event_reset_encode(msynth->parser); - runtime->event = snd_midi_input_event; - runtime->private_data = msynth; -+ msynth->input_rfile = rfile; -+ rcu_assign_pointer(msynth->input_substream, rfile.input); - snd_rawmidi_kernel_read(msynth->input_rfile.input, NULL, 0); - return 0; - } -@@ -219,10 +234,19 @@ static int midisynth_unsubscribe(void *private_data, struct snd_seq_port_subscri - { - int err; - struct seq_midisynth *msynth = private_data; -+ struct snd_rawmidi_file rfile; - -- if (snd_BUG_ON(!msynth->input_rfile.input)) -+ rcu_assign_pointer(msynth->input_substream, NULL); -+ synchronize_rcu(); -+ snd_use_lock_sync(&msynth->input_use_lock); -+ -+ rfile = msynth->input_rfile; -+ msynth->input_rfile = (struct snd_rawmidi_file){}; -+ -+ if (snd_BUG_ON(!rfile.input)) - return -EINVAL; -- err = snd_rawmidi_kernel_release(&msynth->input_rfile); -+ -+ err = snd_rawmidi_kernel_release(&rfile); - return err; - } - -@@ -252,8 +276,8 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info - return err; - } - snd_midi_event_reset_decode(msynth->parser); -- scoped_guard(spinlock_irqsave, &msynth->output_lock) -- msynth->output_rfile = rfile; -+ msynth->output_rfile = rfile; -+ rcu_assign_pointer(msynth->output_substream, rfile.output); - return 0; - } - -@@ -261,17 +285,16 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info - static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *info) - { - struct seq_midisynth *msynth = private_data; -- struct snd_rawmidi_file rfile = {}; -+ struct snd_rawmidi_file rfile; - -- scoped_guard(spinlock_irqsave, &msynth->output_lock) { -- rfile = msynth->output_rfile; -- msynth->output_rfile = (struct snd_rawmidi_file){}; -- } -+ rcu_assign_pointer(msynth->output_substream, NULL); -+ synchronize_rcu(); -+ snd_use_lock_sync(&msynth->output_use_lock); -+ rfile = msynth->output_rfile; -+ msynth->output_rfile = (struct snd_rawmidi_file){}; - - if (snd_BUG_ON(!rfile.output)) - return -EINVAL; -- -- snd_use_lock_sync(&msynth->output_use_lock); - snd_rawmidi_drain_output(rfile.output); - return snd_rawmidi_kernel_release(&rfile); - } diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c --- a/sound/core/seq/seq_ports.c +++ b/sound/core/seq/seq_ports.c @@ -7149,24 +5327,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 static const struct coef_fw alc256_fixup_set_coef_defaults_coefs[] = { WRITE_COEF(0x10, 0x0020), WRITE_COEF(0x24, 0x0000), WRITE_COEF(0x26, 0x0000), WRITE_COEF(0x29, 0x3000), -@@ -3524,6 +3579,16 @@ static void alc287_fixup_yoga9_14iap7_bass_spk_pin(struct hda_codec *codec, - } - } - -+static void alc_fixup_yoga_pro7_audio(struct hda_codec *codec, -+ const struct hda_fixup *fix, int action) -+{ -+ /* Reuse the DAC routing selected for ThinkPad X1 Gen7 */ -+ alc285_fixup_thinkpad_x1_gen7(codec, fix, action); -+ -+ /* Limit the internal mic boost to 0 or 1 to avoid noise */ -+ alc269_fixup_limit_int_mic_boost(codec, fix, action); -+} -+ - static void alc295_fixup_dell_inspiron_top_speakers(struct hda_codec *codec, - const struct hda_fixup *fix, int action) - { -@@ -3929,6 +3994,7 @@ enum { +@@ -3939,6 +3994,7 @@ enum { ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572, ALC269_FIXUP_ACER_AC700, ALC269_FIXUP_LIMIT_INT_MIC_BOOST, @@ -7174,7 +5335,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 ALC269VB_FIXUP_ASUS_ZENBOOK, ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A, ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE, -@@ -3997,6 +4063,7 @@ enum { +@@ -4007,6 +4063,7 @@ enum { ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, ALC295_FIXUP_DISABLE_DAC3, ALC285_FIXUP_SPEAKER2_TO_DAC1, @@ -7182,7 +5343,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 ALC285_FIXUP_ASUS_SPEAKER2_TO_DAC1, ALC285_FIXUP_ASUS_HEADSET_MIC, ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS, -@@ -4066,7 +4133,6 @@ enum { +@@ -4076,7 +4133,6 @@ enum { ALC294_FIXUP_ASUS_ALLY, ALC294_FIXUP_ASUS_ALLY_PINS, ALC294_FIXUP_ASUS_ALLY_VERBS, @@ -7190,7 +5351,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 ALC294_FIXUP_ASUS_HPE, ALC294_FIXUP_ASUS_COEF_1B, ALC294_FIXUP_ASUS_GX502_HP, -@@ -4091,6 +4157,8 @@ enum { +@@ -4101,6 +4157,8 @@ enum { ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS, ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS, ALC298_FIXUP_LG_GRAM_STYLE_14, @@ -7199,7 +5360,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, -@@ -4108,7 +4176,6 @@ enum { +@@ -4118,7 +4176,6 @@ enum { ALC269_FIXUP_LEMOTE_A190X, ALC256_FIXUP_INTEL_NUC8_RUGGED, ALC233_FIXUP_INTEL_NUC8_DMIC, @@ -7207,7 +5368,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 ALC256_FIXUP_INTEL_NUC10, ALC255_FIXUP_XIAOMI_HEADSET_MIC, ALC274_FIXUP_HP_MIC, -@@ -4139,6 +4206,7 @@ enum { +@@ -4149,6 +4206,7 @@ enum { ALC298_FIXUP_LENOVO_C940_DUET7, ALC287_FIXUP_LENOVO_YOGA_BOOK_9I, ALC287_FIXUP_LENOVO_YOGA_PRO7, @@ -7215,7 +5376,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 ALC287_FIXUP_13S_GEN2_SPEAKERS, ALC256_FIXUP_SET_COEF_DEFAULTS, ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE, -@@ -4183,7 +4251,6 @@ enum { +@@ -4193,7 +4251,6 @@ enum { ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD, ALC2XX_FIXUP_HEADSET_MIC, ALC289_FIXUP_DELL_CS35L41_SPI_2, @@ -7223,7 +5384,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 ALC256_FIXUP_ACER_SFG16_MICMUTE_LED, ALC256_FIXUP_HEADPHONE_AMP_VOL, ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX, -@@ -4197,6 +4264,7 @@ enum { +@@ -4207,6 +4264,7 @@ enum { ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318, ALC256_FIXUP_CHROME_BOOK, ALC245_FIXUP_CLEVO_NOISY_MIC, @@ -7231,7 +5392,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE, ALC233_FIXUP_MEDION_MTL_SPK, ALC233_FIXUP_STARLABS_STARFIGHTER, -@@ -4206,7 +4274,6 @@ enum { +@@ -4216,7 +4274,6 @@ enum { ALC274_FIXUP_HP_AIO_BIND_DACS, ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2, ALC285_FIXUP_ASUS_GA605K_HEADSET_MIC, @@ -7239,7 +5400,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC, ALC289_FIXUP_ASUS_ZEPHYRUS_DUAL_SPK, ALC256_FIXUP_VAIO_RPL_MIC_NO_PRESENCE, -@@ -4226,6 +4293,10 @@ enum { +@@ -4236,6 +4293,10 @@ enum { ALC236_FIXUP_DELL_HP_POP_NOISE, ALC274_FIXUP_HP_89E9_GPIO, ALC274_FIXUP_HP_VERBS, @@ -7250,7 +5411,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 }; /* A special fixup for Lenovo C940 and Yoga Duet 7; -@@ -4263,6 +4334,20 @@ static void alc287_fixup_lenovo_yoga_book_9i(struct hda_codec *codec, +@@ -4273,6 +4334,20 @@ static void alc287_fixup_lenovo_yoga_book_9i(struct hda_codec *codec, } static const struct hda_fixup alc269_fixups[] = { @@ -7271,7 +5432,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 [ALC233_FIXUP_WUJIE_SPEAKERS] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { -@@ -4703,6 +4788,10 @@ static const struct hda_fixup alc269_fixups[] = { +@@ -4713,6 +4788,10 @@ static const struct hda_fixup alc269_fixups[] = { .chain_id = ALC271_FIXUP_DMIC, }, [ALC269_FIXUP_LIMIT_INT_MIC_BOOST] = { @@ -7282,7 +5443,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_limit_int_mic_boost, .chained = true, -@@ -5078,11 +5167,7 @@ static const struct hda_fixup alc269_fixups[] = { +@@ -5088,11 +5167,7 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic, .chained = true, @@ -7295,7 +5456,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 }, [ALC255_FIXUP_DELL_SPK_NOISE] = { .type = HDA_FIXUP_FUNC, -@@ -5143,6 +5228,10 @@ static const struct hda_fixup alc269_fixups[] = { +@@ -5153,6 +5228,10 @@ static const struct hda_fixup alc269_fixups[] = { [ALC285_FIXUP_SPEAKER2_TO_DAC1] = { .type = HDA_FIXUP_FUNC, .v.func = alc285_fixup_speaker2_to_dac1, @@ -7306,7 +5467,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 .chained = true, .chain_id = ALC269_FIXUP_THINKPAD_ACPI }, -@@ -5662,11 +5751,7 @@ static const struct hda_fixup alc269_fixups[] = { +@@ -5672,11 +5751,7 @@ static const struct hda_fixup alc269_fixups[] = { { } }, .chained = true, @@ -7319,13 +5480,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 }, [ALC285_FIXUP_THINKPAD_X1_GEN7] = { .type = HDA_FIXUP_FUNC, -@@ -6209,11 +6294,20 @@ static const struct hda_fixup alc269_fixups[] = { - }, - [ALC287_FIXUP_LENOVO_YOGA_PRO7] = { - .type = HDA_FIXUP_FUNC, -- /* Reuse the DAC routing selected for ThinkPad X1 Gen7 */ -- .v.func = alc285_fixup_thinkpad_x1_gen7, -+ .v.func = alc_fixup_yoga_pro7_audio, +@@ -6223,6 +6298,16 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_LENOVO_XPAD_ACPI, }, @@ -7342,7 +5497,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 [ALC623_FIXUP_LENOVO_THINKSTATION_P340] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_no_shutup, -@@ -6653,10 +6747,6 @@ static const struct hda_fixup alc269_fixups[] = { +@@ -6662,10 +6747,6 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC289_FIXUP_DUAL_SPK }, @@ -7353,7 +5508,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 [ALC256_FIXUP_ACER_SFG16_MICMUTE_LED] = { .type = HDA_FIXUP_FUNC, .v.func = alc256_fixup_acer_sfg16_micmute_led, -@@ -6729,6 +6819,10 @@ static const struct hda_fixup alc269_fixups[] = { +@@ -6738,6 +6819,10 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE, }, @@ -7364,7 +5519,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 [ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { -@@ -6749,6 +6843,8 @@ static const struct hda_fixup alc269_fixups[] = { +@@ -6758,6 +6843,8 @@ static const struct hda_fixup alc269_fixups[] = { [ALC233_FIXUP_STARLABS_STARFIGHTER] = { .type = HDA_FIXUP_FUNC, .v.func = alc233_fixup_starlabs_starfighter, @@ -7373,7 +5528,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 }, [ALC294_FIXUP_BASS_SPEAKER_15] = { .type = HDA_FIXUP_FUNC, -@@ -6776,11 +6872,7 @@ static const struct hda_fixup alc269_fixups[] = { +@@ -6785,11 +6872,7 @@ static const struct hda_fixup alc269_fixups[] = { { } }, .chained = true, @@ -7386,7 +5541,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 }, [ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC] = { .type = HDA_FIXUP_FUNC, -@@ -6898,6 +6990,26 @@ static const struct hda_fixup alc269_fixups[] = { +@@ -6907,6 +6990,26 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC274_FIXUP_HP_89E9_GPIO, }, @@ -7413,7 +5568,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 }; static const struct hda_quirk alc269_fixup_tbl[] = { -@@ -6951,8 +7063,10 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -6960,8 +7063,10 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x1597, "Acer Nitro 5 AN517-55", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x159e, "Acer Nitro 5 AN515-46", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x160e, "Acer PT316-51S", ALC2XX_FIXUP_HEADSET_MIC), @@ -7424,7 +5579,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x1025, 0x1679, "Acer Nitro 16 AN16-41", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x169a, "Acer Swift SFG16", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x171e, "Acer Nitro ANV15-51", ALC245_FIXUP_ACER_MICMUTE_LED), -@@ -6961,6 +7075,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -6970,6 +7075,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x1826, "Acer Helios ZPC", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), SND_PCI_QUIRK(0x1025, 0x182c, "Acer Helios ZPD", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), SND_PCI_QUIRK(0x1025, 0x1844, "Acer Helios ZPS", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), @@ -7432,7 +5587,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), SND_PCI_QUIRK(0x1028, 0x053c, "Dell Latitude E5430", ALC292_FIXUP_DELL_E7X), SND_PCI_QUIRK(0x1028, 0x054b, "Dell XPS one 2710", ALC275_FIXUP_DELL_XPS), -@@ -7145,10 +7260,12 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7154,10 +7260,12 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x85c6, "HP Pavilion x360 Convertible 14-dy1xxx", ALC295_FIXUP_HP_MUTE_LED_COEFBIT11), SND_PCI_QUIRK(0x103c, 0x85de, "HP Envy x360 13-ar0xxx", ALC285_FIXUP_HP_ENVY_X360), SND_PCI_QUIRK(0x103c, 0x85f0, "HP Laptop 15-dw0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), @@ -7445,7 +5600,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x86c1, "HP Laptop 15-da3001TU", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x86c7, "HP Envy AiO 32", ALC274_FIXUP_HP_ENVY_GPIO), -@@ -7259,6 +7376,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7268,6 +7376,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x89e7, "HP Elite x2 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a05, "HP Dragonfly Folio G3 2-in-1", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a06, "HP Dragonfly Folio G3 2-in-1", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), @@ -7453,7 +5608,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x103c, 0x8a0f, "HP Pavilion 14-ec1xxx", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a1b, "HP 255 15.6 inch G9 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x8a1f, "HP Laptop 14s-dr5xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), -@@ -7350,10 +7468,11 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7359,10 +7468,11 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8bf0, "HP", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c15, "HP Spectre x360 2-in-1 Laptop 14-eu0xxx", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX), SND_PCI_QUIRK(0x103c, 0x8c16, "HP Spectre x360 2-in-1 Laptop 16-aa0xxx", ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX), @@ -7466,7 +5621,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x103c, 0x8c46, "HP EliteBook 830 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c47, "HP EliteBook 840 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c48, "HP EliteBook 860 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), -@@ -7502,6 +7621,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7511,6 +7621,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8f42, "HP ZBook 8 G2a 14W", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8f57, "HP Trekker G7JC", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8f62, "HP ZBook 8 G2a 16W", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), @@ -7474,7 +5629,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x103c, 0x8f94, "HP ZBook 8 G2a 14", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED_INVERTED), SND_PCI_QUIRK(0x103c, 0x8f95, "HP ZBook 8 G2a 16", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED_INVERTED), SND_PCI_QUIRK(0x1043, 0x1024, "ASUS Zephyrus G14 2025", ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC), -@@ -7509,6 +7629,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7518,6 +7629,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1034, "ASUS GU605C", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), @@ -7482,7 +5637,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x1043, 0x1054, "ASUS G614FH/FM/FP", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1043, 0x106f, "ASUS VivoBook X515UA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), -@@ -7516,16 +7637,15 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7525,16 +7637,15 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x10a1, "ASUS UX391UA", ALC294_FIXUP_ASUS_SPK), SND_PCI_QUIRK(0x1043, 0x10a4, "ASUS TP3407SA", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x1043, 0x10c0, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), @@ -7502,7 +5657,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x1043, 0x125e, "ASUS Q524UQK", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1271, "ASUS X430UN", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1290, "ASUS X441SA", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE), -@@ -7574,7 +7694,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7583,7 +7694,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1863, "ASUS UX6404VI/VV", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1881, "ASUS Zephyrus S/M", ALC294_FIXUP_ASUS_GX502_PINS), SND_PCI_QUIRK(0x1043, 0x18b1, "Asus MJ401TA", ALC256_FIXUP_ASUS_HEADSET_MIC), @@ -7511,7 +5666,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x1043, 0x18f1, "Asus FX505DT", ALC256_FIXUP_ASUS_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x194e, "ASUS UX563FD", ALC294_FIXUP_ASUS_HPE), SND_PCI_QUIRK(0x1043, 0x1970, "ASUS UX550VE", ALC289_FIXUP_ASUS_GA401), -@@ -7585,7 +7705,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7594,7 +7705,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x19f4, "ASUS UM3405GA", ALC294_FIXUP_ASUS_I2C_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), SND_PCI_QUIRK(0x1043, 0x1a63, "ASUS UX3405MA", ALC294_FIXUP_ASUS_SPI_HEADSET_MIC), @@ -7520,7 +5675,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x1043, 0x1a8e, "ASUS G712LWS", ALC294_FIXUP_LENOVO_MIC_LOCATION), SND_PCI_QUIRK(0x1043, 0x1a8f, "ASUS UX582ZS", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1b11, "ASUS UX431DA", ALC294_FIXUP_ASUS_COEF_1B), -@@ -7614,6 +7734,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7623,6 +7734,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1264, "ASUS UM5606KA", ALC294_FIXUP_BASS_SPEAKER_15), SND_PCI_QUIRK(0x1043, 0x1e02, "ASUS UX3402ZA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1e10, "ASUS VivoBook X507UAR", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), @@ -7528,7 +5683,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x1043, 0x1e11, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA502), SND_PCI_QUIRK(0x1043, 0x1e12, "ASUS UM3402", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1e1f, "ASUS Vivobook 15 X1504VAP", ALC2XX_FIXUP_HEADSET_MIC), -@@ -7625,8 +7746,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7634,8 +7746,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1e93, "ASUS ExpertBook B9403CVAR", ALC294_FIXUP_ASUS_HPE), SND_PCI_QUIRK(0x1043, 0x1eb3, "ASUS Ally RC72LA", ALC287_FIXUP_ASUS_ALLY_X), SND_PCI_QUIRK(0x1043, 0x1ed3, "ASUS HN7306W", ALC287_FIXUP_CS35L41_I2C_2), @@ -7538,7 +5693,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x1043, 0x1c52, "ASUS Zephyrus G15 2022", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x1f11, "ASUS Zephyrus G14", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x1f12, "ASUS UM5302", ALC287_FIXUP_CS35L41_I2C_2), -@@ -7837,7 +7957,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7846,7 +7957,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x2211, "Thinkpad W541", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad T440", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad X240", ALC292_FIXUP_TPT440_DOCK), @@ -7547,7 +5702,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x17aa, 0x2218, "Thinkpad X1 Carbon 2nd", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2223, "ThinkPad T550", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK), -@@ -7853,7 +7973,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7862,7 +7973,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x224b, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x224c, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x224d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), @@ -7557,7 +5712,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x17aa, 0x2288, "Thinkpad X390", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK), SND_PCI_QUIRK(0x17aa, 0x2292, "Thinkpad X1 Carbon 7th", ALC285_FIXUP_THINKPAD_HEADSET_JACK), SND_PCI_QUIRK(0x17aa, 0x22be, "Thinkpad X1 Carbon 8th", ALC285_FIXUP_THINKPAD_HEADSET_JACK), -@@ -7928,7 +8049,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7937,7 +8049,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3869, "Lenovo Yoga7 14IAL7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), HDA_CODEC_QUIRK(0x17aa, 0x386a, "Lenovo Yoga 7 16IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), HDA_CODEC_QUIRK(0x17aa, 0x386e, "Legion Y9000X 2022 IAH7", ALC287_FIXUP_CS35L41_I2C_2), @@ -7566,7 +5721,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ HDA_CODEC_QUIRK(0x17aa, 0x38a7, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), -@@ -7963,6 +8084,12 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7972,6 +8084,12 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x38b8, "Yoga S780-14.5 proX AMD YC Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38b9, "Yoga S780-14.5 proX AMD LX Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38ba, "Yoga S780-14.5 Air AMD quad YC", ALC287_FIXUP_TAS2781_I2C), @@ -7579,7 +5734,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x17aa, 0x38bb, "Yoga S780-14.5 Air AMD quad AAC", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38be, "Yoga S980-14.5 proX YC Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38bf, "Yoga S980-14.5 proX LX Dual", ALC287_FIXUP_TAS2781_I2C), -@@ -7980,7 +8107,6 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7989,7 +8107,6 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x38df, "Yoga Y990 Intel YC Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38e0, "Yoga Y990 Intel VECO Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38f8, "Yoga Book 9i", ALC287_FIXUP_TAS2781_I2C), @@ -7587,7 +5742,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 /* Legion 7 15ASH11 shares PCI SSID 17aa:38f9 with Thinkbook 16P Gen5; * use codec SSID to distinguish them */ -@@ -7990,7 +8116,9 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7999,7 +8116,9 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x38fc, "Lenovo Yoga Pro 7 15ASH11", ALC287_FIXUP_LENOVO_YOGA_PRO7), SND_PCI_QUIRK(0x17aa, 0x38fd, "ThinkBook plus Gen5 Hybrid", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), @@ -7598,7 +5753,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x17aa, 0x3911, "Lenovo Yoga Pro 7 14IAH10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x3912, "Lenovo Xiaoxin 14 GT", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x3913, "Lenovo 145", ALC236_FIXUP_LENOVO_INV_DMIC), -@@ -7998,14 +8126,19 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -8007,14 +8126,19 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_TXNW2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3929, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x392b, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), @@ -7620,7 +5775,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x17aa, 0x5034, "Thinkpad T450", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x5036, "Thinkpad T450s", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x503c, "Thinkpad L450", ALC292_FIXUP_TPT440_DOCK), -@@ -8018,7 +8151,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -8027,7 +8151,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x505f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x5062, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x508b, "Thinkpad X12 Gen 1", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS), @@ -7629,7 +5784,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x17aa, 0x511e, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), -@@ -8032,9 +8165,14 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -8041,9 +8165,14 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1854, 0x0489, "LG gram 16 (16Z90R-A)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), SND_PCI_QUIRK(0x1854, 0x048a, "LG gram 17 (17ZD90R)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), SND_PCI_QUIRK(0x1854, 0x0490, "LG Gram Style 14 (14Z90RS)", ALC298_FIXUP_LG_GRAM_STYLE_14), @@ -7644,7 +5799,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x1b35, 0x1235, "CZC B20", ALC269_FIXUP_CZC_B20), SND_PCI_QUIRK(0x1b35, 0x1236, "CZC TMI", ALC269_FIXUP_CZC_TMI), SND_PCI_QUIRK(0x1b35, 0x1237, "CZC L101", ALC269_FIXUP_CZC_L101), -@@ -8069,13 +8207,17 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -8078,13 +8207,17 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1d72, 0x1947, "RedmiBook Air", ALC255_FIXUP_XIAOMI_HEADSET_MIC), SND_PCI_QUIRK(0x1e39, 0xca14, "MEDION NM14LNL", ALC233_FIXUP_MEDION_MTL_SPK), SND_PCI_QUIRK(0x1e50, 0x7007, "Positivo DN50E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), @@ -7662,7 +5817,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x2782, 0x0228, "Infinix ZERO BOOK 13", ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13), SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO), -@@ -8086,6 +8228,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -8095,6 +8228,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x2782, 0x1705, "MEDION E15433", ALC269VC_FIXUP_INFINIX_Y4_MAX), SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME), SND_PCI_QUIRK(0x2782, 0x4900, "MEDION E15443", ALC233_FIXUP_MEDION_MTL_SPK), @@ -7670,7 +5825,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 SND_PCI_QUIRK(0x2782, 0xa212, "Lunnen Ground 14", ALC269VC_FIXUP_LUNNEN_GROUND_14), SND_PCI_QUIRK(0x7017, 0x2014, "Star Labs StarFighter", ALC233_FIXUP_STARLABS_STARFIGHTER), SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), -@@ -8159,6 +8302,7 @@ static const struct hda_quirk alc269_fixup_vendor_tbl[] = { +@@ -8168,6 +8302,7 @@ static const struct hda_quirk alc269_fixup_vendor_tbl[] = { SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo XPAD", ALC269_FIXUP_LENOVO_XPAD_ACPI), SND_PCI_QUIRK_VENDOR(0x19e5, "Huawei Matebook", ALC255_FIXUP_MIC_MUTE_LED), @@ -7678,7 +5833,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 {} }; -@@ -8298,6 +8442,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { +@@ -8307,6 +8442,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC2XX_FIXUP_HEADSET_MIC, .name = "alc2xx-fixup-headset-mic"}, {.id = ALC245_FIXUP_BASS_HP_DAC, .name = "alc245-fixup-bass-hp-dac"}, {.id = ALC256_FIXUP_HONOR_MRB_XXX_M1020_AUDIO, .name = "alc256-honor-mrb-xxx-m1020-audio"}, @@ -7686,7 +5841,7 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 {} }; #define ALC225_STANDARD_PINS \ -@@ -8903,6 +9048,7 @@ static int alc269_probe(struct hda_codec *codec, const struct hda_device_id *id) +@@ -8912,6 +9048,7 @@ static int alc269_probe(struct hda_codec *codec, const struct hda_device_id *id) spec->init_hook = alc256_init; spec->gen.mixer_nid = 0; /* ALC256 does not have any loopback mixer path */ if (codec->core.vendor_id == 0x10ec0236 && @@ -8476,49 +6631,6 @@ diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/s acpi_dev_put(adev); return ret; -@@ -173,13 +171,13 @@ static void tas2781_hda_playback_hook(struct device *dev, int action) - case HDA_GEN_PCM_ACT_OPEN: - pm_runtime_get_sync(dev); - scoped_guard(mutex, &tas_hda->priv->codec_lock) { -- tasdevice_tuning_switch(tas_hda->priv, 0); -+ tasdevice_tuning_switch(tas_hda->priv, 0, false); - tas_hda->priv->playback_started = true; - } - break; - case HDA_GEN_PCM_ACT_CLOSE: - scoped_guard(mutex, &tas_hda->priv->codec_lock) { -- tasdevice_tuning_switch(tas_hda->priv, 1); -+ tasdevice_tuning_switch(tas_hda->priv, 1, false); - tas_hda->priv->playback_started = false; - } - -@@ -722,7 +720,7 @@ static int tas2781_runtime_suspend(struct device *dev) - * Stop the playback if it's unused. - */ - if (tas_hda->priv->playback_started) { -- tasdevice_tuning_switch(tas_hda->priv, 1); -+ tasdevice_tuning_switch(tas_hda->priv, 1, false); - tas_hda->priv->playback_started = false; - } - -@@ -752,7 +750,7 @@ static int tas2781_system_suspend(struct device *dev) - - /* Shutdown chip before system suspend */ - if (tas_hda->priv->playback_started) -- tasdevice_tuning_switch(tas_hda->priv, 1); -+ tasdevice_tuning_switch(tas_hda->priv, 1, false); - - /* - * Reset GPIO may be shared, so cannot reset here. -@@ -785,7 +783,7 @@ static int tas2781_system_resume(struct device *dev) - TASDEVICE_BIN_BLK_PRE_POWER_UP); - - if (tas_hda->priv->playback_started) -- tasdevice_tuning_switch(tas_hda->priv, 0); -+ tasdevice_tuning_switch(tas_hda->priv, 0, false); - - return 0; - } diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c --- a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c @@ -8556,56 +6668,6 @@ diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c b/sound/hda/codecs/s return ret; } -@@ -400,11 +399,11 @@ static void tas2781_hda_playback_hook(struct device *dev, int action) - pm_runtime_get_sync(dev); - guard(mutex)(&tas_priv->codec_lock); - if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK) -- tasdevice_tuning_switch(tas_hda->priv, 0); -+ tasdevice_tuning_switch(tas_hda->priv, 0, false); - } else if (action == HDA_GEN_PCM_ACT_CLOSE) { - guard(mutex)(&tas_priv->codec_lock); - if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK) -- tasdevice_tuning_switch(tas_priv, 1); -+ tasdevice_tuning_switch(tas_priv, 1, false); - pm_runtime_put_autosuspend(dev); - } - } -@@ -848,7 +847,7 @@ static int tas2781_runtime_suspend(struct device *dev) - - if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK - && tas_priv->playback_started) -- tasdevice_tuning_switch(tas_priv, 1); -+ tasdevice_tuning_switch(tas_priv, 1, false); - - tas_priv->tasdevice[tas_priv->index].cur_book = -1; - tas_priv->tasdevice[tas_priv->index].cur_conf = -1; -@@ -865,7 +864,7 @@ static int tas2781_runtime_resume(struct device *dev) - - if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK - && tas_priv->playback_started) -- tasdevice_tuning_switch(tas_priv, 0); -+ tasdevice_tuning_switch(tas_priv, 0, false); - - return 0; - } -@@ -883,7 +882,7 @@ static int tas2781_system_suspend(struct device *dev) - /* Shutdown chip before system suspend */ - if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK - && tas_priv->playback_started) -- tasdevice_tuning_switch(tas_priv, 1); -+ tasdevice_tuning_switch(tas_priv, 1, false); - - return 0; - } -@@ -918,7 +917,7 @@ static int tas2781_system_resume(struct device *dev) - tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK; - - if (tas_priv->playback_started) -- tasdevice_tuning_switch(tas_priv, 0); -+ tasdevice_tuning_switch(tas_priv, 0, false); - } - - return ret; diff --git a/sound/hda/common/auto_parser.c b/sound/hda/common/auto_parser.c --- a/sound/hda/common/auto_parser.c +++ b/sound/hda/common/auto_parser.c @@ -8762,7 +6824,7 @@ diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c guard(mutex)(&bus->core.cmd_mutex); if (flags & HDA_RW_NO_RESPONSE_FALLBACK) bus->no_response_fallback = 1; -@@ -2967,8 +2973,11 @@ static void hda_codec_pm_complete(struct device *dev) +@@ -2969,8 +2975,11 @@ static void hda_codec_pm_complete(struct device *dev) dev->power.power_state = PMSG_RESUME; if (pm_runtime_suspended(dev) && (codec->jackpoll_interval || @@ -8775,7 +6837,7 @@ diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c } static int hda_codec_pm_suspend(struct device *dev) -@@ -3370,7 +3379,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, +@@ -3372,7 +3381,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, for (; knew->name; knew++) { struct snd_kcontrol *kctl; int addr = 0, idx = 0; @@ -8827,29 +6889,6 @@ diff --git a/sound/hda/common/jack.c b/sound/hda/common/jack.c err = snd_hda_jack_add_kctl(codec, nid, name, phantom_jack, 0, NULL); if (err < 0) return err; -diff --git a/sound/hda/common/proc.c b/sound/hda/common/proc.c ---- a/sound/hda/common/proc.c -+++ b/sound/hda/common/proc.c -@@ -624,7 +624,7 @@ static void print_conn_list(struct snd_info_buffer *buffer, - /* Get Cache connections info */ - cache_len = snd_hda_get_conn_list(codec, nid, &list); - if (cache_len >= 0 && (cache_len != conn_len || -- memcmp(list, conn, conn_len) != 0)) { -+ memcmp(list, conn, conn_len * sizeof(*conn)) != 0)) { - snd_iprintf(buffer, " In-driver Connection: %d\n", cache_len); - if (cache_len > 0) { - snd_iprintf(buffer, " "); -diff --git a/sound/hda/controllers/Kconfig b/sound/hda/controllers/Kconfig ---- a/sound/hda/controllers/Kconfig -+++ b/sound/hda/controllers/Kconfig -@@ -33,6 +33,7 @@ config SND_HDA_TEGRA - config SND_HDA_CIX_IPBLOQ - tristate "CIX IPBLOQ HD Audio" - depends on ARCH_CIX || COMPILE_TEST -+ depends on ARCH_DMA_ADDR_T_64BIT - select SND_HDA - select SND_HDA_ALIGNED_MMIO - help diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c --- a/sound/hda/controllers/intel.c +++ b/sound/hda/controllers/intel.c @@ -9012,7 +7051,7 @@ diff --git a/sound/hda/core/controller.c b/sound/hda/core/controller.c diff --git a/sound/hda/core/device.c b/sound/hda/core/device.c --- a/sound/hda/core/device.c +++ b/sound/hda/core/device.c -@@ -663,6 +663,7 @@ static const struct hda_vendor_id hda_vendor_ids[] = { +@@ -664,6 +664,7 @@ static const struct hda_vendor_id hda_vendor_ids[] = { { 0x1af4, "QEMU" }, { 0x1fa8, "Senarytech" }, { 0x434d, "C-Media" }, @@ -9020,7 +7059,7 @@ diff --git a/sound/hda/core/device.c b/sound/hda/core/device.c { 0x8086, "Intel" }, { 0x8384, "SigmaTel" }, {} /* terminator */ -@@ -765,7 +766,7 @@ unsigned int snd_hdac_stream_format_bits(snd_pcm_format_t format, snd_pcm_subfor +@@ -766,7 +767,7 @@ unsigned int snd_hdac_stream_format_bits(snd_pcm_format_t format, snd_pcm_subfor params_set_format(¶ms, snd_hdac_format_normalize(format)); snd_mask_set(hw_param_mask(¶ms, SNDRV_PCM_HW_PARAM_SUBFORMAT), @@ -9304,21 +7343,6 @@ diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c static const snd_pcm_format_t hpi_to_alsa_formats[] = { INVALID_FORMAT, /* INVALID */ -diff --git a/sound/pci/asihpi/hpi6000.c b/sound/pci/asihpi/hpi6000.c ---- a/sound/pci/asihpi/hpi6000.c -+++ b/sound/pci/asihpi/hpi6000.c -@@ -537,6 +537,11 @@ static short create_adapter_obj(struct hpi_adapter_obj *pao, - hr1.size = sizeof(hr1); - - error = hpi6000_message_response_sequence(pao, 0, &hm, &hr0); -+ if (error) { -+ HPI_DEBUG_LOG(ERROR, "message transport error %d\n", -+ error); -+ return error; -+ } - if (hr0.error) { - HPI_DEBUG_LOG(DEBUG, "message error %d\n", hr0.error); - return hr0.error; diff --git a/sound/pci/asihpi/hpidspcd.c b/sound/pci/asihpi/hpidspcd.c --- a/sound/pci/asihpi/hpidspcd.c +++ b/sound/pci/asihpi/hpidspcd.c @@ -9715,44 +7739,6 @@ diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c } rc = sis_alloc_suspend(sis); -diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c ---- a/sound/pci/via82xx_modem.c -+++ b/sound/pci/via82xx_modem.c -@@ -573,24 +573,18 @@ static inline unsigned int calc_linear_pos(struct via82xx_modem *chip, - viadev->bufsize2, viadev->idx_table[idx].offset, - viadev->idx_table[idx].size, count); - #endif -- if (count && size < count) { -+ if (! count) -+ /* bogus count 0 on the DMA boundary? */ -+ res = viadev->idx_table[idx].offset; -+ else -+ /* count register returns full size -+ * when end of buffer is reached -+ */ -+ res = viadev->idx_table[idx].offset + size; -+ if (check_invalid_pos(viadev, res)) { - dev_dbg(chip->card->dev, -- "invalid via82xx_cur_ptr, using last valid pointer\n"); -+ "invalid via82xx_cur_ptr (2), using last valid pointer\n"); - res = viadev->lastpos; -- } else { -- if (! count) -- /* bogus count 0 on the DMA boundary? */ -- res = viadev->idx_table[idx].offset; -- else -- /* count register returns full size -- * when end of buffer is reached -- */ -- res = viadev->idx_table[idx].offset + size; -- if (check_invalid_pos(viadev, res)) { -- dev_dbg(chip->card->dev, -- "invalid via82xx_cur_ptr (2), using last valid pointer\n"); -- res = viadev->lastpos; -- } - } - } - viadev->lastpos = res; /* remember the last position */ diff --git a/sound/sh/aica.c b/sound/sh/aica.c --- a/sound/sh/aica.c +++ b/sound/sh/aica.c @@ -9775,1351 +7761,10 @@ diff --git a/sound/sh/aica.c b/sound/sh/aica.c return err; } -diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c ---- a/sound/soc/amd/acp-config.c -+++ b/sound/soc/amd/acp-config.c -@@ -63,6 +63,13 @@ static const struct dmi_system_id acp70_acpi_flag_override_table[] = { - DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook 18 M1807GA"), - }, - }, -+ { -+ /* HP OmniBook X Flip 14-kc0xxx (Strix Point, ACP 7.2) */ -+ .matches = { -+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"), -+ DMI_MATCH(DMI_BOARD_NAME, "8EA1"), -+ }, -+ }, - {} - }; - -@@ -379,5 +386,17 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[] = { - }; - EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sof_machines); - -+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp7x_sof_machines[] = { -+ { -+ .id = "AMDI1010", -+ .drv_name = "acp7x-dsp", -+ .pdata = &acp_quirk_data, -+ .fw_filename = "sof-acp7x.ri", -+ .sof_tplg_filename = "sof-acp7x.tplg", -+ }, -+ {}, -+}; -+EXPORT_SYMBOL(snd_soc_acpi_amd_acp7x_sof_machines); -+ - MODULE_DESCRIPTION("AMD ACP Machine Configuration Module"); - MODULE_LICENSE("Dual BSD/GPL"); -diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c ---- a/sound/soc/amd/acp-pcm-dma.c -+++ b/sound/soc/amd/acp-pcm-dma.c -@@ -691,12 +691,10 @@ static irqreturn_t dma_irq_handler(int irq, void *arg) - { - u16 dscr_idx; - u32 intr_flag, ext_intr_status; -- struct audio_drv_data *irq_data; -+ struct audio_drv_data *irq_data = arg; - void __iomem *acp_mmio; -- struct device *dev = arg; - bool valid_irq = false; - -- irq_data = dev_get_drvdata(dev); - acp_mmio = irq_data->acp_mmio; - - ext_intr_status = acp_reg_read(acp_mmio, mmACP_EXTERNAL_INTR_STAT); -@@ -1291,10 +1289,10 @@ static int acp_audio_probe(struct platform_device *pdev) - - irq = platform_get_irq(pdev, 0); - if (irq < 0) -- return -ENODEV; -+ return irq; - - status = devm_request_irq(&pdev->dev, irq, dma_irq_handler, -- 0, "ACP_IRQ", &pdev->dev); -+ 0, "ACP_IRQ", audio_drv_data); - if (status) { - dev_err(&pdev->dev, "ACP IRQ request failed\n"); - return status; -diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c ---- a/sound/soc/amd/acp/acp-i2s.c -+++ b/sound/soc/amd/acp/acp-i2s.c -@@ -686,6 +686,10 @@ static int acp_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_d - return 0; - } - -+static const u64 acp_i2s_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A; -+ - const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = { - .startup = acp_i2s_startup, - .hw_params = acp_i2s_hwparams, -@@ -693,6 +697,8 @@ const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = { - .trigger = acp_i2s_trigger, - .set_fmt = acp_i2s_set_fmt, - .set_tdm_slot = acp_i2s_set_tdm_slot, -+ .auto_selectable_formats = &acp_i2s_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - EXPORT_SYMBOL_NS_GPL(asoc_acp_cpu_dai_ops, "SND_SOC_ACP_COMMON"); - -diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c ---- a/sound/soc/amd/acp/acp-mach-common.c -+++ b/sound/soc/amd/acp/acp-mach-common.c -@@ -938,15 +938,10 @@ static int acp_max98388_hw_params(struct snd_pcm_substream *substream, - struct snd_soc_dai *codec_dai = - snd_soc_card_get_codec_dai(card, - MAX98388_CODEC_DAI); -- int ret; - -- ret = snd_soc_dai_set_fmt(codec_dai, -- SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_I2S | -- SND_SOC_DAIFMT_NB_NF); -- if (ret < 0) -- return ret; -- -- return ret; -+ return snd_soc_dai_set_fmt(codec_dai, -+ SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_I2S | -+ SND_SOC_DAIFMT_NB_NF); - } - - static const struct snd_soc_ops acp_max98388_ops = { -diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c ---- a/sound/soc/amd/acp/acp-pci.c -+++ b/sound/soc/amd/acp/acp-pci.c -@@ -118,17 +118,10 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id - if (!chip) - return -ENOMEM; - -- if (pci_enable_device(pci)) -+ if (pcim_enable_device(pci)) - return dev_err_probe(&pci->dev, -ENODEV, - "pci_enable_device failed\n"); - -- ret = pci_request_regions(pci, "AMD ACP3x audio"); -- if (ret < 0) { -- dev_err(&pci->dev, "pci_request_regions failed\n"); -- ret = -ENOMEM; -- goto disable_pci; -- } -- - pci_set_master(pci); - - chip->acp_rev = pci->revision; -@@ -161,24 +154,21 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id - break; - default: - dev_err(dev, "Unsupported device revision:0x%x\n", pci->revision); -- ret = -EINVAL; -- goto release_regions; -+ return -EINVAL; - } - chip->flag = flag; - - addr = pci_resource_start(pci, 0); -- chip->base = devm_ioremap(&pci->dev, addr, pci_resource_len(pci, 0)); -- if (!chip->base) { -- ret = -ENOMEM; -- goto release_regions; -- } -+ chip->base = pcim_iomap_region(pci, 0, "AMD ACP3x audio"); -+ if (IS_ERR(chip->base)) -+ return PTR_ERR(chip->base); - - chip->addr = addr; - - chip->acp_hw_ops_init(chip); - ret = acp_hw_init(chip); - if (ret) -- goto release_regions; -+ goto de_init; - - ret = devm_request_irq(dev, pci->irq, irq_handler, - IRQF_SHARED, "ACP_I2S_IRQ", chip); -@@ -214,10 +204,6 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id - - de_init: - acp_hw_deinit(chip); --release_regions: -- pci_release_regions(pci); --disable_pci: -- pci_disable_device(pci); - - return ret; - }; -diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c ---- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c -+++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c -@@ -432,7 +432,7 @@ static int soc_card_dai_links_create(struct snd_soc_card *card) - if (!soc_aux) - return -ENOMEM; - -- ret = asoc_sdw_parse_sdw_endpoints(card, soc_aux, soc_dais, soc_ends, &num_confs); -+ ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, soc_aux, soc_dais, soc_ends, &num_confs); - if (ret < 0) - return ret; - -@@ -521,15 +521,15 @@ static int mc_probe(struct platform_device *pdev) - dmi_check_system(soc_sdw_quirk_table); - - if (quirk_override != -1) { -- dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n", -+ dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n", - soc_sdw_quirk, quirk_override); - soc_sdw_quirk = quirk_override; - } - -- log_quirks(card->dev); -+ log_quirks(&pdev->dev); - - ctx->mc_quirk = soc_sdw_quirk; -- dev_dbg(card->dev, "legacy quirk 0x%lx\n", ctx->mc_quirk); -+ dev_dbg(&pdev->dev, "legacy quirk 0x%lx\n", ctx->mc_quirk); - /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ - for (i = 0; i < ctx->codec_info_list_count; i++) - codec_info_list[i].amp_num = 0; -@@ -546,12 +546,12 @@ static int mc_probe(struct platform_device *pdev) - for (i = 0; i < ctx->codec_info_list_count; i++) - amp_num += codec_info_list[i].amp_num; - -- card->components = devm_kasprintf(card->dev, GFP_KERNEL, -+ card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, - " cfg-amp:%d", amp_num); - if (!card->components) - return -ENOMEM; - if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC) { -- card->components = devm_kasprintf(card->dev, GFP_KERNEL, -+ card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, - "%s mic:acp-dmic cfg-mics:%d", - card->components, - 1); -@@ -560,9 +560,9 @@ static int mc_probe(struct platform_device *pdev) - } - - /* Register the card */ -- ret = devm_snd_soc_register_card(card->dev, card); -+ ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) { -- dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret); -+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed %d\n", ret); - asoc_sdw_mc_dailink_exit_loop(card); - return ret; - } -@@ -580,8 +580,8 @@ static void mc_remove(struct platform_device *pdev) - } - - static const struct platform_device_id mc_id_table[] = { -- { "amd_sdw", }, -- {} -+ { .name = "amd_sdw" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, mc_id_table); - -diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c ---- a/sound/soc/amd/acp/acp-sdw-sof-mach.c -+++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c -@@ -303,7 +303,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) - if (!sof_aux) - return -ENOMEM; - -- ret = asoc_sdw_parse_sdw_endpoints(card, sof_aux, sof_dais, sof_ends, &num_devs); -+ ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_devs); - if (ret < 0) - return ret; - -@@ -388,12 +388,12 @@ static int mc_probe(struct platform_device *pdev) - dmi_check_system(sof_sdw_quirk_table); - - if (quirk_override != -1) { -- dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n", -+ dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n", - sof_sdw_quirk, quirk_override); - sof_sdw_quirk = quirk_override; - } - -- log_quirks(card->dev); -+ log_quirks(&pdev->dev); - - ctx->mc_quirk = sof_sdw_quirk; - /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ -@@ -412,15 +412,15 @@ static int mc_probe(struct platform_device *pdev) - for (i = 0; i < ctx->codec_info_list_count; i++) - amp_num += codec_info_list[i].amp_num; - -- card->components = devm_kasprintf(card->dev, GFP_KERNEL, -+ card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, - " cfg-amp:%d", amp_num); - if (!card->components) - return -ENOMEM; - - /* Register the card */ -- ret = devm_snd_soc_register_card(card->dev, card); -+ ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) { -- dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret); -+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed %d\n", ret); - asoc_sdw_mc_dailink_exit_loop(card); - return ret; - } -@@ -438,8 +438,8 @@ static void mc_remove(struct platform_device *pdev) - } - - static const struct platform_device_id mc_id_table[] = { -- { "amd_sof_sdw", }, -- {} -+ { .name = "amd_sof_sdw" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, mc_id_table); - -diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c ---- a/sound/soc/amd/acp/acp-sof-mach.c -+++ b/sound/soc/amd/acp/acp-sof-mach.c -@@ -128,31 +128,31 @@ static int acp_sof_probe(struct platform_device *pdev) - static const struct platform_device_id board_ids[] = { - { - .name = "rt5682-rt1019", -- .driver_data = (kernel_ulong_t)&sof_rt5682_rt1019_data -+ .driver_data = (kernel_ulong_t)&sof_rt5682_rt1019_data, - }, - { - .name = "rt5682-max", -- .driver_data = (kernel_ulong_t)&sof_rt5682_max_data -+ .driver_data = (kernel_ulong_t)&sof_rt5682_max_data, - }, - { - .name = "rt5682s-max", -- .driver_data = (kernel_ulong_t)&sof_rt5682s_max_data -+ .driver_data = (kernel_ulong_t)&sof_rt5682s_max_data, - }, - { - .name = "rt5682s-rt1019", -- .driver_data = (kernel_ulong_t)&sof_rt5682s_rt1019_data -+ .driver_data = (kernel_ulong_t)&sof_rt5682s_rt1019_data, - }, - { - .name = "nau8825-max", -- .driver_data = (kernel_ulong_t)&sof_nau8825_data -+ .driver_data = (kernel_ulong_t)&sof_nau8825_data, - }, - { - .name = "rt5682s-hs-rt1019", -- .driver_data = (kernel_ulong_t)&sof_rt5682s_hs_rt1019_data -+ .driver_data = (kernel_ulong_t)&sof_rt5682s_hs_rt1019_data, - }, - { - .name = "nau8821-max", -- .driver_data = (kernel_ulong_t)&sof_nau8821_max98388_data -+ .driver_data = (kernel_ulong_t)&sof_nau8821_max98388_data, - }, - { } - }; -diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c ---- a/sound/soc/amd/acp/amd-acp70-acpi-match.c -+++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c -@@ -659,6 +659,56 @@ static const struct snd_soc_acpi_link_adr acp70_rt721_l1u0_tas2783x2_l1u8b[] = { - {} - }; - -+static const struct snd_soc_acpi_adr_device rt712_vb_l1u0_adr[] = { -+ { -+ .adr = 0x000130025D071201ull, -+ /* -+ * On this platform speakers are provided by two TAS2783 amps, -+ * so the AIF2 amp path is left unused: jack + DMIC only. -+ */ -+ .num_endpoints = ARRAY_SIZE(jack_dmic_endpoints), -+ .endpoints = jack_dmic_endpoints, -+ .name_prefix = "rt712" -+ } -+}; -+ -+/* -+ * Unique ID 0xC drives the left speaker and 0x9 the right one. The order of the -+ * entries matters as much as the endpoints: asoc_sdw_parse_sdw_endpoints() -+ * appends them to the dailink in array order and sdw_compute_slave_ports() -+ * hands out payload block offsets in that same order, so the first entry is the -+ * one that receives channel 0. -+ */ -+static const struct snd_soc_acpi_adr_device tas2783x2_l0u9c_adr[] = { -+ { -+ .adr = 0x00003C0102000001ull, -+ .num_endpoints = 1, -+ .endpoints = &spk_l_endpoint, -+ .name_prefix = "tas2783-1", -+ }, -+ { -+ .adr = 0x0000390102000001ull, -+ .num_endpoints = 1, -+ .endpoints = &spk_r_endpoint, -+ .name_prefix = "tas2783-2", -+ }, -+}; -+ -+/* HP OmniBook X Flip 14-kc0xxx (board 8EA1) */ -+static const struct snd_soc_acpi_link_adr acp70_tas2783x2_l0u9c_rt712_vb_l1u0[] = { -+ { -+ .mask = BIT(0), -+ .num_adr = ARRAY_SIZE(tas2783x2_l0u9c_adr), -+ .adr_d = tas2783x2_l0u9c_adr, -+ }, -+ { -+ .mask = BIT(1), -+ .num_adr = ARRAY_SIZE(rt712_vb_l1u0_adr), -+ .adr_d = rt712_vb_l1u0_adr, -+ }, -+ {} -+}; -+ - static const struct snd_soc_acpi_endpoint rt721_endpoints[] = { - { /* Jack Playback/Capture Endpoint (AIF1) */ - .num = 0, -@@ -704,6 +754,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = { - .links = acp70_tas2783_2, - .drv_name = "amd_sdw", - }, -+ { -+ .link_mask = BIT(0) | BIT(1), -+ .links = acp70_tas2783x2_l0u9c_rt712_vb_l1u0, -+ .machine_check = snd_soc_acpi_amd_sdca_is_device_rt712_vb, -+ .drv_name = "amd_sdw", -+ }, - { - .link_mask = BIT(0) | BIT(1), - .links = acp70_rt1320_l0_rt722_l1, -diff --git a/sound/soc/amd/mach-config.h b/sound/soc/amd/mach-config.h ---- a/sound/soc/amd/mach-config.h -+++ b/sound/soc/amd/mach-config.h -@@ -28,6 +28,7 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_sdw_machines[]; - extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[]; - extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[]; - extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_sdw_machines[]; -+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp7x_sof_machines[]; - - struct config_entry { - u32 flags; -diff --git a/sound/soc/amd/raven/acp3x-i2s.c b/sound/soc/amd/raven/acp3x-i2s.c ---- a/sound/soc/amd/raven/acp3x-i2s.c -+++ b/sound/soc/amd/raven/acp3x-i2s.c -@@ -250,11 +250,17 @@ static int acp3x_i2s_trigger(struct snd_pcm_substream *substream, - return ret; - } - -+static const u64 acp3x_i2s_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A; -+ - static const struct snd_soc_dai_ops acp3x_i2s_dai_ops = { - .hw_params = acp3x_i2s_hwparams, - .trigger = acp3x_i2s_trigger, - .set_fmt = acp3x_i2s_set_fmt, - .set_tdm_slot = acp3x_i2s_set_tdm_slot, -+ .auto_selectable_formats = &acp3x_i2s_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static const struct snd_soc_component_driver acp3x_dai_component = { -diff --git a/sound/soc/amd/vangogh/acp5x-i2s.c b/sound/soc/amd/vangogh/acp5x-i2s.c ---- a/sound/soc/amd/vangogh/acp5x-i2s.c -+++ b/sound/soc/amd/vangogh/acp5x-i2s.c -@@ -337,11 +337,17 @@ static int acp5x_i2s_trigger(struct snd_pcm_substream *substream, - return ret; - } - -+static const u64 acp5x_i2s_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A; -+ - static const struct snd_soc_dai_ops acp5x_i2s_dai_ops = { - .hw_params = acp5x_i2s_hwparams, - .trigger = acp5x_i2s_trigger, - .set_fmt = acp5x_i2s_set_fmt, - .set_tdm_slot = acp5x_i2s_set_tdm_slot, -+ .auto_selectable_formats = &acp5x_i2s_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static const struct snd_soc_component_driver acp5x_dai_component = { -diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c ---- a/sound/soc/amd/yc/acp6x-mach.c -+++ b/sound/soc/amd/yc/acp6x-mach.c -@@ -795,12 +795,12 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { - } - }, - { -- .driver_data = &acp6x_card, -- .matches = { -- DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), -- DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook_ASUSLaptop M6501RR_M6501RR"), -- } -- }, -+ .driver_data = &acp6x_card, -+ .matches = { -+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), -+ DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook_ASUSLaptop M6501RR_M6501RR"), -+ } -+ }, - { - .driver_data = &acp6x_card, - .matches = { -diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c ---- a/sound/soc/apple/mca.c -+++ b/sound/soc/apple/mca.c -@@ -133,12 +133,17 @@ struct mca_cluster { - struct clk *clk_parent; - struct dma_chan *dma_chans[SNDRV_PCM_STREAM_LAST + 1]; - -- bool port_started[SNDRV_PCM_STREAM_LAST + 1]; -- int port_driver; /* The cluster driving this cluster's port */ -+ bool clk_provider; -+ -+ bool port_clk_started[SNDRV_PCM_STREAM_LAST + 1]; -+ int port_clk_driver; /* The cluster driving this cluster's port */ - - bool clocks_in_use[SNDRV_PCM_STREAM_LAST + 1]; - struct device_link *pd_link; - -+ /* In case of clock consumer FE */ -+ int syncgen_in_use; -+ - unsigned int bclk_ratio; - - /* Masks etc. picked up via the set_tdm_slot method */ -@@ -157,7 +162,7 @@ struct mca_data { - struct reset_control *rstc; - struct device_link *pd_link; - -- /* Mutex for accessing port_driver of foreign clusters */ -+ /* Mutex for accessing port_clk_driver of foreign clusters */ - struct mutex port_mutex; - - int nclusters; -@@ -210,16 +215,22 @@ static void mca_fe_early_trigger(struct snd_pcm_substream *substream, int cmd, - SERDES_STATUS_EN | SERDES_STATUS_RST, - SERDES_STATUS_RST); - /* -- * Experiments suggest that it takes at most ~1 us -- * for the bit to clear, so wait 2 us for good measure. -+ * The SERDES cluster needs a bit of time to reset itself -+ * and settle before we start poking it. This is... slow... - */ -- udelay(2); -+ udelay(25); - WARN_ON(readl_relaxed(cl->base + serdes_unit + REG_SERDES_STATUS) & - SERDES_STATUS_RST); - mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL, - FIELD_PREP(SERDES_CONF_SYNC_SEL, 0)); - mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL, - FIELD_PREP(SERDES_CONF_SYNC_SEL, cl->no + 1)); -+ /* -+ * ADMAC gets started right after this. This delay seems -+ * to be needed for that to be reliable, e.g. ensure the -+ * clock is stable? -+ */ -+ udelay(100); - break; - default: - break; -@@ -256,11 +267,29 @@ static int mca_fe_trigger(struct snd_pcm_substream *substream, int cmd, - return 0; - } - -+static int mca_fe_get_portmask(struct snd_pcm_substream *substream) -+{ -+ struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); -+ struct snd_soc_dpcm *dpcm; -+ int mask = 0; -+ -+ for_each_dpcm_be(fe, substream->stream, dpcm) { -+ int no = mca_dai_to_cluster(snd_soc_rtd_to_cpu(dpcm->be, 0))->no; -+ -+ mask |= 1 << no; -+ } -+ -+ return mask; -+} -+ - static int mca_fe_enable_clocks(struct mca_cluster *cl) - { - struct mca_data *mca = cl->host; - int ret; - -+ if (!cl->clk_provider) -+ return -EINVAL; -+ - ret = clk_prepare_enable(cl->clk_parent); - if (ret) { - dev_err(mca->dev, -@@ -311,7 +340,7 @@ static bool mca_fe_clocks_in_use(struct mca_cluster *cl) - for (i = 0; i < mca->nclusters; i++) { - be_cl = &mca->clusters[i]; - -- if (be_cl->port_driver != cl->no) -+ if (be_cl->port_clk_driver != cl->no) - continue; - - for_each_pcm_streams(stream) { -@@ -323,36 +352,6 @@ static bool mca_fe_clocks_in_use(struct mca_cluster *cl) - return false; - } - --static int mca_be_prepare(struct snd_pcm_substream *substream, -- struct snd_soc_dai *dai) --{ -- struct mca_cluster *cl = mca_dai_to_cluster(dai); -- struct mca_data *mca = cl->host; -- struct mca_cluster *fe_cl; -- int ret; -- -- if (cl->port_driver < 0) -- return -EINVAL; -- -- fe_cl = &mca->clusters[cl->port_driver]; -- -- /* -- * Typically the CODECs we are paired with will require clocks -- * to be present at time of unmute with the 'mute_stream' op -- * or at time of DAPM widget power-up. We need to enable clocks -- * here at the latest (frontend prepare would be too late). -- */ -- if (!mca_fe_clocks_in_use(fe_cl)) { -- ret = mca_fe_enable_clocks(fe_cl); -- if (ret < 0) -- return ret; -- } -- -- cl->clocks_in_use[substream->stream] = true; -- -- return 0; --} -- - static int mca_be_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) - { -@@ -360,7 +359,7 @@ static int mca_be_hw_free(struct snd_pcm_substream *substream, - struct mca_data *mca = cl->host; - struct mca_cluster *fe_cl; - -- if (cl->port_driver < 0) -+ if (cl->port_clk_driver < 0) - return -EINVAL; - - /* -@@ -368,7 +367,7 @@ static int mca_be_hw_free(struct snd_pcm_substream *substream, - * belong to the same PCM, accesses should have been - * synchronized at ASoC level. - */ -- fe_cl = &mca->clusters[cl->port_driver]; -+ fe_cl = &mca->clusters[cl->port_clk_driver]; - if (!mca_fe_clocks_in_use(fe_cl)) - return 0; /* Nothing to do */ - -@@ -380,6 +379,56 @@ static int mca_be_hw_free(struct snd_pcm_substream *substream, - return 0; - } - -+static int mca_fe_prepare(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ struct mca_cluster *cl = mca_dai_to_cluster(dai); -+ struct mca_data *mca = cl->host; -+ -+ if (cl->clk_provider) -+ return 0; -+ -+ /* Turn on the cluster power domain if not already in use */ -+ if (!cl->syncgen_in_use) { -+ int port = ffs(mca_fe_get_portmask(substream)); -+ -+ cl->pd_link = device_link_add(mca->dev, cl->pd_dev, -+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | -+ DL_FLAG_RPM_ACTIVE); -+ if (!cl->pd_link) { -+ dev_err(mca->dev, -+ "cluster %d: unable to prop-up power domain\n", cl->no); -+ return -EINVAL; -+ } -+ -+ mca_modify(cl, REG_SYNCGEN_MCLK_SEL, SYNCGEN_MCLK_SEL, BIT(port)); -+ mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN, -+ SYNCGEN_STATUS_EN); -+ } -+ cl->syncgen_in_use |= 1 << substream->stream; -+ -+ return 0; -+} -+ -+static int mca_fe_hw_free(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ struct mca_cluster *cl = mca_dai_to_cluster(dai); -+ -+ if (cl->clk_provider) -+ return 0; -+ -+ cl->syncgen_in_use &= ~(1 << substream->stream); -+ if (cl->syncgen_in_use) -+ return 0; -+ -+ mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN, 0); -+ if (cl->pd_link) -+ device_link_del(cl->pd_link); -+ -+ return 0; -+} -+ - static unsigned int mca_crop_mask(unsigned int mask, int nchans) - { - while (hweight32(mask) > nchans) -@@ -390,7 +439,7 @@ static unsigned int mca_crop_mask(unsigned int mask, int nchans) - - static int mca_configure_serdes(struct mca_cluster *cl, int serdes_unit, - unsigned int mask, int slots, int nchans, -- int slot_width, bool is_tx, int port) -+ int slot_width, bool is_tx, int portmask) - { - __iomem void *serdes_base = cl->base + serdes_unit; - u32 serdes_conf, serdes_conf_mask; -@@ -449,7 +498,7 @@ static int mca_configure_serdes(struct mca_cluster *cl, int serdes_unit, - serdes_base + REG_RX_SERDES_SLOTMASK); - writel_relaxed(~((u32)mca_crop_mask(mask, nchans)), - serdes_base + REG_RX_SERDES_SLOTMASK + 0x4); -- writel_relaxed(1 << port, -+ writel_relaxed(portmask, - serdes_base + REG_RX_SERDES_PORT); - } - -@@ -505,9 +554,18 @@ static int mca_fe_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) - u32 serdes_conf = 0; - u32 bitstart; - -- if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != -- SND_SOC_DAIFMT_BP_FP) -+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { -+ case SND_SOC_DAIFMT_BP_FP: -+ cl->clk_provider = true; -+ break; -+ -+ case SND_SOC_DAIFMT_BC_FC: -+ cl->clk_provider = false; -+ break; -+ -+ default: - goto err; -+ } - - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: -@@ -564,24 +622,6 @@ static int mca_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) - return 0; - } - --static int mca_fe_get_port(struct snd_pcm_substream *substream) --{ -- struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); -- struct snd_soc_pcm_runtime *be; -- struct snd_soc_dpcm *dpcm; -- -- be = NULL; -- for_each_dpcm_be(fe, substream->stream, dpcm) { -- be = dpcm->be; -- break; -- } -- -- if (!be) -- return -EINVAL; -- -- return mca_dai_to_cluster(snd_soc_rtd_to_cpu(be, 0))->no; --} -- - static int mca_fe_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -@@ -595,7 +635,7 @@ static int mca_fe_hw_params(struct snd_pcm_substream *substream, - unsigned long bclk_ratio; - unsigned int tdm_slots, tdm_slot_width, tdm_mask; - u32 regval, pad; -- int ret, port, nchans_ceiled; -+ int ret, portmask, nchans_ceiled; - - if (!cl->tdm_slot_width) { - /* -@@ -644,13 +684,13 @@ static int mca_fe_hw_params(struct snd_pcm_substream *substream, - tdm_mask = (1 << tdm_slots) - 1; - } - -- port = mca_fe_get_port(substream); -- if (port < 0) -- return port; -+ portmask = mca_fe_get_portmask(substream); -+ if (!portmask) -+ return -EINVAL; - - ret = mca_configure_serdes(cl, is_tx ? CLUSTER_TX_OFF : CLUSTER_RX_OFF, - tdm_mask, tdm_slots, params_channels(params), -- tdm_slot_width, is_tx, port); -+ tdm_slot_width, is_tx, portmask); - if (ret) - return ret; - -@@ -699,6 +739,19 @@ static int mca_fe_hw_params(struct snd_pcm_substream *substream, - return 0; - } - -+static const u64 mca_fe_selectable_formats[] = -+{ -+ /* pattern 1 */ -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_NB_IF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_IF, -+ -+ /* pattern 2 */ -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_NF -+}; -+ - static const struct snd_soc_dai_ops mca_fe_ops = { - .startup = mca_fe_startup, - .set_fmt = mca_fe_set_fmt, -@@ -706,66 +759,128 @@ static const struct snd_soc_dai_ops mca_fe_ops = { - .set_tdm_slot = mca_fe_set_tdm_slot, - .hw_params = mca_fe_hw_params, - .trigger = mca_fe_trigger, -+ .prepare = mca_fe_prepare, -+ .hw_free = mca_fe_hw_free, -+ .auto_selectable_formats = mca_fe_selectable_formats, -+ .num_auto_selectable_formats = ARRAY_SIZE(mca_fe_selectable_formats), - }; - --static bool mca_be_started(struct mca_cluster *cl) -+/* -+ * Is there a FE attached which will be feeding this port's clocks? -+ */ -+static bool mca_be_clk_started(struct mca_cluster *cl) - { - int stream; - - for_each_pcm_streams(stream) -- if (cl->port_started[stream]) -+ if (cl->port_clk_started[stream]) - return true; - return false; - } - -+static struct snd_soc_pcm_runtime *mca_be_get_fe(struct snd_soc_pcm_runtime *be, -+ int stream) -+{ -+ struct snd_soc_pcm_runtime *fe = NULL; -+ struct snd_soc_dpcm *dpcm; -+ -+ for_each_dpcm_fe(be, stream, dpcm) { -+ if (fe && dpcm->fe != fe) { -+ dev_err(be->dev, "many FE per one BE unsupported\n"); -+ return NULL; -+ } -+ -+ fe = dpcm->fe; -+ } -+ -+ return fe; -+} -+ -+static int mca_be_prepare(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream); -+ struct snd_soc_pcm_runtime *fe = mca_be_get_fe(be, substream->stream); -+ struct mca_cluster *cl = mca_dai_to_cluster(dai); -+ struct mca_data *mca = cl->host; -+ struct mca_cluster *fe_cl, *fe_clk_cl; -+ int ret; -+ -+ fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0)); -+ -+ if (!fe_cl->clk_provider) -+ return 0; -+ -+ if (cl->port_clk_driver < 0) -+ return 0; -+ -+ fe_clk_cl = &mca->clusters[cl->port_clk_driver]; -+ -+ /* -+ * Typically the CODECs we are paired with will require clocks -+ * to be present at time of unmute with the 'mute_stream' op -+ * or at time of DAPM widget power-up. We need to enable clocks -+ * here at the latest (frontend prepare would be too late). -+ */ -+ if (!mca_fe_clocks_in_use(fe_clk_cl)) { -+ ret = mca_fe_enable_clocks(fe_clk_cl); -+ if (ret < 0) -+ return ret; -+ } -+ -+ cl->clocks_in_use[substream->stream] = true; -+ -+ return 0; -+} -+ - static int mca_be_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) - { - struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream); -- struct snd_soc_pcm_runtime *fe; -+ struct snd_soc_pcm_runtime *fe = mca_be_get_fe(be, substream->stream); - struct mca_cluster *cl = mca_dai_to_cluster(dai); - struct mca_cluster *fe_cl; - struct mca_data *mca = cl->host; -- struct snd_soc_dpcm *dpcm; -- -- fe = NULL; -- -- for_each_dpcm_fe(be, substream->stream, dpcm) { -- if (fe && dpcm->fe != fe) { -- dev_err(mca->dev, "many FE per one BE unsupported\n"); -- return -EINVAL; -- } -- -- fe = dpcm->fe; -- } - - if (!fe) - return -EINVAL; -- - fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0)); - -- if (mca_be_started(cl)) { -+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { -+ writel_relaxed(PORT_DATA_SEL_TXA(fe_cl->no), -+ cl->base + REG_PORT_DATA_SEL); -+ mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_TX_DATA, -+ PORT_ENABLES_TX_DATA); -+ } -+ -+ if (!fe_cl->clk_provider) -+ return 0; -+ -+ if (mca_be_clk_started(cl)) { - /* - * Port is already started in the other direction. - * Make sure there isn't a conflict with another cluster -- * driving the port. -+ * driving the port clocks. - */ -- if (cl->port_driver != fe_cl->no) -+ if (cl->port_clk_driver != fe_cl->no) - return -EINVAL; - -- cl->port_started[substream->stream] = true; -+ cl->port_clk_started[substream->stream] = true; - return 0; - } - -- writel_relaxed(PORT_ENABLES_CLOCKS | PORT_ENABLES_TX_DATA, -- cl->base + REG_PORT_ENABLES); - writel_relaxed(FIELD_PREP(PORT_CLOCK_SEL, fe_cl->no + 1), - cl->base + REG_PORT_CLOCK_SEL); -+ - writel_relaxed(PORT_DATA_SEL_TXA(fe_cl->no), - cl->base + REG_PORT_DATA_SEL); -+ -+ mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_CLOCKS, -+ PORT_ENABLES_CLOCKS); -+ - scoped_guard(mutex, &mca->port_mutex) -- cl->port_driver = fe_cl->no; -- cl->port_started[substream->stream] = true; -+ cl->port_clk_driver = fe_cl->no; -+ cl->port_clk_started[substream->stream] = true; - - return 0; - } -@@ -773,20 +888,34 @@ static int mca_be_startup(struct snd_pcm_substream *substream, - static void mca_be_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) - { -+ struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream); -+ struct snd_soc_pcm_runtime *fe = mca_be_get_fe(be, substream->stream); - struct mca_cluster *cl = mca_dai_to_cluster(dai); -+ struct mca_cluster *fe_cl; - struct mca_data *mca = cl->host; - -- cl->port_started[substream->stream] = false; -+ if (!fe) -+ return; -+ fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0)); - -- if (!mca_be_started(cl)) { -+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { -+ mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_TX_DATA, 0); -+ writel_relaxed(0, cl->base + REG_PORT_DATA_SEL); -+ } -+ -+ if (!fe_cl->clk_provider) -+ return; -+ -+ cl->port_clk_started[substream->stream] = false; -+ if (!mca_be_clk_started(cl)) { - /* - * Were we the last direction to shutdown? -- * Turn off the lights. -+ * Turn off the lights (clocks). - */ -- writel_relaxed(0, cl->base + REG_PORT_ENABLES); -- writel_relaxed(0, cl->base + REG_PORT_DATA_SEL); -+ mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_CLOCKS, 0); -+ writel_relaxed(0, cl->base + REG_PORT_CLOCK_SEL); - scoped_guard(mutex, &mca->port_mutex) -- cl->port_driver = -1; -+ cl->port_clk_driver = -1; - } - } - -@@ -1092,7 +1221,7 @@ static int apple_mca_probe(struct platform_device *pdev) - cl->host = mca; - cl->no = i; - cl->base = base + CLUSTER_STRIDE * i; -- cl->port_driver = -1; -+ cl->port_clk_driver = -1; - cl->clk_parent = of_clk_get(pdev->dev.of_node, i); - if (IS_ERR(cl->clk_parent)) { - dev_err(&pdev->dev, "unable to obtain clock %d: %ld\n", -diff --git a/sound/soc/atmel/atmel-i2s.c b/sound/soc/atmel/atmel-i2s.c ---- a/sound/soc/atmel/atmel-i2s.c -+++ b/sound/soc/atmel/atmel-i2s.c -@@ -540,12 +540,16 @@ static int atmel_i2s_dai_probe(struct snd_soc_dai *dai) - return 0; - } - -+static const u64 atmel_i2s_selectable_formats = SND_SOC_POSSIBLE_DAIFMT_I2S; -+ - static const struct snd_soc_dai_ops atmel_i2s_dai_ops = { - .probe = atmel_i2s_dai_probe, - .prepare = atmel_i2s_prepare, - .trigger = atmel_i2s_trigger, - .hw_params = atmel_i2s_hw_params, - .set_fmt = atmel_i2s_set_dai_fmt, -+ .auto_selectable_formats = &atmel_i2s_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static struct snd_soc_dai_driver atmel_i2s_dai = { -diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c ---- a/sound/soc/atmel/atmel_ssc_dai.c -+++ b/sound/soc/atmel/atmel_ssc_dai.c -@@ -825,6 +825,11 @@ static int atmel_ssc_resume(struct snd_soc_component *component) - #define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ - SNDRV_PCM_FMTBIT_S32_LE) - -+static const u64 atmel_ssc_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A; -+ - static const struct snd_soc_dai_ops atmel_ssc_dai_ops = { - .startup = atmel_ssc_startup, - .shutdown = atmel_ssc_shutdown, -@@ -833,6 +838,8 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = { - .hw_params = atmel_ssc_hw_params, - .set_fmt = atmel_ssc_set_dai_fmt, - .set_clkdiv = atmel_ssc_set_dai_clkdiv, -+ .auto_selectable_formats = &atmel_ssc_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static struct snd_soc_dai_driver atmel_ssc_dai = { -diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c ---- a/sound/soc/atmel/atmel_wm8904.c -+++ b/sound/soc/atmel/atmel_wm8904.c -@@ -82,32 +82,33 @@ static struct snd_soc_card atmel_asoc_wm8904_card = { - - static int atmel_asoc_wm8904_dt_init(struct platform_device *pdev) - { -- struct device_node *np = pdev->dev.of_node; -+ struct device *dev = &pdev->dev; -+ struct device_node *np = dev->of_node; - struct device_node *codec_np, *cpu_np; - struct snd_soc_card *card = &atmel_asoc_wm8904_card; - struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink; - int ret; - - if (!np) { -- dev_err(&pdev->dev, "only device tree supported\n"); -+ dev_err(dev, "only device tree supported\n"); - return -EINVAL; - } - - ret = snd_soc_of_parse_card_name(card, "atmel,model"); - if (ret) { -- dev_err(&pdev->dev, "failed to parse card name\n"); -+ dev_err(dev, "failed to parse card name\n"); - return ret; - } - - ret = snd_soc_of_parse_audio_routing(card, "atmel,audio-routing"); - if (ret) { -- dev_err(&pdev->dev, "failed to parse audio routing\n"); -+ dev_err(dev, "failed to parse audio routing\n"); - return ret; - } - - cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0); - if (!cpu_np) { -- dev_err(&pdev->dev, "failed to get dai and pcm info\n"); -+ dev_err(dev, "failed to get dai and pcm info\n"); - ret = -EINVAL; - return ret; - } -@@ -117,7 +118,7 @@ static int atmel_asoc_wm8904_dt_init(struct platform_device *pdev) - - codec_np = of_parse_phandle(np, "atmel,audio-codec", 0); - if (!codec_np) { -- dev_err(&pdev->dev, "failed to get codec info\n"); -+ dev_err(dev, "failed to get codec info\n"); - ret = -EINVAL; - return ret; - } -diff --git a/sound/soc/atmel/mchp-i2s-mcc.c b/sound/soc/atmel/mchp-i2s-mcc.c ---- a/sound/soc/atmel/mchp-i2s-mcc.c -+++ b/sound/soc/atmel/mchp-i2s-mcc.c -@@ -912,6 +912,13 @@ static int mchp_i2s_mcc_dai_probe(struct snd_soc_dai *dai) - return 0; - } - -+static const u64 mchp_i2s_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A | -+ SND_SOC_POSSIBLE_DAIFMT_GATED | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF; -+ - static const struct snd_soc_dai_ops mchp_i2s_mcc_dai_ops = { - .probe = mchp_i2s_mcc_dai_probe, - .set_sysclk = mchp_i2s_mcc_set_sysclk, -@@ -922,6 +929,8 @@ static const struct snd_soc_dai_ops mchp_i2s_mcc_dai_ops = { - .hw_free = mchp_i2s_mcc_hw_free, - .set_fmt = mchp_i2s_mcc_set_dai_fmt, - .set_tdm_slot = mchp_i2s_mcc_set_dai_tdm_slot, -+ .auto_selectable_formats = &mchp_i2s_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - #define MCHP_I2SMCC_RATES SNDRV_PCM_RATE_8000_192000 -diff --git a/sound/soc/atmel/mchp-pdmc.c b/sound/soc/atmel/mchp-pdmc.c ---- a/sound/soc/atmel/mchp-pdmc.c -+++ b/sound/soc/atmel/mchp-pdmc.c -@@ -741,6 +741,8 @@ static int mchp_pdmc_pcm_new(struct snd_soc_pcm_runtime *rtd, - return ret; - } - -+static const u64 mchp_selectable_formats = SND_SOC_POSSIBLE_DAIFMT_PDM; -+ - static const struct snd_soc_dai_ops mchp_pdmc_dai_ops = { - .probe = mchp_pdmc_dai_probe, - .set_fmt = mchp_pdmc_set_fmt, -@@ -748,6 +750,8 @@ static const struct snd_soc_dai_ops mchp_pdmc_dai_ops = { - .hw_params = mchp_pdmc_hw_params, - .trigger = mchp_pdmc_trigger, - .pcm_new = &mchp_pdmc_pcm_new, -+ .auto_selectable_formats = &mchp_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static struct snd_soc_dai_driver mchp_pdmc_dai = { -diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c ---- a/sound/soc/au1x/db1200.c -+++ b/sound/soc/au1x/db1200.c -@@ -42,7 +42,7 @@ static const struct platform_device_id db1200_pids[] = { - .name = "db1550-i2s", - .driver_data = 5, - }, -- {}, -+ { } - }; - MODULE_DEVICE_TABLE(platform, db1200_pids); - -diff --git a/sound/soc/au1x/i2sc.c b/sound/soc/au1x/i2sc.c ---- a/sound/soc/au1x/i2sc.c -+++ b/sound/soc/au1x/i2sc.c -@@ -202,11 +202,22 @@ static int au1xi2s_startup(struct snd_pcm_substream *substream, - return 0; - } - -+static const u64 au1xi2s_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_NB_IF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_IF; -+ - static const struct snd_soc_dai_ops au1xi2s_dai_ops = { - .startup = au1xi2s_startup, - .trigger = au1xi2s_trigger, - .hw_params = au1xi2s_hw_params, - .set_fmt = au1xi2s_set_fmt, -+ .auto_selectable_formats = &au1xi2s_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static struct snd_soc_dai_driver au1xi2s_dai_driver = { -diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c ---- a/sound/soc/au1x/psc-ac97.c -+++ b/sound/soc/au1x/psc-ac97.c -@@ -210,9 +210,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, - { - struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai); - unsigned long r, ro, stat; -- int chans, t, stype = substream->stream; -- -- chans = params_channels(params); -+ int t, stype = substream->stream; - - r = ro = __raw_readl(AC97_CFG(pscdata)); - stat = __raw_readl(AC97_STAT(pscdata)); -diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c ---- a/sound/soc/au1x/psc-i2s.c -+++ b/sound/soc/au1x/psc-i2s.c -@@ -262,11 +262,22 @@ static int au1xpsc_i2s_startup(struct snd_pcm_substream *substream, - return 0; - } - -+static const u64 au1xpsc_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_NB_IF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_IF; -+ - static const struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = { - .startup = au1xpsc_i2s_startup, - .trigger = au1xpsc_i2s_trigger, - .hw_params = au1xpsc_i2s_hw_params, - .set_fmt = au1xpsc_i2s_set_fmt, -+ .auto_selectable_formats = &au1xpsc_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static const struct snd_soc_dai_driver au1xpsc_i2s_dai_template = { -diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c ---- a/sound/soc/bcm/bcm2835-i2s.c -+++ b/sound/soc/bcm/bcm2835-i2s.c -@@ -748,6 +748,17 @@ static int bcm2835_i2s_dai_probe(struct snd_soc_dai *dai) - return 0; - } - -+static const u64 bcm2835_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_B | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_NB_IF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_IF; -+ - static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = { - .probe = bcm2835_i2s_dai_probe, - .startup = bcm2835_i2s_startup, -@@ -758,6 +769,8 @@ static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = { - .set_fmt = bcm2835_i2s_set_dai_fmt, - .set_bclk_ratio = bcm2835_i2s_set_dai_bclk_ratio, - .set_tdm_slot = bcm2835_i2s_set_dai_tdm_slot, -+ .auto_selectable_formats = &bcm2835_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static struct snd_soc_dai_driver bcm2835_i2s_dai = { -diff --git a/sound/soc/bcm/cygnus-ssp.c b/sound/soc/bcm/cygnus-ssp.c ---- a/sound/soc/bcm/cygnus-ssp.c -+++ b/sound/soc/bcm/cygnus-ssp.c -@@ -1133,6 +1133,11 @@ static int cygnus_ssp_resume(struct snd_soc_component *component) - #define cygnus_ssp_resume NULL - #endif - -+static const u64 cygnus_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_B; -+ - static const struct snd_soc_dai_ops cygnus_ssp_dai_ops = { - .startup = cygnus_ssp_startup, - .shutdown = cygnus_ssp_shutdown, -@@ -1141,6 +1146,8 @@ static const struct snd_soc_dai_ops cygnus_ssp_dai_ops = { - .set_fmt = cygnus_ssp_set_fmt, - .set_sysclk = cygnus_ssp_set_sysclk, - .set_tdm_slot = cygnus_set_dai_tdm_slot, -+ .auto_selectable_formats = &cygnus_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static const struct snd_soc_dai_ops cygnus_spdif_dai_ops = { -diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c ---- a/sound/soc/cirrus/ep93xx-i2s.c -+++ b/sound/soc/cirrus/ep93xx-i2s.c -@@ -401,6 +401,15 @@ static int ep93xx_i2s_resume(struct snd_soc_component *component) - #define ep93xx_i2s_resume NULL - #endif - -+static const u64 ep93xx_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_NB_IF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_IF; -+ - static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = { - .probe = ep93xx_i2s_dai_probe, - .startup = ep93xx_i2s_startup, -@@ -408,6 +417,8 @@ static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = { - .hw_params = ep93xx_i2s_hw_params, - .set_sysclk = ep93xx_i2s_set_sysclk, - .set_fmt = ep93xx_i2s_set_dai_fmt, -+ .auto_selectable_formats = &ep93xx_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - #define EP93XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S32_LE) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig -@@ -238,6 +238,7 @@ config SND_SOC_ALL_CODECS - imply SND_SOC_RT715_SDCA_SDW - imply SND_SOC_RT721_SDCA_SDW - imply SND_SOC_RT722_SDCA_SDW -+ imply SND_SOC_RT766_SDCA_SDW - imply SND_SOC_RT1308_SDW - imply SND_SOC_RT1316_SDW - imply SND_SOC_RT1318 -@@ -367,6 +368,7 @@ config SND_SOC_ALL_CODECS - imply SND_SOC_WSA881X - imply SND_SOC_WSA883X - imply SND_SOC_WSA884X -+ imply SND_SOC_WSA885X - imply SND_SOC_ZL38060 - help - Normally ASoC codec drivers are only built if a machine driver which -@@ -615,8 +617,8 @@ config SND_SOC_AK4613 - depends on I2C - - config SND_SOC_AK4619 -- tristate "AKM AK4619 CODEC" -- depends on I2C -+ tristate "AKM AK4619 CODEC" -+ depends on I2C - - config SND_SOC_AK4642 - tristate "AKM AK4642 CODEC" -@@ -726,6 +728,10 @@ config SND_SOC_AW87390 +@@ -726,6 +726,10 @@ config SND_SOC_AW87390 sound quality, which is a new high efficiency, low noise, constant large volume, 6th Smart K audio amplifier. @@ -11130,7 +7775,7 @@ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig config SND_SOC_AW88399 tristate "Soc Audio for awinic aw88399" depends on I2C -@@ -733,6 +739,7 @@ config SND_SOC_AW88399 +@@ -733,6 +737,7 @@ config SND_SOC_AW88399 select REGMAP_I2C select GPIOLIB select SND_SOC_AW88395_LIB @@ -11138,127 +7783,6 @@ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig help This option enables support for aw88399 Smart PA. The awinic AW88399 is an I2S/TDM input, high efficiency -@@ -889,6 +896,7 @@ config SND_SOC_CS35L56_SDW - tristate "Cirrus Logic CS35L56 CODEC (SDW)" - depends on SOUNDWIRE - select REGMAP_SOUNDWIRE -+ select IRQ_DOMAIN - select SND_SOC_CS35L56 - select SND_SOC_CS35L56_SHARED - help -@@ -1201,16 +1209,16 @@ config SND_SOC_JZ4725B_CODEC - will be called snd-soc-jz4725b-codec. - - config SND_SOC_JZ4760_CODEC -- depends on MACH_INGENIC || COMPILE_TEST -- depends on OF -- select REGMAP -- tristate "Ingenic JZ4760 internal CODEC" -- help -- Enable support for the internal CODEC found in the JZ4760 SoC -- from Ingenic. -+ depends on MACH_INGENIC || COMPILE_TEST -+ depends on OF -+ select REGMAP -+ tristate "Ingenic JZ4760 internal CODEC" -+ help -+ Enable support for the internal CODEC found in the JZ4760 SoC -+ from Ingenic. - -- This driver can also be built as a module. If so, the module -- will be called snd-soc-jz4760-codec. -+ This driver can also be built as a module. If so, the module -+ will be called snd-soc-jz4760-codec. - - config SND_SOC_JZ4770_CODEC - depends on MACH_INGENIC || COMPILE_TEST -@@ -1888,7 +1896,7 @@ config SND_SOC_RT5670 - depends on I2C - - config SND_SOC_RT5677 -- tristate -+ tristate "Realtek RT5677 Codec" - depends on I2C - select REGMAP_I2C - select REGMAP_IRQ -@@ -1978,6 +1986,15 @@ config SND_SOC_RT715_SDCA_SDW - select REGMAP_SOUNDWIRE - select REGMAP_SOUNDWIRE_MBQ - -+config SND_SOC_RT766_SDCA_SDW -+ tristate "Realtek RT766 SDCA Codec - SDW" -+ depends on SOUNDWIRE -+ depends on SND_SOC_SDCA -+ select SND_SOC_SDCA_HID -+ select SND_SOC_SDCA_IRQ -+ select REGMAP_SOUNDWIRE -+ select REGMAP_SOUNDWIRE_MBQ -+ - config SND_SOC_RT9120 - tristate "Richtek RT9120 Stereo Class-D Amplifier" - depends on I2C -@@ -2297,8 +2314,8 @@ config SND_SOC_TLV320ADC3XXX - depends on I2C - depends on GPIOLIB - help -- Enable support for Texas Instruments TLV320ADC3001 and TLV320ADC3101 -- ADCs. -+ Enable support for Texas Instruments TLV320ADC3001 and TLV320ADC3101 -+ ADCs. - - config SND_SOC_TLV320AIC23 - tristate -@@ -2771,6 +2788,16 @@ config SND_SOC_WSA884X - This enables support for Qualcomm WSA8840/WSA8845/WSA8845H Class-D - Smart Speaker Amplifier. - -+config SND_SOC_WSA885X -+ tristate "WSA885X Codec" -+ depends on I2C -+ select REGMAP_I2C -+ help -+ This enables support for Qualcomm WSA885X Stereo Smart Speaker -+ Amplifier. The codec driver programs the amplifier register -+ map and exposes the DAI and mixer controls used by Qualcomm -+ audio machine drivers. -+ - config SND_SOC_ZL38060 - tristate "Microsemi ZL38060 Connected Home Audio Processor" - depends on SPI_MASTER -@@ -2894,28 +2921,32 @@ config SND_SOC_TPA6130A2 - depends on I2C - - config SND_SOC_LPASS_MACRO_COMMON -- tristate -+ tristate - - config SND_SOC_LPASS_WSA_MACRO - depends on COMMON_CLK -+ depends on PM_CLK - select REGMAP_MMIO - select SND_SOC_LPASS_MACRO_COMMON - tristate "Qualcomm WSA Macro in LPASS(Low Power Audio SubSystem)" - - config SND_SOC_LPASS_VA_MACRO - depends on COMMON_CLK -+ depends on PM_CLK - select REGMAP_MMIO - select SND_SOC_LPASS_MACRO_COMMON - tristate "Qualcomm VA Macro in LPASS(Low Power Audio SubSystem)" - - config SND_SOC_LPASS_RX_MACRO - depends on COMMON_CLK -+ depends on PM_CLK - select REGMAP_MMIO - select SND_SOC_LPASS_MACRO_COMMON - tristate "Qualcomm RX Macro in LPASS(Low Power Audio SubSystem)" - - config SND_SOC_LPASS_TX_MACRO - depends on COMMON_CLK -+ depends on PM_CLK - select REGMAP_MMIO - select SND_SOC_LPASS_MACRO_COMMON - tristate "Qualcomm TX Macro in LPASS(Low Power Audio SubSystem)" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -11270,23 +7794,7 @@ diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile snd-soc-bd28623-y := bd28623.o snd-soc-bt-sco-y := bt-sco.o snd-soc-chv3-codec-y := chv3-codec.o -@@ -284,6 +285,7 @@ snd-soc-rt715-y := rt715.o rt715-sdw.o - snd-soc-rt715-sdca-y := rt715-sdca.o rt715-sdca-sdw.o - snd-soc-rt721-sdca-y := rt721-sdca.o rt721-sdca-sdw.o - snd-soc-rt722-sdca-y := rt722-sdca.o rt722-sdca-sdw.o -+snd-soc-rt766-sdca-y := rt766-sdca.o rt766-sdca-sdw.o - snd-soc-rt9120-y := rt9120.o - snd-soc-rt9123-y := rt9123.o - snd-soc-rt9123p-y := rt9123p.o -@@ -426,6 +428,7 @@ snd-soc-wm-hubs-y := wm_hubs.o - snd-soc-wsa881x-y := wsa881x.o - snd-soc-wsa883x-y := wsa883x.o - snd-soc-wsa884x-y := wsa884x.o -+snd-soc-wsa885x-y := wsa885x.o - snd-soc-zl38060-y := zl38060.o - # Amp - snd-soc-max9877-y := max9877.o -@@ -496,6 +499,7 @@ obj-$(CONFIG_SND_SOC_AW88395) +=snd-soc-aw88395.o +@@ -496,6 +497,7 @@ obj-$(CONFIG_SND_SOC_AW88395) +=snd-soc-aw88395.o obj-$(CONFIG_SND_SOC_AW88166) +=snd-soc-aw88166.o obj-$(CONFIG_SND_SOC_AW88261) +=snd-soc-aw88261.o obj-$(CONFIG_SND_SOC_AW88399) += snd-soc-aw88399.o @@ -11294,1216 +7802,11 @@ diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile obj-$(CONFIG_SND_SOC_BD28623) += snd-soc-bd28623.o obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o obj-$(CONFIG_SND_SOC_CHV3_CODEC) += snd-soc-chv3-codec.o -@@ -724,6 +728,7 @@ obj-$(CONFIG_SND_SOC_RT715) += snd-soc-rt715.o - obj-$(CONFIG_SND_SOC_RT715_SDCA_SDW) += snd-soc-rt715-sdca.o - obj-$(CONFIG_SND_SOC_RT721_SDCA_SDW) += snd-soc-rt721-sdca.o - obj-$(CONFIG_SND_SOC_RT722_SDCA_SDW) += snd-soc-rt722-sdca.o -+obj-$(CONFIG_SND_SOC_RT766_SDCA_SDW) += snd-soc-rt766-sdca.o - obj-$(CONFIG_SND_SOC_RT9120) += snd-soc-rt9120.o - obj-$(CONFIG_SND_SOC_RT9123) += snd-soc-rt9123.o - obj-$(CONFIG_SND_SOC_RT9123P) += snd-soc-rt9123p.o -@@ -876,6 +881,7 @@ obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o - obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o - obj-$(CONFIG_SND_SOC_WSA883X) += snd-soc-wsa883x.o - obj-$(CONFIG_SND_SOC_WSA884X) += snd-soc-wsa884x.o -+obj-$(CONFIG_SND_SOC_WSA885X) += snd-soc-wsa885x.o - obj-$(CONFIG_SND_SOC_ZL38060) += snd-soc-zl38060.o - - # Amp -diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c ---- a/sound/soc/codecs/ab8500-codec.c -+++ b/sound/soc/codecs/ab8500-codec.c -@@ -14,6 +14,7 @@ - * for ST-Ericsson. - */ - -+#include - #include - #include - #include -@@ -989,9 +990,8 @@ static int sid_status_control_get(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev); - -- mutex_lock(&drvdata->ctrl_lock); -+ guard(mutex)(&drvdata->ctrl_lock); - ucontrol->value.enumerated.item[0] = drvdata->sid_status; -- mutex_unlock(&drvdata->ctrl_lock); - - return 0; - } -@@ -1014,7 +1014,7 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol, - return -EIO; - } - -- mutex_lock(&drvdata->ctrl_lock); -+ guard(mutex)(&drvdata->ctrl_lock); - - sidconf = snd_soc_component_read(component, AB8500_SIDFIRCONF); - if (((sidconf & BIT(AB8500_SIDFIRCONF_FIRSIDBUSY)) != 0)) { -@@ -1025,7 +1025,8 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol, - } else { - status = -EBUSY; - } -- goto out; -+ dev_dbg(component->dev, "%s: Exit\n", __func__); -+ return status; - } - - snd_soc_component_write(component, AB8500_SIDFIRADR, 0); -@@ -1043,9 +1044,6 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol, - - drvdata->sid_status = SID_FIR_CONFIGURED; - --out: -- mutex_unlock(&drvdata->ctrl_lock); -- - dev_dbg(component->dev, "%s: Exit\n", __func__); - - return status; -diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c ---- a/sound/soc/codecs/ad1836.c -+++ b/sound/soc/codecs/ad1836.c -@@ -193,9 +193,15 @@ static int ad1836_hw_params(struct snd_pcm_substream *substream, - return 0; - } - -+static const u64 ad1836_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A | -+ SND_SOC_POSSIBLE_DAIFMT_IB_IF; -+ - static const struct snd_soc_dai_ops ad1836_dai_ops = { - .hw_params = ad1836_hw_params, - .set_fmt = ad1836_set_dai_fmt, -+ .auto_selectable_formats = &ad1836_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - #define AD183X_DAI(_name, num_dacs, num_adcs) \ -diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c ---- a/sound/soc/codecs/ad193x.c -+++ b/sound/soc/codecs/ad193x.c -@@ -394,6 +394,14 @@ static int ad193x_startup(struct snd_pcm_substream *substream, - &constr); - } - -+static const u64 ad193x_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_NB_IF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_IF; -+ - static const struct snd_soc_dai_ops ad193x_dai_ops = { - .startup = ad193x_startup, - .hw_params = ad193x_hw_params, -@@ -401,6 +409,8 @@ static const struct snd_soc_dai_ops ad193x_dai_ops = { - .set_tdm_slot = ad193x_set_tdm_slot, - .set_sysclk = ad193x_set_dai_sysclk, - .set_fmt = ad193x_set_dai_fmt, -+ .auto_selectable_formats = &ad193x_selectable_formats, -+ .num_auto_selectable_formats = 1, - .no_capture_mute = 1, - }; - -diff --git a/sound/soc/codecs/adau7118-hw.c b/sound/soc/codecs/adau7118-hw.c ---- a/sound/soc/codecs/adau7118-hw.c -+++ b/sound/soc/codecs/adau7118-hw.c -@@ -22,7 +22,7 @@ static const struct of_device_id adau7118_of_match[] = { - MODULE_DEVICE_TABLE(of, adau7118_of_match); - - static const struct platform_device_id adau7118_id[] = { -- { .name = "adau7118" }, -+ { .name = "adau7118" }, - { } - }; - MODULE_DEVICE_TABLE(platform, adau7118_id); -diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c ---- a/sound/soc/codecs/adav80x.c -+++ b/sound/soc/codecs/adav80x.c -@@ -743,11 +743,19 @@ static void adav80x_dai_shutdown(struct snd_pcm_substream *substream, - adav80x->rate = 0; - } - -+static const u64 adav80x_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF; -+ - static const struct snd_soc_dai_ops adav80x_dai_ops = { - .set_fmt = adav80x_set_dai_fmt, - .hw_params = adav80x_hw_params, - .startup = adav80x_dai_startup, - .shutdown = adav80x_dai_shutdown, -+ .auto_selectable_formats = &adav80x_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - #define ADAV80X_PLAYBACK_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ -diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c ---- a/sound/soc/codecs/ak4613.c -+++ b/sound/soc/codecs/ak4613.c -@@ -95,6 +95,7 @@ - * see - * AK4613_ENABLE_TDM_TEST - */ -+#include - #include - #include - #include -@@ -384,7 +385,7 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream, - struct ak4613_priv *priv = snd_soc_component_get_drvdata(component); - struct device *dev = component->dev; - -- mutex_lock(&priv->lock); -+ guard(mutex)(&priv->lock); - priv->cnt--; - if (priv->cnt < 0) { - dev_err(dev, "unexpected counter error\n"); -@@ -392,7 +393,6 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream, - } - if (!priv->cnt) - priv->ctrl1 = 0; -- mutex_unlock(&priv->lock); - } - - static void ak4613_hw_constraints(struct ak4613_priv *priv, -@@ -507,10 +507,9 @@ static int ak4613_dai_startup(struct snd_pcm_substream *substream, - struct snd_soc_component *component = dai->component; - struct ak4613_priv *priv = snd_soc_component_get_drvdata(component); - -- mutex_lock(&priv->lock); -+ guard(mutex)(&priv->lock); - ak4613_hw_constraints(priv, substream); - priv->cnt++; -- mutex_unlock(&priv->lock); - - return 0; - } -@@ -599,45 +598,47 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, - */ - ret = -EINVAL; - -- mutex_lock(&priv->lock); -- if (priv->cnt > 1) { -- /* -- * If it was already working, use current priv->ctrl1 -- */ -- ret = 0; -- } else { -- /* -- * It is not yet working, -- */ -- unsigned int channel = params_channels(params); -- u8 tdm; -+ scoped_guard(mutex, &priv->lock) { -+ if (priv->cnt > 1) { -+ /* -+ * If it was already working, use current priv->ctrl1 -+ */ -+ ret = 0; -+ } else { -+ /* -+ * It is not yet working, -+ */ -+ unsigned int channel = params_channels(params); -+ u8 tdm; - -- /* STEREO or TDM */ -- if (channel == 2) -- tdm = AK4613_CONFIG_MODE_STEREO; -- else -- tdm = AK4613_CONFIG_GET(priv, MODE); -+ /* STEREO or TDM */ -+ if (channel == 2) -+ tdm = AK4613_CONFIG_MODE_STEREO; -+ else -+ tdm = AK4613_CONFIG_GET(priv, MODE); - -- for (i = ARRAY_SIZE(ak4613_iface) - 1; i >= 0; i--) { -- const struct ak4613_interface *iface = ak4613_iface + i; -+ for (i = ARRAY_SIZE(ak4613_iface) - 1; i >= 0; i--) { -+ const struct ak4613_interface *iface = ak4613_iface + i; - -- if ((iface->fmt == fmt) && (iface->width == width)) { -- /* -- * Ctrl1 -- * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | -- * |TDM1|TDM0|DIF2|DIF1|DIF0|ATS1|ATS0|SMUTE| -- * < tdm > < iface->dif > -- */ -- priv->ctrl1 = (tdm << 6) | (iface->dif << 3); -- ret = 0; -- break; -+ if (iface->fmt == fmt && iface->width == width) { -+ /* -+ * Ctrl1 -+ * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | -+ * |TDM1|TDM0|DIF2|DIF1|DIF0|ATS1|ATS0|SMUTE| -+ * < tdm > < iface->dif > -+ */ -+ priv->ctrl1 = (tdm << 6) | (iface->dif << 3); -+ ret = 0; -+ break; -+ } - } - } - } -- mutex_unlock(&priv->lock); - -- if (ret < 0) -- goto hw_params_end; -+ if (ret < 0) { -+ dev_warn(dev, "unsupported data width/format combination\n"); -+ return ret; -+ } - - snd_soc_component_update_bits(component, CTRL1, FMT_MASK, priv->ctrl1); - snd_soc_component_update_bits(component, CTRL2, DFS_MASK, ctrl2); -@@ -645,10 +646,6 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, - snd_soc_component_update_bits(component, ICTRL, ICTRL_MASK, priv->ic); - snd_soc_component_update_bits(component, OCTRL, OCTRL_MASK, priv->oc); - --hw_params_end: -- if (ret < 0) -- dev_warn(dev, "unsupported data width/format combination\n"); -- - return ret; - } - -diff --git a/sound/soc/codecs/arizona-jack.c b/sound/soc/codecs/arizona-jack.c ---- a/sound/soc/codecs/arizona-jack.c -+++ b/sound/soc/codecs/arizona-jack.c -@@ -5,6 +5,7 @@ - * Copyright (C) 2012-2014 Wolfson Microelectronics plc - */ - -+#include - #include - #include - #include -@@ -707,15 +708,13 @@ static void arizona_micd_timeout_work(struct work_struct *work) - struct arizona_priv, - micd_timeout_work.work); - -- mutex_lock(&info->lock); -+ guard(mutex)(&info->lock); - - dev_dbg(info->arizona->dev, "MICD timed out, reporting HP\n"); - - info->detecting = false; - - arizona_identify_headphone(info); -- -- mutex_unlock(&info->lock); - } - - static int arizona_micd_adc_read(struct arizona_priv *info) -@@ -921,12 +920,11 @@ static void arizona_micd_detect(struct work_struct *work) - - cancel_delayed_work_sync(&info->micd_timeout_work); - -- mutex_lock(&info->lock); -+ guard(mutex)(&info->lock); - - /* If the cable was removed while measuring ignore the result */ - if (!(info->jack->status & SND_JACK_MECHANICAL)) { - dev_dbg(arizona->dev, "Ignoring MICDET for removed cable\n"); -- mutex_unlock(&info->lock); - return; - } - -@@ -936,7 +934,6 @@ static void arizona_micd_detect(struct work_struct *work) - arizona_button_reading(info); - - pm_runtime_mark_last_busy(arizona->dev); -- mutex_unlock(&info->lock); - } - - static irqreturn_t arizona_micdet(int irq, void *data) -@@ -948,10 +945,10 @@ static irqreturn_t arizona_micdet(int irq, void *data) - cancel_delayed_work_sync(&info->micd_detect_work); - cancel_delayed_work_sync(&info->micd_timeout_work); - -- mutex_lock(&info->lock); -- if (!info->detecting) -- debounce = 0; -- mutex_unlock(&info->lock); -+ scoped_guard(mutex, &info->lock) { -+ if (!info->detecting) -+ debounce = 0; -+ } - - if (debounce) - queue_delayed_work(system_power_efficient_wq, -@@ -969,9 +966,8 @@ static void arizona_hpdet_work(struct work_struct *work) - struct arizona_priv, - hpdet_work.work); - -- mutex_lock(&info->lock); -+ guard(mutex)(&info->lock); - arizona_start_hpdet_acc_id(info); -- mutex_unlock(&info->lock); - } - - static int arizona_hpdet_wait(struct arizona_priv *info) -@@ -1018,9 +1014,9 @@ static irqreturn_t arizona_jackdet(int irq, void *data) - cancelled_hp = cancel_delayed_work_sync(&info->hpdet_work); - cancelled_mic = cancel_delayed_work_sync(&info->micd_timeout_work); - -- pm_runtime_get_sync(arizona->dev); -+ guard(pm_runtime_active_auto)(arizona->dev); - -- mutex_lock(&info->lock); -+ guard(mutex)(&info->lock); - - if (info->micd_clamp) { - mask = ARIZONA_MICD_CLAMP_STS; -@@ -1036,8 +1032,6 @@ static irqreturn_t arizona_jackdet(int irq, void *data) - ret = regmap_read(arizona->regmap, ARIZONA_AOD_IRQ_RAW_STATUS, &val); - if (ret) { - dev_err(arizona->dev, "Failed to read jackdet status: %d\n", ret); -- mutex_unlock(&info->lock); -- pm_runtime_put_autosuspend(arizona->dev); - return IRQ_NONE; - } - -@@ -1056,62 +1050,61 @@ static irqreturn_t arizona_jackdet(int irq, void *data) - &info->micd_timeout_work, - msecs_to_jiffies(micd_timeout)); - } -+ } else { -+ info->last_jackdet = val; - -- goto out; -- } -- info->last_jackdet = val; -+ if (info->last_jackdet == present) { -+ dev_dbg(arizona->dev, "Detected jack\n"); -+ snd_soc_jack_report(info->jack, SND_JACK_MECHANICAL, SND_JACK_MECHANICAL); - -- if (info->last_jackdet == present) { -- dev_dbg(arizona->dev, "Detected jack\n"); -- snd_soc_jack_report(info->jack, SND_JACK_MECHANICAL, SND_JACK_MECHANICAL); -+ info->detecting = true; -+ info->mic = false; -+ info->jack_flips = 0; - -- info->detecting = true; -- info->mic = false; -- info->jack_flips = 0; -+ if (!arizona->pdata.hpdet_acc_id) { -+ arizona_start_mic(info); -+ } else { -+ queue_delayed_work(system_power_efficient_wq, -+ &info->hpdet_work, -+ msecs_to_jiffies(HPDET_DEBOUNCE)); -+ } - -- if (!arizona->pdata.hpdet_acc_id) { -- arizona_start_mic(info); -+ if (info->micd_clamp || !arizona->pdata.jd_invert) -+ regmap_update_bits(arizona->regmap, -+ ARIZONA_JACK_DETECT_DEBOUNCE, -+ ARIZONA_MICD_CLAMP_DB | -+ ARIZONA_JD1_DB, 0); - } else { -- queue_delayed_work(system_power_efficient_wq, -- &info->hpdet_work, -- msecs_to_jiffies(HPDET_DEBOUNCE)); -- } -+ dev_dbg(arizona->dev, "Detected jack removal\n"); -+ -+ arizona_stop_mic(info); -+ -+ info->num_hpdet_res = 0; -+ for (i = 0; i < ARRAY_SIZE(info->hpdet_res); i++) -+ info->hpdet_res[i] = 0; -+ info->mic = false; -+ info->hpdet_done = false; -+ info->hpdet_retried = false; -+ -+ snd_soc_jack_report(info->jack, 0, -+ ARIZONA_JACK_MASK | info->micd_button_mask); -+ -+ /* -+ * If the jack was removed during a headphone detection we -+ * need to wait for the headphone detection to finish, as -+ * it can not be aborted. We don't want to be able to start -+ * a new headphone detection from a fresh insert until this -+ * one is finished. -+ */ -+ arizona_hpdet_wait(info); - -- if (info->micd_clamp || !arizona->pdata.jd_invert) - regmap_update_bits(arizona->regmap, - ARIZONA_JACK_DETECT_DEBOUNCE, -- ARIZONA_MICD_CLAMP_DB | -- ARIZONA_JD1_DB, 0); -- } else { -- dev_dbg(arizona->dev, "Detected jack removal\n"); -- -- arizona_stop_mic(info); -- -- info->num_hpdet_res = 0; -- for (i = 0; i < ARRAY_SIZE(info->hpdet_res); i++) -- info->hpdet_res[i] = 0; -- info->mic = false; -- info->hpdet_done = false; -- info->hpdet_retried = false; -- -- snd_soc_jack_report(info->jack, 0, ARIZONA_JACK_MASK | info->micd_button_mask); -- -- /* -- * If the jack was removed during a headphone detection we -- * need to wait for the headphone detection to finish, as -- * it can not be aborted. We don't want to be able to start -- * a new headphone detection from a fresh insert until this -- * one is finished. -- */ -- arizona_hpdet_wait(info); -- -- regmap_update_bits(arizona->regmap, -- ARIZONA_JACK_DETECT_DEBOUNCE, -- ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB, -- ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB); -+ ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB, -+ ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB); -+ } - } - --out: - /* Clear trig_sts to make sure DCVDD is not forced up */ - regmap_write(arizona->regmap, ARIZONA_AOD_WKUP_AND_TRIG, - ARIZONA_MICD_CLAMP_FALL_TRIG_STS | -@@ -1119,10 +1112,6 @@ static irqreturn_t arizona_jackdet(int irq, void *data) - ARIZONA_JD1_FALL_TRIG_STS | - ARIZONA_JD1_RISE_TRIG_STS); - -- mutex_unlock(&info->lock); -- -- pm_runtime_put_autosuspend(arizona->dev); -- - return IRQ_HANDLED; - } - -diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c ---- a/sound/soc/codecs/arizona.c -+++ b/sound/soc/codecs/arizona.c -@@ -7,6 +7,7 @@ - * Author: Mark Brown - */ - -+#include - #include - #include - #include -@@ -1158,17 +1159,16 @@ int arizona_dvfs_up(struct snd_soc_component *component, unsigned int flags) - struct arizona_priv *priv = snd_soc_component_get_drvdata(component); - int ret = 0; - -- mutex_lock(&priv->dvfs_lock); -+ guard(mutex)(&priv->dvfs_lock); - - if (!priv->dvfs_cached && !priv->dvfs_reqs) { - ret = arizona_dvfs_enable(component); - if (ret) -- goto err; -+ return ret; - } - - priv->dvfs_reqs |= flags; --err: -- mutex_unlock(&priv->dvfs_lock); -+ - return ret; - } - EXPORT_SYMBOL_GPL(arizona_dvfs_up); -@@ -1179,7 +1179,7 @@ int arizona_dvfs_down(struct snd_soc_component *component, unsigned int flags) - unsigned int old_reqs; - int ret = 0; - -- mutex_lock(&priv->dvfs_lock); -+ guard(mutex)(&priv->dvfs_lock); - - old_reqs = priv->dvfs_reqs; - priv->dvfs_reqs &= ~flags; -@@ -1187,7 +1187,6 @@ int arizona_dvfs_down(struct snd_soc_component *component, unsigned int flags) - if (!priv->dvfs_cached && old_reqs && !priv->dvfs_reqs) - ret = arizona_dvfs_disable(component); - -- mutex_unlock(&priv->dvfs_lock); - return ret; - } - EXPORT_SYMBOL_GPL(arizona_dvfs_down); -@@ -1199,7 +1198,7 @@ int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w, - struct arizona_priv *priv = snd_soc_component_get_drvdata(component); - int ret = 0; - -- mutex_lock(&priv->dvfs_lock); -+ guard(mutex)(&priv->dvfs_lock); - - switch (event) { - case SND_SOC_DAPM_POST_PMU: -@@ -1222,7 +1221,6 @@ int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w, - break; - } - -- mutex_unlock(&priv->dvfs_lock); - return ret; - } - EXPORT_SYMBOL_GPL(arizona_dvfs_sysclk_ev); -@@ -1657,13 +1655,11 @@ static void arizona_wm5102_set_dac_comp(struct snd_soc_component *component, - { 0x80, 0x0 }, - }; - -- mutex_lock(&arizona->dac_comp_lock); -- -- dac_comp[1].def = arizona->dac_comp_coeff; -- if (rate >= 176400) -- dac_comp[2].def = arizona->dac_comp_enabled; -- -- mutex_unlock(&arizona->dac_comp_lock); -+ scoped_guard(mutex, &arizona->dac_comp_lock) { -+ dac_comp[1].def = arizona->dac_comp_coeff; -+ if (rate >= 176400) -+ dac_comp[2].def = arizona->dac_comp_enabled; -+ } - - regmap_multi_reg_write(arizona->regmap, - dac_comp, -diff --git a/sound/soc/codecs/aw87390.c b/sound/soc/codecs/aw87390.c ---- a/sound/soc/codecs/aw87390.c -+++ b/sound/soc/codecs/aw87390.c -@@ -7,6 +7,7 @@ - // Author: Weidong Wang - // - -+#include - #include - #include - #include -@@ -225,11 +226,10 @@ static int aw87390_profile_set(struct snd_kcontrol *kcontrol, - struct aw87390 *aw87390 = snd_soc_component_get_drvdata(codec); - int ret; - -- mutex_lock(&aw87390->lock); -+ guard(mutex)(&aw87390->lock); - ret = aw87390_dev_set_profile_index(aw87390->aw_pa, ucontrol->value.integer.value[0]); - if (ret) { - dev_dbg(codec->dev, "profile index does not change\n"); -- mutex_unlock(&aw87390->lock); - return 0; - } - -@@ -238,8 +238,6 @@ static int aw87390_profile_set(struct snd_kcontrol *kcontrol, - aw87390_power_on(aw87390->aw_pa); - } - -- mutex_unlock(&aw87390->lock); -- - return 1; - } - -@@ -250,7 +248,7 @@ static const struct snd_kcontrol_new aw87390_controls[] = { - - static int aw87390_request_firmware_file(struct aw87390 *aw87390) - { -- const struct firmware *cont = NULL; -+ const struct firmware *cont __free(firmware) = NULL; - int ret; - - aw87390->aw_pa->fw_status = AW87390_DEV_FW_FAILED; -@@ -265,14 +263,11 @@ static int aw87390_request_firmware_file(struct aw87390 *aw87390) - - aw87390->aw_cfg = devm_kzalloc(aw87390->aw_pa->dev, - struct_size(aw87390->aw_cfg, data, cont->size), GFP_KERNEL); -- if (!aw87390->aw_cfg) { -- release_firmware(cont); -+ if (!aw87390->aw_cfg) - return -ENOMEM; -- } - - aw87390->aw_cfg->len = cont->size; - memcpy(aw87390->aw_cfg->data, cont->data, cont->size); -- release_firmware(cont); - - ret = aw88395_dev_load_acf_check(aw87390->aw_pa, aw87390->aw_cfg); - if (ret) { -@@ -280,14 +275,12 @@ static int aw87390_request_firmware_file(struct aw87390 *aw87390) - return ret; - } - -- mutex_lock(&aw87390->lock); -+ guard(mutex)(&aw87390->lock); - - ret = aw88395_dev_cfg_load(aw87390->aw_pa, aw87390->aw_cfg); - if (ret) - dev_err(aw87390->aw_pa->dev, "aw_dev acf parse failed\n"); - -- mutex_unlock(&aw87390->lock); -- - return ret; - } - -diff --git a/sound/soc/codecs/aw88081.c b/sound/soc/codecs/aw88081.c ---- a/sound/soc/codecs/aw88081.c -+++ b/sound/soc/codecs/aw88081.c -@@ -7,6 +7,7 @@ - // Author: Weidong Wang - // - -+#include - #include - #include - #include -@@ -763,9 +764,8 @@ static void aw88081_startup_work(struct work_struct *work) - struct aw88081 *aw88081 = - container_of(work, struct aw88081, start_work.work); - -- mutex_lock(&aw88081->lock); -+ guard(mutex)(&aw88081->lock); - aw88081_start_pa(aw88081); -- mutex_unlock(&aw88081->lock); - } - - static void aw88081_start(struct aw88081 *aw88081, bool sync_start) -@@ -942,11 +942,10 @@ static int aw88081_profile_set(struct snd_kcontrol *kcontrol, - int ret; - - /* pa stop or stopping just set profile */ -- mutex_lock(&aw88081->lock); -+ guard(mutex)(&aw88081->lock); - ret = aw88081_dev_set_profile_index(aw88081->aw_pa, ucontrol->value.integer.value[0]); - if (ret) { - dev_dbg(codec->dev, "profile index does not change"); -- mutex_unlock(&aw88081->lock); - return 0; - } - -@@ -955,8 +954,6 @@ static int aw88081_profile_set(struct snd_kcontrol *kcontrol, - aw88081_start(aw88081, AW88081_SYNC_START); - } - -- mutex_unlock(&aw88081->lock); -- - return 1; - } - -@@ -1136,7 +1133,7 @@ static int aw88081_dev_init(struct aw88081 *aw88081, struct aw_container *aw_cfg - - static int aw88081_request_firmware_file(struct aw88081 *aw88081) - { -- const struct firmware *cont = NULL; -+ const struct firmware *cont __free(firmware) = NULL; - struct aw_container *aw_cfg; - int ret; - -@@ -1150,26 +1147,21 @@ static int aw88081_request_firmware_file(struct aw88081 *aw88081) - AW88081_ACF_FILE, cont ? cont->size : 0); - - aw_cfg = devm_kzalloc(aw88081->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL); -- if (!aw_cfg) { -- release_firmware(cont); -+ if (!aw_cfg) - return -ENOMEM; -- } -+ - aw_cfg->len = (int)cont->size; - memcpy(aw_cfg->data, cont->data, cont->size); - - aw88081->aw_cfg = aw_cfg; - -- release_firmware(cont); -- - ret = aw88395_dev_load_acf_check(aw88081->aw_pa, aw88081->aw_cfg); - if (ret) - return ret; - -- mutex_lock(&aw88081->lock); -- ret = aw88081_dev_init(aw88081, aw88081->aw_cfg); -- mutex_unlock(&aw88081->lock); -+ guard(mutex)(&aw88081->lock); - -- return ret; -+ return aw88081_dev_init(aw88081, aw88081->aw_cfg); - } - - static int aw88081_playback_event(struct snd_soc_dapm_widget *w, -@@ -1178,7 +1170,7 @@ static int aw88081_playback_event(struct snd_soc_dapm_widget *w, - struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); - struct aw88081 *aw88081 = snd_soc_component_get_drvdata(component); - -- mutex_lock(&aw88081->lock); -+ guard(mutex)(&aw88081->lock); - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - aw88081_start(aw88081, AW88081_ASYNC_START); -@@ -1189,7 +1181,6 @@ static int aw88081_playback_event(struct snd_soc_dapm_widget *w, - default: - break; - } -- mutex_unlock(&aw88081->lock); - - return 0; - } -diff --git a/sound/soc/codecs/aw88166.c b/sound/soc/codecs/aw88166.c ---- a/sound/soc/codecs/aw88166.c -+++ b/sound/soc/codecs/aw88166.c -@@ -7,6 +7,7 @@ - // Author: Weidong Wang - // - -+#include - #include - #include - #include -@@ -1173,9 +1174,8 @@ static void aw88166_startup_work(struct work_struct *work) - struct aw88166 *aw88166 = - container_of(work, struct aw88166, start_work.work); - -- mutex_lock(&aw88166->lock); -+ guard(mutex)(&aw88166->lock); - aw88166_start_pa(aw88166); -- mutex_unlock(&aw88166->lock); - } - - static void aw88166_start(struct aw88166 *aw88166, bool sync_start) -@@ -1413,11 +1413,10 @@ static int aw88166_profile_set(struct snd_kcontrol *kcontrol, - struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec); - int ret; - -- mutex_lock(&aw88166->lock); -+ guard(mutex)(&aw88166->lock); - ret = aw88166_dev_set_profile_index(aw88166->aw_pa, ucontrol->value.integer.value[0]); - if (ret) { - dev_dbg(codec->dev, "profile index does not change"); -- mutex_unlock(&aw88166->lock); - return 0; - } - -@@ -1426,8 +1425,6 @@ static int aw88166_profile_set(struct snd_kcontrol *kcontrol, - aw88166_start(aw88166, AW88166_SYNC_START); - } - -- mutex_unlock(&aw88166->lock); -- - return 1; - } - -@@ -1573,7 +1570,7 @@ static int aw88166_dev_init(struct aw88166 *aw88166, struct aw_container *aw_cfg - - static int aw88166_request_firmware_file(struct aw88166 *aw88166) - { -- const struct firmware *cont = NULL; -+ const struct firmware *cont __free(firmware) = NULL; - const char *fw_name; - int ret; - -@@ -1593,13 +1590,11 @@ static int aw88166_request_firmware_file(struct aw88166 *aw88166) - - aw88166->aw_cfg = devm_kzalloc(aw88166->aw_pa->dev, - struct_size(aw88166->aw_cfg, data, cont->size), GFP_KERNEL); -- if (!aw88166->aw_cfg) { -- release_firmware(cont); -+ if (!aw88166->aw_cfg) - return -ENOMEM; -- } -+ - aw88166->aw_cfg->len = (int)cont->size; - memcpy(aw88166->aw_cfg->data, cont->data, cont->size); -- release_firmware(cont); - - ret = aw88395_dev_load_acf_check(aw88166->aw_pa, aw88166->aw_cfg); - if (ret) { -@@ -1607,12 +1602,12 @@ static int aw88166_request_firmware_file(struct aw88166 *aw88166) - return ret; - } - -- mutex_lock(&aw88166->lock); -- /* aw device init */ -- ret = aw88166_dev_init(aw88166, aw88166->aw_cfg); -- if (ret) -- dev_err(aw88166->aw_pa->dev, "dev init failed\n"); -- mutex_unlock(&aw88166->lock); -+ scoped_guard(mutex, &aw88166->lock) { -+ /* aw device init */ -+ ret = aw88166_dev_init(aw88166, aw88166->aw_cfg); -+ if (ret) -+ dev_err(aw88166->aw_pa->dev, "dev init failed\n"); -+ } - - return ret; - } -@@ -1639,7 +1634,7 @@ static int aw88166_playback_event(struct snd_soc_dapm_widget *w, - struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); - struct aw88166 *aw88166 = snd_soc_component_get_drvdata(component); - -- mutex_lock(&aw88166->lock); -+ guard(mutex)(&aw88166->lock); - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - aw88166_start(aw88166, AW88166_ASYNC_START); -@@ -1650,7 +1645,6 @@ static int aw88166_playback_event(struct snd_soc_dapm_widget *w, - default: - break; - } -- mutex_unlock(&aw88166->lock); - - return 0; - } -diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c ---- a/sound/soc/codecs/aw88261.c -+++ b/sound/soc/codecs/aw88261.c -@@ -8,6 +8,7 @@ - // Author: Weidong Wang - // - -+#include - #include - #include - #include -@@ -960,11 +961,10 @@ static int aw88261_profile_set(struct snd_kcontrol *kcontrol, - int ret; - - /* pa stop or stopping just set profile */ -- mutex_lock(&aw88261->lock); -+ guard(mutex)(&aw88261->lock); - ret = aw88261_dev_set_profile_index(aw88261->aw_pa, ucontrol->value.integer.value[0]); - if (ret) { - dev_dbg(codec->dev, "profile index does not change"); -- mutex_unlock(&aw88261->lock); - return 0; - } - -@@ -973,8 +973,6 @@ static int aw88261_profile_set(struct snd_kcontrol *kcontrol, - aw88261_start(aw88261); - } - -- mutex_unlock(&aw88261->lock); -- - return 1; - } - -@@ -1038,7 +1036,7 @@ static int aw88261_playback_event(struct snd_soc_dapm_widget *w, - struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); - struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); - -- mutex_lock(&aw88261->lock); -+ guard(mutex)(&aw88261->lock); - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - aw88261_start(aw88261); -@@ -1049,7 +1047,6 @@ static int aw88261_playback_event(struct snd_soc_dapm_widget *w, - default: - break; - } -- mutex_unlock(&aw88261->lock); - - return 0; - } -@@ -1152,7 +1149,7 @@ static int aw88261_dev_init(struct aw88261 *aw88261, struct aw_container *aw_cfg - - static int aw88261_request_firmware_file(struct aw88261 *aw88261) - { -- const struct firmware *cont = NULL; -+ const struct firmware *cont __free(firmware) = NULL; - struct aw_container *aw_cfg; - const char *fw_name; - int ret; -@@ -1172,13 +1169,11 @@ static int aw88261_request_firmware_file(struct aw88261 *aw88261) - fw_name, cont ? cont->size : 0); - - aw_cfg = devm_kzalloc(aw88261->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL); -- if (!aw_cfg) { -- release_firmware(cont); -+ if (!aw_cfg) - return -ENOMEM; -- } -+ - aw_cfg->len = (int)cont->size; - memcpy(aw_cfg->data, cont->data, cont->size); -- release_firmware(cont); - - aw88261->aw_cfg = aw_cfg; - -@@ -1188,12 +1183,12 @@ static int aw88261_request_firmware_file(struct aw88261 *aw88261) - return ret; - } - -- mutex_lock(&aw88261->lock); -- /* aw device init */ -- ret = aw88261_dev_init(aw88261, aw88261->aw_cfg); -- if (ret) -- dev_err(aw88261->aw_pa->dev, "dev init failed"); -- mutex_unlock(&aw88261->lock); -+ scoped_guard(mutex, &aw88261->lock) { -+ /* aw device init */ -+ ret = aw88261_dev_init(aw88261, aw88261->aw_cfg); -+ if (ret) -+ dev_err(aw88261->aw_pa->dev, "dev init failed"); -+ } - - return ret; - } -diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c ---- a/sound/soc/codecs/aw88395/aw88395.c -+++ b/sound/soc/codecs/aw88395/aw88395.c -@@ -8,6 +8,7 @@ - // Author: Weidong Wang - // - -+#include - #include - #include - #include -@@ -51,9 +52,8 @@ static void aw88395_startup_work(struct work_struct *work) - struct aw88395 *aw88395 = - container_of(work, struct aw88395, start_work.work); - -- mutex_lock(&aw88395->lock); -+ guard(mutex)(&aw88395->lock); - aw88395_start_pa(aw88395); -- mutex_unlock(&aw88395->lock); - } - - static void aw88395_start(struct aw88395 *aw88395, bool sync_start) -@@ -224,11 +224,10 @@ static int aw88395_profile_set(struct snd_kcontrol *kcontrol, - int ret; - - /* pa stop or stopping just set profile */ -- mutex_lock(&aw88395->lock); -+ guard(mutex)(&aw88395->lock); - ret = aw88395_dev_set_profile_index(aw88395->aw_pa, ucontrol->value.integer.value[0]); - if (ret < 0) { - dev_dbg(codec->dev, "profile index does not change"); -- mutex_unlock(&aw88395->lock); - return 0; - } - -@@ -237,8 +236,6 @@ static int aw88395_profile_set(struct snd_kcontrol *kcontrol, - aw88395_start(aw88395, AW88395_SYNC_START); - } - -- mutex_unlock(&aw88395->lock); -- - return 1; - } - -@@ -366,7 +363,7 @@ static int aw88395_playback_event(struct snd_soc_dapm_widget *w, - struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); - struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component); - -- mutex_lock(&aw88395->lock); -+ guard(mutex)(&aw88395->lock); - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - aw88395_start(aw88395, AW88395_ASYNC_START); -@@ -377,7 +374,6 @@ static int aw88395_playback_event(struct snd_soc_dapm_widget *w, - default: - break; - } -- mutex_unlock(&aw88395->lock); - - return 0; - } -@@ -461,7 +457,7 @@ static void aw88395_hw_reset(struct aw88395 *aw88395) - - static int aw88395_request_firmware_file(struct aw88395 *aw88395) - { -- const struct firmware *cont = NULL; -+ const struct firmware *cont __free(firmware) = NULL; - struct aw_container *aw_cfg; - int ret; - -@@ -477,13 +473,11 @@ static int aw88395_request_firmware_file(struct aw88395 *aw88395) - AW88395_ACF_FILE, cont ? cont->size : 0); - - aw_cfg = devm_kzalloc(aw88395->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL); -- if (!aw_cfg) { -- release_firmware(cont); -+ if (!aw_cfg) - return -ENOMEM; -- } -+ - aw_cfg->len = (int)cont->size; - memcpy(aw_cfg->data, cont->data, cont->size); -- release_firmware(cont); - - aw88395->aw_cfg = aw_cfg; - -@@ -495,12 +489,12 @@ static int aw88395_request_firmware_file(struct aw88395 *aw88395) - - dev_dbg(aw88395->aw_pa->dev, "%s : bin load success\n", __func__); - -- mutex_lock(&aw88395->lock); -- /* aw device init */ -- ret = aw88395_dev_init(aw88395->aw_pa, aw88395->aw_cfg); -- if (ret < 0) -- dev_err(aw88395->aw_pa->dev, "dev init failed"); -- mutex_unlock(&aw88395->lock); -+ scoped_guard(mutex, &aw88395->lock) { -+ /* aw device init */ -+ ret = aw88395_dev_init(aw88395->aw_pa, aw88395->aw_cfg); -+ if (ret < 0) -+ dev_err(aw88395->aw_pa->dev, "dev init failed"); -+ } - - return ret; - } -diff --git a/sound/soc/codecs/aw88395/aw88395_device.c b/sound/soc/codecs/aw88395/aw88395_device.c ---- a/sound/soc/codecs/aw88395/aw88395_device.c -+++ b/sound/soc/codecs/aw88395/aw88395_device.c -@@ -8,6 +8,7 @@ - // Author: Ben Yi - // - -+#include - #include - #include - #include -@@ -70,7 +71,7 @@ int aw_dev_dsp_write(struct aw_device *aw_dev, - u32 reg_value; - int ret; - -- mutex_lock(&aw_dev->dsp_lock); -+ guard(mutex)(&aw_dev->dsp_lock); - switch (data_type) { - case AW_DSP_16_DATA: - ret = aw_dev_dsp_write_16bit(aw_dev, dsp_addr, dsp_data); -@@ -93,7 +94,6 @@ int aw_dev_dsp_write(struct aw_device *aw_dev, - /* clear dsp chip select state*/ - if (regmap_read(aw_dev->regmap, AW88395_ID_REG, ®_value)) - dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret); -- mutex_unlock(&aw_dev->dsp_lock); - - return ret; - } -@@ -156,7 +156,7 @@ int aw_dev_dsp_read(struct aw_device *aw_dev, - u32 reg_value; - int ret; - -- mutex_lock(&aw_dev->dsp_lock); -+ guard(mutex)(&aw_dev->dsp_lock); - switch (data_type) { - case AW_DSP_16_DATA: - ret = aw_dev_dsp_read_16bit(aw_dev, dsp_addr, dsp_data); -@@ -179,7 +179,6 @@ int aw_dev_dsp_read(struct aw_device *aw_dev, - /* clear dsp chip select state*/ - if (regmap_read(aw_dev->regmap, AW88395_ID_REG, ®_value)) - dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret); -- mutex_unlock(&aw_dev->dsp_lock); - - return ret; - } -@@ -1110,42 +1109,36 @@ static int aw_dev_dsp_update_container(struct aw_device *aw_dev, - #ifdef AW88395_DSP_I2C_WRITES - u32 tmp_len; - -- mutex_lock(&aw_dev->dsp_lock); -+ guard(mutex)(&aw_dev->dsp_lock); - ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, base); - if (ret) -- goto error_operation; -+ return ret; - - for (i = 0; i < len; i += AW88395_MAX_RAM_WRITE_BYTE_SIZE) { - tmp_len = min(len - i, AW88395_MAX_RAM_WRITE_BYTE_SIZE); - ret = regmap_raw_write(aw_dev->regmap, AW88395_DSPMDAT_REG, - &data[i], tmp_len); - if (ret) -- goto error_operation; -+ return ret; - } -- mutex_unlock(&aw_dev->dsp_lock); - #else - __be16 reg_val; - -- mutex_lock(&aw_dev->dsp_lock); -+ guard(mutex)(&aw_dev->dsp_lock); - /* i2c write */ - ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, base); - if (ret) -- goto error_operation; -+ return ret; - for (i = 0; i < len; i += 2) { - reg_val = cpu_to_be16p((u16 *)(data + i)); - ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, - (u16)reg_val); - if (ret) -- goto error_operation; -+ return ret; - } -- mutex_unlock(&aw_dev->dsp_lock); - #endif - - return 0; -- --error_operation: -- mutex_unlock(&aw_dev->dsp_lock); -- return ret; - } - - static int aw_dev_dsp_update_fw(struct aw_device *aw_dev, -@@ -1231,14 +1224,14 @@ static int aw_dev_check_sram(struct aw_device *aw_dev) - { - unsigned int reg_val; - -- mutex_lock(&aw_dev->dsp_lock); -+ guard(mutex)(&aw_dev->dsp_lock); - /* check the odd bits of reg 0x40 */ - regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, AW88395_DSP_ODD_NUM_BIT_TEST); - regmap_read(aw_dev->regmap, AW88395_DSPMADD_REG, ®_val); - if (reg_val != AW88395_DSP_ODD_NUM_BIT_TEST) { - dev_err(aw_dev->dev, "check reg 0x40 odd bit failed, read[0x%x] != write[0x%x]", - reg_val, AW88395_DSP_ODD_NUM_BIT_TEST); -- goto error; -+ return -EPERM; - } - - /* check the even bits of reg 0x40 */ -@@ -1247,7 +1240,7 @@ static int aw_dev_check_sram(struct aw_device *aw_dev) - if (reg_val != AW88395_DSP_EVEN_NUM_BIT_TEST) { - dev_err(aw_dev->dev, "check reg 0x40 even bit failed, read[0x%x] != write[0x%x]", - reg_val, AW88395_DSP_EVEN_NUM_BIT_TEST); -- goto error; -+ return -EPERM; - } - - /* check dsp_fw_base_addr */ -@@ -1256,7 +1249,7 @@ static int aw_dev_check_sram(struct aw_device *aw_dev) - if (reg_val != AW88395_DSP_EVEN_NUM_BIT_TEST) { - dev_err(aw_dev->dev, "check dsp fw addr failed, read[0x%x] != write[0x%x]", - reg_val, AW88395_DSP_EVEN_NUM_BIT_TEST); -- goto error; -+ return -EPERM; - } - - /* check dsp_cfg_base_addr */ -@@ -1265,15 +1258,10 @@ static int aw_dev_check_sram(struct aw_device *aw_dev) - if (reg_val != AW88395_DSP_ODD_NUM_BIT_TEST) { - dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] != write[0x%x]", - reg_val, AW88395_DSP_ODD_NUM_BIT_TEST); -- goto error; -+ return -EPERM; - } -- mutex_unlock(&aw_dev->dsp_lock); - - return 0; -- --error: -- mutex_unlock(&aw_dev->dsp_lock); -- return -EPERM; - } - - int aw88395_dev_fw_update(struct aw_device *aw_dev, bool up_dsp_fw_en, bool force_up_en) diff --git a/sound/soc/codecs/aw88399-lib.c b/sound/soc/codecs/aw88399-lib.c new file mode 100644 --- /dev/null +++ b/sound/soc/codecs/aw88399-lib.c -@@ -0,0 +1,1409 @@ +@@ -0,0 +1,1411 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88399-lib.c -- AW88399 Common functions for ASoC and HDA audio drivers @@ -13788,7 +9091,7 @@ new file mode 100644 + +int aw88399_request_firmware_file(struct aw88399 *aw88399) +{ -+ const struct firmware *cont __free(firmware) = NULL; ++ const struct firmware *cont = NULL; + int ret; + + aw88399->aw_pa->fw_status = AW88399_DEV_FW_FAILED; @@ -13804,11 +9107,13 @@ new file mode 100644 + + aw88399->aw_cfg = devm_kzalloc(aw88399->aw_pa->dev, + struct_size(aw88399->aw_cfg, data, cont->size), GFP_KERNEL); -+ if (!aw88399->aw_cfg) ++ if (!aw88399->aw_cfg) { ++ release_firmware(cont); + return -ENOMEM; -+ ++ } + aw88399->aw_cfg->len = (int)cont->size; + memcpy(aw88399->aw_cfg->data, cont->data, cont->size); ++ release_firmware(cont); + + ret = aw88395_dev_load_acf_check(aw88399->aw_pa, aw88399->aw_cfg); + if (ret) { @@ -13916,11 +9221,7 @@ new file mode 100644 diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c --- a/sound/soc/codecs/aw88399.c +++ b/sound/soc/codecs/aw88399.c -@@ -7,1220 +7,16 @@ - // Author: Weidong Wang - // - -+#include +@@ -10,1217 +10,12 @@ #include #include #include @@ -15138,29 +10439,7 @@ diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c static struct snd_soc_dai_driver aw88399_dai[] = { { .name = "aw88399-aif", -@@ -1702,11 +498,10 @@ static int aw88399_profile_set(struct snd_kcontrol *kcontrol, - struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec); - int ret; - -- mutex_lock(&aw88399->lock); -+ guard(mutex)(&aw88399->lock); - ret = aw88399_dev_set_profile_index(aw88399->aw_pa, ucontrol->value.integer.value[0]); - if (ret) { - dev_dbg(codec->dev, "profile index does not change"); -- mutex_unlock(&aw88399->lock); - return 0; - } - -@@ -1715,8 +510,6 @@ static int aw88399_profile_set(struct snd_kcontrol *kcontrol, - aw88399_start(aw88399, AW88399_SYNC_START); - } - -- mutex_unlock(&aw88399->lock); -- - return 1; - } - -@@ -1869,86 +662,6 @@ static int aw88399_calib_set(struct snd_kcontrol *kcontrol, +@@ -1869,86 +664,6 @@ static int aw88399_calib_set(struct snd_kcontrol *kcontrol, return 0; } @@ -15247,24 +10526,7 @@ diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c static const struct snd_kcontrol_new aw88399_controls[] = { SOC_SINGLE_EXT("PCM Playback Volume", AW88399_SYSCTRL2_REG, 6, AW88399_MUTE_VOL, 0, aw88399_volume_get, -@@ -1975,7 +688,7 @@ static int aw88399_playback_event(struct snd_soc_dapm_widget *w, - struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); - struct aw88399 *aw88399 = snd_soc_component_get_drvdata(component); - -- mutex_lock(&aw88399->lock); -+ guard(mutex)(&aw88399->lock); - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - aw88399_start(aw88399, AW88399_ASYNC_START); -@@ -1986,7 +699,6 @@ static int aw88399_playback_event(struct snd_soc_dapm_widget *w, - default: - break; - } -- mutex_unlock(&aw88399->lock); - - return 0; - } -@@ -2040,70 +752,6 @@ static const struct snd_soc_component_driver soc_codec_dev_aw88399 = { +@@ -2040,70 +755,6 @@ static const struct snd_soc_component_driver soc_codec_dev_aw88399 = { .num_controls = ARRAY_SIZE(aw88399_controls), }; @@ -15934,11848 +11196,6 @@ diff --git a/sound/soc/codecs/aw88399.h b/sound/soc/codecs/aw88399.h - -#endif +#endif /* __AW88399_H__ */ -diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c ---- a/sound/soc/codecs/bt-sco.c -+++ b/sound/soc/codecs/bt-sco.c -@@ -85,13 +85,9 @@ static int bt_sco_probe(struct platform_device *pdev) - } - - static const struct platform_device_id bt_sco_driver_ids[] = { -- { -- .name = "dfbmcs320", -- }, -- { -- .name = "bt-sco", -- }, -- {}, -+ { .name = "dfbmcs320" }, -+ { .name = "bt-sco" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids); - -diff --git a/sound/soc/codecs/cpcap.c b/sound/soc/codecs/cpcap.c ---- a/sound/soc/codecs/cpcap.c -+++ b/sound/soc/codecs/cpcap.c -@@ -26,14 +26,6 @@ - /* Register 9 - CPCAP_REG_INTS2 --- Interrupt Sense 2 */ - #define CPCAP_BIT_PTT_S 11 /* Push To Talk */ - --/* Register 512 CPCAP_REG_VAUDIOC --- Audio Regulator and Bias Voltage */ --#define CPCAP_BIT_AUDIO_LOW_PWR 6 --#define CPCAP_BIT_AUD_LOWPWR_SPEED 5 --#define CPCAP_BIT_VAUDIOPRISTBY 4 --#define CPCAP_BIT_VAUDIO_MODE1 2 --#define CPCAP_BIT_VAUDIO_MODE0 1 --#define CPCAP_BIT_V_AUDIO_EN 0 -- - /* Register 513 CPCAP_REG_CC --- CODEC */ - #define CPCAP_BIT_CDC_CLK2 15 - #define CPCAP_BIT_CDC_CLK1 14 -@@ -239,7 +231,6 @@ struct cpcap_reg_info { - }; - - static const struct cpcap_reg_info cpcap_default_regs[] = { -- { CPCAP_REG_VAUDIOC, 0x003F, 0x0000 }, - { CPCAP_REG_CC, 0xFFFF, 0x0000 }, - { CPCAP_REG_CC, 0xFFFF, 0x0000 }, - { CPCAP_REG_CDI, 0xBFFF, 0x0000 }, -@@ -1265,6 +1256,7 @@ static int cpcap_voice_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) - { -+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_soc_component *component = dai->component; - struct device *dev = component->dev; - struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); -@@ -1298,7 +1290,7 @@ static int cpcap_voice_hw_params(struct snd_pcm_substream *substream, - return err; - } - -- return 0; -+ return snd_soc_runtime_set_dai_fmt(rtd, rtd->dai_link->dai_fmt); - } - - static int cpcap_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, -@@ -1391,121 +1383,8 @@ static int cpcap_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, - return 0; - } - -- --/* -- * Configure codec for voice call if requested. -- * -- * We can configure most with snd_soc_dai_set_sysclk(), snd_soc_dai_set_fmt() -- * and snd_soc_dai_set_tdm_slot(). This function configures the rest of the -- * cpcap related hardware as CPU is not involved in the voice call. -- */ --static int cpcap_voice_call(struct cpcap_audio *cpcap, struct snd_soc_dai *dai, -- bool voice_call) --{ -- int mask, err; -- -- /* Modem to codec VAUDIO_MODE1 */ -- mask = BIT(CPCAP_BIT_VAUDIO_MODE1); -- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_VAUDIOC, -- mask, voice_call ? mask : 0); -- if (err) -- return err; -- -- /* Clear MIC1_MUX for call */ -- mask = BIT(CPCAP_BIT_MIC1_MUX); -- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI, -- mask, voice_call ? 0 : mask); -- if (err) -- return err; -- -- /* Set MIC2_MUX for call */ -- mask = BIT(CPCAP_BIT_MB_ON1L) | BIT(CPCAP_BIT_MB_ON1R) | -- BIT(CPCAP_BIT_MIC2_MUX) | BIT(CPCAP_BIT_MIC2_PGA_EN); -- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI, -- mask, voice_call ? mask : 0); -- if (err) -- return err; -- -- /* Enable LDSP for call */ -- mask = BIT(CPCAP_BIT_A2_LDSP_L_EN) | BIT(CPCAP_BIT_A2_LDSP_R_EN); -- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXOA, -- mask, voice_call ? mask : 0); -- if (err) -- return err; -- -- /* Enable CPCAP_BIT_PGA_CDC_EN for call */ -- mask = BIT(CPCAP_BIT_PGA_CDC_EN); -- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXCOA, -- mask, voice_call ? mask : 0); -- if (err) -- return err; -- -- /* Unmute voice for call */ -- if (dai) { -- err = snd_soc_dai_digital_mute(dai, !voice_call, -- SNDRV_PCM_STREAM_PLAYBACK); -- if (err) -- return err; -- } -- -- /* Set modem to codec mic CDC and HPF for call */ -- mask = BIT(CPCAP_BIT_MIC2_CDC_EN) | BIT(CPCAP_BIT_CDC_EN_RX) | -- BIT(CPCAP_BIT_AUDOHPF_1) | BIT(CPCAP_BIT_AUDOHPF_0) | -- BIT(CPCAP_BIT_AUDIHPF_1) | BIT(CPCAP_BIT_AUDIHPF_0); -- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CC, -- mask, voice_call ? mask : 0); -- if (err) -- return err; -- -- /* Enable modem to codec CDC for call*/ -- mask = BIT(CPCAP_BIT_CDC_CLK_EN); -- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI, -- mask, voice_call ? mask : 0); -- -- return err; --} -- --static int cpcap_voice_set_tdm_slot(struct snd_soc_dai *dai, -- unsigned int tx_mask, unsigned int rx_mask, -- int slots, int slot_width) --{ -- struct snd_soc_component *component = dai->component; -- struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); -- int err, ts_mask, mask; -- bool voice_call; -- -- /* -- * Primitive test for voice call, probably needs more checks -- * later on for 16-bit calls detected, Bluetooth headset etc. -- */ -- if (tx_mask == 0 && rx_mask == 1 && slot_width == 8) -- voice_call = true; -- else -- voice_call = false; -- -- ts_mask = 0x7 << CPCAP_BIT_MIC2_TIMESLOT0; -- ts_mask |= 0x7 << CPCAP_BIT_MIC1_RX_TIMESLOT0; -- -- mask = (tx_mask & 0x7) << CPCAP_BIT_MIC2_TIMESLOT0; -- mask |= (rx_mask & 0x7) << CPCAP_BIT_MIC1_RX_TIMESLOT0; -- -- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI, -- ts_mask, mask); -- if (err) -- return err; -- -- err = cpcap_set_samprate(cpcap, CPCAP_DAI_VOICE, slot_width * 1000); -- if (err) -- return err; -- -- err = cpcap_voice_call(cpcap, dai, voice_call); -- if (err) -- return err; -- -- return 0; --} -- --static int cpcap_voice_set_mute(struct snd_soc_dai *dai, int mute, int direction) -+static int cpcap_voice_set_mute(struct snd_soc_dai *dai, -+ int mute, int direction) - { - struct snd_soc_component *component = dai->component; - struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); -@@ -1526,7 +1405,6 @@ static const struct snd_soc_dai_ops cpcap_dai_voice_ops = { - .hw_params = cpcap_voice_hw_params, - .set_sysclk = cpcap_voice_set_dai_sysclk, - .set_fmt = cpcap_voice_set_dai_fmt, -- .set_tdm_slot = cpcap_voice_set_tdm_slot, - .mute_stream = cpcap_voice_set_mute, - .no_capture_mute = 1, - }; -diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c ---- a/sound/soc/codecs/cros_ec_codec.c -+++ b/sound/soc/codecs/cros_ec_codec.c -@@ -12,6 +12,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -319,10 +320,18 @@ static int i2s_rx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) - (uint8_t *)&p, sizeof(p), NULL, 0); - } - -+static const u64 i2s_rx_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF; -+ - static const struct snd_soc_dai_ops i2s_rx_dai_ops = { - .hw_params = i2s_rx_hw_params, - .set_fmt = i2s_rx_set_fmt, - .set_bclk_ratio = i2s_rx_set_bclk_ratio, -+ .auto_selectable_formats = &i2s_rx_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static int i2s_rx_event(struct snd_soc_dapm_widget *w, -@@ -608,10 +617,10 @@ static void wov_copy_work(struct work_struct *w) - container_of(w, struct cros_ec_codec_priv, wov_copy_work.work); - int ret; - -- mutex_lock(&priv->wov_dma_lock); -+ guard(mutex)(&priv->wov_dma_lock); - if (!priv->wov_substream) { - dev_warn(priv->dev, "no pcm substream\n"); -- goto leave; -+ return; - } - - if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_AUDIO_SHM)) -@@ -624,8 +633,6 @@ static void wov_copy_work(struct work_struct *w) - msecs_to_jiffies(10)); - else if (ret) - dev_err(priv->dev, "failed to read audio data\n"); --leave: -- mutex_unlock(&priv->wov_dma_lock); - } - - static int wov_enable_get(struct snd_kcontrol *kcontrol, -@@ -895,12 +902,11 @@ static int wov_pcm_hw_params(struct snd_soc_component *component, - struct cros_ec_codec_priv *priv = - snd_soc_component_get_drvdata(component); - -- mutex_lock(&priv->wov_dma_lock); -+ guard(mutex)(&priv->wov_dma_lock); - priv->wov_substream = substream; - priv->wov_rp = priv->wov_wp = 0; - priv->wov_dma_offset = 0; - priv->wov_burst_read = true; -- mutex_unlock(&priv->wov_dma_lock); - - return 0; - } -@@ -911,10 +917,10 @@ static int wov_pcm_hw_free(struct snd_soc_component *component, - struct cros_ec_codec_priv *priv = - snd_soc_component_get_drvdata(component); - -- mutex_lock(&priv->wov_dma_lock); -- wov_queue_dequeue(priv, wov_queue_size(priv)); -- priv->wov_substream = NULL; -- mutex_unlock(&priv->wov_dma_lock); -+ scoped_guard(mutex, &priv->wov_dma_lock) { -+ wov_queue_dequeue(priv, wov_queue_size(priv)); -+ priv->wov_substream = NULL; -+ } - - cancel_delayed_work_sync(&priv->wov_copy_work); - -diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c ---- a/sound/soc/codecs/cs-amp-lib.c -+++ b/sound/soc/codecs/cs-amp-lib.c -@@ -7,6 +7,7 @@ - - #include - #include -+#include - #include - #include - #include -@@ -83,10 +84,12 @@ static int cs_amp_write_cal_coeff(struct cs_dsp *dsp, - KUNIT_STATIC_STUB_REDIRECT(cs_amp_write_cal_coeff, dsp, controls, ctl_name, val); - - if (IS_REACHABLE(CONFIG_FW_CS_DSP)) { -- mutex_lock(&dsp->pwr_lock); -- cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, controls->mem_region, controls->alg_id); -- ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, &beval, sizeof(beval)); -- mutex_unlock(&dsp->pwr_lock); -+ scoped_guard(mutex, &dsp->pwr_lock) { -+ cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, -+ controls->mem_region, -+ controls->alg_id); -+ ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, &beval, sizeof(beval)); -+ } - - if (ret < 0) { - dev_err(dsp->dev, "Failed to write to '%s': %d\n", ctl_name, ret); -diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c ---- a/sound/soc/codecs/cs35l32.c -+++ b/sound/soc/codecs/cs35l32.c -@@ -538,7 +538,15 @@ static int cs35l32_runtime_resume(struct device *dev) - gpiod_set_value_cansleep(cs35l32->reset_gpio, 1); - - regcache_cache_only(cs35l32->regmap, false); -- regcache_sync(cs35l32->regmap); -+ ret = regcache_sync(cs35l32->regmap); -+ if (ret) { -+ regcache_cache_only(cs35l32->regmap, true); -+ regcache_mark_dirty(cs35l32->regmap); -+ gpiod_set_value_cansleep(cs35l32->reset_gpio, 0); -+ regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies), -+ cs35l32->supplies); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c ---- a/sound/soc/codecs/cs35l56-i2c.c -+++ b/sound/soc/codecs/cs35l56-i2c.c -@@ -51,15 +51,7 @@ static int cs35l56_i2c_probe(struct i2c_client *client) - return dev_err_probe(cs35l56->base.dev, ret, "Failed to allocate register map\n"); - } - -- ret = cs35l56_common_probe(cs35l56); -- if (ret != 0) -- return ret; -- -- ret = cs35l56_irq_request(&cs35l56->base, client->irq); -- if (ret < 0) -- cs35l56_remove(cs35l56); -- -- return ret; -+ return cs35l56_common_probe(cs35l56, client->irq); - } - - static void cs35l56_i2c_remove(struct i2c_client *client) -diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c ---- a/sound/soc/codecs/cs35l56-sdw.c -+++ b/sound/soc/codecs/cs35l56-sdw.c -@@ -231,7 +231,7 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral) - * a soft reset. - */ - if (cs35l56->base.init_done) -- cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); -+ cs35l56_unmask_soundwire_interrupts(cs35l56); - - out: - pm_runtime_put_autosuspend(cs35l56->base.dev); -@@ -240,47 +240,17 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral) - static int cs35l56_sdw_interrupt(struct sdw_slave *peripheral, - struct sdw_slave_intr_status *status) - { -- struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); -- -- /* SoundWire core holds our pm_runtime when calling this function. */ -- -- dev_dbg(cs35l56->base.dev, "int control_port=%#x\n", status->control_port); -- -- if ((status->control_port & SDW_SCP_INT1_IMPL_DEF) == 0) -- return 0; -- - /* -- * Prevent bus manager suspending and possibly issuing a -- * bus-reset before the queued work has run. -+ * The IRQ itself was handled through the regmap_irq handler, this is -+ * just clearing up the additional Cirrus SoundWire registers that are -+ * not covered by the SoundWire framework or the IRQ handler itself. - */ -- pm_runtime_get_noresume(cs35l56->base.dev); -- -- /* -- * Mask and clear until it has been handled. -- * None of the interrupts are time-critical so use the -- * power-efficient queue. -- */ -- cs35l56_mask_soundwire_interrupts(peripheral); -- queue_work(system_power_efficient_wq, &cs35l56->sdw_irq_work); -+ sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1); -+ sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); - - return 0; - } - --static void cs35l56_sdw_irq_work(struct work_struct *work) --{ -- struct cs35l56_private *cs35l56 = container_of(work, -- struct cs35l56_private, -- sdw_irq_work); -- -- cs35l56_irq(-1, &cs35l56->base); -- -- /* unmask interrupts */ -- if (!cs35l56->sdw_irq_no_unmask) -- cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); -- -- pm_runtime_put_autosuspend(cs35l56->base.dev); --} -- - static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral) - { - struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); -@@ -302,6 +272,7 @@ static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral) - prop->source_ports = BIT(CS35L56_SDW1_CAPTURE_PORT); - prop->sink_ports = BIT(CS35L56_SDW1_PLAYBACK_PORT); - prop->paging_support = true; -+ prop->use_domain_irq = true; - prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; - prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY | SDW_SCP_INT1_IMPL_DEF; - -@@ -406,7 +377,7 @@ static int __maybe_unused cs35l56_sdw_runtime_resume(struct device *dev) - if (ret) - return ret; - -- cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); -+ cs35l56_unmask_soundwire_interrupts(cs35l56); - - return 0; - } -@@ -418,21 +389,12 @@ static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev) - if (!cs35l56->base.init_done) - return 0; - -- cs35l56_disable_sdw_interrupts(cs35l56); -+ /* runtime_resume unmasks the interrupt */ -+ cs35l56_mask_soundwire_interrupts(cs35l56); - - return cs35l56_system_suspend(dev); - } - --static int __maybe_unused cs35l56_sdw_system_resume(struct device *dev) --{ -- struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); -- -- cs35l56->sdw_irq_no_unmask = false; -- /* runtime_resume re-enables the interrupt */ -- -- return cs35l56_system_resume(dev); --} -- - static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id) - { - struct device *dev = &peripheral->dev; -@@ -447,7 +409,6 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi - cs35l56->base.dev = dev; - cs35l56->sdw_peripheral = peripheral; - cs35l56->sdw_link_num = peripheral->bus->link_id; -- INIT_WORK(&cs35l56->sdw_irq_work, cs35l56_sdw_irq_work); - - dev_set_drvdata(dev, cs35l56); - -@@ -457,6 +418,7 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi - regmap_config = &cs35l56_regmap_sdw; - break; - case 0x3563: -+ case 0x3562: - regmap_config = &cs35l63_regmap_sdw; - break; - default: -@@ -483,25 +445,21 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi - /* Start in cache-only until device is enumerated */ - regcache_cache_only(cs35l56->base.regmap, true); - -- ret = cs35l56_common_probe(cs35l56); -- if (ret != 0) -- return ret; -- -- return 0; -+ return cs35l56_common_probe(cs35l56, peripheral->irq); - } - - static void cs35l56_sdw_remove(struct sdw_slave *peripheral) - { - struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); - -- cs35l56_disable_sdw_interrupts(cs35l56); -+ cs35l56_mask_soundwire_interrupts(cs35l56); - - cs35l56_remove(cs35l56); - } - - static const struct dev_pm_ops cs35l56_sdw_pm = { - SET_RUNTIME_PM_OPS(cs35l56_sdw_runtime_suspend, cs35l56_sdw_runtime_resume, NULL) -- SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_sdw_system_resume) -+ SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_system_resume) - LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early) - /* NOIRQ stage not needed, SoundWire doesn't use a hard IRQ */ - }; -@@ -509,6 +467,7 @@ static const struct dev_pm_ops cs35l56_sdw_pm = { - static const struct sdw_device_id cs35l56_sdw_id[] = { - SDW_SLAVE_ENTRY(0x01FA, 0x3556, 0x3556), - SDW_SLAVE_ENTRY(0x01FA, 0x3557, 0x3557), -+ SDW_SLAVE_ENTRY(0x01FA, 0x3562, 0x3562), - SDW_SLAVE_ENTRY(0x01FA, 0x3563, 0x3563), - {}, - }; -diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c ---- a/sound/soc/codecs/cs35l56-shared.c -+++ b/sound/soc/codecs/cs35l56-shared.c -@@ -96,6 +96,7 @@ int cs35l56_set_patch(struct cs35l56_base *cs35l56_base) - ARRAY_SIZE(cs35l56_patch_fw)); - break; - case 0x63: -+ case 0x62: - ret = regmap_register_patch(cs35l56_base->regmap, cs35l63_patch_fw, - ARRAY_SIZE(cs35l63_patch_fw)); - break; -@@ -389,6 +390,7 @@ static void cs35l56_set_fw_reg_table(struct cs35l56_base *cs35l56_base) - } - break; - case 0x63: -+ case 0x62: - cs35l56_base->fw_reg = &cs35l63_fw_reg; - break; - } -@@ -595,6 +597,7 @@ void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire) - } - break; - case 0x63: -+ case 0x62: - regmap_multi_reg_write_bypassed(cs35l56_base->regmap, - cs35l63_system_reset_seq, - ARRAY_SIZE(cs35l63_system_reset_seq)); -@@ -613,26 +616,7 @@ void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire) - } - EXPORT_SYMBOL_NS_GPL(cs35l56_system_reset, "SND_SOC_CS35L56_SHARED"); - --int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq) --{ -- int ret; -- -- if (irq < 1) -- return 0; -- -- ret = devm_request_threaded_irq(cs35l56_base->dev, irq, NULL, cs35l56_irq, -- IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, -- "cs35l56", cs35l56_base); -- if (!ret) -- cs35l56_base->irq = irq; -- else -- dev_err(cs35l56_base->dev, "Failed to get IRQ: %d\n", ret); -- -- return ret; --} --EXPORT_SYMBOL_NS_GPL(cs35l56_irq_request, "SND_SOC_CS35L56_SHARED"); -- --irqreturn_t cs35l56_irq(int irq, void *data) -+static irqreturn_t cs35l56_irq(int irq, void *data) - { - struct cs35l56_base *cs35l56_base = data; - unsigned int status1 = 0, status8 = 0, status20 = 0; -@@ -640,23 +624,22 @@ irqreturn_t cs35l56_irq(int irq, void *data) - unsigned int val; - int rv; - -- irqreturn_t ret = IRQ_NONE; -- - if (!cs35l56_base->init_done) - return IRQ_NONE; - -- mutex_lock(&cs35l56_base->irq_lock); -+ guard(mutex)(&cs35l56_base->irq_lock); - -- rv = pm_runtime_resume_and_get(cs35l56_base->dev); -+ PM_RUNTIME_ACQUIRE_IF_ENABLED(cs35l56_base->dev, pm); -+ rv = PM_RUNTIME_ACQUIRE_ERR(&pm); - if (rv < 0) { - dev_err(cs35l56_base->dev, "irq: failed to get pm_runtime: %d\n", rv); -- goto err_unlock; -+ return IRQ_NONE; - } - - regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_STATUS, &val); - if ((val & CS35L56_IRQ1_STS_MASK) == 0) { - dev_dbg(cs35l56_base->dev, "Spurious IRQ: no pending interrupt\n"); -- goto err; -+ return IRQ_NONE; - } - - /* Ack interrupts */ -@@ -680,7 +663,7 @@ irqreturn_t cs35l56_irq(int irq, void *data) - - /* Check to see if unmasked bits are active */ - if (!status1 && !status8 && !status20) -- goto err; -+ return IRQ_NONE; - - if (status1 & CS35L56_AMP_SHORT_ERR_EINT1_MASK) - dev_crit(cs35l56_base->dev, "Amp short error\n"); -@@ -688,16 +671,27 @@ irqreturn_t cs35l56_irq(int irq, void *data) - if (status8 & CS35L56_TEMP_ERR_EINT1_MASK) - dev_crit(cs35l56_base->dev, "Overtemp error\n"); - -- ret = IRQ_HANDLED; -+ return IRQ_HANDLED; -+} - --err: -- pm_runtime_put(cs35l56_base->dev); --err_unlock: -- mutex_unlock(&cs35l56_base->irq_lock); -+int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq) -+{ -+ int ret; -+ -+ if (irq < 1) -+ return 0; -+ -+ ret = devm_request_threaded_irq(cs35l56_base->dev, irq, NULL, cs35l56_irq, -+ IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, -+ "cs35l56", cs35l56_base); -+ if (!ret) -+ cs35l56_base->irq = irq; -+ else -+ dev_err(cs35l56_base->dev, "Failed to get IRQ: %d\n", ret); - - return ret; - } --EXPORT_SYMBOL_NS_GPL(cs35l56_irq, "SND_SOC_CS35L56_SHARED"); -+EXPORT_SYMBOL_NS_GPL(cs35l56_irq_request, "SND_SOC_CS35L56_SHARED"); - - int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base) - { -@@ -816,14 +810,10 @@ int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_sou - if (!cs35l56_base->init_done) - return 0; - -- if (!cs35l56_base->can_hibernate) -- goto out_sync; -- -- /* Must be done before releasing cache-only */ -- if (!is_soundwire) -+ /* Hibernate wake must be done before releasing cache-only */ -+ if (cs35l56_base->can_hibernate && !is_soundwire) - cs35l56_issue_wake_event(cs35l56_base); - --out_sync: - ret = cs35l56_wait_for_firmware_boot(cs35l56_base); - if (ret) { - dev_err(cs35l56_base->dev, "Hibernate wake failed: %d\n", ret); -@@ -1470,6 +1460,7 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) - cs35l56_base->calibration_controls = &cs35l56_calibration_controls; - break; - case 0x35A630: -+ case 0x35A620: - cs35l56_base->calibration_controls = &cs35l63_calibration_controls; - devid = devid >> 4; - break; -diff --git a/sound/soc/codecs/cs35l56-spi.c b/sound/soc/codecs/cs35l56-spi.c ---- a/sound/soc/codecs/cs35l56-spi.c -+++ b/sound/soc/codecs/cs35l56-spi.c -@@ -40,15 +40,7 @@ static int cs35l56_spi_probe(struct spi_device *spi) - if (ret) - return ret; - -- ret = cs35l56_common_probe(cs35l56); -- if (ret != 0) -- return ret; -- -- ret = cs35l56_irq_request(&cs35l56->base, spi->irq); -- if (ret < 0) -- cs35l56_remove(cs35l56); -- -- return ret; -+ return cs35l56_common_probe(cs35l56, spi->irq); - } - - static void cs35l56_spi_remove(struct spi_device *spi) -diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c ---- a/sound/soc/codecs/cs35l56.c -+++ b/sound/soc/codecs/cs35l56.c -@@ -37,48 +37,49 @@ - #include "wm_adsp.h" - #include "cs35l56.h" - --void cs35l56_mask_soundwire_interrupts(struct sdw_slave *peripheral) -+void cs35l56_mask_soundwire_interrupts(struct cs35l56_private *cs35l56) - { - /* -+ * Mask unconditionally. -+ * - * The read of GEN_INT_STAT_1 is required as per the SoundWire spec - * for interrupt status bits to clear. - * GEN_INT_MASK_1 masks the _inputs_ to GEN_INT_STAT1. - */ -- sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); -- sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1); -- sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); -+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); -+ sdw_read_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1); -+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); - } - EXPORT_SYMBOL_NS_GPL(cs35l56_mask_soundwire_interrupts, "SND_SOC_CS35L56_CORE"); - --void cs35l56_unmask_soundwire_interrupts(struct sdw_slave *peripheral) -+void cs35l56_unmask_soundwire_interrupts(struct cs35l56_private *cs35l56) - { -- sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, CS35L56_SDW_INT_MASK_CODEC_IRQ); -+ if (!cs35l56->base.irq) -+ return; -+ -+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, -+ CS35L56_SDW_INT_MASK_CODEC_IRQ); - } - EXPORT_SYMBOL_NS_GPL(cs35l56_unmask_soundwire_interrupts, "SND_SOC_CS35L56_CORE"); - --void cs35l56_disable_sdw_interrupts(struct cs35l56_private *cs35l56) -+static void cs35l56_disable_sdw_interrupts(struct cs35l56_private *cs35l56) - { - if (!cs35l56->sdw_peripheral) - return; - -- cs35l56->sdw_irq_no_unmask = true; -- flush_work(&cs35l56->sdw_irq_work); -- -- /* Mask interrupts and flush in case sdw_irq_work was queued again */ -- cs35l56_mask_soundwire_interrupts(cs35l56->sdw_peripheral); -- flush_work(&cs35l56->sdw_irq_work); -+ cs35l56_mask_soundwire_interrupts(cs35l56); -+ if (cs35l56->base.irq) -+ disable_irq(cs35l56->base.irq); - } --EXPORT_SYMBOL_NS_GPL(cs35l56_disable_sdw_interrupts, "SND_SOC_CS35L56_CORE"); - --void cs35l56_enable_sdw_interrupts(struct cs35l56_private *cs35l56) -+static void cs35l56_enable_sdw_interrupts(struct cs35l56_private *cs35l56) - { -- if (!cs35l56->sdw_peripheral) -+ if (!cs35l56->sdw_peripheral || !cs35l56->base.irq) - return; - -- cs35l56->sdw_irq_no_unmask = false; -- cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); -+ enable_irq(cs35l56->base.irq); -+ cs35l56_unmask_soundwire_interrupts(cs35l56); - } --EXPORT_SYMBOL_NS_GPL(cs35l56_enable_sdw_interrupts, "SND_SOC_CS35L56_CORE"); - - static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event); -@@ -828,11 +829,7 @@ static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing - { - int ret; - -- /* -- * Disable SoundWire interrupts to prevent race with IRQ work. -- * Setting sdw_irq_no_unmask prevents the handler re-enabling -- * the SoundWire interrupt. -- */ -+ /* Disable SoundWire interrupts to prevent race with IRQ handler thread */ - cs35l56_disable_sdw_interrupts(cs35l56); - - ret = cs35l56_firmware_shutdown(&cs35l56->base); -@@ -1402,6 +1399,7 @@ static int _cs35l56_component_probe(struct snd_soc_component *component) - ARRAY_SIZE(cs35l56_controls)); - break; - case 0x63: -+ case 0x62: - ret = snd_soc_add_component_controls(component, cs35l63_controls, - ARRAY_SIZE(cs35l63_controls)); - break; -@@ -1941,7 +1939,7 @@ static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l5 - return ret; - } - --int cs35l56_common_probe(struct cs35l56_private *cs35l56) -+int cs35l56_common_probe(struct cs35l56_private *cs35l56, int irq) - { - int ret; - -@@ -2018,16 +2016,24 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) - goto err_remove_wm_adsp; - } - -+ ret = cs35l56_irq_request(&cs35l56->base, irq); -+ if (ret) -+ goto err_remove_wm_adsp; -+ - ret = snd_soc_register_component(cs35l56->base.dev, - &soc_component_dev_cs35l56, - cs35l56_dai, ARRAY_SIZE(cs35l56_dai)); - if (ret < 0) { - dev_err_probe(cs35l56->base.dev, ret, "Register codec failed\n"); -- goto err_remove_wm_adsp; -+ goto err_free_irq; - } - - return 0; - -+err_free_irq: -+ if (cs35l56->base.irq) -+ devm_free_irq(cs35l56->base.dev, cs35l56->base.irq, &cs35l56->base); -+ - err_remove_wm_adsp: - wm_adsp2_remove(&cs35l56->dsp); - -diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h ---- a/sound/soc/codecs/cs35l56.h -+++ b/sound/soc/codecs/cs35l56.h -@@ -39,8 +39,6 @@ struct cs35l56_private { - struct sdw_slave *sdw_peripheral; - struct regmap *sdw_bus_regmap; - const char *fallback_fw_suffix; -- struct work_struct sdw_irq_work; -- bool sdw_irq_no_unmask; - bool soft_resetting; - bool sdw_attached; - struct completion init_completion; -@@ -65,10 +63,8 @@ static inline struct cs35l56_private *cs35l56_private_from_base(struct cs35l56_b - - extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi; - --void cs35l56_mask_soundwire_interrupts(struct sdw_slave *peripheral); --void cs35l56_unmask_soundwire_interrupts(struct sdw_slave *peripheral); --void cs35l56_disable_sdw_interrupts(struct cs35l56_private *cs35l56); --void cs35l56_enable_sdw_interrupts(struct cs35l56_private *cs35l56); -+void cs35l56_mask_soundwire_interrupts(struct cs35l56_private *cs35l56); -+void cs35l56_unmask_soundwire_interrupts(struct cs35l56_private *cs35l56); - - int cs35l56_system_suspend(struct device *dev); - int cs35l56_system_suspend_late(struct device *dev); -@@ -76,9 +72,8 @@ int cs35l56_system_suspend_no_irq(struct device *dev); - int cs35l56_system_resume_no_irq(struct device *dev); - int cs35l56_system_resume_early(struct device *dev); - int cs35l56_system_resume(struct device *dev); --irqreturn_t cs35l56_irq(int irq, void *data); - int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq); --int cs35l56_common_probe(struct cs35l56_private *cs35l56); -+int cs35l56_common_probe(struct cs35l56_private *cs35l56, int irq); - int cs35l56_init(struct cs35l56_private *cs35l56); - void cs35l56_remove(struct cs35l56_private *cs35l56); - -diff --git a/sound/soc/codecs/cs40l50-codec.c b/sound/soc/codecs/cs40l50-codec.c ---- a/sound/soc/codecs/cs40l50-codec.c -+++ b/sound/soc/codecs/cs40l50-codec.c -@@ -288,8 +288,8 @@ static int cs40l50_codec_driver_probe(struct platform_device *pdev) - } - - static const struct platform_device_id cs40l50_id[] = { -- { "cs40l50-codec", }, -- {} -+ { .name = "cs40l50-codec" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, cs40l50_id); - -diff --git a/sound/soc/codecs/cs42l42-sdw.c b/sound/soc/codecs/cs42l42-sdw.c ---- a/sound/soc/codecs/cs42l42-sdw.c -+++ b/sound/soc/codecs/cs42l42-sdw.c -@@ -481,10 +481,20 @@ static int cs42l42_sdw_runtime_resume(struct device *dev) - regcache_cache_only(cs42l42->regmap, false); - - /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */ -- regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); -- regcache_sync(cs42l42->regmap); -+ ret = regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); -+ if (ret) -+ goto err_sync; -+ -+ ret = regcache_sync(cs42l42->regmap); -+ if (ret) -+ goto err_sync; - - return 0; -+ -+err_sync: -+ regcache_cache_only(cs42l42->regmap, true); -+ regcache_mark_dirty(cs42l42->regmap); -+ return ret; - } - - static int cs42l42_sdw_resume(struct device *dev) -diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c ---- a/sound/soc/codecs/cs42l42.c -+++ b/sound/soc/codecs/cs42l42.c -@@ -9,6 +9,7 @@ - * Author: Michael White - */ - -+#include - #include - #include - #include -@@ -565,7 +566,7 @@ static int cs42l42_set_jack(struct snd_soc_component *component, struct snd_soc_ - struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); - - /* Prevent race with interrupt handler */ -- mutex_lock(&cs42l42->irq_lock); -+ guard(mutex)(&cs42l42->irq_lock); - cs42l42->jack = jk; - - if (jk) { -@@ -581,7 +582,6 @@ static int cs42l42_set_jack(struct snd_soc_component *component, struct snd_soc_ - break; - } - } -- mutex_unlock(&cs42l42->irq_lock); - - return 0; - } -@@ -1668,13 +1668,10 @@ irqreturn_t cs42l42_irq_thread(int irq, void *data) - unsigned int current_button_status; - unsigned int i; - -- pm_runtime_get_sync(cs42l42->dev); -- mutex_lock(&cs42l42->irq_lock); -- if (cs42l42->suspended || !cs42l42->init_done) { -- mutex_unlock(&cs42l42->irq_lock); -- pm_runtime_put_autosuspend(cs42l42->dev); -+ guard(pm_runtime_active_auto)(cs42l42->dev); -+ guard(mutex)(&cs42l42->irq_lock); -+ if (cs42l42->suspended || !cs42l42->init_done) - return IRQ_NONE; -- } - - /* Read sticky registers to clear interurpt */ - for (i = 0; i < ARRAY_SIZE(stickies); i++) { -@@ -1774,9 +1771,6 @@ irqreturn_t cs42l42_irq_thread(int irq, void *data) - } - } - -- mutex_unlock(&cs42l42->irq_lock); -- pm_runtime_put_autosuspend(cs42l42->dev); -- - return IRQ_HANDLED; - } - EXPORT_SYMBOL_NS_GPL(cs42l42_irq_thread, "SND_SOC_CS42L42_CORE"); -@@ -2163,23 +2157,23 @@ int cs42l42_suspend(struct device *dev) - * future interrupts. This ensures a safe disable if the interrupt - * is shared. - */ -- mutex_lock(&cs42l42->irq_lock); -- cs42l42->suspended = true; -+ scoped_guard(mutex, &cs42l42->irq_lock) { -+ cs42l42->suspended = true; - -- /* Save register values that will be overwritten by shutdown sequence */ -- for (i = 0; i < ARRAY_SIZE(cs42l42_shutdown_seq); ++i) { -- regmap_read(cs42l42->regmap, cs42l42_shutdown_seq[i].reg, ®); -- save_regs[i] = (u8)reg; -+ /* Save register values that will be overwritten by shutdown sequence */ -+ for (i = 0; i < ARRAY_SIZE(cs42l42_shutdown_seq); ++i) { -+ regmap_read(cs42l42->regmap, cs42l42_shutdown_seq[i].reg, ®); -+ save_regs[i] = (u8)reg; -+ } -+ -+ /* Shutdown codec */ -+ regmap_multi_reg_write(cs42l42->regmap, -+ cs42l42_shutdown_seq, -+ ARRAY_SIZE(cs42l42_shutdown_seq)); -+ -+ /* All interrupt sources are now disabled */ - } - -- /* Shutdown codec */ -- regmap_multi_reg_write(cs42l42->regmap, -- cs42l42_shutdown_seq, -- ARRAY_SIZE(cs42l42_shutdown_seq)); -- -- /* All interrupt sources are now disabled */ -- mutex_unlock(&cs42l42->irq_lock); -- - /* Wait for power-down complete */ - msleep(CS42L42_PDN_DONE_TIME_MS); - ret = regmap_read_poll_timeout(cs42l42->regmap, -@@ -2250,13 +2244,13 @@ void cs42l42_resume_restore(struct device *dev) - regcache_cache_only(cs42l42->regmap, false); - regcache_mark_dirty(cs42l42->regmap); - -- mutex_lock(&cs42l42->irq_lock); -- /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */ -- regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); -- regcache_sync(cs42l42->regmap); -+ scoped_guard(mutex, &cs42l42->irq_lock) { -+ /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */ -+ regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); -+ regcache_sync(cs42l42->regmap); - -- cs42l42->suspended = false; -- mutex_unlock(&cs42l42->irq_lock); -+ cs42l42->suspended = false; -+ } - - dev_dbg(dev, "System resumed\n"); - } -diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c ---- a/sound/soc/codecs/cs42l43-jack.c -+++ b/sound/soc/codecs/cs42l43-jack.c -@@ -6,6 +6,7 @@ - // Cirrus Logic International Semiconductor Ltd. - - #include -+#include - #include - #include - #include -@@ -67,6 +68,21 @@ static int cs42l43_find_index(struct cs42l43_codec *priv, const char * const pro - return -EINVAL; - } - -+static void cs42l43_apply_accdet_config(struct cs42l43_codec *priv, -+ unsigned int autocontrol, -+ unsigned int pdncntl) -+{ -+ struct cs42l43 *cs42l43 = priv->core; -+ -+ regmap_update_bits(cs42l43->regmap, CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, -+ CS42L43_JACKDET_MODE_MASK | CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK | -+ CS42L43_HSBIAS_SENSE_TRIP_MASK, autocontrol); -+ regmap_update_bits(cs42l43->regmap, CS42L43_PDNCNTL, -+ CS42L43_RING_SENSE_EN_MASK, pdncntl); -+ -+ dev_dbg(priv->dev, "Successfully configured accessory detect\n"); -+} -+ - int cs42l43_set_jack(struct snd_soc_component *component, - struct snd_soc_jack *jack, void *d) - { -@@ -80,31 +96,34 @@ int cs42l43_set_jack(struct snd_soc_component *component, - - dev_dbg(priv->dev, "Configure accessory detect\n"); - -- ret = pm_runtime_resume_and_get(priv->dev); -+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(priv->dev, pm); -+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm); - if (ret) { - dev_err(priv->dev, "Failed to resume for jack config: %d\n", ret); - return ret; - } - -- mutex_lock(&priv->jack_lock); -+ guard(mutex)(&priv->jack_lock); - - priv->jack_hp = jack; - -- if (!jack) -- goto done; -+ if (!jack) { -+ cs42l43_apply_accdet_config(priv, autocontrol, pdncntl); -+ return 0; -+ } - - ret = device_property_count_u32(cs42l43->dev, "cirrus,buttons-ohms"); - if (ret != -EINVAL) { - if (ret < 0) { - dev_err(priv->dev, "Property cirrus,buttons-ohms malformed: %d\n", - ret); -- goto error; -+ return ret; - } - - if (ret > CS42L43_N_BUTTONS) { - ret = -EINVAL; - dev_err(priv->dev, "Property cirrus,buttons-ohms too many entries\n"); -- goto error; -+ return ret; - } - - ret = device_property_read_u32_array(cs42l43->dev, "cirrus,buttons-ohms", -@@ -112,7 +131,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, - if (ret < 0) { - dev_err(priv->dev, "Property cirrus,button-ohms malformed: %d\n", - ret); -- goto error; -+ return ret; - } - } else { - priv->buttons[0] = 70; -@@ -124,7 +143,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, - ret = cs42l43_find_index(priv, "cirrus,detect-us", 50000, &priv->detect_us, - cs42l43_accdet_us, ARRAY_SIZE(cs42l43_accdet_us)); - if (ret < 0) -- goto error; -+ return ret; - - hs2 |= ret << CS42L43_AUTO_HSDET_TIME_SHIFT; - -@@ -134,7 +153,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, - &priv->bias_ramp_ms, cs42l43_accdet_ramp_ms, - ARRAY_SIZE(cs42l43_accdet_ramp_ms)); - if (ret < 0) -- goto error; -+ return ret; - - hs2 |= ret << CS42L43_HSBIAS_RAMP_SHIFT; - -@@ -142,7 +161,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, - &priv->bias_sense_ua, cs42l43_accdet_bias_sense, - ARRAY_SIZE(cs42l43_accdet_bias_sense)); - if (ret < 0) -- goto error; -+ return ret; - - if (priv->bias_sense_ua) - autocontrol |= ret << CS42L43_HSBIAS_SENSE_TRIP_SHIFT; -@@ -154,7 +173,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, - &priv->tip_debounce_ms); - if (ret < 0 && ret != -EINVAL) { - dev_err(priv->dev, "Property cirrus,tip-debounce-ms malformed: %d\n", ret); -- goto error; -+ return ret; - } - - /* This tip sense invert is set normally, as TIPSENSE_INV already inverted */ -@@ -170,7 +189,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, - &priv->tip_fall_db_ms, cs42l43_accdet_db_ms, - ARRAY_SIZE(cs42l43_accdet_db_ms)); - if (ret < 0) -- goto error; -+ return ret; - - tip_deb |= ret << CS42L43_TIPSENSE_FALLING_DB_TIME_SHIFT; - -@@ -178,7 +197,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, - &priv->tip_rise_db_ms, cs42l43_accdet_db_ms, - ARRAY_SIZE(cs42l43_accdet_db_ms)); - if (ret < 0) -- goto error; -+ return ret; - - tip_deb |= ret << CS42L43_TIPSENSE_RISING_DB_TIME_SHIFT; - -@@ -199,7 +218,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, - NULL, cs42l43_accdet_db_ms, - ARRAY_SIZE(cs42l43_accdet_db_ms)); - if (ret < 0) -- goto error; -+ return ret; - - ring_deb |= ret << CS42L43_RINGSENSE_FALLING_DB_TIME_SHIFT; - -@@ -207,7 +226,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, - NULL, cs42l43_accdet_db_ms, - ARRAY_SIZE(cs42l43_accdet_db_ms)); - if (ret < 0) -- goto error; -+ return ret; - - ring_deb |= ret << CS42L43_RINGSENSE_RISING_DB_TIME_SHIFT; - pdncntl |= CS42L43_RING_SENSE_EN_MASK; -@@ -228,23 +247,9 @@ int cs42l43_set_jack(struct snd_soc_component *component, - CS42L43_HSBIAS_RAMP_MASK | CS42L43_HSDET_MODE_MASK | - CS42L43_AUTO_HSDET_TIME_MASK, hs2); - --done: -- ret = 0; -+ cs42l43_apply_accdet_config(priv, autocontrol, pdncntl); - -- regmap_update_bits(cs42l43->regmap, CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, -- CS42L43_JACKDET_MODE_MASK | CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK | -- CS42L43_HSBIAS_SENSE_TRIP_MASK, autocontrol); -- regmap_update_bits(cs42l43->regmap, CS42L43_PDNCNTL, -- CS42L43_RING_SENSE_EN_MASK, pdncntl); -- -- dev_dbg(priv->dev, "Successfully configured accessory detect\n"); -- --error: -- mutex_unlock(&priv->jack_lock); -- -- pm_runtime_put_autosuspend(priv->dev); -- -- return ret; -+ return 0; - } - - static void cs42l43_start_hs_bias(struct cs42l43_codec *priv, bool type_detect) -@@ -370,22 +375,22 @@ irqreturn_t cs42l43_button_press(int irq, void *data) - { - struct cs42l43_codec *priv = data; - struct cs42l43 *cs42l43 = priv->core; -- irqreturn_t iret = IRQ_NONE; - unsigned int buttons = 0; - unsigned int val = 0; - int i, ret; - -- ret = pm_runtime_resume_and_get(priv->dev); -+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(priv->dev, pm); -+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm); - if (ret) { - dev_err(priv->dev, "Failed to resume for button press: %d\n", ret); -- return iret; -+ return IRQ_NONE; - } - -- mutex_lock(&priv->jack_lock); -+ guard(mutex)(&priv->jack_lock); - - if (!priv->button_detect_running) { - dev_dbg(priv->dev, "Spurious button press IRQ\n"); -- goto error; -+ return IRQ_NONE; - } - - // Wait for 2 full cycles of comb filter to ensure good reading -@@ -396,12 +401,12 @@ irqreturn_t cs42l43_button_press(int irq, void *data) - /* Bail if jack removed, the button is irrelevant and likely invalid */ - if (!cs42l43_jack_present(priv)) { - dev_dbg(priv->dev, "Button ignored due to removal\n"); -- goto error; -+ return IRQ_NONE; - } - - if (val & CS42L43_HSBIAS_CLAMP_STS_MASK) { - dev_dbg(priv->dev, "Button ignored due to bias sense\n"); -- goto error; -+ return IRQ_NONE; - } - - val = (val & CS42L43_HSDET_DC_STS_MASK) >> CS42L43_HSDET_DC_STS_SHIFT; -@@ -424,45 +429,32 @@ irqreturn_t cs42l43_button_press(int irq, void *data) - - snd_soc_jack_report(priv->jack_hp, buttons, CS42L43_JACK_BUTTONS); - -- iret = IRQ_HANDLED; -- --error: -- mutex_unlock(&priv->jack_lock); -- -- pm_runtime_put_autosuspend(priv->dev); -- -- return iret; -+ return IRQ_HANDLED; - } - - irqreturn_t cs42l43_button_release(int irq, void *data) - { - struct cs42l43_codec *priv = data; -- irqreturn_t iret = IRQ_NONE; - int ret; - -- ret = pm_runtime_resume_and_get(priv->dev); -+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(priv->dev, pm); -+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm); - if (ret) { - dev_err(priv->dev, "Failed to resume for button release: %d\n", ret); -- return iret; -+ return IRQ_NONE; - } - -- mutex_lock(&priv->jack_lock); -+ guard(mutex)(&priv->jack_lock); - -- if (priv->button_detect_running) { -- dev_dbg(priv->dev, "Button release IRQ\n"); -- -- snd_soc_jack_report(priv->jack_hp, 0, CS42L43_JACK_BUTTONS); -- -- iret = IRQ_HANDLED; -- } else { -+ if (!priv->button_detect_running) { - dev_dbg(priv->dev, "Spurious button release IRQ\n"); -+ return IRQ_NONE; - } - -- mutex_unlock(&priv->jack_lock); -+ dev_dbg(priv->dev, "Button release IRQ\n"); -+ snd_soc_jack_report(priv->jack_hp, 0, CS42L43_JACK_BUTTONS); - -- pm_runtime_put_autosuspend(priv->dev); -- -- return iret; -+ return IRQ_HANDLED; - } - - void cs42l43_bias_sense_timeout(struct work_struct *work) -@@ -472,13 +464,14 @@ void cs42l43_bias_sense_timeout(struct work_struct *work) - struct cs42l43 *cs42l43 = priv->core; - int ret; - -- ret = pm_runtime_resume_and_get(priv->dev); -+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(priv->dev, pm); -+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm); - if (ret) { - dev_err(priv->dev, "Failed to resume for bias sense: %d\n", ret); - return; - } - -- mutex_lock(&priv->jack_lock); -+ guard(mutex)(&priv->jack_lock); - - if (cs42l43_jack_present(priv) && priv->button_detect_running) { - dev_dbg(priv->dev, "Bias sense timeout out, restore bias\n"); -@@ -491,10 +484,6 @@ void cs42l43_bias_sense_timeout(struct work_struct *work) - CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, - CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK); - } -- -- mutex_unlock(&priv->jack_lock); -- -- pm_runtime_put_autosuspend(priv->dev); - } - - static const struct reg_sequence cs42l43_3pole_patch[] = { -@@ -896,9 +885,8 @@ int cs42l43_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); - -- mutex_lock(&priv->jack_lock); -+ guard(mutex)(&priv->jack_lock); - ucontrol->value.integer.value[0] = priv->jack_override; -- mutex_unlock(&priv->jack_lock); - - return 0; - } -@@ -914,17 +902,13 @@ int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u - if (override >= e->items) - return -EINVAL; - -- mutex_lock(&priv->jack_lock); -+ guard(mutex)(&priv->jack_lock); - -- if (!cs42l43_jack_present(priv)) { -- mutex_unlock(&priv->jack_lock); -+ if (!cs42l43_jack_present(priv)) - return -EBUSY; -- } - -- if (override == priv->jack_override) { -- mutex_unlock(&priv->jack_lock); -+ if (override == priv->jack_override) - return 0; -- } - - priv->jack_override = override; - -@@ -984,7 +968,5 @@ int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u - cs42l43_jack_override_modes[override].report); - } - -- mutex_unlock(&priv->jack_lock); -- - return 1; - } -diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c ---- a/sound/soc/codecs/cs42l43.c -+++ b/sound/soc/codecs/cs42l43.c -@@ -8,6 +8,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -1207,14 +1208,12 @@ static void cs42l43_spk_vu_sync(struct cs42l43_codec *priv) - { - struct cs42l43 *cs42l43 = priv->core; - -- mutex_lock(&priv->spk_vu_lock); -+ guard(mutex)(&priv->spk_vu_lock); - - regmap_update_bits(cs42l43->regmap, CS42L43_INTP_VOLUME_CTRL1, - CS42L43_AMP1_2_VU_MASK, CS42L43_AMP1_2_VU_MASK); - regmap_update_bits(cs42l43->regmap, CS42L43_INTP_VOLUME_CTRL1, - CS42L43_AMP1_2_VU_MASK, 0); -- -- mutex_unlock(&priv->spk_vu_lock); - } - - static int cs42l43_shutter_get(struct cs42l43_codec *priv, unsigned int shift) -@@ -1601,7 +1600,7 @@ static int cs42l43_pll_ev(struct snd_soc_dapm_widget *w, - struct cs42l43 *cs42l43 = priv->core; - int ret; - -- mutex_lock(&cs42l43->pll_lock); -+ guard(mutex)(&cs42l43->pll_lock); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: -@@ -1626,8 +1625,6 @@ static int cs42l43_pll_ev(struct snd_soc_dapm_widget *w, - break; - } - -- mutex_unlock(&cs42l43->pll_lock); -- - return ret; - } - -@@ -2565,13 +2562,10 @@ static int cs42l43_set_sysclk(struct snd_soc_component *component, int clk_id, - { - struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); - struct cs42l43 *cs42l43 = priv->core; -- int ret; - -- mutex_lock(&cs42l43->pll_lock); -- ret = cs42l43_set_pll(priv, src, freq); -- mutex_unlock(&cs42l43->pll_lock); -+ guard(mutex)(&cs42l43->pll_lock); - -- return ret; -+ return cs42l43_set_pll(priv, src, freq); - } - - static int cs42l43_component_probe(struct snd_soc_component *component) -@@ -2941,8 +2935,8 @@ static const struct dev_pm_ops cs42l43_codec_pm_ops = { - }; - - static const struct platform_device_id cs42l43_codec_id_table[] = { -- { "cs42l43-codec", }, -- {} -+ { .name = "cs42l43-codec" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, cs42l43_codec_id_table); - -diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c ---- a/sound/soc/codecs/cs42l84.c -+++ b/sound/soc/codecs/cs42l84.c -@@ -10,6 +10,7 @@ - - #include - #include -+#include - #include - #include - #include -@@ -281,10 +282,9 @@ static int cs42l84_set_jack(struct snd_soc_component *component, struct snd_soc_ - struct cs42l84_private *cs42l84 = snd_soc_component_get_drvdata(component); - - /* Prevent race with interrupt handler */ -- mutex_lock(&cs42l84->irq_lock); -+ guard(mutex)(&cs42l84->irq_lock); - cs42l84->jack = jk; - snd_soc_jack_report(jk, cs42l84->hs_type, SND_JACK_HEADSET); -- mutex_unlock(&cs42l84->irq_lock); - - return 0; - } -@@ -831,7 +831,7 @@ static irqreturn_t cs42l84_irq_thread(int irq, void *data) - u8 current_ring_state; - int i; - -- mutex_lock(&cs42l84->irq_lock); -+ guard(mutex)(&cs42l84->irq_lock); - /* Read sticky registers to clear interrupt */ - for (i = 0; i < ARRAY_SIZE(stickies); i++) { - regmap_read(cs42l84->regmap, irq_params_table[i].status_addr, -@@ -902,8 +902,6 @@ static irqreturn_t cs42l84_irq_thread(int irq, void *data) - break; - } - -- mutex_unlock(&cs42l84->irq_lock); -- - return IRQ_HANDLED; - } - -@@ -919,8 +917,6 @@ static irqreturn_t cs42l84_irq_thread(int irq, void *data) - } - } - -- mutex_unlock(&cs42l84->irq_lock); -- - return IRQ_HANDLED; - } - -diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c ---- a/sound/soc/codecs/cs43130.c -+++ b/sound/soc/codecs/cs43130.c -@@ -6,6 +6,7 @@ - * - * Authors: Li Xu - */ -+#include - #include - #include - #include -@@ -818,26 +819,26 @@ static int cs43130_dsd_hw_params(struct snd_pcm_substream *substream, - unsigned int required_clk; - u8 dsd_speed; - -- mutex_lock(&cs43130->clk_mutex); -- if (!cs43130->clk_req) { -- /* no DAI is currently using clk */ -- if (!(CS43130_MCLK_22M % params_rate(params))) -- required_clk = CS43130_MCLK_22M; -- else -- required_clk = CS43130_MCLK_24M; -+ scoped_guard(mutex, &cs43130->clk_mutex) { -+ if (!cs43130->clk_req) { -+ /* no DAI is currently using clk */ -+ if (!(CS43130_MCLK_22M % params_rate(params))) -+ required_clk = CS43130_MCLK_22M; -+ else -+ required_clk = CS43130_MCLK_24M; - -- cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); -- if (cs43130->pll_bypass) -- cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); -- else -- cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); -+ cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); -+ if (cs43130->pll_bypass) -+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); -+ else -+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); -+ } -+ -+ cs43130->clk_req++; -+ if (cs43130->clk_req == 2) -+ cs43130_pcm_dsd_mix(true, cs43130->regmap); - } - -- cs43130->clk_req++; -- if (cs43130->clk_req == 2) -- cs43130_pcm_dsd_mix(true, cs43130->regmap); -- mutex_unlock(&cs43130->clk_mutex); -- - switch (params_rate(params)) { - case 176400: - dsd_speed = 0; -@@ -881,26 +882,26 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream, - unsigned int required_clk; - u8 dsd_speed; - -- mutex_lock(&cs43130->clk_mutex); -- if (!cs43130->clk_req) { -- /* no DAI is currently using clk */ -- if (!(CS43130_MCLK_22M % params_rate(params))) -- required_clk = CS43130_MCLK_22M; -- else -- required_clk = CS43130_MCLK_24M; -+ scoped_guard(mutex, &cs43130->clk_mutex) { -+ if (!cs43130->clk_req) { -+ /* no DAI is currently using clk */ -+ if (!(CS43130_MCLK_22M % params_rate(params))) -+ required_clk = CS43130_MCLK_22M; -+ else -+ required_clk = CS43130_MCLK_24M; - -- cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); -- if (cs43130->pll_bypass) -- cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); -- else -- cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); -+ cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); -+ if (cs43130->pll_bypass) -+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); -+ else -+ cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); -+ } -+ -+ cs43130->clk_req++; -+ if (cs43130->clk_req == 2) -+ cs43130_pcm_dsd_mix(true, cs43130->regmap); - } - -- cs43130->clk_req++; -- if (cs43130->clk_req == 2) -- cs43130_pcm_dsd_mix(true, cs43130->regmap); -- mutex_unlock(&cs43130->clk_mutex); -- - switch (dai->id) { - case CS43130_ASP_DOP_DAI: - case CS43130_XSP_DOP_DAI: -@@ -988,14 +989,13 @@ static int cs43130_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_component *component = dai->component; - struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); - -- mutex_lock(&cs43130->clk_mutex); -+ guard(mutex)(&cs43130->clk_mutex); - cs43130->clk_req--; - if (!cs43130->clk_req) { - /* no DAI is currently using clk */ - cs43130_change_clksrc(component, CS43130_MCLK_SRC_RCO); - cs43130_pcm_dsd_mix(false, cs43130->regmap); - } -- mutex_unlock(&cs43130->clk_mutex); - - return 0; - } -diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c ---- a/sound/soc/codecs/cs4349.c -+++ b/sound/soc/codecs/cs4349.c -@@ -340,7 +340,13 @@ static int cs4349_runtime_resume(struct device *dev) - gpiod_set_value_cansleep(cs4349->reset_gpio, 1); - - regcache_cache_only(cs4349->regmap, false); -- regcache_sync(cs4349->regmap); -+ ret = regcache_sync(cs4349->regmap); -+ if (ret) { -+ regcache_cache_only(cs4349->regmap, true); -+ regcache_mark_dirty(cs4349->regmap); -+ gpiod_set_value_cansleep(cs4349->reset_gpio, 0); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/cs47l15.c b/sound/soc/codecs/cs47l15.c ---- a/sound/soc/codecs/cs47l15.c -+++ b/sound/soc/codecs/cs47l15.c -@@ -6,6 +6,7 @@ - // Cirrus Logic International Semiconductor Ltd. - // - -+#include - #include - #include - #include -@@ -1285,9 +1286,8 @@ static int cs47l15_component_probe(struct snd_soc_component *component) - - snd_soc_component_init_regmap(component, madera->regmap); - -- mutex_lock(&madera->dapm_ptr_lock); -- madera->dapm = snd_soc_component_to_dapm(component); -- mutex_unlock(&madera->dapm_ptr_lock); -+ scoped_guard(mutex, &madera->dapm_ptr_lock) -+ madera->dapm = snd_soc_component_to_dapm(component); - - ret = madera_init_inputs(component); - if (ret) -@@ -1317,9 +1317,8 @@ static void cs47l15_component_remove(struct snd_soc_component *component) - struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component); - struct madera *madera = cs47l15->core.madera; - -- mutex_lock(&madera->dapm_ptr_lock); -- madera->dapm = NULL; -- mutex_unlock(&madera->dapm_ptr_lock); -+ scoped_guard(mutex, &madera->dapm_ptr_lock) -+ madera->dapm = NULL; - - wm_adsp2_component_remove(&cs47l15->core.adsp[0], component); - } -diff --git a/sound/soc/codecs/cs47l35.c b/sound/soc/codecs/cs47l35.c ---- a/sound/soc/codecs/cs47l35.c -+++ b/sound/soc/codecs/cs47l35.c -@@ -6,6 +6,7 @@ - // Cirrus Logic International Semiconductor Ltd. - // - -+#include - #include - #include - #include -@@ -1566,9 +1567,8 @@ static int cs47l35_component_probe(struct snd_soc_component *component) - - snd_soc_component_init_regmap(component, madera->regmap); - -- mutex_lock(&madera->dapm_ptr_lock); -- madera->dapm = snd_soc_component_to_dapm(component); -- mutex_unlock(&madera->dapm_ptr_lock); -+ scoped_guard(mutex, &madera->dapm_ptr_lock) -+ madera->dapm = snd_soc_component_to_dapm(component); - - ret = madera_init_inputs(component); - if (ret) -@@ -1600,9 +1600,8 @@ static void cs47l35_component_remove(struct snd_soc_component *component) - struct madera *madera = cs47l35->core.madera; - int i; - -- mutex_lock(&madera->dapm_ptr_lock); -- madera->dapm = NULL; -- mutex_unlock(&madera->dapm_ptr_lock); -+ scoped_guard(mutex, &madera->dapm_ptr_lock) -+ madera->dapm = NULL; - - for (i = 0; i < CS47L35_NUM_ADSP; i++) - wm_adsp2_component_remove(&cs47l35->core.adsp[i], component); -diff --git a/sound/soc/codecs/cs47l85.c b/sound/soc/codecs/cs47l85.c ---- a/sound/soc/codecs/cs47l85.c -+++ b/sound/soc/codecs/cs47l85.c -@@ -6,6 +6,7 @@ - // Cirrus Logic International Semiconductor Ltd. - // - -+#include - #include - #include - #include -@@ -2504,9 +2505,8 @@ static int cs47l85_component_probe(struct snd_soc_component *component) - - snd_soc_component_init_regmap(component, madera->regmap); - -- mutex_lock(&madera->dapm_ptr_lock); -- madera->dapm = snd_soc_component_to_dapm(component); -- mutex_unlock(&madera->dapm_ptr_lock); -+ scoped_guard(mutex, &madera->dapm_ptr_lock) -+ madera->dapm = snd_soc_component_to_dapm(component); - - ret = madera_init_inputs(component); - if (ret) -@@ -2537,9 +2537,8 @@ static void cs47l85_component_remove(struct snd_soc_component *component) - struct madera *madera = cs47l85->core.madera; - int i; - -- mutex_lock(&madera->dapm_ptr_lock); -- madera->dapm = NULL; -- mutex_unlock(&madera->dapm_ptr_lock); -+ scoped_guard(mutex, &madera->dapm_ptr_lock) -+ madera->dapm = NULL; - - for (i = 0; i < CS47L85_NUM_ADSP; i++) - wm_adsp2_component_remove(&cs47l85->core.adsp[i], component); -diff --git a/sound/soc/codecs/cs47l90.c b/sound/soc/codecs/cs47l90.c ---- a/sound/soc/codecs/cs47l90.c -+++ b/sound/soc/codecs/cs47l90.c -@@ -6,6 +6,7 @@ - // Cirrus Logic International Semiconductor Ltd. - // - -+#include - #include - #include - #include -@@ -2423,9 +2424,8 @@ static int cs47l90_component_probe(struct snd_soc_component *component) - - snd_soc_component_init_regmap(component, madera->regmap); - -- mutex_lock(&madera->dapm_ptr_lock); -- madera->dapm = snd_soc_component_to_dapm(component); -- mutex_unlock(&madera->dapm_ptr_lock); -+ scoped_guard(mutex, &madera->dapm_ptr_lock) -+ madera->dapm = snd_soc_component_to_dapm(component); - - ret = madera_init_inputs(component); - if (ret) -@@ -2456,9 +2456,8 @@ static void cs47l90_component_remove(struct snd_soc_component *component) - struct madera *madera = cs47l90->core.madera; - int i; - -- mutex_lock(&madera->dapm_ptr_lock); -- madera->dapm = NULL; -- mutex_unlock(&madera->dapm_ptr_lock); -+ scoped_guard(mutex, &madera->dapm_ptr_lock) -+ madera->dapm = NULL; - - for (i = 0; i < CS47L90_NUM_ADSP; i++) - wm_adsp2_component_remove(&cs47l90->core.adsp[i], component); -diff --git a/sound/soc/codecs/cs47l92.c b/sound/soc/codecs/cs47l92.c ---- a/sound/soc/codecs/cs47l92.c -+++ b/sound/soc/codecs/cs47l92.c -@@ -6,6 +6,7 @@ - // Cirrus Logic International Semiconductor Ltd. - // - -+#include - #include - #include - #include -@@ -1892,9 +1893,8 @@ static int cs47l92_component_probe(struct snd_soc_component *component) - - snd_soc_component_init_regmap(component, madera->regmap); - -- mutex_lock(&madera->dapm_ptr_lock); -- madera->dapm = snd_soc_component_to_dapm(component); -- mutex_unlock(&madera->dapm_ptr_lock); -+ scoped_guard(mutex, &madera->dapm_ptr_lock) -+ madera->dapm = snd_soc_component_to_dapm(component); - - ret = madera_init_inputs(component); - if (ret) -@@ -1922,9 +1922,8 @@ static void cs47l92_component_remove(struct snd_soc_component *component) - struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component); - struct madera *madera = cs47l92->core.madera; - -- mutex_lock(&madera->dapm_ptr_lock); -- madera->dapm = NULL; -- mutex_unlock(&madera->dapm_ptr_lock); -+ scoped_guard(mutex, &madera->dapm_ptr_lock) -+ madera->dapm = NULL; - - wm_adsp2_component_remove(&cs47l92->core.adsp[0], component); - } -diff --git a/sound/soc/codecs/cs48l32.c b/sound/soc/codecs/cs48l32.c ---- a/sound/soc/codecs/cs48l32.c -+++ b/sound/soc/codecs/cs48l32.c -@@ -8,6 +8,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -236,15 +237,13 @@ static int cs48l32_rate_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v - int ret; - - /* Prevent any mixer mux changes while we do this */ -- mutex_lock(&cs48l32_codec->rate_lock); -+ guard(mutex)(&cs48l32_codec->rate_lock); - - /* The write must be guarded by a number of SYSCLK cycles */ - cs48l32_spin_sysclk(cs48l32_codec); - ret = snd_soc_put_enum_double(kcontrol, ucontrol); - cs48l32_spin_sysclk(cs48l32_codec); - -- mutex_unlock(&cs48l32_codec->rate_lock); -- - return ret; - } - -@@ -2242,7 +2241,6 @@ static int cs48l32_dai_set_sysclk(struct snd_soc_dai *dai, - struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[dai->id - 1]; - unsigned int base = dai->driver->base; - unsigned int current_asp_rate, target_asp_rate; -- bool change_rate_domain = false; - int ret; - - if (clk_id == dai_priv->clk) -@@ -2284,19 +2282,15 @@ static int cs48l32_dai_set_sysclk(struct snd_soc_dai *dai, - - if ((current_asp_rate & CS48L32_ASP_RATE_MASK) != - (target_asp_rate & CS48L32_ASP_RATE_MASK)) { -- change_rate_domain = true; -- -- mutex_lock(&cs48l32_codec->rate_lock); - /* Guard the rate change with SYSCLK cycles */ -- cs48l32_spin_sysclk(cs48l32_codec); -- } -- -- snd_soc_component_update_bits(component, base + CS48L32_ASP_CONTROL1, -- CS48L32_ASP_RATE_MASK, target_asp_rate); -- -- if (change_rate_domain) { -- cs48l32_spin_sysclk(cs48l32_codec); -- mutex_unlock(&cs48l32_codec->rate_lock); -+ scoped_guard(mutex, &cs48l32_codec->rate_lock) { -+ cs48l32_spin_sysclk(cs48l32_codec); -+ snd_soc_component_update_bits(component, -+ base + CS48L32_ASP_CONTROL1, -+ CS48L32_ASP_RATE_MASK, -+ target_asp_rate); -+ cs48l32_spin_sysclk(cs48l32_codec); -+ } - } - } - -diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c ---- a/sound/soc/codecs/cx2072x.c -+++ b/sound/soc/codecs/cx2072x.c -@@ -9,6 +9,7 @@ - // - - #include -+#include - #include - #include - #include -@@ -1408,7 +1409,7 @@ static int cx2072x_jack_status_check(void *data) - unsigned int type = 0; - int state = 0; - -- mutex_lock(&cx2072x->lock); -+ guard(mutex)(&cx2072x->lock); - - regmap_read(cx2072x->regmap, CX2072X_PORTA_PIN_SENSE, &jack); - jack = jack >> 24; -@@ -1434,8 +1435,6 @@ static int cx2072x_jack_status_check(void *data) - /* clear interrupt */ - regmap_write(cx2072x->regmap, CX2072X_UM_INTERRUPT_CRTL_E, 0x12 << 24); - -- mutex_unlock(&cx2072x->lock); -- - dev_dbg(codec->dev, "CX2072X_HSDETECT type=0x%X,Jack state = %x\n", - type, state); - return state; -diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c ---- a/sound/soc/codecs/da7213.c -+++ b/sound/soc/codecs/da7213.c -@@ -11,6 +11,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -216,13 +217,10 @@ static int da7213_volsw_locked_get(struct snd_kcontrol *kcontrol, - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); -- int ret; - -- mutex_lock(&da7213->ctrl_lock); -- ret = snd_soc_get_volsw(kcontrol, ucontrol); -- mutex_unlock(&da7213->ctrl_lock); -+ guard(mutex)(&da7213->ctrl_lock); - -- return ret; -+ return snd_soc_get_volsw(kcontrol, ucontrol); - } - - static int da7213_volsw_locked_put(struct snd_kcontrol *kcontrol, -@@ -230,13 +228,10 @@ static int da7213_volsw_locked_put(struct snd_kcontrol *kcontrol, - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); -- int ret; - -- mutex_lock(&da7213->ctrl_lock); -- ret = snd_soc_put_volsw(kcontrol, ucontrol); -- mutex_unlock(&da7213->ctrl_lock); -+ guard(mutex)(&da7213->ctrl_lock); - -- return ret; -+ return snd_soc_put_volsw(kcontrol, ucontrol); - } - - static int da7213_enum_locked_get(struct snd_kcontrol *kcontrol, -@@ -244,13 +239,10 @@ static int da7213_enum_locked_get(struct snd_kcontrol *kcontrol, - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); -- int ret; - -- mutex_lock(&da7213->ctrl_lock); -- ret = snd_soc_get_enum_double(kcontrol, ucontrol); -- mutex_unlock(&da7213->ctrl_lock); -+ guard(mutex)(&da7213->ctrl_lock); - -- return ret; -+ return snd_soc_get_enum_double(kcontrol, ucontrol); - } - - static int da7213_enum_locked_put(struct snd_kcontrol *kcontrol, -@@ -258,13 +250,10 @@ static int da7213_enum_locked_put(struct snd_kcontrol *kcontrol, - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); -- int ret; - -- mutex_lock(&da7213->ctrl_lock); -- ret = snd_soc_put_enum_double(kcontrol, ucontrol); -- mutex_unlock(&da7213->ctrl_lock); -+ guard(mutex)(&da7213->ctrl_lock); - -- return ret; -+ return snd_soc_put_enum_double(kcontrol, ucontrol); - } - - /* ALC */ -@@ -465,9 +454,8 @@ static int da7213_tonegen_freq_get(struct snd_kcontrol *kcontrol, - __le16 val; - int ret; - -- mutex_lock(&da7213->ctrl_lock); -- ret = regmap_raw_read(da7213->regmap, reg, &val, sizeof(val)); -- mutex_unlock(&da7213->ctrl_lock); -+ scoped_guard(mutex, &da7213->ctrl_lock) -+ ret = regmap_raw_read(da7213->regmap, reg, &val, sizeof(val)); - - if (ret) - return ret; -@@ -499,12 +487,11 @@ static int da7213_tonegen_freq_put(struct snd_kcontrol *kcontrol, - */ - val_new = cpu_to_le16(ucontrol->value.integer.value[0]); - -- mutex_lock(&da7213->ctrl_lock); -+ guard(mutex)(&da7213->ctrl_lock); - ret = regmap_raw_read(da7213->regmap, reg, &val_old, sizeof(val_old)); - if (ret == 0 && (val_old != val_new)) - ret = regmap_raw_write(da7213->regmap, reg, - &val_new, sizeof(val_new)); -- mutex_unlock(&da7213->ctrl_lock); - - if (ret < 0) - return ret; -diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c ---- a/sound/soc/codecs/da7219.c -+++ b/sound/soc/codecs/da7219.c -@@ -8,6 +8,7 @@ - */ - - #include -+#include - #include - #include - #include -@@ -256,13 +257,10 @@ static int da7219_volsw_locked_get(struct snd_kcontrol *kcontrol, - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); -- int ret; - -- mutex_lock(&da7219->ctrl_lock); -- ret = snd_soc_get_volsw(kcontrol, ucontrol); -- mutex_unlock(&da7219->ctrl_lock); -+ guard(mutex)(&da7219->ctrl_lock); - -- return ret; -+ return snd_soc_get_volsw(kcontrol, ucontrol); - } - - static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol, -@@ -270,13 +268,10 @@ static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol, - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); -- int ret; - -- mutex_lock(&da7219->ctrl_lock); -- ret = snd_soc_put_volsw(kcontrol, ucontrol); -- mutex_unlock(&da7219->ctrl_lock); -+ guard(mutex)(&da7219->ctrl_lock); - -- return ret; -+ return snd_soc_put_volsw(kcontrol, ucontrol); - } - - static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol, -@@ -284,13 +279,10 @@ static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol, - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); -- int ret; - -- mutex_lock(&da7219->ctrl_lock); -- ret = snd_soc_get_enum_double(kcontrol, ucontrol); -- mutex_unlock(&da7219->ctrl_lock); -+ guard(mutex)(&da7219->ctrl_lock); - -- return ret; -+ return snd_soc_get_enum_double(kcontrol, ucontrol); - } - - static int da7219_enum_locked_put(struct snd_kcontrol *kcontrol, -@@ -298,13 +290,10 @@ static int da7219_enum_locked_put(struct snd_kcontrol *kcontrol, - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); -- int ret; - -- mutex_lock(&da7219->ctrl_lock); -- ret = snd_soc_put_enum_double(kcontrol, ucontrol); -- mutex_unlock(&da7219->ctrl_lock); -+ guard(mutex)(&da7219->ctrl_lock); - -- return ret; -+ return snd_soc_put_enum_double(kcontrol, ucontrol); - } - - /* ALC */ -@@ -422,9 +411,8 @@ static int da7219_tonegen_freq_get(struct snd_kcontrol *kcontrol, - __le16 val; - int ret; - -- mutex_lock(&da7219->ctrl_lock); -- ret = regmap_raw_read(da7219->regmap, reg, &val, sizeof(val)); -- mutex_unlock(&da7219->ctrl_lock); -+ scoped_guard(mutex, &da7219->ctrl_lock) -+ ret = regmap_raw_read(da7219->regmap, reg, &val, sizeof(val)); - - if (ret) - return ret; -@@ -456,12 +444,11 @@ static int da7219_tonegen_freq_put(struct snd_kcontrol *kcontrol, - */ - val_new = cpu_to_le16(ucontrol->value.integer.value[0]); - -- mutex_lock(&da7219->ctrl_lock); -+ guard(mutex)(&da7219->ctrl_lock); - ret = regmap_raw_read(da7219->regmap, reg, &val_old, sizeof(val_old)); - if (ret == 0 && (val_old != val_new)) - ret = regmap_raw_write(da7219->regmap, reg, -- &val_new, sizeof(val_new)); -- mutex_unlock(&da7219->ctrl_lock); -+ &val_new, sizeof(val_new)); - - if (ret < 0) - return ret; -@@ -1167,15 +1154,12 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, - struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); - int ret = 0; - -- mutex_lock(&da7219->pll_lock); -+ guard(mutex)(&da7219->pll_lock); - -- if ((da7219->clk_src == clk_id) && (da7219->mclk_rate == freq)) { -- mutex_unlock(&da7219->pll_lock); -+ if (da7219->clk_src == clk_id && da7219->mclk_rate == freq) - return 0; -- } - -- if ((freq < 2000000) || (freq > 54000000)) { -- mutex_unlock(&da7219->pll_lock); -+ if (freq < 2000000 || freq > 54000000) { - dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", - freq); - return -EINVAL; -@@ -1193,7 +1177,6 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, - break; - default: - dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id); -- mutex_unlock(&da7219->pll_lock); - return -EINVAL; - } - -@@ -1203,17 +1186,13 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, - freq = clk_round_rate(da7219->mclk, freq); - ret = clk_set_rate(da7219->mclk, freq); - if (ret) { -- dev_err(codec_dai->dev, "Failed to set clock rate %d\n", -- freq); -- mutex_unlock(&da7219->pll_lock); -+ dev_err(codec_dai->dev, "Failed to set clock rate %d\n", freq); - return ret; - } - } - - da7219->mclk_rate = freq; - -- mutex_unlock(&da7219->pll_lock); -- - return 0; - } - -@@ -1296,13 +1275,10 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, - { - struct snd_soc_component *component = codec_dai->component; - struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); -- int ret; - -- mutex_lock(&da7219->pll_lock); -- ret = da7219_set_pll(component, source, fout); -- mutex_unlock(&da7219->pll_lock); -+ guard(mutex)(&da7219->pll_lock); - -- return ret; -+ return da7219_set_pll(component, source, fout); - } - - static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) -diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c ---- a/sound/soc/codecs/es8316.c -+++ b/sound/soc/codecs/es8316.c -@@ -9,11 +9,13 @@ - - #include - #include -+#include - #include - #include - #include - #include - #include -+#include - #include - #include - #include -@@ -30,6 +32,13 @@ static const unsigned int supported_mclk_lrck_ratios[] = { - 256, 384, 400, 500, 512, 768, 1024 - }; - -+static const char * const es8316_supply_names[] = { -+ "avdd", -+ "cpvdd", -+ "dvdd", -+ "pvdd", -+}; -+ - struct es8316_priv { - struct mutex lock; - struct clk *mclk; -@@ -620,15 +629,15 @@ static irqreturn_t es8316_irq(int irq, void *data) - struct snd_soc_component *comp = es8316->component; - unsigned int flags; - -- mutex_lock(&es8316->lock); -+ guard(mutex)(&es8316->lock); - - regmap_read(es8316->regmap, ES8316_GPIO_FLAG, &flags); - if (flags == 0x00) -- goto out; /* Powered-down / reset */ -+ return IRQ_HANDLED; /* Powered-down / reset */ - - /* Catch spurious IRQ before set_jack is called */ - if (!es8316->jack) -- goto out; -+ return IRQ_HANDLED; - - if (es8316->jd_inverted) - flags ^= ES8316_GPIO_FLAG_HP_NOT_INSERTED; -@@ -681,8 +690,6 @@ static irqreturn_t es8316_irq(int irq, void *data) - } - } - --out: -- mutex_unlock(&es8316->lock); - return IRQ_HANDLED; - } - -@@ -699,18 +706,16 @@ static void es8316_enable_jack_detect(struct snd_soc_component *component, - es8316->jd_inverted = device_property_read_bool(component->dev, - "everest,jack-detect-inverted"); - -- mutex_lock(&es8316->lock); -+ scoped_guard(mutex, &es8316->lock) { -+ es8316->jack = jack; - -- es8316->jack = jack; -+ if (es8316->jack->status & SND_JACK_MICROPHONE) -+ es8316_enable_micbias_for_mic_gnd_short_detect(component); - -- if (es8316->jack->status & SND_JACK_MICROPHONE) -- es8316_enable_micbias_for_mic_gnd_short_detect(component); -- -- snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, -- ES8316_GPIO_ENABLE_INTERRUPT, -- ES8316_GPIO_ENABLE_INTERRUPT); -- -- mutex_unlock(&es8316->lock); -+ snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, -+ ES8316_GPIO_ENABLE_INTERRUPT, -+ ES8316_GPIO_ENABLE_INTERRUPT); -+ } - - /* Enable irq and sync initial jack state */ - enable_irq(es8316->irq); -@@ -726,7 +731,7 @@ static void es8316_disable_jack_detect(struct snd_soc_component *component) - - disable_irq(es8316->irq); - -- mutex_lock(&es8316->lock); -+ guard(mutex)(&es8316->lock); - - snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, - ES8316_GPIO_ENABLE_INTERRUPT, 0); -@@ -737,8 +742,6 @@ static void es8316_disable_jack_detect(struct snd_soc_component *component) - } - - es8316->jack = NULL; -- -- mutex_unlock(&es8316->lock); - } - - static int es8316_set_jack(struct snd_soc_component *component, -@@ -871,6 +874,11 @@ static int es8316_i2c_probe(struct i2c_client *i2c_client) - - i2c_set_clientdata(i2c_client, es8316); - -+ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(es8316_supply_names), -+ es8316_supply_names); -+ if (ret) -+ return dev_err_probe(dev, ret, "unable to enable supplies\n"); -+ - es8316->regmap = devm_regmap_init_i2c(i2c_client, &es8316_regmap); - if (IS_ERR(es8316->regmap)) - return PTR_ERR(es8316->regmap); -diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c ---- a/sound/soc/codecs/es8326.c -+++ b/sound/soc/codecs/es8326.c -@@ -6,6 +6,7 @@ - // Authors: David Yang - // - -+#include - #include - #include - #include -@@ -790,7 +791,7 @@ static void es8326_jack_button_handler(struct work_struct *work) - if (!(es8326->jack->status & SND_JACK_HEADSET)) /* Jack unplugged */ - return; - -- mutex_lock(&es8326->lock); -+ guard(mutex)(&es8326->lock); - iface = snd_soc_component_read(comp, ES8326_HPDET_STA); - switch (iface) { - case 0x93: -@@ -845,7 +846,6 @@ static void es8326_jack_button_handler(struct work_struct *work) - } - es8326_disable_micbias(es8326->component); - } -- mutex_unlock(&es8326->lock); - } - - static void es8326_jack_detect_handler(struct work_struct *work) -@@ -855,7 +855,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) - struct snd_soc_component *comp = es8326->component; - unsigned int iface; - -- mutex_lock(&es8326->lock); -+ guard(mutex)(&es8326->lock); - iface = snd_soc_component_read(comp, ES8326_HPDET_STA); - dev_dbg(comp->dev, "gpio flag %#04x", iface); - -@@ -873,7 +873,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) - regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, - ES8326_HP_DET_JACK_POL, (es8326->jd_inverted ? - ~es8326->jack_pol : es8326->jack_pol)); -- goto exit; -+ return; - } - - if ((iface & ES8326_HPINSERT_FLAG) == 0) { -@@ -930,7 +930,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) - queue_delayed_work(system_dfl_wq, &es8326->jack_detect_work, - msecs_to_jiffies(400)); - es8326->hp = 1; -- goto exit; -+ return; - } - if (es8326->jack->status & SND_JACK_HEADSET) { - /* detect button */ -@@ -939,7 +939,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) - (ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON)); - es8326_enable_micbias(es8326->component); - queue_delayed_work(system_dfl_wq, &es8326->button_press_work, 10); -- goto exit; -+ return; - } - if ((iface & ES8326_HPBUTTON_FLAG) == 0x01) { - dev_dbg(comp->dev, "Headphone detected\n"); -@@ -958,8 +958,6 @@ static void es8326_jack_detect_handler(struct work_struct *work) - usleep_range(10000, 15000); - } - } --exit: -- mutex_unlock(&es8326->lock); - } - - static irqreturn_t es8326_irq(int irq, void *dev_id) -@@ -1200,13 +1198,12 @@ static void es8326_enable_jack_detect(struct snd_soc_component *component, - { - struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); - -- mutex_lock(&es8326->lock); -- if (es8326->jd_inverted) -- snd_soc_component_update_bits(component, ES8326_HPDET_TYPE, -- ES8326_HP_DET_JACK_POL, ~es8326->jack_pol); -- es8326->jack = jack; -- -- mutex_unlock(&es8326->lock); -+ scoped_guard(mutex, &es8326->lock) { -+ if (es8326->jd_inverted) -+ snd_soc_component_update_bits(component, ES8326_HPDET_TYPE, -+ ES8326_HP_DET_JACK_POL, ~es8326->jack_pol); -+ es8326->jack = jack; -+ } - es8326_irq(es8326->irq, es8326); - } - -@@ -1219,13 +1216,12 @@ static void es8326_disable_jack_detect(struct snd_soc_component *component) - return; /* Already disabled (or never enabled) */ - cancel_delayed_work_sync(&es8326->jack_detect_work); - -- mutex_lock(&es8326->lock); -+ guard(mutex)(&es8326->lock); - if (es8326->jack->status & SND_JACK_MICROPHONE) { - es8326_disable_micbias(component); - snd_soc_jack_report(es8326->jack, 0, SND_JACK_HEADSET); - } - es8326->jack = NULL; -- mutex_unlock(&es8326->lock); - } - - static int es8326_set_jack(struct snd_soc_component *component, -diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c ---- a/sound/soc/codecs/es8328.c -+++ b/sound/soc/codecs/es8328.c -@@ -405,6 +405,11 @@ static const struct snd_soc_dapm_route es8328_dapm_routes[] = { - - { "Mic Bias", NULL, "Mic Bias Gen" }, - -+ { "LINPUT1", NULL, "Mic Bias" }, -+ { "RINPUT1", NULL, "Mic Bias" }, -+ { "LINPUT2", NULL, "Mic Bias" }, -+ { "RINPUT2", NULL, "Mic Bias" }, -+ - { "Left Mixer", NULL, "Left DAC" }, - { "Left Mixer", "Left Bypass Switch", "Left Line Mux" }, - { "Left Mixer", "Right Playback Switch", "Right DAC" }, -diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c ---- a/sound/soc/codecs/es8389.c -+++ b/sound/soc/codecs/es8389.c -@@ -36,6 +36,10 @@ struct es8389_private { - unsigned int sysclk; - int mastermode; - -+ u8 hpfl; -+ u8 hpfr; -+ u32 hpf_freq; -+ u32 capture_rate; - u8 mclk_src; - u8 vddd; - int version; -@@ -50,10 +54,29 @@ static const char * const es8389_core_supplies[] = { - static bool es8389_volatile_register(struct device *dev, - unsigned int reg) - { -- if ((reg <= 0xff)) -- return true; -- else -+ switch (reg) { -+ case ES8389_ADCL_VOL: -+ case ES8389_ADCR_VOL: -+ case ES8389_MIC1_GAIN: -+ case ES8389_MIC2_GAIN: -+ case ES8389_DACL_VOL: -+ case ES8389_DACR_VOL: -+ case ES8389_ALC_ON: -+ case ES8389_ALC_CTL: -+ case ES8389_ALC_TARGET: -+ case ES8389_ALC_GAIN: -+ case ES8389_ADC_MUTE: -+ case ES8389_OSR_VOL: -+ case ES8389_DAC_INV: -+ case ES8389_MIX_VOL: -+ case ES8389_DAC_MIX: -+ case ES8389_ADC_RESET: -+ case ES8389_ADC_MODE: -+ case ES8389_DMIC_EN: - return false; -+ default: -+ return true; -+ } - } - - static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9550, 50, 0); -@@ -63,6 +86,92 @@ static const DECLARE_TLV_DB_SCALE(mix_vol_tlv, -9500, 100, 0); - static const DECLARE_TLV_DB_SCALE(alc_target_tlv, -3200, 200, 0); - static const DECLARE_TLV_DB_SCALE(alc_max_level, -3200, 200, 0); - -+static const u32 hpf_table[10][10] = { -+ {1020, 754, 624, 559, 527, 511, 502, 498, 497, 496}, -+ {754, 495, 368, 306, 274, 259, 251, 247, 246, 244}, -+ {624, 368, 243, 182, 151, 136, 128, 124, 123, 121}, -+ {559, 306, 182, 120, 90, 75, 68, 63, 62, 60}, -+ {527, 274, 151, 90, 60, 45, 38, 33, 32, 31}, -+ {511, 259, 136, 75, 45, 30, 23, 19, 18, 17}, -+ {502, 251, 128, 68, 38, 23, 16, 13, 11, 11}, -+ {498, 247, 124, 63, 33, 19, 13, 10, 8, 8}, -+ {497, 246, 123, 62, 32, 18, 11, 8, 8, 0}, -+ {496, 244, 121, 60, 31, 17, 11, 8, 0, 0} -+}; -+ -+static bool find_best_hpf_freq(u32 target_hz, u8 *hpf1, u8 *hpf2, u32 *out) -+{ -+ int best_row = -1, best_col = -1; -+ u32 min_diff = U32_MAX; -+ u32 f, diff; -+ int i, j; -+ -+ if (target_hz > 1020) -+ return false; -+ -+ for (i = 0; i < 10; i++) { -+ for (j = i; j < 10; j++) { -+ f = hpf_table[i][j]; -+ -+ diff = (target_hz > f) ? (target_hz - f) : (f - target_hz); -+ if (diff < min_diff) { -+ min_diff = diff; -+ best_row = i; -+ best_col = j; -+ *out = f; -+ } -+ } -+ } -+ -+ *hpf1 = best_col + ES8389_HPF_OFFSET; -+ *hpf2 = best_row + ES8389_HPF_OFFSET; -+ -+ return true; -+} -+ -+static int es8389_hpf_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); -+ -+ ucontrol->value.integer.value[0] = es8389->hpf_freq; -+ return 0; -+} -+ -+static int es8389_hpf_set(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); -+ u32 freq; -+ bool hpf; -+ -+ if (es8389->hpf_freq == ucontrol->value.integer.value[0]) -+ return 0; -+ -+ if (es8389->capture_rate) { -+ freq = (ucontrol->value.integer.value[0] * 48000) / es8389->capture_rate; -+ -+ hpf = find_best_hpf_freq(freq, &es8389->hpfl, &es8389->hpfr, &es8389->hpf_freq); -+ if (!hpf) -+ return -EBUSY; -+ -+ if (es8389->hpf_freq != ucontrol->value.integer.value[0]) -+ dev_dbg(component->dev, "At the %u Hz sampling rate, %ld Hz could not be obtained." -+ "the frequency has been set to the closest value, %u Hz\n", -+ es8389->capture_rate, ucontrol->value.integer.value[0], es8389->hpf_freq); -+ -+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, es8389->hpfl); -+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, es8389->hpfr); -+ } else { -+ es8389->hpf_freq = ucontrol->value.integer.value[0]; -+ dev_dbg(component->dev, "PCM_STREAM_CAPTURE is not active.retain the input frequency\n"); -+ } -+ -+ return 1; -+} -+ - static int es8389_dmic_set(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) - { -@@ -143,6 +252,16 @@ static const struct soc_enum alc_ramprate = - static const struct soc_enum alc_winsize = - SOC_ENUM_SINGLE(ES8389_ALC_CTL, 0, 16, winsize); - -+static const char *const es8389_adcl_mux_txt[] = { -+ "Normal", -+ "ADC2 channel to ADC1 channel", -+}; -+ -+static const char *const es8389_adcr_mux_txt[] = { -+ "Normal", -+ "ADC1 channel to ADC2 channel", -+}; -+ - static const char *const es8389_outl_mux_txt[] = { - "Normal", - "DAC2 channel to DAC1 channel", -@@ -170,6 +289,20 @@ static const unsigned int es8389_pga_values[] = { - 1, 5, 6 - }; - -+static const struct soc_enum es8389_adcl_mux_enum = -+ SOC_ENUM_SINGLE(ES8389_ADC_MODE, 5, -+ ARRAY_SIZE(es8389_adcl_mux_txt), es8389_adcl_mux_txt); -+ -+static const struct snd_kcontrol_new es8389_adcl_mux_controls = -+ SOC_DAPM_ENUM("INPUTL MUX", es8389_adcl_mux_enum); -+ -+static const struct soc_enum es8389_adcr_mux_enum = -+ SOC_ENUM_SINGLE(ES8389_ADC_MODE, 4, -+ ARRAY_SIZE(es8389_adcr_mux_txt), es8389_adcr_mux_txt); -+ -+static const struct snd_kcontrol_new es8389_adcr_mux_controls = -+ SOC_DAPM_ENUM("INPUTR MUX", es8389_adcr_mux_enum); -+ - static const struct soc_enum es8389_outl_mux_enum = - SOC_ENUM_SINGLE(ES8389_DAC_MIX, 5, - ARRAY_SIZE(es8389_outl_mux_txt), es8389_outl_mux_txt); -@@ -240,6 +373,8 @@ static const struct snd_kcontrol_new es8389_snd_controls[] = { - SOC_DOUBLE("ADC OSR Volume ON Switch", ES8389_ADC_MUTE, 6, 7, 1, 0), - SOC_SINGLE_TLV("ADC OSR Volume", ES8389_OSR_VOL, 0, 0xFF, 0, adc_vol_tlv), - SOC_DOUBLE("ADC OUTPUT Invert Switch", ES8389_ADC_HPF2, 5, 6, 1, 0), -+ SOC_SINGLE_EXT("ADC HPF Freq Select", SND_SOC_NOPM, 0, 1020, 0, -+ es8389_hpf_get, es8389_hpf_set), - - SOC_SINGLE_TLV("DACL Playback Volume", ES8389_DACL_VOL, 0, 0xFF, 0, dac_vol_tlv), - SOC_SINGLE_TLV("DACR Playback Volume", ES8389_DACR_VOL, 0, 0xFF, 0, dac_vol_tlv), -@@ -298,6 +433,8 @@ static const struct snd_soc_dapm_widget es8389_dapm_widgets[] = { - &es8389_adc_mixer_controls[0], - ARRAY_SIZE(es8389_adc_mixer_controls)), - SND_SOC_DAPM_MUX("ADC MUX", SND_SOC_NOPM, 0, 0, &es8389_dmic_mux_controls), -+ SND_SOC_DAPM_MUX("INPUTL MUX", SND_SOC_NOPM, 0, 0, &es8389_adcl_mux_controls), -+ SND_SOC_DAPM_MUX("INPUTR MUX", SND_SOC_NOPM, 0, 0, &es8389_adcr_mux_controls), - - SND_SOC_DAPM_MUX("OUTL MUX", SND_SOC_NOPM, 0, 0, &es8389_outl_mux_controls), - SND_SOC_DAPM_MUX("OUTR MUX", SND_SOC_NOPM, 0, 0, &es8389_outr_mux_controls), -@@ -311,10 +448,15 @@ static const struct snd_soc_dapm_route es8389_dapm_routes[] = { - {"ADCL", NULL, "PGAL"}, - {"ADCR", NULL, "PGAR"}, - -+ {"INPUTL MUX", "Normal", "ADCL"}, -+ {"INPUTL MUX", "ADC2 channel to ADC1 channel", "ADCR"}, -+ {"INPUTR MUX", "Normal", "ADCR"}, -+ {"INPUTR MUX", "ADC1 channel to ADC2 channel", "ADCL"}, -+ - {"ADC Mixer", "DACL ADCL Mixer", "DACL"}, - {"ADC Mixer", "DACR ADCR Mixer", "DACR"}, -- {"ADC Mixer", NULL, "ADCL"}, -- {"ADC Mixer", NULL, "ADCR"}, -+ {"ADC Mixer", NULL, "INPUTL MUX"}, -+ {"ADC Mixer", NULL, "INPUTR MUX"}, - - {"ADC MUX", "AMIC", "ADC Mixer"}, - {"ADC MUX", "DMIC", "DMIC"}, -@@ -415,52 +557,54 @@ static const struct _coeff_div coeff_div[] = { - {36, 576000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {48, 768000, 16000, 0x02, 0x57, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {50, 800000, 16000, 0x00, 0x7E, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -- {64, 1024000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -- {72, 1152000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -+ {64, 1024000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -+ {72, 1152000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {96, 1536000, 16000, 0x02, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {128, 2048000, 16000, 0x00, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {144, 2304000, 16000, 0x00, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -+ {150, 2400000, 16000, 0x02, 0x7E, 0x01, 0xC9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {192, 3072000, 16000, 0x02, 0x65, 0x25, 0xE0, 0x00, 0xE1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -- {256, 4096000, 16000, 0x00, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -+ {256, 4096000, 16000, 0x00, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {300, 4800000, 16000, 0x02, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {384, 6144000, 16000, 0x02, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -- {512, 8192000, 16000, 0x01, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -+ {512, 8192000, 16000, 0x01, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {750, 12000000, 16000, 0x0E, 0x7E, 0x01, 0xC9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -- {768, 12288000, 16000, 0x02, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -- {1024, 16384000, 16000, 0x03, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -+ {768, 12288000, 16000, 0x02, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -+ {1024, 16384000, 16000, 0x03, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {1152, 18432000, 16000, 0x08, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {1200, 19200000, 16000, 0x0B, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {1500, 24000000, 16000, 0x0E, 0x26, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -- {1536, 24576000, 16000, 0x05, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, -+ {1536, 24576000, 16000, 0x05, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {1625, 26000000, 16000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, - {800, 19200000, 24000, 0x07, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x1A, 0x49, 0x14, 2, 2}, - {375, 12000000, 32000, 0x0E, 0x2E, 0x05, 0xC8, 0x00, 0xC2, 0x80, 0x40, 0x01, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B, 2, 0}, -- {600, 19200000, 32000, 0x05, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B, 2, 2}, -- {32, 1411200, 44100, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {600, 19200000, 32000, 0x05, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B, 2, 2}, -+ {32, 1411200, 44100, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {64, 2822400, 44100, 0x00, 0x51, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -- {128, 5644800, 44100, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -- {256, 11289600, 44100, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -- {512, 22579200, 44100, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -- {32, 1536000, 48000, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {128, 5644800, 44100, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {256, 11289600, 44100, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {512, 22579200, 44100, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {32, 1536000, 48000, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {48, 2304000, 48000, 0x02, 0x55, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {50, 2400000, 48000, 0x00, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {64, 3072000, 48000, 0x00, 0x51, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -- {100, 4800000, 48000, 0x00, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {100, 4800000, 48000, 0x00, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {125, 6000000, 48000, 0x04, 0x6E, 0x05, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x01, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -- {128, 6144000, 48000, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -- {200, 9600000, 48000, 0x01, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {128, 6144000, 48000, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {200, 9600000, 48000, 0x01, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {250, 12000000, 48000, 0x04, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -- {256, 12288000, 48000, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -- {384, 18432000, 48000, 0x02, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -- {400, 19200000, 48000, 0x03, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -- {500, 24000000, 48000, 0x04, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -- {512, 24576000, 48000, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {256, 12288000, 48000, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {384, 18432000, 48000, 0x02, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {400, 19200000, 48000, 0x03, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {500, 24000000, 48000, 0x04, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, -+ {512, 24576000, 48000, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {800, 38400000, 48000, 0x18, 0x45, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, - {128, 11289600, 88200, 0x00, 0x50, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x32, 0x89, 0x25, 2, 2}, -- {64, 6144000, 96000, 0x00, 0x41, 0x00, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, -+ {64, 6144000, 96000, 0x00, 0x41, 0x00, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, - {96, 9216000, 96000, 0x02, 0x43, 0x00, 0xC0, 0x10, 0xC0, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, - {256, 24576000, 96000, 0x00, 0x40, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, - {128, 24576000, 192000, 0x00, 0x50, 0x00, 0xC0, 0x18, 0xC1, 0x81, 0xC0, 0x00, 0x8F, 0x7F, 0xBF, 0xC0, 0x3F, 0x7F, 0x80, 0x12, 0xC0, 0x3F, 0xF9, 0x3F, 2, 2}, -+ {64, 12288000, 192000, 0x00, 0x41, 0x00, 0xC0, 0x18, 0xC1, 0x80, 0x00, 0x00, 0x8F, 0x7F, 0xEF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x3F, 0xF9, 0x3F, 1, 0}, - }; - - static inline int get_coeff(u8 vddd, u8 dmic, int mclk, int rate) -@@ -564,6 +708,8 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream, - int coeff, ret; - u8 dmic_enable, state = 0; - unsigned int regv; -+ u32 freq; -+ bool hpf; - - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: -@@ -590,7 +736,7 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream, - - if (es8389->mclk_src == ES8389_SCLK_PIN) { - regmap_update_bits(es8389->regmap, ES8389_MASTER_CLK, -- ES8389_MCLK_SOURCE, es8389->mclk_src); -+ ES8389_MCLK_MASK, ES8389_MCLK_FROM_SCLK); - es8389->sysclk = params_channels(params) * params_width(params) * params_rate(params); - } - -@@ -641,6 +787,28 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream, - dev_warn(component->dev, "Clock coefficients do not match"); - } - -+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { -+ es8389->capture_rate = params_rate(params); -+ freq = (es8389->hpf_freq * 48000) / params_rate(params); -+ hpf = find_best_hpf_freq(freq, &es8389->hpfl, &es8389->hpfr, &es8389->hpf_freq); -+ if (!hpf) { -+ dev_err(component->dev, "The HPF frequency is invalid\n"); -+ return -EINVAL; -+ } -+ } -+ -+ return 0; -+} -+ -+static int es8389_pcm_hw_free(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ struct snd_soc_component *component = dai->component; -+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); -+ -+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) -+ es8389->capture_rate = 0; -+ - return 0; - } - -@@ -721,8 +889,8 @@ static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction) - regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE, - 0x03, 0x00); - } else { -- regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x0a); -- regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x0a); -+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, es8389->hpfl); -+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, es8389->hpfr); - regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE, - 0x03, 0x00); - } -@@ -738,6 +906,7 @@ static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction) - - static const struct snd_soc_dai_ops es8389_ops = { - .hw_params = es8389_pcm_hw_params, -+ .hw_free = es8389_pcm_hw_free, - .set_fmt = es8389_set_dai_fmt, - .set_sysclk = es8389_set_dai_sysclk, - .set_tdm_slot = es8389_set_tdm_slot, -@@ -771,7 +940,7 @@ static void es8389_init(struct snd_soc_component *component) - - regmap_read(es8389->regmap, ES8389_MAX_REGISTER, ®); - es8389->version = reg; -- regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x00); -+ regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x56); - regmap_write(es8389->regmap, ES8389_RESET, 0x7E); - regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x38); - regmap_write(es8389->regmap, ES8389_ADC_HPF1, 0x64); -@@ -823,7 +992,7 @@ static void es8389_init(struct snd_soc_component *component) - regmap_write(es8389->regmap, ES8389_SCLK_DIV, 0x04); - regmap_write(es8389->regmap, ES8389_LRCK_DIV1, 0x01); - regmap_write(es8389->regmap, ES8389_LRCK_DIV2, 0x00); -- regmap_write(es8389->regmap, ES8389_OSC_CLK, 0x00); -+ regmap_write(es8389->regmap, ES8389_OSC_CLK, 0x10); - regmap_write(es8389->regmap, ES8389_ADC_OSR, 0x1F); - regmap_write(es8389->regmap, ES8389_ADC_DSP, 0x7F); - regmap_write(es8389->regmap, ES8389_ADC_MUTE, 0xC0); -@@ -861,13 +1030,13 @@ static int es8389_resume(struct snd_soc_component *component) - regcache_cache_only(es8389->regmap, false); - regcache_cache_bypass(es8389->regmap, true); - regmap_read(es8389->regmap, ES8389_RESET, ®v); -- regcache_cache_bypass(es8389->regmap, false); - - if (regv == 0xff) - es8389_init(component); - else - es8389_set_bias_level(component, SND_SOC_BIAS_ON); - -+ regcache_cache_bypass(es8389->regmap, false); - regcache_sync(es8389->regmap); - - return 0; -@@ -913,6 +1082,7 @@ static int es8389_probe(struct snd_soc_component *component) - return ret; - } - -+ es8389->hpf_freq = ES8389_HPF_DEFAULT; - es8389_init(component); - es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY); - -diff --git a/sound/soc/codecs/es8389.h b/sound/soc/codecs/es8389.h ---- a/sound/soc/codecs/es8389.h -+++ b/sound/soc/codecs/es8389.h -@@ -106,6 +106,9 @@ - #define ES8389_MIC_SEL_MASK (7 << 4) - #define ES8389_MIC_DEFAULT (1 << 4) - -+#define ES8389_HPF_DEFAULT 16 -+#define ES8389_HPF_OFFSET 4 -+ - #define ES8389_MASTER_MODE_EN (1 << 0) - - #define ES8389_TDM_OFF (0 << 0) -@@ -116,9 +119,11 @@ - #define ES8389_TDM_SLOT (0x70 << 0) - #define ES8389_TDM_SHIFT 4 - --#define ES8389_MCLK_SOURCE (1 << 6) --#define ES8389_MCLK_PIN (1 << 6) --#define ES8389_SCLK_PIN (0 << 6) -+#define ES8389_MCLK_MASK (3 << 6) -+#define ES8389_MCLK_FROM_SCLK (1 << 6) -+#define ES8389_MCLK_SOURCE ES8389_MCLK_PIN -+#define ES8389_MCLK_PIN 0 -+#define ES8389_SCLK_PIN 1 - - /* ES8389_FMT */ - #define ES8389_S24_LE (0 << 5) -diff --git a/sound/soc/codecs/es9356.c b/sound/soc/codecs/es9356.c ---- a/sound/soc/codecs/es9356.c -+++ b/sound/soc/codecs/es9356.c -@@ -6,6 +6,7 @@ - // - // - -+#include - #include - #include - #include -@@ -19,8 +20,6 @@ - #include - #include - #include --#include --#include - #include - #include - #include -@@ -500,16 +499,19 @@ static int es9356_power_state(struct snd_soc_dai *dai, unsigned char ps, unsigne - } - - /* power state changes are not independent across functions */ -- mutex_lock(&es9356->pde_lock); -- ret = es9356_pde_transition_delay(es9356, func, pde_entity, ps?ps0:ps3); -- if (ret) { -- regmap_write(es9356->regmap, -- SDW_SDCA_CTL(func, pde_entity, ES9356_SDCA_CTL_REQ_POWER_STATE, 0), ps?ps3:ps0); -- es9356_pde_transition_delay(es9356, func, pde_entity, ps?ps3:ps0); -- } else -- dev_dbg(component->dev, "%s PDE is already %d\n", __func__, ps?ps0:ps3); -- -- mutex_unlock(&es9356->pde_lock); -+ scoped_guard(mutex, &es9356->pde_lock) { -+ ret = es9356_pde_transition_delay(es9356, func, pde_entity, ps ? ps0 : ps3); -+ if (ret) { -+ regmap_write(es9356->regmap, -+ SDW_SDCA_CTL(func, pde_entity, -+ ES9356_SDCA_CTL_REQ_POWER_STATE, 0), -+ ps ? ps3 : ps0); -+ es9356_pde_transition_delay(es9356, func, pde_entity, ps ? ps3 : ps0); -+ } else { -+ dev_dbg(component->dev, "%s PDE is already %d\n", __func__, -+ ps ? ps0 : ps3); -+ } -+ } - - if (rate) - regmap_write(es9356->regmap, -@@ -671,7 +673,7 @@ static int es9356_sdca_mbq_size(struct device *dev, unsigned int reg) - } - } - --static struct regmap_sdw_mbq_cfg es9356_mbq_config = { -+static const struct regmap_sdw_mbq_cfg es9356_mbq_config = { - .mbq_size = es9356_sdca_mbq_size, - }; - -@@ -1091,9 +1093,8 @@ static int es9356_sdca_dev_system_suspend(struct device *dev) - { - struct es9356_sdw_priv *es9356 = dev_get_drvdata(dev); - -- mutex_lock(&es9356->disable_irq_lock); -- es9356->disable_irq = true; -- mutex_unlock(&es9356->disable_irq_lock); -+ scoped_guard(mutex, &es9356->disable_irq_lock) -+ es9356->disable_irq = true; - - return es9356_sdca_dev_suspend(dev); - } -diff --git a/sound/soc/codecs/fs-amp-lib.c b/sound/soc/codecs/fs-amp-lib.c ---- a/sound/soc/codecs/fs-amp-lib.c -+++ b/sound/soc/codecs/fs-amp-lib.c -@@ -221,7 +221,7 @@ static void fs_print_firmware_info(struct fs_amp_lib *amp_lib) - - int fs_amp_load_firmware(struct fs_amp_lib *amp_lib, const char *name) - { -- const struct firmware *cont; -+ const struct firmware *cont __free(firmware) = NULL; - struct fs_fwm_header *hdr; - int ret; - -@@ -237,7 +237,6 @@ int fs_amp_load_firmware(struct fs_amp_lib *amp_lib, const char *name) - dev_info(amp_lib->dev, "Loading %s - size: %zu\n", name, cont->size); - - hdr = devm_kmemdup(amp_lib->dev, cont->data, cont->size, GFP_KERNEL); -- release_firmware(cont); - if (!hdr) - return -ENOMEM; - -diff --git a/sound/soc/codecs/fs210x.c b/sound/soc/codecs/fs210x.c ---- a/sound/soc/codecs/fs210x.c -+++ b/sound/soc/codecs/fs210x.c -@@ -4,6 +4,7 @@ - // - // Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd. - -+#include - #include - #include - #include -@@ -771,9 +772,8 @@ static int fs210x_dai_hw_params(struct snd_pcm_substream *substream, - if (fs210x->devid == FS2105S_DEVICE_ID && fs210x->srate == 16000) - return -EOPNOTSUPP; - -- mutex_lock(&fs210x->lock); -- ret = fs210x_set_hw_params(fs210x); -- mutex_unlock(&fs210x->lock); -+ scoped_guard(mutex, &fs210x->lock) -+ ret = fs210x_set_hw_params(fs210x); - if (ret) - dev_err(fs210x->dev, "Failed to set hw params: %d\n", ret); - -@@ -790,15 +790,11 @@ static int fs210x_dai_mute(struct snd_soc_dai *dai, int mute, int stream) - - fs210x = snd_soc_component_get_drvdata(dai->component); - -- mutex_lock(&fs210x->lock); -- -- if (!fs210x->is_inited || fs210x->is_suspended) { -- mutex_unlock(&fs210x->lock); -- return 0; -+ scoped_guard(mutex, &fs210x->lock) { -+ if (!fs210x->is_inited || fs210x->is_suspended) -+ return 0; - } - -- mutex_unlock(&fs210x->lock); -- - if (mute) { - cancel_delayed_work_sync(&fs210x->fault_check_work); - cancel_delayed_work_sync(&fs210x->start_work); -@@ -817,15 +813,11 @@ static int fs210x_dai_trigger(struct snd_pcm_substream *substream, - - fs210x = snd_soc_component_get_drvdata(dai->component); - -- mutex_lock(&fs210x->lock); -- -- if (!fs210x->is_inited || fs210x->is_suspended || fs210x->is_playing) { -- mutex_unlock(&fs210x->lock); -- return 0; -+ scoped_guard(mutex, &fs210x->lock) { -+ if (!fs210x->is_inited || fs210x->is_suspended || fs210x->is_playing) -+ return 0; - } - -- mutex_unlock(&fs210x->lock); -- - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: -@@ -853,13 +845,11 @@ static void fs210x_start_work(struct work_struct *work) - - fs210x = container_of(work, struct fs210x_priv, start_work.work); - -- mutex_lock(&fs210x->lock); -+ guard(mutex)(&fs210x->lock); - - ret = fs210x_dev_play(fs210x); - if (ret) - dev_err(fs210x->dev, "Failed to start playing: %d\n", ret); -- -- mutex_unlock(&fs210x->lock); - } - - static void fs210x_fault_check_work(struct work_struct *work) -@@ -870,15 +860,13 @@ static void fs210x_fault_check_work(struct work_struct *work) - - fs210x = container_of(work, struct fs210x_priv, fault_check_work.work); - -- mutex_lock(&fs210x->lock); -+ scoped_guard(mutex, &fs210x->lock) { -+ if (!fs210x->is_inited || fs210x->is_suspended || !fs210x->is_playing) -+ return; - -- if (!fs210x->is_inited || fs210x->is_suspended || !fs210x->is_playing) { -- mutex_unlock(&fs210x->lock); -- return; -+ ret = fs210x_reg_read(fs210x, FS210X_05H_ANASTAT, &status); - } - -- ret = fs210x_reg_read(fs210x, FS210X_05H_ANASTAT, &status); -- mutex_unlock(&fs210x->lock); - if (ret) - return; - -@@ -991,7 +979,7 @@ static int fs210x_effect_scene_get(struct snd_kcontrol *kcontrol, - if (fs210x->scene_id < 1) - return -EINVAL; - -- mutex_lock(&fs210x->lock); -+ guard(mutex)(&fs210x->lock); - /* - * FS210x has scene(s) as below: - * init scene: id = 0 -@@ -1000,7 +988,6 @@ static int fs210x_effect_scene_get(struct snd_kcontrol *kcontrol, - */ - index = fs210x->scene_id - 1; - ucontrol->value.integer.value[0] = index; -- mutex_unlock(&fs210x->lock); - - return 0; - } -@@ -1019,7 +1006,7 @@ static int fs210x_effect_scene_put(struct snd_kcontrol *kcontrol, - return -EINVAL; - } - -- mutex_lock(&fs210x->lock); -+ guard(mutex)(&fs210x->lock); - - /* - * FS210x has scene(s) as below: -@@ -1029,17 +1016,14 @@ static int fs210x_effect_scene_put(struct snd_kcontrol *kcontrol, - */ - scene_id = ucontrol->value.integer.value[0] + 1; - scene_count = fs210x->amp_lib.scene_count - 1; /* Skip init scene */ -- if (scene_id < 1 || scene_id > scene_count) { -- mutex_unlock(&fs210x->lock); -+ if (scene_id < 1 || scene_id > scene_count) - return -ERANGE; -- } - - if (scene_id != fs210x->scene_id) - is_changed = true; - - if (fs210x->is_suspended) { - fs210x->scene_id = scene_id; -- mutex_unlock(&fs210x->lock); - return is_changed; - } - -@@ -1047,8 +1031,6 @@ static int fs210x_effect_scene_put(struct snd_kcontrol *kcontrol, - if (ret) - dev_err(fs210x->dev, "Failed to set scene: %d\n", ret); - -- mutex_unlock(&fs210x->lock); -- - if (!ret && is_changed) - return 1; - -@@ -1062,12 +1044,10 @@ static int fs210x_playback_event(struct snd_soc_dapm_widget *w, - struct fs210x_priv *fs210x = snd_soc_component_get_drvdata(cmpnt); - int ret = 0; - -- mutex_lock(&fs210x->lock); -+ guard(mutex)(&fs210x->lock); - -- if (fs210x->is_suspended) { -- mutex_unlock(&fs210x->lock); -+ if (fs210x->is_suspended) - return 0; -- } - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: -@@ -1088,8 +1068,6 @@ static int fs210x_playback_event(struct snd_soc_dapm_widget *w, - break; - } - -- mutex_unlock(&fs210x->lock); -- - return ret; - } - -@@ -1220,11 +1198,9 @@ static int fs210x_probe(struct snd_soc_component *cmpnt) - if (ret) - return ret; - -- mutex_lock(&fs210x->lock); -- ret = fs210x_init_chip(fs210x); -- mutex_unlock(&fs210x->lock); -+ guard(mutex)(&fs210x->lock); - -- return ret; -+ return fs210x_init_chip(fs210x); - } - - static void fs210x_remove(struct snd_soc_component *cmpnt) -@@ -1251,15 +1227,15 @@ static int fs210x_suspend(struct snd_soc_component *cmpnt) - - regcache_cache_only(fs210x->regmap, true); - -- mutex_lock(&fs210x->lock); -- fs210x->cur_scene = NULL; -- fs210x->is_inited = false; -- fs210x->is_playing = false; -- fs210x->is_suspended = true; -+ scoped_guard(mutex, &fs210x->lock) { -+ fs210x->cur_scene = NULL; -+ fs210x->is_inited = false; -+ fs210x->is_playing = false; -+ fs210x->is_suspended = true; - -- gpiod_set_value_cansleep(fs210x->gpio_sdz, 1); /* Active */ -- fsleep(30000); /* >= 30ms */ -- mutex_unlock(&fs210x->lock); -+ gpiod_set_value_cansleep(fs210x->gpio_sdz, 1); /* Active */ -+ fsleep(30000); /* >= 30ms */ -+ } - - cancel_delayed_work_sync(&fs210x->start_work); - cancel_delayed_work_sync(&fs210x->fault_check_work); -@@ -1288,14 +1264,11 @@ static int fs210x_resume(struct snd_soc_component *cmpnt) - return ret; - } - -- mutex_lock(&fs210x->lock); -+ guard(mutex)(&fs210x->lock); - - fs210x->is_suspended = false; -- ret = fs210x_init_chip(fs210x); - -- mutex_unlock(&fs210x->lock); -- -- return ret; -+ return fs210x_init_chip(fs210x); - } - #else - #define fs210x_suspend NULL -diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c ---- a/sound/soc/codecs/hdac_hda.c -+++ b/sound/soc/codecs/hdac_hda.c -@@ -437,7 +437,7 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) - - #ifdef CONFIG_SND_HDA_PATCH_LOADER - if (loadable_patch[hda_pvt->dev_index] && *loadable_patch[hda_pvt->dev_index]) { -- const struct firmware *fw; -+ const struct firmware *fw __free(firmware) = NULL; - - dev_info(&hdev->dev, "Applying patch firmware '%s'\n", - loadable_patch[hda_pvt->dev_index]); -@@ -451,7 +451,6 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) - dev_err(&hdev->dev, "%s: failed to load hda patch %d\n", __func__, ret); - goto error_no_pm; - } -- release_firmware(fw); - } - } - #endif -diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c ---- a/sound/soc/codecs/hdac_hdmi.c -+++ b/sound/soc/codecs/hdac_hdmi.c -@@ -10,6 +10,7 @@ - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - -+#include - #include - #include - #include -@@ -537,10 +538,11 @@ static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt( - continue; - - list_for_each_entry(port, &pcm->port_list, head) { -- mutex_lock(&pcm->lock); -- ret = hdac_hdmi_query_port_connlist(hdev, -- port->pin, port); -- mutex_unlock(&pcm->lock); -+ scoped_guard(mutex, &pcm->lock) { -+ ret = hdac_hdmi_query_port_connlist(hdev, -+ port->pin, -+ port); -+ } - if (ret < 0) - continue; - -@@ -640,11 +642,11 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, - pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt); - - if (pcm) { -- mutex_lock(&pcm->lock); -- pcm->chmap_set = false; -- memset(pcm->chmap, 0, sizeof(pcm->chmap)); -- pcm->channels = 0; -- mutex_unlock(&pcm->lock); -+ scoped_guard(mutex, &pcm->lock) { -+ pcm->chmap_set = false; -+ memset(pcm->chmap, 0, sizeof(pcm->chmap)); -+ pcm->channels = 0; -+ } - } - - if (dai_map->port) -@@ -922,7 +924,7 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, - if (port == NULL) - return -EINVAL; - -- mutex_lock(&hdmi->pin_mutex); -+ guard(mutex)(&hdmi->pin_mutex); - list_for_each_entry(pcm, &hdmi->pcm_list, head) { - if (list_empty(&pcm->port_list)) - continue; -@@ -945,12 +947,10 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, - list_add_tail(&port->head, &pcm->port_list); - if (port->eld.monitor_present && port->eld.eld_valid) { - hdac_hdmi_jack_report_sync(pcm, port, true); -- mutex_unlock(&hdmi->pin_mutex); - return ret; - } - } - } -- mutex_unlock(&hdmi->pin_mutex); - - return ret; - } -@@ -1274,67 +1274,65 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, - * In case of non MST pin, get_eld info API expectes port - * to be -1. - */ -- mutex_lock(&hdmi->pin_mutex); -- port->eld.monitor_present = false; -+ scoped_guard(mutex, &hdmi->pin_mutex) { -+ port->eld.monitor_present = false; - -- if (pin->mst_capable) -- port_id = port->id; -+ if (pin->mst_capable) -+ port_id = port->id; - -- size = snd_hdac_acomp_get_eld(hdev, pin->nid, port_id, -- &port->eld.monitor_present, -- port->eld.eld_buffer, -- ELD_MAX_SIZE); -+ size = snd_hdac_acomp_get_eld(hdev, pin->nid, port_id, -+ &port->eld.monitor_present, -+ port->eld.eld_buffer, -+ ELD_MAX_SIZE); - -- if (size > 0) { -- size = min(size, ELD_MAX_SIZE); -- if (hdac_hdmi_parse_eld(hdev, port) < 0) -- size = -EINVAL; -- } -- -- eld_valid = port->eld.eld_valid; -- -- if (size > 0) { -- port->eld.eld_valid = true; -- port->eld.eld_size = size; -- } else { -- port->eld.eld_valid = false; -- port->eld.eld_size = 0; -- } -- -- eld_changed = (eld_valid != port->eld.eld_valid); -- -- pcm = hdac_hdmi_get_pcm(hdev, port); -- -- if (!port->eld.monitor_present || !port->eld.eld_valid) { -- -- dev_dbg(&hdev->dev, "%s: disconnect for pin:port %d:%d\n", -- __func__, pin->nid, port->id); -- -- /* -- * PCMs are not registered during device probe, so don't -- * report jack here. It will be done in usermode mux -- * control select. -- */ -- if (pcm) { -- hdac_hdmi_jack_report(pcm, port, false); -- schedule_work(&port->dapm_work); -+ if (size > 0) { -+ size = min(size, ELD_MAX_SIZE); -+ if (hdac_hdmi_parse_eld(hdev, port) < 0) -+ size = -EINVAL; - } - -- mutex_unlock(&hdmi->pin_mutex); -- return; -- } -+ eld_valid = port->eld.eld_valid; - -- if (port->eld.monitor_present && port->eld.eld_valid) { -- if (pcm) { -- hdac_hdmi_jack_report(pcm, port, true); -- schedule_work(&port->dapm_work); -+ if (size > 0) { -+ port->eld.eld_valid = true; -+ port->eld.eld_size = size; -+ } else { -+ port->eld.eld_valid = false; -+ port->eld.eld_size = 0; - } - -- print_hex_dump_debug("ELD: ", DUMP_PREFIX_OFFSET, 16, 1, -- port->eld.eld_buffer, port->eld.eld_size, false); -+ eld_changed = (eld_valid != port->eld.eld_valid); - -+ pcm = hdac_hdmi_get_pcm(hdev, port); -+ -+ if (!port->eld.monitor_present || !port->eld.eld_valid) { -+ -+ dev_dbg(&hdev->dev, "%s: disconnect for pin:port %d:%d\n", -+ __func__, pin->nid, port->id); -+ -+ /* -+ * PCMs are not registered during device probe, so don't -+ * report jack here. It will be done in usermode mux -+ * control select. -+ */ -+ if (pcm) { -+ hdac_hdmi_jack_report(pcm, port, false); -+ schedule_work(&port->dapm_work); -+ } -+ -+ return; -+ } -+ -+ if (port->eld.monitor_present && port->eld.eld_valid) { -+ if (pcm) { -+ hdac_hdmi_jack_report(pcm, port, true); -+ schedule_work(&port->dapm_work); -+ } -+ -+ print_hex_dump_debug("ELD: ", DUMP_PREFIX_OFFSET, 16, 1, -+ port->eld.eld_buffer, port->eld.eld_size, false); -+ } - } -- mutex_unlock(&hdmi->pin_mutex); - - if (eld_changed && pcm) - snd_ctl_notify(hdmi->card, -@@ -1795,13 +1793,12 @@ static void hdac_hdmi_set_chmap(struct hdac_device *hdev, int pcm_idx, - if (list_empty(&pcm->port_list)) - return; - -- mutex_lock(&pcm->lock); -+ guard(mutex)(&pcm->lock); - pcm->chmap_set = true; - memcpy(pcm->chmap, chmap, ARRAY_SIZE(pcm->chmap)); - list_for_each_entry(port, &pcm->port_list, head) - if (prepared) - hdac_hdmi_setup_audio_infoframe(hdev, pcm, port); -- mutex_unlock(&pcm->lock); - } - - static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdev, int pcm_idx) -@@ -1866,8 +1863,10 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) - snd_hdac_ext_bus_link_get(hdev->bus, hlink); - - hdmi_priv = devm_kzalloc(&hdev->dev, sizeof(*hdmi_priv), GFP_KERNEL); -- if (hdmi_priv == NULL) -+ if (hdmi_priv == NULL) { -+ snd_hdac_ext_bus_link_put(hdev->bus, hlink); - return -ENOMEM; -+ } - - snd_hdac_register_chmap_ops(hdev, &hdmi_priv->chmap); - hdmi_priv->chmap.ops.get_chmap = hdac_hdmi_get_chmap; -@@ -1876,8 +1875,10 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) - hdmi_priv->chmap.ops.get_spk_alloc = hdac_hdmi_get_spk_alloc; - hdmi_priv->hdev = hdev; - -- if (!hdac_id) -+ if (!hdac_id) { -+ snd_hdac_ext_bus_link_put(hdev->bus, hlink); - return -ENODEV; -+ } - - if (hdac_id->driver_data) - hdmi_priv->drv_data = -@@ -1902,6 +1903,8 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) - if (ret < 0) { - dev_err(&hdev->dev, - "Failed in parse and map nid with err: %d\n", ret); -+ snd_hdac_ext_bus_link_put(hdev->bus, hlink); -+ snd_hdac_display_power(hdev->bus, hdev->addr, false); - return ret; - } - snd_hdac_refresh_widgets(hdev); -diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c ---- a/sound/soc/codecs/hdmi-codec.c -+++ b/sound/soc/codecs/hdmi-codec.c -@@ -4,6 +4,7 @@ - * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ - * Author: Jyri Sarha - */ -+#include - #include - #include - #include -@@ -452,31 +453,30 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream, - if (!((has_playback && tx) || (has_capture && !tx))) - return 0; - -- mutex_lock(&hcp->lock); -+ guard(mutex)(&hcp->lock); - if (hcp->busy) { - dev_err(dai->dev, "Only one simultaneous stream supported!\n"); -- mutex_unlock(&hcp->lock); - return -EINVAL; - } - - if (hcp->hcd.ops->audio_startup) { - ret = hcp->hcd.ops->audio_startup(dai->dev->parent, hcp->hcd.data); - if (ret) -- goto err; -+ return ret; - } - - if (tx && hcp->hcd.ops->get_eld) { - ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->hcd.data, - hcp->eld, sizeof(hcp->eld)); - if (ret) -- goto err; -+ return ret; - - snd_parse_eld(dai->dev, &hcp->eld_parsed, - hcp->eld, sizeof(hcp->eld)); - - ret = snd_pcm_hw_constraint_eld(substream->runtime, hcp->eld); - if (ret) -- goto err; -+ return ret; - - /* Select chmap supported */ - hdmi_codec_eld_chmap(hcp); -@@ -484,8 +484,6 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream, - - hcp->busy = true; - --err: -- mutex_unlock(&hcp->lock); - return ret; - } - -@@ -503,9 +501,8 @@ static void hdmi_codec_shutdown(struct snd_pcm_substream *substream, - hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; - hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data); - -- mutex_lock(&hcp->lock); -+ guard(mutex)(&hcp->lock); - hcp->busy = false; -- mutex_unlock(&hcp->lock); - } - - static int hdmi_codec_fill_codec_params(struct snd_soc_dai *dai, -diff --git a/sound/soc/codecs/idt821034.c b/sound/soc/codecs/idt821034.c ---- a/sound/soc/codecs/idt821034.c -+++ b/sound/soc/codecs/idt821034.c -@@ -7,6 +7,7 @@ - // Author: Herve Codina - - #include -+#include - #include - #include - #include -@@ -413,12 +414,12 @@ static int idt821034_kctrl_gain_get(struct snd_kcontrol *kcontrol, - - ch = IDT821034_ID_GET_CHAN(mc->reg); - -- mutex_lock(&idt821034->mutex); -- if (IDT821034_ID_IS_OUT(mc->reg)) -- val = idt821034->amps.ch[ch].amp_out.gain; -- else -- val = idt821034->amps.ch[ch].amp_in.gain; -- mutex_unlock(&idt821034->mutex); -+ scoped_guard(mutex, &idt821034->mutex) { -+ if (IDT821034_ID_IS_OUT(mc->reg)) -+ val = idt821034->amps.ch[ch].amp_out.gain; -+ else -+ val = idt821034->amps.ch[ch].amp_in.gain; -+ } - - ucontrol->value.integer.value[0] = val & mask; - if (invert) -@@ -456,7 +457,7 @@ static int idt821034_kctrl_gain_put(struct snd_kcontrol *kcontrol, - - ch = IDT821034_ID_GET_CHAN(mc->reg); - -- mutex_lock(&idt821034->mutex); -+ guard(mutex)(&idt821034->mutex); - - if (IDT821034_ID_IS_OUT(mc->reg)) { - amp = &idt821034->amps.ch[ch].amp_out; -@@ -466,22 +467,18 @@ static int idt821034_kctrl_gain_put(struct snd_kcontrol *kcontrol, - gain_type = IDT821034_GAIN_TX; - } - -- if (amp->gain == val) { -- ret = 0; -- goto end; -- } -+ if (amp->gain == val) -+ return 0; - - if (!amp->is_muted) { - ret = idt821034_set_gain_channel(idt821034, ch, gain_type, val); - if (ret) -- goto end; -+ return ret; - } - - amp->gain = val; -- ret = 1; /* The value changed */ --end: -- mutex_unlock(&idt821034->mutex); -- return ret; -+ -+ return 1; - } - - static int idt821034_kctrl_mute_get(struct snd_kcontrol *kcontrol, -@@ -495,11 +492,11 @@ static int idt821034_kctrl_mute_get(struct snd_kcontrol *kcontrol, - - ch = IDT821034_ID_GET_CHAN(id); - -- mutex_lock(&idt821034->mutex); -- is_muted = IDT821034_ID_IS_OUT(id) ? -- idt821034->amps.ch[ch].amp_out.is_muted : -- idt821034->amps.ch[ch].amp_in.is_muted; -- mutex_unlock(&idt821034->mutex); -+ scoped_guard(mutex, &idt821034->mutex) { -+ is_muted = IDT821034_ID_IS_OUT(id) ? -+ idt821034->amps.ch[ch].amp_out.is_muted : -+ idt821034->amps.ch[ch].amp_in.is_muted; -+ } - - ucontrol->value.integer.value[0] = !is_muted; - -@@ -521,7 +518,7 @@ static int idt821034_kctrl_mute_put(struct snd_kcontrol *kcontrol, - ch = IDT821034_ID_GET_CHAN(id); - is_mute = !ucontrol->value.integer.value[0]; - -- mutex_lock(&idt821034->mutex); -+ guard(mutex)(&idt821034->mutex); - - if (IDT821034_ID_IS_OUT(id)) { - amp = &idt821034->amps.ch[ch].amp_out; -@@ -531,21 +528,17 @@ static int idt821034_kctrl_mute_put(struct snd_kcontrol *kcontrol, - gain_type = IDT821034_GAIN_TX; - } - -- if (amp->is_muted == is_mute) { -- ret = 0; -- goto end; -- } -+ if (amp->is_muted == is_mute) -+ return 0; - - ret = idt821034_set_gain_channel(idt821034, ch, gain_type, - is_mute ? 0 : amp->gain); - if (ret) -- goto end; -+ return ret; - - amp->is_muted = is_mute; -- ret = 1; /* The value changed */ --end: -- mutex_unlock(&idt821034->mutex); -- return ret; -+ -+ return 1; - } - - static const DECLARE_TLV_DB_LINEAR(idt821034_gain_in, -300, 1300); -@@ -623,24 +616,20 @@ static int idt821034_power_event(struct snd_soc_dapm_widget *w, - struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component); - unsigned int id = w->shift; - u8 power, mask; -- int ret; - u8 ch; - - ch = IDT821034_ID_GET_CHAN(id); - mask = IDT821034_ID_IS_OUT(id) ? IDT821034_CONF_PWRUP_RX : IDT821034_CONF_PWRUP_TX; - -- mutex_lock(&idt821034->mutex); -+ guard(mutex)(&idt821034->mutex); - - power = idt821034_get_channel_power(idt821034, ch); - if (SND_SOC_DAPM_EVENT_ON(event)) - power |= mask; - else - power &= ~mask; -- ret = idt821034_set_channel_power(idt821034, ch, power); - -- mutex_unlock(&idt821034->mutex); -- -- return ret; -+ return idt821034_set_channel_power(idt821034, ch, power); - } - - static const struct snd_soc_dapm_widget idt821034_dapm_widgets[] = { -@@ -717,9 +706,9 @@ static int idt821034_dai_set_tdm_slot(struct snd_soc_dai *dai, - ch = 0; - while (mask && ch < IDT821034_NB_CHANNEL) { - if (mask & 0x1) { -- mutex_lock(&idt821034->mutex); -- ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_RX, slot); -- mutex_unlock(&idt821034->mutex); -+ scoped_guard(mutex, &idt821034->mutex) -+ ret = idt821034_set_channel_ts(idt821034, ch, -+ IDT821034_CH_RX, slot); - if (ret) { - dev_err(dai->dev, "ch%u set tx tdm slot failed (%d)\n", - ch, ret); -@@ -742,9 +731,9 @@ static int idt821034_dai_set_tdm_slot(struct snd_soc_dai *dai, - ch = 0; - while (mask && ch < IDT821034_NB_CHANNEL) { - if (mask & 0x1) { -- mutex_lock(&idt821034->mutex); -- ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_TX, slot); -- mutex_unlock(&idt821034->mutex); -+ scoped_guard(mutex, &idt821034->mutex) -+ ret = idt821034_set_channel_ts(idt821034, ch, -+ IDT821034_CH_TX, slot); - if (ret) { - dev_err(dai->dev, "ch%u set rx tdm slot failed (%d)\n", - ch, ret); -@@ -769,9 +758,8 @@ static int idt821034_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) - { - struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component); - u8 conf; -- int ret; - -- mutex_lock(&idt821034->mutex); -+ guard(mutex)(&idt821034->mutex); - - conf = idt821034_get_codec_conf(idt821034); - -@@ -785,13 +773,10 @@ static int idt821034_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) - default: - dev_err(dai->dev, "Unsupported DAI format 0x%x\n", - fmt & SND_SOC_DAIFMT_FORMAT_MASK); -- ret = -EINVAL; -- goto end; -+ return -EINVAL; - } -- ret = idt821034_set_codec_conf(idt821034, conf); --end: -- mutex_unlock(&idt821034->mutex); -- return ret; -+ -+ return idt821034_set_codec_conf(idt821034, conf); - } - - static int idt821034_dai_hw_params(struct snd_pcm_substream *substream, -@@ -800,9 +785,8 @@ static int idt821034_dai_hw_params(struct snd_pcm_substream *substream, - { - struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component); - u8 conf; -- int ret; - -- mutex_lock(&idt821034->mutex); -+ guard(mutex)(&idt821034->mutex); - - conf = idt821034_get_codec_conf(idt821034); - -@@ -816,13 +800,10 @@ static int idt821034_dai_hw_params(struct snd_pcm_substream *substream, - default: - dev_err(dai->dev, "Unsupported PCM format 0x%x\n", - params_format(params)); -- ret = -EINVAL; -- goto end; -+ return -EINVAL; - } -- ret = idt821034_set_codec_conf(idt821034, conf); --end: -- mutex_unlock(&idt821034->mutex); -- return ret; -+ -+ return idt821034_set_codec_conf(idt821034, conf); - } - - static const unsigned int idt821034_sample_bits[] = {8}; -@@ -897,11 +878,11 @@ static int idt821034_reset_audio(struct idt821034 *idt821034) - int ret; - u8 i; - -- mutex_lock(&idt821034->mutex); -+ guard(mutex)(&idt821034->mutex); - - ret = idt821034_set_codec_conf(idt821034, 0); - if (ret) -- goto end; -+ return ret; - - for (i = 0; i < IDT821034_NB_CHANNEL; i++) { - idt821034->amps.ch[i].amp_out.gain = IDT821034_GAIN_OUT_INIT_RAW; -@@ -909,24 +890,21 @@ static int idt821034_reset_audio(struct idt821034 *idt821034) - ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_RX, - idt821034->amps.ch[i].amp_out.gain); - if (ret) -- goto end; -+ return ret; - - idt821034->amps.ch[i].amp_in.gain = IDT821034_GAIN_IN_INIT_RAW; - idt821034->amps.ch[i].amp_in.is_muted = false; - ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_TX, - idt821034->amps.ch[i].amp_in.gain); - if (ret) -- goto end; -+ return ret; - - ret = idt821034_set_channel_power(idt821034, i, 0); - if (ret) -- goto end; -+ return ret; - } - -- ret = 0; --end: -- mutex_unlock(&idt821034->mutex); -- return ret; -+ return 0; - } - - static int idt821034_component_probe(struct snd_soc_component *component) -@@ -965,7 +943,7 @@ static int idt821034_chip_gpio_set(struct gpio_chip *c, unsigned int offset, - u8 slic_raw; - int ret; - -- mutex_lock(&idt821034->mutex); -+ guard(mutex)(&idt821034->mutex); - - slic_raw = idt821034_get_written_slic_raw(idt821034, ch); - if (val) -@@ -974,8 +952,6 @@ static int idt821034_chip_gpio_set(struct gpio_chip *c, unsigned int offset, - slic_raw &= ~mask; - ret = idt821034_write_slic_raw(idt821034, ch, slic_raw); - -- mutex_unlock(&idt821034->mutex); -- - if (ret) - dev_err(&idt821034->spi->dev, "set gpio %d (%u, 0x%x) failed (%d)\n", - offset, ch, mask, ret); -@@ -991,9 +967,8 @@ static int idt821034_chip_gpio_get(struct gpio_chip *c, unsigned int offset) - u8 slic_raw; - int ret; - -- mutex_lock(&idt821034->mutex); -- ret = idt821034_read_slic_raw(idt821034, ch, &slic_raw); -- mutex_unlock(&idt821034->mutex); -+ scoped_guard(mutex, &idt821034->mutex) -+ ret = idt821034_read_slic_raw(idt821034, ch, &slic_raw); - if (ret) { - dev_err(&idt821034->spi->dev, "get gpio %d (%u, 0x%x) failed (%d)\n", - offset, ch, mask, ret); -@@ -1015,9 +990,8 @@ static int idt821034_chip_get_direction(struct gpio_chip *c, unsigned int offset - struct idt821034 *idt821034 = gpiochip_get_data(c); - u8 slic_dir; - -- mutex_lock(&idt821034->mutex); -+ guard(mutex)(&idt821034->mutex); - slic_dir = idt821034_get_slic_conf(idt821034, ch); -- mutex_unlock(&idt821034->mutex); - - return slic_dir & mask ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT; - } -@@ -1034,7 +1008,7 @@ static int idt821034_chip_direction_input(struct gpio_chip *c, unsigned int offs - if (mask & ~(IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN)) - return -EPERM; - -- mutex_lock(&idt821034->mutex); -+ guard(mutex)(&idt821034->mutex); - - slic_conf = idt821034_get_slic_conf(idt821034, ch) | mask; - -@@ -1044,7 +1018,6 @@ static int idt821034_chip_direction_input(struct gpio_chip *c, unsigned int offs - offset, ch, mask, ret); - } - -- mutex_unlock(&idt821034->mutex); - return ret; - } - -@@ -1060,7 +1033,7 @@ static int idt821034_chip_direction_output(struct gpio_chip *c, unsigned int off - if (ret) - return ret; - -- mutex_lock(&idt821034->mutex); -+ guard(mutex)(&idt821034->mutex); - - slic_conf = idt821034_get_slic_conf(idt821034, ch) & ~mask; - -@@ -1070,7 +1043,6 @@ static int idt821034_chip_direction_output(struct gpio_chip *c, unsigned int off - offset, ch, mask, ret); - } - -- mutex_unlock(&idt821034->mutex); - return ret; - } - -@@ -1079,24 +1051,22 @@ static int idt821034_reset_gpio(struct idt821034 *idt821034) - int ret; - u8 i; - -- mutex_lock(&idt821034->mutex); -+ guard(mutex)(&idt821034->mutex); - - /* IO0 and IO1 as input for all channels and output IO set to 0 */ - for (i = 0; i < IDT821034_NB_CHANNEL; i++) { - ret = idt821034_set_slic_conf(idt821034, i, - IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN); - if (ret) -- goto end; -+ return ret; - - ret = idt821034_write_slic_raw(idt821034, i, 0); - if (ret) -- goto end; -+ return ret; - - } -- ret = 0; --end: -- mutex_unlock(&idt821034->mutex); -- return ret; -+ -+ return 0; - } - - static int idt821034_gpio_init(struct idt821034 *idt821034) -diff --git a/sound/soc/codecs/lochnagar-sc.c b/sound/soc/codecs/lochnagar-sc.c ---- a/sound/soc/codecs/lochnagar-sc.c -+++ b/sound/soc/codecs/lochnagar-sc.c -@@ -137,15 +137,23 @@ static int lochnagar_sc_set_usb_fmt(struct snd_soc_dai *dai, unsigned int fmt) - return lochnagar_sc_check_fmt(dai, fmt, SND_SOC_DAIFMT_CBP_CFP); - } - -+static const u64 lochnagar_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF; -+ - static const struct snd_soc_dai_ops lochnagar_sc_line_ops = { - .startup = lochnagar_sc_line_startup, - .shutdown = lochnagar_sc_line_shutdown, - .set_fmt = lochnagar_sc_set_line_fmt, -+ .auto_selectable_formats = &lochnagar_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static const struct snd_soc_dai_ops lochnagar_sc_usb_ops = { - .startup = lochnagar_sc_startup, - .set_fmt = lochnagar_sc_set_usb_fmt, -+ .auto_selectable_formats = &lochnagar_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static struct snd_soc_dai_driver lochnagar_sc_dai[] = { -diff --git a/sound/soc/codecs/lpass-macro-common.c b/sound/soc/codecs/lpass-macro-common.c ---- a/sound/soc/codecs/lpass-macro-common.c -+++ b/sound/soc/codecs/lpass-macro-common.c -@@ -1,6 +1,7 @@ - // SPDX-License-Identifier: GPL-2.0-only - // Copyright (c) 2022, The Linux Foundation. All rights reserved. - -+#include - #include - #include - #include -@@ -71,21 +72,16 @@ EXPORT_SYMBOL_GPL(lpass_macro_pds_exit); - - void lpass_macro_set_codec_version(enum lpass_codec_version version) - { -- mutex_lock(&lpass_codec_mutex); -+ guard(mutex)(&lpass_codec_mutex); - lpass_codec_version = version; -- mutex_unlock(&lpass_codec_mutex); - } - EXPORT_SYMBOL_GPL(lpass_macro_set_codec_version); - - enum lpass_codec_version lpass_macro_get_codec_version(void) - { -- enum lpass_codec_version ver; -+ guard(mutex)(&lpass_codec_mutex); - -- mutex_lock(&lpass_codec_mutex); -- ver = lpass_codec_version; -- mutex_unlock(&lpass_codec_mutex); -- -- return ver; -+ return lpass_codec_version; - } - EXPORT_SYMBOL_GPL(lpass_macro_get_codec_version); - -diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c ---- a/sound/soc/codecs/lpass-rx-macro.c -+++ b/sound/soc/codecs/lpass-rx-macro.c -@@ -6,6 +6,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -2030,9 +2031,10 @@ static struct snd_soc_dai_driver rx_macro_dai[] = { - }, - }; - --static void rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable) -+static int rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable) - { - struct regmap *regmap = rx->regmap; -+ int ret; - - if (mclk_enable) { - if (rx->rx_mclk_users == 0) { -@@ -2047,14 +2049,16 @@ static void rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable) - CDC_RX_FS_MCLK_CNT_EN_MASK, - CDC_RX_FS_MCLK_CNT_ENABLE); - regcache_mark_dirty(regmap); -- regcache_sync(regmap); -+ ret = regcache_sync(regmap); -+ if (ret) -+ return ret; - } - rx->rx_mclk_users++; - } else { - if (rx->rx_mclk_users <= 0) { - dev_err(rx->dev, "%s: clock already disabled\n", __func__); - rx->rx_mclk_users = 0; -- return; -+ return 0; - } - rx->rx_mclk_users--; - if (rx->rx_mclk_users == 0) { -@@ -2068,6 +2072,8 @@ static void rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable) - CDC_RX_CLK_MCLK2_EN_MASK, 0x0); - } - } -+ -+ return 0; - } - - static int rx_macro_mclk_event(struct snd_soc_dapm_widget *w, -@@ -2079,11 +2085,9 @@ static int rx_macro_mclk_event(struct snd_soc_dapm_widget *w, - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: -- rx_macro_mclk_enable(rx, true); -- break; -+ return rx_macro_mclk_enable(rx, true); - case SND_SOC_DAPM_POST_PMD: -- rx_macro_mclk_enable(rx, false); -- break; -+ return rx_macro_mclk_enable(rx, false); - default: - dev_err(component->dev, "%s: invalid DAPM event %d\n", __func__, event); - ret = -EINVAL; -@@ -3671,14 +3675,16 @@ static int swclk_gate_enable(struct clk_hw *hw) - struct rx_macro *rx = to_rx_macro(hw); - int ret; - -- ret = clk_prepare_enable(rx->mclk); -+ ret = pm_runtime_resume_and_get(rx->dev); -+ if (ret < 0) -+ return ret; -+ -+ ret = rx_macro_mclk_enable(rx, true); - if (ret) { -- dev_err(rx->dev, "unable to prepare mclk\n"); -+ clk_disable_unprepare(rx->mclk); - return ret; - } - -- rx_macro_mclk_enable(rx, true); -- - regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, - CDC_RX_SWR_CLK_EN_MASK, 1); - -@@ -3693,7 +3699,7 @@ static void swclk_gate_disable(struct clk_hw *hw) - CDC_RX_SWR_CLK_EN_MASK, 0); - - rx_macro_mclk_enable(rx, false); -- clk_disable_unprepare(rx->mclk); -+ pm_runtime_put_autosuspend(rx->dev); - } - - static int swclk_gate_is_enabled(struct clk_hw *hw) -@@ -3864,28 +3870,31 @@ static int rx_macro_probe(struct platform_device *pdev) - rx->dev = dev; - - /* set MCLK and NPL rates */ -- clk_set_rate(rx->mclk, MCLK_FREQ); -- clk_set_rate(rx->npl, MCLK_FREQ); -- -- ret = clk_prepare_enable(rx->macro); -+ ret = clk_set_rate(rx->mclk, MCLK_FREQ); - if (ret) - return ret; - -- ret = clk_prepare_enable(rx->dcodec); -+ ret = clk_set_rate(rx->npl, MCLK_FREQ); - if (ret) -- goto err_dcodec; -+ return ret; - -- ret = clk_prepare_enable(rx->mclk); -+ ret = devm_pm_clk_create(dev); - if (ret) -- goto err_mclk; -+ return ret; - -- ret = clk_prepare_enable(rx->npl); -- if (ret) -- goto err_npl; -+ ret = of_pm_clk_add_clks(dev); -+ if (ret < 0) -+ return ret; - -- ret = clk_prepare_enable(rx->fsgen); -+ pm_runtime_set_autosuspend_delay(dev, 100); -+ pm_runtime_use_autosuspend(dev); -+ ret = devm_pm_runtime_enable(dev); - if (ret) -- goto err_fsgen; -+ return ret; -+ -+ ret = pm_runtime_resume_and_get(dev); -+ if (ret) -+ return ret; - - /* reset swr block */ - regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, -@@ -3902,46 +3911,25 @@ static int rx_macro_probe(struct platform_device *pdev) - rx_macro_dai, - ARRAY_SIZE(rx_macro_dai)); - if (ret) -- goto err_clkout; -- -- -- pm_runtime_set_autosuspend_delay(dev, 3000); -- pm_runtime_use_autosuspend(dev); -- pm_runtime_mark_last_busy(dev); -- pm_runtime_set_active(dev); -- pm_runtime_enable(dev); -+ goto err_rpm_put; - - ret = rx_macro_register_mclk_output(rx); - if (ret) -- goto err_clkout; -+ goto err_rpm_put; -+ -+ ret = pm_runtime_put_autosuspend(dev); -+ if (ret < 0) -+ dev_warn(dev, "runtime PM put failed after probe: %d\n", ret); - - return 0; - --err_clkout: -- clk_disable_unprepare(rx->fsgen); --err_fsgen: -- clk_disable_unprepare(rx->npl); --err_npl: -- clk_disable_unprepare(rx->mclk); --err_mclk: -- clk_disable_unprepare(rx->dcodec); --err_dcodec: -- clk_disable_unprepare(rx->macro); -+err_rpm_put: -+ if (pm_runtime_put_sync_suspend(dev) < 0) -+ dev_warn(dev, "runtime PM sync suspend failed in probe unwind\n"); - - return ret; - } - --static void rx_macro_remove(struct platform_device *pdev) --{ -- struct rx_macro *rx = dev_get_drvdata(&pdev->dev); -- -- clk_disable_unprepare(rx->mclk); -- clk_disable_unprepare(rx->npl); -- clk_disable_unprepare(rx->fsgen); -- clk_disable_unprepare(rx->macro); -- clk_disable_unprepare(rx->dcodec); --} -- - static const struct of_device_id rx_macro_dt_match[] = { - { - .compatible = "qcom,sc7280-lpass-rx-macro", -@@ -3969,13 +3957,17 @@ MODULE_DEVICE_TABLE(of, rx_macro_dt_match); - static int rx_macro_runtime_suspend(struct device *dev) - { - struct rx_macro *rx = dev_get_drvdata(dev); -+ int ret; - - regcache_cache_only(rx->regmap, true); -- regcache_mark_dirty(rx->regmap); - -- clk_disable_unprepare(rx->fsgen); -- clk_disable_unprepare(rx->npl); -- clk_disable_unprepare(rx->mclk); -+ ret = pm_clk_suspend(dev); -+ if (ret) { -+ regcache_cache_only(rx->regmap, false); -+ return ret; -+ } -+ -+ regcache_mark_dirty(rx->regmap); - - return 0; - } -@@ -3985,33 +3977,23 @@ static int rx_macro_runtime_resume(struct device *dev) - struct rx_macro *rx = dev_get_drvdata(dev); - int ret; - -- ret = clk_prepare_enable(rx->mclk); -+ ret = pm_clk_resume(dev); - if (ret) { -- dev_err(dev, "unable to prepare mclk\n"); -+ regcache_cache_only(rx->regmap, true); -+ regcache_mark_dirty(rx->regmap); - return ret; - } - -- ret = clk_prepare_enable(rx->npl); -- if (ret) { -- dev_err(dev, "unable to prepare mclkx2\n"); -- goto err_npl; -- } -- -- ret = clk_prepare_enable(rx->fsgen); -- if (ret) { -- dev_err(dev, "unable to prepare fsgen\n"); -- goto err_fsgen; -- } - regcache_cache_only(rx->regmap, false); -- regcache_sync(rx->regmap); -+ ret = regcache_sync(rx->regmap); -+ if (ret) { -+ regcache_cache_only(rx->regmap, true); -+ regcache_mark_dirty(rx->regmap); -+ pm_clk_suspend(dev); -+ return ret; -+ } - - return 0; --err_fsgen: -- clk_disable_unprepare(rx->npl); --err_npl: -- clk_disable_unprepare(rx->mclk); -- -- return ret; - } - - static const struct dev_pm_ops rx_macro_pm_ops = { -@@ -4026,7 +4008,6 @@ static struct platform_driver rx_macro_driver = { - .pm = pm_ptr(&rx_macro_pm_ops), - }, - .probe = rx_macro_probe, -- .remove = rx_macro_remove, - }; - - module_platform_driver(rx_macro_driver); -diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c ---- a/sound/soc/codecs/lpass-tx-macro.c -+++ b/sound/soc/codecs/lpass-tx-macro.c -@@ -6,6 +6,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -614,6 +615,7 @@ static int tx_macro_mclk_enable(struct tx_macro *tx, - bool mclk_enable) - { - struct regmap *regmap = tx->regmap; -+ int ret; - - if (mclk_enable) { - if (tx->tx_mclk_users == 0) { -@@ -626,7 +628,9 @@ static int tx_macro_mclk_enable(struct tx_macro *tx, - CDC_TX_FS_CNT_EN_MASK, - CDC_TX_FS_CNT_ENABLE); - regcache_mark_dirty(regmap); -- regcache_sync(regmap); -+ ret = regcache_sync(regmap); -+ if (ret) -+ return ret; - } - tx->tx_mclk_users++; - } else { -@@ -739,11 +743,9 @@ static int tx_macro_mclk_event(struct snd_soc_dapm_widget *w, - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: -- tx_macro_mclk_enable(tx, true); -- break; -+ return tx_macro_mclk_enable(tx, true); - case SND_SOC_DAPM_POST_PMD: -- tx_macro_mclk_enable(tx, false); -- break; -+ return tx_macro_mclk_enable(tx, false); - default: - break; - } -@@ -2149,17 +2151,20 @@ static int swclk_gate_enable(struct clk_hw *hw) - struct regmap *regmap = tx->regmap; - int ret; - -- ret = clk_prepare_enable(tx->mclk); -+ ret = pm_runtime_resume_and_get(tx->dev); -+ if (ret < 0) -+ return ret; -+ -+ ret = tx_macro_mclk_enable(tx, true); - if (ret) { -- dev_err(tx->dev, "failed to enable mclk\n"); -+ pm_runtime_put_autosuspend(tx->dev); - return ret; - } - -- tx_macro_mclk_enable(tx, true); -- - regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, - CDC_TX_SWR_CLK_EN_MASK, - CDC_TX_SWR_CLK_ENABLE); -+ - return 0; - } - -@@ -2172,7 +2177,7 @@ static void swclk_gate_disable(struct clk_hw *hw) - CDC_TX_SWR_CLK_EN_MASK, 0x0); - - tx_macro_mclk_enable(tx, false); -- clk_disable_unprepare(tx->mclk); -+ pm_runtime_put_autosuspend(tx->dev); - } - - static int swclk_gate_is_enabled(struct clk_hw *hw) -@@ -2315,28 +2320,31 @@ static int tx_macro_probe(struct platform_device *pdev) - tx->active_decimator[TX_MACRO_AIF3_CAP] = -1; - - /* set MCLK and NPL rates */ -- clk_set_rate(tx->mclk, MCLK_FREQ); -- clk_set_rate(tx->npl, MCLK_FREQ); -- -- ret = clk_prepare_enable(tx->macro); -+ ret = clk_set_rate(tx->mclk, MCLK_FREQ); - if (ret) - goto err; - -- ret = clk_prepare_enable(tx->dcodec); -+ ret = clk_set_rate(tx->npl, MCLK_FREQ); - if (ret) -- goto err_dcodec; -+ goto err; - -- ret = clk_prepare_enable(tx->mclk); -+ ret = devm_pm_clk_create(dev); - if (ret) -- goto err_mclk; -+ goto err; - -- ret = clk_prepare_enable(tx->npl); -- if (ret) -- goto err_npl; -+ ret = of_pm_clk_add_clks(dev); -+ if (ret < 0) -+ goto err; - -- ret = clk_prepare_enable(tx->fsgen); -+ pm_runtime_set_autosuspend_delay(dev, 100); -+ pm_runtime_use_autosuspend(dev); -+ ret = devm_pm_runtime_enable(dev); - if (ret) -- goto err_fsgen; -+ goto err; -+ -+ ret = pm_runtime_resume_and_get(dev); -+ if (ret < 0) -+ goto err; - - - /* reset soundwire block */ -@@ -2356,30 +2364,21 @@ static int tx_macro_probe(struct platform_device *pdev) - tx_macro_dai, - ARRAY_SIZE(tx_macro_dai)); - if (ret) -- goto err_clkout; -- -- pm_runtime_set_autosuspend_delay(dev, 3000); -- pm_runtime_use_autosuspend(dev); -- pm_runtime_mark_last_busy(dev); -- pm_runtime_set_active(dev); -- pm_runtime_enable(dev); -+ goto err_rpm_put; - - ret = tx_macro_register_mclk_output(tx); - if (ret) -- goto err_clkout; -+ goto err_rpm_put; -+ -+ ret = pm_runtime_put_autosuspend(dev); -+ if (ret < 0) -+ dev_warn(dev, "runtime PM put failed after probe: %d\n", ret); - - return 0; - --err_clkout: -- clk_disable_unprepare(tx->fsgen); --err_fsgen: -- clk_disable_unprepare(tx->npl); --err_npl: -- clk_disable_unprepare(tx->mclk); --err_mclk: -- clk_disable_unprepare(tx->dcodec); --err_dcodec: -- clk_disable_unprepare(tx->macro); -+err_rpm_put: -+ if (pm_runtime_put_sync_suspend(dev) < 0) -+ dev_warn(dev, "runtime PM sync suspend failed in probe unwind\n"); - err: - lpass_macro_pds_exit(tx->pds); - -@@ -2390,25 +2389,23 @@ static void tx_macro_remove(struct platform_device *pdev) - { - struct tx_macro *tx = dev_get_drvdata(&pdev->dev); - -- clk_disable_unprepare(tx->macro); -- clk_disable_unprepare(tx->dcodec); -- clk_disable_unprepare(tx->mclk); -- clk_disable_unprepare(tx->npl); -- clk_disable_unprepare(tx->fsgen); -- - lpass_macro_pds_exit(tx->pds); - } - - static int tx_macro_runtime_suspend(struct device *dev) - { - struct tx_macro *tx = dev_get_drvdata(dev); -+ int ret; - - regcache_cache_only(tx->regmap, true); -- regcache_mark_dirty(tx->regmap); - -- clk_disable_unprepare(tx->fsgen); -- clk_disable_unprepare(tx->npl); -- clk_disable_unprepare(tx->mclk); -+ ret = pm_clk_suspend(dev); -+ if (ret) { -+ regcache_cache_only(tx->regmap, false); -+ return ret; -+ } -+ -+ regcache_mark_dirty(tx->regmap); - - return 0; - } -@@ -2418,34 +2415,23 @@ static int tx_macro_runtime_resume(struct device *dev) - struct tx_macro *tx = dev_get_drvdata(dev); - int ret; - -- ret = clk_prepare_enable(tx->mclk); -+ ret = pm_clk_resume(dev); - if (ret) { -- dev_err(dev, "unable to prepare mclk\n"); -+ regcache_cache_only(tx->regmap, true); -+ regcache_mark_dirty(tx->regmap); - return ret; - } - -- ret = clk_prepare_enable(tx->npl); -- if (ret) { -- dev_err(dev, "unable to prepare npl\n"); -- goto err_npl; -- } -- -- ret = clk_prepare_enable(tx->fsgen); -- if (ret) { -- dev_err(dev, "unable to prepare fsgen\n"); -- goto err_fsgen; -- } -- - regcache_cache_only(tx->regmap, false); -- regcache_sync(tx->regmap); -+ ret = regcache_sync(tx->regmap); -+ if (ret) { -+ regcache_cache_only(tx->regmap, true); -+ regcache_mark_dirty(tx->regmap); -+ pm_clk_suspend(dev); -+ return ret; -+ } - - return 0; --err_fsgen: -- clk_disable_unprepare(tx->npl); --err_npl: -- clk_disable_unprepare(tx->mclk); -- -- return ret; - } - - static const struct dev_pm_ops tx_macro_pm_ops = { -diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c ---- a/sound/soc/codecs/lpass-va-macro.c -+++ b/sound/soc/codecs/lpass-va-macro.c -@@ -10,6 +10,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -1351,34 +1352,43 @@ static int fsgen_gate_enable(struct clk_hw *hw) - { - struct va_macro *va = to_va_macro(hw); - struct regmap *regmap = va->regmap; -- int ret; -+ int ret, rpm_ret; - -- if (va->has_swr_master) { -- ret = clk_prepare_enable(va->mclk); -- if (ret) -- return ret; -- } -+ ret = pm_runtime_resume_and_get(va->dev); -+ if (ret < 0) -+ return ret; - - ret = va_macro_mclk_enable(va, true); -+ if (ret) { -+ rpm_ret = pm_runtime_put_autosuspend(va->dev); -+ if (rpm_ret < 0) -+ dev_warn(va->dev, -+ "runtime PM put failed in fsgen enable unwind: %d\n", -+ rpm_ret); -+ return ret; -+ } - if (va->has_swr_master) - regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, - CDC_VA_SWR_CLK_EN_MASK, CDC_VA_SWR_CLK_ENABLE); - -- return ret; -+ return 0; - } - - static void fsgen_gate_disable(struct clk_hw *hw) - { - struct va_macro *va = to_va_macro(hw); - struct regmap *regmap = va->regmap; -+ int ret; - - if (va->has_swr_master) - regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, - CDC_VA_SWR_CLK_EN_MASK, 0x0); - - va_macro_mclk_enable(va, false); -- if (va->has_swr_master) -- clk_disable_unprepare(va->mclk); -+ -+ ret = pm_runtime_put_autosuspend(va->dev); -+ if (ret < 0) -+ dev_warn(va->dev, "runtime PM put failed in fsgen disable: %d\n", ret); - } - - static int fsgen_gate_is_enabled(struct clk_hw *hw) -@@ -1539,6 +1549,7 @@ static int va_macro_probe(struct platform_device *pdev) - void __iomem *base; - u32 sample_rate = 0; - int ret; -+ int rpm_ret; - - va = devm_kzalloc(dev, sizeof(*va), GFP_KERNEL); - if (!va) -@@ -1594,7 +1605,9 @@ static int va_macro_probe(struct platform_device *pdev) - va->has_npl_clk = data->has_npl_clk; - - /* mclk rate */ -- clk_set_rate(va->mclk, 2 * VA_MACRO_MCLK_FREQ); -+ ret = clk_set_rate(va->mclk, 2 * VA_MACRO_MCLK_FREQ); -+ if (ret) -+ goto err; - - if (va->has_npl_clk) { - va->npl = devm_clk_get(dev, "npl"); -@@ -1603,25 +1616,29 @@ static int va_macro_probe(struct platform_device *pdev) - goto err; - } - -- clk_set_rate(va->npl, 2 * VA_MACRO_MCLK_FREQ); -+ ret = clk_set_rate(va->npl, 2 * VA_MACRO_MCLK_FREQ); -+ if (ret) -+ goto err; - } - -- ret = clk_prepare_enable(va->macro); -+ ret = devm_pm_clk_create(dev); - if (ret) - goto err; - -- ret = clk_prepare_enable(va->dcodec); -- if (ret) -- goto err_dcodec; -+ ret = of_pm_clk_add_clks(dev); -+ if (ret < 0) -+ goto err; - -- ret = clk_prepare_enable(va->mclk); -+ pm_runtime_set_autosuspend_delay(dev, 100); -+ pm_runtime_use_autosuspend(dev); -+ ret = devm_pm_runtime_enable(dev); - if (ret) -- goto err_mclk; -+ goto err; - -- if (va->has_npl_clk) { -- ret = clk_prepare_enable(va->npl); -- if (ret) -- goto err_npl; -+ rpm_ret = pm_runtime_resume_and_get(dev); -+ if (rpm_ret < 0) { -+ ret = rpm_ret; -+ goto err; - } - - /** -@@ -1634,7 +1651,7 @@ static int va_macro_probe(struct platform_device *pdev) - /* read version from register */ - ret = va_macro_set_lpass_codec_version(va); - if (ret) -- goto err_clkout; -+ goto err_rpm_put; - } - - if (va->has_swr_master) { -@@ -1664,35 +1681,28 @@ static int va_macro_probe(struct platform_device *pdev) - va_macro_dais, - ARRAY_SIZE(va_macro_dais)); - if (ret) -- goto err_clkout; -- -- pm_runtime_set_autosuspend_delay(dev, 3000); -- pm_runtime_use_autosuspend(dev); -- pm_runtime_mark_last_busy(dev); -- pm_runtime_set_active(dev); -- pm_runtime_enable(dev); -+ goto err_rpm_put; - - ret = va_macro_register_fsgen_output(va); - if (ret) -- goto err_clkout; -+ goto err_rpm_put; - - va->fsgen = devm_clk_hw_get_clk(dev, &va->hw, "fsgen"); - if (IS_ERR(va->fsgen)) { - ret = PTR_ERR(va->fsgen); -- goto err_clkout; -+ goto err_rpm_put; - } - -+ rpm_ret = pm_runtime_put_autosuspend(dev); -+ if (rpm_ret < 0) -+ dev_warn(dev, "runtime PM put failed after probe: %d\n", rpm_ret); -+ - return 0; - --err_clkout: -- if (va->has_npl_clk) -- clk_disable_unprepare(va->npl); --err_npl: -- clk_disable_unprepare(va->mclk); --err_mclk: -- clk_disable_unprepare(va->dcodec); --err_dcodec: -- clk_disable_unprepare(va->macro); -+err_rpm_put: -+ rpm_ret = pm_runtime_put_sync_suspend(dev); -+ if (rpm_ret < 0) -+ dev_warn(dev, "runtime PM sync suspend failed in probe unwind: %d\n", rpm_ret); - err: - lpass_macro_pds_exit(va->pds); - -@@ -1703,53 +1713,52 @@ static void va_macro_remove(struct platform_device *pdev) - { - struct va_macro *va = dev_get_drvdata(&pdev->dev); - -- if (va->has_npl_clk) -- clk_disable_unprepare(va->npl); -- -- clk_disable_unprepare(va->mclk); -- clk_disable_unprepare(va->dcodec); -- clk_disable_unprepare(va->macro); -- - lpass_macro_pds_exit(va->pds); - } - - static int va_macro_runtime_suspend(struct device *dev) - { - struct va_macro *va = dev_get_drvdata(dev); -+ int ret; - - regcache_cache_only(va->regmap, true); -+ -+ ret = pm_clk_suspend(dev); -+ if (ret) { -+ regcache_cache_only(va->regmap, false); -+ return ret; -+ } -+ - regcache_mark_dirty(va->regmap); - -- if (va->has_npl_clk) -- clk_disable_unprepare(va->npl); -- -- clk_disable_unprepare(va->mclk); -- - return 0; - } - - static int va_macro_runtime_resume(struct device *dev) - { - struct va_macro *va = dev_get_drvdata(dev); -- int ret; -+ int ret, sret; - -- ret = clk_prepare_enable(va->mclk); -+ ret = pm_clk_resume(dev); - if (ret) { -- dev_err(va->dev, "unable to prepare mclk\n"); -+ regcache_cache_only(va->regmap, true); -+ regcache_mark_dirty(va->regmap); - return ret; - } - -- if (va->has_npl_clk) { -- ret = clk_prepare_enable(va->npl); -- if (ret) { -- clk_disable_unprepare(va->mclk); -- dev_err(va->dev, "unable to prepare npl\n"); -- return ret; -- } -- } -- - regcache_cache_only(va->regmap, false); -- regcache_sync(va->regmap); -+ -+ ret = regcache_sync(va->regmap); -+ if (ret) { -+ regcache_cache_only(va->regmap, true); -+ regcache_mark_dirty(va->regmap); -+ sret = pm_clk_suspend(dev); -+ if (sret) -+ dev_err(va->dev, -+ "failed to suspend clocks after regcache sync failure: %d\n", -+ sret); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c ---- a/sound/soc/codecs/lpass-wsa-macro.c -+++ b/sound/soc/codecs/lpass-wsa-macro.c -@@ -12,6 +12,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -2529,15 +2530,13 @@ static const struct snd_soc_dapm_route wsa_audio_map[] = { - static int wsa_swrm_clock(struct wsa_macro *wsa, bool enable) - { - struct regmap *regmap = wsa->regmap; -+ int ret; - - if (enable) { -- int ret; -- -- ret = clk_prepare_enable(wsa->mclk); -- if (ret) { -- dev_err(wsa->dev, "failed to enable mclk\n"); -+ ret = pm_runtime_resume_and_get(wsa->dev); -+ if (ret < 0) - return ret; -- } -+ - wsa_macro_mclk_enable(wsa, true); - - regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, -@@ -2548,7 +2547,10 @@ static int wsa_swrm_clock(struct wsa_macro *wsa, bool enable) - regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, - CDC_WSA_SWR_CLK_EN_MASK, 0); - wsa_macro_mclk_enable(wsa, false); -- clk_disable_unprepare(wsa->mclk); -+ -+ ret = pm_runtime_put_autosuspend(wsa->dev); -+ if (ret < 0) -+ dev_warn(wsa->dev, "runtime PM put failed: %d\n", ret); - } - - return 0; -@@ -2656,7 +2658,7 @@ static int wsa_macro_register_mclk_output(struct wsa_macro *wsa) - init.num_parents = 1; - wsa->hw.init = &init; - hw = &wsa->hw; -- ret = clk_hw_register(wsa->dev, hw); -+ ret = devm_clk_hw_register(wsa->dev, hw); - if (ret) - return ret; - -@@ -2771,28 +2773,31 @@ static int wsa_macro_probe(struct platform_device *pdev) - wsa->dev = dev; - - /* set MCLK and NPL rates */ -- clk_set_rate(wsa->mclk, WSA_MACRO_MCLK_FREQ); -- clk_set_rate(wsa->npl, WSA_MACRO_MCLK_FREQ); -- -- ret = clk_prepare_enable(wsa->macro); -+ ret = clk_set_rate(wsa->mclk, WSA_MACRO_MCLK_FREQ); - if (ret) -- goto err; -+ return ret; - -- ret = clk_prepare_enable(wsa->dcodec); -+ ret = clk_set_rate(wsa->npl, WSA_MACRO_MCLK_FREQ); - if (ret) -- goto err_dcodec; -+ return ret; - -- ret = clk_prepare_enable(wsa->mclk); -+ ret = devm_pm_clk_create(dev); - if (ret) -- goto err_mclk; -+ return ret; - -- ret = clk_prepare_enable(wsa->npl); -- if (ret) -- goto err_npl; -+ ret = of_pm_clk_add_clks(dev); -+ if (ret < 0) -+ return ret; - -- ret = clk_prepare_enable(wsa->fsgen); -+ pm_runtime_set_autosuspend_delay(dev, 100); -+ pm_runtime_use_autosuspend(dev); -+ ret = devm_pm_runtime_enable(dev); - if (ret) -- goto err_fsgen; -+ return ret; -+ -+ ret = pm_runtime_resume_and_get(dev); -+ if (ret < 0) -+ return ret; - - /* reset swr ip */ - regmap_update_bits(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, -@@ -2809,56 +2814,37 @@ static int wsa_macro_probe(struct platform_device *pdev) - wsa_macro_dai, - ARRAY_SIZE(wsa_macro_dai)); - if (ret) -- goto err_clkout; -- -- pm_runtime_set_autosuspend_delay(dev, 3000); -- pm_runtime_use_autosuspend(dev); -- pm_runtime_mark_last_busy(dev); -- pm_runtime_set_active(dev); -- pm_runtime_enable(dev); -+ goto err_rpm_put; - - ret = wsa_macro_register_mclk_output(wsa); - if (ret) -- goto err_clkout; -+ goto err_rpm_put; -+ -+ ret = pm_runtime_put_autosuspend(dev); -+ if (ret < 0) -+ dev_warn(dev, "runtime PM put failed after probe: %d\n", ret); - - return 0; -- --err_clkout: -- clk_disable_unprepare(wsa->fsgen); --err_fsgen: -- clk_disable_unprepare(wsa->npl); --err_npl: -- clk_disable_unprepare(wsa->mclk); --err_mclk: -- clk_disable_unprepare(wsa->dcodec); --err_dcodec: -- clk_disable_unprepare(wsa->macro); --err: -+err_rpm_put: -+ if (pm_runtime_put_sync_suspend(dev) < 0) -+ dev_warn(dev, "runtime PM sync suspend failed in probe unwind\n"); - return ret; -- --} -- --static void wsa_macro_remove(struct platform_device *pdev) --{ -- struct wsa_macro *wsa = dev_get_drvdata(&pdev->dev); -- -- clk_disable_unprepare(wsa->macro); -- clk_disable_unprepare(wsa->dcodec); -- clk_disable_unprepare(wsa->mclk); -- clk_disable_unprepare(wsa->npl); -- clk_disable_unprepare(wsa->fsgen); - } - - static int wsa_macro_runtime_suspend(struct device *dev) - { - struct wsa_macro *wsa = dev_get_drvdata(dev); -+ int ret; - - regcache_cache_only(wsa->regmap, true); -- regcache_mark_dirty(wsa->regmap); - -- clk_disable_unprepare(wsa->fsgen); -- clk_disable_unprepare(wsa->npl); -- clk_disable_unprepare(wsa->mclk); -+ ret = pm_clk_suspend(dev); -+ if (ret) { -+ regcache_cache_only(wsa->regmap, false); -+ return ret; -+ } -+ -+ regcache_mark_dirty(wsa->regmap); - - return 0; - } -@@ -2866,36 +2852,29 @@ static int wsa_macro_runtime_suspend(struct device *dev) - static int wsa_macro_runtime_resume(struct device *dev) - { - struct wsa_macro *wsa = dev_get_drvdata(dev); -- int ret; -+ int ret, sret; - -- ret = clk_prepare_enable(wsa->mclk); -+ ret = pm_clk_resume(dev); - if (ret) { -- dev_err(dev, "unable to prepare mclk\n"); -+ regcache_cache_only(wsa->regmap, true); -+ regcache_mark_dirty(wsa->regmap); -+ return ret; -+ } -+ regcache_cache_only(wsa->regmap, false); -+ -+ ret = regcache_sync(wsa->regmap); -+ if (ret) { -+ regcache_cache_only(wsa->regmap, true); -+ regcache_mark_dirty(wsa->regmap); -+ sret = pm_clk_suspend(dev); -+ if (sret) -+ dev_err(dev, -+ "failed to suspend clocks after regcache sync failure: %d\n", -+ sret); - return ret; - } - -- ret = clk_prepare_enable(wsa->npl); -- if (ret) { -- dev_err(dev, "unable to prepare mclkx2\n"); -- goto err_npl; -- } -- -- ret = clk_prepare_enable(wsa->fsgen); -- if (ret) { -- dev_err(dev, "unable to prepare fsgen\n"); -- goto err_fsgen; -- } -- -- regcache_cache_only(wsa->regmap, false); -- regcache_sync(wsa->regmap); -- - return 0; --err_fsgen: -- clk_disable_unprepare(wsa->npl); --err_npl: -- clk_disable_unprepare(wsa->mclk); -- -- return ret; - } - - static const struct dev_pm_ops wsa_macro_pm_ops = { -@@ -2929,7 +2908,6 @@ static struct platform_driver wsa_macro_driver = { - .pm = pm_ptr(&wsa_macro_pm_ops), - }, - .probe = wsa_macro_probe, -- .remove = wsa_macro_remove, - }; - - module_platform_driver(wsa_macro_driver); -diff --git a/sound/soc/codecs/madera.c b/sound/soc/codecs/madera.c ---- a/sound/soc/codecs/madera.c -+++ b/sound/soc/codecs/madera.c -@@ -6,6 +6,7 @@ - // Cirrus Logic International Semiconductor Ltd. - // - -+#include - #include - #include - #include -@@ -513,7 +514,7 @@ int madera_domain_clk_ev(struct snd_soc_dapm_widget *w, - * We can't rely on the DAPM mutex for locking because we need a lock - * that can safely be called in hw_params - */ -- mutex_lock(&priv->rate_lock); -+ guard(mutex)(&priv->rate_lock); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: -@@ -532,8 +533,6 @@ int madera_domain_clk_ev(struct snd_soc_dapm_widget *w, - - madera_debug_dump_domain_groups(priv); - -- mutex_unlock(&priv->rate_lock); -- - return 0; - } - EXPORT_SYMBOL_GPL(madera_domain_clk_ev); -@@ -875,9 +874,8 @@ static int madera_adsp_rate_get(struct snd_kcontrol *kcontrol, - const int adsp_num = e->shift_l; - int item; - -- mutex_lock(&priv->rate_lock); -- cached_rate = priv->adsp_rate_cache[adsp_num]; -- mutex_unlock(&priv->rate_lock); -+ scoped_guard(mutex, &priv->rate_lock) -+ cached_rate = priv->adsp_rate_cache[adsp_num]; - - item = snd_soc_enum_val_to_item(e, cached_rate); - ucontrol->value.enumerated.item[0] = item; -@@ -893,7 +891,6 @@ static int madera_adsp_rate_put(struct snd_kcontrol *kcontrol, - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - const int adsp_num = e->shift_l; - const unsigned int item = ucontrol->value.enumerated.item[0]; -- int ret = 0; - - if (item >= e->items) - return -EINVAL; -@@ -903,22 +900,20 @@ static int madera_adsp_rate_put(struct snd_kcontrol *kcontrol, - * maintain consistent behaviour that rate domains cannot be changed - * while in use since this is a hardware requirement - */ -- mutex_lock(&priv->rate_lock); -+ guard(mutex)(&priv->rate_lock); - - if (!madera_can_change_grp_rate(priv, priv->adsp[adsp_num].cs_dsp.base)) { - dev_warn(priv->madera->dev, - "Cannot change '%s' while in use by active audio paths\n", - kcontrol->id.name); -- ret = -EBUSY; -+ return -EBUSY; - } else if (priv->adsp_rate_cache[adsp_num] != e->values[item]) { - /* Volatile register so defer until the codec is powered up */ - priv->adsp_rate_cache[adsp_num] = e->values[item]; -- ret = 1; -+ return 1; - } - -- mutex_unlock(&priv->rate_lock); -- -- return ret; -+ return 0; - } - - static const struct soc_enum madera_adsp_rate_enum[] = { -@@ -1061,15 +1056,13 @@ int madera_rate_put(struct snd_kcontrol *kcontrol, - * Prevent the domain powering up while we're checking whether it's - * safe to change rate domain - */ -- mutex_lock(&priv->rate_lock); -+ guard(mutex)(&priv->rate_lock); - - val = snd_soc_component_read(component, e->reg); - val >>= e->shift_l; - val &= e->mask; -- if (snd_soc_enum_item_to_val(e, item) == val) { -- ret = 0; -- goto out; -- } -+ if (snd_soc_enum_item_to_val(e, item) == val) -+ return 0; - - if (!madera_can_change_grp_rate(priv, e->reg)) { - dev_warn(priv->madera->dev, -@@ -1082,8 +1075,6 @@ int madera_rate_put(struct snd_kcontrol *kcontrol, - ret = snd_soc_put_enum_double(kcontrol, ucontrol); - madera_spin_sysclk(priv); - } --out: -- mutex_unlock(&priv->rate_lock); - - return ret; - } -@@ -3041,12 +3032,11 @@ static int madera_hw_params_rate(struct snd_pcm_substream *substream, - if ((cur & MADERA_AIF1_RATE_MASK) == (tar & MADERA_AIF1_RATE_MASK)) - return 0; - -- mutex_lock(&priv->rate_lock); -+ guard(mutex)(&priv->rate_lock); - - if (!madera_can_change_grp_rate(priv, base + MADERA_AIF_RATE_CTRL)) { - madera_aif_warn(dai, "Cannot change rate while active\n"); -- ret = -EBUSY; -- goto out; -+ return -EBUSY; - } - - /* Guard the rate change with SYSCLK cycles */ -@@ -3055,9 +3045,6 @@ static int madera_hw_params_rate(struct snd_pcm_substream *substream, - MADERA_AIF1_RATE_MASK, tar); - madera_spin_sysclk(priv); - --out: -- mutex_unlock(&priv->rate_lock); -- - return ret; - } - -@@ -3345,6 +3332,16 @@ static int madera_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, - return 0; - } - -+static const u64 madera_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_B | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_NB_IF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_NF | -+ SND_SOC_POSSIBLE_DAIFMT_IB_IF; -+ - const struct snd_soc_dai_ops madera_dai_ops = { - .startup = &madera_startup, - .set_fmt = &madera_set_fmt, -@@ -3352,6 +3349,8 @@ const struct snd_soc_dai_ops madera_dai_ops = { - .hw_params = &madera_hw_params, - .set_sysclk = &madera_dai_set_sysclk, - .set_tristate = &madera_set_tristate, -+ .auto_selectable_formats = &madera_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - EXPORT_SYMBOL_GPL(madera_dai_ops); - -diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c ---- a/sound/soc/codecs/max98090.c -+++ b/sound/soc/codecs/max98090.c -@@ -2341,6 +2341,7 @@ static irqreturn_t max98090_interrupt(int irq, void *data) - * - * @component: MAX98090 component - * @jack: jack to report detection events on -+ * @data: can be used if codec driver need extra data for configuring jack - * - * Enable microphone detection via IRQ on the MAX98090. If GPIOs are - * being used to bring out signals to the processor then only platform -@@ -2646,12 +2647,18 @@ static void max98090_i2c_remove(struct i2c_client *client) - static int max98090_runtime_resume(struct device *dev) - { - struct max98090_priv *max98090 = dev_get_drvdata(dev); -+ int ret; - - regcache_cache_only(max98090->regmap, false); - - max98090_reset(max98090); - -- regcache_sync(max98090->regmap); -+ ret = regcache_sync(max98090->regmap); -+ if (ret < 0) { -+ regcache_cache_only(max98090->regmap, true); -+ regcache_mark_dirty(max98090->regmap); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c ---- a/sound/soc/codecs/max98095.c -+++ b/sound/soc/codecs/max98095.c -@@ -5,6 +5,7 @@ - * Copyright 2011 Maxim Integrated Products - */ - -+#include - #include - #include - #include -@@ -1532,15 +1533,17 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol, - regsave = snd_soc_component_read(component, M98095_088_CFG_LEVEL); - snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, 0); - -- mutex_lock(&max98095->lock); -- snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG); -- m98095_eq_band(component, channel, 0, coef_set->band1); -- m98095_eq_band(component, channel, 1, coef_set->band2); -- m98095_eq_band(component, channel, 2, coef_set->band3); -- m98095_eq_band(component, channel, 3, coef_set->band4); -- m98095_eq_band(component, channel, 4, coef_set->band5); -- snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, 0); -- mutex_unlock(&max98095->lock); -+ scoped_guard(mutex, &max98095->lock) { -+ snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, -+ M98095_SEG, M98095_SEG); -+ m98095_eq_band(component, channel, 0, coef_set->band1); -+ m98095_eq_band(component, channel, 1, coef_set->band2); -+ m98095_eq_band(component, channel, 2, coef_set->band3); -+ m98095_eq_band(component, channel, 3, coef_set->band4); -+ m98095_eq_band(component, channel, 4, coef_set->band5); -+ snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, -+ M98095_SEG, 0); -+ } - - /* Restore the original on/off state */ - snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, regsave); -@@ -1683,12 +1686,14 @@ static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol, - regsave = snd_soc_component_read(component, M98095_088_CFG_LEVEL); - snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, 0); - -- mutex_lock(&max98095->lock); -- snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG); -- m98095_biquad_band(component, channel, 0, coef_set->band1); -- m98095_biquad_band(component, channel, 1, coef_set->band2); -- snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, 0); -- mutex_unlock(&max98095->lock); -+ scoped_guard(mutex, &max98095->lock) { -+ snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, -+ M98095_SEG, M98095_SEG); -+ m98095_biquad_band(component, channel, 0, coef_set->band1); -+ m98095_biquad_band(component, channel, 1, coef_set->band2); -+ snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, -+ M98095_SEG, 0); -+ } - - /* Restore the original on/off state */ - snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, regsave); -diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c ---- a/sound/soc/codecs/max98363.c -+++ b/sound/soc/codecs/max98363.c -@@ -100,7 +100,12 @@ static int max98363_resume(struct device *dev) - return ret; - - regcache_cache_only(max98363->regmap, false); -- regcache_sync(max98363->regmap); -+ ret = regcache_sync(max98363->regmap); -+ if (ret) { -+ regcache_cache_only(max98363->regmap, true); -+ regcache_mark_dirty(max98363->regmap); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/max98373-i2c.c b/sound/soc/codecs/max98373-i2c.c ---- a/sound/soc/codecs/max98373-i2c.c -+++ b/sound/soc/codecs/max98373-i2c.c -@@ -488,10 +488,17 @@ static int max98373_suspend(struct device *dev) - static int max98373_resume(struct device *dev) - { - struct max98373_priv *max98373 = dev_get_drvdata(dev); -+ int ret; - - regcache_cache_only(max98373->regmap, false); - max98373_reset(max98373, dev); -- regcache_sync(max98373->regmap); -+ ret = regcache_sync(max98373->regmap); -+ if (ret) { -+ regcache_cache_only(max98373->regmap, true); -+ regcache_mark_dirty(max98373->regmap); -+ return ret; -+ } -+ - return 0; - } - -diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c ---- a/sound/soc/codecs/max98373-sdw.c -+++ b/sound/soc/codecs/max98373-sdw.c -@@ -277,7 +277,12 @@ static int max98373_resume(struct device *dev) - } - - regcache_cache_only(max98373->regmap, false); -- regcache_sync(max98373->regmap); -+ ret = regcache_sync(max98373->regmap); -+ if (ret) { -+ regcache_cache_only(max98373->regmap, true); -+ regcache_mark_dirty(max98373->regmap); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/max98388.c b/sound/soc/codecs/max98388.c ---- a/sound/soc/codecs/max98388.c -+++ b/sound/soc/codecs/max98388.c -@@ -863,10 +863,16 @@ static int max98388_suspend(struct device *dev) - static int max98388_resume(struct device *dev) - { - struct max98388_priv *max98388 = dev_get_drvdata(dev); -+ int ret; - - regcache_cache_only(max98388->regmap, false); - max98388_reset(max98388, dev); -- regcache_sync(max98388->regmap); -+ ret = regcache_sync(max98388->regmap); -+ if (ret) { -+ regcache_cache_only(max98388->regmap, true); -+ regcache_mark_dirty(max98388->regmap); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c ---- a/sound/soc/codecs/max98390.c -+++ b/sound/soc/codecs/max98390.c -@@ -788,7 +788,6 @@ static int max98390_dsm_init(struct snd_soc_component *component) - const char *vendor, *product; - struct max98390_priv *max98390 = - snd_soc_component_get_drvdata(component); -- const struct firmware *fw; - char *dsm_param; - - vendor = dmi_get_system_info(DMI_SYS_VENDOR); -@@ -805,6 +804,8 @@ static int max98390_dsm_init(struct snd_soc_component *component) - snprintf(filename, sizeof(filename), "%s", - max98390->dsm_param_name); - } -+ -+ const struct firmware *fw __free(firmware) = NULL; - ret = request_firmware(&fw, filename, component->dev); - if (ret) { - ret = request_firmware(&fw, "dsm_param.bin", component->dev); -@@ -812,7 +813,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) - ret = request_firmware(&fw, "dsmparam.bin", - component->dev); - if (ret) -- goto err; -+ return ret; - } - } - -@@ -822,8 +823,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) - if (fw->size < MAX98390_DSM_PARAM_MIN_SIZE) { - dev_err(component->dev, - "param fw is invalid.\n"); -- ret = -EINVAL; -- goto err_alloc; -+ return -EINVAL; - } - dsm_param = (char *)fw->data; - param_start_addr = (dsm_param[0] & 0xff) | (dsm_param[1] & 0xff) << 8; -@@ -833,8 +833,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) - fw->size < param_size + MAX98390_DSM_PAYLOAD_OFFSET) { - dev_err(component->dev, - "param fw is invalid.\n"); -- ret = -EINVAL; -- goto err_alloc; -+ return -EINVAL; - } - regmap_write(max98390->regmap, MAX98390_R203A_AMP_EN, 0x80); - dsm_param += MAX98390_DSM_PAYLOAD_OFFSET; -@@ -842,10 +841,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) - dsm_param, param_size); - regmap_write(max98390->regmap, MAX98390_R23E1_DSP_GLOBAL_EN, 0x01); - --err_alloc: -- release_firmware(fw); --err: -- return ret; -+ return 0; - } - - static void max98390_init_regs(struct snd_soc_component *component) -@@ -952,11 +948,17 @@ static int max98390_suspend(struct device *dev) - static int max98390_resume(struct device *dev) - { - struct max98390_priv *max98390 = dev_get_drvdata(dev); -+ int ret; - - dev_dbg(dev, "%s:Enter\n", __func__); - - regcache_cache_only(max98390->regmap, false); -- regcache_sync(max98390->regmap); -+ ret = regcache_sync(max98390->regmap); -+ if (ret) { -+ regcache_cache_only(max98390->regmap, true); -+ regcache_mark_dirty(max98390->regmap); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c ---- a/sound/soc/codecs/max98396.c -+++ b/sound/soc/codecs/max98396.c -@@ -1600,19 +1600,37 @@ static int max98396_resume(struct device *dev) - if (max98396->pvdd) { - ret = regulator_enable(max98396->pvdd); - if (ret < 0) -- return ret; -+ goto err_core_supplies; - } - - if (max98396->vbat) { - ret = regulator_enable(max98396->vbat); - if (ret < 0) -- return ret; -+ goto err_pvdd; - } - - regcache_cache_only(max98396->regmap, false); - max98396_reset(max98396, dev); -- regcache_sync(max98396->regmap); -+ ret = regcache_sync(max98396->regmap); -+ if (ret < 0) { -+ regcache_cache_only(max98396->regmap, true); -+ regcache_mark_dirty(max98396->regmap); -+ goto err_vbat; -+ } -+ - return 0; -+ -+err_vbat: -+ if (max98396->vbat) -+ regulator_disable(max98396->vbat); -+err_pvdd: -+ if (max98396->pvdd) -+ regulator_disable(max98396->pvdd); -+err_core_supplies: -+ regulator_bulk_disable(MAX98396_NUM_CORE_SUPPLIES, -+ max98396->core_supplies); -+ -+ return ret; - } - - static const struct dev_pm_ops max98396_pm = { -diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c ---- a/sound/soc/codecs/max98927.c -+++ b/sound/soc/codecs/max98927.c -@@ -742,11 +742,17 @@ static int max98927_suspend(struct device *dev) - static int max98927_resume(struct device *dev) - { - struct max98927_priv *max98927 = dev_get_drvdata(dev); -+ int ret; - - regmap_write(max98927->regmap, MAX98927_R0100_SOFT_RESET, - MAX98927_SOFT_RESET); - regcache_cache_only(max98927->regmap, false); -- regcache_sync(max98927->regmap); -+ ret = regcache_sync(max98927->regmap); -+ if (ret) { -+ regcache_cache_only(max98927->regmap, true); -+ regcache_mark_dirty(max98927->regmap); -+ return ret; -+ } - return 0; - } - -diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c ---- a/sound/soc/codecs/msm8916-wcd-analog.c -+++ b/sound/soc/codecs/msm8916-wcd-analog.c -@@ -217,9 +217,11 @@ - #define CDC_A_RX_HPH_BIAS_LDO_OCP (0xf195) - #define CDC_A_RX_HPH_BIAS_CNP (0xf196) - #define CDC_A_RX_HPH_CNP_EN (0xf197) -+#define CDC_A_RX_HPH_L_TEST (0xf19A) - #define CDC_A_RX_HPH_L_PA_DAC_CTL (0xf19B) - #define RX_HPA_L_PA_DAC_CTL_DATA_RESET_MASK BIT(1) - #define RX_HPA_L_PA_DAC_CTL_DATA_RESET_RESET BIT(1) -+#define CDC_A_RX_HPH_R_TEST (0xf19C) - #define CDC_A_RX_HPH_R_PA_DAC_CTL (0xf19D) - #define RX_HPH_R_PA_DAC_CTL_DATA_RESET BIT(1) - #define RX_HPH_R_PA_DAC_CTL_DATA_RESET_MASK BIT(1) -@@ -232,6 +234,8 @@ - #define RX_EAR_CTL_PA_SEL_MASK BIT(7) - #define RX_EAR_CTL_PA_SEL BIT(7) - -+#define CDC_A_RX_EAR_STATUS (0xf1A1) -+ - #define CDC_A_SPKR_DAC_CTL (0xf1B0) - #define SPKR_DAC_CTL_DAC_RESET_MASK BIT(4) - #define SPKR_DAC_CTL_DAC_RESET_NORMAL 0 -@@ -250,6 +254,7 @@ - SPKR_DRV_CAL_EN | SPKR_DRV_SETTLE_EN | \ - SPKR_DRV_FW_EN | SPKR_DRV_BOOST_SET | \ - SPKR_DRV_CMFB_SET | SPKR_DRV_GAIN_SET) -+#define CDC_A_SPKR_ANA_BIAS_SET (0xf1B3) - #define CDC_A_SPKR_OCP_CTL (0xf1B4) - #define CDC_A_SPKR_PWRSTG_CTL (0xf1B5) - #define SPKR_PWRSTG_CTL_DAC_EN_MASK BIT(0) -@@ -264,6 +269,7 @@ - - #define CDC_A_SPKR_DRV_DBG (0xf1B7) - #define CDC_A_CURRENT_LIMIT (0xf1C0) -+#define CDC_A_BYPASS_MODE (0xf1C2) - #define CDC_A_BOOST_EN_CTL (0xf1C3) - #define CDC_A_SLOPE_COMP_IP_ZERO (0xf1C4) - #define CDC_A_SEC_ACCESS (0xf1D0) -@@ -286,6 +292,11 @@ static const char * const supply_names[] = { - - #define MBHC_MAX_BUTTONS (5) - -+struct wcd_reg_seq { -+ const struct reg_default *seq; -+ int seq_size; -+}; -+ - struct pm8916_wcd_analog_priv { - u16 pmic_rev; - u16 codec_version; -@@ -696,6 +707,59 @@ static int pm8916_wcd_analog_enable_ear_pa(struct snd_soc_dapm_widget *w, - return 0; - } - -+static int pm8916_wcd_analog_enable_hphl_pa(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *kcontrol, -+ int event) -+{ -+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); -+ struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev); -+ -+ /* This quirk is not required for revisions prior to CAJON_2_0 */ -+ if (priv->codec_version < 4) -+ return 0; -+ -+ switch (event) { -+ case SND_SOC_DAPM_POST_PMU: -+ usleep_range(7000, 7100); -+ snd_soc_component_update_bits(component, CDC_A_RX_HPH_L_TEST, -+ 0x04, 0x04); -+ break; -+ case SND_SOC_DAPM_POST_PMD: -+ /* wait 20 ms after the digital codec has powered down */ -+ msleep(20); -+ snd_soc_component_update_bits(component, CDC_A_RX_HPH_L_TEST, -+ 0x04, 0x00); -+ break; -+ } -+ return 0; -+} -+ -+static int pm8916_wcd_analog_enable_hphr_pa(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *kcontrol, -+ int event) -+{ -+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); -+ struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev); -+ -+ /* This quirk is not required for revisions prior to CAJON_2_0 */ -+ if (priv->codec_version < 4) -+ return 0; -+ -+ switch (event) { -+ case SND_SOC_DAPM_POST_PMU: -+ usleep_range(7000, 7100); -+ snd_soc_component_update_bits(component, CDC_A_RX_HPH_R_TEST, -+ 0x04, 0x04); -+ break; -+ case SND_SOC_DAPM_POST_PMD: -+ msleep(20); -+ snd_soc_component_update_bits(component, CDC_A_RX_HPH_R_TEST, -+ 0x04, 0x00); -+ break; -+ } -+ return 0; -+} -+ - static const struct reg_default wcd_reg_defaults_2_0[] = { - {CDC_A_RX_COM_OCP_CTL, 0xD1}, - {CDC_A_RX_COM_OCP_COUNT, 0xFF}, -@@ -715,9 +779,69 @@ static const struct reg_default wcd_reg_defaults_2_0[] = { - {CDC_A_MASTER_BIAS_CTL, 0x30}, - }; - -+static const struct wcd_reg_seq pm8916_data = { -+ .seq = wcd_reg_defaults_2_0, -+ .seq_size = ARRAY_SIZE(wcd_reg_defaults_2_0), -+}; -+ -+static const struct reg_default wcd_reg_defaults_pm8950[] = { -+ {CDC_A_RX_COM_OCP_CTL, 0xd1}, -+ {CDC_A_RX_COM_OCP_COUNT, 0xff}, -+ {CDC_D_SEC_ACCESS, 0xa5}, -+ {CDC_D_PERPH_RESET_CTL3, 0x0f}, -+ {CDC_A_TX_1_2_OPAMP_BIAS, 0x4c}, -+ {CDC_A_NCP_FBCTRL, 0xa8}, -+ {CDC_A_NCP_VCTRL, 0xa4}, -+ {CDC_A_SPKR_DRV_CTL, 0x69}, -+ {CDC_A_SPKR_DRV_DBG, 0x01}, -+ {CDC_A_SEC_ACCESS, 0xa5}, -+ {CDC_A_PERPH_RESET_CTL3, 0x0f}, -+ {CDC_A_CURRENT_LIMIT, 0x82}, -+ {CDC_A_SPKR_ANA_BIAS_SET, 0x41}, -+ {CDC_A_SPKR_DAC_CTL, 0x03}, -+ {CDC_A_SPKR_OCP_CTL, 0xe1}, -+ {CDC_A_RX_HPH_BIAS_PA, 0xfa}, -+ {CDC_A_MASTER_BIAS_CTL, 0x30}, -+ {CDC_A_MICB_1_INT_RBIAS, 0x00}, -+}; -+ -+static const struct wcd_reg_seq pm8950_data = { -+ .seq = wcd_reg_defaults_pm8950, -+ .seq_size = ARRAY_SIZE(wcd_reg_defaults_pm8950), -+}; -+ -+static const struct reg_default wcd_reg_defaults_pm8953[] = { -+ {CDC_A_RX_COM_OCP_CTL, 0xd1}, -+ {CDC_A_RX_COM_OCP_COUNT, 0xff}, -+ {CDC_D_SEC_ACCESS, 0xa5}, -+ {CDC_D_PERPH_RESET_CTL3, 0x0f}, -+ {CDC_A_TX_1_2_OPAMP_BIAS, 0x4c}, -+ {CDC_A_NCP_FBCTRL, 0xa8}, -+ {CDC_A_NCP_VCTRL, 0xa4}, -+ {CDC_A_SPKR_DRV_CTL, 0x69}, -+ {CDC_A_SPKR_DRV_DBG, 0x01}, -+ {CDC_A_SEC_ACCESS, 0xa5}, -+ {CDC_A_PERPH_RESET_CTL3, 0x0f}, -+ {CDC_A_CURRENT_LIMIT, 0xa2}, -+ {CDC_A_BYPASS_MODE, 0x18}, -+ {CDC_A_SPKR_ANA_BIAS_SET, 0x41}, -+ {CDC_A_SPKR_DAC_CTL, 0x03}, -+ {CDC_A_SPKR_OCP_CTL, 0xe1}, -+ {CDC_A_RX_HPH_BIAS_PA, 0xfa}, -+ {CDC_A_RX_EAR_STATUS, 0x10}, -+ {CDC_A_MASTER_BIAS_CTL, 0x30}, -+ {CDC_A_MICB_1_INT_RBIAS, 0x00}, -+}; -+ -+static const struct wcd_reg_seq pm8953_data = { -+ .seq = wcd_reg_defaults_pm8953, -+ .seq_size = ARRAY_SIZE(wcd_reg_defaults_pm8953), -+}; -+ - static int pm8916_wcd_analog_probe(struct snd_soc_component *component) - { - struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev); -+ const struct wcd_reg_seq *wcd_reg_init_data; - int err, reg; - - err = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); -@@ -738,9 +862,11 @@ static int pm8916_wcd_analog_probe(struct snd_soc_component *component) - snd_soc_component_write(component, CDC_D_PERPH_RESET_CTL4, 0x01); - snd_soc_component_write(component, CDC_A_PERPH_RESET_CTL4, 0x01); - -- for (reg = 0; reg < ARRAY_SIZE(wcd_reg_defaults_2_0); reg++) -- snd_soc_component_write(component, wcd_reg_defaults_2_0[reg].reg, -- wcd_reg_defaults_2_0[reg].def); -+ wcd_reg_init_data = of_device_get_match_data(component->dev); -+ -+ for (reg = 0; reg < wcd_reg_init_data->seq_size; reg++) -+ snd_soc_component_write(component, wcd_reg_init_data->seq[reg].reg, -+ wcd_reg_init_data->seq[reg].def); - - priv->component = component; - -@@ -883,11 +1009,15 @@ static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = { - SND_SOC_DAPM_MUX("EAR_S", SND_SOC_NOPM, 0, 0, &ear_mux), - SND_SOC_DAPM_SUPPLY("EAR CP", CDC_A_NCP_EN, 4, 0, NULL, 0), - -- SND_SOC_DAPM_PGA("HPHL PA", CDC_A_RX_HPH_CNP_EN, 5, 0, NULL, 0), -+ SND_SOC_DAPM_PGA_E("HPHL PA", CDC_A_RX_HPH_CNP_EN, 5, 0, NULL, 0, -+ pm8916_wcd_analog_enable_hphl_pa, -+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MUX("HPHL", SND_SOC_NOPM, 0, 0, &hphl_mux), - SND_SOC_DAPM_MIXER("HPHL DAC", CDC_A_RX_HPH_L_PA_DAC_CTL, 3, 0, NULL, - 0), -- SND_SOC_DAPM_PGA("HPHR PA", CDC_A_RX_HPH_CNP_EN, 4, 0, NULL, 0), -+ SND_SOC_DAPM_PGA_E("HPHR PA", CDC_A_RX_HPH_CNP_EN, 4, 0, NULL, 0, -+ pm8916_wcd_analog_enable_hphr_pa, -+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MUX("HPHR", SND_SOC_NOPM, 0, 0, &hphr_mux), - SND_SOC_DAPM_MIXER("HPHR DAC", CDC_A_RX_HPH_R_PA_DAC_CTL, 3, 0, NULL, - 0), -@@ -1259,7 +1389,9 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev) - } - - static const struct of_device_id pm8916_wcd_analog_spmi_match_table[] = { -- { .compatible = "qcom,pm8916-wcd-analog-codec", }, -+ { .compatible = "qcom,pm8916-wcd-analog-codec", .data = &pm8916_data }, -+ { .compatible = "qcom,pm8950-wcd-analog-codec", .data = &pm8950_data }, -+ { .compatible = "qcom,pm8953-wcd-analog-codec", .data = &pm8953_data }, - { } - }; - -diff --git a/sound/soc/codecs/mt6357.c b/sound/soc/codecs/mt6357.c ---- a/sound/soc/codecs/mt6357.c -+++ b/sound/soc/codecs/mt6357.c -@@ -1834,8 +1834,8 @@ static int mt6357_platform_driver_probe(struct platform_device *pdev) - } - - static const struct platform_device_id mt6357_platform_ids[] = { -- {"mt6357-sound", 0}, -- { /* sentinel */ }, -+ { .name = "mt6357-sound" }, -+ { /* sentinel */ } - }; - MODULE_DEVICE_TABLE(platform, mt6357_platform_ids); - -diff --git a/sound/soc/codecs/mt6359-accdet.c b/sound/soc/codecs/mt6359-accdet.c ---- a/sound/soc/codecs/mt6359-accdet.c -+++ b/sound/soc/codecs/mt6359-accdet.c -@@ -6,6 +6,7 @@ - // Author: Argus Lin - // - -+#include - #include - #include - #include -@@ -398,14 +399,13 @@ static void mt6359_accdet_work(struct work_struct *work) - struct mt6359_accdet *priv = - container_of(work, struct mt6359_accdet, accdet_work); - -- mutex_lock(&priv->res_lock); -+ guard(mutex)(&priv->res_lock); - priv->pre_accdet_status = priv->accdet_status; - check_jack_btn_type(priv); - - if (priv->jack_plugged && - priv->pre_accdet_status != priv->accdet_status) - mt6359_accdet_jack_report(priv); -- mutex_unlock(&priv->res_lock); - } - - static void mt6359_accdet_jd_work(struct work_struct *work) -@@ -416,7 +416,7 @@ static void mt6359_accdet_jd_work(struct work_struct *work) - struct mt6359_accdet *priv = - container_of(work, struct mt6359_accdet, jd_work); - -- mutex_lock(&priv->res_lock); -+ guard(mutex)(&priv->res_lock); - if (priv->jd_sts == M_PLUG_IN) { - priv->jack_plugged = true; - -@@ -450,7 +450,6 @@ static void mt6359_accdet_jd_work(struct work_struct *work) - - if (priv->caps & ACCDET_PMIC_EINT_IRQ) - recover_eint_setting(priv); -- mutex_unlock(&priv->res_lock); - } - - static irqreturn_t mt6359_accdet_irq(int irq, void *data) -@@ -459,7 +458,7 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) - unsigned int irq_val = 0, val = 0, value = 0; - int ret; - -- mutex_lock(&priv->res_lock); -+ guard(mutex)(&priv->res_lock); - regmap_read(priv->regmap, ACCDET_IRQ_ADDR, &irq_val); - - if (irq_val & ACCDET_IRQ_MASK_SFT) { -@@ -474,7 +473,6 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) - 1000); - if (ret) { - dev_err(priv->dev, "%s(), ret %d\n", __func__, ret); -- mutex_unlock(&priv->res_lock); - return IRQ_NONE; - } - regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, -@@ -498,7 +496,6 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) - if (ret) { - dev_err(priv->dev, "%s(), ret %d\n", __func__, - ret); -- mutex_unlock(&priv->res_lock); - return IRQ_NONE; - } - regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, -@@ -521,7 +518,6 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) - if (ret) { - dev_err(priv->dev, "%s(), ret %d\n", __func__, - ret); -- mutex_unlock(&priv->res_lock); - return IRQ_NONE; - } - regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, -@@ -540,7 +536,6 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) - - queue_work(priv->jd_workqueue, &priv->jd_work); - } -- mutex_unlock(&priv->res_lock); - - return IRQ_HANDLED; - } -diff --git a/sound/soc/codecs/ntpfw.c b/sound/soc/codecs/ntpfw.c ---- a/sound/soc/codecs/ntpfw.c -+++ b/sound/soc/codecs/ntpfw.c -@@ -89,7 +89,7 @@ int ntpfw_load(struct i2c_client *i2c, const char *name, u32 magic) - { - struct device *dev = &i2c->dev; - const struct ntpfw_chunk *chunk; -- const struct firmware *fw; -+ const struct firmware *fw __free(firmware) = NULL; - const u8 *data; - size_t leftover; - int ret; -@@ -101,10 +101,8 @@ int ntpfw_load(struct i2c_client *i2c, const char *name, u32 magic) - return ret; - } - -- if (!ntpfw_verify(dev, fw->data, fw->size, magic)) { -- ret = -EINVAL; -- goto done; -- } -+ if (!ntpfw_verify(dev, fw->data, fw->size, magic)) -+ return -EINVAL; - - data = fw->data + sizeof(struct ntpfw_header); - leftover = fw->size - sizeof(struct ntpfw_header); -@@ -112,23 +110,18 @@ int ntpfw_load(struct i2c_client *i2c, const char *name, u32 magic) - while (leftover) { - chunk = (struct ntpfw_chunk *)data; - -- if (!ntpfw_verify_chunk(dev, chunk, leftover)) { -- ret = -EINVAL; -- goto done; -- } -+ if (!ntpfw_verify_chunk(dev, chunk, leftover)) -+ return -EINVAL; - - ret = ntpfw_send_chunk(i2c, chunk); - if (ret) -- goto done; -+ return ret; - - data += be16_to_cpu(chunk->length) + sizeof(*chunk); - leftover -= be16_to_cpu(chunk->length) + sizeof(*chunk); - } - --done: -- release_firmware(fw); -- -- return ret; -+ return 0; - } - EXPORT_SYMBOL_GPL(ntpfw_load); - -diff --git a/sound/soc/codecs/ntpfw.h b/sound/soc/codecs/ntpfw.h ---- a/sound/soc/codecs/ntpfw.h -+++ b/sound/soc/codecs/ntpfw.h -@@ -1,5 +1,5 @@ - /* SPDX-License-Identifier: GPL-2.0-only */ --/** -+/* - * ntpfw.h - Firmware helper functions for Neofidelity codecs - * - * Copyright (c) 2024, SaluteDevices. All Rights Reserved. -@@ -13,10 +13,11 @@ - /** - * ntpfw_load - load firmware to amplifier over i2c interface. - * -- * @i2c Pointer to amplifier's I2C client. -- * @name Firmware file name. -- * @magic Magic number to validate firmware. -- * @return 0 or error code upon error. -+ * @i2c: Pointer to amplifier's I2C client. -+ * @name: Firmware file name. -+ * @magic: Magic number to validate firmware. -+ * -+ * Returns: 0 or error code upon error. - */ - int ntpfw_load(struct i2c_client *i2c, const char *name, const u32 magic); - -diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c ---- a/sound/soc/codecs/pcm1681.c -+++ b/sound/soc/codecs/pcm1681.c -@@ -199,10 +199,17 @@ static int pcm1681_hw_params(struct snd_pcm_substream *substream, - return pcm1681_set_deemph(component); - } - -+static const u64 pcm1681_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J; -+ - static const struct snd_soc_dai_ops pcm1681_dai_ops = { - .set_fmt = pcm1681_set_dai_fmt, - .hw_params = pcm1681_hw_params, - .mute_stream = pcm1681_mute, -+ .auto_selectable_formats = &pcm1681_selectable_formats, -+ .num_auto_selectable_formats = 1, - .no_capture_mute = 1, - }; - -diff --git a/sound/soc/codecs/pcm1754.c b/sound/soc/codecs/pcm1754.c ---- a/sound/soc/codecs/pcm1754.c -+++ b/sound/soc/codecs/pcm1754.c -@@ -78,10 +78,16 @@ static int pcm1754_mute_stream(struct snd_soc_dai *dai, int mute, int stream) - return 0; - } - -+static const u64 pcm1754_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J; -+ - static const struct snd_soc_dai_ops pcm1754_dai_ops = { - .set_fmt = pcm1754_set_dai_fmt, - .hw_params = pcm1754_hw_params, - .mute_stream = pcm1754_mute_stream, -+ .auto_selectable_formats = &pcm1754_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static const struct snd_soc_dai_driver pcm1754_dai = { -diff --git a/sound/soc/codecs/pcm1789.c b/sound/soc/codecs/pcm1789.c ---- a/sound/soc/codecs/pcm1789.c -+++ b/sound/soc/codecs/pcm1789.c -@@ -164,11 +164,18 @@ static int pcm1789_trigger(struct snd_pcm_substream *substream, int cmd, - return ret; - } - -+static const u64 pcm1789_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J; -+ - static const struct snd_soc_dai_ops pcm1789_dai_ops = { - .set_fmt = pcm1789_set_dai_fmt, - .hw_params = pcm1789_hw_params, - .mute_stream = pcm1789_mute, - .trigger = pcm1789_trigger, -+ .auto_selectable_formats = &pcm1789_selectable_formats, -+ .num_auto_selectable_formats = 1, - .no_capture_mute = 1, - }; - -diff --git a/sound/soc/codecs/pcm179x.c b/sound/soc/codecs/pcm179x.c ---- a/sound/soc/codecs/pcm179x.c -+++ b/sound/soc/codecs/pcm179x.c -@@ -142,10 +142,16 @@ static int pcm179x_hw_params(struct snd_pcm_substream *substream, - return 0; - } - -+static const u64 pcm179x_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J; -+ - static const struct snd_soc_dai_ops pcm179x_dai_ops = { - .set_fmt = pcm179x_set_dai_fmt, - .hw_params = pcm179x_hw_params, - .mute_stream = pcm179x_mute, -+ .auto_selectable_formats = &pcm179x_selectable_formats, -+ .num_auto_selectable_formats = 1, - .no_capture_mute = 1, - }; - -diff --git a/sound/soc/codecs/pcm186x.c b/sound/soc/codecs/pcm186x.c ---- a/sound/soc/codecs/pcm186x.c -+++ b/sound/soc/codecs/pcm186x.c -@@ -473,11 +473,20 @@ static int pcm186x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, - return 0; - } - -+static const u64 pcm186x_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_B | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF; -+ - static const struct snd_soc_dai_ops pcm186x_dai_ops = { - .set_sysclk = pcm186x_set_dai_sysclk, - .set_tdm_slot = pcm186x_set_tdm_slot, - .set_fmt = pcm186x_set_fmt, - .hw_params = pcm186x_hw_params, -+ .auto_selectable_formats = &pcm186x_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static struct snd_soc_dai_driver pcm1863_dai = { -diff --git a/sound/soc/codecs/pcm3060.c b/sound/soc/codecs/pcm3060.c ---- a/sound/soc/codecs/pcm3060.c -+++ b/sound/soc/codecs/pcm3060.c -@@ -164,10 +164,18 @@ static int pcm3060_hw_params(struct snd_pcm_substream *substream, - return 0; - } - -+static const u64 pcm3060_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_NB_NF; -+ - static const struct snd_soc_dai_ops pcm3060_dai_ops = { - .set_sysclk = pcm3060_set_sysclk, - .set_fmt = pcm3060_set_fmt, - .hw_params = pcm3060_hw_params, -+ .auto_selectable_formats = &pcm3060_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - #define PCM3060_DAI_RATES_ADC (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | \ -diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c ---- a/sound/soc/codecs/pcm512x.c -+++ b/sound/soc/codecs/pcm512x.c -@@ -6,7 +6,7 @@ - * Copyright 2014 Linaro Ltd - */ - -- -+#include - #include - #include - #include -@@ -399,10 +399,9 @@ static int pcm512x_digital_playback_switch_get(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); - -- mutex_lock(&pcm512x->mutex); -+ guard(mutex)(&pcm512x->mutex); - ucontrol->value.integer.value[0] = !(pcm512x->mute & 0x4); - ucontrol->value.integer.value[1] = !(pcm512x->mute & 0x2); -- mutex_unlock(&pcm512x->mutex); - - return 0; - } -@@ -414,7 +413,7 @@ static int pcm512x_digital_playback_switch_put(struct snd_kcontrol *kcontrol, - struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); - int ret, changed = 0; - -- mutex_lock(&pcm512x->mutex); -+ guard(mutex)(&pcm512x->mutex); - - if ((pcm512x->mute & 0x4) == (ucontrol->value.integer.value[0] << 2)) { - pcm512x->mute ^= 0x4; -@@ -430,13 +429,10 @@ static int pcm512x_digital_playback_switch_put(struct snd_kcontrol *kcontrol, - if (ret != 0) { - dev_err(component->dev, - "Failed to update digital mute: %d\n", ret); -- mutex_unlock(&pcm512x->mutex); - return ret; - } - } - -- mutex_unlock(&pcm512x->mutex); -- - return changed; - } - -@@ -1465,7 +1461,7 @@ static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) - int ret; - unsigned int mute_det; - -- mutex_lock(&pcm512x->mutex); -+ guard(mutex)(&pcm512x->mutex); - - if (mute) { - pcm512x->mute |= 0x1; -@@ -1475,7 +1471,7 @@ static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) - if (ret != 0) { - dev_err(component->dev, - "Failed to set digital mute: %d\n", ret); -- goto unlock; -+ return ret; - } - - regmap_read_poll_timeout(pcm512x->regmap, -@@ -1488,7 +1484,7 @@ static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) - if (ret != 0) { - dev_err(component->dev, - "Failed to update digital mute: %d\n", ret); -- goto unlock; -+ return ret; - } - - regmap_read_poll_timeout(pcm512x->regmap, -@@ -1499,18 +1495,24 @@ static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) - 200, 10000); - } - --unlock: -- mutex_unlock(&pcm512x->mutex); -- - return ret; - } - -+static const u64 pcm512x_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_B; -+ - static const struct snd_soc_dai_ops pcm512x_dai_ops = { - .startup = pcm512x_dai_startup, - .hw_params = pcm512x_hw_params, - .set_fmt = pcm512x_set_fmt, - .mute_stream = pcm512x_mute, - .set_bclk_ratio = pcm512x_set_bclk_ratio, -+ .auto_selectable_formats = &pcm512x_selectable_formats, -+ .num_auto_selectable_formats = 1, - .no_capture_mute = 1, - }; - -diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c ---- a/sound/soc/codecs/pcm6240.c -+++ b/sound/soc/codecs/pcm6240.c -@@ -12,6 +12,7 @@ - // Author: Shenghao Ding - // - -+#include - #include - #include - #include -@@ -605,7 +606,7 @@ static int pcmdev_get_volsw(struct snd_kcontrol *kcontrol, - unsigned int reg = mc->reg; - unsigned int val; - -- mutex_lock(&pcm_dev->codec_lock); -+ guard(mutex)(&pcm_dev->codec_lock); - - if (pcm_dev->chip_id == PCM1690) { - ret = pcmdev_dev_read(pcm_dev, dev_no, PCM1690_REG_MODE_CTRL, -@@ -613,18 +614,18 @@ static int pcmdev_get_volsw(struct snd_kcontrol *kcontrol, - if (ret) { - dev_err(pcm_dev->dev, "%s: read mode err=%d\n", - __func__, ret); -- goto out; -+ return ret; - } - val &= PCM1690_REG_MODE_CTRL_DAMS_MSK; - /* Set to wide-range mode, before using vol ctrl. */ - if (!val && vol_ctrl_type == PCMDEV_PCM1690_VOL_CTRL) { - ucontrol->value.integer.value[0] = -25500; -- goto out; -+ return ret; - } - /* Set to fine mode, before using fine vol ctrl. */ - if (val && vol_ctrl_type == PCMDEV_PCM1690_FINE_VOL_CTRL) { - ucontrol->value.integer.value[0] = -12750; -- goto out; -+ return ret; - } - } - -@@ -632,15 +633,14 @@ static int pcmdev_get_volsw(struct snd_kcontrol *kcontrol, - if (ret) { - dev_err(pcm_dev->dev, "%s: read err=%d\n", - __func__, ret); -- goto out; -+ return ret; - } - - val = (val >> shift) & mask; - val = (val > max) ? max : val; - val = mc->invert ? max - val : val; - ucontrol->value.integer.value[0] = val; --out: -- mutex_unlock(&pcm_dev->codec_lock); -+ - return ret; - } - -@@ -678,7 +678,7 @@ static int pcmdev_put_volsw(struct snd_kcontrol *kcontrol, - unsigned int val, val_mask; - unsigned int reg = mc->reg; - -- mutex_lock(&pcm_dev->codec_lock); -+ guard(mutex)(&pcm_dev->codec_lock); - val = ucontrol->value.integer.value[0] & mask; - val = (val > max) ? max : val; - val = mc->invert ? max - val : val; -@@ -702,7 +702,7 @@ static int pcmdev_put_volsw(struct snd_kcontrol *kcontrol, - __func__, rc); - else - rc = 1; -- mutex_unlock(&pcm_dev->codec_lock); -+ - return rc; - } - -@@ -1577,10 +1577,10 @@ static int pcmdevice_comp_probe(struct snd_soc_component *comp) - { - struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(comp); - struct i2c_adapter *adap = pcm_dev->client->adapter; -- const struct firmware *fw_entry = NULL; -+ const struct firmware *fw_entry __free(firmware) = NULL; - int ret, i, j; - -- mutex_lock(&pcm_dev->codec_lock); -+ guard(mutex)(&pcm_dev->codec_lock); - - pcm_dev->component = comp; - -@@ -1588,7 +1588,7 @@ static int pcmdevice_comp_probe(struct snd_soc_component *comp) - for (j = 0; j < 2; j++) { - ret = pcmdev_gain_ctrl_add(pcm_dev, i, j); - if (ret < 0) -- goto out; -+ return ret; - } - } - -@@ -1621,21 +1621,17 @@ static int pcmdevice_comp_probe(struct snd_soc_component *comp) - if (ret) { - dev_err(pcm_dev->dev, "%s: request %s err = %d\n", __func__, - pcm_dev->bin_name, ret); -- goto out; -+ return ret; - } - - ret = pcmdev_regbin_ready(fw_entry, pcm_dev); - if (ret) { - dev_err(pcm_dev->dev, "%s: %s parse err = %d\n", __func__, - pcm_dev->bin_name, ret); -- goto out; -+ return ret; - } -- ret = pcmdev_profile_ctrl_add(pcm_dev); --out: -- release_firmware(fw_entry); - -- mutex_unlock(&pcm_dev->codec_lock); -- return ret; -+ return pcmdev_profile_ctrl_add(pcm_dev); - } - - -@@ -1645,9 +1641,8 @@ static void pcmdevice_comp_remove(struct snd_soc_component *codec) - - if (!pcm_dev) - return; -- mutex_lock(&pcm_dev->codec_lock); -+ guard(mutex)(&pcm_dev->codec_lock); - pcmdevice_config_info_remove(pcm_dev); -- mutex_unlock(&pcm_dev->codec_lock); - } - - static const struct snd_soc_dapm_widget pcmdevice_dapm_widgets[] = { -@@ -1890,9 +1885,9 @@ static int pcmdevice_mute(struct snd_soc_dai *dai, int mute, int stream) - else - block_type = PCMDEVICE_BIN_BLK_PRE_POWER_UP; - -- mutex_lock(&pcm_dev->codec_lock); -+ guard(mutex)(&pcm_dev->codec_lock); - pcmdevice_select_cfg_blk(pcm_dev, pcm_dev->cur_conf, block_type); -- mutex_unlock(&pcm_dev->codec_lock); -+ - return 0; - } - -diff --git a/sound/soc/codecs/peb2466.c b/sound/soc/codecs/peb2466.c ---- a/sound/soc/codecs/peb2466.c -+++ b/sound/soc/codecs/peb2466.c -@@ -6,6 +6,7 @@ - // - // Author: Herve Codina - -+#include - #include - #include - #include -@@ -1537,17 +1538,14 @@ static int peb2466_fw_parse(struct snd_soc_component *component, - - static int peb2466_load_coeffs(struct snd_soc_component *component, const char *fw_name) - { -- const struct firmware *fw; -+ const struct firmware *fw __free(firmware) = NULL; - int ret; - - ret = request_firmware(&fw, fw_name, component->dev); - if (ret) - return ret; - -- ret = peb2466_fw_parse(component, fw->data, fw->size); -- release_firmware(fw); -- -- return ret; -+ return peb2466_fw_parse(component, fw->data, fw->size); - } - - static int peb2466_component_probe(struct snd_soc_component *component) -@@ -1704,13 +1702,11 @@ static int peb2466_chip_gpio_update_bits(struct peb2466 *peb2466, unsigned int x - * So, a specific cache value is used. - */ - -- mutex_lock(&peb2466->gpio.lock); -+ guard(mutex)(&peb2466->gpio.lock); - - cache = peb2466_chip_gpio_get_cache(peb2466, xr_reg); -- if (!cache) { -- ret = -EINVAL; -- goto end; -- } -+ if (!cache) -+ return -EINVAL; - - tmp = *cache; - tmp &= ~mask; -@@ -1718,14 +1714,11 @@ static int peb2466_chip_gpio_update_bits(struct peb2466 *peb2466, unsigned int x - - ret = regmap_write(peb2466->regmap, xr_reg, tmp); - if (ret) -- goto end; -+ return ret; - - *cache = tmp; -- ret = 0; - --end: -- mutex_unlock(&peb2466->gpio.lock); -- return ret; -+ return 0; - } - - static int peb2466_chip_gpio_set(struct gpio_chip *c, unsigned int offset, -diff --git a/sound/soc/codecs/pm4125-sdw.c b/sound/soc/codecs/pm4125-sdw.c ---- a/sound/soc/codecs/pm4125-sdw.c -+++ b/sound/soc/codecs/pm4125-sdw.c -@@ -464,10 +464,16 @@ static int __maybe_unused pm4125_sdw_runtime_suspend(struct device *dev) - static int __maybe_unused pm4125_sdw_runtime_resume(struct device *dev) - { - struct pm4125_sdw_priv *priv = dev_get_drvdata(dev); -+ int ret; - - if (priv->regmap) { - regcache_cache_only(priv->regmap, false); -- regcache_sync(priv->regmap); -+ ret = regcache_sync(priv->regmap); -+ if (ret) { -+ regcache_cache_only(priv->regmap, true); -+ regcache_mark_dirty(priv->regmap); -+ return ret; -+ } - } - - return 0; -diff --git a/sound/soc/codecs/pm4125.c b/sound/soc/codecs/pm4125.c ---- a/sound/soc/codecs/pm4125.c -+++ b/sound/soc/codecs/pm4125.c -@@ -5,6 +5,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -1295,7 +1296,7 @@ static const struct irq_domain_ops pm4125_domain_ops = { - - static int pm4125_irq_init(struct pm4125_priv *pm4125, struct device *dev) - { -- pm4125->virq = irq_domain_add_linear(NULL, 1, &pm4125_domain_ops, NULL); -+ pm4125->virq = irq_domain_create_linear(NULL, 1, &pm4125_domain_ops, NULL); - if (!(pm4125->virq)) { - dev_err(dev, "%s: Failed to add IRQ domain\n", __func__); - return -EINVAL; -diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c ---- a/sound/soc/codecs/rt1017-sdca-sdw.c -+++ b/sound/soc/codecs/rt1017-sdca-sdw.c -@@ -784,7 +784,12 @@ static int rt1017_sdca_dev_resume(struct device *dev) - } - - regcache_cache_only(rt1017->regmap, false); -- regcache_sync(rt1017->regmap); -+ ret = regcache_sync(rt1017->regmap); -+ if (ret) { -+ regcache_cache_only(rt1017->regmap, true); -+ regcache_mark_dirty(rt1017->regmap); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c ---- a/sound/soc/codecs/rt1316-sdw.c -+++ b/sound/soc/codecs/rt1316-sdw.c -@@ -756,7 +756,12 @@ static int rt1316_dev_resume(struct device *dev) - } - - regcache_cache_only(rt1316->regmap, false); -- regcache_sync(rt1316->regmap); -+ ret = regcache_sync(rt1316->regmap); -+ if (ret) { -+ regcache_cache_only(rt1316->regmap, true); -+ regcache_mark_dirty(rt1316->regmap); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c ---- a/sound/soc/codecs/rt1318-sdw.c -+++ b/sound/soc/codecs/rt1318-sdw.c -@@ -830,7 +830,12 @@ static int rt1318_dev_resume(struct device *dev) - return ret; - - regcache_cache_only(rt1318->regmap, false); -- regcache_sync(rt1318->regmap); -+ ret = regcache_sync(rt1318->regmap); -+ if (ret) { -+ regcache_cache_only(rt1318->regmap, true); -+ regcache_mark_dirty(rt1318->regmap); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c ---- a/sound/soc/codecs/rt1320-sdw.c -+++ b/sound/soc/codecs/rt1320-sdw.c -@@ -410,6 +410,402 @@ static const struct reg_sequence rt1321_blind_write[] = { - { 0x41001988, 0x03 }, - }; - -+static const struct reg_sequence rt1321_va1_blind_write[] = { -+ { 0x0000c003, 0xf0 }, -+ { 0x0000c01b, 0xfc }, -+ { 0x0000c5c3, 0xf2 }, -+ { 0x0000c5c2, 0x00 }, -+ { 0x0000c5c1, 0x10 }, -+ { 0x0000c5c0, 0x04 }, -+ { 0x0000c5c7, 0x03 }, -+ { 0x0000c5c6, 0x10 }, -+ { 0x0000c526, 0x47 }, -+ { 0x0000c5c4, 0x12 }, -+ { 0x0000c5c5, 0x60 }, -+ { 0x0000c520, 0x10 }, -+ { 0x0000c521, 0x32 }, -+ { 0x0000c5c7, 0x00 }, -+ { 0x0000c5c8, 0x03 }, -+ { 0x0000c5d3, 0x08 }, -+ { 0x0000c5d2, 0x0a }, -+ { 0x0000c5d1, 0x49 }, -+ { 0x0000c5d0, 0x0f }, -+ { 0x0000c580, 0x10 }, -+ { 0x0000c581, 0x32 }, -+ { 0x0000c582, 0x01 }, -+ { 0x0000c682, 0x60 }, -+ { 0x0000c019, 0x10 }, -+ { 0x0000c5f0, 0x01 }, -+ { 0x0000c5f7, 0x22 }, -+ { 0x0000c5f6, 0x22 }, -+ { 0x0000c057, 0x51 }, -+ { 0x0000c054, 0x55 }, -+ { 0x0000c053, 0x55 }, -+ { 0x0000c052, 0x55 }, -+ { 0x0000c051, 0x01 }, -+ { 0x0000c050, 0x15 }, -+ { 0x0000c060, 0x99 }, -+ { 0x0000c030, 0x55 }, -+ { 0x0000c061, 0x55 }, -+ { 0x0000c063, 0x55 }, -+ { 0x0000c065, 0xa5 }, -+ { 0x0000c06b, 0x0a }, -+ { 0x0000ca05, 0xd6 }, -+ { 0x0000ca06, 0x11 }, -+ { 0x0000ca07, 0x1e }, -+ { 0x0000ca25, 0xd6 }, -+ { 0x0000ca26, 0x11 }, -+ { 0x0000ca27, 0x1e }, -+ { 0x0000cd00, 0x05 }, -+ { 0x0000cd81, 0x49 }, -+ { 0x0000cd82, 0x49 }, -+ { 0x0000c604, 0x40 }, -+ { 0x0000c609, 0x40 }, -+ { 0x0000c046, 0xf7 }, -+ { 0x0000c045, 0xff }, -+ { 0x0000c044, 0xff }, -+ { 0x0000c043, 0xff }, -+ { 0x0000c042, 0xff }, -+ { 0x0000c041, 0xff }, -+ { 0x0000c040, 0xff }, -+ { 0x0000c049, 0xff }, -+ { 0x0000c028, 0x3f }, -+ { 0x0000c020, 0x3f }, -+ { 0x0000c032, 0x13 }, -+ { 0x0000c033, 0x01 }, -+ { 0x0000cc10, 0x01 }, -+ { 0x0000dc20, 0x03 }, -+ { 0x0000de03, 0x05 }, -+ { 0x0000dc00, 0x00 }, -+ { 0x0000c700, 0xf0 }, -+ { 0x0000c701, 0x13 }, -+ { 0x0000c900, 0xc3 }, -+ { 0x0000c570, 0x08 }, -+ { 0x0000c086, 0x02 }, -+ { 0x0000c085, 0x7f }, -+ { 0x0000c084, 0x00 }, -+ { 0x0000c081, 0xff }, -+ { 0x0000f084, 0x0f }, -+ { 0x0000f083, 0xff }, -+ { 0x0000f082, 0xff }, -+ { 0x0000f081, 0xff }, -+ { 0x0000f080, 0xff }, -+ { 0x20003003, 0x3f }, -+ { 0x20005818, 0x81 }, -+ { 0x20009018, 0x81 }, -+ { 0x2000301c, 0x81 }, -+ { 0x0000c003, 0xc0 }, -+ { 0x0000c047, 0x80 }, -+ { 0x0000d541, 0x80 }, -+ { 0x0000d487, 0x0b }, -+ { 0x0000d487, 0x3b }, -+ { 0x0000d486, 0xc3 }, -+ { 0x0000d470, 0x89 }, -+ { 0x0000d471, 0x3a }, -+ { 0x0000d472, 0x3d }, -+ { 0x0000d474, 0x11 }, -+ { 0x0000d475, 0x32 }, -+ { 0x0000d476, 0x64 }, -+ { 0x0000d477, 0x10 }, -+ { 0x0000d478, 0xff }, -+ { 0x0000d479, 0x20 }, -+ { 0x0000d47a, 0x10 }, -+ { 0x0000d73c, 0xb7 }, -+ { 0x0000d73d, 0xd7 }, -+ { 0x0000d73e, 0x00 }, -+ { 0x0000d73f, 0x10 }, -+ { 0x1000cd56, 0x00 }, -+ { 0x3fc2dfc0, 0x03 }, -+ { 0x3fc2dfc1, 0x00 }, -+ { 0x3fc2dfc2, 0x00 }, -+ { 0x3fc2dfc3, 0x00 }, -+ { 0x3fc2dfc4, 0x01 }, -+ { 0x3fc2dfc5, 0x00 }, -+ { 0x3fc2dfc6, 0x00 }, -+ { 0x3fc2dfc7, 0x00 }, -+ { 0x3fc2df80, 0x00 }, -+ { 0x3fc2df81, 0x00 }, -+ { 0x3fc2df82, 0x00 }, -+ { 0x3fc2df83, 0x00 }, -+ { 0x0000d541, 0x40 }, -+ { 0x0000d486, 0x43 }, -+ { 0x1000db00, 0x04 }, -+ { 0x1000db01, 0x00 }, -+ { 0x1000db02, 0x10 }, -+ { 0x1000db03, 0x00 }, -+ { 0x1000db04, 0x00 }, -+ { 0x1000db05, 0x45 }, -+ { 0x1000db06, 0x0d }, -+ { 0x1000db07, 0x01 }, -+ { 0x1000db08, 0x00 }, -+ { 0x1000db09, 0x00 }, -+ { 0x1000db0a, 0xbf }, -+ { 0x1000db0b, 0x0b }, -+ { 0x1000db0c, 0x11 }, -+ { 0x1000db0d, 0x00 }, -+ { 0x1000db0e, 0x00 }, -+ { 0x1000db0f, 0x00 }, -+ { 0x1000db10, 0x2c }, -+ { 0x1000db11, 0xfa }, -+ { 0x1000db12, 0x00 }, -+ { 0x1000db13, 0x00 }, -+ { 0x1000db14, 0x09 }, -+ { 0x0000d540, 0x21 }, -+ { 0x0000c570, 0x08 }, -+ { 0x0000d714, 0x17 }, -+ { 0x0000c5c3, 0xf2 }, -+ { 0x0000c5c8, 0x03 }, -+ { 0x20009012, 0x00 }, -+ { 0x0000dd08, 0x17 }, -+ { 0x0000dd09, 0x0e }, -+ { 0x0000dd0a, 0x17 }, -+ { 0x0000dd0b, 0x0e }, -+ { 0x0000c570, 0x08 }, -+ { 0x0000d471, 0x3a }, -+ { 0x0000db00, 0x00 }, -+ { 0x0000db01, 0x00 }, -+ { 0x0000db02, 0x73 }, -+ { 0x0000db03, 0x00 }, -+ { 0x0000db04, 0x00 }, -+ { 0x0000db05, 0x00 }, -+ { 0x0000db06, 0x00 }, -+ { 0x0000db07, 0x00 }, -+ { 0x0000db08, 0x7f }, -+ { 0x0000db09, 0x00 }, -+ { 0x0000db1a, 0x00 }, -+ { 0x0000db1b, 0x00 }, -+ { 0x0000db19, 0x00 }, -+}; -+ -+static const struct reg_sequence rt1321_va2_blind_write[] = { -+ { 0x0000c003, 0xf0 }, -+ { 0x0000c01b, 0xfc }, -+ { 0x0000c5c3, 0xf2 }, -+ { 0x0000c5c2, 0x00 }, -+ { 0x0000c5c1, 0x10 }, -+ { 0x0000c5c0, 0x04 }, -+ { 0x0000c5c7, 0x03 }, -+ { 0x0000c5c6, 0x10 }, -+ { 0x0000c526, 0x47 }, -+ { 0x0000c5c4, 0x12 }, -+ { 0x0000c5c5, 0x60 }, -+ { 0x0000c520, 0x10 }, -+ { 0x0000c521, 0x32 }, -+ { 0x0000c5c7, 0x00 }, -+ { 0x0000c5c8, 0x03 }, -+ { 0x0000c5d3, 0x08 }, -+ { 0x0000c5d2, 0x0a }, -+ { 0x0000c5d1, 0x49 }, -+ { 0x0000c5d0, 0x0f }, -+ { 0x0000c580, 0x10 }, -+ { 0x0000c581, 0x32 }, -+ { 0x0000c582, 0x01 }, -+ { 0x0000c682, 0x60 }, -+ { 0x0000c019, 0x10 }, -+ { 0x0000c5f0, 0x01 }, -+ { 0x0000c5f7, 0x22 }, -+ { 0x0000c5f6, 0x22 }, -+ { 0x0000c057, 0x51 }, -+ { 0x0000c054, 0x55 }, -+ { 0x0000c053, 0x55 }, -+ { 0x0000c052, 0x55 }, -+ { 0x0000c051, 0x01 }, -+ { 0x0000c050, 0x15 }, -+ { 0x0000c060, 0x99 }, -+ { 0x0000c030, 0x55 }, -+ { 0x0000c061, 0x55 }, -+ { 0x0000c063, 0x55 }, -+ { 0x0000c065, 0xa5 }, -+ { 0x0000c06b, 0x0a }, -+ { 0x0000ca05, 0xd6 }, -+ { 0x0000ca06, 0x11 }, -+ { 0x0000ca07, 0x1e }, -+ { 0x0000ca25, 0xd6 }, -+ { 0x0000ca26, 0x11 }, -+ { 0x0000ca27, 0x1e }, -+ { 0x0000cd00, 0x05 }, -+ { 0x0000cd81, 0x49 }, -+ { 0x0000cd82, 0x49 }, -+ { 0x0000c604, 0x40 }, -+ { 0x0000c609, 0x40 }, -+ { 0x0000c046, 0xf7 }, -+ { 0x0000c045, 0xff }, -+ { 0x0000c044, 0xff }, -+ { 0x0000c043, 0xff }, -+ { 0x0000c042, 0xff }, -+ { 0x0000c041, 0xff }, -+ { 0x0000c040, 0xff }, -+ { 0x0000c049, 0xff }, -+ { 0x0000c028, 0x3f }, -+ { 0x0000c020, 0x3f }, -+ { 0x0000c032, 0x13 }, -+ { 0x0000c033, 0x01 }, -+ { 0x0000cc10, 0x01 }, -+ { 0x0000dc20, 0x03 }, -+ { 0x0000de03, 0x05 }, -+ { 0x0000dc00, 0x00 }, -+ { 0x0000c700, 0xf0 }, -+ { 0x0000c701, 0x13 }, -+ { 0x0000c900, 0xc3 }, -+ { 0x0000c570, 0x08 }, -+ { 0x0000c086, 0x02 }, -+ { 0x0000c085, 0x7f }, -+ { 0x0000c084, 0x00 }, -+ { 0x0000c081, 0xff }, -+ { 0x0000f084, 0x0f }, -+ { 0x0000f083, 0xff }, -+ { 0x0000f082, 0xff }, -+ { 0x0000f081, 0xff }, -+ { 0x0000f080, 0xff }, -+ { 0x20003003, 0x3f }, -+ { 0x20005818, 0x81 }, -+ { 0x20009018, 0x81 }, -+ { 0x2000301c, 0x81 }, -+ { 0x0000c003, 0xc0 }, -+ { 0x0000c047, 0x80 }, -+ { 0x0000d541, 0x80 }, -+ { 0x0000d487, 0x0b }, -+ { 0x0000d487, 0x3b }, -+ { 0x0000d486, 0xc3 }, -+ { 0x0000d470, 0x89 }, -+ { 0x0000d471, 0x3a }, -+ { 0x0000d472, 0x3d }, -+ { 0x0000d474, 0x11 }, -+ { 0x0000d475, 0x32 }, -+ { 0x0000d476, 0x64 }, -+ { 0x0000d477, 0x10 }, -+ { 0x0000d478, 0xff }, -+ { 0x0000d479, 0x20 }, -+ { 0x0000d47a, 0x10 }, -+ { 0x10008000, 0x67 }, -+ { 0x10008001, 0x80 }, -+ { 0x10008002, 0x00 }, -+ { 0x10008003, 0x00 }, -+ { 0x1000cd56, 0x00 }, -+ { 0x0000d486, 0x43 }, -+ { 0x3fc2dfc3, 0x00 }, -+ { 0x3fc2dfc2, 0x00 }, -+ { 0x3fc2dfc1, 0x00 }, -+ { 0x3fc2dfc0, 0x03 }, -+ { 0x3fc2dfc7, 0x00 }, -+ { 0x3fc2dfc6, 0x00 }, -+ { 0x3fc2dfc5, 0x00 }, -+ { 0x3fc2dfc4, 0x01 }, -+ { 0x3fc2dfa3, 0x00 }, -+ { 0x3fc2dfa2, 0x00 }, -+ { 0x3fc2dfa1, 0x00 }, -+ { 0x3fc2dfa0, 0x00 }, -+ { 0x3fc2df80, 0x10 }, -+ { 0x3fc2df81, 0x20 }, -+ { 0x3fc2df82, 0x00 }, -+ { 0x3fc2df83, 0x00 }, -+ { 0x3fc2df84, 0x50 }, -+ { 0x3fc2df85, 0x19 }, -+ { 0x3fc2df86, 0x00 }, -+ { 0x3fc2df87, 0x00 }, -+ { 0x3fc2df88, 0x52 }, -+ { 0x3fc2df89, 0x23 }, -+ { 0x3fc2df8a, 0x00 }, -+ { 0x3fc2df8b, 0x00 }, -+ { 0x3fc2df8c, 0xe0 }, -+ { 0x3fc2df8d, 0x2e }, -+ { 0x3fc2df8e, 0x00 }, -+ { 0x3fc2df8f, 0x00 }, -+ { 0x3fc2df90, 0xe0 }, -+ { 0x3fc2df91, 0x2e }, -+ { 0x3fc2df92, 0x00 }, -+ { 0x3fc2df93, 0x00 }, -+ { 0x3fc2df94, 0x01 }, -+ { 0x3fc2df95, 0x08 }, -+ { 0x3fc2df96, 0x00 }, -+ { 0x3fc2df97, 0x00 }, -+ { 0x3fc2df40, 0x80 }, -+ { 0x3fc2df41, 0xbb }, -+ { 0x3fc2df42, 0x00 }, -+ { 0x3fc2df43, 0x00 }, -+ { 0x3fc2df44, 0xc0 }, -+ { 0x3fc2df45, 0x99 }, -+ { 0x3fc2df46, 0x01 }, -+ { 0x3fc2df47, 0x00 }, -+ { 0x3fc2df48, 0x00 }, -+ { 0x3fc2df49, 0x00 }, -+ { 0x3fc2df4a, 0x00 }, -+ { 0x3fc2df4b, 0x00 }, -+ { 0x3fc2df4c, 0x00 }, -+ { 0x3fc2df4d, 0x00 }, -+ { 0x3fc2df4e, 0x00 }, -+ { 0x3fc2df4f, 0x00 }, -+ { 0x3fc2df50, 0x01 }, -+ { 0x3fc2df51, 0x00 }, -+ { 0x3fc2df52, 0x00 }, -+ { 0x3fc2df53, 0x00 }, -+ { 0x3fc2df54, 0x01 }, -+ { 0x3fc2df55, 0x00 }, -+ { 0x3fc2df56, 0x00 }, -+ { 0x3fc2df57, 0x00 }, -+ { 0x3fc2df58, 0x00 }, -+ { 0x3fc2df59, 0x00 }, -+ { 0x3fc2df5a, 0x00 }, -+ { 0x3fc2df5b, 0x00 }, -+ { 0x3fc2df5c, 0x01 }, -+ { 0x3fc2df5d, 0x00 }, -+ { 0x3fc2df5e, 0x00 }, -+ { 0x3fc2df5f, 0x00 }, -+ { 0x3fc2df60, 0x00 }, -+ { 0x3fc2df61, 0x00 }, -+ { 0x3fc2df62, 0x00 }, -+ { 0x3fc2df63, 0x00 }, -+ { 0x3fc2df64, 0x00 }, -+ { 0x3fc2df65, 0x00 }, -+ { 0x3fc2df66, 0x00 }, -+ { 0x3fc2df67, 0x10 }, -+ { 0x3fc2df68, 0x01 }, -+ { 0x3fc2df69, 0x00 }, -+ { 0x3fc2df6a, 0x00 }, -+ { 0x3fc2df6b, 0x00 }, -+ { 0x3fc2df6c, 0x01 }, -+ { 0x3fc2df6d, 0x00 }, -+ { 0x3fc2df6e, 0x00 }, -+ { 0x3fc2df6f, 0x00 }, -+ { 0x3fc2df70, 0x04 }, -+ { 0x3fc2df71, 0x00 }, -+ { 0x3fc2df72, 0x00 }, -+ { 0x3fc2df73, 0x00 }, -+ { 0x3fc2df74, 0x01 }, -+ { 0x3fc2df75, 0x00 }, -+ { 0x3fc2df76, 0x00 }, -+ { 0x3fc2df77, 0x00 }, -+ { 0x1000db00, 0x04 }, -+ { 0x1000db01, 0x00 }, -+ { 0x1000db02, 0x10 }, -+ { 0x1000db03, 0x00 }, -+ { 0x1000db04, 0x00 }, -+ { 0x1000db05, 0x45 }, -+ { 0x1000db06, 0x0d }, -+ { 0x1000db07, 0x01 }, -+ { 0x1000db08, 0x00 }, -+ { 0x1000db09, 0x00 }, -+ { 0x1000db0a, 0xbf }, -+ { 0x1000db0b, 0x0b }, -+ { 0x1000db0c, 0x11 }, -+ { 0x1000db0d, 0x00 }, -+ { 0x1000db0e, 0x00 }, -+ { 0x1000db0f, 0x00 }, -+ { 0x1000db10, 0x2c }, -+ { 0x1000db11, 0xfa }, -+ { 0x1000db12, 0x00 }, -+ { 0x1000db13, 0x00 }, -+ { 0x1000db14, 0x09 }, -+ { 0x0000d540, 0x21 }, -+ { 0x0000d714, 0x17 }, -+ { 0x0000dd0b, 0x0d }, -+ { 0x0000dd0a, 0xff }, -+ { 0x0000dd09, 0x0d }, -+ { 0x0000dd08, 0xff }, -+ { 0x0000c5fb, 0x12 }, -+ { 0x0000c570, 0x08 }, -+}; -+ - static const struct reg_default rt1320_reg_defaults[] = { - { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, -@@ -440,6 +836,7 @@ static const struct reg_default rt1320_mbq_defaults[] = { - static bool rt1320_readable_register(struct device *dev, unsigned int reg) - { - switch (reg) { -+ case 0x004d: - case 0xc000 ... 0xc086: - case 0xc400 ... 0xc409: - case 0xc480 ... 0xc48f: -@@ -497,10 +894,15 @@ static bool rt1320_readable_register(struct device *dev, unsigned int reg) - case RT1321_PATCH_MAIN_VER ... RT1321_PATCH_BETA_VER: - case 0x1000f008: - case 0x1000f021: -- case 0x2000300f: -+ case 0x20003000 ... 0x2000300f: - case 0x2000301c: -- case 0x2000900f: -+ case 0x20003040 ... 0x20003059: -+ case 0x20005800 ... 0x2000580f: -+ case 0x20005818: -+ case 0x20005840 ... 0x20005859: -+ case 0x20009000 ... 0x2000900f: - case 0x20009018: -+ case 0x20009040 ... 0x20009059: - case 0x3fc000c0 ... 0x3fc2dfc8: - case 0x3fe00000 ... 0x3fe36fff: - /* 0x40801508/0x40801809/0x4080180a/0x40801909/0x4080190a */ -@@ -545,6 +947,7 @@ static bool rt1320_readable_register(struct device *dev, unsigned int reg) - static bool rt1320_volatile_register(struct device *dev, unsigned int reg) - { - switch (reg) { -+ case 0x004d: - case 0xc000: - case 0xc003: - case 0xc081: -@@ -596,15 +999,20 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg) - case 0x1000c000 ... 0x1000dfff: - case 0x1000f008: - case 0x1000f021: -- case 0x2000300f: -+ case 0x2000300e ... 0x2000300f: - case 0x2000301c: -- case 0x2000900f: -+ case 0x20003040 ... 0x20003059: -+ case 0x2000580e ... 0x2000580f: -+ case 0x20005818: -+ case 0x20005840 ... 0x20005859: -+ case 0x2000900e ... 0x2000900f: - case 0x20009018: -+ case 0x20009040 ... 0x20009059: - case 0x3fc2ab80 ... 0x3fc2ac4c: - case 0x3fc2b780: - case 0x3fc2bf80 ... 0x3fc2bf83: - case 0x3fc2bfc0 ... 0x3fc2bfc8: -- case 0x3fc2d300 ... 0x3fc2d354: -+ case 0x3fc2d300 ... 0x3fc2d3cc: - case 0x3fc2dfc0 ... 0x3fc2dfc8: - case 0x3fe2e000 ... 0x3fe2e003: - case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0): -@@ -892,6 +1300,42 @@ static int rt1320_check_fw_ready(struct rt1320_sdw_priv *rt1320) - return 0; - } - -+static int rt1320_dspfw_status(struct rt1320_sdw_priv *rt1320) -+{ -+ struct device *dev = &rt1320->sdw_slave->dev; -+ unsigned int fw_status_addr, fw_ready; -+ unsigned int dspfw_run; -+ -+ switch (rt1320->dev_id) { -+ case RT1320_DEV_ID: -+ fw_status_addr = RT1320_DSPFW_STATUS_ADDR; -+ break; -+ case RT1321_DEV_ID: -+ fw_status_addr = RT1321_DSPFW_STATUS_ADDR; -+ break; -+ default: -+ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); -+ return -EINVAL; -+ } -+ -+ regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); -+ fw_ready &= 0x1; -+ -+ if (rt1320->dev_id == RT1321_DEV_ID) { -+ regmap_read(rt1320->regmap, 0xf01e, &dspfw_run); -+ dspfw_run &= 0x1; -+ fw_ready = (!dspfw_run && fw_ready); -+ } -+ -+ if (fw_ready) { -+ dev_dbg(dev, "%s, DSP FW was already\n", __func__); -+ return 1; -+ } -+ -+ dev_dbg(dev, "%s, DSP FW is NOT ready. Please load DSP FW first\n", __func__); -+ return 0; -+} -+ - static int rt1320_check_power_state_ready(struct rt1320_sdw_priv *rt1320, enum rt1320_power_state ps) - { - struct device *dev = &rt1320->sdw_slave->dev; -@@ -982,6 +1426,9 @@ static int rt1320_fw_param_protocol(struct rt1320_sdw_priv *rt1320, enum rt1320_ - if (!tempbuf) - return -ENOMEM; - -+ if (rt1320->dev_id == RT1321_DEV_ID && rt1320->version_id == RT1321_VA2) -+ paramid += 0x0bff0000; -+ - paramhr.moudleid = 1; - paramhr.commandtype = cmdid; - /* 8 is "sizeof(paramid) + sizeof(paramlength)" */ -@@ -1120,45 +1567,73 @@ static void rt1320_calc_r0(struct rt1320_sdw_priv *rt1320) - l_calir0, l_calir0_lo, r_calir0, r_calir0_lo); - } - -+static int rt1320_pilot_tone_output(struct rt1320_sdw_priv *rt1320) -+{ -+ struct device *dev = &rt1320->sdw_slave->dev; -+ int l_targetpostgain, r_targetpostgain; -+ unsigned long long factor = (1 << 12); -+ int l_pilotgain[9], r_pilotgain[9]; -+ const int postgain_step = 234; -+ int targetGain; -+ -+ switch (rt1320->dev_id) { -+ case RT1320_DEV_ID: -+ targetGain = -320000; -+ break; -+ case RT1321_DEV_ID: -+ targetGain = -420000; -+ if (rt1320->version_id == RT1321_VA0) -+ targetGain = -320000; -+ break; -+ default: -+ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); -+ return -EINVAL; -+ } -+ -+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 70, &l_pilotgain[0], sizeof(l_pilotgain)); -+ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 71, &r_pilotgain[0], sizeof(r_pilotgain)); -+ dev_dbg(dev, "%s, LR pilotgain %d, %d\n", __func__, l_pilotgain[2], r_pilotgain[2]); -+ -+ /* calculate pilot tone gain */ -+ l_pilotgain[2] = (l_pilotgain[2] * 10000) / factor; -+ r_pilotgain[2] = (r_pilotgain[2] * 10000) / factor; -+ -+ /* calculate post gain to meet target gain */ -+ l_targetpostgain = abs(targetGain - l_pilotgain[2]) / postgain_step; -+ r_targetpostgain = abs(targetGain - r_pilotgain[2]) / postgain_step; -+ l_targetpostgain = 0xfff - l_targetpostgain; -+ r_targetpostgain = 0xfff - r_targetpostgain; -+ dev_dbg(dev, "%s, LR targetpostgain=0x%x, 0x%x\n", __func__, l_targetpostgain, r_targetpostgain); -+ -+ regmap_write(rt1320->regmap, 0xdd0b, (l_targetpostgain & 0xf00) >> 8); -+ regmap_write(rt1320->regmap, 0xdd0a, l_targetpostgain & 0xff); -+ regmap_write(rt1320->regmap, 0xdd09, (r_targetpostgain & 0xf00) >> 8); -+ regmap_write(rt1320->regmap, 0xdd08, r_targetpostgain & 0xff); -+ -+ return 0; -+} -+ - static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) - { - struct device *dev = &rt1320->sdw_slave->dev; - struct rt1320_datafixpoint audfixpoint[2]; - unsigned int reg_c5fb, reg_c570, reg_cd00; -- unsigned int vol_reg[4], fw_ready; -+ unsigned int vol_reg[4]; - unsigned long long l_meanr0, r_meanr0; -- unsigned int fw_status_addr; - int l_re[5], r_re[5]; - int ret, tmp; - unsigned long long factor = (1 << 27); - unsigned short l_advancegain, r_advancegain; - unsigned int delay_s = 7; /* delay seconds for the calibration */ -+ int dspfw_status; - - if (!rt1320->component) - return; - -- switch (rt1320->dev_id) { -- case RT1320_DEV_ID: -- fw_status_addr = RT1320_DSPFW_STATUS_ADDR; -- break; -- case RT1321_DEV_ID: -- fw_status_addr = RT1321_DSPFW_STATUS_ADDR; -- break; -- default: -- dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); -- return; -- } -- -- /* set volume 0dB */ - regmap_read(rt1320->regmap, 0xdd0b, &vol_reg[3]); - regmap_read(rt1320->regmap, 0xdd0a, &vol_reg[2]); - regmap_read(rt1320->regmap, 0xdd09, &vol_reg[1]); - regmap_read(rt1320->regmap, 0xdd08, &vol_reg[0]); -- regmap_write(rt1320->regmap, 0xdd0b, 0x0f); -- regmap_write(rt1320->regmap, 0xdd0a, 0xff); -- regmap_write(rt1320->regmap, 0xdd09, 0x0f); -- regmap_write(rt1320->regmap, 0xdd08, 0xff); -- - regmap_read(rt1320->regmap, 0xc5fb, ®_c5fb); - regmap_read(rt1320->regmap, 0xc570, ®_c570); - regmap_read(rt1320->regmap, 0xcd00, ®_cd00); -@@ -1171,9 +1646,8 @@ static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) - goto _finish_; - } - -- regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); -- fw_ready &= 0x1; -- if (!fw_ready) { -+ dspfw_status = rt1320_dspfw_status(rt1320); -+ if (dspfw_status <= 0) { - dev_dbg(dev, "%s, DSP FW is NOT ready. Please load DSP FW first\n", __func__); - goto _finish_; - } -@@ -1184,8 +1658,19 @@ static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) - goto _finish_; - } - -- if (rt1320->dev_id == RT1320_DEV_ID) -- regmap_write(rt1320->regmap, 0xc5fb, 0x00); -+ /* fine tune pilot tone output */ -+ ret = rt1320_pilot_tone_output(rt1320); -+ if (ret < 0) { -+ dev_dbg(dev, "%s, Failed to tune pilot tone output\n", __func__); -+ goto _finish_; -+ } -+ -+ if (rt1320->dev_id == RT1321_DEV_ID) { -+ regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x00); -+ regmap_write(rt1320->regmap, 0xc5c4, 0x12); -+ } -+ -+ regmap_write(rt1320->regmap, 0xc5fb, 0x00); - regmap_write(rt1320->regmap, 0xc570, 0x0b); - regmap_write(rt1320->regmap, 0xcd00, 0xc5); - -@@ -1246,6 +1731,11 @@ static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) - SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03); - rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x03); - -+ if (rt1320->dev_id == RT1321_DEV_ID) { -+ regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x80); -+ regmap_write(rt1320->regmap, 0xc5c4, 0x10); -+ } -+ - /* advance gain will be set when R0 load, not here */ - regmap_write(rt1320->regmap, 0xdd0b, vol_reg[3]); - regmap_write(rt1320->regmap, 0xdd0a, vol_reg[2]); -@@ -1296,7 +1786,7 @@ static int rt1320_r0_cali_put(struct snd_kcontrol *kcontrol, - static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320) - { - struct sdw_slave *slave = rt1320->sdw_slave; -- const struct firmware *patch; -+ const struct firmware *patch __free(firmware) = NULL; - const char *filename; - unsigned int addr, val, min_addr, max_addr; - const unsigned char *ptr; -@@ -1312,6 +1802,9 @@ static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320) - max_addr = 0x10007fff; - break; - case RT1321_DEV_ID: -+ if (rt1320->version_id == RT1321_VA2) -+ return; -+ - filename = RT1321_VA_MCU_PATCH; - min_addr = 0x10008000; - max_addr = 0x10008fff; -@@ -1347,17 +1840,15 @@ static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320) - - if (addr > max_addr || addr < min_addr) { - dev_err(&slave->dev, "%s: the address 0x%x is wrong", __func__, addr); -- goto _exit_; -+ return; - } - if (val > 0xff) { - dev_err(&slave->dev, "%s: the value 0x%x is wrong", __func__, val); -- goto _exit_; -+ return; - } - regmap_write(rt1320->regmap, addr, val); - } - } --_exit_: -- release_firmware(patch); - } - } - -@@ -1382,30 +1873,17 @@ static void rt1320_vab_preset(struct rt1320_sdw_priv *rt1320) - static void rt1320_t0_load(struct rt1320_sdw_priv *rt1320, unsigned int l_t0, unsigned int r_t0) - { - struct device *dev = &rt1320->sdw_slave->dev; -- unsigned int factor = (1 << 22), fw_ready; -+ unsigned int factor = (1 << 22); - int l_t0_data[38], r_t0_data[38]; -- unsigned int fw_status_addr; -- -- switch (rt1320->dev_id) { -- case RT1320_DEV_ID: -- fw_status_addr = RT1320_DSPFW_STATUS_ADDR; -- break; -- case RT1321_DEV_ID: -- fw_status_addr = RT1321_DSPFW_STATUS_ADDR; -- break; -- default: -- dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); -- return; -- } -+ int dspfw_status; - - regmap_write(rt1320->regmap, - SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, - RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00); - rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00); - -- regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); -- fw_ready &= 0x1; -- if (!fw_ready) { -+ dspfw_status = rt1320_dspfw_status(rt1320); -+ if (dspfw_status <= 0) { - dev_warn(dev, "%s, DSP FW is NOT ready\n", __func__); - goto _exit_; - } -@@ -1444,7 +1922,7 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) - struct device *dev = &rt1320->sdw_slave->dev; - static const char func_tag[] = "FUNC"; - static const char xu_tag[] = "XU"; -- const struct firmware *rae_fw = NULL; -+ const struct firmware *rae_fw __free(firmware) = NULL; - unsigned int fw_offset; - unsigned char *fw_data; - unsigned char *param_data; -@@ -1483,22 +1961,46 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) - request_firmware(&rae_fw, rae_filename, dev); - if (rae_fw) { - -- /* RAE CRC clear */ -- regmap_write(rt1320->regmap, 0xe80b, 0x0f); -- -- /* RAE stop & CRC disable */ -- regmap_update_bits(rt1320->regmap, 0xe803, 0xbc, 0x00); -- -- while (--retry) { -- regmap_read(rt1320->regmap, 0xe83f, &value); -- if (value & 0x40) -- break; -- usleep_range(1000, 1100); -- } -- if (!retry && !(value & 0x40)) { -- dev_err(dev, "%s: RAE is not ready to load\n", __func__); -- release_firmware(rae_fw); -- return -ETIMEDOUT; -+ switch (rt1320->dev_id) { -+ case RT1320_DEV_ID: -+ /* RAE CRC clear */ -+ regmap_write(rt1320->regmap, 0xe80b, 0x0f); -+ /* RAE stop & CRC disable */ -+ regmap_update_bits(rt1320->regmap, 0xe803, 0xbc, 0x00); -+ while (--retry) { -+ regmap_read(rt1320->regmap, 0xe83f, &value); -+ if (value & 0x40) -+ break; -+ usleep_range(1000, 1100); -+ } -+ if (!retry && !(value & 0x40)) { -+ dev_err(dev, "%s: RAE is not ready to load\n", __func__); -+ return -ETIMEDOUT; -+ } -+ break; -+ case RT1321_DEV_ID: -+ /* RAE CRC clear */ -+ regmap_write(rt1320->regmap, 0x2000300e, 0xc0); -+ regmap_write(rt1320->regmap, 0x2000300f, 0x0f); -+ /* RAE stop & Phase sync & CRC disable */ -+ regmap_update_bits(rt1320->regmap, 0x20003003, 0xfe, 0x00); -+ regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x00); -+ regmap_update_bits(rt1320->regmap, 0x2000301c, 0x01, 0x00); -+ /* check whether write state is ready */ -+ while (--retry) { -+ regmap_read(rt1320->regmap, 0x20003043, &value); -+ if (value & 0x40) -+ break; -+ usleep_range(1000, 1100); -+ } -+ if (!retry && !(value & 0x40)) { -+ dev_err(dev, "%s: RAE is not ready to load\n", __func__); -+ return -ETIMEDOUT; -+ } -+ break; -+ default: -+ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); -+ return -EINVAL; - } - - dev_dbg(dev, "%s, rae_fw size=0x%zx\n", __func__, rae_fw->size); -@@ -1551,7 +2053,6 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) - } - - regcache_cache_bypass(rt1320->regmap, false); -- release_firmware(rae_fw); - - } else { - dev_err(dev, "%s: Failed to load %s firmware\n", __func__, rae_filename); -@@ -1559,18 +2060,34 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) - goto _exit_; - } - -- /* RAE CRC enable */ -- regmap_update_bits(rt1320->regmap, 0xe803, 0x0c, 0x0c); -- -- /* RAE update */ -- regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x00); -- regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x80); -- -- /* RAE run */ -- regmap_update_bits(rt1320->regmap, 0xe803, 0x80, 0x80); -- -- regmap_read(rt1320->regmap, 0xe80b, &value); -- dev_dbg(dev, "%s: CAE run => 0xe80b reg = 0x%x\n", __func__, value); -+ switch (rt1320->dev_id) { -+ case RT1320_DEV_ID: -+ /* RAE CRC enable */ -+ regmap_update_bits(rt1320->regmap, 0xe803, 0x0c, 0x0c); -+ /* RAE update */ -+ regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x00); -+ regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x80); -+ /* RAE run */ -+ regmap_update_bits(rt1320->regmap, 0xe803, 0x80, 0x80); -+ regmap_read(rt1320->regmap, 0xe80b, &value); -+ dev_dbg(dev, "%s: CAE run => 0xe80b reg = 0x%x\n", __func__, value); -+ break; -+ case RT1321_DEV_ID: -+ /* RAE CRC enable */ -+ regmap_update_bits(rt1320->regmap, 0x20003003, 0x30, 0x30); -+ /* RAE update */ -+ regmap_update_bits(rt1320->regmap, 0x2000301c, 0x80, 0x00); -+ regmap_update_bits(rt1320->regmap, 0x2000301c, 0x80, 0x80); -+ regmap_update_bits(rt1320->regmap, 0x20009018, 0x80, 0x00); -+ regmap_update_bits(rt1320->regmap, 0x20009018, 0x80, 0x80); -+ regmap_update_bits(rt1320->regmap, 0x20005818, 0x80, 0x00); -+ regmap_update_bits(rt1320->regmap, 0x20005818, 0x80, 0x80); -+ /* RAE run */ -+ regmap_update_bits(rt1320->regmap, 0x2000301c, 0x01, 0x01); -+ /* Phase sync eanble */ -+ regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x80); -+ break; -+ } - - rt1320->rae_update_done = true; - -@@ -1598,32 +2115,21 @@ struct rt1320_dspfwheader { - - struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt1320->component); - struct device *dev = &rt1320->sdw_slave->dev; -- unsigned int val, i, fw_offset, fw_ready; -- unsigned int fw_status_addr; -+ unsigned int val, i, fw_offset; - struct rt1320_dspfwheader *fwheader; - struct rt1320_imageinfo *ptr_img; - struct sdw_bpt_section sec[10]; -- const struct firmware *fw = NULL; -+ const struct firmware *fw __free(firmware) = NULL; - unsigned char *fw_data; - bool dev_fw_match = false; - static const char hdr_sig[] = "AFX"; - unsigned int hdr_size = 0; - const char *dmi_vendor, *dmi_product, *dmi_sku; - int len_vendor, len_product, len_sku; -+ unsigned char boot_mode = 0; /* 0: from RAM; 1: from ROM */ -+ unsigned char has_0x3fc00000 = 0; - char filename[512]; - -- switch (rt1320->dev_id) { -- case RT1320_DEV_ID: -- fw_status_addr = RT1320_DSPFW_STATUS_ADDR; -- break; -- case RT1321_DEV_ID: -- fw_status_addr = RT1321_DSPFW_STATUS_ADDR; -- break; -- default: -- dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); -- return; -- } -- - dmi_vendor = dmi_get_system_info(DMI_SYS_VENDOR); - dmi_product = dmi_get_system_info(DMI_PRODUCT_NAME); - dmi_sku = dmi_get_system_info(DMI_PRODUCT_SKU); -@@ -1653,17 +2159,12 @@ struct rt1320_dspfwheader { - RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00); - rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00); - -- regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); -- fw_ready &= 0x1; -- if (fw_ready) { -+ if (rt1320_dspfw_status(rt1320)) { - dev_dbg(dev, "%s, DSP FW was already\n", __func__); - rt1320->fw_load_done = true; - goto _exit_; - } - -- /* change to IRAM */ -- regmap_update_bits(rt1320->regmap, 0xf01e, 0x80, 0x00); -- - request_firmware(&fw, filename, dev); - if (fw) { - fwheader = (struct rt1320_dspfwheader *)fw->data; -@@ -1672,7 +2173,6 @@ struct rt1320_dspfwheader { - - if (fwheader->sync != 0x0a1c5679) { - dev_err(dev, "%s: FW sync error\n", __func__); -- release_firmware(fw); - goto _exit_; - } - -@@ -1709,9 +2209,11 @@ struct rt1320_dspfwheader { - dev_fw_match = true; - break; - case RT1321_DEV_ID: -- if (ptr_img->addr == 0x3fc00000) -- if (fw_data[9] == '1') -+ if (ptr_img->addr == 0x3fc00000) { -+ if (fw_data[7] == '1') - dev_fw_match = true; -+ has_0x3fc00000 = 1; -+ } - break; - default: - dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); -@@ -1721,6 +2223,22 @@ struct rt1320_dspfwheader { - fw_offset += ptr_img->size; - } - -+ if (rt1320->dev_id == RT1321_DEV_ID && rt1320->version_id == RT1321_VA2) { -+ /* -+ * For 1321 VA2, if the FW doesn't include the section for address 0x3fc00000, -+ * it means the FW will boot from ROM and force dev_fw_match to true to download FW by BRA. -+ */ -+ if (!has_0x3fc00000) { -+ boot_mode = 1; -+ dev_fw_match = true; -+ } -+ dev_dbg(dev, "%s: Boot from %s for VA2\n", __func__, (boot_mode ? "ROM" : "RAM")); -+ } -+ -+ /* change to IRAM */ -+ if (!boot_mode) -+ regmap_update_bits(rt1320->regmap, 0xf01e, 0x80, 0x00); -+ - if (dev_fw_match) { - dev_dbg(dev, "%s, starting BRA downloading FW..\n", __func__); - rt1320->bra_msg.dev_num = rt1320->sdw_slave->dev_num; -@@ -1732,7 +2250,6 @@ struct rt1320_dspfwheader { - } - - regcache_cache_bypass(rt1320->regmap, false); -- release_firmware(fw); - - if (!dev_fw_match) { - dev_err(dev, "%s: FW file doesn't match to device\n", __func__); -@@ -1743,24 +2260,39 @@ struct rt1320_dspfwheader { - goto _exit_; - } - -- /* run RAM code */ -- regmap_read(rt1320->regmap, 0x3fc2bfc0, &val); -- val |= 0x8; -- regmap_write(rt1320->regmap, 0x3fc2bfc0, val); -- -- /* clear frame counter */ - switch (rt1320->dev_id) { - case RT1320_DEV_ID: -+ /* run RAM code */ -+ regmap_read(rt1320->regmap, 0x3fc2bfc0, &val); -+ val |= 0x8; -+ regmap_write(rt1320->regmap, 0x3fc2bfc0, val); -+ /* clear frame counter */ - regmap_write(rt1320->regmap, 0x3fc2bfcb, 0x00); - regmap_write(rt1320->regmap, 0x3fc2bfca, 0x00); - regmap_write(rt1320->regmap, 0x3fc2bfc9, 0x00); - regmap_write(rt1320->regmap, 0x3fc2bfc8, 0x00); - break; - case RT1321_DEV_ID: -+ if (!boot_mode) { -+ /* run RAM code */ -+ regmap_read(rt1320->regmap, 0x3fc2dfc0, &val); -+ val |= 0x8; -+ regmap_write(rt1320->regmap, 0x3fc2dfc0, val); -+ } -+ /* clear frame counter */ - regmap_write(rt1320->regmap, 0x3fc2dfcb, 0x00); - regmap_write(rt1320->regmap, 0x3fc2dfca, 0x00); - regmap_write(rt1320->regmap, 0x3fc2dfc9, 0x00); - regmap_write(rt1320->regmap, 0x3fc2dfc8, 0x00); -+ /* enable handshake */ -+ regmap_write(rt1320->regmap, 0x3fc2dfc4, 0x00); -+ regmap_write(rt1320->regmap, 0xd470, 0xad); -+ /* minimum phase settings */ -+ regmap_write(rt1320->regmap, 0xc5c4, 0x10); -+ regmap_write(rt1320->regmap, 0x20003003, 0x31); -+ regmap_update_bits(rt1320->regmap, 0x20003002, 0x40, 0x00); -+ regmap_write(rt1320->regmap, 0xc5b3, 0x01); -+ regmap_write(rt1320->regmap, 0xc052, 0x11); - break; - } - -@@ -1843,14 +2375,35 @@ static void rt1320_vc_preset(struct rt1320_sdw_priv *rt1320) - - static void rt1321_preset(struct rt1320_sdw_priv *rt1320) - { -+ const struct reg_sequence *blindwrite; - unsigned int i, reg, val, delay; -+ unsigned int array_size; - -- for (i = 0; i < ARRAY_SIZE(rt1321_blind_write); i++) { -- reg = rt1321_blind_write[i].reg; -- val = rt1321_blind_write[i].def; -- delay = rt1321_blind_write[i].delay_us; -+ switch (rt1320->version_id) { -+ case RT1321_VA0: -+ blindwrite = rt1321_blind_write; -+ array_size = ARRAY_SIZE(rt1321_blind_write); -+ break; -+ case RT1321_VA1: -+ blindwrite = rt1321_va1_blind_write; -+ array_size = ARRAY_SIZE(rt1321_va1_blind_write); -+ break; -+ case RT1321_VA2: -+ blindwrite = rt1321_va2_blind_write; -+ array_size = ARRAY_SIZE(rt1321_va2_blind_write); -+ break; -+ default: -+ dev_err(&rt1320->sdw_slave->dev, "%s: Unknown version ID %d\n", -+ __func__, rt1320->version_id); -+ return; -+ } - -- if (reg == 0x3fc2dfc3) -+ for (i = 0; i < array_size; i++) { -+ reg = blindwrite[i].reg; -+ val = blindwrite[i].def; -+ delay = blindwrite[i].delay_us; -+ -+ if (reg == 0x1000cd56) - rt1320_load_mcu_patch(rt1320); - - regmap_write(rt1320->regmap, reg, val); -@@ -1892,6 +2445,24 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave) - regmap_read(rt1320->regmap, RT1320_DEV_ID_0, &val); - regmap_read(rt1320->regmap, RT1320_DEV_ID_1, &tmp); - rt1320->dev_id = (val << 8) | tmp; -+ -+ /* This is a workaround that reads the value twice to obtain the correct result. */ -+ rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_0, &val); -+ rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_1, &tmp); -+ rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_0, &val); -+ rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_1, &tmp); -+ val = (val << 8) | tmp; -+ -+ if (rt1320->dev_id == RT1321_DEV_ID) { -+ if (rt1320->version_id == 0x01) -+ rt1320->version_id = RT1321_VA2; -+ else if (val == RT1321_DEV_HV_VA0_ID) -+ rt1320->version_id = RT1321_VA0; -+ else if (val == RT1321_DEV_HV_VA1_ID) -+ rt1320->version_id = RT1321_VA1; -+ else -+ dev_err(dev, "%s: Unknown version ID 0x%x for RT1321\n", __func__, rt1320->version_id); -+ } - } - - regmap_read(rt1320->regmap, -@@ -2326,25 +2897,12 @@ static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); - static int rt1320_r0_load(struct rt1320_sdw_priv *rt1320) - { - struct device *dev = regmap_get_device(rt1320->regmap); -- unsigned int fw_status_addr; -- unsigned int fw_ready; -+ int dspfw_status; - int ret = 0; - - if (!rt1320->r0_l_reg || !rt1320->r0_r_reg) - return -EINVAL; - -- switch (rt1320->dev_id) { -- case RT1320_DEV_ID: -- fw_status_addr = RT1320_DSPFW_STATUS_ADDR; -- break; -- case RT1321_DEV_ID: -- fw_status_addr = RT1321_DSPFW_STATUS_ADDR; -- break; -- default: -- dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); -- return -EINVAL; -- } -- - regmap_write(rt1320->regmap, - SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00); - ret = rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00); -@@ -2353,9 +2911,8 @@ static int rt1320_r0_load(struct rt1320_sdw_priv *rt1320) - goto _timeout_; - } - -- regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); -- fw_ready &= 0x1; -- if (!fw_ready) { -+ dspfw_status = rt1320_dspfw_status(rt1320); -+ if (dspfw_status <= 0) { - dev_dbg(dev, "%s, DSP FW is NOT ready\n", __func__); - goto _timeout_; - } -@@ -2458,7 +3015,7 @@ static int rt1320_dspfw_load_put(struct snd_kcontrol *kcontrol, - if (!rt1320->hw_init) - return 0; - -- ret = pm_runtime_resume(component->dev); -+ ret = pm_runtime_resume_and_get(component->dev); - if (ret < 0 && ret != -EACCES) - return ret; - -@@ -2469,6 +3026,8 @@ static int rt1320_dspfw_load_put(struct snd_kcontrol *kcontrol, - if (!ucontrol->value.integer.value[0]) - rt1320->fw_load_done = false; - -+ pm_runtime_mark_last_busy(component->dev); -+ pm_runtime_put_autosuspend(component->dev); - return 0; - } - -@@ -2819,6 +3378,37 @@ static int rt1320_sdw_pcm_hw_free(struct snd_pcm_substream *substream, - return 0; - } - -+static int rt1320_bus_config(struct sdw_slave *slave, -+ struct sdw_bus_params *params) -+{ -+ struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(&slave->dev); -+ unsigned int clk_base; -+ -+ regmap_read(rt1320->regmap, 0x004d, &clk_base); -+ dev_dbg(&rt1320->sdw_slave->dev, "%s clk_base=%x", __func__, clk_base); -+ -+ switch (clk_base) { -+ case RT1320_CLK_FREQ_19_2_MHZ: -+ case RT1320_CLK_FREQ_24MHZ: -+ regmap_write(rt1320->regmap, 0xc600, 0x04); -+ regmap_write(rt1320->regmap, 0xc601, 0x83); -+ regmap_write(rt1320->regmap, 0xc602, 0x1f); -+ regmap_write(rt1320->regmap, 0xc603, 0x40); -+ break; -+ case RT1320_CLK_FREQ_24_576MHZ: -+ case RT1320_CLK_FREQ_22_5792MHZ: -+ regmap_write(rt1320->regmap, 0xc600, 0x07); -+ regmap_write(rt1320->regmap, 0xc601, 0x80); -+ regmap_write(rt1320->regmap, 0xc602, 0x0f); -+ regmap_write(rt1320->regmap, 0xc603, 0x40); -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ - /* - * slave_ops: callbacks for get_clock_stop_mode, clock_stop and - * port_prep are not defined for now -@@ -2826,12 +3416,13 @@ static int rt1320_sdw_pcm_hw_free(struct snd_pcm_substream *substream, - static const struct sdw_slave_ops rt1320_slave_ops = { - .read_prop = rt1320_read_prop, - .update_status = rt1320_update_status, -+ .bus_config = rt1320_bus_config, - }; - - static int rt1320_sdw_component_probe(struct snd_soc_component *component) - { -- int ret; - struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); -+ int ret; - - rt1320->component = component; - -@@ -3062,10 +3653,23 @@ static int rt1320_dev_resume(struct device *dev) - return ret; - - regcache_cache_only(rt1320->regmap, false); -- regcache_sync(rt1320->regmap); -+ ret = regcache_sync(rt1320->regmap); -+ if (ret) -+ goto err_sync; -+ - regcache_cache_only(rt1320->mbq_regmap, false); -- regcache_sync(rt1320->mbq_regmap); -+ ret = regcache_sync(rt1320->mbq_regmap); -+ if (ret) -+ goto err_sync; -+ - return 0; -+ -+err_sync: -+ regcache_cache_only(rt1320->regmap, true); -+ regcache_cache_only(rt1320->mbq_regmap, true); -+ regcache_mark_dirty(rt1320->regmap); -+ regcache_mark_dirty(rt1320->mbq_regmap); -+ return ret; - } - - static const struct dev_pm_ops rt1320_pm = { -diff --git a/sound/soc/codecs/rt1320-sdw.h b/sound/soc/codecs/rt1320-sdw.h ---- a/sound/soc/codecs/rt1320-sdw.h -+++ b/sound/soc/codecs/rt1320-sdw.h -@@ -17,12 +17,17 @@ - - #define RT1320_DEV_ID 0x6981 - #define RT1321_DEV_ID 0x7045 -+#define RT1321_DEV_HV_VA0_ID 0x6997 -+#define RT1321_DEV_HV_VA1_ID 0x7071 - - /* imp-defined registers */ - #define RT1320_DEV_VERSION_ID_1 0xc404 - #define RT1320_DEV_ID_1 0xc405 - #define RT1320_DEV_ID_0 0xc406 - -+#define RT1320_HV_DEV_ID_0 0xf622 -+#define RT1320_HV_DEV_ID_1 0xf623 -+ - #define RT1320_POWER_STATE 0xc560 - - #define RT1321_PATCH_MAIN_VER 0x1000cffe -@@ -94,6 +99,12 @@ enum rt1320_version_id { - RT1320_VC, - }; - -+enum rt1321_version_id { -+ RT1321_VA0, -+ RT1321_VA1, -+ RT1321_VA2, -+}; -+ - #define RT1320_VER_B_ID 0x07392238 - #define RT1320_VAB_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vab.bin" - #define RT1320_VC_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vc.bin" -@@ -121,6 +132,7 @@ struct rt1320_datafixpoint { - int invrs; - }; - -+/* FW parameter id 1300 */ - typedef struct FwPara_HwSwGain { - unsigned int SwAdvGain; - unsigned int SwBasGain; -@@ -163,6 +175,13 @@ enum rt1320_rw_type { - RT1320_PARAM_READ = 3, - }; - -+enum { -+ RT1320_CLK_FREQ_19_2_MHZ = 1, -+ RT1320_CLK_FREQ_24MHZ = 2, -+ RT1320_CLK_FREQ_24_576MHZ = 3, -+ RT1320_CLK_FREQ_22_5792MHZ = 4, -+}; -+ - struct rt1320_sdw_priv { - struct snd_soc_component *component; - struct regmap *regmap; -diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c ---- a/sound/soc/codecs/rt5514-spi.c -+++ b/sound/soc/codecs/rt5514-spi.c -@@ -6,6 +6,7 @@ - * Author: Oder Chiou - */ - -+#include - #include - #include - #include -@@ -79,17 +80,17 @@ static void rt5514_spi_copy_work(struct work_struct *work) - unsigned int cur_wp, remain_data; - u8 buf[8]; - -- mutex_lock(&rt5514_dsp->dma_lock); -+ guard(mutex)(&rt5514_dsp->dma_lock); - if (!rt5514_dsp->substream) { - dev_err(rt5514_dsp->dev, "No pcm substream\n"); -- goto done; -+ return; - } - - runtime = rt5514_dsp->substream->runtime; - period_bytes = snd_pcm_lib_period_bytes(rt5514_dsp->substream); - if (!period_bytes) { - schedule_delayed_work(&rt5514_dsp->copy_work, 5); -- goto done; -+ return; - } - - if (rt5514_dsp->buf_size % period_bytes) -@@ -111,7 +112,7 @@ static void rt5514_spi_copy_work(struct work_struct *work) - - if (remain_data < period_bytes) { - schedule_delayed_work(&rt5514_dsp->copy_work, 5); -- goto done; -+ return; - } - } - -@@ -146,9 +147,6 @@ static void rt5514_spi_copy_work(struct work_struct *work) - snd_pcm_period_elapsed(rt5514_dsp->substream); - - schedule_delayed_work(&rt5514_dsp->copy_work, 5); -- --done: -- mutex_unlock(&rt5514_dsp->dma_lock); - } - - static void rt5514_schedule_copy(struct rt5514_dsp *rt5514_dsp) -@@ -216,7 +214,7 @@ static int rt5514_spi_hw_params(struct snd_soc_component *component, - snd_soc_component_get_drvdata(component); - u8 buf[8]; - -- mutex_lock(&rt5514_dsp->dma_lock); -+ guard(mutex)(&rt5514_dsp->dma_lock); - rt5514_dsp->substream = substream; - rt5514_dsp->dma_offset = 0; - -@@ -225,8 +223,6 @@ static int rt5514_spi_hw_params(struct snd_soc_component *component, - if (buf[0] & RT5514_IRQ_STATUS_BIT) - rt5514_schedule_copy(rt5514_dsp); - -- mutex_unlock(&rt5514_dsp->dma_lock); -- - return 0; - } - -@@ -236,9 +232,8 @@ static int rt5514_spi_hw_free(struct snd_soc_component *component, - struct rt5514_dsp *rt5514_dsp = - snd_soc_component_get_drvdata(component); - -- mutex_lock(&rt5514_dsp->dma_lock); -- rt5514_dsp->substream = NULL; -- mutex_unlock(&rt5514_dsp->dma_lock); -+ scoped_guard(mutex, &rt5514_dsp->dma_lock) -+ rt5514_dsp->substream = NULL; - - cancel_delayed_work_sync(&rt5514_dsp->copy_work); - -diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c ---- a/sound/soc/codecs/rt5514.c -+++ b/sound/soc/codecs/rt5514.c -@@ -1073,12 +1073,18 @@ static int rt5514_set_bias_level(struct snd_soc_component *component, - * settings to make sure recording properly. - */ - if (rt5514->dsp_enabled) { -- rt5514->dsp_enabled = 0; -- regmap_multi_reg_write(rt5514->i2c_regmap, -- rt5514_i2c_patch, -- ARRAY_SIZE(rt5514_i2c_patch)); -+ ret = regmap_multi_reg_write(rt5514->i2c_regmap, -+ rt5514_i2c_patch, -+ ARRAY_SIZE(rt5514_i2c_patch)); -+ if (ret) -+ return ret; -+ - regcache_mark_dirty(rt5514->regmap); -- regcache_sync(rt5514->regmap); -+ ret = regcache_sync(rt5514->regmap); -+ if (ret) -+ return ret; -+ -+ rt5514->dsp_enabled = 0; - } - } - break; -diff --git a/sound/soc/codecs/rt5575-spi.c b/sound/soc/codecs/rt5575-spi.c ---- a/sound/soc/codecs/rt5575-spi.c -+++ b/sound/soc/codecs/rt5575-spi.c -@@ -93,7 +93,6 @@ static void rt5575_spi_burst_write(struct spi_device *spi, u32 addr, const u8 *t - int rt5575_spi_fw_load(struct spi_device *spi) - { - struct device *dev = &spi->dev; -- const struct firmware *firmware; - int i, ret; - static const char * const fw_path[] = { - "realtek/rt5575/rt5575_fw1.bin", -@@ -104,6 +103,7 @@ int rt5575_spi_fw_load(struct spi_device *spi) - static const u32 fw_addr[] = { 0x5f400000, 0x5f600000, 0x5f7fe000, 0x5f7ff000 }; - - for (i = 0; i < ARRAY_SIZE(fw_addr); i++) { -+ const struct firmware *firmware __free(firmware) = NULL; - ret = request_firmware(&firmware, fw_path[i], dev); - if (ret) { - dev_err(dev, "Request firmware failure: %d\n", ret); -@@ -111,7 +111,6 @@ int rt5575_spi_fw_load(struct spi_device *spi) - } - - rt5575_spi_burst_write(spi, fw_addr[i], firmware->data, firmware->size); -- release_firmware(firmware); - } - - return 0; -diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c ---- a/sound/soc/codecs/rt5645.c -+++ b/sound/soc/codecs/rt5645.c -@@ -6,6 +6,7 @@ - * Author: Bard Liao - */ - -+#include - #include - #include - #include -@@ -3323,92 +3324,89 @@ static void rt5645_jack_detect_work(struct work_struct *work) - if (!rt5645->component) - return; - -- mutex_lock(&rt5645->jd_mutex); -- -- switch (rt5645->pdata.jd_mode) { -- case 0: /* Not using rt5645 JD */ -- if (rt5645->gpiod_hp_det) { -- gpio_state = gpiod_get_value(rt5645->gpiod_hp_det); -- if (rt5645->pdata.inv_hp_pol) -- gpio_state ^= 1; -- dev_dbg(rt5645->component->dev, "gpio_state = %d\n", -- gpio_state); -- report = rt5645_jack_detect(rt5645->component, gpio_state); -- } -- snd_soc_jack_report(rt5645->hp_jack, -- report, SND_JACK_HEADPHONE); -- snd_soc_jack_report(rt5645->mic_jack, -- report, SND_JACK_MICROPHONE); -- mutex_unlock(&rt5645->jd_mutex); -- return; -- case 4: -- val = snd_soc_component_read(rt5645->component, RT5645_A_JD_CTRL1) & 0x0020; -- break; -- default: /* read rt5645 jd1_1 status */ -- val = snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x1000; -- break; -- -- } -- -- if (!val && (rt5645->jack_type == 0)) { /* jack in */ -- report = rt5645_jack_detect(rt5645->component, 1); -- } else if (!val && rt5645->jack_type == SND_JACK_HEADSET) { -- /* for push button and jack out */ -- btn_type = 0; -- if (snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x4) { -- /* button pressed */ -- report = SND_JACK_HEADSET; -- btn_type = rt5645_button_detect(rt5645->component); -- /* rt5650 can report three kinds of button behavior, -- one click, double click and hold. However, -- currently we will report button pressed/released -- event. So all the three button behaviors are -- treated as button pressed. */ -- switch (btn_type) { -- case 0x8000: -- case 0x4000: -- case 0x2000: -- report |= SND_JACK_BTN_0; -- break; -- case 0x1000: -- case 0x0800: -- case 0x0400: -- report |= SND_JACK_BTN_1; -- break; -- case 0x0200: -- case 0x0100: -- case 0x0080: -- report |= SND_JACK_BTN_2; -- break; -- case 0x0040: -- case 0x0020: -- case 0x0010: -- report |= SND_JACK_BTN_3; -- break; -- case 0x0000: /* unpressed */ -- break; -- default: -- dev_err(rt5645->component->dev, -- "Unexpected button code 0x%04x\n", -- btn_type); -- break; -+ scoped_guard(mutex, &rt5645->jd_mutex) { -+ switch (rt5645->pdata.jd_mode) { -+ case 0: /* Not using rt5645 JD */ -+ if (rt5645->gpiod_hp_det) { -+ gpio_state = gpiod_get_value(rt5645->gpiod_hp_det); -+ if (rt5645->pdata.inv_hp_pol) -+ gpio_state ^= 1; -+ dev_dbg(rt5645->component->dev, "gpio_state = %d\n", -+ gpio_state); -+ report = rt5645_jack_detect(rt5645->component, gpio_state); - } -+ snd_soc_jack_report(rt5645->hp_jack, -+ report, SND_JACK_HEADPHONE); -+ snd_soc_jack_report(rt5645->mic_jack, -+ report, SND_JACK_MICROPHONE); -+ return; -+ case 4: -+ val = snd_soc_component_read(rt5645->component, RT5645_A_JD_CTRL1) & 0x0020; -+ break; -+ default: /* read rt5645 jd1_1 status */ -+ val = snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x1000; -+ break; - } -- if (btn_type == 0)/* button release */ -- report = rt5645->jack_type; -- else { -- mod_timer(&rt5645->btn_check_timer, -- msecs_to_jiffies(100)); -- } -- } else { -- /* jack out */ -- report = 0; -- snd_soc_component_update_bits(rt5645->component, -- RT5645_INT_IRQ_ST, 0x1, 0x0); -- rt5645_jack_detect(rt5645->component, 0); -- } - -- mutex_unlock(&rt5645->jd_mutex); -+ if (!val && rt5645->jack_type == 0) { /* jack in */ -+ report = rt5645_jack_detect(rt5645->component, 1); -+ } else if (!val && rt5645->jack_type == SND_JACK_HEADSET) { -+ /* for push button and jack out */ -+ btn_type = 0; -+ if (snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x4) { -+ /* button pressed */ -+ report = SND_JACK_HEADSET; -+ btn_type = rt5645_button_detect(rt5645->component); -+ /* -+ * rt5650 can report three kinds of button behavior, -+ * one click, double click and hold. However, -+ * currently we will report button pressed/released -+ * event. So all the three button behaviors are -+ * treated as button pressed. -+ */ -+ switch (btn_type) { -+ case 0x8000: -+ case 0x4000: -+ case 0x2000: -+ report |= SND_JACK_BTN_0; -+ break; -+ case 0x1000: -+ case 0x0800: -+ case 0x0400: -+ report |= SND_JACK_BTN_1; -+ break; -+ case 0x0200: -+ case 0x0100: -+ case 0x0080: -+ report |= SND_JACK_BTN_2; -+ break; -+ case 0x0040: -+ case 0x0020: -+ case 0x0010: -+ report |= SND_JACK_BTN_3; -+ break; -+ case 0x0000: /* unpressed */ -+ break; -+ default: -+ dev_err(rt5645->component->dev, -+ "Unexpected button code 0x%04x\n", -+ btn_type); -+ break; -+ } -+ } -+ if (btn_type == 0)/* button release */ -+ report = rt5645->jack_type; -+ else -+ mod_timer(&rt5645->btn_check_timer, -+ msecs_to_jiffies(100)); -+ } else { -+ /* jack out */ -+ report = 0; -+ snd_soc_component_update_bits(rt5645->component, -+ RT5645_INT_IRQ_ST, 0x1, 0x0); -+ rt5645_jack_detect(rt5645->component, 0); -+ } -+ } - - snd_soc_jack_report(rt5645->hp_jack, report, SND_JACK_HEADPHONE); - snd_soc_jack_report(rt5645->mic_jack, report, SND_JACK_MICROPHONE); -@@ -3522,9 +3520,15 @@ static int rt5645_suspend(struct snd_soc_component *component) - static int rt5645_resume(struct snd_soc_component *component) - { - struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); -+ int ret; - - regcache_cache_only(rt5645->regmap, false); -- regcache_sync(rt5645->regmap); -+ ret = regcache_sync(rt5645->regmap); -+ if (ret) { -+ regcache_cache_only(rt5645->regmap, true); -+ regcache_mark_dirty(rt5645->regmap); -+ return ret; -+ } - - return 0; - } -@@ -4335,9 +4339,15 @@ static int rt5645_sys_suspend(struct device *dev) - static int rt5645_sys_resume(struct device *dev) - { - struct rt5645_priv *rt5645 = dev_get_drvdata(dev); -+ int ret; - - regcache_cache_only(rt5645->regmap, false); -- regcache_sync(rt5645->regmap); -+ ret = regcache_sync(rt5645->regmap); -+ if (ret) { -+ regcache_cache_only(rt5645->regmap, true); -+ regcache_mark_dirty(rt5645->regmap); -+ return ret; -+ } - - if (rt5645->hp_jack) { - rt5645->jack_type = 0; -diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c ---- a/sound/soc/codecs/rt5665.c -+++ b/sound/soc/codecs/rt5665.c -@@ -6,6 +6,7 @@ - * Author: Bard Liao - */ - -+#include - #include - #include - #include -@@ -1208,7 +1209,7 @@ static void rt5665_jack_detect_handler(struct work_struct *work) - usleep_range(10000, 15000); - } - -- mutex_lock(&rt5665->calibrate_mutex); -+ guard(mutex)(&rt5665->calibrate_mutex); - - val = snd_soc_component_read(rt5665->component, RT5665_AJD1_CTRL) & 0x0010; - if (!val) { -@@ -1274,8 +1275,6 @@ static void rt5665_jack_detect_handler(struct work_struct *work) - schedule_delayed_work(&rt5665->jd_check_work, 0); - else - cancel_delayed_work_sync(&rt5665->jd_check_work); -- -- mutex_unlock(&rt5665->calibrate_mutex); - } - - static const char * const rt5665_clk_sync[] = { -@@ -4564,7 +4563,7 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665) - { - int value, count; - -- mutex_lock(&rt5665->calibrate_mutex); -+ guard(mutex)(&rt5665->calibrate_mutex); - - regcache_cache_bypass(rt5665->regmap, true); - -@@ -4601,7 +4600,8 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665) - pr_err("HP Calibration Failure\n"); - regmap_write(rt5665->regmap, RT5665_RESET, 0); - regcache_cache_bypass(rt5665->regmap, false); -- goto out_unlock; -+ rt5665->calibration_done = true; -+ return; - } - - count++; -@@ -4620,7 +4620,8 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665) - pr_err("MONO Calibration Failure\n"); - regmap_write(rt5665->regmap, RT5665_RESET, 0); - regcache_cache_bypass(rt5665->regmap, false); -- goto out_unlock; -+ rt5665->calibration_done = true; -+ return; - } - - count++; -@@ -4635,9 +4636,7 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665) - regmap_write(rt5665->regmap, RT5665_BIAS_CUR_CTRL_8, 0xa602); - regmap_write(rt5665->regmap, RT5665_ASRC_8, 0x0120); - --out_unlock: - rt5665->calibration_done = true; -- mutex_unlock(&rt5665->calibrate_mutex); - } - - static void rt5665_calibrate_handler(struct work_struct *work) -diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c ---- a/sound/soc/codecs/rt5668.c -+++ b/sound/soc/codecs/rt5668.c -@@ -6,6 +6,7 @@ - * Author: Bard Liao - */ - -+#include - #include - #include - #include -@@ -986,7 +987,7 @@ static void rt5668_jack_detect_handler(struct work_struct *work) - return; - } - -- mutex_lock(&rt5668->calibrate_mutex); -+ guard(mutex)(&rt5668->calibrate_mutex); - - val = snd_soc_component_read(rt5668->component, RT5668_AJD1_CTRL) - & RT5668_JDH_RS_MASK; -@@ -1053,8 +1054,6 @@ static void rt5668_jack_detect_handler(struct work_struct *work) - schedule_delayed_work(&rt5668->jd_check_work, 0); - else - cancel_delayed_work_sync(&rt5668->jd_check_work); -- -- mutex_unlock(&rt5668->calibrate_mutex); - } - - static const struct snd_kcontrol_new rt5668_snd_controls[] = { -@@ -2356,7 +2355,7 @@ static void rt5668_calibrate(struct rt5668_priv *rt5668) - { - int value, count; - -- mutex_lock(&rt5668->calibrate_mutex); -+ guard(mutex)(&rt5668->calibrate_mutex); - - rt5668_reset(rt5668->regmap); - regmap_write(rt5668->regmap, RT5668_PWR_ANLG_1, 0xa2bf); -@@ -2400,9 +2399,6 @@ static void rt5668_calibrate(struct rt5668_priv *rt5668) - /* restore settings */ - regmap_write(rt5668->regmap, RT5668_STO1_ADC_MIXER, 0xc0c4); - regmap_write(rt5668->regmap, RT5668_PWR_DIG_1, 0x0000); -- -- mutex_unlock(&rt5668->calibrate_mutex); -- - } - - static int rt5668_i2c_probe(struct i2c_client *i2c) -diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c ---- a/sound/soc/codecs/rt5677-spi.c -+++ b/sound/soc/codecs/rt5677-spi.c -@@ -6,6 +6,7 @@ - * Author: Oder Chiou - */ - -+#include - #include - #include - #include -@@ -133,9 +134,8 @@ static int rt5677_spi_hw_params( - struct rt5677_dsp *rt5677_dsp = - snd_soc_component_get_drvdata(component); - -- mutex_lock(&rt5677_dsp->dma_lock); -+ guard(mutex)(&rt5677_dsp->dma_lock); - rt5677_dsp->substream = substream; -- mutex_unlock(&rt5677_dsp->dma_lock); - - return 0; - } -@@ -147,9 +147,8 @@ static int rt5677_spi_hw_free( - struct rt5677_dsp *rt5677_dsp = - snd_soc_component_get_drvdata(component); - -- mutex_lock(&rt5677_dsp->dma_lock); -+ guard(mutex)(&rt5677_dsp->dma_lock); - rt5677_dsp->substream = NULL; -- mutex_unlock(&rt5677_dsp->dma_lock); - - return 0; - } -@@ -311,17 +310,17 @@ static void rt5677_spi_copy_work(struct work_struct *work) - int ret = 0; - - /* Ensure runtime->dma_area buffer does not go away while copying. */ -- mutex_lock(&rt5677_dsp->dma_lock); -+ guard(mutex)(&rt5677_dsp->dma_lock); - if (!rt5677_dsp->substream) { - dev_err(rt5677_dsp->dev, "No pcm substream\n"); -- goto done; -+ return; - } - - runtime = rt5677_dsp->substream->runtime; - - if (rt5677_spi_mic_write_offset(&mic_write_offset)) { - dev_err(rt5677_dsp->dev, "No mic_write_offset\n"); -- goto done; -+ return; - } - - /* If this is the first time that we've asked for streaming data after -@@ -355,7 +354,7 @@ static void rt5677_spi_copy_work(struct work_struct *work) - ret = rt5677_spi_copy(rt5677_dsp, copy_bytes); - if (ret) { - dev_err(rt5677_dsp->dev, "Copy failed %d\n", ret); -- goto done; -+ return; - } - rt5677_dsp->avail_bytes += copy_bytes; - if (rt5677_dsp->avail_bytes >= period_bytes) { -@@ -367,8 +366,6 @@ static void rt5677_spi_copy_work(struct work_struct *work) - - delay = bytes_to_frames(runtime, period_bytes) / runtime->rate; - schedule_delayed_work(&rt5677_dsp->copy_work, secs_to_jiffies(delay)); --done: -- mutex_unlock(&rt5677_dsp->dma_lock); - } - - static int rt5677_spi_pcm_new(struct snd_soc_component *component, -@@ -507,10 +504,8 @@ int rt5677_spi_read(u32 addr, void *rxbuf, size_t len) - header[3] = ((addr + offset) & 0x0000ff00) >> 8; - header[4] = ((addr + offset) & 0x000000ff) >> 0; - -- mutex_lock(&spi_mutex); -- status |= spi_sync(g_spi, &m); -- mutex_unlock(&spi_mutex); -- -+ scoped_guard(mutex, &spi_mutex) -+ status |= spi_sync(g_spi, &m); - - /* Copy data back to caller buffer */ - rt5677_spi_reverse(cb + offset, len - offset, body, t[1].len); -@@ -564,9 +559,8 @@ int rt5677_spi_write(u32 addr, const void *txbuf, size_t len) - offset += t.len; - t.len += RT5677_SPI_HEADER + 1; - -- mutex_lock(&spi_mutex); -- status |= spi_sync(g_spi, &m); -- mutex_unlock(&spi_mutex); -+ scoped_guard(mutex, &spi_mutex) -+ status |= spi_sync(g_spi, &m); - } - return status; - } -@@ -591,10 +585,10 @@ void rt5677_spi_hotword_detected(void) - return; - } - -- mutex_lock(&rt5677_dsp->dma_lock); -- dev_info(rt5677_dsp->dev, "Hotword detected\n"); -- rt5677_dsp->new_hotword = true; -- mutex_unlock(&rt5677_dsp->dma_lock); -+ scoped_guard(mutex, &rt5677_dsp->dma_lock) { -+ dev_info(rt5677_dsp->dev, "Hotword detected\n"); -+ rt5677_dsp->new_hotword = true; -+ } - - schedule_delayed_work(&rt5677_dsp->copy_work, 0); - } -diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c ---- a/sound/soc/codecs/rt5677.c -+++ b/sound/soc/codecs/rt5677.c -@@ -6,6 +6,8 @@ - * Author: Oder Chiou - */ - -+#include -+#include - #include - #include - #include -@@ -563,46 +565,43 @@ static int rt5677_dsp_mode_i2c_write_addr(struct rt5677_priv *rt5677, - struct snd_soc_component *component = rt5677->component; - int ret; - -- mutex_lock(&rt5677->dsp_cmd_lock); -+ guard(mutex)(&rt5677->dsp_cmd_lock); - - ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB, - addr >> 16); - if (ret < 0) { - dev_err(component->dev, "Failed to set addr msb value: %d\n", ret); -- goto err; -+ return ret; - } - - ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB, - addr & 0xffff); - if (ret < 0) { - dev_err(component->dev, "Failed to set addr lsb value: %d\n", ret); -- goto err; -+ return ret; - } - - ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_MSB, - value >> 16); - if (ret < 0) { - dev_err(component->dev, "Failed to set data msb value: %d\n", ret); -- goto err; -+ return ret; - } - - ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_LSB, - value & 0xffff); - if (ret < 0) { - dev_err(component->dev, "Failed to set data lsb value: %d\n", ret); -- goto err; -+ return ret; - } - - ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE, - opcode); - if (ret < 0) { - dev_err(component->dev, "Failed to set op code value: %d\n", ret); -- goto err; -+ return ret; - } - --err: -- mutex_unlock(&rt5677->dsp_cmd_lock); -- - return ret; - } - -@@ -622,36 +621,33 @@ static int rt5677_dsp_mode_i2c_read_addr( - int ret; - unsigned int msb, lsb; - -- mutex_lock(&rt5677->dsp_cmd_lock); -+ guard(mutex)(&rt5677->dsp_cmd_lock); - - ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB, - addr >> 16); - if (ret < 0) { - dev_err(component->dev, "Failed to set addr msb value: %d\n", ret); -- goto err; -+ return ret; - } - - ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB, - addr & 0xffff); - if (ret < 0) { - dev_err(component->dev, "Failed to set addr lsb value: %d\n", ret); -- goto err; -+ return ret; - } - - ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE, - 0x0002); - if (ret < 0) { - dev_err(component->dev, "Failed to set op code value: %d\n", ret); -- goto err; -+ return ret; - } - - regmap_read(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_MSB, &msb); - regmap_read(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_LSB, &lsb); - *value = (msb << 16) | lsb; - --err: -- mutex_unlock(&rt5677->dsp_cmd_lock); -- - return ret; - } - -@@ -853,11 +849,11 @@ static int rt5677_parse_and_load_dsp(struct rt5677_priv *rt5677, const u8 *buf, - - static int rt5677_load_dsp_from_file(struct rt5677_priv *rt5677) - { -- const struct firmware *fwp; - struct device *dev = rt5677->component->dev; -- int ret = 0; -+ int ret; - - /* Load dsp firmware from rt5677_elf_vad file */ -+ const struct firmware *fwp __free(firmware) = NULL; - ret = request_firmware(&fwp, "rt5677_elf_vad", dev); - if (ret) { - dev_err(dev, "Request rt5677_elf_vad failed %d\n", ret); -@@ -865,9 +861,7 @@ static int rt5677_load_dsp_from_file(struct rt5677_priv *rt5677) - } - dev_info(dev, "Requested rt5677_elf_vad (%zu)\n", fwp->size); - -- ret = rt5677_parse_and_load_dsp(rt5677, fwp->data, fwp->size); -- release_firmware(fwp); -- return ret; -+ return rt5677_parse_and_load_dsp(rt5677, fwp->data, fwp->size); - } - - static int rt5677_set_dsp_vad(struct snd_soc_component *component, bool on) -@@ -940,21 +934,20 @@ static void rt5677_dsp_work(struct work_struct *work) - activity = false; - - /* Don't turn off the DSP while handling irqs */ -- mutex_lock(&rt5677->irq_lock); -- /* Set DSP CPU to Stop */ -- regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, -- RT5677_PWR_DSP_CPU, RT5677_PWR_DSP_CPU); -+ scoped_guard(mutex, &rt5677->irq_lock) { -+ /* Set DSP CPU to Stop */ -+ regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, -+ RT5677_PWR_DSP_CPU, RT5677_PWR_DSP_CPU); - -- rt5677_set_dsp_mode(rt5677, false); -+ rt5677_set_dsp_mode(rt5677, false); - -- /* Disable and clear VAD interrupt */ -- regmap_write(rt5677->regmap, RT5677_VAD_CTRL1, 0x2184); -+ /* Disable and clear VAD interrupt */ -+ regmap_write(rt5677->regmap, RT5677_VAD_CTRL1, 0x2184); - -- /* Set GPIO1 pin back to be IRQ output for jack detect */ -- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, -- RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ); -- -- mutex_unlock(&rt5677->irq_lock); -+ /* Set GPIO1 pin back to be IRQ output for jack detect */ -+ regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, -+ RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ); -+ } - } - } - -@@ -4767,6 +4760,21 @@ static int rt5677_gpio_direction_in(struct gpio_chip *chip, unsigned offset) - return rt5677_update_gpio_bits(rt5677, offset, m, v); - } - -+static int rt5677_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) -+{ -+ struct rt5677_priv *rt5677 = gpiochip_get_data(chip); -+ unsigned int shift = RT5677_GPIOx_DIR_SFT + (offset % 5) * 3; -+ unsigned int bank = offset / 5; -+ unsigned int reg = bank ? RT5677_GPIO_CTRL3 : RT5677_GPIO_CTRL2; -+ int ret; -+ -+ ret = regmap_test_bits(rt5677->regmap, reg, BIT(shift)); -+ if (ret < 0) -+ return ret; -+ -+ return ret ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; -+} -+ - /* - * Configures the GPIO as - * 0 - floating -@@ -4834,6 +4842,7 @@ static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset) - static const struct gpio_chip rt5677_template_chip = { - .label = RT5677_DRV_NAME, - .owner = THIS_MODULE, -+ .get_direction = rt5677_gpio_get_direction, - .direction_output = rt5677_gpio_direction_out, - .set = rt5677_gpio_set, - .direction_input = rt5677_gpio_direction_in, -@@ -4980,11 +4989,11 @@ static int rt5677_read(void *context, unsigned int reg, unsigned int *val) - - if (rt5677->is_dsp_mode) { - if (reg > 0xff) { -- mutex_lock(&rt5677->dsp_pri_lock); -- rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, -- reg & 0xff); -- rt5677_dsp_mode_i2c_read(rt5677, RT5677_PRIV_DATA, val); -- mutex_unlock(&rt5677->dsp_pri_lock); -+ scoped_guard(mutex, &rt5677->dsp_pri_lock) { -+ rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, -+ reg & 0xff); -+ rt5677_dsp_mode_i2c_read(rt5677, RT5677_PRIV_DATA, val); -+ } - } else { - rt5677_dsp_mode_i2c_read(rt5677, reg, val); - } -@@ -5002,12 +5011,12 @@ static int rt5677_write(void *context, unsigned int reg, unsigned int val) - - if (rt5677->is_dsp_mode) { - if (reg > 0xff) { -- mutex_lock(&rt5677->dsp_pri_lock); -- rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, -- reg & 0xff); -- rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_DATA, -- val); -- mutex_unlock(&rt5677->dsp_pri_lock); -+ scoped_guard(mutex, &rt5677->dsp_pri_lock) { -+ rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, -+ reg & 0xff); -+ rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_DATA, -+ val); -+ } - } else { - rt5677_dsp_mode_i2c_write(rt5677, reg, val); - } -@@ -5399,7 +5408,7 @@ static void rt5677_resume_irq_check(struct work_struct *work) - * Without this explicit check, unplug the headset right after suspend - * starts, then after resume the headset is still shown as plugged in. - */ -- mutex_lock(&rt5677->irq_lock); -+ guard(mutex)(&rt5677->irq_lock); - for (i = 0; i < RT5677_IRQ_NUM; i++) { - if (rt5677->irq_en & rt5677_irq_descs[i].enable_mask) { - virq = irq_find_mapping(rt5677->domain, i); -@@ -5407,7 +5416,6 @@ static void rt5677_resume_irq_check(struct work_struct *work) - handle_nested_irq(virq); - } - } -- mutex_unlock(&rt5677->irq_lock); - } - - static void rt5677_irq_bus_lock(struct irq_data *data) -diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c ---- a/sound/soc/codecs/rt5682-sdw.c -+++ b/sound/soc/codecs/rt5682-sdw.c -@@ -6,6 +6,7 @@ - // Author: Oder Chiou - // - -+#include - #include - #include - #include -@@ -410,7 +411,9 @@ static int rt5682_io_init(struct device *dev, struct sdw_slave *slave) - if (rt5682->first_hw_init) { - regcache_cache_bypass(rt5682->regmap, false); - regcache_mark_dirty(rt5682->regmap); -- regcache_sync(rt5682->regmap); -+ ret = regcache_sync(rt5682->regmap); -+ if (ret) -+ goto err_sync; - - /* volatile registers */ - regmap_update_bits(rt5682->regmap, RT5682_CBJ_CTRL_2, -@@ -472,8 +475,16 @@ static int rt5682_io_init(struct device *dev, struct sdw_slave *slave) - /* Mark Slave initialization complete */ - rt5682->hw_init = true; - rt5682->first_hw_init = true; -+ goto out; -+ -+err_sync: -+ regcache_cache_bypass(rt5682->regmap, false); -+ regcache_cache_only(rt5682->sdw_regmap, true); -+ regcache_cache_only(rt5682->regmap, true); -+ regcache_mark_dirty(rt5682->regmap); - - err_nodev: -+out: - pm_runtime_put_autosuspend(&slave->dev); - - dev_dbg(&slave->dev, "%s hw_init complete: %d\n", __func__, ret); -@@ -660,12 +671,11 @@ static int rt5682_interrupt_callback(struct sdw_slave *slave, - dev_dbg(&slave->dev, - "%s control_port_stat=%x", __func__, status->control_port); - -- mutex_lock(&rt5682->disable_irq_lock); -+ guard(mutex)(&rt5682->disable_irq_lock); - if (status->control_port & 0x4 && !rt5682->disable_irq) { - mod_delayed_work(system_power_efficient_wq, - &rt5682->jack_detect_work, msecs_to_jiffies(rt5682->irq_work_delay_time)); - } -- mutex_unlock(&rt5682->disable_irq_lock); - - return 0; - } -@@ -736,11 +746,11 @@ static int rt5682_dev_system_suspend(struct device *dev) - * deferred work completes and before the parent disables - * interrupts on the link - */ -- mutex_lock(&rt5682->disable_irq_lock); -- rt5682->disable_irq = true; -- ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, -- SDW_SCP_INT1_IMPL_DEF, 0); -- mutex_unlock(&rt5682->disable_irq_lock); -+ scoped_guard(mutex, &rt5682->disable_irq_lock) { -+ rt5682->disable_irq = true; -+ ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, -+ SDW_SCP_INT1_IMPL_DEF, 0); -+ } - - if (ret < 0) { - /* log but don't prevent suspend from happening */ -@@ -760,12 +770,12 @@ static int rt5682_dev_resume(struct device *dev) - return 0; - - if (!slave->unattach_request) { -- mutex_lock(&rt5682->disable_irq_lock); -- if (rt5682->disable_irq == true) { -- sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); -- rt5682->disable_irq = false; -+ scoped_guard(mutex, &rt5682->disable_irq_lock) { -+ if (rt5682->disable_irq) { -+ sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); -+ rt5682->disable_irq = false; -+ } - } -- mutex_unlock(&rt5682->disable_irq_lock); - } - - ret = sdw_slave_wait_for_init(slave, RT5682_PROBE_TIMEOUT); -@@ -776,7 +786,13 @@ static int rt5682_dev_resume(struct device *dev) - - regcache_cache_only(rt5682->sdw_regmap, false); - regcache_cache_only(rt5682->regmap, false); -- regcache_sync(rt5682->regmap); -+ ret = regcache_sync(rt5682->regmap); -+ if (ret) { -+ regcache_cache_only(rt5682->sdw_regmap, true); -+ regcache_cache_only(rt5682->regmap, true); -+ regcache_mark_dirty(rt5682->regmap); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c ---- a/sound/soc/codecs/rt5682.c -+++ b/sound/soc/codecs/rt5682.c -@@ -6,6 +6,7 @@ - // Author: Bard Liao - // - -+#include - #include - #include - #include -@@ -3125,7 +3126,7 @@ void rt5682_calibrate(struct rt5682_priv *rt5682) - { - int value, count; - -- mutex_lock(&rt5682->calibrate_mutex); -+ guard(mutex)(&rt5682->calibrate_mutex); - - rt5682_reset(rt5682); - regmap_write(rt5682->regmap, RT5682_I2C_CTRL, 0x000f); -@@ -3175,8 +3176,6 @@ void rt5682_calibrate(struct rt5682_priv *rt5682) - regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x2005); - regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0xc0c4); - regmap_write(rt5682->regmap, RT5682_CAL_REC, 0x0c0c); -- -- mutex_unlock(&rt5682->calibrate_mutex); - } - EXPORT_SYMBOL_GPL(rt5682_calibrate); - -diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c ---- a/sound/soc/codecs/rt5682s.c -+++ b/sound/soc/codecs/rt5682s.c -@@ -6,6 +6,7 @@ - // Author: Derek Fang - // - -+#include - #include - #include - #include -@@ -648,7 +649,7 @@ static void rt5682s_sar_power_mode(struct snd_soc_component *component, int mode - { - struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); - -- mutex_lock(&rt5682s->sar_mutex); -+ guard(mutex)(&rt5682s->sar_mutex); - - switch (mode) { - case SAR_PWR_SAVING: -@@ -695,8 +696,6 @@ static void rt5682s_sar_power_mode(struct snd_soc_component *component, int mode - dev_err(component->dev, "Invalid SAR Power mode: %d\n", mode); - break; - } -- -- mutex_unlock(&rt5682s->sar_mutex); - } - - static void rt5682s_enable_push_button_irq(struct snd_soc_component *component) -@@ -2534,7 +2533,7 @@ static int rt5682s_wclk_prepare(struct clk_hw *hw) - if (!rt5682s_clk_check(rt5682s)) - return -EINVAL; - -- mutex_lock(&rt5682s->wclk_mutex); -+ guard(mutex)(&rt5682s->wclk_mutex); - - snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1, - RT5682S_PWR_VREF2 | RT5682S_PWR_FV2 | RT5682S_PWR_MB, -@@ -2556,8 +2555,6 @@ static int rt5682s_wclk_prepare(struct clk_hw *hw) - - rt5682s->wclk_enabled = 1; - -- mutex_unlock(&rt5682s->wclk_mutex); -- - return 0; - } - -@@ -2570,7 +2567,7 @@ static void rt5682s_wclk_unprepare(struct clk_hw *hw) - if (!rt5682s_clk_check(rt5682s)) - return; - -- mutex_lock(&rt5682s->wclk_mutex); -+ guard(mutex)(&rt5682s->wclk_mutex); - - if (!rt5682s->jack_type) - snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1, -@@ -2585,8 +2582,6 @@ static void rt5682s_wclk_unprepare(struct clk_hw *hw) - rt5682s_set_pllb_power(rt5682s, 0); - - rt5682s->wclk_enabled = 0; -- -- mutex_unlock(&rt5682s->wclk_mutex); - } - - static unsigned long rt5682s_wclk_recalc_rate(struct clk_hw *hw, -@@ -2997,7 +2992,7 @@ static void rt5682s_calibrate(struct rt5682s_priv *rt5682s) - { - unsigned int count, value; - -- mutex_lock(&rt5682s->calibrate_mutex); -+ guard(mutex)(&rt5682s->calibrate_mutex); - - regmap_write(rt5682s->regmap, RT5682S_PWR_ANLG_1, 0xaa80); - usleep_range(15000, 20000); -@@ -3034,8 +3029,6 @@ static void rt5682s_calibrate(struct rt5682s_priv *rt5682s) - regmap_write(rt5682s->regmap, RT5682S_PWR_DIG_1, 0x00c0); - regmap_write(rt5682s->regmap, RT5682S_PWR_ANLG_1, 0x0800); - regmap_write(rt5682s->regmap, RT5682S_GLB_CLK, 0x0000); -- -- mutex_unlock(&rt5682s->calibrate_mutex); - } - - static const struct regmap_config rt5682s_regmap = { -diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c ---- a/sound/soc/codecs/rt700-sdw.c -+++ b/sound/soc/codecs/rt700-sdw.c -@@ -6,6 +6,7 @@ - // - // - -+#include - #include - #include - #include -@@ -415,12 +416,11 @@ static int rt700_interrupt_callback(struct sdw_slave *slave, - dev_dbg(&slave->dev, - "%s control_port_stat=%x", __func__, status->control_port); - -- mutex_lock(&rt700->disable_irq_lock); -+ guard(mutex)(&rt700->disable_irq_lock); - if (status->control_port & 0x4 && !rt700->disable_irq) { - mod_delayed_work(system_power_efficient_wq, - &rt700->jack_detect_work, msecs_to_jiffies(250)); - } -- mutex_unlock(&rt700->disable_irq_lock); - - return 0; - } -@@ -458,10 +458,8 @@ static void rt700_sdw_remove(struct sdw_slave *slave) - { - struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev); - -- if (rt700->hw_init) { -- cancel_delayed_work_sync(&rt700->jack_detect_work); -- cancel_delayed_work_sync(&rt700->jack_btn_check_work); -- } -+ cancel_delayed_work_sync(&rt700->jack_detect_work); -+ cancel_delayed_work_sync(&rt700->jack_btn_check_work); - - pm_runtime_disable(&slave->dev); - } -@@ -501,11 +499,11 @@ static int rt700_dev_system_suspend(struct device *dev) - * deferred work completes and before the parent disables - * interrupts on the link - */ -- mutex_lock(&rt700->disable_irq_lock); -- rt700->disable_irq = true; -- ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, -- SDW_SCP_INT1_IMPL_DEF, 0); -- mutex_unlock(&rt700->disable_irq_lock); -+ scoped_guard(mutex, &rt700->disable_irq_lock) { -+ rt700->disable_irq = true; -+ ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, -+ SDW_SCP_INT1_IMPL_DEF, 0); -+ } - - if (ret < 0) { - /* log but don't prevent suspend from happening */ -diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c ---- a/sound/soc/codecs/rt711-sdca-sdw.c -+++ b/sound/soc/codecs/rt711-sdca-sdw.c -@@ -6,6 +6,7 @@ - // - // - -+#include - #include - #include - #include -@@ -415,13 +416,13 @@ static int rt711_sdca_dev_system_suspend(struct device *dev) - * deferred work completes and before the parent disables - * interrupts on the link - */ -- mutex_lock(&rt711_sdca->disable_irq_lock); -- rt711_sdca->disable_irq = true; -- ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, -- SDW_SCP_SDCA_INTMASK_SDCA_0, 0); -- ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, -- SDW_SCP_SDCA_INTMASK_SDCA_8, 0); -- mutex_unlock(&rt711_sdca->disable_irq_lock); -+ scoped_guard(mutex, &rt711_sdca->disable_irq_lock) { -+ rt711_sdca->disable_irq = true; -+ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, -+ SDW_SCP_SDCA_INTMASK_SDCA_0, 0); -+ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, -+ SDW_SCP_SDCA_INTMASK_SDCA_8, 0); -+ } - - if (ret1 < 0 || ret2 < 0) { - /* log but don't prevent suspend from happening */ -@@ -443,13 +444,15 @@ static int rt711_sdca_dev_resume(struct device *dev) - return 0; - - if (!slave->unattach_request) { -- mutex_lock(&rt711->disable_irq_lock); -- if (rt711->disable_irq == true) { -- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); -- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); -- rt711->disable_irq = false; -+ scoped_guard(mutex, &rt711->disable_irq_lock) { -+ if (rt711->disable_irq) { -+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, -+ SDW_SCP_SDCA_INTMASK_SDCA_0); -+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, -+ SDW_SCP_SDCA_INTMASK_SDCA_8); -+ rt711->disable_irq = false; -+ } - } -- mutex_unlock(&rt711->disable_irq_lock); - } - - ret = sdw_slave_wait_for_init(slave, RT711_PROBE_TIMEOUT); -diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c ---- a/sound/soc/codecs/rt711-sdca.c -+++ b/sound/soc/codecs/rt711-sdca.c -@@ -6,6 +6,7 @@ - // - // - -+#include - #include - #include - #include -@@ -450,7 +451,7 @@ static void rt711_sdca_btn_check_handler(struct work_struct *work) - - static void rt711_sdca_jack_init(struct rt711_sdca_priv *rt711) - { -- mutex_lock(&rt711->calibrate_mutex); -+ guard(mutex)(&rt711->calibrate_mutex); - - if (rt711->hs_jack) { - /* Enable HID1 event & set button RTC mode */ -@@ -515,8 +516,6 @@ static void rt711_sdca_jack_init(struct rt711_sdca_priv *rt711) - - dev_dbg(&rt711->slave->dev, "in %s disable\n", __func__); - } -- -- mutex_unlock(&rt711->calibrate_mutex); - } - - static int rt711_sdca_set_jack_detect(struct snd_soc_component *component, -diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c ---- a/sound/soc/codecs/rt711-sdw.c -+++ b/sound/soc/codecs/rt711-sdw.c -@@ -6,6 +6,7 @@ - // - // - -+#include - #include - #include - #include -@@ -422,12 +423,11 @@ static int rt711_interrupt_callback(struct sdw_slave *slave, - dev_dbg(&slave->dev, - "%s control_port_stat=%x", __func__, status->control_port); - -- mutex_lock(&rt711->disable_irq_lock); -+ guard(mutex)(&rt711->disable_irq_lock); - if (status->control_port & 0x4 && !rt711->disable_irq) { - mod_delayed_work(system_power_efficient_wq, - &rt711->jack_detect_work, msecs_to_jiffies(250)); - } -- mutex_unlock(&rt711->disable_irq_lock); - - return 0; - } -@@ -509,11 +509,11 @@ static int rt711_dev_system_suspend(struct device *dev) - * deferred work completes and before the parent disables - * interrupts on the link - */ -- mutex_lock(&rt711->disable_irq_lock); -- rt711->disable_irq = true; -- ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, -- SDW_SCP_INT1_IMPL_DEF, 0); -- mutex_unlock(&rt711->disable_irq_lock); -+ scoped_guard(mutex, &rt711->disable_irq_lock) { -+ rt711->disable_irq = true; -+ ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, -+ SDW_SCP_INT1_IMPL_DEF, 0); -+ } - - if (ret < 0) { - /* log but don't prevent suspend from happening */ -@@ -535,12 +535,12 @@ static int rt711_dev_resume(struct device *dev) - return 0; - - if (!slave->unattach_request) { -- mutex_lock(&rt711->disable_irq_lock); -- if (rt711->disable_irq == true) { -- sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); -- rt711->disable_irq = false; -+ scoped_guard(mutex, &rt711->disable_irq_lock) { -+ if (rt711->disable_irq) { -+ sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); -+ rt711->disable_irq = false; -+ } - } -- mutex_unlock(&rt711->disable_irq_lock); - } - - ret = sdw_slave_wait_for_init(slave, RT711_PROBE_TIMEOUT); -diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c ---- a/sound/soc/codecs/rt711.c -+++ b/sound/soc/codecs/rt711.c -@@ -6,6 +6,7 @@ - // - // - -+#include - #include - #include - #include -@@ -89,24 +90,24 @@ static int rt711_calibration(struct rt711_priv *rt711) - struct regmap *regmap = rt711->regmap; - int ret = 0; - -- mutex_lock(&rt711->calibrate_mutex); -+ guard(mutex)(&rt711->calibrate_mutex); - regmap_write(rt711->regmap, -- RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); -+ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); - - dev = regmap_get_device(regmap); - - /* Calibration manual mode */ - rt711_index_update_bits(regmap, RT711_VENDOR_REG, RT711_FSM_CTL, -- 0xf, 0x0); -+ 0xf, 0x0); - - /* trigger */ - rt711_index_update_bits(regmap, RT711_VENDOR_CALI, -- RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_CALI_TRIGGER, -- RT711_DAC_DC_CALI_TRIGGER); -+ RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_CALI_TRIGGER, -+ RT711_DAC_DC_CALI_TRIGGER); - - /* wait for calibration process */ - rt711_index_read(regmap, RT711_VENDOR_CALI, -- RT711_DAC_DC_CALI_CTL1, &val); -+ RT711_DAC_DC_CALI_CTL1, &val); - - while (val & RT711_DAC_DC_CALI_TRIGGER) { - if (loop >= 500) { -@@ -119,16 +120,15 @@ static int rt711_calibration(struct rt711_priv *rt711) - - usleep_range(10000, 11000); - rt711_index_read(regmap, RT711_VENDOR_CALI, -- RT711_DAC_DC_CALI_CTL1, &val); -+ RT711_DAC_DC_CALI_CTL1, &val); - } - - /* depop mode */ - rt711_index_update_bits(regmap, RT711_VENDOR_REG, -- RT711_FSM_CTL, 0xf, RT711_DEPOP_CTL); -+ RT711_FSM_CTL, 0xf, RT711_DEPOP_CTL); - - regmap_write(rt711->regmap, -- RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); -- mutex_unlock(&rt711->calibrate_mutex); -+ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); - - dev_dbg(dev, "%s calibration complete, ret=%d\n", __func__, ret); - return ret; -@@ -362,24 +362,24 @@ static void rt711_jack_init(struct rt711_priv *rt711) - { - struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt711->component); - -- mutex_lock(&rt711->calibrate_mutex); -+ guard(mutex)(&rt711->calibrate_mutex); - /* power on */ - if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY) - regmap_write(rt711->regmap, -- RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); -+ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); - - if (rt711->hs_jack) { - /* unsolicited response & IRQ control */ - regmap_write(rt711->regmap, -- RT711_SET_MIC2_UNSOLICITED_ENABLE, 0x82); -+ RT711_SET_MIC2_UNSOLICITED_ENABLE, 0x82); - regmap_write(rt711->regmap, -- RT711_SET_HP_UNSOLICITED_ENABLE, 0x81); -+ RT711_SET_HP_UNSOLICITED_ENABLE, 0x81); - regmap_write(rt711->regmap, -- RT711_SET_INLINE_UNSOLICITED_ENABLE, 0x83); -+ RT711_SET_INLINE_UNSOLICITED_ENABLE, 0x83); - rt711_index_write(rt711->regmap, RT711_VENDOR_REG, -- 0x10, 0x2420); -+ 0x10, 0x2420); - rt711_index_write(rt711->regmap, RT711_VENDOR_REG, -- 0x19, 0x2e11); -+ 0x19, 0x2e11); - - switch (rt711->jd_src) { - case RT711_JD1: -@@ -449,8 +449,7 @@ static void rt711_jack_init(struct rt711_priv *rt711) - /* power off */ - if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY) - regmap_write(rt711->regmap, -- RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); -- mutex_unlock(&rt711->calibrate_mutex); -+ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); - } - - static int rt711_set_jack_detect(struct snd_soc_component *component, -@@ -511,7 +510,7 @@ static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol, - unsigned int read_ll, read_rl; - int i; - -- mutex_lock(&rt711->calibrate_mutex); -+ guard(mutex)(&rt711->calibrate_mutex); - - /* Can't use update bit function, so read the original value first */ - addr_h = mc->reg; -@@ -599,7 +598,6 @@ static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol, - regmap_write(rt711->regmap, - RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); - -- mutex_unlock(&rt711->calibrate_mutex); - return 0; - } - -@@ -908,11 +906,11 @@ static int rt711_set_bias_level(struct snd_soc_component *component, - break; - - case SND_SOC_BIAS_STANDBY: -- mutex_lock(&rt711->calibrate_mutex); -- regmap_write(rt711->regmap, -- RT711_SET_AUDIO_POWER_STATE, -- AC_PWRST_D3); -- mutex_unlock(&rt711->calibrate_mutex); -+ scoped_guard(mutex, &rt711->calibrate_mutex) { -+ regmap_write(rt711->regmap, -+ RT711_SET_AUDIO_POWER_STATE, -+ AC_PWRST_D3); -+ } - break; - - default: -diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c ---- a/sound/soc/codecs/rt712-sdca-dmic.c -+++ b/sound/soc/codecs/rt712-sdca-dmic.c -@@ -916,10 +916,23 @@ static int rt712_sdca_dmic_dev_resume(struct device *dev) - } - - regcache_cache_only(rt712->regmap, false); -- regcache_sync(rt712->regmap); -+ ret = regcache_sync(rt712->regmap); -+ if (ret) -+ goto err_sync; -+ - regcache_cache_only(rt712->mbq_regmap, false); -- regcache_sync(rt712->mbq_regmap); -+ ret = regcache_sync(rt712->mbq_regmap); -+ if (ret) -+ goto err_sync; -+ - return 0; -+ -+err_sync: -+ regcache_cache_only(rt712->regmap, true); -+ regcache_cache_only(rt712->mbq_regmap, true); -+ regcache_mark_dirty(rt712->regmap); -+ regcache_mark_dirty(rt712->mbq_regmap); -+ return ret; - } - - static const struct dev_pm_ops rt712_sdca_dmic_pm = { -diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c ---- a/sound/soc/codecs/rt712-sdca-sdw.c -+++ b/sound/soc/codecs/rt712-sdca-sdw.c -@@ -6,6 +6,7 @@ - // - // - -+#include - #include - #include - #include -@@ -427,13 +428,13 @@ static int rt712_sdca_dev_system_suspend(struct device *dev) - * deferred work completes and before the parent disables - * interrupts on the link - */ -- mutex_lock(&rt712_sdca->disable_irq_lock); -- rt712_sdca->disable_irq = true; -- ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, -- SDW_SCP_SDCA_INTMASK_SDCA_0, 0); -- ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, -- SDW_SCP_SDCA_INTMASK_SDCA_8, 0); -- mutex_unlock(&rt712_sdca->disable_irq_lock); -+ scoped_guard(mutex, &rt712_sdca->disable_irq_lock) { -+ rt712_sdca->disable_irq = true; -+ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, -+ SDW_SCP_SDCA_INTMASK_SDCA_0, 0); -+ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, -+ SDW_SCP_SDCA_INTMASK_SDCA_8, 0); -+ } - - if (ret1 < 0 || ret2 < 0) { - /* log but don't prevent suspend from happening */ -@@ -455,14 +456,15 @@ static int rt712_sdca_dev_resume(struct device *dev) - return 0; - - if (!slave->unattach_request) { -- mutex_lock(&rt712->disable_irq_lock); -- if (rt712->disable_irq == true) { -- -- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); -- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); -- rt712->disable_irq = false; -+ scoped_guard(mutex, &rt712->disable_irq_lock) { -+ if (rt712->disable_irq) { -+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, -+ SDW_SCP_SDCA_INTMASK_SDCA_0); -+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, -+ SDW_SCP_SDCA_INTMASK_SDCA_8); -+ rt712->disable_irq = false; -+ } - } -- mutex_unlock(&rt712->disable_irq_lock); - } - - ret = sdw_slave_wait_for_init(slave, RT712_PROBE_TIMEOUT); -@@ -472,9 +474,20 @@ static int rt712_sdca_dev_resume(struct device *dev) - } - - regcache_cache_only(rt712->regmap, false); -- regcache_sync(rt712->regmap); -+ ret = regcache_sync(rt712->regmap); -+ if (ret) { -+ regcache_cache_only(rt712->regmap, true); -+ return ret; -+ } -+ - regcache_cache_only(rt712->mbq_regmap, false); -- regcache_sync(rt712->mbq_regmap); -+ ret = regcache_sync(rt712->mbq_regmap); -+ if (ret) { -+ regcache_cache_only(rt712->mbq_regmap, true); -+ regcache_cache_only(rt712->regmap, true); -+ return ret; -+ } -+ - return 0; - } - -diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c ---- a/sound/soc/codecs/rt712-sdca.c -+++ b/sound/soc/codecs/rt712-sdca.c -@@ -7,6 +7,7 @@ - // - - #include -+#include - #include - #include - #include -@@ -403,7 +404,7 @@ static void rt712_sdca_btn_check_handler(struct work_struct *work) - - static void rt712_sdca_jack_init(struct rt712_sdca_priv *rt712) - { -- mutex_lock(&rt712->calibrate_mutex); -+ guard(mutex)(&rt712->calibrate_mutex); - - if (rt712->hs_jack) { - /* Enable HID1 event & set button RTC mode */ -@@ -450,8 +451,6 @@ static void rt712_sdca_jack_init(struct rt712_sdca_priv *rt712) - - dev_dbg(&rt712->slave->dev, "in %s disable\n", __func__); - } -- -- mutex_unlock(&rt712->calibrate_mutex); - } - - static int rt712_sdca_set_jack_detect(struct snd_soc_component *component, -diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c ---- a/sound/soc/codecs/rt721-sdca-sdw.c -+++ b/sound/soc/codecs/rt721-sdca-sdw.c -@@ -6,6 +6,7 @@ - // - // - -+#include - #include - #include - #include -@@ -466,13 +467,13 @@ static int rt721_sdca_dev_system_suspend(struct device *dev) - * deferred work completes and before the parent disables - * interrupts on the link - */ -- mutex_lock(&rt721_sdca->disable_irq_lock); -- rt721_sdca->disable_irq = true; -- ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, -- SDW_SCP_SDCA_INTMASK_SDCA_0, 0); -- ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, -- SDW_SCP_SDCA_INTMASK_SDCA_8, 0); -- mutex_unlock(&rt721_sdca->disable_irq_lock); -+ scoped_guard(mutex, &rt721_sdca->disable_irq_lock) { -+ rt721_sdca->disable_irq = true; -+ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, -+ SDW_SCP_SDCA_INTMASK_SDCA_0, 0); -+ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, -+ SDW_SCP_SDCA_INTMASK_SDCA_8, 0); -+ } - - if (ret1 < 0 || ret2 < 0) { - /* log but don't prevent suspend from happening */ -@@ -494,13 +495,15 @@ static int rt721_sdca_dev_resume(struct device *dev) - return 0; - - if (!slave->unattach_request) { -- mutex_lock(&rt721->disable_irq_lock); -- if (rt721->disable_irq == true) { -- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); -- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); -- rt721->disable_irq = false; -+ scoped_guard(mutex, &rt721->disable_irq_lock) { -+ if (rt721->disable_irq) { -+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, -+ SDW_SCP_SDCA_INTMASK_SDCA_0); -+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, -+ SDW_SCP_SDCA_INTMASK_SDCA_8); -+ rt721->disable_irq = false; -+ } - } -- mutex_unlock(&rt721->disable_irq_lock); - } - - ret = sdw_slave_wait_for_init(slave, RT721_PROBE_TIMEOUT); -@@ -510,9 +513,20 @@ static int rt721_sdca_dev_resume(struct device *dev) - } - - regcache_cache_only(rt721->regmap, false); -- regcache_sync(rt721->regmap); -+ ret = regcache_sync(rt721->regmap); -+ if (ret) { -+ regcache_cache_only(rt721->regmap, true); -+ return ret; -+ } -+ - regcache_cache_only(rt721->mbq_regmap, false); -- regcache_sync(rt721->mbq_regmap); -+ ret = regcache_sync(rt721->mbq_regmap); -+ if (ret) { -+ regcache_cache_only(rt721->mbq_regmap, true); -+ regcache_cache_only(rt721->regmap, true); -+ return ret; -+ } -+ - return 0; - } - -diff --git a/sound/soc/codecs/rt721-sdca.c b/sound/soc/codecs/rt721-sdca.c ---- a/sound/soc/codecs/rt721-sdca.c -+++ b/sound/soc/codecs/rt721-sdca.c -@@ -5,7 +5,7 @@ - // Copyright(c) 2024 Realtek Semiconductor Corp. - // - // -- -+#include - #include - #include - #include -@@ -289,7 +289,7 @@ static void rt721_sdca_jack_preset(struct rt721_sdca_priv *rt721) - - static void rt721_sdca_jack_init(struct rt721_sdca_priv *rt721) - { -- mutex_lock(&rt721->calibrate_mutex); -+ guard(mutex)(&rt721->calibrate_mutex); - if (rt721->hs_jack) { - sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INTMASK1, - SDW_SCP_SDCA_INTMASK_SDCA_0); -@@ -309,7 +309,6 @@ static void rt721_sdca_jack_init(struct rt721_sdca_priv *rt721) - rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT, - RT721_GE_REL_CTRL1, 0x4000, 0x4000); - } -- mutex_unlock(&rt721->calibrate_mutex); - } - - static int rt721_sdca_set_jack_detect(struct snd_soc_component *component, -diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c ---- a/sound/soc/codecs/rt722-sdca-sdw.c -+++ b/sound/soc/codecs/rt722-sdca-sdw.c -@@ -6,6 +6,7 @@ - // - // - -+#include - #include - #include - #include -@@ -517,13 +518,13 @@ static int rt722_sdca_dev_system_suspend(struct device *dev) - * deferred work completes and before the parent disables - * interrupts on the link - */ -- mutex_lock(&rt722_sdca->disable_irq_lock); -- rt722_sdca->disable_irq = true; -- ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, -- SDW_SCP_SDCA_INTMASK_SDCA_0, 0); -- ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, -- SDW_SCP_SDCA_INTMASK_SDCA_8, 0); -- mutex_unlock(&rt722_sdca->disable_irq_lock); -+ scoped_guard(mutex, &rt722_sdca->disable_irq_lock) { -+ rt722_sdca->disable_irq = true; -+ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, -+ SDW_SCP_SDCA_INTMASK_SDCA_0, 0); -+ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, -+ SDW_SCP_SDCA_INTMASK_SDCA_8, 0); -+ } - - if (ret1 < 0 || ret2 < 0) { - /* log but don't prevent suspend from happening */ -@@ -545,13 +546,15 @@ static int rt722_sdca_dev_resume(struct device *dev) - return 0; - - if (!slave->unattach_request) { -- mutex_lock(&rt722->disable_irq_lock); -- if (rt722->disable_irq == true) { -- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); -- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); -- rt722->disable_irq = false; -+ scoped_guard(mutex, &rt722->disable_irq_lock) { -+ if (rt722->disable_irq) { -+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, -+ SDW_SCP_SDCA_INTMASK_SDCA_0); -+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, -+ SDW_SCP_SDCA_INTMASK_SDCA_8); -+ rt722->disable_irq = false; -+ } - } -- mutex_unlock(&rt722->disable_irq_lock); - } - - ret = sdw_slave_wait_for_init(slave, RT722_PROBE_TIMEOUT); -@@ -561,7 +564,13 @@ static int rt722_sdca_dev_resume(struct device *dev) - } - - regcache_cache_only(rt722->regmap, false); -- regcache_sync(rt722->regmap); -+ ret = regcache_sync(rt722->regmap); -+ if (ret) { -+ regcache_cache_only(rt722->regmap, true); -+ regcache_mark_dirty(rt722->regmap); -+ return ret; -+ } -+ - return 0; - } - -diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c ---- a/sound/soc/codecs/rt722-sdca.c -+++ b/sound/soc/codecs/rt722-sdca.c -@@ -6,6 +6,7 @@ - // - // - -+#include - #include - #include - #include -@@ -294,7 +295,7 @@ static void rt722_sdca_btn_check_handler(struct work_struct *work) - - static void rt722_sdca_jack_init(struct rt722_sdca_priv *rt722) - { -- mutex_lock(&rt722->calibrate_mutex); -+ guard(mutex)(&rt722->calibrate_mutex); - if (rt722->hs_jack) { - /* set SCP_SDCA_IntMask1[0]=1 */ - sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK1, -@@ -317,7 +318,6 @@ static void rt722_sdca_jack_init(struct rt722_sdca_priv *rt722) - rt722_sdca_index_update_bits(rt722, RT722_VENDOR_HDA_CTL, - RT722_GE_RELATED_CTL2, 0x4000, 0x4000); - } -- mutex_unlock(&rt722->calibrate_mutex); - } - - static int rt722_sdca_set_jack_detect(struct snd_soc_component *component, -@@ -352,8 +352,6 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) - static const char func_tag[] = "FUNC"; - static const char xu_tag[] = "XU"; - const char *dmi_vendor, *dmi_product, *dmi_sku; -- char *cae_filename; -- const struct firmware *cae_fw = NULL; - unsigned int cae_st_spk, cae_st_hp, cae_st_mic; - unsigned int func, value; - unsigned int combined_val; -@@ -385,7 +383,8 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) - space = strchr(dmi_sku, ' '); - s_len = space ? space - dmi_sku : strlen(dmi_sku); - -- cae_filename = kasprintf(GFP_KERNEL, -+ char *cae_filename __free(kfree) = -+ kasprintf(GFP_KERNEL, - "realtek/rt722/rt722_RAE_%.*s_%.*s_%.*s.dat", - v_len, dmi_vendor, - p_len, dmi_product, -@@ -399,8 +398,8 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) - regmap_write(rt722->regmap, RT722_MIC_CAE_PARAM39, 0x5f); - usleep_range(50000, 60000); - -+ const struct firmware *cae_fw __free(firmware) = NULL; - request_firmware(&cae_fw, cae_filename, dev); -- kfree(cae_filename); - if (!cae_fw) { - dev_err(dev, "%s: Failed to load CAE firmware\n", __func__); - return -ENOENT; -@@ -555,7 +554,6 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) - regcache_cache_bypass(rt722->regmap, false); - rt722->cae_update_done = 1; - dev_dbg(dev, "%s: CAE FW update done.\n", __func__); -- release_firmware(cae_fw); - return 0; - - verify_abort: -@@ -565,7 +563,6 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) - out_release: - rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG, - RT722_MISC_CTRL1, 0x8000, 0x0000); -- release_firmware(cae_fw); - dev_err(dev, "%s: CAE FW update aborted (ret=%d).\n", __func__, ret); - return ret; - } -@@ -1906,6 +1903,9 @@ int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave) - rt722_sdca_amp_preset(rt722); - rt722_sdca_jack_preset(rt722); - -+ if (rt722->hs_jack && (!rt722->first_hw_init)) -+ rt722_sdca_jack_init(rt722); -+ - if (rt722->first_hw_init) { - regcache_cache_bypass(rt722->regmap, false); - regcache_mark_dirty(rt722->regmap); -diff --git a/sound/soc/codecs/rt766-sdca-sdw.c b/sound/soc/codecs/rt766-sdca-sdw.c -new file mode 100644 ---- /dev/null -+++ b/sound/soc/codecs/rt766-sdca-sdw.c -@@ -0,0 +1,339 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+// -+// rt766-sdca-sdw.c -- rt766 SDCA ALSA SoC audio driver -+// -+// Copyright(c) 2026 Realtek Semiconductor Corp. -+// -+// -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "rt766-sdca.h" -+#include "rt766-sdca-sdw.h" -+ -+#define RT766_PROBE_TIMEOUT 5000 -+ -+static bool rt766_sdca_readable_register(struct device *dev, unsigned int reg) -+{ -+ switch (reg) { -+ case SDW_SCP_SDCA_INT1 ... SDW_SCP_SDCA_INTMASK4: -+ case RT766_VERSION_ID ... RT766_BOND_LATCH_ID: -+ case 0xc344 ... 0xc345: -+ case 0xc900: -+ case 0xc920: -+ case 0xd540 ... 0xd542: -+ case 0xf01e: -+ case RT766_HP_POWER_STATE ... RT766_HP_FSM_CTL2_1: -+ case 0x310100: -+ case RT766_MCU_PATCH_ADDR1_START ... RT766_MCU_PATCH_ADDR1_END: -+ case RT766_MCU_PATCH_ADDR2_START ... RT766_MCU_PATCH_ADDR2_END: -+ case RT766_MUTE_REG(UAJ, USER_FU41, 1): -+ case RT766_MUTE_REG(UAJ, USER_FU41, 2): -+ case RT766_VOLUME_REG(UAJ, USER_FU41, 1): -+ case RT766_VOLUME_REG(UAJ, USER_FU41, 2): -+ case RT766_MUTE_REG(UAJ, USER_FU36, 1): -+ case RT766_MUTE_REG(UAJ, USER_FU36, 2): -+ case RT766_VOLUME_REG(UAJ, USER_FU36, 1): -+ case RT766_VOLUME_REG(UAJ, USER_FU36, 2): -+ case RT766_PDE_REQ_REG(UAJ, PDE47): -+ case RT766_PDE_REQ_REG(UAJ, PDE34): -+ case RT766_SDCA_CTL(UAJ, CS41, SDCA_CTL_CS_SAMPLERATEINDEX): -+ case RT766_SDCA_CTL(UAJ, CS36, SDCA_CTL_CS_SAMPLERATEINDEX): -+ case RT766_FUNC_STATUS_REG(UAJ): /* 0x40480000 */ -+ case RT766_PDE_ACTUAL_REG(UAJ, PDE47): /* 0x40481400 */ -+ case RT766_PDE_ACTUAL_REG(UAJ, PDE34): /* 0x40481480 */ -+ case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1): -+ case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2): -+ case RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_SELECTED_MODE): -+ case RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_DETECTED_MODE): /* 0x40600490 */ -+ case RT766_PDE_REQ_REG(MIC, PDE11): -+ case RT766_MUTE_REG(MIC, USER_FU113, 1): -+ case RT766_MUTE_REG(MIC, USER_FU113, 2): -+ case RT766_MUTE_REG(MIC, USER_FU113, 3): -+ case RT766_MUTE_REG(MIC, USER_FU113, 4): -+ case RT766_VOLUME_REG(MIC, USER_FU113, 1): -+ case RT766_VOLUME_REG(MIC, USER_FU113, 2): -+ case RT766_VOLUME_REG(MIC, USER_FU113, 3): -+ case RT766_VOLUME_REG(MIC, USER_FU113, 4): -+ case RT766_FUNC_STATUS_REG(MIC): /* 0x40880000 */ -+ case RT766_SDCA_CTL(MIC, CS113, SDCA_CTL_CS_SAMPLERATEINDEX): -+ case RT766_PDE_ACTUAL_REG(MIC, PDE11): /* 0x40881500 */ -+ case RT766_FUNC_STATUS_REG(HID): /* 0x40c80000 */ -+ /* 0x40c80080 - 0x40c80098 */ -+ case RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_CURRENTOWNER) ... -+ RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_MESSAGELENGTH): -+ case RT766_MUTE_REG(AMP, USER_FU21, 1): -+ case RT766_MUTE_REG(AMP, USER_FU21, 2): -+ case RT766_VOLUME_REG(AMP, USER_FU21, 1): -+ case RT766_VOLUME_REG(AMP, USER_FU21, 2): -+ case RT766_PDE_REQ_REG(AMP, PDE23): -+ case RT766_FUNC_STATUS_REG(AMP): /* 0x41080000 */ -+ case RT766_SDCA_CTL(AMP, PPU21, SDCA_CTL_PPU_POSTURENUMBER): -+ case RT766_SDCA_CTL(AMP, CS21, SDCA_CTL_CS_SAMPLERATEINDEX): -+ case RT766_PDE_ACTUAL_REG(AMP, PDE23): /* 0x41081980 */ -+ case RT766_BUF_ADDR_HID1 ... RT766_BUF_ADDR_HID2: -+ return true; -+ default: -+ return false; -+ } -+} -+ -+static bool rt766_sdca_volatile_register(struct device *dev, unsigned int reg) -+{ -+ switch (reg) { -+ case SDW_SCP_SDCA_INT1 ... SDW_SCP_SDCA_INTMASK4: -+ case RT766_VERSION_ID ... RT766_BOND_LATCH_ID: -+ case 0xc344 ... 0xc345: -+ case 0xc900: -+ case 0xc920: -+ case 0xd540 ... 0xd542: -+ case 0xf01e: -+ case RT766_HP_POWER_STATE ... RT766_HP_FSM_CTL2_1: -+ case 0x310100: -+ case RT766_MCU_PATCH_ADDR1_START ... RT766_MCU_PATCH_ADDR1_END: -+ case RT766_MCU_PATCH_ADDR2_START ... RT766_MCU_PATCH_ADDR2_END: -+ case RT766_FUNC_STATUS_REG(UAJ): -+ case RT766_PDE_ACTUAL_REG(UAJ, PDE47): -+ case RT766_PDE_ACTUAL_REG(UAJ, PDE34): -+ case RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_DETECTED_MODE): -+ case RT766_FUNC_STATUS_REG(MIC): -+ case RT766_PDE_ACTUAL_REG(MIC, PDE11): -+ case RT766_FUNC_STATUS_REG(HID): -+ case RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_CURRENTOWNER) ... -+ RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_MESSAGELENGTH): -+ case RT766_FUNC_STATUS_REG(AMP): -+ case RT766_PDE_ACTUAL_REG(AMP, PDE23): -+ case RT766_BUF_ADDR_HID1 ... RT766_BUF_ADDR_HID2: -+ return true; -+ default: -+ return false; -+ } -+} -+ -+static int rt766_sdca_mbq_size(struct device *dev, unsigned int reg) -+{ -+ switch (reg) { -+ case RT766_VOLUME_REG(UAJ, USER_FU41, 1): -+ case RT766_VOLUME_REG(UAJ, USER_FU41, 2): -+ case RT766_VOLUME_REG(UAJ, USER_FU36, 1): -+ case RT766_VOLUME_REG(UAJ, USER_FU36, 2): -+ case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1): -+ case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2): -+ case RT766_VOLUME_REG(MIC, USER_FU113, 1): -+ case RT766_VOLUME_REG(MIC, USER_FU113, 2): -+ case RT766_VOLUME_REG(MIC, USER_FU113, 3): -+ case RT766_VOLUME_REG(MIC, USER_FU113, 4): -+ case RT766_VOLUME_REG(AMP, USER_FU21, 1): -+ case RT766_VOLUME_REG(AMP, USER_FU21, 2): -+ return 2; -+ default: -+ return 1; -+ } -+} -+ -+static const struct regmap_sdw_mbq_cfg rt766_sdca_mbq_cfg = { -+ .mbq_size = rt766_sdca_mbq_size, -+}; -+ -+static const struct regmap_config rt766_sdca_regmap = { -+ .reg_bits = 32, -+ .val_bits = 16, -+ .readable_reg = rt766_sdca_readable_register, -+ .volatile_reg = rt766_sdca_volatile_register, -+ .reg_defaults = rt766_sdca_defaults, -+ .num_reg_defaults = ARRAY_SIZE(rt766_sdca_defaults), -+ .max_register = SDW_SDCA_MAX_REGISTER, -+ .cache_type = REGCACHE_MAPLE, -+ .use_single_read = true, -+ .use_single_write = true, -+}; -+ -+static int rt766_sdca_update_status(struct sdw_slave *slave, -+ enum sdw_slave_status status) -+{ -+ struct rt766_sdca_priv *rt766 = dev_get_drvdata(&slave->dev); -+ -+ if (status == SDW_SLAVE_UNATTACHED) -+ rt766->hw_init = false; -+ -+ if (status == SDW_SLAVE_ATTACHED) { -+ if (rt766->hs_jack) { -+ /* -+ * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then -+ * if the device attached again, we will need to set the setting back. -+ * It could avoid losing the jack detection interrupt. -+ * This also could sync with the cache value as the rt766_sdca_jack_init set. -+ */ -+ sdw_write_no_pm(rt766->slave, SDW_SCP_SDCA_INTMASK3, -+ SDW_SCP_SDCA_INTMASK_SDCA_16); -+ sdw_write_no_pm(rt766->slave, SDW_SCP_SDCA_INTMASK4, -+ SDW_SCP_SDCA_INTMASK_SDCA_24); -+ } -+ } -+ -+ /* -+ * Perform initialization only if slave status is present and -+ * hw_init flag is false -+ */ -+ if (rt766->hw_init || status != SDW_SLAVE_ATTACHED) -+ return 0; -+ -+ /* perform I/O transfers required for Slave initialization */ -+ return rt766_sdca_io_init(&slave->dev, slave); -+} -+ -+static int rt766_sdca_read_prop(struct sdw_slave *slave) -+{ -+ struct sdw_slave_prop *prop = &slave->prop; -+ int ret; -+ -+ ret = sdw_slave_read_prop(slave); -+ if (ret < 0) -+ return ret; -+ -+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; -+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; -+ /* -+ * SDCA interrupts are routed through SoundWire domain IRQ. -+ */ -+ prop->use_domain_irq = true; -+ -+ return 0; -+} -+ -+static const struct sdw_slave_ops rt766_sdca_slave_ops = { -+ .read_prop = rt766_sdca_read_prop, -+ .update_status = rt766_sdca_update_status, -+}; -+ -+static int rt766_sdca_sdw_probe(struct sdw_slave *slave, -+ const struct sdw_device_id *id) -+{ -+ struct regmap *regmap; -+ -+ /* Regmap Initialization */ -+ regmap = devm_regmap_init_sdw_mbq_cfg(&slave->dev, slave, -+ &rt766_sdca_regmap, &rt766_sdca_mbq_cfg); -+ if (IS_ERR(regmap)) -+ return PTR_ERR(regmap); -+ -+ return rt766_sdca_init(&slave->dev, regmap, slave); -+} -+ -+static void rt766_sdca_sdw_remove(struct sdw_slave *slave) -+{ -+ pm_runtime_disable(&slave->dev); -+} -+ -+static const struct sdw_device_id rt766_sdca_id[] = { -+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x766, 0x3, 0x1, 0), -+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x767, 0x3, 0x1, 0), -+ {}, -+}; -+MODULE_DEVICE_TABLE(sdw, rt766_sdca_id); -+ -+static int rt766_sdca_dev_suspend(struct device *dev) -+{ -+ struct rt766_sdca_priv *rt766 = dev_get_drvdata(dev); -+ -+ if (!rt766->hw_init) -+ return 0; -+ -+ regcache_cache_only(rt766->regmap, true); -+ return 0; -+} -+ -+static int rt766_sdca_dev_system_suspend(struct device *dev) -+{ -+ struct rt766_sdca_priv *rt766 = dev_get_drvdata(dev); -+ struct sdw_slave *slave = dev_to_sdw_dev(dev); -+ int ret1, ret2; -+ -+ if (!rt766->hw_init) -+ return 0; -+ -+ /* -+ * prevent new interrupts from being handled after the -+ * deferred work completes and before the parent disables -+ * interrupts on the link -+ */ -+ mutex_lock(&rt766->disable_irq_lock); -+ rt766->disable_irq = true; -+ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK3, -+ SDW_SCP_SDCA_INTMASK_SDCA_16, 0); -+ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK4, -+ SDW_SCP_SDCA_INTMASK_SDCA_24, 0); -+ mutex_unlock(&rt766->disable_irq_lock); -+ -+ if (ret1 < 0 || ret2 < 0) { -+ /* log but don't prevent suspend from happening */ -+ dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__); -+ } -+ -+ return rt766_sdca_dev_suspend(dev); -+} -+ -+static int rt766_sdca_dev_resume(struct device *dev) -+{ -+ struct sdw_slave *slave = dev_to_sdw_dev(dev); -+ struct rt766_sdca_priv *rt766 = dev_get_drvdata(dev); -+ int ret; -+ -+ if (!rt766->first_hw_init) -+ return 0; -+ -+ if (!slave->unattach_request) { -+ mutex_lock(&rt766->disable_irq_lock); -+ if (rt766->disable_irq == true) { -+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK3, SDW_SCP_SDCA_INTMASK_SDCA_16); -+ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK4, SDW_SCP_SDCA_INTMASK_SDCA_24); -+ rt766->disable_irq = false; -+ } -+ mutex_unlock(&rt766->disable_irq_lock); -+ goto regmap_sync; -+ } -+ -+ ret = sdw_slave_wait_for_init(slave, RT766_PROBE_TIMEOUT); -+ if (ret) { -+ sdw_show_ping_status(slave->bus, true); -+ return ret; -+ } -+ -+regmap_sync: -+ regcache_cache_only(rt766->regmap, false); -+ ret = regcache_sync(rt766->regmap); -+ if (ret) { -+ regcache_cache_only(rt766->regmap, true); -+ regcache_mark_dirty(rt766->regmap); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static const struct dev_pm_ops rt766_sdca_pm = { -+ SYSTEM_SLEEP_PM_OPS(rt766_sdca_dev_system_suspend, rt766_sdca_dev_resume) -+ RUNTIME_PM_OPS(rt766_sdca_dev_suspend, rt766_sdca_dev_resume, NULL) -+}; -+ -+static struct sdw_driver rt766_sdca_sdw_driver = { -+ .driver = { -+ .name = "rt766-sdca", -+ .pm = pm_ptr(&rt766_sdca_pm), -+ }, -+ .probe = rt766_sdca_sdw_probe, -+ .remove = rt766_sdca_sdw_remove, -+ .ops = &rt766_sdca_slave_ops, -+ .id_table = rt766_sdca_id, -+}; -+module_sdw_driver(rt766_sdca_sdw_driver); -+ -+MODULE_DESCRIPTION("ASoC RT766 SDCA SDW driver"); -+MODULE_AUTHOR("Shuming Fan "); -+MODULE_LICENSE("GPL"); -+MODULE_IMPORT_NS("SND_SOC_SDCA"); -diff --git a/sound/soc/codecs/rt766-sdca-sdw.h b/sound/soc/codecs/rt766-sdca-sdw.h -new file mode 100644 ---- /dev/null -+++ b/sound/soc/codecs/rt766-sdca-sdw.h -@@ -0,0 +1,61 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * rt766-sdca-sdw.h -- RT766 SDCA ALSA SoC audio driver header -+ * -+ * Copyright(c) 2026 Realtek Semiconductor Corp. -+ */ -+ -+#ifndef __RT766_SDW_H__ -+#define __RT766_SDW_H__ -+ -+#include -+#include -+ -+static const struct reg_default rt766_sdca_defaults[] = { -+ /* 0x40400289 - 0x4040028a */ -+ { RT766_MUTE_REG(UAJ, USER_FU41, 1), 0x01 }, -+ { RT766_MUTE_REG(UAJ, USER_FU41, 2), 0x01 }, -+ /* 0x40400291 - 0x40400292 */ -+ { RT766_VOLUME_REG(UAJ, USER_FU41, 1), 0x0000 }, -+ { RT766_VOLUME_REG(UAJ, USER_FU41, 2), 0x0000 }, -+ /* 0x40400789 - 0x4040078a */ -+ { RT766_MUTE_REG(UAJ, USER_FU36, 1), 0x01 }, -+ { RT766_MUTE_REG(UAJ, USER_FU36, 2), 0x01 }, -+ /* 0x40400791 - 0x40400792 */ -+ { RT766_VOLUME_REG(UAJ, USER_FU36, 1), 0x0000 }, -+ { RT766_VOLUME_REG(UAJ, USER_FU36, 2), 0x0000 }, -+ { RT766_PDE_REQ_REG(UAJ, PDE47), 0x03 }, /* 0x40401408 */ -+ { RT766_PDE_REQ_REG(UAJ, PDE34), 0x03 }, /* 0x40401488 */ -+ { RT766_SDCA_CTL(UAJ, CS41, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 }, /* 0x40480080 */ -+ { RT766_SDCA_CTL(UAJ, CS36, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 }, /* 0x40480880 */ -+ /* 0x40600259 - 0x4060025a */ -+ { RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1), 0xfe00 }, -+ { RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2), 0xfe00 }, -+ { RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_SELECTED_MODE), 0x00 }, /* 0x40600488 */ -+ -+ { RT766_PDE_REQ_REG(MIC, PDE11), 0x03 }, /* 0x40801508 */ -+ /* 0x40801809 - 0x4080180c */ -+ { RT766_MUTE_REG(MIC, USER_FU113, 1), 0x01 }, -+ { RT766_MUTE_REG(MIC, USER_FU113, 2), 0x01 }, -+ { RT766_MUTE_REG(MIC, USER_FU113, 3), 0x01 }, -+ { RT766_MUTE_REG(MIC, USER_FU113, 4), 0x01 }, -+ /* 0x40801811 - 0x40801814 */ -+ { RT766_VOLUME_REG(MIC, USER_FU113, 1), 0x0000 }, -+ { RT766_VOLUME_REG(MIC, USER_FU113, 2), 0x0000 }, -+ { RT766_VOLUME_REG(MIC, USER_FU113, 3), 0x0000 }, -+ { RT766_VOLUME_REG(MIC, USER_FU113, 4), 0x0000 }, -+ { RT766_SDCA_CTL(MIC, CS113, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 }, /* 0x40880900 */ -+ -+ /* 0x41000189 - 0x4100018a */ -+ { RT766_MUTE_REG(AMP, USER_FU21, 1), 0x01 }, -+ { RT766_MUTE_REG(AMP, USER_FU21, 2), 0x01 }, -+ /* 0x41000191 - 0x41000192 */ -+ { RT766_VOLUME_REG(AMP, USER_FU21, 1), 0x0000 }, -+ { RT766_VOLUME_REG(AMP, USER_FU21, 2), 0x0000 }, -+ { RT766_PDE_REQ_REG(AMP, PDE23), 0x03 }, /* 0x41001988 */ -+ { RT766_SDCA_CTL(AMP, PPU21, SDCA_CTL_PPU_POSTURENUMBER), 0x00 }, /* 0x41080200 */ -+ { RT766_SDCA_CTL(AMP, CS21, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 }, /* 0x41081080 */ -+ -+}; -+ -+#endif /* __RT766_SDW_H__ */ -diff --git a/sound/soc/codecs/rt766-sdca.c b/sound/soc/codecs/rt766-sdca.c -new file mode 100644 ---- /dev/null -+++ b/sound/soc/codecs/rt766-sdca.c -@@ -0,0 +1,1378 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+// -+// rt766-sdca.c -- rt766 SDCA ALSA SoC audio driver -+// -+// Copyright(c) 2026 Realtek Semiconductor Corp. -+// -+// -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "rt766-sdca.h" -+#include "rt-sdw-common.h" -+ -+static int rt766_sdca_btn_detect(struct sdca_interrupt *interrupt) -+{ -+ struct rt766_sdca_priv *rt766 = interrupt->priv; -+ unsigned char *buf = NULL; -+ unsigned int offset, owner, length; -+ unsigned int det_mode, idx, val; -+ int ret; -+ -+ ret = regmap_read(rt766->regmap, -+ RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_DETECTED_MODE), -+ &det_mode); -+ if (ret < 0) -+ goto io_error; -+ -+ /* get current UMP message owner */ -+ ret = regmap_read(rt766->regmap, -+ RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_CURRENTOWNER), -+ &owner); -+ if (ret < 0) -+ goto io_error; -+ -+ /* if owner is device then there is no button event from device */ -+ if (owner == 1) -+ return 0; -+ -+ if (det_mode) { -+ /* read UMP message length */ -+ ret = regmap_read(rt766->regmap, -+ RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_MESSAGELENGTH), -+ &length); -+ if (ret < 0) -+ goto _end_btn_det_; -+ -+ /* read UMP message offset */ -+ ret = regmap_read(rt766->regmap, -+ RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_MESSAGEOFFSET), -+ &offset); -+ if (ret < 0) -+ goto _end_btn_det_; -+ -+ buf = devm_kzalloc(&rt766->slave->dev, length, GFP_KERNEL); -+ if (!buf) { -+ dev_err(&rt766->slave->dev, "%s: alloc buf failed\n", __func__); -+ goto _end_btn_det_; -+ } -+ -+ for (idx = 0; idx < length; idx++) { -+ ret = regmap_read(rt766->regmap, -+ RT766_BUF_ADDR_HID1 + offset + idx, &val); -+ if (ret < 0) -+ goto _end_btn_det_; -+ buf[idx] = val & 0xff; -+ } -+ -+ if (rt766->hid) -+ hid_input_report(rt766->hid, HID_INPUT_REPORT, -+ buf, length, 1); -+ } -+ -+_end_btn_det_: -+ if (buf) -+ devm_kfree(&rt766->slave->dev, buf); -+ -+ /* Host is owner, so set back to device */ -+ if (owner == 0) { -+ regmap_write(rt766->regmap, -+ RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_CURRENTOWNER), 0x01); -+ } -+ -+ return 0; -+ -+io_error: -+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); -+ return ret; -+} -+ -+static irqreturn_t rt766_sdca_irq_btn_handler(int irq, void *data) -+{ -+ struct sdca_interrupt *interrupt = data; -+ struct rt766_sdca_priv *rt766 = interrupt->priv; -+ -+ if (!rt766->hs_jack) -+ return IRQ_HANDLED; -+ -+ if (!rt766->component->card || !rt766->component->card->instantiated) -+ return IRQ_HANDLED; -+ -+ mutex_lock(&rt766->disable_irq_lock); -+ if (!rt766->disable_irq) -+ rt766_sdca_btn_detect(interrupt); -+ mutex_unlock(&rt766->disable_irq_lock); -+ return IRQ_HANDLED; -+} -+ -+static int rt766_sdca_headset_detect(struct rt766_sdca_priv *rt766) -+{ -+ unsigned int det_mode; -+ int ret; -+ -+ /* get detected_mode */ -+ ret = regmap_read(rt766->regmap, -+ RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_DETECTED_MODE), -+ &det_mode); -+ if (ret < 0) -+ goto io_error; -+ -+ switch (det_mode) { -+ case 0x00: -+ rt766->jack_type = 0; -+ break; -+ case 0x03: -+ rt766->jack_type = SND_JACK_HEADPHONE; -+ break; -+ case 0x05: -+ rt766->jack_type = SND_JACK_HEADSET; -+ break; -+ } -+ -+ /* write selected_mode */ -+ if (det_mode) { -+ ret = regmap_write(rt766->regmap, -+ RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_SELECTED_MODE), -+ det_mode); -+ if (ret < 0) -+ goto io_error; -+ } -+ -+ dev_dbg(&rt766->slave->dev, -+ "%s, detected_mode=0x%x\n", __func__, det_mode); -+ -+ return 0; -+ -+io_error: -+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); -+ return ret; -+} -+ -+static irqreturn_t rt766_sdca_irq_jd_handler(int irq, void *data) -+{ -+ struct sdca_interrupt *interrupt = data; -+ struct rt766_sdca_priv *rt766 = interrupt->priv; -+ -+ if (!rt766->hs_jack) -+ return IRQ_HANDLED; -+ -+ if (!rt766->component->card || !rt766->component->card->instantiated) -+ return IRQ_HANDLED; -+ -+ mutex_lock(&rt766->disable_irq_lock); -+ if (!rt766->disable_irq) -+ rt766_sdca_headset_detect(rt766); -+ mutex_unlock(&rt766->disable_irq_lock); -+ -+ dev_dbg(&rt766->slave->dev, -+ "in %s, jack_type=%d\n", __func__, rt766->jack_type); -+ -+ snd_soc_jack_report(rt766->hs_jack, rt766->jack_type, SND_JACK_HEADSET); -+ return IRQ_HANDLED; -+} -+ -+static void rt766_sdca_destroy_hid_device(struct sdca_interrupt *interrupt) -+{ -+ struct rt766_sdca_priv *rt766 = interrupt->priv; -+ -+ hid_destroy_device(rt766->hid); -+} -+ -+static int rt766_sdca_irq_ctl(struct rt766_sdca_priv *rt766, -+ struct sdca_function_data *function, -+ struct snd_soc_component *component, -+ struct sdca_interrupt_info *info, -+ bool enabled) -+{ -+ struct device *dev = &rt766->slave->dev; -+ struct sdca_interrupt *interrupt; -+ struct sdca_control *control; -+ struct sdca_entity *entity; -+ irq_handler_t handler; -+ int i, j, irq, ret; -+ -+ for (i = 0; i < function->num_entities; i++) { -+ entity = &function->entities[i]; -+ -+ for (j = 0; j < entity->num_controls; j++) { -+ control = &entity->controls[j]; -+ irq = control->interrupt_position; -+ -+ switch (SDCA_CTL_TYPE(entity->type, control->sel)) { -+ case SDCA_CTL_TYPE_S(GE, DETECTED_MODE): -+ handler = rt766_sdca_irq_jd_handler; -+ break; -+ case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER): -+ handler = rt766_sdca_irq_btn_handler; -+ break; -+ default: -+ continue; -+ } -+ -+ interrupt = &info->irqs[irq]; -+ -+ if (enabled) { -+ ret = sdca_irq_data_populate(dev, rt766->regmap, component, -+ function, entity, control, -+ interrupt); -+ if (ret) -+ return ret; -+ -+ if (handler == rt766_sdca_irq_btn_handler) { -+ ret = sdca_add_hid_device(interrupt); -+ if (ret) -+ return ret; -+ -+ interrupt->free_priv = rt766_sdca_destroy_hid_device; -+ rt766->hid = interrupt->priv; -+ } -+ -+ interrupt->priv = rt766; -+ ret = sdca_irq_request(dev, info, irq, interrupt->name, -+ handler, interrupt); -+ if (ret) { -+ dev_err(dev, "failed to request irq %s: %d\n", -+ interrupt->name, ret); -+ sdca_irq_cleanup_late(dev, function, info); -+ return ret; -+ } -+ dev_dbg(dev, "Requesting IRQ %d InterruptName=%s\n", irq, interrupt->name); -+ } else { -+ sdca_irq_cleanup_late(dev, function, info); -+ dev_dbg(dev, "Freeing IRQ %d\n", irq); -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+static int rt766_sdca_set_jack_detect(struct snd_soc_component *component, -+ struct snd_soc_jack *hs_jack, void *data) -+{ -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ int ret; -+ -+ if (!rt766->uaj_func_data) { -+ dev_err(&rt766->slave->dev, "The SDCA UAJ function is not supported.\n"); -+ return -EINVAL; -+ } -+ -+ rt766->hs_jack = hs_jack; -+ -+ if (!rt766->first_hw_init) -+ return 0; -+ -+ ret = pm_runtime_resume_and_get(component->dev); -+ if (ret < 0) { -+ if (ret != -EACCES) { -+ dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret); -+ return ret; -+ } -+ -+ /* pm_runtime not enabled yet */ -+ dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__); -+ return 0; -+ } -+ -+ /* disable interrupts if hs_jack is not set */ -+ if (!rt766->hs_jack) { -+ if (rt766->uaj_func_data) -+ rt766_sdca_irq_ctl(rt766, rt766->uaj_func_data, -+ rt766->component, rt766->irq_info, false); -+ -+ if (rt766->hid_func_data) -+ rt766_sdca_irq_ctl(rt766, rt766->hid_func_data, -+ rt766->component, rt766->irq_info, false); -+ } -+ -+ pm_runtime_put_autosuspend(component->dev); -+ -+ return 0; -+} -+ -+static int rt766_sdca_set_fu_ctl(struct rt766_sdca_priv *rt766, int func_num, int fu_num) -+{ -+ unsigned int fu01_reg, fu02_reg; -+ unsigned int ch_01, ch_02; -+ unsigned int ch_mute; -+ unsigned int fu_reg; -+ int err, i; -+ -+ switch (fu_num) { -+ case RT766_SDCA_ENT_USER_FU41: -+ ch_01 = (rt766->fu41_dapm_mute || rt766->fu41_mixer_l_mute) ? 0x01 : 0x00; -+ ch_02 = (rt766->fu41_dapm_mute || rt766->fu41_mixer_r_mute) ? 0x01 : 0x00; -+ break; -+ case RT766_SDCA_ENT_USER_FU36: -+ ch_01 = (rt766->fu36_dapm_mute || rt766->fu36_mixer_l_mute) ? 0x01 : 0x00; -+ ch_02 = (rt766->fu36_dapm_mute || rt766->fu36_mixer_r_mute) ? 0x01 : 0x00; -+ break; -+ case RT766_SDCA_ENT_USER_FU21: -+ ch_01 = (rt766->fu21_dapm_mute || rt766->fu21_mixer_l_mute) ? 0x01 : 0x00; -+ ch_02 = (rt766->fu21_dapm_mute || rt766->fu21_mixer_r_mute) ? 0x01 : 0x00; -+ break; -+ case RT766_SDCA_ENT_USER_FU113: -+ for (i = 0; i < ARRAY_SIZE(rt766->fu113_mixer_mute); i++) { -+ ch_mute = (rt766->fu113_dapm_mute || rt766->fu113_mixer_mute[i]) ? 0x01 : 0x00; -+ fu_reg = SDW_SDCA_CTL(func_num, fu_num, SDCA_CTL_FU_MUTE, 1) + i; -+ err = regmap_write(rt766->regmap, fu_reg, ch_mute); -+ if (err < 0) -+ return err; -+ } -+ return 0; -+ } -+ -+ fu01_reg = SDW_SDCA_CTL(func_num, fu_num, SDCA_CTL_FU_MUTE, 1); -+ fu02_reg = SDW_SDCA_CTL(func_num, fu_num, SDCA_CTL_FU_MUTE, 2); -+ err = regmap_write(rt766->regmap, fu01_reg, ch_01); -+ if (err < 0) -+ return err; -+ err = regmap_write(rt766->regmap, fu02_reg, ch_02); -+ if (err < 0) -+ return err; -+ -+ return 0; -+} -+ -+static int rt766_sdca_fu41_playback_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ -+ ucontrol->value.integer.value[0] = !rt766->fu41_mixer_l_mute; -+ ucontrol->value.integer.value[1] = !rt766->fu41_mixer_r_mute; -+ return 0; -+} -+ -+static int rt766_sdca_fu41_playback_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ int err; -+ -+ if (rt766->fu41_mixer_l_mute == !ucontrol->value.integer.value[0] && -+ rt766->fu41_mixer_r_mute == !ucontrol->value.integer.value[1]) -+ return 0; -+ -+ rt766->fu41_mixer_l_mute = !ucontrol->value.integer.value[0]; -+ rt766->fu41_mixer_r_mute = !ucontrol->value.integer.value[1]; -+ -+ err = rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU41); -+ if (err < 0) -+ return err; -+ -+ return 1; -+} -+ -+static int rt766_sdca_fu36_capture_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ -+ ucontrol->value.integer.value[0] = !rt766->fu36_mixer_l_mute; -+ ucontrol->value.integer.value[1] = !rt766->fu36_mixer_r_mute; -+ return 0; -+} -+ -+static int rt766_sdca_fu36_capture_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ int err; -+ -+ if (rt766->fu36_mixer_l_mute == !ucontrol->value.integer.value[0] && -+ rt766->fu36_mixer_r_mute == !ucontrol->value.integer.value[1]) -+ return 0; -+ -+ rt766->fu36_mixer_l_mute = !ucontrol->value.integer.value[0]; -+ rt766->fu36_mixer_r_mute = !ucontrol->value.integer.value[1]; -+ err = rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU36); -+ if (err < 0) -+ return err; -+ -+ return 1; -+} -+ -+static int rt766_sdca_fu21_playback_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ -+ ucontrol->value.integer.value[0] = !rt766->fu21_mixer_l_mute; -+ ucontrol->value.integer.value[1] = !rt766->fu21_mixer_r_mute; -+ return 0; -+} -+ -+static int rt766_sdca_fu21_playback_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ int err; -+ -+ if (rt766->fu21_mixer_l_mute == !ucontrol->value.integer.value[0] && -+ rt766->fu21_mixer_r_mute == !ucontrol->value.integer.value[1]) -+ return 0; -+ -+ rt766->fu21_mixer_l_mute = !ucontrol->value.integer.value[0]; -+ rt766->fu21_mixer_r_mute = !ucontrol->value.integer.value[1]; -+ -+ err = rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_AMP, RT766_SDCA_ENT_USER_FU21); -+ if (err < 0) -+ return err; -+ -+ return 1; -+} -+ -+static int rt766_sdca_fu113_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *kcontrol, int event) -+{ -+ struct snd_soc_component *component = -+ snd_soc_dapm_to_component(w->dapm); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ -+ switch (event) { -+ case SND_SOC_DAPM_POST_PMU: -+ rt766->fu113_dapm_mute = false; -+ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_MIC, RT766_SDCA_ENT_USER_FU113); -+ break; -+ case SND_SOC_DAPM_PRE_PMD: -+ rt766->fu113_dapm_mute = true; -+ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_MIC, RT766_SDCA_ENT_USER_FU113); -+ break; -+ } -+ return 0; -+} -+ -+static int rt766_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ struct rt_sdca_dmic_kctrl_priv *p = -+ (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value; -+ const unsigned int interval_offset = 0xc0; -+ unsigned int regvalue, ctl, i; -+ -+ /* check all channels */ -+ for (i = 0; i < p->count; i++) { -+ regmap_read(rt766->regmap, p->reg_base + i, ®value); -+ ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset); -+ -+ ucontrol->value.integer.value[i] = ctl; -+ } -+ -+ return 0; -+} -+ -+static int rt766_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct rt_sdca_dmic_kctrl_priv *p = -+ (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value; -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ const unsigned int interval_offset = 0xc0; -+ unsigned int gain_val[4]; -+ unsigned int i, changed = 0; -+ unsigned int regvalue[4]; -+ int err; -+ -+ /* check all channels */ -+ for (i = 0; i < p->count; i++) { -+ regmap_read(rt766->regmap, p->reg_base + i, ®value[i]); -+ -+ gain_val[i] = ucontrol->value.integer.value[i]; -+ if (gain_val[i] > p->max) -+ gain_val[i] = p->max; -+ -+ gain_val[i] = 0x1e00 - ((p->max - gain_val[i]) * interval_offset); -+ gain_val[i] &= 0xffff; -+ -+ if (regvalue[i] != gain_val[i]) -+ changed = 1; -+ } -+ -+ if (!changed) -+ return 0; -+ -+ for (i = 0; i < p->count; i++) { -+ err = regmap_write(rt766->regmap, p->reg_base + i, gain_val[i]); -+ if (err < 0) -+ dev_err(&rt766->slave->dev, "0x%08x can't be set\n", p->reg_base + i); -+ } -+ -+ return changed; -+} -+ -+static int rt766_sdca_dmic_fu113_capture_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ unsigned int i; -+ -+ for (i = 0; i < 4; i++) -+ ucontrol->value.integer.value[i] = !rt766->fu113_mixer_mute[i]; -+ return 0; -+} -+ -+static int rt766_sdca_dmic_fu113_capture_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ int err, changed = 0, i; -+ -+ for (i = 0; i < 4; i++) { -+ if (rt766->fu113_mixer_mute[i] != !ucontrol->value.integer.value[i]) -+ changed = 1; -+ rt766->fu113_mixer_mute[i] = !ucontrol->value.integer.value[i]; -+ } -+ -+ err = rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_MIC, RT766_SDCA_ENT_USER_FU113); -+ if (err < 0) -+ return err; -+ return changed; -+} -+ -+static int rt766_sdca_fu41_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *kcontrol, int event) -+{ -+ struct snd_soc_component *component = -+ snd_soc_dapm_to_component(w->dapm); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ -+ switch (event) { -+ case SND_SOC_DAPM_POST_PMU: -+ rt766->fu41_dapm_mute = false; -+ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU41); -+ break; -+ case SND_SOC_DAPM_PRE_PMD: -+ rt766->fu41_dapm_mute = true; -+ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU41); -+ break; -+ } -+ return 0; -+} -+ -+static int rt766_sdca_pde_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *kcontrol, int event, int func_num, int pde_num, const char *pde_ent) -+{ -+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ struct sdca_function_data *func_data; -+ unsigned char ps0 = 0x0, ps3 = 0x3; -+ const struct sdca_entity *entity; -+ unsigned int pde_req_reg; -+ int from_ps, to_ps; -+ int ret; -+ -+ pde_req_reg = SDW_SDCA_CTL(func_num, pde_num, SDCA_CTL_PDE_REQUESTED_PS, 0); -+ -+ switch (func_num) { -+ case RT766_FUNC_NUM_UAJ: -+ func_data = rt766->uaj_func_data; -+ break; -+ case RT766_FUNC_NUM_AMP: -+ func_data = rt766->sa_func_data; -+ break; -+ case RT766_FUNC_NUM_MIC: -+ func_data = rt766->sm_func_data; -+ break; -+ default: -+ dev_err(component->dev, "%s: unsupported func_num %d\n", -+ __func__, func_num); -+ return -EINVAL; -+ } -+ -+ switch (event) { -+ case SND_SOC_DAPM_POST_PMU: -+ regmap_write(rt766->regmap, pde_req_reg, ps0); -+ from_ps = ps3; -+ to_ps = ps0; -+ break; -+ case SND_SOC_DAPM_PRE_PMD: -+ regmap_write(rt766->regmap, pde_req_reg, ps3); -+ from_ps = ps0; -+ to_ps = ps3; -+ break; -+ } -+ -+ entity = sdca_find_entity_by_label(func_data, pde_ent); -+ if (!entity) { -+ dev_err(component->dev, "%s: failed to find entity %s\n", -+ __func__, pde_ent); -+ return -EINVAL; -+ } -+ -+ ret = sdca_asoc_pde_poll_actual_ps(rt766->regmap, -+ func_num, -+ pde_num, -+ from_ps, to_ps, -+ entity->pde.max_delay, -+ entity->pde.num_max_delay); -+ if (ret) -+ dev_err(component->dev, "%s: PDE transition %x -> %x failed, err=%d\n", -+ __func__, from_ps, to_ps, ret); -+ -+ return ret; -+} -+ -+static int rt766_sdca_pde47_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *kcontrol, int event) -+{ -+ return rt766_sdca_pde_event(w, kcontrol, event, -+ RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_PDE47, "PDE 47"); -+} -+ -+static int rt766_sdca_fu36_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *kcontrol, int event) -+{ -+ struct snd_soc_component *component = -+ snd_soc_dapm_to_component(w->dapm); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ -+ switch (event) { -+ case SND_SOC_DAPM_POST_PMU: -+ rt766->fu36_dapm_mute = false; -+ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU36); -+ break; -+ case SND_SOC_DAPM_PRE_PMD: -+ rt766->fu36_dapm_mute = true; -+ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU36); -+ break; -+ } -+ return 0; -+} -+ -+static int rt766_sdca_pde34_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *kcontrol, int event) -+{ -+ return rt766_sdca_pde_event(w, kcontrol, event, -+ RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_PDE34, "PDE 34"); -+} -+ -+static int rt766_sdca_fu21_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *kcontrol, int event) -+{ -+ struct snd_soc_component *component = -+ snd_soc_dapm_to_component(w->dapm); -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ -+ switch (event) { -+ case SND_SOC_DAPM_POST_PMU: -+ rt766->fu21_dapm_mute = false; -+ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_AMP, RT766_SDCA_ENT_USER_FU21); -+ break; -+ case SND_SOC_DAPM_PRE_PMD: -+ rt766->fu21_dapm_mute = true; -+ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_AMP, RT766_SDCA_ENT_USER_FU21); -+ break; -+ } -+ return 0; -+} -+ -+static int rt766_sdca_pde23_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *kcontrol, int event) -+{ -+ return rt766_sdca_pde_event(w, kcontrol, event, -+ RT766_FUNC_NUM_AMP, RT766_SDCA_ENT_PDE23, "PDE 23"); -+} -+ -+static int rt766_sdca_pde11_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *kcontrol, int event) -+{ -+ return rt766_sdca_pde_event(w, kcontrol, event, -+ RT766_FUNC_NUM_MIC, RT766_SDCA_ENT_PDE11, "PDE 11"); -+} -+ -+static int rt766_dmic_fu_info(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_info *uinfo) -+{ -+ struct rt_sdca_dmic_kctrl_priv *p = -+ (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value; -+ -+ if (p->max == 1) -+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; -+ else -+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; -+ uinfo->count = p->count; -+ uinfo->value.integer.min = 0; -+ uinfo->value.integer.max = p->max; -+ return 0; -+} -+ -+static const char * const rt766_rx_data_ch_select[] = { -+ "L,R", -+ "R,L", -+ "L,L", -+ "R,R", -+ "L,L+R", -+ "R,L+R", -+ "L+R,L", -+ "L+R,R", -+ "L+R,L+R", -+}; -+ -+static SOC_ENUM_SINGLE_DECL(rt766_rx_data_ch_enum, -+ RT766_SDCA_CTL(AMP, PPU21, SDCA_CTL_PPU_POSTURENUMBER), 0, -+ rt766_rx_data_ch_select); -+ -+static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -9525, 75, 0); -+static const DECLARE_TLV_DB_SCALE(spk_vol_tlv, -6525, 75, 0); -+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -1725, 75, 0); -+static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, -200, 200, 0); -+ -+#define RT766_P75DB_STEP 0xC0 /* 0.75 dB in Q7.8 format */ -+#define RT766_2DB_STEP 0x200 /* 2 dB in Q7.8 format */ -+#define RT766_HP_VOL_MIN (-127) /* -95.25 dB / 0.75 dB step */ -+#define RT766_HS_VOL_MIN (-23) /* -17.25 dB / 0.75 dB step */ -+#define RT766_HS_BOOST_VOL_MIN (-1) /* -2 dB / 2 dB step */ -+#define RT766_SPK_VOL_MIN (-87) /* -65.25 dB / 0.75 dB step */ -+#define RT766_P_VOL_MAX 0 /* 0 dB / 0.75 dB step */ -+#define RT766_HS_VOL_MAX 40 /* 30 dB / 0.75 dB step */ -+#define RT766_HS_BOOST_VOL_MAX 20 /* 40 dB / 2 dB step */ -+ -+static const struct snd_kcontrol_new rt766_sdca_controls[] = { -+ SOC_DOUBLE_EXT("FU41 Playback Switch", SND_SOC_NOPM, 0, 1, 1, 0, -+ rt766_sdca_fu41_playback_get, rt766_sdca_fu41_playback_put), -+ SDCA_DOUBLE_Q78_TLV("FU41 Playback Volume", -+ RT766_VOLUME_REG(UAJ, USER_FU41, 1), -+ RT766_VOLUME_REG(UAJ, USER_FU41, 2), -+ RT766_HP_VOL_MIN, RT766_P_VOL_MAX, RT766_P75DB_STEP, hp_vol_tlv), -+ SOC_DOUBLE_EXT("FU36 Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0, -+ rt766_sdca_fu36_capture_get, rt766_sdca_fu36_capture_put), -+ SDCA_DOUBLE_Q78_TLV("FU36 Capture Volume", -+ RT766_VOLUME_REG(UAJ, USER_FU36, 1), -+ RT766_VOLUME_REG(UAJ, USER_FU36, 2), -+ RT766_HS_VOL_MIN, RT766_HS_VOL_MAX, RT766_P75DB_STEP, mic_vol_tlv), -+ SDCA_DOUBLE_Q78_TLV("FU33 Boost Volume", -+ RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1), -+ RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2), -+ RT766_HS_BOOST_VOL_MIN, RT766_HS_BOOST_VOL_MAX, RT766_2DB_STEP, boost_vol_tlv), -+ -+ SOC_DOUBLE_EXT("FU21 Playback Switch", SND_SOC_NOPM, 0, 1, 1, 0, -+ rt766_sdca_fu21_playback_get, rt766_sdca_fu21_playback_put), -+ SDCA_DOUBLE_Q78_TLV("FU21 Playback Volume", -+ RT766_VOLUME_REG(AMP, USER_FU21, 1), -+ RT766_VOLUME_REG(AMP, USER_FU21, 2), -+ RT766_SPK_VOL_MIN, RT766_P_VOL_MAX, RT766_P75DB_STEP, spk_vol_tlv), -+ SOC_ENUM("RX Channel Select", rt766_rx_data_ch_enum), -+ -+ RT_SDCA_FU_CTRL("FU113 Capture Switch", -+ RT766_MUTE_REG(MIC, USER_FU113, 1), 1, 1, 4, rt766_dmic_fu_info, -+ rt766_sdca_dmic_fu113_capture_get, rt766_sdca_dmic_fu113_capture_put), -+ RT_SDCA_EXT_TLV("FU113 Capture Volume", -+ RT766_VOLUME_REG(MIC, USER_FU113, 1), -+ rt766_sdca_dmic_set_gain_get, rt766_sdca_dmic_set_gain_put, -+ 4, 0x3f, mic_vol_tlv, rt766_dmic_fu_info), -+}; -+ -+static const struct snd_soc_dapm_widget rt766_sdca_dapm_widgets[] = { -+ /* UAJ */ -+ SND_SOC_DAPM_OUTPUT("HP"), -+ SND_SOC_DAPM_INPUT("MIC2"), -+ SND_SOC_DAPM_SUPPLY("PDE 47", SND_SOC_NOPM, 0, 0, -+ rt766_sdca_pde47_event, -+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), -+ SND_SOC_DAPM_SUPPLY("PDE 34", SND_SOC_NOPM, 0, 0, -+ rt766_sdca_pde34_event, -+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), -+ SND_SOC_DAPM_DAC_E("FU 41", NULL, SND_SOC_NOPM, 0, 0, -+ rt766_sdca_fu41_event, -+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), -+ SND_SOC_DAPM_ADC_E("FU 36", NULL, SND_SOC_NOPM, 0, 0, -+ rt766_sdca_fu36_event, -+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), -+ SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_OUT("DP12TX", "DP12 Capture", 0, SND_SOC_NOPM, 0, 0), -+ -+ /* AMP */ -+ SND_SOC_DAPM_OUTPUT("SPOL"), -+ SND_SOC_DAPM_OUTPUT("SPOR"), -+ SND_SOC_DAPM_DAC_E("FU 21", NULL, SND_SOC_NOPM, 0, 0, -+ rt766_sdca_fu21_event, -+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), -+ SND_SOC_DAPM_SUPPLY("PDE 23", SND_SOC_NOPM, 0, 0, -+ rt766_sdca_pde23_event, -+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), -+ SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0), -+ -+ /* DMIC */ -+ SND_SOC_DAPM_INPUT("DMIC1"), -+ SND_SOC_DAPM_INPUT("DMIC2"), -+ SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0, -+ rt766_sdca_pde11_event, -+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), -+ SND_SOC_DAPM_ADC_E("FU 113", NULL, SND_SOC_NOPM, 0, 0, -+ rt766_sdca_fu113_event, -+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), -+ SND_SOC_DAPM_AIF_OUT("DP8TX", "DP8 Capture", 0, SND_SOC_NOPM, 0, 0), -+}; -+ -+static const struct snd_soc_dapm_route rt766_sdca_audio_map[] = { -+ { "FU 41", NULL, "DP3RX" }, -+ { "DP12TX", NULL, "FU 36" }, -+ { "FU 36", NULL, "PDE 34" }, -+ { "FU 36", NULL, "MIC2" }, -+ { "HP", NULL, "PDE 47" }, -+ { "HP", NULL, "FU 41" }, -+ -+ { "FU 21", NULL, "DP1RX" }, -+ { "FU 21", NULL, "PDE 23" }, -+ { "SPOL", NULL, "FU 21" }, -+ { "SPOR", NULL, "FU 21" }, -+ -+ {"DP8TX", NULL, "FU 113"}, -+ {"FU 113", NULL, "PDE 11"}, -+ {"FU 113", NULL, "DMIC1"}, -+ {"FU 113", NULL, "DMIC2"}, -+}; -+ -+static int rt766_sdca_probe(struct snd_soc_component *component) -+{ -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ struct device *dev = &rt766->slave->dev; -+ int ret; -+ -+ rt766->component = component; -+ -+ ret = pm_runtime_resume(component->dev); -+ if (ret < 0 && ret != -EACCES) -+ return ret; -+ -+ if (rt766->uaj_func_data) { -+ dev_dbg(dev, "%s : irq %d\n", __func__, rt766->slave->irq); -+ -+ rt766->irq_info = devm_sdca_irq_allocate(dev, rt766->regmap, rt766->slave->irq); -+ if (IS_ERR(rt766->irq_info)) -+ return PTR_ERR(rt766->irq_info); -+ -+ ret = rt766_sdca_irq_ctl(rt766, rt766->uaj_func_data, -+ component, rt766->irq_info, true); -+ if (ret < 0) { -+ dev_err(dev, "Failed to request UAJ SDCA IRQ: %d\n", ret); -+ return ret; -+ } -+ -+ if (rt766->hid_func_data) { -+ ret = rt766_sdca_irq_ctl(rt766, rt766->hid_func_data, -+ component, rt766->irq_info, true); -+ if (ret < 0) { -+ dev_err(dev, "Failed to request HID SDCA IRQ: %d\n", ret); -+ return ret; -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+static void rt766_sdca_remove(struct snd_soc_component *component) -+{ -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ -+ sdca_irq_cleanup_late(component->dev, rt766->uaj_func_data, rt766->irq_info); -+ sdca_irq_cleanup_late(component->dev, rt766->hid_func_data, rt766->irq_info); -+} -+ -+static const struct snd_soc_component_driver soc_sdca_dev_rt766 = { -+ .probe = rt766_sdca_probe, -+ .remove = rt766_sdca_remove, -+ .controls = rt766_sdca_controls, -+ .num_controls = ARRAY_SIZE(rt766_sdca_controls), -+ .dapm_widgets = rt766_sdca_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(rt766_sdca_dapm_widgets), -+ .dapm_routes = rt766_sdca_audio_map, -+ .num_dapm_routes = ARRAY_SIZE(rt766_sdca_audio_map), -+ .set_jack = rt766_sdca_set_jack_detect, -+ .endianness = 1, -+}; -+ -+static int rt766_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, -+ int direction) -+{ -+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream); -+ -+ return 0; -+} -+ -+static void rt766_sdca_shutdown(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ snd_soc_dai_set_dma_data(dai, substream, NULL); -+} -+ -+static int rt766_sdca_pcm_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params, -+ struct snd_soc_dai *dai) -+{ -+ struct snd_soc_component *component = dai->component; -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ struct sdw_stream_config stream_config; -+ struct sdw_port_config port_config; -+ enum sdw_data_direction direction; -+ struct sdw_stream_runtime *sdw_stream; -+ unsigned int sampling_rate; -+ int retval, port; -+ -+ dev_dbg(dai->dev, "%s %s id %d", __func__, dai->name, dai->id); -+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream); -+ -+ if (!sdw_stream) -+ return -EINVAL; -+ -+ if (!rt766->slave) -+ return -EINVAL; -+ -+ /* SoundWire specific configuration */ -+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config); -+ -+ /* SoundWire specific configuration */ -+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { -+ direction = SDW_DATA_DIR_RX; -+ if (dai->id == RT766_AIF1) -+ port = 3; -+ else if (dai->id == RT766_AIF2) -+ port = 1; -+ else -+ return -EINVAL; -+ } else { -+ direction = SDW_DATA_DIR_TX; -+ if (dai->id == RT766_AIF1) -+ port = 12; -+ else if (dai->id == RT766_AIF3) -+ port = 8; -+ else -+ return -EINVAL; -+ } -+ -+ port_config.num = port; -+ retval = sdw_stream_add_slave(rt766->slave, &stream_config, -+ &port_config, 1, sdw_stream); -+ if (retval) { -+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__); -+ return retval; -+ } -+ -+ if (params_channels(params) > 16) { -+ dev_err(component->dev, "%s: Unsupported channels %d\n", -+ __func__, params_channels(params)); -+ return -EINVAL; -+ } -+ -+ /* sampling rate configuration */ -+ switch (params_rate(params)) { -+ case 44100: -+ sampling_rate = RT766_SDCA_RATE_44100HZ; -+ break; -+ case 48000: -+ sampling_rate = RT766_SDCA_RATE_48000HZ; -+ break; -+ case 96000: -+ sampling_rate = RT766_SDCA_RATE_96000HZ; -+ break; -+ case 192000: -+ sampling_rate = RT766_SDCA_RATE_192000HZ; -+ break; -+ default: -+ dev_err(component->dev, "%s: Rate %d is not supported\n", -+ __func__, params_rate(params)); -+ return -EINVAL; -+ } -+ -+ /* set sampling frequency */ -+ switch (dai->id) { -+ case RT766_AIF1: -+ regmap_write(rt766->regmap, -+ RT766_SDCA_CTL(UAJ, CS41, SDCA_CTL_CS_SAMPLERATEINDEX), -+ sampling_rate); -+ regmap_write(rt766->regmap, -+ RT766_SDCA_CTL(UAJ, CS36, SDCA_CTL_CS_SAMPLERATEINDEX), -+ sampling_rate); -+ break; -+ case RT766_AIF2: -+ regmap_write(rt766->regmap, -+ RT766_SDCA_CTL(AMP, CS21, SDCA_CTL_CS_SAMPLERATEINDEX), -+ sampling_rate); -+ break; -+ case RT766_AIF3: -+ regmap_write(rt766->regmap, -+ RT766_SDCA_CTL(MIC, CS113, SDCA_CTL_CS_SAMPLERATEINDEX), -+ sampling_rate); -+ break; -+ default: -+ dev_err(component->dev, "%s: Wrong DAI id\n", __func__); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int rt766_sdca_pcm_hw_free(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ struct snd_soc_component *component = dai->component; -+ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); -+ struct sdw_stream_runtime *sdw_stream = -+ snd_soc_dai_get_dma_data(dai, substream); -+ -+ if (!rt766->slave) -+ return -EINVAL; -+ -+ sdw_stream_remove_slave(rt766->slave, sdw_stream); -+ return 0; -+} -+ -+#define RT766_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \ -+ SNDRV_PCM_RATE_192000) -+#define RT766_DAC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) -+#define RT766_ADC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ -+ SNDRV_PCM_FMTBIT_S32_LE) -+ -+static const struct snd_soc_dai_ops rt766_sdca_ops = { -+ .hw_params = rt766_sdca_pcm_hw_params, -+ .hw_free = rt766_sdca_pcm_hw_free, -+ .set_stream = rt766_sdca_set_sdw_stream, -+ .shutdown = rt766_sdca_shutdown, -+}; -+ -+static struct snd_soc_dai_driver rt766_sdca_dai[] = { -+ { -+ .name = "rt766-sdca-aif1", -+ .id = RT766_AIF1, -+ .playback = { -+ .stream_name = "DP3 Playback", -+ .channels_min = 1, -+ .channels_max = 2, -+ .rates = RT766_STEREO_RATES, -+ .formats = RT766_DAC_FORMATS, -+ }, -+ .capture = { -+ .stream_name = "DP12 Capture", -+ .channels_min = 1, -+ .channels_max = 2, -+ .rates = RT766_STEREO_RATES, -+ .formats = RT766_ADC_FORMATS, -+ }, -+ .ops = &rt766_sdca_ops, -+ .symmetric_rate = 1, -+ }, -+ { -+ .name = "rt766-sdca-aif2", -+ .id = RT766_AIF2, -+ .playback = { -+ .stream_name = "DP1 Playback", -+ .channels_min = 1, -+ .channels_max = 4, -+ .rates = RT766_STEREO_RATES, -+ .formats = RT766_DAC_FORMATS, -+ }, -+ .ops = &rt766_sdca_ops, -+ }, -+ { -+ .name = "rt766-sdca-aif3", -+ .id = RT766_AIF3, -+ .capture = { -+ .stream_name = "DP8 Capture", -+ .channels_min = 1, -+ .channels_max = 4, -+ .rates = RT766_STEREO_RATES, -+ .formats = RT766_ADC_FORMATS, -+ }, -+ .ops = &rt766_sdca_ops, -+ } -+}; -+ -+static unsigned int rt766_find_dt_rates(struct device *dev, struct sdca_function_data *function, -+ const char *label) -+{ -+ struct snd_soc_pcm_stream stream; -+ struct sdca_entity *entity; -+ int i, ret; -+ -+ for (i = 0; i < function->num_entities; i++) { -+ entity = &function->entities[i]; -+ -+ if (strcmp(entity->label, label)) -+ continue; -+ -+ /* Can't check earlier as only terminals have an iot member. */ -+ if (!entity->iot.is_dataport) -+ continue; -+ -+ ret = sdca_asoc_populate_rate_format(dev, function, entity, &stream); -+ if (ret < 0) { -+ dev_dbg(dev, "%s: failed to parse rates for entity %s\n", -+ __func__, entity->label); -+ return 0; -+ } -+ -+ dev_dbg(dev, "%s: %s supports rates 0x%08x\n", __func__, entity->label, stream.rates); -+ } -+ -+ return stream.rates; -+} -+ -+int rt766_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave *slave) -+{ -+ struct sdca_function_data *func_data_ptr; -+ struct snd_soc_dai_driver *dai_drv; -+ struct rt766_sdca_priv *rt766; -+ unsigned int rates; -+ int ret; -+ int i; -+ -+ rt766 = devm_kzalloc(dev, sizeof(*rt766), GFP_KERNEL); -+ if (!rt766) -+ return -ENOMEM; -+ -+ dev_set_drvdata(dev, rt766); -+ rt766->slave = slave; -+ rt766->regmap = regmap; -+ -+ regcache_cache_only(rt766->regmap, true); -+ -+ ret = devm_mutex_init(dev, &rt766->disable_irq_lock); -+ if (ret < 0) { -+ dev_err(dev, "Failed to initialize mutex\n"); -+ return ret; -+ } -+ -+ /* -+ * Mark hw_init to false -+ * HW init will be performed when device reports present -+ */ -+ rt766->hw_init = false; -+ rt766->first_hw_init = false; -+ rt766->fu41_dapm_mute = true; -+ rt766->fu41_mixer_l_mute = rt766->fu41_mixer_r_mute = false; -+ rt766->fu36_dapm_mute = true; -+ rt766->fu36_mixer_l_mute = rt766->fu36_mixer_r_mute = true; -+ rt766->fu21_dapm_mute = true; -+ rt766->fu21_mixer_l_mute = rt766->fu21_mixer_r_mute = false; -+ rt766->fu113_dapm_mute = true; -+ rt766->fu113_mixer_mute[0] = rt766->fu113_mixer_mute[1] = -+ rt766->fu113_mixer_mute[2] = rt766->fu113_mixer_mute[3] = true; -+ -+ dai_drv = devm_kzalloc(dev, sizeof(struct snd_soc_dai_driver) * ARRAY_SIZE(rt766_sdca_dai), GFP_KERNEL); -+ if (!dai_drv) { -+ dev_err(dev, "Failed to allocate memory for DAI driver\n"); -+ ret = -ENOMEM; -+ goto _sdw_init_err_; -+ } -+ memcpy(dai_drv, rt766_sdca_dai, sizeof(struct snd_soc_dai_driver) * ARRAY_SIZE(rt766_sdca_dai)); -+ -+ /* get SDCA function data */ -+ dev_dbg(dev, "SDCA functions found: %d", slave->sdca_data.num_functions); -+ for (i = 0; i < slave->sdca_data.num_functions; i++) { -+ func_data_ptr = devm_kzalloc(dev, sizeof(*func_data_ptr), GFP_KERNEL); -+ if (!func_data_ptr) { -+ dev_err(dev, "Failed to allocate memory for function data\n"); -+ ret = -ENOMEM; -+ goto _free_dai_drv_; -+ } -+ -+ func_data_ptr->desc = &slave->sdca_data.function[i]; -+ ret = sdca_parse_function(dev, func_data_ptr); -+ if (ret) { -+ devm_kfree(dev, func_data_ptr); -+ goto _free_dai_drv_; -+ } -+ dev_dbg(dev, "Function type=%d, num_entities=%d", -+ slave->sdca_data.function[i].type, func_data_ptr->num_entities); -+ -+ switch (slave->sdca_data.function[i].type) { -+ case SDCA_FUNCTION_TYPE_UAJ: -+ rt766->uaj_func_data = func_data_ptr; -+ /* -+ * Some machines may only support a subset of the sample rates supported by the codec. -+ * Therefore, we need to parse the supported sample rates from the DisCo table and -+ * configure them in the DAI. If the DisCo table does not provide sample rate information, -+ * we will fall back to the default supported rates defined in the codec driver. -+ */ -+ rates = rt766_find_dt_rates(dev, func_data_ptr, "IT 41"); -+ if (rates) -+ dai_drv[RT766_DAI_UAJ].playback.rates = rates; -+ -+ rates = rt766_find_dt_rates(dev, func_data_ptr, "OT 36"); -+ if (rates) -+ dai_drv[RT766_DAI_UAJ].capture.rates = rates; -+ break; -+ case SDCA_FUNCTION_TYPE_SMART_AMP: -+ rt766->sa_func_data = func_data_ptr; -+ rates = rt766_find_dt_rates(dev, func_data_ptr, "IT 21"); -+ if (rates) -+ dai_drv[RT766_DAI_AMP].playback.rates = rates; -+ break; -+ case SDCA_FUNCTION_TYPE_SMART_MIC: -+ rt766->sm_func_data = func_data_ptr; -+ rates = rt766_find_dt_rates(dev, func_data_ptr, "OT 113"); -+ if (rates) -+ dai_drv[RT766_DAI_MIC].capture.rates = rates; -+ break; -+ case SDCA_FUNCTION_TYPE_HID: -+ rt766->hid_func_data = func_data_ptr; -+ break; -+ default: -+ dev_dbg(dev, "Unexpected SDCA function type found: %d", -+ slave->sdca_data.function[i].type); -+ } -+ } -+ -+ ret = devm_snd_soc_register_component(dev, -+ &soc_sdca_dev_rt766, dai_drv, ARRAY_SIZE(rt766_sdca_dai)); -+ if (ret < 0) -+ goto _free_dai_drv_; -+ -+ /* set autosuspend parameters */ -+ pm_runtime_set_autosuspend_delay(dev, 3000); -+ pm_runtime_use_autosuspend(dev); -+ -+ /* make sure the device does not suspend immediately */ -+ pm_runtime_mark_last_busy(dev); -+ -+ pm_runtime_enable(dev); -+ dev_dbg(dev, "%s\n", __func__); -+ return 0; -+ -+_free_dai_drv_: -+ if (dai_drv) -+ devm_kfree(dev, dai_drv); -+ -+_sdw_init_err_: -+ return ret; -+} -+ -+static int rt766_func_initialize(struct rt766_sdca_priv *rt766, struct sdca_function_data *func_data) -+{ -+ struct device *dev = &rt766->slave->dev; -+ unsigned int func_status_reg; -+ unsigned int func_status; -+ int ret; -+ -+ switch (func_data->desc->type) { -+ case SDCA_FUNCTION_TYPE_UAJ: -+ func_status_reg = RT766_FUNC_STATUS_REG(UAJ); -+ break; -+ case SDCA_FUNCTION_TYPE_SMART_AMP: -+ func_status_reg = RT766_FUNC_STATUS_REG(AMP); -+ break; -+ case SDCA_FUNCTION_TYPE_SMART_MIC: -+ func_status_reg = RT766_FUNC_STATUS_REG(MIC); -+ break; -+ case SDCA_FUNCTION_TYPE_HID: -+ func_status_reg = RT766_FUNC_STATUS_REG(HID); -+ break; -+ default: -+ dev_dbg(dev, "Unexpected SDCA function type found: %d", -+ func_data->desc->type); -+ return -EINVAL; -+ } -+ -+ regmap_read(rt766->regmap, func_status_reg, &func_status); -+ dev_dbg(dev, "%s, %s func_status=0x%x\n", __func__, func_data->desc->name, func_status); -+ -+ if ((func_status & SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION) || (!rt766->first_hw_init)) { -+ ret = sdca_regmap_write_init(dev, rt766->regmap, func_data); -+ if (ret) { -+ dev_err(dev, "%s initialization table update failed\n", func_data->desc->name); -+ goto _func_init_err_; -+ } -+ -+ regmap_write(rt766->regmap, func_status_reg, -+ SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION); -+ } -+ -+ return 0; -+ -+_func_init_err_: -+ dev_err(dev, "%s: %s init writes failed, err=%d", __func__, func_data->desc->name, ret); -+ return ret; -+} -+ -+int rt766_sdca_io_init(struct device *dev, struct sdw_slave *slave) -+{ -+ struct rt766_sdca_priv *rt766 = dev_get_drvdata(dev); -+ unsigned int val; -+ -+ rt766->disable_irq = false; -+ -+ if (rt766->hw_init) -+ return 0; -+ -+ regcache_cache_only(rt766->regmap, false); -+ if (rt766->first_hw_init) { -+ regcache_cache_bypass(rt766->regmap, true); -+ } else { -+ /* -+ * PM runtime status is marked as 'active' only when a Slave reports as Attached -+ */ -+ -+ /* update count of parent 'active' children */ -+ pm_runtime_set_active(&slave->dev); -+ } -+ -+ pm_runtime_get_noresume(&slave->dev); -+ -+ regmap_read(rt766->regmap, RT766_BOND_LATCH_ID, &val); -+ dev_dbg(&slave->dev, "%s bond ID=0x%x (%s)\n", __func__, val, (val == 0x1) ? "RT767" : "RT766"); -+ -+ /* check function status and initialize if needed */ -+ if (rt766->uaj_func_data) -+ rt766_func_initialize(rt766, rt766->uaj_func_data); -+ if (rt766->sa_func_data) -+ rt766_func_initialize(rt766, rt766->sa_func_data); -+ if (rt766->sm_func_data) -+ rt766_func_initialize(rt766, rt766->sm_func_data); -+ if (rt766->hid_func_data) -+ rt766_func_initialize(rt766, rt766->hid_func_data); -+ -+ if (rt766->first_hw_init) { -+ regcache_cache_bypass(rt766->regmap, false); -+ regcache_mark_dirty(rt766->regmap); -+ } else { -+ rt766->first_hw_init = true; -+ } -+ -+ /* Mark Slave initialization complete */ -+ rt766->hw_init = true; -+ -+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); -+ -+ pm_runtime_put_autosuspend(&slave->dev); -+ -+ return 0; -+} -+ -+MODULE_DESCRIPTION("ASoC RT766 SDCA SDW driver"); -+MODULE_AUTHOR("Shuming Fan "); -+MODULE_LICENSE("GPL"); -+MODULE_IMPORT_NS("SND_SOC_SDCA"); -diff --git a/sound/soc/codecs/rt766-sdca.h b/sound/soc/codecs/rt766-sdca.h -new file mode 100644 ---- /dev/null -+++ b/sound/soc/codecs/rt766-sdca.h -@@ -0,0 +1,140 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * rt766-sdca.h -- RT766 SDCA ALSA SoC audio driver header -+ * -+ * Copyright(c) 2026 Realtek Semiconductor Corp. -+ */ -+ -+#ifndef __RT766_H__ -+#define __RT766_H__ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+struct rt766_sdca_priv { -+ struct regmap *regmap; -+ struct snd_soc_component *component; -+ struct sdw_slave *slave; -+ bool hw_init; -+ bool first_hw_init; -+ struct snd_soc_jack *hs_jack; -+ struct mutex disable_irq_lock; /* SDCA irq lock protection */ -+ bool disable_irq; -+ int jack_type; -+ bool fu41_dapm_mute; -+ bool fu41_mixer_l_mute; -+ bool fu41_mixer_r_mute; -+ bool fu113_dapm_mute; -+ bool fu113_mixer_mute[4]; -+ bool fu21_dapm_mute; -+ bool fu21_mixer_l_mute; -+ bool fu21_mixer_r_mute; -+ bool fu36_dapm_mute; -+ bool fu36_mixer_l_mute; -+ bool fu36_mixer_r_mute; -+ struct sdca_function_data *uaj_func_data; -+ struct sdca_function_data *sm_func_data; -+ struct sdca_function_data *sa_func_data; -+ struct sdca_function_data *hid_func_data; -+ struct sdca_interrupt_info *irq_info; -+ struct hid_device *hid; -+}; -+ -+/* vendor registers */ -+#define RT766_VERSION_ID 0xc404 -+#define RT766_DEV_ID1 0xc405 -+#define RT766_DEV_ID0 0xc406 -+#define RT766_BOND_LATCH_ID 0xc407 -+ -+#define RT766_HP_POWER_STATE 0x1000004 -+#define RT766_HP_FSM_CTL2_1 0x100000d -+ -+/* MCU Patch address */ -+#define RT766_MCU_PATCH_ADDR1_START 0x10010000 -+#define RT766_MCU_PATCH_ADDR1_END 0x10011fff -+#define RT766_MCU_PATCH_ADDR2_START 0x10020000 -+#define RT766_MCU_PATCH_ADDR2_END 0x10023fff -+ -+/* Buffer address for HID */ -+#define RT766_BUF_ADDR_HID1 0x44030000 -+#define RT766_BUF_ADDR_HID2 0x44030020 -+ -+/* SDCA (Channel) */ -+#define RT766_CH_1 0x01 -+#define RT766_CH_2 0x02 -+#define RT766_CH_3 0x03 -+#define RT766_CH_4 0x04 -+ -+/* RT766 SDCA Control - function number */ -+#define RT766_FUNC_NUM_UAJ 0x01 -+#define RT766_FUNC_NUM_MIC 0x02 -+#define RT766_FUNC_NUM_HID 0x03 -+#define RT766_FUNC_NUM_AMP 0x04 -+ -+/* RT766 SDCA entity */ -+#define RT766_SDCA_ENT_0 0x00 -+#define RT766_SDCA_ENT_HID101 0x01 -+#define RT766_SDCA_ENT_GE49 0x49 -+#define RT766_SDCA_ENT_USER_FU41 0x05 -+#define RT766_SDCA_ENT_USER_FU36 0x0f -+#define RT766_SDCA_ENT_USER_FU21 0x03 -+#define RT766_SDCA_ENT_USER_FU113 0x30 -+#define RT766_SDCA_ENT_PDE23 0x33 -+#define RT766_SDCA_ENT_PDE47 0x28 -+#define RT766_SDCA_ENT_PDE11 0x2a -+#define RT766_SDCA_ENT_PDE34 0x29 -+#define RT766_SDCA_ENT_CS41 0x01 -+#define RT766_SDCA_ENT_CS36 0x11 -+#define RT766_SDCA_ENT_CS113 0x12 -+#define RT766_SDCA_ENT_CS21 0x21 -+#define RT766_SDCA_ENT_PLATFORM_FU33 0x44 -+#define RT766_SDCA_ENT_PPU21 0x04 -+ -+/* sample frequency index */ -+#define RT766_SDCA_RATE_44100HZ 0x08 -+#define RT766_SDCA_RATE_48000HZ 0x09 -+#define RT766_SDCA_RATE_96000HZ 0x0b -+#define RT766_SDCA_RATE_192000HZ 0x0d -+ -+/* SDCA Register macros */ -+#define RT766_MUTE_REG(func, fu, ch) \ -+ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##fu, SDCA_CTL_FU_MUTE, RT766_CH_##ch) -+ -+#define RT766_VOLUME_REG(func, fu, ch) \ -+ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##fu, SDCA_CTL_FU_CHANNEL_VOLUME, RT766_CH_##ch) -+ -+#define RT766_GAIN_REG(func, fu, ch) \ -+ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##fu, SDCA_CTL_FU_GAIN, RT766_CH_##ch) -+ -+#define RT766_PDE_REQ_REG(func, pde) \ -+ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##pde, SDCA_CTL_PDE_REQUESTED_PS, 0) -+ -+#define RT766_PDE_ACTUAL_REG(func, pde) \ -+ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##pde, SDCA_CTL_PDE_ACTUAL_PS, 0) -+ -+#define RT766_FUNC_STATUS_REG(func) \ -+ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_0, SDCA_CTL_ENTITY_0_FUNCTION_STATUS, 0) -+ -+#define RT766_SDCA_CTL(func, ent, ctl) \ -+ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##ent, ctl, 0) -+ -+enum { -+ RT766_AIF1, -+ RT766_AIF2, -+ RT766_AIF3, -+}; -+ -+enum { -+ RT766_DAI_UAJ, -+ RT766_DAI_AMP, -+ RT766_DAI_MIC, -+}; -+ -+int rt766_sdca_io_init(struct device *dev, struct sdw_slave *slave); -+int rt766_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave *slave); -+#endif /* __RT766_H__ */ -diff --git a/sound/soc/codecs/rt9120.c b/sound/soc/codecs/rt9120.c ---- a/sound/soc/codecs/rt9120.c -+++ b/sound/soc/codecs/rt9120.c -@@ -606,12 +606,19 @@ static int rt9120_runtime_suspend(struct device *dev) - static int rt9120_runtime_resume(struct device *dev) - { - struct rt9120_data *data = dev_get_drvdata(dev); -+ int ret; - - if (data->pwdnn_gpio) { - gpiod_set_value(data->pwdnn_gpio, 1); - msleep(RT9120_CHIPON_WAITMS); - regcache_cache_only(data->regmap, false); -- regcache_sync(data->regmap); -+ ret = regcache_sync(data->regmap); -+ if (ret) { -+ regcache_cache_only(data->regmap, true); -+ regcache_mark_dirty(data->regmap); -+ gpiod_set_value(data->pwdnn_gpio, 0); -+ return ret; -+ } - } - - return 0; -diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c ---- a/sound/soc/codecs/sigmadsp.c -+++ b/sound/soc/codecs/sigmadsp.c -@@ -5,6 +5,7 @@ - * Copyright 2009-2014 Analog Devices Inc. - */ - -+#include - #include - #include - #include -@@ -135,7 +136,7 @@ static int sigmadsp_ctrl_put(struct snd_kcontrol *kcontrol, - uint8_t *data; - int ret = 0; - -- mutex_lock(&sigmadsp->lock); -+ guard(mutex)(&sigmadsp->lock); - - data = ucontrol->value.bytes.data; - -@@ -148,8 +149,6 @@ static int sigmadsp_ctrl_put(struct snd_kcontrol *kcontrol, - ctrl->cached = true; - } - -- mutex_unlock(&sigmadsp->lock); -- - return ret; - } - -@@ -160,7 +159,7 @@ static int sigmadsp_ctrl_get(struct snd_kcontrol *kcontrol, - struct sigmadsp *sigmadsp = snd_kcontrol_chip(kcontrol); - int ret = 0; - -- mutex_lock(&sigmadsp->lock); -+ guard(mutex)(&sigmadsp->lock); - - if (!ctrl->cached) { - ret = sigmadsp_read(sigmadsp, ctrl->addr, ctrl->cache, -@@ -174,8 +173,6 @@ static int sigmadsp_ctrl_get(struct snd_kcontrol *kcontrol, - ctrl->num_bytes); - } - -- mutex_unlock(&sigmadsp->lock); -- - return ret; - } - -@@ -487,7 +484,7 @@ static void devm_sigmadsp_release(struct device *dev, void *res) - static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) - { - const struct sigma_firmware_header *ssfw_head; -- const struct firmware *fw; -+ const struct firmware *fw __free(firmware) = NULL; - int ret; - u32 crc; - -@@ -495,7 +492,7 @@ static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) - ret = request_firmware(&fw, name, sigmadsp->dev); - if (ret) { - pr_debug("%s: request_firmware() failed with %i\n", __func__, ret); -- goto done; -+ return ret; - } - - /* then verify the header */ -@@ -509,13 +506,13 @@ static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) - */ - if (fw->size < sizeof(*ssfw_head) || fw->size >= 0x4000000) { - dev_err(sigmadsp->dev, "Failed to load firmware: Invalid size\n"); -- goto done; -+ return -EINVAL; - } - - ssfw_head = (void *)fw->data; - if (memcmp(ssfw_head->magic, SIGMA_MAGIC, ARRAY_SIZE(ssfw_head->magic))) { - dev_err(sigmadsp->dev, "Failed to load firmware: Invalid magic\n"); -- goto done; -+ return -EINVAL; - } - - crc = crc32(0, fw->data + sizeof(*ssfw_head), -@@ -524,7 +521,7 @@ static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) - if (crc != le32_to_cpu(ssfw_head->crc)) { - dev_err(sigmadsp->dev, "Failed to load firmware: Wrong crc checksum: expected %x got %x\n", - le32_to_cpu(ssfw_head->crc), crc); -- goto done; -+ return -EINVAL; - } - - switch (ssfw_head->version) { -@@ -545,9 +542,6 @@ static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) - if (ret) - sigmadsp_firmware_release(sigmadsp); - --done: -- release_firmware(fw); -- - return ret; - } - -@@ -677,10 +671,10 @@ static void sigmadsp_activate_ctrl(struct sigmadsp *sigmadsp, - return; - changed = snd_ctl_activate_id(card, &ctrl->kcontrol->id, active); - if (active && changed > 0) { -- mutex_lock(&sigmadsp->lock); -- if (ctrl->cached) -- sigmadsp_ctrl_write(sigmadsp, ctrl, ctrl->cache); -- mutex_unlock(&sigmadsp->lock); -+ scoped_guard(mutex, &sigmadsp->lock) { -+ if (ctrl->cached) -+ sigmadsp_ctrl_write(sigmadsp, ctrl, ctrl->cache); -+ } - } - } - -diff --git a/sound/soc/codecs/sma1303.c b/sound/soc/codecs/sma1303.c ---- a/sound/soc/codecs/sma1303.c -+++ b/sound/soc/codecs/sma1303.c -@@ -1777,6 +1777,8 @@ static void sma1303_i2c_remove(struct i2c_client *client) - struct sma1303_priv *sma1303 = - (struct sma1303_priv *) i2c_get_clientdata(client); - -+ if (sma1303->attr_grp) -+ sysfs_remove_group(sma1303->kobj, sma1303->attr_grp); - cancel_delayed_work_sync(&sma1303->check_fault_work); - } - -diff --git a/sound/soc/codecs/sma1307.c b/sound/soc/codecs/sma1307.c ---- a/sound/soc/codecs/sma1307.c -+++ b/sound/soc/codecs/sma1307.c -@@ -1690,7 +1690,7 @@ static void sma1307_check_fault_worker(struct work_struct *work) - - static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *file) - { -- const struct firmware *fw; -+ const struct firmware *fw __free(firmware) = NULL; - int size, offset, num_mode; - int ret; - -@@ -1703,22 +1703,18 @@ static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *fil - return; - } else if ((fw->size) < SMA1307_SETTING_HEADER_SIZE) { - dev_err(sma1307->dev, "%s: Invalid file\n", __func__); -- release_firmware(fw); - sma1307->set.status = false; - return; - } - - int *data __free(kfree) = kzalloc(fw->size, GFP_KERNEL); - if (!data) { -- release_firmware(fw); - sma1307->set.status = false; - return; - } - size = fw->size >> 2; - memcpy(data, fw->data, fw->size); - -- release_firmware(fw); -- - /* HEADER */ - sma1307->set.header_size = SMA1307_SETTING_HEADER_SIZE; - sma1307->set.checksum = data[sma1307->set.header_size - 2]; -diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c ---- a/sound/soc/codecs/sta32x.c -+++ b/sound/soc/codecs/sta32x.c -@@ -17,6 +17,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -269,9 +270,9 @@ static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol, - int numcoef = kcontrol->private_value >> 16; - int index = kcontrol->private_value & 0xffff; - unsigned int cfud, val; -- int i, ret = 0; -+ int i; - -- mutex_lock(&sta32x->coeff_lock); -+ guard(mutex)(&sta32x->coeff_lock); - - /* preserve reserved bits in STA32X_CFUD */ - regmap_read(sta32x->regmap, STA32X_CFUD, &cfud); -@@ -283,24 +284,20 @@ static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol, - regmap_write(sta32x->regmap, STA32X_CFUD, cfud); - - regmap_write(sta32x->regmap, STA32X_CFADDR2, index); -- if (numcoef == 1) { -+ if (numcoef == 1) - regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x04); -- } else if (numcoef == 5) { -+ else if (numcoef == 5) - regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x08); -- } else { -- ret = -EINVAL; -- goto exit_unlock; -- } -+ else -+ return -EINVAL; -+ - - for (i = 0; i < 3 * numcoef; i++) { - regmap_read(sta32x->regmap, STA32X_B1CF1 + i, &val); - ucontrol->value.bytes.data[i] = val; - } - --exit_unlock: -- mutex_unlock(&sta32x->coeff_lock); -- -- return ret; -+ return 0; - } - - static int sta32x_coefficient_put(struct snd_kcontrol *kcontrol, -diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c ---- a/sound/soc/codecs/sta350.c -+++ b/sound/soc/codecs/sta350.c -@@ -16,6 +16,7 @@ - - #define pr_fmt(fmt) KBUILD_MODNAME ":%s:%d: " fmt, __func__, __LINE__ - -+#include - #include - #include - #include -@@ -306,9 +307,9 @@ static int sta350_coefficient_get(struct snd_kcontrol *kcontrol, - int numcoef = kcontrol->private_value >> 16; - int index = kcontrol->private_value & 0xffff; - unsigned int cfud, val; -- int i, ret = 0; -+ int i; - -- mutex_lock(&sta350->coeff_lock); -+ guard(mutex)(&sta350->coeff_lock); - - /* preserve reserved bits in STA350_CFUD */ - regmap_read(sta350->regmap, STA350_CFUD, &cfud); -@@ -320,24 +321,19 @@ static int sta350_coefficient_get(struct snd_kcontrol *kcontrol, - regmap_write(sta350->regmap, STA350_CFUD, cfud); - - regmap_write(sta350->regmap, STA350_CFADDR2, index); -- if (numcoef == 1) { -+ if (numcoef == 1) - regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x04); -- } else if (numcoef == 5) { -+ else if (numcoef == 5) - regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x08); -- } else { -- ret = -EINVAL; -- goto exit_unlock; -- } -+ else -+ return -EINVAL; - - for (i = 0; i < 3 * numcoef; i++) { - regmap_read(sta350->regmap, STA350_B1CF1 + i, &val); - ucontrol->value.bytes.data[i] = val; - } - --exit_unlock: -- mutex_unlock(&sta350->coeff_lock); -- -- return ret; -+ return 0; - } - - static int sta350_coefficient_put(struct snd_kcontrol *kcontrol, -diff --git a/sound/soc/codecs/tac5xx2-sdw.c b/sound/soc/codecs/tac5xx2-sdw.c ---- a/sound/soc/codecs/tac5xx2-sdw.c -+++ b/sound/soc/codecs/tac5xx2-sdw.c -@@ -88,6 +88,9 @@ - #define TAC_FW_FILE_HDR 20 - #define TAC_MAX_FW_CHUNKS 512 - -+#define TAC_UAJ_PREP_CONNECTED 0xff -+#define TAC_UAJ_PREP_DISCONNECTED 0xdf -+ - struct tac_fw_hdr { - u32 size; - u32 version_offset; -@@ -138,6 +141,7 @@ struct tac5xx2_prv { - bool hw_init; - bool first_hw_init_done; - u32 part_id; -+ u32 rev_id; - struct snd_soc_jack *hs_jack; - int jack_type; - /* Custom fw binary. UMP File Download is not used. */ -@@ -261,7 +265,6 @@ static const struct reg_default tac_reg_default[] = { - {TAC_REG_SDW(0, 0, 0x78), 0x0}, - {TAC_REG_SDW(0, 0, 0x7b), 0x0}, - {TAC_REG_SDW(0, 0, 0x7c), 0xd0}, -- {TAC_REG_SDW(0, 0, 0x7d), 0x0}, - {TAC_REG_SDW(0, 0, 0x7e), 0x0}, - {TAC_REG_SDW(0, 1, 0x1), 0x0}, - {TAC_REG_SDW(0, 1, 0x2), 0x0}, -@@ -507,18 +510,6 @@ static const struct regmap_config tac_regmap = { - .use_single_write = true, - }; - --/* Check if device has DSP algo that needs status monitoring */ --static bool tac_has_dsp_algo(struct tac5xx2_prv *tac_dev) --{ -- switch (tac_dev->part_id) { -- case 0x5682: -- case 0x2883: -- return true; -- default: -- return false; -- } --} -- - /* Check if device has UAJ (Universal Audio Jack) support */ - static bool tac_has_uaj_support(struct tac5xx2_prv *tac_dev) - { -@@ -800,7 +791,7 @@ static int tac_sdw_hw_params(struct snd_pcm_substream *substream, - return ret; - } - -- ret = sdca_asoc_pde_poll_actual_ps(tac_dev->dev, tac_dev->regmap, function_id, pde_entity, -+ ret = sdca_asoc_pde_poll_actual_ps(tac_dev->regmap, function_id, pde_entity, - SDCA_PDE_PS3, SDCA_PDE_PS0, NULL, 0); - if (ret) - dev_err(tac_dev->dev, "failed to transition func %d, pde %d from PS3 -> PS0, err=%d\n", -@@ -847,7 +838,7 @@ static int tac_sdw_pcm_hw_free(struct snd_pcm_substream *substream, - return ret; - } - -- ret = sdca_asoc_pde_poll_actual_ps(tac_dev->dev, tac_dev->regmap, function_id, -+ ret = sdca_asoc_pde_poll_actual_ps(tac_dev->regmap, function_id, - pde_entity, SDCA_PDE_PS0, SDCA_PDE_PS3, - NULL, 0); - if (ret) -@@ -942,6 +933,7 @@ static int tac5xx2_sdca_button_detect(struct tac5xx2_prv *tac_dev) - static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev) - { - int val, ret; -+ u8 jack_prep; - - ret = regmap_read(tac_dev->regmap, - SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_GE35, -@@ -951,6 +943,8 @@ static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev) - return ret; - } - -+ jack_prep = TAC_UAJ_PREP_CONNECTED; -+ - switch (val) { - case 4: - tac_dev->jack_type = SND_JACK_MICROPHONE; -@@ -964,6 +958,7 @@ static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev) - case 0: - default: - tac_dev->jack_type = 0; -+ jack_prep = TAC_UAJ_PREP_DISCONNECTED; - break; - } - -@@ -973,26 +968,44 @@ static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev) - if (ret) - dev_err(tac_dev->dev, "Failed to update the jack type to device"); - -+ /* -+ * When uaj is uplugged and booted, we end up with the channel prepare -+ * timeout error for the uaj ports. This writes allows the channel prepare -+ * to succeed when the uaj is not plugged in. -+ */ -+ if (tac_dev->rev_id >= 0x30) { -+ ret = regmap_write(tac_dev->regmap, TAC_REG_SDW(0, 3, 127), jack_prep); -+ if (ret) -+ dev_warn(tac_dev->dev, "Failed to write jack_prep register: %d\n", ret); -+ } -+ - return 0; - } - - static int tac5xx2_jack_init(struct tac5xx2_prv *tac_dev) - { -+ u32 jd_int_mask, hid_int_mask; - int ret = 0; - -+ if (tac_dev->rev_id >= 0x30) { -+ jd_int_mask = SDW_SCP_SDCA_INTMASK_SDCA_12; -+ hid_int_mask = SDW_SCP_SDCA_INTMASK_SDCA_17; -+ } else { -+ jd_int_mask = SDW_SCP_SDCA_INTMASK_SDCA_11; -+ hid_int_mask = SDW_SCP_SDCA_INTMASK_SDCA_16; -+ } -+ - if (!tac_dev->hs_jack) - goto disable_interrupts; - -- ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK2, -- SDW_SCP_SDCA_INTMASK_SDCA_11); -+ ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK2, jd_int_mask); - if (ret) { - dev_err(tac_dev->dev, - "Failed to register jack detection interrupt: %d\n", ret); - goto disable_interrupts; - } - -- ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK3, -- SDW_SCP_SDCA_INTMASK_SDCA_16); -+ ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK3, hid_int_mask); - if (ret) { - dev_err(tac_dev->dev, - "Failed to register for button detect interrupt: %d\n", ret); -@@ -1051,6 +1064,7 @@ static int tac_interrupt_callback(struct sdw_slave *slave, - unsigned int sdca_int2, sdca_int3, jack_report_mask = 0; - struct tac5xx2_prv *tac_dev = dev_get_drvdata(&slave->dev); - struct device *dev = &slave->dev; -+ u32 headset_detect_chk, hid_detect_chk; - int btn_type = 0; - int ret = 0; - -@@ -1081,14 +1095,22 @@ static int tac_interrupt_callback(struct sdw_slave *slave, - dev_dbg(dev, "SDCA_INT2: 0x%02x, SDCA_INT3: 0x%02x\n", - sdca_int2, sdca_int3); - -- if (sdca_int2 & SDW_SCP_SDCA_INT_SDCA_11) { -+ if (tac_dev->rev_id >= 0x30) { -+ headset_detect_chk = SDW_SCP_SDCA_INT_SDCA_12; -+ hid_detect_chk = SDW_SCP_SDCA_INT_SDCA_17; -+ } else { -+ headset_detect_chk = SDW_SCP_SDCA_INT_SDCA_11; -+ hid_detect_chk = SDW_SCP_SDCA_INT_SDCA_16; -+ } -+ -+ if (sdca_int2 & headset_detect_chk) { - ret = tac5xx2_sdca_headset_detect(tac_dev); - if (ret < 0) - goto clear; - jack_report_mask |= SND_JACK_HEADSET; - } - -- if (sdca_int3 & SDW_SCP_SDCA_INT_SDCA_16) { -+ if (sdca_int3 & hid_detect_chk) { - btn_type = tac5xx2_sdca_button_detect(tac_dev); - if (btn_type < 0) - btn_type = 0; -@@ -1695,6 +1717,15 @@ static int tac_io_init(struct device *dev, struct sdw_slave *slave, bool first) - goto io_init_err; - } - -+ if (!tac_dev->rev_id) { -+ ret = regmap_read(tac_dev->regmap, TAC_REV_ID, &tac_dev->rev_id); -+ if (ret) { -+ dev_err(tac_dev->dev, "failed to rev id, err=%d\n", ret); -+ goto io_init_err; -+ } -+ dev_dbg(tac_dev->dev, "detected rev_id 0x%x", tac_dev->rev_id); -+ } -+ - if (tac_dev->fw_files && tac_dev->fw_file_cnt > 0) { - ret = tac_download_fw_to_hw(tac_dev); - if (ret) { -@@ -1940,7 +1971,7 @@ static s32 tac_sdw_probe(struct sdw_slave *peripheral, - "failed to allocate %s function data", - func_name); - function_data->desc = &peripheral->sdca_data.function[i]; -- ret = sdca_parse_function(dev, peripheral, function_data); -+ ret = sdca_parse_function(dev, function_data); - if (!ret) - *func_ptr = function_data; - else -@@ -1959,6 +1990,7 @@ static s32 tac_sdw_probe(struct sdw_slave *peripheral, - tac_dev->hw_init = false; - tac_dev->first_hw_init_done = false; - tac_dev->part_id = id->part_id; -+ tac_dev->rev_id = 0x0; - dev_set_drvdata(dev, tac_dev); - - regmap = devm_regmap_init_sdw_mbq_cfg(&peripheral->dev, peripheral, -@@ -1972,17 +2004,13 @@ static s32 tac_sdw_probe(struct sdw_slave *peripheral, - tac_dev->jack_type = 0; - init_completion(&tac_dev->fw_caching_complete); - -- if (tac_has_dsp_algo(tac_dev)) { -- tac_generate_fw_name(peripheral, tac_dev->fw_binaryname, -- sizeof(tac_dev->fw_binaryname)); -+ tac_generate_fw_name(peripheral, tac_dev->fw_binaryname, -+ sizeof(tac_dev->fw_binaryname)); - -- ret = tac_load_and_cache_firmware_async(tac_dev); -- if (ret) { -- complete_all(&tac_dev->fw_caching_complete); -- dev_dbg(dev, "failed to load fw: %d, use rom mode\n", ret); -- } -- } else { -+ ret = tac_load_and_cache_firmware_async(tac_dev); -+ if (ret) { - complete_all(&tac_dev->fw_caching_complete); -+ dev_dbg(dev, "failed to load fw: %d, use rom mode\n", ret); - } - - ret = tac_init(tac_dev); -diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c ---- a/sound/soc/codecs/tas2552.c -+++ b/sound/soc/codecs/tas2552.c -@@ -495,13 +495,21 @@ static int tas2552_runtime_suspend(struct device *dev) - static int tas2552_runtime_resume(struct device *dev) - { - struct tas2552_data *tas2552 = dev_get_drvdata(dev); -+ int ret; - - gpiod_set_value_cansleep(tas2552->enable_gpio, 1); - - tas2552_sw_shutdown(tas2552, 0); - - regcache_cache_only(tas2552->regmap, false); -- regcache_sync(tas2552->regmap); -+ ret = regcache_sync(tas2552->regmap); -+ if (ret) { -+ regcache_cache_only(tas2552->regmap, true); -+ regcache_mark_dirty(tas2552->regmap); -+ tas2552_sw_shutdown(tas2552, 1); -+ gpiod_set_value_cansleep(tas2552->enable_gpio, 0); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/tas2781-comlib-i2c.c b/sound/soc/codecs/tas2781-comlib-i2c.c ---- a/sound/soc/codecs/tas2781-comlib-i2c.c -+++ b/sound/soc/codecs/tas2781-comlib-i2c.c -@@ -6,6 +6,7 @@ - // - // Author: Shenghao Ding - -+#include - #include - #include - #include -@@ -342,7 +343,7 @@ int tascodec_init(struct tasdevice_priv *tas_priv, void *codec, - /* Codec Lock Hold to ensure that codec_probe and firmware parsing and - * loading do not simultaneously execute. - */ -- mutex_lock(&tas_priv->codec_lock); -+ guard(mutex)(&tas_priv->codec_lock); - - if (tas_priv->name_prefix) - scnprintf(tas_priv->rca_binaryname, 64, "%s-%sRCA%d.bin", -@@ -360,8 +361,6 @@ int tascodec_init(struct tasdevice_priv *tas_priv, void *codec, - dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n", - ret); - -- /* Codec Lock Release*/ -- mutex_unlock(&tas_priv->codec_lock); - return ret; - } - EXPORT_SYMBOL_GPL(tascodec_init); diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c --- a/sound/soc/codecs/tas2781-fmwlib.c +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -27808,6973 +11228,10 @@ diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib } else if (ret < 0) /*err in current device, skip it*/ break; } -@@ -2243,7 +2253,7 @@ int tas2781_load_calibration(void *context, char *file_name, - { - struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; - struct tasdevice *tasdev = &(tas_priv->tasdevice[i]); -- const struct firmware *fw_entry = NULL; -+ const struct firmware *fw_entry __free(firmware) = NULL; - struct tasdevice_fw *tas_fmw; - struct firmware fmw; - int offset = 0; -@@ -2253,60 +2263,50 @@ int tas2781_load_calibration(void *context, char *file_name, - if (ret) { - dev_err(tas_priv->dev, "%s: Request firmware %s failed\n", - __func__, file_name); -- goto out; -+ return ret; - } - - if (!fw_entry->size) { - dev_err(tas_priv->dev, "%s: file read error: size = %lu\n", - __func__, (unsigned long)fw_entry->size); -- ret = -EINVAL; -- goto out; -+ return -EINVAL; - } - fmw.size = fw_entry->size; - fmw.data = fw_entry->data; - - tas_fmw = tasdev->cali_data_fmw = kzalloc_obj(struct tasdevice_fw); -- if (!tasdev->cali_data_fmw) { -- ret = -ENOMEM; -- goto out; -- } -+ if (!tasdev->cali_data_fmw) -+ return -ENOMEM; -+ - tas_fmw->dev = tas_priv->dev; - offset = fw_parse_header(tas_priv, tas_fmw, &fmw, offset); - if (offset == -EINVAL) { - dev_err(tas_priv->dev, "fw_parse_header EXIT!\n"); -- ret = offset; -- goto out; -+ return -EINVAL; - } - offset = fw_parse_variable_hdr_cal(tas_priv, tas_fmw, &fmw, offset); - if (offset == -EINVAL) { - dev_err(tas_priv->dev, - "%s: fw_parse_variable_header_cal EXIT!\n", __func__); -- ret = offset; -- goto out; -+ return -EINVAL; - } - offset = fw_parse_program_data(tas_priv, tas_fmw, &fmw, offset); - if (offset < 0) { - dev_err(tas_priv->dev, "fw_parse_program_data EXIT!\n"); -- ret = offset; -- goto out; -+ return offset; - } - offset = fw_parse_configuration_data(tas_priv, tas_fmw, &fmw, offset); - if (offset < 0) { - dev_err(tas_priv->dev, "fw_parse_configuration_data EXIT!\n"); -- ret = offset; -- goto out; -+ return offset; - } - offset = fw_parse_calibration_data(tas_priv, tas_fmw, &fmw, offset); - if (offset < 0) { - dev_err(tas_priv->dev, "fw_parse_calibration_data EXIT!\n"); -- ret = offset; -- goto out; -+ return offset; - } - --out: -- release_firmware(fw_entry); -- -- return ret; -+ return 0; - } - EXPORT_SYMBOL_NS_GPL(tas2781_load_calibration, "SND_SOC_TAS2781_FMWLIB"); - -@@ -2399,7 +2399,7 @@ static int tasdevice_dspfw_ready(const struct firmware *fmw, - int tasdevice_dsp_parser(void *context) - { - struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; -- const struct firmware *fw_entry; -+ const struct firmware *fw_entry __free(firmware) = NULL; - int ret; - - ret = request_firmware(&fw_entry, tas_priv->coef_binaryname, -@@ -2407,15 +2407,10 @@ int tasdevice_dsp_parser(void *context) - if (ret) { - dev_err(tas_priv->dev, "%s: load %s error\n", __func__, - tas_priv->coef_binaryname); -- goto out; -+ return ret; - } - -- ret = tasdevice_dspfw_ready(fw_entry, tas_priv); -- release_firmware(fw_entry); -- fw_entry = NULL; -- --out: -- return ret; -+ return tasdevice_dspfw_ready(fw_entry, tas_priv); - } - EXPORT_SYMBOL_NS_GPL(tasdevice_dsp_parser, "SND_SOC_TAS2781_FMWLIB"); - -@@ -2797,11 +2792,12 @@ int tasdevice_prmg_load(void *context, int prm_no) - } - EXPORT_SYMBOL_NS_GPL(tasdevice_prmg_load, "SND_SOC_TAS2781_FMWLIB"); - --void tasdevice_tuning_switch(void *context, int state) -+void tasdevice_tuning_switch(void *context, int state, bool is_cap) - { - struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; - struct tasdevice_fw *tas_fmw = tas_priv->fmw; -- int profile_cfg_id = tas_priv->rcabin.profile_cfg_id; -+ int profile_cfg_id = is_cap ? tas_priv->rcabin.capture_profile_id : -+ tas_priv->rcabin.profile_cfg_id; - - /* - * Only RCA-based Playback can still work with no dsp program running -@@ -2818,7 +2814,6 @@ void tasdevice_tuning_switch(void *context, int state) - if (state == 0) { - if (tas_fmw && tas_priv->cur_prog < tas_fmw->nr_programs) { - /* dsp mode or tuning mode */ -- profile_cfg_id = tas_priv->rcabin.profile_cfg_id; - tasdevice_select_tuningprm_cfg(tas_priv, - tas_priv->cur_prog, tas_priv->cur_conf, - profile_cfg_id); -diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c ---- a/sound/soc/codecs/tas2781-i2c.c -+++ b/sound/soc/codecs/tas2781-i2c.c -@@ -13,6 +13,7 @@ - // Author: Kevin Lu - // - -+#include - #include - #include - #include -@@ -617,6 +618,7 @@ static int tasdev_calib_stop_put(struct snd_kcontrol *kcontrol, - { - struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); - struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp); -+ int i; - - guard(mutex)(&priv->codec_lock); - if (priv->chip_id == TAS2563) -@@ -624,6 +626,14 @@ static int tasdev_calib_stop_put(struct snd_kcontrol *kcontrol, - else - tas2781_calib_stop_put(priv); - -+ /* -+ * Set reloading-firmware flag after calibration, the flag will work -+ * during next playback, then set to the program id after reloading -+ * firmware. -+ */ -+ for (i = 0; i < priv->ndev; i++) -+ priv->tasdevice[i].cur_prog = -1; -+ - return 1; - } - -@@ -843,12 +853,12 @@ static int tasdevice_digital_gain_get( - unsigned char data[4]; - int ret; - -- mutex_lock(&tas_dev->codec_lock); -+ guard(mutex)(&tas_dev->codec_lock); - /* Read the primary device */ - ret = tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4); - if (ret) { - dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__); -- goto out; -+ return ret; - } - - target = get_unaligned_be32(&data[0]); -@@ -868,8 +878,7 @@ static int tasdevice_digital_gain_get( - /* find out the member same as or closer to the current volume */ - ucontrol->value.integer.value[0] = - abs(target - ar_l) <= abs(target - ar_r) ? l : r; --out: -- mutex_unlock(&tas_dev->codec_lock); -+ - return 0; - } - -@@ -882,29 +891,26 @@ static int tasdevice_digital_gain_put( - struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol); - struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec); - int vol = ucontrol->value.integer.value[0]; -- int status = 0, max = mc->max, rc = 1; -+ int status = 0, max = mc->max; - int i, ret; - unsigned int reg = mc->reg; - unsigned int volrd, volwr; - unsigned char data[4]; - - vol = clamp(vol, 0, max); -- mutex_lock(&tas_dev->codec_lock); -+ guard(mutex)(&tas_dev->codec_lock); - /* Read the primary device */ - ret = tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4); - if (ret) { - dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__); -- rc = -1; -- goto out; -+ return -1; - } - - volrd = get_unaligned_be32(&data[0]); - volwr = get_unaligned_be32(tas_dev->dvc_tlv_table[vol]); - -- if (volrd == volwr) { -- rc = 0; -- goto out; -- } -+ if (volrd == volwr) -+ return 0; - - for (i = 0; i < tas_dev->ndev; i++) { - ret = tasdevice_dev_bulk_write(tas_dev, i, reg, -@@ -918,10 +924,9 @@ static int tasdevice_digital_gain_put( - } - - if (status) -- rc = -1; --out: -- mutex_unlock(&tas_dev->codec_lock); -- return rc; -+ return -1; -+ -+ return 1; - } - - static const struct snd_kcontrol_new tasdevice_cali_controls[] = { -@@ -995,6 +1000,57 @@ static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol, - return ret; - } - -+/** -+ * tasdevice_set_capture_profile_id - Set runtime capture profile index via -+ * ALSA control -+ * @kcontrol: ALSA kcontrol handle that triggers this operation -+ * @ucontrol: User space control value carrying the new profile index -+ * -+ * This mixer control handler validates the user-provided capture profile ID -+ * against the maximum valid index parsed from the loaded DSP firmware, -+ * then updates the runtime stored capture profile ID only if the new value -+ * differs from the current active one. It will immediately return -EINVAL -+ * if the submitted profile ID falls outside the valid range, including the -+ * edge case that no valid configuration blocks are detected in firmware. -+ * -+ * No actual DSP register write is performed in this handler. The updated -+ * profile ID will be applied to the hardware when the next ALSA capture -+ * stream starts up. Caller does not need to take extra codec lock here, -+ * as the ALSA control core already guarantees serialized execution. -+ * -+ * Return: 1 if profile ID value was changed, 0 if no modification needed, -+ * -EINVAL if the input profile ID is out of valid range -+ */ -+static int tasdevice_set_capture_profile_id(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol); -+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); -+ unsigned int user_prof_id = ucontrol->value.integer.value[0]; -+ unsigned int max_valid_id; -+ int ret = 0; -+ -+ /* -+ * Align valid range with the bound defined in -+ * tasdevice_info_profile() -+ */ -+ max_valid_id = tas_priv->rcabin.ncfgs - 1; -+ -+ /* -+ * Reject invalid input including zero total configuration edge -+ * case -+ */ -+ if (tas_priv->rcabin.ncfgs == 0 || user_prof_id > max_valid_id) -+ return -EINVAL; -+ -+ if (tas_priv->rcabin.capture_profile_id != user_prof_id) { -+ tas_priv->rcabin.capture_profile_id = user_prof_id; -+ ret = 1; -+ } -+ -+ return ret; -+} -+ - static int tasdevice_info_active_num(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) - { -@@ -1075,6 +1131,41 @@ static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol, - return 0; - } - -+/** -+ * tasdevice_get_capture_profile_id - Report current active capture profile -+ * ID to user space -+ * @kcontrol: ALSA kcontrol structure passed from ALSA core -+ * @ucontrol: User-space control element value buffer to write the result back -+ * -+ * This function ensures the returned profile ID is always clamped inside the -+ * valid range advertised by the info callback, preventing accidental invalid -+ * values from being exposed to applications even if internal driver state is -+ * temporarily inconsistent. -+ * -+ * Returns 0 on successful fill of the control value, no error conditions -+ * are defined for this getter callback. -+ */ -+static int tasdevice_get_capture_profile_id(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol); -+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); -+ unsigned int max_valid_id, current_prof_id; -+ -+ max_valid_id = tas_priv->rcabin.ncfgs > 0 ? -+ (tas_priv->rcabin.ncfgs - 1U) : 0; -+ -+ /* -+ * Cast current profile id to unsigned to match type with max_valid_id, -+ * avoid signedness mismatch; -+ */ -+ current_prof_id = (unsigned int)tas_priv->rcabin.capture_profile_id; -+ /* Prevent underflow when there are no loaded capture profiles. */ -+ ucontrol->value.integer.value[0] = min(current_prof_id, max_valid_id); -+ -+ return 0; -+} -+ - static int tasdevice_get_chip_id(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) - { -@@ -1117,6 +1208,41 @@ static int tasdevice_create_control(struct tasdevice_priv *tas_priv) - ret = snd_soc_add_component_controls(tas_priv->codec, - prof_ctrls, nr_controls < mix_index ? nr_controls : mix_index); - -+ mix_index = 0; -+ switch (tas_priv->chip_id) { -+ case TAS2563: -+ case TAS2568: -+ case TAS2570: -+ case TAS2572: -+ case TAS2573: -+ case TAS2574: -+ case TAS2781: -+ prof_ctrls = devm_kcalloc(tas_priv->dev, nr_controls, -+ sizeof(prof_ctrls[0]), GFP_KERNEL); -+ if (!prof_ctrls) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ /* Create a mixer item for selecting the capture profile */ -+ name = devm_kstrdup(tas_priv->dev, "Speaker Capture Profile Id", -+ GFP_KERNEL); -+ if (!name) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ prof_ctrls[mix_index].name = name; -+ prof_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; -+ prof_ctrls[mix_index].info = tasdevice_info_profile; -+ prof_ctrls[mix_index].get = tasdevice_get_capture_profile_id; -+ prof_ctrls[mix_index].put = tasdevice_set_capture_profile_id; -+ mix_index++; -+ -+ ret = snd_soc_add_component_controls(tas_priv->codec, -+ prof_ctrls, nr_controls < mix_index ? nr_controls : mix_index); -+ break; -+ } -+ - out: - return ret; - } -@@ -1330,6 +1456,43 @@ static void alpa_cali_update(struct bulk_reg_val *p, - p->val_len = 4; - } - -+static int create_tas2781_cali_start_ktrl(struct tasdevice_priv -+ *priv, struct snd_kcontrol_new *cali_ctrl) -+{ -+ struct soc_bytes_ext *ext_cali_start; -+ char *cali_start_name; -+ -+ ext_cali_start = devm_kzalloc(priv->dev, -+ sizeof(*ext_cali_start), GFP_KERNEL); -+ if (!ext_cali_start) -+ return -ENOMEM; -+ -+ cali_start_name = devm_kstrdup(priv->dev, -+ "Calibration Start", GFP_KERNEL); -+ if (!cali_start_name) -+ return -ENOMEM; -+ /* -+ * package structure for tas2781 ftc start: -+ * Pkg len (1 byte) -+ * Reg id (1 byte, constant 'r') -+ * book, page, register for pilot threshold, pilot tone -+ * and sine gain (12 bytes) -+ * for (i = 0; i < Device-Sum; i++) { -+ * Device #i index_info (1 byte) -+ * Sine gain for Device #i (8 bytes) -+ * } -+ */ -+ ext_cali_start->max = 14 + priv->ndev * 9; -+ cali_ctrl->name = cali_start_name; -+ cali_ctrl->iface = SNDRV_CTL_ELEM_IFACE_MIXER; -+ cali_ctrl->info = snd_soc_bytes_info_ext; -+ cali_ctrl->put = tas2781_calib_start_put; -+ cali_ctrl->get = tasdev_nop_get; -+ cali_ctrl->private_value = (unsigned long)ext_cali_start; -+ -+ return 0; -+} -+ - static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv) - { - struct calidata *cali_data = &priv->cali_data; -@@ -1448,37 +1611,11 @@ static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv) - */ - cali_data->data[0] = 0xff; - if (priv->chip_id == TAS2781) { -- struct soc_bytes_ext *ext_cali_start; -- char *cali_start_name; -- -- ext_cali_start = devm_kzalloc(priv->dev, -- sizeof(*ext_cali_start), GFP_KERNEL); -- if (!ext_cali_start) -- return -ENOMEM; -- -- cali_start_name = devm_kstrdup(priv->dev, -- "Calibration Start", GFP_KERNEL); -- if (!cali_start_name) -- return -ENOMEM; -- /* -- * package structure for tas2781 ftc start: -- * Pkg len (1 byte) -- * Reg id (1 byte, constant 'r') -- * book, page, register for pilot threshold, pilot tone -- * and sine gain (12 bytes) -- * for (i = 0; i < Device-Sum; i++) { -- * Device #i index_info (1 byte) -- * Sine gain for Device #i (8 bytes) -- * } -- */ -- ext_cali_start->max = 14 + priv->ndev * 9; -- cali_ctrls[i].name = cali_start_name; -- cali_ctrls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; -- cali_ctrls[i].info = snd_soc_bytes_info_ext; -- cali_ctrls[i].put = tas2781_calib_start_put; -- cali_ctrls[i].get = tasdev_nop_get; -- cali_ctrls[i].private_value = (unsigned long)ext_cali_start; -+ rc = create_tas2781_cali_start_ktrl(priv, &cali_ctrls[i]); -+ if (rc != 0) -+ return rc; - i++; -+ - } - - return snd_soc_add_component_controls(priv->codec, cali_ctrls, -@@ -1765,13 +1902,25 @@ static int tasdevice_dapm_event(struct snd_soc_dapm_widget *w, - struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); - int state = 0; - -- /* Codec Lock Hold */ -- mutex_lock(&tas_priv->codec_lock); -+ guard(mutex)(&tas_priv->codec_lock); - if (event == SND_SOC_DAPM_PRE_PMD) - state = 1; -- tasdevice_tuning_switch(tas_priv, state); -- /* Codec Lock Release*/ -- mutex_unlock(&tas_priv->codec_lock); -+ tasdevice_tuning_switch(tas_priv, state, false); -+ -+ return 0; -+} -+ -+static int tasdevice_capture_dapm_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *kcontrol, int event) -+{ -+ struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); -+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); -+ int state = 0; -+ -+ guard(mutex)(&tas_priv->codec_lock); -+ if (event == SND_SOC_DAPM_PRE_PMD) -+ state = 1; -+ tasdevice_tuning_switch(tas_priv, state, true); - - return 0; - } -@@ -1779,7 +1928,7 @@ static int tasdevice_dapm_event(struct snd_soc_dapm_widget *w, - static const struct snd_soc_dapm_widget tasdevice_dapm_widgets[] = { - SND_SOC_DAPM_AIF_IN("ASI", "ASI Playback", 0, SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_AIF_OUT_E("ASI OUT", "ASI Capture", 0, SND_SOC_NOPM, -- 0, 0, tasdevice_dapm_event, -+ 0, 0, tasdevice_capture_dapm_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - SND_SOC_DAPM_SPK("SPK", tasdevice_dapm_event), - SND_SOC_DAPM_OUTPUT("OUT"), -diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c ---- a/sound/soc/codecs/tas2783-sdw.c -+++ b/sound/soc/codecs/tas2783-sdw.c -@@ -12,6 +12,7 @@ - // Author: Baojun Xu - // Author: Kevin Lu - -+#include - #include - #include - #include -@@ -494,6 +495,56 @@ static bool tas2783_readable_register(struct device *dev, unsigned int reg) - return tas2783_sdca_mbq_size(dev, reg) > 0; - } - -+static bool tas2783_writeable_register(struct device *dev, unsigned int reg) -+{ -+ /* -+ * The Latency Control of every Entity, together with the Power Domain -+ * actual state and the protection status, is read-only. They are -+ * listed in tas2783_reg_default[] with a placeholder value, so without -+ * this a regcache_sync() would try to write them back and the -+ * peripheral would reject the transaction, aborting the sync. -+ */ -+ switch (reg) { -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU21, 0x10, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU23, 0x10, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU26, 0x10, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_XU22, 0x06, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_XU22, 0x07, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_XU22, 0x08, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS24, 0x02, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS21, 0x02, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS25, 0x02, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS26, 0x02, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS28, 0x02, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_PDE23, 0x10, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_UDMPU23, 0x06, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_SAPU29, 0x05, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_SAPU29, 0x11, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_PPU21, 0x06, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_PPU26, 0x06, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT21, 0x08, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT29, 0x08, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT26, 0x08, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT28, 0x08, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT24, 0x08, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT23, 0x08, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT25, 0x08, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT28, 0x08, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MU26, 0x06, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT127, 0x08, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU127, 0x10, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS127, 0x02, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU21, 0x08, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU21, 0x04, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU26, 0x08, 0): -+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU26, 0x04, 0): -+ return false; -+ -+ default: -+ return tas2783_sdca_mbq_size(dev, reg) > 0; -+ } -+} -+ - static bool tas2783_volatile_register(struct device *dev, u32 reg) - { - switch (reg) { -@@ -515,6 +566,7 @@ static const struct regmap_config tas_regmap = { - .reg_bits = 32, - .val_bits = 8, - .readable_reg = tas2783_readable_register, -+ .writeable_reg = tas2783_writeable_register, - .volatile_reg = tas2783_volatile_register, - .reg_defaults = tas2783_reg_default, - .num_reg_defaults = ARRAY_SIZE(tas2783_reg_default), -@@ -692,12 +744,12 @@ static s32 tas2783_update_calibdata(struct tas2783_prv *tas_dev) - return 0; - } - -- mutex_lock(&tas_dev->calib_lock); -- ret = tas2783_validate_calibdata(tas_dev, tas_dev->cali_data.data, -- tas_dev->cali_data.read_sz); -- if (!ret) -- tas2783_set_calib_params_to_device(tas_dev, tmp_val); -- mutex_unlock(&tas_dev->calib_lock); -+ scoped_guard(mutex, &tas_dev->calib_lock) { -+ ret = tas2783_validate_calibdata(tas_dev, tas_dev->cali_data.data, -+ tas_dev->cali_data.read_sz); -+ if (!ret) -+ tas2783_set_calib_params_to_device(tas_dev, tmp_val); -+ } - - return ret; - } -@@ -926,22 +978,23 @@ static s32 tas_sdw_hw_params(struct snd_pcm_substream *substream, - dev_err(tas_dev->dev, - "clear latch failed, err=%d", ret); - -- mutex_lock(&tas_dev->pde_lock); -- /* -- * Sometimes, there is error returned during power on. -- * So added retry logic to ensure power on so that -- * port prepare succeeds -- */ -- do { -- ret = regmap_write(tas_dev->regmap, -- SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, -- TAS2783_SDCA_CTL_REQ_POW_STATE, 0), -- TAS2783_SDCA_POW_STATE_ON); -- if (!ret) -- break; -- usleep_range(2000, 2200); -- } while (retry--); -- mutex_unlock(&tas_dev->pde_lock); -+ scoped_guard(mutex, &tas_dev->pde_lock) { -+ /* -+ * Sometimes, there is error returned during power on. -+ * So added retry logic to ensure power on so that -+ * port prepare succeeds -+ */ -+ do { -+ ret = regmap_write(tas_dev->regmap, -+ SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, -+ TAS2783_SDCA_CTL_REQ_POW_STATE, 0), -+ TAS2783_SDCA_POW_STATE_ON); -+ if (!ret) -+ break; -+ usleep_range(2000, 2200); -+ } while (retry--); -+ } -+ - if (ret) - return ret; - -@@ -965,7 +1018,6 @@ static s32 tas_sdw_hw_params(struct snd_pcm_substream *substream, - static s32 tas_sdw_pcm_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) - { -- s32 ret; - struct snd_soc_component *component = dai->component; - struct tas2783_prv *tas_dev = - snd_soc_component_get_drvdata(component); -@@ -974,14 +1026,11 @@ static s32 tas_sdw_pcm_hw_free(struct snd_pcm_substream *substream, - - sdw_stream_remove_slave(tas_dev->sdw_peripheral, sdw_stream); - -- mutex_lock(&tas_dev->pde_lock); -- ret = regmap_write(tas_dev->regmap, -- SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, -- TAS2783_SDCA_CTL_REQ_POW_STATE, 0), -- TAS2783_SDCA_POW_STATE_OFF); -- mutex_unlock(&tas_dev->pde_lock); -- -- return ret; -+ guard(mutex)(&tas_dev->pde_lock); -+ return regmap_write(tas_dev->regmap, -+ SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, -+ TAS2783_SDCA_CTL_REQ_POW_STATE, 0), -+ TAS2783_SDCA_POW_STATE_OFF); - } - - static const struct snd_soc_dai_ops tas_dai_ops = { -@@ -1098,7 +1147,13 @@ static s32 tas2783_sdca_dev_resume(struct device *dev) - } - - regcache_cache_only(tas_dev->regmap, false); -- regcache_sync(tas_dev->regmap); -+ ret = regcache_sync(tas_dev->regmap); -+ if (ret) { -+ regcache_cache_only(tas_dev->regmap, true); -+ regcache_mark_dirty(tas_dev->regmap); -+ return ret; -+ } -+ - return 0; - } - -@@ -1224,9 +1279,23 @@ static s32 tas_update_status(struct sdw_slave *slave, - if (tas_dev->hw_init || tas_dev->status != SDW_SLAVE_ATTACHED) - return 0; - -- /* updated the cache data to device */ - regcache_cache_only(tas_dev->regmap, false); -- regcache_sync(tas_dev->regmap); -+ -+ /* -+ * The device is attaching uninitialized: either this is the first -+ * attach, or it lost power (and with it all register and DSP state) -+ * while the controller was power-gated during system suspend. The -+ * cache still holds the pre-suspend values, and tas_io_init() below -+ * resets the device via TAS2783_SW_RESET anyway, so syncing it back -+ * is both useless and harmful: later read-modify-write updates would -+ * compare against stale data and skip the hardware write. -+ * -+ * Drop the cache instead, so that subsequent accesses see the real -+ * hardware state. Syncing after the reset is not an option either: -+ * the cache accepts registers for which tas2783_sdca_mbq_size() -+ * returns 0, and writing those back fails with -EINVAL. -+ */ -+ regcache_drop_region(tas_dev->regmap, 0, UINT_MAX); - - /* perform I/O transfers required for Slave initialization */ - return tas_io_init(&slave->dev, slave); -@@ -1329,7 +1398,7 @@ static s32 tas_sdw_probe(struct sdw_slave *peripheral, - function_data->desc = &peripheral->sdca_data.function[i]; - - /* Parse the function */ -- ret = sdca_parse_function(dev, peripheral, function_data); -+ ret = sdca_parse_function(dev, function_data); - if (!ret) - tas_dev->sa_func_data = function_data; - else -diff --git a/sound/soc/codecs/tas5805m.c b/sound/soc/codecs/tas5805m.c ---- a/sound/soc/codecs/tas5805m.c -+++ b/sound/soc/codecs/tas5805m.c -@@ -12,6 +12,7 @@ - // - // It has been simplified a little and reworked for the 5.x ALSA SoC API. - -+#include - #include - #include - #include -@@ -230,10 +231,9 @@ static int tas5805m_vol_get(struct snd_kcontrol *kcontrol, - struct tas5805m_priv *tas5805m = - snd_soc_component_get_drvdata(component); - -- mutex_lock(&tas5805m->lock); -+ guard(mutex)(&tas5805m->lock); - ucontrol->value.integer.value[0] = tas5805m->vol[0]; - ucontrol->value.integer.value[1] = tas5805m->vol[1]; -- mutex_unlock(&tas5805m->lock); - - return 0; - } -@@ -249,13 +249,12 @@ static int tas5805m_vol_put(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct tas5805m_priv *tas5805m = - snd_soc_component_get_drvdata(component); -- int ret = 0; - - if (!(volume_is_valid(ucontrol->value.integer.value[0]) && - volume_is_valid(ucontrol->value.integer.value[1]))) - return -EINVAL; - -- mutex_lock(&tas5805m->lock); -+ guard(mutex)(&tas5805m->lock); - if (tas5805m->vol[0] != ucontrol->value.integer.value[0] || - tas5805m->vol[1] != ucontrol->value.integer.value[1]) { - tas5805m->vol[0] = ucontrol->value.integer.value[0]; -@@ -265,11 +264,10 @@ static int tas5805m_vol_put(struct snd_kcontrol *kcontrol, - tas5805m->is_powered); - if (tas5805m->is_powered) - tas5805m_refresh(tas5805m); -- ret = 1; -+ return 1; - } -- mutex_unlock(&tas5805m->lock); - -- return ret; -+ return 0; - } - - static const struct snd_kcontrol_new tas5805m_snd_controls[] = { -@@ -332,7 +330,7 @@ static void do_work(struct work_struct *work) - - dev_dbg(&tas5805m->i2c->dev, "DSP startup\n"); - -- mutex_lock(&tas5805m->lock); -+ guard(mutex)(&tas5805m->lock); - /* We mustn't issue any I2C transactions until the I2S - * clock is stable. Furthermore, we must allow a 5ms - * delay after the first set of register writes to -@@ -345,7 +343,6 @@ static void do_work(struct work_struct *work) - - tas5805m->is_powered = true; - tas5805m_refresh(tas5805m); -- mutex_unlock(&tas5805m->lock); - } - - static int tas5805m_dac_event(struct snd_soc_dapm_widget *w, -@@ -362,7 +359,7 @@ static int tas5805m_dac_event(struct snd_soc_dapm_widget *w, - dev_dbg(component->dev, "DSP shutdown\n"); - cancel_work_sync(&tas5805m->work); - -- mutex_lock(&tas5805m->lock); -+ guard(mutex)(&tas5805m->lock); - if (tas5805m->is_powered) { - tas5805m->is_powered = false; - -@@ -379,7 +376,6 @@ static int tas5805m_dac_event(struct snd_soc_dapm_widget *w, - - regmap_write(rm, REG_DEVICE_CTRL_2, DCTRL2_MODE_HIZ); - } -- mutex_unlock(&tas5805m->lock); - } - - return 0; -@@ -414,14 +410,13 @@ static int tas5805m_mute(struct snd_soc_dai *dai, int mute, int direction) - struct tas5805m_priv *tas5805m = - snd_soc_component_get_drvdata(component); - -- mutex_lock(&tas5805m->lock); -+ guard(mutex)(&tas5805m->lock); - dev_dbg(component->dev, "set mute=%d (is_powered=%d)\n", - mute, tas5805m->is_powered); - - tas5805m->is_muted = mute; - if (tas5805m->is_powered) - tas5805m_refresh(tas5805m); -- mutex_unlock(&tas5805m->lock); - - return 0; - } -@@ -462,7 +457,6 @@ static int tas5805m_i2c_probe(struct i2c_client *i2c) - struct tas5805m_priv *tas5805m; - char filename[128]; - const char *config_name; -- const struct firmware *fw; - int ret; - - regmap = devm_regmap_init_i2c(i2c, &tas5805m_regmap); -@@ -507,24 +501,20 @@ static int tas5805m_i2c_probe(struct i2c_client *i2c) - - snprintf(filename, sizeof(filename), "tas5805m_dsp_%s.bin", - config_name); -+ const struct firmware *fw __free(firmware) = NULL; - ret = request_firmware(&fw, filename, dev); - if (ret) - return ret; - - if ((fw->size < 2) || (fw->size & 1)) { - dev_err(dev, "firmware is invalid\n"); -- release_firmware(fw); - return -EINVAL; - } - - tas5805m->dsp_cfg_len = fw->size; - tas5805m->dsp_cfg_data = devm_kmemdup(dev, fw->data, fw->size, GFP_KERNEL); -- if (!tas5805m->dsp_cfg_data) { -- release_firmware(fw); -+ if (!tas5805m->dsp_cfg_data) - return -ENOMEM; -- } -- -- release_firmware(fw); - - /* Do the first part of the power-on here, while we can expect - * the I2S interface to be quiet. We must raise PDN# and then -diff --git a/sound/soc/codecs/tas675x.c b/sound/soc/codecs/tas675x.c ---- a/sound/soc/codecs/tas675x.c -+++ b/sound/soc/codecs/tas675x.c -@@ -1924,15 +1924,6 @@ static const struct reg_default tas675x_reg_defaults[] = { - { TAS675X_PWM_PHASE_M_CTRL_CH2_REG, 0x00 }, - { TAS675X_PWM_PHASE_M_CTRL_CH3_REG, 0x00 }, - { TAS675X_PWM_PHASE_M_CTRL_CH4_REG, 0x00 }, -- { TAS675X_DC_LDG_CTRL_REG, 0x00 }, -- { TAS675X_DC_LDG_LO_CTRL_REG, 0x00 }, -- { TAS675X_DC_LDG_TIME_CTRL_REG, 0x00 }, -- { TAS675X_DC_LDG_SL_CH1_CH2_CTRL_REG, 0x11 }, -- { TAS675X_DC_LDG_SL_CH3_CH4_CTRL_REG, 0x11 }, -- { TAS675X_AC_LDG_CTRL_REG, 0x10 }, -- { TAS675X_TWEETER_DETECT_CTRL_REG, 0x08 }, -- { TAS675X_TWEETER_DETECT_THRESH_REG, 0x00 }, -- { TAS675X_AC_LDG_FREQ_CTRL_REG, 0xC8 }, - { TAS675X_REPORT_ROUTING_1_REG, 0x00 }, - { TAS675X_OTSD_RECOVERY_EN_REG, 0x00 }, - { TAS675X_REPORT_ROUTING_2_REG, 0xA2 }, -@@ -1943,6 +1934,15 @@ static const struct reg_default tas675x_reg_defaults[] = { - { TAS675X_GPIO1_OUTPUT_SEL_REG, 0x00 }, - { TAS675X_GPIO2_OUTPUT_SEL_REG, 0x00 }, - { TAS675X_GPIO_CTRL_REG, TAS675X_GPIO_CTRL_RSTVAL }, -+ { TAS675X_DC_LDG_CTRL_REG, 0x00 }, -+ { TAS675X_DC_LDG_LO_CTRL_REG, 0x00 }, -+ { TAS675X_DC_LDG_TIME_CTRL_REG, 0x00 }, -+ { TAS675X_DC_LDG_SL_CH1_CH2_CTRL_REG, 0x11 }, -+ { TAS675X_DC_LDG_SL_CH3_CH4_CTRL_REG, 0x11 }, -+ { TAS675X_AC_LDG_CTRL_REG, 0x10 }, -+ { TAS675X_TWEETER_DETECT_CTRL_REG, 0x08 }, -+ { TAS675X_TWEETER_DETECT_THRESH_REG, 0x00 }, -+ { TAS675X_AC_LDG_FREQ_CTRL_REG, 0xC8 }, - { TAS675X_OTW_CTRL_CH1_CH2_REG, 0x11 }, - { TAS675X_OTW_CTRL_CH3_CH4_REG, 0x11 }, - }; -diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c ---- a/sound/soc/codecs/tlv320aic26.c -+++ b/sound/soc/codecs/tlv320aic26.c -@@ -320,8 +320,14 @@ static int aic26_probe(struct snd_soc_component *component) - return 0; - } - -+static void aic26_remove(struct snd_soc_component *component) -+{ -+ device_remove_file(component->dev, &dev_attr_keyclick); -+} -+ - static const struct snd_soc_component_driver aic26_soc_component_dev = { - .probe = aic26_probe, -+ .remove = aic26_remove, - .controls = aic26_snd_controls, - .num_controls = ARRAY_SIZE(aic26_snd_controls), - .dapm_widgets = tlv320aic26_dapm_widgets, -diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c ---- a/sound/soc/codecs/tlv320aic31xx.c -+++ b/sound/soc/codecs/tlv320aic31xx.c -@@ -1720,18 +1720,14 @@ static int tlv320dac3100_fw_load(struct aic31xx_priv *aic31xx, - static int tlv320dac3100_load_coeffs(struct aic31xx_priv *aic31xx, - const char *fw_name) - { -- const struct firmware *fw; -+ const struct firmware *fw __free(firmware) = NULL; - int ret; - - ret = request_firmware(&fw, fw_name, aic31xx->dev); - if (ret) - return ret; - -- ret = tlv320dac3100_fw_load(aic31xx, fw->data, fw->size); -- -- release_firmware(fw); -- -- return ret; -+ return tlv320dac3100_fw_load(aic31xx, fw->data, fw->size); - } - - static int aic31xx_i2c_probe(struct i2c_client *i2c) -diff --git a/sound/soc/codecs/tlv320aic32x4-clk.c b/sound/soc/codecs/tlv320aic32x4-clk.c ---- a/sound/soc/codecs/tlv320aic32x4-clk.c -+++ b/sound/soc/codecs/tlv320aic32x4-clk.c -@@ -1,5 +1,5 @@ --/* SPDX-License-Identifier: GPL-2.0 -- * -+// SPDX-License-Identifier: GPL-2.0 -+/* - * Clock Tree for the Texas Instruments TLV320AIC32x4 - * - * Copyright 2019 Annaliese McDermond -@@ -9,6 +9,7 @@ - - #include - #include -+#include - #include - #include - -@@ -57,7 +58,7 @@ static void clk_aic32x4_pll_unprepare(struct clk_hw *hw) - struct clk_aic32x4 *pll = to_clk_aic32x4(hw); - - regmap_update_bits(pll->regmap, AIC32X4_PLLPR, -- AIC32X4_PLLEN, 0); -+ AIC32X4_PLLEN, 0); - } - - static int clk_aic32x4_pll_is_prepared(struct clk_hw *hw) -@@ -75,7 +76,7 @@ static int clk_aic32x4_pll_is_prepared(struct clk_hw *hw) - } - - static int clk_aic32x4_pll_get_muldiv(struct clk_aic32x4 *pll, -- struct clk_aic32x4_pll_muldiv *settings) -+ struct clk_aic32x4_pll_muldiv *settings) - { - /* Change to use regmap_bulk_read? */ - unsigned int val; -@@ -106,19 +107,19 @@ static int clk_aic32x4_pll_get_muldiv(struct clk_aic32x4 *pll, - } - - static int clk_aic32x4_pll_set_muldiv(struct clk_aic32x4 *pll, -- struct clk_aic32x4_pll_muldiv *settings) -+ struct clk_aic32x4_pll_muldiv *settings) - { - int ret; - /* Change to use regmap_bulk_write for some if not all? */ - - ret = regmap_update_bits(pll->regmap, AIC32X4_PLLPR, -- AIC32X4_PLL_R_MASK, settings->r); -+ AIC32X4_PLL_R_MASK, settings->r); - if (ret < 0) - return ret; - - ret = regmap_update_bits(pll->regmap, AIC32X4_PLLPR, -- AIC32X4_PLL_P_MASK, -- settings->p << AIC32X4_PLL_P_SHIFT); -+ AIC32X4_PLL_P_MASK, -+ settings->p << AIC32X4_PLL_P_SHIFT); - if (ret < 0) - return ret; - -@@ -136,23 +137,21 @@ static int clk_aic32x4_pll_set_muldiv(struct clk_aic32x4 *pll, - return 0; - } - --static unsigned long clk_aic32x4_pll_calc_rate( -- struct clk_aic32x4_pll_muldiv *settings, -- unsigned long parent_rate) -+static unsigned long clk_aic32x4_pll_calc_rate(struct clk_aic32x4_pll_muldiv *settings, -+ unsigned long parent_rate) - { - u64 rate; - /* - * We scale j by 10000 to account for the decimal part of P and divide - * it back out later. - */ -- rate = (u64) parent_rate * settings->r * -- ((settings->j * 10000) + settings->d); -+ rate = (u64)parent_rate * settings->r * ((settings->j * 10000) + settings->d); - -- return (unsigned long) DIV_ROUND_UP_ULL(rate, settings->p * 10000); -+ return (unsigned long)DIV_ROUND_UP_ULL(rate, settings->p * 10000); - } - - static int clk_aic32x4_pll_calc_muldiv(struct clk_aic32x4_pll_muldiv *settings, -- unsigned long rate, unsigned long parent_rate) -+ unsigned long rate, unsigned long parent_rate) - { - u64 multiplier; - -@@ -165,14 +164,14 @@ static int clk_aic32x4_pll_calc_muldiv(struct clk_aic32x4_pll_muldiv *settings, - * of the multiplier. This is because we can't do floating point - * math in the kernel. - */ -- multiplier = (u64) rate * settings->p * 10000; -+ multiplier = (u64)rate * settings->p * 10000; - do_div(multiplier, parent_rate); - - /* - * J can't be over 64, so R can scale this. - * R can't be greater than 4. - */ -- settings->r = ((u32) multiplier / 640000) + 1; -+ settings->r = ((u32)multiplier / 640000) + 1; - if (settings->r > 4) - return -1; - do_div(multiplier, settings->r); -@@ -184,14 +183,14 @@ static int clk_aic32x4_pll_calc_muldiv(struct clk_aic32x4_pll_muldiv *settings, - return -1; - - /* Figure out the integer part, J, and the fractional part, D. */ -- settings->j = (u32) multiplier / 10000; -- settings->d = (u32) multiplier % 10000; -+ settings->j = (u32)multiplier / 10000; -+ settings->d = (u32)multiplier % 10000; - - return 0; - } - - static unsigned long clk_aic32x4_pll_recalc_rate(struct clk_hw *hw, -- unsigned long parent_rate) -+ unsigned long parent_rate) - { - struct clk_aic32x4 *pll = to_clk_aic32x4(hw); - struct clk_aic32x4_pll_muldiv settings; -@@ -220,8 +219,8 @@ static int clk_aic32x4_pll_determine_rate(struct clk_hw *hw, - } - - static int clk_aic32x4_pll_set_rate(struct clk_hw *hw, -- unsigned long rate, -- unsigned long parent_rate) -+ unsigned long rate, -+ unsigned long parent_rate) - { - struct clk_aic32x4 *pll = to_clk_aic32x4(hw); - struct clk_aic32x4_pll_muldiv settings; -@@ -236,7 +235,7 @@ static int clk_aic32x4_pll_set_rate(struct clk_hw *hw, - return ret; - - /* 10ms is the delay to wait before the clocks are stable */ -- msleep(10); -+ usleep_range(10000, 20000); - - return 0; - } -@@ -261,7 +260,6 @@ static u8 clk_aic32x4_pll_get_parent(struct clk_hw *hw) - return (val & AIC32X4_PLL_CLKIN_MASK) >> AIC32X4_PLL_CLKIN_SHIFT; - } - -- - static const struct clk_ops aic32x4_pll_ops = { - .prepare = clk_aic32x4_pll_prepare, - .unprepare = clk_aic32x4_pll_unprepare, -@@ -311,11 +309,11 @@ static void clk_aic32x4_div_unprepare(struct clk_hw *hw) - struct clk_aic32x4 *div = to_clk_aic32x4(hw); - - regmap_update_bits(div->regmap, div->reg, -- AIC32X4_DIVEN, 0); -+ AIC32X4_DIVEN, 0); - } - - static int clk_aic32x4_div_set_rate(struct clk_hw *hw, unsigned long rate, -- unsigned long parent_rate) -+ unsigned long parent_rate) - { - struct clk_aic32x4 *div = to_clk_aic32x4(hw); - u8 divisor; -@@ -342,7 +340,7 @@ static int clk_aic32x4_div_determine_rate(struct clk_hw *hw, - } - - static unsigned long clk_aic32x4_div_recalc_rate(struct clk_hw *hw, -- unsigned long parent_rate) -+ unsigned long parent_rate) - { - struct clk_aic32x4 *div = to_clk_aic32x4(hw); - unsigned int val; -@@ -399,7 +397,7 @@ static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { - { - .name = "pll", - .parent_names = -- (const char* []) { "mclk", "bclk", "gpio", "din" }, -+ (const char *[]) { "mclk", "bclk", "gpio", "din" }, - .num_parents = 4, - .ops = &aic32x4_pll_ops, - .reg = 0, -@@ -414,28 +412,28 @@ static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { - }, - { - .name = "ndac", -- .parent_names = (const char * []) { "codec_clkin" }, -+ .parent_names = (const char *[]) { "codec_clkin" }, - .num_parents = 1, - .ops = &aic32x4_div_ops, - .reg = AIC32X4_NDAC, - }, - { - .name = "mdac", -- .parent_names = (const char * []) { "ndac" }, -+ .parent_names = (const char *[]) { "ndac" }, - .num_parents = 1, - .ops = &aic32x4_div_ops, - .reg = AIC32X4_MDAC, - }, - { - .name = "nadc", -- .parent_names = (const char * []) { "codec_clkin" }, -+ .parent_names = (const char *[]) { "codec_clkin" }, - .num_parents = 1, - .ops = &aic32x4_div_ops, - .reg = AIC32X4_NADC, - }, - { - .name = "madc", -- .parent_names = (const char * []) { "nadc" }, -+ .parent_names = (const char *[]) { "nadc" }, - .num_parents = 1, - .ops = &aic32x4_div_ops, - .reg = AIC32X4_MADC, -@@ -451,7 +449,7 @@ static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { - }; - - static struct clk *aic32x4_register_clk(struct device *dev, -- struct aic32x4_clkdesc *desc) -+ struct aic32x4_clkdesc *desc) - { - struct clk_init_data init; - struct clk_aic32x4 *priv; -@@ -464,8 +462,8 @@ static struct clk *aic32x4_register_clk(struct device *dev, - init.flags = 0; - - priv = devm_kzalloc(dev, sizeof(struct clk_aic32x4), GFP_KERNEL); -- if (priv == NULL) -- return (struct clk *) -ENOMEM; -+ if (!priv) -+ return ERR_PTR(-ENOMEM); - - priv->dev = dev; - priv->hw.init = &init; -diff --git a/sound/soc/codecs/tlv320aic32x4-i2c.c b/sound/soc/codecs/tlv320aic32x4-i2c.c ---- a/sound/soc/codecs/tlv320aic32x4-i2c.c -+++ b/sound/soc/codecs/tlv320aic32x4-i2c.c -@@ -1,11 +1,10 @@ --/* SPDX-License-Identifier: GPL-2.0 -- * -+// SPDX-License-Identifier: GPL-2.0 -+/* - * Copyright 2011-2019 NW Digital Radio - * - * Author: Annaliese McDermond - * - * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27. -- * - */ - - #include -@@ -16,17 +15,23 @@ - - #include "tlv320aic32x4.h" - -+static const struct regmap_config aic32x4_i2c_regmap_config = { -+ .reg_bits = 8, -+ .val_bits = 8, -+ .max_register = AIC32X4_REFPOWERUP, -+ .ranges = aic32x4_regmap_pages, -+ .num_ranges = 1, -+}; -+ - static int aic32x4_i2c_probe(struct i2c_client *i2c) - { - struct regmap *regmap; -- struct regmap_config config; - enum aic32x4_type type; - -- config = aic32x4_regmap_config; -- config.reg_bits = 8; -- config.val_bits = 8; -+ regmap = devm_regmap_init_i2c(i2c, &aic32x4_i2c_regmap_config); -+ if (IS_ERR(regmap)) -+ return PTR_ERR(regmap); - -- regmap = devm_regmap_init_i2c(i2c, &config); - type = (uintptr_t)i2c_get_match_data(i2c); - - return aic32x4_probe(&i2c->dev, regmap, type); -diff --git a/sound/soc/codecs/tlv320aic32x4-spi.c b/sound/soc/codecs/tlv320aic32x4-spi.c ---- a/sound/soc/codecs/tlv320aic32x4-spi.c -+++ b/sound/soc/codecs/tlv320aic32x4-spi.c -@@ -1,11 +1,10 @@ --/* SPDX-License-Identifier: GPL-2.0 -- * -+// SPDX-License-Identifier: GPL-2.0 -+/* - * Copyright 2011-2019 NW Digital Radio - * - * Author: Annaliese McDermond - * - * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27. -- * - */ - - #include -@@ -16,19 +15,25 @@ - - #include "tlv320aic32x4.h" - -+static const struct regmap_config aic32x4_spi_regmap_config = { -+ .reg_bits = 7, -+ .pad_bits = 1, -+ .val_bits = 8, -+ .read_flag_mask = 0x01, -+ .max_register = AIC32X4_REFPOWERUP, -+ .ranges = aic32x4_regmap_pages, -+ .num_ranges = 1, -+}; -+ - static int aic32x4_spi_probe(struct spi_device *spi) - { - struct regmap *regmap; -- struct regmap_config config; - enum aic32x4_type type; - -- config = aic32x4_regmap_config; -- config.reg_bits = 7; -- config.pad_bits = 1; -- config.val_bits = 8; -- config.read_flag_mask = 0x01; -+ regmap = devm_regmap_init_spi(spi, &aic32x4_spi_regmap_config); -+ if (IS_ERR(regmap)) -+ return PTR_ERR(regmap); - -- regmap = devm_regmap_init_spi(spi, &config); - type = (uintptr_t)spi_get_device_match_data(spi); - - return aic32x4_probe(&spi->dev, regmap, type); -diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c ---- a/sound/soc/codecs/tlv320aic32x4.c -+++ b/sound/soc/codecs/tlv320aic32x4.c -@@ -1,7 +1,5 @@ - // SPDX-License-Identifier: GPL-2.0-or-later - /* -- * linux/sound/soc/codecs/tlv320aic32x4.c -- * - * Copyright 2011 Vista Silicon S.L. - * - * Author: Javier Martin -@@ -28,7 +26,6 @@ - #include - #include - #include --#include - - #include "tlv320aic32x4.h" - -@@ -45,7 +42,8 @@ struct aic32x4_priv { - struct regulator *supply_dv; - struct regulator *supply_av; - -- struct aic32x4_setup_data *setup; -+ unsigned int gpio_func[5]; -+ - struct device *dev; - enum aic32x4_type type; - -@@ -75,7 +73,7 @@ static int aic32x4_reset_adc(struct snd_soc_dapm_widget *w, - }; - - static int mic_bias_event(struct snd_soc_dapm_widget *w, -- struct snd_kcontrol *kcontrol, int event) -+ struct snd_kcontrol *kcontrol, int event) - { - struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); - -@@ -83,25 +81,23 @@ static int mic_bias_event(struct snd_soc_dapm_widget *w, - case SND_SOC_DAPM_POST_PMU: - /* Change Mic Bias Registor */ - snd_soc_component_update_bits(component, AIC32X4_MICBIAS, -- AIC32x4_MICBIAS_MASK, -- AIC32X4_MICBIAS_LDOIN | -- AIC32X4_MICBIAS_2075V); -- printk(KERN_DEBUG "%s: Mic Bias will be turned ON\n", __func__); -+ AIC32x4_MICBIAS_MASK, -+ AIC32X4_MICBIAS_LDOIN | -+ AIC32X4_MICBIAS_2075V); -+ dev_dbg(component->dev, "Mic Bias will be turned ON\n"); - break; - case SND_SOC_DAPM_PRE_PMD: - snd_soc_component_update_bits(component, AIC32X4_MICBIAS, -- AIC32x4_MICBIAS_MASK, 0); -- printk(KERN_DEBUG "%s: Mic Bias will be turned OFF\n", -- __func__); -+ AIC32x4_MICBIAS_MASK, 0); -+ dev_dbg(component->dev, "Mic Bias will be turned OFF\n"); - break; - } - - return 0; - } - -- - static int aic32x4_get_mfp1_gpio(struct snd_kcontrol *kcontrol, -- struct snd_ctl_elem_value *ucontrol) -+ struct snd_ctl_elem_value *ucontrol) - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - u8 val; -@@ -114,7 +110,7 @@ static int aic32x4_get_mfp1_gpio(struct snd_kcontrol *kcontrol, - }; - - static int aic32x4_set_mfp2_gpio(struct snd_kcontrol *kcontrol, -- struct snd_ctl_elem_value *ucontrol) -+ struct snd_ctl_elem_value *ucontrol) - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - u8 val; -@@ -123,8 +119,7 @@ static int aic32x4_set_mfp2_gpio(struct snd_kcontrol *kcontrol, - val = snd_soc_component_read(component, AIC32X4_DOUTCTL); - gpio_check = (val & AIC32X4_MFP_GPIO_ENABLED); - if (gpio_check != AIC32X4_MFP_GPIO_ENABLED) { -- printk(KERN_ERR "%s: MFP2 is not configure as a GPIO output\n", -- __func__); -+ dev_err(component->dev, "MFP2 is not configure as a GPIO output\n"); - return -EINVAL; - } - -@@ -142,7 +137,7 @@ static int aic32x4_set_mfp2_gpio(struct snd_kcontrol *kcontrol, - }; - - static int aic32x4_get_mfp3_gpio(struct snd_kcontrol *kcontrol, -- struct snd_ctl_elem_value *ucontrol) -+ struct snd_ctl_elem_value *ucontrol) - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - u8 val; -@@ -155,7 +150,7 @@ static int aic32x4_get_mfp3_gpio(struct snd_kcontrol *kcontrol, - }; - - static int aic32x4_set_mfp4_gpio(struct snd_kcontrol *kcontrol, -- struct snd_ctl_elem_value *ucontrol) -+ struct snd_ctl_elem_value *ucontrol) - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - u8 val; -@@ -164,8 +159,7 @@ static int aic32x4_set_mfp4_gpio(struct snd_kcontrol *kcontrol, - val = snd_soc_component_read(component, AIC32X4_MISOCTL); - gpio_check = (val & AIC32X4_MFP_GPIO_ENABLED); - if (gpio_check != AIC32X4_MFP_GPIO_ENABLED) { -- printk(KERN_ERR "%s: MFP4 is not configure as a GPIO output\n", -- __func__); -+ dev_err(component->dev, "MFP4 is not configure as a GPIO output\n"); - return -EINVAL; - } - -@@ -183,7 +177,7 @@ static int aic32x4_set_mfp4_gpio(struct snd_kcontrol *kcontrol, - }; - - static int aic32x4_get_mfp5_gpio(struct snd_kcontrol *kcontrol, -- struct snd_ctl_elem_value *ucontrol) -+ struct snd_ctl_elem_value *ucontrol) - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - u8 val; -@@ -195,7 +189,7 @@ static int aic32x4_get_mfp5_gpio(struct snd_kcontrol *kcontrol, - }; - - static int aic32x4_set_mfp5_gpio(struct snd_kcontrol *kcontrol, -- struct snd_ctl_elem_value *ucontrol) -+ struct snd_ctl_elem_value *ucontrol) - { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - u8 val; -@@ -204,8 +198,7 @@ static int aic32x4_set_mfp5_gpio(struct snd_kcontrol *kcontrol, - val = snd_soc_component_read(component, AIC32X4_GPIOCTL); - gpio_check = (val & AIC32X4_MFP5_GPIO_OUTPUT); - if (gpio_check != AIC32X4_MFP5_GPIO_OUTPUT) { -- printk(KERN_ERR "%s: MFP5 is not configure as a GPIO output\n", -- __func__); -+ dev_err(component->dev, "MFP5 is not configure as a GPIO output\n"); - return -EINVAL; - } - -@@ -222,25 +215,31 @@ static int aic32x4_set_mfp5_gpio(struct snd_kcontrol *kcontrol, - return 0; - }; - --static const struct snd_kcontrol_new aic32x4_mfp1[] = { -- SOC_SINGLE_BOOL_EXT("MFP1 GPIO", 0, aic32x4_get_mfp1_gpio, NULL), --}; -- --static const struct snd_kcontrol_new aic32x4_mfp2[] = { -- SOC_SINGLE_BOOL_EXT("MFP2 GPIO", 0, NULL, aic32x4_set_mfp2_gpio), --}; -- --static const struct snd_kcontrol_new aic32x4_mfp3[] = { -- SOC_SINGLE_BOOL_EXT("MFP3 GPIO", 0, aic32x4_get_mfp3_gpio, NULL), --}; -- --static const struct snd_kcontrol_new aic32x4_mfp4[] = { -- SOC_SINGLE_BOOL_EXT("MFP4 GPIO", 0, NULL, aic32x4_set_mfp4_gpio), --}; -- --static const struct snd_kcontrol_new aic32x4_mfp5[] = { -- SOC_SINGLE_BOOL_EXT("MFP5 GPIO", 0, aic32x4_get_mfp5_gpio, -- aic32x4_set_mfp5_gpio), -+static const struct { -+ unsigned int reg; -+ struct snd_kcontrol_new ctrl; -+} aic32x4_mfp_cfg[] = { -+ { -+ .reg = AIC32X4_DINCTL, -+ .ctrl = SOC_SINGLE_BOOL_EXT("MFP1 GPIO", 0, aic32x4_get_mfp1_gpio, NULL), -+ }, -+ { -+ .reg = AIC32X4_DOUTCTL, -+ .ctrl = SOC_SINGLE_BOOL_EXT("MFP2 GPIO", 0, NULL, aic32x4_set_mfp2_gpio), -+ }, -+ { -+ .reg = AIC32X4_SCLKCTL, -+ .ctrl = SOC_SINGLE_BOOL_EXT("MFP3 GPIO", 0, aic32x4_get_mfp3_gpio, NULL), -+ }, -+ { -+ .reg = AIC32X4_MISOCTL, -+ .ctrl = SOC_SINGLE_BOOL_EXT("MFP4 GPIO", 0, NULL, aic32x4_set_mfp4_gpio), -+ }, -+ { -+ .reg = AIC32X4_GPIOCTL, -+ .ctrl = SOC_SINGLE_BOOL_EXT("MFP5 GPIO", 0, aic32x4_get_mfp5_gpio, -+ aic32x4_set_mfp5_gpio), -+ }, - }; - - /* 0dB min, 0.5dB steps */ -@@ -270,53 +269,42 @@ static SOC_ENUM_SINGLE_DECL(r_ptm_enum, AIC32X4_RPLAYBACK, 2, ptm_text); - - static const struct snd_kcontrol_new aic32x4_snd_controls[] = { - SOC_DOUBLE_R_S_TLV("PCM Playback Volume", AIC32X4_LDACVOL, -- AIC32X4_RDACVOL, 0, -0x7f, 0x30, 7, 0, tlv_pcm), -+ AIC32X4_RDACVOL, 0, -0x7f, 0x30, 7, 0, tlv_pcm), - SOC_ENUM("DAC Left Playback PowerTune Switch", l_ptm_enum), - SOC_ENUM("DAC Right Playback PowerTune Switch", r_ptm_enum), - SOC_DOUBLE_R_S_TLV("HP Driver Gain Volume", AIC32X4_HPLGAIN, -- AIC32X4_HPRGAIN, 0, -0x6, 0x1d, 5, 0, -- tlv_driver_gain), -+ AIC32X4_HPRGAIN, 0, -0x6, 0x1d, 5, 0, tlv_driver_gain), - SOC_DOUBLE_R_S_TLV("LO Driver Gain Volume", AIC32X4_LOLGAIN, -- AIC32X4_LORGAIN, 0, -0x6, 0x1d, 5, 0, -- tlv_driver_gain), -+ AIC32X4_LORGAIN, 0, -0x6, 0x1d, 5, 0, tlv_driver_gain), - SOC_DOUBLE_R("HP DAC Playback Switch", AIC32X4_HPLGAIN, -- AIC32X4_HPRGAIN, 6, 0x01, 1), -+ AIC32X4_HPRGAIN, 6, 0x01, 1), - SOC_DOUBLE_R("LO DAC Playback Switch", AIC32X4_LOLGAIN, -- AIC32X4_LORGAIN, 6, 0x01, 1), -+ AIC32X4_LORGAIN, 6, 0x01, 1), - SOC_ENUM("LO Playback Common Mode Switch", lo_cm_enum), - SOC_DOUBLE_R("Mic PGA Switch", AIC32X4_LMICPGAVOL, -- AIC32X4_RMICPGAVOL, 7, 0x01, 1), -+ AIC32X4_RMICPGAVOL, 7, 0x01, 1), - - SOC_SINGLE("ADCFGA Left Mute Switch", AIC32X4_ADCFGA, 7, 1, 0), - SOC_SINGLE("ADCFGA Right Mute Switch", AIC32X4_ADCFGA, 3, 1, 0), - - SOC_DOUBLE_R_S_TLV("ADC Level Volume", AIC32X4_LADCVOL, -- AIC32X4_RADCVOL, 0, -0x18, 0x28, 6, 0, tlv_adc_vol), -+ AIC32X4_RADCVOL, 0, -0x18, 0x28, 6, 0, tlv_adc_vol), - SOC_DOUBLE_R_TLV("PGA Level Volume", AIC32X4_LMICPGAVOL, -- AIC32X4_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5), -+ AIC32X4_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5), - - SOC_SINGLE("Auto-mute Switch", AIC32X4_DACMUTE, 4, 7, 0), - - SOC_SINGLE("AGC Left Switch", AIC32X4_LAGC1, 7, 1, 0), - SOC_SINGLE("AGC Right Switch", AIC32X4_RAGC1, 7, 1, 0), -- SOC_DOUBLE_R("AGC Target Level", AIC32X4_LAGC1, AIC32X4_RAGC1, -- 4, 0x07, 0), -- SOC_DOUBLE_R("AGC Gain Hysteresis", AIC32X4_LAGC1, AIC32X4_RAGC1, -- 0, 0x03, 0), -- SOC_DOUBLE_R("AGC Hysteresis", AIC32X4_LAGC2, AIC32X4_RAGC2, -- 6, 0x03, 0), -- SOC_DOUBLE_R("AGC Noise Threshold", AIC32X4_LAGC2, AIC32X4_RAGC2, -- 1, 0x1F, 0), -- SOC_DOUBLE_R("AGC Max PGA", AIC32X4_LAGC3, AIC32X4_RAGC3, -- 0, 0x7F, 0), -- SOC_DOUBLE_R("AGC Attack Time", AIC32X4_LAGC4, AIC32X4_RAGC4, -- 3, 0x1F, 0), -- SOC_DOUBLE_R("AGC Decay Time", AIC32X4_LAGC5, AIC32X4_RAGC5, -- 3, 0x1F, 0), -- SOC_DOUBLE_R("AGC Noise Debounce", AIC32X4_LAGC6, AIC32X4_RAGC6, -- 0, 0x1F, 0), -- SOC_DOUBLE_R("AGC Signal Debounce", AIC32X4_LAGC7, AIC32X4_RAGC7, -- 0, 0x0F, 0), -+ SOC_DOUBLE_R("AGC Target Level", AIC32X4_LAGC1, AIC32X4_RAGC1, 4, 0x07, 0), -+ SOC_DOUBLE_R("AGC Gain Hysteresis", AIC32X4_LAGC1, AIC32X4_RAGC1, 0, 0x03, 0), -+ SOC_DOUBLE_R("AGC Hysteresis", AIC32X4_LAGC2, AIC32X4_RAGC2, 6, 0x03, 0), -+ SOC_DOUBLE_R("AGC Noise Threshold", AIC32X4_LAGC2, AIC32X4_RAGC2, 1, 0x1F, 0), -+ SOC_DOUBLE_R("AGC Max PGA", AIC32X4_LAGC3, AIC32X4_RAGC3, 0, 0x7F, 0), -+ SOC_DOUBLE_R("AGC Attack Time", AIC32X4_LAGC4, AIC32X4_RAGC4, 3, 0x1F, 0), -+ SOC_DOUBLE_R("AGC Decay Time", AIC32X4_LAGC5, AIC32X4_RAGC5, 3, 0x1F, 0), -+ SOC_DOUBLE_R("AGC Noise Debounce", AIC32X4_LAGC6, AIC32X4_RAGC6, 0, 0x1F, 0), -+ SOC_DOUBLE_R("AGC Signal Debounce", AIC32X4_LAGC7, AIC32X4_RAGC7, 0, 0x0F, 0), - }; - - static const struct snd_kcontrol_new hpl_output_mixer_controls[] = { -@@ -354,21 +342,27 @@ static SOC_ENUM_SINGLE_DECL(in3r_lpga_n_enum, AIC32X4_LMICPGANIN, 2, resistor_te - static const struct snd_kcontrol_new in1l_to_lmixer_controls[] = { - SOC_DAPM_ENUM("IN1_L L+ Switch", in1l_lpga_p_enum), - }; -+ - static const struct snd_kcontrol_new in2l_to_lmixer_controls[] = { - SOC_DAPM_ENUM("IN2_L L+ Switch", in2l_lpga_p_enum), - }; -+ - static const struct snd_kcontrol_new in3l_to_lmixer_controls[] = { - SOC_DAPM_ENUM("IN3_L L+ Switch", in3l_lpga_p_enum), - }; -+ - static const struct snd_kcontrol_new in1r_to_lmixer_controls[] = { - SOC_DAPM_ENUM("IN1_R L+ Switch", in1r_lpga_p_enum), - }; -+ - static const struct snd_kcontrol_new cml_to_lmixer_controls[] = { - SOC_DAPM_ENUM("CM_L L- Switch", cml_lpga_n_enum), - }; -+ - static const struct snd_kcontrol_new in2r_to_lmixer_controls[] = { - SOC_DAPM_ENUM("IN2_R L- Switch", in2r_lpga_n_enum), - }; -+ - static const struct snd_kcontrol_new in3r_to_lmixer_controls[] = { - SOC_DAPM_ENUM("IN3_R L- Switch", in3r_lpga_n_enum), - }; -@@ -385,21 +379,27 @@ static SOC_ENUM_SINGLE_DECL(in3l_rpga_n_enum, AIC32X4_RMICPGANIN, 2, resistor_te - static const struct snd_kcontrol_new in1r_to_rmixer_controls[] = { - SOC_DAPM_ENUM("IN1_R R+ Switch", in1r_rpga_p_enum), - }; -+ - static const struct snd_kcontrol_new in2r_to_rmixer_controls[] = { - SOC_DAPM_ENUM("IN2_R R+ Switch", in2r_rpga_p_enum), - }; -+ - static const struct snd_kcontrol_new in3r_to_rmixer_controls[] = { - SOC_DAPM_ENUM("IN3_R R+ Switch", in3r_rpga_p_enum), - }; -+ - static const struct snd_kcontrol_new in2l_to_rmixer_controls[] = { - SOC_DAPM_ENUM("IN2_L R+ Switch", in2l_rpga_p_enum), - }; -+ - static const struct snd_kcontrol_new cmr_to_rmixer_controls[] = { - SOC_DAPM_ENUM("CM_R R- Switch", cmr_rpga_n_enum), - }; -+ - static const struct snd_kcontrol_new in1l_to_rmixer_controls[] = { - SOC_DAPM_ENUM("IN1_L R- Switch", in1l_rpga_n_enum), - }; -+ - static const struct snd_kcontrol_new in3l_to_rmixer_controls[] = { - SOC_DAPM_ENUM("IN3_L R- Switch", in3l_rpga_n_enum), - }; -@@ -428,38 +428,38 @@ static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = { - - SND_SOC_DAPM_ADC("Right ADC", "Right Capture", AIC32X4_ADCSETUP, 6, 0), - SND_SOC_DAPM_MUX("IN1_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, -- in1r_to_rmixer_controls), -+ in1r_to_rmixer_controls), - SND_SOC_DAPM_MUX("IN2_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, -- in2r_to_rmixer_controls), -+ in2r_to_rmixer_controls), - SND_SOC_DAPM_MUX("IN3_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, -- in3r_to_rmixer_controls), -+ in3r_to_rmixer_controls), - SND_SOC_DAPM_MUX("IN2_L to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, -- in2l_to_rmixer_controls), -+ in2l_to_rmixer_controls), - SND_SOC_DAPM_MUX("CM_R to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, -- cmr_to_rmixer_controls), -+ cmr_to_rmixer_controls), - SND_SOC_DAPM_MUX("IN1_L to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, -- in1l_to_rmixer_controls), -+ in1l_to_rmixer_controls), - SND_SOC_DAPM_MUX("IN3_L to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, -- in3l_to_rmixer_controls), -+ in3l_to_rmixer_controls), - - SND_SOC_DAPM_ADC("Left ADC", "Left Capture", AIC32X4_ADCSETUP, 7, 0), - SND_SOC_DAPM_MUX("IN1_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, -- in1l_to_lmixer_controls), -+ in1l_to_lmixer_controls), - SND_SOC_DAPM_MUX("IN2_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, -- in2l_to_lmixer_controls), -+ in2l_to_lmixer_controls), - SND_SOC_DAPM_MUX("IN3_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, -- in3l_to_lmixer_controls), -+ in3l_to_lmixer_controls), - SND_SOC_DAPM_MUX("IN1_R to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, -- in1r_to_lmixer_controls), -+ in1r_to_lmixer_controls), - SND_SOC_DAPM_MUX("CM_L to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, -- cml_to_lmixer_controls), -+ cml_to_lmixer_controls), - SND_SOC_DAPM_MUX("IN2_R to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, -- in2r_to_lmixer_controls), -+ in2r_to_lmixer_controls), - SND_SOC_DAPM_MUX("IN3_R to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, -- in3r_to_lmixer_controls), -+ in3r_to_lmixer_controls), - - SND_SOC_DAPM_SUPPLY("Mic Bias", AIC32X4_MICBIAS, 6, 0, mic_bias_event, -- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), -+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - - SND_SOC_DAPM_POST("ADC Reset", aic32x4_reset_adc), - -@@ -575,7 +575,7 @@ static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = { - {"IN3_R to Left Mixer Negative Resistor", "40 kOhm", "IN3_R"}, - }; - --static const struct regmap_range_cfg aic32x4_regmap_pages[] = { -+const struct regmap_range_cfg aic32x4_regmap_pages[] = { - { - .selector_reg = 0, - .selector_mask = 0xff, -@@ -585,13 +585,7 @@ static const struct regmap_range_cfg aic32x4_regmap_pages[] = { - .range_max = AIC32X4_REFPOWERUP, - }, - }; -- --const struct regmap_config aic32x4_regmap_config = { -- .max_register = AIC32X4_REFPOWERUP, -- .ranges = aic32x4_regmap_pages, -- .num_ranges = ARRAY_SIZE(aic32x4_regmap_pages), --}; --EXPORT_SYMBOL(aic32x4_regmap_config); -+EXPORT_SYMBOL_GPL(aic32x4_regmap_pages); - - static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai, - int clk_id, unsigned int freq, int dir) -@@ -624,7 +618,7 @@ static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) - case SND_SOC_DAIFMT_CBC_CFC: - break; - default: -- printk(KERN_ERR "aic32x4: invalid clock provider\n"); -+ dev_err(component->dev, "invalid clock provider\n"); - return -EINVAL; - } - -@@ -651,19 +645,20 @@ static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) - AIC32X4_IFACE1_DATATYPE_SHIFT); - break; - default: -- printk(KERN_ERR "aic32x4: invalid DAI interface format\n"); -+ dev_err(component->dev, "invalid DAI interface format\n"); - return -EINVAL; - } - - aic32x4->fmt = fmt; - - snd_soc_component_update_bits(component, AIC32X4_IFACE1, -- AIC32X4_IFACE1_DATATYPE_MASK | -- AIC32X4_IFACE1_MASTER_MASK, iface_reg_1); -+ AIC32X4_IFACE1_DATATYPE_MASK | -+ AIC32X4_IFACE1_MASTER_MASK, -+ iface_reg_1); - snd_soc_component_update_bits(component, AIC32X4_IFACE2, -- AIC32X4_DATA_OFFSET_MASK, iface_reg_2); -+ AIC32X4_DATA_OFFSET_MASK, iface_reg_2); - snd_soc_component_update_bits(component, AIC32X4_IFACE3, -- AIC32X4_BCLKINV_MASK, iface_reg_3); -+ AIC32X4_BCLKINV_MASK, iface_reg_3); - - return 0; - } -@@ -676,14 +671,13 @@ static int aic32x4_set_aosr(struct snd_soc_component *component, u8 aosr) - static int aic32x4_set_dosr(struct snd_soc_component *component, u16 dosr) - { - snd_soc_component_write(component, AIC32X4_DOSRMSB, dosr >> 8); -- snd_soc_component_write(component, AIC32X4_DOSRLSB, -- (dosr & 0xff)); -+ snd_soc_component_write(component, AIC32X4_DOSRLSB, dosr & 0xff); - - return 0; - } - - static int aic32x4_set_processing_blocks(struct snd_soc_component *component, -- u8 r_block, u8 p_block) -+ u8 r_block, u8 p_block) - { - struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); - -@@ -703,6 +697,45 @@ static int aic32x4_set_processing_blocks(struct snd_soc_component *component, - return 0; - } - -+static int aic32x4_configure_rate(struct snd_soc_component *component, -+ unsigned int rate, u8 *aosr, u8 *adc_rc, -+ u8 *dac_rc, u8 *dosr_inc) -+{ -+ struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); -+ u8 prb_rx, prb_tx; -+ -+ if (rate <= 48000) { -+ *aosr = 128; -+ *adc_rc = 6; -+ *dac_rc = 8; -+ *dosr_inc = 8; -+ prb_rx = 1; -+ prb_tx = 1; -+ } else if (rate <= 96000) { -+ *aosr = 64; -+ *adc_rc = 6; -+ *dac_rc = 8; -+ *dosr_inc = 4; -+ prb_rx = 1; -+ prb_tx = (aic32x4->type == AIC32X4_TYPE_TAS2505) ? 1 : 9; -+ } else if (rate == 192000) { -+ *aosr = 32; -+ *adc_rc = 3; -+ *dac_rc = 4; -+ *dosr_inc = 2; -+ prb_rx = 13; -+ prb_tx = (aic32x4->type == AIC32X4_TYPE_TAS2505) ? 1 : 19; -+ } else { -+ dev_err(component->dev, "Sampling rate %u not supported\n", rate); -+ return -EINVAL; -+ } -+ -+ if (aic32x4->type == AIC32X4_TYPE_TAS2505) -+ prb_rx = 0; -+ -+ return aic32x4_set_processing_blocks(component, prb_rx, prb_tx); -+} -+ - static int aic32x4_setup_clocks(struct snd_soc_component *component, - unsigned int sample_rate, unsigned int channels, - unsigned int bit_depth) -@@ -717,7 +750,7 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, - unsigned long adc_clock_rate, dac_clock_rate; - int ret; - -- static struct clk_bulk_data clocks[] = { -+ struct clk_bulk_data clocks[] = { - { .id = "pll" }, - { .id = "nadc" }, - { .id = "madc" }, -@@ -729,37 +762,11 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, - if (ret) - return ret; - -- if (sample_rate <= 48000) { -- aosr = 128; -- adc_resource_class = 6; -- dac_resource_class = 8; -- dosr_increment = 8; -- if (aic32x4->type == AIC32X4_TYPE_TAS2505) -- aic32x4_set_processing_blocks(component, 0, 1); -- else -- aic32x4_set_processing_blocks(component, 1, 1); -- } else if (sample_rate <= 96000) { -- aosr = 64; -- adc_resource_class = 6; -- dac_resource_class = 8; -- dosr_increment = 4; -- if (aic32x4->type == AIC32X4_TYPE_TAS2505) -- aic32x4_set_processing_blocks(component, 0, 1); -- else -- aic32x4_set_processing_blocks(component, 1, 9); -- } else if (sample_rate == 192000) { -- aosr = 32; -- adc_resource_class = 3; -- dac_resource_class = 4; -- dosr_increment = 2; -- if (aic32x4->type == AIC32X4_TYPE_TAS2505) -- aic32x4_set_processing_blocks(component, 0, 1); -- else -- aic32x4_set_processing_blocks(component, 13, 19); -- } else { -- dev_err(component->dev, "Sampling rate not supported\n"); -- return -EINVAL; -- } -+ ret = aic32x4_configure_rate(component, sample_rate, &aosr, -+ &adc_resource_class, &dac_resource_class, -+ &dosr_increment); -+ if (ret) -+ return ret; - - /* PCM over I2S is always 2-channel */ - if ((aic32x4->fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S) -@@ -781,50 +788,39 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, - (min_mdac * dosr * sample_rate); - for (mdac = min_mdac; mdac <= 128; ++mdac) { - for (ndac = max_ndac; ndac > 0; --ndac) { -- dac_clock_rate = ndac * mdac * dosr * -- sample_rate; -- if (dac_clock_rate == adc_clock_rate) { -- if (clk_round_rate(clocks[0].clk, dac_clock_rate) == 0) -- continue; -+ dac_clock_rate = ndac * mdac * dosr * sample_rate; -+ if (dac_clock_rate != adc_clock_rate) -+ continue; - -- clk_set_rate(clocks[0].clk, -- dac_clock_rate); -+ if (clk_round_rate(clocks[0].clk, dac_clock_rate) == 0) -+ continue; - -- clk_set_rate(clocks[1].clk, -- sample_rate * aosr * -- madc); -- clk_set_rate(clocks[2].clk, -- sample_rate * aosr); -- aic32x4_set_aosr(component, -- aosr); -+ clk_set_rate(clocks[0].clk, dac_clock_rate); - -- clk_set_rate(clocks[3].clk, -- sample_rate * dosr * -- mdac); -- clk_set_rate(clocks[4].clk, -- sample_rate * dosr); -- aic32x4_set_dosr(component, -- dosr); -+ clk_set_rate(clocks[1].clk, sample_rate * aosr * madc); -+ clk_set_rate(clocks[2].clk, sample_rate * aosr); -+ aic32x4_set_aosr(component, aosr); - -- clk_set_rate(clocks[5].clk, -- sample_rate * channels * -- bit_depth); -+ clk_set_rate(clocks[3].clk, sample_rate * dosr * mdac); -+ clk_set_rate(clocks[4].clk, sample_rate * dosr); -+ aic32x4_set_dosr(component, dosr); - -- return 0; -- } -+ clk_set_rate(clocks[5].clk, -+ sample_rate * channels * bit_depth); -+ -+ return 0; - } - } - } - } - -- dev_err(component->dev, -- "Could not set clocks to support sample rate.\n"); -+ dev_err(component->dev, "Could not set clocks to support sample rate.\n"); - return -EINVAL; - } - - static int aic32x4_hw_params(struct snd_pcm_substream *substream, -- struct snd_pcm_hw_params *params, -- struct snd_soc_dai *dai) -+ struct snd_pcm_hw_params *params, -+ struct snd_soc_dai *dai) - { - struct snd_soc_component *component = dai->component; - struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); -@@ -837,24 +833,20 @@ static int aic32x4_hw_params(struct snd_pcm_substream *substream, - - switch (params_physical_width(params)) { - case 16: -- iface1_reg |= (AIC32X4_WORD_LEN_16BITS << -- AIC32X4_IFACE1_DATALEN_SHIFT); -+ iface1_reg |= (AIC32X4_WORD_LEN_16BITS << AIC32X4_IFACE1_DATALEN_SHIFT); - break; - case 20: -- iface1_reg |= (AIC32X4_WORD_LEN_20BITS << -- AIC32X4_IFACE1_DATALEN_SHIFT); -+ iface1_reg |= (AIC32X4_WORD_LEN_20BITS << AIC32X4_IFACE1_DATALEN_SHIFT); - break; - case 24: -- iface1_reg |= (AIC32X4_WORD_LEN_24BITS << -- AIC32X4_IFACE1_DATALEN_SHIFT); -+ iface1_reg |= (AIC32X4_WORD_LEN_24BITS << AIC32X4_IFACE1_DATALEN_SHIFT); - break; - case 32: -- iface1_reg |= (AIC32X4_WORD_LEN_32BITS << -- AIC32X4_IFACE1_DATALEN_SHIFT); -+ iface1_reg |= (AIC32X4_WORD_LEN_32BITS << AIC32X4_IFACE1_DATALEN_SHIFT); - break; - } - snd_soc_component_update_bits(component, AIC32X4_IFACE1, -- AIC32X4_IFACE1_DATALEN_MASK, iface1_reg); -+ AIC32X4_IFACE1_DATALEN_MASK, iface1_reg); - - if (params_channels(params) == 1) { - dacsetup_reg = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2LCHN; -@@ -865,7 +857,7 @@ static int aic32x4_hw_params(struct snd_pcm_substream *substream, - dacsetup_reg = AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN; - } - snd_soc_component_update_bits(component, AIC32X4_DACSETUP, -- AIC32X4_DAC_CHAN_MASK, dacsetup_reg); -+ AIC32X4_DAC_CHAN_MASK, dacsetup_reg); - - return 0; - } -@@ -875,7 +867,7 @@ static int aic32x4_mute(struct snd_soc_dai *dai, int mute, int direction) - struct snd_soc_component *component = dai->component; - - snd_soc_component_update_bits(component, AIC32X4_DACMUTE, -- AIC32X4_MUTEON, mute ? AIC32X4_MUTEON : 0); -+ AIC32X4_MUTEON, mute ? AIC32X4_MUTEON : 0); - - return 0; - } -@@ -886,7 +878,7 @@ static int aic32x4_set_bias_level(struct snd_soc_component *component, - struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); - int ret; - -- static struct clk_bulk_data clocks[] = { -+ struct clk_bulk_data clocks[] = { - { .id = "madc" }, - { .id = "mdac" }, - { .id = "bdiv" }, -@@ -955,44 +947,14 @@ static void aic32x4_setup_gpios(struct snd_soc_component *component) - struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); - - /* setup GPIO functions */ -- /* MFP1 */ -- if (aic32x4->setup->gpio_func[0] != AIC32X4_MFPX_DEFAULT_VALUE) { -- snd_soc_component_write(component, AIC32X4_DINCTL, -- aic32x4->setup->gpio_func[0]); -- snd_soc_add_component_controls(component, aic32x4_mfp1, -- ARRAY_SIZE(aic32x4_mfp1)); -- } -+ BUILD_BUG_ON(ARRAY_SIZE(aic32x4->gpio_func) != ARRAY_SIZE(aic32x4_mfp_cfg)); -+ for (int i = 0; i < ARRAY_SIZE(aic32x4->gpio_func); i++) { -+ if (aic32x4->gpio_func[i] == AIC32X4_MFPX_DEFAULT_VALUE) -+ continue; - -- /* MFP2 */ -- if (aic32x4->setup->gpio_func[1] != AIC32X4_MFPX_DEFAULT_VALUE) { -- snd_soc_component_write(component, AIC32X4_DOUTCTL, -- aic32x4->setup->gpio_func[1]); -- snd_soc_add_component_controls(component, aic32x4_mfp2, -- ARRAY_SIZE(aic32x4_mfp2)); -- } -- -- /* MFP3 */ -- if (aic32x4->setup->gpio_func[2] != AIC32X4_MFPX_DEFAULT_VALUE) { -- snd_soc_component_write(component, AIC32X4_SCLKCTL, -- aic32x4->setup->gpio_func[2]); -- snd_soc_add_component_controls(component, aic32x4_mfp3, -- ARRAY_SIZE(aic32x4_mfp3)); -- } -- -- /* MFP4 */ -- if (aic32x4->setup->gpio_func[3] != AIC32X4_MFPX_DEFAULT_VALUE) { -- snd_soc_component_write(component, AIC32X4_MISOCTL, -- aic32x4->setup->gpio_func[3]); -- snd_soc_add_component_controls(component, aic32x4_mfp4, -- ARRAY_SIZE(aic32x4_mfp4)); -- } -- -- /* MFP5 */ -- if (aic32x4->setup->gpio_func[4] != AIC32X4_MFPX_DEFAULT_VALUE) { -- snd_soc_component_write(component, AIC32X4_GPIOCTL, -- aic32x4->setup->gpio_func[4]); -- snd_soc_add_component_controls(component, aic32x4_mfp5, -- ARRAY_SIZE(aic32x4_mfp5)); -+ snd_soc_component_write(component, aic32x4_mfp_cfg[i].reg, -+ aic32x4->gpio_func[i]); -+ snd_soc_add_component_controls(component, &aic32x4_mfp_cfg[i].ctrl, 1); - } - } - -@@ -1002,7 +964,7 @@ static int aic32x4_component_probe(struct snd_soc_component *component) - u32 tmp_reg; - int ret; - -- static struct clk_bulk_data clocks[] = { -+ struct clk_bulk_data clocks[] = { - { .id = "codec_clkin" }, - { .id = "pll" }, - { .id = "bdiv" }, -@@ -1013,8 +975,7 @@ static int aic32x4_component_probe(struct snd_soc_component *component) - if (ret) - return ret; - -- if (aic32x4->setup) -- aic32x4_setup_gpios(component); -+ aic32x4_setup_gpios(component); - - clk_set_parent(clocks[0].clk, clocks[1].clk); - clk_set_parent(clocks[2].clk, clocks[3].clk); -@@ -1022,7 +983,7 @@ static int aic32x4_component_probe(struct snd_soc_component *component) - /* Power platform configuration */ - if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) { - snd_soc_component_write(component, AIC32X4_MICBIAS, -- AIC32X4_MICBIAS_LDOIN | AIC32X4_MICBIAS_2075V); -+ AIC32X4_MICBIAS_LDOIN | AIC32X4_MICBIAS_2075V); - } - if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE) - snd_soc_component_write(component, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE); -@@ -1041,16 +1002,16 @@ static int aic32x4_component_probe(struct snd_soc_component *component) - /* Mic PGA routing */ - if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K) - snd_soc_component_write(component, AIC32X4_LMICPGANIN, -- AIC32X4_LMICPGANIN_IN2R_10K); -+ AIC32X4_LMICPGANIN_IN2R_10K); - else - snd_soc_component_write(component, AIC32X4_LMICPGANIN, -- AIC32X4_LMICPGANIN_CM1L_10K); -+ AIC32X4_LMICPGANIN_CM1L_10K); - if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K) - snd_soc_component_write(component, AIC32X4_RMICPGANIN, -- AIC32X4_RMICPGANIN_IN1L_10K); -+ AIC32X4_RMICPGANIN_IN1L_10K); - else - snd_soc_component_write(component, AIC32X4_RMICPGANIN, -- AIC32X4_RMICPGANIN_CM1R_10K); -+ AIC32X4_RMICPGANIN_CM1R_10K); - - /* - * Workaround: for an unknown reason, the ADC needs to be powered up -@@ -1058,8 +1019,8 @@ static int aic32x4_component_probe(struct snd_soc_component *component) - * a HW BUG or some kind of behavior not documented in the datasheet. - */ - tmp_reg = snd_soc_component_read(component, AIC32X4_ADCSETUP); -- snd_soc_component_write(component, AIC32X4_ADCSETUP, tmp_reg | -- AIC32X4_LADC_EN | AIC32X4_RADC_EN); -+ snd_soc_component_write(component, AIC32X4_ADCSETUP, -+ tmp_reg | AIC32X4_LADC_EN | AIC32X4_RADC_EN); - snd_soc_component_write(component, AIC32X4_ADCSETUP, tmp_reg); - - /* -@@ -1102,13 +1063,13 @@ static const struct snd_kcontrol_new aic32x4_tas2505_snd_controls[] = { - SOC_ENUM("DAC Playback PowerTune Switch", l_ptm_enum), - - SOC_SINGLE_TLV("HP Driver Gain Volume", -- AIC32X4_HPLGAIN, 0, 0x74, 1, tlv_tas_driver_gain), -+ AIC32X4_HPLGAIN, 0, 0x74, 1, tlv_tas_driver_gain), - SOC_SINGLE("HP DAC Playback Switch", AIC32X4_HPLGAIN, 6, 1, 1), - - SOC_SINGLE_TLV("Speaker Driver Playback Volume", -- TAS2505_SPKVOL1, 0, 0x74, 1, tlv_tas_driver_gain), -+ TAS2505_SPKVOL1, 0, 0x74, 1, tlv_tas_driver_gain), - SOC_SINGLE_TLV("Speaker Amplifier Playback Volume", -- TAS2505_SPKVOL2, 4, 5, 0, tlv_amp_vol), -+ TAS2505_SPKVOL2, 4, 5, 0, tlv_amp_vol), - - SOC_SINGLE("Auto-mute Switch", AIC32X4_DACMUTE, 4, 7, 0), - }; -@@ -1144,11 +1105,12 @@ static const struct snd_soc_dapm_route aic32x4_tas2505_dapm_routes[] = { - static struct snd_soc_dai_driver aic32x4_tas2505_dai = { - .name = "tas2505-hifi", - .playback = { -- .stream_name = "Playback", -- .channels_min = 1, -- .channels_max = 2, -- .rates = SNDRV_PCM_RATE_8000_96000, -- .formats = AIC32X4_FORMATS,}, -+ .stream_name = "Playback", -+ .channels_min = 1, -+ .channels_max = 2, -+ .rates = SNDRV_PCM_RATE_8000_96000, -+ .formats = AIC32X4_FORMATS, -+ }, - .ops = &aic32x4_ops, - .symmetric_rate = 1, - }; -@@ -1159,7 +1121,7 @@ static int aic32x4_tas2505_component_probe(struct snd_soc_component *component) - u32 tmp_reg; - int ret; - -- static struct clk_bulk_data clocks[] = { -+ struct clk_bulk_data clocks[] = { - { .id = "codec_clkin" }, - { .id = "pll" }, - { .id = "bdiv" }, -@@ -1170,8 +1132,7 @@ static int aic32x4_tas2505_component_probe(struct snd_soc_component *component) - if (ret) - return ret; - -- if (aic32x4->setup) -- aic32x4_setup_gpios(component); -+ aic32x4_setup_gpios(component); - - clk_set_parent(clocks[0].clk, clocks[1].clk); - clk_set_parent(clocks[2].clk, clocks[3].clk); -@@ -1192,7 +1153,7 @@ static int aic32x4_tas2505_component_probe(struct snd_soc_component *component) - snd_soc_component_write(component, AIC32X4_CMMODE, tmp_reg); - - /* -- * Enable the fast charging feature and ensure the needed 40ms ellapsed -+ * Enable the fast charging feature and ensure the needed 40ms elapsed - * before using the analog circuits. - */ - snd_soc_component_write(component, TAS2505_REFPOWERUP, -@@ -1218,17 +1179,10 @@ static const struct snd_soc_component_driver soc_component_dev_aic32x4_tas2505 = - .endianness = 1, - }; - --static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, -- struct device_node *np) -+static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, struct device_node *np) - { -- struct aic32x4_setup_data *aic32x4_setup; - int ret; - -- aic32x4_setup = devm_kzalloc(aic32x4->dev, sizeof(*aic32x4_setup), -- GFP_KERNEL); -- if (!aic32x4_setup) -- return -ENOMEM; -- - ret = of_property_match_string(np, "clock-names", "mclk"); - if (ret < 0) - return -EINVAL; -@@ -1245,9 +1199,11 @@ static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, - gpiod_set_consumer_name(aic32x4->rstn_gpio, "tlv320aic32x4_rstn"); - } - -- if (of_property_read_u32_array(np, "aic32x4-gpio-func", -- aic32x4_setup->gpio_func, 5) >= 0) -- aic32x4->setup = aic32x4_setup; -+ for (int i = 0; i < ARRAY_SIZE(aic32x4->gpio_func); i++) -+ aic32x4->gpio_func[i] = AIC32X4_MFPX_DEFAULT_VALUE; -+ of_property_read_u32_array(np, "aic32x4-gpio-func", -+ aic32x4->gpio_func, ARRAY_SIZE(aic32x4->gpio_func)); -+ - return 0; - } - -@@ -1266,7 +1222,7 @@ static void aic32x4_disable_regulators(struct aic32x4_priv *aic32x4) - } - - static int aic32x4_setup_regulators(struct device *dev, -- struct aic32x4_priv *aic32x4) -+ struct aic32x4_priv *aic32x4) - { - int ret = 0; - -@@ -1356,12 +1312,8 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap, - struct device_node *np = dev->of_node; - int ret; - -- if (IS_ERR(regmap)) -- return PTR_ERR(regmap); -- -- aic32x4 = devm_kzalloc(dev, sizeof(struct aic32x4_priv), -- GFP_KERNEL); -- if (aic32x4 == NULL) -+ aic32x4 = devm_kzalloc(dev, sizeof(struct aic32x4_priv), GFP_KERNEL); -+ if (!aic32x4) - return -ENOMEM; - - aic32x4->dev = dev; -@@ -1405,11 +1357,13 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap, - switch (aic32x4->type) { - case AIC32X4_TYPE_TAS2505: - ret = devm_snd_soc_register_component(dev, -- &soc_component_dev_aic32x4_tas2505, &aic32x4_tas2505_dai, 1); -+ &soc_component_dev_aic32x4_tas2505, -+ &aic32x4_tas2505_dai, 1); - break; - default: - ret = devm_snd_soc_register_component(dev, -- &soc_component_dev_aic32x4, &aic32x4_dai, 1); -+ &soc_component_dev_aic32x4, -+ &aic32x4_dai, 1); - } - - if (ret) { -diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h ---- a/sound/soc/codecs/tlv320aic32x4.h -+++ b/sound/soc/codecs/tlv320aic32x4.h -@@ -3,7 +3,6 @@ - * tlv320aic32x4.h - */ - -- - #ifndef _TLV320AIC32X4_H - #define _TLV320AIC32X4_H - -@@ -16,7 +15,7 @@ enum aic32x4_type { - AIC32X4_TYPE_TAS2505, - }; - --extern const struct regmap_config aic32x4_regmap_config; -+extern const struct regmap_range_cfg aic32x4_regmap_pages[]; - int aic32x4_probe(struct device *dev, struct regmap *regmap, - enum aic32x4_type type); - void aic32x4_remove(struct device *dev); -@@ -24,7 +23,7 @@ int aic32x4_register_clocks(struct device *dev, const char *mclk_name); - - /* tlv320aic32x4 register space (in decimal to match datasheet) */ - --#define AIC32X4_REG(page, reg) ((page * 128) + reg) -+#define AIC32X4_REG(page, reg) (((page) * 128) + (reg)) - - #define AIC32X4_PSEL AIC32X4_REG(0, 0) - -@@ -234,4 +233,31 @@ int aic32x4_register_clocks(struct device *dev, const char *mclk_name); - #define AIC32X4_MAX_CODEC_CLKIN_FREQ 110000000 - #define AIC32X4_MAX_PLL_CLKIN 20000000 - -+#define AIC32X4_PWR_MICBIAS_2075_LDOIN 0x00000001 -+#define AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE 0x00000002 -+#define AIC32X4_PWR_AIC32X4_LDO_ENABLE 0x00000004 -+#define AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36 0x00000008 -+#define AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED 0x00000010 -+ -+#define AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K 0x00000001 -+#define AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K 0x00000002 -+ -+/* GPIO API */ -+#define AIC32X4_MFPX_DEFAULT_VALUE 0xff -+ -+#define AIC32X4_MFP1_DIN_DISABLED 0 -+#define AIC32X4_MFP1_DIN_ENABLED 0x2 -+#define AIC32X4_MFP1_GPIO_IN 0x4 -+ -+#define AIC32X4_MFP2_GPIO_OUT_LOW 0x0 -+#define AIC32X4_MFP2_GPIO_OUT_HIGH 0x1 -+ -+#define AIC32X4_MFP_GPIO_ENABLED 0x4 -+ -+#define AIC32X4_MFP5_GPIO_DISABLED 0x0 -+#define AIC32X4_MFP5_GPIO_INPUT 0x8 -+#define AIC32X4_MFP5_GPIO_OUTPUT 0xc -+#define AIC32X4_MFP5_GPIO_OUT_LOW 0x0 -+#define AIC32X4_MFP5_GPIO_OUT_HIGH 0x1 -+ - #endif /* _TLV320AIC32X4_H */ -diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c ---- a/sound/soc/codecs/tlv320dac33.c -+++ b/sound/soc/codecs/tlv320dac33.c -@@ -7,6 +7,7 @@ - * Copyright: (C) 2009 Nokia Corporation - */ - -+#include - #include - #include - #include -@@ -236,13 +237,10 @@ static int dac33_write_locked(struct snd_soc_component *component, unsigned int - unsigned int value) - { - struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); -- int ret; - -- mutex_lock(&dac33->mutex); -- ret = dac33_write(component, reg, value); -- mutex_unlock(&dac33->mutex); -+ guard(mutex)(&dac33->mutex); - -- return ret; -+ return dac33_write(component, reg, value); - } - - #define DAC33_I2C_ADDR_AUTOINC 0x80 -@@ -365,13 +363,13 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) - struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); - int ret = 0; - -- mutex_lock(&dac33->mutex); -+ guard(mutex)(&dac33->mutex); - - /* Safety check */ - if (unlikely(power == dac33->chip_power)) { - dev_dbg(component->dev, "Trying to set the same power state: %s\n", - power ? "ON" : "OFF"); -- goto exit; -+ return ret; - } - - if (power) { -@@ -380,7 +378,7 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) - if (ret != 0) { - dev_err(component->dev, - "Failed to enable supplies: %d\n", ret); -- goto exit; -+ return ret; - } - - if (dac33->reset_gpiod) { -@@ -388,7 +386,7 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) - if (ret < 0) { - dev_err(&dac33->i2c->dev, - "Failed to set reset GPIO: %d\n", ret); -- goto exit; -+ return ret; - } - } - -@@ -400,7 +398,7 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) - if (ret < 0) { - dev_err(&dac33->i2c->dev, - "Failed to set reset GPIO: %d\n", ret); -- goto exit; -+ return ret; - } - } - -@@ -409,14 +407,12 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) - if (ret != 0) { - dev_err(component->dev, - "Failed to disable supplies: %d\n", ret); -- goto exit; -+ return ret; - } - - dac33->chip_power = 0; - } - --exit: -- mutex_unlock(&dac33->mutex); - return ret; - } - -@@ -659,7 +655,6 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) - { - struct snd_soc_component *component = dac33->component; - unsigned int delay; -- unsigned long flags; - - switch (dac33->fifo_mode) { - case DAC33_FIFO_MODE1: -@@ -667,10 +662,10 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) - DAC33_THRREG(dac33->nsample)); - - /* Take the timestamps */ -- spin_lock_irqsave(&dac33->lock, flags); -- dac33->t_stamp2 = ktime_to_us(ktime_get()); -- dac33->t_stamp1 = dac33->t_stamp2; -- spin_unlock_irqrestore(&dac33->lock, flags); -+ scoped_guard(spinlock_irqsave, &dac33->lock) { -+ dac33->t_stamp2 = ktime_to_us(ktime_get()); -+ dac33->t_stamp1 = dac33->t_stamp2; -+ } - - dac33_write16(component, DAC33_PREFILL_MSB, - DAC33_THRREG(dac33->alarm_threshold)); -@@ -682,11 +677,11 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) - break; - case DAC33_FIFO_MODE7: - /* Take the timestamp */ -- spin_lock_irqsave(&dac33->lock, flags); -- dac33->t_stamp1 = ktime_to_us(ktime_get()); -- /* Move back the timestamp with drain time */ -- dac33->t_stamp1 -= dac33->mode7_us_to_lthr; -- spin_unlock_irqrestore(&dac33->lock, flags); -+ scoped_guard(spinlock_irqsave, &dac33->lock) { -+ dac33->t_stamp1 = ktime_to_us(ktime_get()); -+ /* Move back the timestamp with drain time */ -+ dac33->t_stamp1 -= dac33->mode7_us_to_lthr; -+ } - - dac33_write16(component, DAC33_PREFILL_MSB, - DAC33_THRREG(DAC33_MODE7_MARGIN)); -@@ -704,14 +699,12 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) - static inline void dac33_playback_handler(struct tlv320dac33_priv *dac33) - { - struct snd_soc_component *component = dac33->component; -- unsigned long flags; - - switch (dac33->fifo_mode) { - case DAC33_FIFO_MODE1: - /* Take the timestamp */ -- spin_lock_irqsave(&dac33->lock, flags); -- dac33->t_stamp2 = ktime_to_us(ktime_get()); -- spin_unlock_irqrestore(&dac33->lock, flags); -+ scoped_guard(spinlock_irqsave, &dac33->lock) -+ dac33->t_stamp2 = ktime_to_us(ktime_get()); - - dac33_write16(component, DAC33_NSAMPLE_MSB, - DAC33_THRREG(dac33->nsample)); -@@ -735,7 +728,7 @@ static void dac33_work(struct work_struct *work) - dac33 = container_of(work, struct tlv320dac33_priv, work); - component = dac33->component; - -- mutex_lock(&dac33->mutex); -+ guard(mutex)(&dac33->mutex); - switch (dac33->state) { - case DAC33_PREFILL: - dac33->state = DAC33_PLAYBACK; -@@ -757,18 +750,15 @@ static void dac33_work(struct work_struct *work) - dac33_write(component, DAC33_FIFO_CTRL_A, reg); - break; - } -- mutex_unlock(&dac33->mutex); - } - - static irqreturn_t dac33_interrupt_handler(int irq, void *dev) - { - struct snd_soc_component *component = dev; - struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); -- unsigned long flags; - -- spin_lock_irqsave(&dac33->lock, flags); -- dac33->t_stamp1 = ktime_to_us(ktime_get()); -- spin_unlock_irqrestore(&dac33->lock, flags); -+ scoped_guard(spinlock_irqsave, &dac33->lock) -+ dac33->t_stamp1 = ktime_to_us(ktime_get()); - - /* Do not schedule the workqueue in Mode7 */ - if (dac33->fifo_mode != DAC33_FIFO_MODE7) -@@ -902,14 +892,13 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream, - return -EINVAL; - } - -- mutex_lock(&dac33->mutex); -+ guard(mutex)(&dac33->mutex); - - if (!dac33->chip_power) { - /* - * Chip is not powered yet. - * Do the init in the dac33_set_bias_level later. - */ -- mutex_unlock(&dac33->mutex); - return 0; - } - -@@ -1053,8 +1042,6 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream, - break; - } - -- mutex_unlock(&dac33->mutex); -- - return 0; - } - -@@ -1156,21 +1143,20 @@ static snd_pcm_sframes_t dac33_dai_delay( - unsigned int time_delta, uthr; - int samples_out, samples_in, samples; - snd_pcm_sframes_t delay = 0; -- unsigned long flags; - - switch (dac33->fifo_mode) { - case DAC33_FIFO_BYPASS: - break; - case DAC33_FIFO_MODE1: -- spin_lock_irqsave(&dac33->lock, flags); -- t0 = dac33->t_stamp1; -- t1 = dac33->t_stamp2; -- spin_unlock_irqrestore(&dac33->lock, flags); -+ scoped_guard(spinlock_irqsave, &dac33->lock) { -+ t0 = dac33->t_stamp1; -+ t1 = dac33->t_stamp2; -+ } - t_now = ktime_to_us(ktime_get()); - - /* We have not started to fill the FIFO yet, delay is 0 */ - if (!t1) -- goto out; -+ return 0; - - if (t0 > t1) { - /* -@@ -1230,23 +1216,22 @@ static snd_pcm_sframes_t dac33_dai_delay( - } - break; - case DAC33_FIFO_MODE7: -- spin_lock_irqsave(&dac33->lock, flags); -- t0 = dac33->t_stamp1; -- uthr = dac33->uthr; -- spin_unlock_irqrestore(&dac33->lock, flags); -+ scoped_guard(spinlock_irqsave, &dac33->lock) { -+ t0 = dac33->t_stamp1; -+ uthr = dac33->uthr; -+ } - t_now = ktime_to_us(ktime_get()); - - /* We have not started to fill the FIFO yet, delay is 0 */ - if (!t0) -- goto out; -+ return 0; - - if (t_now <= t0) { - /* - * Either the timestamps are messed or equal. Report - * maximum delay - */ -- delay = uthr; -- goto out; -+ return uthr; - } - - time_delta = t_now - t0; -@@ -1287,7 +1272,7 @@ static snd_pcm_sframes_t dac33_dai_delay( - dac33->fifo_mode); - break; - } --out: -+ - return delay; - } - -diff --git a/sound/soc/codecs/tscs42xx.c b/sound/soc/codecs/tscs42xx.c ---- a/sound/soc/codecs/tscs42xx.c -+++ b/sound/soc/codecs/tscs42xx.c -@@ -12,6 +12,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -210,25 +211,21 @@ static int power_up_audio_plls(struct snd_soc_component *component) - return ret; - } - -- mutex_lock(&tscs42xx->pll_lock); -+ guard(mutex)(&tscs42xx->pll_lock); - - ret = snd_soc_component_update_bits(component, R_PLLCTL1C, mask, val); - if (ret < 0) { - dev_err(component->dev, "Failed to turn PLL on (%d)\n", ret); -- goto exit; -+ return ret; - } - - if (!plls_locked(component)) { - dev_err(component->dev, "Failed to lock plls\n"); - ret = -ENOMSG; -- goto exit; -+ return ret; - } - -- ret = 0; --exit: -- mutex_unlock(&tscs42xx->pll_lock); -- -- return ret; -+ return 0; - } - - static int power_down_audio_plls(struct snd_soc_component *component) -@@ -236,28 +233,24 @@ static int power_down_audio_plls(struct snd_soc_component *component) - struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); - int ret; - -- mutex_lock(&tscs42xx->pll_lock); -+ guard(mutex)(&tscs42xx->pll_lock); - - ret = snd_soc_component_update_bits(component, R_PLLCTL1C, - RM_PLLCTL1C_PDB_PLL1, - RV_PLLCTL1C_PDB_PLL1_DISABLE); - if (ret < 0) { - dev_err(component->dev, "Failed to turn PLL off (%d)\n", ret); -- goto exit; -+ return ret; - } - ret = snd_soc_component_update_bits(component, R_PLLCTL1C, - RM_PLLCTL1C_PDB_PLL2, - RV_PLLCTL1C_PDB_PLL2_DISABLE); - if (ret < 0) { - dev_err(component->dev, "Failed to turn PLL off (%d)\n", ret); -- goto exit; -+ return ret; - } - -- ret = 0; --exit: -- mutex_unlock(&tscs42xx->pll_lock); -- -- return ret; -+ return 0; - } - - static int coeff_ram_get(struct snd_kcontrol *kcontrol, -@@ -269,13 +262,11 @@ static int coeff_ram_get(struct snd_kcontrol *kcontrol, - (struct coeff_ram_ctl *)kcontrol->private_value; - struct soc_bytes_ext *params = &ctl->bytes_ext; - -- mutex_lock(&tscs42xx->coeff_ram_lock); -+ guard(mutex)(&tscs42xx->coeff_ram_lock); - - memcpy(ucontrol->value.bytes.data, - &tscs42xx->coeff_ram[ctl->addr * COEFF_SIZE], params->max); - -- mutex_unlock(&tscs42xx->coeff_ram_lock); -- - return 0; - } - -@@ -290,14 +281,14 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol, - unsigned int coeff_cnt = params->max / COEFF_SIZE; - int ret; - -- mutex_lock(&tscs42xx->coeff_ram_lock); -+ guard(mutex)(&tscs42xx->coeff_ram_lock); - - tscs42xx->coeff_ram_synced = false; - - memcpy(&tscs42xx->coeff_ram[ctl->addr * COEFF_SIZE], - ucontrol->value.bytes.data, params->max); - -- mutex_lock(&tscs42xx->pll_lock); -+ guard(mutex)(&tscs42xx->pll_lock); - - if (plls_locked(component)) { - ret = write_coeff_ram(component, tscs42xx->coeff_ram, -@@ -305,18 +296,12 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol, - if (ret < 0) { - dev_err(component->dev, - "Failed to flush coeff ram cache (%d)\n", ret); -- goto exit; -+ return ret; - } - tscs42xx->coeff_ram_synced = true; - } - -- ret = 0; --exit: -- mutex_unlock(&tscs42xx->pll_lock); -- -- mutex_unlock(&tscs42xx->coeff_ram_lock); -- -- return ret; -+ return 0; - } - - /* Input L Capture Route */ -@@ -385,21 +370,17 @@ static int dac_event(struct snd_soc_dapm_widget *w, - struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); - int ret; - -- mutex_lock(&tscs42xx->coeff_ram_lock); -+ guard(mutex)(&tscs42xx->coeff_ram_lock); - - if (!tscs42xx->coeff_ram_synced) { - ret = write_coeff_ram(component, tscs42xx->coeff_ram, 0x00, - COEFF_RAM_COEFF_COUNT); - if (ret < 0) -- goto exit; -+ return ret; - tscs42xx->coeff_ram_synced = true; - } - -- ret = 0; --exit: -- mutex_unlock(&tscs42xx->coeff_ram_lock); -- -- return ret; -+ return 0; - } - - static const struct snd_soc_dapm_widget tscs42xx_dapm_widgets[] = { -@@ -926,12 +907,10 @@ static int setup_sample_rate(struct snd_soc_component *component, - return ret; - } - -- mutex_lock(&tscs42xx->audio_params_lock); -+ guard(mutex)(&tscs42xx->audio_params_lock); - - tscs42xx->samplerate = rate; - -- mutex_unlock(&tscs42xx->audio_params_lock); -- - return 0; - } - -@@ -1253,12 +1232,10 @@ static int tscs42xx_set_dai_bclk_ratio(struct snd_soc_dai *codec_dai, - return ret; - } - -- mutex_lock(&tscs42xx->audio_params_lock); -+ guard(mutex)(&tscs42xx->audio_params_lock); - - tscs42xx->bclk_ratio = ratio; - -- mutex_unlock(&tscs42xx->audio_params_lock); -- - return 0; - } - -diff --git a/sound/soc/codecs/tscs454.c b/sound/soc/codecs/tscs454.c ---- a/sound/soc/codecs/tscs454.c -+++ b/sound/soc/codecs/tscs454.c -@@ -4,6 +4,7 @@ - // Author: Steven Eckhoff - - #include -+#include - #include - #include - #include -@@ -329,12 +330,10 @@ static int coeff_ram_get(struct snd_kcontrol *kcontrol, - return -EINVAL; - } - -- mutex_lock(coeff_ram_lock); -- -- memcpy(ucontrol->value.bytes.data, -- &coeff_ram[ctl->addr * COEFF_SIZE], params->max); -- -- mutex_unlock(coeff_ram_lock); -+ scoped_guard(mutex, coeff_ram_lock) { -+ memcpy(ucontrol->value.bytes.data, -+ &coeff_ram[ctl->addr * COEFF_SIZE], params->max); -+ } - - return 0; - } -@@ -428,15 +427,15 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol, - return -EINVAL; - } - -- mutex_lock(coeff_ram_lock); -+ guard(mutex)(coeff_ram_lock); - - *coeff_ram_synced = false; - - memcpy(&coeff_ram[ctl->addr * COEFF_SIZE], - ucontrol->value.bytes.data, params->max); - -- mutex_lock(&tscs454->pll1.lock); -- mutex_lock(&tscs454->pll2.lock); -+ guard(mutex)(&tscs454->pll1.lock); -+ guard(mutex)(&tscs454->pll2.lock); - - val = snd_soc_component_read(component, R_PLLSTAT); - if (val) { /* PLLs locked */ -@@ -446,18 +445,12 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol, - if (ret < 0) { - dev_err(component->dev, - "Failed to flush coeff ram cache (%d)\n", ret); -- goto exit; -+ return ret; - } - *coeff_ram_synced = true; - } - -- ret = 0; --exit: -- mutex_unlock(&tscs454->pll2.lock); -- mutex_unlock(&tscs454->pll1.lock); -- mutex_unlock(coeff_ram_lock); -- -- return ret; -+ return 0; - } - - static inline int coeff_ram_sync(struct snd_soc_component *component, -@@ -465,41 +458,35 @@ static inline int coeff_ram_sync(struct snd_soc_component *component, - { - int ret; - -- mutex_lock(&tscs454->dac_ram.lock); -- if (!tscs454->dac_ram.synced) { -- ret = write_coeff_ram(component, tscs454->dac_ram.cache, -- R_DACCRS, R_DACCRADD, R_DACCRWDL, -- 0x00, COEFF_RAM_COEFF_COUNT); -- if (ret < 0) { -- mutex_unlock(&tscs454->dac_ram.lock); -- return ret; -+ scoped_guard(mutex, &tscs454->dac_ram.lock) { -+ if (!tscs454->dac_ram.synced) { -+ ret = write_coeff_ram(component, tscs454->dac_ram.cache, -+ R_DACCRS, R_DACCRADD, R_DACCRWDL, -+ 0x00, COEFF_RAM_COEFF_COUNT); -+ if (ret < 0) -+ return ret; - } - } -- mutex_unlock(&tscs454->dac_ram.lock); - -- mutex_lock(&tscs454->spk_ram.lock); -- if (!tscs454->spk_ram.synced) { -- ret = write_coeff_ram(component, tscs454->spk_ram.cache, -- R_SPKCRS, R_SPKCRADD, R_SPKCRWDL, -- 0x00, COEFF_RAM_COEFF_COUNT); -- if (ret < 0) { -- mutex_unlock(&tscs454->spk_ram.lock); -- return ret; -+ scoped_guard(mutex, &tscs454->spk_ram.lock) { -+ if (!tscs454->spk_ram.synced) { -+ ret = write_coeff_ram(component, tscs454->spk_ram.cache, -+ R_SPKCRS, R_SPKCRADD, R_SPKCRWDL, -+ 0x00, COEFF_RAM_COEFF_COUNT); -+ if (ret < 0) -+ return ret; - } - } -- mutex_unlock(&tscs454->spk_ram.lock); - -- mutex_lock(&tscs454->sub_ram.lock); -- if (!tscs454->sub_ram.synced) { -- ret = write_coeff_ram(component, tscs454->sub_ram.cache, -- R_SUBCRS, R_SUBCRADD, R_SUBCRWDL, -- 0x00, COEFF_RAM_COEFF_COUNT); -- if (ret < 0) { -- mutex_unlock(&tscs454->sub_ram.lock); -- return ret; -+ scoped_guard(mutex, &tscs454->sub_ram.lock) { -+ if (!tscs454->sub_ram.synced) { -+ ret = write_coeff_ram(component, tscs454->sub_ram.cache, -+ R_SUBCRS, R_SUBCRADD, R_SUBCRWDL, -+ 0x00, COEFF_RAM_COEFF_COUNT); -+ if (ret < 0) -+ return ret; - } - } -- mutex_unlock(&tscs454->sub_ram.lock); - - return 0; - } -@@ -658,16 +645,14 @@ static int set_sysclk(struct snd_soc_component *component) - - static inline void reserve_pll(struct pll *pll) - { -- mutex_lock(&pll->lock); -+ guard(mutex)(&pll->lock); - pll->users++; -- mutex_unlock(&pll->lock); - } - - static inline void free_pll(struct pll *pll) - { -- mutex_lock(&pll->lock); -+ guard(mutex)(&pll->lock); - pll->users--; -- mutex_unlock(&pll->lock); - } - - static int pll_connected(struct snd_soc_dapm_widget *source, -@@ -679,15 +664,13 @@ static int pll_connected(struct snd_soc_dapm_widget *source, - int users; - - if (strstr(source->name, "PLL 1")) { -- mutex_lock(&tscs454->pll1.lock); -- users = tscs454->pll1.users; -- mutex_unlock(&tscs454->pll1.lock); -+ scoped_guard(mutex, &tscs454->pll1.lock) -+ users = tscs454->pll1.users; - dev_dbg(component->dev, "%s(): PLL 1 users = %d\n", __func__, - users); - } else { -- mutex_lock(&tscs454->pll2.lock); -- users = tscs454->pll2.users; -- mutex_unlock(&tscs454->pll2.lock); -+ scoped_guard(mutex, &tscs454->pll2.lock) -+ users = tscs454->pll2.users; - dev_dbg(component->dev, "%s(): PLL 2 users = %d\n", __func__, - users); - } -@@ -806,7 +789,7 @@ static inline int aif_free(struct snd_soc_component *component, - { - struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); - -- mutex_lock(&tscs454->aifs_status_lock); -+ guard(mutex)(&tscs454->aifs_status_lock); - - dev_dbg(component->dev, "%s(): aif %d\n", __func__, aif->id); - -@@ -829,8 +812,6 @@ static inline int aif_free(struct snd_soc_component *component, - free_pll(tscs454->internal_rate.pll); - } - -- mutex_unlock(&tscs454->aifs_status_lock); -- - return 0; - } - -@@ -3174,7 +3155,7 @@ static int tscs454_hw_params(struct snd_pcm_substream *substream, - unsigned int val; - int ret; - -- mutex_lock(&tscs454->aifs_status_lock); -+ guard(mutex)(&tscs454->aifs_status_lock); - - dev_dbg(component->dev, "%s(): aif %d fs = %u\n", __func__, - aif->id, fs); -@@ -3207,14 +3188,14 @@ static int tscs454_hw_params(struct snd_pcm_substream *substream, - ret = set_aif_fs(component, aif->id, fs); - if (ret < 0) { - dev_err(component->dev, "Failed to set aif fs (%d)\n", ret); -- goto exit; -+ return ret; - } - - ret = set_aif_sample_format(component, params_format(params), aif->id); - if (ret < 0) { - dev_err(component->dev, - "Failed to set aif sample format (%d)\n", ret); -- goto exit; -+ return ret; - } - - set_aif_status_active(&tscs454->aifs_status, aif->id, -@@ -3223,11 +3204,7 @@ static int tscs454_hw_params(struct snd_pcm_substream *substream, - dev_dbg(component->dev, "Set aif %d active. Streams status is 0x%x\n", - aif->id, tscs454->aifs_status.streams); - -- ret = 0; --exit: -- mutex_unlock(&tscs454->aifs_status_lock); -- -- return ret; -+ return 0; - } - - static int tscs454_hw_free(struct snd_pcm_substream *substream, -diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c ---- a/sound/soc/codecs/twl6040.c -+++ b/sound/soc/codecs/twl6040.c -@@ -8,6 +8,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -273,7 +274,7 @@ static void twl6040_hs_jack_report(struct snd_soc_component *component, - struct twl6040_data *priv = snd_soc_component_get_drvdata(component); - int status; - -- mutex_lock(&priv->mutex); -+ guard(mutex)(&priv->mutex); - - /* Sync status */ - status = twl6040_read(component, TWL6040_REG_STATUS); -@@ -281,8 +282,6 @@ static void twl6040_hs_jack_report(struct snd_soc_component *component, - snd_soc_jack_report(jack, report, report); - else - snd_soc_jack_report(jack, 0, report); -- -- mutex_unlock(&priv->mutex); - } - - void twl6040_hs_jack_detect(struct snd_soc_component *component, -diff --git a/sound/soc/codecs/uda1342.c b/sound/soc/codecs/uda1342.c ---- a/sound/soc/codecs/uda1342.c -+++ b/sound/soc/codecs/uda1342.c -@@ -308,9 +308,16 @@ static int uda1342_suspend(struct device *dev) - static int uda1342_resume(struct device *dev) - { - struct uda1342_priv *uda1342 = dev_get_drvdata(dev); -+ int ret; - -+ regcache_cache_only(uda1342->regmap, false); - regcache_mark_dirty(uda1342->regmap); -- regcache_sync(uda1342->regmap); -+ ret = regcache_sync(uda1342->regmap); -+ if (ret) { -+ regcache_cache_only(uda1342->regmap, true); -+ regcache_mark_dirty(uda1342->regmap); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c ---- a/sound/soc/codecs/wcd9335.c -+++ b/sound/soc/codecs/wcd9335.c -@@ -5010,9 +5010,9 @@ static int wcd9335_power_on_reset(struct wcd9335_codec *wcd) - */ - usleep_range(600, 650); - -- gpiod_set_value(wcd->reset_gpio, 1); -+ gpiod_set_value_cansleep(wcd->reset_gpio, 1); - msleep(20); -- gpiod_set_value(wcd->reset_gpio, 0); -+ gpiod_set_value_cansleep(wcd->reset_gpio, 0); - msleep(20); - - return 0; -diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c ---- a/sound/soc/codecs/wcd934x.c -+++ b/sound/soc/codecs/wcd934x.c -@@ -1265,13 +1265,10 @@ static int wcd934x_set_sido_input_src(struct wcd934x_codec *wcd, int sido_src) - - static int wcd934x_enable_ana_bias_and_sysclk(struct wcd934x_codec *wcd) - { -- mutex_lock(&wcd->sysclk_mutex); -- -- if (++wcd->sysclk_users != 1) { -- mutex_unlock(&wcd->sysclk_mutex); -- return 0; -+ scoped_guard(mutex, &wcd->sysclk_mutex) { -+ if (++wcd->sysclk_users != 1) -+ return 0; - } -- mutex_unlock(&wcd->sysclk_mutex); - - regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS, - WCD934X_ANA_BIAS_EN_MASK, -@@ -1328,12 +1325,10 @@ static int wcd934x_enable_ana_bias_and_sysclk(struct wcd934x_codec *wcd) - - static int wcd934x_disable_ana_bias_and_syclk(struct wcd934x_codec *wcd) - { -- mutex_lock(&wcd->sysclk_mutex); -- if (--wcd->sysclk_users != 0) { -- mutex_unlock(&wcd->sysclk_mutex); -- return 0; -+ scoped_guard(mutex, &wcd->sysclk_mutex) { -+ if (--wcd->sysclk_users != 0) -+ return 0; - } -- mutex_unlock(&wcd->sysclk_mutex); - - regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG, - WCD934X_EXT_CLK_BUF_EN_MASK | -@@ -2384,7 +2379,7 @@ static int wcd934x_micbias_control(struct snd_soc_component *component, - __func__, micb_num); - return -EINVAL; - } -- mutex_lock(&wcd934x->micb_lock); -+ guard(mutex)(&wcd934x->micb_lock); - - switch (req) { - case MICB_PULLUP_ENABLE: -@@ -2446,8 +2441,6 @@ static int wcd934x_micbias_control(struct snd_soc_component *component, - break; - } - -- mutex_unlock(&wcd934x->micb_lock); -- - return 0; - } - -@@ -2488,7 +2481,7 @@ static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - int req_volt, int micb_num) - { - struct wcd934x_codec *wcd934x = snd_soc_component_get_drvdata(component); -- int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0; -+ int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en; - - switch (micb_num) { - case MIC_BIAS_1: -@@ -2506,7 +2499,7 @@ static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - default: - return -EINVAL; - } -- mutex_lock(&wcd934x->micb_lock); -+ guard(mutex)(&wcd934x->micb_lock); - /* - * If requested micbias voltage is same as current micbias - * voltage, then just return. Otherwise, adjust voltage as -@@ -2521,15 +2514,11 @@ static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - WCD934X_MICB_VAL_MASK); - - req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt); -- if (req_vout_ctl < 0) { -- ret = -EINVAL; -- goto exit; -- } -+ if (req_vout_ctl < 0) -+ return -EINVAL; - -- if (cur_vout_ctl == req_vout_ctl) { -- ret = 0; -- goto exit; -- } -+ if (cur_vout_ctl == req_vout_ctl) -+ return 0; - - if (micb_en == WCD934X_MICB_ENABLE) - snd_soc_component_write_field(component, micb_reg, -@@ -2550,9 +2539,8 @@ static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - */ - usleep_range(2000, 2100); - } --exit: -- mutex_unlock(&wcd934x->micb_lock); -- return ret; -+ -+ return 0; - } - - static int wcd934x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, -@@ -5899,10 +5887,8 @@ static int wcd934x_codec_probe(struct platform_device *pdev) - } - - static const struct platform_device_id wcd934x_driver_id[] = { -- { -- .name = "wcd934x-codec", -- }, -- {}, -+ { .name = "wcd934x-codec" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, wcd934x_driver_id); - -diff --git a/sound/soc/codecs/wcd937x-sdw.c b/sound/soc/codecs/wcd937x-sdw.c ---- a/sound/soc/codecs/wcd937x-sdw.c -+++ b/sound/soc/codecs/wcd937x-sdw.c -@@ -1084,10 +1084,16 @@ static int wcd937x_sdw_runtime_suspend(struct device *dev) - static int wcd937x_sdw_runtime_resume(struct device *dev) - { - struct wcd937x_sdw_priv *wcd = dev_get_drvdata(dev); -+ int ret; - - if (wcd->regmap) { - regcache_cache_only(wcd->regmap, false); -- regcache_sync(wcd->regmap); -+ ret = regcache_sync(wcd->regmap); -+ if (ret) { -+ regcache_cache_only(wcd->regmap, true); -+ regcache_mark_dirty(wcd->regmap); -+ return ret; -+ } - } - - return 0; -diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c ---- a/sound/soc/codecs/wcd937x.c -+++ b/sound/soc/codecs/wcd937x.c -@@ -2,6 +2,7 @@ - // Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved. - - #include -+#include - #include - #include - #include -@@ -1056,7 +1057,7 @@ static int wcd937x_micbias_control(struct snd_soc_component *component, - return -EINVAL; - } - -- mutex_lock(&wcd937x->micb_lock); -+ guard(mutex)(&wcd937x->micb_lock); - switch (req) { - case MICB_PULLUP_ENABLE: - wcd937x->pullup_ref[micb_index]++; -@@ -1136,7 +1137,6 @@ static int wcd937x_micbias_control(struct snd_soc_component *component, - } - break; - } -- mutex_unlock(&wcd937x->micb_lock); - - return 0; - } -@@ -1460,7 +1460,7 @@ static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - int req_volt, int micb_num) - { - struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); -- int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0; -+ int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en; - - switch (micb_num) { - case MIC_BIAS_1: -@@ -1475,7 +1475,7 @@ static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - default: - return -EINVAL; - } -- mutex_lock(&wcd937x->micb_lock); -+ guard(mutex)(&wcd937x->micb_lock); - /* - * If requested micbias voltage is same as current micbias - * voltage, then just return. Otherwise, adjust voltage as -@@ -1490,15 +1490,11 @@ static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - WCD937X_MICB_VOUT_MASK); - - req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt); -- if (req_vout_ctl < 0) { -- ret = -EINVAL; -- goto exit; -- } -+ if (req_vout_ctl < 0) -+ return -EINVAL; - -- if (cur_vout_ctl == req_vout_ctl) { -- ret = 0; -- goto exit; -- } -+ if (cur_vout_ctl == req_vout_ctl) -+ return 0; - - if (micb_en == WCD937X_MICB_ENABLE) - snd_soc_component_write_field(component, micb_reg, -@@ -1519,9 +1515,8 @@ static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - */ - usleep_range(2000, 2100); - } --exit: -- mutex_unlock(&wcd937x->micb_lock); -- return ret; -+ -+ return 0; - } - - static int wcd937x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, -diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c ---- a/sound/soc/codecs/wcd938x-sdw.c -+++ b/sound/soc/codecs/wcd938x-sdw.c -@@ -1245,10 +1245,16 @@ static int wcd938x_sdw_runtime_suspend(struct device *dev) - static int wcd938x_sdw_runtime_resume(struct device *dev) - { - struct wcd938x_sdw_priv *wcd = dev_get_drvdata(dev); -+ int ret; - - if (wcd->regmap) { - regcache_cache_only(wcd->regmap, false); -- regcache_sync(wcd->regmap); -+ ret = regcache_sync(wcd->regmap); -+ if (ret) { -+ regcache_cache_only(wcd->regmap, true); -+ regcache_mark_dirty(wcd->regmap); -+ return ret; -+ } - } - - pm_runtime_mark_last_busy(dev); -diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c ---- a/sound/soc/codecs/wcd938x.c -+++ b/sound/soc/codecs/wcd938x.c -@@ -1,6 +1,7 @@ - // SPDX-License-Identifier: GPL-2.0-only - // Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. - -+#include - #include - #include - #include -@@ -1976,7 +1977,7 @@ static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - int req_volt, int micb_num) - { - struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); -- int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0; -+ int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en; - - switch (micb_num) { - case MIC_BIAS_1: -@@ -1994,7 +1995,7 @@ static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - default: - return -EINVAL; - } -- mutex_lock(&wcd938x->micb_lock); -+ guard(mutex)(&wcd938x->micb_lock); - /* - * If requested micbias voltage is same as current micbias - * voltage, then just return. Otherwise, adjust voltage as -@@ -2009,15 +2010,11 @@ static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - WCD938X_MICB_VOUT_MASK); - - req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt); -- if (req_vout_ctl < 0) { -- ret = -EINVAL; -- goto exit; -- } -+ if (req_vout_ctl < 0) -+ return -EINVAL; - -- if (cur_vout_ctl == req_vout_ctl) { -- ret = 0; -- goto exit; -- } -+ if (cur_vout_ctl == req_vout_ctl) -+ return 0; - - if (micb_en == WCD938X_MICB_ENABLE) - snd_soc_component_write_field(component, micb_reg, -@@ -2038,9 +2035,8 @@ static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - */ - usleep_range(2000, 2100); - } --exit: -- mutex_unlock(&wcd938x->micb_lock); -- return ret; -+ -+ return 0; - } - - static int wcd938x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, -diff --git a/sound/soc/codecs/wcd939x-sdw.c b/sound/soc/codecs/wcd939x-sdw.c ---- a/sound/soc/codecs/wcd939x-sdw.c -+++ b/sound/soc/codecs/wcd939x-sdw.c -@@ -1431,10 +1431,16 @@ static int wcd939x_sdw_runtime_suspend(struct device *dev) - static int wcd939x_sdw_runtime_resume(struct device *dev) - { - struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev); -+ int ret; - - if (wcd->regmap) { - regcache_cache_only(wcd->regmap, false); -- regcache_sync(wcd->regmap); -+ ret = regcache_sync(wcd->regmap); -+ if (ret) { -+ regcache_cache_only(wcd->regmap, true); -+ regcache_mark_dirty(wcd->regmap); -+ return ret; -+ } - } - - return 0; -diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c ---- a/sound/soc/codecs/wcd939x.c -+++ b/sound/soc/codecs/wcd939x.c -@@ -8,6 +8,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -1923,7 +1924,6 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); - unsigned int micb_reg, cur_vout_ctl, micb_en; - int req_vout_ctl; -- int ret = 0; - - switch (micb_num) { - case MIC_BIAS_1: -@@ -1941,7 +1941,7 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - default: - return -EINVAL; - } -- mutex_lock(&wcd939x->micb_lock); -+ guard(mutex)(&wcd939x->micb_lock); - - /* - * If requested micbias voltage is same as current micbias -@@ -1957,15 +1957,11 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - WCD939X_MICB_VOUT_CTL); - - req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt); -- if (req_vout_ctl < 0) { -- ret = req_vout_ctl; -- goto exit; -- } -+ if (req_vout_ctl < 0) -+ return req_vout_ctl; - -- if (cur_vout_ctl == req_vout_ctl) { -- ret = 0; -- goto exit; -- } -+ if (cur_vout_ctl == req_vout_ctl) -+ return 0; - - dev_dbg(component->dev, "%s: micb_num: %d, cur_mv: %d, req_mv: %d, micb_en: %d\n", - __func__, micb_num, WCD_VOUT_CTL_TO_MICB(cur_vout_ctl), -@@ -1990,9 +1986,7 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, - usleep_range(2000, 2100); - } - --exit: -- mutex_unlock(&wcd939x->micb_lock); -- return ret; -+ return 0; - } - - static int wcd939x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, -diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c ---- a/sound/soc/codecs/wm0010.c -+++ b/sound/soc/codecs/wm0010.c -@@ -9,6 +9,7 @@ - * Scott Ling - */ - -+#include - #include - #include - #include -@@ -148,13 +149,11 @@ static const char *wm0010_state_to_str(enum wm0010_state state) - static void wm0010_halt(struct snd_soc_component *component) - { - struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); -- unsigned long flags; - enum wm0010_state state; - - /* Fetch the wm0010 state */ -- spin_lock_irqsave(&wm0010->irq_lock, flags); -- state = wm0010->state; -- spin_unlock_irqrestore(&wm0010->irq_lock, flags); -+ scoped_guard(spinlock_irqsave, &wm0010->irq_lock) -+ state = wm0010->state; - - switch (state) { - case WM0010_POWER_OFF: -@@ -173,9 +172,8 @@ static void wm0010_halt(struct snd_soc_component *component) - break; - } - -- spin_lock_irqsave(&wm0010->irq_lock, flags); -- wm0010->state = WM0010_POWER_OFF; -- spin_unlock_irqrestore(&wm0010->irq_lock, flags); -+ scoped_guard(spinlock_irqsave, &wm0010->irq_lock) -+ wm0010->state = WM0010_POWER_OFF; - } - - struct wm0010_boot_xfer { -@@ -190,11 +188,9 @@ struct wm0010_boot_xfer { - static void wm0010_mark_boot_failure(struct wm0010_priv *wm0010) - { - enum wm0010_state state; -- unsigned long flags; - -- spin_lock_irqsave(&wm0010->irq_lock, flags); -- state = wm0010->state; -- spin_unlock_irqrestore(&wm0010->irq_lock, flags); -+ scoped_guard(spinlock_irqsave, &wm0010->irq_lock) -+ state = wm0010->state; - - dev_err(wm0010->dev, "Failed to transition from `%s' state to `%s' state\n", - wm0010_state_to_str(state), wm0010_state_to_str(state + 1)); -@@ -337,7 +333,6 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp - struct wm0010_boot_xfer *xfer; - int ret; - DECLARE_COMPLETION_ONSTACK(done); -- const struct firmware *fw; - const struct dfw_binrec *rec; - const struct dfw_inforec *inforec; - u64 *img; -@@ -346,6 +341,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp - - INIT_LIST_HEAD(&xfer_list); - -+ const struct firmware *fw __free(firmware) = NULL; - ret = request_firmware(&fw, name, component->dev); - if (ret != 0) { - dev_err(component->dev, "Failed to request application(%s): %d\n", -@@ -364,16 +360,14 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp - /* First record should be INFO */ - if (rec->command != DFW_CMD_INFO) { - dev_err(component->dev, "First record not INFO\r\n"); -- ret = -EINVAL; -- goto abort; -+ return -EINVAL; - } - - if (inforec->info_version != INFO_VERSION) { - dev_err(component->dev, - "Unsupported version (%02d) of INFO record\r\n", - inforec->info_version); -- ret = -EINVAL; -- goto abort; -+ return -EINVAL; - } - - dev_dbg(component->dev, "Version v%02d INFO record found\r\n", -@@ -382,8 +376,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp - /* Check it's a DSP file */ - if (dsp != DEVICE_ID_WM0010) { - dev_err(component->dev, "Not a WM0010 firmware file.\r\n"); -- ret = -EINVAL; -- goto abort; -+ return -EINVAL; - } - - /* Skip the info record as we don't need to send it */ -@@ -408,14 +401,14 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp - out = kzalloc(len, GFP_KERNEL | GFP_DMA); - if (!out) { - ret = -ENOMEM; -- goto abort1; -+ goto abort; - } - xfer->t.rx_buf = out; - - img = kzalloc(len, GFP_KERNEL | GFP_DMA); - if (!img) { - ret = -ENOMEM; -- goto abort1; -+ goto abort; - } - xfer->t.tx_buf = img; - -@@ -453,13 +446,13 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp - ret = spi_async(spi, &xfer->m); - if (ret != 0) { - dev_err(component->dev, "Write failed: %d\n", ret); -- goto abort1; -+ goto abort; - } - - if (wm0010->boot_failed) { - dev_dbg(component->dev, "Boot fail!\n"); - ret = -EINVAL; -- goto abort1; -+ goto abort; - } - } - -@@ -467,7 +460,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp - - ret = 0; - --abort1: -+abort: - while (!list_empty(&xfer_list)) { - xfer = list_first_entry(&xfer_list, struct wm0010_boot_xfer, - list); -@@ -477,8 +470,6 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp - kfree(xfer); - } - --abort: -- release_firmware(fw); - return ret; - } - -@@ -486,14 +477,12 @@ static int wm0010_stage2_load(struct snd_soc_component *component) - { - struct spi_device *spi = to_spi_device(component->dev); - struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); -- const struct firmware *fw; - struct spi_message m; - struct spi_transfer t; -- u32 *img; -- u8 *out; - int i; - int ret = 0; - -+ const struct firmware *fw __free(firmware) = NULL; - ret = request_firmware(&fw, "wm0010_stage2.bin", component->dev); - if (ret != 0) { - dev_err(component->dev, "Failed to request stage2 loader: %d\n", -@@ -504,17 +493,15 @@ static int wm0010_stage2_load(struct snd_soc_component *component) - dev_dbg(component->dev, "Downloading %zu byte stage 2 loader\n", fw->size); - - /* Copy to local buffer first as vmalloc causes problems for dma */ -- img = kmemdup(&fw->data[0], fw->size, GFP_KERNEL | GFP_DMA); -- if (!img) { -- ret = -ENOMEM; -- goto abort2; -- } -+ u32 *img __free(kfree) = -+ kmemdup(&fw->data[0], fw->size, GFP_KERNEL | GFP_DMA); -+ if (!img) -+ return -ENOMEM; - -- out = kzalloc(fw->size, GFP_KERNEL | GFP_DMA); -- if (!out) { -- ret = -ENOMEM; -- goto abort1; -- } -+ u8 *out __free(kfree) = -+ kzalloc(fw->size, GFP_KERNEL | GFP_DMA); -+ if (!out) -+ return -ENOMEM; - - spi_message_init(&m); - memset(&t, 0, sizeof(t)); -@@ -531,7 +518,7 @@ static int wm0010_stage2_load(struct snd_soc_component *component) - ret = spi_sync(spi, &m); - if (ret != 0) { - dev_err(component->dev, "Initial download failed: %d\n", ret); -- goto abort; -+ return ret; - } - - /* Look for errors from the boot ROM */ -@@ -540,18 +527,11 @@ static int wm0010_stage2_load(struct snd_soc_component *component) - dev_err(component->dev, "Boot ROM error: %x in %d\n", - out[i], i); - wm0010_mark_boot_failure(wm0010); -- ret = -EBUSY; -- goto abort; -+ return -EBUSY; - } - } --abort: -- kfree(out); --abort1: -- kfree(img); --abort2: -- release_firmware(fw); - -- return ret; -+ return 0; - } - - static int wm0010_boot(struct snd_soc_component *component) -@@ -734,9 +714,8 @@ static int wm0010_set_bias_level(struct snd_soc_component *component, - break; - case SND_SOC_BIAS_STANDBY: - if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_PREPARE) { -- mutex_lock(&wm0010->lock); -- wm0010_halt(component); -- mutex_unlock(&wm0010->lock); -+ scoped_guard(mutex, &wm0010->lock) -+ wm0010_halt(component); - } - break; - case SND_SOC_BIAS_OFF: -@@ -832,9 +811,8 @@ static irqreturn_t wm0010_irq(int irq, void *data) - case WM0010_OUT_OF_RESET: - case WM0010_BOOTROM: - case WM0010_STAGE2: -- spin_lock(&wm0010->irq_lock); -- complete(&wm0010->boot_completion); -- spin_unlock(&wm0010->irq_lock); -+ scoped_guard(spinlock, &wm0010->irq_lock) -+ complete(&wm0010->boot_completion); - return IRQ_HANDLED; - default: - return IRQ_NONE; -diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c ---- a/sound/soc/codecs/wm2000.c -+++ b/sound/soc/codecs/wm2000.c -@@ -23,6 +23,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -612,20 +613,15 @@ static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); - unsigned int anc_active = ucontrol->value.integer.value[0]; -- int ret; - - if (anc_active > 1) - return -EINVAL; - -- mutex_lock(&wm2000->lock); -+ guard(mutex)(&wm2000->lock); - - wm2000->anc_active = anc_active; - -- ret = wm2000_anc_set_mode(wm2000); -- -- mutex_unlock(&wm2000->lock); -- -- return ret; -+ return wm2000_anc_set_mode(wm2000); - } - - static int wm2000_speaker_get(struct snd_kcontrol *kcontrol, -@@ -645,20 +641,15 @@ static int wm2000_speaker_put(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); - unsigned int val = ucontrol->value.integer.value[0]; -- int ret; - - if (val > 1) - return -EINVAL; - -- mutex_lock(&wm2000->lock); -+ guard(mutex)(&wm2000->lock); - - wm2000->spk_ena = val; - -- ret = wm2000_anc_set_mode(wm2000); -- -- mutex_unlock(&wm2000->lock); -- -- return ret; -+ return wm2000_anc_set_mode(wm2000); - } - - static const struct snd_kcontrol_new wm2000_controls[] = { -@@ -676,9 +667,8 @@ static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w, - { - struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); - struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); -- int ret; - -- mutex_lock(&wm2000->lock); -+ guard(mutex)(&wm2000->lock); - - if (SND_SOC_DAPM_EVENT_ON(event)) - wm2000->anc_eng_ena = 1; -@@ -686,11 +676,7 @@ static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w, - if (SND_SOC_DAPM_EVENT_OFF(event)) - wm2000->anc_eng_ena = 0; - -- ret = wm2000_anc_set_mode(wm2000); -- -- mutex_unlock(&wm2000->lock); -- -- return ret; -+ return wm2000_anc_set_mode(wm2000); - } - - static const struct snd_soc_dapm_widget wm2000_dapm_widgets[] = { -@@ -810,7 +796,7 @@ static int wm2000_i2c_probe(struct i2c_client *i2c) - struct wm2000_priv *wm2000; - struct wm2000_platform_data *pdata; - const char *filename; -- const struct firmware *fw = NULL; -+ const struct firmware *fw __free(firmware) = NULL; - int ret, i; - unsigned int reg; - u16 id; -@@ -828,7 +814,7 @@ static int wm2000_i2c_probe(struct i2c_client *i2c) - ret = PTR_ERR(wm2000->regmap); - dev_err(&i2c->dev, "Failed to allocate register map: %d\n", - ret); -- goto out; -+ return ret; - } - - for (i = 0; i < WM2000_NUM_SUPPLIES; i++) -@@ -922,9 +908,6 @@ static int wm2000_i2c_probe(struct i2c_client *i2c) - - err_supplies: - regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); -- --out: -- release_firmware(fw); - return ret; - } - -diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c ---- a/sound/soc/codecs/wm2200.c -+++ b/sound/soc/codecs/wm2200.c -@@ -2461,7 +2461,15 @@ static int wm2200_runtime_resume(struct device *dev) - } - - regcache_cache_only(wm2200->regmap, false); -- regcache_sync(wm2200->regmap); -+ ret = regcache_sync(wm2200->regmap); -+ if (ret) { -+ regcache_cache_only(wm2200->regmap, true); -+ regcache_mark_dirty(wm2200->regmap); -+ gpiod_set_value_cansleep(wm2200->ldo_ena, 0); -+ regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies), -+ wm2200->core_supplies); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c ---- a/sound/soc/codecs/wm5100.c -+++ b/sound/soc/codecs/wm5100.c -@@ -2659,7 +2659,15 @@ static int wm5100_runtime_resume(struct device *dev) - } - - regcache_cache_only(wm5100->regmap, false); -- regcache_sync(wm5100->regmap); -+ ret = regcache_sync(wm5100->regmap); -+ if (ret) { -+ regcache_cache_only(wm5100->regmap, true); -+ regcache_mark_dirty(wm5100->regmap); -+ gpiod_set_value_cansleep(wm5100->ldo_ena, 0); -+ regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies), -+ wm5100->core_supplies); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c ---- a/sound/soc/codecs/wm5102.c -+++ b/sound/soc/codecs/wm5102.c -@@ -7,6 +7,7 @@ - * Author: Mark Brown - */ - -+#include - #include - #include - #include -@@ -667,10 +668,9 @@ static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct arizona *arizona = dev_get_drvdata(component->dev->parent); - -- mutex_lock(&arizona->dac_comp_lock); -+ guard(mutex)(&arizona->dac_comp_lock); - put_unaligned_be16(arizona->dac_comp_coeff, - ucontrol->value.bytes.data); -- mutex_unlock(&arizona->dac_comp_lock); - - return 0; - } -@@ -681,16 +681,14 @@ static int wm5102_out_comp_coeff_put(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct arizona *arizona = dev_get_drvdata(component->dev->parent); - uint16_t dac_comp_coeff = get_unaligned_be16(ucontrol->value.bytes.data); -- int ret = 0; - -- mutex_lock(&arizona->dac_comp_lock); -+ guard(mutex)(&arizona->dac_comp_lock); - if (arizona->dac_comp_coeff != dac_comp_coeff) { - arizona->dac_comp_coeff = dac_comp_coeff; -- ret = 1; -+ return 1; - } -- mutex_unlock(&arizona->dac_comp_lock); - -- return ret; -+ return 0; - } - - static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol, -@@ -699,9 +697,8 @@ static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct arizona *arizona = dev_get_drvdata(component->dev->parent); - -- mutex_lock(&arizona->dac_comp_lock); -+ guard(mutex)(&arizona->dac_comp_lock); - ucontrol->value.integer.value[0] = arizona->dac_comp_enabled; -- mutex_unlock(&arizona->dac_comp_lock); - - return 0; - } -@@ -712,19 +709,17 @@ static int wm5102_out_comp_switch_put(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct arizona *arizona = dev_get_drvdata(component->dev->parent); - struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; -- int ret = 0; - - if (ucontrol->value.integer.value[0] > mc->max) - return -EINVAL; - -- mutex_lock(&arizona->dac_comp_lock); -+ guard(mutex)(&arizona->dac_comp_lock); - if (arizona->dac_comp_enabled != ucontrol->value.integer.value[0]) { - arizona->dac_comp_enabled = ucontrol->value.integer.value[0]; -- ret = 1; -+ return 1; - } -- mutex_unlock(&arizona->dac_comp_lock); - -- return ret; -+ return 0; - } - - static const char * const wm5102_osr_text[] = { -diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c ---- a/sound/soc/codecs/wm8731.c -+++ b/sound/soc/codecs/wm8731.c -@@ -10,6 +10,7 @@ - * Based on wm8753.c by Liam Girdwood - */ - -+#include - #include - #include - #include -@@ -110,22 +111,20 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component); - unsigned int deemph = ucontrol->value.integer.value[0]; -- int ret = 0; - - if (deemph > 1) - return -EINVAL; - -- mutex_lock(&wm8731->lock); -+ guard(mutex)(&wm8731->lock); - if (wm8731->deemph != deemph) { - wm8731->deemph = deemph; - - wm8731_set_deemph(component); - -- ret = 1; -+ return 1; - } -- mutex_unlock(&wm8731->lock); - -- return ret; -+ return 0; - } - - static const DECLARE_TLV_DB_SCALE(in_tlv, -3450, 150, 0); -diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c ---- a/sound/soc/codecs/wm8903.c -+++ b/sound/soc/codecs/wm8903.c -@@ -14,6 +14,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -458,22 +459,20 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component); - unsigned int deemph = ucontrol->value.integer.value[0]; -- int ret = 0; - - if (deemph > 1) - return -EINVAL; - -- mutex_lock(&wm8903->lock); -+ guard(mutex)(&wm8903->lock); - if (wm8903->deemph != deemph) { - wm8903->deemph = deemph; - - wm8903_set_deemph(component); - -- ret = 1; -+ return 1; - } -- mutex_unlock(&wm8903->lock); - -- return ret; -+ return 0; - } - - /* ALSA can only do steps of .01dB */ -diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c ---- a/sound/soc/codecs/wm8940.c -+++ b/sound/soc/codecs/wm8940.c -@@ -333,7 +333,7 @@ static const struct snd_soc_dapm_route wm8940_dapm_routes[] = { - {"ADC", NULL, "Boost Mixer"}, - }; - --#define wm8940_reset(c) snd_soc_component_write(c, WM8940_SOFTRESET, 0); -+#define wm8940_reset(c) snd_soc_component_write(c, WM8940_SOFTRESET, 0) - - static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai, - unsigned int fmt) -diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c ---- a/sound/soc/codecs/wm8958-dsp2.c -+++ b/sound/soc/codecs/wm8958-dsp2.c -@@ -7,6 +7,7 @@ - * Author: Mark Brown - */ - -+#include - #include - #include - #include -@@ -864,9 +865,8 @@ static void wm8958_enh_eq_loaded(const struct firmware *fw, void *context) - struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); - - if (fw && (wm8958_dsp2_fw(component, "ENH_EQ", fw, true) == 0)) { -- mutex_lock(&wm8994->fw_lock); -+ guard(mutex)(&wm8994->fw_lock); - wm8994->enh_eq = fw; -- mutex_unlock(&wm8994->fw_lock); - } - } - -@@ -876,9 +876,8 @@ static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context) - struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); - - if (fw && (wm8958_dsp2_fw(component, "MBC+VSS", fw, true) == 0)) { -- mutex_lock(&wm8994->fw_lock); -+ guard(mutex)(&wm8994->fw_lock); - wm8994->mbc_vss = fw; -- mutex_unlock(&wm8994->fw_lock); - } - } - -@@ -888,9 +887,8 @@ static void wm8958_mbc_loaded(const struct firmware *fw, void *context) - struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); - - if (fw && (wm8958_dsp2_fw(component, "MBC", fw, true) == 0)) { -- mutex_lock(&wm8994->fw_lock); -+ guard(mutex)(&wm8994->fw_lock); - wm8994->mbc = fw; -- mutex_unlock(&wm8994->fw_lock); - } - } - -diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c ---- a/sound/soc/codecs/wm8962.c -+++ b/sound/soc/codecs/wm8962.c -@@ -10,6 +10,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -1564,11 +1565,10 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); - int old = wm8962->dsp2_ena; -- int ret = 0; - int dsp2_running = snd_soc_component_read(component, WM8962_DSP2_POWER_MANAGEMENT) & - WM8962_DSP2_ENA; - -- mutex_lock(&wm8962->dsp2_ena_lock); -+ guard(mutex)(&wm8962->dsp2_ena_lock); - - if (ucontrol->value.integer.value[0]) - wm8962->dsp2_ena |= 1 << shift; -@@ -1576,9 +1576,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol, - wm8962->dsp2_ena &= ~(1 << shift); - - if (wm8962->dsp2_ena == old) -- goto out; -- -- ret = 1; -+ return 0; - - if (dsp2_running) { - if (wm8962->dsp2_ena) -@@ -1587,10 +1585,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol, - wm8962_dsp2_stop(component); - } - --out: -- mutex_unlock(&wm8962->dsp2_ena_lock); -- -- return ret; -+ return 1; - } - - /* The VU bits for the headphones are in a different register to the mute -@@ -3937,7 +3932,9 @@ static int wm8962_runtime_resume(struct device *dev) - WM8962_OSC_ENA | WM8962_PLL2_ENA | WM8962_PLL3_ENA, - 0); - -- regcache_sync(wm8962->regmap); -+ ret = regcache_sync(wm8962->regmap); -+ if (ret) -+ goto cache_sync_err; - - regmap_update_bits(wm8962->regmap, WM8962_ANTI_POP, - WM8962_STARTUP_BIAS_ENA | WM8962_VMID_BUF_ENA, -@@ -3955,6 +3952,11 @@ static int wm8962_runtime_resume(struct device *dev) - - return 0; - -+cache_sync_err: -+ regcache_cache_only(wm8962->regmap, true); -+ regcache_mark_dirty(wm8962->regmap); -+ regulator_bulk_disable(ARRAY_SIZE(wm8962->supplies), -+ wm8962->supplies); - disable_clock: - clk_disable_unprepare(wm8962->pdata.mclk); - return ret; -diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c ---- a/sound/soc/codecs/wm8994.c -+++ b/sound/soc/codecs/wm8994.c -@@ -7,6 +7,7 @@ - * Author: Mark Brown - */ - -+#include - #include - #include - #include -@@ -766,7 +767,7 @@ static void active_reference(struct snd_soc_component *component) - { - struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); - -- mutex_lock(&wm8994->accdet_lock); -+ guard(mutex)(&wm8994->accdet_lock); - - wm8994->active_refcount++; - -@@ -775,8 +776,6 @@ static void active_reference(struct snd_soc_component *component) - - /* If we're using jack detection go into audio mode */ - wm1811_jackdet_set_mode(component, WM1811_JACKDET_MODE_AUDIO); -- -- mutex_unlock(&wm8994->accdet_lock); - } - - static void active_dereference(struct snd_soc_component *component) -@@ -784,7 +783,7 @@ static void active_dereference(struct snd_soc_component *component) - struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); - u16 mode; - -- mutex_lock(&wm8994->accdet_lock); -+ guard(mutex)(&wm8994->accdet_lock); - - wm8994->active_refcount--; - -@@ -800,8 +799,6 @@ static void active_dereference(struct snd_soc_component *component) - - wm1811_jackdet_set_mode(component, mode); - } -- -- mutex_unlock(&wm8994->accdet_lock); - } - - static int clk_sys_event(struct snd_soc_dapm_widget *w, -@@ -3704,7 +3701,7 @@ static void wm8958_open_circuit_work(struct work_struct *work) - open_circuit_work.work); - struct device *dev = wm8994->wm8994->dev; - -- mutex_lock(&wm8994->accdet_lock); -+ guard(mutex)(&wm8994->accdet_lock); - - wm1811_micd_stop(wm8994->hubs.component); - -@@ -3718,8 +3715,6 @@ static void wm8958_open_circuit_work(struct work_struct *work) - snd_soc_jack_report(wm8994->micdet[0].jack, 0, - wm8994->btn_mask | - SND_JACK_HEADSET); -- -- mutex_unlock(&wm8994->accdet_lock); - } - - static void wm8958_mic_id(void *data, u16 status) -@@ -3777,7 +3772,7 @@ static void wm1811_mic_work(struct work_struct *work) - struct snd_soc_component *component = wm8994->hubs.component; - struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); - -- pm_runtime_get_sync(component->dev); -+ guard(pm_runtime_active)(component->dev); - - /* If required for an external cap force MICBIAS on */ - if (control->pdata.jd_ext_cap) { -@@ -3785,7 +3780,7 @@ static void wm1811_mic_work(struct work_struct *work) - snd_soc_dapm_sync(dapm); - } - -- mutex_lock(&wm8994->accdet_lock); -+ guard(mutex)(&wm8994->accdet_lock); - - dev_dbg(component->dev, "Starting mic detection\n"); - -@@ -3803,10 +3798,6 @@ static void wm1811_mic_work(struct work_struct *work) - snd_soc_component_update_bits(component, WM8958_MIC_DETECT_1, - WM8958_MICD_ENA, WM8958_MICD_ENA); - } -- -- mutex_unlock(&wm8994->accdet_lock); -- -- pm_runtime_put(component->dev); - } - - static irqreturn_t wm1811_jackdet_irq(int irq, void *data) -@@ -4026,15 +4017,10 @@ static void wm8958_mic_work(struct work_struct *work) - mic_complete_work.work); - struct snd_soc_component *component = wm8994->hubs.component; - -- pm_runtime_get_sync(component->dev); -- -- mutex_lock(&wm8994->accdet_lock); -+ guard(pm_runtime_active)(component->dev); -+ guard(mutex)(&wm8994->accdet_lock); - - wm8994->mic_id_cb(wm8994->mic_id_cb_data, wm8994->mic_status); -- -- mutex_unlock(&wm8994->accdet_lock); -- -- pm_runtime_put(component->dev); - } - - static irqreturn_t wm8958_mic_irq(int irq, void *data) -diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c ---- a/sound/soc/codecs/wm9712.c -+++ b/sound/soc/codecs/wm9712.c -@@ -6,6 +6,7 @@ - * Author: Liam Girdwood - */ - -+#include - #include - #include - #include -@@ -229,7 +230,7 @@ static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol, - shift = mc->shift & 0xff; - mask = 1 << shift; - -- mutex_lock(&wm9712->lock); -+ guard(mutex)(&wm9712->lock); - old = wm9712->hp_mixer[mixer]; - if (ucontrol->value.integer.value[0]) - wm9712->hp_mixer[mixer] |= mask; -@@ -251,8 +252,6 @@ static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol, - &update); - } - -- mutex_unlock(&wm9712->lock); -- - return change; - } - -diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c ---- a/sound/soc/codecs/wm9713.c -+++ b/sound/soc/codecs/wm9713.c -@@ -11,6 +11,7 @@ - * o Support for DAPM - */ - -+#include - #include - #include - #include -@@ -238,7 +239,7 @@ static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol, - shift = mc->shift & 0xff; - mask = (1 << shift); - -- mutex_lock(&wm9713->lock); -+ guard(mutex)(&wm9713->lock); - old = wm9713->hp_mixer[mixer]; - if (ucontrol->value.integer.value[0]) - wm9713->hp_mixer[mixer] |= mask; -@@ -260,8 +261,6 @@ static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol, - &update); - } - -- mutex_unlock(&wm9713->lock); -- - return change; - } - -diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c ---- a/sound/soc/codecs/wm_adsp.c -+++ b/sound/soc/codecs/wm_adsp.c -@@ -348,7 +348,6 @@ int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); -- int ret = 1; - - if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw) - return 0; -@@ -356,16 +355,14 @@ int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, - if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW) - return -EINVAL; - -- mutex_lock(&dsp[e->shift_l].cs_dsp.pwr_lock); -+ guard(mutex)(&dsp[e->shift_l].cs_dsp.pwr_lock); - - if (dsp[e->shift_l].cs_dsp.booted || !list_empty(&dsp[e->shift_l].compr_list)) -- ret = -EBUSY; -+ return -EBUSY; - else - dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; - -- mutex_unlock(&dsp[e->shift_l].cs_dsp.pwr_lock); -- -- return ret; -+ return 1; - } - EXPORT_SYMBOL_GPL(wm_adsp_fw_put); - -@@ -450,15 +447,13 @@ static int wm_coeff_put_acked(struct snd_kcontrol *kctl, - if (val == 0) - return 0; /* 0 means no event */ - -- mutex_lock(&cs_ctl->dsp->pwr_lock); -+ guard(mutex)(&cs_ctl->dsp->pwr_lock); - - if (cs_ctl->enabled) - ret = cs_dsp_coeff_write_acked_control(cs_ctl, val); - else - ret = -EPERM; - -- mutex_unlock(&cs_ctl->dsp->pwr_lock); -- - if (ret < 0) - return ret; - -@@ -486,15 +481,13 @@ static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, - struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; - int ret = 0; - -- mutex_lock(&cs_ctl->dsp->pwr_lock); -+ guard(mutex)(&cs_ctl->dsp->pwr_lock); - - ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, cs_ctl->cache, size); - - if (!ret && copy_to_user(bytes, cs_ctl->cache, size)) - ret = -EFAULT; - -- mutex_unlock(&cs_ctl->dsp->pwr_lock); -- - return ret; - } - -@@ -694,10 +687,9 @@ int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, - struct cs_dsp_coeff_ctl *cs_ctl; - int ret; - -- mutex_lock(&dsp->cs_dsp.pwr_lock); -+ guard(mutex)(&dsp->cs_dsp.pwr_lock); - cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg); - ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len); -- mutex_unlock(&dsp->cs_dsp.pwr_lock); - - if (ret < 0) - return ret; -@@ -709,14 +701,10 @@ EXPORT_SYMBOL_GPL(wm_adsp_write_ctl); - int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type, - unsigned int alg, void *buf, size_t len) - { -- int ret; -+ guard(mutex)(&dsp->cs_dsp.pwr_lock); - -- mutex_lock(&dsp->cs_dsp.pwr_lock); -- ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg), -+ return cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg), - 0, buf, len); -- mutex_unlock(&dsp->cs_dsp.pwr_lock); -- -- return ret; - } - EXPORT_SYMBOL_GPL(wm_adsp_read_ctl); - -@@ -1270,38 +1258,32 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) - { - struct wm_adsp_compr *compr, *tmp; - struct snd_soc_pcm_runtime *rtd = stream->private_data; -- int ret = 0; - -- mutex_lock(&dsp->cs_dsp.pwr_lock); -+ guard(mutex)(&dsp->cs_dsp.pwr_lock); - - if (wm_adsp_fw[dsp->fw].num_caps == 0) { - adsp_err(dsp, "%s: Firmware does not support compressed API\n", - snd_soc_rtd_to_codec(rtd, 0)->name); -- ret = -ENXIO; -- goto out; -+ return -ENXIO; - } - - if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { - adsp_err(dsp, "%s: Firmware does not support stream direction\n", - snd_soc_rtd_to_codec(rtd, 0)->name); -- ret = -EINVAL; -- goto out; -+ return -EINVAL; - } - - list_for_each_entry(tmp, &dsp->compr_list, list) { - if (!strcmp(tmp->name, snd_soc_rtd_to_codec(rtd, 0)->name)) { - adsp_err(dsp, "%s: Only a single stream supported per dai\n", - snd_soc_rtd_to_codec(rtd, 0)->name); -- ret = -EBUSY; -- goto out; -+ return -EBUSY; - } - } - - compr = kzalloc_obj(*compr); -- if (!compr) { -- ret = -ENOMEM; -- goto out; -- } -+ if (!compr) -+ return -ENOMEM; - - compr->dsp = dsp; - compr->stream = stream; -@@ -1311,10 +1293,7 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) - - stream->runtime->private_data = compr; - --out: -- mutex_unlock(&dsp->cs_dsp.pwr_lock); -- -- return ret; -+ return 0; - } - EXPORT_SYMBOL_GPL(wm_adsp_compr_open); - -@@ -1324,7 +1303,7 @@ int wm_adsp_compr_free(struct snd_soc_component *component, - struct wm_adsp_compr *compr = stream->runtime->private_data; - struct wm_adsp *dsp = compr->dsp; - -- mutex_lock(&dsp->cs_dsp.pwr_lock); -+ guard(mutex)(&dsp->cs_dsp.pwr_lock); - - wm_adsp_compr_detach(compr); - list_del(&compr->list); -@@ -1332,8 +1311,6 @@ int wm_adsp_compr_free(struct snd_soc_component *component, - kfree(compr->raw_buf); - kfree(compr); - -- mutex_unlock(&dsp->cs_dsp.pwr_lock); -- - return 0; - } - EXPORT_SYMBOL_GPL(wm_adsp_compr_free); -@@ -1741,7 +1718,7 @@ int wm_adsp_compr_trigger(struct snd_soc_component *component, - - compr_dbg(compr, "Trigger: %d\n", cmd); - -- mutex_lock(&dsp->cs_dsp.pwr_lock); -+ guard(mutex)(&dsp->cs_dsp.pwr_lock); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: -@@ -1777,8 +1754,6 @@ int wm_adsp_compr_trigger(struct snd_soc_component *component, - break; - } - -- mutex_unlock(&dsp->cs_dsp.pwr_lock); -- - return ret; - } - EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger); -@@ -1907,21 +1882,20 @@ int wm_adsp_compr_pointer(struct snd_soc_component *component, - - compr_dbg(compr, "Pointer request\n"); - -- mutex_lock(&dsp->cs_dsp.pwr_lock); -+ guard(mutex)(&dsp->cs_dsp.pwr_lock); - - buf = compr->buf; - - if (dsp->fatal_error || !buf || buf->error) { - snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN); -- ret = -EIO; -- goto out; -+ return -EIO; - } - - if (buf->avail < wm_adsp_compr_frag_words(compr)) { - ret = wm_adsp_buffer_update_avail(buf); - if (ret < 0) { - compr_err(compr, "Error reading avail: %d\n", ret); -- goto out; -+ return ret; - } - - /* -@@ -1934,14 +1908,14 @@ int wm_adsp_compr_pointer(struct snd_soc_component *component, - if (buf->error) - snd_compr_stop_error(stream, - SNDRV_PCM_STATE_XRUN); -- goto out; -+ return ret; - } - - ret = wm_adsp_buffer_reenable_irq(buf); - if (ret < 0) { - compr_err(compr, "Failed to re-enable buffer IRQ: %d\n", - ret); -- goto out; -+ return ret; - } - } - } -@@ -1950,9 +1924,6 @@ int wm_adsp_compr_pointer(struct snd_soc_component *component, - tstamp->copied_total += buf->avail * CS_DSP_DATA_WORD_SIZE; - tstamp->sampling_rate = compr->sample_rate; - --out: -- mutex_unlock(&dsp->cs_dsp.pwr_lock); -- - return ret; - } - EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer); -@@ -2063,15 +2034,13 @@ int wm_adsp_compr_copy(struct snd_soc_component *component, - struct wm_adsp *dsp = compr->dsp; - int ret; - -- mutex_lock(&dsp->cs_dsp.pwr_lock); -+ guard(mutex)(&dsp->cs_dsp.pwr_lock); - - if (stream->direction == SND_COMPRESS_CAPTURE) - ret = wm_adsp_compr_read(compr, buf, count); - else - ret = -ENOTSUPP; - -- mutex_unlock(&dsp->cs_dsp.pwr_lock); -- - return ret; - } - EXPORT_SYMBOL_GPL(wm_adsp_compr_copy); -diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c ---- a/sound/soc/codecs/wsa881x.c -+++ b/sound/soc/codecs/wsa881x.c -@@ -1178,7 +1178,13 @@ static int wsa881x_runtime_resume(struct device *dev) - } - - regcache_cache_only(regmap, false); -- regcache_sync(regmap); -+ ret = regcache_sync(regmap); -+ if (ret) { -+ regcache_cache_only(regmap, true); -+ regcache_mark_dirty(regmap); -+ gpiod_direction_output(wsa881x->sd_n, 1); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c ---- a/sound/soc/codecs/wsa883x.c -+++ b/sound/soc/codecs/wsa883x.c -@@ -4,6 +4,7 @@ - */ - - #include -+#include - #include - #include - #include -@@ -1237,9 +1238,8 @@ static int wsa883x_spkr_event(struct snd_soc_dapm_widget *w, - - switch (event) { - case SND_SOC_DAPM_POST_PMU: -- mutex_lock(&wsa883x->sp_lock); -- wsa883x->pa_on = true; -- mutex_unlock(&wsa883x->sp_lock); -+ scoped_guard(mutex, &wsa883x->sp_lock) -+ wsa883x->pa_on = true; - - switch (wsa883x->dev_mode) { - case RECEIVER: -@@ -1290,9 +1290,8 @@ static int wsa883x_spkr_event(struct snd_soc_dapm_widget *w, - WSA883X_GLOBAL_PA_EN_MASK, 0); - snd_soc_component_write_field(component, WSA883X_PDM_WD_CTL, - WSA883X_PDM_EN_MASK, 0); -- mutex_lock(&wsa883x->sp_lock); -- wsa883x->pa_on = false; -- mutex_unlock(&wsa883x->sp_lock); -+ scoped_guard(mutex, &wsa883x->sp_lock) -+ wsa883x->pa_on = false; - break; - } - return 0; -@@ -1696,9 +1695,15 @@ static int wsa883x_runtime_suspend(struct device *dev) - static int wsa883x_runtime_resume(struct device *dev) - { - struct regmap *regmap = dev_get_regmap(dev, NULL); -+ int ret; - - regcache_cache_only(regmap, false); -- regcache_sync(regmap); -+ ret = regcache_sync(regmap); -+ if (ret) { -+ regcache_cache_only(regmap, true); -+ regcache_mark_dirty(regmap); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c ---- a/sound/soc/codecs/wsa884x.c -+++ b/sound/soc/codecs/wsa884x.c -@@ -1701,9 +1701,8 @@ static int wsa884x_spkr_event(struct snd_soc_dapm_widget *w, - - switch (event) { - case SND_SOC_DAPM_POST_PMU: -- mutex_lock(&wsa884x->sp_lock); -- wsa884x->pa_on = true; -- mutex_unlock(&wsa884x->sp_lock); -+ scoped_guard(mutex, &wsa884x->sp_lock) -+ wsa884x->pa_on = true; - - wsa884x_spkr_post_pmu(component, wsa884x); - -@@ -1717,9 +1716,8 @@ static int wsa884x_spkr_event(struct snd_soc_dapm_widget *w, - WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK, - 0x0); - -- mutex_lock(&wsa884x->sp_lock); -- wsa884x->pa_on = false; -- mutex_unlock(&wsa884x->sp_lock); -+ scoped_guard(mutex, &wsa884x->sp_lock) -+ wsa884x->pa_on = false; - break; - } - -@@ -2147,9 +2145,15 @@ static int wsa884x_runtime_suspend(struct device *dev) - static int wsa884x_runtime_resume(struct device *dev) - { - struct regmap *regmap = dev_get_regmap(dev, NULL); -+ int ret; - - regcache_cache_only(regmap, false); -- regcache_sync(regmap); -+ ret = regcache_sync(regmap); -+ if (ret) { -+ regcache_cache_only(regmap, true); -+ regcache_mark_dirty(regmap); -+ return ret; -+ } - - return 0; - } -diff --git a/sound/soc/codecs/wsa885x.c b/sound/soc/codecs/wsa885x.c -new file mode 100644 ---- /dev/null -+++ b/sound/soc/codecs/wsa885x.c -@@ -0,0 +1,1533 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. -+ */ -+ -+/* WSA885X codec driver */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* Control Registers - Audio Processing */ -+#define WSA885X_SMP_AMP_CTRL_STEREO_STEREO_SMP_AMP_CTRL_I2S 0x0000 -+#define WSA885X_SMP_AMP_CTRL_STEREO_CMT_GRP_MASK 0x0004 -+#define WSA885X_SMP_AMP_CTRL_STEREO_IT21_CLUSERINDEX 0x0140 -+#define WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID 0x0208 -+#define WSA885X_SMP_AMP_CTRL_STEREO_CS21_SAMPLERATEINDEX 0x0240 -+#define WSA885X_SMP_AMP_CTRL_STEREO_PPU21_POSTURENUMBER 0x0340 -+#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0 0x4405 -+#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1 0x4406 -+#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB 0x4409 -+#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_MSB 0x6409 -+#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB 0x440a -+#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_MSB 0x640a -+#define WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS 0x0a04 -+#define WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS 0x0a40 -+#define WSA885X_SMP_AMP_CTRL_STEREO_OT23_USAGE 0x0b10 -+#define WSA885X_SMP_AMP_CTRL_STEREO_CS24_SAMPLERATEINDEX 0x0e40 -+ -+/* Analog Top Registers - Power and Clock Control */ -+#define WSA885X_ANA_TOP_PON_CKSK_CTL_0 0x800d -+#define WSA885X_ANA_TOP_BG_TVP_UVLO1_PROG 0x8024 -+#define WSA885X_ANA_TOP_BG_TVP_UVLO2_PROG 0x8025 -+#define WSA885X_ANA_TOP_BG_TVP_OVRD_CTL 0x8034 -+ -+/* Analog PLL Registers */ -+#define WSA885X_ANA_PLL_DIV_CTL_0 0x8090 -+#define WSA885X_ANA_PLL_DIV_CTL_1 0x8091 -+#define WSA885X_ANA_TOP_PLL_VCO_CTL 0x8092 -+#define WSA885X_ANA_TOP_PLL_LOOPFILT_0 0x8093 -+#define WSA885X_ANA_TOP_PLL_OVRD_CTL 0x8098 -+#define WSA885X_ANA_TOP_PLL_STATUS_0 0x809a -+#define WSA885X_ANA_TOP_PLL_STATUS_1 0x809b -+ -+/* Analog Boost Control Registers */ -+#define WSA885X_ANA_TOP_BOOST_STB_CTRL2 0x805b -+#define WSA885X_ANA_TOP_BOOST_STB_CTRL3 0x805c -+#define WSA885X_ANA_TOP_BOOST_BYP_CTRL2 0x805e -+#define WSA885X_ANA_TOP_BOOST_BYP_CTRL3 0x805f -+#define WSA885X_ANA_TOP_BOOST_MISC 0x8063 -+#define WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL2 0x8065 -+#define WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL4 0x8067 -+ -+/* Analog IV Sense ADC Registers */ -+#define WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL2 0x80ca -+#define WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3 0x80cb -+#define WSA885X_ANA_TOP_IVSENSE_ADC_REF_CTL 0x80cc -+#define WSA885X_ANA_TOP_IVSENSE_ADC_CDAC_CAL_CTL2 0x80d0 -+ -+/* Analog Speaker Power Stage Registers */ -+#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_CTRL3 0x8108 -+#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_TUNE3 0x810b -+#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_CTRL3 0x810e -+#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_TUNE3 0x8111 -+#define WSA885X_ANA_TOP_SPK_TOP_SPARE3 0x813c -+#define WSA885X_SPK_TOP_LF_CH1_CTRL11 0x811c -+#define WSA885X_SPK_TOP_LF_CH1_TUNE1 0x811d -+#define WSA885X_SPK_TOP_LF_CH2_TUNE1 0x8129 -+#define WSA885X_SPK_TOP_LF_CH1_CTRL9 0x811a -+#define WSA885X_SPK_TOP_LF_CH2_CTRL9 0x8126 -+#define WSA885X_SPK_TOP_LF_CH2_CTRL11 0x8128 -+#define WSA885X_SPK_TOP_COMMON_CTRL2 0x8102 -+#define WSA885X_SPK_TOP_COMMON_TUNE1 0x8103 -+#define WSA885X_IVSENSE_VSNS_ISNS_CTL_CH1 0x80ba -+#define WSA885X_DIG_CTRL0_TOP_CLK_CFG 0x8418 -+#define WSA885X_DIG_CTRL0_SDCA_COMMIT 0x8419 -+#define WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE 0x841a -+#define WSA885X_DIG_CTRL0_SYS_CLK_SEL 0x841b -+#define WSA885X_DIG_CTRL0_CDC_CLK_CTL 0x841c -+#define WSA885X_DIG_CTRL0_PA_FSM_CTL 0x8420 -+#define WSA885X_DIG_CTRL0_POWER_FSM_CTL0 0x8423 -+#define WSA885X_DIG_CTRL0_POWER_FSM_CTL1 0x8424 -+#define WSA885X_DIG_CTRL0_PA0_FSM_CTL1 0x842b -+#define WSA885X_DIG_CTRL0_PA1_FSM_CTL1 0x8435 -+#define WSA885X_DIG_CTRL0_VBAT_THRM_FLT_CTL 0x8458 -+#define WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL 0x8470 -+#define WSA885X_DIG_CTRL0_GAIN_RAMP0_CTL1 0x84b4 -+#define WSA885X_DIG_CTRL0_GAIN_RAMP1_CTL1 0x84b7 -+#define WSA885X_DIG_CTRL0_PCM_DATA_WD0_CTL1 0x84A0 -+#define WSA885X_DIG_CTRL0_PCM_DATA_WD1_CTL1 0x84A4 -+ -+/* Digital Control 1 Registers - I2S/TDM Interface */ -+#define WSA885X_DIG_CTRL1_I2S_CTL0 0x85A0 -+#define WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX 0x85A2 -+#define WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX 0x85A3 -+#define WSA885X_DIG_CTRL1_I2S_TDM_CTL0 0x85A7 -+#define WSA885X_DIG_CTRL1_I2S_TDM_CTL1 0x85A9 -+#define WSA885X_DIG_CTRL1_I2S_TDM_CH_RX 0x85AA -+#define WSA885X_DIG_CTRL1_I2S_TDM_CH_TX 0x85AB -+#define WSA885X_DIG_CTRL1_I2S_RESET_CTL 0x85AE -+ -+/* CDC RX Path Registers - Audio Data Path */ -+#define WSA885X_CDC_RX0_RX_PATH_CFG0 0x8601 -+#define WSA885X_CDC_RX0_RX_PATH_CFG1 0x8602 -+#define WSA885X_CDC_RX0_RX_PATH_CTL 0x8606 -+#define WSA885X_RX0_RX_PATH_DSMDEM_CTL 0x8613 -+#define WSA885X_CDC_RX1_RX_PATH_CFG0 0x8621 -+#define WSA885X_CDC_RX1_RX_PATH_CFG1 0x8622 -+#define WSA885X_CDC_RX1_RX_PATH_CTL 0x8626 -+#define WSA885X_RX1_RX_PATH_DSMDEM_CTL 0x8633 -+ -+/* CDC Compander Registers - Dynamic Range Control */ -+#define WSA885X_CDC_COMPANDER0_CTL0 0x8640 -+#define WSA885X_CDC_COMPANDER0_CTL7 0x8647 -+#define WSA885X_CDC_COMPANDER1_CTL0 0x8660 -+#define WSA885X_CDC_COMPANDER1_CTL7 0x8667 -+ -+/* CDC Speaker Protection Registers - IV Sense */ -+#define WSA885X_CDC_VSENSE0_SPKR_PROT_PATH_CTL 0x86A1 -+#define WSA885X_CDC_VSENSE1_SPKR_PROT_PATH_CTL 0x86B1 -+#define WSA885X_CDC_ISENSE0_SPKR_PROT_PATH_CTL 0x86A9 -+#define WSA885X_CDC_ISENSE1_SPKR_PROT_PATH_CTL 0x86B9 -+ -+/* CDC Class-H Registers - Headroom Control */ -+#define WSA885X_CDC_CLSH_V1P8_BP_CTL1 0x86CD -+#define WSA885X_CDC_CLSH_V1P8_BP_CTL0 0x86CC -+#define WSA885X_CDC_CLSH_CLSH_SIG_DP_CTL0 0x86C7 -+#define WSA885X_CDC_CLSH_CLSH_V_HD_PA 0x86C3 -+#define WSA885X_CDC_CLSH_V1P8_BP_CTL2 0x86CE -+ -+/* Driver Constants */ -+#define WSA885X_CLK_RATE_FIXED 73728000 -+#define WSA885X_NUM_REGS 0x03 -+ -+/* Interrupt Registers */ -+#define WSA885X_INTR_STATUS0 0x8584 -+#define WSA885X_INTR_MASK0 0x8581 -+#define WSA885X_INTR_CLEAR0 0x8587 -+ -+/* Power and PA FSM Control Registers */ -+#define WSA885X_PA0_FSM_CTL0 0x842A -+#define WSA885X_PA1_FSM_CTL0 0x8434 -+ -+/* Digital Control GPIO and Interrupt Registers */ -+#define WSA885X_DIG_CTRL1_PIN_CT 0x8510 -+#define WSA885X_DIG_CTRL1_SPMI_PAD_GPIO2_CTL 0x8518 -+#define WSA885X_DIG_CTRL1_INTR_MODE 0x8580 -+ -+#define WSA885X_I2S_CTL0_PCM_RATE_MASK GENMASK(4, 1) -+#define WSA885X_I2S_CTL0_ENABLE_MASK BIT(0) -+#define WSA885X_I2S_CTL0_PCM_RATE(v) \ -+ FIELD_PREP(WSA885X_I2S_CTL0_PCM_RATE_MASK, (v)) -+#define WSA885X_I2S_CTL0_PCM_RATE_8KHZ 0x0 -+#define WSA885X_I2S_CTL0_PCM_RATE_16KHZ 0x1 -+#define WSA885X_I2S_CTL0_PCM_RATE_32KHZ 0x2 -+#define WSA885X_I2S_CTL0_PCM_RATE_48_OR_44KHZ 0x3 -+#define WSA885X_I2S_CTL0_PCM_RATE_96_OR_88KHZ 0x4 -+#define WSA885X_I2S_CTL0_PCM_RATE_192_OR_176KHZ 0x5 -+#define WSA885X_I2S_CTL0_PCM_RATE_384_OR_352KHZ 0x6 -+#define WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK GENMASK(2, 0) -+#define WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK GENMASK(6, 4) -+#define WSA885X_I2S_CFG0_TDM_TX_SLOT0(v) \ -+ FIELD_PREP_CONST(WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK, (v)) -+#define WSA885X_I2S_CFG0_TDM_TX_SLOT1(v) \ -+ FIELD_PREP_CONST(WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK, (v)) -+#define WSA885X_I2S_CFG1_TDM_TX_SLOT2_MASK GENMASK(2, 0) -+#define WSA885X_I2S_CFG1_TDM_TX_SLOT3_MASK GENMASK(6, 4) -+#define WSA885X_I2S_CFG1_TDM_TX_SLOT2(v) \ -+ FIELD_PREP_CONST(WSA885X_I2S_CFG1_TDM_TX_SLOT2_MASK, (v)) -+#define WSA885X_I2S_CFG1_TDM_TX_SLOT3(v) \ -+ FIELD_PREP_CONST(WSA885X_I2S_CFG1_TDM_TX_SLOT3_MASK, (v)) -+#define WSA885X_I2S_TDM_CTL0_I2S_TDM_EN_MASK BIT(0) -+#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK GENMASK(3, 2) -+#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_2 \ -+ FIELD_PREP_CONST(WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, 0) -+#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_4 \ -+ FIELD_PREP_CONST(WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, 1) -+#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_8 \ -+ FIELD_PREP_CONST(WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, 3) -+#define WSA885X_I2S_TDM_CH_TX_CH0_EN BIT(0) -+#define WSA885X_I2S_TDM_CH_TX_CH1_EN BIT(1) -+#define WSA885X_I2S_TDM_CH_TX_CH2_EN BIT(2) -+#define WSA885X_I2S_TDM_CH_TX_CH3_EN BIT(3) -+#define WSA885X_I2S_TDM_CH_RX_CH0_EN BIT(0) -+#define WSA885X_I2S_TDM_CH_RX_CH3_EN BIT(3) -+#define WSA885X_I2S_RESET_CTL_RESET_MASK BIT(0) -+#define WSA885X_PCM_DATA_WD_CTL1_PCM_DATA_WD_EN_MASK BIT(2) -+#define WSA885X_POWER_FSM_CTL0_CLEAR_ERROR_MASK BIT(3) -+#define WSA885X_PA_FSM_CTL0_CLEAR_ERROR_MASK BIT(2) -+ -+#define WSA885X_I2S_TX_SLOT_ISENSE0 0x1 -+#define WSA885X_I2S_TX_SLOT_ISENSE1 0x2 -+#define WSA885X_I2S_TX_SLOT_CUR_SENSE0 0x5 -+#define WSA885X_I2S_TX_SLOT_CUR_SENSE1 0x6 -+ -+/* RX Sample Rate Index Values - Audio Playback Path */ -+#define WSA885X_RX_RATE_8000HZ 0x00 -+#define WSA885X_RX_RATE_16000HZ 0x01 -+#define WSA885X_RX_RATE_32000HZ 0x02 -+#define WSA885X_RX_RATE_44100HZ 0x03 -+#define WSA885X_RX_RATE_48000HZ 0x04 -+#define WSA885X_RX_RATE_96000HZ 0x05 -+#define WSA885X_RX_RATE_192000HZ 0x06 -+#define WSA885X_RX_RATE_384000HZ 0x07 -+ -+/* VI Sample Rate Index Values - Voltage/Current Sensing Path */ -+#define WSA885X_VI_RATE_8000HZ 0x00 -+#define WSA885X_VI_RATE_16000HZ 0x01 -+#define WSA885X_VI_RATE_44100HZ 0x02 -+#define WSA885X_VI_RATE_48000HZ 0x03 -+#define WSA885X_VI_RATE_96000HZ 0x04 -+#define WSA885X_VI_RATE_22050HZ 0x05 -+#define WSA885X_VI_RATE_24000HZ 0x06 -+#define WSA885X_VI_RATE_192000HZ 0x07 -+#define WSA885X_VI_RATE_384000HZ 0x08 -+ -+/* Channel Configuration Masks */ -+#define WSA885X_CHANNEL_STEREO 0x03 -+#define WSA885X_CHANNEL_MONO_LEFT 0x01 -+#define WSA885X_CHANNEL_MONO_RIGHT 0x02 -+ -+#define WSA885X_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \ -+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | \ -+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) -+ -+#define WSA885X_PLL_LOCK_BIT BIT(0) -+ -+#define WSA885X_FU21_VOL_STEPS 124 -+#define WSA885X_USAGE_MODE_MAX 8 -+static const DECLARE_TLV_DB_SCALE(wsa885x_fu21_digital_gain, -8400, 100, 0); -+ -+static bool wsa885x_is_valid_rx_slot_mask(u32 mask) -+{ -+ return mask == WSA885X_CHANNEL_MONO_LEFT || -+ mask == WSA885X_CHANNEL_MONO_RIGHT || -+ mask == WSA885X_CHANNEL_STEREO; -+} -+ -+static const char *const wsa885x_supply_name[] = { -+ "vdd-1p8", -+ "vdd-io", -+}; -+ -+enum { -+ WSA885X_BATT_1S = 1, -+ WSA885X_BATT_2S, -+}; -+ -+enum { -+ WSA885X_IRQ_INT_SAF2WAR = 0, -+ WSA885X_IRQ_INT_WAR2SAF, -+ WSA885X_IRQ_INT_DISABLE, -+ WSA885X_IRQ_INT_PA0_OCP, -+ WSA885X_IRQ_INT_PA1_OCP, -+ WSA885X_IRQ_INT_CLIP0, -+ WSA885X_IRQ_INT_CLIP1, -+ WSA885X_IRQ_INT_CLK_WD, -+ WSA885X_IRQ_INT_INTR_GPIO1_PIN, -+ WSA885X_IRQ_INT_INTR_GPIO2_PIN, -+ WSA885X_IRQ_INT_UVLO, -+ WSA885X_IRQ_INT_BOP, -+ WSA885X_IRQ_INT_PA0_FSM_ERR, -+ WSA885X_IRQ_INT_PA1_FSM_ERR, -+ WSA885X_IRQ_INT_MAIN_FSM_ERR, -+ WSA885X_IRQ_INT_PCM_DATA0_WD, -+ WSA885X_IRQ_INT_PCM_DATA1_WD, -+ WSA885X_IRQ_INT_PCM_DATA0_DC, -+ WSA885X_IRQ_INT_PCM_DATA1_DC, -+ WSA885X_IRQ_INT_PLL_UNLOCKED, -+ WSA885X_IRQ_INT_PROT_MODE_CHANGE, -+ WSA885X_IRQ_INT_PB_CLOCK_VALID, -+ WSA885X_IRQ_INT_SENSE_CLOCK_VALID, -+ WSA885X_IRQ_MAX, -+}; -+ -+struct wsa885x_priv { -+ struct i2c_client *client; -+ struct regmap *regmap; -+ struct device *dev; -+ struct snd_soc_component *component; -+ struct gpio_desc *sd_n; -+ struct reset_control *sd_reset; -+ u32 usage_mode; -+ u32 rx_slot_mask; -+ u32 batt_conf; -+ int stereo_vol_db; -+ struct mutex state_lock; /* protects mutable control state */ -+}; -+ -+struct wsa885x_reg_update { -+ unsigned int reg; -+ unsigned int mask; -+ unsigned int val; -+}; -+ -+static const struct regmap_range_cfg wsa885x_regmap_ranges[] = { -+ { -+ .range_min = 0, -+ .range_max = 0x88ff, -+ .selector_reg = 0x0, -+ .selector_mask = 0xFF, -+ .selector_shift = 0, -+ .window_start = 0, -+ .window_len = 0x100, -+ }, -+}; -+ -+static const struct reg_default wsa885x_codec_reg_defaults[] = { -+ {WSA885X_SMP_AMP_CTRL_STEREO_STEREO_SMP_AMP_CTRL_I2S, 0x00}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_IT21_CLUSERINDEX, 0x01}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_CMT_GRP_MASK, 0x00}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_OT23_USAGE, 0x00}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID, 0x00}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_CS21_SAMPLERATEINDEX, 0x04}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_PPU21_POSTURENUMBER, 0x01}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0, 0x01}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1, 0x01}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_MSB, 0xac}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB, 0x00}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_MSB, 0xac}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB, 0x00}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS, 0x03}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS, 0x03}, -+ {WSA885X_SMP_AMP_CTRL_STEREO_CS24_SAMPLERATEINDEX, 0x03}, -+ {WSA885X_ANA_TOP_PON_CKSK_CTL_0, 0x00}, -+ {WSA885X_ANA_TOP_BG_TVP_UVLO1_PROG, 0x19}, -+ {WSA885X_ANA_TOP_BG_TVP_UVLO2_PROG, 0x22}, -+ {WSA885X_ANA_PLL_DIV_CTL_0, 0x0c}, -+ {WSA885X_ANA_PLL_DIV_CTL_1, 0x50}, -+ {WSA885X_ANA_TOP_PLL_VCO_CTL, 0x00}, -+ {WSA885X_ANA_TOP_PLL_LOOPFILT_0, 0xb4}, -+ {WSA885X_ANA_TOP_PLL_OVRD_CTL, 0x00}, -+ {WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x00}, -+ {WSA885X_ANA_TOP_BOOST_STB_CTRL2, 0x03}, -+ {WSA885X_ANA_TOP_BOOST_STB_CTRL3, 0x3c}, -+ {WSA885X_ANA_TOP_BOOST_BYP_CTRL2, 0xc5}, -+ {WSA885X_ANA_TOP_BOOST_BYP_CTRL3, 0x13}, -+ {WSA885X_ANA_TOP_BOOST_MISC, 0x79}, -+ {WSA885X_ANA_TOP_SPK_TOP_SPARE3, 0x00}, -+ {WSA885X_SPK_TOP_COMMON_CTRL2, 0x08}, -+ {WSA885X_SPK_TOP_LF_CH1_CTRL11, 0x09}, -+ {WSA885X_SPK_TOP_LF_CH1_TUNE1, 0x00}, -+ {WSA885X_SPK_TOP_LF_CH2_TUNE1, 0x00}, -+ {WSA885X_SPK_TOP_LF_CH1_CTRL9, 0x00}, -+ {WSA885X_SPK_TOP_LF_CH2_CTRL9, 0x00}, -+ {WSA885X_SPK_TOP_LF_CH2_CTRL11, 0x09}, -+ {WSA885X_SPK_TOP_COMMON_TUNE1, 0x03}, -+ {WSA885X_IVSENSE_VSNS_ISNS_CTL_CH1, 0x00}, -+ {WSA885X_DIG_CTRL0_CDC_CLK_CTL, 0x0e}, -+ {WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL2, 0x40}, -+ {WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL4, 0xff}, -+ {WSA885X_ANA_TOP_PLL_STATUS_0, 0x00}, -+ {WSA885X_ANA_TOP_PLL_STATUS_1, 0x00}, -+ {WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL2, 0x84}, -+ {WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3, 0x02}, -+ {WSA885X_ANA_TOP_IVSENSE_ADC_REF_CTL, 0x00}, -+ {WSA885X_ANA_TOP_IVSENSE_ADC_CDAC_CAL_CTL2, 0xe0}, -+ {WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_CTRL3, 0xa4}, -+ {WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_TUNE3, 0xc9}, -+ {WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_CTRL3, 0xa4}, -+ {WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_TUNE3, 0xc9}, -+ {WSA885X_DIG_CTRL0_TOP_CLK_CFG, 0x00}, -+ {WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x00}, -+ {WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE, 0x00}, -+ {WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x00}, -+ {WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00}, -+ {WSA885X_DIG_CTRL0_POWER_FSM_CTL0, 0x05}, -+ {WSA885X_DIG_CTRL0_POWER_FSM_CTL1, 0x00}, -+ {WSA885X_DIG_CTRL0_PA0_FSM_CTL1, 0x45}, -+ {WSA885X_DIG_CTRL0_PA1_FSM_CTL1, 0x45}, -+ {WSA885X_DIG_CTRL0_VBAT_THRM_FLT_CTL, 0x7f}, -+ {WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x00}, -+ {WSA885X_DIG_CTRL0_GAIN_RAMP0_CTL1, 0x01}, -+ {WSA885X_DIG_CTRL0_GAIN_RAMP1_CTL1, 0x01}, -+ {WSA885X_DIG_CTRL1_I2S_CTL0, 0x06}, -+ {WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, 0x00}, -+ {WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX, 0x00}, -+ {WSA885X_DIG_CTRL1_I2S_TDM_CTL0, 0x00}, -+ {WSA885X_DIG_CTRL1_I2S_TDM_CTL1, 0x05}, -+ {WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, 0x00}, -+ {WSA885X_DIG_CTRL1_I2S_RESET_CTL, 0x00}, -+ {WSA885X_DIG_CTRL1_I2S_TDM_CH_RX, WSA885X_I2S_TDM_CH_RX_CH3_EN}, -+ {WSA885X_CDC_RX0_RX_PATH_CFG0, 0x89}, -+ {WSA885X_CDC_RX0_RX_PATH_CFG1, 0x64}, -+ {WSA885X_CDC_RX0_RX_PATH_CTL, 0x24}, -+ {WSA885X_RX0_RX_PATH_DSMDEM_CTL, 0x01}, -+ {WSA885X_CDC_RX1_RX_PATH_CFG0, 0x89}, -+ {WSA885X_CDC_RX1_RX_PATH_CFG1, 0x64}, -+ {WSA885X_CDC_RX1_RX_PATH_CTL, 0x04}, -+ {WSA885X_RX1_RX_PATH_DSMDEM_CTL, 0x01}, -+ {WSA885X_CDC_COMPANDER0_CTL0, 0x01}, -+ {WSA885X_CDC_COMPANDER0_CTL7, 0x2a}, -+ {WSA885X_CDC_COMPANDER1_CTL0, 0x01}, -+ {WSA885X_CDC_COMPANDER1_CTL7, 0x2a}, -+ {WSA885X_CDC_VSENSE0_SPKR_PROT_PATH_CTL, 0x14}, -+ {WSA885X_CDC_VSENSE1_SPKR_PROT_PATH_CTL, 0x14}, -+ {WSA885X_CDC_ISENSE0_SPKR_PROT_PATH_CTL, 0x14}, -+ {WSA885X_CDC_ISENSE1_SPKR_PROT_PATH_CTL, 0x14}, -+ {WSA885X_CDC_CLSH_V1P8_BP_CTL1, 0x50}, -+ {WSA885X_CDC_CLSH_V1P8_BP_CTL0, 0x6c}, -+ {WSA885X_CDC_CLSH_CLSH_SIG_DP_CTL0, 0x0d}, -+ {WSA885X_CDC_CLSH_CLSH_V_HD_PA, 0x03}, -+ {WSA885X_CDC_CLSH_V1P8_BP_CTL2, 0x05}, -+}; -+ -+static void wsa885x_multi_update_bits(struct regmap *regmap, -+ const struct wsa885x_reg_update *updates, -+ size_t num_updates) -+{ -+ size_t i; -+ -+ for (i = 0; i < num_updates; i++) -+ regmap_update_bits(regmap, updates[i].reg, -+ updates[i].mask, updates[i].val); -+} -+ -+static void wsa885x_toggle_irq_bit(struct wsa885x_priv *wsa885x, -+ unsigned int reg, unsigned int mask) -+{ -+ regmap_update_bits(wsa885x->regmap, reg, mask, 0); -+ regmap_update_bits(wsa885x->regmap, reg, mask, mask); -+} -+ -+static void wsa885x_pulse_irq_bit(struct wsa885x_priv *wsa885x, -+ unsigned int reg, unsigned int mask) -+{ -+ regmap_update_bits(wsa885x->regmap, reg, mask, 0); -+ regmap_update_bits(wsa885x->regmap, reg, mask, mask); -+ regmap_update_bits(wsa885x->regmap, reg, mask, 0); -+} -+ -+static int wsa885x_tdm_ctl0_slot_num_val(int slots, unsigned int *slot_num_val) -+{ -+ if (!slot_num_val) -+ return -EINVAL; -+ -+ switch (slots) { -+ case 2: -+ *slot_num_val = WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_2; -+ return 0; -+ case 4: -+ *slot_num_val = WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_4; -+ return 0; -+ case 8: -+ *slot_num_val = WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_8; -+ return 0; -+ default: -+ return -EINVAL; -+ } -+} -+ -+static int wsa885x_reg_update_sequence(struct regmap *regmap, int slots) -+{ -+ static const struct reg_sequence regs[] = { -+ { WSA885X_DIG_CTRL1_I2S_TDM_CTL1, 0x15 }, -+ { WSA885X_DIG_CTRL1_I2S_TDM_CTL1, 0x11 }, -+ }; -+ unsigned int slot_num_val; -+ int ret; -+ -+ if (!regmap) -+ return -EINVAL; -+ -+ ret = wsa885x_tdm_ctl0_slot_num_val(slots, &slot_num_val); -+ if (ret) -+ return ret; -+ -+ regmap_multi_reg_write(regmap, regs, ARRAY_SIZE(regs)); -+ -+ regmap_update_bits(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CTL0, -+ WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, -+ slot_num_val); -+ regmap_update_bits(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CTL0, -+ WSA885X_I2S_TDM_CTL0_I2S_TDM_EN_MASK, -+ WSA885X_I2S_TDM_CTL0_I2S_TDM_EN_MASK); -+ regmap_write(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, -+ WSA885X_I2S_TDM_CH_TX_CH0_EN); -+ regmap_update_bits(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, -+ WSA885X_I2S_TDM_CH_TX_CH1_EN, -+ WSA885X_I2S_TDM_CH_TX_CH1_EN); -+ -+ return 0; -+} -+ -+static int wsa885x_wait_for_pll_lock(struct wsa885x_priv *wsa885x) -+{ -+ unsigned int status = 0; -+ int cnt = 0, ret = 0; -+ -+ do { -+ usleep_range(1000, 1100); -+ ret = regmap_read(wsa885x->regmap, WSA885X_ANA_TOP_PLL_STATUS_0, &status); -+ if (ret) { -+ dev_err(wsa885x->dev, "PLL status read failed: %d\n", ret); -+ return ret; -+ } -+ -+ if (status & WSA885X_PLL_LOCK_BIT) -+ return 0; -+ } while (++cnt < 20); -+ -+ dev_warn(wsa885x->dev, "PLL lock timeout after 20ms, status=0x%x\n", status); -+ return -ETIMEDOUT; -+} -+ -+static int wsa885x_2s_conf(struct wsa885x_priv *wsa885x) -+{ -+ static const struct reg_sequence regs[] = { -+ { WSA885X_SPK_TOP_COMMON_TUNE1, 0x26 }, -+ { WSA885X_SPK_TOP_LF_CH1_CTRL11, 0x0d }, -+ { WSA885X_SPK_TOP_LF_CH2_CTRL11, 0x0d }, -+ { WSA885X_CDC_CLSH_V1P8_BP_CTL1, 0x71 }, -+ { WSA885X_CDC_CLSH_V1P8_BP_CTL0, 0xAA }, -+ }; -+ -+ return regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs)); -+} -+ -+static const struct reg_sequence wsa885x_reg_init[] = { -+ { WSA885X_CDC_RX0_RX_PATH_CTL, 0x24 }, -+ { WSA885X_CDC_RX1_RX_PATH_CTL, 0x24 }, -+ { WSA885X_RX0_RX_PATH_DSMDEM_CTL, 0x01 }, -+ { WSA885X_RX1_RX_PATH_DSMDEM_CTL, 0x01 }, -+ { WSA885X_CDC_COMPANDER0_CTL0, 0x01 }, -+ { WSA885X_CDC_COMPANDER1_CTL0, 0x01 }, -+ { WSA885X_CDC_VSENSE0_SPKR_PROT_PATH_CTL, 0x14 }, -+ { WSA885X_CDC_VSENSE1_SPKR_PROT_PATH_CTL, 0x14 }, -+ { WSA885X_CDC_ISENSE0_SPKR_PROT_PATH_CTL, 0x14 }, -+ { WSA885X_CDC_ISENSE1_SPKR_PROT_PATH_CTL, 0x14 }, -+ { WSA885X_DIG_CTRL0_CDC_CLK_CTL, 0x0f }, -+ { WSA885X_DIG_CTRL0_CDC_CLK_CTL, 0x4f }, -+ { WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x02 }, -+ { WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x00 }, -+ { WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x01 }, -+ { WSA885X_SMP_AMP_CTRL_STEREO_CMT_GRP_MASK, 0x01 }, -+ { WSA885X_CDC_RX0_RX_PATH_CFG1, 0x60 }, -+ { WSA885X_CDC_RX1_RX_PATH_CFG1, 0x60 }, -+ { WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_CTRL3, 0xa5 }, -+ { WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_CTRL3, 0xa5 }, -+ { WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL2, 0x85 }, -+ { WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3, 0x0c }, -+ { WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3, 0x0e }, -+ { WSA885X_ANA_TOP_IVSENSE_ADC_REF_CTL, 0x0c }, -+ { WSA885X_DIG_CTRL0_GAIN_RAMP0_CTL1, 0x01 }, -+ { WSA885X_DIG_CTRL0_GAIN_RAMP1_CTL1, 0x01 }, -+ { WSA885X_CDC_RX0_RX_PATH_CFG0, 0x88 }, -+ { WSA885X_CDC_RX0_RX_PATH_CFG0, 0x89 }, -+ { WSA885X_CDC_RX1_RX_PATH_CFG0, 0x88 }, -+ { WSA885X_CDC_RX1_RX_PATH_CFG0, 0x89 }, -+ { WSA885X_ANA_TOP_BOOST_STB_CTRL2, 0x82 }, -+ { WSA885X_ANA_TOP_BOOST_STB_CTRL3, 0x34 }, -+ { WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL2, 0x41 }, -+ { WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL4, 0x7f }, -+ { WSA885X_CDC_CLSH_V1P8_BP_CTL1, 0x50 }, -+ { WSA885X_CDC_CLSH_V1P8_BP_CTL0, 0x6c }, -+ { WSA885X_CDC_CLSH_CLSH_SIG_DP_CTL0, 0x0d }, -+ { WSA885X_CDC_CLSH_CLSH_V_HD_PA, 0x03 }, -+ { WSA885X_DIG_CTRL0_POWER_FSM_CTL0, 0x05 }, -+ { WSA885X_ANA_TOP_PON_CKSK_CTL_0, 0x20 }, -+ { WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_TUNE3, 0x45 }, -+ { WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_TUNE3, 0x45 }, -+ { WSA885X_CDC_CLSH_V1P8_BP_CTL2, 0x05 }, -+ { WSA885X_ANA_TOP_BG_TVP_UVLO1_PROG, 0x35 }, -+ { WSA885X_ANA_TOP_BG_TVP_UVLO2_PROG, 0x21 }, -+ { WSA885X_ANA_TOP_BOOST_BYP_CTRL2, 0xc7 }, -+ { WSA885X_ANA_TOP_BOOST_BYP_CTRL3, 0x11 }, -+ { WSA885X_ANA_TOP_IVSENSE_ADC_CDAC_CAL_CTL2, 0x80 }, -+ { WSA885X_ANA_TOP_SPK_TOP_SPARE3, 0x08 }, -+ { WSA885X_DIG_CTRL0_PA0_FSM_CTL1, 0x47 }, -+ { WSA885X_DIG_CTRL0_PA1_FSM_CTL1, 0x47 }, -+ { WSA885X_CDC_COMPANDER0_CTL7, 0x34 }, -+ { WSA885X_CDC_COMPANDER1_CTL7, 0x34 }, -+ { WSA885X_DIG_CTRL0_VBAT_THRM_FLT_CTL, 0x79 }, -+}; -+ -+static int wsa885x_hw_init(struct wsa885x_priv *wsa885x) -+{ -+ static const struct reg_sequence regs[] = { -+ { WSA885X_DIG_CTRL1_SPMI_PAD_GPIO2_CTL, 0x2e }, -+ { WSA885X_DIG_CTRL1_INTR_MODE, 0x01 }, -+ { WSA885X_DIG_CTRL1_PIN_CT, 0x04 }, -+ }; -+ int ret; -+ -+ ret = regmap_multi_reg_write(wsa885x->regmap, wsa885x_reg_init, -+ ARRAY_SIZE(wsa885x_reg_init)); -+ if (ret) -+ return ret; -+ -+ if (wsa885x->batt_conf == WSA885X_BATT_2S) { -+ ret = wsa885x_2s_conf(wsa885x); -+ if (ret) -+ return ret; -+ } -+ -+ return regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs)); -+} -+ -+static int wsa885x_unmask_interrupts(struct wsa885x_priv *wsa885x) -+{ -+ static const struct reg_sequence regs[] = { -+ { WSA885X_INTR_MASK0, 0x00 }, -+ { WSA885X_INTR_MASK0 + 1, 0x00 }, -+ { WSA885X_INTR_MASK0 + 2, 0xf8 }, -+ }; -+ -+ return regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs)); -+} -+ -+static int wsa885x_wait_for_pde_state(struct wsa885x_priv *wsa885x, int ps) -+{ -+ unsigned int act_ps = 0, clock_valid = 0; -+ int rc = 0, cnt = 0; -+ -+ if (ps < 0 || ps > 3) -+ return -EINVAL; -+ -+ do { -+ usleep_range(1000, 1500); -+ rc = regmap_read(wsa885x->regmap, -+ WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS, -+ &act_ps); -+ if (rc) { -+ dev_err(wsa885x->dev, "PDE state read failed: %d\n", rc); -+ return rc; -+ } -+ if (act_ps == ps) -+ return 0; -+ } while (++cnt < 5); -+ -+ if (regmap_read(wsa885x->regmap, -+ WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID, -+ &clock_valid)) -+ dev_err(wsa885x->dev, -+ "PDE power state %d request failed, actual_ps %d, clock_valid read failed\n", -+ ps, act_ps); -+ else -+ dev_err(wsa885x->dev, -+ "PDE power state %d request failed, actual_ps %d, clock_valid:%d\n", -+ ps, act_ps, clock_valid); -+ -+ return -ETIMEDOUT; -+} -+ -+static void wsa885x_program_stereo_volume(struct wsa885x_priv *wsa885x, -+ int stereo_vol_db, bool commit) -+{ -+ regmap_write(wsa885x->regmap, -+ WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_MSB, -+ (u8)(s8)stereo_vol_db); -+ regmap_write(wsa885x->regmap, -+ WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB, 0x00); -+ regmap_write(wsa885x->regmap, -+ WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_MSB, -+ (u8)(s8)stereo_vol_db); -+ regmap_write(wsa885x->regmap, -+ WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB, 0x00); -+ -+ if (commit) -+ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01); -+} -+ -+static int wsa885x_codec_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params, -+ struct snd_soc_dai *dai) -+{ -+ struct wsa885x_priv *wsa885x; -+ u8 pcm_rate, cs21_sample_rate_idx, cs24_sample_rate_idx; -+ -+ wsa885x = snd_soc_component_get_drvdata(dai->component); -+ -+ switch (params_rate(params)) { -+ case 8000: -+ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_8KHZ; -+ cs21_sample_rate_idx = WSA885X_RX_RATE_8000HZ; -+ cs24_sample_rate_idx = WSA885X_VI_RATE_8000HZ; -+ break; -+ case 16000: -+ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_16KHZ; -+ cs21_sample_rate_idx = WSA885X_RX_RATE_16000HZ; -+ cs24_sample_rate_idx = WSA885X_VI_RATE_16000HZ; -+ break; -+ case 32000: -+ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_32KHZ; -+ cs21_sample_rate_idx = WSA885X_RX_RATE_32000HZ; -+ cs24_sample_rate_idx = WSA885X_VI_RATE_48000HZ; -+ break; -+ case 44100: -+ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_48_OR_44KHZ; -+ cs21_sample_rate_idx = WSA885X_RX_RATE_44100HZ; -+ cs24_sample_rate_idx = WSA885X_VI_RATE_44100HZ; -+ break; -+ case 48000: -+ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_48_OR_44KHZ; -+ cs21_sample_rate_idx = WSA885X_RX_RATE_48000HZ; -+ cs24_sample_rate_idx = WSA885X_VI_RATE_48000HZ; -+ break; -+ case 88200: -+ case 96000: -+ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_96_OR_88KHZ; -+ cs21_sample_rate_idx = WSA885X_RX_RATE_96000HZ; -+ cs24_sample_rate_idx = WSA885X_VI_RATE_96000HZ; -+ break; -+ case 176400: -+ case 192000: -+ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_192_OR_176KHZ; -+ cs21_sample_rate_idx = WSA885X_RX_RATE_192000HZ; -+ cs24_sample_rate_idx = WSA885X_VI_RATE_192000HZ; -+ break; -+ case 352800: -+ case 384000: -+ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_384_OR_352KHZ; -+ cs21_sample_rate_idx = WSA885X_RX_RATE_384000HZ; -+ cs24_sample_rate_idx = WSA885X_VI_RATE_384000HZ; -+ break; -+ default: -+ dev_err(wsa885x->dev, "sampling rate %d is not supported\n", params_rate(params)); -+ return -EINVAL; -+ } -+ -+ regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_CTL0, -+ WSA885X_I2S_CTL0_PCM_RATE_MASK | -+ WSA885X_I2S_CTL0_ENABLE_MASK, -+ WSA885X_I2S_CTL0_PCM_RATE(pcm_rate) | -+ WSA885X_I2S_CTL0_ENABLE_MASK); -+ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_RESET_CTL, 0x00); -+ regmap_write(wsa885x->regmap, WSA885X_SMP_AMP_CTRL_STEREO_CS21_SAMPLERATEINDEX, -+ cs21_sample_rate_idx); -+ regmap_write(wsa885x->regmap, WSA885X_SMP_AMP_CTRL_STEREO_CS24_SAMPLERATEINDEX, -+ cs24_sample_rate_idx); -+ -+ mutex_lock(&wsa885x->state_lock); -+ wsa885x_program_stereo_volume(wsa885x, wsa885x->stereo_vol_db, false); -+ mutex_unlock(&wsa885x->state_lock); -+ -+ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01); -+ -+ return 0; -+} -+ -+static int wsa885x_codec_set_tdm_slot(struct snd_soc_dai *dai, -+ unsigned int tx_slot_mask, -+ unsigned int rx_slot_mask, int slots, -+ int slot_width) -+{ -+ static const struct wsa885x_reg_update stereo_updates[] = { -+ { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK, -+ WSA885X_I2S_CFG0_TDM_TX_SLOT0(WSA885X_I2S_TX_SLOT_ISENSE0) }, -+ { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK, -+ WSA885X_I2S_CFG0_TDM_TX_SLOT1(WSA885X_I2S_TX_SLOT_ISENSE1) }, -+ { WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX, WSA885X_I2S_CFG1_TDM_TX_SLOT2_MASK, -+ WSA885X_I2S_CFG1_TDM_TX_SLOT2(WSA885X_I2S_TX_SLOT_CUR_SENSE0) }, -+ { WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX, WSA885X_I2S_CFG1_TDM_TX_SLOT3_MASK, -+ WSA885X_I2S_CFG1_TDM_TX_SLOT3(WSA885X_I2S_TX_SLOT_CUR_SENSE1) }, -+ }; -+ static const struct wsa885x_reg_update mono_left_updates[] = { -+ { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK, -+ WSA885X_I2S_CFG0_TDM_TX_SLOT0(WSA885X_I2S_TX_SLOT_ISENSE0) }, -+ { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK, -+ WSA885X_I2S_CFG0_TDM_TX_SLOT1(WSA885X_I2S_TX_SLOT_CUR_SENSE0) }, -+ }; -+ static const struct wsa885x_reg_update mono_right_updates[] = { -+ { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK, -+ WSA885X_I2S_CFG0_TDM_TX_SLOT0(WSA885X_I2S_TX_SLOT_ISENSE1) }, -+ { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK, -+ WSA885X_I2S_CFG0_TDM_TX_SLOT1(WSA885X_I2S_TX_SLOT_CUR_SENSE1) }, -+ }; -+ struct wsa885x_priv *wsa885x; -+ unsigned int slot_num_val; -+ u32 mask; -+ int ret; -+ -+ wsa885x = snd_soc_component_get_drvdata(dai->component); -+ -+ ret = wsa885x_tdm_ctl0_slot_num_val(slots, &slot_num_val); -+ if (ret) { -+ dev_err(wsa885x->dev, "%s: unsupported slot count %d\n", -+ __func__, slots); -+ return ret; -+ } -+ -+ if (rx_slot_mask && !wsa885x_is_valid_rx_slot_mask(rx_slot_mask)) { -+ dev_err(wsa885x->dev, -+ "%s: unsupported rx_slot_mask 0x%x\n", -+ __func__, rx_slot_mask); -+ return -EINVAL; -+ } -+ -+ mutex_lock(&wsa885x->state_lock); -+ if (rx_slot_mask) -+ wsa885x->rx_slot_mask = rx_slot_mask; -+ else if (!wsa885x_is_valid_rx_slot_mask(wsa885x->rx_slot_mask)) -+ wsa885x->rx_slot_mask = WSA885X_CHANNEL_STEREO; -+ mask = wsa885x->rx_slot_mask; -+ -+ regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_RESET_CTL, -+ WSA885X_I2S_RESET_CTL_RESET_MASK, -+ WSA885X_I2S_RESET_CTL_RESET_MASK); -+ -+ if (mask == WSA885X_CHANNEL_STEREO) { -+ wsa885x_multi_update_bits(wsa885x->regmap, stereo_updates, -+ ARRAY_SIZE(stereo_updates)); -+ ret = wsa885x_reg_update_sequence(wsa885x->regmap, slots); -+ if (ret) -+ goto exit_unlock; -+ regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, -+ WSA885X_I2S_TDM_CH_TX_CH2_EN, -+ WSA885X_I2S_TDM_CH_TX_CH2_EN); -+ regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, -+ WSA885X_I2S_TDM_CH_TX_CH3_EN, -+ WSA885X_I2S_TDM_CH_TX_CH3_EN); -+ } else if (mask == WSA885X_CHANNEL_MONO_LEFT) { -+ wsa885x_multi_update_bits(wsa885x->regmap, mono_left_updates, -+ ARRAY_SIZE(mono_left_updates)); -+ ret = wsa885x_reg_update_sequence(wsa885x->regmap, slots); -+ if (ret) -+ goto exit_unlock; -+ } else if (mask == WSA885X_CHANNEL_MONO_RIGHT) { -+ wsa885x_multi_update_bits(wsa885x->regmap, mono_right_updates, -+ ARRAY_SIZE(mono_right_updates)); -+ ret = wsa885x_reg_update_sequence(wsa885x->regmap, slots); -+ if (ret) -+ goto exit_unlock; -+ } -+ -+ regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_CTL0, -+ WSA885X_I2S_CTL0_ENABLE_MASK, -+ WSA885X_I2S_CTL0_ENABLE_MASK); -+ regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_RESET_CTL, -+ WSA885X_I2S_RESET_CTL_RESET_MASK, 0); -+ -+ ret = 0; -+ -+exit_unlock: -+ mutex_unlock(&wsa885x->state_lock); -+ -+ return ret; -+} -+ -+static int wsa885x_codec_set_sysclk(struct snd_soc_dai *dai, int clk_id, -+ unsigned int freq, int dir) -+{ -+ static const struct reg_sequence pll_prep[] = { -+ { WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x03 }, -+ { WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x04 }, -+ { WSA885X_ANA_TOP_PLL_LOOPFILT_0, 0xB4 }, -+ { WSA885X_ANA_TOP_PLL_VCO_CTL, 0x00 }, -+ { WSA885X_ANA_TOP_PLL_OVRD_CTL, 0x00 }, -+ }; -+ static const struct reg_sequence pll_cleanup[] = { -+ { WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE, 0x00 }, -+ { WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x00 }, -+ { WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x00 }, -+ }; -+ struct wsa885x_priv *wsa885x; -+ u32 pll_div; -+ int ret = 0; -+ -+ wsa885x = snd_soc_component_get_drvdata(dai->component); -+ -+ if (!freq) -+ return -EINVAL; -+ if (WSA885X_CLK_RATE_FIXED % freq) -+ return -EINVAL; -+ pll_div = WSA885X_CLK_RATE_FIXED / freq; -+ if (pll_div > 0xff) -+ return -EINVAL; -+ -+ regmap_multi_reg_write(wsa885x->regmap, pll_prep, ARRAY_SIZE(pll_prep)); -+ regmap_write(wsa885x->regmap, WSA885X_ANA_PLL_DIV_CTL_0, pll_div); -+ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE, 0x02); -+ -+ ret = wsa885x_wait_for_pll_lock(wsa885x); -+ if (ret) { -+ dev_err(wsa885x->dev, "PLL lock failed, aborting sysclk configuration\n"); -+ regmap_multi_reg_write(wsa885x->regmap, pll_cleanup, -+ ARRAY_SIZE(pll_cleanup)); -+ return ret; -+ } -+ -+ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x00); -+ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_POWER_FSM_CTL1, 0x01); -+ regmap_write(wsa885x->regmap, WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x00); -+ -+ return 0; -+} -+ -+static int wsa885x_codec_mute_stream(struct snd_soc_dai *dai, int mute, int stream) -+{ -+ static const struct reg_sequence mute_regs[] = { -+ { WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00 }, -+ { WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS, 0x03 }, -+ }; -+ static const struct reg_sequence mute_commit_regs[] = { -+ { WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0, 0x01 }, -+ { WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1, 0x01 }, -+ { WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01 }, -+ }; -+ static const struct reg_sequence unmute_prep_head_regs[] = { -+ { WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00 }, -+ }; -+ static const struct reg_sequence unmute_prep_tail_regs[] = { -+ { WSA885X_SMP_AMP_CTRL_STEREO_IT21_CLUSERINDEX, 0x01 }, -+ { WSA885X_SMP_AMP_CTRL_STEREO_PPU21_POSTURENUMBER, 0x01 }, -+ }; -+ static const struct reg_sequence unmute_volume_regs[] = { -+ { WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB, 0x00 }, -+ { WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB, 0x00 }, -+ }; -+ static const struct reg_sequence unmute_commit_regs[] = { -+ { WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01 }, -+ { WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS, 0x00 }, -+ }; -+ static const struct reg_sequence unmute_finish_regs[] = { -+ { WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0, 0x00 }, -+ { WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1, 0x00 }, -+ { WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01 }, -+ }; -+ struct wsa885x_priv *wsa885x; -+ int ret = 0, ps0 = 0, ps3 = 3; -+ -+ wsa885x = snd_soc_component_get_drvdata(dai->component); -+ -+ if (stream != SNDRV_PCM_STREAM_PLAYBACK) -+ return 0; -+ -+ mutex_lock(&wsa885x->state_lock); -+ -+ if (wsa885x->usage_mode > WSA885X_USAGE_MODE_MAX) { -+ ret = -EINVAL; -+ goto exit_unlock; -+ } -+ -+ if (!wsa885x_is_valid_rx_slot_mask(wsa885x->rx_slot_mask)) -+ wsa885x->rx_slot_mask = WSA885X_CHANNEL_STEREO; -+ -+ if (mute) { -+ regmap_multi_reg_write(wsa885x->regmap, mute_regs, -+ ARRAY_SIZE(mute_regs)); -+ ret = wsa885x_wait_for_pde_state(wsa885x, ps3); -+ if (ret) { -+ dev_err(wsa885x->dev, -+ "PS3 transition failed: %d\n", ret); -+ } else { -+ regmap_multi_reg_write(wsa885x->regmap, mute_commit_regs, -+ ARRAY_SIZE(mute_commit_regs)); -+ } -+ } else { -+ regmap_multi_reg_write(wsa885x->regmap, unmute_prep_head_regs, -+ ARRAY_SIZE(unmute_prep_head_regs)); -+ regmap_write(wsa885x->regmap, WSA885X_SMP_AMP_CTRL_STEREO_OT23_USAGE, -+ wsa885x->usage_mode); -+ regmap_multi_reg_write(wsa885x->regmap, unmute_prep_tail_regs, -+ ARRAY_SIZE(unmute_prep_tail_regs)); -+ wsa885x_program_stereo_volume(wsa885x, wsa885x->stereo_vol_db, false); -+ regmap_multi_reg_write(wsa885x->regmap, unmute_volume_regs, -+ ARRAY_SIZE(unmute_volume_regs)); -+ regmap_multi_reg_write(wsa885x->regmap, unmute_commit_regs, -+ ARRAY_SIZE(unmute_commit_regs)); -+ ret = wsa885x_wait_for_pde_state(wsa885x, ps0); -+ if (ret) -+ goto exit_unlock; -+ -+ if (wsa885x->rx_slot_mask == WSA885X_CHANNEL_STEREO) { -+ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_RX, -+ WSA885X_I2S_TDM_CH_RX_CH0_EN | -+ WSA885X_I2S_TDM_CH_RX_CH3_EN); -+ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x03); -+ } else if (wsa885x->rx_slot_mask == WSA885X_CHANNEL_MONO_LEFT) { -+ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_RX, -+ WSA885X_I2S_TDM_CH_RX_CH3_EN); -+ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x01); -+ } else if (wsa885x->rx_slot_mask == WSA885X_CHANNEL_MONO_RIGHT) { -+ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_RX, -+ WSA885X_I2S_TDM_CH_RX_CH0_EN); -+ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x02); -+ } -+ -+ regmap_multi_reg_write(wsa885x->regmap, unmute_finish_regs, -+ ARRAY_SIZE(unmute_finish_regs)); -+ } -+ -+exit_unlock: -+ mutex_unlock(&wsa885x->state_lock); -+ -+ return ret; -+} -+ -+static int wsa885x_codec_hw_free(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ static const struct reg_sequence regs[] = { -+ { WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00 }, -+ }; -+ struct wsa885x_priv *wsa885x; -+ -+ wsa885x = snd_soc_component_get_drvdata(dai->component); -+ -+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) -+ return 0; -+ -+ mutex_lock(&wsa885x->state_lock); -+ regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs)); -+ mutex_unlock(&wsa885x->state_lock); -+ -+ return 0; -+} -+ -+static const struct snd_soc_dai_ops wsa885x_dai_ops = { -+ .hw_params = wsa885x_codec_hw_params, -+ .set_tdm_slot = wsa885x_codec_set_tdm_slot, -+ .set_sysclk = wsa885x_codec_set_sysclk, -+ .mute_stream = wsa885x_codec_mute_stream, -+ .hw_free = wsa885x_codec_hw_free, -+}; -+ -+static struct snd_soc_dai_driver wsa885x_dai[] = { -+ { -+ .name = "wsa885x_dai_drv", -+ .playback = { -+ .stream_name = "WSA885X TDM Playback", -+ .channels_min = 1, -+ .channels_max = 2, -+ .rates = WSA885X_RATES, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | -+ SNDRV_PCM_FMTBIT_S32_LE, -+ }, -+ .ops = &wsa885x_dai_ops, -+ }, -+}; -+ -+static void wsa885x_reset_assert(void *data) -+{ -+ struct wsa885x_priv *wsa885x = data; -+ -+ if (wsa885x->sd_reset) -+ reset_control_assert(wsa885x->sd_reset); -+ else -+ gpiod_direction_output(wsa885x->sd_n, 1); -+} -+ -+static void wsa885x_reset_deassert(struct wsa885x_priv *wsa885x) -+{ -+ if (wsa885x->sd_reset) -+ reset_control_deassert(wsa885x->sd_reset); -+ else -+ gpiod_direction_output(wsa885x->sd_n, 0); -+} -+ -+static int wsa885x_get_reset(struct device *dev, struct wsa885x_priv *wsa885x) -+{ -+ wsa885x->sd_reset = devm_reset_control_get_optional_shared(dev, NULL); -+ if (IS_ERR(wsa885x->sd_reset)) -+ return dev_err_probe(dev, PTR_ERR(wsa885x->sd_reset), -+ "Failed to get reset\n"); -+ else if (wsa885x->sd_reset) -+ return 0; -+ -+ wsa885x->sd_n = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); -+ if (IS_ERR(wsa885x->sd_n)) -+ return dev_err_probe(dev, PTR_ERR(wsa885x->sd_n), -+ "Shutdown Control GPIO not found\n"); -+ -+ return 0; -+} -+ -+static bool wsa885x_volatile_register(struct device *dev, unsigned int reg) -+{ -+ switch (reg) { -+ case WSA885X_ANA_TOP_PLL_STATUS_0: -+ case WSA885X_ANA_TOP_PLL_STATUS_1: -+ case WSA885X_DIG_CTRL0_SDCA_COMMIT: -+ case WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS: -+ case WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID: -+ case WSA885X_INTR_STATUS0: -+ case WSA885X_INTR_STATUS0 + 1: -+ case WSA885X_INTR_STATUS0 + 2: -+ case WSA885X_INTR_CLEAR0: -+ case WSA885X_INTR_CLEAR0 + 1: -+ case WSA885X_INTR_CLEAR0 + 2: -+ return true; -+ default: -+ return false; -+ } -+} -+ -+static bool wsa885x_readable_register(struct device *dev, unsigned int reg) -+{ -+ if (reg == WSA885X_INTR_CLEAR0 || -+ reg == WSA885X_INTR_CLEAR0 + 1 || -+ reg == WSA885X_INTR_CLEAR0 + 2) -+ return false; -+ return reg <= 0x88ff; -+} -+ -+static bool wsa885x_writeable_register(struct device *dev, unsigned int reg) -+{ -+ if (reg > 0x88ff) -+ return false; -+ -+ switch (reg) { -+ case WSA885X_ANA_TOP_PLL_STATUS_0: -+ case WSA885X_ANA_TOP_PLL_STATUS_1: -+ case WSA885X_INTR_STATUS0: -+ case WSA885X_INTR_STATUS0 + 1: -+ case WSA885X_INTR_STATUS0 + 2: -+ case WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS: -+ case WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID: -+ return false; -+ default: -+ return true; -+ } -+} -+ -+static const struct regmap_config wsa885x_regmap_cfg = { -+ .reg_bits = 8, -+ .val_bits = 8, -+ .max_register = 0x88FF, -+ .ranges = wsa885x_regmap_ranges, -+ .num_ranges = ARRAY_SIZE(wsa885x_regmap_ranges), -+ .reg_defaults = wsa885x_codec_reg_defaults, -+ .num_reg_defaults = ARRAY_SIZE(wsa885x_codec_reg_defaults), -+ .volatile_reg = wsa885x_volatile_register, -+ .writeable_reg = wsa885x_writeable_register, -+ .readable_reg = wsa885x_readable_register, -+ .cache_type = REGCACHE_MAPLE, -+ .use_single_read = true, -+ .use_single_write = true, -+}; -+ -+static int wsa885x_component_probe(struct snd_soc_component *component) -+{ -+ struct wsa885x_priv *wsa885x = -+ snd_soc_component_get_drvdata(component); -+ int ret; -+ -+ wsa885x->component = component; -+ snd_soc_component_init_regmap(component, wsa885x->regmap); -+ -+ ret = wsa885x_hw_init(wsa885x); -+ if (ret) -+ return ret; -+ -+ return wsa885x_unmask_interrupts(wsa885x); -+} -+ -+static int wsa885x_stereo_gain_offset_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); -+ int val; -+ -+ mutex_lock(&wsa885x->state_lock); -+ val = wsa885x->stereo_vol_db + 84; -+ mutex_unlock(&wsa885x->state_lock); -+ if (val < 0 || val > WSA885X_FU21_VOL_STEPS) -+ return -ERANGE; -+ -+ ucontrol->value.integer.value[0] = val; -+ return 0; -+} -+ -+static int wsa885x_stereo_gain_offset_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); -+ long val; -+ int stereo_vol_db; -+ -+ val = ucontrol->value.integer.value[0]; -+ -+ if (val < 0 || val > WSA885X_FU21_VOL_STEPS) { -+ dev_err(component->dev, "%s: Invalid range, Val: %ld\n", __func__, val); -+ return -EINVAL; -+ } -+ -+ stereo_vol_db = (int)val - 84; -+ -+ mutex_lock(&wsa885x->state_lock); -+ if (wsa885x->stereo_vol_db == stereo_vol_db) { -+ mutex_unlock(&wsa885x->state_lock); -+ return 0; -+ } -+ -+ wsa885x_program_stereo_volume(wsa885x, stereo_vol_db, true); -+ wsa885x->stereo_vol_db = stereo_vol_db; -+ mutex_unlock(&wsa885x->state_lock); -+ -+ return 1; -+} -+ -+static int wsa885x_usage_modes_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); -+ -+ mutex_lock(&wsa885x->state_lock); -+ if (wsa885x->usage_mode > WSA885X_USAGE_MODE_MAX) { -+ mutex_unlock(&wsa885x->state_lock); -+ return -ERANGE; -+ } -+ -+ ucontrol->value.integer.value[0] = wsa885x->usage_mode; -+ mutex_unlock(&wsa885x->state_lock); -+ -+ return 0; -+} -+ -+static int wsa885x_usage_modes_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); -+ u32 val = ucontrol->value.integer.value[0]; -+ -+ if (val > WSA885X_USAGE_MODE_MAX) -+ return -EINVAL; -+ -+ mutex_lock(&wsa885x->state_lock); -+ if (wsa885x->usage_mode == val) { -+ mutex_unlock(&wsa885x->state_lock); -+ return 0; -+ } -+ -+ wsa885x->usage_mode = val; -+ mutex_unlock(&wsa885x->state_lock); -+ -+ return 1; -+} -+ -+static int wsa885x_rx_slot_mask_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); -+ u32 mask; -+ -+ mutex_lock(&wsa885x->state_lock); -+ mask = wsa885x->rx_slot_mask; -+ mutex_unlock(&wsa885x->state_lock); -+ if (!wsa885x_is_valid_rx_slot_mask(mask)) -+ return -ERANGE; -+ -+ ucontrol->value.integer.value[0] = mask; -+ -+ return 0; -+} -+ -+static int wsa885x_rx_slot_mask_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); -+ u32 mask = ucontrol->value.integer.value[0]; -+ -+ if (!wsa885x_is_valid_rx_slot_mask(mask)) -+ return -EINVAL; -+ -+ mutex_lock(&wsa885x->state_lock); -+ if (wsa885x->rx_slot_mask == mask) { -+ mutex_unlock(&wsa885x->state_lock); -+ return 0; -+ } -+ -+ wsa885x->rx_slot_mask = mask; -+ mutex_unlock(&wsa885x->state_lock); -+ -+ return 1; -+} -+ -+static const struct snd_kcontrol_new wsa885x_snd_controls[] = { -+ SOC_SINGLE_EXT("Usage Mode", SND_SOC_NOPM, 0, WSA885X_USAGE_MODE_MAX, 0, -+ wsa885x_usage_modes_get, -+ wsa885x_usage_modes_put), -+ -+ SOC_SINGLE_EXT_TLV("Speaker Volume", SND_SOC_NOPM, -+ 0, WSA885X_FU21_VOL_STEPS, 0, -+ wsa885x_stereo_gain_offset_get, -+ wsa885x_stereo_gain_offset_put, -+ wsa885x_fu21_digital_gain), -+ -+ SOC_SINGLE_EXT("Rx Slot Mask", SND_SOC_NOPM, 0, 3, 0, -+ wsa885x_rx_slot_mask_get, -+ wsa885x_rx_slot_mask_put), -+}; -+ -+static const struct snd_soc_component_driver wsa885x_component = { -+ .name = "wsa885x", -+ .probe = wsa885x_component_probe, -+ .controls = wsa885x_snd_controls, -+ .num_controls = ARRAY_SIZE(wsa885x_snd_controls), -+}; -+ -+static irqreturn_t wsa885x_handle_irq(int irq_idx, void *data) -+{ -+ struct wsa885x_priv *wsa885x = data; -+ -+ if (irq_idx < 0 || irq_idx >= WSA885X_IRQ_MAX) -+ return IRQ_NONE; -+ -+ switch (irq_idx) { -+ case WSA885X_IRQ_INT_SAF2WAR: -+ case WSA885X_IRQ_INT_WAR2SAF: -+ case WSA885X_IRQ_INT_DISABLE: -+ case WSA885X_IRQ_INT_INTR_GPIO1_PIN: -+ case WSA885X_IRQ_INT_INTR_GPIO2_PIN: -+ case WSA885X_IRQ_INT_PA0_OCP: -+ case WSA885X_IRQ_INT_PA1_OCP: -+ case WSA885X_IRQ_INT_CLIP0: -+ case WSA885X_IRQ_INT_CLIP1: -+ case WSA885X_IRQ_INT_CLK_WD: -+ case WSA885X_IRQ_INT_BOP: -+ case WSA885X_IRQ_INT_UVLO: -+ case WSA885X_IRQ_INT_PCM_DATA0_DC: -+ case WSA885X_IRQ_INT_PCM_DATA1_DC: -+ case WSA885X_IRQ_INT_PLL_UNLOCKED: -+ case WSA885X_IRQ_INT_PROT_MODE_CHANGE: -+ case WSA885X_IRQ_INT_PB_CLOCK_VALID: -+ case WSA885X_IRQ_INT_SENSE_CLOCK_VALID: -+ break; -+ case WSA885X_IRQ_INT_PCM_DATA0_WD: -+ case WSA885X_IRQ_INT_PCM_DATA1_WD: -+ if (irq_idx == WSA885X_IRQ_INT_PCM_DATA0_WD) -+ wsa885x_toggle_irq_bit(wsa885x, WSA885X_DIG_CTRL0_PCM_DATA_WD0_CTL1, -+ WSA885X_PCM_DATA_WD_CTL1_PCM_DATA_WD_EN_MASK); -+ else -+ wsa885x_toggle_irq_bit(wsa885x, WSA885X_DIG_CTRL0_PCM_DATA_WD1_CTL1, -+ WSA885X_PCM_DATA_WD_CTL1_PCM_DATA_WD_EN_MASK); -+ break; -+ case WSA885X_IRQ_INT_PA0_FSM_ERR: -+ case WSA885X_IRQ_INT_PA1_FSM_ERR: -+ case WSA885X_IRQ_INT_MAIN_FSM_ERR: -+ if (irq_idx == WSA885X_IRQ_INT_MAIN_FSM_ERR) { -+ wsa885x_pulse_irq_bit(wsa885x, WSA885X_DIG_CTRL0_POWER_FSM_CTL0, -+ WSA885X_POWER_FSM_CTL0_CLEAR_ERROR_MASK); -+ } else if (irq_idx == WSA885X_IRQ_INT_PA0_FSM_ERR) { -+ wsa885x_pulse_irq_bit(wsa885x, WSA885X_PA0_FSM_CTL0, -+ WSA885X_PA_FSM_CTL0_CLEAR_ERROR_MASK); -+ } else if (irq_idx == WSA885X_IRQ_INT_PA1_FSM_ERR) { -+ wsa885x_pulse_irq_bit(wsa885x, WSA885X_PA1_FSM_CTL0, -+ WSA885X_PA_FSM_CTL0_CLEAR_ERROR_MASK); -+ } -+ break; -+ default: -+ break; -+ } -+ -+ return IRQ_HANDLED; -+} -+ -+static irqreturn_t wsa885x_interrupt_handler(int irq, void *data) -+{ -+ static const unsigned int status_reg[WSA885X_NUM_REGS] = { -+ WSA885X_INTR_STATUS0, -+ WSA885X_INTR_STATUS0 + 1, -+ WSA885X_INTR_STATUS0 + 2, -+ }; -+ static const unsigned int clear_reg[WSA885X_NUM_REGS] = { -+ WSA885X_INTR_CLEAR0, -+ WSA885X_INTR_CLEAR0 + 1, -+ WSA885X_INTR_CLEAR0 + 2, -+ }; -+ unsigned int status[WSA885X_NUM_REGS] = { 0 }; -+ struct wsa885x_priv *wsa885x = data; -+ irqreturn_t handled = IRQ_NONE; -+ irqreturn_t irq_ret; -+ int i, bit, ret, irq_num; -+ -+ for (i = 0; i < WSA885X_NUM_REGS; i++) { -+ ret = regmap_read(wsa885x->regmap, status_reg[i], &status[i]); -+ if (ret) { -+ dev_err(wsa885x->dev, -+ "Failed to read status_reg[%d] (0x%x): %d\n", -+ i, status_reg[i], ret); -+ status[i] = 0; -+ continue; -+ } -+ } -+ -+ for (i = 0; i < WSA885X_NUM_REGS; i++) { -+ for (bit = 0; bit < 8; bit++) { -+ if (status[i] & BIT(bit)) { -+ irq_num = i * 8 + bit; -+ regmap_write(wsa885x->regmap, clear_reg[i], BIT(bit)); -+ regmap_write(wsa885x->regmap, clear_reg[i], 0); -+ if (irq_num >= WSA885X_IRQ_MAX) { -+ dev_warn_ratelimited(wsa885x->dev, -+ "Unexpected IRQ bit %d (reg %d)\n", -+ bit, i); -+ handled = IRQ_HANDLED; -+ continue; -+ } -+ irq_ret = wsa885x_handle_irq(irq_num, wsa885x); -+ if (irq_ret == IRQ_HANDLED) -+ handled = IRQ_HANDLED; -+ } -+ } -+ } -+ return handled; -+} -+ -+static int wsa885x_register_irq(struct wsa885x_priv *wsa885x) -+{ -+ if (!wsa885x->client->irq) -+ return dev_err_probe(wsa885x->dev, -EINVAL, -+ "IRQ is not configured\n"); -+ -+ return devm_request_threaded_irq(wsa885x->dev, wsa885x->client->irq, NULL, -+ wsa885x_interrupt_handler, -+ IRQF_ONESHOT, -+ dev_name(wsa885x->dev), wsa885x); -+} -+ -+static int wsa885x_probe(struct i2c_client *client) -+{ -+ struct wsa885x_priv *wsa885x; -+ const struct snd_soc_component_driver *component_driver = &wsa885x_component; -+ const char *battery_config; -+ unsigned int i; -+ int ret; -+ struct device *dev = &client->dev; -+ -+ wsa885x = devm_kzalloc(dev, sizeof(*wsa885x), GFP_KERNEL); -+ if (!wsa885x) -+ return -ENOMEM; -+ -+ wsa885x->client = client; -+ wsa885x->dev = dev; -+ wsa885x->stereo_vol_db = -84; -+ wsa885x->rx_slot_mask = WSA885X_CHANNEL_STEREO; -+ mutex_init(&wsa885x->state_lock); -+ wsa885x->regmap = devm_regmap_init_i2c(client, &wsa885x_regmap_cfg); -+ -+ if (IS_ERR(wsa885x->regmap)) -+ return PTR_ERR(wsa885x->regmap); -+ -+ ret = device_property_read_string(dev, "qcom,battery-config", -+ &battery_config); -+ if (ret) { -+ wsa885x->batt_conf = WSA885X_BATT_1S; -+ } else if (!strcmp(battery_config, "1s")) { -+ wsa885x->batt_conf = WSA885X_BATT_1S; -+ } else if (!strcmp(battery_config, "2s")) { -+ wsa885x->batt_conf = WSA885X_BATT_2S; -+ } else { -+ return dev_err_probe(dev, -EINVAL, -+ "Invalid battery config %s (expected 1s or 2s)\n", -+ battery_config); -+ } -+ -+ for (i = 0; i < ARRAY_SIZE(wsa885x_supply_name); i++) { -+ ret = devm_regulator_get_enable(dev, wsa885x_supply_name[i]); -+ if (ret) -+ return dev_err_probe(dev, ret, -+ "Failed to enable regulator %s\n", -+ wsa885x_supply_name[i]); -+ } -+ -+ ret = wsa885x_get_reset(dev, wsa885x); -+ if (ret) -+ return ret; -+ -+ wsa885x_reset_deassert(wsa885x); -+ usleep_range(5000, 5500); -+ -+ ret = devm_add_action_or_reset(dev, wsa885x_reset_assert, wsa885x); -+ if (ret) -+ return dev_err_probe(dev, ret, "devm_add_action_or_reset failed\n"); -+ -+ i2c_set_clientdata(client, wsa885x); -+ -+ ret = wsa885x_register_irq(wsa885x); -+ if (ret) -+ return dev_err_probe(dev, ret, "wsa885x irq registration failed\n"); -+ -+ ret = devm_snd_soc_register_component(dev, component_driver, -+ wsa885x_dai, -+ ARRAY_SIZE(wsa885x_dai)); -+ if (ret) -+ return dev_err_probe(dev, ret, "Codec component registration failed\n"); -+ -+ return 0; -+} -+ -+static const struct of_device_id wsa885x_dt_match[] = { -+ { -+ .compatible = "qcom,wsa8855", -+ }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, wsa885x_dt_match); -+ -+static const struct i2c_device_id wsa885x_id[] = { -+ { -+ .name = "wsa885x", -+ .driver_data = 0, -+ }, -+ {} -+}; -+MODULE_DEVICE_TABLE(i2c, wsa885x_id); -+ -+static struct i2c_driver wsa885x_driver = { -+ .driver = { -+ .name = "wsa885x", -+ .of_match_table = wsa885x_dt_match, -+ }, -+ .probe = wsa885x_probe, -+ .id_table = wsa885x_id, -+}; -+ -+module_i2c_driver(wsa885x_driver); -+ -+MODULE_DESCRIPTION("ASoC WSA885X Stereo Smart PA Codec Driver"); -+MODULE_LICENSE("GPL"); -diff --git a/sound/soc/codecs/zl38060.c b/sound/soc/codecs/zl38060.c ---- a/sound/soc/codecs/zl38060.c -+++ b/sound/soc/codecs/zl38060.c -@@ -162,7 +162,7 @@ static int zl38_fw_send_xaddr(struct regmap *regmap, const void *data) - static int zl38_load_firmware(struct device *dev, struct regmap *regmap) - { - const struct ihex_binrec *rec; -- const struct firmware *fw; -+ const struct firmware *fw __free(firmware) = NULL; - u32 addr; - u16 len; - int err; -@@ -180,7 +180,7 @@ static int zl38_load_firmware(struct device *dev, struct regmap *regmap) - return err; - err = zl38_fw_enter_boot_mode(regmap); - if (err) -- goto out; -+ return err; - rec = (const struct ihex_binrec *)fw->data; - while (rec) { - addr = be32_to_cpu(rec->addr); -@@ -195,15 +195,12 @@ static int zl38_load_firmware(struct device *dev, struct regmap *regmap) - err = -EINVAL; - } - if (err) -- goto out; -+ return err; - /* next ! */ - rec = ihex_next_binrec(rec); - } -- err = zl38_fw_go(regmap); - --out: -- release_firmware(fw); -- return err; -+ return zl38_fw_go(regmap); - } - - -diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c ---- a/sound/soc/dwc/dwc-i2s.c -+++ b/sound/soc/dwc/dwc-i2s.c -@@ -955,7 +955,9 @@ static int dw_i2s_probe(struct platform_device *pdev) - } - - irq = platform_get_irq_optional(pdev, 0); -- if (irq >= 0) { -+ if (irq == -EPROBE_DEFER) -+ return irq; -+ if (irq > 0) { - ret = devm_request_irq(&pdev->dev, irq, i2s_irq_handler, 0, - pdev->name, dev); - if (ret < 0) { diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c -@@ -69,7 +69,6 @@ static const struct snd_pcm_hw_constraint_list cs42888_channel_constraints = { - - /** - * struct codec_priv - CODEC private data -- * @mclk: Main clock of the CODEC - * @mclk_freq: Clock rate of MCLK - * @free_freq: Clock rate of MCLK for hw_free() - * @mclk_id: MCLK (or main clock) id for set_sysclk() -@@ -80,7 +79,6 @@ static const struct snd_pcm_hw_constraint_list cs42888_channel_constraints = { - * to stay within PLL frequency limits - */ - struct codec_priv { -- struct clk *mclk; - unsigned long mclk_freq; - unsigned long free_freq; - u32 mclk_id; -@@ -109,12 +107,58 @@ struct cpu_priv { - u32 slot_num; - }; - -+struct fsl_asoc_card_priv; -+ -+/* -+ * struct fsl_asoc_card_pdata - per-compatible static card description -+ * @sysclk_dir: initial CPU SYSCLK direction override (0 = leave default IN) -+ * @sysclk_ratio: SYSCLK ratio on sample rate (0 = not used) -+ * @slot_width: TDM slot width (0 = not TDM) -+ * @codec_dai_name: name of the codec DAI -+ * @codec_mclk_id: MCLK id passed to set_sysclk() for the codec -+ * @codec_fll_id: FLL id; only valid when has_pll is true -+ * @codec_pll_id: PLL id; only valid when has_pll is true -+ * @codec_pll_ratio_s24: PLL output ratio for S24_LE -+ * @has_pll: codec uses PLL/FLL; codec_fll_id and codec_pll_id are valid -+ * @dai_fmt: DAI format flags -+ * @playback_only: restrict card to playback direction -+ * @capture_only: restrict card to capture direction -+ * @dapm_routes: DAPM route table override -+ * @num_dapm_routes: number of entries in dapm_routes -+ * @exclude_format: PCM format bitmask excluded (for SAI + WM8960/WM8962) -+ * @codec_init: codec-specific init run after mclk_freq is populated -+ * @probe_init: optional DT-driven init run at end of probe() (e.g. SPDIF codec discovery) -+ */ -+struct fsl_asoc_card_pdata { -+ u32 sysclk_dir[2]; -+ u32 sysclk_ratio[2]; -+ u32 slot_width; -+ const char *codec_dai_name; -+ u32 codec_mclk_id; -+ int codec_fll_id; -+ int codec_pll_id; -+ int codec_pll_ratio_s24; -+ bool has_pll; -+ bool playback_only; -+ bool capture_only; -+ u32 dai_fmt; -+ const struct snd_soc_dapm_route *dapm_routes; -+ int num_dapm_routes; -+ u64 exclude_format; -+ int (*codec_init)(struct fsl_asoc_card_priv *priv); -+ int (*probe_init)(struct device_node *codec_np[], -+ struct device_node *cpu_np, -+ const char *codec_dai_name[], -+ struct fsl_asoc_card_priv *priv); -+}; -+ - /** - * struct fsl_asoc_card_priv - Freescale Generic ASOC card private data - * @dai_link: DAI link structure including normal one and DPCM link - * @hp_jack: Headphone Jack structure - * @mic_jack: Microphone Jack structure - * @pdev: platform device pointer -+ * @pdata: pointer to the per-compatible card platform data - * @codec_priv: CODEC private data - * @cpu_priv: CPU private data - * @card: ASoC card structure -@@ -135,6 +179,7 @@ struct fsl_asoc_card_priv { - struct simple_util_jack hp_jack; - struct simple_util_jack mic_jack; - struct platform_device *pdev; -+ const struct fsl_asoc_card_pdata *pdata; - struct codec_priv codec_priv[2]; - struct cpu_priv cpu_priv; - struct snd_soc_card card; -@@ -609,6 +654,192 @@ static int fsl_asoc_card_spdif_init(struct device_node *codec_np[], - return 0; - } - -+static int fsl_asoc_card_cs42888_codec_init(struct fsl_asoc_card_priv *priv) -+{ -+ unsigned long mclk_freq = priv->codec_priv[0].mclk_freq; -+ -+ /* -+ * Set CPU sysclk frequency from codec MCLK only if not already -+ * set by the CPU DAI init (e.g. ESAI extal clock takes precedence). -+ */ -+ if (!priv->cpu_priv.sysclk_freq[TX]) -+ priv->cpu_priv.sysclk_freq[TX] = mclk_freq; -+ if (!priv->cpu_priv.sysclk_freq[RX]) -+ priv->cpu_priv.sysclk_freq[RX] = mclk_freq; -+ -+ priv->constraint_channels = &cs42888_channel_constraints; -+ if (mclk_freq % 12288000 == 0) -+ priv->constraint_rates = &cs42888_rate_48k_constraints; -+ else if (mclk_freq % 11289600 == 0) -+ priv->constraint_rates = &cs42888_rate_44k_constraints; -+ else -+ dev_warn(&priv->pdev->dev, -+ "Unknown MCLK frequency %lu, no rate constraints\n", -+ mclk_freq); -+ -+ return 0; -+} -+ -+static int fsl_asoc_card_wm8958_codec_init(struct fsl_asoc_card_priv *priv) -+{ -+ priv->codec_priv[0].free_freq = priv->codec_priv[0].mclk_freq; -+ return 0; -+} -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_cs42888_pdata = { -+ .codec_dai_name = "cs42888", -+ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBC_CFC, -+ .sysclk_dir = { SND_SOC_CLOCK_OUT, SND_SOC_CLOCK_OUT }, -+ .slot_width = 32, -+ .dapm_routes = audio_map, -+ .num_dapm_routes = ARRAY_SIZE(audio_map), -+ .codec_init = fsl_asoc_card_cs42888_codec_init, -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_cs427x_pdata = { -+ .codec_dai_name = "cs4271-hifi", -+ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, -+ .codec_mclk_id = CS427x_SYSCLK_MCLK, -+ .dapm_routes = audio_map, -+ .num_dapm_routes = ARRAY_SIZE(audio_map), -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_sgtl5000_pdata = { -+ .codec_dai_name = "sgtl5000", -+ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, -+ .codec_mclk_id = SGTL5000_SYSCLK, -+ .dapm_routes = audio_map, -+ .num_dapm_routes = ARRAY_SIZE(audio_map), -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_tlv320aic32x4_pdata = { -+ .codec_dai_name = "tlv320aic32x4-hifi", -+ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, -+ .dapm_routes = audio_map, -+ .num_dapm_routes = ARRAY_SIZE(audio_map), -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_tlv320aic31xx_pdata = { -+ .codec_dai_name = "tlv320dac31xx-hifi", -+ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBC_CFC, -+ .sysclk_dir = { SND_SOC_CLOCK_OUT, SND_SOC_CLOCK_OUT }, -+ .playback_only = true, -+ .dapm_routes = audio_map_tx, -+ .num_dapm_routes = ARRAY_SIZE(audio_map_tx), -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_wm8962_pdata = { -+ .codec_dai_name = "wm8962", -+ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, -+ .codec_mclk_id = WM8962_SYSCLK_MCLK, -+ .has_pll = true, -+ .codec_fll_id = WM8962_SYSCLK_FLL, -+ .codec_pll_id = WM8962_FLL, -+ /* -+ * WM8962 has same BCLK generation limitations as WM8960. -+ * See WM8960 section for detailed explanation. -+ */ -+ .exclude_format = SNDRV_PCM_FMTBIT_S20_3LE, -+ .dapm_routes = audio_map, -+ .num_dapm_routes = ARRAY_SIZE(audio_map), -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_wm8960_pdata = { -+ .codec_dai_name = "wm8960-hifi", -+ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, -+ .has_pll = true, -+ .codec_fll_id = WM8960_SYSCLK_AUTO, -+ .codec_pll_id = WM8960_SYSCLK_AUTO, -+ /* -+ * WM8960 in master mode cannot generate exact 1.92 MHz BCLK -+ * required for S20_3LE (48kHz x 2ch x 20bit). Closest available -+ * is 2.048 MHz (SYSCLK/6), which causes right channel corruption. -+ * -+ * In SAI master mode, SAI derive BCLK from MCLK using integer -+ * dividers only. S20_3LE requires non-integer divider ratios -+ * with standard MCLK frequencies. For example, 48kHz stereo -+ * needs 1.920 MHz BCLK, which requires a divider of 6.4 from -+ * 12.288 MHz MCLK (not an integer). -+ */ -+ .exclude_format = SNDRV_PCM_FMTBIT_S20_3LE, -+ .dapm_routes = audio_map, -+ .num_dapm_routes = ARRAY_SIZE(audio_map), -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_ac97_pdata = { -+ .codec_dai_name = "ac97-hifi", -+ .dai_fmt = SND_SOC_DAIFMT_AC97, -+ .dapm_routes = audio_map_ac97, -+ .num_dapm_routes = ARRAY_SIZE(audio_map_ac97), -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_mqs_pdata = { -+ .codec_dai_name = "fsl-mqs-dai", -+ .dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBC_CFC | -+ SND_SOC_DAIFMT_NB_NF, -+ .playback_only = true, -+ .dapm_routes = audio_map_tx, -+ .num_dapm_routes = ARRAY_SIZE(audio_map_tx), -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_wm8524_pdata = { -+ .codec_dai_name = "wm8524-hifi", -+ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBC_CFC, -+ /* RX=0, TX=1: set TX (index 1) to CLOCK_OUT, RX stays at default IN */ -+ .sysclk_dir = { 0, SND_SOC_CLOCK_OUT }, -+ .sysclk_ratio = { 0, 256 }, -+ .slot_width = 32, -+ .playback_only = true, -+ .dapm_routes = audio_map_tx, -+ .num_dapm_routes = ARRAY_SIZE(audio_map_tx), -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_si476x_pdata = { -+ .codec_dai_name = "si476x-codec", -+ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBC_CFC, -+ .dapm_routes = audio_map_rx, -+ .num_dapm_routes = ARRAY_SIZE(audio_map_rx), -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_wm8958_pdata = { -+ .codec_dai_name = "wm8994-aif1", -+ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, -+ .codec_mclk_id = WM8994_FLL_SRC_MCLK1, -+ .has_pll = true, -+ .codec_fll_id = WM8994_SYSCLK_FLL1, -+ .codec_pll_id = WM8994_FLL1, -+ .codec_init = fsl_asoc_card_wm8958_codec_init, -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_nau8822_pdata = { -+ .codec_dai_name = "nau8822-hifi", -+ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, -+ .codec_mclk_id = NAU8822_CLK_MCLK, -+ .has_pll = true, -+ .codec_fll_id = NAU8822_CLK_PLL, -+ .codec_pll_id = NAU8822_CLK_PLL, -+ .dapm_routes = audio_map, -+ .num_dapm_routes = ARRAY_SIZE(audio_map), -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_wm8904_pdata = { -+ .codec_dai_name = "wm8904-hifi", -+ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, -+ .codec_mclk_id = WM8904_FLL_MCLK, -+ .has_pll = true, -+ .codec_fll_id = WM8904_CLK_FLL, -+ .codec_pll_id = WM8904_FLL_MCLK, -+ .codec_pll_ratio_s24 = 192, -+ .dapm_routes = audio_map, -+ .num_dapm_routes = ARRAY_SIZE(audio_map), -+}; -+ -+static const struct fsl_asoc_card_pdata fsl_asoc_spdif_pdata = { -+ .codec_dai_name = "spdif", -+ .dai_fmt = DAI_FMT_BASE, -+ .probe_init = fsl_asoc_card_spdif_init, -+}; -+ - static int hp_jack_event(struct notifier_block *nb, unsigned long event, - void *data) - { -@@ -643,17 +874,173 @@ static struct notifier_block mic_jack_nb = { - .notifier_call = mic_jack_event, - }; - --static int fsl_asoc_card_late_probe(struct snd_soc_card *card) -+/* -+ * fsl_asoc_card_init_cpu - configure CPU DAI-specific settings. -+ * -+ * Called from late_probe() when the CPU DAI component is guaranteed bound. -+ */ -+static int fsl_asoc_card_init_cpu(struct snd_soc_card *card, -+ struct snd_soc_pcm_runtime *rtd) - { - struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); -- struct snd_soc_pcm_runtime *rtd = list_first_entry( -- &card->rtd_list, struct snd_soc_pcm_runtime, list); -+ struct device_node *np = priv->pdev->dev.of_node; -+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); -+ const char *comp_drv_name = cpu_dai->component->driver->name; -+ struct device *dev = card->dev; -+ int ret; -+ -+ if (!strcmp(comp_drv_name, "fsl-ssi")) { -+ /* Only SSI needs to configure AUDMUX */ -+ ret = fsl_asoc_card_audmux_init(np, priv); -+ if (ret) { -+ dev_err(dev, "failed to init audmux\n"); -+ return ret; -+ } -+ } else if (!strcmp(comp_drv_name, "fsl-esai")) { -+ struct clk *esai_clk = clk_get(cpu_dai->dev, "extal"); -+ -+ if (!IS_ERR(esai_clk)) { -+ priv->cpu_priv.sysclk_freq[TX] = clk_get_rate(esai_clk); -+ priv->cpu_priv.sysclk_freq[RX] = clk_get_rate(esai_clk); -+ clk_put(esai_clk); -+ } else { -+ dev_warn(dev, "failed to get ESAI extal clock: %ld\n", PTR_ERR(esai_clk)); -+ } -+ -+ priv->cpu_priv.sysclk_id[TX] = ESAI_HCKT_EXTAL; -+ priv->cpu_priv.sysclk_id[RX] = ESAI_HCKR_EXTAL; -+ } else if (!strcmp(comp_drv_name, "fsl-sai")) { -+ priv->cpu_priv.sysclk_id[TX] = FSL_SAI_CLK_MAST1; -+ priv->cpu_priv.sysclk_id[RX] = FSL_SAI_CLK_MAST1; -+ -+ if (priv->pdata->exclude_format) -+ priv->exclude_format = priv->pdata->exclude_format; -+ } -+ -+ return 0; -+} -+ -+/* -+ * fsl_asoc_card_init_codecs - read codec MCLK rates and set codec sysclk. -+ * -+ * Called from late_probe() after all components are bound. -+ */ -+static int fsl_asoc_card_init_codecs(struct snd_soc_card *card, -+ struct snd_soc_pcm_runtime *rtd) -+{ -+ struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); -+ const struct fsl_asoc_card_pdata *pdata = priv->pdata; - struct snd_soc_dai *codec_dai; - struct codec_priv *codec_priv; - struct device *dev = card->dev; - int codec_idx; - int ret; - -+ /* Read MCLK rate from each bound codec component */ -+ for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { -+ struct clk *codec_clk = clk_get(codec_dai->component->dev, NULL); -+ -+ codec_priv = &priv->codec_priv[codec_idx]; -+ if (!IS_ERR(codec_clk)) { -+ codec_priv->mclk_freq = clk_get_rate(codec_clk); -+ clk_put(codec_clk); -+ } -+ } -+ -+ if (pdata->codec_init) { -+ ret = pdata->codec_init(priv); -+ if (ret) -+ return ret; -+ } -+ -+ for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { -+ codec_priv = &priv->codec_priv[codec_idx]; -+ -+ ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, -+ codec_priv->mclk_freq, SND_SOC_CLOCK_IN); -+ if (ret && ret != -ENOTSUPP) { -+ dev_err(dev, "failed to set sysclk in %s\n", __func__); -+ return ret; -+ } -+ } -+ -+ return 0; -+} -+ -+static void fsl_asoc_card_free_jack(struct snd_soc_card *card) -+{ -+ struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); -+ -+ if (priv->hp_jack.gpio.desc) { -+ snd_soc_jack_notifier_unregister(&priv->hp_jack.jack, &hp_jack_nb); -+ snd_soc_jack_free_gpios(&priv->hp_jack.jack, 1, &priv->hp_jack.gpio); -+ priv->hp_jack.gpio.desc = NULL; -+ } -+ -+ if (priv->mic_jack.gpio.desc) { -+ snd_soc_jack_notifier_unregister(&priv->mic_jack.jack, &mic_jack_nb); -+ snd_soc_jack_free_gpios(&priv->mic_jack.jack, 1, &priv->mic_jack.gpio); -+ priv->mic_jack.gpio.desc = NULL; -+ } -+} -+ -+/* -+ * fsl_asoc_card_init_jack - register optional headphone and mic jacks. -+ * -+ * Called from late_probe() once per card bind cycle. -+ */ -+static int fsl_asoc_card_init_jack(struct snd_soc_card *card) -+{ -+ struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); -+ struct device_node *np = priv->pdev->dev.of_node; -+ int ret; -+ -+ /* -+ * Properties "hp-det-gpios" and "mic-det-gpios" are optional. -+ * simple_util_init_jack() checks for the GPIO property and -+ * does nothing if it is absent. -+ */ -+ if (of_property_present(np, "hp-det-gpios") || -+ of_property_present(np, "hp-det-gpio") /* deprecated */) { -+ ret = simple_util_init_jack(card, &priv->hp_jack, -+ 1, NULL, "Headphone Jack"); -+ if (ret) -+ return ret; -+ -+ snd_soc_jack_notifier_register(&priv->hp_jack.jack, &hp_jack_nb); -+ } -+ -+ if (of_property_present(np, "mic-det-gpios") || -+ of_property_present(np, "mic-det-gpio") /* deprecated */) { -+ ret = simple_util_init_jack(card, &priv->mic_jack, -+ 0, NULL, "Mic Jack"); -+ if (ret) -+ return ret; -+ -+ snd_soc_jack_notifier_register(&priv->mic_jack.jack, &mic_jack_nb); -+ } -+ -+ return 0; -+} -+ -+static int fsl_asoc_card_late_probe(struct snd_soc_card *card) -+{ -+ struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); -+ struct snd_soc_pcm_runtime *rtd; -+ int ret; -+ -+ /* Use the first rtd which carries the CPU+codec DAIs */ -+ rtd = list_first_entry(&card->rtd_list, -+ struct snd_soc_pcm_runtime, list); -+ -+ ret = fsl_asoc_card_init_jack(card); -+ if (ret) -+ goto jack_fail; -+ -+ ret = fsl_asoc_card_init_cpu(card, rtd); -+ if (ret) -+ goto jack_fail; -+ - if (fsl_asoc_card_is_ac97(priv)) { - #if IS_ENABLED(CONFIG_SND_AC97_CODEC) - struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component; -@@ -671,19 +1058,20 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card) - return 0; - } - -- for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { -- codec_priv = &priv->codec_priv[codec_idx]; -+ ret = fsl_asoc_card_init_codecs(card, rtd); -+ if (ret) -+ goto jack_fail; - -- ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, -- codec_priv->mclk_freq, SND_SOC_CLOCK_IN); -- if (ret && ret != -ENOTSUPP) { -- dev_err(dev, "failed to set sysclk in %s\n", __func__); -- return ret; -- } -+ return 0; - -- if (!IS_ERR_OR_NULL(codec_priv->mclk)) -- clk_prepare_enable(codec_priv->mclk); -- } -+jack_fail: -+ fsl_asoc_card_free_jack(card); -+ return ret; -+} -+ -+static int fsl_asoc_card_card_remove(struct snd_soc_card *card) -+{ -+ fsl_asoc_card_free_jack(card); - - return 0; - } -@@ -697,12 +1085,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) - struct platform_device *asrc_pdev = NULL; - struct device_node *bitclkprovider = NULL; - struct device_node *frameprovider = NULL; -- struct platform_device *cpu_pdev; - struct fsl_asoc_card_priv *priv; -- struct device *codec_dev[2] = { NULL, NULL }; -+ const struct fsl_asoc_card_pdata *pdata; - struct snd_soc_dai_link_component *dlc; -- const char *codec_dai_name[2]; -- const char *codec_dev_name[2]; -+ const char *codec_dai_name[2] = { NULL, NULL }; - u32 asrc_fmt = 0; - int codec_idx; - u32 width; -@@ -714,6 +1100,13 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) - - priv->pdev = pdev; - -+ pdata = of_device_get_match_data(&pdev->dev); -+ if (!pdata) { -+ dev_err(&pdev->dev, "unknown Device Tree compatible\n"); -+ return -EINVAL; -+ } -+ priv->pdata = pdata; -+ - cpu_np = of_parse_phandle(np, "audio-cpu", 0); - /* Give a chance to old DT bindings */ - if (!cpu_np) -@@ -726,52 +1119,13 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) - goto fail; - } - -- cpu_pdev = of_find_device_by_node(cpu_np); -- if (!cpu_pdev) { -- dev_err(&pdev->dev, "failed to find CPU DAI device\n"); -- ret = -EINVAL; -- goto fail; -- } -- - codec_np[0] = of_parse_phandle(np, "audio-codec", 0); - codec_np[1] = of_parse_phandle(np, "audio-codec", 1); - -- for (codec_idx = 0; codec_idx < 2; codec_idx++) { -- if (codec_np[codec_idx]) { -- struct platform_device *codec_pdev; -- struct i2c_client *codec_i2c; -- -- codec_i2c = of_find_i2c_device_by_node(codec_np[codec_idx]); -- if (codec_i2c) { -- codec_dev[codec_idx] = &codec_i2c->dev; -- codec_dev_name[codec_idx] = codec_i2c->name; -- } -- if (!codec_dev[codec_idx]) { -- codec_pdev = of_find_device_by_node(codec_np[codec_idx]); -- if (codec_pdev) { -- codec_dev[codec_idx] = &codec_pdev->dev; -- codec_dev_name[codec_idx] = codec_pdev->name; -- } -- } -- } -- } -- - asrc_np = of_parse_phandle(np, "audio-asrc", 0); - if (asrc_np) - asrc_pdev = of_find_device_by_node(asrc_np); - -- /* Get the MCLK rate only, and leave it controlled by CODEC drivers */ -- for (codec_idx = 0; codec_idx < 2; codec_idx++) { -- if (codec_dev[codec_idx]) { -- struct clk *codec_clk = clk_get(codec_dev[codec_idx], NULL); -- -- if (!IS_ERR(codec_clk)) { -- priv->codec_priv[codec_idx].mclk_freq = clk_get_rate(codec_clk); -- clk_put(codec_clk); -- } -- } -- } -- - /* Default sample rate and format, will be updated in hw_params() */ - priv->sample_rate = 44100; - priv->sample_format = SNDRV_PCM_FORMAT_S16_LE; -@@ -823,133 +1177,40 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) - } - - /* Diversify the card configurations */ -- if (of_device_is_compatible(np, "fsl,imx-audio-cs42888")) { -- codec_dai_name[0] = "cs42888"; -- priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv[0].mclk_freq; -- priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv[0].mclk_freq; -- priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; -- priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT; -- priv->cpu_priv.slot_width = 32; -- priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; -- priv->constraint_channels = &cs42888_channel_constraints; -- if (priv->codec_priv[0].mclk_freq % 12288000 == 0) -- priv->constraint_rates = &cs42888_rate_48k_constraints; -- else if (priv->codec_priv[0].mclk_freq % 11289600 == 0) -- priv->constraint_rates = &cs42888_rate_44k_constraints; -- else -- dev_warn(&pdev->dev, "Unknown MCLK frequency %lu, no rate constraints\n", -- priv->codec_priv[0].mclk_freq); -- } else if (of_device_is_compatible(np, "fsl,imx-audio-cs427x")) { -- codec_dai_name[0] = "cs4271-hifi"; -- priv->codec_priv[0].mclk_id = CS427x_SYSCLK_MCLK; -- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; -- } else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) { -- codec_dai_name[0] = "sgtl5000"; -- priv->codec_priv[0].mclk_id = SGTL5000_SYSCLK; -- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; -- } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic32x4")) { -- codec_dai_name[0] = "tlv320aic32x4-hifi"; -- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; -- } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic31xx")) { -- codec_dai_name[0] = "tlv320dac31xx-hifi"; -- priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; -+ priv->cpu_priv.sysclk_dir[TX] = pdata->sysclk_dir[TX]; -+ priv->cpu_priv.sysclk_dir[RX] = pdata->sysclk_dir[RX]; -+ priv->cpu_priv.sysclk_ratio[TX] = pdata->sysclk_ratio[TX]; -+ priv->cpu_priv.sysclk_ratio[RX] = pdata->sysclk_ratio[RX]; -+ priv->cpu_priv.slot_width = pdata->slot_width; -+ -+ codec_dai_name[0] = pdata->codec_dai_name; -+ priv->codec_priv[0].mclk_id = pdata->codec_mclk_id; -+ if (pdata->has_pll) { -+ priv->codec_priv[0].fll_id = pdata->codec_fll_id; -+ priv->codec_priv[0].pll_id = pdata->codec_pll_id; -+ } -+ if (pdata->codec_pll_ratio_s24) -+ priv->codec_priv[0].pll_ratio_s24 = pdata->codec_pll_ratio_s24; -+ -+ if (pdata->playback_only) { - priv->dai_link[1].playback_only = 1; - priv->dai_link[2].playback_only = 1; -- priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; -- priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT; -- priv->card.dapm_routes = audio_map_tx; -- priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); -- } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) { -- codec_dai_name[0] = "wm8962"; -- priv->codec_priv[0].mclk_id = WM8962_SYSCLK_MCLK; -- priv->codec_priv[0].fll_id = WM8962_SYSCLK_FLL; -- priv->codec_priv[0].pll_id = WM8962_FLL; -- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; -- /* -- * WM8962 has same BCLK generation limitations as WM8960. -- * See WM8960 section for detailed explanation. -- */ -- if (of_node_name_eq(cpu_np, "sai")) -- priv->exclude_format = SNDRV_PCM_FMTBIT_S20_3LE; -- } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8960")) { -- codec_dai_name[0] = "wm8960-hifi"; -- priv->codec_priv[0].fll_id = WM8960_SYSCLK_AUTO; -- priv->codec_priv[0].pll_id = WM8960_SYSCLK_AUTO; -- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; -- /* -- * WM8960 in master mode cannot generate exact 1.92 MHz BCLK -- * required for S20_3LE (48kHz × 2ch × 20bit). Closest available -- * is 2.048 MHz (SYSCLK/6), which causes right channel corruption. -- * -- * In SAI master mode, SAI derive BCLK from MCLK using integer -- * dividers only. S20_3LE requires non-integer divider ratios -- * with standard MCLK frequencies. For example, 48kHz stereo -- * needs 1.920 MHz BCLK, which requires a divider of 6.4 from -- * 12.288 MHz MCLK (not an integer). -- */ -- if (of_node_name_eq(cpu_np, "sai")) -- priv->exclude_format = SNDRV_PCM_FMTBIT_S20_3LE; -- } else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) { -- codec_dai_name[0] = "ac97-hifi"; -- priv->dai_fmt = SND_SOC_DAIFMT_AC97; -- priv->card.dapm_routes = audio_map_ac97; -- priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_ac97); -- } else if (of_device_is_compatible(np, "fsl,imx-audio-mqs")) { -- codec_dai_name[0] = "fsl-mqs-dai"; -- priv->dai_fmt = SND_SOC_DAIFMT_LEFT_J | -- SND_SOC_DAIFMT_CBC_CFC | -- SND_SOC_DAIFMT_NB_NF; -- priv->dai_link[1].playback_only = 1; -- priv->dai_link[2].playback_only = 1; -- priv->card.dapm_routes = audio_map_tx; -- priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); -- } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8524")) { -- codec_dai_name[0] = "wm8524-hifi"; -- priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; -- priv->dai_link[1].playback_only = 1; -- priv->dai_link[2].playback_only = 1; -- priv->cpu_priv.slot_width = 32; -- priv->card.dapm_routes = audio_map_tx; -- priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); -- priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; -- priv->cpu_priv.sysclk_ratio[TX] = 256; -- } else if (of_device_is_compatible(np, "fsl,imx-audio-si476x")) { -- codec_dai_name[0] = "si476x-codec"; -- priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; -- priv->card.dapm_routes = audio_map_rx; -- priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_rx); -- } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8958")) { -- codec_dai_name[0] = "wm8994-aif1"; -- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; -- priv->codec_priv[0].mclk_id = WM8994_FLL_SRC_MCLK1; -- priv->codec_priv[0].fll_id = WM8994_SYSCLK_FLL1; -- priv->codec_priv[0].pll_id = WM8994_FLL1; -- priv->codec_priv[0].free_freq = priv->codec_priv[0].mclk_freq; -- priv->card.dapm_routes = NULL; -- priv->card.num_dapm_routes = 0; -- } else if (of_device_is_compatible(np, "fsl,imx-audio-nau8822")) { -- codec_dai_name[0] = "nau8822-hifi"; -- priv->codec_priv[0].mclk_id = NAU8822_CLK_MCLK; -- priv->codec_priv[0].fll_id = NAU8822_CLK_PLL; -- priv->codec_priv[0].pll_id = NAU8822_CLK_PLL; -- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; -- if (codec_dev[0]) -- priv->codec_priv[0].mclk = devm_clk_get(codec_dev[0], NULL); -- } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8904")) { -- codec_dai_name[0] = "wm8904-hifi"; -- priv->codec_priv[0].mclk_id = WM8904_FLL_MCLK; -- priv->codec_priv[0].fll_id = WM8904_CLK_FLL; -- priv->codec_priv[0].pll_id = WM8904_FLL_MCLK; -- priv->codec_priv[0].pll_ratio_s24 = 192; -- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; -- } else if (of_device_is_compatible(np, "fsl,imx-audio-spdif")) { -- ret = fsl_asoc_card_spdif_init(codec_np, cpu_np, codec_dai_name, priv); -+ } -+ if (pdata->capture_only) { -+ priv->dai_link[1].capture_only = 1; -+ priv->dai_link[2].capture_only = 1; -+ } -+ -+ priv->dai_fmt = pdata->dai_fmt; -+ -+ priv->card.dapm_routes = pdata->dapm_routes; -+ priv->card.num_dapm_routes = pdata->num_dapm_routes; -+ -+ if (pdata->probe_init) { -+ ret = pdata->probe_init(codec_np, cpu_np, -+ codec_dai_name, priv); - if (ret) - goto asrc_fail; -- } else { -- dev_err(&pdev->dev, "unknown Device Tree compatible\n"); -- ret = -EINVAL; -- goto asrc_fail; - } - - /* -@@ -995,51 +1256,21 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) - of_node_put(bitclkprovider); - of_node_put(frameprovider); - -- if (!fsl_asoc_card_is_ac97(priv) && !codec_dev[0] -- && codec_dai_name[0] != snd_soc_dummy_dlc.dai_name) { -- dev_dbg(&pdev->dev, "failed to find codec device\n"); -- ret = -EPROBE_DEFER; -- goto asrc_fail; -- } -- -- /* Common settings for corresponding Freescale CPU DAI driver */ -- if (of_node_name_eq(cpu_np, "ssi")) { -- /* Only SSI needs to configure AUDMUX */ -- ret = fsl_asoc_card_audmux_init(np, priv); -- if (ret) { -- dev_err(&pdev->dev, "failed to init audmux\n"); -- goto asrc_fail; -- } -- } else if (of_node_name_eq(cpu_np, "esai")) { -- struct clk *esai_clk = clk_get(&cpu_pdev->dev, "extal"); -- -- if (!IS_ERR(esai_clk)) { -- priv->cpu_priv.sysclk_freq[TX] = clk_get_rate(esai_clk); -- priv->cpu_priv.sysclk_freq[RX] = clk_get_rate(esai_clk); -- clk_put(esai_clk); -- } else if (PTR_ERR(esai_clk) == -EPROBE_DEFER) { -- ret = -EPROBE_DEFER; -- goto asrc_fail; -- } -- -- priv->cpu_priv.sysclk_id[1] = ESAI_HCKT_EXTAL; -- priv->cpu_priv.sysclk_id[0] = ESAI_HCKR_EXTAL; -- } else if (of_node_name_eq(cpu_np, "sai")) { -- priv->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1; -- priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1; -- } -- - /* Initialize sound card */ - priv->card.dev = &pdev->dev; - priv->card.owner = THIS_MODULE; - ret = snd_soc_of_parse_card_name(&priv->card, "model"); - if (ret) { -- snprintf(priv->name, sizeof(priv->name), "%s-audio", -- fsl_asoc_card_is_ac97(priv) ? "ac97" : codec_dev_name[0]); -- priv->card.name = priv->name; -+ /* -+ * "model" is required by the DT binding. Enforce it here so -+ * the driver fails with a clear message. -+ */ -+ dev_err(&pdev->dev, "Error parsing card name: %d\n", ret); -+ goto asrc_fail; - } - priv->card.dai_link = priv->dai_link; - priv->card.late_probe = fsl_asoc_card_late_probe; -+ priv->card.remove = fsl_asoc_card_card_remove; - priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets; - priv->card.num_dapm_widgets = ARRAY_SIZE(fsl_asoc_card_dapm_widgets); - -@@ -1118,7 +1349,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) +@@ -1118,7 +1118,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) } ret = of_property_read_u32(asrc_np, "fsl,asrc-format", &asrc_fmt); @@ -34783,129 +11240,10 @@ diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c if (ret) { /* Fallback to old binding; translate to asrc_format */ ret = of_property_read_u32(asrc_np, "fsl,asrc-width", -@@ -1146,39 +1377,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) - goto asrc_fail; - } - -- /* -- * Properties "hp-det-gpios" and "mic-det-gpios" are optional, and -- * simple_util_init_jack() uses these properties for creating -- * Headphone Jack and Microphone Jack. -- * -- * The notifier is initialized in snd_soc_card_jack_new(), then -- * snd_soc_jack_notifier_register can be called. -- */ -- if (of_property_present(np, "hp-det-gpios") || -- of_property_present(np, "hp-det-gpio") /* deprecated */) { -- ret = simple_util_init_jack(&priv->card, &priv->hp_jack, -- 1, NULL, "Headphone Jack"); -- if (ret) -- goto asrc_fail; -- -- snd_soc_jack_notifier_register(&priv->hp_jack.jack, &hp_jack_nb); -- } -- -- if (of_property_present(np, "mic-det-gpios") || -- of_property_present(np, "mic-det-gpio") /* deprecated */) { -- ret = simple_util_init_jack(&priv->card, &priv->mic_jack, -- 0, NULL, "Mic Jack"); -- if (ret) -- goto asrc_fail; -- -- snd_soc_jack_notifier_register(&priv->mic_jack.jack, &mic_jack_nb); -- } -- - asrc_fail: - of_node_put(asrc_np); - of_node_put(codec_np[0]); - of_node_put(codec_np[1]); -- put_device(&cpu_pdev->dev); - fail: - of_node_put(cpu_np); - -@@ -1186,21 +1388,21 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) - } - - static const struct of_device_id fsl_asoc_card_dt_ids[] = { -- { .compatible = "fsl,imx-audio-ac97", }, -- { .compatible = "fsl,imx-audio-cs42888", }, -- { .compatible = "fsl,imx-audio-cs427x", }, -- { .compatible = "fsl,imx-audio-tlv320aic32x4", }, -- { .compatible = "fsl,imx-audio-tlv320aic31xx", }, -- { .compatible = "fsl,imx-audio-sgtl5000", }, -- { .compatible = "fsl,imx-audio-wm8962", }, -- { .compatible = "fsl,imx-audio-wm8960", }, -- { .compatible = "fsl,imx-audio-mqs", }, -- { .compatible = "fsl,imx-audio-wm8524", }, -- { .compatible = "fsl,imx-audio-si476x", }, -- { .compatible = "fsl,imx-audio-wm8958", }, -- { .compatible = "fsl,imx-audio-nau8822", }, -- { .compatible = "fsl,imx-audio-wm8904", }, -- { .compatible = "fsl,imx-audio-spdif", }, -+ { .compatible = "fsl,imx-audio-ac97", .data = &fsl_asoc_ac97_pdata }, -+ { .compatible = "fsl,imx-audio-cs42888", .data = &fsl_asoc_cs42888_pdata }, -+ { .compatible = "fsl,imx-audio-cs427x", .data = &fsl_asoc_cs427x_pdata }, -+ { .compatible = "fsl,imx-audio-tlv320aic32x4", .data = &fsl_asoc_tlv320aic32x4_pdata }, -+ { .compatible = "fsl,imx-audio-tlv320aic31xx", .data = &fsl_asoc_tlv320aic31xx_pdata }, -+ { .compatible = "fsl,imx-audio-sgtl5000", .data = &fsl_asoc_sgtl5000_pdata }, -+ { .compatible = "fsl,imx-audio-wm8962", .data = &fsl_asoc_wm8962_pdata }, -+ { .compatible = "fsl,imx-audio-wm8960", .data = &fsl_asoc_wm8960_pdata }, -+ { .compatible = "fsl,imx-audio-mqs", .data = &fsl_asoc_mqs_pdata }, -+ { .compatible = "fsl,imx-audio-wm8524", .data = &fsl_asoc_wm8524_pdata }, -+ { .compatible = "fsl,imx-audio-si476x", .data = &fsl_asoc_si476x_pdata }, -+ { .compatible = "fsl,imx-audio-wm8958", .data = &fsl_asoc_wm8958_pdata }, -+ { .compatible = "fsl,imx-audio-nau8822", .data = &fsl_asoc_nau8822_pdata }, -+ { .compatible = "fsl,imx-audio-wm8904", .data = &fsl_asoc_wm8904_pdata }, -+ { .compatible = "fsl,imx-audio-spdif", .data = &fsl_asoc_spdif_pdata }, - {} - }; - MODULE_DEVICE_TABLE(of, fsl_asoc_card_dt_ids); diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c -@@ -222,10 +222,9 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair) - enum asrc_pair_index index = ASRC_INVALID_PAIR; - struct fsl_asrc *asrc = pair->asrc; - struct device *dev = &asrc->pdev->dev; -- unsigned long lock_flags; - int i, ret = 0; - -- spin_lock_irqsave(&asrc->lock, lock_flags); -+ guard(spinlock_irqsave)(&asrc->lock); - - for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) { - if (asrc->pair[i] != NULL) -@@ -250,8 +249,6 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair) - pair->index = index; - } - -- spin_unlock_irqrestore(&asrc->lock, lock_flags); -- - return ret; - } - -@@ -265,19 +262,16 @@ static void fsl_asrc_release_pair(struct fsl_asrc_pair *pair) - { - struct fsl_asrc *asrc = pair->asrc; - enum asrc_pair_index index = pair->index; -- unsigned long lock_flags; - - /* Make sure the pair is disabled */ - regmap_update_bits(asrc->regmap, REG_ASRCTR, - ASRCTR_ASRCEi_MASK(index), 0); - -- spin_lock_irqsave(&asrc->lock, lock_flags); -+ guard(spinlock_irqsave)(&asrc->lock); - - asrc->channel_avail += pair->channels; - asrc->pair[index] = NULL; - pair->error = 0; -- -- spin_unlock_irqrestore(&asrc->lock, lock_flags); - } - - /** -@@ -1358,7 +1352,7 @@ static int fsl_asrc_probe(struct platform_device *pdev) +@@ -1358,7 +1358,7 @@ static int fsl_asrc_probe(struct platform_device *pdev) } ret = of_property_read_u32(np, "fsl,asrc-format", &asrc_fmt); @@ -34914,50 +11252,6 @@ diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c if (ret) { ret = of_property_read_u32(np, "fsl,asrc-width", &width); if (ret) { -diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h ---- a/sound/soc/fsl/fsl_asrc.h -+++ b/sound/soc/fsl/fsl_asrc.h -@@ -444,7 +444,7 @@ struct dma_block { - }; - - /** -- * fsl_asrc_soc_data: soc specific data -+ * struct fsl_asrc_soc_data - soc specific data - * - * @use_edma: using edma as dma device or not - * @channel_bits: width of ASRCNCR register for each pair -@@ -457,7 +457,7 @@ struct fsl_asrc_soc_data { - }; - - /** -- * fsl_asrc_pair_priv: ASRC Pair private data -+ * struct fsl_asrc_pair_priv - ASRC Pair private data - * - * @config: configuration profile - */ -@@ -466,7 +466,7 @@ struct fsl_asrc_pair_priv { - }; - - /** -- * fsl_asrc_priv: ASRC private data -+ * struct fsl_asrc_priv - ASRC private data - * - * @asrck_clk: clock sources to driver ASRC internal logic - * @soc: soc specific data -diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c ---- a/sound/soc/fsl/fsl_asrc_dma.c -+++ b/sound/soc/fsl/fsl_asrc_dma.c -@@ -219,7 +219,9 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component, - */ - component_be = snd_soc_lookup_component_nolocked(dev_be, SND_DMAENGINE_PCM_DRV_NAME); - if (component_be) { -- be_chan = soc_component_to_pcm(component_be)->chan[substream->stream]; -+ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component_be); -+ -+ be_chan = pcm->chan[substream->stream]; - tmp_chan = be_chan; - } - if (!tmp_chan) { diff --git a/sound/soc/fsl/fsl_asrc_m2m.c b/sound/soc/fsl/fsl_asrc_m2m.c --- a/sound/soc/fsl/fsl_asrc_m2m.c +++ b/sound/soc/fsl/fsl_asrc_m2m.c @@ -34988,307 +11282,10 @@ diff --git a/sound/soc/fsl/fsl_asrc_m2m.c b/sound/soc/fsl/fsl_asrc_m2m.c codec->descriptor[j].pcm_formats = cap.fmt_out; codec->descriptor[j].src.out_sample_rate_min = cap.rate_out[0]; codec->descriptor[j].src.out_sample_rate_max = -diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c ---- a/sound/soc/fsl/fsl_audmix.c -+++ b/sound/soc/fsl/fsl_audmix.c -@@ -286,7 +286,6 @@ static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) - { - struct fsl_audmix *priv = snd_soc_dai_get_drvdata(dai); -- unsigned long lock_flags; - - /* Capture stream shall not be handled */ - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) -@@ -296,16 +295,14 @@ static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd, - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: -- spin_lock_irqsave(&priv->lock, lock_flags); -- priv->tdms |= BIT(dai->driver->id); -- spin_unlock_irqrestore(&priv->lock, lock_flags); -+ scoped_guard(spinlock_irqsave, &priv->lock) -+ priv->tdms |= BIT(dai->driver->id); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: -- spin_lock_irqsave(&priv->lock, lock_flags); -- priv->tdms &= ~BIT(dai->driver->id); -- spin_unlock_irqrestore(&priv->lock, lock_flags); -+ scoped_guard(spinlock_irqsave, &priv->lock) -+ priv->tdms &= ~BIT(dai->driver->id); - break; - default: - return -EINVAL; -@@ -457,6 +454,9 @@ static const struct of_device_id fsl_audmix_ids[] = { - }; - MODULE_DEVICE_TABLE(of, fsl_audmix_ids); - -+static int fsl_audmix_runtime_resume(struct device *dev); -+static int fsl_audmix_runtime_suspend(struct device *dev); -+ - static int fsl_audmix_probe(struct platform_device *pdev) - { - struct device *dev = &pdev->dev; -@@ -488,13 +488,25 @@ static int fsl_audmix_probe(struct platform_device *pdev) - spin_lock_init(&priv->lock); - platform_set_drvdata(pdev, priv); - pm_runtime_enable(dev); -+ if (!pm_runtime_enabled(dev)) { -+ ret = fsl_audmix_runtime_resume(dev); -+ if (ret) -+ goto err_disable_pm; -+ } -+ -+ ret = pm_runtime_resume_and_get(dev); -+ if (ret < 0) -+ goto err_pm_get_sync; -+ -+ /* To enable regmap cache only when runtime PM enabled */ -+ pm_runtime_put(dev); - - ret = devm_snd_soc_register_component(dev, &fsl_audmix_component, - fsl_audmix_dai, - ARRAY_SIZE(fsl_audmix_dai)); - if (ret) { - dev_err(dev, "failed to register ASoC DAI\n"); -- goto err_disable_pm; -+ goto err_pm_get_sync; - } - - /* -@@ -506,12 +518,15 @@ static int fsl_audmix_probe(struct platform_device *pdev) - if (IS_ERR(priv->pdev)) { - ret = PTR_ERR(priv->pdev); - dev_err(dev, "failed to register platform: %d\n", ret); -- goto err_disable_pm; -+ goto err_pm_get_sync; - } - } - - return 0; - -+err_pm_get_sync: -+ if (!pm_runtime_status_suspended(dev)) -+ fsl_audmix_runtime_suspend(dev); - err_disable_pm: - pm_runtime_disable(dev); - return ret; -@@ -522,6 +537,8 @@ static void fsl_audmix_remove(struct platform_device *pdev) - struct fsl_audmix *priv = dev_get_drvdata(&pdev->dev); - - pm_runtime_disable(&pdev->dev); -+ if (!pm_runtime_status_suspended(&pdev->dev)) -+ fsl_audmix_runtime_suspend(&pdev->dev); - - if (priv->pdev) - platform_device_unregister(priv->pdev); -diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c ---- a/sound/soc/fsl/fsl_dma.c -+++ b/sound/soc/fsl/fsl_dma.c -@@ -18,8 +18,6 @@ - #include - #include - #include --#include --#include - #include - #include - -@@ -824,9 +822,34 @@ static int fsl_soc_dma_probe(struct platform_device *pdev) - struct device_node *np = pdev->dev.of_node; - struct device_node *ssi_np; - struct resource res; -+ void __iomem *channel; - const uint32_t *iprop; -+ int irq; - int ret; - -+ channel = devm_platform_ioremap_resource(pdev, 0); -+ if (IS_ERR(channel)) -+ return PTR_ERR(channel); -+ -+ irq = platform_get_irq(pdev, 0); -+ if (irq < 0) -+ return irq; -+ -+ dma = devm_kzalloc(&pdev->dev, sizeof(*dma), GFP_KERNEL); -+ if (!dma) -+ return -ENOMEM; -+ -+ dma->dai.name = DRV_NAME; -+ dma->dai.open = fsl_dma_open; -+ dma->dai.close = fsl_dma_close; -+ dma->dai.hw_params = fsl_dma_hw_params; -+ dma->dai.hw_free = fsl_dma_hw_free; -+ dma->dai.pointer = fsl_dma_pointer; -+ dma->dai.pcm_new = fsl_dma_new; -+ -+ dma->channel = channel; -+ dma->irq = irq; -+ - /* Find the SSI node that points to us. */ - ssi_np = find_ssi_node(np); - if (!ssi_np) { -@@ -842,55 +865,19 @@ static int fsl_soc_dma_probe(struct platform_device *pdev) - return ret; - } - -- dma = kzalloc_obj(*dma); -- if (!dma) { -- of_node_put(ssi_np); -- return -ENOMEM; -- } -- -- dma->dai.name = DRV_NAME; -- dma->dai.open = fsl_dma_open; -- dma->dai.close = fsl_dma_close; -- dma->dai.hw_params = fsl_dma_hw_params; -- dma->dai.hw_free = fsl_dma_hw_free; -- dma->dai.pointer = fsl_dma_pointer; -- dma->dai.pcm_new = fsl_dma_new; -- - /* Store the SSI-specific information that we need */ - dma->ssi_stx_phys = res.start + REG_SSI_STX0; - dma->ssi_srx_phys = res.start + REG_SSI_SRX0; - - iprop = of_get_property(ssi_np, "fsl,fifo-depth", NULL); -+ of_node_put(ssi_np); - if (iprop) - dma->ssi_fifo_depth = be32_to_cpup(iprop); - else - /* Older 8610 DTs didn't have the fifo-depth property */ - dma->ssi_fifo_depth = 8; - -- of_node_put(ssi_np); -- -- ret = devm_snd_soc_register_component(&pdev->dev, &dma->dai, NULL, 0); -- if (ret) { -- dev_err(&pdev->dev, "could not register platform\n"); -- kfree(dma); -- return ret; -- } -- -- dma->channel = of_iomap(np, 0); -- dma->irq = irq_of_parse_and_map(np, 0); -- -- dev_set_drvdata(&pdev->dev, dma); -- -- return 0; --} -- --static void fsl_soc_dma_remove(struct platform_device *pdev) --{ -- struct dma_object *dma = dev_get_drvdata(&pdev->dev); -- -- iounmap(dma->channel); -- irq_dispose_mapping(dma->irq); -- kfree(dma); -+ return devm_snd_soc_register_component(&pdev->dev, &dma->dai, NULL, 0); - } - - static const struct of_device_id fsl_soc_dma_ids[] = { -@@ -905,7 +892,6 @@ static struct platform_driver fsl_soc_dma_driver = { - .of_match_table = fsl_soc_dma_ids, - }, - .probe = fsl_soc_dma_probe, -- .remove = fsl_soc_dma_remove, - }; - - module_platform_driver(fsl_soc_dma_driver); -diff --git a/sound/soc/fsl/fsl_dma.h b/sound/soc/fsl/fsl_dma.h ---- a/sound/soc/fsl/fsl_dma.h -+++ b/sound/soc/fsl/fsl_dma.h -@@ -92,7 +92,7 @@ static inline u32 CCSR_DMA_ECLNDAR_ADDR(u64 x) - #define CCSR_DMA_ATR_SNOOP 0x00050000 - #define CCSR_DMA_ATR_ESAD_MASK 0x0000000F - --/** -+/* - * List Descriptor for extended chaining mode DMA operations. - * - * The CLSDAR register points to the first (in a linked-list) List -@@ -107,7 +107,7 @@ struct fsl_dma_list_descriptor { - u8 res[8]; /* Reserved */ - } __attribute__ ((aligned(32), packed)); - --/** -+/* - * Link Descriptor for basic and extended chaining mode DMA operations. - * - * A Link Descriptor points to a single DMA buffer. Each link descriptor diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c --- a/sound/soc/fsl/fsl_easrc.c +++ b/sound/soc/fsl/fsl_easrc.c -@@ -1025,7 +1025,6 @@ static int fsl_easrc_config_context(struct fsl_asrc *easrc, unsigned int ctx_id) - struct fsl_easrc_ctx_priv *ctx_priv; - struct fsl_asrc_pair *ctx; - struct device *dev; -- unsigned long lock_flags; - int ret; - - if (!easrc) -@@ -1053,9 +1052,8 @@ static int fsl_easrc_config_context(struct fsl_asrc *easrc, unsigned int ctx_id) - if (ret) - return ret; - -- spin_lock_irqsave(&easrc->lock, lock_flags); -- ret = fsl_easrc_config_slot(easrc, ctx->index); -- spin_unlock_irqrestore(&easrc->lock, lock_flags); -+ scoped_guard(spinlock_irqsave, &easrc->lock) -+ ret = fsl_easrc_config_slot(easrc, ctx->index); - if (ret) - return ret; - -@@ -1301,13 +1299,12 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx) - enum asrc_pair_index index = ASRC_INVALID_PAIR; - struct fsl_asrc *easrc = ctx->asrc; - struct device *dev; -- unsigned long lock_flags; - int ret = 0; - int i; - - dev = &easrc->pdev->dev; - -- spin_lock_irqsave(&easrc->lock, lock_flags); -+ guard(spinlock_irqsave)(&easrc->lock); - - for (i = ASRC_PAIR_A; i < EASRC_CTX_MAX_NUM; i++) { - if (easrc->pair[i]) -@@ -1331,8 +1328,6 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx) - easrc->channel_avail -= channels; - } - -- spin_unlock_irqrestore(&easrc->lock, lock_flags); -- - return ret; - } - -@@ -1343,7 +1338,6 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx) - */ - static void fsl_easrc_release_context(struct fsl_asrc_pair *ctx) - { -- unsigned long lock_flags; - struct fsl_asrc *easrc; - - if (!ctx) -@@ -1351,14 +1345,12 @@ static void fsl_easrc_release_context(struct fsl_asrc_pair *ctx) - - easrc = ctx->asrc; - -- spin_lock_irqsave(&easrc->lock, lock_flags); -+ guard(spinlock_irqsave)(&easrc->lock); - - fsl_easrc_release_slot(easrc, ctx->index); - - easrc->channel_avail += ctx->channels; - easrc->pair[ctx->index] = NULL; -- -- spin_unlock_irqrestore(&easrc->lock, lock_flags); - } - - /* -@@ -2227,7 +2219,7 @@ static int fsl_easrc_probe(struct platform_device *pdev) +@@ -2227,7 +2227,7 @@ static int fsl_easrc_probe(struct platform_device *pdev) } ret = of_property_read_u32(np, "fsl,asrc-format", &asrc_fmt); @@ -35297,134 +11294,6 @@ diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c if (ret) { dev_err(dev, "failed to asrc format\n"); return ret; -@@ -2292,15 +2284,13 @@ static int fsl_easrc_runtime_suspend(struct device *dev) - { - struct fsl_asrc *easrc = dev_get_drvdata(dev); - struct fsl_easrc_priv *easrc_priv = easrc->private; -- unsigned long lock_flags; - - regcache_cache_only(easrc->regmap, true); - - clk_disable_unprepare(easrc->mem_clk); - -- spin_lock_irqsave(&easrc->lock, lock_flags); -- easrc_priv->firmware_loaded = 0; -- spin_unlock_irqrestore(&easrc->lock, lock_flags); -+ scoped_guard(spinlock_irqsave, &easrc->lock) -+ easrc_priv->firmware_loaded = 0; - - return 0; - } -@@ -2311,7 +2301,6 @@ static int fsl_easrc_runtime_resume(struct device *dev) - struct fsl_easrc_priv *easrc_priv = easrc->private; - struct fsl_easrc_ctx_priv *ctx_priv; - struct fsl_asrc_pair *ctx; -- unsigned long lock_flags; - int ret; - int i; - -@@ -2323,13 +2312,11 @@ static int fsl_easrc_runtime_resume(struct device *dev) - regcache_mark_dirty(easrc->regmap); - regcache_sync(easrc->regmap); - -- spin_lock_irqsave(&easrc->lock, lock_flags); -- if (easrc_priv->firmware_loaded) { -- spin_unlock_irqrestore(&easrc->lock, lock_flags); -- goto skip_load; -+ scoped_guard(spinlock_irqsave, &easrc->lock) { -+ if (easrc_priv->firmware_loaded) -+ return 0; -+ easrc_priv->firmware_loaded = 1; - } -- easrc_priv->firmware_loaded = 1; -- spin_unlock_irqrestore(&easrc->lock, lock_flags); - - ret = fsl_easrc_get_firmware(easrc); - if (ret) { -@@ -2377,9 +2364,7 @@ static int fsl_easrc_runtime_resume(struct device *dev) - goto disable_mem_clk; - } - --skip_load: - return 0; -- - disable_mem_clk: - clk_disable_unprepare(easrc->mem_clk); - return ret; -diff --git a/sound/soc/fsl/fsl_easrc.h b/sound/soc/fsl/fsl_easrc.h ---- a/sound/soc/fsl/fsl_easrc.h -+++ b/sound/soc/fsl/fsl_easrc.h -@@ -584,7 +584,7 @@ struct fsl_easrc_slot { - }; - - /** -- * fsl_easrc_ctx_priv: EASRC context private data -+ * struct fsl_easrc_ctx_priv - EASRC context private data - * - * @in_params: input parameter - * @out_params: output parameter -@@ -625,7 +625,7 @@ struct fsl_easrc_ctx_priv { - }; - - /** -- * fsl_easrc_priv: EASRC private data -+ * struct fsl_easrc_priv - EASRC private data - * - * @slot: slot setting - * @firmware_hdr: the header of firmware -diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c ---- a/sound/soc/fsl/fsl_esai.c -+++ b/sound/soc/fsl/fsl_esai.c -@@ -709,10 +709,9 @@ static void fsl_esai_hw_reset(struct work_struct *work) - { - struct fsl_esai *esai_priv = container_of(work, struct fsl_esai, work); - bool tx = true, rx = false, enabled[2]; -- unsigned long lock_flags; - u32 tfcr, rfcr; - -- spin_lock_irqsave(&esai_priv->lock, lock_flags); -+ guard(spinlock_irqsave)(&esai_priv->lock); - /* Save the registers */ - regmap_read(esai_priv->regmap, REG_ESAI_TFCR, &tfcr); - regmap_read(esai_priv->regmap, REG_ESAI_RFCR, &rfcr); -@@ -750,8 +749,6 @@ static void fsl_esai_hw_reset(struct work_struct *work) - fsl_esai_trigger_start(esai_priv, tx); - if (enabled[rx]) - fsl_esai_trigger_start(esai_priv, rx); -- -- spin_unlock_irqrestore(&esai_priv->lock, lock_flags); - } - - static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, -@@ -759,7 +756,6 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, - { - struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); - bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; -- unsigned long lock_flags; - - esai_priv->channels[tx] = substream->runtime->channels; - -@@ -767,16 +763,14 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: -- spin_lock_irqsave(&esai_priv->lock, lock_flags); -- fsl_esai_trigger_start(esai_priv, tx); -- spin_unlock_irqrestore(&esai_priv->lock, lock_flags); -+ scoped_guard(spinlock_irqsave, &esai_priv->lock) -+ fsl_esai_trigger_start(esai_priv, tx); - break; - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: -- spin_lock_irqsave(&esai_priv->lock, lock_flags); -- fsl_esai_trigger_stop(esai_priv, tx); -- spin_unlock_irqrestore(&esai_priv->lock, lock_flags); -+ scoped_guard(spinlock_irqsave, &esai_priv->lock) -+ fsl_esai_trigger_stop(esai_priv, tx); - break; - default: - return -EINVAL; diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c --- a/sound/soc/fsl/fsl_qmc_audio.c +++ b/sound/soc/fsl/fsl_qmc_audio.c @@ -35450,318 +11319,6 @@ diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c ret = snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_ACCESS, access); if (ret) { -diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c ---- a/sound/soc/fsl/fsl_spdif.c -+++ b/sound/soc/fsl/fsl_spdif.c -@@ -853,17 +853,15 @@ static int fsl_spdif_subcode_get(struct snd_kcontrol *kcontrol, - struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); - struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); - struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; -- unsigned long flags; - int ret = -EAGAIN; - -- spin_lock_irqsave(&ctrl->ctl_lock, flags); -+ guard(spinlock_irqsave)(&ctrl->ctl_lock); - if (ctrl->ready_buf) { - int idx = (ctrl->ready_buf - 1) * SPDIF_UBITS_SIZE; - memcpy(&ucontrol->value.iec958.subcode[0], - &ctrl->subcode[idx], SPDIF_UBITS_SIZE); - ret = 0; - } -- spin_unlock_irqrestore(&ctrl->ctl_lock, flags); - - return ret; - } -@@ -885,17 +883,15 @@ static int fsl_spdif_qget(struct snd_kcontrol *kcontrol, - struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); - struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); - struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; -- unsigned long flags; - int ret = -EAGAIN; - -- spin_lock_irqsave(&ctrl->ctl_lock, flags); -+ guard(spinlock_irqsave)(&ctrl->ctl_lock); - if (ctrl->ready_buf) { - int idx = (ctrl->ready_buf - 1) * SPDIF_QSUB_SIZE; - memcpy(&ucontrol->value.bytes.data[0], - &ctrl->qsub[idx], SPDIF_QSUB_SIZE); - ret = 0; - } -- spin_unlock_irqrestore(&ctrl->ctl_lock, flags); - - return ret; - } -diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c ---- a/sound/soc/fsl/fsl_ssi.c -+++ b/sound/soc/fsl/fsl_ssi.c -@@ -1218,13 +1218,13 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, - if (reg > 0x7f) - return; - -- mutex_lock(&fsl_ac97_data->ac97_reg_lock); -+ guard(mutex)(&fsl_ac97_data->ac97_reg_lock); - - ret = clk_prepare_enable(fsl_ac97_data->clk); - if (ret) { - pr_err("ac97 write clk_prepare_enable failed: %d\n", - ret); -- goto ret_unlock; -+ return; - } - - lreg = reg << 12; -@@ -1238,9 +1238,6 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, - udelay(100); - - clk_disable_unprepare(fsl_ac97_data->clk); -- --ret_unlock: -- mutex_unlock(&fsl_ac97_data->ac97_reg_lock); - } - - static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, -@@ -1252,12 +1249,12 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, - unsigned int lreg; - int ret; - -- mutex_lock(&fsl_ac97_data->ac97_reg_lock); -+ guard(mutex)(&fsl_ac97_data->ac97_reg_lock); - - ret = clk_prepare_enable(fsl_ac97_data->clk); - if (ret) { - pr_err("ac97 read clk_prepare_enable failed: %d\n", ret); -- goto ret_unlock; -+ return val; - } - - lreg = (reg & 0x7f) << 12; -@@ -1272,8 +1269,6 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, - - clk_disable_unprepare(fsl_ac97_data->clk); - --ret_unlock: -- mutex_unlock(&fsl_ac97_data->ac97_reg_lock); - return val; - } - -diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c ---- a/sound/soc/fsl/fsl_xcvr.c -+++ b/sound/soc/fsl/fsl_xcvr.c -@@ -797,10 +797,9 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, - { - struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); - bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; -- unsigned long lock_flags; - int ret = 0; - -- spin_lock_irqsave(&xcvr->lock, lock_flags); -+ guard(spinlock_irqsave)(&xcvr->lock); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: -@@ -812,7 +811,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, - FSL_XCVR_EXT_CTRL_DPTH_RESET(tx)); - if (ret < 0) { - dev_err(dai->dev, "Failed to set DPATH RESET: %d\n", ret); -- goto release_lock; -+ return ret; - } - - if (tx) { -@@ -824,7 +823,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, - FSL_XCVR_ISR_CMDC_TX_EN); - if (ret < 0) { - dev_err(dai->dev, "err updating isr %d\n", ret); -- goto release_lock; -+ return ret; - } - fallthrough; - case FSL_XCVR_MODE_SPDIF: -@@ -833,7 +832,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, - FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX); - if (ret < 0) { - dev_err(dai->dev, "Failed to start DATA_TX: %d\n", ret); -- goto release_lock; -+ return ret; - } - break; - } -@@ -844,14 +843,14 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, - FSL_XCVR_EXT_CTRL_DMA_DIS(tx), 0); - if (ret < 0) { - dev_err(dai->dev, "Failed to enable DMA: %d\n", ret); -- goto release_lock; -+ return ret; - } - - ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0, - FSL_XCVR_IRQ_EARC_ALL, FSL_XCVR_IRQ_EARC_ALL); - if (ret < 0) { - dev_err(dai->dev, "Error while setting IER0: %d\n", ret); -- goto release_lock; -+ return ret; - } - - /* clear DPATH RESET */ -@@ -860,7 +859,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, - 0); - if (ret < 0) { - dev_err(dai->dev, "Failed to clear DPATH RESET: %d\n", ret); -- goto release_lock; -+ return ret; - } - - break; -@@ -873,14 +872,14 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, - FSL_XCVR_EXT_CTRL_DMA_DIS(tx)); - if (ret < 0) { - dev_err(dai->dev, "Failed to disable DMA: %d\n", ret); -- goto release_lock; -+ return ret; - } - - ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0, - FSL_XCVR_IRQ_EARC_ALL, 0); - if (ret < 0) { - dev_err(dai->dev, "Failed to clear IER0: %d\n", ret); -- goto release_lock; -+ return ret; - } - - if (tx) { -@@ -891,7 +890,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, - FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX); - if (ret < 0) { - dev_err(dai->dev, "Failed to stop DATA_TX: %d\n", ret); -- goto release_lock; -+ return ret; - } - if (xcvr->soc_data->spdif_only) - break; -@@ -905,7 +904,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, - if (ret < 0) { - dev_err(dai->dev, - "Err updating ISR %d\n", ret); -- goto release_lock; -+ return ret; - } - break; - } -@@ -916,18 +915,16 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, - break; - } - --release_lock: -- spin_unlock_irqrestore(&xcvr->lock, lock_flags); - return ret; - } - - static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) - { - struct device *dev = &xcvr->pdev->dev; -- const struct firmware *fw; - int ret = 0, rem, off, out, page = 0, size = FSL_XCVR_REG_OFFSET; - u32 mask, val; - -+ const struct firmware *fw __free(firmware) = NULL; - ret = request_firmware(&fw, xcvr->soc_data->fw_name, dev); - if (ret) { - dev_err(dev, "failed to request firmware.\n"); -@@ -939,7 +936,6 @@ static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) - /* RAM is 20KiB = 16KiB code + 4KiB data => max 10 pages 2KiB each */ - if (rem > 16384) { - dev_err(dev, "FW size %d is bigger than 16KiB.\n", rem); -- release_firmware(fw); - return -ENOMEM; - } - -@@ -950,7 +946,7 @@ static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) - if (ret < 0) { - dev_err(dev, "FW: failed to set page %d, err=%d\n", - page, ret); -- goto err_firmware; -+ return ret; - } - - off = page * size; -@@ -971,11 +967,6 @@ static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) - } - } - --err_firmware: -- release_firmware(fw); -- if (ret < 0) -- return ret; -- - /* configure watermarks */ - mask = FSL_XCVR_EXT_CTRL_RX_FWM_MASK | FSL_XCVR_EXT_CTRL_TX_FWM_MASK; - val = FSL_XCVR_EXT_CTRL_RX_FWM(FSL_XCVR_FIFO_WMK_RX); -@@ -1448,11 +1439,10 @@ static void reset_rx_work(struct work_struct *work) - { - struct fsl_xcvr *xcvr = container_of(work, struct fsl_xcvr, work_rst); - struct device *dev = &xcvr->pdev->dev; -- unsigned long lock_flags; - u32 ext_ctrl; - - dev_dbg(dev, "reset rx path\n"); -- spin_lock_irqsave(&xcvr->lock, lock_flags); -+ guard(spinlock_irqsave)(&xcvr->lock); - regmap_read(xcvr->regmap, FSL_XCVR_EXT_CTRL, &ext_ctrl); - - if (!(ext_ctrl & FSL_XCVR_EXT_CTRL_DMA_RD_DIS)) { -@@ -1469,7 +1459,6 @@ static void reset_rx_work(struct work_struct *work) - FSL_XCVR_EXT_CTRL_RX_DPTH_RESET, - 0); - } -- spin_unlock_irqrestore(&xcvr->lock, lock_flags); - } - - static irqreturn_t irq0_isr(int irq, void *devid) -diff --git a/sound/soc/fsl/imx-audio-rpmsg.c b/sound/soc/fsl/imx-audio-rpmsg.c ---- a/sound/soc/fsl/imx-audio-rpmsg.c -+++ b/sound/soc/fsl/imx-audio-rpmsg.c -@@ -22,7 +22,6 @@ static int imx_audio_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, - struct rpmsg_r_msg *r_msg = (struct rpmsg_r_msg *)data; - struct rpmsg_info *info; - struct rpmsg_msg *msg; -- unsigned long flags; - - if (!rpmsg->rpmsg_pdev) - return 0; -@@ -37,21 +36,21 @@ static int imx_audio_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, - /* TYPE C is notification from M core */ - switch (r_msg->header.cmd) { - case TX_PERIOD_DONE: -- spin_lock_irqsave(&info->lock[TX], flags); -- msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM]; -- msg->r_msg.param.buffer_tail = -- r_msg->param.buffer_tail; -- msg->r_msg.param.buffer_tail %= info->num_period[TX]; -- spin_unlock_irqrestore(&info->lock[TX], flags); -+ scoped_guard(spinlock_irqsave, &info->lock[TX]) { -+ msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM]; -+ msg->r_msg.param.buffer_tail = -+ r_msg->param.buffer_tail; -+ msg->r_msg.param.buffer_tail %= info->num_period[TX]; -+ } - info->callback[TX](info->callback_param[TX]); - break; - case RX_PERIOD_DONE: -- spin_lock_irqsave(&info->lock[RX], flags); -- msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM]; -- msg->r_msg.param.buffer_tail = -- r_msg->param.buffer_tail; -- msg->r_msg.param.buffer_tail %= info->num_period[1]; -- spin_unlock_irqrestore(&info->lock[RX], flags); -+ scoped_guard(spinlock_irqsave, &info->lock[RX]) { -+ msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM]; -+ msg->r_msg.param.buffer_tail = -+ r_msg->param.buffer_tail; -+ msg->r_msg.param.buffer_tail %= info->num_period[1]; -+ } - info->callback[RX](info->callback_param[RX]); - break; - default: diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c --- a/sound/soc/fsl/imx-card.c +++ b/sound/soc/fsl/imx-card.c @@ -35783,2271 +11340,6 @@ diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c if (ret) { /* Fallback to old binding; translate to asrc_format */ ret = of_property_read_u32(args.np, "fsl,asrc-width", &width); -diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c ---- a/sound/soc/fsl/imx-pcm-rpmsg.c -+++ b/sound/soc/fsl/imx-pcm-rpmsg.c -@@ -39,10 +39,9 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, - struct rpmsg_device *rpdev = info->rpdev; - int ret = 0; - -- mutex_lock(&info->msg_lock); -+ guard(mutex)(&info->msg_lock); - if (!rpdev) { - dev_err(info->dev, "rpmsg channel not ready\n"); -- mutex_unlock(&info->msg_lock); - return -EINVAL; - } - -@@ -55,15 +54,12 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, - sizeof(struct rpmsg_s_msg)); - if (ret) { - dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret); -- mutex_unlock(&info->msg_lock); - return ret; - } - - /* No receive msg for TYPE_C command */ -- if (msg->s_msg.header.type == MSG_TYPE_C) { -- mutex_unlock(&info->msg_lock); -+ if (msg->s_msg.header.type == MSG_TYPE_C) - return 0; -- } - - /* wait response from rpmsg */ - ret = wait_for_completion_timeout(&info->cmd_complete, -@@ -71,7 +67,6 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, - if (!ret) { - dev_err(&rpdev->dev, "rpmsg_send cmd %d timeout!\n", - msg->s_msg.header.cmd); -- mutex_unlock(&info->msg_lock); - return -ETIMEDOUT; - } - -@@ -100,8 +95,6 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, - dev_dbg(&rpdev->dev, "cmd:%d, resp %d\n", msg->s_msg.header.cmd, - info->r_msg.param.resp); - -- mutex_unlock(&info->msg_lock); -- - return 0; - } - -@@ -109,14 +102,13 @@ static int imx_rpmsg_insert_workqueue(struct snd_pcm_substream *substream, - struct rpmsg_msg *msg, - struct rpmsg_info *info) - { -- unsigned long flags; - int ret = 0; - - /* - * Queue the work to workqueue. - * If the queue is full, drop the message. - */ -- spin_lock_irqsave(&info->wq_lock, flags); -+ guard(spinlock_irqsave)(&info->wq_lock); - if (info->work_write_index != info->work_read_index) { - int index = info->work_write_index; - -@@ -130,7 +122,6 @@ static int imx_rpmsg_insert_workqueue(struct snd_pcm_substream *substream, - info->msg_drop_count[substream->stream]++; - ret = -EPIPE; - } -- spin_unlock_irqrestore(&info->wq_lock, flags); - - return ret; - } -@@ -523,7 +514,6 @@ static int imx_rpmsg_pcm_ack(struct snd_soc_component *component, - snd_pcm_sframes_t avail; - struct timer_list *timer; - struct rpmsg_msg *msg; -- unsigned long flags; - int buffer_tail = 0; - int written_num; - -@@ -553,11 +543,11 @@ static int imx_rpmsg_pcm_ack(struct snd_soc_component *component, - msg->s_msg.param.buffer_tail = buffer_tail; - - /* The notification message is updated to latest */ -- spin_lock_irqsave(&info->lock[substream->stream], flags); -- memcpy(&info->notify[substream->stream], msg, -- sizeof(struct rpmsg_s_msg)); -- info->notify_updated[substream->stream] = true; -- spin_unlock_irqrestore(&info->lock[substream->stream], flags); -+ scoped_guard(spinlock_irqsave, &info->lock[substream->stream]) { -+ memcpy(&info->notify[substream->stream], msg, -+ sizeof(struct rpmsg_s_msg)); -+ info->notify_updated[substream->stream] = true; -+ } - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - avail = snd_pcm_playback_hw_avail(runtime); -@@ -641,7 +631,7 @@ static void imx_rpmsg_pcm_work(struct work_struct *work) - bool is_notification = false; - struct rpmsg_info *info; - struct rpmsg_msg msg; -- unsigned long flags; -+ bool updated; - - work_of_rpmsg = container_of(work, struct work_of_rpmsg, work); - info = work_of_rpmsg->info; -@@ -652,25 +642,26 @@ static void imx_rpmsg_pcm_work(struct work_struct *work) - * enough data in M core side, need to let M core know - * data is updated immediately. - */ -- spin_lock_irqsave(&info->lock[TX], flags); -- if (info->notify_updated[TX]) { -- memcpy(&msg, &info->notify[TX], sizeof(struct rpmsg_s_msg)); -- info->notify_updated[TX] = false; -- spin_unlock_irqrestore(&info->lock[TX], flags); -- info->send_message(&msg, info); -- } else { -- spin_unlock_irqrestore(&info->lock[TX], flags); -+ scoped_guard(spinlock_irqsave, &info->lock[TX]) { -+ updated = info->notify_updated[TX]; -+ if (updated) { -+ memcpy(&msg, &info->notify[TX], sizeof(struct rpmsg_s_msg)); -+ info->notify_updated[TX] = false; -+ } - } -+ if (updated) -+ info->send_message(&msg, info); - -- spin_lock_irqsave(&info->lock[RX], flags); -- if (info->notify_updated[RX]) { -- memcpy(&msg, &info->notify[RX], sizeof(struct rpmsg_s_msg)); -- info->notify_updated[RX] = false; -- spin_unlock_irqrestore(&info->lock[RX], flags); -- info->send_message(&msg, info); -- } else { -- spin_unlock_irqrestore(&info->lock[RX], flags); -+ scoped_guard(spinlock_irqsave, &info->lock[RX]) { -+ updated = info->notify_updated[RX]; -+ if (updated) { -+ memcpy(&msg, &info->notify[RX], sizeof(struct rpmsg_s_msg)); -+ info->notify_updated[RX] = false; -+ } - } -+ if (updated) -+ info->send_message(&msg, info); -+ - - /* Skip the notification message for it has been processed above */ - if (work_of_rpmsg->msg.s_msg.header.type == MSG_TYPE_C && -@@ -682,10 +673,10 @@ static void imx_rpmsg_pcm_work(struct work_struct *work) - info->send_message(&work_of_rpmsg->msg, info); - - /* update read index */ -- spin_lock_irqsave(&info->wq_lock, flags); -- info->work_read_index++; -- info->work_read_index %= WORK_MAX_NUM; -- spin_unlock_irqrestore(&info->wq_lock, flags); -+ scoped_guard(spinlock_irqsave, &info->wq_lock) { -+ info->work_read_index++; -+ info->work_read_index %= WORK_MAX_NUM; -+ } - } - - static int imx_rpmsg_pcm_probe(struct platform_device *pdev) -@@ -819,9 +810,9 @@ static const struct dev_pm_ops imx_rpmsg_pcm_pm_ops = { - }; - - static const struct platform_device_id imx_rpmsg_pcm_id_table[] = { -- { .name = "rpmsg-audio-channel" }, -- { .name = "rpmsg-micfil-channel" }, -- { }, -+ { .name = "rpmsg-audio-channel" }, -+ { .name = "rpmsg-micfil-channel" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, imx_rpmsg_pcm_id_table); - -diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c ---- a/sound/soc/fsl/mpc5200_dma.c -+++ b/sound/soc/fsl/mpc5200_dma.c -@@ -77,18 +77,20 @@ static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream) - { - struct psc_dma_stream *s = _psc_dma_stream; - -- spin_lock(&s->psc_dma->lock); -- /* For each finished period, dequeue the completed period buffer -- * and enqueue a new one in it's place. */ -- while (bcom_buffer_done(s->bcom_task)) { -- bcom_retrieve_buffer(s->bcom_task, NULL, NULL); -+ scoped_guard(spinlock, &s->psc_dma->lock) { -+ /* -+ * For each finished period, dequeue the completed period buffer -+ * and enqueue a new one in its place -+ */ -+ while (bcom_buffer_done(s->bcom_task)) { -+ bcom_retrieve_buffer(s->bcom_task, NULL, NULL); - -- s->period_current = (s->period_current+1) % s->runtime->periods; -- s->period_count++; -+ s->period_current = (s->period_current+1) % s->runtime->periods; -+ s->period_count++; - -- psc_dma_bcom_enqueue_next_buffer(s); -+ psc_dma_bcom_enqueue_next_buffer(s); -+ } - } -- spin_unlock(&s->psc_dma->lock); - - /* If the stream is active, then also inform the PCM middle layer - * of the period finished event. */ -@@ -116,7 +118,6 @@ static int psc_dma_trigger(struct snd_soc_component *component, - struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); - struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; - u16 imr; -- unsigned long flags; - int i; - - switch (cmd) { -@@ -135,19 +136,18 @@ static int psc_dma_trigger(struct snd_soc_component *component, - /* Fill up the bestcomm bd queue and enable DMA. - * This will begin filling the PSC's fifo. - */ -- spin_lock_irqsave(&psc_dma->lock, flags); -+ scoped_guard(spinlock_irqsave, &psc_dma->lock) { -+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) -+ bcom_gen_bd_rx_reset(s->bcom_task); -+ else -+ bcom_gen_bd_tx_reset(s->bcom_task); - -- if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) -- bcom_gen_bd_rx_reset(s->bcom_task); -- else -- bcom_gen_bd_tx_reset(s->bcom_task); -+ for (i = 0; i < runtime->periods; i++) -+ if (!bcom_queue_full(s->bcom_task)) -+ psc_dma_bcom_enqueue_next_buffer(s); - -- for (i = 0; i < runtime->periods; i++) -- if (!bcom_queue_full(s->bcom_task)) -- psc_dma_bcom_enqueue_next_buffer(s); -- -- bcom_enable(s->bcom_task); -- spin_unlock_irqrestore(&psc_dma->lock, flags); -+ bcom_enable(s->bcom_task); -+ } - - out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); - -@@ -158,13 +158,13 @@ static int psc_dma_trigger(struct snd_soc_component *component, - substream->pstr->stream, s->period_count); - s->active = 0; - -- spin_lock_irqsave(&psc_dma->lock, flags); -- bcom_disable(s->bcom_task); -- if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) -- bcom_gen_bd_rx_reset(s->bcom_task); -- else -- bcom_gen_bd_tx_reset(s->bcom_task); -- spin_unlock_irqrestore(&psc_dma->lock, flags); -+ scoped_guard(spinlock_irqsave, &psc_dma->lock) { -+ bcom_disable(s->bcom_task); -+ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) -+ bcom_gen_bd_rx_reset(s->bcom_task); -+ else -+ bcom_gen_bd_tx_reset(s->bcom_task); -+ } - - break; - -@@ -314,35 +314,29 @@ int mpc5200_audio_dma_create(struct platform_device *op) - { - phys_addr_t fifo; - struct psc_dma *psc_dma; -- struct resource res; -+ struct resource *res; - int size, irq, rc; - const __be32 *prop; - void __iomem *regs; -- int ret; -+ -+ regs = devm_platform_get_and_ioremap_resource(op, 0, &res); -+ if (IS_ERR(regs)) -+ return PTR_ERR(regs); - - /* Fetch the registers and IRQ of the PSC */ -- irq = irq_of_parse_and_map(op->dev.of_node, 0); -- if (of_address_to_resource(op->dev.of_node, 0, &res)) { -- dev_err(&op->dev, "Missing reg property\n"); -- return -ENODEV; -- } -- regs = devm_ioremap(&op->dev, res.start, resource_size(&res)); -- if (!regs) { -- dev_err(&op->dev, "Could not map registers\n"); -- return -ENODEV; -- } -+ irq = platform_get_irq(op, 0); -+ if (irq < 0) -+ return irq; - - /* Allocate and initialize the driver private data */ -- psc_dma = kzalloc_obj(*psc_dma); -+ psc_dma = devm_kzalloc(&op->dev, sizeof(*psc_dma), GFP_KERNEL); - if (!psc_dma) - return -ENOMEM; - - /* Get the PSC ID */ - prop = of_get_property(op->dev.of_node, "cell-index", &size); -- if (!prop || size < sizeof *prop) { -- ret = -ENODEV; -- goto out_free; -- } -+ if (!prop || size < sizeof *prop) -+ return -ENODEV; - - spin_lock_init(&psc_dma->lock); - mutex_init(&psc_dma->mutex); -@@ -357,7 +351,7 @@ int mpc5200_audio_dma_create(struct platform_device *op) - - /* Find the address of the fifo data registers and setup the - * DMA tasks */ -- fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32); -+ fifo = res->start + offsetof(struct mpc52xx_psc, buffer.buffer_32); - psc_dma->capture.bcom_task = - bcom_psc_gen_bd_rx_init(psc_dma->id, 10, fifo, 512); - psc_dma->playback.bcom_task = -@@ -365,8 +359,7 @@ int mpc5200_audio_dma_create(struct platform_device *op) - if (!psc_dma->capture.bcom_task || - !psc_dma->playback.bcom_task) { - dev_err(&op->dev, "Could not allocate bestcomm tasks\n"); -- ret = -ENODEV; -- goto out_free; -+ return -ENODEV; - } - - /* Disable all interrupts and reset the PSC */ -@@ -399,16 +392,14 @@ int mpc5200_audio_dma_create(struct platform_device *op) - psc_dma->capture.irq = - bcom_get_task_irq(psc_dma->capture.bcom_task); - -- rc = request_irq(psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED, -+ rc = devm_request_irq(&op->dev, psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED, - "psc-dma-status", psc_dma); -- rc |= request_irq(psc_dma->capture.irq, &psc_dma_bcom_irq, IRQF_SHARED, -+ rc |= devm_request_irq(&op->dev, psc_dma->capture.irq, &psc_dma_bcom_irq, IRQF_SHARED, - "psc-dma-capture", &psc_dma->capture); -- rc |= request_irq(psc_dma->playback.irq, &psc_dma_bcom_irq, IRQF_SHARED, -+ rc |= devm_request_irq(&op->dev, psc_dma->playback.irq, &psc_dma_bcom_irq, IRQF_SHARED, - "psc-dma-playback", &psc_dma->playback); -- if (rc) { -- ret = -ENODEV; -- goto out_irq; -- } -+ if (rc) -+ return -ENODEV; - - /* Save what we've done so it can be found again later */ - dev_set_drvdata(&op->dev, psc_dma); -@@ -416,13 +407,6 @@ int mpc5200_audio_dma_create(struct platform_device *op) - /* Tell the ASoC OF helpers about it */ - return devm_snd_soc_register_component(&op->dev, - &mpc5200_audio_dma_component, NULL, 0); --out_irq: -- free_irq(psc_dma->irq, psc_dma); -- free_irq(psc_dma->capture.irq, &psc_dma->capture); -- free_irq(psc_dma->playback.irq, &psc_dma->playback); --out_free: -- kfree(psc_dma); -- return ret; - } - EXPORT_SYMBOL_GPL(mpc5200_audio_dma_create); - -@@ -435,12 +419,6 @@ int mpc5200_audio_dma_destroy(struct platform_device *op) - bcom_gen_bd_rx_release(psc_dma->capture.bcom_task); - bcom_gen_bd_tx_release(psc_dma->playback.bcom_task); - -- /* Release irqs */ -- free_irq(psc_dma->irq, psc_dma); -- free_irq(psc_dma->capture.irq, &psc_dma->capture); -- free_irq(psc_dma->playback.irq, &psc_dma->playback); -- -- kfree(psc_dma); - dev_set_drvdata(&op->dev, NULL); - - return 0; -diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c ---- a/sound/soc/fsl/mpc5200_psc_ac97.c -+++ b/sound/soc/fsl/mpc5200_psc_ac97.c -@@ -30,14 +30,13 @@ static unsigned short psc_ac97_read(struct snd_ac97 *ac97, unsigned short reg) - int status; - unsigned int val; - -- mutex_lock(&psc_dma->mutex); -+ guard(mutex)(&psc_dma->mutex); - - /* Wait for command send status zero = ready */ - status = spin_event_timeout(!(in_be16(&psc_dma->psc_regs->sr_csr.status) & - MPC52xx_PSC_SR_CMDSEND), 100, 0); - if (status == 0) { - pr_err("timeout on ac97 bus (rdy)\n"); -- mutex_unlock(&psc_dma->mutex); - return -ENODEV; - } - -@@ -53,19 +52,16 @@ static unsigned short psc_ac97_read(struct snd_ac97 *ac97, unsigned short reg) - if (status == 0) { - pr_err("timeout on ac97 read (val) %x\n", - in_be16(&psc_dma->psc_regs->sr_csr.status)); -- mutex_unlock(&psc_dma->mutex); - return -ENODEV; - } - /* Get the data */ - val = in_be32(&psc_dma->psc_regs->ac97_data); - if (((val >> 24) & 0x7f) != reg) { - pr_err("reg echo error on ac97 read\n"); -- mutex_unlock(&psc_dma->mutex); - return -ENODEV; - } - val = (val >> 8) & 0xffff; - -- mutex_unlock(&psc_dma->mutex); - return (unsigned short) val; - } - -@@ -74,52 +70,46 @@ static void psc_ac97_write(struct snd_ac97 *ac97, - { - int status; - -- mutex_lock(&psc_dma->mutex); -+ guard(mutex)(&psc_dma->mutex); - - /* Wait for command status zero = ready */ - status = spin_event_timeout(!(in_be16(&psc_dma->psc_regs->sr_csr.status) & - MPC52xx_PSC_SR_CMDSEND), 100, 0); - if (status == 0) { - pr_err("timeout on ac97 bus (write)\n"); -- goto out; -+ return; - } - /* Write data */ - out_be32(&psc_dma->psc_regs->ac97_cmd, - ((reg & 0x7f) << 24) | (val << 8)); -- -- out: -- mutex_unlock(&psc_dma->mutex); - } - - static void psc_ac97_warm_reset(struct snd_ac97 *ac97) - { - struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; - -- mutex_lock(&psc_dma->mutex); -+ guard(mutex)(&psc_dma->mutex); - - out_be32(®s->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_AWR); - udelay(3); - out_be32(®s->sicr, psc_dma->sicr); -- -- mutex_unlock(&psc_dma->mutex); - } - - static void psc_ac97_cold_reset(struct snd_ac97 *ac97) - { - struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; - -- mutex_lock(&psc_dma->mutex); -- dev_dbg(psc_dma->dev, "cold reset\n"); -+ scoped_guard(mutex, &psc_dma->mutex) { -+ dev_dbg(psc_dma->dev, "cold reset\n"); - -- mpc5200_psc_ac97_gpio_reset(psc_dma->id); -+ mpc5200_psc_ac97_gpio_reset(psc_dma->id); - -- /* Notify the PSC that a reset has occurred */ -- out_be32(®s->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_ACRB); -+ /* Notify the PSC that a reset has occurred */ -+ out_be32(®s->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_ACRB); - -- /* Re-enable RX and TX */ -- out_8(®s->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); -- -- mutex_unlock(&psc_dma->mutex); -+ /* Re-enable RX and TX */ -+ out_8(®s->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); -+ } - - usleep_range(1000, 2000); - psc_ac97_warm_reset(ac97); -@@ -286,7 +276,7 @@ static int psc_ac97_of_probe(struct platform_device *op) - return rc; - } - -- rc = snd_soc_register_component(&op->dev, &psc_ac97_component, -+ rc = devm_snd_soc_register_component(&op->dev, &psc_ac97_component, - psc_ac97_dai, ARRAY_SIZE(psc_ac97_dai)); - if (rc != 0) { - dev_err(&op->dev, "Failed to register DAI\n"); -@@ -312,8 +302,6 @@ static int psc_ac97_of_probe(struct platform_device *op) - static void psc_ac97_of_remove(struct platform_device *op) - { - mpc5200_audio_dma_destroy(op); -- snd_soc_unregister_component(&op->dev); -- snd_soc_set_ac97_ops(NULL); - } - - /* Match table for of_platform binding */ -diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c ---- a/sound/soc/fsl/mpc5200_psc_i2s.c -+++ b/sound/soc/fsl/mpc5200_psc_i2s.c -@@ -79,9 +79,12 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream, - * and we don't care about the frequency. Return an error if the direction - * is not SND_SOC_CLOCK_IN. - * -+ * @cpu_dai: DAI runtime data pointer - * @clk_id: reserved, should be zero - * @freq: the frequency of the given clock ID, currently ignored - * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master) -+ * -+ * Returns: %0 on success or %-EINVAL on failure. - */ - static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, - int clk_id, unsigned int freq, int dir) -@@ -101,7 +104,10 @@ static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, - * This driver only supports I2S mode. Return an error if the format is - * not SND_SOC_DAIFMT_I2S. - * -+ * @cpu_dai: DAI runtime data pointer - * @format: one of SND_SOC_DAIFMT_xxx -+ * -+ * Returns: %0 on success or %-EINVAL on failure. - */ - static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) - { -@@ -119,7 +125,7 @@ static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) - */ - - /** -- * psc_i2s_dai_template: template CPU Digital Audio Interface -+ * var psc_i2s_dai_ops - template CPU Digital Audio Interface - */ - static const struct snd_soc_dai_ops psc_i2s_dai_ops = { - .hw_params = psc_i2s_hw_params, -@@ -166,7 +172,7 @@ static int psc_i2s_of_probe(struct platform_device *op) - if (rc != 0) - return rc; - -- rc = snd_soc_register_component(&op->dev, &psc_i2s_component, -+ rc = devm_snd_soc_register_component(&op->dev, &psc_i2s_component, - psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai)); - if (rc != 0) { - pr_err("Failed to register DAI\n"); -@@ -213,7 +219,6 @@ static int psc_i2s_of_probe(struct platform_device *op) - static void psc_i2s_of_remove(struct platform_device *op) - { - mpc5200_audio_dma_destroy(op); -- snd_soc_unregister_component(&op->dev); - } - - /* Match table for of_platform binding */ -diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c ---- a/sound/soc/fsl/p1022_ds.c -+++ b/sound/soc/fsl/p1022_ds.c -@@ -73,6 +73,8 @@ struct machine_data { - char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */ - }; - -+#define card_to_mdata(_card) container_of(_card, struct machine_data, card) -+ - /** - * p1022_ds_machine_probe: initialize the board - * -@@ -82,8 +84,7 @@ struct machine_data { - */ - static int p1022_ds_machine_probe(struct snd_soc_card *card) - { -- struct machine_data *mdata = -- container_of(card, struct machine_data, card); -+ struct machine_data *mdata = card_to_mdata(card); - struct ccsr_guts __iomem *guts; - - guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); -@@ -122,8 +123,7 @@ static int p1022_ds_machine_probe(struct snd_soc_card *card) - static int p1022_ds_startup(struct snd_pcm_substream *substream) - { - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); -- struct machine_data *mdata = -- container_of(rtd->card, struct machine_data, card); -+ struct machine_data *mdata = card_to_mdata(rtd->card); - struct device *dev = rtd->card->dev; - int ret = 0; - -@@ -156,8 +156,7 @@ static int p1022_ds_startup(struct snd_pcm_substream *substream) - */ - static int p1022_ds_machine_remove(struct snd_soc_card *card) - { -- struct machine_data *mdata = -- container_of(card, struct machine_data, card); -+ struct machine_data *mdata = card_to_mdata(card); - struct ccsr_guts __iomem *guts; - - guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); -@@ -399,8 +398,7 @@ static int p1022_ds_probe(struct platform_device *pdev) - static void p1022_ds_remove(struct platform_device *pdev) - { - struct snd_soc_card *card = platform_get_drvdata(pdev); -- struct machine_data *mdata = -- container_of(card, struct machine_data, card); -+ struct machine_data *mdata = card_to_mdata(card); - - snd_soc_unregister_card(card); - kfree(mdata); -diff --git a/sound/soc/fsl/p1022_rdk.c b/sound/soc/fsl/p1022_rdk.c ---- a/sound/soc/fsl/p1022_rdk.c -+++ b/sound/soc/fsl/p1022_rdk.c -@@ -79,6 +79,8 @@ struct machine_data { - char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */ - }; - -+#define card_to_mdata(_card) container_of(_card, struct machine_data, card) -+ - /** - * p1022_rdk_machine_probe - initialize the board - * @card: ASoC card instance -@@ -91,8 +93,7 @@ struct machine_data { - */ - static int p1022_rdk_machine_probe(struct snd_soc_card *card) - { -- struct machine_data *mdata = -- container_of(card, struct machine_data, card); -+ struct machine_data *mdata = card_to_mdata(card); - struct ccsr_guts __iomem *guts; - - guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); -@@ -134,8 +135,7 @@ static int p1022_rdk_machine_probe(struct snd_soc_card *card) - static int p1022_rdk_startup(struct snd_pcm_substream *substream) - { - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); -- struct machine_data *mdata = -- container_of(rtd->card, struct machine_data, card); -+ struct machine_data *mdata = card_to_mdata(rtd->card); - struct device *dev = rtd->card->dev; - int ret = 0; - -@@ -169,8 +169,7 @@ static int p1022_rdk_startup(struct snd_pcm_substream *substream) - */ - static int p1022_rdk_machine_remove(struct snd_soc_card *card) - { -- struct machine_data *mdata = -- container_of(card, struct machine_data, card); -+ struct machine_data *mdata = card_to_mdata(card); - struct ccsr_guts __iomem *guts; - - guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); -@@ -361,8 +360,7 @@ static int p1022_rdk_probe(struct platform_device *pdev) - static void p1022_rdk_remove(struct platform_device *pdev) - { - struct snd_soc_card *card = platform_get_drvdata(pdev); -- struct machine_data *mdata = -- container_of(card, struct machine_data, card); -+ struct machine_data *mdata = card_to_mdata(card); - - snd_soc_unregister_card(card); - kfree(mdata); -diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c ---- a/sound/soc/generic/audio-graph-card.c -+++ b/sound/soc/generic/audio-graph-card.c -@@ -579,11 +579,11 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) - goto end; - } - -- ret = simple_util_parse_widgets(card, NULL); -+ ret = simple_util_parse_widgets(priv, NULL); - if (ret < 0) - goto end; - -- ret = simple_util_parse_routing(card, NULL); -+ ret = simple_util_parse_routing(priv, NULL); - if (ret < 0) - goto end; - -@@ -594,6 +594,7 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) - if (ret < 0) - goto err; - -+ /* Card name should be set after graph_for_each_link() */ - ret = simple_util_parse_card_name(priv, NULL); - if (ret < 0) - goto err; -@@ -603,14 +604,13 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) - simple_util_debug_info(priv); - - ret = devm_snd_soc_register_card(dev, card); -- if (ret < 0) -- goto err; -- -- return 0; - err: -- simple_util_clean_reference(card); -+ if (ret < 0) { -+ simple_util_clean_reference(priv); -+ return dev_err_probe(dev, ret, "parse error\n"); -+ } - end: -- return dev_err_probe(dev, ret, "parse error\n"); -+ return graph_ret(priv, ret); - } - EXPORT_SYMBOL_GPL(audio_graph_parse_of); - -diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c ---- a/sound/soc/generic/audio-graph-card2.c -+++ b/sound/soc/generic/audio-graph-card2.c -@@ -1303,11 +1303,11 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, - struct graph2_custom_hooks *hooks) - { - struct snd_soc_card *card = simple_priv_to_card(priv); -- int ret; -+ int ret = -ENOMEM; - - struct link_info *li __free(kfree) = kzalloc_obj(*li); - if (!li) -- return -ENOMEM; -+ goto end; - - card->probe = graph_util_card_probe; - card->owner = THIS_MODULE; -@@ -1316,31 +1316,35 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, - if ((hooks) && (hooks)->hook_pre) { - ret = (hooks)->hook_pre(priv); - if (ret < 0) -- goto err; -+ goto end; - } - - ret = graph_for_each_link(priv, hooks, li, graph_count); - if (!li->link) - ret = -EINVAL; - if (ret < 0) -- goto err; -+ goto end; - - ret = simple_util_init_priv(priv, li); - if (ret < 0) -- goto err; -+ goto end; - - priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW); - if (IS_ERR(priv->pa_gpio)) { - ret = PTR_ERR(priv->pa_gpio); - dev_err(dev, "failed to get amplifier gpio: %d\n", ret); -- goto err; -+ goto end; - } - -- ret = simple_util_parse_widgets(card, NULL); -+ ret = simple_util_parse_widgets(priv, NULL); - if (ret < 0) -- goto err; -+ goto end; - -- ret = simple_util_parse_routing(card, NULL); -+ ret = simple_util_parse_routing(priv, NULL); -+ if (ret < 0) -+ goto end; -+ -+ ret = simple_util_parse_aux_devs(priv, NULL); - if (ret < 0) - goto err; - -@@ -1349,6 +1353,7 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, - if (ret < 0) - goto err; - -+ /* Card name should be set after graph_for_each_link() */ - ret = simple_util_parse_card_name(priv, NULL); - if (ret < 0) - goto err; -@@ -1363,15 +1368,13 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, - - simple_util_debug_info(priv); - -- ret = snd_soc_of_parse_aux_devs(card, "aux-devs"); -- if (ret < 0) -- goto err; -- - ret = devm_snd_soc_register_card(dev, card); - err: -- if (ret < 0) -- dev_err_probe(dev, ret, "parse error\n"); -- -+ if (ret < 0) { -+ simple_util_clean_reference(priv); -+ return dev_err_probe(dev, ret, "parse error\n"); -+ } -+end: - return graph_ret(priv, ret); - } - EXPORT_SYMBOL_GPL(audio_graph2_parse_of); -diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c ---- a/sound/soc/generic/simple-card-utils.c -+++ b/sound/soc/generic/simple-card-utils.c -@@ -216,6 +216,27 @@ int simple_util_set_dailink_name(struct simple_util_priv *priv, - } - EXPORT_SYMBOL_GPL(simple_util_set_dailink_name); - -+int simple_util_parse_property(struct simple_util_priv *priv, -+ int (*func)(struct snd_soc_card *card, const char *propname), -+ char *prefix, char *property) -+{ -+ struct snd_soc_card *card = simple_priv_to_card(priv); -+ struct device_node *node = card->dev->of_node; -+ char prop[128]; -+ -+ if (!prefix) -+ prefix = ""; -+ -+ snprintf(prop, sizeof(prop), "%s%s", prefix, property); -+ -+ /* no property is not error */ -+ if (!of_property_present(node, prop)) -+ return 0; -+ -+ return func(card, prop); -+} -+EXPORT_SYMBOL_GPL(simple_util_parse_property); -+ - int simple_util_parse_card_name(struct simple_util_priv *priv, - char *prefix) - { -@@ -731,11 +752,12 @@ void simple_util_canonicalize_cpu(struct snd_soc_dai_link_component *cpus, - } - EXPORT_SYMBOL_GPL(simple_util_canonicalize_cpu); - --void simple_util_clean_reference(struct snd_soc_card *card) -+void simple_util_clean_reference(struct simple_util_priv *priv) - { - struct snd_soc_dai_link *dai_link; - struct snd_soc_dai_link_component *cpu; - struct snd_soc_dai_link_component *codec; -+ struct snd_soc_card *card = simple_priv_to_card(priv); - int i, j; - - for_each_card_prelinks(card, i, dai_link) { -@@ -747,57 +769,6 @@ void simple_util_clean_reference(struct snd_soc_card *card) - } - EXPORT_SYMBOL_GPL(simple_util_clean_reference); - --int simple_util_parse_routing(struct snd_soc_card *card, -- char *prefix) --{ -- struct device_node *node = card->dev->of_node; -- char prop[128]; -- -- if (!prefix) -- prefix = ""; -- -- snprintf(prop, sizeof(prop), "%s%s", prefix, "routing"); -- -- if (!of_property_present(node, prop)) -- return 0; -- -- return snd_soc_of_parse_audio_routing(card, prop); --} --EXPORT_SYMBOL_GPL(simple_util_parse_routing); -- --int simple_util_parse_widgets(struct snd_soc_card *card, -- char *prefix) --{ -- struct device_node *node = card->dev->of_node; -- char prop[128]; -- -- if (!prefix) -- prefix = ""; -- -- snprintf(prop, sizeof(prop), "%s%s", prefix, "widgets"); -- -- if (of_property_present(node, prop)) -- return snd_soc_of_parse_audio_simple_widgets(card, prop); -- -- /* no widgets is not error */ -- return 0; --} --EXPORT_SYMBOL_GPL(simple_util_parse_widgets); -- --int simple_util_parse_pin_switches(struct snd_soc_card *card, -- char *prefix) --{ -- char prop[128]; -- -- if (!prefix) -- prefix = ""; -- -- snprintf(prop, sizeof(prop), "%s%s", prefix, "pin-switches"); -- -- return snd_soc_of_parse_pin_switches(card, prop); --} --EXPORT_SYMBOL_GPL(simple_util_parse_pin_switches); -- - int simple_util_init_jack(struct snd_soc_card *card, - struct simple_util_jack *sjack, - int is_hp, char *prefix, -@@ -854,9 +825,9 @@ int simple_util_init_jack(struct snd_soc_card *card, - } - EXPORT_SYMBOL_GPL(simple_util_init_jack); - --int simple_util_init_aux_jacks(struct simple_util_priv *priv, char *prefix) -+int simple_util_init_aux_jacks(struct snd_soc_card *card, char *prefix) - { -- struct snd_soc_card *card = simple_priv_to_card(priv); -+ struct simple_util_priv *priv = snd_soc_card_get_drvdata(card); - struct snd_soc_component *component; - int found_jack_index = 0; - int type = 0; -@@ -1026,8 +997,9 @@ EXPORT_SYMBOL_GPL(simple_util_init_priv); - void simple_util_remove(struct platform_device *pdev) - { - struct snd_soc_card *card = platform_get_drvdata(pdev); -+ struct simple_util_priv *priv = snd_soc_card_get_drvdata(card); - -- simple_util_clean_reference(card); -+ simple_util_clean_reference(priv); - } - EXPORT_SYMBOL_GPL(simple_util_remove); - -diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c ---- a/sound/soc/generic/simple-card.c -+++ b/sound/soc/generic/simple-card.c -@@ -517,44 +517,6 @@ static int simple_populate_aux(struct simple_util_priv *priv) - return simple_ret(priv, ret); - } - --static int simple_parse_of(struct simple_util_priv *priv, struct link_info *li) --{ -- struct snd_soc_card *card = simple_priv_to_card(priv); -- int ret; -- -- ret = simple_util_parse_widgets(card, PREFIX); -- if (ret < 0) -- goto end; -- -- ret = simple_util_parse_routing(card, PREFIX); -- if (ret < 0) -- goto end; -- -- ret = simple_util_parse_pin_switches(card, PREFIX); -- if (ret < 0) -- goto end; -- -- /* Single/Muti DAI link(s) & New style of DT node */ -- memset(li, 0, sizeof(*li)); -- ret = simple_for_each_link(priv, li, -- simple_dai_link_of, -- simple_dai_link_of_dpcm); -- if (ret < 0) -- goto end; -- -- ret = simple_util_parse_card_name(priv, PREFIX); -- if (ret < 0) -- goto end; -- -- ret = simple_populate_aux(priv); -- if (ret < 0) -- goto end; -- -- ret = snd_soc_of_parse_aux_devs(card, PREFIX "aux-devs"); --end: -- return simple_ret(priv, ret); --} -- - static int simple_count_noml(struct simple_util_priv *priv, - struct device_node *np, - struct device_node *codec, -@@ -699,28 +661,19 @@ static int simple_soc_probe(struct snd_soc_card *card) - if (ret < 0) - goto end; - -- ret = simple_util_init_aux_jacks(priv, PREFIX); -+ ret = simple_util_init_aux_jacks(card, PREFIX); - end: - return simple_ret(priv, ret); - } - --static int simple_probe(struct platform_device *pdev) -+static int simple_parse_of(struct simple_util_priv *priv) - { -- struct simple_util_priv *priv; -- struct device *dev = &pdev->dev; -- struct snd_soc_card *card; -- int ret; -+ struct snd_soc_card *card = simple_priv_to_card(priv); -+ struct device *dev = card->dev; -+ int ret = -EINVAL; - -- /* Allocate the private data and the DAI link array */ -- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -- if (!priv) -- return -ENOMEM; -- -- card = simple_priv_to_card(priv); -- card->owner = THIS_MODULE; -- card->dev = dev; -- card->probe = simple_soc_probe; -- card->driver_name = "simple-card"; -+ if (!dev) -+ return simple_ret(priv, ret); - - ret = -ENOMEM; - struct link_info *li __free(kfree) = kzalloc_obj(*li); -@@ -739,25 +692,71 @@ static int simple_probe(struct platform_device *pdev) - if (ret < 0) - goto end; - -- ret = simple_parse_of(priv, li); -- if (ret < 0) { -- dev_err_probe(dev, ret, "parse error\n"); -+ ret = simple_util_parse_widgets(priv, PREFIX); -+ if (ret < 0) -+ goto end; -+ -+ ret = simple_util_parse_routing(priv, PREFIX); -+ if (ret < 0) -+ goto end; -+ -+ ret = simple_util_parse_pin_switches(priv, PREFIX); -+ if (ret < 0) -+ goto end; -+ -+ ret = simple_util_parse_aux_devs(priv, PREFIX); -+ if (ret < 0) -+ goto err; -+ -+ /* Single/Muti DAI link(s) & New style of DT node */ -+ memset(li, 0, sizeof(*li)); -+ ret = simple_for_each_link(priv, li, -+ simple_dai_link_of, -+ simple_dai_link_of_dpcm); -+ if (ret < 0) -+ goto err; -+ -+ /* Card name should be set after simple_for_each_link() */ -+ ret = simple_util_parse_card_name(priv, PREFIX); -+ if (ret < 0) -+ goto err; -+ -+ ret = simple_populate_aux(priv); -+ if (ret < 0) - goto err; -- } - - snd_soc_card_set_drvdata(card, priv); - - simple_util_debug_info(priv); - - ret = devm_snd_soc_register_card(dev, card); -- if (ret < 0) -- goto err; -- -- return 0; - err: -- simple_util_clean_reference(card); -+ if (ret < 0) { -+ simple_util_clean_reference(priv); -+ return dev_err_probe(dev, ret, "parse error\n"); -+ } - end: -- return dev_err_probe(dev, ret, "parse error\n"); -+ return simple_ret(priv, ret); -+} -+ -+static int simple_probe(struct platform_device *pdev) -+{ -+ struct simple_util_priv *priv; -+ struct device *dev = &pdev->dev; -+ struct snd_soc_card *card; -+ -+ /* Allocate the private data and the DAI link array */ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ card = simple_priv_to_card(priv); -+ card->owner = THIS_MODULE; -+ card->dev = dev; -+ card->probe = simple_soc_probe; -+ card->driver_name = "simple-card"; -+ -+ return simple_parse_of(priv); - } - - static const struct of_device_id simple_of_match[] = { -diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c ---- a/sound/soc/intel/atom/sst-atom-controls.c -+++ b/sound/soc/intel/atom/sst-atom-controls.c -@@ -14,6 +14,7 @@ - */ - #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -+#include - #include - #include - #include -@@ -73,14 +74,10 @@ static int sst_fill_and_send_cmd(struct sst_data *drv, - u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id, - void *cmd_data, u16 len) - { -- int ret; -+ guard(mutex)(&drv->lock); - -- mutex_lock(&drv->lock); -- ret = sst_fill_and_send_cmd_unlocked(drv, ipc_msg, block, -- task_id, pipe_id, cmd_data, len); -- mutex_unlock(&drv->lock); -- -- return ret; -+ return sst_fill_and_send_cmd_unlocked(drv, ipc_msg, block, -+ task_id, pipe_id, cmd_data, len); - } - - /* -@@ -167,7 +164,7 @@ static int sst_slot_get(struct snd_kcontrol *kcontrol, - unsigned int val, mux; - u8 *map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map; - -- mutex_lock(&drv->lock); -+ guard(mutex)(&drv->lock); - val = 1 << ctl_no; - /* search which slot/channel has this bit set - there should be only one */ - for (mux = e->max; mux > 0; mux--) -@@ -175,7 +172,6 @@ static int sst_slot_get(struct snd_kcontrol *kcontrol, - break; - - ucontrol->value.enumerated.item[0] = mux; -- mutex_unlock(&drv->lock); - - dev_dbg(c->dev, "%s - %s map = %#x\n", - is_tx ? "tx channel" : "rx slot", -@@ -235,7 +231,7 @@ static int sst_slot_put(struct snd_kcontrol *kcontrol, - if (mux > e->max - 1) - return -EINVAL; - -- mutex_lock(&drv->lock); -+ guard(mutex)(&drv->lock); - /* first clear all registers of this bit */ - for (i = 0; i < e->max; i++) - map[i] &= ~val; -@@ -244,7 +240,6 @@ static int sst_slot_put(struct snd_kcontrol *kcontrol, - /* kctl set to 'none' and we reset the bits so send IPC */ - ret = sst_check_and_send_slot_map(drv, kcontrol); - -- mutex_unlock(&drv->lock); - return ret; - } - -@@ -258,7 +253,6 @@ static int sst_slot_put(struct snd_kcontrol *kcontrol, - - ret = sst_check_and_send_slot_map(drv, kcontrol); - -- mutex_unlock(&drv->lock); - return ret; - } - -@@ -354,13 +348,12 @@ static int sst_algo_control_set(struct snd_kcontrol *kcontrol, - struct sst_algo_control *bc = (void *)kcontrol->private_value; - - dev_dbg(cmpnt->dev, "control_name=%s\n", kcontrol->id.name); -- mutex_lock(&drv->lock); -+ guard(mutex)(&drv->lock); - switch (bc->type) { - case SST_ALGO_PARAMS: - memcpy(bc->params, ucontrol->value.bytes.data, bc->max); - break; - default: -- mutex_unlock(&drv->lock); - dev_err(cmpnt->dev, "Invalid Input- algo type:%d\n", - bc->type); - return -EINVAL; -@@ -368,7 +361,6 @@ static int sst_algo_control_set(struct snd_kcontrol *kcontrol, - /*if pipe is enabled, need to send the algo params from here*/ - if (bc->w && bc->w->power) - ret = sst_send_algo_cmd(drv, bc); -- mutex_unlock(&drv->lock); - - return ret; - } -@@ -475,7 +467,7 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, - struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value; - struct sst_gain_value *gv = mc->gain_val; - -- mutex_lock(&drv->lock); -+ guard(mutex)(&drv->lock); - - switch (mc->type) { - case SST_GAIN_TLV: -@@ -497,7 +489,6 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, - break; - - default: -- mutex_unlock(&drv->lock); - dev_err(cmpnt->dev, "Invalid Input- gain type:%d\n", - mc->type); - return -EINVAL; -@@ -506,7 +497,6 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, - if (mc->w && mc->w->power) - ret = sst_send_gain_cmd(drv, gv, mc->task_id, - mc->pipe_id | mc->instance_id, mc->module_id, 0); -- mutex_unlock(&drv->lock); - - return ret; - } -@@ -521,10 +511,9 @@ static int sst_send_pipe_module_params(struct snd_soc_dapm_widget *w, - struct sst_data *drv = snd_soc_component_get_drvdata(c); - struct sst_ids *ids = w->priv; - -- mutex_lock(&drv->lock); -+ guard(mutex)(&drv->lock); - sst_find_and_send_pipe_algo(drv, w->name, ids); - sst_set_pipe_gain(ids, drv, 0); -- mutex_unlock(&drv->lock); - - return 0; - } -@@ -761,27 +750,29 @@ int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable) - return ret; - } - -- mutex_lock(&drv->lock); -- if (enable) -- timer_usage++; -- else -- timer_usage--; -- -- /* -- * Send the command only if this call is the first enable or last -- * disable -- */ -- if ((enable && (timer_usage == 1)) || -- (!enable && (timer_usage == 0))) { -- ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD, -- SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd, -- sizeof(cmd.header) + cmd.header.length); -- if (ret && enable) { -+ scoped_guard(mutex, &drv->lock) { -+ if (enable) -+ timer_usage++; -+ else - timer_usage--; -- enable = false; -+ -+ /* -+ * Send the command only if this call is the first enable or last -+ * disable -+ */ -+ if ((enable && timer_usage == 1) || -+ (!enable && timer_usage == 0)) { -+ ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD, -+ SST_FLAG_BLOCKED, -+ SST_TASK_SBA, 0, &cmd, -+ sizeof(cmd.header) + -+ cmd.header.length); -+ if (ret && enable) { -+ timer_usage--; -+ enable = false; -+ } - } - } -- mutex_unlock(&drv->lock); - - if (!enable) - sst->ops->power(sst->dev, false); -diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c ---- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c -+++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c -@@ -11,6 +11,7 @@ - */ - #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -+#include - #include - #include - #include -@@ -32,7 +33,7 @@ int sst_register_dsp(struct sst_device *dev) - return -EINVAL; - if (!try_module_get(dev->dev->driver->owner)) - return -ENODEV; -- mutex_lock(&sst_lock); -+ guard(mutex)(&sst_lock); - if (sst) { - dev_err(dev->dev, "we already have a device %s\n", sst->name); - module_put(dev->dev->driver->owner); -@@ -41,7 +42,7 @@ int sst_register_dsp(struct sst_device *dev) - } - dev_dbg(dev->dev, "registering device %s\n", dev->name); - sst = dev; -- mutex_unlock(&sst_lock); -+ - return 0; - } - EXPORT_SYMBOL_GPL(sst_register_dsp); -@@ -53,17 +54,15 @@ int sst_unregister_dsp(struct sst_device *dev) - if (dev != sst) - return -EINVAL; - -- mutex_lock(&sst_lock); -+ guard(mutex)(&sst_lock); - -- if (!sst) { -- mutex_unlock(&sst_lock); -+ if (!sst) - return -EIO; -- } - - module_put(sst->dev->driver->owner); - dev_dbg(dev->dev, "unreg %s\n", sst->name); - sst = NULL; -- mutex_unlock(&sst_lock); -+ - return 0; - } - EXPORT_SYMBOL_GPL(sst_unregister_dsp); -@@ -103,21 +102,14 @@ static int sst_media_digital_mute(struct snd_soc_dai *dai, int mute, int stream) - void sst_set_stream_status(struct sst_runtime_stream *stream, - int state) - { -- unsigned long flags; -- spin_lock_irqsave(&stream->status_lock, flags); -+ guard(spinlock_irqsave)(&stream->status_lock); - stream->stream_status = state; -- spin_unlock_irqrestore(&stream->status_lock, flags); - } - - static inline int sst_get_stream_status(struct sst_runtime_stream *stream) - { -- int state; -- unsigned long flags; -- -- spin_lock_irqsave(&stream->status_lock, flags); -- state = stream->stream_status; -- spin_unlock_irqrestore(&stream->status_lock, flags); -- return state; -+ guard(spinlock_irqsave)(&stream->status_lock); -+ return stream->stream_status; - } - - static void sst_fill_alloc_params(struct snd_pcm_substream *substream, -@@ -244,12 +236,7 @@ static int sst_platform_alloc_stream(struct snd_pcm_substream *substream, - - stream->stream_info.str_id = str_params.stream_id; - -- ret_val = stream->ops->open(sst->dev, &str_params); -- if (ret_val <= 0) -- return ret_val; -- -- -- return ret_val; -+ return stream->ops->open(sst->dev, &str_params); - } - - static void sst_period_elapsed(void *arg) -@@ -304,7 +291,7 @@ static int sst_media_open(struct snd_pcm_substream *substream, - { - int ret_val = 0; - struct snd_pcm_runtime *runtime = substream->runtime; -- struct sst_runtime_stream *stream; -+ struct sst_runtime_stream *stream __free(kfree) = NULL; - - stream = kzalloc_obj(*stream); - if (!stream) -@@ -312,15 +299,14 @@ static int sst_media_open(struct snd_pcm_substream *substream, - spin_lock_init(&stream->status_lock); - - /* get the sst ops */ -- mutex_lock(&sst_lock); -- if (!sst || -- !try_module_get(sst->dev->driver->owner)) { -- dev_err(dai->dev, "no device available to run\n"); -- ret_val = -ENODEV; -- goto out_ops; -+ scoped_guard(mutex, &sst_lock) { -+ if (!sst || -+ !try_module_get(sst->dev->driver->owner)) { -+ dev_err(dai->dev, "no device available to run\n"); -+ return -ENODEV; -+ } -+ stream->ops = sst->ops; - } -- stream->ops = sst->ops; -- mutex_unlock(&sst_lock); - - stream->stream_info.str_id = 0; - -@@ -330,7 +316,7 @@ static int sst_media_open(struct snd_pcm_substream *substream, - - ret_val = power_up_sst(stream); - if (ret_val < 0) -- goto out_power_up; -+ return ret_val; - - /* - * Make sure the period to be multiple of 1ms to align the -@@ -347,12 +333,14 @@ static int sst_media_open(struct snd_pcm_substream *substream, - snd_pcm_hw_constraint_step(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_PERIODS, 2); - -- return snd_pcm_hw_constraint_integer(runtime, -- SNDRV_PCM_HW_PARAM_PERIODS); --out_ops: -- mutex_unlock(&sst_lock); --out_power_up: -- kfree(stream); -+ ret_val = snd_pcm_hw_constraint_integer(runtime, -+ SNDRV_PCM_HW_PARAM_PERIODS); -+ -+ if (ret_val < 0) -+ return ret_val; -+ -+ stream = NULL; -+ - return ret_val; - } - -diff --git a/sound/soc/intel/atom/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c ---- a/sound/soc/intel/atom/sst/sst_ipc.c -+++ b/sound/soc/intel/atom/sst/sst_ipc.c -@@ -11,6 +11,7 @@ - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ -+#include - #include - #include - #include -@@ -180,9 +181,8 @@ void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx) - union interrupt_reg_mrfld isr; - union interrupt_reg_mrfld imr; - union ipc_header_mrfld clear_ipc; -- unsigned long irq_flags; - -- spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags); -+ guard(spinlock_irqsave)(&sst_drv_ctx->ipc_spin_lock); - imr.full = sst_shim_read64(sst_drv_ctx->shim, SST_IMRX); - isr.full = sst_shim_read64(sst_drv_ctx->shim, SST_ISRX); - -@@ -200,7 +200,6 @@ void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx) - /* un mask busy interrupt */ - imr.part.busy_interrupt = 0; - sst_shim_write64(sst_drv_ctx->shim, SST_IMRX, imr.full); -- spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); - } - - -diff --git a/sound/soc/intel/atom/sst/sst_pvt.c b/sound/soc/intel/atom/sst/sst_pvt.c ---- a/sound/soc/intel/atom/sst/sst_pvt.c -+++ b/sound/soc/intel/atom/sst/sst_pvt.c -@@ -11,6 +11,7 @@ - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ -+#include - #include - #include - #include -@@ -64,9 +65,8 @@ u64 sst_shim_read64(void __iomem *addr, int offset) - void sst_set_fw_state_locked( - struct intel_sst_drv *sst_drv_ctx, int sst_state) - { -- mutex_lock(&sst_drv_ctx->sst_lock); -+ guard(mutex)(&sst_drv_ctx->sst_lock); - sst_drv_ctx->sst_state = sst_state; -- mutex_unlock(&sst_drv_ctx->sst_lock); - } - - /* -@@ -302,18 +302,17 @@ int sst_assign_pvt_id(struct intel_sst_drv *drv) - { - int local; - -- spin_lock(&drv->block_lock); -+ guard(spinlock)(&drv->block_lock); - /* find first zero index from lsb */ - local = ffz(drv->pvt_id); - dev_dbg(drv->dev, "pvt_id assigned --> %d\n", local); - if (local >= SST_MAX_BLOCKS){ -- spin_unlock(&drv->block_lock); - dev_err(drv->dev, "PVT _ID error: no free id blocks "); - return -EINVAL; - } - /* toggle the index */ - change_bit(local, &drv->pvt_id); -- spin_unlock(&drv->block_lock); -+ - return local; - } - -diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c ---- a/sound/soc/intel/avs/apl.c -+++ b/sound/soc/intel/avs/apl.c -@@ -6,6 +6,7 @@ - // Amadeusz Slawinski - // - -+#include - #include - #include - #include -@@ -190,7 +191,7 @@ static bool avs_apl_lp_streaming(struct avs_dev *adev) - { - struct avs_path *path; - -- spin_lock(&adev->path_list_lock); -+ guard(spinlock)(&adev->path_list_lock); - /* Any gateway without buffer allocated in LP area disqualifies D0IX. */ - list_for_each_entry(path, &adev->path_list, node) { - struct avs_path_pipeline *ppl; -@@ -210,14 +211,11 @@ static bool avs_apl_lp_streaming(struct avs_dev *adev) - if (cfg->copier.dma_type == INVALID_OBJECT_ID) - continue; - -- if (!mod->gtw_attrs.lp_buffer_alloc) { -- spin_unlock(&adev->path_list_lock); -+ if (!mod->gtw_attrs.lp_buffer_alloc) - return false; -- } - } - } - } -- spin_unlock(&adev->path_list_lock); - - return true; - } -diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h ---- a/sound/soc/intel/avs/avs.h -+++ b/sound/soc/intel/avs/avs.h -@@ -336,14 +336,13 @@ int avs_icl_load_basefw(struct avs_dev *adev, struct firmware *fw); - /* Soc component members */ - - struct avs_soc_component { -- struct snd_soc_component base; -+ struct snd_soc_component *base; - struct avs_tplg *tplg; - - struct list_head node; - }; - --#define to_avs_soc_component(comp) \ -- container_of(comp, struct avs_soc_component, base) -+#define to_avs_soc_component(comp) snd_soc_component_to_priv(comp) - - extern const struct snd_soc_dai_ops avs_dai_fe_ops; - -diff --git a/sound/soc/intel/avs/boards/da7219.c b/sound/soc/intel/avs/boards/da7219.c ---- a/sound/soc/intel/avs/boards/da7219.c -+++ b/sound/soc/intel/avs/boards/da7219.c -@@ -175,7 +175,6 @@ static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot, - if (!dl || !platform) - return -ENOMEM; - -- dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port); - dl->name = devm_kasprintf(dev, GFP_KERNEL, - AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot)); - dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL); -@@ -259,10 +258,8 @@ static int avs_da7219_probe(struct platform_device *pdev) - } - - static const struct platform_device_id avs_da7219_driver_ids[] = { -- { -- .name = "avs_da7219", -- }, -- {}, -+ { .name = "avs_da7219" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_da7219_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/dmic.c b/sound/soc/intel/avs/boards/dmic.c ---- a/sound/soc/intel/avs/boards/dmic.c -+++ b/sound/soc/intel/avs/boards/dmic.c -@@ -104,10 +104,8 @@ static int avs_dmic_probe(struct platform_device *pdev) - } - - static const struct platform_device_id avs_dmic_driver_ids[] = { -- { -- .name = "avs_dmic", -- }, -- {}, -+ { .name = "avs_dmic" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_dmic_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/es8336.c b/sound/soc/intel/avs/boards/es8336.c ---- a/sound/soc/intel/avs/boards/es8336.c -+++ b/sound/soc/intel/avs/boards/es8336.c -@@ -309,10 +309,8 @@ static int avs_es8336_probe(struct platform_device *pdev) - } - - static const struct platform_device_id avs_es8336_driver_ids[] = { -- { -- .name = "avs_es8336", -- }, -- {}, -+ { .name = "avs_es8336" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_es8336_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/hdaudio.c b/sound/soc/intel/avs/boards/hdaudio.c ---- a/sound/soc/intel/avs/boards/hdaudio.c -+++ b/sound/soc/intel/avs/boards/hdaudio.c -@@ -231,10 +231,8 @@ static int avs_hdaudio_probe(struct platform_device *pdev) - } - - static const struct platform_device_id avs_hdaudio_driver_ids[] = { -- { -- .name = "avs_hdaudio", -- }, -- {}, -+ { .name = "avs_hdaudio" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_hdaudio_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/i2s_test.c b/sound/soc/intel/avs/boards/i2s_test.c ---- a/sound/soc/intel/avs/boards/i2s_test.c -+++ b/sound/soc/intel/avs/boards/i2s_test.c -@@ -107,10 +107,8 @@ static int avs_i2s_test_probe(struct platform_device *pdev) - } - - static const struct platform_device_id avs_i2s_test_driver_ids[] = { -- { -- .name = "avs_i2s_test", -- }, -- {}, -+ { .name = "avs_i2s_test" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_i2s_test_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/max98357a.c b/sound/soc/intel/avs/boards/max98357a.c ---- a/sound/soc/intel/avs/boards/max98357a.c -+++ b/sound/soc/intel/avs/boards/max98357a.c -@@ -136,10 +136,8 @@ static int avs_max98357a_probe(struct platform_device *pdev) - } - - static const struct platform_device_id avs_max98357a_driver_ids[] = { -- { -- .name = "avs_max98357a", -- }, -- {}, -+ { .name = "avs_max98357a" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_max98357a_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/max98373.c b/sound/soc/intel/avs/boards/max98373.c ---- a/sound/soc/intel/avs/boards/max98373.c -+++ b/sound/soc/intel/avs/boards/max98373.c -@@ -191,10 +191,8 @@ static int avs_max98373_probe(struct platform_device *pdev) - } - - static const struct platform_device_id avs_max98373_driver_ids[] = { -- { -- .name = "avs_max98373", -- }, -- {}, -+ { .name = "avs_max98373" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_max98373_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/max98927.c b/sound/soc/intel/avs/boards/max98927.c ---- a/sound/soc/intel/avs/boards/max98927.c -+++ b/sound/soc/intel/avs/boards/max98927.c -@@ -188,10 +188,8 @@ static int avs_max98927_probe(struct platform_device *pdev) - } - - static const struct platform_device_id avs_max98927_driver_ids[] = { -- { -- .name = "avs_max98927", -- }, -- {}, -+ { .name = "avs_max98927" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_max98927_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/nau8825.c b/sound/soc/intel/avs/boards/nau8825.c ---- a/sound/soc/intel/avs/boards/nau8825.c -+++ b/sound/soc/intel/avs/boards/nau8825.c -@@ -293,10 +293,8 @@ static int avs_nau8825_probe(struct platform_device *pdev) - } - - static const struct platform_device_id avs_nau8825_driver_ids[] = { -- { -- .name = "avs_nau8825", -- }, -- {}, -+ { .name = "avs_nau8825" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_nau8825_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/pcm3168a.c b/sound/soc/intel/avs/boards/pcm3168a.c ---- a/sound/soc/intel/avs/boards/pcm3168a.c -+++ b/sound/soc/intel/avs/boards/pcm3168a.c -@@ -132,10 +132,8 @@ static int avs_pcm3168a_probe(struct platform_device *pdev) - } - - static const struct platform_device_id avs_pcm3168a_driver_ids[] = { -- { -- .name = "avs_pcm3168a", -- }, -- {}, -+ { .name = "avs_pcm3168a" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_pcm3168a_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/probe.c b/sound/soc/intel/avs/boards/probe.c ---- a/sound/soc/intel/avs/boards/probe.c -+++ b/sound/soc/intel/avs/boards/probe.c -@@ -64,7 +64,7 @@ static const struct platform_device_id avs_probe_mb_driver_ids[] = { - { - .name = "avs_probe_mb", - }, -- {}, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_probe_mb_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/rt274.c b/sound/soc/intel/avs/boards/rt274.c ---- a/sound/soc/intel/avs/boards/rt274.c -+++ b/sound/soc/intel/avs/boards/rt274.c -@@ -261,7 +261,7 @@ static const struct platform_device_id avs_rt274_driver_ids[] = { - { - .name = "avs_rt274", - }, -- {}, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_rt274_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/rt286.c b/sound/soc/intel/avs/boards/rt286.c ---- a/sound/soc/intel/avs/boards/rt286.c -+++ b/sound/soc/intel/avs/boards/rt286.c -@@ -231,7 +231,7 @@ static const struct platform_device_id avs_rt286_driver_ids[] = { - { - .name = "avs_rt286", - }, -- {}, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_rt286_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/rt298.c b/sound/soc/intel/avs/boards/rt298.c ---- a/sound/soc/intel/avs/boards/rt298.c -+++ b/sound/soc/intel/avs/boards/rt298.c -@@ -250,7 +250,7 @@ static const struct platform_device_id avs_rt298_driver_ids[] = { - { - .name = "avs_rt298", - }, -- {}, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_rt298_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/rt5514.c b/sound/soc/intel/avs/boards/rt5514.c ---- a/sound/soc/intel/avs/boards/rt5514.c -+++ b/sound/soc/intel/avs/boards/rt5514.c -@@ -178,7 +178,7 @@ static const struct platform_device_id avs_rt5514_driver_ids[] = { - { - .name = "avs_rt5514", - }, -- {}, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_rt5514_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/rt5640.c b/sound/soc/intel/avs/boards/rt5640.c ---- a/sound/soc/intel/avs/boards/rt5640.c -+++ b/sound/soc/intel/avs/boards/rt5640.c -@@ -252,7 +252,7 @@ static const struct platform_device_id avs_rt5640_driver_ids[] = { - { - .name = "avs_rt5640", - }, -- {}, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_rt5640_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/rt5663.c b/sound/soc/intel/avs/boards/rt5663.c ---- a/sound/soc/intel/avs/boards/rt5663.c -+++ b/sound/soc/intel/avs/boards/rt5663.c -@@ -249,7 +249,7 @@ static const struct platform_device_id avs_rt5663_driver_ids[] = { - { - .name = "avs_rt5663", - }, -- {}, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_rt5663_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/rt5682.c b/sound/soc/intel/avs/boards/rt5682.c ---- a/sound/soc/intel/avs/boards/rt5682.c -+++ b/sound/soc/intel/avs/boards/rt5682.c -@@ -325,7 +325,7 @@ static const struct platform_device_id avs_rt5682_driver_ids[] = { - { - .name = "avs_rt5682", - }, -- {}, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_rt5682_driver_ids); - -diff --git a/sound/soc/intel/avs/boards/ssm4567.c b/sound/soc/intel/avs/boards/ssm4567.c ---- a/sound/soc/intel/avs/boards/ssm4567.c -+++ b/sound/soc/intel/avs/boards/ssm4567.c -@@ -180,7 +180,7 @@ static const struct platform_device_id avs_ssm4567_driver_ids[] = { - { - .name = "avs_ssm4567", - }, -- {}, -+ { } - }; - MODULE_DEVICE_TABLE(platform, avs_ssm4567_driver_ids); - -diff --git a/sound/soc/intel/avs/control.c b/sound/soc/intel/avs/control.c ---- a/sound/soc/intel/avs/control.c -+++ b/sound/soc/intel/avs/control.c -@@ -27,7 +27,7 @@ static struct avs_path_module *avs_get_volume_module(struct avs_dev *adev, u32 i - struct avs_path_pipeline *ppl; - struct avs_path_module *mod; - -- spin_lock(&adev->path_list_lock); -+ guard(spinlock)(&adev->path_list_lock); - list_for_each_entry(path, &adev->path_list, node) { - list_for_each_entry(ppl, &path->ppl_list, node) { - list_for_each_entry(mod, &ppl->mod_list, node) { -@@ -35,14 +35,11 @@ static struct avs_path_module *avs_get_volume_module(struct avs_dev *adev, u32 i - - if ((guid_equal(type, &AVS_PEAKVOL_MOD_UUID) || - guid_equal(type, &AVS_GAIN_MOD_UUID)) && -- mod->template->ctl_id == id) { -- spin_unlock(&adev->path_list_lock); -+ mod->template->ctl_id == id) - return mod; -- } - } - } - } -- spin_unlock(&adev->path_list_lock); - - return NULL; - } -diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c ---- a/sound/soc/intel/avs/core.c -+++ b/sound/soc/intel/avs/core.c -@@ -14,6 +14,7 @@ - // foundation of this driver - // - -+#include - #include - #include - #include -@@ -273,7 +274,7 @@ static irqreturn_t avs_hda_interrupt(struct hdac_bus *bus) - if (snd_hdac_bus_handle_stream_irq(bus, status, hdac_update_stream)) - ret = IRQ_HANDLED; - -- spin_lock_irq(&bus->reg_lock); -+ guard(spinlock_irq)(&bus->reg_lock); - /* Clear RIRB interrupt. */ - status = snd_hdac_chip_readb(bus, RIRBSTS); - if (status & RIRB_INT_MASK) { -@@ -283,7 +284,6 @@ static irqreturn_t avs_hda_interrupt(struct hdac_bus *bus) - ret = IRQ_HANDLED; - } - -- spin_unlock_irq(&bus->reg_lock); - return ret; - } - -diff --git a/sound/soc/intel/avs/debug.h b/sound/soc/intel/avs/debug.h ---- a/sound/soc/intel/avs/debug.h -+++ b/sound/soc/intel/avs/debug.h -@@ -9,6 +9,7 @@ - #ifndef __SOUND_SOC_INTEL_AVS_DEBUG_H - #define __SOUND_SOC_INTEL_AVS_DEBUG_H - -+#include - #include "messages.h" - #include "registers.h" - -@@ -26,14 +27,9 @@ struct avs_dev; - - static inline int avs_log_buffer_status_locked(struct avs_dev *adev, union avs_notify_msg *msg) - { -- unsigned long flags; -- int ret; -+ guard(spinlock_irqsave)(&adev->trace_lock); - -- spin_lock_irqsave(&adev->trace_lock, flags); -- ret = avs_dsp_op(adev, log_buffer_status, msg); -- spin_unlock_irqrestore(&adev->trace_lock, flags); -- -- return ret; -+ return avs_dsp_op(adev, log_buffer_status, msg); - } - - struct avs_apl_log_buffer_layout { -diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c ---- a/sound/soc/intel/avs/debugfs.c -+++ b/sound/soc/intel/avs/debugfs.c -@@ -6,6 +6,7 @@ - // Amadeusz Slawinski - // - -+#include - #include - #include - #include -@@ -251,24 +252,22 @@ static int strace_release(struct inode *inode, struct file *file) - union avs_notify_msg msg = AVS_NOTIFICATION(LOG_BUFFER_STATUS); - struct avs_dev *adev = file->private_data; - unsigned long resource_mask; -- unsigned long flags, i; -+ unsigned long i; - u32 num_cores; - - resource_mask = adev->logged_resources; - num_cores = adev->hw_cfg.dsp_cores; - -- spin_lock_irqsave(&adev->trace_lock, flags); -+ scoped_guard(spinlock_irqsave, &adev->trace_lock) { -+ /* Gather any remaining logs. */ -+ for_each_set_bit(i, &resource_mask, num_cores) { -+ msg.log.core = i; -+ avs_dsp_op(adev, log_buffer_status, &msg); -+ } - -- /* Gather any remaining logs. */ -- for_each_set_bit(i, &resource_mask, num_cores) { -- msg.log.core = i; -- avs_dsp_op(adev, log_buffer_status, &msg); -+ kfifo_free(&adev->trace_fifo); - } - -- kfifo_free(&adev->trace_fifo); -- -- spin_unlock_irqrestore(&adev->trace_lock, flags); -- - module_put(adev->dev->driver->owner); - return 0; - } -diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c ---- a/sound/soc/intel/avs/ipc.c -+++ b/sound/soc/intel/avs/ipc.c -@@ -6,6 +6,7 @@ - // Amadeusz Slawinski - // - -+#include - #include - #include - #include -@@ -99,39 +100,39 @@ static void avs_dsp_recovery(struct avs_dev *adev) - unsigned int core_mask; - int ret; - -- mutex_lock(&adev->comp_list_mutex); -- /* disconnect all running streams */ -- list_for_each_entry(acomp, &adev->comp_list, node) { -- struct snd_soc_pcm_runtime *rtd; -- struct snd_soc_card *card; -+ scoped_guard(mutex, &adev->comp_list_mutex) { -+ /* disconnect all running streams */ -+ list_for_each_entry(acomp, &adev->comp_list, node) { -+ struct snd_soc_pcm_runtime *rtd; -+ struct snd_soc_card *card; - -- card = acomp->base.card; -- if (!card) -- continue; -- -- for_each_card_rtds(card, rtd) { -- struct snd_pcm *pcm; -- int dir; -- -- pcm = rtd->pcm; -- if (!pcm || rtd->dai_link->no_pcm) -+ card = acomp->base->card; -+ if (!card) - continue; - -- for_each_pcm_streams(dir) { -- struct snd_pcm_substream *substream; -+ for_each_card_rtds(card, rtd) { -+ struct snd_pcm *pcm; -+ int dir; - -- substream = pcm->streams[dir].substream; -- if (!substream || !substream->runtime) -+ pcm = rtd->pcm; -+ if (!pcm || rtd->dai_link->no_pcm) - continue; - -- /* No need for _irq() as we are in nonatomic context. */ -- snd_pcm_stream_lock(substream); -- snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); -- snd_pcm_stream_unlock(substream); -+ for_each_pcm_streams(dir) { -+ struct snd_pcm_substream *substream; -+ -+ substream = pcm->streams[dir].substream; -+ if (!substream || !substream->runtime) -+ continue; -+ -+ /* No need for _irq() as we are in nonatomic context. */ -+ snd_pcm_stream_lock(substream); -+ snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); -+ snd_pcm_stream_unlock(substream); -+ } - } - } - } -- mutex_unlock(&adev->comp_list_mutex); - - /* forcibly shutdown all cores */ - core_mask = GENMASK(adev->hw_cfg.dsp_cores - 1, 0); -@@ -397,7 +398,7 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request - if (!ipc->ready) - return -EPERM; - -- mutex_lock(&ipc->msg_mutex); -+ guard(mutex)(&ipc->msg_mutex); - - spin_lock(&ipc->rx_lock); - avs_ipc_msg_init(ipc, reply); -@@ -412,7 +413,7 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request - /* Same treatment as on exception, just stack_dump=0. */ - avs_dsp_exception_caught(adev, &msg); - } -- goto exit; -+ return ret; - } - - ret = ipc->rx.rsp.status; -@@ -436,8 +437,6 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request - memcpy(reply->data, ipc->rx.data, reply->size); - } - --exit: -- mutex_unlock(&ipc->msg_mutex); - return ret; - } - -@@ -501,7 +500,7 @@ static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *req - struct avs_ipc *ipc = adev->ipc; - int ret; - -- mutex_lock(&ipc->msg_mutex); -+ guard(mutex)(&ipc->msg_mutex); - - spin_lock(&ipc->rx_lock); - avs_ipc_msg_init(ipc, NULL); -@@ -522,8 +521,6 @@ static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *req - dev_err(adev->dev, "%s (0x%08x 0x%08x) failed: %d\n", - name, request->glb.primary, request->glb.ext.val, ret); - -- mutex_unlock(&ipc->msg_mutex); -- - return ret; - } - -diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c ---- a/sound/soc/intel/avs/loader.c -+++ b/sound/soc/intel/avs/loader.c -@@ -6,6 +6,7 @@ - // Amadeusz Slawinski - // - -+#include - #include - #include - #include -@@ -630,15 +631,15 @@ static int avs_load_firmware(struct avs_dev *adev, bool purge) - if (ret) - goto reenable_gating; - -- mutex_lock(&adev->comp_list_mutex); -- list_for_each_entry(acomp, &adev->comp_list, node) { -- struct avs_tplg *tplg = acomp->tplg; -+ scoped_guard(mutex, &adev->comp_list_mutex) { -+ list_for_each_entry(acomp, &adev->comp_list, node) { -+ struct avs_tplg *tplg = acomp->tplg; - -- ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs); -- if (ret < 0) -- break; -+ ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs); -+ if (ret < 0) -+ break; -+ } - } -- mutex_unlock(&adev->comp_list_mutex); - - reenable_gating: - avs_hda_l1sen_enable(adev, true); -diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c ---- a/sound/soc/intel/avs/path.c -+++ b/sound/soc/intel/avs/path.c -@@ -6,6 +6,7 @@ - // Amadeusz Slawinski - // - -+#include - #include - #include - #include -@@ -69,16 +70,13 @@ avs_path_find_path(struct avs_dev *adev, const char *name, u32 template_id) - if (!template) - return NULL; - -- spin_lock(&adev->path_list_lock); -+ guard(spinlock)(&adev->path_list_lock); - /* Only one variant of given path template may be instantiated at a time. */ - list_for_each_entry(path, &adev->path_list, node) { -- if (path->template->owner == template) { -- spin_unlock(&adev->path_list_lock); -+ if (path->template->owner == template) - return path; -- } - } - -- spin_unlock(&adev->path_list_lock); - return NULL; - } - -@@ -1305,7 +1303,7 @@ void avs_path_free(struct avs_path *path) - struct avs_path *cpath, *csave; - struct avs_dev *adev = path->owner; - -- mutex_lock(&adev->path_mutex); -+ guard(mutex)(&adev->path_mutex); - - /* Free all condpaths this path spawned. */ - list_for_each_entry_safe(cpath, csave, &path->source_list, source_node) -@@ -1314,8 +1312,6 @@ void avs_path_free(struct avs_path *path) - avs_condpath_free(path->owner, cpath); - - avs_path_free_unlocked(path); -- -- mutex_unlock(&adev->path_mutex); - } - - struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id, -@@ -1334,13 +1330,13 @@ struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id, - } - - /* Serialize path and its components creation. */ -- mutex_lock(&adev->path_mutex); -+ guard(mutex)(&adev->path_mutex); - /* Satisfy needs of avs_path_find_tplg(). */ -- mutex_lock(&adev->comp_list_mutex); -+ guard(mutex)(&adev->comp_list_mutex); - - path = avs_path_create_unlocked(adev, dma_id, variant); - if (IS_ERR(path)) -- goto exit; -+ return path; - - ret = avs_condpaths_walk_all(adev, path); - if (ret) { -@@ -1348,10 +1344,6 @@ struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id, - path = ERR_PTR(ret); - } - --exit: -- mutex_unlock(&adev->comp_list_mutex); -- mutex_unlock(&adev->path_mutex); -- - return path; - } - -@@ -1496,15 +1488,13 @@ static void avs_condpaths_pause(struct avs_dev *adev, struct avs_path *path) - { - struct avs_path *cpath; - -- mutex_lock(&adev->path_mutex); -+ guard(mutex)(&adev->path_mutex); - - /* If either source or sink stops, so do the attached conditional paths. */ - list_for_each_entry(cpath, &path->source_list, source_node) - avs_condpath_pause(adev, cpath); - list_for_each_entry(cpath, &path->sink_list, sink_node) - avs_condpath_pause(adev, cpath); -- -- mutex_unlock(&adev->path_mutex); - } - - int avs_path_pause(struct avs_path *path) -@@ -1560,7 +1550,7 @@ static void avs_condpaths_run(struct avs_dev *adev, struct avs_path *path, int t - { - struct avs_path *cpath; - -- mutex_lock(&adev->path_mutex); -+ guard(mutex)(&adev->path_mutex); - - /* Run conditional paths only if source and sink are both running. */ - list_for_each_entry(cpath, &path->source_list, source_node) -@@ -1572,8 +1562,6 @@ static void avs_condpaths_run(struct avs_dev *adev, struct avs_path *path, int t - if (cpath->source->state == AVS_PPL_STATE_RUNNING && - cpath->sink->state == AVS_PPL_STATE_RUNNING) - avs_condpath_run(adev, cpath, trigger); -- -- mutex_unlock(&adev->path_mutex); - } - - int avs_path_run(struct avs_path *path, int trigger) -diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c ---- a/sound/soc/intel/avs/pcm.c -+++ b/sound/soc/intel/avs/pcm.c -@@ -958,7 +958,7 @@ static const struct file_operations topology_name_fops = { - static int avs_component_load_libraries(struct avs_soc_component *acomp) - { - struct avs_tplg *tplg = acomp->tplg; -- struct avs_dev *adev = to_avs_dev(acomp->base.dev); -+ struct avs_dev *adev = to_avs_dev(acomp->base->dev); - int ret; - - if (!tplg->num_libs) -@@ -1387,25 +1387,28 @@ int avs_register_component(struct device *dev, const char *name, - struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais) - { - struct avs_soc_component *acomp; -- int ret; -+ const char *comp_name; - - acomp = devm_kzalloc(dev, sizeof(*acomp), GFP_KERNEL); - if (!acomp) - return -ENOMEM; - -- acomp->base.name = devm_kstrdup(dev, name, GFP_KERNEL); -- if (!acomp->base.name) -+ acomp->base = snd_soc_component_alloc(dev); -+ if (!acomp->base) -+ return -ENOMEM; -+ -+ comp_name = devm_kstrdup(dev, name, GFP_KERNEL); -+ if (!comp_name) - return -ENOMEM; - - INIT_LIST_HEAD(&acomp->node); - - drv->use_dai_pcm_id = !obsolete_card_names; - -- ret = snd_soc_component_initialize(&acomp->base, drv, dev); -- if (ret < 0) -- return ret; -+ snd_soc_component_set_name(acomp->base, comp_name); -+ snd_soc_component_set_priv(acomp->base, acomp); - -- return snd_soc_add_component(&acomp->base, cpu_dais, num_cpu_dais); -+ return snd_soc_register_component(acomp->base, drv, cpu_dais, num_cpu_dais); - } - - static struct snd_soc_dai_driver dmic_cpu_dais[] = { diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c --- a/sound/soc/intel/avs/probes.c +++ b/sound/soc/intel/avs/probes.c @@ -38060,1561 +11352,6 @@ diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c if (bps < 0) return bps; format_val = snd_hdac_stream_format(params->codec.ch_out, bps, params->codec.sample_rate); -@@ -296,19 +296,19 @@ static const struct snd_soc_component_driver avs_probe_component_driver = { - int avs_register_probe_component(struct avs_dev *adev, const char *name) - { - struct snd_soc_component *component; -- int ret; -+ const char *comp_name; - -- component = devm_kzalloc(adev->dev, sizeof(*component), GFP_KERNEL); -+ component = snd_soc_component_alloc(adev->dev); - if (!component) - return -ENOMEM; - -- component->name = devm_kstrdup(adev->dev, name, GFP_KERNEL); -- if (!component->name) -+ comp_name = devm_kstrdup(adev->dev, name, GFP_KERNEL); -+ if (!comp_name) - return -ENOMEM; - -- ret = snd_soc_component_initialize(component, &avs_probe_component_driver, adev->dev); -- if (ret) -- return ret; -+ snd_soc_component_set_name(component, comp_name); - -- return snd_soc_add_component(component, probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais)); -+ return snd_soc_register_component(component, -+ &avs_probe_component_driver, -+ probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais)); - } -diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c ---- a/sound/soc/intel/avs/topology.c -+++ b/sound/soc/intel/avs/topology.c -@@ -2222,7 +2222,7 @@ struct avs_tplg *avs_tplg_new(struct snd_soc_component *comp) - - int avs_load_topology(struct snd_soc_component *comp, const char *filename) - { -- const struct firmware *fw; -+ const struct firmware *fw __free(firmware) = NULL; - int ret; - - ret = request_firmware(&fw, filename, comp->dev); -@@ -2235,7 +2235,6 @@ int avs_load_topology(struct snd_soc_component *comp, const char *filename) - if (ret < 0) - dev_err(comp->dev, "load topology \"%s\" failed: %d\n", filename, ret); - -- release_firmware(fw); - return ret; - } - -diff --git a/sound/soc/intel/avs/utils.c b/sound/soc/intel/avs/utils.c ---- a/sound/soc/intel/avs/utils.c -+++ b/sound/soc/intel/avs/utils.c -@@ -6,6 +6,7 @@ - // Amadeusz Slawinski - // - -+#include - #include - #include - #include -@@ -48,13 +49,12 @@ int avs_get_module_entry(struct avs_dev *adev, const guid_t *uuid, struct avs_mo - { - int idx; - -- mutex_lock(&adev->modres_mutex); -+ guard(mutex)(&adev->modres_mutex); - - idx = avs_module_entry_index(adev, uuid); - if (idx >= 0) - memcpy(entry, &adev->mods_info->entries[idx], sizeof(*entry)); - -- mutex_unlock(&adev->modres_mutex); - return (idx < 0) ? idx : 0; - } - -@@ -62,13 +62,12 @@ int avs_get_module_id_entry(struct avs_dev *adev, u32 module_id, struct avs_modu - { - int idx; - -- mutex_lock(&adev->modres_mutex); -+ guard(mutex)(&adev->modres_mutex); - - idx = avs_module_id_entry_index(adev, module_id); - if (idx >= 0) - memcpy(entry, &adev->mods_info->entries[idx], sizeof(*entry)); - -- mutex_unlock(&adev->modres_mutex); - return (idx < 0) ? idx : 0; - } - -@@ -86,13 +85,12 @@ bool avs_is_module_ida_empty(struct avs_dev *adev, u32 module_id) - bool ret = false; - int idx; - -- mutex_lock(&adev->modres_mutex); -+ guard(mutex)(&adev->modres_mutex); - - idx = avs_module_id_entry_index(adev, module_id); - if (idx >= 0) - ret = ida_is_empty(adev->mod_idas[idx]); - -- mutex_unlock(&adev->modres_mutex); - return ret; - } - -@@ -163,68 +161,57 @@ int avs_module_info_init(struct avs_dev *adev, bool purge) - if (ret) - return AVS_IPC_RET(ret); - -- mutex_lock(&adev->modres_mutex); -+ guard(mutex)(&adev->modres_mutex); - - ret = avs_module_ida_alloc(adev, info, purge); - if (ret < 0) { - dev_err(adev->dev, "initialize module idas failed: %d\n", ret); -- goto exit; -+ return ret; - } - - /* Refresh current information with newly received table. */ - kfree(adev->mods_info); - adev->mods_info = info; - --exit: -- mutex_unlock(&adev->modres_mutex); - return ret; - } - - void avs_module_info_free(struct avs_dev *adev) - { -- mutex_lock(&adev->modres_mutex); -+ guard(mutex)(&adev->modres_mutex); - - avs_module_ida_destroy(adev); - kfree(adev->mods_info); - adev->mods_info = NULL; -- -- mutex_unlock(&adev->modres_mutex); - } - - int avs_module_id_alloc(struct avs_dev *adev, u16 module_id) - { -- int ret, idx, max_id; -+ int idx, max_id; - -- mutex_lock(&adev->modres_mutex); -+ guard(mutex)(&adev->modres_mutex); - - idx = avs_module_id_entry_index(adev, module_id); - if (idx == -ENOENT) { - dev_err(adev->dev, "invalid module id: %d", module_id); -- ret = -EINVAL; -- goto exit; -+ return -EINVAL; - } - max_id = adev->mods_info->entries[idx].instance_max_count - 1; -- ret = ida_alloc_max(adev->mod_idas[idx], max_id, GFP_KERNEL); --exit: -- mutex_unlock(&adev->modres_mutex); -- return ret; -+ -+ return ida_alloc_max(adev->mod_idas[idx], max_id, GFP_KERNEL); - } - - void avs_module_id_free(struct avs_dev *adev, u16 module_id, u8 instance_id) - { - int idx; - -- mutex_lock(&adev->modres_mutex); -+ guard(mutex)(&adev->modres_mutex); - - idx = avs_module_id_entry_index(adev, module_id); -- if (idx == -ENOENT) { -+ if (idx == -ENOENT) - dev_err(adev->dev, "invalid module id: %d", module_id); -- goto exit; -- } -- -- ida_free(adev->mod_idas[idx], instance_id); --exit: -- mutex_unlock(&adev->modres_mutex); -+ else -+ ida_free(adev->mod_idas[idx], instance_id); - } - - /* -diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c ---- a/sound/soc/intel/boards/sof_sdw.c -+++ b/sound/soc/intel/boards/sof_sdw.c -@@ -1288,7 +1288,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) - goto err_dai; - } - -- ret = asoc_sdw_parse_sdw_endpoints(card, sof_aux, sof_dais, sof_ends, &num_confs); -+ ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_confs); - if (ret < 0) - goto err_end; - -@@ -1483,12 +1483,12 @@ static int mc_probe(struct platform_device *pdev) - dmi_check_system(sof_sdw_quirk_table); - - if (quirk_override != -1) { -- dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n", -+ dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n", - sof_sdw_quirk, quirk_override); - sof_sdw_quirk = quirk_override; - } - -- log_quirks(card->dev); -+ log_quirks(&pdev->dev); - - ctx->mc_quirk = sof_sdw_quirk; - /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ -@@ -1507,13 +1507,13 @@ static int mc_probe(struct platform_device *pdev) - for (i = 0; i < ctx->codec_info_list_count; i++) - amp_num += codec_info_list[i].amp_num; - -- card->components = devm_kasprintf(card->dev, GFP_KERNEL, -+ card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, - " cfg-amp:%d", amp_num); - if (!card->components) - return -ENOMEM; - - if (mach->mach_params.dmic_num) { -- card->components = devm_kasprintf(card->dev, GFP_KERNEL, -+ card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, - "%s mic:dmic cfg-mics:%d", - card->components, - mach->mach_params.dmic_num); -@@ -1524,7 +1524,7 @@ static int mc_probe(struct platform_device *pdev) - /* Register the card */ - ret = devm_snd_soc_register_card(card->dev, card); - if (ret) { -- dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret); -+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed %d\n", ret); - asoc_sdw_mc_dailink_exit_loop(card); - return ret; - } -@@ -1542,8 +1542,8 @@ static void mc_remove(struct platform_device *pdev) - } - - static const struct platform_device_id mc_id_table[] = { -- { "sof_sdw", }, -- {} -+ { .name = "sof_sdw" }, -+ { } - }; - MODULE_DEVICE_TABLE(platform, mc_id_table); - -diff --git a/sound/soc/intel/catpt/core.h b/sound/soc/intel/catpt/core.h ---- a/sound/soc/intel/catpt/core.h -+++ b/sound/soc/intel/catpt/core.h -@@ -139,9 +139,7 @@ int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request, - - int catpt_first_boot_firmware(struct catpt_dev *cdev); - int catpt_boot_firmware(struct catpt_dev *cdev, bool restore); --int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan); --int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan); --int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan); -+int catpt_store_firmware_context(struct catpt_dev *cdev); - int catpt_coredump(struct catpt_dev *cdev); - - #include -diff --git a/sound/soc/intel/catpt/device.c b/sound/soc/intel/catpt/device.c ---- a/sound/soc/intel/catpt/device.c -+++ b/sound/soc/intel/catpt/device.c -@@ -28,44 +28,17 @@ - static int catpt_do_suspend(struct device *dev) - { - struct catpt_dev *cdev = dev_get_drvdata(dev); -- struct dma_chan *chan; - int ret; - -- chan = catpt_dma_request_config_chan(cdev); -- if (IS_ERR(chan)) -- return PTR_ERR(chan); -- - memset(&cdev->dx_ctx, 0, sizeof(cdev->dx_ctx)); - ret = catpt_ipc_enter_dxstate(cdev, CATPT_DX_STATE_D3, &cdev->dx_ctx); -- if (ret) { -- ret = CATPT_IPC_RET(ret); -- goto release_dma_chan; -- } -- -- ret = catpt_dsp_stall(cdev, true); - if (ret) -- goto release_dma_chan; -+ return CATPT_IPC_RET(ret); - -- ret = catpt_store_memdumps(cdev, chan); -- if (ret) { -- dev_err(cdev->dev, "store memdumps failed: %d\n", ret); -- goto release_dma_chan; -- } -- -- ret = catpt_store_module_states(cdev, chan); -- if (ret) { -- dev_err(cdev->dev, "store module states failed: %d\n", ret); -- goto release_dma_chan; -- } -- -- ret = catpt_store_streams_context(cdev, chan); -- if (ret) -- dev_err(cdev->dev, "store streams ctx failed: %d\n", ret); -- --release_dma_chan: -- dma_release_channel(chan); -+ ret = catpt_store_firmware_context(cdev); - if (ret) - return ret; -+ - return catpt_dsp_power_down(cdev); - } - -diff --git a/sound/soc/intel/catpt/loader.c b/sound/soc/intel/catpt/loader.c ---- a/sound/soc/intel/catpt/loader.c -+++ b/sound/soc/intel/catpt/loader.c -@@ -26,7 +26,7 @@ struct catpt_fw_hdr { - u32 reserved[4]; - } __packed; - --struct catpt_fw_mod_hdr { -+struct catpt_fw_module_hdr { - char signature[FW_SIGNATURE_SIZE]; - u32 mod_size; - u32 blocks; -@@ -81,7 +81,7 @@ catpt_request_region(struct resource *root, resource_size_t size) - return __request_region(root, addr, size, NULL, 0); - } - --int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) -+static int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) - { - struct catpt_stream_runtime *stream; - -@@ -108,7 +108,7 @@ int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) - return 0; - } - --int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan) -+static int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan) - { - int i; - -@@ -138,7 +138,7 @@ int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan) - return 0; - } - --int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) -+static int catpt_store_dram_data(struct catpt_dev *cdev, struct dma_chan *chan) - { - int i; - -@@ -171,6 +171,39 @@ int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) - return 0; - } - -+int catpt_store_firmware_context(struct catpt_dev *cdev) -+{ -+ struct dma_chan *chan; -+ int ret; -+ -+ chan = catpt_dma_request_config_chan(cdev); -+ if (IS_ERR(chan)) -+ return PTR_ERR(chan); -+ -+ ret = catpt_dsp_stall(cdev, true); -+ if (ret) -+ goto exit; -+ -+ ret = catpt_store_dram_data(cdev, chan); -+ if (ret) { -+ dev_err(cdev->dev, "store memdumps failed: %d\n", ret); -+ goto exit; -+ } -+ -+ ret = catpt_store_module_states(cdev, chan); -+ if (ret) { -+ dev_err(cdev->dev, "store module states failed: %d\n", ret); -+ goto exit; -+ } -+ -+ ret = catpt_store_streams_context(cdev, chan); -+ if (ret) -+ dev_err(cdev->dev, "store streams ctx failed: %d\n", ret); -+exit: -+ dma_release_channel(chan); -+ return ret; -+} -+ - static int - catpt_restore_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) - { -@@ -199,7 +232,7 @@ catpt_restore_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) - return 0; - } - --static int catpt_restore_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) -+static int catpt_restore_dram_data(struct catpt_dev *cdev, struct dma_chan *chan) - { - int i; - -@@ -234,9 +267,9 @@ static int catpt_restore_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) - return 0; - } - --static int catpt_restore_fwimage(struct catpt_dev *cdev, -- struct dma_chan *chan, dma_addr_t paddr, -- struct catpt_fw_block_hdr *blk) -+static int catpt_restore_dram_rodata(struct catpt_dev *cdev, -+ struct dma_chan *chan, dma_addr_t paddr, -+ struct catpt_fw_block_hdr *blk) - { - struct resource r1 = {}; - int i; -@@ -324,28 +357,26 @@ static int catpt_load_block(struct catpt_dev *cdev, - - static int catpt_restore_basefw(struct catpt_dev *cdev, - struct dma_chan *chan, dma_addr_t paddr, -- struct catpt_fw_mod_hdr *basefw) -+ struct catpt_fw_module_hdr *basefw) - { -- u32 offset = sizeof(*basefw); -+ u32 off = sizeof(*basefw); - int ret, i; - - print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, - basefw, sizeof(*basefw), false); - -- /* restore basefw image */ -+ /* Restore IRAM and .rodata for DRAM based on the firmware image. */ - for (i = 0; i < basefw->blocks; i++) { - struct catpt_fw_block_hdr *blk; - -- blk = (struct catpt_fw_block_hdr *)((u8 *)basefw + offset); -+ blk = (struct catpt_fw_block_hdr *)((u8 *)basefw + off); - - switch (blk->ram_type) { - case CATPT_RAM_TYPE_IRAM: -- ret = catpt_load_block(cdev, chan, paddr + offset, -- blk, false); -+ ret = catpt_load_block(cdev, chan, paddr + off, blk, false); - break; - default: -- ret = catpt_restore_fwimage(cdev, chan, paddr + offset, -- blk); -+ ret = catpt_restore_dram_rodata(cdev, chan, paddr + off, blk); - break; - } - -@@ -354,11 +385,11 @@ static int catpt_restore_basefw(struct catpt_dev *cdev, - return ret; - } - -- offset += sizeof(*blk) + blk->size; -+ off += sizeof(*blk) + blk->size; - } - -- /* then proceed with memory dumps */ -- ret = catpt_restore_memdumps(cdev, chan); -+ /* Then proceed with DRAM .data saved before D3. */ -+ ret = catpt_restore_dram_data(cdev, chan); - if (ret) - dev_err(cdev->dev, "restore memdumps failed: %d\n", ret); - -@@ -367,9 +398,9 @@ static int catpt_restore_basefw(struct catpt_dev *cdev, - - static int catpt_restore_module(struct catpt_dev *cdev, - struct dma_chan *chan, dma_addr_t paddr, -- struct catpt_fw_mod_hdr *mod) -+ struct catpt_fw_module_hdr *mod) - { -- u32 offset = sizeof(*mod); -+ u32 off = sizeof(*mod); - int i; - - print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, -@@ -379,7 +410,7 @@ static int catpt_restore_module(struct catpt_dev *cdev, - struct catpt_fw_block_hdr *blk; - int ret; - -- blk = (struct catpt_fw_block_hdr *)((u8 *)mod + offset); -+ blk = (struct catpt_fw_block_hdr *)((u8 *)mod + off); - - switch (blk->ram_type) { - case CATPT_RAM_TYPE_INSTANCE: -@@ -390,7 +421,7 @@ static int catpt_restore_module(struct catpt_dev *cdev, - ALIGN(blk->size, 4)); - break; - default: -- ret = catpt_load_block(cdev, chan, paddr + offset, -+ ret = catpt_load_block(cdev, chan, paddr + off, - blk, false); - break; - } -@@ -400,7 +431,7 @@ static int catpt_restore_module(struct catpt_dev *cdev, - return ret; - } - -- offset += sizeof(*blk) + blk->size; -+ off += sizeof(*blk) + blk->size; - } - - return 0; -@@ -408,10 +439,10 @@ static int catpt_restore_module(struct catpt_dev *cdev, - - static int catpt_load_module(struct catpt_dev *cdev, - struct dma_chan *chan, dma_addr_t paddr, -- struct catpt_fw_mod_hdr *mod) -+ struct catpt_fw_module_hdr *mod) - { - struct catpt_module_type *type; -- u32 offset = sizeof(*mod); -+ u32 off = sizeof(*mod); - int i; - - print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, -@@ -423,9 +454,9 @@ static int catpt_load_module(struct catpt_dev *cdev, - struct catpt_fw_block_hdr *blk; - int ret; - -- blk = (struct catpt_fw_block_hdr *)((u8 *)mod + offset); -+ blk = (struct catpt_fw_block_hdr *)((u8 *)mod + off); - -- ret = catpt_load_block(cdev, chan, paddr + offset, blk, true); -+ ret = catpt_load_block(cdev, chan, paddr + off, blk, true); - if (ret) { - dev_err(cdev->dev, "load block failed: %d\n", ret); - return ret; -@@ -440,7 +471,7 @@ static int catpt_load_module(struct catpt_dev *cdev, - type->state_size = blk->size; - } - -- offset += sizeof(*blk) + blk->size; -+ off += sizeof(*blk) + blk->size; - } - - /* init module type static info */ -@@ -457,17 +488,17 @@ static int catpt_restore_firmware(struct catpt_dev *cdev, - struct dma_chan *chan, dma_addr_t paddr, - struct catpt_fw_hdr *fw) - { -- u32 offset = sizeof(*fw); -+ u32 off = sizeof(*fw); - int i; - - print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, - fw, sizeof(*fw), false); - - for (i = 0; i < fw->modules; i++) { -- struct catpt_fw_mod_hdr *mod; -+ struct catpt_fw_module_hdr *mod; - int ret; - -- mod = (struct catpt_fw_mod_hdr *)((u8 *)fw + offset); -+ mod = (struct catpt_fw_module_hdr *)((u8 *)fw + off); - if (strncmp(fw->signature, mod->signature, - FW_SIGNATURE_SIZE)) { - dev_err(cdev->dev, "module signature mismatch\n"); -@@ -479,12 +510,10 @@ static int catpt_restore_firmware(struct catpt_dev *cdev, - - switch (mod->module_id) { - case CATPT_MODID_BASE_FW: -- ret = catpt_restore_basefw(cdev, chan, paddr + offset, -- mod); -+ ret = catpt_restore_basefw(cdev, chan, paddr + off, mod); - break; - default: -- ret = catpt_restore_module(cdev, chan, paddr + offset, -- mod); -+ ret = catpt_restore_module(cdev, chan, paddr + off, mod); - break; - } - -@@ -493,7 +522,7 @@ static int catpt_restore_firmware(struct catpt_dev *cdev, - return ret; - } - -- offset += sizeof(*mod) + mod->mod_size; -+ off += sizeof(*mod) + mod->mod_size; - } - - return 0; -@@ -503,17 +532,17 @@ static int catpt_load_firmware(struct catpt_dev *cdev, - struct dma_chan *chan, dma_addr_t paddr, - struct catpt_fw_hdr *fw) - { -- u32 offset = sizeof(*fw); -+ u32 off = sizeof(*fw); - int i; - - print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, - fw, sizeof(*fw), false); - - for (i = 0; i < fw->modules; i++) { -- struct catpt_fw_mod_hdr *mod; -+ struct catpt_fw_module_hdr *mod; - int ret; - -- mod = (struct catpt_fw_mod_hdr *)((u8 *)fw + offset); -+ mod = (struct catpt_fw_module_hdr *)((u8 *)fw + off); - if (strncmp(fw->signature, mod->signature, - FW_SIGNATURE_SIZE)) { - dev_err(cdev->dev, "module signature mismatch\n"); -@@ -523,44 +552,40 @@ static int catpt_load_firmware(struct catpt_dev *cdev, - if (mod->module_id > CATPT_MODID_LAST) - return -EINVAL; - -- ret = catpt_load_module(cdev, chan, paddr + offset, mod); -+ ret = catpt_load_module(cdev, chan, paddr + off, mod); - if (ret) { - dev_err(cdev->dev, "load module failed: %d\n", ret); - return ret; - } - -- offset += sizeof(*mod) + mod->mod_size; -+ off += sizeof(*mod) + mod->mod_size; - } - - return 0; - } - --static int catpt_load_image(struct catpt_dev *cdev, struct dma_chan *chan, -- const char *name, const char *signature, -- bool restore) -+static int catpt_request_load_firmware(struct catpt_dev *cdev, struct dma_chan *chan, -+ const char *name, bool restore) - { - struct catpt_fw_hdr *fw; -- struct firmware *img; - dma_addr_t paddr; - void *vaddr; - int ret; - -- ret = request_firmware((const struct firmware **)&img, name, cdev->dev); -+ const struct firmware *img __free(firmware) = NULL; -+ ret = request_firmware(&img, name, cdev->dev); - if (ret) - return ret; - - fw = (struct catpt_fw_hdr *)img->data; -- if (strncmp(fw->signature, signature, FW_SIGNATURE_SIZE)) { -+ if (strncmp(fw->signature, FW_SIGNATURE, FW_SIGNATURE_SIZE)) { - dev_err(cdev->dev, "firmware signature mismatch\n"); -- ret = -EINVAL; -- goto release_fw; -+ return -EINVAL; - } - - vaddr = dma_alloc_coherent(cdev->dev, img->size, &paddr, GFP_KERNEL); -- if (!vaddr) { -- ret = -ENOMEM; -- goto release_fw; -- } -+ if (!vaddr) -+ return -ENOMEM; - - memcpy(vaddr, img->data, img->size); - fw = (struct catpt_fw_hdr *)vaddr; -@@ -570,12 +595,10 @@ static int catpt_load_image(struct catpt_dev *cdev, struct dma_chan *chan, - ret = catpt_load_firmware(cdev, chan, paddr, fw); - - dma_free_coherent(cdev->dev, img->size, vaddr, paddr); --release_fw: -- release_firmware(img); - return ret; - } - --static int catpt_load_images(struct catpt_dev *cdev, bool restore) -+static int catpt_request_dma_load_firmware(struct catpt_dev *cdev, bool restore) - { - struct dma_chan *chan; - int ret; -@@ -584,8 +607,7 @@ static int catpt_load_images(struct catpt_dev *cdev, bool restore) - if (IS_ERR(chan)) - return PTR_ERR(chan); - -- ret = catpt_load_image(cdev, chan, cdev->spec->fw_name, -- FW_SIGNATURE, restore); -+ ret = catpt_request_load_firmware(cdev, chan, cdev->spec->fw_name, restore); - if (ret) - goto release_dma_chan; - -@@ -605,7 +627,7 @@ int catpt_boot_firmware(struct catpt_dev *cdev, bool restore) - - catpt_dsp_stall(cdev, true); - -- ret = catpt_load_images(cdev, restore); -+ ret = catpt_request_dma_load_firmware(cdev, restore); - if (ret) { - dev_err(cdev->dev, "load binaries failed: %d\n", ret); - return ret; -@@ -619,10 +641,10 @@ int catpt_boot_firmware(struct catpt_dev *cdev, bool restore) - if (!ret) { - dev_err(cdev->dev, "firmware ready timeout\n"); - return -ETIMEDOUT; -- /* Wake up does not mean FW is ready, an exception could occur. */ -- } else if (!cdev->ipc.ready) { -- return -EREMOTEIO; - } -+ /* Wake up does not mean FW is ready, an exception could occur. */ -+ if (!cdev->ipc.ready) -+ return -EREMOTEIO; - - /* update sram pg & clock once done booting */ - catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask); -diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c ---- a/sound/soc/intel/catpt/pcm.c -+++ b/sound/soc/intel/catpt/pcm.c -@@ -75,8 +75,8 @@ static struct catpt_stream_template *catpt_topology[] = { - static struct catpt_stream_template * - catpt_get_stream_template(struct snd_pcm_substream *substream) - { -- struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream); -- struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtm, 0); -+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); -+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); - enum catpt_stream_type type; - - type = cpu_dai->driver->id; -@@ -159,11 +159,11 @@ static void catpt_stream_read_position(struct catpt_dev *cdev, - static void catpt_arrange_page_table(struct snd_pcm_substream *substream, - struct snd_dma_buffer *pgtbl) - { -- struct snd_pcm_runtime *rtm = substream->runtime; -+ struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_dma_buffer *databuf = snd_pcm_get_dma_buf(substream); - int i, pages; - -- pages = snd_sgbuf_aligned_pages(rtm->dma_bytes); -+ pages = snd_sgbuf_aligned_pages(runtime->dma_bytes); - - for (i = 0; i < pages; i++) { - u32 pfn, offset; -@@ -386,7 +386,7 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) - { -- struct snd_pcm_runtime *rtm = substream->runtime; -+ struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_dma_buffer *dmab; - struct catpt_stream_runtime *stream; - struct catpt_audio_format afmt; -@@ -412,8 +412,8 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream, - - memset(&rinfo, 0, sizeof(rinfo)); - rinfo.page_table_addr = stream->pgtbl.addr; -- rinfo.num_pages = DIV_ROUND_UP(rtm->dma_bytes, PAGE_SIZE); -- rinfo.size = rtm->dma_bytes; -+ rinfo.num_pages = DIV_ROUND_UP(runtime->dma_bytes, PAGE_SIZE); -+ rinfo.size = runtime->dma_bytes; - rinfo.offset = 0; - rinfo.ring_first_page_pfn = PFN_DOWN(snd_sgbuf_get_addr(dmab, 0)); - -@@ -544,11 +544,11 @@ void catpt_stream_update_position(struct catpt_dev *cdev, - struct catpt_notify_position *pos) - { - struct snd_pcm_substream *substream = stream->substream; -- struct snd_pcm_runtime *r = substream->runtime; -+ struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_uframes_t dsppos, newpos; - int ret; - -- dsppos = bytes_to_frames(r, pos->stream_position); -+ dsppos = bytes_to_frames(runtime, pos->stream_position); - - if (!stream->prepared) - goto exit; -@@ -556,8 +556,8 @@ void catpt_stream_update_position(struct catpt_dev *cdev, - if (stream->template->type != CATPT_STRM_TYPE_RENDER) - goto exit; - -- if (dsppos >= r->buffer_size / 2) -- newpos = r->buffer_size / 2; -+ if (dsppos >= runtime->buffer_size / 2) -+ newpos = runtime->buffer_size / 2; - else - newpos = 0; - /* -@@ -565,7 +565,7 @@ void catpt_stream_update_position(struct catpt_dev *cdev, - * (buffer half consumed) update wp to allow stream progression. - */ - ret = catpt_ipc_set_write_pos(cdev, stream->info.stream_hw_id, -- frames_to_bytes(r, newpos), -+ frames_to_bytes(runtime, newpos), - false, false); - if (ret) { - dev_err(cdev->dev, "update position for stream %d failed: %d\n", -@@ -600,11 +600,11 @@ static const struct snd_pcm_hardware catpt_pcm_hardware = { - }; - - static int catpt_component_pcm_new(struct snd_soc_component *component, -- struct snd_soc_pcm_runtime *rtm) -+ struct snd_soc_pcm_runtime *rtd) - { - struct catpt_dev *cdev = dev_get_drvdata(component->dev); - -- snd_pcm_set_managed_buffer_all(rtm->pcm, SNDRV_DMA_TYPE_DEV_SG, -+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV_SG, - cdev->dev, - catpt_pcm_hardware.buffer_bytes_max, - catpt_pcm_hardware.buffer_bytes_max); -@@ -615,9 +615,9 @@ static int catpt_component_pcm_new(struct snd_soc_component *component, - static int catpt_component_open(struct snd_soc_component *component, - struct snd_pcm_substream *substream) - { -- struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream); -+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - -- if (!rtm->dai_link->no_pcm) -+ if (!rtd->dai_link->no_pcm) - snd_soc_set_runtime_hwparams(substream, &catpt_pcm_hardware); - return 0; - } -@@ -626,13 +626,13 @@ static snd_pcm_uframes_t - catpt_component_pointer(struct snd_soc_component *component, - struct snd_pcm_substream *substream) - { -- struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream); -- struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtm, 0); -+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); -+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); - struct catpt_stream_runtime *stream; - struct catpt_dev *cdev = dev_get_drvdata(component->dev); - u32 pos; - -- if (rtm->dai_link->no_pcm) -+ if (rtd->dai_link->no_pcm) - return 0; - - stream = snd_soc_dai_get_dma_data(cpu_dai, substream); -@@ -650,10 +650,10 @@ static const struct snd_soc_dai_ops catpt_fe_dai_ops = { - .trigger = catpt_dai_trigger, - }; - --static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtm, -+static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_dai *dai) - { -- struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtm, 0); -+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); - struct catpt_ssp_device_format devfmt; - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); - int ret; -@@ -871,8 +871,7 @@ static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol) - return CATPT_IPC_RET(ret); - } - --static int catpt_volume_info(struct snd_kcontrol *kcontrol, -- struct snd_ctl_elem_info *uinfo) -+static int catpt_volume_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) - { - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = CATPT_CHANNELS_MAX; -@@ -1073,18 +1072,14 @@ int catpt_arm_stream_templates(struct catpt_dev *cdev) - int catpt_register_plat_component(struct catpt_dev *cdev) - { - struct snd_soc_component *component; -- int ret; - -- component = devm_kzalloc(cdev->dev, sizeof(*component), GFP_KERNEL); -+ component = snd_soc_component_alloc(cdev->dev); - if (!component) - return -ENOMEM; - -- ret = snd_soc_component_initialize(component, &catpt_comp_driver, -- cdev->dev); -- if (ret) -- return ret; -+ snd_soc_component_set_name(component, catpt_comp_driver.name); - -- component->name = catpt_comp_driver.name; -- return snd_soc_add_component(component, dai_drivers, -- ARRAY_SIZE(dai_drivers)); -+ return snd_soc_register_component(component, -+ &catpt_comp_driver, -+ dai_drivers, ARRAY_SIZE(dai_drivers)); - } -diff --git a/sound/soc/intel/common/soc-acpi-intel-arl-match.c b/sound/soc/intel/common/soc-acpi-intel-arl-match.c ---- a/sound/soc/intel/common/soc-acpi-intel-arl-match.c -+++ b/sound/soc/intel/common/soc-acpi-intel-arl-match.c -@@ -572,6 +572,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = { - .links = arl_rt711_l0_rt1316_l3, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-arl-rt711-l0-rt1316-l3.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0) | BIT(3), -@@ -600,12 +601,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = { - .links = arl_rvp, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-arl-rt711.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = 0x1, /* link0 required */ - .links = arl_sdca_rvp, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-arl-rt711-l0.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(2), -diff --git a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c ---- a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c -+++ b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c -@@ -717,24 +717,28 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { - .links = lnl_3_in_1_sdca, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-lnl-rt711-l0-rt1316-l23-rt714-l1.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0) | BIT(2) | BIT(3), - .links = lnl_cs42l43_l0_cs35l56_l23, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-lnl-cs42l43-l0-cs35l56-l23.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(1) | BIT(2) | BIT(3), - .links = lnl_cs42l43_l2_cs35l56x6_l13, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-lnl-cs42l43-l2-cs35l56x6-l13.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0) | BIT(3), - .links = lnl_cs42l43_l0_cs35l56_l3, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-lnl-cs42l43-l0-cs35l56-l3.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0), -@@ -748,12 +752,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { - .links = lnl_rvp, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-lnl-rt711.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(2) | BIT(3), - .links = lnl_712_only, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-lnl-rt712-l2-rt1712-l3.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0), -@@ -766,19 +772,22 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { - .link_mask = GENMASK(2, 0), - .links = lnl_sdw_rt1318_l12_rt714_l0, - .drv_name = "sof_sdw", -- .sof_tplg_filename = "sof-lnl-rt1318-l12-rt714-l0.tplg" -+ .sof_tplg_filename = "sof-lnl-rt1318-l12-rt714-l0.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = GENMASK(2, 0), - .links = lnl_sdw_rt1320_l12_rt714_l0, - .drv_name = "sof_sdw", -- .sof_tplg_filename = "sof-lnl-rt1320-l12-rt714-l0.tplg" -+ .sof_tplg_filename = "sof-lnl-rt1320-l12-rt714-l0.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0) | BIT(1), - .links = lnl_sdw_rt713_l0_rt1318_l1, - .drv_name = "sof_sdw", -- .sof_tplg_filename = "sof-lnl-rt713-l0-rt1318-l1.tplg" -+ .sof_tplg_filename = "sof-lnl-rt713-l0-rt1318-l1.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(1) | BIT(2), -diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c ---- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c -+++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c -@@ -1321,60 +1321,70 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { - .links = tac5572_l0, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-tac5572.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0), - .links = tac5672_l0, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-tac5672.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0), - .links = tac5682_l0, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-tac5682.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0), - .links = tas2783_link0, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-tas2783.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0), - .links = tas2883_l0, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-tas2883.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = GENMASK(3, 0), - .links = mtl_rt713_l0_rt1316_l12_rt1713_l3, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-rt713-l0-rt1316-l12-rt1713-l3.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = GENMASK(3, 0), - .links = mtl_rt713_l0_rt1318_l12_rt1713_l3, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-rt713-l0-rt1318-l12-rt1713-l3.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0) | BIT(1) | BIT(3), - .links = mtl_rt713_l0_rt1318_l1_rt1713_l3, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-rt713-l0-rt1318-l1-rt1713-l3.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = GENMASK(2, 0), - .links = mtl_rt713_l0_rt1316_l12, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-rt713-l0-rt1316-l12.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(3) | BIT(0), - .links = mtl_712_l0_1712_l3, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-rt712-l0-rt1712-l3.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0), -@@ -1395,7 +1405,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { - .link_mask = GENMASK(2, 0), - .links = mtl_sdw_rt1318_l12_rt714_l0, - .drv_name = "sof_sdw", -- .sof_tplg_filename = "sof-mtl-rt1318-l12-rt714-l0.tplg" -+ .sof_tplg_filename = "sof-mtl-rt1318-l12-rt714-l0.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0) | BIT(2) | BIT(3), -@@ -1455,12 +1466,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { - .links = mtl_3_in_1_sdca, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-rt711-l0-rt1316-l23-rt714-l1.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = 0x9, /* 2 active links required */ - .links = mtl_rt711_l0_rt1316_l3, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-rt711-l0-rt1316-l3.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0), -@@ -1474,18 +1487,21 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { - .links = mtl_rvp, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-rt711.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0) | BIT(2), - .links = rt5682_link2_max98373_link0, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-sdw-rt5682-l2-max98373-l0.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(0) | BIT(2), - .links = cs42l42_link0_max98363_link2, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-mtl-sdw-cs42l42-l0-max98363-l2.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - {}, - }; -diff --git a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c ---- a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c -+++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c -@@ -492,7 +492,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = { - .links = ptl_rt722_l0_rt1320_l23, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-ptl-rt722-l0-rt1320-l23.tplg", -- .get_function_tplg_files = sof_sdw_get_tplg_files, -+ .machine_check = snd_soc_acpi_intel_no_function_topology, - }, - { - .link_mask = BIT(1) | BIT(2), -@@ -522,14 +522,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = { - .links = ptl_rvp, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-ptl-rt711.tplg", -+ .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - { - .link_mask = BIT(3), - .links = ptl_sdw_rt712_vb_l3_rt1320_l3, - .drv_name = "sof_sdw", -- .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, -+ .machine_check = snd_soc_acpi_intel_rt712_vb_no_function_topology, - .sof_tplg_filename = "sof-ptl-rt712-l3-rt1320-l3.tplg", -- .get_function_tplg_files = sof_sdw_get_tplg_files, - }, - {}, - }; -diff --git a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c ---- a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c -+++ b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c -@@ -53,6 +53,14 @@ bool snd_soc_acpi_intel_no_function_topology(void *arg) - } - EXPORT_SYMBOL_NS(snd_soc_acpi_intel_no_function_topology, "SND_SOC_ACPI_INTEL_SDCA_QUIRKS"); - -+bool snd_soc_acpi_intel_rt712_vb_no_function_topology(void *arg) -+{ -+ return snd_soc_acpi_intel_sdca_is_device_rt712_vb(arg) && -+ snd_soc_acpi_intel_no_function_topology(arg); -+} -+EXPORT_SYMBOL_NS(snd_soc_acpi_intel_rt712_vb_no_function_topology, -+ "SND_SOC_ACPI_INTEL_SDCA_QUIRKS"); -+ - MODULE_DESCRIPTION("ASoC ACPI Intel SDCA quirks"); - MODULE_LICENSE("GPL"); - MODULE_IMPORT_NS("SND_SOC_SDCA"); -diff --git a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h ---- a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h -+++ b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h -@@ -11,5 +11,6 @@ - - bool snd_soc_acpi_intel_sdca_is_device_rt712_vb(void *arg); - bool snd_soc_acpi_intel_no_function_topology(void *arg); -+bool snd_soc_acpi_intel_rt712_vb_no_function_topology(void *arg); - - #endif -diff --git a/sound/soc/intel/keembay/kmb_platform.c b/sound/soc/intel/keembay/kmb_platform.c ---- a/sound/soc/intel/keembay/kmb_platform.c -+++ b/sound/soc/intel/keembay/kmb_platform.c -@@ -873,6 +873,8 @@ static int kmb_plat_dai_probe(struct platform_device *pdev) - - if (kmb_i2s->use_pio) { - irq = platform_get_irq_optional(pdev, 0); -+ if (irq == -EPROBE_DEFER) -+ return irq; - if (irq > 0) { - ret = devm_request_irq(dev, irq, kmb_i2s_irq_handler, 0, - pdev->name, kmb_i2s); -diff --git a/sound/soc/loongson/loongson_card.c b/sound/soc/loongson/loongson_card.c ---- a/sound/soc/loongson/loongson_card.c -+++ b/sound/soc/loongson/loongson_card.c -@@ -2,24 +2,131 @@ - // - // Loongson ASoC Audio Machine driver - // --// Copyright (C) 2023 Loongson Technology Corporation Limited -+// Copyright (C) 2023-2026 Loongson Technology Corporation Limited - // Author: Yingkun Meng -+// Binbin Zhou - // - -+#include -+#include - #include -+#include -+#include -+#include - #include - #include --#include --#include --#include - - static char codec_name[SND_ACPI_I2C_ID_LEN]; - - struct loongson_card_data { - struct snd_soc_card snd_card; - unsigned int mclk_fs; -+ struct gpio_desc *gpiod_hp_det; -+ struct gpio_desc *gpiod_hp_ctl; -+ struct gpio_desc *gpiod_spkr_en; -+ const struct loongson_card_config *cfg; - }; - -+struct loongson_card_config { -+ unsigned int fmt; -+ bool add_hp_jack; -+ bool add_dapm_widgets; -+ bool add_dapm_routes; -+}; -+ -+static const struct loongson_card_config ls2k1000_card_config = { -+ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBC_CFC, -+ .add_hp_jack = false, -+ .add_dapm_widgets = false, -+ .add_dapm_routes = false, -+}; -+ -+static const struct loongson_card_config ls2k0300_forever_pi_card_config = { -+ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC, -+ .add_hp_jack = false, -+ .add_dapm_widgets = false, -+ .add_dapm_routes = false, -+}; -+ -+static const struct loongson_card_config ls2k0300_dl2k0300b_card_config = { -+ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC, -+ .add_hp_jack = true, -+ .add_dapm_widgets = true, -+ .add_dapm_routes = true, -+}; -+ -+static int tegra_machine_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *k, int event) -+{ -+ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm); -+ struct loongson_card_data *priv = snd_soc_card_get_drvdata(card); -+ -+ if (!snd_soc_dapm_widget_name_cmp(w, "Speaker")) -+ gpiod_set_value_cansleep(priv->gpiod_spkr_en, -+ SND_SOC_DAPM_EVENT_ON(event)); -+ -+ if (!snd_soc_dapm_widget_name_cmp(w, "Headphone")) -+ gpiod_set_value_cansleep(priv->gpiod_hp_ctl, -+ SND_SOC_DAPM_EVENT_ON(event)); -+ -+ return 0; -+} -+ -+static const struct snd_soc_dapm_widget loongson_aosc_dapm_widgets[] = { -+ SND_SOC_DAPM_HP("Headphone", tegra_machine_event), -+ SND_SOC_DAPM_SPK("Speaker", tegra_machine_event), -+}; -+ -+/* Headphones Jack */ -+ -+static struct snd_soc_jack loongson_asoc_hp_jack; -+ -+static struct snd_soc_jack_pin loongson_asoc_hp_jack_pins[] = { -+ { -+ .pin = "Headphone", -+ .mask = SND_JACK_HEADPHONE -+ }, -+ { -+ .pin = "Speaker", -+ .mask = SND_JACK_HEADPHONE, -+ .invert = 1 -+ }, -+}; -+ -+static struct snd_soc_jack_gpio loongson_asoc_hp_jack_gpio = { -+ .name = "Headphones detection", -+ .report = SND_JACK_HEADPHONE, -+ .debounce_time = 150, -+}; -+ -+static int loongson_asoc_machine_init(struct snd_soc_pcm_runtime *rtd) -+{ -+ struct snd_soc_card *card = rtd->card; -+ struct loongson_card_data *ls_priv = snd_soc_card_get_drvdata(card); -+ int ret = 0; -+ -+ if (!ls_priv->cfg->add_hp_jack || !ls_priv->gpiod_hp_det) -+ return 0; -+ -+ ret = snd_soc_card_jack_new_pins(card, "Headphones Jack", -+ SND_JACK_HEADPHONE, -+ &loongson_asoc_hp_jack, -+ loongson_asoc_hp_jack_pins, -+ ARRAY_SIZE(loongson_asoc_hp_jack_pins)); -+ if (ret) { -+ dev_err(rtd->dev, "Headphones Jack creation failed: %d\n", ret); -+ return ret; -+ } -+ -+ loongson_asoc_hp_jack_gpio.desc = ls_priv->gpiod_hp_det; -+ -+ ret = snd_soc_jack_add_gpios(&loongson_asoc_hp_jack, 1, &loongson_asoc_hp_jack_gpio); -+ if (ret) -+ dev_err(rtd->dev, "Headphone GPIO not added: %d\n", ret); -+ -+ return ret; -+} -+ - static int loongson_card_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) - { -@@ -45,7 +152,7 @@ static int loongson_card_hw_params(struct snd_pcm_substream *substream, - return ret; - } - -- return 0; -+ return snd_soc_runtime_set_dai_fmt(rtd, ls_card->cfg->fmt); - } - - static const struct snd_soc_ops loongson_ops = { -@@ -61,8 +168,7 @@ static struct snd_soc_dai_link loongson_dai_links[] = { - { - .name = "Loongson Audio Port", - .stream_name = "Loongson Audio", -- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_NF -- | SND_SOC_DAIFMT_CBC_CFC, -+ .init = loongson_asoc_machine_init, - SND_SOC_DAILINK_REG(analog), - .ops = &loongson_ops, - }, -@@ -123,16 +229,35 @@ static int loongson_card_parse_acpi(struct loongson_card_data *data) - - static int loongson_card_parse_of(struct loongson_card_data *data) - { -- struct device_node *cpu, *codec; - struct snd_soc_card *card = &data->snd_card; -+ struct device_node *cpu, *codec; - struct device *dev = card->dev; - int ret, i; - -+ data->gpiod_hp_det = devm_gpiod_get_optional(dev, "hp-det", GPIOD_IN); -+ if (IS_ERR(data->gpiod_hp_det)) -+ return PTR_ERR(data->gpiod_hp_det); -+ -+ data->gpiod_hp_ctl = devm_gpiod_get_optional(dev, "hp-ctl", GPIOD_OUT_LOW); -+ if (IS_ERR(data->gpiod_hp_ctl)) -+ return PTR_ERR(data->gpiod_hp_ctl); -+ -+ data->gpiod_spkr_en = devm_gpiod_get_optional(dev, "spkr-en", GPIOD_OUT_LOW); -+ if (IS_ERR(data->gpiod_spkr_en)) -+ return PTR_ERR(data->gpiod_spkr_en); -+ -+ if (data->cfg->add_dapm_routes) { -+ ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); -+ if (ret) -+ return ret; -+ } -+ - cpu = of_get_child_by_name(dev->of_node, "cpu"); - if (!cpu) { - dev_err(dev, "platform property missing or invalid\n"); - return -EINVAL; - } -+ - codec = of_get_child_by_name(dev->of_node, "codec"); - if (!codec) { - dev_err(dev, "audio-codec property missing or invalid\n"); -@@ -177,12 +302,22 @@ static int loongson_asoc_card_probe(struct platform_device *pdev) - if (!ls_priv) - return -ENOMEM; - -+ ls_priv->cfg = (const struct loongson_card_config *)device_get_match_data(dev); -+ if (!ls_priv->cfg) -+ return -EINVAL; -+ - card = &ls_priv->snd_card; - - card->dev = dev; - card->owner = THIS_MODULE; - card->dai_link = loongson_dai_links; - card->num_links = ARRAY_SIZE(loongson_dai_links); -+ -+ if (ls_priv->cfg->add_dapm_widgets) { -+ card->dapm_widgets = loongson_aosc_dapm_widgets; -+ card->num_dapm_widgets = ARRAY_SIZE(loongson_aosc_dapm_widgets); -+ } -+ - snd_soc_card_set_drvdata(card, ls_priv); - - ret = device_property_read_string(dev, "model", &card->name); -@@ -202,7 +337,19 @@ static int loongson_asoc_card_probe(struct platform_device *pdev) - } - - static const struct of_device_id loongson_asoc_dt_ids[] = { -- { .compatible = "loongson,ls-audio-card" }, -+ /* Loongson-2K1000/Loongson-2K2000/LS7A */ -+ { -+ .compatible = "loongson,ls-audio-card", -+ .data = &ls2k1000_card_config -+ }, -+ { -+ .compatible = "loongson,ls2k0300-forever-pi-audio-card", -+ .data = &ls2k0300_forever_pi_card_config -+ }, -+ { -+ .compatible = "loongson,ls2k0300-dl2k0300b-audio-card", -+ .data = &ls2k0300_dl2k0300b_card_config -+ }, - { /* sentinel */ }, - }; - MODULE_DEVICE_TABLE(of, loongson_asoc_dt_ids); -diff --git a/sound/soc/loongson/loongson_i2s_plat.c b/sound/soc/loongson/loongson_i2s_plat.c ---- a/sound/soc/loongson/loongson_i2s_plat.c -+++ b/sound/soc/loongson/loongson_i2s_plat.c -@@ -2,7 +2,7 @@ - // - // Loongson I2S controller master mode dirver(platform device) - // --// Copyright (C) 2023-2024 Loongson Technology Corporation Limited -+// Copyright (C) 2023-2026 Loongson Technology Corporation Limited - // - // Author: Yingkun Meng - // Binbin Zhou -@@ -21,6 +21,7 @@ - #include "loongson_i2s.h" - #include "loongson_dma.h" - -+/* Loongson-2K1000 APBDMA routing */ - #define LOONGSON_I2S_RX_DMA_OFFSET 21 - #define LOONGSON_I2S_TX_DMA_OFFSET 18 - -@@ -30,6 +31,11 @@ - #define LOONGSON_DMA3_CONF 0x3 - #define LOONGSON_DMA4_CONF 0x4 - -+struct loongson_i2s_plat_config { -+ int rev_id; -+ int (*i2s_dma_config)(struct platform_device *pdev); -+}; -+ - static int loongson_i2s_apbdma_config(struct platform_device *pdev) - { - int val; -@@ -47,8 +53,18 @@ static int loongson_i2s_apbdma_config(struct platform_device *pdev) - return 0; - } - -+static const struct loongson_i2s_plat_config ls2k0300_i2s_plat_config = { -+ .rev_id = 1, -+}; -+ -+static const struct loongson_i2s_plat_config ls2k1000_i2s_plat_config = { -+ .rev_id = 0, -+ .i2s_dma_config = loongson_i2s_apbdma_config, -+}; -+ - static int loongson_i2s_plat_probe(struct platform_device *pdev) - { -+ const struct loongson_i2s_plat_config *plat_config; - struct device *dev = &pdev->dev; - struct loongson_i2s *i2s; - struct resource *res; -@@ -59,12 +75,17 @@ static int loongson_i2s_plat_probe(struct platform_device *pdev) - if (!i2s) - return -ENOMEM; - -- ret = loongson_i2s_apbdma_config(pdev); -- if (ret) -- return ret; -+ plat_config = device_get_match_data(dev); -+ if (!plat_config) -+ return -EINVAL; - -- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- i2s->reg_base = devm_ioremap_resource(&pdev->dev, res); -+ if (plat_config->i2s_dma_config) { -+ ret = plat_config->i2s_dma_config(pdev); -+ if (ret) -+ return ret; -+ } -+ -+ i2s->reg_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(i2s->reg_base)) - return dev_err_probe(dev, PTR_ERR(i2s->reg_base), - "devm_ioremap_resource failed\n"); -@@ -87,11 +108,17 @@ static int loongson_i2s_plat_probe(struct platform_device *pdev) - if (IS_ERR(i2s_clk)) - return dev_err_probe(dev, PTR_ERR(i2s_clk), "clock property invalid\n"); - i2s->clk_rate = clk_get_rate(i2s_clk); -+ i2s->rev_id = plat_config->rev_id; - - dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); - dev_set_name(dev, LS_I2S_DRVNAME); - dev_set_drvdata(dev, i2s); - -+ if (i2s->rev_id == 1) { -+ regmap_update_bits(i2s->regmap, LS_I2S_CTRL, I2S_CTRL_RESET, I2S_CTRL_RESET); -+ fsleep(200); -+ } -+ - ret = devm_snd_soc_register_component(dev, &loongson_i2s_edma_component, - &loongson_i2s_dai, 1); - if (ret) -@@ -102,7 +129,8 @@ static int loongson_i2s_plat_probe(struct platform_device *pdev) - } - - static const struct of_device_id loongson_i2s_ids[] = { -- { .compatible = "loongson,ls2k1000-i2s" }, -+ { .compatible = "loongson,ls2k0300-i2s", .data = &ls2k0300_i2s_plat_config }, -+ { .compatible = "loongson,ls2k1000-i2s", .data = &ls2k1000_i2s_plat_config }, - { /* sentinel */ }, - }; - MODULE_DEVICE_TABLE(of, loongson_i2s_ids); -diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig ---- a/sound/soc/mediatek/Kconfig -+++ b/sound/soc/mediatek/Kconfig -@@ -7,7 +7,7 @@ config SND_SOC_MEDIATEK - - config SND_SOC_MT2701 - tristate "ASoC support for Mediatek MT2701 chip" -- depends on ARCH_MEDIATEK -+ depends on ARCH_MEDIATEK || COMPILE_TEST - select SND_SOC_MEDIATEK - help - This adds ASoC driver for Mediatek MT2701 boards -diff --git a/sound/soc/mediatek/common/mtk-dsp-sof-common.c b/sound/soc/mediatek/common/mtk-dsp-sof-common.c ---- a/sound/soc/mediatek/common/mtk-dsp-sof-common.c -+++ b/sound/soc/mediatek/common/mtk-dsp-sof-common.c -@@ -232,6 +232,7 @@ int mtk_sof_dailink_parse_of(struct device *dev, struct snd_soc_card *card, - const char *propname) - { - struct device_node *np = dev->of_node; -+ struct snd_soc_dai_link *dai_link; - struct snd_soc_dai_link *parsed_dai_link; - const char *dai_name = NULL; - int i, j, ret, num_links, parsed_num_links = 0; -@@ -254,9 +255,9 @@ int mtk_sof_dailink_parse_of(struct device *dev, struct snd_soc_card *card, - return ret; - } - dev_dbg(dev, "ASoC: Property get dai_name:%s\n", dai_name); -- for (j = 0; j < card->num_links; j++) { -- if (!strcmp(dai_name, card->dai_link[j].name)) { -- memcpy(&parsed_dai_link[parsed_num_links++], &card->dai_link[j], -+ for_each_card_prelinks(card, j, dai_link) { -+ if (!strcmp(dai_name, dai_link->name)) { -+ memcpy(&parsed_dai_link[parsed_num_links++], dai_link, - sizeof(struct snd_soc_dai_link)); - break; - } -diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c ---- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c -+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c -@@ -315,7 +315,7 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev) - &mt8173_rt5650_priv.pll_from); - if (ret) { - dev_err(&pdev->dev, -- "%s snd_soc_register_card fail %d\n", -+ "%s device_property_read_u32() fail %d\n", - __func__, ret); - } - } diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c --- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c @@ -39681,18 +11418,6 @@ diff --git a/sound/soc/mediatek/mt8188/mt8188-mt6359.c b/sound/soc/mediatek/mt81 params_set_format(params, SNDRV_PCM_FORMAT_S32_LE); -diff --git a/sound/soc/mediatek/mt8189/mt8189-dai-adda.c b/sound/soc/mediatek/mt8189/mt8189-dai-adda.c ---- a/sound/soc/mediatek/mt8189/mt8189-dai-adda.c -+++ b/sound/soc/mediatek/mt8189/mt8189-dai-adda.c -@@ -385,8 +385,6 @@ static int mtk_adda_pad_top_event(struct snd_soc_dapm_widget *w, - if (event == SND_SOC_DAPM_PRE_PMU) { - if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2_CLK_P2) - regmap_write(afe->regmap, AFE_AUD_PAD_TOP_CFG0, 0xB8); -- else if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2) -- regmap_write(afe->regmap, AFE_AUD_PAD_TOP_CFG0, 0xB0); - else - regmap_write(afe->regmap, AFE_AUD_PAD_TOP_CFG0, 0xB0); - } diff --git a/sound/soc/mediatek/mt8189/mt8189-nau8825.c b/sound/soc/mediatek/mt8189/mt8189-nau8825.c --- a/sound/soc/mediatek/mt8189/mt8189-nau8825.c +++ b/sound/soc/mediatek/mt8189/mt8189-nau8825.c @@ -39717,52 +11442,6 @@ diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); -@@ -1019,7 +1019,7 @@ static struct snd_soc_card mt8192_mt6359_rt1015p_rt5682x_card = { - .num_dapm_routes = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682x_routes), - }; - --static int mt8192_mt6359_card_set_be_link(struct snd_soc_card *card, -+static int mt8192_mt6359_card_set_be_link(struct device *dev, - struct snd_soc_dai_link *link, - struct device_node *node, - char *link_name) -@@ -1027,9 +1027,9 @@ static int mt8192_mt6359_card_set_be_link(struct snd_soc_card *card, - int ret; - - if (node && strcmp(link->name, link_name) == 0) { -- ret = snd_soc_of_get_dai_link_codecs(card->dev, node, link); -+ ret = snd_soc_of_get_dai_link_codecs(dev, node, link); - if (ret < 0) { -- dev_err_probe(card->dev, ret, "get dai link codecs fail\n"); -+ dev_err_probe(dev, ret, "get dai link codecs fail\n"); - return ret; - } - } -@@ -1065,21 +1065,21 @@ static int mt8192_mt6359_legacy_probe(struct mtk_soc_card_data *soc_card_data) - } - - for_each_card_prelinks(card, i, dai_link) { -- ret = mt8192_mt6359_card_set_be_link(card, dai_link, speaker_codec, "I2S3"); -+ ret = mt8192_mt6359_card_set_be_link(dev, dai_link, speaker_codec, "I2S3"); - if (ret) { - dev_err_probe(dev, ret, "%s set speaker_codec fail\n", - dai_link->name); - break; - } - -- ret = mt8192_mt6359_card_set_be_link(card, dai_link, headset_codec, "I2S8"); -+ ret = mt8192_mt6359_card_set_be_link(dev, dai_link, headset_codec, "I2S8"); - if (ret) { - dev_err_probe(dev, ret, "%s set headset_codec fail\n", - dai_link->name); - break; - } - -- ret = mt8192_mt6359_card_set_be_link(card, dai_link, headset_codec, "I2S9"); -+ ret = mt8192_mt6359_card_set_be_link(dev, dai_link, headset_codec, "I2S9"); - if (ret) { - dev_err_probe(dev, ret, "%s set headset_codec fail\n", - dai_link->name); diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359.c b/sound/soc/mediatek/mt8195/mt8195-mt6359.c --- a/sound/soc/mediatek/mt8195/mt8195-mt6359.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359.c @@ -39787,16 +11466,7 @@ diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359.c b/sound/soc/mediatek/mt81 diff --git a/sound/soc/mediatek/mt8196/mt8196-nau8825.c b/sound/soc/mediatek/mt8196/mt8196-nau8825.c --- a/sound/soc/mediatek/mt8196/mt8196-nau8825.c +++ b/sound/soc/mediatek/mt8196/mt8196-nau8825.c -@@ -104,8 +104,6 @@ static const struct snd_kcontrol_new mt8196_nau8825_controls[] = { - - #define EXT_SPK_AMP_W_NAME "Ext_Speaker_Amp" - --static struct snd_soc_card mt8196_nau8825_soc_card; -- - static const struct snd_soc_dapm_widget mt8196_nau8825_card_widgets[] = { - /* SOF Uplink */ - SND_SOC_DAPM_MIXER("SOF_DMA_UL0", SND_SOC_NOPM, 0, 0, NULL, 0), -@@ -180,7 +178,7 @@ static int mt8196_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, +@@ -180,7 +180,7 @@ static int mt8196_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, /* fix BE i2s format to 32bit, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), @@ -39805,1491 +11475,6 @@ diff --git a/sound/soc/mediatek/mt8196/mt8196-nau8825.c b/sound/soc/mediatek/mt8 params_set_format(params, SNDRV_PCM_FORMAT_S32_LE); return 0; -diff --git a/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c b/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c ---- a/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c -+++ b/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c -@@ -2120,19 +2120,15 @@ static int mt8365_afe_pcm_dev_probe(struct platform_device *pdev) - spin_lock_init(&afe_priv->afe_ctrl_lock); - mutex_init(&afe_priv->afe_clk_mutex); - -- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- afe->base_addr = devm_ioremap_resource(&pdev->dev, res); -+ afe->base_addr = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(afe->base_addr)) - return PTR_ERR(afe->base_addr); - -- res = platform_get_resource(pdev, IORESOURCE_MEM, 1); -- if (res) { -- afe_priv->afe_sram_vir_addr = -- devm_ioremap_resource(&pdev->dev, res); -- if (!IS_ERR(afe_priv->afe_sram_vir_addr)) { -- afe_priv->afe_sram_phy_addr = res->start; -- afe_priv->afe_sram_size = resource_size(res); -- } -+ afe_priv->afe_sram_vir_addr = -+ devm_platform_get_and_ioremap_resource(pdev, 1, &res); -+ if (!IS_ERR(afe_priv->afe_sram_vir_addr)) { -+ afe_priv->afe_sram_phy_addr = res->start; -+ afe_priv->afe_sram_size = resource_size(res); - } - - /* initial audio related clock */ -diff --git a/sound/soc/mediatek/mt8365/mt8365-mt6357.c b/sound/soc/mediatek/mt8365/mt8365-mt6357.c ---- a/sound/soc/mediatek/mt8365/mt8365-mt6357.c -+++ b/sound/soc/mediatek/mt8365/mt8365-mt6357.c -@@ -70,7 +70,8 @@ static const struct snd_soc_dapm_route mt8365_mt6357_routes[] = { - static int mt8365_mt6357_int_adda_startup(struct snd_pcm_substream *substream) - { - struct snd_soc_pcm_runtime *rtd = substream->private_data; -- struct mt8365_mt6357_priv *priv = snd_soc_card_get_drvdata(rtd->card); -+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card); -+ struct mt8365_mt6357_priv *priv = soc_card_data->mach_priv; - int ret = 0; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { -@@ -101,7 +102,8 @@ static int mt8365_mt6357_int_adda_startup(struct snd_pcm_substream *substream) - static void mt8365_mt6357_int_adda_shutdown(struct snd_pcm_substream *substream) - { - struct snd_soc_pcm_runtime *rtd = substream->private_data; -- struct mt8365_mt6357_priv *priv = snd_soc_card_get_drvdata(rtd->card); -+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card); -+ struct mt8365_mt6357_priv *priv = soc_card_data->mach_priv; - int ret = 0; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { -@@ -245,7 +247,8 @@ static struct snd_soc_dai_link mt8365_mt6357_dais[] = { - - static int mt8365_mt6357_gpio_probe(struct snd_soc_card *card) - { -- struct mt8365_mt6357_priv *priv = snd_soc_card_get_drvdata(card); -+ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card); -+ struct mt8365_mt6357_priv *priv = soc_card_data->mach_priv; - struct device *dev = card->dev; - int ret, i; - -@@ -294,7 +297,6 @@ static int mt8365_mt6357_dev_probe(struct mtk_soc_card_data *soc_card_data, bool - struct mt8365_mt6357_priv *mach_priv; - int ret; - -- card->dev = dev; - ret = parse_dai_link_info(card); - if (ret) - goto err; -diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile ---- a/sound/soc/meson/Makefile -+++ b/sound/soc/meson/Makefile -@@ -4,6 +4,8 @@ snd-soc-meson-aiu-y := aiu.o - snd-soc-meson-aiu-y += aiu-acodec-ctrl.o - snd-soc-meson-aiu-y += aiu-codec-ctrl.o - snd-soc-meson-aiu-y += aiu-encoder-i2s.o -+snd-soc-meson-aiu-y += gx-formatter.o -+snd-soc-meson-aiu-y += aiu-formatter-i2s.o - snd-soc-meson-aiu-y += aiu-encoder-spdif.o - snd-soc-meson-aiu-y += aiu-fifo.o - snd-soc-meson-aiu-y += aiu-fifo-i2s.o -diff --git a/sound/soc/meson/aiu-encoder-i2s.c b/sound/soc/meson/aiu-encoder-i2s.c ---- a/sound/soc/meson/aiu-encoder-i2s.c -+++ b/sound/soc/meson/aiu-encoder-i2s.c -@@ -10,14 +10,11 @@ - #include - - #include "aiu.h" -+#include "gx-formatter.h" -+#include "gx-interface.h" - --#define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0) --#define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5) --#define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9) - #define AIU_I2S_SOURCE_DESC_MODE_SPLIT BIT(11) --#define AIU_RST_SOFT_I2S_FAST BIT(0) - --#define AIU_I2S_DAC_CFG_MSB_FIRST BIT(2) - #define AIU_CLK_CTRL_I2S_DIV_EN BIT(0) - #define AIU_CLK_CTRL_I2S_DIV GENMASK(3, 2) - #define AIU_CLK_CTRL_AOCLK_INVERT BIT(6) -@@ -35,49 +32,6 @@ static void aiu_encoder_i2s_divider_enable(struct snd_soc_component *component, - enable ? AIU_CLK_CTRL_I2S_DIV_EN : 0); - } - --static int aiu_encoder_i2s_setup_desc(struct snd_soc_component *component, -- struct snd_pcm_hw_params *params) --{ -- /* Always operate in split (classic interleaved) mode */ -- unsigned int desc = AIU_I2S_SOURCE_DESC_MODE_SPLIT; -- -- /* Reset required to update the pipeline */ -- snd_soc_component_write(component, AIU_RST_SOFT, AIU_RST_SOFT_I2S_FAST); -- snd_soc_component_read(component, AIU_I2S_SYNC); -- -- switch (params_physical_width(params)) { -- case 16: /* Nothing to do */ -- break; -- -- case 32: -- desc |= (AIU_I2S_SOURCE_DESC_MODE_24BIT | -- AIU_I2S_SOURCE_DESC_MODE_32BIT); -- break; -- -- default: -- return -EINVAL; -- } -- -- switch (params_channels(params)) { -- case 2: /* Nothing to do */ -- break; -- case 8: -- desc |= AIU_I2S_SOURCE_DESC_MODE_8CH; -- break; -- default: -- return -EINVAL; -- } -- -- snd_soc_component_update_bits(component, AIU_I2S_SOURCE_DESC, -- AIU_I2S_SOURCE_DESC_MODE_8CH | -- AIU_I2S_SOURCE_DESC_MODE_24BIT | -- AIU_I2S_SOURCE_DESC_MODE_32BIT | -- AIU_I2S_SOURCE_DESC_MODE_SPLIT, -- desc); -- -- return 0; --} -- - static int aiu_encoder_i2s_set_legacy_div(struct snd_soc_component *component, - struct snd_pcm_hw_params *params, - unsigned int bs) -@@ -108,6 +62,32 @@ static int aiu_encoder_i2s_set_legacy_div(struct snd_soc_component *component, - return 0; - } - -+/* -+ * Return true if the given combination of channels and sample width requires -+ * the bs quirk. Return false otherwise. -+ */ -+static bool aiu_encoder_is_bs_quirk(unsigned int channels, int width) -+{ -+ return (channels == 8) && (width == 16); -+} -+ -+static int aiu_encoder_check_bs_quirk(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params, -+ struct snd_soc_dai *dai) -+{ -+ struct gx_stream *other_stream = snd_soc_dai_dma_data_get(dai, !substream->stream); -+ -+ /* Nothing to do if the other stream doesn't exist or it's not configured yet. */ -+ if (!other_stream || !other_stream->channels) -+ return 0; -+ -+ if (aiu_encoder_is_bs_quirk(other_stream->channels, other_stream->width) != -+ aiu_encoder_is_bs_quirk(params_channels(params), params_width(params))) -+ return -EINVAL; -+ -+ return 0; -+} -+ - static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component, - struct snd_pcm_hw_params *params, - unsigned int bs) -@@ -119,7 +99,7 @@ static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component, - * increased by 50% to get the correct output rate. - * No idea why ! - */ -- if (params_width(params) == 16 && params_channels(params) == 8) { -+ if (aiu_encoder_is_bs_quirk(params_channels(params), params_width(params))) { - if (bs % 2) { - dev_err(component->dev, - "Cannot increase i2s divider by 50%%\n"); -@@ -141,25 +121,23 @@ static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component, - return 0; - } - --static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component, -- struct snd_pcm_hw_params *params) -+static int aiu_encoder_i2s_set_clocks(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params, -+ struct snd_soc_dai *dai) - { -+ struct snd_soc_component *component = dai->component; - struct aiu *aiu = snd_soc_component_get_drvdata(component); -+ struct gx_iface *iface = &aiu->i2s.iface; - unsigned int srate = params_rate(params); - unsigned int fs, bs; - int ret; - - /* Get the oversampling factor */ -- fs = DIV_ROUND_CLOSEST(clk_get_rate(aiu->i2s.clks[MCLK].clk), srate); -+ fs = DIV_ROUND_CLOSEST(iface->mclk_rate, srate); - -- if (fs % 64) -+ if ((fs % 64) || (fs == 0)) - return -EINVAL; - -- /* Send data MSB first */ -- snd_soc_component_update_bits(component, AIU_I2S_DAC_CFG, -- AIU_I2S_DAC_CFG_MSB_FIRST, -- AIU_I2S_DAC_CFG_MSB_FIRST); -- - /* Set bclk to lrlck ratio */ - snd_soc_component_update_bits(component, AIU_CODEC_DAC_LRCLK_CTRL, - AIU_CODEC_DAC_LRCLK_CTRL_DIV, -@@ -168,10 +146,22 @@ static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component, - - bs = fs / 64; - -- if (aiu->platform->has_clk_ctrl_more_i2s_div) -+ if (aiu->platform->has_clk_ctrl_more_i2s_div) { -+ /* -+ * The hw rules added in startup() make this unreachable in the -+ * sequential case, but both streams may be refined concurrently -+ * before either commits its config, since only ops->hw_params -+ * runs under the card's pcm_mutex. Re-check against the committed -+ * state of the other stream, which is stable under that mutex. -+ */ -+ if (aiu_encoder_check_bs_quirk(substream, params, dai)) { -+ dev_err(dai->dev, "bclk requirements incompatible with other stream\n"); -+ return -EINVAL; -+ } - ret = aiu_encoder_i2s_set_more_div(component, params, bs); -- else -+ } else { - ret = aiu_encoder_i2s_set_legacy_div(component, params, bs); -+ } - - if (ret) - return ret; -@@ -188,23 +178,37 @@ static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) - { -+ struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); -+ int ret; -+ -+ ret = aiu_encoder_i2s_set_clocks(substream, params, dai); -+ if (ret) { -+ dev_err(dai->dev, "setting i2s clocks failed: %d\n", ret); -+ return ret; -+ } -+ -+ ts->physical_width = params_physical_width(params); -+ ts->width = params_width(params); -+ ts->channels = params_channels(params); -+ -+ return 0; -+} -+ -+static int aiu_encoder_i2s_prepare(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); - struct snd_soc_component *component = dai->component; - int ret; - -- /* Disable the clock while changing the settings */ -- aiu_encoder_i2s_divider_enable(component, false); -+ if (ts->clk_enabled) -+ return 0; - -- ret = aiu_encoder_i2s_setup_desc(component, params); -- if (ret) { -- dev_err(dai->dev, "setting i2s desc failed\n"); -+ ret = clk_prepare_enable(ts->iface->mclk); -+ if (ret) - return ret; -- } - -- ret = aiu_encoder_i2s_set_clocks(component, params); -- if (ret) { -- dev_err(dai->dev, "setting i2s clocks failed\n"); -- return ret; -- } -+ ts->clk_enabled = true; - - aiu_encoder_i2s_divider_enable(component, true); - -@@ -214,9 +218,24 @@ static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream, - static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) - { -+ struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); - struct snd_soc_component *component = dai->component; - -- aiu_encoder_i2s_divider_enable(component, false); -+ /* -+ * If this is the last substream being closed then disable the i2s -+ * clock divider. -+ */ -+ if (snd_soc_dai_active(dai) <= 1) -+ aiu_encoder_i2s_divider_enable(component, 0); -+ -+ if (ts->clk_enabled) { -+ clk_disable_unprepare(ts->iface->mclk); -+ ts->clk_enabled = false; -+ } -+ -+ ts->channels = 0; -+ ts->width = 0; -+ ts->physical_width = 0; - - return 0; - } -@@ -224,6 +243,8 @@ static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream, - static int aiu_encoder_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) - { - struct snd_soc_component *component = dai->component; -+ struct aiu *aiu = snd_soc_component_get_drvdata(component); -+ struct gx_iface *iface = &aiu->i2s.iface; - unsigned int inv = fmt & SND_SOC_DAIFMT_INV_MASK; - unsigned int val = 0; - unsigned int skew; -@@ -255,9 +276,12 @@ static int aiu_encoder_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) - skew = 0; - break; - default: -+ dev_err(dai->dev, "unsupported dai format\n"); - return -EINVAL; - } - -+ iface->fmt = fmt; -+ - val |= FIELD_PREP(AIU_CLK_CTRL_LRCLK_SKEW, skew); - snd_soc_component_update_bits(component, AIU_CLK_CTRL, - AIU_CLK_CTRL_LRCLK_INVERT | -@@ -272,6 +296,7 @@ static int aiu_encoder_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, - unsigned int freq, int dir) - { - struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); -+ struct gx_iface *iface = &aiu->i2s.iface; - int ret; - - if (WARN_ON(clk_id != 0)) -@@ -280,11 +305,15 @@ static int aiu_encoder_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, - if (dir == SND_SOC_CLOCK_IN) - return 0; - -- ret = clk_set_rate(aiu->i2s.clks[MCLK].clk, freq); -- if (ret) -- dev_err(dai->dev, "Failed to set sysclk to %uHz", freq); -+ ret = clk_set_rate(iface->mclk, freq); -+ if (ret) { -+ dev_err(dai->dev, "Failed to set sysclk to %uHz: %d", freq, ret); -+ return ret; -+ } - -- return ret; -+ iface->mclk_rate = freq; -+ -+ return 0; - } - - static const unsigned int hw_channels[] = {2, 8}; -@@ -294,10 +323,45 @@ static const struct snd_pcm_hw_constraint_list hw_channel_constraints = { - .mask = 0, - }; - -+static int aiu_encoder_i2s_pcm_hw_rule(struct snd_pcm_hw_params *params, -+ struct snd_pcm_hw_rule *rule) -+{ -+ struct gx_stream *other = rule->private; -+ struct snd_interval *ch = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); -+ /* -+ * The quirk is technically based on the significant bits whereas here -+ * we're using the physical width for simplicity. This works because -+ * S16_LE is the only format supported by this encoder that has: -+ * significant bits = physical width = 16-bits -+ */ -+ struct snd_interval *phys_width = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); -+ struct snd_interval new_i; -+ -+ if (other->channels == 0) -+ return 0; -+ -+ snd_interval_any(&new_i); -+ -+ if (rule->var == SNDRV_PCM_HW_PARAM_CHANNELS) { -+ if (aiu_encoder_is_bs_quirk(other->channels, other->width)) -+ new_i.min = new_i.max = 8; -+ else if (snd_interval_single(phys_width) && phys_width->min == 16) -+ new_i.max = 2; /* Force 2ch */ -+ } else { /* SNDRV_PCM_HW_PARAM_SAMPLE_BITS */ -+ if (aiu_encoder_is_bs_quirk(other->channels, other->width)) -+ new_i.min = new_i.max = 16; -+ else if (snd_interval_single(ch) && ch->min == 8) -+ new_i.min = 17; /* Request physical width > 16 bits */ -+ } -+ -+ return snd_interval_refine(hw_param_interval(params, rule->var), &new_i); -+} -+ - static int aiu_encoder_i2s_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) - { - struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); -+ struct gx_stream *other_stream = snd_soc_dai_dma_data_get(dai, !substream->stream); - int ret; - - /* Make sure the encoder gets either 2 or 8 channels */ -@@ -305,15 +369,79 @@ static int aiu_encoder_i2s_startup(struct snd_pcm_substream *substream, - SNDRV_PCM_HW_PARAM_CHANNELS, - &hw_channel_constraints); - if (ret) { -- dev_err(dai->dev, "adding channels constraints failed\n"); -+ dev_err(dai->dev, "adding channels constraints failed: %d\n", ret); - return ret; - } - -- ret = clk_bulk_prepare_enable(aiu->i2s.clk_num, aiu->i2s.clks); -- if (ret) -- dev_err(dai->dev, "failed to enable i2s clocks\n"); -+ /* -+ * If DAI supports both playback and capture streams ensure the bs-quirk is -+ * handled correctly. -+ * This is only valid for GX platforms (has_clk_ctrl_more_i2s_div=true). -+ */ -+ if (aiu->platform->has_clk_ctrl_more_i2s_div && other_stream) { -+ ret = snd_pcm_hw_rule_add(substream->runtime, 0, -+ SNDRV_PCM_HW_PARAM_CHANNELS, -+ aiu_encoder_i2s_pcm_hw_rule, -+ other_stream, -+ SNDRV_PCM_HW_PARAM_CHANNELS, -+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); -+ if (ret) -+ return ret; - -- return ret; -+ ret = snd_pcm_hw_rule_add(substream->runtime, 0, -+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -+ aiu_encoder_i2s_pcm_hw_rule, -+ other_stream, -+ SNDRV_PCM_HW_PARAM_CHANNELS, -+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); -+ if (ret) -+ return ret; -+ } -+ -+ /* -+ * Enable only clocks which are required for the interface internal -+ * logic. MCLK is enabled/disabled from the formatter and the I2S -+ * divider is enabled/disabled in "hw_params"/"hw_free", respectively. -+ */ -+ ret = clk_prepare_enable(aiu->i2s.clks[PCLK].clk); -+ if (ret) { -+ dev_err(dai->dev, "failed to enable PCLK: %d\n", ret); -+ return ret; -+ } -+ ret = clk_prepare_enable(aiu->i2s.clks[MIXER].clk); -+ if (ret) { -+ dev_err(dai->dev, "failed to enable MIXER: %d\n", ret); -+ clk_disable_unprepare(aiu->i2s.clks[PCLK].clk); -+ return ret; -+ } -+ ret = clk_prepare_enable(aiu->i2s.clks[AOCLK].clk); -+ if (ret) { -+ dev_err(dai->dev, "failed to enable AOCLK: %d\n", ret); -+ clk_disable_unprepare(aiu->i2s.clks[MIXER].clk); -+ clk_disable_unprepare(aiu->i2s.clks[PCLK].clk); -+ return ret; -+ } -+ -+ /* -+ * We're always operating in split mode for the playback stream. -+ * -+ * This setting arguably belong to the 'aiu-formatter', but it's kept -+ * here for backward compatibility reason. At reset the I2S encoder -+ * operates in normal mode which would only support 8ch, but by default -+ * only 2ch are enabled. If a playback stream is started without -+ * changing to split mode, then the I2S encoder doesn't consume audio -+ * samples and the playback fails. -+ * Moving this to 'aiu-formatter' would cause the split mode to be set -+ * only when the formatter is enabled, which doesn't happen at boot as -+ * the default value for "HDMI CTRL SRC" is "DISABLED". -+ */ -+ ret = snd_soc_component_update_bits(dai->component, AIU_I2S_SOURCE_DESC, -+ AIU_I2S_SOURCE_DESC_MODE_SPLIT, -+ AIU_I2S_SOURCE_DESC_MODE_SPLIT); -+ if (ret < 0) -+ dev_err(dai->dev, "failed to update AIU_I2S_SOURCE_DESC: %d", ret); -+ -+ return 0; - } - - static void aiu_encoder_i2s_shutdown(struct snd_pcm_substream *substream, -@@ -321,14 +449,89 @@ static void aiu_encoder_i2s_shutdown(struct snd_pcm_substream *substream, - { - struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); - -- clk_bulk_disable_unprepare(aiu->i2s.clk_num, aiu->i2s.clks); -+ clk_disable_unprepare(aiu->i2s.clks[AOCLK].clk); -+ clk_disable_unprepare(aiu->i2s.clks[MIXER].clk); -+ clk_disable_unprepare(aiu->i2s.clks[PCLK].clk); -+} -+ -+static int aiu_encoder_i2s_trigger(struct snd_pcm_substream *substream, -+ int cmd, -+ struct snd_soc_dai *dai) -+{ -+ struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); -+ int ret; -+ -+ switch (cmd) { -+ case SNDRV_PCM_TRIGGER_START: -+ case SNDRV_PCM_TRIGGER_RESUME: -+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: -+ ret = gx_stream_start(ts); -+ break; -+ case SNDRV_PCM_TRIGGER_SUSPEND: -+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: -+ case SNDRV_PCM_TRIGGER_STOP: -+ gx_stream_stop(ts); -+ ret = 0; -+ break; -+ default: -+ ret = -EINVAL; -+ } -+ -+ return ret; -+} -+ -+static int aiu_encoder_i2s_remove_dai(struct snd_soc_dai *dai) -+{ -+ int stream; -+ -+ for_each_pcm_streams(stream) { -+ struct gx_stream *ts; -+ -+ ts = snd_soc_dai_dma_data_get(dai, stream); -+ if (ts) -+ gx_stream_free(ts); -+ -+ snd_soc_dai_dma_data_set(dai, stream, NULL); -+ } -+ -+ return 0; -+} -+ -+static int aiu_encoder_i2s_probe_dai(struct snd_soc_dai *dai) -+{ -+ struct aiu *aiu = snd_soc_dai_get_drvdata(dai); -+ struct gx_iface *iface = &aiu->i2s.iface; -+ int stream; -+ -+ for_each_pcm_streams(stream) { -+ struct gx_stream *ts; -+ -+ if (!snd_soc_dai_get_widget(dai, stream)) -+ continue; -+ -+ ts = gx_stream_alloc(iface); -+ if (!ts) { -+ aiu_encoder_i2s_remove_dai(dai); -+ return -ENOMEM; -+ } -+ snd_soc_dai_dma_data_set(dai, stream, ts); -+ } -+ -+ iface->mclk = aiu->i2s.clks[MCLK].clk; -+ iface->mclk_rate = clk_get_rate(iface->mclk); -+ -+ return 0; - } - - const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops = { -+ .probe = aiu_encoder_i2s_probe_dai, -+ .remove = aiu_encoder_i2s_remove_dai, - .hw_params = aiu_encoder_i2s_hw_params, -+ .prepare = aiu_encoder_i2s_prepare, - .hw_free = aiu_encoder_i2s_hw_free, - .set_fmt = aiu_encoder_i2s_set_fmt, - .set_sysclk = aiu_encoder_i2s_set_sysclk, - .startup = aiu_encoder_i2s_startup, - .shutdown = aiu_encoder_i2s_shutdown, -+ .trigger = aiu_encoder_i2s_trigger, - }; -diff --git a/sound/soc/meson/aiu-formatter-i2s.c b/sound/soc/meson/aiu-formatter-i2s.c -new file mode 100644 ---- /dev/null -+++ b/sound/soc/meson/aiu-formatter-i2s.c -@@ -0,0 +1,103 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// -+// Copyright (c) 2026 BayLibre, SAS. -+// Author: Valerio Setti -+ -+#include -+#include -+#include -+ -+#include "aiu.h" -+#include "gx-formatter.h" -+ -+#define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0) -+#define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5) -+#define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9) -+ -+#define AIU_I2S_DAC_CFG_MSB_FIRST BIT(2) -+ -+static struct snd_soc_dai * -+aiu_formatter_i2s_get_be(struct snd_soc_dapm_widget *w) -+{ -+ struct snd_soc_dapm_path *p; -+ struct snd_soc_dai *be; -+ -+ snd_soc_dapm_widget_for_each_sink_path(w, p) { -+ if (!p->connect) -+ continue; -+ -+ if (p->sink->id == snd_soc_dapm_dai_in) -+ return (struct snd_soc_dai *)p->sink->priv; -+ -+ be = aiu_formatter_i2s_get_be(p->sink); -+ if (be) -+ return be; -+ } -+ -+ return NULL; -+} -+ -+static struct gx_stream * -+aiu_formatter_i2s_get_stream(struct snd_soc_dapm_widget *w) -+{ -+ struct snd_soc_dai *be = aiu_formatter_i2s_get_be(w); -+ -+ if (!be) -+ return NULL; -+ -+ return snd_soc_dai_dma_data_get_playback(be); -+} -+ -+static int aiu_formatter_i2s_prepare(struct regmap *map, -+ const struct gx_formatter_hw *quirks, -+ struct gx_stream *ts) -+{ -+ /* Always operate in split (classic interleaved) mode */ -+ unsigned int desc = 0; -+ -+ /* -+ * Pipeline reset is already implemented in aiu_fifo_i2s_trigger() at -+ * trigger time. -+ */ -+ -+ switch (ts->physical_width) { -+ case 16: /* Nothing to do */ -+ break; -+ -+ case 32: -+ desc |= (AIU_I2S_SOURCE_DESC_MODE_24BIT | -+ AIU_I2S_SOURCE_DESC_MODE_32BIT); -+ break; -+ -+ default: -+ return -EINVAL; -+ } -+ -+ switch (ts->channels) { -+ case 2: /* Nothing to do */ -+ break; -+ case 8: -+ desc |= AIU_I2S_SOURCE_DESC_MODE_8CH; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ regmap_update_bits(map, AIU_I2S_SOURCE_DESC, -+ AIU_I2S_SOURCE_DESC_MODE_8CH | -+ AIU_I2S_SOURCE_DESC_MODE_24BIT | -+ AIU_I2S_SOURCE_DESC_MODE_32BIT, -+ desc); -+ -+ /* Send data MSB first */ -+ regmap_update_bits(map, AIU_I2S_DAC_CFG, -+ AIU_I2S_DAC_CFG_MSB_FIRST, -+ AIU_I2S_DAC_CFG_MSB_FIRST); -+ -+ return 0; -+} -+ -+const struct gx_formatter_ops aiu_formatter_i2s_ops = { -+ .get_stream = aiu_formatter_i2s_get_stream, -+ .prepare = aiu_formatter_i2s_prepare, -+}; -diff --git a/sound/soc/meson/aiu.c b/sound/soc/meson/aiu.c ---- a/sound/soc/meson/aiu.c -+++ b/sound/soc/meson/aiu.c -@@ -29,13 +29,22 @@ static SOC_ENUM_SINGLE_DECL(aiu_spdif_encode_sel_enum, AIU_I2S_MISC, - static const struct snd_kcontrol_new aiu_spdif_encode_mux = - SOC_DAPM_ENUM("SPDIF Buffer Src", aiu_spdif_encode_sel_enum); - --static const struct snd_soc_dapm_widget aiu_cpu_dapm_widgets[] = { -- SND_SOC_DAPM_MUX("SPDIF SRC SEL", SND_SOC_NOPM, 0, 0, -- &aiu_spdif_encode_mux), -+#define AIU_WIDGET_SPDIF_SRC_SEL 0 -+#define AIU_WIDGET_I2S_FORMATTER 1 -+ -+static struct snd_soc_dapm_widget aiu_cpu_dapm_widgets[] = { -+ [AIU_WIDGET_SPDIF_SRC_SEL] = -+ SND_SOC_DAPM_MUX("SPDIF SRC SEL", SND_SOC_NOPM, 0, 0, -+ &aiu_spdif_encode_mux), -+ [AIU_WIDGET_I2S_FORMATTER] = -+ SND_SOC_DAPM_PGA_E("I2S Formatter", SND_SOC_NOPM, 0, 0, NULL, 0, -+ gx_formatter_event, -+ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD)), - }; - - static const struct snd_soc_dapm_route aiu_cpu_dapm_routes[] = { -- { "I2S Encoder Playback", NULL, "I2S FIFO Playback" }, -+ { "I2S Formatter", NULL, "I2S FIFO Playback" }, -+ { "I2S Encoder Playback", NULL, "I2S Formatter" }, - { "SPDIF SRC SEL", "SPDIF", "SPDIF FIFO Playback" }, - { "SPDIF SRC SEL", "I2S", "I2S FIFO Playback" }, - { "SPDIF Encoder Playback", NULL, "SPDIF SRC SEL" }, -@@ -145,6 +154,7 @@ static struct snd_soc_dai_driver aiu_cpu_dai_drv[] = { - .formats = AIU_FORMATS, - }, - .ops = &aiu_encoder_i2s_dai_ops, -+ .symmetric_rate = 1, - }, - [CPU_SPDIF_ENCODER] = { - .name = "SPDIF Encoder", -@@ -172,6 +182,11 @@ static const struct regmap_config aiu_regmap_cfg = { - .max_register = 0x2ac, - }; - -+static const struct gx_formatter_driver aiu_formatter_i2s_drv = { -+ .regmap_cfg = &aiu_regmap_cfg, -+ .ops = &aiu_formatter_i2s_ops, -+}; -+ - static int aiu_clk_bulk_get(struct device *dev, - const char * const *ids, - unsigned int num, -@@ -282,6 +297,14 @@ static int aiu_probe(struct platform_device *pdev) - if (ret) - return ret; - -+ /* Allocate the aiu-formatter into its widget */ -+ ret = gx_formatter_create(dev, &aiu_cpu_dapm_widgets[AIU_WIDGET_I2S_FORMATTER], -+ &aiu_formatter_i2s_drv, map); -+ if (ret) { -+ dev_err(dev, "Failed to allocate aiu formatter\n"); -+ goto err; -+ } -+ - /* Register the cpu component of the aiu */ - ret = snd_soc_register_component(dev, &aiu_cpu_component, - aiu_cpu_dai_drv, -@@ -310,12 +333,14 @@ static int aiu_probe(struct platform_device *pdev) - - return 0; - err: -+ gx_formatter_free(&aiu_cpu_dapm_widgets[AIU_WIDGET_I2S_FORMATTER]); - snd_soc_unregister_component(dev); - return ret; - } - - static void aiu_remove(struct platform_device *pdev) - { -+ gx_formatter_free(&aiu_cpu_dapm_widgets[AIU_WIDGET_I2S_FORMATTER]); - snd_soc_unregister_component(&pdev->dev); - } - -diff --git a/sound/soc/meson/aiu.h b/sound/soc/meson/aiu.h ---- a/sound/soc/meson/aiu.h -+++ b/sound/soc/meson/aiu.h -@@ -7,6 +7,8 @@ - #ifndef _MESON_AIU_H - #define _MESON_AIU_H - -+#include "gx-formatter.h" -+ - struct clk; - struct clk_bulk_data; - struct device; -@@ -25,6 +27,7 @@ struct aiu_interface { - struct clk_bulk_data *clks; - unsigned int clk_num; - int irq; -+ struct gx_iface iface; - }; - - struct aiu_platform_data { -@@ -58,6 +61,7 @@ extern const struct snd_soc_dai_ops aiu_fifo_i2s_dai_ops; - extern const struct snd_soc_dai_ops aiu_fifo_spdif_dai_ops; - extern const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops; - extern const struct snd_soc_dai_ops aiu_encoder_spdif_dai_ops; -+extern const struct gx_formatter_ops aiu_formatter_i2s_ops; - - #define AIU_IEC958_BPF 0x000 - #define AIU_958_MISC 0x010 -diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c ---- a/sound/soc/meson/axg-card.c -+++ b/sound/soc/meson/axg-card.c -@@ -107,6 +107,7 @@ static int axg_card_add_tdm_loopback(struct snd_soc_card *card, - struct snd_soc_dai_link *pad; - struct snd_soc_dai_link *lb; - struct snd_soc_dai_link_component *dlc; -+ struct device *dev = card->dev; - int ret; - - /* extend links */ -@@ -117,11 +118,11 @@ static int axg_card_add_tdm_loopback(struct snd_soc_card *card, - pad = &card->dai_link[*index]; - lb = &card->dai_link[*index + 1]; - -- lb->name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-lb", pad->name); -+ lb->name = devm_kasprintf(dev, GFP_KERNEL, "%s-lb", pad->name); - if (!lb->name) - return -ENOMEM; - -- dlc = devm_kzalloc(card->dev, sizeof(*dlc), GFP_KERNEL); -+ dlc = devm_kzalloc(dev, sizeof(*dlc), GFP_KERNEL); - if (!dlc) - return -ENOMEM; - -@@ -158,13 +159,14 @@ static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card, - struct device_node *node, - struct axg_dai_link_tdm_data *be) - { -+ struct device *dev = card->dev; - char propname[32]; - u32 tx, rx; - int i; - -- be->tx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES, -+ be->tx_mask = devm_kcalloc(dev, AXG_TDM_NUM_LANES, - sizeof(*be->tx_mask), GFP_KERNEL); -- be->rx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES, -+ be->rx_mask = devm_kcalloc(dev, AXG_TDM_NUM_LANES, - sizeof(*be->rx_mask), GFP_KERNEL); - if (!be->tx_mask || !be->rx_mask) - return -ENOMEM; -@@ -191,7 +193,7 @@ static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card, - - /* ... but the interface should at least have one direction */ - if (!tx && !rx) { -- dev_err(card->dev, "tdm link has no cpu slots\n"); -+ dev_err(dev, "tdm link has no cpu slots\n"); - return -EINVAL; - } - -@@ -207,7 +209,7 @@ static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card, - * Error if the slots can't accommodate the largest mask or - * if it is just too big - */ -- dev_err(card->dev, "bad slot number\n"); -+ dev_err(dev, "bad slot number\n"); - return -EINVAL; - } - -@@ -222,8 +224,9 @@ static int axg_card_parse_codecs_masks(struct snd_soc_card *card, - struct axg_dai_link_tdm_data *be) - { - struct axg_dai_link_tdm_mask *codec_mask; -+ struct device *dev = card->dev; - -- codec_mask = devm_kcalloc(card->dev, link->num_codecs, -+ codec_mask = devm_kcalloc(dev, link->num_codecs, - sizeof(*codec_mask), GFP_KERNEL); - if (!codec_mask) - return -ENOMEM; -@@ -249,10 +252,11 @@ static int axg_card_parse_tdm(struct snd_soc_card *card, - struct meson_card *priv = snd_soc_card_get_drvdata(card); - struct snd_soc_dai_link *link = &card->dai_link[*index]; - struct axg_dai_link_tdm_data *be; -+ struct device *dev = card->dev; - int ret; - - /* Allocate tdm link parameters */ -- be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL); -+ be = devm_kzalloc(dev, sizeof(*be), GFP_KERNEL); - if (!be) - return -ENOMEM; - priv->link_data[*index] = be; -@@ -266,7 +270,7 @@ static int axg_card_parse_tdm(struct snd_soc_card *card, - - ret = axg_card_parse_cpu_tdm_slots(card, link, node, be); - if (ret) { -- dev_err(card->dev, "error parsing tdm link slots\n"); -+ dev_err(dev, "error parsing tdm link slots\n"); - return ret; - } - -@@ -310,9 +314,10 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np, - { - struct snd_soc_dai_link *dai_link = &card->dai_link[*index]; - struct snd_soc_dai_link_component *cpu; -+ struct device *dev = card->dev; - int ret; - -- cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL); -+ cpu = devm_kzalloc(dev, sizeof(*cpu), GFP_KERNEL); - if (!cpu) - return -ENOMEM; - -diff --git a/sound/soc/meson/g12a-toacodec.c b/sound/soc/meson/g12a-toacodec.c ---- a/sound/soc/meson/g12a-toacodec.c -+++ b/sound/soc/meson/g12a-toacodec.c -@@ -312,7 +312,7 @@ static int g12a_toacodec_probe(struct platform_device *pdev) - - ret = device_reset(dev); - if (ret) -- return ret; -+ return dev_err_probe(dev, ret, "failed to reset device\n"); - - regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(regs)) -diff --git a/sound/soc/meson/g12a-tohdmitx.c b/sound/soc/meson/g12a-tohdmitx.c ---- a/sound/soc/meson/g12a-tohdmitx.c -+++ b/sound/soc/meson/g12a-tohdmitx.c -@@ -251,7 +251,7 @@ static int g12a_tohdmitx_probe(struct platform_device *pdev) - - ret = device_reset(dev); - if (ret) -- return ret; -+ return dev_err_probe(dev, ret, "failed to reset device\n"); - - regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(regs)) -diff --git a/sound/soc/meson/gx-card.c b/sound/soc/meson/gx-card.c ---- a/sound/soc/meson/gx-card.c -+++ b/sound/soc/meson/gx-card.c -@@ -48,9 +48,10 @@ static int gx_card_parse_i2s(struct snd_soc_card *card, - struct meson_card *priv = snd_soc_card_get_drvdata(card); - struct snd_soc_dai_link *link = &card->dai_link[*index]; - struct gx_dai_link_i2s_data *be; -+ struct device *dev = card->dev; - - /* Allocate i2s link parameters */ -- be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL); -+ be = devm_kzalloc(dev, sizeof(*be), GFP_KERNEL); - if (!be) - return -ENOMEM; - priv->link_data[*index] = be; -@@ -81,9 +82,10 @@ static int gx_card_add_link(struct snd_soc_card *card, struct device_node *np, - { - struct snd_soc_dai_link *dai_link = &card->dai_link[*index]; - struct snd_soc_dai_link_component *cpu; -+ struct device *dev = card->dev; - int ret; - -- cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL); -+ cpu = devm_kzalloc(dev, sizeof(*cpu), GFP_KERNEL); - if (!cpu) - return -ENOMEM; - -diff --git a/sound/soc/meson/gx-formatter.c b/sound/soc/meson/gx-formatter.c -new file mode 100644 ---- /dev/null -+++ b/sound/soc/meson/gx-formatter.c -@@ -0,0 +1,282 @@ -+// SPDX-License-Identifier: (GPL-2.0 OR MIT) -+// -+// Copyright (c) 2026 BayLibre, SAS. -+// Author: Valerio Setti -+ -+#include -+#include -+#include -+#include -+ -+#include "gx-formatter.h" -+ -+struct gx_formatter { -+ struct list_head list; -+ struct gx_stream *stream; -+ const struct gx_formatter_driver *drv; -+ bool enabled; -+ struct regmap *map; -+}; -+ -+static int gx_formatter_enable(struct gx_formatter *formatter) -+{ -+ int ret; -+ -+ /* Do nothing if the formatter is already enabled */ -+ if (formatter->enabled) -+ return 0; -+ -+ /* Setup the stream parameter in the formatter */ -+ if (formatter->drv->ops->prepare) { -+ ret = formatter->drv->ops->prepare(formatter->map, -+ formatter->drv->quirks, -+ formatter->stream); -+ if (ret) -+ return ret; -+ } -+ -+ /* Finally, actually enable the formatter */ -+ if (formatter->drv->ops->enable) -+ formatter->drv->ops->enable(formatter->map); -+ -+ formatter->enabled = true; -+ -+ return 0; -+} -+ -+static void gx_formatter_disable(struct gx_formatter *formatter) -+{ -+ /* Do nothing if the formatter is already disabled */ -+ if (!formatter->enabled) -+ return; -+ -+ if (formatter->drv->ops->disable) -+ formatter->drv->ops->disable(formatter->map); -+ -+ formatter->enabled = false; -+} -+ -+static int gx_formatter_attach(struct gx_formatter *formatter) -+{ -+ struct gx_stream *ts = formatter->stream; -+ int ret = 0; -+ -+ mutex_lock(&ts->lock); -+ -+ /* Catch up if the stream is already running when we attach */ -+ if (ts->ready) { -+ ret = gx_formatter_enable(formatter); -+ if (ret) { -+ pr_err("failed to enable formatter\n"); -+ goto out; -+ } -+ } -+ -+ list_add_tail(&formatter->list, &ts->formatter_list); -+out: -+ mutex_unlock(&ts->lock); -+ return ret; -+} -+ -+static void gx_formatter_detach(struct gx_formatter *formatter) -+{ -+ struct gx_stream *ts = formatter->stream; -+ -+ if (!ts) -+ return; -+ -+ mutex_lock(&ts->lock); -+ list_del(&formatter->list); -+ mutex_unlock(&ts->lock); -+ -+ gx_formatter_disable(formatter); -+} -+ -+static int gx_formatter_power_up(struct gx_formatter *formatter, -+ struct snd_soc_dapm_widget *w) -+{ -+ struct gx_stream *ts = formatter->drv->ops->get_stream(w); -+ int ret; -+ -+ /* -+ * If we don't get a stream at this stage, it would mean that the -+ * widget is powering up but is not attached to any backend DAI. -+ * It should not happen, ever ! -+ */ -+ if (WARN_ON(!ts)) -+ return -ENODEV; -+ -+ formatter->stream = ts; -+ INIT_LIST_HEAD(&formatter->list); -+ ret = gx_formatter_attach(formatter); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+static void gx_formatter_power_down(struct gx_formatter *formatter) -+{ -+ gx_formatter_detach(formatter); -+ formatter->stream = NULL; -+} -+ -+int gx_formatter_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *control, -+ int event) -+{ -+ struct snd_soc_component *c; -+ struct gx_formatter *formatter; -+ int ret = 0; -+ -+ c = snd_soc_dapm_to_component(w->dapm); -+ -+ if (w->priv) -+ formatter = w->priv; -+ else -+ formatter = snd_soc_component_get_drvdata(c); -+ -+ switch (event) { -+ case SND_SOC_DAPM_PRE_PMU: -+ ret = gx_formatter_power_up(formatter, w); -+ break; -+ -+ case SND_SOC_DAPM_PRE_PMD: -+ gx_formatter_power_down(formatter); -+ break; -+ -+ default: -+ dev_err(c->dev, "Unexpected event %d\n", event); -+ return -EINVAL; -+ } -+ -+ return ret; -+} -+EXPORT_SYMBOL_GPL(gx_formatter_event); -+ -+int gx_formatter_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ const struct gx_formatter_driver *drv; -+ struct gx_formatter *formatter; -+ void __iomem *regs; -+ -+ drv = of_device_get_match_data(dev); -+ if (!drv) { -+ dev_err(dev, "failed to match device\n"); -+ return -ENODEV; -+ } -+ -+ formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL); -+ if (!formatter) -+ return -ENOMEM; -+ platform_set_drvdata(pdev, formatter); -+ formatter->drv = drv; -+ -+ regs = devm_platform_ioremap_resource(pdev, 0); -+ if (IS_ERR(regs)) -+ return PTR_ERR(regs); -+ -+ formatter->map = devm_regmap_init_mmio(dev, regs, drv->regmap_cfg); -+ if (IS_ERR(formatter->map)) { -+ dev_err(dev, "failed to init regmap: %ld\n", -+ PTR_ERR(formatter->map)); -+ return PTR_ERR(formatter->map); -+ } -+ -+ return devm_snd_soc_register_component(dev, drv->component_drv, -+ NULL, 0); -+} -+EXPORT_SYMBOL_GPL(gx_formatter_probe); -+ -+int gx_formatter_create(struct device *dev, -+ struct snd_soc_dapm_widget *w, -+ const struct gx_formatter_driver *drv, -+ struct regmap *regmap) -+{ -+ struct gx_formatter *formatter; -+ -+ formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL); -+ if (!formatter) -+ return -ENOMEM; -+ -+ formatter->drv = drv; -+ formatter->map = regmap; -+ -+ w->priv = formatter; -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(gx_formatter_create); -+ -+int gx_stream_start(struct gx_stream *ts) -+{ -+ struct gx_formatter *formatter; -+ int ret = 0; -+ -+ mutex_lock(&ts->lock); -+ -+ /* Start all the formatters attached to the stream */ -+ list_for_each_entry(formatter, &ts->formatter_list, list) { -+ ret = gx_formatter_enable(formatter); -+ if (ret) { -+ pr_err("failed to enable formatter\n"); -+ goto out; -+ } -+ } -+ -+ ts->ready = true; -+ -+out: -+ mutex_unlock(&ts->lock); -+ return ret; -+} -+EXPORT_SYMBOL_GPL(gx_stream_start); -+ -+void gx_stream_stop(struct gx_stream *ts) -+{ -+ struct gx_formatter *formatter; -+ -+ mutex_lock(&ts->lock); -+ ts->ready = false; -+ -+ /* Stop all the formatters attached to the stream */ -+ list_for_each_entry(formatter, &ts->formatter_list, list) { -+ gx_formatter_disable(formatter); -+ } -+ -+ mutex_unlock(&ts->lock); -+} -+EXPORT_SYMBOL_GPL(gx_stream_stop); -+ -+struct gx_stream *gx_stream_alloc(struct gx_iface *iface) -+{ -+ struct gx_stream *ts; -+ -+ ts = kzalloc(sizeof(*ts), GFP_KERNEL); -+ if (ts) { -+ INIT_LIST_HEAD(&ts->formatter_list); -+ mutex_init(&ts->lock); -+ ts->iface = iface; -+ } -+ -+ return ts; -+} -+EXPORT_SYMBOL_GPL(gx_stream_alloc); -+ -+void gx_stream_free(struct gx_stream *ts) -+{ -+ /* -+ * If the list is not empty, it would mean that one of the formatter -+ * widget is still powered and attached to the interface while we -+ * are removing the TDM DAI. It should not be possible -+ */ -+ WARN_ON(!list_empty(&ts->formatter_list)); -+ mutex_destroy(&ts->lock); -+ kfree(ts); -+} -+EXPORT_SYMBOL_GPL(gx_stream_free); -+ -+MODULE_DESCRIPTION("Amlogic GX formatter driver"); -+MODULE_AUTHOR("Valerio Setti "); -+MODULE_LICENSE("GPL"); -diff --git a/sound/soc/meson/gx-formatter.h b/sound/soc/meson/gx-formatter.h -new file mode 100644 ---- /dev/null -+++ b/sound/soc/meson/gx-formatter.h -@@ -0,0 +1,56 @@ -+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ -+/* -+ * Copyright (c) 2026 Baylibre SAS. -+ * Author: Valerio Setti -+ */ -+ -+#ifndef _MESON_GX_FORMATTER_H -+#define _MESON_GX_FORMATTER_H -+ -+#include "gx-interface.h" -+ -+struct platform_device; -+struct regmap; -+struct snd_soc_dapm_widget; -+struct snd_kcontrol; -+ -+struct gx_formatter_hw { -+ unsigned int skew_offset; -+}; -+ -+struct gx_formatter_ops { -+ struct gx_stream *(*get_stream)(struct snd_soc_dapm_widget *w); -+ void (*enable)(struct regmap *map); -+ void (*disable)(struct regmap *map); -+ int (*prepare)(struct regmap *map, -+ const struct gx_formatter_hw *quirks, -+ struct gx_stream *ts); -+}; -+ -+struct gx_formatter_driver { -+ const struct snd_soc_component_driver *component_drv; -+ const struct regmap_config *regmap_cfg; -+ const struct gx_formatter_ops *ops; -+ const struct gx_formatter_hw *quirks; -+}; -+ -+int gx_formatter_event(struct snd_soc_dapm_widget *w, -+ struct snd_kcontrol *control, -+ int event); -+int gx_formatter_probe(struct platform_device *pdev); -+ -+int gx_formatter_create(struct device *dev, -+ struct snd_soc_dapm_widget *w, -+ const struct gx_formatter_driver *drv, -+ struct regmap *regmap); -+ -+/* -+ * Formatter data is already freed when the associated device is removed, -+ * so we just need to remove the pointer from the widget. -+ */ -+static inline void gx_formatter_free(struct snd_soc_dapm_widget *w) -+{ -+ w->priv = NULL; -+} -+ -+#endif /* _MESON_GX_FORMATTER_H */ -diff --git a/sound/soc/meson/gx-interface.h b/sound/soc/meson/gx-interface.h -new file mode 100644 ---- /dev/null -+++ b/sound/soc/meson/gx-interface.h -@@ -0,0 +1,42 @@ -+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ -+/* -+ * Copyright (c) 2026 Baylibre SAS. -+ * Author: Valerio Setti -+ */ -+ -+#ifndef _MESON_GX_INTERFACE_H -+#define _MESON_GX_INTERFACE_H -+ -+#include -+#include -+#include -+#include -+#include -+ -+struct gx_iface { -+ struct clk *mclk; -+ unsigned long mclk_rate; -+ -+ /* format is common to all the DAIs of the iface */ -+ unsigned int fmt; -+}; -+ -+struct gx_stream { -+ struct gx_iface *iface; -+ struct list_head formatter_list; -+ struct mutex lock; -+ unsigned int channels; -+ unsigned int width; -+ unsigned int physical_width; -+ bool ready; -+ -+ /* For continuous clock tracking */ -+ bool clk_enabled; -+}; -+ -+struct gx_stream *gx_stream_alloc(struct gx_iface *iface); -+void gx_stream_free(struct gx_stream *ts); -+int gx_stream_start(struct gx_stream *ts); -+void gx_stream_stop(struct gx_stream *ts); -+ -+#endif /* _MESON_GX_INTERFACE_H */ -diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c ---- a/sound/soc/meson/meson-card-utils.c -+++ b/sound/soc/meson/meson-card-utils.c -@@ -50,25 +50,20 @@ int meson_card_reallocate_links(struct snd_soc_card *card, - num_links * sizeof(*priv->card.dai_link), - GFP_KERNEL | __GFP_ZERO); - if (!links) -- goto err_links; -+ return -ENOMEM; -+ -+ priv->card.dai_link = links; -+ priv->card.num_links = num_links; - - ldata = krealloc(priv->link_data, - num_links * sizeof(*priv->link_data), - GFP_KERNEL | __GFP_ZERO); -+ /* meson_card_clean_references() will free the links on this error path */ - if (!ldata) -- goto err_ldata; -+ return -ENOMEM; - -- priv->card.dai_link = links; - priv->link_data = ldata; -- priv->card.num_links = num_links; - return 0; -- --err_ldata: -- kfree(links); --err_links: -- dev_err(priv->card.dev, "failed to allocate links\n"); -- return -ENOMEM; -- - } - EXPORT_SYMBOL_GPL(meson_card_reallocate_links); - -@@ -76,6 +71,7 @@ int meson_card_parse_dai(struct snd_soc_card *card, - struct device_node *node, - struct snd_soc_dai_link_component *dlc) - { -+ struct device *dev = card->dev; - int ret; - - if (!dlc || !node) -@@ -83,7 +79,7 @@ int meson_card_parse_dai(struct snd_soc_card *card, - - ret = snd_soc_of_get_dlc(node, NULL, dlc, 0); - if (ret) -- return dev_err_probe(card->dev, ret, "can't parse dai\n"); -+ return dev_err_probe(dev, ret, "can't parse dai\n"); - - return ret; - } -@@ -94,7 +90,8 @@ static int meson_card_set_link_name(struct snd_soc_card *card, - struct device_node *node, - const char *prefix) - { -- char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s", -+ struct device *dev = card->dev; -+ char *name = devm_kasprintf(dev, GFP_KERNEL, "%s.%s", - prefix, node->full_name); - if (!name) - return -ENOMEM; -@@ -137,16 +134,17 @@ int meson_card_set_be_link(struct snd_soc_card *card, - struct device_node *node) - { - struct snd_soc_dai_link_component *codec; -+ struct device *dev = card->dev; - int ret, num_codecs; - - num_codecs = of_get_child_count(node); - if (!num_codecs) { -- dev_err(card->dev, "be link %s has no codec\n", -+ dev_err(dev, "be link %s has no codec\n", - node->full_name); - return -EINVAL; - } - -- codec = devm_kcalloc(card->dev, num_codecs, sizeof(*codec), GFP_KERNEL); -+ codec = devm_kcalloc(dev, num_codecs, sizeof(*codec), GFP_KERNEL); - if (!codec) - return -ENOMEM; - -@@ -163,7 +161,7 @@ int meson_card_set_be_link(struct snd_soc_card *card, - - ret = meson_card_set_link_name(card, link, node, "be"); - if (ret) -- dev_err(card->dev, "error setting %pOFn link name\n", node); -+ dev_err(dev, "error setting %pOFn link name\n", node); - - return ret; - } -@@ -194,12 +192,13 @@ EXPORT_SYMBOL_GPL(meson_card_set_fe_link); - static int meson_card_add_links(struct snd_soc_card *card) - { - struct meson_card *priv = snd_soc_card_get_drvdata(card); -- struct device_node *node = card->dev->of_node; -+ struct device *dev = card->dev; -+ struct device_node *node = dev->of_node; - int num, i, ret; - - num = of_get_child_count(node); - if (!num) { -- dev_err(card->dev, "card has no links\n"); -+ dev_err(dev, "card has no links\n"); - return -EINVAL; - } - -@@ -224,8 +223,10 @@ static int meson_card_parse_of_optional(struct snd_soc_card *card, - int (*func)(struct snd_soc_card *c, - const char *p)) - { -+ struct device *dev = card->dev; -+ - /* If property is not provided, don't fail ... */ -- if (!of_property_present(card->dev->of_node, propname)) -+ if (!of_property_present(dev->of_node, propname)) - return 0; - - /* ... but do fail if it is provided and the parsing fails */ diff --git a/sound/soc/meson/meson-codec-glue.c b/sound/soc/meson/meson-codec-glue.c --- a/sound/soc/meson/meson-codec-glue.c +++ b/sound/soc/meson/meson-codec-glue.c @@ -41302,8834 +11487,6 @@ diff --git a/sound/soc/meson/meson-codec-glue.c b/sound/soc/meson/meson-codec-gl data->params.channels_min = params_channels(params); data->params.channels_max = params_channels(params); data->params.sig_bits = dai->driver->playback.sig_bits; -diff --git a/sound/soc/meson/t9015.c b/sound/soc/meson/t9015.c ---- a/sound/soc/meson/t9015.c -+++ b/sound/soc/meson/t9015.c -@@ -78,8 +78,14 @@ static int t9015_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) - return 0; - } - -+static const u64 t9015_dai_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J; -+ - static const struct snd_soc_dai_ops t9015_dai_ops = { - .set_fmt = t9015_dai_set_fmt, -+ .auto_selectable_formats = &t9015_dai_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static struct snd_soc_dai_driver t9015_dai = { -@@ -265,10 +271,8 @@ static int t9015_probe(struct platform_device *pdev) - return dev_err_probe(dev, PTR_ERR(priv->avdd), "failed to AVDD\n"); - - ret = device_reset(dev); -- if (ret) { -- dev_err(dev, "reset failed\n"); -- return ret; -- } -+ if (ret) -+ return dev_err_probe(dev, ret, "failed to reset device\n"); - - regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(regs)) { -diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c ---- a/sound/soc/mxs/mxs-saif.c -+++ b/sound/soc/mxs/mxs-saif.c -@@ -826,12 +826,9 @@ static int mxs_saif_probe(struct platform_device *pdev) - mxs_saif[saif->id] = saif; - - saif->clk = devm_clk_get(&pdev->dev, NULL); -- if (IS_ERR(saif->clk)) { -- ret = PTR_ERR(saif->clk); -- dev_err(&pdev->dev, "Cannot get the clock: %d\n", -- ret); -- return ret; -- } -+ if (IS_ERR(saif->clk)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(saif->clk), -+ "Cannot get the clock\n"); - - saif->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(saif->base)) -@@ -844,10 +841,8 @@ static int mxs_saif_probe(struct platform_device *pdev) - saif->dev = &pdev->dev; - ret = devm_request_irq(&pdev->dev, irq, mxs_saif_irq, 0, - dev_name(&pdev->dev), saif); -- if (ret) { -- dev_err(&pdev->dev, "failed to request irq\n"); -+ if (ret) - return ret; -- } - - platform_set_drvdata(pdev, saif); - -@@ -860,16 +855,12 @@ static int mxs_saif_probe(struct platform_device *pdev) - - ret = devm_snd_soc_register_component(&pdev->dev, &mxs_saif_component, - &mxs_saif_dai, 1); -- if (ret) { -- dev_err(&pdev->dev, "register DAI failed\n"); -+ if (ret) - return ret; -- } - - ret = mxs_pcm_platform_register(&pdev->dev); -- if (ret) { -- dev_err(&pdev->dev, "register PCM failed: %d\n", ret); -+ if (ret) - return ret; -- } - - return 0; - } -diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c ---- a/sound/soc/mxs/mxs-sgtl5000.c -+++ b/sound/soc/mxs/mxs-sgtl5000.c -@@ -155,8 +155,6 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev) - - ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); - if (ret) { -- dev_err(&pdev->dev, "failed to parse audio-routing (%d)\n", -- ret); - mxs_saif_put_mclk(0); - return ret; - } -diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c ---- a/sound/soc/pxa/pxa-ssp.c -+++ b/sound/soc/pxa/pxa-ssp.c -@@ -769,13 +769,10 @@ static int pxa_ssp_probe(struct snd_soc_dai *dai) - goto err_priv; - } - -- priv->extclk = devm_clk_get(dev, "extclk"); -+ priv->extclk = devm_clk_get_optional(dev, "extclk"); - if (IS_ERR(priv->extclk)) { - ret = PTR_ERR(priv->extclk); -- if (ret == -EPROBE_DEFER) -- goto err_priv; -- -- priv->extclk = NULL; -+ goto err_priv; - } - } else { - priv->ssp = pxa_ssp_request(dai->id + 1, "SoC audio"); -diff --git a/sound/soc/pxa/pxa2xx-ac97-lib.c b/sound/soc/pxa/pxa2xx-ac97-lib.c ---- a/sound/soc/pxa/pxa2xx-ac97-lib.c -+++ b/sound/soc/pxa/pxa2xx-ac97-lib.c -@@ -316,10 +316,8 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev) - int irq; - - ac97_reg_base = devm_platform_ioremap_resource(dev, 0); -- if (IS_ERR(ac97_reg_base)) { -- dev_err(&dev->dev, "Missing MMIO resource\n"); -+ if (IS_ERR(ac97_reg_base)) - return PTR_ERR(ac97_reg_base); -- } - - if (cpu_is_pxa27x()) { - /* Assert reset using GPIOD_OUT_HIGH, because reset is GPIO_ACTIVE_LOW */ -diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c ---- a/sound/soc/qcom/common.c -+++ b/sound/soc/qcom/common.c -@@ -23,6 +23,161 @@ static const struct snd_soc_dapm_widget qcom_jack_snd_widgets[] = { - SND_SOC_DAPM_SPK("DP7 Jack", NULL), - }; - -+static struct device_node *qcom_snd_get_link_node(struct snd_soc_pcm_runtime *rtd) -+{ -+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); -+ struct snd_soc_card *card = rtd->card; -+ struct of_phandle_args args; -+ int ret; -+ -+ if (!card->dev || !card->dev->of_node) -+ return NULL; -+ -+ for_each_available_child_of_node_scoped(card->dev->of_node, np) { -+ struct device_node *cpu_np __free(device_node) = -+ of_get_child_by_name(np, "cpu"); -+ -+ if (!cpu_np) -+ continue; -+ -+ ret = of_parse_phandle_with_args(cpu_np, "sound-dai", "#sound-dai-cells", 0, -+ &args); -+ if (ret) -+ continue; -+ -+ if (args.np == rtd->dai_link->cpus[0].of_node && -+ args.args_count == 1 && args.args[0] == cpu_dai->id) { -+ of_node_put(args.np); -+ return of_node_get(np); -+ } -+ -+ of_node_put(args.np); -+ } -+ -+ return NULL; -+} -+ -+static int qcom_snd_parse_tdm_slot(struct device_node *np, -+ struct qcom_snd_tdm_slot_cfg *cfg) -+{ -+ memset(cfg, 0, sizeof(*cfg)); -+ -+ return snd_soc_of_parse_tdm_slot(np, &cfg->tx_mask, &cfg->rx_mask, -+ &cfg->slots, &cfg->slot_width); -+} -+ -+static int qcom_snd_normalize_tdm_slots(struct qcom_snd_tdm_slot_cfg *cpu_cfg, -+ struct qcom_snd_tdm_slot_cfg *codec_cfg) -+{ -+ unsigned int slots; -+ unsigned int slot_width; -+ -+ if (cpu_cfg->slots && codec_cfg->slots && cpu_cfg->slots != codec_cfg->slots) -+ return -EINVAL; -+ -+ if (cpu_cfg->slot_width && codec_cfg->slot_width && -+ cpu_cfg->slot_width != codec_cfg->slot_width) -+ return -EINVAL; -+ -+ slots = cpu_cfg->slots ?: codec_cfg->slots; -+ if (!slots) -+ return 0; -+ -+ slot_width = cpu_cfg->slot_width ?: codec_cfg->slot_width; -+ if (!slot_width) -+ return -EINVAL; -+ -+ cpu_cfg->slots = slots; -+ codec_cfg->slots = slots; -+ cpu_cfg->slot_width = slot_width; -+ codec_cfg->slot_width = slot_width; -+ -+ return 0; -+} -+ -+static int qcom_snd_parse_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, -+ struct qcom_snd_tdm_slot_cfg *cpu_cfg, -+ struct qcom_snd_tdm_slot_cfg *codec_cfg) -+{ -+ struct device_node *link_np __free(device_node) = qcom_snd_get_link_node(rtd); -+ int ret; -+ -+ if (!link_np) -+ return -EINVAL; -+ -+ struct device_node *cpu_np __free(device_node) = -+ of_get_child_by_name(link_np, "cpu"); -+ struct device_node *codec_np __free(device_node) = -+ of_get_child_by_name(link_np, "codec"); -+ if (!cpu_np || !codec_np) -+ return -EINVAL; -+ -+ ret = qcom_snd_parse_tdm_slot(cpu_np, cpu_cfg); -+ if (ret) -+ return ret; -+ -+ return qcom_snd_parse_tdm_slot(codec_np, codec_cfg); -+} -+ -+int qcom_snd_get_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, -+ struct qcom_snd_tdm_slot_cfg *cpu_cfg, -+ struct qcom_snd_tdm_slot_cfg *codec_cfg) -+{ -+ int ret; -+ -+ ret = qcom_snd_parse_dai_tdm_slots(rtd, cpu_cfg, codec_cfg); -+ if (ret) -+ return ret; -+ -+ return qcom_snd_normalize_tdm_slots(cpu_cfg, codec_cfg); -+} -+EXPORT_SYMBOL_GPL(qcom_snd_get_dai_tdm_slots); -+ -+int qcom_snd_apply_dai_tdm_slots_cfg(struct snd_soc_pcm_runtime *rtd, -+ const struct qcom_snd_tdm_slot_cfg *cpu_cfg, -+ const struct qcom_snd_tdm_slot_cfg *codec_cfg) -+{ -+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); -+ struct snd_soc_dai *codec_dai; -+ int i; -+ int ret; -+ -+ if (!cpu_cfg->slots) -+ return 0; -+ -+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, cpu_cfg->tx_mask, cpu_cfg->rx_mask, -+ cpu_cfg->slots, cpu_cfg->slot_width); -+ if (ret) -+ return ret; -+ -+ for_each_rtd_codec_dais(rtd, i, codec_dai) { -+ ret = snd_soc_dai_set_tdm_slot(codec_dai, -+ codec_cfg->tx_mask, -+ codec_cfg->rx_mask, -+ codec_cfg->slots, -+ codec_cfg->slot_width); -+ if (ret) -+ return ret; -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(qcom_snd_apply_dai_tdm_slots_cfg); -+ -+int qcom_snd_apply_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd) -+{ -+ struct qcom_snd_tdm_slot_cfg cpu_cfg; -+ struct qcom_snd_tdm_slot_cfg codec_cfg; -+ int ret; -+ -+ ret = qcom_snd_get_dai_tdm_slots(rtd, &cpu_cfg, &codec_cfg); -+ if (ret) -+ return ret == -EINVAL ? 0 : ret; -+ -+ return qcom_snd_apply_dai_tdm_slots_cfg(rtd, &cpu_cfg, &codec_cfg); -+} -+EXPORT_SYMBOL_GPL(qcom_snd_apply_dai_tdm_slots); -+ - int qcom_snd_parse_of(struct snd_soc_card *card) - { - struct device *dev = card->dev; -@@ -91,7 +246,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) - - ret = of_property_read_string(np, "link-name", &link->name); - if (ret) { -- dev_err(card->dev, "error getting codec dai_link name\n"); -+ dev_err(dev, "error getting codec dai_link name\n"); - return ret; - } - -@@ -109,7 +264,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) - - ret = snd_soc_of_get_dlc(cpu, &args, link->cpus, 0); - if (ret) { -- dev_err_probe(card->dev, ret, -+ dev_err_probe(dev, ret, - "%s: error getting cpu dai name\n", link->name); - return ret; - } -@@ -126,7 +281,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) - "sound-dai", - 0); - if (!link->platforms->of_node) { -- dev_err(card->dev, "%s: platform dai not found\n", link->name); -+ dev_err(dev, "%s: platform dai not found\n", link->name); - return -EINVAL; - } - } else { -@@ -136,7 +291,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) - if (codec) { - ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); - if (ret < 0) { -- dev_err_probe(card->dev, ret, -+ dev_err_probe(dev, ret, - "%s: codec dai not found\n", link->name); - return ret; - } -@@ -215,6 +370,7 @@ int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd, - } - - switch (cpu_dai->id) { -+ case LPI_MI2S_RX_0: - case TX_CODEC_DMA_TX_0: - case TX_CODEC_DMA_TX_1: - case TX_CODEC_DMA_TX_2: -diff --git a/sound/soc/qcom/common.h b/sound/soc/qcom/common.h ---- a/sound/soc/qcom/common.h -+++ b/sound/soc/qcom/common.h -@@ -7,9 +7,23 @@ - #include - #include - --#define LPASS_MAX_PORT (SENARY_MI2S_TX + 1) -+#define LPASS_MAX_PORT (LPI_MI2S_TX_6 + 1) -+ -+struct qcom_snd_tdm_slot_cfg { -+ unsigned int tx_mask; -+ unsigned int rx_mask; -+ unsigned int slots; -+ unsigned int slot_width; -+}; - - int qcom_snd_parse_of(struct snd_soc_card *card); -+int qcom_snd_get_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, -+ struct qcom_snd_tdm_slot_cfg *cpu_cfg, -+ struct qcom_snd_tdm_slot_cfg *codec_cfg); -+int qcom_snd_apply_dai_tdm_slots_cfg(struct snd_soc_pcm_runtime *rtd, -+ const struct qcom_snd_tdm_slot_cfg *cpu_cfg, -+ const struct qcom_snd_tdm_slot_cfg *codec_cfg); -+int qcom_snd_apply_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd); - int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_jack *jack, bool *jack_setup); - int qcom_snd_dp_jack_setup(struct snd_soc_pcm_runtime *rtd, -diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c ---- a/sound/soc/qcom/qdsp6/audioreach.c -+++ b/sound/soc/qcom/qdsp6/audioreach.c -@@ -152,6 +152,13 @@ struct apm_i2s_module_intf_cfg { - - #define APM_I2S_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_i2s_module_intf_cfg), 8) - -+struct apm_audio_if_module_intf_cfg { -+ struct apm_module_param_data param_data; -+ struct param_id_audio_if_intf_cfg cfg; -+} __packed; -+ -+#define APM_AUDIO_IF_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_audio_if_module_intf_cfg), 8) -+ - struct apm_module_hw_ep_mf_cfg { - struct apm_module_param_data param_data; - struct param_id_hw_ep_mf mf; -@@ -168,6 +175,13 @@ struct apm_module_frame_size_factor_cfg { - - #define APM_FS_CFG_PSIZE ALIGN(sizeof(struct apm_module_frame_size_factor_cfg), 8) - -+struct apm_module_hw_ep_frame_duration_cfg { -+ struct apm_module_param_data param_data; -+ struct param_id_hw_ep_frame_duration frame_duration; -+} __packed; -+ -+#define APM_HW_EP_FRAME_DURATION_PSIZE ALIGN(sizeof(struct apm_module_hw_ep_frame_duration_cfg), 8) -+ - struct apm_module_hw_ep_power_mode_cfg { - struct apm_module_param_data param_data; - struct param_id_hw_ep_power_mode_cfg power_mode; -@@ -703,6 +717,7 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph, - int pm_sz = APM_HW_EP_PMODE_CFG_PSIZE; - int size = ic_sz + ep_sz + fs_sz + pm_sz; - void *p; -+ int i; - - struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0); - if (IS_ERR(pkt)) -@@ -741,7 +756,12 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph, - - intf_cfg->cfg.lpaif_type = module->hw_interface_type; - intf_cfg->cfg.intf_index = module->hw_interface_idx; -- intf_cfg->cfg.active_channels_mask = (1 << cfg->num_channels) - 1; -+ intf_cfg->cfg.active_channels_mask = 0; -+ /* Convert the physical channel mapping into a bit field */ -+ for (i = 0; i < AR_PCM_MAX_NUM_CHANNEL; i++) -+ if (cfg->channel_map[i]) -+ intf_cfg->cfg.active_channels_mask |= BIT(i); -+ - p += ic_sz; - - pm_cfg = p; -@@ -840,7 +860,7 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, - uint32_t num_channels = cfg->num_channels; - int payload_size = APM_MFC_CFG_PSIZE(media_format, num_channels) + - APM_MODULE_PARAM_DATA_SIZE; -- int i; -+ int i, j; - void *p; - - struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); -@@ -860,8 +880,12 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, - media_format->sample_rate = cfg->sample_rate; - media_format->bit_width = cfg->bit_width; - media_format->num_channels = cfg->num_channels; -- for (i = 0; i < num_channels; i++) -- media_format->channel_mapping[i] = cfg->channel_map[i]; -+ /* Convert the physical mapping to a logical mapping of the channels */ -+ for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < cfg->num_channels; i++) { -+ if (!cfg->channel_map[i]) -+ continue; -+ media_format->channel_mapping[j++] = cfg->channel_map[i]; -+ } - - return q6apm_send_cmd_sync(graph->apm, pkt, 0); - } -@@ -1042,6 +1066,81 @@ static int audioreach_i2s_set_media_format(struct q6apm_graph *graph, - return q6apm_send_cmd_sync(graph->apm, pkt, 0); - } - -+static int audioreach_audio_if_set_media_format(struct q6apm_graph *graph, -+ const struct audioreach_module *module, -+ const struct audioreach_module_config *cfg) -+{ -+ struct apm_module_hw_ep_frame_duration_cfg *fd_cfg; -+ struct apm_module_param_data *param_data; -+ struct apm_audio_if_module_intf_cfg *intf_cfg; -+ struct apm_module_hw_ep_mf_cfg *hw_cfg; -+ int ic_sz = APM_AUDIO_IF_INTF_CFG_PSIZE; -+ int ep_sz = APM_HW_EP_CFG_PSIZE; -+ int fd_sz = APM_HW_EP_FRAME_DURATION_PSIZE; -+ int size = ic_sz + ep_sz + fd_sz; -+ u32 slot_mask = cfg->slot_mask ? cfg->slot_mask : module->slot_mask; -+ u16 nslots_per_frame = cfg->nslots_per_frame ? -+ (u16)cfg->nslots_per_frame : module->nslots_per_frame; -+ u16 slot_width = cfg->slot_width ? (u16)cfg->slot_width : module->slot_width; -+ void *p; -+ -+ struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0); -+ if (IS_ERR(pkt)) -+ return PTR_ERR(pkt); -+ -+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; -+ intf_cfg = p; -+ -+ param_data = &intf_cfg->param_data; -+ param_data->module_instance_id = module->instance_id; -+ param_data->error_code = 0; -+ param_data->param_id = PARAM_ID_AUDIO_IF_INTF_CFG; -+ param_data->param_size = ic_sz - APM_MODULE_PARAM_DATA_SIZE; -+ intf_cfg->cfg.qaif_type = module->qaif_type; -+ intf_cfg->cfg.intf_idx = (u16)module->hw_interface_idx; -+ intf_cfg->cfg.intf_mode = module->intf_mode; -+ intf_cfg->cfg.ctrl_data_out_enable = module->ctrl_data_out_enable; -+ intf_cfg->cfg.active_slot_mask = slot_mask; -+ intf_cfg->cfg.nslots_per_frame = nslots_per_frame; -+ intf_cfg->cfg.slot_width = slot_width; -+ intf_cfg->cfg.active_lane_mask = module->active_lane_mask; -+ intf_cfg->cfg.frame_sync_rate = module->frame_sync_rate; -+ intf_cfg->cfg.frame_sync_src = module->sync_src; -+ intf_cfg->cfg.frame_sync_mode = module->sync_mode; -+ intf_cfg->cfg.invert_frame_sync_pulse = module->ctrl_invert_sync_pulse; -+ intf_cfg->cfg.frame_sync_data_delay = module->ctrl_sync_data_delay; -+ intf_cfg->cfg.bit_clk_type = module->bit_clk_type; -+ intf_cfg->cfg.inv_int_bit_clk = module->inv_int_bit_clk; -+ intf_cfg->cfg.inv_ext_bit_clk = module->inv_ext_bit_clk; -+ -+ p += ic_sz; -+ hw_cfg = p; -+ param_data = &hw_cfg->param_data; -+ param_data->module_instance_id = module->instance_id; -+ param_data->error_code = 0; -+ param_data->param_id = PARAM_ID_HW_EP_MF_CFG; -+ param_data->param_size = ep_sz - APM_MODULE_PARAM_DATA_SIZE; -+ -+ hw_cfg->mf.sample_rate = cfg->sample_rate; -+ hw_cfg->mf.bit_width = cfg->bit_width; -+ hw_cfg->mf.num_channels = cfg->num_channels; -+ hw_cfg->mf.data_format = module->data_format; -+ -+ p += ep_sz; -+ fd_cfg = p; -+ param_data = &fd_cfg->param_data; -+ param_data->module_instance_id = module->instance_id; -+ param_data->error_code = 0; -+ param_data->param_id = PARAM_ID_HW_EP_FRAME_DURATION; -+ param_data->param_size = fd_sz - APM_MODULE_PARAM_DATA_SIZE; -+ fd_cfg->frame_duration.frame_duration_in_us = AUDIO_IF_FRAME_DURATION_US; -+ fd_cfg->frame_duration.allow_frame_duration_normalization = AUDIO_IF_FRAME_DURATION_NORMALIZATION_ENABLE; -+ fd_cfg->frame_duration.min_normalized_frame_dur_us = AUDIO_IF_FRAME_DURATION_MIN_US; -+ fd_cfg->frame_duration.max_normalized_frame_dur_us = AUDIO_IF_FRAME_DURATION_MAX_US; -+ -+ return q6apm_send_cmd_sync(graph->apm, pkt, 0); -+} -+ - static int audioreach_logging_set_media_format(struct q6apm_graph *graph, - const struct audioreach_module *module) - { -@@ -1080,6 +1179,7 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph, - struct apm_pcm_module_media_fmt_cmd *cfg; - struct apm_module_param_data *param_data; - int payload_size; -+ int i, j; - - if (num_channels > 4) { - dev_err(graph->dev, "Error: Invalid channels (%d)!\n", num_channels); -@@ -1113,7 +1213,12 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph, - media_cfg->num_channels = mcfg->num_channels; - media_cfg->q_factor = mcfg->bit_width - 1; - media_cfg->bits_per_sample = mcfg->bit_width; -- memcpy(media_cfg->channel_mapping, mcfg->channel_map, mcfg->num_channels); -+ /* Convert the physical mapping to a logical mapping of the channels */ -+ for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < mcfg->num_channels; i++) { -+ if (!mcfg->channel_map[i]) -+ continue; -+ media_cfg->channel_mapping[j++] = mcfg->channel_map[i]; -+ } - - return q6apm_send_cmd_sync(graph->apm, pkt, 0); - } -@@ -1163,6 +1268,7 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph, - struct payload_media_fmt_pcm *cfg; - struct media_format *header; - int rc, payload_size; -+ int i, j; - void *p; - - if (num_channels > 4) { -@@ -1202,7 +1308,12 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph, - cfg->q_factor = mcfg->bit_width - 1; - cfg->endianness = PCM_LITTLE_ENDIAN; - cfg->num_channels = mcfg->num_channels; -- memcpy(cfg->channel_mapping, mcfg->channel_map, mcfg->num_channels); -+ /* Convert the physical mapping to a logical mapping of the channels */ -+ for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < cfg->num_channels; i++) { -+ if (!mcfg->channel_map[i]) -+ continue; -+ cfg->channel_mapping[j++] = mcfg->channel_map[i]; -+ } - } else { - rc = audioreach_set_compr_media_format(header, p, mcfg); - if (rc) -@@ -1279,7 +1390,7 @@ static int audioreach_speaker_protection_vi(struct q6apm_graph *graph, - struct apm_module_sp_vi_ex_mode_cfg *ex_cfg; - int op_sz, cm_sz, ex_sz; - struct apm_module_param_data *param_data; -- int rc, i, payload_size; -+ int rc, i, payload_size, j; - struct gpr_pkt *pkt; - void *p; - -@@ -1320,14 +1431,19 @@ static int audioreach_speaker_protection_vi(struct q6apm_graph *graph, - param_data->param_size = cm_sz - APM_MODULE_PARAM_DATA_SIZE; - - cm_cfg->cfg.num_channels = num_channels * 2; -- for (i = 0; i < num_channels; i++) { -+ /* Convert the physical mapping to a logical mapping of the channels */ -+ for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < num_channels; i++) { -+ if (!mcfg->channel_map[i]) -+ continue; - /* - * Map speakers into Vsense and then Isense of each channel. - * E.g. for PCM_CHANNEL_FL and PCM_CHANNEL_FR to: - * [1, 2, 3, 4] - */ -- cm_cfg->cfg.channel_mapping[2 * i] = (mcfg->channel_map[i] - 1) * 2 + 1; -- cm_cfg->cfg.channel_mapping[2 * i + 1] = (mcfg->channel_map[i] - 1) * 2 + 2; -+ cm_cfg->cfg.channel_mapping[2 * j] = (mcfg->channel_map[i] - 1) * 2 + 1; -+ cm_cfg->cfg.channel_mapping[2 * j + 1] = (mcfg->channel_map[i] - 1) * 2 + 2; -+ -+ ++j; - } - - p += cm_sz; -@@ -1411,6 +1527,10 @@ int audioreach_set_media_format(struct q6apm_graph *graph, - if (!rc) - rc = audioreach_module_enable(graph, module, true); - break; -+ case MODULE_ID_AUDIO_IF_SOURCE: -+ case MODULE_ID_AUDIO_IF_SINK: -+ rc = audioreach_audio_if_set_media_format(graph, module, cfg); -+ break; - - default: - rc = 0; -diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h ---- a/sound/soc/qcom/qdsp6/audioreach.h -+++ b/sound/soc/qcom/qdsp6/audioreach.h -@@ -36,6 +36,8 @@ struct q6apm_graph; - #define MODULE_ID_SPEAKER_PROTECTION 0x070010E2 - #define MODULE_ID_SPEAKER_PROTECTION_VI 0x070010E3 - #define MODULE_ID_OPUS_DEC 0x07001174 -+#define MODULE_ID_AUDIO_IF_SINK 0x0700117C -+#define MODULE_ID_AUDIO_IF_SOURCE 0x0700117D - - #define APM_CMD_GET_SPF_STATE 0x01001021 - #define APM_CMD_RSP_GET_SPF_STATE 0x02001007 -@@ -544,6 +546,74 @@ struct param_id_i2s_intf_cfg { - #define PORT_ID_I2S_OUPUT 1 - #define I2S_STACK_SIZE 2048 - -+#define PARAM_ID_AUDIO_IF_INTF_CFG 0x08001B11 -+ -+/* -+ * struct param_id_audio_if_intf_cfg - Audio interface configuration -+ * @qaif_type: Audio interface type (e.g. QAIF, QAIF_VA) -+ * @intf_idx: Interface instance index -+ * @intf_mode: Interface operating mode (TDM/PCM/I2S) -+ * @ctrl_data_out_enable: Enable sharing of data-out signal with other masters -+ * @active_slot_mask: Bitmask indicating active slots -+ * @nslots_per_frame: Number of slots per audio frame -+ * @slot_width: Width of each slot in bits -+ * @active_lane_mask: Bitmask of active data lanes -+ * @frame_sync_rate: Frame sync rate in Hz -+ * @frame_sync_src: Frame sync source selection -+ * @frame_sync_mode: Frame sync mode configuration -+ * @invert_frame_sync_pulse: Invert frame sync polarity when set -+ * @frame_sync_data_delay: Data delay from frame sync in bit clocks -+ * @bit_clk_type: Bit clock type (internal / external) -+ * @inv_int_bit_clk: Invert internal bit clock when set -+ * @inv_ext_bit_clk: Invert external bit clock when set -+ * -+ * This structure defines configuration parameters for the Qualcomm -+ * Audio Interface (QAIF) block. It is used to program interface -+ * characteristics such as slot configuration, clocking and frame -+ * synchronization behaviour. -+ */ -+struct param_id_audio_if_intf_cfg { -+ uint16_t qaif_type; -+ uint16_t intf_idx; -+ uint16_t intf_mode; -+ uint16_t ctrl_data_out_enable; -+ uint32_t active_slot_mask; -+ uint16_t nslots_per_frame; -+ uint16_t slot_width; -+ uint32_t active_lane_mask; -+ uint32_t frame_sync_rate; -+ uint16_t frame_sync_src; -+ uint16_t frame_sync_mode; -+ uint16_t invert_frame_sync_pulse; -+ uint16_t frame_sync_data_delay; -+ uint16_t bit_clk_type; -+ uint8_t inv_int_bit_clk; -+ uint8_t inv_ext_bit_clk; -+} __packed; -+ -+#define PARAM_ID_HW_EP_FRAME_DURATION 0x08001B2F -+#define AUDIO_IF_FRAME_DURATION_US 1000 -+#define AUDIO_IF_FRAME_DURATION_NORMALIZATION_ENABLE 1 -+#define AUDIO_IF_FRAME_DURATION_MIN_US 1 -+#define AUDIO_IF_FRAME_DURATION_MAX_US 100000 -+ -+/** -+ * struct param_id_hw_ep_frame_duration - Hardware endpoint frame duration -+ * @frame_duration_in_us: Frame duration in microseconds. -+ * @allow_frame_duration_normalization: Permit SPF to normalize frame duration. -+ * @min_normalized_frame_dur_us: Minimum normalized frame duration in microseconds. -+ * @max_normalized_frame_dur_us: Maximum normalized frame duration in microseconds. -+ * -+ * This structure configures the frame duration for the Audio IF hardware -+ * endpoint and, when enabled, the allowed normalization range. -+ */ -+struct param_id_hw_ep_frame_duration { -+ uint32_t frame_duration_in_us; -+ uint32_t allow_frame_duration_normalization; -+ uint32_t min_normalized_frame_dur_us; -+ uint32_t max_normalized_frame_dur_us; -+} __packed; -+ - #define PARAM_ID_DISPLAY_PORT_INTF_CFG 0x08001154 - - struct param_id_display_port_intf_cfg { -@@ -877,6 +947,23 @@ struct audioreach_module { - uint32_t data_format; - uint32_t hw_interface_type; - -+ /* Audio IF module (TDM/PCM/I2S) */ -+ u32 slot_mask; -+ u32 active_lane_mask; -+ u32 frame_sync_rate; -+ u16 qaif_type; -+ u16 sync_src; -+ u16 ctrl_data_out_enable; -+ u16 nslots_per_frame; -+ u16 slot_width; -+ u16 intf_mode; -+ u16 sync_mode; -+ u16 ctrl_invert_sync_pulse; -+ u16 ctrl_sync_data_delay; -+ u16 bit_clk_type; -+ u8 inv_int_bit_clk; -+ u8 inv_ext_bit_clk; -+ - /* PCM module specific */ - uint32_t interleave_type; - -@@ -907,6 +994,9 @@ struct audioreach_module_config { - u32 channel_allocation; - u32 sd_line_mask; - int fmt; -+ u32 slot_mask; -+ u16 nslots_per_frame; -+ u16 slot_width; - struct snd_codec codec; - u8 channel_map[AR_PCM_MAX_NUM_CHANNEL]; - }; -diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c ---- a/sound/soc/qcom/qdsp6/q6afe-dai.c -+++ b/sound/soc/qcom/qdsp6/q6afe-dai.c -@@ -412,6 +412,8 @@ static int q6afe_dai_prepare(struct snd_pcm_substream *substream, - case SENARY_MI2S_RX ... SENARY_MI2S_TX: - case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: - case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: -+ case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: -+ case LPI_MI2S_RX_5 ... LPI_MI2S_TX_6: - rc = q6afe_i2s_port_prepare(dai_data->port[dai->id], - &dai_data->port_config[dai->id].i2s_cfg); - if (rc < 0) { -@@ -665,6 +667,21 @@ static const struct snd_soc_dapm_route q6afe_dapm_routes[] = { - - /* USB playback AFE port receives data for playback, hence use the RX port */ - {"USB Playback", NULL, "USB_RX"}, -+ -+ {"LPI RX0 MI2S Playback", NULL, "LPI_MI2S_RX_0"}, -+ {"LPI_MI2S_TX_0", NULL, "LPI TX0 MI2S Capture"}, -+ {"LPI RX1 MI2S Playback", NULL, "LPI_MI2S_RX_1"}, -+ {"LPI_MI2S_TX_1", NULL, "LPI TX1 MI2S Capture"}, -+ {"LPI RX2 MI2S Playback", NULL, "LPI_MI2S_RX_2"}, -+ {"LPI_MI2S_TX_2", NULL, "LPI TX2 MI2S Capture"}, -+ {"LPI RX3 MI2S Playback", NULL, "LPI_MI2S_RX_3"}, -+ {"LPI_MI2S_TX_3", NULL, "LPI TX3 MI2S Capture"}, -+ {"LPI RX4 MI2S Playback", NULL, "LPI_MI2S_RX_4"}, -+ {"LPI_MI2S_TX_4", NULL, "LPI TX4 MI2S Capture"}, -+ {"LPI RX5 MI2S Playback", NULL, "LPI_MI2S_RX_5"}, -+ {"LPI_MI2S_TX_5", NULL, "LPI TX5 MI2S Capture"}, -+ {"LPI RX6 MI2S Playback", NULL, "LPI_MI2S_RX_6"}, -+ {"LPI_MI2S_TX_6", NULL, "LPI TX6 MI2S Capture"}, - }; - - static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai) -@@ -1011,6 +1028,35 @@ static const struct snd_soc_dapm_widget q6afe_dai_widgets[] = { - 0, SND_SOC_NOPM, 0, 0), - - SND_SOC_DAPM_AIF_IN("USB_RX", NULL, 0, SND_SOC_NOPM, 0, 0), -+ -+ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_0", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_0", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_1", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_1", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_2", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_2", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_3", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_3", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_4", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_4", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_5", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_5", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_6", NULL, -+ 0, SND_SOC_NOPM, 0, 0), -+ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_6", NULL, -+ 0, SND_SOC_NOPM, 0, 0), - }; - - static const struct snd_soc_component_driver q6afe_dai_component = { -@@ -1045,6 +1091,8 @@ static void of_q6afe_parse_dai_data(struct device *dev, - case SENARY_MI2S_RX ... SENARY_MI2S_TX: - case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: - case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: -+ case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: -+ case LPI_MI2S_RX_5 ... LPI_MI2S_TX_6: - priv = &data->priv[id]; - ret = of_property_read_variable_u32_array(node, - "qcom,sd-lines", -diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c ---- a/sound/soc/qcom/qdsp6/q6afe.c -+++ b/sound/soc/qcom/qdsp6/q6afe.c -@@ -132,6 +132,20 @@ - #define AFE_PORT_ID_QUINARY_MI2S_TX 0x1017 - #define AFE_PORT_ID_SENARY_MI2S_RX 0x1018 - #define AFE_PORT_ID_SENARY_MI2S_TX 0x1019 -+#define AFE_PORT_ID_INT0_MI2S_RX 0x102e -+#define AFE_PORT_ID_INT0_MI2S_TX 0x102f -+#define AFE_PORT_ID_INT1_MI2S_RX 0x1030 -+#define AFE_PORT_ID_INT1_MI2S_TX 0x1031 -+#define AFE_PORT_ID_INT2_MI2S_RX 0x1032 -+#define AFE_PORT_ID_INT2_MI2S_TX 0x1033 -+#define AFE_PORT_ID_INT3_MI2S_RX 0x1034 -+#define AFE_PORT_ID_INT3_MI2S_TX 0x1035 -+#define AFE_PORT_ID_INT4_MI2S_RX 0x1036 -+#define AFE_PORT_ID_INT4_MI2S_TX 0x1037 -+#define AFE_PORT_ID_INT5_MI2S_RX 0x1038 -+#define AFE_PORT_ID_INT5_MI2S_TX 0x1039 -+#define AFE_PORT_ID_INT6_MI2S_RX 0x103a -+#define AFE_PORT_ID_INT6_MI2S_TX 0x103b - - /* Start of the range of port IDs for TDM devices. */ - #define AFE_PORT_ID_TDM_PORT_RANGE_START 0x9000 -@@ -931,6 +945,34 @@ static struct afe_port_map port_maps[AFE_PORT_MAX] = { - [RX_CODEC_DMA_RX_7] = { AFE_PORT_ID_RX_CODEC_DMA_RX_7, - RX_CODEC_DMA_RX_7, 1, 1}, - [USB_RX] = { AFE_PORT_ID_USB_RX, USB_RX, 1, 1}, -+ [LPI_MI2S_RX_0] = { AFE_PORT_ID_INT0_MI2S_RX, -+ LPI_MI2S_RX_0, 1, 1}, -+ [LPI_MI2S_TX_0] = { AFE_PORT_ID_INT0_MI2S_TX, -+ LPI_MI2S_TX_0, 0, 1}, -+ [LPI_MI2S_RX_1] = { AFE_PORT_ID_INT1_MI2S_RX, -+ LPI_MI2S_RX_1, 1, 1}, -+ [LPI_MI2S_TX_1] = { AFE_PORT_ID_INT1_MI2S_TX, -+ LPI_MI2S_TX_1, 0, 1}, -+ [LPI_MI2S_RX_2] = { AFE_PORT_ID_INT2_MI2S_RX, -+ LPI_MI2S_RX_2, 1, 1}, -+ [LPI_MI2S_TX_2] = { AFE_PORT_ID_INT2_MI2S_TX, -+ LPI_MI2S_TX_2, 0, 1}, -+ [LPI_MI2S_RX_3] = { AFE_PORT_ID_INT3_MI2S_RX, -+ LPI_MI2S_RX_3, 1, 1}, -+ [LPI_MI2S_TX_3] = { AFE_PORT_ID_INT3_MI2S_TX, -+ LPI_MI2S_TX_3, 0, 1}, -+ [LPI_MI2S_RX_4] = { AFE_PORT_ID_INT4_MI2S_RX, -+ LPI_MI2S_RX_4, 1, 1}, -+ [LPI_MI2S_TX_4] = { AFE_PORT_ID_INT4_MI2S_TX, -+ LPI_MI2S_TX_4, 0, 1}, -+ [LPI_MI2S_RX_5] = { AFE_PORT_ID_INT5_MI2S_RX, -+ LPI_MI2S_RX_5, 1, 1}, -+ [LPI_MI2S_TX_5] = { AFE_PORT_ID_INT5_MI2S_TX, -+ LPI_MI2S_TX_5, 0, 1}, -+ [LPI_MI2S_RX_6] = { AFE_PORT_ID_INT6_MI2S_RX, -+ LPI_MI2S_RX_6, 1, 1}, -+ [LPI_MI2S_TX_6] = { AFE_PORT_ID_INT6_MI2S_TX, -+ LPI_MI2S_TX_6, 0, 1}, - }; - - static void q6afe_port_free(struct kref *ref) -@@ -1785,6 +1827,20 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id) - case AFE_PORT_ID_QUINARY_MI2S_TX: - case AFE_PORT_ID_SENARY_MI2S_RX: - case AFE_PORT_ID_SENARY_MI2S_TX: -+ case AFE_PORT_ID_INT0_MI2S_RX: -+ case AFE_PORT_ID_INT0_MI2S_TX: -+ case AFE_PORT_ID_INT1_MI2S_RX: -+ case AFE_PORT_ID_INT1_MI2S_TX: -+ case AFE_PORT_ID_INT2_MI2S_RX: -+ case AFE_PORT_ID_INT2_MI2S_TX: -+ case AFE_PORT_ID_INT3_MI2S_RX: -+ case AFE_PORT_ID_INT3_MI2S_TX: -+ case AFE_PORT_ID_INT4_MI2S_RX: -+ case AFE_PORT_ID_INT4_MI2S_TX: -+ case AFE_PORT_ID_INT5_MI2S_RX: -+ case AFE_PORT_ID_INT5_MI2S_TX: -+ case AFE_PORT_ID_INT6_MI2S_RX: -+ case AFE_PORT_ID_INT6_MI2S_TX: - cfg_type = AFE_PARAM_ID_I2S_CONFIG; - break; - case AFE_PORT_ID_PRIMARY_TDM_RX ... AFE_PORT_ID_QUINARY_TDM_TX_7: -diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c ---- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c -+++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c -@@ -2,10 +2,12 @@ - // Copyright (c) 2021, Linaro Limited - - #include -+#include - #include - #include - #include - #include -+#include - #include - #include - #include -@@ -15,15 +17,54 @@ - #include "q6dsp-common.h" - #include "audioreach.h" - #include "q6apm.h" -+#include "q6prm.h" - - #define AUDIOREACH_BE_PCM_BASE 16 - -+struct q6apm_dai_priv_data { -+ struct clk *mclk; -+ struct clk *bclk; -+ bool mclk_enabled, bclk_enabled; -+}; -+ - struct q6apm_lpass_dai_data { - struct q6apm_graph *graph[APM_PORT_MAX]; - bool is_port_started[APM_PORT_MAX]; - struct audioreach_module_config module_config[APM_PORT_MAX]; -+ struct q6apm_dai_priv_data priv[APM_PORT_MAX]; - }; - -+static void q6apm_lpass_dai_disable_clocks(struct q6apm_lpass_dai_data *dai_data, int id) -+{ -+ if (dai_data->priv[id].mclk_enabled) { -+ clk_disable_unprepare(dai_data->priv[id].mclk); -+ dai_data->priv[id].mclk_enabled = false; -+ } -+ -+ if (dai_data->priv[id].bclk_enabled) { -+ clk_disable_unprepare(dai_data->priv[id].bclk); -+ dai_data->priv[id].bclk_enabled = false; -+ } -+} -+ -+static void q6apm_lpass_dai_put_clocks(struct q6apm_lpass_dai_data *dai_data) -+{ -+ int i; -+ -+ for (i = 0; i < APM_PORT_MAX; i++) { -+ q6apm_lpass_dai_disable_clocks(dai_data, i); -+ -+ if (dai_data->priv[i].mclk) { -+ clk_put(dai_data->priv[i].mclk); -+ dai_data->priv[i].mclk = NULL; -+ } -+ if (dai_data->priv[i].bclk) { -+ clk_put(dai_data->priv[i].bclk); -+ dai_data->priv[i].bclk = NULL; -+ } -+ } -+} -+ - static int q6dma_set_channel_map(struct snd_soc_dai *dai, - unsigned int tx_num, - const unsigned int *tx_ch_mask, -@@ -251,6 +292,62 @@ static int q6apm_lpass_dai_startup(struct snd_pcm_substream *substream, struct s - return 0; - } - -+static int q6i2s_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) -+{ -+ return q6apm_lpass_dai_startup(substream, dai); -+} -+ -+static void q6i2s_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) -+{ -+ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); -+ -+ q6apm_lpass_dai_shutdown(substream, dai); -+ q6apm_lpass_dai_disable_clocks(dai_data, dai->id); -+} -+ -+static int q6i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) -+{ -+ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); -+ struct clk *sysclk = NULL; -+ bool *enabled = NULL; -+ int ret = 0; -+ -+ switch (clk_id) { -+ case LPAIF_MI2S_MCLK: -+ sysclk = dai_data->priv[dai->id].mclk; -+ enabled = &dai_data->priv[dai->id].mclk_enabled; -+ break; -+ case LPAIF_MI2S_BCLK: -+ sysclk = dai_data->priv[dai->id].bclk; -+ enabled = &dai_data->priv[dai->id].bclk_enabled; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ if (sysclk) { -+ ret = clk_set_rate(sysclk, freq); -+ if (ret) { -+ dev_err(dai->dev, "Error, Unable to set rate (%d) for sysclk %d\n", -+ freq, clk_id); -+ return ret; -+ } -+ -+ if (*enabled) -+ return 0; -+ -+ ret = clk_prepare_enable(sysclk); -+ if (ret) { -+ dev_err(dai->dev, "Error, Unable to prepare (%d) sysclk\n", clk_id); -+ return ret; -+ } -+ -+ *enabled = true; -+ } -+ -+ return ret; -+} -+ - static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) - { - struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); -@@ -261,6 +358,53 @@ static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) - return 0; - } - -+static int q6tdm_set_tdm_slot(struct snd_soc_dai *dai, -+ unsigned int tx_mask, -+ unsigned int rx_mask, -+ int slots, int slot_width) -+{ -+ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); -+ struct audioreach_module_config *cfg = &dai_data->module_config[dai->id]; -+ unsigned int cap_mask, slot_mask; -+ -+ if (slot_width != 16 && slot_width != 32) { -+ dev_err(dai->dev, "%s: invalid slot_width %d\n", __func__, slot_width); -+ return -EINVAL; -+ } -+ -+ switch (slots) { -+ case 2: -+ case 4: -+ case 8: -+ case 16: -+ cap_mask = GENMASK(slots - 1, 0); -+ break; -+ default: -+ dev_err(dai->dev, "%s: invalid slots %d\n", __func__, slots); -+ return -EINVAL; -+ } -+ -+ switch (dai->id) { -+ case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: -+ slot_mask = (dai->id & 0x1) ? tx_mask : rx_mask; -+ if (slot_mask & ~cap_mask) { -+ dev_err(dai->dev, "%s: invalid slot mask 0x%x for %d slots\n", -+ __func__, slot_mask, slots); -+ return -EINVAL; -+ } -+ -+ cfg->nslots_per_frame = slots; -+ cfg->slot_width = slot_width; -+ cfg->slot_mask = slot_mask; -+ break; -+ default: -+ dev_err(dai->dev, "%s: invalid dai id 0x%x\n", __func__, dai->id); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ - static const struct snd_soc_dai_ops q6dma_ops = { - .prepare = q6apm_lpass_dai_prepare, - .startup = q6apm_lpass_dai_startup, -@@ -272,11 +416,12 @@ static const struct snd_soc_dai_ops q6dma_ops = { - - static const struct snd_soc_dai_ops q6i2s_ops = { - .prepare = q6apm_lpass_dai_prepare, -- .startup = q6apm_lpass_dai_startup, -- .shutdown = q6apm_lpass_dai_shutdown, -+ .startup = q6i2s_dai_startup, -+ .shutdown = q6i2s_lpass_dai_shutdown, - .set_channel_map = q6dma_set_channel_map, - .hw_params = q6dma_hw_params, - .set_fmt = q6i2s_set_fmt, -+ .set_sysclk = q6i2s_set_sysclk, - .trigger = q6apm_lpass_dai_trigger, - }; - -@@ -289,6 +434,17 @@ static const struct snd_soc_dai_ops q6hdmi_ops = { - .trigger = q6apm_lpass_dai_trigger, - }; - -+static const struct snd_soc_dai_ops q6tdm_ops = { -+ .prepare = q6apm_lpass_dai_prepare, -+ .startup = q6apm_lpass_dai_startup, -+ .shutdown = q6i2s_lpass_dai_shutdown, -+ .set_tdm_slot = q6tdm_set_tdm_slot, -+ .hw_params = q6dma_hw_params, -+ .set_fmt = q6i2s_set_fmt, -+ .set_sysclk = q6i2s_set_sysclk, -+ .trigger = q6apm_lpass_dai_trigger, -+}; -+ - static const struct snd_soc_component_driver q6apm_lpass_dai_component = { - .name = "q6apm-be-dai-component", - .of_xlate_dai_name = q6dsp_audio_ports_of_xlate_dai_name, -@@ -297,6 +453,65 @@ static const struct snd_soc_component_driver q6apm_lpass_dai_component = { - .remove_order = SND_SOC_COMP_ORDER_FIRST, - }; - -+static int of_q6apm_parse_dai_data(struct device *dev, -+ struct q6apm_lpass_dai_data *data) -+{ -+ int ret; -+ -+ for_each_child_of_node_scoped(dev->of_node, node) { -+ struct q6apm_dai_priv_data *priv; -+ int id; -+ -+ ret = of_property_read_u32(node, "reg", &id); -+ if (ret || id < 0 || id >= APM_PORT_MAX) { -+ dev_err(dev, "valid dai id not found:%d\n", ret); -+ continue; -+ } -+ -+ switch (id) { -+ /* MI2S specific properties */ -+ case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: -+ case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: -+ case SENARY_MI2S_RX ... SENARY_MI2S_TX: -+ case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: -+ priv = &data->priv[id]; -+ priv->mclk = of_clk_get_by_name(node, "mclk"); -+ if (IS_ERR(priv->mclk)) { -+ int err = PTR_ERR(priv->mclk); -+ -+ priv->mclk = NULL; -+ if (err == -EPROBE_DEFER) { -+ q6apm_lpass_dai_put_clocks(data); -+ return dev_err_probe(dev, err, -+ "unable to get mi2s mclk\n"); -+ } -+ } -+ -+ priv->bclk = of_clk_get_by_name(node, "bclk"); -+ if (IS_ERR(priv->bclk)) { -+ int err = PTR_ERR(priv->bclk); -+ -+ priv->bclk = NULL; -+ if (err == -EPROBE_DEFER) { -+ q6apm_lpass_dai_put_clocks(data); -+ return dev_err_probe(dev, err, -+ "unable to get mi2s bclk\n"); -+ } -+ } -+ break; -+ default: -+ break; -+ } -+ } -+ -+ return 0; -+} -+ -+static void q6apm_lpass_dai_clocks_action(void *data) -+{ -+ q6apm_lpass_dai_put_clocks(data); -+} -+ - static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) - { - struct q6dsp_audio_port_dai_driver_config cfg; -@@ -304,17 +519,26 @@ static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) - struct snd_soc_dai_driver *dais; - struct device *dev = &pdev->dev; - int num_dais; -+ int ret; - - dai_data = devm_kzalloc(dev, sizeof(*dai_data), GFP_KERNEL); - if (!dai_data) - return -ENOMEM; - - dev_set_drvdata(dev, dai_data); -+ ret = of_q6apm_parse_dai_data(dev, dai_data); -+ if (ret) -+ return ret; -+ -+ ret = devm_add_action_or_reset(dev, q6apm_lpass_dai_clocks_action, dai_data); -+ if (ret) -+ return ret; - - memset(&cfg, 0, sizeof(cfg)); - cfg.q6i2s_ops = &q6i2s_ops; - cfg.q6dma_ops = &q6dma_ops; - cfg.q6hdmi_ops = &q6hdmi_ops; -+ cfg.q6tdm_ops = &q6tdm_ops; - dais = q6dsp_audio_ports_set_config(dev, &cfg, &num_dais); - - return devm_snd_soc_register_component(dev, &q6apm_lpass_dai_component, dais, num_dais); -diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c ---- a/sound/soc/qcom/qdsp6/q6apm.c -+++ b/sound/soc/qcom/qdsp6/q6apm.c -@@ -802,14 +802,17 @@ EXPORT_SYMBOL_GPL(q6apm_graph_prepare); - int q6apm_graph_start(struct q6apm_graph *graph) - { - struct audioreach_graph *ar_graph = graph->ar_graph; -- int ret = 0; -+ int ret; - -- if (ar_graph->start_count == 0) -+ if (ar_graph->start_count == 0) { - ret = audioreach_graph_mgmt_cmd(ar_graph, APM_CMD_GRAPH_START); -+ if (ret) -+ return ret; -+ } - - ar_graph->start_count++; - -- return ret; -+ return 0; - } - EXPORT_SYMBOL_GPL(q6apm_graph_start); - -@@ -817,6 +820,9 @@ int q6apm_graph_stop(struct q6apm_graph *graph) - { - struct audioreach_graph *ar_graph = graph->ar_graph; - -+ if (ar_graph->start_count == 0) -+ return 0; -+ - if (--ar_graph->start_count > 0) - return 0; - -diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c ---- a/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c -+++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c -@@ -12,7 +12,7 @@ - #include - #include "q6dsp-lpass-clocks.h" - --#define Q6DSP_MAX_CLK_ID 104 -+#define Q6DSP_MAX_CLK_ID 105 - #define Q6DSP_LPASS_CLK_ROOT_DEFAULT 0 - - -diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c ---- a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c -+++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c -@@ -553,11 +553,15 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { - Q6AFE_MI2S_RX_DAI("LPI RX2", LPI_MI2S_RX_2), - Q6AFE_MI2S_RX_DAI("LPI RX3", LPI_MI2S_RX_3), - Q6AFE_MI2S_RX_DAI("LPI RX4", LPI_MI2S_RX_4), -+ Q6AFE_MI2S_RX_DAI("LPI RX5", LPI_MI2S_RX_5), -+ Q6AFE_MI2S_RX_DAI("LPI RX6", LPI_MI2S_RX_6), - Q6AFE_MI2S_TX_DAI("LPI TX0", LPI_MI2S_TX_0), - Q6AFE_MI2S_TX_DAI("LPI TX1", LPI_MI2S_TX_1), - Q6AFE_MI2S_TX_DAI("LPI TX2", LPI_MI2S_TX_2), - Q6AFE_MI2S_TX_DAI("LPI TX3", LPI_MI2S_TX_3), - Q6AFE_MI2S_TX_DAI("LPI TX4", LPI_MI2S_TX_4), -+ Q6AFE_MI2S_TX_DAI("LPI TX5", LPI_MI2S_TX_5), -+ Q6AFE_MI2S_TX_DAI("LPI TX6", LPI_MI2S_TX_6), - Q6AFE_TDM_PB_DAI("Primary", 0, PRIMARY_TDM_RX_0), - Q6AFE_TDM_PB_DAI("Primary", 1, PRIMARY_TDM_RX_1), - Q6AFE_TDM_PB_DAI("Primary", 2, PRIMARY_TDM_RX_2), -@@ -712,6 +716,7 @@ struct snd_soc_dai_driver *q6dsp_audio_ports_set_config(struct device *dev, - case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: - case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: - case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: -+ case LPI_MI2S_RX_5 ... LPI_MI2S_TX_6: - q6dsp_audio_fe_dais[i].ops = cfg->q6i2s_ops; - break; - case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: -diff --git a/sound/soc/qcom/qdsp6/q6prm-clocks.c b/sound/soc/qcom/qdsp6/q6prm-clocks.c ---- a/sound/soc/qcom/qdsp6/q6prm-clocks.c -+++ b/sound/soc/qcom/qdsp6/q6prm-clocks.c -@@ -42,6 +42,11 @@ static const struct q6dsp_clk_init q6prm_clks[] = { - Q6PRM_CLK(LPASS_CLK_ID_INT5_MI2S_IBIT), - Q6PRM_CLK(LPASS_CLK_ID_INT6_MI2S_IBIT), - Q6PRM_CLK(LPASS_CLK_ID_QUI_MI2S_OSR), -+ Q6PRM_CLK(LPASS_CLK_ID_MCLK_1), -+ Q6PRM_CLK(LPASS_CLK_ID_MCLK_2), -+ Q6PRM_CLK(LPASS_CLK_ID_MCLK_3), -+ Q6PRM_CLK(LPASS_CLK_ID_MCLK_4), -+ Q6PRM_CLK(LPASS_CLK_ID_MCLK_5), - Q6PRM_CLK(LPASS_CLK_ID_WSA_CORE_MCLK), - Q6PRM_CLK(LPASS_CLK_ID_WSA_CORE_NPL_MCLK), - Q6PRM_CLK(LPASS_CLK_ID_VA_CORE_MCLK), -@@ -59,10 +64,40 @@ static const struct q6dsp_clk_init q6prm_clks[] = { - Q6PRM_CLK(LPASS_CLK_ID_WSA2_CORE_TX_MCLK), - Q6PRM_CLK(LPASS_CLK_ID_WSA2_CORE_TX_2X_MCLK), - Q6PRM_CLK(LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF0_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF0_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF1_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF1_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF2_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF2_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF3_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF3_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF4_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF4_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF5_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF5_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF6_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF6_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF7_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF7_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF8_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF8_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF9_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF9_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF10_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF10_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF11_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF11_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF12_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF12_EBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_VA_QAIF_IF0_IBIT), -+ Q6PRM_CLK(LAPSS_CLK_ID_VA_QAIF_IF0_EBIT), - Q6DSP_VOTE_CLK(LPASS_HW_MACRO_VOTE, Q6PRM_HW_CORE_ID_LPASS, - "LPASS_HW_MACRO"), - Q6DSP_VOTE_CLK(LPASS_HW_DCODEC_VOTE, Q6PRM_HW_CORE_ID_DCODEC, - "LPASS_HW_DCODEC"), -+ Q6DSP_VOTE_CLK(LPASS_HW_LPR_VOTE, Q6PRM_HW_LPR_VOTE, -+ "LPASS_HW_LPR_VOTE"), - }; - - static const struct q6dsp_clk_desc q6dsp_clk_q6prm __maybe_unused = { -diff --git a/sound/soc/qcom/qdsp6/q6prm.c b/sound/soc/qcom/qdsp6/q6prm.c ---- a/sound/soc/qcom/qdsp6/q6prm.c -+++ b/sound/soc/qcom/qdsp6/q6prm.c -@@ -31,10 +31,16 @@ struct q6prm { - #define PARAM_ID_RSC_HW_CORE 0x08001032 - #define PARAM_ID_RSC_LPASS_CORE 0x0800102B - #define PARAM_ID_RSC_AUDIO_HW_CLK 0x0800102C -+#define PARAM_ID_RSC_CPU_LPR 0x08001A6E -+ -+#define LPR_CPU_SS_SLEEP_DISABLE 0x1 - - struct prm_cmd_request_hw_core { - struct apm_module_param_data param_data; -- uint32_t hw_clk_id; -+ union { -+ u32 hw_clk_id; -+ u32 lpr_state; -+ }; - } __packed; - - struct prm_cmd_request_rsc { -@@ -62,6 +68,7 @@ static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool - struct prm_cmd_request_hw_core *req; - gpr_device_t *gdev = prm->gdev; - uint32_t opcode, rsp_opcode; -+ bool lpr_req = (hw_block_id == Q6PRM_HW_LPR_VOTE); - - if (enable) { - opcode = PRM_CMD_REQUEST_HW_RSC; -@@ -82,10 +89,13 @@ static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool - - param_data->module_instance_id = GPR_PRM_MODULE_IID; - param_data->error_code = 0; -- param_data->param_id = PARAM_ID_RSC_HW_CORE; -+ param_data->param_id = lpr_req ? PARAM_ID_RSC_CPU_LPR : PARAM_ID_RSC_HW_CORE; - param_data->param_size = sizeof(*req) - APM_MODULE_PARAM_DATA_SIZE; - -- req->hw_clk_id = hw_block_id; -+ if (lpr_req) -+ req->lpr_state = LPR_CPU_SS_SLEEP_DISABLE; -+ else -+ req->hw_clk_id = hw_block_id; - - return q6prm_send_cmd_sync(prm, pkt, rsp_opcode); - } -diff --git a/sound/soc/qcom/qdsp6/q6prm.h b/sound/soc/qcom/qdsp6/q6prm.h ---- a/sound/soc/qcom/qdsp6/q6prm.h -+++ b/sound/soc/qcom/qdsp6/q6prm.h -@@ -3,6 +3,9 @@ - #ifndef __Q6PRM_H__ - #define __Q6PRM_H__ - -+#define LPAIF_MI2S_MCLK 1 -+#define LPAIF_MI2S_BCLK 2 -+ - /* Clock ID for Primary I2S IBIT */ - #define Q6PRM_LPASS_CLK_ID_PRI_MI2S_IBIT 0x100 - /* Clock ID for Primary I2S EBIT */ -@@ -52,6 +55,17 @@ - /* Clock ID for QUINARY MI2S OSR CLK */ - #define Q6PRM_LPASS_CLK_ID_QUI_MI2S_OSR 0x116 - -+/* Clock ID for MCLK1 */ -+#define Q6PRM_LPASS_CLK_ID_MCLK_1 0x300 -+/* Clock ID for MCLK2 */ -+#define Q6PRM_LPASS_CLK_ID_MCLK_2 0x301 -+/* Clock ID for MCLK3 */ -+#define Q6PRM_LPASS_CLK_ID_MCLK_3 0x302 -+/* Clock ID for MCLK4 */ -+#define Q6PRM_LPASS_CLK_ID_MCLK_4 0x303 -+/* Clock ID for MCLK5 */ -+#define Q6PRM_LPASS_CLK_ID_MCLK_5 0x304 -+ - #define Q6PRM_LPASS_CLK_ID_WSA_CORE_MCLK 0x305 - #define Q6PRM_LPASS_CLK_ID_WSA_CORE_NPL_MCLK 0x306 - -@@ -83,10 +97,40 @@ - /* Clock ID for RX CORE MCLK2 2X MCLK */ - #define Q6PRM_LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK 0x318 - -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF0_IBIT 0x500 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF0_EBIT 0x501 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF1_IBIT 0x502 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF1_EBIT 0x503 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF2_IBIT 0x504 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF2_EBIT 0x505 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF3_IBIT 0x506 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF3_EBIT 0x507 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF4_IBIT 0x508 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF4_EBIT 0x509 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF5_IBIT 0x50A -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF5_EBIT 0x50B -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF6_IBIT 0x50C -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF6_EBIT 0x50D -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF7_IBIT 0x50E -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF7_EBIT 0x50F -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF8_IBIT 0x510 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF8_EBIT 0x511 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF9_IBIT 0x512 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF9_EBIT 0x513 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF10_IBIT 0x514 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF10_EBIT 0x515 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF11_IBIT 0x516 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF11_EBIT 0x517 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF12_IBIT 0x518 -+#define Q6PRM_LAPSS_CLK_ID_QAIF_IF12_EBIT 0x519 -+#define Q6PRM_LAPSS_CLK_ID_VA_QAIF_IF0_IBIT 0x550 -+#define Q6PRM_LAPSS_CLK_ID_VA_QAIF_IF0_EBIT 0x551 -+ - #define Q6PRM_LPASS_CLK_SRC_INTERNAL 1 - #define Q6PRM_LPASS_CLK_ROOT_DEFAULT 0 - #define Q6PRM_HW_CORE_ID_LPASS 1 - #define Q6PRM_HW_CORE_ID_DCODEC 2 -+#define Q6PRM_HW_LPR_VOTE 3 - - int q6prm_set_lpass_clock(struct device *dev, int clk_id, int clk_attr, - int clk_root, unsigned int freq); -diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c ---- a/sound/soc/qcom/qdsp6/q6routing.c -+++ b/sound/soc/qcom/qdsp6/q6routing.c -@@ -127,7 +127,14 @@ - { mix_name, "TX_CODEC_DMA_TX_2", "TX_CODEC_DMA_TX_2"}, \ - { mix_name, "TX_CODEC_DMA_TX_3", "TX_CODEC_DMA_TX_3"}, \ - { mix_name, "TX_CODEC_DMA_TX_4", "TX_CODEC_DMA_TX_4"}, \ -- { mix_name, "TX_CODEC_DMA_TX_5", "TX_CODEC_DMA_TX_5"} -+ { mix_name, "TX_CODEC_DMA_TX_5", "TX_CODEC_DMA_TX_5"}, \ -+ { mix_name, "LPI_MI2S_TX_0", "LPI_MI2S_TX_0" }, \ -+ { mix_name, "LPI_MI2S_TX_1", "LPI_MI2S_TX_1" }, \ -+ { mix_name, "LPI_MI2S_TX_2", "LPI_MI2S_TX_2" }, \ -+ { mix_name, "LPI_MI2S_TX_3", "LPI_MI2S_TX_3" }, \ -+ { mix_name, "LPI_MI2S_TX_4", "LPI_MI2S_TX_4" }, \ -+ { mix_name, "LPI_MI2S_TX_5", "LPI_MI2S_TX_5" }, \ -+ { mix_name, "LPI_MI2S_TX_6", "LPI_MI2S_TX_6" } - - #define Q6ROUTING_TX_MIXERS(id) \ - SOC_SINGLE_EXT("PRI_MI2S_TX", PRIMARY_MI2S_TX, \ -@@ -320,6 +327,27 @@ - id, 1, 0, msm_routing_get_audio_mixer, \ - msm_routing_put_audio_mixer), \ - SOC_SINGLE_EXT("TX_CODEC_DMA_TX_5", TX_CODEC_DMA_TX_5, \ -+ id, 1, 0, msm_routing_get_audio_mixer, \ -+ msm_routing_put_audio_mixer), \ -+ SOC_SINGLE_EXT("LPI_MI2S_TX_0", LPI_MI2S_TX_0, \ -+ id, 1, 0, msm_routing_get_audio_mixer, \ -+ msm_routing_put_audio_mixer), \ -+ SOC_SINGLE_EXT("LPI_MI2S_TX_1", LPI_MI2S_TX_1, \ -+ id, 1, 0, msm_routing_get_audio_mixer, \ -+ msm_routing_put_audio_mixer), \ -+ SOC_SINGLE_EXT("LPI_MI2S_TX_2", LPI_MI2S_TX_2, \ -+ id, 1, 0, msm_routing_get_audio_mixer, \ -+ msm_routing_put_audio_mixer), \ -+ SOC_SINGLE_EXT("LPI_MI2S_TX_3", LPI_MI2S_TX_3, \ -+ id, 1, 0, msm_routing_get_audio_mixer, \ -+ msm_routing_put_audio_mixer), \ -+ SOC_SINGLE_EXT("LPI_MI2S_TX_4", LPI_MI2S_TX_4, \ -+ id, 1, 0, msm_routing_get_audio_mixer, \ -+ msm_routing_put_audio_mixer), \ -+ SOC_SINGLE_EXT("LPI_MI2S_TX_5", LPI_MI2S_TX_5, \ -+ id, 1, 0, msm_routing_get_audio_mixer, \ -+ msm_routing_put_audio_mixer), \ -+ SOC_SINGLE_EXT("LPI_MI2S_TX_6", LPI_MI2S_TX_6, \ - id, 1, 0, msm_routing_get_audio_mixer, \ - msm_routing_put_audio_mixer), - -@@ -709,6 +737,26 @@ static const struct snd_kcontrol_new rxcodec_dma_rx_6_mixer_controls[] = { - static const struct snd_kcontrol_new rx_codec_dma_rx_7_mixer_controls[] = { - Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_7) }; - -+static const struct snd_kcontrol_new lpi_mi2s_rx_0_mixer_controls[] = { -+ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_0) }; -+ -+static const struct snd_kcontrol_new lpi_mi2s_rx_1_mixer_controls[] = { -+ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_1) }; -+ -+static const struct snd_kcontrol_new lpi_mi2s_rx_2_mixer_controls[] = { -+ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_2) }; -+ -+static const struct snd_kcontrol_new lpi_mi2s_rx_3_mixer_controls[] = { -+ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_3) }; -+ -+static const struct snd_kcontrol_new lpi_mi2s_rx_4_mixer_controls[] = { -+ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_4) }; -+ -+static const struct snd_kcontrol_new lpi_mi2s_rx_5_mixer_controls[] = { -+ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_5) }; -+ -+static const struct snd_kcontrol_new lpi_mi2s_rx_6_mixer_controls[] = { -+ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_6) }; - - static const struct snd_kcontrol_new mmul1_mixer_controls[] = { - Q6ROUTING_TX_MIXERS(MSM_FRONTEND_DAI_MULTIMEDIA1) }; -@@ -938,6 +986,27 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { - SND_SOC_DAPM_MIXER("USB_RX Audio Mixer", SND_SOC_NOPM, 0, 0, - usb_rx_mixer_controls, - ARRAY_SIZE(usb_rx_mixer_controls)), -+ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, -+ lpi_mi2s_rx_0_mixer_controls, -+ ARRAY_SIZE(lpi_mi2s_rx_0_mixer_controls)), -+ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, -+ lpi_mi2s_rx_1_mixer_controls, -+ ARRAY_SIZE(lpi_mi2s_rx_1_mixer_controls)), -+ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0, -+ lpi_mi2s_rx_2_mixer_controls, -+ ARRAY_SIZE(lpi_mi2s_rx_2_mixer_controls)), -+ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0, -+ lpi_mi2s_rx_3_mixer_controls, -+ ARRAY_SIZE(lpi_mi2s_rx_3_mixer_controls)), -+ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0, -+ lpi_mi2s_rx_4_mixer_controls, -+ ARRAY_SIZE(lpi_mi2s_rx_4_mixer_controls)), -+ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_5 Audio Mixer", SND_SOC_NOPM, 0, 0, -+ lpi_mi2s_rx_5_mixer_controls, -+ ARRAY_SIZE(lpi_mi2s_rx_5_mixer_controls)), -+ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_6 Audio Mixer", SND_SOC_NOPM, 0, 0, -+ lpi_mi2s_rx_6_mixer_controls, -+ ARRAY_SIZE(lpi_mi2s_rx_6_mixer_controls)), - SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0, - mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)), - SND_SOC_DAPM_MIXER("MultiMedia2 Mixer", SND_SOC_NOPM, 0, 0, -@@ -1031,6 +1100,13 @@ static const struct snd_soc_dapm_route intercon[] = { - Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_6 Audio Mixer", "RX_CODEC_DMA_RX_6"), - Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_7 Audio Mixer", "RX_CODEC_DMA_RX_7"), - Q6ROUTING_RX_DAPM_ROUTE("USB_RX Audio Mixer", "USB_RX"), -+ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_0 Audio Mixer", "LPI_MI2S_RX_0"), -+ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_1 Audio Mixer", "LPI_MI2S_RX_1"), -+ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_2 Audio Mixer", "LPI_MI2S_RX_2"), -+ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_3 Audio Mixer", "LPI_MI2S_RX_3"), -+ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_4 Audio Mixer", "LPI_MI2S_RX_4"), -+ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_5 Audio Mixer", "LPI_MI2S_RX_5"), -+ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_6 Audio Mixer", "LPI_MI2S_RX_6"), - Q6ROUTING_TX_DAPM_ROUTE("MultiMedia1 Mixer"), - Q6ROUTING_TX_DAPM_ROUTE("MultiMedia2 Mixer"), - Q6ROUTING_TX_DAPM_ROUTE("MultiMedia3 Mixer"), -diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c ---- a/sound/soc/qcom/qdsp6/topology.c -+++ b/sound/soc/qcom/qdsp6/topology.c -@@ -753,6 +753,103 @@ static int audioreach_widget_i2s_module_load(struct audioreach_module *mod, - return 0; - } - -+static int audioreach_widget_audio_if_module_load(struct audioreach_module *mod, -+ const struct snd_soc_tplg_vendor_array *mod_array) -+{ -+ const struct snd_soc_tplg_vendor_value_elem *mod_elem; -+ int tkn_count = 0; -+ u32 val; -+ -+ mod_elem = mod_array->value; -+ -+ while (tkn_count < le32_to_cpu(mod_array->num_elems)) { -+ val = le32_to_cpu(mod_elem->value); -+ switch (le32_to_cpu(mod_elem->token)) { -+ case AR_TKN_U32_MODULE_HW_IF_IDX: -+ mod->hw_interface_idx = val; -+ break; -+ case AR_TKN_U32_MODULE_FMT_DATA: -+ mod->data_format = val; -+ break; -+ case AR_TKN_U16_MODULE_SYNC_SRC: -+ if (val > U16_MAX) -+ return -EINVAL; -+ mod->sync_src = (u16)val; -+ break; -+ case AR_TKN_U16_MODULE_CTRL_DATA_OUT_ENABLE: -+ if (val > U16_MAX) -+ return -EINVAL; -+ mod->ctrl_data_out_enable = (u16)val; -+ break; -+ case AR_TKN_U32_MODULE_SLOT_MASK: -+ mod->slot_mask = val; -+ break; -+ case AR_TKN_U16_MODULE_NSLOTS_PER_FRAME: -+ if (val > U16_MAX) -+ return -EINVAL; -+ mod->nslots_per_frame = (u16)val; -+ break; -+ case AR_TKN_U16_MODULE_SLOT_WIDTH: -+ if (val > U16_MAX) -+ return -EINVAL; -+ mod->slot_width = (u16)val; -+ break; -+ case AR_TKN_U16_MODULE_INTF_MODE: -+ if (val > U16_MAX) -+ return -EINVAL; -+ mod->intf_mode = (u16)val; -+ break; -+ case AR_TKN_U16_MODULE_SYNC_MODE: -+ if (val > U16_MAX) -+ return -EINVAL; -+ mod->sync_mode = (u16)val; -+ break; -+ case AR_TKN_U16_MODULE_CTRL_INVERT_SYNC_PULSE: -+ if (val > U16_MAX) -+ return -EINVAL; -+ mod->ctrl_invert_sync_pulse = (u16)val; -+ break; -+ case AR_TKN_U16_MODULE_CTRL_SYNC_DATA_DELAY: -+ if (val > U16_MAX) -+ return -EINVAL; -+ mod->ctrl_sync_data_delay = (u16)val; -+ break; -+ case AR_TKN_U16_MODULE_QAIF_TYPE: -+ if (val > U16_MAX) -+ return -EINVAL; -+ mod->qaif_type = (u16)val; -+ break; -+ case AR_TKN_U32_MODULE_ACTIVE_LANE_MASK: -+ mod->active_lane_mask = val; -+ break; -+ case AR_TKN_U32_MODULE_FRAME_SYNC_RATE: -+ mod->frame_sync_rate = val; -+ break; -+ case AR_TKN_U16_MODULE_BIT_CLK_TYPE: -+ if (val > U16_MAX) -+ return -EINVAL; -+ mod->bit_clk_type = (u16)val; -+ break; -+ case AR_TKN_U8_MODULE_INV_INT_BIT_CLK: -+ if (val > U8_MAX) -+ return -EINVAL; -+ mod->inv_int_bit_clk = (u8)val; -+ break; -+ case AR_TKN_U8_MODULE_INV_EXT_BIT_CLK: -+ if (val > U8_MAX) -+ return -EINVAL; -+ mod->inv_ext_bit_clk = (u8)val; -+ break; -+ default: -+ break; -+ } -+ tkn_count++; -+ mod_elem++; -+ } -+ -+ return 0; -+} -+ - static int audioreach_widget_dp_module_load(struct audioreach_module *mod, - const struct snd_soc_tplg_vendor_array *mod_array) - { -@@ -806,6 +903,12 @@ static int audioreach_widget_load_buffer(struct snd_soc_component *component, - case MODULE_ID_I2S_SOURCE: - audioreach_widget_i2s_module_load(mod, mod_array); - break; -+ case MODULE_ID_AUDIO_IF_SINK: -+ case MODULE_ID_AUDIO_IF_SOURCE: -+ ret = audioreach_widget_audio_if_module_load(mod, mod_array); -+ if (ret) -+ return ret; -+ break; - case MODULE_ID_DISPLAY_PORT_SINK: - audioreach_widget_dp_module_load(mod, mod_array); - break; -@@ -1313,7 +1416,6 @@ int audioreach_tplg_init(struct snd_soc_component *component) - { - struct snd_soc_card *card = component->card; - struct device *dev = component->dev; -- const struct firmware *fw; - int ret; - - /* Inline with Qualcomm UCM configs and linux-firmware path */ -@@ -1323,6 +1425,7 @@ int audioreach_tplg_init(struct snd_soc_component *component) - if (!tplg_fw_name) - return -ENOMEM; - -+ const struct firmware *fw __free(firmware) = NULL; - ret = request_firmware(&fw, tplg_fw_name, dev); - if (ret < 0) { - dev_err(dev, "tplg firmware loading %s failed %d\n", tplg_fw_name, ret); -@@ -1335,8 +1438,6 @@ int audioreach_tplg_init(struct snd_soc_component *component) - dev_err(dev, "tplg component load failed: %d\n", ret); - } - -- release_firmware(fw); -- - return ret; - } - EXPORT_SYMBOL_GPL(audioreach_tplg_init); -diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c ---- a/sound/soc/qcom/sc8280xp.c -+++ b/sound/soc/qcom/sc8280xp.c -@@ -12,17 +12,157 @@ - #include - #include - #include "qdsp6/q6afe.h" -+#include "qdsp6/q6apm.h" -+#include "qdsp6/q6prm.h" -+#include "qdsp6/q6dsp-common.h" - #include "common.h" - #include "sdw.h" - -+#define I2S_MCLKFS 256 -+ -+#define I2S_MCLK_RATE(rate) \ -+ ((rate) * (I2S_MCLKFS)) -+#define I2S_BIT_RATE(rate, channels, format) \ -+ ((rate) * (channels) * (format)) -+ -+static struct snd_soc_dapm_widget sc8280xp_dapm_widgets[] = { -+ SND_SOC_DAPM_HP("Headphone Jack", NULL), -+ SND_SOC_DAPM_MIC("Mic Jack", NULL), -+ SND_SOC_DAPM_SPK("DP0 Jack", NULL), -+ SND_SOC_DAPM_SPK("DP1 Jack", NULL), -+ SND_SOC_DAPM_SPK("DP2 Jack", NULL), -+ SND_SOC_DAPM_SPK("DP3 Jack", NULL), -+ SND_SOC_DAPM_SPK("DP4 Jack", NULL), -+ SND_SOC_DAPM_SPK("DP5 Jack", NULL), -+ SND_SOC_DAPM_SPK("DP6 Jack", NULL), -+ SND_SOC_DAPM_SPK("DP7 Jack", NULL), -+}; -+ -+static const struct snd_kcontrol_new max98090_controls[] = { -+ SOC_DAPM_PIN_SWITCH("Headset Mic12"), -+ SOC_DAPM_PIN_SWITCH("Headphone"), -+ SOC_DAPM_PIN_SWITCH("Headset Mic56"), -+ SOC_DAPM_PIN_SWITCH("Speaker"), -+ SOC_DAPM_PIN_SWITCH("Receiver"), -+ SOC_DAPM_PIN_SWITCH("Int Mic"), -+}; -+ -+static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = { -+ SND_SOC_DAPM_HP("Headphone Jack", NULL), -+ SND_SOC_DAPM_MIC("Mic Jack", NULL), -+ SND_SOC_DAPM_HP("Headphone", NULL), -+ SND_SOC_DAPM_MIC("Headset Mic12", NULL), -+ SND_SOC_DAPM_MIC("Headset Mic56", NULL), -+ SND_SOC_DAPM_MIC("Int Mic", NULL), -+ SND_SOC_DAPM_SPK("Receiver", NULL), -+ SND_SOC_DAPM_SPK("Speaker", NULL), -+}; -+ -+struct qcom_snd_soc_common { -+ const char *driver_name; -+ const struct snd_soc_dapm_widget *dapm_widgets; -+ int num_dapm_widgets; -+ const struct snd_soc_dapm_route *dapm_routes; -+ int num_dapm_routes; -+ const struct snd_kcontrol_new *controls; -+ int num_controls; -+ unsigned int codec_dai_fmt; -+ bool codec_sysclk_set; -+ bool mi2s_mclk_enable; -+ bool mi2s_bclk_enable; -+ bool wcd_jack; -+ int (*snd_prepare)(struct snd_pcm_substream *substream); -+}; -+ - struct sc8280xp_snd_data { - bool stream_prepared[AFE_PORT_MAX]; - struct snd_soc_card *card; - struct snd_soc_jack jack; - struct snd_soc_jack dp_jack[8]; -+ const struct qcom_snd_soc_common *priv; - bool jack_setup; - }; - -+static inline int sc8280xp_get_mclk_freq(struct snd_pcm_hw_params *params) -+{ -+ int rate = params_rate(params); -+ -+ switch (rate) { -+ case 11025: -+ case 44100: -+ case 88200: -+ return I2S_MCLK_RATE(44100); -+ default: -+ break; -+ } -+ -+ return I2S_MCLK_RATE(rate); -+} -+ -+static inline int sc8280xp_get_bclk_freq(struct snd_pcm_hw_params *params) -+{ -+ return I2S_BIT_RATE(params_rate(params), -+ params_channels(params), -+ snd_pcm_format_width(params_format(params))); -+} -+ -+static int sc8280xp_tdm_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params) -+{ -+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); -+ struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); -+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); -+ struct snd_soc_dai *codec_dai; -+ struct qcom_snd_tdm_slot_cfg cpu_cfg; -+ struct qcom_snd_tdm_slot_cfg codec_cfg; -+ unsigned int bclk_freq; -+ int ret; -+ int i; -+ -+ ret = qcom_snd_get_dai_tdm_slots(rtd, &cpu_cfg, &codec_cfg); -+ if (ret) -+ return ret == -EINVAL ? 0 : ret; -+ -+ if (!cpu_cfg.slots) -+ return 0; -+ -+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); -+ if (ret) -+ return ret; -+ -+ ret = qcom_snd_apply_dai_tdm_slots_cfg(rtd, &cpu_cfg, &codec_cfg); -+ if (ret) -+ return ret; -+ -+ bclk_freq = snd_soc_tdm_params_to_bclk(params, cpu_cfg.slot_width, cpu_cfg.slots, 1); -+ if (!bclk_freq) -+ return -EINVAL; -+ -+ if (data->priv->mi2s_bclk_enable) { -+ ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_MI2S_BCLK, bclk_freq, -+ SND_SOC_CLOCK_IN); -+ if (ret) { -+ dev_err(rtd->dev, "%s: failed to set cpu sysclk: %d\n", -+ __func__, ret); -+ return ret; -+ } -+ } -+ -+ if (data->priv->codec_sysclk_set) { -+ for_each_rtd_codec_dais(rtd, i, codec_dai) { -+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, bclk_freq, -+ SND_SOC_CLOCK_IN); -+ if (ret) { -+ dev_err(rtd->dev, "%s: failed to set codec sysclk on %s: %d\n", -+ __func__, codec_dai->name, ret); -+ return ret; -+ } -+ } -+ } -+ -+ return 0; -+} -+ - static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) - { - struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); -@@ -32,10 +172,6 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) - int dp_pcm_id = 0; - - switch (cpu_dai->id) { -- case PRIMARY_MI2S_RX...QUATERNARY_MI2S_TX: -- case QUINARY_MI2S_RX...QUINARY_MI2S_TX: -- snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); -- break; - case WSA_CODEC_DMA_RX_0: - case WSA_CODEC_DMA_RX_1: - /* -@@ -64,7 +200,10 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) - if (dp_jack) - return qcom_snd_dp_jack_setup(rtd, dp_jack, dp_pcm_id); - -- return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); -+ if (data->priv->wcd_jack) -+ return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); -+ -+ return 0; - } - - static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, -@@ -96,12 +235,118 @@ static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, - return 0; - } - -+static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params) -+{ -+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); -+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); -+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); -+ struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); -+ int mclk_freq = sc8280xp_get_mclk_freq(params); -+ int bclk_freq = sc8280xp_get_bclk_freq(params); -+ int ret; -+ -+ switch (cpu_dai->id) { -+ case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: -+ case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: -+ case SENARY_MI2S_RX ... SENARY_MI2S_TX: -+ case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: -+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); -+ if (ret && ret != -ENOTSUPP) -+ return ret; -+ -+ if (data->priv->codec_dai_fmt) { -+ ret = snd_soc_dai_set_fmt(codec_dai, -+ data->priv->codec_dai_fmt); -+ if (ret && ret != -ENOTSUPP) -+ return ret; -+ } -+ -+ if (data->priv->mi2s_mclk_enable) { -+ ret = snd_soc_dai_set_sysclk(cpu_dai, -+ LPAIF_MI2S_MCLK, mclk_freq, -+ SND_SOC_CLOCK_OUT); -+ if (ret) -+ return ret; -+ } -+ -+ if (data->priv->mi2s_bclk_enable) { -+ ret = snd_soc_dai_set_sysclk(cpu_dai, -+ LPAIF_MI2S_BCLK, bclk_freq, -+ SND_SOC_CLOCK_OUT); -+ if (ret) -+ return ret; -+ } -+ -+ if (data->priv->codec_sysclk_set) { -+ ret = snd_soc_dai_set_sysclk(codec_dai, -+ 0, mclk_freq, -+ SND_SOC_CLOCK_IN); -+ if (ret && ret != -ENOTSUPP) -+ return ret; -+ } -+ break; -+ case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: -+ return sc8280xp_tdm_hw_params(substream, params); -+ default: -+ break; -+ } -+ -+ return 0; -+} -+ -+/* -+ * WSA and WSA2 are handled as a single interface with the -+ * following channels mask: -+ * __________________________________________________ -+ * | Bits | 3 | 2 | 1 | 0 | -+ * --------------------------------------------------- -+ * | Line | WSA2 Ch2 | WSA2 Ch1 | WSA Ch2 | WSA Ch1 | -+ * --------------------------------------------------- -+ * -+ * The Ayaneo Pocket S2 speakers are connected only to -+ * the WSA2 interface and the WSA interface is not enabled. -+ * -+ * Set the channel mapping on the WSA2 channels only. -+ */ -+static const unsigned int ayaneo_ps2_channels_mapping[] = { -+ 0, /* WSA Ch1 */ -+ 0, /* WSA Ch2 */ -+ PCM_CHANNEL_FL, /* WSA2 Ch1 */ -+ PCM_CHANNEL_FR /* WSA2 Ch2 */ -+}; -+ -+static int ayaneo_ps2_snd_prepare(struct snd_pcm_substream *substream) -+{ -+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); -+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); -+ unsigned int channels = substream->runtime->channels; -+ -+ if (cpu_dai->id != WSA_CODEC_DMA_RX_0) -+ return 0; -+ -+ if (channels != 2) -+ return -EINVAL; -+ -+ return snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, -+ ARRAY_SIZE(ayaneo_ps2_channels_mapping), -+ ayaneo_ps2_channels_mapping); -+} -+ - static int sc8280xp_snd_prepare(struct snd_pcm_substream *substream) - { - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); - struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); - -+ if (data->priv->snd_prepare) { -+ int ret; -+ -+ ret = data->priv->snd_prepare(substream); -+ if (ret) -+ return ret; -+ } -+ - return qcom_snd_sdw_prepare(substream, &data->stream_prepared[cpu_dai->id]); - } - -@@ -117,6 +362,7 @@ static int sc8280xp_snd_hw_free(struct snd_pcm_substream *substream) - static const struct snd_soc_ops sc8280xp_be_ops = { - .startup = qcom_snd_sdw_startup, - .shutdown = qcom_snd_sdw_shutdown, -+ .hw_params = sc8280xp_snd_hw_params, - .hw_free = sc8280xp_snd_hw_free, - .prepare = sc8280xp_snd_prepare, - }; -@@ -145,38 +391,182 @@ static int sc8280xp_platform_probe(struct platform_device *pdev) - card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); - if (!card) - return -ENOMEM; -- card->owner = THIS_MODULE; -+ - /* Allocate the private data */ - data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - -+ data->priv = of_device_get_match_data(dev); -+ if (!data->priv) -+ return -ENODEV; -+ -+ card->owner = THIS_MODULE; - card->dev = dev; - dev_set_drvdata(dev, card); - snd_soc_card_set_drvdata(card, data); -+ card->dapm_widgets = data->priv->dapm_widgets; -+ card->num_dapm_widgets = data->priv->num_dapm_widgets; -+ card->dapm_routes = data->priv->dapm_routes; -+ card->num_dapm_routes = data->priv->num_dapm_routes; -+ card->controls = data->priv->controls; -+ card->num_controls = data->priv->num_controls; -+ - ret = qcom_snd_parse_of(card); - if (ret) - return ret; - -- card->driver_name = of_device_get_match_data(dev); -+ card->driver_name = data->priv->driver_name; - sc8280xp_add_be_ops(card); - return devm_snd_soc_register_card(dev, card); - } - -+static struct qcom_snd_soc_common ayaneo_ps2_priv_data = { -+ .driver_name = "ayaneo-ps2", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .snd_prepare = ayaneo_ps2_snd_prepare, -+ .wcd_jack = true, -+}; -+ -+static const struct qcom_snd_soc_common eliza_priv_data = { -+ .driver_name = "eliza", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .wcd_jack = true, -+}; -+ -+static const struct qcom_snd_soc_common hawi_priv_data = { -+ .driver_name = "hawi", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .codec_sysclk_set = true, -+ .mi2s_bclk_enable = true, -+ .wcd_jack = true, -+}; -+ -+static const struct qcom_snd_soc_common kaanapali_priv_data = { -+ .driver_name = "kaanapali", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .wcd_jack = true, -+}; -+ -+static const struct qcom_snd_soc_common qcs9100_priv_data = { -+ .driver_name = "sa8775p", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+}; -+ -+static const struct qcom_snd_soc_common qcs615_priv_data = { -+ .driver_name = "qcs615", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .codec_sysclk_set = true, -+}; -+ -+static const struct qcom_snd_soc_common qcm6490_priv_data = { -+ .driver_name = "qcm6490", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .wcd_jack = true, -+}; -+ -+static const struct qcom_snd_soc_common qcs6490_priv_data = { -+ .driver_name = "qcs6490", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .wcd_jack = true, -+}; -+ -+static const struct qcom_snd_soc_common qcs8275_priv_data = { -+ .driver_name = "qcs8300", -+ .dapm_widgets = max98090_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(max98090_dapm_widgets), -+ .controls = max98090_controls, -+ .num_controls = ARRAY_SIZE(max98090_controls), -+ .codec_sysclk_set = true, -+ .codec_dai_fmt = SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_BC_FC, -+}; -+ -+static const struct qcom_snd_soc_common sc8280xp_priv_data = { -+ .driver_name = "sc8280xp", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .wcd_jack = true, -+}; -+ -+static const struct qcom_snd_soc_common sm8450_priv_data = { -+ .driver_name = "sm8450", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .wcd_jack = true, -+ /* I2S Connected to HDMI */ -+ .mi2s_mclk_enable = true, -+ .mi2s_bclk_enable = true, -+ .codec_dai_fmt = SND_SOC_DAIFMT_BC_FC | -+ SND_SOC_DAIFMT_NB_NF | -+ SND_SOC_DAIFMT_I2S, -+}; -+ -+static const struct qcom_snd_soc_common sm8475_priv_data = { -+ .driver_name = "sm8475", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .wcd_jack = true, -+}; -+ -+static const struct qcom_snd_soc_common sm8550_priv_data = { -+ .driver_name = "sm8550", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .wcd_jack = true, -+ /* I2S Connected to HDMI */ -+ .mi2s_mclk_enable = true, -+ .mi2s_bclk_enable = true, -+ .codec_dai_fmt = SND_SOC_DAIFMT_BC_FC | -+ SND_SOC_DAIFMT_NB_NF | -+ SND_SOC_DAIFMT_I2S, -+}; -+ -+static const struct qcom_snd_soc_common sm8650_priv_data = { -+ .driver_name = "sm8650", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .wcd_jack = true, -+ /* I2S Connected to HDMI */ -+ .mi2s_mclk_enable = true, -+ .mi2s_bclk_enable = true, -+ .codec_dai_fmt = SND_SOC_DAIFMT_BC_FC | -+ SND_SOC_DAIFMT_NB_NF | -+ SND_SOC_DAIFMT_I2S, -+}; -+ -+static const struct qcom_snd_soc_common sm8750_priv_data = { -+ .driver_name = "sm8750", -+ .dapm_widgets = sc8280xp_dapm_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), -+ .wcd_jack = true, -+}; -+ - static const struct of_device_id snd_sc8280xp_dt_match[] = { -- {.compatible = "qcom,eliza-sndcard", "eliza"}, -- {.compatible = "qcom,kaanapali-sndcard", "kaanapali"}, -- {.compatible = "qcom,qcm6490-idp-sndcard", "qcm6490"}, -- {.compatible = "qcom,qcs615-sndcard", "qcs615"}, -- {.compatible = "qcom,qcs6490-rb3gen2-sndcard", "qcs6490"}, -- {.compatible = "qcom,qcs8275-sndcard", "qcs8300"}, -- {.compatible = "qcom,qcs9075-sndcard", "sa8775p"}, -- {.compatible = "qcom,qcs9100-sndcard", "sa8775p"}, -- {.compatible = "qcom,sc8280xp-sndcard", "sc8280xp"}, -- {.compatible = "qcom,sm8450-sndcard", "sm8450"}, -- {.compatible = "qcom,sm8550-sndcard", "sm8550"}, -- {.compatible = "qcom,sm8650-sndcard", "sm8650"}, -- {.compatible = "qcom,sm8750-sndcard", "sm8750"}, -+ { .compatible = "ayaneo,pocket-s2-sndcard", .data = &ayaneo_ps2_priv_data }, -+ { .compatible = "qcom,eliza-sndcard", .data = &eliza_priv_data }, -+ { .compatible = "qcom,hawi-sndcard", .data = &hawi_priv_data }, -+ { .compatible = "qcom,kaanapali-sndcard", .data = &kaanapali_priv_data }, -+ { .compatible = "qcom,maili-sndcard", .data = &hawi_priv_data }, -+ { .compatible = "qcom,qcm6490-idp-sndcard", .data = &qcm6490_priv_data }, -+ { .compatible = "qcom,qcs615-sndcard", .data = &qcs615_priv_data }, -+ { .compatible = "qcom,qcs6490-rb3gen2-sndcard", .data = &qcs6490_priv_data }, -+ { .compatible = "qcom,qcs8275-sndcard", .data = &qcs8275_priv_data }, -+ { .compatible = "qcom,qcs9075-sndcard", .data = &qcs9100_priv_data }, -+ { .compatible = "qcom,qcs9100-sndcard", .data = &qcs9100_priv_data }, -+ { .compatible = "qcom,sc8280xp-sndcard", .data = &sc8280xp_priv_data }, -+ { .compatible = "qcom,sm8450-sndcard", .data = &sm8450_priv_data }, -+ { .compatible = "qcom,sm8475-sndcard", .data = &sm8475_priv_data }, -+ { .compatible = "qcom,sm8550-sndcard", .data = &sm8550_priv_data }, -+ { .compatible = "qcom,sm8650-sndcard", .data = &sm8650_priv_data }, -+ { .compatible = "qcom,sm8750-sndcard", .data = &sm8750_priv_data }, - {} - }; - -diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c ---- a/sound/soc/qcom/sdm845.c -+++ b/sound/soc/qcom/sdm845.c -@@ -36,7 +36,6 @@ struct sdm845_snd_data { - bool jack_setup; - bool slim_port_setup; - bool stream_prepared[AFE_PORT_MAX]; -- struct snd_soc_card *card; - uint32_t pri_mi2s_clk_count; - uint32_t sec_mi2s_clk_count; - uint32_t quat_tdm_clk_count; -@@ -564,7 +563,6 @@ static int sdm845_snd_platform_probe(struct platform_device *pdev) - if (ret) - return ret; - -- data->card = card; - snd_soc_card_set_drvdata(card, data); - - sdm845_add_ops(card); -diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c ---- a/sound/soc/qcom/sm8250.c -+++ b/sound/soc/qcom/sm8250.c -@@ -112,6 +112,22 @@ static int sm8250_snd_startup(struct snd_pcm_substream *substream) - snd_soc_dai_set_fmt(cpu_dai, fmt); - snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); - break; -+ case LPI_MI2S_RX_0: -+ codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S; -+ snd_soc_dai_set_sysclk(cpu_dai, -+ Q6AFE_LPASS_CLK_ID_INT0_MI2S_IBIT, -+ MI2S_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); -+ snd_soc_dai_set_fmt(cpu_dai, fmt); -+ snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); -+ break; -+ case LPI_MI2S_TX_3: -+ codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S; -+ snd_soc_dai_set_sysclk(cpu_dai, -+ Q6AFE_LPASS_CLK_ID_INT3_MI2S_IBIT, -+ MI2S_BCLK_RATE, SNDRV_PCM_STREAM_CAPTURE); -+ snd_soc_dai_set_fmt(cpu_dai, fmt); -+ snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); -+ break; - default: - break; - } -@@ -194,6 +210,7 @@ static const struct of_device_id snd_sm8250_dt_match[] = { - { .compatible = "qcom,qrb2210-sndcard", .data = "qcm2290" }, - { .compatible = "qcom,qrb4210-rb2-sndcard", .data = "sm4250" }, - { .compatible = "qcom,qrb5165-rb5-sndcard", .data = "sm8250" }, -+ { .compatible = "qcom,sdm660-sndcard", .data = "sdm660" }, - { .compatible = "qcom,sm8250-sndcard", .data = "sm8250" }, - {} - }; -diff --git a/sound/soc/qcom/storm.c b/sound/soc/qcom/storm.c ---- a/sound/soc/qcom/storm.c -+++ b/sound/soc/qcom/storm.c -@@ -64,21 +64,21 @@ static struct snd_soc_dai_link storm_dai_link = { - SND_SOC_DAILINK_REG(hifi), - }; - --static int storm_parse_of(struct snd_soc_card *card) -+static int storm_parse_of(struct device *dev) - { -- struct snd_soc_dai_link *dai_link = card->dai_link; -- struct device_node *np = card->dev->of_node; -+ struct snd_soc_dai_link *dai_link = &storm_dai_link; -+ struct device_node *np = dev->of_node; - - dai_link->cpus->of_node = of_parse_phandle(np, "cpu", 0); - if (!dai_link->cpus->of_node) { -- dev_err(card->dev, "error getting cpu phandle\n"); -+ dev_err(dev, "error getting cpu phandle\n"); - return -EINVAL; - } - dai_link->platforms->of_node = dai_link->cpus->of_node; - - dai_link->codecs->of_node = of_parse_phandle(np, "codec", 0); - if (!dai_link->codecs->of_node) { -- dev_err(card->dev, "error getting codec phandle\n"); -+ dev_err(dev, "error getting codec phandle\n"); - return -EINVAL; - } - -@@ -106,7 +106,7 @@ static int storm_platform_probe(struct platform_device *pdev) - card->dai_link = &storm_dai_link; - card->num_links = 1; - -- ret = storm_parse_of(card); -+ ret = storm_parse_of(&pdev->dev); - if (ret) { - dev_err(&pdev->dev, "error resolving dai links: %d\n", ret); - return ret; -diff --git a/sound/soc/renesas/fsi.c b/sound/soc/renesas/fsi.c ---- a/sound/soc/renesas/fsi.c -+++ b/sound/soc/renesas/fsi.c -@@ -1979,8 +1979,8 @@ static const struct of_device_id fsi_of_match[] = { - MODULE_DEVICE_TABLE(of, fsi_of_match); - - static const struct platform_device_id fsi_id_table[] = { -- { "sh_fsi", (kernel_ulong_t)&fsi1_core }, -- {}, -+ /* an array with no valid entry prevents matching on driver name */ -+ { } - }; - MODULE_DEVICE_TABLE(platform, fsi_id_table); - -@@ -1992,7 +1992,7 @@ static int fsi_probe(struct platform_device *pdev) - const struct fsi_core *core; - struct fsi_priv *fsi; - struct resource *res; -- unsigned int irq; -+ int irq; - int ret; - - memset(&info, 0, sizeof(info)); -@@ -2007,12 +2007,15 @@ static int fsi_probe(struct platform_device *pdev) - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- irq = platform_get_irq(pdev, 0); -- if (!res || (int)irq <= 0) { -+ if (!res) { - dev_err(&pdev->dev, "Not enough FSI platform resources.\n"); - return -ENODEV; - } - -+ irq = platform_get_irq(pdev, 0); -+ if (irq < 0) -+ return irq; -+ - master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL); - if (!master) - return -ENOMEM; -diff --git a/sound/soc/renesas/rcar/adg.c b/sound/soc/renesas/rcar/adg.c ---- a/sound/soc/renesas/rcar/adg.c -+++ b/sound/soc/renesas/rcar/adg.c -@@ -565,7 +565,7 @@ static struct clk *rsnd_adg_create_null_clk(struct rsnd_priv *priv, - struct clk *clk; - - clk = clk_register_fixed_rate(dev, name, parent, 0, 0); -- if (IS_ERR_OR_NULL(clk)) { -+ if (IS_ERR(clk)) { - dev_err(dev, "create null clk error\n"); - return ERR_CAST(clk); - } -@@ -618,7 +618,7 @@ static int rsnd_adg_get_clkin(struct rsnd_priv *priv) - * No "adg" is not error - */ - clk = devm_clk_get(dev, "adg"); -- if (IS_ERR_OR_NULL(clk)) -+ if (IS_ERR(clk)) - clk = rsnd_adg_null_clk_get(priv); - adg->adg = clk; - -@@ -626,9 +626,9 @@ static int rsnd_adg_get_clkin(struct rsnd_priv *priv) - for (i = 0; i < clkin_size; i++) { - clk = devm_clk_get(dev, clkin_name[i]); - -- if (IS_ERR_OR_NULL(clk)) -+ if (IS_ERR(clk)) - clk = rsnd_adg_null_clk_get(priv); -- if (IS_ERR_OR_NULL(clk)) -+ if (IS_ERR(clk)) - goto err; - - adg->clkin[i] = clk; -diff --git a/sound/soc/renesas/siu_dai.c b/sound/soc/renesas/siu_dai.c ---- a/sound/soc/renesas/siu_dai.c -+++ b/sound/soc/renesas/siu_dai.c -@@ -715,7 +715,6 @@ static struct snd_soc_dai_driver siu_i2s_dai = { - - static int siu_probe(struct platform_device *pdev) - { -- const struct firmware *fw_entry; - struct resource *res, *region; - struct siu_info *info; - int ret; -@@ -726,6 +725,7 @@ static int siu_probe(struct platform_device *pdev) - siu_i2s_data = info; - info->dev = &pdev->dev; - -+ const struct firmware *fw_entry __free(firmware) = NULL; - ret = request_firmware(&fw_entry, "siu_spb.bin", &pdev->dev); - if (ret) - return ret; -@@ -736,8 +736,6 @@ static int siu_probe(struct platform_device *pdev) - */ - memcpy(&info->fw, fw_entry->data, fw_entry->size); - -- release_firmware(fw_entry); -- - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; -diff --git a/sound/soc/rockchip/rk3288_hdmi_analog.c b/sound/soc/rockchip/rk3288_hdmi_analog.c ---- a/sound/soc/rockchip/rk3288_hdmi_analog.c -+++ b/sound/soc/rockchip/rk3288_hdmi_analog.c -@@ -185,10 +185,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) - gpiod_set_consumer_name(machine->gpio_hp_en, "hp_en"); - - ret = snd_soc_of_parse_card_name(card, "rockchip,model"); -- if (ret) { -- dev_err(card->dev, "SoC parse card name failed %d\n", ret); -+ if (ret) - return ret; -- } - - rk_dailink.codecs[0].of_node = of_parse_phandle(np, - "rockchip,audio-codec", -@@ -207,10 +205,9 @@ static int snd_rk_mc_probe(struct platform_device *pdev) - } - - ret = snd_soc_get_dai_name(&args, &rk_dailink.codecs[0].dai_name); -- if (ret) { -- dev_err(&pdev->dev, "Unable to get codec_dai_name\n"); -- return ret; -- } -+ if (ret) -+ return dev_err_probe(&pdev->dev, ret, -+ "Unable to get codec_dai_name\n"); - - rk_dailink.cpus->of_node = of_parse_phandle(np, "rockchip,i2s-controller", - 0); -@@ -223,11 +220,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) - rk_dailink.platforms->of_node = rk_dailink.cpus->of_node; - - ret = snd_soc_of_parse_audio_routing(card, "rockchip,routing"); -- if (ret) { -- dev_err(&pdev->dev, -- "Unable to parse 'rockchip,routing' property\n"); -+ if (ret) - return ret; -- } - - snd_soc_card_set_drvdata(card, machine); - -diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c ---- a/sound/soc/rockchip/rockchip_i2s.c -+++ b/sound/soc/rockchip/rockchip_i2s.c -@@ -767,16 +767,14 @@ static int rockchip_i2s_probe(struct platform_device *pdev) - - /* try to prepare related clocks */ - i2s->hclk = devm_clk_get_enabled(&pdev->dev, "i2s_hclk"); -- if (IS_ERR(i2s->hclk)) { -- dev_err(&pdev->dev, "Can't retrieve i2s bus clock\n"); -- return PTR_ERR(i2s->hclk); -- } -+ if (IS_ERR(i2s->hclk)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->hclk), -+ "Can't retrieve i2s bus clock\n"); - - i2s->mclk = devm_clk_get(&pdev->dev, "i2s_clk"); -- if (IS_ERR(i2s->mclk)) { -- dev_err(&pdev->dev, "Can't retrieve i2s master clock\n"); -- return PTR_ERR(i2s->mclk); -- } -+ if (IS_ERR(i2s->mclk)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->mclk), -+ "Can't retrieve i2s master clock\n"); - - regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(regs)) -@@ -792,7 +790,11 @@ static int rockchip_i2s_probe(struct platform_device *pdev) - - i2s->bclk_ratio = 64; - i2s->pinctrl = devm_pinctrl_get(&pdev->dev); -- if (!IS_ERR(i2s->pinctrl)) { -+ if (IS_ERR(i2s->pinctrl)) { -+ if (PTR_ERR(i2s->pinctrl) == -EPROBE_DEFER) -+ return -EPROBE_DEFER; -+ dev_dbg(&pdev->dev, "failed to find i2s pinctrl\n"); -+ } else { - i2s->bclk_on = pinctrl_lookup_state(i2s->pinctrl, "bclk_on"); - if (!IS_ERR_OR_NULL(i2s->bclk_on)) { - i2s->bclk_off = pinctrl_lookup_state(i2s->pinctrl, "bclk_off"); -@@ -801,8 +803,6 @@ static int rockchip_i2s_probe(struct platform_device *pdev) - return -EINVAL; - } - } -- } else { -- dev_dbg(&pdev->dev, "failed to find i2s pinctrl\n"); - } - - i2s_pinctrl_select_bclk_off(i2s); -@@ -831,16 +831,12 @@ static int rockchip_i2s_probe(struct platform_device *pdev) - &rockchip_i2s_component, - dai, 1); - -- if (ret) { -- dev_err(&pdev->dev, "Could not register DAI\n"); -+ if (ret) - return ret; -- } - - ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); -- if (ret) { -- dev_err(&pdev->dev, "Could not register PCM\n"); -+ if (ret) - return ret; -- } - - return 0; - } -diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c ---- a/sound/soc/rockchip/rockchip_i2s_tdm.c -+++ b/sound/soc/rockchip/rockchip_i2s_tdm.c -@@ -1263,16 +1263,14 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) - i2s_tdm->tx_reset = devm_reset_control_get_optional_exclusive(&pdev->dev, - "tx-m"); - if (IS_ERR(i2s_tdm->tx_reset)) { -- ret = PTR_ERR(i2s_tdm->tx_reset); -- return dev_err_probe(i2s_tdm->dev, ret, -+ return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->tx_reset), - "Error in tx-m reset control\n"); - } - - i2s_tdm->rx_reset = devm_reset_control_get_optional_exclusive(&pdev->dev, - "rx-m"); - if (IS_ERR(i2s_tdm->rx_reset)) { -- ret = PTR_ERR(i2s_tdm->rx_reset); -- return dev_err_probe(i2s_tdm->dev, ret, -+ return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->rx_reset), - "Error in rx-m reset control\n"); - } - -@@ -1363,17 +1361,12 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) - ret = devm_snd_soc_register_component(&pdev->dev, - &rockchip_i2s_tdm_component, - i2s_tdm->dai, 1); -- -- if (ret) { -- dev_err(&pdev->dev, "Could not register DAI\n"); -+ if (ret) - goto err_suspend; -- } - - ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); -- if (ret) { -- dev_err(&pdev->dev, "Could not register PCM\n"); -+ if (ret) - goto err_suspend; -- } - - return 0; - -diff --git a/sound/soc/rockchip/rockchip_max98090.c b/sound/soc/rockchip/rockchip_max98090.c ---- a/sound/soc/rockchip/rockchip_max98090.c -+++ b/sound/soc/rockchip/rockchip_max98090.c -@@ -428,11 +428,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) - - /* Parse card name. */ - ret = snd_soc_of_parse_card_name(card, "rockchip,model"); -- if (ret) { -- dev_err(&pdev->dev, -- "Soc parse card name failed %d\n", ret); -+ if (ret) - return ret; -- } - - /* register the soc card */ - ret = devm_snd_soc_register_card(&pdev->dev, card); -diff --git a/sound/soc/rockchip/rockchip_pdm.c b/sound/soc/rockchip/rockchip_pdm.c ---- a/sound/soc/rockchip/rockchip_pdm.c -+++ b/sound/soc/rockchip/rockchip_pdm.c -@@ -630,10 +630,8 @@ static int rockchip_pdm_probe(struct platform_device *pdev) - &rockchip_pdm_component, - &rockchip_pdm_dai, 1); - -- if (ret) { -- dev_err(&pdev->dev, "could not register dai: %d\n", ret); -+ if (ret) - goto err_suspend; -- } - - rockchip_pdm_rxctrl(pdm, 0); - -@@ -642,10 +640,8 @@ static int rockchip_pdm_probe(struct platform_device *pdev) - goto err_suspend; - - ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); -- if (ret) { -- dev_err(&pdev->dev, "could not register pcm: %d\n", ret); -+ if (ret) - goto err_suspend; -- } - - return 0; - -diff --git a/sound/soc/rockchip/rockchip_rt5645.c b/sound/soc/rockchip/rockchip_rt5645.c ---- a/sound/soc/rockchip/rockchip_rt5645.c -+++ b/sound/soc/rockchip/rockchip_rt5645.c -@@ -191,11 +191,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) - rk_dailink.platforms->of_node = rk_dailink.cpus->of_node; - - ret = snd_soc_of_parse_card_name(card, "rockchip,model"); -- if (ret) { -- dev_err(&pdev->dev, -- "Soc parse card name failed %d\n", ret); -+ if (ret) - goto put_cpu_of_node; -- } - - ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) { -diff --git a/sound/soc/rockchip/rockchip_sai.c b/sound/soc/rockchip/rockchip_sai.c ---- a/sound/soc/rockchip/rockchip_sai.c -+++ b/sound/soc/rockchip/rockchip_sai.c -@@ -1428,12 +1428,13 @@ static int rockchip_sai_probe(struct platform_device *pdev) - "Failed to initialize regmap\n"); - - irq = platform_get_irq_optional(pdev, 0); -+ if (irq == -EPROBE_DEFER) -+ return irq; - if (irq > 0) { - ret = devm_request_irq(&pdev->dev, irq, rockchip_sai_isr, - IRQF_SHARED, node->name, sai); - if (ret) -- return dev_err_probe(&pdev->dev, ret, -- "Failed to request irq %d\n", irq); -+ return ret; - } else { - dev_dbg(&pdev->dev, "Asked for an IRQ but got %d\n", irq); - } -@@ -1456,7 +1457,7 @@ static int rockchip_sai_probe(struct platform_device *pdev) - - ret = rockchip_sai_parse_paths(sai, node); - if (ret) -- return dev_err_probe(&pdev->dev, ret, "Failed to parse paths\n"); -+ return ret; - - /* - * From here on, all register accesses need to be wrapped in -@@ -1471,18 +1472,14 @@ static int rockchip_sai_probe(struct platform_device *pdev) - return dev_err_probe(&pdev->dev, ret, "Failed to resume device\n"); - - ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); -- if (ret) { -- dev_err(&pdev->dev, "Failed to register PCM: %d\n", ret); -+ if (ret) - goto err_runtime_suspend; -- } - - ret = devm_snd_soc_register_component(&pdev->dev, - &rockchip_sai_component, - dai, 1); -- if (ret) { -- dev_err(&pdev->dev, "Failed to register component: %d\n", ret); -+ if (ret) - goto err_runtime_suspend; -- } - - pm_runtime_use_autosuspend(&pdev->dev); - pm_runtime_put(&pdev->dev); -diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c ---- a/sound/soc/rockchip/rockchip_spdif.c -+++ b/sound/soc/rockchip/rockchip_spdif.c -@@ -396,13 +396,13 @@ static int rk_spdif_probe(struct platform_device *pdev) - - ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); - if (ret) -- return dev_err_probe(&pdev->dev, ret, "Could not register PCM\n"); -+ return ret; - - ret = devm_snd_soc_register_component(&pdev->dev, - &rk_spdif_component, - &rk_spdif_dai, 1); - if (ret) -- return dev_err_probe(&pdev->dev, ret, "Could not register DAI\n"); -+ return ret; - - return 0; - } -diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c ---- a/sound/soc/samsung/i2s.c -+++ b/sound/soc/samsung/i2s.c -@@ -8,6 +8,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -512,7 +513,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, - u32 mod, mask, val = 0; - int ret = 0; - -- pm_runtime_get_sync(dai->dev); -+ guard(pm_runtime_active)(dai->dev); - - scoped_guard(spinlock_irqsave, &priv->lock) - mod = readl(priv->addr + I2SMOD); -@@ -537,8 +538,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, - && (mod & cdcon_mask))))) { - dev_err(&i2s->pdev->dev, - "%s:%d Other DAI busy\n", __func__, __LINE__); -- ret = -EAGAIN; -- goto err; -+ return -EAGAIN; - } - - if (dir == SND_SOC_CLOCK_IN) -@@ -566,7 +566,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, - } else { - priv->rclk_srcrate = - clk_get_rate(priv->op_clk); -- goto done; -+ return 0; - } - } - -@@ -580,14 +580,14 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, - if (WARN_ON(IS_ERR(priv->op_clk))) { - ret = PTR_ERR(priv->op_clk); - priv->op_clk = NULL; -- goto err; -+ return ret; - } - - ret = clk_prepare_enable(priv->op_clk); - if (ret) { - clk_put(priv->op_clk); - priv->op_clk = NULL; -- goto err; -+ return ret; - } - priv->rclk_srcrate = clk_get_rate(priv->op_clk); - -@@ -595,11 +595,10 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, - || (clk_id && !(mod & rsrc_mask))) { - dev_err(&i2s->pdev->dev, - "%s:%d Other DAI busy\n", __func__, __LINE__); -- ret = -EAGAIN; -- goto err; -+ return -EAGAIN; - } else { - /* Call can't be on the active DAI */ -- goto done; -+ return 0; - } - - if (clk_id == 1) -@@ -607,8 +606,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, - break; - default: - dev_err(&i2s->pdev->dev, "We don't serve that!\n"); -- ret = -EINVAL; -- goto err; -+ return -EINVAL; - } - - scoped_guard(spinlock_irqsave, &priv->lock) { -@@ -616,13 +614,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, - mod = (mod & ~mask) | val; - writel(mod, priv->addr + I2SMOD); - } --done: -- pm_runtime_put(dai->dev); - - return 0; --err: -- pm_runtime_put(dai->dev); -- return ret; - } - - static int i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) -@@ -1123,15 +1116,31 @@ static const struct snd_soc_dapm_widget samsung_i2s_widgets[] = { - - static const struct snd_soc_dapm_route samsung_i2s_dapm_routes[] = { - { "Playback Mixer", NULL, "Primary Playback" }, -- { "Playback Mixer", NULL, "Secondary Playback" }, -- - { "Mixer DAI TX", NULL, "Playback Mixer" }, - { "Primary Capture", NULL, "Mixer DAI RX" }, - }; - -+static const struct snd_soc_dapm_route samsung_i2s_dapm_routes_sec_play[] = { -+ { "Playback Mixer", NULL, "Secondary Playback" }, -+}; -+ -+static int samsung_i2s_component_probe(struct snd_soc_component *component) -+{ -+ struct samsung_i2s_priv *priv = snd_soc_component_get_drvdata(component); -+ -+ if (priv->quirks & QUIRK_SEC_DAI) -+ snd_soc_dapm_add_routes(snd_soc_component_to_dapm(component), -+ samsung_i2s_dapm_routes_sec_play, -+ ARRAY_SIZE(samsung_i2s_dapm_routes_sec_play)); -+ -+ return 0; -+} -+ - static const struct snd_soc_component_driver samsung_i2s_component = { - .name = "samsung-i2s", - -+ .probe = samsung_i2s_component_probe, -+ - .dapm_widgets = samsung_i2s_widgets, - .num_dapm_widgets = ARRAY_SIZE(samsung_i2s_widgets), - -@@ -1448,10 +1457,10 @@ static int samsung_i2s_probe(struct platform_device *pdev) - regs_base = res->start; - - priv->clk = devm_clk_get(&pdev->dev, "iis"); -- if (IS_ERR(priv->clk)) { -- dev_err(&pdev->dev, "Failed to get iis clock\n"); -- return PTR_ERR(priv->clk); -- } -+ if (IS_ERR(priv->clk)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk), -+ "Failed to get iis clock\n"); -+ - - ret = clk_prepare_enable(priv->clk); - if (ret != 0) { -@@ -1657,8 +1666,7 @@ static const struct samsung_i2s_dai_data i2sv6_dai_type __maybe_unused = { - }; - - static const struct samsung_i2s_dai_data i2sv7_dai_type __maybe_unused = { -- .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | -- QUIRK_SUPPORTS_TDM, -+ .quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR | QUIRK_SUPPORTS_TDM, - .pcm_rates = SNDRV_PCM_RATE_8000_192000, - .i2s_variant_regs = &i2sv7_regs, - }; -@@ -1682,7 +1690,7 @@ static const struct platform_device_id samsung_i2s_driver_ids[] = { - .name = "samsung-i2s", - .driver_data = (kernel_ulong_t)&i2sv3_dai_type, - }, -- {}, -+ { } - }; - MODULE_DEVICE_TABLE(platform, samsung_i2s_driver_ids); - -diff --git a/sound/soc/samsung/smdk_spdif.c b/sound/soc/samsung/smdk_spdif.c ---- a/sound/soc/samsung/smdk_spdif.c -+++ b/sound/soc/samsung/smdk_spdif.c -@@ -130,12 +130,8 @@ static int smdk_hw_params(struct snd_pcm_substream *substream, - return ret; - - /* Set S/PDIF uses internal source clock */ -- ret = snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK, -- rclk_rate, SND_SOC_CLOCK_IN); -- if (ret < 0) -- return ret; -- -- return ret; -+ return snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK, -+ rclk_rate, SND_SOC_CLOCK_IN); - } - - static const struct snd_soc_ops smdk_spdif_ops = { -diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c ---- a/sound/soc/samsung/spdif.c -+++ b/sound/soc/samsung/spdif.c -@@ -380,8 +380,8 @@ static int spdif_probe(struct platform_device *pdev) - - spdif->pclk = devm_clk_get(&pdev->dev, "spdif"); - if (IS_ERR(spdif->pclk)) { -- dev_err(&pdev->dev, "failed to get peri-clock\n"); -- ret = -ENOENT; -+ ret = dev_err_probe(&pdev->dev, PTR_ERR(spdif->pclk), -+ "failed to get peri-clock\n"); - goto err0; - } - ret = clk_prepare_enable(spdif->pclk); -@@ -390,8 +390,8 @@ static int spdif_probe(struct platform_device *pdev) - - spdif->sclk = devm_clk_get(&pdev->dev, "sclk_spdif"); - if (IS_ERR(spdif->sclk)) { -- dev_err(&pdev->dev, "failed to get internal source clock\n"); -- ret = -ENOENT; -+ ret = dev_err_probe(&pdev->dev, PTR_ERR(spdif->sclk), -+ "failed to get internal source clock\n"); - goto err1; - } - ret = clk_prepare_enable(spdif->sclk); -diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c ---- a/sound/soc/sdca/sdca_asoc.c -+++ b/sound/soc/sdca/sdca_asoc.c -@@ -364,7 +364,6 @@ static int entity_parse_ot(struct device *dev, - - /** - * sdca_asoc_pde_poll_actual_ps - Verify PDE power state reached target state -- * @dev: Pointer to the device for error logging. - * @regmap: Register map for reading ACTUAL_PS register. - * @function_id: SDCA function identifier. - * @entity_id: SDCA entity identifier for the power domain. -@@ -389,11 +388,11 @@ static int entity_parse_ot(struct device *dev, - * polling times out before reaching the target state, or a negative error code if - * a register read fails. - */ --int sdca_asoc_pde_poll_actual_ps(struct device *dev, struct regmap *regmap, -+int sdca_asoc_pde_poll_actual_ps(struct regmap *regmap, - int function_id, int entity_id, -- int from_ps, int to_ps, -- const struct sdca_pde_delay *pde_delays, -- int num_delays) -+ int from_ps, int to_ps, -+ const struct sdca_pde_delay *pde_delays, -+ int num_delays) - { - static const int polls = 100; - static const int default_poll_us = 1000; -@@ -451,14 +450,14 @@ static int entity_pde_event(struct snd_soc_dapm_widget *widget, - return 0; - } - -- ret = sdca_asoc_pde_poll_actual_ps(component->dev, component->regmap, -+ ret = sdca_asoc_pde_poll_actual_ps(component->regmap, - SDW_SDCA_CTL_FUNC(widget->reg), - SDW_SDCA_CTL_ENT(widget->reg), - from, to, - entity->pde.max_delay, - entity->pde.num_max_delay); - if (ret) -- dev_err(component->dev, "%s: PDE transition %x -> %x failed, err=%d\n", -+ dev_err(component->dev, "%s: pde transition %x -> %x failed: %d\n", - entity->label, from, to, ret); - - return ret; -@@ -1231,7 +1230,7 @@ static u64 width_find_mask(unsigned int bits) - } - } - --static int populate_rate_format(struct device *dev, -+int sdca_asoc_populate_rate_format(struct device *dev, - struct sdca_function_data *function, - struct sdca_entity *entity, - struct snd_soc_pcm_stream *stream) -@@ -1292,6 +1291,7 @@ static int populate_rate_format(struct device *dev, - - return 0; - } -+EXPORT_SYMBOL_NS(sdca_asoc_populate_rate_format, "SND_SOC_SDCA"); - - /** - * sdca_asoc_populate_dais - fill in an array of DAI drivers for a Function -@@ -1344,7 +1344,7 @@ int sdca_asoc_populate_dais(struct device *dev, struct sdca_function_data *funct - stream->channels_min = 1; - stream->channels_max = SDCA_MAX_CHANNEL_COUNT; - -- ret = populate_rate_format(dev, function, entity, stream); -+ ret = sdca_asoc_populate_rate_format(dev, function, entity, stream); - if (ret) - return ret; - -diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c ---- a/sound/soc/sdca/sdca_class_function.c -+++ b/sound/soc/sdca/sdca_class_function.c -@@ -329,7 +329,7 @@ static int class_function_probe(struct auxiliary_device *auxdev, - drv->core = core; - drv->function = &sdev->function; - -- ret = sdca_parse_function(dev, core->sdw, drv->function); -+ ret = sdca_parse_function(dev, drv->function); - if (ret) - return ret; - -diff --git a/sound/soc/sdca/sdca_fdl.c b/sound/soc/sdca/sdca_fdl.c ---- a/sound/soc/sdca/sdca_fdl.c -+++ b/sound/soc/sdca/sdca_fdl.c -@@ -195,7 +195,6 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, - { - struct device *dev = interrupt->dev; - struct sdca_fdl_data *fdl_data = &interrupt->function->fdl_data; -- const struct firmware *firmware = NULL; - struct acpi_sw_file *swf = NULL, *tmp; - struct sdca_fdl_file *fdl_file; - char *disk_filename; -@@ -230,6 +229,7 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, - - dev_dbg(dev, "FDL disk filename: %s\n", disk_filename); - -+ const struct firmware *firmware __free(firmware) = NULL; - ret = firmware_request_nowarn(&firmware, disk_filename, dev); - kfree(disk_filename); - if (ret) { -@@ -258,8 +258,7 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, - - if (!swf) { - dev_err(dev, "failed to locate SWF\n"); -- ret = -ENOENT; -- goto error; -+ return -ENOENT; - } - - dev_info(dev, "loading SWF: %x-%x-%x\n", -@@ -271,9 +270,6 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, - SDCA_CTL_XU_FDL_MESSAGEOFFSET, fdl_file->fdl_offset, - SDCA_CTL_XU_FDL_MESSAGELENGTH, swf->data, - swf->file_length - offsetof(struct acpi_sw_file, data)); -- --error: -- release_firmware(firmware); - return ret; - } - -diff --git a/sound/soc/sdca/sdca_function_device.c b/sound/soc/sdca/sdca_function_device.c ---- a/sound/soc/sdca/sdca_function_device.c -+++ b/sound/soc/sdca/sdca_function_device.c -@@ -32,7 +32,8 @@ static void sdca_dev_release(struct device *dev) - - /* alloc, init and add link devices */ - static struct sdca_dev *sdca_dev_register(struct device *parent, -- struct sdca_function_desc *function_desc) -+ struct sdca_function_desc *function_desc, -+ struct acpi_table_swft *swft) - { - struct sdca_dev *sdev; - struct auxiliary_device *auxdev; -@@ -50,6 +51,7 @@ static struct sdca_dev *sdca_dev_register(struct device *parent, - auxdev->dev.release = sdca_dev_release; - - sdev->function.desc = function_desc; -+ sdev->function.fdl_data.swft = swft; - - rc = ida_alloc(&sdca_function_ida, GFP_KERNEL); - if (rc < 0) { -@@ -99,7 +101,8 @@ int sdca_dev_register_functions(struct sdw_slave *slave) - struct sdca_dev *func_dev; - - func_dev = sdca_dev_register(&slave->dev, -- &sdca_data->function[i]); -+ &sdca_data->function[i], -+ sdca_data->swft); - if (IS_ERR(func_dev)) { - ret = PTR_ERR(func_dev); - /* -diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c ---- a/sound/soc/sdca/sdca_functions.c -+++ b/sound/soc/sdca/sdca_functions.c -@@ -7,6 +7,7 @@ - */ - - #include -+#include - #include - #include - #include -@@ -18,7 +19,6 @@ - #include - #include - #include --#include - - /* - * Should be long enough to encompass all the MIPI DisCo properties. -@@ -1365,90 +1365,76 @@ static int find_sdca_entity_ge(struct device *dev, - return -EINVAL; - } - --static int --find_sdca_entity_hide(struct device *dev, struct sdw_slave *sdw, -- struct fwnode_handle *function_node, -- struct fwnode_handle *entity_node, struct sdca_entity *entity) -+static int find_sdca_entity_hide(struct device *dev, -+ struct fwnode_handle *entity_node, -+ struct sdca_entity *entity) - { - struct sdca_entity_hide *hide = &entity->hide; -- unsigned int delay, *af_list = hide->af_number_list; -- int nval, ret; -- unsigned char *report_desc = NULL; -+ int num_reports, ret; -+ unsigned int delay; - - ret = fwnode_property_read_u32(entity_node, -- "mipi-sdca-RxUMP-ownership-transition-max-delay", &delay); -+ "mipi-sdca-RxUMP-ownership-transition-max-delay", -+ &delay); - if (!ret) - hide->max_delay = delay; - -- nval = fwnode_property_count_u32(entity_node, "mipi-sdca-HIDTx-supported-report-ids"); -- if (nval > 0) { -- hide->num_hidtx_ids = nval; -+ num_reports = fwnode_property_count_u32(entity_node, -+ "mipi-sdca-HIDTx-supported-report-ids"); -+ if (num_reports < 0 && num_reports != -EINVAL) { -+ dev_err(dev, "%pfwP: failed to read hid tx ids: %d\n", -+ entity_node, num_reports); -+ return num_reports; -+ } else if (num_reports > 0) { -+ hide->num_hidtx_ids = num_reports; - hide->hidtx_ids = devm_kcalloc(dev, hide->num_hidtx_ids, - sizeof(*hide->hidtx_ids), GFP_KERNEL); - if (!hide->hidtx_ids) - return -ENOMEM; - -- ret = fwnode_property_read_u32_array(entity_node, -- "mipi-sdca-HIDTx-supported-report-ids", -- hide->hidtx_ids, -- hide->num_hidtx_ids); -- if (ret < 0) -- return ret; -+ fwnode_property_read_u32_array(entity_node, -+ "mipi-sdca-HIDTx-supported-report-ids", -+ hide->hidtx_ids, hide->num_hidtx_ids); - } - -- nval = fwnode_property_count_u32(entity_node, "mipi-sdca-HIDRx-supported-report-ids"); -- if (nval > 0) { -- hide->num_hidrx_ids = nval; -+ num_reports = fwnode_property_count_u32(entity_node, -+ "mipi-sdca-HIDRx-supported-report-ids"); -+ if (num_reports < 0 && num_reports != -EINVAL) { -+ dev_err(dev, "%pfwP: failed to read hid rx ids: %d\n", -+ entity_node, num_reports); -+ return num_reports; -+ } else if (num_reports > 0) { -+ hide->num_hidrx_ids = num_reports; - hide->hidrx_ids = devm_kcalloc(dev, hide->num_hidrx_ids, - sizeof(*hide->hidrx_ids), GFP_KERNEL); - if (!hide->hidrx_ids) - return -ENOMEM; - -- ret = fwnode_property_read_u32_array(entity_node, -- "mipi-sdca-HIDRx-supported-report-ids", -- hide->hidrx_ids, -- hide->num_hidrx_ids); -- if (ret < 0) -- return ret; -+ fwnode_property_read_u32_array(entity_node, -+ "mipi-sdca-HIDRx-supported-report-ids", -+ hide->hidrx_ids, hide->num_hidrx_ids); - } - -- nval = fwnode_property_count_u32(entity_node, "mipi-sdca-hide-related-audio-function-list"); -- if (nval <= 0) { -+ /* -+ * FIXME: This should probably link to the actual sdca_function_data pointer, -+ * but updating to do so should probably wait until we have a user. -+ */ -+ num_reports = fwnode_property_count_u32(entity_node, -+ "mipi-sdca-hide-related-audio-function-list"); -+ if (num_reports <= 0) { - dev_err(dev, "%pfwP: audio function numbers list missing: %d\n", -- entity_node, nval); -+ entity_node, num_reports); - return -EINVAL; -- } else if (nval > SDCA_MAX_FUNCTION_COUNT) { -- dev_err(dev, "%pfwP: maximum number of audio function exceeded\n", entity_node); -+ } else if (num_reports > ARRAY_SIZE(hide->af_number_list)) { -+ dev_err(dev, "%pfwP: maximum number of audio function exceeded\n", -+ entity_node); - return -EINVAL; - } - -- hide->hide_reside_function_num = nval; -+ hide->hide_reside_function_num = num_reports; - fwnode_property_read_u32_array(entity_node, -- "mipi-sdca-hide-related-audio-function-list", af_list, nval); -- -- nval = fwnode_property_count_u8(function_node, "mipi-sdca-hid-descriptor"); -- if (nval) -- fwnode_property_read_u8_array(function_node, "mipi-sdca-hid-descriptor", -- (u8 *)&hide->hid_desc, nval); -- -- if (hide->hid_desc.bNumDescriptors) { -- nval = fwnode_property_count_u8(function_node, "mipi-sdca-report-descriptor"); -- if (nval) { -- report_desc = devm_kzalloc(dev, nval, GFP_KERNEL); -- if (!report_desc) -- return -ENOMEM; -- hide->hid_report_desc = report_desc; -- fwnode_property_read_u8_array(function_node, "mipi-sdca-report-descriptor", -- report_desc, nval); -- -- /* add HID device */ -- ret = sdca_add_hid_device(dev, sdw, entity); -- if (ret) { -- dev_err(dev, "%pfwP: failed to add HID device: %d\n", entity_node, ret); -- return ret; -- } -- } -- } -+ "mipi-sdca-hide-related-audio-function-list", -+ hide->af_number_list, num_reports); - - return 0; - } -@@ -1475,8 +1461,7 @@ static int find_sdca_entity_xu(struct device *dev, - return 0; - } - --static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw, -- struct sdca_function_data *function, -+static int find_sdca_entity(struct device *dev, struct sdca_function_data *function, - struct fwnode_handle *function_node, - struct fwnode_handle *entity_node, - struct sdca_entity *entity) -@@ -1528,8 +1513,7 @@ static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw, - ret = find_sdca_entity_ge(dev, entity_node, entity); - break; - case SDCA_ENTITY_TYPE_HIDE: -- ret = find_sdca_entity_hide(dev, sdw, function_node, -- entity_node, entity); -+ ret = find_sdca_entity_hide(dev, entity_node, entity); - break; - default: - break; -@@ -1544,8 +1528,7 @@ static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw, - return 0; - } - --static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw, -- struct fwnode_handle *function_node, -+static int find_sdca_entities(struct device *dev, struct fwnode_handle *function_node, - struct sdca_function_data *function) - { - struct sdca_entity *entities; -@@ -1596,7 +1579,7 @@ static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw, - return -EINVAL; - } - -- ret = find_sdca_entity(dev, sdw, function, function_node, -+ ret = find_sdca_entity(dev, function, function_node, - entity_node, &entities[i]); - fwnode_handle_put(entity_node); - if (ret) -@@ -1619,7 +1602,7 @@ static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw, - return 0; - } - --static struct sdca_entity *find_sdca_entity_by_label(struct sdca_function_data *function, -+struct sdca_entity *sdca_find_entity_by_label(struct sdca_function_data *function, - const char *entity_label) - { - struct sdca_entity *entity = NULL; -@@ -1648,6 +1631,7 @@ static struct sdca_entity *find_sdca_entity_by_label(struct sdca_function_data * - - return NULL; - } -+EXPORT_SYMBOL_NS(sdca_find_entity_by_label, "SND_SOC_SDCA"); - - static struct sdca_entity *find_sdca_entity_by_id(struct sdca_function_data *function, - const int id) -@@ -1688,7 +1672,7 @@ static int find_sdca_entity_connection_iot(struct device *dev, - return ret; - } - -- clock_entity = find_sdca_entity_by_label(function, clock_label); -+ clock_entity = sdca_find_entity_by_label(function, clock_label); - if (!clock_entity) { - dev_err(dev, "%s: failed to find clock with label %s\n", - entity->label, clock_label); -@@ -1873,7 +1857,7 @@ static int find_sdca_entity_connection(struct device *dev, - return ret; - } - -- connected_entity = find_sdca_entity_by_label(function, connected_label); -+ connected_entity = sdca_find_entity_by_label(function, connected_label); - if (!connected_entity) { - dev_err(dev, "%s: failed to find entity with label %s\n", - entity->label, connected_label); -@@ -2088,8 +2072,7 @@ static int find_sdca_clusters(struct device *dev, - return 0; - } - --static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, -- struct fwnode_handle *function_node, -+static int find_sdca_filesets(struct device *dev, struct fwnode_handle *function_node, - struct sdca_function_data *function) - { - static const int mult_fileset = 3; -@@ -2171,23 +2154,65 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, - set->files = files; - } - -- function->fdl_data.swft = sdw->sdca_data.swft; - function->fdl_data.num_sets = num_sets; - function->fdl_data.sets = sets; - - return 0; - } - -+static int find_sdca_hid(struct device *dev, struct fwnode_handle *function_node, -+ struct sdca_function_data *function) -+{ -+ int num_desc; -+ -+ num_desc = fwnode_property_count_u8(function_node, "mipi-sdca-hid-descriptor"); -+ if (!num_desc) { -+ return 0; -+ } else if (num_desc < 0) { -+ dev_err(dev, "%pfwP: failed to read hid descriptor: %d\n", -+ function_node, num_desc); -+ return num_desc; -+ } else if (num_desc > sizeof(function->hid.desc)) { -+ dev_err(dev, "%pfwP: hid descriptor too large: %d\n", -+ function_node, num_desc); -+ return -EINVAL; -+ } -+ -+ fwnode_property_read_u8_array(function_node, "mipi-sdca-hid-descriptor", -+ (u8 *)&function->hid.desc, num_desc); -+ -+ if (!function->hid.desc.bNumDescriptors) -+ return 0; -+ -+ num_desc = fwnode_property_count_u8(function_node, "mipi-sdca-report-descriptor"); -+ if (num_desc <= 0) { -+ dev_err(dev, "%pfwP: failed to read report descriptor: %d\n", -+ function_node, num_desc); -+ -+ if (!num_desc) -+ return -EINVAL; -+ -+ return num_desc; -+ } -+ -+ function->hid.report_desc = devm_kzalloc(dev, num_desc, GFP_KERNEL); -+ if (!function->hid.report_desc) -+ return -ENOMEM; -+ -+ fwnode_property_read_u8_array(function_node, "mipi-sdca-report-descriptor", -+ function->hid.report_desc, num_desc); -+ -+ return 0; -+} -+ - /** - * sdca_parse_function - parse ACPI DisCo for a Function - * @dev: Pointer to device against which function data will be allocated. -- * @sdw: SoundWire slave device to be processed. - * @function: Pointer to the Function information, to be populated. - * - * Return: Returns 0 for success. - */ --int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, -- struct sdca_function_data *function) -+int sdca_parse_function(struct device *dev, struct sdca_function_data *function) - { - struct fwnode_handle *node = function->desc->node; - u32 tmp; -@@ -2213,7 +2238,7 @@ int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, - if (ret) - return ret; - -- ret = find_sdca_entities(dev, sdw, node, function); -+ ret = find_sdca_entities(dev, node, function); - if (ret) - return ret; - -@@ -2225,10 +2250,20 @@ int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, - if (ret < 0) - return ret; - -- ret = find_sdca_filesets(dev, sdw, node, function); -+ ret = find_sdca_filesets(dev, node, function); - if (ret) - return ret; - -+ switch (function->desc->type) { -+ case SDCA_FUNCTION_TYPE_HID: -+ ret = find_sdca_hid(dev, node, function); -+ if (ret) -+ return ret; -+ break; -+ default: -+ break; -+ } -+ - return 0; - } - EXPORT_SYMBOL_NS(sdca_parse_function, "SND_SOC_SDCA"); -diff --git a/sound/soc/sdca/sdca_hid.c b/sound/soc/sdca/sdca_hid.c ---- a/sound/soc/sdca/sdca_hid.c -+++ b/sound/soc/sdca/sdca_hid.c -@@ -14,6 +14,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -23,18 +24,18 @@ - - static int sdwhid_parse(struct hid_device *hid) - { -- struct sdca_entity *entity = hid->driver_data; -+ struct sdca_function_data *function = hid->driver_data; - unsigned int rsize; - int ret; - -- rsize = le16_to_cpu(entity->hide.hid_desc.rpt_desc.wDescriptorLength); -+ rsize = le16_to_cpu(function->hid.desc.rpt_desc.wDescriptorLength); - - if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { - dev_err(&hid->dev, "invalid size of report descriptor (%u)\n", rsize); - return -EINVAL; - } - -- ret = hid_parse_report(hid, entity->hide.hid_report_desc, rsize); -+ ret = hid_parse_report(hid, function->hid.report_desc, rsize); - - if (!ret) - return 0; -@@ -85,10 +86,16 @@ static const struct hid_ll_driver sdw_hid_driver = { - .raw_request = sdwhid_raw_request, - }; - --int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, -- struct sdca_entity *entity) -+/** -+ * sdca_add_hid_device - create a new SDCA HID device -+ * @interrupt: Pointer to the SDCA interrupt information structure. -+ * -+ * Return: Zero on success, and a negative error code on failure. -+ */ -+int sdca_add_hid_device(struct sdca_interrupt *interrupt) - { -- struct sdw_bus *bus = sdw->bus; -+ struct device *dev = interrupt->dev; -+ struct sdca_function_data *function = interrupt->function; - struct hid_device *hid; - int ret; - -@@ -100,16 +107,13 @@ int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, - - hid->dev.parent = dev; - hid->bus = BUS_SDW; -- hid->version = le16_to_cpu(entity->hide.hid_desc.bcdHID); -+ hid->version = le16_to_cpu(function->hid.desc.bcdHID); - -- snprintf(hid->name, sizeof(hid->name), -- "HID sdw:%01x:%01x:%04x:%04x:%02x", -- bus->controller_id, bus->link_id, sdw->id.mfg_id, -- sdw->id.part_id, sdw->id.class_id); -+ strscpy(hid->phys, dev_name(dev)); -+ snprintf(hid->name, sizeof(hid->name), "SDCA %s:%02x", -+ function->desc->name, function->desc->adr); - -- snprintf(hid->phys, sizeof(hid->phys), "%s", dev->bus->name); -- -- hid->driver_data = entity; -+ hid->driver_data = function; - - ret = hid_add_device(hid); - if (ret && ret != -ENODEV) { -@@ -118,12 +122,24 @@ int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, - return ret; - } - -- entity->hide.hid = hid; -+ interrupt->priv = hid; - - return 0; - } - EXPORT_SYMBOL_NS(sdca_add_hid_device, "SND_SOC_SDCA"); - -+/** -+ * sdca_destroy_hid_device - destroy the HID device -+ * @interrupt: Pointer to the SDCA interrupt information structure. -+ */ -+void sdca_destroy_hid_device(struct sdca_interrupt *interrupt) -+{ -+ struct hid_device *hid = interrupt->priv; -+ -+ hid_destroy_device(hid); -+} -+EXPORT_SYMBOL_NS(sdca_destroy_hid_device, "SND_SOC_SDCA"); -+ - /** - * sdca_hid_process_report - read a HID event from the device and report - * @interrupt: Pointer to the SDCA interrupt information structure. -@@ -133,7 +149,7 @@ EXPORT_SYMBOL_NS(sdca_add_hid_device, "SND_SOC_SDCA"); - int sdca_hid_process_report(struct sdca_interrupt *interrupt) - { - struct device *dev = interrupt->dev; -- struct hid_device *hid = interrupt->entity->hide.hid; -+ struct hid_device *hid = interrupt->priv; - void *val __free(kfree) = NULL; - int len, ret; - -diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c ---- a/sound/soc/sdca/sdca_interrupts.c -+++ b/sound/soc/sdca/sdca_interrupts.c -@@ -487,6 +487,12 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *regmap, - } - break; - case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER): -+ interrupt->free_priv = sdca_destroy_hid_device; -+ -+ ret = sdca_add_hid_device(interrupt); -+ if (ret) -+ return ret; -+ - interrupt->handler = hid_handler; - break; - default: -diff --git a/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c b/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c ---- a/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c -+++ b/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c -@@ -99,11 +99,9 @@ static const struct snd_soc_dai_link bridge_dai_template = { - SND_SOC_DAILINK_REG(asoc_sdw_bridge_dai), - }; - --int asoc_sdw_bridge_cs35l56_count_sidecar(struct snd_soc_card *card, -+int asoc_sdw_bridge_cs35l56_count_sidecar(struct asoc_sdw_mc_private *ctx, - int *num_dais, int *num_devs) - { -- struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); -- - if (ctx->mc_quirk & SOC_SDW_SIDECAR_AMPS) { - (*num_dais)++; - (*num_devs) += ARRAY_SIZE(bridge_cs35l56_name_prefixes); -diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c ---- a/sound/soc/sdw_utils/soc_sdw_utils.c -+++ b/sound/soc/sdw_utils/soc_sdw_utils.c -@@ -834,6 +834,35 @@ struct asoc_sdw_codec_info codec_info_list[] = { - }, - .dai_num = 2, - }, -+ { -+ .vendor_id = 0x01fa, -+ .part_id = 0x3562, -+ .name_prefix = "AMP", -+ .is_amp = true, -+ .dais = { -+ { -+ .direction = {true, false}, -+ .dai_name = "cs35l56-sdw1", -+ .component_name = "cs35l56", -+ .dai_type = SOC_SDW_DAI_TYPE_AMP, -+ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, -+ .init = asoc_sdw_cs_amp_init, -+ .rtd_init = asoc_sdw_cs_spk_rtd_init, -+ .controls = generic_spk_controls, -+ .num_controls = ARRAY_SIZE(generic_spk_controls), -+ .widgets = generic_spk_widgets, -+ .num_widgets = ARRAY_SIZE(generic_spk_widgets), -+ }, -+ { -+ .direction = {false, true}, -+ .dai_name = "cs35l56-sdw1c", -+ .dai_type = SOC_SDW_DAI_TYPE_AMP, -+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, -+ .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, -+ }, -+ }, -+ .dai_num = 2, -+ }, - { - .vendor_id = 0x01fa, - .part_id = 0x3563, -@@ -1789,9 +1818,7 @@ static int is_sdca_aux_dev_present(struct device *dev, - int adr_index) - { - struct sdw_slave *slave; -- struct device *sdw_dev; - const char *sdw_codec_name; -- int ret = 0; - int i; - - if (!aux_codec_name) -@@ -1801,7 +1828,8 @@ static int is_sdca_aux_dev_present(struct device *dev, - if (!sdw_codec_name) - return -ENOMEM; - -- sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); -+ struct device *sdw_dev __free(put_device) = -+ bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); - if (!sdw_dev) { - dev_err(dev, "codec %s not found\n", sdw_codec_name); - return -EINVAL; -@@ -1811,24 +1839,19 @@ static int is_sdca_aux_dev_present(struct device *dev, - - if (!slave->sdca_data.interface_revision) { - dev_warn(dev, "No SDCA properties, assuming aux '%s' present\n", aux_codec_name); -- ret = 1; -- goto put_dev; -+ return 1; - } - - for (i = 0; i < slave->sdca_data.num_functions; i++) { - const char *fname = slave->sdca_data.function[i].name; - -- if (fname && strstr(aux_codec_name, fname)) { -- ret = 1; -- goto put_dev; -- } -+ if (fname && strstr(aux_codec_name, fname)) -+ return 1; - } - - dev_dbg(dev, "SDCA function for aux '%s' NOT FOUND on slave, skipping\n", aux_codec_name); - --put_dev: -- put_device(sdw_dev); -- return ret; -+ return 0; - } - - int asoc_sdw_count_sdw_endpoints(struct snd_soc_card *card, -@@ -1928,9 +1951,8 @@ static int is_sdca_endpoint_present(struct device *dev, - const struct snd_soc_acpi_endpoint *adr_end; - const struct asoc_sdw_dai_info *dai_info; - struct sdw_slave *slave; -- struct device *sdw_dev; - const char *sdw_codec_name; -- int ret, i; -+ int i; - - adr_end = &adr_dev->endpoints[end_index]; - dai_info = &codec_info->dais[adr_end->num]; -@@ -1939,7 +1961,8 @@ static int is_sdca_endpoint_present(struct device *dev, - if (!sdw_codec_name) - return -ENOMEM; - -- sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); -+ struct device *sdw_dev __free(put_device) = -+ bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); - if (!sdw_dev) { - dev_err(dev, "codec %s not found\n", sdw_codec_name); - return -EINVAL; -@@ -1950,8 +1973,7 @@ static int is_sdca_endpoint_present(struct device *dev, - /* Make sure BIOS provides SDCA properties */ - if (!slave->sdca_data.interface_revision) { - dev_warn(&slave->dev, "SDCA properties not found in the BIOS\n"); -- ret = 1; -- goto put_device; -+ return 1; - } - - for (i = 0; i < slave->sdca_data.num_functions; i++) { -@@ -1960,8 +1982,7 @@ static int is_sdca_endpoint_present(struct device *dev, - if (dai_type == dai_info->dai_type) { - dev_dbg(&slave->dev, "DAI type %d sdca function %s found\n", - dai_type, slave->sdca_data.function[i].name); -- ret = 1; -- goto put_device; -+ return 1; - } - } - -@@ -1969,21 +1990,16 @@ static int is_sdca_endpoint_present(struct device *dev, - "SDCA device function for DAI type %d not supported, skip endpoint\n", - dai_info->dai_type); - -- ret = 0; -- --put_device: -- put_device(sdw_dev); -- return ret; -+ return 0; - } - --int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, -+int asoc_sdw_parse_sdw_endpoints(struct device *dev, -+ struct asoc_sdw_mc_private *ctx, - struct snd_soc_aux_dev *soc_aux, - struct asoc_sdw_dailink *soc_dais, - struct asoc_sdw_endpoint *soc_ends, - int *num_devs) - { -- struct device *dev = card->dev; -- struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); - struct snd_soc_acpi_mach *mach = dev_get_platdata(dev); - struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; - const struct snd_soc_acpi_link_adr *adr_link; -@@ -2045,7 +2061,7 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, - ctx->ignore_internal_dmic |= codec_info->ignore_internal_dmic; - - if (codec_info->count_sidecar && codec_info->add_sidecar) { -- ret = codec_info->count_sidecar(card, &num_dais, num_devs); -+ ret = codec_info->count_sidecar(ctx, &num_dais, num_devs); - if (ret) - return ret; - -diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c ---- a/sound/soc/soc-component.c -+++ b/sound/soc/soc-component.c -@@ -29,17 +29,42 @@ static inline int _soc_component_ret_reg_rw(struct snd_soc_component *component, - func, component->name, reg); - } - --static inline int soc_component_field_shift(struct snd_soc_component *component, -- unsigned int mask) -+struct snd_soc_component *snd_soc_component_alloc(struct device *dev) - { -- if (!mask) { -- dev_err(component->dev, "ASoC: error field mask is zero for %s\n", -- component->name); -- return 0; -- } -+ struct snd_soc_component *component = devm_kzalloc(dev, sizeof(*component), GFP_KERNEL); - -- return (ffs(mask) - 1); -+ if (!component) -+ return NULL; -+ -+ component->dev = dev; -+ -+ return component; - } -+EXPORT_SYMBOL_GPL(snd_soc_component_alloc); -+ -+void snd_soc_component_set_name(struct snd_soc_component *component, const char *name) -+{ -+ component->name = name; -+} -+EXPORT_SYMBOL_GPL(snd_soc_component_set_name); -+ -+const char *snd_soc_component_name(struct snd_soc_component *component) -+{ -+ return component->name; -+} -+EXPORT_SYMBOL_GPL(snd_soc_component_name); -+ -+void snd_soc_component_set_priv(struct snd_soc_component *component, void *priv) -+{ -+ component->priv = priv; -+} -+EXPORT_SYMBOL_GPL(snd_soc_component_set_priv); -+ -+void *snd_soc_component_to_priv(struct snd_soc_component *component) -+{ -+ return component->priv; -+} -+EXPORT_SYMBOL_GPL(snd_soc_component_to_priv); - - /* - * We might want to check substream by using list. -@@ -820,6 +845,18 @@ int snd_soc_component_update_bits_async(struct snd_soc_component *component, - } - EXPORT_SYMBOL_GPL(snd_soc_component_update_bits_async); - -+static inline int soc_component_field_shift(struct snd_soc_component *component, -+ unsigned int mask) -+{ -+ if (!mask) { -+ dev_err(component->dev, "ASoC: error field mask is zero for %s\n", -+ component->name); -+ return 0; -+ } -+ -+ return (ffs(mask) - 1); -+} -+ - /** - * snd_soc_component_read_field() - Read register field value - * @component: Component to read from -diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c ---- a/sound/soc/soc-core.c -+++ b/sound/soc/soc-core.c -@@ -2707,10 +2707,11 @@ static void snd_soc_del_component_unlocked(struct snd_soc_component *component) - list_del(&component->list); - } - --int snd_soc_component_initialize(struct snd_soc_component *component, -- const struct snd_soc_component_driver *driver, -- struct device *dev) -+static int soc_component_initialize(struct snd_soc_component *component, -+ const struct snd_soc_component_driver *driver) - { -+ struct device *dev = component->dev; -+ - component->dapm = snd_soc_dapm_alloc(dev); - if (!component->dapm) - return -ENOMEM; -@@ -2730,16 +2731,14 @@ int snd_soc_component_initialize(struct snd_soc_component *component, - } - } - -- component->dev = dev; - component->driver = driver; - - return 0; - } --EXPORT_SYMBOL_GPL(snd_soc_component_initialize); - --int snd_soc_add_component(struct snd_soc_component *component, -- struct snd_soc_dai_driver *dai_drv, -- int num_dai) -+static int soc_component_add(struct snd_soc_component *component, -+ struct snd_soc_dai_driver *dai_drv, -+ int num_dai) - { - struct snd_soc_card *card, *c; - int ret; -@@ -2778,27 +2777,36 @@ int snd_soc_add_component(struct snd_soc_component *component, - - return ret; - } --EXPORT_SYMBOL_GPL(snd_soc_add_component); - --int snd_soc_register_component(struct device *dev, -+int snd_soc_register_component_c(struct snd_soc_component *component, - const struct snd_soc_component_driver *component_driver, - struct snd_soc_dai_driver *dai_drv, - int num_dai) - { -- struct snd_soc_component *component; - int ret; - -- component = devm_kzalloc(dev, sizeof(*component), GFP_KERNEL); -- if (!component) -- return -ENOMEM; -- -- ret = snd_soc_component_initialize(component, component_driver, dev); -+ ret = soc_component_initialize(component, component_driver); - if (ret < 0) - return ret; - -- return snd_soc_add_component(component, dai_drv, num_dai); -+ return soc_component_add(component, dai_drv, num_dai); - } --EXPORT_SYMBOL_GPL(snd_soc_register_component); -+EXPORT_SYMBOL_GPL(snd_soc_register_component_c); -+ -+int snd_soc_register_component_d(struct device *dev, -+ const struct snd_soc_component_driver *component_driver, -+ struct snd_soc_dai_driver *dai_drv, -+ int num_dai) -+{ -+ struct snd_soc_component *component; -+ -+ component = snd_soc_component_alloc(dev); -+ if (!component) -+ return -ENOMEM; -+ -+ return snd_soc_register_component_c(component, component_driver, dai_drv, num_dai); -+} -+EXPORT_SYMBOL_GPL(snd_soc_register_component_d); - - /** - * snd_soc_unregister_component_by_driver - Unregister component using a given driver -diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c ---- a/sound/soc/soc-dapm.c -+++ b/sound/soc/soc-dapm.c -@@ -1203,6 +1203,8 @@ static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w, - case snd_soc_dapm_pga: - case snd_soc_dapm_effect: - case snd_soc_dapm_out_drv: -+ case snd_soc_dapm_encoder: -+ case snd_soc_dapm_decoder: - wname_in_long_name = true; - kcname_in_long_name = true; - break; -@@ -2785,6 +2787,8 @@ static ssize_t dapm_widget_show_component(struct snd_soc_component *component, - case snd_soc_dapm_pga: - case snd_soc_dapm_effect: - case snd_soc_dapm_out_drv: -+ case snd_soc_dapm_encoder: -+ case snd_soc_dapm_decoder: - case snd_soc_dapm_mixer: - case snd_soc_dapm_mixer_named_ctl: - case snd_soc_dapm_supply: -@@ -3367,6 +3371,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) - case snd_soc_dapm_pga: - case snd_soc_dapm_effect: - case snd_soc_dapm_out_drv: -+ case snd_soc_dapm_encoder: -+ case snd_soc_dapm_decoder: - dapm_new_pga(w); - break; - case snd_soc_dapm_dai_link: -diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c ---- a/sound/soc/soc-generic-dmaengine-pcm.c -+++ b/sound/soc/soc-generic-dmaengine-pcm.c -@@ -3,6 +3,7 @@ - // Copyright (C) 2013, Analog Devices Inc. - // Author: Lars-Peter Clausen - -+#include - #include - #include - #include -@@ -77,7 +78,7 @@ static int dmaengine_pcm_hw_params(struct snd_soc_component *component, - struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) - { -- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); -+ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); - struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); - struct dma_slave_config slave_config; - int ret; -@@ -99,7 +100,7 @@ dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component, - struct snd_pcm_substream *substream) - { - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); -- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); -+ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); - struct device *dma_dev = dmaengine_dma_dev(pcm, substream); - struct dma_chan *chan = pcm->chan[substream->stream]; - struct snd_dmaengine_dai_dma_data *dma_data; -@@ -148,7 +149,7 @@ dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component, - static int dmaengine_pcm_open(struct snd_soc_component *component, - struct snd_pcm_substream *substream) - { -- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); -+ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); - struct dma_chan *chan = pcm->chan[substream->stream]; - int ret; - -@@ -176,7 +177,7 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel( - struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_substream *substream) - { -- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); -+ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); - struct snd_dmaengine_dai_dma_data *dma_data; - - if (rtd->dai_link->num_cpus > 1) { -@@ -219,7 +220,7 @@ static bool dmaengine_pcm_can_report_residue(struct device *dev, - static int dmaengine_pcm_new(struct snd_soc_component *component, - struct snd_soc_pcm_runtime *rtd) - { -- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); -+ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); - const struct snd_dmaengine_pcm_config *config = pcm->config; - struct device *dev = component->dev; - size_t prealloc_buffer_size; -@@ -279,7 +280,7 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer( - struct snd_soc_component *component, - struct snd_pcm_substream *substream) - { -- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); -+ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); - - if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE) - return snd_dmaengine_pcm_pointer_no_residue(substream); -@@ -293,7 +294,7 @@ static int dmaengine_copy(struct snd_soc_component *component, - struct iov_iter *iter, unsigned long bytes) - { - struct snd_pcm_runtime *runtime = substream->runtime; -- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); -+ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); - int (*process)(struct snd_pcm_substream *substream, - int channel, unsigned long hwoff, - unsigned long bytes) = pcm->config->process; -@@ -395,6 +396,27 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, - */ - if (PTR_ERR(chan) == -EPROBE_DEFER) - return -EPROBE_DEFER; -+ -+ bool has_fw_node = dev->of_node || is_acpi_device_node(dev->fwnode); -+ bool name_exists_in_fw = false; -+ -+ if (has_fw_node) -+ name_exists_in_fw = device_property_match_string(dev, -+ "dma-names", -+ name) >= 0; -+ -+ if (has_fw_node && name_exists_in_fw) -+ dev_warn(dev, "DTS/ACPI DMA channel '%s' request failed (%ld)\n", -+ name, PTR_ERR(chan)); -+ -+ if (has_fw_node && !name_exists_in_fw) -+ dev_warn(dev, "DTS/ACPI name '%s' not found, legacy failed (%ld)\n", -+ name, PTR_ERR(chan)); -+ -+ if (!has_fw_node) -+ dev_warn(dev, "Legacy DMA channel '%s' request failed (%ld)\n", -+ name, PTR_ERR(chan)); -+ - pcm->chan[i] = NULL; - } else { - pcm->chan[i] = chan; -@@ -406,6 +428,12 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, - if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) - pcm->chan[1] = pcm->chan[0]; - -+ if (!pcm->chan[0] && -+ !pcm->chan[1]) { -+ dev_err(dev, "no DMA channel found for either playback or capture\n"); -+ return -ENODEV; -+ } -+ - return 0; - } - -@@ -435,10 +463,15 @@ static const struct snd_dmaengine_pcm_config snd_dmaengine_pcm_default_config = - int snd_dmaengine_pcm_register(struct device *dev, - const struct snd_dmaengine_pcm_config *config, unsigned int flags) - { -+ struct snd_soc_component *component; - const struct snd_soc_component_driver *driver; - struct dmaengine_pcm *pcm; - int ret; - -+ component = snd_soc_component_alloc(dev); -+ if (!component) -+ return -ENOMEM; -+ - pcm = kzalloc_obj(*pcm); - if (!pcm) - return -ENOMEM; -@@ -449,7 +482,8 @@ int snd_dmaengine_pcm_register(struct device *dev, - pcm->flags = flags; - - if (config->name) -- pcm->component.name = config->name; -+ snd_soc_component_set_name(component, config->name); -+ snd_soc_component_set_priv(component, pcm); - - ret = dmaengine_pcm_request_chan_of(pcm, dev, config); - if (ret) -@@ -460,11 +494,7 @@ int snd_dmaengine_pcm_register(struct device *dev, - else - driver = &dmaengine_pcm_component; - -- ret = snd_soc_component_initialize(&pcm->component, driver, dev); -- if (ret) -- goto err_free_dma; -- -- ret = snd_soc_add_component(&pcm->component, NULL, 0); -+ ret = snd_soc_register_component(component, driver, NULL, 0); - if (ret) - goto err_free_dma; - -@@ -493,7 +523,7 @@ void snd_dmaengine_pcm_unregister(struct device *dev) - if (!component) - return; - -- pcm = soc_component_to_pcm(component); -+ pcm = snd_soc_component_to_priv(component); - - snd_soc_unregister_component_by_driver(dev, component->driver); - dmaengine_pcm_release_chan(pcm); -diff --git a/sound/soc/soc-topology-test.c b/sound/soc/soc-topology-test.c ---- a/sound/soc/soc-topology-test.c -+++ b/sound/soc/soc-topology-test.c -@@ -45,15 +45,13 @@ static void snd_soc_tplg_test_exit(struct kunit *test) - struct kunit_soc_component { - struct kunit *kunit; - int expect; /* what result we expect when loading topology */ -- struct snd_soc_component comp; - struct snd_soc_card card; - struct firmware fw; - }; - - static int d_probe(struct snd_soc_component *component) - { -- struct kunit_soc_component *kunit_comp = -- container_of(component, struct kunit_soc_component, comp); -+ struct kunit_soc_component *kunit_comp = snd_soc_component_to_priv(component); - int ret; - - ret = snd_soc_tplg_component_load(component, NULL, &kunit_comp->fw); -@@ -65,8 +63,7 @@ static int d_probe(struct snd_soc_component *component) - - static void d_remove(struct snd_soc_component *component) - { -- struct kunit_soc_component *kunit_comp = -- container_of(component, struct kunit_soc_component, comp); -+ struct kunit_soc_component *kunit_comp = snd_soc_component_to_priv(component); - int ret; - - ret = snd_soc_tplg_component_remove(component); -@@ -214,8 +211,7 @@ static struct tplg_tmpl_002 tplg_tmpl_with_pcm = { - */ - static int d_probe_null_comp(struct snd_soc_component *component) - { -- struct kunit_soc_component *kunit_comp = -- container_of(component, struct kunit_soc_component, comp); -+ struct kunit_soc_component *kunit_comp = snd_soc_component_to_priv(component); - int ret; - - /* instead of passing component pointer as first argument, pass NULL here */ -@@ -234,6 +230,7 @@ static const struct snd_soc_component_driver test_component_null_comp = { - static void snd_soc_tplg_test_load_with_null_comp(struct kunit *test) - { - struct kunit_soc_component *kunit_comp; -+ struct snd_soc_component *component; - int ret; - - /* prepare */ -@@ -249,15 +246,17 @@ static void snd_soc_tplg_test_load_with_null_comp(struct kunit *test) - kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); - kunit_comp->card.fully_routed = true; - -+ component = snd_soc_component_alloc(test_dev); -+ KUNIT_ASSERT_NOT_NULL(test, component); -+ -+ snd_soc_component_set_priv(component, kunit_comp); -+ - /* run test */ - ret = snd_soc_register_card(&kunit_comp->card); - if (ret != 0 && ret != -EPROBE_DEFER) - KUNIT_FAIL(test, "Failed to register card"); - -- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component_null_comp, test_dev); -- KUNIT_EXPECT_EQ(test, 0, ret); -- -- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); -+ ret = snd_soc_register_component(component, &test_component_null_comp, NULL, 0); - KUNIT_EXPECT_EQ(test, 0, ret); - - /* cleanup */ -@@ -276,6 +275,7 @@ static void snd_soc_tplg_test_load_with_null_comp(struct kunit *test) - static void snd_soc_tplg_test_load_with_null_ops(struct kunit *test) - { - struct kunit_soc_component *kunit_comp; -+ struct snd_soc_component *component; - int ret; - - /* prepare */ -@@ -291,15 +291,17 @@ static void snd_soc_tplg_test_load_with_null_ops(struct kunit *test) - kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); - kunit_comp->card.fully_routed = true; - -+ component = snd_soc_component_alloc(test_dev); -+ KUNIT_ASSERT_NOT_NULL(test, component); -+ -+ snd_soc_component_set_priv(component, kunit_comp); -+ - /* run test */ - ret = snd_soc_register_card(&kunit_comp->card); - if (ret != 0 && ret != -EPROBE_DEFER) - KUNIT_FAIL(test, "Failed to register card"); - -- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); -- KUNIT_EXPECT_EQ(test, 0, ret); -- -- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); -+ ret = snd_soc_register_component(component, &test_component, NULL, 0); - KUNIT_EXPECT_EQ(test, 0, ret); - - /* cleanup */ -@@ -318,8 +320,7 @@ static void snd_soc_tplg_test_load_with_null_ops(struct kunit *test) - */ - static int d_probe_null_fw(struct snd_soc_component *component) - { -- struct kunit_soc_component *kunit_comp = -- container_of(component, struct kunit_soc_component, comp); -+ struct kunit_soc_component *kunit_comp = snd_soc_component_to_priv(component); - int ret; - - /* instead of passing fw pointer as third argument, pass NULL here */ -@@ -338,6 +339,7 @@ static const struct snd_soc_component_driver test_component_null_fw = { - static void snd_soc_tplg_test_load_with_null_fw(struct kunit *test) - { - struct kunit_soc_component *kunit_comp; -+ struct snd_soc_component *component; - int ret; - - /* prepare */ -@@ -353,15 +355,17 @@ static void snd_soc_tplg_test_load_with_null_fw(struct kunit *test) - kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); - kunit_comp->card.fully_routed = true; - -+ component = snd_soc_component_alloc(test_dev); -+ KUNIT_ASSERT_NOT_NULL(test, component); -+ -+ snd_soc_component_set_priv(component, kunit_comp); -+ - /* run test */ - ret = snd_soc_register_card(&kunit_comp->card); - if (ret != 0 && ret != -EPROBE_DEFER) - KUNIT_FAIL(test, "Failed to register card"); - -- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component_null_fw, test_dev); -- KUNIT_EXPECT_EQ(test, 0, ret); -- -- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); -+ ret = snd_soc_register_component(component, &test_component_null_fw, NULL, 0); - KUNIT_EXPECT_EQ(test, 0, ret); - - /* cleanup */ -@@ -375,6 +379,7 @@ static void snd_soc_tplg_test_load_with_null_fw(struct kunit *test) - static void snd_soc_tplg_test_load_empty_tplg(struct kunit *test) - { - struct kunit_soc_component *kunit_comp; -+ struct snd_soc_component *component; - struct tplg_tmpl_001 *data; - int size; - int ret; -@@ -401,15 +406,17 @@ static void snd_soc_tplg_test_load_empty_tplg(struct kunit *test) - kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); - kunit_comp->card.fully_routed = true; - -+ component = snd_soc_component_alloc(test_dev); -+ KUNIT_ASSERT_NOT_NULL(test, component); -+ -+ snd_soc_component_set_priv(component, kunit_comp); -+ - /* run test */ - ret = snd_soc_register_card(&kunit_comp->card); - if (ret != 0 && ret != -EPROBE_DEFER) - KUNIT_FAIL(test, "Failed to register card"); - -- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); -- KUNIT_EXPECT_EQ(test, 0, ret); -- -- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); -+ ret = snd_soc_register_component(component, &test_component, NULL, 0); - KUNIT_EXPECT_EQ(test, 0, ret); - - /* cleanup */ -@@ -425,6 +432,7 @@ static void snd_soc_tplg_test_load_empty_tplg(struct kunit *test) - static void snd_soc_tplg_test_load_empty_tplg_bad_magic(struct kunit *test) - { - struct kunit_soc_component *kunit_comp; -+ struct snd_soc_component *component; - struct tplg_tmpl_001 *data; - int size; - int ret; -@@ -456,15 +464,17 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_magic(struct kunit *test) - kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); - kunit_comp->card.fully_routed = true; - -+ component = snd_soc_component_alloc(test_dev); -+ KUNIT_ASSERT_NOT_NULL(test, component); -+ -+ snd_soc_component_set_priv(component, kunit_comp); -+ - /* run test */ - ret = snd_soc_register_card(&kunit_comp->card); - if (ret != 0 && ret != -EPROBE_DEFER) - KUNIT_FAIL(test, "Failed to register card"); - -- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); -- KUNIT_EXPECT_EQ(test, 0, ret); -- -- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); -+ ret = snd_soc_register_component(component, &test_component, NULL, 0); - KUNIT_EXPECT_EQ(test, 0, ret); - - /* cleanup */ -@@ -480,6 +490,7 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_magic(struct kunit *test) - static void snd_soc_tplg_test_load_empty_tplg_bad_abi(struct kunit *test) - { - struct kunit_soc_component *kunit_comp; -+ struct snd_soc_component *component; - struct tplg_tmpl_001 *data; - int size; - int ret; -@@ -511,15 +522,17 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_abi(struct kunit *test) - kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); - kunit_comp->card.fully_routed = true; - -+ component = snd_soc_component_alloc(test_dev); -+ KUNIT_ASSERT_NOT_NULL(test, component); -+ -+ snd_soc_component_set_priv(component, kunit_comp); -+ - /* run test */ - ret = snd_soc_register_card(&kunit_comp->card); - if (ret != 0 && ret != -EPROBE_DEFER) - KUNIT_FAIL(test, "Failed to register card"); - -- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); -- KUNIT_EXPECT_EQ(test, 0, ret); -- -- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); -+ ret = snd_soc_register_component(component, &test_component, NULL, 0); - KUNIT_EXPECT_EQ(test, 0, ret); - - /* cleanup */ -@@ -535,6 +548,7 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_abi(struct kunit *test) - static void snd_soc_tplg_test_load_empty_tplg_bad_size(struct kunit *test) - { - struct kunit_soc_component *kunit_comp; -+ struct snd_soc_component *component; - struct tplg_tmpl_001 *data; - int size; - int ret; -@@ -566,15 +580,17 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_size(struct kunit *test) - kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); - kunit_comp->card.fully_routed = true; - -+ component = snd_soc_component_alloc(test_dev); -+ KUNIT_ASSERT_NOT_NULL(test, component); -+ -+ snd_soc_component_set_priv(component, kunit_comp); -+ - /* run test */ - ret = snd_soc_register_card(&kunit_comp->card); - if (ret != 0 && ret != -EPROBE_DEFER) - KUNIT_FAIL(test, "Failed to register card"); - -- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); -- KUNIT_EXPECT_EQ(test, 0, ret); -- -- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); -+ ret = snd_soc_register_component(component, &test_component, NULL, 0); - KUNIT_EXPECT_EQ(test, 0, ret); - - /* cleanup */ -@@ -590,6 +606,7 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_size(struct kunit *test) - static void snd_soc_tplg_test_load_empty_tplg_bad_payload_size(struct kunit *test) - { - struct kunit_soc_component *kunit_comp; -+ struct snd_soc_component *component; - struct tplg_tmpl_001 *data; - int size; - int ret; -@@ -622,15 +639,17 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_payload_size(struct kunit *tes - kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); - kunit_comp->card.fully_routed = true; - -+ component = snd_soc_component_alloc(test_dev); -+ KUNIT_ASSERT_NOT_NULL(test, component); -+ -+ snd_soc_component_set_priv(component, kunit_comp); -+ - /* run test */ - ret = snd_soc_register_card(&kunit_comp->card); - if (ret != 0 && ret != -EPROBE_DEFER) - KUNIT_FAIL(test, "Failed to register card"); - -- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); -- KUNIT_EXPECT_EQ(test, 0, ret); -- -- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); -+ ret = snd_soc_register_component(component, &test_component, NULL, 0); - KUNIT_EXPECT_EQ(test, 0, ret); - - /* cleanup */ -@@ -644,6 +663,7 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_payload_size(struct kunit *tes - static void snd_soc_tplg_test_load_pcm_tplg(struct kunit *test) - { - struct kunit_soc_component *kunit_comp; -+ struct snd_soc_component *component; - u8 *data; - int size; - int ret; -@@ -670,15 +690,17 @@ static void snd_soc_tplg_test_load_pcm_tplg(struct kunit *test) - kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); - kunit_comp->card.fully_routed = true; - -+ component = snd_soc_component_alloc(test_dev); -+ KUNIT_ASSERT_NOT_NULL(test, component); -+ -+ snd_soc_component_set_priv(component, kunit_comp); -+ - /* run test */ - ret = snd_soc_register_card(&kunit_comp->card); - if (ret != 0 && ret != -EPROBE_DEFER) - KUNIT_FAIL(test, "Failed to register card"); - -- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); -- KUNIT_EXPECT_EQ(test, 0, ret); -- -- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); -+ ret = snd_soc_register_component(component, &test_component, NULL, 0); - KUNIT_EXPECT_EQ(test, 0, ret); - - snd_soc_unregister_component(test_dev); -@@ -693,6 +715,7 @@ static void snd_soc_tplg_test_load_pcm_tplg(struct kunit *test) - static void snd_soc_tplg_test_load_pcm_tplg_reload_comp(struct kunit *test) - { - struct kunit_soc_component *kunit_comp; -+ struct snd_soc_component *component; - u8 *data; - int size; - int ret; -@@ -720,16 +743,19 @@ static void snd_soc_tplg_test_load_pcm_tplg_reload_comp(struct kunit *test) - kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); - kunit_comp->card.fully_routed = true; - -+ component = snd_soc_component_alloc(test_dev); -+ KUNIT_ASSERT_NOT_NULL(test, component); -+ -+ snd_soc_component_set_priv(component, kunit_comp); -+ - /* run test */ - ret = snd_soc_register_card(&kunit_comp->card); - if (ret != 0 && ret != -EPROBE_DEFER) - KUNIT_FAIL(test, "Failed to register card"); - - for (i = 0; i < 100; i++) { -- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); -- KUNIT_EXPECT_EQ(test, 0, ret); - -- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); -+ ret = snd_soc_register_component(component, &test_component, NULL, 0); - KUNIT_EXPECT_EQ(test, 0, ret); - - snd_soc_unregister_component(test_dev); -@@ -745,6 +771,7 @@ static void snd_soc_tplg_test_load_pcm_tplg_reload_comp(struct kunit *test) - static void snd_soc_tplg_test_load_pcm_tplg_reload_card(struct kunit *test) - { - struct kunit_soc_component *kunit_comp; -+ struct snd_soc_component *component; - u8 *data; - int size; - int ret; -@@ -772,11 +799,13 @@ static void snd_soc_tplg_test_load_pcm_tplg_reload_card(struct kunit *test) - kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); - kunit_comp->card.fully_routed = true; - -- /* run test */ -- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); -- KUNIT_EXPECT_EQ(test, 0, ret); -+ component = snd_soc_component_alloc(test_dev); -+ KUNIT_ASSERT_NOT_NULL(test, component); - -- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); -+ snd_soc_component_set_priv(component, kunit_comp); -+ -+ /* run test */ -+ ret = snd_soc_register_component(component, &test_component, NULL, 0); - KUNIT_EXPECT_EQ(test, 0, ret); - - for (i = 0; i < 100; i++) { -diff --git a/sound/soc/sof/amd/Kconfig b/sound/soc/sof/amd/Kconfig ---- a/sound/soc/sof/amd/Kconfig -+++ b/sound/soc/sof/amd/Kconfig -@@ -104,4 +104,15 @@ config SND_SOC_SOF_AMD_ACP70 - AMD ACP7.0/ACP7.1 version based platforms. - Say Y if you want to enable SOF on ACP7.0/ACP7.1 based platforms. - -+config SND_SOC_SOF_AMD_ACP7X -+ tristate "SOF support for ACP7.B/7.F platforms" -+ depends on SND_SOC_SOF_PCI -+ depends on AMD_NODE -+ select SND_SOC_SOF_AMD_COMMON -+ help -+ Select this option for SOF support on AMD ACP7.B and ACP7.F PCI -+ revision based platforms. -+ Say Y if you want to enable SOF on ACP7.B/7.F based platforms. -+ If unsure select "N". -+ - endif -diff --git a/sound/soc/sof/amd/Makefile b/sound/soc/sof/amd/Makefile ---- a/sound/soc/sof/amd/Makefile -+++ b/sound/soc/sof/amd/Makefile -@@ -11,6 +11,7 @@ snd-sof-amd-rembrandt-y := pci-rmb.o rembrandt.o - snd-sof-amd-vangogh-y := pci-vangogh.o vangogh.o - snd-sof-amd-acp63-y := pci-acp63.o acp63.o - snd-sof-amd-acp70-y := pci-acp70.o acp70.o -+snd-sof-amd-acp7x-y := pci-acp7x.o acp7x.o - - obj-$(CONFIG_SND_SOC_SOF_AMD_COMMON) += snd-sof-amd-acp.o - obj-$(CONFIG_SND_SOC_SOF_AMD_RENOIR) += snd-sof-amd-renoir.o -@@ -18,3 +19,4 @@ obj-$(CONFIG_SND_SOC_SOF_AMD_REMBRANDT) += snd-sof-amd-rembrandt.o - obj-$(CONFIG_SND_SOC_SOF_AMD_VANGOGH) += snd-sof-amd-vangogh.o - obj-$(CONFIG_SND_SOC_SOF_AMD_ACP63) += snd-sof-amd-acp63.o - obj-$(CONFIG_SND_SOC_SOF_AMD_ACP70) += snd-sof-amd-acp70.o -+obj-$(CONFIG_SND_SOC_SOF_AMD_ACP7X) += snd-sof-amd-acp7x.o -diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h ---- a/sound/soc/sof/amd/acp-dsp-offset.h -+++ b/sound/soc/sof/amd/acp-dsp-offset.h -@@ -68,12 +68,14 @@ - #define ACP5X_PGFSM_BASE 0x1424 - #define ACP6X_PGFSM_BASE 0x1024 - #define ACP70_PGFSM_BASE ACP6X_PGFSM_BASE -+#define ACP7X_PGFSM_BASE ACP6X_PGFSM_BASE - #define PGFSM_CONTROL_OFFSET 0x0 - #define PGFSM_STATUS_OFFSET 0x4 - #define ACP3X_CLKMUX_SEL 0x1424 - #define ACP5X_CLKMUX_SEL 0x142C - #define ACP6X_CLKMUX_SEL 0x102C - #define ACP70_CLKMUX_SEL ACP6X_CLKMUX_SEL -+#define ACP7X_CLKMUX_SEL ACP6X_CLKMUX_SEL - - /* Registers from ACP_INTR block */ - #define ACP3X_EXT_INTR_STAT 0x1808 -@@ -86,21 +88,31 @@ - #define ACP70_EXTERNAL_INTR_CNTL ACP6X_EXTERNAL_INTR_CNTL - #define ACP70_EXT_INTR_STAT ACP6X_EXT_INTR_STAT - #define ACP70_EXT_INTR_STAT1 ACP6X_EXT_INTR_STAT1 -+#define ACP7X_EXTERNAL_INTR_ENB ACP6X_EXTERNAL_INTR_ENB -+#define ACP7X_EXTERNAL_INTR_CNTL 0x1A04 -+#define ACP7X_EXT_INTR_STAT 0x1A1C -+#define ACP7X_EXTERNAL_INTR_CNTL1 0x1A08 -+#define ACP7X_EXT_INTR_STAT1 0x1A20 - - #define ACP3X_DSP_SW_INTR_BASE 0x1814 - #define ACP5X_DSP_SW_INTR_BASE 0x1814 - #define ACP6X_DSP_SW_INTR_BASE 0x1808 - #define ACP70_DSP_SW_INTR_BASE ACP6X_DSP_SW_INTR_BASE -+#define ACP7X_DSP_SW_INTR_BASE 0x1860 - #define DSP_SW_INTR_CNTL_OFFSET 0x0 - #define DSP_SW_INTR_STAT_OFFSET 0x4 -+#define ACP7X_DSP_SW_INTR_STAT (ACP7X_DSP_SW_INTR_BASE + DSP_SW_INTR_STAT_OFFSET) - #define DSP_SW_INTR_TRIG_OFFSET 0x8 -+#define ACP7X_DSP_SW_INTR_TRIG_OFFSET 0x30 - #define ACP3X_ERROR_STATUS 0x18C4 - #define ACP6X_ERROR_STATUS 0x1A4C - #define ACP70_ERROR_STATUS ACP6X_ERROR_STATUS -+#define ACP7X_ERROR_STATUS 0x1A88 - #define ACP3X_AXI2DAGB_SEM_0 0x1880 - #define ACP5X_AXI2DAGB_SEM_0 0x1884 - #define ACP6X_AXI2DAGB_SEM_0 0x1874 - #define ACP70_AXI2DAGB_SEM_0 ACP6X_AXI2DAGB_SEM_0 -+#define ACP7X_AXI2DAGB_SEM_0 0x18F4 - - /* ACP common registers to report errors related to I2S & SoundWire interfaces */ - #define ACP3X_SW_I2S_ERROR_REASON 0x18C8 -@@ -123,6 +135,7 @@ - #define ACP_SCRATCH_REG_0 0x10000 - #define ACP6X_DSP_FUSION_RUNSTALL 0x0644 - #define ACP70_DSP_FUSION_RUNSTALL ACP6X_DSP_FUSION_RUNSTALL -+#define ACP7X_DSP_FUSION_RUNSTALL ACP6X_DSP_FUSION_RUNSTALL - - /* Cache window registers */ - #define ACP_DSP0_CACHE_OFFSET0 0x0420 -@@ -139,5 +152,9 @@ - #define ACP70_SDW1_HOST_WAKE_STAT BIT(25) - #define ACP70_SDW0_PME_STAT BIT(26) - #define ACP70_SDW1_PME_STAT BIT(27) -+#define ACP7X_DSP0_IDMA_ERROR_MASK 0x4B0 -+#define ACP7X_IDMA_ERROR_MASK 0x1FF9FF -+#define ACP7X_ZSC_DSP_CTRL 0x001014 -+#define ACP7X_PME_EN ACP70_PME_EN - - #endif -diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c ---- a/sound/soc/sof/amd/acp-ipc.c -+++ b/sound/soc/sof/amd/acp-ipc.c -@@ -32,11 +32,20 @@ static void acpbus_trigger_host_to_dsp_swintr(struct acp_dev_data *adata) - struct snd_sof_dev *sdev = adata->dev; - const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); - u32 swintr_trigger; -+ unsigned int swintr_trigger_reg_offset; - -+ switch (adata->pci_rev) { -+ case ACP7B_PCI_ID: -+ case ACP7F_PCI_ID: -+ swintr_trigger_reg_offset = ACP7X_DSP_SW_INTR_TRIG_OFFSET; -+ break; -+ default: -+ swintr_trigger_reg_offset = DSP_SW_INTR_TRIG_OFFSET; -+ } - swintr_trigger = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->dsp_intr_base + -- DSP_SW_INTR_TRIG_OFFSET); -+ swintr_trigger_reg_offset); - swintr_trigger |= 0x01; -- snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->dsp_intr_base + DSP_SW_INTR_TRIG_OFFSET, -+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->dsp_intr_base + swintr_trigger_reg_offset, - swintr_trigger); - } - -diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c ---- a/sound/soc/sof/amd/acp-loader.c -+++ b/sound/soc/sof/amd/acp-loader.c -@@ -14,6 +14,7 @@ - #include - #include - #include -+#include - - #include "../ops.h" - #include "acp-dsp-offset.h" -@@ -173,10 +174,31 @@ int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev) - - adata = sdev->pdata->hw_pdata; - -- if (adata->quirks && adata->quirks->signed_fw_image) -+ if (adata->pci_rev >= ACP7B_PCI_ID) { -+ if (adata->acp_sof_signed_firmware_image) { -+ if (adata->fw_bin_size <= ACP_IMAGE_HEADER_SIZE) { -+ dev_err(sdev->dev, "Invalid signed firmware size %u\n", -+ adata->fw_bin_size); -+ return -EINVAL; -+ } -+ size_fw = get_unaligned_le32(adata->bin_buf + -+ ACP_IMAGE_HDR_SIZE_FW_SIGNED_OFF); -+ if (!size_fw || -+ size_fw > adata->fw_bin_size - ACP_IMAGE_HEADER_SIZE) { -+ dev_err(sdev->dev, -+ "Invalid signed firmware payload size %u (max %u)\n", -+ size_fw, adata->fw_bin_size - ACP_IMAGE_HEADER_SIZE); -+ return -EINVAL; -+ } -+ size_fw += ACP_IMAGE_HEADER_SIZE; -+ } else { -+ size_fw = adata->fw_bin_size; -+ } -+ } else if (adata->quirks && adata->quirks->signed_fw_image) { - size_fw = adata->fw_bin_size - ACP_FIRMWARE_SIGNATURE; -- else -+ } else { - size_fw = adata->fw_bin_size; -+ } - - page_count = PAGE_ALIGN(size_fw) >> PAGE_SHIFT; - adata->fw_bin_page_count = page_count; -@@ -312,9 +334,14 @@ int acp_sof_load_signed_firmware(struct snd_sof_dev *sdev) - } - kfree(fw_filename); - -- ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_DRAM, 0, -- (void *)adata->fw_dbin->data, -- adata->fw_dbin->size); -+ if (adata->pci_rev >= ACP7B_PCI_ID) -+ ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_SRAM, 0, -+ (void *)adata->fw_dbin->data, -+ adata->fw_dbin->size); -+ else -+ ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_DRAM, 0, -+ (void *)adata->fw_dbin->data, -+ adata->fw_dbin->size); - return ret; - } - EXPORT_SYMBOL_NS(acp_sof_load_signed_firmware, "SND_SOC_SOF_AMD_COMMON"); -diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c ---- a/sound/soc/sof/amd/acp.c -+++ b/sound/soc/sof/amd/acp.c -@@ -12,6 +12,7 @@ - * Hardware interface for generic AMD ACP processor - */ - -+#include - #include - #include - #include -@@ -60,6 +61,8 @@ static void init_dma_descriptor(struct acp_dev_data *adata) - case ACP70_PCI_ID: - case ACP71_PCI_ID: - case ACP72_PCI_ID: -+ case ACP7B_PCI_ID: -+ case ACP7F_PCI_ID: - acp_dma_desc_base_addr = ACP70_DMA_DESC_BASE_ADDR; - acp_dma_desc_max_num_dscr = ACP70_DMA_DESC_MAX_NUM_DSCR; - break; -@@ -101,6 +104,8 @@ static int config_dma_channel(struct acp_dev_data *adata, unsigned int ch, - case ACP70_PCI_ID: - case ACP71_PCI_ID: - case ACP72_PCI_ID: -+ case ACP7B_PCI_ID: -+ case ACP7F_PCI_ID: - acp_dma_cntl_0 = ACP70_DMA_CNTL_0; - acp_dma_ch_rst_sts = ACP70_DMA_CH_RST_STS; - acp_dma_dscr_err_sts_0 = ACP70_DMA_ERR_STS_0; -@@ -283,7 +288,8 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr, - } - } - -- if (adata->quirks && adata->quirks->signed_fw_image) -+ if ((adata->quirks && adata->quirks->signed_fw_image) || -+ adata->acp_sof_signed_firmware_image) - snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_INCLUDE_HDR, ACP_SHA_HEADER); - - snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_STRT_ADDR, start_addr); -@@ -342,6 +348,8 @@ int acp_dma_status(struct acp_dev_data *adata, unsigned char ch) - case ACP70_PCI_ID: - case ACP71_PCI_ID: - case ACP72_PCI_ID: -+ case ACP7B_PCI_ID: -+ case ACP7F_PCI_ID: - acp_dma_ch_sts = ACP70_DMA_CH_STS; - break; - default: -@@ -534,8 +542,17 @@ static irqreturn_t acp_irq_handler(int irq, void *dev_id) - snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP_ERROR_IRQ_MASK); - snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_sw0_i2s_err_reason, 0); - /* ACP_SW1_I2S_ERROR_REASON is newly added register from rmb platform onwards */ -- if (adata->pci_rev >= ACP_RMB_PCI_ID) -+ switch (adata->pci_rev) { -+ case ACP_RMB_PCI_ID: -+ case ACP63_PCI_ID: -+ case ACP70_PCI_ID: -+ case ACP71_PCI_ID: -+ case ACP72_PCI_ID: - snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SW1_I2S_ERROR_REASON, 0); -+ break; -+ default: -+ break; -+ } - snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_error_stat, 0); - irq_flag = 1; - } -@@ -564,6 +581,35 @@ static irqreturn_t acp_irq_handler(int irq, void *dev_id) - return IRQ_NONE; - } - -+static irqreturn_t acp7x_irq_handler(int irq, void *dev_id) -+{ -+ struct snd_sof_dev *sdev = dev_id; -+ const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); -+ unsigned int base = desc->dsp_intr_base; -+ unsigned int val; -+ unsigned int ext_intr_stat; -+ int irq_flag = 0; -+ -+ val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET); -+ if (val & ACP_DSP_TO_HOST_IRQ) { -+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET, -+ ACP_DSP_TO_HOST_IRQ); -+ return IRQ_WAKE_THREAD; -+ } -+ -+ ext_intr_stat = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat); -+ if (ext_intr_stat & ACP_ERROR_IRQ_MASK) { -+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP_ERROR_IRQ_MASK); -+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_error_stat, 0); -+ irq_flag = 1; -+ } -+ -+ if (irq_flag) -+ return IRQ_HANDLED; -+ -+ return IRQ_NONE; -+} -+ - static int acp_power_on(struct snd_sof_dev *sdev) - { - const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); -@@ -571,6 +617,7 @@ static int acp_power_on(struct snd_sof_dev *sdev) - unsigned int base = desc->pgfsm_base; - unsigned int val; - unsigned int acp_pgfsm_status_mask, acp_pgfsm_cntl_mask; -+ bool use_masked_status = false; - int ret; - - val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + PGFSM_STATUS_OFFSET); -@@ -595,6 +642,12 @@ static int acp_power_on(struct snd_sof_dev *sdev) - acp_pgfsm_status_mask = ACP70_PGFSM_STATUS_MASK; - acp_pgfsm_cntl_mask = ACP70_PGFSM_CNTL_POWER_ON_MASK; - break; -+ case ACP7B_PCI_ID: -+ case ACP7F_PCI_ID: -+ acp_pgfsm_status_mask = ACP7X_PGFSM_STATUS_MASK; -+ acp_pgfsm_cntl_mask = ACP7X_PGFSM_CNTL_POWER_ON_MASK; -+ use_masked_status = true; -+ break; - default: - return -EINVAL; - } -@@ -603,8 +656,17 @@ static int acp_power_on(struct snd_sof_dev *sdev) - snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + PGFSM_CONTROL_OFFSET, - acp_pgfsm_cntl_mask); - -- ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, base + PGFSM_STATUS_OFFSET, val, -- !val, ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US); -+ if (use_masked_status) -+ ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, -+ base + PGFSM_STATUS_OFFSET, val, -+ !(val & acp_pgfsm_status_mask), -+ ACP_REG_POLL_INTERVAL, -+ ACP_REG_POLL_TIMEOUT_US); -+ else -+ ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, -+ base + PGFSM_STATUS_OFFSET, val, -+ !val, ACP_REG_POLL_INTERVAL, -+ ACP_REG_POLL_TIMEOUT_US); - if (ret < 0) - dev_err(sdev->dev, "timeout in ACP_PGFSM_STATUS read\n"); - -@@ -703,6 +765,13 @@ static int acp_init(struct snd_sof_dev *sdev) - - snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP70_PME_EN, 1); - break; -+ case ACP7B_PCI_ID: -+ case ACP7F_PCI_ID: -+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_ZSC_DSP_CTRL, 0); -+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_PME_EN, 1); -+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_DSP0_IDMA_ERROR_MASK, -+ ACP7X_IDMA_ERROR_MASK); -+ break; - } - return 0; - } -@@ -749,6 +818,8 @@ int amd_sof_acp_suspend(struct snd_sof_dev *sdev, u32 target_state) - case ACP72_PCI_ID: - enable = true; - break; -+ default: -+ break; - } - snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_CONTROL, enable); - -@@ -990,6 +1061,194 @@ void amd_sof_acp_remove(struct snd_sof_dev *sdev) - } - EXPORT_SYMBOL_NS(amd_sof_acp_remove, "SND_SOC_SOF_AMD_COMMON"); - -+int amd_sof_acp7x_probe(struct snd_sof_dev *sdev) -+{ -+ struct pci_dev *pci = to_pci_dev(sdev->dev); -+ struct acp_dev_data *adata; -+ const struct sof_amd_acp_desc *chip; -+ const union acpi_object *obj; -+ struct acpi_device *adev; -+ unsigned int addr; -+ unsigned int irqflags; -+ int ret; -+ -+ chip = get_chip_info(sdev->pdata); -+ if (!chip) { -+ dev_err(sdev->dev, "no such device supported, chip id:%x\n", pci->device); -+ return -EIO; -+ } -+ adata = devm_kzalloc(sdev->dev, sizeof(struct acp_dev_data), GFP_KERNEL); -+ if (!adata) -+ return -ENOMEM; -+ -+ adata->dev = sdev; -+ adata->dmic_dev = platform_device_register_data(sdev->dev, "dmic-codec", -+ PLATFORM_DEVID_NONE, NULL, 0); -+ if (IS_ERR(adata->dmic_dev)) { -+ dev_err(sdev->dev, "failed to register platform for dmic codec\n"); -+ return PTR_ERR(adata->dmic_dev); -+ } -+ -+ addr = pci_resource_start(pci, ACP_DSP_BAR); -+ sdev->bar[ACP_DSP_BAR] = devm_ioremap(sdev->dev, addr, pci_resource_len(pci, ACP_DSP_BAR)); -+ if (!sdev->bar[ACP_DSP_BAR]) { -+ dev_err(sdev->dev, "ioremap error\n"); -+ ret = -ENXIO; -+ goto unregister_dev; -+ } -+ -+ pci_set_master(pci); -+ adata->addr = addr; -+ adata->reg_range = chip->reg_end_addr - chip->reg_start_addr; -+ adata->pci_rev = pci->revision; -+ mutex_init(&adata->acp_lock); -+ sdev->pdata->hw_pdata = adata; -+ -+ ret = acp_init(sdev); -+ if (ret < 0) -+ goto unregister_dev; -+ -+ adev = ACPI_COMPANION(&pci->dev); -+ -+ sdev->ipc_irq = pci->irq; -+ irqflags = IRQF_SHARED; -+ -+ ret = request_threaded_irq(pci->irq, acp7x_irq_handler, acp_irq_thread, -+ irqflags, "AudioDSP", sdev); -+ if (ret < 0) { -+ dev_err(sdev->dev, "failed to register IRQ %d\n", sdev->ipc_irq); -+ goto unregister_dev; -+ } -+ -+ if (adev) { -+ if (!acpi_dev_get_property(adev, "acp-sof-signed-firmware-image", -+ ACPI_TYPE_INTEGER, &obj)) -+ adata->acp_sof_signed_firmware_image = obj->integer.value; -+ } -+ -+ sdev->dsp_box.offset = 0; -+ sdev->dsp_box.size = BOX_SIZE_512; -+ -+ sdev->host_box.offset = sdev->dsp_box.offset + sdev->dsp_box.size; -+ sdev->host_box.size = BOX_SIZE_512; -+ -+ sdev->debug_box.offset = sdev->host_box.offset + sdev->host_box.size; -+ sdev->debug_box.size = BOX_SIZE_1024; -+ -+ if (adata->acp_sof_signed_firmware_image) { -+ adata->fw_code_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, -+ "sof-%s-code.bin", chip->name); -+ if (!adata->fw_code_bin) { -+ ret = -ENOMEM; -+ goto free_ipc_irq; -+ } -+ adata->fw_data_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, -+ "sof-%s-data.bin", chip->name); -+ if (!adata->fw_data_bin) { -+ ret = -ENOMEM; -+ goto free_ipc_irq; -+ } -+ } -+ -+ adata->enable_fw_debug = enable_fw_debug; -+ acp_memory_init(sdev); -+ acp_dsp_stream_init(sdev); -+ -+ return 0; -+ -+free_ipc_irq: -+ free_irq(sdev->ipc_irq, sdev); -+unregister_dev: -+ platform_device_unregister(adata->dmic_dev); -+ return ret; -+} -+EXPORT_SYMBOL_NS(amd_sof_acp7x_probe, "SND_SOC_SOF_AMD_COMMON"); -+ -+void amd_sof_acp7x_remove(struct snd_sof_dev *sdev) -+{ -+ struct acp_dev_data *adata = sdev->pdata->hw_pdata; -+ -+ if (sdev->ipc_irq) -+ free_irq(sdev->ipc_irq, sdev); -+ -+ if (adata->dmic_dev) -+ platform_device_unregister(adata->dmic_dev); -+ -+ acp_reset(sdev); -+} -+EXPORT_SYMBOL_NS(amd_sof_acp7x_remove, "SND_SOC_SOF_AMD_COMMON"); -+ -+int amd_sof_acp7x_suspend(struct snd_sof_dev *sdev, u32 target_state) -+{ -+ struct acp_dev_data *acp_data; -+ int ret; -+ bool enable = false; -+ -+ acp_data = sdev->pdata->hw_pdata; -+ -+ ret = acp_reset(sdev); -+ if (ret) { -+ dev_err(sdev->dev, "ACP Reset failed\n"); -+ return ret; -+ } -+ switch (acp_data->pci_rev) { -+ case ACP7B_PCI_ID: -+ case ACP7F_PCI_ID: -+ enable = true; -+ break; -+ default: -+ break; -+ } -+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_CONTROL, enable); -+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_ZSC_DSP_CTRL, 1); -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS(amd_sof_acp7x_suspend, "SND_SOC_SOF_AMD_COMMON"); -+ -+int amd_sof_acp7x_resume(struct snd_sof_dev *sdev) -+{ -+ struct acp_dev_data *acp_data; -+ int ret; -+ -+ acp_data = sdev->pdata->hw_pdata; -+ -+ ret = acp_init(sdev); -+ if (ret) { -+ dev_err(sdev->dev, "ACP Init failed\n"); -+ return ret; -+ } -+ ret = acp_memory_init(sdev); -+ if (ret) { -+ dev_err(sdev->dev, "ACP Memory init failed\n"); -+ return ret; -+ } -+ -+ switch (acp_data->pci_rev) { -+ case ACP7B_PCI_ID: -+ case ACP7F_PCI_ID: -+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_PME_EN, 1); -+ break; -+ default: -+ break; -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS(amd_sof_acp7x_resume, "SND_SOC_SOF_AMD_COMMON"); -+ -+int amd_sof_acp7x_suspend_runtime(struct snd_sof_dev *sdev) -+{ -+ return amd_sof_acp7x_suspend(sdev, 0); -+} -+EXPORT_SYMBOL_NS(amd_sof_acp7x_suspend_runtime, "SND_SOC_SOF_AMD_COMMON"); -+ -+int amd_sof_acp7x_resume_runtime(struct snd_sof_dev *sdev) -+{ -+ return amd_sof_acp7x_resume(sdev); -+} -+EXPORT_SYMBOL_NS(amd_sof_acp7x_resume_runtime, "SND_SOC_SOF_AMD_COMMON"); -+ - MODULE_LICENSE("Dual BSD/GPL"); - MODULE_DESCRIPTION("AMD ACP sof driver"); - MODULE_IMPORT_NS("SOUNDWIRE_AMD_INIT"); -diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h ---- a/sound/soc/sof/amd/acp.h -+++ b/sound/soc/sof/amd/acp.h -@@ -76,6 +76,12 @@ - #define ACP70_PCI_ID 0x70 - #define ACP71_PCI_ID 0x71 - #define ACP72_PCI_ID 0x72 -+#define ACP7B_PCI_ID 0x7B -+#define ACP7F_PCI_ID 0x7F -+ -+#define ACP7X_PGFSM_CNTL_POWER_ON_MASK 0x7F -+#define ACP7X_PGFSM_STATUS_MASK 0xFFF -+#define ACP7X_SRAM_PTE_OFFSET ACP6X_SRAM_PTE_OFFSET - - #define HOST_BRIDGE_CZN 0x1630 - #define HOST_BRIDGE_VGH 0x1645 -@@ -107,6 +113,9 @@ - #define PROBE_STATUS_BIT BIT(31) - - #define ACP_FIRMWARE_SIGNATURE 0x100 -+#define ACP_IMAGE_HEADER_SIZE ACP_FIRMWARE_SIGNATURE -+#define ACP_IMAGE_HDR_SIZE_FW_SIGNED_OFF 0x14 -+ - #define ACP_ERROR_IRQ_MASK BIT(29) - #define ACP_SDW0_IRQ_MASK BIT(21) - #define ACP_SDW1_IRQ_MASK BIT(2) -@@ -271,6 +280,7 @@ struct acp_dev_data { - /* acp70_sdw1_wake_event flag set to true when wake irq asserted for SW1 instance */ - bool acp70_sdw1_wake_event; - unsigned int pci_rev; -+ int acp_sof_signed_firmware_image; - }; - - void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes); -@@ -344,6 +354,16 @@ int sof_acp63_ops_init(struct snd_sof_dev *sdev); - extern struct snd_sof_dsp_ops sof_acp70_ops; - int sof_acp70_ops_init(struct snd_sof_dev *sdev); - -+extern struct snd_sof_dsp_ops sof_acp7x_ops; -+int sof_acp7x_ops_init(struct snd_sof_dev *sdev); -+ -+int amd_sof_acp7x_probe(struct snd_sof_dev *sdev); -+void amd_sof_acp7x_remove(struct snd_sof_dev *sdev); -+int amd_sof_acp7x_suspend(struct snd_sof_dev *sdev, u32 target_state); -+int amd_sof_acp7x_resume(struct snd_sof_dev *sdev); -+int amd_sof_acp7x_suspend_runtime(struct snd_sof_dev *sdev); -+int amd_sof_acp7x_resume_runtime(struct snd_sof_dev *sdev); -+ - struct snd_soc_acpi_mach *amd_sof_machine_select(struct snd_sof_dev *sdev); - /* Machine configuration */ - int snd_amd_acp_find_config(struct pci_dev *pci); -diff --git a/sound/soc/sof/amd/acp7x.c b/sound/soc/sof/amd/acp7x.c -new file mode 100644 ---- /dev/null -+++ b/sound/soc/sof/amd/acp7x.c -@@ -0,0 +1,185 @@ -+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) -+// -+// This file is provided under a dual BSD/GPLv2 license. When using or -+// redistributing this file, you may do so under either license. -+// -+// Copyright(c) 2025 Advanced Micro Devices, Inc. -+// -+// Authors: Vijendar Mukunda -+ -+/* -+ * Hardware interface for Audio DSP on ACP7.B/7.F platforms -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include "../ops.h" -+#include "../sof-audio.h" -+#include "acp.h" -+#include "acp-dsp-offset.h" -+ -+#define I2S_TDM0_INSTANCE 0 -+#define I2S_TDM1_INSTANCE 1 -+#define I2S_TDM2_INSTANCE 2 -+#define PDM0_DMIC_INSTANCE 3 -+#define PDM1_DMIC_INSTANCE 4 -+ -+static struct snd_soc_dai_driver acp7x_sof_dai[] = { -+ [I2S_TDM0_INSTANCE] = { -+ .id = I2S_TDM0_INSTANCE, -+ .name = "acp-sof-i2s0", -+ .playback = { -+ .rates = SNDRV_PCM_RATE_8000_96000, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | -+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, -+ .channels_min = 2, -+ .channels_max = 8, -+ .rate_min = 8000, -+ .rate_max = 96000, -+ }, -+ .capture = { -+ .rates = SNDRV_PCM_RATE_8000_48000, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | -+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, -+ /* Supporting only stereo for I2S HS controller capture */ -+ .channels_min = 2, -+ .channels_max = 2, -+ .rate_min = 8000, -+ .rate_max = 48000, -+ }, -+ }, -+ -+ [I2S_TDM1_INSTANCE] = { -+ .id = I2S_TDM1_INSTANCE, -+ .name = "acp-sof-i2s1", -+ .playback = { -+ .rates = SNDRV_PCM_RATE_8000_96000, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | -+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, -+ .channels_min = 2, -+ .channels_max = 8, -+ .rate_min = 8000, -+ .rate_max = 96000, -+ }, -+ .capture = { -+ .rates = SNDRV_PCM_RATE_8000_48000, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | -+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, -+ /* Supporting only stereo for I2S BT controller capture */ -+ .channels_min = 2, -+ .channels_max = 2, -+ .rate_min = 8000, -+ .rate_max = 48000, -+ }, -+ }, -+ -+ [I2S_TDM2_INSTANCE] = { -+ .id = I2S_TDM2_INSTANCE, -+ .name = "acp-sof-i2s2", -+ .playback = { -+ .rates = SNDRV_PCM_RATE_8000_96000, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | -+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, -+ .channels_min = 2, -+ .channels_max = 8, -+ .rate_min = 8000, -+ .rate_max = 96000, -+ }, -+ .capture = { -+ .rates = SNDRV_PCM_RATE_8000_48000, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | -+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, -+ /* Supporting only stereo for I2S SP controller capture */ -+ .channels_min = 2, -+ .channels_max = 2, -+ .rate_min = 8000, -+ .rate_max = 48000, -+ }, -+ }, -+ -+ [PDM0_DMIC_INSTANCE] = { -+ .id = PDM0_DMIC_INSTANCE, -+ .name = "acp-sof-dmic0", -+ .capture = { -+ .rates = SNDRV_PCM_RATE_8000_48000, -+ .formats = SNDRV_PCM_FMTBIT_S32_LE, -+ .channels_min = 2, -+ .channels_max = 4, -+ .rate_min = 8000, -+ .rate_max = 48000, -+ }, -+ }, -+ -+ [PDM1_DMIC_INSTANCE] = { -+ .id = PDM1_DMIC_INSTANCE, -+ .name = "acp-sof-dmic1", -+ .capture = { -+ .rates = SNDRV_PCM_RATE_8000_96000, -+ .formats = SNDRV_PCM_FMTBIT_S32_LE, -+ .channels_min = 2, -+ .channels_max = 4, -+ .rate_min = 8000, -+ .rate_max = 96000, -+ }, -+ }, -+}; -+ -+static int sof_acp7x_post_fw_run_delay(struct snd_sof_dev *sdev) -+{ -+ /* -+ * Resuming from suspend in some cases may cause the DSP firmware -+ * to enter an unrecoverable faulty state. Delaying a bit any host -+ * to DSP transmission right after firmware boot completion seems -+ * to resolve the issue. -+ */ -+ if (!sdev->first_boot) -+ usleep_range(100, 150); -+ -+ return 0; -+} -+ -+struct snd_sof_dsp_ops sof_acp7x_ops; -+EXPORT_SYMBOL_NS(sof_acp7x_ops, "SND_SOC_SOF_AMD_COMMON"); -+ -+int sof_acp7x_ops_init(struct snd_sof_dev *sdev) -+{ -+ struct acpi_device *adev = ACPI_COMPANION(&to_pci_dev(sdev->dev)->dev); -+ const union acpi_object *obj; -+ int acp_sof_signed_firmware_image = 0; -+ int acp_sof_post_fw_run_delay = 0; -+ -+ /* common defaults */ -+ memcpy(&sof_acp7x_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops)); -+ -+ sof_acp7x_ops.drv = acp7x_sof_dai; -+ sof_acp7x_ops.num_drv = ARRAY_SIZE(acp7x_sof_dai); -+ sof_acp7x_ops.probe = amd_sof_acp7x_probe; -+ sof_acp7x_ops.remove = amd_sof_acp7x_remove; -+ -+ if (adev) { -+ if (!acpi_dev_get_property(adev, "acp-sof-signed-firmware-image", -+ ACPI_TYPE_INTEGER, &obj)) -+ acp_sof_signed_firmware_image = obj->integer.value; -+ -+ if (!acpi_dev_get_property(adev, "acp-sof-post_fw_run_delay", -+ ACPI_TYPE_INTEGER, &obj)) -+ acp_sof_post_fw_run_delay = obj->integer.value; -+ } -+ -+ if (acp_sof_signed_firmware_image) -+ sof_acp7x_ops.load_firmware = acp_sof_load_signed_firmware; -+ -+ if (acp_sof_post_fw_run_delay) -+ sof_acp7x_ops.post_fw_run = sof_acp7x_post_fw_run_delay; -+ -+ sof_acp7x_ops.suspend = amd_sof_acp7x_suspend; -+ sof_acp7x_ops.resume = amd_sof_acp7x_resume; -+ sof_acp7x_ops.runtime_suspend = amd_sof_acp7x_suspend_runtime; -+ sof_acp7x_ops.runtime_resume = amd_sof_acp7x_resume_runtime; -+ -+ return 0; -+} -diff --git a/sound/soc/sof/amd/pci-acp7x.c b/sound/soc/sof/amd/pci-acp7x.c -new file mode 100644 ---- /dev/null -+++ b/sound/soc/sof/amd/pci-acp7x.c -@@ -0,0 +1,116 @@ -+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) -+// -+// This file is provided under a dual BSD/GPLv2 license. When using or -+// redistributing this file, you may do so under either license. -+// -+// Copyright(c) 2025 Advanced Micro Devices, Inc. All rights reserved. -+// -+// Authors: Vijendar Mukunda -+ -+/* -+ * PCI interface for ACP7.B/7.F devices -+ */ -+ -+#include -+#include -+#include -+#include -+ -+#include "../ops.h" -+#include "../sof-pci-dev.h" -+#include "../../amd/mach-config.h" -+#include "acp.h" -+#include "acp-dsp-offset.h" -+ -+#define ACP7X_FUTURE_REG_ACLK_0 0x18e0 -+#define ACP7X_REG_START 0x1240000 -+#define ACP7X_REG_END 0x125C000 -+ -+static const struct sof_amd_acp_desc acp7x_chip_info = { -+ .name = "acp7x", -+ .pgfsm_base = ACP7X_PGFSM_BASE, -+ .ext_intr_enb = ACP6X_EXTERNAL_INTR_ENB, -+ .ext_intr_cntl = ACP7X_EXTERNAL_INTR_CNTL, -+ .ext_intr_stat = ACP7X_EXT_INTR_STAT, -+ .ext_intr_stat1 = ACP7X_EXT_INTR_STAT1, -+ .dsp_intr_base = ACP7X_DSP_SW_INTR_BASE, -+ .acp_error_stat = ACP7X_ERROR_STATUS, -+ .sram_pte_offset = ACP7X_SRAM_PTE_OFFSET, -+ .hw_semaphore_offset = ACP7X_AXI2DAGB_SEM_0, -+ .fusion_dsp_offset = ACP7X_DSP_FUSION_RUNSTALL, -+ .probe_reg_offset = ACP7X_FUTURE_REG_ACLK_0, -+ .reg_start_addr = ACP7X_REG_START, -+ .reg_end_addr = ACP7X_REG_END, -+}; -+ -+static const struct sof_dev_desc acp7x_desc = { -+ .machines = snd_soc_acpi_amd_acp7x_sof_machines, -+ .resindex_lpe_base = 0, -+ .resindex_pcicfg_base = -1, -+ .resindex_imr_base = -1, -+ .irqindex_host_ipc = -1, -+ .chip_info = &acp7x_chip_info, -+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_3), -+ .ipc_default = SOF_IPC_TYPE_3, -+ .default_fw_path = { -+ [SOF_IPC_TYPE_3] = "amd/sof", -+ }, -+ .default_tplg_path = { -+ [SOF_IPC_TYPE_3] = "amd/sof-tplg", -+ }, -+ .default_fw_filename = { -+ [SOF_IPC_TYPE_3] = "sof-acp7x.ri", -+ }, -+ .nocodec_tplg_filename = "sof-acp.tplg", -+ .ops = &sof_acp7x_ops, -+ .ops_init = sof_acp7x_ops_init, -+}; -+ -+static int acp7x_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) -+{ -+ unsigned int flag; -+ -+ switch (pci->revision) { -+ case ACP7B_PCI_ID: -+ case ACP7F_PCI_ID: -+ break; -+ default: -+ return -ENODEV; -+ } -+ -+ flag = snd_amd_acp_find_config(pci); -+ if (flag != FLAG_AMD_SOF && flag != FLAG_AMD_SOF_ONLY_DMIC) -+ return -ENODEV; -+ -+ return sof_pci_probe(pci, pci_id); -+} -+ -+static void acp7x_pci_remove(struct pci_dev *pci) -+{ -+ sof_pci_remove(pci); -+} -+ -+/* PCI IDs */ -+static const struct pci_device_id acp7x_pci_ids[] = { -+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID), -+ .driver_data = (unsigned long)&acp7x_desc}, -+ { 0, } -+}; -+MODULE_DEVICE_TABLE(pci, acp7x_pci_ids); -+ -+/* pci_driver definition */ -+static struct pci_driver snd_sof_pci_amd_acp7x_driver = { -+ .name = KBUILD_MODNAME, -+ .id_table = acp7x_pci_ids, -+ .probe = acp7x_pci_probe, -+ .remove = acp7x_pci_remove, -+ .driver = { -+ .pm = pm_ptr(&sof_pci_pm), -+ }, -+}; -+module_pci_driver(snd_sof_pci_amd_acp7x_driver); -+ -+MODULE_LICENSE("Dual BSD/GPL"); -+MODULE_DESCRIPTION("ACP7X SOF Driver"); -+MODULE_IMPORT_NS("SND_SOC_SOF_AMD_COMMON"); -+MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV"); -diff --git a/sound/soc/sof/fw-file-profile.c b/sound/soc/sof/fw-file-profile.c ---- a/sound/soc/sof/fw-file-profile.c -+++ b/sound/soc/sof/fw-file-profile.c -@@ -16,20 +16,19 @@ static int sof_test_firmware_file(struct device *dev, - enum sof_ipc_type *ipc_type_to_adjust) - { - enum sof_ipc_type fw_ipc_type; -- const struct firmware *fw; -- const char *fw_filename; - const u32 *magic; - int ret; - -- fw_filename = kasprintf(GFP_KERNEL, "%s/%s", profile->fw_path, -- profile->fw_name); -+ const char *fw_filename __free(kfree) = -+ kasprintf(GFP_KERNEL, "%s/%s", profile->fw_path, -+ profile->fw_name); - if (!fw_filename) - return -ENOMEM; - -+ const struct firmware *fw __free(firmware) = NULL; - ret = firmware_request_nowarn(&fw, fw_filename, dev); - if (ret < 0) { - dev_dbg(dev, "Failed to open firmware file: %s\n", fw_filename); -- kfree(fw_filename); - return ret; - } - -@@ -44,8 +43,7 @@ static int sof_test_firmware_file(struct device *dev, - break; - default: - dev_err(dev, "Invalid firmware magic: %#x\n", *magic); -- ret = -EINVAL; -- goto out; -+ return -EINVAL; - } - - if (ipc_type_to_adjust) { -@@ -54,13 +52,10 @@ static int sof_test_firmware_file(struct device *dev, - dev_err(dev, - "ipc type mismatch between %s and expected: %d vs %d\n", - fw_filename, fw_ipc_type, profile->ipc_type); -- ret = -EINVAL; -+ return -EINVAL; - } --out: -- release_firmware(fw); -- kfree(fw_filename); - -- return ret; -+ return 0; - } - - static int sof_test_topology_file(struct device *dev, -diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c ---- a/sound/soc/sof/intel/hda-ctrl.c -+++ b/sound/soc/sof/intel/hda-ctrl.c -@@ -186,6 +186,7 @@ EXPORT_SYMBOL_NS(hda_dsp_ctrl_clock_power_gating, "SND_SOC_SOF_INTEL_HDA_COMMON" - int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec) - { - struct hdac_bus *bus = sof_to_bus(sdev); -+ struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); - struct hdac_stream *stream; - int sd_offset, ret = 0; - u32 gctl; -@@ -193,6 +194,16 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec) - if (bus->chip_init) - return 0; - -+ /* -+ * The controller reset clears the ACE2+ link DMA stream allocation -+ * constraints; reset the masks to reflect this. -+ */ -+ memset(sof_hda->link_dma_active_sdw_mask, 0, -+ sizeof(sof_hda->link_dma_active_sdw_mask)); -+ memset(sof_hda->link_dma_active_multi_mask, 0, -+ sizeof(sof_hda->link_dma_active_multi_mask)); -+ sof_hda->link_dma_out_hda_used_mask = 0; -+ - hda_codec_set_codec_wakeup(sdev, true); - - hda_dsp_ctrl_misc_clock_gating(sdev, false); -@@ -223,6 +234,14 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec) - /* Accept unsolicited responses */ - snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL); - -+ /* Perform a one-time enumeration of the Multi-Link capability */ -+ ret = hda_bus_ml_init(bus); -+ if (ret < 0) { -+ dev_err(sdev->dev, "%s: failed to enumerate multi-links\n", -+ __func__); -+ goto err; -+ } -+ - if (detect_codec) - hda_codec_detect_mask(sdev); - -diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c ---- a/sound/soc/sof/intel/hda-dai-ops.c -+++ b/sound/soc/sof/intel/hda-dai-ops.c -@@ -20,7 +20,7 @@ - /* These ops are only applicable for the HDA DAI's in their current form */ - #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK) - /* -- * This function checks if the host dma channel corresponding -+ * This function checks if the host DMA stream corresponding - * to the link DMA stream_tag argument is assigned to one - * of the FEs connected to the BE DAI. - */ -@@ -42,23 +42,53 @@ static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd, - } - - static struct hdac_ext_stream * --hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream) -+hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream, -+ enum hda_bus_ml_link_type link_type) - { - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); -+ struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); - struct sof_intel_hda_stream *hda_stream; - const struct sof_intel_dsp_desc *chip; - struct snd_sof_dev *sdev; - struct hdac_ext_stream *res = NULL; - struct hdac_stream *hstream = NULL; -- - int stream_dir = substream->stream; -+ bool is_multi = link_type == HDA_BUS_ML_LINK_HDA || link_type == HDA_BUS_ML_LINK_UAOL; -+ bool is_play = stream_dir == SNDRV_PCM_STREAM_PLAYBACK; -+ bool is_sdw = link_type == HDA_BUS_ML_LINK_SDW; -+ bool is_hda = link_type == HDA_BUS_ML_LINK_HDA; -+ u32 concur_block_mask = 0; -+ u32 seq_block_mask = 0; -+ unsigned int stream_idx; - - if (!bus->ppcap) { - dev_err(bus->dev, "stream type not supported\n"); - return NULL; - } - -+ /* -+ * On ACE2+ the link DMA stream allocator must avoid two HW errata, -+ * see the comment on struct sof_intel_hda_dev. -+ * -+ * - Concurrent cross-direction: SoundWire conflicts with HDA, iDisp -+ * and UAOL on the same physical stream index; SSP and DMIC are safe. -+ * - Sequential playback: a stream index previously used by an HDA/iDisp -+ * link cannot drive any non-HDA/iDisp link in the same direction -+ * until the next controller reset. -+ * -+ * The masks are protected by bus->reg_lock; sample them inside the -+ * lock together with the stream walk to keep the decision atomic -+ * with concurrent allocations and releases. -+ */ - guard(spinlock_irq)(&bus->reg_lock); -+ -+ if (is_sdw) -+ concur_block_mask = sof_hda->link_dma_active_multi_mask[!stream_dir]; -+ else if (is_multi) -+ concur_block_mask = sof_hda->link_dma_active_sdw_mask[!stream_dir]; -+ if (is_play && !is_hda) -+ seq_block_mask = sof_hda->link_dma_out_hda_used_mask; -+ - list_for_each_entry(hstream, &bus->stream_list, list) { - struct hdac_ext_stream *hext_stream = - stream_to_hdac_ext_stream(hstream); -@@ -69,6 +99,12 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream - sdev = hda_stream->sdev; - chip = get_chip_info(sdev->pdata); - -+ stream_idx = hstream->stream_tag - 1; -+ -+ /* skip streams blocked by the ACE2+ allocator constraints */ -+ if ((concur_block_mask | seq_block_mask) & BIT(stream_idx)) -+ continue; -+ - /* check if link is available */ - if (!hext_stream->link_locked) { - /* -@@ -95,7 +131,7 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream - - /* - * This must be a hostless stream. -- * So reserve the host DMA channel. -+ * So reserve the host DMA stream. - */ - hda_stream->host_reserved = 1; - break; -@@ -109,6 +145,16 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream - - res->link_locked = 1; - res->link_substream = substream; -+ -+ stream_idx = res->hstream.stream_tag - 1; -+ if (is_sdw) -+ sof_hda->link_dma_active_sdw_mask[stream_dir] |= BIT(stream_idx); -+ else if (is_multi) -+ sof_hda->link_dma_active_multi_mask[stream_dir] |= BIT(stream_idx); -+ -+ /* persistent OUT HDA/iDisp shadow, cleared only on CRST# */ -+ if (is_hda && is_play) -+ sof_hda->link_dma_out_hda_used_mask |= BIT(stream_idx); - } - - return res; -@@ -143,11 +189,13 @@ static struct hdac_ext_stream *hda_ipc4_get_hext_stream(struct snd_sof_dev *sdev - - static struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev, - struct snd_soc_dai *cpu_dai, -- struct snd_pcm_substream *substream) -+ struct snd_pcm_substream *substream, -+ struct hdac_ext_link *hlink) - { - struct hdac_ext_stream *hext_stream; -+ enum hda_bus_ml_link_type link_type = hda_bus_ml_link_get_type(hlink); - -- hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream); -+ hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream, link_type); - if (!hext_stream) - return NULL; - -@@ -160,6 +208,22 @@ static void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai - struct snd_pcm_substream *substream) - { - struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream); -+ struct sof_intel_hda_dev *sof_hda = sdev->pdata->hw_pdata; -+ struct hdac_bus *bus = sof_to_bus(sdev); -+ int dir = substream->stream; -+ unsigned int stream_idx = hext_stream->hstream.stream_tag - 1; -+ -+ /* -+ * Drop the stream index from the per-direction active concurrency masks. -+ * The two masks are mutually exclusive for a given stream/direction -+ * (and a stream of the SSP/DMIC kind appears in neither), so a blind -+ * clear of both is safe and lets us avoid having to remember the -+ * link type at allocation time. -+ */ -+ scoped_guard(spinlock_irq, &bus->reg_lock) { -+ sof_hda->link_dma_active_sdw_mask[dir] &= ~BIT(stream_idx); -+ sof_hda->link_dma_active_multi_mask[dir] &= ~BIT(stream_idx); -+ } - - snd_soc_dai_set_dma_data(cpu_dai, substream, NULL); - snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK); -diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c ---- a/sound/soc/sof/intel/hda-dai.c -+++ b/sound/soc/sof/intel/hda-dai.c -@@ -188,7 +188,7 @@ static int hda_link_dma_hw_params(struct snd_pcm_substream *substream, - - if (!hext_stream) { - if (ops->assign_hext_stream) -- hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream); -+ hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream, hlink); - } - - if (!hext_stream) -diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c ---- a/sound/soc/sof/intel/hda-dsp.c -+++ b/sound/soc/sof/intel/hda-dsp.c -@@ -1114,11 +1114,7 @@ static int hda_dsp_s5_quirk(struct snd_sof_dev *sdev) - usleep_range(500, 1000); - - /* Restore state for shutdown, back to reset */ -- ret = hda_dsp_ctrl_link_reset(sdev, true); -- if (ret < 0) -- return ret; -- -- return ret; -+ return hda_dsp_ctrl_link_reset(sdev, true); - } - - int hda_dsp_shutdown_dma_flush(struct snd_sof_dev *sdev) -diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c ---- a/sound/soc/sof/intel/hda-loader.c -+++ b/sound/soc/sof/intel/hda-loader.c -@@ -216,9 +216,10 @@ int hda_cl_trigger(struct device *dev, struct hdac_ext_stream *hext_stream, int - EXPORT_SYMBOL_NS(hda_cl_trigger, "SND_SOC_SOF_INTEL_HDA_COMMON"); - - int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab, -- bool persistent_buffer, struct hdac_ext_stream *hext_stream) -+ bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool is_iccmax) - { -- return hda_data_stream_cleanup(dev, dmab, persistent_buffer, hext_stream, false); -+ return hda_data_stream_cleanup(dev, dmab, persistent_buffer, hext_stream, -+ is_iccmax, false); - } - EXPORT_SYMBOL_NS(hda_cl_cleanup, "SND_SOC_SOF_INTEL_HDA_COMMON"); - -@@ -302,7 +303,7 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev) - * If the cleanup also fails, we return the initial error - */ - ret1 = hda_cl_cleanup(sdev->dev, &hda->iccmax_dmab, -- persistent_cl_buffer, iccmax_stream); -+ persistent_cl_buffer, iccmax_stream, true); - if (ret1 < 0) { - dev_err(sdev->dev, "error: ICCMAX stream cleanup failed\n"); - -@@ -458,7 +459,7 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) - * If the cleanup also fails, we return the initial error - */ - ret1 = hda_cl_cleanup(sdev->dev, &hda->cl_dmab, -- persistent_cl_buffer, hext_stream); -+ persistent_cl_buffer, hext_stream, false); - if (ret1 < 0) { - dev_err(sdev->dev, "error: Code loader DSP cleanup failed\n"); - -@@ -587,7 +588,7 @@ int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev, - cleanup: - /* clean up even in case of error and return the first error */ - ret1 = hda_cl_cleanup(sdev->dev, &hda->cl_dmab, persistent_cl_buffer, -- hext_stream); -+ hext_stream, false); - if (ret1 < 0) { - dev_err(sdev->dev, "%s: Code loader DSP cleanup failed\n", __func__); - -diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c ---- a/sound/soc/sof/intel/hda-mlink.c -+++ b/sound/soc/sof/intel/hda-mlink.c -@@ -432,6 +432,10 @@ int hda_bus_ml_init(struct hdac_bus *bus) - if (!bus->mlcap) - return 0; - -+ /* Enumeration is a one time operation, skip if already done */ -+ if (!list_empty(&bus->hlink_list)) -+ return 0; -+ - link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1; - - dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count); -@@ -880,19 +884,6 @@ int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, - return 0; - } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_map_stream_ch, "SND_SOC_SOF_HDA_MLINK"); - --void hda_bus_ml_put_all(struct hdac_bus *bus) --{ -- struct hdac_ext_link *hlink; -- -- list_for_each_entry(hlink, &bus->hlink_list, list) { -- struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); -- -- if (!h2link->alt) -- snd_hdac_ext_bus_link_put(bus, hlink); -- } --} --EXPORT_SYMBOL_NS(hda_bus_ml_put_all, "SND_SOC_SOF_HDA_MLINK"); -- - void hda_bus_ml_reset_losidv(struct hdac_bus *bus) - { - struct hdac_ext_link *hlink; -@@ -903,6 +894,24 @@ void hda_bus_ml_reset_losidv(struct hdac_bus *bus) - } - EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, "SND_SOC_SOF_HDA_MLINK"); - -+enum hda_bus_ml_link_type hda_bus_ml_link_get_type(struct hdac_ext_link *hlink) -+{ -+ struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); -+ -+ if (!h2link->alt) -+ return HDA_BUS_ML_LINK_HDA; -+ -+ switch (h2link->elid) { -+ case AZX_REG_ML_LEPTR_ID_SDW: -+ return HDA_BUS_ML_LINK_SDW; -+ case AZX_REG_ML_LEPTR_ID_INTEL_UAOL: -+ return HDA_BUS_ML_LINK_UAOL; -+ default: -+ return HDA_BUS_ML_LINK_OTHER; -+ } -+} -+EXPORT_SYMBOL_NS(hda_bus_ml_link_get_type, "SND_SOC_SOF_HDA_MLINK"); -+ - int hda_bus_ml_resume(struct hdac_bus *bus) - { - struct hdac_ext_link *hlink; -diff --git a/sound/soc/sof/intel/hda-sdw-bpt.c b/sound/soc/sof/intel/hda-sdw-bpt.c ---- a/sound/soc/sof/intel/hda-sdw-bpt.c -+++ b/sound/soc/sof/intel/hda-sdw-bpt.c -@@ -163,7 +163,7 @@ static int hda_sdw_bpt_dma_deprepare(struct device *dev, struct hdac_ext_stream - u32 mask; - int ret; - -- ret = hda_data_stream_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream, true); -+ ret = hda_data_stream_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream, false, true); - if (ret < 0) { - dev_err(sdev->dev, "%s: SDW BPT DMA cleanup failed\n", - __func__); -diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c ---- a/sound/soc/sof/intel/hda-stream.c -+++ b/sound/soc/sof/intel/hda-stream.c -@@ -198,13 +198,18 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, - - mask = (1 << hstream->index); - -+ /* Reset the spib_addr before disabling SPIB */ -+ if (!enable) -+ sof_io_write(sdev, hstream->spib_addr, 0); -+ - /* enable/disable SPIB for the stream */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_SPIB_BAR, - SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCCTL, mask, - enable << hstream->index); - - /* set the SPIB value */ -- sof_io_write(sdev, hstream->spib_addr, size); -+ if (enable) -+ sof_io_write(sdev, hstream->spib_addr, size); - - return 0; - } -@@ -608,6 +613,9 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, - return ret; - } - -+ /* Host DMA is not running */ -+ hstream->running = false; -+ - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, - sd_offset + SOF_HDA_ADSP_REG_SD_STS, - SOF_HDA_CL_DMA_SD_INT_MASK, -@@ -1323,16 +1331,18 @@ hda_data_stream_prepare(struct device *dev, unsigned int format, unsigned int si - EXPORT_SYMBOL_NS(hda_data_stream_prepare, "SND_SOC_SOF_INTEL_HDA_COMMON"); - - int hda_data_stream_cleanup(struct device *dev, struct snd_dma_buffer *dmab, -- bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool pair) -+ bool persistent_buffer, struct hdac_ext_stream *hext_stream, -+ bool is_iccmax, bool pair) - { - struct snd_sof_dev *sdev = dev_get_drvdata(dev); - struct hdac_stream *hstream = hdac_stream(hext_stream); - int sd_offset = SOF_STREAM_SD_OFFSET(hstream); - int ret = 0; - -- if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK) -+ if (!is_iccmax) - ret = hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0); -- else -+ -+ if (hstream->direction == SNDRV_PCM_STREAM_CAPTURE) - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, - SOF_HDA_SD_CTL_DMA_START, 0); - -diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c ---- a/sound/soc/sof/intel/hda.c -+++ b/sound/soc/sof/intel/hda.c -@@ -421,6 +421,20 @@ static inline void hda_dsp_sdw_process_mic_privacy(struct snd_sof_dev *sdev) { } - /* pre fw run operations */ - int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev) - { -+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; -+ const struct sof_intel_dsp_desc *chip = hda->desc; -+ int ret; -+ -+ /* Power down DSP if left enabled to ensure a clean boot state. */ -+ if (hda_dsp_core_is_enabled(sdev, chip->host_managed_cores_mask)) { -+ dev_dbg(sdev->dev, "DSP core enabled, power down DSP first\n"); -+ -+ ret = chip->power_down_dsp(sdev); -+ if (ret < 0) -+ dev_warn(sdev->dev, -+ "%s: failed to power down already-enabled DSP\n", __func__); -+ } -+ - /* disable clock gating and power gating */ - return hda_dsp_ctrl_clock_power_gating(sdev, false); - } -@@ -625,8 +639,6 @@ static int hda_init_caps(struct snd_sof_dev *sdev) - return ret; - } - -- hda_bus_ml_init(bus); -- - /* Skip SoundWire if it is not supported */ - if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH))) - goto skip_soundwire; -@@ -670,8 +682,6 @@ static int hda_init_caps(struct snd_sof_dev *sdev) - if (!HDA_IDISP_CODEC(bus->codec_mask)) - hda_codec_i915_display_power(sdev, false); - -- hda_bus_ml_put_all(bus); -- - return 0; - } - -diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h ---- a/sound/soc/sof/intel/hda.h -+++ b/sound/soc/sof/intel/hda.h -@@ -523,6 +523,29 @@ struct sof_intel_hda_dev { - /* the maximum number of streams (playback + capture) supported */ - u32 stream_max; - -+ /* -+ * ACE2+ link DMA stream allocation constraints (stream index = -+ * stream_tag - 1, shared between input and output directions). All -+ * masks are cleared by hda_dsp_ctrl_init_chip() on controller reset -+ * (CRST#). -+ * -+ * - Concurrent (cross-direction) constraint: a SoundWire stream and -+ * a HDA/iDisp/UAOL stream cannot share a physical stream index -+ * across directions, the resulting LLP/timestamp values are wrong. -+ * link_dma_active_sdw_mask and link_dma_active_multi_mask -+ * (indexed by SNDRV_PCM_STREAM_*) track currently allocated -+ * streams per direction in each of the conflicting groups; SSP -+ * and DMIC do not participate. Bits are cleared on stream release. -+ * -+ * - Sequential (playback only) constraint: once a HDA/iDisp link -+ * has used a playback stream index, that index cannot drive a -+ * non-HDA/iDisp link in the same direction until the next CRST#. -+ * link_dma_out_hda_used_mask records this. -+ */ -+ u32 link_dma_active_sdw_mask[SNDRV_PCM_STREAM_LAST + 1]; -+ u32 link_dma_active_multi_mask[SNDRV_PCM_STREAM_LAST + 1]; -+ u32 link_dma_out_hda_used_mask; -+ - /* PM related */ - bool l1_disabled;/* is DMI link L1 disabled? */ - -@@ -739,7 +762,7 @@ struct hdac_ext_stream *hda_cl_prepare(struct device *dev, unsigned int format, - int hda_cl_trigger(struct device *dev, struct hdac_ext_stream *hext_stream, int cmd); - - int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab, -- bool persistent_buffer, struct hdac_ext_stream *hext_stream); -+ bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool is_iccmax); - int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot); - #define HDA_CL_STREAM_FORMAT 0x40 - -@@ -911,7 +934,8 @@ hda_data_stream_prepare(struct device *dev, unsigned int format, unsigned int si - bool is_iccmax, bool pair); - - int hda_data_stream_cleanup(struct device *dev, struct snd_dma_buffer *dmab, -- bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool pair); -+ bool persistent_buffer, struct hdac_ext_stream *hext_stream, -+ bool is_iccmax, bool pair); - - /* common dai driver */ - extern struct snd_soc_dai_driver skl_dai[]; -@@ -1030,7 +1054,8 @@ struct hda_dai_widget_dma_ops { - struct snd_pcm_substream *substream); - struct hdac_ext_stream *(*assign_hext_stream)(struct snd_sof_dev *sdev, - struct snd_soc_dai *cpu_dai, -- struct snd_pcm_substream *substream); -+ struct snd_pcm_substream *substream, -+ struct hdac_ext_link *hlink); - void (*release_hext_stream)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, - struct snd_pcm_substream *substream); - void (*setup_hext_stream)(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream, -diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c ---- a/sound/soc/sof/intel/icl.c -+++ b/sound/soc/sof/intel/icl.c -@@ -9,10 +9,14 @@ - * Hardware interface for audio DSP on IceLake. - */ - --#include --#include --#include -+#include - #include -+#include -+#include -+#include -+#include -+#include -+ - #include "../ipc4-priv.h" - #include "../ops.h" - #include "hda.h" -diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c ---- a/sound/soc/sof/intel/mtl.c -+++ b/sound/soc/sof/intel/mtl.c -@@ -236,6 +236,17 @@ int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable) - } - EXPORT_SYMBOL_NS(mtl_enable_interrupts, "SND_SOC_SOF_INTEL_MTL"); - -+static bool mtl_dsp_is_enabled(struct snd_sof_dev *sdev) -+{ -+ int val; -+ -+ val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_HFDSSCS); -+ if (val & MTL_HFDSSCS_CPA_MASK) -+ return true; -+ -+ return false; -+} -+ - /* pre fw run operations */ - static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev) - { -@@ -249,6 +260,18 @@ static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev) - u32 dsppwrsts; - const struct sof_intel_dsp_desc *chip; - -+ /* Power down the DSP if it is left enabled to ensure clean boot state */ -+ if (mtl_dsp_is_enabled(sdev)) { -+ dev_dbg(sdev->dev, "powering down DSP first\n"); -+ -+ ret = mtl_power_down_dsp(sdev); -+ if (ret < 0) { -+ dev_warn(sdev->dev, -+ "%s: failed to power down already-enabled DSP\n", __func__); -+ /* Continue anyway to attempt recovery */ -+ } -+ } -+ - chip = get_chip_info(sdev->pdata); - if (chip->hw_ip_version > SOF_INTEL_ACE_2_0) { - dsppwrctl = PTL_HFPWRCTL2; -diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c ---- a/sound/soc/sof/ipc3-pcm.c -+++ b/sound/soc/sof/ipc3-pcm.c -@@ -421,6 +421,12 @@ static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, - dev_dbg(component->dev, "AMD_SDW channels_min: %d channels_max: %d\n", - channels->min, channels->max); - break; -+ case SOF_DAI_AMD_I2S: -+ rate->min = private->dai_config->acp_i2s.fsync_rate; -+ rate->max = private->dai_config->acp_i2s.fsync_rate; -+ channels->min = private->dai_config->acp_i2s.tdm_slots; -+ channels->max = private->dai_config->acp_i2s.tdm_slots; -+ break; - default: - dev_err(component->dev, "Invalid DAI type %d\n", private->dai_config->type); - break; -diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c ---- a/sound/soc/sof/ipc3-topology.c -+++ b/sound/soc/sof/ipc3-topology.c -@@ -281,7 +281,10 @@ static const struct sof_topology_token acpdmic_tokens[] = { - offsetof(struct sof_ipc_dai_acpdmic_params, pdm_ch)}, - }; - --/* ACPI2S */ -+/* -+ * ACPI2S tokens fill struct sof_ipc_dai_acp_params; SOF_DAI_AMD_I2S (ACPTDM -+ * on ACP7.B/7.F) reuses this tuple group rather than defining a parallel set. -+ */ - static const struct sof_topology_token acpi2s_tokens[] = { - {SOF_TKN_AMD_ACPI2S_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_acp_params, fsync_rate)}, -@@ -289,6 +292,8 @@ static const struct sof_topology_token acpi2s_tokens[] = { - offsetof(struct sof_ipc_dai_acp_params, tdm_slots)}, - {SOF_TKN_AMD_ACPI2S_TDM_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_acp_params, tdm_mode)}, -+ {SOF_TKN_AMD_ACPI2S_FORMAT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, -+ offsetof(struct sof_ipc_dai_acp_params, format)}, - }; - - /* MICFIL PDM */ -@@ -1363,6 +1368,35 @@ static int sof_link_acp_sdw_load(struct snd_soc_component *scomp, struct snd_sof - return 0; - } - -+static int sof_link_acp_i2s_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, -+ struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) -+{ -+ struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; -+ struct sof_dai_private_data *private = dai->private; -+ u32 size = sizeof(*config); -+ int ret; -+ -+ /* handle master/slave and inverted clocks */ -+ sof_dai_set_format(hw_config, config); -+ -+ /* init IPC */ -+ memset(&config->acp_i2s, 0, sizeof(config->acp_i2s)); -+ config->hdr.size = size; -+ -+ ret = sof_update_ipc_object(scomp, &config->acp_i2s, SOF_ACPI2S_TOKENS, slink->tuples, -+ slink->num_tuples, size, slink->num_hw_configs); -+ if (ret < 0) -+ return ret; -+ -+ dai->number_configs = 1; -+ dai->current_config = 0; -+ private->dai_config = kmemdup(config, size, GFP_KERNEL); -+ if (!private->dai_config) -+ return -ENOMEM; -+ -+ return 0; -+} -+ - static int sof_link_afe_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, - struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) - { -@@ -1692,6 +1726,9 @@ static int sof_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget) - case SOF_DAI_AMD_SDW: - ret = sof_link_acp_sdw_load(scomp, slink, config, dai); - break; -+ case SOF_DAI_AMD_I2S: -+ ret = sof_link_acp_i2s_load(scomp, slink, config, dai); -+ break; - default: - break; - } -diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c ---- a/sound/soc/sof/ipc4-topology.c -+++ b/sound/soc/sof/ipc4-topology.c -@@ -163,11 +163,11 @@ static const struct sof_topology_token comp_ext_tokens[] = { - {SOF_TKN_COMP_SCHED_DOMAIN, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_domain, - offsetof(struct snd_sof_widget, comp_domain)}, - {SOF_TKN_COMP_DOMAIN_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, -- offsetof(struct snd_sof_widget, dp_domain_id)}, -+ offsetof(struct snd_sof_widget, domain_id)}, - {SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, -- offsetof(struct snd_sof_widget, dp_heap_bytes)}, -+ offsetof(struct snd_sof_widget, heap_bytes)}, - {SOF_TKN_COMP_STACK_BYTES_REQUIREMENT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, -- offsetof(struct snd_sof_widget, dp_stack_bytes)}, -+ offsetof(struct snd_sof_widget, stack_bytes)}, - }; - - static const struct sof_topology_token gain_tokens[] = { -@@ -406,6 +406,39 @@ sof_ipc4_get_input_pin_audio_fmt(struct snd_sof_widget *swidget, int pin_index) - return NULL; - } - -+static void -+sof_ipc4_evaluate_params_change(struct sof_ipc4_available_audio_format *available_fmt) -+{ -+ struct sof_ipc4_audio_format *fmt; -+ u32 in_rate, in_channels, in_valid_bits; -+ u32 out_rate, out_channels, out_valid_bits; -+ u32 changed_params = 0; -+ int i, j; -+ -+ for (i = 0; i < available_fmt->num_input_formats; i++) { -+ fmt = &available_fmt->input_pin_fmts[i].audio_fmt; -+ in_rate = fmt->sampling_frequency; -+ in_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg); -+ in_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); -+ -+ for (j = 0; j < available_fmt->num_output_formats; j++) { -+ fmt = &available_fmt->output_pin_fmts[j].audio_fmt; -+ out_rate = fmt->sampling_frequency; -+ out_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg); -+ out_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); -+ -+ if (in_rate != out_rate) -+ changed_params |= BIT(SNDRV_PCM_HW_PARAM_RATE); -+ if (in_channels != out_channels) -+ changed_params |= BIT(SNDRV_PCM_HW_PARAM_CHANNELS); -+ if (in_valid_bits != out_valid_bits) -+ changed_params |= BIT(SNDRV_PCM_HW_PARAM_FORMAT); -+ } -+ } -+ -+ available_fmt->changed_params = changed_params; -+} -+ - /** - * sof_ipc4_get_audio_fmt - get available audio formats from swidget->tuples - * @scomp: pointer to pointer to SOC component -@@ -497,6 +530,8 @@ static int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp, - available_fmt->num_output_formats); - } - -+ sof_ipc4_evaluate_params_change(available_fmt); -+ - return 0; - - err_out: -@@ -661,6 +696,9 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget) - if (ret) - goto free_copier; - -+ /* Copier can only change format */ -+ available_fmt->changed_params &= BIT(SNDRV_PCM_HW_PARAM_FORMAT); -+ - /* - * This callback is used by host copier and module-to-module copier, - * and only host copier needs to set gtw_cfg. -@@ -789,6 +827,9 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) - if (ret) - goto free_copier; - -+ /* Copier can only change format */ -+ available_fmt->changed_params &= BIT(SNDRV_PCM_HW_PARAM_FORMAT); -+ - ret = sof_update_ipc_object(scomp, &node_type, - SOF_COPIER_TOKENS, swidget->tuples, - swidget->num_tuples, sizeof(node_type), 1); -@@ -1127,6 +1168,7 @@ static int sof_ipc4_widget_setup_comp_src(struct snd_sof_widget *swidget) - "Invalid number of formats: input: %d, output: %d\n", - src->available_fmt.num_input_formats, - src->available_fmt.num_output_formats); -+ ret = -EINVAL; - goto err; - } - -@@ -1179,6 +1221,7 @@ static int sof_ipc4_widget_setup_comp_asrc(struct snd_sof_widget *swidget) - "Invalid number of formats: input: %d, output: %d\n", - asrc->available_fmt.num_input_formats, - asrc->available_fmt.num_output_formats); -+ ret = -EINVAL; - goto err; - } - -@@ -1369,6 +1412,22 @@ sof_ipc4_update_resource_usage(struct snd_sof_dev *sdev, struct snd_sof_widget * - pipeline = pipe_widget->private; - pipeline->mem_usage += total; - -+ /* -+ * If this is not a Data Processing module instance, add the -+ * required heap sizes to the sum of all module instances belonging -+ * to the same pipeline, and find the maximum stack requirement -+ * among all module instances belonging to the same pipeline. -+ */ -+ if (swidget->comp_domain != SOF_COMP_DOMAIN_DP) { -+ pipe_widget->heap_bytes += swidget->heap_bytes; -+ if (pipe_widget->stack_bytes < swidget->stack_bytes) -+ pipe_widget->stack_bytes = swidget->stack_bytes; -+ -+ dev_dbg(sdev->dev, "%s mem reqs to %s heap %u stack %u", -+ swidget->widget->name, pipe_widget->widget->name, -+ pipe_widget->heap_bytes, pipe_widget->stack_bytes); -+ } -+ - /* Update base_config->cpc from the module manifest */ - sof_ipc4_update_cpc_from_manifest(sdev, fw_module, base_config); - -@@ -1686,6 +1745,8 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget) - pipe_widget = swidget->spipe->pipe_widget; - pipeline = pipe_widget->private; - pipeline->mem_usage = 0; -+ pipe_widget->heap_bytes = 0; -+ pipe_widget->stack_bytes = 0; - - if (WIDGET_IS_AIF(swidget->id) || swidget->id == snd_soc_dapm_buffer) { - if (pipeline->use_chain_dma) { -@@ -2063,10 +2124,57 @@ static void sof_ipc4_host_config(struct snd_sof_dev *sdev, struct snd_sof_widget - } - - static int --sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, -- struct snd_pcm_hw_params *fe_params, -- struct snd_sof_platform_stream_params *platform_params, -- struct snd_pcm_hw_params *pipeline_params, int dir) -+sof_ipc4_copier_module_update_params(struct snd_sof_widget *swidget, -+ struct snd_pcm_hw_params *pipeline_params) -+{ -+ struct snd_soc_component *scomp = swidget->scomp; -+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); -+ struct sof_ipc4_copier_data *copier_data; -+ struct sof_ipc4_copier *ipc4_copier; -+ -+ switch (swidget->id) { -+ case snd_soc_dapm_aif_in: -+ case snd_soc_dapm_aif_out: -+ case snd_soc_dapm_buffer: -+ ipc4_copier = swidget->private; -+ copier_data = &ipc4_copier->data; -+ break; -+ case snd_soc_dapm_dai_in: -+ case snd_soc_dapm_dai_out: -+ { -+ struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; -+ struct sof_ipc4_pipeline *pipeline = pipe_widget->private; -+ struct snd_sof_dai *dai; -+ -+ if (pipeline->use_chain_dma) -+ return 0; -+ -+ dai = swidget->private; -+ -+ ipc4_copier = (struct sof_ipc4_copier *)dai->private; -+ copier_data = &ipc4_copier->data; -+ -+ break; -+ } -+ default: -+ dev_err(sdev->dev, "unsupported type %d for copier %s", -+ swidget->id, swidget->widget->name); -+ return -EINVAL; -+ } -+ -+ /* modify the input params for the next widget */ -+ return sof_ipc4_update_hw_params(sdev, pipeline_params, -+ &copier_data->out_format, -+ BIT(SNDRV_PCM_HW_PARAM_FORMAT) | -+ BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | -+ BIT(SNDRV_PCM_HW_PARAM_RATE)); -+} -+ -+static int -+_sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, -+ struct snd_pcm_hw_params *fe_params, -+ struct snd_sof_platform_stream_params *platform_params, -+ struct snd_pcm_hw_params *pipeline_params, int dir) - { - struct sof_ipc4_available_audio_format *available_fmt; - struct snd_soc_component *scomp = swidget->scomp; -@@ -2538,6 +2646,21 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, - return 0; - } - -+static int -+sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, -+ struct snd_pcm_hw_params *fe_params, -+ struct snd_sof_platform_stream_params *platform_params, -+ struct snd_pcm_hw_params *pipeline_params, int dir) -+{ -+ if (swidget->prepared) -+ return sof_ipc4_copier_module_update_params(swidget, -+ pipeline_params); -+ -+ return _sof_ipc4_prepare_copier_module(swidget, fe_params, -+ platform_params, pipeline_params, -+ dir); -+} -+ - static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, - struct snd_pcm_hw_params *fe_params, - struct snd_sof_platform_stream_params *platform_params, -@@ -2551,6 +2674,10 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, - u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type; - int input_fmt_index, output_fmt_index; - -+ /* This cannot happen */ -+ if (unlikely(swidget->prepared)) -+ return 0; -+ - input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, - &gain->data.base_config, - pipeline_params, -@@ -2596,6 +2723,10 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget, - u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type; - int input_fmt_index, output_fmt_index; - -+ /* Already prepared, nothing to do */ -+ if (swidget->prepared) -+ return 0; -+ - input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, - &mixer->base_config, - pipeline_params, -@@ -2642,6 +2773,10 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, - u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type; - int output_fmt_index, input_fmt_index; - -+ /* This cannot happen */ -+ if (unlikely(swidget->prepared)) -+ return 0; -+ - input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, - &src->data.base_config, - pipeline_params, -@@ -2808,6 +2943,18 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, - int ret; - - if (available_fmt->num_input_formats) { -+ if (swidget->prepared) { -+ if (!available_fmt->num_output_formats) -+ return 0; -+ -+ /* modify the pipeline params with the output format */ -+ return sof_ipc4_update_hw_params(sdev, pipeline_params, -+ &process->output_format, -+ BIT(SNDRV_PCM_HW_PARAM_FORMAT) | -+ BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | -+ BIT(SNDRV_PCM_HW_PARAM_RATE)); -+ } -+ - input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, - &process->base_config, - pipeline_params, -@@ -2820,39 +2967,69 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, - if (available_fmt->num_output_formats) { - struct sof_ipc4_audio_format *in_fmt; - struct sof_ipc4_pin_format *pin_fmt; -- u32 out_ref_rate, out_ref_channels; -- int out_ref_valid_bits, out_ref_type; -+ u32 ref_rate, ref_channels; -+ int ref_valid_bits, ref_type; - - if (available_fmt->num_input_formats) { -+ /* -+ * The process module can change parameters and their operation -+ * depends on the direction: -+ * Playback: typically they have single output format. This is -+ * to 'force' the conversion from input to output. -+ * Use the input format as reference since the single -+ * format is going to be picked. -+ * Capture: typically they have multiple output formats to -+ * convert from dai (input) to FE (output) parameters. -+ * Use the input format as base and replace the param -+ * which is changed by the module with the FE parameter -+ * Reason: we can have module which changes the -+ * parameters in path, we cannot use the full -+ * FE param set for the module output lookup. -+ */ - in_fmt = &available_fmt->input_pin_fmts[input_fmt_index].audio_fmt; - -- out_ref_rate = in_fmt->sampling_frequency; -- out_ref_channels = -+ ref_rate = in_fmt->sampling_frequency; -+ ref_channels = - SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg); -- out_ref_valid_bits = -+ ref_valid_bits = - SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); -- out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg); -+ ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg); - } else { - /* for modules without input formats, use FE params as reference */ -- out_ref_rate = params_rate(fe_params); -- out_ref_channels = params_channels(fe_params); -+ ref_rate = params_rate(fe_params); -+ ref_channels = params_channels(fe_params); - ret = sof_ipc4_get_sample_type(sdev, fe_params); - if (ret < 0) - return ret; -- out_ref_type = (u32)ret; -+ ref_type = (u32)ret; - -- out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); -- if (out_ref_valid_bits < 0) -- return out_ref_valid_bits; -+ ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); -+ if (ref_valid_bits < 0) -+ return ref_valid_bits; - } - -+ if (dir == SNDRV_PCM_STREAM_CAPTURE) { -+ if (available_fmt->changed_params & BIT(SNDRV_PCM_HW_PARAM_RATE)) -+ ref_rate = params_rate(fe_params); -+ if (available_fmt->changed_params & BIT(SNDRV_PCM_HW_PARAM_CHANNELS)) -+ ref_channels = params_channels(fe_params); -+ if (available_fmt->changed_params & BIT(SNDRV_PCM_HW_PARAM_FORMAT)) { -+ ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); -+ if (ref_valid_bits < 0) -+ return ref_valid_bits; -+ -+ ref_type = sof_ipc4_get_sample_type(sdev, fe_params); -+ if (ref_type < 0) -+ return ref_type; -+ } -+ } - output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget, - &process->base_config, - available_fmt, -- out_ref_rate, -- out_ref_channels, -- out_ref_valid_bits, -- out_ref_type); -+ ref_rate, -+ ref_channels, -+ ref_valid_bits, -+ ref_type); - if (output_fmt_index < 0) - return output_fmt_index; - -@@ -2866,9 +3043,7 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, - /* modify the pipeline params with the output format */ - ret = sof_ipc4_update_hw_params(sdev, pipeline_params, - &process->output_format, -- BIT(SNDRV_PCM_HW_PARAM_FORMAT) | -- BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | -- BIT(SNDRV_PCM_HW_PARAM_RATE)); -+ available_fmt->changed_params); - if (ret) - return ret; - } -@@ -3083,27 +3258,47 @@ static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr - return 0; - } - --static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev, -- struct snd_sof_widget *swidget, -- struct sof_ipc4_msg *msg, -- void *ipc_data, u32 ipc_size, -- void **new_data) -+static void sof_ipc4_add_init_ext_dp_memory_data(struct snd_sof_dev *sdev, -+ struct snd_sof_widget *swidget, -+ u32 *payload, u32 *ext_pos, -+ struct sof_ipc4_module_init_ext_object **hdr) -+{ -+ /* Add memory_data if comp_domain indicates DP */ -+ if (swidget->comp_domain == SOF_COMP_DOMAIN_DP) { -+ struct sof_ipc4_mod_init_ext_dp_memory_data *dp_mem_data; -+ -+ *hdr = (struct sof_ipc4_module_init_ext_object *)&payload[*ext_pos]; -+ (*hdr)->header = -+ SOF_IPC4_MOD_INIT_EXT_OBJ_ID(SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA) | -+ SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS(DIV_ROUND_UP(sizeof(*dp_mem_data), -+ sizeof(u32))); -+ *ext_pos += DIV_ROUND_UP(sizeof(**hdr), sizeof(u32)); -+ dp_mem_data = (struct sof_ipc4_mod_init_ext_dp_memory_data *)&payload[*ext_pos]; -+ dp_mem_data->domain_id = swidget->domain_id; -+ dp_mem_data->stack_bytes = swidget->stack_bytes; -+ dp_mem_data->heap_bytes = swidget->heap_bytes; -+ *ext_pos += DIV_ROUND_UP(sizeof(*dp_mem_data), sizeof(u32)); -+ } -+} -+ -+static int sof_ipc4_widget_mod_init_msg_payload(struct snd_sof_dev *sdev, -+ struct snd_sof_widget *swidget, -+ struct sof_ipc4_msg *msg, -+ void *ipc_data, u32 ipc_size, -+ void **new_data) - { -- struct sof_ipc4_mod_init_ext_dp_memory_data *dp_mem_data; - struct sof_ipc4_module_init_ext_init *ext_init; -- struct sof_ipc4_module_init_ext_object *hdr; -+ struct sof_ipc4_module_init_ext_object *hdr = NULL; - int new_size; - u32 *payload; - u32 ext_pos; - -- /* For the moment the only reason for adding init_ext_init payload is DP -- * memory data. If both stack and heap size are 0 (= use default), then -- * there is no need for init_ext_init payload. -+ /* -+ * Only DP widgets currently add init-ext objects here. Avoid allocating -+ * a max-sized payload buffer for widgets that will immediately return 0. - */ -- if (swidget->comp_domain != SOF_COMP_DOMAIN_DP) { -- msg->extension &= ~SOF_IPC4_MOD_EXT_EXTENDED_INIT_MASK; -+ if (swidget->comp_domain != SOF_COMP_DOMAIN_DP) - return 0; -- } - - payload = kzalloc(sdev->ipc->max_payload_size, GFP_KERNEL); - if (!payload) -@@ -3111,27 +3306,26 @@ static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev, - - /* Add ext_init first and set objects array flag to 1 */ - ext_init = (struct sof_ipc4_module_init_ext_init *)payload; -- ext_init->word0 |= SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_MASK; - ext_pos = DIV_ROUND_UP(sizeof(*ext_init), sizeof(u32)); - - /* Add object array objects after ext_init */ - -- /* Add dp_memory_data if comp_domain indicates DP */ -- if (swidget->comp_domain == SOF_COMP_DOMAIN_DP) { -- hdr = (struct sof_ipc4_module_init_ext_object *)&payload[ext_pos]; -- hdr->header = SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_MASK | -- SOF_IPC4_MOD_INIT_EXT_OBJ_ID(SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA) | -- SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS(DIV_ROUND_UP(sizeof(*dp_mem_data), -- sizeof(u32))); -- ext_pos += DIV_ROUND_UP(sizeof(*hdr), sizeof(u32)); -- dp_mem_data = (struct sof_ipc4_mod_init_ext_dp_memory_data *)&payload[ext_pos]; -- dp_mem_data->domain_id = swidget->dp_domain_id; -- dp_mem_data->stack_bytes = swidget->dp_stack_bytes; -- dp_mem_data->heap_bytes = swidget->dp_heap_bytes; -- ext_pos += DIV_ROUND_UP(sizeof(*dp_mem_data), sizeof(u32)); -+ sof_ipc4_add_init_ext_dp_memory_data(sdev, swidget, payload, &ext_pos, &hdr); -+ -+ /* Add following object array items here */ -+ -+ if (!hdr) { -+ /* -+ * NOTE: Remove this early bail out, when struct -+ * sof_ipc4_module_init_ext_init alone has some -+ * function. -+ */ -+ kfree(payload); -+ return 0; - } - -- /* If another array object is added, remember clear previous OBJ_LAST bit */ -+ ext_init->word0 |= SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_MASK; -+ hdr->header |= SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_MASK; - - /* Calculate final size and check that it fits to max payload size */ - new_size = ext_pos * sizeof(u32) + ipc_size; -@@ -3154,6 +3348,69 @@ static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev, - return new_size; - } - -+static void sof_ipc4_widget_pipe_ext_obj_memory_data(struct snd_sof_dev *sdev, -+ struct snd_sof_widget *swidget, -+ u32 *payload, u32 *ext_pos, -+ struct sof_ipc4_glb_pipe_ext_object **hdr) -+{ -+ struct sof_ipc4_glb_pipe_ext_obj_memory_data *mem_data; -+ -+ *hdr = (struct sof_ipc4_glb_pipe_ext_object *)&payload[*ext_pos]; -+ (*hdr)->header = -+ SOF_IPC4_GLB_PIPE_EXT_OBJ_ID(SOF_IPC4_GLB_PIPE_DATA_ID_MEM_DATA) | -+ SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS(DIV_ROUND_UP(sizeof(*mem_data), -+ sizeof(u32))); -+ *ext_pos += DIV_ROUND_UP(sizeof(**hdr), sizeof(u32)); -+ mem_data = (struct sof_ipc4_glb_pipe_ext_obj_memory_data *)&payload[*ext_pos]; -+ mem_data->domain_id = swidget->domain_id; -+ mem_data->stack_bytes = swidget->stack_bytes; -+ mem_data->heap_bytes = swidget->heap_bytes; -+ *ext_pos += DIV_ROUND_UP(sizeof(*mem_data), sizeof(u32)); -+ -+ dev_dbg(sdev->dev, -+ "%s; domain_id %u stack %u heap %u bytes", -+ swidget->widget->name, mem_data->domain_id, mem_data->stack_bytes, -+ mem_data->heap_bytes); -+} -+ -+static int sof_ipc4_widget_pipe_create_msg_payload(struct snd_sof_dev *sdev, -+ struct snd_sof_widget *swidget, -+ struct sof_ipc4_msg *msg, -+ void **new_data) -+{ -+ struct sof_ipc4_glb_pipe_payload *payload_hdr; -+ struct sof_ipc4_glb_pipe_ext_object *hdr = NULL; -+ u32 *payload; -+ u32 ext_pos; -+ -+ payload = kzalloc(sdev->ipc->max_payload_size, GFP_KERNEL); -+ if (!payload) -+ return -ENOMEM; -+ -+ /* Add sof_ipc4_glb_pipe_payload and set array bit to 1 */ -+ payload_hdr = (struct sof_ipc4_glb_pipe_payload *)payload; -+ payload_hdr->word0 |= SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_MASK; -+ ext_pos = DIV_ROUND_UP(sizeof(*payload_hdr), sizeof(u32)); -+ -+ sof_ipc4_widget_pipe_ext_obj_memory_data(sdev, swidget, payload, &ext_pos, &hdr); -+ /* Add following array objects here */ -+ -+ /* Mark end of object array */ -+ hdr->header |= SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_MASK; -+ -+ /* Put total payload size in words to the payload header */ -+ payload_hdr->word0 |= SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS(ext_pos); -+ *new_data = payload; -+ -+ /* Update msg extension bits according to the payload changes */ -+ msg->extension |= SOF_IPC4_GLB_PIPE_PAYLOAD_MASK; -+ -+ dev_dbg(sdev->dev, "%s: payload word0 %#x", swidget->widget->name, -+ payload_hdr->word0); -+ -+ return ext_pos * sizeof(int32_t); -+} -+ - static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) - { - struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; -@@ -3340,8 +3597,8 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget - swidget->widget->name, swidget->pipeline_id, module_id, - swidget->instance_id, swidget->core); - -- ret = sof_ipc4_widget_setup_msg_payload(sdev, swidget, msg, ipc_data, ipc_size, -- &ext_data); -+ ret = sof_ipc4_widget_mod_init_msg_payload(sdev, swidget, msg, ipc_data, ipc_size, -+ &ext_data); - if (ret < 0) - goto fail; - -@@ -3353,6 +3610,17 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget - dev_dbg(sdev->dev, "Create pipeline %s (pipe %d) - instance %d, core %d\n", - swidget->widget->name, swidget->pipeline_id, - swidget->instance_id, swidget->core); -+ -+ msg->extension &= ~SOF_IPC4_GLB_PIPE_PAYLOAD_MASK; -+ ret = sof_ipc4_widget_pipe_create_msg_payload(sdev, swidget, msg, -+ &ext_data); -+ if (ret < 0) -+ goto fail; -+ -+ if (ret > 0) { -+ ipc_size = ret; -+ ipc_data = ext_data; -+ } - } - - msg->data_size = ipc_size; -@@ -3410,6 +3678,8 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget - swidget->widget->name); - - pipeline->mem_usage = 0; -+ swidget->heap_bytes = 0; -+ swidget->stack_bytes = 0; - pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED; - ida_free(&pipeline_ida, swidget->instance_id); - swidget->instance_id = -EINVAL; -diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h ---- a/sound/soc/sof/ipc4-topology.h -+++ b/sound/soc/sof/ipc4-topology.h -@@ -167,7 +167,7 @@ struct sof_ipc4_pipeline { - }; - - /** -- * struct sof_ipc4_multi_pipeline_data - multi pipeline trigger IPC data -+ * struct ipc4_pipeline_set_state_data - multi pipeline trigger IPC data - * @count: Number of pipelines to be triggered - * @pipeline_instance_ids: Flexible array of IDs of the pipelines to be triggered - */ -@@ -197,12 +197,15 @@ struct sof_ipc4_pin_format { - * @input_pin_fmts: Available input pin formats - * @num_input_formats: Number of input pin formats - * @num_output_formats: Number of output pin formats -+ * @changed_params: Mask of changed params by the module instance between it's -+ * input and output formts (rate, channels, depth) - */ - struct sof_ipc4_available_audio_format { - struct sof_ipc4_pin_format *output_pin_fmts; - struct sof_ipc4_pin_format *input_pin_fmts; - u32 num_input_formats; - u32 num_output_formats; -+ u32 changed_params; - }; - - /** -@@ -210,7 +213,7 @@ struct sof_ipc4_available_audio_format { - * @node_id: ID of Gateway Node - * @dma_buffer_size: Preferred Gateway DMA buffer size (in bytes) - * @config_length: Length of gateway node configuration blob specified in #config_data -- * config_data: Gateway node configuration blob -+ * @config_data: Gateway node configuration blob - */ - struct sof_copier_gateway_cfg { - uint32_t node_id; -@@ -285,7 +288,9 @@ struct sof_ipc4_dma_stream_ch_map { - struct sof_ipc4_dma_config { - uint8_t dma_method; - uint8_t pre_allocated_by_host; -+ /* private: */ - uint16_t rsvd; -+ /* public: */ - uint32_t dma_channel_id; - uint32_t stream_id; - struct sof_ipc4_dma_stream_ch_map dma_stream_channel_map; -@@ -296,7 +301,7 @@ struct sof_ipc4_dma_config { - #define SOF_IPC4_GTW_DMA_CONFIG_ID 0x1000 - - /** -- * struct sof_ipc4_dma_config: DMA configuration -+ * struct sof_ipc4_dma_config_tlv - DMA configuration - * @type: set to SOF_IPC4_GTW_DMA_CONFIG_ID - * @length: sizeof(struct sof_ipc4_dma_config) + dma_config.dma_priv_config_size - * @dma_config: actual DMA configuration -@@ -321,6 +326,7 @@ struct sof_ipc4_alh_configuration_blob { - * @data: IPC copier data - * @copier_config: Copier + blob - * @ipc_config_size: Size of copier_config -+ * @ipc_config_data: Copier module config data - * @available_fmt: Available audio format - * @frame_fmt: frame format - * @msg: message structure for copier -diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c ---- a/sound/soc/sof/ipc4.c -+++ b/sound/soc/sof/ipc4.c -@@ -289,6 +289,89 @@ static void sof_ipc4_dump_payload(struct snd_sof_dev *sdev, - 16, 4, ipc_data, size, false); - } - -+static const char *sof_ipc4_resource_type_str(u32 type) -+{ -+ switch (type) { -+ case SOF_IPC4_MODULE_INSTANCE: -+ return "resource: MODULE_INSTANCE"; -+ case SOF_IPC4_PIPELINE: -+ return "resource: PIPELINE"; -+ case SOF_IPC4_GATEWAY: -+ return "resource: GATEWAY"; -+ case SOF_IPC4_EDF_TASK: -+ return "resource: EDF_TASK"; -+ case SOF_IPC4_INVALID_RESOURCE_TYPE: -+ return "Resource is invalid"; -+ default: -+ return "Unknown resource type"; -+ } -+} -+ -+static const char *sof_ipc4_resource_event_type_str(u32 event_type) -+{ -+ switch (event_type) { -+ case SOF_IPC4_MIXER_UNDERRUN_DETECTED: -+ return "event: MIXER_UNDERRUN_DETECTED"; -+ case SOF_IPC4_PROCESS_DATA_ERROR: -+ return "event: PROCESS_DATA_ERROR"; -+ case SOF_IPC4_GATEWAY_UNDERRUN_DETECTED: -+ return "event: GATEWAY_UNDERRUN_DETECTED"; -+ case SOF_IPC4_GATEWAY_OVERRUN_DETECTED: -+ return "event: GATEWAY_OVERRUN_DETECTED"; -+ default: -+ return "Unknown event type"; -+ } -+} -+ -+static void sof_ipc4_resource_event_handler(struct snd_sof_dev *sdev, -+ struct sof_ipc4_msg *ipc4_msg) -+{ -+ struct sof_ipc4_notify_resource_data *data = ipc4_msg->data_ptr; -+ -+ /* Print event details */ -+ switch (data->event_type) { -+ case SOF_IPC4_MIXER_UNDERRUN_DETECTED: -+ dev_dbg(sdev->dev, "%s (%u): eos %u, mixed %u, expected %u\n", -+ sof_ipc4_resource_event_type_str(data->event_type), -+ data->event_type, data->data.mixer_underrun.eos_flag, -+ data->data.mixer_underrun.data_mixed, -+ data->data.mixer_underrun.expected_data_mixed); -+ break; -+ case SOF_IPC4_PROCESS_DATA_ERROR: -+ dev_dbg(sdev->dev, "%s (%u): error_code %#x\n", -+ sof_ipc4_resource_event_type_str(data->event_type), -+ data->event_type, data->data.process_data_error.error_code); -+ break; -+ case SOF_IPC4_GATEWAY_UNDERRUN_DETECTED: -+ case SOF_IPC4_GATEWAY_OVERRUN_DETECTED: -+ dev_dbg(sdev->dev, "%s (%u)\n", -+ sof_ipc4_resource_event_type_str(data->event_type), -+ data->event_type); -+ break; -+ default: -+ dev_dbg(sdev->dev, "%s (%u): raw dws %#x %#x %#x %#x %#x %#x\n", -+ sof_ipc4_resource_event_type_str(data->event_type), -+ data->event_type, -+ data->data.dws[0], data->data.dws[1], data->data.dws[2], -+ data->data.dws[3], data->data.dws[4], data->data.dws[5]); -+ break; -+ } -+ -+ /* Print resource details */ -+ if (data->resource_type == SOF_IPC4_MODULE_INSTANCE) { -+ u32 module_id = SOF_IPC4_MOD_ID_GET(data->resource_id); -+ u32 instance_id = SOF_IPC4_MOD_INSTANCE_GET(data->resource_id); -+ -+ dev_dbg(sdev->dev, "%s (%u), module_id %u, instance_id %u\n", -+ sof_ipc4_resource_type_str(data->resource_type), -+ data->resource_type, module_id, instance_id); -+ } else if (data->resource_type != SOF_IPC4_INVALID_RESOURCE_TYPE) { -+ dev_dbg(sdev->dev, "%s (%u), id %u\n", -+ sof_ipc4_resource_type_str(data->resource_type), -+ data->resource_type, data->resource_id); -+ } -+} -+ - static int sof_ipc4_get_reply(struct snd_sof_dev *sdev) - { - struct snd_sof_ipc_msg *msg = sdev->msg; -@@ -734,6 +817,7 @@ static void sof_ipc4_rx_msg(struct snd_sof_dev *sdev) - break; - case SOF_IPC4_NOTIFY_RESOURCE_EVENT: - data_size = sizeof(struct sof_ipc4_notify_resource_data); -+ handler_func = sof_ipc4_resource_event_handler; - break; - case SOF_IPC4_NOTIFY_LOG_BUFFER_STATUS: - sof_ipc4_mtrace_update_pos(sdev, SOF_IPC4_LOG_CORE_GET(ipc4_msg->primary)); -diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c ---- a/sound/soc/sof/nocodec.c -+++ b/sound/soc/sof/nocodec.c -@@ -21,12 +21,13 @@ static struct snd_soc_card sof_nocodec_card = { - static int sof_nocodec_bes_setup(struct device *dev, - struct snd_soc_dai_driver *drv, - struct snd_soc_dai_link *links, -- int link_num, struct snd_soc_card *card) -+ int link_num) - { -+ struct snd_soc_card *card = &sof_nocodec_card; - struct snd_soc_dai_link_component *dlc; - int i; - -- if (!drv || !links || !card) -+ if (!drv || !links) - return -EINVAL; - - /* set up BE dai_links */ -@@ -67,10 +68,10 @@ static int sof_nocodec_bes_setup(struct device *dev, - return 0; - } - --static int sof_nocodec_setup(struct device *dev, -- u32 num_dai_drivers, -- struct snd_soc_dai_driver *dai_drivers) -+static int sof_nocodec_setup(struct device *dev, struct snd_soc_acpi_mach *mach) - { -+ u32 num_dai_drivers = mach->mach_params.num_dai_drivers; -+ struct snd_soc_dai_driver *dai_drivers = mach->mach_params.dai_drivers; - struct snd_soc_dai_link *links; - - /* create dummy BE dai_links */ -@@ -78,7 +79,7 @@ static int sof_nocodec_setup(struct device *dev, - if (!links) - return -ENOMEM; - -- return sof_nocodec_bes_setup(dev, dai_drivers, links, num_dai_drivers, &sof_nocodec_card); -+ return sof_nocodec_bes_setup(dev, dai_drivers, links, num_dai_drivers); - } - - static int sof_nocodec_probe(struct platform_device *pdev) -@@ -92,8 +93,7 @@ static int sof_nocodec_probe(struct platform_device *pdev) - - snd_soc_card_set_topology_name(card, "sof"); - -- ret = sof_nocodec_setup(card->dev, mach->mach_params.num_dai_drivers, -- mach->mach_params.dai_drivers); -+ ret = sof_nocodec_setup(card->dev, mach); - if (ret < 0) - return ret; - -diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c ---- a/sound/soc/sof/pcm.c -+++ b/sound/soc/sof/pcm.c -@@ -62,7 +62,7 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream) - * To avoid sending IPC before the previous IPC is handled, we - * schedule delayed work here to call the snd_pcm_period_elapsed(). - */ -- schedule_work(&spcm->stream[substream->stream].period_elapsed_work); -+ queue_work(system_highpri_wq, &spcm->stream[substream->stream].period_elapsed_work); - } - EXPORT_SYMBOL(snd_sof_pcm_period_elapsed); - -diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c ---- a/sound/soc/sof/sof-audio.c -+++ b/sound/soc/sof/sof-audio.c -@@ -501,9 +501,8 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget - !sof_widget_in_same_direction(swidget, dir)) - return 0; - -- /* skip widgets already prepared or aggregated DAI widgets*/ -- if (!widget_ops[widget->id].ipc_prepare || swidget->prepared || -- is_aggregated_dai(swidget)) -+ /* skip widgets aggregated DAI widgets */ -+ if (!widget_ops[widget->id].ipc_prepare || is_aggregated_dai(swidget)) - goto sink_prepare; - - /* prepare the source widget */ -diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h ---- a/sound/soc/sof/sof-audio.h -+++ b/sound/soc/sof/sof-audio.h -@@ -459,10 +459,10 @@ struct snd_sof_widget { - /* Scheduling domain (enum sof_comp_domain), unset, Low Latency, or Data Processing */ - u32 comp_domain; - -- /* The values below are added to mod_init pay load if comp_domain indicates DP component */ -- u32 dp_domain_id; /* DP process userspace domain ID */ -- u32 dp_stack_bytes; /* DP process stack size requirement in bytes */ -- u32 dp_heap_bytes; /* DP process heap size requirement in bytes */ -+ /* Module instance's memory configuration. */ -+ u32 domain_id; /* Module instance's userspace domain ID */ -+ u32 stack_bytes; /* Module instance's stack size requirement */ -+ u32 heap_bytes; /* Module instance's heap size requirement */ - - struct snd_soc_dapm_widget *widget; - struct list_head list; /* list in sdev widget list */ -diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c ---- a/sound/soc/sof/topology.c -+++ b/sound/soc/sof/topology.c -@@ -309,7 +309,7 @@ static const struct sof_dai_types sof_dais[] = { - {"ACPHS_VIRTUAL", SOF_DAI_AMD_HS_VIRTUAL}, - {"MICFIL", SOF_DAI_IMX_MICFIL}, - {"ACP_SDW", SOF_DAI_AMD_SDW}, -- -+ {"ACPTDM", SOF_DAI_AMD_I2S}, - }; - - static enum sof_ipc_dai_type find_dai(const char *name) -@@ -846,6 +846,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, - struct snd_soc_tplg_mixer_control *mc = - container_of(hdr, struct snd_soc_tplg_mixer_control, hdr); - int tlv[SOF_TLV_ITEMS]; -+ u32 min, max; - unsigned int mask; - int ret; - -@@ -853,6 +854,11 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, - if (le32_to_cpu(mc->num_channels) > SND_SOC_TPLG_MAX_CHAN) - return -EINVAL; - -+ min = le32_to_cpu(mc->min); -+ max = le32_to_cpu(mc->max); -+ if (min > max || max >= INT_MAX) -+ return -EINVAL; -+ - /* - * If control has more than 2 channels we need to override the info. This is because even if - * ASoC layer has defined topology's max channel count to SND_SOC_TPLG_MAX_CHAN = 8, the -@@ -863,12 +869,12 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, - kc->info = snd_sof_volume_info; - - scontrol->comp_id = sdev->next_comp_id; -- scontrol->min_volume_step = le32_to_cpu(mc->min); -- scontrol->max_volume_step = le32_to_cpu(mc->max); -+ scontrol->min_volume_step = min; -+ scontrol->max_volume_step = max; - scontrol->num_channels = le32_to_cpu(mc->num_channels); - -- scontrol->max = le32_to_cpu(mc->max); -- if (le32_to_cpu(mc->max) == 1) -+ scontrol->max = max; -+ if (max == 1) - goto skip; - - /* extract tlv data */ -@@ -878,7 +884,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, - } - - /* set up volume table */ -- ret = set_up_volume_table(scontrol, tlv, le32_to_cpu(mc->max) + 1); -+ ret = set_up_volume_table(scontrol, tlv, max + 1); - if (ret < 0) { - dev_err(scomp->dev, "error: setting up volume table\n"); - return ret; -@@ -911,7 +917,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, - return 0; - - err: -- if (le32_to_cpu(mc->max) > 1) -+ if (max > 1) - kfree(scontrol->volume_table); - - return ret; -@@ -1102,7 +1108,7 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, - - full = NULL; - partial = NULL; -- list_for_each_entry(rtd, &card->rtd_list, list) { -+ for_each_card_rtds(card, rtd) { - /* does stream match DAI link ? */ - if (rtd->dai_link->stream_name) { - if (!strcmp(rtd->dai_link->stream_name, w->sname)) { -@@ -1167,7 +1173,7 @@ static void sof_disconnect_dai_widget(struct snd_soc_component *scomp, - else - return; - -- list_for_each_entry(rtd, &card->rtd_list, list) { -+ for_each_card_rtds(card, rtd) { - /* does stream match DAI link ? */ - if (!rtd->dai_link->stream_name || - !strstr(rtd->dai_link->stream_name, sname)) -@@ -1943,8 +1949,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ - private->array, le32_to_cpu(private->size)); - if (ret < 0) { - dev_err(scomp->dev, "Failed tp parse common DAI link tokens\n"); -- kfree(slink); -- return ret; -+ goto free_slink; - } - - token_list = tplg_ops ? tplg_ops->token_list : NULL; -@@ -1995,6 +2000,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ - case SOF_DAI_AMD_HS: - case SOF_DAI_AMD_SP_VIRTUAL: - case SOF_DAI_AMD_HS_VIRTUAL: -+ case SOF_DAI_AMD_I2S: - token_id = SOF_ACPI2S_TOKENS; - num_tuples += token_list[SOF_ACPI2S_TOKENS].count; - break; -@@ -2013,8 +2019,8 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ - /* allocate memory for tuples array */ - slink->tuples = kzalloc_objs(*slink->tuples, num_tuples); - if (!slink->tuples) { -- kfree(slink); -- return -ENOMEM; -+ ret = -ENOMEM; -+ goto free_slink; - } - - if (token_list[SOF_DAI_LINK_TOKENS].tokens) { -@@ -2070,6 +2076,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ - - err: - kfree(slink->tuples); -+free_slink: - kfree(slink); - - return ret; -@@ -2505,13 +2512,12 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_sof_pdata *sof_pdata = sdev->pdata; - const char *tplg_filename_prefix = sof_pdata->tplg_filename_prefix; -- const struct firmware *fw; -- const char **tplg_files; - int tplg_cnt = 0; - int ret; - int i; - -- tplg_files = kcalloc(scomp->card->num_links, sizeof(char *), GFP_KERNEL); -+ const char **tplg_files __free(kfree) = -+ kcalloc(scomp->card->num_links, sizeof(char *), GFP_KERNEL); - if (!tplg_files) - return -ENOMEM; - -@@ -2537,10 +2543,8 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) - tplg_filename_prefix, - &tplg_files, - no_fallback); -- if (tplg_cnt < 0) { -- kfree(tplg_files); -+ if (tplg_cnt < 0) - return tplg_cnt; -- } - } - - /* -@@ -2551,8 +2555,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) - if (strstr(file, "dummy")) { - dev_err(scomp->dev, - "Function topology is required, please upgrade sof-firmware\n"); -- -- kfree(tplg_files); - return -EINVAL; - } - tplg_files[0] = file; -@@ -2567,6 +2569,7 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) - if (tplg_files[0] != file) - dev_info(scomp->dev, "loading topology %d: %s\n", i, tplg_files[i]); - -+ const struct firmware *fw __free(firmware) = NULL; - ret = request_firmware(&fw, tplg_files[i], scomp->dev); - if (ret < 0) { - /* -@@ -2585,8 +2588,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) - else - ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw); - -- release_firmware(fw); -- - if (ret < 0) { - dev_err(scomp->dev, "tplg %s component load failed %d\n", - tplg_files[i], ret); -@@ -2605,6 +2606,8 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) - goto out; - } - dev_info(scomp->dev, "loading feature topology %d: %s\n", i, feature_topology); -+ -+ const struct firmware *fw __free(firmware) = NULL; - ret = request_firmware(&fw, feature_topology, scomp->dev); - if (ret < 0) { - /* -@@ -2629,8 +2632,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) - else - ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw); - -- release_firmware(fw); -- - if (ret < 0) { - dev_err(scomp->dev, "feature tplg %s component load failed %d\n", - feature_topologies[i], ret); -@@ -2649,8 +2650,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) - if (ret >= 0 && sdev->led_present) - ret = snd_ctl_led_request(); - -- kfree(tplg_files); -- - return ret; - } - EXPORT_SYMBOL(snd_sof_load_topology); -diff --git a/sound/soc/sophgo/cv1800b-tdm.c b/sound/soc/sophgo/cv1800b-tdm.c ---- a/sound/soc/sophgo/cv1800b-tdm.c -+++ b/sound/soc/sophgo/cv1800b-tdm.c -@@ -558,7 +558,7 @@ static const struct snd_soc_dai_ops cv1800b_i2s_dai_ops = { - .set_sysclk = cv1800b_i2s_dai_set_sysclk, - }; - --static const struct snd_soc_dai_driver cv1800b_i2s_dai_template = { -+static struct snd_soc_dai_driver cv1800b_i2s_dai_template = { - .name = "cv1800b-i2s", - .playback = { - .stream_name = "Playback", -@@ -636,7 +636,6 @@ static int cv1800b_i2s_probe(struct platform_device *pdev) - struct cv1800b_i2s *i2s; - struct resource *res; - void __iomem *regs; -- struct snd_soc_dai_driver *dai; - int ret; - - i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL); -@@ -666,21 +665,14 @@ static int cv1800b_i2s_probe(struct platform_device *pdev) - platform_set_drvdata(pdev, i2s); - cv1800b_i2s_setup_tdm(i2s); - -- dai = devm_kmemdup(dev, &cv1800b_i2s_dai_template, sizeof(*dai), -- GFP_KERNEL); -- if (!dai) -- return -ENOMEM; -- -- ret = devm_snd_soc_register_component(dev, &cv1800b_i2s_component, dai, -- 1); -+ ret = devm_snd_soc_register_component(dev, &cv1800b_i2s_component, -+ &cv1800b_i2s_dai_template, 1); - if (ret) - return ret; - - ret = devm_snd_dmaengine_pcm_register(dev, &cv1800b_i2s_pcm_config, 0); -- if (ret) { -- dev_err(dev, "dmaengine_pcm_register failed: %d\n", ret); -+ if (ret) - return ret; -- } - - return 0; - } -diff --git a/sound/soc/spacemit/k1_i2s.c b/sound/soc/spacemit/k1_i2s.c ---- a/sound/soc/spacemit/k1_i2s.c -+++ b/sound/soc/spacemit/k1_i2s.c -@@ -52,7 +52,7 @@ struct spacemit_i2s_dev { - - struct clk *sysclk; - struct clk *bclk; -- struct clk *sspa_clk; -+ struct clk *func_clk; - struct clk *sysclk_div; - struct clk *c_sysclk; - struct clk *c_bclk; -@@ -221,7 +221,7 @@ static int spacemit_i2s_hw_params(struct snd_pcm_substream *substream, - if (ret) - return ret; - -- return clk_set_rate(i2s->sspa_clk, bclk_rate); -+ return clk_set_rate(i2s->func_clk, bclk_rate); - } - - static int spacemit_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, -@@ -335,6 +335,11 @@ static int spacemit_i2s_dai_remove(struct snd_soc_dai *dai) - return 0; - } - -+static const u64 spacemit_i2s_selectable_formats = -+ SND_SOC_POSSIBLE_DAIFMT_I2S | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_A | -+ SND_SOC_POSSIBLE_DAIFMT_DSP_B; -+ - static const struct snd_soc_dai_ops spacemit_i2s_dai_ops = { - .probe = spacemit_i2s_dai_probe, - .remove = spacemit_i2s_dai_remove, -@@ -343,26 +348,12 @@ static const struct snd_soc_dai_ops spacemit_i2s_dai_ops = { - .set_sysclk = spacemit_i2s_set_sysclk, - .set_fmt = spacemit_i2s_set_fmt, - .trigger = spacemit_i2s_trigger, -+ .auto_selectable_formats = &spacemit_i2s_selectable_formats, -+ .num_auto_selectable_formats = 1, - }; - - static struct snd_soc_dai_driver spacemit_i2s_dai = { - .ops = &spacemit_i2s_dai_ops, -- .playback = { -- .channels_min = 1, -- .channels_max = 2, -- .rates = SPACEMIT_PCM_RATES, -- .rate_min = SNDRV_PCM_RATE_8000, -- .rate_max = SNDRV_PCM_RATE_48000, -- .formats = SPACEMIT_PCM_FORMATS, -- }, -- .capture = { -- .channels_min = 1, -- .channels_max = 2, -- .rates = SPACEMIT_PCM_RATES, -- .rate_min = SNDRV_PCM_RATE_8000, -- .rate_max = SNDRV_PCM_RATE_48000, -- .formats = SPACEMIT_PCM_FORMATS, -- }, - .symmetric_rate = 1, - }; - -@@ -392,6 +383,8 @@ static int spacemit_i2s_init_dai(struct spacemit_i2s_dev *i2s, - dai->playback.channels_min = 1; - dai->playback.channels_max = 2; - dai->playback.rates = SPACEMIT_PCM_RATES; -+ dai->playback.rate_min = SNDRV_PCM_RATE_8000; -+ dai->playback.rate_max = SNDRV_PCM_RATE_48000; - dai->playback.formats = SPACEMIT_PCM_FORMATS; - - i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; -@@ -404,6 +397,8 @@ static int spacemit_i2s_init_dai(struct spacemit_i2s_dev *i2s, - dai->capture.channels_min = 1; - dai->capture.channels_max = 2; - dai->capture.rates = SPACEMIT_PCM_RATES; -+ dai->capture.rate_min = SNDRV_PCM_RATE_8000; -+ dai->capture.rate_max = SNDRV_PCM_RATE_48000; - dai->capture.formats = SPACEMIT_PCM_FORMATS; - - i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; -@@ -445,14 +440,14 @@ static int spacemit_i2s_probe(struct platform_device *pdev) - if (IS_ERR(i2s->bclk)) - return dev_err_probe(i2s->dev, PTR_ERR(i2s->bclk), "failed to enable bit clock\n"); - -- clk = devm_clk_get_enabled(i2s->dev, "sspa_bus"); -+ clk = devm_clk_get_enabled(i2s->dev, "bus"); - if (IS_ERR(clk)) -- return dev_err_probe(i2s->dev, PTR_ERR(clk), "failed to enable sspa_bus clock\n"); -+ return dev_err_probe(i2s->dev, PTR_ERR(clk), "failed to enable bus clock\n"); - -- i2s->sspa_clk = devm_clk_get_enabled(i2s->dev, "sspa"); -- if (IS_ERR(i2s->sspa_clk)) -- return dev_err_probe(i2s->dev, PTR_ERR(i2s->sspa_clk), -- "failed to enable sspa clock\n"); -+ i2s->func_clk = devm_clk_get_enabled(i2s->dev, "func"); -+ if (IS_ERR(i2s->func_clk)) -+ return dev_err_probe(i2s->dev, PTR_ERR(i2s->func_clk), -+ "failed to enable func clock\n"); - - i2s->sysclk_div = devm_clk_get_optional_enabled(i2s->dev, "sysclk_div"); - if (IS_ERR(i2s->sysclk_div)) -@@ -471,7 +466,7 @@ static int spacemit_i2s_probe(struct platform_device *pdev) - - i2s->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(i2s->base)) -- return dev_err_probe(i2s->dev, PTR_ERR(i2s->base), "failed to map registers\n"); -+ return PTR_ERR(i2s->base); - - i2s->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL); - if (IS_ERR(i2s->reset)) -@@ -488,7 +483,7 @@ static int spacemit_i2s_probe(struct platform_device *pdev) - &spacemit_i2s_component, - dai, 1); - if (ret) -- return dev_err_probe(i2s->dev, ret, "failed to register component"); -+ return ret; - - return devm_snd_dmaengine_pcm_register(&pdev->dev, &spacemit_dmaengine_pcm_config, 0); - } -diff --git a/sound/soc/spear/spdif_in.c b/sound/soc/spear/spdif_in.c ---- a/sound/soc/spear/spdif_in.c -+++ b/sound/soc/spear/spdif_in.c -@@ -55,7 +55,7 @@ static int spdif_in_dai_probe(struct snd_soc_dai *dai) - struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai); - - host->dma_params_rx.filter_data = &host->dma_params; -- dai->capture_dma_data = &host->dma_params_rx; -+ snd_soc_dai_dma_data_set_capture(dai, &host->dma_params_rx); - - return 0; - } -@@ -150,12 +150,12 @@ static int spdif_in_trigger(struct snd_pcm_substream *substream, int cmd, - - static const struct snd_soc_dai_ops spdif_in_dai_ops = { - .shutdown = spdif_in_shutdown, -+ .probe = spdif_in_dai_probe, - .trigger = spdif_in_trigger, - .hw_params = spdif_in_hw_params, - }; - - static struct snd_soc_dai_driver spdif_in_dai = { -- .probe = spdif_in_dai_probe, - .capture = { - .channels_min = 2, - .channels_max = 2, -@@ -218,10 +218,8 @@ static int spdif_in_probe(struct platform_device *pdev) - - host->io_base = io_base; - host->irq = platform_get_irq(pdev, 0); -- if (host->irq < 0) { -- dev_warn(&pdev->dev, "failed to get IRQ: %d\n", host->irq); -+ if (host->irq < 0) - return host->irq; -- } - - host->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(host->clk)) -@@ -243,10 +241,8 @@ static int spdif_in_probe(struct platform_device *pdev) - - ret = devm_request_irq(&pdev->dev, host->irq, spdif_in_irq, 0, - "spdif-in", host); -- if (ret) { -- dev_warn(&pdev->dev, "request_irq failed\n"); -+ if (ret) - return ret; -- } - - ret = devm_snd_soc_register_component(&pdev->dev, &spdif_in_component, - &spdif_in_dai, 1); -diff --git a/sound/soc/spear/spdif_out.c b/sound/soc/spear/spdif_out.c ---- a/sound/soc/spear/spdif_out.c -+++ b/sound/soc/spear/spdif_out.c -@@ -249,6 +249,7 @@ static int spdif_soc_dai_probe(struct snd_soc_dai *dai) - } - - static const struct snd_soc_dai_ops spdif_out_dai_ops = { -+ .probe = spdif_soc_dai_probe, - .mute_stream = spdif_mute, - .startup = spdif_out_startup, - .shutdown = spdif_out_shutdown, -@@ -266,7 +267,6 @@ static struct snd_soc_dai_driver spdif_out_dai = { - SNDRV_PCM_RATE_192000), - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, -- .probe = spdif_soc_dai_probe, - .ops = &spdif_out_dai_ops, - }; - -diff --git a/sound/soc/sprd/sprd-mcdt.c b/sound/soc/sprd/sprd-mcdt.c ---- a/sound/soc/sprd/sprd-mcdt.c -+++ b/sound/soc/sprd/sprd-mcdt.c -@@ -931,10 +931,8 @@ static int sprd_mcdt_probe(struct platform_device *pdev) - - ret = devm_request_irq(&pdev->dev, irq, sprd_mcdt_irq_handler, - 0, "sprd-mcdt", mcdt); -- if (ret) { -- dev_err(&pdev->dev, "Failed to request MCDT IRQ\n"); -+ if (ret) - return ret; -- } - - sprd_mcdt_init_chans(mcdt, res); - -diff --git a/sound/soc/sprd/sprd-pcm-dma.c b/sound/soc/sprd/sprd-pcm-dma.c ---- a/sound/soc/sprd/sprd-pcm-dma.c -+++ b/sound/soc/sprd/sprd-pcm-dma.c -@@ -469,8 +469,6 @@ static int sprd_soc_platform_probe(struct platform_device *pdev) - - ret = devm_snd_soc_register_component(&pdev->dev, &sprd_soc_component, - NULL, 0); -- if (ret) -- dev_err(&pdev->dev, "could not register platform:%d\n", ret); - - return ret; - } -diff --git a/sound/soc/starfive/jh7110_pwmdac.c b/sound/soc/starfive/jh7110_pwmdac.c ---- a/sound/soc/starfive/jh7110_pwmdac.c -+++ b/sound/soc/starfive/jh7110_pwmdac.c -@@ -477,25 +477,22 @@ static int jh7110_pwmdac_probe(struct platform_device *pdev) - &jh7110_pwmdac_component, - &jh7110_pwmdac_dai, 1); - if (ret) -- return dev_err_probe(&pdev->dev, ret, "failed to register dai\n"); -+ return ret; - - ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); - if (ret) -- return dev_err_probe(&pdev->dev, ret, "failed to register pcm\n"); -+ return ret; - - pm_runtime_enable(dev->dev); - if (!pm_runtime_enabled(&pdev->dev)) { - ret = jh7110_pwmdac_runtime_resume(&pdev->dev); -- if (ret) -- goto err_pm_disable; -+ if (ret) { -+ pm_runtime_disable(&pdev->dev); -+ return ret; -+ } - } - - return 0; -- --err_pm_disable: -- pm_runtime_disable(&pdev->dev); -- -- return ret; - } - - static void jh7110_pwmdac_remove(struct platform_device *pdev) -diff --git a/sound/soc/starfive/jh7110_tdm.c b/sound/soc/starfive/jh7110_tdm.c ---- a/sound/soc/starfive/jh7110_tdm.c -+++ b/sound/soc/starfive/jh7110_tdm.c -@@ -559,10 +559,8 @@ static int jh7110_tdm_clk_reset_get(struct platform_device *pdev, - tdm->clks[5].id = "tdm"; - - ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(tdm->clks), tdm->clks); -- if (ret) { -- dev_err(&pdev->dev, "Failed to get tdm clocks\n"); -+ if (ret) - return ret; -- } - - tdm->resets = devm_reset_control_array_get_exclusive(&pdev->dev); - if (IS_ERR(tdm->resets)) { -@@ -589,42 +587,33 @@ static int jh7110_tdm_probe(struct platform_device *pdev) - tdm->dev = &pdev->dev; - - ret = jh7110_tdm_clk_reset_get(pdev, tdm); -- if (ret) { -- dev_err(&pdev->dev, "Failed to enable audio-tdm clock\n"); -+ if (ret) - return ret; -- } - - jh7110_tdm_init_params(tdm); - - dev_set_drvdata(&pdev->dev, tdm); - ret = devm_snd_soc_register_component(&pdev->dev, &jh7110_tdm_component, - &jh7110_tdm_dai, 1); -- if (ret) { -- dev_err(&pdev->dev, "Failed to register dai\n"); -+ if (ret) - return ret; -- } - - ret = devm_snd_dmaengine_pcm_register(&pdev->dev, - &jh7110_dmaengine_pcm_config, - SND_DMAENGINE_PCM_FLAG_COMPAT); -- if (ret) { -- dev_err(&pdev->dev, "Could not register pcm: %d\n", ret); -+ if (ret) - return ret; -- } - - pm_runtime_enable(&pdev->dev); - if (!pm_runtime_enabled(&pdev->dev)) { - ret = jh7110_tdm_runtime_resume(&pdev->dev); -- if (ret) -- goto err_pm_disable; -+ if (ret) { -+ pm_runtime_disable(&pdev->dev); -+ return ret; -+ } - } - - return 0; -- --err_pm_disable: -- pm_runtime_disable(&pdev->dev); -- -- return ret; - } - - static void jh7110_tdm_dev_remove(struct platform_device *pdev) -diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c ---- a/sound/soc/stm/stm32_adfsdm.c -+++ b/sound/soc/stm/stm32_adfsdm.c -@@ -360,11 +360,8 @@ static int stm32_adfsdm_probe(struct platform_device *pdev) - ret = devm_snd_soc_register_component(&pdev->dev, - &stm32_adfsdm_soc_platform, - NULL, 0); -- if (ret < 0) { -- dev_err(&pdev->dev, "%s: Failed to register PCM platform\n", -- __func__); -+ if (ret < 0) - return ret; -- } - - pm_runtime_enable(&pdev->dev); - -diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c ---- a/sound/soc/stm/stm32_i2s.c -+++ b/sound/soc/stm/stm32_i2s.c -@@ -1238,10 +1238,8 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev, - - ret = devm_request_irq(&pdev->dev, irq, stm32_i2s_isr, 0, - dev_name(&pdev->dev), i2s); -- if (ret) { -- dev_err(&pdev->dev, "irq request returned %d\n", ret); -+ if (ret) - return ret; -- } - - /* Reset */ - rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); -@@ -1295,7 +1293,7 @@ static int stm32_i2s_probe(struct platform_device *pdev) - - ret = snd_dmaengine_pcm_register(&pdev->dev, &stm32_i2s_pcm_config, 0); - if (ret) -- return dev_err_probe(&pdev->dev, ret, "PCM DMA register error\n"); -+ return ret; - - ret = snd_soc_register_component(&pdev->dev, &stm32_i2s_component, - i2s->dai_drv, 1); -diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c ---- a/sound/soc/stm/stm32_sai_sub.c -+++ b/sound/soc/stm/stm32_sai_sub.c -@@ -1696,19 +1696,15 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) - - ret = devm_request_irq(&pdev->dev, sai->pdata->irq, stm32_sai_isr, - IRQF_SHARED, dev_name(&pdev->dev), sai); -- if (ret) { -- dev_err(&pdev->dev, "IRQ request returned %d\n", ret); -+ if (ret) - goto err_unprepare_pclk; -- } - - if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) - conf = &stm32_sai_pcm_config_spdif; - - ret = snd_dmaengine_pcm_register(&pdev->dev, conf, 0); -- if (ret) { -- ret = dev_err_probe(&pdev->dev, ret, "Could not register pcm dma\n"); -+ if (ret) - goto err_unprepare_pclk; -- } - - ret = snd_soc_register_component(&pdev->dev, &stm32_component, - &sai->cpu_dai_drv, 1); -diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c ---- a/sound/soc/stm/stm32_spdifrx.c -+++ b/sound/soc/stm/stm32_spdifrx.c -@@ -970,10 +970,8 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) - - ret = devm_request_irq(&pdev->dev, spdifrx->irq, stm32_spdifrx_isr, 0, - dev_name(&pdev->dev), spdifrx); -- if (ret) { -- dev_err(&pdev->dev, "IRQ request returned %d\n", ret); -+ if (ret) - return ret; -- } - - rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); - if (IS_ERR(rst)) -@@ -987,7 +985,7 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) - pcm_config = &stm32_spdifrx_pcm_config; - ret = snd_dmaengine_pcm_register(&pdev->dev, pcm_config, 0); - if (ret) -- return dev_err_probe(&pdev->dev, ret, "PCM DMA register error\n"); -+ return ret; - - ret = snd_soc_register_component(&pdev->dev, - &stm32_spdifrx_component, -diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c ---- a/sound/soc/sunxi/sun4i-codec.c -+++ b/sound/soc/sunxi/sun4i-codec.c -@@ -22,13 +22,13 @@ - #include - - #include -+#include -+#include - #include - #include - #include - #include - #include --#include --#include - - /* Codec DAC digital controls and FIFO registers */ - #define SUN4I_CODEC_DAC_DPC (0x00) -@@ -2317,67 +2317,54 @@ static int sun4i_codec_probe(struct platform_device *pdev) - - scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base, - quirks->regmap_config); -- if (IS_ERR(scodec->regmap)) { -- dev_err(&pdev->dev, "Failed to create our regmap\n"); -- return PTR_ERR(scodec->regmap); -- } -+ if (IS_ERR(scodec->regmap)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->regmap), -+ "Failed to create our regmap\n"); - - /* Get the clocks from the DT */ - scodec->clk_apb = devm_clk_get_enabled(&pdev->dev, "apb"); -- if (IS_ERR(scodec->clk_apb)) { -- dev_err(&pdev->dev, "Failed to get the APB clock\n"); -- return PTR_ERR(scodec->clk_apb); -- } -+ if (IS_ERR(scodec->clk_apb)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->clk_apb), -+ "Failed to get the APB clock\n"); - - scodec->clk_module = devm_clk_get(&pdev->dev, "codec"); -- if (IS_ERR(scodec->clk_module)) { -- dev_err(&pdev->dev, "Failed to get the module clock\n"); -- return PTR_ERR(scodec->clk_module); -- } -+ if (IS_ERR(scodec->clk_module)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->clk_module), -+ "Failed to get the module clock\n"); - - if (quirks->has_reset) { - scodec->rst = devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL); -- if (IS_ERR(scodec->rst)) { -- dev_err(&pdev->dev, "Failed to get reset control\n"); -- return PTR_ERR(scodec->rst); -- } -+ if (IS_ERR(scodec->rst)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->rst), -+ "Failed to get reset control\n"); - } - - scodec->gpio_pa = devm_gpiod_get_optional(&pdev->dev, "allwinner,pa", - GPIOD_OUT_LOW); -- if (IS_ERR(scodec->gpio_pa)) { -- ret = PTR_ERR(scodec->gpio_pa); -- dev_err_probe(&pdev->dev, ret, "Failed to get pa gpio\n"); -- return ret; -- } -+ if (IS_ERR(scodec->gpio_pa)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->gpio_pa), -+ "Failed to get pa gpio\n"); -+ - - scodec->gpio_hp = devm_gpiod_get_optional(&pdev->dev, "hp-det", GPIOD_IN); -- if (IS_ERR(scodec->gpio_hp)) { -- ret = PTR_ERR(scodec->gpio_hp); -- dev_err_probe(&pdev->dev, ret, "Failed to get hp-det gpio\n"); -- return ret; -- } -+ if (IS_ERR(scodec->gpio_hp)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->gpio_hp), -+ "Failed to get hp-det gpio\n"); - - /* reg_field setup */ - scodec->reg_adc_fifoc = devm_regmap_field_alloc(&pdev->dev, - scodec->regmap, - quirks->reg_adc_fifoc); -- if (IS_ERR(scodec->reg_adc_fifoc)) { -- ret = PTR_ERR(scodec->reg_adc_fifoc); -- dev_err(&pdev->dev, "Failed to create regmap fields: %d\n", -- ret); -- return ret; -- } -+ if (IS_ERR(scodec->reg_adc_fifoc)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->reg_adc_fifoc), -+ "Failed to create regmap fields\n"); - - scodec->reg_dac_fifoc = devm_regmap_field_alloc(&pdev->dev, - scodec->regmap, - quirks->reg_dac_fifoc); -- if (IS_ERR(scodec->reg_dac_fifoc)) { -- ret = PTR_ERR(scodec->reg_dac_fifoc); -- dev_err(&pdev->dev, "Failed to create regmap fields: %d\n", -- ret); -- return ret; -- } -+ if (IS_ERR(scodec->reg_dac_fifoc)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->reg_dac_fifoc), -+ "Failed to create regmap fields\n"); - - /* DMA configuration for TX FIFO */ - scodec->playback_dma_data.addr = res->start + quirks->reg_dac_txdata; -@@ -2394,39 +2381,29 @@ static int sun4i_codec_probe(struct platform_device *pdev) - - ret = devm_snd_soc_register_component(&pdev->dev, quirks->codec, - &sun4i_codec_dai, 1); -- if (ret) { -- dev_err(&pdev->dev, "Failed to register our codec\n"); -+ if (ret) - return ret; -- } - - ret = devm_snd_soc_register_component(&pdev->dev, - &sun4i_codec_component, - &dummy_cpu_dai, 1); -- if (ret) { -- dev_err(&pdev->dev, "Failed to register our DAI\n"); -+ if (ret) - return ret; -- } - - ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); -- if (ret) { -- dev_err(&pdev->dev, "Failed to register against DMAEngine\n"); -+ if (ret) - return ret; -- } - - card = quirks->create_card(&pdev->dev); -- if (IS_ERR(card)) { -- ret = PTR_ERR(card); -- dev_err(&pdev->dev, "Failed to create our card\n"); -- return ret; -- } -+ if (IS_ERR(card)) -+ return PTR_ERR(card); - - snd_soc_card_set_drvdata(card, scodec); - - ret = snd_soc_register_card(card); -- if (ret) { -- dev_err_probe(&pdev->dev, ret, "Failed to register our card\n"); -- return ret; -- } -+ if (ret) -+ return dev_err_probe(&pdev->dev, ret, -+ "Failed to register our card\n"); - - return 0; - } -diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c ---- a/sound/soc/sunxi/sun4i-i2s.c -+++ b/sound/soc/sunxi/sun4i-i2s.c -@@ -1550,30 +1550,26 @@ static int sun4i_i2s_probe(struct platform_device *pdev) - } - - i2s->bus_clk = devm_clk_get(&pdev->dev, "apb"); -- if (IS_ERR(i2s->bus_clk)) { -- dev_err(&pdev->dev, "Can't get our bus clock\n"); -- return PTR_ERR(i2s->bus_clk); -- } -+ if (IS_ERR(i2s->bus_clk)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->bus_clk), -+ "Can't get our bus clock\n"); - - i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs, - i2s->variant->sun4i_i2s_regmap); -- if (IS_ERR(i2s->regmap)) { -- dev_err(&pdev->dev, "Regmap initialisation failed\n"); -- return PTR_ERR(i2s->regmap); -- } -+ if (IS_ERR(i2s->regmap)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->regmap), -+ "Regmap initialisation failed\n"); - - i2s->mod_clk = devm_clk_get(&pdev->dev, "mod"); -- if (IS_ERR(i2s->mod_clk)) { -- dev_err(&pdev->dev, "Can't get our mod clock\n"); -- return PTR_ERR(i2s->mod_clk); -- } -+ if (IS_ERR(i2s->mod_clk)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->mod_clk), -+ "Can't get our mod clock\n"); - - if (i2s->variant->has_reset) { - i2s->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); -- if (IS_ERR(i2s->rst)) { -- dev_err(&pdev->dev, "Failed to get reset control\n"); -- return PTR_ERR(i2s->rst); -- } -+ if (IS_ERR(i2s->rst)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->rst), -+ "Failed to get reset control\n"); - } - - if (!IS_ERR(i2s->rst)) { -diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c ---- a/sound/soc/sunxi/sun4i-spdif.c -+++ b/sound/soc/sunxi/sun4i-spdif.c -@@ -684,26 +684,23 @@ static int sun4i_spdif_probe(struct platform_device *pdev) - - host->regmap = devm_regmap_init_mmio(&pdev->dev, base, - &sun4i_spdif_regmap_config); -- if (IS_ERR(host->regmap)) { -- dev_err(&pdev->dev, "failed to initialise regmap.\n"); -- return PTR_ERR(host->regmap); -- } -+ if (IS_ERR(host->regmap)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(host->regmap), -+ "failed to initialise regmap.\n"); - - /* Clocks */ - host->apb_clk = devm_clk_get(&pdev->dev, "apb"); -- if (IS_ERR(host->apb_clk)) { -- dev_err(&pdev->dev, "failed to get a apb clock.\n"); -- return PTR_ERR(host->apb_clk); -- } -+ if (IS_ERR(host->apb_clk)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(host->apb_clk), -+ "failed to get a apb clock.\n"); - - if (quirks->tx_clk_name) - tx_clk_name = quirks->tx_clk_name; - host->spdif_clk = devm_clk_get(&pdev->dev, tx_clk_name); -- if (IS_ERR(host->spdif_clk)) { -- dev_err(&pdev->dev, "failed to get the \"%s\" clock.\n", -- tx_clk_name); -- return PTR_ERR(host->spdif_clk); -- } -+ if (IS_ERR(host->spdif_clk)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(host->spdif_clk), -+ "failed to get the \"%s\" clock.\n", -+ tx_clk_name); - - host->dma_params_tx.addr = res->start + quirks->reg_dac_txdata; - host->dma_params_tx.maxburst = 8; -diff --git a/sound/soc/sunxi/sun50i-codec-analog.c b/sound/soc/sunxi/sun50i-codec-analog.c ---- a/sound/soc/sunxi/sun50i-codec-analog.c -+++ b/sound/soc/sunxi/sun50i-codec-analog.c -@@ -551,16 +551,13 @@ static int sun50i_codec_analog_probe(struct platform_device *pdev) - bool enable; - - base = devm_platform_ioremap_resource(pdev, 0); -- if (IS_ERR(base)) { -- dev_err(&pdev->dev, "Failed to map the registers\n"); -+ if (IS_ERR(base)) - return PTR_ERR(base); -- } - - regmap = sun8i_adda_pr_regmap_init(&pdev->dev, base); -- if (IS_ERR(regmap)) { -- dev_err(&pdev->dev, "Failed to create regmap\n"); -- return PTR_ERR(regmap); -- } -+ if (IS_ERR(regmap)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(regmap), -+ "Failed to create regmap\n"); - - enable = device_property_read_bool(&pdev->dev, - "allwinner,internal-bias-resistor"); -diff --git a/sound/soc/sunxi/sun8i-codec-analog.c b/sound/soc/sunxi/sun8i-codec-analog.c ---- a/sound/soc/sunxi/sun8i-codec-analog.c -+++ b/sound/soc/sunxi/sun8i-codec-analog.c -@@ -822,16 +822,13 @@ static int sun8i_codec_analog_probe(struct platform_device *pdev) - void __iomem *base; - - base = devm_platform_ioremap_resource(pdev, 0); -- if (IS_ERR(base)) { -- dev_err(&pdev->dev, "Failed to map the registers\n"); -+ if (IS_ERR(base)) - return PTR_ERR(base); -- } - - regmap = sun8i_adda_pr_regmap_init(&pdev->dev, base); -- if (IS_ERR(regmap)) { -- dev_err(&pdev->dev, "Failed to create regmap\n"); -- return PTR_ERR(regmap); -- } -+ if (IS_ERR(regmap)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(regmap), -+ "Failed to create regmap\n"); - - return devm_snd_soc_register_component(&pdev->dev, - &sun8i_codec_analog_cmpnt_drv, -diff --git a/sound/soc/tegra/tegra20_das.c b/sound/soc/tegra/tegra20_das.c ---- a/sound/soc/tegra/tegra20_das.c -+++ b/sound/soc/tegra/tegra20_das.c -@@ -167,10 +167,9 @@ static int tegra20_das_probe(struct platform_device *pdev) - - das->regmap = devm_regmap_init_mmio(&pdev->dev, regs, - &tegra20_das_regmap_config); -- if (IS_ERR(das->regmap)) { -- dev_err(&pdev->dev, "regmap init failed\n"); -- return PTR_ERR(das->regmap); -- } -+ if (IS_ERR(das->regmap)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(das->regmap), -+ "regmap init failed\n"); - - tegra20_das_connect_dap_to_dac(das, TEGRA20_DAS_DAP_ID_1, - TEGRA20_DAS_DAP_SEL_DAC1); -diff --git a/sound/soc/tegra/tegra210_adx.c b/sound/soc/tegra/tegra210_adx.c ---- a/sound/soc/tegra/tegra210_adx.c -+++ b/sound/soc/tegra/tegra210_adx.c -@@ -496,16 +496,12 @@ static struct snd_kcontrol_new tegra264_adx_controls[] = { - static int tegra210_adx_component_probe(struct snd_soc_component *component) - { - struct tegra210_adx *adx = snd_soc_component_get_drvdata(component); -- int err = 0; - -- if (adx->soc_data->num_controls) { -- err = snd_soc_add_component_controls(component, adx->soc_data->controls, -- adx->soc_data->num_controls); -- if (err) -- dev_err(component->dev, "can't add ADX controls, err: %d\n", err); -- } -+ if (adx->soc_data->num_controls) -+ return snd_soc_add_component_controls(component, adx->soc_data->controls, -+ adx->soc_data->num_controls); - -- return err; -+ return 0; - } - - static const struct snd_soc_component_driver tegra210_adx_cmpnt = { -diff --git a/sound/soc/tegra/tegra210_amx.c b/sound/soc/tegra/tegra210_amx.c ---- a/sound/soc/tegra/tegra210_amx.c -+++ b/sound/soc/tegra/tegra210_amx.c -@@ -504,16 +504,12 @@ static struct snd_kcontrol_new tegra264_amx_controls[] = { - static int tegra210_amx_component_probe(struct snd_soc_component *component) - { - struct tegra210_amx *amx = snd_soc_component_get_drvdata(component); -- int err = 0; - -- if (amx->soc_data->num_controls) { -- err = snd_soc_add_component_controls(component, amx->soc_data->controls, -- amx->soc_data->num_controls); -- if (err) -- dev_err(component->dev, "can't add AMX controls, err: %d\n", err); -- } -+ if (amx->soc_data->num_controls) -+ return snd_soc_add_component_controls(component, amx->soc_data->controls, -+ amx->soc_data->num_controls); - -- return err; -+ return 0; - } - - static const struct snd_soc_component_driver tegra210_amx_cmpnt = { -diff --git a/sound/soc/tegra/tegra210_i2s.c b/sound/soc/tegra/tegra210_i2s.c ---- a/sound/soc/tegra/tegra210_i2s.c -+++ b/sound/soc/tegra/tegra210_i2s.c -@@ -1090,13 +1090,13 @@ static int tegra210_i2s_probe(struct platform_device *pdev) - "can't retrieve I2S bit clock\n"); - - /* -- * Not an error, as this clock is needed only when some other I/O -- * requires input clock from current I2S instance, which is -- * configurable from DT. -+ * This clock is optional and is only needed when another I/O uses -+ * the current I2S instance as its input clock, as configured in DT. - */ -- i2s->clk_sync_input = devm_clk_get(dev, "sync_input"); -+ i2s->clk_sync_input = devm_clk_get_optional(dev, "sync_input"); - if (IS_ERR(i2s->clk_sync_input)) -- dev_dbg(dev, "can't retrieve I2S sync input clock\n"); -+ return dev_err_probe(dev, PTR_ERR(i2s->clk_sync_input), -+ "can't retrieve I2S sync input clock\n"); - - regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(regs)) -diff --git a/sound/soc/ti/ams-delta.c b/sound/soc/ti/ams-delta.c ---- a/sound/soc/ti/ams-delta.c -+++ b/sound/soc/ti/ams-delta.c -@@ -562,21 +562,21 @@ static int ams_delta_probe(struct platform_device *pdev) - - card->dev = &pdev->dev; - -- handset_mute = devm_gpiod_get(card->dev, "handset_mute", -+ handset_mute = devm_gpiod_get(&pdev->dev, "handset_mute", - GPIOD_OUT_HIGH); - if (IS_ERR(handset_mute)) - return PTR_ERR(handset_mute); - -- handsfree_mute = devm_gpiod_get(card->dev, "handsfree_mute", -+ handsfree_mute = devm_gpiod_get(&pdev->dev, "handsfree_mute", - GPIOD_OUT_HIGH); - if (IS_ERR(handsfree_mute)) - return PTR_ERR(handsfree_mute); - - ret = snd_soc_register_card(card); - if (ret) { -- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); - card->dev = NULL; -- return ret; -+ return dev_err_probe(&pdev->dev, ret, -+ "snd_soc_register_card() failed\n"); - } - return 0; - } -diff --git a/sound/soc/ti/davinci-evm.c b/sound/soc/ti/davinci-evm.c ---- a/sound/soc/ti/davinci-evm.c -+++ b/sound/soc/ti/davinci-evm.c -@@ -245,9 +245,8 @@ static int davinci_evm_probe(struct platform_device *pdev) - - snd_soc_card_set_drvdata(&evm_soc_card, drvdata); - ret = devm_snd_soc_register_card(&pdev->dev, &evm_soc_card); -- - if (ret) { -- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); -+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); - goto err_put; - } - -diff --git a/sound/soc/ti/j721e-evm.c b/sound/soc/ti/j721e-evm.c ---- a/sound/soc/ti/j721e-evm.c -+++ b/sound/soc/ti/j721e-evm.c -@@ -4,6 +4,7 @@ - * Author: Peter Ujfalusi - */ - -+#include - #include - #include - #include -@@ -263,7 +264,7 @@ static int j721e_audio_startup(struct snd_pcm_substream *substream) - int ret = 0; - int i; - -- guard(mutex)(&priv->mutex); -+ mutex_lock(&priv->mutex); - - domain->active++; - -@@ -303,6 +304,7 @@ static int j721e_audio_startup(struct snd_pcm_substream *substream) - out: - if (ret) - domain->active--; -+ mutex_unlock(&priv->mutex); - - return ret; - } -@@ -868,10 +870,9 @@ static int j721e_soc_probe(struct platform_device *pdev) - card->num_dapm_routes = ARRAY_SIZE(j721e_cpb_dapm_routes); - card->fully_routed = 1; - -- if (snd_soc_of_parse_card_name(card, "model")) { -- dev_err(&pdev->dev, "Card name is not provided\n"); -- return -ENODEV; -- } -+ ret = snd_soc_of_parse_card_name(card, "model"); -+ if (ret) -+ return ret; - - link_cnt = 0; - conf_cnt = 0; -diff --git a/sound/soc/ti/omap-abe-twl6040.c b/sound/soc/ti/omap-abe-twl6040.c ---- a/sound/soc/ti/omap-abe-twl6040.c -+++ b/sound/soc/ti/omap-abe-twl6040.c -@@ -234,16 +234,13 @@ static int omap_abe_probe(struct platform_device *pdev) - card->dapm_routes = audio_map; - card->num_dapm_routes = ARRAY_SIZE(audio_map); - -- if (snd_soc_of_parse_card_name(card, "ti,model")) { -- dev_err(&pdev->dev, "Card name is not provided\n"); -- return -ENODEV; -- } -+ ret = snd_soc_of_parse_card_name(card, "ti,model"); -+ if (ret) -+ return ret; - - ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing"); -- if (ret) { -- dev_err(&pdev->dev, "Error while parsing DAPM routing\n"); -+ if (ret) - return ret; -- } - - dai_node = of_parse_phandle(node, "ti,mcpdm", 0); - if (!dai_node) { -@@ -299,8 +296,8 @@ static int omap_abe_probe(struct platform_device *pdev) - - ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) -- dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n", -- ret); -+ dev_err_probe(&pdev->dev, ret, -+ "devm_snd_soc_register_card() failed\n"); - - return ret; - } -diff --git a/sound/soc/ti/omap-dmic.c b/sound/soc/ti/omap-dmic.c ---- a/sound/soc/ti/omap-dmic.c -+++ b/sound/soc/ti/omap-dmic.c -@@ -465,10 +465,9 @@ static int asoc_dmic_probe(struct platform_device *pdev) - mutex_init(&dmic->mutex); - - dmic->fclk = devm_clk_get(dmic->dev, "fck"); -- if (IS_ERR(dmic->fclk)) { -- dev_err(dmic->dev, "can't get fck\n"); -- return -ENODEV; -- } -+ if (IS_ERR(dmic->fclk)) -+ return dev_err_probe(dmic->dev, PTR_ERR(dmic->fclk), -+ "can't get fck\n"); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); - if (!res) { -diff --git a/sound/soc/ti/omap-hdmi.c b/sound/soc/ti/omap-hdmi.c ---- a/sound/soc/ti/omap-hdmi.c -+++ b/sound/soc/ti/omap-hdmi.c -@@ -24,8 +24,6 @@ - #define DRV_NAME "omap-hdmi-audio" - - struct hdmi_audio_data { -- struct snd_soc_card *card; -- - const struct omap_hdmi_audio_ops *ops; - struct device *dssdev; - struct snd_dmaengine_dai_dma_data dma_data; -@@ -375,12 +373,9 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev) - card->dev = dev; - - ret = devm_snd_soc_register_card(dev, card); -- if (ret) { -- dev_err(dev, "snd_soc_register_card failed (%d)\n", ret); -- return ret; -- } -+ if (ret) -+ return dev_err_probe(dev, ret, "snd_soc_register_card() failed\n"); - -- ad->card = card; - snd_soc_card_set_drvdata(card, ad); - - dev_set_drvdata(dev, ad); -diff --git a/sound/soc/ti/omap-twl4030.c b/sound/soc/ti/omap-twl4030.c ---- a/sound/soc/ti/omap-twl4030.c -+++ b/sound/soc/ti/omap-twl4030.c -@@ -17,7 +17,6 @@ - */ - - #include --#include - #include - #include - -@@ -29,6 +28,7 @@ - #include "omap-mcbsp.h" - - struct omap_twl4030 { -+ struct snd_soc_jack_gpio hs_jack_gpio; - struct snd_soc_jack hs_jack; - }; - -@@ -124,38 +124,18 @@ static struct snd_soc_jack_pin hs_jack_pins[] = { - }, - }; - --/* Headset jack detection gpios */ --static struct snd_soc_jack_gpio hs_jack_gpios[] = { -- { -- .name = "ti,jack-det", -- .report = SND_JACK_HEADSET, -- .debounce_time = 200, -- }, --}; -- --static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm, -- int connected, char *pin) --{ -- if (!connected) -- snd_soc_dapm_disable_pin(dapm, pin); --} -- - static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd) - { - struct snd_soc_card *card = rtd->card; -- struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card); -- struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev); -+ - struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card); -- int ret = 0; -+ int ret; - - /* - * This is a bit of a hack, but the GPIO is optional so we - * only want to add the jack detection if the GPIO is there. - */ - if (of_property_present(card->dev->of_node, "ti,jack-det-gpio")) { -- hs_jack_gpios[0].gpiod_dev = card->dev; -- hs_jack_gpios[0].idx = 0; -- - ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack", - SND_JACK_HEADSET, - &priv->hs_jack, hs_jack_pins, -@@ -163,36 +143,19 @@ static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd) - if (ret) - return ret; - -- ret = snd_soc_jack_add_gpios(&priv->hs_jack, -- ARRAY_SIZE(hs_jack_gpios), -- hs_jack_gpios); -+ priv->hs_jack_gpio.name = "ti,jack-det"; -+ priv->hs_jack_gpio.report = SND_JACK_HEADSET; -+ priv->hs_jack_gpio.debounce_time = 200; -+ priv->hs_jack_gpio.gpiod_dev = card->dev; -+ priv->hs_jack_gpio.idx = 0; -+ -+ ret = snd_soc_jack_add_gpios(&priv->hs_jack, 1, -+ &priv->hs_jack_gpio); - if (ret) - return ret; - } - -- /* -- * NULL pdata means we booted with DT. In this case the routing is -- * provided and the card is fully routed, no need to mark pins. -- */ -- if (!pdata || !pdata->custom_routing) -- return ret; -- -- /* Disable not connected paths if not used */ -- twl4030_disconnect_pin(dapm, pdata->has_ear, "Earpiece Spk"); -- twl4030_disconnect_pin(dapm, pdata->has_hf, "Handsfree Spk"); -- twl4030_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone"); -- twl4030_disconnect_pin(dapm, pdata->has_predriv, "Ext Spk"); -- twl4030_disconnect_pin(dapm, pdata->has_carkit, "Carkit Spk"); -- -- twl4030_disconnect_pin(dapm, pdata->has_mainmic, "Main Mic"); -- twl4030_disconnect_pin(dapm, pdata->has_submic, "Sub Mic"); -- twl4030_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic"); -- twl4030_disconnect_pin(dapm, pdata->has_carkitmic, "Carkit Mic"); -- twl4030_disconnect_pin(dapm, pdata->has_digimic0, "Digital0 Mic"); -- twl4030_disconnect_pin(dapm, pdata->has_digimic1, "Digital1 Mic"); -- twl4030_disconnect_pin(dapm, pdata->has_linein, "Line In"); -- -- return ret; -+ return 0; - } - - /* Digital audio interface glue - connects codec <--> CPU */ -@@ -237,11 +200,15 @@ static struct snd_soc_card omap_twl4030_card = { - - static int omap_twl4030_probe(struct platform_device *pdev) - { -- struct omap_tw4030_pdata *pdata = dev_get_platdata(&pdev->dev); -- struct device_node *node = pdev->dev.of_node; - struct snd_soc_card *card = &omap_twl4030_card; -+ struct device_node *node, *dai_node; - struct omap_twl4030 *priv; -- int ret = 0; -+ struct property *prop; -+ int ret; -+ -+ node = pdev->dev.of_node; -+ if (!node) -+ return -ENODEV; - - card->dev = &pdev->dev; - -@@ -249,69 +216,52 @@ static int omap_twl4030_probe(struct platform_device *pdev) - if (priv == NULL) - return -ENOMEM; - -- if (node) { -- struct device_node *dai_node; -- struct property *prop; -+ ret = snd_soc_of_parse_card_name(card, "ti,model"); -+ if (ret) -+ return ret; - -- if (snd_soc_of_parse_card_name(card, "ti,model")) { -- dev_err(&pdev->dev, "Card name is not provided\n"); -- return -ENODEV; -- } -- -- dai_node = of_parse_phandle(node, "ti,mcbsp", 0); -- if (!dai_node) { -- dev_err(&pdev->dev, "McBSP node is not provided\n"); -- return -EINVAL; -- } -- omap_twl4030_dai_links[0].cpus->dai_name = NULL; -- omap_twl4030_dai_links[0].cpus->of_node = dai_node; -- -- omap_twl4030_dai_links[0].platforms->name = NULL; -- omap_twl4030_dai_links[0].platforms->of_node = dai_node; -- -- dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0); -- if (!dai_node) { -- card->num_links = 1; -- } else { -- omap_twl4030_dai_links[1].cpus->dai_name = NULL; -- omap_twl4030_dai_links[1].cpus->of_node = dai_node; -- -- omap_twl4030_dai_links[1].platforms->name = NULL; -- omap_twl4030_dai_links[1].platforms->of_node = dai_node; -- } -- -- /* Optional: audio routing can be provided */ -- prop = of_find_property(node, "ti,audio-routing", NULL); -- if (prop) { -- ret = snd_soc_of_parse_audio_routing(card, -- "ti,audio-routing"); -- if (ret) -- return ret; -- -- card->fully_routed = 1; -- } -- } else if (pdata) { -- if (pdata->card_name) { -- card->name = pdata->card_name; -- } else { -- dev_err(&pdev->dev, "Card name is not provided\n"); -- return -ENODEV; -- } -- -- if (!pdata->voice_connected) -- card->num_links = 1; -- } else { -- dev_err(&pdev->dev, "Missing pdata\n"); -+ if (!card->name) { -+ dev_err(&pdev->dev, "Card name is not provided\n"); - return -ENODEV; - } - -+ dai_node = of_parse_phandle(node, "ti,mcbsp", 0); -+ if (!dai_node) { -+ dev_err(&pdev->dev, "McBSP node is not provided\n"); -+ return -EINVAL; -+ } -+ omap_twl4030_dai_links[0].cpus->dai_name = NULL; -+ omap_twl4030_dai_links[0].cpus->of_node = dai_node; -+ -+ omap_twl4030_dai_links[0].platforms->name = NULL; -+ omap_twl4030_dai_links[0].platforms->of_node = dai_node; -+ -+ dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0); -+ if (!dai_node) { -+ card->num_links = 1; -+ } else { -+ omap_twl4030_dai_links[1].cpus->dai_name = NULL; -+ omap_twl4030_dai_links[1].cpus->of_node = dai_node; -+ -+ omap_twl4030_dai_links[1].platforms->name = NULL; -+ omap_twl4030_dai_links[1].platforms->of_node = dai_node; -+ } -+ -+ /* Optional: audio routing can be provided */ -+ prop = of_find_property(node, "ti,audio-routing", NULL); -+ if (prop) { -+ ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing"); -+ if (ret) -+ return ret; -+ -+ card->fully_routed = 1; -+ } -+ - snd_soc_card_set_drvdata(card, priv); - ret = devm_snd_soc_register_card(&pdev->dev, card); -- if (ret) { -- dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n", -- ret); -- return ret; -- } -+ if (ret) -+ return dev_err_probe(&pdev->dev, ret, -+ "devm_snd_soc_register_card() failed\n"); - - return 0; - } -diff --git a/sound/soc/ti/rx51.c b/sound/soc/ti/rx51.c ---- a/sound/soc/ti/rx51.c -+++ b/sound/soc/ti/rx51.c -@@ -358,21 +358,22 @@ static struct snd_soc_card rx51_sound_card = { - static int rx51_soc_probe(struct platform_device *pdev) - { - struct rx51_audio_pdata *pdata; -- struct device_node *np = pdev->dev.of_node; -+ struct device *dev = &pdev->dev; -+ struct device_node *np = dev->of_node; - struct snd_soc_card *card = &rx51_sound_card; - int err; - - if (!of_machine_is_compatible("nokia,omap3-n900")) - return -ENODEV; - -- card->dev = &pdev->dev; -+ card->dev = dev; - - if (np) { - struct device_node *dai_node; - - dai_node = of_parse_phandle(np, "nokia,cpu-dai", 0); - if (!dai_node) { -- dev_err(card->dev, "McBSP node is not provided\n"); -+ dev_err(dev, "McBSP node is not provided\n"); - return -EINVAL; - } - rx51_dai[0].cpus->dai_name = NULL; -@@ -382,7 +383,7 @@ static int rx51_soc_probe(struct platform_device *pdev) - - dai_node = of_parse_phandle(np, "nokia,audio-codec", 0); - if (!dai_node) { -- dev_err(card->dev, "Codec node is not provided\n"); -+ dev_err(dev, "Codec node is not provided\n"); - return -EINVAL; - } - rx51_dai[0].codecs->name = NULL; -@@ -390,7 +391,7 @@ static int rx51_soc_probe(struct platform_device *pdev) - - dai_node = of_parse_phandle(np, "nokia,audio-codec", 1); - if (!dai_node) { -- dev_err(card->dev, "Auxiliary Codec node is not provided\n"); -+ dev_err(dev, "Auxiliary Codec node is not provided\n"); - return -EINVAL; - } - rx51_aux_dev[0].dlc.name = NULL; -@@ -400,7 +401,7 @@ static int rx51_soc_probe(struct platform_device *pdev) - - dai_node = of_parse_phandle(np, "nokia,headphone-amplifier", 0); - if (!dai_node) { -- dev_err(card->dev, "Headphone amplifier node is not provided\n"); -+ dev_err(dev, "Headphone amplifier node is not provided\n"); - return -EINVAL; - } - rx51_aux_dev[1].dlc.name = NULL; -@@ -409,40 +410,36 @@ static int rx51_soc_probe(struct platform_device *pdev) - rx51_codec_conf[1].dlc.of_node = dai_node; - } - -- pdata = devm_kzalloc(card->dev, sizeof(*pdata), GFP_KERNEL); -+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (pdata == NULL) - return -ENOMEM; - - snd_soc_card_set_drvdata(card, pdata); - -- pdata->tvout_selection_gpio = devm_gpiod_get(card->dev, -+ pdata->tvout_selection_gpio = devm_gpiod_get(dev, - "tvout-selection", - GPIOD_OUT_LOW); -- if (IS_ERR(pdata->tvout_selection_gpio)) { -- dev_err(card->dev, "could not get tvout selection gpio\n"); -- return PTR_ERR(pdata->tvout_selection_gpio); -- } -+ if (IS_ERR(pdata->tvout_selection_gpio)) -+ return dev_err_probe(dev, PTR_ERR(pdata->tvout_selection_gpio), -+ "could not get tvout selection gpio\n"); - -- pdata->eci_sw_gpio = devm_gpiod_get(card->dev, "eci-switch", -+ pdata->eci_sw_gpio = devm_gpiod_get(dev, "eci-switch", - GPIOD_OUT_HIGH); -- if (IS_ERR(pdata->eci_sw_gpio)) { -- dev_err(card->dev, "could not get eci switch gpio\n"); -- return PTR_ERR(pdata->eci_sw_gpio); -- } -+ if (IS_ERR(pdata->eci_sw_gpio)) -+ return dev_err_probe(dev, PTR_ERR(pdata->eci_sw_gpio), -+ "could not get eci switch gpio\n"); - -- pdata->speaker_amp_gpio = devm_gpiod_get(card->dev, -+ pdata->speaker_amp_gpio = devm_gpiod_get(dev, - "speaker-amplifier", - GPIOD_OUT_LOW); -- if (IS_ERR(pdata->speaker_amp_gpio)) { -- dev_err(card->dev, "could not get speaker enable gpio\n"); -- return PTR_ERR(pdata->speaker_amp_gpio); -- } -+ if (IS_ERR(pdata->speaker_amp_gpio)) -+ return dev_err_probe(dev, PTR_ERR(pdata->speaker_amp_gpio), -+ "could not get speaker enable gpio\n"); - -- err = devm_snd_soc_register_card(card->dev, card); -- if (err) { -- dev_err(card->dev, "snd_soc_register_card failed (%d)\n", err); -- return err; -- } -+ err = devm_snd_soc_register_card(dev, card); -+ if (err) -+ return dev_err_probe(dev, err, -+ "snd_soc_register_card() failed\n"); - - return 0; - } -diff --git a/sound/soc/uniphier/aio-cpu.c b/sound/soc/uniphier/aio-cpu.c ---- a/sound/soc/uniphier/aio-cpu.c -+++ b/sound/soc/uniphier/aio-cpu.c -@@ -795,16 +795,12 @@ int uniphier_aio_probe(struct platform_device *pdev) - ret = devm_snd_soc_register_component(dev, &uniphier_aio_component, - chip->chip_spec->dais, - chip->chip_spec->num_dais); -- if (ret) { -- dev_err(dev, "Register component failed.\n"); -+ if (ret) - goto err_out_reset; -- } - - ret = uniphier_aiodma_soc_register_platform(pdev); -- if (ret) { -- dev_err(dev, "Register platform failed.\n"); -+ if (ret) - goto err_out_reset; -- } - - return 0; - -diff --git a/sound/soc/uniphier/aio.h b/sound/soc/uniphier/aio.h ---- a/sound/soc/uniphier/aio.h -+++ b/sound/soc/uniphier/aio.h -@@ -156,7 +156,7 @@ struct uniphier_aio_selector { - int hw; - }; - --/** -+/* - * 'SoftWare MAPping' setting of UniPhier AIO registers. - * - * We have to setup 'virtual' register maps to access 'real' registers of AIO. -diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c ---- a/sound/soc/ux500/mop500.c -+++ b/sound/soc/ux500/mop500.c -@@ -68,10 +68,11 @@ static void mop500_of_node_put(void) - of_node_put(mop500_dai_links[0].codecs->of_node); - } - --static int mop500_of_probe(struct platform_device *pdev, -- struct device_node *np) -+static int mop500_of_probe(struct snd_soc_card *card) - { -+ struct device *dev = card->dev; - struct device_node *codec_np, *msp_np[2]; -+ struct device_node *np = dev->of_node; - int i; - - msp_np[0] = of_parse_phandle(np, "stericsson,cpu-dai", 0); -@@ -79,7 +80,7 @@ static int mop500_of_probe(struct platform_device *pdev, - codec_np = of_parse_phandle(np, "stericsson,audio-codec", 0); - - if (!(msp_np[0] && msp_np[1] && codec_np)) { -- dev_err(&pdev->dev, "Phandle missing or invalid\n"); -+ dev_err(dev, "Phandle missing or invalid\n"); - for (i = 0; i < 2; i++) - of_node_put(msp_np[i]); - of_node_put(codec_np); -@@ -95,21 +96,20 @@ static int mop500_of_probe(struct platform_device *pdev, - mop500_dai_links[i].codecs->name = NULL; - } - -- snd_soc_of_parse_card_name(&mop500_card, "stericsson,card-name"); -+ snd_soc_of_parse_card_name(card, "stericsson,card-name"); - - return 0; - } - - static int mop500_probe(struct platform_device *pdev) - { -- struct device_node *np = pdev->dev.of_node; - int ret; - - dev_dbg(&pdev->dev, "%s: Enter.\n", __func__); - - mop500_card.dev = &pdev->dev; - -- ret = mop500_of_probe(pdev, np); -+ ret = mop500_of_probe(&mop500_card); - if (ret) - return ret; - -diff --git a/sound/soc/ux500/mop500_ab8500.c b/sound/soc/ux500/mop500_ab8500.c ---- a/sound/soc/ux500/mop500_ab8500.c -+++ b/sound/soc/ux500/mop500_ab8500.c -@@ -433,5 +433,5 @@ void mop500_ab8500_remove(struct snd_soc_card *card) - clk_put(drvdata->clk_ptr_ulpclk); - clk_put(drvdata->clk_ptr_intclk); - -- snd_soc_card_set_drvdata(card, drvdata); -+ snd_soc_card_set_drvdata(card, NULL); - } -diff --git a/sound/soc/xilinx/xlnx_formatter_pcm.c b/sound/soc/xilinx/xlnx_formatter_pcm.c ---- a/sound/soc/xilinx/xlnx_formatter_pcm.c -+++ b/sound/soc/xilinx/xlnx_formatter_pcm.c -@@ -9,8 +9,6 @@ - #include - #include - #include --#include --#include - #include - - #include -@@ -384,7 +382,7 @@ static int xlnx_formatter_pcm_open(struct snd_soc_component *component, - if (err) { - dev_err(component->dev, - "Unable to set constraint on period bytes\n"); -- return err; -+ goto error; - } - - /* Resize the buffer bytes as divisible by 64 */ -@@ -394,7 +392,7 @@ static int xlnx_formatter_pcm_open(struct snd_soc_component *component, - if (err) { - dev_err(component->dev, - "Unable to set constraint on buffer bytes\n"); -- return err; -+ goto error; - } - - /* Set periods as integer multiple */ -@@ -403,7 +401,7 @@ static int xlnx_formatter_pcm_open(struct snd_soc_component *component, - if (err < 0) { - dev_err(component->dev, - "Unable to set constraint on periods to be integer\n"); -- return err; -+ goto error; - } - - /* enable DMA IOC irq */ -@@ -412,6 +410,14 @@ static int xlnx_formatter_pcm_open(struct snd_soc_component *component, - writel(val, stream_data->mmio + XLNX_AUD_CTRL); - - return 0; -+ -+error: -+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) -+ adata->play_stream = NULL; -+ else -+ adata->capture_stream = NULL; -+ kfree(stream_data); -+ return err; - } - - static int xlnx_formatter_pcm_close(struct snd_soc_component *component, -diff --git a/sound/soc/xilinx/xlnx_i2s.c b/sound/soc/xilinx/xlnx_i2s.c ---- a/sound/soc/xilinx/xlnx_i2s.c -+++ b/sound/soc/xilinx/xlnx_i2s.c -@@ -9,9 +9,8 @@ - - #include - #include --#include --#include - #include -+#include - #include - #include - -@@ -174,7 +173,6 @@ static int xlnx_i2s_probe(struct platform_device *pdev) - int ret; - u32 format; - struct device *dev = &pdev->dev; -- struct device_node *node = dev->of_node; - - drv_data = devm_kzalloc(&pdev->dev, sizeof(*drv_data), GFP_KERNEL); - if (!drv_data) -@@ -184,18 +182,16 @@ static int xlnx_i2s_probe(struct platform_device *pdev) - if (IS_ERR(drv_data->base)) - return PTR_ERR(drv_data->base); - -- ret = of_property_read_u32(node, "xlnx,num-channels", &drv_data->channels); -- if (ret < 0) { -- dev_err(dev, "cannot get supported channels\n"); -- return ret; -- } -+ ret = device_property_read_u32(dev, "xlnx,num-channels", &drv_data->channels); -+ if (ret < 0) -+ return dev_err_probe(dev, ret, "cannot get supported channels\n"); -+ - drv_data->channels *= 2; - -- ret = of_property_read_u32(node, "xlnx,dwidth", &drv_data->data_width); -- if (ret < 0) { -- dev_err(dev, "cannot get data width\n"); -- return ret; -- } -+ ret = device_property_read_u32(dev, "xlnx,dwidth", &drv_data->data_width); -+ if (ret < 0) -+ return dev_err_probe(dev, ret, "cannot get data width\n"); -+ - switch (drv_data->data_width) { - case 16: - format = SNDRV_PCM_FMTBIT_S16_LE; -@@ -207,7 +203,7 @@ static int xlnx_i2s_probe(struct platform_device *pdev) - return -EINVAL; - } - -- if (of_device_is_compatible(node, "xlnx,i2s-transmitter-1.0")) { -+ if (device_is_compatible(dev, "xlnx,i2s-transmitter-1.0")) { - drv_data->dai_drv.name = "xlnx_i2s_playback"; - drv_data->dai_drv.playback.stream_name = "Playback"; - drv_data->dai_drv.playback.formats = format; -@@ -215,7 +211,7 @@ static int xlnx_i2s_probe(struct platform_device *pdev) - drv_data->dai_drv.playback.channels_max = drv_data->channels; - drv_data->dai_drv.playback.rates = SNDRV_PCM_RATE_8000_192000; - drv_data->dai_drv.ops = &xlnx_i2s_dai_ops; -- } else if (of_device_is_compatible(node, "xlnx,i2s-receiver-1.0")) { -+ } else if (device_is_compatible(dev, "xlnx,i2s-receiver-1.0")) { - drv_data->dai_drv.name = "xlnx_i2s_capture"; - drv_data->dai_drv.capture.stream_name = "Capture"; - drv_data->dai_drv.capture.formats = format; -@@ -233,10 +229,8 @@ static int xlnx_i2s_probe(struct platform_device *pdev) - - ret = devm_snd_soc_register_component(&pdev->dev, &xlnx_i2s_component, - &drv_data->dai_drv, 1); -- if (ret) { -- dev_err(&pdev->dev, "i2s component registration failed\n"); -+ if (ret) - return ret; -- } - - dev_info(&pdev->dev, "%s DAI registered\n", drv_data->dai_drv.name); - -diff --git a/sound/soc/xilinx/xlnx_spdif.c b/sound/soc/xilinx/xlnx_spdif.c ---- a/sound/soc/xilinx/xlnx_spdif.c -+++ b/sound/soc/xilinx/xlnx_spdif.c -@@ -10,8 +10,7 @@ - #include - #include - #include --#include --#include -+#include - #include - #include - #include -@@ -242,28 +241,24 @@ static int xlnx_spdif_probe(struct platform_device *pdev) - struct spdif_dev_data *ctx; - - struct device *dev = &pdev->dev; -- struct device_node *node = dev->of_node; - - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - ctx->axi_clk = devm_clk_get_enabled(dev, "s_axi_aclk"); -- if (IS_ERR(ctx->axi_clk)) { -- ret = PTR_ERR(ctx->axi_clk); -- dev_err(dev, "failed to get s_axi_aclk(%d)\n", ret); -- return ret; -- } -+ if (IS_ERR(ctx->axi_clk)) -+ return dev_err_probe(dev, PTR_ERR(ctx->axi_clk), -+ "failed to get s_axi_aclk\n"); - - ctx->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(ctx->base)) - return PTR_ERR(ctx->base); - -- ret = of_property_read_u32(node, "xlnx,spdif-mode", &ctx->mode); -- if (ret < 0) { -- dev_err(dev, "cannot get SPDIF mode\n"); -- return ret; -- } -+ ret = device_property_read_u32(dev, "xlnx,spdif-mode", &ctx->mode); -+ if (ret < 0) -+ return dev_err_probe(dev, ret, "cannot get SPDIF mode\n"); -+ - if (ctx->mode) { - dai_drv = &xlnx_spdif_tx_dai; - } else { -@@ -274,29 +269,23 @@ static int xlnx_spdif_probe(struct platform_device *pdev) - ret = devm_request_irq(dev, ret, - xlnx_spdifrx_irq_handler, - 0, "XLNX_SPDIF_RX", ctx); -- if (ret) { -- dev_err(dev, "spdif rx irq request failed\n"); -- return -ENODEV; -- } -+ if (ret) -+ return ret; - - init_waitqueue_head(&ctx->chsts_q); - dai_drv = &xlnx_spdif_rx_dai; - } - -- ret = of_property_read_u32(node, "xlnx,aud_clk_i", &ctx->aclk); -- if (ret < 0) { -- dev_err(dev, "cannot get aud_clk_i value\n"); -- return ret; -- } -+ ret = device_property_read_u32(dev, "xlnx,aud_clk_i", &ctx->aclk); -+ if (ret < 0) -+ return dev_err_probe(dev, ret, "cannot get aud_clk_i value\n"); - - dev_set_drvdata(dev, ctx); - - ret = devm_snd_soc_register_component(dev, &xlnx_spdif_component, - dai_drv, 1); -- if (ret) { -- dev_err(dev, "SPDIF component registration failed\n"); -+ if (ret) - return ret; -- } - - writel(XSPDIF_SOFT_RESET_VALUE, ctx->base + XSPDIF_SOFT_RESET_REG); - dev_info(dev, "%s DAI registered\n", dai_drv->name); -diff --git a/sound/soc/xtensa/xtfpga-i2s.c b/sound/soc/xtensa/xtfpga-i2s.c ---- a/sound/soc/xtensa/xtfpga-i2s.c -+++ b/sound/soc/xtensa/xtfpga-i2s.c -@@ -533,34 +533,25 @@ static int xtfpga_i2s_probe(struct platform_device *pdev) - int err, irq; - - i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); -- if (!i2s) { -- err = -ENOMEM; -- goto err; -- } -+ if (!i2s) -+ return -ENOMEM; -+ - platform_set_drvdata(pdev, i2s); - i2s->dev = &pdev->dev; - dev_dbg(&pdev->dev, "dev: %p, i2s: %p\n", &pdev->dev, i2s); - - i2s->regs = devm_platform_ioremap_resource(pdev, 0); -- if (IS_ERR(i2s->regs)) { -- err = PTR_ERR(i2s->regs); -- goto err; -- } -+ if (IS_ERR(i2s->regs)) -+ return PTR_ERR(i2s->regs); - - i2s->regmap = devm_regmap_init_mmio(&pdev->dev, i2s->regs, - &xtfpga_i2s_regmap_config); -- if (IS_ERR(i2s->regmap)) { -- dev_err(&pdev->dev, "regmap init failed\n"); -- err = PTR_ERR(i2s->regmap); -- goto err; -- } -+ if (IS_ERR(i2s->regmap)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->regmap), "regmap init failed\n"); - - i2s->clk = devm_clk_get(&pdev->dev, NULL); -- if (IS_ERR(i2s->clk)) { -- dev_err(&pdev->dev, "couldn't get clock\n"); -- err = PTR_ERR(i2s->clk); -- goto err; -- } -+ if (IS_ERR(i2s->clk)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->clk), "couldn't get clock\n"); - - regmap_write(i2s->regmap, XTFPGA_I2S_CONFIG, - (0x1 << XTFPGA_I2S_CONFIG_CHANNEL_BASE)); -@@ -568,41 +559,34 @@ static int xtfpga_i2s_probe(struct platform_device *pdev) - regmap_write(i2s->regmap, XTFPGA_I2S_INT_MASK, XTFPGA_I2S_INT_UNDERRUN); - - irq = platform_get_irq(pdev, 0); -- if (irq < 0) { -- err = irq; -- goto err; -- } -+ if (irq < 0) -+ return irq; -+ - err = devm_request_threaded_irq(&pdev->dev, irq, NULL, - xtfpga_i2s_threaded_irq_handler, - IRQF_SHARED | IRQF_ONESHOT, - pdev->name, i2s); -- if (err < 0) { -- dev_err(&pdev->dev, "request_irq failed\n"); -- goto err; -- } -+ if (err < 0) -+ return err; - - err = devm_snd_soc_register_component(&pdev->dev, - &xtfpga_i2s_component, - xtfpga_i2s_dai, - ARRAY_SIZE(xtfpga_i2s_dai)); -- if (err < 0) { -- dev_err(&pdev->dev, "couldn't register component\n"); -- goto err; -- } -+ if (err < 0) -+ return err; -+ - - pm_runtime_enable(&pdev->dev); - if (!pm_runtime_enabled(&pdev->dev)) { - err = xtfpga_i2s_runtime_resume(&pdev->dev); -- if (err) -- goto err_pm_disable; -+ if (err) { -+ pm_runtime_disable(&pdev->dev); -+ return err; -+ } - } -- return 0; - --err_pm_disable: -- pm_runtime_disable(&pdev->dev); --err: -- dev_err(&pdev->dev, "%s: err = %d\n", __func__, err); -- return err; -+ return 0; - } - - static void xtfpga_i2s_remove(struct platform_device *pdev) diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c --- a/sound/sparc/dbri.c +++ b/sound/sparc/dbri.c @@ -50398,7 +11755,7 @@ diff --git a/sound/usb/clock.c b/sound/usb/clock.c diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c -@@ -1236,7 +1236,10 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep) +@@ -1237,7 +1237,10 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep) /* try to use enough URBs to contain an entire ALSA buffer */ max_urbs = min((unsigned) MAX_URBS, MAX_QUEUE * packs_per_ms / urb_packs); @@ -50409,8 +11766,8 @@ diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c + ep->nurbs = min(max_urbs, urbs_per_period * ep->cur_buffer_periods); } - /* allocate and initialize data urbs */ -@@ -1260,6 +1263,8 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep) + if (fmt->fmt_type == UAC_FORMAT_TYPE_II) +@@ -1262,6 +1265,8 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep) goto out_of_memory; u->urb->pipe = ep->pipe; u->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; diff --git a/pkgbuilds/linux-omarchy/0510-sound-updates.patch.sig b/pkgbuilds/linux-omarchy/0510-sound-updates.patch.sig index cfb31fc..3f24244 100644 Binary files a/pkgbuilds/linux-omarchy/0510-sound-updates.patch.sig and b/pkgbuilds/linux-omarchy/0510-sound-updates.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0511-asoc-updates.patch b/pkgbuilds/linux-omarchy/0511-asoc-updates.patch new file mode 100644 index 0000000..75c49b4 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0511-asoc-updates.patch @@ -0,0 +1,37891 @@ +diff --git a/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c b/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c +--- a/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c ++++ b/drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c +@@ -147,7 +147,7 @@ static const struct reg_default halo_register_defaults[] = { + { 0x2b805c0, 0 }, /* HALO_SCRATCH1 */ + { 0x2b805c8, 0 }, /* HALO_SCRATCH2 */ + { 0x2b805d0, 0 }, /* HALO_SCRATCH3 */ +- { 0x2b805c8, 0 }, /* HALO_SCRATCH4 */ ++ { 0x2b805d8, 0 }, /* HALO_SCRATCH4 */ + { 0x2bc1000, 0 }, /* HALO_CCM_CORE_CONTROL */ + { 0x2bc7000, 0 }, /* HALO_WDT_CONTROL */ + +diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c +--- a/drivers/soundwire/bus_type.c ++++ b/drivers/soundwire/bus_type.c +@@ -105,6 +105,9 @@ static int sdw_bus_probe(struct device *dev) + } + slave->index = ret; + ++ /* Create IRQ mapping now so the driver can get it in probe() */ ++ sdw_irq_create_mapping(slave); ++ + ret = drv->probe(slave, id); + if (ret) { + ida_free(&slave->bus->slave_ida, slave->index); +@@ -117,9 +120,6 @@ static int sdw_bus_probe(struct device *dev) + if (drv->ops && drv->ops->read_prop) + drv->ops->read_prop(slave); + +- if (slave->prop.use_domain_irq) +- sdw_irq_create_mapping(slave); +- + /* init the dynamic sysfs attributes we need */ + ret = sdw_slave_sysfs_dpn_init(slave); + if (ret < 0) +diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h +--- a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h ++++ b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h +@@ -152,6 +152,10 @@ + #define LPI_MI2S_TX_4 146 + #define SENARY_MI2S_RX 147 + #define SENARY_MI2S_TX 148 ++#define LPI_MI2S_RX_5 149 ++#define LPI_MI2S_TX_5 150 ++#define LPI_MI2S_RX_6 151 ++#define LPI_MI2S_TX_6 152 + + #define LPASS_CLK_ID_PRI_MI2S_IBIT 1 + #define LPASS_CLK_ID_PRI_MI2S_EBIT 2 +@@ -233,11 +237,39 @@ + /* Clock ID for RX CORE MCLK2 2X MCLK */ + #define LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK 70 + ++#define LAPSS_CLK_ID_QAIF_IF0_IBIT 71 ++#define LAPSS_CLK_ID_QAIF_IF0_EBIT 72 ++#define LAPSS_CLK_ID_QAIF_IF1_IBIT 73 ++#define LAPSS_CLK_ID_QAIF_IF1_EBIT 74 ++#define LAPSS_CLK_ID_QAIF_IF2_IBIT 75 ++#define LAPSS_CLK_ID_QAIF_IF2_EBIT 76 ++#define LAPSS_CLK_ID_QAIF_IF3_IBIT 77 ++#define LAPSS_CLK_ID_QAIF_IF3_EBIT 78 ++#define LAPSS_CLK_ID_QAIF_IF4_IBIT 79 ++#define LAPSS_CLK_ID_QAIF_IF4_EBIT 80 ++#define LAPSS_CLK_ID_QAIF_IF5_IBIT 81 ++#define LAPSS_CLK_ID_QAIF_IF5_EBIT 82 ++#define LAPSS_CLK_ID_QAIF_IF6_IBIT 83 ++#define LAPSS_CLK_ID_QAIF_IF6_EBIT 84 ++#define LAPSS_CLK_ID_QAIF_IF7_IBIT 85 ++#define LAPSS_CLK_ID_QAIF_IF7_EBIT 86 ++#define LAPSS_CLK_ID_QAIF_IF8_IBIT 87 ++#define LAPSS_CLK_ID_QAIF_IF8_EBIT 88 ++#define LAPSS_CLK_ID_QAIF_IF9_IBIT 89 ++#define LAPSS_CLK_ID_QAIF_IF9_EBIT 90 ++#define LAPSS_CLK_ID_QAIF_IF10_IBIT 91 ++#define LAPSS_CLK_ID_QAIF_IF10_EBIT 92 ++#define LAPSS_CLK_ID_QAIF_IF11_IBIT 93 ++#define LAPSS_CLK_ID_QAIF_IF11_EBIT 94 ++#define LAPSS_CLK_ID_QAIF_IF12_IBIT 95 ++#define LAPSS_CLK_ID_QAIF_IF12_EBIT 96 ++#define LAPSS_CLK_ID_VA_QAIF_IF0_IBIT 97 ++#define LAPSS_CLK_ID_VA_QAIF_IF0_EBIT 98 ++ + #define LPASS_HW_AVTIMER_VOTE 101 + #define LPASS_HW_MACRO_VOTE 102 + #define LPASS_HW_DCODEC_VOTE 103 +- +-#define Q6AFE_MAX_CLK_ID 104 ++#define LPASS_HW_LPR_VOTE 104 + + #define LPASS_CLK_ATTRIBUTE_INVALID 0x0 + #define LPASS_CLK_ATTRIBUTE_COUPLE_NO 0x1 +diff --git a/include/linux/platform_data/omap-twl4030.h b/include/linux/platform_data/omap-twl4030.h +deleted file mode 100644 +--- a/include/linux/platform_data/omap-twl4030.h ++++ /dev/null +@@ -1,42 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-only */ +-/** +- * omap-twl4030.h - ASoC machine driver for TI SoC based boards with twl4030 +- * codec, header. +- * +- * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com +- * All rights reserved. +- * +- * Author: Peter Ujfalusi +- */ +- +-#ifndef _OMAP_TWL4030_H_ +-#define _OMAP_TWL4030_H_ +- +-/* To select if only one channel is connected in a stereo port */ +-#define OMAP_TWL4030_LEFT (1 << 0) +-#define OMAP_TWL4030_RIGHT (1 << 1) +- +-struct omap_tw4030_pdata { +- const char *card_name; +- /* Voice port is connected to McBSP3 */ +- bool voice_connected; +- +- /* The driver will parse the connection flags if this flag is set */ +- bool custom_routing; +- /* Flags to indicate connected audio ports. */ +- u8 has_hs; +- u8 has_hf; +- u8 has_predriv; +- u8 has_carkit; +- bool has_ear; +- +- bool has_mainmic; +- bool has_submic; +- bool has_hsmic; +- bool has_carkitmic; +- bool has_digimic0; +- bool has_digimic1; +- u8 has_linein; +-}; +- +-#endif /* _OMAP_TWL4030_H_ */ +diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h +--- a/include/sound/cs35l56.h ++++ b/include/sound/cs35l56.h +@@ -417,7 +417,6 @@ void cs35l56_wait_control_port_ready(void); + void cs35l56_wait_min_reset_pulse(void); + void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire); + int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq); +-irqreturn_t cs35l56_irq(int irq, void *data); + int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base); + int cs35l56_runtime_suspend_common(struct cs35l56_base *cs35l56_base); + int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_soundwire); +diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h +--- a/include/sound/dmaengine_pcm.h ++++ b/include/sound/dmaengine_pcm.h +@@ -175,12 +175,6 @@ int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream *substream, + struct dmaengine_pcm { + struct dma_chan *chan[SNDRV_PCM_STREAM_LAST + 1]; + const struct snd_dmaengine_pcm_config *config; +- struct snd_soc_component component; + unsigned int flags; + }; +- +-static inline struct dmaengine_pcm *soc_component_to_pcm(struct snd_soc_component *p) +-{ +- return container_of(p, struct dmaengine_pcm, component); +-} + #endif +diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h +--- a/include/sound/hda-mlink.h ++++ b/include/sound/hda-mlink.h +@@ -9,6 +9,22 @@ + struct hdac_bus; + struct hdac_ext_link; + ++/** ++ * enum hda_bus_ml_link_type - mlink link type, used by SOF link DMA ++ * allocator constraints (see struct sof_intel_hda_dev). ++ * ++ * @HDA_BUS_ML_LINK_HDA: non-alt link, i.e. HDA codec or iDisp ++ * @HDA_BUS_ML_LINK_SDW: alt link, SoundWire ++ * @HDA_BUS_ML_LINK_UAOL: alt link, USB Audio Offload ++ * @HDA_BUS_ML_LINK_OTHER: alt link, SSP or DMIC ++ */ ++enum hda_bus_ml_link_type { ++ HDA_BUS_ML_LINK_HDA, ++ HDA_BUS_ML_LINK_SDW, ++ HDA_BUS_ML_LINK_UAOL, ++ HDA_BUS_ML_LINK_OTHER, ++}; ++ + #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK) + + int hda_bus_ml_init(struct hdac_bus *bus); +@@ -49,11 +65,12 @@ int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num); + int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, + int channel_mask, int stream_id, int dir); + +-void hda_bus_ml_put_all(struct hdac_bus *bus); + void hda_bus_ml_reset_losidv(struct hdac_bus *bus); + int hda_bus_ml_resume(struct hdac_bus *bus); + int hda_bus_ml_suspend(struct hdac_bus *bus); + ++enum hda_bus_ml_link_type hda_bus_ml_link_get_type(struct hdac_ext_link *hlink); ++ + struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus); + struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus); + struct hdac_ext_link *hdac_bus_eml_sdw_get_hlink(struct hdac_bus *bus); +@@ -169,11 +186,13 @@ hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, + return 0; + } + +-static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { } + static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { } + static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; } + static inline int hda_bus_ml_suspend(struct hdac_bus *bus) { return 0; } + ++static inline enum hda_bus_ml_link_type ++hda_bus_ml_link_get_type(struct hdac_ext_link *hlink) { return HDA_BUS_ML_LINK_HDA; } ++ + static inline struct hdac_ext_link * + hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus) { return NULL; } + +diff --git a/include/sound/sdca_asoc.h b/include/sound/sdca_asoc.h +--- a/include/sound/sdca_asoc.h ++++ b/include/sound/sdca_asoc.h +@@ -25,6 +25,8 @@ struct snd_soc_dai_driver; + struct snd_soc_dai_ops; + struct snd_soc_dapm_route; + struct snd_soc_dapm_widget; ++struct snd_soc_pcm_stream; ++struct sdca_entity; + + /* convenient macro to handle the mono volume in 7.8 fixed format representation */ + #define SDCA_SINGLE_Q78_TLV(xname, xreg, xmin, xmax, xstep, tlv_array) \ +@@ -82,6 +84,11 @@ int sdca_asoc_populate_component(struct device *dev, + struct snd_soc_dai_driver **dai_drv, int *num_dai_drv, + const struct snd_soc_dai_ops *ops); + ++int sdca_asoc_populate_rate_format(struct device *dev, ++ struct sdca_function_data *function, ++ struct sdca_entity *entity, ++ struct snd_soc_pcm_stream *stream); ++ + int sdca_asoc_set_constraints(struct device *dev, struct regmap *regmap, + struct sdca_function_data *function, + struct snd_pcm_substream *substream, +@@ -100,9 +107,9 @@ int sdca_asoc_q78_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + int sdca_asoc_q78_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +-int sdca_asoc_pde_poll_actual_ps(struct device *dev, struct regmap *regmap, ++int sdca_asoc_pde_poll_actual_ps(struct regmap *regmap, + int function_id, int entity_id, +- int from_ps, int to_ps, +- const struct sdca_pde_delay *pde_delays, +- int num_delays); ++ int from_ps, int to_ps, ++ const struct sdca_pde_delay *pde_delays, ++ int num_delays); + #endif // __SDCA_ASOC_H__ +diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h +--- a/include/sound/sdca_function.h ++++ b/include/sound/sdca_function.h +@@ -298,7 +298,7 @@ enum sdca_xu_controls { + }; + + /** +- * enum sdca_set_index_range - Column definitions UMP SetIndex ++ * enum sdca_fdl_set_index_range - Column definitions UMP SetIndex + */ + enum sdca_fdl_set_index_range { + SDCA_FDL_SET_INDEX_SET_NUMBER = 0, +@@ -803,6 +803,8 @@ struct sdca_control_range { + * @mode: Access mode of the Control. + * @layers: Bitmask of access layers of the Control. + * @deferrable: Indicates if the access to the Control can be deferred. ++ * @is_volatile: Indicates the Control registers are forced to be treated ++ * as volatile. + * @has_default: Indicates the Control has a default value to be written. + * @has_reset: Indicates the Control has a defined reset value. + * @has_fixed: Indicates the Control only supports a single value. +@@ -1116,7 +1118,6 @@ struct sdca_entity_ge { + + /** + * struct sdca_entity_hide - information specific to HIDE Entities +- * @hid: HID device structure + * @num_hidtx_ids: number of HIDTx Report ID + * @num_hidrx_ids: number of HIDRx Report ID + * @hidtx_ids: HIDTx Report ID +@@ -1127,11 +1128,8 @@ struct sdca_entity_ge { + * within this Device + * @max_delay: the maximum time in microseconds allowed for the Device + * to change the ownership from Device to Host +- * @hid_report_desc: HID Report Descriptor for the HIDE Entity +- * @hid_desc: HID descriptor for the HIDE Entity + */ + struct sdca_entity_hide { +- struct hid_device *hid; + unsigned int *hidtx_ids; + unsigned int *hidrx_ids; + int num_hidtx_ids; +@@ -1139,12 +1137,10 @@ struct sdca_entity_hide { + unsigned int af_number_list[SDCA_MAX_FUNCTION_COUNT]; + unsigned int hide_reside_function_num; + unsigned int max_delay; +- unsigned char *hid_report_desc; +- struct hid_descriptor hid_desc; + }; + + /** +- * enum sdca_xu_reset_machanism - SDCA FDL Resets ++ * enum sdca_xu_reset_mechanism - SDCA FDL Resets + */ + enum sdca_xu_reset_mechanism { + SDCA_XU_RESET_FUNCTION = 0x0, +@@ -1401,6 +1397,16 @@ struct sdca_fdl_data { + int num_sets; + }; + ++/** ++ * struct sdca_function_hid - information about a function's HID descriptors ++ * @report_desc: HID Report Descriptor for the HID Function ++ * @desc: HID descriptor for the HID Function ++ */ ++struct sdca_function_hid { ++ unsigned char *report_desc; ++ struct hid_descriptor desc; ++}; ++ + /** + * struct sdca_function_data - top-level information for one SDCA function + * @desc: Pointer to short descriptor from initial parsing. +@@ -1415,6 +1421,7 @@ struct sdca_fdl_data { + * @reset_max_delay: Maximum Function reset delay in microseconds, before an + * error should be reported. + * @fdl_data: FDL data for this Function, if available. ++ * @hid: HID data for this Function, if available. + */ + struct sdca_function_data { + struct sdca_function_desc *desc; +@@ -1430,6 +1437,10 @@ struct sdca_function_data { + unsigned int reset_max_delay; + + struct sdca_fdl_data fdl_data; ++ ++ union { ++ struct sdca_function_hid hid; ++ }; + }; + + static inline u32 sdca_range(struct sdca_control_range *range, +@@ -1451,8 +1462,7 @@ static inline u32 sdca_range_search(struct sdca_control_range *range, + return 0; + } + +-int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, +- struct sdca_function_data *function); ++int sdca_parse_function(struct device *dev, struct sdca_function_data *function); + + const char *sdca_find_terminal_name(enum sdca_terminal_type type); + +@@ -1469,5 +1479,7 @@ struct sdca_control_range *sdca_selector_find_range(struct device *dev, + struct sdca_cluster *sdca_id_find_cluster(struct device *dev, + struct sdca_function_data *function, + const int id); ++struct sdca_entity *sdca_find_entity_by_label(struct sdca_function_data *function, ++ const char *entity_label); + + #endif +diff --git a/include/sound/sdca_hid.h b/include/sound/sdca_hid.h +--- a/include/sound/sdca_hid.h ++++ b/include/sound/sdca_hid.h +@@ -16,18 +16,21 @@ struct sdca_interrupt; + + #if IS_ENABLED(CONFIG_SND_SOC_SDCA_HID) + +-int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, +- struct sdca_entity *entity); ++int sdca_add_hid_device(struct sdca_interrupt *interrupt); ++void sdca_destroy_hid_device(struct sdca_interrupt *interrupt); + int sdca_hid_process_report(struct sdca_interrupt *interrupt); + + #else + +-static inline int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, +- struct sdca_entity *entity) ++static inline int sdca_add_hid_device(struct sdca_interrupt *interrupt) + { + return 0; + } + ++static inline void sdca_destroy_hid_device(struct sdca_interrupt *interrupt) ++{ ++} ++ + static inline int sdca_hid_process_report(struct sdca_interrupt *interrupt) + { + return 0; +diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h +--- a/include/sound/simple_card_utils.h ++++ b/include/sound/simple_card_utils.h +@@ -181,7 +181,7 @@ void simple_util_canonicalize_platform(struct snd_soc_dai_link_component *platfo + void simple_util_canonicalize_cpu(struct snd_soc_dai_link_component *cpus, + int is_single_links); + +-void simple_util_clean_reference(struct snd_soc_card *card); ++void simple_util_clean_reference(struct simple_util_priv *priv); + + void simple_util_parse_convert(struct device_node *np, char *prefix, + struct simple_util_data *data); +@@ -189,18 +189,37 @@ bool simple_util_is_convert_required(const struct simple_util_data *data); + + int simple_util_get_sample_fmt(struct simple_util_data *data); + +-int simple_util_parse_routing(struct snd_soc_card *card, +- char *prefix); +-int simple_util_parse_widgets(struct snd_soc_card *card, +- char *prefix); +-int simple_util_parse_pin_switches(struct snd_soc_card *card, +- char *prefix); ++int simple_util_parse_property(struct simple_util_priv *priv, ++ int (*func)(struct snd_soc_card *card, const char *propname), ++ char *prefix, char *property); ++static inline int simple_util_parse_routing(struct simple_util_priv *priv, char *prefix) ++{ ++ return simple_util_parse_property(priv, snd_soc_of_parse_audio_routing, ++ prefix, "routing"); ++} ++ ++static inline int simple_util_parse_widgets(struct simple_util_priv *priv, char *prefix) ++{ ++ return simple_util_parse_property(priv, snd_soc_of_parse_audio_simple_widgets, ++ prefix, "widgets"); ++} ++ ++static inline int simple_util_parse_pin_switches(struct simple_util_priv *priv, char *prefix) ++{ ++ return simple_util_parse_property(priv, snd_soc_of_parse_pin_switches, ++ prefix, "pin-switches"); ++} ++ ++static inline int simple_util_parse_aux_devs(struct simple_util_priv *priv, char *prefix) ++{ ++ return simple_util_parse_property(priv, snd_soc_of_parse_aux_devs, ++ prefix, "aux-devs"); ++} + + int simple_util_init_jack(struct snd_soc_card *card, + struct simple_util_jack *sjack, + int is_hp, char *prefix, char *pin); +-int simple_util_init_aux_jacks(struct simple_util_priv *priv, +- char *prefix); ++int simple_util_init_aux_jacks(struct snd_soc_card *card, char *prefix); + int simple_util_init_priv(struct simple_util_priv *priv, + struct link_info *li); + void simple_util_remove(struct platform_device *pdev); +diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h +--- a/include/sound/soc-acpi.h ++++ b/include/sound/soc-acpi.h +@@ -57,7 +57,7 @@ static inline struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg) + #endif + + /** +- * snd_soc_acpi_mach_params: interface for machine driver configuration ++ * struct snd_soc_acpi_mach_params - interface for machine driver configuration + * + * @acpi_ipc_irq_index: used for BYT-CR detection + * @platform: string used for HDAudio codec support +@@ -93,7 +93,7 @@ struct snd_soc_acpi_mach_params { + }; + + /** +- * snd_soc_acpi_endpoint - endpoint descriptor ++ * struct snd_soc_acpi_endpoint - endpoint descriptor + * @num: endpoint number (mandatory, unique per device) + * @aggregated: 0 (independent) or 1 (logically grouped) + * @group_position: zero-based order (only when @aggregated is 1) +@@ -107,7 +107,7 @@ struct snd_soc_acpi_endpoint { + }; + + /** +- * snd_soc_acpi_adr_device - descriptor for _ADR-enumerated device ++ * struct snd_soc_acpi_adr_device - descriptor for _ADR-enumerated device + * @adr: 64 bit ACPI _ADR value + * @num_endpoints: number of endpoints for this device + * @endpoints: array of endpoints +@@ -121,7 +121,7 @@ struct snd_soc_acpi_adr_device { + }; + + /** +- * snd_soc_acpi_link_adr - ACPI-based list of _ADR enumerated devices ++ * struct snd_soc_acpi_link_adr - ACPI-based list of _ADR enumerated devices + * @mask: one bit set indicates the link this list applies to + * @num_adr: ARRAY_SIZE of devices + * @adr_d: array of devices +@@ -167,8 +167,8 @@ struct snd_soc_acpi_link_adr { + #define SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME BIT(4) + + /** +- * snd_soc_acpi_mach: ACPI-based machine descriptor. Most of the fields are +- * related to the hardware, except for the firmware and topology file names. ++ * struct snd_soc_acpi_mach - ACPI-based machine descriptor. Most of the fields ++ * are related to the hardware, except for the firmware and topology file names. + * A platform supported by legacy and Sound Open Firmware (SOF) would expose + * all firmware/topology related fields. + * +@@ -192,6 +192,7 @@ struct snd_soc_acpi_link_adr { + * the initial selection in the snd_soc_acpi_mach table. + * @pdata: intended for platform data or machine specific-ops. This structure + * is not constant since this field may be updated at run-time ++ * @mach_params: machine driver configuration + * @sof_tplg_filename: Sound Open Firmware topology file name, if enabled + * @tplg_quirk_mask: quirks to select different topology files dynamically + * @get_function_tplg_files: This is an optional callback, if specified then instead of +@@ -199,11 +200,11 @@ struct snd_soc_acpi_link_adr { + * files to be loaded. + * Return value: The number of the files or negative ERRNO. 0 means that the single topology + * file should be used, no function topology split can be used on the machine. +- * @card: the pointer of the card +- * @mach: the pointer of the machine driver +- * @prefix: the prefix of the topology file name. Typically, it is the path. +- * @tplg_files: the pointer of the array of the topology file names. +- * @best_effort: ignore non supported links and try to build the card in best effort ++ * card: the pointer of the card ++ * mach: the pointer of the machine driver ++ * prefix: the prefix of the topology file name. Typically, it is the path. ++ * tplg_files: the pointer of the array of the topology file names. ++ * best_effort: ignore non supported links and try to build the card in best effort + * with supported links + */ + /* Descriptor for SST ASoC machine driver */ +diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h +--- a/include/sound/soc-component.h ++++ b/include/sound/soc-component.h +@@ -253,6 +253,9 @@ struct snd_soc_component { + void *mark_pm; + + struct dentry *debugfs_root; ++ ++ /* Component private data */ ++ void *priv; + }; + + #define for_each_component_dais(component, dai)\ +@@ -283,6 +286,14 @@ static inline int snd_soc_component_cache_sync( + return regcache_sync(component->regmap); + } + ++struct snd_soc_component *snd_soc_component_alloc(struct device *dev); ++ ++void snd_soc_component_set_name(struct snd_soc_component *component, const char *name); ++const char *snd_soc_component_name(struct snd_soc_component *component); ++ ++void snd_soc_component_set_priv(struct snd_soc_component *component, void *priv); ++void *snd_soc_component_to_priv(struct snd_soc_component *component); ++ + void snd_soc_component_set_aux(struct snd_soc_component *component, + struct snd_soc_aux_dev *aux); + int snd_soc_component_init(struct snd_soc_component *component); +diff --git a/include/sound/soc.h b/include/sound/soc.h +--- a/include/sound/soc.h ++++ b/include/sound/soc.h +@@ -447,15 +447,16 @@ static inline int snd_soc_resume(struct device *dev) + } + #endif + int snd_soc_poweroff(struct device *dev); +-int snd_soc_component_initialize(struct snd_soc_component *component, +- const struct snd_soc_component_driver *driver, +- struct device *dev); +-int snd_soc_add_component(struct snd_soc_component *component, +- struct snd_soc_dai_driver *dai_drv, +- int num_dai); +-int snd_soc_register_component(struct device *dev, ++int snd_soc_register_component_c(struct snd_soc_component *component, + const struct snd_soc_component_driver *component_driver, + struct snd_soc_dai_driver *dai_drv, int num_dai); ++int snd_soc_register_component_d(struct device *dev, ++ const struct snd_soc_component_driver *component_driver, ++ struct snd_soc_dai_driver *dai_drv, int num_dai); ++#define snd_soc_register_component(x, ...) _Generic((x), \ ++struct device * : snd_soc_register_component_d, \ ++struct snd_soc_component * : snd_soc_register_component_c)(x, __VA_ARGS__) ++ + int devm_snd_soc_register_component(struct device *dev, + const struct snd_soc_component_driver *component_driver, + struct snd_soc_dai_driver *dai_drv, int num_dai); +diff --git a/include/sound/sof/dai-amd.h b/include/sound/sof/dai-amd.h +--- a/include/sound/sof/dai-amd.h ++++ b/include/sound/sof/dai-amd.h +@@ -18,6 +18,7 @@ struct sof_ipc_dai_acp_params { + uint32_t fsync_rate; /* FSYNC frequency in Hz */ + uint32_t tdm_slots; + uint32_t tdm_mode; ++ uint32_t format; + } __packed; + + /* ACPDMIC Configuration Request - SOF_IPC_DAI_AMD_CONFIG */ +diff --git a/include/sound/sof/dai.h b/include/sound/sof/dai.h +--- a/include/sound/sof/dai.h ++++ b/include/sound/sof/dai.h +@@ -90,6 +90,8 @@ enum sof_ipc_dai_type { + SOF_DAI_AMD_HS_VIRTUAL, /**< AMD ACP HS VIRTUAL */ + SOF_DAI_IMX_MICFIL, /** < i.MX MICFIL PDM */ + SOF_DAI_AMD_SDW, /**< AMD ACP SDW */ ++ SOF_DAI_INTEL_UAOL, /**< Intel UAOL */ ++ SOF_DAI_AMD_I2S, /**< AMD ACP I2S */ + }; + + /* general purpose DAI configuration */ +@@ -121,6 +123,7 @@ struct sof_ipc_dai_config { + struct sof_ipc_dai_mtk_afe_params afe; + struct sof_ipc_dai_micfil_params micfil; + struct sof_ipc_dai_acp_sdw_params acp_sdw; ++ struct sof_ipc_dai_acp_params acp_i2s; + }; + } __packed; + +diff --git a/include/sound/sof/header.h b/include/sound/sof/header.h +--- a/include/sound/sof/header.h ++++ b/include/sound/sof/header.h +@@ -179,7 +179,7 @@ struct sof_ipc_compound_hdr { + uint32_t count; /**< count of 0 means end of compound sequence */ + } __packed; + +-/** ++/* + * OOPS header architecture specific data. + */ + struct sof_ipc_dsp_oops_arch_hdr { +@@ -187,7 +187,7 @@ struct sof_ipc_dsp_oops_arch_hdr { + uint32_t totalsize; /* Total size of oops message */ + } __packed; + +-/** ++/* + * OOPS header platform specific data. + */ + struct sof_ipc_dsp_oops_plat_hdr { +diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h +--- a/include/sound/sof/ipc4/header.h ++++ b/include/sound/sof/ipc4/header.h +@@ -187,6 +187,10 @@ enum sof_ipc4_pipeline_state { + #define SOF_IPC4_GLB_PIPE_EXT_CORE_ID_MASK GENMASK(23, 20) + #define SOF_IPC4_GLB_PIPE_EXT_CORE_ID(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_CORE_ID_SHIFT) + ++#define SOF_IPC4_GLB_PIPE_PAYLOAD_SHIFT 29 ++#define SOF_IPC4_GLB_PIPE_PAYLOAD_MASK BIT(29) ++#define SOF_IPC4_GLB_PIPE_PAYLOAD(x) ((x) << SOF_IPC4_GLB_PIPE_PAYLOAD_SHIFT) ++ + /* pipeline set state ipc msg */ + #define SOF_IPC4_GLB_PIPE_STATE_ID_SHIFT 16 + #define SOF_IPC4_GLB_PIPE_STATE_ID_MASK GENMASK(23, 16) +@@ -536,12 +540,63 @@ enum sof_ipc4_notification_type { + SOF_IPC4_NOTIFY_TYPE_LAST, + }; + ++enum sof_ipc4_resource_type { ++ SOF_IPC4_MODULE_INSTANCE, ++ SOF_IPC4_PIPELINE, ++ SOF_IPC4_GATEWAY, ++ SOF_IPC4_EDF_TASK, ++ SOF_IPC4_INVALID_RESOURCE_TYPE, ++}; ++ ++enum sof_ipc4_event_type { ++ /* Underrun detected by the Mixer */ ++ SOF_IPC4_MIXER_UNDERRUN_DETECTED = 1, ++ /* Error caught during data processing */ ++ SOF_IPC4_PROCESS_DATA_ERROR = 3, ++ /* Underrun detected by gateway. */ ++ SOF_IPC4_GATEWAY_UNDERRUN_DETECTED = 6, ++ /* Overrun detected by gateway */ ++ SOF_IPC4_GATEWAY_OVERRUN_DETECTED, ++}; ++ ++/** ++ * struct sof_ipc4_process_data_error_event_data - process data error event payload ++ * @error_code: Error code returned by data processing function ++ */ ++struct sof_ipc4_process_data_error_event_data { ++ uint32_t error_code; ++}; ++ ++/** ++ * struct sof_ipc4_mixer_underrun_event_data - mixer underrun event payload ++ * @eos_flag: Indicates EndOfStream ++ * @data_mixed: Data processed by module (in bytes) ++ * @expected_data_mixed: Expected data to be processed (in bytes) ++ */ ++struct sof_ipc4_mixer_underrun_event_data { ++ uint32_t eos_flag; ++ uint32_t data_mixed; ++ uint32_t expected_data_mixed; ++}; ++ ++/** ++ * union sof_ipc4_resource_event_data - resource event specific payload ++ * @dws: Raw event data payload as six dwords ++ * @process_data_error: SOF_IPC4_PROCESS_DATA_ERROR payload ++ * @mixer_underrun: SOF_IPC4_MIXER_UNDERRUN_DETECTED payload ++ */ ++union sof_ipc4_resource_event_data { ++ uint32_t dws[6]; ++ struct sof_ipc4_process_data_error_event_data process_data_error; ++ struct sof_ipc4_mixer_underrun_event_data mixer_underrun; ++}; ++ + struct sof_ipc4_notify_resource_data { + uint32_t resource_type; + uint32_t resource_id; + uint32_t event_type; + uint32_t reserved; +- uint32_t data[6]; ++ union sof_ipc4_resource_event_data data; + } __packed __aligned(4); + + #define SOF_IPC4_DEBUG_DESCRIPTOR_SIZE 12 /* 3 x u32 */ +@@ -654,11 +709,81 @@ enum sof_ipc4_mod_init_ext_obj_id { + SOF_IPC4_MOD_INIT_DATA_ID_MAX = SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA, + }; + +-/* DP module memory configuration data object for ext_init object array */ ++/* DP module memory configuration data object for object array */ + struct sof_ipc4_mod_init_ext_dp_memory_data { ++ u32 domain_id; /* userspace domain ID */ ++ u32 stack_bytes; /* required stack size in bytes */ ++ u32 heap_bytes; /* required heap size in bytes */ ++} __packed __aligned(4); ++ ++/* ++ * This set of macros are very similar to the set above, but these are ++ * for building payload to SOF_IPC4_GLB_CREATE_PIPELINE message. ++ * ++ * Macros for creating struct sof_ipc4_glb_pipe_payload payload with ++ * its associated data. struct sof_ipc4_glb_pipe_payload should be the ++ * first piece of payload following SOF_IPC4_GLB_CREATE_PIPELINE msg, ++ * and its existence is indicated with SOF_IPC4_GLB_PIPE_PAYLOAD bit. ++ * ++ * The macros below apply to sof_ipc4_glb_pipe_payload.word0 ++ */ ++#define SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS_SHIFT 0 ++#define SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS_MASK GENMASK(23, 0) ++#define SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS(x) ((x) << SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS_SHIFT) ++ ++#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_SHIFT 24 ++#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_MASK BIT(24) ++#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_SHIFT) ++ ++struct sof_ipc4_glb_pipe_payload { ++ u32 word0; ++ u32 rsvd1; ++ u32 rsvd2; ++} __packed __aligned(4); ++ ++/* ++ * SOF_IPC4_GLB_CREATE_PIPELINE payload may be followed by arbitrary ++ * number of object array objects. SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY-bit ++ * indicates that an array object follows struct ++ * sof_ipc4_glb_pipe_payload. ++ * ++ * The object header's SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST-bit in struct ++ * sof_ipc4_glb_pipe_ext_object indicates if the array is continued ++ * with another object. The header has also fields to identify the ++ * object, SOF_IPC4_GLB_PIPE_EXT_OBJ_ID, and to indicate the object's ++ * size in 32-bit words, SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS, not ++ * including the header itself. ++ * ++ * The macros below apply to sof_ipc4_glb_pipe_ext_object.header ++ */ ++#define SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_SHIFT 0 ++#define SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_MASK BIT(0) ++#define SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_SHIFT) ++ ++#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ID_SHIFT 1 ++#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ID_MASK GENMASK(15, 1) ++#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ID(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_ID_SHIFT) ++ ++#define SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS_SHIFT 16 ++#define SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS_MASK GENMASK(31, 16) ++#define SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS_SHIFT) ++ ++struct sof_ipc4_glb_pipe_ext_object { ++ u32 header; ++ u32 data[]; ++} __packed __aligned(4); ++ ++enum sof_ipc4_glb_pipe_ext_obj_id { ++ SOF_IPC4_GLB_PIPE_DATA_ID_INVALID = 0, ++ SOF_IPC4_GLB_PIPE_DATA_ID_MEM_DATA, ++ SOF_IPC4_GLB_PIPE_DATA_ID_MAX = SOF_IPC4_GLB_PIPE_DATA_ID_MEM_DATA, ++}; ++ ++/* Pipeline memory configuration data object for ext_init object array */ ++struct sof_ipc4_glb_pipe_ext_obj_memory_data { + u32 domain_id; /* userspace domain ID */ +- u32 stack_bytes; /* stack size in bytes, 0 means default size */ +- u32 heap_bytes; /* stack size in bytes, 0 means default size */ ++ u32 stack_bytes; /* stack size in bytes */ ++ u32 heap_bytes; /* heap size in bytes */ + } __packed __aligned(4); + + /** @}*/ +diff --git a/include/sound/tas2781-dsp.h b/include/sound/tas2781-dsp.h +--- a/include/sound/tas2781-dsp.h ++++ b/include/sound/tas2781-dsp.h +@@ -201,6 +201,11 @@ struct tasdevice_rca { + int ncfgs; + struct tasdevice_config_info **cfg_info; + int profile_cfg_id; ++ /* ++ * Used among SmartAMP for PDM microphone recording or IV data ++ * capture. ++ */ ++ int capture_profile_id; + /* + * Since version 0x105, the keyword 'init' was introduced into the + * profile, which is used for chip initialization, particularly to +@@ -222,7 +227,7 @@ void tasdevice_calbin_remove(void *context); + int tasdevice_select_tuningprm_cfg(void *context, int prm, + int cfg_no, int rca_conf_no); + int tasdevice_prmg_load(void *context, int prm_no); +-void tasdevice_tuning_switch(void *context, int state); ++void tasdevice_tuning_switch(void *context, int state, bool is_cap); + int tas2781_load_calibration(void *context, char *file_name, + unsigned short i); + +diff --git a/include/sound/tlv320aic32x4.h b/include/sound/tlv320aic32x4.h +deleted file mode 100644 +--- a/include/sound/tlv320aic32x4.h ++++ /dev/null +@@ -1,43 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-only */ +-/* +- * tlv320aic32x4.h -- TLV320AIC32X4 Soc Audio driver platform data +- * +- * Copyright 2011 Vista Silicon S.L. +- * +- * Author: Javier Martin +- */ +- +-#ifndef _AIC32X4_PDATA_H +-#define _AIC32X4_PDATA_H +- +-#define AIC32X4_PWR_MICBIAS_2075_LDOIN 0x00000001 +-#define AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE 0x00000002 +-#define AIC32X4_PWR_AIC32X4_LDO_ENABLE 0x00000004 +-#define AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36 0x00000008 +-#define AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED 0x00000010 +- +-#define AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K 0x00000001 +-#define AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K 0x00000002 +- +-/* GPIO API */ +-#define AIC32X4_MFPX_DEFAULT_VALUE 0xff +- +-#define AIC32X4_MFP1_DIN_DISABLED 0 +-#define AIC32X4_MFP1_DIN_ENABLED 0x2 +-#define AIC32X4_MFP1_GPIO_IN 0x4 +- +-#define AIC32X4_MFP2_GPIO_OUT_LOW 0x0 +-#define AIC32X4_MFP2_GPIO_OUT_HIGH 0x1 +- +-#define AIC32X4_MFP_GPIO_ENABLED 0x4 +- +-#define AIC32X4_MFP5_GPIO_DISABLED 0x0 +-#define AIC32X4_MFP5_GPIO_INPUT 0x8 +-#define AIC32X4_MFP5_GPIO_OUTPUT 0xc +-#define AIC32X4_MFP5_GPIO_OUT_LOW 0x0 +-#define AIC32X4_MFP5_GPIO_OUT_HIGH 0x1 +- +-struct aic32x4_setup_data { +- unsigned int gpio_func[5]; +-}; +-#endif +diff --git a/include/sound/wm8904.h b/include/sound/wm8904.h +--- a/include/sound/wm8904.h ++++ b/include/sound/wm8904.h +@@ -116,7 +116,7 @@ + #define WM8904_DRC_REGS 4 + #define WM8904_EQ_REGS 24 + +-/** ++/* + * DRC configurations are specified with a label and a set of register + * values to write (the enable bits will be ignored). At runtime an + * enumerated control will be presented for each DRC block allowing +@@ -131,7 +131,7 @@ struct wm8904_drc_cfg { + u16 regs[WM8904_DRC_REGS]; + }; + +-/** ++/* + * ReTune Mobile configurations are specified with a label, sample + * rate and set of values to write (the enable bits will be ignored). + * +diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h +deleted file mode 100644 +--- a/include/uapi/sound/skl-tplg-interface.h ++++ /dev/null +@@ -1,168 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +-/* +- * skl-tplg-interface.h - Intel DSP FW private data interface +- * +- * Copyright (C) 2015 Intel Corp +- * Author: Jeeja KP +- * Nilofer, Samreen +- */ +- +-#ifndef __HDA_TPLG_INTERFACE_H__ +-#define __HDA_TPLG_INTERFACE_H__ +- +-#include +- +-/* +- * Default types range from 0~12. type can range from 0 to 0xff +- * SST types start at higher to avoid any overlapping in future +- */ +-#define SKL_CONTROL_TYPE_BYTE_TLV 0x100 +-#define SKL_CONTROL_TYPE_MIC_SELECT 0x102 +-#define SKL_CONTROL_TYPE_MULTI_IO_SELECT 0x103 +-#define SKL_CONTROL_TYPE_MULTI_IO_SELECT_DMIC 0x104 +- +-#define HDA_SST_CFG_MAX 900 /* size of copier cfg*/ +-#define MAX_IN_QUEUE 8 +-#define MAX_OUT_QUEUE 8 +- +-#define SKL_UUID_STR_SZ 40 +-/* Event types goes here */ +-/* Reserve event type 0 for no event handlers */ +-enum skl_event_types { +- SKL_EVENT_NONE = 0, +- SKL_MIXER_EVENT, +- SKL_MUX_EVENT, +- SKL_VMIXER_EVENT, +- SKL_PGA_EVENT +-}; +- +-/** +- * enum skl_ch_cfg - channel configuration +- * +- * @SKL_CH_CFG_MONO: One channel only +- * @SKL_CH_CFG_STEREO: L & R +- * @SKL_CH_CFG_2_1: L, R & LFE +- * @SKL_CH_CFG_3_0: L, C & R +- * @SKL_CH_CFG_3_1: L, C, R & LFE +- * @SKL_CH_CFG_QUATRO: L, R, Ls & Rs +- * @SKL_CH_CFG_4_0: L, C, R & Cs +- * @SKL_CH_CFG_5_0: L, C, R, Ls & Rs +- * @SKL_CH_CFG_5_1: L, C, R, Ls, Rs & LFE +- * @SKL_CH_CFG_DUAL_MONO: One channel replicated in two +- * @SKL_CH_CFG_I2S_DUAL_STEREO_0: Stereo(L,R) in 4 slots, 1st stream:[ L, R, -, - ] +- * @SKL_CH_CFG_I2S_DUAL_STEREO_1: Stereo(L,R) in 4 slots, 2nd stream:[ -, -, L, R ] +- * @SKL_CH_CFG_INVALID: Invalid +- */ +-enum skl_ch_cfg { +- SKL_CH_CFG_MONO = 0, +- SKL_CH_CFG_STEREO = 1, +- SKL_CH_CFG_2_1 = 2, +- SKL_CH_CFG_3_0 = 3, +- SKL_CH_CFG_3_1 = 4, +- SKL_CH_CFG_QUATRO = 5, +- SKL_CH_CFG_4_0 = 6, +- SKL_CH_CFG_5_0 = 7, +- SKL_CH_CFG_5_1 = 8, +- SKL_CH_CFG_DUAL_MONO = 9, +- SKL_CH_CFG_I2S_DUAL_STEREO_0 = 10, +- SKL_CH_CFG_I2S_DUAL_STEREO_1 = 11, +- SKL_CH_CFG_7_1 = 12, +- SKL_CH_CFG_4_CHANNEL = SKL_CH_CFG_7_1, +- SKL_CH_CFG_INVALID +-}; +- +-enum skl_module_type { +- SKL_MODULE_TYPE_MIXER = 0, +- SKL_MODULE_TYPE_COPIER, +- SKL_MODULE_TYPE_UPDWMIX, +- SKL_MODULE_TYPE_SRCINT, +- SKL_MODULE_TYPE_ALGO, +- SKL_MODULE_TYPE_BASE_OUTFMT, +- SKL_MODULE_TYPE_KPB, +- SKL_MODULE_TYPE_MIC_SELECT, +-}; +- +-enum skl_core_affinity { +- SKL_AFFINITY_CORE_0 = 0, +- SKL_AFFINITY_CORE_1, +- SKL_AFFINITY_CORE_MAX +-}; +- +-enum skl_pipe_conn_type { +- SKL_PIPE_CONN_TYPE_NONE = 0, +- SKL_PIPE_CONN_TYPE_FE, +- SKL_PIPE_CONN_TYPE_BE +-}; +- +-enum skl_hw_conn_type { +- SKL_CONN_NONE = 0, +- SKL_CONN_SOURCE = 1, +- SKL_CONN_SINK = 2 +-}; +- +-enum skl_dev_type { +- SKL_DEVICE_BT = 0x0, +- SKL_DEVICE_DMIC = 0x1, +- SKL_DEVICE_I2S = 0x2, +- SKL_DEVICE_SLIMBUS = 0x3, +- SKL_DEVICE_HDALINK = 0x4, +- SKL_DEVICE_HDAHOST = 0x5, +- SKL_DEVICE_NONE +-}; +- +-/** +- * enum skl_interleaving - interleaving style +- * +- * @SKL_INTERLEAVING_PER_CHANNEL: [s1_ch1...s1_chN,...,sM_ch1...sM_chN] +- * @SKL_INTERLEAVING_PER_SAMPLE: [s1_ch1...sM_ch1,...,s1_chN...sM_chN] +- */ +-enum skl_interleaving { +- SKL_INTERLEAVING_PER_CHANNEL = 0, +- SKL_INTERLEAVING_PER_SAMPLE = 1, +-}; +- +-enum skl_sample_type { +- SKL_SAMPLE_TYPE_INT_MSB = 0, +- SKL_SAMPLE_TYPE_INT_LSB = 1, +- SKL_SAMPLE_TYPE_INT_SIGNED = 2, +- SKL_SAMPLE_TYPE_INT_UNSIGNED = 3, +- SKL_SAMPLE_TYPE_FLOAT = 4 +-}; +- +-enum module_pin_type { +- /* All pins of the module takes same PCM inputs or outputs +- * e.g. mixout +- */ +- SKL_PIN_TYPE_HOMOGENEOUS, +- /* All pins of the module takes different PCM inputs or outputs +- * e.g mux +- */ +- SKL_PIN_TYPE_HETEROGENEOUS, +-}; +- +-enum skl_module_param_type { +- SKL_PARAM_DEFAULT = 0, +- SKL_PARAM_INIT, +- SKL_PARAM_SET, +- SKL_PARAM_BIND +-}; +- +-struct skl_dfw_algo_data { +- __u32 set_params:2; +- __u32 rsvd:30; +- __u32 param_id; +- __u32 max; +- char params[]; +-} __packed; +- +-enum skl_tkn_dir { +- SKL_DIR_IN, +- SKL_DIR_OUT +-}; +- +-enum skl_tuple_type { +- SKL_TYPE_TUPLE, +- SKL_TYPE_DATA +-}; +- +-#endif +diff --git a/include/uapi/sound/snd_ar_tokens.h b/include/uapi/sound/snd_ar_tokens.h +--- a/include/uapi/sound/snd_ar_tokens.h ++++ b/include/uapi/sound/snd_ar_tokens.h +@@ -58,7 +58,7 @@ enum ar_event_types { + #define SND_SOC_AR_TPLG_FE_BE_GRAPH_CTL_MIX 256 + #define SND_SOC_AR_TPLG_VOL_CTL 257 + +-/** ++/* + * %AR_TKN_U32_SUB_GRAPH_INSTANCE_ID: Sub Graph Instance Id + * + * %AR_TKN_U32_SUB_GRAPH_PERF_MODE: Performance mode of subgraph +@@ -168,6 +168,60 @@ enum ar_event_types { + * LOG_WAIT = 0, + * LOG_IMMEDIATELY = 1 + * ++ * %AR_TKN_U16_MODULE_SYNC_SRC: Frame sync source ++ * AR_AUDIO_IF_SYNC_SRC_EXTERNAL = 0, ++ * AR_AUDIO_IF_SYNC_SRC_INTERNAL = 1 ++ * ++ * %AR_TKN_U16_MODULE_CTRL_DATA_OUT_ENABLE: Enable data-out tri-state control ++ * AR_AUDIO_IF_CTRL_DATA_OE_DISABLE = 0, ++ * AR_AUDIO_IF_CTRL_DATA_OE_ENABLE = 1 ++ * ++ * %AR_TKN_U32_MODULE_SLOT_MASK: Active TDM slot bitmask ++ * ++ * %AR_TKN_U16_MODULE_NSLOTS_PER_FRAME: Number of slots per TDM frame ++ * ++ * %AR_TKN_U16_MODULE_SLOT_WIDTH: Slot width in bits (16 or 32) ++ * ++ * %AR_TKN_U16_MODULE_SYNC_MODE: Frame sync mode ++ * AR_AUDIO_IF_FRAME_SYNC_MODE_SHORT = 0, ++ * AR_AUDIO_IF_FRAME_SYNC_MODE_ONE_SLOT = 1, ++ * AR_AUDIO_IF_FRAME_SYNC_MODE_LONG = 2 ++ * ++ * %AR_TKN_U16_MODULE_CTRL_INVERT_SYNC_PULSE: Invert frame sync pulse polarity ++ * AR_AUDIO_IF_SYNC_NORMAL = 0, ++ * AR_AUDIO_IF_SYNC_INVERTED = 1 ++ * ++ * %AR_TKN_U16_MODULE_CTRL_SYNC_DATA_DELAY: Data delay relative to frame sync ++ * AR_AUDIO_IF_DATA_DELAY_NONE = 0, ++ * AR_AUDIO_IF_DATA_DELAY_1_CYCLE = 1, ++ * AR_AUDIO_IF_DATA_DELAY_2_CYCLE = 2 ++ * ++ * %AR_TKN_U16_MODULE_INTF_MODE: Audio IF interface mode ++ * AR_AUDIO_IF_INTF_MODE_TDM = 0, ++ * AR_AUDIO_IF_INTF_MODE_PCM = 1, ++ * AR_AUDIO_IF_INTF_MODE_I2S = 2 ++ * ++ * %AR_TKN_U16_MODULE_QAIF_TYPE: QAIF hardware port type index ++ * AR_AUDIO_IF_QAIF = 0, ++ * AR_AUDIO_IF_QAIF_VA = 1 ++ * ++ * %AR_TKN_U32_MODULE_ACTIVE_LANE_MASK: Active lane bitmask for multi-lane ++ * ++ * %AR_TKN_U32_MODULE_FRAME_SYNC_RATE: Frame sync rate in Hz ++ * ++ * %AR_TKN_U16_MODULE_BIT_CLK_TYPE: Bit clock type ++ * AR_AUDIO_IF_BIT_CLK_INTERNAL = 0, ++ * AR_AUDIO_IF_BIT_CLK_EXTERNAL = 1, ++ * AR_AUDIO_IF_BIT_CLK_SKIP = 2 ++ * ++ * %AR_TKN_U8_MODULE_INV_INT_BIT_CLK: Invert internal bit clock ++ * AR_AUDIO_IF_CLK_NORMAL = 0, ++ * AR_AUDIO_IF_CLK_INVERTED = 1 ++ * ++ * %AR_TKN_U8_MODULE_INV_EXT_BIT_CLK: Invert external bit clock ++ * AR_AUDIO_IF_CLK_NORMAL = 0, ++ * AR_AUDIO_IF_CLK_INVERTED = 1 ++ * + * %AR_TKN_DAI_INDEX: dai index + * + */ +@@ -240,6 +294,45 @@ enum ar_event_types { + #define AR_TKN_U32_MODULE_LOG_TAP_POINT_ID 260 + #define AR_TKN_U32_MODULE_LOG_MODE 261 + ++#define AR_TKN_U16_MODULE_SYNC_SRC 262 ++#define AR_TKN_U16_MODULE_CTRL_DATA_OUT_ENABLE 263 ++#define AR_TKN_U32_MODULE_SLOT_MASK 264 ++#define AR_TKN_U16_MODULE_NSLOTS_PER_FRAME 265 ++#define AR_TKN_U16_MODULE_SLOT_WIDTH 266 ++#define AR_TKN_U16_MODULE_SYNC_MODE 267 ++#define AR_TKN_U16_MODULE_CTRL_INVERT_SYNC_PULSE 268 ++#define AR_TKN_U16_MODULE_CTRL_SYNC_DATA_DELAY 269 ++#define AR_TKN_U16_MODULE_INTF_MODE 270 ++#define AR_TKN_U16_MODULE_QAIF_TYPE 271 ++#define AR_TKN_U32_MODULE_ACTIVE_LANE_MASK 272 ++#define AR_TKN_U32_MODULE_FRAME_SYNC_RATE 273 ++#define AR_TKN_U16_MODULE_BIT_CLK_TYPE 274 ++#define AR_TKN_U8_MODULE_INV_INT_BIT_CLK 275 ++#define AR_TKN_U8_MODULE_INV_EXT_BIT_CLK 276 ++ ++#define AR_AUDIO_IF_SYNC_SRC_EXTERNAL 0 ++#define AR_AUDIO_IF_SYNC_SRC_INTERNAL 1 ++#define AR_AUDIO_IF_CTRL_DATA_OE_DISABLE 0 ++#define AR_AUDIO_IF_CTRL_DATA_OE_ENABLE 1 ++#define AR_AUDIO_IF_INTF_MODE_TDM 0 ++#define AR_AUDIO_IF_INTF_MODE_PCM 1 ++#define AR_AUDIO_IF_INTF_MODE_I2S 2 ++#define AR_AUDIO_IF_QAIF 0 ++#define AR_AUDIO_IF_QAIF_VA 1 ++#define AR_AUDIO_IF_FRAME_SYNC_MODE_SHORT 0 ++#define AR_AUDIO_IF_FRAME_SYNC_MODE_ONE_SLOT 1 ++#define AR_AUDIO_IF_FRAME_SYNC_MODE_LONG 2 ++#define AR_AUDIO_IF_SYNC_NORMAL 0 ++#define AR_AUDIO_IF_SYNC_INVERTED 1 ++#define AR_AUDIO_IF_DATA_DELAY_NONE 0 ++#define AR_AUDIO_IF_DATA_DELAY_1_CYCLE 1 ++#define AR_AUDIO_IF_DATA_DELAY_2_CYCLE 2 ++#define AR_AUDIO_IF_BIT_CLK_INTERNAL 0 ++#define AR_AUDIO_IF_BIT_CLK_EXTERNAL 1 ++#define AR_AUDIO_IF_BIT_CLK_SKIP 2 ++#define AR_AUDIO_IF_CLK_NORMAL 0 ++#define AR_AUDIO_IF_CLK_INVERTED 1 ++ + #define SND_SOC_AR_TPLG_MODULE_CFG_TYPE 0x01001006 + struct audioreach_module_priv_data { + __le32 size; /* size in bytes of the array, including all elements */ +diff --git a/include/uapi/sound/snd_sst_tokens.h b/include/uapi/sound/snd_sst_tokens.h +deleted file mode 100644 +--- a/include/uapi/sound/snd_sst_tokens.h ++++ /dev/null +@@ -1,324 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +-/* +- * snd_sst_tokens.h - Intel SST tokens definition +- * +- * Copyright (C) 2016 Intel Corp +- * Author: Shreyas NC +- */ +-#ifndef __SND_SST_TOKENS_H__ +-#define __SND_SST_TOKENS_H__ +- +-/** +- * %SKL_TKN_UUID: Module UUID +- * +- * %SKL_TKN_U8_BLOCK_TYPE: Type of the private data block.Can be: +- * tuples, bytes, short and words +- * +- * %SKL_TKN_U8_IN_PIN_TYPE: Input pin type, +- * homogenous=0, heterogenous=1 +- * +- * %SKL_TKN_U8_OUT_PIN_TYPE: Output pin type, +- * homogenous=0, heterogenous=1 +- * %SKL_TKN_U8_DYN_IN_PIN: Configure Input pin dynamically +- * if true +- * +- * %SKL_TKN_U8_DYN_OUT_PIN: Configure Output pin dynamically +- * if true +- * +- * %SKL_TKN_U8_IN_QUEUE_COUNT: Store the number of Input pins +- * +- * %SKL_TKN_U8_OUT_QUEUE_COUNT: Store the number of Output pins +- * +- * %SKL_TKN_U8_TIME_SLOT: TDM slot number +- * +- * %SKL_TKN_U8_CORE_ID: Stores module affinity value.Can take +- * the values: +- * SKL_AFFINITY_CORE_0 = 0, +- * SKL_AFFINITY_CORE_1, +- * SKL_AFFINITY_CORE_MAX +- * +- * %SKL_TKN_U8_MOD_TYPE: Module type value. +- * +- * %SKL_TKN_U8_CONN_TYPE: Module connection type can be a FE, +- * BE or NONE as defined : +- * SKL_PIPE_CONN_TYPE_NONE = 0, +- * SKL_PIPE_CONN_TYPE_FE = 1 (HOST_DMA) +- * SKL_PIPE_CONN_TYPE_BE = 2 (LINK_DMA) +- * +- * %SKL_TKN_U8_DEV_TYPE: Type of device to which the module is +- * connected +- * Can take the values: +- * SKL_DEVICE_BT = 0x0, +- * SKL_DEVICE_DMIC = 0x1, +- * SKL_DEVICE_I2S = 0x2, +- * SKL_DEVICE_SLIMBUS = 0x3, +- * SKL_DEVICE_HDALINK = 0x4, +- * SKL_DEVICE_HDAHOST = 0x5, +- * SKL_DEVICE_NONE +- * +- * %SKL_TKN_U8_HW_CONN_TYPE: Connection type of the HW to which the +- * module is connected +- * SKL_CONN_NONE = 0, +- * SKL_CONN_SOURCE = 1, +- * SKL_CONN_SINK = 2 +- * +- * %SKL_TKN_U16_PIN_INST_ID: Stores the pin instance id +- * +- * %SKL_TKN_U16_MOD_INST_ID: Stores the mdule instance id +- * +- * %SKL_TKN_U32_MAX_MCPS: Module max mcps value +- * +- * %SKL_TKN_U32_MEM_PAGES: Module resource pages +- * +- * %SKL_TKN_U32_OBS: Stores Output Buffer size +- * +- * %SKL_TKN_U32_IBS: Stores input buffer size +- * +- * %SKL_TKN_U32_VBUS_ID: Module VBUS_ID. PDM=0, SSP0=0, +- * SSP1=1,SSP2=2, +- * SSP3=3, SSP4=4, +- * SSP5=5, SSP6=6,INVALID +- * +- * %SKL_TKN_U32_PARAMS_FIXUP: Module Params fixup mask +- * %SKL_TKN_U32_CONVERTER: Module params converter mask +- * %SKL_TKN_U32_PIPE_ID: Stores the pipe id +- * +- * %SKL_TKN_U32_PIPE_CONN_TYPE: Type of the token to which the pipe is +- * connected to. It can be +- * SKL_PIPE_CONN_TYPE_NONE = 0, +- * SKL_PIPE_CONN_TYPE_FE = 1 (HOST_DMA), +- * SKL_PIPE_CONN_TYPE_BE = 2 (LINK_DMA), +- * +- * %SKL_TKN_U32_PIPE_PRIORITY: Pipe priority value +- * %SKL_TKN_U32_PIPE_MEM_PGS: Pipe resource pages +- * +- * %SKL_TKN_U32_DIR_PIN_COUNT: Value for the direction to set input/output +- * formats and the pin count. +- * The first 4 bits have the direction +- * value and the next 4 have +- * the pin count value. +- * SKL_DIR_IN = 0, SKL_DIR_OUT = 1. +- * The input and output formats +- * share the same set of tokens +- * with the distinction between input +- * and output made by reading direction +- * token. +- * +- * %SKL_TKN_U32_FMT_CH: Supported channel count +- * +- * %SKL_TKN_U32_FMT_FREQ: Supported frequency/sample rate +- * +- * %SKL_TKN_U32_FMT_BIT_DEPTH: Supported container size +- * +- * %SKL_TKN_U32_FMT_SAMPLE_SIZE:Number of samples in the container +- * +- * %SKL_TKN_U32_FMT_CH_CONFIG: Supported channel configurations for the +- * input/output. +- * +- * %SKL_TKN_U32_FMT_INTERLEAVE: Interleaving style which can be per +- * channel or per sample. The values can be : +- * SKL_INTERLEAVING_PER_CHANNEL = 0, +- * SKL_INTERLEAVING_PER_SAMPLE = 1, +- * +- * %SKL_TKN_U32_FMT_SAMPLE_TYPE: +- * Specifies the sample type. Can take the +- * values: SKL_SAMPLE_TYPE_INT_MSB = 0, +- * SKL_SAMPLE_TYPE_INT_LSB = 1, +- * SKL_SAMPLE_TYPE_INT_SIGNED = 2, +- * SKL_SAMPLE_TYPE_INT_UNSIGNED = 3, +- * SKL_SAMPLE_TYPE_FLOAT = 4 +- * +- * %SKL_TKN_U32_CH_MAP: Channel map values +- * %SKL_TKN_U32_MOD_SET_PARAMS: It can take these values: +- * SKL_PARAM_DEFAULT, SKL_PARAM_INIT, +- * SKL_PARAM_SET, SKL_PARAM_BIND +- * +- * %SKL_TKN_U32_MOD_PARAM_ID: ID of the module params +- * +- * %SKL_TKN_U32_CAPS_SET_PARAMS: +- * Set params value +- * +- * %SKL_TKN_U32_CAPS_PARAMS_ID: Params ID +- * +- * %SKL_TKN_U32_CAPS_SIZE: Caps size +- * +- * %SKL_TKN_U32_PROC_DOMAIN: Specify processing domain +- * +- * %SKL_TKN_U32_LIB_COUNT: Specifies the number of libraries +- * +- * %SKL_TKN_STR_LIB_NAME: Specifies the library name +- * +- * %SKL_TKN_U32_PMODE: Specifies the power mode for pipe +- * +- * %SKL_TKL_U32_D0I3_CAPS: Specifies the D0i3 capability for module +- * +- * %SKL_TKN_U32_DMA_BUF_SIZE: DMA buffer size in millisec +- * +- * %SKL_TKN_U32_PIPE_DIR: Specifies pipe direction. Can be +- * playback/capture. +- * +- * %SKL_TKN_U32_NUM_CONFIGS: Number of pipe configs +- * +- * %SKL_TKN_U32_PATH_MEM_PGS: Size of memory (in pages) required for pipeline +- * and its data +- * +- * %SKL_TKN_U32_PIPE_CONFIG_ID: Config id for the modules in the pipe +- * and PCM params supported by that pipe +- * config. This is used as index to fill +- * up the pipe config and module config +- * structure. +- * +- * %SKL_TKN_U32_CFG_FREQ: +- * %SKL_TKN_U8_CFG_CHAN: +- * %SKL_TKN_U8_CFG_BPS: PCM params (freq, channels, bits per sample) +- * supported for each of the pipe configs. +- * +- * %SKL_TKN_CFG_MOD_RES_ID: Module's resource index for each of the +- * pipe config +- * +- * %SKL_TKN_CFG_MOD_FMT_ID: Module's interface index for each of the +- * pipe config +- * +- * %SKL_TKN_U8_NUM_MOD: Number of modules in the manifest +- * +- * %SKL_TKN_MM_U8_MOD_IDX: Current index of the module in the manifest +- * +- * %SKL_TKN_MM_U8_NUM_RES: Number of resources for the module +- * +- * %SKL_TKN_MM_U8_NUM_INTF: Number of interfaces for the module +- * +- * %SKL_TKN_MM_U32_RES_ID: Resource index for the resource info to +- * be filled into. +- * A module can support multiple resource +- * configuration and is represnted as a +- * resource table. This index is used to +- * fill information into appropriate index. +- * +- * %SKL_TKN_MM_U32_CPS: DSP cycles per second +- * +- * %SKL_TKN_MM_U32_DMA_SIZE: Allocated buffer size for gateway DMA +- * +- * %SKL_TKN_MM_U32_CPC: DSP cycles allocated per frame +- * +- * %SKL_TKN_MM_U32_RES_PIN_ID: Resource pin index in the module +- * +- * %SKL_TKN_MM_U32_INTF_PIN_ID: Interface index in the module +- * +- * %SKL_TKN_MM_U32_PIN_BUF: Buffer size of the module pin +- * +- * %SKL_TKN_MM_U32_FMT_ID: Format index for each of the interface/ +- * format information to be filled into. +- * +- * %SKL_TKN_MM_U32_NUM_IN_FMT: Number of input formats +- * %SKL_TKN_MM_U32_NUM_OUT_FMT: Number of output formats +- * +- * %SKL_TKN_U32_ASTATE_IDX: Table Index for the A-State entry to be filled +- * with kcps and clock source +- * +- * %SKL_TKN_U32_ASTATE_COUNT: Number of valid entries in A-State table +- * +- * %SKL_TKN_U32_ASTATE_KCPS: Specifies the core load threshold (in kilo +- * cycles per second) below which DSP is clocked +- * from source specified by clock source. +- * +- * %SKL_TKN_U32_ASTATE_CLK_SRC: Clock source for A-State entry +- * +- * %SKL_TKN_U32_FMT_CFG_IDX: Format config index +- * +- * module_id and loadable flags dont have tokens as these values will be +- * read from the DSP FW manifest +- * +- * Tokens defined can be used either in the manifest or widget private data. +- * +- * SKL_TKN_MM is used as a suffix for all tokens that represent +- * module data in the manifest. +- */ +-enum SKL_TKNS { +- SKL_TKN_UUID = 1, +- SKL_TKN_U8_NUM_BLOCKS, +- SKL_TKN_U8_BLOCK_TYPE, +- SKL_TKN_U8_IN_PIN_TYPE, +- SKL_TKN_U8_OUT_PIN_TYPE, +- SKL_TKN_U8_DYN_IN_PIN, +- SKL_TKN_U8_DYN_OUT_PIN, +- SKL_TKN_U8_IN_QUEUE_COUNT, +- SKL_TKN_U8_OUT_QUEUE_COUNT, +- SKL_TKN_U8_TIME_SLOT, +- SKL_TKN_U8_CORE_ID, +- SKL_TKN_U8_MOD_TYPE, +- SKL_TKN_U8_CONN_TYPE, +- SKL_TKN_U8_DEV_TYPE, +- SKL_TKN_U8_HW_CONN_TYPE, +- SKL_TKN_U16_MOD_INST_ID, +- SKL_TKN_U16_BLOCK_SIZE, +- SKL_TKN_U32_MAX_MCPS, +- SKL_TKN_U32_MEM_PAGES, +- SKL_TKN_U32_OBS, +- SKL_TKN_U32_IBS, +- SKL_TKN_U32_VBUS_ID, +- SKL_TKN_U32_PARAMS_FIXUP, +- SKL_TKN_U32_CONVERTER, +- SKL_TKN_U32_PIPE_ID, +- SKL_TKN_U32_PIPE_CONN_TYPE, +- SKL_TKN_U32_PIPE_PRIORITY, +- SKL_TKN_U32_PIPE_MEM_PGS, +- SKL_TKN_U32_DIR_PIN_COUNT, +- SKL_TKN_U32_FMT_CH, +- SKL_TKN_U32_FMT_FREQ, +- SKL_TKN_U32_FMT_BIT_DEPTH, +- SKL_TKN_U32_FMT_SAMPLE_SIZE, +- SKL_TKN_U32_FMT_CH_CONFIG, +- SKL_TKN_U32_FMT_INTERLEAVE, +- SKL_TKN_U32_FMT_SAMPLE_TYPE, +- SKL_TKN_U32_FMT_CH_MAP, +- SKL_TKN_U32_PIN_MOD_ID, +- SKL_TKN_U32_PIN_INST_ID, +- SKL_TKN_U32_MOD_SET_PARAMS, +- SKL_TKN_U32_MOD_PARAM_ID, +- SKL_TKN_U32_CAPS_SET_PARAMS, +- SKL_TKN_U32_CAPS_PARAMS_ID, +- SKL_TKN_U32_CAPS_SIZE, +- SKL_TKN_U32_PROC_DOMAIN, +- SKL_TKN_U32_LIB_COUNT, +- SKL_TKN_STR_LIB_NAME, +- SKL_TKN_U32_PMODE, +- SKL_TKL_U32_D0I3_CAPS, /* Typo added at v4.10 */ +- SKL_TKN_U32_D0I3_CAPS = SKL_TKL_U32_D0I3_CAPS, +- SKL_TKN_U32_DMA_BUF_SIZE, +- +- SKL_TKN_U32_PIPE_DIRECTION, +- SKL_TKN_U32_PIPE_CONFIG_ID, +- SKL_TKN_U32_NUM_CONFIGS, +- SKL_TKN_U32_PATH_MEM_PGS, +- +- SKL_TKN_U32_CFG_FREQ, +- SKL_TKN_U8_CFG_CHAN, +- SKL_TKN_U8_CFG_BPS, +- SKL_TKN_CFG_MOD_RES_ID, +- SKL_TKN_CFG_MOD_FMT_ID, +- SKL_TKN_U8_NUM_MOD, +- +- SKL_TKN_MM_U8_MOD_IDX, +- SKL_TKN_MM_U8_NUM_RES, +- SKL_TKN_MM_U8_NUM_INTF, +- SKL_TKN_MM_U32_RES_ID, +- SKL_TKN_MM_U32_CPS, +- SKL_TKN_MM_U32_DMA_SIZE, +- SKL_TKN_MM_U32_CPC, +- SKL_TKN_MM_U32_RES_PIN_ID, +- SKL_TKN_MM_U32_INTF_PIN_ID, +- SKL_TKN_MM_U32_PIN_BUF, +- SKL_TKN_MM_U32_FMT_ID, +- SKL_TKN_MM_U32_NUM_IN_FMT, +- SKL_TKN_MM_U32_NUM_OUT_FMT, +- +- SKL_TKN_U32_ASTATE_IDX, +- SKL_TKN_U32_ASTATE_COUNT, +- SKL_TKN_U32_ASTATE_KCPS, +- SKL_TKN_U32_ASTATE_CLK_SRC, +- +- SKL_TKN_U32_FMT_CFG_IDX = 96, +- SKL_TKN_MAX = SKL_TKN_U32_FMT_CFG_IDX, +-}; +- +-#endif +diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h +--- a/include/uapi/sound/sof/tokens.h ++++ b/include/uapi/sound/sof/tokens.h +@@ -111,8 +111,8 @@ + + #define SOF_TKN_COMP_SCHED_DOMAIN 418 + #define SOF_TKN_COMP_DOMAIN_ID 419 +-#define SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT 420 +-#define SOF_TKN_COMP_STACK_BYTES_REQUIREMENT 421 ++#define SOF_TKN_COMP_STACK_BYTES_REQUIREMENT 420 ++#define SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT 421 + + /* SSP */ + #define SOF_TKN_INTEL_SSP_CLKS_CONTROL 500 +@@ -223,6 +223,7 @@ + #define SOF_TKN_AMD_ACPI2S_RATE 1700 + #define SOF_TKN_AMD_ACPI2S_CH 1701 + #define SOF_TKN_AMD_ACPI2S_TDM_MODE 1702 ++#define SOF_TKN_AMD_ACPI2S_FORMAT 1703 + + /* MICFIL PDM */ + #define SOF_TKN_IMX_MICFIL_RATE 2000 +diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c +--- a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c ++++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c +@@ -171,13 +171,13 @@ static void tas2781_hda_playback_hook(struct device *dev, int action) + case HDA_GEN_PCM_ACT_OPEN: + pm_runtime_get_sync(dev); + scoped_guard(mutex, &tas_hda->priv->codec_lock) { +- tasdevice_tuning_switch(tas_hda->priv, 0); ++ tasdevice_tuning_switch(tas_hda->priv, 0, false); + tas_hda->priv->playback_started = true; + } + break; + case HDA_GEN_PCM_ACT_CLOSE: + scoped_guard(mutex, &tas_hda->priv->codec_lock) { +- tasdevice_tuning_switch(tas_hda->priv, 1); ++ tasdevice_tuning_switch(tas_hda->priv, 1, false); + tas_hda->priv->playback_started = false; + } + +@@ -720,7 +720,7 @@ static int tas2781_runtime_suspend(struct device *dev) + * Stop the playback if it's unused. + */ + if (tas_hda->priv->playback_started) { +- tasdevice_tuning_switch(tas_hda->priv, 1); ++ tasdevice_tuning_switch(tas_hda->priv, 1, false); + tas_hda->priv->playback_started = false; + } + +@@ -750,7 +750,7 @@ static int tas2781_system_suspend(struct device *dev) + + /* Shutdown chip before system suspend */ + if (tas_hda->priv->playback_started) +- tasdevice_tuning_switch(tas_hda->priv, 1); ++ tasdevice_tuning_switch(tas_hda->priv, 1, false); + + /* + * Reset GPIO may be shared, so cannot reset here. +@@ -783,7 +783,7 @@ static int tas2781_system_resume(struct device *dev) + TASDEVICE_BIN_BLK_PRE_POWER_UP); + + if (tas_hda->priv->playback_started) +- tasdevice_tuning_switch(tas_hda->priv, 0); ++ tasdevice_tuning_switch(tas_hda->priv, 0, false); + + return 0; + } +diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c +--- a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c ++++ b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c +@@ -399,11 +399,11 @@ static void tas2781_hda_playback_hook(struct device *dev, int action) + pm_runtime_get_sync(dev); + guard(mutex)(&tas_priv->codec_lock); + if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK) +- tasdevice_tuning_switch(tas_hda->priv, 0); ++ tasdevice_tuning_switch(tas_hda->priv, 0, false); + } else if (action == HDA_GEN_PCM_ACT_CLOSE) { + guard(mutex)(&tas_priv->codec_lock); + if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK) +- tasdevice_tuning_switch(tas_priv, 1); ++ tasdevice_tuning_switch(tas_priv, 1, false); + pm_runtime_put_autosuspend(dev); + } + } +@@ -847,7 +847,7 @@ static int tas2781_runtime_suspend(struct device *dev) + + if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK + && tas_priv->playback_started) +- tasdevice_tuning_switch(tas_priv, 1); ++ tasdevice_tuning_switch(tas_priv, 1, false); + + tas_priv->tasdevice[tas_priv->index].cur_book = -1; + tas_priv->tasdevice[tas_priv->index].cur_conf = -1; +@@ -864,7 +864,7 @@ static int tas2781_runtime_resume(struct device *dev) + + if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK + && tas_priv->playback_started) +- tasdevice_tuning_switch(tas_priv, 0); ++ tasdevice_tuning_switch(tas_priv, 0, false); + + return 0; + } +@@ -882,7 +882,7 @@ static int tas2781_system_suspend(struct device *dev) + /* Shutdown chip before system suspend */ + if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK + && tas_priv->playback_started) +- tasdevice_tuning_switch(tas_priv, 1); ++ tasdevice_tuning_switch(tas_priv, 1, false); + + return 0; + } +@@ -917,7 +917,7 @@ static int tas2781_system_resume(struct device *dev) + tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK; + + if (tas_priv->playback_started) +- tasdevice_tuning_switch(tas_priv, 0); ++ tasdevice_tuning_switch(tas_priv, 0, false); + } + + return ret; +diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c +--- a/sound/soc/amd/acp-config.c ++++ b/sound/soc/amd/acp-config.c +@@ -63,6 +63,13 @@ static const struct dmi_system_id acp70_acpi_flag_override_table[] = { + DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook 18 M1807GA"), + }, + }, ++ { ++ /* HP OmniBook X Flip 14-kc0xxx (Strix Point, ACP 7.2) */ ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "HP"), ++ DMI_MATCH(DMI_BOARD_NAME, "8EA1"), ++ }, ++ }, + {} + }; + +@@ -379,5 +386,17 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[] = { + }; + EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sof_machines); + ++struct snd_soc_acpi_mach snd_soc_acpi_amd_acp7x_sof_machines[] = { ++ { ++ .id = "AMDI1010", ++ .drv_name = "acp7x-dsp", ++ .pdata = &acp_quirk_data, ++ .fw_filename = "sof-acp7x.ri", ++ .sof_tplg_filename = "sof-acp7x.tplg", ++ }, ++ {}, ++}; ++EXPORT_SYMBOL(snd_soc_acpi_amd_acp7x_sof_machines); ++ + MODULE_DESCRIPTION("AMD ACP Machine Configuration Module"); + MODULE_LICENSE("Dual BSD/GPL"); +diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c +--- a/sound/soc/amd/acp-pcm-dma.c ++++ b/sound/soc/amd/acp-pcm-dma.c +@@ -691,12 +691,10 @@ static irqreturn_t dma_irq_handler(int irq, void *arg) + { + u16 dscr_idx; + u32 intr_flag, ext_intr_status; +- struct audio_drv_data *irq_data; ++ struct audio_drv_data *irq_data = arg; + void __iomem *acp_mmio; +- struct device *dev = arg; + bool valid_irq = false; + +- irq_data = dev_get_drvdata(dev); + acp_mmio = irq_data->acp_mmio; + + ext_intr_status = acp_reg_read(acp_mmio, mmACP_EXTERNAL_INTR_STAT); +@@ -1291,10 +1289,10 @@ static int acp_audio_probe(struct platform_device *pdev) + + irq = platform_get_irq(pdev, 0); + if (irq < 0) +- return -ENODEV; ++ return irq; + + status = devm_request_irq(&pdev->dev, irq, dma_irq_handler, +- 0, "ACP_IRQ", &pdev->dev); ++ 0, "ACP_IRQ", audio_drv_data); + if (status) { + dev_err(&pdev->dev, "ACP IRQ request failed\n"); + return status; +diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c +--- a/sound/soc/amd/acp/acp-i2s.c ++++ b/sound/soc/amd/acp/acp-i2s.c +@@ -686,6 +686,10 @@ static int acp_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_d + return 0; + } + ++static const u64 acp_i2s_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A; ++ + const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = { + .startup = acp_i2s_startup, + .hw_params = acp_i2s_hwparams, +@@ -693,6 +697,8 @@ const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = { + .trigger = acp_i2s_trigger, + .set_fmt = acp_i2s_set_fmt, + .set_tdm_slot = acp_i2s_set_tdm_slot, ++ .auto_selectable_formats = &acp_i2s_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + EXPORT_SYMBOL_NS_GPL(asoc_acp_cpu_dai_ops, "SND_SOC_ACP_COMMON"); + +diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c +--- a/sound/soc/amd/acp/acp-mach-common.c ++++ b/sound/soc/amd/acp/acp-mach-common.c +@@ -938,15 +938,10 @@ static int acp_max98388_hw_params(struct snd_pcm_substream *substream, + struct snd_soc_dai *codec_dai = + snd_soc_card_get_codec_dai(card, + MAX98388_CODEC_DAI); +- int ret; + +- ret = snd_soc_dai_set_fmt(codec_dai, +- SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_I2S | +- SND_SOC_DAIFMT_NB_NF); +- if (ret < 0) +- return ret; +- +- return ret; ++ return snd_soc_dai_set_fmt(codec_dai, ++ SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_I2S | ++ SND_SOC_DAIFMT_NB_NF); + } + + static const struct snd_soc_ops acp_max98388_ops = { +diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c +--- a/sound/soc/amd/acp/acp-pci.c ++++ b/sound/soc/amd/acp/acp-pci.c +@@ -118,17 +118,10 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id + if (!chip) + return -ENOMEM; + +- if (pci_enable_device(pci)) ++ if (pcim_enable_device(pci)) + return dev_err_probe(&pci->dev, -ENODEV, + "pci_enable_device failed\n"); + +- ret = pci_request_regions(pci, "AMD ACP3x audio"); +- if (ret < 0) { +- dev_err(&pci->dev, "pci_request_regions failed\n"); +- ret = -ENOMEM; +- goto disable_pci; +- } +- + pci_set_master(pci); + + chip->acp_rev = pci->revision; +@@ -161,24 +154,21 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id + break; + default: + dev_err(dev, "Unsupported device revision:0x%x\n", pci->revision); +- ret = -EINVAL; +- goto release_regions; ++ return -EINVAL; + } + chip->flag = flag; + + addr = pci_resource_start(pci, 0); +- chip->base = devm_ioremap(&pci->dev, addr, pci_resource_len(pci, 0)); +- if (!chip->base) { +- ret = -ENOMEM; +- goto release_regions; +- } ++ chip->base = pcim_iomap_region(pci, 0, "AMD ACP3x audio"); ++ if (IS_ERR(chip->base)) ++ return PTR_ERR(chip->base); + + chip->addr = addr; + + chip->acp_hw_ops_init(chip); + ret = acp_hw_init(chip); + if (ret) +- goto release_regions; ++ goto de_init; + + ret = devm_request_irq(dev, pci->irq, irq_handler, + IRQF_SHARED, "ACP_I2S_IRQ", chip); +@@ -214,10 +204,6 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id + + de_init: + acp_hw_deinit(chip); +-release_regions: +- pci_release_regions(pci); +-disable_pci: +- pci_disable_device(pci); + + return ret; + }; +diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c +--- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c ++++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c +@@ -531,15 +531,15 @@ static int mc_probe(struct platform_device *pdev) + dmi_check_system(soc_sdw_quirk_table); + + if (quirk_override != -1) { +- dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n", ++ dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n", + soc_sdw_quirk, quirk_override); + soc_sdw_quirk = quirk_override; + } + +- log_quirks(card->dev); ++ log_quirks(&pdev->dev); + + ctx->mc_quirk = soc_sdw_quirk; +- dev_dbg(card->dev, "legacy quirk 0x%lx\n", ctx->mc_quirk); ++ dev_dbg(&pdev->dev, "legacy quirk 0x%lx\n", ctx->mc_quirk); + /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ + for (i = 0; i < ctx->codec_info_list_count; i++) + codec_info_list[i].amp_num = 0; +@@ -556,12 +556,12 @@ static int mc_probe(struct platform_device *pdev) + for (i = 0; i < ctx->codec_info_list_count; i++) + amp_num += codec_info_list[i].amp_num; + +- card->components = devm_kasprintf(card->dev, GFP_KERNEL, ++ card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, + " cfg-amp:%d", amp_num); + if (!card->components) + return -ENOMEM; + if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC) { +- card->components = devm_kasprintf(card->dev, GFP_KERNEL, ++ card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, + "%s mic:acp-dmic cfg-mics:%d", + card->components, + 1); +@@ -570,9 +570,9 @@ static int mc_probe(struct platform_device *pdev) + } + + /* Register the card */ +- ret = devm_snd_soc_register_card(card->dev, card); ++ ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) { +- dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret); ++ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed %d\n", ret); + asoc_sdw_mc_dailink_exit_loop(card); + return ret; + } +@@ -590,8 +590,8 @@ static void mc_remove(struct platform_device *pdev) + } + + static const struct platform_device_id mc_id_table[] = { +- { "amd_sdw", }, +- {} ++ { .name = "amd_sdw" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, mc_id_table); + +diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c +--- a/sound/soc/amd/acp/acp-sdw-sof-mach.c ++++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c +@@ -314,7 +314,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) + if (!sof_aux) + return -ENOMEM; + +- ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_confs); ++ ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_devs); + if (ret < 0) + return ret; + +@@ -399,12 +399,12 @@ static int mc_probe(struct platform_device *pdev) + dmi_check_system(sof_sdw_quirk_table); + + if (quirk_override != -1) { +- dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n", ++ dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n", + sof_sdw_quirk, quirk_override); + sof_sdw_quirk = quirk_override; + } + +- log_quirks(card->dev); ++ log_quirks(&pdev->dev); + + ctx->mc_quirk = sof_sdw_quirk; + /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ +@@ -423,15 +423,15 @@ static int mc_probe(struct platform_device *pdev) + for (i = 0; i < ctx->codec_info_list_count; i++) + amp_num += codec_info_list[i].amp_num; + +- card->components = devm_kasprintf(card->dev, GFP_KERNEL, ++ card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, + " cfg-amp:%d", amp_num); + if (!card->components) + return -ENOMEM; + + /* Register the card */ +- ret = devm_snd_soc_register_card(card->dev, card); ++ ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) { +- dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret); ++ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed %d\n", ret); + asoc_sdw_mc_dailink_exit_loop(card); + return ret; + } +@@ -449,8 +449,8 @@ static void mc_remove(struct platform_device *pdev) + } + + static const struct platform_device_id mc_id_table[] = { +- { "amd_sof_sdw", }, +- {} ++ { .name = "amd_sof_sdw" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, mc_id_table); + +diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c +--- a/sound/soc/amd/acp/acp-sof-mach.c ++++ b/sound/soc/amd/acp/acp-sof-mach.c +@@ -128,31 +128,31 @@ static int acp_sof_probe(struct platform_device *pdev) + static const struct platform_device_id board_ids[] = { + { + .name = "rt5682-rt1019", +- .driver_data = (kernel_ulong_t)&sof_rt5682_rt1019_data ++ .driver_data = (kernel_ulong_t)&sof_rt5682_rt1019_data, + }, + { + .name = "rt5682-max", +- .driver_data = (kernel_ulong_t)&sof_rt5682_max_data ++ .driver_data = (kernel_ulong_t)&sof_rt5682_max_data, + }, + { + .name = "rt5682s-max", +- .driver_data = (kernel_ulong_t)&sof_rt5682s_max_data ++ .driver_data = (kernel_ulong_t)&sof_rt5682s_max_data, + }, + { + .name = "rt5682s-rt1019", +- .driver_data = (kernel_ulong_t)&sof_rt5682s_rt1019_data ++ .driver_data = (kernel_ulong_t)&sof_rt5682s_rt1019_data, + }, + { + .name = "nau8825-max", +- .driver_data = (kernel_ulong_t)&sof_nau8825_data ++ .driver_data = (kernel_ulong_t)&sof_nau8825_data, + }, + { + .name = "rt5682s-hs-rt1019", +- .driver_data = (kernel_ulong_t)&sof_rt5682s_hs_rt1019_data ++ .driver_data = (kernel_ulong_t)&sof_rt5682s_hs_rt1019_data, + }, + { + .name = "nau8821-max", +- .driver_data = (kernel_ulong_t)&sof_nau8821_max98388_data ++ .driver_data = (kernel_ulong_t)&sof_nau8821_max98388_data, + }, + { } + }; +diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c +--- a/sound/soc/amd/acp/amd-acp70-acpi-match.c ++++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c +@@ -659,6 +659,56 @@ static const struct snd_soc_acpi_link_adr acp70_rt721_l1u0_tas2783x2_l1u8b[] = { + {} + }; + ++static const struct snd_soc_acpi_adr_device rt712_vb_l1u0_adr[] = { ++ { ++ .adr = 0x000130025D071201ull, ++ /* ++ * On this platform speakers are provided by two TAS2783 amps, ++ * so the AIF2 amp path is left unused: jack + DMIC only. ++ */ ++ .num_endpoints = ARRAY_SIZE(jack_dmic_endpoints), ++ .endpoints = jack_dmic_endpoints, ++ .name_prefix = "rt712" ++ } ++}; ++ ++/* ++ * Unique ID 0xC drives the left speaker and 0x9 the right one. The order of the ++ * entries matters as much as the endpoints: asoc_sdw_parse_sdw_endpoints() ++ * appends them to the dailink in array order and sdw_compute_slave_ports() ++ * hands out payload block offsets in that same order, so the first entry is the ++ * one that receives channel 0. ++ */ ++static const struct snd_soc_acpi_adr_device tas2783x2_l0u9c_adr[] = { ++ { ++ .adr = 0x00003C0102000001ull, ++ .num_endpoints = 1, ++ .endpoints = &spk_l_endpoint, ++ .name_prefix = "tas2783-1", ++ }, ++ { ++ .adr = 0x0000390102000001ull, ++ .num_endpoints = 1, ++ .endpoints = &spk_r_endpoint, ++ .name_prefix = "tas2783-2", ++ }, ++}; ++ ++/* HP OmniBook X Flip 14-kc0xxx (board 8EA1) */ ++static const struct snd_soc_acpi_link_adr acp70_tas2783x2_l0u9c_rt712_vb_l1u0[] = { ++ { ++ .mask = BIT(0), ++ .num_adr = ARRAY_SIZE(tas2783x2_l0u9c_adr), ++ .adr_d = tas2783x2_l0u9c_adr, ++ }, ++ { ++ .mask = BIT(1), ++ .num_adr = ARRAY_SIZE(rt712_vb_l1u0_adr), ++ .adr_d = rt712_vb_l1u0_adr, ++ }, ++ {} ++}; ++ + static const struct snd_soc_acpi_endpoint rt721_endpoints[] = { + { /* Jack Playback/Capture Endpoint (AIF1) */ + .num = 0, +@@ -704,6 +754,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = { + .links = acp70_tas2783_2, + .drv_name = "amd_sdw", + }, ++ { ++ .link_mask = BIT(0) | BIT(1), ++ .links = acp70_tas2783x2_l0u9c_rt712_vb_l1u0, ++ .machine_check = snd_soc_acpi_amd_sdca_is_device_rt712_vb, ++ .drv_name = "amd_sdw", ++ }, + { + .link_mask = BIT(0) | BIT(1), + .links = acp70_rt1320_l0_rt722_l1, +diff --git a/sound/soc/amd/mach-config.h b/sound/soc/amd/mach-config.h +--- a/sound/soc/amd/mach-config.h ++++ b/sound/soc/amd/mach-config.h +@@ -28,6 +28,7 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_sdw_machines[]; + extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[]; + extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[]; + extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_sdw_machines[]; ++extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp7x_sof_machines[]; + + struct config_entry { + u32 flags; +diff --git a/sound/soc/amd/raven/acp3x-i2s.c b/sound/soc/amd/raven/acp3x-i2s.c +--- a/sound/soc/amd/raven/acp3x-i2s.c ++++ b/sound/soc/amd/raven/acp3x-i2s.c +@@ -250,11 +250,17 @@ static int acp3x_i2s_trigger(struct snd_pcm_substream *substream, + return ret; + } + ++static const u64 acp3x_i2s_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A; ++ + static const struct snd_soc_dai_ops acp3x_i2s_dai_ops = { + .hw_params = acp3x_i2s_hwparams, + .trigger = acp3x_i2s_trigger, + .set_fmt = acp3x_i2s_set_fmt, + .set_tdm_slot = acp3x_i2s_set_tdm_slot, ++ .auto_selectable_formats = &acp3x_i2s_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static const struct snd_soc_component_driver acp3x_dai_component = { +diff --git a/sound/soc/amd/vangogh/acp5x-i2s.c b/sound/soc/amd/vangogh/acp5x-i2s.c +--- a/sound/soc/amd/vangogh/acp5x-i2s.c ++++ b/sound/soc/amd/vangogh/acp5x-i2s.c +@@ -337,11 +337,17 @@ static int acp5x_i2s_trigger(struct snd_pcm_substream *substream, + return ret; + } + ++static const u64 acp5x_i2s_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A; ++ + static const struct snd_soc_dai_ops acp5x_i2s_dai_ops = { + .hw_params = acp5x_i2s_hwparams, + .trigger = acp5x_i2s_trigger, + .set_fmt = acp5x_i2s_set_fmt, + .set_tdm_slot = acp5x_i2s_set_tdm_slot, ++ .auto_selectable_formats = &acp5x_i2s_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static const struct snd_soc_component_driver acp5x_dai_component = { +diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c +--- a/sound/soc/amd/yc/acp6x-mach.c ++++ b/sound/soc/amd/yc/acp6x-mach.c +@@ -795,12 +795,12 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { + } + }, + { +- .driver_data = &acp6x_card, +- .matches = { +- DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), +- DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook_ASUSLaptop M6501RR_M6501RR"), +- } +- }, ++ .driver_data = &acp6x_card, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook_ASUSLaptop M6501RR_M6501RR"), ++ } ++ }, + { + .driver_data = &acp6x_card, + .matches = { +diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c +--- a/sound/soc/apple/mca.c ++++ b/sound/soc/apple/mca.c +@@ -133,12 +133,17 @@ struct mca_cluster { + struct clk *clk_parent; + struct dma_chan *dma_chans[SNDRV_PCM_STREAM_LAST + 1]; + +- bool port_started[SNDRV_PCM_STREAM_LAST + 1]; +- int port_driver; /* The cluster driving this cluster's port */ ++ bool clk_provider; ++ ++ bool port_clk_started[SNDRV_PCM_STREAM_LAST + 1]; ++ int port_clk_driver; /* The cluster driving this cluster's port */ + + bool clocks_in_use[SNDRV_PCM_STREAM_LAST + 1]; + struct device_link *pd_link; + ++ /* In case of clock consumer FE */ ++ int syncgen_in_use; ++ + unsigned int bclk_ratio; + + /* Masks etc. picked up via the set_tdm_slot method */ +@@ -157,7 +162,7 @@ struct mca_data { + struct reset_control *rstc; + struct device_link *pd_link; + +- /* Mutex for accessing port_driver of foreign clusters */ ++ /* Mutex for accessing port_clk_driver of foreign clusters */ + struct mutex port_mutex; + + int nclusters; +@@ -220,6 +225,12 @@ static void mca_fe_early_trigger(struct snd_pcm_substream *substream, int cmd, + FIELD_PREP(SERDES_CONF_SYNC_SEL, 0)); + mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL, + FIELD_PREP(SERDES_CONF_SYNC_SEL, cl->no + 1)); ++ /* ++ * ADMAC gets started right after this. This delay seems ++ * to be needed for that to be reliable, e.g. ensure the ++ * clock is stable? ++ */ ++ udelay(100); + break; + default: + break; +@@ -256,11 +267,29 @@ static int mca_fe_trigger(struct snd_pcm_substream *substream, int cmd, + return 0; + } + ++static int mca_fe_get_portmask(struct snd_pcm_substream *substream) ++{ ++ struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); ++ struct snd_soc_dpcm *dpcm; ++ int mask = 0; ++ ++ for_each_dpcm_be(fe, substream->stream, dpcm) { ++ int no = mca_dai_to_cluster(snd_soc_rtd_to_cpu(dpcm->be, 0))->no; ++ ++ mask |= 1 << no; ++ } ++ ++ return mask; ++} ++ + static int mca_fe_enable_clocks(struct mca_cluster *cl) + { + struct mca_data *mca = cl->host; + int ret; + ++ if (!cl->clk_provider) ++ return -EINVAL; ++ + ret = clk_prepare_enable(cl->clk_parent); + if (ret) { + dev_err(mca->dev, +@@ -311,7 +340,7 @@ static bool mca_fe_clocks_in_use(struct mca_cluster *cl) + for (i = 0; i < mca->nclusters; i++) { + be_cl = &mca->clusters[i]; + +- if (be_cl->port_driver != cl->no) ++ if (be_cl->port_clk_driver != cl->no) + continue; + + for_each_pcm_streams(stream) { +@@ -323,36 +352,6 @@ static bool mca_fe_clocks_in_use(struct mca_cluster *cl) + return false; + } + +-static int mca_be_prepare(struct snd_pcm_substream *substream, +- struct snd_soc_dai *dai) +-{ +- struct mca_cluster *cl = mca_dai_to_cluster(dai); +- struct mca_data *mca = cl->host; +- struct mca_cluster *fe_cl; +- int ret; +- +- if (cl->port_driver < 0) +- return -EINVAL; +- +- fe_cl = &mca->clusters[cl->port_driver]; +- +- /* +- * Typically the CODECs we are paired with will require clocks +- * to be present at time of unmute with the 'mute_stream' op +- * or at time of DAPM widget power-up. We need to enable clocks +- * here at the latest (frontend prepare would be too late). +- */ +- if (!mca_fe_clocks_in_use(fe_cl)) { +- ret = mca_fe_enable_clocks(fe_cl); +- if (ret < 0) +- return ret; +- } +- +- cl->clocks_in_use[substream->stream] = true; +- +- return 0; +-} +- + static int mca_be_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + { +@@ -360,7 +359,7 @@ static int mca_be_hw_free(struct snd_pcm_substream *substream, + struct mca_data *mca = cl->host; + struct mca_cluster *fe_cl; + +- if (cl->port_driver < 0) ++ if (cl->port_clk_driver < 0) + return -EINVAL; + + /* +@@ -368,7 +367,7 @@ static int mca_be_hw_free(struct snd_pcm_substream *substream, + * belong to the same PCM, accesses should have been + * synchronized at ASoC level. + */ +- fe_cl = &mca->clusters[cl->port_driver]; ++ fe_cl = &mca->clusters[cl->port_clk_driver]; + if (!mca_fe_clocks_in_use(fe_cl)) + return 0; /* Nothing to do */ + +@@ -380,6 +379,56 @@ static int mca_be_hw_free(struct snd_pcm_substream *substream, + return 0; + } + ++static int mca_fe_prepare(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct mca_cluster *cl = mca_dai_to_cluster(dai); ++ struct mca_data *mca = cl->host; ++ ++ if (cl->clk_provider) ++ return 0; ++ ++ /* Turn on the cluster power domain if not already in use */ ++ if (!cl->syncgen_in_use) { ++ int port = ffs(mca_fe_get_portmask(substream)); ++ ++ cl->pd_link = device_link_add(mca->dev, cl->pd_dev, ++ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | ++ DL_FLAG_RPM_ACTIVE); ++ if (!cl->pd_link) { ++ dev_err(mca->dev, ++ "cluster %d: unable to prop-up power domain\n", cl->no); ++ return -EINVAL; ++ } ++ ++ mca_modify(cl, REG_SYNCGEN_MCLK_SEL, SYNCGEN_MCLK_SEL, BIT(port)); ++ mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN, ++ SYNCGEN_STATUS_EN); ++ } ++ cl->syncgen_in_use |= 1 << substream->stream; ++ ++ return 0; ++} ++ ++static int mca_fe_hw_free(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct mca_cluster *cl = mca_dai_to_cluster(dai); ++ ++ if (cl->clk_provider) ++ return 0; ++ ++ cl->syncgen_in_use &= ~(1 << substream->stream); ++ if (cl->syncgen_in_use) ++ return 0; ++ ++ mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN, 0); ++ if (cl->pd_link) ++ device_link_del(cl->pd_link); ++ ++ return 0; ++} ++ + static unsigned int mca_crop_mask(unsigned int mask, int nchans) + { + while (hweight32(mask) > nchans) +@@ -390,7 +439,7 @@ static unsigned int mca_crop_mask(unsigned int mask, int nchans) + + static int mca_configure_serdes(struct mca_cluster *cl, int serdes_unit, + unsigned int mask, int slots, int nchans, +- int slot_width, bool is_tx, int port) ++ int slot_width, bool is_tx, int portmask) + { + __iomem void *serdes_base = cl->base + serdes_unit; + u32 serdes_conf, serdes_conf_mask; +@@ -449,7 +498,7 @@ static int mca_configure_serdes(struct mca_cluster *cl, int serdes_unit, + serdes_base + REG_RX_SERDES_SLOTMASK); + writel_relaxed(~((u32)mca_crop_mask(mask, nchans)), + serdes_base + REG_RX_SERDES_SLOTMASK + 0x4); +- writel_relaxed(1 << port, ++ writel_relaxed(portmask, + serdes_base + REG_RX_SERDES_PORT); + } + +@@ -505,9 +554,18 @@ static int mca_fe_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) + u32 serdes_conf = 0; + u32 bitstart; + +- if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != +- SND_SOC_DAIFMT_BP_FP) ++ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { ++ case SND_SOC_DAIFMT_BP_FP: ++ cl->clk_provider = true; ++ break; ++ ++ case SND_SOC_DAIFMT_BC_FC: ++ cl->clk_provider = false; ++ break; ++ ++ default: + goto err; ++ } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: +@@ -564,24 +622,6 @@ static int mca_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) + return 0; + } + +-static int mca_fe_get_port(struct snd_pcm_substream *substream) +-{ +- struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); +- struct snd_soc_pcm_runtime *be; +- struct snd_soc_dpcm *dpcm; +- +- be = NULL; +- for_each_dpcm_be(fe, substream->stream, dpcm) { +- be = dpcm->be; +- break; +- } +- +- if (!be) +- return -EINVAL; +- +- return mca_dai_to_cluster(snd_soc_rtd_to_cpu(be, 0))->no; +-} +- + static int mca_fe_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +@@ -595,7 +635,7 @@ static int mca_fe_hw_params(struct snd_pcm_substream *substream, + unsigned long bclk_ratio; + unsigned int tdm_slots, tdm_slot_width, tdm_mask; + u32 regval, pad; +- int ret, port, nchans_ceiled; ++ int ret, portmask, nchans_ceiled; + + if (!cl->tdm_slot_width) { + /* +@@ -644,13 +684,13 @@ static int mca_fe_hw_params(struct snd_pcm_substream *substream, + tdm_mask = (1 << tdm_slots) - 1; + } + +- port = mca_fe_get_port(substream); +- if (port < 0) +- return port; ++ portmask = mca_fe_get_portmask(substream); ++ if (!portmask) ++ return -EINVAL; + + ret = mca_configure_serdes(cl, is_tx ? CLUSTER_TX_OFF : CLUSTER_RX_OFF, + tdm_mask, tdm_slots, params_channels(params), +- tdm_slot_width, is_tx, port); ++ tdm_slot_width, is_tx, portmask); + if (ret) + return ret; + +@@ -699,6 +739,19 @@ static int mca_fe_hw_params(struct snd_pcm_substream *substream, + return 0; + } + ++static const u64 mca_fe_selectable_formats[] = ++{ ++ /* pattern 1 */ ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_NB_IF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_IF, ++ ++ /* pattern 2 */ ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_NF ++}; ++ + static const struct snd_soc_dai_ops mca_fe_ops = { + .startup = mca_fe_startup, + .set_fmt = mca_fe_set_fmt, +@@ -706,66 +759,128 @@ static const struct snd_soc_dai_ops mca_fe_ops = { + .set_tdm_slot = mca_fe_set_tdm_slot, + .hw_params = mca_fe_hw_params, + .trigger = mca_fe_trigger, ++ .prepare = mca_fe_prepare, ++ .hw_free = mca_fe_hw_free, ++ .auto_selectable_formats = mca_fe_selectable_formats, ++ .num_auto_selectable_formats = ARRAY_SIZE(mca_fe_selectable_formats), + }; + +-static bool mca_be_started(struct mca_cluster *cl) ++/* ++ * Is there a FE attached which will be feeding this port's clocks? ++ */ ++static bool mca_be_clk_started(struct mca_cluster *cl) + { + int stream; + + for_each_pcm_streams(stream) +- if (cl->port_started[stream]) ++ if (cl->port_clk_started[stream]) + return true; + return false; + } + ++static struct snd_soc_pcm_runtime *mca_be_get_fe(struct snd_soc_pcm_runtime *be, ++ int stream) ++{ ++ struct snd_soc_pcm_runtime *fe = NULL; ++ struct snd_soc_dpcm *dpcm; ++ ++ for_each_dpcm_fe(be, stream, dpcm) { ++ if (fe && dpcm->fe != fe) { ++ dev_err(be->dev, "many FE per one BE unsupported\n"); ++ return NULL; ++ } ++ ++ fe = dpcm->fe; ++ } ++ ++ return fe; ++} ++ ++static int mca_be_prepare(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream); ++ struct snd_soc_pcm_runtime *fe = mca_be_get_fe(be, substream->stream); ++ struct mca_cluster *cl = mca_dai_to_cluster(dai); ++ struct mca_data *mca = cl->host; ++ struct mca_cluster *fe_cl, *fe_clk_cl; ++ int ret; ++ ++ fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0)); ++ ++ if (!fe_cl->clk_provider) ++ return 0; ++ ++ if (cl->port_clk_driver < 0) ++ return 0; ++ ++ fe_clk_cl = &mca->clusters[cl->port_clk_driver]; ++ ++ /* ++ * Typically the CODECs we are paired with will require clocks ++ * to be present at time of unmute with the 'mute_stream' op ++ * or at time of DAPM widget power-up. We need to enable clocks ++ * here at the latest (frontend prepare would be too late). ++ */ ++ if (!mca_fe_clocks_in_use(fe_clk_cl)) { ++ ret = mca_fe_enable_clocks(fe_clk_cl); ++ if (ret < 0) ++ return ret; ++ } ++ ++ cl->clocks_in_use[substream->stream] = true; ++ ++ return 0; ++} ++ + static int mca_be_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + { + struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream); +- struct snd_soc_pcm_runtime *fe; ++ struct snd_soc_pcm_runtime *fe = mca_be_get_fe(be, substream->stream); + struct mca_cluster *cl = mca_dai_to_cluster(dai); + struct mca_cluster *fe_cl; + struct mca_data *mca = cl->host; +- struct snd_soc_dpcm *dpcm; +- +- fe = NULL; +- +- for_each_dpcm_fe(be, substream->stream, dpcm) { +- if (fe && dpcm->fe != fe) { +- dev_err(mca->dev, "many FE per one BE unsupported\n"); +- return -EINVAL; +- } +- +- fe = dpcm->fe; +- } + + if (!fe) + return -EINVAL; +- + fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0)); + +- if (mca_be_started(cl)) { ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ writel_relaxed(PORT_DATA_SEL_TXA(fe_cl->no), ++ cl->base + REG_PORT_DATA_SEL); ++ mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_TX_DATA, ++ PORT_ENABLES_TX_DATA); ++ } ++ ++ if (!fe_cl->clk_provider) ++ return 0; ++ ++ if (mca_be_clk_started(cl)) { + /* + * Port is already started in the other direction. + * Make sure there isn't a conflict with another cluster +- * driving the port. ++ * driving the port clocks. + */ +- if (cl->port_driver != fe_cl->no) ++ if (cl->port_clk_driver != fe_cl->no) + return -EINVAL; + +- cl->port_started[substream->stream] = true; ++ cl->port_clk_started[substream->stream] = true; + return 0; + } + +- writel_relaxed(PORT_ENABLES_CLOCKS | PORT_ENABLES_TX_DATA, +- cl->base + REG_PORT_ENABLES); + writel_relaxed(FIELD_PREP(PORT_CLOCK_SEL, fe_cl->no + 1), + cl->base + REG_PORT_CLOCK_SEL); ++ + writel_relaxed(PORT_DATA_SEL_TXA(fe_cl->no), + cl->base + REG_PORT_DATA_SEL); ++ ++ mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_CLOCKS, ++ PORT_ENABLES_CLOCKS); ++ + scoped_guard(mutex, &mca->port_mutex) +- cl->port_driver = fe_cl->no; +- cl->port_started[substream->stream] = true; ++ cl->port_clk_driver = fe_cl->no; ++ cl->port_clk_started[substream->stream] = true; + + return 0; + } +@@ -773,20 +888,34 @@ static int mca_be_startup(struct snd_pcm_substream *substream, + static void mca_be_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + { ++ struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream); ++ struct snd_soc_pcm_runtime *fe = mca_be_get_fe(be, substream->stream); + struct mca_cluster *cl = mca_dai_to_cluster(dai); ++ struct mca_cluster *fe_cl; + struct mca_data *mca = cl->host; + +- cl->port_started[substream->stream] = false; ++ if (!fe) ++ return; ++ fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0)); + +- if (!mca_be_started(cl)) { ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_TX_DATA, 0); ++ writel_relaxed(0, cl->base + REG_PORT_DATA_SEL); ++ } ++ ++ if (!fe_cl->clk_provider) ++ return; ++ ++ cl->port_clk_started[substream->stream] = false; ++ if (!mca_be_clk_started(cl)) { + /* + * Were we the last direction to shutdown? +- * Turn off the lights. ++ * Turn off the lights (clocks). + */ +- writel_relaxed(0, cl->base + REG_PORT_ENABLES); +- writel_relaxed(0, cl->base + REG_PORT_DATA_SEL); ++ mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_CLOCKS, 0); ++ writel_relaxed(0, cl->base + REG_PORT_CLOCK_SEL); + scoped_guard(mutex, &mca->port_mutex) +- cl->port_driver = -1; ++ cl->port_clk_driver = -1; + } + } + +@@ -1092,7 +1221,7 @@ static int apple_mca_probe(struct platform_device *pdev) + cl->host = mca; + cl->no = i; + cl->base = base + CLUSTER_STRIDE * i; +- cl->port_driver = -1; ++ cl->port_clk_driver = -1; + cl->clk_parent = of_clk_get(pdev->dev.of_node, i); + if (IS_ERR(cl->clk_parent)) { + dev_err(&pdev->dev, "unable to obtain clock %d: %ld\n", +diff --git a/sound/soc/atmel/atmel-i2s.c b/sound/soc/atmel/atmel-i2s.c +--- a/sound/soc/atmel/atmel-i2s.c ++++ b/sound/soc/atmel/atmel-i2s.c +@@ -540,12 +540,16 @@ static int atmel_i2s_dai_probe(struct snd_soc_dai *dai) + return 0; + } + ++static const u64 atmel_i2s_selectable_formats = SND_SOC_POSSIBLE_DAIFMT_I2S; ++ + static const struct snd_soc_dai_ops atmel_i2s_dai_ops = { + .probe = atmel_i2s_dai_probe, + .prepare = atmel_i2s_prepare, + .trigger = atmel_i2s_trigger, + .hw_params = atmel_i2s_hw_params, + .set_fmt = atmel_i2s_set_dai_fmt, ++ .auto_selectable_formats = &atmel_i2s_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static struct snd_soc_dai_driver atmel_i2s_dai = { +diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c +--- a/sound/soc/atmel/atmel_ssc_dai.c ++++ b/sound/soc/atmel/atmel_ssc_dai.c +@@ -825,6 +825,11 @@ static int atmel_ssc_resume(struct snd_soc_component *component) + #define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + ++static const u64 atmel_ssc_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A; ++ + static const struct snd_soc_dai_ops atmel_ssc_dai_ops = { + .startup = atmel_ssc_startup, + .shutdown = atmel_ssc_shutdown, +@@ -833,6 +838,8 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = { + .hw_params = atmel_ssc_hw_params, + .set_fmt = atmel_ssc_set_dai_fmt, + .set_clkdiv = atmel_ssc_set_dai_clkdiv, ++ .auto_selectable_formats = &atmel_ssc_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static struct snd_soc_dai_driver atmel_ssc_dai = { +diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c +--- a/sound/soc/atmel/atmel_wm8904.c ++++ b/sound/soc/atmel/atmel_wm8904.c +@@ -82,32 +82,33 @@ static struct snd_soc_card atmel_asoc_wm8904_card = { + + static int atmel_asoc_wm8904_dt_init(struct platform_device *pdev) + { +- struct device_node *np = pdev->dev.of_node; ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; + struct device_node *codec_np, *cpu_np; + struct snd_soc_card *card = &atmel_asoc_wm8904_card; + struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink; + int ret; + + if (!np) { +- dev_err(&pdev->dev, "only device tree supported\n"); ++ dev_err(dev, "only device tree supported\n"); + return -EINVAL; + } + + ret = snd_soc_of_parse_card_name(card, "atmel,model"); + if (ret) { +- dev_err(&pdev->dev, "failed to parse card name\n"); ++ dev_err(dev, "failed to parse card name\n"); + return ret; + } + + ret = snd_soc_of_parse_audio_routing(card, "atmel,audio-routing"); + if (ret) { +- dev_err(&pdev->dev, "failed to parse audio routing\n"); ++ dev_err(dev, "failed to parse audio routing\n"); + return ret; + } + + cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0); + if (!cpu_np) { +- dev_err(&pdev->dev, "failed to get dai and pcm info\n"); ++ dev_err(dev, "failed to get dai and pcm info\n"); + ret = -EINVAL; + return ret; + } +@@ -117,7 +118,7 @@ static int atmel_asoc_wm8904_dt_init(struct platform_device *pdev) + + codec_np = of_parse_phandle(np, "atmel,audio-codec", 0); + if (!codec_np) { +- dev_err(&pdev->dev, "failed to get codec info\n"); ++ dev_err(dev, "failed to get codec info\n"); + ret = -EINVAL; + return ret; + } +diff --git a/sound/soc/atmel/mchp-i2s-mcc.c b/sound/soc/atmel/mchp-i2s-mcc.c +--- a/sound/soc/atmel/mchp-i2s-mcc.c ++++ b/sound/soc/atmel/mchp-i2s-mcc.c +@@ -912,6 +912,13 @@ static int mchp_i2s_mcc_dai_probe(struct snd_soc_dai *dai) + return 0; + } + ++static const u64 mchp_i2s_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A | ++ SND_SOC_POSSIBLE_DAIFMT_GATED | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF; ++ + static const struct snd_soc_dai_ops mchp_i2s_mcc_dai_ops = { + .probe = mchp_i2s_mcc_dai_probe, + .set_sysclk = mchp_i2s_mcc_set_sysclk, +@@ -922,6 +929,8 @@ static const struct snd_soc_dai_ops mchp_i2s_mcc_dai_ops = { + .hw_free = mchp_i2s_mcc_hw_free, + .set_fmt = mchp_i2s_mcc_set_dai_fmt, + .set_tdm_slot = mchp_i2s_mcc_set_dai_tdm_slot, ++ .auto_selectable_formats = &mchp_i2s_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + #define MCHP_I2SMCC_RATES SNDRV_PCM_RATE_8000_192000 +diff --git a/sound/soc/atmel/mchp-pdmc.c b/sound/soc/atmel/mchp-pdmc.c +--- a/sound/soc/atmel/mchp-pdmc.c ++++ b/sound/soc/atmel/mchp-pdmc.c +@@ -741,6 +741,8 @@ static int mchp_pdmc_pcm_new(struct snd_soc_pcm_runtime *rtd, + return ret; + } + ++static const u64 mchp_selectable_formats = SND_SOC_POSSIBLE_DAIFMT_PDM; ++ + static const struct snd_soc_dai_ops mchp_pdmc_dai_ops = { + .probe = mchp_pdmc_dai_probe, + .set_fmt = mchp_pdmc_set_fmt, +@@ -748,6 +750,8 @@ static const struct snd_soc_dai_ops mchp_pdmc_dai_ops = { + .hw_params = mchp_pdmc_hw_params, + .trigger = mchp_pdmc_trigger, + .pcm_new = &mchp_pdmc_pcm_new, ++ .auto_selectable_formats = &mchp_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static struct snd_soc_dai_driver mchp_pdmc_dai = { +diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c +--- a/sound/soc/au1x/db1200.c ++++ b/sound/soc/au1x/db1200.c +@@ -42,7 +42,7 @@ static const struct platform_device_id db1200_pids[] = { + .name = "db1550-i2s", + .driver_data = 5, + }, +- {}, ++ { } + }; + MODULE_DEVICE_TABLE(platform, db1200_pids); + +diff --git a/sound/soc/au1x/i2sc.c b/sound/soc/au1x/i2sc.c +--- a/sound/soc/au1x/i2sc.c ++++ b/sound/soc/au1x/i2sc.c +@@ -202,11 +202,22 @@ static int au1xi2s_startup(struct snd_pcm_substream *substream, + return 0; + } + ++static const u64 au1xi2s_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_NB_IF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_IF; ++ + static const struct snd_soc_dai_ops au1xi2s_dai_ops = { + .startup = au1xi2s_startup, + .trigger = au1xi2s_trigger, + .hw_params = au1xi2s_hw_params, + .set_fmt = au1xi2s_set_fmt, ++ .auto_selectable_formats = &au1xi2s_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static struct snd_soc_dai_driver au1xi2s_dai_driver = { +diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c +--- a/sound/soc/au1x/psc-ac97.c ++++ b/sound/soc/au1x/psc-ac97.c +@@ -210,9 +210,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, + { + struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai); + unsigned long r, ro, stat; +- int chans, t, stype = substream->stream; +- +- chans = params_channels(params); ++ int t, stype = substream->stream; + + r = ro = __raw_readl(AC97_CFG(pscdata)); + stat = __raw_readl(AC97_STAT(pscdata)); +diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c +--- a/sound/soc/au1x/psc-i2s.c ++++ b/sound/soc/au1x/psc-i2s.c +@@ -262,11 +262,22 @@ static int au1xpsc_i2s_startup(struct snd_pcm_substream *substream, + return 0; + } + ++static const u64 au1xpsc_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_NB_IF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_IF; ++ + static const struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = { + .startup = au1xpsc_i2s_startup, + .trigger = au1xpsc_i2s_trigger, + .hw_params = au1xpsc_i2s_hw_params, + .set_fmt = au1xpsc_i2s_set_fmt, ++ .auto_selectable_formats = &au1xpsc_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static const struct snd_soc_dai_driver au1xpsc_i2s_dai_template = { +diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c +--- a/sound/soc/bcm/bcm2835-i2s.c ++++ b/sound/soc/bcm/bcm2835-i2s.c +@@ -748,6 +748,17 @@ static int bcm2835_i2s_dai_probe(struct snd_soc_dai *dai) + return 0; + } + ++static const u64 bcm2835_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_B | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_NB_IF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_IF; ++ + static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = { + .probe = bcm2835_i2s_dai_probe, + .startup = bcm2835_i2s_startup, +@@ -758,6 +769,8 @@ static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = { + .set_fmt = bcm2835_i2s_set_dai_fmt, + .set_bclk_ratio = bcm2835_i2s_set_dai_bclk_ratio, + .set_tdm_slot = bcm2835_i2s_set_dai_tdm_slot, ++ .auto_selectable_formats = &bcm2835_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static struct snd_soc_dai_driver bcm2835_i2s_dai = { +diff --git a/sound/soc/bcm/cygnus-ssp.c b/sound/soc/bcm/cygnus-ssp.c +--- a/sound/soc/bcm/cygnus-ssp.c ++++ b/sound/soc/bcm/cygnus-ssp.c +@@ -1133,6 +1133,11 @@ static int cygnus_ssp_resume(struct snd_soc_component *component) + #define cygnus_ssp_resume NULL + #endif + ++static const u64 cygnus_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_B; ++ + static const struct snd_soc_dai_ops cygnus_ssp_dai_ops = { + .startup = cygnus_ssp_startup, + .shutdown = cygnus_ssp_shutdown, +@@ -1141,6 +1146,8 @@ static const struct snd_soc_dai_ops cygnus_ssp_dai_ops = { + .set_fmt = cygnus_ssp_set_fmt, + .set_sysclk = cygnus_ssp_set_sysclk, + .set_tdm_slot = cygnus_set_dai_tdm_slot, ++ .auto_selectable_formats = &cygnus_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static const struct snd_soc_dai_ops cygnus_spdif_dai_ops = { +diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c +--- a/sound/soc/cirrus/ep93xx-i2s.c ++++ b/sound/soc/cirrus/ep93xx-i2s.c +@@ -401,6 +401,15 @@ static int ep93xx_i2s_resume(struct snd_soc_component *component) + #define ep93xx_i2s_resume NULL + #endif + ++static const u64 ep93xx_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_NB_IF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_IF; ++ + static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = { + .probe = ep93xx_i2s_dai_probe, + .startup = ep93xx_i2s_startup, +@@ -408,6 +417,8 @@ static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = { + .hw_params = ep93xx_i2s_hw_params, + .set_sysclk = ep93xx_i2s_set_sysclk, + .set_fmt = ep93xx_i2s_set_dai_fmt, ++ .auto_selectable_formats = &ep93xx_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + #define EP93XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S32_LE) +diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -238,6 +238,7 @@ config SND_SOC_ALL_CODECS + imply SND_SOC_RT715_SDCA_SDW + imply SND_SOC_RT721_SDCA_SDW + imply SND_SOC_RT722_SDCA_SDW ++ imply SND_SOC_RT766_SDCA_SDW + imply SND_SOC_RT1308_SDW + imply SND_SOC_RT1316_SDW + imply SND_SOC_RT1318 +@@ -367,6 +368,7 @@ config SND_SOC_ALL_CODECS + imply SND_SOC_WSA881X + imply SND_SOC_WSA883X + imply SND_SOC_WSA884X ++ imply SND_SOC_WSA885X + imply SND_SOC_ZL38060 + help + Normally ASoC codec drivers are only built if a machine driver which +@@ -615,8 +617,8 @@ config SND_SOC_AK4613 + depends on I2C + + config SND_SOC_AK4619 +- tristate "AKM AK4619 CODEC" +- depends on I2C ++ tristate "AKM AK4619 CODEC" ++ depends on I2C + + config SND_SOC_AK4642 + tristate "AKM AK4642 CODEC" +@@ -894,6 +896,7 @@ config SND_SOC_CS35L56_SDW + tristate "Cirrus Logic CS35L56 CODEC (SDW)" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE ++ select IRQ_DOMAIN + select SND_SOC_CS35L56 + select SND_SOC_CS35L56_SHARED + help +@@ -1206,16 +1209,16 @@ config SND_SOC_JZ4725B_CODEC + will be called snd-soc-jz4725b-codec. + + config SND_SOC_JZ4760_CODEC +- depends on MACH_INGENIC || COMPILE_TEST +- depends on OF +- select REGMAP +- tristate "Ingenic JZ4760 internal CODEC" +- help +- Enable support for the internal CODEC found in the JZ4760 SoC +- from Ingenic. ++ depends on MACH_INGENIC || COMPILE_TEST ++ depends on OF ++ select REGMAP ++ tristate "Ingenic JZ4760 internal CODEC" ++ help ++ Enable support for the internal CODEC found in the JZ4760 SoC ++ from Ingenic. + +- This driver can also be built as a module. If so, the module +- will be called snd-soc-jz4760-codec. ++ This driver can also be built as a module. If so, the module ++ will be called snd-soc-jz4760-codec. + + config SND_SOC_JZ4770_CODEC + depends on MACH_INGENIC || COMPILE_TEST +@@ -1893,7 +1896,7 @@ config SND_SOC_RT5670 + depends on I2C + + config SND_SOC_RT5677 +- tristate ++ tristate "Realtek RT5677 Codec" + depends on I2C + select REGMAP_I2C + select REGMAP_IRQ +@@ -1983,6 +1986,15 @@ config SND_SOC_RT715_SDCA_SDW + select REGMAP_SOUNDWIRE + select REGMAP_SOUNDWIRE_MBQ + ++config SND_SOC_RT766_SDCA_SDW ++ tristate "Realtek RT766 SDCA Codec - SDW" ++ depends on SOUNDWIRE ++ depends on SND_SOC_SDCA ++ select SND_SOC_SDCA_HID ++ select SND_SOC_SDCA_IRQ ++ select REGMAP_SOUNDWIRE ++ select REGMAP_SOUNDWIRE_MBQ ++ + config SND_SOC_RT9120 + tristate "Richtek RT9120 Stereo Class-D Amplifier" + depends on I2C +@@ -2302,8 +2314,8 @@ config SND_SOC_TLV320ADC3XXX + depends on I2C + depends on GPIOLIB + help +- Enable support for Texas Instruments TLV320ADC3001 and TLV320ADC3101 +- ADCs. ++ Enable support for Texas Instruments TLV320ADC3001 and TLV320ADC3101 ++ ADCs. + + config SND_SOC_TLV320AIC23 + tristate +@@ -2776,6 +2788,16 @@ config SND_SOC_WSA884X + This enables support for Qualcomm WSA8840/WSA8845/WSA8845H Class-D + Smart Speaker Amplifier. + ++config SND_SOC_WSA885X ++ tristate "WSA885X Codec" ++ depends on I2C ++ select REGMAP_I2C ++ help ++ This enables support for Qualcomm WSA885X Stereo Smart Speaker ++ Amplifier. The codec driver programs the amplifier register ++ map and exposes the DAI and mixer controls used by Qualcomm ++ audio machine drivers. ++ + config SND_SOC_ZL38060 + tristate "Microsemi ZL38060 Connected Home Audio Processor" + depends on SPI_MASTER +@@ -2899,28 +2921,32 @@ config SND_SOC_TPA6130A2 + depends on I2C + + config SND_SOC_LPASS_MACRO_COMMON +- tristate ++ tristate + + config SND_SOC_LPASS_WSA_MACRO + depends on COMMON_CLK ++ depends on PM_CLK + select REGMAP_MMIO + select SND_SOC_LPASS_MACRO_COMMON + tristate "Qualcomm WSA Macro in LPASS(Low Power Audio SubSystem)" + + config SND_SOC_LPASS_VA_MACRO + depends on COMMON_CLK ++ depends on PM_CLK + select REGMAP_MMIO + select SND_SOC_LPASS_MACRO_COMMON + tristate "Qualcomm VA Macro in LPASS(Low Power Audio SubSystem)" + + config SND_SOC_LPASS_RX_MACRO + depends on COMMON_CLK ++ depends on PM_CLK + select REGMAP_MMIO + select SND_SOC_LPASS_MACRO_COMMON + tristate "Qualcomm RX Macro in LPASS(Low Power Audio SubSystem)" + + config SND_SOC_LPASS_TX_MACRO + depends on COMMON_CLK ++ depends on PM_CLK + select REGMAP_MMIO + select SND_SOC_LPASS_MACRO_COMMON + tristate "Qualcomm TX Macro in LPASS(Low Power Audio SubSystem)" +diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile +--- a/sound/soc/codecs/Makefile ++++ b/sound/soc/codecs/Makefile +@@ -285,6 +285,7 @@ snd-soc-rt715-y := rt715.o rt715-sdw.o + snd-soc-rt715-sdca-y := rt715-sdca.o rt715-sdca-sdw.o + snd-soc-rt721-sdca-y := rt721-sdca.o rt721-sdca-sdw.o + snd-soc-rt722-sdca-y := rt722-sdca.o rt722-sdca-sdw.o ++snd-soc-rt766-sdca-y := rt766-sdca.o rt766-sdca-sdw.o + snd-soc-rt9120-y := rt9120.o + snd-soc-rt9123-y := rt9123.o + snd-soc-rt9123p-y := rt9123p.o +@@ -427,6 +428,7 @@ snd-soc-wm-hubs-y := wm_hubs.o + snd-soc-wsa881x-y := wsa881x.o + snd-soc-wsa883x-y := wsa883x.o + snd-soc-wsa884x-y := wsa884x.o ++snd-soc-wsa885x-y := wsa885x.o + snd-soc-zl38060-y := zl38060.o + # Amp + snd-soc-max9877-y := max9877.o +@@ -726,6 +728,7 @@ obj-$(CONFIG_SND_SOC_RT715) += snd-soc-rt715.o + obj-$(CONFIG_SND_SOC_RT715_SDCA_SDW) += snd-soc-rt715-sdca.o + obj-$(CONFIG_SND_SOC_RT721_SDCA_SDW) += snd-soc-rt721-sdca.o + obj-$(CONFIG_SND_SOC_RT722_SDCA_SDW) += snd-soc-rt722-sdca.o ++obj-$(CONFIG_SND_SOC_RT766_SDCA_SDW) += snd-soc-rt766-sdca.o + obj-$(CONFIG_SND_SOC_RT9120) += snd-soc-rt9120.o + obj-$(CONFIG_SND_SOC_RT9123) += snd-soc-rt9123.o + obj-$(CONFIG_SND_SOC_RT9123P) += snd-soc-rt9123p.o +@@ -878,6 +881,7 @@ obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o + obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o + obj-$(CONFIG_SND_SOC_WSA883X) += snd-soc-wsa883x.o + obj-$(CONFIG_SND_SOC_WSA884X) += snd-soc-wsa884x.o ++obj-$(CONFIG_SND_SOC_WSA885X) += snd-soc-wsa885x.o + obj-$(CONFIG_SND_SOC_ZL38060) += snd-soc-zl38060.o + + # Amp +diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c +--- a/sound/soc/codecs/ab8500-codec.c ++++ b/sound/soc/codecs/ab8500-codec.c +@@ -14,14 +14,17 @@ + * for ST-Ericsson. + */ + ++#include + #include + #include + #include + #include + #include + #include ++#include + #include + #include ++#include + #include + #include + #include +@@ -51,9 +54,32 @@ + /* Macrocell register definitions */ + #define AB8500_GPIO_DIR4_REG 0x13 /* Bank AB8500_MISC */ + ++/* Nr of FIR/IIR-coeff banks in ANC-block */ ++#define AB8500_NR_OF_ANC_COEFF_BANKS 2 ++ ++/* Minimum duration to keep ANC IIR Init bit high or ++low before proceeding with the configuration sequence */ ++#define AB8500_ANC_SM_DELAY 2000 ++ ++/* Sidetone states */ ++static const char * const enum_sid_state[] = { ++ "Unconfigured", ++ "Apply FIR", ++ "FIR is configured", ++}; ++enum sid_state { ++ SID_UNCONFIGURED = 0, ++ SID_APPLY_FIR = 1, ++ SID_FIR_CONFIGURED = 2, ++}; ++ + /* Private data for AB8500 device-driver */ + struct ab8500_codec_drvdata { + struct regmap *regmap; ++ struct mutex ctrl_lock; ++ ++ /* Sidetone */ ++ enum sid_state sid_status; + }; + + static inline const char *amic_micbias_str(enum amic_micbias micbias) +@@ -616,6 +642,9 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = { + NULL, 0), + /* Acoustical Noise Cancellation path */ + ++ SND_SOC_DAPM_INPUT("ANC Configure Input"), ++ SND_SOC_DAPM_OUTPUT("ANC Configure Output"), ++ + SND_SOC_DAPM_MUX("ANC Source", + SND_SOC_NOPM, 0, 0, + dapm_anc_in_select), +@@ -658,6 +687,10 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { + {"Main Supply", NULL, "Audio Power"}, + {"Main Supply", NULL, "Audio Analog Power"}, + ++ /* ANC Configure */ ++ {"ANC Configure Input", NULL, "Main Supply"}, ++ {"ANC Configure Output", NULL, "ANC Configure Input"}, ++ + /* Powerup charge pump if DA1/2 is in use */ + + {"DA_IN1", NULL, "ab8500_0p"}, +@@ -939,6 +972,75 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes_mic2_vamicx[] = { + {"MIC2 V-AMICx Enable", NULL, "V-AMIC2"}, + }; + ++/* ++ * Control-events ++ */ ++ ++static int sid_status_control_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev); ++ ++ guard(mutex)(&drvdata->ctrl_lock); ++ ucontrol->value.enumerated.item[0] = drvdata->sid_status; ++ ++ return 0; ++} ++ ++/* Write sidetone FIR-coefficients configuration sequence */ ++static int sid_status_control_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev); ++ unsigned int param, sidconf; ++ int status = 1; ++ ++ dev_dbg(component->dev, "%s: Enter\n", __func__); ++ ++ if (ucontrol->value.enumerated.item[0] != SID_APPLY_FIR) { ++ dev_err(component->dev, ++ "%s: ERROR: This control supports '%s' only!\n", ++ __func__, enum_sid_state[SID_APPLY_FIR]); ++ return -EIO; ++ } ++ ++ guard(mutex)(&drvdata->ctrl_lock); ++ ++ sidconf = snd_soc_component_read(component, AB8500_SIDFIRCONF); ++ if (((sidconf & BIT(AB8500_SIDFIRCONF_FIRSIDBUSY)) != 0)) { ++ if ((sidconf & BIT(AB8500_SIDFIRCONF_ENFIRSIDS)) == 0) { ++ dev_err(component->dev, "%s: Sidetone busy while off!\n", ++ __func__); ++ status = -EPERM; ++ } else { ++ status = -EBUSY; ++ } ++ dev_dbg(component->dev, "%s: Exit\n", __func__); ++ return status; ++ } ++ ++ snd_soc_component_write(component, AB8500_SIDFIRADR, 0); ++ ++ for (param = 0; param < AB8500_SID_FIR_COEFFS; param++) { ++ snd_soc_component_write(component, AB8500_SIDFIRCOEF1, 0); ++ snd_soc_component_write(component, AB8500_SIDFIRCOEF2, 0); ++ } ++ ++ snd_soc_component_update_bits(component, AB8500_SIDFIRADR, ++ BIT(AB8500_SIDFIRADR_FIRSIDSET), ++ BIT(AB8500_SIDFIRADR_FIRSIDSET)); ++ snd_soc_component_update_bits(component, AB8500_SIDFIRADR, ++ BIT(AB8500_SIDFIRADR_FIRSIDSET), 0); ++ ++ drvdata->sid_status = SID_FIR_CONFIGURED; ++ ++ dev_dbg(component->dev, "%s: Exit\n", __func__); ++ ++ return status; ++} ++ + /* + * Controls - Non-DAPM ASoC + */ +@@ -1222,6 +1324,9 @@ static SOC_ENUM_SINGLE_DECL(soc_enum_bfifomast, + AB8500_FIFOCONF3, AB8500_FIFOCONF3_BFIFOMAST_SHIFT, + enum_slavemaster); + ++/* Sidetone */ ++static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_sidstate, enum_sid_state); ++ + /* ANC */ + + static struct snd_kcontrol_new ab8500_ctrls[] = { +@@ -1512,6 +1617,8 @@ static struct snd_kcontrol_new ab8500_ctrls[] = { + AB8500_ANC_WARP_DELAY_MIN, AB8500_ANC_WARP_DELAY_MAX, 0), + + /* Sidetone */ ++ SOC_ENUM_EXT("Sidetone Status", soc_enum_sidstate, ++ sid_status_control_get, sid_status_control_put), + SOC_SINGLE_STROBE("Sidetone Reset", + AB8500_SIDFIRADR, AB8500_SIDFIRADR_FIRSIDSET, 0), + }; +@@ -2041,8 +2148,10 @@ static void ab8500_codec_of_probe(struct device *dev, struct device_node *np, + + static int ab8500_codec_probe(struct snd_soc_component *component) + { ++ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); + struct device *dev = component->dev; + struct device_node *np = dev->of_node; ++ struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(dev); + struct ab8500_codec_platform_data codec_pdata; + int status; + +@@ -2075,6 +2184,10 @@ static int ab8500_codec_probe(struct snd_soc_component *component) + snd_soc_component_write(component, AB8500_SHORTCIRCONF, + BIT(AB8500_SHORTCIRCONF_HSZCDDIS)); + ++ snd_soc_dapm_disable_pin(dapm, "ANC Configure Input"); ++ ++ mutex_init(&drvdata->ctrl_lock); ++ + return status; + } + +@@ -2103,6 +2216,7 @@ static int ab8500_codec_driver_probe(struct platform_device *pdev) + GFP_KERNEL); + if (!drvdata) + return -ENOMEM; ++ drvdata->sid_status = SID_UNCONFIGURED; + dev_set_drvdata(&pdev->dev, drvdata); + + drvdata->regmap = devm_regmap_init(&pdev->dev, NULL, &pdev->dev, +diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c +--- a/sound/soc/codecs/ad1836.c ++++ b/sound/soc/codecs/ad1836.c +@@ -193,9 +193,15 @@ static int ad1836_hw_params(struct snd_pcm_substream *substream, + return 0; + } + ++static const u64 ad1836_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A | ++ SND_SOC_POSSIBLE_DAIFMT_IB_IF; ++ + static const struct snd_soc_dai_ops ad1836_dai_ops = { + .hw_params = ad1836_hw_params, + .set_fmt = ad1836_set_dai_fmt, ++ .auto_selectable_formats = &ad1836_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + #define AD183X_DAI(_name, num_dacs, num_adcs) \ +diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c +--- a/sound/soc/codecs/ad193x.c ++++ b/sound/soc/codecs/ad193x.c +@@ -394,6 +394,14 @@ static int ad193x_startup(struct snd_pcm_substream *substream, + &constr); + } + ++static const u64 ad193x_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_NB_IF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_IF; ++ + static const struct snd_soc_dai_ops ad193x_dai_ops = { + .startup = ad193x_startup, + .hw_params = ad193x_hw_params, +@@ -401,6 +409,8 @@ static const struct snd_soc_dai_ops ad193x_dai_ops = { + .set_tdm_slot = ad193x_set_tdm_slot, + .set_sysclk = ad193x_set_dai_sysclk, + .set_fmt = ad193x_set_dai_fmt, ++ .auto_selectable_formats = &ad193x_selectable_formats, ++ .num_auto_selectable_formats = 1, + .no_capture_mute = 1, + }; + +diff --git a/sound/soc/codecs/adau7118-hw.c b/sound/soc/codecs/adau7118-hw.c +--- a/sound/soc/codecs/adau7118-hw.c ++++ b/sound/soc/codecs/adau7118-hw.c +@@ -22,7 +22,7 @@ static const struct of_device_id adau7118_of_match[] = { + MODULE_DEVICE_TABLE(of, adau7118_of_match); + + static const struct platform_device_id adau7118_id[] = { +- { .name = "adau7118" }, ++ { .name = "adau7118" }, + { } + }; + MODULE_DEVICE_TABLE(platform, adau7118_id); +diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c +--- a/sound/soc/codecs/adav80x.c ++++ b/sound/soc/codecs/adav80x.c +@@ -743,11 +743,19 @@ static void adav80x_dai_shutdown(struct snd_pcm_substream *substream, + adav80x->rate = 0; + } + ++static const u64 adav80x_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF; ++ + static const struct snd_soc_dai_ops adav80x_dai_ops = { + .set_fmt = adav80x_set_dai_fmt, + .hw_params = adav80x_hw_params, + .startup = adav80x_dai_startup, + .shutdown = adav80x_dai_shutdown, ++ .auto_selectable_formats = &adav80x_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + #define ADAV80X_PLAYBACK_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ +diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c +--- a/sound/soc/codecs/ak4613.c ++++ b/sound/soc/codecs/ak4613.c +@@ -95,6 +95,7 @@ + * see + * AK4613_ENABLE_TDM_TEST + */ ++#include + #include + #include + #include +@@ -384,7 +385,7 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream, + struct ak4613_priv *priv = snd_soc_component_get_drvdata(component); + struct device *dev = component->dev; + +- mutex_lock(&priv->lock); ++ guard(mutex)(&priv->lock); + priv->cnt--; + if (priv->cnt < 0) { + dev_err(dev, "unexpected counter error\n"); +@@ -392,7 +393,6 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream, + } + if (!priv->cnt) + priv->ctrl1 = 0; +- mutex_unlock(&priv->lock); + } + + static void ak4613_hw_constraints(struct ak4613_priv *priv, +@@ -507,10 +507,9 @@ static int ak4613_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_component *component = dai->component; + struct ak4613_priv *priv = snd_soc_component_get_drvdata(component); + +- mutex_lock(&priv->lock); ++ guard(mutex)(&priv->lock); + ak4613_hw_constraints(priv, substream); + priv->cnt++; +- mutex_unlock(&priv->lock); + + return 0; + } +@@ -599,45 +598,47 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, + */ + ret = -EINVAL; + +- mutex_lock(&priv->lock); +- if (priv->cnt > 1) { +- /* +- * If it was already working, use current priv->ctrl1 +- */ +- ret = 0; +- } else { +- /* +- * It is not yet working, +- */ +- unsigned int channel = params_channels(params); +- u8 tdm; ++ scoped_guard(mutex, &priv->lock) { ++ if (priv->cnt > 1) { ++ /* ++ * If it was already working, use current priv->ctrl1 ++ */ ++ ret = 0; ++ } else { ++ /* ++ * It is not yet working, ++ */ ++ unsigned int channel = params_channels(params); ++ u8 tdm; + +- /* STEREO or TDM */ +- if (channel == 2) +- tdm = AK4613_CONFIG_MODE_STEREO; +- else +- tdm = AK4613_CONFIG_GET(priv, MODE); ++ /* STEREO or TDM */ ++ if (channel == 2) ++ tdm = AK4613_CONFIG_MODE_STEREO; ++ else ++ tdm = AK4613_CONFIG_GET(priv, MODE); + +- for (i = ARRAY_SIZE(ak4613_iface) - 1; i >= 0; i--) { +- const struct ak4613_interface *iface = ak4613_iface + i; ++ for (i = ARRAY_SIZE(ak4613_iface) - 1; i >= 0; i--) { ++ const struct ak4613_interface *iface = ak4613_iface + i; + +- if ((iface->fmt == fmt) && (iface->width == width)) { +- /* +- * Ctrl1 +- * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | +- * |TDM1|TDM0|DIF2|DIF1|DIF0|ATS1|ATS0|SMUTE| +- * < tdm > < iface->dif > +- */ +- priv->ctrl1 = (tdm << 6) | (iface->dif << 3); +- ret = 0; +- break; ++ if (iface->fmt == fmt && iface->width == width) { ++ /* ++ * Ctrl1 ++ * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | ++ * |TDM1|TDM0|DIF2|DIF1|DIF0|ATS1|ATS0|SMUTE| ++ * < tdm > < iface->dif > ++ */ ++ priv->ctrl1 = (tdm << 6) | (iface->dif << 3); ++ ret = 0; ++ break; ++ } + } + } + } +- mutex_unlock(&priv->lock); + +- if (ret < 0) +- goto hw_params_end; ++ if (ret < 0) { ++ dev_warn(dev, "unsupported data width/format combination\n"); ++ return ret; ++ } + + snd_soc_component_update_bits(component, CTRL1, FMT_MASK, priv->ctrl1); + snd_soc_component_update_bits(component, CTRL2, DFS_MASK, ctrl2); +@@ -645,10 +646,6 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, + snd_soc_component_update_bits(component, ICTRL, ICTRL_MASK, priv->ic); + snd_soc_component_update_bits(component, OCTRL, OCTRL_MASK, priv->oc); + +-hw_params_end: +- if (ret < 0) +- dev_warn(dev, "unsupported data width/format combination\n"); +- + return ret; + } + +diff --git a/sound/soc/codecs/arizona-jack.c b/sound/soc/codecs/arizona-jack.c +--- a/sound/soc/codecs/arizona-jack.c ++++ b/sound/soc/codecs/arizona-jack.c +@@ -5,6 +5,7 @@ + * Copyright (C) 2012-2014 Wolfson Microelectronics plc + */ + ++#include + #include + #include + #include +@@ -707,15 +708,13 @@ static void arizona_micd_timeout_work(struct work_struct *work) + struct arizona_priv, + micd_timeout_work.work); + +- mutex_lock(&info->lock); ++ guard(mutex)(&info->lock); + + dev_dbg(info->arizona->dev, "MICD timed out, reporting HP\n"); + + info->detecting = false; + + arizona_identify_headphone(info); +- +- mutex_unlock(&info->lock); + } + + static int arizona_micd_adc_read(struct arizona_priv *info) +@@ -921,12 +920,11 @@ static void arizona_micd_detect(struct work_struct *work) + + cancel_delayed_work_sync(&info->micd_timeout_work); + +- mutex_lock(&info->lock); ++ guard(mutex)(&info->lock); + + /* If the cable was removed while measuring ignore the result */ + if (!(info->jack->status & SND_JACK_MECHANICAL)) { + dev_dbg(arizona->dev, "Ignoring MICDET for removed cable\n"); +- mutex_unlock(&info->lock); + return; + } + +@@ -936,7 +934,6 @@ static void arizona_micd_detect(struct work_struct *work) + arizona_button_reading(info); + + pm_runtime_mark_last_busy(arizona->dev); +- mutex_unlock(&info->lock); + } + + static irqreturn_t arizona_micdet(int irq, void *data) +@@ -948,10 +945,10 @@ static irqreturn_t arizona_micdet(int irq, void *data) + cancel_delayed_work_sync(&info->micd_detect_work); + cancel_delayed_work_sync(&info->micd_timeout_work); + +- mutex_lock(&info->lock); +- if (!info->detecting) +- debounce = 0; +- mutex_unlock(&info->lock); ++ scoped_guard(mutex, &info->lock) { ++ if (!info->detecting) ++ debounce = 0; ++ } + + if (debounce) + queue_delayed_work(system_power_efficient_wq, +@@ -969,9 +966,8 @@ static void arizona_hpdet_work(struct work_struct *work) + struct arizona_priv, + hpdet_work.work); + +- mutex_lock(&info->lock); ++ guard(mutex)(&info->lock); + arizona_start_hpdet_acc_id(info); +- mutex_unlock(&info->lock); + } + + static int arizona_hpdet_wait(struct arizona_priv *info) +@@ -1018,9 +1014,9 @@ static irqreturn_t arizona_jackdet(int irq, void *data) + cancelled_hp = cancel_delayed_work_sync(&info->hpdet_work); + cancelled_mic = cancel_delayed_work_sync(&info->micd_timeout_work); + +- pm_runtime_get_sync(arizona->dev); ++ guard(pm_runtime_active_auto)(arizona->dev); + +- mutex_lock(&info->lock); ++ guard(mutex)(&info->lock); + + if (info->micd_clamp) { + mask = ARIZONA_MICD_CLAMP_STS; +@@ -1036,8 +1032,6 @@ static irqreturn_t arizona_jackdet(int irq, void *data) + ret = regmap_read(arizona->regmap, ARIZONA_AOD_IRQ_RAW_STATUS, &val); + if (ret) { + dev_err(arizona->dev, "Failed to read jackdet status: %d\n", ret); +- mutex_unlock(&info->lock); +- pm_runtime_put_autosuspend(arizona->dev); + return IRQ_NONE; + } + +@@ -1056,62 +1050,61 @@ static irqreturn_t arizona_jackdet(int irq, void *data) + &info->micd_timeout_work, + msecs_to_jiffies(micd_timeout)); + } ++ } else { ++ info->last_jackdet = val; + +- goto out; +- } +- info->last_jackdet = val; ++ if (info->last_jackdet == present) { ++ dev_dbg(arizona->dev, "Detected jack\n"); ++ snd_soc_jack_report(info->jack, SND_JACK_MECHANICAL, SND_JACK_MECHANICAL); + +- if (info->last_jackdet == present) { +- dev_dbg(arizona->dev, "Detected jack\n"); +- snd_soc_jack_report(info->jack, SND_JACK_MECHANICAL, SND_JACK_MECHANICAL); ++ info->detecting = true; ++ info->mic = false; ++ info->jack_flips = 0; + +- info->detecting = true; +- info->mic = false; +- info->jack_flips = 0; ++ if (!arizona->pdata.hpdet_acc_id) { ++ arizona_start_mic(info); ++ } else { ++ queue_delayed_work(system_power_efficient_wq, ++ &info->hpdet_work, ++ msecs_to_jiffies(HPDET_DEBOUNCE)); ++ } + +- if (!arizona->pdata.hpdet_acc_id) { +- arizona_start_mic(info); ++ if (info->micd_clamp || !arizona->pdata.jd_invert) ++ regmap_update_bits(arizona->regmap, ++ ARIZONA_JACK_DETECT_DEBOUNCE, ++ ARIZONA_MICD_CLAMP_DB | ++ ARIZONA_JD1_DB, 0); + } else { +- queue_delayed_work(system_power_efficient_wq, +- &info->hpdet_work, +- msecs_to_jiffies(HPDET_DEBOUNCE)); +- } ++ dev_dbg(arizona->dev, "Detected jack removal\n"); ++ ++ arizona_stop_mic(info); ++ ++ info->num_hpdet_res = 0; ++ for (i = 0; i < ARRAY_SIZE(info->hpdet_res); i++) ++ info->hpdet_res[i] = 0; ++ info->mic = false; ++ info->hpdet_done = false; ++ info->hpdet_retried = false; ++ ++ snd_soc_jack_report(info->jack, 0, ++ ARIZONA_JACK_MASK | info->micd_button_mask); ++ ++ /* ++ * If the jack was removed during a headphone detection we ++ * need to wait for the headphone detection to finish, as ++ * it can not be aborted. We don't want to be able to start ++ * a new headphone detection from a fresh insert until this ++ * one is finished. ++ */ ++ arizona_hpdet_wait(info); + +- if (info->micd_clamp || !arizona->pdata.jd_invert) + regmap_update_bits(arizona->regmap, + ARIZONA_JACK_DETECT_DEBOUNCE, +- ARIZONA_MICD_CLAMP_DB | +- ARIZONA_JD1_DB, 0); +- } else { +- dev_dbg(arizona->dev, "Detected jack removal\n"); +- +- arizona_stop_mic(info); +- +- info->num_hpdet_res = 0; +- for (i = 0; i < ARRAY_SIZE(info->hpdet_res); i++) +- info->hpdet_res[i] = 0; +- info->mic = false; +- info->hpdet_done = false; +- info->hpdet_retried = false; +- +- snd_soc_jack_report(info->jack, 0, ARIZONA_JACK_MASK | info->micd_button_mask); +- +- /* +- * If the jack was removed during a headphone detection we +- * need to wait for the headphone detection to finish, as +- * it can not be aborted. We don't want to be able to start +- * a new headphone detection from a fresh insert until this +- * one is finished. +- */ +- arizona_hpdet_wait(info); +- +- regmap_update_bits(arizona->regmap, +- ARIZONA_JACK_DETECT_DEBOUNCE, +- ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB, +- ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB); ++ ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB, ++ ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB); ++ } + } + +-out: + /* Clear trig_sts to make sure DCVDD is not forced up */ + regmap_write(arizona->regmap, ARIZONA_AOD_WKUP_AND_TRIG, + ARIZONA_MICD_CLAMP_FALL_TRIG_STS | +@@ -1119,10 +1112,6 @@ static irqreturn_t arizona_jackdet(int irq, void *data) + ARIZONA_JD1_FALL_TRIG_STS | + ARIZONA_JD1_RISE_TRIG_STS); + +- mutex_unlock(&info->lock); +- +- pm_runtime_put_autosuspend(arizona->dev); +- + return IRQ_HANDLED; + } + +diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c +--- a/sound/soc/codecs/arizona.c ++++ b/sound/soc/codecs/arizona.c +@@ -7,6 +7,7 @@ + * Author: Mark Brown + */ + ++#include + #include + #include + #include +@@ -1158,17 +1159,16 @@ int arizona_dvfs_up(struct snd_soc_component *component, unsigned int flags) + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + int ret = 0; + +- mutex_lock(&priv->dvfs_lock); ++ guard(mutex)(&priv->dvfs_lock); + + if (!priv->dvfs_cached && !priv->dvfs_reqs) { + ret = arizona_dvfs_enable(component); + if (ret) +- goto err; ++ return ret; + } + + priv->dvfs_reqs |= flags; +-err: +- mutex_unlock(&priv->dvfs_lock); ++ + return ret; + } + EXPORT_SYMBOL_GPL(arizona_dvfs_up); +@@ -1179,7 +1179,7 @@ int arizona_dvfs_down(struct snd_soc_component *component, unsigned int flags) + unsigned int old_reqs; + int ret = 0; + +- mutex_lock(&priv->dvfs_lock); ++ guard(mutex)(&priv->dvfs_lock); + + old_reqs = priv->dvfs_reqs; + priv->dvfs_reqs &= ~flags; +@@ -1187,7 +1187,6 @@ int arizona_dvfs_down(struct snd_soc_component *component, unsigned int flags) + if (!priv->dvfs_cached && old_reqs && !priv->dvfs_reqs) + ret = arizona_dvfs_disable(component); + +- mutex_unlock(&priv->dvfs_lock); + return ret; + } + EXPORT_SYMBOL_GPL(arizona_dvfs_down); +@@ -1199,7 +1198,7 @@ int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w, + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + int ret = 0; + +- mutex_lock(&priv->dvfs_lock); ++ guard(mutex)(&priv->dvfs_lock); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: +@@ -1222,7 +1221,6 @@ int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w, + break; + } + +- mutex_unlock(&priv->dvfs_lock); + return ret; + } + EXPORT_SYMBOL_GPL(arizona_dvfs_sysclk_ev); +@@ -1657,13 +1655,11 @@ static void arizona_wm5102_set_dac_comp(struct snd_soc_component *component, + { 0x80, 0x0 }, + }; + +- mutex_lock(&arizona->dac_comp_lock); +- +- dac_comp[1].def = arizona->dac_comp_coeff; +- if (rate >= 176400) +- dac_comp[2].def = arizona->dac_comp_enabled; +- +- mutex_unlock(&arizona->dac_comp_lock); ++ scoped_guard(mutex, &arizona->dac_comp_lock) { ++ dac_comp[1].def = arizona->dac_comp_coeff; ++ if (rate >= 176400) ++ dac_comp[2].def = arizona->dac_comp_enabled; ++ } + + regmap_multi_reg_write(arizona->regmap, + dac_comp, +diff --git a/sound/soc/codecs/aw87390.c b/sound/soc/codecs/aw87390.c +--- a/sound/soc/codecs/aw87390.c ++++ b/sound/soc/codecs/aw87390.c +@@ -7,6 +7,7 @@ + // Author: Weidong Wang + // + ++#include + #include + #include + #include +@@ -225,11 +226,10 @@ static int aw87390_profile_set(struct snd_kcontrol *kcontrol, + struct aw87390 *aw87390 = snd_soc_component_get_drvdata(codec); + int ret; + +- mutex_lock(&aw87390->lock); ++ guard(mutex)(&aw87390->lock); + ret = aw87390_dev_set_profile_index(aw87390->aw_pa, ucontrol->value.integer.value[0]); + if (ret) { + dev_dbg(codec->dev, "profile index does not change\n"); +- mutex_unlock(&aw87390->lock); + return 0; + } + +@@ -238,8 +238,6 @@ static int aw87390_profile_set(struct snd_kcontrol *kcontrol, + aw87390_power_on(aw87390->aw_pa); + } + +- mutex_unlock(&aw87390->lock); +- + return 1; + } + +@@ -250,7 +248,7 @@ static const struct snd_kcontrol_new aw87390_controls[] = { + + static int aw87390_request_firmware_file(struct aw87390 *aw87390) + { +- const struct firmware *cont = NULL; ++ const struct firmware *cont __free(firmware) = NULL; + int ret; + + aw87390->aw_pa->fw_status = AW87390_DEV_FW_FAILED; +@@ -265,14 +263,11 @@ static int aw87390_request_firmware_file(struct aw87390 *aw87390) + + aw87390->aw_cfg = devm_kzalloc(aw87390->aw_pa->dev, + struct_size(aw87390->aw_cfg, data, cont->size), GFP_KERNEL); +- if (!aw87390->aw_cfg) { +- release_firmware(cont); ++ if (!aw87390->aw_cfg) + return -ENOMEM; +- } + + aw87390->aw_cfg->len = cont->size; + memcpy(aw87390->aw_cfg->data, cont->data, cont->size); +- release_firmware(cont); + + ret = aw88395_dev_load_acf_check(aw87390->aw_pa, aw87390->aw_cfg); + if (ret) { +@@ -280,14 +275,12 @@ static int aw87390_request_firmware_file(struct aw87390 *aw87390) + return ret; + } + +- mutex_lock(&aw87390->lock); ++ guard(mutex)(&aw87390->lock); + + ret = aw88395_dev_cfg_load(aw87390->aw_pa, aw87390->aw_cfg); + if (ret) + dev_err(aw87390->aw_pa->dev, "aw_dev acf parse failed\n"); + +- mutex_unlock(&aw87390->lock); +- + return ret; + } + +diff --git a/sound/soc/codecs/aw88081.c b/sound/soc/codecs/aw88081.c +--- a/sound/soc/codecs/aw88081.c ++++ b/sound/soc/codecs/aw88081.c +@@ -7,6 +7,7 @@ + // Author: Weidong Wang + // + ++#include + #include + #include + #include +@@ -763,9 +764,8 @@ static void aw88081_startup_work(struct work_struct *work) + struct aw88081 *aw88081 = + container_of(work, struct aw88081, start_work.work); + +- mutex_lock(&aw88081->lock); ++ guard(mutex)(&aw88081->lock); + aw88081_start_pa(aw88081); +- mutex_unlock(&aw88081->lock); + } + + static void aw88081_start(struct aw88081 *aw88081, bool sync_start) +@@ -942,11 +942,10 @@ static int aw88081_profile_set(struct snd_kcontrol *kcontrol, + int ret; + + /* pa stop or stopping just set profile */ +- mutex_lock(&aw88081->lock); ++ guard(mutex)(&aw88081->lock); + ret = aw88081_dev_set_profile_index(aw88081->aw_pa, ucontrol->value.integer.value[0]); + if (ret) { + dev_dbg(codec->dev, "profile index does not change"); +- mutex_unlock(&aw88081->lock); + return 0; + } + +@@ -955,8 +954,6 @@ static int aw88081_profile_set(struct snd_kcontrol *kcontrol, + aw88081_start(aw88081, AW88081_SYNC_START); + } + +- mutex_unlock(&aw88081->lock); +- + return 1; + } + +@@ -1136,7 +1133,7 @@ static int aw88081_dev_init(struct aw88081 *aw88081, struct aw_container *aw_cfg + + static int aw88081_request_firmware_file(struct aw88081 *aw88081) + { +- const struct firmware *cont = NULL; ++ const struct firmware *cont __free(firmware) = NULL; + struct aw_container *aw_cfg; + int ret; + +@@ -1150,26 +1147,21 @@ static int aw88081_request_firmware_file(struct aw88081 *aw88081) + AW88081_ACF_FILE, cont ? cont->size : 0); + + aw_cfg = devm_kzalloc(aw88081->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL); +- if (!aw_cfg) { +- release_firmware(cont); ++ if (!aw_cfg) + return -ENOMEM; +- } ++ + aw_cfg->len = (int)cont->size; + memcpy(aw_cfg->data, cont->data, cont->size); + + aw88081->aw_cfg = aw_cfg; + +- release_firmware(cont); +- + ret = aw88395_dev_load_acf_check(aw88081->aw_pa, aw88081->aw_cfg); + if (ret) + return ret; + +- mutex_lock(&aw88081->lock); +- ret = aw88081_dev_init(aw88081, aw88081->aw_cfg); +- mutex_unlock(&aw88081->lock); ++ guard(mutex)(&aw88081->lock); + +- return ret; ++ return aw88081_dev_init(aw88081, aw88081->aw_cfg); + } + + static int aw88081_playback_event(struct snd_soc_dapm_widget *w, +@@ -1178,7 +1170,7 @@ static int aw88081_playback_event(struct snd_soc_dapm_widget *w, + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct aw88081 *aw88081 = snd_soc_component_get_drvdata(component); + +- mutex_lock(&aw88081->lock); ++ guard(mutex)(&aw88081->lock); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + aw88081_start(aw88081, AW88081_ASYNC_START); +@@ -1189,7 +1181,6 @@ static int aw88081_playback_event(struct snd_soc_dapm_widget *w, + default: + break; + } +- mutex_unlock(&aw88081->lock); + + return 0; + } +diff --git a/sound/soc/codecs/aw88166.c b/sound/soc/codecs/aw88166.c +--- a/sound/soc/codecs/aw88166.c ++++ b/sound/soc/codecs/aw88166.c +@@ -7,6 +7,7 @@ + // Author: Weidong Wang + // + ++#include + #include + #include + #include +@@ -1173,9 +1174,8 @@ static void aw88166_startup_work(struct work_struct *work) + struct aw88166 *aw88166 = + container_of(work, struct aw88166, start_work.work); + +- mutex_lock(&aw88166->lock); ++ guard(mutex)(&aw88166->lock); + aw88166_start_pa(aw88166); +- mutex_unlock(&aw88166->lock); + } + + static void aw88166_start(struct aw88166 *aw88166, bool sync_start) +@@ -1413,11 +1413,10 @@ static int aw88166_profile_set(struct snd_kcontrol *kcontrol, + struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec); + int ret; + +- mutex_lock(&aw88166->lock); ++ guard(mutex)(&aw88166->lock); + ret = aw88166_dev_set_profile_index(aw88166->aw_pa, ucontrol->value.integer.value[0]); + if (ret) { + dev_dbg(codec->dev, "profile index does not change"); +- mutex_unlock(&aw88166->lock); + return 0; + } + +@@ -1426,8 +1425,6 @@ static int aw88166_profile_set(struct snd_kcontrol *kcontrol, + aw88166_start(aw88166, AW88166_SYNC_START); + } + +- mutex_unlock(&aw88166->lock); +- + return 1; + } + +@@ -1573,7 +1570,7 @@ static int aw88166_dev_init(struct aw88166 *aw88166, struct aw_container *aw_cfg + + static int aw88166_request_firmware_file(struct aw88166 *aw88166) + { +- const struct firmware *cont = NULL; ++ const struct firmware *cont __free(firmware) = NULL; + const char *fw_name; + int ret; + +@@ -1593,13 +1590,11 @@ static int aw88166_request_firmware_file(struct aw88166 *aw88166) + + aw88166->aw_cfg = devm_kzalloc(aw88166->aw_pa->dev, + struct_size(aw88166->aw_cfg, data, cont->size), GFP_KERNEL); +- if (!aw88166->aw_cfg) { +- release_firmware(cont); ++ if (!aw88166->aw_cfg) + return -ENOMEM; +- } ++ + aw88166->aw_cfg->len = (int)cont->size; + memcpy(aw88166->aw_cfg->data, cont->data, cont->size); +- release_firmware(cont); + + ret = aw88395_dev_load_acf_check(aw88166->aw_pa, aw88166->aw_cfg); + if (ret) { +@@ -1607,12 +1602,12 @@ static int aw88166_request_firmware_file(struct aw88166 *aw88166) + return ret; + } + +- mutex_lock(&aw88166->lock); +- /* aw device init */ +- ret = aw88166_dev_init(aw88166, aw88166->aw_cfg); +- if (ret) +- dev_err(aw88166->aw_pa->dev, "dev init failed\n"); +- mutex_unlock(&aw88166->lock); ++ scoped_guard(mutex, &aw88166->lock) { ++ /* aw device init */ ++ ret = aw88166_dev_init(aw88166, aw88166->aw_cfg); ++ if (ret) ++ dev_err(aw88166->aw_pa->dev, "dev init failed\n"); ++ } + + return ret; + } +@@ -1639,7 +1634,7 @@ static int aw88166_playback_event(struct snd_soc_dapm_widget *w, + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct aw88166 *aw88166 = snd_soc_component_get_drvdata(component); + +- mutex_lock(&aw88166->lock); ++ guard(mutex)(&aw88166->lock); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + aw88166_start(aw88166, AW88166_ASYNC_START); +@@ -1650,7 +1645,6 @@ static int aw88166_playback_event(struct snd_soc_dapm_widget *w, + default: + break; + } +- mutex_unlock(&aw88166->lock); + + return 0; + } +diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c +--- a/sound/soc/codecs/aw88261.c ++++ b/sound/soc/codecs/aw88261.c +@@ -8,6 +8,7 @@ + // Author: Weidong Wang + // + ++#include + #include + #include + #include +@@ -960,11 +961,10 @@ static int aw88261_profile_set(struct snd_kcontrol *kcontrol, + int ret; + + /* pa stop or stopping just set profile */ +- mutex_lock(&aw88261->lock); ++ guard(mutex)(&aw88261->lock); + ret = aw88261_dev_set_profile_index(aw88261->aw_pa, ucontrol->value.integer.value[0]); + if (ret) { + dev_dbg(codec->dev, "profile index does not change"); +- mutex_unlock(&aw88261->lock); + return 0; + } + +@@ -973,8 +973,6 @@ static int aw88261_profile_set(struct snd_kcontrol *kcontrol, + aw88261_start(aw88261); + } + +- mutex_unlock(&aw88261->lock); +- + return 1; + } + +@@ -1038,7 +1036,7 @@ static int aw88261_playback_event(struct snd_soc_dapm_widget *w, + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); + +- mutex_lock(&aw88261->lock); ++ guard(mutex)(&aw88261->lock); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + aw88261_start(aw88261); +@@ -1049,7 +1047,6 @@ static int aw88261_playback_event(struct snd_soc_dapm_widget *w, + default: + break; + } +- mutex_unlock(&aw88261->lock); + + return 0; + } +@@ -1152,7 +1149,7 @@ static int aw88261_dev_init(struct aw88261 *aw88261, struct aw_container *aw_cfg + + static int aw88261_request_firmware_file(struct aw88261 *aw88261) + { +- const struct firmware *cont = NULL; ++ const struct firmware *cont __free(firmware) = NULL; + struct aw_container *aw_cfg; + const char *fw_name; + int ret; +@@ -1172,13 +1169,11 @@ static int aw88261_request_firmware_file(struct aw88261 *aw88261) + fw_name, cont ? cont->size : 0); + + aw_cfg = devm_kzalloc(aw88261->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL); +- if (!aw_cfg) { +- release_firmware(cont); ++ if (!aw_cfg) + return -ENOMEM; +- } ++ + aw_cfg->len = (int)cont->size; + memcpy(aw_cfg->data, cont->data, cont->size); +- release_firmware(cont); + + aw88261->aw_cfg = aw_cfg; + +@@ -1188,12 +1183,12 @@ static int aw88261_request_firmware_file(struct aw88261 *aw88261) + return ret; + } + +- mutex_lock(&aw88261->lock); +- /* aw device init */ +- ret = aw88261_dev_init(aw88261, aw88261->aw_cfg); +- if (ret) +- dev_err(aw88261->aw_pa->dev, "dev init failed"); +- mutex_unlock(&aw88261->lock); ++ scoped_guard(mutex, &aw88261->lock) { ++ /* aw device init */ ++ ret = aw88261_dev_init(aw88261, aw88261->aw_cfg); ++ if (ret) ++ dev_err(aw88261->aw_pa->dev, "dev init failed"); ++ } + + return ret; + } +diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c +--- a/sound/soc/codecs/aw88395/aw88395.c ++++ b/sound/soc/codecs/aw88395/aw88395.c +@@ -8,6 +8,7 @@ + // Author: Weidong Wang + // + ++#include + #include + #include + #include +@@ -51,9 +52,8 @@ static void aw88395_startup_work(struct work_struct *work) + struct aw88395 *aw88395 = + container_of(work, struct aw88395, start_work.work); + +- mutex_lock(&aw88395->lock); ++ guard(mutex)(&aw88395->lock); + aw88395_start_pa(aw88395); +- mutex_unlock(&aw88395->lock); + } + + static void aw88395_start(struct aw88395 *aw88395, bool sync_start) +@@ -224,11 +224,10 @@ static int aw88395_profile_set(struct snd_kcontrol *kcontrol, + int ret; + + /* pa stop or stopping just set profile */ +- mutex_lock(&aw88395->lock); ++ guard(mutex)(&aw88395->lock); + ret = aw88395_dev_set_profile_index(aw88395->aw_pa, ucontrol->value.integer.value[0]); + if (ret < 0) { + dev_dbg(codec->dev, "profile index does not change"); +- mutex_unlock(&aw88395->lock); + return 0; + } + +@@ -237,8 +236,6 @@ static int aw88395_profile_set(struct snd_kcontrol *kcontrol, + aw88395_start(aw88395, AW88395_SYNC_START); + } + +- mutex_unlock(&aw88395->lock); +- + return 1; + } + +@@ -366,7 +363,7 @@ static int aw88395_playback_event(struct snd_soc_dapm_widget *w, + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component); + +- mutex_lock(&aw88395->lock); ++ guard(mutex)(&aw88395->lock); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + aw88395_start(aw88395, AW88395_ASYNC_START); +@@ -377,7 +374,6 @@ static int aw88395_playback_event(struct snd_soc_dapm_widget *w, + default: + break; + } +- mutex_unlock(&aw88395->lock); + + return 0; + } +@@ -461,7 +457,7 @@ static void aw88395_hw_reset(struct aw88395 *aw88395) + + static int aw88395_request_firmware_file(struct aw88395 *aw88395) + { +- const struct firmware *cont = NULL; ++ const struct firmware *cont __free(firmware) = NULL; + struct aw_container *aw_cfg; + int ret; + +@@ -477,13 +473,11 @@ static int aw88395_request_firmware_file(struct aw88395 *aw88395) + AW88395_ACF_FILE, cont ? cont->size : 0); + + aw_cfg = devm_kzalloc(aw88395->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL); +- if (!aw_cfg) { +- release_firmware(cont); ++ if (!aw_cfg) + return -ENOMEM; +- } ++ + aw_cfg->len = (int)cont->size; + memcpy(aw_cfg->data, cont->data, cont->size); +- release_firmware(cont); + + aw88395->aw_cfg = aw_cfg; + +@@ -495,12 +489,12 @@ static int aw88395_request_firmware_file(struct aw88395 *aw88395) + + dev_dbg(aw88395->aw_pa->dev, "%s : bin load success\n", __func__); + +- mutex_lock(&aw88395->lock); +- /* aw device init */ +- ret = aw88395_dev_init(aw88395->aw_pa, aw88395->aw_cfg); +- if (ret < 0) +- dev_err(aw88395->aw_pa->dev, "dev init failed"); +- mutex_unlock(&aw88395->lock); ++ scoped_guard(mutex, &aw88395->lock) { ++ /* aw device init */ ++ ret = aw88395_dev_init(aw88395->aw_pa, aw88395->aw_cfg); ++ if (ret < 0) ++ dev_err(aw88395->aw_pa->dev, "dev init failed"); ++ } + + return ret; + } +diff --git a/sound/soc/codecs/aw88395/aw88395_device.c b/sound/soc/codecs/aw88395/aw88395_device.c +--- a/sound/soc/codecs/aw88395/aw88395_device.c ++++ b/sound/soc/codecs/aw88395/aw88395_device.c +@@ -8,6 +8,7 @@ + // Author: Ben Yi + // + ++#include + #include + #include + #include +@@ -70,7 +71,7 @@ int aw_dev_dsp_write(struct aw_device *aw_dev, + u32 reg_value; + int ret; + +- mutex_lock(&aw_dev->dsp_lock); ++ guard(mutex)(&aw_dev->dsp_lock); + switch (data_type) { + case AW_DSP_16_DATA: + ret = aw_dev_dsp_write_16bit(aw_dev, dsp_addr, dsp_data); +@@ -93,7 +94,6 @@ int aw_dev_dsp_write(struct aw_device *aw_dev, + /* clear dsp chip select state*/ + if (regmap_read(aw_dev->regmap, AW88395_ID_REG, ®_value)) + dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret); +- mutex_unlock(&aw_dev->dsp_lock); + + return ret; + } +@@ -156,7 +156,7 @@ int aw_dev_dsp_read(struct aw_device *aw_dev, + u32 reg_value; + int ret; + +- mutex_lock(&aw_dev->dsp_lock); ++ guard(mutex)(&aw_dev->dsp_lock); + switch (data_type) { + case AW_DSP_16_DATA: + ret = aw_dev_dsp_read_16bit(aw_dev, dsp_addr, dsp_data); +@@ -179,7 +179,6 @@ int aw_dev_dsp_read(struct aw_device *aw_dev, + /* clear dsp chip select state*/ + if (regmap_read(aw_dev->regmap, AW88395_ID_REG, ®_value)) + dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret); +- mutex_unlock(&aw_dev->dsp_lock); + + return ret; + } +@@ -1110,42 +1109,36 @@ static int aw_dev_dsp_update_container(struct aw_device *aw_dev, + #ifdef AW88395_DSP_I2C_WRITES + u32 tmp_len; + +- mutex_lock(&aw_dev->dsp_lock); ++ guard(mutex)(&aw_dev->dsp_lock); + ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, base); + if (ret) +- goto error_operation; ++ return ret; + + for (i = 0; i < len; i += AW88395_MAX_RAM_WRITE_BYTE_SIZE) { + tmp_len = min(len - i, AW88395_MAX_RAM_WRITE_BYTE_SIZE); + ret = regmap_raw_write(aw_dev->regmap, AW88395_DSPMDAT_REG, + &data[i], tmp_len); + if (ret) +- goto error_operation; ++ return ret; + } +- mutex_unlock(&aw_dev->dsp_lock); + #else + __be16 reg_val; + +- mutex_lock(&aw_dev->dsp_lock); ++ guard(mutex)(&aw_dev->dsp_lock); + /* i2c write */ + ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, base); + if (ret) +- goto error_operation; ++ return ret; + for (i = 0; i < len; i += 2) { + reg_val = cpu_to_be16p((u16 *)(data + i)); + ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, + (u16)reg_val); + if (ret) +- goto error_operation; ++ return ret; + } +- mutex_unlock(&aw_dev->dsp_lock); + #endif + + return 0; +- +-error_operation: +- mutex_unlock(&aw_dev->dsp_lock); +- return ret; + } + + static int aw_dev_dsp_update_fw(struct aw_device *aw_dev, +@@ -1231,14 +1224,14 @@ static int aw_dev_check_sram(struct aw_device *aw_dev) + { + unsigned int reg_val; + +- mutex_lock(&aw_dev->dsp_lock); ++ guard(mutex)(&aw_dev->dsp_lock); + /* check the odd bits of reg 0x40 */ + regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, AW88395_DSP_ODD_NUM_BIT_TEST); + regmap_read(aw_dev->regmap, AW88395_DSPMADD_REG, ®_val); + if (reg_val != AW88395_DSP_ODD_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check reg 0x40 odd bit failed, read[0x%x] != write[0x%x]", + reg_val, AW88395_DSP_ODD_NUM_BIT_TEST); +- goto error; ++ return -EPERM; + } + + /* check the even bits of reg 0x40 */ +@@ -1247,7 +1240,7 @@ static int aw_dev_check_sram(struct aw_device *aw_dev) + if (reg_val != AW88395_DSP_EVEN_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check reg 0x40 even bit failed, read[0x%x] != write[0x%x]", + reg_val, AW88395_DSP_EVEN_NUM_BIT_TEST); +- goto error; ++ return -EPERM; + } + + /* check dsp_fw_base_addr */ +@@ -1256,7 +1249,7 @@ static int aw_dev_check_sram(struct aw_device *aw_dev) + if (reg_val != AW88395_DSP_EVEN_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check dsp fw addr failed, read[0x%x] != write[0x%x]", + reg_val, AW88395_DSP_EVEN_NUM_BIT_TEST); +- goto error; ++ return -EPERM; + } + + /* check dsp_cfg_base_addr */ +@@ -1265,15 +1258,10 @@ static int aw_dev_check_sram(struct aw_device *aw_dev) + if (reg_val != AW88395_DSP_ODD_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] != write[0x%x]", + reg_val, AW88395_DSP_ODD_NUM_BIT_TEST); +- goto error; ++ return -EPERM; + } +- mutex_unlock(&aw_dev->dsp_lock); + + return 0; +- +-error: +- mutex_unlock(&aw_dev->dsp_lock); +- return -EPERM; + } + + int aw88395_dev_fw_update(struct aw_device *aw_dev, bool up_dsp_fw_en, bool force_up_en) +diff --git a/sound/soc/codecs/aw88399-lib.c b/sound/soc/codecs/aw88399-lib.c +--- a/sound/soc/codecs/aw88399-lib.c ++++ b/sound/soc/codecs/aw88399-lib.c +@@ -1282,7 +1282,7 @@ static int aw88399_dev_init(struct aw88399 *aw88399, struct aw_container *aw_cfg + + int aw88399_request_firmware_file(struct aw88399 *aw88399) + { +- const struct firmware *cont = NULL; ++ const struct firmware *cont __free(firmware) = NULL; + int ret; + + aw88399->aw_pa->fw_status = AW88399_DEV_FW_FAILED; +@@ -1298,13 +1298,11 @@ int aw88399_request_firmware_file(struct aw88399 *aw88399) + + aw88399->aw_cfg = devm_kzalloc(aw88399->aw_pa->dev, + struct_size(aw88399->aw_cfg, data, cont->size), GFP_KERNEL); +- if (!aw88399->aw_cfg) { +- release_firmware(cont); ++ if (!aw88399->aw_cfg) + return -ENOMEM; +- } ++ + aw88399->aw_cfg->len = (int)cont->size; + memcpy(aw88399->aw_cfg->data, cont->data, cont->size); +- release_firmware(cont); + + ret = aw88395_dev_load_acf_check(aw88399->aw_pa, aw88399->aw_cfg); + if (ret) { +diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c +--- a/sound/soc/codecs/aw88399.c ++++ b/sound/soc/codecs/aw88399.c +@@ -7,6 +7,7 @@ + // Author: Weidong Wang + // + ++#include + #include + #include + #include +@@ -497,11 +498,10 @@ static int aw88399_profile_set(struct snd_kcontrol *kcontrol, + struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec); + int ret; + +- mutex_lock(&aw88399->lock); ++ guard(mutex)(&aw88399->lock); + ret = aw88399_dev_set_profile_index(aw88399->aw_pa, ucontrol->value.integer.value[0]); + if (ret) { + dev_dbg(codec->dev, "profile index does not change"); +- mutex_unlock(&aw88399->lock); + return 0; + } + +@@ -510,8 +510,6 @@ static int aw88399_profile_set(struct snd_kcontrol *kcontrol, + aw88399_start(aw88399, AW88399_SYNC_START); + } + +- mutex_unlock(&aw88399->lock); +- + return 1; + } + +@@ -690,7 +688,7 @@ static int aw88399_playback_event(struct snd_soc_dapm_widget *w, + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct aw88399 *aw88399 = snd_soc_component_get_drvdata(component); + +- mutex_lock(&aw88399->lock); ++ guard(mutex)(&aw88399->lock); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + aw88399_start(aw88399, AW88399_ASYNC_START); +@@ -701,7 +699,6 @@ static int aw88399_playback_event(struct snd_soc_dapm_widget *w, + default: + break; + } +- mutex_unlock(&aw88399->lock); + + return 0; + } +diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c +--- a/sound/soc/codecs/bt-sco.c ++++ b/sound/soc/codecs/bt-sco.c +@@ -85,13 +85,9 @@ static int bt_sco_probe(struct platform_device *pdev) + } + + static const struct platform_device_id bt_sco_driver_ids[] = { +- { +- .name = "dfbmcs320", +- }, +- { +- .name = "bt-sco", +- }, +- {}, ++ { .name = "dfbmcs320" }, ++ { .name = "bt-sco" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids); + +diff --git a/sound/soc/codecs/cpcap.c b/sound/soc/codecs/cpcap.c +--- a/sound/soc/codecs/cpcap.c ++++ b/sound/soc/codecs/cpcap.c +@@ -26,14 +26,6 @@ + /* Register 9 - CPCAP_REG_INTS2 --- Interrupt Sense 2 */ + #define CPCAP_BIT_PTT_S 11 /* Push To Talk */ + +-/* Register 512 CPCAP_REG_VAUDIOC --- Audio Regulator and Bias Voltage */ +-#define CPCAP_BIT_AUDIO_LOW_PWR 6 +-#define CPCAP_BIT_AUD_LOWPWR_SPEED 5 +-#define CPCAP_BIT_VAUDIOPRISTBY 4 +-#define CPCAP_BIT_VAUDIO_MODE1 2 +-#define CPCAP_BIT_VAUDIO_MODE0 1 +-#define CPCAP_BIT_V_AUDIO_EN 0 +- + /* Register 513 CPCAP_REG_CC --- CODEC */ + #define CPCAP_BIT_CDC_CLK2 15 + #define CPCAP_BIT_CDC_CLK1 14 +@@ -239,7 +231,6 @@ struct cpcap_reg_info { + }; + + static const struct cpcap_reg_info cpcap_default_regs[] = { +- { CPCAP_REG_VAUDIOC, 0x003F, 0x0000 }, + { CPCAP_REG_CC, 0xFFFF, 0x0000 }, + { CPCAP_REG_CC, 0xFFFF, 0x0000 }, + { CPCAP_REG_CDI, 0xBFFF, 0x0000 }, +@@ -1265,6 +1256,7 @@ static int cpcap_voice_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) + { ++ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_component *component = dai->component; + struct device *dev = component->dev; + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); +@@ -1298,7 +1290,7 @@ static int cpcap_voice_hw_params(struct snd_pcm_substream *substream, + return err; + } + +- return 0; ++ return snd_soc_runtime_set_dai_fmt(rtd, rtd->dai_link->dai_fmt); + } + + static int cpcap_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, +@@ -1391,121 +1383,8 @@ static int cpcap_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, + return 0; + } + +- +-/* +- * Configure codec for voice call if requested. +- * +- * We can configure most with snd_soc_dai_set_sysclk(), snd_soc_dai_set_fmt() +- * and snd_soc_dai_set_tdm_slot(). This function configures the rest of the +- * cpcap related hardware as CPU is not involved in the voice call. +- */ +-static int cpcap_voice_call(struct cpcap_audio *cpcap, struct snd_soc_dai *dai, +- bool voice_call) +-{ +- int mask, err; +- +- /* Modem to codec VAUDIO_MODE1 */ +- mask = BIT(CPCAP_BIT_VAUDIO_MODE1); +- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_VAUDIOC, +- mask, voice_call ? mask : 0); +- if (err) +- return err; +- +- /* Clear MIC1_MUX for call */ +- mask = BIT(CPCAP_BIT_MIC1_MUX); +- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI, +- mask, voice_call ? 0 : mask); +- if (err) +- return err; +- +- /* Set MIC2_MUX for call */ +- mask = BIT(CPCAP_BIT_MB_ON1L) | BIT(CPCAP_BIT_MB_ON1R) | +- BIT(CPCAP_BIT_MIC2_MUX) | BIT(CPCAP_BIT_MIC2_PGA_EN); +- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI, +- mask, voice_call ? mask : 0); +- if (err) +- return err; +- +- /* Enable LDSP for call */ +- mask = BIT(CPCAP_BIT_A2_LDSP_L_EN) | BIT(CPCAP_BIT_A2_LDSP_R_EN); +- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXOA, +- mask, voice_call ? mask : 0); +- if (err) +- return err; +- +- /* Enable CPCAP_BIT_PGA_CDC_EN for call */ +- mask = BIT(CPCAP_BIT_PGA_CDC_EN); +- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXCOA, +- mask, voice_call ? mask : 0); +- if (err) +- return err; +- +- /* Unmute voice for call */ +- if (dai) { +- err = snd_soc_dai_digital_mute(dai, !voice_call, +- SNDRV_PCM_STREAM_PLAYBACK); +- if (err) +- return err; +- } +- +- /* Set modem to codec mic CDC and HPF for call */ +- mask = BIT(CPCAP_BIT_MIC2_CDC_EN) | BIT(CPCAP_BIT_CDC_EN_RX) | +- BIT(CPCAP_BIT_AUDOHPF_1) | BIT(CPCAP_BIT_AUDOHPF_0) | +- BIT(CPCAP_BIT_AUDIHPF_1) | BIT(CPCAP_BIT_AUDIHPF_0); +- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CC, +- mask, voice_call ? mask : 0); +- if (err) +- return err; +- +- /* Enable modem to codec CDC for call*/ +- mask = BIT(CPCAP_BIT_CDC_CLK_EN); +- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI, +- mask, voice_call ? mask : 0); +- +- return err; +-} +- +-static int cpcap_voice_set_tdm_slot(struct snd_soc_dai *dai, +- unsigned int tx_mask, unsigned int rx_mask, +- int slots, int slot_width) +-{ +- struct snd_soc_component *component = dai->component; +- struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); +- int err, ts_mask, mask; +- bool voice_call; +- +- /* +- * Primitive test for voice call, probably needs more checks +- * later on for 16-bit calls detected, Bluetooth headset etc. +- */ +- if (tx_mask == 0 && rx_mask == 1 && slot_width == 8) +- voice_call = true; +- else +- voice_call = false; +- +- ts_mask = 0x7 << CPCAP_BIT_MIC2_TIMESLOT0; +- ts_mask |= 0x7 << CPCAP_BIT_MIC1_RX_TIMESLOT0; +- +- mask = (tx_mask & 0x7) << CPCAP_BIT_MIC2_TIMESLOT0; +- mask |= (rx_mask & 0x7) << CPCAP_BIT_MIC1_RX_TIMESLOT0; +- +- err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI, +- ts_mask, mask); +- if (err) +- return err; +- +- err = cpcap_set_samprate(cpcap, CPCAP_DAI_VOICE, slot_width * 1000); +- if (err) +- return err; +- +- err = cpcap_voice_call(cpcap, dai, voice_call); +- if (err) +- return err; +- +- return 0; +-} +- +-static int cpcap_voice_set_mute(struct snd_soc_dai *dai, int mute, int direction) ++static int cpcap_voice_set_mute(struct snd_soc_dai *dai, ++ int mute, int direction) + { + struct snd_soc_component *component = dai->component; + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); +@@ -1526,7 +1405,6 @@ static const struct snd_soc_dai_ops cpcap_dai_voice_ops = { + .hw_params = cpcap_voice_hw_params, + .set_sysclk = cpcap_voice_set_dai_sysclk, + .set_fmt = cpcap_voice_set_dai_fmt, +- .set_tdm_slot = cpcap_voice_set_tdm_slot, + .mute_stream = cpcap_voice_set_mute, + .no_capture_mute = 1, + }; +diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c +--- a/sound/soc/codecs/cros_ec_codec.c ++++ b/sound/soc/codecs/cros_ec_codec.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -319,10 +320,18 @@ static int i2s_rx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) + (uint8_t *)&p, sizeof(p), NULL, 0); + } + ++static const u64 i2s_rx_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF; ++ + static const struct snd_soc_dai_ops i2s_rx_dai_ops = { + .hw_params = i2s_rx_hw_params, + .set_fmt = i2s_rx_set_fmt, + .set_bclk_ratio = i2s_rx_set_bclk_ratio, ++ .auto_selectable_formats = &i2s_rx_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static int i2s_rx_event(struct snd_soc_dapm_widget *w, +@@ -608,10 +617,10 @@ static void wov_copy_work(struct work_struct *w) + container_of(w, struct cros_ec_codec_priv, wov_copy_work.work); + int ret; + +- mutex_lock(&priv->wov_dma_lock); ++ guard(mutex)(&priv->wov_dma_lock); + if (!priv->wov_substream) { + dev_warn(priv->dev, "no pcm substream\n"); +- goto leave; ++ return; + } + + if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_AUDIO_SHM)) +@@ -624,8 +633,6 @@ static void wov_copy_work(struct work_struct *w) + msecs_to_jiffies(10)); + else if (ret) + dev_err(priv->dev, "failed to read audio data\n"); +-leave: +- mutex_unlock(&priv->wov_dma_lock); + } + + static int wov_enable_get(struct snd_kcontrol *kcontrol, +@@ -895,12 +902,11 @@ static int wov_pcm_hw_params(struct snd_soc_component *component, + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + +- mutex_lock(&priv->wov_dma_lock); ++ guard(mutex)(&priv->wov_dma_lock); + priv->wov_substream = substream; + priv->wov_rp = priv->wov_wp = 0; + priv->wov_dma_offset = 0; + priv->wov_burst_read = true; +- mutex_unlock(&priv->wov_dma_lock); + + return 0; + } +@@ -911,10 +917,10 @@ static int wov_pcm_hw_free(struct snd_soc_component *component, + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + +- mutex_lock(&priv->wov_dma_lock); +- wov_queue_dequeue(priv, wov_queue_size(priv)); +- priv->wov_substream = NULL; +- mutex_unlock(&priv->wov_dma_lock); ++ scoped_guard(mutex, &priv->wov_dma_lock) { ++ wov_queue_dequeue(priv, wov_queue_size(priv)); ++ priv->wov_substream = NULL; ++ } + + cancel_delayed_work_sync(&priv->wov_copy_work); + +diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c +--- a/sound/soc/codecs/cs-amp-lib.c ++++ b/sound/soc/codecs/cs-amp-lib.c +@@ -7,6 +7,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -83,10 +84,12 @@ static int cs_amp_write_cal_coeff(struct cs_dsp *dsp, + KUNIT_STATIC_STUB_REDIRECT(cs_amp_write_cal_coeff, dsp, controls, ctl_name, val); + + if (IS_REACHABLE(CONFIG_FW_CS_DSP)) { +- mutex_lock(&dsp->pwr_lock); +- cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, controls->mem_region, controls->alg_id); +- ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, &beval, sizeof(beval)); +- mutex_unlock(&dsp->pwr_lock); ++ scoped_guard(mutex, &dsp->pwr_lock) { ++ cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, ++ controls->mem_region, ++ controls->alg_id); ++ ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, &beval, sizeof(beval)); ++ } + + if (ret < 0) { + dev_err(dsp->dev, "Failed to write to '%s': %d\n", ctl_name, ret); +diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c +--- a/sound/soc/codecs/cs35l32.c ++++ b/sound/soc/codecs/cs35l32.c +@@ -538,7 +538,15 @@ static int cs35l32_runtime_resume(struct device *dev) + gpiod_set_value_cansleep(cs35l32->reset_gpio, 1); + + regcache_cache_only(cs35l32->regmap, false); +- regcache_sync(cs35l32->regmap); ++ ret = regcache_sync(cs35l32->regmap); ++ if (ret) { ++ regcache_cache_only(cs35l32->regmap, true); ++ regcache_mark_dirty(cs35l32->regmap); ++ gpiod_set_value_cansleep(cs35l32->reset_gpio, 0); ++ regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies), ++ cs35l32->supplies); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c +--- a/sound/soc/codecs/cs35l56-sdw.c ++++ b/sound/soc/codecs/cs35l56-sdw.c +@@ -231,7 +231,7 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral) + * a soft reset. + */ + if (cs35l56->base.init_done) +- cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); ++ cs35l56_unmask_soundwire_interrupts(cs35l56); + + out: + pm_runtime_put_autosuspend(cs35l56->base.dev); +@@ -240,47 +240,17 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral) + static int cs35l56_sdw_interrupt(struct sdw_slave *peripheral, + struct sdw_slave_intr_status *status) + { +- struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); +- +- /* SoundWire core holds our pm_runtime when calling this function. */ +- +- dev_dbg(cs35l56->base.dev, "int control_port=%#x\n", status->control_port); +- +- if ((status->control_port & SDW_SCP_INT1_IMPL_DEF) == 0) +- return 0; +- + /* +- * Prevent bus manager suspending and possibly issuing a +- * bus-reset before the queued work has run. ++ * The IRQ itself was handled through the regmap_irq handler, this is ++ * just clearing up the additional Cirrus SoundWire registers that are ++ * not covered by the SoundWire framework or the IRQ handler itself. + */ +- pm_runtime_get_noresume(cs35l56->base.dev); +- +- /* +- * Mask and clear until it has been handled. +- * None of the interrupts are time-critical so use the +- * power-efficient queue. +- */ +- cs35l56_mask_soundwire_interrupts(peripheral); +- queue_work(system_power_efficient_wq, &cs35l56->sdw_irq_work); ++ sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1); ++ sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); + + return 0; + } + +-static void cs35l56_sdw_irq_work(struct work_struct *work) +-{ +- struct cs35l56_private *cs35l56 = container_of(work, +- struct cs35l56_private, +- sdw_irq_work); +- +- cs35l56_irq(-1, &cs35l56->base); +- +- /* unmask interrupts */ +- if (!cs35l56->sdw_irq_no_unmask) +- cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); +- +- pm_runtime_put_autosuspend(cs35l56->base.dev); +-} +- + static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral) + { + struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); +@@ -302,6 +272,7 @@ static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral) + prop->source_ports = BIT(CS35L56_SDW1_CAPTURE_PORT); + prop->sink_ports = BIT(CS35L56_SDW1_PLAYBACK_PORT); + prop->paging_support = true; ++ prop->use_domain_irq = true; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY | SDW_SCP_INT1_IMPL_DEF; + +@@ -406,7 +377,7 @@ static int __maybe_unused cs35l56_sdw_runtime_resume(struct device *dev) + if (ret) + return ret; + +- cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); ++ cs35l56_unmask_soundwire_interrupts(cs35l56); + + return 0; + } +@@ -418,21 +389,12 @@ static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev) + if (!cs35l56->base.init_done) + return 0; + +- cs35l56_disable_sdw_interrupts(cs35l56); ++ /* runtime_resume unmasks the interrupt */ ++ cs35l56_mask_soundwire_interrupts(cs35l56); + + return cs35l56_system_suspend(dev); + } + +-static int __maybe_unused cs35l56_sdw_system_resume(struct device *dev) +-{ +- struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); +- +- cs35l56->sdw_irq_no_unmask = false; +- /* runtime_resume re-enables the interrupt */ +- +- return cs35l56_system_resume(dev); +-} +- + static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id) + { + struct device *dev = &peripheral->dev; +@@ -447,7 +409,6 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi + cs35l56->base.dev = dev; + cs35l56->sdw_peripheral = peripheral; + cs35l56->sdw_link_num = peripheral->bus->link_id; +- INIT_WORK(&cs35l56->sdw_irq_work, cs35l56_sdw_irq_work); + + dev_set_drvdata(dev, cs35l56); + +@@ -457,6 +418,7 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi + regmap_config = &cs35l56_regmap_sdw; + break; + case 0x3563: ++ case 0x3562: + regmap_config = &cs35l63_regmap_sdw; + break; + default: +@@ -483,21 +445,21 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi + /* Start in cache-only until device is enumerated */ + regcache_cache_only(cs35l56->base.regmap, true); + +- return cs35l56_common_probe(cs35l56, -EINVAL); ++ return cs35l56_common_probe(cs35l56, peripheral->irq); + } + + static void cs35l56_sdw_remove(struct sdw_slave *peripheral) + { + struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); + +- cs35l56_disable_sdw_interrupts(cs35l56); ++ cs35l56_mask_soundwire_interrupts(cs35l56); + + cs35l56_remove(cs35l56); + } + + static const struct dev_pm_ops cs35l56_sdw_pm = { + SET_RUNTIME_PM_OPS(cs35l56_sdw_runtime_suspend, cs35l56_sdw_runtime_resume, NULL) +- SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_sdw_system_resume) ++ SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_system_resume) + LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early) + /* NOIRQ stage not needed, SoundWire doesn't use a hard IRQ */ + }; +@@ -505,6 +467,7 @@ static const struct dev_pm_ops cs35l56_sdw_pm = { + static const struct sdw_device_id cs35l56_sdw_id[] = { + SDW_SLAVE_ENTRY(0x01FA, 0x3556, 0x3556), + SDW_SLAVE_ENTRY(0x01FA, 0x3557, 0x3557), ++ SDW_SLAVE_ENTRY(0x01FA, 0x3562, 0x3562), + SDW_SLAVE_ENTRY(0x01FA, 0x3563, 0x3563), + {}, + }; +diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c +--- a/sound/soc/codecs/cs35l56-shared.c ++++ b/sound/soc/codecs/cs35l56-shared.c +@@ -96,6 +96,7 @@ int cs35l56_set_patch(struct cs35l56_base *cs35l56_base) + ARRAY_SIZE(cs35l56_patch_fw)); + break; + case 0x63: ++ case 0x62: + ret = regmap_register_patch(cs35l56_base->regmap, cs35l63_patch_fw, + ARRAY_SIZE(cs35l63_patch_fw)); + break; +@@ -389,6 +390,7 @@ static void cs35l56_set_fw_reg_table(struct cs35l56_base *cs35l56_base) + } + break; + case 0x63: ++ case 0x62: + cs35l56_base->fw_reg = &cs35l63_fw_reg; + break; + } +@@ -595,6 +597,7 @@ void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire) + } + break; + case 0x63: ++ case 0x62: + regmap_multi_reg_write_bypassed(cs35l56_base->regmap, + cs35l63_system_reset_seq, + ARRAY_SIZE(cs35l63_system_reset_seq)); +@@ -613,26 +616,7 @@ void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire) + } + EXPORT_SYMBOL_NS_GPL(cs35l56_system_reset, "SND_SOC_CS35L56_SHARED"); + +-int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq) +-{ +- int ret; +- +- if (irq < 1) +- return 0; +- +- ret = devm_request_threaded_irq(cs35l56_base->dev, irq, NULL, cs35l56_irq, +- IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, +- "cs35l56", cs35l56_base); +- if (!ret) +- cs35l56_base->irq = irq; +- else +- dev_err(cs35l56_base->dev, "Failed to get IRQ: %d\n", ret); +- +- return ret; +-} +-EXPORT_SYMBOL_NS_GPL(cs35l56_irq_request, "SND_SOC_CS35L56_SHARED"); +- +-irqreturn_t cs35l56_irq(int irq, void *data) ++static irqreturn_t cs35l56_irq(int irq, void *data) + { + struct cs35l56_base *cs35l56_base = data; + unsigned int status1 = 0, status8 = 0, status20 = 0; +@@ -640,23 +624,22 @@ irqreturn_t cs35l56_irq(int irq, void *data) + unsigned int val; + int rv; + +- irqreturn_t ret = IRQ_NONE; +- + if (!cs35l56_base->init_done) + return IRQ_NONE; + +- mutex_lock(&cs35l56_base->irq_lock); ++ guard(mutex)(&cs35l56_base->irq_lock); + +- rv = pm_runtime_resume_and_get(cs35l56_base->dev); ++ PM_RUNTIME_ACQUIRE_IF_ENABLED(cs35l56_base->dev, pm); ++ rv = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (rv < 0) { + dev_err(cs35l56_base->dev, "irq: failed to get pm_runtime: %d\n", rv); +- goto err_unlock; ++ return IRQ_NONE; + } + + regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_STATUS, &val); + if ((val & CS35L56_IRQ1_STS_MASK) == 0) { + dev_dbg(cs35l56_base->dev, "Spurious IRQ: no pending interrupt\n"); +- goto err; ++ return IRQ_NONE; + } + + /* Ack interrupts */ +@@ -680,7 +663,7 @@ irqreturn_t cs35l56_irq(int irq, void *data) + + /* Check to see if unmasked bits are active */ + if (!status1 && !status8 && !status20) +- goto err; ++ return IRQ_NONE; + + if (status1 & CS35L56_AMP_SHORT_ERR_EINT1_MASK) + dev_crit(cs35l56_base->dev, "Amp short error\n"); +@@ -688,16 +671,27 @@ irqreturn_t cs35l56_irq(int irq, void *data) + if (status8 & CS35L56_TEMP_ERR_EINT1_MASK) + dev_crit(cs35l56_base->dev, "Overtemp error\n"); + +- ret = IRQ_HANDLED; ++ return IRQ_HANDLED; ++} + +-err: +- pm_runtime_put(cs35l56_base->dev); +-err_unlock: +- mutex_unlock(&cs35l56_base->irq_lock); ++int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq) ++{ ++ int ret; ++ ++ if (irq < 1) ++ return 0; ++ ++ ret = devm_request_threaded_irq(cs35l56_base->dev, irq, NULL, cs35l56_irq, ++ IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, ++ "cs35l56", cs35l56_base); ++ if (!ret) ++ cs35l56_base->irq = irq; ++ else ++ dev_err(cs35l56_base->dev, "Failed to get IRQ: %d\n", ret); + + return ret; + } +-EXPORT_SYMBOL_NS_GPL(cs35l56_irq, "SND_SOC_CS35L56_SHARED"); ++EXPORT_SYMBOL_NS_GPL(cs35l56_irq_request, "SND_SOC_CS35L56_SHARED"); + + int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base) + { +@@ -816,14 +810,10 @@ int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_sou + if (!cs35l56_base->init_done) + return 0; + +- if (!cs35l56_base->can_hibernate) +- goto out_sync; +- +- /* Must be done before releasing cache-only */ +- if (!is_soundwire) ++ /* Hibernate wake must be done before releasing cache-only */ ++ if (cs35l56_base->can_hibernate && !is_soundwire) + cs35l56_issue_wake_event(cs35l56_base); + +-out_sync: + ret = cs35l56_wait_for_firmware_boot(cs35l56_base); + if (ret) { + dev_err(cs35l56_base->dev, "Hibernate wake failed: %d\n", ret); +@@ -1470,6 +1460,7 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) + cs35l56_base->calibration_controls = &cs35l56_calibration_controls; + break; + case 0x35A630: ++ case 0x35A620: + cs35l56_base->calibration_controls = &cs35l63_calibration_controls; + devid = devid >> 4; + break; +diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c +--- a/sound/soc/codecs/cs35l56.c ++++ b/sound/soc/codecs/cs35l56.c +@@ -18,11 +18,9 @@ + #include + #include + #include +-#include + #include + #include + #include +-#include + #include + #include + #include +@@ -39,55 +37,49 @@ + #include "wm_adsp.h" + #include "cs35l56.h" + +-/* +- * snd_soc_register_component() can call component_probe() on all instances +- * in a card, so deferred registration must be protected across all instances. +- */ +-static DEFINE_MUTEX(cs35l56_component_register_lock); +-static bool cs35l56_shutting_down; +- +-void cs35l56_mask_soundwire_interrupts(struct sdw_slave *peripheral) ++void cs35l56_mask_soundwire_interrupts(struct cs35l56_private *cs35l56) + { + /* ++ * Mask unconditionally. ++ * + * The read of GEN_INT_STAT_1 is required as per the SoundWire spec + * for interrupt status bits to clear. + * GEN_INT_MASK_1 masks the _inputs_ to GEN_INT_STAT1. + */ +- sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); +- sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1); +- sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); ++ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); ++ sdw_read_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1); ++ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); + } + EXPORT_SYMBOL_NS_GPL(cs35l56_mask_soundwire_interrupts, "SND_SOC_CS35L56_CORE"); + +-void cs35l56_unmask_soundwire_interrupts(struct sdw_slave *peripheral) ++void cs35l56_unmask_soundwire_interrupts(struct cs35l56_private *cs35l56) + { +- sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, CS35L56_SDW_INT_MASK_CODEC_IRQ); ++ if (!cs35l56->base.irq) ++ return; ++ ++ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, ++ CS35L56_SDW_INT_MASK_CODEC_IRQ); + } + EXPORT_SYMBOL_NS_GPL(cs35l56_unmask_soundwire_interrupts, "SND_SOC_CS35L56_CORE"); + +-void cs35l56_disable_sdw_interrupts(struct cs35l56_private *cs35l56) ++static void cs35l56_disable_sdw_interrupts(struct cs35l56_private *cs35l56) + { + if (!cs35l56->sdw_peripheral) + return; + +- cs35l56->sdw_irq_no_unmask = true; +- flush_work(&cs35l56->sdw_irq_work); +- +- /* Mask interrupts and flush in case sdw_irq_work was queued again */ +- cs35l56_mask_soundwire_interrupts(cs35l56->sdw_peripheral); +- flush_work(&cs35l56->sdw_irq_work); ++ cs35l56_mask_soundwire_interrupts(cs35l56); ++ if (cs35l56->base.irq) ++ disable_irq(cs35l56->base.irq); + } +-EXPORT_SYMBOL_NS_GPL(cs35l56_disable_sdw_interrupts, "SND_SOC_CS35L56_CORE"); + +-void cs35l56_enable_sdw_interrupts(struct cs35l56_private *cs35l56) ++static void cs35l56_enable_sdw_interrupts(struct cs35l56_private *cs35l56) + { +- if (!cs35l56->sdw_peripheral) ++ if (!cs35l56->sdw_peripheral || !cs35l56->base.irq) + return; + +- cs35l56->sdw_irq_no_unmask = false; +- cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); ++ enable_irq(cs35l56->base.irq); ++ cs35l56_unmask_soundwire_interrupts(cs35l56); + } +-EXPORT_SYMBOL_NS_GPL(cs35l56_enable_sdw_interrupts, "SND_SOC_CS35L56_CORE"); + + static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); +@@ -837,11 +829,7 @@ static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing + { + int ret; + +- /* +- * Disable SoundWire interrupts to prevent race with IRQ work. +- * Setting sdw_irq_no_unmask prevents the handler re-enabling +- * the SoundWire interrupt. +- */ ++ /* Disable SoundWire interrupts to prevent race with IRQ handler thread */ + cs35l56_disable_sdw_interrupts(cs35l56); + + ret = cs35l56_firmware_shutdown(&cs35l56->base); +@@ -1405,6 +1393,7 @@ static int _cs35l56_component_probe(struct snd_soc_component *component) + ARRAY_SIZE(cs35l56_controls)); + break; + case 0x63: ++ case 0x62: + ret = snd_soc_add_component_controls(component, cs35l63_controls, + ARRAY_SIZE(cs35l63_controls)); + break; +@@ -1968,11 +1957,6 @@ static void cs35l56_component_register_work(struct work_struct *work) + component_register_work); + int ret; + +- guard(mutex)(&cs35l56_component_register_lock); +- +- if (cs35l56_shutting_down) +- return; +- + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(cs35l56->base.dev, pm_err); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm_err); + if (ret) { +@@ -2233,37 +2217,6 @@ EXPORT_NS_GPL_DEV_PM_OPS(cs35l56_pm_ops_i2c_spi, SND_SOC_CS35L56_CORE) = { + }; + #endif + +-static int cs35l56_reboot_notify(struct notifier_block *nb, +- unsigned long action, void *data) +-{ +- guard(mutex)(&cs35l56_component_register_lock); +- cs35l56_shutting_down = true; +- +- return NOTIFY_DONE; +-} +- +-static struct notifier_block cs35l56_reboot_notifier = { +- .notifier_call = cs35l56_reboot_notify, +-}; +- +-static int __init cs35l56_modinit(void) +-{ +- /* +- * Use reboot notifier to prevent race between shutdown and +- * snd_soc_register_component(). Driver shutdown() callback would +- * run too late, after device_shutdown() is already walking the +- * device list that component registration can modify. +- */ +- return register_reboot_notifier(&cs35l56_reboot_notifier); +-} +-module_init(cs35l56_modinit); +- +-static void __exit cs35l56_modexit(void) +-{ +- unregister_reboot_notifier(&cs35l56_reboot_notifier); +-} +-module_exit(cs35l56_modexit); +- + MODULE_DESCRIPTION("ASoC CS35L56 driver"); + MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED"); + MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB"); +diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h +--- a/sound/soc/codecs/cs35l56.h ++++ b/sound/soc/codecs/cs35l56.h +@@ -32,7 +32,6 @@ struct sdw_slave; + struct cs35l56_private { + struct wm_adsp dsp; /* must be first member */ + struct cs35l56_base base; +- struct work_struct component_register_work; + struct work_struct dsp_work; + struct workqueue_struct *dsp_wq; + struct snd_soc_component *component; +@@ -40,11 +39,8 @@ struct cs35l56_private { + struct sdw_slave *sdw_peripheral; + struct regmap *sdw_bus_regmap; + const char *fallback_fw_suffix; +- struct work_struct sdw_irq_work; +- bool sdw_irq_no_unmask; + bool soft_resetting; + bool sdw_attached; +- bool component_registered; + struct completion init_completion; + + int speaker_id; +@@ -67,10 +63,8 @@ static inline struct cs35l56_private *cs35l56_private_from_base(struct cs35l56_b + + extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi; + +-void cs35l56_mask_soundwire_interrupts(struct sdw_slave *peripheral); +-void cs35l56_unmask_soundwire_interrupts(struct sdw_slave *peripheral); +-void cs35l56_disable_sdw_interrupts(struct cs35l56_private *cs35l56); +-void cs35l56_enable_sdw_interrupts(struct cs35l56_private *cs35l56); ++void cs35l56_mask_soundwire_interrupts(struct cs35l56_private *cs35l56); ++void cs35l56_unmask_soundwire_interrupts(struct cs35l56_private *cs35l56); + + int cs35l56_system_suspend(struct device *dev); + int cs35l56_system_suspend_late(struct device *dev); +@@ -78,7 +72,6 @@ int cs35l56_system_suspend_no_irq(struct device *dev); + int cs35l56_system_resume_no_irq(struct device *dev); + int cs35l56_system_resume_early(struct device *dev); + int cs35l56_system_resume(struct device *dev); +-irqreturn_t cs35l56_irq(int irq, void *data); + int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq); + int cs35l56_common_probe(struct cs35l56_private *cs35l56, int irq); + int cs35l56_init(struct cs35l56_private *cs35l56); +diff --git a/sound/soc/codecs/cs40l50-codec.c b/sound/soc/codecs/cs40l50-codec.c +--- a/sound/soc/codecs/cs40l50-codec.c ++++ b/sound/soc/codecs/cs40l50-codec.c +@@ -288,8 +288,8 @@ static int cs40l50_codec_driver_probe(struct platform_device *pdev) + } + + static const struct platform_device_id cs40l50_id[] = { +- { "cs40l50-codec", }, +- {} ++ { .name = "cs40l50-codec" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, cs40l50_id); + +diff --git a/sound/soc/codecs/cs42l42-sdw.c b/sound/soc/codecs/cs42l42-sdw.c +--- a/sound/soc/codecs/cs42l42-sdw.c ++++ b/sound/soc/codecs/cs42l42-sdw.c +@@ -481,10 +481,20 @@ static int cs42l42_sdw_runtime_resume(struct device *dev) + regcache_cache_only(cs42l42->regmap, false); + + /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */ +- regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); +- regcache_sync(cs42l42->regmap); ++ ret = regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); ++ if (ret) ++ goto err_sync; ++ ++ ret = regcache_sync(cs42l42->regmap); ++ if (ret) ++ goto err_sync; + + return 0; ++ ++err_sync: ++ regcache_cache_only(cs42l42->regmap, true); ++ regcache_mark_dirty(cs42l42->regmap); ++ return ret; + } + + static int cs42l42_sdw_resume(struct device *dev) +diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c +--- a/sound/soc/codecs/cs42l42.c ++++ b/sound/soc/codecs/cs42l42.c +@@ -9,6 +9,7 @@ + * Author: Michael White + */ + ++#include + #include + #include + #include +@@ -565,7 +566,7 @@ static int cs42l42_set_jack(struct snd_soc_component *component, struct snd_soc_ + struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); + + /* Prevent race with interrupt handler */ +- mutex_lock(&cs42l42->irq_lock); ++ guard(mutex)(&cs42l42->irq_lock); + cs42l42->jack = jk; + + if (jk) { +@@ -581,7 +582,6 @@ static int cs42l42_set_jack(struct snd_soc_component *component, struct snd_soc_ + break; + } + } +- mutex_unlock(&cs42l42->irq_lock); + + return 0; + } +@@ -1668,13 +1668,10 @@ irqreturn_t cs42l42_irq_thread(int irq, void *data) + unsigned int current_button_status; + unsigned int i; + +- pm_runtime_get_sync(cs42l42->dev); +- mutex_lock(&cs42l42->irq_lock); +- if (cs42l42->suspended || !cs42l42->init_done) { +- mutex_unlock(&cs42l42->irq_lock); +- pm_runtime_put_autosuspend(cs42l42->dev); ++ guard(pm_runtime_active_auto)(cs42l42->dev); ++ guard(mutex)(&cs42l42->irq_lock); ++ if (cs42l42->suspended || !cs42l42->init_done) + return IRQ_NONE; +- } + + /* Read sticky registers to clear interurpt */ + for (i = 0; i < ARRAY_SIZE(stickies); i++) { +@@ -1774,9 +1771,6 @@ irqreturn_t cs42l42_irq_thread(int irq, void *data) + } + } + +- mutex_unlock(&cs42l42->irq_lock); +- pm_runtime_put_autosuspend(cs42l42->dev); +- + return IRQ_HANDLED; + } + EXPORT_SYMBOL_NS_GPL(cs42l42_irq_thread, "SND_SOC_CS42L42_CORE"); +@@ -2163,23 +2157,23 @@ int cs42l42_suspend(struct device *dev) + * future interrupts. This ensures a safe disable if the interrupt + * is shared. + */ +- mutex_lock(&cs42l42->irq_lock); +- cs42l42->suspended = true; ++ scoped_guard(mutex, &cs42l42->irq_lock) { ++ cs42l42->suspended = true; + +- /* Save register values that will be overwritten by shutdown sequence */ +- for (i = 0; i < ARRAY_SIZE(cs42l42_shutdown_seq); ++i) { +- regmap_read(cs42l42->regmap, cs42l42_shutdown_seq[i].reg, ®); +- save_regs[i] = (u8)reg; ++ /* Save register values that will be overwritten by shutdown sequence */ ++ for (i = 0; i < ARRAY_SIZE(cs42l42_shutdown_seq); ++i) { ++ regmap_read(cs42l42->regmap, cs42l42_shutdown_seq[i].reg, ®); ++ save_regs[i] = (u8)reg; ++ } ++ ++ /* Shutdown codec */ ++ regmap_multi_reg_write(cs42l42->regmap, ++ cs42l42_shutdown_seq, ++ ARRAY_SIZE(cs42l42_shutdown_seq)); ++ ++ /* All interrupt sources are now disabled */ + } + +- /* Shutdown codec */ +- regmap_multi_reg_write(cs42l42->regmap, +- cs42l42_shutdown_seq, +- ARRAY_SIZE(cs42l42_shutdown_seq)); +- +- /* All interrupt sources are now disabled */ +- mutex_unlock(&cs42l42->irq_lock); +- + /* Wait for power-down complete */ + msleep(CS42L42_PDN_DONE_TIME_MS); + ret = regmap_read_poll_timeout(cs42l42->regmap, +@@ -2250,13 +2244,13 @@ void cs42l42_resume_restore(struct device *dev) + regcache_cache_only(cs42l42->regmap, false); + regcache_mark_dirty(cs42l42->regmap); + +- mutex_lock(&cs42l42->irq_lock); +- /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */ +- regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); +- regcache_sync(cs42l42->regmap); ++ scoped_guard(mutex, &cs42l42->irq_lock) { ++ /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */ ++ regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); ++ regcache_sync(cs42l42->regmap); + +- cs42l42->suspended = false; +- mutex_unlock(&cs42l42->irq_lock); ++ cs42l42->suspended = false; ++ } + + dev_dbg(dev, "System resumed\n"); + } +diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c +--- a/sound/soc/codecs/cs42l43-jack.c ++++ b/sound/soc/codecs/cs42l43-jack.c +@@ -6,6 +6,7 @@ + // Cirrus Logic International Semiconductor Ltd. + + #include ++#include + #include + #include + #include +@@ -67,6 +68,21 @@ static int cs42l43_find_index(struct cs42l43_codec *priv, const char * const pro + return -EINVAL; + } + ++static void cs42l43_apply_accdet_config(struct cs42l43_codec *priv, ++ unsigned int autocontrol, ++ unsigned int pdncntl) ++{ ++ struct cs42l43 *cs42l43 = priv->core; ++ ++ regmap_update_bits(cs42l43->regmap, CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, ++ CS42L43_JACKDET_MODE_MASK | CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK | ++ CS42L43_HSBIAS_SENSE_TRIP_MASK, autocontrol); ++ regmap_update_bits(cs42l43->regmap, CS42L43_PDNCNTL, ++ CS42L43_RING_SENSE_EN_MASK, pdncntl); ++ ++ dev_dbg(priv->dev, "Successfully configured accessory detect\n"); ++} ++ + int cs42l43_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *d) + { +@@ -80,31 +96,34 @@ int cs42l43_set_jack(struct snd_soc_component *component, + + dev_dbg(priv->dev, "Configure accessory detect\n"); + +- ret = pm_runtime_resume_and_get(priv->dev); ++ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(priv->dev, pm); ++ ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) { + dev_err(priv->dev, "Failed to resume for jack config: %d\n", ret); + return ret; + } + +- mutex_lock(&priv->jack_lock); ++ guard(mutex)(&priv->jack_lock); + + priv->jack_hp = jack; + +- if (!jack) +- goto done; ++ if (!jack) { ++ cs42l43_apply_accdet_config(priv, autocontrol, pdncntl); ++ return 0; ++ } + + ret = device_property_count_u32(cs42l43->dev, "cirrus,buttons-ohms"); + if (ret != -EINVAL) { + if (ret < 0) { + dev_err(priv->dev, "Property cirrus,buttons-ohms malformed: %d\n", + ret); +- goto error; ++ return ret; + } + + if (ret > CS42L43_N_BUTTONS) { + ret = -EINVAL; + dev_err(priv->dev, "Property cirrus,buttons-ohms too many entries\n"); +- goto error; ++ return ret; + } + + ret = device_property_read_u32_array(cs42l43->dev, "cirrus,buttons-ohms", +@@ -112,7 +131,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, + if (ret < 0) { + dev_err(priv->dev, "Property cirrus,button-ohms malformed: %d\n", + ret); +- goto error; ++ return ret; + } + } else { + priv->buttons[0] = 70; +@@ -124,7 +143,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, + ret = cs42l43_find_index(priv, "cirrus,detect-us", 50000, &priv->detect_us, + cs42l43_accdet_us, ARRAY_SIZE(cs42l43_accdet_us)); + if (ret < 0) +- goto error; ++ return ret; + + hs2 |= ret << CS42L43_AUTO_HSDET_TIME_SHIFT; + +@@ -134,7 +153,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, + &priv->bias_ramp_ms, cs42l43_accdet_ramp_ms, + ARRAY_SIZE(cs42l43_accdet_ramp_ms)); + if (ret < 0) +- goto error; ++ return ret; + + hs2 |= ret << CS42L43_HSBIAS_RAMP_SHIFT; + +@@ -142,7 +161,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, + &priv->bias_sense_ua, cs42l43_accdet_bias_sense, + ARRAY_SIZE(cs42l43_accdet_bias_sense)); + if (ret < 0) +- goto error; ++ return ret; + + if (priv->bias_sense_ua) + autocontrol |= ret << CS42L43_HSBIAS_SENSE_TRIP_SHIFT; +@@ -154,7 +173,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, + &priv->tip_debounce_ms); + if (ret < 0 && ret != -EINVAL) { + dev_err(priv->dev, "Property cirrus,tip-debounce-ms malformed: %d\n", ret); +- goto error; ++ return ret; + } + + /* This tip sense invert is set normally, as TIPSENSE_INV already inverted */ +@@ -170,7 +189,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, + &priv->tip_fall_db_ms, cs42l43_accdet_db_ms, + ARRAY_SIZE(cs42l43_accdet_db_ms)); + if (ret < 0) +- goto error; ++ return ret; + + tip_deb |= ret << CS42L43_TIPSENSE_FALLING_DB_TIME_SHIFT; + +@@ -178,7 +197,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, + &priv->tip_rise_db_ms, cs42l43_accdet_db_ms, + ARRAY_SIZE(cs42l43_accdet_db_ms)); + if (ret < 0) +- goto error; ++ return ret; + + tip_deb |= ret << CS42L43_TIPSENSE_RISING_DB_TIME_SHIFT; + +@@ -199,7 +218,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, + NULL, cs42l43_accdet_db_ms, + ARRAY_SIZE(cs42l43_accdet_db_ms)); + if (ret < 0) +- goto error; ++ return ret; + + ring_deb |= ret << CS42L43_RINGSENSE_FALLING_DB_TIME_SHIFT; + +@@ -207,7 +226,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, + NULL, cs42l43_accdet_db_ms, + ARRAY_SIZE(cs42l43_accdet_db_ms)); + if (ret < 0) +- goto error; ++ return ret; + + ring_deb |= ret << CS42L43_RINGSENSE_RISING_DB_TIME_SHIFT; + pdncntl |= CS42L43_RING_SENSE_EN_MASK; +@@ -228,23 +247,9 @@ int cs42l43_set_jack(struct snd_soc_component *component, + CS42L43_HSBIAS_RAMP_MASK | CS42L43_HSDET_MODE_MASK | + CS42L43_AUTO_HSDET_TIME_MASK, hs2); + +-done: +- ret = 0; ++ cs42l43_apply_accdet_config(priv, autocontrol, pdncntl); + +- regmap_update_bits(cs42l43->regmap, CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, +- CS42L43_JACKDET_MODE_MASK | CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK | +- CS42L43_HSBIAS_SENSE_TRIP_MASK, autocontrol); +- regmap_update_bits(cs42l43->regmap, CS42L43_PDNCNTL, +- CS42L43_RING_SENSE_EN_MASK, pdncntl); +- +- dev_dbg(priv->dev, "Successfully configured accessory detect\n"); +- +-error: +- mutex_unlock(&priv->jack_lock); +- +- pm_runtime_put_autosuspend(priv->dev); +- +- return ret; ++ return 0; + } + + static void cs42l43_start_hs_bias(struct cs42l43_codec *priv, bool type_detect) +@@ -370,22 +375,22 @@ irqreturn_t cs42l43_button_press(int irq, void *data) + { + struct cs42l43_codec *priv = data; + struct cs42l43 *cs42l43 = priv->core; +- irqreturn_t iret = IRQ_NONE; + unsigned int buttons = 0; + unsigned int val = 0; + int i, ret; + +- ret = pm_runtime_resume_and_get(priv->dev); ++ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(priv->dev, pm); ++ ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) { + dev_err(priv->dev, "Failed to resume for button press: %d\n", ret); +- return iret; ++ return IRQ_NONE; + } + +- mutex_lock(&priv->jack_lock); ++ guard(mutex)(&priv->jack_lock); + + if (!priv->button_detect_running) { + dev_dbg(priv->dev, "Spurious button press IRQ\n"); +- goto error; ++ return IRQ_NONE; + } + + // Wait for 2 full cycles of comb filter to ensure good reading +@@ -396,12 +401,12 @@ irqreturn_t cs42l43_button_press(int irq, void *data) + /* Bail if jack removed, the button is irrelevant and likely invalid */ + if (!cs42l43_jack_present(priv)) { + dev_dbg(priv->dev, "Button ignored due to removal\n"); +- goto error; ++ return IRQ_NONE; + } + + if (val & CS42L43_HSBIAS_CLAMP_STS_MASK) { + dev_dbg(priv->dev, "Button ignored due to bias sense\n"); +- goto error; ++ return IRQ_NONE; + } + + val = (val & CS42L43_HSDET_DC_STS_MASK) >> CS42L43_HSDET_DC_STS_SHIFT; +@@ -424,45 +429,32 @@ irqreturn_t cs42l43_button_press(int irq, void *data) + + snd_soc_jack_report(priv->jack_hp, buttons, CS42L43_JACK_BUTTONS); + +- iret = IRQ_HANDLED; +- +-error: +- mutex_unlock(&priv->jack_lock); +- +- pm_runtime_put_autosuspend(priv->dev); +- +- return iret; ++ return IRQ_HANDLED; + } + + irqreturn_t cs42l43_button_release(int irq, void *data) + { + struct cs42l43_codec *priv = data; +- irqreturn_t iret = IRQ_NONE; + int ret; + +- ret = pm_runtime_resume_and_get(priv->dev); ++ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(priv->dev, pm); ++ ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) { + dev_err(priv->dev, "Failed to resume for button release: %d\n", ret); +- return iret; ++ return IRQ_NONE; + } + +- mutex_lock(&priv->jack_lock); ++ guard(mutex)(&priv->jack_lock); + +- if (priv->button_detect_running) { +- dev_dbg(priv->dev, "Button release IRQ\n"); +- +- snd_soc_jack_report(priv->jack_hp, 0, CS42L43_JACK_BUTTONS); +- +- iret = IRQ_HANDLED; +- } else { ++ if (!priv->button_detect_running) { + dev_dbg(priv->dev, "Spurious button release IRQ\n"); ++ return IRQ_NONE; + } + +- mutex_unlock(&priv->jack_lock); ++ dev_dbg(priv->dev, "Button release IRQ\n"); ++ snd_soc_jack_report(priv->jack_hp, 0, CS42L43_JACK_BUTTONS); + +- pm_runtime_put_autosuspend(priv->dev); +- +- return iret; ++ return IRQ_HANDLED; + } + + void cs42l43_bias_sense_timeout(struct work_struct *work) +@@ -472,13 +464,14 @@ void cs42l43_bias_sense_timeout(struct work_struct *work) + struct cs42l43 *cs42l43 = priv->core; + int ret; + +- ret = pm_runtime_resume_and_get(priv->dev); ++ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(priv->dev, pm); ++ ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) { + dev_err(priv->dev, "Failed to resume for bias sense: %d\n", ret); + return; + } + +- mutex_lock(&priv->jack_lock); ++ guard(mutex)(&priv->jack_lock); + + if (cs42l43_jack_present(priv) && priv->button_detect_running) { + dev_dbg(priv->dev, "Bias sense timeout out, restore bias\n"); +@@ -491,10 +484,6 @@ void cs42l43_bias_sense_timeout(struct work_struct *work) + CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, + CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK); + } +- +- mutex_unlock(&priv->jack_lock); +- +- pm_runtime_put_autosuspend(priv->dev); + } + + static const struct reg_sequence cs42l43_3pole_patch[] = { +@@ -896,9 +885,8 @@ int cs42l43_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + +- mutex_lock(&priv->jack_lock); ++ guard(mutex)(&priv->jack_lock); + ucontrol->value.integer.value[0] = priv->jack_override; +- mutex_unlock(&priv->jack_lock); + + return 0; + } +@@ -914,17 +902,13 @@ int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u + if (override >= e->items) + return -EINVAL; + +- mutex_lock(&priv->jack_lock); ++ guard(mutex)(&priv->jack_lock); + +- if (!cs42l43_jack_present(priv)) { +- mutex_unlock(&priv->jack_lock); ++ if (!cs42l43_jack_present(priv)) + return -EBUSY; +- } + +- if (override == priv->jack_override) { +- mutex_unlock(&priv->jack_lock); ++ if (override == priv->jack_override) + return 0; +- } + + priv->jack_override = override; + +@@ -984,7 +968,5 @@ int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u + cs42l43_jack_override_modes[override].report); + } + +- mutex_unlock(&priv->jack_lock); +- + return 1; + } +diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c +--- a/sound/soc/codecs/cs42l43.c ++++ b/sound/soc/codecs/cs42l43.c +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1207,14 +1208,12 @@ static void cs42l43_spk_vu_sync(struct cs42l43_codec *priv) + { + struct cs42l43 *cs42l43 = priv->core; + +- mutex_lock(&priv->spk_vu_lock); ++ guard(mutex)(&priv->spk_vu_lock); + + regmap_update_bits(cs42l43->regmap, CS42L43_INTP_VOLUME_CTRL1, + CS42L43_AMP1_2_VU_MASK, CS42L43_AMP1_2_VU_MASK); + regmap_update_bits(cs42l43->regmap, CS42L43_INTP_VOLUME_CTRL1, + CS42L43_AMP1_2_VU_MASK, 0); +- +- mutex_unlock(&priv->spk_vu_lock); + } + + static int cs42l43_shutter_get(struct cs42l43_codec *priv, unsigned int shift) +@@ -1601,7 +1600,7 @@ static int cs42l43_pll_ev(struct snd_soc_dapm_widget *w, + struct cs42l43 *cs42l43 = priv->core; + int ret; + +- mutex_lock(&cs42l43->pll_lock); ++ guard(mutex)(&cs42l43->pll_lock); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: +@@ -1626,8 +1625,6 @@ static int cs42l43_pll_ev(struct snd_soc_dapm_widget *w, + break; + } + +- mutex_unlock(&cs42l43->pll_lock); +- + return ret; + } + +@@ -2565,13 +2562,10 @@ static int cs42l43_set_sysclk(struct snd_soc_component *component, int clk_id, + { + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; +- int ret; + +- mutex_lock(&cs42l43->pll_lock); +- ret = cs42l43_set_pll(priv, src, freq); +- mutex_unlock(&cs42l43->pll_lock); ++ guard(mutex)(&cs42l43->pll_lock); + +- return ret; ++ return cs42l43_set_pll(priv, src, freq); + } + + static int cs42l43_component_probe(struct snd_soc_component *component) +@@ -2941,8 +2935,8 @@ static const struct dev_pm_ops cs42l43_codec_pm_ops = { + }; + + static const struct platform_device_id cs42l43_codec_id_table[] = { +- { "cs42l43-codec", }, +- {} ++ { .name = "cs42l43-codec" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, cs42l43_codec_id_table); + +diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c +--- a/sound/soc/codecs/cs42l84.c ++++ b/sound/soc/codecs/cs42l84.c +@@ -10,6 +10,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -281,10 +282,9 @@ static int cs42l84_set_jack(struct snd_soc_component *component, struct snd_soc_ + struct cs42l84_private *cs42l84 = snd_soc_component_get_drvdata(component); + + /* Prevent race with interrupt handler */ +- mutex_lock(&cs42l84->irq_lock); ++ guard(mutex)(&cs42l84->irq_lock); + cs42l84->jack = jk; + snd_soc_jack_report(jk, cs42l84->hs_type, SND_JACK_HEADSET); +- mutex_unlock(&cs42l84->irq_lock); + + return 0; + } +@@ -831,7 +831,7 @@ static irqreturn_t cs42l84_irq_thread(int irq, void *data) + u8 current_ring_state; + int i; + +- mutex_lock(&cs42l84->irq_lock); ++ guard(mutex)(&cs42l84->irq_lock); + /* Read sticky registers to clear interrupt */ + for (i = 0; i < ARRAY_SIZE(stickies); i++) { + regmap_read(cs42l84->regmap, irq_params_table[i].status_addr, +@@ -902,8 +902,6 @@ static irqreturn_t cs42l84_irq_thread(int irq, void *data) + break; + } + +- mutex_unlock(&cs42l84->irq_lock); +- + return IRQ_HANDLED; + } + +@@ -919,8 +917,6 @@ static irqreturn_t cs42l84_irq_thread(int irq, void *data) + } + } + +- mutex_unlock(&cs42l84->irq_lock); +- + return IRQ_HANDLED; + } + +diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c +--- a/sound/soc/codecs/cs43130.c ++++ b/sound/soc/codecs/cs43130.c +@@ -6,6 +6,7 @@ + * + * Authors: Li Xu + */ ++#include + #include + #include + #include +@@ -818,26 +819,26 @@ static int cs43130_dsd_hw_params(struct snd_pcm_substream *substream, + unsigned int required_clk; + u8 dsd_speed; + +- mutex_lock(&cs43130->clk_mutex); +- if (!cs43130->clk_req) { +- /* no DAI is currently using clk */ +- if (!(CS43130_MCLK_22M % params_rate(params))) +- required_clk = CS43130_MCLK_22M; +- else +- required_clk = CS43130_MCLK_24M; ++ scoped_guard(mutex, &cs43130->clk_mutex) { ++ if (!cs43130->clk_req) { ++ /* no DAI is currently using clk */ ++ if (!(CS43130_MCLK_22M % params_rate(params))) ++ required_clk = CS43130_MCLK_22M; ++ else ++ required_clk = CS43130_MCLK_24M; + +- cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); +- if (cs43130->pll_bypass) +- cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); +- else +- cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); ++ cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); ++ if (cs43130->pll_bypass) ++ cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); ++ else ++ cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); ++ } ++ ++ cs43130->clk_req++; ++ if (cs43130->clk_req == 2) ++ cs43130_pcm_dsd_mix(true, cs43130->regmap); + } + +- cs43130->clk_req++; +- if (cs43130->clk_req == 2) +- cs43130_pcm_dsd_mix(true, cs43130->regmap); +- mutex_unlock(&cs43130->clk_mutex); +- + switch (params_rate(params)) { + case 176400: + dsd_speed = 0; +@@ -881,26 +882,26 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream, + unsigned int required_clk; + u8 dsd_speed; + +- mutex_lock(&cs43130->clk_mutex); +- if (!cs43130->clk_req) { +- /* no DAI is currently using clk */ +- if (!(CS43130_MCLK_22M % params_rate(params))) +- required_clk = CS43130_MCLK_22M; +- else +- required_clk = CS43130_MCLK_24M; ++ scoped_guard(mutex, &cs43130->clk_mutex) { ++ if (!cs43130->clk_req) { ++ /* no DAI is currently using clk */ ++ if (!(CS43130_MCLK_22M % params_rate(params))) ++ required_clk = CS43130_MCLK_22M; ++ else ++ required_clk = CS43130_MCLK_24M; + +- cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); +- if (cs43130->pll_bypass) +- cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); +- else +- cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); ++ cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); ++ if (cs43130->pll_bypass) ++ cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); ++ else ++ cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); ++ } ++ ++ cs43130->clk_req++; ++ if (cs43130->clk_req == 2) ++ cs43130_pcm_dsd_mix(true, cs43130->regmap); + } + +- cs43130->clk_req++; +- if (cs43130->clk_req == 2) +- cs43130_pcm_dsd_mix(true, cs43130->regmap); +- mutex_unlock(&cs43130->clk_mutex); +- + switch (dai->id) { + case CS43130_ASP_DOP_DAI: + case CS43130_XSP_DOP_DAI: +@@ -988,14 +989,13 @@ static int cs43130_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_component *component = dai->component; + struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); + +- mutex_lock(&cs43130->clk_mutex); ++ guard(mutex)(&cs43130->clk_mutex); + cs43130->clk_req--; + if (!cs43130->clk_req) { + /* no DAI is currently using clk */ + cs43130_change_clksrc(component, CS43130_MCLK_SRC_RCO); + cs43130_pcm_dsd_mix(false, cs43130->regmap); + } +- mutex_unlock(&cs43130->clk_mutex); + + return 0; + } +diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c +--- a/sound/soc/codecs/cs4349.c ++++ b/sound/soc/codecs/cs4349.c +@@ -340,7 +340,13 @@ static int cs4349_runtime_resume(struct device *dev) + gpiod_set_value_cansleep(cs4349->reset_gpio, 1); + + regcache_cache_only(cs4349->regmap, false); +- regcache_sync(cs4349->regmap); ++ ret = regcache_sync(cs4349->regmap); ++ if (ret) { ++ regcache_cache_only(cs4349->regmap, true); ++ regcache_mark_dirty(cs4349->regmap); ++ gpiod_set_value_cansleep(cs4349->reset_gpio, 0); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/cs47l15.c b/sound/soc/codecs/cs47l15.c +--- a/sound/soc/codecs/cs47l15.c ++++ b/sound/soc/codecs/cs47l15.c +@@ -6,6 +6,7 @@ + // Cirrus Logic International Semiconductor Ltd. + // + ++#include + #include + #include + #include +@@ -1285,9 +1286,8 @@ static int cs47l15_component_probe(struct snd_soc_component *component) + + snd_soc_component_init_regmap(component, madera->regmap); + +- mutex_lock(&madera->dapm_ptr_lock); +- madera->dapm = snd_soc_component_to_dapm(component); +- mutex_unlock(&madera->dapm_ptr_lock); ++ scoped_guard(mutex, &madera->dapm_ptr_lock) ++ madera->dapm = snd_soc_component_to_dapm(component); + + ret = madera_init_inputs(component); + if (ret) +@@ -1317,9 +1317,8 @@ static void cs47l15_component_remove(struct snd_soc_component *component) + struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component); + struct madera *madera = cs47l15->core.madera; + +- mutex_lock(&madera->dapm_ptr_lock); +- madera->dapm = NULL; +- mutex_unlock(&madera->dapm_ptr_lock); ++ scoped_guard(mutex, &madera->dapm_ptr_lock) ++ madera->dapm = NULL; + + wm_adsp2_component_remove(&cs47l15->core.adsp[0], component); + } +diff --git a/sound/soc/codecs/cs47l35.c b/sound/soc/codecs/cs47l35.c +--- a/sound/soc/codecs/cs47l35.c ++++ b/sound/soc/codecs/cs47l35.c +@@ -6,6 +6,7 @@ + // Cirrus Logic International Semiconductor Ltd. + // + ++#include + #include + #include + #include +@@ -1566,9 +1567,8 @@ static int cs47l35_component_probe(struct snd_soc_component *component) + + snd_soc_component_init_regmap(component, madera->regmap); + +- mutex_lock(&madera->dapm_ptr_lock); +- madera->dapm = snd_soc_component_to_dapm(component); +- mutex_unlock(&madera->dapm_ptr_lock); ++ scoped_guard(mutex, &madera->dapm_ptr_lock) ++ madera->dapm = snd_soc_component_to_dapm(component); + + ret = madera_init_inputs(component); + if (ret) +@@ -1600,9 +1600,8 @@ static void cs47l35_component_remove(struct snd_soc_component *component) + struct madera *madera = cs47l35->core.madera; + int i; + +- mutex_lock(&madera->dapm_ptr_lock); +- madera->dapm = NULL; +- mutex_unlock(&madera->dapm_ptr_lock); ++ scoped_guard(mutex, &madera->dapm_ptr_lock) ++ madera->dapm = NULL; + + for (i = 0; i < CS47L35_NUM_ADSP; i++) + wm_adsp2_component_remove(&cs47l35->core.adsp[i], component); +diff --git a/sound/soc/codecs/cs47l85.c b/sound/soc/codecs/cs47l85.c +--- a/sound/soc/codecs/cs47l85.c ++++ b/sound/soc/codecs/cs47l85.c +@@ -6,6 +6,7 @@ + // Cirrus Logic International Semiconductor Ltd. + // + ++#include + #include + #include + #include +@@ -2504,9 +2505,8 @@ static int cs47l85_component_probe(struct snd_soc_component *component) + + snd_soc_component_init_regmap(component, madera->regmap); + +- mutex_lock(&madera->dapm_ptr_lock); +- madera->dapm = snd_soc_component_to_dapm(component); +- mutex_unlock(&madera->dapm_ptr_lock); ++ scoped_guard(mutex, &madera->dapm_ptr_lock) ++ madera->dapm = snd_soc_component_to_dapm(component); + + ret = madera_init_inputs(component); + if (ret) +@@ -2537,9 +2537,8 @@ static void cs47l85_component_remove(struct snd_soc_component *component) + struct madera *madera = cs47l85->core.madera; + int i; + +- mutex_lock(&madera->dapm_ptr_lock); +- madera->dapm = NULL; +- mutex_unlock(&madera->dapm_ptr_lock); ++ scoped_guard(mutex, &madera->dapm_ptr_lock) ++ madera->dapm = NULL; + + for (i = 0; i < CS47L85_NUM_ADSP; i++) + wm_adsp2_component_remove(&cs47l85->core.adsp[i], component); +diff --git a/sound/soc/codecs/cs47l90.c b/sound/soc/codecs/cs47l90.c +--- a/sound/soc/codecs/cs47l90.c ++++ b/sound/soc/codecs/cs47l90.c +@@ -6,6 +6,7 @@ + // Cirrus Logic International Semiconductor Ltd. + // + ++#include + #include + #include + #include +@@ -2423,9 +2424,8 @@ static int cs47l90_component_probe(struct snd_soc_component *component) + + snd_soc_component_init_regmap(component, madera->regmap); + +- mutex_lock(&madera->dapm_ptr_lock); +- madera->dapm = snd_soc_component_to_dapm(component); +- mutex_unlock(&madera->dapm_ptr_lock); ++ scoped_guard(mutex, &madera->dapm_ptr_lock) ++ madera->dapm = snd_soc_component_to_dapm(component); + + ret = madera_init_inputs(component); + if (ret) +@@ -2456,9 +2456,8 @@ static void cs47l90_component_remove(struct snd_soc_component *component) + struct madera *madera = cs47l90->core.madera; + int i; + +- mutex_lock(&madera->dapm_ptr_lock); +- madera->dapm = NULL; +- mutex_unlock(&madera->dapm_ptr_lock); ++ scoped_guard(mutex, &madera->dapm_ptr_lock) ++ madera->dapm = NULL; + + for (i = 0; i < CS47L90_NUM_ADSP; i++) + wm_adsp2_component_remove(&cs47l90->core.adsp[i], component); +diff --git a/sound/soc/codecs/cs47l92.c b/sound/soc/codecs/cs47l92.c +--- a/sound/soc/codecs/cs47l92.c ++++ b/sound/soc/codecs/cs47l92.c +@@ -6,6 +6,7 @@ + // Cirrus Logic International Semiconductor Ltd. + // + ++#include + #include + #include + #include +@@ -1892,9 +1893,8 @@ static int cs47l92_component_probe(struct snd_soc_component *component) + + snd_soc_component_init_regmap(component, madera->regmap); + +- mutex_lock(&madera->dapm_ptr_lock); +- madera->dapm = snd_soc_component_to_dapm(component); +- mutex_unlock(&madera->dapm_ptr_lock); ++ scoped_guard(mutex, &madera->dapm_ptr_lock) ++ madera->dapm = snd_soc_component_to_dapm(component); + + ret = madera_init_inputs(component); + if (ret) +@@ -1922,9 +1922,8 @@ static void cs47l92_component_remove(struct snd_soc_component *component) + struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component); + struct madera *madera = cs47l92->core.madera; + +- mutex_lock(&madera->dapm_ptr_lock); +- madera->dapm = NULL; +- mutex_unlock(&madera->dapm_ptr_lock); ++ scoped_guard(mutex, &madera->dapm_ptr_lock) ++ madera->dapm = NULL; + + wm_adsp2_component_remove(&cs47l92->core.adsp[0], component); + } +diff --git a/sound/soc/codecs/cs48l32.c b/sound/soc/codecs/cs48l32.c +--- a/sound/soc/codecs/cs48l32.c ++++ b/sound/soc/codecs/cs48l32.c +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -236,15 +237,13 @@ static int cs48l32_rate_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v + int ret; + + /* Prevent any mixer mux changes while we do this */ +- mutex_lock(&cs48l32_codec->rate_lock); ++ guard(mutex)(&cs48l32_codec->rate_lock); + + /* The write must be guarded by a number of SYSCLK cycles */ + cs48l32_spin_sysclk(cs48l32_codec); + ret = snd_soc_put_enum_double(kcontrol, ucontrol); + cs48l32_spin_sysclk(cs48l32_codec); + +- mutex_unlock(&cs48l32_codec->rate_lock); +- + return ret; + } + +@@ -2242,7 +2241,6 @@ static int cs48l32_dai_set_sysclk(struct snd_soc_dai *dai, + struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[dai->id - 1]; + unsigned int base = dai->driver->base; + unsigned int current_asp_rate, target_asp_rate; +- bool change_rate_domain = false; + int ret; + + if (clk_id == dai_priv->clk) +@@ -2284,19 +2282,15 @@ static int cs48l32_dai_set_sysclk(struct snd_soc_dai *dai, + + if ((current_asp_rate & CS48L32_ASP_RATE_MASK) != + (target_asp_rate & CS48L32_ASP_RATE_MASK)) { +- change_rate_domain = true; +- +- mutex_lock(&cs48l32_codec->rate_lock); + /* Guard the rate change with SYSCLK cycles */ +- cs48l32_spin_sysclk(cs48l32_codec); +- } +- +- snd_soc_component_update_bits(component, base + CS48L32_ASP_CONTROL1, +- CS48L32_ASP_RATE_MASK, target_asp_rate); +- +- if (change_rate_domain) { +- cs48l32_spin_sysclk(cs48l32_codec); +- mutex_unlock(&cs48l32_codec->rate_lock); ++ scoped_guard(mutex, &cs48l32_codec->rate_lock) { ++ cs48l32_spin_sysclk(cs48l32_codec); ++ snd_soc_component_update_bits(component, ++ base + CS48L32_ASP_CONTROL1, ++ CS48L32_ASP_RATE_MASK, ++ target_asp_rate); ++ cs48l32_spin_sysclk(cs48l32_codec); ++ } + } + } + +diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c +--- a/sound/soc/codecs/cx2072x.c ++++ b/sound/soc/codecs/cx2072x.c +@@ -9,6 +9,7 @@ + // + + #include ++#include + #include + #include + #include +@@ -1408,7 +1409,7 @@ static int cx2072x_jack_status_check(void *data) + unsigned int type = 0; + int state = 0; + +- mutex_lock(&cx2072x->lock); ++ guard(mutex)(&cx2072x->lock); + + regmap_read(cx2072x->regmap, CX2072X_PORTA_PIN_SENSE, &jack); + jack = jack >> 24; +@@ -1434,8 +1435,6 @@ static int cx2072x_jack_status_check(void *data) + /* clear interrupt */ + regmap_write(cx2072x->regmap, CX2072X_UM_INTERRUPT_CRTL_E, 0x12 << 24); + +- mutex_unlock(&cx2072x->lock); +- + dev_dbg(codec->dev, "CX2072X_HSDETECT type=0x%X,Jack state = %x\n", + type, state); + return state; +diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c +--- a/sound/soc/codecs/da7213.c ++++ b/sound/soc/codecs/da7213.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -216,13 +217,10 @@ static int da7213_volsw_locked_get(struct snd_kcontrol *kcontrol, + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); +- int ret; + +- mutex_lock(&da7213->ctrl_lock); +- ret = snd_soc_get_volsw(kcontrol, ucontrol); +- mutex_unlock(&da7213->ctrl_lock); ++ guard(mutex)(&da7213->ctrl_lock); + +- return ret; ++ return snd_soc_get_volsw(kcontrol, ucontrol); + } + + static int da7213_volsw_locked_put(struct snd_kcontrol *kcontrol, +@@ -230,13 +228,10 @@ static int da7213_volsw_locked_put(struct snd_kcontrol *kcontrol, + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); +- int ret; + +- mutex_lock(&da7213->ctrl_lock); +- ret = snd_soc_put_volsw(kcontrol, ucontrol); +- mutex_unlock(&da7213->ctrl_lock); ++ guard(mutex)(&da7213->ctrl_lock); + +- return ret; ++ return snd_soc_put_volsw(kcontrol, ucontrol); + } + + static int da7213_enum_locked_get(struct snd_kcontrol *kcontrol, +@@ -244,13 +239,10 @@ static int da7213_enum_locked_get(struct snd_kcontrol *kcontrol, + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); +- int ret; + +- mutex_lock(&da7213->ctrl_lock); +- ret = snd_soc_get_enum_double(kcontrol, ucontrol); +- mutex_unlock(&da7213->ctrl_lock); ++ guard(mutex)(&da7213->ctrl_lock); + +- return ret; ++ return snd_soc_get_enum_double(kcontrol, ucontrol); + } + + static int da7213_enum_locked_put(struct snd_kcontrol *kcontrol, +@@ -258,13 +250,10 @@ static int da7213_enum_locked_put(struct snd_kcontrol *kcontrol, + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); +- int ret; + +- mutex_lock(&da7213->ctrl_lock); +- ret = snd_soc_put_enum_double(kcontrol, ucontrol); +- mutex_unlock(&da7213->ctrl_lock); ++ guard(mutex)(&da7213->ctrl_lock); + +- return ret; ++ return snd_soc_put_enum_double(kcontrol, ucontrol); + } + + /* ALC */ +@@ -465,9 +454,8 @@ static int da7213_tonegen_freq_get(struct snd_kcontrol *kcontrol, + __le16 val; + int ret; + +- mutex_lock(&da7213->ctrl_lock); +- ret = regmap_raw_read(da7213->regmap, reg, &val, sizeof(val)); +- mutex_unlock(&da7213->ctrl_lock); ++ scoped_guard(mutex, &da7213->ctrl_lock) ++ ret = regmap_raw_read(da7213->regmap, reg, &val, sizeof(val)); + + if (ret) + return ret; +@@ -499,12 +487,11 @@ static int da7213_tonegen_freq_put(struct snd_kcontrol *kcontrol, + */ + val_new = cpu_to_le16(ucontrol->value.integer.value[0]); + +- mutex_lock(&da7213->ctrl_lock); ++ guard(mutex)(&da7213->ctrl_lock); + ret = regmap_raw_read(da7213->regmap, reg, &val_old, sizeof(val_old)); + if (ret == 0 && (val_old != val_new)) + ret = regmap_raw_write(da7213->regmap, reg, + &val_new, sizeof(val_new)); +- mutex_unlock(&da7213->ctrl_lock); + + if (ret < 0) + return ret; +diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c +--- a/sound/soc/codecs/da7219.c ++++ b/sound/soc/codecs/da7219.c +@@ -8,6 +8,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -256,13 +257,10 @@ static int da7219_volsw_locked_get(struct snd_kcontrol *kcontrol, + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); +- int ret; + +- mutex_lock(&da7219->ctrl_lock); +- ret = snd_soc_get_volsw(kcontrol, ucontrol); +- mutex_unlock(&da7219->ctrl_lock); ++ guard(mutex)(&da7219->ctrl_lock); + +- return ret; ++ return snd_soc_get_volsw(kcontrol, ucontrol); + } + + static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol, +@@ -270,13 +268,10 @@ static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol, + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); +- int ret; + +- mutex_lock(&da7219->ctrl_lock); +- ret = snd_soc_put_volsw(kcontrol, ucontrol); +- mutex_unlock(&da7219->ctrl_lock); ++ guard(mutex)(&da7219->ctrl_lock); + +- return ret; ++ return snd_soc_put_volsw(kcontrol, ucontrol); + } + + static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol, +@@ -284,13 +279,10 @@ static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol, + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); +- int ret; + +- mutex_lock(&da7219->ctrl_lock); +- ret = snd_soc_get_enum_double(kcontrol, ucontrol); +- mutex_unlock(&da7219->ctrl_lock); ++ guard(mutex)(&da7219->ctrl_lock); + +- return ret; ++ return snd_soc_get_enum_double(kcontrol, ucontrol); + } + + static int da7219_enum_locked_put(struct snd_kcontrol *kcontrol, +@@ -298,13 +290,10 @@ static int da7219_enum_locked_put(struct snd_kcontrol *kcontrol, + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); +- int ret; + +- mutex_lock(&da7219->ctrl_lock); +- ret = snd_soc_put_enum_double(kcontrol, ucontrol); +- mutex_unlock(&da7219->ctrl_lock); ++ guard(mutex)(&da7219->ctrl_lock); + +- return ret; ++ return snd_soc_put_enum_double(kcontrol, ucontrol); + } + + /* ALC */ +@@ -422,9 +411,8 @@ static int da7219_tonegen_freq_get(struct snd_kcontrol *kcontrol, + __le16 val; + int ret; + +- mutex_lock(&da7219->ctrl_lock); +- ret = regmap_raw_read(da7219->regmap, reg, &val, sizeof(val)); +- mutex_unlock(&da7219->ctrl_lock); ++ scoped_guard(mutex, &da7219->ctrl_lock) ++ ret = regmap_raw_read(da7219->regmap, reg, &val, sizeof(val)); + + if (ret) + return ret; +@@ -456,12 +444,11 @@ static int da7219_tonegen_freq_put(struct snd_kcontrol *kcontrol, + */ + val_new = cpu_to_le16(ucontrol->value.integer.value[0]); + +- mutex_lock(&da7219->ctrl_lock); ++ guard(mutex)(&da7219->ctrl_lock); + ret = regmap_raw_read(da7219->regmap, reg, &val_old, sizeof(val_old)); + if (ret == 0 && (val_old != val_new)) + ret = regmap_raw_write(da7219->regmap, reg, +- &val_new, sizeof(val_new)); +- mutex_unlock(&da7219->ctrl_lock); ++ &val_new, sizeof(val_new)); + + if (ret < 0) + return ret; +@@ -1167,15 +1154,12 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + int ret = 0; + +- mutex_lock(&da7219->pll_lock); ++ guard(mutex)(&da7219->pll_lock); + +- if ((da7219->clk_src == clk_id) && (da7219->mclk_rate == freq)) { +- mutex_unlock(&da7219->pll_lock); ++ if (da7219->clk_src == clk_id && da7219->mclk_rate == freq) + return 0; +- } + +- if ((freq < 2000000) || (freq > 54000000)) { +- mutex_unlock(&da7219->pll_lock); ++ if (freq < 2000000 || freq > 54000000) { + dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", + freq); + return -EINVAL; +@@ -1193,7 +1177,6 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, + break; + default: + dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id); +- mutex_unlock(&da7219->pll_lock); + return -EINVAL; + } + +@@ -1203,17 +1186,13 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, + freq = clk_round_rate(da7219->mclk, freq); + ret = clk_set_rate(da7219->mclk, freq); + if (ret) { +- dev_err(codec_dai->dev, "Failed to set clock rate %d\n", +- freq); +- mutex_unlock(&da7219->pll_lock); ++ dev_err(codec_dai->dev, "Failed to set clock rate %d\n", freq); + return ret; + } + } + + da7219->mclk_rate = freq; + +- mutex_unlock(&da7219->pll_lock); +- + return 0; + } + +@@ -1296,13 +1275,10 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + { + struct snd_soc_component *component = codec_dai->component; + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); +- int ret; + +- mutex_lock(&da7219->pll_lock); +- ret = da7219_set_pll(component, source, fout); +- mutex_unlock(&da7219->pll_lock); ++ guard(mutex)(&da7219->pll_lock); + +- return ret; ++ return da7219_set_pll(component, source, fout); + } + + static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c +--- a/sound/soc/codecs/es8316.c ++++ b/sound/soc/codecs/es8316.c +@@ -9,11 +9,13 @@ + + #include + #include ++#include + #include + #include + #include + #include + #include ++#include + #include + #include + #include +@@ -30,6 +32,13 @@ static const unsigned int supported_mclk_lrck_ratios[] = { + 256, 384, 400, 500, 512, 768, 1024 + }; + ++static const char * const es8316_supply_names[] = { ++ "avdd", ++ "cpvdd", ++ "dvdd", ++ "pvdd", ++}; ++ + struct es8316_priv { + struct mutex lock; + struct clk *mclk; +@@ -620,15 +629,15 @@ static irqreturn_t es8316_irq(int irq, void *data) + struct snd_soc_component *comp = es8316->component; + unsigned int flags; + +- mutex_lock(&es8316->lock); ++ guard(mutex)(&es8316->lock); + + regmap_read(es8316->regmap, ES8316_GPIO_FLAG, &flags); + if (flags == 0x00) +- goto out; /* Powered-down / reset */ ++ return IRQ_HANDLED; /* Powered-down / reset */ + + /* Catch spurious IRQ before set_jack is called */ + if (!es8316->jack) +- goto out; ++ return IRQ_HANDLED; + + if (es8316->jd_inverted) + flags ^= ES8316_GPIO_FLAG_HP_NOT_INSERTED; +@@ -681,8 +690,6 @@ static irqreturn_t es8316_irq(int irq, void *data) + } + } + +-out: +- mutex_unlock(&es8316->lock); + return IRQ_HANDLED; + } + +@@ -699,18 +706,16 @@ static void es8316_enable_jack_detect(struct snd_soc_component *component, + es8316->jd_inverted = device_property_read_bool(component->dev, + "everest,jack-detect-inverted"); + +- mutex_lock(&es8316->lock); ++ scoped_guard(mutex, &es8316->lock) { ++ es8316->jack = jack; + +- es8316->jack = jack; ++ if (es8316->jack->status & SND_JACK_MICROPHONE) ++ es8316_enable_micbias_for_mic_gnd_short_detect(component); + +- if (es8316->jack->status & SND_JACK_MICROPHONE) +- es8316_enable_micbias_for_mic_gnd_short_detect(component); +- +- snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, +- ES8316_GPIO_ENABLE_INTERRUPT, +- ES8316_GPIO_ENABLE_INTERRUPT); +- +- mutex_unlock(&es8316->lock); ++ snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, ++ ES8316_GPIO_ENABLE_INTERRUPT, ++ ES8316_GPIO_ENABLE_INTERRUPT); ++ } + + /* Enable irq and sync initial jack state */ + enable_irq(es8316->irq); +@@ -726,7 +731,7 @@ static void es8316_disable_jack_detect(struct snd_soc_component *component) + + disable_irq(es8316->irq); + +- mutex_lock(&es8316->lock); ++ guard(mutex)(&es8316->lock); + + snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, + ES8316_GPIO_ENABLE_INTERRUPT, 0); +@@ -737,8 +742,6 @@ static void es8316_disable_jack_detect(struct snd_soc_component *component) + } + + es8316->jack = NULL; +- +- mutex_unlock(&es8316->lock); + } + + static int es8316_set_jack(struct snd_soc_component *component, +@@ -871,6 +874,11 @@ static int es8316_i2c_probe(struct i2c_client *i2c_client) + + i2c_set_clientdata(i2c_client, es8316); + ++ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(es8316_supply_names), ++ es8316_supply_names); ++ if (ret) ++ return dev_err_probe(dev, ret, "unable to enable supplies\n"); ++ + es8316->regmap = devm_regmap_init_i2c(i2c_client, &es8316_regmap); + if (IS_ERR(es8316->regmap)) + return PTR_ERR(es8316->regmap); +diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c +--- a/sound/soc/codecs/es8326.c ++++ b/sound/soc/codecs/es8326.c +@@ -6,6 +6,7 @@ + // Authors: David Yang + // + ++#include + #include + #include + #include +@@ -790,7 +791,7 @@ static void es8326_jack_button_handler(struct work_struct *work) + if (!(es8326->jack->status & SND_JACK_HEADSET)) /* Jack unplugged */ + return; + +- mutex_lock(&es8326->lock); ++ guard(mutex)(&es8326->lock); + iface = snd_soc_component_read(comp, ES8326_HPDET_STA); + switch (iface) { + case 0x93: +@@ -845,7 +846,6 @@ static void es8326_jack_button_handler(struct work_struct *work) + } + es8326_disable_micbias(es8326->component); + } +- mutex_unlock(&es8326->lock); + } + + static void es8326_jack_detect_handler(struct work_struct *work) +@@ -855,7 +855,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) + struct snd_soc_component *comp = es8326->component; + unsigned int iface; + +- mutex_lock(&es8326->lock); ++ guard(mutex)(&es8326->lock); + iface = snd_soc_component_read(comp, ES8326_HPDET_STA); + dev_dbg(comp->dev, "gpio flag %#04x", iface); + +@@ -873,7 +873,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) + regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, + ES8326_HP_DET_JACK_POL, (es8326->jd_inverted ? + ~es8326->jack_pol : es8326->jack_pol)); +- goto exit; ++ return; + } + + if ((iface & ES8326_HPINSERT_FLAG) == 0) { +@@ -930,7 +930,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) + queue_delayed_work(system_dfl_wq, &es8326->jack_detect_work, + msecs_to_jiffies(400)); + es8326->hp = 1; +- goto exit; ++ return; + } + if (es8326->jack->status & SND_JACK_HEADSET) { + /* detect button */ +@@ -939,7 +939,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) + (ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON)); + es8326_enable_micbias(es8326->component); + queue_delayed_work(system_dfl_wq, &es8326->button_press_work, 10); +- goto exit; ++ return; + } + if ((iface & ES8326_HPBUTTON_FLAG) == 0x01) { + dev_dbg(comp->dev, "Headphone detected\n"); +@@ -958,8 +958,6 @@ static void es8326_jack_detect_handler(struct work_struct *work) + usleep_range(10000, 15000); + } + } +-exit: +- mutex_unlock(&es8326->lock); + } + + static irqreturn_t es8326_irq(int irq, void *dev_id) +@@ -1200,13 +1198,12 @@ static void es8326_enable_jack_detect(struct snd_soc_component *component, + { + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + +- mutex_lock(&es8326->lock); +- if (es8326->jd_inverted) +- snd_soc_component_update_bits(component, ES8326_HPDET_TYPE, +- ES8326_HP_DET_JACK_POL, ~es8326->jack_pol); +- es8326->jack = jack; +- +- mutex_unlock(&es8326->lock); ++ scoped_guard(mutex, &es8326->lock) { ++ if (es8326->jd_inverted) ++ snd_soc_component_update_bits(component, ES8326_HPDET_TYPE, ++ ES8326_HP_DET_JACK_POL, ~es8326->jack_pol); ++ es8326->jack = jack; ++ } + es8326_irq(es8326->irq, es8326); + } + +@@ -1219,13 +1216,12 @@ static void es8326_disable_jack_detect(struct snd_soc_component *component) + return; /* Already disabled (or never enabled) */ + cancel_delayed_work_sync(&es8326->jack_detect_work); + +- mutex_lock(&es8326->lock); ++ guard(mutex)(&es8326->lock); + if (es8326->jack->status & SND_JACK_MICROPHONE) { + es8326_disable_micbias(component); + snd_soc_jack_report(es8326->jack, 0, SND_JACK_HEADSET); + } + es8326->jack = NULL; +- mutex_unlock(&es8326->lock); + } + + static int es8326_set_jack(struct snd_soc_component *component, +diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c +--- a/sound/soc/codecs/es8328.c ++++ b/sound/soc/codecs/es8328.c +@@ -405,6 +405,11 @@ static const struct snd_soc_dapm_route es8328_dapm_routes[] = { + + { "Mic Bias", NULL, "Mic Bias Gen" }, + ++ { "LINPUT1", NULL, "Mic Bias" }, ++ { "RINPUT1", NULL, "Mic Bias" }, ++ { "LINPUT2", NULL, "Mic Bias" }, ++ { "RINPUT2", NULL, "Mic Bias" }, ++ + { "Left Mixer", NULL, "Left DAC" }, + { "Left Mixer", "Left Bypass Switch", "Left Line Mux" }, + { "Left Mixer", "Right Playback Switch", "Right DAC" }, +diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c +--- a/sound/soc/codecs/es8389.c ++++ b/sound/soc/codecs/es8389.c +@@ -36,6 +36,10 @@ struct es8389_private { + unsigned int sysclk; + int mastermode; + ++ u8 hpfl; ++ u8 hpfr; ++ u32 hpf_freq; ++ u32 capture_rate; + u8 mclk_src; + u8 vddd; + int version; +@@ -50,10 +54,29 @@ static const char * const es8389_core_supplies[] = { + static bool es8389_volatile_register(struct device *dev, + unsigned int reg) + { +- if ((reg <= 0xff)) +- return true; +- else ++ switch (reg) { ++ case ES8389_ADCL_VOL: ++ case ES8389_ADCR_VOL: ++ case ES8389_MIC1_GAIN: ++ case ES8389_MIC2_GAIN: ++ case ES8389_DACL_VOL: ++ case ES8389_DACR_VOL: ++ case ES8389_ALC_ON: ++ case ES8389_ALC_CTL: ++ case ES8389_ALC_TARGET: ++ case ES8389_ALC_GAIN: ++ case ES8389_ADC_MUTE: ++ case ES8389_OSR_VOL: ++ case ES8389_DAC_INV: ++ case ES8389_MIX_VOL: ++ case ES8389_DAC_MIX: ++ case ES8389_ADC_RESET: ++ case ES8389_ADC_MODE: ++ case ES8389_DMIC_EN: + return false; ++ default: ++ return true; ++ } + } + + static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9550, 50, 0); +@@ -63,6 +86,92 @@ static const DECLARE_TLV_DB_SCALE(mix_vol_tlv, -9500, 100, 0); + static const DECLARE_TLV_DB_SCALE(alc_target_tlv, -3200, 200, 0); + static const DECLARE_TLV_DB_SCALE(alc_max_level, -3200, 200, 0); + ++static const u32 hpf_table[10][10] = { ++ {1020, 754, 624, 559, 527, 511, 502, 498, 497, 496}, ++ {754, 495, 368, 306, 274, 259, 251, 247, 246, 244}, ++ {624, 368, 243, 182, 151, 136, 128, 124, 123, 121}, ++ {559, 306, 182, 120, 90, 75, 68, 63, 62, 60}, ++ {527, 274, 151, 90, 60, 45, 38, 33, 32, 31}, ++ {511, 259, 136, 75, 45, 30, 23, 19, 18, 17}, ++ {502, 251, 128, 68, 38, 23, 16, 13, 11, 11}, ++ {498, 247, 124, 63, 33, 19, 13, 10, 8, 8}, ++ {497, 246, 123, 62, 32, 18, 11, 8, 8, 0}, ++ {496, 244, 121, 60, 31, 17, 11, 8, 0, 0} ++}; ++ ++static bool find_best_hpf_freq(u32 target_hz, u8 *hpf1, u8 *hpf2, u32 *out) ++{ ++ int best_row = -1, best_col = -1; ++ u32 min_diff = U32_MAX; ++ u32 f, diff; ++ int i, j; ++ ++ if (target_hz > 1020) ++ return false; ++ ++ for (i = 0; i < 10; i++) { ++ for (j = i; j < 10; j++) { ++ f = hpf_table[i][j]; ++ ++ diff = (target_hz > f) ? (target_hz - f) : (f - target_hz); ++ if (diff < min_diff) { ++ min_diff = diff; ++ best_row = i; ++ best_col = j; ++ *out = f; ++ } ++ } ++ } ++ ++ *hpf1 = best_col + ES8389_HPF_OFFSET; ++ *hpf2 = best_row + ES8389_HPF_OFFSET; ++ ++ return true; ++} ++ ++static int es8389_hpf_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); ++ ++ ucontrol->value.integer.value[0] = es8389->hpf_freq; ++ return 0; ++} ++ ++static int es8389_hpf_set(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); ++ u32 freq; ++ bool hpf; ++ ++ if (es8389->hpf_freq == ucontrol->value.integer.value[0]) ++ return 0; ++ ++ if (es8389->capture_rate) { ++ freq = (ucontrol->value.integer.value[0] * 48000) / es8389->capture_rate; ++ ++ hpf = find_best_hpf_freq(freq, &es8389->hpfl, &es8389->hpfr, &es8389->hpf_freq); ++ if (!hpf) ++ return -EBUSY; ++ ++ if (es8389->hpf_freq != ucontrol->value.integer.value[0]) ++ dev_dbg(component->dev, "At the %u Hz sampling rate, %ld Hz could not be obtained." ++ "the frequency has been set to the closest value, %u Hz\n", ++ es8389->capture_rate, ucontrol->value.integer.value[0], es8389->hpf_freq); ++ ++ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, es8389->hpfl); ++ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, es8389->hpfr); ++ } else { ++ es8389->hpf_freq = ucontrol->value.integer.value[0]; ++ dev_dbg(component->dev, "PCM_STREAM_CAPTURE is not active.retain the input frequency\n"); ++ } ++ ++ return 1; ++} ++ + static int es8389_dmic_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) + { +@@ -143,6 +252,16 @@ static const struct soc_enum alc_ramprate = + static const struct soc_enum alc_winsize = + SOC_ENUM_SINGLE(ES8389_ALC_CTL, 0, 16, winsize); + ++static const char *const es8389_adcl_mux_txt[] = { ++ "Normal", ++ "ADC2 channel to ADC1 channel", ++}; ++ ++static const char *const es8389_adcr_mux_txt[] = { ++ "Normal", ++ "ADC1 channel to ADC2 channel", ++}; ++ + static const char *const es8389_outl_mux_txt[] = { + "Normal", + "DAC2 channel to DAC1 channel", +@@ -170,6 +289,20 @@ static const unsigned int es8389_pga_values[] = { + 1, 5, 6 + }; + ++static const struct soc_enum es8389_adcl_mux_enum = ++ SOC_ENUM_SINGLE(ES8389_ADC_MODE, 5, ++ ARRAY_SIZE(es8389_adcl_mux_txt), es8389_adcl_mux_txt); ++ ++static const struct snd_kcontrol_new es8389_adcl_mux_controls = ++ SOC_DAPM_ENUM("INPUTL MUX", es8389_adcl_mux_enum); ++ ++static const struct soc_enum es8389_adcr_mux_enum = ++ SOC_ENUM_SINGLE(ES8389_ADC_MODE, 4, ++ ARRAY_SIZE(es8389_adcr_mux_txt), es8389_adcr_mux_txt); ++ ++static const struct snd_kcontrol_new es8389_adcr_mux_controls = ++ SOC_DAPM_ENUM("INPUTR MUX", es8389_adcr_mux_enum); ++ + static const struct soc_enum es8389_outl_mux_enum = + SOC_ENUM_SINGLE(ES8389_DAC_MIX, 5, + ARRAY_SIZE(es8389_outl_mux_txt), es8389_outl_mux_txt); +@@ -240,6 +373,8 @@ static const struct snd_kcontrol_new es8389_snd_controls[] = { + SOC_DOUBLE("ADC OSR Volume ON Switch", ES8389_ADC_MUTE, 6, 7, 1, 0), + SOC_SINGLE_TLV("ADC OSR Volume", ES8389_OSR_VOL, 0, 0xFF, 0, adc_vol_tlv), + SOC_DOUBLE("ADC OUTPUT Invert Switch", ES8389_ADC_HPF2, 5, 6, 1, 0), ++ SOC_SINGLE_EXT("ADC HPF Freq Select", SND_SOC_NOPM, 0, 1020, 0, ++ es8389_hpf_get, es8389_hpf_set), + + SOC_SINGLE_TLV("DACL Playback Volume", ES8389_DACL_VOL, 0, 0xFF, 0, dac_vol_tlv), + SOC_SINGLE_TLV("DACR Playback Volume", ES8389_DACR_VOL, 0, 0xFF, 0, dac_vol_tlv), +@@ -298,6 +433,8 @@ static const struct snd_soc_dapm_widget es8389_dapm_widgets[] = { + &es8389_adc_mixer_controls[0], + ARRAY_SIZE(es8389_adc_mixer_controls)), + SND_SOC_DAPM_MUX("ADC MUX", SND_SOC_NOPM, 0, 0, &es8389_dmic_mux_controls), ++ SND_SOC_DAPM_MUX("INPUTL MUX", SND_SOC_NOPM, 0, 0, &es8389_adcl_mux_controls), ++ SND_SOC_DAPM_MUX("INPUTR MUX", SND_SOC_NOPM, 0, 0, &es8389_adcr_mux_controls), + + SND_SOC_DAPM_MUX("OUTL MUX", SND_SOC_NOPM, 0, 0, &es8389_outl_mux_controls), + SND_SOC_DAPM_MUX("OUTR MUX", SND_SOC_NOPM, 0, 0, &es8389_outr_mux_controls), +@@ -311,10 +448,15 @@ static const struct snd_soc_dapm_route es8389_dapm_routes[] = { + {"ADCL", NULL, "PGAL"}, + {"ADCR", NULL, "PGAR"}, + ++ {"INPUTL MUX", "Normal", "ADCL"}, ++ {"INPUTL MUX", "ADC2 channel to ADC1 channel", "ADCR"}, ++ {"INPUTR MUX", "Normal", "ADCR"}, ++ {"INPUTR MUX", "ADC1 channel to ADC2 channel", "ADCL"}, ++ + {"ADC Mixer", "DACL ADCL Mixer", "DACL"}, + {"ADC Mixer", "DACR ADCR Mixer", "DACR"}, +- {"ADC Mixer", NULL, "ADCL"}, +- {"ADC Mixer", NULL, "ADCR"}, ++ {"ADC Mixer", NULL, "INPUTL MUX"}, ++ {"ADC Mixer", NULL, "INPUTR MUX"}, + + {"ADC MUX", "AMIC", "ADC Mixer"}, + {"ADC MUX", "DMIC", "DMIC"}, +@@ -415,52 +557,54 @@ static const struct _coeff_div coeff_div[] = { + {36, 576000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {48, 768000, 16000, 0x02, 0x57, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {50, 800000, 16000, 0x00, 0x7E, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, +- {64, 1024000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, +- {72, 1152000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, ++ {64, 1024000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, ++ {72, 1152000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {96, 1536000, 16000, 0x02, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {128, 2048000, 16000, 0x00, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {144, 2304000, 16000, 0x00, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xBF, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, ++ {150, 2400000, 16000, 0x02, 0x7E, 0x01, 0xC9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {192, 3072000, 16000, 0x02, 0x65, 0x25, 0xE0, 0x00, 0xE1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, +- {256, 4096000, 16000, 0x00, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, ++ {256, 4096000, 16000, 0x00, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {300, 4800000, 16000, 0x02, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {384, 6144000, 16000, 0x02, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, +- {512, 8192000, 16000, 0x01, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, ++ {512, 8192000, 16000, 0x01, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {750, 12000000, 16000, 0x0E, 0x7E, 0x01, 0xC9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, +- {768, 12288000, 16000, 0x02, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, +- {1024, 16384000, 16000, 0x03, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, ++ {768, 12288000, 16000, 0x02, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, ++ {1024, 16384000, 16000, 0x03, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {1152, 18432000, 16000, 0x08, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {1200, 19200000, 16000, 0x0B, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {1500, 24000000, 16000, 0x0E, 0x26, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, +- {1536, 24576000, 16000, 0x05, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, ++ {1536, 24576000, 16000, 0x05, 0x41, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {1625, 26000000, 16000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E, 2, 2}, + {800, 19200000, 24000, 0x07, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x1A, 0x49, 0x14, 2, 2}, + {375, 12000000, 32000, 0x0E, 0x2E, 0x05, 0xC8, 0x00, 0xC2, 0x80, 0x40, 0x01, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B, 2, 0}, +- {600, 19200000, 32000, 0x05, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B, 2, 2}, +- {32, 1411200, 44100, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {600, 19200000, 32000, 0x05, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B, 2, 2}, ++ {32, 1411200, 44100, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {64, 2822400, 44100, 0x00, 0x51, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, +- {128, 5644800, 44100, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, +- {256, 11289600, 44100, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, +- {512, 22579200, 44100, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, +- {32, 1536000, 48000, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {128, 5644800, 44100, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {256, 11289600, 44100, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {512, 22579200, 44100, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {32, 1536000, 48000, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {48, 2304000, 48000, 0x02, 0x55, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {50, 2400000, 48000, 0x00, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {64, 3072000, 48000, 0x00, 0x51, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, +- {100, 4800000, 48000, 0x00, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {100, 4800000, 48000, 0x00, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {125, 6000000, 48000, 0x04, 0x6E, 0x05, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x01, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, +- {128, 6144000, 48000, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, +- {200, 9600000, 48000, 0x01, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {128, 6144000, 48000, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {200, 9600000, 48000, 0x01, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {250, 12000000, 48000, 0x04, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, +- {256, 12288000, 48000, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, +- {384, 18432000, 48000, 0x02, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, +- {400, 19200000, 48000, 0x03, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, +- {500, 24000000, 48000, 0x04, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, +- {512, 24576000, 48000, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {256, 12288000, 48000, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {384, 18432000, 48000, 0x02, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {400, 19200000, 48000, 0x03, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {500, 24000000, 48000, 0x04, 0x46, 0x01, 0xD8, 0x10, 0xC2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, ++ {512, 24576000, 48000, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {800, 38400000, 48000, 0x18, 0x45, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28, 2, 2}, + {128, 11289600, 88200, 0x00, 0x50, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x32, 0x89, 0x25, 2, 2}, +- {64, 6144000, 96000, 0x00, 0x41, 0x00, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, ++ {64, 6144000, 96000, 0x00, 0x41, 0x00, 0xD0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, + {96, 9216000, 96000, 0x02, 0x43, 0x00, 0xC0, 0x10, 0xC0, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, + {256, 24576000, 96000, 0x00, 0x40, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28, 2, 2}, + {128, 24576000, 192000, 0x00, 0x50, 0x00, 0xC0, 0x18, 0xC1, 0x81, 0xC0, 0x00, 0x8F, 0x7F, 0xBF, 0xC0, 0x3F, 0x7F, 0x80, 0x12, 0xC0, 0x3F, 0xF9, 0x3F, 2, 2}, ++ {64, 12288000, 192000, 0x00, 0x41, 0x00, 0xC0, 0x18, 0xC1, 0x80, 0x00, 0x00, 0x8F, 0x7F, 0xEF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x3F, 0xF9, 0x3F, 1, 0}, + }; + + static inline int get_coeff(u8 vddd, u8 dmic, int mclk, int rate) +@@ -564,6 +708,8 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream, + int coeff, ret; + u8 dmic_enable, state = 0; + unsigned int regv; ++ u32 freq; ++ bool hpf; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: +@@ -590,7 +736,7 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream, + + if (es8389->mclk_src == ES8389_SCLK_PIN) { + regmap_update_bits(es8389->regmap, ES8389_MASTER_CLK, +- ES8389_MCLK_SOURCE, es8389->mclk_src); ++ ES8389_MCLK_MASK, ES8389_MCLK_FROM_SCLK); + es8389->sysclk = params_channels(params) * params_width(params) * params_rate(params); + } + +@@ -641,6 +787,28 @@ static int es8389_pcm_hw_params(struct snd_pcm_substream *substream, + dev_warn(component->dev, "Clock coefficients do not match"); + } + ++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { ++ es8389->capture_rate = params_rate(params); ++ freq = (es8389->hpf_freq * 48000) / params_rate(params); ++ hpf = find_best_hpf_freq(freq, &es8389->hpfl, &es8389->hpfr, &es8389->hpf_freq); ++ if (!hpf) { ++ dev_err(component->dev, "The HPF frequency is invalid\n"); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++static int es8389_pcm_hw_free(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); ++ ++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ++ es8389->capture_rate = 0; ++ + return 0; + } + +@@ -721,8 +889,8 @@ static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction) + regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE, + 0x03, 0x00); + } else { +- regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x0a); +- regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x0a); ++ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, es8389->hpfl); ++ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, es8389->hpfr); + regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE, + 0x03, 0x00); + } +@@ -738,6 +906,7 @@ static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction) + + static const struct snd_soc_dai_ops es8389_ops = { + .hw_params = es8389_pcm_hw_params, ++ .hw_free = es8389_pcm_hw_free, + .set_fmt = es8389_set_dai_fmt, + .set_sysclk = es8389_set_dai_sysclk, + .set_tdm_slot = es8389_set_tdm_slot, +@@ -771,7 +940,7 @@ static void es8389_init(struct snd_soc_component *component) + + regmap_read(es8389->regmap, ES8389_MAX_REGISTER, ®); + es8389->version = reg; +- regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x00); ++ regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x56); + regmap_write(es8389->regmap, ES8389_RESET, 0x7E); + regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x38); + regmap_write(es8389->regmap, ES8389_ADC_HPF1, 0x64); +@@ -823,7 +992,7 @@ static void es8389_init(struct snd_soc_component *component) + regmap_write(es8389->regmap, ES8389_SCLK_DIV, 0x04); + regmap_write(es8389->regmap, ES8389_LRCK_DIV1, 0x01); + regmap_write(es8389->regmap, ES8389_LRCK_DIV2, 0x00); +- regmap_write(es8389->regmap, ES8389_OSC_CLK, 0x00); ++ regmap_write(es8389->regmap, ES8389_OSC_CLK, 0x10); + regmap_write(es8389->regmap, ES8389_ADC_OSR, 0x1F); + regmap_write(es8389->regmap, ES8389_ADC_DSP, 0x7F); + regmap_write(es8389->regmap, ES8389_ADC_MUTE, 0xC0); +@@ -861,13 +1030,13 @@ static int es8389_resume(struct snd_soc_component *component) + regcache_cache_only(es8389->regmap, false); + regcache_cache_bypass(es8389->regmap, true); + regmap_read(es8389->regmap, ES8389_RESET, ®v); +- regcache_cache_bypass(es8389->regmap, false); + + if (regv == 0xff) + es8389_init(component); + else + es8389_set_bias_level(component, SND_SOC_BIAS_ON); + ++ regcache_cache_bypass(es8389->regmap, false); + regcache_sync(es8389->regmap); + + return 0; +@@ -913,6 +1082,7 @@ static int es8389_probe(struct snd_soc_component *component) + return ret; + } + ++ es8389->hpf_freq = ES8389_HPF_DEFAULT; + es8389_init(component); + es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY); + +diff --git a/sound/soc/codecs/es8389.h b/sound/soc/codecs/es8389.h +--- a/sound/soc/codecs/es8389.h ++++ b/sound/soc/codecs/es8389.h +@@ -106,6 +106,9 @@ + #define ES8389_MIC_SEL_MASK (7 << 4) + #define ES8389_MIC_DEFAULT (1 << 4) + ++#define ES8389_HPF_DEFAULT 16 ++#define ES8389_HPF_OFFSET 4 ++ + #define ES8389_MASTER_MODE_EN (1 << 0) + + #define ES8389_TDM_OFF (0 << 0) +@@ -116,9 +119,11 @@ + #define ES8389_TDM_SLOT (0x70 << 0) + #define ES8389_TDM_SHIFT 4 + +-#define ES8389_MCLK_SOURCE (1 << 6) +-#define ES8389_MCLK_PIN (1 << 6) +-#define ES8389_SCLK_PIN (0 << 6) ++#define ES8389_MCLK_MASK (3 << 6) ++#define ES8389_MCLK_FROM_SCLK (1 << 6) ++#define ES8389_MCLK_SOURCE ES8389_MCLK_PIN ++#define ES8389_MCLK_PIN 0 ++#define ES8389_SCLK_PIN 1 + + /* ES8389_FMT */ + #define ES8389_S24_LE (0 << 5) +diff --git a/sound/soc/codecs/es9356.c b/sound/soc/codecs/es9356.c +--- a/sound/soc/codecs/es9356.c ++++ b/sound/soc/codecs/es9356.c +@@ -6,6 +6,7 @@ + // + // + ++#include + #include + #include + #include +@@ -19,8 +20,6 @@ + #include + #include + #include +-#include +-#include + #include + #include + #include +@@ -500,16 +499,19 @@ static int es9356_power_state(struct snd_soc_dai *dai, unsigned char ps, unsigne + } + + /* power state changes are not independent across functions */ +- mutex_lock(&es9356->pde_lock); +- ret = es9356_pde_transition_delay(es9356, func, pde_entity, ps?ps0:ps3); +- if (ret) { +- regmap_write(es9356->regmap, +- SDW_SDCA_CTL(func, pde_entity, ES9356_SDCA_CTL_REQ_POWER_STATE, 0), ps?ps3:ps0); +- es9356_pde_transition_delay(es9356, func, pde_entity, ps?ps3:ps0); +- } else +- dev_dbg(component->dev, "%s PDE is already %d\n", __func__, ps?ps0:ps3); +- +- mutex_unlock(&es9356->pde_lock); ++ scoped_guard(mutex, &es9356->pde_lock) { ++ ret = es9356_pde_transition_delay(es9356, func, pde_entity, ps ? ps0 : ps3); ++ if (ret) { ++ regmap_write(es9356->regmap, ++ SDW_SDCA_CTL(func, pde_entity, ++ ES9356_SDCA_CTL_REQ_POWER_STATE, 0), ++ ps ? ps3 : ps0); ++ es9356_pde_transition_delay(es9356, func, pde_entity, ps ? ps3 : ps0); ++ } else { ++ dev_dbg(component->dev, "%s PDE is already %d\n", __func__, ++ ps ? ps0 : ps3); ++ } ++ } + + if (rate) + regmap_write(es9356->regmap, +@@ -671,7 +673,7 @@ static int es9356_sdca_mbq_size(struct device *dev, unsigned int reg) + } + } + +-static struct regmap_sdw_mbq_cfg es9356_mbq_config = { ++static const struct regmap_sdw_mbq_cfg es9356_mbq_config = { + .mbq_size = es9356_sdca_mbq_size, + }; + +@@ -1091,9 +1093,8 @@ static int es9356_sdca_dev_system_suspend(struct device *dev) + { + struct es9356_sdw_priv *es9356 = dev_get_drvdata(dev); + +- mutex_lock(&es9356->disable_irq_lock); +- es9356->disable_irq = true; +- mutex_unlock(&es9356->disable_irq_lock); ++ scoped_guard(mutex, &es9356->disable_irq_lock) ++ es9356->disable_irq = true; + + return es9356_sdca_dev_suspend(dev); + } +diff --git a/sound/soc/codecs/fs-amp-lib.c b/sound/soc/codecs/fs-amp-lib.c +--- a/sound/soc/codecs/fs-amp-lib.c ++++ b/sound/soc/codecs/fs-amp-lib.c +@@ -221,7 +221,7 @@ static void fs_print_firmware_info(struct fs_amp_lib *amp_lib) + + int fs_amp_load_firmware(struct fs_amp_lib *amp_lib, const char *name) + { +- const struct firmware *cont; ++ const struct firmware *cont __free(firmware) = NULL; + struct fs_fwm_header *hdr; + int ret; + +@@ -237,7 +237,6 @@ int fs_amp_load_firmware(struct fs_amp_lib *amp_lib, const char *name) + dev_info(amp_lib->dev, "Loading %s - size: %zu\n", name, cont->size); + + hdr = devm_kmemdup(amp_lib->dev, cont->data, cont->size, GFP_KERNEL); +- release_firmware(cont); + if (!hdr) + return -ENOMEM; + +diff --git a/sound/soc/codecs/fs210x.c b/sound/soc/codecs/fs210x.c +--- a/sound/soc/codecs/fs210x.c ++++ b/sound/soc/codecs/fs210x.c +@@ -4,6 +4,7 @@ + // + // Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd. + ++#include + #include + #include + #include +@@ -771,9 +772,8 @@ static int fs210x_dai_hw_params(struct snd_pcm_substream *substream, + if (fs210x->devid == FS2105S_DEVICE_ID && fs210x->srate == 16000) + return -EOPNOTSUPP; + +- mutex_lock(&fs210x->lock); +- ret = fs210x_set_hw_params(fs210x); +- mutex_unlock(&fs210x->lock); ++ scoped_guard(mutex, &fs210x->lock) ++ ret = fs210x_set_hw_params(fs210x); + if (ret) + dev_err(fs210x->dev, "Failed to set hw params: %d\n", ret); + +@@ -790,15 +790,11 @@ static int fs210x_dai_mute(struct snd_soc_dai *dai, int mute, int stream) + + fs210x = snd_soc_component_get_drvdata(dai->component); + +- mutex_lock(&fs210x->lock); +- +- if (!fs210x->is_inited || fs210x->is_suspended) { +- mutex_unlock(&fs210x->lock); +- return 0; ++ scoped_guard(mutex, &fs210x->lock) { ++ if (!fs210x->is_inited || fs210x->is_suspended) ++ return 0; + } + +- mutex_unlock(&fs210x->lock); +- + if (mute) { + cancel_delayed_work_sync(&fs210x->fault_check_work); + cancel_delayed_work_sync(&fs210x->start_work); +@@ -817,15 +813,11 @@ static int fs210x_dai_trigger(struct snd_pcm_substream *substream, + + fs210x = snd_soc_component_get_drvdata(dai->component); + +- mutex_lock(&fs210x->lock); +- +- if (!fs210x->is_inited || fs210x->is_suspended || fs210x->is_playing) { +- mutex_unlock(&fs210x->lock); +- return 0; ++ scoped_guard(mutex, &fs210x->lock) { ++ if (!fs210x->is_inited || fs210x->is_suspended || fs210x->is_playing) ++ return 0; + } + +- mutex_unlock(&fs210x->lock); +- + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: +@@ -853,13 +845,11 @@ static void fs210x_start_work(struct work_struct *work) + + fs210x = container_of(work, struct fs210x_priv, start_work.work); + +- mutex_lock(&fs210x->lock); ++ guard(mutex)(&fs210x->lock); + + ret = fs210x_dev_play(fs210x); + if (ret) + dev_err(fs210x->dev, "Failed to start playing: %d\n", ret); +- +- mutex_unlock(&fs210x->lock); + } + + static void fs210x_fault_check_work(struct work_struct *work) +@@ -870,15 +860,13 @@ static void fs210x_fault_check_work(struct work_struct *work) + + fs210x = container_of(work, struct fs210x_priv, fault_check_work.work); + +- mutex_lock(&fs210x->lock); ++ scoped_guard(mutex, &fs210x->lock) { ++ if (!fs210x->is_inited || fs210x->is_suspended || !fs210x->is_playing) ++ return; + +- if (!fs210x->is_inited || fs210x->is_suspended || !fs210x->is_playing) { +- mutex_unlock(&fs210x->lock); +- return; ++ ret = fs210x_reg_read(fs210x, FS210X_05H_ANASTAT, &status); + } + +- ret = fs210x_reg_read(fs210x, FS210X_05H_ANASTAT, &status); +- mutex_unlock(&fs210x->lock); + if (ret) + return; + +@@ -991,7 +979,7 @@ static int fs210x_effect_scene_get(struct snd_kcontrol *kcontrol, + if (fs210x->scene_id < 1) + return -EINVAL; + +- mutex_lock(&fs210x->lock); ++ guard(mutex)(&fs210x->lock); + /* + * FS210x has scene(s) as below: + * init scene: id = 0 +@@ -1000,7 +988,6 @@ static int fs210x_effect_scene_get(struct snd_kcontrol *kcontrol, + */ + index = fs210x->scene_id - 1; + ucontrol->value.integer.value[0] = index; +- mutex_unlock(&fs210x->lock); + + return 0; + } +@@ -1019,7 +1006,7 @@ static int fs210x_effect_scene_put(struct snd_kcontrol *kcontrol, + return -EINVAL; + } + +- mutex_lock(&fs210x->lock); ++ guard(mutex)(&fs210x->lock); + + /* + * FS210x has scene(s) as below: +@@ -1029,17 +1016,14 @@ static int fs210x_effect_scene_put(struct snd_kcontrol *kcontrol, + */ + scene_id = ucontrol->value.integer.value[0] + 1; + scene_count = fs210x->amp_lib.scene_count - 1; /* Skip init scene */ +- if (scene_id < 1 || scene_id > scene_count) { +- mutex_unlock(&fs210x->lock); ++ if (scene_id < 1 || scene_id > scene_count) + return -ERANGE; +- } + + if (scene_id != fs210x->scene_id) + is_changed = true; + + if (fs210x->is_suspended) { + fs210x->scene_id = scene_id; +- mutex_unlock(&fs210x->lock); + return is_changed; + } + +@@ -1047,8 +1031,6 @@ static int fs210x_effect_scene_put(struct snd_kcontrol *kcontrol, + if (ret) + dev_err(fs210x->dev, "Failed to set scene: %d\n", ret); + +- mutex_unlock(&fs210x->lock); +- + if (!ret && is_changed) + return 1; + +@@ -1062,12 +1044,10 @@ static int fs210x_playback_event(struct snd_soc_dapm_widget *w, + struct fs210x_priv *fs210x = snd_soc_component_get_drvdata(cmpnt); + int ret = 0; + +- mutex_lock(&fs210x->lock); ++ guard(mutex)(&fs210x->lock); + +- if (fs210x->is_suspended) { +- mutex_unlock(&fs210x->lock); ++ if (fs210x->is_suspended) + return 0; +- } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: +@@ -1088,8 +1068,6 @@ static int fs210x_playback_event(struct snd_soc_dapm_widget *w, + break; + } + +- mutex_unlock(&fs210x->lock); +- + return ret; + } + +@@ -1220,11 +1198,9 @@ static int fs210x_probe(struct snd_soc_component *cmpnt) + if (ret) + return ret; + +- mutex_lock(&fs210x->lock); +- ret = fs210x_init_chip(fs210x); +- mutex_unlock(&fs210x->lock); ++ guard(mutex)(&fs210x->lock); + +- return ret; ++ return fs210x_init_chip(fs210x); + } + + static void fs210x_remove(struct snd_soc_component *cmpnt) +@@ -1251,15 +1227,15 @@ static int fs210x_suspend(struct snd_soc_component *cmpnt) + + regcache_cache_only(fs210x->regmap, true); + +- mutex_lock(&fs210x->lock); +- fs210x->cur_scene = NULL; +- fs210x->is_inited = false; +- fs210x->is_playing = false; +- fs210x->is_suspended = true; ++ scoped_guard(mutex, &fs210x->lock) { ++ fs210x->cur_scene = NULL; ++ fs210x->is_inited = false; ++ fs210x->is_playing = false; ++ fs210x->is_suspended = true; + +- gpiod_set_value_cansleep(fs210x->gpio_sdz, 1); /* Active */ +- fsleep(30000); /* >= 30ms */ +- mutex_unlock(&fs210x->lock); ++ gpiod_set_value_cansleep(fs210x->gpio_sdz, 1); /* Active */ ++ fsleep(30000); /* >= 30ms */ ++ } + + cancel_delayed_work_sync(&fs210x->start_work); + cancel_delayed_work_sync(&fs210x->fault_check_work); +@@ -1288,14 +1264,11 @@ static int fs210x_resume(struct snd_soc_component *cmpnt) + return ret; + } + +- mutex_lock(&fs210x->lock); ++ guard(mutex)(&fs210x->lock); + + fs210x->is_suspended = false; +- ret = fs210x_init_chip(fs210x); + +- mutex_unlock(&fs210x->lock); +- +- return ret; ++ return fs210x_init_chip(fs210x); + } + #else + #define fs210x_suspend NULL +diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c +--- a/sound/soc/codecs/hdac_hda.c ++++ b/sound/soc/codecs/hdac_hda.c +@@ -437,7 +437,7 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) + + #ifdef CONFIG_SND_HDA_PATCH_LOADER + if (loadable_patch[hda_pvt->dev_index] && *loadable_patch[hda_pvt->dev_index]) { +- const struct firmware *fw; ++ const struct firmware *fw __free(firmware) = NULL; + + dev_info(&hdev->dev, "Applying patch firmware '%s'\n", + loadable_patch[hda_pvt->dev_index]); +@@ -451,7 +451,6 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) + dev_err(&hdev->dev, "%s: failed to load hda patch %d\n", __func__, ret); + goto error_no_pm; + } +- release_firmware(fw); + } + } + #endif +diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c +--- a/sound/soc/codecs/hdac_hdmi.c ++++ b/sound/soc/codecs/hdac_hdmi.c +@@ -10,6 +10,7 @@ + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + ++#include + #include + #include + #include +@@ -537,10 +538,11 @@ static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt( + continue; + + list_for_each_entry(port, &pcm->port_list, head) { +- mutex_lock(&pcm->lock); +- ret = hdac_hdmi_query_port_connlist(hdev, +- port->pin, port); +- mutex_unlock(&pcm->lock); ++ scoped_guard(mutex, &pcm->lock) { ++ ret = hdac_hdmi_query_port_connlist(hdev, ++ port->pin, ++ port); ++ } + if (ret < 0) + continue; + +@@ -640,11 +642,11 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, + pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt); + + if (pcm) { +- mutex_lock(&pcm->lock); +- pcm->chmap_set = false; +- memset(pcm->chmap, 0, sizeof(pcm->chmap)); +- pcm->channels = 0; +- mutex_unlock(&pcm->lock); ++ scoped_guard(mutex, &pcm->lock) { ++ pcm->chmap_set = false; ++ memset(pcm->chmap, 0, sizeof(pcm->chmap)); ++ pcm->channels = 0; ++ } + } + + if (dai_map->port) +@@ -922,7 +924,7 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, + if (port == NULL) + return -EINVAL; + +- mutex_lock(&hdmi->pin_mutex); ++ guard(mutex)(&hdmi->pin_mutex); + list_for_each_entry(pcm, &hdmi->pcm_list, head) { + if (list_empty(&pcm->port_list)) + continue; +@@ -945,12 +947,10 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, + list_add_tail(&port->head, &pcm->port_list); + if (port->eld.monitor_present && port->eld.eld_valid) { + hdac_hdmi_jack_report_sync(pcm, port, true); +- mutex_unlock(&hdmi->pin_mutex); + return ret; + } + } + } +- mutex_unlock(&hdmi->pin_mutex); + + return ret; + } +@@ -1274,67 +1274,65 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, + * In case of non MST pin, get_eld info API expectes port + * to be -1. + */ +- mutex_lock(&hdmi->pin_mutex); +- port->eld.monitor_present = false; ++ scoped_guard(mutex, &hdmi->pin_mutex) { ++ port->eld.monitor_present = false; + +- if (pin->mst_capable) +- port_id = port->id; ++ if (pin->mst_capable) ++ port_id = port->id; + +- size = snd_hdac_acomp_get_eld(hdev, pin->nid, port_id, +- &port->eld.monitor_present, +- port->eld.eld_buffer, +- ELD_MAX_SIZE); ++ size = snd_hdac_acomp_get_eld(hdev, pin->nid, port_id, ++ &port->eld.monitor_present, ++ port->eld.eld_buffer, ++ ELD_MAX_SIZE); + +- if (size > 0) { +- size = min(size, ELD_MAX_SIZE); +- if (hdac_hdmi_parse_eld(hdev, port) < 0) +- size = -EINVAL; +- } +- +- eld_valid = port->eld.eld_valid; +- +- if (size > 0) { +- port->eld.eld_valid = true; +- port->eld.eld_size = size; +- } else { +- port->eld.eld_valid = false; +- port->eld.eld_size = 0; +- } +- +- eld_changed = (eld_valid != port->eld.eld_valid); +- +- pcm = hdac_hdmi_get_pcm(hdev, port); +- +- if (!port->eld.monitor_present || !port->eld.eld_valid) { +- +- dev_dbg(&hdev->dev, "%s: disconnect for pin:port %d:%d\n", +- __func__, pin->nid, port->id); +- +- /* +- * PCMs are not registered during device probe, so don't +- * report jack here. It will be done in usermode mux +- * control select. +- */ +- if (pcm) { +- hdac_hdmi_jack_report(pcm, port, false); +- schedule_work(&port->dapm_work); ++ if (size > 0) { ++ size = min(size, ELD_MAX_SIZE); ++ if (hdac_hdmi_parse_eld(hdev, port) < 0) ++ size = -EINVAL; + } + +- mutex_unlock(&hdmi->pin_mutex); +- return; +- } ++ eld_valid = port->eld.eld_valid; + +- if (port->eld.monitor_present && port->eld.eld_valid) { +- if (pcm) { +- hdac_hdmi_jack_report(pcm, port, true); +- schedule_work(&port->dapm_work); ++ if (size > 0) { ++ port->eld.eld_valid = true; ++ port->eld.eld_size = size; ++ } else { ++ port->eld.eld_valid = false; ++ port->eld.eld_size = 0; + } + +- print_hex_dump_debug("ELD: ", DUMP_PREFIX_OFFSET, 16, 1, +- port->eld.eld_buffer, port->eld.eld_size, false); ++ eld_changed = (eld_valid != port->eld.eld_valid); + ++ pcm = hdac_hdmi_get_pcm(hdev, port); ++ ++ if (!port->eld.monitor_present || !port->eld.eld_valid) { ++ ++ dev_dbg(&hdev->dev, "%s: disconnect for pin:port %d:%d\n", ++ __func__, pin->nid, port->id); ++ ++ /* ++ * PCMs are not registered during device probe, so don't ++ * report jack here. It will be done in usermode mux ++ * control select. ++ */ ++ if (pcm) { ++ hdac_hdmi_jack_report(pcm, port, false); ++ schedule_work(&port->dapm_work); ++ } ++ ++ return; ++ } ++ ++ if (port->eld.monitor_present && port->eld.eld_valid) { ++ if (pcm) { ++ hdac_hdmi_jack_report(pcm, port, true); ++ schedule_work(&port->dapm_work); ++ } ++ ++ print_hex_dump_debug("ELD: ", DUMP_PREFIX_OFFSET, 16, 1, ++ port->eld.eld_buffer, port->eld.eld_size, false); ++ } + } +- mutex_unlock(&hdmi->pin_mutex); + + if (eld_changed && pcm) + snd_ctl_notify(hdmi->card, +@@ -1795,13 +1793,12 @@ static void hdac_hdmi_set_chmap(struct hdac_device *hdev, int pcm_idx, + if (list_empty(&pcm->port_list)) + return; + +- mutex_lock(&pcm->lock); ++ guard(mutex)(&pcm->lock); + pcm->chmap_set = true; + memcpy(pcm->chmap, chmap, ARRAY_SIZE(pcm->chmap)); + list_for_each_entry(port, &pcm->port_list, head) + if (prepared) + hdac_hdmi_setup_audio_infoframe(hdev, pcm, port); +- mutex_unlock(&pcm->lock); + } + + static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdev, int pcm_idx) +@@ -1866,8 +1863,10 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) + snd_hdac_ext_bus_link_get(hdev->bus, hlink); + + hdmi_priv = devm_kzalloc(&hdev->dev, sizeof(*hdmi_priv), GFP_KERNEL); +- if (hdmi_priv == NULL) ++ if (hdmi_priv == NULL) { ++ snd_hdac_ext_bus_link_put(hdev->bus, hlink); + return -ENOMEM; ++ } + + snd_hdac_register_chmap_ops(hdev, &hdmi_priv->chmap); + hdmi_priv->chmap.ops.get_chmap = hdac_hdmi_get_chmap; +@@ -1876,8 +1875,10 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) + hdmi_priv->chmap.ops.get_spk_alloc = hdac_hdmi_get_spk_alloc; + hdmi_priv->hdev = hdev; + +- if (!hdac_id) ++ if (!hdac_id) { ++ snd_hdac_ext_bus_link_put(hdev->bus, hlink); + return -ENODEV; ++ } + + if (hdac_id->driver_data) + hdmi_priv->drv_data = +@@ -1902,6 +1903,8 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) + if (ret < 0) { + dev_err(&hdev->dev, + "Failed in parse and map nid with err: %d\n", ret); ++ snd_hdac_ext_bus_link_put(hdev->bus, hlink); ++ snd_hdac_display_power(hdev->bus, hdev->addr, false); + return ret; + } + snd_hdac_refresh_widgets(hdev); +diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c +--- a/sound/soc/codecs/hdmi-codec.c ++++ b/sound/soc/codecs/hdmi-codec.c +@@ -4,6 +4,7 @@ + * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ + * Author: Jyri Sarha + */ ++#include + #include + #include + #include +@@ -456,31 +457,30 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream, + if (!((has_playback && tx) || (has_capture && !tx))) + return 0; + +- mutex_lock(&hcp->lock); ++ guard(mutex)(&hcp->lock); + if (hcp->busy) { + dev_err(dai->dev, "Only one simultaneous stream supported!\n"); +- mutex_unlock(&hcp->lock); + return -EINVAL; + } + + if (hcp->hcd.ops->audio_startup) { + ret = hcp->hcd.ops->audio_startup(dai->dev->parent, hcp->hcd.data); + if (ret) +- goto err; ++ return ret; + } + + if (tx && hcp->hcd.ops->get_eld) { + ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->hcd.data, + hcp->eld, sizeof(hcp->eld)); + if (ret) +- goto err; ++ return ret; + + snd_parse_eld(dai->dev, &hcp->eld_parsed, + hcp->eld, sizeof(hcp->eld)); + + ret = snd_pcm_hw_constraint_eld(substream->runtime, hcp->eld); + if (ret) +- goto err; ++ return ret; + + /* Select chmap supported */ + hdmi_codec_eld_chmap(hcp); +@@ -488,8 +488,6 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream, + + hcp->busy = true; + +-err: +- mutex_unlock(&hcp->lock); + return ret; + } + +@@ -507,9 +505,8 @@ static void hdmi_codec_shutdown(struct snd_pcm_substream *substream, + hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; + hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data); + +- mutex_lock(&hcp->lock); ++ guard(mutex)(&hcp->lock); + hcp->busy = false; +- mutex_unlock(&hcp->lock); + } + + static int hdmi_codec_fill_codec_params(struct snd_soc_dai *dai, +diff --git a/sound/soc/codecs/idt821034.c b/sound/soc/codecs/idt821034.c +--- a/sound/soc/codecs/idt821034.c ++++ b/sound/soc/codecs/idt821034.c +@@ -7,6 +7,7 @@ + // Author: Herve Codina + + #include ++#include + #include + #include + #include +@@ -413,12 +414,12 @@ static int idt821034_kctrl_gain_get(struct snd_kcontrol *kcontrol, + + ch = IDT821034_ID_GET_CHAN(mc->reg); + +- mutex_lock(&idt821034->mutex); +- if (IDT821034_ID_IS_OUT(mc->reg)) +- val = idt821034->amps.ch[ch].amp_out.gain; +- else +- val = idt821034->amps.ch[ch].amp_in.gain; +- mutex_unlock(&idt821034->mutex); ++ scoped_guard(mutex, &idt821034->mutex) { ++ if (IDT821034_ID_IS_OUT(mc->reg)) ++ val = idt821034->amps.ch[ch].amp_out.gain; ++ else ++ val = idt821034->amps.ch[ch].amp_in.gain; ++ } + + ucontrol->value.integer.value[0] = val & mask; + if (invert) +@@ -456,7 +457,7 @@ static int idt821034_kctrl_gain_put(struct snd_kcontrol *kcontrol, + + ch = IDT821034_ID_GET_CHAN(mc->reg); + +- mutex_lock(&idt821034->mutex); ++ guard(mutex)(&idt821034->mutex); + + if (IDT821034_ID_IS_OUT(mc->reg)) { + amp = &idt821034->amps.ch[ch].amp_out; +@@ -466,22 +467,18 @@ static int idt821034_kctrl_gain_put(struct snd_kcontrol *kcontrol, + gain_type = IDT821034_GAIN_TX; + } + +- if (amp->gain == val) { +- ret = 0; +- goto end; +- } ++ if (amp->gain == val) ++ return 0; + + if (!amp->is_muted) { + ret = idt821034_set_gain_channel(idt821034, ch, gain_type, val); + if (ret) +- goto end; ++ return ret; + } + + amp->gain = val; +- ret = 1; /* The value changed */ +-end: +- mutex_unlock(&idt821034->mutex); +- return ret; ++ ++ return 1; + } + + static int idt821034_kctrl_mute_get(struct snd_kcontrol *kcontrol, +@@ -495,11 +492,11 @@ static int idt821034_kctrl_mute_get(struct snd_kcontrol *kcontrol, + + ch = IDT821034_ID_GET_CHAN(id); + +- mutex_lock(&idt821034->mutex); +- is_muted = IDT821034_ID_IS_OUT(id) ? +- idt821034->amps.ch[ch].amp_out.is_muted : +- idt821034->amps.ch[ch].amp_in.is_muted; +- mutex_unlock(&idt821034->mutex); ++ scoped_guard(mutex, &idt821034->mutex) { ++ is_muted = IDT821034_ID_IS_OUT(id) ? ++ idt821034->amps.ch[ch].amp_out.is_muted : ++ idt821034->amps.ch[ch].amp_in.is_muted; ++ } + + ucontrol->value.integer.value[0] = !is_muted; + +@@ -521,7 +518,7 @@ static int idt821034_kctrl_mute_put(struct snd_kcontrol *kcontrol, + ch = IDT821034_ID_GET_CHAN(id); + is_mute = !ucontrol->value.integer.value[0]; + +- mutex_lock(&idt821034->mutex); ++ guard(mutex)(&idt821034->mutex); + + if (IDT821034_ID_IS_OUT(id)) { + amp = &idt821034->amps.ch[ch].amp_out; +@@ -531,21 +528,17 @@ static int idt821034_kctrl_mute_put(struct snd_kcontrol *kcontrol, + gain_type = IDT821034_GAIN_TX; + } + +- if (amp->is_muted == is_mute) { +- ret = 0; +- goto end; +- } ++ if (amp->is_muted == is_mute) ++ return 0; + + ret = idt821034_set_gain_channel(idt821034, ch, gain_type, + is_mute ? 0 : amp->gain); + if (ret) +- goto end; ++ return ret; + + amp->is_muted = is_mute; +- ret = 1; /* The value changed */ +-end: +- mutex_unlock(&idt821034->mutex); +- return ret; ++ ++ return 1; + } + + static const DECLARE_TLV_DB_LINEAR(idt821034_gain_in, -300, 1300); +@@ -623,24 +616,20 @@ static int idt821034_power_event(struct snd_soc_dapm_widget *w, + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component); + unsigned int id = w->shift; + u8 power, mask; +- int ret; + u8 ch; + + ch = IDT821034_ID_GET_CHAN(id); + mask = IDT821034_ID_IS_OUT(id) ? IDT821034_CONF_PWRUP_RX : IDT821034_CONF_PWRUP_TX; + +- mutex_lock(&idt821034->mutex); ++ guard(mutex)(&idt821034->mutex); + + power = idt821034_get_channel_power(idt821034, ch); + if (SND_SOC_DAPM_EVENT_ON(event)) + power |= mask; + else + power &= ~mask; +- ret = idt821034_set_channel_power(idt821034, ch, power); + +- mutex_unlock(&idt821034->mutex); +- +- return ret; ++ return idt821034_set_channel_power(idt821034, ch, power); + } + + static const struct snd_soc_dapm_widget idt821034_dapm_widgets[] = { +@@ -717,9 +706,9 @@ static int idt821034_dai_set_tdm_slot(struct snd_soc_dai *dai, + ch = 0; + while (mask && ch < IDT821034_NB_CHANNEL) { + if (mask & 0x1) { +- mutex_lock(&idt821034->mutex); +- ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_RX, slot); +- mutex_unlock(&idt821034->mutex); ++ scoped_guard(mutex, &idt821034->mutex) ++ ret = idt821034_set_channel_ts(idt821034, ch, ++ IDT821034_CH_RX, slot); + if (ret) { + dev_err(dai->dev, "ch%u set tx tdm slot failed (%d)\n", + ch, ret); +@@ -742,9 +731,9 @@ static int idt821034_dai_set_tdm_slot(struct snd_soc_dai *dai, + ch = 0; + while (mask && ch < IDT821034_NB_CHANNEL) { + if (mask & 0x1) { +- mutex_lock(&idt821034->mutex); +- ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_TX, slot); +- mutex_unlock(&idt821034->mutex); ++ scoped_guard(mutex, &idt821034->mutex) ++ ret = idt821034_set_channel_ts(idt821034, ch, ++ IDT821034_CH_TX, slot); + if (ret) { + dev_err(dai->dev, "ch%u set rx tdm slot failed (%d)\n", + ch, ret); +@@ -769,9 +758,8 @@ static int idt821034_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) + { + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component); + u8 conf; +- int ret; + +- mutex_lock(&idt821034->mutex); ++ guard(mutex)(&idt821034->mutex); + + conf = idt821034_get_codec_conf(idt821034); + +@@ -785,13 +773,10 @@ static int idt821034_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) + default: + dev_err(dai->dev, "Unsupported DAI format 0x%x\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); +- ret = -EINVAL; +- goto end; ++ return -EINVAL; + } +- ret = idt821034_set_codec_conf(idt821034, conf); +-end: +- mutex_unlock(&idt821034->mutex); +- return ret; ++ ++ return idt821034_set_codec_conf(idt821034, conf); + } + + static int idt821034_dai_hw_params(struct snd_pcm_substream *substream, +@@ -800,9 +785,8 @@ static int idt821034_dai_hw_params(struct snd_pcm_substream *substream, + { + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component); + u8 conf; +- int ret; + +- mutex_lock(&idt821034->mutex); ++ guard(mutex)(&idt821034->mutex); + + conf = idt821034_get_codec_conf(idt821034); + +@@ -816,13 +800,10 @@ static int idt821034_dai_hw_params(struct snd_pcm_substream *substream, + default: + dev_err(dai->dev, "Unsupported PCM format 0x%x\n", + params_format(params)); +- ret = -EINVAL; +- goto end; ++ return -EINVAL; + } +- ret = idt821034_set_codec_conf(idt821034, conf); +-end: +- mutex_unlock(&idt821034->mutex); +- return ret; ++ ++ return idt821034_set_codec_conf(idt821034, conf); + } + + static const unsigned int idt821034_sample_bits[] = {8}; +@@ -897,11 +878,11 @@ static int idt821034_reset_audio(struct idt821034 *idt821034) + int ret; + u8 i; + +- mutex_lock(&idt821034->mutex); ++ guard(mutex)(&idt821034->mutex); + + ret = idt821034_set_codec_conf(idt821034, 0); + if (ret) +- goto end; ++ return ret; + + for (i = 0; i < IDT821034_NB_CHANNEL; i++) { + idt821034->amps.ch[i].amp_out.gain = IDT821034_GAIN_OUT_INIT_RAW; +@@ -909,24 +890,21 @@ static int idt821034_reset_audio(struct idt821034 *idt821034) + ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_RX, + idt821034->amps.ch[i].amp_out.gain); + if (ret) +- goto end; ++ return ret; + + idt821034->amps.ch[i].amp_in.gain = IDT821034_GAIN_IN_INIT_RAW; + idt821034->amps.ch[i].amp_in.is_muted = false; + ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_TX, + idt821034->amps.ch[i].amp_in.gain); + if (ret) +- goto end; ++ return ret; + + ret = idt821034_set_channel_power(idt821034, i, 0); + if (ret) +- goto end; ++ return ret; + } + +- ret = 0; +-end: +- mutex_unlock(&idt821034->mutex); +- return ret; ++ return 0; + } + + static int idt821034_component_probe(struct snd_soc_component *component) +@@ -965,7 +943,7 @@ static int idt821034_chip_gpio_set(struct gpio_chip *c, unsigned int offset, + u8 slic_raw; + int ret; + +- mutex_lock(&idt821034->mutex); ++ guard(mutex)(&idt821034->mutex); + + slic_raw = idt821034_get_written_slic_raw(idt821034, ch); + if (val) +@@ -974,8 +952,6 @@ static int idt821034_chip_gpio_set(struct gpio_chip *c, unsigned int offset, + slic_raw &= ~mask; + ret = idt821034_write_slic_raw(idt821034, ch, slic_raw); + +- mutex_unlock(&idt821034->mutex); +- + if (ret) + dev_err(&idt821034->spi->dev, "set gpio %d (%u, 0x%x) failed (%d)\n", + offset, ch, mask, ret); +@@ -991,9 +967,8 @@ static int idt821034_chip_gpio_get(struct gpio_chip *c, unsigned int offset) + u8 slic_raw; + int ret; + +- mutex_lock(&idt821034->mutex); +- ret = idt821034_read_slic_raw(idt821034, ch, &slic_raw); +- mutex_unlock(&idt821034->mutex); ++ scoped_guard(mutex, &idt821034->mutex) ++ ret = idt821034_read_slic_raw(idt821034, ch, &slic_raw); + if (ret) { + dev_err(&idt821034->spi->dev, "get gpio %d (%u, 0x%x) failed (%d)\n", + offset, ch, mask, ret); +@@ -1015,9 +990,8 @@ static int idt821034_chip_get_direction(struct gpio_chip *c, unsigned int offset + struct idt821034 *idt821034 = gpiochip_get_data(c); + u8 slic_dir; + +- mutex_lock(&idt821034->mutex); ++ guard(mutex)(&idt821034->mutex); + slic_dir = idt821034_get_slic_conf(idt821034, ch); +- mutex_unlock(&idt821034->mutex); + + return slic_dir & mask ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT; + } +@@ -1034,7 +1008,7 @@ static int idt821034_chip_direction_input(struct gpio_chip *c, unsigned int offs + if (mask & ~(IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN)) + return -EPERM; + +- mutex_lock(&idt821034->mutex); ++ guard(mutex)(&idt821034->mutex); + + slic_conf = idt821034_get_slic_conf(idt821034, ch) | mask; + +@@ -1044,7 +1018,6 @@ static int idt821034_chip_direction_input(struct gpio_chip *c, unsigned int offs + offset, ch, mask, ret); + } + +- mutex_unlock(&idt821034->mutex); + return ret; + } + +@@ -1060,7 +1033,7 @@ static int idt821034_chip_direction_output(struct gpio_chip *c, unsigned int off + if (ret) + return ret; + +- mutex_lock(&idt821034->mutex); ++ guard(mutex)(&idt821034->mutex); + + slic_conf = idt821034_get_slic_conf(idt821034, ch) & ~mask; + +@@ -1070,7 +1043,6 @@ static int idt821034_chip_direction_output(struct gpio_chip *c, unsigned int off + offset, ch, mask, ret); + } + +- mutex_unlock(&idt821034->mutex); + return ret; + } + +@@ -1079,24 +1051,22 @@ static int idt821034_reset_gpio(struct idt821034 *idt821034) + int ret; + u8 i; + +- mutex_lock(&idt821034->mutex); ++ guard(mutex)(&idt821034->mutex); + + /* IO0 and IO1 as input for all channels and output IO set to 0 */ + for (i = 0; i < IDT821034_NB_CHANNEL; i++) { + ret = idt821034_set_slic_conf(idt821034, i, + IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN); + if (ret) +- goto end; ++ return ret; + + ret = idt821034_write_slic_raw(idt821034, i, 0); + if (ret) +- goto end; ++ return ret; + + } +- ret = 0; +-end: +- mutex_unlock(&idt821034->mutex); +- return ret; ++ ++ return 0; + } + + static int idt821034_gpio_init(struct idt821034 *idt821034) +diff --git a/sound/soc/codecs/lochnagar-sc.c b/sound/soc/codecs/lochnagar-sc.c +--- a/sound/soc/codecs/lochnagar-sc.c ++++ b/sound/soc/codecs/lochnagar-sc.c +@@ -137,15 +137,23 @@ static int lochnagar_sc_set_usb_fmt(struct snd_soc_dai *dai, unsigned int fmt) + return lochnagar_sc_check_fmt(dai, fmt, SND_SOC_DAIFMT_CBP_CFP); + } + ++static const u64 lochnagar_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF; ++ + static const struct snd_soc_dai_ops lochnagar_sc_line_ops = { + .startup = lochnagar_sc_line_startup, + .shutdown = lochnagar_sc_line_shutdown, + .set_fmt = lochnagar_sc_set_line_fmt, ++ .auto_selectable_formats = &lochnagar_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static const struct snd_soc_dai_ops lochnagar_sc_usb_ops = { + .startup = lochnagar_sc_startup, + .set_fmt = lochnagar_sc_set_usb_fmt, ++ .auto_selectable_formats = &lochnagar_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static struct snd_soc_dai_driver lochnagar_sc_dai[] = { +diff --git a/sound/soc/codecs/lpass-macro-common.c b/sound/soc/codecs/lpass-macro-common.c +--- a/sound/soc/codecs/lpass-macro-common.c ++++ b/sound/soc/codecs/lpass-macro-common.c +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: GPL-2.0-only + // Copyright (c) 2022, The Linux Foundation. All rights reserved. + ++#include + #include + #include + #include +@@ -71,21 +72,16 @@ EXPORT_SYMBOL_GPL(lpass_macro_pds_exit); + + void lpass_macro_set_codec_version(enum lpass_codec_version version) + { +- mutex_lock(&lpass_codec_mutex); ++ guard(mutex)(&lpass_codec_mutex); + lpass_codec_version = version; +- mutex_unlock(&lpass_codec_mutex); + } + EXPORT_SYMBOL_GPL(lpass_macro_set_codec_version); + + enum lpass_codec_version lpass_macro_get_codec_version(void) + { +- enum lpass_codec_version ver; ++ guard(mutex)(&lpass_codec_mutex); + +- mutex_lock(&lpass_codec_mutex); +- ver = lpass_codec_version; +- mutex_unlock(&lpass_codec_mutex); +- +- return ver; ++ return lpass_codec_version; + } + EXPORT_SYMBOL_GPL(lpass_macro_get_codec_version); + +diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c +--- a/sound/soc/codecs/lpass-rx-macro.c ++++ b/sound/soc/codecs/lpass-rx-macro.c +@@ -6,6 +6,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -2030,9 +2031,10 @@ static struct snd_soc_dai_driver rx_macro_dai[] = { + }, + }; + +-static void rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable) ++static int rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable) + { + struct regmap *regmap = rx->regmap; ++ int ret; + + if (mclk_enable) { + if (rx->rx_mclk_users == 0) { +@@ -2047,14 +2049,16 @@ static void rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable) + CDC_RX_FS_MCLK_CNT_EN_MASK, + CDC_RX_FS_MCLK_CNT_ENABLE); + regcache_mark_dirty(regmap); +- regcache_sync(regmap); ++ ret = regcache_sync(regmap); ++ if (ret) ++ return ret; + } + rx->rx_mclk_users++; + } else { + if (rx->rx_mclk_users <= 0) { + dev_err(rx->dev, "%s: clock already disabled\n", __func__); + rx->rx_mclk_users = 0; +- return; ++ return 0; + } + rx->rx_mclk_users--; + if (rx->rx_mclk_users == 0) { +@@ -2068,6 +2072,8 @@ static void rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable) + CDC_RX_CLK_MCLK2_EN_MASK, 0x0); + } + } ++ ++ return 0; + } + + static int rx_macro_mclk_event(struct snd_soc_dapm_widget *w, +@@ -2079,11 +2085,9 @@ static int rx_macro_mclk_event(struct snd_soc_dapm_widget *w, + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: +- rx_macro_mclk_enable(rx, true); +- break; ++ return rx_macro_mclk_enable(rx, true); + case SND_SOC_DAPM_POST_PMD: +- rx_macro_mclk_enable(rx, false); +- break; ++ return rx_macro_mclk_enable(rx, false); + default: + dev_err(component->dev, "%s: invalid DAPM event %d\n", __func__, event); + ret = -EINVAL; +@@ -3671,14 +3675,16 @@ static int swclk_gate_enable(struct clk_hw *hw) + struct rx_macro *rx = to_rx_macro(hw); + int ret; + +- ret = clk_prepare_enable(rx->mclk); ++ ret = pm_runtime_resume_and_get(rx->dev); ++ if (ret < 0) ++ return ret; ++ ++ ret = rx_macro_mclk_enable(rx, true); + if (ret) { +- dev_err(rx->dev, "unable to prepare mclk\n"); ++ clk_disable_unprepare(rx->mclk); + return ret; + } + +- rx_macro_mclk_enable(rx, true); +- + regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, + CDC_RX_SWR_CLK_EN_MASK, 1); + +@@ -3693,7 +3699,7 @@ static void swclk_gate_disable(struct clk_hw *hw) + CDC_RX_SWR_CLK_EN_MASK, 0); + + rx_macro_mclk_enable(rx, false); +- clk_disable_unprepare(rx->mclk); ++ pm_runtime_put_autosuspend(rx->dev); + } + + static int swclk_gate_is_enabled(struct clk_hw *hw) +@@ -3864,28 +3870,31 @@ static int rx_macro_probe(struct platform_device *pdev) + rx->dev = dev; + + /* set MCLK and NPL rates */ +- clk_set_rate(rx->mclk, MCLK_FREQ); +- clk_set_rate(rx->npl, MCLK_FREQ); +- +- ret = clk_prepare_enable(rx->macro); ++ ret = clk_set_rate(rx->mclk, MCLK_FREQ); + if (ret) + return ret; + +- ret = clk_prepare_enable(rx->dcodec); ++ ret = clk_set_rate(rx->npl, MCLK_FREQ); + if (ret) +- goto err_dcodec; ++ return ret; + +- ret = clk_prepare_enable(rx->mclk); ++ ret = devm_pm_clk_create(dev); + if (ret) +- goto err_mclk; ++ return ret; + +- ret = clk_prepare_enable(rx->npl); +- if (ret) +- goto err_npl; ++ ret = of_pm_clk_add_clks(dev); ++ if (ret < 0) ++ return ret; + +- ret = clk_prepare_enable(rx->fsgen); ++ pm_runtime_set_autosuspend_delay(dev, 100); ++ pm_runtime_use_autosuspend(dev); ++ ret = devm_pm_runtime_enable(dev); + if (ret) +- goto err_fsgen; ++ return ret; ++ ++ ret = pm_runtime_resume_and_get(dev); ++ if (ret) ++ return ret; + + /* reset swr block */ + regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, +@@ -3902,46 +3911,25 @@ static int rx_macro_probe(struct platform_device *pdev) + rx_macro_dai, + ARRAY_SIZE(rx_macro_dai)); + if (ret) +- goto err_clkout; +- +- +- pm_runtime_set_autosuspend_delay(dev, 3000); +- pm_runtime_use_autosuspend(dev); +- pm_runtime_mark_last_busy(dev); +- pm_runtime_set_active(dev); +- pm_runtime_enable(dev); ++ goto err_rpm_put; + + ret = rx_macro_register_mclk_output(rx); + if (ret) +- goto err_clkout; ++ goto err_rpm_put; ++ ++ ret = pm_runtime_put_autosuspend(dev); ++ if (ret < 0) ++ dev_warn(dev, "runtime PM put failed after probe: %d\n", ret); + + return 0; + +-err_clkout: +- clk_disable_unprepare(rx->fsgen); +-err_fsgen: +- clk_disable_unprepare(rx->npl); +-err_npl: +- clk_disable_unprepare(rx->mclk); +-err_mclk: +- clk_disable_unprepare(rx->dcodec); +-err_dcodec: +- clk_disable_unprepare(rx->macro); ++err_rpm_put: ++ if (pm_runtime_put_sync_suspend(dev) < 0) ++ dev_warn(dev, "runtime PM sync suspend failed in probe unwind\n"); + + return ret; + } + +-static void rx_macro_remove(struct platform_device *pdev) +-{ +- struct rx_macro *rx = dev_get_drvdata(&pdev->dev); +- +- clk_disable_unprepare(rx->mclk); +- clk_disable_unprepare(rx->npl); +- clk_disable_unprepare(rx->fsgen); +- clk_disable_unprepare(rx->macro); +- clk_disable_unprepare(rx->dcodec); +-} +- + static const struct of_device_id rx_macro_dt_match[] = { + { + .compatible = "qcom,sc7280-lpass-rx-macro", +@@ -3969,13 +3957,17 @@ MODULE_DEVICE_TABLE(of, rx_macro_dt_match); + static int rx_macro_runtime_suspend(struct device *dev) + { + struct rx_macro *rx = dev_get_drvdata(dev); ++ int ret; + + regcache_cache_only(rx->regmap, true); +- regcache_mark_dirty(rx->regmap); + +- clk_disable_unprepare(rx->fsgen); +- clk_disable_unprepare(rx->npl); +- clk_disable_unprepare(rx->mclk); ++ ret = pm_clk_suspend(dev); ++ if (ret) { ++ regcache_cache_only(rx->regmap, false); ++ return ret; ++ } ++ ++ regcache_mark_dirty(rx->regmap); + + return 0; + } +@@ -3985,33 +3977,23 @@ static int rx_macro_runtime_resume(struct device *dev) + struct rx_macro *rx = dev_get_drvdata(dev); + int ret; + +- ret = clk_prepare_enable(rx->mclk); ++ ret = pm_clk_resume(dev); + if (ret) { +- dev_err(dev, "unable to prepare mclk\n"); ++ regcache_cache_only(rx->regmap, true); ++ regcache_mark_dirty(rx->regmap); + return ret; + } + +- ret = clk_prepare_enable(rx->npl); +- if (ret) { +- dev_err(dev, "unable to prepare mclkx2\n"); +- goto err_npl; +- } +- +- ret = clk_prepare_enable(rx->fsgen); +- if (ret) { +- dev_err(dev, "unable to prepare fsgen\n"); +- goto err_fsgen; +- } + regcache_cache_only(rx->regmap, false); +- regcache_sync(rx->regmap); ++ ret = regcache_sync(rx->regmap); ++ if (ret) { ++ regcache_cache_only(rx->regmap, true); ++ regcache_mark_dirty(rx->regmap); ++ pm_clk_suspend(dev); ++ return ret; ++ } + + return 0; +-err_fsgen: +- clk_disable_unprepare(rx->npl); +-err_npl: +- clk_disable_unprepare(rx->mclk); +- +- return ret; + } + + static const struct dev_pm_ops rx_macro_pm_ops = { +@@ -4026,7 +4008,6 @@ static struct platform_driver rx_macro_driver = { + .pm = pm_ptr(&rx_macro_pm_ops), + }, + .probe = rx_macro_probe, +- .remove = rx_macro_remove, + }; + + module_platform_driver(rx_macro_driver); +diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c +--- a/sound/soc/codecs/lpass-tx-macro.c ++++ b/sound/soc/codecs/lpass-tx-macro.c +@@ -6,6 +6,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -614,6 +615,7 @@ static int tx_macro_mclk_enable(struct tx_macro *tx, + bool mclk_enable) + { + struct regmap *regmap = tx->regmap; ++ int ret; + + if (mclk_enable) { + if (tx->tx_mclk_users == 0) { +@@ -626,7 +628,9 @@ static int tx_macro_mclk_enable(struct tx_macro *tx, + CDC_TX_FS_CNT_EN_MASK, + CDC_TX_FS_CNT_ENABLE); + regcache_mark_dirty(regmap); +- regcache_sync(regmap); ++ ret = regcache_sync(regmap); ++ if (ret) ++ return ret; + } + tx->tx_mclk_users++; + } else { +@@ -739,11 +743,9 @@ static int tx_macro_mclk_event(struct snd_soc_dapm_widget *w, + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: +- tx_macro_mclk_enable(tx, true); +- break; ++ return tx_macro_mclk_enable(tx, true); + case SND_SOC_DAPM_POST_PMD: +- tx_macro_mclk_enable(tx, false); +- break; ++ return tx_macro_mclk_enable(tx, false); + default: + break; + } +@@ -2149,17 +2151,20 @@ static int swclk_gate_enable(struct clk_hw *hw) + struct regmap *regmap = tx->regmap; + int ret; + +- ret = clk_prepare_enable(tx->mclk); ++ ret = pm_runtime_resume_and_get(tx->dev); ++ if (ret < 0) ++ return ret; ++ ++ ret = tx_macro_mclk_enable(tx, true); + if (ret) { +- dev_err(tx->dev, "failed to enable mclk\n"); ++ pm_runtime_put_autosuspend(tx->dev); + return ret; + } + +- tx_macro_mclk_enable(tx, true); +- + regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, + CDC_TX_SWR_CLK_EN_MASK, + CDC_TX_SWR_CLK_ENABLE); ++ + return 0; + } + +@@ -2172,7 +2177,7 @@ static void swclk_gate_disable(struct clk_hw *hw) + CDC_TX_SWR_CLK_EN_MASK, 0x0); + + tx_macro_mclk_enable(tx, false); +- clk_disable_unprepare(tx->mclk); ++ pm_runtime_put_autosuspend(tx->dev); + } + + static int swclk_gate_is_enabled(struct clk_hw *hw) +@@ -2315,28 +2320,31 @@ static int tx_macro_probe(struct platform_device *pdev) + tx->active_decimator[TX_MACRO_AIF3_CAP] = -1; + + /* set MCLK and NPL rates */ +- clk_set_rate(tx->mclk, MCLK_FREQ); +- clk_set_rate(tx->npl, MCLK_FREQ); +- +- ret = clk_prepare_enable(tx->macro); ++ ret = clk_set_rate(tx->mclk, MCLK_FREQ); + if (ret) + goto err; + +- ret = clk_prepare_enable(tx->dcodec); ++ ret = clk_set_rate(tx->npl, MCLK_FREQ); + if (ret) +- goto err_dcodec; ++ goto err; + +- ret = clk_prepare_enable(tx->mclk); ++ ret = devm_pm_clk_create(dev); + if (ret) +- goto err_mclk; ++ goto err; + +- ret = clk_prepare_enable(tx->npl); +- if (ret) +- goto err_npl; ++ ret = of_pm_clk_add_clks(dev); ++ if (ret < 0) ++ goto err; + +- ret = clk_prepare_enable(tx->fsgen); ++ pm_runtime_set_autosuspend_delay(dev, 100); ++ pm_runtime_use_autosuspend(dev); ++ ret = devm_pm_runtime_enable(dev); + if (ret) +- goto err_fsgen; ++ goto err; ++ ++ ret = pm_runtime_resume_and_get(dev); ++ if (ret < 0) ++ goto err; + + + /* reset soundwire block */ +@@ -2356,30 +2364,21 @@ static int tx_macro_probe(struct platform_device *pdev) + tx_macro_dai, + ARRAY_SIZE(tx_macro_dai)); + if (ret) +- goto err_clkout; +- +- pm_runtime_set_autosuspend_delay(dev, 3000); +- pm_runtime_use_autosuspend(dev); +- pm_runtime_mark_last_busy(dev); +- pm_runtime_set_active(dev); +- pm_runtime_enable(dev); ++ goto err_rpm_put; + + ret = tx_macro_register_mclk_output(tx); + if (ret) +- goto err_clkout; ++ goto err_rpm_put; ++ ++ ret = pm_runtime_put_autosuspend(dev); ++ if (ret < 0) ++ dev_warn(dev, "runtime PM put failed after probe: %d\n", ret); + + return 0; + +-err_clkout: +- clk_disable_unprepare(tx->fsgen); +-err_fsgen: +- clk_disable_unprepare(tx->npl); +-err_npl: +- clk_disable_unprepare(tx->mclk); +-err_mclk: +- clk_disable_unprepare(tx->dcodec); +-err_dcodec: +- clk_disable_unprepare(tx->macro); ++err_rpm_put: ++ if (pm_runtime_put_sync_suspend(dev) < 0) ++ dev_warn(dev, "runtime PM sync suspend failed in probe unwind\n"); + err: + lpass_macro_pds_exit(tx->pds); + +@@ -2390,25 +2389,23 @@ static void tx_macro_remove(struct platform_device *pdev) + { + struct tx_macro *tx = dev_get_drvdata(&pdev->dev); + +- clk_disable_unprepare(tx->macro); +- clk_disable_unprepare(tx->dcodec); +- clk_disable_unprepare(tx->mclk); +- clk_disable_unprepare(tx->npl); +- clk_disable_unprepare(tx->fsgen); +- + lpass_macro_pds_exit(tx->pds); + } + + static int tx_macro_runtime_suspend(struct device *dev) + { + struct tx_macro *tx = dev_get_drvdata(dev); ++ int ret; + + regcache_cache_only(tx->regmap, true); +- regcache_mark_dirty(tx->regmap); + +- clk_disable_unprepare(tx->fsgen); +- clk_disable_unprepare(tx->npl); +- clk_disable_unprepare(tx->mclk); ++ ret = pm_clk_suspend(dev); ++ if (ret) { ++ regcache_cache_only(tx->regmap, false); ++ return ret; ++ } ++ ++ regcache_mark_dirty(tx->regmap); + + return 0; + } +@@ -2418,34 +2415,23 @@ static int tx_macro_runtime_resume(struct device *dev) + struct tx_macro *tx = dev_get_drvdata(dev); + int ret; + +- ret = clk_prepare_enable(tx->mclk); ++ ret = pm_clk_resume(dev); + if (ret) { +- dev_err(dev, "unable to prepare mclk\n"); ++ regcache_cache_only(tx->regmap, true); ++ regcache_mark_dirty(tx->regmap); + return ret; + } + +- ret = clk_prepare_enable(tx->npl); +- if (ret) { +- dev_err(dev, "unable to prepare npl\n"); +- goto err_npl; +- } +- +- ret = clk_prepare_enable(tx->fsgen); +- if (ret) { +- dev_err(dev, "unable to prepare fsgen\n"); +- goto err_fsgen; +- } +- + regcache_cache_only(tx->regmap, false); +- regcache_sync(tx->regmap); ++ ret = regcache_sync(tx->regmap); ++ if (ret) { ++ regcache_cache_only(tx->regmap, true); ++ regcache_mark_dirty(tx->regmap); ++ pm_clk_suspend(dev); ++ return ret; ++ } + + return 0; +-err_fsgen: +- clk_disable_unprepare(tx->npl); +-err_npl: +- clk_disable_unprepare(tx->mclk); +- +- return ret; + } + + static const struct dev_pm_ops tx_macro_pm_ops = { +diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c +--- a/sound/soc/codecs/lpass-va-macro.c ++++ b/sound/soc/codecs/lpass-va-macro.c +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1351,34 +1352,43 @@ static int fsgen_gate_enable(struct clk_hw *hw) + { + struct va_macro *va = to_va_macro(hw); + struct regmap *regmap = va->regmap; +- int ret; ++ int ret, rpm_ret; + +- if (va->has_swr_master) { +- ret = clk_prepare_enable(va->mclk); +- if (ret) +- return ret; +- } ++ ret = pm_runtime_resume_and_get(va->dev); ++ if (ret < 0) ++ return ret; + + ret = va_macro_mclk_enable(va, true); ++ if (ret) { ++ rpm_ret = pm_runtime_put_autosuspend(va->dev); ++ if (rpm_ret < 0) ++ dev_warn(va->dev, ++ "runtime PM put failed in fsgen enable unwind: %d\n", ++ rpm_ret); ++ return ret; ++ } + if (va->has_swr_master) + regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, + CDC_VA_SWR_CLK_EN_MASK, CDC_VA_SWR_CLK_ENABLE); + +- return ret; ++ return 0; + } + + static void fsgen_gate_disable(struct clk_hw *hw) + { + struct va_macro *va = to_va_macro(hw); + struct regmap *regmap = va->regmap; ++ int ret; + + if (va->has_swr_master) + regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, + CDC_VA_SWR_CLK_EN_MASK, 0x0); + + va_macro_mclk_enable(va, false); +- if (va->has_swr_master) +- clk_disable_unprepare(va->mclk); ++ ++ ret = pm_runtime_put_autosuspend(va->dev); ++ if (ret < 0) ++ dev_warn(va->dev, "runtime PM put failed in fsgen disable: %d\n", ret); + } + + static int fsgen_gate_is_enabled(struct clk_hw *hw) +@@ -1539,6 +1549,7 @@ static int va_macro_probe(struct platform_device *pdev) + void __iomem *base; + u32 sample_rate = 0; + int ret; ++ int rpm_ret; + + va = devm_kzalloc(dev, sizeof(*va), GFP_KERNEL); + if (!va) +@@ -1594,7 +1605,9 @@ static int va_macro_probe(struct platform_device *pdev) + va->has_npl_clk = data->has_npl_clk; + + /* mclk rate */ +- clk_set_rate(va->mclk, 2 * VA_MACRO_MCLK_FREQ); ++ ret = clk_set_rate(va->mclk, 2 * VA_MACRO_MCLK_FREQ); ++ if (ret) ++ goto err; + + if (va->has_npl_clk) { + va->npl = devm_clk_get(dev, "npl"); +@@ -1603,25 +1616,29 @@ static int va_macro_probe(struct platform_device *pdev) + goto err; + } + +- clk_set_rate(va->npl, 2 * VA_MACRO_MCLK_FREQ); ++ ret = clk_set_rate(va->npl, 2 * VA_MACRO_MCLK_FREQ); ++ if (ret) ++ goto err; + } + +- ret = clk_prepare_enable(va->macro); ++ ret = devm_pm_clk_create(dev); + if (ret) + goto err; + +- ret = clk_prepare_enable(va->dcodec); +- if (ret) +- goto err_dcodec; ++ ret = of_pm_clk_add_clks(dev); ++ if (ret < 0) ++ goto err; + +- ret = clk_prepare_enable(va->mclk); ++ pm_runtime_set_autosuspend_delay(dev, 100); ++ pm_runtime_use_autosuspend(dev); ++ ret = devm_pm_runtime_enable(dev); + if (ret) +- goto err_mclk; ++ goto err; + +- if (va->has_npl_clk) { +- ret = clk_prepare_enable(va->npl); +- if (ret) +- goto err_npl; ++ rpm_ret = pm_runtime_resume_and_get(dev); ++ if (rpm_ret < 0) { ++ ret = rpm_ret; ++ goto err; + } + + /** +@@ -1634,7 +1651,7 @@ static int va_macro_probe(struct platform_device *pdev) + /* read version from register */ + ret = va_macro_set_lpass_codec_version(va); + if (ret) +- goto err_clkout; ++ goto err_rpm_put; + } + + if (va->has_swr_master) { +@@ -1664,35 +1681,28 @@ static int va_macro_probe(struct platform_device *pdev) + va_macro_dais, + ARRAY_SIZE(va_macro_dais)); + if (ret) +- goto err_clkout; +- +- pm_runtime_set_autosuspend_delay(dev, 3000); +- pm_runtime_use_autosuspend(dev); +- pm_runtime_mark_last_busy(dev); +- pm_runtime_set_active(dev); +- pm_runtime_enable(dev); ++ goto err_rpm_put; + + ret = va_macro_register_fsgen_output(va); + if (ret) +- goto err_clkout; ++ goto err_rpm_put; + + va->fsgen = devm_clk_hw_get_clk(dev, &va->hw, "fsgen"); + if (IS_ERR(va->fsgen)) { + ret = PTR_ERR(va->fsgen); +- goto err_clkout; ++ goto err_rpm_put; + } + ++ rpm_ret = pm_runtime_put_autosuspend(dev); ++ if (rpm_ret < 0) ++ dev_warn(dev, "runtime PM put failed after probe: %d\n", rpm_ret); ++ + return 0; + +-err_clkout: +- if (va->has_npl_clk) +- clk_disable_unprepare(va->npl); +-err_npl: +- clk_disable_unprepare(va->mclk); +-err_mclk: +- clk_disable_unprepare(va->dcodec); +-err_dcodec: +- clk_disable_unprepare(va->macro); ++err_rpm_put: ++ rpm_ret = pm_runtime_put_sync_suspend(dev); ++ if (rpm_ret < 0) ++ dev_warn(dev, "runtime PM sync suspend failed in probe unwind: %d\n", rpm_ret); + err: + lpass_macro_pds_exit(va->pds); + +@@ -1703,53 +1713,52 @@ static void va_macro_remove(struct platform_device *pdev) + { + struct va_macro *va = dev_get_drvdata(&pdev->dev); + +- if (va->has_npl_clk) +- clk_disable_unprepare(va->npl); +- +- clk_disable_unprepare(va->mclk); +- clk_disable_unprepare(va->dcodec); +- clk_disable_unprepare(va->macro); +- + lpass_macro_pds_exit(va->pds); + } + + static int va_macro_runtime_suspend(struct device *dev) + { + struct va_macro *va = dev_get_drvdata(dev); ++ int ret; + + regcache_cache_only(va->regmap, true); ++ ++ ret = pm_clk_suspend(dev); ++ if (ret) { ++ regcache_cache_only(va->regmap, false); ++ return ret; ++ } ++ + regcache_mark_dirty(va->regmap); + +- if (va->has_npl_clk) +- clk_disable_unprepare(va->npl); +- +- clk_disable_unprepare(va->mclk); +- + return 0; + } + + static int va_macro_runtime_resume(struct device *dev) + { + struct va_macro *va = dev_get_drvdata(dev); +- int ret; ++ int ret, sret; + +- ret = clk_prepare_enable(va->mclk); ++ ret = pm_clk_resume(dev); + if (ret) { +- dev_err(va->dev, "unable to prepare mclk\n"); ++ regcache_cache_only(va->regmap, true); ++ regcache_mark_dirty(va->regmap); + return ret; + } + +- if (va->has_npl_clk) { +- ret = clk_prepare_enable(va->npl); +- if (ret) { +- clk_disable_unprepare(va->mclk); +- dev_err(va->dev, "unable to prepare npl\n"); +- return ret; +- } +- } +- + regcache_cache_only(va->regmap, false); +- regcache_sync(va->regmap); ++ ++ ret = regcache_sync(va->regmap); ++ if (ret) { ++ regcache_cache_only(va->regmap, true); ++ regcache_mark_dirty(va->regmap); ++ sret = pm_clk_suspend(dev); ++ if (sret) ++ dev_err(va->dev, ++ "failed to suspend clocks after regcache sync failure: %d\n", ++ sret); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c +--- a/sound/soc/codecs/lpass-wsa-macro.c ++++ b/sound/soc/codecs/lpass-wsa-macro.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -2529,15 +2530,13 @@ static const struct snd_soc_dapm_route wsa_audio_map[] = { + static int wsa_swrm_clock(struct wsa_macro *wsa, bool enable) + { + struct regmap *regmap = wsa->regmap; ++ int ret; + + if (enable) { +- int ret; +- +- ret = clk_prepare_enable(wsa->mclk); +- if (ret) { +- dev_err(wsa->dev, "failed to enable mclk\n"); ++ ret = pm_runtime_resume_and_get(wsa->dev); ++ if (ret < 0) + return ret; +- } ++ + wsa_macro_mclk_enable(wsa, true); + + regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, +@@ -2548,7 +2547,10 @@ static int wsa_swrm_clock(struct wsa_macro *wsa, bool enable) + regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, + CDC_WSA_SWR_CLK_EN_MASK, 0); + wsa_macro_mclk_enable(wsa, false); +- clk_disable_unprepare(wsa->mclk); ++ ++ ret = pm_runtime_put_autosuspend(wsa->dev); ++ if (ret < 0) ++ dev_warn(wsa->dev, "runtime PM put failed: %d\n", ret); + } + + return 0; +@@ -2656,7 +2658,7 @@ static int wsa_macro_register_mclk_output(struct wsa_macro *wsa) + init.num_parents = 1; + wsa->hw.init = &init; + hw = &wsa->hw; +- ret = clk_hw_register(wsa->dev, hw); ++ ret = devm_clk_hw_register(wsa->dev, hw); + if (ret) + return ret; + +@@ -2771,28 +2773,31 @@ static int wsa_macro_probe(struct platform_device *pdev) + wsa->dev = dev; + + /* set MCLK and NPL rates */ +- clk_set_rate(wsa->mclk, WSA_MACRO_MCLK_FREQ); +- clk_set_rate(wsa->npl, WSA_MACRO_MCLK_FREQ); +- +- ret = clk_prepare_enable(wsa->macro); ++ ret = clk_set_rate(wsa->mclk, WSA_MACRO_MCLK_FREQ); + if (ret) +- goto err; ++ return ret; + +- ret = clk_prepare_enable(wsa->dcodec); ++ ret = clk_set_rate(wsa->npl, WSA_MACRO_MCLK_FREQ); + if (ret) +- goto err_dcodec; ++ return ret; + +- ret = clk_prepare_enable(wsa->mclk); ++ ret = devm_pm_clk_create(dev); + if (ret) +- goto err_mclk; ++ return ret; + +- ret = clk_prepare_enable(wsa->npl); +- if (ret) +- goto err_npl; ++ ret = of_pm_clk_add_clks(dev); ++ if (ret < 0) ++ return ret; + +- ret = clk_prepare_enable(wsa->fsgen); ++ pm_runtime_set_autosuspend_delay(dev, 100); ++ pm_runtime_use_autosuspend(dev); ++ ret = devm_pm_runtime_enable(dev); + if (ret) +- goto err_fsgen; ++ return ret; ++ ++ ret = pm_runtime_resume_and_get(dev); ++ if (ret < 0) ++ return ret; + + /* reset swr ip */ + regmap_update_bits(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, +@@ -2809,56 +2814,37 @@ static int wsa_macro_probe(struct platform_device *pdev) + wsa_macro_dai, + ARRAY_SIZE(wsa_macro_dai)); + if (ret) +- goto err_clkout; +- +- pm_runtime_set_autosuspend_delay(dev, 3000); +- pm_runtime_use_autosuspend(dev); +- pm_runtime_mark_last_busy(dev); +- pm_runtime_set_active(dev); +- pm_runtime_enable(dev); ++ goto err_rpm_put; + + ret = wsa_macro_register_mclk_output(wsa); + if (ret) +- goto err_clkout; ++ goto err_rpm_put; ++ ++ ret = pm_runtime_put_autosuspend(dev); ++ if (ret < 0) ++ dev_warn(dev, "runtime PM put failed after probe: %d\n", ret); + + return 0; +- +-err_clkout: +- clk_disable_unprepare(wsa->fsgen); +-err_fsgen: +- clk_disable_unprepare(wsa->npl); +-err_npl: +- clk_disable_unprepare(wsa->mclk); +-err_mclk: +- clk_disable_unprepare(wsa->dcodec); +-err_dcodec: +- clk_disable_unprepare(wsa->macro); +-err: ++err_rpm_put: ++ if (pm_runtime_put_sync_suspend(dev) < 0) ++ dev_warn(dev, "runtime PM sync suspend failed in probe unwind\n"); + return ret; +- +-} +- +-static void wsa_macro_remove(struct platform_device *pdev) +-{ +- struct wsa_macro *wsa = dev_get_drvdata(&pdev->dev); +- +- clk_disable_unprepare(wsa->macro); +- clk_disable_unprepare(wsa->dcodec); +- clk_disable_unprepare(wsa->mclk); +- clk_disable_unprepare(wsa->npl); +- clk_disable_unprepare(wsa->fsgen); + } + + static int wsa_macro_runtime_suspend(struct device *dev) + { + struct wsa_macro *wsa = dev_get_drvdata(dev); ++ int ret; + + regcache_cache_only(wsa->regmap, true); +- regcache_mark_dirty(wsa->regmap); + +- clk_disable_unprepare(wsa->fsgen); +- clk_disable_unprepare(wsa->npl); +- clk_disable_unprepare(wsa->mclk); ++ ret = pm_clk_suspend(dev); ++ if (ret) { ++ regcache_cache_only(wsa->regmap, false); ++ return ret; ++ } ++ ++ regcache_mark_dirty(wsa->regmap); + + return 0; + } +@@ -2866,36 +2852,29 @@ static int wsa_macro_runtime_suspend(struct device *dev) + static int wsa_macro_runtime_resume(struct device *dev) + { + struct wsa_macro *wsa = dev_get_drvdata(dev); +- int ret; ++ int ret, sret; + +- ret = clk_prepare_enable(wsa->mclk); ++ ret = pm_clk_resume(dev); + if (ret) { +- dev_err(dev, "unable to prepare mclk\n"); ++ regcache_cache_only(wsa->regmap, true); ++ regcache_mark_dirty(wsa->regmap); ++ return ret; ++ } ++ regcache_cache_only(wsa->regmap, false); ++ ++ ret = regcache_sync(wsa->regmap); ++ if (ret) { ++ regcache_cache_only(wsa->regmap, true); ++ regcache_mark_dirty(wsa->regmap); ++ sret = pm_clk_suspend(dev); ++ if (sret) ++ dev_err(dev, ++ "failed to suspend clocks after regcache sync failure: %d\n", ++ sret); + return ret; + } + +- ret = clk_prepare_enable(wsa->npl); +- if (ret) { +- dev_err(dev, "unable to prepare mclkx2\n"); +- goto err_npl; +- } +- +- ret = clk_prepare_enable(wsa->fsgen); +- if (ret) { +- dev_err(dev, "unable to prepare fsgen\n"); +- goto err_fsgen; +- } +- +- regcache_cache_only(wsa->regmap, false); +- regcache_sync(wsa->regmap); +- + return 0; +-err_fsgen: +- clk_disable_unprepare(wsa->npl); +-err_npl: +- clk_disable_unprepare(wsa->mclk); +- +- return ret; + } + + static const struct dev_pm_ops wsa_macro_pm_ops = { +@@ -2929,7 +2908,6 @@ static struct platform_driver wsa_macro_driver = { + .pm = pm_ptr(&wsa_macro_pm_ops), + }, + .probe = wsa_macro_probe, +- .remove = wsa_macro_remove, + }; + + module_platform_driver(wsa_macro_driver); +diff --git a/sound/soc/codecs/madera.c b/sound/soc/codecs/madera.c +--- a/sound/soc/codecs/madera.c ++++ b/sound/soc/codecs/madera.c +@@ -6,6 +6,7 @@ + // Cirrus Logic International Semiconductor Ltd. + // + ++#include + #include + #include + #include +@@ -513,7 +514,7 @@ int madera_domain_clk_ev(struct snd_soc_dapm_widget *w, + * We can't rely on the DAPM mutex for locking because we need a lock + * that can safely be called in hw_params + */ +- mutex_lock(&priv->rate_lock); ++ guard(mutex)(&priv->rate_lock); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: +@@ -532,8 +533,6 @@ int madera_domain_clk_ev(struct snd_soc_dapm_widget *w, + + madera_debug_dump_domain_groups(priv); + +- mutex_unlock(&priv->rate_lock); +- + return 0; + } + EXPORT_SYMBOL_GPL(madera_domain_clk_ev); +@@ -875,9 +874,8 @@ static int madera_adsp_rate_get(struct snd_kcontrol *kcontrol, + const int adsp_num = e->shift_l; + int item; + +- mutex_lock(&priv->rate_lock); +- cached_rate = priv->adsp_rate_cache[adsp_num]; +- mutex_unlock(&priv->rate_lock); ++ scoped_guard(mutex, &priv->rate_lock) ++ cached_rate = priv->adsp_rate_cache[adsp_num]; + + item = snd_soc_enum_val_to_item(e, cached_rate); + ucontrol->value.enumerated.item[0] = item; +@@ -893,7 +891,6 @@ static int madera_adsp_rate_put(struct snd_kcontrol *kcontrol, + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + const int adsp_num = e->shift_l; + const unsigned int item = ucontrol->value.enumerated.item[0]; +- int ret = 0; + + if (item >= e->items) + return -EINVAL; +@@ -903,22 +900,20 @@ static int madera_adsp_rate_put(struct snd_kcontrol *kcontrol, + * maintain consistent behaviour that rate domains cannot be changed + * while in use since this is a hardware requirement + */ +- mutex_lock(&priv->rate_lock); ++ guard(mutex)(&priv->rate_lock); + + if (!madera_can_change_grp_rate(priv, priv->adsp[adsp_num].cs_dsp.base)) { + dev_warn(priv->madera->dev, + "Cannot change '%s' while in use by active audio paths\n", + kcontrol->id.name); +- ret = -EBUSY; ++ return -EBUSY; + } else if (priv->adsp_rate_cache[adsp_num] != e->values[item]) { + /* Volatile register so defer until the codec is powered up */ + priv->adsp_rate_cache[adsp_num] = e->values[item]; +- ret = 1; ++ return 1; + } + +- mutex_unlock(&priv->rate_lock); +- +- return ret; ++ return 0; + } + + static const struct soc_enum madera_adsp_rate_enum[] = { +@@ -1061,15 +1056,13 @@ int madera_rate_put(struct snd_kcontrol *kcontrol, + * Prevent the domain powering up while we're checking whether it's + * safe to change rate domain + */ +- mutex_lock(&priv->rate_lock); ++ guard(mutex)(&priv->rate_lock); + + val = snd_soc_component_read(component, e->reg); + val >>= e->shift_l; + val &= e->mask; +- if (snd_soc_enum_item_to_val(e, item) == val) { +- ret = 0; +- goto out; +- } ++ if (snd_soc_enum_item_to_val(e, item) == val) ++ return 0; + + if (!madera_can_change_grp_rate(priv, e->reg)) { + dev_warn(priv->madera->dev, +@@ -1082,8 +1075,6 @@ int madera_rate_put(struct snd_kcontrol *kcontrol, + ret = snd_soc_put_enum_double(kcontrol, ucontrol); + madera_spin_sysclk(priv); + } +-out: +- mutex_unlock(&priv->rate_lock); + + return ret; + } +@@ -3041,12 +3032,11 @@ static int madera_hw_params_rate(struct snd_pcm_substream *substream, + if ((cur & MADERA_AIF1_RATE_MASK) == (tar & MADERA_AIF1_RATE_MASK)) + return 0; + +- mutex_lock(&priv->rate_lock); ++ guard(mutex)(&priv->rate_lock); + + if (!madera_can_change_grp_rate(priv, base + MADERA_AIF_RATE_CTRL)) { + madera_aif_warn(dai, "Cannot change rate while active\n"); +- ret = -EBUSY; +- goto out; ++ return -EBUSY; + } + + /* Guard the rate change with SYSCLK cycles */ +@@ -3055,9 +3045,6 @@ static int madera_hw_params_rate(struct snd_pcm_substream *substream, + MADERA_AIF1_RATE_MASK, tar); + madera_spin_sysclk(priv); + +-out: +- mutex_unlock(&priv->rate_lock); +- + return ret; + } + +@@ -3345,6 +3332,16 @@ static int madera_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + return 0; + } + ++static const u64 madera_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_B | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_NB_IF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_NF | ++ SND_SOC_POSSIBLE_DAIFMT_IB_IF; ++ + const struct snd_soc_dai_ops madera_dai_ops = { + .startup = &madera_startup, + .set_fmt = &madera_set_fmt, +@@ -3352,6 +3349,8 @@ const struct snd_soc_dai_ops madera_dai_ops = { + .hw_params = &madera_hw_params, + .set_sysclk = &madera_dai_set_sysclk, + .set_tristate = &madera_set_tristate, ++ .auto_selectable_formats = &madera_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + EXPORT_SYMBOL_GPL(madera_dai_ops); + +diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c +--- a/sound/soc/codecs/max98090.c ++++ b/sound/soc/codecs/max98090.c +@@ -2341,6 +2341,7 @@ static irqreturn_t max98090_interrupt(int irq, void *data) + * + * @component: MAX98090 component + * @jack: jack to report detection events on ++ * @data: can be used if codec driver need extra data for configuring jack + * + * Enable microphone detection via IRQ on the MAX98090. If GPIOs are + * being used to bring out signals to the processor then only platform +@@ -2646,12 +2647,18 @@ static void max98090_i2c_remove(struct i2c_client *client) + static int max98090_runtime_resume(struct device *dev) + { + struct max98090_priv *max98090 = dev_get_drvdata(dev); ++ int ret; + + regcache_cache_only(max98090->regmap, false); + + max98090_reset(max98090); + +- regcache_sync(max98090->regmap); ++ ret = regcache_sync(max98090->regmap); ++ if (ret < 0) { ++ regcache_cache_only(max98090->regmap, true); ++ regcache_mark_dirty(max98090->regmap); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c +--- a/sound/soc/codecs/max98095.c ++++ b/sound/soc/codecs/max98095.c +@@ -5,6 +5,7 @@ + * Copyright 2011 Maxim Integrated Products + */ + ++#include + #include + #include + #include +@@ -1532,15 +1533,17 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol, + regsave = snd_soc_component_read(component, M98095_088_CFG_LEVEL); + snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, 0); + +- mutex_lock(&max98095->lock); +- snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG); +- m98095_eq_band(component, channel, 0, coef_set->band1); +- m98095_eq_band(component, channel, 1, coef_set->band2); +- m98095_eq_band(component, channel, 2, coef_set->band3); +- m98095_eq_band(component, channel, 3, coef_set->band4); +- m98095_eq_band(component, channel, 4, coef_set->band5); +- snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, 0); +- mutex_unlock(&max98095->lock); ++ scoped_guard(mutex, &max98095->lock) { ++ snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, ++ M98095_SEG, M98095_SEG); ++ m98095_eq_band(component, channel, 0, coef_set->band1); ++ m98095_eq_band(component, channel, 1, coef_set->band2); ++ m98095_eq_band(component, channel, 2, coef_set->band3); ++ m98095_eq_band(component, channel, 3, coef_set->band4); ++ m98095_eq_band(component, channel, 4, coef_set->band5); ++ snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, ++ M98095_SEG, 0); ++ } + + /* Restore the original on/off state */ + snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, regsave); +@@ -1683,12 +1686,14 @@ static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol, + regsave = snd_soc_component_read(component, M98095_088_CFG_LEVEL); + snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, 0); + +- mutex_lock(&max98095->lock); +- snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG); +- m98095_biquad_band(component, channel, 0, coef_set->band1); +- m98095_biquad_band(component, channel, 1, coef_set->band2); +- snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, 0); +- mutex_unlock(&max98095->lock); ++ scoped_guard(mutex, &max98095->lock) { ++ snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, ++ M98095_SEG, M98095_SEG); ++ m98095_biquad_band(component, channel, 0, coef_set->band1); ++ m98095_biquad_band(component, channel, 1, coef_set->band2); ++ snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, ++ M98095_SEG, 0); ++ } + + /* Restore the original on/off state */ + snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, regsave); +diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c +--- a/sound/soc/codecs/max98363.c ++++ b/sound/soc/codecs/max98363.c +@@ -100,7 +100,12 @@ static int max98363_resume(struct device *dev) + return ret; + + regcache_cache_only(max98363->regmap, false); +- regcache_sync(max98363->regmap); ++ ret = regcache_sync(max98363->regmap); ++ if (ret) { ++ regcache_cache_only(max98363->regmap, true); ++ regcache_mark_dirty(max98363->regmap); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/max98373-i2c.c b/sound/soc/codecs/max98373-i2c.c +--- a/sound/soc/codecs/max98373-i2c.c ++++ b/sound/soc/codecs/max98373-i2c.c +@@ -488,10 +488,17 @@ static int max98373_suspend(struct device *dev) + static int max98373_resume(struct device *dev) + { + struct max98373_priv *max98373 = dev_get_drvdata(dev); ++ int ret; + + regcache_cache_only(max98373->regmap, false); + max98373_reset(max98373, dev); +- regcache_sync(max98373->regmap); ++ ret = regcache_sync(max98373->regmap); ++ if (ret) { ++ regcache_cache_only(max98373->regmap, true); ++ regcache_mark_dirty(max98373->regmap); ++ return ret; ++ } ++ + return 0; + } + +diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c +--- a/sound/soc/codecs/max98373-sdw.c ++++ b/sound/soc/codecs/max98373-sdw.c +@@ -277,7 +277,12 @@ static int max98373_resume(struct device *dev) + } + + regcache_cache_only(max98373->regmap, false); +- regcache_sync(max98373->regmap); ++ ret = regcache_sync(max98373->regmap); ++ if (ret) { ++ regcache_cache_only(max98373->regmap, true); ++ regcache_mark_dirty(max98373->regmap); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/max98388.c b/sound/soc/codecs/max98388.c +--- a/sound/soc/codecs/max98388.c ++++ b/sound/soc/codecs/max98388.c +@@ -863,10 +863,16 @@ static int max98388_suspend(struct device *dev) + static int max98388_resume(struct device *dev) + { + struct max98388_priv *max98388 = dev_get_drvdata(dev); ++ int ret; + + regcache_cache_only(max98388->regmap, false); + max98388_reset(max98388, dev); +- regcache_sync(max98388->regmap); ++ ret = regcache_sync(max98388->regmap); ++ if (ret) { ++ regcache_cache_only(max98388->regmap, true); ++ regcache_mark_dirty(max98388->regmap); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c +--- a/sound/soc/codecs/max98390.c ++++ b/sound/soc/codecs/max98390.c +@@ -788,7 +788,6 @@ static int max98390_dsm_init(struct snd_soc_component *component) + const char *vendor, *product; + struct max98390_priv *max98390 = + snd_soc_component_get_drvdata(component); +- const struct firmware *fw; + char *dsm_param; + + vendor = dmi_get_system_info(DMI_SYS_VENDOR); +@@ -805,6 +804,8 @@ static int max98390_dsm_init(struct snd_soc_component *component) + snprintf(filename, sizeof(filename), "%s", + max98390->dsm_param_name); + } ++ ++ const struct firmware *fw __free(firmware) = NULL; + ret = request_firmware(&fw, filename, component->dev); + if (ret) { + ret = request_firmware(&fw, "dsm_param.bin", component->dev); +@@ -812,7 +813,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) + ret = request_firmware(&fw, "dsmparam.bin", + component->dev); + if (ret) +- goto err; ++ return ret; + } + } + +@@ -822,8 +823,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) + if (fw->size < MAX98390_DSM_PARAM_MIN_SIZE) { + dev_err(component->dev, + "param fw is invalid.\n"); +- ret = -EINVAL; +- goto err_alloc; ++ return -EINVAL; + } + dsm_param = (char *)fw->data; + param_start_addr = (dsm_param[0] & 0xff) | (dsm_param[1] & 0xff) << 8; +@@ -833,8 +833,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) + fw->size < param_size + MAX98390_DSM_PAYLOAD_OFFSET) { + dev_err(component->dev, + "param fw is invalid.\n"); +- ret = -EINVAL; +- goto err_alloc; ++ return -EINVAL; + } + regmap_write(max98390->regmap, MAX98390_R203A_AMP_EN, 0x80); + dsm_param += MAX98390_DSM_PAYLOAD_OFFSET; +@@ -842,10 +841,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) + dsm_param, param_size); + regmap_write(max98390->regmap, MAX98390_R23E1_DSP_GLOBAL_EN, 0x01); + +-err_alloc: +- release_firmware(fw); +-err: +- return ret; ++ return 0; + } + + static void max98390_init_regs(struct snd_soc_component *component) +@@ -952,11 +948,17 @@ static int max98390_suspend(struct device *dev) + static int max98390_resume(struct device *dev) + { + struct max98390_priv *max98390 = dev_get_drvdata(dev); ++ int ret; + + dev_dbg(dev, "%s:Enter\n", __func__); + + regcache_cache_only(max98390->regmap, false); +- regcache_sync(max98390->regmap); ++ ret = regcache_sync(max98390->regmap); ++ if (ret) { ++ regcache_cache_only(max98390->regmap, true); ++ regcache_mark_dirty(max98390->regmap); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c +--- a/sound/soc/codecs/max98396.c ++++ b/sound/soc/codecs/max98396.c +@@ -1600,19 +1600,37 @@ static int max98396_resume(struct device *dev) + if (max98396->pvdd) { + ret = regulator_enable(max98396->pvdd); + if (ret < 0) +- return ret; ++ goto err_core_supplies; + } + + if (max98396->vbat) { + ret = regulator_enable(max98396->vbat); + if (ret < 0) +- return ret; ++ goto err_pvdd; + } + + regcache_cache_only(max98396->regmap, false); + max98396_reset(max98396, dev); +- regcache_sync(max98396->regmap); ++ ret = regcache_sync(max98396->regmap); ++ if (ret < 0) { ++ regcache_cache_only(max98396->regmap, true); ++ regcache_mark_dirty(max98396->regmap); ++ goto err_vbat; ++ } ++ + return 0; ++ ++err_vbat: ++ if (max98396->vbat) ++ regulator_disable(max98396->vbat); ++err_pvdd: ++ if (max98396->pvdd) ++ regulator_disable(max98396->pvdd); ++err_core_supplies: ++ regulator_bulk_disable(MAX98396_NUM_CORE_SUPPLIES, ++ max98396->core_supplies); ++ ++ return ret; + } + + static const struct dev_pm_ops max98396_pm = { +diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c +--- a/sound/soc/codecs/max98927.c ++++ b/sound/soc/codecs/max98927.c +@@ -742,11 +742,17 @@ static int max98927_suspend(struct device *dev) + static int max98927_resume(struct device *dev) + { + struct max98927_priv *max98927 = dev_get_drvdata(dev); ++ int ret; + + regmap_write(max98927->regmap, MAX98927_R0100_SOFT_RESET, + MAX98927_SOFT_RESET); + regcache_cache_only(max98927->regmap, false); +- regcache_sync(max98927->regmap); ++ ret = regcache_sync(max98927->regmap); ++ if (ret) { ++ regcache_cache_only(max98927->regmap, true); ++ regcache_mark_dirty(max98927->regmap); ++ return ret; ++ } + return 0; + } + +diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c +--- a/sound/soc/codecs/msm8916-wcd-analog.c ++++ b/sound/soc/codecs/msm8916-wcd-analog.c +@@ -217,9 +217,11 @@ + #define CDC_A_RX_HPH_BIAS_LDO_OCP (0xf195) + #define CDC_A_RX_HPH_BIAS_CNP (0xf196) + #define CDC_A_RX_HPH_CNP_EN (0xf197) ++#define CDC_A_RX_HPH_L_TEST (0xf19A) + #define CDC_A_RX_HPH_L_PA_DAC_CTL (0xf19B) + #define RX_HPA_L_PA_DAC_CTL_DATA_RESET_MASK BIT(1) + #define RX_HPA_L_PA_DAC_CTL_DATA_RESET_RESET BIT(1) ++#define CDC_A_RX_HPH_R_TEST (0xf19C) + #define CDC_A_RX_HPH_R_PA_DAC_CTL (0xf19D) + #define RX_HPH_R_PA_DAC_CTL_DATA_RESET BIT(1) + #define RX_HPH_R_PA_DAC_CTL_DATA_RESET_MASK BIT(1) +@@ -232,6 +234,8 @@ + #define RX_EAR_CTL_PA_SEL_MASK BIT(7) + #define RX_EAR_CTL_PA_SEL BIT(7) + ++#define CDC_A_RX_EAR_STATUS (0xf1A1) ++ + #define CDC_A_SPKR_DAC_CTL (0xf1B0) + #define SPKR_DAC_CTL_DAC_RESET_MASK BIT(4) + #define SPKR_DAC_CTL_DAC_RESET_NORMAL 0 +@@ -250,6 +254,7 @@ + SPKR_DRV_CAL_EN | SPKR_DRV_SETTLE_EN | \ + SPKR_DRV_FW_EN | SPKR_DRV_BOOST_SET | \ + SPKR_DRV_CMFB_SET | SPKR_DRV_GAIN_SET) ++#define CDC_A_SPKR_ANA_BIAS_SET (0xf1B3) + #define CDC_A_SPKR_OCP_CTL (0xf1B4) + #define CDC_A_SPKR_PWRSTG_CTL (0xf1B5) + #define SPKR_PWRSTG_CTL_DAC_EN_MASK BIT(0) +@@ -264,6 +269,7 @@ + + #define CDC_A_SPKR_DRV_DBG (0xf1B7) + #define CDC_A_CURRENT_LIMIT (0xf1C0) ++#define CDC_A_BYPASS_MODE (0xf1C2) + #define CDC_A_BOOST_EN_CTL (0xf1C3) + #define CDC_A_SLOPE_COMP_IP_ZERO (0xf1C4) + #define CDC_A_SEC_ACCESS (0xf1D0) +@@ -286,6 +292,11 @@ static const char * const supply_names[] = { + + #define MBHC_MAX_BUTTONS (5) + ++struct wcd_reg_seq { ++ const struct reg_default *seq; ++ int seq_size; ++}; ++ + struct pm8916_wcd_analog_priv { + u16 pmic_rev; + u16 codec_version; +@@ -696,6 +707,59 @@ static int pm8916_wcd_analog_enable_ear_pa(struct snd_soc_dapm_widget *w, + return 0; + } + ++static int pm8916_wcd_analog_enable_hphl_pa(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, ++ int event) ++{ ++ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); ++ struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev); ++ ++ /* This quirk is not required for revisions prior to CAJON_2_0 */ ++ if (priv->codec_version < 4) ++ return 0; ++ ++ switch (event) { ++ case SND_SOC_DAPM_POST_PMU: ++ usleep_range(7000, 7100); ++ snd_soc_component_update_bits(component, CDC_A_RX_HPH_L_TEST, ++ 0x04, 0x04); ++ break; ++ case SND_SOC_DAPM_POST_PMD: ++ /* wait 20 ms after the digital codec has powered down */ ++ msleep(20); ++ snd_soc_component_update_bits(component, CDC_A_RX_HPH_L_TEST, ++ 0x04, 0x00); ++ break; ++ } ++ return 0; ++} ++ ++static int pm8916_wcd_analog_enable_hphr_pa(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, ++ int event) ++{ ++ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); ++ struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev); ++ ++ /* This quirk is not required for revisions prior to CAJON_2_0 */ ++ if (priv->codec_version < 4) ++ return 0; ++ ++ switch (event) { ++ case SND_SOC_DAPM_POST_PMU: ++ usleep_range(7000, 7100); ++ snd_soc_component_update_bits(component, CDC_A_RX_HPH_R_TEST, ++ 0x04, 0x04); ++ break; ++ case SND_SOC_DAPM_POST_PMD: ++ msleep(20); ++ snd_soc_component_update_bits(component, CDC_A_RX_HPH_R_TEST, ++ 0x04, 0x00); ++ break; ++ } ++ return 0; ++} ++ + static const struct reg_default wcd_reg_defaults_2_0[] = { + {CDC_A_RX_COM_OCP_CTL, 0xD1}, + {CDC_A_RX_COM_OCP_COUNT, 0xFF}, +@@ -715,9 +779,69 @@ static const struct reg_default wcd_reg_defaults_2_0[] = { + {CDC_A_MASTER_BIAS_CTL, 0x30}, + }; + ++static const struct wcd_reg_seq pm8916_data = { ++ .seq = wcd_reg_defaults_2_0, ++ .seq_size = ARRAY_SIZE(wcd_reg_defaults_2_0), ++}; ++ ++static const struct reg_default wcd_reg_defaults_pm8950[] = { ++ {CDC_A_RX_COM_OCP_CTL, 0xd1}, ++ {CDC_A_RX_COM_OCP_COUNT, 0xff}, ++ {CDC_D_SEC_ACCESS, 0xa5}, ++ {CDC_D_PERPH_RESET_CTL3, 0x0f}, ++ {CDC_A_TX_1_2_OPAMP_BIAS, 0x4c}, ++ {CDC_A_NCP_FBCTRL, 0xa8}, ++ {CDC_A_NCP_VCTRL, 0xa4}, ++ {CDC_A_SPKR_DRV_CTL, 0x69}, ++ {CDC_A_SPKR_DRV_DBG, 0x01}, ++ {CDC_A_SEC_ACCESS, 0xa5}, ++ {CDC_A_PERPH_RESET_CTL3, 0x0f}, ++ {CDC_A_CURRENT_LIMIT, 0x82}, ++ {CDC_A_SPKR_ANA_BIAS_SET, 0x41}, ++ {CDC_A_SPKR_DAC_CTL, 0x03}, ++ {CDC_A_SPKR_OCP_CTL, 0xe1}, ++ {CDC_A_RX_HPH_BIAS_PA, 0xfa}, ++ {CDC_A_MASTER_BIAS_CTL, 0x30}, ++ {CDC_A_MICB_1_INT_RBIAS, 0x00}, ++}; ++ ++static const struct wcd_reg_seq pm8950_data = { ++ .seq = wcd_reg_defaults_pm8950, ++ .seq_size = ARRAY_SIZE(wcd_reg_defaults_pm8950), ++}; ++ ++static const struct reg_default wcd_reg_defaults_pm8953[] = { ++ {CDC_A_RX_COM_OCP_CTL, 0xd1}, ++ {CDC_A_RX_COM_OCP_COUNT, 0xff}, ++ {CDC_D_SEC_ACCESS, 0xa5}, ++ {CDC_D_PERPH_RESET_CTL3, 0x0f}, ++ {CDC_A_TX_1_2_OPAMP_BIAS, 0x4c}, ++ {CDC_A_NCP_FBCTRL, 0xa8}, ++ {CDC_A_NCP_VCTRL, 0xa4}, ++ {CDC_A_SPKR_DRV_CTL, 0x69}, ++ {CDC_A_SPKR_DRV_DBG, 0x01}, ++ {CDC_A_SEC_ACCESS, 0xa5}, ++ {CDC_A_PERPH_RESET_CTL3, 0x0f}, ++ {CDC_A_CURRENT_LIMIT, 0xa2}, ++ {CDC_A_BYPASS_MODE, 0x18}, ++ {CDC_A_SPKR_ANA_BIAS_SET, 0x41}, ++ {CDC_A_SPKR_DAC_CTL, 0x03}, ++ {CDC_A_SPKR_OCP_CTL, 0xe1}, ++ {CDC_A_RX_HPH_BIAS_PA, 0xfa}, ++ {CDC_A_RX_EAR_STATUS, 0x10}, ++ {CDC_A_MASTER_BIAS_CTL, 0x30}, ++ {CDC_A_MICB_1_INT_RBIAS, 0x00}, ++}; ++ ++static const struct wcd_reg_seq pm8953_data = { ++ .seq = wcd_reg_defaults_pm8953, ++ .seq_size = ARRAY_SIZE(wcd_reg_defaults_pm8953), ++}; ++ + static int pm8916_wcd_analog_probe(struct snd_soc_component *component) + { + struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev); ++ const struct wcd_reg_seq *wcd_reg_init_data; + int err, reg; + + err = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); +@@ -738,9 +862,11 @@ static int pm8916_wcd_analog_probe(struct snd_soc_component *component) + snd_soc_component_write(component, CDC_D_PERPH_RESET_CTL4, 0x01); + snd_soc_component_write(component, CDC_A_PERPH_RESET_CTL4, 0x01); + +- for (reg = 0; reg < ARRAY_SIZE(wcd_reg_defaults_2_0); reg++) +- snd_soc_component_write(component, wcd_reg_defaults_2_0[reg].reg, +- wcd_reg_defaults_2_0[reg].def); ++ wcd_reg_init_data = of_device_get_match_data(component->dev); ++ ++ for (reg = 0; reg < wcd_reg_init_data->seq_size; reg++) ++ snd_soc_component_write(component, wcd_reg_init_data->seq[reg].reg, ++ wcd_reg_init_data->seq[reg].def); + + priv->component = component; + +@@ -883,11 +1009,15 @@ static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = { + SND_SOC_DAPM_MUX("EAR_S", SND_SOC_NOPM, 0, 0, &ear_mux), + SND_SOC_DAPM_SUPPLY("EAR CP", CDC_A_NCP_EN, 4, 0, NULL, 0), + +- SND_SOC_DAPM_PGA("HPHL PA", CDC_A_RX_HPH_CNP_EN, 5, 0, NULL, 0), ++ SND_SOC_DAPM_PGA_E("HPHL PA", CDC_A_RX_HPH_CNP_EN, 5, 0, NULL, 0, ++ pm8916_wcd_analog_enable_hphl_pa, ++ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("HPHL", SND_SOC_NOPM, 0, 0, &hphl_mux), + SND_SOC_DAPM_MIXER("HPHL DAC", CDC_A_RX_HPH_L_PA_DAC_CTL, 3, 0, NULL, + 0), +- SND_SOC_DAPM_PGA("HPHR PA", CDC_A_RX_HPH_CNP_EN, 4, 0, NULL, 0), ++ SND_SOC_DAPM_PGA_E("HPHR PA", CDC_A_RX_HPH_CNP_EN, 4, 0, NULL, 0, ++ pm8916_wcd_analog_enable_hphr_pa, ++ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("HPHR", SND_SOC_NOPM, 0, 0, &hphr_mux), + SND_SOC_DAPM_MIXER("HPHR DAC", CDC_A_RX_HPH_R_PA_DAC_CTL, 3, 0, NULL, + 0), +@@ -1259,7 +1389,9 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev) + } + + static const struct of_device_id pm8916_wcd_analog_spmi_match_table[] = { +- { .compatible = "qcom,pm8916-wcd-analog-codec", }, ++ { .compatible = "qcom,pm8916-wcd-analog-codec", .data = &pm8916_data }, ++ { .compatible = "qcom,pm8950-wcd-analog-codec", .data = &pm8950_data }, ++ { .compatible = "qcom,pm8953-wcd-analog-codec", .data = &pm8953_data }, + { } + }; + +diff --git a/sound/soc/codecs/mt6357.c b/sound/soc/codecs/mt6357.c +--- a/sound/soc/codecs/mt6357.c ++++ b/sound/soc/codecs/mt6357.c +@@ -1834,8 +1834,8 @@ static int mt6357_platform_driver_probe(struct platform_device *pdev) + } + + static const struct platform_device_id mt6357_platform_ids[] = { +- {"mt6357-sound", 0}, +- { /* sentinel */ }, ++ { .name = "mt6357-sound" }, ++ { /* sentinel */ } + }; + MODULE_DEVICE_TABLE(platform, mt6357_platform_ids); + +diff --git a/sound/soc/codecs/mt6359-accdet.c b/sound/soc/codecs/mt6359-accdet.c +--- a/sound/soc/codecs/mt6359-accdet.c ++++ b/sound/soc/codecs/mt6359-accdet.c +@@ -6,6 +6,7 @@ + // Author: Argus Lin + // + ++#include + #include + #include + #include +@@ -398,14 +399,13 @@ static void mt6359_accdet_work(struct work_struct *work) + struct mt6359_accdet *priv = + container_of(work, struct mt6359_accdet, accdet_work); + +- mutex_lock(&priv->res_lock); ++ guard(mutex)(&priv->res_lock); + priv->pre_accdet_status = priv->accdet_status; + check_jack_btn_type(priv); + + if (priv->jack_plugged && + priv->pre_accdet_status != priv->accdet_status) + mt6359_accdet_jack_report(priv); +- mutex_unlock(&priv->res_lock); + } + + static void mt6359_accdet_jd_work(struct work_struct *work) +@@ -416,7 +416,7 @@ static void mt6359_accdet_jd_work(struct work_struct *work) + struct mt6359_accdet *priv = + container_of(work, struct mt6359_accdet, jd_work); + +- mutex_lock(&priv->res_lock); ++ guard(mutex)(&priv->res_lock); + if (priv->jd_sts == M_PLUG_IN) { + priv->jack_plugged = true; + +@@ -450,7 +450,6 @@ static void mt6359_accdet_jd_work(struct work_struct *work) + + if (priv->caps & ACCDET_PMIC_EINT_IRQ) + recover_eint_setting(priv); +- mutex_unlock(&priv->res_lock); + } + + static irqreturn_t mt6359_accdet_irq(int irq, void *data) +@@ -459,7 +458,7 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) + unsigned int irq_val = 0, val = 0, value = 0; + int ret; + +- mutex_lock(&priv->res_lock); ++ guard(mutex)(&priv->res_lock); + regmap_read(priv->regmap, ACCDET_IRQ_ADDR, &irq_val); + + if (irq_val & ACCDET_IRQ_MASK_SFT) { +@@ -474,7 +473,6 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) + 1000); + if (ret) { + dev_err(priv->dev, "%s(), ret %d\n", __func__, ret); +- mutex_unlock(&priv->res_lock); + return IRQ_NONE; + } + regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, +@@ -498,7 +496,6 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) + if (ret) { + dev_err(priv->dev, "%s(), ret %d\n", __func__, + ret); +- mutex_unlock(&priv->res_lock); + return IRQ_NONE; + } + regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, +@@ -521,7 +518,6 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) + if (ret) { + dev_err(priv->dev, "%s(), ret %d\n", __func__, + ret); +- mutex_unlock(&priv->res_lock); + return IRQ_NONE; + } + regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, +@@ -540,7 +536,6 @@ static irqreturn_t mt6359_accdet_irq(int irq, void *data) + + queue_work(priv->jd_workqueue, &priv->jd_work); + } +- mutex_unlock(&priv->res_lock); + + return IRQ_HANDLED; + } +diff --git a/sound/soc/codecs/ntpfw.c b/sound/soc/codecs/ntpfw.c +--- a/sound/soc/codecs/ntpfw.c ++++ b/sound/soc/codecs/ntpfw.c +@@ -89,7 +89,7 @@ int ntpfw_load(struct i2c_client *i2c, const char *name, u32 magic) + { + struct device *dev = &i2c->dev; + const struct ntpfw_chunk *chunk; +- const struct firmware *fw; ++ const struct firmware *fw __free(firmware) = NULL; + const u8 *data; + size_t leftover; + int ret; +@@ -101,10 +101,8 @@ int ntpfw_load(struct i2c_client *i2c, const char *name, u32 magic) + return ret; + } + +- if (!ntpfw_verify(dev, fw->data, fw->size, magic)) { +- ret = -EINVAL; +- goto done; +- } ++ if (!ntpfw_verify(dev, fw->data, fw->size, magic)) ++ return -EINVAL; + + data = fw->data + sizeof(struct ntpfw_header); + leftover = fw->size - sizeof(struct ntpfw_header); +@@ -112,23 +110,18 @@ int ntpfw_load(struct i2c_client *i2c, const char *name, u32 magic) + while (leftover) { + chunk = (struct ntpfw_chunk *)data; + +- if (!ntpfw_verify_chunk(dev, chunk, leftover)) { +- ret = -EINVAL; +- goto done; +- } ++ if (!ntpfw_verify_chunk(dev, chunk, leftover)) ++ return -EINVAL; + + ret = ntpfw_send_chunk(i2c, chunk); + if (ret) +- goto done; ++ return ret; + + data += be16_to_cpu(chunk->length) + sizeof(*chunk); + leftover -= be16_to_cpu(chunk->length) + sizeof(*chunk); + } + +-done: +- release_firmware(fw); +- +- return ret; ++ return 0; + } + EXPORT_SYMBOL_GPL(ntpfw_load); + +diff --git a/sound/soc/codecs/ntpfw.h b/sound/soc/codecs/ntpfw.h +--- a/sound/soc/codecs/ntpfw.h ++++ b/sound/soc/codecs/ntpfw.h +@@ -1,5 +1,5 @@ + /* SPDX-License-Identifier: GPL-2.0-only */ +-/** ++/* + * ntpfw.h - Firmware helper functions for Neofidelity codecs + * + * Copyright (c) 2024, SaluteDevices. All Rights Reserved. +@@ -13,10 +13,11 @@ + /** + * ntpfw_load - load firmware to amplifier over i2c interface. + * +- * @i2c Pointer to amplifier's I2C client. +- * @name Firmware file name. +- * @magic Magic number to validate firmware. +- * @return 0 or error code upon error. ++ * @i2c: Pointer to amplifier's I2C client. ++ * @name: Firmware file name. ++ * @magic: Magic number to validate firmware. ++ * ++ * Returns: 0 or error code upon error. + */ + int ntpfw_load(struct i2c_client *i2c, const char *name, const u32 magic); + +diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c +--- a/sound/soc/codecs/pcm1681.c ++++ b/sound/soc/codecs/pcm1681.c +@@ -199,10 +199,17 @@ static int pcm1681_hw_params(struct snd_pcm_substream *substream, + return pcm1681_set_deemph(component); + } + ++static const u64 pcm1681_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J; ++ + static const struct snd_soc_dai_ops pcm1681_dai_ops = { + .set_fmt = pcm1681_set_dai_fmt, + .hw_params = pcm1681_hw_params, + .mute_stream = pcm1681_mute, ++ .auto_selectable_formats = &pcm1681_selectable_formats, ++ .num_auto_selectable_formats = 1, + .no_capture_mute = 1, + }; + +diff --git a/sound/soc/codecs/pcm1754.c b/sound/soc/codecs/pcm1754.c +--- a/sound/soc/codecs/pcm1754.c ++++ b/sound/soc/codecs/pcm1754.c +@@ -78,10 +78,16 @@ static int pcm1754_mute_stream(struct snd_soc_dai *dai, int mute, int stream) + return 0; + } + ++static const u64 pcm1754_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J; ++ + static const struct snd_soc_dai_ops pcm1754_dai_ops = { + .set_fmt = pcm1754_set_dai_fmt, + .hw_params = pcm1754_hw_params, + .mute_stream = pcm1754_mute_stream, ++ .auto_selectable_formats = &pcm1754_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static const struct snd_soc_dai_driver pcm1754_dai = { +diff --git a/sound/soc/codecs/pcm1789.c b/sound/soc/codecs/pcm1789.c +--- a/sound/soc/codecs/pcm1789.c ++++ b/sound/soc/codecs/pcm1789.c +@@ -164,11 +164,18 @@ static int pcm1789_trigger(struct snd_pcm_substream *substream, int cmd, + return ret; + } + ++static const u64 pcm1789_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J; ++ + static const struct snd_soc_dai_ops pcm1789_dai_ops = { + .set_fmt = pcm1789_set_dai_fmt, + .hw_params = pcm1789_hw_params, + .mute_stream = pcm1789_mute, + .trigger = pcm1789_trigger, ++ .auto_selectable_formats = &pcm1789_selectable_formats, ++ .num_auto_selectable_formats = 1, + .no_capture_mute = 1, + }; + +diff --git a/sound/soc/codecs/pcm179x.c b/sound/soc/codecs/pcm179x.c +--- a/sound/soc/codecs/pcm179x.c ++++ b/sound/soc/codecs/pcm179x.c +@@ -142,10 +142,16 @@ static int pcm179x_hw_params(struct snd_pcm_substream *substream, + return 0; + } + ++static const u64 pcm179x_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J; ++ + static const struct snd_soc_dai_ops pcm179x_dai_ops = { + .set_fmt = pcm179x_set_dai_fmt, + .hw_params = pcm179x_hw_params, + .mute_stream = pcm179x_mute, ++ .auto_selectable_formats = &pcm179x_selectable_formats, ++ .num_auto_selectable_formats = 1, + .no_capture_mute = 1, + }; + +diff --git a/sound/soc/codecs/pcm186x.c b/sound/soc/codecs/pcm186x.c +--- a/sound/soc/codecs/pcm186x.c ++++ b/sound/soc/codecs/pcm186x.c +@@ -473,11 +473,20 @@ static int pcm186x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + return 0; + } + ++static const u64 pcm186x_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_B | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF; ++ + static const struct snd_soc_dai_ops pcm186x_dai_ops = { + .set_sysclk = pcm186x_set_dai_sysclk, + .set_tdm_slot = pcm186x_set_tdm_slot, + .set_fmt = pcm186x_set_fmt, + .hw_params = pcm186x_hw_params, ++ .auto_selectable_formats = &pcm186x_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static struct snd_soc_dai_driver pcm1863_dai = { +diff --git a/sound/soc/codecs/pcm3060.c b/sound/soc/codecs/pcm3060.c +--- a/sound/soc/codecs/pcm3060.c ++++ b/sound/soc/codecs/pcm3060.c +@@ -164,10 +164,18 @@ static int pcm3060_hw_params(struct snd_pcm_substream *substream, + return 0; + } + ++static const u64 pcm3060_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_NB_NF; ++ + static const struct snd_soc_dai_ops pcm3060_dai_ops = { + .set_sysclk = pcm3060_set_sysclk, + .set_fmt = pcm3060_set_fmt, + .hw_params = pcm3060_hw_params, ++ .auto_selectable_formats = &pcm3060_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + #define PCM3060_DAI_RATES_ADC (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | \ +diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c +--- a/sound/soc/codecs/pcm512x.c ++++ b/sound/soc/codecs/pcm512x.c +@@ -6,7 +6,7 @@ + * Copyright 2014 Linaro Ltd + */ + +- ++#include + #include + #include + #include +@@ -399,10 +399,9 @@ static int pcm512x_digital_playback_switch_get(struct snd_kcontrol *kcontrol, + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + +- mutex_lock(&pcm512x->mutex); ++ guard(mutex)(&pcm512x->mutex); + ucontrol->value.integer.value[0] = !(pcm512x->mute & 0x4); + ucontrol->value.integer.value[1] = !(pcm512x->mute & 0x2); +- mutex_unlock(&pcm512x->mutex); + + return 0; + } +@@ -414,7 +413,7 @@ static int pcm512x_digital_playback_switch_put(struct snd_kcontrol *kcontrol, + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + int ret, changed = 0; + +- mutex_lock(&pcm512x->mutex); ++ guard(mutex)(&pcm512x->mutex); + + if ((pcm512x->mute & 0x4) == (ucontrol->value.integer.value[0] << 2)) { + pcm512x->mute ^= 0x4; +@@ -430,13 +429,10 @@ static int pcm512x_digital_playback_switch_put(struct snd_kcontrol *kcontrol, + if (ret != 0) { + dev_err(component->dev, + "Failed to update digital mute: %d\n", ret); +- mutex_unlock(&pcm512x->mutex); + return ret; + } + } + +- mutex_unlock(&pcm512x->mutex); +- + return changed; + } + +@@ -1465,7 +1461,7 @@ static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) + int ret; + unsigned int mute_det; + +- mutex_lock(&pcm512x->mutex); ++ guard(mutex)(&pcm512x->mutex); + + if (mute) { + pcm512x->mute |= 0x1; +@@ -1475,7 +1471,7 @@ static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) + if (ret != 0) { + dev_err(component->dev, + "Failed to set digital mute: %d\n", ret); +- goto unlock; ++ return ret; + } + + regmap_read_poll_timeout(pcm512x->regmap, +@@ -1488,7 +1484,7 @@ static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) + if (ret != 0) { + dev_err(component->dev, + "Failed to update digital mute: %d\n", ret); +- goto unlock; ++ return ret; + } + + regmap_read_poll_timeout(pcm512x->regmap, +@@ -1499,18 +1495,24 @@ static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) + 200, 10000); + } + +-unlock: +- mutex_unlock(&pcm512x->mutex); +- + return ret; + } + ++static const u64 pcm512x_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_B; ++ + static const struct snd_soc_dai_ops pcm512x_dai_ops = { + .startup = pcm512x_dai_startup, + .hw_params = pcm512x_hw_params, + .set_fmt = pcm512x_set_fmt, + .mute_stream = pcm512x_mute, + .set_bclk_ratio = pcm512x_set_bclk_ratio, ++ .auto_selectable_formats = &pcm512x_selectable_formats, ++ .num_auto_selectable_formats = 1, + .no_capture_mute = 1, + }; + +diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c +--- a/sound/soc/codecs/pcm6240.c ++++ b/sound/soc/codecs/pcm6240.c +@@ -12,6 +12,7 @@ + // Author: Shenghao Ding + // + ++#include + #include + #include + #include +@@ -605,7 +606,7 @@ static int pcmdev_get_volsw(struct snd_kcontrol *kcontrol, + unsigned int reg = mc->reg; + unsigned int val; + +- mutex_lock(&pcm_dev->codec_lock); ++ guard(mutex)(&pcm_dev->codec_lock); + + if (pcm_dev->chip_id == PCM1690) { + ret = pcmdev_dev_read(pcm_dev, dev_no, PCM1690_REG_MODE_CTRL, +@@ -613,18 +614,18 @@ static int pcmdev_get_volsw(struct snd_kcontrol *kcontrol, + if (ret) { + dev_err(pcm_dev->dev, "%s: read mode err=%d\n", + __func__, ret); +- goto out; ++ return ret; + } + val &= PCM1690_REG_MODE_CTRL_DAMS_MSK; + /* Set to wide-range mode, before using vol ctrl. */ + if (!val && vol_ctrl_type == PCMDEV_PCM1690_VOL_CTRL) { + ucontrol->value.integer.value[0] = -25500; +- goto out; ++ return ret; + } + /* Set to fine mode, before using fine vol ctrl. */ + if (val && vol_ctrl_type == PCMDEV_PCM1690_FINE_VOL_CTRL) { + ucontrol->value.integer.value[0] = -12750; +- goto out; ++ return ret; + } + } + +@@ -632,15 +633,14 @@ static int pcmdev_get_volsw(struct snd_kcontrol *kcontrol, + if (ret) { + dev_err(pcm_dev->dev, "%s: read err=%d\n", + __func__, ret); +- goto out; ++ return ret; + } + + val = (val >> shift) & mask; + val = (val > max) ? max : val; + val = mc->invert ? max - val : val; + ucontrol->value.integer.value[0] = val; +-out: +- mutex_unlock(&pcm_dev->codec_lock); ++ + return ret; + } + +@@ -678,7 +678,7 @@ static int pcmdev_put_volsw(struct snd_kcontrol *kcontrol, + unsigned int val, val_mask; + unsigned int reg = mc->reg; + +- mutex_lock(&pcm_dev->codec_lock); ++ guard(mutex)(&pcm_dev->codec_lock); + val = ucontrol->value.integer.value[0] & mask; + val = (val > max) ? max : val; + val = mc->invert ? max - val : val; +@@ -702,7 +702,7 @@ static int pcmdev_put_volsw(struct snd_kcontrol *kcontrol, + __func__, rc); + else + rc = 1; +- mutex_unlock(&pcm_dev->codec_lock); ++ + return rc; + } + +@@ -1577,10 +1577,10 @@ static int pcmdevice_comp_probe(struct snd_soc_component *comp) + { + struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(comp); + struct i2c_adapter *adap = pcm_dev->client->adapter; +- const struct firmware *fw_entry = NULL; ++ const struct firmware *fw_entry __free(firmware) = NULL; + int ret, i, j; + +- mutex_lock(&pcm_dev->codec_lock); ++ guard(mutex)(&pcm_dev->codec_lock); + + pcm_dev->component = comp; + +@@ -1588,7 +1588,7 @@ static int pcmdevice_comp_probe(struct snd_soc_component *comp) + for (j = 0; j < 2; j++) { + ret = pcmdev_gain_ctrl_add(pcm_dev, i, j); + if (ret < 0) +- goto out; ++ return ret; + } + } + +@@ -1621,21 +1621,17 @@ static int pcmdevice_comp_probe(struct snd_soc_component *comp) + if (ret) { + dev_err(pcm_dev->dev, "%s: request %s err = %d\n", __func__, + pcm_dev->bin_name, ret); +- goto out; ++ return ret; + } + + ret = pcmdev_regbin_ready(fw_entry, pcm_dev); + if (ret) { + dev_err(pcm_dev->dev, "%s: %s parse err = %d\n", __func__, + pcm_dev->bin_name, ret); +- goto out; ++ return ret; + } +- ret = pcmdev_profile_ctrl_add(pcm_dev); +-out: +- release_firmware(fw_entry); + +- mutex_unlock(&pcm_dev->codec_lock); +- return ret; ++ return pcmdev_profile_ctrl_add(pcm_dev); + } + + +@@ -1645,9 +1641,8 @@ static void pcmdevice_comp_remove(struct snd_soc_component *codec) + + if (!pcm_dev) + return; +- mutex_lock(&pcm_dev->codec_lock); ++ guard(mutex)(&pcm_dev->codec_lock); + pcmdevice_config_info_remove(pcm_dev); +- mutex_unlock(&pcm_dev->codec_lock); + } + + static const struct snd_soc_dapm_widget pcmdevice_dapm_widgets[] = { +@@ -1890,9 +1885,9 @@ static int pcmdevice_mute(struct snd_soc_dai *dai, int mute, int stream) + else + block_type = PCMDEVICE_BIN_BLK_PRE_POWER_UP; + +- mutex_lock(&pcm_dev->codec_lock); ++ guard(mutex)(&pcm_dev->codec_lock); + pcmdevice_select_cfg_blk(pcm_dev, pcm_dev->cur_conf, block_type); +- mutex_unlock(&pcm_dev->codec_lock); ++ + return 0; + } + +diff --git a/sound/soc/codecs/peb2466.c b/sound/soc/codecs/peb2466.c +--- a/sound/soc/codecs/peb2466.c ++++ b/sound/soc/codecs/peb2466.c +@@ -6,6 +6,7 @@ + // + // Author: Herve Codina + ++#include + #include + #include + #include +@@ -1537,17 +1538,14 @@ static int peb2466_fw_parse(struct snd_soc_component *component, + + static int peb2466_load_coeffs(struct snd_soc_component *component, const char *fw_name) + { +- const struct firmware *fw; ++ const struct firmware *fw __free(firmware) = NULL; + int ret; + + ret = request_firmware(&fw, fw_name, component->dev); + if (ret) + return ret; + +- ret = peb2466_fw_parse(component, fw->data, fw->size); +- release_firmware(fw); +- +- return ret; ++ return peb2466_fw_parse(component, fw->data, fw->size); + } + + static int peb2466_component_probe(struct snd_soc_component *component) +@@ -1704,13 +1702,11 @@ static int peb2466_chip_gpio_update_bits(struct peb2466 *peb2466, unsigned int x + * So, a specific cache value is used. + */ + +- mutex_lock(&peb2466->gpio.lock); ++ guard(mutex)(&peb2466->gpio.lock); + + cache = peb2466_chip_gpio_get_cache(peb2466, xr_reg); +- if (!cache) { +- ret = -EINVAL; +- goto end; +- } ++ if (!cache) ++ return -EINVAL; + + tmp = *cache; + tmp &= ~mask; +@@ -1718,14 +1714,11 @@ static int peb2466_chip_gpio_update_bits(struct peb2466 *peb2466, unsigned int x + + ret = regmap_write(peb2466->regmap, xr_reg, tmp); + if (ret) +- goto end; ++ return ret; + + *cache = tmp; +- ret = 0; + +-end: +- mutex_unlock(&peb2466->gpio.lock); +- return ret; ++ return 0; + } + + static int peb2466_chip_gpio_set(struct gpio_chip *c, unsigned int offset, +diff --git a/sound/soc/codecs/pm4125-sdw.c b/sound/soc/codecs/pm4125-sdw.c +--- a/sound/soc/codecs/pm4125-sdw.c ++++ b/sound/soc/codecs/pm4125-sdw.c +@@ -464,10 +464,16 @@ static int __maybe_unused pm4125_sdw_runtime_suspend(struct device *dev) + static int __maybe_unused pm4125_sdw_runtime_resume(struct device *dev) + { + struct pm4125_sdw_priv *priv = dev_get_drvdata(dev); ++ int ret; + + if (priv->regmap) { + regcache_cache_only(priv->regmap, false); +- regcache_sync(priv->regmap); ++ ret = regcache_sync(priv->regmap); ++ if (ret) { ++ regcache_cache_only(priv->regmap, true); ++ regcache_mark_dirty(priv->regmap); ++ return ret; ++ } + } + + return 0; +diff --git a/sound/soc/codecs/pm4125.c b/sound/soc/codecs/pm4125.c +--- a/sound/soc/codecs/pm4125.c ++++ b/sound/soc/codecs/pm4125.c +@@ -5,6 +5,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1295,7 +1296,7 @@ static const struct irq_domain_ops pm4125_domain_ops = { + + static int pm4125_irq_init(struct pm4125_priv *pm4125, struct device *dev) + { +- pm4125->virq = irq_domain_add_linear(NULL, 1, &pm4125_domain_ops, NULL); ++ pm4125->virq = irq_domain_create_linear(NULL, 1, &pm4125_domain_ops, NULL); + if (!(pm4125->virq)) { + dev_err(dev, "%s: Failed to add IRQ domain\n", __func__); + return -EINVAL; +diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c +--- a/sound/soc/codecs/rt1017-sdca-sdw.c ++++ b/sound/soc/codecs/rt1017-sdca-sdw.c +@@ -784,7 +784,12 @@ static int rt1017_sdca_dev_resume(struct device *dev) + } + + regcache_cache_only(rt1017->regmap, false); +- regcache_sync(rt1017->regmap); ++ ret = regcache_sync(rt1017->regmap); ++ if (ret) { ++ regcache_cache_only(rt1017->regmap, true); ++ regcache_mark_dirty(rt1017->regmap); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c +--- a/sound/soc/codecs/rt1316-sdw.c ++++ b/sound/soc/codecs/rt1316-sdw.c +@@ -756,7 +756,12 @@ static int rt1316_dev_resume(struct device *dev) + } + + regcache_cache_only(rt1316->regmap, false); +- regcache_sync(rt1316->regmap); ++ ret = regcache_sync(rt1316->regmap); ++ if (ret) { ++ regcache_cache_only(rt1316->regmap, true); ++ regcache_mark_dirty(rt1316->regmap); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c +--- a/sound/soc/codecs/rt1318-sdw.c ++++ b/sound/soc/codecs/rt1318-sdw.c +@@ -830,7 +830,12 @@ static int rt1318_dev_resume(struct device *dev) + return ret; + + regcache_cache_only(rt1318->regmap, false); +- regcache_sync(rt1318->regmap); ++ ret = regcache_sync(rt1318->regmap); ++ if (ret) { ++ regcache_cache_only(rt1318->regmap, true); ++ regcache_mark_dirty(rt1318->regmap); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c +--- a/sound/soc/codecs/rt1320-sdw.c ++++ b/sound/soc/codecs/rt1320-sdw.c +@@ -410,6 +410,402 @@ static const struct reg_sequence rt1321_blind_write[] = { + { 0x41001988, 0x03 }, + }; + ++static const struct reg_sequence rt1321_va1_blind_write[] = { ++ { 0x0000c003, 0xf0 }, ++ { 0x0000c01b, 0xfc }, ++ { 0x0000c5c3, 0xf2 }, ++ { 0x0000c5c2, 0x00 }, ++ { 0x0000c5c1, 0x10 }, ++ { 0x0000c5c0, 0x04 }, ++ { 0x0000c5c7, 0x03 }, ++ { 0x0000c5c6, 0x10 }, ++ { 0x0000c526, 0x47 }, ++ { 0x0000c5c4, 0x12 }, ++ { 0x0000c5c5, 0x60 }, ++ { 0x0000c520, 0x10 }, ++ { 0x0000c521, 0x32 }, ++ { 0x0000c5c7, 0x00 }, ++ { 0x0000c5c8, 0x03 }, ++ { 0x0000c5d3, 0x08 }, ++ { 0x0000c5d2, 0x0a }, ++ { 0x0000c5d1, 0x49 }, ++ { 0x0000c5d0, 0x0f }, ++ { 0x0000c580, 0x10 }, ++ { 0x0000c581, 0x32 }, ++ { 0x0000c582, 0x01 }, ++ { 0x0000c682, 0x60 }, ++ { 0x0000c019, 0x10 }, ++ { 0x0000c5f0, 0x01 }, ++ { 0x0000c5f7, 0x22 }, ++ { 0x0000c5f6, 0x22 }, ++ { 0x0000c057, 0x51 }, ++ { 0x0000c054, 0x55 }, ++ { 0x0000c053, 0x55 }, ++ { 0x0000c052, 0x55 }, ++ { 0x0000c051, 0x01 }, ++ { 0x0000c050, 0x15 }, ++ { 0x0000c060, 0x99 }, ++ { 0x0000c030, 0x55 }, ++ { 0x0000c061, 0x55 }, ++ { 0x0000c063, 0x55 }, ++ { 0x0000c065, 0xa5 }, ++ { 0x0000c06b, 0x0a }, ++ { 0x0000ca05, 0xd6 }, ++ { 0x0000ca06, 0x11 }, ++ { 0x0000ca07, 0x1e }, ++ { 0x0000ca25, 0xd6 }, ++ { 0x0000ca26, 0x11 }, ++ { 0x0000ca27, 0x1e }, ++ { 0x0000cd00, 0x05 }, ++ { 0x0000cd81, 0x49 }, ++ { 0x0000cd82, 0x49 }, ++ { 0x0000c604, 0x40 }, ++ { 0x0000c609, 0x40 }, ++ { 0x0000c046, 0xf7 }, ++ { 0x0000c045, 0xff }, ++ { 0x0000c044, 0xff }, ++ { 0x0000c043, 0xff }, ++ { 0x0000c042, 0xff }, ++ { 0x0000c041, 0xff }, ++ { 0x0000c040, 0xff }, ++ { 0x0000c049, 0xff }, ++ { 0x0000c028, 0x3f }, ++ { 0x0000c020, 0x3f }, ++ { 0x0000c032, 0x13 }, ++ { 0x0000c033, 0x01 }, ++ { 0x0000cc10, 0x01 }, ++ { 0x0000dc20, 0x03 }, ++ { 0x0000de03, 0x05 }, ++ { 0x0000dc00, 0x00 }, ++ { 0x0000c700, 0xf0 }, ++ { 0x0000c701, 0x13 }, ++ { 0x0000c900, 0xc3 }, ++ { 0x0000c570, 0x08 }, ++ { 0x0000c086, 0x02 }, ++ { 0x0000c085, 0x7f }, ++ { 0x0000c084, 0x00 }, ++ { 0x0000c081, 0xff }, ++ { 0x0000f084, 0x0f }, ++ { 0x0000f083, 0xff }, ++ { 0x0000f082, 0xff }, ++ { 0x0000f081, 0xff }, ++ { 0x0000f080, 0xff }, ++ { 0x20003003, 0x3f }, ++ { 0x20005818, 0x81 }, ++ { 0x20009018, 0x81 }, ++ { 0x2000301c, 0x81 }, ++ { 0x0000c003, 0xc0 }, ++ { 0x0000c047, 0x80 }, ++ { 0x0000d541, 0x80 }, ++ { 0x0000d487, 0x0b }, ++ { 0x0000d487, 0x3b }, ++ { 0x0000d486, 0xc3 }, ++ { 0x0000d470, 0x89 }, ++ { 0x0000d471, 0x3a }, ++ { 0x0000d472, 0x3d }, ++ { 0x0000d474, 0x11 }, ++ { 0x0000d475, 0x32 }, ++ { 0x0000d476, 0x64 }, ++ { 0x0000d477, 0x10 }, ++ { 0x0000d478, 0xff }, ++ { 0x0000d479, 0x20 }, ++ { 0x0000d47a, 0x10 }, ++ { 0x0000d73c, 0xb7 }, ++ { 0x0000d73d, 0xd7 }, ++ { 0x0000d73e, 0x00 }, ++ { 0x0000d73f, 0x10 }, ++ { 0x1000cd56, 0x00 }, ++ { 0x3fc2dfc0, 0x03 }, ++ { 0x3fc2dfc1, 0x00 }, ++ { 0x3fc2dfc2, 0x00 }, ++ { 0x3fc2dfc3, 0x00 }, ++ { 0x3fc2dfc4, 0x01 }, ++ { 0x3fc2dfc5, 0x00 }, ++ { 0x3fc2dfc6, 0x00 }, ++ { 0x3fc2dfc7, 0x00 }, ++ { 0x3fc2df80, 0x00 }, ++ { 0x3fc2df81, 0x00 }, ++ { 0x3fc2df82, 0x00 }, ++ { 0x3fc2df83, 0x00 }, ++ { 0x0000d541, 0x40 }, ++ { 0x0000d486, 0x43 }, ++ { 0x1000db00, 0x04 }, ++ { 0x1000db01, 0x00 }, ++ { 0x1000db02, 0x10 }, ++ { 0x1000db03, 0x00 }, ++ { 0x1000db04, 0x00 }, ++ { 0x1000db05, 0x45 }, ++ { 0x1000db06, 0x0d }, ++ { 0x1000db07, 0x01 }, ++ { 0x1000db08, 0x00 }, ++ { 0x1000db09, 0x00 }, ++ { 0x1000db0a, 0xbf }, ++ { 0x1000db0b, 0x0b }, ++ { 0x1000db0c, 0x11 }, ++ { 0x1000db0d, 0x00 }, ++ { 0x1000db0e, 0x00 }, ++ { 0x1000db0f, 0x00 }, ++ { 0x1000db10, 0x2c }, ++ { 0x1000db11, 0xfa }, ++ { 0x1000db12, 0x00 }, ++ { 0x1000db13, 0x00 }, ++ { 0x1000db14, 0x09 }, ++ { 0x0000d540, 0x21 }, ++ { 0x0000c570, 0x08 }, ++ { 0x0000d714, 0x17 }, ++ { 0x0000c5c3, 0xf2 }, ++ { 0x0000c5c8, 0x03 }, ++ { 0x20009012, 0x00 }, ++ { 0x0000dd08, 0x17 }, ++ { 0x0000dd09, 0x0e }, ++ { 0x0000dd0a, 0x17 }, ++ { 0x0000dd0b, 0x0e }, ++ { 0x0000c570, 0x08 }, ++ { 0x0000d471, 0x3a }, ++ { 0x0000db00, 0x00 }, ++ { 0x0000db01, 0x00 }, ++ { 0x0000db02, 0x73 }, ++ { 0x0000db03, 0x00 }, ++ { 0x0000db04, 0x00 }, ++ { 0x0000db05, 0x00 }, ++ { 0x0000db06, 0x00 }, ++ { 0x0000db07, 0x00 }, ++ { 0x0000db08, 0x7f }, ++ { 0x0000db09, 0x00 }, ++ { 0x0000db1a, 0x00 }, ++ { 0x0000db1b, 0x00 }, ++ { 0x0000db19, 0x00 }, ++}; ++ ++static const struct reg_sequence rt1321_va2_blind_write[] = { ++ { 0x0000c003, 0xf0 }, ++ { 0x0000c01b, 0xfc }, ++ { 0x0000c5c3, 0xf2 }, ++ { 0x0000c5c2, 0x00 }, ++ { 0x0000c5c1, 0x10 }, ++ { 0x0000c5c0, 0x04 }, ++ { 0x0000c5c7, 0x03 }, ++ { 0x0000c5c6, 0x10 }, ++ { 0x0000c526, 0x47 }, ++ { 0x0000c5c4, 0x12 }, ++ { 0x0000c5c5, 0x60 }, ++ { 0x0000c520, 0x10 }, ++ { 0x0000c521, 0x32 }, ++ { 0x0000c5c7, 0x00 }, ++ { 0x0000c5c8, 0x03 }, ++ { 0x0000c5d3, 0x08 }, ++ { 0x0000c5d2, 0x0a }, ++ { 0x0000c5d1, 0x49 }, ++ { 0x0000c5d0, 0x0f }, ++ { 0x0000c580, 0x10 }, ++ { 0x0000c581, 0x32 }, ++ { 0x0000c582, 0x01 }, ++ { 0x0000c682, 0x60 }, ++ { 0x0000c019, 0x10 }, ++ { 0x0000c5f0, 0x01 }, ++ { 0x0000c5f7, 0x22 }, ++ { 0x0000c5f6, 0x22 }, ++ { 0x0000c057, 0x51 }, ++ { 0x0000c054, 0x55 }, ++ { 0x0000c053, 0x55 }, ++ { 0x0000c052, 0x55 }, ++ { 0x0000c051, 0x01 }, ++ { 0x0000c050, 0x15 }, ++ { 0x0000c060, 0x99 }, ++ { 0x0000c030, 0x55 }, ++ { 0x0000c061, 0x55 }, ++ { 0x0000c063, 0x55 }, ++ { 0x0000c065, 0xa5 }, ++ { 0x0000c06b, 0x0a }, ++ { 0x0000ca05, 0xd6 }, ++ { 0x0000ca06, 0x11 }, ++ { 0x0000ca07, 0x1e }, ++ { 0x0000ca25, 0xd6 }, ++ { 0x0000ca26, 0x11 }, ++ { 0x0000ca27, 0x1e }, ++ { 0x0000cd00, 0x05 }, ++ { 0x0000cd81, 0x49 }, ++ { 0x0000cd82, 0x49 }, ++ { 0x0000c604, 0x40 }, ++ { 0x0000c609, 0x40 }, ++ { 0x0000c046, 0xf7 }, ++ { 0x0000c045, 0xff }, ++ { 0x0000c044, 0xff }, ++ { 0x0000c043, 0xff }, ++ { 0x0000c042, 0xff }, ++ { 0x0000c041, 0xff }, ++ { 0x0000c040, 0xff }, ++ { 0x0000c049, 0xff }, ++ { 0x0000c028, 0x3f }, ++ { 0x0000c020, 0x3f }, ++ { 0x0000c032, 0x13 }, ++ { 0x0000c033, 0x01 }, ++ { 0x0000cc10, 0x01 }, ++ { 0x0000dc20, 0x03 }, ++ { 0x0000de03, 0x05 }, ++ { 0x0000dc00, 0x00 }, ++ { 0x0000c700, 0xf0 }, ++ { 0x0000c701, 0x13 }, ++ { 0x0000c900, 0xc3 }, ++ { 0x0000c570, 0x08 }, ++ { 0x0000c086, 0x02 }, ++ { 0x0000c085, 0x7f }, ++ { 0x0000c084, 0x00 }, ++ { 0x0000c081, 0xff }, ++ { 0x0000f084, 0x0f }, ++ { 0x0000f083, 0xff }, ++ { 0x0000f082, 0xff }, ++ { 0x0000f081, 0xff }, ++ { 0x0000f080, 0xff }, ++ { 0x20003003, 0x3f }, ++ { 0x20005818, 0x81 }, ++ { 0x20009018, 0x81 }, ++ { 0x2000301c, 0x81 }, ++ { 0x0000c003, 0xc0 }, ++ { 0x0000c047, 0x80 }, ++ { 0x0000d541, 0x80 }, ++ { 0x0000d487, 0x0b }, ++ { 0x0000d487, 0x3b }, ++ { 0x0000d486, 0xc3 }, ++ { 0x0000d470, 0x89 }, ++ { 0x0000d471, 0x3a }, ++ { 0x0000d472, 0x3d }, ++ { 0x0000d474, 0x11 }, ++ { 0x0000d475, 0x32 }, ++ { 0x0000d476, 0x64 }, ++ { 0x0000d477, 0x10 }, ++ { 0x0000d478, 0xff }, ++ { 0x0000d479, 0x20 }, ++ { 0x0000d47a, 0x10 }, ++ { 0x10008000, 0x67 }, ++ { 0x10008001, 0x80 }, ++ { 0x10008002, 0x00 }, ++ { 0x10008003, 0x00 }, ++ { 0x1000cd56, 0x00 }, ++ { 0x0000d486, 0x43 }, ++ { 0x3fc2dfc3, 0x00 }, ++ { 0x3fc2dfc2, 0x00 }, ++ { 0x3fc2dfc1, 0x00 }, ++ { 0x3fc2dfc0, 0x03 }, ++ { 0x3fc2dfc7, 0x00 }, ++ { 0x3fc2dfc6, 0x00 }, ++ { 0x3fc2dfc5, 0x00 }, ++ { 0x3fc2dfc4, 0x01 }, ++ { 0x3fc2dfa3, 0x00 }, ++ { 0x3fc2dfa2, 0x00 }, ++ { 0x3fc2dfa1, 0x00 }, ++ { 0x3fc2dfa0, 0x00 }, ++ { 0x3fc2df80, 0x10 }, ++ { 0x3fc2df81, 0x20 }, ++ { 0x3fc2df82, 0x00 }, ++ { 0x3fc2df83, 0x00 }, ++ { 0x3fc2df84, 0x50 }, ++ { 0x3fc2df85, 0x19 }, ++ { 0x3fc2df86, 0x00 }, ++ { 0x3fc2df87, 0x00 }, ++ { 0x3fc2df88, 0x52 }, ++ { 0x3fc2df89, 0x23 }, ++ { 0x3fc2df8a, 0x00 }, ++ { 0x3fc2df8b, 0x00 }, ++ { 0x3fc2df8c, 0xe0 }, ++ { 0x3fc2df8d, 0x2e }, ++ { 0x3fc2df8e, 0x00 }, ++ { 0x3fc2df8f, 0x00 }, ++ { 0x3fc2df90, 0xe0 }, ++ { 0x3fc2df91, 0x2e }, ++ { 0x3fc2df92, 0x00 }, ++ { 0x3fc2df93, 0x00 }, ++ { 0x3fc2df94, 0x01 }, ++ { 0x3fc2df95, 0x08 }, ++ { 0x3fc2df96, 0x00 }, ++ { 0x3fc2df97, 0x00 }, ++ { 0x3fc2df40, 0x80 }, ++ { 0x3fc2df41, 0xbb }, ++ { 0x3fc2df42, 0x00 }, ++ { 0x3fc2df43, 0x00 }, ++ { 0x3fc2df44, 0xc0 }, ++ { 0x3fc2df45, 0x99 }, ++ { 0x3fc2df46, 0x01 }, ++ { 0x3fc2df47, 0x00 }, ++ { 0x3fc2df48, 0x00 }, ++ { 0x3fc2df49, 0x00 }, ++ { 0x3fc2df4a, 0x00 }, ++ { 0x3fc2df4b, 0x00 }, ++ { 0x3fc2df4c, 0x00 }, ++ { 0x3fc2df4d, 0x00 }, ++ { 0x3fc2df4e, 0x00 }, ++ { 0x3fc2df4f, 0x00 }, ++ { 0x3fc2df50, 0x01 }, ++ { 0x3fc2df51, 0x00 }, ++ { 0x3fc2df52, 0x00 }, ++ { 0x3fc2df53, 0x00 }, ++ { 0x3fc2df54, 0x01 }, ++ { 0x3fc2df55, 0x00 }, ++ { 0x3fc2df56, 0x00 }, ++ { 0x3fc2df57, 0x00 }, ++ { 0x3fc2df58, 0x00 }, ++ { 0x3fc2df59, 0x00 }, ++ { 0x3fc2df5a, 0x00 }, ++ { 0x3fc2df5b, 0x00 }, ++ { 0x3fc2df5c, 0x01 }, ++ { 0x3fc2df5d, 0x00 }, ++ { 0x3fc2df5e, 0x00 }, ++ { 0x3fc2df5f, 0x00 }, ++ { 0x3fc2df60, 0x00 }, ++ { 0x3fc2df61, 0x00 }, ++ { 0x3fc2df62, 0x00 }, ++ { 0x3fc2df63, 0x00 }, ++ { 0x3fc2df64, 0x00 }, ++ { 0x3fc2df65, 0x00 }, ++ { 0x3fc2df66, 0x00 }, ++ { 0x3fc2df67, 0x10 }, ++ { 0x3fc2df68, 0x01 }, ++ { 0x3fc2df69, 0x00 }, ++ { 0x3fc2df6a, 0x00 }, ++ { 0x3fc2df6b, 0x00 }, ++ { 0x3fc2df6c, 0x01 }, ++ { 0x3fc2df6d, 0x00 }, ++ { 0x3fc2df6e, 0x00 }, ++ { 0x3fc2df6f, 0x00 }, ++ { 0x3fc2df70, 0x04 }, ++ { 0x3fc2df71, 0x00 }, ++ { 0x3fc2df72, 0x00 }, ++ { 0x3fc2df73, 0x00 }, ++ { 0x3fc2df74, 0x01 }, ++ { 0x3fc2df75, 0x00 }, ++ { 0x3fc2df76, 0x00 }, ++ { 0x3fc2df77, 0x00 }, ++ { 0x1000db00, 0x04 }, ++ { 0x1000db01, 0x00 }, ++ { 0x1000db02, 0x10 }, ++ { 0x1000db03, 0x00 }, ++ { 0x1000db04, 0x00 }, ++ { 0x1000db05, 0x45 }, ++ { 0x1000db06, 0x0d }, ++ { 0x1000db07, 0x01 }, ++ { 0x1000db08, 0x00 }, ++ { 0x1000db09, 0x00 }, ++ { 0x1000db0a, 0xbf }, ++ { 0x1000db0b, 0x0b }, ++ { 0x1000db0c, 0x11 }, ++ { 0x1000db0d, 0x00 }, ++ { 0x1000db0e, 0x00 }, ++ { 0x1000db0f, 0x00 }, ++ { 0x1000db10, 0x2c }, ++ { 0x1000db11, 0xfa }, ++ { 0x1000db12, 0x00 }, ++ { 0x1000db13, 0x00 }, ++ { 0x1000db14, 0x09 }, ++ { 0x0000d540, 0x21 }, ++ { 0x0000d714, 0x17 }, ++ { 0x0000dd0b, 0x0d }, ++ { 0x0000dd0a, 0xff }, ++ { 0x0000dd09, 0x0d }, ++ { 0x0000dd08, 0xff }, ++ { 0x0000c5fb, 0x12 }, ++ { 0x0000c570, 0x08 }, ++}; ++ + static const struct reg_default rt1320_reg_defaults[] = { + { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, +@@ -440,6 +836,7 @@ static const struct reg_default rt1320_mbq_defaults[] = { + static bool rt1320_readable_register(struct device *dev, unsigned int reg) + { + switch (reg) { ++ case 0x004d: + case 0xc000 ... 0xc086: + case 0xc400 ... 0xc409: + case 0xc480 ... 0xc48f: +@@ -497,10 +894,15 @@ static bool rt1320_readable_register(struct device *dev, unsigned int reg) + case RT1321_PATCH_MAIN_VER ... RT1321_PATCH_BETA_VER: + case 0x1000f008: + case 0x1000f021: +- case 0x2000300f: ++ case 0x20003000 ... 0x2000300f: + case 0x2000301c: +- case 0x2000900f: ++ case 0x20003040 ... 0x20003059: ++ case 0x20005800 ... 0x2000580f: ++ case 0x20005818: ++ case 0x20005840 ... 0x20005859: ++ case 0x20009000 ... 0x2000900f: + case 0x20009018: ++ case 0x20009040 ... 0x20009059: + case 0x3fc000c0 ... 0x3fc2dfc8: + case 0x3fe00000 ... 0x3fe36fff: + /* 0x40801508/0x40801809/0x4080180a/0x40801909/0x4080190a */ +@@ -545,6 +947,7 @@ static bool rt1320_readable_register(struct device *dev, unsigned int reg) + static bool rt1320_volatile_register(struct device *dev, unsigned int reg) + { + switch (reg) { ++ case 0x004d: + case 0xc000: + case 0xc003: + case 0xc081: +@@ -596,15 +999,20 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg) + case 0x1000c000 ... 0x1000dfff: + case 0x1000f008: + case 0x1000f021: +- case 0x2000300f: ++ case 0x2000300e ... 0x2000300f: + case 0x2000301c: +- case 0x2000900f: ++ case 0x20003040 ... 0x20003059: ++ case 0x2000580e ... 0x2000580f: ++ case 0x20005818: ++ case 0x20005840 ... 0x20005859: ++ case 0x2000900e ... 0x2000900f: + case 0x20009018: ++ case 0x20009040 ... 0x20009059: + case 0x3fc2ab80 ... 0x3fc2ac4c: + case 0x3fc2b780: + case 0x3fc2bf80 ... 0x3fc2bf83: + case 0x3fc2bfc0 ... 0x3fc2bfc8: +- case 0x3fc2d300 ... 0x3fc2d354: ++ case 0x3fc2d300 ... 0x3fc2d3cc: + case 0x3fc2dfc0 ... 0x3fc2dfc8: + case 0x3fe2e000 ... 0x3fe2e003: + case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0): +@@ -892,6 +1300,42 @@ static int rt1320_check_fw_ready(struct rt1320_sdw_priv *rt1320) + return 0; + } + ++static int rt1320_dspfw_status(struct rt1320_sdw_priv *rt1320) ++{ ++ struct device *dev = &rt1320->sdw_slave->dev; ++ unsigned int fw_status_addr, fw_ready; ++ unsigned int dspfw_run; ++ ++ switch (rt1320->dev_id) { ++ case RT1320_DEV_ID: ++ fw_status_addr = RT1320_DSPFW_STATUS_ADDR; ++ break; ++ case RT1321_DEV_ID: ++ fw_status_addr = RT1321_DSPFW_STATUS_ADDR; ++ break; ++ default: ++ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); ++ return -EINVAL; ++ } ++ ++ regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); ++ fw_ready &= 0x1; ++ ++ if (rt1320->dev_id == RT1321_DEV_ID) { ++ regmap_read(rt1320->regmap, 0xf01e, &dspfw_run); ++ dspfw_run &= 0x1; ++ fw_ready = (!dspfw_run && fw_ready); ++ } ++ ++ if (fw_ready) { ++ dev_dbg(dev, "%s, DSP FW was already\n", __func__); ++ return 1; ++ } ++ ++ dev_dbg(dev, "%s, DSP FW is NOT ready. Please load DSP FW first\n", __func__); ++ return 0; ++} ++ + static int rt1320_check_power_state_ready(struct rt1320_sdw_priv *rt1320, enum rt1320_power_state ps) + { + struct device *dev = &rt1320->sdw_slave->dev; +@@ -982,6 +1426,9 @@ static int rt1320_fw_param_protocol(struct rt1320_sdw_priv *rt1320, enum rt1320_ + if (!tempbuf) + return -ENOMEM; + ++ if (rt1320->dev_id == RT1321_DEV_ID && rt1320->version_id == RT1321_VA2) ++ paramid += 0x0bff0000; ++ + paramhr.moudleid = 1; + paramhr.commandtype = cmdid; + /* 8 is "sizeof(paramid) + sizeof(paramlength)" */ +@@ -1120,45 +1567,73 @@ static void rt1320_calc_r0(struct rt1320_sdw_priv *rt1320) + l_calir0, l_calir0_lo, r_calir0, r_calir0_lo); + } + ++static int rt1320_pilot_tone_output(struct rt1320_sdw_priv *rt1320) ++{ ++ struct device *dev = &rt1320->sdw_slave->dev; ++ int l_targetpostgain, r_targetpostgain; ++ unsigned long long factor = (1 << 12); ++ int l_pilotgain[9], r_pilotgain[9]; ++ const int postgain_step = 234; ++ int targetGain; ++ ++ switch (rt1320->dev_id) { ++ case RT1320_DEV_ID: ++ targetGain = -320000; ++ break; ++ case RT1321_DEV_ID: ++ targetGain = -420000; ++ if (rt1320->version_id == RT1321_VA0) ++ targetGain = -320000; ++ break; ++ default: ++ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); ++ return -EINVAL; ++ } ++ ++ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 70, &l_pilotgain[0], sizeof(l_pilotgain)); ++ rt1320_fw_param_protocol(rt1320, RT1320_GET_PARAM, 71, &r_pilotgain[0], sizeof(r_pilotgain)); ++ dev_dbg(dev, "%s, LR pilotgain %d, %d\n", __func__, l_pilotgain[2], r_pilotgain[2]); ++ ++ /* calculate pilot tone gain */ ++ l_pilotgain[2] = (l_pilotgain[2] * 10000) / factor; ++ r_pilotgain[2] = (r_pilotgain[2] * 10000) / factor; ++ ++ /* calculate post gain to meet target gain */ ++ l_targetpostgain = abs(targetGain - l_pilotgain[2]) / postgain_step; ++ r_targetpostgain = abs(targetGain - r_pilotgain[2]) / postgain_step; ++ l_targetpostgain = 0xfff - l_targetpostgain; ++ r_targetpostgain = 0xfff - r_targetpostgain; ++ dev_dbg(dev, "%s, LR targetpostgain=0x%x, 0x%x\n", __func__, l_targetpostgain, r_targetpostgain); ++ ++ regmap_write(rt1320->regmap, 0xdd0b, (l_targetpostgain & 0xf00) >> 8); ++ regmap_write(rt1320->regmap, 0xdd0a, l_targetpostgain & 0xff); ++ regmap_write(rt1320->regmap, 0xdd09, (r_targetpostgain & 0xf00) >> 8); ++ regmap_write(rt1320->regmap, 0xdd08, r_targetpostgain & 0xff); ++ ++ return 0; ++} ++ + static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) + { + struct device *dev = &rt1320->sdw_slave->dev; + struct rt1320_datafixpoint audfixpoint[2]; + unsigned int reg_c5fb, reg_c570, reg_cd00; +- unsigned int vol_reg[4], fw_ready; ++ unsigned int vol_reg[4]; + unsigned long long l_meanr0, r_meanr0; +- unsigned int fw_status_addr; + int l_re[5], r_re[5]; + int ret, tmp; + unsigned long long factor = (1 << 27); + unsigned short l_advancegain, r_advancegain; + unsigned int delay_s = 7; /* delay seconds for the calibration */ ++ int dspfw_status; + + if (!rt1320->component) + return; + +- switch (rt1320->dev_id) { +- case RT1320_DEV_ID: +- fw_status_addr = RT1320_DSPFW_STATUS_ADDR; +- break; +- case RT1321_DEV_ID: +- fw_status_addr = RT1321_DSPFW_STATUS_ADDR; +- break; +- default: +- dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); +- return; +- } +- +- /* set volume 0dB */ + regmap_read(rt1320->regmap, 0xdd0b, &vol_reg[3]); + regmap_read(rt1320->regmap, 0xdd0a, &vol_reg[2]); + regmap_read(rt1320->regmap, 0xdd09, &vol_reg[1]); + regmap_read(rt1320->regmap, 0xdd08, &vol_reg[0]); +- regmap_write(rt1320->regmap, 0xdd0b, 0x0f); +- regmap_write(rt1320->regmap, 0xdd0a, 0xff); +- regmap_write(rt1320->regmap, 0xdd09, 0x0f); +- regmap_write(rt1320->regmap, 0xdd08, 0xff); +- + regmap_read(rt1320->regmap, 0xc5fb, ®_c5fb); + regmap_read(rt1320->regmap, 0xc570, ®_c570); + regmap_read(rt1320->regmap, 0xcd00, ®_cd00); +@@ -1171,9 +1646,8 @@ static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) + goto _finish_; + } + +- regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); +- fw_ready &= 0x1; +- if (!fw_ready) { ++ dspfw_status = rt1320_dspfw_status(rt1320); ++ if (dspfw_status <= 0) { + dev_dbg(dev, "%s, DSP FW is NOT ready. Please load DSP FW first\n", __func__); + goto _finish_; + } +@@ -1184,8 +1658,19 @@ static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) + goto _finish_; + } + +- if (rt1320->dev_id == RT1320_DEV_ID) +- regmap_write(rt1320->regmap, 0xc5fb, 0x00); ++ /* fine tune pilot tone output */ ++ ret = rt1320_pilot_tone_output(rt1320); ++ if (ret < 0) { ++ dev_dbg(dev, "%s, Failed to tune pilot tone output\n", __func__); ++ goto _finish_; ++ } ++ ++ if (rt1320->dev_id == RT1321_DEV_ID) { ++ regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x00); ++ regmap_write(rt1320->regmap, 0xc5c4, 0x12); ++ } ++ ++ regmap_write(rt1320->regmap, 0xc5fb, 0x00); + regmap_write(rt1320->regmap, 0xc570, 0x0b); + regmap_write(rt1320->regmap, 0xcd00, 0xc5); + +@@ -1246,6 +1731,11 @@ static void rt1320_calibrate(struct rt1320_sdw_priv *rt1320) + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03); + rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x03); + ++ if (rt1320->dev_id == RT1321_DEV_ID) { ++ regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x80); ++ regmap_write(rt1320->regmap, 0xc5c4, 0x10); ++ } ++ + /* advance gain will be set when R0 load, not here */ + regmap_write(rt1320->regmap, 0xdd0b, vol_reg[3]); + regmap_write(rt1320->regmap, 0xdd0a, vol_reg[2]); +@@ -1296,7 +1786,7 @@ static int rt1320_r0_cali_put(struct snd_kcontrol *kcontrol, + static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320) + { + struct sdw_slave *slave = rt1320->sdw_slave; +- const struct firmware *patch; ++ const struct firmware *patch __free(firmware) = NULL; + const char *filename; + unsigned int addr, val, min_addr, max_addr; + const unsigned char *ptr; +@@ -1312,6 +1802,9 @@ static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320) + max_addr = 0x10007fff; + break; + case RT1321_DEV_ID: ++ if (rt1320->version_id == RT1321_VA2) ++ return; ++ + filename = RT1321_VA_MCU_PATCH; + min_addr = 0x10008000; + max_addr = 0x10008fff; +@@ -1347,17 +1840,15 @@ static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320) + + if (addr > max_addr || addr < min_addr) { + dev_err(&slave->dev, "%s: the address 0x%x is wrong", __func__, addr); +- goto _exit_; ++ return; + } + if (val > 0xff) { + dev_err(&slave->dev, "%s: the value 0x%x is wrong", __func__, val); +- goto _exit_; ++ return; + } + regmap_write(rt1320->regmap, addr, val); + } + } +-_exit_: +- release_firmware(patch); + } + } + +@@ -1382,30 +1873,17 @@ static void rt1320_vab_preset(struct rt1320_sdw_priv *rt1320) + static void rt1320_t0_load(struct rt1320_sdw_priv *rt1320, unsigned int l_t0, unsigned int r_t0) + { + struct device *dev = &rt1320->sdw_slave->dev; +- unsigned int factor = (1 << 22), fw_ready; ++ unsigned int factor = (1 << 22); + int l_t0_data[38], r_t0_data[38]; +- unsigned int fw_status_addr; +- +- switch (rt1320->dev_id) { +- case RT1320_DEV_ID: +- fw_status_addr = RT1320_DSPFW_STATUS_ADDR; +- break; +- case RT1321_DEV_ID: +- fw_status_addr = RT1321_DSPFW_STATUS_ADDR; +- break; +- default: +- dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); +- return; +- } ++ int dspfw_status; + + regmap_write(rt1320->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, + RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00); + rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00); + +- regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); +- fw_ready &= 0x1; +- if (!fw_ready) { ++ dspfw_status = rt1320_dspfw_status(rt1320); ++ if (dspfw_status <= 0) { + dev_warn(dev, "%s, DSP FW is NOT ready\n", __func__); + goto _exit_; + } +@@ -1444,7 +1922,7 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) + struct device *dev = &rt1320->sdw_slave->dev; + static const char func_tag[] = "FUNC"; + static const char xu_tag[] = "XU"; +- const struct firmware *rae_fw = NULL; ++ const struct firmware *rae_fw __free(firmware) = NULL; + unsigned int fw_offset; + unsigned char *fw_data; + unsigned char *param_data; +@@ -1483,22 +1961,46 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) + request_firmware(&rae_fw, rae_filename, dev); + if (rae_fw) { + +- /* RAE CRC clear */ +- regmap_write(rt1320->regmap, 0xe80b, 0x0f); +- +- /* RAE stop & CRC disable */ +- regmap_update_bits(rt1320->regmap, 0xe803, 0xbc, 0x00); +- +- while (--retry) { +- regmap_read(rt1320->regmap, 0xe83f, &value); +- if (value & 0x40) +- break; +- usleep_range(1000, 1100); +- } +- if (!retry && !(value & 0x40)) { +- dev_err(dev, "%s: RAE is not ready to load\n", __func__); +- release_firmware(rae_fw); +- return -ETIMEDOUT; ++ switch (rt1320->dev_id) { ++ case RT1320_DEV_ID: ++ /* RAE CRC clear */ ++ regmap_write(rt1320->regmap, 0xe80b, 0x0f); ++ /* RAE stop & CRC disable */ ++ regmap_update_bits(rt1320->regmap, 0xe803, 0xbc, 0x00); ++ while (--retry) { ++ regmap_read(rt1320->regmap, 0xe83f, &value); ++ if (value & 0x40) ++ break; ++ usleep_range(1000, 1100); ++ } ++ if (!retry && !(value & 0x40)) { ++ dev_err(dev, "%s: RAE is not ready to load\n", __func__); ++ return -ETIMEDOUT; ++ } ++ break; ++ case RT1321_DEV_ID: ++ /* RAE CRC clear */ ++ regmap_write(rt1320->regmap, 0x2000300e, 0xc0); ++ regmap_write(rt1320->regmap, 0x2000300f, 0x0f); ++ /* RAE stop & Phase sync & CRC disable */ ++ regmap_update_bits(rt1320->regmap, 0x20003003, 0xfe, 0x00); ++ regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x00); ++ regmap_update_bits(rt1320->regmap, 0x2000301c, 0x01, 0x00); ++ /* check whether write state is ready */ ++ while (--retry) { ++ regmap_read(rt1320->regmap, 0x20003043, &value); ++ if (value & 0x40) ++ break; ++ usleep_range(1000, 1100); ++ } ++ if (!retry && !(value & 0x40)) { ++ dev_err(dev, "%s: RAE is not ready to load\n", __func__); ++ return -ETIMEDOUT; ++ } ++ break; ++ default: ++ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); ++ return -EINVAL; + } + + dev_dbg(dev, "%s, rae_fw size=0x%zx\n", __func__, rae_fw->size); +@@ -1551,7 +2053,6 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) + } + + regcache_cache_bypass(rt1320->regmap, false); +- release_firmware(rae_fw); + + } else { + dev_err(dev, "%s: Failed to load %s firmware\n", __func__, rae_filename); +@@ -1559,18 +2060,34 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320) + goto _exit_; + } + +- /* RAE CRC enable */ +- regmap_update_bits(rt1320->regmap, 0xe803, 0x0c, 0x0c); +- +- /* RAE update */ +- regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x00); +- regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x80); +- +- /* RAE run */ +- regmap_update_bits(rt1320->regmap, 0xe803, 0x80, 0x80); +- +- regmap_read(rt1320->regmap, 0xe80b, &value); +- dev_dbg(dev, "%s: CAE run => 0xe80b reg = 0x%x\n", __func__, value); ++ switch (rt1320->dev_id) { ++ case RT1320_DEV_ID: ++ /* RAE CRC enable */ ++ regmap_update_bits(rt1320->regmap, 0xe803, 0x0c, 0x0c); ++ /* RAE update */ ++ regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x00); ++ regmap_update_bits(rt1320->regmap, 0xe80b, 0x80, 0x80); ++ /* RAE run */ ++ regmap_update_bits(rt1320->regmap, 0xe803, 0x80, 0x80); ++ regmap_read(rt1320->regmap, 0xe80b, &value); ++ dev_dbg(dev, "%s: CAE run => 0xe80b reg = 0x%x\n", __func__, value); ++ break; ++ case RT1321_DEV_ID: ++ /* RAE CRC enable */ ++ regmap_update_bits(rt1320->regmap, 0x20003003, 0x30, 0x30); ++ /* RAE update */ ++ regmap_update_bits(rt1320->regmap, 0x2000301c, 0x80, 0x00); ++ regmap_update_bits(rt1320->regmap, 0x2000301c, 0x80, 0x80); ++ regmap_update_bits(rt1320->regmap, 0x20009018, 0x80, 0x00); ++ regmap_update_bits(rt1320->regmap, 0x20009018, 0x80, 0x80); ++ regmap_update_bits(rt1320->regmap, 0x20005818, 0x80, 0x00); ++ regmap_update_bits(rt1320->regmap, 0x20005818, 0x80, 0x80); ++ /* RAE run */ ++ regmap_update_bits(rt1320->regmap, 0x2000301c, 0x01, 0x01); ++ /* Phase sync eanble */ ++ regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x80); ++ break; ++ } + + rt1320->rae_update_done = true; + +@@ -1598,32 +2115,21 @@ struct rt1320_dspfwheader { + + struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt1320->component); + struct device *dev = &rt1320->sdw_slave->dev; +- unsigned int val, i, fw_offset, fw_ready; +- unsigned int fw_status_addr; ++ unsigned int val, i, fw_offset; + struct rt1320_dspfwheader *fwheader; + struct rt1320_imageinfo *ptr_img; + struct sdw_bpt_section sec[10]; +- const struct firmware *fw = NULL; ++ const struct firmware *fw __free(firmware) = NULL; + unsigned char *fw_data; + bool dev_fw_match = false; + static const char hdr_sig[] = "AFX"; + unsigned int hdr_size = 0; + const char *dmi_vendor, *dmi_product, *dmi_sku; + int len_vendor, len_product, len_sku; ++ unsigned char boot_mode = 0; /* 0: from RAM; 1: from ROM */ ++ unsigned char has_0x3fc00000 = 0; + char filename[512]; + +- switch (rt1320->dev_id) { +- case RT1320_DEV_ID: +- fw_status_addr = RT1320_DSPFW_STATUS_ADDR; +- break; +- case RT1321_DEV_ID: +- fw_status_addr = RT1321_DSPFW_STATUS_ADDR; +- break; +- default: +- dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); +- return; +- } +- + dmi_vendor = dmi_get_system_info(DMI_SYS_VENDOR); + dmi_product = dmi_get_system_info(DMI_PRODUCT_NAME); + dmi_sku = dmi_get_system_info(DMI_PRODUCT_SKU); +@@ -1653,17 +2159,12 @@ struct rt1320_dspfwheader { + RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00); + rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00); + +- regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); +- fw_ready &= 0x1; +- if (fw_ready) { ++ if (rt1320_dspfw_status(rt1320)) { + dev_dbg(dev, "%s, DSP FW was already\n", __func__); + rt1320->fw_load_done = true; + goto _exit_; + } + +- /* change to IRAM */ +- regmap_update_bits(rt1320->regmap, 0xf01e, 0x80, 0x00); +- + request_firmware(&fw, filename, dev); + if (fw) { + fwheader = (struct rt1320_dspfwheader *)fw->data; +@@ -1672,7 +2173,6 @@ struct rt1320_dspfwheader { + + if (fwheader->sync != 0x0a1c5679) { + dev_err(dev, "%s: FW sync error\n", __func__); +- release_firmware(fw); + goto _exit_; + } + +@@ -1709,9 +2209,11 @@ struct rt1320_dspfwheader { + dev_fw_match = true; + break; + case RT1321_DEV_ID: +- if (ptr_img->addr == 0x3fc00000) +- if (fw_data[9] == '1') ++ if (ptr_img->addr == 0x3fc00000) { ++ if (fw_data[7] == '1') + dev_fw_match = true; ++ has_0x3fc00000 = 1; ++ } + break; + default: + dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); +@@ -1721,6 +2223,22 @@ struct rt1320_dspfwheader { + fw_offset += ptr_img->size; + } + ++ if (rt1320->dev_id == RT1321_DEV_ID && rt1320->version_id == RT1321_VA2) { ++ /* ++ * For 1321 VA2, if the FW doesn't include the section for address 0x3fc00000, ++ * it means the FW will boot from ROM and force dev_fw_match to true to download FW by BRA. ++ */ ++ if (!has_0x3fc00000) { ++ boot_mode = 1; ++ dev_fw_match = true; ++ } ++ dev_dbg(dev, "%s: Boot from %s for VA2\n", __func__, (boot_mode ? "ROM" : "RAM")); ++ } ++ ++ /* change to IRAM */ ++ if (!boot_mode) ++ regmap_update_bits(rt1320->regmap, 0xf01e, 0x80, 0x00); ++ + if (dev_fw_match) { + dev_dbg(dev, "%s, starting BRA downloading FW..\n", __func__); + rt1320->bra_msg.dev_num = rt1320->sdw_slave->dev_num; +@@ -1732,7 +2250,6 @@ struct rt1320_dspfwheader { + } + + regcache_cache_bypass(rt1320->regmap, false); +- release_firmware(fw); + + if (!dev_fw_match) { + dev_err(dev, "%s: FW file doesn't match to device\n", __func__); +@@ -1743,24 +2260,39 @@ struct rt1320_dspfwheader { + goto _exit_; + } + +- /* run RAM code */ +- regmap_read(rt1320->regmap, 0x3fc2bfc0, &val); +- val |= 0x8; +- regmap_write(rt1320->regmap, 0x3fc2bfc0, val); +- +- /* clear frame counter */ + switch (rt1320->dev_id) { + case RT1320_DEV_ID: ++ /* run RAM code */ ++ regmap_read(rt1320->regmap, 0x3fc2bfc0, &val); ++ val |= 0x8; ++ regmap_write(rt1320->regmap, 0x3fc2bfc0, val); ++ /* clear frame counter */ + regmap_write(rt1320->regmap, 0x3fc2bfcb, 0x00); + regmap_write(rt1320->regmap, 0x3fc2bfca, 0x00); + regmap_write(rt1320->regmap, 0x3fc2bfc9, 0x00); + regmap_write(rt1320->regmap, 0x3fc2bfc8, 0x00); + break; + case RT1321_DEV_ID: ++ if (!boot_mode) { ++ /* run RAM code */ ++ regmap_read(rt1320->regmap, 0x3fc2dfc0, &val); ++ val |= 0x8; ++ regmap_write(rt1320->regmap, 0x3fc2dfc0, val); ++ } ++ /* clear frame counter */ + regmap_write(rt1320->regmap, 0x3fc2dfcb, 0x00); + regmap_write(rt1320->regmap, 0x3fc2dfca, 0x00); + regmap_write(rt1320->regmap, 0x3fc2dfc9, 0x00); + regmap_write(rt1320->regmap, 0x3fc2dfc8, 0x00); ++ /* enable handshake */ ++ regmap_write(rt1320->regmap, 0x3fc2dfc4, 0x00); ++ regmap_write(rt1320->regmap, 0xd470, 0xad); ++ /* minimum phase settings */ ++ regmap_write(rt1320->regmap, 0xc5c4, 0x10); ++ regmap_write(rt1320->regmap, 0x20003003, 0x31); ++ regmap_update_bits(rt1320->regmap, 0x20003002, 0x40, 0x00); ++ regmap_write(rt1320->regmap, 0xc5b3, 0x01); ++ regmap_write(rt1320->regmap, 0xc052, 0x11); + break; + } + +@@ -1843,14 +2375,35 @@ static void rt1320_vc_preset(struct rt1320_sdw_priv *rt1320) + + static void rt1321_preset(struct rt1320_sdw_priv *rt1320) + { ++ const struct reg_sequence *blindwrite; + unsigned int i, reg, val, delay; ++ unsigned int array_size; + +- for (i = 0; i < ARRAY_SIZE(rt1321_blind_write); i++) { +- reg = rt1321_blind_write[i].reg; +- val = rt1321_blind_write[i].def; +- delay = rt1321_blind_write[i].delay_us; ++ switch (rt1320->version_id) { ++ case RT1321_VA0: ++ blindwrite = rt1321_blind_write; ++ array_size = ARRAY_SIZE(rt1321_blind_write); ++ break; ++ case RT1321_VA1: ++ blindwrite = rt1321_va1_blind_write; ++ array_size = ARRAY_SIZE(rt1321_va1_blind_write); ++ break; ++ case RT1321_VA2: ++ blindwrite = rt1321_va2_blind_write; ++ array_size = ARRAY_SIZE(rt1321_va2_blind_write); ++ break; ++ default: ++ dev_err(&rt1320->sdw_slave->dev, "%s: Unknown version ID %d\n", ++ __func__, rt1320->version_id); ++ return; ++ } + +- if (reg == 0x3fc2dfc3) ++ for (i = 0; i < array_size; i++) { ++ reg = blindwrite[i].reg; ++ val = blindwrite[i].def; ++ delay = blindwrite[i].delay_us; ++ ++ if (reg == 0x1000cd56) + rt1320_load_mcu_patch(rt1320); + + regmap_write(rt1320->regmap, reg, val); +@@ -1892,6 +2445,24 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave) + regmap_read(rt1320->regmap, RT1320_DEV_ID_0, &val); + regmap_read(rt1320->regmap, RT1320_DEV_ID_1, &tmp); + rt1320->dev_id = (val << 8) | tmp; ++ ++ /* This is a workaround that reads the value twice to obtain the correct result. */ ++ rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_0, &val); ++ rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_1, &tmp); ++ rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_0, &val); ++ rt1320_pr_read(rt1320, RT1320_HV_DEV_ID_1, &tmp); ++ val = (val << 8) | tmp; ++ ++ if (rt1320->dev_id == RT1321_DEV_ID) { ++ if (rt1320->version_id == 0x01) ++ rt1320->version_id = RT1321_VA2; ++ else if (val == RT1321_DEV_HV_VA0_ID) ++ rt1320->version_id = RT1321_VA0; ++ else if (val == RT1321_DEV_HV_VA1_ID) ++ rt1320->version_id = RT1321_VA1; ++ else ++ dev_err(dev, "%s: Unknown version ID 0x%x for RT1321\n", __func__, rt1320->version_id); ++ } + } + + regmap_read(rt1320->regmap, +@@ -2326,25 +2897,12 @@ static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); + static int rt1320_r0_load(struct rt1320_sdw_priv *rt1320) + { + struct device *dev = regmap_get_device(rt1320->regmap); +- unsigned int fw_status_addr; +- unsigned int fw_ready; ++ int dspfw_status; + int ret = 0; + + if (!rt1320->r0_l_reg || !rt1320->r0_r_reg) + return -EINVAL; + +- switch (rt1320->dev_id) { +- case RT1320_DEV_ID: +- fw_status_addr = RT1320_DSPFW_STATUS_ADDR; +- break; +- case RT1321_DEV_ID: +- fw_status_addr = RT1321_DSPFW_STATUS_ADDR; +- break; +- default: +- dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); +- return -EINVAL; +- } +- + regmap_write(rt1320->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00); + ret = rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, 0x00); +@@ -2353,9 +2911,8 @@ static int rt1320_r0_load(struct rt1320_sdw_priv *rt1320) + goto _timeout_; + } + +- regmap_read(rt1320->regmap, fw_status_addr, &fw_ready); +- fw_ready &= 0x1; +- if (!fw_ready) { ++ dspfw_status = rt1320_dspfw_status(rt1320); ++ if (dspfw_status <= 0) { + dev_dbg(dev, "%s, DSP FW is NOT ready\n", __func__); + goto _timeout_; + } +@@ -2458,7 +3015,7 @@ static int rt1320_dspfw_load_put(struct snd_kcontrol *kcontrol, + if (!rt1320->hw_init) + return 0; + +- ret = pm_runtime_resume(component->dev); ++ ret = pm_runtime_resume_and_get(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + +@@ -2469,6 +3026,8 @@ static int rt1320_dspfw_load_put(struct snd_kcontrol *kcontrol, + if (!ucontrol->value.integer.value[0]) + rt1320->fw_load_done = false; + ++ pm_runtime_mark_last_busy(component->dev); ++ pm_runtime_put_autosuspend(component->dev); + return 0; + } + +@@ -2819,6 +3378,37 @@ static int rt1320_sdw_pcm_hw_free(struct snd_pcm_substream *substream, + return 0; + } + ++static int rt1320_bus_config(struct sdw_slave *slave, ++ struct sdw_bus_params *params) ++{ ++ struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(&slave->dev); ++ unsigned int clk_base; ++ ++ regmap_read(rt1320->regmap, 0x004d, &clk_base); ++ dev_dbg(&rt1320->sdw_slave->dev, "%s clk_base=%x", __func__, clk_base); ++ ++ switch (clk_base) { ++ case RT1320_CLK_FREQ_19_2_MHZ: ++ case RT1320_CLK_FREQ_24MHZ: ++ regmap_write(rt1320->regmap, 0xc600, 0x04); ++ regmap_write(rt1320->regmap, 0xc601, 0x83); ++ regmap_write(rt1320->regmap, 0xc602, 0x1f); ++ regmap_write(rt1320->regmap, 0xc603, 0x40); ++ break; ++ case RT1320_CLK_FREQ_24_576MHZ: ++ case RT1320_CLK_FREQ_22_5792MHZ: ++ regmap_write(rt1320->regmap, 0xc600, 0x07); ++ regmap_write(rt1320->regmap, 0xc601, 0x80); ++ regmap_write(rt1320->regmap, 0xc602, 0x0f); ++ regmap_write(rt1320->regmap, 0xc603, 0x40); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ + /* + * slave_ops: callbacks for get_clock_stop_mode, clock_stop and + * port_prep are not defined for now +@@ -2826,12 +3416,13 @@ static int rt1320_sdw_pcm_hw_free(struct snd_pcm_substream *substream, + static const struct sdw_slave_ops rt1320_slave_ops = { + .read_prop = rt1320_read_prop, + .update_status = rt1320_update_status, ++ .bus_config = rt1320_bus_config, + }; + + static int rt1320_sdw_component_probe(struct snd_soc_component *component) + { +- int ret; + struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); ++ int ret; + + rt1320->component = component; + +@@ -3062,10 +3653,23 @@ static int rt1320_dev_resume(struct device *dev) + return ret; + + regcache_cache_only(rt1320->regmap, false); +- regcache_sync(rt1320->regmap); ++ ret = regcache_sync(rt1320->regmap); ++ if (ret) ++ goto err_sync; ++ + regcache_cache_only(rt1320->mbq_regmap, false); +- regcache_sync(rt1320->mbq_regmap); ++ ret = regcache_sync(rt1320->mbq_regmap); ++ if (ret) ++ goto err_sync; ++ + return 0; ++ ++err_sync: ++ regcache_cache_only(rt1320->regmap, true); ++ regcache_cache_only(rt1320->mbq_regmap, true); ++ regcache_mark_dirty(rt1320->regmap); ++ regcache_mark_dirty(rt1320->mbq_regmap); ++ return ret; + } + + static const struct dev_pm_ops rt1320_pm = { +diff --git a/sound/soc/codecs/rt1320-sdw.h b/sound/soc/codecs/rt1320-sdw.h +--- a/sound/soc/codecs/rt1320-sdw.h ++++ b/sound/soc/codecs/rt1320-sdw.h +@@ -17,12 +17,17 @@ + + #define RT1320_DEV_ID 0x6981 + #define RT1321_DEV_ID 0x7045 ++#define RT1321_DEV_HV_VA0_ID 0x6997 ++#define RT1321_DEV_HV_VA1_ID 0x7071 + + /* imp-defined registers */ + #define RT1320_DEV_VERSION_ID_1 0xc404 + #define RT1320_DEV_ID_1 0xc405 + #define RT1320_DEV_ID_0 0xc406 + ++#define RT1320_HV_DEV_ID_0 0xf622 ++#define RT1320_HV_DEV_ID_1 0xf623 ++ + #define RT1320_POWER_STATE 0xc560 + + #define RT1321_PATCH_MAIN_VER 0x1000cffe +@@ -94,6 +99,12 @@ enum rt1320_version_id { + RT1320_VC, + }; + ++enum rt1321_version_id { ++ RT1321_VA0, ++ RT1321_VA1, ++ RT1321_VA2, ++}; ++ + #define RT1320_VER_B_ID 0x07392238 + #define RT1320_VAB_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vab.bin" + #define RT1320_VC_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vc.bin" +@@ -121,6 +132,7 @@ struct rt1320_datafixpoint { + int invrs; + }; + ++/* FW parameter id 1300 */ + typedef struct FwPara_HwSwGain { + unsigned int SwAdvGain; + unsigned int SwBasGain; +@@ -163,6 +175,13 @@ enum rt1320_rw_type { + RT1320_PARAM_READ = 3, + }; + ++enum { ++ RT1320_CLK_FREQ_19_2_MHZ = 1, ++ RT1320_CLK_FREQ_24MHZ = 2, ++ RT1320_CLK_FREQ_24_576MHZ = 3, ++ RT1320_CLK_FREQ_22_5792MHZ = 4, ++}; ++ + struct rt1320_sdw_priv { + struct snd_soc_component *component; + struct regmap *regmap; +diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c +--- a/sound/soc/codecs/rt5514-spi.c ++++ b/sound/soc/codecs/rt5514-spi.c +@@ -6,6 +6,7 @@ + * Author: Oder Chiou + */ + ++#include + #include + #include + #include +@@ -79,17 +80,17 @@ static void rt5514_spi_copy_work(struct work_struct *work) + unsigned int cur_wp, remain_data; + u8 buf[8]; + +- mutex_lock(&rt5514_dsp->dma_lock); ++ guard(mutex)(&rt5514_dsp->dma_lock); + if (!rt5514_dsp->substream) { + dev_err(rt5514_dsp->dev, "No pcm substream\n"); +- goto done; ++ return; + } + + runtime = rt5514_dsp->substream->runtime; + period_bytes = snd_pcm_lib_period_bytes(rt5514_dsp->substream); + if (!period_bytes) { + schedule_delayed_work(&rt5514_dsp->copy_work, 5); +- goto done; ++ return; + } + + if (rt5514_dsp->buf_size % period_bytes) +@@ -111,7 +112,7 @@ static void rt5514_spi_copy_work(struct work_struct *work) + + if (remain_data < period_bytes) { + schedule_delayed_work(&rt5514_dsp->copy_work, 5); +- goto done; ++ return; + } + } + +@@ -146,9 +147,6 @@ static void rt5514_spi_copy_work(struct work_struct *work) + snd_pcm_period_elapsed(rt5514_dsp->substream); + + schedule_delayed_work(&rt5514_dsp->copy_work, 5); +- +-done: +- mutex_unlock(&rt5514_dsp->dma_lock); + } + + static void rt5514_schedule_copy(struct rt5514_dsp *rt5514_dsp) +@@ -216,7 +214,7 @@ static int rt5514_spi_hw_params(struct snd_soc_component *component, + snd_soc_component_get_drvdata(component); + u8 buf[8]; + +- mutex_lock(&rt5514_dsp->dma_lock); ++ guard(mutex)(&rt5514_dsp->dma_lock); + rt5514_dsp->substream = substream; + rt5514_dsp->dma_offset = 0; + +@@ -225,8 +223,6 @@ static int rt5514_spi_hw_params(struct snd_soc_component *component, + if (buf[0] & RT5514_IRQ_STATUS_BIT) + rt5514_schedule_copy(rt5514_dsp); + +- mutex_unlock(&rt5514_dsp->dma_lock); +- + return 0; + } + +@@ -236,9 +232,8 @@ static int rt5514_spi_hw_free(struct snd_soc_component *component, + struct rt5514_dsp *rt5514_dsp = + snd_soc_component_get_drvdata(component); + +- mutex_lock(&rt5514_dsp->dma_lock); +- rt5514_dsp->substream = NULL; +- mutex_unlock(&rt5514_dsp->dma_lock); ++ scoped_guard(mutex, &rt5514_dsp->dma_lock) ++ rt5514_dsp->substream = NULL; + + cancel_delayed_work_sync(&rt5514_dsp->copy_work); + +diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c +--- a/sound/soc/codecs/rt5514.c ++++ b/sound/soc/codecs/rt5514.c +@@ -1073,12 +1073,18 @@ static int rt5514_set_bias_level(struct snd_soc_component *component, + * settings to make sure recording properly. + */ + if (rt5514->dsp_enabled) { +- rt5514->dsp_enabled = 0; +- regmap_multi_reg_write(rt5514->i2c_regmap, +- rt5514_i2c_patch, +- ARRAY_SIZE(rt5514_i2c_patch)); ++ ret = regmap_multi_reg_write(rt5514->i2c_regmap, ++ rt5514_i2c_patch, ++ ARRAY_SIZE(rt5514_i2c_patch)); ++ if (ret) ++ return ret; ++ + regcache_mark_dirty(rt5514->regmap); +- regcache_sync(rt5514->regmap); ++ ret = regcache_sync(rt5514->regmap); ++ if (ret) ++ return ret; ++ ++ rt5514->dsp_enabled = 0; + } + } + break; +diff --git a/sound/soc/codecs/rt5575-spi.c b/sound/soc/codecs/rt5575-spi.c +--- a/sound/soc/codecs/rt5575-spi.c ++++ b/sound/soc/codecs/rt5575-spi.c +@@ -93,7 +93,6 @@ static void rt5575_spi_burst_write(struct spi_device *spi, u32 addr, const u8 *t + int rt5575_spi_fw_load(struct spi_device *spi) + { + struct device *dev = &spi->dev; +- const struct firmware *firmware; + int i, ret; + static const char * const fw_path[] = { + "realtek/rt5575/rt5575_fw1.bin", +@@ -104,6 +103,7 @@ int rt5575_spi_fw_load(struct spi_device *spi) + static const u32 fw_addr[] = { 0x5f400000, 0x5f600000, 0x5f7fe000, 0x5f7ff000 }; + + for (i = 0; i < ARRAY_SIZE(fw_addr); i++) { ++ const struct firmware *firmware __free(firmware) = NULL; + ret = request_firmware(&firmware, fw_path[i], dev); + if (ret) { + dev_err(dev, "Request firmware failure: %d\n", ret); +@@ -111,7 +111,6 @@ int rt5575_spi_fw_load(struct spi_device *spi) + } + + rt5575_spi_burst_write(spi, fw_addr[i], firmware->data, firmware->size); +- release_firmware(firmware); + } + + return 0; +diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c +--- a/sound/soc/codecs/rt5645.c ++++ b/sound/soc/codecs/rt5645.c +@@ -6,6 +6,7 @@ + * Author: Bard Liao + */ + ++#include + #include + #include + #include +@@ -3323,92 +3324,89 @@ static void rt5645_jack_detect_work(struct work_struct *work) + if (!rt5645->component) + return; + +- mutex_lock(&rt5645->jd_mutex); +- +- switch (rt5645->pdata.jd_mode) { +- case 0: /* Not using rt5645 JD */ +- if (rt5645->gpiod_hp_det) { +- gpio_state = gpiod_get_value(rt5645->gpiod_hp_det); +- if (rt5645->pdata.inv_hp_pol) +- gpio_state ^= 1; +- dev_dbg(rt5645->component->dev, "gpio_state = %d\n", +- gpio_state); +- report = rt5645_jack_detect(rt5645->component, gpio_state); +- } +- snd_soc_jack_report(rt5645->hp_jack, +- report, SND_JACK_HEADPHONE); +- snd_soc_jack_report(rt5645->mic_jack, +- report, SND_JACK_MICROPHONE); +- mutex_unlock(&rt5645->jd_mutex); +- return; +- case 4: +- val = snd_soc_component_read(rt5645->component, RT5645_A_JD_CTRL1) & 0x0020; +- break; +- default: /* read rt5645 jd1_1 status */ +- val = snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x1000; +- break; +- +- } +- +- if (!val && (rt5645->jack_type == 0)) { /* jack in */ +- report = rt5645_jack_detect(rt5645->component, 1); +- } else if (!val && rt5645->jack_type == SND_JACK_HEADSET) { +- /* for push button and jack out */ +- btn_type = 0; +- if (snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x4) { +- /* button pressed */ +- report = SND_JACK_HEADSET; +- btn_type = rt5645_button_detect(rt5645->component); +- /* rt5650 can report three kinds of button behavior, +- one click, double click and hold. However, +- currently we will report button pressed/released +- event. So all the three button behaviors are +- treated as button pressed. */ +- switch (btn_type) { +- case 0x8000: +- case 0x4000: +- case 0x2000: +- report |= SND_JACK_BTN_0; +- break; +- case 0x1000: +- case 0x0800: +- case 0x0400: +- report |= SND_JACK_BTN_1; +- break; +- case 0x0200: +- case 0x0100: +- case 0x0080: +- report |= SND_JACK_BTN_2; +- break; +- case 0x0040: +- case 0x0020: +- case 0x0010: +- report |= SND_JACK_BTN_3; +- break; +- case 0x0000: /* unpressed */ +- break; +- default: +- dev_err(rt5645->component->dev, +- "Unexpected button code 0x%04x\n", +- btn_type); +- break; ++ scoped_guard(mutex, &rt5645->jd_mutex) { ++ switch (rt5645->pdata.jd_mode) { ++ case 0: /* Not using rt5645 JD */ ++ if (rt5645->gpiod_hp_det) { ++ gpio_state = gpiod_get_value(rt5645->gpiod_hp_det); ++ if (rt5645->pdata.inv_hp_pol) ++ gpio_state ^= 1; ++ dev_dbg(rt5645->component->dev, "gpio_state = %d\n", ++ gpio_state); ++ report = rt5645_jack_detect(rt5645->component, gpio_state); + } ++ snd_soc_jack_report(rt5645->hp_jack, ++ report, SND_JACK_HEADPHONE); ++ snd_soc_jack_report(rt5645->mic_jack, ++ report, SND_JACK_MICROPHONE); ++ return; ++ case 4: ++ val = snd_soc_component_read(rt5645->component, RT5645_A_JD_CTRL1) & 0x0020; ++ break; ++ default: /* read rt5645 jd1_1 status */ ++ val = snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x1000; ++ break; + } +- if (btn_type == 0)/* button release */ +- report = rt5645->jack_type; +- else { +- mod_timer(&rt5645->btn_check_timer, +- msecs_to_jiffies(100)); +- } +- } else { +- /* jack out */ +- report = 0; +- snd_soc_component_update_bits(rt5645->component, +- RT5645_INT_IRQ_ST, 0x1, 0x0); +- rt5645_jack_detect(rt5645->component, 0); +- } + +- mutex_unlock(&rt5645->jd_mutex); ++ if (!val && rt5645->jack_type == 0) { /* jack in */ ++ report = rt5645_jack_detect(rt5645->component, 1); ++ } else if (!val && rt5645->jack_type == SND_JACK_HEADSET) { ++ /* for push button and jack out */ ++ btn_type = 0; ++ if (snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x4) { ++ /* button pressed */ ++ report = SND_JACK_HEADSET; ++ btn_type = rt5645_button_detect(rt5645->component); ++ /* ++ * rt5650 can report three kinds of button behavior, ++ * one click, double click and hold. However, ++ * currently we will report button pressed/released ++ * event. So all the three button behaviors are ++ * treated as button pressed. ++ */ ++ switch (btn_type) { ++ case 0x8000: ++ case 0x4000: ++ case 0x2000: ++ report |= SND_JACK_BTN_0; ++ break; ++ case 0x1000: ++ case 0x0800: ++ case 0x0400: ++ report |= SND_JACK_BTN_1; ++ break; ++ case 0x0200: ++ case 0x0100: ++ case 0x0080: ++ report |= SND_JACK_BTN_2; ++ break; ++ case 0x0040: ++ case 0x0020: ++ case 0x0010: ++ report |= SND_JACK_BTN_3; ++ break; ++ case 0x0000: /* unpressed */ ++ break; ++ default: ++ dev_err(rt5645->component->dev, ++ "Unexpected button code 0x%04x\n", ++ btn_type); ++ break; ++ } ++ } ++ if (btn_type == 0)/* button release */ ++ report = rt5645->jack_type; ++ else ++ mod_timer(&rt5645->btn_check_timer, ++ msecs_to_jiffies(100)); ++ } else { ++ /* jack out */ ++ report = 0; ++ snd_soc_component_update_bits(rt5645->component, ++ RT5645_INT_IRQ_ST, 0x1, 0x0); ++ rt5645_jack_detect(rt5645->component, 0); ++ } ++ } + + snd_soc_jack_report(rt5645->hp_jack, report, SND_JACK_HEADPHONE); + snd_soc_jack_report(rt5645->mic_jack, report, SND_JACK_MICROPHONE); +@@ -3522,9 +3520,15 @@ static int rt5645_suspend(struct snd_soc_component *component) + static int rt5645_resume(struct snd_soc_component *component) + { + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); ++ int ret; + + regcache_cache_only(rt5645->regmap, false); +- regcache_sync(rt5645->regmap); ++ ret = regcache_sync(rt5645->regmap); ++ if (ret) { ++ regcache_cache_only(rt5645->regmap, true); ++ regcache_mark_dirty(rt5645->regmap); ++ return ret; ++ } + + return 0; + } +@@ -4335,9 +4339,15 @@ static int rt5645_sys_suspend(struct device *dev) + static int rt5645_sys_resume(struct device *dev) + { + struct rt5645_priv *rt5645 = dev_get_drvdata(dev); ++ int ret; + + regcache_cache_only(rt5645->regmap, false); +- regcache_sync(rt5645->regmap); ++ ret = regcache_sync(rt5645->regmap); ++ if (ret) { ++ regcache_cache_only(rt5645->regmap, true); ++ regcache_mark_dirty(rt5645->regmap); ++ return ret; ++ } + + if (rt5645->hp_jack) { + rt5645->jack_type = 0; +diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c +--- a/sound/soc/codecs/rt5665.c ++++ b/sound/soc/codecs/rt5665.c +@@ -6,6 +6,7 @@ + * Author: Bard Liao + */ + ++#include + #include + #include + #include +@@ -1208,7 +1209,7 @@ static void rt5665_jack_detect_handler(struct work_struct *work) + usleep_range(10000, 15000); + } + +- mutex_lock(&rt5665->calibrate_mutex); ++ guard(mutex)(&rt5665->calibrate_mutex); + + val = snd_soc_component_read(rt5665->component, RT5665_AJD1_CTRL) & 0x0010; + if (!val) { +@@ -1274,8 +1275,6 @@ static void rt5665_jack_detect_handler(struct work_struct *work) + schedule_delayed_work(&rt5665->jd_check_work, 0); + else + cancel_delayed_work_sync(&rt5665->jd_check_work); +- +- mutex_unlock(&rt5665->calibrate_mutex); + } + + static const char * const rt5665_clk_sync[] = { +@@ -4564,7 +4563,7 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665) + { + int value, count; + +- mutex_lock(&rt5665->calibrate_mutex); ++ guard(mutex)(&rt5665->calibrate_mutex); + + regcache_cache_bypass(rt5665->regmap, true); + +@@ -4601,7 +4600,8 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665) + pr_err("HP Calibration Failure\n"); + regmap_write(rt5665->regmap, RT5665_RESET, 0); + regcache_cache_bypass(rt5665->regmap, false); +- goto out_unlock; ++ rt5665->calibration_done = true; ++ return; + } + + count++; +@@ -4620,7 +4620,8 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665) + pr_err("MONO Calibration Failure\n"); + regmap_write(rt5665->regmap, RT5665_RESET, 0); + regcache_cache_bypass(rt5665->regmap, false); +- goto out_unlock; ++ rt5665->calibration_done = true; ++ return; + } + + count++; +@@ -4635,9 +4636,7 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665) + regmap_write(rt5665->regmap, RT5665_BIAS_CUR_CTRL_8, 0xa602); + regmap_write(rt5665->regmap, RT5665_ASRC_8, 0x0120); + +-out_unlock: + rt5665->calibration_done = true; +- mutex_unlock(&rt5665->calibrate_mutex); + } + + static void rt5665_calibrate_handler(struct work_struct *work) +diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c +--- a/sound/soc/codecs/rt5668.c ++++ b/sound/soc/codecs/rt5668.c +@@ -6,6 +6,7 @@ + * Author: Bard Liao + */ + ++#include + #include + #include + #include +@@ -986,7 +987,7 @@ static void rt5668_jack_detect_handler(struct work_struct *work) + return; + } + +- mutex_lock(&rt5668->calibrate_mutex); ++ guard(mutex)(&rt5668->calibrate_mutex); + + val = snd_soc_component_read(rt5668->component, RT5668_AJD1_CTRL) + & RT5668_JDH_RS_MASK; +@@ -1053,8 +1054,6 @@ static void rt5668_jack_detect_handler(struct work_struct *work) + schedule_delayed_work(&rt5668->jd_check_work, 0); + else + cancel_delayed_work_sync(&rt5668->jd_check_work); +- +- mutex_unlock(&rt5668->calibrate_mutex); + } + + static const struct snd_kcontrol_new rt5668_snd_controls[] = { +@@ -2356,7 +2355,7 @@ static void rt5668_calibrate(struct rt5668_priv *rt5668) + { + int value, count; + +- mutex_lock(&rt5668->calibrate_mutex); ++ guard(mutex)(&rt5668->calibrate_mutex); + + rt5668_reset(rt5668->regmap); + regmap_write(rt5668->regmap, RT5668_PWR_ANLG_1, 0xa2bf); +@@ -2400,9 +2399,6 @@ static void rt5668_calibrate(struct rt5668_priv *rt5668) + /* restore settings */ + regmap_write(rt5668->regmap, RT5668_STO1_ADC_MIXER, 0xc0c4); + regmap_write(rt5668->regmap, RT5668_PWR_DIG_1, 0x0000); +- +- mutex_unlock(&rt5668->calibrate_mutex); +- + } + + static int rt5668_i2c_probe(struct i2c_client *i2c) +diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c +--- a/sound/soc/codecs/rt5677-spi.c ++++ b/sound/soc/codecs/rt5677-spi.c +@@ -6,6 +6,7 @@ + * Author: Oder Chiou + */ + ++#include + #include + #include + #include +@@ -133,9 +134,8 @@ static int rt5677_spi_hw_params( + struct rt5677_dsp *rt5677_dsp = + snd_soc_component_get_drvdata(component); + +- mutex_lock(&rt5677_dsp->dma_lock); ++ guard(mutex)(&rt5677_dsp->dma_lock); + rt5677_dsp->substream = substream; +- mutex_unlock(&rt5677_dsp->dma_lock); + + return 0; + } +@@ -147,9 +147,8 @@ static int rt5677_spi_hw_free( + struct rt5677_dsp *rt5677_dsp = + snd_soc_component_get_drvdata(component); + +- mutex_lock(&rt5677_dsp->dma_lock); ++ guard(mutex)(&rt5677_dsp->dma_lock); + rt5677_dsp->substream = NULL; +- mutex_unlock(&rt5677_dsp->dma_lock); + + return 0; + } +@@ -311,17 +310,17 @@ static void rt5677_spi_copy_work(struct work_struct *work) + int ret = 0; + + /* Ensure runtime->dma_area buffer does not go away while copying. */ +- mutex_lock(&rt5677_dsp->dma_lock); ++ guard(mutex)(&rt5677_dsp->dma_lock); + if (!rt5677_dsp->substream) { + dev_err(rt5677_dsp->dev, "No pcm substream\n"); +- goto done; ++ return; + } + + runtime = rt5677_dsp->substream->runtime; + + if (rt5677_spi_mic_write_offset(&mic_write_offset)) { + dev_err(rt5677_dsp->dev, "No mic_write_offset\n"); +- goto done; ++ return; + } + + /* If this is the first time that we've asked for streaming data after +@@ -355,7 +354,7 @@ static void rt5677_spi_copy_work(struct work_struct *work) + ret = rt5677_spi_copy(rt5677_dsp, copy_bytes); + if (ret) { + dev_err(rt5677_dsp->dev, "Copy failed %d\n", ret); +- goto done; ++ return; + } + rt5677_dsp->avail_bytes += copy_bytes; + if (rt5677_dsp->avail_bytes >= period_bytes) { +@@ -367,8 +366,6 @@ static void rt5677_spi_copy_work(struct work_struct *work) + + delay = bytes_to_frames(runtime, period_bytes) / runtime->rate; + schedule_delayed_work(&rt5677_dsp->copy_work, secs_to_jiffies(delay)); +-done: +- mutex_unlock(&rt5677_dsp->dma_lock); + } + + static int rt5677_spi_pcm_new(struct snd_soc_component *component, +@@ -507,10 +504,8 @@ int rt5677_spi_read(u32 addr, void *rxbuf, size_t len) + header[3] = ((addr + offset) & 0x0000ff00) >> 8; + header[4] = ((addr + offset) & 0x000000ff) >> 0; + +- mutex_lock(&spi_mutex); +- status |= spi_sync(g_spi, &m); +- mutex_unlock(&spi_mutex); +- ++ scoped_guard(mutex, &spi_mutex) ++ status |= spi_sync(g_spi, &m); + + /* Copy data back to caller buffer */ + rt5677_spi_reverse(cb + offset, len - offset, body, t[1].len); +@@ -564,9 +559,8 @@ int rt5677_spi_write(u32 addr, const void *txbuf, size_t len) + offset += t.len; + t.len += RT5677_SPI_HEADER + 1; + +- mutex_lock(&spi_mutex); +- status |= spi_sync(g_spi, &m); +- mutex_unlock(&spi_mutex); ++ scoped_guard(mutex, &spi_mutex) ++ status |= spi_sync(g_spi, &m); + } + return status; + } +@@ -591,10 +585,10 @@ void rt5677_spi_hotword_detected(void) + return; + } + +- mutex_lock(&rt5677_dsp->dma_lock); +- dev_info(rt5677_dsp->dev, "Hotword detected\n"); +- rt5677_dsp->new_hotword = true; +- mutex_unlock(&rt5677_dsp->dma_lock); ++ scoped_guard(mutex, &rt5677_dsp->dma_lock) { ++ dev_info(rt5677_dsp->dev, "Hotword detected\n"); ++ rt5677_dsp->new_hotword = true; ++ } + + schedule_delayed_work(&rt5677_dsp->copy_work, 0); + } +diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c +--- a/sound/soc/codecs/rt5677.c ++++ b/sound/soc/codecs/rt5677.c +@@ -6,6 +6,8 @@ + * Author: Oder Chiou + */ + ++#include ++#include + #include + #include + #include +@@ -563,46 +565,43 @@ static int rt5677_dsp_mode_i2c_write_addr(struct rt5677_priv *rt5677, + struct snd_soc_component *component = rt5677->component; + int ret; + +- mutex_lock(&rt5677->dsp_cmd_lock); ++ guard(mutex)(&rt5677->dsp_cmd_lock); + + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB, + addr >> 16); + if (ret < 0) { + dev_err(component->dev, "Failed to set addr msb value: %d\n", ret); +- goto err; ++ return ret; + } + + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB, + addr & 0xffff); + if (ret < 0) { + dev_err(component->dev, "Failed to set addr lsb value: %d\n", ret); +- goto err; ++ return ret; + } + + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_MSB, + value >> 16); + if (ret < 0) { + dev_err(component->dev, "Failed to set data msb value: %d\n", ret); +- goto err; ++ return ret; + } + + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_LSB, + value & 0xffff); + if (ret < 0) { + dev_err(component->dev, "Failed to set data lsb value: %d\n", ret); +- goto err; ++ return ret; + } + + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE, + opcode); + if (ret < 0) { + dev_err(component->dev, "Failed to set op code value: %d\n", ret); +- goto err; ++ return ret; + } + +-err: +- mutex_unlock(&rt5677->dsp_cmd_lock); +- + return ret; + } + +@@ -622,36 +621,33 @@ static int rt5677_dsp_mode_i2c_read_addr( + int ret; + unsigned int msb, lsb; + +- mutex_lock(&rt5677->dsp_cmd_lock); ++ guard(mutex)(&rt5677->dsp_cmd_lock); + + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB, + addr >> 16); + if (ret < 0) { + dev_err(component->dev, "Failed to set addr msb value: %d\n", ret); +- goto err; ++ return ret; + } + + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB, + addr & 0xffff); + if (ret < 0) { + dev_err(component->dev, "Failed to set addr lsb value: %d\n", ret); +- goto err; ++ return ret; + } + + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE, + 0x0002); + if (ret < 0) { + dev_err(component->dev, "Failed to set op code value: %d\n", ret); +- goto err; ++ return ret; + } + + regmap_read(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_MSB, &msb); + regmap_read(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_LSB, &lsb); + *value = (msb << 16) | lsb; + +-err: +- mutex_unlock(&rt5677->dsp_cmd_lock); +- + return ret; + } + +@@ -853,11 +849,11 @@ static int rt5677_parse_and_load_dsp(struct rt5677_priv *rt5677, const u8 *buf, + + static int rt5677_load_dsp_from_file(struct rt5677_priv *rt5677) + { +- const struct firmware *fwp; + struct device *dev = rt5677->component->dev; +- int ret = 0; ++ int ret; + + /* Load dsp firmware from rt5677_elf_vad file */ ++ const struct firmware *fwp __free(firmware) = NULL; + ret = request_firmware(&fwp, "rt5677_elf_vad", dev); + if (ret) { + dev_err(dev, "Request rt5677_elf_vad failed %d\n", ret); +@@ -865,9 +861,7 @@ static int rt5677_load_dsp_from_file(struct rt5677_priv *rt5677) + } + dev_info(dev, "Requested rt5677_elf_vad (%zu)\n", fwp->size); + +- ret = rt5677_parse_and_load_dsp(rt5677, fwp->data, fwp->size); +- release_firmware(fwp); +- return ret; ++ return rt5677_parse_and_load_dsp(rt5677, fwp->data, fwp->size); + } + + static int rt5677_set_dsp_vad(struct snd_soc_component *component, bool on) +@@ -940,21 +934,20 @@ static void rt5677_dsp_work(struct work_struct *work) + activity = false; + + /* Don't turn off the DSP while handling irqs */ +- mutex_lock(&rt5677->irq_lock); +- /* Set DSP CPU to Stop */ +- regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, +- RT5677_PWR_DSP_CPU, RT5677_PWR_DSP_CPU); ++ scoped_guard(mutex, &rt5677->irq_lock) { ++ /* Set DSP CPU to Stop */ ++ regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, ++ RT5677_PWR_DSP_CPU, RT5677_PWR_DSP_CPU); + +- rt5677_set_dsp_mode(rt5677, false); ++ rt5677_set_dsp_mode(rt5677, false); + +- /* Disable and clear VAD interrupt */ +- regmap_write(rt5677->regmap, RT5677_VAD_CTRL1, 0x2184); ++ /* Disable and clear VAD interrupt */ ++ regmap_write(rt5677->regmap, RT5677_VAD_CTRL1, 0x2184); + +- /* Set GPIO1 pin back to be IRQ output for jack detect */ +- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, +- RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ); +- +- mutex_unlock(&rt5677->irq_lock); ++ /* Set GPIO1 pin back to be IRQ output for jack detect */ ++ regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, ++ RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ); ++ } + } + } + +@@ -4767,6 +4760,21 @@ static int rt5677_gpio_direction_in(struct gpio_chip *chip, unsigned offset) + return rt5677_update_gpio_bits(rt5677, offset, m, v); + } + ++static int rt5677_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) ++{ ++ struct rt5677_priv *rt5677 = gpiochip_get_data(chip); ++ unsigned int shift = RT5677_GPIOx_DIR_SFT + (offset % 5) * 3; ++ unsigned int bank = offset / 5; ++ unsigned int reg = bank ? RT5677_GPIO_CTRL3 : RT5677_GPIO_CTRL2; ++ int ret; ++ ++ ret = regmap_test_bits(rt5677->regmap, reg, BIT(shift)); ++ if (ret < 0) ++ return ret; ++ ++ return ret ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; ++} ++ + /* + * Configures the GPIO as + * 0 - floating +@@ -4834,6 +4842,7 @@ static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset) + static const struct gpio_chip rt5677_template_chip = { + .label = RT5677_DRV_NAME, + .owner = THIS_MODULE, ++ .get_direction = rt5677_gpio_get_direction, + .direction_output = rt5677_gpio_direction_out, + .set = rt5677_gpio_set, + .direction_input = rt5677_gpio_direction_in, +@@ -4980,11 +4989,11 @@ static int rt5677_read(void *context, unsigned int reg, unsigned int *val) + + if (rt5677->is_dsp_mode) { + if (reg > 0xff) { +- mutex_lock(&rt5677->dsp_pri_lock); +- rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, +- reg & 0xff); +- rt5677_dsp_mode_i2c_read(rt5677, RT5677_PRIV_DATA, val); +- mutex_unlock(&rt5677->dsp_pri_lock); ++ scoped_guard(mutex, &rt5677->dsp_pri_lock) { ++ rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, ++ reg & 0xff); ++ rt5677_dsp_mode_i2c_read(rt5677, RT5677_PRIV_DATA, val); ++ } + } else { + rt5677_dsp_mode_i2c_read(rt5677, reg, val); + } +@@ -5002,12 +5011,12 @@ static int rt5677_write(void *context, unsigned int reg, unsigned int val) + + if (rt5677->is_dsp_mode) { + if (reg > 0xff) { +- mutex_lock(&rt5677->dsp_pri_lock); +- rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, +- reg & 0xff); +- rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_DATA, +- val); +- mutex_unlock(&rt5677->dsp_pri_lock); ++ scoped_guard(mutex, &rt5677->dsp_pri_lock) { ++ rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, ++ reg & 0xff); ++ rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_DATA, ++ val); ++ } + } else { + rt5677_dsp_mode_i2c_write(rt5677, reg, val); + } +@@ -5399,7 +5408,7 @@ static void rt5677_resume_irq_check(struct work_struct *work) + * Without this explicit check, unplug the headset right after suspend + * starts, then after resume the headset is still shown as plugged in. + */ +- mutex_lock(&rt5677->irq_lock); ++ guard(mutex)(&rt5677->irq_lock); + for (i = 0; i < RT5677_IRQ_NUM; i++) { + if (rt5677->irq_en & rt5677_irq_descs[i].enable_mask) { + virq = irq_find_mapping(rt5677->domain, i); +@@ -5407,7 +5416,6 @@ static void rt5677_resume_irq_check(struct work_struct *work) + handle_nested_irq(virq); + } + } +- mutex_unlock(&rt5677->irq_lock); + } + + static void rt5677_irq_bus_lock(struct irq_data *data) +diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c +--- a/sound/soc/codecs/rt5682-sdw.c ++++ b/sound/soc/codecs/rt5682-sdw.c +@@ -6,6 +6,7 @@ + // Author: Oder Chiou + // + ++#include + #include + #include + #include +@@ -410,7 +411,9 @@ static int rt5682_io_init(struct device *dev, struct sdw_slave *slave) + if (rt5682->first_hw_init) { + regcache_cache_bypass(rt5682->regmap, false); + regcache_mark_dirty(rt5682->regmap); +- regcache_sync(rt5682->regmap); ++ ret = regcache_sync(rt5682->regmap); ++ if (ret) ++ goto err_sync; + + /* volatile registers */ + regmap_update_bits(rt5682->regmap, RT5682_CBJ_CTRL_2, +@@ -472,8 +475,16 @@ static int rt5682_io_init(struct device *dev, struct sdw_slave *slave) + /* Mark Slave initialization complete */ + rt5682->hw_init = true; + rt5682->first_hw_init = true; ++ goto out; ++ ++err_sync: ++ regcache_cache_bypass(rt5682->regmap, false); ++ regcache_cache_only(rt5682->sdw_regmap, true); ++ regcache_cache_only(rt5682->regmap, true); ++ regcache_mark_dirty(rt5682->regmap); + + err_nodev: ++out: + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete: %d\n", __func__, ret); +@@ -660,12 +671,11 @@ static int rt5682_interrupt_callback(struct sdw_slave *slave, + dev_dbg(&slave->dev, + "%s control_port_stat=%x", __func__, status->control_port); + +- mutex_lock(&rt5682->disable_irq_lock); ++ guard(mutex)(&rt5682->disable_irq_lock); + if (status->control_port & 0x4 && !rt5682->disable_irq) { + mod_delayed_work(system_power_efficient_wq, + &rt5682->jack_detect_work, msecs_to_jiffies(rt5682->irq_work_delay_time)); + } +- mutex_unlock(&rt5682->disable_irq_lock); + + return 0; + } +@@ -736,11 +746,11 @@ static int rt5682_dev_system_suspend(struct device *dev) + * deferred work completes and before the parent disables + * interrupts on the link + */ +- mutex_lock(&rt5682->disable_irq_lock); +- rt5682->disable_irq = true; +- ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, +- SDW_SCP_INT1_IMPL_DEF, 0); +- mutex_unlock(&rt5682->disable_irq_lock); ++ scoped_guard(mutex, &rt5682->disable_irq_lock) { ++ rt5682->disable_irq = true; ++ ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, ++ SDW_SCP_INT1_IMPL_DEF, 0); ++ } + + if (ret < 0) { + /* log but don't prevent suspend from happening */ +@@ -760,12 +770,12 @@ static int rt5682_dev_resume(struct device *dev) + return 0; + + if (!slave->unattach_request) { +- mutex_lock(&rt5682->disable_irq_lock); +- if (rt5682->disable_irq == true) { +- sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); +- rt5682->disable_irq = false; ++ scoped_guard(mutex, &rt5682->disable_irq_lock) { ++ if (rt5682->disable_irq) { ++ sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); ++ rt5682->disable_irq = false; ++ } + } +- mutex_unlock(&rt5682->disable_irq_lock); + } + + ret = sdw_slave_wait_for_init(slave, RT5682_PROBE_TIMEOUT); +@@ -776,7 +786,13 @@ static int rt5682_dev_resume(struct device *dev) + + regcache_cache_only(rt5682->sdw_regmap, false); + regcache_cache_only(rt5682->regmap, false); +- regcache_sync(rt5682->regmap); ++ ret = regcache_sync(rt5682->regmap); ++ if (ret) { ++ regcache_cache_only(rt5682->sdw_regmap, true); ++ regcache_cache_only(rt5682->regmap, true); ++ regcache_mark_dirty(rt5682->regmap); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c +--- a/sound/soc/codecs/rt5682.c ++++ b/sound/soc/codecs/rt5682.c +@@ -6,6 +6,7 @@ + // Author: Bard Liao + // + ++#include + #include + #include + #include +@@ -3125,7 +3126,7 @@ void rt5682_calibrate(struct rt5682_priv *rt5682) + { + int value, count; + +- mutex_lock(&rt5682->calibrate_mutex); ++ guard(mutex)(&rt5682->calibrate_mutex); + + rt5682_reset(rt5682); + regmap_write(rt5682->regmap, RT5682_I2C_CTRL, 0x000f); +@@ -3175,8 +3176,6 @@ void rt5682_calibrate(struct rt5682_priv *rt5682) + regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x2005); + regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0xc0c4); + regmap_write(rt5682->regmap, RT5682_CAL_REC, 0x0c0c); +- +- mutex_unlock(&rt5682->calibrate_mutex); + } + EXPORT_SYMBOL_GPL(rt5682_calibrate); + +diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c +--- a/sound/soc/codecs/rt5682s.c ++++ b/sound/soc/codecs/rt5682s.c +@@ -6,6 +6,7 @@ + // Author: Derek Fang + // + ++#include + #include + #include + #include +@@ -648,7 +649,7 @@ static void rt5682s_sar_power_mode(struct snd_soc_component *component, int mode + { + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + +- mutex_lock(&rt5682s->sar_mutex); ++ guard(mutex)(&rt5682s->sar_mutex); + + switch (mode) { + case SAR_PWR_SAVING: +@@ -695,8 +696,6 @@ static void rt5682s_sar_power_mode(struct snd_soc_component *component, int mode + dev_err(component->dev, "Invalid SAR Power mode: %d\n", mode); + break; + } +- +- mutex_unlock(&rt5682s->sar_mutex); + } + + static void rt5682s_enable_push_button_irq(struct snd_soc_component *component) +@@ -2534,7 +2533,7 @@ static int rt5682s_wclk_prepare(struct clk_hw *hw) + if (!rt5682s_clk_check(rt5682s)) + return -EINVAL; + +- mutex_lock(&rt5682s->wclk_mutex); ++ guard(mutex)(&rt5682s->wclk_mutex); + + snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1, + RT5682S_PWR_VREF2 | RT5682S_PWR_FV2 | RT5682S_PWR_MB, +@@ -2556,8 +2555,6 @@ static int rt5682s_wclk_prepare(struct clk_hw *hw) + + rt5682s->wclk_enabled = 1; + +- mutex_unlock(&rt5682s->wclk_mutex); +- + return 0; + } + +@@ -2570,7 +2567,7 @@ static void rt5682s_wclk_unprepare(struct clk_hw *hw) + if (!rt5682s_clk_check(rt5682s)) + return; + +- mutex_lock(&rt5682s->wclk_mutex); ++ guard(mutex)(&rt5682s->wclk_mutex); + + if (!rt5682s->jack_type) + snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1, +@@ -2585,8 +2582,6 @@ static void rt5682s_wclk_unprepare(struct clk_hw *hw) + rt5682s_set_pllb_power(rt5682s, 0); + + rt5682s->wclk_enabled = 0; +- +- mutex_unlock(&rt5682s->wclk_mutex); + } + + static unsigned long rt5682s_wclk_recalc_rate(struct clk_hw *hw, +@@ -2997,7 +2992,7 @@ static void rt5682s_calibrate(struct rt5682s_priv *rt5682s) + { + unsigned int count, value; + +- mutex_lock(&rt5682s->calibrate_mutex); ++ guard(mutex)(&rt5682s->calibrate_mutex); + + regmap_write(rt5682s->regmap, RT5682S_PWR_ANLG_1, 0xaa80); + usleep_range(15000, 20000); +@@ -3034,8 +3029,6 @@ static void rt5682s_calibrate(struct rt5682s_priv *rt5682s) + regmap_write(rt5682s->regmap, RT5682S_PWR_DIG_1, 0x00c0); + regmap_write(rt5682s->regmap, RT5682S_PWR_ANLG_1, 0x0800); + regmap_write(rt5682s->regmap, RT5682S_GLB_CLK, 0x0000); +- +- mutex_unlock(&rt5682s->calibrate_mutex); + } + + static const struct regmap_config rt5682s_regmap = { +diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c +--- a/sound/soc/codecs/rt700-sdw.c ++++ b/sound/soc/codecs/rt700-sdw.c +@@ -6,6 +6,7 @@ + // + // + ++#include + #include + #include + #include +@@ -415,12 +416,11 @@ static int rt700_interrupt_callback(struct sdw_slave *slave, + dev_dbg(&slave->dev, + "%s control_port_stat=%x", __func__, status->control_port); + +- mutex_lock(&rt700->disable_irq_lock); ++ guard(mutex)(&rt700->disable_irq_lock); + if (status->control_port & 0x4 && !rt700->disable_irq) { + mod_delayed_work(system_power_efficient_wq, + &rt700->jack_detect_work, msecs_to_jiffies(250)); + } +- mutex_unlock(&rt700->disable_irq_lock); + + return 0; + } +@@ -499,11 +499,11 @@ static int rt700_dev_system_suspend(struct device *dev) + * deferred work completes and before the parent disables + * interrupts on the link + */ +- mutex_lock(&rt700->disable_irq_lock); +- rt700->disable_irq = true; +- ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, +- SDW_SCP_INT1_IMPL_DEF, 0); +- mutex_unlock(&rt700->disable_irq_lock); ++ scoped_guard(mutex, &rt700->disable_irq_lock) { ++ rt700->disable_irq = true; ++ ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, ++ SDW_SCP_INT1_IMPL_DEF, 0); ++ } + + if (ret < 0) { + /* log but don't prevent suspend from happening */ +diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c +--- a/sound/soc/codecs/rt711-sdca-sdw.c ++++ b/sound/soc/codecs/rt711-sdca-sdw.c +@@ -6,6 +6,7 @@ + // + // + ++#include + #include + #include + #include +@@ -415,13 +416,13 @@ static int rt711_sdca_dev_system_suspend(struct device *dev) + * deferred work completes and before the parent disables + * interrupts on the link + */ +- mutex_lock(&rt711_sdca->disable_irq_lock); +- rt711_sdca->disable_irq = true; +- ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, +- SDW_SCP_SDCA_INTMASK_SDCA_0, 0); +- ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, +- SDW_SCP_SDCA_INTMASK_SDCA_8, 0); +- mutex_unlock(&rt711_sdca->disable_irq_lock); ++ scoped_guard(mutex, &rt711_sdca->disable_irq_lock) { ++ rt711_sdca->disable_irq = true; ++ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, ++ SDW_SCP_SDCA_INTMASK_SDCA_0, 0); ++ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, ++ SDW_SCP_SDCA_INTMASK_SDCA_8, 0); ++ } + + if (ret1 < 0 || ret2 < 0) { + /* log but don't prevent suspend from happening */ +@@ -443,13 +444,15 @@ static int rt711_sdca_dev_resume(struct device *dev) + return 0; + + if (!slave->unattach_request) { +- mutex_lock(&rt711->disable_irq_lock); +- if (rt711->disable_irq == true) { +- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); +- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); +- rt711->disable_irq = false; ++ scoped_guard(mutex, &rt711->disable_irq_lock) { ++ if (rt711->disable_irq) { ++ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, ++ SDW_SCP_SDCA_INTMASK_SDCA_0); ++ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, ++ SDW_SCP_SDCA_INTMASK_SDCA_8); ++ rt711->disable_irq = false; ++ } + } +- mutex_unlock(&rt711->disable_irq_lock); + } + + ret = sdw_slave_wait_for_init(slave, RT711_PROBE_TIMEOUT); +diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c +--- a/sound/soc/codecs/rt711-sdca.c ++++ b/sound/soc/codecs/rt711-sdca.c +@@ -6,6 +6,7 @@ + // + // + ++#include + #include + #include + #include +@@ -450,7 +451,7 @@ static void rt711_sdca_btn_check_handler(struct work_struct *work) + + static void rt711_sdca_jack_init(struct rt711_sdca_priv *rt711) + { +- mutex_lock(&rt711->calibrate_mutex); ++ guard(mutex)(&rt711->calibrate_mutex); + + if (rt711->hs_jack) { + /* Enable HID1 event & set button RTC mode */ +@@ -515,8 +516,6 @@ static void rt711_sdca_jack_init(struct rt711_sdca_priv *rt711) + + dev_dbg(&rt711->slave->dev, "in %s disable\n", __func__); + } +- +- mutex_unlock(&rt711->calibrate_mutex); + } + + static int rt711_sdca_set_jack_detect(struct snd_soc_component *component, +diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c +--- a/sound/soc/codecs/rt711-sdw.c ++++ b/sound/soc/codecs/rt711-sdw.c +@@ -6,6 +6,7 @@ + // + // + ++#include + #include + #include + #include +@@ -422,12 +423,11 @@ static int rt711_interrupt_callback(struct sdw_slave *slave, + dev_dbg(&slave->dev, + "%s control_port_stat=%x", __func__, status->control_port); + +- mutex_lock(&rt711->disable_irq_lock); ++ guard(mutex)(&rt711->disable_irq_lock); + if (status->control_port & 0x4 && !rt711->disable_irq) { + mod_delayed_work(system_power_efficient_wq, + &rt711->jack_detect_work, msecs_to_jiffies(250)); + } +- mutex_unlock(&rt711->disable_irq_lock); + + return 0; + } +@@ -509,11 +509,11 @@ static int rt711_dev_system_suspend(struct device *dev) + * deferred work completes and before the parent disables + * interrupts on the link + */ +- mutex_lock(&rt711->disable_irq_lock); +- rt711->disable_irq = true; +- ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, +- SDW_SCP_INT1_IMPL_DEF, 0); +- mutex_unlock(&rt711->disable_irq_lock); ++ scoped_guard(mutex, &rt711->disable_irq_lock) { ++ rt711->disable_irq = true; ++ ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, ++ SDW_SCP_INT1_IMPL_DEF, 0); ++ } + + if (ret < 0) { + /* log but don't prevent suspend from happening */ +@@ -535,12 +535,12 @@ static int rt711_dev_resume(struct device *dev) + return 0; + + if (!slave->unattach_request) { +- mutex_lock(&rt711->disable_irq_lock); +- if (rt711->disable_irq == true) { +- sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); +- rt711->disable_irq = false; ++ scoped_guard(mutex, &rt711->disable_irq_lock) { ++ if (rt711->disable_irq) { ++ sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); ++ rt711->disable_irq = false; ++ } + } +- mutex_unlock(&rt711->disable_irq_lock); + } + + ret = sdw_slave_wait_for_init(slave, RT711_PROBE_TIMEOUT); +diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c +--- a/sound/soc/codecs/rt711.c ++++ b/sound/soc/codecs/rt711.c +@@ -6,6 +6,7 @@ + // + // + ++#include + #include + #include + #include +@@ -89,24 +90,24 @@ static int rt711_calibration(struct rt711_priv *rt711) + struct regmap *regmap = rt711->regmap; + int ret = 0; + +- mutex_lock(&rt711->calibrate_mutex); ++ guard(mutex)(&rt711->calibrate_mutex); + regmap_write(rt711->regmap, +- RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); ++ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + + dev = regmap_get_device(regmap); + + /* Calibration manual mode */ + rt711_index_update_bits(regmap, RT711_VENDOR_REG, RT711_FSM_CTL, +- 0xf, 0x0); ++ 0xf, 0x0); + + /* trigger */ + rt711_index_update_bits(regmap, RT711_VENDOR_CALI, +- RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_CALI_TRIGGER, +- RT711_DAC_DC_CALI_TRIGGER); ++ RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_CALI_TRIGGER, ++ RT711_DAC_DC_CALI_TRIGGER); + + /* wait for calibration process */ + rt711_index_read(regmap, RT711_VENDOR_CALI, +- RT711_DAC_DC_CALI_CTL1, &val); ++ RT711_DAC_DC_CALI_CTL1, &val); + + while (val & RT711_DAC_DC_CALI_TRIGGER) { + if (loop >= 500) { +@@ -119,16 +120,15 @@ static int rt711_calibration(struct rt711_priv *rt711) + + usleep_range(10000, 11000); + rt711_index_read(regmap, RT711_VENDOR_CALI, +- RT711_DAC_DC_CALI_CTL1, &val); ++ RT711_DAC_DC_CALI_CTL1, &val); + } + + /* depop mode */ + rt711_index_update_bits(regmap, RT711_VENDOR_REG, +- RT711_FSM_CTL, 0xf, RT711_DEPOP_CTL); ++ RT711_FSM_CTL, 0xf, RT711_DEPOP_CTL); + + regmap_write(rt711->regmap, +- RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); +- mutex_unlock(&rt711->calibrate_mutex); ++ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + + dev_dbg(dev, "%s calibration complete, ret=%d\n", __func__, ret); + return ret; +@@ -362,24 +362,24 @@ static void rt711_jack_init(struct rt711_priv *rt711) + { + struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(rt711->component); + +- mutex_lock(&rt711->calibrate_mutex); ++ guard(mutex)(&rt711->calibrate_mutex); + /* power on */ + if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY) + regmap_write(rt711->regmap, +- RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); ++ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + + if (rt711->hs_jack) { + /* unsolicited response & IRQ control */ + regmap_write(rt711->regmap, +- RT711_SET_MIC2_UNSOLICITED_ENABLE, 0x82); ++ RT711_SET_MIC2_UNSOLICITED_ENABLE, 0x82); + regmap_write(rt711->regmap, +- RT711_SET_HP_UNSOLICITED_ENABLE, 0x81); ++ RT711_SET_HP_UNSOLICITED_ENABLE, 0x81); + regmap_write(rt711->regmap, +- RT711_SET_INLINE_UNSOLICITED_ENABLE, 0x83); ++ RT711_SET_INLINE_UNSOLICITED_ENABLE, 0x83); + rt711_index_write(rt711->regmap, RT711_VENDOR_REG, +- 0x10, 0x2420); ++ 0x10, 0x2420); + rt711_index_write(rt711->regmap, RT711_VENDOR_REG, +- 0x19, 0x2e11); ++ 0x19, 0x2e11); + + switch (rt711->jd_src) { + case RT711_JD1: +@@ -449,8 +449,7 @@ static void rt711_jack_init(struct rt711_priv *rt711) + /* power off */ + if (snd_soc_dapm_get_bias_level(dapm) <= SND_SOC_BIAS_STANDBY) + regmap_write(rt711->regmap, +- RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); +- mutex_unlock(&rt711->calibrate_mutex); ++ RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + } + + static int rt711_set_jack_detect(struct snd_soc_component *component, +@@ -511,7 +510,7 @@ static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol, + unsigned int read_ll, read_rl; + int i; + +- mutex_lock(&rt711->calibrate_mutex); ++ guard(mutex)(&rt711->calibrate_mutex); + + /* Can't use update bit function, so read the original value first */ + addr_h = mc->reg; +@@ -599,7 +598,6 @@ static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol, + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + +- mutex_unlock(&rt711->calibrate_mutex); + return 0; + } + +@@ -908,11 +906,11 @@ static int rt711_set_bias_level(struct snd_soc_component *component, + break; + + case SND_SOC_BIAS_STANDBY: +- mutex_lock(&rt711->calibrate_mutex); +- regmap_write(rt711->regmap, +- RT711_SET_AUDIO_POWER_STATE, +- AC_PWRST_D3); +- mutex_unlock(&rt711->calibrate_mutex); ++ scoped_guard(mutex, &rt711->calibrate_mutex) { ++ regmap_write(rt711->regmap, ++ RT711_SET_AUDIO_POWER_STATE, ++ AC_PWRST_D3); ++ } + break; + + default: +diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c +--- a/sound/soc/codecs/rt712-sdca-dmic.c ++++ b/sound/soc/codecs/rt712-sdca-dmic.c +@@ -912,10 +912,23 @@ static int rt712_sdca_dmic_dev_resume(struct device *dev) + } + + regcache_cache_only(rt712->regmap, false); +- regcache_sync(rt712->regmap); ++ ret = regcache_sync(rt712->regmap); ++ if (ret) ++ goto err_sync; ++ + regcache_cache_only(rt712->mbq_regmap, false); +- regcache_sync(rt712->mbq_regmap); ++ ret = regcache_sync(rt712->mbq_regmap); ++ if (ret) ++ goto err_sync; ++ + return 0; ++ ++err_sync: ++ regcache_cache_only(rt712->regmap, true); ++ regcache_cache_only(rt712->mbq_regmap, true); ++ regcache_mark_dirty(rt712->regmap); ++ regcache_mark_dirty(rt712->mbq_regmap); ++ return ret; + } + + static const struct dev_pm_ops rt712_sdca_dmic_pm = { +diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c +--- a/sound/soc/codecs/rt712-sdca-sdw.c ++++ b/sound/soc/codecs/rt712-sdca-sdw.c +@@ -6,6 +6,7 @@ + // + // + ++#include + #include + #include + #include +@@ -427,13 +428,13 @@ static int rt712_sdca_dev_system_suspend(struct device *dev) + * deferred work completes and before the parent disables + * interrupts on the link + */ +- mutex_lock(&rt712_sdca->disable_irq_lock); +- rt712_sdca->disable_irq = true; +- ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, +- SDW_SCP_SDCA_INTMASK_SDCA_0, 0); +- ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, +- SDW_SCP_SDCA_INTMASK_SDCA_8, 0); +- mutex_unlock(&rt712_sdca->disable_irq_lock); ++ scoped_guard(mutex, &rt712_sdca->disable_irq_lock) { ++ rt712_sdca->disable_irq = true; ++ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, ++ SDW_SCP_SDCA_INTMASK_SDCA_0, 0); ++ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, ++ SDW_SCP_SDCA_INTMASK_SDCA_8, 0); ++ } + + if (ret1 < 0 || ret2 < 0) { + /* log but don't prevent suspend from happening */ +@@ -455,14 +456,15 @@ static int rt712_sdca_dev_resume(struct device *dev) + return 0; + + if (!slave->unattach_request) { +- mutex_lock(&rt712->disable_irq_lock); +- if (rt712->disable_irq == true) { +- +- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); +- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); +- rt712->disable_irq = false; ++ scoped_guard(mutex, &rt712->disable_irq_lock) { ++ if (rt712->disable_irq) { ++ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, ++ SDW_SCP_SDCA_INTMASK_SDCA_0); ++ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, ++ SDW_SCP_SDCA_INTMASK_SDCA_8); ++ rt712->disable_irq = false; ++ } + } +- mutex_unlock(&rt712->disable_irq_lock); + } + + ret = sdw_slave_wait_for_init(slave, RT712_PROBE_TIMEOUT); +@@ -472,9 +474,20 @@ static int rt712_sdca_dev_resume(struct device *dev) + } + + regcache_cache_only(rt712->regmap, false); +- regcache_sync(rt712->regmap); ++ ret = regcache_sync(rt712->regmap); ++ if (ret) { ++ regcache_cache_only(rt712->regmap, true); ++ return ret; ++ } ++ + regcache_cache_only(rt712->mbq_regmap, false); +- regcache_sync(rt712->mbq_regmap); ++ ret = regcache_sync(rt712->mbq_regmap); ++ if (ret) { ++ regcache_cache_only(rt712->mbq_regmap, true); ++ regcache_cache_only(rt712->regmap, true); ++ return ret; ++ } ++ + return 0; + } + +diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c +--- a/sound/soc/codecs/rt712-sdca.c ++++ b/sound/soc/codecs/rt712-sdca.c +@@ -7,6 +7,7 @@ + // + + #include ++#include + #include + #include + #include +@@ -403,7 +404,7 @@ static void rt712_sdca_btn_check_handler(struct work_struct *work) + + static void rt712_sdca_jack_init(struct rt712_sdca_priv *rt712) + { +- mutex_lock(&rt712->calibrate_mutex); ++ guard(mutex)(&rt712->calibrate_mutex); + + if (rt712->hs_jack) { + /* Enable HID1 event & set button RTC mode */ +@@ -450,8 +451,6 @@ static void rt712_sdca_jack_init(struct rt712_sdca_priv *rt712) + + dev_dbg(&rt712->slave->dev, "in %s disable\n", __func__); + } +- +- mutex_unlock(&rt712->calibrate_mutex); + } + + static int rt712_sdca_set_jack_detect(struct snd_soc_component *component, +diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c +--- a/sound/soc/codecs/rt721-sdca-sdw.c ++++ b/sound/soc/codecs/rt721-sdca-sdw.c +@@ -6,6 +6,7 @@ + // + // + ++#include + #include + #include + #include +@@ -466,13 +467,13 @@ static int rt721_sdca_dev_system_suspend(struct device *dev) + * deferred work completes and before the parent disables + * interrupts on the link + */ +- mutex_lock(&rt721_sdca->disable_irq_lock); +- rt721_sdca->disable_irq = true; +- ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, +- SDW_SCP_SDCA_INTMASK_SDCA_0, 0); +- ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, +- SDW_SCP_SDCA_INTMASK_SDCA_8, 0); +- mutex_unlock(&rt721_sdca->disable_irq_lock); ++ scoped_guard(mutex, &rt721_sdca->disable_irq_lock) { ++ rt721_sdca->disable_irq = true; ++ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, ++ SDW_SCP_SDCA_INTMASK_SDCA_0, 0); ++ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, ++ SDW_SCP_SDCA_INTMASK_SDCA_8, 0); ++ } + + if (ret1 < 0 || ret2 < 0) { + /* log but don't prevent suspend from happening */ +@@ -494,13 +495,15 @@ static int rt721_sdca_dev_resume(struct device *dev) + return 0; + + if (!slave->unattach_request) { +- mutex_lock(&rt721->disable_irq_lock); +- if (rt721->disable_irq == true) { +- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); +- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); +- rt721->disable_irq = false; ++ scoped_guard(mutex, &rt721->disable_irq_lock) { ++ if (rt721->disable_irq) { ++ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, ++ SDW_SCP_SDCA_INTMASK_SDCA_0); ++ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, ++ SDW_SCP_SDCA_INTMASK_SDCA_8); ++ rt721->disable_irq = false; ++ } + } +- mutex_unlock(&rt721->disable_irq_lock); + } + + ret = sdw_slave_wait_for_init(slave, RT721_PROBE_TIMEOUT); +@@ -510,9 +513,20 @@ static int rt721_sdca_dev_resume(struct device *dev) + } + + regcache_cache_only(rt721->regmap, false); +- regcache_sync(rt721->regmap); ++ ret = regcache_sync(rt721->regmap); ++ if (ret) { ++ regcache_cache_only(rt721->regmap, true); ++ return ret; ++ } ++ + regcache_cache_only(rt721->mbq_regmap, false); +- regcache_sync(rt721->mbq_regmap); ++ ret = regcache_sync(rt721->mbq_regmap); ++ if (ret) { ++ regcache_cache_only(rt721->mbq_regmap, true); ++ regcache_cache_only(rt721->regmap, true); ++ return ret; ++ } ++ + return 0; + } + +diff --git a/sound/soc/codecs/rt721-sdca.c b/sound/soc/codecs/rt721-sdca.c +--- a/sound/soc/codecs/rt721-sdca.c ++++ b/sound/soc/codecs/rt721-sdca.c +@@ -5,7 +5,7 @@ + // Copyright(c) 2024 Realtek Semiconductor Corp. + // + // +- ++#include + #include + #include + #include +@@ -289,7 +289,7 @@ static void rt721_sdca_jack_preset(struct rt721_sdca_priv *rt721) + + static void rt721_sdca_jack_init(struct rt721_sdca_priv *rt721) + { +- mutex_lock(&rt721->calibrate_mutex); ++ guard(mutex)(&rt721->calibrate_mutex); + if (rt721->hs_jack) { + sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0); +@@ -309,7 +309,6 @@ static void rt721_sdca_jack_init(struct rt721_sdca_priv *rt721) + rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT, + RT721_GE_REL_CTRL1, 0x4000, 0x4000); + } +- mutex_unlock(&rt721->calibrate_mutex); + } + + static int rt721_sdca_set_jack_detect(struct snd_soc_component *component, +diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c +--- a/sound/soc/codecs/rt722-sdca-sdw.c ++++ b/sound/soc/codecs/rt722-sdca-sdw.c +@@ -6,6 +6,7 @@ + // + // + ++#include + #include + #include + #include +@@ -517,13 +518,13 @@ static int rt722_sdca_dev_system_suspend(struct device *dev) + * deferred work completes and before the parent disables + * interrupts on the link + */ +- mutex_lock(&rt722_sdca->disable_irq_lock); +- rt722_sdca->disable_irq = true; +- ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, +- SDW_SCP_SDCA_INTMASK_SDCA_0, 0); +- ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, +- SDW_SCP_SDCA_INTMASK_SDCA_8, 0); +- mutex_unlock(&rt722_sdca->disable_irq_lock); ++ scoped_guard(mutex, &rt722_sdca->disable_irq_lock) { ++ rt722_sdca->disable_irq = true; ++ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, ++ SDW_SCP_SDCA_INTMASK_SDCA_0, 0); ++ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, ++ SDW_SCP_SDCA_INTMASK_SDCA_8, 0); ++ } + + if (ret1 < 0 || ret2 < 0) { + /* log but don't prevent suspend from happening */ +@@ -545,13 +546,15 @@ static int rt722_sdca_dev_resume(struct device *dev) + return 0; + + if (!slave->unattach_request) { +- mutex_lock(&rt722->disable_irq_lock); +- if (rt722->disable_irq == true) { +- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); +- sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); +- rt722->disable_irq = false; ++ scoped_guard(mutex, &rt722->disable_irq_lock) { ++ if (rt722->disable_irq) { ++ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, ++ SDW_SCP_SDCA_INTMASK_SDCA_0); ++ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, ++ SDW_SCP_SDCA_INTMASK_SDCA_8); ++ rt722->disable_irq = false; ++ } + } +- mutex_unlock(&rt722->disable_irq_lock); + } + + ret = sdw_slave_wait_for_init(slave, RT722_PROBE_TIMEOUT); +@@ -561,7 +564,13 @@ static int rt722_sdca_dev_resume(struct device *dev) + } + + regcache_cache_only(rt722->regmap, false); +- regcache_sync(rt722->regmap); ++ ret = regcache_sync(rt722->regmap); ++ if (ret) { ++ regcache_cache_only(rt722->regmap, true); ++ regcache_mark_dirty(rt722->regmap); ++ return ret; ++ } ++ + return 0; + } + +diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c +--- a/sound/soc/codecs/rt722-sdca.c ++++ b/sound/soc/codecs/rt722-sdca.c +@@ -6,6 +6,7 @@ + // + // + ++#include + #include + #include + #include +@@ -294,7 +295,7 @@ static void rt722_sdca_btn_check_handler(struct work_struct *work) + + static void rt722_sdca_jack_init(struct rt722_sdca_priv *rt722) + { +- mutex_lock(&rt722->calibrate_mutex); ++ guard(mutex)(&rt722->calibrate_mutex); + if (rt722->hs_jack) { + /* set SCP_SDCA_IntMask1[0]=1 */ + sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK1, +@@ -317,7 +318,6 @@ static void rt722_sdca_jack_init(struct rt722_sdca_priv *rt722) + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_HDA_CTL, + RT722_GE_RELATED_CTL2, 0x4000, 0x4000); + } +- mutex_unlock(&rt722->calibrate_mutex); + } + + static int rt722_sdca_set_jack_detect(struct snd_soc_component *component, +@@ -352,8 +352,6 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) + static const char func_tag[] = "FUNC"; + static const char xu_tag[] = "XU"; + const char *dmi_vendor, *dmi_product, *dmi_sku; +- char *cae_filename; +- const struct firmware *cae_fw = NULL; + unsigned int cae_st_spk, cae_st_hp, cae_st_mic; + unsigned int func, value; + unsigned int combined_val; +@@ -385,7 +383,8 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) + space = strchr(dmi_sku, ' '); + s_len = space ? space - dmi_sku : strlen(dmi_sku); + +- cae_filename = kasprintf(GFP_KERNEL, ++ char *cae_filename __free(kfree) = ++ kasprintf(GFP_KERNEL, + "realtek/rt722/rt722_RAE_%.*s_%.*s_%.*s.dat", + v_len, dmi_vendor, + p_len, dmi_product, +@@ -399,8 +398,8 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) + regmap_write(rt722->regmap, RT722_MIC_CAE_PARAM39, 0x5f); + usleep_range(50000, 60000); + ++ const struct firmware *cae_fw __free(firmware) = NULL; + request_firmware(&cae_fw, cae_filename, dev); +- kfree(cae_filename); + if (!cae_fw) { + dev_err(dev, "%s: Failed to load CAE firmware\n", __func__); + return -ENOENT; +@@ -555,7 +554,6 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) + regcache_cache_bypass(rt722->regmap, false); + rt722->cae_update_done = 1; + dev_dbg(dev, "%s: CAE FW update done.\n", __func__); +- release_firmware(cae_fw); + return 0; + + verify_abort: +@@ -565,7 +563,6 @@ static int rt722_cae_load(struct rt722_sdca_priv *rt722) + out_release: + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG, + RT722_MISC_CTRL1, 0x8000, 0x0000); +- release_firmware(cae_fw); + dev_err(dev, "%s: CAE FW update aborted (ret=%d).\n", __func__, ret); + return ret; + } +@@ -1906,6 +1903,9 @@ int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave) + rt722_sdca_amp_preset(rt722); + rt722_sdca_jack_preset(rt722); + ++ if (rt722->hs_jack && (!rt722->first_hw_init)) ++ rt722_sdca_jack_init(rt722); ++ + if (rt722->first_hw_init) { + regcache_cache_bypass(rt722->regmap, false); + regcache_mark_dirty(rt722->regmap); +diff --git a/sound/soc/codecs/rt766-sdca-sdw.c b/sound/soc/codecs/rt766-sdca-sdw.c +new file mode 100644 +--- /dev/null ++++ b/sound/soc/codecs/rt766-sdca-sdw.c +@@ -0,0 +1,339 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++// ++// rt766-sdca-sdw.c -- rt766 SDCA ALSA SoC audio driver ++// ++// Copyright(c) 2026 Realtek Semiconductor Corp. ++// ++// ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "rt766-sdca.h" ++#include "rt766-sdca-sdw.h" ++ ++#define RT766_PROBE_TIMEOUT 5000 ++ ++static bool rt766_sdca_readable_register(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case SDW_SCP_SDCA_INT1 ... SDW_SCP_SDCA_INTMASK4: ++ case RT766_VERSION_ID ... RT766_BOND_LATCH_ID: ++ case 0xc344 ... 0xc345: ++ case 0xc900: ++ case 0xc920: ++ case 0xd540 ... 0xd542: ++ case 0xf01e: ++ case RT766_HP_POWER_STATE ... RT766_HP_FSM_CTL2_1: ++ case 0x310100: ++ case RT766_MCU_PATCH_ADDR1_START ... RT766_MCU_PATCH_ADDR1_END: ++ case RT766_MCU_PATCH_ADDR2_START ... RT766_MCU_PATCH_ADDR2_END: ++ case RT766_MUTE_REG(UAJ, USER_FU41, 1): ++ case RT766_MUTE_REG(UAJ, USER_FU41, 2): ++ case RT766_VOLUME_REG(UAJ, USER_FU41, 1): ++ case RT766_VOLUME_REG(UAJ, USER_FU41, 2): ++ case RT766_MUTE_REG(UAJ, USER_FU36, 1): ++ case RT766_MUTE_REG(UAJ, USER_FU36, 2): ++ case RT766_VOLUME_REG(UAJ, USER_FU36, 1): ++ case RT766_VOLUME_REG(UAJ, USER_FU36, 2): ++ case RT766_PDE_REQ_REG(UAJ, PDE47): ++ case RT766_PDE_REQ_REG(UAJ, PDE34): ++ case RT766_SDCA_CTL(UAJ, CS41, SDCA_CTL_CS_SAMPLERATEINDEX): ++ case RT766_SDCA_CTL(UAJ, CS36, SDCA_CTL_CS_SAMPLERATEINDEX): ++ case RT766_FUNC_STATUS_REG(UAJ): /* 0x40480000 */ ++ case RT766_PDE_ACTUAL_REG(UAJ, PDE47): /* 0x40481400 */ ++ case RT766_PDE_ACTUAL_REG(UAJ, PDE34): /* 0x40481480 */ ++ case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1): ++ case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2): ++ case RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_SELECTED_MODE): ++ case RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_DETECTED_MODE): /* 0x40600490 */ ++ case RT766_PDE_REQ_REG(MIC, PDE11): ++ case RT766_MUTE_REG(MIC, USER_FU113, 1): ++ case RT766_MUTE_REG(MIC, USER_FU113, 2): ++ case RT766_MUTE_REG(MIC, USER_FU113, 3): ++ case RT766_MUTE_REG(MIC, USER_FU113, 4): ++ case RT766_VOLUME_REG(MIC, USER_FU113, 1): ++ case RT766_VOLUME_REG(MIC, USER_FU113, 2): ++ case RT766_VOLUME_REG(MIC, USER_FU113, 3): ++ case RT766_VOLUME_REG(MIC, USER_FU113, 4): ++ case RT766_FUNC_STATUS_REG(MIC): /* 0x40880000 */ ++ case RT766_SDCA_CTL(MIC, CS113, SDCA_CTL_CS_SAMPLERATEINDEX): ++ case RT766_PDE_ACTUAL_REG(MIC, PDE11): /* 0x40881500 */ ++ case RT766_FUNC_STATUS_REG(HID): /* 0x40c80000 */ ++ /* 0x40c80080 - 0x40c80098 */ ++ case RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_CURRENTOWNER) ... ++ RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_MESSAGELENGTH): ++ case RT766_MUTE_REG(AMP, USER_FU21, 1): ++ case RT766_MUTE_REG(AMP, USER_FU21, 2): ++ case RT766_VOLUME_REG(AMP, USER_FU21, 1): ++ case RT766_VOLUME_REG(AMP, USER_FU21, 2): ++ case RT766_PDE_REQ_REG(AMP, PDE23): ++ case RT766_FUNC_STATUS_REG(AMP): /* 0x41080000 */ ++ case RT766_SDCA_CTL(AMP, PPU21, SDCA_CTL_PPU_POSTURENUMBER): ++ case RT766_SDCA_CTL(AMP, CS21, SDCA_CTL_CS_SAMPLERATEINDEX): ++ case RT766_PDE_ACTUAL_REG(AMP, PDE23): /* 0x41081980 */ ++ case RT766_BUF_ADDR_HID1 ... RT766_BUF_ADDR_HID2: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static bool rt766_sdca_volatile_register(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case SDW_SCP_SDCA_INT1 ... SDW_SCP_SDCA_INTMASK4: ++ case RT766_VERSION_ID ... RT766_BOND_LATCH_ID: ++ case 0xc344 ... 0xc345: ++ case 0xc900: ++ case 0xc920: ++ case 0xd540 ... 0xd542: ++ case 0xf01e: ++ case RT766_HP_POWER_STATE ... RT766_HP_FSM_CTL2_1: ++ case 0x310100: ++ case RT766_MCU_PATCH_ADDR1_START ... RT766_MCU_PATCH_ADDR1_END: ++ case RT766_MCU_PATCH_ADDR2_START ... RT766_MCU_PATCH_ADDR2_END: ++ case RT766_FUNC_STATUS_REG(UAJ): ++ case RT766_PDE_ACTUAL_REG(UAJ, PDE47): ++ case RT766_PDE_ACTUAL_REG(UAJ, PDE34): ++ case RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_DETECTED_MODE): ++ case RT766_FUNC_STATUS_REG(MIC): ++ case RT766_PDE_ACTUAL_REG(MIC, PDE11): ++ case RT766_FUNC_STATUS_REG(HID): ++ case RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_CURRENTOWNER) ... ++ RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_MESSAGELENGTH): ++ case RT766_FUNC_STATUS_REG(AMP): ++ case RT766_PDE_ACTUAL_REG(AMP, PDE23): ++ case RT766_BUF_ADDR_HID1 ... RT766_BUF_ADDR_HID2: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static int rt766_sdca_mbq_size(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case RT766_VOLUME_REG(UAJ, USER_FU41, 1): ++ case RT766_VOLUME_REG(UAJ, USER_FU41, 2): ++ case RT766_VOLUME_REG(UAJ, USER_FU36, 1): ++ case RT766_VOLUME_REG(UAJ, USER_FU36, 2): ++ case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1): ++ case RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2): ++ case RT766_VOLUME_REG(MIC, USER_FU113, 1): ++ case RT766_VOLUME_REG(MIC, USER_FU113, 2): ++ case RT766_VOLUME_REG(MIC, USER_FU113, 3): ++ case RT766_VOLUME_REG(MIC, USER_FU113, 4): ++ case RT766_VOLUME_REG(AMP, USER_FU21, 1): ++ case RT766_VOLUME_REG(AMP, USER_FU21, 2): ++ return 2; ++ default: ++ return 1; ++ } ++} ++ ++static const struct regmap_sdw_mbq_cfg rt766_sdca_mbq_cfg = { ++ .mbq_size = rt766_sdca_mbq_size, ++}; ++ ++static const struct regmap_config rt766_sdca_regmap = { ++ .reg_bits = 32, ++ .val_bits = 16, ++ .readable_reg = rt766_sdca_readable_register, ++ .volatile_reg = rt766_sdca_volatile_register, ++ .reg_defaults = rt766_sdca_defaults, ++ .num_reg_defaults = ARRAY_SIZE(rt766_sdca_defaults), ++ .max_register = SDW_SDCA_MAX_REGISTER, ++ .cache_type = REGCACHE_MAPLE, ++ .use_single_read = true, ++ .use_single_write = true, ++}; ++ ++static int rt766_sdca_update_status(struct sdw_slave *slave, ++ enum sdw_slave_status status) ++{ ++ struct rt766_sdca_priv *rt766 = dev_get_drvdata(&slave->dev); ++ ++ if (status == SDW_SLAVE_UNATTACHED) ++ rt766->hw_init = false; ++ ++ if (status == SDW_SLAVE_ATTACHED) { ++ if (rt766->hs_jack) { ++ /* ++ * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then ++ * if the device attached again, we will need to set the setting back. ++ * It could avoid losing the jack detection interrupt. ++ * This also could sync with the cache value as the rt766_sdca_jack_init set. ++ */ ++ sdw_write_no_pm(rt766->slave, SDW_SCP_SDCA_INTMASK3, ++ SDW_SCP_SDCA_INTMASK_SDCA_16); ++ sdw_write_no_pm(rt766->slave, SDW_SCP_SDCA_INTMASK4, ++ SDW_SCP_SDCA_INTMASK_SDCA_24); ++ } ++ } ++ ++ /* ++ * Perform initialization only if slave status is present and ++ * hw_init flag is false ++ */ ++ if (rt766->hw_init || status != SDW_SLAVE_ATTACHED) ++ return 0; ++ ++ /* perform I/O transfers required for Slave initialization */ ++ return rt766_sdca_io_init(&slave->dev, slave); ++} ++ ++static int rt766_sdca_read_prop(struct sdw_slave *slave) ++{ ++ struct sdw_slave_prop *prop = &slave->prop; ++ int ret; ++ ++ ret = sdw_slave_read_prop(slave); ++ if (ret < 0) ++ return ret; ++ ++ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; ++ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; ++ /* ++ * SDCA interrupts are routed through SoundWire domain IRQ. ++ */ ++ prop->use_domain_irq = true; ++ ++ return 0; ++} ++ ++static const struct sdw_slave_ops rt766_sdca_slave_ops = { ++ .read_prop = rt766_sdca_read_prop, ++ .update_status = rt766_sdca_update_status, ++}; ++ ++static int rt766_sdca_sdw_probe(struct sdw_slave *slave, ++ const struct sdw_device_id *id) ++{ ++ struct regmap *regmap; ++ ++ /* Regmap Initialization */ ++ regmap = devm_regmap_init_sdw_mbq_cfg(&slave->dev, slave, ++ &rt766_sdca_regmap, &rt766_sdca_mbq_cfg); ++ if (IS_ERR(regmap)) ++ return PTR_ERR(regmap); ++ ++ return rt766_sdca_init(&slave->dev, regmap, slave); ++} ++ ++static void rt766_sdca_sdw_remove(struct sdw_slave *slave) ++{ ++ pm_runtime_disable(&slave->dev); ++} ++ ++static const struct sdw_device_id rt766_sdca_id[] = { ++ SDW_SLAVE_ENTRY_EXT(0x025d, 0x766, 0x3, 0x1, 0), ++ SDW_SLAVE_ENTRY_EXT(0x025d, 0x767, 0x3, 0x1, 0), ++ {}, ++}; ++MODULE_DEVICE_TABLE(sdw, rt766_sdca_id); ++ ++static int rt766_sdca_dev_suspend(struct device *dev) ++{ ++ struct rt766_sdca_priv *rt766 = dev_get_drvdata(dev); ++ ++ if (!rt766->hw_init) ++ return 0; ++ ++ regcache_cache_only(rt766->regmap, true); ++ return 0; ++} ++ ++static int rt766_sdca_dev_system_suspend(struct device *dev) ++{ ++ struct rt766_sdca_priv *rt766 = dev_get_drvdata(dev); ++ struct sdw_slave *slave = dev_to_sdw_dev(dev); ++ int ret1, ret2; ++ ++ if (!rt766->hw_init) ++ return 0; ++ ++ /* ++ * prevent new interrupts from being handled after the ++ * deferred work completes and before the parent disables ++ * interrupts on the link ++ */ ++ mutex_lock(&rt766->disable_irq_lock); ++ rt766->disable_irq = true; ++ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK3, ++ SDW_SCP_SDCA_INTMASK_SDCA_16, 0); ++ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK4, ++ SDW_SCP_SDCA_INTMASK_SDCA_24, 0); ++ mutex_unlock(&rt766->disable_irq_lock); ++ ++ if (ret1 < 0 || ret2 < 0) { ++ /* log but don't prevent suspend from happening */ ++ dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__); ++ } ++ ++ return rt766_sdca_dev_suspend(dev); ++} ++ ++static int rt766_sdca_dev_resume(struct device *dev) ++{ ++ struct sdw_slave *slave = dev_to_sdw_dev(dev); ++ struct rt766_sdca_priv *rt766 = dev_get_drvdata(dev); ++ int ret; ++ ++ if (!rt766->first_hw_init) ++ return 0; ++ ++ if (!slave->unattach_request) { ++ mutex_lock(&rt766->disable_irq_lock); ++ if (rt766->disable_irq == true) { ++ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK3, SDW_SCP_SDCA_INTMASK_SDCA_16); ++ sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK4, SDW_SCP_SDCA_INTMASK_SDCA_24); ++ rt766->disable_irq = false; ++ } ++ mutex_unlock(&rt766->disable_irq_lock); ++ goto regmap_sync; ++ } ++ ++ ret = sdw_slave_wait_for_init(slave, RT766_PROBE_TIMEOUT); ++ if (ret) { ++ sdw_show_ping_status(slave->bus, true); ++ return ret; ++ } ++ ++regmap_sync: ++ regcache_cache_only(rt766->regmap, false); ++ ret = regcache_sync(rt766->regmap); ++ if (ret) { ++ regcache_cache_only(rt766->regmap, true); ++ regcache_mark_dirty(rt766->regmap); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops rt766_sdca_pm = { ++ SYSTEM_SLEEP_PM_OPS(rt766_sdca_dev_system_suspend, rt766_sdca_dev_resume) ++ RUNTIME_PM_OPS(rt766_sdca_dev_suspend, rt766_sdca_dev_resume, NULL) ++}; ++ ++static struct sdw_driver rt766_sdca_sdw_driver = { ++ .driver = { ++ .name = "rt766-sdca", ++ .pm = pm_ptr(&rt766_sdca_pm), ++ }, ++ .probe = rt766_sdca_sdw_probe, ++ .remove = rt766_sdca_sdw_remove, ++ .ops = &rt766_sdca_slave_ops, ++ .id_table = rt766_sdca_id, ++}; ++module_sdw_driver(rt766_sdca_sdw_driver); ++ ++MODULE_DESCRIPTION("ASoC RT766 SDCA SDW driver"); ++MODULE_AUTHOR("Shuming Fan "); ++MODULE_LICENSE("GPL"); ++MODULE_IMPORT_NS("SND_SOC_SDCA"); +diff --git a/sound/soc/codecs/rt766-sdca-sdw.h b/sound/soc/codecs/rt766-sdca-sdw.h +new file mode 100644 +--- /dev/null ++++ b/sound/soc/codecs/rt766-sdca-sdw.h +@@ -0,0 +1,61 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * rt766-sdca-sdw.h -- RT766 SDCA ALSA SoC audio driver header ++ * ++ * Copyright(c) 2026 Realtek Semiconductor Corp. ++ */ ++ ++#ifndef __RT766_SDW_H__ ++#define __RT766_SDW_H__ ++ ++#include ++#include ++ ++static const struct reg_default rt766_sdca_defaults[] = { ++ /* 0x40400289 - 0x4040028a */ ++ { RT766_MUTE_REG(UAJ, USER_FU41, 1), 0x01 }, ++ { RT766_MUTE_REG(UAJ, USER_FU41, 2), 0x01 }, ++ /* 0x40400291 - 0x40400292 */ ++ { RT766_VOLUME_REG(UAJ, USER_FU41, 1), 0x0000 }, ++ { RT766_VOLUME_REG(UAJ, USER_FU41, 2), 0x0000 }, ++ /* 0x40400789 - 0x4040078a */ ++ { RT766_MUTE_REG(UAJ, USER_FU36, 1), 0x01 }, ++ { RT766_MUTE_REG(UAJ, USER_FU36, 2), 0x01 }, ++ /* 0x40400791 - 0x40400792 */ ++ { RT766_VOLUME_REG(UAJ, USER_FU36, 1), 0x0000 }, ++ { RT766_VOLUME_REG(UAJ, USER_FU36, 2), 0x0000 }, ++ { RT766_PDE_REQ_REG(UAJ, PDE47), 0x03 }, /* 0x40401408 */ ++ { RT766_PDE_REQ_REG(UAJ, PDE34), 0x03 }, /* 0x40401488 */ ++ { RT766_SDCA_CTL(UAJ, CS41, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 }, /* 0x40480080 */ ++ { RT766_SDCA_CTL(UAJ, CS36, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 }, /* 0x40480880 */ ++ /* 0x40600259 - 0x4060025a */ ++ { RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1), 0xfe00 }, ++ { RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2), 0xfe00 }, ++ { RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_SELECTED_MODE), 0x00 }, /* 0x40600488 */ ++ ++ { RT766_PDE_REQ_REG(MIC, PDE11), 0x03 }, /* 0x40801508 */ ++ /* 0x40801809 - 0x4080180c */ ++ { RT766_MUTE_REG(MIC, USER_FU113, 1), 0x01 }, ++ { RT766_MUTE_REG(MIC, USER_FU113, 2), 0x01 }, ++ { RT766_MUTE_REG(MIC, USER_FU113, 3), 0x01 }, ++ { RT766_MUTE_REG(MIC, USER_FU113, 4), 0x01 }, ++ /* 0x40801811 - 0x40801814 */ ++ { RT766_VOLUME_REG(MIC, USER_FU113, 1), 0x0000 }, ++ { RT766_VOLUME_REG(MIC, USER_FU113, 2), 0x0000 }, ++ { RT766_VOLUME_REG(MIC, USER_FU113, 3), 0x0000 }, ++ { RT766_VOLUME_REG(MIC, USER_FU113, 4), 0x0000 }, ++ { RT766_SDCA_CTL(MIC, CS113, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 }, /* 0x40880900 */ ++ ++ /* 0x41000189 - 0x4100018a */ ++ { RT766_MUTE_REG(AMP, USER_FU21, 1), 0x01 }, ++ { RT766_MUTE_REG(AMP, USER_FU21, 2), 0x01 }, ++ /* 0x41000191 - 0x41000192 */ ++ { RT766_VOLUME_REG(AMP, USER_FU21, 1), 0x0000 }, ++ { RT766_VOLUME_REG(AMP, USER_FU21, 2), 0x0000 }, ++ { RT766_PDE_REQ_REG(AMP, PDE23), 0x03 }, /* 0x41001988 */ ++ { RT766_SDCA_CTL(AMP, PPU21, SDCA_CTL_PPU_POSTURENUMBER), 0x00 }, /* 0x41080200 */ ++ { RT766_SDCA_CTL(AMP, CS21, SDCA_CTL_CS_SAMPLERATEINDEX), 0x09 }, /* 0x41081080 */ ++ ++}; ++ ++#endif /* __RT766_SDW_H__ */ +diff --git a/sound/soc/codecs/rt766-sdca.c b/sound/soc/codecs/rt766-sdca.c +new file mode 100644 +--- /dev/null ++++ b/sound/soc/codecs/rt766-sdca.c +@@ -0,0 +1,1378 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++// ++// rt766-sdca.c -- rt766 SDCA ALSA SoC audio driver ++// ++// Copyright(c) 2026 Realtek Semiconductor Corp. ++// ++// ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "rt766-sdca.h" ++#include "rt-sdw-common.h" ++ ++static int rt766_sdca_btn_detect(struct sdca_interrupt *interrupt) ++{ ++ struct rt766_sdca_priv *rt766 = interrupt->priv; ++ unsigned char *buf = NULL; ++ unsigned int offset, owner, length; ++ unsigned int det_mode, idx, val; ++ int ret; ++ ++ ret = regmap_read(rt766->regmap, ++ RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_DETECTED_MODE), ++ &det_mode); ++ if (ret < 0) ++ goto io_error; ++ ++ /* get current UMP message owner */ ++ ret = regmap_read(rt766->regmap, ++ RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_CURRENTOWNER), ++ &owner); ++ if (ret < 0) ++ goto io_error; ++ ++ /* if owner is device then there is no button event from device */ ++ if (owner == 1) ++ return 0; ++ ++ if (det_mode) { ++ /* read UMP message length */ ++ ret = regmap_read(rt766->regmap, ++ RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_MESSAGELENGTH), ++ &length); ++ if (ret < 0) ++ goto _end_btn_det_; ++ ++ /* read UMP message offset */ ++ ret = regmap_read(rt766->regmap, ++ RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_MESSAGEOFFSET), ++ &offset); ++ if (ret < 0) ++ goto _end_btn_det_; ++ ++ buf = devm_kzalloc(&rt766->slave->dev, length, GFP_KERNEL); ++ if (!buf) { ++ dev_err(&rt766->slave->dev, "%s: alloc buf failed\n", __func__); ++ goto _end_btn_det_; ++ } ++ ++ for (idx = 0; idx < length; idx++) { ++ ret = regmap_read(rt766->regmap, ++ RT766_BUF_ADDR_HID1 + offset + idx, &val); ++ if (ret < 0) ++ goto _end_btn_det_; ++ buf[idx] = val & 0xff; ++ } ++ ++ if (rt766->hid) ++ hid_input_report(rt766->hid, HID_INPUT_REPORT, ++ buf, length, 1); ++ } ++ ++_end_btn_det_: ++ if (buf) ++ devm_kfree(&rt766->slave->dev, buf); ++ ++ /* Host is owner, so set back to device */ ++ if (owner == 0) { ++ regmap_write(rt766->regmap, ++ RT766_SDCA_CTL(HID, HID101, SDCA_CTL_HIDE_HIDTX_CURRENTOWNER), 0x01); ++ } ++ ++ return 0; ++ ++io_error: ++ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); ++ return ret; ++} ++ ++static irqreturn_t rt766_sdca_irq_btn_handler(int irq, void *data) ++{ ++ struct sdca_interrupt *interrupt = data; ++ struct rt766_sdca_priv *rt766 = interrupt->priv; ++ ++ if (!rt766->hs_jack) ++ return IRQ_HANDLED; ++ ++ if (!rt766->component->card || !rt766->component->card->instantiated) ++ return IRQ_HANDLED; ++ ++ mutex_lock(&rt766->disable_irq_lock); ++ if (!rt766->disable_irq) ++ rt766_sdca_btn_detect(interrupt); ++ mutex_unlock(&rt766->disable_irq_lock); ++ return IRQ_HANDLED; ++} ++ ++static int rt766_sdca_headset_detect(struct rt766_sdca_priv *rt766) ++{ ++ unsigned int det_mode; ++ int ret; ++ ++ /* get detected_mode */ ++ ret = regmap_read(rt766->regmap, ++ RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_DETECTED_MODE), ++ &det_mode); ++ if (ret < 0) ++ goto io_error; ++ ++ switch (det_mode) { ++ case 0x00: ++ rt766->jack_type = 0; ++ break; ++ case 0x03: ++ rt766->jack_type = SND_JACK_HEADPHONE; ++ break; ++ case 0x05: ++ rt766->jack_type = SND_JACK_HEADSET; ++ break; ++ } ++ ++ /* write selected_mode */ ++ if (det_mode) { ++ ret = regmap_write(rt766->regmap, ++ RT766_SDCA_CTL(UAJ, GE49, SDCA_CTL_GE_SELECTED_MODE), ++ det_mode); ++ if (ret < 0) ++ goto io_error; ++ } ++ ++ dev_dbg(&rt766->slave->dev, ++ "%s, detected_mode=0x%x\n", __func__, det_mode); ++ ++ return 0; ++ ++io_error: ++ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); ++ return ret; ++} ++ ++static irqreturn_t rt766_sdca_irq_jd_handler(int irq, void *data) ++{ ++ struct sdca_interrupt *interrupt = data; ++ struct rt766_sdca_priv *rt766 = interrupt->priv; ++ ++ if (!rt766->hs_jack) ++ return IRQ_HANDLED; ++ ++ if (!rt766->component->card || !rt766->component->card->instantiated) ++ return IRQ_HANDLED; ++ ++ mutex_lock(&rt766->disable_irq_lock); ++ if (!rt766->disable_irq) ++ rt766_sdca_headset_detect(rt766); ++ mutex_unlock(&rt766->disable_irq_lock); ++ ++ dev_dbg(&rt766->slave->dev, ++ "in %s, jack_type=%d\n", __func__, rt766->jack_type); ++ ++ snd_soc_jack_report(rt766->hs_jack, rt766->jack_type, SND_JACK_HEADSET); ++ return IRQ_HANDLED; ++} ++ ++static void rt766_sdca_destroy_hid_device(struct sdca_interrupt *interrupt) ++{ ++ struct rt766_sdca_priv *rt766 = interrupt->priv; ++ ++ hid_destroy_device(rt766->hid); ++} ++ ++static int rt766_sdca_irq_ctl(struct rt766_sdca_priv *rt766, ++ struct sdca_function_data *function, ++ struct snd_soc_component *component, ++ struct sdca_interrupt_info *info, ++ bool enabled) ++{ ++ struct device *dev = &rt766->slave->dev; ++ struct sdca_interrupt *interrupt; ++ struct sdca_control *control; ++ struct sdca_entity *entity; ++ irq_handler_t handler; ++ int i, j, irq, ret; ++ ++ for (i = 0; i < function->num_entities; i++) { ++ entity = &function->entities[i]; ++ ++ for (j = 0; j < entity->num_controls; j++) { ++ control = &entity->controls[j]; ++ irq = control->interrupt_position; ++ ++ switch (SDCA_CTL_TYPE(entity->type, control->sel)) { ++ case SDCA_CTL_TYPE_S(GE, DETECTED_MODE): ++ handler = rt766_sdca_irq_jd_handler; ++ break; ++ case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER): ++ handler = rt766_sdca_irq_btn_handler; ++ break; ++ default: ++ continue; ++ } ++ ++ interrupt = &info->irqs[irq]; ++ ++ if (enabled) { ++ ret = sdca_irq_data_populate(dev, rt766->regmap, component, ++ function, entity, control, ++ interrupt); ++ if (ret) ++ return ret; ++ ++ if (handler == rt766_sdca_irq_btn_handler) { ++ ret = sdca_add_hid_device(interrupt); ++ if (ret) ++ return ret; ++ ++ interrupt->free_priv = rt766_sdca_destroy_hid_device; ++ rt766->hid = interrupt->priv; ++ } ++ ++ interrupt->priv = rt766; ++ ret = sdca_irq_request(dev, info, irq, interrupt->name, ++ handler, interrupt); ++ if (ret) { ++ dev_err(dev, "failed to request irq %s: %d\n", ++ interrupt->name, ret); ++ sdca_irq_cleanup_late(dev, function, info); ++ return ret; ++ } ++ dev_dbg(dev, "Requesting IRQ %d InterruptName=%s\n", irq, interrupt->name); ++ } else { ++ sdca_irq_cleanup_late(dev, function, info); ++ dev_dbg(dev, "Freeing IRQ %d\n", irq); ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++static int rt766_sdca_set_jack_detect(struct snd_soc_component *component, ++ struct snd_soc_jack *hs_jack, void *data) ++{ ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ int ret; ++ ++ if (!rt766->uaj_func_data) { ++ dev_err(&rt766->slave->dev, "The SDCA UAJ function is not supported.\n"); ++ return -EINVAL; ++ } ++ ++ rt766->hs_jack = hs_jack; ++ ++ if (!rt766->first_hw_init) ++ return 0; ++ ++ ret = pm_runtime_resume_and_get(component->dev); ++ if (ret < 0) { ++ if (ret != -EACCES) { ++ dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret); ++ return ret; ++ } ++ ++ /* pm_runtime not enabled yet */ ++ dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__); ++ return 0; ++ } ++ ++ /* disable interrupts if hs_jack is not set */ ++ if (!rt766->hs_jack) { ++ if (rt766->uaj_func_data) ++ rt766_sdca_irq_ctl(rt766, rt766->uaj_func_data, ++ rt766->component, rt766->irq_info, false); ++ ++ if (rt766->hid_func_data) ++ rt766_sdca_irq_ctl(rt766, rt766->hid_func_data, ++ rt766->component, rt766->irq_info, false); ++ } ++ ++ pm_runtime_put_autosuspend(component->dev); ++ ++ return 0; ++} ++ ++static int rt766_sdca_set_fu_ctl(struct rt766_sdca_priv *rt766, int func_num, int fu_num) ++{ ++ unsigned int fu01_reg, fu02_reg; ++ unsigned int ch_01, ch_02; ++ unsigned int ch_mute; ++ unsigned int fu_reg; ++ int err, i; ++ ++ switch (fu_num) { ++ case RT766_SDCA_ENT_USER_FU41: ++ ch_01 = (rt766->fu41_dapm_mute || rt766->fu41_mixer_l_mute) ? 0x01 : 0x00; ++ ch_02 = (rt766->fu41_dapm_mute || rt766->fu41_mixer_r_mute) ? 0x01 : 0x00; ++ break; ++ case RT766_SDCA_ENT_USER_FU36: ++ ch_01 = (rt766->fu36_dapm_mute || rt766->fu36_mixer_l_mute) ? 0x01 : 0x00; ++ ch_02 = (rt766->fu36_dapm_mute || rt766->fu36_mixer_r_mute) ? 0x01 : 0x00; ++ break; ++ case RT766_SDCA_ENT_USER_FU21: ++ ch_01 = (rt766->fu21_dapm_mute || rt766->fu21_mixer_l_mute) ? 0x01 : 0x00; ++ ch_02 = (rt766->fu21_dapm_mute || rt766->fu21_mixer_r_mute) ? 0x01 : 0x00; ++ break; ++ case RT766_SDCA_ENT_USER_FU113: ++ for (i = 0; i < ARRAY_SIZE(rt766->fu113_mixer_mute); i++) { ++ ch_mute = (rt766->fu113_dapm_mute || rt766->fu113_mixer_mute[i]) ? 0x01 : 0x00; ++ fu_reg = SDW_SDCA_CTL(func_num, fu_num, SDCA_CTL_FU_MUTE, 1) + i; ++ err = regmap_write(rt766->regmap, fu_reg, ch_mute); ++ if (err < 0) ++ return err; ++ } ++ return 0; ++ } ++ ++ fu01_reg = SDW_SDCA_CTL(func_num, fu_num, SDCA_CTL_FU_MUTE, 1); ++ fu02_reg = SDW_SDCA_CTL(func_num, fu_num, SDCA_CTL_FU_MUTE, 2); ++ err = regmap_write(rt766->regmap, fu01_reg, ch_01); ++ if (err < 0) ++ return err; ++ err = regmap_write(rt766->regmap, fu02_reg, ch_02); ++ if (err < 0) ++ return err; ++ ++ return 0; ++} ++ ++static int rt766_sdca_fu41_playback_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ ++ ucontrol->value.integer.value[0] = !rt766->fu41_mixer_l_mute; ++ ucontrol->value.integer.value[1] = !rt766->fu41_mixer_r_mute; ++ return 0; ++} ++ ++static int rt766_sdca_fu41_playback_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ int err; ++ ++ if (rt766->fu41_mixer_l_mute == !ucontrol->value.integer.value[0] && ++ rt766->fu41_mixer_r_mute == !ucontrol->value.integer.value[1]) ++ return 0; ++ ++ rt766->fu41_mixer_l_mute = !ucontrol->value.integer.value[0]; ++ rt766->fu41_mixer_r_mute = !ucontrol->value.integer.value[1]; ++ ++ err = rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU41); ++ if (err < 0) ++ return err; ++ ++ return 1; ++} ++ ++static int rt766_sdca_fu36_capture_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ ++ ucontrol->value.integer.value[0] = !rt766->fu36_mixer_l_mute; ++ ucontrol->value.integer.value[1] = !rt766->fu36_mixer_r_mute; ++ return 0; ++} ++ ++static int rt766_sdca_fu36_capture_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ int err; ++ ++ if (rt766->fu36_mixer_l_mute == !ucontrol->value.integer.value[0] && ++ rt766->fu36_mixer_r_mute == !ucontrol->value.integer.value[1]) ++ return 0; ++ ++ rt766->fu36_mixer_l_mute = !ucontrol->value.integer.value[0]; ++ rt766->fu36_mixer_r_mute = !ucontrol->value.integer.value[1]; ++ err = rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU36); ++ if (err < 0) ++ return err; ++ ++ return 1; ++} ++ ++static int rt766_sdca_fu21_playback_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ ++ ucontrol->value.integer.value[0] = !rt766->fu21_mixer_l_mute; ++ ucontrol->value.integer.value[1] = !rt766->fu21_mixer_r_mute; ++ return 0; ++} ++ ++static int rt766_sdca_fu21_playback_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ int err; ++ ++ if (rt766->fu21_mixer_l_mute == !ucontrol->value.integer.value[0] && ++ rt766->fu21_mixer_r_mute == !ucontrol->value.integer.value[1]) ++ return 0; ++ ++ rt766->fu21_mixer_l_mute = !ucontrol->value.integer.value[0]; ++ rt766->fu21_mixer_r_mute = !ucontrol->value.integer.value[1]; ++ ++ err = rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_AMP, RT766_SDCA_ENT_USER_FU21); ++ if (err < 0) ++ return err; ++ ++ return 1; ++} ++ ++static int rt766_sdca_fu113_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, int event) ++{ ++ struct snd_soc_component *component = ++ snd_soc_dapm_to_component(w->dapm); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ ++ switch (event) { ++ case SND_SOC_DAPM_POST_PMU: ++ rt766->fu113_dapm_mute = false; ++ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_MIC, RT766_SDCA_ENT_USER_FU113); ++ break; ++ case SND_SOC_DAPM_PRE_PMD: ++ rt766->fu113_dapm_mute = true; ++ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_MIC, RT766_SDCA_ENT_USER_FU113); ++ break; ++ } ++ return 0; ++} ++ ++static int rt766_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ struct rt_sdca_dmic_kctrl_priv *p = ++ (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value; ++ const unsigned int interval_offset = 0xc0; ++ unsigned int regvalue, ctl, i; ++ ++ /* check all channels */ ++ for (i = 0; i < p->count; i++) { ++ regmap_read(rt766->regmap, p->reg_base + i, ®value); ++ ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset); ++ ++ ucontrol->value.integer.value[i] = ctl; ++ } ++ ++ return 0; ++} ++ ++static int rt766_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct rt_sdca_dmic_kctrl_priv *p = ++ (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value; ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ const unsigned int interval_offset = 0xc0; ++ unsigned int gain_val[4]; ++ unsigned int i, changed = 0; ++ unsigned int regvalue[4]; ++ int err; ++ ++ /* check all channels */ ++ for (i = 0; i < p->count; i++) { ++ regmap_read(rt766->regmap, p->reg_base + i, ®value[i]); ++ ++ gain_val[i] = ucontrol->value.integer.value[i]; ++ if (gain_val[i] > p->max) ++ gain_val[i] = p->max; ++ ++ gain_val[i] = 0x1e00 - ((p->max - gain_val[i]) * interval_offset); ++ gain_val[i] &= 0xffff; ++ ++ if (regvalue[i] != gain_val[i]) ++ changed = 1; ++ } ++ ++ if (!changed) ++ return 0; ++ ++ for (i = 0; i < p->count; i++) { ++ err = regmap_write(rt766->regmap, p->reg_base + i, gain_val[i]); ++ if (err < 0) ++ dev_err(&rt766->slave->dev, "0x%08x can't be set\n", p->reg_base + i); ++ } ++ ++ return changed; ++} ++ ++static int rt766_sdca_dmic_fu113_capture_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ unsigned int i; ++ ++ for (i = 0; i < 4; i++) ++ ucontrol->value.integer.value[i] = !rt766->fu113_mixer_mute[i]; ++ return 0; ++} ++ ++static int rt766_sdca_dmic_fu113_capture_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ int err, changed = 0, i; ++ ++ for (i = 0; i < 4; i++) { ++ if (rt766->fu113_mixer_mute[i] != !ucontrol->value.integer.value[i]) ++ changed = 1; ++ rt766->fu113_mixer_mute[i] = !ucontrol->value.integer.value[i]; ++ } ++ ++ err = rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_MIC, RT766_SDCA_ENT_USER_FU113); ++ if (err < 0) ++ return err; ++ return changed; ++} ++ ++static int rt766_sdca_fu41_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, int event) ++{ ++ struct snd_soc_component *component = ++ snd_soc_dapm_to_component(w->dapm); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ ++ switch (event) { ++ case SND_SOC_DAPM_POST_PMU: ++ rt766->fu41_dapm_mute = false; ++ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU41); ++ break; ++ case SND_SOC_DAPM_PRE_PMD: ++ rt766->fu41_dapm_mute = true; ++ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU41); ++ break; ++ } ++ return 0; ++} ++ ++static int rt766_sdca_pde_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, int event, int func_num, int pde_num, const char *pde_ent) ++{ ++ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ struct sdca_function_data *func_data; ++ unsigned char ps0 = 0x0, ps3 = 0x3; ++ const struct sdca_entity *entity; ++ unsigned int pde_req_reg; ++ int from_ps, to_ps; ++ int ret; ++ ++ pde_req_reg = SDW_SDCA_CTL(func_num, pde_num, SDCA_CTL_PDE_REQUESTED_PS, 0); ++ ++ switch (func_num) { ++ case RT766_FUNC_NUM_UAJ: ++ func_data = rt766->uaj_func_data; ++ break; ++ case RT766_FUNC_NUM_AMP: ++ func_data = rt766->sa_func_data; ++ break; ++ case RT766_FUNC_NUM_MIC: ++ func_data = rt766->sm_func_data; ++ break; ++ default: ++ dev_err(component->dev, "%s: unsupported func_num %d\n", ++ __func__, func_num); ++ return -EINVAL; ++ } ++ ++ switch (event) { ++ case SND_SOC_DAPM_POST_PMU: ++ regmap_write(rt766->regmap, pde_req_reg, ps0); ++ from_ps = ps3; ++ to_ps = ps0; ++ break; ++ case SND_SOC_DAPM_PRE_PMD: ++ regmap_write(rt766->regmap, pde_req_reg, ps3); ++ from_ps = ps0; ++ to_ps = ps3; ++ break; ++ } ++ ++ entity = sdca_find_entity_by_label(func_data, pde_ent); ++ if (!entity) { ++ dev_err(component->dev, "%s: failed to find entity %s\n", ++ __func__, pde_ent); ++ return -EINVAL; ++ } ++ ++ ret = sdca_asoc_pde_poll_actual_ps(rt766->regmap, ++ func_num, ++ pde_num, ++ from_ps, to_ps, ++ entity->pde.max_delay, ++ entity->pde.num_max_delay); ++ if (ret) ++ dev_err(component->dev, "%s: PDE transition %x -> %x failed, err=%d\n", ++ __func__, from_ps, to_ps, ret); ++ ++ return ret; ++} ++ ++static int rt766_sdca_pde47_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, int event) ++{ ++ return rt766_sdca_pde_event(w, kcontrol, event, ++ RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_PDE47, "PDE 47"); ++} ++ ++static int rt766_sdca_fu36_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, int event) ++{ ++ struct snd_soc_component *component = ++ snd_soc_dapm_to_component(w->dapm); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ ++ switch (event) { ++ case SND_SOC_DAPM_POST_PMU: ++ rt766->fu36_dapm_mute = false; ++ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU36); ++ break; ++ case SND_SOC_DAPM_PRE_PMD: ++ rt766->fu36_dapm_mute = true; ++ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_USER_FU36); ++ break; ++ } ++ return 0; ++} ++ ++static int rt766_sdca_pde34_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, int event) ++{ ++ return rt766_sdca_pde_event(w, kcontrol, event, ++ RT766_FUNC_NUM_UAJ, RT766_SDCA_ENT_PDE34, "PDE 34"); ++} ++ ++static int rt766_sdca_fu21_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, int event) ++{ ++ struct snd_soc_component *component = ++ snd_soc_dapm_to_component(w->dapm); ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ ++ switch (event) { ++ case SND_SOC_DAPM_POST_PMU: ++ rt766->fu21_dapm_mute = false; ++ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_AMP, RT766_SDCA_ENT_USER_FU21); ++ break; ++ case SND_SOC_DAPM_PRE_PMD: ++ rt766->fu21_dapm_mute = true; ++ rt766_sdca_set_fu_ctl(rt766, RT766_FUNC_NUM_AMP, RT766_SDCA_ENT_USER_FU21); ++ break; ++ } ++ return 0; ++} ++ ++static int rt766_sdca_pde23_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, int event) ++{ ++ return rt766_sdca_pde_event(w, kcontrol, event, ++ RT766_FUNC_NUM_AMP, RT766_SDCA_ENT_PDE23, "PDE 23"); ++} ++ ++static int rt766_sdca_pde11_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, int event) ++{ ++ return rt766_sdca_pde_event(w, kcontrol, event, ++ RT766_FUNC_NUM_MIC, RT766_SDCA_ENT_PDE11, "PDE 11"); ++} ++ ++static int rt766_dmic_fu_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ struct rt_sdca_dmic_kctrl_priv *p = ++ (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value; ++ ++ if (p->max == 1) ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; ++ else ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = p->count; ++ uinfo->value.integer.min = 0; ++ uinfo->value.integer.max = p->max; ++ return 0; ++} ++ ++static const char * const rt766_rx_data_ch_select[] = { ++ "L,R", ++ "R,L", ++ "L,L", ++ "R,R", ++ "L,L+R", ++ "R,L+R", ++ "L+R,L", ++ "L+R,R", ++ "L+R,L+R", ++}; ++ ++static SOC_ENUM_SINGLE_DECL(rt766_rx_data_ch_enum, ++ RT766_SDCA_CTL(AMP, PPU21, SDCA_CTL_PPU_POSTURENUMBER), 0, ++ rt766_rx_data_ch_select); ++ ++static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -9525, 75, 0); ++static const DECLARE_TLV_DB_SCALE(spk_vol_tlv, -6525, 75, 0); ++static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -1725, 75, 0); ++static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, -200, 200, 0); ++ ++#define RT766_P75DB_STEP 0xC0 /* 0.75 dB in Q7.8 format */ ++#define RT766_2DB_STEP 0x200 /* 2 dB in Q7.8 format */ ++#define RT766_HP_VOL_MIN (-127) /* -95.25 dB / 0.75 dB step */ ++#define RT766_HS_VOL_MIN (-23) /* -17.25 dB / 0.75 dB step */ ++#define RT766_HS_BOOST_VOL_MIN (-1) /* -2 dB / 2 dB step */ ++#define RT766_SPK_VOL_MIN (-87) /* -65.25 dB / 0.75 dB step */ ++#define RT766_P_VOL_MAX 0 /* 0 dB / 0.75 dB step */ ++#define RT766_HS_VOL_MAX 40 /* 30 dB / 0.75 dB step */ ++#define RT766_HS_BOOST_VOL_MAX 20 /* 40 dB / 2 dB step */ ++ ++static const struct snd_kcontrol_new rt766_sdca_controls[] = { ++ SOC_DOUBLE_EXT("FU41 Playback Switch", SND_SOC_NOPM, 0, 1, 1, 0, ++ rt766_sdca_fu41_playback_get, rt766_sdca_fu41_playback_put), ++ SDCA_DOUBLE_Q78_TLV("FU41 Playback Volume", ++ RT766_VOLUME_REG(UAJ, USER_FU41, 1), ++ RT766_VOLUME_REG(UAJ, USER_FU41, 2), ++ RT766_HP_VOL_MIN, RT766_P_VOL_MAX, RT766_P75DB_STEP, hp_vol_tlv), ++ SOC_DOUBLE_EXT("FU36 Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0, ++ rt766_sdca_fu36_capture_get, rt766_sdca_fu36_capture_put), ++ SDCA_DOUBLE_Q78_TLV("FU36 Capture Volume", ++ RT766_VOLUME_REG(UAJ, USER_FU36, 1), ++ RT766_VOLUME_REG(UAJ, USER_FU36, 2), ++ RT766_HS_VOL_MIN, RT766_HS_VOL_MAX, RT766_P75DB_STEP, mic_vol_tlv), ++ SDCA_DOUBLE_Q78_TLV("FU33 Boost Volume", ++ RT766_GAIN_REG(UAJ, PLATFORM_FU33, 1), ++ RT766_GAIN_REG(UAJ, PLATFORM_FU33, 2), ++ RT766_HS_BOOST_VOL_MIN, RT766_HS_BOOST_VOL_MAX, RT766_2DB_STEP, boost_vol_tlv), ++ ++ SOC_DOUBLE_EXT("FU21 Playback Switch", SND_SOC_NOPM, 0, 1, 1, 0, ++ rt766_sdca_fu21_playback_get, rt766_sdca_fu21_playback_put), ++ SDCA_DOUBLE_Q78_TLV("FU21 Playback Volume", ++ RT766_VOLUME_REG(AMP, USER_FU21, 1), ++ RT766_VOLUME_REG(AMP, USER_FU21, 2), ++ RT766_SPK_VOL_MIN, RT766_P_VOL_MAX, RT766_P75DB_STEP, spk_vol_tlv), ++ SOC_ENUM("RX Channel Select", rt766_rx_data_ch_enum), ++ ++ RT_SDCA_FU_CTRL("FU113 Capture Switch", ++ RT766_MUTE_REG(MIC, USER_FU113, 1), 1, 1, 4, rt766_dmic_fu_info, ++ rt766_sdca_dmic_fu113_capture_get, rt766_sdca_dmic_fu113_capture_put), ++ RT_SDCA_EXT_TLV("FU113 Capture Volume", ++ RT766_VOLUME_REG(MIC, USER_FU113, 1), ++ rt766_sdca_dmic_set_gain_get, rt766_sdca_dmic_set_gain_put, ++ 4, 0x3f, mic_vol_tlv, rt766_dmic_fu_info), ++}; ++ ++static const struct snd_soc_dapm_widget rt766_sdca_dapm_widgets[] = { ++ /* UAJ */ ++ SND_SOC_DAPM_OUTPUT("HP"), ++ SND_SOC_DAPM_INPUT("MIC2"), ++ SND_SOC_DAPM_SUPPLY("PDE 47", SND_SOC_NOPM, 0, 0, ++ rt766_sdca_pde47_event, ++ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), ++ SND_SOC_DAPM_SUPPLY("PDE 34", SND_SOC_NOPM, 0, 0, ++ rt766_sdca_pde34_event, ++ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), ++ SND_SOC_DAPM_DAC_E("FU 41", NULL, SND_SOC_NOPM, 0, 0, ++ rt766_sdca_fu41_event, ++ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), ++ SND_SOC_DAPM_ADC_E("FU 36", NULL, SND_SOC_NOPM, 0, 0, ++ rt766_sdca_fu36_event, ++ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), ++ SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_OUT("DP12TX", "DP12 Capture", 0, SND_SOC_NOPM, 0, 0), ++ ++ /* AMP */ ++ SND_SOC_DAPM_OUTPUT("SPOL"), ++ SND_SOC_DAPM_OUTPUT("SPOR"), ++ SND_SOC_DAPM_DAC_E("FU 21", NULL, SND_SOC_NOPM, 0, 0, ++ rt766_sdca_fu21_event, ++ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), ++ SND_SOC_DAPM_SUPPLY("PDE 23", SND_SOC_NOPM, 0, 0, ++ rt766_sdca_pde23_event, ++ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), ++ SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0), ++ ++ /* DMIC */ ++ SND_SOC_DAPM_INPUT("DMIC1"), ++ SND_SOC_DAPM_INPUT("DMIC2"), ++ SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0, ++ rt766_sdca_pde11_event, ++ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), ++ SND_SOC_DAPM_ADC_E("FU 113", NULL, SND_SOC_NOPM, 0, 0, ++ rt766_sdca_fu113_event, ++ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), ++ SND_SOC_DAPM_AIF_OUT("DP8TX", "DP8 Capture", 0, SND_SOC_NOPM, 0, 0), ++}; ++ ++static const struct snd_soc_dapm_route rt766_sdca_audio_map[] = { ++ { "FU 41", NULL, "DP3RX" }, ++ { "DP12TX", NULL, "FU 36" }, ++ { "FU 36", NULL, "PDE 34" }, ++ { "FU 36", NULL, "MIC2" }, ++ { "HP", NULL, "PDE 47" }, ++ { "HP", NULL, "FU 41" }, ++ ++ { "FU 21", NULL, "DP1RX" }, ++ { "FU 21", NULL, "PDE 23" }, ++ { "SPOL", NULL, "FU 21" }, ++ { "SPOR", NULL, "FU 21" }, ++ ++ {"DP8TX", NULL, "FU 113"}, ++ {"FU 113", NULL, "PDE 11"}, ++ {"FU 113", NULL, "DMIC1"}, ++ {"FU 113", NULL, "DMIC2"}, ++}; ++ ++static int rt766_sdca_probe(struct snd_soc_component *component) ++{ ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ struct device *dev = &rt766->slave->dev; ++ int ret; ++ ++ rt766->component = component; ++ ++ ret = pm_runtime_resume(component->dev); ++ if (ret < 0 && ret != -EACCES) ++ return ret; ++ ++ if (rt766->uaj_func_data) { ++ dev_dbg(dev, "%s : irq %d\n", __func__, rt766->slave->irq); ++ ++ rt766->irq_info = devm_sdca_irq_allocate(dev, rt766->regmap, rt766->slave->irq); ++ if (IS_ERR(rt766->irq_info)) ++ return PTR_ERR(rt766->irq_info); ++ ++ ret = rt766_sdca_irq_ctl(rt766, rt766->uaj_func_data, ++ component, rt766->irq_info, true); ++ if (ret < 0) { ++ dev_err(dev, "Failed to request UAJ SDCA IRQ: %d\n", ret); ++ return ret; ++ } ++ ++ if (rt766->hid_func_data) { ++ ret = rt766_sdca_irq_ctl(rt766, rt766->hid_func_data, ++ component, rt766->irq_info, true); ++ if (ret < 0) { ++ dev_err(dev, "Failed to request HID SDCA IRQ: %d\n", ret); ++ return ret; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++static void rt766_sdca_remove(struct snd_soc_component *component) ++{ ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ ++ sdca_irq_cleanup_late(component->dev, rt766->uaj_func_data, rt766->irq_info); ++ sdca_irq_cleanup_late(component->dev, rt766->hid_func_data, rt766->irq_info); ++} ++ ++static const struct snd_soc_component_driver soc_sdca_dev_rt766 = { ++ .probe = rt766_sdca_probe, ++ .remove = rt766_sdca_remove, ++ .controls = rt766_sdca_controls, ++ .num_controls = ARRAY_SIZE(rt766_sdca_controls), ++ .dapm_widgets = rt766_sdca_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(rt766_sdca_dapm_widgets), ++ .dapm_routes = rt766_sdca_audio_map, ++ .num_dapm_routes = ARRAY_SIZE(rt766_sdca_audio_map), ++ .set_jack = rt766_sdca_set_jack_detect, ++ .endianness = 1, ++}; ++ ++static int rt766_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, ++ int direction) ++{ ++ snd_soc_dai_dma_data_set(dai, direction, sdw_stream); ++ ++ return 0; ++} ++ ++static void rt766_sdca_shutdown(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ snd_soc_dai_set_dma_data(dai, substream, NULL); ++} ++ ++static int rt766_sdca_pcm_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ struct sdw_stream_config stream_config; ++ struct sdw_port_config port_config; ++ enum sdw_data_direction direction; ++ struct sdw_stream_runtime *sdw_stream; ++ unsigned int sampling_rate; ++ int retval, port; ++ ++ dev_dbg(dai->dev, "%s %s id %d", __func__, dai->name, dai->id); ++ sdw_stream = snd_soc_dai_get_dma_data(dai, substream); ++ ++ if (!sdw_stream) ++ return -EINVAL; ++ ++ if (!rt766->slave) ++ return -EINVAL; ++ ++ /* SoundWire specific configuration */ ++ snd_sdw_params_to_config(substream, params, &stream_config, &port_config); ++ ++ /* SoundWire specific configuration */ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ direction = SDW_DATA_DIR_RX; ++ if (dai->id == RT766_AIF1) ++ port = 3; ++ else if (dai->id == RT766_AIF2) ++ port = 1; ++ else ++ return -EINVAL; ++ } else { ++ direction = SDW_DATA_DIR_TX; ++ if (dai->id == RT766_AIF1) ++ port = 12; ++ else if (dai->id == RT766_AIF3) ++ port = 8; ++ else ++ return -EINVAL; ++ } ++ ++ port_config.num = port; ++ retval = sdw_stream_add_slave(rt766->slave, &stream_config, ++ &port_config, 1, sdw_stream); ++ if (retval) { ++ dev_err(dai->dev, "%s: Unable to configure port\n", __func__); ++ return retval; ++ } ++ ++ if (params_channels(params) > 16) { ++ dev_err(component->dev, "%s: Unsupported channels %d\n", ++ __func__, params_channels(params)); ++ return -EINVAL; ++ } ++ ++ /* sampling rate configuration */ ++ switch (params_rate(params)) { ++ case 44100: ++ sampling_rate = RT766_SDCA_RATE_44100HZ; ++ break; ++ case 48000: ++ sampling_rate = RT766_SDCA_RATE_48000HZ; ++ break; ++ case 96000: ++ sampling_rate = RT766_SDCA_RATE_96000HZ; ++ break; ++ case 192000: ++ sampling_rate = RT766_SDCA_RATE_192000HZ; ++ break; ++ default: ++ dev_err(component->dev, "%s: Rate %d is not supported\n", ++ __func__, params_rate(params)); ++ return -EINVAL; ++ } ++ ++ /* set sampling frequency */ ++ switch (dai->id) { ++ case RT766_AIF1: ++ regmap_write(rt766->regmap, ++ RT766_SDCA_CTL(UAJ, CS41, SDCA_CTL_CS_SAMPLERATEINDEX), ++ sampling_rate); ++ regmap_write(rt766->regmap, ++ RT766_SDCA_CTL(UAJ, CS36, SDCA_CTL_CS_SAMPLERATEINDEX), ++ sampling_rate); ++ break; ++ case RT766_AIF2: ++ regmap_write(rt766->regmap, ++ RT766_SDCA_CTL(AMP, CS21, SDCA_CTL_CS_SAMPLERATEINDEX), ++ sampling_rate); ++ break; ++ case RT766_AIF3: ++ regmap_write(rt766->regmap, ++ RT766_SDCA_CTL(MIC, CS113, SDCA_CTL_CS_SAMPLERATEINDEX), ++ sampling_rate); ++ break; ++ default: ++ dev_err(component->dev, "%s: Wrong DAI id\n", __func__); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int rt766_sdca_pcm_hw_free(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_component *component = dai->component; ++ struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); ++ struct sdw_stream_runtime *sdw_stream = ++ snd_soc_dai_get_dma_data(dai, substream); ++ ++ if (!rt766->slave) ++ return -EINVAL; ++ ++ sdw_stream_remove_slave(rt766->slave, sdw_stream); ++ return 0; ++} ++ ++#define RT766_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \ ++ SNDRV_PCM_RATE_192000) ++#define RT766_DAC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) ++#define RT766_ADC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ ++ SNDRV_PCM_FMTBIT_S32_LE) ++ ++static const struct snd_soc_dai_ops rt766_sdca_ops = { ++ .hw_params = rt766_sdca_pcm_hw_params, ++ .hw_free = rt766_sdca_pcm_hw_free, ++ .set_stream = rt766_sdca_set_sdw_stream, ++ .shutdown = rt766_sdca_shutdown, ++}; ++ ++static struct snd_soc_dai_driver rt766_sdca_dai[] = { ++ { ++ .name = "rt766-sdca-aif1", ++ .id = RT766_AIF1, ++ .playback = { ++ .stream_name = "DP3 Playback", ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = RT766_STEREO_RATES, ++ .formats = RT766_DAC_FORMATS, ++ }, ++ .capture = { ++ .stream_name = "DP12 Capture", ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = RT766_STEREO_RATES, ++ .formats = RT766_ADC_FORMATS, ++ }, ++ .ops = &rt766_sdca_ops, ++ .symmetric_rate = 1, ++ }, ++ { ++ .name = "rt766-sdca-aif2", ++ .id = RT766_AIF2, ++ .playback = { ++ .stream_name = "DP1 Playback", ++ .channels_min = 1, ++ .channels_max = 4, ++ .rates = RT766_STEREO_RATES, ++ .formats = RT766_DAC_FORMATS, ++ }, ++ .ops = &rt766_sdca_ops, ++ }, ++ { ++ .name = "rt766-sdca-aif3", ++ .id = RT766_AIF3, ++ .capture = { ++ .stream_name = "DP8 Capture", ++ .channels_min = 1, ++ .channels_max = 4, ++ .rates = RT766_STEREO_RATES, ++ .formats = RT766_ADC_FORMATS, ++ }, ++ .ops = &rt766_sdca_ops, ++ } ++}; ++ ++static unsigned int rt766_find_dt_rates(struct device *dev, struct sdca_function_data *function, ++ const char *label) ++{ ++ struct snd_soc_pcm_stream stream; ++ struct sdca_entity *entity; ++ int i, ret; ++ ++ for (i = 0; i < function->num_entities; i++) { ++ entity = &function->entities[i]; ++ ++ if (strcmp(entity->label, label)) ++ continue; ++ ++ /* Can't check earlier as only terminals have an iot member. */ ++ if (!entity->iot.is_dataport) ++ continue; ++ ++ ret = sdca_asoc_populate_rate_format(dev, function, entity, &stream); ++ if (ret < 0) { ++ dev_dbg(dev, "%s: failed to parse rates for entity %s\n", ++ __func__, entity->label); ++ return 0; ++ } ++ ++ dev_dbg(dev, "%s: %s supports rates 0x%08x\n", __func__, entity->label, stream.rates); ++ } ++ ++ return stream.rates; ++} ++ ++int rt766_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave *slave) ++{ ++ struct sdca_function_data *func_data_ptr; ++ struct snd_soc_dai_driver *dai_drv; ++ struct rt766_sdca_priv *rt766; ++ unsigned int rates; ++ int ret; ++ int i; ++ ++ rt766 = devm_kzalloc(dev, sizeof(*rt766), GFP_KERNEL); ++ if (!rt766) ++ return -ENOMEM; ++ ++ dev_set_drvdata(dev, rt766); ++ rt766->slave = slave; ++ rt766->regmap = regmap; ++ ++ regcache_cache_only(rt766->regmap, true); ++ ++ ret = devm_mutex_init(dev, &rt766->disable_irq_lock); ++ if (ret < 0) { ++ dev_err(dev, "Failed to initialize mutex\n"); ++ return ret; ++ } ++ ++ /* ++ * Mark hw_init to false ++ * HW init will be performed when device reports present ++ */ ++ rt766->hw_init = false; ++ rt766->first_hw_init = false; ++ rt766->fu41_dapm_mute = true; ++ rt766->fu41_mixer_l_mute = rt766->fu41_mixer_r_mute = false; ++ rt766->fu36_dapm_mute = true; ++ rt766->fu36_mixer_l_mute = rt766->fu36_mixer_r_mute = true; ++ rt766->fu21_dapm_mute = true; ++ rt766->fu21_mixer_l_mute = rt766->fu21_mixer_r_mute = false; ++ rt766->fu113_dapm_mute = true; ++ rt766->fu113_mixer_mute[0] = rt766->fu113_mixer_mute[1] = ++ rt766->fu113_mixer_mute[2] = rt766->fu113_mixer_mute[3] = true; ++ ++ dai_drv = devm_kzalloc(dev, sizeof(struct snd_soc_dai_driver) * ARRAY_SIZE(rt766_sdca_dai), GFP_KERNEL); ++ if (!dai_drv) { ++ dev_err(dev, "Failed to allocate memory for DAI driver\n"); ++ ret = -ENOMEM; ++ goto _sdw_init_err_; ++ } ++ memcpy(dai_drv, rt766_sdca_dai, sizeof(struct snd_soc_dai_driver) * ARRAY_SIZE(rt766_sdca_dai)); ++ ++ /* get SDCA function data */ ++ dev_dbg(dev, "SDCA functions found: %d", slave->sdca_data.num_functions); ++ for (i = 0; i < slave->sdca_data.num_functions; i++) { ++ func_data_ptr = devm_kzalloc(dev, sizeof(*func_data_ptr), GFP_KERNEL); ++ if (!func_data_ptr) { ++ dev_err(dev, "Failed to allocate memory for function data\n"); ++ ret = -ENOMEM; ++ goto _free_dai_drv_; ++ } ++ ++ func_data_ptr->desc = &slave->sdca_data.function[i]; ++ ret = sdca_parse_function(dev, func_data_ptr); ++ if (ret) { ++ devm_kfree(dev, func_data_ptr); ++ goto _free_dai_drv_; ++ } ++ dev_dbg(dev, "Function type=%d, num_entities=%d", ++ slave->sdca_data.function[i].type, func_data_ptr->num_entities); ++ ++ switch (slave->sdca_data.function[i].type) { ++ case SDCA_FUNCTION_TYPE_UAJ: ++ rt766->uaj_func_data = func_data_ptr; ++ /* ++ * Some machines may only support a subset of the sample rates supported by the codec. ++ * Therefore, we need to parse the supported sample rates from the DisCo table and ++ * configure them in the DAI. If the DisCo table does not provide sample rate information, ++ * we will fall back to the default supported rates defined in the codec driver. ++ */ ++ rates = rt766_find_dt_rates(dev, func_data_ptr, "IT 41"); ++ if (rates) ++ dai_drv[RT766_DAI_UAJ].playback.rates = rates; ++ ++ rates = rt766_find_dt_rates(dev, func_data_ptr, "OT 36"); ++ if (rates) ++ dai_drv[RT766_DAI_UAJ].capture.rates = rates; ++ break; ++ case SDCA_FUNCTION_TYPE_SMART_AMP: ++ rt766->sa_func_data = func_data_ptr; ++ rates = rt766_find_dt_rates(dev, func_data_ptr, "IT 21"); ++ if (rates) ++ dai_drv[RT766_DAI_AMP].playback.rates = rates; ++ break; ++ case SDCA_FUNCTION_TYPE_SMART_MIC: ++ rt766->sm_func_data = func_data_ptr; ++ rates = rt766_find_dt_rates(dev, func_data_ptr, "OT 113"); ++ if (rates) ++ dai_drv[RT766_DAI_MIC].capture.rates = rates; ++ break; ++ case SDCA_FUNCTION_TYPE_HID: ++ rt766->hid_func_data = func_data_ptr; ++ break; ++ default: ++ dev_dbg(dev, "Unexpected SDCA function type found: %d", ++ slave->sdca_data.function[i].type); ++ } ++ } ++ ++ ret = devm_snd_soc_register_component(dev, ++ &soc_sdca_dev_rt766, dai_drv, ARRAY_SIZE(rt766_sdca_dai)); ++ if (ret < 0) ++ goto _free_dai_drv_; ++ ++ /* set autosuspend parameters */ ++ pm_runtime_set_autosuspend_delay(dev, 3000); ++ pm_runtime_use_autosuspend(dev); ++ ++ /* make sure the device does not suspend immediately */ ++ pm_runtime_mark_last_busy(dev); ++ ++ pm_runtime_enable(dev); ++ dev_dbg(dev, "%s\n", __func__); ++ return 0; ++ ++_free_dai_drv_: ++ if (dai_drv) ++ devm_kfree(dev, dai_drv); ++ ++_sdw_init_err_: ++ return ret; ++} ++ ++static int rt766_func_initialize(struct rt766_sdca_priv *rt766, struct sdca_function_data *func_data) ++{ ++ struct device *dev = &rt766->slave->dev; ++ unsigned int func_status_reg; ++ unsigned int func_status; ++ int ret; ++ ++ switch (func_data->desc->type) { ++ case SDCA_FUNCTION_TYPE_UAJ: ++ func_status_reg = RT766_FUNC_STATUS_REG(UAJ); ++ break; ++ case SDCA_FUNCTION_TYPE_SMART_AMP: ++ func_status_reg = RT766_FUNC_STATUS_REG(AMP); ++ break; ++ case SDCA_FUNCTION_TYPE_SMART_MIC: ++ func_status_reg = RT766_FUNC_STATUS_REG(MIC); ++ break; ++ case SDCA_FUNCTION_TYPE_HID: ++ func_status_reg = RT766_FUNC_STATUS_REG(HID); ++ break; ++ default: ++ dev_dbg(dev, "Unexpected SDCA function type found: %d", ++ func_data->desc->type); ++ return -EINVAL; ++ } ++ ++ regmap_read(rt766->regmap, func_status_reg, &func_status); ++ dev_dbg(dev, "%s, %s func_status=0x%x\n", __func__, func_data->desc->name, func_status); ++ ++ if ((func_status & SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION) || (!rt766->first_hw_init)) { ++ ret = sdca_regmap_write_init(dev, rt766->regmap, func_data); ++ if (ret) { ++ dev_err(dev, "%s initialization table update failed\n", func_data->desc->name); ++ goto _func_init_err_; ++ } ++ ++ regmap_write(rt766->regmap, func_status_reg, ++ SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION); ++ } ++ ++ return 0; ++ ++_func_init_err_: ++ dev_err(dev, "%s: %s init writes failed, err=%d", __func__, func_data->desc->name, ret); ++ return ret; ++} ++ ++int rt766_sdca_io_init(struct device *dev, struct sdw_slave *slave) ++{ ++ struct rt766_sdca_priv *rt766 = dev_get_drvdata(dev); ++ unsigned int val; ++ ++ rt766->disable_irq = false; ++ ++ if (rt766->hw_init) ++ return 0; ++ ++ regcache_cache_only(rt766->regmap, false); ++ if (rt766->first_hw_init) { ++ regcache_cache_bypass(rt766->regmap, true); ++ } else { ++ /* ++ * PM runtime status is marked as 'active' only when a Slave reports as Attached ++ */ ++ ++ /* update count of parent 'active' children */ ++ pm_runtime_set_active(&slave->dev); ++ } ++ ++ pm_runtime_get_noresume(&slave->dev); ++ ++ regmap_read(rt766->regmap, RT766_BOND_LATCH_ID, &val); ++ dev_dbg(&slave->dev, "%s bond ID=0x%x (%s)\n", __func__, val, (val == 0x1) ? "RT767" : "RT766"); ++ ++ /* check function status and initialize if needed */ ++ if (rt766->uaj_func_data) ++ rt766_func_initialize(rt766, rt766->uaj_func_data); ++ if (rt766->sa_func_data) ++ rt766_func_initialize(rt766, rt766->sa_func_data); ++ if (rt766->sm_func_data) ++ rt766_func_initialize(rt766, rt766->sm_func_data); ++ if (rt766->hid_func_data) ++ rt766_func_initialize(rt766, rt766->hid_func_data); ++ ++ if (rt766->first_hw_init) { ++ regcache_cache_bypass(rt766->regmap, false); ++ regcache_mark_dirty(rt766->regmap); ++ } else { ++ rt766->first_hw_init = true; ++ } ++ ++ /* Mark Slave initialization complete */ ++ rt766->hw_init = true; ++ ++ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); ++ ++ pm_runtime_put_autosuspend(&slave->dev); ++ ++ return 0; ++} ++ ++MODULE_DESCRIPTION("ASoC RT766 SDCA SDW driver"); ++MODULE_AUTHOR("Shuming Fan "); ++MODULE_LICENSE("GPL"); ++MODULE_IMPORT_NS("SND_SOC_SDCA"); +diff --git a/sound/soc/codecs/rt766-sdca.h b/sound/soc/codecs/rt766-sdca.h +new file mode 100644 +--- /dev/null ++++ b/sound/soc/codecs/rt766-sdca.h +@@ -0,0 +1,140 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * rt766-sdca.h -- RT766 SDCA ALSA SoC audio driver header ++ * ++ * Copyright(c) 2026 Realtek Semiconductor Corp. ++ */ ++ ++#ifndef __RT766_H__ ++#define __RT766_H__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct rt766_sdca_priv { ++ struct regmap *regmap; ++ struct snd_soc_component *component; ++ struct sdw_slave *slave; ++ bool hw_init; ++ bool first_hw_init; ++ struct snd_soc_jack *hs_jack; ++ struct mutex disable_irq_lock; /* SDCA irq lock protection */ ++ bool disable_irq; ++ int jack_type; ++ bool fu41_dapm_mute; ++ bool fu41_mixer_l_mute; ++ bool fu41_mixer_r_mute; ++ bool fu113_dapm_mute; ++ bool fu113_mixer_mute[4]; ++ bool fu21_dapm_mute; ++ bool fu21_mixer_l_mute; ++ bool fu21_mixer_r_mute; ++ bool fu36_dapm_mute; ++ bool fu36_mixer_l_mute; ++ bool fu36_mixer_r_mute; ++ struct sdca_function_data *uaj_func_data; ++ struct sdca_function_data *sm_func_data; ++ struct sdca_function_data *sa_func_data; ++ struct sdca_function_data *hid_func_data; ++ struct sdca_interrupt_info *irq_info; ++ struct hid_device *hid; ++}; ++ ++/* vendor registers */ ++#define RT766_VERSION_ID 0xc404 ++#define RT766_DEV_ID1 0xc405 ++#define RT766_DEV_ID0 0xc406 ++#define RT766_BOND_LATCH_ID 0xc407 ++ ++#define RT766_HP_POWER_STATE 0x1000004 ++#define RT766_HP_FSM_CTL2_1 0x100000d ++ ++/* MCU Patch address */ ++#define RT766_MCU_PATCH_ADDR1_START 0x10010000 ++#define RT766_MCU_PATCH_ADDR1_END 0x10011fff ++#define RT766_MCU_PATCH_ADDR2_START 0x10020000 ++#define RT766_MCU_PATCH_ADDR2_END 0x10023fff ++ ++/* Buffer address for HID */ ++#define RT766_BUF_ADDR_HID1 0x44030000 ++#define RT766_BUF_ADDR_HID2 0x44030020 ++ ++/* SDCA (Channel) */ ++#define RT766_CH_1 0x01 ++#define RT766_CH_2 0x02 ++#define RT766_CH_3 0x03 ++#define RT766_CH_4 0x04 ++ ++/* RT766 SDCA Control - function number */ ++#define RT766_FUNC_NUM_UAJ 0x01 ++#define RT766_FUNC_NUM_MIC 0x02 ++#define RT766_FUNC_NUM_HID 0x03 ++#define RT766_FUNC_NUM_AMP 0x04 ++ ++/* RT766 SDCA entity */ ++#define RT766_SDCA_ENT_0 0x00 ++#define RT766_SDCA_ENT_HID101 0x01 ++#define RT766_SDCA_ENT_GE49 0x49 ++#define RT766_SDCA_ENT_USER_FU41 0x05 ++#define RT766_SDCA_ENT_USER_FU36 0x0f ++#define RT766_SDCA_ENT_USER_FU21 0x03 ++#define RT766_SDCA_ENT_USER_FU113 0x30 ++#define RT766_SDCA_ENT_PDE23 0x33 ++#define RT766_SDCA_ENT_PDE47 0x28 ++#define RT766_SDCA_ENT_PDE11 0x2a ++#define RT766_SDCA_ENT_PDE34 0x29 ++#define RT766_SDCA_ENT_CS41 0x01 ++#define RT766_SDCA_ENT_CS36 0x11 ++#define RT766_SDCA_ENT_CS113 0x12 ++#define RT766_SDCA_ENT_CS21 0x21 ++#define RT766_SDCA_ENT_PLATFORM_FU33 0x44 ++#define RT766_SDCA_ENT_PPU21 0x04 ++ ++/* sample frequency index */ ++#define RT766_SDCA_RATE_44100HZ 0x08 ++#define RT766_SDCA_RATE_48000HZ 0x09 ++#define RT766_SDCA_RATE_96000HZ 0x0b ++#define RT766_SDCA_RATE_192000HZ 0x0d ++ ++/* SDCA Register macros */ ++#define RT766_MUTE_REG(func, fu, ch) \ ++ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##fu, SDCA_CTL_FU_MUTE, RT766_CH_##ch) ++ ++#define RT766_VOLUME_REG(func, fu, ch) \ ++ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##fu, SDCA_CTL_FU_CHANNEL_VOLUME, RT766_CH_##ch) ++ ++#define RT766_GAIN_REG(func, fu, ch) \ ++ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##fu, SDCA_CTL_FU_GAIN, RT766_CH_##ch) ++ ++#define RT766_PDE_REQ_REG(func, pde) \ ++ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##pde, SDCA_CTL_PDE_REQUESTED_PS, 0) ++ ++#define RT766_PDE_ACTUAL_REG(func, pde) \ ++ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##pde, SDCA_CTL_PDE_ACTUAL_PS, 0) ++ ++#define RT766_FUNC_STATUS_REG(func) \ ++ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_0, SDCA_CTL_ENTITY_0_FUNCTION_STATUS, 0) ++ ++#define RT766_SDCA_CTL(func, ent, ctl) \ ++ SDW_SDCA_CTL(RT766_FUNC_NUM_##func, RT766_SDCA_ENT_##ent, ctl, 0) ++ ++enum { ++ RT766_AIF1, ++ RT766_AIF2, ++ RT766_AIF3, ++}; ++ ++enum { ++ RT766_DAI_UAJ, ++ RT766_DAI_AMP, ++ RT766_DAI_MIC, ++}; ++ ++int rt766_sdca_io_init(struct device *dev, struct sdw_slave *slave); ++int rt766_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave *slave); ++#endif /* __RT766_H__ */ +diff --git a/sound/soc/codecs/rt9120.c b/sound/soc/codecs/rt9120.c +--- a/sound/soc/codecs/rt9120.c ++++ b/sound/soc/codecs/rt9120.c +@@ -606,12 +606,19 @@ static int rt9120_runtime_suspend(struct device *dev) + static int rt9120_runtime_resume(struct device *dev) + { + struct rt9120_data *data = dev_get_drvdata(dev); ++ int ret; + + if (data->pwdnn_gpio) { + gpiod_set_value(data->pwdnn_gpio, 1); + msleep(RT9120_CHIPON_WAITMS); + regcache_cache_only(data->regmap, false); +- regcache_sync(data->regmap); ++ ret = regcache_sync(data->regmap); ++ if (ret) { ++ regcache_cache_only(data->regmap, true); ++ regcache_mark_dirty(data->regmap); ++ gpiod_set_value(data->pwdnn_gpio, 0); ++ return ret; ++ } + } + + return 0; +diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c +--- a/sound/soc/codecs/sigmadsp.c ++++ b/sound/soc/codecs/sigmadsp.c +@@ -5,6 +5,7 @@ + * Copyright 2009-2014 Analog Devices Inc. + */ + ++#include + #include + #include + #include +@@ -135,7 +136,7 @@ static int sigmadsp_ctrl_put(struct snd_kcontrol *kcontrol, + uint8_t *data; + int ret = 0; + +- mutex_lock(&sigmadsp->lock); ++ guard(mutex)(&sigmadsp->lock); + + data = ucontrol->value.bytes.data; + +@@ -148,8 +149,6 @@ static int sigmadsp_ctrl_put(struct snd_kcontrol *kcontrol, + ctrl->cached = true; + } + +- mutex_unlock(&sigmadsp->lock); +- + return ret; + } + +@@ -160,7 +159,7 @@ static int sigmadsp_ctrl_get(struct snd_kcontrol *kcontrol, + struct sigmadsp *sigmadsp = snd_kcontrol_chip(kcontrol); + int ret = 0; + +- mutex_lock(&sigmadsp->lock); ++ guard(mutex)(&sigmadsp->lock); + + if (!ctrl->cached) { + ret = sigmadsp_read(sigmadsp, ctrl->addr, ctrl->cache, +@@ -174,8 +173,6 @@ static int sigmadsp_ctrl_get(struct snd_kcontrol *kcontrol, + ctrl->num_bytes); + } + +- mutex_unlock(&sigmadsp->lock); +- + return ret; + } + +@@ -487,7 +484,7 @@ static void devm_sigmadsp_release(struct device *dev, void *res) + static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) + { + const struct sigma_firmware_header *ssfw_head; +- const struct firmware *fw; ++ const struct firmware *fw __free(firmware) = NULL; + int ret; + u32 crc; + +@@ -495,7 +492,7 @@ static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) + ret = request_firmware(&fw, name, sigmadsp->dev); + if (ret) { + pr_debug("%s: request_firmware() failed with %i\n", __func__, ret); +- goto done; ++ return ret; + } + + /* then verify the header */ +@@ -509,13 +506,13 @@ static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) + */ + if (fw->size < sizeof(*ssfw_head) || fw->size >= 0x4000000) { + dev_err(sigmadsp->dev, "Failed to load firmware: Invalid size\n"); +- goto done; ++ return -EINVAL; + } + + ssfw_head = (void *)fw->data; + if (memcmp(ssfw_head->magic, SIGMA_MAGIC, ARRAY_SIZE(ssfw_head->magic))) { + dev_err(sigmadsp->dev, "Failed to load firmware: Invalid magic\n"); +- goto done; ++ return -EINVAL; + } + + crc = crc32(0, fw->data + sizeof(*ssfw_head), +@@ -524,7 +521,7 @@ static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) + if (crc != le32_to_cpu(ssfw_head->crc)) { + dev_err(sigmadsp->dev, "Failed to load firmware: Wrong crc checksum: expected %x got %x\n", + le32_to_cpu(ssfw_head->crc), crc); +- goto done; ++ return -EINVAL; + } + + switch (ssfw_head->version) { +@@ -545,9 +542,6 @@ static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) + if (ret) + sigmadsp_firmware_release(sigmadsp); + +-done: +- release_firmware(fw); +- + return ret; + } + +@@ -677,10 +671,10 @@ static void sigmadsp_activate_ctrl(struct sigmadsp *sigmadsp, + return; + changed = snd_ctl_activate_id(card, &ctrl->kcontrol->id, active); + if (active && changed > 0) { +- mutex_lock(&sigmadsp->lock); +- if (ctrl->cached) +- sigmadsp_ctrl_write(sigmadsp, ctrl, ctrl->cache); +- mutex_unlock(&sigmadsp->lock); ++ scoped_guard(mutex, &sigmadsp->lock) { ++ if (ctrl->cached) ++ sigmadsp_ctrl_write(sigmadsp, ctrl, ctrl->cache); ++ } + } + } + +diff --git a/sound/soc/codecs/sma1303.c b/sound/soc/codecs/sma1303.c +--- a/sound/soc/codecs/sma1303.c ++++ b/sound/soc/codecs/sma1303.c +@@ -1777,6 +1777,8 @@ static void sma1303_i2c_remove(struct i2c_client *client) + struct sma1303_priv *sma1303 = + (struct sma1303_priv *) i2c_get_clientdata(client); + ++ if (sma1303->attr_grp) ++ sysfs_remove_group(sma1303->kobj, sma1303->attr_grp); + cancel_delayed_work_sync(&sma1303->check_fault_work); + } + +diff --git a/sound/soc/codecs/sma1307.c b/sound/soc/codecs/sma1307.c +--- a/sound/soc/codecs/sma1307.c ++++ b/sound/soc/codecs/sma1307.c +@@ -1690,7 +1690,7 @@ static void sma1307_check_fault_worker(struct work_struct *work) + + static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *file) + { +- const struct firmware *fw; ++ const struct firmware *fw __free(firmware) = NULL; + int size, offset, num_mode; + int ret; + +@@ -1703,22 +1703,18 @@ static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *fil + return; + } else if ((fw->size) < SMA1307_SETTING_HEADER_SIZE) { + dev_err(sma1307->dev, "%s: Invalid file\n", __func__); +- release_firmware(fw); + sma1307->set.status = false; + return; + } + + int *data __free(kfree) = kzalloc(fw->size, GFP_KERNEL); + if (!data) { +- release_firmware(fw); + sma1307->set.status = false; + return; + } + size = fw->size >> 2; + memcpy(data, fw->data, fw->size); + +- release_firmware(fw); +- + /* HEADER */ + sma1307->set.header_size = SMA1307_SETTING_HEADER_SIZE; + sma1307->set.checksum = data[sma1307->set.header_size - 2]; +diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c +--- a/sound/soc/codecs/sta32x.c ++++ b/sound/soc/codecs/sta32x.c +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -269,9 +270,9 @@ static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol, + int numcoef = kcontrol->private_value >> 16; + int index = kcontrol->private_value & 0xffff; + unsigned int cfud, val; +- int i, ret = 0; ++ int i; + +- mutex_lock(&sta32x->coeff_lock); ++ guard(mutex)(&sta32x->coeff_lock); + + /* preserve reserved bits in STA32X_CFUD */ + regmap_read(sta32x->regmap, STA32X_CFUD, &cfud); +@@ -283,24 +284,20 @@ static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol, + regmap_write(sta32x->regmap, STA32X_CFUD, cfud); + + regmap_write(sta32x->regmap, STA32X_CFADDR2, index); +- if (numcoef == 1) { ++ if (numcoef == 1) + regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x04); +- } else if (numcoef == 5) { ++ else if (numcoef == 5) + regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x08); +- } else { +- ret = -EINVAL; +- goto exit_unlock; +- } ++ else ++ return -EINVAL; ++ + + for (i = 0; i < 3 * numcoef; i++) { + regmap_read(sta32x->regmap, STA32X_B1CF1 + i, &val); + ucontrol->value.bytes.data[i] = val; + } + +-exit_unlock: +- mutex_unlock(&sta32x->coeff_lock); +- +- return ret; ++ return 0; + } + + static int sta32x_coefficient_put(struct snd_kcontrol *kcontrol, +diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c +--- a/sound/soc/codecs/sta350.c ++++ b/sound/soc/codecs/sta350.c +@@ -16,6 +16,7 @@ + + #define pr_fmt(fmt) KBUILD_MODNAME ":%s:%d: " fmt, __func__, __LINE__ + ++#include + #include + #include + #include +@@ -306,9 +307,9 @@ static int sta350_coefficient_get(struct snd_kcontrol *kcontrol, + int numcoef = kcontrol->private_value >> 16; + int index = kcontrol->private_value & 0xffff; + unsigned int cfud, val; +- int i, ret = 0; ++ int i; + +- mutex_lock(&sta350->coeff_lock); ++ guard(mutex)(&sta350->coeff_lock); + + /* preserve reserved bits in STA350_CFUD */ + regmap_read(sta350->regmap, STA350_CFUD, &cfud); +@@ -320,24 +321,19 @@ static int sta350_coefficient_get(struct snd_kcontrol *kcontrol, + regmap_write(sta350->regmap, STA350_CFUD, cfud); + + regmap_write(sta350->regmap, STA350_CFADDR2, index); +- if (numcoef == 1) { ++ if (numcoef == 1) + regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x04); +- } else if (numcoef == 5) { ++ else if (numcoef == 5) + regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x08); +- } else { +- ret = -EINVAL; +- goto exit_unlock; +- } ++ else ++ return -EINVAL; + + for (i = 0; i < 3 * numcoef; i++) { + regmap_read(sta350->regmap, STA350_B1CF1 + i, &val); + ucontrol->value.bytes.data[i] = val; + } + +-exit_unlock: +- mutex_unlock(&sta350->coeff_lock); +- +- return ret; ++ return 0; + } + + static int sta350_coefficient_put(struct snd_kcontrol *kcontrol, +diff --git a/sound/soc/codecs/tac5xx2-sdw.c b/sound/soc/codecs/tac5xx2-sdw.c +--- a/sound/soc/codecs/tac5xx2-sdw.c ++++ b/sound/soc/codecs/tac5xx2-sdw.c +@@ -88,6 +88,9 @@ + #define TAC_FW_FILE_HDR 20 + #define TAC_MAX_FW_CHUNKS 512 + ++#define TAC_UAJ_PREP_CONNECTED 0xff ++#define TAC_UAJ_PREP_DISCONNECTED 0xdf ++ + struct tac_fw_hdr { + u32 size; + u32 version_offset; +@@ -138,6 +141,7 @@ struct tac5xx2_prv { + bool hw_init; + bool first_hw_init_done; + u32 part_id; ++ u32 rev_id; + struct snd_soc_jack *hs_jack; + int jack_type; + /* Custom fw binary. UMP File Download is not used. */ +@@ -261,7 +265,6 @@ static const struct reg_default tac_reg_default[] = { + {TAC_REG_SDW(0, 0, 0x78), 0x0}, + {TAC_REG_SDW(0, 0, 0x7b), 0x0}, + {TAC_REG_SDW(0, 0, 0x7c), 0xd0}, +- {TAC_REG_SDW(0, 0, 0x7d), 0x0}, + {TAC_REG_SDW(0, 0, 0x7e), 0x0}, + {TAC_REG_SDW(0, 1, 0x1), 0x0}, + {TAC_REG_SDW(0, 1, 0x2), 0x0}, +@@ -507,18 +510,6 @@ static const struct regmap_config tac_regmap = { + .use_single_write = true, + }; + +-/* Check if device has DSP algo that needs status monitoring */ +-static bool tac_has_dsp_algo(struct tac5xx2_prv *tac_dev) +-{ +- switch (tac_dev->part_id) { +- case 0x5682: +- case 0x2883: +- return true; +- default: +- return false; +- } +-} +- + /* Check if device has UAJ (Universal Audio Jack) support */ + static bool tac_has_uaj_support(struct tac5xx2_prv *tac_dev) + { +@@ -800,7 +791,7 @@ static int tac_sdw_hw_params(struct snd_pcm_substream *substream, + return ret; + } + +- ret = sdca_asoc_pde_poll_actual_ps(tac_dev->dev, tac_dev->regmap, function_id, pde_entity, ++ ret = sdca_asoc_pde_poll_actual_ps(tac_dev->regmap, function_id, pde_entity, + SDCA_PDE_PS3, SDCA_PDE_PS0, NULL, 0); + if (ret) + dev_err(tac_dev->dev, "failed to transition func %d, pde %d from PS3 -> PS0, err=%d\n", +@@ -847,7 +838,7 @@ static int tac_sdw_pcm_hw_free(struct snd_pcm_substream *substream, + return ret; + } + +- ret = sdca_asoc_pde_poll_actual_ps(tac_dev->dev, tac_dev->regmap, function_id, ++ ret = sdca_asoc_pde_poll_actual_ps(tac_dev->regmap, function_id, + pde_entity, SDCA_PDE_PS0, SDCA_PDE_PS3, + NULL, 0); + if (ret) +@@ -942,6 +933,7 @@ static int tac5xx2_sdca_button_detect(struct tac5xx2_prv *tac_dev) + static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev) + { + int val, ret; ++ u8 jack_prep; + + ret = regmap_read(tac_dev->regmap, + SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_GE35, +@@ -951,6 +943,8 @@ static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev) + return ret; + } + ++ jack_prep = TAC_UAJ_PREP_CONNECTED; ++ + switch (val) { + case 4: + tac_dev->jack_type = SND_JACK_MICROPHONE; +@@ -964,6 +958,7 @@ static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev) + case 0: + default: + tac_dev->jack_type = 0; ++ jack_prep = TAC_UAJ_PREP_DISCONNECTED; + break; + } + +@@ -973,26 +968,44 @@ static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev) + if (ret) + dev_err(tac_dev->dev, "Failed to update the jack type to device"); + ++ /* ++ * When uaj is uplugged and booted, we end up with the channel prepare ++ * timeout error for the uaj ports. This writes allows the channel prepare ++ * to succeed when the uaj is not plugged in. ++ */ ++ if (tac_dev->rev_id >= 0x30) { ++ ret = regmap_write(tac_dev->regmap, TAC_REG_SDW(0, 3, 127), jack_prep); ++ if (ret) ++ dev_warn(tac_dev->dev, "Failed to write jack_prep register: %d\n", ret); ++ } ++ + return 0; + } + + static int tac5xx2_jack_init(struct tac5xx2_prv *tac_dev) + { ++ u32 jd_int_mask, hid_int_mask; + int ret = 0; + ++ if (tac_dev->rev_id >= 0x30) { ++ jd_int_mask = SDW_SCP_SDCA_INTMASK_SDCA_12; ++ hid_int_mask = SDW_SCP_SDCA_INTMASK_SDCA_17; ++ } else { ++ jd_int_mask = SDW_SCP_SDCA_INTMASK_SDCA_11; ++ hid_int_mask = SDW_SCP_SDCA_INTMASK_SDCA_16; ++ } ++ + if (!tac_dev->hs_jack) + goto disable_interrupts; + +- ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK2, +- SDW_SCP_SDCA_INTMASK_SDCA_11); ++ ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK2, jd_int_mask); + if (ret) { + dev_err(tac_dev->dev, + "Failed to register jack detection interrupt: %d\n", ret); + goto disable_interrupts; + } + +- ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK3, +- SDW_SCP_SDCA_INTMASK_SDCA_16); ++ ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK3, hid_int_mask); + if (ret) { + dev_err(tac_dev->dev, + "Failed to register for button detect interrupt: %d\n", ret); +@@ -1051,6 +1064,7 @@ static int tac_interrupt_callback(struct sdw_slave *slave, + unsigned int sdca_int2, sdca_int3, jack_report_mask = 0; + struct tac5xx2_prv *tac_dev = dev_get_drvdata(&slave->dev); + struct device *dev = &slave->dev; ++ u32 headset_detect_chk, hid_detect_chk; + int btn_type = 0; + int ret = 0; + +@@ -1081,14 +1095,22 @@ static int tac_interrupt_callback(struct sdw_slave *slave, + dev_dbg(dev, "SDCA_INT2: 0x%02x, SDCA_INT3: 0x%02x\n", + sdca_int2, sdca_int3); + +- if (sdca_int2 & SDW_SCP_SDCA_INT_SDCA_11) { ++ if (tac_dev->rev_id >= 0x30) { ++ headset_detect_chk = SDW_SCP_SDCA_INT_SDCA_12; ++ hid_detect_chk = SDW_SCP_SDCA_INT_SDCA_17; ++ } else { ++ headset_detect_chk = SDW_SCP_SDCA_INT_SDCA_11; ++ hid_detect_chk = SDW_SCP_SDCA_INT_SDCA_16; ++ } ++ ++ if (sdca_int2 & headset_detect_chk) { + ret = tac5xx2_sdca_headset_detect(tac_dev); + if (ret < 0) + goto clear; + jack_report_mask |= SND_JACK_HEADSET; + } + +- if (sdca_int3 & SDW_SCP_SDCA_INT_SDCA_16) { ++ if (sdca_int3 & hid_detect_chk) { + btn_type = tac5xx2_sdca_button_detect(tac_dev); + if (btn_type < 0) + btn_type = 0; +@@ -1695,6 +1717,15 @@ static int tac_io_init(struct device *dev, struct sdw_slave *slave, bool first) + goto io_init_err; + } + ++ if (!tac_dev->rev_id) { ++ ret = regmap_read(tac_dev->regmap, TAC_REV_ID, &tac_dev->rev_id); ++ if (ret) { ++ dev_err(tac_dev->dev, "failed to rev id, err=%d\n", ret); ++ goto io_init_err; ++ } ++ dev_dbg(tac_dev->dev, "detected rev_id 0x%x", tac_dev->rev_id); ++ } ++ + if (tac_dev->fw_files && tac_dev->fw_file_cnt > 0) { + ret = tac_download_fw_to_hw(tac_dev); + if (ret) { +@@ -1940,7 +1971,7 @@ static s32 tac_sdw_probe(struct sdw_slave *peripheral, + "failed to allocate %s function data", + func_name); + function_data->desc = &peripheral->sdca_data.function[i]; +- ret = sdca_parse_function(dev, peripheral, function_data); ++ ret = sdca_parse_function(dev, function_data); + if (!ret) + *func_ptr = function_data; + else +@@ -1959,6 +1990,7 @@ static s32 tac_sdw_probe(struct sdw_slave *peripheral, + tac_dev->hw_init = false; + tac_dev->first_hw_init_done = false; + tac_dev->part_id = id->part_id; ++ tac_dev->rev_id = 0x0; + dev_set_drvdata(dev, tac_dev); + + regmap = devm_regmap_init_sdw_mbq_cfg(&peripheral->dev, peripheral, +@@ -1972,17 +2004,13 @@ static s32 tac_sdw_probe(struct sdw_slave *peripheral, + tac_dev->jack_type = 0; + init_completion(&tac_dev->fw_caching_complete); + +- if (tac_has_dsp_algo(tac_dev)) { +- tac_generate_fw_name(peripheral, tac_dev->fw_binaryname, +- sizeof(tac_dev->fw_binaryname)); ++ tac_generate_fw_name(peripheral, tac_dev->fw_binaryname, ++ sizeof(tac_dev->fw_binaryname)); + +- ret = tac_load_and_cache_firmware_async(tac_dev); +- if (ret) { +- complete_all(&tac_dev->fw_caching_complete); +- dev_dbg(dev, "failed to load fw: %d, use rom mode\n", ret); +- } +- } else { ++ ret = tac_load_and_cache_firmware_async(tac_dev); ++ if (ret) { + complete_all(&tac_dev->fw_caching_complete); ++ dev_dbg(dev, "failed to load fw: %d, use rom mode\n", ret); + } + + ret = tac_init(tac_dev); +diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c +--- a/sound/soc/codecs/tas2552.c ++++ b/sound/soc/codecs/tas2552.c +@@ -495,13 +495,21 @@ static int tas2552_runtime_suspend(struct device *dev) + static int tas2552_runtime_resume(struct device *dev) + { + struct tas2552_data *tas2552 = dev_get_drvdata(dev); ++ int ret; + + gpiod_set_value_cansleep(tas2552->enable_gpio, 1); + + tas2552_sw_shutdown(tas2552, 0); + + regcache_cache_only(tas2552->regmap, false); +- regcache_sync(tas2552->regmap); ++ ret = regcache_sync(tas2552->regmap); ++ if (ret) { ++ regcache_cache_only(tas2552->regmap, true); ++ regcache_mark_dirty(tas2552->regmap); ++ tas2552_sw_shutdown(tas2552, 1); ++ gpiod_set_value_cansleep(tas2552->enable_gpio, 0); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/tas2781-comlib-i2c.c b/sound/soc/codecs/tas2781-comlib-i2c.c +--- a/sound/soc/codecs/tas2781-comlib-i2c.c ++++ b/sound/soc/codecs/tas2781-comlib-i2c.c +@@ -6,6 +6,7 @@ + // + // Author: Shenghao Ding + ++#include + #include + #include + #include +@@ -342,7 +343,7 @@ int tascodec_init(struct tasdevice_priv *tas_priv, void *codec, + /* Codec Lock Hold to ensure that codec_probe and firmware parsing and + * loading do not simultaneously execute. + */ +- mutex_lock(&tas_priv->codec_lock); ++ guard(mutex)(&tas_priv->codec_lock); + + if (tas_priv->name_prefix) + scnprintf(tas_priv->rca_binaryname, 64, "%s-%sRCA%d.bin", +@@ -360,8 +361,6 @@ int tascodec_init(struct tasdevice_priv *tas_priv, void *codec, + dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n", + ret); + +- /* Codec Lock Release*/ +- mutex_unlock(&tas_priv->codec_lock); + return ret; + } + EXPORT_SYMBOL_GPL(tascodec_init); +diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c +--- a/sound/soc/codecs/tas2781-fmwlib.c ++++ b/sound/soc/codecs/tas2781-fmwlib.c +@@ -2253,7 +2253,7 @@ int tas2781_load_calibration(void *context, char *file_name, + { + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; + struct tasdevice *tasdev = &(tas_priv->tasdevice[i]); +- const struct firmware *fw_entry = NULL; ++ const struct firmware *fw_entry __free(firmware) = NULL; + struct tasdevice_fw *tas_fmw; + struct firmware fmw; + int offset = 0; +@@ -2263,60 +2263,50 @@ int tas2781_load_calibration(void *context, char *file_name, + if (ret) { + dev_err(tas_priv->dev, "%s: Request firmware %s failed\n", + __func__, file_name); +- goto out; ++ return ret; + } + + if (!fw_entry->size) { + dev_err(tas_priv->dev, "%s: file read error: size = %lu\n", + __func__, (unsigned long)fw_entry->size); +- ret = -EINVAL; +- goto out; ++ return -EINVAL; + } + fmw.size = fw_entry->size; + fmw.data = fw_entry->data; + + tas_fmw = tasdev->cali_data_fmw = kzalloc_obj(struct tasdevice_fw); +- if (!tasdev->cali_data_fmw) { +- ret = -ENOMEM; +- goto out; +- } ++ if (!tasdev->cali_data_fmw) ++ return -ENOMEM; ++ + tas_fmw->dev = tas_priv->dev; + offset = fw_parse_header(tas_priv, tas_fmw, &fmw, offset); + if (offset == -EINVAL) { + dev_err(tas_priv->dev, "fw_parse_header EXIT!\n"); +- ret = offset; +- goto out; ++ return -EINVAL; + } + offset = fw_parse_variable_hdr_cal(tas_priv, tas_fmw, &fmw, offset); + if (offset == -EINVAL) { + dev_err(tas_priv->dev, + "%s: fw_parse_variable_header_cal EXIT!\n", __func__); +- ret = offset; +- goto out; ++ return -EINVAL; + } + offset = fw_parse_program_data(tas_priv, tas_fmw, &fmw, offset); + if (offset < 0) { + dev_err(tas_priv->dev, "fw_parse_program_data EXIT!\n"); +- ret = offset; +- goto out; ++ return offset; + } + offset = fw_parse_configuration_data(tas_priv, tas_fmw, &fmw, offset); + if (offset < 0) { + dev_err(tas_priv->dev, "fw_parse_configuration_data EXIT!\n"); +- ret = offset; +- goto out; ++ return offset; + } + offset = fw_parse_calibration_data(tas_priv, tas_fmw, &fmw, offset); + if (offset < 0) { + dev_err(tas_priv->dev, "fw_parse_calibration_data EXIT!\n"); +- ret = offset; +- goto out; ++ return offset; + } + +-out: +- release_firmware(fw_entry); +- +- return ret; ++ return 0; + } + EXPORT_SYMBOL_NS_GPL(tas2781_load_calibration, "SND_SOC_TAS2781_FMWLIB"); + +@@ -2409,7 +2399,7 @@ static int tasdevice_dspfw_ready(const struct firmware *fmw, + int tasdevice_dsp_parser(void *context) + { + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; +- const struct firmware *fw_entry; ++ const struct firmware *fw_entry __free(firmware) = NULL; + int ret; + + ret = request_firmware(&fw_entry, tas_priv->coef_binaryname, +@@ -2417,15 +2407,10 @@ int tasdevice_dsp_parser(void *context) + if (ret) { + dev_err(tas_priv->dev, "%s: load %s error\n", __func__, + tas_priv->coef_binaryname); +- goto out; ++ return ret; + } + +- ret = tasdevice_dspfw_ready(fw_entry, tas_priv); +- release_firmware(fw_entry); +- fw_entry = NULL; +- +-out: +- return ret; ++ return tasdevice_dspfw_ready(fw_entry, tas_priv); + } + EXPORT_SYMBOL_NS_GPL(tasdevice_dsp_parser, "SND_SOC_TAS2781_FMWLIB"); + +@@ -2807,11 +2792,12 @@ int tasdevice_prmg_load(void *context, int prm_no) + } + EXPORT_SYMBOL_NS_GPL(tasdevice_prmg_load, "SND_SOC_TAS2781_FMWLIB"); + +-void tasdevice_tuning_switch(void *context, int state) ++void tasdevice_tuning_switch(void *context, int state, bool is_cap) + { + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_fw *tas_fmw = tas_priv->fmw; +- int profile_cfg_id = tas_priv->rcabin.profile_cfg_id; ++ int profile_cfg_id = is_cap ? tas_priv->rcabin.capture_profile_id : ++ tas_priv->rcabin.profile_cfg_id; + + /* + * Only RCA-based Playback can still work with no dsp program running +@@ -2828,7 +2814,6 @@ void tasdevice_tuning_switch(void *context, int state) + if (state == 0) { + if (tas_fmw && tas_priv->cur_prog < tas_fmw->nr_programs) { + /* dsp mode or tuning mode */ +- profile_cfg_id = tas_priv->rcabin.profile_cfg_id; + tasdevice_select_tuningprm_cfg(tas_priv, + tas_priv->cur_prog, tas_priv->cur_conf, + profile_cfg_id); +diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c +--- a/sound/soc/codecs/tas2781-i2c.c ++++ b/sound/soc/codecs/tas2781-i2c.c +@@ -13,6 +13,7 @@ + // Author: Kevin Lu + // + ++#include + #include + #include + #include +@@ -617,6 +618,7 @@ static int tasdev_calib_stop_put(struct snd_kcontrol *kcontrol, + { + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp); ++ int i; + + guard(mutex)(&priv->codec_lock); + if (priv->chip_id == TAS2563) +@@ -624,6 +626,14 @@ static int tasdev_calib_stop_put(struct snd_kcontrol *kcontrol, + else + tas2781_calib_stop_put(priv); + ++ /* ++ * Set reloading-firmware flag after calibration, the flag will work ++ * during next playback, then set to the program id after reloading ++ * firmware. ++ */ ++ for (i = 0; i < priv->ndev; i++) ++ priv->tasdevice[i].cur_prog = -1; ++ + return 1; + } + +@@ -843,12 +853,12 @@ static int tasdevice_digital_gain_get( + unsigned char data[4]; + int ret; + +- mutex_lock(&tas_dev->codec_lock); ++ guard(mutex)(&tas_dev->codec_lock); + /* Read the primary device */ + ret = tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4); + if (ret) { + dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__); +- goto out; ++ return ret; + } + + target = get_unaligned_be32(&data[0]); +@@ -868,8 +878,7 @@ static int tasdevice_digital_gain_get( + /* find out the member same as or closer to the current volume */ + ucontrol->value.integer.value[0] = + abs(target - ar_l) <= abs(target - ar_r) ? l : r; +-out: +- mutex_unlock(&tas_dev->codec_lock); ++ + return 0; + } + +@@ -882,29 +891,26 @@ static int tasdevice_digital_gain_put( + struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol); + struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec); + int vol = ucontrol->value.integer.value[0]; +- int status = 0, max = mc->max, rc = 1; ++ int status = 0, max = mc->max; + int i, ret; + unsigned int reg = mc->reg; + unsigned int volrd, volwr; + unsigned char data[4]; + + vol = clamp(vol, 0, max); +- mutex_lock(&tas_dev->codec_lock); ++ guard(mutex)(&tas_dev->codec_lock); + /* Read the primary device */ + ret = tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4); + if (ret) { + dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__); +- rc = -1; +- goto out; ++ return -1; + } + + volrd = get_unaligned_be32(&data[0]); + volwr = get_unaligned_be32(tas_dev->dvc_tlv_table[vol]); + +- if (volrd == volwr) { +- rc = 0; +- goto out; +- } ++ if (volrd == volwr) ++ return 0; + + for (i = 0; i < tas_dev->ndev; i++) { + ret = tasdevice_dev_bulk_write(tas_dev, i, reg, +@@ -918,10 +924,9 @@ static int tasdevice_digital_gain_put( + } + + if (status) +- rc = -1; +-out: +- mutex_unlock(&tas_dev->codec_lock); +- return rc; ++ return -1; ++ ++ return 1; + } + + static const struct snd_kcontrol_new tasdevice_cali_controls[] = { +@@ -995,6 +1000,57 @@ static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol, + return ret; + } + ++/** ++ * tasdevice_set_capture_profile_id - Set runtime capture profile index via ++ * ALSA control ++ * @kcontrol: ALSA kcontrol handle that triggers this operation ++ * @ucontrol: User space control value carrying the new profile index ++ * ++ * This mixer control handler validates the user-provided capture profile ID ++ * against the maximum valid index parsed from the loaded DSP firmware, ++ * then updates the runtime stored capture profile ID only if the new value ++ * differs from the current active one. It will immediately return -EINVAL ++ * if the submitted profile ID falls outside the valid range, including the ++ * edge case that no valid configuration blocks are detected in firmware. ++ * ++ * No actual DSP register write is performed in this handler. The updated ++ * profile ID will be applied to the hardware when the next ALSA capture ++ * stream starts up. Caller does not need to take extra codec lock here, ++ * as the ALSA control core already guarantees serialized execution. ++ * ++ * Return: 1 if profile ID value was changed, 0 if no modification needed, ++ * -EINVAL if the input profile ID is out of valid range ++ */ ++static int tasdevice_set_capture_profile_id(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol); ++ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); ++ unsigned int user_prof_id = ucontrol->value.integer.value[0]; ++ unsigned int max_valid_id; ++ int ret = 0; ++ ++ /* ++ * Align valid range with the bound defined in ++ * tasdevice_info_profile() ++ */ ++ max_valid_id = tas_priv->rcabin.ncfgs - 1; ++ ++ /* ++ * Reject invalid input including zero total configuration edge ++ * case ++ */ ++ if (tas_priv->rcabin.ncfgs == 0 || user_prof_id > max_valid_id) ++ return -EINVAL; ++ ++ if (tas_priv->rcabin.capture_profile_id != user_prof_id) { ++ tas_priv->rcabin.capture_profile_id = user_prof_id; ++ ret = 1; ++ } ++ ++ return ret; ++} ++ + static int tasdevice_info_active_num(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) + { +@@ -1075,6 +1131,41 @@ static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol, + return 0; + } + ++/** ++ * tasdevice_get_capture_profile_id - Report current active capture profile ++ * ID to user space ++ * @kcontrol: ALSA kcontrol structure passed from ALSA core ++ * @ucontrol: User-space control element value buffer to write the result back ++ * ++ * This function ensures the returned profile ID is always clamped inside the ++ * valid range advertised by the info callback, preventing accidental invalid ++ * values from being exposed to applications even if internal driver state is ++ * temporarily inconsistent. ++ * ++ * Returns 0 on successful fill of the control value, no error conditions ++ * are defined for this getter callback. ++ */ ++static int tasdevice_get_capture_profile_id(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol); ++ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); ++ unsigned int max_valid_id, current_prof_id; ++ ++ max_valid_id = tas_priv->rcabin.ncfgs > 0 ? ++ (tas_priv->rcabin.ncfgs - 1U) : 0; ++ ++ /* ++ * Cast current profile id to unsigned to match type with max_valid_id, ++ * avoid signedness mismatch; ++ */ ++ current_prof_id = (unsigned int)tas_priv->rcabin.capture_profile_id; ++ /* Prevent underflow when there are no loaded capture profiles. */ ++ ucontrol->value.integer.value[0] = min(current_prof_id, max_valid_id); ++ ++ return 0; ++} ++ + static int tasdevice_get_chip_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) + { +@@ -1117,6 +1208,41 @@ static int tasdevice_create_control(struct tasdevice_priv *tas_priv) + ret = snd_soc_add_component_controls(tas_priv->codec, + prof_ctrls, nr_controls < mix_index ? nr_controls : mix_index); + ++ mix_index = 0; ++ switch (tas_priv->chip_id) { ++ case TAS2563: ++ case TAS2568: ++ case TAS2570: ++ case TAS2572: ++ case TAS2573: ++ case TAS2574: ++ case TAS2781: ++ prof_ctrls = devm_kcalloc(tas_priv->dev, nr_controls, ++ sizeof(prof_ctrls[0]), GFP_KERNEL); ++ if (!prof_ctrls) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ /* Create a mixer item for selecting the capture profile */ ++ name = devm_kstrdup(tas_priv->dev, "Speaker Capture Profile Id", ++ GFP_KERNEL); ++ if (!name) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ prof_ctrls[mix_index].name = name; ++ prof_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; ++ prof_ctrls[mix_index].info = tasdevice_info_profile; ++ prof_ctrls[mix_index].get = tasdevice_get_capture_profile_id; ++ prof_ctrls[mix_index].put = tasdevice_set_capture_profile_id; ++ mix_index++; ++ ++ ret = snd_soc_add_component_controls(tas_priv->codec, ++ prof_ctrls, nr_controls < mix_index ? nr_controls : mix_index); ++ break; ++ } ++ + out: + return ret; + } +@@ -1330,6 +1456,43 @@ static void alpa_cali_update(struct bulk_reg_val *p, + p->val_len = 4; + } + ++static int create_tas2781_cali_start_ktrl(struct tasdevice_priv ++ *priv, struct snd_kcontrol_new *cali_ctrl) ++{ ++ struct soc_bytes_ext *ext_cali_start; ++ char *cali_start_name; ++ ++ ext_cali_start = devm_kzalloc(priv->dev, ++ sizeof(*ext_cali_start), GFP_KERNEL); ++ if (!ext_cali_start) ++ return -ENOMEM; ++ ++ cali_start_name = devm_kstrdup(priv->dev, ++ "Calibration Start", GFP_KERNEL); ++ if (!cali_start_name) ++ return -ENOMEM; ++ /* ++ * package structure for tas2781 ftc start: ++ * Pkg len (1 byte) ++ * Reg id (1 byte, constant 'r') ++ * book, page, register for pilot threshold, pilot tone ++ * and sine gain (12 bytes) ++ * for (i = 0; i < Device-Sum; i++) { ++ * Device #i index_info (1 byte) ++ * Sine gain for Device #i (8 bytes) ++ * } ++ */ ++ ext_cali_start->max = 14 + priv->ndev * 9; ++ cali_ctrl->name = cali_start_name; ++ cali_ctrl->iface = SNDRV_CTL_ELEM_IFACE_MIXER; ++ cali_ctrl->info = snd_soc_bytes_info_ext; ++ cali_ctrl->put = tas2781_calib_start_put; ++ cali_ctrl->get = tasdev_nop_get; ++ cali_ctrl->private_value = (unsigned long)ext_cali_start; ++ ++ return 0; ++} ++ + static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv) + { + struct calidata *cali_data = &priv->cali_data; +@@ -1448,37 +1611,11 @@ static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv) + */ + cali_data->data[0] = 0xff; + if (priv->chip_id == TAS2781) { +- struct soc_bytes_ext *ext_cali_start; +- char *cali_start_name; +- +- ext_cali_start = devm_kzalloc(priv->dev, +- sizeof(*ext_cali_start), GFP_KERNEL); +- if (!ext_cali_start) +- return -ENOMEM; +- +- cali_start_name = devm_kstrdup(priv->dev, +- "Calibration Start", GFP_KERNEL); +- if (!cali_start_name) +- return -ENOMEM; +- /* +- * package structure for tas2781 ftc start: +- * Pkg len (1 byte) +- * Reg id (1 byte, constant 'r') +- * book, page, register for pilot threshold, pilot tone +- * and sine gain (12 bytes) +- * for (i = 0; i < Device-Sum; i++) { +- * Device #i index_info (1 byte) +- * Sine gain for Device #i (8 bytes) +- * } +- */ +- ext_cali_start->max = 14 + priv->ndev * 9; +- cali_ctrls[i].name = cali_start_name; +- cali_ctrls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; +- cali_ctrls[i].info = snd_soc_bytes_info_ext; +- cali_ctrls[i].put = tas2781_calib_start_put; +- cali_ctrls[i].get = tasdev_nop_get; +- cali_ctrls[i].private_value = (unsigned long)ext_cali_start; ++ rc = create_tas2781_cali_start_ktrl(priv, &cali_ctrls[i]); ++ if (rc != 0) ++ return rc; + i++; ++ + } + + return snd_soc_add_component_controls(priv->codec, cali_ctrls, +@@ -1765,13 +1902,25 @@ static int tasdevice_dapm_event(struct snd_soc_dapm_widget *w, + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + int state = 0; + +- /* Codec Lock Hold */ +- mutex_lock(&tas_priv->codec_lock); ++ guard(mutex)(&tas_priv->codec_lock); + if (event == SND_SOC_DAPM_PRE_PMD) + state = 1; +- tasdevice_tuning_switch(tas_priv, state); +- /* Codec Lock Release*/ +- mutex_unlock(&tas_priv->codec_lock); ++ tasdevice_tuning_switch(tas_priv, state, false); ++ ++ return 0; ++} ++ ++static int tasdevice_capture_dapm_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, int event) ++{ ++ struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); ++ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); ++ int state = 0; ++ ++ guard(mutex)(&tas_priv->codec_lock); ++ if (event == SND_SOC_DAPM_PRE_PMD) ++ state = 1; ++ tasdevice_tuning_switch(tas_priv, state, true); + + return 0; + } +@@ -1779,7 +1928,7 @@ static int tasdevice_dapm_event(struct snd_soc_dapm_widget *w, + static const struct snd_soc_dapm_widget tasdevice_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("ASI", "ASI Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT_E("ASI OUT", "ASI Capture", 0, SND_SOC_NOPM, +- 0, 0, tasdevice_dapm_event, ++ 0, 0, tasdevice_capture_dapm_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SPK("SPK", tasdevice_dapm_event), + SND_SOC_DAPM_OUTPUT("OUT"), +diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c +--- a/sound/soc/codecs/tas2783-sdw.c ++++ b/sound/soc/codecs/tas2783-sdw.c +@@ -12,6 +12,7 @@ + // Author: Baojun Xu + // Author: Kevin Lu + ++#include + #include + #include + #include +@@ -494,6 +495,56 @@ static bool tas2783_readable_register(struct device *dev, unsigned int reg) + return tas2783_sdca_mbq_size(dev, reg) > 0; + } + ++static bool tas2783_writeable_register(struct device *dev, unsigned int reg) ++{ ++ /* ++ * The Latency Control of every Entity, together with the Power Domain ++ * actual state and the protection status, is read-only. They are ++ * listed in tas2783_reg_default[] with a placeholder value, so without ++ * this a regcache_sync() would try to write them back and the ++ * peripheral would reject the transaction, aborting the sync. ++ */ ++ switch (reg) { ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU21, 0x10, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU23, 0x10, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU26, 0x10, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_XU22, 0x06, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_XU22, 0x07, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_XU22, 0x08, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS24, 0x02, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS21, 0x02, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS25, 0x02, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS26, 0x02, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS28, 0x02, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_PDE23, 0x10, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_UDMPU23, 0x06, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_SAPU29, 0x05, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_SAPU29, 0x11, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_PPU21, 0x06, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_PPU26, 0x06, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT21, 0x08, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT29, 0x08, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT26, 0x08, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT28, 0x08, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT24, 0x08, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT23, 0x08, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT25, 0x08, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT28, 0x08, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MU26, 0x06, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT127, 0x08, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU127, 0x10, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS127, 0x02, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU21, 0x08, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU21, 0x04, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU26, 0x08, 0): ++ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU26, 0x04, 0): ++ return false; ++ ++ default: ++ return tas2783_sdca_mbq_size(dev, reg) > 0; ++ } ++} ++ + static bool tas2783_volatile_register(struct device *dev, u32 reg) + { + switch (reg) { +@@ -515,6 +566,7 @@ static const struct regmap_config tas_regmap = { + .reg_bits = 32, + .val_bits = 8, + .readable_reg = tas2783_readable_register, ++ .writeable_reg = tas2783_writeable_register, + .volatile_reg = tas2783_volatile_register, + .reg_defaults = tas2783_reg_default, + .num_reg_defaults = ARRAY_SIZE(tas2783_reg_default), +@@ -692,12 +744,12 @@ static s32 tas2783_update_calibdata(struct tas2783_prv *tas_dev) + return 0; + } + +- mutex_lock(&tas_dev->calib_lock); +- ret = tas2783_validate_calibdata(tas_dev, tas_dev->cali_data.data, +- tas_dev->cali_data.read_sz); +- if (!ret) +- tas2783_set_calib_params_to_device(tas_dev, tmp_val); +- mutex_unlock(&tas_dev->calib_lock); ++ scoped_guard(mutex, &tas_dev->calib_lock) { ++ ret = tas2783_validate_calibdata(tas_dev, tas_dev->cali_data.data, ++ tas_dev->cali_data.read_sz); ++ if (!ret) ++ tas2783_set_calib_params_to_device(tas_dev, tmp_val); ++ } + + return ret; + } +@@ -926,22 +978,23 @@ static s32 tas_sdw_hw_params(struct snd_pcm_substream *substream, + dev_err(tas_dev->dev, + "clear latch failed, err=%d", ret); + +- mutex_lock(&tas_dev->pde_lock); +- /* +- * Sometimes, there is error returned during power on. +- * So added retry logic to ensure power on so that +- * port prepare succeeds +- */ +- do { +- ret = regmap_write(tas_dev->regmap, +- SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, +- TAS2783_SDCA_CTL_REQ_POW_STATE, 0), +- TAS2783_SDCA_POW_STATE_ON); +- if (!ret) +- break; +- usleep_range(2000, 2200); +- } while (retry--); +- mutex_unlock(&tas_dev->pde_lock); ++ scoped_guard(mutex, &tas_dev->pde_lock) { ++ /* ++ * Sometimes, there is error returned during power on. ++ * So added retry logic to ensure power on so that ++ * port prepare succeeds ++ */ ++ do { ++ ret = regmap_write(tas_dev->regmap, ++ SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, ++ TAS2783_SDCA_CTL_REQ_POW_STATE, 0), ++ TAS2783_SDCA_POW_STATE_ON); ++ if (!ret) ++ break; ++ usleep_range(2000, 2200); ++ } while (retry--); ++ } ++ + if (ret) + return ret; + +@@ -965,7 +1018,6 @@ static s32 tas_sdw_hw_params(struct snd_pcm_substream *substream, + static s32 tas_sdw_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + { +- s32 ret; + struct snd_soc_component *component = dai->component; + struct tas2783_prv *tas_dev = + snd_soc_component_get_drvdata(component); +@@ -974,14 +1026,11 @@ static s32 tas_sdw_pcm_hw_free(struct snd_pcm_substream *substream, + + sdw_stream_remove_slave(tas_dev->sdw_peripheral, sdw_stream); + +- mutex_lock(&tas_dev->pde_lock); +- ret = regmap_write(tas_dev->regmap, +- SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, +- TAS2783_SDCA_CTL_REQ_POW_STATE, 0), +- TAS2783_SDCA_POW_STATE_OFF); +- mutex_unlock(&tas_dev->pde_lock); +- +- return ret; ++ guard(mutex)(&tas_dev->pde_lock); ++ return regmap_write(tas_dev->regmap, ++ SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, ++ TAS2783_SDCA_CTL_REQ_POW_STATE, 0), ++ TAS2783_SDCA_POW_STATE_OFF); + } + + static const struct snd_soc_dai_ops tas_dai_ops = { +@@ -1349,7 +1398,7 @@ static s32 tas_sdw_probe(struct sdw_slave *peripheral, + function_data->desc = &peripheral->sdca_data.function[i]; + + /* Parse the function */ +- ret = sdca_parse_function(dev, peripheral, function_data); ++ ret = sdca_parse_function(dev, function_data); + if (!ret) + tas_dev->sa_func_data = function_data; + else +diff --git a/sound/soc/codecs/tas5805m.c b/sound/soc/codecs/tas5805m.c +--- a/sound/soc/codecs/tas5805m.c ++++ b/sound/soc/codecs/tas5805m.c +@@ -12,6 +12,7 @@ + // + // It has been simplified a little and reworked for the 5.x ALSA SoC API. + ++#include + #include + #include + #include +@@ -230,10 +231,9 @@ static int tas5805m_vol_get(struct snd_kcontrol *kcontrol, + struct tas5805m_priv *tas5805m = + snd_soc_component_get_drvdata(component); + +- mutex_lock(&tas5805m->lock); ++ guard(mutex)(&tas5805m->lock); + ucontrol->value.integer.value[0] = tas5805m->vol[0]; + ucontrol->value.integer.value[1] = tas5805m->vol[1]; +- mutex_unlock(&tas5805m->lock); + + return 0; + } +@@ -249,13 +249,12 @@ static int tas5805m_vol_put(struct snd_kcontrol *kcontrol, + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct tas5805m_priv *tas5805m = + snd_soc_component_get_drvdata(component); +- int ret = 0; + + if (!(volume_is_valid(ucontrol->value.integer.value[0]) && + volume_is_valid(ucontrol->value.integer.value[1]))) + return -EINVAL; + +- mutex_lock(&tas5805m->lock); ++ guard(mutex)(&tas5805m->lock); + if (tas5805m->vol[0] != ucontrol->value.integer.value[0] || + tas5805m->vol[1] != ucontrol->value.integer.value[1]) { + tas5805m->vol[0] = ucontrol->value.integer.value[0]; +@@ -265,11 +264,10 @@ static int tas5805m_vol_put(struct snd_kcontrol *kcontrol, + tas5805m->is_powered); + if (tas5805m->is_powered) + tas5805m_refresh(tas5805m); +- ret = 1; ++ return 1; + } +- mutex_unlock(&tas5805m->lock); + +- return ret; ++ return 0; + } + + static const struct snd_kcontrol_new tas5805m_snd_controls[] = { +@@ -332,7 +330,7 @@ static void do_work(struct work_struct *work) + + dev_dbg(&tas5805m->i2c->dev, "DSP startup\n"); + +- mutex_lock(&tas5805m->lock); ++ guard(mutex)(&tas5805m->lock); + /* We mustn't issue any I2C transactions until the I2S + * clock is stable. Furthermore, we must allow a 5ms + * delay after the first set of register writes to +@@ -345,7 +343,6 @@ static void do_work(struct work_struct *work) + + tas5805m->is_powered = true; + tas5805m_refresh(tas5805m); +- mutex_unlock(&tas5805m->lock); + } + + static int tas5805m_dac_event(struct snd_soc_dapm_widget *w, +@@ -362,7 +359,7 @@ static int tas5805m_dac_event(struct snd_soc_dapm_widget *w, + dev_dbg(component->dev, "DSP shutdown\n"); + cancel_work_sync(&tas5805m->work); + +- mutex_lock(&tas5805m->lock); ++ guard(mutex)(&tas5805m->lock); + if (tas5805m->is_powered) { + tas5805m->is_powered = false; + +@@ -379,7 +376,6 @@ static int tas5805m_dac_event(struct snd_soc_dapm_widget *w, + + regmap_write(rm, REG_DEVICE_CTRL_2, DCTRL2_MODE_HIZ); + } +- mutex_unlock(&tas5805m->lock); + } + + return 0; +@@ -414,14 +410,13 @@ static int tas5805m_mute(struct snd_soc_dai *dai, int mute, int direction) + struct tas5805m_priv *tas5805m = + snd_soc_component_get_drvdata(component); + +- mutex_lock(&tas5805m->lock); ++ guard(mutex)(&tas5805m->lock); + dev_dbg(component->dev, "set mute=%d (is_powered=%d)\n", + mute, tas5805m->is_powered); + + tas5805m->is_muted = mute; + if (tas5805m->is_powered) + tas5805m_refresh(tas5805m); +- mutex_unlock(&tas5805m->lock); + + return 0; + } +@@ -462,7 +457,6 @@ static int tas5805m_i2c_probe(struct i2c_client *i2c) + struct tas5805m_priv *tas5805m; + char filename[128]; + const char *config_name; +- const struct firmware *fw; + int ret; + + regmap = devm_regmap_init_i2c(i2c, &tas5805m_regmap); +@@ -507,24 +501,20 @@ static int tas5805m_i2c_probe(struct i2c_client *i2c) + + snprintf(filename, sizeof(filename), "tas5805m_dsp_%s.bin", + config_name); ++ const struct firmware *fw __free(firmware) = NULL; + ret = request_firmware(&fw, filename, dev); + if (ret) + return ret; + + if ((fw->size < 2) || (fw->size & 1)) { + dev_err(dev, "firmware is invalid\n"); +- release_firmware(fw); + return -EINVAL; + } + + tas5805m->dsp_cfg_len = fw->size; + tas5805m->dsp_cfg_data = devm_kmemdup(dev, fw->data, fw->size, GFP_KERNEL); +- if (!tas5805m->dsp_cfg_data) { +- release_firmware(fw); ++ if (!tas5805m->dsp_cfg_data) + return -ENOMEM; +- } +- +- release_firmware(fw); + + /* Do the first part of the power-on here, while we can expect + * the I2S interface to be quiet. We must raise PDN# and then +diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c +--- a/sound/soc/codecs/tlv320aic26.c ++++ b/sound/soc/codecs/tlv320aic26.c +@@ -320,8 +320,14 @@ static int aic26_probe(struct snd_soc_component *component) + return 0; + } + ++static void aic26_remove(struct snd_soc_component *component) ++{ ++ device_remove_file(component->dev, &dev_attr_keyclick); ++} ++ + static const struct snd_soc_component_driver aic26_soc_component_dev = { + .probe = aic26_probe, ++ .remove = aic26_remove, + .controls = aic26_snd_controls, + .num_controls = ARRAY_SIZE(aic26_snd_controls), + .dapm_widgets = tlv320aic26_dapm_widgets, +diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c +--- a/sound/soc/codecs/tlv320aic31xx.c ++++ b/sound/soc/codecs/tlv320aic31xx.c +@@ -1720,18 +1720,14 @@ static int tlv320dac3100_fw_load(struct aic31xx_priv *aic31xx, + static int tlv320dac3100_load_coeffs(struct aic31xx_priv *aic31xx, + const char *fw_name) + { +- const struct firmware *fw; ++ const struct firmware *fw __free(firmware) = NULL; + int ret; + + ret = request_firmware(&fw, fw_name, aic31xx->dev); + if (ret) + return ret; + +- ret = tlv320dac3100_fw_load(aic31xx, fw->data, fw->size); +- +- release_firmware(fw); +- +- return ret; ++ return tlv320dac3100_fw_load(aic31xx, fw->data, fw->size); + } + + static int aic31xx_i2c_probe(struct i2c_client *i2c) +diff --git a/sound/soc/codecs/tlv320aic32x4-clk.c b/sound/soc/codecs/tlv320aic32x4-clk.c +--- a/sound/soc/codecs/tlv320aic32x4-clk.c ++++ b/sound/soc/codecs/tlv320aic32x4-clk.c +@@ -1,5 +1,5 @@ +-/* SPDX-License-Identifier: GPL-2.0 +- * ++// SPDX-License-Identifier: GPL-2.0 ++/* + * Clock Tree for the Texas Instruments TLV320AIC32x4 + * + * Copyright 2019 Annaliese McDermond +@@ -9,6 +9,7 @@ + + #include + #include ++#include + #include + #include + +@@ -57,7 +58,7 @@ static void clk_aic32x4_pll_unprepare(struct clk_hw *hw) + struct clk_aic32x4 *pll = to_clk_aic32x4(hw); + + regmap_update_bits(pll->regmap, AIC32X4_PLLPR, +- AIC32X4_PLLEN, 0); ++ AIC32X4_PLLEN, 0); + } + + static int clk_aic32x4_pll_is_prepared(struct clk_hw *hw) +@@ -75,7 +76,7 @@ static int clk_aic32x4_pll_is_prepared(struct clk_hw *hw) + } + + static int clk_aic32x4_pll_get_muldiv(struct clk_aic32x4 *pll, +- struct clk_aic32x4_pll_muldiv *settings) ++ struct clk_aic32x4_pll_muldiv *settings) + { + /* Change to use regmap_bulk_read? */ + unsigned int val; +@@ -106,19 +107,19 @@ static int clk_aic32x4_pll_get_muldiv(struct clk_aic32x4 *pll, + } + + static int clk_aic32x4_pll_set_muldiv(struct clk_aic32x4 *pll, +- struct clk_aic32x4_pll_muldiv *settings) ++ struct clk_aic32x4_pll_muldiv *settings) + { + int ret; + /* Change to use regmap_bulk_write for some if not all? */ + + ret = regmap_update_bits(pll->regmap, AIC32X4_PLLPR, +- AIC32X4_PLL_R_MASK, settings->r); ++ AIC32X4_PLL_R_MASK, settings->r); + if (ret < 0) + return ret; + + ret = regmap_update_bits(pll->regmap, AIC32X4_PLLPR, +- AIC32X4_PLL_P_MASK, +- settings->p << AIC32X4_PLL_P_SHIFT); ++ AIC32X4_PLL_P_MASK, ++ settings->p << AIC32X4_PLL_P_SHIFT); + if (ret < 0) + return ret; + +@@ -136,23 +137,21 @@ static int clk_aic32x4_pll_set_muldiv(struct clk_aic32x4 *pll, + return 0; + } + +-static unsigned long clk_aic32x4_pll_calc_rate( +- struct clk_aic32x4_pll_muldiv *settings, +- unsigned long parent_rate) ++static unsigned long clk_aic32x4_pll_calc_rate(struct clk_aic32x4_pll_muldiv *settings, ++ unsigned long parent_rate) + { + u64 rate; + /* + * We scale j by 10000 to account for the decimal part of P and divide + * it back out later. + */ +- rate = (u64) parent_rate * settings->r * +- ((settings->j * 10000) + settings->d); ++ rate = (u64)parent_rate * settings->r * ((settings->j * 10000) + settings->d); + +- return (unsigned long) DIV_ROUND_UP_ULL(rate, settings->p * 10000); ++ return (unsigned long)DIV_ROUND_UP_ULL(rate, settings->p * 10000); + } + + static int clk_aic32x4_pll_calc_muldiv(struct clk_aic32x4_pll_muldiv *settings, +- unsigned long rate, unsigned long parent_rate) ++ unsigned long rate, unsigned long parent_rate) + { + u64 multiplier; + +@@ -165,14 +164,14 @@ static int clk_aic32x4_pll_calc_muldiv(struct clk_aic32x4_pll_muldiv *settings, + * of the multiplier. This is because we can't do floating point + * math in the kernel. + */ +- multiplier = (u64) rate * settings->p * 10000; ++ multiplier = (u64)rate * settings->p * 10000; + do_div(multiplier, parent_rate); + + /* + * J can't be over 64, so R can scale this. + * R can't be greater than 4. + */ +- settings->r = ((u32) multiplier / 640000) + 1; ++ settings->r = ((u32)multiplier / 640000) + 1; + if (settings->r > 4) + return -1; + do_div(multiplier, settings->r); +@@ -184,14 +183,14 @@ static int clk_aic32x4_pll_calc_muldiv(struct clk_aic32x4_pll_muldiv *settings, + return -1; + + /* Figure out the integer part, J, and the fractional part, D. */ +- settings->j = (u32) multiplier / 10000; +- settings->d = (u32) multiplier % 10000; ++ settings->j = (u32)multiplier / 10000; ++ settings->d = (u32)multiplier % 10000; + + return 0; + } + + static unsigned long clk_aic32x4_pll_recalc_rate(struct clk_hw *hw, +- unsigned long parent_rate) ++ unsigned long parent_rate) + { + struct clk_aic32x4 *pll = to_clk_aic32x4(hw); + struct clk_aic32x4_pll_muldiv settings; +@@ -220,8 +219,8 @@ static int clk_aic32x4_pll_determine_rate(struct clk_hw *hw, + } + + static int clk_aic32x4_pll_set_rate(struct clk_hw *hw, +- unsigned long rate, +- unsigned long parent_rate) ++ unsigned long rate, ++ unsigned long parent_rate) + { + struct clk_aic32x4 *pll = to_clk_aic32x4(hw); + struct clk_aic32x4_pll_muldiv settings; +@@ -236,7 +235,7 @@ static int clk_aic32x4_pll_set_rate(struct clk_hw *hw, + return ret; + + /* 10ms is the delay to wait before the clocks are stable */ +- msleep(10); ++ usleep_range(10000, 20000); + + return 0; + } +@@ -261,7 +260,6 @@ static u8 clk_aic32x4_pll_get_parent(struct clk_hw *hw) + return (val & AIC32X4_PLL_CLKIN_MASK) >> AIC32X4_PLL_CLKIN_SHIFT; + } + +- + static const struct clk_ops aic32x4_pll_ops = { + .prepare = clk_aic32x4_pll_prepare, + .unprepare = clk_aic32x4_pll_unprepare, +@@ -311,11 +309,11 @@ static void clk_aic32x4_div_unprepare(struct clk_hw *hw) + struct clk_aic32x4 *div = to_clk_aic32x4(hw); + + regmap_update_bits(div->regmap, div->reg, +- AIC32X4_DIVEN, 0); ++ AIC32X4_DIVEN, 0); + } + + static int clk_aic32x4_div_set_rate(struct clk_hw *hw, unsigned long rate, +- unsigned long parent_rate) ++ unsigned long parent_rate) + { + struct clk_aic32x4 *div = to_clk_aic32x4(hw); + u8 divisor; +@@ -342,7 +340,7 @@ static int clk_aic32x4_div_determine_rate(struct clk_hw *hw, + } + + static unsigned long clk_aic32x4_div_recalc_rate(struct clk_hw *hw, +- unsigned long parent_rate) ++ unsigned long parent_rate) + { + struct clk_aic32x4 *div = to_clk_aic32x4(hw); + unsigned int val; +@@ -399,7 +397,7 @@ static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { + { + .name = "pll", + .parent_names = +- (const char* []) { "mclk", "bclk", "gpio", "din" }, ++ (const char *[]) { "mclk", "bclk", "gpio", "din" }, + .num_parents = 4, + .ops = &aic32x4_pll_ops, + .reg = 0, +@@ -414,28 +412,28 @@ static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { + }, + { + .name = "ndac", +- .parent_names = (const char * []) { "codec_clkin" }, ++ .parent_names = (const char *[]) { "codec_clkin" }, + .num_parents = 1, + .ops = &aic32x4_div_ops, + .reg = AIC32X4_NDAC, + }, + { + .name = "mdac", +- .parent_names = (const char * []) { "ndac" }, ++ .parent_names = (const char *[]) { "ndac" }, + .num_parents = 1, + .ops = &aic32x4_div_ops, + .reg = AIC32X4_MDAC, + }, + { + .name = "nadc", +- .parent_names = (const char * []) { "codec_clkin" }, ++ .parent_names = (const char *[]) { "codec_clkin" }, + .num_parents = 1, + .ops = &aic32x4_div_ops, + .reg = AIC32X4_NADC, + }, + { + .name = "madc", +- .parent_names = (const char * []) { "nadc" }, ++ .parent_names = (const char *[]) { "nadc" }, + .num_parents = 1, + .ops = &aic32x4_div_ops, + .reg = AIC32X4_MADC, +@@ -451,7 +449,7 @@ static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { + }; + + static struct clk *aic32x4_register_clk(struct device *dev, +- struct aic32x4_clkdesc *desc) ++ struct aic32x4_clkdesc *desc) + { + struct clk_init_data init; + struct clk_aic32x4 *priv; +@@ -464,8 +462,8 @@ static struct clk *aic32x4_register_clk(struct device *dev, + init.flags = 0; + + priv = devm_kzalloc(dev, sizeof(struct clk_aic32x4), GFP_KERNEL); +- if (priv == NULL) +- return (struct clk *) -ENOMEM; ++ if (!priv) ++ return ERR_PTR(-ENOMEM); + + priv->dev = dev; + priv->hw.init = &init; +diff --git a/sound/soc/codecs/tlv320aic32x4-i2c.c b/sound/soc/codecs/tlv320aic32x4-i2c.c +--- a/sound/soc/codecs/tlv320aic32x4-i2c.c ++++ b/sound/soc/codecs/tlv320aic32x4-i2c.c +@@ -1,11 +1,10 @@ +-/* SPDX-License-Identifier: GPL-2.0 +- * ++// SPDX-License-Identifier: GPL-2.0 ++/* + * Copyright 2011-2019 NW Digital Radio + * + * Author: Annaliese McDermond + * + * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27. +- * + */ + + #include +@@ -16,17 +15,23 @@ + + #include "tlv320aic32x4.h" + ++static const struct regmap_config aic32x4_i2c_regmap_config = { ++ .reg_bits = 8, ++ .val_bits = 8, ++ .max_register = AIC32X4_REFPOWERUP, ++ .ranges = aic32x4_regmap_pages, ++ .num_ranges = 1, ++}; ++ + static int aic32x4_i2c_probe(struct i2c_client *i2c) + { + struct regmap *regmap; +- struct regmap_config config; + enum aic32x4_type type; + +- config = aic32x4_regmap_config; +- config.reg_bits = 8; +- config.val_bits = 8; ++ regmap = devm_regmap_init_i2c(i2c, &aic32x4_i2c_regmap_config); ++ if (IS_ERR(regmap)) ++ return PTR_ERR(regmap); + +- regmap = devm_regmap_init_i2c(i2c, &config); + type = (uintptr_t)i2c_get_match_data(i2c); + + return aic32x4_probe(&i2c->dev, regmap, type); +diff --git a/sound/soc/codecs/tlv320aic32x4-spi.c b/sound/soc/codecs/tlv320aic32x4-spi.c +--- a/sound/soc/codecs/tlv320aic32x4-spi.c ++++ b/sound/soc/codecs/tlv320aic32x4-spi.c +@@ -1,11 +1,10 @@ +-/* SPDX-License-Identifier: GPL-2.0 +- * ++// SPDX-License-Identifier: GPL-2.0 ++/* + * Copyright 2011-2019 NW Digital Radio + * + * Author: Annaliese McDermond + * + * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27. +- * + */ + + #include +@@ -16,19 +15,25 @@ + + #include "tlv320aic32x4.h" + ++static const struct regmap_config aic32x4_spi_regmap_config = { ++ .reg_bits = 7, ++ .pad_bits = 1, ++ .val_bits = 8, ++ .read_flag_mask = 0x01, ++ .max_register = AIC32X4_REFPOWERUP, ++ .ranges = aic32x4_regmap_pages, ++ .num_ranges = 1, ++}; ++ + static int aic32x4_spi_probe(struct spi_device *spi) + { + struct regmap *regmap; +- struct regmap_config config; + enum aic32x4_type type; + +- config = aic32x4_regmap_config; +- config.reg_bits = 7; +- config.pad_bits = 1; +- config.val_bits = 8; +- config.read_flag_mask = 0x01; ++ regmap = devm_regmap_init_spi(spi, &aic32x4_spi_regmap_config); ++ if (IS_ERR(regmap)) ++ return PTR_ERR(regmap); + +- regmap = devm_regmap_init_spi(spi, &config); + type = (uintptr_t)spi_get_device_match_data(spi); + + return aic32x4_probe(&spi->dev, regmap, type); +diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c +--- a/sound/soc/codecs/tlv320aic32x4.c ++++ b/sound/soc/codecs/tlv320aic32x4.c +@@ -1,7 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0-or-later + /* +- * linux/sound/soc/codecs/tlv320aic32x4.c +- * + * Copyright 2011 Vista Silicon S.L. + * + * Author: Javier Martin +@@ -28,7 +26,6 @@ + #include + #include + #include +-#include + + #include "tlv320aic32x4.h" + +@@ -45,7 +42,8 @@ struct aic32x4_priv { + struct regulator *supply_dv; + struct regulator *supply_av; + +- struct aic32x4_setup_data *setup; ++ unsigned int gpio_func[5]; ++ + struct device *dev; + enum aic32x4_type type; + +@@ -75,7 +73,7 @@ static int aic32x4_reset_adc(struct snd_soc_dapm_widget *w, + }; + + static int mic_bias_event(struct snd_soc_dapm_widget *w, +- struct snd_kcontrol *kcontrol, int event) ++ struct snd_kcontrol *kcontrol, int event) + { + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + +@@ -83,25 +81,23 @@ static int mic_bias_event(struct snd_soc_dapm_widget *w, + case SND_SOC_DAPM_POST_PMU: + /* Change Mic Bias Registor */ + snd_soc_component_update_bits(component, AIC32X4_MICBIAS, +- AIC32x4_MICBIAS_MASK, +- AIC32X4_MICBIAS_LDOIN | +- AIC32X4_MICBIAS_2075V); +- printk(KERN_DEBUG "%s: Mic Bias will be turned ON\n", __func__); ++ AIC32x4_MICBIAS_MASK, ++ AIC32X4_MICBIAS_LDOIN | ++ AIC32X4_MICBIAS_2075V); ++ dev_dbg(component->dev, "Mic Bias will be turned ON\n"); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, AIC32X4_MICBIAS, +- AIC32x4_MICBIAS_MASK, 0); +- printk(KERN_DEBUG "%s: Mic Bias will be turned OFF\n", +- __func__); ++ AIC32x4_MICBIAS_MASK, 0); ++ dev_dbg(component->dev, "Mic Bias will be turned OFF\n"); + break; + } + + return 0; + } + +- + static int aic32x4_get_mfp1_gpio(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) ++ struct snd_ctl_elem_value *ucontrol) + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + u8 val; +@@ -114,7 +110,7 @@ static int aic32x4_get_mfp1_gpio(struct snd_kcontrol *kcontrol, + }; + + static int aic32x4_set_mfp2_gpio(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) ++ struct snd_ctl_elem_value *ucontrol) + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + u8 val; +@@ -123,8 +119,7 @@ static int aic32x4_set_mfp2_gpio(struct snd_kcontrol *kcontrol, + val = snd_soc_component_read(component, AIC32X4_DOUTCTL); + gpio_check = (val & AIC32X4_MFP_GPIO_ENABLED); + if (gpio_check != AIC32X4_MFP_GPIO_ENABLED) { +- printk(KERN_ERR "%s: MFP2 is not configure as a GPIO output\n", +- __func__); ++ dev_err(component->dev, "MFP2 is not configure as a GPIO output\n"); + return -EINVAL; + } + +@@ -142,7 +137,7 @@ static int aic32x4_set_mfp2_gpio(struct snd_kcontrol *kcontrol, + }; + + static int aic32x4_get_mfp3_gpio(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) ++ struct snd_ctl_elem_value *ucontrol) + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + u8 val; +@@ -155,7 +150,7 @@ static int aic32x4_get_mfp3_gpio(struct snd_kcontrol *kcontrol, + }; + + static int aic32x4_set_mfp4_gpio(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) ++ struct snd_ctl_elem_value *ucontrol) + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + u8 val; +@@ -164,8 +159,7 @@ static int aic32x4_set_mfp4_gpio(struct snd_kcontrol *kcontrol, + val = snd_soc_component_read(component, AIC32X4_MISOCTL); + gpio_check = (val & AIC32X4_MFP_GPIO_ENABLED); + if (gpio_check != AIC32X4_MFP_GPIO_ENABLED) { +- printk(KERN_ERR "%s: MFP4 is not configure as a GPIO output\n", +- __func__); ++ dev_err(component->dev, "MFP4 is not configure as a GPIO output\n"); + return -EINVAL; + } + +@@ -183,7 +177,7 @@ static int aic32x4_set_mfp4_gpio(struct snd_kcontrol *kcontrol, + }; + + static int aic32x4_get_mfp5_gpio(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) ++ struct snd_ctl_elem_value *ucontrol) + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + u8 val; +@@ -195,7 +189,7 @@ static int aic32x4_get_mfp5_gpio(struct snd_kcontrol *kcontrol, + }; + + static int aic32x4_set_mfp5_gpio(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) ++ struct snd_ctl_elem_value *ucontrol) + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + u8 val; +@@ -204,8 +198,7 @@ static int aic32x4_set_mfp5_gpio(struct snd_kcontrol *kcontrol, + val = snd_soc_component_read(component, AIC32X4_GPIOCTL); + gpio_check = (val & AIC32X4_MFP5_GPIO_OUTPUT); + if (gpio_check != AIC32X4_MFP5_GPIO_OUTPUT) { +- printk(KERN_ERR "%s: MFP5 is not configure as a GPIO output\n", +- __func__); ++ dev_err(component->dev, "MFP5 is not configure as a GPIO output\n"); + return -EINVAL; + } + +@@ -222,25 +215,31 @@ static int aic32x4_set_mfp5_gpio(struct snd_kcontrol *kcontrol, + return 0; + }; + +-static const struct snd_kcontrol_new aic32x4_mfp1[] = { +- SOC_SINGLE_BOOL_EXT("MFP1 GPIO", 0, aic32x4_get_mfp1_gpio, NULL), +-}; +- +-static const struct snd_kcontrol_new aic32x4_mfp2[] = { +- SOC_SINGLE_BOOL_EXT("MFP2 GPIO", 0, NULL, aic32x4_set_mfp2_gpio), +-}; +- +-static const struct snd_kcontrol_new aic32x4_mfp3[] = { +- SOC_SINGLE_BOOL_EXT("MFP3 GPIO", 0, aic32x4_get_mfp3_gpio, NULL), +-}; +- +-static const struct snd_kcontrol_new aic32x4_mfp4[] = { +- SOC_SINGLE_BOOL_EXT("MFP4 GPIO", 0, NULL, aic32x4_set_mfp4_gpio), +-}; +- +-static const struct snd_kcontrol_new aic32x4_mfp5[] = { +- SOC_SINGLE_BOOL_EXT("MFP5 GPIO", 0, aic32x4_get_mfp5_gpio, +- aic32x4_set_mfp5_gpio), ++static const struct { ++ unsigned int reg; ++ struct snd_kcontrol_new ctrl; ++} aic32x4_mfp_cfg[] = { ++ { ++ .reg = AIC32X4_DINCTL, ++ .ctrl = SOC_SINGLE_BOOL_EXT("MFP1 GPIO", 0, aic32x4_get_mfp1_gpio, NULL), ++ }, ++ { ++ .reg = AIC32X4_DOUTCTL, ++ .ctrl = SOC_SINGLE_BOOL_EXT("MFP2 GPIO", 0, NULL, aic32x4_set_mfp2_gpio), ++ }, ++ { ++ .reg = AIC32X4_SCLKCTL, ++ .ctrl = SOC_SINGLE_BOOL_EXT("MFP3 GPIO", 0, aic32x4_get_mfp3_gpio, NULL), ++ }, ++ { ++ .reg = AIC32X4_MISOCTL, ++ .ctrl = SOC_SINGLE_BOOL_EXT("MFP4 GPIO", 0, NULL, aic32x4_set_mfp4_gpio), ++ }, ++ { ++ .reg = AIC32X4_GPIOCTL, ++ .ctrl = SOC_SINGLE_BOOL_EXT("MFP5 GPIO", 0, aic32x4_get_mfp5_gpio, ++ aic32x4_set_mfp5_gpio), ++ }, + }; + + /* 0dB min, 0.5dB steps */ +@@ -270,53 +269,42 @@ static SOC_ENUM_SINGLE_DECL(r_ptm_enum, AIC32X4_RPLAYBACK, 2, ptm_text); + + static const struct snd_kcontrol_new aic32x4_snd_controls[] = { + SOC_DOUBLE_R_S_TLV("PCM Playback Volume", AIC32X4_LDACVOL, +- AIC32X4_RDACVOL, 0, -0x7f, 0x30, 7, 0, tlv_pcm), ++ AIC32X4_RDACVOL, 0, -0x7f, 0x30, 7, 0, tlv_pcm), + SOC_ENUM("DAC Left Playback PowerTune Switch", l_ptm_enum), + SOC_ENUM("DAC Right Playback PowerTune Switch", r_ptm_enum), + SOC_DOUBLE_R_S_TLV("HP Driver Gain Volume", AIC32X4_HPLGAIN, +- AIC32X4_HPRGAIN, 0, -0x6, 0x1d, 5, 0, +- tlv_driver_gain), ++ AIC32X4_HPRGAIN, 0, -0x6, 0x1d, 5, 0, tlv_driver_gain), + SOC_DOUBLE_R_S_TLV("LO Driver Gain Volume", AIC32X4_LOLGAIN, +- AIC32X4_LORGAIN, 0, -0x6, 0x1d, 5, 0, +- tlv_driver_gain), ++ AIC32X4_LORGAIN, 0, -0x6, 0x1d, 5, 0, tlv_driver_gain), + SOC_DOUBLE_R("HP DAC Playback Switch", AIC32X4_HPLGAIN, +- AIC32X4_HPRGAIN, 6, 0x01, 1), ++ AIC32X4_HPRGAIN, 6, 0x01, 1), + SOC_DOUBLE_R("LO DAC Playback Switch", AIC32X4_LOLGAIN, +- AIC32X4_LORGAIN, 6, 0x01, 1), ++ AIC32X4_LORGAIN, 6, 0x01, 1), + SOC_ENUM("LO Playback Common Mode Switch", lo_cm_enum), + SOC_DOUBLE_R("Mic PGA Switch", AIC32X4_LMICPGAVOL, +- AIC32X4_RMICPGAVOL, 7, 0x01, 1), ++ AIC32X4_RMICPGAVOL, 7, 0x01, 1), + + SOC_SINGLE("ADCFGA Left Mute Switch", AIC32X4_ADCFGA, 7, 1, 0), + SOC_SINGLE("ADCFGA Right Mute Switch", AIC32X4_ADCFGA, 3, 1, 0), + + SOC_DOUBLE_R_S_TLV("ADC Level Volume", AIC32X4_LADCVOL, +- AIC32X4_RADCVOL, 0, -0x18, 0x28, 6, 0, tlv_adc_vol), ++ AIC32X4_RADCVOL, 0, -0x18, 0x28, 6, 0, tlv_adc_vol), + SOC_DOUBLE_R_TLV("PGA Level Volume", AIC32X4_LMICPGAVOL, +- AIC32X4_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5), ++ AIC32X4_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5), + + SOC_SINGLE("Auto-mute Switch", AIC32X4_DACMUTE, 4, 7, 0), + + SOC_SINGLE("AGC Left Switch", AIC32X4_LAGC1, 7, 1, 0), + SOC_SINGLE("AGC Right Switch", AIC32X4_RAGC1, 7, 1, 0), +- SOC_DOUBLE_R("AGC Target Level", AIC32X4_LAGC1, AIC32X4_RAGC1, +- 4, 0x07, 0), +- SOC_DOUBLE_R("AGC Gain Hysteresis", AIC32X4_LAGC1, AIC32X4_RAGC1, +- 0, 0x03, 0), +- SOC_DOUBLE_R("AGC Hysteresis", AIC32X4_LAGC2, AIC32X4_RAGC2, +- 6, 0x03, 0), +- SOC_DOUBLE_R("AGC Noise Threshold", AIC32X4_LAGC2, AIC32X4_RAGC2, +- 1, 0x1F, 0), +- SOC_DOUBLE_R("AGC Max PGA", AIC32X4_LAGC3, AIC32X4_RAGC3, +- 0, 0x7F, 0), +- SOC_DOUBLE_R("AGC Attack Time", AIC32X4_LAGC4, AIC32X4_RAGC4, +- 3, 0x1F, 0), +- SOC_DOUBLE_R("AGC Decay Time", AIC32X4_LAGC5, AIC32X4_RAGC5, +- 3, 0x1F, 0), +- SOC_DOUBLE_R("AGC Noise Debounce", AIC32X4_LAGC6, AIC32X4_RAGC6, +- 0, 0x1F, 0), +- SOC_DOUBLE_R("AGC Signal Debounce", AIC32X4_LAGC7, AIC32X4_RAGC7, +- 0, 0x0F, 0), ++ SOC_DOUBLE_R("AGC Target Level", AIC32X4_LAGC1, AIC32X4_RAGC1, 4, 0x07, 0), ++ SOC_DOUBLE_R("AGC Gain Hysteresis", AIC32X4_LAGC1, AIC32X4_RAGC1, 0, 0x03, 0), ++ SOC_DOUBLE_R("AGC Hysteresis", AIC32X4_LAGC2, AIC32X4_RAGC2, 6, 0x03, 0), ++ SOC_DOUBLE_R("AGC Noise Threshold", AIC32X4_LAGC2, AIC32X4_RAGC2, 1, 0x1F, 0), ++ SOC_DOUBLE_R("AGC Max PGA", AIC32X4_LAGC3, AIC32X4_RAGC3, 0, 0x7F, 0), ++ SOC_DOUBLE_R("AGC Attack Time", AIC32X4_LAGC4, AIC32X4_RAGC4, 3, 0x1F, 0), ++ SOC_DOUBLE_R("AGC Decay Time", AIC32X4_LAGC5, AIC32X4_RAGC5, 3, 0x1F, 0), ++ SOC_DOUBLE_R("AGC Noise Debounce", AIC32X4_LAGC6, AIC32X4_RAGC6, 0, 0x1F, 0), ++ SOC_DOUBLE_R("AGC Signal Debounce", AIC32X4_LAGC7, AIC32X4_RAGC7, 0, 0x0F, 0), + }; + + static const struct snd_kcontrol_new hpl_output_mixer_controls[] = { +@@ -354,21 +342,27 @@ static SOC_ENUM_SINGLE_DECL(in3r_lpga_n_enum, AIC32X4_LMICPGANIN, 2, resistor_te + static const struct snd_kcontrol_new in1l_to_lmixer_controls[] = { + SOC_DAPM_ENUM("IN1_L L+ Switch", in1l_lpga_p_enum), + }; ++ + static const struct snd_kcontrol_new in2l_to_lmixer_controls[] = { + SOC_DAPM_ENUM("IN2_L L+ Switch", in2l_lpga_p_enum), + }; ++ + static const struct snd_kcontrol_new in3l_to_lmixer_controls[] = { + SOC_DAPM_ENUM("IN3_L L+ Switch", in3l_lpga_p_enum), + }; ++ + static const struct snd_kcontrol_new in1r_to_lmixer_controls[] = { + SOC_DAPM_ENUM("IN1_R L+ Switch", in1r_lpga_p_enum), + }; ++ + static const struct snd_kcontrol_new cml_to_lmixer_controls[] = { + SOC_DAPM_ENUM("CM_L L- Switch", cml_lpga_n_enum), + }; ++ + static const struct snd_kcontrol_new in2r_to_lmixer_controls[] = { + SOC_DAPM_ENUM("IN2_R L- Switch", in2r_lpga_n_enum), + }; ++ + static const struct snd_kcontrol_new in3r_to_lmixer_controls[] = { + SOC_DAPM_ENUM("IN3_R L- Switch", in3r_lpga_n_enum), + }; +@@ -385,21 +379,27 @@ static SOC_ENUM_SINGLE_DECL(in3l_rpga_n_enum, AIC32X4_RMICPGANIN, 2, resistor_te + static const struct snd_kcontrol_new in1r_to_rmixer_controls[] = { + SOC_DAPM_ENUM("IN1_R R+ Switch", in1r_rpga_p_enum), + }; ++ + static const struct snd_kcontrol_new in2r_to_rmixer_controls[] = { + SOC_DAPM_ENUM("IN2_R R+ Switch", in2r_rpga_p_enum), + }; ++ + static const struct snd_kcontrol_new in3r_to_rmixer_controls[] = { + SOC_DAPM_ENUM("IN3_R R+ Switch", in3r_rpga_p_enum), + }; ++ + static const struct snd_kcontrol_new in2l_to_rmixer_controls[] = { + SOC_DAPM_ENUM("IN2_L R+ Switch", in2l_rpga_p_enum), + }; ++ + static const struct snd_kcontrol_new cmr_to_rmixer_controls[] = { + SOC_DAPM_ENUM("CM_R R- Switch", cmr_rpga_n_enum), + }; ++ + static const struct snd_kcontrol_new in1l_to_rmixer_controls[] = { + SOC_DAPM_ENUM("IN1_L R- Switch", in1l_rpga_n_enum), + }; ++ + static const struct snd_kcontrol_new in3l_to_rmixer_controls[] = { + SOC_DAPM_ENUM("IN3_L R- Switch", in3l_rpga_n_enum), + }; +@@ -428,38 +428,38 @@ static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = { + + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", AIC32X4_ADCSETUP, 6, 0), + SND_SOC_DAPM_MUX("IN1_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, +- in1r_to_rmixer_controls), ++ in1r_to_rmixer_controls), + SND_SOC_DAPM_MUX("IN2_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, +- in2r_to_rmixer_controls), ++ in2r_to_rmixer_controls), + SND_SOC_DAPM_MUX("IN3_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, +- in3r_to_rmixer_controls), ++ in3r_to_rmixer_controls), + SND_SOC_DAPM_MUX("IN2_L to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, +- in2l_to_rmixer_controls), ++ in2l_to_rmixer_controls), + SND_SOC_DAPM_MUX("CM_R to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, +- cmr_to_rmixer_controls), ++ cmr_to_rmixer_controls), + SND_SOC_DAPM_MUX("IN1_L to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, +- in1l_to_rmixer_controls), ++ in1l_to_rmixer_controls), + SND_SOC_DAPM_MUX("IN3_L to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, +- in3l_to_rmixer_controls), ++ in3l_to_rmixer_controls), + + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", AIC32X4_ADCSETUP, 7, 0), + SND_SOC_DAPM_MUX("IN1_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, +- in1l_to_lmixer_controls), ++ in1l_to_lmixer_controls), + SND_SOC_DAPM_MUX("IN2_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, +- in2l_to_lmixer_controls), ++ in2l_to_lmixer_controls), + SND_SOC_DAPM_MUX("IN3_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, +- in3l_to_lmixer_controls), ++ in3l_to_lmixer_controls), + SND_SOC_DAPM_MUX("IN1_R to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, +- in1r_to_lmixer_controls), ++ in1r_to_lmixer_controls), + SND_SOC_DAPM_MUX("CM_L to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, +- cml_to_lmixer_controls), ++ cml_to_lmixer_controls), + SND_SOC_DAPM_MUX("IN2_R to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, +- in2r_to_lmixer_controls), ++ in2r_to_lmixer_controls), + SND_SOC_DAPM_MUX("IN3_R to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, +- in3r_to_lmixer_controls), ++ in3r_to_lmixer_controls), + + SND_SOC_DAPM_SUPPLY("Mic Bias", AIC32X4_MICBIAS, 6, 0, mic_bias_event, +- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), ++ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_POST("ADC Reset", aic32x4_reset_adc), + +@@ -575,7 +575,7 @@ static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = { + {"IN3_R to Left Mixer Negative Resistor", "40 kOhm", "IN3_R"}, + }; + +-static const struct regmap_range_cfg aic32x4_regmap_pages[] = { ++const struct regmap_range_cfg aic32x4_regmap_pages[] = { + { + .selector_reg = 0, + .selector_mask = 0xff, +@@ -585,13 +585,7 @@ static const struct regmap_range_cfg aic32x4_regmap_pages[] = { + .range_max = AIC32X4_REFPOWERUP, + }, + }; +- +-const struct regmap_config aic32x4_regmap_config = { +- .max_register = AIC32X4_REFPOWERUP, +- .ranges = aic32x4_regmap_pages, +- .num_ranges = ARRAY_SIZE(aic32x4_regmap_pages), +-}; +-EXPORT_SYMBOL(aic32x4_regmap_config); ++EXPORT_SYMBOL_GPL(aic32x4_regmap_pages); + + static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +@@ -624,7 +618,7 @@ static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: +- printk(KERN_ERR "aic32x4: invalid clock provider\n"); ++ dev_err(component->dev, "invalid clock provider\n"); + return -EINVAL; + } + +@@ -651,19 +645,20 @@ static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) + AIC32X4_IFACE1_DATATYPE_SHIFT); + break; + default: +- printk(KERN_ERR "aic32x4: invalid DAI interface format\n"); ++ dev_err(component->dev, "invalid DAI interface format\n"); + return -EINVAL; + } + + aic32x4->fmt = fmt; + + snd_soc_component_update_bits(component, AIC32X4_IFACE1, +- AIC32X4_IFACE1_DATATYPE_MASK | +- AIC32X4_IFACE1_MASTER_MASK, iface_reg_1); ++ AIC32X4_IFACE1_DATATYPE_MASK | ++ AIC32X4_IFACE1_MASTER_MASK, ++ iface_reg_1); + snd_soc_component_update_bits(component, AIC32X4_IFACE2, +- AIC32X4_DATA_OFFSET_MASK, iface_reg_2); ++ AIC32X4_DATA_OFFSET_MASK, iface_reg_2); + snd_soc_component_update_bits(component, AIC32X4_IFACE3, +- AIC32X4_BCLKINV_MASK, iface_reg_3); ++ AIC32X4_BCLKINV_MASK, iface_reg_3); + + return 0; + } +@@ -676,14 +671,13 @@ static int aic32x4_set_aosr(struct snd_soc_component *component, u8 aosr) + static int aic32x4_set_dosr(struct snd_soc_component *component, u16 dosr) + { + snd_soc_component_write(component, AIC32X4_DOSRMSB, dosr >> 8); +- snd_soc_component_write(component, AIC32X4_DOSRLSB, +- (dosr & 0xff)); ++ snd_soc_component_write(component, AIC32X4_DOSRLSB, dosr & 0xff); + + return 0; + } + + static int aic32x4_set_processing_blocks(struct snd_soc_component *component, +- u8 r_block, u8 p_block) ++ u8 r_block, u8 p_block) + { + struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); + +@@ -703,6 +697,45 @@ static int aic32x4_set_processing_blocks(struct snd_soc_component *component, + return 0; + } + ++static int aic32x4_configure_rate(struct snd_soc_component *component, ++ unsigned int rate, u8 *aosr, u8 *adc_rc, ++ u8 *dac_rc, u8 *dosr_inc) ++{ ++ struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); ++ u8 prb_rx, prb_tx; ++ ++ if (rate <= 48000) { ++ *aosr = 128; ++ *adc_rc = 6; ++ *dac_rc = 8; ++ *dosr_inc = 8; ++ prb_rx = 1; ++ prb_tx = 1; ++ } else if (rate <= 96000) { ++ *aosr = 64; ++ *adc_rc = 6; ++ *dac_rc = 8; ++ *dosr_inc = 4; ++ prb_rx = 1; ++ prb_tx = (aic32x4->type == AIC32X4_TYPE_TAS2505) ? 1 : 9; ++ } else if (rate == 192000) { ++ *aosr = 32; ++ *adc_rc = 3; ++ *dac_rc = 4; ++ *dosr_inc = 2; ++ prb_rx = 13; ++ prb_tx = (aic32x4->type == AIC32X4_TYPE_TAS2505) ? 1 : 19; ++ } else { ++ dev_err(component->dev, "Sampling rate %u not supported\n", rate); ++ return -EINVAL; ++ } ++ ++ if (aic32x4->type == AIC32X4_TYPE_TAS2505) ++ prb_rx = 0; ++ ++ return aic32x4_set_processing_blocks(component, prb_rx, prb_tx); ++} ++ + static int aic32x4_setup_clocks(struct snd_soc_component *component, + unsigned int sample_rate, unsigned int channels, + unsigned int bit_depth) +@@ -717,7 +750,7 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, + unsigned long adc_clock_rate, dac_clock_rate; + int ret; + +- static struct clk_bulk_data clocks[] = { ++ struct clk_bulk_data clocks[] = { + { .id = "pll" }, + { .id = "nadc" }, + { .id = "madc" }, +@@ -729,37 +762,11 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, + if (ret) + return ret; + +- if (sample_rate <= 48000) { +- aosr = 128; +- adc_resource_class = 6; +- dac_resource_class = 8; +- dosr_increment = 8; +- if (aic32x4->type == AIC32X4_TYPE_TAS2505) +- aic32x4_set_processing_blocks(component, 0, 1); +- else +- aic32x4_set_processing_blocks(component, 1, 1); +- } else if (sample_rate <= 96000) { +- aosr = 64; +- adc_resource_class = 6; +- dac_resource_class = 8; +- dosr_increment = 4; +- if (aic32x4->type == AIC32X4_TYPE_TAS2505) +- aic32x4_set_processing_blocks(component, 0, 1); +- else +- aic32x4_set_processing_blocks(component, 1, 9); +- } else if (sample_rate == 192000) { +- aosr = 32; +- adc_resource_class = 3; +- dac_resource_class = 4; +- dosr_increment = 2; +- if (aic32x4->type == AIC32X4_TYPE_TAS2505) +- aic32x4_set_processing_blocks(component, 0, 1); +- else +- aic32x4_set_processing_blocks(component, 13, 19); +- } else { +- dev_err(component->dev, "Sampling rate not supported\n"); +- return -EINVAL; +- } ++ ret = aic32x4_configure_rate(component, sample_rate, &aosr, ++ &adc_resource_class, &dac_resource_class, ++ &dosr_increment); ++ if (ret) ++ return ret; + + /* PCM over I2S is always 2-channel */ + if ((aic32x4->fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S) +@@ -781,50 +788,39 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, + (min_mdac * dosr * sample_rate); + for (mdac = min_mdac; mdac <= 128; ++mdac) { + for (ndac = max_ndac; ndac > 0; --ndac) { +- dac_clock_rate = ndac * mdac * dosr * +- sample_rate; +- if (dac_clock_rate == adc_clock_rate) { +- if (clk_round_rate(clocks[0].clk, dac_clock_rate) == 0) +- continue; ++ dac_clock_rate = ndac * mdac * dosr * sample_rate; ++ if (dac_clock_rate != adc_clock_rate) ++ continue; + +- clk_set_rate(clocks[0].clk, +- dac_clock_rate); ++ if (clk_round_rate(clocks[0].clk, dac_clock_rate) == 0) ++ continue; + +- clk_set_rate(clocks[1].clk, +- sample_rate * aosr * +- madc); +- clk_set_rate(clocks[2].clk, +- sample_rate * aosr); +- aic32x4_set_aosr(component, +- aosr); ++ clk_set_rate(clocks[0].clk, dac_clock_rate); + +- clk_set_rate(clocks[3].clk, +- sample_rate * dosr * +- mdac); +- clk_set_rate(clocks[4].clk, +- sample_rate * dosr); +- aic32x4_set_dosr(component, +- dosr); ++ clk_set_rate(clocks[1].clk, sample_rate * aosr * madc); ++ clk_set_rate(clocks[2].clk, sample_rate * aosr); ++ aic32x4_set_aosr(component, aosr); + +- clk_set_rate(clocks[5].clk, +- sample_rate * channels * +- bit_depth); ++ clk_set_rate(clocks[3].clk, sample_rate * dosr * mdac); ++ clk_set_rate(clocks[4].clk, sample_rate * dosr); ++ aic32x4_set_dosr(component, dosr); + +- return 0; +- } ++ clk_set_rate(clocks[5].clk, ++ sample_rate * channels * bit_depth); ++ ++ return 0; + } + } + } + } + +- dev_err(component->dev, +- "Could not set clocks to support sample rate.\n"); ++ dev_err(component->dev, "Could not set clocks to support sample rate.\n"); + return -EINVAL; + } + + static int aic32x4_hw_params(struct snd_pcm_substream *substream, +- struct snd_pcm_hw_params *params, +- struct snd_soc_dai *dai) ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) + { + struct snd_soc_component *component = dai->component; + struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); +@@ -837,24 +833,20 @@ static int aic32x4_hw_params(struct snd_pcm_substream *substream, + + switch (params_physical_width(params)) { + case 16: +- iface1_reg |= (AIC32X4_WORD_LEN_16BITS << +- AIC32X4_IFACE1_DATALEN_SHIFT); ++ iface1_reg |= (AIC32X4_WORD_LEN_16BITS << AIC32X4_IFACE1_DATALEN_SHIFT); + break; + case 20: +- iface1_reg |= (AIC32X4_WORD_LEN_20BITS << +- AIC32X4_IFACE1_DATALEN_SHIFT); ++ iface1_reg |= (AIC32X4_WORD_LEN_20BITS << AIC32X4_IFACE1_DATALEN_SHIFT); + break; + case 24: +- iface1_reg |= (AIC32X4_WORD_LEN_24BITS << +- AIC32X4_IFACE1_DATALEN_SHIFT); ++ iface1_reg |= (AIC32X4_WORD_LEN_24BITS << AIC32X4_IFACE1_DATALEN_SHIFT); + break; + case 32: +- iface1_reg |= (AIC32X4_WORD_LEN_32BITS << +- AIC32X4_IFACE1_DATALEN_SHIFT); ++ iface1_reg |= (AIC32X4_WORD_LEN_32BITS << AIC32X4_IFACE1_DATALEN_SHIFT); + break; + } + snd_soc_component_update_bits(component, AIC32X4_IFACE1, +- AIC32X4_IFACE1_DATALEN_MASK, iface1_reg); ++ AIC32X4_IFACE1_DATALEN_MASK, iface1_reg); + + if (params_channels(params) == 1) { + dacsetup_reg = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2LCHN; +@@ -865,7 +857,7 @@ static int aic32x4_hw_params(struct snd_pcm_substream *substream, + dacsetup_reg = AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN; + } + snd_soc_component_update_bits(component, AIC32X4_DACSETUP, +- AIC32X4_DAC_CHAN_MASK, dacsetup_reg); ++ AIC32X4_DAC_CHAN_MASK, dacsetup_reg); + + return 0; + } +@@ -875,7 +867,7 @@ static int aic32x4_mute(struct snd_soc_dai *dai, int mute, int direction) + struct snd_soc_component *component = dai->component; + + snd_soc_component_update_bits(component, AIC32X4_DACMUTE, +- AIC32X4_MUTEON, mute ? AIC32X4_MUTEON : 0); ++ AIC32X4_MUTEON, mute ? AIC32X4_MUTEON : 0); + + return 0; + } +@@ -886,7 +878,7 @@ static int aic32x4_set_bias_level(struct snd_soc_component *component, + struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); + int ret; + +- static struct clk_bulk_data clocks[] = { ++ struct clk_bulk_data clocks[] = { + { .id = "madc" }, + { .id = "mdac" }, + { .id = "bdiv" }, +@@ -955,44 +947,14 @@ static void aic32x4_setup_gpios(struct snd_soc_component *component) + struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); + + /* setup GPIO functions */ +- /* MFP1 */ +- if (aic32x4->setup->gpio_func[0] != AIC32X4_MFPX_DEFAULT_VALUE) { +- snd_soc_component_write(component, AIC32X4_DINCTL, +- aic32x4->setup->gpio_func[0]); +- snd_soc_add_component_controls(component, aic32x4_mfp1, +- ARRAY_SIZE(aic32x4_mfp1)); +- } ++ BUILD_BUG_ON(ARRAY_SIZE(aic32x4->gpio_func) != ARRAY_SIZE(aic32x4_mfp_cfg)); ++ for (int i = 0; i < ARRAY_SIZE(aic32x4->gpio_func); i++) { ++ if (aic32x4->gpio_func[i] == AIC32X4_MFPX_DEFAULT_VALUE) ++ continue; + +- /* MFP2 */ +- if (aic32x4->setup->gpio_func[1] != AIC32X4_MFPX_DEFAULT_VALUE) { +- snd_soc_component_write(component, AIC32X4_DOUTCTL, +- aic32x4->setup->gpio_func[1]); +- snd_soc_add_component_controls(component, aic32x4_mfp2, +- ARRAY_SIZE(aic32x4_mfp2)); +- } +- +- /* MFP3 */ +- if (aic32x4->setup->gpio_func[2] != AIC32X4_MFPX_DEFAULT_VALUE) { +- snd_soc_component_write(component, AIC32X4_SCLKCTL, +- aic32x4->setup->gpio_func[2]); +- snd_soc_add_component_controls(component, aic32x4_mfp3, +- ARRAY_SIZE(aic32x4_mfp3)); +- } +- +- /* MFP4 */ +- if (aic32x4->setup->gpio_func[3] != AIC32X4_MFPX_DEFAULT_VALUE) { +- snd_soc_component_write(component, AIC32X4_MISOCTL, +- aic32x4->setup->gpio_func[3]); +- snd_soc_add_component_controls(component, aic32x4_mfp4, +- ARRAY_SIZE(aic32x4_mfp4)); +- } +- +- /* MFP5 */ +- if (aic32x4->setup->gpio_func[4] != AIC32X4_MFPX_DEFAULT_VALUE) { +- snd_soc_component_write(component, AIC32X4_GPIOCTL, +- aic32x4->setup->gpio_func[4]); +- snd_soc_add_component_controls(component, aic32x4_mfp5, +- ARRAY_SIZE(aic32x4_mfp5)); ++ snd_soc_component_write(component, aic32x4_mfp_cfg[i].reg, ++ aic32x4->gpio_func[i]); ++ snd_soc_add_component_controls(component, &aic32x4_mfp_cfg[i].ctrl, 1); + } + } + +@@ -1002,7 +964,7 @@ static int aic32x4_component_probe(struct snd_soc_component *component) + u32 tmp_reg; + int ret; + +- static struct clk_bulk_data clocks[] = { ++ struct clk_bulk_data clocks[] = { + { .id = "codec_clkin" }, + { .id = "pll" }, + { .id = "bdiv" }, +@@ -1013,8 +975,7 @@ static int aic32x4_component_probe(struct snd_soc_component *component) + if (ret) + return ret; + +- if (aic32x4->setup) +- aic32x4_setup_gpios(component); ++ aic32x4_setup_gpios(component); + + clk_set_parent(clocks[0].clk, clocks[1].clk); + clk_set_parent(clocks[2].clk, clocks[3].clk); +@@ -1022,7 +983,7 @@ static int aic32x4_component_probe(struct snd_soc_component *component) + /* Power platform configuration */ + if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) { + snd_soc_component_write(component, AIC32X4_MICBIAS, +- AIC32X4_MICBIAS_LDOIN | AIC32X4_MICBIAS_2075V); ++ AIC32X4_MICBIAS_LDOIN | AIC32X4_MICBIAS_2075V); + } + if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE) + snd_soc_component_write(component, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE); +@@ -1041,16 +1002,16 @@ static int aic32x4_component_probe(struct snd_soc_component *component) + /* Mic PGA routing */ + if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K) + snd_soc_component_write(component, AIC32X4_LMICPGANIN, +- AIC32X4_LMICPGANIN_IN2R_10K); ++ AIC32X4_LMICPGANIN_IN2R_10K); + else + snd_soc_component_write(component, AIC32X4_LMICPGANIN, +- AIC32X4_LMICPGANIN_CM1L_10K); ++ AIC32X4_LMICPGANIN_CM1L_10K); + if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K) + snd_soc_component_write(component, AIC32X4_RMICPGANIN, +- AIC32X4_RMICPGANIN_IN1L_10K); ++ AIC32X4_RMICPGANIN_IN1L_10K); + else + snd_soc_component_write(component, AIC32X4_RMICPGANIN, +- AIC32X4_RMICPGANIN_CM1R_10K); ++ AIC32X4_RMICPGANIN_CM1R_10K); + + /* + * Workaround: for an unknown reason, the ADC needs to be powered up +@@ -1058,8 +1019,8 @@ static int aic32x4_component_probe(struct snd_soc_component *component) + * a HW BUG or some kind of behavior not documented in the datasheet. + */ + tmp_reg = snd_soc_component_read(component, AIC32X4_ADCSETUP); +- snd_soc_component_write(component, AIC32X4_ADCSETUP, tmp_reg | +- AIC32X4_LADC_EN | AIC32X4_RADC_EN); ++ snd_soc_component_write(component, AIC32X4_ADCSETUP, ++ tmp_reg | AIC32X4_LADC_EN | AIC32X4_RADC_EN); + snd_soc_component_write(component, AIC32X4_ADCSETUP, tmp_reg); + + /* +@@ -1102,13 +1063,13 @@ static const struct snd_kcontrol_new aic32x4_tas2505_snd_controls[] = { + SOC_ENUM("DAC Playback PowerTune Switch", l_ptm_enum), + + SOC_SINGLE_TLV("HP Driver Gain Volume", +- AIC32X4_HPLGAIN, 0, 0x74, 1, tlv_tas_driver_gain), ++ AIC32X4_HPLGAIN, 0, 0x74, 1, tlv_tas_driver_gain), + SOC_SINGLE("HP DAC Playback Switch", AIC32X4_HPLGAIN, 6, 1, 1), + + SOC_SINGLE_TLV("Speaker Driver Playback Volume", +- TAS2505_SPKVOL1, 0, 0x74, 1, tlv_tas_driver_gain), ++ TAS2505_SPKVOL1, 0, 0x74, 1, tlv_tas_driver_gain), + SOC_SINGLE_TLV("Speaker Amplifier Playback Volume", +- TAS2505_SPKVOL2, 4, 5, 0, tlv_amp_vol), ++ TAS2505_SPKVOL2, 4, 5, 0, tlv_amp_vol), + + SOC_SINGLE("Auto-mute Switch", AIC32X4_DACMUTE, 4, 7, 0), + }; +@@ -1144,11 +1105,12 @@ static const struct snd_soc_dapm_route aic32x4_tas2505_dapm_routes[] = { + static struct snd_soc_dai_driver aic32x4_tas2505_dai = { + .name = "tas2505-hifi", + .playback = { +- .stream_name = "Playback", +- .channels_min = 1, +- .channels_max = 2, +- .rates = SNDRV_PCM_RATE_8000_96000, +- .formats = AIC32X4_FORMATS,}, ++ .stream_name = "Playback", ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_96000, ++ .formats = AIC32X4_FORMATS, ++ }, + .ops = &aic32x4_ops, + .symmetric_rate = 1, + }; +@@ -1159,7 +1121,7 @@ static int aic32x4_tas2505_component_probe(struct snd_soc_component *component) + u32 tmp_reg; + int ret; + +- static struct clk_bulk_data clocks[] = { ++ struct clk_bulk_data clocks[] = { + { .id = "codec_clkin" }, + { .id = "pll" }, + { .id = "bdiv" }, +@@ -1170,8 +1132,7 @@ static int aic32x4_tas2505_component_probe(struct snd_soc_component *component) + if (ret) + return ret; + +- if (aic32x4->setup) +- aic32x4_setup_gpios(component); ++ aic32x4_setup_gpios(component); + + clk_set_parent(clocks[0].clk, clocks[1].clk); + clk_set_parent(clocks[2].clk, clocks[3].clk); +@@ -1192,7 +1153,7 @@ static int aic32x4_tas2505_component_probe(struct snd_soc_component *component) + snd_soc_component_write(component, AIC32X4_CMMODE, tmp_reg); + + /* +- * Enable the fast charging feature and ensure the needed 40ms ellapsed ++ * Enable the fast charging feature and ensure the needed 40ms elapsed + * before using the analog circuits. + */ + snd_soc_component_write(component, TAS2505_REFPOWERUP, +@@ -1218,17 +1179,10 @@ static const struct snd_soc_component_driver soc_component_dev_aic32x4_tas2505 = + .endianness = 1, + }; + +-static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, +- struct device_node *np) ++static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, struct device_node *np) + { +- struct aic32x4_setup_data *aic32x4_setup; + int ret; + +- aic32x4_setup = devm_kzalloc(aic32x4->dev, sizeof(*aic32x4_setup), +- GFP_KERNEL); +- if (!aic32x4_setup) +- return -ENOMEM; +- + ret = of_property_match_string(np, "clock-names", "mclk"); + if (ret < 0) + return -EINVAL; +@@ -1245,9 +1199,11 @@ static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, + gpiod_set_consumer_name(aic32x4->rstn_gpio, "tlv320aic32x4_rstn"); + } + +- if (of_property_read_u32_array(np, "aic32x4-gpio-func", +- aic32x4_setup->gpio_func, 5) >= 0) +- aic32x4->setup = aic32x4_setup; ++ for (int i = 0; i < ARRAY_SIZE(aic32x4->gpio_func); i++) ++ aic32x4->gpio_func[i] = AIC32X4_MFPX_DEFAULT_VALUE; ++ of_property_read_u32_array(np, "aic32x4-gpio-func", ++ aic32x4->gpio_func, ARRAY_SIZE(aic32x4->gpio_func)); ++ + return 0; + } + +@@ -1266,7 +1222,7 @@ static void aic32x4_disable_regulators(struct aic32x4_priv *aic32x4) + } + + static int aic32x4_setup_regulators(struct device *dev, +- struct aic32x4_priv *aic32x4) ++ struct aic32x4_priv *aic32x4) + { + int ret = 0; + +@@ -1356,12 +1312,8 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap, + struct device_node *np = dev->of_node; + int ret; + +- if (IS_ERR(regmap)) +- return PTR_ERR(regmap); +- +- aic32x4 = devm_kzalloc(dev, sizeof(struct aic32x4_priv), +- GFP_KERNEL); +- if (aic32x4 == NULL) ++ aic32x4 = devm_kzalloc(dev, sizeof(struct aic32x4_priv), GFP_KERNEL); ++ if (!aic32x4) + return -ENOMEM; + + aic32x4->dev = dev; +@@ -1405,11 +1357,13 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap, + switch (aic32x4->type) { + case AIC32X4_TYPE_TAS2505: + ret = devm_snd_soc_register_component(dev, +- &soc_component_dev_aic32x4_tas2505, &aic32x4_tas2505_dai, 1); ++ &soc_component_dev_aic32x4_tas2505, ++ &aic32x4_tas2505_dai, 1); + break; + default: + ret = devm_snd_soc_register_component(dev, +- &soc_component_dev_aic32x4, &aic32x4_dai, 1); ++ &soc_component_dev_aic32x4, ++ &aic32x4_dai, 1); + } + + if (ret) { +diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h +--- a/sound/soc/codecs/tlv320aic32x4.h ++++ b/sound/soc/codecs/tlv320aic32x4.h +@@ -3,7 +3,6 @@ + * tlv320aic32x4.h + */ + +- + #ifndef _TLV320AIC32X4_H + #define _TLV320AIC32X4_H + +@@ -16,7 +15,7 @@ enum aic32x4_type { + AIC32X4_TYPE_TAS2505, + }; + +-extern const struct regmap_config aic32x4_regmap_config; ++extern const struct regmap_range_cfg aic32x4_regmap_pages[]; + int aic32x4_probe(struct device *dev, struct regmap *regmap, + enum aic32x4_type type); + void aic32x4_remove(struct device *dev); +@@ -24,7 +23,7 @@ int aic32x4_register_clocks(struct device *dev, const char *mclk_name); + + /* tlv320aic32x4 register space (in decimal to match datasheet) */ + +-#define AIC32X4_REG(page, reg) ((page * 128) + reg) ++#define AIC32X4_REG(page, reg) (((page) * 128) + (reg)) + + #define AIC32X4_PSEL AIC32X4_REG(0, 0) + +@@ -234,4 +233,31 @@ int aic32x4_register_clocks(struct device *dev, const char *mclk_name); + #define AIC32X4_MAX_CODEC_CLKIN_FREQ 110000000 + #define AIC32X4_MAX_PLL_CLKIN 20000000 + ++#define AIC32X4_PWR_MICBIAS_2075_LDOIN 0x00000001 ++#define AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE 0x00000002 ++#define AIC32X4_PWR_AIC32X4_LDO_ENABLE 0x00000004 ++#define AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36 0x00000008 ++#define AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED 0x00000010 ++ ++#define AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K 0x00000001 ++#define AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K 0x00000002 ++ ++/* GPIO API */ ++#define AIC32X4_MFPX_DEFAULT_VALUE 0xff ++ ++#define AIC32X4_MFP1_DIN_DISABLED 0 ++#define AIC32X4_MFP1_DIN_ENABLED 0x2 ++#define AIC32X4_MFP1_GPIO_IN 0x4 ++ ++#define AIC32X4_MFP2_GPIO_OUT_LOW 0x0 ++#define AIC32X4_MFP2_GPIO_OUT_HIGH 0x1 ++ ++#define AIC32X4_MFP_GPIO_ENABLED 0x4 ++ ++#define AIC32X4_MFP5_GPIO_DISABLED 0x0 ++#define AIC32X4_MFP5_GPIO_INPUT 0x8 ++#define AIC32X4_MFP5_GPIO_OUTPUT 0xc ++#define AIC32X4_MFP5_GPIO_OUT_LOW 0x0 ++#define AIC32X4_MFP5_GPIO_OUT_HIGH 0x1 ++ + #endif /* _TLV320AIC32X4_H */ +diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c +--- a/sound/soc/codecs/tlv320dac33.c ++++ b/sound/soc/codecs/tlv320dac33.c +@@ -7,6 +7,7 @@ + * Copyright: (C) 2009 Nokia Corporation + */ + ++#include + #include + #include + #include +@@ -236,13 +237,10 @@ static int dac33_write_locked(struct snd_soc_component *component, unsigned int + unsigned int value) + { + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); +- int ret; + +- mutex_lock(&dac33->mutex); +- ret = dac33_write(component, reg, value); +- mutex_unlock(&dac33->mutex); ++ guard(mutex)(&dac33->mutex); + +- return ret; ++ return dac33_write(component, reg, value); + } + + #define DAC33_I2C_ADDR_AUTOINC 0x80 +@@ -365,13 +363,13 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); + int ret = 0; + +- mutex_lock(&dac33->mutex); ++ guard(mutex)(&dac33->mutex); + + /* Safety check */ + if (unlikely(power == dac33->chip_power)) { + dev_dbg(component->dev, "Trying to set the same power state: %s\n", + power ? "ON" : "OFF"); +- goto exit; ++ return ret; + } + + if (power) { +@@ -380,7 +378,7 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) + if (ret != 0) { + dev_err(component->dev, + "Failed to enable supplies: %d\n", ret); +- goto exit; ++ return ret; + } + + if (dac33->reset_gpiod) { +@@ -388,7 +386,7 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) + if (ret < 0) { + dev_err(&dac33->i2c->dev, + "Failed to set reset GPIO: %d\n", ret); +- goto exit; ++ return ret; + } + } + +@@ -400,7 +398,7 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) + if (ret < 0) { + dev_err(&dac33->i2c->dev, + "Failed to set reset GPIO: %d\n", ret); +- goto exit; ++ return ret; + } + } + +@@ -409,14 +407,12 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) + if (ret != 0) { + dev_err(component->dev, + "Failed to disable supplies: %d\n", ret); +- goto exit; ++ return ret; + } + + dac33->chip_power = 0; + } + +-exit: +- mutex_unlock(&dac33->mutex); + return ret; + } + +@@ -659,7 +655,6 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) + { + struct snd_soc_component *component = dac33->component; + unsigned int delay; +- unsigned long flags; + + switch (dac33->fifo_mode) { + case DAC33_FIFO_MODE1: +@@ -667,10 +662,10 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) + DAC33_THRREG(dac33->nsample)); + + /* Take the timestamps */ +- spin_lock_irqsave(&dac33->lock, flags); +- dac33->t_stamp2 = ktime_to_us(ktime_get()); +- dac33->t_stamp1 = dac33->t_stamp2; +- spin_unlock_irqrestore(&dac33->lock, flags); ++ scoped_guard(spinlock_irqsave, &dac33->lock) { ++ dac33->t_stamp2 = ktime_to_us(ktime_get()); ++ dac33->t_stamp1 = dac33->t_stamp2; ++ } + + dac33_write16(component, DAC33_PREFILL_MSB, + DAC33_THRREG(dac33->alarm_threshold)); +@@ -682,11 +677,11 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) + break; + case DAC33_FIFO_MODE7: + /* Take the timestamp */ +- spin_lock_irqsave(&dac33->lock, flags); +- dac33->t_stamp1 = ktime_to_us(ktime_get()); +- /* Move back the timestamp with drain time */ +- dac33->t_stamp1 -= dac33->mode7_us_to_lthr; +- spin_unlock_irqrestore(&dac33->lock, flags); ++ scoped_guard(spinlock_irqsave, &dac33->lock) { ++ dac33->t_stamp1 = ktime_to_us(ktime_get()); ++ /* Move back the timestamp with drain time */ ++ dac33->t_stamp1 -= dac33->mode7_us_to_lthr; ++ } + + dac33_write16(component, DAC33_PREFILL_MSB, + DAC33_THRREG(DAC33_MODE7_MARGIN)); +@@ -704,14 +699,12 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) + static inline void dac33_playback_handler(struct tlv320dac33_priv *dac33) + { + struct snd_soc_component *component = dac33->component; +- unsigned long flags; + + switch (dac33->fifo_mode) { + case DAC33_FIFO_MODE1: + /* Take the timestamp */ +- spin_lock_irqsave(&dac33->lock, flags); +- dac33->t_stamp2 = ktime_to_us(ktime_get()); +- spin_unlock_irqrestore(&dac33->lock, flags); ++ scoped_guard(spinlock_irqsave, &dac33->lock) ++ dac33->t_stamp2 = ktime_to_us(ktime_get()); + + dac33_write16(component, DAC33_NSAMPLE_MSB, + DAC33_THRREG(dac33->nsample)); +@@ -735,7 +728,7 @@ static void dac33_work(struct work_struct *work) + dac33 = container_of(work, struct tlv320dac33_priv, work); + component = dac33->component; + +- mutex_lock(&dac33->mutex); ++ guard(mutex)(&dac33->mutex); + switch (dac33->state) { + case DAC33_PREFILL: + dac33->state = DAC33_PLAYBACK; +@@ -757,18 +750,15 @@ static void dac33_work(struct work_struct *work) + dac33_write(component, DAC33_FIFO_CTRL_A, reg); + break; + } +- mutex_unlock(&dac33->mutex); + } + + static irqreturn_t dac33_interrupt_handler(int irq, void *dev) + { + struct snd_soc_component *component = dev; + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); +- unsigned long flags; + +- spin_lock_irqsave(&dac33->lock, flags); +- dac33->t_stamp1 = ktime_to_us(ktime_get()); +- spin_unlock_irqrestore(&dac33->lock, flags); ++ scoped_guard(spinlock_irqsave, &dac33->lock) ++ dac33->t_stamp1 = ktime_to_us(ktime_get()); + + /* Do not schedule the workqueue in Mode7 */ + if (dac33->fifo_mode != DAC33_FIFO_MODE7) +@@ -902,14 +892,13 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream, + return -EINVAL; + } + +- mutex_lock(&dac33->mutex); ++ guard(mutex)(&dac33->mutex); + + if (!dac33->chip_power) { + /* + * Chip is not powered yet. + * Do the init in the dac33_set_bias_level later. + */ +- mutex_unlock(&dac33->mutex); + return 0; + } + +@@ -1053,8 +1042,6 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream, + break; + } + +- mutex_unlock(&dac33->mutex); +- + return 0; + } + +@@ -1156,21 +1143,20 @@ static snd_pcm_sframes_t dac33_dai_delay( + unsigned int time_delta, uthr; + int samples_out, samples_in, samples; + snd_pcm_sframes_t delay = 0; +- unsigned long flags; + + switch (dac33->fifo_mode) { + case DAC33_FIFO_BYPASS: + break; + case DAC33_FIFO_MODE1: +- spin_lock_irqsave(&dac33->lock, flags); +- t0 = dac33->t_stamp1; +- t1 = dac33->t_stamp2; +- spin_unlock_irqrestore(&dac33->lock, flags); ++ scoped_guard(spinlock_irqsave, &dac33->lock) { ++ t0 = dac33->t_stamp1; ++ t1 = dac33->t_stamp2; ++ } + t_now = ktime_to_us(ktime_get()); + + /* We have not started to fill the FIFO yet, delay is 0 */ + if (!t1) +- goto out; ++ return 0; + + if (t0 > t1) { + /* +@@ -1230,23 +1216,22 @@ static snd_pcm_sframes_t dac33_dai_delay( + } + break; + case DAC33_FIFO_MODE7: +- spin_lock_irqsave(&dac33->lock, flags); +- t0 = dac33->t_stamp1; +- uthr = dac33->uthr; +- spin_unlock_irqrestore(&dac33->lock, flags); ++ scoped_guard(spinlock_irqsave, &dac33->lock) { ++ t0 = dac33->t_stamp1; ++ uthr = dac33->uthr; ++ } + t_now = ktime_to_us(ktime_get()); + + /* We have not started to fill the FIFO yet, delay is 0 */ + if (!t0) +- goto out; ++ return 0; + + if (t_now <= t0) { + /* + * Either the timestamps are messed or equal. Report + * maximum delay + */ +- delay = uthr; +- goto out; ++ return uthr; + } + + time_delta = t_now - t0; +@@ -1287,7 +1272,7 @@ static snd_pcm_sframes_t dac33_dai_delay( + dac33->fifo_mode); + break; + } +-out: ++ + return delay; + } + +diff --git a/sound/soc/codecs/tscs42xx.c b/sound/soc/codecs/tscs42xx.c +--- a/sound/soc/codecs/tscs42xx.c ++++ b/sound/soc/codecs/tscs42xx.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -210,25 +211,21 @@ static int power_up_audio_plls(struct snd_soc_component *component) + return ret; + } + +- mutex_lock(&tscs42xx->pll_lock); ++ guard(mutex)(&tscs42xx->pll_lock); + + ret = snd_soc_component_update_bits(component, R_PLLCTL1C, mask, val); + if (ret < 0) { + dev_err(component->dev, "Failed to turn PLL on (%d)\n", ret); +- goto exit; ++ return ret; + } + + if (!plls_locked(component)) { + dev_err(component->dev, "Failed to lock plls\n"); + ret = -ENOMSG; +- goto exit; ++ return ret; + } + +- ret = 0; +-exit: +- mutex_unlock(&tscs42xx->pll_lock); +- +- return ret; ++ return 0; + } + + static int power_down_audio_plls(struct snd_soc_component *component) +@@ -236,28 +233,24 @@ static int power_down_audio_plls(struct snd_soc_component *component) + struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); + int ret; + +- mutex_lock(&tscs42xx->pll_lock); ++ guard(mutex)(&tscs42xx->pll_lock); + + ret = snd_soc_component_update_bits(component, R_PLLCTL1C, + RM_PLLCTL1C_PDB_PLL1, + RV_PLLCTL1C_PDB_PLL1_DISABLE); + if (ret < 0) { + dev_err(component->dev, "Failed to turn PLL off (%d)\n", ret); +- goto exit; ++ return ret; + } + ret = snd_soc_component_update_bits(component, R_PLLCTL1C, + RM_PLLCTL1C_PDB_PLL2, + RV_PLLCTL1C_PDB_PLL2_DISABLE); + if (ret < 0) { + dev_err(component->dev, "Failed to turn PLL off (%d)\n", ret); +- goto exit; ++ return ret; + } + +- ret = 0; +-exit: +- mutex_unlock(&tscs42xx->pll_lock); +- +- return ret; ++ return 0; + } + + static int coeff_ram_get(struct snd_kcontrol *kcontrol, +@@ -269,13 +262,11 @@ static int coeff_ram_get(struct snd_kcontrol *kcontrol, + (struct coeff_ram_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + +- mutex_lock(&tscs42xx->coeff_ram_lock); ++ guard(mutex)(&tscs42xx->coeff_ram_lock); + + memcpy(ucontrol->value.bytes.data, + &tscs42xx->coeff_ram[ctl->addr * COEFF_SIZE], params->max); + +- mutex_unlock(&tscs42xx->coeff_ram_lock); +- + return 0; + } + +@@ -290,14 +281,14 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol, + unsigned int coeff_cnt = params->max / COEFF_SIZE; + int ret; + +- mutex_lock(&tscs42xx->coeff_ram_lock); ++ guard(mutex)(&tscs42xx->coeff_ram_lock); + + tscs42xx->coeff_ram_synced = false; + + memcpy(&tscs42xx->coeff_ram[ctl->addr * COEFF_SIZE], + ucontrol->value.bytes.data, params->max); + +- mutex_lock(&tscs42xx->pll_lock); ++ guard(mutex)(&tscs42xx->pll_lock); + + if (plls_locked(component)) { + ret = write_coeff_ram(component, tscs42xx->coeff_ram, +@@ -305,18 +296,12 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol, + if (ret < 0) { + dev_err(component->dev, + "Failed to flush coeff ram cache (%d)\n", ret); +- goto exit; ++ return ret; + } + tscs42xx->coeff_ram_synced = true; + } + +- ret = 0; +-exit: +- mutex_unlock(&tscs42xx->pll_lock); +- +- mutex_unlock(&tscs42xx->coeff_ram_lock); +- +- return ret; ++ return 0; + } + + /* Input L Capture Route */ +@@ -385,21 +370,17 @@ static int dac_event(struct snd_soc_dapm_widget *w, + struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); + int ret; + +- mutex_lock(&tscs42xx->coeff_ram_lock); ++ guard(mutex)(&tscs42xx->coeff_ram_lock); + + if (!tscs42xx->coeff_ram_synced) { + ret = write_coeff_ram(component, tscs42xx->coeff_ram, 0x00, + COEFF_RAM_COEFF_COUNT); + if (ret < 0) +- goto exit; ++ return ret; + tscs42xx->coeff_ram_synced = true; + } + +- ret = 0; +-exit: +- mutex_unlock(&tscs42xx->coeff_ram_lock); +- +- return ret; ++ return 0; + } + + static const struct snd_soc_dapm_widget tscs42xx_dapm_widgets[] = { +@@ -926,12 +907,10 @@ static int setup_sample_rate(struct snd_soc_component *component, + return ret; + } + +- mutex_lock(&tscs42xx->audio_params_lock); ++ guard(mutex)(&tscs42xx->audio_params_lock); + + tscs42xx->samplerate = rate; + +- mutex_unlock(&tscs42xx->audio_params_lock); +- + return 0; + } + +@@ -1253,12 +1232,10 @@ static int tscs42xx_set_dai_bclk_ratio(struct snd_soc_dai *codec_dai, + return ret; + } + +- mutex_lock(&tscs42xx->audio_params_lock); ++ guard(mutex)(&tscs42xx->audio_params_lock); + + tscs42xx->bclk_ratio = ratio; + +- mutex_unlock(&tscs42xx->audio_params_lock); +- + return 0; + } + +diff --git a/sound/soc/codecs/tscs454.c b/sound/soc/codecs/tscs454.c +--- a/sound/soc/codecs/tscs454.c ++++ b/sound/soc/codecs/tscs454.c +@@ -4,6 +4,7 @@ + // Author: Steven Eckhoff + + #include ++#include + #include + #include + #include +@@ -329,12 +330,10 @@ static int coeff_ram_get(struct snd_kcontrol *kcontrol, + return -EINVAL; + } + +- mutex_lock(coeff_ram_lock); +- +- memcpy(ucontrol->value.bytes.data, +- &coeff_ram[ctl->addr * COEFF_SIZE], params->max); +- +- mutex_unlock(coeff_ram_lock); ++ scoped_guard(mutex, coeff_ram_lock) { ++ memcpy(ucontrol->value.bytes.data, ++ &coeff_ram[ctl->addr * COEFF_SIZE], params->max); ++ } + + return 0; + } +@@ -428,15 +427,15 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol, + return -EINVAL; + } + +- mutex_lock(coeff_ram_lock); ++ guard(mutex)(coeff_ram_lock); + + *coeff_ram_synced = false; + + memcpy(&coeff_ram[ctl->addr * COEFF_SIZE], + ucontrol->value.bytes.data, params->max); + +- mutex_lock(&tscs454->pll1.lock); +- mutex_lock(&tscs454->pll2.lock); ++ guard(mutex)(&tscs454->pll1.lock); ++ guard(mutex)(&tscs454->pll2.lock); + + val = snd_soc_component_read(component, R_PLLSTAT); + if (val) { /* PLLs locked */ +@@ -446,18 +445,12 @@ static int coeff_ram_put(struct snd_kcontrol *kcontrol, + if (ret < 0) { + dev_err(component->dev, + "Failed to flush coeff ram cache (%d)\n", ret); +- goto exit; ++ return ret; + } + *coeff_ram_synced = true; + } + +- ret = 0; +-exit: +- mutex_unlock(&tscs454->pll2.lock); +- mutex_unlock(&tscs454->pll1.lock); +- mutex_unlock(coeff_ram_lock); +- +- return ret; ++ return 0; + } + + static inline int coeff_ram_sync(struct snd_soc_component *component, +@@ -465,41 +458,35 @@ static inline int coeff_ram_sync(struct snd_soc_component *component, + { + int ret; + +- mutex_lock(&tscs454->dac_ram.lock); +- if (!tscs454->dac_ram.synced) { +- ret = write_coeff_ram(component, tscs454->dac_ram.cache, +- R_DACCRS, R_DACCRADD, R_DACCRWDL, +- 0x00, COEFF_RAM_COEFF_COUNT); +- if (ret < 0) { +- mutex_unlock(&tscs454->dac_ram.lock); +- return ret; ++ scoped_guard(mutex, &tscs454->dac_ram.lock) { ++ if (!tscs454->dac_ram.synced) { ++ ret = write_coeff_ram(component, tscs454->dac_ram.cache, ++ R_DACCRS, R_DACCRADD, R_DACCRWDL, ++ 0x00, COEFF_RAM_COEFF_COUNT); ++ if (ret < 0) ++ return ret; + } + } +- mutex_unlock(&tscs454->dac_ram.lock); + +- mutex_lock(&tscs454->spk_ram.lock); +- if (!tscs454->spk_ram.synced) { +- ret = write_coeff_ram(component, tscs454->spk_ram.cache, +- R_SPKCRS, R_SPKCRADD, R_SPKCRWDL, +- 0x00, COEFF_RAM_COEFF_COUNT); +- if (ret < 0) { +- mutex_unlock(&tscs454->spk_ram.lock); +- return ret; ++ scoped_guard(mutex, &tscs454->spk_ram.lock) { ++ if (!tscs454->spk_ram.synced) { ++ ret = write_coeff_ram(component, tscs454->spk_ram.cache, ++ R_SPKCRS, R_SPKCRADD, R_SPKCRWDL, ++ 0x00, COEFF_RAM_COEFF_COUNT); ++ if (ret < 0) ++ return ret; + } + } +- mutex_unlock(&tscs454->spk_ram.lock); + +- mutex_lock(&tscs454->sub_ram.lock); +- if (!tscs454->sub_ram.synced) { +- ret = write_coeff_ram(component, tscs454->sub_ram.cache, +- R_SUBCRS, R_SUBCRADD, R_SUBCRWDL, +- 0x00, COEFF_RAM_COEFF_COUNT); +- if (ret < 0) { +- mutex_unlock(&tscs454->sub_ram.lock); +- return ret; ++ scoped_guard(mutex, &tscs454->sub_ram.lock) { ++ if (!tscs454->sub_ram.synced) { ++ ret = write_coeff_ram(component, tscs454->sub_ram.cache, ++ R_SUBCRS, R_SUBCRADD, R_SUBCRWDL, ++ 0x00, COEFF_RAM_COEFF_COUNT); ++ if (ret < 0) ++ return ret; + } + } +- mutex_unlock(&tscs454->sub_ram.lock); + + return 0; + } +@@ -658,16 +645,14 @@ static int set_sysclk(struct snd_soc_component *component) + + static inline void reserve_pll(struct pll *pll) + { +- mutex_lock(&pll->lock); ++ guard(mutex)(&pll->lock); + pll->users++; +- mutex_unlock(&pll->lock); + } + + static inline void free_pll(struct pll *pll) + { +- mutex_lock(&pll->lock); ++ guard(mutex)(&pll->lock); + pll->users--; +- mutex_unlock(&pll->lock); + } + + static int pll_connected(struct snd_soc_dapm_widget *source, +@@ -679,15 +664,13 @@ static int pll_connected(struct snd_soc_dapm_widget *source, + int users; + + if (strstr(source->name, "PLL 1")) { +- mutex_lock(&tscs454->pll1.lock); +- users = tscs454->pll1.users; +- mutex_unlock(&tscs454->pll1.lock); ++ scoped_guard(mutex, &tscs454->pll1.lock) ++ users = tscs454->pll1.users; + dev_dbg(component->dev, "%s(): PLL 1 users = %d\n", __func__, + users); + } else { +- mutex_lock(&tscs454->pll2.lock); +- users = tscs454->pll2.users; +- mutex_unlock(&tscs454->pll2.lock); ++ scoped_guard(mutex, &tscs454->pll2.lock) ++ users = tscs454->pll2.users; + dev_dbg(component->dev, "%s(): PLL 2 users = %d\n", __func__, + users); + } +@@ -806,7 +789,7 @@ static inline int aif_free(struct snd_soc_component *component, + { + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + +- mutex_lock(&tscs454->aifs_status_lock); ++ guard(mutex)(&tscs454->aifs_status_lock); + + dev_dbg(component->dev, "%s(): aif %d\n", __func__, aif->id); + +@@ -829,8 +812,6 @@ static inline int aif_free(struct snd_soc_component *component, + free_pll(tscs454->internal_rate.pll); + } + +- mutex_unlock(&tscs454->aifs_status_lock); +- + return 0; + } + +@@ -3174,7 +3155,7 @@ static int tscs454_hw_params(struct snd_pcm_substream *substream, + unsigned int val; + int ret; + +- mutex_lock(&tscs454->aifs_status_lock); ++ guard(mutex)(&tscs454->aifs_status_lock); + + dev_dbg(component->dev, "%s(): aif %d fs = %u\n", __func__, + aif->id, fs); +@@ -3207,14 +3188,14 @@ static int tscs454_hw_params(struct snd_pcm_substream *substream, + ret = set_aif_fs(component, aif->id, fs); + if (ret < 0) { + dev_err(component->dev, "Failed to set aif fs (%d)\n", ret); +- goto exit; ++ return ret; + } + + ret = set_aif_sample_format(component, params_format(params), aif->id); + if (ret < 0) { + dev_err(component->dev, + "Failed to set aif sample format (%d)\n", ret); +- goto exit; ++ return ret; + } + + set_aif_status_active(&tscs454->aifs_status, aif->id, +@@ -3223,11 +3204,7 @@ static int tscs454_hw_params(struct snd_pcm_substream *substream, + dev_dbg(component->dev, "Set aif %d active. Streams status is 0x%x\n", + aif->id, tscs454->aifs_status.streams); + +- ret = 0; +-exit: +- mutex_unlock(&tscs454->aifs_status_lock); +- +- return ret; ++ return 0; + } + + static int tscs454_hw_free(struct snd_pcm_substream *substream, +diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c +--- a/sound/soc/codecs/twl6040.c ++++ b/sound/soc/codecs/twl6040.c +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -273,7 +274,7 @@ static void twl6040_hs_jack_report(struct snd_soc_component *component, + struct twl6040_data *priv = snd_soc_component_get_drvdata(component); + int status; + +- mutex_lock(&priv->mutex); ++ guard(mutex)(&priv->mutex); + + /* Sync status */ + status = twl6040_read(component, TWL6040_REG_STATUS); +@@ -281,8 +282,6 @@ static void twl6040_hs_jack_report(struct snd_soc_component *component, + snd_soc_jack_report(jack, report, report); + else + snd_soc_jack_report(jack, 0, report); +- +- mutex_unlock(&priv->mutex); + } + + void twl6040_hs_jack_detect(struct snd_soc_component *component, +diff --git a/sound/soc/codecs/uda1342.c b/sound/soc/codecs/uda1342.c +--- a/sound/soc/codecs/uda1342.c ++++ b/sound/soc/codecs/uda1342.c +@@ -308,9 +308,16 @@ static int uda1342_suspend(struct device *dev) + static int uda1342_resume(struct device *dev) + { + struct uda1342_priv *uda1342 = dev_get_drvdata(dev); ++ int ret; + ++ regcache_cache_only(uda1342->regmap, false); + regcache_mark_dirty(uda1342->regmap); +- regcache_sync(uda1342->regmap); ++ ret = regcache_sync(uda1342->regmap); ++ if (ret) { ++ regcache_cache_only(uda1342->regmap, true); ++ regcache_mark_dirty(uda1342->regmap); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c +--- a/sound/soc/codecs/wcd9335.c ++++ b/sound/soc/codecs/wcd9335.c +@@ -5010,9 +5010,9 @@ static int wcd9335_power_on_reset(struct wcd9335_codec *wcd) + */ + usleep_range(600, 650); + +- gpiod_set_value(wcd->reset_gpio, 1); ++ gpiod_set_value_cansleep(wcd->reset_gpio, 1); + msleep(20); +- gpiod_set_value(wcd->reset_gpio, 0); ++ gpiod_set_value_cansleep(wcd->reset_gpio, 0); + msleep(20); + + return 0; +diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c +--- a/sound/soc/codecs/wcd934x.c ++++ b/sound/soc/codecs/wcd934x.c +@@ -1265,13 +1265,10 @@ static int wcd934x_set_sido_input_src(struct wcd934x_codec *wcd, int sido_src) + + static int wcd934x_enable_ana_bias_and_sysclk(struct wcd934x_codec *wcd) + { +- mutex_lock(&wcd->sysclk_mutex); +- +- if (++wcd->sysclk_users != 1) { +- mutex_unlock(&wcd->sysclk_mutex); +- return 0; ++ scoped_guard(mutex, &wcd->sysclk_mutex) { ++ if (++wcd->sysclk_users != 1) ++ return 0; + } +- mutex_unlock(&wcd->sysclk_mutex); + + regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS, + WCD934X_ANA_BIAS_EN_MASK, +@@ -1328,12 +1325,10 @@ static int wcd934x_enable_ana_bias_and_sysclk(struct wcd934x_codec *wcd) + + static int wcd934x_disable_ana_bias_and_syclk(struct wcd934x_codec *wcd) + { +- mutex_lock(&wcd->sysclk_mutex); +- if (--wcd->sysclk_users != 0) { +- mutex_unlock(&wcd->sysclk_mutex); +- return 0; ++ scoped_guard(mutex, &wcd->sysclk_mutex) { ++ if (--wcd->sysclk_users != 0) ++ return 0; + } +- mutex_unlock(&wcd->sysclk_mutex); + + regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG, + WCD934X_EXT_CLK_BUF_EN_MASK | +@@ -2384,7 +2379,7 @@ static int wcd934x_micbias_control(struct snd_soc_component *component, + __func__, micb_num); + return -EINVAL; + } +- mutex_lock(&wcd934x->micb_lock); ++ guard(mutex)(&wcd934x->micb_lock); + + switch (req) { + case MICB_PULLUP_ENABLE: +@@ -2446,8 +2441,6 @@ static int wcd934x_micbias_control(struct snd_soc_component *component, + break; + } + +- mutex_unlock(&wcd934x->micb_lock); +- + return 0; + } + +@@ -2488,7 +2481,7 @@ static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + int req_volt, int micb_num) + { + struct wcd934x_codec *wcd934x = snd_soc_component_get_drvdata(component); +- int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0; ++ int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en; + + switch (micb_num) { + case MIC_BIAS_1: +@@ -2506,7 +2499,7 @@ static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + default: + return -EINVAL; + } +- mutex_lock(&wcd934x->micb_lock); ++ guard(mutex)(&wcd934x->micb_lock); + /* + * If requested micbias voltage is same as current micbias + * voltage, then just return. Otherwise, adjust voltage as +@@ -2521,15 +2514,11 @@ static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + WCD934X_MICB_VAL_MASK); + + req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt); +- if (req_vout_ctl < 0) { +- ret = -EINVAL; +- goto exit; +- } ++ if (req_vout_ctl < 0) ++ return -EINVAL; + +- if (cur_vout_ctl == req_vout_ctl) { +- ret = 0; +- goto exit; +- } ++ if (cur_vout_ctl == req_vout_ctl) ++ return 0; + + if (micb_en == WCD934X_MICB_ENABLE) + snd_soc_component_write_field(component, micb_reg, +@@ -2550,9 +2539,8 @@ static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + */ + usleep_range(2000, 2100); + } +-exit: +- mutex_unlock(&wcd934x->micb_lock); +- return ret; ++ ++ return 0; + } + + static int wcd934x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, +@@ -5899,10 +5887,8 @@ static int wcd934x_codec_probe(struct platform_device *pdev) + } + + static const struct platform_device_id wcd934x_driver_id[] = { +- { +- .name = "wcd934x-codec", +- }, +- {}, ++ { .name = "wcd934x-codec" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, wcd934x_driver_id); + +diff --git a/sound/soc/codecs/wcd937x-sdw.c b/sound/soc/codecs/wcd937x-sdw.c +--- a/sound/soc/codecs/wcd937x-sdw.c ++++ b/sound/soc/codecs/wcd937x-sdw.c +@@ -1084,10 +1084,16 @@ static int wcd937x_sdw_runtime_suspend(struct device *dev) + static int wcd937x_sdw_runtime_resume(struct device *dev) + { + struct wcd937x_sdw_priv *wcd = dev_get_drvdata(dev); ++ int ret; + + if (wcd->regmap) { + regcache_cache_only(wcd->regmap, false); +- regcache_sync(wcd->regmap); ++ ret = regcache_sync(wcd->regmap); ++ if (ret) { ++ regcache_cache_only(wcd->regmap, true); ++ regcache_mark_dirty(wcd->regmap); ++ return ret; ++ } + } + + return 0; +diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c +--- a/sound/soc/codecs/wcd937x.c ++++ b/sound/soc/codecs/wcd937x.c +@@ -2,6 +2,7 @@ + // Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved. + + #include ++#include + #include + #include + #include +@@ -1056,7 +1057,7 @@ static int wcd937x_micbias_control(struct snd_soc_component *component, + return -EINVAL; + } + +- mutex_lock(&wcd937x->micb_lock); ++ guard(mutex)(&wcd937x->micb_lock); + switch (req) { + case MICB_PULLUP_ENABLE: + wcd937x->pullup_ref[micb_index]++; +@@ -1136,7 +1137,6 @@ static int wcd937x_micbias_control(struct snd_soc_component *component, + } + break; + } +- mutex_unlock(&wcd937x->micb_lock); + + return 0; + } +@@ -1460,7 +1460,7 @@ static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + int req_volt, int micb_num) + { + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); +- int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0; ++ int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en; + + switch (micb_num) { + case MIC_BIAS_1: +@@ -1475,7 +1475,7 @@ static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + default: + return -EINVAL; + } +- mutex_lock(&wcd937x->micb_lock); ++ guard(mutex)(&wcd937x->micb_lock); + /* + * If requested micbias voltage is same as current micbias + * voltage, then just return. Otherwise, adjust voltage as +@@ -1490,15 +1490,11 @@ static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + WCD937X_MICB_VOUT_MASK); + + req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt); +- if (req_vout_ctl < 0) { +- ret = -EINVAL; +- goto exit; +- } ++ if (req_vout_ctl < 0) ++ return -EINVAL; + +- if (cur_vout_ctl == req_vout_ctl) { +- ret = 0; +- goto exit; +- } ++ if (cur_vout_ctl == req_vout_ctl) ++ return 0; + + if (micb_en == WCD937X_MICB_ENABLE) + snd_soc_component_write_field(component, micb_reg, +@@ -1519,9 +1515,8 @@ static int wcd937x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + */ + usleep_range(2000, 2100); + } +-exit: +- mutex_unlock(&wcd937x->micb_lock); +- return ret; ++ ++ return 0; + } + + static int wcd937x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, +diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c +--- a/sound/soc/codecs/wcd938x-sdw.c ++++ b/sound/soc/codecs/wcd938x-sdw.c +@@ -1245,10 +1245,16 @@ static int wcd938x_sdw_runtime_suspend(struct device *dev) + static int wcd938x_sdw_runtime_resume(struct device *dev) + { + struct wcd938x_sdw_priv *wcd = dev_get_drvdata(dev); ++ int ret; + + if (wcd->regmap) { + regcache_cache_only(wcd->regmap, false); +- regcache_sync(wcd->regmap); ++ ret = regcache_sync(wcd->regmap); ++ if (ret) { ++ regcache_cache_only(wcd->regmap, true); ++ regcache_mark_dirty(wcd->regmap); ++ return ret; ++ } + } + + pm_runtime_mark_last_busy(dev); +diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c +--- a/sound/soc/codecs/wcd938x.c ++++ b/sound/soc/codecs/wcd938x.c +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: GPL-2.0-only + // Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + ++#include + #include + #include + #include +@@ -1976,7 +1977,7 @@ static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + int req_volt, int micb_num) + { + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); +- int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0; ++ int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en; + + switch (micb_num) { + case MIC_BIAS_1: +@@ -1994,7 +1995,7 @@ static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + default: + return -EINVAL; + } +- mutex_lock(&wcd938x->micb_lock); ++ guard(mutex)(&wcd938x->micb_lock); + /* + * If requested micbias voltage is same as current micbias + * voltage, then just return. Otherwise, adjust voltage as +@@ -2009,15 +2010,11 @@ static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + WCD938X_MICB_VOUT_MASK); + + req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt); +- if (req_vout_ctl < 0) { +- ret = -EINVAL; +- goto exit; +- } ++ if (req_vout_ctl < 0) ++ return -EINVAL; + +- if (cur_vout_ctl == req_vout_ctl) { +- ret = 0; +- goto exit; +- } ++ if (cur_vout_ctl == req_vout_ctl) ++ return 0; + + if (micb_en == WCD938X_MICB_ENABLE) + snd_soc_component_write_field(component, micb_reg, +@@ -2038,9 +2035,8 @@ static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + */ + usleep_range(2000, 2100); + } +-exit: +- mutex_unlock(&wcd938x->micb_lock); +- return ret; ++ ++ return 0; + } + + static int wcd938x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, +diff --git a/sound/soc/codecs/wcd939x-sdw.c b/sound/soc/codecs/wcd939x-sdw.c +--- a/sound/soc/codecs/wcd939x-sdw.c ++++ b/sound/soc/codecs/wcd939x-sdw.c +@@ -1431,10 +1431,16 @@ static int wcd939x_sdw_runtime_suspend(struct device *dev) + static int wcd939x_sdw_runtime_resume(struct device *dev) + { + struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev); ++ int ret; + + if (wcd->regmap) { + regcache_cache_only(wcd->regmap, false); +- regcache_sync(wcd->regmap); ++ ret = regcache_sync(wcd->regmap); ++ if (ret) { ++ regcache_cache_only(wcd->regmap, true); ++ regcache_mark_dirty(wcd->regmap); ++ return ret; ++ } + } + + return 0; +diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c +--- a/sound/soc/codecs/wcd939x.c ++++ b/sound/soc/codecs/wcd939x.c +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1923,7 +1924,6 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + unsigned int micb_reg, cur_vout_ctl, micb_en; + int req_vout_ctl; +- int ret = 0; + + switch (micb_num) { + case MIC_BIAS_1: +@@ -1941,7 +1941,7 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + default: + return -EINVAL; + } +- mutex_lock(&wcd939x->micb_lock); ++ guard(mutex)(&wcd939x->micb_lock); + + /* + * If requested micbias voltage is same as current micbias +@@ -1957,15 +1957,11 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + WCD939X_MICB_VOUT_CTL); + + req_vout_ctl = wcd_get_micb_vout_ctl_val(component->dev, req_volt); +- if (req_vout_ctl < 0) { +- ret = req_vout_ctl; +- goto exit; +- } ++ if (req_vout_ctl < 0) ++ return req_vout_ctl; + +- if (cur_vout_ctl == req_vout_ctl) { +- ret = 0; +- goto exit; +- } ++ if (cur_vout_ctl == req_vout_ctl) ++ return 0; + + dev_dbg(component->dev, "%s: micb_num: %d, cur_mv: %d, req_mv: %d, micb_en: %d\n", + __func__, micb_num, WCD_VOUT_CTL_TO_MICB(cur_vout_ctl), +@@ -1990,9 +1986,7 @@ static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + usleep_range(2000, 2100); + } + +-exit: +- mutex_unlock(&wcd939x->micb_lock); +- return ret; ++ return 0; + } + + static int wcd939x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, +diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c +--- a/sound/soc/codecs/wm0010.c ++++ b/sound/soc/codecs/wm0010.c +@@ -9,6 +9,7 @@ + * Scott Ling + */ + ++#include + #include + #include + #include +@@ -148,13 +149,11 @@ static const char *wm0010_state_to_str(enum wm0010_state state) + static void wm0010_halt(struct snd_soc_component *component) + { + struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); +- unsigned long flags; + enum wm0010_state state; + + /* Fetch the wm0010 state */ +- spin_lock_irqsave(&wm0010->irq_lock, flags); +- state = wm0010->state; +- spin_unlock_irqrestore(&wm0010->irq_lock, flags); ++ scoped_guard(spinlock_irqsave, &wm0010->irq_lock) ++ state = wm0010->state; + + switch (state) { + case WM0010_POWER_OFF: +@@ -173,9 +172,8 @@ static void wm0010_halt(struct snd_soc_component *component) + break; + } + +- spin_lock_irqsave(&wm0010->irq_lock, flags); +- wm0010->state = WM0010_POWER_OFF; +- spin_unlock_irqrestore(&wm0010->irq_lock, flags); ++ scoped_guard(spinlock_irqsave, &wm0010->irq_lock) ++ wm0010->state = WM0010_POWER_OFF; + } + + struct wm0010_boot_xfer { +@@ -190,11 +188,9 @@ struct wm0010_boot_xfer { + static void wm0010_mark_boot_failure(struct wm0010_priv *wm0010) + { + enum wm0010_state state; +- unsigned long flags; + +- spin_lock_irqsave(&wm0010->irq_lock, flags); +- state = wm0010->state; +- spin_unlock_irqrestore(&wm0010->irq_lock, flags); ++ scoped_guard(spinlock_irqsave, &wm0010->irq_lock) ++ state = wm0010->state; + + dev_err(wm0010->dev, "Failed to transition from `%s' state to `%s' state\n", + wm0010_state_to_str(state), wm0010_state_to_str(state + 1)); +@@ -337,7 +333,6 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp + struct wm0010_boot_xfer *xfer; + int ret; + DECLARE_COMPLETION_ONSTACK(done); +- const struct firmware *fw; + const struct dfw_binrec *rec; + const struct dfw_inforec *inforec; + u64 *img; +@@ -346,6 +341,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp + + INIT_LIST_HEAD(&xfer_list); + ++ const struct firmware *fw __free(firmware) = NULL; + ret = request_firmware(&fw, name, component->dev); + if (ret != 0) { + dev_err(component->dev, "Failed to request application(%s): %d\n", +@@ -364,16 +360,14 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp + /* First record should be INFO */ + if (rec->command != DFW_CMD_INFO) { + dev_err(component->dev, "First record not INFO\r\n"); +- ret = -EINVAL; +- goto abort; ++ return -EINVAL; + } + + if (inforec->info_version != INFO_VERSION) { + dev_err(component->dev, + "Unsupported version (%02d) of INFO record\r\n", + inforec->info_version); +- ret = -EINVAL; +- goto abort; ++ return -EINVAL; + } + + dev_dbg(component->dev, "Version v%02d INFO record found\r\n", +@@ -382,8 +376,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp + /* Check it's a DSP file */ + if (dsp != DEVICE_ID_WM0010) { + dev_err(component->dev, "Not a WM0010 firmware file.\r\n"); +- ret = -EINVAL; +- goto abort; ++ return -EINVAL; + } + + /* Skip the info record as we don't need to send it */ +@@ -408,14 +401,14 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp + out = kzalloc(len, GFP_KERNEL | GFP_DMA); + if (!out) { + ret = -ENOMEM; +- goto abort1; ++ goto abort; + } + xfer->t.rx_buf = out; + + img = kzalloc(len, GFP_KERNEL | GFP_DMA); + if (!img) { + ret = -ENOMEM; +- goto abort1; ++ goto abort; + } + xfer->t.tx_buf = img; + +@@ -453,13 +446,13 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp + ret = spi_async(spi, &xfer->m); + if (ret != 0) { + dev_err(component->dev, "Write failed: %d\n", ret); +- goto abort1; ++ goto abort; + } + + if (wm0010->boot_failed) { + dev_dbg(component->dev, "Boot fail!\n"); + ret = -EINVAL; +- goto abort1; ++ goto abort; + } + } + +@@ -467,7 +460,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp + + ret = 0; + +-abort1: ++abort: + while (!list_empty(&xfer_list)) { + xfer = list_first_entry(&xfer_list, struct wm0010_boot_xfer, + list); +@@ -477,8 +470,6 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp + kfree(xfer); + } + +-abort: +- release_firmware(fw); + return ret; + } + +@@ -486,14 +477,12 @@ static int wm0010_stage2_load(struct snd_soc_component *component) + { + struct spi_device *spi = to_spi_device(component->dev); + struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); +- const struct firmware *fw; + struct spi_message m; + struct spi_transfer t; +- u32 *img; +- u8 *out; + int i; + int ret = 0; + ++ const struct firmware *fw __free(firmware) = NULL; + ret = request_firmware(&fw, "wm0010_stage2.bin", component->dev); + if (ret != 0) { + dev_err(component->dev, "Failed to request stage2 loader: %d\n", +@@ -504,17 +493,15 @@ static int wm0010_stage2_load(struct snd_soc_component *component) + dev_dbg(component->dev, "Downloading %zu byte stage 2 loader\n", fw->size); + + /* Copy to local buffer first as vmalloc causes problems for dma */ +- img = kmemdup(&fw->data[0], fw->size, GFP_KERNEL | GFP_DMA); +- if (!img) { +- ret = -ENOMEM; +- goto abort2; +- } ++ u32 *img __free(kfree) = ++ kmemdup(&fw->data[0], fw->size, GFP_KERNEL | GFP_DMA); ++ if (!img) ++ return -ENOMEM; + +- out = kzalloc(fw->size, GFP_KERNEL | GFP_DMA); +- if (!out) { +- ret = -ENOMEM; +- goto abort1; +- } ++ u8 *out __free(kfree) = ++ kzalloc(fw->size, GFP_KERNEL | GFP_DMA); ++ if (!out) ++ return -ENOMEM; + + spi_message_init(&m); + memset(&t, 0, sizeof(t)); +@@ -531,7 +518,7 @@ static int wm0010_stage2_load(struct snd_soc_component *component) + ret = spi_sync(spi, &m); + if (ret != 0) { + dev_err(component->dev, "Initial download failed: %d\n", ret); +- goto abort; ++ return ret; + } + + /* Look for errors from the boot ROM */ +@@ -540,18 +527,11 @@ static int wm0010_stage2_load(struct snd_soc_component *component) + dev_err(component->dev, "Boot ROM error: %x in %d\n", + out[i], i); + wm0010_mark_boot_failure(wm0010); +- ret = -EBUSY; +- goto abort; ++ return -EBUSY; + } + } +-abort: +- kfree(out); +-abort1: +- kfree(img); +-abort2: +- release_firmware(fw); + +- return ret; ++ return 0; + } + + static int wm0010_boot(struct snd_soc_component *component) +@@ -734,9 +714,8 @@ static int wm0010_set_bias_level(struct snd_soc_component *component, + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_PREPARE) { +- mutex_lock(&wm0010->lock); +- wm0010_halt(component); +- mutex_unlock(&wm0010->lock); ++ scoped_guard(mutex, &wm0010->lock) ++ wm0010_halt(component); + } + break; + case SND_SOC_BIAS_OFF: +@@ -832,9 +811,8 @@ static irqreturn_t wm0010_irq(int irq, void *data) + case WM0010_OUT_OF_RESET: + case WM0010_BOOTROM: + case WM0010_STAGE2: +- spin_lock(&wm0010->irq_lock); +- complete(&wm0010->boot_completion); +- spin_unlock(&wm0010->irq_lock); ++ scoped_guard(spinlock, &wm0010->irq_lock) ++ complete(&wm0010->boot_completion); + return IRQ_HANDLED; + default: + return IRQ_NONE; +diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c +--- a/sound/soc/codecs/wm2000.c ++++ b/sound/soc/codecs/wm2000.c +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -612,20 +613,15 @@ static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol, + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); + unsigned int anc_active = ucontrol->value.integer.value[0]; +- int ret; + + if (anc_active > 1) + return -EINVAL; + +- mutex_lock(&wm2000->lock); ++ guard(mutex)(&wm2000->lock); + + wm2000->anc_active = anc_active; + +- ret = wm2000_anc_set_mode(wm2000); +- +- mutex_unlock(&wm2000->lock); +- +- return ret; ++ return wm2000_anc_set_mode(wm2000); + } + + static int wm2000_speaker_get(struct snd_kcontrol *kcontrol, +@@ -645,20 +641,15 @@ static int wm2000_speaker_put(struct snd_kcontrol *kcontrol, + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); + unsigned int val = ucontrol->value.integer.value[0]; +- int ret; + + if (val > 1) + return -EINVAL; + +- mutex_lock(&wm2000->lock); ++ guard(mutex)(&wm2000->lock); + + wm2000->spk_ena = val; + +- ret = wm2000_anc_set_mode(wm2000); +- +- mutex_unlock(&wm2000->lock); +- +- return ret; ++ return wm2000_anc_set_mode(wm2000); + } + + static const struct snd_kcontrol_new wm2000_controls[] = { +@@ -676,9 +667,8 @@ static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w, + { + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); +- int ret; + +- mutex_lock(&wm2000->lock); ++ guard(mutex)(&wm2000->lock); + + if (SND_SOC_DAPM_EVENT_ON(event)) + wm2000->anc_eng_ena = 1; +@@ -686,11 +676,7 @@ static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w, + if (SND_SOC_DAPM_EVENT_OFF(event)) + wm2000->anc_eng_ena = 0; + +- ret = wm2000_anc_set_mode(wm2000); +- +- mutex_unlock(&wm2000->lock); +- +- return ret; ++ return wm2000_anc_set_mode(wm2000); + } + + static const struct snd_soc_dapm_widget wm2000_dapm_widgets[] = { +@@ -810,7 +796,7 @@ static int wm2000_i2c_probe(struct i2c_client *i2c) + struct wm2000_priv *wm2000; + struct wm2000_platform_data *pdata; + const char *filename; +- const struct firmware *fw = NULL; ++ const struct firmware *fw __free(firmware) = NULL; + int ret, i; + unsigned int reg; + u16 id; +@@ -828,7 +814,7 @@ static int wm2000_i2c_probe(struct i2c_client *i2c) + ret = PTR_ERR(wm2000->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); +- goto out; ++ return ret; + } + + for (i = 0; i < WM2000_NUM_SUPPLIES; i++) +@@ -922,9 +908,6 @@ static int wm2000_i2c_probe(struct i2c_client *i2c) + + err_supplies: + regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); +- +-out: +- release_firmware(fw); + return ret; + } + +diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c +--- a/sound/soc/codecs/wm2200.c ++++ b/sound/soc/codecs/wm2200.c +@@ -2461,7 +2461,15 @@ static int wm2200_runtime_resume(struct device *dev) + } + + regcache_cache_only(wm2200->regmap, false); +- regcache_sync(wm2200->regmap); ++ ret = regcache_sync(wm2200->regmap); ++ if (ret) { ++ regcache_cache_only(wm2200->regmap, true); ++ regcache_mark_dirty(wm2200->regmap); ++ gpiod_set_value_cansleep(wm2200->ldo_ena, 0); ++ regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies), ++ wm2200->core_supplies); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c +--- a/sound/soc/codecs/wm5100.c ++++ b/sound/soc/codecs/wm5100.c +@@ -2659,7 +2659,15 @@ static int wm5100_runtime_resume(struct device *dev) + } + + regcache_cache_only(wm5100->regmap, false); +- regcache_sync(wm5100->regmap); ++ ret = regcache_sync(wm5100->regmap); ++ if (ret) { ++ regcache_cache_only(wm5100->regmap, true); ++ regcache_mark_dirty(wm5100->regmap); ++ gpiod_set_value_cansleep(wm5100->ldo_ena, 0); ++ regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies), ++ wm5100->core_supplies); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c +--- a/sound/soc/codecs/wm5102.c ++++ b/sound/soc/codecs/wm5102.c +@@ -7,6 +7,7 @@ + * Author: Mark Brown + */ + ++#include + #include + #include + #include +@@ -667,10 +668,9 @@ static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol, + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct arizona *arizona = dev_get_drvdata(component->dev->parent); + +- mutex_lock(&arizona->dac_comp_lock); ++ guard(mutex)(&arizona->dac_comp_lock); + put_unaligned_be16(arizona->dac_comp_coeff, + ucontrol->value.bytes.data); +- mutex_unlock(&arizona->dac_comp_lock); + + return 0; + } +@@ -681,16 +681,14 @@ static int wm5102_out_comp_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct arizona *arizona = dev_get_drvdata(component->dev->parent); + uint16_t dac_comp_coeff = get_unaligned_be16(ucontrol->value.bytes.data); +- int ret = 0; + +- mutex_lock(&arizona->dac_comp_lock); ++ guard(mutex)(&arizona->dac_comp_lock); + if (arizona->dac_comp_coeff != dac_comp_coeff) { + arizona->dac_comp_coeff = dac_comp_coeff; +- ret = 1; ++ return 1; + } +- mutex_unlock(&arizona->dac_comp_lock); + +- return ret; ++ return 0; + } + + static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol, +@@ -699,9 +697,8 @@ static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol, + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct arizona *arizona = dev_get_drvdata(component->dev->parent); + +- mutex_lock(&arizona->dac_comp_lock); ++ guard(mutex)(&arizona->dac_comp_lock); + ucontrol->value.integer.value[0] = arizona->dac_comp_enabled; +- mutex_unlock(&arizona->dac_comp_lock); + + return 0; + } +@@ -712,19 +709,17 @@ static int wm5102_out_comp_switch_put(struct snd_kcontrol *kcontrol, + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct arizona *arizona = dev_get_drvdata(component->dev->parent); + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; +- int ret = 0; + + if (ucontrol->value.integer.value[0] > mc->max) + return -EINVAL; + +- mutex_lock(&arizona->dac_comp_lock); ++ guard(mutex)(&arizona->dac_comp_lock); + if (arizona->dac_comp_enabled != ucontrol->value.integer.value[0]) { + arizona->dac_comp_enabled = ucontrol->value.integer.value[0]; +- ret = 1; ++ return 1; + } +- mutex_unlock(&arizona->dac_comp_lock); + +- return ret; ++ return 0; + } + + static const char * const wm5102_osr_text[] = { +diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c +--- a/sound/soc/codecs/wm8731.c ++++ b/sound/soc/codecs/wm8731.c +@@ -10,6 +10,7 @@ + * Based on wm8753.c by Liam Girdwood + */ + ++#include + #include + #include + #include +@@ -110,22 +111,20 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component); + unsigned int deemph = ucontrol->value.integer.value[0]; +- int ret = 0; + + if (deemph > 1) + return -EINVAL; + +- mutex_lock(&wm8731->lock); ++ guard(mutex)(&wm8731->lock); + if (wm8731->deemph != deemph) { + wm8731->deemph = deemph; + + wm8731_set_deemph(component); + +- ret = 1; ++ return 1; + } +- mutex_unlock(&wm8731->lock); + +- return ret; ++ return 0; + } + + static const DECLARE_TLV_DB_SCALE(in_tlv, -3450, 150, 0); +diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c +--- a/sound/soc/codecs/wm8903.c ++++ b/sound/soc/codecs/wm8903.c +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -458,22 +459,20 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component); + unsigned int deemph = ucontrol->value.integer.value[0]; +- int ret = 0; + + if (deemph > 1) + return -EINVAL; + +- mutex_lock(&wm8903->lock); ++ guard(mutex)(&wm8903->lock); + if (wm8903->deemph != deemph) { + wm8903->deemph = deemph; + + wm8903_set_deemph(component); + +- ret = 1; ++ return 1; + } +- mutex_unlock(&wm8903->lock); + +- return ret; ++ return 0; + } + + /* ALSA can only do steps of .01dB */ +diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c +--- a/sound/soc/codecs/wm8940.c ++++ b/sound/soc/codecs/wm8940.c +@@ -333,7 +333,7 @@ static const struct snd_soc_dapm_route wm8940_dapm_routes[] = { + {"ADC", NULL, "Boost Mixer"}, + }; + +-#define wm8940_reset(c) snd_soc_component_write(c, WM8940_SOFTRESET, 0); ++#define wm8940_reset(c) snd_soc_component_write(c, WM8940_SOFTRESET, 0) + + static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c +--- a/sound/soc/codecs/wm8958-dsp2.c ++++ b/sound/soc/codecs/wm8958-dsp2.c +@@ -7,6 +7,7 @@ + * Author: Mark Brown + */ + ++#include + #include + #include + #include +@@ -864,9 +865,8 @@ static void wm8958_enh_eq_loaded(const struct firmware *fw, void *context) + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + if (fw && (wm8958_dsp2_fw(component, "ENH_EQ", fw, true) == 0)) { +- mutex_lock(&wm8994->fw_lock); ++ guard(mutex)(&wm8994->fw_lock); + wm8994->enh_eq = fw; +- mutex_unlock(&wm8994->fw_lock); + } + } + +@@ -876,9 +876,8 @@ static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context) + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + if (fw && (wm8958_dsp2_fw(component, "MBC+VSS", fw, true) == 0)) { +- mutex_lock(&wm8994->fw_lock); ++ guard(mutex)(&wm8994->fw_lock); + wm8994->mbc_vss = fw; +- mutex_unlock(&wm8994->fw_lock); + } + } + +@@ -888,9 +887,8 @@ static void wm8958_mbc_loaded(const struct firmware *fw, void *context) + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + if (fw && (wm8958_dsp2_fw(component, "MBC", fw, true) == 0)) { +- mutex_lock(&wm8994->fw_lock); ++ guard(mutex)(&wm8994->fw_lock); + wm8994->mbc = fw; +- mutex_unlock(&wm8994->fw_lock); + } + } + +diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c +--- a/sound/soc/codecs/wm8962.c ++++ b/sound/soc/codecs/wm8962.c +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1564,11 +1565,10 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol, + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); + int old = wm8962->dsp2_ena; +- int ret = 0; + int dsp2_running = snd_soc_component_read(component, WM8962_DSP2_POWER_MANAGEMENT) & + WM8962_DSP2_ENA; + +- mutex_lock(&wm8962->dsp2_ena_lock); ++ guard(mutex)(&wm8962->dsp2_ena_lock); + + if (ucontrol->value.integer.value[0]) + wm8962->dsp2_ena |= 1 << shift; +@@ -1576,9 +1576,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol, + wm8962->dsp2_ena &= ~(1 << shift); + + if (wm8962->dsp2_ena == old) +- goto out; +- +- ret = 1; ++ return 0; + + if (dsp2_running) { + if (wm8962->dsp2_ena) +@@ -1587,10 +1585,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol, + wm8962_dsp2_stop(component); + } + +-out: +- mutex_unlock(&wm8962->dsp2_ena_lock); +- +- return ret; ++ return 1; + } + + /* The VU bits for the headphones are in a different register to the mute +@@ -3937,7 +3932,9 @@ static int wm8962_runtime_resume(struct device *dev) + WM8962_OSC_ENA | WM8962_PLL2_ENA | WM8962_PLL3_ENA, + 0); + +- regcache_sync(wm8962->regmap); ++ ret = regcache_sync(wm8962->regmap); ++ if (ret) ++ goto cache_sync_err; + + regmap_update_bits(wm8962->regmap, WM8962_ANTI_POP, + WM8962_STARTUP_BIAS_ENA | WM8962_VMID_BUF_ENA, +@@ -3955,6 +3952,11 @@ static int wm8962_runtime_resume(struct device *dev) + + return 0; + ++cache_sync_err: ++ regcache_cache_only(wm8962->regmap, true); ++ regcache_mark_dirty(wm8962->regmap); ++ regulator_bulk_disable(ARRAY_SIZE(wm8962->supplies), ++ wm8962->supplies); + disable_clock: + clk_disable_unprepare(wm8962->pdata.mclk); + return ret; +diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c +--- a/sound/soc/codecs/wm8994.c ++++ b/sound/soc/codecs/wm8994.c +@@ -7,6 +7,7 @@ + * Author: Mark Brown + */ + ++#include + #include + #include + #include +@@ -766,7 +767,7 @@ static void active_reference(struct snd_soc_component *component) + { + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + +- mutex_lock(&wm8994->accdet_lock); ++ guard(mutex)(&wm8994->accdet_lock); + + wm8994->active_refcount++; + +@@ -775,8 +776,6 @@ static void active_reference(struct snd_soc_component *component) + + /* If we're using jack detection go into audio mode */ + wm1811_jackdet_set_mode(component, WM1811_JACKDET_MODE_AUDIO); +- +- mutex_unlock(&wm8994->accdet_lock); + } + + static void active_dereference(struct snd_soc_component *component) +@@ -784,7 +783,7 @@ static void active_dereference(struct snd_soc_component *component) + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + u16 mode; + +- mutex_lock(&wm8994->accdet_lock); ++ guard(mutex)(&wm8994->accdet_lock); + + wm8994->active_refcount--; + +@@ -800,8 +799,6 @@ static void active_dereference(struct snd_soc_component *component) + + wm1811_jackdet_set_mode(component, mode); + } +- +- mutex_unlock(&wm8994->accdet_lock); + } + + static int clk_sys_event(struct snd_soc_dapm_widget *w, +@@ -3704,7 +3701,7 @@ static void wm8958_open_circuit_work(struct work_struct *work) + open_circuit_work.work); + struct device *dev = wm8994->wm8994->dev; + +- mutex_lock(&wm8994->accdet_lock); ++ guard(mutex)(&wm8994->accdet_lock); + + wm1811_micd_stop(wm8994->hubs.component); + +@@ -3718,8 +3715,6 @@ static void wm8958_open_circuit_work(struct work_struct *work) + snd_soc_jack_report(wm8994->micdet[0].jack, 0, + wm8994->btn_mask | + SND_JACK_HEADSET); +- +- mutex_unlock(&wm8994->accdet_lock); + } + + static void wm8958_mic_id(void *data, u16 status) +@@ -3777,7 +3772,7 @@ static void wm1811_mic_work(struct work_struct *work) + struct snd_soc_component *component = wm8994->hubs.component; + struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); + +- pm_runtime_get_sync(component->dev); ++ guard(pm_runtime_active)(component->dev); + + /* If required for an external cap force MICBIAS on */ + if (control->pdata.jd_ext_cap) { +@@ -3785,7 +3780,7 @@ static void wm1811_mic_work(struct work_struct *work) + snd_soc_dapm_sync(dapm); + } + +- mutex_lock(&wm8994->accdet_lock); ++ guard(mutex)(&wm8994->accdet_lock); + + dev_dbg(component->dev, "Starting mic detection\n"); + +@@ -3803,10 +3798,6 @@ static void wm1811_mic_work(struct work_struct *work) + snd_soc_component_update_bits(component, WM8958_MIC_DETECT_1, + WM8958_MICD_ENA, WM8958_MICD_ENA); + } +- +- mutex_unlock(&wm8994->accdet_lock); +- +- pm_runtime_put(component->dev); + } + + static irqreturn_t wm1811_jackdet_irq(int irq, void *data) +@@ -4026,15 +4017,10 @@ static void wm8958_mic_work(struct work_struct *work) + mic_complete_work.work); + struct snd_soc_component *component = wm8994->hubs.component; + +- pm_runtime_get_sync(component->dev); +- +- mutex_lock(&wm8994->accdet_lock); ++ guard(pm_runtime_active)(component->dev); ++ guard(mutex)(&wm8994->accdet_lock); + + wm8994->mic_id_cb(wm8994->mic_id_cb_data, wm8994->mic_status); +- +- mutex_unlock(&wm8994->accdet_lock); +- +- pm_runtime_put(component->dev); + } + + static irqreturn_t wm8958_mic_irq(int irq, void *data) +diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c +--- a/sound/soc/codecs/wm9712.c ++++ b/sound/soc/codecs/wm9712.c +@@ -6,6 +6,7 @@ + * Author: Liam Girdwood + */ + ++#include + #include + #include + #include +@@ -229,7 +230,7 @@ static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol, + shift = mc->shift & 0xff; + mask = 1 << shift; + +- mutex_lock(&wm9712->lock); ++ guard(mutex)(&wm9712->lock); + old = wm9712->hp_mixer[mixer]; + if (ucontrol->value.integer.value[0]) + wm9712->hp_mixer[mixer] |= mask; +@@ -251,8 +252,6 @@ static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol, + &update); + } + +- mutex_unlock(&wm9712->lock); +- + return change; + } + +diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c +--- a/sound/soc/codecs/wm9713.c ++++ b/sound/soc/codecs/wm9713.c +@@ -11,6 +11,7 @@ + * o Support for DAPM + */ + ++#include + #include + #include + #include +@@ -238,7 +239,7 @@ static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol, + shift = mc->shift & 0xff; + mask = (1 << shift); + +- mutex_lock(&wm9713->lock); ++ guard(mutex)(&wm9713->lock); + old = wm9713->hp_mixer[mixer]; + if (ucontrol->value.integer.value[0]) + wm9713->hp_mixer[mixer] |= mask; +@@ -260,8 +261,6 @@ static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol, + &update); + } + +- mutex_unlock(&wm9713->lock); +- + return change; + } + +diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c +--- a/sound/soc/codecs/wm_adsp.c ++++ b/sound/soc/codecs/wm_adsp.c +@@ -348,7 +348,6 @@ int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); +- int ret = 1; + + if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw) + return 0; +@@ -356,16 +355,14 @@ int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, + if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW) + return -EINVAL; + +- mutex_lock(&dsp[e->shift_l].cs_dsp.pwr_lock); ++ guard(mutex)(&dsp[e->shift_l].cs_dsp.pwr_lock); + + if (dsp[e->shift_l].cs_dsp.booted || !list_empty(&dsp[e->shift_l].compr_list)) +- ret = -EBUSY; ++ return -EBUSY; + else + dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; + +- mutex_unlock(&dsp[e->shift_l].cs_dsp.pwr_lock); +- +- return ret; ++ return 1; + } + EXPORT_SYMBOL_GPL(wm_adsp_fw_put); + +@@ -450,15 +447,13 @@ static int wm_coeff_put_acked(struct snd_kcontrol *kctl, + if (val == 0) + return 0; /* 0 means no event */ + +- mutex_lock(&cs_ctl->dsp->pwr_lock); ++ guard(mutex)(&cs_ctl->dsp->pwr_lock); + + if (cs_ctl->enabled) + ret = cs_dsp_coeff_write_acked_control(cs_ctl, val); + else + ret = -EPERM; + +- mutex_unlock(&cs_ctl->dsp->pwr_lock); +- + if (ret < 0) + return ret; + +@@ -486,15 +481,13 @@ static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, + struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; + int ret = 0; + +- mutex_lock(&cs_ctl->dsp->pwr_lock); ++ guard(mutex)(&cs_ctl->dsp->pwr_lock); + + ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, cs_ctl->cache, size); + + if (!ret && copy_to_user(bytes, cs_ctl->cache, size)) + ret = -EFAULT; + +- mutex_unlock(&cs_ctl->dsp->pwr_lock); +- + return ret; + } + +@@ -694,10 +687,9 @@ int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, + struct cs_dsp_coeff_ctl *cs_ctl; + int ret; + +- mutex_lock(&dsp->cs_dsp.pwr_lock); ++ guard(mutex)(&dsp->cs_dsp.pwr_lock); + cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg); + ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len); +- mutex_unlock(&dsp->cs_dsp.pwr_lock); + + if (ret < 0) + return ret; +@@ -709,14 +701,10 @@ EXPORT_SYMBOL_GPL(wm_adsp_write_ctl); + int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type, + unsigned int alg, void *buf, size_t len) + { +- int ret; ++ guard(mutex)(&dsp->cs_dsp.pwr_lock); + +- mutex_lock(&dsp->cs_dsp.pwr_lock); +- ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg), ++ return cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg), + 0, buf, len); +- mutex_unlock(&dsp->cs_dsp.pwr_lock); +- +- return ret; + } + EXPORT_SYMBOL_GPL(wm_adsp_read_ctl); + +@@ -1270,38 +1258,32 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) + { + struct wm_adsp_compr *compr, *tmp; + struct snd_soc_pcm_runtime *rtd = stream->private_data; +- int ret = 0; + +- mutex_lock(&dsp->cs_dsp.pwr_lock); ++ guard(mutex)(&dsp->cs_dsp.pwr_lock); + + if (wm_adsp_fw[dsp->fw].num_caps == 0) { + adsp_err(dsp, "%s: Firmware does not support compressed API\n", + snd_soc_rtd_to_codec(rtd, 0)->name); +- ret = -ENXIO; +- goto out; ++ return -ENXIO; + } + + if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { + adsp_err(dsp, "%s: Firmware does not support stream direction\n", + snd_soc_rtd_to_codec(rtd, 0)->name); +- ret = -EINVAL; +- goto out; ++ return -EINVAL; + } + + list_for_each_entry(tmp, &dsp->compr_list, list) { + if (!strcmp(tmp->name, snd_soc_rtd_to_codec(rtd, 0)->name)) { + adsp_err(dsp, "%s: Only a single stream supported per dai\n", + snd_soc_rtd_to_codec(rtd, 0)->name); +- ret = -EBUSY; +- goto out; ++ return -EBUSY; + } + } + + compr = kzalloc_obj(*compr); +- if (!compr) { +- ret = -ENOMEM; +- goto out; +- } ++ if (!compr) ++ return -ENOMEM; + + compr->dsp = dsp; + compr->stream = stream; +@@ -1311,10 +1293,7 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) + + stream->runtime->private_data = compr; + +-out: +- mutex_unlock(&dsp->cs_dsp.pwr_lock); +- +- return ret; ++ return 0; + } + EXPORT_SYMBOL_GPL(wm_adsp_compr_open); + +@@ -1324,7 +1303,7 @@ int wm_adsp_compr_free(struct snd_soc_component *component, + struct wm_adsp_compr *compr = stream->runtime->private_data; + struct wm_adsp *dsp = compr->dsp; + +- mutex_lock(&dsp->cs_dsp.pwr_lock); ++ guard(mutex)(&dsp->cs_dsp.pwr_lock); + + wm_adsp_compr_detach(compr); + list_del(&compr->list); +@@ -1332,8 +1311,6 @@ int wm_adsp_compr_free(struct snd_soc_component *component, + kfree(compr->raw_buf); + kfree(compr); + +- mutex_unlock(&dsp->cs_dsp.pwr_lock); +- + return 0; + } + EXPORT_SYMBOL_GPL(wm_adsp_compr_free); +@@ -1741,7 +1718,7 @@ int wm_adsp_compr_trigger(struct snd_soc_component *component, + + compr_dbg(compr, "Trigger: %d\n", cmd); + +- mutex_lock(&dsp->cs_dsp.pwr_lock); ++ guard(mutex)(&dsp->cs_dsp.pwr_lock); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: +@@ -1777,8 +1754,6 @@ int wm_adsp_compr_trigger(struct snd_soc_component *component, + break; + } + +- mutex_unlock(&dsp->cs_dsp.pwr_lock); +- + return ret; + } + EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger); +@@ -1907,21 +1882,20 @@ int wm_adsp_compr_pointer(struct snd_soc_component *component, + + compr_dbg(compr, "Pointer request\n"); + +- mutex_lock(&dsp->cs_dsp.pwr_lock); ++ guard(mutex)(&dsp->cs_dsp.pwr_lock); + + buf = compr->buf; + + if (dsp->fatal_error || !buf || buf->error) { + snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN); +- ret = -EIO; +- goto out; ++ return -EIO; + } + + if (buf->avail < wm_adsp_compr_frag_words(compr)) { + ret = wm_adsp_buffer_update_avail(buf); + if (ret < 0) { + compr_err(compr, "Error reading avail: %d\n", ret); +- goto out; ++ return ret; + } + + /* +@@ -1934,14 +1908,14 @@ int wm_adsp_compr_pointer(struct snd_soc_component *component, + if (buf->error) + snd_compr_stop_error(stream, + SNDRV_PCM_STATE_XRUN); +- goto out; ++ return ret; + } + + ret = wm_adsp_buffer_reenable_irq(buf); + if (ret < 0) { + compr_err(compr, "Failed to re-enable buffer IRQ: %d\n", + ret); +- goto out; ++ return ret; + } + } + } +@@ -1950,9 +1924,6 @@ int wm_adsp_compr_pointer(struct snd_soc_component *component, + tstamp->copied_total += buf->avail * CS_DSP_DATA_WORD_SIZE; + tstamp->sampling_rate = compr->sample_rate; + +-out: +- mutex_unlock(&dsp->cs_dsp.pwr_lock); +- + return ret; + } + EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer); +@@ -2063,15 +2034,13 @@ int wm_adsp_compr_copy(struct snd_soc_component *component, + struct wm_adsp *dsp = compr->dsp; + int ret; + +- mutex_lock(&dsp->cs_dsp.pwr_lock); ++ guard(mutex)(&dsp->cs_dsp.pwr_lock); + + if (stream->direction == SND_COMPRESS_CAPTURE) + ret = wm_adsp_compr_read(compr, buf, count); + else + ret = -ENOTSUPP; + +- mutex_unlock(&dsp->cs_dsp.pwr_lock); +- + return ret; + } + EXPORT_SYMBOL_GPL(wm_adsp_compr_copy); +diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c +--- a/sound/soc/codecs/wsa881x.c ++++ b/sound/soc/codecs/wsa881x.c +@@ -1178,7 +1178,13 @@ static int wsa881x_runtime_resume(struct device *dev) + } + + regcache_cache_only(regmap, false); +- regcache_sync(regmap); ++ ret = regcache_sync(regmap); ++ if (ret) { ++ regcache_cache_only(regmap, true); ++ regcache_mark_dirty(regmap); ++ gpiod_direction_output(wsa881x->sd_n, 1); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c +--- a/sound/soc/codecs/wsa883x.c ++++ b/sound/soc/codecs/wsa883x.c +@@ -4,6 +4,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -1237,9 +1238,8 @@ static int wsa883x_spkr_event(struct snd_soc_dapm_widget *w, + + switch (event) { + case SND_SOC_DAPM_POST_PMU: +- mutex_lock(&wsa883x->sp_lock); +- wsa883x->pa_on = true; +- mutex_unlock(&wsa883x->sp_lock); ++ scoped_guard(mutex, &wsa883x->sp_lock) ++ wsa883x->pa_on = true; + + switch (wsa883x->dev_mode) { + case RECEIVER: +@@ -1290,9 +1290,8 @@ static int wsa883x_spkr_event(struct snd_soc_dapm_widget *w, + WSA883X_GLOBAL_PA_EN_MASK, 0); + snd_soc_component_write_field(component, WSA883X_PDM_WD_CTL, + WSA883X_PDM_EN_MASK, 0); +- mutex_lock(&wsa883x->sp_lock); +- wsa883x->pa_on = false; +- mutex_unlock(&wsa883x->sp_lock); ++ scoped_guard(mutex, &wsa883x->sp_lock) ++ wsa883x->pa_on = false; + break; + } + return 0; +@@ -1696,9 +1695,15 @@ static int wsa883x_runtime_suspend(struct device *dev) + static int wsa883x_runtime_resume(struct device *dev) + { + struct regmap *regmap = dev_get_regmap(dev, NULL); ++ int ret; + + regcache_cache_only(regmap, false); +- regcache_sync(regmap); ++ ret = regcache_sync(regmap); ++ if (ret) { ++ regcache_cache_only(regmap, true); ++ regcache_mark_dirty(regmap); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c +--- a/sound/soc/codecs/wsa884x.c ++++ b/sound/soc/codecs/wsa884x.c +@@ -1701,9 +1701,8 @@ static int wsa884x_spkr_event(struct snd_soc_dapm_widget *w, + + switch (event) { + case SND_SOC_DAPM_POST_PMU: +- mutex_lock(&wsa884x->sp_lock); +- wsa884x->pa_on = true; +- mutex_unlock(&wsa884x->sp_lock); ++ scoped_guard(mutex, &wsa884x->sp_lock) ++ wsa884x->pa_on = true; + + wsa884x_spkr_post_pmu(component, wsa884x); + +@@ -1717,9 +1716,8 @@ static int wsa884x_spkr_event(struct snd_soc_dapm_widget *w, + WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK, + 0x0); + +- mutex_lock(&wsa884x->sp_lock); +- wsa884x->pa_on = false; +- mutex_unlock(&wsa884x->sp_lock); ++ scoped_guard(mutex, &wsa884x->sp_lock) ++ wsa884x->pa_on = false; + break; + } + +@@ -2147,9 +2145,15 @@ static int wsa884x_runtime_suspend(struct device *dev) + static int wsa884x_runtime_resume(struct device *dev) + { + struct regmap *regmap = dev_get_regmap(dev, NULL); ++ int ret; + + regcache_cache_only(regmap, false); +- regcache_sync(regmap); ++ ret = regcache_sync(regmap); ++ if (ret) { ++ regcache_cache_only(regmap, true); ++ regcache_mark_dirty(regmap); ++ return ret; ++ } + + return 0; + } +diff --git a/sound/soc/codecs/wsa885x.c b/sound/soc/codecs/wsa885x.c +new file mode 100644 +--- /dev/null ++++ b/sound/soc/codecs/wsa885x.c +@@ -0,0 +1,1533 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. ++ */ ++ ++/* WSA885X codec driver */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Control Registers - Audio Processing */ ++#define WSA885X_SMP_AMP_CTRL_STEREO_STEREO_SMP_AMP_CTRL_I2S 0x0000 ++#define WSA885X_SMP_AMP_CTRL_STEREO_CMT_GRP_MASK 0x0004 ++#define WSA885X_SMP_AMP_CTRL_STEREO_IT21_CLUSERINDEX 0x0140 ++#define WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID 0x0208 ++#define WSA885X_SMP_AMP_CTRL_STEREO_CS21_SAMPLERATEINDEX 0x0240 ++#define WSA885X_SMP_AMP_CTRL_STEREO_PPU21_POSTURENUMBER 0x0340 ++#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0 0x4405 ++#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1 0x4406 ++#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB 0x4409 ++#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_MSB 0x6409 ++#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB 0x440a ++#define WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_MSB 0x640a ++#define WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS 0x0a04 ++#define WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS 0x0a40 ++#define WSA885X_SMP_AMP_CTRL_STEREO_OT23_USAGE 0x0b10 ++#define WSA885X_SMP_AMP_CTRL_STEREO_CS24_SAMPLERATEINDEX 0x0e40 ++ ++/* Analog Top Registers - Power and Clock Control */ ++#define WSA885X_ANA_TOP_PON_CKSK_CTL_0 0x800d ++#define WSA885X_ANA_TOP_BG_TVP_UVLO1_PROG 0x8024 ++#define WSA885X_ANA_TOP_BG_TVP_UVLO2_PROG 0x8025 ++#define WSA885X_ANA_TOP_BG_TVP_OVRD_CTL 0x8034 ++ ++/* Analog PLL Registers */ ++#define WSA885X_ANA_PLL_DIV_CTL_0 0x8090 ++#define WSA885X_ANA_PLL_DIV_CTL_1 0x8091 ++#define WSA885X_ANA_TOP_PLL_VCO_CTL 0x8092 ++#define WSA885X_ANA_TOP_PLL_LOOPFILT_0 0x8093 ++#define WSA885X_ANA_TOP_PLL_OVRD_CTL 0x8098 ++#define WSA885X_ANA_TOP_PLL_STATUS_0 0x809a ++#define WSA885X_ANA_TOP_PLL_STATUS_1 0x809b ++ ++/* Analog Boost Control Registers */ ++#define WSA885X_ANA_TOP_BOOST_STB_CTRL2 0x805b ++#define WSA885X_ANA_TOP_BOOST_STB_CTRL3 0x805c ++#define WSA885X_ANA_TOP_BOOST_BYP_CTRL2 0x805e ++#define WSA885X_ANA_TOP_BOOST_BYP_CTRL3 0x805f ++#define WSA885X_ANA_TOP_BOOST_MISC 0x8063 ++#define WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL2 0x8065 ++#define WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL4 0x8067 ++ ++/* Analog IV Sense ADC Registers */ ++#define WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL2 0x80ca ++#define WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3 0x80cb ++#define WSA885X_ANA_TOP_IVSENSE_ADC_REF_CTL 0x80cc ++#define WSA885X_ANA_TOP_IVSENSE_ADC_CDAC_CAL_CTL2 0x80d0 ++ ++/* Analog Speaker Power Stage Registers */ ++#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_CTRL3 0x8108 ++#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_TUNE3 0x810b ++#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_CTRL3 0x810e ++#define WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_TUNE3 0x8111 ++#define WSA885X_ANA_TOP_SPK_TOP_SPARE3 0x813c ++#define WSA885X_SPK_TOP_LF_CH1_CTRL11 0x811c ++#define WSA885X_SPK_TOP_LF_CH1_TUNE1 0x811d ++#define WSA885X_SPK_TOP_LF_CH2_TUNE1 0x8129 ++#define WSA885X_SPK_TOP_LF_CH1_CTRL9 0x811a ++#define WSA885X_SPK_TOP_LF_CH2_CTRL9 0x8126 ++#define WSA885X_SPK_TOP_LF_CH2_CTRL11 0x8128 ++#define WSA885X_SPK_TOP_COMMON_CTRL2 0x8102 ++#define WSA885X_SPK_TOP_COMMON_TUNE1 0x8103 ++#define WSA885X_IVSENSE_VSNS_ISNS_CTL_CH1 0x80ba ++#define WSA885X_DIG_CTRL0_TOP_CLK_CFG 0x8418 ++#define WSA885X_DIG_CTRL0_SDCA_COMMIT 0x8419 ++#define WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE 0x841a ++#define WSA885X_DIG_CTRL0_SYS_CLK_SEL 0x841b ++#define WSA885X_DIG_CTRL0_CDC_CLK_CTL 0x841c ++#define WSA885X_DIG_CTRL0_PA_FSM_CTL 0x8420 ++#define WSA885X_DIG_CTRL0_POWER_FSM_CTL0 0x8423 ++#define WSA885X_DIG_CTRL0_POWER_FSM_CTL1 0x8424 ++#define WSA885X_DIG_CTRL0_PA0_FSM_CTL1 0x842b ++#define WSA885X_DIG_CTRL0_PA1_FSM_CTL1 0x8435 ++#define WSA885X_DIG_CTRL0_VBAT_THRM_FLT_CTL 0x8458 ++#define WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL 0x8470 ++#define WSA885X_DIG_CTRL0_GAIN_RAMP0_CTL1 0x84b4 ++#define WSA885X_DIG_CTRL0_GAIN_RAMP1_CTL1 0x84b7 ++#define WSA885X_DIG_CTRL0_PCM_DATA_WD0_CTL1 0x84A0 ++#define WSA885X_DIG_CTRL0_PCM_DATA_WD1_CTL1 0x84A4 ++ ++/* Digital Control 1 Registers - I2S/TDM Interface */ ++#define WSA885X_DIG_CTRL1_I2S_CTL0 0x85A0 ++#define WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX 0x85A2 ++#define WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX 0x85A3 ++#define WSA885X_DIG_CTRL1_I2S_TDM_CTL0 0x85A7 ++#define WSA885X_DIG_CTRL1_I2S_TDM_CTL1 0x85A9 ++#define WSA885X_DIG_CTRL1_I2S_TDM_CH_RX 0x85AA ++#define WSA885X_DIG_CTRL1_I2S_TDM_CH_TX 0x85AB ++#define WSA885X_DIG_CTRL1_I2S_RESET_CTL 0x85AE ++ ++/* CDC RX Path Registers - Audio Data Path */ ++#define WSA885X_CDC_RX0_RX_PATH_CFG0 0x8601 ++#define WSA885X_CDC_RX0_RX_PATH_CFG1 0x8602 ++#define WSA885X_CDC_RX0_RX_PATH_CTL 0x8606 ++#define WSA885X_RX0_RX_PATH_DSMDEM_CTL 0x8613 ++#define WSA885X_CDC_RX1_RX_PATH_CFG0 0x8621 ++#define WSA885X_CDC_RX1_RX_PATH_CFG1 0x8622 ++#define WSA885X_CDC_RX1_RX_PATH_CTL 0x8626 ++#define WSA885X_RX1_RX_PATH_DSMDEM_CTL 0x8633 ++ ++/* CDC Compander Registers - Dynamic Range Control */ ++#define WSA885X_CDC_COMPANDER0_CTL0 0x8640 ++#define WSA885X_CDC_COMPANDER0_CTL7 0x8647 ++#define WSA885X_CDC_COMPANDER1_CTL0 0x8660 ++#define WSA885X_CDC_COMPANDER1_CTL7 0x8667 ++ ++/* CDC Speaker Protection Registers - IV Sense */ ++#define WSA885X_CDC_VSENSE0_SPKR_PROT_PATH_CTL 0x86A1 ++#define WSA885X_CDC_VSENSE1_SPKR_PROT_PATH_CTL 0x86B1 ++#define WSA885X_CDC_ISENSE0_SPKR_PROT_PATH_CTL 0x86A9 ++#define WSA885X_CDC_ISENSE1_SPKR_PROT_PATH_CTL 0x86B9 ++ ++/* CDC Class-H Registers - Headroom Control */ ++#define WSA885X_CDC_CLSH_V1P8_BP_CTL1 0x86CD ++#define WSA885X_CDC_CLSH_V1P8_BP_CTL0 0x86CC ++#define WSA885X_CDC_CLSH_CLSH_SIG_DP_CTL0 0x86C7 ++#define WSA885X_CDC_CLSH_CLSH_V_HD_PA 0x86C3 ++#define WSA885X_CDC_CLSH_V1P8_BP_CTL2 0x86CE ++ ++/* Driver Constants */ ++#define WSA885X_CLK_RATE_FIXED 73728000 ++#define WSA885X_NUM_REGS 0x03 ++ ++/* Interrupt Registers */ ++#define WSA885X_INTR_STATUS0 0x8584 ++#define WSA885X_INTR_MASK0 0x8581 ++#define WSA885X_INTR_CLEAR0 0x8587 ++ ++/* Power and PA FSM Control Registers */ ++#define WSA885X_PA0_FSM_CTL0 0x842A ++#define WSA885X_PA1_FSM_CTL0 0x8434 ++ ++/* Digital Control GPIO and Interrupt Registers */ ++#define WSA885X_DIG_CTRL1_PIN_CT 0x8510 ++#define WSA885X_DIG_CTRL1_SPMI_PAD_GPIO2_CTL 0x8518 ++#define WSA885X_DIG_CTRL1_INTR_MODE 0x8580 ++ ++#define WSA885X_I2S_CTL0_PCM_RATE_MASK GENMASK(4, 1) ++#define WSA885X_I2S_CTL0_ENABLE_MASK BIT(0) ++#define WSA885X_I2S_CTL0_PCM_RATE(v) \ ++ FIELD_PREP(WSA885X_I2S_CTL0_PCM_RATE_MASK, (v)) ++#define WSA885X_I2S_CTL0_PCM_RATE_8KHZ 0x0 ++#define WSA885X_I2S_CTL0_PCM_RATE_16KHZ 0x1 ++#define WSA885X_I2S_CTL0_PCM_RATE_32KHZ 0x2 ++#define WSA885X_I2S_CTL0_PCM_RATE_48_OR_44KHZ 0x3 ++#define WSA885X_I2S_CTL0_PCM_RATE_96_OR_88KHZ 0x4 ++#define WSA885X_I2S_CTL0_PCM_RATE_192_OR_176KHZ 0x5 ++#define WSA885X_I2S_CTL0_PCM_RATE_384_OR_352KHZ 0x6 ++#define WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK GENMASK(2, 0) ++#define WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK GENMASK(6, 4) ++#define WSA885X_I2S_CFG0_TDM_TX_SLOT0(v) \ ++ FIELD_PREP_CONST(WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK, (v)) ++#define WSA885X_I2S_CFG0_TDM_TX_SLOT1(v) \ ++ FIELD_PREP_CONST(WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK, (v)) ++#define WSA885X_I2S_CFG1_TDM_TX_SLOT2_MASK GENMASK(2, 0) ++#define WSA885X_I2S_CFG1_TDM_TX_SLOT3_MASK GENMASK(6, 4) ++#define WSA885X_I2S_CFG1_TDM_TX_SLOT2(v) \ ++ FIELD_PREP_CONST(WSA885X_I2S_CFG1_TDM_TX_SLOT2_MASK, (v)) ++#define WSA885X_I2S_CFG1_TDM_TX_SLOT3(v) \ ++ FIELD_PREP_CONST(WSA885X_I2S_CFG1_TDM_TX_SLOT3_MASK, (v)) ++#define WSA885X_I2S_TDM_CTL0_I2S_TDM_EN_MASK BIT(0) ++#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK GENMASK(3, 2) ++#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_2 \ ++ FIELD_PREP_CONST(WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, 0) ++#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_4 \ ++ FIELD_PREP_CONST(WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, 1) ++#define WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_8 \ ++ FIELD_PREP_CONST(WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, 3) ++#define WSA885X_I2S_TDM_CH_TX_CH0_EN BIT(0) ++#define WSA885X_I2S_TDM_CH_TX_CH1_EN BIT(1) ++#define WSA885X_I2S_TDM_CH_TX_CH2_EN BIT(2) ++#define WSA885X_I2S_TDM_CH_TX_CH3_EN BIT(3) ++#define WSA885X_I2S_TDM_CH_RX_CH0_EN BIT(0) ++#define WSA885X_I2S_TDM_CH_RX_CH3_EN BIT(3) ++#define WSA885X_I2S_RESET_CTL_RESET_MASK BIT(0) ++#define WSA885X_PCM_DATA_WD_CTL1_PCM_DATA_WD_EN_MASK BIT(2) ++#define WSA885X_POWER_FSM_CTL0_CLEAR_ERROR_MASK BIT(3) ++#define WSA885X_PA_FSM_CTL0_CLEAR_ERROR_MASK BIT(2) ++ ++#define WSA885X_I2S_TX_SLOT_ISENSE0 0x1 ++#define WSA885X_I2S_TX_SLOT_ISENSE1 0x2 ++#define WSA885X_I2S_TX_SLOT_CUR_SENSE0 0x5 ++#define WSA885X_I2S_TX_SLOT_CUR_SENSE1 0x6 ++ ++/* RX Sample Rate Index Values - Audio Playback Path */ ++#define WSA885X_RX_RATE_8000HZ 0x00 ++#define WSA885X_RX_RATE_16000HZ 0x01 ++#define WSA885X_RX_RATE_32000HZ 0x02 ++#define WSA885X_RX_RATE_44100HZ 0x03 ++#define WSA885X_RX_RATE_48000HZ 0x04 ++#define WSA885X_RX_RATE_96000HZ 0x05 ++#define WSA885X_RX_RATE_192000HZ 0x06 ++#define WSA885X_RX_RATE_384000HZ 0x07 ++ ++/* VI Sample Rate Index Values - Voltage/Current Sensing Path */ ++#define WSA885X_VI_RATE_8000HZ 0x00 ++#define WSA885X_VI_RATE_16000HZ 0x01 ++#define WSA885X_VI_RATE_44100HZ 0x02 ++#define WSA885X_VI_RATE_48000HZ 0x03 ++#define WSA885X_VI_RATE_96000HZ 0x04 ++#define WSA885X_VI_RATE_22050HZ 0x05 ++#define WSA885X_VI_RATE_24000HZ 0x06 ++#define WSA885X_VI_RATE_192000HZ 0x07 ++#define WSA885X_VI_RATE_384000HZ 0x08 ++ ++/* Channel Configuration Masks */ ++#define WSA885X_CHANNEL_STEREO 0x03 ++#define WSA885X_CHANNEL_MONO_LEFT 0x01 ++#define WSA885X_CHANNEL_MONO_RIGHT 0x02 ++ ++#define WSA885X_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \ ++ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | \ ++ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) ++ ++#define WSA885X_PLL_LOCK_BIT BIT(0) ++ ++#define WSA885X_FU21_VOL_STEPS 124 ++#define WSA885X_USAGE_MODE_MAX 8 ++static const DECLARE_TLV_DB_SCALE(wsa885x_fu21_digital_gain, -8400, 100, 0); ++ ++static bool wsa885x_is_valid_rx_slot_mask(u32 mask) ++{ ++ return mask == WSA885X_CHANNEL_MONO_LEFT || ++ mask == WSA885X_CHANNEL_MONO_RIGHT || ++ mask == WSA885X_CHANNEL_STEREO; ++} ++ ++static const char *const wsa885x_supply_name[] = { ++ "vdd-1p8", ++ "vdd-io", ++}; ++ ++enum { ++ WSA885X_BATT_1S = 1, ++ WSA885X_BATT_2S, ++}; ++ ++enum { ++ WSA885X_IRQ_INT_SAF2WAR = 0, ++ WSA885X_IRQ_INT_WAR2SAF, ++ WSA885X_IRQ_INT_DISABLE, ++ WSA885X_IRQ_INT_PA0_OCP, ++ WSA885X_IRQ_INT_PA1_OCP, ++ WSA885X_IRQ_INT_CLIP0, ++ WSA885X_IRQ_INT_CLIP1, ++ WSA885X_IRQ_INT_CLK_WD, ++ WSA885X_IRQ_INT_INTR_GPIO1_PIN, ++ WSA885X_IRQ_INT_INTR_GPIO2_PIN, ++ WSA885X_IRQ_INT_UVLO, ++ WSA885X_IRQ_INT_BOP, ++ WSA885X_IRQ_INT_PA0_FSM_ERR, ++ WSA885X_IRQ_INT_PA1_FSM_ERR, ++ WSA885X_IRQ_INT_MAIN_FSM_ERR, ++ WSA885X_IRQ_INT_PCM_DATA0_WD, ++ WSA885X_IRQ_INT_PCM_DATA1_WD, ++ WSA885X_IRQ_INT_PCM_DATA0_DC, ++ WSA885X_IRQ_INT_PCM_DATA1_DC, ++ WSA885X_IRQ_INT_PLL_UNLOCKED, ++ WSA885X_IRQ_INT_PROT_MODE_CHANGE, ++ WSA885X_IRQ_INT_PB_CLOCK_VALID, ++ WSA885X_IRQ_INT_SENSE_CLOCK_VALID, ++ WSA885X_IRQ_MAX, ++}; ++ ++struct wsa885x_priv { ++ struct i2c_client *client; ++ struct regmap *regmap; ++ struct device *dev; ++ struct snd_soc_component *component; ++ struct gpio_desc *sd_n; ++ struct reset_control *sd_reset; ++ u32 usage_mode; ++ u32 rx_slot_mask; ++ u32 batt_conf; ++ int stereo_vol_db; ++ struct mutex state_lock; /* protects mutable control state */ ++}; ++ ++struct wsa885x_reg_update { ++ unsigned int reg; ++ unsigned int mask; ++ unsigned int val; ++}; ++ ++static const struct regmap_range_cfg wsa885x_regmap_ranges[] = { ++ { ++ .range_min = 0, ++ .range_max = 0x88ff, ++ .selector_reg = 0x0, ++ .selector_mask = 0xFF, ++ .selector_shift = 0, ++ .window_start = 0, ++ .window_len = 0x100, ++ }, ++}; ++ ++static const struct reg_default wsa885x_codec_reg_defaults[] = { ++ {WSA885X_SMP_AMP_CTRL_STEREO_STEREO_SMP_AMP_CTRL_I2S, 0x00}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_IT21_CLUSERINDEX, 0x01}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_CMT_GRP_MASK, 0x00}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_OT23_USAGE, 0x00}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID, 0x00}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_CS21_SAMPLERATEINDEX, 0x04}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_PPU21_POSTURENUMBER, 0x01}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0, 0x01}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1, 0x01}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_MSB, 0xac}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB, 0x00}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_MSB, 0xac}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB, 0x00}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS, 0x03}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS, 0x03}, ++ {WSA885X_SMP_AMP_CTRL_STEREO_CS24_SAMPLERATEINDEX, 0x03}, ++ {WSA885X_ANA_TOP_PON_CKSK_CTL_0, 0x00}, ++ {WSA885X_ANA_TOP_BG_TVP_UVLO1_PROG, 0x19}, ++ {WSA885X_ANA_TOP_BG_TVP_UVLO2_PROG, 0x22}, ++ {WSA885X_ANA_PLL_DIV_CTL_0, 0x0c}, ++ {WSA885X_ANA_PLL_DIV_CTL_1, 0x50}, ++ {WSA885X_ANA_TOP_PLL_VCO_CTL, 0x00}, ++ {WSA885X_ANA_TOP_PLL_LOOPFILT_0, 0xb4}, ++ {WSA885X_ANA_TOP_PLL_OVRD_CTL, 0x00}, ++ {WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x00}, ++ {WSA885X_ANA_TOP_BOOST_STB_CTRL2, 0x03}, ++ {WSA885X_ANA_TOP_BOOST_STB_CTRL3, 0x3c}, ++ {WSA885X_ANA_TOP_BOOST_BYP_CTRL2, 0xc5}, ++ {WSA885X_ANA_TOP_BOOST_BYP_CTRL3, 0x13}, ++ {WSA885X_ANA_TOP_BOOST_MISC, 0x79}, ++ {WSA885X_ANA_TOP_SPK_TOP_SPARE3, 0x00}, ++ {WSA885X_SPK_TOP_COMMON_CTRL2, 0x08}, ++ {WSA885X_SPK_TOP_LF_CH1_CTRL11, 0x09}, ++ {WSA885X_SPK_TOP_LF_CH1_TUNE1, 0x00}, ++ {WSA885X_SPK_TOP_LF_CH2_TUNE1, 0x00}, ++ {WSA885X_SPK_TOP_LF_CH1_CTRL9, 0x00}, ++ {WSA885X_SPK_TOP_LF_CH2_CTRL9, 0x00}, ++ {WSA885X_SPK_TOP_LF_CH2_CTRL11, 0x09}, ++ {WSA885X_SPK_TOP_COMMON_TUNE1, 0x03}, ++ {WSA885X_IVSENSE_VSNS_ISNS_CTL_CH1, 0x00}, ++ {WSA885X_DIG_CTRL0_CDC_CLK_CTL, 0x0e}, ++ {WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL2, 0x40}, ++ {WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL4, 0xff}, ++ {WSA885X_ANA_TOP_PLL_STATUS_0, 0x00}, ++ {WSA885X_ANA_TOP_PLL_STATUS_1, 0x00}, ++ {WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL2, 0x84}, ++ {WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3, 0x02}, ++ {WSA885X_ANA_TOP_IVSENSE_ADC_REF_CTL, 0x00}, ++ {WSA885X_ANA_TOP_IVSENSE_ADC_CDAC_CAL_CTL2, 0xe0}, ++ {WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_CTRL3, 0xa4}, ++ {WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_TUNE3, 0xc9}, ++ {WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_CTRL3, 0xa4}, ++ {WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_TUNE3, 0xc9}, ++ {WSA885X_DIG_CTRL0_TOP_CLK_CFG, 0x00}, ++ {WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x00}, ++ {WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE, 0x00}, ++ {WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x00}, ++ {WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00}, ++ {WSA885X_DIG_CTRL0_POWER_FSM_CTL0, 0x05}, ++ {WSA885X_DIG_CTRL0_POWER_FSM_CTL1, 0x00}, ++ {WSA885X_DIG_CTRL0_PA0_FSM_CTL1, 0x45}, ++ {WSA885X_DIG_CTRL0_PA1_FSM_CTL1, 0x45}, ++ {WSA885X_DIG_CTRL0_VBAT_THRM_FLT_CTL, 0x7f}, ++ {WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x00}, ++ {WSA885X_DIG_CTRL0_GAIN_RAMP0_CTL1, 0x01}, ++ {WSA885X_DIG_CTRL0_GAIN_RAMP1_CTL1, 0x01}, ++ {WSA885X_DIG_CTRL1_I2S_CTL0, 0x06}, ++ {WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, 0x00}, ++ {WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX, 0x00}, ++ {WSA885X_DIG_CTRL1_I2S_TDM_CTL0, 0x00}, ++ {WSA885X_DIG_CTRL1_I2S_TDM_CTL1, 0x05}, ++ {WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, 0x00}, ++ {WSA885X_DIG_CTRL1_I2S_RESET_CTL, 0x00}, ++ {WSA885X_DIG_CTRL1_I2S_TDM_CH_RX, WSA885X_I2S_TDM_CH_RX_CH3_EN}, ++ {WSA885X_CDC_RX0_RX_PATH_CFG0, 0x89}, ++ {WSA885X_CDC_RX0_RX_PATH_CFG1, 0x64}, ++ {WSA885X_CDC_RX0_RX_PATH_CTL, 0x24}, ++ {WSA885X_RX0_RX_PATH_DSMDEM_CTL, 0x01}, ++ {WSA885X_CDC_RX1_RX_PATH_CFG0, 0x89}, ++ {WSA885X_CDC_RX1_RX_PATH_CFG1, 0x64}, ++ {WSA885X_CDC_RX1_RX_PATH_CTL, 0x04}, ++ {WSA885X_RX1_RX_PATH_DSMDEM_CTL, 0x01}, ++ {WSA885X_CDC_COMPANDER0_CTL0, 0x01}, ++ {WSA885X_CDC_COMPANDER0_CTL7, 0x2a}, ++ {WSA885X_CDC_COMPANDER1_CTL0, 0x01}, ++ {WSA885X_CDC_COMPANDER1_CTL7, 0x2a}, ++ {WSA885X_CDC_VSENSE0_SPKR_PROT_PATH_CTL, 0x14}, ++ {WSA885X_CDC_VSENSE1_SPKR_PROT_PATH_CTL, 0x14}, ++ {WSA885X_CDC_ISENSE0_SPKR_PROT_PATH_CTL, 0x14}, ++ {WSA885X_CDC_ISENSE1_SPKR_PROT_PATH_CTL, 0x14}, ++ {WSA885X_CDC_CLSH_V1P8_BP_CTL1, 0x50}, ++ {WSA885X_CDC_CLSH_V1P8_BP_CTL0, 0x6c}, ++ {WSA885X_CDC_CLSH_CLSH_SIG_DP_CTL0, 0x0d}, ++ {WSA885X_CDC_CLSH_CLSH_V_HD_PA, 0x03}, ++ {WSA885X_CDC_CLSH_V1P8_BP_CTL2, 0x05}, ++}; ++ ++static void wsa885x_multi_update_bits(struct regmap *regmap, ++ const struct wsa885x_reg_update *updates, ++ size_t num_updates) ++{ ++ size_t i; ++ ++ for (i = 0; i < num_updates; i++) ++ regmap_update_bits(regmap, updates[i].reg, ++ updates[i].mask, updates[i].val); ++} ++ ++static void wsa885x_toggle_irq_bit(struct wsa885x_priv *wsa885x, ++ unsigned int reg, unsigned int mask) ++{ ++ regmap_update_bits(wsa885x->regmap, reg, mask, 0); ++ regmap_update_bits(wsa885x->regmap, reg, mask, mask); ++} ++ ++static void wsa885x_pulse_irq_bit(struct wsa885x_priv *wsa885x, ++ unsigned int reg, unsigned int mask) ++{ ++ regmap_update_bits(wsa885x->regmap, reg, mask, 0); ++ regmap_update_bits(wsa885x->regmap, reg, mask, mask); ++ regmap_update_bits(wsa885x->regmap, reg, mask, 0); ++} ++ ++static int wsa885x_tdm_ctl0_slot_num_val(int slots, unsigned int *slot_num_val) ++{ ++ if (!slot_num_val) ++ return -EINVAL; ++ ++ switch (slots) { ++ case 2: ++ *slot_num_val = WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_2; ++ return 0; ++ case 4: ++ *slot_num_val = WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_4; ++ return 0; ++ case 8: ++ *slot_num_val = WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_8; ++ return 0; ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int wsa885x_reg_update_sequence(struct regmap *regmap, int slots) ++{ ++ static const struct reg_sequence regs[] = { ++ { WSA885X_DIG_CTRL1_I2S_TDM_CTL1, 0x15 }, ++ { WSA885X_DIG_CTRL1_I2S_TDM_CTL1, 0x11 }, ++ }; ++ unsigned int slot_num_val; ++ int ret; ++ ++ if (!regmap) ++ return -EINVAL; ++ ++ ret = wsa885x_tdm_ctl0_slot_num_val(slots, &slot_num_val); ++ if (ret) ++ return ret; ++ ++ regmap_multi_reg_write(regmap, regs, ARRAY_SIZE(regs)); ++ ++ regmap_update_bits(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CTL0, ++ WSA885X_I2S_TDM_CTL0_NUM_CHANNELS_MASK, ++ slot_num_val); ++ regmap_update_bits(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CTL0, ++ WSA885X_I2S_TDM_CTL0_I2S_TDM_EN_MASK, ++ WSA885X_I2S_TDM_CTL0_I2S_TDM_EN_MASK); ++ regmap_write(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, ++ WSA885X_I2S_TDM_CH_TX_CH0_EN); ++ regmap_update_bits(regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, ++ WSA885X_I2S_TDM_CH_TX_CH1_EN, ++ WSA885X_I2S_TDM_CH_TX_CH1_EN); ++ ++ return 0; ++} ++ ++static int wsa885x_wait_for_pll_lock(struct wsa885x_priv *wsa885x) ++{ ++ unsigned int status = 0; ++ int cnt = 0, ret = 0; ++ ++ do { ++ usleep_range(1000, 1100); ++ ret = regmap_read(wsa885x->regmap, WSA885X_ANA_TOP_PLL_STATUS_0, &status); ++ if (ret) { ++ dev_err(wsa885x->dev, "PLL status read failed: %d\n", ret); ++ return ret; ++ } ++ ++ if (status & WSA885X_PLL_LOCK_BIT) ++ return 0; ++ } while (++cnt < 20); ++ ++ dev_warn(wsa885x->dev, "PLL lock timeout after 20ms, status=0x%x\n", status); ++ return -ETIMEDOUT; ++} ++ ++static int wsa885x_2s_conf(struct wsa885x_priv *wsa885x) ++{ ++ static const struct reg_sequence regs[] = { ++ { WSA885X_SPK_TOP_COMMON_TUNE1, 0x26 }, ++ { WSA885X_SPK_TOP_LF_CH1_CTRL11, 0x0d }, ++ { WSA885X_SPK_TOP_LF_CH2_CTRL11, 0x0d }, ++ { WSA885X_CDC_CLSH_V1P8_BP_CTL1, 0x71 }, ++ { WSA885X_CDC_CLSH_V1P8_BP_CTL0, 0xAA }, ++ }; ++ ++ return regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs)); ++} ++ ++static const struct reg_sequence wsa885x_reg_init[] = { ++ { WSA885X_CDC_RX0_RX_PATH_CTL, 0x24 }, ++ { WSA885X_CDC_RX1_RX_PATH_CTL, 0x24 }, ++ { WSA885X_RX0_RX_PATH_DSMDEM_CTL, 0x01 }, ++ { WSA885X_RX1_RX_PATH_DSMDEM_CTL, 0x01 }, ++ { WSA885X_CDC_COMPANDER0_CTL0, 0x01 }, ++ { WSA885X_CDC_COMPANDER1_CTL0, 0x01 }, ++ { WSA885X_CDC_VSENSE0_SPKR_PROT_PATH_CTL, 0x14 }, ++ { WSA885X_CDC_VSENSE1_SPKR_PROT_PATH_CTL, 0x14 }, ++ { WSA885X_CDC_ISENSE0_SPKR_PROT_PATH_CTL, 0x14 }, ++ { WSA885X_CDC_ISENSE1_SPKR_PROT_PATH_CTL, 0x14 }, ++ { WSA885X_DIG_CTRL0_CDC_CLK_CTL, 0x0f }, ++ { WSA885X_DIG_CTRL0_CDC_CLK_CTL, 0x4f }, ++ { WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x02 }, ++ { WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x00 }, ++ { WSA885X_DIG_CTRL0_CDC_RXTX_FSCNT_CTL, 0x01 }, ++ { WSA885X_SMP_AMP_CTRL_STEREO_CMT_GRP_MASK, 0x01 }, ++ { WSA885X_CDC_RX0_RX_PATH_CFG1, 0x60 }, ++ { WSA885X_CDC_RX1_RX_PATH_CFG1, 0x60 }, ++ { WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_CTRL3, 0xa5 }, ++ { WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_CTRL3, 0xa5 }, ++ { WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL2, 0x85 }, ++ { WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3, 0x0c }, ++ { WSA885X_ANA_TOP_IVSENSE_ADC_MODE_CTL3, 0x0e }, ++ { WSA885X_ANA_TOP_IVSENSE_ADC_REF_CTL, 0x0c }, ++ { WSA885X_DIG_CTRL0_GAIN_RAMP0_CTL1, 0x01 }, ++ { WSA885X_DIG_CTRL0_GAIN_RAMP1_CTL1, 0x01 }, ++ { WSA885X_CDC_RX0_RX_PATH_CFG0, 0x88 }, ++ { WSA885X_CDC_RX0_RX_PATH_CFG0, 0x89 }, ++ { WSA885X_CDC_RX1_RX_PATH_CFG0, 0x88 }, ++ { WSA885X_CDC_RX1_RX_PATH_CFG0, 0x89 }, ++ { WSA885X_ANA_TOP_BOOST_STB_CTRL2, 0x82 }, ++ { WSA885X_ANA_TOP_BOOST_STB_CTRL3, 0x34 }, ++ { WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL2, 0x41 }, ++ { WSA885X_ANA_TOP_BOOST_PWRSTAGE_CTRL4, 0x7f }, ++ { WSA885X_CDC_CLSH_V1P8_BP_CTL1, 0x50 }, ++ { WSA885X_CDC_CLSH_V1P8_BP_CTL0, 0x6c }, ++ { WSA885X_CDC_CLSH_CLSH_SIG_DP_CTL0, 0x0d }, ++ { WSA885X_CDC_CLSH_CLSH_V_HD_PA, 0x03 }, ++ { WSA885X_DIG_CTRL0_POWER_FSM_CTL0, 0x05 }, ++ { WSA885X_ANA_TOP_PON_CKSK_CTL_0, 0x20 }, ++ { WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH1_TUNE3, 0x45 }, ++ { WSA885X_ANA_TOP_SPK_TOP_PWRSTG_CH2_TUNE3, 0x45 }, ++ { WSA885X_CDC_CLSH_V1P8_BP_CTL2, 0x05 }, ++ { WSA885X_ANA_TOP_BG_TVP_UVLO1_PROG, 0x35 }, ++ { WSA885X_ANA_TOP_BG_TVP_UVLO2_PROG, 0x21 }, ++ { WSA885X_ANA_TOP_BOOST_BYP_CTRL2, 0xc7 }, ++ { WSA885X_ANA_TOP_BOOST_BYP_CTRL3, 0x11 }, ++ { WSA885X_ANA_TOP_IVSENSE_ADC_CDAC_CAL_CTL2, 0x80 }, ++ { WSA885X_ANA_TOP_SPK_TOP_SPARE3, 0x08 }, ++ { WSA885X_DIG_CTRL0_PA0_FSM_CTL1, 0x47 }, ++ { WSA885X_DIG_CTRL0_PA1_FSM_CTL1, 0x47 }, ++ { WSA885X_CDC_COMPANDER0_CTL7, 0x34 }, ++ { WSA885X_CDC_COMPANDER1_CTL7, 0x34 }, ++ { WSA885X_DIG_CTRL0_VBAT_THRM_FLT_CTL, 0x79 }, ++}; ++ ++static int wsa885x_hw_init(struct wsa885x_priv *wsa885x) ++{ ++ static const struct reg_sequence regs[] = { ++ { WSA885X_DIG_CTRL1_SPMI_PAD_GPIO2_CTL, 0x2e }, ++ { WSA885X_DIG_CTRL1_INTR_MODE, 0x01 }, ++ { WSA885X_DIG_CTRL1_PIN_CT, 0x04 }, ++ }; ++ int ret; ++ ++ ret = regmap_multi_reg_write(wsa885x->regmap, wsa885x_reg_init, ++ ARRAY_SIZE(wsa885x_reg_init)); ++ if (ret) ++ return ret; ++ ++ if (wsa885x->batt_conf == WSA885X_BATT_2S) { ++ ret = wsa885x_2s_conf(wsa885x); ++ if (ret) ++ return ret; ++ } ++ ++ return regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs)); ++} ++ ++static int wsa885x_unmask_interrupts(struct wsa885x_priv *wsa885x) ++{ ++ static const struct reg_sequence regs[] = { ++ { WSA885X_INTR_MASK0, 0x00 }, ++ { WSA885X_INTR_MASK0 + 1, 0x00 }, ++ { WSA885X_INTR_MASK0 + 2, 0xf8 }, ++ }; ++ ++ return regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs)); ++} ++ ++static int wsa885x_wait_for_pde_state(struct wsa885x_priv *wsa885x, int ps) ++{ ++ unsigned int act_ps = 0, clock_valid = 0; ++ int rc = 0, cnt = 0; ++ ++ if (ps < 0 || ps > 3) ++ return -EINVAL; ++ ++ do { ++ usleep_range(1000, 1500); ++ rc = regmap_read(wsa885x->regmap, ++ WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS, ++ &act_ps); ++ if (rc) { ++ dev_err(wsa885x->dev, "PDE state read failed: %d\n", rc); ++ return rc; ++ } ++ if (act_ps == ps) ++ return 0; ++ } while (++cnt < 5); ++ ++ if (regmap_read(wsa885x->regmap, ++ WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID, ++ &clock_valid)) ++ dev_err(wsa885x->dev, ++ "PDE power state %d request failed, actual_ps %d, clock_valid read failed\n", ++ ps, act_ps); ++ else ++ dev_err(wsa885x->dev, ++ "PDE power state %d request failed, actual_ps %d, clock_valid:%d\n", ++ ps, act_ps, clock_valid); ++ ++ return -ETIMEDOUT; ++} ++ ++static void wsa885x_program_stereo_volume(struct wsa885x_priv *wsa885x, ++ int stereo_vol_db, bool commit) ++{ ++ regmap_write(wsa885x->regmap, ++ WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_MSB, ++ (u8)(s8)stereo_vol_db); ++ regmap_write(wsa885x->regmap, ++ WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB, 0x00); ++ regmap_write(wsa885x->regmap, ++ WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_MSB, ++ (u8)(s8)stereo_vol_db); ++ regmap_write(wsa885x->regmap, ++ WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB, 0x00); ++ ++ if (commit) ++ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01); ++} ++ ++static int wsa885x_codec_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ struct wsa885x_priv *wsa885x; ++ u8 pcm_rate, cs21_sample_rate_idx, cs24_sample_rate_idx; ++ ++ wsa885x = snd_soc_component_get_drvdata(dai->component); ++ ++ switch (params_rate(params)) { ++ case 8000: ++ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_8KHZ; ++ cs21_sample_rate_idx = WSA885X_RX_RATE_8000HZ; ++ cs24_sample_rate_idx = WSA885X_VI_RATE_8000HZ; ++ break; ++ case 16000: ++ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_16KHZ; ++ cs21_sample_rate_idx = WSA885X_RX_RATE_16000HZ; ++ cs24_sample_rate_idx = WSA885X_VI_RATE_16000HZ; ++ break; ++ case 32000: ++ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_32KHZ; ++ cs21_sample_rate_idx = WSA885X_RX_RATE_32000HZ; ++ cs24_sample_rate_idx = WSA885X_VI_RATE_48000HZ; ++ break; ++ case 44100: ++ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_48_OR_44KHZ; ++ cs21_sample_rate_idx = WSA885X_RX_RATE_44100HZ; ++ cs24_sample_rate_idx = WSA885X_VI_RATE_44100HZ; ++ break; ++ case 48000: ++ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_48_OR_44KHZ; ++ cs21_sample_rate_idx = WSA885X_RX_RATE_48000HZ; ++ cs24_sample_rate_idx = WSA885X_VI_RATE_48000HZ; ++ break; ++ case 88200: ++ case 96000: ++ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_96_OR_88KHZ; ++ cs21_sample_rate_idx = WSA885X_RX_RATE_96000HZ; ++ cs24_sample_rate_idx = WSA885X_VI_RATE_96000HZ; ++ break; ++ case 176400: ++ case 192000: ++ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_192_OR_176KHZ; ++ cs21_sample_rate_idx = WSA885X_RX_RATE_192000HZ; ++ cs24_sample_rate_idx = WSA885X_VI_RATE_192000HZ; ++ break; ++ case 352800: ++ case 384000: ++ pcm_rate = WSA885X_I2S_CTL0_PCM_RATE_384_OR_352KHZ; ++ cs21_sample_rate_idx = WSA885X_RX_RATE_384000HZ; ++ cs24_sample_rate_idx = WSA885X_VI_RATE_384000HZ; ++ break; ++ default: ++ dev_err(wsa885x->dev, "sampling rate %d is not supported\n", params_rate(params)); ++ return -EINVAL; ++ } ++ ++ regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_CTL0, ++ WSA885X_I2S_CTL0_PCM_RATE_MASK | ++ WSA885X_I2S_CTL0_ENABLE_MASK, ++ WSA885X_I2S_CTL0_PCM_RATE(pcm_rate) | ++ WSA885X_I2S_CTL0_ENABLE_MASK); ++ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_RESET_CTL, 0x00); ++ regmap_write(wsa885x->regmap, WSA885X_SMP_AMP_CTRL_STEREO_CS21_SAMPLERATEINDEX, ++ cs21_sample_rate_idx); ++ regmap_write(wsa885x->regmap, WSA885X_SMP_AMP_CTRL_STEREO_CS24_SAMPLERATEINDEX, ++ cs24_sample_rate_idx); ++ ++ mutex_lock(&wsa885x->state_lock); ++ wsa885x_program_stereo_volume(wsa885x, wsa885x->stereo_vol_db, false); ++ mutex_unlock(&wsa885x->state_lock); ++ ++ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01); ++ ++ return 0; ++} ++ ++static int wsa885x_codec_set_tdm_slot(struct snd_soc_dai *dai, ++ unsigned int tx_slot_mask, ++ unsigned int rx_slot_mask, int slots, ++ int slot_width) ++{ ++ static const struct wsa885x_reg_update stereo_updates[] = { ++ { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK, ++ WSA885X_I2S_CFG0_TDM_TX_SLOT0(WSA885X_I2S_TX_SLOT_ISENSE0) }, ++ { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK, ++ WSA885X_I2S_CFG0_TDM_TX_SLOT1(WSA885X_I2S_TX_SLOT_ISENSE1) }, ++ { WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX, WSA885X_I2S_CFG1_TDM_TX_SLOT2_MASK, ++ WSA885X_I2S_CFG1_TDM_TX_SLOT2(WSA885X_I2S_TX_SLOT_CUR_SENSE0) }, ++ { WSA885X_DIG_CTRL1_I2S_CFG1_TDM_TX, WSA885X_I2S_CFG1_TDM_TX_SLOT3_MASK, ++ WSA885X_I2S_CFG1_TDM_TX_SLOT3(WSA885X_I2S_TX_SLOT_CUR_SENSE1) }, ++ }; ++ static const struct wsa885x_reg_update mono_left_updates[] = { ++ { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK, ++ WSA885X_I2S_CFG0_TDM_TX_SLOT0(WSA885X_I2S_TX_SLOT_ISENSE0) }, ++ { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK, ++ WSA885X_I2S_CFG0_TDM_TX_SLOT1(WSA885X_I2S_TX_SLOT_CUR_SENSE0) }, ++ }; ++ static const struct wsa885x_reg_update mono_right_updates[] = { ++ { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT0_MASK, ++ WSA885X_I2S_CFG0_TDM_TX_SLOT0(WSA885X_I2S_TX_SLOT_ISENSE1) }, ++ { WSA885X_DIG_CTRL1_I2S_CFG0_TDM_TX, WSA885X_I2S_CFG0_TDM_TX_SLOT1_MASK, ++ WSA885X_I2S_CFG0_TDM_TX_SLOT1(WSA885X_I2S_TX_SLOT_CUR_SENSE1) }, ++ }; ++ struct wsa885x_priv *wsa885x; ++ unsigned int slot_num_val; ++ u32 mask; ++ int ret; ++ ++ wsa885x = snd_soc_component_get_drvdata(dai->component); ++ ++ ret = wsa885x_tdm_ctl0_slot_num_val(slots, &slot_num_val); ++ if (ret) { ++ dev_err(wsa885x->dev, "%s: unsupported slot count %d\n", ++ __func__, slots); ++ return ret; ++ } ++ ++ if (rx_slot_mask && !wsa885x_is_valid_rx_slot_mask(rx_slot_mask)) { ++ dev_err(wsa885x->dev, ++ "%s: unsupported rx_slot_mask 0x%x\n", ++ __func__, rx_slot_mask); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&wsa885x->state_lock); ++ if (rx_slot_mask) ++ wsa885x->rx_slot_mask = rx_slot_mask; ++ else if (!wsa885x_is_valid_rx_slot_mask(wsa885x->rx_slot_mask)) ++ wsa885x->rx_slot_mask = WSA885X_CHANNEL_STEREO; ++ mask = wsa885x->rx_slot_mask; ++ ++ regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_RESET_CTL, ++ WSA885X_I2S_RESET_CTL_RESET_MASK, ++ WSA885X_I2S_RESET_CTL_RESET_MASK); ++ ++ if (mask == WSA885X_CHANNEL_STEREO) { ++ wsa885x_multi_update_bits(wsa885x->regmap, stereo_updates, ++ ARRAY_SIZE(stereo_updates)); ++ ret = wsa885x_reg_update_sequence(wsa885x->regmap, slots); ++ if (ret) ++ goto exit_unlock; ++ regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, ++ WSA885X_I2S_TDM_CH_TX_CH2_EN, ++ WSA885X_I2S_TDM_CH_TX_CH2_EN); ++ regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_TX, ++ WSA885X_I2S_TDM_CH_TX_CH3_EN, ++ WSA885X_I2S_TDM_CH_TX_CH3_EN); ++ } else if (mask == WSA885X_CHANNEL_MONO_LEFT) { ++ wsa885x_multi_update_bits(wsa885x->regmap, mono_left_updates, ++ ARRAY_SIZE(mono_left_updates)); ++ ret = wsa885x_reg_update_sequence(wsa885x->regmap, slots); ++ if (ret) ++ goto exit_unlock; ++ } else if (mask == WSA885X_CHANNEL_MONO_RIGHT) { ++ wsa885x_multi_update_bits(wsa885x->regmap, mono_right_updates, ++ ARRAY_SIZE(mono_right_updates)); ++ ret = wsa885x_reg_update_sequence(wsa885x->regmap, slots); ++ if (ret) ++ goto exit_unlock; ++ } ++ ++ regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_CTL0, ++ WSA885X_I2S_CTL0_ENABLE_MASK, ++ WSA885X_I2S_CTL0_ENABLE_MASK); ++ regmap_update_bits(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_RESET_CTL, ++ WSA885X_I2S_RESET_CTL_RESET_MASK, 0); ++ ++ ret = 0; ++ ++exit_unlock: ++ mutex_unlock(&wsa885x->state_lock); ++ ++ return ret; ++} ++ ++static int wsa885x_codec_set_sysclk(struct snd_soc_dai *dai, int clk_id, ++ unsigned int freq, int dir) ++{ ++ static const struct reg_sequence pll_prep[] = { ++ { WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x03 }, ++ { WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x04 }, ++ { WSA885X_ANA_TOP_PLL_LOOPFILT_0, 0xB4 }, ++ { WSA885X_ANA_TOP_PLL_VCO_CTL, 0x00 }, ++ { WSA885X_ANA_TOP_PLL_OVRD_CTL, 0x00 }, ++ }; ++ static const struct reg_sequence pll_cleanup[] = { ++ { WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE, 0x00 }, ++ { WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x00 }, ++ { WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x00 }, ++ }; ++ struct wsa885x_priv *wsa885x; ++ u32 pll_div; ++ int ret = 0; ++ ++ wsa885x = snd_soc_component_get_drvdata(dai->component); ++ ++ if (!freq) ++ return -EINVAL; ++ if (WSA885X_CLK_RATE_FIXED % freq) ++ return -EINVAL; ++ pll_div = WSA885X_CLK_RATE_FIXED / freq; ++ if (pll_div > 0xff) ++ return -EINVAL; ++ ++ regmap_multi_reg_write(wsa885x->regmap, pll_prep, ARRAY_SIZE(pll_prep)); ++ regmap_write(wsa885x->regmap, WSA885X_ANA_PLL_DIV_CTL_0, pll_div); ++ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_CLK_SOURCE_ENABLE, 0x02); ++ ++ ret = wsa885x_wait_for_pll_lock(wsa885x); ++ if (ret) { ++ dev_err(wsa885x->dev, "PLL lock failed, aborting sysclk configuration\n"); ++ regmap_multi_reg_write(wsa885x->regmap, pll_cleanup, ++ ARRAY_SIZE(pll_cleanup)); ++ return ret; ++ } ++ ++ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_SYS_CLK_SEL, 0x00); ++ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_POWER_FSM_CTL1, 0x01); ++ regmap_write(wsa885x->regmap, WSA885X_ANA_TOP_BG_TVP_OVRD_CTL, 0x00); ++ ++ return 0; ++} ++ ++static int wsa885x_codec_mute_stream(struct snd_soc_dai *dai, int mute, int stream) ++{ ++ static const struct reg_sequence mute_regs[] = { ++ { WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00 }, ++ { WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS, 0x03 }, ++ }; ++ static const struct reg_sequence mute_commit_regs[] = { ++ { WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0, 0x01 }, ++ { WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1, 0x01 }, ++ { WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01 }, ++ }; ++ static const struct reg_sequence unmute_prep_head_regs[] = { ++ { WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00 }, ++ }; ++ static const struct reg_sequence unmute_prep_tail_regs[] = { ++ { WSA885X_SMP_AMP_CTRL_STEREO_IT21_CLUSERINDEX, 0x01 }, ++ { WSA885X_SMP_AMP_CTRL_STEREO_PPU21_POSTURENUMBER, 0x01 }, ++ }; ++ static const struct reg_sequence unmute_volume_regs[] = { ++ { WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X0_LSB, 0x00 }, ++ { WSA885X_SMP_AMP_CTRL_STEREO_FU21_CH_VOL_CH2X1_LSB, 0x00 }, ++ }; ++ static const struct reg_sequence unmute_commit_regs[] = { ++ { WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01 }, ++ { WSA885X_SMP_AMP_CTRL_STEREO_PDE23_REQ_PS, 0x00 }, ++ }; ++ static const struct reg_sequence unmute_finish_regs[] = { ++ { WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X0, 0x00 }, ++ { WSA885X_SMP_AMP_CTRL_STEREO_FU21_MUTE_CH2X1, 0x00 }, ++ { WSA885X_DIG_CTRL0_SDCA_COMMIT, 0x01 }, ++ }; ++ struct wsa885x_priv *wsa885x; ++ int ret = 0, ps0 = 0, ps3 = 3; ++ ++ wsa885x = snd_soc_component_get_drvdata(dai->component); ++ ++ if (stream != SNDRV_PCM_STREAM_PLAYBACK) ++ return 0; ++ ++ mutex_lock(&wsa885x->state_lock); ++ ++ if (wsa885x->usage_mode > WSA885X_USAGE_MODE_MAX) { ++ ret = -EINVAL; ++ goto exit_unlock; ++ } ++ ++ if (!wsa885x_is_valid_rx_slot_mask(wsa885x->rx_slot_mask)) ++ wsa885x->rx_slot_mask = WSA885X_CHANNEL_STEREO; ++ ++ if (mute) { ++ regmap_multi_reg_write(wsa885x->regmap, mute_regs, ++ ARRAY_SIZE(mute_regs)); ++ ret = wsa885x_wait_for_pde_state(wsa885x, ps3); ++ if (ret) { ++ dev_err(wsa885x->dev, ++ "PS3 transition failed: %d\n", ret); ++ } else { ++ regmap_multi_reg_write(wsa885x->regmap, mute_commit_regs, ++ ARRAY_SIZE(mute_commit_regs)); ++ } ++ } else { ++ regmap_multi_reg_write(wsa885x->regmap, unmute_prep_head_regs, ++ ARRAY_SIZE(unmute_prep_head_regs)); ++ regmap_write(wsa885x->regmap, WSA885X_SMP_AMP_CTRL_STEREO_OT23_USAGE, ++ wsa885x->usage_mode); ++ regmap_multi_reg_write(wsa885x->regmap, unmute_prep_tail_regs, ++ ARRAY_SIZE(unmute_prep_tail_regs)); ++ wsa885x_program_stereo_volume(wsa885x, wsa885x->stereo_vol_db, false); ++ regmap_multi_reg_write(wsa885x->regmap, unmute_volume_regs, ++ ARRAY_SIZE(unmute_volume_regs)); ++ regmap_multi_reg_write(wsa885x->regmap, unmute_commit_regs, ++ ARRAY_SIZE(unmute_commit_regs)); ++ ret = wsa885x_wait_for_pde_state(wsa885x, ps0); ++ if (ret) ++ goto exit_unlock; ++ ++ if (wsa885x->rx_slot_mask == WSA885X_CHANNEL_STEREO) { ++ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_RX, ++ WSA885X_I2S_TDM_CH_RX_CH0_EN | ++ WSA885X_I2S_TDM_CH_RX_CH3_EN); ++ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x03); ++ } else if (wsa885x->rx_slot_mask == WSA885X_CHANNEL_MONO_LEFT) { ++ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_RX, ++ WSA885X_I2S_TDM_CH_RX_CH3_EN); ++ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x01); ++ } else if (wsa885x->rx_slot_mask == WSA885X_CHANNEL_MONO_RIGHT) { ++ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL1_I2S_TDM_CH_RX, ++ WSA885X_I2S_TDM_CH_RX_CH0_EN); ++ regmap_write(wsa885x->regmap, WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x02); ++ } ++ ++ regmap_multi_reg_write(wsa885x->regmap, unmute_finish_regs, ++ ARRAY_SIZE(unmute_finish_regs)); ++ } ++ ++exit_unlock: ++ mutex_unlock(&wsa885x->state_lock); ++ ++ return ret; ++} ++ ++static int wsa885x_codec_hw_free(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ static const struct reg_sequence regs[] = { ++ { WSA885X_DIG_CTRL0_PA_FSM_CTL, 0x00 }, ++ }; ++ struct wsa885x_priv *wsa885x; ++ ++ wsa885x = snd_soc_component_get_drvdata(dai->component); ++ ++ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) ++ return 0; ++ ++ mutex_lock(&wsa885x->state_lock); ++ regmap_multi_reg_write(wsa885x->regmap, regs, ARRAY_SIZE(regs)); ++ mutex_unlock(&wsa885x->state_lock); ++ ++ return 0; ++} ++ ++static const struct snd_soc_dai_ops wsa885x_dai_ops = { ++ .hw_params = wsa885x_codec_hw_params, ++ .set_tdm_slot = wsa885x_codec_set_tdm_slot, ++ .set_sysclk = wsa885x_codec_set_sysclk, ++ .mute_stream = wsa885x_codec_mute_stream, ++ .hw_free = wsa885x_codec_hw_free, ++}; ++ ++static struct snd_soc_dai_driver wsa885x_dai[] = { ++ { ++ .name = "wsa885x_dai_drv", ++ .playback = { ++ .stream_name = "WSA885X TDM Playback", ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = WSA885X_RATES, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | ++ SNDRV_PCM_FMTBIT_S32_LE, ++ }, ++ .ops = &wsa885x_dai_ops, ++ }, ++}; ++ ++static void wsa885x_reset_assert(void *data) ++{ ++ struct wsa885x_priv *wsa885x = data; ++ ++ if (wsa885x->sd_reset) ++ reset_control_assert(wsa885x->sd_reset); ++ else ++ gpiod_direction_output(wsa885x->sd_n, 1); ++} ++ ++static void wsa885x_reset_deassert(struct wsa885x_priv *wsa885x) ++{ ++ if (wsa885x->sd_reset) ++ reset_control_deassert(wsa885x->sd_reset); ++ else ++ gpiod_direction_output(wsa885x->sd_n, 0); ++} ++ ++static int wsa885x_get_reset(struct device *dev, struct wsa885x_priv *wsa885x) ++{ ++ wsa885x->sd_reset = devm_reset_control_get_optional_shared(dev, NULL); ++ if (IS_ERR(wsa885x->sd_reset)) ++ return dev_err_probe(dev, PTR_ERR(wsa885x->sd_reset), ++ "Failed to get reset\n"); ++ else if (wsa885x->sd_reset) ++ return 0; ++ ++ wsa885x->sd_n = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); ++ if (IS_ERR(wsa885x->sd_n)) ++ return dev_err_probe(dev, PTR_ERR(wsa885x->sd_n), ++ "Shutdown Control GPIO not found\n"); ++ ++ return 0; ++} ++ ++static bool wsa885x_volatile_register(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case WSA885X_ANA_TOP_PLL_STATUS_0: ++ case WSA885X_ANA_TOP_PLL_STATUS_1: ++ case WSA885X_DIG_CTRL0_SDCA_COMMIT: ++ case WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS: ++ case WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID: ++ case WSA885X_INTR_STATUS0: ++ case WSA885X_INTR_STATUS0 + 1: ++ case WSA885X_INTR_STATUS0 + 2: ++ case WSA885X_INTR_CLEAR0: ++ case WSA885X_INTR_CLEAR0 + 1: ++ case WSA885X_INTR_CLEAR0 + 2: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static bool wsa885x_readable_register(struct device *dev, unsigned int reg) ++{ ++ if (reg == WSA885X_INTR_CLEAR0 || ++ reg == WSA885X_INTR_CLEAR0 + 1 || ++ reg == WSA885X_INTR_CLEAR0 + 2) ++ return false; ++ return reg <= 0x88ff; ++} ++ ++static bool wsa885x_writeable_register(struct device *dev, unsigned int reg) ++{ ++ if (reg > 0x88ff) ++ return false; ++ ++ switch (reg) { ++ case WSA885X_ANA_TOP_PLL_STATUS_0: ++ case WSA885X_ANA_TOP_PLL_STATUS_1: ++ case WSA885X_INTR_STATUS0: ++ case WSA885X_INTR_STATUS0 + 1: ++ case WSA885X_INTR_STATUS0 + 2: ++ case WSA885X_SMP_AMP_CTRL_STEREO_PDE23_ACT_PS: ++ case WSA885X_SMP_AMP_CTRL_STEREO_CS21_CLOCK_VALID: ++ return false; ++ default: ++ return true; ++ } ++} ++ ++static const struct regmap_config wsa885x_regmap_cfg = { ++ .reg_bits = 8, ++ .val_bits = 8, ++ .max_register = 0x88FF, ++ .ranges = wsa885x_regmap_ranges, ++ .num_ranges = ARRAY_SIZE(wsa885x_regmap_ranges), ++ .reg_defaults = wsa885x_codec_reg_defaults, ++ .num_reg_defaults = ARRAY_SIZE(wsa885x_codec_reg_defaults), ++ .volatile_reg = wsa885x_volatile_register, ++ .writeable_reg = wsa885x_writeable_register, ++ .readable_reg = wsa885x_readable_register, ++ .cache_type = REGCACHE_MAPLE, ++ .use_single_read = true, ++ .use_single_write = true, ++}; ++ ++static int wsa885x_component_probe(struct snd_soc_component *component) ++{ ++ struct wsa885x_priv *wsa885x = ++ snd_soc_component_get_drvdata(component); ++ int ret; ++ ++ wsa885x->component = component; ++ snd_soc_component_init_regmap(component, wsa885x->regmap); ++ ++ ret = wsa885x_hw_init(wsa885x); ++ if (ret) ++ return ret; ++ ++ return wsa885x_unmask_interrupts(wsa885x); ++} ++ ++static int wsa885x_stereo_gain_offset_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); ++ int val; ++ ++ mutex_lock(&wsa885x->state_lock); ++ val = wsa885x->stereo_vol_db + 84; ++ mutex_unlock(&wsa885x->state_lock); ++ if (val < 0 || val > WSA885X_FU21_VOL_STEPS) ++ return -ERANGE; ++ ++ ucontrol->value.integer.value[0] = val; ++ return 0; ++} ++ ++static int wsa885x_stereo_gain_offset_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); ++ long val; ++ int stereo_vol_db; ++ ++ val = ucontrol->value.integer.value[0]; ++ ++ if (val < 0 || val > WSA885X_FU21_VOL_STEPS) { ++ dev_err(component->dev, "%s: Invalid range, Val: %ld\n", __func__, val); ++ return -EINVAL; ++ } ++ ++ stereo_vol_db = (int)val - 84; ++ ++ mutex_lock(&wsa885x->state_lock); ++ if (wsa885x->stereo_vol_db == stereo_vol_db) { ++ mutex_unlock(&wsa885x->state_lock); ++ return 0; ++ } ++ ++ wsa885x_program_stereo_volume(wsa885x, stereo_vol_db, true); ++ wsa885x->stereo_vol_db = stereo_vol_db; ++ mutex_unlock(&wsa885x->state_lock); ++ ++ return 1; ++} ++ ++static int wsa885x_usage_modes_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); ++ ++ mutex_lock(&wsa885x->state_lock); ++ if (wsa885x->usage_mode > WSA885X_USAGE_MODE_MAX) { ++ mutex_unlock(&wsa885x->state_lock); ++ return -ERANGE; ++ } ++ ++ ucontrol->value.integer.value[0] = wsa885x->usage_mode; ++ mutex_unlock(&wsa885x->state_lock); ++ ++ return 0; ++} ++ ++static int wsa885x_usage_modes_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); ++ u32 val = ucontrol->value.integer.value[0]; ++ ++ if (val > WSA885X_USAGE_MODE_MAX) ++ return -EINVAL; ++ ++ mutex_lock(&wsa885x->state_lock); ++ if (wsa885x->usage_mode == val) { ++ mutex_unlock(&wsa885x->state_lock); ++ return 0; ++ } ++ ++ wsa885x->usage_mode = val; ++ mutex_unlock(&wsa885x->state_lock); ++ ++ return 1; ++} ++ ++static int wsa885x_rx_slot_mask_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); ++ u32 mask; ++ ++ mutex_lock(&wsa885x->state_lock); ++ mask = wsa885x->rx_slot_mask; ++ mutex_unlock(&wsa885x->state_lock); ++ if (!wsa885x_is_valid_rx_slot_mask(mask)) ++ return -ERANGE; ++ ++ ucontrol->value.integer.value[0] = mask; ++ ++ return 0; ++} ++ ++static int wsa885x_rx_slot_mask_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); ++ struct wsa885x_priv *wsa885x = snd_soc_component_get_drvdata(component); ++ u32 mask = ucontrol->value.integer.value[0]; ++ ++ if (!wsa885x_is_valid_rx_slot_mask(mask)) ++ return -EINVAL; ++ ++ mutex_lock(&wsa885x->state_lock); ++ if (wsa885x->rx_slot_mask == mask) { ++ mutex_unlock(&wsa885x->state_lock); ++ return 0; ++ } ++ ++ wsa885x->rx_slot_mask = mask; ++ mutex_unlock(&wsa885x->state_lock); ++ ++ return 1; ++} ++ ++static const struct snd_kcontrol_new wsa885x_snd_controls[] = { ++ SOC_SINGLE_EXT("Usage Mode", SND_SOC_NOPM, 0, WSA885X_USAGE_MODE_MAX, 0, ++ wsa885x_usage_modes_get, ++ wsa885x_usage_modes_put), ++ ++ SOC_SINGLE_EXT_TLV("Speaker Volume", SND_SOC_NOPM, ++ 0, WSA885X_FU21_VOL_STEPS, 0, ++ wsa885x_stereo_gain_offset_get, ++ wsa885x_stereo_gain_offset_put, ++ wsa885x_fu21_digital_gain), ++ ++ SOC_SINGLE_EXT("Rx Slot Mask", SND_SOC_NOPM, 0, 3, 0, ++ wsa885x_rx_slot_mask_get, ++ wsa885x_rx_slot_mask_put), ++}; ++ ++static const struct snd_soc_component_driver wsa885x_component = { ++ .name = "wsa885x", ++ .probe = wsa885x_component_probe, ++ .controls = wsa885x_snd_controls, ++ .num_controls = ARRAY_SIZE(wsa885x_snd_controls), ++}; ++ ++static irqreturn_t wsa885x_handle_irq(int irq_idx, void *data) ++{ ++ struct wsa885x_priv *wsa885x = data; ++ ++ if (irq_idx < 0 || irq_idx >= WSA885X_IRQ_MAX) ++ return IRQ_NONE; ++ ++ switch (irq_idx) { ++ case WSA885X_IRQ_INT_SAF2WAR: ++ case WSA885X_IRQ_INT_WAR2SAF: ++ case WSA885X_IRQ_INT_DISABLE: ++ case WSA885X_IRQ_INT_INTR_GPIO1_PIN: ++ case WSA885X_IRQ_INT_INTR_GPIO2_PIN: ++ case WSA885X_IRQ_INT_PA0_OCP: ++ case WSA885X_IRQ_INT_PA1_OCP: ++ case WSA885X_IRQ_INT_CLIP0: ++ case WSA885X_IRQ_INT_CLIP1: ++ case WSA885X_IRQ_INT_CLK_WD: ++ case WSA885X_IRQ_INT_BOP: ++ case WSA885X_IRQ_INT_UVLO: ++ case WSA885X_IRQ_INT_PCM_DATA0_DC: ++ case WSA885X_IRQ_INT_PCM_DATA1_DC: ++ case WSA885X_IRQ_INT_PLL_UNLOCKED: ++ case WSA885X_IRQ_INT_PROT_MODE_CHANGE: ++ case WSA885X_IRQ_INT_PB_CLOCK_VALID: ++ case WSA885X_IRQ_INT_SENSE_CLOCK_VALID: ++ break; ++ case WSA885X_IRQ_INT_PCM_DATA0_WD: ++ case WSA885X_IRQ_INT_PCM_DATA1_WD: ++ if (irq_idx == WSA885X_IRQ_INT_PCM_DATA0_WD) ++ wsa885x_toggle_irq_bit(wsa885x, WSA885X_DIG_CTRL0_PCM_DATA_WD0_CTL1, ++ WSA885X_PCM_DATA_WD_CTL1_PCM_DATA_WD_EN_MASK); ++ else ++ wsa885x_toggle_irq_bit(wsa885x, WSA885X_DIG_CTRL0_PCM_DATA_WD1_CTL1, ++ WSA885X_PCM_DATA_WD_CTL1_PCM_DATA_WD_EN_MASK); ++ break; ++ case WSA885X_IRQ_INT_PA0_FSM_ERR: ++ case WSA885X_IRQ_INT_PA1_FSM_ERR: ++ case WSA885X_IRQ_INT_MAIN_FSM_ERR: ++ if (irq_idx == WSA885X_IRQ_INT_MAIN_FSM_ERR) { ++ wsa885x_pulse_irq_bit(wsa885x, WSA885X_DIG_CTRL0_POWER_FSM_CTL0, ++ WSA885X_POWER_FSM_CTL0_CLEAR_ERROR_MASK); ++ } else if (irq_idx == WSA885X_IRQ_INT_PA0_FSM_ERR) { ++ wsa885x_pulse_irq_bit(wsa885x, WSA885X_PA0_FSM_CTL0, ++ WSA885X_PA_FSM_CTL0_CLEAR_ERROR_MASK); ++ } else if (irq_idx == WSA885X_IRQ_INT_PA1_FSM_ERR) { ++ wsa885x_pulse_irq_bit(wsa885x, WSA885X_PA1_FSM_CTL0, ++ WSA885X_PA_FSM_CTL0_CLEAR_ERROR_MASK); ++ } ++ break; ++ default: ++ break; ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t wsa885x_interrupt_handler(int irq, void *data) ++{ ++ static const unsigned int status_reg[WSA885X_NUM_REGS] = { ++ WSA885X_INTR_STATUS0, ++ WSA885X_INTR_STATUS0 + 1, ++ WSA885X_INTR_STATUS0 + 2, ++ }; ++ static const unsigned int clear_reg[WSA885X_NUM_REGS] = { ++ WSA885X_INTR_CLEAR0, ++ WSA885X_INTR_CLEAR0 + 1, ++ WSA885X_INTR_CLEAR0 + 2, ++ }; ++ unsigned int status[WSA885X_NUM_REGS] = { 0 }; ++ struct wsa885x_priv *wsa885x = data; ++ irqreturn_t handled = IRQ_NONE; ++ irqreturn_t irq_ret; ++ int i, bit, ret, irq_num; ++ ++ for (i = 0; i < WSA885X_NUM_REGS; i++) { ++ ret = regmap_read(wsa885x->regmap, status_reg[i], &status[i]); ++ if (ret) { ++ dev_err(wsa885x->dev, ++ "Failed to read status_reg[%d] (0x%x): %d\n", ++ i, status_reg[i], ret); ++ status[i] = 0; ++ continue; ++ } ++ } ++ ++ for (i = 0; i < WSA885X_NUM_REGS; i++) { ++ for (bit = 0; bit < 8; bit++) { ++ if (status[i] & BIT(bit)) { ++ irq_num = i * 8 + bit; ++ regmap_write(wsa885x->regmap, clear_reg[i], BIT(bit)); ++ regmap_write(wsa885x->regmap, clear_reg[i], 0); ++ if (irq_num >= WSA885X_IRQ_MAX) { ++ dev_warn_ratelimited(wsa885x->dev, ++ "Unexpected IRQ bit %d (reg %d)\n", ++ bit, i); ++ handled = IRQ_HANDLED; ++ continue; ++ } ++ irq_ret = wsa885x_handle_irq(irq_num, wsa885x); ++ if (irq_ret == IRQ_HANDLED) ++ handled = IRQ_HANDLED; ++ } ++ } ++ } ++ return handled; ++} ++ ++static int wsa885x_register_irq(struct wsa885x_priv *wsa885x) ++{ ++ if (!wsa885x->client->irq) ++ return dev_err_probe(wsa885x->dev, -EINVAL, ++ "IRQ is not configured\n"); ++ ++ return devm_request_threaded_irq(wsa885x->dev, wsa885x->client->irq, NULL, ++ wsa885x_interrupt_handler, ++ IRQF_ONESHOT, ++ dev_name(wsa885x->dev), wsa885x); ++} ++ ++static int wsa885x_probe(struct i2c_client *client) ++{ ++ struct wsa885x_priv *wsa885x; ++ const struct snd_soc_component_driver *component_driver = &wsa885x_component; ++ const char *battery_config; ++ unsigned int i; ++ int ret; ++ struct device *dev = &client->dev; ++ ++ wsa885x = devm_kzalloc(dev, sizeof(*wsa885x), GFP_KERNEL); ++ if (!wsa885x) ++ return -ENOMEM; ++ ++ wsa885x->client = client; ++ wsa885x->dev = dev; ++ wsa885x->stereo_vol_db = -84; ++ wsa885x->rx_slot_mask = WSA885X_CHANNEL_STEREO; ++ mutex_init(&wsa885x->state_lock); ++ wsa885x->regmap = devm_regmap_init_i2c(client, &wsa885x_regmap_cfg); ++ ++ if (IS_ERR(wsa885x->regmap)) ++ return PTR_ERR(wsa885x->regmap); ++ ++ ret = device_property_read_string(dev, "qcom,battery-config", ++ &battery_config); ++ if (ret) { ++ wsa885x->batt_conf = WSA885X_BATT_1S; ++ } else if (!strcmp(battery_config, "1s")) { ++ wsa885x->batt_conf = WSA885X_BATT_1S; ++ } else if (!strcmp(battery_config, "2s")) { ++ wsa885x->batt_conf = WSA885X_BATT_2S; ++ } else { ++ return dev_err_probe(dev, -EINVAL, ++ "Invalid battery config %s (expected 1s or 2s)\n", ++ battery_config); ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(wsa885x_supply_name); i++) { ++ ret = devm_regulator_get_enable(dev, wsa885x_supply_name[i]); ++ if (ret) ++ return dev_err_probe(dev, ret, ++ "Failed to enable regulator %s\n", ++ wsa885x_supply_name[i]); ++ } ++ ++ ret = wsa885x_get_reset(dev, wsa885x); ++ if (ret) ++ return ret; ++ ++ wsa885x_reset_deassert(wsa885x); ++ usleep_range(5000, 5500); ++ ++ ret = devm_add_action_or_reset(dev, wsa885x_reset_assert, wsa885x); ++ if (ret) ++ return dev_err_probe(dev, ret, "devm_add_action_or_reset failed\n"); ++ ++ i2c_set_clientdata(client, wsa885x); ++ ++ ret = wsa885x_register_irq(wsa885x); ++ if (ret) ++ return dev_err_probe(dev, ret, "wsa885x irq registration failed\n"); ++ ++ ret = devm_snd_soc_register_component(dev, component_driver, ++ wsa885x_dai, ++ ARRAY_SIZE(wsa885x_dai)); ++ if (ret) ++ return dev_err_probe(dev, ret, "Codec component registration failed\n"); ++ ++ return 0; ++} ++ ++static const struct of_device_id wsa885x_dt_match[] = { ++ { ++ .compatible = "qcom,wsa8855", ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, wsa885x_dt_match); ++ ++static const struct i2c_device_id wsa885x_id[] = { ++ { ++ .name = "wsa885x", ++ .driver_data = 0, ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(i2c, wsa885x_id); ++ ++static struct i2c_driver wsa885x_driver = { ++ .driver = { ++ .name = "wsa885x", ++ .of_match_table = wsa885x_dt_match, ++ }, ++ .probe = wsa885x_probe, ++ .id_table = wsa885x_id, ++}; ++ ++module_i2c_driver(wsa885x_driver); ++ ++MODULE_DESCRIPTION("ASoC WSA885X Stereo Smart PA Codec Driver"); ++MODULE_LICENSE("GPL"); +diff --git a/sound/soc/codecs/zl38060.c b/sound/soc/codecs/zl38060.c +--- a/sound/soc/codecs/zl38060.c ++++ b/sound/soc/codecs/zl38060.c +@@ -162,7 +162,7 @@ static int zl38_fw_send_xaddr(struct regmap *regmap, const void *data) + static int zl38_load_firmware(struct device *dev, struct regmap *regmap) + { + const struct ihex_binrec *rec; +- const struct firmware *fw; ++ const struct firmware *fw __free(firmware) = NULL; + u32 addr; + u16 len; + int err; +@@ -180,7 +180,7 @@ static int zl38_load_firmware(struct device *dev, struct regmap *regmap) + return err; + err = zl38_fw_enter_boot_mode(regmap); + if (err) +- goto out; ++ return err; + rec = (const struct ihex_binrec *)fw->data; + while (rec) { + addr = be32_to_cpu(rec->addr); +@@ -195,15 +195,12 @@ static int zl38_load_firmware(struct device *dev, struct regmap *regmap) + err = -EINVAL; + } + if (err) +- goto out; ++ return err; + /* next ! */ + rec = ihex_next_binrec(rec); + } +- err = zl38_fw_go(regmap); + +-out: +- release_firmware(fw); +- return err; ++ return zl38_fw_go(regmap); + } + + +diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c +--- a/sound/soc/dwc/dwc-i2s.c ++++ b/sound/soc/dwc/dwc-i2s.c +@@ -955,7 +955,9 @@ static int dw_i2s_probe(struct platform_device *pdev) + } + + irq = platform_get_irq_optional(pdev, 0); +- if (irq >= 0) { ++ if (irq == -EPROBE_DEFER) ++ return irq; ++ if (irq > 0) { + ret = devm_request_irq(&pdev->dev, irq, i2s_irq_handler, 0, + pdev->name, dev); + if (ret < 0) { +diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c +--- a/sound/soc/fsl/fsl-asoc-card.c ++++ b/sound/soc/fsl/fsl-asoc-card.c +@@ -69,7 +69,6 @@ static const struct snd_pcm_hw_constraint_list cs42888_channel_constraints = { + + /** + * struct codec_priv - CODEC private data +- * @mclk: Main clock of the CODEC + * @mclk_freq: Clock rate of MCLK + * @free_freq: Clock rate of MCLK for hw_free() + * @mclk_id: MCLK (or main clock) id for set_sysclk() +@@ -80,7 +79,6 @@ static const struct snd_pcm_hw_constraint_list cs42888_channel_constraints = { + * to stay within PLL frequency limits + */ + struct codec_priv { +- struct clk *mclk; + unsigned long mclk_freq; + unsigned long free_freq; + u32 mclk_id; +@@ -109,12 +107,58 @@ struct cpu_priv { + u32 slot_num; + }; + ++struct fsl_asoc_card_priv; ++ ++/* ++ * struct fsl_asoc_card_pdata - per-compatible static card description ++ * @sysclk_dir: initial CPU SYSCLK direction override (0 = leave default IN) ++ * @sysclk_ratio: SYSCLK ratio on sample rate (0 = not used) ++ * @slot_width: TDM slot width (0 = not TDM) ++ * @codec_dai_name: name of the codec DAI ++ * @codec_mclk_id: MCLK id passed to set_sysclk() for the codec ++ * @codec_fll_id: FLL id; only valid when has_pll is true ++ * @codec_pll_id: PLL id; only valid when has_pll is true ++ * @codec_pll_ratio_s24: PLL output ratio for S24_LE ++ * @has_pll: codec uses PLL/FLL; codec_fll_id and codec_pll_id are valid ++ * @dai_fmt: DAI format flags ++ * @playback_only: restrict card to playback direction ++ * @capture_only: restrict card to capture direction ++ * @dapm_routes: DAPM route table override ++ * @num_dapm_routes: number of entries in dapm_routes ++ * @exclude_format: PCM format bitmask excluded (for SAI + WM8960/WM8962) ++ * @codec_init: codec-specific init run after mclk_freq is populated ++ * @probe_init: optional DT-driven init run at end of probe() (e.g. SPDIF codec discovery) ++ */ ++struct fsl_asoc_card_pdata { ++ u32 sysclk_dir[2]; ++ u32 sysclk_ratio[2]; ++ u32 slot_width; ++ const char *codec_dai_name; ++ u32 codec_mclk_id; ++ int codec_fll_id; ++ int codec_pll_id; ++ int codec_pll_ratio_s24; ++ bool has_pll; ++ bool playback_only; ++ bool capture_only; ++ u32 dai_fmt; ++ const struct snd_soc_dapm_route *dapm_routes; ++ int num_dapm_routes; ++ u64 exclude_format; ++ int (*codec_init)(struct fsl_asoc_card_priv *priv); ++ int (*probe_init)(struct device_node *codec_np[], ++ struct device_node *cpu_np, ++ const char *codec_dai_name[], ++ struct fsl_asoc_card_priv *priv); ++}; ++ + /** + * struct fsl_asoc_card_priv - Freescale Generic ASOC card private data + * @dai_link: DAI link structure including normal one and DPCM link + * @hp_jack: Headphone Jack structure + * @mic_jack: Microphone Jack structure + * @pdev: platform device pointer ++ * @pdata: pointer to the per-compatible card platform data + * @codec_priv: CODEC private data + * @cpu_priv: CPU private data + * @card: ASoC card structure +@@ -135,6 +179,7 @@ struct fsl_asoc_card_priv { + struct simple_util_jack hp_jack; + struct simple_util_jack mic_jack; + struct platform_device *pdev; ++ const struct fsl_asoc_card_pdata *pdata; + struct codec_priv codec_priv[2]; + struct cpu_priv cpu_priv; + struct snd_soc_card card; +@@ -609,6 +654,192 @@ static int fsl_asoc_card_spdif_init(struct device_node *codec_np[], + return 0; + } + ++static int fsl_asoc_card_cs42888_codec_init(struct fsl_asoc_card_priv *priv) ++{ ++ unsigned long mclk_freq = priv->codec_priv[0].mclk_freq; ++ ++ /* ++ * Set CPU sysclk frequency from codec MCLK only if not already ++ * set by the CPU DAI init (e.g. ESAI extal clock takes precedence). ++ */ ++ if (!priv->cpu_priv.sysclk_freq[TX]) ++ priv->cpu_priv.sysclk_freq[TX] = mclk_freq; ++ if (!priv->cpu_priv.sysclk_freq[RX]) ++ priv->cpu_priv.sysclk_freq[RX] = mclk_freq; ++ ++ priv->constraint_channels = &cs42888_channel_constraints; ++ if (mclk_freq % 12288000 == 0) ++ priv->constraint_rates = &cs42888_rate_48k_constraints; ++ else if (mclk_freq % 11289600 == 0) ++ priv->constraint_rates = &cs42888_rate_44k_constraints; ++ else ++ dev_warn(&priv->pdev->dev, ++ "Unknown MCLK frequency %lu, no rate constraints\n", ++ mclk_freq); ++ ++ return 0; ++} ++ ++static int fsl_asoc_card_wm8958_codec_init(struct fsl_asoc_card_priv *priv) ++{ ++ priv->codec_priv[0].free_freq = priv->codec_priv[0].mclk_freq; ++ return 0; ++} ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_cs42888_pdata = { ++ .codec_dai_name = "cs42888", ++ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBC_CFC, ++ .sysclk_dir = { SND_SOC_CLOCK_OUT, SND_SOC_CLOCK_OUT }, ++ .slot_width = 32, ++ .dapm_routes = audio_map, ++ .num_dapm_routes = ARRAY_SIZE(audio_map), ++ .codec_init = fsl_asoc_card_cs42888_codec_init, ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_cs427x_pdata = { ++ .codec_dai_name = "cs4271-hifi", ++ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, ++ .codec_mclk_id = CS427x_SYSCLK_MCLK, ++ .dapm_routes = audio_map, ++ .num_dapm_routes = ARRAY_SIZE(audio_map), ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_sgtl5000_pdata = { ++ .codec_dai_name = "sgtl5000", ++ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, ++ .codec_mclk_id = SGTL5000_SYSCLK, ++ .dapm_routes = audio_map, ++ .num_dapm_routes = ARRAY_SIZE(audio_map), ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_tlv320aic32x4_pdata = { ++ .codec_dai_name = "tlv320aic32x4-hifi", ++ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, ++ .dapm_routes = audio_map, ++ .num_dapm_routes = ARRAY_SIZE(audio_map), ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_tlv320aic31xx_pdata = { ++ .codec_dai_name = "tlv320dac31xx-hifi", ++ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBC_CFC, ++ .sysclk_dir = { SND_SOC_CLOCK_OUT, SND_SOC_CLOCK_OUT }, ++ .playback_only = true, ++ .dapm_routes = audio_map_tx, ++ .num_dapm_routes = ARRAY_SIZE(audio_map_tx), ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_wm8962_pdata = { ++ .codec_dai_name = "wm8962", ++ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, ++ .codec_mclk_id = WM8962_SYSCLK_MCLK, ++ .has_pll = true, ++ .codec_fll_id = WM8962_SYSCLK_FLL, ++ .codec_pll_id = WM8962_FLL, ++ /* ++ * WM8962 has same BCLK generation limitations as WM8960. ++ * See WM8960 section for detailed explanation. ++ */ ++ .exclude_format = SNDRV_PCM_FMTBIT_S20_3LE, ++ .dapm_routes = audio_map, ++ .num_dapm_routes = ARRAY_SIZE(audio_map), ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_wm8960_pdata = { ++ .codec_dai_name = "wm8960-hifi", ++ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, ++ .has_pll = true, ++ .codec_fll_id = WM8960_SYSCLK_AUTO, ++ .codec_pll_id = WM8960_SYSCLK_AUTO, ++ /* ++ * WM8960 in master mode cannot generate exact 1.92 MHz BCLK ++ * required for S20_3LE (48kHz x 2ch x 20bit). Closest available ++ * is 2.048 MHz (SYSCLK/6), which causes right channel corruption. ++ * ++ * In SAI master mode, SAI derive BCLK from MCLK using integer ++ * dividers only. S20_3LE requires non-integer divider ratios ++ * with standard MCLK frequencies. For example, 48kHz stereo ++ * needs 1.920 MHz BCLK, which requires a divider of 6.4 from ++ * 12.288 MHz MCLK (not an integer). ++ */ ++ .exclude_format = SNDRV_PCM_FMTBIT_S20_3LE, ++ .dapm_routes = audio_map, ++ .num_dapm_routes = ARRAY_SIZE(audio_map), ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_ac97_pdata = { ++ .codec_dai_name = "ac97-hifi", ++ .dai_fmt = SND_SOC_DAIFMT_AC97, ++ .dapm_routes = audio_map_ac97, ++ .num_dapm_routes = ARRAY_SIZE(audio_map_ac97), ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_mqs_pdata = { ++ .codec_dai_name = "fsl-mqs-dai", ++ .dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBC_CFC | ++ SND_SOC_DAIFMT_NB_NF, ++ .playback_only = true, ++ .dapm_routes = audio_map_tx, ++ .num_dapm_routes = ARRAY_SIZE(audio_map_tx), ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_wm8524_pdata = { ++ .codec_dai_name = "wm8524-hifi", ++ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBC_CFC, ++ /* RX=0, TX=1: set TX (index 1) to CLOCK_OUT, RX stays at default IN */ ++ .sysclk_dir = { 0, SND_SOC_CLOCK_OUT }, ++ .sysclk_ratio = { 0, 256 }, ++ .slot_width = 32, ++ .playback_only = true, ++ .dapm_routes = audio_map_tx, ++ .num_dapm_routes = ARRAY_SIZE(audio_map_tx), ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_si476x_pdata = { ++ .codec_dai_name = "si476x-codec", ++ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBC_CFC, ++ .dapm_routes = audio_map_rx, ++ .num_dapm_routes = ARRAY_SIZE(audio_map_rx), ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_wm8958_pdata = { ++ .codec_dai_name = "wm8994-aif1", ++ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, ++ .codec_mclk_id = WM8994_FLL_SRC_MCLK1, ++ .has_pll = true, ++ .codec_fll_id = WM8994_SYSCLK_FLL1, ++ .codec_pll_id = WM8994_FLL1, ++ .codec_init = fsl_asoc_card_wm8958_codec_init, ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_nau8822_pdata = { ++ .codec_dai_name = "nau8822-hifi", ++ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, ++ .codec_mclk_id = NAU8822_CLK_MCLK, ++ .has_pll = true, ++ .codec_fll_id = NAU8822_CLK_PLL, ++ .codec_pll_id = NAU8822_CLK_PLL, ++ .dapm_routes = audio_map, ++ .num_dapm_routes = ARRAY_SIZE(audio_map), ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_wm8904_pdata = { ++ .codec_dai_name = "wm8904-hifi", ++ .dai_fmt = DAI_FMT_BASE | SND_SOC_DAIFMT_CBP_CFP, ++ .codec_mclk_id = WM8904_FLL_MCLK, ++ .has_pll = true, ++ .codec_fll_id = WM8904_CLK_FLL, ++ .codec_pll_id = WM8904_FLL_MCLK, ++ .codec_pll_ratio_s24 = 192, ++ .dapm_routes = audio_map, ++ .num_dapm_routes = ARRAY_SIZE(audio_map), ++}; ++ ++static const struct fsl_asoc_card_pdata fsl_asoc_spdif_pdata = { ++ .codec_dai_name = "spdif", ++ .dai_fmt = DAI_FMT_BASE, ++ .probe_init = fsl_asoc_card_spdif_init, ++}; ++ + static int hp_jack_event(struct notifier_block *nb, unsigned long event, + void *data) + { +@@ -643,17 +874,173 @@ static struct notifier_block mic_jack_nb = { + .notifier_call = mic_jack_event, + }; + +-static int fsl_asoc_card_late_probe(struct snd_soc_card *card) ++/* ++ * fsl_asoc_card_init_cpu - configure CPU DAI-specific settings. ++ * ++ * Called from late_probe() when the CPU DAI component is guaranteed bound. ++ */ ++static int fsl_asoc_card_init_cpu(struct snd_soc_card *card, ++ struct snd_soc_pcm_runtime *rtd) + { + struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); +- struct snd_soc_pcm_runtime *rtd = list_first_entry( +- &card->rtd_list, struct snd_soc_pcm_runtime, list); ++ struct device_node *np = priv->pdev->dev.of_node; ++ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); ++ const char *comp_drv_name = cpu_dai->component->driver->name; ++ struct device *dev = card->dev; ++ int ret; ++ ++ if (!strcmp(comp_drv_name, "fsl-ssi")) { ++ /* Only SSI needs to configure AUDMUX */ ++ ret = fsl_asoc_card_audmux_init(np, priv); ++ if (ret) { ++ dev_err(dev, "failed to init audmux\n"); ++ return ret; ++ } ++ } else if (!strcmp(comp_drv_name, "fsl-esai")) { ++ struct clk *esai_clk = clk_get(cpu_dai->dev, "extal"); ++ ++ if (!IS_ERR(esai_clk)) { ++ priv->cpu_priv.sysclk_freq[TX] = clk_get_rate(esai_clk); ++ priv->cpu_priv.sysclk_freq[RX] = clk_get_rate(esai_clk); ++ clk_put(esai_clk); ++ } else { ++ dev_warn(dev, "failed to get ESAI extal clock: %ld\n", PTR_ERR(esai_clk)); ++ } ++ ++ priv->cpu_priv.sysclk_id[TX] = ESAI_HCKT_EXTAL; ++ priv->cpu_priv.sysclk_id[RX] = ESAI_HCKR_EXTAL; ++ } else if (!strcmp(comp_drv_name, "fsl-sai")) { ++ priv->cpu_priv.sysclk_id[TX] = FSL_SAI_CLK_MAST1; ++ priv->cpu_priv.sysclk_id[RX] = FSL_SAI_CLK_MAST1; ++ ++ if (priv->pdata->exclude_format) ++ priv->exclude_format = priv->pdata->exclude_format; ++ } ++ ++ return 0; ++} ++ ++/* ++ * fsl_asoc_card_init_codecs - read codec MCLK rates and set codec sysclk. ++ * ++ * Called from late_probe() after all components are bound. ++ */ ++static int fsl_asoc_card_init_codecs(struct snd_soc_card *card, ++ struct snd_soc_pcm_runtime *rtd) ++{ ++ struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); ++ const struct fsl_asoc_card_pdata *pdata = priv->pdata; + struct snd_soc_dai *codec_dai; + struct codec_priv *codec_priv; + struct device *dev = card->dev; + int codec_idx; + int ret; + ++ /* Read MCLK rate from each bound codec component */ ++ for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { ++ struct clk *codec_clk = clk_get(codec_dai->component->dev, NULL); ++ ++ codec_priv = &priv->codec_priv[codec_idx]; ++ if (!IS_ERR(codec_clk)) { ++ codec_priv->mclk_freq = clk_get_rate(codec_clk); ++ clk_put(codec_clk); ++ } ++ } ++ ++ if (pdata->codec_init) { ++ ret = pdata->codec_init(priv); ++ if (ret) ++ return ret; ++ } ++ ++ for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { ++ codec_priv = &priv->codec_priv[codec_idx]; ++ ++ ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, ++ codec_priv->mclk_freq, SND_SOC_CLOCK_IN); ++ if (ret && ret != -ENOTSUPP) { ++ dev_err(dev, "failed to set sysclk in %s\n", __func__); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static void fsl_asoc_card_free_jack(struct snd_soc_card *card) ++{ ++ struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); ++ ++ if (priv->hp_jack.gpio.desc) { ++ snd_soc_jack_notifier_unregister(&priv->hp_jack.jack, &hp_jack_nb); ++ snd_soc_jack_free_gpios(&priv->hp_jack.jack, 1, &priv->hp_jack.gpio); ++ priv->hp_jack.gpio.desc = NULL; ++ } ++ ++ if (priv->mic_jack.gpio.desc) { ++ snd_soc_jack_notifier_unregister(&priv->mic_jack.jack, &mic_jack_nb); ++ snd_soc_jack_free_gpios(&priv->mic_jack.jack, 1, &priv->mic_jack.gpio); ++ priv->mic_jack.gpio.desc = NULL; ++ } ++} ++ ++/* ++ * fsl_asoc_card_init_jack - register optional headphone and mic jacks. ++ * ++ * Called from late_probe() once per card bind cycle. ++ */ ++static int fsl_asoc_card_init_jack(struct snd_soc_card *card) ++{ ++ struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); ++ struct device_node *np = priv->pdev->dev.of_node; ++ int ret; ++ ++ /* ++ * Properties "hp-det-gpios" and "mic-det-gpios" are optional. ++ * simple_util_init_jack() checks for the GPIO property and ++ * does nothing if it is absent. ++ */ ++ if (of_property_present(np, "hp-det-gpios") || ++ of_property_present(np, "hp-det-gpio") /* deprecated */) { ++ ret = simple_util_init_jack(card, &priv->hp_jack, ++ 1, NULL, "Headphone Jack"); ++ if (ret) ++ return ret; ++ ++ snd_soc_jack_notifier_register(&priv->hp_jack.jack, &hp_jack_nb); ++ } ++ ++ if (of_property_present(np, "mic-det-gpios") || ++ of_property_present(np, "mic-det-gpio") /* deprecated */) { ++ ret = simple_util_init_jack(card, &priv->mic_jack, ++ 0, NULL, "Mic Jack"); ++ if (ret) ++ return ret; ++ ++ snd_soc_jack_notifier_register(&priv->mic_jack.jack, &mic_jack_nb); ++ } ++ ++ return 0; ++} ++ ++static int fsl_asoc_card_late_probe(struct snd_soc_card *card) ++{ ++ struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); ++ struct snd_soc_pcm_runtime *rtd; ++ int ret; ++ ++ /* Use the first rtd which carries the CPU+codec DAIs */ ++ rtd = list_first_entry(&card->rtd_list, ++ struct snd_soc_pcm_runtime, list); ++ ++ ret = fsl_asoc_card_init_jack(card); ++ if (ret) ++ goto jack_fail; ++ ++ ret = fsl_asoc_card_init_cpu(card, rtd); ++ if (ret) ++ goto jack_fail; ++ + if (fsl_asoc_card_is_ac97(priv)) { + #if IS_ENABLED(CONFIG_SND_AC97_CODEC) + struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component; +@@ -671,19 +1058,20 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card) + return 0; + } + +- for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { +- codec_priv = &priv->codec_priv[codec_idx]; ++ ret = fsl_asoc_card_init_codecs(card, rtd); ++ if (ret) ++ goto jack_fail; + +- ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, +- codec_priv->mclk_freq, SND_SOC_CLOCK_IN); +- if (ret && ret != -ENOTSUPP) { +- dev_err(dev, "failed to set sysclk in %s\n", __func__); +- return ret; +- } ++ return 0; + +- if (!IS_ERR_OR_NULL(codec_priv->mclk)) +- clk_prepare_enable(codec_priv->mclk); +- } ++jack_fail: ++ fsl_asoc_card_free_jack(card); ++ return ret; ++} ++ ++static int fsl_asoc_card_card_remove(struct snd_soc_card *card) ++{ ++ fsl_asoc_card_free_jack(card); + + return 0; + } +@@ -697,12 +1085,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) + struct platform_device *asrc_pdev = NULL; + struct device_node *bitclkprovider = NULL; + struct device_node *frameprovider = NULL; +- struct platform_device *cpu_pdev; + struct fsl_asoc_card_priv *priv; +- struct device *codec_dev[2] = { NULL, NULL }; ++ const struct fsl_asoc_card_pdata *pdata; + struct snd_soc_dai_link_component *dlc; +- const char *codec_dai_name[2]; +- const char *codec_dev_name[2]; ++ const char *codec_dai_name[2] = { NULL, NULL }; + u32 asrc_fmt = 0; + int codec_idx; + u32 width; +@@ -714,6 +1100,13 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) + + priv->pdev = pdev; + ++ pdata = of_device_get_match_data(&pdev->dev); ++ if (!pdata) { ++ dev_err(&pdev->dev, "unknown Device Tree compatible\n"); ++ return -EINVAL; ++ } ++ priv->pdata = pdata; ++ + cpu_np = of_parse_phandle(np, "audio-cpu", 0); + /* Give a chance to old DT bindings */ + if (!cpu_np) +@@ -726,52 +1119,13 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) + goto fail; + } + +- cpu_pdev = of_find_device_by_node(cpu_np); +- if (!cpu_pdev) { +- ret = dev_err_probe(&pdev->dev, -EPROBE_DEFER, +- "failed to find CPU DAI device\n"); +- goto fail; +- } +- + codec_np[0] = of_parse_phandle(np, "audio-codec", 0); + codec_np[1] = of_parse_phandle(np, "audio-codec", 1); + +- for (codec_idx = 0; codec_idx < 2; codec_idx++) { +- if (codec_np[codec_idx]) { +- struct platform_device *codec_pdev; +- struct i2c_client *codec_i2c; +- +- codec_i2c = of_find_i2c_device_by_node(codec_np[codec_idx]); +- if (codec_i2c) { +- codec_dev[codec_idx] = &codec_i2c->dev; +- codec_dev_name[codec_idx] = codec_i2c->name; +- } +- if (!codec_dev[codec_idx]) { +- codec_pdev = of_find_device_by_node(codec_np[codec_idx]); +- if (codec_pdev) { +- codec_dev[codec_idx] = &codec_pdev->dev; +- codec_dev_name[codec_idx] = codec_pdev->name; +- } +- } +- } +- } +- + asrc_np = of_parse_phandle(np, "audio-asrc", 0); + if (asrc_np) + asrc_pdev = of_find_device_by_node(asrc_np); + +- /* Get the MCLK rate only, and leave it controlled by CODEC drivers */ +- for (codec_idx = 0; codec_idx < 2; codec_idx++) { +- if (codec_dev[codec_idx]) { +- struct clk *codec_clk = clk_get(codec_dev[codec_idx], NULL); +- +- if (!IS_ERR(codec_clk)) { +- priv->codec_priv[codec_idx].mclk_freq = clk_get_rate(codec_clk); +- clk_put(codec_clk); +- } +- } +- } +- + /* Default sample rate and format, will be updated in hw_params() */ + priv->sample_rate = 44100; + priv->sample_format = SNDRV_PCM_FORMAT_S16_LE; +@@ -823,133 +1177,40 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) + } + + /* Diversify the card configurations */ +- if (of_device_is_compatible(np, "fsl,imx-audio-cs42888")) { +- codec_dai_name[0] = "cs42888"; +- priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv[0].mclk_freq; +- priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv[0].mclk_freq; +- priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; +- priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT; +- priv->cpu_priv.slot_width = 32; +- priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; +- priv->constraint_channels = &cs42888_channel_constraints; +- if (priv->codec_priv[0].mclk_freq % 12288000 == 0) +- priv->constraint_rates = &cs42888_rate_48k_constraints; +- else if (priv->codec_priv[0].mclk_freq % 11289600 == 0) +- priv->constraint_rates = &cs42888_rate_44k_constraints; +- else +- dev_warn(&pdev->dev, "Unknown MCLK frequency %lu, no rate constraints\n", +- priv->codec_priv[0].mclk_freq); +- } else if (of_device_is_compatible(np, "fsl,imx-audio-cs427x")) { +- codec_dai_name[0] = "cs4271-hifi"; +- priv->codec_priv[0].mclk_id = CS427x_SYSCLK_MCLK; +- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; +- } else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) { +- codec_dai_name[0] = "sgtl5000"; +- priv->codec_priv[0].mclk_id = SGTL5000_SYSCLK; +- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; +- } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic32x4")) { +- codec_dai_name[0] = "tlv320aic32x4-hifi"; +- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; +- } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic31xx")) { +- codec_dai_name[0] = "tlv320dac31xx-hifi"; +- priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; ++ priv->cpu_priv.sysclk_dir[TX] = pdata->sysclk_dir[TX]; ++ priv->cpu_priv.sysclk_dir[RX] = pdata->sysclk_dir[RX]; ++ priv->cpu_priv.sysclk_ratio[TX] = pdata->sysclk_ratio[TX]; ++ priv->cpu_priv.sysclk_ratio[RX] = pdata->sysclk_ratio[RX]; ++ priv->cpu_priv.slot_width = pdata->slot_width; ++ ++ codec_dai_name[0] = pdata->codec_dai_name; ++ priv->codec_priv[0].mclk_id = pdata->codec_mclk_id; ++ if (pdata->has_pll) { ++ priv->codec_priv[0].fll_id = pdata->codec_fll_id; ++ priv->codec_priv[0].pll_id = pdata->codec_pll_id; ++ } ++ if (pdata->codec_pll_ratio_s24) ++ priv->codec_priv[0].pll_ratio_s24 = pdata->codec_pll_ratio_s24; ++ ++ if (pdata->playback_only) { + priv->dai_link[1].playback_only = 1; + priv->dai_link[2].playback_only = 1; +- priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; +- priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT; +- priv->card.dapm_routes = audio_map_tx; +- priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); +- } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) { +- codec_dai_name[0] = "wm8962"; +- priv->codec_priv[0].mclk_id = WM8962_SYSCLK_MCLK; +- priv->codec_priv[0].fll_id = WM8962_SYSCLK_FLL; +- priv->codec_priv[0].pll_id = WM8962_FLL; +- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; +- /* +- * WM8962 has same BCLK generation limitations as WM8960. +- * See WM8960 section for detailed explanation. +- */ +- if (of_node_name_eq(cpu_np, "sai")) +- priv->exclude_format = SNDRV_PCM_FMTBIT_S20_3LE; +- } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8960")) { +- codec_dai_name[0] = "wm8960-hifi"; +- priv->codec_priv[0].fll_id = WM8960_SYSCLK_AUTO; +- priv->codec_priv[0].pll_id = WM8960_SYSCLK_AUTO; +- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; +- /* +- * WM8960 in master mode cannot generate exact 1.92 MHz BCLK +- * required for S20_3LE (48kHz × 2ch × 20bit). Closest available +- * is 2.048 MHz (SYSCLK/6), which causes right channel corruption. +- * +- * In SAI master mode, SAI derive BCLK from MCLK using integer +- * dividers only. S20_3LE requires non-integer divider ratios +- * with standard MCLK frequencies. For example, 48kHz stereo +- * needs 1.920 MHz BCLK, which requires a divider of 6.4 from +- * 12.288 MHz MCLK (not an integer). +- */ +- if (of_node_name_eq(cpu_np, "sai")) +- priv->exclude_format = SNDRV_PCM_FMTBIT_S20_3LE; +- } else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) { +- codec_dai_name[0] = "ac97-hifi"; +- priv->dai_fmt = SND_SOC_DAIFMT_AC97; +- priv->card.dapm_routes = audio_map_ac97; +- priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_ac97); +- } else if (of_device_is_compatible(np, "fsl,imx-audio-mqs")) { +- codec_dai_name[0] = "fsl-mqs-dai"; +- priv->dai_fmt = SND_SOC_DAIFMT_LEFT_J | +- SND_SOC_DAIFMT_CBC_CFC | +- SND_SOC_DAIFMT_NB_NF; +- priv->dai_link[1].playback_only = 1; +- priv->dai_link[2].playback_only = 1; +- priv->card.dapm_routes = audio_map_tx; +- priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); +- } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8524")) { +- codec_dai_name[0] = "wm8524-hifi"; +- priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; +- priv->dai_link[1].playback_only = 1; +- priv->dai_link[2].playback_only = 1; +- priv->cpu_priv.slot_width = 32; +- priv->card.dapm_routes = audio_map_tx; +- priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); +- priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; +- priv->cpu_priv.sysclk_ratio[TX] = 256; +- } else if (of_device_is_compatible(np, "fsl,imx-audio-si476x")) { +- codec_dai_name[0] = "si476x-codec"; +- priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; +- priv->card.dapm_routes = audio_map_rx; +- priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_rx); +- } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8958")) { +- codec_dai_name[0] = "wm8994-aif1"; +- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; +- priv->codec_priv[0].mclk_id = WM8994_FLL_SRC_MCLK1; +- priv->codec_priv[0].fll_id = WM8994_SYSCLK_FLL1; +- priv->codec_priv[0].pll_id = WM8994_FLL1; +- priv->codec_priv[0].free_freq = priv->codec_priv[0].mclk_freq; +- priv->card.dapm_routes = NULL; +- priv->card.num_dapm_routes = 0; +- } else if (of_device_is_compatible(np, "fsl,imx-audio-nau8822")) { +- codec_dai_name[0] = "nau8822-hifi"; +- priv->codec_priv[0].mclk_id = NAU8822_CLK_MCLK; +- priv->codec_priv[0].fll_id = NAU8822_CLK_PLL; +- priv->codec_priv[0].pll_id = NAU8822_CLK_PLL; +- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; +- if (codec_dev[0]) +- priv->codec_priv[0].mclk = devm_clk_get(codec_dev[0], NULL); +- } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8904")) { +- codec_dai_name[0] = "wm8904-hifi"; +- priv->codec_priv[0].mclk_id = WM8904_FLL_MCLK; +- priv->codec_priv[0].fll_id = WM8904_CLK_FLL; +- priv->codec_priv[0].pll_id = WM8904_FLL_MCLK; +- priv->codec_priv[0].pll_ratio_s24 = 192; +- priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; +- } else if (of_device_is_compatible(np, "fsl,imx-audio-spdif")) { +- ret = fsl_asoc_card_spdif_init(codec_np, cpu_np, codec_dai_name, priv); ++ } ++ if (pdata->capture_only) { ++ priv->dai_link[1].capture_only = 1; ++ priv->dai_link[2].capture_only = 1; ++ } ++ ++ priv->dai_fmt = pdata->dai_fmt; ++ ++ priv->card.dapm_routes = pdata->dapm_routes; ++ priv->card.num_dapm_routes = pdata->num_dapm_routes; ++ ++ if (pdata->probe_init) { ++ ret = pdata->probe_init(codec_np, cpu_np, ++ codec_dai_name, priv); + if (ret) + goto asrc_fail; +- } else { +- dev_err(&pdev->dev, "unknown Device Tree compatible\n"); +- ret = -EINVAL; +- goto asrc_fail; + } + + /* +@@ -995,51 +1256,21 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) + of_node_put(bitclkprovider); + of_node_put(frameprovider); + +- if (!fsl_asoc_card_is_ac97(priv) && !codec_dev[0] +- && codec_dai_name[0] != snd_soc_dummy_dlc.dai_name) { +- dev_dbg(&pdev->dev, "failed to find codec device\n"); +- ret = -EPROBE_DEFER; +- goto asrc_fail; +- } +- +- /* Common settings for corresponding Freescale CPU DAI driver */ +- if (of_node_name_eq(cpu_np, "ssi")) { +- /* Only SSI needs to configure AUDMUX */ +- ret = fsl_asoc_card_audmux_init(np, priv); +- if (ret) { +- dev_err(&pdev->dev, "failed to init audmux\n"); +- goto asrc_fail; +- } +- } else if (of_node_name_eq(cpu_np, "esai")) { +- struct clk *esai_clk = clk_get(&cpu_pdev->dev, "extal"); +- +- if (!IS_ERR(esai_clk)) { +- priv->cpu_priv.sysclk_freq[TX] = clk_get_rate(esai_clk); +- priv->cpu_priv.sysclk_freq[RX] = clk_get_rate(esai_clk); +- clk_put(esai_clk); +- } else if (PTR_ERR(esai_clk) == -EPROBE_DEFER) { +- ret = -EPROBE_DEFER; +- goto asrc_fail; +- } +- +- priv->cpu_priv.sysclk_id[1] = ESAI_HCKT_EXTAL; +- priv->cpu_priv.sysclk_id[0] = ESAI_HCKR_EXTAL; +- } else if (of_node_name_eq(cpu_np, "sai")) { +- priv->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1; +- priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1; +- } +- + /* Initialize sound card */ + priv->card.dev = &pdev->dev; + priv->card.owner = THIS_MODULE; + ret = snd_soc_of_parse_card_name(&priv->card, "model"); + if (ret) { +- snprintf(priv->name, sizeof(priv->name), "%s-audio", +- fsl_asoc_card_is_ac97(priv) ? "ac97" : codec_dev_name[0]); +- priv->card.name = priv->name; ++ /* ++ * "model" is required by the DT binding. Enforce it here so ++ * the driver fails with a clear message. ++ */ ++ dev_err(&pdev->dev, "Error parsing card name: %d\n", ret); ++ goto asrc_fail; + } + priv->card.dai_link = priv->dai_link; + priv->card.late_probe = fsl_asoc_card_late_probe; ++ priv->card.remove = fsl_asoc_card_card_remove; + priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets; + priv->card.num_dapm_widgets = ARRAY_SIZE(fsl_asoc_card_dapm_widgets); + +@@ -1146,39 +1377,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) + goto asrc_fail; + } + +- /* +- * Properties "hp-det-gpios" and "mic-det-gpios" are optional, and +- * simple_util_init_jack() uses these properties for creating +- * Headphone Jack and Microphone Jack. +- * +- * The notifier is initialized in snd_soc_card_jack_new(), then +- * snd_soc_jack_notifier_register can be called. +- */ +- if (of_property_present(np, "hp-det-gpios") || +- of_property_present(np, "hp-det-gpio") /* deprecated */) { +- ret = simple_util_init_jack(&priv->card, &priv->hp_jack, +- 1, NULL, "Headphone Jack"); +- if (ret) +- goto asrc_fail; +- +- snd_soc_jack_notifier_register(&priv->hp_jack.jack, &hp_jack_nb); +- } +- +- if (of_property_present(np, "mic-det-gpios") || +- of_property_present(np, "mic-det-gpio") /* deprecated */) { +- ret = simple_util_init_jack(&priv->card, &priv->mic_jack, +- 0, NULL, "Mic Jack"); +- if (ret) +- goto asrc_fail; +- +- snd_soc_jack_notifier_register(&priv->mic_jack.jack, &mic_jack_nb); +- } +- + asrc_fail: + of_node_put(asrc_np); + of_node_put(codec_np[0]); + of_node_put(codec_np[1]); +- put_device(&cpu_pdev->dev); + fail: + of_node_put(cpu_np); + +@@ -1186,21 +1388,21 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) + } + + static const struct of_device_id fsl_asoc_card_dt_ids[] = { +- { .compatible = "fsl,imx-audio-ac97", }, +- { .compatible = "fsl,imx-audio-cs42888", }, +- { .compatible = "fsl,imx-audio-cs427x", }, +- { .compatible = "fsl,imx-audio-tlv320aic32x4", }, +- { .compatible = "fsl,imx-audio-tlv320aic31xx", }, +- { .compatible = "fsl,imx-audio-sgtl5000", }, +- { .compatible = "fsl,imx-audio-wm8962", }, +- { .compatible = "fsl,imx-audio-wm8960", }, +- { .compatible = "fsl,imx-audio-mqs", }, +- { .compatible = "fsl,imx-audio-wm8524", }, +- { .compatible = "fsl,imx-audio-si476x", }, +- { .compatible = "fsl,imx-audio-wm8958", }, +- { .compatible = "fsl,imx-audio-nau8822", }, +- { .compatible = "fsl,imx-audio-wm8904", }, +- { .compatible = "fsl,imx-audio-spdif", }, ++ { .compatible = "fsl,imx-audio-ac97", .data = &fsl_asoc_ac97_pdata }, ++ { .compatible = "fsl,imx-audio-cs42888", .data = &fsl_asoc_cs42888_pdata }, ++ { .compatible = "fsl,imx-audio-cs427x", .data = &fsl_asoc_cs427x_pdata }, ++ { .compatible = "fsl,imx-audio-tlv320aic32x4", .data = &fsl_asoc_tlv320aic32x4_pdata }, ++ { .compatible = "fsl,imx-audio-tlv320aic31xx", .data = &fsl_asoc_tlv320aic31xx_pdata }, ++ { .compatible = "fsl,imx-audio-sgtl5000", .data = &fsl_asoc_sgtl5000_pdata }, ++ { .compatible = "fsl,imx-audio-wm8962", .data = &fsl_asoc_wm8962_pdata }, ++ { .compatible = "fsl,imx-audio-wm8960", .data = &fsl_asoc_wm8960_pdata }, ++ { .compatible = "fsl,imx-audio-mqs", .data = &fsl_asoc_mqs_pdata }, ++ { .compatible = "fsl,imx-audio-wm8524", .data = &fsl_asoc_wm8524_pdata }, ++ { .compatible = "fsl,imx-audio-si476x", .data = &fsl_asoc_si476x_pdata }, ++ { .compatible = "fsl,imx-audio-wm8958", .data = &fsl_asoc_wm8958_pdata }, ++ { .compatible = "fsl,imx-audio-nau8822", .data = &fsl_asoc_nau8822_pdata }, ++ { .compatible = "fsl,imx-audio-wm8904", .data = &fsl_asoc_wm8904_pdata }, ++ { .compatible = "fsl,imx-audio-spdif", .data = &fsl_asoc_spdif_pdata }, + {} + }; + MODULE_DEVICE_TABLE(of, fsl_asoc_card_dt_ids); +diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c +--- a/sound/soc/fsl/fsl_asrc.c ++++ b/sound/soc/fsl/fsl_asrc.c +@@ -222,10 +222,9 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair) + enum asrc_pair_index index = ASRC_INVALID_PAIR; + struct fsl_asrc *asrc = pair->asrc; + struct device *dev = &asrc->pdev->dev; +- unsigned long lock_flags; + int i, ret = 0; + +- spin_lock_irqsave(&asrc->lock, lock_flags); ++ guard(spinlock_irqsave)(&asrc->lock); + + for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) { + if (asrc->pair[i] != NULL) +@@ -250,8 +249,6 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair) + pair->index = index; + } + +- spin_unlock_irqrestore(&asrc->lock, lock_flags); +- + return ret; + } + +@@ -265,19 +262,16 @@ static void fsl_asrc_release_pair(struct fsl_asrc_pair *pair) + { + struct fsl_asrc *asrc = pair->asrc; + enum asrc_pair_index index = pair->index; +- unsigned long lock_flags; + + /* Make sure the pair is disabled */ + regmap_update_bits(asrc->regmap, REG_ASRCTR, + ASRCTR_ASRCEi_MASK(index), 0); + +- spin_lock_irqsave(&asrc->lock, lock_flags); ++ guard(spinlock_irqsave)(&asrc->lock); + + asrc->channel_avail += pair->channels; + asrc->pair[index] = NULL; + pair->error = 0; +- +- spin_unlock_irqrestore(&asrc->lock, lock_flags); + } + + /** +diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h +--- a/sound/soc/fsl/fsl_asrc.h ++++ b/sound/soc/fsl/fsl_asrc.h +@@ -444,7 +444,7 @@ struct dma_block { + }; + + /** +- * fsl_asrc_soc_data: soc specific data ++ * struct fsl_asrc_soc_data - soc specific data + * + * @use_edma: using edma as dma device or not + * @channel_bits: width of ASRCNCR register for each pair +@@ -457,7 +457,7 @@ struct fsl_asrc_soc_data { + }; + + /** +- * fsl_asrc_pair_priv: ASRC Pair private data ++ * struct fsl_asrc_pair_priv - ASRC Pair private data + * + * @config: configuration profile + */ +@@ -466,7 +466,7 @@ struct fsl_asrc_pair_priv { + }; + + /** +- * fsl_asrc_priv: ASRC private data ++ * struct fsl_asrc_priv - ASRC private data + * + * @asrck_clk: clock sources to driver ASRC internal logic + * @soc: soc specific data +diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c +--- a/sound/soc/fsl/fsl_asrc_dma.c ++++ b/sound/soc/fsl/fsl_asrc_dma.c +@@ -219,7 +219,9 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component, + */ + component_be = snd_soc_lookup_component_nolocked(dev_be, SND_DMAENGINE_PCM_DRV_NAME); + if (component_be) { +- be_chan = soc_component_to_pcm(component_be)->chan[substream->stream]; ++ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component_be); ++ ++ be_chan = pcm->chan[substream->stream]; + tmp_chan = be_chan; + } + if (!tmp_chan) { +diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c +--- a/sound/soc/fsl/fsl_audmix.c ++++ b/sound/soc/fsl/fsl_audmix.c +@@ -286,7 +286,6 @@ static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) + { + struct fsl_audmix *priv = snd_soc_dai_get_drvdata(dai); +- unsigned long lock_flags; + + /* Capture stream shall not be handled */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) +@@ -296,16 +295,14 @@ static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd, + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: +- spin_lock_irqsave(&priv->lock, lock_flags); +- priv->tdms |= BIT(dai->driver->id); +- spin_unlock_irqrestore(&priv->lock, lock_flags); ++ scoped_guard(spinlock_irqsave, &priv->lock) ++ priv->tdms |= BIT(dai->driver->id); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: +- spin_lock_irqsave(&priv->lock, lock_flags); +- priv->tdms &= ~BIT(dai->driver->id); +- spin_unlock_irqrestore(&priv->lock, lock_flags); ++ scoped_guard(spinlock_irqsave, &priv->lock) ++ priv->tdms &= ~BIT(dai->driver->id); + break; + default: + return -EINVAL; +diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c +--- a/sound/soc/fsl/fsl_dma.c ++++ b/sound/soc/fsl/fsl_dma.c +@@ -18,8 +18,6 @@ + #include + #include + #include +-#include +-#include + #include + #include + +@@ -824,9 +822,34 @@ static int fsl_soc_dma_probe(struct platform_device *pdev) + struct device_node *np = pdev->dev.of_node; + struct device_node *ssi_np; + struct resource res; ++ void __iomem *channel; + const uint32_t *iprop; ++ int irq; + int ret; + ++ channel = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(channel)) ++ return PTR_ERR(channel); ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) ++ return irq; ++ ++ dma = devm_kzalloc(&pdev->dev, sizeof(*dma), GFP_KERNEL); ++ if (!dma) ++ return -ENOMEM; ++ ++ dma->dai.name = DRV_NAME; ++ dma->dai.open = fsl_dma_open; ++ dma->dai.close = fsl_dma_close; ++ dma->dai.hw_params = fsl_dma_hw_params; ++ dma->dai.hw_free = fsl_dma_hw_free; ++ dma->dai.pointer = fsl_dma_pointer; ++ dma->dai.pcm_new = fsl_dma_new; ++ ++ dma->channel = channel; ++ dma->irq = irq; ++ + /* Find the SSI node that points to us. */ + ssi_np = find_ssi_node(np); + if (!ssi_np) { +@@ -842,55 +865,19 @@ static int fsl_soc_dma_probe(struct platform_device *pdev) + return ret; + } + +- dma = kzalloc_obj(*dma); +- if (!dma) { +- of_node_put(ssi_np); +- return -ENOMEM; +- } +- +- dma->dai.name = DRV_NAME; +- dma->dai.open = fsl_dma_open; +- dma->dai.close = fsl_dma_close; +- dma->dai.hw_params = fsl_dma_hw_params; +- dma->dai.hw_free = fsl_dma_hw_free; +- dma->dai.pointer = fsl_dma_pointer; +- dma->dai.pcm_new = fsl_dma_new; +- + /* Store the SSI-specific information that we need */ + dma->ssi_stx_phys = res.start + REG_SSI_STX0; + dma->ssi_srx_phys = res.start + REG_SSI_SRX0; + + iprop = of_get_property(ssi_np, "fsl,fifo-depth", NULL); ++ of_node_put(ssi_np); + if (iprop) + dma->ssi_fifo_depth = be32_to_cpup(iprop); + else + /* Older 8610 DTs didn't have the fifo-depth property */ + dma->ssi_fifo_depth = 8; + +- of_node_put(ssi_np); +- +- ret = devm_snd_soc_register_component(&pdev->dev, &dma->dai, NULL, 0); +- if (ret) { +- dev_err(&pdev->dev, "could not register platform\n"); +- kfree(dma); +- return ret; +- } +- +- dma->channel = of_iomap(np, 0); +- dma->irq = irq_of_parse_and_map(np, 0); +- +- dev_set_drvdata(&pdev->dev, dma); +- +- return 0; +-} +- +-static void fsl_soc_dma_remove(struct platform_device *pdev) +-{ +- struct dma_object *dma = dev_get_drvdata(&pdev->dev); +- +- iounmap(dma->channel); +- irq_dispose_mapping(dma->irq); +- kfree(dma); ++ return devm_snd_soc_register_component(&pdev->dev, &dma->dai, NULL, 0); + } + + static const struct of_device_id fsl_soc_dma_ids[] = { +@@ -905,7 +892,6 @@ static struct platform_driver fsl_soc_dma_driver = { + .of_match_table = fsl_soc_dma_ids, + }, + .probe = fsl_soc_dma_probe, +- .remove = fsl_soc_dma_remove, + }; + + module_platform_driver(fsl_soc_dma_driver); +diff --git a/sound/soc/fsl/fsl_dma.h b/sound/soc/fsl/fsl_dma.h +--- a/sound/soc/fsl/fsl_dma.h ++++ b/sound/soc/fsl/fsl_dma.h +@@ -92,7 +92,7 @@ static inline u32 CCSR_DMA_ECLNDAR_ADDR(u64 x) + #define CCSR_DMA_ATR_SNOOP 0x00050000 + #define CCSR_DMA_ATR_ESAD_MASK 0x0000000F + +-/** ++/* + * List Descriptor for extended chaining mode DMA operations. + * + * The CLSDAR register points to the first (in a linked-list) List +@@ -107,7 +107,7 @@ struct fsl_dma_list_descriptor { + u8 res[8]; /* Reserved */ + } __attribute__ ((aligned(32), packed)); + +-/** ++/* + * Link Descriptor for basic and extended chaining mode DMA operations. + * + * A Link Descriptor points to a single DMA buffer. Each link descriptor +diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c +--- a/sound/soc/fsl/fsl_easrc.c ++++ b/sound/soc/fsl/fsl_easrc.c +@@ -1025,7 +1025,6 @@ static int fsl_easrc_config_context(struct fsl_asrc *easrc, unsigned int ctx_id) + struct fsl_easrc_ctx_priv *ctx_priv; + struct fsl_asrc_pair *ctx; + struct device *dev; +- unsigned long lock_flags; + int ret; + + if (!easrc) +@@ -1053,9 +1052,8 @@ static int fsl_easrc_config_context(struct fsl_asrc *easrc, unsigned int ctx_id) + if (ret) + return ret; + +- spin_lock_irqsave(&easrc->lock, lock_flags); +- ret = fsl_easrc_config_slot(easrc, ctx->index); +- spin_unlock_irqrestore(&easrc->lock, lock_flags); ++ scoped_guard(spinlock_irqsave, &easrc->lock) ++ ret = fsl_easrc_config_slot(easrc, ctx->index); + if (ret) + return ret; + +@@ -1301,13 +1299,12 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx) + enum asrc_pair_index index = ASRC_INVALID_PAIR; + struct fsl_asrc *easrc = ctx->asrc; + struct device *dev; +- unsigned long lock_flags; + int ret = 0; + int i; + + dev = &easrc->pdev->dev; + +- spin_lock_irqsave(&easrc->lock, lock_flags); ++ guard(spinlock_irqsave)(&easrc->lock); + + for (i = ASRC_PAIR_A; i < EASRC_CTX_MAX_NUM; i++) { + if (easrc->pair[i]) +@@ -1331,8 +1328,6 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx) + easrc->channel_avail -= channels; + } + +- spin_unlock_irqrestore(&easrc->lock, lock_flags); +- + return ret; + } + +@@ -1343,7 +1338,6 @@ static int fsl_easrc_request_context(int channels, struct fsl_asrc_pair *ctx) + */ + static void fsl_easrc_release_context(struct fsl_asrc_pair *ctx) + { +- unsigned long lock_flags; + struct fsl_asrc *easrc; + + if (!ctx) +@@ -1351,14 +1345,12 @@ static void fsl_easrc_release_context(struct fsl_asrc_pair *ctx) + + easrc = ctx->asrc; + +- spin_lock_irqsave(&easrc->lock, lock_flags); ++ guard(spinlock_irqsave)(&easrc->lock); + + fsl_easrc_release_slot(easrc, ctx->index); + + easrc->channel_avail += ctx->channels; + easrc->pair[ctx->index] = NULL; +- +- spin_unlock_irqrestore(&easrc->lock, lock_flags); + } + + /* +@@ -2292,15 +2284,13 @@ static int fsl_easrc_runtime_suspend(struct device *dev) + { + struct fsl_asrc *easrc = dev_get_drvdata(dev); + struct fsl_easrc_priv *easrc_priv = easrc->private; +- unsigned long lock_flags; + + regcache_cache_only(easrc->regmap, true); + + clk_disable_unprepare(easrc->mem_clk); + +- spin_lock_irqsave(&easrc->lock, lock_flags); +- easrc_priv->firmware_loaded = 0; +- spin_unlock_irqrestore(&easrc->lock, lock_flags); ++ scoped_guard(spinlock_irqsave, &easrc->lock) ++ easrc_priv->firmware_loaded = 0; + + return 0; + } +@@ -2311,7 +2301,6 @@ static int fsl_easrc_runtime_resume(struct device *dev) + struct fsl_easrc_priv *easrc_priv = easrc->private; + struct fsl_easrc_ctx_priv *ctx_priv; + struct fsl_asrc_pair *ctx; +- unsigned long lock_flags; + int ret; + int i; + +@@ -2323,13 +2312,11 @@ static int fsl_easrc_runtime_resume(struct device *dev) + regcache_mark_dirty(easrc->regmap); + regcache_sync(easrc->regmap); + +- spin_lock_irqsave(&easrc->lock, lock_flags); +- if (easrc_priv->firmware_loaded) { +- spin_unlock_irqrestore(&easrc->lock, lock_flags); +- goto skip_load; ++ scoped_guard(spinlock_irqsave, &easrc->lock) { ++ if (easrc_priv->firmware_loaded) ++ return 0; ++ easrc_priv->firmware_loaded = 1; + } +- easrc_priv->firmware_loaded = 1; +- spin_unlock_irqrestore(&easrc->lock, lock_flags); + + ret = fsl_easrc_get_firmware(easrc); + if (ret) { +@@ -2377,9 +2364,7 @@ static int fsl_easrc_runtime_resume(struct device *dev) + goto disable_mem_clk; + } + +-skip_load: + return 0; +- + disable_mem_clk: + clk_disable_unprepare(easrc->mem_clk); + return ret; +diff --git a/sound/soc/fsl/fsl_easrc.h b/sound/soc/fsl/fsl_easrc.h +--- a/sound/soc/fsl/fsl_easrc.h ++++ b/sound/soc/fsl/fsl_easrc.h +@@ -584,7 +584,7 @@ struct fsl_easrc_slot { + }; + + /** +- * fsl_easrc_ctx_priv: EASRC context private data ++ * struct fsl_easrc_ctx_priv - EASRC context private data + * + * @in_params: input parameter + * @out_params: output parameter +@@ -625,7 +625,7 @@ struct fsl_easrc_ctx_priv { + }; + + /** +- * fsl_easrc_priv: EASRC private data ++ * struct fsl_easrc_priv - EASRC private data + * + * @slot: slot setting + * @firmware_hdr: the header of firmware +diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c +--- a/sound/soc/fsl/fsl_esai.c ++++ b/sound/soc/fsl/fsl_esai.c +@@ -709,10 +709,9 @@ static void fsl_esai_hw_reset(struct work_struct *work) + { + struct fsl_esai *esai_priv = container_of(work, struct fsl_esai, work); + bool tx = true, rx = false, enabled[2]; +- unsigned long lock_flags; + u32 tfcr, rfcr; + +- spin_lock_irqsave(&esai_priv->lock, lock_flags); ++ guard(spinlock_irqsave)(&esai_priv->lock); + /* Save the registers */ + regmap_read(esai_priv->regmap, REG_ESAI_TFCR, &tfcr); + regmap_read(esai_priv->regmap, REG_ESAI_RFCR, &rfcr); +@@ -750,8 +749,6 @@ static void fsl_esai_hw_reset(struct work_struct *work) + fsl_esai_trigger_start(esai_priv, tx); + if (enabled[rx]) + fsl_esai_trigger_start(esai_priv, rx); +- +- spin_unlock_irqrestore(&esai_priv->lock, lock_flags); + } + + static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, +@@ -759,7 +756,6 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, + { + struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; +- unsigned long lock_flags; + + esai_priv->channels[tx] = substream->runtime->channels; + +@@ -767,16 +763,14 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: +- spin_lock_irqsave(&esai_priv->lock, lock_flags); +- fsl_esai_trigger_start(esai_priv, tx); +- spin_unlock_irqrestore(&esai_priv->lock, lock_flags); ++ scoped_guard(spinlock_irqsave, &esai_priv->lock) ++ fsl_esai_trigger_start(esai_priv, tx); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: +- spin_lock_irqsave(&esai_priv->lock, lock_flags); +- fsl_esai_trigger_stop(esai_priv, tx); +- spin_unlock_irqrestore(&esai_priv->lock, lock_flags); ++ scoped_guard(spinlock_irqsave, &esai_priv->lock) ++ fsl_esai_trigger_stop(esai_priv, tx); + break; + default: + return -EINVAL; +diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c +--- a/sound/soc/fsl/fsl_spdif.c ++++ b/sound/soc/fsl/fsl_spdif.c +@@ -853,17 +853,15 @@ static int fsl_spdif_subcode_get(struct snd_kcontrol *kcontrol, + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; +- unsigned long flags; + int ret = -EAGAIN; + +- spin_lock_irqsave(&ctrl->ctl_lock, flags); ++ guard(spinlock_irqsave)(&ctrl->ctl_lock); + if (ctrl->ready_buf) { + int idx = (ctrl->ready_buf - 1) * SPDIF_UBITS_SIZE; + memcpy(&ucontrol->value.iec958.subcode[0], + &ctrl->subcode[idx], SPDIF_UBITS_SIZE); + ret = 0; + } +- spin_unlock_irqrestore(&ctrl->ctl_lock, flags); + + return ret; + } +@@ -885,17 +883,15 @@ static int fsl_spdif_qget(struct snd_kcontrol *kcontrol, + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; +- unsigned long flags; + int ret = -EAGAIN; + +- spin_lock_irqsave(&ctrl->ctl_lock, flags); ++ guard(spinlock_irqsave)(&ctrl->ctl_lock); + if (ctrl->ready_buf) { + int idx = (ctrl->ready_buf - 1) * SPDIF_QSUB_SIZE; + memcpy(&ucontrol->value.bytes.data[0], + &ctrl->qsub[idx], SPDIF_QSUB_SIZE); + ret = 0; + } +- spin_unlock_irqrestore(&ctrl->ctl_lock, flags); + + return ret; + } +diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c +--- a/sound/soc/fsl/fsl_ssi.c ++++ b/sound/soc/fsl/fsl_ssi.c +@@ -1218,13 +1218,13 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, + if (reg > 0x7f) + return; + +- mutex_lock(&fsl_ac97_data->ac97_reg_lock); ++ guard(mutex)(&fsl_ac97_data->ac97_reg_lock); + + ret = clk_prepare_enable(fsl_ac97_data->clk); + if (ret) { + pr_err("ac97 write clk_prepare_enable failed: %d\n", + ret); +- goto ret_unlock; ++ return; + } + + lreg = reg << 12; +@@ -1238,9 +1238,6 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, + udelay(100); + + clk_disable_unprepare(fsl_ac97_data->clk); +- +-ret_unlock: +- mutex_unlock(&fsl_ac97_data->ac97_reg_lock); + } + + static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, +@@ -1252,12 +1249,12 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, + unsigned int lreg; + int ret; + +- mutex_lock(&fsl_ac97_data->ac97_reg_lock); ++ guard(mutex)(&fsl_ac97_data->ac97_reg_lock); + + ret = clk_prepare_enable(fsl_ac97_data->clk); + if (ret) { + pr_err("ac97 read clk_prepare_enable failed: %d\n", ret); +- goto ret_unlock; ++ return val; + } + + lreg = (reg & 0x7f) << 12; +@@ -1272,8 +1269,6 @@ static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, + + clk_disable_unprepare(fsl_ac97_data->clk); + +-ret_unlock: +- mutex_unlock(&fsl_ac97_data->ac97_reg_lock); + return val; + } + +diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c +--- a/sound/soc/fsl/fsl_xcvr.c ++++ b/sound/soc/fsl/fsl_xcvr.c +@@ -797,10 +797,9 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, + { + struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; +- unsigned long lock_flags; + int ret = 0; + +- spin_lock_irqsave(&xcvr->lock, lock_flags); ++ guard(spinlock_irqsave)(&xcvr->lock); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: +@@ -812,7 +811,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, + FSL_XCVR_EXT_CTRL_DPTH_RESET(tx)); + if (ret < 0) { + dev_err(dai->dev, "Failed to set DPATH RESET: %d\n", ret); +- goto release_lock; ++ return ret; + } + + if (tx) { +@@ -824,7 +823,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, + FSL_XCVR_ISR_CMDC_TX_EN); + if (ret < 0) { + dev_err(dai->dev, "err updating isr %d\n", ret); +- goto release_lock; ++ return ret; + } + fallthrough; + case FSL_XCVR_MODE_SPDIF: +@@ -833,7 +832,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, + FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX); + if (ret < 0) { + dev_err(dai->dev, "Failed to start DATA_TX: %d\n", ret); +- goto release_lock; ++ return ret; + } + break; + } +@@ -844,14 +843,14 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, + FSL_XCVR_EXT_CTRL_DMA_DIS(tx), 0); + if (ret < 0) { + dev_err(dai->dev, "Failed to enable DMA: %d\n", ret); +- goto release_lock; ++ return ret; + } + + ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0, + FSL_XCVR_IRQ_EARC_ALL, FSL_XCVR_IRQ_EARC_ALL); + if (ret < 0) { + dev_err(dai->dev, "Error while setting IER0: %d\n", ret); +- goto release_lock; ++ return ret; + } + + /* clear DPATH RESET */ +@@ -860,7 +859,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, + 0); + if (ret < 0) { + dev_err(dai->dev, "Failed to clear DPATH RESET: %d\n", ret); +- goto release_lock; ++ return ret; + } + + break; +@@ -873,14 +872,14 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, + FSL_XCVR_EXT_CTRL_DMA_DIS(tx)); + if (ret < 0) { + dev_err(dai->dev, "Failed to disable DMA: %d\n", ret); +- goto release_lock; ++ return ret; + } + + ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0, + FSL_XCVR_IRQ_EARC_ALL, 0); + if (ret < 0) { + dev_err(dai->dev, "Failed to clear IER0: %d\n", ret); +- goto release_lock; ++ return ret; + } + + if (tx) { +@@ -891,7 +890,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, + FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX); + if (ret < 0) { + dev_err(dai->dev, "Failed to stop DATA_TX: %d\n", ret); +- goto release_lock; ++ return ret; + } + if (xcvr->soc_data->spdif_only) + break; +@@ -905,7 +904,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, + if (ret < 0) { + dev_err(dai->dev, + "Err updating ISR %d\n", ret); +- goto release_lock; ++ return ret; + } + break; + } +@@ -916,18 +915,16 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, + break; + } + +-release_lock: +- spin_unlock_irqrestore(&xcvr->lock, lock_flags); + return ret; + } + + static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) + { + struct device *dev = &xcvr->pdev->dev; +- const struct firmware *fw; + int ret = 0, rem, off, out, page = 0, size = FSL_XCVR_REG_OFFSET; + u32 mask, val; + ++ const struct firmware *fw __free(firmware) = NULL; + ret = request_firmware(&fw, xcvr->soc_data->fw_name, dev); + if (ret) { + dev_err(dev, "failed to request firmware.\n"); +@@ -939,7 +936,6 @@ static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) + /* RAM is 20KiB = 16KiB code + 4KiB data => max 10 pages 2KiB each */ + if (rem > 16384) { + dev_err(dev, "FW size %d is bigger than 16KiB.\n", rem); +- release_firmware(fw); + return -ENOMEM; + } + +@@ -950,7 +946,7 @@ static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) + if (ret < 0) { + dev_err(dev, "FW: failed to set page %d, err=%d\n", + page, ret); +- goto err_firmware; ++ return ret; + } + + off = page * size; +@@ -971,11 +967,6 @@ static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) + } + } + +-err_firmware: +- release_firmware(fw); +- if (ret < 0) +- return ret; +- + /* configure watermarks */ + mask = FSL_XCVR_EXT_CTRL_RX_FWM_MASK | FSL_XCVR_EXT_CTRL_TX_FWM_MASK; + val = FSL_XCVR_EXT_CTRL_RX_FWM(FSL_XCVR_FIFO_WMK_RX); +@@ -1448,11 +1439,10 @@ static void reset_rx_work(struct work_struct *work) + { + struct fsl_xcvr *xcvr = container_of(work, struct fsl_xcvr, work_rst); + struct device *dev = &xcvr->pdev->dev; +- unsigned long lock_flags; + u32 ext_ctrl; + + dev_dbg(dev, "reset rx path\n"); +- spin_lock_irqsave(&xcvr->lock, lock_flags); ++ guard(spinlock_irqsave)(&xcvr->lock); + regmap_read(xcvr->regmap, FSL_XCVR_EXT_CTRL, &ext_ctrl); + + if (!(ext_ctrl & FSL_XCVR_EXT_CTRL_DMA_RD_DIS)) { +@@ -1469,7 +1459,6 @@ static void reset_rx_work(struct work_struct *work) + FSL_XCVR_EXT_CTRL_RX_DPTH_RESET, + 0); + } +- spin_unlock_irqrestore(&xcvr->lock, lock_flags); + } + + static irqreturn_t irq0_isr(int irq, void *devid) +diff --git a/sound/soc/fsl/imx-audio-rpmsg.c b/sound/soc/fsl/imx-audio-rpmsg.c +--- a/sound/soc/fsl/imx-audio-rpmsg.c ++++ b/sound/soc/fsl/imx-audio-rpmsg.c +@@ -22,7 +22,6 @@ static int imx_audio_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, + struct rpmsg_r_msg *r_msg = (struct rpmsg_r_msg *)data; + struct rpmsg_info *info; + struct rpmsg_msg *msg; +- unsigned long flags; + + if (!rpmsg->rpmsg_pdev) + return 0; +@@ -37,21 +36,21 @@ static int imx_audio_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, + /* TYPE C is notification from M core */ + switch (r_msg->header.cmd) { + case TX_PERIOD_DONE: +- spin_lock_irqsave(&info->lock[TX], flags); +- msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM]; +- msg->r_msg.param.buffer_tail = +- r_msg->param.buffer_tail; +- msg->r_msg.param.buffer_tail %= info->num_period[TX]; +- spin_unlock_irqrestore(&info->lock[TX], flags); ++ scoped_guard(spinlock_irqsave, &info->lock[TX]) { ++ msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM]; ++ msg->r_msg.param.buffer_tail = ++ r_msg->param.buffer_tail; ++ msg->r_msg.param.buffer_tail %= info->num_period[TX]; ++ } + info->callback[TX](info->callback_param[TX]); + break; + case RX_PERIOD_DONE: +- spin_lock_irqsave(&info->lock[RX], flags); +- msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM]; +- msg->r_msg.param.buffer_tail = +- r_msg->param.buffer_tail; +- msg->r_msg.param.buffer_tail %= info->num_period[1]; +- spin_unlock_irqrestore(&info->lock[RX], flags); ++ scoped_guard(spinlock_irqsave, &info->lock[RX]) { ++ msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM]; ++ msg->r_msg.param.buffer_tail = ++ r_msg->param.buffer_tail; ++ msg->r_msg.param.buffer_tail %= info->num_period[1]; ++ } + info->callback[RX](info->callback_param[RX]); + break; + default: +diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c +--- a/sound/soc/fsl/imx-pcm-rpmsg.c ++++ b/sound/soc/fsl/imx-pcm-rpmsg.c +@@ -39,10 +39,9 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, + struct rpmsg_device *rpdev = info->rpdev; + int ret = 0; + +- mutex_lock(&info->msg_lock); ++ guard(mutex)(&info->msg_lock); + if (!rpdev) { + dev_err(info->dev, "rpmsg channel not ready\n"); +- mutex_unlock(&info->msg_lock); + return -EINVAL; + } + +@@ -55,15 +54,12 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, + sizeof(struct rpmsg_s_msg)); + if (ret) { + dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret); +- mutex_unlock(&info->msg_lock); + return ret; + } + + /* No receive msg for TYPE_C command */ +- if (msg->s_msg.header.type == MSG_TYPE_C) { +- mutex_unlock(&info->msg_lock); ++ if (msg->s_msg.header.type == MSG_TYPE_C) + return 0; +- } + + /* wait response from rpmsg */ + ret = wait_for_completion_timeout(&info->cmd_complete, +@@ -71,7 +67,6 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, + if (!ret) { + dev_err(&rpdev->dev, "rpmsg_send cmd %d timeout!\n", + msg->s_msg.header.cmd); +- mutex_unlock(&info->msg_lock); + return -ETIMEDOUT; + } + +@@ -100,8 +95,6 @@ static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg, + dev_dbg(&rpdev->dev, "cmd:%d, resp %d\n", msg->s_msg.header.cmd, + info->r_msg.param.resp); + +- mutex_unlock(&info->msg_lock); +- + return 0; + } + +@@ -109,14 +102,13 @@ static int imx_rpmsg_insert_workqueue(struct snd_pcm_substream *substream, + struct rpmsg_msg *msg, + struct rpmsg_info *info) + { +- unsigned long flags; + int ret = 0; + + /* + * Queue the work to workqueue. + * If the queue is full, drop the message. + */ +- spin_lock_irqsave(&info->wq_lock, flags); ++ guard(spinlock_irqsave)(&info->wq_lock); + if (info->work_write_index != info->work_read_index) { + int index = info->work_write_index; + +@@ -130,7 +122,6 @@ static int imx_rpmsg_insert_workqueue(struct snd_pcm_substream *substream, + info->msg_drop_count[substream->stream]++; + ret = -EPIPE; + } +- spin_unlock_irqrestore(&info->wq_lock, flags); + + return ret; + } +@@ -523,7 +514,6 @@ static int imx_rpmsg_pcm_ack(struct snd_soc_component *component, + snd_pcm_sframes_t avail; + struct timer_list *timer; + struct rpmsg_msg *msg; +- unsigned long flags; + int buffer_tail = 0; + int written_num; + +@@ -553,11 +543,11 @@ static int imx_rpmsg_pcm_ack(struct snd_soc_component *component, + msg->s_msg.param.buffer_tail = buffer_tail; + + /* The notification message is updated to latest */ +- spin_lock_irqsave(&info->lock[substream->stream], flags); +- memcpy(&info->notify[substream->stream], msg, +- sizeof(struct rpmsg_s_msg)); +- info->notify_updated[substream->stream] = true; +- spin_unlock_irqrestore(&info->lock[substream->stream], flags); ++ scoped_guard(spinlock_irqsave, &info->lock[substream->stream]) { ++ memcpy(&info->notify[substream->stream], msg, ++ sizeof(struct rpmsg_s_msg)); ++ info->notify_updated[substream->stream] = true; ++ } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + avail = snd_pcm_playback_hw_avail(runtime); +@@ -641,7 +631,7 @@ static void imx_rpmsg_pcm_work(struct work_struct *work) + bool is_notification = false; + struct rpmsg_info *info; + struct rpmsg_msg msg; +- unsigned long flags; ++ bool updated; + + work_of_rpmsg = container_of(work, struct work_of_rpmsg, work); + info = work_of_rpmsg->info; +@@ -652,25 +642,26 @@ static void imx_rpmsg_pcm_work(struct work_struct *work) + * enough data in M core side, need to let M core know + * data is updated immediately. + */ +- spin_lock_irqsave(&info->lock[TX], flags); +- if (info->notify_updated[TX]) { +- memcpy(&msg, &info->notify[TX], sizeof(struct rpmsg_s_msg)); +- info->notify_updated[TX] = false; +- spin_unlock_irqrestore(&info->lock[TX], flags); +- info->send_message(&msg, info); +- } else { +- spin_unlock_irqrestore(&info->lock[TX], flags); ++ scoped_guard(spinlock_irqsave, &info->lock[TX]) { ++ updated = info->notify_updated[TX]; ++ if (updated) { ++ memcpy(&msg, &info->notify[TX], sizeof(struct rpmsg_s_msg)); ++ info->notify_updated[TX] = false; ++ } + } ++ if (updated) ++ info->send_message(&msg, info); + +- spin_lock_irqsave(&info->lock[RX], flags); +- if (info->notify_updated[RX]) { +- memcpy(&msg, &info->notify[RX], sizeof(struct rpmsg_s_msg)); +- info->notify_updated[RX] = false; +- spin_unlock_irqrestore(&info->lock[RX], flags); +- info->send_message(&msg, info); +- } else { +- spin_unlock_irqrestore(&info->lock[RX], flags); ++ scoped_guard(spinlock_irqsave, &info->lock[RX]) { ++ updated = info->notify_updated[RX]; ++ if (updated) { ++ memcpy(&msg, &info->notify[RX], sizeof(struct rpmsg_s_msg)); ++ info->notify_updated[RX] = false; ++ } + } ++ if (updated) ++ info->send_message(&msg, info); ++ + + /* Skip the notification message for it has been processed above */ + if (work_of_rpmsg->msg.s_msg.header.type == MSG_TYPE_C && +@@ -682,10 +673,10 @@ static void imx_rpmsg_pcm_work(struct work_struct *work) + info->send_message(&work_of_rpmsg->msg, info); + + /* update read index */ +- spin_lock_irqsave(&info->wq_lock, flags); +- info->work_read_index++; +- info->work_read_index %= WORK_MAX_NUM; +- spin_unlock_irqrestore(&info->wq_lock, flags); ++ scoped_guard(spinlock_irqsave, &info->wq_lock) { ++ info->work_read_index++; ++ info->work_read_index %= WORK_MAX_NUM; ++ } + } + + static int imx_rpmsg_pcm_probe(struct platform_device *pdev) +@@ -819,9 +810,9 @@ static const struct dev_pm_ops imx_rpmsg_pcm_pm_ops = { + }; + + static const struct platform_device_id imx_rpmsg_pcm_id_table[] = { +- { .name = "rpmsg-audio-channel" }, +- { .name = "rpmsg-micfil-channel" }, +- { }, ++ { .name = "rpmsg-audio-channel" }, ++ { .name = "rpmsg-micfil-channel" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, imx_rpmsg_pcm_id_table); + +diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c +--- a/sound/soc/fsl/mpc5200_dma.c ++++ b/sound/soc/fsl/mpc5200_dma.c +@@ -77,18 +77,20 @@ static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream) + { + struct psc_dma_stream *s = _psc_dma_stream; + +- spin_lock(&s->psc_dma->lock); +- /* For each finished period, dequeue the completed period buffer +- * and enqueue a new one in it's place. */ +- while (bcom_buffer_done(s->bcom_task)) { +- bcom_retrieve_buffer(s->bcom_task, NULL, NULL); ++ scoped_guard(spinlock, &s->psc_dma->lock) { ++ /* ++ * For each finished period, dequeue the completed period buffer ++ * and enqueue a new one in its place ++ */ ++ while (bcom_buffer_done(s->bcom_task)) { ++ bcom_retrieve_buffer(s->bcom_task, NULL, NULL); + +- s->period_current = (s->period_current+1) % s->runtime->periods; +- s->period_count++; ++ s->period_current = (s->period_current+1) % s->runtime->periods; ++ s->period_count++; + +- psc_dma_bcom_enqueue_next_buffer(s); ++ psc_dma_bcom_enqueue_next_buffer(s); ++ } + } +- spin_unlock(&s->psc_dma->lock); + + /* If the stream is active, then also inform the PCM middle layer + * of the period finished event. */ +@@ -116,7 +118,6 @@ static int psc_dma_trigger(struct snd_soc_component *component, + struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); + struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; + u16 imr; +- unsigned long flags; + int i; + + switch (cmd) { +@@ -135,19 +136,18 @@ static int psc_dma_trigger(struct snd_soc_component *component, + /* Fill up the bestcomm bd queue and enable DMA. + * This will begin filling the PSC's fifo. + */ +- spin_lock_irqsave(&psc_dma->lock, flags); ++ scoped_guard(spinlock_irqsave, &psc_dma->lock) { ++ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) ++ bcom_gen_bd_rx_reset(s->bcom_task); ++ else ++ bcom_gen_bd_tx_reset(s->bcom_task); + +- if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) +- bcom_gen_bd_rx_reset(s->bcom_task); +- else +- bcom_gen_bd_tx_reset(s->bcom_task); ++ for (i = 0; i < runtime->periods; i++) ++ if (!bcom_queue_full(s->bcom_task)) ++ psc_dma_bcom_enqueue_next_buffer(s); + +- for (i = 0; i < runtime->periods; i++) +- if (!bcom_queue_full(s->bcom_task)) +- psc_dma_bcom_enqueue_next_buffer(s); +- +- bcom_enable(s->bcom_task); +- spin_unlock_irqrestore(&psc_dma->lock, flags); ++ bcom_enable(s->bcom_task); ++ } + + out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); + +@@ -158,13 +158,13 @@ static int psc_dma_trigger(struct snd_soc_component *component, + substream->pstr->stream, s->period_count); + s->active = 0; + +- spin_lock_irqsave(&psc_dma->lock, flags); +- bcom_disable(s->bcom_task); +- if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) +- bcom_gen_bd_rx_reset(s->bcom_task); +- else +- bcom_gen_bd_tx_reset(s->bcom_task); +- spin_unlock_irqrestore(&psc_dma->lock, flags); ++ scoped_guard(spinlock_irqsave, &psc_dma->lock) { ++ bcom_disable(s->bcom_task); ++ if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) ++ bcom_gen_bd_rx_reset(s->bcom_task); ++ else ++ bcom_gen_bd_tx_reset(s->bcom_task); ++ } + + break; + +@@ -314,35 +314,29 @@ int mpc5200_audio_dma_create(struct platform_device *op) + { + phys_addr_t fifo; + struct psc_dma *psc_dma; +- struct resource res; ++ struct resource *res; + int size, irq, rc; + const __be32 *prop; + void __iomem *regs; +- int ret; ++ ++ regs = devm_platform_get_and_ioremap_resource(op, 0, &res); ++ if (IS_ERR(regs)) ++ return PTR_ERR(regs); + + /* Fetch the registers and IRQ of the PSC */ +- irq = irq_of_parse_and_map(op->dev.of_node, 0); +- if (of_address_to_resource(op->dev.of_node, 0, &res)) { +- dev_err(&op->dev, "Missing reg property\n"); +- return -ENODEV; +- } +- regs = devm_ioremap(&op->dev, res.start, resource_size(&res)); +- if (!regs) { +- dev_err(&op->dev, "Could not map registers\n"); +- return -ENODEV; +- } ++ irq = platform_get_irq(op, 0); ++ if (irq < 0) ++ return irq; + + /* Allocate and initialize the driver private data */ +- psc_dma = kzalloc_obj(*psc_dma); ++ psc_dma = devm_kzalloc(&op->dev, sizeof(*psc_dma), GFP_KERNEL); + if (!psc_dma) + return -ENOMEM; + + /* Get the PSC ID */ + prop = of_get_property(op->dev.of_node, "cell-index", &size); +- if (!prop || size < sizeof *prop) { +- ret = -ENODEV; +- goto out_free; +- } ++ if (!prop || size < sizeof *prop) ++ return -ENODEV; + + spin_lock_init(&psc_dma->lock); + mutex_init(&psc_dma->mutex); +@@ -357,7 +351,7 @@ int mpc5200_audio_dma_create(struct platform_device *op) + + /* Find the address of the fifo data registers and setup the + * DMA tasks */ +- fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32); ++ fifo = res->start + offsetof(struct mpc52xx_psc, buffer.buffer_32); + psc_dma->capture.bcom_task = + bcom_psc_gen_bd_rx_init(psc_dma->id, 10, fifo, 512); + psc_dma->playback.bcom_task = +@@ -365,8 +359,7 @@ int mpc5200_audio_dma_create(struct platform_device *op) + if (!psc_dma->capture.bcom_task || + !psc_dma->playback.bcom_task) { + dev_err(&op->dev, "Could not allocate bestcomm tasks\n"); +- ret = -ENODEV; +- goto out_free; ++ return -ENODEV; + } + + /* Disable all interrupts and reset the PSC */ +@@ -399,16 +392,14 @@ int mpc5200_audio_dma_create(struct platform_device *op) + psc_dma->capture.irq = + bcom_get_task_irq(psc_dma->capture.bcom_task); + +- rc = request_irq(psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED, ++ rc = devm_request_irq(&op->dev, psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED, + "psc-dma-status", psc_dma); +- rc |= request_irq(psc_dma->capture.irq, &psc_dma_bcom_irq, IRQF_SHARED, ++ rc |= devm_request_irq(&op->dev, psc_dma->capture.irq, &psc_dma_bcom_irq, IRQF_SHARED, + "psc-dma-capture", &psc_dma->capture); +- rc |= request_irq(psc_dma->playback.irq, &psc_dma_bcom_irq, IRQF_SHARED, ++ rc |= devm_request_irq(&op->dev, psc_dma->playback.irq, &psc_dma_bcom_irq, IRQF_SHARED, + "psc-dma-playback", &psc_dma->playback); +- if (rc) { +- ret = -ENODEV; +- goto out_irq; +- } ++ if (rc) ++ return -ENODEV; + + /* Save what we've done so it can be found again later */ + dev_set_drvdata(&op->dev, psc_dma); +@@ -416,13 +407,6 @@ int mpc5200_audio_dma_create(struct platform_device *op) + /* Tell the ASoC OF helpers about it */ + return devm_snd_soc_register_component(&op->dev, + &mpc5200_audio_dma_component, NULL, 0); +-out_irq: +- free_irq(psc_dma->irq, psc_dma); +- free_irq(psc_dma->capture.irq, &psc_dma->capture); +- free_irq(psc_dma->playback.irq, &psc_dma->playback); +-out_free: +- kfree(psc_dma); +- return ret; + } + EXPORT_SYMBOL_GPL(mpc5200_audio_dma_create); + +@@ -435,12 +419,6 @@ int mpc5200_audio_dma_destroy(struct platform_device *op) + bcom_gen_bd_rx_release(psc_dma->capture.bcom_task); + bcom_gen_bd_tx_release(psc_dma->playback.bcom_task); + +- /* Release irqs */ +- free_irq(psc_dma->irq, psc_dma); +- free_irq(psc_dma->capture.irq, &psc_dma->capture); +- free_irq(psc_dma->playback.irq, &psc_dma->playback); +- +- kfree(psc_dma); + dev_set_drvdata(&op->dev, NULL); + + return 0; +diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c +--- a/sound/soc/fsl/mpc5200_psc_ac97.c ++++ b/sound/soc/fsl/mpc5200_psc_ac97.c +@@ -30,14 +30,13 @@ static unsigned short psc_ac97_read(struct snd_ac97 *ac97, unsigned short reg) + int status; + unsigned int val; + +- mutex_lock(&psc_dma->mutex); ++ guard(mutex)(&psc_dma->mutex); + + /* Wait for command send status zero = ready */ + status = spin_event_timeout(!(in_be16(&psc_dma->psc_regs->sr_csr.status) & + MPC52xx_PSC_SR_CMDSEND), 100, 0); + if (status == 0) { + pr_err("timeout on ac97 bus (rdy)\n"); +- mutex_unlock(&psc_dma->mutex); + return -ENODEV; + } + +@@ -53,19 +52,16 @@ static unsigned short psc_ac97_read(struct snd_ac97 *ac97, unsigned short reg) + if (status == 0) { + pr_err("timeout on ac97 read (val) %x\n", + in_be16(&psc_dma->psc_regs->sr_csr.status)); +- mutex_unlock(&psc_dma->mutex); + return -ENODEV; + } + /* Get the data */ + val = in_be32(&psc_dma->psc_regs->ac97_data); + if (((val >> 24) & 0x7f) != reg) { + pr_err("reg echo error on ac97 read\n"); +- mutex_unlock(&psc_dma->mutex); + return -ENODEV; + } + val = (val >> 8) & 0xffff; + +- mutex_unlock(&psc_dma->mutex); + return (unsigned short) val; + } + +@@ -74,52 +70,46 @@ static void psc_ac97_write(struct snd_ac97 *ac97, + { + int status; + +- mutex_lock(&psc_dma->mutex); ++ guard(mutex)(&psc_dma->mutex); + + /* Wait for command status zero = ready */ + status = spin_event_timeout(!(in_be16(&psc_dma->psc_regs->sr_csr.status) & + MPC52xx_PSC_SR_CMDSEND), 100, 0); + if (status == 0) { + pr_err("timeout on ac97 bus (write)\n"); +- goto out; ++ return; + } + /* Write data */ + out_be32(&psc_dma->psc_regs->ac97_cmd, + ((reg & 0x7f) << 24) | (val << 8)); +- +- out: +- mutex_unlock(&psc_dma->mutex); + } + + static void psc_ac97_warm_reset(struct snd_ac97 *ac97) + { + struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; + +- mutex_lock(&psc_dma->mutex); ++ guard(mutex)(&psc_dma->mutex); + + out_be32(®s->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_AWR); + udelay(3); + out_be32(®s->sicr, psc_dma->sicr); +- +- mutex_unlock(&psc_dma->mutex); + } + + static void psc_ac97_cold_reset(struct snd_ac97 *ac97) + { + struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; + +- mutex_lock(&psc_dma->mutex); +- dev_dbg(psc_dma->dev, "cold reset\n"); ++ scoped_guard(mutex, &psc_dma->mutex) { ++ dev_dbg(psc_dma->dev, "cold reset\n"); + +- mpc5200_psc_ac97_gpio_reset(psc_dma->id); ++ mpc5200_psc_ac97_gpio_reset(psc_dma->id); + +- /* Notify the PSC that a reset has occurred */ +- out_be32(®s->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_ACRB); ++ /* Notify the PSC that a reset has occurred */ ++ out_be32(®s->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_ACRB); + +- /* Re-enable RX and TX */ +- out_8(®s->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); +- +- mutex_unlock(&psc_dma->mutex); ++ /* Re-enable RX and TX */ ++ out_8(®s->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); ++ } + + usleep_range(1000, 2000); + psc_ac97_warm_reset(ac97); +@@ -286,7 +276,7 @@ static int psc_ac97_of_probe(struct platform_device *op) + return rc; + } + +- rc = snd_soc_register_component(&op->dev, &psc_ac97_component, ++ rc = devm_snd_soc_register_component(&op->dev, &psc_ac97_component, + psc_ac97_dai, ARRAY_SIZE(psc_ac97_dai)); + if (rc != 0) { + dev_err(&op->dev, "Failed to register DAI\n"); +@@ -312,8 +302,6 @@ static int psc_ac97_of_probe(struct platform_device *op) + static void psc_ac97_of_remove(struct platform_device *op) + { + mpc5200_audio_dma_destroy(op); +- snd_soc_unregister_component(&op->dev); +- snd_soc_set_ac97_ops(NULL); + } + + /* Match table for of_platform binding */ +diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c +--- a/sound/soc/fsl/mpc5200_psc_i2s.c ++++ b/sound/soc/fsl/mpc5200_psc_i2s.c +@@ -79,9 +79,12 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream, + * and we don't care about the frequency. Return an error if the direction + * is not SND_SOC_CLOCK_IN. + * ++ * @cpu_dai: DAI runtime data pointer + * @clk_id: reserved, should be zero + * @freq: the frequency of the given clock ID, currently ignored + * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master) ++ * ++ * Returns: %0 on success or %-EINVAL on failure. + */ + static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +@@ -101,7 +104,10 @@ static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, + * This driver only supports I2S mode. Return an error if the format is + * not SND_SOC_DAIFMT_I2S. + * ++ * @cpu_dai: DAI runtime data pointer + * @format: one of SND_SOC_DAIFMT_xxx ++ * ++ * Returns: %0 on success or %-EINVAL on failure. + */ + static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) + { +@@ -119,7 +125,7 @@ static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) + */ + + /** +- * psc_i2s_dai_template: template CPU Digital Audio Interface ++ * var psc_i2s_dai_ops - template CPU Digital Audio Interface + */ + static const struct snd_soc_dai_ops psc_i2s_dai_ops = { + .hw_params = psc_i2s_hw_params, +@@ -166,7 +172,7 @@ static int psc_i2s_of_probe(struct platform_device *op) + if (rc != 0) + return rc; + +- rc = snd_soc_register_component(&op->dev, &psc_i2s_component, ++ rc = devm_snd_soc_register_component(&op->dev, &psc_i2s_component, + psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai)); + if (rc != 0) { + pr_err("Failed to register DAI\n"); +@@ -213,7 +219,6 @@ static int psc_i2s_of_probe(struct platform_device *op) + static void psc_i2s_of_remove(struct platform_device *op) + { + mpc5200_audio_dma_destroy(op); +- snd_soc_unregister_component(&op->dev); + } + + /* Match table for of_platform binding */ +diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c +--- a/sound/soc/fsl/p1022_ds.c ++++ b/sound/soc/fsl/p1022_ds.c +@@ -73,6 +73,8 @@ struct machine_data { + char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */ + }; + ++#define card_to_mdata(_card) container_of(_card, struct machine_data, card) ++ + /** + * p1022_ds_machine_probe: initialize the board + * +@@ -82,8 +84,7 @@ struct machine_data { + */ + static int p1022_ds_machine_probe(struct snd_soc_card *card) + { +- struct machine_data *mdata = +- container_of(card, struct machine_data, card); ++ struct machine_data *mdata = card_to_mdata(card); + struct ccsr_guts __iomem *guts; + + guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); +@@ -122,8 +123,7 @@ static int p1022_ds_machine_probe(struct snd_soc_card *card) + static int p1022_ds_startup(struct snd_pcm_substream *substream) + { + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); +- struct machine_data *mdata = +- container_of(rtd->card, struct machine_data, card); ++ struct machine_data *mdata = card_to_mdata(rtd->card); + struct device *dev = rtd->card->dev; + int ret = 0; + +@@ -156,8 +156,7 @@ static int p1022_ds_startup(struct snd_pcm_substream *substream) + */ + static int p1022_ds_machine_remove(struct snd_soc_card *card) + { +- struct machine_data *mdata = +- container_of(card, struct machine_data, card); ++ struct machine_data *mdata = card_to_mdata(card); + struct ccsr_guts __iomem *guts; + + guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); +@@ -399,8 +398,7 @@ static int p1022_ds_probe(struct platform_device *pdev) + static void p1022_ds_remove(struct platform_device *pdev) + { + struct snd_soc_card *card = platform_get_drvdata(pdev); +- struct machine_data *mdata = +- container_of(card, struct machine_data, card); ++ struct machine_data *mdata = card_to_mdata(card); + + snd_soc_unregister_card(card); + kfree(mdata); +diff --git a/sound/soc/fsl/p1022_rdk.c b/sound/soc/fsl/p1022_rdk.c +--- a/sound/soc/fsl/p1022_rdk.c ++++ b/sound/soc/fsl/p1022_rdk.c +@@ -79,6 +79,8 @@ struct machine_data { + char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */ + }; + ++#define card_to_mdata(_card) container_of(_card, struct machine_data, card) ++ + /** + * p1022_rdk_machine_probe - initialize the board + * @card: ASoC card instance +@@ -91,8 +93,7 @@ struct machine_data { + */ + static int p1022_rdk_machine_probe(struct snd_soc_card *card) + { +- struct machine_data *mdata = +- container_of(card, struct machine_data, card); ++ struct machine_data *mdata = card_to_mdata(card); + struct ccsr_guts __iomem *guts; + + guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); +@@ -134,8 +135,7 @@ static int p1022_rdk_machine_probe(struct snd_soc_card *card) + static int p1022_rdk_startup(struct snd_pcm_substream *substream) + { + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); +- struct machine_data *mdata = +- container_of(rtd->card, struct machine_data, card); ++ struct machine_data *mdata = card_to_mdata(rtd->card); + struct device *dev = rtd->card->dev; + int ret = 0; + +@@ -169,8 +169,7 @@ static int p1022_rdk_startup(struct snd_pcm_substream *substream) + */ + static int p1022_rdk_machine_remove(struct snd_soc_card *card) + { +- struct machine_data *mdata = +- container_of(card, struct machine_data, card); ++ struct machine_data *mdata = card_to_mdata(card); + struct ccsr_guts __iomem *guts; + + guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); +@@ -361,8 +360,7 @@ static int p1022_rdk_probe(struct platform_device *pdev) + static void p1022_rdk_remove(struct platform_device *pdev) + { + struct snd_soc_card *card = platform_get_drvdata(pdev); +- struct machine_data *mdata = +- container_of(card, struct machine_data, card); ++ struct machine_data *mdata = card_to_mdata(card); + + snd_soc_unregister_card(card); + kfree(mdata); +diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c +--- a/sound/soc/generic/audio-graph-card.c ++++ b/sound/soc/generic/audio-graph-card.c +@@ -579,11 +579,11 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) + goto end; + } + +- ret = simple_util_parse_widgets(card, NULL); ++ ret = simple_util_parse_widgets(priv, NULL); + if (ret < 0) + goto end; + +- ret = simple_util_parse_routing(card, NULL); ++ ret = simple_util_parse_routing(priv, NULL); + if (ret < 0) + goto end; + +@@ -594,6 +594,7 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) + if (ret < 0) + goto err; + ++ /* Card name should be set after graph_for_each_link() */ + ret = simple_util_parse_card_name(priv, NULL); + if (ret < 0) + goto err; +@@ -603,14 +604,13 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) + simple_util_debug_info(priv); + + ret = devm_snd_soc_register_card(dev, card); +- if (ret < 0) +- goto err; +- +- return 0; + err: +- simple_util_clean_reference(card); ++ if (ret < 0) { ++ simple_util_clean_reference(priv); ++ return dev_err_probe(dev, ret, "parse error\n"); ++ } + end: +- return dev_err_probe(dev, ret, "parse error\n"); ++ return graph_ret(priv, ret); + } + EXPORT_SYMBOL_GPL(audio_graph_parse_of); + +diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c +--- a/sound/soc/generic/audio-graph-card2.c ++++ b/sound/soc/generic/audio-graph-card2.c +@@ -1303,11 +1303,11 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, + struct graph2_custom_hooks *hooks) + { + struct snd_soc_card *card = simple_priv_to_card(priv); +- int ret; ++ int ret = -ENOMEM; + + struct link_info *li __free(kfree) = kzalloc_obj(*li); + if (!li) +- return -ENOMEM; ++ goto end; + + card->probe = graph_util_card_probe; + card->owner = THIS_MODULE; +@@ -1316,31 +1316,35 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, + if ((hooks) && (hooks)->hook_pre) { + ret = (hooks)->hook_pre(priv); + if (ret < 0) +- goto err; ++ goto end; + } + + ret = graph_for_each_link(priv, hooks, li, graph_count); + if (!li->link) + ret = -EINVAL; + if (ret < 0) +- goto err; ++ goto end; + + ret = simple_util_init_priv(priv, li); + if (ret < 0) +- goto err; ++ goto end; + + priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW); + if (IS_ERR(priv->pa_gpio)) { + ret = PTR_ERR(priv->pa_gpio); + dev_err(dev, "failed to get amplifier gpio: %d\n", ret); +- goto err; ++ goto end; + } + +- ret = simple_util_parse_widgets(card, NULL); ++ ret = simple_util_parse_widgets(priv, NULL); + if (ret < 0) +- goto err; ++ goto end; + +- ret = simple_util_parse_routing(card, NULL); ++ ret = simple_util_parse_routing(priv, NULL); ++ if (ret < 0) ++ goto end; ++ ++ ret = simple_util_parse_aux_devs(priv, NULL); + if (ret < 0) + goto err; + +@@ -1349,6 +1353,7 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, + if (ret < 0) + goto err; + ++ /* Card name should be set after graph_for_each_link() */ + ret = simple_util_parse_card_name(priv, NULL); + if (ret < 0) + goto err; +@@ -1363,15 +1368,13 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, + + simple_util_debug_info(priv); + +- ret = snd_soc_of_parse_aux_devs(card, "aux-devs"); +- if (ret < 0) +- goto err; +- + ret = devm_snd_soc_register_card(dev, card); + err: +- if (ret < 0) +- dev_err_probe(dev, ret, "parse error\n"); +- ++ if (ret < 0) { ++ simple_util_clean_reference(priv); ++ return dev_err_probe(dev, ret, "parse error\n"); ++ } ++end: + return graph_ret(priv, ret); + } + EXPORT_SYMBOL_GPL(audio_graph2_parse_of); +diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c +--- a/sound/soc/generic/simple-card-utils.c ++++ b/sound/soc/generic/simple-card-utils.c +@@ -216,6 +216,27 @@ int simple_util_set_dailink_name(struct simple_util_priv *priv, + } + EXPORT_SYMBOL_GPL(simple_util_set_dailink_name); + ++int simple_util_parse_property(struct simple_util_priv *priv, ++ int (*func)(struct snd_soc_card *card, const char *propname), ++ char *prefix, char *property) ++{ ++ struct snd_soc_card *card = simple_priv_to_card(priv); ++ struct device_node *node = card->dev->of_node; ++ char prop[128]; ++ ++ if (!prefix) ++ prefix = ""; ++ ++ snprintf(prop, sizeof(prop), "%s%s", prefix, property); ++ ++ /* no property is not error */ ++ if (!of_property_present(node, prop)) ++ return 0; ++ ++ return func(card, prop); ++} ++EXPORT_SYMBOL_GPL(simple_util_parse_property); ++ + int simple_util_parse_card_name(struct simple_util_priv *priv, + char *prefix) + { +@@ -731,11 +752,12 @@ void simple_util_canonicalize_cpu(struct snd_soc_dai_link_component *cpus, + } + EXPORT_SYMBOL_GPL(simple_util_canonicalize_cpu); + +-void simple_util_clean_reference(struct snd_soc_card *card) ++void simple_util_clean_reference(struct simple_util_priv *priv) + { + struct snd_soc_dai_link *dai_link; + struct snd_soc_dai_link_component *cpu; + struct snd_soc_dai_link_component *codec; ++ struct snd_soc_card *card = simple_priv_to_card(priv); + int i, j; + + for_each_card_prelinks(card, i, dai_link) { +@@ -747,57 +769,6 @@ void simple_util_clean_reference(struct snd_soc_card *card) + } + EXPORT_SYMBOL_GPL(simple_util_clean_reference); + +-int simple_util_parse_routing(struct snd_soc_card *card, +- char *prefix) +-{ +- struct device_node *node = card->dev->of_node; +- char prop[128]; +- +- if (!prefix) +- prefix = ""; +- +- snprintf(prop, sizeof(prop), "%s%s", prefix, "routing"); +- +- if (!of_property_present(node, prop)) +- return 0; +- +- return snd_soc_of_parse_audio_routing(card, prop); +-} +-EXPORT_SYMBOL_GPL(simple_util_parse_routing); +- +-int simple_util_parse_widgets(struct snd_soc_card *card, +- char *prefix) +-{ +- struct device_node *node = card->dev->of_node; +- char prop[128]; +- +- if (!prefix) +- prefix = ""; +- +- snprintf(prop, sizeof(prop), "%s%s", prefix, "widgets"); +- +- if (of_property_present(node, prop)) +- return snd_soc_of_parse_audio_simple_widgets(card, prop); +- +- /* no widgets is not error */ +- return 0; +-} +-EXPORT_SYMBOL_GPL(simple_util_parse_widgets); +- +-int simple_util_parse_pin_switches(struct snd_soc_card *card, +- char *prefix) +-{ +- char prop[128]; +- +- if (!prefix) +- prefix = ""; +- +- snprintf(prop, sizeof(prop), "%s%s", prefix, "pin-switches"); +- +- return snd_soc_of_parse_pin_switches(card, prop); +-} +-EXPORT_SYMBOL_GPL(simple_util_parse_pin_switches); +- + int simple_util_init_jack(struct snd_soc_card *card, + struct simple_util_jack *sjack, + int is_hp, char *prefix, +@@ -854,9 +825,9 @@ int simple_util_init_jack(struct snd_soc_card *card, + } + EXPORT_SYMBOL_GPL(simple_util_init_jack); + +-int simple_util_init_aux_jacks(struct simple_util_priv *priv, char *prefix) ++int simple_util_init_aux_jacks(struct snd_soc_card *card, char *prefix) + { +- struct snd_soc_card *card = simple_priv_to_card(priv); ++ struct simple_util_priv *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component; + int found_jack_index = 0; + int type = 0; +@@ -1026,8 +997,9 @@ EXPORT_SYMBOL_GPL(simple_util_init_priv); + void simple_util_remove(struct platform_device *pdev) + { + struct snd_soc_card *card = platform_get_drvdata(pdev); ++ struct simple_util_priv *priv = snd_soc_card_get_drvdata(card); + +- simple_util_clean_reference(card); ++ simple_util_clean_reference(priv); + } + EXPORT_SYMBOL_GPL(simple_util_remove); + +diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c +--- a/sound/soc/generic/simple-card.c ++++ b/sound/soc/generic/simple-card.c +@@ -517,44 +517,6 @@ static int simple_populate_aux(struct simple_util_priv *priv) + return simple_ret(priv, ret); + } + +-static int simple_parse_of(struct simple_util_priv *priv, struct link_info *li) +-{ +- struct snd_soc_card *card = simple_priv_to_card(priv); +- int ret; +- +- ret = simple_util_parse_widgets(card, PREFIX); +- if (ret < 0) +- goto end; +- +- ret = simple_util_parse_routing(card, PREFIX); +- if (ret < 0) +- goto end; +- +- ret = simple_util_parse_pin_switches(card, PREFIX); +- if (ret < 0) +- goto end; +- +- /* Single/Muti DAI link(s) & New style of DT node */ +- memset(li, 0, sizeof(*li)); +- ret = simple_for_each_link(priv, li, +- simple_dai_link_of, +- simple_dai_link_of_dpcm); +- if (ret < 0) +- goto end; +- +- ret = simple_util_parse_card_name(priv, PREFIX); +- if (ret < 0) +- goto end; +- +- ret = simple_populate_aux(priv); +- if (ret < 0) +- goto end; +- +- ret = snd_soc_of_parse_aux_devs(card, PREFIX "aux-devs"); +-end: +- return simple_ret(priv, ret); +-} +- + static int simple_count_noml(struct simple_util_priv *priv, + struct device_node *np, + struct device_node *codec, +@@ -699,28 +661,19 @@ static int simple_soc_probe(struct snd_soc_card *card) + if (ret < 0) + goto end; + +- ret = simple_util_init_aux_jacks(priv, PREFIX); ++ ret = simple_util_init_aux_jacks(card, PREFIX); + end: + return simple_ret(priv, ret); + } + +-static int simple_probe(struct platform_device *pdev) ++static int simple_parse_of(struct simple_util_priv *priv) + { +- struct simple_util_priv *priv; +- struct device *dev = &pdev->dev; +- struct snd_soc_card *card; +- int ret; ++ struct snd_soc_card *card = simple_priv_to_card(priv); ++ struct device *dev = card->dev; ++ int ret = -EINVAL; + +- /* Allocate the private data and the DAI link array */ +- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- card = simple_priv_to_card(priv); +- card->owner = THIS_MODULE; +- card->dev = dev; +- card->probe = simple_soc_probe; +- card->driver_name = "simple-card"; ++ if (!dev) ++ return simple_ret(priv, ret); + + ret = -ENOMEM; + struct link_info *li __free(kfree) = kzalloc_obj(*li); +@@ -739,25 +692,71 @@ static int simple_probe(struct platform_device *pdev) + if (ret < 0) + goto end; + +- ret = simple_parse_of(priv, li); +- if (ret < 0) { +- dev_err_probe(dev, ret, "parse error\n"); ++ ret = simple_util_parse_widgets(priv, PREFIX); ++ if (ret < 0) ++ goto end; ++ ++ ret = simple_util_parse_routing(priv, PREFIX); ++ if (ret < 0) ++ goto end; ++ ++ ret = simple_util_parse_pin_switches(priv, PREFIX); ++ if (ret < 0) ++ goto end; ++ ++ ret = simple_util_parse_aux_devs(priv, PREFIX); ++ if (ret < 0) ++ goto err; ++ ++ /* Single/Muti DAI link(s) & New style of DT node */ ++ memset(li, 0, sizeof(*li)); ++ ret = simple_for_each_link(priv, li, ++ simple_dai_link_of, ++ simple_dai_link_of_dpcm); ++ if (ret < 0) ++ goto err; ++ ++ /* Card name should be set after simple_for_each_link() */ ++ ret = simple_util_parse_card_name(priv, PREFIX); ++ if (ret < 0) ++ goto err; ++ ++ ret = simple_populate_aux(priv); ++ if (ret < 0) + goto err; +- } + + snd_soc_card_set_drvdata(card, priv); + + simple_util_debug_info(priv); + + ret = devm_snd_soc_register_card(dev, card); +- if (ret < 0) +- goto err; +- +- return 0; + err: +- simple_util_clean_reference(card); ++ if (ret < 0) { ++ simple_util_clean_reference(priv); ++ return dev_err_probe(dev, ret, "parse error\n"); ++ } + end: +- return dev_err_probe(dev, ret, "parse error\n"); ++ return simple_ret(priv, ret); ++} ++ ++static int simple_probe(struct platform_device *pdev) ++{ ++ struct simple_util_priv *priv; ++ struct device *dev = &pdev->dev; ++ struct snd_soc_card *card; ++ ++ /* Allocate the private data and the DAI link array */ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ card = simple_priv_to_card(priv); ++ card->owner = THIS_MODULE; ++ card->dev = dev; ++ card->probe = simple_soc_probe; ++ card->driver_name = "simple-card"; ++ ++ return simple_parse_of(priv); + } + + static const struct of_device_id simple_of_match[] = { +diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c +--- a/sound/soc/intel/atom/sst-atom-controls.c ++++ b/sound/soc/intel/atom/sst-atom-controls.c +@@ -14,6 +14,7 @@ + */ + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + ++#include + #include + #include + #include +@@ -73,14 +74,10 @@ static int sst_fill_and_send_cmd(struct sst_data *drv, + u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id, + void *cmd_data, u16 len) + { +- int ret; ++ guard(mutex)(&drv->lock); + +- mutex_lock(&drv->lock); +- ret = sst_fill_and_send_cmd_unlocked(drv, ipc_msg, block, +- task_id, pipe_id, cmd_data, len); +- mutex_unlock(&drv->lock); +- +- return ret; ++ return sst_fill_and_send_cmd_unlocked(drv, ipc_msg, block, ++ task_id, pipe_id, cmd_data, len); + } + + /* +@@ -167,7 +164,7 @@ static int sst_slot_get(struct snd_kcontrol *kcontrol, + unsigned int val, mux; + u8 *map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map; + +- mutex_lock(&drv->lock); ++ guard(mutex)(&drv->lock); + val = 1 << ctl_no; + /* search which slot/channel has this bit set - there should be only one */ + for (mux = e->max; mux > 0; mux--) +@@ -175,7 +172,6 @@ static int sst_slot_get(struct snd_kcontrol *kcontrol, + break; + + ucontrol->value.enumerated.item[0] = mux; +- mutex_unlock(&drv->lock); + + dev_dbg(c->dev, "%s - %s map = %#x\n", + is_tx ? "tx channel" : "rx slot", +@@ -235,7 +231,7 @@ static int sst_slot_put(struct snd_kcontrol *kcontrol, + if (mux > e->max - 1) + return -EINVAL; + +- mutex_lock(&drv->lock); ++ guard(mutex)(&drv->lock); + /* first clear all registers of this bit */ + for (i = 0; i < e->max; i++) + map[i] &= ~val; +@@ -244,7 +240,6 @@ static int sst_slot_put(struct snd_kcontrol *kcontrol, + /* kctl set to 'none' and we reset the bits so send IPC */ + ret = sst_check_and_send_slot_map(drv, kcontrol); + +- mutex_unlock(&drv->lock); + return ret; + } + +@@ -258,7 +253,6 @@ static int sst_slot_put(struct snd_kcontrol *kcontrol, + + ret = sst_check_and_send_slot_map(drv, kcontrol); + +- mutex_unlock(&drv->lock); + return ret; + } + +@@ -354,13 +348,12 @@ static int sst_algo_control_set(struct snd_kcontrol *kcontrol, + struct sst_algo_control *bc = (void *)kcontrol->private_value; + + dev_dbg(cmpnt->dev, "control_name=%s\n", kcontrol->id.name); +- mutex_lock(&drv->lock); ++ guard(mutex)(&drv->lock); + switch (bc->type) { + case SST_ALGO_PARAMS: + memcpy(bc->params, ucontrol->value.bytes.data, bc->max); + break; + default: +- mutex_unlock(&drv->lock); + dev_err(cmpnt->dev, "Invalid Input- algo type:%d\n", + bc->type); + return -EINVAL; +@@ -368,7 +361,6 @@ static int sst_algo_control_set(struct snd_kcontrol *kcontrol, + /*if pipe is enabled, need to send the algo params from here*/ + if (bc->w && bc->w->power) + ret = sst_send_algo_cmd(drv, bc); +- mutex_unlock(&drv->lock); + + return ret; + } +@@ -475,7 +467,7 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, + struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value; + struct sst_gain_value *gv = mc->gain_val; + +- mutex_lock(&drv->lock); ++ guard(mutex)(&drv->lock); + + switch (mc->type) { + case SST_GAIN_TLV: +@@ -497,7 +489,6 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, + break; + + default: +- mutex_unlock(&drv->lock); + dev_err(cmpnt->dev, "Invalid Input- gain type:%d\n", + mc->type); + return -EINVAL; +@@ -506,7 +497,6 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, + if (mc->w && mc->w->power) + ret = sst_send_gain_cmd(drv, gv, mc->task_id, + mc->pipe_id | mc->instance_id, mc->module_id, 0); +- mutex_unlock(&drv->lock); + + return ret; + } +@@ -521,10 +511,9 @@ static int sst_send_pipe_module_params(struct snd_soc_dapm_widget *w, + struct sst_data *drv = snd_soc_component_get_drvdata(c); + struct sst_ids *ids = w->priv; + +- mutex_lock(&drv->lock); ++ guard(mutex)(&drv->lock); + sst_find_and_send_pipe_algo(drv, w->name, ids); + sst_set_pipe_gain(ids, drv, 0); +- mutex_unlock(&drv->lock); + + return 0; + } +@@ -761,27 +750,29 @@ int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable) + return ret; + } + +- mutex_lock(&drv->lock); +- if (enable) +- timer_usage++; +- else +- timer_usage--; +- +- /* +- * Send the command only if this call is the first enable or last +- * disable +- */ +- if ((enable && (timer_usage == 1)) || +- (!enable && (timer_usage == 0))) { +- ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD, +- SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd, +- sizeof(cmd.header) + cmd.header.length); +- if (ret && enable) { ++ scoped_guard(mutex, &drv->lock) { ++ if (enable) ++ timer_usage++; ++ else + timer_usage--; +- enable = false; ++ ++ /* ++ * Send the command only if this call is the first enable or last ++ * disable ++ */ ++ if ((enable && timer_usage == 1) || ++ (!enable && timer_usage == 0)) { ++ ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD, ++ SST_FLAG_BLOCKED, ++ SST_TASK_SBA, 0, &cmd, ++ sizeof(cmd.header) + ++ cmd.header.length); ++ if (ret && enable) { ++ timer_usage--; ++ enable = false; ++ } + } + } +- mutex_unlock(&drv->lock); + + if (!enable) + sst->ops->power(sst->dev, false); +diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c +--- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c ++++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c +@@ -11,6 +11,7 @@ + */ + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + ++#include + #include + #include + #include +@@ -32,7 +33,7 @@ int sst_register_dsp(struct sst_device *dev) + return -EINVAL; + if (!try_module_get(dev->dev->driver->owner)) + return -ENODEV; +- mutex_lock(&sst_lock); ++ guard(mutex)(&sst_lock); + if (sst) { + dev_err(dev->dev, "we already have a device %s\n", sst->name); + module_put(dev->dev->driver->owner); +@@ -41,7 +42,7 @@ int sst_register_dsp(struct sst_device *dev) + } + dev_dbg(dev->dev, "registering device %s\n", dev->name); + sst = dev; +- mutex_unlock(&sst_lock); ++ + return 0; + } + EXPORT_SYMBOL_GPL(sst_register_dsp); +@@ -53,17 +54,15 @@ int sst_unregister_dsp(struct sst_device *dev) + if (dev != sst) + return -EINVAL; + +- mutex_lock(&sst_lock); ++ guard(mutex)(&sst_lock); + +- if (!sst) { +- mutex_unlock(&sst_lock); ++ if (!sst) + return -EIO; +- } + + module_put(sst->dev->driver->owner); + dev_dbg(dev->dev, "unreg %s\n", sst->name); + sst = NULL; +- mutex_unlock(&sst_lock); ++ + return 0; + } + EXPORT_SYMBOL_GPL(sst_unregister_dsp); +@@ -103,21 +102,14 @@ static int sst_media_digital_mute(struct snd_soc_dai *dai, int mute, int stream) + void sst_set_stream_status(struct sst_runtime_stream *stream, + int state) + { +- unsigned long flags; +- spin_lock_irqsave(&stream->status_lock, flags); ++ guard(spinlock_irqsave)(&stream->status_lock); + stream->stream_status = state; +- spin_unlock_irqrestore(&stream->status_lock, flags); + } + + static inline int sst_get_stream_status(struct sst_runtime_stream *stream) + { +- int state; +- unsigned long flags; +- +- spin_lock_irqsave(&stream->status_lock, flags); +- state = stream->stream_status; +- spin_unlock_irqrestore(&stream->status_lock, flags); +- return state; ++ guard(spinlock_irqsave)(&stream->status_lock); ++ return stream->stream_status; + } + + static void sst_fill_alloc_params(struct snd_pcm_substream *substream, +@@ -244,12 +236,7 @@ static int sst_platform_alloc_stream(struct snd_pcm_substream *substream, + + stream->stream_info.str_id = str_params.stream_id; + +- ret_val = stream->ops->open(sst->dev, &str_params); +- if (ret_val <= 0) +- return ret_val; +- +- +- return ret_val; ++ return stream->ops->open(sst->dev, &str_params); + } + + static void sst_period_elapsed(void *arg) +@@ -304,7 +291,7 @@ static int sst_media_open(struct snd_pcm_substream *substream, + { + int ret_val = 0; + struct snd_pcm_runtime *runtime = substream->runtime; +- struct sst_runtime_stream *stream; ++ struct sst_runtime_stream *stream __free(kfree) = NULL; + + stream = kzalloc_obj(*stream); + if (!stream) +@@ -312,15 +299,14 @@ static int sst_media_open(struct snd_pcm_substream *substream, + spin_lock_init(&stream->status_lock); + + /* get the sst ops */ +- mutex_lock(&sst_lock); +- if (!sst || +- !try_module_get(sst->dev->driver->owner)) { +- dev_err(dai->dev, "no device available to run\n"); +- ret_val = -ENODEV; +- goto out_ops; ++ scoped_guard(mutex, &sst_lock) { ++ if (!sst || ++ !try_module_get(sst->dev->driver->owner)) { ++ dev_err(dai->dev, "no device available to run\n"); ++ return -ENODEV; ++ } ++ stream->ops = sst->ops; + } +- stream->ops = sst->ops; +- mutex_unlock(&sst_lock); + + stream->stream_info.str_id = 0; + +@@ -330,7 +316,7 @@ static int sst_media_open(struct snd_pcm_substream *substream, + + ret_val = power_up_sst(stream); + if (ret_val < 0) +- goto out_power_up; ++ return ret_val; + + /* + * Make sure the period to be multiple of 1ms to align the +@@ -347,12 +333,14 @@ static int sst_media_open(struct snd_pcm_substream *substream, + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIODS, 2); + +- return snd_pcm_hw_constraint_integer(runtime, +- SNDRV_PCM_HW_PARAM_PERIODS); +-out_ops: +- mutex_unlock(&sst_lock); +-out_power_up: +- kfree(stream); ++ ret_val = snd_pcm_hw_constraint_integer(runtime, ++ SNDRV_PCM_HW_PARAM_PERIODS); ++ ++ if (ret_val < 0) ++ return ret_val; ++ ++ stream = NULL; ++ + return ret_val; + } + +diff --git a/sound/soc/intel/atom/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c +--- a/sound/soc/intel/atom/sst/sst_ipc.c ++++ b/sound/soc/intel/atom/sst/sst_ipc.c +@@ -11,6 +11,7 @@ + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ ++#include + #include + #include + #include +@@ -180,9 +181,8 @@ void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx) + union interrupt_reg_mrfld isr; + union interrupt_reg_mrfld imr; + union ipc_header_mrfld clear_ipc; +- unsigned long irq_flags; + +- spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags); ++ guard(spinlock_irqsave)(&sst_drv_ctx->ipc_spin_lock); + imr.full = sst_shim_read64(sst_drv_ctx->shim, SST_IMRX); + isr.full = sst_shim_read64(sst_drv_ctx->shim, SST_ISRX); + +@@ -200,7 +200,6 @@ void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx) + /* un mask busy interrupt */ + imr.part.busy_interrupt = 0; + sst_shim_write64(sst_drv_ctx->shim, SST_IMRX, imr.full); +- spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); + } + + +diff --git a/sound/soc/intel/atom/sst/sst_pvt.c b/sound/soc/intel/atom/sst/sst_pvt.c +--- a/sound/soc/intel/atom/sst/sst_pvt.c ++++ b/sound/soc/intel/atom/sst/sst_pvt.c +@@ -11,6 +11,7 @@ + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ ++#include + #include + #include + #include +@@ -64,9 +65,8 @@ u64 sst_shim_read64(void __iomem *addr, int offset) + void sst_set_fw_state_locked( + struct intel_sst_drv *sst_drv_ctx, int sst_state) + { +- mutex_lock(&sst_drv_ctx->sst_lock); ++ guard(mutex)(&sst_drv_ctx->sst_lock); + sst_drv_ctx->sst_state = sst_state; +- mutex_unlock(&sst_drv_ctx->sst_lock); + } + + /* +@@ -302,18 +302,17 @@ int sst_assign_pvt_id(struct intel_sst_drv *drv) + { + int local; + +- spin_lock(&drv->block_lock); ++ guard(spinlock)(&drv->block_lock); + /* find first zero index from lsb */ + local = ffz(drv->pvt_id); + dev_dbg(drv->dev, "pvt_id assigned --> %d\n", local); + if (local >= SST_MAX_BLOCKS){ +- spin_unlock(&drv->block_lock); + dev_err(drv->dev, "PVT _ID error: no free id blocks "); + return -EINVAL; + } + /* toggle the index */ + change_bit(local, &drv->pvt_id); +- spin_unlock(&drv->block_lock); ++ + return local; + } + +diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c +--- a/sound/soc/intel/avs/apl.c ++++ b/sound/soc/intel/avs/apl.c +@@ -6,6 +6,7 @@ + // Amadeusz Slawinski + // + ++#include + #include + #include + #include +@@ -190,7 +191,7 @@ static bool avs_apl_lp_streaming(struct avs_dev *adev) + { + struct avs_path *path; + +- spin_lock(&adev->path_list_lock); ++ guard(spinlock)(&adev->path_list_lock); + /* Any gateway without buffer allocated in LP area disqualifies D0IX. */ + list_for_each_entry(path, &adev->path_list, node) { + struct avs_path_pipeline *ppl; +@@ -210,14 +211,11 @@ static bool avs_apl_lp_streaming(struct avs_dev *adev) + if (cfg->copier.dma_type == INVALID_OBJECT_ID) + continue; + +- if (!mod->gtw_attrs.lp_buffer_alloc) { +- spin_unlock(&adev->path_list_lock); ++ if (!mod->gtw_attrs.lp_buffer_alloc) + return false; +- } + } + } + } +- spin_unlock(&adev->path_list_lock); + + return true; + } +diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h +--- a/sound/soc/intel/avs/avs.h ++++ b/sound/soc/intel/avs/avs.h +@@ -336,14 +336,13 @@ int avs_icl_load_basefw(struct avs_dev *adev, struct firmware *fw); + /* Soc component members */ + + struct avs_soc_component { +- struct snd_soc_component base; ++ struct snd_soc_component *base; + struct avs_tplg *tplg; + + struct list_head node; + }; + +-#define to_avs_soc_component(comp) \ +- container_of(comp, struct avs_soc_component, base) ++#define to_avs_soc_component(comp) snd_soc_component_to_priv(comp) + + extern const struct snd_soc_dai_ops avs_dai_fe_ops; + +diff --git a/sound/soc/intel/avs/boards/da7219.c b/sound/soc/intel/avs/boards/da7219.c +--- a/sound/soc/intel/avs/boards/da7219.c ++++ b/sound/soc/intel/avs/boards/da7219.c +@@ -175,7 +175,6 @@ static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot, + if (!dl || !platform) + return -ENOMEM; + +- dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port); + dl->name = devm_kasprintf(dev, GFP_KERNEL, + AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot)); + dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL); +@@ -259,10 +258,8 @@ static int avs_da7219_probe(struct platform_device *pdev) + } + + static const struct platform_device_id avs_da7219_driver_ids[] = { +- { +- .name = "avs_da7219", +- }, +- {}, ++ { .name = "avs_da7219" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_da7219_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/dmic.c b/sound/soc/intel/avs/boards/dmic.c +--- a/sound/soc/intel/avs/boards/dmic.c ++++ b/sound/soc/intel/avs/boards/dmic.c +@@ -104,10 +104,8 @@ static int avs_dmic_probe(struct platform_device *pdev) + } + + static const struct platform_device_id avs_dmic_driver_ids[] = { +- { +- .name = "avs_dmic", +- }, +- {}, ++ { .name = "avs_dmic" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_dmic_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/es8336.c b/sound/soc/intel/avs/boards/es8336.c +--- a/sound/soc/intel/avs/boards/es8336.c ++++ b/sound/soc/intel/avs/boards/es8336.c +@@ -309,10 +309,8 @@ static int avs_es8336_probe(struct platform_device *pdev) + } + + static const struct platform_device_id avs_es8336_driver_ids[] = { +- { +- .name = "avs_es8336", +- }, +- {}, ++ { .name = "avs_es8336" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_es8336_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/hdaudio.c b/sound/soc/intel/avs/boards/hdaudio.c +--- a/sound/soc/intel/avs/boards/hdaudio.c ++++ b/sound/soc/intel/avs/boards/hdaudio.c +@@ -231,10 +231,8 @@ static int avs_hdaudio_probe(struct platform_device *pdev) + } + + static const struct platform_device_id avs_hdaudio_driver_ids[] = { +- { +- .name = "avs_hdaudio", +- }, +- {}, ++ { .name = "avs_hdaudio" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_hdaudio_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/i2s_test.c b/sound/soc/intel/avs/boards/i2s_test.c +--- a/sound/soc/intel/avs/boards/i2s_test.c ++++ b/sound/soc/intel/avs/boards/i2s_test.c +@@ -107,10 +107,8 @@ static int avs_i2s_test_probe(struct platform_device *pdev) + } + + static const struct platform_device_id avs_i2s_test_driver_ids[] = { +- { +- .name = "avs_i2s_test", +- }, +- {}, ++ { .name = "avs_i2s_test" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_i2s_test_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/max98357a.c b/sound/soc/intel/avs/boards/max98357a.c +--- a/sound/soc/intel/avs/boards/max98357a.c ++++ b/sound/soc/intel/avs/boards/max98357a.c +@@ -136,10 +136,8 @@ static int avs_max98357a_probe(struct platform_device *pdev) + } + + static const struct platform_device_id avs_max98357a_driver_ids[] = { +- { +- .name = "avs_max98357a", +- }, +- {}, ++ { .name = "avs_max98357a" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_max98357a_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/max98373.c b/sound/soc/intel/avs/boards/max98373.c +--- a/sound/soc/intel/avs/boards/max98373.c ++++ b/sound/soc/intel/avs/boards/max98373.c +@@ -191,10 +191,8 @@ static int avs_max98373_probe(struct platform_device *pdev) + } + + static const struct platform_device_id avs_max98373_driver_ids[] = { +- { +- .name = "avs_max98373", +- }, +- {}, ++ { .name = "avs_max98373" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_max98373_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/max98927.c b/sound/soc/intel/avs/boards/max98927.c +--- a/sound/soc/intel/avs/boards/max98927.c ++++ b/sound/soc/intel/avs/boards/max98927.c +@@ -188,10 +188,8 @@ static int avs_max98927_probe(struct platform_device *pdev) + } + + static const struct platform_device_id avs_max98927_driver_ids[] = { +- { +- .name = "avs_max98927", +- }, +- {}, ++ { .name = "avs_max98927" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_max98927_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/nau8825.c b/sound/soc/intel/avs/boards/nau8825.c +--- a/sound/soc/intel/avs/boards/nau8825.c ++++ b/sound/soc/intel/avs/boards/nau8825.c +@@ -293,10 +293,8 @@ static int avs_nau8825_probe(struct platform_device *pdev) + } + + static const struct platform_device_id avs_nau8825_driver_ids[] = { +- { +- .name = "avs_nau8825", +- }, +- {}, ++ { .name = "avs_nau8825" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_nau8825_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/pcm3168a.c b/sound/soc/intel/avs/boards/pcm3168a.c +--- a/sound/soc/intel/avs/boards/pcm3168a.c ++++ b/sound/soc/intel/avs/boards/pcm3168a.c +@@ -132,10 +132,8 @@ static int avs_pcm3168a_probe(struct platform_device *pdev) + } + + static const struct platform_device_id avs_pcm3168a_driver_ids[] = { +- { +- .name = "avs_pcm3168a", +- }, +- {}, ++ { .name = "avs_pcm3168a" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_pcm3168a_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/probe.c b/sound/soc/intel/avs/boards/probe.c +--- a/sound/soc/intel/avs/boards/probe.c ++++ b/sound/soc/intel/avs/boards/probe.c +@@ -64,7 +64,7 @@ static const struct platform_device_id avs_probe_mb_driver_ids[] = { + { + .name = "avs_probe_mb", + }, +- {}, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_probe_mb_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/rt274.c b/sound/soc/intel/avs/boards/rt274.c +--- a/sound/soc/intel/avs/boards/rt274.c ++++ b/sound/soc/intel/avs/boards/rt274.c +@@ -261,7 +261,7 @@ static const struct platform_device_id avs_rt274_driver_ids[] = { + { + .name = "avs_rt274", + }, +- {}, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_rt274_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/rt286.c b/sound/soc/intel/avs/boards/rt286.c +--- a/sound/soc/intel/avs/boards/rt286.c ++++ b/sound/soc/intel/avs/boards/rt286.c +@@ -231,7 +231,7 @@ static const struct platform_device_id avs_rt286_driver_ids[] = { + { + .name = "avs_rt286", + }, +- {}, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_rt286_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/rt298.c b/sound/soc/intel/avs/boards/rt298.c +--- a/sound/soc/intel/avs/boards/rt298.c ++++ b/sound/soc/intel/avs/boards/rt298.c +@@ -250,7 +250,7 @@ static const struct platform_device_id avs_rt298_driver_ids[] = { + { + .name = "avs_rt298", + }, +- {}, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_rt298_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/rt5514.c b/sound/soc/intel/avs/boards/rt5514.c +--- a/sound/soc/intel/avs/boards/rt5514.c ++++ b/sound/soc/intel/avs/boards/rt5514.c +@@ -178,7 +178,7 @@ static const struct platform_device_id avs_rt5514_driver_ids[] = { + { + .name = "avs_rt5514", + }, +- {}, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_rt5514_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/rt5640.c b/sound/soc/intel/avs/boards/rt5640.c +--- a/sound/soc/intel/avs/boards/rt5640.c ++++ b/sound/soc/intel/avs/boards/rt5640.c +@@ -252,7 +252,7 @@ static const struct platform_device_id avs_rt5640_driver_ids[] = { + { + .name = "avs_rt5640", + }, +- {}, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_rt5640_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/rt5663.c b/sound/soc/intel/avs/boards/rt5663.c +--- a/sound/soc/intel/avs/boards/rt5663.c ++++ b/sound/soc/intel/avs/boards/rt5663.c +@@ -249,7 +249,7 @@ static const struct platform_device_id avs_rt5663_driver_ids[] = { + { + .name = "avs_rt5663", + }, +- {}, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_rt5663_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/rt5682.c b/sound/soc/intel/avs/boards/rt5682.c +--- a/sound/soc/intel/avs/boards/rt5682.c ++++ b/sound/soc/intel/avs/boards/rt5682.c +@@ -325,7 +325,7 @@ static const struct platform_device_id avs_rt5682_driver_ids[] = { + { + .name = "avs_rt5682", + }, +- {}, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_rt5682_driver_ids); + +diff --git a/sound/soc/intel/avs/boards/ssm4567.c b/sound/soc/intel/avs/boards/ssm4567.c +--- a/sound/soc/intel/avs/boards/ssm4567.c ++++ b/sound/soc/intel/avs/boards/ssm4567.c +@@ -180,7 +180,7 @@ static const struct platform_device_id avs_ssm4567_driver_ids[] = { + { + .name = "avs_ssm4567", + }, +- {}, ++ { } + }; + MODULE_DEVICE_TABLE(platform, avs_ssm4567_driver_ids); + +diff --git a/sound/soc/intel/avs/control.c b/sound/soc/intel/avs/control.c +--- a/sound/soc/intel/avs/control.c ++++ b/sound/soc/intel/avs/control.c +@@ -27,7 +27,7 @@ static struct avs_path_module *avs_get_volume_module(struct avs_dev *adev, u32 i + struct avs_path_pipeline *ppl; + struct avs_path_module *mod; + +- spin_lock(&adev->path_list_lock); ++ guard(spinlock)(&adev->path_list_lock); + list_for_each_entry(path, &adev->path_list, node) { + list_for_each_entry(ppl, &path->ppl_list, node) { + list_for_each_entry(mod, &ppl->mod_list, node) { +@@ -35,14 +35,11 @@ static struct avs_path_module *avs_get_volume_module(struct avs_dev *adev, u32 i + + if ((guid_equal(type, &AVS_PEAKVOL_MOD_UUID) || + guid_equal(type, &AVS_GAIN_MOD_UUID)) && +- mod->template->ctl_id == id) { +- spin_unlock(&adev->path_list_lock); ++ mod->template->ctl_id == id) + return mod; +- } + } + } + } +- spin_unlock(&adev->path_list_lock); + + return NULL; + } +diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c +--- a/sound/soc/intel/avs/core.c ++++ b/sound/soc/intel/avs/core.c +@@ -14,6 +14,7 @@ + // foundation of this driver + // + ++#include + #include + #include + #include +@@ -273,7 +274,7 @@ static irqreturn_t avs_hda_interrupt(struct hdac_bus *bus) + if (snd_hdac_bus_handle_stream_irq(bus, status, hdac_update_stream)) + ret = IRQ_HANDLED; + +- spin_lock_irq(&bus->reg_lock); ++ guard(spinlock_irq)(&bus->reg_lock); + /* Clear RIRB interrupt. */ + status = snd_hdac_chip_readb(bus, RIRBSTS); + if (status & RIRB_INT_MASK) { +@@ -283,7 +284,6 @@ static irqreturn_t avs_hda_interrupt(struct hdac_bus *bus) + ret = IRQ_HANDLED; + } + +- spin_unlock_irq(&bus->reg_lock); + return ret; + } + +diff --git a/sound/soc/intel/avs/debug.h b/sound/soc/intel/avs/debug.h +--- a/sound/soc/intel/avs/debug.h ++++ b/sound/soc/intel/avs/debug.h +@@ -9,6 +9,7 @@ + #ifndef __SOUND_SOC_INTEL_AVS_DEBUG_H + #define __SOUND_SOC_INTEL_AVS_DEBUG_H + ++#include + #include "messages.h" + #include "registers.h" + +@@ -26,14 +27,9 @@ struct avs_dev; + + static inline int avs_log_buffer_status_locked(struct avs_dev *adev, union avs_notify_msg *msg) + { +- unsigned long flags; +- int ret; ++ guard(spinlock_irqsave)(&adev->trace_lock); + +- spin_lock_irqsave(&adev->trace_lock, flags); +- ret = avs_dsp_op(adev, log_buffer_status, msg); +- spin_unlock_irqrestore(&adev->trace_lock, flags); +- +- return ret; ++ return avs_dsp_op(adev, log_buffer_status, msg); + } + + struct avs_apl_log_buffer_layout { +diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c +--- a/sound/soc/intel/avs/debugfs.c ++++ b/sound/soc/intel/avs/debugfs.c +@@ -6,9 +6,9 @@ + // Amadeusz Slawinski + // + ++#include + #include + #include +-#include + #include + #include + #include +@@ -236,20 +236,15 @@ static int strace_open(struct inode *inode, struct file *file) + if (!try_module_get(adev->dev->driver->owner)) + return -ENODEV; + +- if (kfifo_initialized(&adev->trace_fifo)) { +- ret = -EBUSY; +- goto err; +- } ++ if (kfifo_initialized(&adev->trace_fifo)) ++ return -EBUSY; + + ret = kfifo_alloc(&adev->trace_fifo, PAGE_SIZE, GFP_KERNEL); + if (ret < 0) +- goto err; ++ return ret; + + file->private_data = adev; + return 0; +-err: +- module_put(adev->dev->driver->owner); +- return ret; + } + + static int strace_release(struct inode *inode, struct file *file) +@@ -257,24 +252,22 @@ static int strace_release(struct inode *inode, struct file *file) + union avs_notify_msg msg = AVS_NOTIFICATION(LOG_BUFFER_STATUS); + struct avs_dev *adev = file->private_data; + unsigned long resource_mask; +- unsigned long flags, i; ++ unsigned long i; + u32 num_cores; + + resource_mask = adev->logged_resources; + num_cores = adev->hw_cfg.dsp_cores; + +- spin_lock_irqsave(&adev->trace_lock, flags); ++ scoped_guard(spinlock_irqsave, &adev->trace_lock) { ++ /* Gather any remaining logs. */ ++ for_each_set_bit(i, &resource_mask, num_cores) { ++ msg.log.core = i; ++ avs_dsp_op(adev, log_buffer_status, &msg); ++ } + +- /* Gather any remaining logs. */ +- for_each_set_bit(i, &resource_mask, num_cores) { +- msg.log.core = i; +- avs_dsp_op(adev, log_buffer_status, &msg); ++ kfifo_free(&adev->trace_fifo); + } + +- kfifo_free(&adev->trace_fifo); +- +- spin_unlock_irqrestore(&adev->trace_lock, flags); +- + module_put(adev->dev->driver->owner); + return 0; + } +diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c +--- a/sound/soc/intel/avs/ipc.c ++++ b/sound/soc/intel/avs/ipc.c +@@ -6,6 +6,7 @@ + // Amadeusz Slawinski + // + ++#include + #include + #include + #include +@@ -99,39 +100,39 @@ static void avs_dsp_recovery(struct avs_dev *adev) + unsigned int core_mask; + int ret; + +- mutex_lock(&adev->comp_list_mutex); +- /* disconnect all running streams */ +- list_for_each_entry(acomp, &adev->comp_list, node) { +- struct snd_soc_pcm_runtime *rtd; +- struct snd_soc_card *card; ++ scoped_guard(mutex, &adev->comp_list_mutex) { ++ /* disconnect all running streams */ ++ list_for_each_entry(acomp, &adev->comp_list, node) { ++ struct snd_soc_pcm_runtime *rtd; ++ struct snd_soc_card *card; + +- card = acomp->base.card; +- if (!card) +- continue; +- +- for_each_card_rtds(card, rtd) { +- struct snd_pcm *pcm; +- int dir; +- +- pcm = rtd->pcm; +- if (!pcm || rtd->dai_link->no_pcm) ++ card = acomp->base->card; ++ if (!card) + continue; + +- for_each_pcm_streams(dir) { +- struct snd_pcm_substream *substream; ++ for_each_card_rtds(card, rtd) { ++ struct snd_pcm *pcm; ++ int dir; + +- substream = pcm->streams[dir].substream; +- if (!substream || !substream->runtime) ++ pcm = rtd->pcm; ++ if (!pcm || rtd->dai_link->no_pcm) + continue; + +- /* No need for _irq() as we are in nonatomic context. */ +- snd_pcm_stream_lock(substream); +- snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); +- snd_pcm_stream_unlock(substream); ++ for_each_pcm_streams(dir) { ++ struct snd_pcm_substream *substream; ++ ++ substream = pcm->streams[dir].substream; ++ if (!substream || !substream->runtime) ++ continue; ++ ++ /* No need for _irq() as we are in nonatomic context. */ ++ snd_pcm_stream_lock(substream); ++ snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); ++ snd_pcm_stream_unlock(substream); ++ } + } + } + } +- mutex_unlock(&adev->comp_list_mutex); + + /* forcibly shutdown all cores */ + core_mask = GENMASK(adev->hw_cfg.dsp_cores - 1, 0); +@@ -397,7 +398,7 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request + if (!ipc->ready) + return -EPERM; + +- mutex_lock(&ipc->msg_mutex); ++ guard(mutex)(&ipc->msg_mutex); + + spin_lock(&ipc->rx_lock); + avs_ipc_msg_init(ipc, reply); +@@ -412,7 +413,7 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request + /* Same treatment as on exception, just stack_dump=0. */ + avs_dsp_exception_caught(adev, &msg); + } +- goto exit; ++ return ret; + } + + ret = ipc->rx.rsp.status; +@@ -436,8 +437,6 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request + memcpy(reply->data, ipc->rx.data, reply->size); + } + +-exit: +- mutex_unlock(&ipc->msg_mutex); + return ret; + } + +@@ -501,7 +500,7 @@ static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *req + struct avs_ipc *ipc = adev->ipc; + int ret; + +- mutex_lock(&ipc->msg_mutex); ++ guard(mutex)(&ipc->msg_mutex); + + spin_lock(&ipc->rx_lock); + avs_ipc_msg_init(ipc, NULL); +@@ -522,8 +521,6 @@ static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *req + dev_err(adev->dev, "%s (0x%08x 0x%08x) failed: %d\n", + name, request->glb.primary, request->glb.ext.val, ret); + +- mutex_unlock(&ipc->msg_mutex); +- + return ret; + } + +diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c +--- a/sound/soc/intel/avs/loader.c ++++ b/sound/soc/intel/avs/loader.c +@@ -6,6 +6,7 @@ + // Amadeusz Slawinski + // + ++#include + #include + #include + #include +@@ -630,15 +631,15 @@ static int avs_load_firmware(struct avs_dev *adev, bool purge) + if (ret) + goto reenable_gating; + +- mutex_lock(&adev->comp_list_mutex); +- list_for_each_entry(acomp, &adev->comp_list, node) { +- struct avs_tplg *tplg = acomp->tplg; ++ scoped_guard(mutex, &adev->comp_list_mutex) { ++ list_for_each_entry(acomp, &adev->comp_list, node) { ++ struct avs_tplg *tplg = acomp->tplg; + +- ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs); +- if (ret < 0) +- break; ++ ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs); ++ if (ret < 0) ++ break; ++ } + } +- mutex_unlock(&adev->comp_list_mutex); + + reenable_gating: + avs_hda_l1sen_enable(adev, true); +diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c +--- a/sound/soc/intel/avs/path.c ++++ b/sound/soc/intel/avs/path.c +@@ -6,6 +6,7 @@ + // Amadeusz Slawinski + // + ++#include + #include + #include + #include +@@ -69,16 +70,13 @@ avs_path_find_path(struct avs_dev *adev, const char *name, u32 template_id) + if (!template) + return NULL; + +- spin_lock(&adev->path_list_lock); ++ guard(spinlock)(&adev->path_list_lock); + /* Only one variant of given path template may be instantiated at a time. */ + list_for_each_entry(path, &adev->path_list, node) { +- if (path->template->owner == template) { +- spin_unlock(&adev->path_list_lock); ++ if (path->template->owner == template) + return path; +- } + } + +- spin_unlock(&adev->path_list_lock); + return NULL; + } + +@@ -1300,7 +1298,7 @@ void avs_path_free(struct avs_path *path) + struct avs_path *cpath, *csave; + struct avs_dev *adev = path->owner; + +- mutex_lock(&adev->path_mutex); ++ guard(mutex)(&adev->path_mutex); + + /* Free all condpaths this path spawned. */ + list_for_each_entry_safe(cpath, csave, &path->source_list, source_node) +@@ -1309,8 +1307,6 @@ void avs_path_free(struct avs_path *path) + avs_condpath_free(path->owner, cpath); + + avs_path_free_unlocked(path); +- +- mutex_unlock(&adev->path_mutex); + } + + struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id, +@@ -1329,13 +1325,13 @@ struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id, + } + + /* Serialize path and its components creation. */ +- mutex_lock(&adev->path_mutex); ++ guard(mutex)(&adev->path_mutex); + /* Satisfy needs of avs_path_find_tplg(). */ +- mutex_lock(&adev->comp_list_mutex); ++ guard(mutex)(&adev->comp_list_mutex); + + path = avs_path_create_unlocked(adev, dma_id, variant); + if (IS_ERR(path)) +- goto exit; ++ return path; + + ret = avs_condpaths_walk_all(adev, path); + if (ret) { +@@ -1343,10 +1339,6 @@ struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id, + path = ERR_PTR(ret); + } + +-exit: +- mutex_unlock(&adev->comp_list_mutex); +- mutex_unlock(&adev->path_mutex); +- + return path; + } + +@@ -1491,15 +1483,13 @@ static void avs_condpaths_pause(struct avs_dev *adev, struct avs_path *path) + { + struct avs_path *cpath; + +- mutex_lock(&adev->path_mutex); ++ guard(mutex)(&adev->path_mutex); + + /* If either source or sink stops, so do the attached conditional paths. */ + list_for_each_entry(cpath, &path->source_list, source_node) + avs_condpath_pause(adev, cpath); + list_for_each_entry(cpath, &path->sink_list, sink_node) + avs_condpath_pause(adev, cpath); +- +- mutex_unlock(&adev->path_mutex); + } + + int avs_path_pause(struct avs_path *path) +@@ -1555,7 +1545,7 @@ static void avs_condpaths_run(struct avs_dev *adev, struct avs_path *path, int t + { + struct avs_path *cpath; + +- mutex_lock(&adev->path_mutex); ++ guard(mutex)(&adev->path_mutex); + + /* Run conditional paths only if source and sink are both running. */ + list_for_each_entry(cpath, &path->source_list, source_node) +@@ -1567,8 +1557,6 @@ static void avs_condpaths_run(struct avs_dev *adev, struct avs_path *path, int t + if (cpath->source->state == AVS_PPL_STATE_RUNNING && + cpath->sink->state == AVS_PPL_STATE_RUNNING) + avs_condpath_run(adev, cpath, trigger); +- +- mutex_unlock(&adev->path_mutex); + } + + int avs_path_run(struct avs_path *path, int trigger) +diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c +--- a/sound/soc/intel/avs/pcm.c ++++ b/sound/soc/intel/avs/pcm.c +@@ -959,7 +959,7 @@ static const struct file_operations topology_name_fops = { + static int avs_component_load_libraries(struct avs_soc_component *acomp) + { + struct avs_tplg *tplg = acomp->tplg; +- struct avs_dev *adev = to_avs_dev(acomp->base.dev); ++ struct avs_dev *adev = to_avs_dev(acomp->base->dev); + int ret; + + if (!tplg->num_libs) +@@ -1393,25 +1393,28 @@ int avs_register_component(struct device *dev, const char *name, + struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais) + { + struct avs_soc_component *acomp; +- int ret; ++ const char *comp_name; + + acomp = devm_kzalloc(dev, sizeof(*acomp), GFP_KERNEL); + if (!acomp) + return -ENOMEM; + +- acomp->base.name = devm_kstrdup(dev, name, GFP_KERNEL); +- if (!acomp->base.name) ++ acomp->base = snd_soc_component_alloc(dev); ++ if (!acomp->base) ++ return -ENOMEM; ++ ++ comp_name = devm_kstrdup(dev, name, GFP_KERNEL); ++ if (!comp_name) + return -ENOMEM; + + INIT_LIST_HEAD(&acomp->node); + + drv->use_dai_pcm_id = !obsolete_card_names; + +- ret = snd_soc_component_initialize(&acomp->base, drv, dev); +- if (ret < 0) +- return ret; ++ snd_soc_component_set_name(acomp->base, comp_name); ++ snd_soc_component_set_priv(acomp->base, acomp); + +- return snd_soc_add_component(&acomp->base, cpu_dais, num_cpu_dais); ++ return snd_soc_register_component(acomp->base, drv, cpu_dais, num_cpu_dais); + } + + static struct snd_soc_dai_driver dmic_cpu_dais[] = { +diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c +--- a/sound/soc/intel/avs/probes.c ++++ b/sound/soc/intel/avs/probes.c +@@ -296,19 +296,19 @@ static const struct snd_soc_component_driver avs_probe_component_driver = { + int avs_register_probe_component(struct avs_dev *adev, const char *name) + { + struct snd_soc_component *component; +- int ret; ++ const char *comp_name; + +- component = devm_kzalloc(adev->dev, sizeof(*component), GFP_KERNEL); ++ component = snd_soc_component_alloc(adev->dev); + if (!component) + return -ENOMEM; + +- component->name = devm_kstrdup(adev->dev, name, GFP_KERNEL); +- if (!component->name) ++ comp_name = devm_kstrdup(adev->dev, name, GFP_KERNEL); ++ if (!comp_name) + return -ENOMEM; + +- ret = snd_soc_component_initialize(component, &avs_probe_component_driver, adev->dev); +- if (ret) +- return ret; ++ snd_soc_component_set_name(component, comp_name); + +- return snd_soc_add_component(component, probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais)); ++ return snd_soc_register_component(component, ++ &avs_probe_component_driver, ++ probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais)); + } +diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c +--- a/sound/soc/intel/avs/topology.c ++++ b/sound/soc/intel/avs/topology.c +@@ -2229,7 +2229,7 @@ struct avs_tplg *avs_tplg_new(struct snd_soc_component *comp) + + int avs_load_topology(struct snd_soc_component *comp, const char *filename) + { +- const struct firmware *fw; ++ const struct firmware *fw __free(firmware) = NULL; + int ret; + + ret = request_firmware(&fw, filename, comp->dev); +@@ -2242,7 +2242,6 @@ int avs_load_topology(struct snd_soc_component *comp, const char *filename) + if (ret < 0) + dev_err(comp->dev, "load topology \"%s\" failed: %d\n", filename, ret); + +- release_firmware(fw); + return ret; + } + +diff --git a/sound/soc/intel/avs/utils.c b/sound/soc/intel/avs/utils.c +--- a/sound/soc/intel/avs/utils.c ++++ b/sound/soc/intel/avs/utils.c +@@ -6,6 +6,7 @@ + // Amadeusz Slawinski + // + ++#include + #include + #include + #include +@@ -48,13 +49,12 @@ int avs_get_module_entry(struct avs_dev *adev, const guid_t *uuid, struct avs_mo + { + int idx; + +- mutex_lock(&adev->modres_mutex); ++ guard(mutex)(&adev->modres_mutex); + + idx = avs_module_entry_index(adev, uuid); + if (idx >= 0) + memcpy(entry, &adev->mods_info->entries[idx], sizeof(*entry)); + +- mutex_unlock(&adev->modres_mutex); + return (idx < 0) ? idx : 0; + } + +@@ -62,13 +62,12 @@ int avs_get_module_id_entry(struct avs_dev *adev, u32 module_id, struct avs_modu + { + int idx; + +- mutex_lock(&adev->modres_mutex); ++ guard(mutex)(&adev->modres_mutex); + + idx = avs_module_id_entry_index(adev, module_id); + if (idx >= 0) + memcpy(entry, &adev->mods_info->entries[idx], sizeof(*entry)); + +- mutex_unlock(&adev->modres_mutex); + return (idx < 0) ? idx : 0; + } + +@@ -86,13 +85,12 @@ bool avs_is_module_ida_empty(struct avs_dev *adev, u32 module_id) + bool ret = false; + int idx; + +- mutex_lock(&adev->modres_mutex); ++ guard(mutex)(&adev->modres_mutex); + + idx = avs_module_id_entry_index(adev, module_id); + if (idx >= 0) + ret = ida_is_empty(adev->mod_idas[idx]); + +- mutex_unlock(&adev->modres_mutex); + return ret; + } + +@@ -163,68 +161,57 @@ int avs_module_info_init(struct avs_dev *adev, bool purge) + if (ret) + return AVS_IPC_RET(ret); + +- mutex_lock(&adev->modres_mutex); ++ guard(mutex)(&adev->modres_mutex); + + ret = avs_module_ida_alloc(adev, info, purge); + if (ret < 0) { + dev_err(adev->dev, "initialize module idas failed: %d\n", ret); +- goto exit; ++ return ret; + } + + /* Refresh current information with newly received table. */ + kfree(adev->mods_info); + adev->mods_info = info; + +-exit: +- mutex_unlock(&adev->modres_mutex); + return ret; + } + + void avs_module_info_free(struct avs_dev *adev) + { +- mutex_lock(&adev->modres_mutex); ++ guard(mutex)(&adev->modres_mutex); + + avs_module_ida_destroy(adev); + kfree(adev->mods_info); + adev->mods_info = NULL; +- +- mutex_unlock(&adev->modres_mutex); + } + + int avs_module_id_alloc(struct avs_dev *adev, u16 module_id) + { +- int ret, idx, max_id; ++ int idx, max_id; + +- mutex_lock(&adev->modres_mutex); ++ guard(mutex)(&adev->modres_mutex); + + idx = avs_module_id_entry_index(adev, module_id); + if (idx == -ENOENT) { + dev_err(adev->dev, "invalid module id: %d", module_id); +- ret = -EINVAL; +- goto exit; ++ return -EINVAL; + } + max_id = adev->mods_info->entries[idx].instance_max_count - 1; +- ret = ida_alloc_max(adev->mod_idas[idx], max_id, GFP_KERNEL); +-exit: +- mutex_unlock(&adev->modres_mutex); +- return ret; ++ ++ return ida_alloc_max(adev->mod_idas[idx], max_id, GFP_KERNEL); + } + + void avs_module_id_free(struct avs_dev *adev, u16 module_id, u8 instance_id) + { + int idx; + +- mutex_lock(&adev->modres_mutex); ++ guard(mutex)(&adev->modres_mutex); + + idx = avs_module_id_entry_index(adev, module_id); +- if (idx == -ENOENT) { ++ if (idx == -ENOENT) + dev_err(adev->dev, "invalid module id: %d", module_id); +- goto exit; +- } +- +- ida_free(adev->mod_idas[idx], instance_id); +-exit: +- mutex_unlock(&adev->modres_mutex); ++ else ++ ida_free(adev->mod_idas[idx], instance_id); + } + + /* +diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c +--- a/sound/soc/intel/boards/sof_sdw.c ++++ b/sound/soc/intel/boards/sof_sdw.c +@@ -1483,12 +1483,12 @@ static int mc_probe(struct platform_device *pdev) + dmi_check_system(sof_sdw_quirk_table); + + if (quirk_override != -1) { +- dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n", ++ dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n", + sof_sdw_quirk, quirk_override); + sof_sdw_quirk = quirk_override; + } + +- log_quirks(card->dev); ++ log_quirks(&pdev->dev); + + ctx->mc_quirk = sof_sdw_quirk; + /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ +@@ -1507,13 +1507,13 @@ static int mc_probe(struct platform_device *pdev) + for (i = 0; i < ctx->codec_info_list_count; i++) + amp_num += codec_info_list[i].amp_num; + +- card->components = devm_kasprintf(card->dev, GFP_KERNEL, ++ card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, + " cfg-amp:%d", amp_num); + if (!card->components) + return -ENOMEM; + + if (mach->mach_params.dmic_num) { +- card->components = devm_kasprintf(card->dev, GFP_KERNEL, ++ card->components = devm_kasprintf(&pdev->dev, GFP_KERNEL, + "%s mic:dmic cfg-mics:%d", + card->components, + mach->mach_params.dmic_num); +@@ -1524,7 +1524,7 @@ static int mc_probe(struct platform_device *pdev) + /* Register the card */ + ret = devm_snd_soc_register_card(card->dev, card); + if (ret) { +- dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret); ++ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed %d\n", ret); + asoc_sdw_mc_dailink_exit_loop(card); + return ret; + } +@@ -1542,8 +1542,8 @@ static void mc_remove(struct platform_device *pdev) + } + + static const struct platform_device_id mc_id_table[] = { +- { "sof_sdw", }, +- {} ++ { .name = "sof_sdw" }, ++ { } + }; + MODULE_DEVICE_TABLE(platform, mc_id_table); + +diff --git a/sound/soc/intel/catpt/core.h b/sound/soc/intel/catpt/core.h +--- a/sound/soc/intel/catpt/core.h ++++ b/sound/soc/intel/catpt/core.h +@@ -139,9 +139,7 @@ int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request, + + int catpt_first_boot_firmware(struct catpt_dev *cdev); + int catpt_boot_firmware(struct catpt_dev *cdev, bool restore); +-int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan); +-int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan); +-int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan); ++int catpt_store_firmware_context(struct catpt_dev *cdev); + int catpt_coredump(struct catpt_dev *cdev); + + #include +diff --git a/sound/soc/intel/catpt/device.c b/sound/soc/intel/catpt/device.c +--- a/sound/soc/intel/catpt/device.c ++++ b/sound/soc/intel/catpt/device.c +@@ -28,44 +28,17 @@ + static int catpt_do_suspend(struct device *dev) + { + struct catpt_dev *cdev = dev_get_drvdata(dev); +- struct dma_chan *chan; + int ret; + +- chan = catpt_dma_request_config_chan(cdev); +- if (IS_ERR(chan)) +- return PTR_ERR(chan); +- + memset(&cdev->dx_ctx, 0, sizeof(cdev->dx_ctx)); + ret = catpt_ipc_enter_dxstate(cdev, CATPT_DX_STATE_D3, &cdev->dx_ctx); +- if (ret) { +- ret = CATPT_IPC_RET(ret); +- goto release_dma_chan; +- } +- +- ret = catpt_dsp_stall(cdev, true); + if (ret) +- goto release_dma_chan; ++ return CATPT_IPC_RET(ret); + +- ret = catpt_store_memdumps(cdev, chan); +- if (ret) { +- dev_err(cdev->dev, "store memdumps failed: %d\n", ret); +- goto release_dma_chan; +- } +- +- ret = catpt_store_module_states(cdev, chan); +- if (ret) { +- dev_err(cdev->dev, "store module states failed: %d\n", ret); +- goto release_dma_chan; +- } +- +- ret = catpt_store_streams_context(cdev, chan); +- if (ret) +- dev_err(cdev->dev, "store streams ctx failed: %d\n", ret); +- +-release_dma_chan: +- dma_release_channel(chan); ++ ret = catpt_store_firmware_context(cdev); + if (ret) + return ret; ++ + return catpt_dsp_power_down(cdev); + } + +diff --git a/sound/soc/intel/catpt/loader.c b/sound/soc/intel/catpt/loader.c +--- a/sound/soc/intel/catpt/loader.c ++++ b/sound/soc/intel/catpt/loader.c +@@ -26,7 +26,7 @@ struct catpt_fw_hdr { + u32 reserved[4]; + } __packed; + +-struct catpt_fw_mod_hdr { ++struct catpt_fw_module_hdr { + char signature[FW_SIGNATURE_SIZE]; + u32 mod_size; + u32 blocks; +@@ -81,7 +81,7 @@ catpt_request_region(struct resource *root, resource_size_t size) + return __request_region(root, addr, size, NULL, 0); + } + +-int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) ++static int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) + { + struct catpt_stream_runtime *stream; + +@@ -108,7 +108,7 @@ int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) + return 0; + } + +-int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan) ++static int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan) + { + int i; + +@@ -138,7 +138,7 @@ int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan) + return 0; + } + +-int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) ++static int catpt_store_dram_data(struct catpt_dev *cdev, struct dma_chan *chan) + { + int i; + +@@ -171,6 +171,39 @@ int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) + return 0; + } + ++int catpt_store_firmware_context(struct catpt_dev *cdev) ++{ ++ struct dma_chan *chan; ++ int ret; ++ ++ chan = catpt_dma_request_config_chan(cdev); ++ if (IS_ERR(chan)) ++ return PTR_ERR(chan); ++ ++ ret = catpt_dsp_stall(cdev, true); ++ if (ret) ++ goto exit; ++ ++ ret = catpt_store_dram_data(cdev, chan); ++ if (ret) { ++ dev_err(cdev->dev, "store memdumps failed: %d\n", ret); ++ goto exit; ++ } ++ ++ ret = catpt_store_module_states(cdev, chan); ++ if (ret) { ++ dev_err(cdev->dev, "store module states failed: %d\n", ret); ++ goto exit; ++ } ++ ++ ret = catpt_store_streams_context(cdev, chan); ++ if (ret) ++ dev_err(cdev->dev, "store streams ctx failed: %d\n", ret); ++exit: ++ dma_release_channel(chan); ++ return ret; ++} ++ + static int + catpt_restore_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) + { +@@ -199,7 +232,7 @@ catpt_restore_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) + return 0; + } + +-static int catpt_restore_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) ++static int catpt_restore_dram_data(struct catpt_dev *cdev, struct dma_chan *chan) + { + int i; + +@@ -234,9 +267,9 @@ static int catpt_restore_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) + return 0; + } + +-static int catpt_restore_fwimage(struct catpt_dev *cdev, +- struct dma_chan *chan, dma_addr_t paddr, +- struct catpt_fw_block_hdr *blk) ++static int catpt_restore_dram_rodata(struct catpt_dev *cdev, ++ struct dma_chan *chan, dma_addr_t paddr, ++ struct catpt_fw_block_hdr *blk) + { + struct resource r1 = {}; + int i; +@@ -324,28 +357,26 @@ static int catpt_load_block(struct catpt_dev *cdev, + + static int catpt_restore_basefw(struct catpt_dev *cdev, + struct dma_chan *chan, dma_addr_t paddr, +- struct catpt_fw_mod_hdr *basefw) ++ struct catpt_fw_module_hdr *basefw) + { +- u32 offset = sizeof(*basefw); ++ u32 off = sizeof(*basefw); + int ret, i; + + print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, + basefw, sizeof(*basefw), false); + +- /* restore basefw image */ ++ /* Restore IRAM and .rodata for DRAM based on the firmware image. */ + for (i = 0; i < basefw->blocks; i++) { + struct catpt_fw_block_hdr *blk; + +- blk = (struct catpt_fw_block_hdr *)((u8 *)basefw + offset); ++ blk = (struct catpt_fw_block_hdr *)((u8 *)basefw + off); + + switch (blk->ram_type) { + case CATPT_RAM_TYPE_IRAM: +- ret = catpt_load_block(cdev, chan, paddr + offset, +- blk, false); ++ ret = catpt_load_block(cdev, chan, paddr + off, blk, false); + break; + default: +- ret = catpt_restore_fwimage(cdev, chan, paddr + offset, +- blk); ++ ret = catpt_restore_dram_rodata(cdev, chan, paddr + off, blk); + break; + } + +@@ -354,11 +385,11 @@ static int catpt_restore_basefw(struct catpt_dev *cdev, + return ret; + } + +- offset += sizeof(*blk) + blk->size; ++ off += sizeof(*blk) + blk->size; + } + +- /* then proceed with memory dumps */ +- ret = catpt_restore_memdumps(cdev, chan); ++ /* Then proceed with DRAM .data saved before D3. */ ++ ret = catpt_restore_dram_data(cdev, chan); + if (ret) + dev_err(cdev->dev, "restore memdumps failed: %d\n", ret); + +@@ -367,9 +398,9 @@ static int catpt_restore_basefw(struct catpt_dev *cdev, + + static int catpt_restore_module(struct catpt_dev *cdev, + struct dma_chan *chan, dma_addr_t paddr, +- struct catpt_fw_mod_hdr *mod) ++ struct catpt_fw_module_hdr *mod) + { +- u32 offset = sizeof(*mod); ++ u32 off = sizeof(*mod); + int i; + + print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, +@@ -379,7 +410,7 @@ static int catpt_restore_module(struct catpt_dev *cdev, + struct catpt_fw_block_hdr *blk; + int ret; + +- blk = (struct catpt_fw_block_hdr *)((u8 *)mod + offset); ++ blk = (struct catpt_fw_block_hdr *)((u8 *)mod + off); + + switch (blk->ram_type) { + case CATPT_RAM_TYPE_INSTANCE: +@@ -390,7 +421,7 @@ static int catpt_restore_module(struct catpt_dev *cdev, + ALIGN(blk->size, 4)); + break; + default: +- ret = catpt_load_block(cdev, chan, paddr + offset, ++ ret = catpt_load_block(cdev, chan, paddr + off, + blk, false); + break; + } +@@ -400,7 +431,7 @@ static int catpt_restore_module(struct catpt_dev *cdev, + return ret; + } + +- offset += sizeof(*blk) + blk->size; ++ off += sizeof(*blk) + blk->size; + } + + return 0; +@@ -408,10 +439,10 @@ static int catpt_restore_module(struct catpt_dev *cdev, + + static int catpt_load_module(struct catpt_dev *cdev, + struct dma_chan *chan, dma_addr_t paddr, +- struct catpt_fw_mod_hdr *mod) ++ struct catpt_fw_module_hdr *mod) + { + struct catpt_module_type *type; +- u32 offset = sizeof(*mod); ++ u32 off = sizeof(*mod); + int i; + + print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, +@@ -423,9 +454,9 @@ static int catpt_load_module(struct catpt_dev *cdev, + struct catpt_fw_block_hdr *blk; + int ret; + +- blk = (struct catpt_fw_block_hdr *)((u8 *)mod + offset); ++ blk = (struct catpt_fw_block_hdr *)((u8 *)mod + off); + +- ret = catpt_load_block(cdev, chan, paddr + offset, blk, true); ++ ret = catpt_load_block(cdev, chan, paddr + off, blk, true); + if (ret) { + dev_err(cdev->dev, "load block failed: %d\n", ret); + return ret; +@@ -440,7 +471,7 @@ static int catpt_load_module(struct catpt_dev *cdev, + type->state_size = blk->size; + } + +- offset += sizeof(*blk) + blk->size; ++ off += sizeof(*blk) + blk->size; + } + + /* init module type static info */ +@@ -457,17 +488,17 @@ static int catpt_restore_firmware(struct catpt_dev *cdev, + struct dma_chan *chan, dma_addr_t paddr, + struct catpt_fw_hdr *fw) + { +- u32 offset = sizeof(*fw); ++ u32 off = sizeof(*fw); + int i; + + print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, + fw, sizeof(*fw), false); + + for (i = 0; i < fw->modules; i++) { +- struct catpt_fw_mod_hdr *mod; ++ struct catpt_fw_module_hdr *mod; + int ret; + +- mod = (struct catpt_fw_mod_hdr *)((u8 *)fw + offset); ++ mod = (struct catpt_fw_module_hdr *)((u8 *)fw + off); + if (strncmp(fw->signature, mod->signature, + FW_SIGNATURE_SIZE)) { + dev_err(cdev->dev, "module signature mismatch\n"); +@@ -479,12 +510,10 @@ static int catpt_restore_firmware(struct catpt_dev *cdev, + + switch (mod->module_id) { + case CATPT_MODID_BASE_FW: +- ret = catpt_restore_basefw(cdev, chan, paddr + offset, +- mod); ++ ret = catpt_restore_basefw(cdev, chan, paddr + off, mod); + break; + default: +- ret = catpt_restore_module(cdev, chan, paddr + offset, +- mod); ++ ret = catpt_restore_module(cdev, chan, paddr + off, mod); + break; + } + +@@ -493,7 +522,7 @@ static int catpt_restore_firmware(struct catpt_dev *cdev, + return ret; + } + +- offset += sizeof(*mod) + mod->mod_size; ++ off += sizeof(*mod) + mod->mod_size; + } + + return 0; +@@ -503,17 +532,17 @@ static int catpt_load_firmware(struct catpt_dev *cdev, + struct dma_chan *chan, dma_addr_t paddr, + struct catpt_fw_hdr *fw) + { +- u32 offset = sizeof(*fw); ++ u32 off = sizeof(*fw); + int i; + + print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, + fw, sizeof(*fw), false); + + for (i = 0; i < fw->modules; i++) { +- struct catpt_fw_mod_hdr *mod; ++ struct catpt_fw_module_hdr *mod; + int ret; + +- mod = (struct catpt_fw_mod_hdr *)((u8 *)fw + offset); ++ mod = (struct catpt_fw_module_hdr *)((u8 *)fw + off); + if (strncmp(fw->signature, mod->signature, + FW_SIGNATURE_SIZE)) { + dev_err(cdev->dev, "module signature mismatch\n"); +@@ -523,44 +552,40 @@ static int catpt_load_firmware(struct catpt_dev *cdev, + if (mod->module_id > CATPT_MODID_LAST) + return -EINVAL; + +- ret = catpt_load_module(cdev, chan, paddr + offset, mod); ++ ret = catpt_load_module(cdev, chan, paddr + off, mod); + if (ret) { + dev_err(cdev->dev, "load module failed: %d\n", ret); + return ret; + } + +- offset += sizeof(*mod) + mod->mod_size; ++ off += sizeof(*mod) + mod->mod_size; + } + + return 0; + } + +-static int catpt_load_image(struct catpt_dev *cdev, struct dma_chan *chan, +- const char *name, const char *signature, +- bool restore) ++static int catpt_request_load_firmware(struct catpt_dev *cdev, struct dma_chan *chan, ++ const char *name, bool restore) + { + struct catpt_fw_hdr *fw; +- struct firmware *img; + dma_addr_t paddr; + void *vaddr; + int ret; + +- ret = request_firmware((const struct firmware **)&img, name, cdev->dev); ++ const struct firmware *img __free(firmware) = NULL; ++ ret = request_firmware(&img, name, cdev->dev); + if (ret) + return ret; + + fw = (struct catpt_fw_hdr *)img->data; +- if (strncmp(fw->signature, signature, FW_SIGNATURE_SIZE)) { ++ if (strncmp(fw->signature, FW_SIGNATURE, FW_SIGNATURE_SIZE)) { + dev_err(cdev->dev, "firmware signature mismatch\n"); +- ret = -EINVAL; +- goto release_fw; ++ return -EINVAL; + } + + vaddr = dma_alloc_coherent(cdev->dev, img->size, &paddr, GFP_KERNEL); +- if (!vaddr) { +- ret = -ENOMEM; +- goto release_fw; +- } ++ if (!vaddr) ++ return -ENOMEM; + + memcpy(vaddr, img->data, img->size); + fw = (struct catpt_fw_hdr *)vaddr; +@@ -570,12 +595,10 @@ static int catpt_load_image(struct catpt_dev *cdev, struct dma_chan *chan, + ret = catpt_load_firmware(cdev, chan, paddr, fw); + + dma_free_coherent(cdev->dev, img->size, vaddr, paddr); +-release_fw: +- release_firmware(img); + return ret; + } + +-static int catpt_load_images(struct catpt_dev *cdev, bool restore) ++static int catpt_request_dma_load_firmware(struct catpt_dev *cdev, bool restore) + { + struct dma_chan *chan; + int ret; +@@ -584,8 +607,7 @@ static int catpt_load_images(struct catpt_dev *cdev, bool restore) + if (IS_ERR(chan)) + return PTR_ERR(chan); + +- ret = catpt_load_image(cdev, chan, cdev->spec->fw_name, +- FW_SIGNATURE, restore); ++ ret = catpt_request_load_firmware(cdev, chan, cdev->spec->fw_name, restore); + if (ret) + goto release_dma_chan; + +@@ -605,7 +627,7 @@ int catpt_boot_firmware(struct catpt_dev *cdev, bool restore) + + catpt_dsp_stall(cdev, true); + +- ret = catpt_load_images(cdev, restore); ++ ret = catpt_request_dma_load_firmware(cdev, restore); + if (ret) { + dev_err(cdev->dev, "load binaries failed: %d\n", ret); + return ret; +@@ -619,10 +641,10 @@ int catpt_boot_firmware(struct catpt_dev *cdev, bool restore) + if (!ret) { + dev_err(cdev->dev, "firmware ready timeout\n"); + return -ETIMEDOUT; +- /* Wake up does not mean FW is ready, an exception could occur. */ +- } else if (!cdev->ipc.ready) { +- return -EREMOTEIO; + } ++ /* Wake up does not mean FW is ready, an exception could occur. */ ++ if (!cdev->ipc.ready) ++ return -EREMOTEIO; + + /* update sram pg & clock once done booting */ + catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask); +diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c +--- a/sound/soc/intel/catpt/pcm.c ++++ b/sound/soc/intel/catpt/pcm.c +@@ -75,8 +75,8 @@ static struct catpt_stream_template *catpt_topology[] = { + static struct catpt_stream_template * + catpt_get_stream_template(struct snd_pcm_substream *substream) + { +- struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream); +- struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtm, 0); ++ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); ++ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + enum catpt_stream_type type; + + type = cpu_dai->driver->id; +@@ -159,11 +159,11 @@ static void catpt_stream_read_position(struct catpt_dev *cdev, + static void catpt_arrange_page_table(struct snd_pcm_substream *substream, + struct snd_dma_buffer *pgtbl) + { +- struct snd_pcm_runtime *rtm = substream->runtime; ++ struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_dma_buffer *databuf = snd_pcm_get_dma_buf(substream); + int i, pages; + +- pages = snd_sgbuf_aligned_pages(rtm->dma_bytes); ++ pages = snd_sgbuf_aligned_pages(runtime->dma_bytes); + + for (i = 0; i < pages; i++) { + u32 pfn, offset; +@@ -386,7 +386,7 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) + { +- struct snd_pcm_runtime *rtm = substream->runtime; ++ struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_dma_buffer *dmab; + struct catpt_stream_runtime *stream; + struct catpt_audio_format afmt; +@@ -412,8 +412,8 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream, + + memset(&rinfo, 0, sizeof(rinfo)); + rinfo.page_table_addr = stream->pgtbl.addr; +- rinfo.num_pages = DIV_ROUND_UP(rtm->dma_bytes, PAGE_SIZE); +- rinfo.size = rtm->dma_bytes; ++ rinfo.num_pages = DIV_ROUND_UP(runtime->dma_bytes, PAGE_SIZE); ++ rinfo.size = runtime->dma_bytes; + rinfo.offset = 0; + rinfo.ring_first_page_pfn = PFN_DOWN(snd_sgbuf_get_addr(dmab, 0)); + +@@ -544,11 +544,11 @@ void catpt_stream_update_position(struct catpt_dev *cdev, + struct catpt_notify_position *pos) + { + struct snd_pcm_substream *substream = stream->substream; +- struct snd_pcm_runtime *r = substream->runtime; ++ struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_uframes_t dsppos, newpos; + int ret; + +- dsppos = bytes_to_frames(r, pos->stream_position); ++ dsppos = bytes_to_frames(runtime, pos->stream_position); + + if (!stream->prepared) + goto exit; +@@ -556,8 +556,8 @@ void catpt_stream_update_position(struct catpt_dev *cdev, + if (stream->template->type != CATPT_STRM_TYPE_RENDER) + goto exit; + +- if (dsppos >= r->buffer_size / 2) +- newpos = r->buffer_size / 2; ++ if (dsppos >= runtime->buffer_size / 2) ++ newpos = runtime->buffer_size / 2; + else + newpos = 0; + /* +@@ -565,7 +565,7 @@ void catpt_stream_update_position(struct catpt_dev *cdev, + * (buffer half consumed) update wp to allow stream progression. + */ + ret = catpt_ipc_set_write_pos(cdev, stream->info.stream_hw_id, +- frames_to_bytes(r, newpos), ++ frames_to_bytes(runtime, newpos), + false, false); + if (ret) { + dev_err(cdev->dev, "update position for stream %d failed: %d\n", +@@ -600,11 +600,11 @@ static const struct snd_pcm_hardware catpt_pcm_hardware = { + }; + + static int catpt_component_pcm_new(struct snd_soc_component *component, +- struct snd_soc_pcm_runtime *rtm) ++ struct snd_soc_pcm_runtime *rtd) + { + struct catpt_dev *cdev = dev_get_drvdata(component->dev); + +- snd_pcm_set_managed_buffer_all(rtm->pcm, SNDRV_DMA_TYPE_DEV_SG, ++ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV_SG, + cdev->dev, + catpt_pcm_hardware.buffer_bytes_max, + catpt_pcm_hardware.buffer_bytes_max); +@@ -615,9 +615,9 @@ static int catpt_component_pcm_new(struct snd_soc_component *component, + static int catpt_component_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) + { +- struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream); ++ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + +- if (!rtm->dai_link->no_pcm) ++ if (!rtd->dai_link->no_pcm) + snd_soc_set_runtime_hwparams(substream, &catpt_pcm_hardware); + return 0; + } +@@ -626,13 +626,13 @@ static snd_pcm_uframes_t + catpt_component_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) + { +- struct snd_soc_pcm_runtime *rtm = snd_soc_substream_to_rtd(substream); +- struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtm, 0); ++ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); ++ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(component->dev); + u32 pos; + +- if (rtm->dai_link->no_pcm) ++ if (rtd->dai_link->no_pcm) + return 0; + + stream = snd_soc_dai_get_dma_data(cpu_dai, substream); +@@ -650,10 +650,10 @@ static const struct snd_soc_dai_ops catpt_fe_dai_ops = { + .trigger = catpt_dai_trigger, + }; + +-static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtm, ++static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_dai *dai) + { +- struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtm, 0); ++ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); + struct catpt_ssp_device_format devfmt; + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); + int ret; +@@ -871,8 +871,7 @@ static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol) + return CATPT_IPC_RET(ret); + } + +-static int catpt_volume_info(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_info *uinfo) ++static int catpt_volume_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) + { + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = CATPT_CHANNELS_MAX; +@@ -1073,18 +1072,14 @@ int catpt_arm_stream_templates(struct catpt_dev *cdev) + int catpt_register_plat_component(struct catpt_dev *cdev) + { + struct snd_soc_component *component; +- int ret; + +- component = devm_kzalloc(cdev->dev, sizeof(*component), GFP_KERNEL); ++ component = snd_soc_component_alloc(cdev->dev); + if (!component) + return -ENOMEM; + +- ret = snd_soc_component_initialize(component, &catpt_comp_driver, +- cdev->dev); +- if (ret) +- return ret; ++ snd_soc_component_set_name(component, catpt_comp_driver.name); + +- component->name = catpt_comp_driver.name; +- return snd_soc_add_component(component, dai_drivers, +- ARRAY_SIZE(dai_drivers)); ++ return snd_soc_register_component(component, ++ &catpt_comp_driver, ++ dai_drivers, ARRAY_SIZE(dai_drivers)); + } +diff --git a/sound/soc/intel/common/soc-acpi-intel-arl-match.c b/sound/soc/intel/common/soc-acpi-intel-arl-match.c +--- a/sound/soc/intel/common/soc-acpi-intel-arl-match.c ++++ b/sound/soc/intel/common/soc-acpi-intel-arl-match.c +@@ -572,6 +572,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = { + .links = arl_rt711_l0_rt1316_l3, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-arl-rt711-l0-rt1316-l3.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0) | BIT(3), +@@ -600,12 +601,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = { + .links = arl_rvp, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-arl-rt711.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = 0x1, /* link0 required */ + .links = arl_sdca_rvp, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-arl-rt711-l0.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(2), +diff --git a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c +--- a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c ++++ b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c +@@ -717,24 +717,28 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { + .links = lnl_3_in_1_sdca, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-rt711-l0-rt1316-l23-rt714-l1.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0) | BIT(2) | BIT(3), + .links = lnl_cs42l43_l0_cs35l56_l23, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-cs42l43-l0-cs35l56-l23.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(1) | BIT(2) | BIT(3), + .links = lnl_cs42l43_l2_cs35l56x6_l13, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-cs42l43-l2-cs35l56x6-l13.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0) | BIT(3), + .links = lnl_cs42l43_l0_cs35l56_l3, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-cs42l43-l0-cs35l56-l3.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0), +@@ -748,12 +752,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { + .links = lnl_rvp, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-rt711.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(2) | BIT(3), + .links = lnl_712_only, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-rt712-l2-rt1712-l3.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0), +@@ -766,19 +772,22 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { + .link_mask = GENMASK(2, 0), + .links = lnl_sdw_rt1318_l12_rt714_l0, + .drv_name = "sof_sdw", +- .sof_tplg_filename = "sof-lnl-rt1318-l12-rt714-l0.tplg" ++ .sof_tplg_filename = "sof-lnl-rt1318-l12-rt714-l0.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = GENMASK(2, 0), + .links = lnl_sdw_rt1320_l12_rt714_l0, + .drv_name = "sof_sdw", +- .sof_tplg_filename = "sof-lnl-rt1320-l12-rt714-l0.tplg" ++ .sof_tplg_filename = "sof-lnl-rt1320-l12-rt714-l0.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0) | BIT(1), + .links = lnl_sdw_rt713_l0_rt1318_l1, + .drv_name = "sof_sdw", +- .sof_tplg_filename = "sof-lnl-rt713-l0-rt1318-l1.tplg" ++ .sof_tplg_filename = "sof-lnl-rt713-l0-rt1318-l1.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(1) | BIT(2), +diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c +--- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c ++++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c +@@ -1321,60 +1321,70 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { + .links = tac5572_l0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-tac5572.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0), + .links = tac5672_l0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-tac5672.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0), + .links = tac5682_l0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-tac5682.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0), + .links = tas2783_link0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-tas2783.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0), + .links = tas2883_l0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-tas2883.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = GENMASK(3, 0), + .links = mtl_rt713_l0_rt1316_l12_rt1713_l3, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-rt713-l0-rt1316-l12-rt1713-l3.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = GENMASK(3, 0), + .links = mtl_rt713_l0_rt1318_l12_rt1713_l3, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-rt713-l0-rt1318-l12-rt1713-l3.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0) | BIT(1) | BIT(3), + .links = mtl_rt713_l0_rt1318_l1_rt1713_l3, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-rt713-l0-rt1318-l1-rt1713-l3.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = GENMASK(2, 0), + .links = mtl_rt713_l0_rt1316_l12, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-rt713-l0-rt1316-l12.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(3) | BIT(0), + .links = mtl_712_l0_1712_l3, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-rt712-l0-rt1712-l3.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0), +@@ -1395,7 +1405,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { + .link_mask = GENMASK(2, 0), + .links = mtl_sdw_rt1318_l12_rt714_l0, + .drv_name = "sof_sdw", +- .sof_tplg_filename = "sof-mtl-rt1318-l12-rt714-l0.tplg" ++ .sof_tplg_filename = "sof-mtl-rt1318-l12-rt714-l0.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0) | BIT(2) | BIT(3), +@@ -1455,12 +1466,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { + .links = mtl_3_in_1_sdca, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-rt711-l0-rt1316-l23-rt714-l1.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = 0x9, /* 2 active links required */ + .links = mtl_rt711_l0_rt1316_l3, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-rt711-l0-rt1316-l3.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0), +@@ -1474,18 +1487,21 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { + .links = mtl_rvp, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-rt711.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0) | BIT(2), + .links = rt5682_link2_max98373_link0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-sdw-rt5682-l2-max98373-l0.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(0) | BIT(2), + .links = cs42l42_link0_max98363_link2, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-sdw-cs42l42-l0-max98363-l2.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + {}, + }; +diff --git a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c +--- a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c ++++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c +@@ -492,7 +492,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = { + .links = ptl_rt722_l0_rt1320_l23, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-ptl-rt722-l0-rt1320-l23.tplg", +- .get_function_tplg_files = sof_sdw_get_tplg_files, ++ .machine_check = snd_soc_acpi_intel_no_function_topology, + }, + { + .link_mask = BIT(1) | BIT(2), +@@ -522,14 +522,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = { + .links = ptl_rvp, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-ptl-rt711.tplg", ++ .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { + .link_mask = BIT(3), + .links = ptl_sdw_rt712_vb_l3_rt1320_l3, + .drv_name = "sof_sdw", +- .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, ++ .machine_check = snd_soc_acpi_intel_rt712_vb_no_function_topology, + .sof_tplg_filename = "sof-ptl-rt712-l3-rt1320-l3.tplg", +- .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + {}, + }; +diff --git a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c +--- a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c ++++ b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c +@@ -53,6 +53,14 @@ bool snd_soc_acpi_intel_no_function_topology(void *arg) + } + EXPORT_SYMBOL_NS(snd_soc_acpi_intel_no_function_topology, "SND_SOC_ACPI_INTEL_SDCA_QUIRKS"); + ++bool snd_soc_acpi_intel_rt712_vb_no_function_topology(void *arg) ++{ ++ return snd_soc_acpi_intel_sdca_is_device_rt712_vb(arg) && ++ snd_soc_acpi_intel_no_function_topology(arg); ++} ++EXPORT_SYMBOL_NS(snd_soc_acpi_intel_rt712_vb_no_function_topology, ++ "SND_SOC_ACPI_INTEL_SDCA_QUIRKS"); ++ + MODULE_DESCRIPTION("ASoC ACPI Intel SDCA quirks"); + MODULE_LICENSE("GPL"); + MODULE_IMPORT_NS("SND_SOC_SDCA"); +diff --git a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h +--- a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h ++++ b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h +@@ -11,5 +11,6 @@ + + bool snd_soc_acpi_intel_sdca_is_device_rt712_vb(void *arg); + bool snd_soc_acpi_intel_no_function_topology(void *arg); ++bool snd_soc_acpi_intel_rt712_vb_no_function_topology(void *arg); + + #endif +diff --git a/sound/soc/intel/keembay/kmb_platform.c b/sound/soc/intel/keembay/kmb_platform.c +--- a/sound/soc/intel/keembay/kmb_platform.c ++++ b/sound/soc/intel/keembay/kmb_platform.c +@@ -873,6 +873,8 @@ static int kmb_plat_dai_probe(struct platform_device *pdev) + + if (kmb_i2s->use_pio) { + irq = platform_get_irq_optional(pdev, 0); ++ if (irq == -EPROBE_DEFER) ++ return irq; + if (irq > 0) { + ret = devm_request_irq(dev, irq, kmb_i2s_irq_handler, 0, + pdev->name, kmb_i2s); +diff --git a/sound/soc/loongson/loongson_card.c b/sound/soc/loongson/loongson_card.c +--- a/sound/soc/loongson/loongson_card.c ++++ b/sound/soc/loongson/loongson_card.c +@@ -2,24 +2,131 @@ + // + // Loongson ASoC Audio Machine driver + // +-// Copyright (C) 2023 Loongson Technology Corporation Limited ++// Copyright (C) 2023-2026 Loongson Technology Corporation Limited + // Author: Yingkun Meng ++// Binbin Zhou + // + ++#include ++#include + #include ++#include ++#include ++#include + #include + #include +-#include +-#include +-#include + + static char codec_name[SND_ACPI_I2C_ID_LEN]; + + struct loongson_card_data { + struct snd_soc_card snd_card; + unsigned int mclk_fs; ++ struct gpio_desc *gpiod_hp_det; ++ struct gpio_desc *gpiod_hp_ctl; ++ struct gpio_desc *gpiod_spkr_en; ++ const struct loongson_card_config *cfg; + }; + ++struct loongson_card_config { ++ unsigned int fmt; ++ bool add_hp_jack; ++ bool add_dapm_widgets; ++ bool add_dapm_routes; ++}; ++ ++static const struct loongson_card_config ls2k1000_card_config = { ++ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBC_CFC, ++ .add_hp_jack = false, ++ .add_dapm_widgets = false, ++ .add_dapm_routes = false, ++}; ++ ++static const struct loongson_card_config ls2k0300_forever_pi_card_config = { ++ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC, ++ .add_hp_jack = false, ++ .add_dapm_widgets = false, ++ .add_dapm_routes = false, ++}; ++ ++static const struct loongson_card_config ls2k0300_dl2k0300b_card_config = { ++ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC, ++ .add_hp_jack = true, ++ .add_dapm_widgets = true, ++ .add_dapm_routes = true, ++}; ++ ++static int tegra_machine_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *k, int event) ++{ ++ struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm); ++ struct loongson_card_data *priv = snd_soc_card_get_drvdata(card); ++ ++ if (!snd_soc_dapm_widget_name_cmp(w, "Speaker")) ++ gpiod_set_value_cansleep(priv->gpiod_spkr_en, ++ SND_SOC_DAPM_EVENT_ON(event)); ++ ++ if (!snd_soc_dapm_widget_name_cmp(w, "Headphone")) ++ gpiod_set_value_cansleep(priv->gpiod_hp_ctl, ++ SND_SOC_DAPM_EVENT_ON(event)); ++ ++ return 0; ++} ++ ++static const struct snd_soc_dapm_widget loongson_aosc_dapm_widgets[] = { ++ SND_SOC_DAPM_HP("Headphone", tegra_machine_event), ++ SND_SOC_DAPM_SPK("Speaker", tegra_machine_event), ++}; ++ ++/* Headphones Jack */ ++ ++static struct snd_soc_jack loongson_asoc_hp_jack; ++ ++static struct snd_soc_jack_pin loongson_asoc_hp_jack_pins[] = { ++ { ++ .pin = "Headphone", ++ .mask = SND_JACK_HEADPHONE ++ }, ++ { ++ .pin = "Speaker", ++ .mask = SND_JACK_HEADPHONE, ++ .invert = 1 ++ }, ++}; ++ ++static struct snd_soc_jack_gpio loongson_asoc_hp_jack_gpio = { ++ .name = "Headphones detection", ++ .report = SND_JACK_HEADPHONE, ++ .debounce_time = 150, ++}; ++ ++static int loongson_asoc_machine_init(struct snd_soc_pcm_runtime *rtd) ++{ ++ struct snd_soc_card *card = rtd->card; ++ struct loongson_card_data *ls_priv = snd_soc_card_get_drvdata(card); ++ int ret = 0; ++ ++ if (!ls_priv->cfg->add_hp_jack || !ls_priv->gpiod_hp_det) ++ return 0; ++ ++ ret = snd_soc_card_jack_new_pins(card, "Headphones Jack", ++ SND_JACK_HEADPHONE, ++ &loongson_asoc_hp_jack, ++ loongson_asoc_hp_jack_pins, ++ ARRAY_SIZE(loongson_asoc_hp_jack_pins)); ++ if (ret) { ++ dev_err(rtd->dev, "Headphones Jack creation failed: %d\n", ret); ++ return ret; ++ } ++ ++ loongson_asoc_hp_jack_gpio.desc = ls_priv->gpiod_hp_det; ++ ++ ret = snd_soc_jack_add_gpios(&loongson_asoc_hp_jack, 1, &loongson_asoc_hp_jack_gpio); ++ if (ret) ++ dev_err(rtd->dev, "Headphone GPIO not added: %d\n", ret); ++ ++ return ret; ++} ++ + static int loongson_card_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) + { +@@ -45,7 +152,7 @@ static int loongson_card_hw_params(struct snd_pcm_substream *substream, + return ret; + } + +- return 0; ++ return snd_soc_runtime_set_dai_fmt(rtd, ls_card->cfg->fmt); + } + + static const struct snd_soc_ops loongson_ops = { +@@ -61,8 +168,7 @@ static struct snd_soc_dai_link loongson_dai_links[] = { + { + .name = "Loongson Audio Port", + .stream_name = "Loongson Audio", +- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_NF +- | SND_SOC_DAIFMT_CBC_CFC, ++ .init = loongson_asoc_machine_init, + SND_SOC_DAILINK_REG(analog), + .ops = &loongson_ops, + }, +@@ -123,16 +229,35 @@ static int loongson_card_parse_acpi(struct loongson_card_data *data) + + static int loongson_card_parse_of(struct loongson_card_data *data) + { +- struct device_node *cpu, *codec; + struct snd_soc_card *card = &data->snd_card; ++ struct device_node *cpu, *codec; + struct device *dev = card->dev; + int ret, i; + ++ data->gpiod_hp_det = devm_gpiod_get_optional(dev, "hp-det", GPIOD_IN); ++ if (IS_ERR(data->gpiod_hp_det)) ++ return PTR_ERR(data->gpiod_hp_det); ++ ++ data->gpiod_hp_ctl = devm_gpiod_get_optional(dev, "hp-ctl", GPIOD_OUT_LOW); ++ if (IS_ERR(data->gpiod_hp_ctl)) ++ return PTR_ERR(data->gpiod_hp_ctl); ++ ++ data->gpiod_spkr_en = devm_gpiod_get_optional(dev, "spkr-en", GPIOD_OUT_LOW); ++ if (IS_ERR(data->gpiod_spkr_en)) ++ return PTR_ERR(data->gpiod_spkr_en); ++ ++ if (data->cfg->add_dapm_routes) { ++ ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); ++ if (ret) ++ return ret; ++ } ++ + cpu = of_get_child_by_name(dev->of_node, "cpu"); + if (!cpu) { + dev_err(dev, "platform property missing or invalid\n"); + return -EINVAL; + } ++ + codec = of_get_child_by_name(dev->of_node, "codec"); + if (!codec) { + dev_err(dev, "audio-codec property missing or invalid\n"); +@@ -177,12 +302,22 @@ static int loongson_asoc_card_probe(struct platform_device *pdev) + if (!ls_priv) + return -ENOMEM; + ++ ls_priv->cfg = (const struct loongson_card_config *)device_get_match_data(dev); ++ if (!ls_priv->cfg) ++ return -EINVAL; ++ + card = &ls_priv->snd_card; + + card->dev = dev; + card->owner = THIS_MODULE; + card->dai_link = loongson_dai_links; + card->num_links = ARRAY_SIZE(loongson_dai_links); ++ ++ if (ls_priv->cfg->add_dapm_widgets) { ++ card->dapm_widgets = loongson_aosc_dapm_widgets; ++ card->num_dapm_widgets = ARRAY_SIZE(loongson_aosc_dapm_widgets); ++ } ++ + snd_soc_card_set_drvdata(card, ls_priv); + + ret = device_property_read_string(dev, "model", &card->name); +@@ -202,7 +337,19 @@ static int loongson_asoc_card_probe(struct platform_device *pdev) + } + + static const struct of_device_id loongson_asoc_dt_ids[] = { +- { .compatible = "loongson,ls-audio-card" }, ++ /* Loongson-2K1000/Loongson-2K2000/LS7A */ ++ { ++ .compatible = "loongson,ls-audio-card", ++ .data = &ls2k1000_card_config ++ }, ++ { ++ .compatible = "loongson,ls2k0300-forever-pi-audio-card", ++ .data = &ls2k0300_forever_pi_card_config ++ }, ++ { ++ .compatible = "loongson,ls2k0300-dl2k0300b-audio-card", ++ .data = &ls2k0300_dl2k0300b_card_config ++ }, + { /* sentinel */ }, + }; + MODULE_DEVICE_TABLE(of, loongson_asoc_dt_ids); +diff --git a/sound/soc/loongson/loongson_i2s_plat.c b/sound/soc/loongson/loongson_i2s_plat.c +--- a/sound/soc/loongson/loongson_i2s_plat.c ++++ b/sound/soc/loongson/loongson_i2s_plat.c +@@ -2,7 +2,7 @@ + // + // Loongson I2S controller master mode dirver(platform device) + // +-// Copyright (C) 2023-2024 Loongson Technology Corporation Limited ++// Copyright (C) 2023-2026 Loongson Technology Corporation Limited + // + // Author: Yingkun Meng + // Binbin Zhou +@@ -21,6 +21,7 @@ + #include "loongson_i2s.h" + #include "loongson_dma.h" + ++/* Loongson-2K1000 APBDMA routing */ + #define LOONGSON_I2S_RX_DMA_OFFSET 21 + #define LOONGSON_I2S_TX_DMA_OFFSET 18 + +@@ -30,6 +31,11 @@ + #define LOONGSON_DMA3_CONF 0x3 + #define LOONGSON_DMA4_CONF 0x4 + ++struct loongson_i2s_plat_config { ++ int rev_id; ++ int (*i2s_dma_config)(struct platform_device *pdev); ++}; ++ + static int loongson_i2s_apbdma_config(struct platform_device *pdev) + { + int val; +@@ -47,8 +53,18 @@ static int loongson_i2s_apbdma_config(struct platform_device *pdev) + return 0; + } + ++static const struct loongson_i2s_plat_config ls2k0300_i2s_plat_config = { ++ .rev_id = 1, ++}; ++ ++static const struct loongson_i2s_plat_config ls2k1000_i2s_plat_config = { ++ .rev_id = 0, ++ .i2s_dma_config = loongson_i2s_apbdma_config, ++}; ++ + static int loongson_i2s_plat_probe(struct platform_device *pdev) + { ++ const struct loongson_i2s_plat_config *plat_config; + struct device *dev = &pdev->dev; + struct loongson_i2s *i2s; + struct resource *res; +@@ -59,12 +75,17 @@ static int loongson_i2s_plat_probe(struct platform_device *pdev) + if (!i2s) + return -ENOMEM; + +- ret = loongson_i2s_apbdma_config(pdev); +- if (ret) +- return ret; ++ plat_config = device_get_match_data(dev); ++ if (!plat_config) ++ return -EINVAL; + +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- i2s->reg_base = devm_ioremap_resource(&pdev->dev, res); ++ if (plat_config->i2s_dma_config) { ++ ret = plat_config->i2s_dma_config(pdev); ++ if (ret) ++ return ret; ++ } ++ ++ i2s->reg_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(i2s->reg_base)) + return dev_err_probe(dev, PTR_ERR(i2s->reg_base), + "devm_ioremap_resource failed\n"); +@@ -87,11 +108,17 @@ static int loongson_i2s_plat_probe(struct platform_device *pdev) + if (IS_ERR(i2s_clk)) + return dev_err_probe(dev, PTR_ERR(i2s_clk), "clock property invalid\n"); + i2s->clk_rate = clk_get_rate(i2s_clk); ++ i2s->rev_id = plat_config->rev_id; + + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + dev_set_name(dev, LS_I2S_DRVNAME); + dev_set_drvdata(dev, i2s); + ++ if (i2s->rev_id == 1) { ++ regmap_update_bits(i2s->regmap, LS_I2S_CTRL, I2S_CTRL_RESET, I2S_CTRL_RESET); ++ fsleep(200); ++ } ++ + ret = devm_snd_soc_register_component(dev, &loongson_i2s_edma_component, + &loongson_i2s_dai, 1); + if (ret) +@@ -102,7 +129,8 @@ static int loongson_i2s_plat_probe(struct platform_device *pdev) + } + + static const struct of_device_id loongson_i2s_ids[] = { +- { .compatible = "loongson,ls2k1000-i2s" }, ++ { .compatible = "loongson,ls2k0300-i2s", .data = &ls2k0300_i2s_plat_config }, ++ { .compatible = "loongson,ls2k1000-i2s", .data = &ls2k1000_i2s_plat_config }, + { /* sentinel */ }, + }; + MODULE_DEVICE_TABLE(of, loongson_i2s_ids); +diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig +--- a/sound/soc/mediatek/Kconfig ++++ b/sound/soc/mediatek/Kconfig +@@ -7,7 +7,7 @@ config SND_SOC_MEDIATEK + + config SND_SOC_MT2701 + tristate "ASoC support for Mediatek MT2701 chip" +- depends on ARCH_MEDIATEK ++ depends on ARCH_MEDIATEK || COMPILE_TEST + select SND_SOC_MEDIATEK + help + This adds ASoC driver for Mediatek MT2701 boards +diff --git a/sound/soc/mediatek/common/mtk-dsp-sof-common.c b/sound/soc/mediatek/common/mtk-dsp-sof-common.c +--- a/sound/soc/mediatek/common/mtk-dsp-sof-common.c ++++ b/sound/soc/mediatek/common/mtk-dsp-sof-common.c +@@ -232,6 +232,7 @@ int mtk_sof_dailink_parse_of(struct device *dev, struct snd_soc_card *card, + const char *propname) + { + struct device_node *np = dev->of_node; ++ struct snd_soc_dai_link *dai_link; + struct snd_soc_dai_link *parsed_dai_link; + const char *dai_name = NULL; + int i, j, ret, num_links, parsed_num_links = 0; +@@ -254,9 +255,9 @@ int mtk_sof_dailink_parse_of(struct device *dev, struct snd_soc_card *card, + return ret; + } + dev_dbg(dev, "ASoC: Property get dai_name:%s\n", dai_name); +- for (j = 0; j < card->num_links; j++) { +- if (!strcmp(dai_name, card->dai_link[j].name)) { +- memcpy(&parsed_dai_link[parsed_num_links++], &card->dai_link[j], ++ for_each_card_prelinks(card, j, dai_link) { ++ if (!strcmp(dai_name, dai_link->name)) { ++ memcpy(&parsed_dai_link[parsed_num_links++], dai_link, + sizeof(struct snd_soc_dai_link)); + break; + } +diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c +--- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c ++++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c +@@ -315,7 +315,7 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev) + &mt8173_rt5650_priv.pll_from); + if (ret) { + dev_err(&pdev->dev, +- "%s snd_soc_register_card fail %d\n", ++ "%s device_property_read_u32() fail %d\n", + __func__, ret); + } + } +diff --git a/sound/soc/mediatek/mt8189/mt8189-dai-adda.c b/sound/soc/mediatek/mt8189/mt8189-dai-adda.c +--- a/sound/soc/mediatek/mt8189/mt8189-dai-adda.c ++++ b/sound/soc/mediatek/mt8189/mt8189-dai-adda.c +@@ -385,8 +385,6 @@ static int mtk_adda_pad_top_event(struct snd_soc_dapm_widget *w, + if (event == SND_SOC_DAPM_PRE_PMU) { + if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2_CLK_P2) + regmap_write(afe->regmap, AFE_AUD_PAD_TOP_CFG0, 0xB8); +- else if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2) +- regmap_write(afe->regmap, AFE_AUD_PAD_TOP_CFG0, 0xB0); + else + regmap_write(afe->regmap, AFE_AUD_PAD_TOP_CFG0, 0xB0); + } +diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c +--- a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c ++++ b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c +@@ -1019,7 +1019,7 @@ static struct snd_soc_card mt8192_mt6359_rt1015p_rt5682x_card = { + .num_dapm_routes = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682x_routes), + }; + +-static int mt8192_mt6359_card_set_be_link(struct snd_soc_card *card, ++static int mt8192_mt6359_card_set_be_link(struct device *dev, + struct snd_soc_dai_link *link, + struct device_node *node, + char *link_name) +@@ -1027,9 +1027,9 @@ static int mt8192_mt6359_card_set_be_link(struct snd_soc_card *card, + int ret; + + if (node && strcmp(link->name, link_name) == 0) { +- ret = snd_soc_of_get_dai_link_codecs(card->dev, node, link); ++ ret = snd_soc_of_get_dai_link_codecs(dev, node, link); + if (ret < 0) { +- dev_err_probe(card->dev, ret, "get dai link codecs fail\n"); ++ dev_err_probe(dev, ret, "get dai link codecs fail\n"); + return ret; + } + } +@@ -1065,21 +1065,21 @@ static int mt8192_mt6359_legacy_probe(struct mtk_soc_card_data *soc_card_data) + } + + for_each_card_prelinks(card, i, dai_link) { +- ret = mt8192_mt6359_card_set_be_link(card, dai_link, speaker_codec, "I2S3"); ++ ret = mt8192_mt6359_card_set_be_link(dev, dai_link, speaker_codec, "I2S3"); + if (ret) { + dev_err_probe(dev, ret, "%s set speaker_codec fail\n", + dai_link->name); + break; + } + +- ret = mt8192_mt6359_card_set_be_link(card, dai_link, headset_codec, "I2S8"); ++ ret = mt8192_mt6359_card_set_be_link(dev, dai_link, headset_codec, "I2S8"); + if (ret) { + dev_err_probe(dev, ret, "%s set headset_codec fail\n", + dai_link->name); + break; + } + +- ret = mt8192_mt6359_card_set_be_link(card, dai_link, headset_codec, "I2S9"); ++ ret = mt8192_mt6359_card_set_be_link(dev, dai_link, headset_codec, "I2S9"); + if (ret) { + dev_err_probe(dev, ret, "%s set headset_codec fail\n", + dai_link->name); +diff --git a/sound/soc/mediatek/mt8196/mt8196-nau8825.c b/sound/soc/mediatek/mt8196/mt8196-nau8825.c +--- a/sound/soc/mediatek/mt8196/mt8196-nau8825.c ++++ b/sound/soc/mediatek/mt8196/mt8196-nau8825.c +@@ -104,8 +104,6 @@ static const struct snd_kcontrol_new mt8196_nau8825_controls[] = { + + #define EXT_SPK_AMP_W_NAME "Ext_Speaker_Amp" + +-static struct snd_soc_card mt8196_nau8825_soc_card; +- + static const struct snd_soc_dapm_widget mt8196_nau8825_card_widgets[] = { + /* SOF Uplink */ + SND_SOC_DAPM_MIXER("SOF_DMA_UL0", SND_SOC_NOPM, 0, 0, NULL, 0), +diff --git a/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c b/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c +--- a/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c ++++ b/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c +@@ -2120,19 +2120,15 @@ static int mt8365_afe_pcm_dev_probe(struct platform_device *pdev) + spin_lock_init(&afe_priv->afe_ctrl_lock); + mutex_init(&afe_priv->afe_clk_mutex); + +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- afe->base_addr = devm_ioremap_resource(&pdev->dev, res); ++ afe->base_addr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(afe->base_addr)) + return PTR_ERR(afe->base_addr); + +- res = platform_get_resource(pdev, IORESOURCE_MEM, 1); +- if (res) { +- afe_priv->afe_sram_vir_addr = +- devm_ioremap_resource(&pdev->dev, res); +- if (!IS_ERR(afe_priv->afe_sram_vir_addr)) { +- afe_priv->afe_sram_phy_addr = res->start; +- afe_priv->afe_sram_size = resource_size(res); +- } ++ afe_priv->afe_sram_vir_addr = ++ devm_platform_get_and_ioremap_resource(pdev, 1, &res); ++ if (!IS_ERR(afe_priv->afe_sram_vir_addr)) { ++ afe_priv->afe_sram_phy_addr = res->start; ++ afe_priv->afe_sram_size = resource_size(res); + } + + /* initial audio related clock */ +diff --git a/sound/soc/mediatek/mt8365/mt8365-mt6357.c b/sound/soc/mediatek/mt8365/mt8365-mt6357.c +--- a/sound/soc/mediatek/mt8365/mt8365-mt6357.c ++++ b/sound/soc/mediatek/mt8365/mt8365-mt6357.c +@@ -70,7 +70,8 @@ static const struct snd_soc_dapm_route mt8365_mt6357_routes[] = { + static int mt8365_mt6357_int_adda_startup(struct snd_pcm_substream *substream) + { + struct snd_soc_pcm_runtime *rtd = substream->private_data; +- struct mt8365_mt6357_priv *priv = snd_soc_card_get_drvdata(rtd->card); ++ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card); ++ struct mt8365_mt6357_priv *priv = soc_card_data->mach_priv; + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +@@ -101,7 +102,8 @@ static int mt8365_mt6357_int_adda_startup(struct snd_pcm_substream *substream) + static void mt8365_mt6357_int_adda_shutdown(struct snd_pcm_substream *substream) + { + struct snd_soc_pcm_runtime *rtd = substream->private_data; +- struct mt8365_mt6357_priv *priv = snd_soc_card_get_drvdata(rtd->card); ++ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card); ++ struct mt8365_mt6357_priv *priv = soc_card_data->mach_priv; + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +@@ -245,7 +247,8 @@ static struct snd_soc_dai_link mt8365_mt6357_dais[] = { + + static int mt8365_mt6357_gpio_probe(struct snd_soc_card *card) + { +- struct mt8365_mt6357_priv *priv = snd_soc_card_get_drvdata(card); ++ struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card); ++ struct mt8365_mt6357_priv *priv = soc_card_data->mach_priv; + struct device *dev = card->dev; + int ret, i; + +@@ -294,7 +297,6 @@ static int mt8365_mt6357_dev_probe(struct mtk_soc_card_data *soc_card_data, bool + struct mt8365_mt6357_priv *mach_priv; + int ret; + +- card->dev = dev; + ret = parse_dai_link_info(card); + if (ret) + goto err; +diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile +--- a/sound/soc/meson/Makefile ++++ b/sound/soc/meson/Makefile +@@ -4,6 +4,8 @@ snd-soc-meson-aiu-y := aiu.o + snd-soc-meson-aiu-y += aiu-acodec-ctrl.o + snd-soc-meson-aiu-y += aiu-codec-ctrl.o + snd-soc-meson-aiu-y += aiu-encoder-i2s.o ++snd-soc-meson-aiu-y += gx-formatter.o ++snd-soc-meson-aiu-y += aiu-formatter-i2s.o + snd-soc-meson-aiu-y += aiu-encoder-spdif.o + snd-soc-meson-aiu-y += aiu-fifo.o + snd-soc-meson-aiu-y += aiu-fifo-i2s.o +diff --git a/sound/soc/meson/aiu-encoder-i2s.c b/sound/soc/meson/aiu-encoder-i2s.c +--- a/sound/soc/meson/aiu-encoder-i2s.c ++++ b/sound/soc/meson/aiu-encoder-i2s.c +@@ -10,14 +10,11 @@ + #include + + #include "aiu.h" ++#include "gx-formatter.h" ++#include "gx-interface.h" + +-#define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0) +-#define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5) +-#define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9) + #define AIU_I2S_SOURCE_DESC_MODE_SPLIT BIT(11) +-#define AIU_RST_SOFT_I2S_FAST BIT(0) + +-#define AIU_I2S_DAC_CFG_MSB_FIRST BIT(2) + #define AIU_CLK_CTRL_I2S_DIV_EN BIT(0) + #define AIU_CLK_CTRL_I2S_DIV GENMASK(3, 2) + #define AIU_CLK_CTRL_AOCLK_INVERT BIT(6) +@@ -35,49 +32,6 @@ static void aiu_encoder_i2s_divider_enable(struct snd_soc_component *component, + enable ? AIU_CLK_CTRL_I2S_DIV_EN : 0); + } + +-static int aiu_encoder_i2s_setup_desc(struct snd_soc_component *component, +- struct snd_pcm_hw_params *params) +-{ +- /* Always operate in split (classic interleaved) mode */ +- unsigned int desc = AIU_I2S_SOURCE_DESC_MODE_SPLIT; +- +- /* Reset required to update the pipeline */ +- snd_soc_component_write(component, AIU_RST_SOFT, AIU_RST_SOFT_I2S_FAST); +- snd_soc_component_read(component, AIU_I2S_SYNC); +- +- switch (params_physical_width(params)) { +- case 16: /* Nothing to do */ +- break; +- +- case 32: +- desc |= (AIU_I2S_SOURCE_DESC_MODE_24BIT | +- AIU_I2S_SOURCE_DESC_MODE_32BIT); +- break; +- +- default: +- return -EINVAL; +- } +- +- switch (params_channels(params)) { +- case 2: /* Nothing to do */ +- break; +- case 8: +- desc |= AIU_I2S_SOURCE_DESC_MODE_8CH; +- break; +- default: +- return -EINVAL; +- } +- +- snd_soc_component_update_bits(component, AIU_I2S_SOURCE_DESC, +- AIU_I2S_SOURCE_DESC_MODE_8CH | +- AIU_I2S_SOURCE_DESC_MODE_24BIT | +- AIU_I2S_SOURCE_DESC_MODE_32BIT | +- AIU_I2S_SOURCE_DESC_MODE_SPLIT, +- desc); +- +- return 0; +-} +- + static int aiu_encoder_i2s_set_legacy_div(struct snd_soc_component *component, + struct snd_pcm_hw_params *params, + unsigned int bs) +@@ -108,6 +62,32 @@ static int aiu_encoder_i2s_set_legacy_div(struct snd_soc_component *component, + return 0; + } + ++/* ++ * Return true if the given combination of channels and sample width requires ++ * the bs quirk. Return false otherwise. ++ */ ++static bool aiu_encoder_is_bs_quirk(unsigned int channels, int width) ++{ ++ return (channels == 8) && (width == 16); ++} ++ ++static int aiu_encoder_check_bs_quirk(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ struct gx_stream *other_stream = snd_soc_dai_dma_data_get(dai, !substream->stream); ++ ++ /* Nothing to do if the other stream doesn't exist or it's not configured yet. */ ++ if (!other_stream || !other_stream->channels) ++ return 0; ++ ++ if (aiu_encoder_is_bs_quirk(other_stream->channels, other_stream->width) != ++ aiu_encoder_is_bs_quirk(params_channels(params), params_width(params))) ++ return -EINVAL; ++ ++ return 0; ++} ++ + static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component, + struct snd_pcm_hw_params *params, + unsigned int bs) +@@ -119,7 +99,7 @@ static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component, + * increased by 50% to get the correct output rate. + * No idea why ! + */ +- if (params_width(params) == 16 && params_channels(params) == 8) { ++ if (aiu_encoder_is_bs_quirk(params_channels(params), params_width(params))) { + if (bs % 2) { + dev_err(component->dev, + "Cannot increase i2s divider by 50%%\n"); +@@ -141,25 +121,23 @@ static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component, + return 0; + } + +-static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component, +- struct snd_pcm_hw_params *params) ++static int aiu_encoder_i2s_set_clocks(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) + { ++ struct snd_soc_component *component = dai->component; + struct aiu *aiu = snd_soc_component_get_drvdata(component); ++ struct gx_iface *iface = &aiu->i2s.iface; + unsigned int srate = params_rate(params); + unsigned int fs, bs; + int ret; + + /* Get the oversampling factor */ +- fs = DIV_ROUND_CLOSEST(clk_get_rate(aiu->i2s.clks[MCLK].clk), srate); ++ fs = DIV_ROUND_CLOSEST(iface->mclk_rate, srate); + +- if (fs % 64) ++ if ((fs % 64) || (fs == 0)) + return -EINVAL; + +- /* Send data MSB first */ +- snd_soc_component_update_bits(component, AIU_I2S_DAC_CFG, +- AIU_I2S_DAC_CFG_MSB_FIRST, +- AIU_I2S_DAC_CFG_MSB_FIRST); +- + /* Set bclk to lrlck ratio */ + snd_soc_component_update_bits(component, AIU_CODEC_DAC_LRCLK_CTRL, + AIU_CODEC_DAC_LRCLK_CTRL_DIV, +@@ -168,10 +146,22 @@ static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component, + + bs = fs / 64; + +- if (aiu->platform->has_clk_ctrl_more_i2s_div) ++ if (aiu->platform->has_clk_ctrl_more_i2s_div) { ++ /* ++ * The hw rules added in startup() make this unreachable in the ++ * sequential case, but both streams may be refined concurrently ++ * before either commits its config, since only ops->hw_params ++ * runs under the card's pcm_mutex. Re-check against the committed ++ * state of the other stream, which is stable under that mutex. ++ */ ++ if (aiu_encoder_check_bs_quirk(substream, params, dai)) { ++ dev_err(dai->dev, "bclk requirements incompatible with other stream\n"); ++ return -EINVAL; ++ } + ret = aiu_encoder_i2s_set_more_div(component, params, bs); +- else ++ } else { + ret = aiu_encoder_i2s_set_legacy_div(component, params, bs); ++ } + + if (ret) + return ret; +@@ -188,23 +178,37 @@ static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) + { ++ struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); ++ int ret; ++ ++ ret = aiu_encoder_i2s_set_clocks(substream, params, dai); ++ if (ret) { ++ dev_err(dai->dev, "setting i2s clocks failed: %d\n", ret); ++ return ret; ++ } ++ ++ ts->physical_width = params_physical_width(params); ++ ts->width = params_width(params); ++ ts->channels = params_channels(params); ++ ++ return 0; ++} ++ ++static int aiu_encoder_i2s_prepare(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); + struct snd_soc_component *component = dai->component; + int ret; + +- /* Disable the clock while changing the settings */ +- aiu_encoder_i2s_divider_enable(component, false); ++ if (ts->clk_enabled) ++ return 0; + +- ret = aiu_encoder_i2s_setup_desc(component, params); +- if (ret) { +- dev_err(dai->dev, "setting i2s desc failed\n"); ++ ret = clk_prepare_enable(ts->iface->mclk); ++ if (ret) + return ret; +- } + +- ret = aiu_encoder_i2s_set_clocks(component, params); +- if (ret) { +- dev_err(dai->dev, "setting i2s clocks failed\n"); +- return ret; +- } ++ ts->clk_enabled = true; + + aiu_encoder_i2s_divider_enable(component, true); + +@@ -214,9 +218,24 @@ static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream, + static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + { ++ struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); + struct snd_soc_component *component = dai->component; + +- aiu_encoder_i2s_divider_enable(component, false); ++ /* ++ * If this is the last substream being closed then disable the i2s ++ * clock divider. ++ */ ++ if (snd_soc_dai_active(dai) <= 1) ++ aiu_encoder_i2s_divider_enable(component, 0); ++ ++ if (ts->clk_enabled) { ++ clk_disable_unprepare(ts->iface->mclk); ++ ts->clk_enabled = false; ++ } ++ ++ ts->channels = 0; ++ ts->width = 0; ++ ts->physical_width = 0; + + return 0; + } +@@ -224,6 +243,8 @@ static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream, + static int aiu_encoder_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) + { + struct snd_soc_component *component = dai->component; ++ struct aiu *aiu = snd_soc_component_get_drvdata(component); ++ struct gx_iface *iface = &aiu->i2s.iface; + unsigned int inv = fmt & SND_SOC_DAIFMT_INV_MASK; + unsigned int val = 0; + unsigned int skew; +@@ -255,9 +276,12 @@ static int aiu_encoder_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) + skew = 0; + break; + default: ++ dev_err(dai->dev, "unsupported dai format\n"); + return -EINVAL; + } + ++ iface->fmt = fmt; ++ + val |= FIELD_PREP(AIU_CLK_CTRL_LRCLK_SKEW, skew); + snd_soc_component_update_bits(component, AIU_CLK_CTRL, + AIU_CLK_CTRL_LRCLK_INVERT | +@@ -272,6 +296,7 @@ static int aiu_encoder_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) + { + struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); ++ struct gx_iface *iface = &aiu->i2s.iface; + int ret; + + if (WARN_ON(clk_id != 0)) +@@ -280,11 +305,15 @@ static int aiu_encoder_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, + if (dir == SND_SOC_CLOCK_IN) + return 0; + +- ret = clk_set_rate(aiu->i2s.clks[MCLK].clk, freq); +- if (ret) +- dev_err(dai->dev, "Failed to set sysclk to %uHz", freq); ++ ret = clk_set_rate(iface->mclk, freq); ++ if (ret) { ++ dev_err(dai->dev, "Failed to set sysclk to %uHz: %d", freq, ret); ++ return ret; ++ } + +- return ret; ++ iface->mclk_rate = freq; ++ ++ return 0; + } + + static const unsigned int hw_channels[] = {2, 8}; +@@ -294,10 +323,45 @@ static const struct snd_pcm_hw_constraint_list hw_channel_constraints = { + .mask = 0, + }; + ++static int aiu_encoder_i2s_pcm_hw_rule(struct snd_pcm_hw_params *params, ++ struct snd_pcm_hw_rule *rule) ++{ ++ struct gx_stream *other = rule->private; ++ struct snd_interval *ch = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); ++ /* ++ * The quirk is technically based on the significant bits whereas here ++ * we're using the physical width for simplicity. This works because ++ * S16_LE is the only format supported by this encoder that has: ++ * significant bits = physical width = 16-bits ++ */ ++ struct snd_interval *phys_width = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); ++ struct snd_interval new_i; ++ ++ if (other->channels == 0) ++ return 0; ++ ++ snd_interval_any(&new_i); ++ ++ if (rule->var == SNDRV_PCM_HW_PARAM_CHANNELS) { ++ if (aiu_encoder_is_bs_quirk(other->channels, other->width)) ++ new_i.min = new_i.max = 8; ++ else if (snd_interval_single(phys_width) && phys_width->min == 16) ++ new_i.max = 2; /* Force 2ch */ ++ } else { /* SNDRV_PCM_HW_PARAM_SAMPLE_BITS */ ++ if (aiu_encoder_is_bs_quirk(other->channels, other->width)) ++ new_i.min = new_i.max = 16; ++ else if (snd_interval_single(ch) && ch->min == 8) ++ new_i.min = 17; /* Request physical width > 16 bits */ ++ } ++ ++ return snd_interval_refine(hw_param_interval(params, rule->var), &new_i); ++} ++ + static int aiu_encoder_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + { + struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); ++ struct gx_stream *other_stream = snd_soc_dai_dma_data_get(dai, !substream->stream); + int ret; + + /* Make sure the encoder gets either 2 or 8 channels */ +@@ -305,15 +369,79 @@ static int aiu_encoder_i2s_startup(struct snd_pcm_substream *substream, + SNDRV_PCM_HW_PARAM_CHANNELS, + &hw_channel_constraints); + if (ret) { +- dev_err(dai->dev, "adding channels constraints failed\n"); ++ dev_err(dai->dev, "adding channels constraints failed: %d\n", ret); + return ret; + } + +- ret = clk_bulk_prepare_enable(aiu->i2s.clk_num, aiu->i2s.clks); +- if (ret) +- dev_err(dai->dev, "failed to enable i2s clocks\n"); ++ /* ++ * If DAI supports both playback and capture streams ensure the bs-quirk is ++ * handled correctly. ++ * This is only valid for GX platforms (has_clk_ctrl_more_i2s_div=true). ++ */ ++ if (aiu->platform->has_clk_ctrl_more_i2s_div && other_stream) { ++ ret = snd_pcm_hw_rule_add(substream->runtime, 0, ++ SNDRV_PCM_HW_PARAM_CHANNELS, ++ aiu_encoder_i2s_pcm_hw_rule, ++ other_stream, ++ SNDRV_PCM_HW_PARAM_CHANNELS, ++ SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); ++ if (ret) ++ return ret; + +- return ret; ++ ret = snd_pcm_hw_rule_add(substream->runtime, 0, ++ SNDRV_PCM_HW_PARAM_SAMPLE_BITS, ++ aiu_encoder_i2s_pcm_hw_rule, ++ other_stream, ++ SNDRV_PCM_HW_PARAM_CHANNELS, ++ SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); ++ if (ret) ++ return ret; ++ } ++ ++ /* ++ * Enable only clocks which are required for the interface internal ++ * logic. MCLK is enabled/disabled from the formatter and the I2S ++ * divider is enabled/disabled in "hw_params"/"hw_free", respectively. ++ */ ++ ret = clk_prepare_enable(aiu->i2s.clks[PCLK].clk); ++ if (ret) { ++ dev_err(dai->dev, "failed to enable PCLK: %d\n", ret); ++ return ret; ++ } ++ ret = clk_prepare_enable(aiu->i2s.clks[MIXER].clk); ++ if (ret) { ++ dev_err(dai->dev, "failed to enable MIXER: %d\n", ret); ++ clk_disable_unprepare(aiu->i2s.clks[PCLK].clk); ++ return ret; ++ } ++ ret = clk_prepare_enable(aiu->i2s.clks[AOCLK].clk); ++ if (ret) { ++ dev_err(dai->dev, "failed to enable AOCLK: %d\n", ret); ++ clk_disable_unprepare(aiu->i2s.clks[MIXER].clk); ++ clk_disable_unprepare(aiu->i2s.clks[PCLK].clk); ++ return ret; ++ } ++ ++ /* ++ * We're always operating in split mode for the playback stream. ++ * ++ * This setting arguably belong to the 'aiu-formatter', but it's kept ++ * here for backward compatibility reason. At reset the I2S encoder ++ * operates in normal mode which would only support 8ch, but by default ++ * only 2ch are enabled. If a playback stream is started without ++ * changing to split mode, then the I2S encoder doesn't consume audio ++ * samples and the playback fails. ++ * Moving this to 'aiu-formatter' would cause the split mode to be set ++ * only when the formatter is enabled, which doesn't happen at boot as ++ * the default value for "HDMI CTRL SRC" is "DISABLED". ++ */ ++ ret = snd_soc_component_update_bits(dai->component, AIU_I2S_SOURCE_DESC, ++ AIU_I2S_SOURCE_DESC_MODE_SPLIT, ++ AIU_I2S_SOURCE_DESC_MODE_SPLIT); ++ if (ret < 0) ++ dev_err(dai->dev, "failed to update AIU_I2S_SOURCE_DESC: %d", ret); ++ ++ return 0; + } + + static void aiu_encoder_i2s_shutdown(struct snd_pcm_substream *substream, +@@ -321,14 +449,89 @@ static void aiu_encoder_i2s_shutdown(struct snd_pcm_substream *substream, + { + struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); + +- clk_bulk_disable_unprepare(aiu->i2s.clk_num, aiu->i2s.clks); ++ clk_disable_unprepare(aiu->i2s.clks[AOCLK].clk); ++ clk_disable_unprepare(aiu->i2s.clks[MIXER].clk); ++ clk_disable_unprepare(aiu->i2s.clks[PCLK].clk); ++} ++ ++static int aiu_encoder_i2s_trigger(struct snd_pcm_substream *substream, ++ int cmd, ++ struct snd_soc_dai *dai) ++{ ++ struct gx_stream *ts = snd_soc_dai_get_dma_data(dai, substream); ++ int ret; ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ ret = gx_stream_start(ts); ++ break; ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ case SNDRV_PCM_TRIGGER_STOP: ++ gx_stream_stop(ts); ++ ret = 0; ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static int aiu_encoder_i2s_remove_dai(struct snd_soc_dai *dai) ++{ ++ int stream; ++ ++ for_each_pcm_streams(stream) { ++ struct gx_stream *ts; ++ ++ ts = snd_soc_dai_dma_data_get(dai, stream); ++ if (ts) ++ gx_stream_free(ts); ++ ++ snd_soc_dai_dma_data_set(dai, stream, NULL); ++ } ++ ++ return 0; ++} ++ ++static int aiu_encoder_i2s_probe_dai(struct snd_soc_dai *dai) ++{ ++ struct aiu *aiu = snd_soc_dai_get_drvdata(dai); ++ struct gx_iface *iface = &aiu->i2s.iface; ++ int stream; ++ ++ for_each_pcm_streams(stream) { ++ struct gx_stream *ts; ++ ++ if (!snd_soc_dai_get_widget(dai, stream)) ++ continue; ++ ++ ts = gx_stream_alloc(iface); ++ if (!ts) { ++ aiu_encoder_i2s_remove_dai(dai); ++ return -ENOMEM; ++ } ++ snd_soc_dai_dma_data_set(dai, stream, ts); ++ } ++ ++ iface->mclk = aiu->i2s.clks[MCLK].clk; ++ iface->mclk_rate = clk_get_rate(iface->mclk); ++ ++ return 0; + } + + const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops = { ++ .probe = aiu_encoder_i2s_probe_dai, ++ .remove = aiu_encoder_i2s_remove_dai, + .hw_params = aiu_encoder_i2s_hw_params, ++ .prepare = aiu_encoder_i2s_prepare, + .hw_free = aiu_encoder_i2s_hw_free, + .set_fmt = aiu_encoder_i2s_set_fmt, + .set_sysclk = aiu_encoder_i2s_set_sysclk, + .startup = aiu_encoder_i2s_startup, + .shutdown = aiu_encoder_i2s_shutdown, ++ .trigger = aiu_encoder_i2s_trigger, + }; +diff --git a/sound/soc/meson/aiu-formatter-i2s.c b/sound/soc/meson/aiu-formatter-i2s.c +new file mode 100644 +--- /dev/null ++++ b/sound/soc/meson/aiu-formatter-i2s.c +@@ -0,0 +1,103 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// ++// Copyright (c) 2026 BayLibre, SAS. ++// Author: Valerio Setti ++ ++#include ++#include ++#include ++ ++#include "aiu.h" ++#include "gx-formatter.h" ++ ++#define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0) ++#define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5) ++#define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9) ++ ++#define AIU_I2S_DAC_CFG_MSB_FIRST BIT(2) ++ ++static struct snd_soc_dai * ++aiu_formatter_i2s_get_be(struct snd_soc_dapm_widget *w) ++{ ++ struct snd_soc_dapm_path *p; ++ struct snd_soc_dai *be; ++ ++ snd_soc_dapm_widget_for_each_sink_path(w, p) { ++ if (!p->connect) ++ continue; ++ ++ if (p->sink->id == snd_soc_dapm_dai_in) ++ return (struct snd_soc_dai *)p->sink->priv; ++ ++ be = aiu_formatter_i2s_get_be(p->sink); ++ if (be) ++ return be; ++ } ++ ++ return NULL; ++} ++ ++static struct gx_stream * ++aiu_formatter_i2s_get_stream(struct snd_soc_dapm_widget *w) ++{ ++ struct snd_soc_dai *be = aiu_formatter_i2s_get_be(w); ++ ++ if (!be) ++ return NULL; ++ ++ return snd_soc_dai_dma_data_get_playback(be); ++} ++ ++static int aiu_formatter_i2s_prepare(struct regmap *map, ++ const struct gx_formatter_hw *quirks, ++ struct gx_stream *ts) ++{ ++ /* Always operate in split (classic interleaved) mode */ ++ unsigned int desc = 0; ++ ++ /* ++ * Pipeline reset is already implemented in aiu_fifo_i2s_trigger() at ++ * trigger time. ++ */ ++ ++ switch (ts->physical_width) { ++ case 16: /* Nothing to do */ ++ break; ++ ++ case 32: ++ desc |= (AIU_I2S_SOURCE_DESC_MODE_24BIT | ++ AIU_I2S_SOURCE_DESC_MODE_32BIT); ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ switch (ts->channels) { ++ case 2: /* Nothing to do */ ++ break; ++ case 8: ++ desc |= AIU_I2S_SOURCE_DESC_MODE_8CH; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ regmap_update_bits(map, AIU_I2S_SOURCE_DESC, ++ AIU_I2S_SOURCE_DESC_MODE_8CH | ++ AIU_I2S_SOURCE_DESC_MODE_24BIT | ++ AIU_I2S_SOURCE_DESC_MODE_32BIT, ++ desc); ++ ++ /* Send data MSB first */ ++ regmap_update_bits(map, AIU_I2S_DAC_CFG, ++ AIU_I2S_DAC_CFG_MSB_FIRST, ++ AIU_I2S_DAC_CFG_MSB_FIRST); ++ ++ return 0; ++} ++ ++const struct gx_formatter_ops aiu_formatter_i2s_ops = { ++ .get_stream = aiu_formatter_i2s_get_stream, ++ .prepare = aiu_formatter_i2s_prepare, ++}; +diff --git a/sound/soc/meson/aiu.c b/sound/soc/meson/aiu.c +--- a/sound/soc/meson/aiu.c ++++ b/sound/soc/meson/aiu.c +@@ -29,13 +29,22 @@ static SOC_ENUM_SINGLE_DECL(aiu_spdif_encode_sel_enum, AIU_I2S_MISC, + static const struct snd_kcontrol_new aiu_spdif_encode_mux = + SOC_DAPM_ENUM("SPDIF Buffer Src", aiu_spdif_encode_sel_enum); + +-static const struct snd_soc_dapm_widget aiu_cpu_dapm_widgets[] = { +- SND_SOC_DAPM_MUX("SPDIF SRC SEL", SND_SOC_NOPM, 0, 0, +- &aiu_spdif_encode_mux), ++#define AIU_WIDGET_SPDIF_SRC_SEL 0 ++#define AIU_WIDGET_I2S_FORMATTER 1 ++ ++static struct snd_soc_dapm_widget aiu_cpu_dapm_widgets[] = { ++ [AIU_WIDGET_SPDIF_SRC_SEL] = ++ SND_SOC_DAPM_MUX("SPDIF SRC SEL", SND_SOC_NOPM, 0, 0, ++ &aiu_spdif_encode_mux), ++ [AIU_WIDGET_I2S_FORMATTER] = ++ SND_SOC_DAPM_PGA_E("I2S Formatter", SND_SOC_NOPM, 0, 0, NULL, 0, ++ gx_formatter_event, ++ (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD)), + }; + + static const struct snd_soc_dapm_route aiu_cpu_dapm_routes[] = { +- { "I2S Encoder Playback", NULL, "I2S FIFO Playback" }, ++ { "I2S Formatter", NULL, "I2S FIFO Playback" }, ++ { "I2S Encoder Playback", NULL, "I2S Formatter" }, + { "SPDIF SRC SEL", "SPDIF", "SPDIF FIFO Playback" }, + { "SPDIF SRC SEL", "I2S", "I2S FIFO Playback" }, + { "SPDIF Encoder Playback", NULL, "SPDIF SRC SEL" }, +@@ -145,6 +154,7 @@ static struct snd_soc_dai_driver aiu_cpu_dai_drv[] = { + .formats = AIU_FORMATS, + }, + .ops = &aiu_encoder_i2s_dai_ops, ++ .symmetric_rate = 1, + }, + [CPU_SPDIF_ENCODER] = { + .name = "SPDIF Encoder", +@@ -172,6 +182,11 @@ static const struct regmap_config aiu_regmap_cfg = { + .max_register = 0x2ac, + }; + ++static const struct gx_formatter_driver aiu_formatter_i2s_drv = { ++ .regmap_cfg = &aiu_regmap_cfg, ++ .ops = &aiu_formatter_i2s_ops, ++}; ++ + static int aiu_clk_bulk_get(struct device *dev, + const char * const *ids, + unsigned int num, +@@ -282,6 +297,14 @@ static int aiu_probe(struct platform_device *pdev) + if (ret) + return ret; + ++ /* Allocate the aiu-formatter into its widget */ ++ ret = gx_formatter_create(dev, &aiu_cpu_dapm_widgets[AIU_WIDGET_I2S_FORMATTER], ++ &aiu_formatter_i2s_drv, map); ++ if (ret) { ++ dev_err(dev, "Failed to allocate aiu formatter\n"); ++ goto err; ++ } ++ + /* Register the cpu component of the aiu */ + ret = snd_soc_register_component(dev, &aiu_cpu_component, + aiu_cpu_dai_drv, +@@ -310,12 +333,14 @@ static int aiu_probe(struct platform_device *pdev) + + return 0; + err: ++ gx_formatter_free(&aiu_cpu_dapm_widgets[AIU_WIDGET_I2S_FORMATTER]); + snd_soc_unregister_component(dev); + return ret; + } + + static void aiu_remove(struct platform_device *pdev) + { ++ gx_formatter_free(&aiu_cpu_dapm_widgets[AIU_WIDGET_I2S_FORMATTER]); + snd_soc_unregister_component(&pdev->dev); + } + +diff --git a/sound/soc/meson/aiu.h b/sound/soc/meson/aiu.h +--- a/sound/soc/meson/aiu.h ++++ b/sound/soc/meson/aiu.h +@@ -7,6 +7,8 @@ + #ifndef _MESON_AIU_H + #define _MESON_AIU_H + ++#include "gx-formatter.h" ++ + struct clk; + struct clk_bulk_data; + struct device; +@@ -25,6 +27,7 @@ struct aiu_interface { + struct clk_bulk_data *clks; + unsigned int clk_num; + int irq; ++ struct gx_iface iface; + }; + + struct aiu_platform_data { +@@ -58,6 +61,7 @@ extern const struct snd_soc_dai_ops aiu_fifo_i2s_dai_ops; + extern const struct snd_soc_dai_ops aiu_fifo_spdif_dai_ops; + extern const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops; + extern const struct snd_soc_dai_ops aiu_encoder_spdif_dai_ops; ++extern const struct gx_formatter_ops aiu_formatter_i2s_ops; + + #define AIU_IEC958_BPF 0x000 + #define AIU_958_MISC 0x010 +diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c +--- a/sound/soc/meson/axg-card.c ++++ b/sound/soc/meson/axg-card.c +@@ -107,6 +107,7 @@ static int axg_card_add_tdm_loopback(struct snd_soc_card *card, + struct snd_soc_dai_link *pad; + struct snd_soc_dai_link *lb; + struct snd_soc_dai_link_component *dlc; ++ struct device *dev = card->dev; + int ret; + + /* extend links */ +@@ -117,11 +118,11 @@ static int axg_card_add_tdm_loopback(struct snd_soc_card *card, + pad = &card->dai_link[*index]; + lb = &card->dai_link[*index + 1]; + +- lb->name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-lb", pad->name); ++ lb->name = devm_kasprintf(dev, GFP_KERNEL, "%s-lb", pad->name); + if (!lb->name) + return -ENOMEM; + +- dlc = devm_kzalloc(card->dev, sizeof(*dlc), GFP_KERNEL); ++ dlc = devm_kzalloc(dev, sizeof(*dlc), GFP_KERNEL); + if (!dlc) + return -ENOMEM; + +@@ -158,13 +159,14 @@ static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card, + struct device_node *node, + struct axg_dai_link_tdm_data *be) + { ++ struct device *dev = card->dev; + char propname[32]; + u32 tx, rx; + int i; + +- be->tx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES, ++ be->tx_mask = devm_kcalloc(dev, AXG_TDM_NUM_LANES, + sizeof(*be->tx_mask), GFP_KERNEL); +- be->rx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES, ++ be->rx_mask = devm_kcalloc(dev, AXG_TDM_NUM_LANES, + sizeof(*be->rx_mask), GFP_KERNEL); + if (!be->tx_mask || !be->rx_mask) + return -ENOMEM; +@@ -191,7 +193,7 @@ static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card, + + /* ... but the interface should at least have one direction */ + if (!tx && !rx) { +- dev_err(card->dev, "tdm link has no cpu slots\n"); ++ dev_err(dev, "tdm link has no cpu slots\n"); + return -EINVAL; + } + +@@ -207,7 +209,7 @@ static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card, + * Error if the slots can't accommodate the largest mask or + * if it is just too big + */ +- dev_err(card->dev, "bad slot number\n"); ++ dev_err(dev, "bad slot number\n"); + return -EINVAL; + } + +@@ -222,8 +224,9 @@ static int axg_card_parse_codecs_masks(struct snd_soc_card *card, + struct axg_dai_link_tdm_data *be) + { + struct axg_dai_link_tdm_mask *codec_mask; ++ struct device *dev = card->dev; + +- codec_mask = devm_kcalloc(card->dev, link->num_codecs, ++ codec_mask = devm_kcalloc(dev, link->num_codecs, + sizeof(*codec_mask), GFP_KERNEL); + if (!codec_mask) + return -ENOMEM; +@@ -249,10 +252,11 @@ static int axg_card_parse_tdm(struct snd_soc_card *card, + struct meson_card *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_dai_link *link = &card->dai_link[*index]; + struct axg_dai_link_tdm_data *be; ++ struct device *dev = card->dev; + int ret; + + /* Allocate tdm link parameters */ +- be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL); ++ be = devm_kzalloc(dev, sizeof(*be), GFP_KERNEL); + if (!be) + return -ENOMEM; + priv->link_data[*index] = be; +@@ -266,7 +270,7 @@ static int axg_card_parse_tdm(struct snd_soc_card *card, + + ret = axg_card_parse_cpu_tdm_slots(card, link, node, be); + if (ret) { +- dev_err(card->dev, "error parsing tdm link slots\n"); ++ dev_err(dev, "error parsing tdm link slots\n"); + return ret; + } + +@@ -310,9 +314,10 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np, + { + struct snd_soc_dai_link *dai_link = &card->dai_link[*index]; + struct snd_soc_dai_link_component *cpu; ++ struct device *dev = card->dev; + int ret; + +- cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL); ++ cpu = devm_kzalloc(dev, sizeof(*cpu), GFP_KERNEL); + if (!cpu) + return -ENOMEM; + +diff --git a/sound/soc/meson/g12a-toacodec.c b/sound/soc/meson/g12a-toacodec.c +--- a/sound/soc/meson/g12a-toacodec.c ++++ b/sound/soc/meson/g12a-toacodec.c +@@ -312,7 +312,7 @@ static int g12a_toacodec_probe(struct platform_device *pdev) + + ret = device_reset(dev); + if (ret) +- return ret; ++ return dev_err_probe(dev, ret, "failed to reset device\n"); + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) +diff --git a/sound/soc/meson/g12a-tohdmitx.c b/sound/soc/meson/g12a-tohdmitx.c +--- a/sound/soc/meson/g12a-tohdmitx.c ++++ b/sound/soc/meson/g12a-tohdmitx.c +@@ -251,7 +251,7 @@ static int g12a_tohdmitx_probe(struct platform_device *pdev) + + ret = device_reset(dev); + if (ret) +- return ret; ++ return dev_err_probe(dev, ret, "failed to reset device\n"); + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) +diff --git a/sound/soc/meson/gx-card.c b/sound/soc/meson/gx-card.c +--- a/sound/soc/meson/gx-card.c ++++ b/sound/soc/meson/gx-card.c +@@ -48,9 +48,10 @@ static int gx_card_parse_i2s(struct snd_soc_card *card, + struct meson_card *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_dai_link *link = &card->dai_link[*index]; + struct gx_dai_link_i2s_data *be; ++ struct device *dev = card->dev; + + /* Allocate i2s link parameters */ +- be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL); ++ be = devm_kzalloc(dev, sizeof(*be), GFP_KERNEL); + if (!be) + return -ENOMEM; + priv->link_data[*index] = be; +@@ -81,9 +82,10 @@ static int gx_card_add_link(struct snd_soc_card *card, struct device_node *np, + { + struct snd_soc_dai_link *dai_link = &card->dai_link[*index]; + struct snd_soc_dai_link_component *cpu; ++ struct device *dev = card->dev; + int ret; + +- cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL); ++ cpu = devm_kzalloc(dev, sizeof(*cpu), GFP_KERNEL); + if (!cpu) + return -ENOMEM; + +diff --git a/sound/soc/meson/gx-formatter.c b/sound/soc/meson/gx-formatter.c +new file mode 100644 +--- /dev/null ++++ b/sound/soc/meson/gx-formatter.c +@@ -0,0 +1,282 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++// ++// Copyright (c) 2026 BayLibre, SAS. ++// Author: Valerio Setti ++ ++#include ++#include ++#include ++#include ++ ++#include "gx-formatter.h" ++ ++struct gx_formatter { ++ struct list_head list; ++ struct gx_stream *stream; ++ const struct gx_formatter_driver *drv; ++ bool enabled; ++ struct regmap *map; ++}; ++ ++static int gx_formatter_enable(struct gx_formatter *formatter) ++{ ++ int ret; ++ ++ /* Do nothing if the formatter is already enabled */ ++ if (formatter->enabled) ++ return 0; ++ ++ /* Setup the stream parameter in the formatter */ ++ if (formatter->drv->ops->prepare) { ++ ret = formatter->drv->ops->prepare(formatter->map, ++ formatter->drv->quirks, ++ formatter->stream); ++ if (ret) ++ return ret; ++ } ++ ++ /* Finally, actually enable the formatter */ ++ if (formatter->drv->ops->enable) ++ formatter->drv->ops->enable(formatter->map); ++ ++ formatter->enabled = true; ++ ++ return 0; ++} ++ ++static void gx_formatter_disable(struct gx_formatter *formatter) ++{ ++ /* Do nothing if the formatter is already disabled */ ++ if (!formatter->enabled) ++ return; ++ ++ if (formatter->drv->ops->disable) ++ formatter->drv->ops->disable(formatter->map); ++ ++ formatter->enabled = false; ++} ++ ++static int gx_formatter_attach(struct gx_formatter *formatter) ++{ ++ struct gx_stream *ts = formatter->stream; ++ int ret = 0; ++ ++ mutex_lock(&ts->lock); ++ ++ /* Catch up if the stream is already running when we attach */ ++ if (ts->ready) { ++ ret = gx_formatter_enable(formatter); ++ if (ret) { ++ pr_err("failed to enable formatter\n"); ++ goto out; ++ } ++ } ++ ++ list_add_tail(&formatter->list, &ts->formatter_list); ++out: ++ mutex_unlock(&ts->lock); ++ return ret; ++} ++ ++static void gx_formatter_detach(struct gx_formatter *formatter) ++{ ++ struct gx_stream *ts = formatter->stream; ++ ++ if (!ts) ++ return; ++ ++ mutex_lock(&ts->lock); ++ list_del(&formatter->list); ++ mutex_unlock(&ts->lock); ++ ++ gx_formatter_disable(formatter); ++} ++ ++static int gx_formatter_power_up(struct gx_formatter *formatter, ++ struct snd_soc_dapm_widget *w) ++{ ++ struct gx_stream *ts = formatter->drv->ops->get_stream(w); ++ int ret; ++ ++ /* ++ * If we don't get a stream at this stage, it would mean that the ++ * widget is powering up but is not attached to any backend DAI. ++ * It should not happen, ever ! ++ */ ++ if (WARN_ON(!ts)) ++ return -ENODEV; ++ ++ formatter->stream = ts; ++ INIT_LIST_HEAD(&formatter->list); ++ ret = gx_formatter_attach(formatter); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static void gx_formatter_power_down(struct gx_formatter *formatter) ++{ ++ gx_formatter_detach(formatter); ++ formatter->stream = NULL; ++} ++ ++int gx_formatter_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *control, ++ int event) ++{ ++ struct snd_soc_component *c; ++ struct gx_formatter *formatter; ++ int ret = 0; ++ ++ c = snd_soc_dapm_to_component(w->dapm); ++ ++ if (w->priv) ++ formatter = w->priv; ++ else ++ formatter = snd_soc_component_get_drvdata(c); ++ ++ switch (event) { ++ case SND_SOC_DAPM_PRE_PMU: ++ ret = gx_formatter_power_up(formatter, w); ++ break; ++ ++ case SND_SOC_DAPM_PRE_PMD: ++ gx_formatter_power_down(formatter); ++ break; ++ ++ default: ++ dev_err(c->dev, "Unexpected event %d\n", event); ++ return -EINVAL; ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(gx_formatter_event); ++ ++int gx_formatter_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ const struct gx_formatter_driver *drv; ++ struct gx_formatter *formatter; ++ void __iomem *regs; ++ ++ drv = of_device_get_match_data(dev); ++ if (!drv) { ++ dev_err(dev, "failed to match device\n"); ++ return -ENODEV; ++ } ++ ++ formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL); ++ if (!formatter) ++ return -ENOMEM; ++ platform_set_drvdata(pdev, formatter); ++ formatter->drv = drv; ++ ++ regs = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(regs)) ++ return PTR_ERR(regs); ++ ++ formatter->map = devm_regmap_init_mmio(dev, regs, drv->regmap_cfg); ++ if (IS_ERR(formatter->map)) { ++ dev_err(dev, "failed to init regmap: %ld\n", ++ PTR_ERR(formatter->map)); ++ return PTR_ERR(formatter->map); ++ } ++ ++ return devm_snd_soc_register_component(dev, drv->component_drv, ++ NULL, 0); ++} ++EXPORT_SYMBOL_GPL(gx_formatter_probe); ++ ++int gx_formatter_create(struct device *dev, ++ struct snd_soc_dapm_widget *w, ++ const struct gx_formatter_driver *drv, ++ struct regmap *regmap) ++{ ++ struct gx_formatter *formatter; ++ ++ formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL); ++ if (!formatter) ++ return -ENOMEM; ++ ++ formatter->drv = drv; ++ formatter->map = regmap; ++ ++ w->priv = formatter; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(gx_formatter_create); ++ ++int gx_stream_start(struct gx_stream *ts) ++{ ++ struct gx_formatter *formatter; ++ int ret = 0; ++ ++ mutex_lock(&ts->lock); ++ ++ /* Start all the formatters attached to the stream */ ++ list_for_each_entry(formatter, &ts->formatter_list, list) { ++ ret = gx_formatter_enable(formatter); ++ if (ret) { ++ pr_err("failed to enable formatter\n"); ++ goto out; ++ } ++ } ++ ++ ts->ready = true; ++ ++out: ++ mutex_unlock(&ts->lock); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(gx_stream_start); ++ ++void gx_stream_stop(struct gx_stream *ts) ++{ ++ struct gx_formatter *formatter; ++ ++ mutex_lock(&ts->lock); ++ ts->ready = false; ++ ++ /* Stop all the formatters attached to the stream */ ++ list_for_each_entry(formatter, &ts->formatter_list, list) { ++ gx_formatter_disable(formatter); ++ } ++ ++ mutex_unlock(&ts->lock); ++} ++EXPORT_SYMBOL_GPL(gx_stream_stop); ++ ++struct gx_stream *gx_stream_alloc(struct gx_iface *iface) ++{ ++ struct gx_stream *ts; ++ ++ ts = kzalloc(sizeof(*ts), GFP_KERNEL); ++ if (ts) { ++ INIT_LIST_HEAD(&ts->formatter_list); ++ mutex_init(&ts->lock); ++ ts->iface = iface; ++ } ++ ++ return ts; ++} ++EXPORT_SYMBOL_GPL(gx_stream_alloc); ++ ++void gx_stream_free(struct gx_stream *ts) ++{ ++ /* ++ * If the list is not empty, it would mean that one of the formatter ++ * widget is still powered and attached to the interface while we ++ * are removing the TDM DAI. It should not be possible ++ */ ++ WARN_ON(!list_empty(&ts->formatter_list)); ++ mutex_destroy(&ts->lock); ++ kfree(ts); ++} ++EXPORT_SYMBOL_GPL(gx_stream_free); ++ ++MODULE_DESCRIPTION("Amlogic GX formatter driver"); ++MODULE_AUTHOR("Valerio Setti "); ++MODULE_LICENSE("GPL"); +diff --git a/sound/soc/meson/gx-formatter.h b/sound/soc/meson/gx-formatter.h +new file mode 100644 +--- /dev/null ++++ b/sound/soc/meson/gx-formatter.h +@@ -0,0 +1,56 @@ ++/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ ++/* ++ * Copyright (c) 2026 Baylibre SAS. ++ * Author: Valerio Setti ++ */ ++ ++#ifndef _MESON_GX_FORMATTER_H ++#define _MESON_GX_FORMATTER_H ++ ++#include "gx-interface.h" ++ ++struct platform_device; ++struct regmap; ++struct snd_soc_dapm_widget; ++struct snd_kcontrol; ++ ++struct gx_formatter_hw { ++ unsigned int skew_offset; ++}; ++ ++struct gx_formatter_ops { ++ struct gx_stream *(*get_stream)(struct snd_soc_dapm_widget *w); ++ void (*enable)(struct regmap *map); ++ void (*disable)(struct regmap *map); ++ int (*prepare)(struct regmap *map, ++ const struct gx_formatter_hw *quirks, ++ struct gx_stream *ts); ++}; ++ ++struct gx_formatter_driver { ++ const struct snd_soc_component_driver *component_drv; ++ const struct regmap_config *regmap_cfg; ++ const struct gx_formatter_ops *ops; ++ const struct gx_formatter_hw *quirks; ++}; ++ ++int gx_formatter_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *control, ++ int event); ++int gx_formatter_probe(struct platform_device *pdev); ++ ++int gx_formatter_create(struct device *dev, ++ struct snd_soc_dapm_widget *w, ++ const struct gx_formatter_driver *drv, ++ struct regmap *regmap); ++ ++/* ++ * Formatter data is already freed when the associated device is removed, ++ * so we just need to remove the pointer from the widget. ++ */ ++static inline void gx_formatter_free(struct snd_soc_dapm_widget *w) ++{ ++ w->priv = NULL; ++} ++ ++#endif /* _MESON_GX_FORMATTER_H */ +diff --git a/sound/soc/meson/gx-interface.h b/sound/soc/meson/gx-interface.h +new file mode 100644 +--- /dev/null ++++ b/sound/soc/meson/gx-interface.h +@@ -0,0 +1,42 @@ ++/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ ++/* ++ * Copyright (c) 2026 Baylibre SAS. ++ * Author: Valerio Setti ++ */ ++ ++#ifndef _MESON_GX_INTERFACE_H ++#define _MESON_GX_INTERFACE_H ++ ++#include ++#include ++#include ++#include ++#include ++ ++struct gx_iface { ++ struct clk *mclk; ++ unsigned long mclk_rate; ++ ++ /* format is common to all the DAIs of the iface */ ++ unsigned int fmt; ++}; ++ ++struct gx_stream { ++ struct gx_iface *iface; ++ struct list_head formatter_list; ++ struct mutex lock; ++ unsigned int channels; ++ unsigned int width; ++ unsigned int physical_width; ++ bool ready; ++ ++ /* For continuous clock tracking */ ++ bool clk_enabled; ++}; ++ ++struct gx_stream *gx_stream_alloc(struct gx_iface *iface); ++void gx_stream_free(struct gx_stream *ts); ++int gx_stream_start(struct gx_stream *ts); ++void gx_stream_stop(struct gx_stream *ts); ++ ++#endif /* _MESON_GX_INTERFACE_H */ +diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c +--- a/sound/soc/meson/meson-card-utils.c ++++ b/sound/soc/meson/meson-card-utils.c +@@ -71,6 +71,7 @@ int meson_card_parse_dai(struct snd_soc_card *card, + struct device_node *node, + struct snd_soc_dai_link_component *dlc) + { ++ struct device *dev = card->dev; + int ret; + + if (!dlc || !node) +@@ -78,7 +79,7 @@ int meson_card_parse_dai(struct snd_soc_card *card, + + ret = snd_soc_of_get_dlc(node, NULL, dlc, 0); + if (ret) +- return dev_err_probe(card->dev, ret, "can't parse dai\n"); ++ return dev_err_probe(dev, ret, "can't parse dai\n"); + + return ret; + } +@@ -89,7 +90,8 @@ static int meson_card_set_link_name(struct snd_soc_card *card, + struct device_node *node, + const char *prefix) + { +- char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s", ++ struct device *dev = card->dev; ++ char *name = devm_kasprintf(dev, GFP_KERNEL, "%s.%s", + prefix, node->full_name); + if (!name) + return -ENOMEM; +@@ -132,16 +134,17 @@ int meson_card_set_be_link(struct snd_soc_card *card, + struct device_node *node) + { + struct snd_soc_dai_link_component *codec; ++ struct device *dev = card->dev; + int ret, num_codecs; + + num_codecs = of_get_child_count(node); + if (!num_codecs) { +- dev_err(card->dev, "be link %s has no codec\n", ++ dev_err(dev, "be link %s has no codec\n", + node->full_name); + return -EINVAL; + } + +- codec = devm_kcalloc(card->dev, num_codecs, sizeof(*codec), GFP_KERNEL); ++ codec = devm_kcalloc(dev, num_codecs, sizeof(*codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + +@@ -158,7 +161,7 @@ int meson_card_set_be_link(struct snd_soc_card *card, + + ret = meson_card_set_link_name(card, link, node, "be"); + if (ret) +- dev_err(card->dev, "error setting %pOFn link name\n", node); ++ dev_err(dev, "error setting %pOFn link name\n", node); + + return ret; + } +@@ -189,12 +192,13 @@ EXPORT_SYMBOL_GPL(meson_card_set_fe_link); + static int meson_card_add_links(struct snd_soc_card *card) + { + struct meson_card *priv = snd_soc_card_get_drvdata(card); +- struct device_node *node = card->dev->of_node; ++ struct device *dev = card->dev; ++ struct device_node *node = dev->of_node; + int num, i, ret; + + num = of_get_child_count(node); + if (!num) { +- dev_err(card->dev, "card has no links\n"); ++ dev_err(dev, "card has no links\n"); + return -EINVAL; + } + +@@ -219,8 +223,10 @@ static int meson_card_parse_of_optional(struct snd_soc_card *card, + int (*func)(struct snd_soc_card *c, + const char *p)) + { ++ struct device *dev = card->dev; ++ + /* If property is not provided, don't fail ... */ +- if (!of_property_present(card->dev->of_node, propname)) ++ if (!of_property_present(dev->of_node, propname)) + return 0; + + /* ... but do fail if it is provided and the parsing fails */ +diff --git a/sound/soc/meson/t9015.c b/sound/soc/meson/t9015.c +--- a/sound/soc/meson/t9015.c ++++ b/sound/soc/meson/t9015.c +@@ -78,8 +78,14 @@ static int t9015_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) + return 0; + } + ++static const u64 t9015_dai_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_LEFT_J; ++ + static const struct snd_soc_dai_ops t9015_dai_ops = { + .set_fmt = t9015_dai_set_fmt, ++ .auto_selectable_formats = &t9015_dai_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static struct snd_soc_dai_driver t9015_dai = { +@@ -265,10 +271,8 @@ static int t9015_probe(struct platform_device *pdev) + return dev_err_probe(dev, PTR_ERR(priv->avdd), "failed to AVDD\n"); + + ret = device_reset(dev); +- if (ret) { +- dev_err(dev, "reset failed\n"); +- return ret; +- } ++ if (ret) ++ return dev_err_probe(dev, ret, "failed to reset device\n"); + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) { +diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c +--- a/sound/soc/mxs/mxs-saif.c ++++ b/sound/soc/mxs/mxs-saif.c +@@ -826,12 +826,9 @@ static int mxs_saif_probe(struct platform_device *pdev) + mxs_saif[saif->id] = saif; + + saif->clk = devm_clk_get(&pdev->dev, NULL); +- if (IS_ERR(saif->clk)) { +- ret = PTR_ERR(saif->clk); +- dev_err(&pdev->dev, "Cannot get the clock: %d\n", +- ret); +- return ret; +- } ++ if (IS_ERR(saif->clk)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(saif->clk), ++ "Cannot get the clock\n"); + + saif->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(saif->base)) +@@ -844,10 +841,8 @@ static int mxs_saif_probe(struct platform_device *pdev) + saif->dev = &pdev->dev; + ret = devm_request_irq(&pdev->dev, irq, mxs_saif_irq, 0, + dev_name(&pdev->dev), saif); +- if (ret) { +- dev_err(&pdev->dev, "failed to request irq\n"); ++ if (ret) + return ret; +- } + + platform_set_drvdata(pdev, saif); + +@@ -860,16 +855,12 @@ static int mxs_saif_probe(struct platform_device *pdev) + + ret = devm_snd_soc_register_component(&pdev->dev, &mxs_saif_component, + &mxs_saif_dai, 1); +- if (ret) { +- dev_err(&pdev->dev, "register DAI failed\n"); ++ if (ret) + return ret; +- } + + ret = mxs_pcm_platform_register(&pdev->dev); +- if (ret) { +- dev_err(&pdev->dev, "register PCM failed: %d\n", ret); ++ if (ret) + return ret; +- } + + return 0; + } +diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c +--- a/sound/soc/mxs/mxs-sgtl5000.c ++++ b/sound/soc/mxs/mxs-sgtl5000.c +@@ -155,8 +155,6 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev) + + ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); + if (ret) { +- dev_err(&pdev->dev, "failed to parse audio-routing (%d)\n", +- ret); + mxs_saif_put_mclk(0); + return ret; + } +diff --git a/sound/soc/pxa/pxa2xx-ac97-lib.c b/sound/soc/pxa/pxa2xx-ac97-lib.c +--- a/sound/soc/pxa/pxa2xx-ac97-lib.c ++++ b/sound/soc/pxa/pxa2xx-ac97-lib.c +@@ -316,10 +316,8 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev) + int irq; + + ac97_reg_base = devm_platform_ioremap_resource(dev, 0); +- if (IS_ERR(ac97_reg_base)) { +- dev_err(&dev->dev, "Missing MMIO resource\n"); ++ if (IS_ERR(ac97_reg_base)) + return PTR_ERR(ac97_reg_base); +- } + + if (cpu_is_pxa27x()) { + /* Assert reset using GPIOD_OUT_HIGH, because reset is GPIO_ACTIVE_LOW */ +diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c +--- a/sound/soc/qcom/common.c ++++ b/sound/soc/qcom/common.c +@@ -23,6 +23,161 @@ static const struct snd_soc_dapm_widget qcom_jack_snd_widgets[] = { + SND_SOC_DAPM_SPK("DP7 Jack", NULL), + }; + ++static struct device_node *qcom_snd_get_link_node(struct snd_soc_pcm_runtime *rtd) ++{ ++ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); ++ struct snd_soc_card *card = rtd->card; ++ struct of_phandle_args args; ++ int ret; ++ ++ if (!card->dev || !card->dev->of_node) ++ return NULL; ++ ++ for_each_available_child_of_node_scoped(card->dev->of_node, np) { ++ struct device_node *cpu_np __free(device_node) = ++ of_get_child_by_name(np, "cpu"); ++ ++ if (!cpu_np) ++ continue; ++ ++ ret = of_parse_phandle_with_args(cpu_np, "sound-dai", "#sound-dai-cells", 0, ++ &args); ++ if (ret) ++ continue; ++ ++ if (args.np == rtd->dai_link->cpus[0].of_node && ++ args.args_count == 1 && args.args[0] == cpu_dai->id) { ++ of_node_put(args.np); ++ return of_node_get(np); ++ } ++ ++ of_node_put(args.np); ++ } ++ ++ return NULL; ++} ++ ++static int qcom_snd_parse_tdm_slot(struct device_node *np, ++ struct qcom_snd_tdm_slot_cfg *cfg) ++{ ++ memset(cfg, 0, sizeof(*cfg)); ++ ++ return snd_soc_of_parse_tdm_slot(np, &cfg->tx_mask, &cfg->rx_mask, ++ &cfg->slots, &cfg->slot_width); ++} ++ ++static int qcom_snd_normalize_tdm_slots(struct qcom_snd_tdm_slot_cfg *cpu_cfg, ++ struct qcom_snd_tdm_slot_cfg *codec_cfg) ++{ ++ unsigned int slots; ++ unsigned int slot_width; ++ ++ if (cpu_cfg->slots && codec_cfg->slots && cpu_cfg->slots != codec_cfg->slots) ++ return -EINVAL; ++ ++ if (cpu_cfg->slot_width && codec_cfg->slot_width && ++ cpu_cfg->slot_width != codec_cfg->slot_width) ++ return -EINVAL; ++ ++ slots = cpu_cfg->slots ?: codec_cfg->slots; ++ if (!slots) ++ return 0; ++ ++ slot_width = cpu_cfg->slot_width ?: codec_cfg->slot_width; ++ if (!slot_width) ++ return -EINVAL; ++ ++ cpu_cfg->slots = slots; ++ codec_cfg->slots = slots; ++ cpu_cfg->slot_width = slot_width; ++ codec_cfg->slot_width = slot_width; ++ ++ return 0; ++} ++ ++static int qcom_snd_parse_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, ++ struct qcom_snd_tdm_slot_cfg *cpu_cfg, ++ struct qcom_snd_tdm_slot_cfg *codec_cfg) ++{ ++ struct device_node *link_np __free(device_node) = qcom_snd_get_link_node(rtd); ++ int ret; ++ ++ if (!link_np) ++ return -EINVAL; ++ ++ struct device_node *cpu_np __free(device_node) = ++ of_get_child_by_name(link_np, "cpu"); ++ struct device_node *codec_np __free(device_node) = ++ of_get_child_by_name(link_np, "codec"); ++ if (!cpu_np || !codec_np) ++ return -EINVAL; ++ ++ ret = qcom_snd_parse_tdm_slot(cpu_np, cpu_cfg); ++ if (ret) ++ return ret; ++ ++ return qcom_snd_parse_tdm_slot(codec_np, codec_cfg); ++} ++ ++int qcom_snd_get_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, ++ struct qcom_snd_tdm_slot_cfg *cpu_cfg, ++ struct qcom_snd_tdm_slot_cfg *codec_cfg) ++{ ++ int ret; ++ ++ ret = qcom_snd_parse_dai_tdm_slots(rtd, cpu_cfg, codec_cfg); ++ if (ret) ++ return ret; ++ ++ return qcom_snd_normalize_tdm_slots(cpu_cfg, codec_cfg); ++} ++EXPORT_SYMBOL_GPL(qcom_snd_get_dai_tdm_slots); ++ ++int qcom_snd_apply_dai_tdm_slots_cfg(struct snd_soc_pcm_runtime *rtd, ++ const struct qcom_snd_tdm_slot_cfg *cpu_cfg, ++ const struct qcom_snd_tdm_slot_cfg *codec_cfg) ++{ ++ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); ++ struct snd_soc_dai *codec_dai; ++ int i; ++ int ret; ++ ++ if (!cpu_cfg->slots) ++ return 0; ++ ++ ret = snd_soc_dai_set_tdm_slot(cpu_dai, cpu_cfg->tx_mask, cpu_cfg->rx_mask, ++ cpu_cfg->slots, cpu_cfg->slot_width); ++ if (ret) ++ return ret; ++ ++ for_each_rtd_codec_dais(rtd, i, codec_dai) { ++ ret = snd_soc_dai_set_tdm_slot(codec_dai, ++ codec_cfg->tx_mask, ++ codec_cfg->rx_mask, ++ codec_cfg->slots, ++ codec_cfg->slot_width); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(qcom_snd_apply_dai_tdm_slots_cfg); ++ ++int qcom_snd_apply_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd) ++{ ++ struct qcom_snd_tdm_slot_cfg cpu_cfg; ++ struct qcom_snd_tdm_slot_cfg codec_cfg; ++ int ret; ++ ++ ret = qcom_snd_get_dai_tdm_slots(rtd, &cpu_cfg, &codec_cfg); ++ if (ret) ++ return ret == -EINVAL ? 0 : ret; ++ ++ return qcom_snd_apply_dai_tdm_slots_cfg(rtd, &cpu_cfg, &codec_cfg); ++} ++EXPORT_SYMBOL_GPL(qcom_snd_apply_dai_tdm_slots); ++ + int qcom_snd_parse_of(struct snd_soc_card *card) + { + struct device *dev = card->dev; +@@ -91,7 +246,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) + + ret = of_property_read_string(np, "link-name", &link->name); + if (ret) { +- dev_err(card->dev, "error getting codec dai_link name\n"); ++ dev_err(dev, "error getting codec dai_link name\n"); + return ret; + } + +@@ -109,7 +264,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) + + ret = snd_soc_of_get_dlc(cpu, &args, link->cpus, 0); + if (ret) { +- dev_err_probe(card->dev, ret, ++ dev_err_probe(dev, ret, + "%s: error getting cpu dai name\n", link->name); + return ret; + } +@@ -126,7 +281,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) + "sound-dai", + 0); + if (!link->platforms->of_node) { +- dev_err(card->dev, "%s: platform dai not found\n", link->name); ++ dev_err(dev, "%s: platform dai not found\n", link->name); + return -EINVAL; + } + } else { +@@ -136,7 +291,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) + if (codec) { + ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); + if (ret < 0) { +- dev_err_probe(card->dev, ret, ++ dev_err_probe(dev, ret, + "%s: codec dai not found\n", link->name); + return ret; + } +@@ -215,6 +370,7 @@ int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd, + } + + switch (cpu_dai->id) { ++ case LPI_MI2S_RX_0: + case TX_CODEC_DMA_TX_0: + case TX_CODEC_DMA_TX_1: + case TX_CODEC_DMA_TX_2: +diff --git a/sound/soc/qcom/common.h b/sound/soc/qcom/common.h +--- a/sound/soc/qcom/common.h ++++ b/sound/soc/qcom/common.h +@@ -7,9 +7,23 @@ + #include + #include + +-#define LPASS_MAX_PORT (SENARY_MI2S_TX + 1) ++#define LPASS_MAX_PORT (LPI_MI2S_TX_6 + 1) ++ ++struct qcom_snd_tdm_slot_cfg { ++ unsigned int tx_mask; ++ unsigned int rx_mask; ++ unsigned int slots; ++ unsigned int slot_width; ++}; + + int qcom_snd_parse_of(struct snd_soc_card *card); ++int qcom_snd_get_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, ++ struct qcom_snd_tdm_slot_cfg *cpu_cfg, ++ struct qcom_snd_tdm_slot_cfg *codec_cfg); ++int qcom_snd_apply_dai_tdm_slots_cfg(struct snd_soc_pcm_runtime *rtd, ++ const struct qcom_snd_tdm_slot_cfg *cpu_cfg, ++ const struct qcom_snd_tdm_slot_cfg *codec_cfg); ++int qcom_snd_apply_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd); + int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_jack *jack, bool *jack_setup); + int qcom_snd_dp_jack_setup(struct snd_soc_pcm_runtime *rtd, +diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c +--- a/sound/soc/qcom/qdsp6/audioreach.c ++++ b/sound/soc/qcom/qdsp6/audioreach.c +@@ -152,6 +152,13 @@ struct apm_i2s_module_intf_cfg { + + #define APM_I2S_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_i2s_module_intf_cfg), 8) + ++struct apm_audio_if_module_intf_cfg { ++ struct apm_module_param_data param_data; ++ struct param_id_audio_if_intf_cfg cfg; ++} __packed; ++ ++#define APM_AUDIO_IF_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_audio_if_module_intf_cfg), 8) ++ + struct apm_module_hw_ep_mf_cfg { + struct apm_module_param_data param_data; + struct param_id_hw_ep_mf mf; +@@ -168,6 +175,13 @@ struct apm_module_frame_size_factor_cfg { + + #define APM_FS_CFG_PSIZE ALIGN(sizeof(struct apm_module_frame_size_factor_cfg), 8) + ++struct apm_module_hw_ep_frame_duration_cfg { ++ struct apm_module_param_data param_data; ++ struct param_id_hw_ep_frame_duration frame_duration; ++} __packed; ++ ++#define APM_HW_EP_FRAME_DURATION_PSIZE ALIGN(sizeof(struct apm_module_hw_ep_frame_duration_cfg), 8) ++ + struct apm_module_hw_ep_power_mode_cfg { + struct apm_module_param_data param_data; + struct param_id_hw_ep_power_mode_cfg power_mode; +@@ -703,6 +717,7 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph, + int pm_sz = APM_HW_EP_PMODE_CFG_PSIZE; + int size = ic_sz + ep_sz + fs_sz + pm_sz; + void *p; ++ int i; + + struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0); + if (IS_ERR(pkt)) +@@ -741,7 +756,12 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph, + + intf_cfg->cfg.lpaif_type = module->hw_interface_type; + intf_cfg->cfg.intf_index = module->hw_interface_idx; +- intf_cfg->cfg.active_channels_mask = (1 << cfg->num_channels) - 1; ++ intf_cfg->cfg.active_channels_mask = 0; ++ /* Convert the physical channel mapping into a bit field */ ++ for (i = 0; i < AR_PCM_MAX_NUM_CHANNEL; i++) ++ if (cfg->channel_map[i]) ++ intf_cfg->cfg.active_channels_mask |= BIT(i); ++ + p += ic_sz; + + pm_cfg = p; +@@ -840,7 +860,7 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, + uint32_t num_channels = cfg->num_channels; + int payload_size = APM_MFC_CFG_PSIZE(media_format, num_channels) + + APM_MODULE_PARAM_DATA_SIZE; +- int i; ++ int i, j; + void *p; + + struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); +@@ -860,8 +880,12 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, + media_format->sample_rate = cfg->sample_rate; + media_format->bit_width = cfg->bit_width; + media_format->num_channels = cfg->num_channels; +- for (i = 0; i < num_channels; i++) +- media_format->channel_mapping[i] = cfg->channel_map[i]; ++ /* Convert the physical mapping to a logical mapping of the channels */ ++ for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < cfg->num_channels; i++) { ++ if (!cfg->channel_map[i]) ++ continue; ++ media_format->channel_mapping[j++] = cfg->channel_map[i]; ++ } + + return q6apm_send_cmd_sync(graph->apm, pkt, 0); + } +@@ -1042,6 +1066,81 @@ static int audioreach_i2s_set_media_format(struct q6apm_graph *graph, + return q6apm_send_cmd_sync(graph->apm, pkt, 0); + } + ++static int audioreach_audio_if_set_media_format(struct q6apm_graph *graph, ++ const struct audioreach_module *module, ++ const struct audioreach_module_config *cfg) ++{ ++ struct apm_module_hw_ep_frame_duration_cfg *fd_cfg; ++ struct apm_module_param_data *param_data; ++ struct apm_audio_if_module_intf_cfg *intf_cfg; ++ struct apm_module_hw_ep_mf_cfg *hw_cfg; ++ int ic_sz = APM_AUDIO_IF_INTF_CFG_PSIZE; ++ int ep_sz = APM_HW_EP_CFG_PSIZE; ++ int fd_sz = APM_HW_EP_FRAME_DURATION_PSIZE; ++ int size = ic_sz + ep_sz + fd_sz; ++ u32 slot_mask = cfg->slot_mask ? cfg->slot_mask : module->slot_mask; ++ u16 nslots_per_frame = cfg->nslots_per_frame ? ++ (u16)cfg->nslots_per_frame : module->nslots_per_frame; ++ u16 slot_width = cfg->slot_width ? (u16)cfg->slot_width : module->slot_width; ++ void *p; ++ ++ struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0); ++ if (IS_ERR(pkt)) ++ return PTR_ERR(pkt); ++ ++ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; ++ intf_cfg = p; ++ ++ param_data = &intf_cfg->param_data; ++ param_data->module_instance_id = module->instance_id; ++ param_data->error_code = 0; ++ param_data->param_id = PARAM_ID_AUDIO_IF_INTF_CFG; ++ param_data->param_size = ic_sz - APM_MODULE_PARAM_DATA_SIZE; ++ intf_cfg->cfg.qaif_type = module->qaif_type; ++ intf_cfg->cfg.intf_idx = (u16)module->hw_interface_idx; ++ intf_cfg->cfg.intf_mode = module->intf_mode; ++ intf_cfg->cfg.ctrl_data_out_enable = module->ctrl_data_out_enable; ++ intf_cfg->cfg.active_slot_mask = slot_mask; ++ intf_cfg->cfg.nslots_per_frame = nslots_per_frame; ++ intf_cfg->cfg.slot_width = slot_width; ++ intf_cfg->cfg.active_lane_mask = module->active_lane_mask; ++ intf_cfg->cfg.frame_sync_rate = module->frame_sync_rate; ++ intf_cfg->cfg.frame_sync_src = module->sync_src; ++ intf_cfg->cfg.frame_sync_mode = module->sync_mode; ++ intf_cfg->cfg.invert_frame_sync_pulse = module->ctrl_invert_sync_pulse; ++ intf_cfg->cfg.frame_sync_data_delay = module->ctrl_sync_data_delay; ++ intf_cfg->cfg.bit_clk_type = module->bit_clk_type; ++ intf_cfg->cfg.inv_int_bit_clk = module->inv_int_bit_clk; ++ intf_cfg->cfg.inv_ext_bit_clk = module->inv_ext_bit_clk; ++ ++ p += ic_sz; ++ hw_cfg = p; ++ param_data = &hw_cfg->param_data; ++ param_data->module_instance_id = module->instance_id; ++ param_data->error_code = 0; ++ param_data->param_id = PARAM_ID_HW_EP_MF_CFG; ++ param_data->param_size = ep_sz - APM_MODULE_PARAM_DATA_SIZE; ++ ++ hw_cfg->mf.sample_rate = cfg->sample_rate; ++ hw_cfg->mf.bit_width = cfg->bit_width; ++ hw_cfg->mf.num_channels = cfg->num_channels; ++ hw_cfg->mf.data_format = module->data_format; ++ ++ p += ep_sz; ++ fd_cfg = p; ++ param_data = &fd_cfg->param_data; ++ param_data->module_instance_id = module->instance_id; ++ param_data->error_code = 0; ++ param_data->param_id = PARAM_ID_HW_EP_FRAME_DURATION; ++ param_data->param_size = fd_sz - APM_MODULE_PARAM_DATA_SIZE; ++ fd_cfg->frame_duration.frame_duration_in_us = AUDIO_IF_FRAME_DURATION_US; ++ fd_cfg->frame_duration.allow_frame_duration_normalization = AUDIO_IF_FRAME_DURATION_NORMALIZATION_ENABLE; ++ fd_cfg->frame_duration.min_normalized_frame_dur_us = AUDIO_IF_FRAME_DURATION_MIN_US; ++ fd_cfg->frame_duration.max_normalized_frame_dur_us = AUDIO_IF_FRAME_DURATION_MAX_US; ++ ++ return q6apm_send_cmd_sync(graph->apm, pkt, 0); ++} ++ + static int audioreach_logging_set_media_format(struct q6apm_graph *graph, + const struct audioreach_module *module) + { +@@ -1080,6 +1179,7 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph, + struct apm_pcm_module_media_fmt_cmd *cfg; + struct apm_module_param_data *param_data; + int payload_size; ++ int i, j; + + if (num_channels > 4) { + dev_err(graph->dev, "Error: Invalid channels (%d)!\n", num_channels); +@@ -1113,7 +1213,12 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph, + media_cfg->num_channels = mcfg->num_channels; + media_cfg->q_factor = mcfg->bit_width - 1; + media_cfg->bits_per_sample = mcfg->bit_width; +- memcpy(media_cfg->channel_mapping, mcfg->channel_map, mcfg->num_channels); ++ /* Convert the physical mapping to a logical mapping of the channels */ ++ for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < mcfg->num_channels; i++) { ++ if (!mcfg->channel_map[i]) ++ continue; ++ media_cfg->channel_mapping[j++] = mcfg->channel_map[i]; ++ } + + return q6apm_send_cmd_sync(graph->apm, pkt, 0); + } +@@ -1163,6 +1268,7 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph, + struct payload_media_fmt_pcm *cfg; + struct media_format *header; + int rc, payload_size; ++ int i, j; + void *p; + + if (num_channels > 4) { +@@ -1202,7 +1308,12 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph, + cfg->q_factor = mcfg->bit_width - 1; + cfg->endianness = PCM_LITTLE_ENDIAN; + cfg->num_channels = mcfg->num_channels; +- memcpy(cfg->channel_mapping, mcfg->channel_map, mcfg->num_channels); ++ /* Convert the physical mapping to a logical mapping of the channels */ ++ for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < cfg->num_channels; i++) { ++ if (!mcfg->channel_map[i]) ++ continue; ++ cfg->channel_mapping[j++] = mcfg->channel_map[i]; ++ } + } else { + rc = audioreach_set_compr_media_format(header, p, mcfg); + if (rc) +@@ -1279,7 +1390,7 @@ static int audioreach_speaker_protection_vi(struct q6apm_graph *graph, + struct apm_module_sp_vi_ex_mode_cfg *ex_cfg; + int op_sz, cm_sz, ex_sz; + struct apm_module_param_data *param_data; +- int rc, i, payload_size; ++ int rc, i, payload_size, j; + struct gpr_pkt *pkt; + void *p; + +@@ -1320,14 +1431,19 @@ static int audioreach_speaker_protection_vi(struct q6apm_graph *graph, + param_data->param_size = cm_sz - APM_MODULE_PARAM_DATA_SIZE; + + cm_cfg->cfg.num_channels = num_channels * 2; +- for (i = 0; i < num_channels; i++) { ++ /* Convert the physical mapping to a logical mapping of the channels */ ++ for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < num_channels; i++) { ++ if (!mcfg->channel_map[i]) ++ continue; + /* + * Map speakers into Vsense and then Isense of each channel. + * E.g. for PCM_CHANNEL_FL and PCM_CHANNEL_FR to: + * [1, 2, 3, 4] + */ +- cm_cfg->cfg.channel_mapping[2 * i] = (mcfg->channel_map[i] - 1) * 2 + 1; +- cm_cfg->cfg.channel_mapping[2 * i + 1] = (mcfg->channel_map[i] - 1) * 2 + 2; ++ cm_cfg->cfg.channel_mapping[2 * j] = (mcfg->channel_map[i] - 1) * 2 + 1; ++ cm_cfg->cfg.channel_mapping[2 * j + 1] = (mcfg->channel_map[i] - 1) * 2 + 2; ++ ++ ++j; + } + + p += cm_sz; +@@ -1411,6 +1527,10 @@ int audioreach_set_media_format(struct q6apm_graph *graph, + if (!rc) + rc = audioreach_module_enable(graph, module, true); + break; ++ case MODULE_ID_AUDIO_IF_SOURCE: ++ case MODULE_ID_AUDIO_IF_SINK: ++ rc = audioreach_audio_if_set_media_format(graph, module, cfg); ++ break; + + default: + rc = 0; +diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h +--- a/sound/soc/qcom/qdsp6/audioreach.h ++++ b/sound/soc/qcom/qdsp6/audioreach.h +@@ -36,6 +36,8 @@ struct q6apm_graph; + #define MODULE_ID_SPEAKER_PROTECTION 0x070010E2 + #define MODULE_ID_SPEAKER_PROTECTION_VI 0x070010E3 + #define MODULE_ID_OPUS_DEC 0x07001174 ++#define MODULE_ID_AUDIO_IF_SINK 0x0700117C ++#define MODULE_ID_AUDIO_IF_SOURCE 0x0700117D + + #define APM_CMD_GET_SPF_STATE 0x01001021 + #define APM_CMD_RSP_GET_SPF_STATE 0x02001007 +@@ -544,6 +546,74 @@ struct param_id_i2s_intf_cfg { + #define PORT_ID_I2S_OUPUT 1 + #define I2S_STACK_SIZE 2048 + ++#define PARAM_ID_AUDIO_IF_INTF_CFG 0x08001B11 ++ ++/* ++ * struct param_id_audio_if_intf_cfg - Audio interface configuration ++ * @qaif_type: Audio interface type (e.g. QAIF, QAIF_VA) ++ * @intf_idx: Interface instance index ++ * @intf_mode: Interface operating mode (TDM/PCM/I2S) ++ * @ctrl_data_out_enable: Enable sharing of data-out signal with other masters ++ * @active_slot_mask: Bitmask indicating active slots ++ * @nslots_per_frame: Number of slots per audio frame ++ * @slot_width: Width of each slot in bits ++ * @active_lane_mask: Bitmask of active data lanes ++ * @frame_sync_rate: Frame sync rate in Hz ++ * @frame_sync_src: Frame sync source selection ++ * @frame_sync_mode: Frame sync mode configuration ++ * @invert_frame_sync_pulse: Invert frame sync polarity when set ++ * @frame_sync_data_delay: Data delay from frame sync in bit clocks ++ * @bit_clk_type: Bit clock type (internal / external) ++ * @inv_int_bit_clk: Invert internal bit clock when set ++ * @inv_ext_bit_clk: Invert external bit clock when set ++ * ++ * This structure defines configuration parameters for the Qualcomm ++ * Audio Interface (QAIF) block. It is used to program interface ++ * characteristics such as slot configuration, clocking and frame ++ * synchronization behaviour. ++ */ ++struct param_id_audio_if_intf_cfg { ++ uint16_t qaif_type; ++ uint16_t intf_idx; ++ uint16_t intf_mode; ++ uint16_t ctrl_data_out_enable; ++ uint32_t active_slot_mask; ++ uint16_t nslots_per_frame; ++ uint16_t slot_width; ++ uint32_t active_lane_mask; ++ uint32_t frame_sync_rate; ++ uint16_t frame_sync_src; ++ uint16_t frame_sync_mode; ++ uint16_t invert_frame_sync_pulse; ++ uint16_t frame_sync_data_delay; ++ uint16_t bit_clk_type; ++ uint8_t inv_int_bit_clk; ++ uint8_t inv_ext_bit_clk; ++} __packed; ++ ++#define PARAM_ID_HW_EP_FRAME_DURATION 0x08001B2F ++#define AUDIO_IF_FRAME_DURATION_US 1000 ++#define AUDIO_IF_FRAME_DURATION_NORMALIZATION_ENABLE 1 ++#define AUDIO_IF_FRAME_DURATION_MIN_US 1 ++#define AUDIO_IF_FRAME_DURATION_MAX_US 100000 ++ ++/** ++ * struct param_id_hw_ep_frame_duration - Hardware endpoint frame duration ++ * @frame_duration_in_us: Frame duration in microseconds. ++ * @allow_frame_duration_normalization: Permit SPF to normalize frame duration. ++ * @min_normalized_frame_dur_us: Minimum normalized frame duration in microseconds. ++ * @max_normalized_frame_dur_us: Maximum normalized frame duration in microseconds. ++ * ++ * This structure configures the frame duration for the Audio IF hardware ++ * endpoint and, when enabled, the allowed normalization range. ++ */ ++struct param_id_hw_ep_frame_duration { ++ uint32_t frame_duration_in_us; ++ uint32_t allow_frame_duration_normalization; ++ uint32_t min_normalized_frame_dur_us; ++ uint32_t max_normalized_frame_dur_us; ++} __packed; ++ + #define PARAM_ID_DISPLAY_PORT_INTF_CFG 0x08001154 + + struct param_id_display_port_intf_cfg { +@@ -877,6 +947,23 @@ struct audioreach_module { + uint32_t data_format; + uint32_t hw_interface_type; + ++ /* Audio IF module (TDM/PCM/I2S) */ ++ u32 slot_mask; ++ u32 active_lane_mask; ++ u32 frame_sync_rate; ++ u16 qaif_type; ++ u16 sync_src; ++ u16 ctrl_data_out_enable; ++ u16 nslots_per_frame; ++ u16 slot_width; ++ u16 intf_mode; ++ u16 sync_mode; ++ u16 ctrl_invert_sync_pulse; ++ u16 ctrl_sync_data_delay; ++ u16 bit_clk_type; ++ u8 inv_int_bit_clk; ++ u8 inv_ext_bit_clk; ++ + /* PCM module specific */ + uint32_t interleave_type; + +@@ -907,6 +994,9 @@ struct audioreach_module_config { + u32 channel_allocation; + u32 sd_line_mask; + int fmt; ++ u32 slot_mask; ++ u16 nslots_per_frame; ++ u16 slot_width; + struct snd_codec codec; + u8 channel_map[AR_PCM_MAX_NUM_CHANNEL]; + }; +diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c +--- a/sound/soc/qcom/qdsp6/q6afe-dai.c ++++ b/sound/soc/qcom/qdsp6/q6afe-dai.c +@@ -412,6 +412,8 @@ static int q6afe_dai_prepare(struct snd_pcm_substream *substream, + case SENARY_MI2S_RX ... SENARY_MI2S_TX: + case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: + case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: ++ case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: ++ case LPI_MI2S_RX_5 ... LPI_MI2S_TX_6: + rc = q6afe_i2s_port_prepare(dai_data->port[dai->id], + &dai_data->port_config[dai->id].i2s_cfg); + if (rc < 0) { +@@ -665,6 +667,21 @@ static const struct snd_soc_dapm_route q6afe_dapm_routes[] = { + + /* USB playback AFE port receives data for playback, hence use the RX port */ + {"USB Playback", NULL, "USB_RX"}, ++ ++ {"LPI RX0 MI2S Playback", NULL, "LPI_MI2S_RX_0"}, ++ {"LPI_MI2S_TX_0", NULL, "LPI TX0 MI2S Capture"}, ++ {"LPI RX1 MI2S Playback", NULL, "LPI_MI2S_RX_1"}, ++ {"LPI_MI2S_TX_1", NULL, "LPI TX1 MI2S Capture"}, ++ {"LPI RX2 MI2S Playback", NULL, "LPI_MI2S_RX_2"}, ++ {"LPI_MI2S_TX_2", NULL, "LPI TX2 MI2S Capture"}, ++ {"LPI RX3 MI2S Playback", NULL, "LPI_MI2S_RX_3"}, ++ {"LPI_MI2S_TX_3", NULL, "LPI TX3 MI2S Capture"}, ++ {"LPI RX4 MI2S Playback", NULL, "LPI_MI2S_RX_4"}, ++ {"LPI_MI2S_TX_4", NULL, "LPI TX4 MI2S Capture"}, ++ {"LPI RX5 MI2S Playback", NULL, "LPI_MI2S_RX_5"}, ++ {"LPI_MI2S_TX_5", NULL, "LPI TX5 MI2S Capture"}, ++ {"LPI RX6 MI2S Playback", NULL, "LPI_MI2S_RX_6"}, ++ {"LPI_MI2S_TX_6", NULL, "LPI TX6 MI2S Capture"}, + }; + + static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai) +@@ -1011,6 +1028,35 @@ static const struct snd_soc_dapm_widget q6afe_dai_widgets[] = { + 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("USB_RX", NULL, 0, SND_SOC_NOPM, 0, 0), ++ ++ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_0", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_0", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_1", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_1", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_2", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_2", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_3", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_3", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_4", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_4", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_5", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_5", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_IN("LPI_MI2S_RX_6", NULL, ++ 0, SND_SOC_NOPM, 0, 0), ++ SND_SOC_DAPM_AIF_OUT("LPI_MI2S_TX_6", NULL, ++ 0, SND_SOC_NOPM, 0, 0), + }; + + static const struct snd_soc_component_driver q6afe_dai_component = { +@@ -1045,6 +1091,8 @@ static void of_q6afe_parse_dai_data(struct device *dev, + case SENARY_MI2S_RX ... SENARY_MI2S_TX: + case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: + case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: ++ case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: ++ case LPI_MI2S_RX_5 ... LPI_MI2S_TX_6: + priv = &data->priv[id]; + ret = of_property_read_variable_u32_array(node, + "qcom,sd-lines", +diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c +--- a/sound/soc/qcom/qdsp6/q6afe.c ++++ b/sound/soc/qcom/qdsp6/q6afe.c +@@ -132,6 +132,20 @@ + #define AFE_PORT_ID_QUINARY_MI2S_TX 0x1017 + #define AFE_PORT_ID_SENARY_MI2S_RX 0x1018 + #define AFE_PORT_ID_SENARY_MI2S_TX 0x1019 ++#define AFE_PORT_ID_INT0_MI2S_RX 0x102e ++#define AFE_PORT_ID_INT0_MI2S_TX 0x102f ++#define AFE_PORT_ID_INT1_MI2S_RX 0x1030 ++#define AFE_PORT_ID_INT1_MI2S_TX 0x1031 ++#define AFE_PORT_ID_INT2_MI2S_RX 0x1032 ++#define AFE_PORT_ID_INT2_MI2S_TX 0x1033 ++#define AFE_PORT_ID_INT3_MI2S_RX 0x1034 ++#define AFE_PORT_ID_INT3_MI2S_TX 0x1035 ++#define AFE_PORT_ID_INT4_MI2S_RX 0x1036 ++#define AFE_PORT_ID_INT4_MI2S_TX 0x1037 ++#define AFE_PORT_ID_INT5_MI2S_RX 0x1038 ++#define AFE_PORT_ID_INT5_MI2S_TX 0x1039 ++#define AFE_PORT_ID_INT6_MI2S_RX 0x103a ++#define AFE_PORT_ID_INT6_MI2S_TX 0x103b + + /* Start of the range of port IDs for TDM devices. */ + #define AFE_PORT_ID_TDM_PORT_RANGE_START 0x9000 +@@ -931,6 +945,34 @@ static struct afe_port_map port_maps[AFE_PORT_MAX] = { + [RX_CODEC_DMA_RX_7] = { AFE_PORT_ID_RX_CODEC_DMA_RX_7, + RX_CODEC_DMA_RX_7, 1, 1}, + [USB_RX] = { AFE_PORT_ID_USB_RX, USB_RX, 1, 1}, ++ [LPI_MI2S_RX_0] = { AFE_PORT_ID_INT0_MI2S_RX, ++ LPI_MI2S_RX_0, 1, 1}, ++ [LPI_MI2S_TX_0] = { AFE_PORT_ID_INT0_MI2S_TX, ++ LPI_MI2S_TX_0, 0, 1}, ++ [LPI_MI2S_RX_1] = { AFE_PORT_ID_INT1_MI2S_RX, ++ LPI_MI2S_RX_1, 1, 1}, ++ [LPI_MI2S_TX_1] = { AFE_PORT_ID_INT1_MI2S_TX, ++ LPI_MI2S_TX_1, 0, 1}, ++ [LPI_MI2S_RX_2] = { AFE_PORT_ID_INT2_MI2S_RX, ++ LPI_MI2S_RX_2, 1, 1}, ++ [LPI_MI2S_TX_2] = { AFE_PORT_ID_INT2_MI2S_TX, ++ LPI_MI2S_TX_2, 0, 1}, ++ [LPI_MI2S_RX_3] = { AFE_PORT_ID_INT3_MI2S_RX, ++ LPI_MI2S_RX_3, 1, 1}, ++ [LPI_MI2S_TX_3] = { AFE_PORT_ID_INT3_MI2S_TX, ++ LPI_MI2S_TX_3, 0, 1}, ++ [LPI_MI2S_RX_4] = { AFE_PORT_ID_INT4_MI2S_RX, ++ LPI_MI2S_RX_4, 1, 1}, ++ [LPI_MI2S_TX_4] = { AFE_PORT_ID_INT4_MI2S_TX, ++ LPI_MI2S_TX_4, 0, 1}, ++ [LPI_MI2S_RX_5] = { AFE_PORT_ID_INT5_MI2S_RX, ++ LPI_MI2S_RX_5, 1, 1}, ++ [LPI_MI2S_TX_5] = { AFE_PORT_ID_INT5_MI2S_TX, ++ LPI_MI2S_TX_5, 0, 1}, ++ [LPI_MI2S_RX_6] = { AFE_PORT_ID_INT6_MI2S_RX, ++ LPI_MI2S_RX_6, 1, 1}, ++ [LPI_MI2S_TX_6] = { AFE_PORT_ID_INT6_MI2S_TX, ++ LPI_MI2S_TX_6, 0, 1}, + }; + + static void q6afe_port_free(struct kref *ref) +@@ -1785,6 +1827,20 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id) + case AFE_PORT_ID_QUINARY_MI2S_TX: + case AFE_PORT_ID_SENARY_MI2S_RX: + case AFE_PORT_ID_SENARY_MI2S_TX: ++ case AFE_PORT_ID_INT0_MI2S_RX: ++ case AFE_PORT_ID_INT0_MI2S_TX: ++ case AFE_PORT_ID_INT1_MI2S_RX: ++ case AFE_PORT_ID_INT1_MI2S_TX: ++ case AFE_PORT_ID_INT2_MI2S_RX: ++ case AFE_PORT_ID_INT2_MI2S_TX: ++ case AFE_PORT_ID_INT3_MI2S_RX: ++ case AFE_PORT_ID_INT3_MI2S_TX: ++ case AFE_PORT_ID_INT4_MI2S_RX: ++ case AFE_PORT_ID_INT4_MI2S_TX: ++ case AFE_PORT_ID_INT5_MI2S_RX: ++ case AFE_PORT_ID_INT5_MI2S_TX: ++ case AFE_PORT_ID_INT6_MI2S_RX: ++ case AFE_PORT_ID_INT6_MI2S_TX: + cfg_type = AFE_PARAM_ID_I2S_CONFIG; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX ... AFE_PORT_ID_QUINARY_TDM_TX_7: +diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +--- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c ++++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +@@ -2,10 +2,12 @@ + // Copyright (c) 2021, Linaro Limited + + #include ++#include + #include + #include + #include + #include ++#include + #include + #include + #include +@@ -15,15 +17,54 @@ + #include "q6dsp-common.h" + #include "audioreach.h" + #include "q6apm.h" ++#include "q6prm.h" + + #define AUDIOREACH_BE_PCM_BASE 16 + ++struct q6apm_dai_priv_data { ++ struct clk *mclk; ++ struct clk *bclk; ++ bool mclk_enabled, bclk_enabled; ++}; ++ + struct q6apm_lpass_dai_data { + struct q6apm_graph *graph[APM_PORT_MAX]; + bool is_port_started[APM_PORT_MAX]; + struct audioreach_module_config module_config[APM_PORT_MAX]; ++ struct q6apm_dai_priv_data priv[APM_PORT_MAX]; + }; + ++static void q6apm_lpass_dai_disable_clocks(struct q6apm_lpass_dai_data *dai_data, int id) ++{ ++ if (dai_data->priv[id].mclk_enabled) { ++ clk_disable_unprepare(dai_data->priv[id].mclk); ++ dai_data->priv[id].mclk_enabled = false; ++ } ++ ++ if (dai_data->priv[id].bclk_enabled) { ++ clk_disable_unprepare(dai_data->priv[id].bclk); ++ dai_data->priv[id].bclk_enabled = false; ++ } ++} ++ ++static void q6apm_lpass_dai_put_clocks(struct q6apm_lpass_dai_data *dai_data) ++{ ++ int i; ++ ++ for (i = 0; i < APM_PORT_MAX; i++) { ++ q6apm_lpass_dai_disable_clocks(dai_data, i); ++ ++ if (dai_data->priv[i].mclk) { ++ clk_put(dai_data->priv[i].mclk); ++ dai_data->priv[i].mclk = NULL; ++ } ++ if (dai_data->priv[i].bclk) { ++ clk_put(dai_data->priv[i].bclk); ++ dai_data->priv[i].bclk = NULL; ++ } ++ } ++} ++ + static int q6dma_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, + const unsigned int *tx_ch_mask, +@@ -251,6 +292,62 @@ static int q6apm_lpass_dai_startup(struct snd_pcm_substream *substream, struct s + return 0; + } + ++static int q6i2s_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) ++{ ++ return q6apm_lpass_dai_startup(substream, dai); ++} ++ ++static void q6i2s_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) ++{ ++ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); ++ ++ q6apm_lpass_dai_shutdown(substream, dai); ++ q6apm_lpass_dai_disable_clocks(dai_data, dai->id); ++} ++ ++static int q6i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) ++{ ++ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); ++ struct clk *sysclk = NULL; ++ bool *enabled = NULL; ++ int ret = 0; ++ ++ switch (clk_id) { ++ case LPAIF_MI2S_MCLK: ++ sysclk = dai_data->priv[dai->id].mclk; ++ enabled = &dai_data->priv[dai->id].mclk_enabled; ++ break; ++ case LPAIF_MI2S_BCLK: ++ sysclk = dai_data->priv[dai->id].bclk; ++ enabled = &dai_data->priv[dai->id].bclk_enabled; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (sysclk) { ++ ret = clk_set_rate(sysclk, freq); ++ if (ret) { ++ dev_err(dai->dev, "Error, Unable to set rate (%d) for sysclk %d\n", ++ freq, clk_id); ++ return ret; ++ } ++ ++ if (*enabled) ++ return 0; ++ ++ ret = clk_prepare_enable(sysclk); ++ if (ret) { ++ dev_err(dai->dev, "Error, Unable to prepare (%d) sysclk\n", clk_id); ++ return ret; ++ } ++ ++ *enabled = true; ++ } ++ ++ return ret; ++} ++ + static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) + { + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); +@@ -261,6 +358,53 @@ static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) + return 0; + } + ++static int q6tdm_set_tdm_slot(struct snd_soc_dai *dai, ++ unsigned int tx_mask, ++ unsigned int rx_mask, ++ int slots, int slot_width) ++{ ++ struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); ++ struct audioreach_module_config *cfg = &dai_data->module_config[dai->id]; ++ unsigned int cap_mask, slot_mask; ++ ++ if (slot_width != 16 && slot_width != 32) { ++ dev_err(dai->dev, "%s: invalid slot_width %d\n", __func__, slot_width); ++ return -EINVAL; ++ } ++ ++ switch (slots) { ++ case 2: ++ case 4: ++ case 8: ++ case 16: ++ cap_mask = GENMASK(slots - 1, 0); ++ break; ++ default: ++ dev_err(dai->dev, "%s: invalid slots %d\n", __func__, slots); ++ return -EINVAL; ++ } ++ ++ switch (dai->id) { ++ case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: ++ slot_mask = (dai->id & 0x1) ? tx_mask : rx_mask; ++ if (slot_mask & ~cap_mask) { ++ dev_err(dai->dev, "%s: invalid slot mask 0x%x for %d slots\n", ++ __func__, slot_mask, slots); ++ return -EINVAL; ++ } ++ ++ cfg->nslots_per_frame = slots; ++ cfg->slot_width = slot_width; ++ cfg->slot_mask = slot_mask; ++ break; ++ default: ++ dev_err(dai->dev, "%s: invalid dai id 0x%x\n", __func__, dai->id); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ + static const struct snd_soc_dai_ops q6dma_ops = { + .prepare = q6apm_lpass_dai_prepare, + .startup = q6apm_lpass_dai_startup, +@@ -272,11 +416,12 @@ static const struct snd_soc_dai_ops q6dma_ops = { + + static const struct snd_soc_dai_ops q6i2s_ops = { + .prepare = q6apm_lpass_dai_prepare, +- .startup = q6apm_lpass_dai_startup, +- .shutdown = q6apm_lpass_dai_shutdown, ++ .startup = q6i2s_dai_startup, ++ .shutdown = q6i2s_lpass_dai_shutdown, + .set_channel_map = q6dma_set_channel_map, + .hw_params = q6dma_hw_params, + .set_fmt = q6i2s_set_fmt, ++ .set_sysclk = q6i2s_set_sysclk, + .trigger = q6apm_lpass_dai_trigger, + }; + +@@ -289,6 +434,17 @@ static const struct snd_soc_dai_ops q6hdmi_ops = { + .trigger = q6apm_lpass_dai_trigger, + }; + ++static const struct snd_soc_dai_ops q6tdm_ops = { ++ .prepare = q6apm_lpass_dai_prepare, ++ .startup = q6apm_lpass_dai_startup, ++ .shutdown = q6i2s_lpass_dai_shutdown, ++ .set_tdm_slot = q6tdm_set_tdm_slot, ++ .hw_params = q6dma_hw_params, ++ .set_fmt = q6i2s_set_fmt, ++ .set_sysclk = q6i2s_set_sysclk, ++ .trigger = q6apm_lpass_dai_trigger, ++}; ++ + static const struct snd_soc_component_driver q6apm_lpass_dai_component = { + .name = "q6apm-be-dai-component", + .of_xlate_dai_name = q6dsp_audio_ports_of_xlate_dai_name, +@@ -297,6 +453,65 @@ static const struct snd_soc_component_driver q6apm_lpass_dai_component = { + .remove_order = SND_SOC_COMP_ORDER_FIRST, + }; + ++static int of_q6apm_parse_dai_data(struct device *dev, ++ struct q6apm_lpass_dai_data *data) ++{ ++ int ret; ++ ++ for_each_child_of_node_scoped(dev->of_node, node) { ++ struct q6apm_dai_priv_data *priv; ++ int id; ++ ++ ret = of_property_read_u32(node, "reg", &id); ++ if (ret || id < 0 || id >= APM_PORT_MAX) { ++ dev_err(dev, "valid dai id not found:%d\n", ret); ++ continue; ++ } ++ ++ switch (id) { ++ /* MI2S specific properties */ ++ case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: ++ case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: ++ case SENARY_MI2S_RX ... SENARY_MI2S_TX: ++ case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: ++ priv = &data->priv[id]; ++ priv->mclk = of_clk_get_by_name(node, "mclk"); ++ if (IS_ERR(priv->mclk)) { ++ int err = PTR_ERR(priv->mclk); ++ ++ priv->mclk = NULL; ++ if (err == -EPROBE_DEFER) { ++ q6apm_lpass_dai_put_clocks(data); ++ return dev_err_probe(dev, err, ++ "unable to get mi2s mclk\n"); ++ } ++ } ++ ++ priv->bclk = of_clk_get_by_name(node, "bclk"); ++ if (IS_ERR(priv->bclk)) { ++ int err = PTR_ERR(priv->bclk); ++ ++ priv->bclk = NULL; ++ if (err == -EPROBE_DEFER) { ++ q6apm_lpass_dai_put_clocks(data); ++ return dev_err_probe(dev, err, ++ "unable to get mi2s bclk\n"); ++ } ++ } ++ break; ++ default: ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++static void q6apm_lpass_dai_clocks_action(void *data) ++{ ++ q6apm_lpass_dai_put_clocks(data); ++} ++ + static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) + { + struct q6dsp_audio_port_dai_driver_config cfg; +@@ -304,17 +519,26 @@ static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) + struct snd_soc_dai_driver *dais; + struct device *dev = &pdev->dev; + int num_dais; ++ int ret; + + dai_data = devm_kzalloc(dev, sizeof(*dai_data), GFP_KERNEL); + if (!dai_data) + return -ENOMEM; + + dev_set_drvdata(dev, dai_data); ++ ret = of_q6apm_parse_dai_data(dev, dai_data); ++ if (ret) ++ return ret; ++ ++ ret = devm_add_action_or_reset(dev, q6apm_lpass_dai_clocks_action, dai_data); ++ if (ret) ++ return ret; + + memset(&cfg, 0, sizeof(cfg)); + cfg.q6i2s_ops = &q6i2s_ops; + cfg.q6dma_ops = &q6dma_ops; + cfg.q6hdmi_ops = &q6hdmi_ops; ++ cfg.q6tdm_ops = &q6tdm_ops; + dais = q6dsp_audio_ports_set_config(dev, &cfg, &num_dais); + + return devm_snd_soc_register_component(dev, &q6apm_lpass_dai_component, dais, num_dais); +diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c +--- a/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c ++++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c +@@ -12,7 +12,7 @@ + #include + #include "q6dsp-lpass-clocks.h" + +-#define Q6DSP_MAX_CLK_ID 104 ++#define Q6DSP_MAX_CLK_ID 105 + #define Q6DSP_LPASS_CLK_ROOT_DEFAULT 0 + + +diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c +--- a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c ++++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c +@@ -553,11 +553,15 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { + Q6AFE_MI2S_RX_DAI("LPI RX2", LPI_MI2S_RX_2), + Q6AFE_MI2S_RX_DAI("LPI RX3", LPI_MI2S_RX_3), + Q6AFE_MI2S_RX_DAI("LPI RX4", LPI_MI2S_RX_4), ++ Q6AFE_MI2S_RX_DAI("LPI RX5", LPI_MI2S_RX_5), ++ Q6AFE_MI2S_RX_DAI("LPI RX6", LPI_MI2S_RX_6), + Q6AFE_MI2S_TX_DAI("LPI TX0", LPI_MI2S_TX_0), + Q6AFE_MI2S_TX_DAI("LPI TX1", LPI_MI2S_TX_1), + Q6AFE_MI2S_TX_DAI("LPI TX2", LPI_MI2S_TX_2), + Q6AFE_MI2S_TX_DAI("LPI TX3", LPI_MI2S_TX_3), + Q6AFE_MI2S_TX_DAI("LPI TX4", LPI_MI2S_TX_4), ++ Q6AFE_MI2S_TX_DAI("LPI TX5", LPI_MI2S_TX_5), ++ Q6AFE_MI2S_TX_DAI("LPI TX6", LPI_MI2S_TX_6), + Q6AFE_TDM_PB_DAI("Primary", 0, PRIMARY_TDM_RX_0), + Q6AFE_TDM_PB_DAI("Primary", 1, PRIMARY_TDM_RX_1), + Q6AFE_TDM_PB_DAI("Primary", 2, PRIMARY_TDM_RX_2), +@@ -712,6 +716,7 @@ struct snd_soc_dai_driver *q6dsp_audio_ports_set_config(struct device *dev, + case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: + case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: + case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: ++ case LPI_MI2S_RX_5 ... LPI_MI2S_TX_6: + q6dsp_audio_fe_dais[i].ops = cfg->q6i2s_ops; + break; + case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: +diff --git a/sound/soc/qcom/qdsp6/q6prm-clocks.c b/sound/soc/qcom/qdsp6/q6prm-clocks.c +--- a/sound/soc/qcom/qdsp6/q6prm-clocks.c ++++ b/sound/soc/qcom/qdsp6/q6prm-clocks.c +@@ -42,6 +42,11 @@ static const struct q6dsp_clk_init q6prm_clks[] = { + Q6PRM_CLK(LPASS_CLK_ID_INT5_MI2S_IBIT), + Q6PRM_CLK(LPASS_CLK_ID_INT6_MI2S_IBIT), + Q6PRM_CLK(LPASS_CLK_ID_QUI_MI2S_OSR), ++ Q6PRM_CLK(LPASS_CLK_ID_MCLK_1), ++ Q6PRM_CLK(LPASS_CLK_ID_MCLK_2), ++ Q6PRM_CLK(LPASS_CLK_ID_MCLK_3), ++ Q6PRM_CLK(LPASS_CLK_ID_MCLK_4), ++ Q6PRM_CLK(LPASS_CLK_ID_MCLK_5), + Q6PRM_CLK(LPASS_CLK_ID_WSA_CORE_MCLK), + Q6PRM_CLK(LPASS_CLK_ID_WSA_CORE_NPL_MCLK), + Q6PRM_CLK(LPASS_CLK_ID_VA_CORE_MCLK), +@@ -59,10 +64,40 @@ static const struct q6dsp_clk_init q6prm_clks[] = { + Q6PRM_CLK(LPASS_CLK_ID_WSA2_CORE_TX_MCLK), + Q6PRM_CLK(LPASS_CLK_ID_WSA2_CORE_TX_2X_MCLK), + Q6PRM_CLK(LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF0_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF0_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF1_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF1_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF2_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF2_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF3_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF3_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF4_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF4_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF5_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF5_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF6_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF6_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF7_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF7_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF8_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF8_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF9_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF9_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF10_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF10_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF11_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF11_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF12_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF12_EBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_VA_QAIF_IF0_IBIT), ++ Q6PRM_CLK(LAPSS_CLK_ID_VA_QAIF_IF0_EBIT), + Q6DSP_VOTE_CLK(LPASS_HW_MACRO_VOTE, Q6PRM_HW_CORE_ID_LPASS, + "LPASS_HW_MACRO"), + Q6DSP_VOTE_CLK(LPASS_HW_DCODEC_VOTE, Q6PRM_HW_CORE_ID_DCODEC, + "LPASS_HW_DCODEC"), ++ Q6DSP_VOTE_CLK(LPASS_HW_LPR_VOTE, Q6PRM_HW_LPR_VOTE, ++ "LPASS_HW_LPR_VOTE"), + }; + + static const struct q6dsp_clk_desc q6dsp_clk_q6prm __maybe_unused = { +diff --git a/sound/soc/qcom/qdsp6/q6prm.c b/sound/soc/qcom/qdsp6/q6prm.c +--- a/sound/soc/qcom/qdsp6/q6prm.c ++++ b/sound/soc/qcom/qdsp6/q6prm.c +@@ -31,10 +31,16 @@ struct q6prm { + #define PARAM_ID_RSC_HW_CORE 0x08001032 + #define PARAM_ID_RSC_LPASS_CORE 0x0800102B + #define PARAM_ID_RSC_AUDIO_HW_CLK 0x0800102C ++#define PARAM_ID_RSC_CPU_LPR 0x08001A6E ++ ++#define LPR_CPU_SS_SLEEP_DISABLE 0x1 + + struct prm_cmd_request_hw_core { + struct apm_module_param_data param_data; +- uint32_t hw_clk_id; ++ union { ++ u32 hw_clk_id; ++ u32 lpr_state; ++ }; + } __packed; + + struct prm_cmd_request_rsc { +@@ -62,6 +68,7 @@ static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool + struct prm_cmd_request_hw_core *req; + gpr_device_t *gdev = prm->gdev; + uint32_t opcode, rsp_opcode; ++ bool lpr_req = (hw_block_id == Q6PRM_HW_LPR_VOTE); + + if (enable) { + opcode = PRM_CMD_REQUEST_HW_RSC; +@@ -82,10 +89,13 @@ static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool + + param_data->module_instance_id = GPR_PRM_MODULE_IID; + param_data->error_code = 0; +- param_data->param_id = PARAM_ID_RSC_HW_CORE; ++ param_data->param_id = lpr_req ? PARAM_ID_RSC_CPU_LPR : PARAM_ID_RSC_HW_CORE; + param_data->param_size = sizeof(*req) - APM_MODULE_PARAM_DATA_SIZE; + +- req->hw_clk_id = hw_block_id; ++ if (lpr_req) ++ req->lpr_state = LPR_CPU_SS_SLEEP_DISABLE; ++ else ++ req->hw_clk_id = hw_block_id; + + return q6prm_send_cmd_sync(prm, pkt, rsp_opcode); + } +diff --git a/sound/soc/qcom/qdsp6/q6prm.h b/sound/soc/qcom/qdsp6/q6prm.h +--- a/sound/soc/qcom/qdsp6/q6prm.h ++++ b/sound/soc/qcom/qdsp6/q6prm.h +@@ -3,6 +3,9 @@ + #ifndef __Q6PRM_H__ + #define __Q6PRM_H__ + ++#define LPAIF_MI2S_MCLK 1 ++#define LPAIF_MI2S_BCLK 2 ++ + /* Clock ID for Primary I2S IBIT */ + #define Q6PRM_LPASS_CLK_ID_PRI_MI2S_IBIT 0x100 + /* Clock ID for Primary I2S EBIT */ +@@ -52,6 +55,17 @@ + /* Clock ID for QUINARY MI2S OSR CLK */ + #define Q6PRM_LPASS_CLK_ID_QUI_MI2S_OSR 0x116 + ++/* Clock ID for MCLK1 */ ++#define Q6PRM_LPASS_CLK_ID_MCLK_1 0x300 ++/* Clock ID for MCLK2 */ ++#define Q6PRM_LPASS_CLK_ID_MCLK_2 0x301 ++/* Clock ID for MCLK3 */ ++#define Q6PRM_LPASS_CLK_ID_MCLK_3 0x302 ++/* Clock ID for MCLK4 */ ++#define Q6PRM_LPASS_CLK_ID_MCLK_4 0x303 ++/* Clock ID for MCLK5 */ ++#define Q6PRM_LPASS_CLK_ID_MCLK_5 0x304 ++ + #define Q6PRM_LPASS_CLK_ID_WSA_CORE_MCLK 0x305 + #define Q6PRM_LPASS_CLK_ID_WSA_CORE_NPL_MCLK 0x306 + +@@ -83,10 +97,40 @@ + /* Clock ID for RX CORE MCLK2 2X MCLK */ + #define Q6PRM_LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK 0x318 + ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF0_IBIT 0x500 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF0_EBIT 0x501 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF1_IBIT 0x502 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF1_EBIT 0x503 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF2_IBIT 0x504 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF2_EBIT 0x505 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF3_IBIT 0x506 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF3_EBIT 0x507 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF4_IBIT 0x508 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF4_EBIT 0x509 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF5_IBIT 0x50A ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF5_EBIT 0x50B ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF6_IBIT 0x50C ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF6_EBIT 0x50D ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF7_IBIT 0x50E ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF7_EBIT 0x50F ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF8_IBIT 0x510 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF8_EBIT 0x511 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF9_IBIT 0x512 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF9_EBIT 0x513 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF10_IBIT 0x514 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF10_EBIT 0x515 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF11_IBIT 0x516 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF11_EBIT 0x517 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF12_IBIT 0x518 ++#define Q6PRM_LAPSS_CLK_ID_QAIF_IF12_EBIT 0x519 ++#define Q6PRM_LAPSS_CLK_ID_VA_QAIF_IF0_IBIT 0x550 ++#define Q6PRM_LAPSS_CLK_ID_VA_QAIF_IF0_EBIT 0x551 ++ + #define Q6PRM_LPASS_CLK_SRC_INTERNAL 1 + #define Q6PRM_LPASS_CLK_ROOT_DEFAULT 0 + #define Q6PRM_HW_CORE_ID_LPASS 1 + #define Q6PRM_HW_CORE_ID_DCODEC 2 ++#define Q6PRM_HW_LPR_VOTE 3 + + int q6prm_set_lpass_clock(struct device *dev, int clk_id, int clk_attr, + int clk_root, unsigned int freq); +diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c +--- a/sound/soc/qcom/qdsp6/q6routing.c ++++ b/sound/soc/qcom/qdsp6/q6routing.c +@@ -127,7 +127,14 @@ + { mix_name, "TX_CODEC_DMA_TX_2", "TX_CODEC_DMA_TX_2"}, \ + { mix_name, "TX_CODEC_DMA_TX_3", "TX_CODEC_DMA_TX_3"}, \ + { mix_name, "TX_CODEC_DMA_TX_4", "TX_CODEC_DMA_TX_4"}, \ +- { mix_name, "TX_CODEC_DMA_TX_5", "TX_CODEC_DMA_TX_5"} ++ { mix_name, "TX_CODEC_DMA_TX_5", "TX_CODEC_DMA_TX_5"}, \ ++ { mix_name, "LPI_MI2S_TX_0", "LPI_MI2S_TX_0" }, \ ++ { mix_name, "LPI_MI2S_TX_1", "LPI_MI2S_TX_1" }, \ ++ { mix_name, "LPI_MI2S_TX_2", "LPI_MI2S_TX_2" }, \ ++ { mix_name, "LPI_MI2S_TX_3", "LPI_MI2S_TX_3" }, \ ++ { mix_name, "LPI_MI2S_TX_4", "LPI_MI2S_TX_4" }, \ ++ { mix_name, "LPI_MI2S_TX_5", "LPI_MI2S_TX_5" }, \ ++ { mix_name, "LPI_MI2S_TX_6", "LPI_MI2S_TX_6" } + + #define Q6ROUTING_TX_MIXERS(id) \ + SOC_SINGLE_EXT("PRI_MI2S_TX", PRIMARY_MI2S_TX, \ +@@ -320,6 +327,27 @@ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TX_CODEC_DMA_TX_5", TX_CODEC_DMA_TX_5, \ ++ id, 1, 0, msm_routing_get_audio_mixer, \ ++ msm_routing_put_audio_mixer), \ ++ SOC_SINGLE_EXT("LPI_MI2S_TX_0", LPI_MI2S_TX_0, \ ++ id, 1, 0, msm_routing_get_audio_mixer, \ ++ msm_routing_put_audio_mixer), \ ++ SOC_SINGLE_EXT("LPI_MI2S_TX_1", LPI_MI2S_TX_1, \ ++ id, 1, 0, msm_routing_get_audio_mixer, \ ++ msm_routing_put_audio_mixer), \ ++ SOC_SINGLE_EXT("LPI_MI2S_TX_2", LPI_MI2S_TX_2, \ ++ id, 1, 0, msm_routing_get_audio_mixer, \ ++ msm_routing_put_audio_mixer), \ ++ SOC_SINGLE_EXT("LPI_MI2S_TX_3", LPI_MI2S_TX_3, \ ++ id, 1, 0, msm_routing_get_audio_mixer, \ ++ msm_routing_put_audio_mixer), \ ++ SOC_SINGLE_EXT("LPI_MI2S_TX_4", LPI_MI2S_TX_4, \ ++ id, 1, 0, msm_routing_get_audio_mixer, \ ++ msm_routing_put_audio_mixer), \ ++ SOC_SINGLE_EXT("LPI_MI2S_TX_5", LPI_MI2S_TX_5, \ ++ id, 1, 0, msm_routing_get_audio_mixer, \ ++ msm_routing_put_audio_mixer), \ ++ SOC_SINGLE_EXT("LPI_MI2S_TX_6", LPI_MI2S_TX_6, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), + +@@ -709,6 +737,26 @@ static const struct snd_kcontrol_new rxcodec_dma_rx_6_mixer_controls[] = { + static const struct snd_kcontrol_new rx_codec_dma_rx_7_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_7) }; + ++static const struct snd_kcontrol_new lpi_mi2s_rx_0_mixer_controls[] = { ++ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_0) }; ++ ++static const struct snd_kcontrol_new lpi_mi2s_rx_1_mixer_controls[] = { ++ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_1) }; ++ ++static const struct snd_kcontrol_new lpi_mi2s_rx_2_mixer_controls[] = { ++ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_2) }; ++ ++static const struct snd_kcontrol_new lpi_mi2s_rx_3_mixer_controls[] = { ++ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_3) }; ++ ++static const struct snd_kcontrol_new lpi_mi2s_rx_4_mixer_controls[] = { ++ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_4) }; ++ ++static const struct snd_kcontrol_new lpi_mi2s_rx_5_mixer_controls[] = { ++ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_5) }; ++ ++static const struct snd_kcontrol_new lpi_mi2s_rx_6_mixer_controls[] = { ++ Q6ROUTING_RX_MIXERS(LPI_MI2S_RX_6) }; + + static const struct snd_kcontrol_new mmul1_mixer_controls[] = { + Q6ROUTING_TX_MIXERS(MSM_FRONTEND_DAI_MULTIMEDIA1) }; +@@ -938,6 +986,27 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { + SND_SOC_DAPM_MIXER("USB_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + usb_rx_mixer_controls, + ARRAY_SIZE(usb_rx_mixer_controls)), ++ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, ++ lpi_mi2s_rx_0_mixer_controls, ++ ARRAY_SIZE(lpi_mi2s_rx_0_mixer_controls)), ++ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, ++ lpi_mi2s_rx_1_mixer_controls, ++ ARRAY_SIZE(lpi_mi2s_rx_1_mixer_controls)), ++ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0, ++ lpi_mi2s_rx_2_mixer_controls, ++ ARRAY_SIZE(lpi_mi2s_rx_2_mixer_controls)), ++ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0, ++ lpi_mi2s_rx_3_mixer_controls, ++ ARRAY_SIZE(lpi_mi2s_rx_3_mixer_controls)), ++ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0, ++ lpi_mi2s_rx_4_mixer_controls, ++ ARRAY_SIZE(lpi_mi2s_rx_4_mixer_controls)), ++ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_5 Audio Mixer", SND_SOC_NOPM, 0, 0, ++ lpi_mi2s_rx_5_mixer_controls, ++ ARRAY_SIZE(lpi_mi2s_rx_5_mixer_controls)), ++ SND_SOC_DAPM_MIXER("LPI_MI2S_RX_6 Audio Mixer", SND_SOC_NOPM, 0, 0, ++ lpi_mi2s_rx_6_mixer_controls, ++ ARRAY_SIZE(lpi_mi2s_rx_6_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0, + mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia2 Mixer", SND_SOC_NOPM, 0, 0, +@@ -1031,6 +1100,13 @@ static const struct snd_soc_dapm_route intercon[] = { + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_6 Audio Mixer", "RX_CODEC_DMA_RX_6"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_7 Audio Mixer", "RX_CODEC_DMA_RX_7"), + Q6ROUTING_RX_DAPM_ROUTE("USB_RX Audio Mixer", "USB_RX"), ++ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_0 Audio Mixer", "LPI_MI2S_RX_0"), ++ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_1 Audio Mixer", "LPI_MI2S_RX_1"), ++ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_2 Audio Mixer", "LPI_MI2S_RX_2"), ++ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_3 Audio Mixer", "LPI_MI2S_RX_3"), ++ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_4 Audio Mixer", "LPI_MI2S_RX_4"), ++ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_5 Audio Mixer", "LPI_MI2S_RX_5"), ++ Q6ROUTING_RX_DAPM_ROUTE("LPI_MI2S_RX_6 Audio Mixer", "LPI_MI2S_RX_6"), + Q6ROUTING_TX_DAPM_ROUTE("MultiMedia1 Mixer"), + Q6ROUTING_TX_DAPM_ROUTE("MultiMedia2 Mixer"), + Q6ROUTING_TX_DAPM_ROUTE("MultiMedia3 Mixer"), +diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c +--- a/sound/soc/qcom/qdsp6/topology.c ++++ b/sound/soc/qcom/qdsp6/topology.c +@@ -753,6 +753,103 @@ static int audioreach_widget_i2s_module_load(struct audioreach_module *mod, + return 0; + } + ++static int audioreach_widget_audio_if_module_load(struct audioreach_module *mod, ++ const struct snd_soc_tplg_vendor_array *mod_array) ++{ ++ const struct snd_soc_tplg_vendor_value_elem *mod_elem; ++ int tkn_count = 0; ++ u32 val; ++ ++ mod_elem = mod_array->value; ++ ++ while (tkn_count < le32_to_cpu(mod_array->num_elems)) { ++ val = le32_to_cpu(mod_elem->value); ++ switch (le32_to_cpu(mod_elem->token)) { ++ case AR_TKN_U32_MODULE_HW_IF_IDX: ++ mod->hw_interface_idx = val; ++ break; ++ case AR_TKN_U32_MODULE_FMT_DATA: ++ mod->data_format = val; ++ break; ++ case AR_TKN_U16_MODULE_SYNC_SRC: ++ if (val > U16_MAX) ++ return -EINVAL; ++ mod->sync_src = (u16)val; ++ break; ++ case AR_TKN_U16_MODULE_CTRL_DATA_OUT_ENABLE: ++ if (val > U16_MAX) ++ return -EINVAL; ++ mod->ctrl_data_out_enable = (u16)val; ++ break; ++ case AR_TKN_U32_MODULE_SLOT_MASK: ++ mod->slot_mask = val; ++ break; ++ case AR_TKN_U16_MODULE_NSLOTS_PER_FRAME: ++ if (val > U16_MAX) ++ return -EINVAL; ++ mod->nslots_per_frame = (u16)val; ++ break; ++ case AR_TKN_U16_MODULE_SLOT_WIDTH: ++ if (val > U16_MAX) ++ return -EINVAL; ++ mod->slot_width = (u16)val; ++ break; ++ case AR_TKN_U16_MODULE_INTF_MODE: ++ if (val > U16_MAX) ++ return -EINVAL; ++ mod->intf_mode = (u16)val; ++ break; ++ case AR_TKN_U16_MODULE_SYNC_MODE: ++ if (val > U16_MAX) ++ return -EINVAL; ++ mod->sync_mode = (u16)val; ++ break; ++ case AR_TKN_U16_MODULE_CTRL_INVERT_SYNC_PULSE: ++ if (val > U16_MAX) ++ return -EINVAL; ++ mod->ctrl_invert_sync_pulse = (u16)val; ++ break; ++ case AR_TKN_U16_MODULE_CTRL_SYNC_DATA_DELAY: ++ if (val > U16_MAX) ++ return -EINVAL; ++ mod->ctrl_sync_data_delay = (u16)val; ++ break; ++ case AR_TKN_U16_MODULE_QAIF_TYPE: ++ if (val > U16_MAX) ++ return -EINVAL; ++ mod->qaif_type = (u16)val; ++ break; ++ case AR_TKN_U32_MODULE_ACTIVE_LANE_MASK: ++ mod->active_lane_mask = val; ++ break; ++ case AR_TKN_U32_MODULE_FRAME_SYNC_RATE: ++ mod->frame_sync_rate = val; ++ break; ++ case AR_TKN_U16_MODULE_BIT_CLK_TYPE: ++ if (val > U16_MAX) ++ return -EINVAL; ++ mod->bit_clk_type = (u16)val; ++ break; ++ case AR_TKN_U8_MODULE_INV_INT_BIT_CLK: ++ if (val > U8_MAX) ++ return -EINVAL; ++ mod->inv_int_bit_clk = (u8)val; ++ break; ++ case AR_TKN_U8_MODULE_INV_EXT_BIT_CLK: ++ if (val > U8_MAX) ++ return -EINVAL; ++ mod->inv_ext_bit_clk = (u8)val; ++ break; ++ default: ++ break; ++ } ++ tkn_count++; ++ mod_elem++; ++ } ++ ++ return 0; ++} ++ + static int audioreach_widget_dp_module_load(struct audioreach_module *mod, + const struct snd_soc_tplg_vendor_array *mod_array) + { +@@ -806,6 +903,12 @@ static int audioreach_widget_load_buffer(struct snd_soc_component *component, + case MODULE_ID_I2S_SOURCE: + audioreach_widget_i2s_module_load(mod, mod_array); + break; ++ case MODULE_ID_AUDIO_IF_SINK: ++ case MODULE_ID_AUDIO_IF_SOURCE: ++ ret = audioreach_widget_audio_if_module_load(mod, mod_array); ++ if (ret) ++ return ret; ++ break; + case MODULE_ID_DISPLAY_PORT_SINK: + audioreach_widget_dp_module_load(mod, mod_array); + break; +@@ -1313,7 +1416,6 @@ int audioreach_tplg_init(struct snd_soc_component *component) + { + struct snd_soc_card *card = component->card; + struct device *dev = component->dev; +- const struct firmware *fw; + int ret; + + /* Inline with Qualcomm UCM configs and linux-firmware path */ +@@ -1323,6 +1425,7 @@ int audioreach_tplg_init(struct snd_soc_component *component) + if (!tplg_fw_name) + return -ENOMEM; + ++ const struct firmware *fw __free(firmware) = NULL; + ret = request_firmware(&fw, tplg_fw_name, dev); + if (ret < 0) { + dev_err(dev, "tplg firmware loading %s failed %d\n", tplg_fw_name, ret); +@@ -1335,8 +1438,6 @@ int audioreach_tplg_init(struct snd_soc_component *component) + dev_err(dev, "tplg component load failed: %d\n", ret); + } + +- release_firmware(fw); +- + return ret; + } + EXPORT_SYMBOL_GPL(audioreach_tplg_init); +diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c +--- a/sound/soc/qcom/sc8280xp.c ++++ b/sound/soc/qcom/sc8280xp.c +@@ -12,17 +12,157 @@ + #include + #include + #include "qdsp6/q6afe.h" ++#include "qdsp6/q6apm.h" ++#include "qdsp6/q6prm.h" ++#include "qdsp6/q6dsp-common.h" + #include "common.h" + #include "sdw.h" + ++#define I2S_MCLKFS 256 ++ ++#define I2S_MCLK_RATE(rate) \ ++ ((rate) * (I2S_MCLKFS)) ++#define I2S_BIT_RATE(rate, channels, format) \ ++ ((rate) * (channels) * (format)) ++ ++static struct snd_soc_dapm_widget sc8280xp_dapm_widgets[] = { ++ SND_SOC_DAPM_HP("Headphone Jack", NULL), ++ SND_SOC_DAPM_MIC("Mic Jack", NULL), ++ SND_SOC_DAPM_SPK("DP0 Jack", NULL), ++ SND_SOC_DAPM_SPK("DP1 Jack", NULL), ++ SND_SOC_DAPM_SPK("DP2 Jack", NULL), ++ SND_SOC_DAPM_SPK("DP3 Jack", NULL), ++ SND_SOC_DAPM_SPK("DP4 Jack", NULL), ++ SND_SOC_DAPM_SPK("DP5 Jack", NULL), ++ SND_SOC_DAPM_SPK("DP6 Jack", NULL), ++ SND_SOC_DAPM_SPK("DP7 Jack", NULL), ++}; ++ ++static const struct snd_kcontrol_new max98090_controls[] = { ++ SOC_DAPM_PIN_SWITCH("Headset Mic12"), ++ SOC_DAPM_PIN_SWITCH("Headphone"), ++ SOC_DAPM_PIN_SWITCH("Headset Mic56"), ++ SOC_DAPM_PIN_SWITCH("Speaker"), ++ SOC_DAPM_PIN_SWITCH("Receiver"), ++ SOC_DAPM_PIN_SWITCH("Int Mic"), ++}; ++ ++static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = { ++ SND_SOC_DAPM_HP("Headphone Jack", NULL), ++ SND_SOC_DAPM_MIC("Mic Jack", NULL), ++ SND_SOC_DAPM_HP("Headphone", NULL), ++ SND_SOC_DAPM_MIC("Headset Mic12", NULL), ++ SND_SOC_DAPM_MIC("Headset Mic56", NULL), ++ SND_SOC_DAPM_MIC("Int Mic", NULL), ++ SND_SOC_DAPM_SPK("Receiver", NULL), ++ SND_SOC_DAPM_SPK("Speaker", NULL), ++}; ++ ++struct qcom_snd_soc_common { ++ const char *driver_name; ++ const struct snd_soc_dapm_widget *dapm_widgets; ++ int num_dapm_widgets; ++ const struct snd_soc_dapm_route *dapm_routes; ++ int num_dapm_routes; ++ const struct snd_kcontrol_new *controls; ++ int num_controls; ++ unsigned int codec_dai_fmt; ++ bool codec_sysclk_set; ++ bool mi2s_mclk_enable; ++ bool mi2s_bclk_enable; ++ bool wcd_jack; ++ int (*snd_prepare)(struct snd_pcm_substream *substream); ++}; ++ + struct sc8280xp_snd_data { + bool stream_prepared[AFE_PORT_MAX]; + struct snd_soc_card *card; + struct snd_soc_jack jack; + struct snd_soc_jack dp_jack[8]; ++ const struct qcom_snd_soc_common *priv; + bool jack_setup; + }; + ++static inline int sc8280xp_get_mclk_freq(struct snd_pcm_hw_params *params) ++{ ++ int rate = params_rate(params); ++ ++ switch (rate) { ++ case 11025: ++ case 44100: ++ case 88200: ++ return I2S_MCLK_RATE(44100); ++ default: ++ break; ++ } ++ ++ return I2S_MCLK_RATE(rate); ++} ++ ++static inline int sc8280xp_get_bclk_freq(struct snd_pcm_hw_params *params) ++{ ++ return I2S_BIT_RATE(params_rate(params), ++ params_channels(params), ++ snd_pcm_format_width(params_format(params))); ++} ++ ++static int sc8280xp_tdm_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); ++ struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); ++ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); ++ struct snd_soc_dai *codec_dai; ++ struct qcom_snd_tdm_slot_cfg cpu_cfg; ++ struct qcom_snd_tdm_slot_cfg codec_cfg; ++ unsigned int bclk_freq; ++ int ret; ++ int i; ++ ++ ret = qcom_snd_get_dai_tdm_slots(rtd, &cpu_cfg, &codec_cfg); ++ if (ret) ++ return ret == -EINVAL ? 0 : ret; ++ ++ if (!cpu_cfg.slots) ++ return 0; ++ ++ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); ++ if (ret) ++ return ret; ++ ++ ret = qcom_snd_apply_dai_tdm_slots_cfg(rtd, &cpu_cfg, &codec_cfg); ++ if (ret) ++ return ret; ++ ++ bclk_freq = snd_soc_tdm_params_to_bclk(params, cpu_cfg.slot_width, cpu_cfg.slots, 1); ++ if (!bclk_freq) ++ return -EINVAL; ++ ++ if (data->priv->mi2s_bclk_enable) { ++ ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_MI2S_BCLK, bclk_freq, ++ SND_SOC_CLOCK_IN); ++ if (ret) { ++ dev_err(rtd->dev, "%s: failed to set cpu sysclk: %d\n", ++ __func__, ret); ++ return ret; ++ } ++ } ++ ++ if (data->priv->codec_sysclk_set) { ++ for_each_rtd_codec_dais(rtd, i, codec_dai) { ++ ret = snd_soc_dai_set_sysclk(codec_dai, 0, bclk_freq, ++ SND_SOC_CLOCK_IN); ++ if (ret) { ++ dev_err(rtd->dev, "%s: failed to set codec sysclk on %s: %d\n", ++ __func__, codec_dai->name, ret); ++ return ret; ++ } ++ } ++ } ++ ++ return 0; ++} ++ + static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) + { + struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); +@@ -32,10 +172,6 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) + int dp_pcm_id = 0; + + switch (cpu_dai->id) { +- case PRIMARY_MI2S_RX...QUATERNARY_MI2S_TX: +- case QUINARY_MI2S_RX...QUINARY_MI2S_TX: +- snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); +- break; + case WSA_CODEC_DMA_RX_0: + case WSA_CODEC_DMA_RX_1: + /* +@@ -64,7 +200,10 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) + if (dp_jack) + return qcom_snd_dp_jack_setup(rtd, dp_jack, dp_pcm_id); + +- return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); ++ if (data->priv->wcd_jack) ++ return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); ++ ++ return 0; + } + + static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, +@@ -96,12 +235,118 @@ static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + return 0; + } + ++static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); ++ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); ++ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); ++ struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); ++ int mclk_freq = sc8280xp_get_mclk_freq(params); ++ int bclk_freq = sc8280xp_get_bclk_freq(params); ++ int ret; ++ ++ switch (cpu_dai->id) { ++ case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: ++ case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: ++ case SENARY_MI2S_RX ... SENARY_MI2S_TX: ++ case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4: ++ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); ++ if (ret && ret != -ENOTSUPP) ++ return ret; ++ ++ if (data->priv->codec_dai_fmt) { ++ ret = snd_soc_dai_set_fmt(codec_dai, ++ data->priv->codec_dai_fmt); ++ if (ret && ret != -ENOTSUPP) ++ return ret; ++ } ++ ++ if (data->priv->mi2s_mclk_enable) { ++ ret = snd_soc_dai_set_sysclk(cpu_dai, ++ LPAIF_MI2S_MCLK, mclk_freq, ++ SND_SOC_CLOCK_OUT); ++ if (ret) ++ return ret; ++ } ++ ++ if (data->priv->mi2s_bclk_enable) { ++ ret = snd_soc_dai_set_sysclk(cpu_dai, ++ LPAIF_MI2S_BCLK, bclk_freq, ++ SND_SOC_CLOCK_OUT); ++ if (ret) ++ return ret; ++ } ++ ++ if (data->priv->codec_sysclk_set) { ++ ret = snd_soc_dai_set_sysclk(codec_dai, ++ 0, mclk_freq, ++ SND_SOC_CLOCK_IN); ++ if (ret && ret != -ENOTSUPP) ++ return ret; ++ } ++ break; ++ case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: ++ return sc8280xp_tdm_hw_params(substream, params); ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ ++/* ++ * WSA and WSA2 are handled as a single interface with the ++ * following channels mask: ++ * __________________________________________________ ++ * | Bits | 3 | 2 | 1 | 0 | ++ * --------------------------------------------------- ++ * | Line | WSA2 Ch2 | WSA2 Ch1 | WSA Ch2 | WSA Ch1 | ++ * --------------------------------------------------- ++ * ++ * The Ayaneo Pocket S2 speakers are connected only to ++ * the WSA2 interface and the WSA interface is not enabled. ++ * ++ * Set the channel mapping on the WSA2 channels only. ++ */ ++static const unsigned int ayaneo_ps2_channels_mapping[] = { ++ 0, /* WSA Ch1 */ ++ 0, /* WSA Ch2 */ ++ PCM_CHANNEL_FL, /* WSA2 Ch1 */ ++ PCM_CHANNEL_FR /* WSA2 Ch2 */ ++}; ++ ++static int ayaneo_ps2_snd_prepare(struct snd_pcm_substream *substream) ++{ ++ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); ++ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); ++ unsigned int channels = substream->runtime->channels; ++ ++ if (cpu_dai->id != WSA_CODEC_DMA_RX_0) ++ return 0; ++ ++ if (channels != 2) ++ return -EINVAL; ++ ++ return snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, ++ ARRAY_SIZE(ayaneo_ps2_channels_mapping), ++ ayaneo_ps2_channels_mapping); ++} ++ + static int sc8280xp_snd_prepare(struct snd_pcm_substream *substream) + { + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + ++ if (data->priv->snd_prepare) { ++ int ret; ++ ++ ret = data->priv->snd_prepare(substream); ++ if (ret) ++ return ret; ++ } ++ + return qcom_snd_sdw_prepare(substream, &data->stream_prepared[cpu_dai->id]); + } + +@@ -117,6 +362,7 @@ static int sc8280xp_snd_hw_free(struct snd_pcm_substream *substream) + static const struct snd_soc_ops sc8280xp_be_ops = { + .startup = qcom_snd_sdw_startup, + .shutdown = qcom_snd_sdw_shutdown, ++ .hw_params = sc8280xp_snd_hw_params, + .hw_free = sc8280xp_snd_hw_free, + .prepare = sc8280xp_snd_prepare, + }; +@@ -145,38 +391,182 @@ static int sc8280xp_platform_probe(struct platform_device *pdev) + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; +- card->owner = THIS_MODULE; ++ + /* Allocate the private data */ + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + ++ data->priv = of_device_get_match_data(dev); ++ if (!data->priv) ++ return -ENODEV; ++ ++ card->owner = THIS_MODULE; + card->dev = dev; + dev_set_drvdata(dev, card); + snd_soc_card_set_drvdata(card, data); ++ card->dapm_widgets = data->priv->dapm_widgets; ++ card->num_dapm_widgets = data->priv->num_dapm_widgets; ++ card->dapm_routes = data->priv->dapm_routes; ++ card->num_dapm_routes = data->priv->num_dapm_routes; ++ card->controls = data->priv->controls; ++ card->num_controls = data->priv->num_controls; ++ + ret = qcom_snd_parse_of(card); + if (ret) + return ret; + +- card->driver_name = of_device_get_match_data(dev); ++ card->driver_name = data->priv->driver_name; + sc8280xp_add_be_ops(card); + return devm_snd_soc_register_card(dev, card); + } + ++static struct qcom_snd_soc_common ayaneo_ps2_priv_data = { ++ .driver_name = "ayaneo-ps2", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .snd_prepare = ayaneo_ps2_snd_prepare, ++ .wcd_jack = true, ++}; ++ ++static const struct qcom_snd_soc_common eliza_priv_data = { ++ .driver_name = "eliza", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .wcd_jack = true, ++}; ++ ++static const struct qcom_snd_soc_common hawi_priv_data = { ++ .driver_name = "hawi", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .codec_sysclk_set = true, ++ .mi2s_bclk_enable = true, ++ .wcd_jack = true, ++}; ++ ++static const struct qcom_snd_soc_common kaanapali_priv_data = { ++ .driver_name = "kaanapali", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .wcd_jack = true, ++}; ++ ++static const struct qcom_snd_soc_common qcs9100_priv_data = { ++ .driver_name = "sa8775p", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++}; ++ ++static const struct qcom_snd_soc_common qcs615_priv_data = { ++ .driver_name = "qcs615", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .codec_sysclk_set = true, ++}; ++ ++static const struct qcom_snd_soc_common qcm6490_priv_data = { ++ .driver_name = "qcm6490", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .wcd_jack = true, ++}; ++ ++static const struct qcom_snd_soc_common qcs6490_priv_data = { ++ .driver_name = "qcs6490", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .wcd_jack = true, ++}; ++ ++static const struct qcom_snd_soc_common qcs8275_priv_data = { ++ .driver_name = "qcs8300", ++ .dapm_widgets = max98090_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(max98090_dapm_widgets), ++ .controls = max98090_controls, ++ .num_controls = ARRAY_SIZE(max98090_controls), ++ .codec_sysclk_set = true, ++ .codec_dai_fmt = SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_BC_FC, ++}; ++ ++static const struct qcom_snd_soc_common sc8280xp_priv_data = { ++ .driver_name = "sc8280xp", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .wcd_jack = true, ++}; ++ ++static const struct qcom_snd_soc_common sm8450_priv_data = { ++ .driver_name = "sm8450", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .wcd_jack = true, ++ /* I2S Connected to HDMI */ ++ .mi2s_mclk_enable = true, ++ .mi2s_bclk_enable = true, ++ .codec_dai_fmt = SND_SOC_DAIFMT_BC_FC | ++ SND_SOC_DAIFMT_NB_NF | ++ SND_SOC_DAIFMT_I2S, ++}; ++ ++static const struct qcom_snd_soc_common sm8475_priv_data = { ++ .driver_name = "sm8475", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .wcd_jack = true, ++}; ++ ++static const struct qcom_snd_soc_common sm8550_priv_data = { ++ .driver_name = "sm8550", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .wcd_jack = true, ++ /* I2S Connected to HDMI */ ++ .mi2s_mclk_enable = true, ++ .mi2s_bclk_enable = true, ++ .codec_dai_fmt = SND_SOC_DAIFMT_BC_FC | ++ SND_SOC_DAIFMT_NB_NF | ++ SND_SOC_DAIFMT_I2S, ++}; ++ ++static const struct qcom_snd_soc_common sm8650_priv_data = { ++ .driver_name = "sm8650", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .wcd_jack = true, ++ /* I2S Connected to HDMI */ ++ .mi2s_mclk_enable = true, ++ .mi2s_bclk_enable = true, ++ .codec_dai_fmt = SND_SOC_DAIFMT_BC_FC | ++ SND_SOC_DAIFMT_NB_NF | ++ SND_SOC_DAIFMT_I2S, ++}; ++ ++static const struct qcom_snd_soc_common sm8750_priv_data = { ++ .driver_name = "sm8750", ++ .dapm_widgets = sc8280xp_dapm_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), ++ .wcd_jack = true, ++}; ++ + static const struct of_device_id snd_sc8280xp_dt_match[] = { +- {.compatible = "qcom,eliza-sndcard", "eliza"}, +- {.compatible = "qcom,kaanapali-sndcard", "kaanapali"}, +- {.compatible = "qcom,qcm6490-idp-sndcard", "qcm6490"}, +- {.compatible = "qcom,qcs615-sndcard", "qcs615"}, +- {.compatible = "qcom,qcs6490-rb3gen2-sndcard", "qcs6490"}, +- {.compatible = "qcom,qcs8275-sndcard", "qcs8300"}, +- {.compatible = "qcom,qcs9075-sndcard", "sa8775p"}, +- {.compatible = "qcom,qcs9100-sndcard", "sa8775p"}, +- {.compatible = "qcom,sc8280xp-sndcard", "sc8280xp"}, +- {.compatible = "qcom,sm8450-sndcard", "sm8450"}, +- {.compatible = "qcom,sm8550-sndcard", "sm8550"}, +- {.compatible = "qcom,sm8650-sndcard", "sm8650"}, +- {.compatible = "qcom,sm8750-sndcard", "sm8750"}, ++ { .compatible = "ayaneo,pocket-s2-sndcard", .data = &ayaneo_ps2_priv_data }, ++ { .compatible = "qcom,eliza-sndcard", .data = &eliza_priv_data }, ++ { .compatible = "qcom,hawi-sndcard", .data = &hawi_priv_data }, ++ { .compatible = "qcom,kaanapali-sndcard", .data = &kaanapali_priv_data }, ++ { .compatible = "qcom,maili-sndcard", .data = &hawi_priv_data }, ++ { .compatible = "qcom,qcm6490-idp-sndcard", .data = &qcm6490_priv_data }, ++ { .compatible = "qcom,qcs615-sndcard", .data = &qcs615_priv_data }, ++ { .compatible = "qcom,qcs6490-rb3gen2-sndcard", .data = &qcs6490_priv_data }, ++ { .compatible = "qcom,qcs8275-sndcard", .data = &qcs8275_priv_data }, ++ { .compatible = "qcom,qcs9075-sndcard", .data = &qcs9100_priv_data }, ++ { .compatible = "qcom,qcs9100-sndcard", .data = &qcs9100_priv_data }, ++ { .compatible = "qcom,sc8280xp-sndcard", .data = &sc8280xp_priv_data }, ++ { .compatible = "qcom,sm8450-sndcard", .data = &sm8450_priv_data }, ++ { .compatible = "qcom,sm8475-sndcard", .data = &sm8475_priv_data }, ++ { .compatible = "qcom,sm8550-sndcard", .data = &sm8550_priv_data }, ++ { .compatible = "qcom,sm8650-sndcard", .data = &sm8650_priv_data }, ++ { .compatible = "qcom,sm8750-sndcard", .data = &sm8750_priv_data }, + {} + }; + +diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c +--- a/sound/soc/qcom/sdm845.c ++++ b/sound/soc/qcom/sdm845.c +@@ -36,7 +36,6 @@ struct sdm845_snd_data { + bool jack_setup; + bool slim_port_setup; + bool stream_prepared[AFE_PORT_MAX]; +- struct snd_soc_card *card; + uint32_t pri_mi2s_clk_count; + uint32_t sec_mi2s_clk_count; + uint32_t quat_tdm_clk_count; +@@ -564,7 +563,6 @@ static int sdm845_snd_platform_probe(struct platform_device *pdev) + if (ret) + return ret; + +- data->card = card; + snd_soc_card_set_drvdata(card, data); + + sdm845_add_ops(card); +diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c +--- a/sound/soc/qcom/sm8250.c ++++ b/sound/soc/qcom/sm8250.c +@@ -112,6 +112,22 @@ static int sm8250_snd_startup(struct snd_pcm_substream *substream) + snd_soc_dai_set_fmt(cpu_dai, fmt); + snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); + break; ++ case LPI_MI2S_RX_0: ++ codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S; ++ snd_soc_dai_set_sysclk(cpu_dai, ++ Q6AFE_LPASS_CLK_ID_INT0_MI2S_IBIT, ++ MI2S_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); ++ snd_soc_dai_set_fmt(cpu_dai, fmt); ++ snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); ++ break; ++ case LPI_MI2S_TX_3: ++ codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S; ++ snd_soc_dai_set_sysclk(cpu_dai, ++ Q6AFE_LPASS_CLK_ID_INT3_MI2S_IBIT, ++ MI2S_BCLK_RATE, SNDRV_PCM_STREAM_CAPTURE); ++ snd_soc_dai_set_fmt(cpu_dai, fmt); ++ snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); ++ break; + default: + break; + } +@@ -194,6 +210,7 @@ static const struct of_device_id snd_sm8250_dt_match[] = { + { .compatible = "qcom,qrb2210-sndcard", .data = "qcm2290" }, + { .compatible = "qcom,qrb4210-rb2-sndcard", .data = "sm4250" }, + { .compatible = "qcom,qrb5165-rb5-sndcard", .data = "sm8250" }, ++ { .compatible = "qcom,sdm660-sndcard", .data = "sdm660" }, + { .compatible = "qcom,sm8250-sndcard", .data = "sm8250" }, + {} + }; +diff --git a/sound/soc/qcom/storm.c b/sound/soc/qcom/storm.c +--- a/sound/soc/qcom/storm.c ++++ b/sound/soc/qcom/storm.c +@@ -64,21 +64,21 @@ static struct snd_soc_dai_link storm_dai_link = { + SND_SOC_DAILINK_REG(hifi), + }; + +-static int storm_parse_of(struct snd_soc_card *card) ++static int storm_parse_of(struct device *dev) + { +- struct snd_soc_dai_link *dai_link = card->dai_link; +- struct device_node *np = card->dev->of_node; ++ struct snd_soc_dai_link *dai_link = &storm_dai_link; ++ struct device_node *np = dev->of_node; + + dai_link->cpus->of_node = of_parse_phandle(np, "cpu", 0); + if (!dai_link->cpus->of_node) { +- dev_err(card->dev, "error getting cpu phandle\n"); ++ dev_err(dev, "error getting cpu phandle\n"); + return -EINVAL; + } + dai_link->platforms->of_node = dai_link->cpus->of_node; + + dai_link->codecs->of_node = of_parse_phandle(np, "codec", 0); + if (!dai_link->codecs->of_node) { +- dev_err(card->dev, "error getting codec phandle\n"); ++ dev_err(dev, "error getting codec phandle\n"); + return -EINVAL; + } + +@@ -106,7 +106,7 @@ static int storm_platform_probe(struct platform_device *pdev) + card->dai_link = &storm_dai_link; + card->num_links = 1; + +- ret = storm_parse_of(card); ++ ret = storm_parse_of(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "error resolving dai links: %d\n", ret); + return ret; +diff --git a/sound/soc/renesas/fsi.c b/sound/soc/renesas/fsi.c +--- a/sound/soc/renesas/fsi.c ++++ b/sound/soc/renesas/fsi.c +@@ -1979,8 +1979,8 @@ static const struct of_device_id fsi_of_match[] = { + MODULE_DEVICE_TABLE(of, fsi_of_match); + + static const struct platform_device_id fsi_id_table[] = { +- { "sh_fsi", (kernel_ulong_t)&fsi1_core }, +- {}, ++ /* an array with no valid entry prevents matching on driver name */ ++ { } + }; + MODULE_DEVICE_TABLE(platform, fsi_id_table); + +@@ -1992,7 +1992,7 @@ static int fsi_probe(struct platform_device *pdev) + const struct fsi_core *core; + struct fsi_priv *fsi; + struct resource *res; +- unsigned int irq; ++ int irq; + int ret; + + memset(&info, 0, sizeof(info)); +@@ -2007,12 +2007,15 @@ static int fsi_probe(struct platform_device *pdev) + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- irq = platform_get_irq(pdev, 0); +- if (!res || (int)irq <= 0) { ++ if (!res) { + dev_err(&pdev->dev, "Not enough FSI platform resources.\n"); + return -ENODEV; + } + ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) ++ return irq; ++ + master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL); + if (!master) + return -ENOMEM; +diff --git a/sound/soc/renesas/rcar/adg.c b/sound/soc/renesas/rcar/adg.c +--- a/sound/soc/renesas/rcar/adg.c ++++ b/sound/soc/renesas/rcar/adg.c +@@ -565,7 +565,7 @@ static struct clk *rsnd_adg_create_null_clk(struct rsnd_priv *priv, + struct clk *clk; + + clk = clk_register_fixed_rate(dev, name, parent, 0, 0); +- if (IS_ERR_OR_NULL(clk)) { ++ if (IS_ERR(clk)) { + dev_err(dev, "create null clk error\n"); + return ERR_CAST(clk); + } +@@ -618,7 +618,7 @@ static int rsnd_adg_get_clkin(struct rsnd_priv *priv) + * No "adg" is not error + */ + clk = devm_clk_get(dev, "adg"); +- if (IS_ERR_OR_NULL(clk)) ++ if (IS_ERR(clk)) + clk = rsnd_adg_null_clk_get(priv); + adg->adg = clk; + +@@ -626,9 +626,9 @@ static int rsnd_adg_get_clkin(struct rsnd_priv *priv) + for (i = 0; i < clkin_size; i++) { + clk = devm_clk_get(dev, clkin_name[i]); + +- if (IS_ERR_OR_NULL(clk)) ++ if (IS_ERR(clk)) + clk = rsnd_adg_null_clk_get(priv); +- if (IS_ERR_OR_NULL(clk)) ++ if (IS_ERR(clk)) + goto err; + + adg->clkin[i] = clk; +diff --git a/sound/soc/renesas/siu_dai.c b/sound/soc/renesas/siu_dai.c +--- a/sound/soc/renesas/siu_dai.c ++++ b/sound/soc/renesas/siu_dai.c +@@ -715,7 +715,6 @@ static struct snd_soc_dai_driver siu_i2s_dai = { + + static int siu_probe(struct platform_device *pdev) + { +- const struct firmware *fw_entry; + struct resource *res, *region; + struct siu_info *info; + int ret; +@@ -726,6 +725,7 @@ static int siu_probe(struct platform_device *pdev) + siu_i2s_data = info; + info->dev = &pdev->dev; + ++ const struct firmware *fw_entry __free(firmware) = NULL; + ret = request_firmware(&fw_entry, "siu_spb.bin", &pdev->dev); + if (ret) + return ret; +@@ -736,8 +736,6 @@ static int siu_probe(struct platform_device *pdev) + */ + memcpy(&info->fw, fw_entry->data, fw_entry->size); + +- release_firmware(fw_entry); +- + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; +diff --git a/sound/soc/rockchip/rk3288_hdmi_analog.c b/sound/soc/rockchip/rk3288_hdmi_analog.c +--- a/sound/soc/rockchip/rk3288_hdmi_analog.c ++++ b/sound/soc/rockchip/rk3288_hdmi_analog.c +@@ -185,10 +185,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) + gpiod_set_consumer_name(machine->gpio_hp_en, "hp_en"); + + ret = snd_soc_of_parse_card_name(card, "rockchip,model"); +- if (ret) { +- dev_err(card->dev, "SoC parse card name failed %d\n", ret); ++ if (ret) + return ret; +- } + + rk_dailink.codecs[0].of_node = of_parse_phandle(np, + "rockchip,audio-codec", +@@ -207,10 +205,9 @@ static int snd_rk_mc_probe(struct platform_device *pdev) + } + + ret = snd_soc_get_dai_name(&args, &rk_dailink.codecs[0].dai_name); +- if (ret) { +- dev_err(&pdev->dev, "Unable to get codec_dai_name\n"); +- return ret; +- } ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, ++ "Unable to get codec_dai_name\n"); + + rk_dailink.cpus->of_node = of_parse_phandle(np, "rockchip,i2s-controller", + 0); +@@ -223,11 +220,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) + rk_dailink.platforms->of_node = rk_dailink.cpus->of_node; + + ret = snd_soc_of_parse_audio_routing(card, "rockchip,routing"); +- if (ret) { +- dev_err(&pdev->dev, +- "Unable to parse 'rockchip,routing' property\n"); ++ if (ret) + return ret; +- } + + snd_soc_card_set_drvdata(card, machine); + +diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c +--- a/sound/soc/rockchip/rockchip_i2s.c ++++ b/sound/soc/rockchip/rockchip_i2s.c +@@ -767,16 +767,14 @@ static int rockchip_i2s_probe(struct platform_device *pdev) + + /* try to prepare related clocks */ + i2s->hclk = devm_clk_get_enabled(&pdev->dev, "i2s_hclk"); +- if (IS_ERR(i2s->hclk)) { +- dev_err(&pdev->dev, "Can't retrieve i2s bus clock\n"); +- return PTR_ERR(i2s->hclk); +- } ++ if (IS_ERR(i2s->hclk)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->hclk), ++ "Can't retrieve i2s bus clock\n"); + + i2s->mclk = devm_clk_get(&pdev->dev, "i2s_clk"); +- if (IS_ERR(i2s->mclk)) { +- dev_err(&pdev->dev, "Can't retrieve i2s master clock\n"); +- return PTR_ERR(i2s->mclk); +- } ++ if (IS_ERR(i2s->mclk)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->mclk), ++ "Can't retrieve i2s master clock\n"); + + regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(regs)) +@@ -792,7 +790,11 @@ static int rockchip_i2s_probe(struct platform_device *pdev) + + i2s->bclk_ratio = 64; + i2s->pinctrl = devm_pinctrl_get(&pdev->dev); +- if (!IS_ERR(i2s->pinctrl)) { ++ if (IS_ERR(i2s->pinctrl)) { ++ if (PTR_ERR(i2s->pinctrl) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ dev_dbg(&pdev->dev, "failed to find i2s pinctrl\n"); ++ } else { + i2s->bclk_on = pinctrl_lookup_state(i2s->pinctrl, "bclk_on"); + if (!IS_ERR_OR_NULL(i2s->bclk_on)) { + i2s->bclk_off = pinctrl_lookup_state(i2s->pinctrl, "bclk_off"); +@@ -801,8 +803,6 @@ static int rockchip_i2s_probe(struct platform_device *pdev) + return -EINVAL; + } + } +- } else { +- dev_dbg(&pdev->dev, "failed to find i2s pinctrl\n"); + } + + i2s_pinctrl_select_bclk_off(i2s); +@@ -831,16 +831,12 @@ static int rockchip_i2s_probe(struct platform_device *pdev) + &rockchip_i2s_component, + dai, 1); + +- if (ret) { +- dev_err(&pdev->dev, "Could not register DAI\n"); ++ if (ret) + return ret; +- } + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); +- if (ret) { +- dev_err(&pdev->dev, "Could not register PCM\n"); ++ if (ret) + return ret; +- } + + return 0; + } +diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c +--- a/sound/soc/rockchip/rockchip_i2s_tdm.c ++++ b/sound/soc/rockchip/rockchip_i2s_tdm.c +@@ -1263,16 +1263,14 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) + i2s_tdm->tx_reset = devm_reset_control_get_optional_exclusive(&pdev->dev, + "tx-m"); + if (IS_ERR(i2s_tdm->tx_reset)) { +- ret = PTR_ERR(i2s_tdm->tx_reset); +- return dev_err_probe(i2s_tdm->dev, ret, ++ return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->tx_reset), + "Error in tx-m reset control\n"); + } + + i2s_tdm->rx_reset = devm_reset_control_get_optional_exclusive(&pdev->dev, + "rx-m"); + if (IS_ERR(i2s_tdm->rx_reset)) { +- ret = PTR_ERR(i2s_tdm->rx_reset); +- return dev_err_probe(i2s_tdm->dev, ret, ++ return dev_err_probe(i2s_tdm->dev, PTR_ERR(i2s_tdm->rx_reset), + "Error in rx-m reset control\n"); + } + +@@ -1363,17 +1361,12 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) + ret = devm_snd_soc_register_component(&pdev->dev, + &rockchip_i2s_tdm_component, + i2s_tdm->dai, 1); +- +- if (ret) { +- dev_err(&pdev->dev, "Could not register DAI\n"); ++ if (ret) + goto err_suspend; +- } + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); +- if (ret) { +- dev_err(&pdev->dev, "Could not register PCM\n"); ++ if (ret) + goto err_suspend; +- } + + return 0; + +diff --git a/sound/soc/rockchip/rockchip_max98090.c b/sound/soc/rockchip/rockchip_max98090.c +--- a/sound/soc/rockchip/rockchip_max98090.c ++++ b/sound/soc/rockchip/rockchip_max98090.c +@@ -428,11 +428,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) + + /* Parse card name. */ + ret = snd_soc_of_parse_card_name(card, "rockchip,model"); +- if (ret) { +- dev_err(&pdev->dev, +- "Soc parse card name failed %d\n", ret); ++ if (ret) + return ret; +- } + + /* register the soc card */ + ret = devm_snd_soc_register_card(&pdev->dev, card); +diff --git a/sound/soc/rockchip/rockchip_pdm.c b/sound/soc/rockchip/rockchip_pdm.c +--- a/sound/soc/rockchip/rockchip_pdm.c ++++ b/sound/soc/rockchip/rockchip_pdm.c +@@ -630,10 +630,8 @@ static int rockchip_pdm_probe(struct platform_device *pdev) + &rockchip_pdm_component, + &rockchip_pdm_dai, 1); + +- if (ret) { +- dev_err(&pdev->dev, "could not register dai: %d\n", ret); ++ if (ret) + goto err_suspend; +- } + + rockchip_pdm_rxctrl(pdm, 0); + +@@ -642,10 +640,8 @@ static int rockchip_pdm_probe(struct platform_device *pdev) + goto err_suspend; + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); +- if (ret) { +- dev_err(&pdev->dev, "could not register pcm: %d\n", ret); ++ if (ret) + goto err_suspend; +- } + + return 0; + +diff --git a/sound/soc/rockchip/rockchip_rt5645.c b/sound/soc/rockchip/rockchip_rt5645.c +--- a/sound/soc/rockchip/rockchip_rt5645.c ++++ b/sound/soc/rockchip/rockchip_rt5645.c +@@ -191,11 +191,8 @@ static int snd_rk_mc_probe(struct platform_device *pdev) + rk_dailink.platforms->of_node = rk_dailink.cpus->of_node; + + ret = snd_soc_of_parse_card_name(card, "rockchip,model"); +- if (ret) { +- dev_err(&pdev->dev, +- "Soc parse card name failed %d\n", ret); ++ if (ret) + goto put_cpu_of_node; +- } + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) { +diff --git a/sound/soc/rockchip/rockchip_sai.c b/sound/soc/rockchip/rockchip_sai.c +--- a/sound/soc/rockchip/rockchip_sai.c ++++ b/sound/soc/rockchip/rockchip_sai.c +@@ -1428,12 +1428,13 @@ static int rockchip_sai_probe(struct platform_device *pdev) + "Failed to initialize regmap\n"); + + irq = platform_get_irq_optional(pdev, 0); ++ if (irq == -EPROBE_DEFER) ++ return irq; + if (irq > 0) { + ret = devm_request_irq(&pdev->dev, irq, rockchip_sai_isr, + IRQF_SHARED, node->name, sai); + if (ret) +- return dev_err_probe(&pdev->dev, ret, +- "Failed to request irq %d\n", irq); ++ return ret; + } else { + dev_dbg(&pdev->dev, "Asked for an IRQ but got %d\n", irq); + } +@@ -1456,7 +1457,7 @@ static int rockchip_sai_probe(struct platform_device *pdev) + + ret = rockchip_sai_parse_paths(sai, node); + if (ret) +- return dev_err_probe(&pdev->dev, ret, "Failed to parse paths\n"); ++ return ret; + + /* + * From here on, all register accesses need to be wrapped in +@@ -1471,18 +1472,14 @@ static int rockchip_sai_probe(struct platform_device *pdev) + return dev_err_probe(&pdev->dev, ret, "Failed to resume device\n"); + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); +- if (ret) { +- dev_err(&pdev->dev, "Failed to register PCM: %d\n", ret); ++ if (ret) + goto err_runtime_suspend; +- } + + ret = devm_snd_soc_register_component(&pdev->dev, + &rockchip_sai_component, + dai, 1); +- if (ret) { +- dev_err(&pdev->dev, "Failed to register component: %d\n", ret); ++ if (ret) + goto err_runtime_suspend; +- } + + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_put(&pdev->dev); +diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c +--- a/sound/soc/rockchip/rockchip_spdif.c ++++ b/sound/soc/rockchip/rockchip_spdif.c +@@ -396,13 +396,13 @@ static int rk_spdif_probe(struct platform_device *pdev) + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) +- return dev_err_probe(&pdev->dev, ret, "Could not register PCM\n"); ++ return ret; + + ret = devm_snd_soc_register_component(&pdev->dev, + &rk_spdif_component, + &rk_spdif_dai, 1); + if (ret) +- return dev_err_probe(&pdev->dev, ret, "Could not register DAI\n"); ++ return ret; + + return 0; + } +diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c +--- a/sound/soc/samsung/i2s.c ++++ b/sound/soc/samsung/i2s.c +@@ -1116,15 +1116,31 @@ static const struct snd_soc_dapm_widget samsung_i2s_widgets[] = { + + static const struct snd_soc_dapm_route samsung_i2s_dapm_routes[] = { + { "Playback Mixer", NULL, "Primary Playback" }, +- { "Playback Mixer", NULL, "Secondary Playback" }, +- + { "Mixer DAI TX", NULL, "Playback Mixer" }, + { "Primary Capture", NULL, "Mixer DAI RX" }, + }; + ++static const struct snd_soc_dapm_route samsung_i2s_dapm_routes_sec_play[] = { ++ { "Playback Mixer", NULL, "Secondary Playback" }, ++}; ++ ++static int samsung_i2s_component_probe(struct snd_soc_component *component) ++{ ++ struct samsung_i2s_priv *priv = snd_soc_component_get_drvdata(component); ++ ++ if (priv->quirks & QUIRK_SEC_DAI) ++ snd_soc_dapm_add_routes(snd_soc_component_to_dapm(component), ++ samsung_i2s_dapm_routes_sec_play, ++ ARRAY_SIZE(samsung_i2s_dapm_routes_sec_play)); ++ ++ return 0; ++} ++ + static const struct snd_soc_component_driver samsung_i2s_component = { + .name = "samsung-i2s", + ++ .probe = samsung_i2s_component_probe, ++ + .dapm_widgets = samsung_i2s_widgets, + .num_dapm_widgets = ARRAY_SIZE(samsung_i2s_widgets), + +@@ -1441,10 +1457,10 @@ static int samsung_i2s_probe(struct platform_device *pdev) + regs_base = res->start; + + priv->clk = devm_clk_get(&pdev->dev, "iis"); +- if (IS_ERR(priv->clk)) { +- dev_err(&pdev->dev, "Failed to get iis clock\n"); +- return PTR_ERR(priv->clk); +- } ++ if (IS_ERR(priv->clk)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk), ++ "Failed to get iis clock\n"); ++ + + ret = clk_prepare_enable(priv->clk); + if (ret != 0) { +@@ -1650,8 +1666,7 @@ static const struct samsung_i2s_dai_data i2sv6_dai_type __maybe_unused = { + }; + + static const struct samsung_i2s_dai_data i2sv7_dai_type __maybe_unused = { +- .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | +- QUIRK_SUPPORTS_TDM, ++ .quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR | QUIRK_SUPPORTS_TDM, + .pcm_rates = SNDRV_PCM_RATE_8000_192000, + .i2s_variant_regs = &i2sv7_regs, + }; +@@ -1675,7 +1690,7 @@ static const struct platform_device_id samsung_i2s_driver_ids[] = { + .name = "samsung-i2s", + .driver_data = (kernel_ulong_t)&i2sv3_dai_type, + }, +- {}, ++ { } + }; + MODULE_DEVICE_TABLE(platform, samsung_i2s_driver_ids); + +diff --git a/sound/soc/samsung/smdk_spdif.c b/sound/soc/samsung/smdk_spdif.c +--- a/sound/soc/samsung/smdk_spdif.c ++++ b/sound/soc/samsung/smdk_spdif.c +@@ -130,12 +130,8 @@ static int smdk_hw_params(struct snd_pcm_substream *substream, + return ret; + + /* Set S/PDIF uses internal source clock */ +- ret = snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK, +- rclk_rate, SND_SOC_CLOCK_IN); +- if (ret < 0) +- return ret; +- +- return ret; ++ return snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK, ++ rclk_rate, SND_SOC_CLOCK_IN); + } + + static const struct snd_soc_ops smdk_spdif_ops = { +diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c +--- a/sound/soc/samsung/spdif.c ++++ b/sound/soc/samsung/spdif.c +@@ -380,8 +380,8 @@ static int spdif_probe(struct platform_device *pdev) + + spdif->pclk = devm_clk_get(&pdev->dev, "spdif"); + if (IS_ERR(spdif->pclk)) { +- dev_err(&pdev->dev, "failed to get peri-clock\n"); +- ret = -ENOENT; ++ ret = dev_err_probe(&pdev->dev, PTR_ERR(spdif->pclk), ++ "failed to get peri-clock\n"); + goto err0; + } + ret = clk_prepare_enable(spdif->pclk); +@@ -390,8 +390,8 @@ static int spdif_probe(struct platform_device *pdev) + + spdif->sclk = devm_clk_get(&pdev->dev, "sclk_spdif"); + if (IS_ERR(spdif->sclk)) { +- dev_err(&pdev->dev, "failed to get internal source clock\n"); +- ret = -ENOENT; ++ ret = dev_err_probe(&pdev->dev, PTR_ERR(spdif->sclk), ++ "failed to get internal source clock\n"); + goto err1; + } + ret = clk_prepare_enable(spdif->sclk); +diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c +--- a/sound/soc/sdca/sdca_asoc.c ++++ b/sound/soc/sdca/sdca_asoc.c +@@ -364,7 +364,6 @@ static int entity_parse_ot(struct device *dev, + + /** + * sdca_asoc_pde_poll_actual_ps - Verify PDE power state reached target state +- * @dev: Pointer to the device for error logging. + * @regmap: Register map for reading ACTUAL_PS register. + * @function_id: SDCA function identifier. + * @entity_id: SDCA entity identifier for the power domain. +@@ -389,11 +388,11 @@ static int entity_parse_ot(struct device *dev, + * polling times out before reaching the target state, or a negative error code if + * a register read fails. + */ +-int sdca_asoc_pde_poll_actual_ps(struct device *dev, struct regmap *regmap, ++int sdca_asoc_pde_poll_actual_ps(struct regmap *regmap, + int function_id, int entity_id, +- int from_ps, int to_ps, +- const struct sdca_pde_delay *pde_delays, +- int num_delays) ++ int from_ps, int to_ps, ++ const struct sdca_pde_delay *pde_delays, ++ int num_delays) + { + static const int polls = 100; + static const int default_poll_us = 1000; +@@ -451,14 +450,14 @@ static int entity_pde_event(struct snd_soc_dapm_widget *widget, + return 0; + } + +- ret = sdca_asoc_pde_poll_actual_ps(component->dev, component->regmap, ++ ret = sdca_asoc_pde_poll_actual_ps(component->regmap, + SDW_SDCA_CTL_FUNC(widget->reg), + SDW_SDCA_CTL_ENT(widget->reg), + from, to, + entity->pde.max_delay, + entity->pde.num_max_delay); + if (ret) +- dev_err(component->dev, "%s: PDE transition %x -> %x failed, err=%d\n", ++ dev_err(component->dev, "%s: pde transition %x -> %x failed: %d\n", + entity->label, from, to, ret); + + return ret; +@@ -1231,7 +1230,7 @@ static u64 width_find_mask(unsigned int bits) + } + } + +-static int populate_rate_format(struct device *dev, ++int sdca_asoc_populate_rate_format(struct device *dev, + struct sdca_function_data *function, + struct sdca_entity *entity, + struct snd_soc_pcm_stream *stream) +@@ -1292,6 +1291,7 @@ static int populate_rate_format(struct device *dev, + + return 0; + } ++EXPORT_SYMBOL_NS(sdca_asoc_populate_rate_format, "SND_SOC_SDCA"); + + /** + * sdca_asoc_populate_dais - fill in an array of DAI drivers for a Function +@@ -1344,7 +1344,7 @@ int sdca_asoc_populate_dais(struct device *dev, struct sdca_function_data *funct + stream->channels_min = 1; + stream->channels_max = SDCA_MAX_CHANNEL_COUNT; + +- ret = populate_rate_format(dev, function, entity, stream); ++ ret = sdca_asoc_populate_rate_format(dev, function, entity, stream); + if (ret) + return ret; + +diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c +--- a/sound/soc/sdca/sdca_class_function.c ++++ b/sound/soc/sdca/sdca_class_function.c +@@ -329,7 +329,7 @@ static int class_function_probe(struct auxiliary_device *auxdev, + drv->core = core; + drv->function = &sdev->function; + +- ret = sdca_parse_function(dev, core->sdw, drv->function); ++ ret = sdca_parse_function(dev, drv->function); + if (ret) + return ret; + +diff --git a/sound/soc/sdca/sdca_fdl.c b/sound/soc/sdca/sdca_fdl.c +--- a/sound/soc/sdca/sdca_fdl.c ++++ b/sound/soc/sdca/sdca_fdl.c +@@ -195,7 +195,6 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, + { + struct device *dev = interrupt->dev; + struct sdca_fdl_data *fdl_data = &interrupt->function->fdl_data; +- const struct firmware *firmware = NULL; + struct acpi_sw_file *swf = NULL, *tmp; + struct sdca_fdl_file *fdl_file; + char *disk_filename; +@@ -230,6 +229,7 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, + + dev_dbg(dev, "FDL disk filename: %s\n", disk_filename); + ++ const struct firmware *firmware __free(firmware) = NULL; + ret = firmware_request_nowarn(&firmware, disk_filename, dev); + kfree(disk_filename); + if (ret) { +@@ -258,8 +258,7 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, + + if (!swf) { + dev_err(dev, "failed to locate SWF\n"); +- ret = -ENOENT; +- goto error; ++ return -ENOENT; + } + + dev_info(dev, "loading SWF: %x-%x-%x\n", +@@ -271,9 +270,6 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, + SDCA_CTL_XU_FDL_MESSAGEOFFSET, fdl_file->fdl_offset, + SDCA_CTL_XU_FDL_MESSAGELENGTH, swf->data, + swf->file_length - offsetof(struct acpi_sw_file, data)); +- +-error: +- release_firmware(firmware); + return ret; + } + +diff --git a/sound/soc/sdca/sdca_function_device.c b/sound/soc/sdca/sdca_function_device.c +--- a/sound/soc/sdca/sdca_function_device.c ++++ b/sound/soc/sdca/sdca_function_device.c +@@ -32,7 +32,8 @@ static void sdca_dev_release(struct device *dev) + + /* alloc, init and add link devices */ + static struct sdca_dev *sdca_dev_register(struct device *parent, +- struct sdca_function_desc *function_desc) ++ struct sdca_function_desc *function_desc, ++ struct acpi_table_swft *swft) + { + struct sdca_dev *sdev; + struct auxiliary_device *auxdev; +@@ -50,6 +51,7 @@ static struct sdca_dev *sdca_dev_register(struct device *parent, + auxdev->dev.release = sdca_dev_release; + + sdev->function.desc = function_desc; ++ sdev->function.fdl_data.swft = swft; + + rc = ida_alloc(&sdca_function_ida, GFP_KERNEL); + if (rc < 0) { +@@ -99,7 +101,8 @@ int sdca_dev_register_functions(struct sdw_slave *slave) + struct sdca_dev *func_dev; + + func_dev = sdca_dev_register(&slave->dev, +- &sdca_data->function[i]); ++ &sdca_data->function[i], ++ sdca_data->swft); + if (IS_ERR(func_dev)) { + ret = PTR_ERR(func_dev); + /* +diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c +--- a/sound/soc/sdca/sdca_functions.c ++++ b/sound/soc/sdca/sdca_functions.c +@@ -7,6 +7,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -18,7 +19,6 @@ + #include + #include + #include +-#include + + /* + * Should be long enough to encompass all the MIPI DisCo properties. +@@ -1365,90 +1365,76 @@ static int find_sdca_entity_ge(struct device *dev, + return -EINVAL; + } + +-static int +-find_sdca_entity_hide(struct device *dev, struct sdw_slave *sdw, +- struct fwnode_handle *function_node, +- struct fwnode_handle *entity_node, struct sdca_entity *entity) ++static int find_sdca_entity_hide(struct device *dev, ++ struct fwnode_handle *entity_node, ++ struct sdca_entity *entity) + { + struct sdca_entity_hide *hide = &entity->hide; +- unsigned int delay, *af_list = hide->af_number_list; +- int nval, ret; +- unsigned char *report_desc = NULL; ++ int num_reports, ret; ++ unsigned int delay; + + ret = fwnode_property_read_u32(entity_node, +- "mipi-sdca-RxUMP-ownership-transition-max-delay", &delay); ++ "mipi-sdca-RxUMP-ownership-transition-max-delay", ++ &delay); + if (!ret) + hide->max_delay = delay; + +- nval = fwnode_property_count_u32(entity_node, "mipi-sdca-HIDTx-supported-report-ids"); +- if (nval > 0) { +- hide->num_hidtx_ids = nval; ++ num_reports = fwnode_property_count_u32(entity_node, ++ "mipi-sdca-HIDTx-supported-report-ids"); ++ if (num_reports < 0 && num_reports != -EINVAL) { ++ dev_err(dev, "%pfwP: failed to read hid tx ids: %d\n", ++ entity_node, num_reports); ++ return num_reports; ++ } else if (num_reports > 0) { ++ hide->num_hidtx_ids = num_reports; + hide->hidtx_ids = devm_kcalloc(dev, hide->num_hidtx_ids, + sizeof(*hide->hidtx_ids), GFP_KERNEL); + if (!hide->hidtx_ids) + return -ENOMEM; + +- ret = fwnode_property_read_u32_array(entity_node, +- "mipi-sdca-HIDTx-supported-report-ids", +- hide->hidtx_ids, +- hide->num_hidtx_ids); +- if (ret < 0) +- return ret; ++ fwnode_property_read_u32_array(entity_node, ++ "mipi-sdca-HIDTx-supported-report-ids", ++ hide->hidtx_ids, hide->num_hidtx_ids); + } + +- nval = fwnode_property_count_u32(entity_node, "mipi-sdca-HIDRx-supported-report-ids"); +- if (nval > 0) { +- hide->num_hidrx_ids = nval; ++ num_reports = fwnode_property_count_u32(entity_node, ++ "mipi-sdca-HIDRx-supported-report-ids"); ++ if (num_reports < 0 && num_reports != -EINVAL) { ++ dev_err(dev, "%pfwP: failed to read hid rx ids: %d\n", ++ entity_node, num_reports); ++ return num_reports; ++ } else if (num_reports > 0) { ++ hide->num_hidrx_ids = num_reports; + hide->hidrx_ids = devm_kcalloc(dev, hide->num_hidrx_ids, + sizeof(*hide->hidrx_ids), GFP_KERNEL); + if (!hide->hidrx_ids) + return -ENOMEM; + +- ret = fwnode_property_read_u32_array(entity_node, +- "mipi-sdca-HIDRx-supported-report-ids", +- hide->hidrx_ids, +- hide->num_hidrx_ids); +- if (ret < 0) +- return ret; ++ fwnode_property_read_u32_array(entity_node, ++ "mipi-sdca-HIDRx-supported-report-ids", ++ hide->hidrx_ids, hide->num_hidrx_ids); + } + +- nval = fwnode_property_count_u32(entity_node, "mipi-sdca-hide-related-audio-function-list"); +- if (nval <= 0) { ++ /* ++ * FIXME: This should probably link to the actual sdca_function_data pointer, ++ * but updating to do so should probably wait until we have a user. ++ */ ++ num_reports = fwnode_property_count_u32(entity_node, ++ "mipi-sdca-hide-related-audio-function-list"); ++ if (num_reports <= 0) { + dev_err(dev, "%pfwP: audio function numbers list missing: %d\n", +- entity_node, nval); ++ entity_node, num_reports); + return -EINVAL; +- } else if (nval > SDCA_MAX_FUNCTION_COUNT) { +- dev_err(dev, "%pfwP: maximum number of audio function exceeded\n", entity_node); ++ } else if (num_reports > ARRAY_SIZE(hide->af_number_list)) { ++ dev_err(dev, "%pfwP: maximum number of audio function exceeded\n", ++ entity_node); + return -EINVAL; + } + +- hide->hide_reside_function_num = nval; ++ hide->hide_reside_function_num = num_reports; + fwnode_property_read_u32_array(entity_node, +- "mipi-sdca-hide-related-audio-function-list", af_list, nval); +- +- nval = fwnode_property_count_u8(function_node, "mipi-sdca-hid-descriptor"); +- if (nval) +- fwnode_property_read_u8_array(function_node, "mipi-sdca-hid-descriptor", +- (u8 *)&hide->hid_desc, nval); +- +- if (hide->hid_desc.bNumDescriptors) { +- nval = fwnode_property_count_u8(function_node, "mipi-sdca-report-descriptor"); +- if (nval) { +- report_desc = devm_kzalloc(dev, nval, GFP_KERNEL); +- if (!report_desc) +- return -ENOMEM; +- hide->hid_report_desc = report_desc; +- fwnode_property_read_u8_array(function_node, "mipi-sdca-report-descriptor", +- report_desc, nval); +- +- /* add HID device */ +- ret = sdca_add_hid_device(dev, sdw, entity); +- if (ret) { +- dev_err(dev, "%pfwP: failed to add HID device: %d\n", entity_node, ret); +- return ret; +- } +- } +- } ++ "mipi-sdca-hide-related-audio-function-list", ++ hide->af_number_list, num_reports); + + return 0; + } +@@ -1475,8 +1461,7 @@ static int find_sdca_entity_xu(struct device *dev, + return 0; + } + +-static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw, +- struct sdca_function_data *function, ++static int find_sdca_entity(struct device *dev, struct sdca_function_data *function, + struct fwnode_handle *function_node, + struct fwnode_handle *entity_node, + struct sdca_entity *entity) +@@ -1528,8 +1513,7 @@ static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw, + ret = find_sdca_entity_ge(dev, entity_node, entity); + break; + case SDCA_ENTITY_TYPE_HIDE: +- ret = find_sdca_entity_hide(dev, sdw, function_node, +- entity_node, entity); ++ ret = find_sdca_entity_hide(dev, entity_node, entity); + break; + default: + break; +@@ -1544,8 +1528,7 @@ static int find_sdca_entity(struct device *dev, struct sdw_slave *sdw, + return 0; + } + +-static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw, +- struct fwnode_handle *function_node, ++static int find_sdca_entities(struct device *dev, struct fwnode_handle *function_node, + struct sdca_function_data *function) + { + struct sdca_entity *entities; +@@ -1596,7 +1579,7 @@ static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw, + return -EINVAL; + } + +- ret = find_sdca_entity(dev, sdw, function, function_node, ++ ret = find_sdca_entity(dev, function, function_node, + entity_node, &entities[i]); + fwnode_handle_put(entity_node); + if (ret) +@@ -1619,7 +1602,7 @@ static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw, + return 0; + } + +-static struct sdca_entity *find_sdca_entity_by_label(struct sdca_function_data *function, ++struct sdca_entity *sdca_find_entity_by_label(struct sdca_function_data *function, + const char *entity_label) + { + struct sdca_entity *entity = NULL; +@@ -1648,6 +1631,7 @@ static struct sdca_entity *find_sdca_entity_by_label(struct sdca_function_data * + + return NULL; + } ++EXPORT_SYMBOL_NS(sdca_find_entity_by_label, "SND_SOC_SDCA"); + + static struct sdca_entity *find_sdca_entity_by_id(struct sdca_function_data *function, + const int id) +@@ -1688,7 +1672,7 @@ static int find_sdca_entity_connection_iot(struct device *dev, + return ret; + } + +- clock_entity = find_sdca_entity_by_label(function, clock_label); ++ clock_entity = sdca_find_entity_by_label(function, clock_label); + if (!clock_entity) { + dev_err(dev, "%s: failed to find clock with label %s\n", + entity->label, clock_label); +@@ -1873,7 +1857,7 @@ static int find_sdca_entity_connection(struct device *dev, + return ret; + } + +- connected_entity = find_sdca_entity_by_label(function, connected_label); ++ connected_entity = sdca_find_entity_by_label(function, connected_label); + if (!connected_entity) { + dev_err(dev, "%s: failed to find entity with label %s\n", + entity->label, connected_label); +@@ -2088,8 +2072,7 @@ static int find_sdca_clusters(struct device *dev, + return 0; + } + +-static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, +- struct fwnode_handle *function_node, ++static int find_sdca_filesets(struct device *dev, struct fwnode_handle *function_node, + struct sdca_function_data *function) + { + static const int mult_fileset = 3; +@@ -2171,23 +2154,65 @@ static int find_sdca_filesets(struct device *dev, struct sdw_slave *sdw, + set->files = files; + } + +- function->fdl_data.swft = sdw->sdca_data.swft; + function->fdl_data.num_sets = num_sets; + function->fdl_data.sets = sets; + + return 0; + } + ++static int find_sdca_hid(struct device *dev, struct fwnode_handle *function_node, ++ struct sdca_function_data *function) ++{ ++ int num_desc; ++ ++ num_desc = fwnode_property_count_u8(function_node, "mipi-sdca-hid-descriptor"); ++ if (!num_desc) { ++ return 0; ++ } else if (num_desc < 0) { ++ dev_err(dev, "%pfwP: failed to read hid descriptor: %d\n", ++ function_node, num_desc); ++ return num_desc; ++ } else if (num_desc > sizeof(function->hid.desc)) { ++ dev_err(dev, "%pfwP: hid descriptor too large: %d\n", ++ function_node, num_desc); ++ return -EINVAL; ++ } ++ ++ fwnode_property_read_u8_array(function_node, "mipi-sdca-hid-descriptor", ++ (u8 *)&function->hid.desc, num_desc); ++ ++ if (!function->hid.desc.bNumDescriptors) ++ return 0; ++ ++ num_desc = fwnode_property_count_u8(function_node, "mipi-sdca-report-descriptor"); ++ if (num_desc <= 0) { ++ dev_err(dev, "%pfwP: failed to read report descriptor: %d\n", ++ function_node, num_desc); ++ ++ if (!num_desc) ++ return -EINVAL; ++ ++ return num_desc; ++ } ++ ++ function->hid.report_desc = devm_kzalloc(dev, num_desc, GFP_KERNEL); ++ if (!function->hid.report_desc) ++ return -ENOMEM; ++ ++ fwnode_property_read_u8_array(function_node, "mipi-sdca-report-descriptor", ++ function->hid.report_desc, num_desc); ++ ++ return 0; ++} ++ + /** + * sdca_parse_function - parse ACPI DisCo for a Function + * @dev: Pointer to device against which function data will be allocated. +- * @sdw: SoundWire slave device to be processed. + * @function: Pointer to the Function information, to be populated. + * + * Return: Returns 0 for success. + */ +-int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, +- struct sdca_function_data *function) ++int sdca_parse_function(struct device *dev, struct sdca_function_data *function) + { + struct fwnode_handle *node = function->desc->node; + u32 tmp; +@@ -2213,7 +2238,7 @@ int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, + if (ret) + return ret; + +- ret = find_sdca_entities(dev, sdw, node, function); ++ ret = find_sdca_entities(dev, node, function); + if (ret) + return ret; + +@@ -2225,10 +2250,20 @@ int sdca_parse_function(struct device *dev, struct sdw_slave *sdw, + if (ret < 0) + return ret; + +- ret = find_sdca_filesets(dev, sdw, node, function); ++ ret = find_sdca_filesets(dev, node, function); + if (ret) + return ret; + ++ switch (function->desc->type) { ++ case SDCA_FUNCTION_TYPE_HID: ++ ret = find_sdca_hid(dev, node, function); ++ if (ret) ++ return ret; ++ break; ++ default: ++ break; ++ } ++ + return 0; + } + EXPORT_SYMBOL_NS(sdca_parse_function, "SND_SOC_SDCA"); +diff --git a/sound/soc/sdca/sdca_hid.c b/sound/soc/sdca/sdca_hid.c +--- a/sound/soc/sdca/sdca_hid.c ++++ b/sound/soc/sdca/sdca_hid.c +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -23,18 +24,18 @@ + + static int sdwhid_parse(struct hid_device *hid) + { +- struct sdca_entity *entity = hid->driver_data; ++ struct sdca_function_data *function = hid->driver_data; + unsigned int rsize; + int ret; + +- rsize = le16_to_cpu(entity->hide.hid_desc.rpt_desc.wDescriptorLength); ++ rsize = le16_to_cpu(function->hid.desc.rpt_desc.wDescriptorLength); + + if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { + dev_err(&hid->dev, "invalid size of report descriptor (%u)\n", rsize); + return -EINVAL; + } + +- ret = hid_parse_report(hid, entity->hide.hid_report_desc, rsize); ++ ret = hid_parse_report(hid, function->hid.report_desc, rsize); + + if (!ret) + return 0; +@@ -85,10 +86,16 @@ static const struct hid_ll_driver sdw_hid_driver = { + .raw_request = sdwhid_raw_request, + }; + +-int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, +- struct sdca_entity *entity) ++/** ++ * sdca_add_hid_device - create a new SDCA HID device ++ * @interrupt: Pointer to the SDCA interrupt information structure. ++ * ++ * Return: Zero on success, and a negative error code on failure. ++ */ ++int sdca_add_hid_device(struct sdca_interrupt *interrupt) + { +- struct sdw_bus *bus = sdw->bus; ++ struct device *dev = interrupt->dev; ++ struct sdca_function_data *function = interrupt->function; + struct hid_device *hid; + int ret; + +@@ -100,16 +107,13 @@ int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, + + hid->dev.parent = dev; + hid->bus = BUS_SDW; +- hid->version = le16_to_cpu(entity->hide.hid_desc.bcdHID); ++ hid->version = le16_to_cpu(function->hid.desc.bcdHID); + +- snprintf(hid->name, sizeof(hid->name), +- "HID sdw:%01x:%01x:%04x:%04x:%02x", +- bus->controller_id, bus->link_id, sdw->id.mfg_id, +- sdw->id.part_id, sdw->id.class_id); ++ strscpy(hid->phys, dev_name(dev)); ++ snprintf(hid->name, sizeof(hid->name), "SDCA %s:%02x", ++ function->desc->name, function->desc->adr); + +- snprintf(hid->phys, sizeof(hid->phys), "%s", dev->bus->name); +- +- hid->driver_data = entity; ++ hid->driver_data = function; + + ret = hid_add_device(hid); + if (ret && ret != -ENODEV) { +@@ -118,12 +122,24 @@ int sdca_add_hid_device(struct device *dev, struct sdw_slave *sdw, + return ret; + } + +- entity->hide.hid = hid; ++ interrupt->priv = hid; + + return 0; + } + EXPORT_SYMBOL_NS(sdca_add_hid_device, "SND_SOC_SDCA"); + ++/** ++ * sdca_destroy_hid_device - destroy the HID device ++ * @interrupt: Pointer to the SDCA interrupt information structure. ++ */ ++void sdca_destroy_hid_device(struct sdca_interrupt *interrupt) ++{ ++ struct hid_device *hid = interrupt->priv; ++ ++ hid_destroy_device(hid); ++} ++EXPORT_SYMBOL_NS(sdca_destroy_hid_device, "SND_SOC_SDCA"); ++ + /** + * sdca_hid_process_report - read a HID event from the device and report + * @interrupt: Pointer to the SDCA interrupt information structure. +@@ -133,7 +149,7 @@ EXPORT_SYMBOL_NS(sdca_add_hid_device, "SND_SOC_SDCA"); + int sdca_hid_process_report(struct sdca_interrupt *interrupt) + { + struct device *dev = interrupt->dev; +- struct hid_device *hid = interrupt->entity->hide.hid; ++ struct hid_device *hid = interrupt->priv; + void *val __free(kfree) = NULL; + int len, ret; + +diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c +--- a/sound/soc/sdca/sdca_interrupts.c ++++ b/sound/soc/sdca/sdca_interrupts.c +@@ -487,6 +487,12 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *regmap, + } + break; + case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER): ++ interrupt->free_priv = sdca_destroy_hid_device; ++ ++ ret = sdca_add_hid_device(interrupt); ++ if (ret) ++ return ret; ++ + interrupt->handler = hid_handler; + break; + default: +diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c +--- a/sound/soc/sdw_utils/soc_sdw_utils.c ++++ b/sound/soc/sdw_utils/soc_sdw_utils.c +@@ -834,6 +834,35 @@ struct asoc_sdw_codec_info codec_info_list[] = { + }, + .dai_num = 2, + }, ++ { ++ .vendor_id = 0x01fa, ++ .part_id = 0x3562, ++ .name_prefix = "AMP", ++ .is_amp = true, ++ .dais = { ++ { ++ .direction = {true, false}, ++ .dai_name = "cs35l56-sdw1", ++ .component_name = "cs35l56", ++ .dai_type = SOC_SDW_DAI_TYPE_AMP, ++ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, ++ .init = asoc_sdw_cs_amp_init, ++ .rtd_init = asoc_sdw_cs_spk_rtd_init, ++ .controls = generic_spk_controls, ++ .num_controls = ARRAY_SIZE(generic_spk_controls), ++ .widgets = generic_spk_widgets, ++ .num_widgets = ARRAY_SIZE(generic_spk_widgets), ++ }, ++ { ++ .direction = {false, true}, ++ .dai_name = "cs35l56-sdw1c", ++ .dai_type = SOC_SDW_DAI_TYPE_AMP, ++ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, ++ .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, ++ }, ++ }, ++ .dai_num = 2, ++ }, + { + .vendor_id = 0x01fa, + .part_id = 0x3563, +@@ -1789,9 +1818,7 @@ static int is_sdca_aux_dev_present(struct device *dev, + int adr_index) + { + struct sdw_slave *slave; +- struct device *sdw_dev; + const char *sdw_codec_name; +- int ret = 0; + int i; + + if (!aux_codec_name) +@@ -1801,7 +1828,8 @@ static int is_sdca_aux_dev_present(struct device *dev, + if (!sdw_codec_name) + return -ENOMEM; + +- sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); ++ struct device *sdw_dev __free(put_device) = ++ bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); + if (!sdw_dev) { + dev_err(dev, "codec %s not found\n", sdw_codec_name); + return -EINVAL; +@@ -1811,24 +1839,19 @@ static int is_sdca_aux_dev_present(struct device *dev, + + if (!slave->sdca_data.interface_revision) { + dev_warn(dev, "No SDCA properties, assuming aux '%s' present\n", aux_codec_name); +- ret = 1; +- goto put_dev; ++ return 1; + } + + for (i = 0; i < slave->sdca_data.num_functions; i++) { + const char *fname = slave->sdca_data.function[i].name; + +- if (fname && strstr(aux_codec_name, fname)) { +- ret = 1; +- goto put_dev; +- } ++ if (fname && strstr(aux_codec_name, fname)) ++ return 1; + } + + dev_dbg(dev, "SDCA function for aux '%s' NOT FOUND on slave, skipping\n", aux_codec_name); + +-put_dev: +- put_device(sdw_dev); +- return ret; ++ return 0; + } + + int asoc_sdw_count_sdw_endpoints(struct snd_soc_card *card, +@@ -1928,9 +1951,8 @@ static int is_sdca_endpoint_present(struct device *dev, + const struct snd_soc_acpi_endpoint *adr_end; + const struct asoc_sdw_dai_info *dai_info; + struct sdw_slave *slave; +- struct device *sdw_dev; + const char *sdw_codec_name; +- int ret, i; ++ int i; + + adr_end = &adr_dev->endpoints[end_index]; + dai_info = &codec_info->dais[adr_end->num]; +@@ -1939,7 +1961,8 @@ static int is_sdca_endpoint_present(struct device *dev, + if (!sdw_codec_name) + return -ENOMEM; + +- sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); ++ struct device *sdw_dev __free(put_device) = ++ bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); + if (!sdw_dev) { + dev_err(dev, "codec %s not found\n", sdw_codec_name); + return -EINVAL; +@@ -1950,8 +1973,7 @@ static int is_sdca_endpoint_present(struct device *dev, + /* Make sure BIOS provides SDCA properties */ + if (!slave->sdca_data.interface_revision) { + dev_warn(&slave->dev, "SDCA properties not found in the BIOS\n"); +- ret = 1; +- goto put_device; ++ return 1; + } + + for (i = 0; i < slave->sdca_data.num_functions; i++) { +@@ -1960,8 +1982,7 @@ static int is_sdca_endpoint_present(struct device *dev, + if (dai_type == dai_info->dai_type) { + dev_dbg(&slave->dev, "DAI type %d sdca function %s found\n", + dai_type, slave->sdca_data.function[i].name); +- ret = 1; +- goto put_device; ++ return 1; + } + } + +@@ -1969,11 +1990,7 @@ static int is_sdca_endpoint_present(struct device *dev, + "SDCA device function for DAI type %d not supported, skip endpoint\n", + dai_info->dai_type); + +- ret = 0; +- +-put_device: +- put_device(sdw_dev); +- return ret; ++ return 0; + } + + int asoc_sdw_parse_sdw_endpoints(struct device *dev, +diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c +--- a/sound/soc/soc-component.c ++++ b/sound/soc/soc-component.c +@@ -29,17 +29,42 @@ static inline int _soc_component_ret_reg_rw(struct snd_soc_component *component, + func, component->name, reg); + } + +-static inline int soc_component_field_shift(struct snd_soc_component *component, +- unsigned int mask) ++struct snd_soc_component *snd_soc_component_alloc(struct device *dev) + { +- if (!mask) { +- dev_err(component->dev, "ASoC: error field mask is zero for %s\n", +- component->name); +- return 0; +- } ++ struct snd_soc_component *component = devm_kzalloc(dev, sizeof(*component), GFP_KERNEL); + +- return (ffs(mask) - 1); ++ if (!component) ++ return NULL; ++ ++ component->dev = dev; ++ ++ return component; + } ++EXPORT_SYMBOL_GPL(snd_soc_component_alloc); ++ ++void snd_soc_component_set_name(struct snd_soc_component *component, const char *name) ++{ ++ component->name = name; ++} ++EXPORT_SYMBOL_GPL(snd_soc_component_set_name); ++ ++const char *snd_soc_component_name(struct snd_soc_component *component) ++{ ++ return component->name; ++} ++EXPORT_SYMBOL_GPL(snd_soc_component_name); ++ ++void snd_soc_component_set_priv(struct snd_soc_component *component, void *priv) ++{ ++ component->priv = priv; ++} ++EXPORT_SYMBOL_GPL(snd_soc_component_set_priv); ++ ++void *snd_soc_component_to_priv(struct snd_soc_component *component) ++{ ++ return component->priv; ++} ++EXPORT_SYMBOL_GPL(snd_soc_component_to_priv); + + /* + * We might want to check substream by using list. +@@ -820,6 +845,18 @@ int snd_soc_component_update_bits_async(struct snd_soc_component *component, + } + EXPORT_SYMBOL_GPL(snd_soc_component_update_bits_async); + ++static inline int soc_component_field_shift(struct snd_soc_component *component, ++ unsigned int mask) ++{ ++ if (!mask) { ++ dev_err(component->dev, "ASoC: error field mask is zero for %s\n", ++ component->name); ++ return 0; ++ } ++ ++ return (ffs(mask) - 1); ++} ++ + /** + * snd_soc_component_read_field() - Read register field value + * @component: Component to read from +diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c +--- a/sound/soc/soc-core.c ++++ b/sound/soc/soc-core.c +@@ -2707,10 +2707,11 @@ static void snd_soc_del_component_unlocked(struct snd_soc_component *component) + list_del(&component->list); + } + +-int snd_soc_component_initialize(struct snd_soc_component *component, +- const struct snd_soc_component_driver *driver, +- struct device *dev) ++static int soc_component_initialize(struct snd_soc_component *component, ++ const struct snd_soc_component_driver *driver) + { ++ struct device *dev = component->dev; ++ + component->dapm = snd_soc_dapm_alloc(dev); + if (!component->dapm) + return -ENOMEM; +@@ -2730,16 +2731,14 @@ int snd_soc_component_initialize(struct snd_soc_component *component, + } + } + +- component->dev = dev; + component->driver = driver; + + return 0; + } +-EXPORT_SYMBOL_GPL(snd_soc_component_initialize); + +-int snd_soc_add_component(struct snd_soc_component *component, +- struct snd_soc_dai_driver *dai_drv, +- int num_dai) ++static int soc_component_add(struct snd_soc_component *component, ++ struct snd_soc_dai_driver *dai_drv, ++ int num_dai) + { + struct snd_soc_card *card, *c; + int ret; +@@ -2778,27 +2777,36 @@ int snd_soc_add_component(struct snd_soc_component *component, + + return ret; + } +-EXPORT_SYMBOL_GPL(snd_soc_add_component); + +-int snd_soc_register_component(struct device *dev, ++int snd_soc_register_component_c(struct snd_soc_component *component, + const struct snd_soc_component_driver *component_driver, + struct snd_soc_dai_driver *dai_drv, + int num_dai) + { +- struct snd_soc_component *component; + int ret; + +- component = devm_kzalloc(dev, sizeof(*component), GFP_KERNEL); +- if (!component) +- return -ENOMEM; +- +- ret = snd_soc_component_initialize(component, component_driver, dev); ++ ret = soc_component_initialize(component, component_driver); + if (ret < 0) + return ret; + +- return snd_soc_add_component(component, dai_drv, num_dai); ++ return soc_component_add(component, dai_drv, num_dai); + } +-EXPORT_SYMBOL_GPL(snd_soc_register_component); ++EXPORT_SYMBOL_GPL(snd_soc_register_component_c); ++ ++int snd_soc_register_component_d(struct device *dev, ++ const struct snd_soc_component_driver *component_driver, ++ struct snd_soc_dai_driver *dai_drv, ++ int num_dai) ++{ ++ struct snd_soc_component *component; ++ ++ component = snd_soc_component_alloc(dev); ++ if (!component) ++ return -ENOMEM; ++ ++ return snd_soc_register_component_c(component, component_driver, dai_drv, num_dai); ++} ++EXPORT_SYMBOL_GPL(snd_soc_register_component_d); + + /** + * snd_soc_unregister_component_by_driver - Unregister component using a given driver +diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c +--- a/sound/soc/soc-dapm.c ++++ b/sound/soc/soc-dapm.c +@@ -1203,6 +1203,8 @@ static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w, + case snd_soc_dapm_pga: + case snd_soc_dapm_effect: + case snd_soc_dapm_out_drv: ++ case snd_soc_dapm_encoder: ++ case snd_soc_dapm_decoder: + wname_in_long_name = true; + kcname_in_long_name = true; + break; +@@ -2785,6 +2787,8 @@ static ssize_t dapm_widget_show_component(struct snd_soc_component *component, + case snd_soc_dapm_pga: + case snd_soc_dapm_effect: + case snd_soc_dapm_out_drv: ++ case snd_soc_dapm_encoder: ++ case snd_soc_dapm_decoder: + case snd_soc_dapm_mixer: + case snd_soc_dapm_mixer_named_ctl: + case snd_soc_dapm_supply: +@@ -3367,6 +3371,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) + case snd_soc_dapm_pga: + case snd_soc_dapm_effect: + case snd_soc_dapm_out_drv: ++ case snd_soc_dapm_encoder: ++ case snd_soc_dapm_decoder: + dapm_new_pga(w); + break; + case snd_soc_dapm_dai_link: +diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c +--- a/sound/soc/soc-generic-dmaengine-pcm.c ++++ b/sound/soc/soc-generic-dmaengine-pcm.c +@@ -3,6 +3,7 @@ + // Copyright (C) 2013, Analog Devices Inc. + // Author: Lars-Peter Clausen + ++#include + #include + #include + #include +@@ -77,7 +78,7 @@ static int dmaengine_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) + { +- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); ++ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); + struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); + struct dma_slave_config slave_config; + int ret; +@@ -99,7 +100,7 @@ dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component, + struct snd_pcm_substream *substream) + { + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); +- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); ++ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); + struct device *dma_dev = dmaengine_dma_dev(pcm, substream); + struct dma_chan *chan = pcm->chan[substream->stream]; + struct snd_dmaengine_dai_dma_data *dma_data; +@@ -148,7 +149,7 @@ dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component, + static int dmaengine_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) + { +- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); ++ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); + struct dma_chan *chan = pcm->chan[substream->stream]; + int ret; + +@@ -176,7 +177,7 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel( + struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream) + { +- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); ++ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); + struct snd_dmaengine_dai_dma_data *dma_data; + + if (rtd->dai_link->num_cpus > 1) { +@@ -219,7 +220,7 @@ static bool dmaengine_pcm_can_report_residue(struct device *dev, + static int dmaengine_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) + { +- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); ++ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); + const struct snd_dmaengine_pcm_config *config = pcm->config; + struct device *dev = component->dev; + size_t prealloc_buffer_size; +@@ -279,7 +280,7 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer( + struct snd_soc_component *component, + struct snd_pcm_substream *substream) + { +- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); ++ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); + + if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE) + return snd_dmaengine_pcm_pointer_no_residue(substream); +@@ -293,7 +294,7 @@ static int dmaengine_copy(struct snd_soc_component *component, + struct iov_iter *iter, unsigned long bytes) + { + struct snd_pcm_runtime *runtime = substream->runtime; +- struct dmaengine_pcm *pcm = soc_component_to_pcm(component); ++ struct dmaengine_pcm *pcm = snd_soc_component_to_priv(component); + int (*process)(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + unsigned long bytes) = pcm->config->process; +@@ -395,6 +396,27 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, + */ + if (PTR_ERR(chan) == -EPROBE_DEFER) + return -EPROBE_DEFER; ++ ++ bool has_fw_node = dev->of_node || is_acpi_device_node(dev->fwnode); ++ bool name_exists_in_fw = false; ++ ++ if (has_fw_node) ++ name_exists_in_fw = device_property_match_string(dev, ++ "dma-names", ++ name) >= 0; ++ ++ if (has_fw_node && name_exists_in_fw) ++ dev_warn(dev, "DTS/ACPI DMA channel '%s' request failed (%ld)\n", ++ name, PTR_ERR(chan)); ++ ++ if (has_fw_node && !name_exists_in_fw) ++ dev_warn(dev, "DTS/ACPI name '%s' not found, legacy failed (%ld)\n", ++ name, PTR_ERR(chan)); ++ ++ if (!has_fw_node) ++ dev_warn(dev, "Legacy DMA channel '%s' request failed (%ld)\n", ++ name, PTR_ERR(chan)); ++ + pcm->chan[i] = NULL; + } else { + pcm->chan[i] = chan; +@@ -406,6 +428,12 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, + if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) + pcm->chan[1] = pcm->chan[0]; + ++ if (!pcm->chan[0] && ++ !pcm->chan[1]) { ++ dev_err(dev, "no DMA channel found for either playback or capture\n"); ++ return -ENODEV; ++ } ++ + return 0; + } + +@@ -435,10 +463,15 @@ static const struct snd_dmaengine_pcm_config snd_dmaengine_pcm_default_config = + int snd_dmaengine_pcm_register(struct device *dev, + const struct snd_dmaengine_pcm_config *config, unsigned int flags) + { ++ struct snd_soc_component *component; + const struct snd_soc_component_driver *driver; + struct dmaengine_pcm *pcm; + int ret; + ++ component = snd_soc_component_alloc(dev); ++ if (!component) ++ return -ENOMEM; ++ + pcm = kzalloc_obj(*pcm); + if (!pcm) + return -ENOMEM; +@@ -449,7 +482,8 @@ int snd_dmaengine_pcm_register(struct device *dev, + pcm->flags = flags; + + if (config->name) +- pcm->component.name = config->name; ++ snd_soc_component_set_name(component, config->name); ++ snd_soc_component_set_priv(component, pcm); + + ret = dmaengine_pcm_request_chan_of(pcm, dev, config); + if (ret) +@@ -460,11 +494,7 @@ int snd_dmaengine_pcm_register(struct device *dev, + else + driver = &dmaengine_pcm_component; + +- ret = snd_soc_component_initialize(&pcm->component, driver, dev); +- if (ret) +- goto err_free_dma; +- +- ret = snd_soc_add_component(&pcm->component, NULL, 0); ++ ret = snd_soc_register_component(component, driver, NULL, 0); + if (ret) + goto err_free_dma; + +@@ -493,7 +523,7 @@ void snd_dmaengine_pcm_unregister(struct device *dev) + if (!component) + return; + +- pcm = soc_component_to_pcm(component); ++ pcm = snd_soc_component_to_priv(component); + + snd_soc_unregister_component_by_driver(dev, component->driver); + dmaengine_pcm_release_chan(pcm); +diff --git a/sound/soc/soc-topology-test.c b/sound/soc/soc-topology-test.c +--- a/sound/soc/soc-topology-test.c ++++ b/sound/soc/soc-topology-test.c +@@ -45,15 +45,13 @@ static void snd_soc_tplg_test_exit(struct kunit *test) + struct kunit_soc_component { + struct kunit *kunit; + int expect; /* what result we expect when loading topology */ +- struct snd_soc_component comp; + struct snd_soc_card card; + struct firmware fw; + }; + + static int d_probe(struct snd_soc_component *component) + { +- struct kunit_soc_component *kunit_comp = +- container_of(component, struct kunit_soc_component, comp); ++ struct kunit_soc_component *kunit_comp = snd_soc_component_to_priv(component); + int ret; + + ret = snd_soc_tplg_component_load(component, NULL, &kunit_comp->fw); +@@ -65,8 +63,7 @@ static int d_probe(struct snd_soc_component *component) + + static void d_remove(struct snd_soc_component *component) + { +- struct kunit_soc_component *kunit_comp = +- container_of(component, struct kunit_soc_component, comp); ++ struct kunit_soc_component *kunit_comp = snd_soc_component_to_priv(component); + int ret; + + ret = snd_soc_tplg_component_remove(component); +@@ -214,8 +211,7 @@ static struct tplg_tmpl_002 tplg_tmpl_with_pcm = { + */ + static int d_probe_null_comp(struct snd_soc_component *component) + { +- struct kunit_soc_component *kunit_comp = +- container_of(component, struct kunit_soc_component, comp); ++ struct kunit_soc_component *kunit_comp = snd_soc_component_to_priv(component); + int ret; + + /* instead of passing component pointer as first argument, pass NULL here */ +@@ -234,6 +230,7 @@ static const struct snd_soc_component_driver test_component_null_comp = { + static void snd_soc_tplg_test_load_with_null_comp(struct kunit *test) + { + struct kunit_soc_component *kunit_comp; ++ struct snd_soc_component *component; + int ret; + + /* prepare */ +@@ -249,15 +246,17 @@ static void snd_soc_tplg_test_load_with_null_comp(struct kunit *test) + kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); + kunit_comp->card.fully_routed = true; + ++ component = snd_soc_component_alloc(test_dev); ++ KUNIT_ASSERT_NOT_NULL(test, component); ++ ++ snd_soc_component_set_priv(component, kunit_comp); ++ + /* run test */ + ret = snd_soc_register_card(&kunit_comp->card); + if (ret != 0 && ret != -EPROBE_DEFER) + KUNIT_FAIL(test, "Failed to register card"); + +- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component_null_comp, test_dev); +- KUNIT_EXPECT_EQ(test, 0, ret); +- +- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); ++ ret = snd_soc_register_component(component, &test_component_null_comp, NULL, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* cleanup */ +@@ -276,6 +275,7 @@ static void snd_soc_tplg_test_load_with_null_comp(struct kunit *test) + static void snd_soc_tplg_test_load_with_null_ops(struct kunit *test) + { + struct kunit_soc_component *kunit_comp; ++ struct snd_soc_component *component; + int ret; + + /* prepare */ +@@ -291,15 +291,17 @@ static void snd_soc_tplg_test_load_with_null_ops(struct kunit *test) + kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); + kunit_comp->card.fully_routed = true; + ++ component = snd_soc_component_alloc(test_dev); ++ KUNIT_ASSERT_NOT_NULL(test, component); ++ ++ snd_soc_component_set_priv(component, kunit_comp); ++ + /* run test */ + ret = snd_soc_register_card(&kunit_comp->card); + if (ret != 0 && ret != -EPROBE_DEFER) + KUNIT_FAIL(test, "Failed to register card"); + +- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); +- KUNIT_EXPECT_EQ(test, 0, ret); +- +- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); ++ ret = snd_soc_register_component(component, &test_component, NULL, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* cleanup */ +@@ -318,8 +320,7 @@ static void snd_soc_tplg_test_load_with_null_ops(struct kunit *test) + */ + static int d_probe_null_fw(struct snd_soc_component *component) + { +- struct kunit_soc_component *kunit_comp = +- container_of(component, struct kunit_soc_component, comp); ++ struct kunit_soc_component *kunit_comp = snd_soc_component_to_priv(component); + int ret; + + /* instead of passing fw pointer as third argument, pass NULL here */ +@@ -338,6 +339,7 @@ static const struct snd_soc_component_driver test_component_null_fw = { + static void snd_soc_tplg_test_load_with_null_fw(struct kunit *test) + { + struct kunit_soc_component *kunit_comp; ++ struct snd_soc_component *component; + int ret; + + /* prepare */ +@@ -353,15 +355,17 @@ static void snd_soc_tplg_test_load_with_null_fw(struct kunit *test) + kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); + kunit_comp->card.fully_routed = true; + ++ component = snd_soc_component_alloc(test_dev); ++ KUNIT_ASSERT_NOT_NULL(test, component); ++ ++ snd_soc_component_set_priv(component, kunit_comp); ++ + /* run test */ + ret = snd_soc_register_card(&kunit_comp->card); + if (ret != 0 && ret != -EPROBE_DEFER) + KUNIT_FAIL(test, "Failed to register card"); + +- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component_null_fw, test_dev); +- KUNIT_EXPECT_EQ(test, 0, ret); +- +- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); ++ ret = snd_soc_register_component(component, &test_component_null_fw, NULL, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* cleanup */ +@@ -375,6 +379,7 @@ static void snd_soc_tplg_test_load_with_null_fw(struct kunit *test) + static void snd_soc_tplg_test_load_empty_tplg(struct kunit *test) + { + struct kunit_soc_component *kunit_comp; ++ struct snd_soc_component *component; + struct tplg_tmpl_001 *data; + int size; + int ret; +@@ -401,15 +406,17 @@ static void snd_soc_tplg_test_load_empty_tplg(struct kunit *test) + kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); + kunit_comp->card.fully_routed = true; + ++ component = snd_soc_component_alloc(test_dev); ++ KUNIT_ASSERT_NOT_NULL(test, component); ++ ++ snd_soc_component_set_priv(component, kunit_comp); ++ + /* run test */ + ret = snd_soc_register_card(&kunit_comp->card); + if (ret != 0 && ret != -EPROBE_DEFER) + KUNIT_FAIL(test, "Failed to register card"); + +- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); +- KUNIT_EXPECT_EQ(test, 0, ret); +- +- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); ++ ret = snd_soc_register_component(component, &test_component, NULL, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* cleanup */ +@@ -425,6 +432,7 @@ static void snd_soc_tplg_test_load_empty_tplg(struct kunit *test) + static void snd_soc_tplg_test_load_empty_tplg_bad_magic(struct kunit *test) + { + struct kunit_soc_component *kunit_comp; ++ struct snd_soc_component *component; + struct tplg_tmpl_001 *data; + int size; + int ret; +@@ -456,15 +464,17 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_magic(struct kunit *test) + kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); + kunit_comp->card.fully_routed = true; + ++ component = snd_soc_component_alloc(test_dev); ++ KUNIT_ASSERT_NOT_NULL(test, component); ++ ++ snd_soc_component_set_priv(component, kunit_comp); ++ + /* run test */ + ret = snd_soc_register_card(&kunit_comp->card); + if (ret != 0 && ret != -EPROBE_DEFER) + KUNIT_FAIL(test, "Failed to register card"); + +- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); +- KUNIT_EXPECT_EQ(test, 0, ret); +- +- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); ++ ret = snd_soc_register_component(component, &test_component, NULL, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* cleanup */ +@@ -480,6 +490,7 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_magic(struct kunit *test) + static void snd_soc_tplg_test_load_empty_tplg_bad_abi(struct kunit *test) + { + struct kunit_soc_component *kunit_comp; ++ struct snd_soc_component *component; + struct tplg_tmpl_001 *data; + int size; + int ret; +@@ -511,15 +522,17 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_abi(struct kunit *test) + kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); + kunit_comp->card.fully_routed = true; + ++ component = snd_soc_component_alloc(test_dev); ++ KUNIT_ASSERT_NOT_NULL(test, component); ++ ++ snd_soc_component_set_priv(component, kunit_comp); ++ + /* run test */ + ret = snd_soc_register_card(&kunit_comp->card); + if (ret != 0 && ret != -EPROBE_DEFER) + KUNIT_FAIL(test, "Failed to register card"); + +- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); +- KUNIT_EXPECT_EQ(test, 0, ret); +- +- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); ++ ret = snd_soc_register_component(component, &test_component, NULL, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* cleanup */ +@@ -535,6 +548,7 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_abi(struct kunit *test) + static void snd_soc_tplg_test_load_empty_tplg_bad_size(struct kunit *test) + { + struct kunit_soc_component *kunit_comp; ++ struct snd_soc_component *component; + struct tplg_tmpl_001 *data; + int size; + int ret; +@@ -566,15 +580,17 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_size(struct kunit *test) + kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); + kunit_comp->card.fully_routed = true; + ++ component = snd_soc_component_alloc(test_dev); ++ KUNIT_ASSERT_NOT_NULL(test, component); ++ ++ snd_soc_component_set_priv(component, kunit_comp); ++ + /* run test */ + ret = snd_soc_register_card(&kunit_comp->card); + if (ret != 0 && ret != -EPROBE_DEFER) + KUNIT_FAIL(test, "Failed to register card"); + +- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); +- KUNIT_EXPECT_EQ(test, 0, ret); +- +- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); ++ ret = snd_soc_register_component(component, &test_component, NULL, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* cleanup */ +@@ -590,6 +606,7 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_size(struct kunit *test) + static void snd_soc_tplg_test_load_empty_tplg_bad_payload_size(struct kunit *test) + { + struct kunit_soc_component *kunit_comp; ++ struct snd_soc_component *component; + struct tplg_tmpl_001 *data; + int size; + int ret; +@@ -622,15 +639,17 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_payload_size(struct kunit *tes + kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); + kunit_comp->card.fully_routed = true; + ++ component = snd_soc_component_alloc(test_dev); ++ KUNIT_ASSERT_NOT_NULL(test, component); ++ ++ snd_soc_component_set_priv(component, kunit_comp); ++ + /* run test */ + ret = snd_soc_register_card(&kunit_comp->card); + if (ret != 0 && ret != -EPROBE_DEFER) + KUNIT_FAIL(test, "Failed to register card"); + +- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); +- KUNIT_EXPECT_EQ(test, 0, ret); +- +- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); ++ ret = snd_soc_register_component(component, &test_component, NULL, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* cleanup */ +@@ -644,6 +663,7 @@ static void snd_soc_tplg_test_load_empty_tplg_bad_payload_size(struct kunit *tes + static void snd_soc_tplg_test_load_pcm_tplg(struct kunit *test) + { + struct kunit_soc_component *kunit_comp; ++ struct snd_soc_component *component; + u8 *data; + int size; + int ret; +@@ -670,15 +690,17 @@ static void snd_soc_tplg_test_load_pcm_tplg(struct kunit *test) + kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); + kunit_comp->card.fully_routed = true; + ++ component = snd_soc_component_alloc(test_dev); ++ KUNIT_ASSERT_NOT_NULL(test, component); ++ ++ snd_soc_component_set_priv(component, kunit_comp); ++ + /* run test */ + ret = snd_soc_register_card(&kunit_comp->card); + if (ret != 0 && ret != -EPROBE_DEFER) + KUNIT_FAIL(test, "Failed to register card"); + +- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); +- KUNIT_EXPECT_EQ(test, 0, ret); +- +- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); ++ ret = snd_soc_register_component(component, &test_component, NULL, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + + snd_soc_unregister_component(test_dev); +@@ -693,6 +715,7 @@ static void snd_soc_tplg_test_load_pcm_tplg(struct kunit *test) + static void snd_soc_tplg_test_load_pcm_tplg_reload_comp(struct kunit *test) + { + struct kunit_soc_component *kunit_comp; ++ struct snd_soc_component *component; + u8 *data; + int size; + int ret; +@@ -720,16 +743,19 @@ static void snd_soc_tplg_test_load_pcm_tplg_reload_comp(struct kunit *test) + kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); + kunit_comp->card.fully_routed = true; + ++ component = snd_soc_component_alloc(test_dev); ++ KUNIT_ASSERT_NOT_NULL(test, component); ++ ++ snd_soc_component_set_priv(component, kunit_comp); ++ + /* run test */ + ret = snd_soc_register_card(&kunit_comp->card); + if (ret != 0 && ret != -EPROBE_DEFER) + KUNIT_FAIL(test, "Failed to register card"); + + for (i = 0; i < 100; i++) { +- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); +- KUNIT_EXPECT_EQ(test, 0, ret); + +- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); ++ ret = snd_soc_register_component(component, &test_component, NULL, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + + snd_soc_unregister_component(test_dev); +@@ -745,6 +771,7 @@ static void snd_soc_tplg_test_load_pcm_tplg_reload_comp(struct kunit *test) + static void snd_soc_tplg_test_load_pcm_tplg_reload_card(struct kunit *test) + { + struct kunit_soc_component *kunit_comp; ++ struct snd_soc_component *component; + u8 *data; + int size; + int ret; +@@ -772,11 +799,13 @@ static void snd_soc_tplg_test_load_pcm_tplg_reload_card(struct kunit *test) + kunit_comp->card.num_links = ARRAY_SIZE(kunit_dai_links); + kunit_comp->card.fully_routed = true; + +- /* run test */ +- ret = snd_soc_component_initialize(&kunit_comp->comp, &test_component, test_dev); +- KUNIT_EXPECT_EQ(test, 0, ret); ++ component = snd_soc_component_alloc(test_dev); ++ KUNIT_ASSERT_NOT_NULL(test, component); + +- ret = snd_soc_add_component(&kunit_comp->comp, NULL, 0); ++ snd_soc_component_set_priv(component, kunit_comp); ++ ++ /* run test */ ++ ret = snd_soc_register_component(component, &test_component, NULL, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + + for (i = 0; i < 100; i++) { +diff --git a/sound/soc/sof/amd/Kconfig b/sound/soc/sof/amd/Kconfig +--- a/sound/soc/sof/amd/Kconfig ++++ b/sound/soc/sof/amd/Kconfig +@@ -104,4 +104,15 @@ config SND_SOC_SOF_AMD_ACP70 + AMD ACP7.0/ACP7.1 version based platforms. + Say Y if you want to enable SOF on ACP7.0/ACP7.1 based platforms. + ++config SND_SOC_SOF_AMD_ACP7X ++ tristate "SOF support for ACP7.B/7.F platforms" ++ depends on SND_SOC_SOF_PCI ++ depends on AMD_NODE ++ select SND_SOC_SOF_AMD_COMMON ++ help ++ Select this option for SOF support on AMD ACP7.B and ACP7.F PCI ++ revision based platforms. ++ Say Y if you want to enable SOF on ACP7.B/7.F based platforms. ++ If unsure select "N". ++ + endif +diff --git a/sound/soc/sof/amd/Makefile b/sound/soc/sof/amd/Makefile +--- a/sound/soc/sof/amd/Makefile ++++ b/sound/soc/sof/amd/Makefile +@@ -11,6 +11,7 @@ snd-sof-amd-rembrandt-y := pci-rmb.o rembrandt.o + snd-sof-amd-vangogh-y := pci-vangogh.o vangogh.o + snd-sof-amd-acp63-y := pci-acp63.o acp63.o + snd-sof-amd-acp70-y := pci-acp70.o acp70.o ++snd-sof-amd-acp7x-y := pci-acp7x.o acp7x.o + + obj-$(CONFIG_SND_SOC_SOF_AMD_COMMON) += snd-sof-amd-acp.o + obj-$(CONFIG_SND_SOC_SOF_AMD_RENOIR) += snd-sof-amd-renoir.o +@@ -18,3 +19,4 @@ obj-$(CONFIG_SND_SOC_SOF_AMD_REMBRANDT) += snd-sof-amd-rembrandt.o + obj-$(CONFIG_SND_SOC_SOF_AMD_VANGOGH) += snd-sof-amd-vangogh.o + obj-$(CONFIG_SND_SOC_SOF_AMD_ACP63) += snd-sof-amd-acp63.o + obj-$(CONFIG_SND_SOC_SOF_AMD_ACP70) += snd-sof-amd-acp70.o ++obj-$(CONFIG_SND_SOC_SOF_AMD_ACP7X) += snd-sof-amd-acp7x.o +diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h +--- a/sound/soc/sof/amd/acp-dsp-offset.h ++++ b/sound/soc/sof/amd/acp-dsp-offset.h +@@ -68,12 +68,14 @@ + #define ACP5X_PGFSM_BASE 0x1424 + #define ACP6X_PGFSM_BASE 0x1024 + #define ACP70_PGFSM_BASE ACP6X_PGFSM_BASE ++#define ACP7X_PGFSM_BASE ACP6X_PGFSM_BASE + #define PGFSM_CONTROL_OFFSET 0x0 + #define PGFSM_STATUS_OFFSET 0x4 + #define ACP3X_CLKMUX_SEL 0x1424 + #define ACP5X_CLKMUX_SEL 0x142C + #define ACP6X_CLKMUX_SEL 0x102C + #define ACP70_CLKMUX_SEL ACP6X_CLKMUX_SEL ++#define ACP7X_CLKMUX_SEL ACP6X_CLKMUX_SEL + + /* Registers from ACP_INTR block */ + #define ACP3X_EXT_INTR_STAT 0x1808 +@@ -86,21 +88,31 @@ + #define ACP70_EXTERNAL_INTR_CNTL ACP6X_EXTERNAL_INTR_CNTL + #define ACP70_EXT_INTR_STAT ACP6X_EXT_INTR_STAT + #define ACP70_EXT_INTR_STAT1 ACP6X_EXT_INTR_STAT1 ++#define ACP7X_EXTERNAL_INTR_ENB ACP6X_EXTERNAL_INTR_ENB ++#define ACP7X_EXTERNAL_INTR_CNTL 0x1A04 ++#define ACP7X_EXT_INTR_STAT 0x1A1C ++#define ACP7X_EXTERNAL_INTR_CNTL1 0x1A08 ++#define ACP7X_EXT_INTR_STAT1 0x1A20 + + #define ACP3X_DSP_SW_INTR_BASE 0x1814 + #define ACP5X_DSP_SW_INTR_BASE 0x1814 + #define ACP6X_DSP_SW_INTR_BASE 0x1808 + #define ACP70_DSP_SW_INTR_BASE ACP6X_DSP_SW_INTR_BASE ++#define ACP7X_DSP_SW_INTR_BASE 0x1860 + #define DSP_SW_INTR_CNTL_OFFSET 0x0 + #define DSP_SW_INTR_STAT_OFFSET 0x4 ++#define ACP7X_DSP_SW_INTR_STAT (ACP7X_DSP_SW_INTR_BASE + DSP_SW_INTR_STAT_OFFSET) + #define DSP_SW_INTR_TRIG_OFFSET 0x8 ++#define ACP7X_DSP_SW_INTR_TRIG_OFFSET 0x30 + #define ACP3X_ERROR_STATUS 0x18C4 + #define ACP6X_ERROR_STATUS 0x1A4C + #define ACP70_ERROR_STATUS ACP6X_ERROR_STATUS ++#define ACP7X_ERROR_STATUS 0x1A88 + #define ACP3X_AXI2DAGB_SEM_0 0x1880 + #define ACP5X_AXI2DAGB_SEM_0 0x1884 + #define ACP6X_AXI2DAGB_SEM_0 0x1874 + #define ACP70_AXI2DAGB_SEM_0 ACP6X_AXI2DAGB_SEM_0 ++#define ACP7X_AXI2DAGB_SEM_0 0x18F4 + + /* ACP common registers to report errors related to I2S & SoundWire interfaces */ + #define ACP3X_SW_I2S_ERROR_REASON 0x18C8 +@@ -123,6 +135,7 @@ + #define ACP_SCRATCH_REG_0 0x10000 + #define ACP6X_DSP_FUSION_RUNSTALL 0x0644 + #define ACP70_DSP_FUSION_RUNSTALL ACP6X_DSP_FUSION_RUNSTALL ++#define ACP7X_DSP_FUSION_RUNSTALL ACP6X_DSP_FUSION_RUNSTALL + + /* Cache window registers */ + #define ACP_DSP0_CACHE_OFFSET0 0x0420 +@@ -139,5 +152,9 @@ + #define ACP70_SDW1_HOST_WAKE_STAT BIT(25) + #define ACP70_SDW0_PME_STAT BIT(26) + #define ACP70_SDW1_PME_STAT BIT(27) ++#define ACP7X_DSP0_IDMA_ERROR_MASK 0x4B0 ++#define ACP7X_IDMA_ERROR_MASK 0x1FF9FF ++#define ACP7X_ZSC_DSP_CTRL 0x001014 ++#define ACP7X_PME_EN ACP70_PME_EN + + #endif +diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c +--- a/sound/soc/sof/amd/acp-ipc.c ++++ b/sound/soc/sof/amd/acp-ipc.c +@@ -32,11 +32,20 @@ static void acpbus_trigger_host_to_dsp_swintr(struct acp_dev_data *adata) + struct snd_sof_dev *sdev = adata->dev; + const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); + u32 swintr_trigger; ++ unsigned int swintr_trigger_reg_offset; + ++ switch (adata->pci_rev) { ++ case ACP7B_PCI_ID: ++ case ACP7F_PCI_ID: ++ swintr_trigger_reg_offset = ACP7X_DSP_SW_INTR_TRIG_OFFSET; ++ break; ++ default: ++ swintr_trigger_reg_offset = DSP_SW_INTR_TRIG_OFFSET; ++ } + swintr_trigger = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->dsp_intr_base + +- DSP_SW_INTR_TRIG_OFFSET); ++ swintr_trigger_reg_offset); + swintr_trigger |= 0x01; +- snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->dsp_intr_base + DSP_SW_INTR_TRIG_OFFSET, ++ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->dsp_intr_base + swintr_trigger_reg_offset, + swintr_trigger); + } + +diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c +--- a/sound/soc/sof/amd/acp-loader.c ++++ b/sound/soc/sof/amd/acp-loader.c +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + + #include "../ops.h" + #include "acp-dsp-offset.h" +@@ -173,10 +174,31 @@ int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev) + + adata = sdev->pdata->hw_pdata; + +- if (adata->quirks && adata->quirks->signed_fw_image) ++ if (adata->pci_rev >= ACP7B_PCI_ID) { ++ if (adata->acp_sof_signed_firmware_image) { ++ if (adata->fw_bin_size <= ACP_IMAGE_HEADER_SIZE) { ++ dev_err(sdev->dev, "Invalid signed firmware size %u\n", ++ adata->fw_bin_size); ++ return -EINVAL; ++ } ++ size_fw = get_unaligned_le32(adata->bin_buf + ++ ACP_IMAGE_HDR_SIZE_FW_SIGNED_OFF); ++ if (!size_fw || ++ size_fw > adata->fw_bin_size - ACP_IMAGE_HEADER_SIZE) { ++ dev_err(sdev->dev, ++ "Invalid signed firmware payload size %u (max %u)\n", ++ size_fw, adata->fw_bin_size - ACP_IMAGE_HEADER_SIZE); ++ return -EINVAL; ++ } ++ size_fw += ACP_IMAGE_HEADER_SIZE; ++ } else { ++ size_fw = adata->fw_bin_size; ++ } ++ } else if (adata->quirks && adata->quirks->signed_fw_image) { + size_fw = adata->fw_bin_size - ACP_FIRMWARE_SIGNATURE; +- else ++ } else { + size_fw = adata->fw_bin_size; ++ } + + page_count = PAGE_ALIGN(size_fw) >> PAGE_SHIFT; + adata->fw_bin_page_count = page_count; +@@ -312,9 +334,14 @@ int acp_sof_load_signed_firmware(struct snd_sof_dev *sdev) + } + kfree(fw_filename); + +- ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_DRAM, 0, +- (void *)adata->fw_dbin->data, +- adata->fw_dbin->size); ++ if (adata->pci_rev >= ACP7B_PCI_ID) ++ ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_SRAM, 0, ++ (void *)adata->fw_dbin->data, ++ adata->fw_dbin->size); ++ else ++ ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_DRAM, 0, ++ (void *)adata->fw_dbin->data, ++ adata->fw_dbin->size); + return ret; + } + EXPORT_SYMBOL_NS(acp_sof_load_signed_firmware, "SND_SOC_SOF_AMD_COMMON"); +diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c +--- a/sound/soc/sof/amd/acp.c ++++ b/sound/soc/sof/amd/acp.c +@@ -12,6 +12,7 @@ + * Hardware interface for generic AMD ACP processor + */ + ++#include + #include + #include + #include +@@ -60,6 +61,8 @@ static void init_dma_descriptor(struct acp_dev_data *adata) + case ACP70_PCI_ID: + case ACP71_PCI_ID: + case ACP72_PCI_ID: ++ case ACP7B_PCI_ID: ++ case ACP7F_PCI_ID: + acp_dma_desc_base_addr = ACP70_DMA_DESC_BASE_ADDR; + acp_dma_desc_max_num_dscr = ACP70_DMA_DESC_MAX_NUM_DSCR; + break; +@@ -101,6 +104,8 @@ static int config_dma_channel(struct acp_dev_data *adata, unsigned int ch, + case ACP70_PCI_ID: + case ACP71_PCI_ID: + case ACP72_PCI_ID: ++ case ACP7B_PCI_ID: ++ case ACP7F_PCI_ID: + acp_dma_cntl_0 = ACP70_DMA_CNTL_0; + acp_dma_ch_rst_sts = ACP70_DMA_CH_RST_STS; + acp_dma_dscr_err_sts_0 = ACP70_DMA_ERR_STS_0; +@@ -283,7 +288,8 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr, + } + } + +- if (adata->quirks && adata->quirks->signed_fw_image) ++ if ((adata->quirks && adata->quirks->signed_fw_image) || ++ adata->acp_sof_signed_firmware_image) + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_INCLUDE_HDR, ACP_SHA_HEADER); + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_STRT_ADDR, start_addr); +@@ -342,6 +348,8 @@ int acp_dma_status(struct acp_dev_data *adata, unsigned char ch) + case ACP70_PCI_ID: + case ACP71_PCI_ID: + case ACP72_PCI_ID: ++ case ACP7B_PCI_ID: ++ case ACP7F_PCI_ID: + acp_dma_ch_sts = ACP70_DMA_CH_STS; + break; + default: +@@ -534,8 +542,17 @@ static irqreturn_t acp_irq_handler(int irq, void *dev_id) + snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP_ERROR_IRQ_MASK); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_sw0_i2s_err_reason, 0); + /* ACP_SW1_I2S_ERROR_REASON is newly added register from rmb platform onwards */ +- if (adata->pci_rev >= ACP_RMB_PCI_ID) ++ switch (adata->pci_rev) { ++ case ACP_RMB_PCI_ID: ++ case ACP63_PCI_ID: ++ case ACP70_PCI_ID: ++ case ACP71_PCI_ID: ++ case ACP72_PCI_ID: + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SW1_I2S_ERROR_REASON, 0); ++ break; ++ default: ++ break; ++ } + snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_error_stat, 0); + irq_flag = 1; + } +@@ -564,6 +581,35 @@ static irqreturn_t acp_irq_handler(int irq, void *dev_id) + return IRQ_NONE; + } + ++static irqreturn_t acp7x_irq_handler(int irq, void *dev_id) ++{ ++ struct snd_sof_dev *sdev = dev_id; ++ const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); ++ unsigned int base = desc->dsp_intr_base; ++ unsigned int val; ++ unsigned int ext_intr_stat; ++ int irq_flag = 0; ++ ++ val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET); ++ if (val & ACP_DSP_TO_HOST_IRQ) { ++ snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET, ++ ACP_DSP_TO_HOST_IRQ); ++ return IRQ_WAKE_THREAD; ++ } ++ ++ ext_intr_stat = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat); ++ if (ext_intr_stat & ACP_ERROR_IRQ_MASK) { ++ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP_ERROR_IRQ_MASK); ++ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_error_stat, 0); ++ irq_flag = 1; ++ } ++ ++ if (irq_flag) ++ return IRQ_HANDLED; ++ ++ return IRQ_NONE; ++} ++ + static int acp_power_on(struct snd_sof_dev *sdev) + { + const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); +@@ -571,6 +617,7 @@ static int acp_power_on(struct snd_sof_dev *sdev) + unsigned int base = desc->pgfsm_base; + unsigned int val; + unsigned int acp_pgfsm_status_mask, acp_pgfsm_cntl_mask; ++ bool use_masked_status = false; + int ret; + + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + PGFSM_STATUS_OFFSET); +@@ -595,6 +642,12 @@ static int acp_power_on(struct snd_sof_dev *sdev) + acp_pgfsm_status_mask = ACP70_PGFSM_STATUS_MASK; + acp_pgfsm_cntl_mask = ACP70_PGFSM_CNTL_POWER_ON_MASK; + break; ++ case ACP7B_PCI_ID: ++ case ACP7F_PCI_ID: ++ acp_pgfsm_status_mask = ACP7X_PGFSM_STATUS_MASK; ++ acp_pgfsm_cntl_mask = ACP7X_PGFSM_CNTL_POWER_ON_MASK; ++ use_masked_status = true; ++ break; + default: + return -EINVAL; + } +@@ -603,8 +656,17 @@ static int acp_power_on(struct snd_sof_dev *sdev) + snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + PGFSM_CONTROL_OFFSET, + acp_pgfsm_cntl_mask); + +- ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, base + PGFSM_STATUS_OFFSET, val, +- !val, ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US); ++ if (use_masked_status) ++ ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ++ base + PGFSM_STATUS_OFFSET, val, ++ !(val & acp_pgfsm_status_mask), ++ ACP_REG_POLL_INTERVAL, ++ ACP_REG_POLL_TIMEOUT_US); ++ else ++ ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ++ base + PGFSM_STATUS_OFFSET, val, ++ !val, ACP_REG_POLL_INTERVAL, ++ ACP_REG_POLL_TIMEOUT_US); + if (ret < 0) + dev_err(sdev->dev, "timeout in ACP_PGFSM_STATUS read\n"); + +@@ -703,6 +765,13 @@ static int acp_init(struct snd_sof_dev *sdev) + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP70_PME_EN, 1); + break; ++ case ACP7B_PCI_ID: ++ case ACP7F_PCI_ID: ++ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_ZSC_DSP_CTRL, 0); ++ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_PME_EN, 1); ++ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_DSP0_IDMA_ERROR_MASK, ++ ACP7X_IDMA_ERROR_MASK); ++ break; + } + return 0; + } +@@ -749,6 +818,8 @@ int amd_sof_acp_suspend(struct snd_sof_dev *sdev, u32 target_state) + case ACP72_PCI_ID: + enable = true; + break; ++ default: ++ break; + } + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_CONTROL, enable); + +@@ -990,6 +1061,194 @@ void amd_sof_acp_remove(struct snd_sof_dev *sdev) + } + EXPORT_SYMBOL_NS(amd_sof_acp_remove, "SND_SOC_SOF_AMD_COMMON"); + ++int amd_sof_acp7x_probe(struct snd_sof_dev *sdev) ++{ ++ struct pci_dev *pci = to_pci_dev(sdev->dev); ++ struct acp_dev_data *adata; ++ const struct sof_amd_acp_desc *chip; ++ const union acpi_object *obj; ++ struct acpi_device *adev; ++ unsigned int addr; ++ unsigned int irqflags; ++ int ret; ++ ++ chip = get_chip_info(sdev->pdata); ++ if (!chip) { ++ dev_err(sdev->dev, "no such device supported, chip id:%x\n", pci->device); ++ return -EIO; ++ } ++ adata = devm_kzalloc(sdev->dev, sizeof(struct acp_dev_data), GFP_KERNEL); ++ if (!adata) ++ return -ENOMEM; ++ ++ adata->dev = sdev; ++ adata->dmic_dev = platform_device_register_data(sdev->dev, "dmic-codec", ++ PLATFORM_DEVID_NONE, NULL, 0); ++ if (IS_ERR(adata->dmic_dev)) { ++ dev_err(sdev->dev, "failed to register platform for dmic codec\n"); ++ return PTR_ERR(adata->dmic_dev); ++ } ++ ++ addr = pci_resource_start(pci, ACP_DSP_BAR); ++ sdev->bar[ACP_DSP_BAR] = devm_ioremap(sdev->dev, addr, pci_resource_len(pci, ACP_DSP_BAR)); ++ if (!sdev->bar[ACP_DSP_BAR]) { ++ dev_err(sdev->dev, "ioremap error\n"); ++ ret = -ENXIO; ++ goto unregister_dev; ++ } ++ ++ pci_set_master(pci); ++ adata->addr = addr; ++ adata->reg_range = chip->reg_end_addr - chip->reg_start_addr; ++ adata->pci_rev = pci->revision; ++ mutex_init(&adata->acp_lock); ++ sdev->pdata->hw_pdata = adata; ++ ++ ret = acp_init(sdev); ++ if (ret < 0) ++ goto unregister_dev; ++ ++ adev = ACPI_COMPANION(&pci->dev); ++ ++ sdev->ipc_irq = pci->irq; ++ irqflags = IRQF_SHARED; ++ ++ ret = request_threaded_irq(pci->irq, acp7x_irq_handler, acp_irq_thread, ++ irqflags, "AudioDSP", sdev); ++ if (ret < 0) { ++ dev_err(sdev->dev, "failed to register IRQ %d\n", sdev->ipc_irq); ++ goto unregister_dev; ++ } ++ ++ if (adev) { ++ if (!acpi_dev_get_property(adev, "acp-sof-signed-firmware-image", ++ ACPI_TYPE_INTEGER, &obj)) ++ adata->acp_sof_signed_firmware_image = obj->integer.value; ++ } ++ ++ sdev->dsp_box.offset = 0; ++ sdev->dsp_box.size = BOX_SIZE_512; ++ ++ sdev->host_box.offset = sdev->dsp_box.offset + sdev->dsp_box.size; ++ sdev->host_box.size = BOX_SIZE_512; ++ ++ sdev->debug_box.offset = sdev->host_box.offset + sdev->host_box.size; ++ sdev->debug_box.size = BOX_SIZE_1024; ++ ++ if (adata->acp_sof_signed_firmware_image) { ++ adata->fw_code_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, ++ "sof-%s-code.bin", chip->name); ++ if (!adata->fw_code_bin) { ++ ret = -ENOMEM; ++ goto free_ipc_irq; ++ } ++ adata->fw_data_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, ++ "sof-%s-data.bin", chip->name); ++ if (!adata->fw_data_bin) { ++ ret = -ENOMEM; ++ goto free_ipc_irq; ++ } ++ } ++ ++ adata->enable_fw_debug = enable_fw_debug; ++ acp_memory_init(sdev); ++ acp_dsp_stream_init(sdev); ++ ++ return 0; ++ ++free_ipc_irq: ++ free_irq(sdev->ipc_irq, sdev); ++unregister_dev: ++ platform_device_unregister(adata->dmic_dev); ++ return ret; ++} ++EXPORT_SYMBOL_NS(amd_sof_acp7x_probe, "SND_SOC_SOF_AMD_COMMON"); ++ ++void amd_sof_acp7x_remove(struct snd_sof_dev *sdev) ++{ ++ struct acp_dev_data *adata = sdev->pdata->hw_pdata; ++ ++ if (sdev->ipc_irq) ++ free_irq(sdev->ipc_irq, sdev); ++ ++ if (adata->dmic_dev) ++ platform_device_unregister(adata->dmic_dev); ++ ++ acp_reset(sdev); ++} ++EXPORT_SYMBOL_NS(amd_sof_acp7x_remove, "SND_SOC_SOF_AMD_COMMON"); ++ ++int amd_sof_acp7x_suspend(struct snd_sof_dev *sdev, u32 target_state) ++{ ++ struct acp_dev_data *acp_data; ++ int ret; ++ bool enable = false; ++ ++ acp_data = sdev->pdata->hw_pdata; ++ ++ ret = acp_reset(sdev); ++ if (ret) { ++ dev_err(sdev->dev, "ACP Reset failed\n"); ++ return ret; ++ } ++ switch (acp_data->pci_rev) { ++ case ACP7B_PCI_ID: ++ case ACP7F_PCI_ID: ++ enable = true; ++ break; ++ default: ++ break; ++ } ++ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_CONTROL, enable); ++ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_ZSC_DSP_CTRL, 1); ++ ++ return 0; ++} ++EXPORT_SYMBOL_NS(amd_sof_acp7x_suspend, "SND_SOC_SOF_AMD_COMMON"); ++ ++int amd_sof_acp7x_resume(struct snd_sof_dev *sdev) ++{ ++ struct acp_dev_data *acp_data; ++ int ret; ++ ++ acp_data = sdev->pdata->hw_pdata; ++ ++ ret = acp_init(sdev); ++ if (ret) { ++ dev_err(sdev->dev, "ACP Init failed\n"); ++ return ret; ++ } ++ ret = acp_memory_init(sdev); ++ if (ret) { ++ dev_err(sdev->dev, "ACP Memory init failed\n"); ++ return ret; ++ } ++ ++ switch (acp_data->pci_rev) { ++ case ACP7B_PCI_ID: ++ case ACP7F_PCI_ID: ++ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_PME_EN, 1); ++ break; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_NS(amd_sof_acp7x_resume, "SND_SOC_SOF_AMD_COMMON"); ++ ++int amd_sof_acp7x_suspend_runtime(struct snd_sof_dev *sdev) ++{ ++ return amd_sof_acp7x_suspend(sdev, 0); ++} ++EXPORT_SYMBOL_NS(amd_sof_acp7x_suspend_runtime, "SND_SOC_SOF_AMD_COMMON"); ++ ++int amd_sof_acp7x_resume_runtime(struct snd_sof_dev *sdev) ++{ ++ return amd_sof_acp7x_resume(sdev); ++} ++EXPORT_SYMBOL_NS(amd_sof_acp7x_resume_runtime, "SND_SOC_SOF_AMD_COMMON"); ++ + MODULE_LICENSE("Dual BSD/GPL"); + MODULE_DESCRIPTION("AMD ACP sof driver"); + MODULE_IMPORT_NS("SOUNDWIRE_AMD_INIT"); +diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h +--- a/sound/soc/sof/amd/acp.h ++++ b/sound/soc/sof/amd/acp.h +@@ -76,6 +76,12 @@ + #define ACP70_PCI_ID 0x70 + #define ACP71_PCI_ID 0x71 + #define ACP72_PCI_ID 0x72 ++#define ACP7B_PCI_ID 0x7B ++#define ACP7F_PCI_ID 0x7F ++ ++#define ACP7X_PGFSM_CNTL_POWER_ON_MASK 0x7F ++#define ACP7X_PGFSM_STATUS_MASK 0xFFF ++#define ACP7X_SRAM_PTE_OFFSET ACP6X_SRAM_PTE_OFFSET + + #define HOST_BRIDGE_CZN 0x1630 + #define HOST_BRIDGE_VGH 0x1645 +@@ -107,6 +113,9 @@ + #define PROBE_STATUS_BIT BIT(31) + + #define ACP_FIRMWARE_SIGNATURE 0x100 ++#define ACP_IMAGE_HEADER_SIZE ACP_FIRMWARE_SIGNATURE ++#define ACP_IMAGE_HDR_SIZE_FW_SIGNED_OFF 0x14 ++ + #define ACP_ERROR_IRQ_MASK BIT(29) + #define ACP_SDW0_IRQ_MASK BIT(21) + #define ACP_SDW1_IRQ_MASK BIT(2) +@@ -271,6 +280,7 @@ struct acp_dev_data { + /* acp70_sdw1_wake_event flag set to true when wake irq asserted for SW1 instance */ + bool acp70_sdw1_wake_event; + unsigned int pci_rev; ++ int acp_sof_signed_firmware_image; + }; + + void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes); +@@ -344,6 +354,16 @@ int sof_acp63_ops_init(struct snd_sof_dev *sdev); + extern struct snd_sof_dsp_ops sof_acp70_ops; + int sof_acp70_ops_init(struct snd_sof_dev *sdev); + ++extern struct snd_sof_dsp_ops sof_acp7x_ops; ++int sof_acp7x_ops_init(struct snd_sof_dev *sdev); ++ ++int amd_sof_acp7x_probe(struct snd_sof_dev *sdev); ++void amd_sof_acp7x_remove(struct snd_sof_dev *sdev); ++int amd_sof_acp7x_suspend(struct snd_sof_dev *sdev, u32 target_state); ++int amd_sof_acp7x_resume(struct snd_sof_dev *sdev); ++int amd_sof_acp7x_suspend_runtime(struct snd_sof_dev *sdev); ++int amd_sof_acp7x_resume_runtime(struct snd_sof_dev *sdev); ++ + struct snd_soc_acpi_mach *amd_sof_machine_select(struct snd_sof_dev *sdev); + /* Machine configuration */ + int snd_amd_acp_find_config(struct pci_dev *pci); +diff --git a/sound/soc/sof/amd/acp7x.c b/sound/soc/sof/amd/acp7x.c +new file mode 100644 +--- /dev/null ++++ b/sound/soc/sof/amd/acp7x.c +@@ -0,0 +1,185 @@ ++// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) ++// ++// This file is provided under a dual BSD/GPLv2 license. When using or ++// redistributing this file, you may do so under either license. ++// ++// Copyright(c) 2025 Advanced Micro Devices, Inc. ++// ++// Authors: Vijendar Mukunda ++ ++/* ++ * Hardware interface for Audio DSP on ACP7.B/7.F platforms ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "../ops.h" ++#include "../sof-audio.h" ++#include "acp.h" ++#include "acp-dsp-offset.h" ++ ++#define I2S_TDM0_INSTANCE 0 ++#define I2S_TDM1_INSTANCE 1 ++#define I2S_TDM2_INSTANCE 2 ++#define PDM0_DMIC_INSTANCE 3 ++#define PDM1_DMIC_INSTANCE 4 ++ ++static struct snd_soc_dai_driver acp7x_sof_dai[] = { ++ [I2S_TDM0_INSTANCE] = { ++ .id = I2S_TDM0_INSTANCE, ++ .name = "acp-sof-i2s0", ++ .playback = { ++ .rates = SNDRV_PCM_RATE_8000_96000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | ++ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, ++ .channels_min = 2, ++ .channels_max = 8, ++ .rate_min = 8000, ++ .rate_max = 96000, ++ }, ++ .capture = { ++ .rates = SNDRV_PCM_RATE_8000_48000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | ++ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, ++ /* Supporting only stereo for I2S HS controller capture */ ++ .channels_min = 2, ++ .channels_max = 2, ++ .rate_min = 8000, ++ .rate_max = 48000, ++ }, ++ }, ++ ++ [I2S_TDM1_INSTANCE] = { ++ .id = I2S_TDM1_INSTANCE, ++ .name = "acp-sof-i2s1", ++ .playback = { ++ .rates = SNDRV_PCM_RATE_8000_96000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | ++ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, ++ .channels_min = 2, ++ .channels_max = 8, ++ .rate_min = 8000, ++ .rate_max = 96000, ++ }, ++ .capture = { ++ .rates = SNDRV_PCM_RATE_8000_48000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | ++ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, ++ /* Supporting only stereo for I2S BT controller capture */ ++ .channels_min = 2, ++ .channels_max = 2, ++ .rate_min = 8000, ++ .rate_max = 48000, ++ }, ++ }, ++ ++ [I2S_TDM2_INSTANCE] = { ++ .id = I2S_TDM2_INSTANCE, ++ .name = "acp-sof-i2s2", ++ .playback = { ++ .rates = SNDRV_PCM_RATE_8000_96000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | ++ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, ++ .channels_min = 2, ++ .channels_max = 8, ++ .rate_min = 8000, ++ .rate_max = 96000, ++ }, ++ .capture = { ++ .rates = SNDRV_PCM_RATE_8000_48000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | ++ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, ++ /* Supporting only stereo for I2S SP controller capture */ ++ .channels_min = 2, ++ .channels_max = 2, ++ .rate_min = 8000, ++ .rate_max = 48000, ++ }, ++ }, ++ ++ [PDM0_DMIC_INSTANCE] = { ++ .id = PDM0_DMIC_INSTANCE, ++ .name = "acp-sof-dmic0", ++ .capture = { ++ .rates = SNDRV_PCM_RATE_8000_48000, ++ .formats = SNDRV_PCM_FMTBIT_S32_LE, ++ .channels_min = 2, ++ .channels_max = 4, ++ .rate_min = 8000, ++ .rate_max = 48000, ++ }, ++ }, ++ ++ [PDM1_DMIC_INSTANCE] = { ++ .id = PDM1_DMIC_INSTANCE, ++ .name = "acp-sof-dmic1", ++ .capture = { ++ .rates = SNDRV_PCM_RATE_8000_96000, ++ .formats = SNDRV_PCM_FMTBIT_S32_LE, ++ .channels_min = 2, ++ .channels_max = 4, ++ .rate_min = 8000, ++ .rate_max = 96000, ++ }, ++ }, ++}; ++ ++static int sof_acp7x_post_fw_run_delay(struct snd_sof_dev *sdev) ++{ ++ /* ++ * Resuming from suspend in some cases may cause the DSP firmware ++ * to enter an unrecoverable faulty state. Delaying a bit any host ++ * to DSP transmission right after firmware boot completion seems ++ * to resolve the issue. ++ */ ++ if (!sdev->first_boot) ++ usleep_range(100, 150); ++ ++ return 0; ++} ++ ++struct snd_sof_dsp_ops sof_acp7x_ops; ++EXPORT_SYMBOL_NS(sof_acp7x_ops, "SND_SOC_SOF_AMD_COMMON"); ++ ++int sof_acp7x_ops_init(struct snd_sof_dev *sdev) ++{ ++ struct acpi_device *adev = ACPI_COMPANION(&to_pci_dev(sdev->dev)->dev); ++ const union acpi_object *obj; ++ int acp_sof_signed_firmware_image = 0; ++ int acp_sof_post_fw_run_delay = 0; ++ ++ /* common defaults */ ++ memcpy(&sof_acp7x_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops)); ++ ++ sof_acp7x_ops.drv = acp7x_sof_dai; ++ sof_acp7x_ops.num_drv = ARRAY_SIZE(acp7x_sof_dai); ++ sof_acp7x_ops.probe = amd_sof_acp7x_probe; ++ sof_acp7x_ops.remove = amd_sof_acp7x_remove; ++ ++ if (adev) { ++ if (!acpi_dev_get_property(adev, "acp-sof-signed-firmware-image", ++ ACPI_TYPE_INTEGER, &obj)) ++ acp_sof_signed_firmware_image = obj->integer.value; ++ ++ if (!acpi_dev_get_property(adev, "acp-sof-post_fw_run_delay", ++ ACPI_TYPE_INTEGER, &obj)) ++ acp_sof_post_fw_run_delay = obj->integer.value; ++ } ++ ++ if (acp_sof_signed_firmware_image) ++ sof_acp7x_ops.load_firmware = acp_sof_load_signed_firmware; ++ ++ if (acp_sof_post_fw_run_delay) ++ sof_acp7x_ops.post_fw_run = sof_acp7x_post_fw_run_delay; ++ ++ sof_acp7x_ops.suspend = amd_sof_acp7x_suspend; ++ sof_acp7x_ops.resume = amd_sof_acp7x_resume; ++ sof_acp7x_ops.runtime_suspend = amd_sof_acp7x_suspend_runtime; ++ sof_acp7x_ops.runtime_resume = amd_sof_acp7x_resume_runtime; ++ ++ return 0; ++} +diff --git a/sound/soc/sof/amd/pci-acp7x.c b/sound/soc/sof/amd/pci-acp7x.c +new file mode 100644 +--- /dev/null ++++ b/sound/soc/sof/amd/pci-acp7x.c +@@ -0,0 +1,116 @@ ++// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) ++// ++// This file is provided under a dual BSD/GPLv2 license. When using or ++// redistributing this file, you may do so under either license. ++// ++// Copyright(c) 2025 Advanced Micro Devices, Inc. All rights reserved. ++// ++// Authors: Vijendar Mukunda ++ ++/* ++ * PCI interface for ACP7.B/7.F devices ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "../ops.h" ++#include "../sof-pci-dev.h" ++#include "../../amd/mach-config.h" ++#include "acp.h" ++#include "acp-dsp-offset.h" ++ ++#define ACP7X_FUTURE_REG_ACLK_0 0x18e0 ++#define ACP7X_REG_START 0x1240000 ++#define ACP7X_REG_END 0x125C000 ++ ++static const struct sof_amd_acp_desc acp7x_chip_info = { ++ .name = "acp7x", ++ .pgfsm_base = ACP7X_PGFSM_BASE, ++ .ext_intr_enb = ACP6X_EXTERNAL_INTR_ENB, ++ .ext_intr_cntl = ACP7X_EXTERNAL_INTR_CNTL, ++ .ext_intr_stat = ACP7X_EXT_INTR_STAT, ++ .ext_intr_stat1 = ACP7X_EXT_INTR_STAT1, ++ .dsp_intr_base = ACP7X_DSP_SW_INTR_BASE, ++ .acp_error_stat = ACP7X_ERROR_STATUS, ++ .sram_pte_offset = ACP7X_SRAM_PTE_OFFSET, ++ .hw_semaphore_offset = ACP7X_AXI2DAGB_SEM_0, ++ .fusion_dsp_offset = ACP7X_DSP_FUSION_RUNSTALL, ++ .probe_reg_offset = ACP7X_FUTURE_REG_ACLK_0, ++ .reg_start_addr = ACP7X_REG_START, ++ .reg_end_addr = ACP7X_REG_END, ++}; ++ ++static const struct sof_dev_desc acp7x_desc = { ++ .machines = snd_soc_acpi_amd_acp7x_sof_machines, ++ .resindex_lpe_base = 0, ++ .resindex_pcicfg_base = -1, ++ .resindex_imr_base = -1, ++ .irqindex_host_ipc = -1, ++ .chip_info = &acp7x_chip_info, ++ .ipc_supported_mask = BIT(SOF_IPC_TYPE_3), ++ .ipc_default = SOF_IPC_TYPE_3, ++ .default_fw_path = { ++ [SOF_IPC_TYPE_3] = "amd/sof", ++ }, ++ .default_tplg_path = { ++ [SOF_IPC_TYPE_3] = "amd/sof-tplg", ++ }, ++ .default_fw_filename = { ++ [SOF_IPC_TYPE_3] = "sof-acp7x.ri", ++ }, ++ .nocodec_tplg_filename = "sof-acp.tplg", ++ .ops = &sof_acp7x_ops, ++ .ops_init = sof_acp7x_ops_init, ++}; ++ ++static int acp7x_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) ++{ ++ unsigned int flag; ++ ++ switch (pci->revision) { ++ case ACP7B_PCI_ID: ++ case ACP7F_PCI_ID: ++ break; ++ default: ++ return -ENODEV; ++ } ++ ++ flag = snd_amd_acp_find_config(pci); ++ if (flag != FLAG_AMD_SOF && flag != FLAG_AMD_SOF_ONLY_DMIC) ++ return -ENODEV; ++ ++ return sof_pci_probe(pci, pci_id); ++} ++ ++static void acp7x_pci_remove(struct pci_dev *pci) ++{ ++ sof_pci_remove(pci); ++} ++ ++/* PCI IDs */ ++static const struct pci_device_id acp7x_pci_ids[] = { ++ { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID), ++ .driver_data = (unsigned long)&acp7x_desc}, ++ { 0, } ++}; ++MODULE_DEVICE_TABLE(pci, acp7x_pci_ids); ++ ++/* pci_driver definition */ ++static struct pci_driver snd_sof_pci_amd_acp7x_driver = { ++ .name = KBUILD_MODNAME, ++ .id_table = acp7x_pci_ids, ++ .probe = acp7x_pci_probe, ++ .remove = acp7x_pci_remove, ++ .driver = { ++ .pm = pm_ptr(&sof_pci_pm), ++ }, ++}; ++module_pci_driver(snd_sof_pci_amd_acp7x_driver); ++ ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_DESCRIPTION("ACP7X SOF Driver"); ++MODULE_IMPORT_NS("SND_SOC_SOF_AMD_COMMON"); ++MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV"); +diff --git a/sound/soc/sof/fw-file-profile.c b/sound/soc/sof/fw-file-profile.c +--- a/sound/soc/sof/fw-file-profile.c ++++ b/sound/soc/sof/fw-file-profile.c +@@ -16,20 +16,19 @@ static int sof_test_firmware_file(struct device *dev, + enum sof_ipc_type *ipc_type_to_adjust) + { + enum sof_ipc_type fw_ipc_type; +- const struct firmware *fw; +- const char *fw_filename; + const u32 *magic; + int ret; + +- fw_filename = kasprintf(GFP_KERNEL, "%s/%s", profile->fw_path, +- profile->fw_name); ++ const char *fw_filename __free(kfree) = ++ kasprintf(GFP_KERNEL, "%s/%s", profile->fw_path, ++ profile->fw_name); + if (!fw_filename) + return -ENOMEM; + ++ const struct firmware *fw __free(firmware) = NULL; + ret = firmware_request_nowarn(&fw, fw_filename, dev); + if (ret < 0) { + dev_dbg(dev, "Failed to open firmware file: %s\n", fw_filename); +- kfree(fw_filename); + return ret; + } + +@@ -44,8 +43,7 @@ static int sof_test_firmware_file(struct device *dev, + break; + default: + dev_err(dev, "Invalid firmware magic: %#x\n", *magic); +- ret = -EINVAL; +- goto out; ++ return -EINVAL; + } + + if (ipc_type_to_adjust) { +@@ -54,13 +52,10 @@ static int sof_test_firmware_file(struct device *dev, + dev_err(dev, + "ipc type mismatch between %s and expected: %d vs %d\n", + fw_filename, fw_ipc_type, profile->ipc_type); +- ret = -EINVAL; ++ return -EINVAL; + } +-out: +- release_firmware(fw); +- kfree(fw_filename); + +- return ret; ++ return 0; + } + + static int sof_test_topology_file(struct device *dev, +diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c +--- a/sound/soc/sof/intel/hda-ctrl.c ++++ b/sound/soc/sof/intel/hda-ctrl.c +@@ -186,6 +186,7 @@ EXPORT_SYMBOL_NS(hda_dsp_ctrl_clock_power_gating, "SND_SOC_SOF_INTEL_HDA_COMMON" + int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec) + { + struct hdac_bus *bus = sof_to_bus(sdev); ++ struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); + struct hdac_stream *stream; + int sd_offset, ret = 0; + u32 gctl; +@@ -193,6 +194,16 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec) + if (bus->chip_init) + return 0; + ++ /* ++ * The controller reset clears the ACE2+ link DMA stream allocation ++ * constraints; reset the masks to reflect this. ++ */ ++ memset(sof_hda->link_dma_active_sdw_mask, 0, ++ sizeof(sof_hda->link_dma_active_sdw_mask)); ++ memset(sof_hda->link_dma_active_multi_mask, 0, ++ sizeof(sof_hda->link_dma_active_multi_mask)); ++ sof_hda->link_dma_out_hda_used_mask = 0; ++ + hda_codec_set_codec_wakeup(sdev, true); + + hda_dsp_ctrl_misc_clock_gating(sdev, false); +@@ -223,6 +234,14 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec) + /* Accept unsolicited responses */ + snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL); + ++ /* Perform a one-time enumeration of the Multi-Link capability */ ++ ret = hda_bus_ml_init(bus); ++ if (ret < 0) { ++ dev_err(sdev->dev, "%s: failed to enumerate multi-links\n", ++ __func__); ++ goto err; ++ } ++ + if (detect_codec) + hda_codec_detect_mask(sdev); + +diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c +--- a/sound/soc/sof/intel/hda-dai-ops.c ++++ b/sound/soc/sof/intel/hda-dai-ops.c +@@ -20,7 +20,7 @@ + /* These ops are only applicable for the HDA DAI's in their current form */ + #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK) + /* +- * This function checks if the host dma channel corresponding ++ * This function checks if the host DMA stream corresponding + * to the link DMA stream_tag argument is assigned to one + * of the FEs connected to the BE DAI. + */ +@@ -42,23 +42,53 @@ static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd, + } + + static struct hdac_ext_stream * +-hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream) ++hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream, ++ enum hda_bus_ml_link_type link_type) + { + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); ++ struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); + struct sof_intel_hda_stream *hda_stream; + const struct sof_intel_dsp_desc *chip; + struct snd_sof_dev *sdev; + struct hdac_ext_stream *res = NULL; + struct hdac_stream *hstream = NULL; +- + int stream_dir = substream->stream; ++ bool is_multi = link_type == HDA_BUS_ML_LINK_HDA || link_type == HDA_BUS_ML_LINK_UAOL; ++ bool is_play = stream_dir == SNDRV_PCM_STREAM_PLAYBACK; ++ bool is_sdw = link_type == HDA_BUS_ML_LINK_SDW; ++ bool is_hda = link_type == HDA_BUS_ML_LINK_HDA; ++ u32 concur_block_mask = 0; ++ u32 seq_block_mask = 0; ++ unsigned int stream_idx; + + if (!bus->ppcap) { + dev_err(bus->dev, "stream type not supported\n"); + return NULL; + } + ++ /* ++ * On ACE2+ the link DMA stream allocator must avoid two HW errata, ++ * see the comment on struct sof_intel_hda_dev. ++ * ++ * - Concurrent cross-direction: SoundWire conflicts with HDA, iDisp ++ * and UAOL on the same physical stream index; SSP and DMIC are safe. ++ * - Sequential playback: a stream index previously used by an HDA/iDisp ++ * link cannot drive any non-HDA/iDisp link in the same direction ++ * until the next controller reset. ++ * ++ * The masks are protected by bus->reg_lock; sample them inside the ++ * lock together with the stream walk to keep the decision atomic ++ * with concurrent allocations and releases. ++ */ + guard(spinlock_irq)(&bus->reg_lock); ++ ++ if (is_sdw) ++ concur_block_mask = sof_hda->link_dma_active_multi_mask[!stream_dir]; ++ else if (is_multi) ++ concur_block_mask = sof_hda->link_dma_active_sdw_mask[!stream_dir]; ++ if (is_play && !is_hda) ++ seq_block_mask = sof_hda->link_dma_out_hda_used_mask; ++ + list_for_each_entry(hstream, &bus->stream_list, list) { + struct hdac_ext_stream *hext_stream = + stream_to_hdac_ext_stream(hstream); +@@ -69,6 +99,12 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream + sdev = hda_stream->sdev; + chip = get_chip_info(sdev->pdata); + ++ stream_idx = hstream->stream_tag - 1; ++ ++ /* skip streams blocked by the ACE2+ allocator constraints */ ++ if ((concur_block_mask | seq_block_mask) & BIT(stream_idx)) ++ continue; ++ + /* check if link is available */ + if (!hext_stream->link_locked) { + /* +@@ -95,7 +131,7 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream + + /* + * This must be a hostless stream. +- * So reserve the host DMA channel. ++ * So reserve the host DMA stream. + */ + hda_stream->host_reserved = 1; + break; +@@ -109,6 +145,16 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream + + res->link_locked = 1; + res->link_substream = substream; ++ ++ stream_idx = res->hstream.stream_tag - 1; ++ if (is_sdw) ++ sof_hda->link_dma_active_sdw_mask[stream_dir] |= BIT(stream_idx); ++ else if (is_multi) ++ sof_hda->link_dma_active_multi_mask[stream_dir] |= BIT(stream_idx); ++ ++ /* persistent OUT HDA/iDisp shadow, cleared only on CRST# */ ++ if (is_hda && is_play) ++ sof_hda->link_dma_out_hda_used_mask |= BIT(stream_idx); + } + + return res; +@@ -143,11 +189,13 @@ static struct hdac_ext_stream *hda_ipc4_get_hext_stream(struct snd_sof_dev *sdev + + static struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev, + struct snd_soc_dai *cpu_dai, +- struct snd_pcm_substream *substream) ++ struct snd_pcm_substream *substream, ++ struct hdac_ext_link *hlink) + { + struct hdac_ext_stream *hext_stream; ++ enum hda_bus_ml_link_type link_type = hda_bus_ml_link_get_type(hlink); + +- hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream); ++ hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream, link_type); + if (!hext_stream) + return NULL; + +@@ -160,6 +208,22 @@ static void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai + struct snd_pcm_substream *substream) + { + struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream); ++ struct sof_intel_hda_dev *sof_hda = sdev->pdata->hw_pdata; ++ struct hdac_bus *bus = sof_to_bus(sdev); ++ int dir = substream->stream; ++ unsigned int stream_idx = hext_stream->hstream.stream_tag - 1; ++ ++ /* ++ * Drop the stream index from the per-direction active concurrency masks. ++ * The two masks are mutually exclusive for a given stream/direction ++ * (and a stream of the SSP/DMIC kind appears in neither), so a blind ++ * clear of both is safe and lets us avoid having to remember the ++ * link type at allocation time. ++ */ ++ scoped_guard(spinlock_irq, &bus->reg_lock) { ++ sof_hda->link_dma_active_sdw_mask[dir] &= ~BIT(stream_idx); ++ sof_hda->link_dma_active_multi_mask[dir] &= ~BIT(stream_idx); ++ } + + snd_soc_dai_set_dma_data(cpu_dai, substream, NULL); + snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK); +diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c +--- a/sound/soc/sof/intel/hda-dai.c ++++ b/sound/soc/sof/intel/hda-dai.c +@@ -188,7 +188,7 @@ static int hda_link_dma_hw_params(struct snd_pcm_substream *substream, + + if (!hext_stream) { + if (ops->assign_hext_stream) +- hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream); ++ hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream, hlink); + } + + if (!hext_stream) +diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c +--- a/sound/soc/sof/intel/hda-dsp.c ++++ b/sound/soc/sof/intel/hda-dsp.c +@@ -1114,11 +1114,7 @@ static int hda_dsp_s5_quirk(struct snd_sof_dev *sdev) + usleep_range(500, 1000); + + /* Restore state for shutdown, back to reset */ +- ret = hda_dsp_ctrl_link_reset(sdev, true); +- if (ret < 0) +- return ret; +- +- return ret; ++ return hda_dsp_ctrl_link_reset(sdev, true); + } + + int hda_dsp_shutdown_dma_flush(struct snd_sof_dev *sdev) +diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c +--- a/sound/soc/sof/intel/hda-loader.c ++++ b/sound/soc/sof/intel/hda-loader.c +@@ -216,9 +216,10 @@ int hda_cl_trigger(struct device *dev, struct hdac_ext_stream *hext_stream, int + EXPORT_SYMBOL_NS(hda_cl_trigger, "SND_SOC_SOF_INTEL_HDA_COMMON"); + + int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab, +- bool persistent_buffer, struct hdac_ext_stream *hext_stream) ++ bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool is_iccmax) + { +- return hda_data_stream_cleanup(dev, dmab, persistent_buffer, hext_stream, false); ++ return hda_data_stream_cleanup(dev, dmab, persistent_buffer, hext_stream, ++ is_iccmax, false); + } + EXPORT_SYMBOL_NS(hda_cl_cleanup, "SND_SOC_SOF_INTEL_HDA_COMMON"); + +@@ -302,7 +303,7 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev) + * If the cleanup also fails, we return the initial error + */ + ret1 = hda_cl_cleanup(sdev->dev, &hda->iccmax_dmab, +- persistent_cl_buffer, iccmax_stream); ++ persistent_cl_buffer, iccmax_stream, true); + if (ret1 < 0) { + dev_err(sdev->dev, "error: ICCMAX stream cleanup failed\n"); + +@@ -458,7 +459,7 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) + * If the cleanup also fails, we return the initial error + */ + ret1 = hda_cl_cleanup(sdev->dev, &hda->cl_dmab, +- persistent_cl_buffer, hext_stream); ++ persistent_cl_buffer, hext_stream, false); + if (ret1 < 0) { + dev_err(sdev->dev, "error: Code loader DSP cleanup failed\n"); + +@@ -587,7 +588,7 @@ int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev, + cleanup: + /* clean up even in case of error and return the first error */ + ret1 = hda_cl_cleanup(sdev->dev, &hda->cl_dmab, persistent_cl_buffer, +- hext_stream); ++ hext_stream, false); + if (ret1 < 0) { + dev_err(sdev->dev, "%s: Code loader DSP cleanup failed\n", __func__); + +diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c +--- a/sound/soc/sof/intel/hda-mlink.c ++++ b/sound/soc/sof/intel/hda-mlink.c +@@ -432,6 +432,10 @@ int hda_bus_ml_init(struct hdac_bus *bus) + if (!bus->mlcap) + return 0; + ++ /* Enumeration is a one time operation, skip if already done */ ++ if (!list_empty(&bus->hlink_list)) ++ return 0; ++ + link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1; + + dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count); +@@ -880,19 +884,6 @@ int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, + return 0; + } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_map_stream_ch, "SND_SOC_SOF_HDA_MLINK"); + +-void hda_bus_ml_put_all(struct hdac_bus *bus) +-{ +- struct hdac_ext_link *hlink; +- +- list_for_each_entry(hlink, &bus->hlink_list, list) { +- struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); +- +- if (!h2link->alt) +- snd_hdac_ext_bus_link_put(bus, hlink); +- } +-} +-EXPORT_SYMBOL_NS(hda_bus_ml_put_all, "SND_SOC_SOF_HDA_MLINK"); +- + void hda_bus_ml_reset_losidv(struct hdac_bus *bus) + { + struct hdac_ext_link *hlink; +@@ -903,6 +894,24 @@ void hda_bus_ml_reset_losidv(struct hdac_bus *bus) + } + EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, "SND_SOC_SOF_HDA_MLINK"); + ++enum hda_bus_ml_link_type hda_bus_ml_link_get_type(struct hdac_ext_link *hlink) ++{ ++ struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); ++ ++ if (!h2link->alt) ++ return HDA_BUS_ML_LINK_HDA; ++ ++ switch (h2link->elid) { ++ case AZX_REG_ML_LEPTR_ID_SDW: ++ return HDA_BUS_ML_LINK_SDW; ++ case AZX_REG_ML_LEPTR_ID_INTEL_UAOL: ++ return HDA_BUS_ML_LINK_UAOL; ++ default: ++ return HDA_BUS_ML_LINK_OTHER; ++ } ++} ++EXPORT_SYMBOL_NS(hda_bus_ml_link_get_type, "SND_SOC_SOF_HDA_MLINK"); ++ + int hda_bus_ml_resume(struct hdac_bus *bus) + { + struct hdac_ext_link *hlink; +diff --git a/sound/soc/sof/intel/hda-sdw-bpt.c b/sound/soc/sof/intel/hda-sdw-bpt.c +--- a/sound/soc/sof/intel/hda-sdw-bpt.c ++++ b/sound/soc/sof/intel/hda-sdw-bpt.c +@@ -163,7 +163,7 @@ static int hda_sdw_bpt_dma_deprepare(struct device *dev, struct hdac_ext_stream + u32 mask; + int ret; + +- ret = hda_data_stream_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream, true); ++ ret = hda_data_stream_cleanup(sdev->dev, dmab_bdl, false, sdw_bpt_stream, false, true); + if (ret < 0) { + dev_err(sdev->dev, "%s: SDW BPT DMA cleanup failed\n", + __func__); +diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c +--- a/sound/soc/sof/intel/hda-stream.c ++++ b/sound/soc/sof/intel/hda-stream.c +@@ -198,13 +198,18 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, + + mask = (1 << hstream->index); + ++ /* Reset the spib_addr before disabling SPIB */ ++ if (!enable) ++ sof_io_write(sdev, hstream->spib_addr, 0); ++ + /* enable/disable SPIB for the stream */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_SPIB_BAR, + SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCCTL, mask, + enable << hstream->index); + + /* set the SPIB value */ +- sof_io_write(sdev, hstream->spib_addr, size); ++ if (enable) ++ sof_io_write(sdev, hstream->spib_addr, size); + + return 0; + } +@@ -608,6 +613,9 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, + return ret; + } + ++ /* Host DMA is not running */ ++ hstream->running = false; ++ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + sd_offset + SOF_HDA_ADSP_REG_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK, +@@ -1323,16 +1331,18 @@ hda_data_stream_prepare(struct device *dev, unsigned int format, unsigned int si + EXPORT_SYMBOL_NS(hda_data_stream_prepare, "SND_SOC_SOF_INTEL_HDA_COMMON"); + + int hda_data_stream_cleanup(struct device *dev, struct snd_dma_buffer *dmab, +- bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool pair) ++ bool persistent_buffer, struct hdac_ext_stream *hext_stream, ++ bool is_iccmax, bool pair) + { + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + struct hdac_stream *hstream = hdac_stream(hext_stream); + int sd_offset = SOF_STREAM_SD_OFFSET(hstream); + int ret = 0; + +- if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK) ++ if (!is_iccmax) + ret = hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0); +- else ++ ++ if (hstream->direction == SNDRV_PCM_STREAM_CAPTURE) + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, + SOF_HDA_SD_CTL_DMA_START, 0); + +diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c +--- a/sound/soc/sof/intel/hda.c ++++ b/sound/soc/sof/intel/hda.c +@@ -421,6 +421,20 @@ static inline void hda_dsp_sdw_process_mic_privacy(struct snd_sof_dev *sdev) { } + /* pre fw run operations */ + int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev) + { ++ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; ++ const struct sof_intel_dsp_desc *chip = hda->desc; ++ int ret; ++ ++ /* Power down DSP if left enabled to ensure a clean boot state. */ ++ if (hda_dsp_core_is_enabled(sdev, chip->host_managed_cores_mask)) { ++ dev_dbg(sdev->dev, "DSP core enabled, power down DSP first\n"); ++ ++ ret = chip->power_down_dsp(sdev); ++ if (ret < 0) ++ dev_warn(sdev->dev, ++ "%s: failed to power down already-enabled DSP\n", __func__); ++ } ++ + /* disable clock gating and power gating */ + return hda_dsp_ctrl_clock_power_gating(sdev, false); + } +@@ -625,8 +639,6 @@ static int hda_init_caps(struct snd_sof_dev *sdev) + return ret; + } + +- hda_bus_ml_init(bus); +- + /* Skip SoundWire if it is not supported */ + if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH))) + goto skip_soundwire; +@@ -670,8 +682,6 @@ static int hda_init_caps(struct snd_sof_dev *sdev) + if (!HDA_IDISP_CODEC(bus->codec_mask)) + hda_codec_i915_display_power(sdev, false); + +- hda_bus_ml_put_all(bus); +- + return 0; + } + +diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h +--- a/sound/soc/sof/intel/hda.h ++++ b/sound/soc/sof/intel/hda.h +@@ -523,6 +523,29 @@ struct sof_intel_hda_dev { + /* the maximum number of streams (playback + capture) supported */ + u32 stream_max; + ++ /* ++ * ACE2+ link DMA stream allocation constraints (stream index = ++ * stream_tag - 1, shared between input and output directions). All ++ * masks are cleared by hda_dsp_ctrl_init_chip() on controller reset ++ * (CRST#). ++ * ++ * - Concurrent (cross-direction) constraint: a SoundWire stream and ++ * a HDA/iDisp/UAOL stream cannot share a physical stream index ++ * across directions, the resulting LLP/timestamp values are wrong. ++ * link_dma_active_sdw_mask and link_dma_active_multi_mask ++ * (indexed by SNDRV_PCM_STREAM_*) track currently allocated ++ * streams per direction in each of the conflicting groups; SSP ++ * and DMIC do not participate. Bits are cleared on stream release. ++ * ++ * - Sequential (playback only) constraint: once a HDA/iDisp link ++ * has used a playback stream index, that index cannot drive a ++ * non-HDA/iDisp link in the same direction until the next CRST#. ++ * link_dma_out_hda_used_mask records this. ++ */ ++ u32 link_dma_active_sdw_mask[SNDRV_PCM_STREAM_LAST + 1]; ++ u32 link_dma_active_multi_mask[SNDRV_PCM_STREAM_LAST + 1]; ++ u32 link_dma_out_hda_used_mask; ++ + /* PM related */ + bool l1_disabled;/* is DMI link L1 disabled? */ + +@@ -739,7 +762,7 @@ struct hdac_ext_stream *hda_cl_prepare(struct device *dev, unsigned int format, + int hda_cl_trigger(struct device *dev, struct hdac_ext_stream *hext_stream, int cmd); + + int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab, +- bool persistent_buffer, struct hdac_ext_stream *hext_stream); ++ bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool is_iccmax); + int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot); + #define HDA_CL_STREAM_FORMAT 0x40 + +@@ -911,7 +934,8 @@ hda_data_stream_prepare(struct device *dev, unsigned int format, unsigned int si + bool is_iccmax, bool pair); + + int hda_data_stream_cleanup(struct device *dev, struct snd_dma_buffer *dmab, +- bool persistent_buffer, struct hdac_ext_stream *hext_stream, bool pair); ++ bool persistent_buffer, struct hdac_ext_stream *hext_stream, ++ bool is_iccmax, bool pair); + + /* common dai driver */ + extern struct snd_soc_dai_driver skl_dai[]; +@@ -1030,7 +1054,8 @@ struct hda_dai_widget_dma_ops { + struct snd_pcm_substream *substream); + struct hdac_ext_stream *(*assign_hext_stream)(struct snd_sof_dev *sdev, + struct snd_soc_dai *cpu_dai, +- struct snd_pcm_substream *substream); ++ struct snd_pcm_substream *substream, ++ struct hdac_ext_link *hlink); + void (*release_hext_stream)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, + struct snd_pcm_substream *substream); + void (*setup_hext_stream)(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream, +diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c +--- a/sound/soc/sof/intel/icl.c ++++ b/sound/soc/sof/intel/icl.c +@@ -9,10 +9,14 @@ + * Hardware interface for audio DSP on IceLake. + */ + +-#include +-#include +-#include ++#include + #include ++#include ++#include ++#include ++#include ++#include ++ + #include "../ipc4-priv.h" + #include "../ops.h" + #include "hda.h" +diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c +--- a/sound/soc/sof/intel/mtl.c ++++ b/sound/soc/sof/intel/mtl.c +@@ -236,6 +236,17 @@ int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable) + } + EXPORT_SYMBOL_NS(mtl_enable_interrupts, "SND_SOC_SOF_INTEL_MTL"); + ++static bool mtl_dsp_is_enabled(struct snd_sof_dev *sdev) ++{ ++ int val; ++ ++ val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_HFDSSCS); ++ if (val & MTL_HFDSSCS_CPA_MASK) ++ return true; ++ ++ return false; ++} ++ + /* pre fw run operations */ + static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev) + { +@@ -249,6 +260,18 @@ static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev) + u32 dsppwrsts; + const struct sof_intel_dsp_desc *chip; + ++ /* Power down the DSP if it is left enabled to ensure clean boot state */ ++ if (mtl_dsp_is_enabled(sdev)) { ++ dev_dbg(sdev->dev, "powering down DSP first\n"); ++ ++ ret = mtl_power_down_dsp(sdev); ++ if (ret < 0) { ++ dev_warn(sdev->dev, ++ "%s: failed to power down already-enabled DSP\n", __func__); ++ /* Continue anyway to attempt recovery */ ++ } ++ } ++ + chip = get_chip_info(sdev->pdata); + if (chip->hw_ip_version > SOF_INTEL_ACE_2_0) { + dsppwrctl = PTL_HFPWRCTL2; +diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c +--- a/sound/soc/sof/ipc3-pcm.c ++++ b/sound/soc/sof/ipc3-pcm.c +@@ -421,6 +421,12 @@ static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, + dev_dbg(component->dev, "AMD_SDW channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; ++ case SOF_DAI_AMD_I2S: ++ rate->min = private->dai_config->acp_i2s.fsync_rate; ++ rate->max = private->dai_config->acp_i2s.fsync_rate; ++ channels->min = private->dai_config->acp_i2s.tdm_slots; ++ channels->max = private->dai_config->acp_i2s.tdm_slots; ++ break; + default: + dev_err(component->dev, "Invalid DAI type %d\n", private->dai_config->type); + break; +diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c +--- a/sound/soc/sof/ipc3-topology.c ++++ b/sound/soc/sof/ipc3-topology.c +@@ -281,7 +281,10 @@ static const struct sof_topology_token acpdmic_tokens[] = { + offsetof(struct sof_ipc_dai_acpdmic_params, pdm_ch)}, + }; + +-/* ACPI2S */ ++/* ++ * ACPI2S tokens fill struct sof_ipc_dai_acp_params; SOF_DAI_AMD_I2S (ACPTDM ++ * on ACP7.B/7.F) reuses this tuple group rather than defining a parallel set. ++ */ + static const struct sof_topology_token acpi2s_tokens[] = { + {SOF_TKN_AMD_ACPI2S_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_acp_params, fsync_rate)}, +@@ -289,6 +292,8 @@ static const struct sof_topology_token acpi2s_tokens[] = { + offsetof(struct sof_ipc_dai_acp_params, tdm_slots)}, + {SOF_TKN_AMD_ACPI2S_TDM_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_acp_params, tdm_mode)}, ++ {SOF_TKN_AMD_ACPI2S_FORMAT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, ++ offsetof(struct sof_ipc_dai_acp_params, format)}, + }; + + /* MICFIL PDM */ +@@ -1363,6 +1368,35 @@ static int sof_link_acp_sdw_load(struct snd_soc_component *scomp, struct snd_sof + return 0; + } + ++static int sof_link_acp_i2s_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, ++ struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) ++{ ++ struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; ++ struct sof_dai_private_data *private = dai->private; ++ u32 size = sizeof(*config); ++ int ret; ++ ++ /* handle master/slave and inverted clocks */ ++ sof_dai_set_format(hw_config, config); ++ ++ /* init IPC */ ++ memset(&config->acp_i2s, 0, sizeof(config->acp_i2s)); ++ config->hdr.size = size; ++ ++ ret = sof_update_ipc_object(scomp, &config->acp_i2s, SOF_ACPI2S_TOKENS, slink->tuples, ++ slink->num_tuples, size, slink->num_hw_configs); ++ if (ret < 0) ++ return ret; ++ ++ dai->number_configs = 1; ++ dai->current_config = 0; ++ private->dai_config = kmemdup(config, size, GFP_KERNEL); ++ if (!private->dai_config) ++ return -ENOMEM; ++ ++ return 0; ++} ++ + static int sof_link_afe_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, + struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) + { +@@ -1692,6 +1726,9 @@ static int sof_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget) + case SOF_DAI_AMD_SDW: + ret = sof_link_acp_sdw_load(scomp, slink, config, dai); + break; ++ case SOF_DAI_AMD_I2S: ++ ret = sof_link_acp_i2s_load(scomp, slink, config, dai); ++ break; + default: + break; + } +diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c +--- a/sound/soc/sof/ipc4-topology.c ++++ b/sound/soc/sof/ipc4-topology.c +@@ -163,11 +163,11 @@ static const struct sof_topology_token comp_ext_tokens[] = { + {SOF_TKN_COMP_SCHED_DOMAIN, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_domain, + offsetof(struct snd_sof_widget, comp_domain)}, + {SOF_TKN_COMP_DOMAIN_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, +- offsetof(struct snd_sof_widget, dp_domain_id)}, ++ offsetof(struct snd_sof_widget, domain_id)}, + {SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, +- offsetof(struct snd_sof_widget, dp_heap_bytes)}, ++ offsetof(struct snd_sof_widget, heap_bytes)}, + {SOF_TKN_COMP_STACK_BYTES_REQUIREMENT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, +- offsetof(struct snd_sof_widget, dp_stack_bytes)}, ++ offsetof(struct snd_sof_widget, stack_bytes)}, + }; + + static const struct sof_topology_token gain_tokens[] = { +@@ -406,6 +406,39 @@ sof_ipc4_get_input_pin_audio_fmt(struct snd_sof_widget *swidget, int pin_index) + return NULL; + } + ++static void ++sof_ipc4_evaluate_params_change(struct sof_ipc4_available_audio_format *available_fmt) ++{ ++ struct sof_ipc4_audio_format *fmt; ++ u32 in_rate, in_channels, in_valid_bits; ++ u32 out_rate, out_channels, out_valid_bits; ++ u32 changed_params = 0; ++ int i, j; ++ ++ for (i = 0; i < available_fmt->num_input_formats; i++) { ++ fmt = &available_fmt->input_pin_fmts[i].audio_fmt; ++ in_rate = fmt->sampling_frequency; ++ in_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg); ++ in_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); ++ ++ for (j = 0; j < available_fmt->num_output_formats; j++) { ++ fmt = &available_fmt->output_pin_fmts[j].audio_fmt; ++ out_rate = fmt->sampling_frequency; ++ out_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg); ++ out_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); ++ ++ if (in_rate != out_rate) ++ changed_params |= BIT(SNDRV_PCM_HW_PARAM_RATE); ++ if (in_channels != out_channels) ++ changed_params |= BIT(SNDRV_PCM_HW_PARAM_CHANNELS); ++ if (in_valid_bits != out_valid_bits) ++ changed_params |= BIT(SNDRV_PCM_HW_PARAM_FORMAT); ++ } ++ } ++ ++ available_fmt->changed_params = changed_params; ++} ++ + /** + * sof_ipc4_get_audio_fmt - get available audio formats from swidget->tuples + * @scomp: pointer to pointer to SOC component +@@ -497,6 +530,8 @@ static int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp, + available_fmt->num_output_formats); + } + ++ sof_ipc4_evaluate_params_change(available_fmt); ++ + return 0; + + err_out: +@@ -661,6 +696,9 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget) + if (ret) + goto free_copier; + ++ /* Copier can only change format */ ++ available_fmt->changed_params &= BIT(SNDRV_PCM_HW_PARAM_FORMAT); ++ + /* + * This callback is used by host copier and module-to-module copier, + * and only host copier needs to set gtw_cfg. +@@ -789,6 +827,9 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) + if (ret) + goto free_copier; + ++ /* Copier can only change format */ ++ available_fmt->changed_params &= BIT(SNDRV_PCM_HW_PARAM_FORMAT); ++ + ret = sof_update_ipc_object(scomp, &node_type, + SOF_COPIER_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(node_type), 1); +@@ -1371,6 +1412,22 @@ sof_ipc4_update_resource_usage(struct snd_sof_dev *sdev, struct snd_sof_widget * + pipeline = pipe_widget->private; + pipeline->mem_usage += total; + ++ /* ++ * If this is not a Data Processing module instance, add the ++ * required heap sizes to the sum of all module instances belonging ++ * to the same pipeline, and find the maximum stack requirement ++ * among all module instances belonging to the same pipeline. ++ */ ++ if (swidget->comp_domain != SOF_COMP_DOMAIN_DP) { ++ pipe_widget->heap_bytes += swidget->heap_bytes; ++ if (pipe_widget->stack_bytes < swidget->stack_bytes) ++ pipe_widget->stack_bytes = swidget->stack_bytes; ++ ++ dev_dbg(sdev->dev, "%s mem reqs to %s heap %u stack %u", ++ swidget->widget->name, pipe_widget->widget->name, ++ pipe_widget->heap_bytes, pipe_widget->stack_bytes); ++ } ++ + /* Update base_config->cpc from the module manifest */ + sof_ipc4_update_cpc_from_manifest(sdev, fw_module, base_config); + +@@ -1688,6 +1745,8 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget) + pipe_widget = swidget->spipe->pipe_widget; + pipeline = pipe_widget->private; + pipeline->mem_usage = 0; ++ pipe_widget->heap_bytes = 0; ++ pipe_widget->stack_bytes = 0; + + if (WIDGET_IS_AIF(swidget->id) || swidget->id == snd_soc_dapm_buffer) { + if (pipeline->use_chain_dma) { +@@ -2065,10 +2124,57 @@ static void sof_ipc4_host_config(struct snd_sof_dev *sdev, struct snd_sof_widget + } + + static int +-sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, +- struct snd_pcm_hw_params *fe_params, +- struct snd_sof_platform_stream_params *platform_params, +- struct snd_pcm_hw_params *pipeline_params, int dir) ++sof_ipc4_copier_module_update_params(struct snd_sof_widget *swidget, ++ struct snd_pcm_hw_params *pipeline_params) ++{ ++ struct snd_soc_component *scomp = swidget->scomp; ++ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); ++ struct sof_ipc4_copier_data *copier_data; ++ struct sof_ipc4_copier *ipc4_copier; ++ ++ switch (swidget->id) { ++ case snd_soc_dapm_aif_in: ++ case snd_soc_dapm_aif_out: ++ case snd_soc_dapm_buffer: ++ ipc4_copier = swidget->private; ++ copier_data = &ipc4_copier->data; ++ break; ++ case snd_soc_dapm_dai_in: ++ case snd_soc_dapm_dai_out: ++ { ++ struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; ++ struct sof_ipc4_pipeline *pipeline = pipe_widget->private; ++ struct snd_sof_dai *dai; ++ ++ if (pipeline->use_chain_dma) ++ return 0; ++ ++ dai = swidget->private; ++ ++ ipc4_copier = (struct sof_ipc4_copier *)dai->private; ++ copier_data = &ipc4_copier->data; ++ ++ break; ++ } ++ default: ++ dev_err(sdev->dev, "unsupported type %d for copier %s", ++ swidget->id, swidget->widget->name); ++ return -EINVAL; ++ } ++ ++ /* modify the input params for the next widget */ ++ return sof_ipc4_update_hw_params(sdev, pipeline_params, ++ &copier_data->out_format, ++ BIT(SNDRV_PCM_HW_PARAM_FORMAT) | ++ BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | ++ BIT(SNDRV_PCM_HW_PARAM_RATE)); ++} ++ ++static int ++_sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, ++ struct snd_pcm_hw_params *fe_params, ++ struct snd_sof_platform_stream_params *platform_params, ++ struct snd_pcm_hw_params *pipeline_params, int dir) + { + struct sof_ipc4_available_audio_format *available_fmt; + struct snd_soc_component *scomp = swidget->scomp; +@@ -2540,6 +2646,21 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, + return 0; + } + ++static int ++sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, ++ struct snd_pcm_hw_params *fe_params, ++ struct snd_sof_platform_stream_params *platform_params, ++ struct snd_pcm_hw_params *pipeline_params, int dir) ++{ ++ if (swidget->prepared) ++ return sof_ipc4_copier_module_update_params(swidget, ++ pipeline_params); ++ ++ return _sof_ipc4_prepare_copier_module(swidget, fe_params, ++ platform_params, pipeline_params, ++ dir); ++} ++ + static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, + struct snd_pcm_hw_params *fe_params, + struct snd_sof_platform_stream_params *platform_params, +@@ -2553,6 +2674,10 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, + u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type; + int input_fmt_index, output_fmt_index; + ++ /* This cannot happen */ ++ if (unlikely(swidget->prepared)) ++ return 0; ++ + input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, + &gain->data.base_config, + pipeline_params, +@@ -2598,6 +2723,10 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget, + u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type; + int input_fmt_index, output_fmt_index; + ++ /* Already prepared, nothing to do */ ++ if (swidget->prepared) ++ return 0; ++ + input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, + &mixer->base_config, + pipeline_params, +@@ -2644,6 +2773,10 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, + u32 out_ref_rate, out_ref_channels, out_ref_valid_bits, out_ref_type; + int output_fmt_index, input_fmt_index; + ++ /* This cannot happen */ ++ if (unlikely(swidget->prepared)) ++ return 0; ++ + input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, + &src->data.base_config, + pipeline_params, +@@ -2810,6 +2943,18 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, + int ret; + + if (available_fmt->num_input_formats) { ++ if (swidget->prepared) { ++ if (!available_fmt->num_output_formats) ++ return 0; ++ ++ /* modify the pipeline params with the output format */ ++ return sof_ipc4_update_hw_params(sdev, pipeline_params, ++ &process->output_format, ++ BIT(SNDRV_PCM_HW_PARAM_FORMAT) | ++ BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | ++ BIT(SNDRV_PCM_HW_PARAM_RATE)); ++ } ++ + input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, + &process->base_config, + pipeline_params, +@@ -2822,39 +2967,69 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, + if (available_fmt->num_output_formats) { + struct sof_ipc4_audio_format *in_fmt; + struct sof_ipc4_pin_format *pin_fmt; +- u32 out_ref_rate, out_ref_channels; +- int out_ref_valid_bits, out_ref_type; ++ u32 ref_rate, ref_channels; ++ int ref_valid_bits, ref_type; + + if (available_fmt->num_input_formats) { ++ /* ++ * The process module can change parameters and their operation ++ * depends on the direction: ++ * Playback: typically they have single output format. This is ++ * to 'force' the conversion from input to output. ++ * Use the input format as reference since the single ++ * format is going to be picked. ++ * Capture: typically they have multiple output formats to ++ * convert from dai (input) to FE (output) parameters. ++ * Use the input format as base and replace the param ++ * which is changed by the module with the FE parameter ++ * Reason: we can have module which changes the ++ * parameters in path, we cannot use the full ++ * FE param set for the module output lookup. ++ */ + in_fmt = &available_fmt->input_pin_fmts[input_fmt_index].audio_fmt; + +- out_ref_rate = in_fmt->sampling_frequency; +- out_ref_channels = ++ ref_rate = in_fmt->sampling_frequency; ++ ref_channels = + SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg); +- out_ref_valid_bits = ++ ref_valid_bits = + SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); +- out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg); ++ ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg); + } else { + /* for modules without input formats, use FE params as reference */ +- out_ref_rate = params_rate(fe_params); +- out_ref_channels = params_channels(fe_params); ++ ref_rate = params_rate(fe_params); ++ ref_channels = params_channels(fe_params); + ret = sof_ipc4_get_sample_type(sdev, fe_params); + if (ret < 0) + return ret; +- out_ref_type = (u32)ret; ++ ref_type = (u32)ret; + +- out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); +- if (out_ref_valid_bits < 0) +- return out_ref_valid_bits; ++ ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); ++ if (ref_valid_bits < 0) ++ return ref_valid_bits; + } + ++ if (dir == SNDRV_PCM_STREAM_CAPTURE) { ++ if (available_fmt->changed_params & BIT(SNDRV_PCM_HW_PARAM_RATE)) ++ ref_rate = params_rate(fe_params); ++ if (available_fmt->changed_params & BIT(SNDRV_PCM_HW_PARAM_CHANNELS)) ++ ref_channels = params_channels(fe_params); ++ if (available_fmt->changed_params & BIT(SNDRV_PCM_HW_PARAM_FORMAT)) { ++ ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); ++ if (ref_valid_bits < 0) ++ return ref_valid_bits; ++ ++ ref_type = sof_ipc4_get_sample_type(sdev, fe_params); ++ if (ref_type < 0) ++ return ref_type; ++ } ++ } + output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget, + &process->base_config, + available_fmt, +- out_ref_rate, +- out_ref_channels, +- out_ref_valid_bits, +- out_ref_type); ++ ref_rate, ++ ref_channels, ++ ref_valid_bits, ++ ref_type); + if (output_fmt_index < 0) + return output_fmt_index; + +@@ -2868,9 +3043,7 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, + /* modify the pipeline params with the output format */ + ret = sof_ipc4_update_hw_params(sdev, pipeline_params, + &process->output_format, +- BIT(SNDRV_PCM_HW_PARAM_FORMAT) | +- BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | +- BIT(SNDRV_PCM_HW_PARAM_RATE)); ++ available_fmt->changed_params); + if (ret) + return ret; + } +@@ -3085,27 +3258,47 @@ static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr + return 0; + } + +-static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev, +- struct snd_sof_widget *swidget, +- struct sof_ipc4_msg *msg, +- void *ipc_data, u32 ipc_size, +- void **new_data) ++static void sof_ipc4_add_init_ext_dp_memory_data(struct snd_sof_dev *sdev, ++ struct snd_sof_widget *swidget, ++ u32 *payload, u32 *ext_pos, ++ struct sof_ipc4_module_init_ext_object **hdr) ++{ ++ /* Add memory_data if comp_domain indicates DP */ ++ if (swidget->comp_domain == SOF_COMP_DOMAIN_DP) { ++ struct sof_ipc4_mod_init_ext_dp_memory_data *dp_mem_data; ++ ++ *hdr = (struct sof_ipc4_module_init_ext_object *)&payload[*ext_pos]; ++ (*hdr)->header = ++ SOF_IPC4_MOD_INIT_EXT_OBJ_ID(SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA) | ++ SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS(DIV_ROUND_UP(sizeof(*dp_mem_data), ++ sizeof(u32))); ++ *ext_pos += DIV_ROUND_UP(sizeof(**hdr), sizeof(u32)); ++ dp_mem_data = (struct sof_ipc4_mod_init_ext_dp_memory_data *)&payload[*ext_pos]; ++ dp_mem_data->domain_id = swidget->domain_id; ++ dp_mem_data->stack_bytes = swidget->stack_bytes; ++ dp_mem_data->heap_bytes = swidget->heap_bytes; ++ *ext_pos += DIV_ROUND_UP(sizeof(*dp_mem_data), sizeof(u32)); ++ } ++} ++ ++static int sof_ipc4_widget_mod_init_msg_payload(struct snd_sof_dev *sdev, ++ struct snd_sof_widget *swidget, ++ struct sof_ipc4_msg *msg, ++ void *ipc_data, u32 ipc_size, ++ void **new_data) + { +- struct sof_ipc4_mod_init_ext_dp_memory_data *dp_mem_data; + struct sof_ipc4_module_init_ext_init *ext_init; +- struct sof_ipc4_module_init_ext_object *hdr; ++ struct sof_ipc4_module_init_ext_object *hdr = NULL; + int new_size; + u32 *payload; + u32 ext_pos; + +- /* For the moment the only reason for adding init_ext_init payload is DP +- * memory data. If both stack and heap size are 0 (= use default), then +- * there is no need for init_ext_init payload. ++ /* ++ * Only DP widgets currently add init-ext objects here. Avoid allocating ++ * a max-sized payload buffer for widgets that will immediately return 0. + */ +- if (swidget->comp_domain != SOF_COMP_DOMAIN_DP) { +- msg->extension &= ~SOF_IPC4_MOD_EXT_EXTENDED_INIT_MASK; ++ if (swidget->comp_domain != SOF_COMP_DOMAIN_DP) + return 0; +- } + + payload = kzalloc(sdev->ipc->max_payload_size, GFP_KERNEL); + if (!payload) +@@ -3113,27 +3306,26 @@ static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev, + + /* Add ext_init first and set objects array flag to 1 */ + ext_init = (struct sof_ipc4_module_init_ext_init *)payload; +- ext_init->word0 |= SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_MASK; + ext_pos = DIV_ROUND_UP(sizeof(*ext_init), sizeof(u32)); + + /* Add object array objects after ext_init */ + +- /* Add dp_memory_data if comp_domain indicates DP */ +- if (swidget->comp_domain == SOF_COMP_DOMAIN_DP) { +- hdr = (struct sof_ipc4_module_init_ext_object *)&payload[ext_pos]; +- hdr->header = SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_MASK | +- SOF_IPC4_MOD_INIT_EXT_OBJ_ID(SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA) | +- SOF_IPC4_MOD_INIT_EXT_OBJ_WORDS(DIV_ROUND_UP(sizeof(*dp_mem_data), +- sizeof(u32))); +- ext_pos += DIV_ROUND_UP(sizeof(*hdr), sizeof(u32)); +- dp_mem_data = (struct sof_ipc4_mod_init_ext_dp_memory_data *)&payload[ext_pos]; +- dp_mem_data->domain_id = swidget->dp_domain_id; +- dp_mem_data->stack_bytes = swidget->dp_stack_bytes; +- dp_mem_data->heap_bytes = swidget->dp_heap_bytes; +- ext_pos += DIV_ROUND_UP(sizeof(*dp_mem_data), sizeof(u32)); ++ sof_ipc4_add_init_ext_dp_memory_data(sdev, swidget, payload, &ext_pos, &hdr); ++ ++ /* Add following object array items here */ ++ ++ if (!hdr) { ++ /* ++ * NOTE: Remove this early bail out, when struct ++ * sof_ipc4_module_init_ext_init alone has some ++ * function. ++ */ ++ kfree(payload); ++ return 0; + } + +- /* If another array object is added, remember clear previous OBJ_LAST bit */ ++ ext_init->word0 |= SOF_IPC4_MOD_INIT_EXT_OBJ_ARRAY_MASK; ++ hdr->header |= SOF_IPC4_MOD_INIT_EXT_OBJ_LAST_MASK; + + /* Calculate final size and check that it fits to max payload size */ + new_size = ext_pos * sizeof(u32) + ipc_size; +@@ -3156,6 +3348,69 @@ static int sof_ipc4_widget_setup_msg_payload(struct snd_sof_dev *sdev, + return new_size; + } + ++static void sof_ipc4_widget_pipe_ext_obj_memory_data(struct snd_sof_dev *sdev, ++ struct snd_sof_widget *swidget, ++ u32 *payload, u32 *ext_pos, ++ struct sof_ipc4_glb_pipe_ext_object **hdr) ++{ ++ struct sof_ipc4_glb_pipe_ext_obj_memory_data *mem_data; ++ ++ *hdr = (struct sof_ipc4_glb_pipe_ext_object *)&payload[*ext_pos]; ++ (*hdr)->header = ++ SOF_IPC4_GLB_PIPE_EXT_OBJ_ID(SOF_IPC4_GLB_PIPE_DATA_ID_MEM_DATA) | ++ SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS(DIV_ROUND_UP(sizeof(*mem_data), ++ sizeof(u32))); ++ *ext_pos += DIV_ROUND_UP(sizeof(**hdr), sizeof(u32)); ++ mem_data = (struct sof_ipc4_glb_pipe_ext_obj_memory_data *)&payload[*ext_pos]; ++ mem_data->domain_id = swidget->domain_id; ++ mem_data->stack_bytes = swidget->stack_bytes; ++ mem_data->heap_bytes = swidget->heap_bytes; ++ *ext_pos += DIV_ROUND_UP(sizeof(*mem_data), sizeof(u32)); ++ ++ dev_dbg(sdev->dev, ++ "%s; domain_id %u stack %u heap %u bytes", ++ swidget->widget->name, mem_data->domain_id, mem_data->stack_bytes, ++ mem_data->heap_bytes); ++} ++ ++static int sof_ipc4_widget_pipe_create_msg_payload(struct snd_sof_dev *sdev, ++ struct snd_sof_widget *swidget, ++ struct sof_ipc4_msg *msg, ++ void **new_data) ++{ ++ struct sof_ipc4_glb_pipe_payload *payload_hdr; ++ struct sof_ipc4_glb_pipe_ext_object *hdr = NULL; ++ u32 *payload; ++ u32 ext_pos; ++ ++ payload = kzalloc(sdev->ipc->max_payload_size, GFP_KERNEL); ++ if (!payload) ++ return -ENOMEM; ++ ++ /* Add sof_ipc4_glb_pipe_payload and set array bit to 1 */ ++ payload_hdr = (struct sof_ipc4_glb_pipe_payload *)payload; ++ payload_hdr->word0 |= SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_MASK; ++ ext_pos = DIV_ROUND_UP(sizeof(*payload_hdr), sizeof(u32)); ++ ++ sof_ipc4_widget_pipe_ext_obj_memory_data(sdev, swidget, payload, &ext_pos, &hdr); ++ /* Add following array objects here */ ++ ++ /* Mark end of object array */ ++ hdr->header |= SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_MASK; ++ ++ /* Put total payload size in words to the payload header */ ++ payload_hdr->word0 |= SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS(ext_pos); ++ *new_data = payload; ++ ++ /* Update msg extension bits according to the payload changes */ ++ msg->extension |= SOF_IPC4_GLB_PIPE_PAYLOAD_MASK; ++ ++ dev_dbg(sdev->dev, "%s: payload word0 %#x", swidget->widget->name, ++ payload_hdr->word0); ++ ++ return ext_pos * sizeof(int32_t); ++} ++ + static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) + { + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; +@@ -3342,8 +3597,8 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget + swidget->widget->name, swidget->pipeline_id, module_id, + swidget->instance_id, swidget->core); + +- ret = sof_ipc4_widget_setup_msg_payload(sdev, swidget, msg, ipc_data, ipc_size, +- &ext_data); ++ ret = sof_ipc4_widget_mod_init_msg_payload(sdev, swidget, msg, ipc_data, ipc_size, ++ &ext_data); + if (ret < 0) + goto fail; + +@@ -3355,6 +3610,17 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget + dev_dbg(sdev->dev, "Create pipeline %s (pipe %d) - instance %d, core %d\n", + swidget->widget->name, swidget->pipeline_id, + swidget->instance_id, swidget->core); ++ ++ msg->extension &= ~SOF_IPC4_GLB_PIPE_PAYLOAD_MASK; ++ ret = sof_ipc4_widget_pipe_create_msg_payload(sdev, swidget, msg, ++ &ext_data); ++ if (ret < 0) ++ goto fail; ++ ++ if (ret > 0) { ++ ipc_size = ret; ++ ipc_data = ext_data; ++ } + } + + msg->data_size = ipc_size; +@@ -3412,6 +3678,8 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget + swidget->widget->name); + + pipeline->mem_usage = 0; ++ swidget->heap_bytes = 0; ++ swidget->stack_bytes = 0; + pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED; + ida_free(&pipeline_ida, swidget->instance_id); + swidget->instance_id = -EINVAL; +diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h +--- a/sound/soc/sof/ipc4-topology.h ++++ b/sound/soc/sof/ipc4-topology.h +@@ -167,7 +167,7 @@ struct sof_ipc4_pipeline { + }; + + /** +- * struct sof_ipc4_multi_pipeline_data - multi pipeline trigger IPC data ++ * struct ipc4_pipeline_set_state_data - multi pipeline trigger IPC data + * @count: Number of pipelines to be triggered + * @pipeline_instance_ids: Flexible array of IDs of the pipelines to be triggered + */ +@@ -197,12 +197,15 @@ struct sof_ipc4_pin_format { + * @input_pin_fmts: Available input pin formats + * @num_input_formats: Number of input pin formats + * @num_output_formats: Number of output pin formats ++ * @changed_params: Mask of changed params by the module instance between it's ++ * input and output formts (rate, channels, depth) + */ + struct sof_ipc4_available_audio_format { + struct sof_ipc4_pin_format *output_pin_fmts; + struct sof_ipc4_pin_format *input_pin_fmts; + u32 num_input_formats; + u32 num_output_formats; ++ u32 changed_params; + }; + + /** +@@ -210,7 +213,7 @@ struct sof_ipc4_available_audio_format { + * @node_id: ID of Gateway Node + * @dma_buffer_size: Preferred Gateway DMA buffer size (in bytes) + * @config_length: Length of gateway node configuration blob specified in #config_data +- * config_data: Gateway node configuration blob ++ * @config_data: Gateway node configuration blob + */ + struct sof_copier_gateway_cfg { + uint32_t node_id; +@@ -285,7 +288,9 @@ struct sof_ipc4_dma_stream_ch_map { + struct sof_ipc4_dma_config { + uint8_t dma_method; + uint8_t pre_allocated_by_host; ++ /* private: */ + uint16_t rsvd; ++ /* public: */ + uint32_t dma_channel_id; + uint32_t stream_id; + struct sof_ipc4_dma_stream_ch_map dma_stream_channel_map; +@@ -296,7 +301,7 @@ struct sof_ipc4_dma_config { + #define SOF_IPC4_GTW_DMA_CONFIG_ID 0x1000 + + /** +- * struct sof_ipc4_dma_config: DMA configuration ++ * struct sof_ipc4_dma_config_tlv - DMA configuration + * @type: set to SOF_IPC4_GTW_DMA_CONFIG_ID + * @length: sizeof(struct sof_ipc4_dma_config) + dma_config.dma_priv_config_size + * @dma_config: actual DMA configuration +@@ -321,6 +326,7 @@ struct sof_ipc4_alh_configuration_blob { + * @data: IPC copier data + * @copier_config: Copier + blob + * @ipc_config_size: Size of copier_config ++ * @ipc_config_data: Copier module config data + * @available_fmt: Available audio format + * @frame_fmt: frame format + * @msg: message structure for copier +diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c +--- a/sound/soc/sof/ipc4.c ++++ b/sound/soc/sof/ipc4.c +@@ -289,6 +289,89 @@ static void sof_ipc4_dump_payload(struct snd_sof_dev *sdev, + 16, 4, ipc_data, size, false); + } + ++static const char *sof_ipc4_resource_type_str(u32 type) ++{ ++ switch (type) { ++ case SOF_IPC4_MODULE_INSTANCE: ++ return "resource: MODULE_INSTANCE"; ++ case SOF_IPC4_PIPELINE: ++ return "resource: PIPELINE"; ++ case SOF_IPC4_GATEWAY: ++ return "resource: GATEWAY"; ++ case SOF_IPC4_EDF_TASK: ++ return "resource: EDF_TASK"; ++ case SOF_IPC4_INVALID_RESOURCE_TYPE: ++ return "Resource is invalid"; ++ default: ++ return "Unknown resource type"; ++ } ++} ++ ++static const char *sof_ipc4_resource_event_type_str(u32 event_type) ++{ ++ switch (event_type) { ++ case SOF_IPC4_MIXER_UNDERRUN_DETECTED: ++ return "event: MIXER_UNDERRUN_DETECTED"; ++ case SOF_IPC4_PROCESS_DATA_ERROR: ++ return "event: PROCESS_DATA_ERROR"; ++ case SOF_IPC4_GATEWAY_UNDERRUN_DETECTED: ++ return "event: GATEWAY_UNDERRUN_DETECTED"; ++ case SOF_IPC4_GATEWAY_OVERRUN_DETECTED: ++ return "event: GATEWAY_OVERRUN_DETECTED"; ++ default: ++ return "Unknown event type"; ++ } ++} ++ ++static void sof_ipc4_resource_event_handler(struct snd_sof_dev *sdev, ++ struct sof_ipc4_msg *ipc4_msg) ++{ ++ struct sof_ipc4_notify_resource_data *data = ipc4_msg->data_ptr; ++ ++ /* Print event details */ ++ switch (data->event_type) { ++ case SOF_IPC4_MIXER_UNDERRUN_DETECTED: ++ dev_dbg(sdev->dev, "%s (%u): eos %u, mixed %u, expected %u\n", ++ sof_ipc4_resource_event_type_str(data->event_type), ++ data->event_type, data->data.mixer_underrun.eos_flag, ++ data->data.mixer_underrun.data_mixed, ++ data->data.mixer_underrun.expected_data_mixed); ++ break; ++ case SOF_IPC4_PROCESS_DATA_ERROR: ++ dev_dbg(sdev->dev, "%s (%u): error_code %#x\n", ++ sof_ipc4_resource_event_type_str(data->event_type), ++ data->event_type, data->data.process_data_error.error_code); ++ break; ++ case SOF_IPC4_GATEWAY_UNDERRUN_DETECTED: ++ case SOF_IPC4_GATEWAY_OVERRUN_DETECTED: ++ dev_dbg(sdev->dev, "%s (%u)\n", ++ sof_ipc4_resource_event_type_str(data->event_type), ++ data->event_type); ++ break; ++ default: ++ dev_dbg(sdev->dev, "%s (%u): raw dws %#x %#x %#x %#x %#x %#x\n", ++ sof_ipc4_resource_event_type_str(data->event_type), ++ data->event_type, ++ data->data.dws[0], data->data.dws[1], data->data.dws[2], ++ data->data.dws[3], data->data.dws[4], data->data.dws[5]); ++ break; ++ } ++ ++ /* Print resource details */ ++ if (data->resource_type == SOF_IPC4_MODULE_INSTANCE) { ++ u32 module_id = SOF_IPC4_MOD_ID_GET(data->resource_id); ++ u32 instance_id = SOF_IPC4_MOD_INSTANCE_GET(data->resource_id); ++ ++ dev_dbg(sdev->dev, "%s (%u), module_id %u, instance_id %u\n", ++ sof_ipc4_resource_type_str(data->resource_type), ++ data->resource_type, module_id, instance_id); ++ } else if (data->resource_type != SOF_IPC4_INVALID_RESOURCE_TYPE) { ++ dev_dbg(sdev->dev, "%s (%u), id %u\n", ++ sof_ipc4_resource_type_str(data->resource_type), ++ data->resource_type, data->resource_id); ++ } ++} ++ + static int sof_ipc4_get_reply(struct snd_sof_dev *sdev) + { + struct snd_sof_ipc_msg *msg = sdev->msg; +@@ -734,6 +817,7 @@ static void sof_ipc4_rx_msg(struct snd_sof_dev *sdev) + break; + case SOF_IPC4_NOTIFY_RESOURCE_EVENT: + data_size = sizeof(struct sof_ipc4_notify_resource_data); ++ handler_func = sof_ipc4_resource_event_handler; + break; + case SOF_IPC4_NOTIFY_LOG_BUFFER_STATUS: + sof_ipc4_mtrace_update_pos(sdev, SOF_IPC4_LOG_CORE_GET(ipc4_msg->primary)); +diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c +--- a/sound/soc/sof/nocodec.c ++++ b/sound/soc/sof/nocodec.c +@@ -21,12 +21,13 @@ static struct snd_soc_card sof_nocodec_card = { + static int sof_nocodec_bes_setup(struct device *dev, + struct snd_soc_dai_driver *drv, + struct snd_soc_dai_link *links, +- int link_num, struct snd_soc_card *card) ++ int link_num) + { ++ struct snd_soc_card *card = &sof_nocodec_card; + struct snd_soc_dai_link_component *dlc; + int i; + +- if (!drv || !links || !card) ++ if (!drv || !links) + return -EINVAL; + + /* set up BE dai_links */ +@@ -67,10 +68,10 @@ static int sof_nocodec_bes_setup(struct device *dev, + return 0; + } + +-static int sof_nocodec_setup(struct device *dev, +- u32 num_dai_drivers, +- struct snd_soc_dai_driver *dai_drivers) ++static int sof_nocodec_setup(struct device *dev, struct snd_soc_acpi_mach *mach) + { ++ u32 num_dai_drivers = mach->mach_params.num_dai_drivers; ++ struct snd_soc_dai_driver *dai_drivers = mach->mach_params.dai_drivers; + struct snd_soc_dai_link *links; + + /* create dummy BE dai_links */ +@@ -78,7 +79,7 @@ static int sof_nocodec_setup(struct device *dev, + if (!links) + return -ENOMEM; + +- return sof_nocodec_bes_setup(dev, dai_drivers, links, num_dai_drivers, &sof_nocodec_card); ++ return sof_nocodec_bes_setup(dev, dai_drivers, links, num_dai_drivers); + } + + static int sof_nocodec_probe(struct platform_device *pdev) +@@ -92,8 +93,7 @@ static int sof_nocodec_probe(struct platform_device *pdev) + + snd_soc_card_set_topology_name(card, "sof"); + +- ret = sof_nocodec_setup(card->dev, mach->mach_params.num_dai_drivers, +- mach->mach_params.dai_drivers); ++ ret = sof_nocodec_setup(card->dev, mach); + if (ret < 0) + return ret; + +diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c +--- a/sound/soc/sof/pcm.c ++++ b/sound/soc/sof/pcm.c +@@ -62,7 +62,7 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream) + * To avoid sending IPC before the previous IPC is handled, we + * schedule delayed work here to call the snd_pcm_period_elapsed(). + */ +- schedule_work(&spcm->stream[substream->stream].period_elapsed_work); ++ queue_work(system_highpri_wq, &spcm->stream[substream->stream].period_elapsed_work); + } + EXPORT_SYMBOL(snd_sof_pcm_period_elapsed); + +diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c +--- a/sound/soc/sof/sof-audio.c ++++ b/sound/soc/sof/sof-audio.c +@@ -501,9 +501,8 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget + !sof_widget_in_same_direction(swidget, dir)) + return 0; + +- /* skip widgets already prepared or aggregated DAI widgets*/ +- if (!widget_ops[widget->id].ipc_prepare || swidget->prepared || +- is_aggregated_dai(swidget)) ++ /* skip widgets aggregated DAI widgets */ ++ if (!widget_ops[widget->id].ipc_prepare || is_aggregated_dai(swidget)) + goto sink_prepare; + + /* prepare the source widget */ +diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h +--- a/sound/soc/sof/sof-audio.h ++++ b/sound/soc/sof/sof-audio.h +@@ -459,10 +459,10 @@ struct snd_sof_widget { + /* Scheduling domain (enum sof_comp_domain), unset, Low Latency, or Data Processing */ + u32 comp_domain; + +- /* The values below are added to mod_init pay load if comp_domain indicates DP component */ +- u32 dp_domain_id; /* DP process userspace domain ID */ +- u32 dp_stack_bytes; /* DP process stack size requirement in bytes */ +- u32 dp_heap_bytes; /* DP process heap size requirement in bytes */ ++ /* Module instance's memory configuration. */ ++ u32 domain_id; /* Module instance's userspace domain ID */ ++ u32 stack_bytes; /* Module instance's stack size requirement */ ++ u32 heap_bytes; /* Module instance's heap size requirement */ + + struct snd_soc_dapm_widget *widget; + struct list_head list; /* list in sdev widget list */ +diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c +--- a/sound/soc/sof/topology.c ++++ b/sound/soc/sof/topology.c +@@ -309,7 +309,7 @@ static const struct sof_dai_types sof_dais[] = { + {"ACPHS_VIRTUAL", SOF_DAI_AMD_HS_VIRTUAL}, + {"MICFIL", SOF_DAI_IMX_MICFIL}, + {"ACP_SDW", SOF_DAI_AMD_SDW}, +- ++ {"ACPTDM", SOF_DAI_AMD_I2S}, + }; + + static enum sof_ipc_dai_type find_dai(const char *name) +@@ -1108,7 +1108,7 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, + + full = NULL; + partial = NULL; +- list_for_each_entry(rtd, &card->rtd_list, list) { ++ for_each_card_rtds(card, rtd) { + /* does stream match DAI link ? */ + if (rtd->dai_link->stream_name) { + if (!strcmp(rtd->dai_link->stream_name, w->sname)) { +@@ -1173,7 +1173,7 @@ static void sof_disconnect_dai_widget(struct snd_soc_component *scomp, + else + return; + +- list_for_each_entry(rtd, &card->rtd_list, list) { ++ for_each_card_rtds(card, rtd) { + /* does stream match DAI link ? */ + if (!rtd->dai_link->stream_name || + !strstr(rtd->dai_link->stream_name, sname)) +@@ -1949,8 +1949,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ + private->array, le32_to_cpu(private->size)); + if (ret < 0) { + dev_err(scomp->dev, "Failed tp parse common DAI link tokens\n"); +- kfree(slink); +- return ret; ++ goto free_slink; + } + + token_list = tplg_ops ? tplg_ops->token_list : NULL; +@@ -2001,6 +2000,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ + case SOF_DAI_AMD_HS: + case SOF_DAI_AMD_SP_VIRTUAL: + case SOF_DAI_AMD_HS_VIRTUAL: ++ case SOF_DAI_AMD_I2S: + token_id = SOF_ACPI2S_TOKENS; + num_tuples += token_list[SOF_ACPI2S_TOKENS].count; + break; +@@ -2019,8 +2019,8 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ + /* allocate memory for tuples array */ + slink->tuples = kzalloc_objs(*slink->tuples, num_tuples); + if (!slink->tuples) { +- kfree(slink); +- return -ENOMEM; ++ ret = -ENOMEM; ++ goto free_slink; + } + + if (token_list[SOF_DAI_LINK_TOKENS].tokens) { +@@ -2076,6 +2076,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ + + err: + kfree(slink->tuples); ++free_slink: + kfree(slink); + + return ret; +@@ -2511,13 +2512,12 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_pdata *sof_pdata = sdev->pdata; + const char *tplg_filename_prefix = sof_pdata->tplg_filename_prefix; +- const struct firmware *fw; +- const char **tplg_files; + int tplg_cnt = 0; + int ret; + int i; + +- tplg_files = kcalloc(scomp->card->num_links, sizeof(char *), GFP_KERNEL); ++ const char **tplg_files __free(kfree) = ++ kcalloc(scomp->card->num_links, sizeof(char *), GFP_KERNEL); + if (!tplg_files) + return -ENOMEM; + +@@ -2543,10 +2543,8 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) + tplg_filename_prefix, + &tplg_files, + no_fallback); +- if (tplg_cnt < 0) { +- kfree(tplg_files); ++ if (tplg_cnt < 0) + return tplg_cnt; +- } + } + + /* +@@ -2557,8 +2555,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) + if (strstr(file, "dummy")) { + dev_err(scomp->dev, + "Function topology is required, please upgrade sof-firmware\n"); +- +- kfree(tplg_files); + return -EINVAL; + } + tplg_files[0] = file; +@@ -2573,6 +2569,7 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) + if (tplg_files[0] != file) + dev_info(scomp->dev, "loading topology %d: %s\n", i, tplg_files[i]); + ++ const struct firmware *fw __free(firmware) = NULL; + ret = request_firmware(&fw, tplg_files[i], scomp->dev); + if (ret < 0) { + /* +@@ -2591,8 +2588,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) + else + ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw); + +- release_firmware(fw); +- + if (ret < 0) { + dev_err(scomp->dev, "tplg %s component load failed %d\n", + tplg_files[i], ret); +@@ -2611,6 +2606,8 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) + goto out; + } + dev_info(scomp->dev, "loading feature topology %d: %s\n", i, feature_topology); ++ ++ const struct firmware *fw __free(firmware) = NULL; + ret = request_firmware(&fw, feature_topology, scomp->dev); + if (ret < 0) { + /* +@@ -2635,8 +2632,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) + else + ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw); + +- release_firmware(fw); +- + if (ret < 0) { + dev_err(scomp->dev, "feature tplg %s component load failed %d\n", + feature_topologies[i], ret); +@@ -2655,8 +2650,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) + if (ret >= 0 && sdev->led_present) + ret = snd_ctl_led_request(); + +- kfree(tplg_files); +- + return ret; + } + EXPORT_SYMBOL(snd_sof_load_topology); +diff --git a/sound/soc/sophgo/cv1800b-tdm.c b/sound/soc/sophgo/cv1800b-tdm.c +--- a/sound/soc/sophgo/cv1800b-tdm.c ++++ b/sound/soc/sophgo/cv1800b-tdm.c +@@ -558,7 +558,7 @@ static const struct snd_soc_dai_ops cv1800b_i2s_dai_ops = { + .set_sysclk = cv1800b_i2s_dai_set_sysclk, + }; + +-static const struct snd_soc_dai_driver cv1800b_i2s_dai_template = { ++static struct snd_soc_dai_driver cv1800b_i2s_dai_template = { + .name = "cv1800b-i2s", + .playback = { + .stream_name = "Playback", +@@ -636,7 +636,6 @@ static int cv1800b_i2s_probe(struct platform_device *pdev) + struct cv1800b_i2s *i2s; + struct resource *res; + void __iomem *regs; +- struct snd_soc_dai_driver *dai; + int ret; + + i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL); +@@ -666,21 +665,14 @@ static int cv1800b_i2s_probe(struct platform_device *pdev) + platform_set_drvdata(pdev, i2s); + cv1800b_i2s_setup_tdm(i2s); + +- dai = devm_kmemdup(dev, &cv1800b_i2s_dai_template, sizeof(*dai), +- GFP_KERNEL); +- if (!dai) +- return -ENOMEM; +- +- ret = devm_snd_soc_register_component(dev, &cv1800b_i2s_component, dai, +- 1); ++ ret = devm_snd_soc_register_component(dev, &cv1800b_i2s_component, ++ &cv1800b_i2s_dai_template, 1); + if (ret) + return ret; + + ret = devm_snd_dmaengine_pcm_register(dev, &cv1800b_i2s_pcm_config, 0); +- if (ret) { +- dev_err(dev, "dmaengine_pcm_register failed: %d\n", ret); ++ if (ret) + return ret; +- } + + return 0; + } +diff --git a/sound/soc/spacemit/k1_i2s.c b/sound/soc/spacemit/k1_i2s.c +--- a/sound/soc/spacemit/k1_i2s.c ++++ b/sound/soc/spacemit/k1_i2s.c +@@ -335,6 +335,11 @@ static int spacemit_i2s_dai_remove(struct snd_soc_dai *dai) + return 0; + } + ++static const u64 spacemit_i2s_selectable_formats = ++ SND_SOC_POSSIBLE_DAIFMT_I2S | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_A | ++ SND_SOC_POSSIBLE_DAIFMT_DSP_B; ++ + static const struct snd_soc_dai_ops spacemit_i2s_dai_ops = { + .probe = spacemit_i2s_dai_probe, + .remove = spacemit_i2s_dai_remove, +@@ -343,6 +348,8 @@ static const struct snd_soc_dai_ops spacemit_i2s_dai_ops = { + .set_sysclk = spacemit_i2s_set_sysclk, + .set_fmt = spacemit_i2s_set_fmt, + .trigger = spacemit_i2s_trigger, ++ .auto_selectable_formats = &spacemit_i2s_selectable_formats, ++ .num_auto_selectable_formats = 1, + }; + + static struct snd_soc_dai_driver spacemit_i2s_dai = { +@@ -459,7 +466,7 @@ static int spacemit_i2s_probe(struct platform_device *pdev) + + i2s->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(i2s->base)) +- return dev_err_probe(i2s->dev, PTR_ERR(i2s->base), "failed to map registers\n"); ++ return PTR_ERR(i2s->base); + + i2s->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(i2s->reset)) +@@ -476,7 +483,7 @@ static int spacemit_i2s_probe(struct platform_device *pdev) + &spacemit_i2s_component, + dai, 1); + if (ret) +- return dev_err_probe(i2s->dev, ret, "failed to register component"); ++ return ret; + + return devm_snd_dmaengine_pcm_register(&pdev->dev, &spacemit_dmaengine_pcm_config, 0); + } +diff --git a/sound/soc/spear/spdif_in.c b/sound/soc/spear/spdif_in.c +--- a/sound/soc/spear/spdif_in.c ++++ b/sound/soc/spear/spdif_in.c +@@ -55,7 +55,7 @@ static int spdif_in_dai_probe(struct snd_soc_dai *dai) + struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai); + + host->dma_params_rx.filter_data = &host->dma_params; +- dai->capture_dma_data = &host->dma_params_rx; ++ snd_soc_dai_dma_data_set_capture(dai, &host->dma_params_rx); + + return 0; + } +@@ -150,12 +150,12 @@ static int spdif_in_trigger(struct snd_pcm_substream *substream, int cmd, + + static const struct snd_soc_dai_ops spdif_in_dai_ops = { + .shutdown = spdif_in_shutdown, ++ .probe = spdif_in_dai_probe, + .trigger = spdif_in_trigger, + .hw_params = spdif_in_hw_params, + }; + + static struct snd_soc_dai_driver spdif_in_dai = { +- .probe = spdif_in_dai_probe, + .capture = { + .channels_min = 2, + .channels_max = 2, +@@ -218,10 +218,8 @@ static int spdif_in_probe(struct platform_device *pdev) + + host->io_base = io_base; + host->irq = platform_get_irq(pdev, 0); +- if (host->irq < 0) { +- dev_warn(&pdev->dev, "failed to get IRQ: %d\n", host->irq); ++ if (host->irq < 0) + return host->irq; +- } + + host->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(host->clk)) +@@ -243,10 +241,8 @@ static int spdif_in_probe(struct platform_device *pdev) + + ret = devm_request_irq(&pdev->dev, host->irq, spdif_in_irq, 0, + "spdif-in", host); +- if (ret) { +- dev_warn(&pdev->dev, "request_irq failed\n"); ++ if (ret) + return ret; +- } + + ret = devm_snd_soc_register_component(&pdev->dev, &spdif_in_component, + &spdif_in_dai, 1); +diff --git a/sound/soc/spear/spdif_out.c b/sound/soc/spear/spdif_out.c +--- a/sound/soc/spear/spdif_out.c ++++ b/sound/soc/spear/spdif_out.c +@@ -249,6 +249,7 @@ static int spdif_soc_dai_probe(struct snd_soc_dai *dai) + } + + static const struct snd_soc_dai_ops spdif_out_dai_ops = { ++ .probe = spdif_soc_dai_probe, + .mute_stream = spdif_mute, + .startup = spdif_out_startup, + .shutdown = spdif_out_shutdown, +@@ -266,7 +267,6 @@ static struct snd_soc_dai_driver spdif_out_dai = { + SNDRV_PCM_RATE_192000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +- .probe = spdif_soc_dai_probe, + .ops = &spdif_out_dai_ops, + }; + +diff --git a/sound/soc/sprd/sprd-mcdt.c b/sound/soc/sprd/sprd-mcdt.c +--- a/sound/soc/sprd/sprd-mcdt.c ++++ b/sound/soc/sprd/sprd-mcdt.c +@@ -931,10 +931,8 @@ static int sprd_mcdt_probe(struct platform_device *pdev) + + ret = devm_request_irq(&pdev->dev, irq, sprd_mcdt_irq_handler, + 0, "sprd-mcdt", mcdt); +- if (ret) { +- dev_err(&pdev->dev, "Failed to request MCDT IRQ\n"); ++ if (ret) + return ret; +- } + + sprd_mcdt_init_chans(mcdt, res); + +diff --git a/sound/soc/sprd/sprd-pcm-dma.c b/sound/soc/sprd/sprd-pcm-dma.c +--- a/sound/soc/sprd/sprd-pcm-dma.c ++++ b/sound/soc/sprd/sprd-pcm-dma.c +@@ -469,8 +469,6 @@ static int sprd_soc_platform_probe(struct platform_device *pdev) + + ret = devm_snd_soc_register_component(&pdev->dev, &sprd_soc_component, + NULL, 0); +- if (ret) +- dev_err(&pdev->dev, "could not register platform:%d\n", ret); + + return ret; + } +diff --git a/sound/soc/starfive/jh7110_pwmdac.c b/sound/soc/starfive/jh7110_pwmdac.c +--- a/sound/soc/starfive/jh7110_pwmdac.c ++++ b/sound/soc/starfive/jh7110_pwmdac.c +@@ -477,25 +477,22 @@ static int jh7110_pwmdac_probe(struct platform_device *pdev) + &jh7110_pwmdac_component, + &jh7110_pwmdac_dai, 1); + if (ret) +- return dev_err_probe(&pdev->dev, ret, "failed to register dai\n"); ++ return ret; + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) +- return dev_err_probe(&pdev->dev, ret, "failed to register pcm\n"); ++ return ret; + + pm_runtime_enable(dev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = jh7110_pwmdac_runtime_resume(&pdev->dev); +- if (ret) +- goto err_pm_disable; ++ if (ret) { ++ pm_runtime_disable(&pdev->dev); ++ return ret; ++ } + } + + return 0; +- +-err_pm_disable: +- pm_runtime_disable(&pdev->dev); +- +- return ret; + } + + static void jh7110_pwmdac_remove(struct platform_device *pdev) +diff --git a/sound/soc/starfive/jh7110_tdm.c b/sound/soc/starfive/jh7110_tdm.c +--- a/sound/soc/starfive/jh7110_tdm.c ++++ b/sound/soc/starfive/jh7110_tdm.c +@@ -559,10 +559,8 @@ static int jh7110_tdm_clk_reset_get(struct platform_device *pdev, + tdm->clks[5].id = "tdm"; + + ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(tdm->clks), tdm->clks); +- if (ret) { +- dev_err(&pdev->dev, "Failed to get tdm clocks\n"); ++ if (ret) + return ret; +- } + + tdm->resets = devm_reset_control_array_get_exclusive(&pdev->dev); + if (IS_ERR(tdm->resets)) { +@@ -589,42 +587,33 @@ static int jh7110_tdm_probe(struct platform_device *pdev) + tdm->dev = &pdev->dev; + + ret = jh7110_tdm_clk_reset_get(pdev, tdm); +- if (ret) { +- dev_err(&pdev->dev, "Failed to enable audio-tdm clock\n"); ++ if (ret) + return ret; +- } + + jh7110_tdm_init_params(tdm); + + dev_set_drvdata(&pdev->dev, tdm); + ret = devm_snd_soc_register_component(&pdev->dev, &jh7110_tdm_component, + &jh7110_tdm_dai, 1); +- if (ret) { +- dev_err(&pdev->dev, "Failed to register dai\n"); ++ if (ret) + return ret; +- } + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, + &jh7110_dmaengine_pcm_config, + SND_DMAENGINE_PCM_FLAG_COMPAT); +- if (ret) { +- dev_err(&pdev->dev, "Could not register pcm: %d\n", ret); ++ if (ret) + return ret; +- } + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = jh7110_tdm_runtime_resume(&pdev->dev); +- if (ret) +- goto err_pm_disable; ++ if (ret) { ++ pm_runtime_disable(&pdev->dev); ++ return ret; ++ } + } + + return 0; +- +-err_pm_disable: +- pm_runtime_disable(&pdev->dev); +- +- return ret; + } + + static void jh7110_tdm_dev_remove(struct platform_device *pdev) +diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c +--- a/sound/soc/stm/stm32_adfsdm.c ++++ b/sound/soc/stm/stm32_adfsdm.c +@@ -360,11 +360,8 @@ static int stm32_adfsdm_probe(struct platform_device *pdev) + ret = devm_snd_soc_register_component(&pdev->dev, + &stm32_adfsdm_soc_platform, + NULL, 0); +- if (ret < 0) { +- dev_err(&pdev->dev, "%s: Failed to register PCM platform\n", +- __func__); ++ if (ret < 0) + return ret; +- } + + pm_runtime_enable(&pdev->dev); + +diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c +--- a/sound/soc/stm/stm32_i2s.c ++++ b/sound/soc/stm/stm32_i2s.c +@@ -1238,10 +1238,8 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev, + + ret = devm_request_irq(&pdev->dev, irq, stm32_i2s_isr, 0, + dev_name(&pdev->dev), i2s); +- if (ret) { +- dev_err(&pdev->dev, "irq request returned %d\n", ret); ++ if (ret) + return ret; +- } + + /* Reset */ + rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); +@@ -1295,7 +1293,7 @@ static int stm32_i2s_probe(struct platform_device *pdev) + + ret = snd_dmaengine_pcm_register(&pdev->dev, &stm32_i2s_pcm_config, 0); + if (ret) +- return dev_err_probe(&pdev->dev, ret, "PCM DMA register error\n"); ++ return ret; + + ret = snd_soc_register_component(&pdev->dev, &stm32_i2s_component, + i2s->dai_drv, 1); +diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c +--- a/sound/soc/stm/stm32_sai_sub.c ++++ b/sound/soc/stm/stm32_sai_sub.c +@@ -1696,19 +1696,15 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) + + ret = devm_request_irq(&pdev->dev, sai->pdata->irq, stm32_sai_isr, + IRQF_SHARED, dev_name(&pdev->dev), sai); +- if (ret) { +- dev_err(&pdev->dev, "IRQ request returned %d\n", ret); ++ if (ret) + goto err_unprepare_pclk; +- } + + if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) + conf = &stm32_sai_pcm_config_spdif; + + ret = snd_dmaengine_pcm_register(&pdev->dev, conf, 0); +- if (ret) { +- ret = dev_err_probe(&pdev->dev, ret, "Could not register pcm dma\n"); ++ if (ret) + goto err_unprepare_pclk; +- } + + ret = snd_soc_register_component(&pdev->dev, &stm32_component, + &sai->cpu_dai_drv, 1); +diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c +--- a/sound/soc/stm/stm32_spdifrx.c ++++ b/sound/soc/stm/stm32_spdifrx.c +@@ -970,10 +970,8 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) + + ret = devm_request_irq(&pdev->dev, spdifrx->irq, stm32_spdifrx_isr, 0, + dev_name(&pdev->dev), spdifrx); +- if (ret) { +- dev_err(&pdev->dev, "IRQ request returned %d\n", ret); ++ if (ret) + return ret; +- } + + rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); + if (IS_ERR(rst)) +@@ -987,7 +985,7 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) + pcm_config = &stm32_spdifrx_pcm_config; + ret = snd_dmaengine_pcm_register(&pdev->dev, pcm_config, 0); + if (ret) +- return dev_err_probe(&pdev->dev, ret, "PCM DMA register error\n"); ++ return ret; + + ret = snd_soc_register_component(&pdev->dev, + &stm32_spdifrx_component, +diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c +--- a/sound/soc/sunxi/sun4i-codec.c ++++ b/sound/soc/sunxi/sun4i-codec.c +@@ -22,13 +22,13 @@ + #include + + #include ++#include ++#include + #include + #include + #include + #include + #include +-#include +-#include + + /* Codec DAC digital controls and FIFO registers */ + #define SUN4I_CODEC_DAC_DPC (0x00) +@@ -2317,67 +2317,54 @@ static int sun4i_codec_probe(struct platform_device *pdev) + + scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base, + quirks->regmap_config); +- if (IS_ERR(scodec->regmap)) { +- dev_err(&pdev->dev, "Failed to create our regmap\n"); +- return PTR_ERR(scodec->regmap); +- } ++ if (IS_ERR(scodec->regmap)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->regmap), ++ "Failed to create our regmap\n"); + + /* Get the clocks from the DT */ + scodec->clk_apb = devm_clk_get_enabled(&pdev->dev, "apb"); +- if (IS_ERR(scodec->clk_apb)) { +- dev_err(&pdev->dev, "Failed to get the APB clock\n"); +- return PTR_ERR(scodec->clk_apb); +- } ++ if (IS_ERR(scodec->clk_apb)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->clk_apb), ++ "Failed to get the APB clock\n"); + + scodec->clk_module = devm_clk_get(&pdev->dev, "codec"); +- if (IS_ERR(scodec->clk_module)) { +- dev_err(&pdev->dev, "Failed to get the module clock\n"); +- return PTR_ERR(scodec->clk_module); +- } ++ if (IS_ERR(scodec->clk_module)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->clk_module), ++ "Failed to get the module clock\n"); + + if (quirks->has_reset) { + scodec->rst = devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL); +- if (IS_ERR(scodec->rst)) { +- dev_err(&pdev->dev, "Failed to get reset control\n"); +- return PTR_ERR(scodec->rst); +- } ++ if (IS_ERR(scodec->rst)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->rst), ++ "Failed to get reset control\n"); + } + + scodec->gpio_pa = devm_gpiod_get_optional(&pdev->dev, "allwinner,pa", + GPIOD_OUT_LOW); +- if (IS_ERR(scodec->gpio_pa)) { +- ret = PTR_ERR(scodec->gpio_pa); +- dev_err_probe(&pdev->dev, ret, "Failed to get pa gpio\n"); +- return ret; +- } ++ if (IS_ERR(scodec->gpio_pa)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->gpio_pa), ++ "Failed to get pa gpio\n"); ++ + + scodec->gpio_hp = devm_gpiod_get_optional(&pdev->dev, "hp-det", GPIOD_IN); +- if (IS_ERR(scodec->gpio_hp)) { +- ret = PTR_ERR(scodec->gpio_hp); +- dev_err_probe(&pdev->dev, ret, "Failed to get hp-det gpio\n"); +- return ret; +- } ++ if (IS_ERR(scodec->gpio_hp)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->gpio_hp), ++ "Failed to get hp-det gpio\n"); + + /* reg_field setup */ + scodec->reg_adc_fifoc = devm_regmap_field_alloc(&pdev->dev, + scodec->regmap, + quirks->reg_adc_fifoc); +- if (IS_ERR(scodec->reg_adc_fifoc)) { +- ret = PTR_ERR(scodec->reg_adc_fifoc); +- dev_err(&pdev->dev, "Failed to create regmap fields: %d\n", +- ret); +- return ret; +- } ++ if (IS_ERR(scodec->reg_adc_fifoc)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->reg_adc_fifoc), ++ "Failed to create regmap fields\n"); + + scodec->reg_dac_fifoc = devm_regmap_field_alloc(&pdev->dev, + scodec->regmap, + quirks->reg_dac_fifoc); +- if (IS_ERR(scodec->reg_dac_fifoc)) { +- ret = PTR_ERR(scodec->reg_dac_fifoc); +- dev_err(&pdev->dev, "Failed to create regmap fields: %d\n", +- ret); +- return ret; +- } ++ if (IS_ERR(scodec->reg_dac_fifoc)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(scodec->reg_dac_fifoc), ++ "Failed to create regmap fields\n"); + + /* DMA configuration for TX FIFO */ + scodec->playback_dma_data.addr = res->start + quirks->reg_dac_txdata; +@@ -2394,39 +2381,29 @@ static int sun4i_codec_probe(struct platform_device *pdev) + + ret = devm_snd_soc_register_component(&pdev->dev, quirks->codec, + &sun4i_codec_dai, 1); +- if (ret) { +- dev_err(&pdev->dev, "Failed to register our codec\n"); ++ if (ret) + return ret; +- } + + ret = devm_snd_soc_register_component(&pdev->dev, + &sun4i_codec_component, + &dummy_cpu_dai, 1); +- if (ret) { +- dev_err(&pdev->dev, "Failed to register our DAI\n"); ++ if (ret) + return ret; +- } + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); +- if (ret) { +- dev_err(&pdev->dev, "Failed to register against DMAEngine\n"); ++ if (ret) + return ret; +- } + + card = quirks->create_card(&pdev->dev); +- if (IS_ERR(card)) { +- ret = PTR_ERR(card); +- dev_err(&pdev->dev, "Failed to create our card\n"); +- return ret; +- } ++ if (IS_ERR(card)) ++ return PTR_ERR(card); + + snd_soc_card_set_drvdata(card, scodec); + + ret = snd_soc_register_card(card); +- if (ret) { +- dev_err_probe(&pdev->dev, ret, "Failed to register our card\n"); +- return ret; +- } ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, ++ "Failed to register our card\n"); + + return 0; + } +diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c +--- a/sound/soc/sunxi/sun4i-i2s.c ++++ b/sound/soc/sunxi/sun4i-i2s.c +@@ -1550,30 +1550,26 @@ static int sun4i_i2s_probe(struct platform_device *pdev) + } + + i2s->bus_clk = devm_clk_get(&pdev->dev, "apb"); +- if (IS_ERR(i2s->bus_clk)) { +- dev_err(&pdev->dev, "Can't get our bus clock\n"); +- return PTR_ERR(i2s->bus_clk); +- } ++ if (IS_ERR(i2s->bus_clk)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->bus_clk), ++ "Can't get our bus clock\n"); + + i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs, + i2s->variant->sun4i_i2s_regmap); +- if (IS_ERR(i2s->regmap)) { +- dev_err(&pdev->dev, "Regmap initialisation failed\n"); +- return PTR_ERR(i2s->regmap); +- } ++ if (IS_ERR(i2s->regmap)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->regmap), ++ "Regmap initialisation failed\n"); + + i2s->mod_clk = devm_clk_get(&pdev->dev, "mod"); +- if (IS_ERR(i2s->mod_clk)) { +- dev_err(&pdev->dev, "Can't get our mod clock\n"); +- return PTR_ERR(i2s->mod_clk); +- } ++ if (IS_ERR(i2s->mod_clk)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->mod_clk), ++ "Can't get our mod clock\n"); + + if (i2s->variant->has_reset) { + i2s->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); +- if (IS_ERR(i2s->rst)) { +- dev_err(&pdev->dev, "Failed to get reset control\n"); +- return PTR_ERR(i2s->rst); +- } ++ if (IS_ERR(i2s->rst)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->rst), ++ "Failed to get reset control\n"); + } + + if (!IS_ERR(i2s->rst)) { +diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c +--- a/sound/soc/sunxi/sun4i-spdif.c ++++ b/sound/soc/sunxi/sun4i-spdif.c +@@ -684,26 +684,23 @@ static int sun4i_spdif_probe(struct platform_device *pdev) + + host->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &sun4i_spdif_regmap_config); +- if (IS_ERR(host->regmap)) { +- dev_err(&pdev->dev, "failed to initialise regmap.\n"); +- return PTR_ERR(host->regmap); +- } ++ if (IS_ERR(host->regmap)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(host->regmap), ++ "failed to initialise regmap.\n"); + + /* Clocks */ + host->apb_clk = devm_clk_get(&pdev->dev, "apb"); +- if (IS_ERR(host->apb_clk)) { +- dev_err(&pdev->dev, "failed to get a apb clock.\n"); +- return PTR_ERR(host->apb_clk); +- } ++ if (IS_ERR(host->apb_clk)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(host->apb_clk), ++ "failed to get a apb clock.\n"); + + if (quirks->tx_clk_name) + tx_clk_name = quirks->tx_clk_name; + host->spdif_clk = devm_clk_get(&pdev->dev, tx_clk_name); +- if (IS_ERR(host->spdif_clk)) { +- dev_err(&pdev->dev, "failed to get the \"%s\" clock.\n", +- tx_clk_name); +- return PTR_ERR(host->spdif_clk); +- } ++ if (IS_ERR(host->spdif_clk)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(host->spdif_clk), ++ "failed to get the \"%s\" clock.\n", ++ tx_clk_name); + + host->dma_params_tx.addr = res->start + quirks->reg_dac_txdata; + host->dma_params_tx.maxburst = 8; +diff --git a/sound/soc/sunxi/sun50i-codec-analog.c b/sound/soc/sunxi/sun50i-codec-analog.c +--- a/sound/soc/sunxi/sun50i-codec-analog.c ++++ b/sound/soc/sunxi/sun50i-codec-analog.c +@@ -551,16 +551,13 @@ static int sun50i_codec_analog_probe(struct platform_device *pdev) + bool enable; + + base = devm_platform_ioremap_resource(pdev, 0); +- if (IS_ERR(base)) { +- dev_err(&pdev->dev, "Failed to map the registers\n"); ++ if (IS_ERR(base)) + return PTR_ERR(base); +- } + + regmap = sun8i_adda_pr_regmap_init(&pdev->dev, base); +- if (IS_ERR(regmap)) { +- dev_err(&pdev->dev, "Failed to create regmap\n"); +- return PTR_ERR(regmap); +- } ++ if (IS_ERR(regmap)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(regmap), ++ "Failed to create regmap\n"); + + enable = device_property_read_bool(&pdev->dev, + "allwinner,internal-bias-resistor"); +diff --git a/sound/soc/sunxi/sun8i-codec-analog.c b/sound/soc/sunxi/sun8i-codec-analog.c +--- a/sound/soc/sunxi/sun8i-codec-analog.c ++++ b/sound/soc/sunxi/sun8i-codec-analog.c +@@ -822,16 +822,13 @@ static int sun8i_codec_analog_probe(struct platform_device *pdev) + void __iomem *base; + + base = devm_platform_ioremap_resource(pdev, 0); +- if (IS_ERR(base)) { +- dev_err(&pdev->dev, "Failed to map the registers\n"); ++ if (IS_ERR(base)) + return PTR_ERR(base); +- } + + regmap = sun8i_adda_pr_regmap_init(&pdev->dev, base); +- if (IS_ERR(regmap)) { +- dev_err(&pdev->dev, "Failed to create regmap\n"); +- return PTR_ERR(regmap); +- } ++ if (IS_ERR(regmap)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(regmap), ++ "Failed to create regmap\n"); + + return devm_snd_soc_register_component(&pdev->dev, + &sun8i_codec_analog_cmpnt_drv, +diff --git a/sound/soc/tegra/tegra20_das.c b/sound/soc/tegra/tegra20_das.c +--- a/sound/soc/tegra/tegra20_das.c ++++ b/sound/soc/tegra/tegra20_das.c +@@ -167,10 +167,9 @@ static int tegra20_das_probe(struct platform_device *pdev) + + das->regmap = devm_regmap_init_mmio(&pdev->dev, regs, + &tegra20_das_regmap_config); +- if (IS_ERR(das->regmap)) { +- dev_err(&pdev->dev, "regmap init failed\n"); +- return PTR_ERR(das->regmap); +- } ++ if (IS_ERR(das->regmap)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(das->regmap), ++ "regmap init failed\n"); + + tegra20_das_connect_dap_to_dac(das, TEGRA20_DAS_DAP_ID_1, + TEGRA20_DAS_DAP_SEL_DAC1); +diff --git a/sound/soc/tegra/tegra210_adx.c b/sound/soc/tegra/tegra210_adx.c +--- a/sound/soc/tegra/tegra210_adx.c ++++ b/sound/soc/tegra/tegra210_adx.c +@@ -496,16 +496,12 @@ static struct snd_kcontrol_new tegra264_adx_controls[] = { + static int tegra210_adx_component_probe(struct snd_soc_component *component) + { + struct tegra210_adx *adx = snd_soc_component_get_drvdata(component); +- int err = 0; + +- if (adx->soc_data->num_controls) { +- err = snd_soc_add_component_controls(component, adx->soc_data->controls, +- adx->soc_data->num_controls); +- if (err) +- dev_err(component->dev, "can't add ADX controls, err: %d\n", err); +- } ++ if (adx->soc_data->num_controls) ++ return snd_soc_add_component_controls(component, adx->soc_data->controls, ++ adx->soc_data->num_controls); + +- return err; ++ return 0; + } + + static const struct snd_soc_component_driver tegra210_adx_cmpnt = { +diff --git a/sound/soc/tegra/tegra210_amx.c b/sound/soc/tegra/tegra210_amx.c +--- a/sound/soc/tegra/tegra210_amx.c ++++ b/sound/soc/tegra/tegra210_amx.c +@@ -504,16 +504,12 @@ static struct snd_kcontrol_new tegra264_amx_controls[] = { + static int tegra210_amx_component_probe(struct snd_soc_component *component) + { + struct tegra210_amx *amx = snd_soc_component_get_drvdata(component); +- int err = 0; + +- if (amx->soc_data->num_controls) { +- err = snd_soc_add_component_controls(component, amx->soc_data->controls, +- amx->soc_data->num_controls); +- if (err) +- dev_err(component->dev, "can't add AMX controls, err: %d\n", err); +- } ++ if (amx->soc_data->num_controls) ++ return snd_soc_add_component_controls(component, amx->soc_data->controls, ++ amx->soc_data->num_controls); + +- return err; ++ return 0; + } + + static const struct snd_soc_component_driver tegra210_amx_cmpnt = { +diff --git a/sound/soc/tegra/tegra210_i2s.c b/sound/soc/tegra/tegra210_i2s.c +--- a/sound/soc/tegra/tegra210_i2s.c ++++ b/sound/soc/tegra/tegra210_i2s.c +@@ -1090,13 +1090,13 @@ static int tegra210_i2s_probe(struct platform_device *pdev) + "can't retrieve I2S bit clock\n"); + + /* +- * Not an error, as this clock is needed only when some other I/O +- * requires input clock from current I2S instance, which is +- * configurable from DT. ++ * This clock is optional and is only needed when another I/O uses ++ * the current I2S instance as its input clock, as configured in DT. + */ +- i2s->clk_sync_input = devm_clk_get(dev, "sync_input"); ++ i2s->clk_sync_input = devm_clk_get_optional(dev, "sync_input"); + if (IS_ERR(i2s->clk_sync_input)) +- dev_dbg(dev, "can't retrieve I2S sync input clock\n"); ++ return dev_err_probe(dev, PTR_ERR(i2s->clk_sync_input), ++ "can't retrieve I2S sync input clock\n"); + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) +diff --git a/sound/soc/ti/ams-delta.c b/sound/soc/ti/ams-delta.c +--- a/sound/soc/ti/ams-delta.c ++++ b/sound/soc/ti/ams-delta.c +@@ -562,21 +562,21 @@ static int ams_delta_probe(struct platform_device *pdev) + + card->dev = &pdev->dev; + +- handset_mute = devm_gpiod_get(card->dev, "handset_mute", ++ handset_mute = devm_gpiod_get(&pdev->dev, "handset_mute", + GPIOD_OUT_HIGH); + if (IS_ERR(handset_mute)) + return PTR_ERR(handset_mute); + +- handsfree_mute = devm_gpiod_get(card->dev, "handsfree_mute", ++ handsfree_mute = devm_gpiod_get(&pdev->dev, "handsfree_mute", + GPIOD_OUT_HIGH); + if (IS_ERR(handsfree_mute)) + return PTR_ERR(handsfree_mute); + + ret = snd_soc_register_card(card); + if (ret) { +- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + card->dev = NULL; +- return ret; ++ return dev_err_probe(&pdev->dev, ret, ++ "snd_soc_register_card() failed\n"); + } + return 0; + } +diff --git a/sound/soc/ti/davinci-evm.c b/sound/soc/ti/davinci-evm.c +--- a/sound/soc/ti/davinci-evm.c ++++ b/sound/soc/ti/davinci-evm.c +@@ -245,9 +245,8 @@ static int davinci_evm_probe(struct platform_device *pdev) + + snd_soc_card_set_drvdata(&evm_soc_card, drvdata); + ret = devm_snd_soc_register_card(&pdev->dev, &evm_soc_card); +- + if (ret) { +- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); ++ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); + goto err_put; + } + +diff --git a/sound/soc/ti/j721e-evm.c b/sound/soc/ti/j721e-evm.c +--- a/sound/soc/ti/j721e-evm.c ++++ b/sound/soc/ti/j721e-evm.c +@@ -870,10 +870,9 @@ static int j721e_soc_probe(struct platform_device *pdev) + card->num_dapm_routes = ARRAY_SIZE(j721e_cpb_dapm_routes); + card->fully_routed = 1; + +- if (snd_soc_of_parse_card_name(card, "model")) { +- dev_err(&pdev->dev, "Card name is not provided\n"); +- return -ENODEV; +- } ++ ret = snd_soc_of_parse_card_name(card, "model"); ++ if (ret) ++ return ret; + + link_cnt = 0; + conf_cnt = 0; +diff --git a/sound/soc/ti/omap-abe-twl6040.c b/sound/soc/ti/omap-abe-twl6040.c +--- a/sound/soc/ti/omap-abe-twl6040.c ++++ b/sound/soc/ti/omap-abe-twl6040.c +@@ -234,16 +234,13 @@ static int omap_abe_probe(struct platform_device *pdev) + card->dapm_routes = audio_map; + card->num_dapm_routes = ARRAY_SIZE(audio_map); + +- if (snd_soc_of_parse_card_name(card, "ti,model")) { +- dev_err(&pdev->dev, "Card name is not provided\n"); +- return -ENODEV; +- } ++ ret = snd_soc_of_parse_card_name(card, "ti,model"); ++ if (ret) ++ return ret; + + ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing"); +- if (ret) { +- dev_err(&pdev->dev, "Error while parsing DAPM routing\n"); ++ if (ret) + return ret; +- } + + dai_node = of_parse_phandle(node, "ti,mcpdm", 0); + if (!dai_node) { +@@ -299,8 +296,8 @@ static int omap_abe_probe(struct platform_device *pdev) + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) +- dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n", +- ret); ++ dev_err_probe(&pdev->dev, ret, ++ "devm_snd_soc_register_card() failed\n"); + + return ret; + } +diff --git a/sound/soc/ti/omap-dmic.c b/sound/soc/ti/omap-dmic.c +--- a/sound/soc/ti/omap-dmic.c ++++ b/sound/soc/ti/omap-dmic.c +@@ -465,10 +465,9 @@ static int asoc_dmic_probe(struct platform_device *pdev) + mutex_init(&dmic->mutex); + + dmic->fclk = devm_clk_get(dmic->dev, "fck"); +- if (IS_ERR(dmic->fclk)) { +- dev_err(dmic->dev, "can't get fck\n"); +- return -ENODEV; +- } ++ if (IS_ERR(dmic->fclk)) ++ return dev_err_probe(dmic->dev, PTR_ERR(dmic->fclk), ++ "can't get fck\n"); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); + if (!res) { +diff --git a/sound/soc/ti/omap-hdmi.c b/sound/soc/ti/omap-hdmi.c +--- a/sound/soc/ti/omap-hdmi.c ++++ b/sound/soc/ti/omap-hdmi.c +@@ -24,8 +24,6 @@ + #define DRV_NAME "omap-hdmi-audio" + + struct hdmi_audio_data { +- struct snd_soc_card *card; +- + const struct omap_hdmi_audio_ops *ops; + struct device *dssdev; + struct snd_dmaengine_dai_dma_data dma_data; +@@ -375,12 +373,9 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev) + card->dev = dev; + + ret = devm_snd_soc_register_card(dev, card); +- if (ret) { +- dev_err(dev, "snd_soc_register_card failed (%d)\n", ret); +- return ret; +- } ++ if (ret) ++ return dev_err_probe(dev, ret, "snd_soc_register_card() failed\n"); + +- ad->card = card; + snd_soc_card_set_drvdata(card, ad); + + dev_set_drvdata(dev, ad); +diff --git a/sound/soc/ti/omap-twl4030.c b/sound/soc/ti/omap-twl4030.c +--- a/sound/soc/ti/omap-twl4030.c ++++ b/sound/soc/ti/omap-twl4030.c +@@ -17,7 +17,6 @@ + */ + + #include +-#include + #include + #include + +@@ -29,6 +28,7 @@ + #include "omap-mcbsp.h" + + struct omap_twl4030 { ++ struct snd_soc_jack_gpio hs_jack_gpio; + struct snd_soc_jack hs_jack; + }; + +@@ -124,38 +124,18 @@ static struct snd_soc_jack_pin hs_jack_pins[] = { + }, + }; + +-/* Headset jack detection gpios */ +-static struct snd_soc_jack_gpio hs_jack_gpios[] = { +- { +- .name = "ti,jack-det", +- .report = SND_JACK_HEADSET, +- .debounce_time = 200, +- }, +-}; +- +-static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm, +- int connected, char *pin) +-{ +- if (!connected) +- snd_soc_dapm_disable_pin(dapm, pin); +-} +- + static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd) + { + struct snd_soc_card *card = rtd->card; +- struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card); +- struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev); ++ + struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card); +- int ret = 0; ++ int ret; + + /* + * This is a bit of a hack, but the GPIO is optional so we + * only want to add the jack detection if the GPIO is there. + */ + if (of_property_present(card->dev->of_node, "ti,jack-det-gpio")) { +- hs_jack_gpios[0].gpiod_dev = card->dev; +- hs_jack_gpios[0].idx = 0; +- + ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack", + SND_JACK_HEADSET, + &priv->hs_jack, hs_jack_pins, +@@ -163,36 +143,19 @@ static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd) + if (ret) + return ret; + +- ret = snd_soc_jack_add_gpios(&priv->hs_jack, +- ARRAY_SIZE(hs_jack_gpios), +- hs_jack_gpios); ++ priv->hs_jack_gpio.name = "ti,jack-det"; ++ priv->hs_jack_gpio.report = SND_JACK_HEADSET; ++ priv->hs_jack_gpio.debounce_time = 200; ++ priv->hs_jack_gpio.gpiod_dev = card->dev; ++ priv->hs_jack_gpio.idx = 0; ++ ++ ret = snd_soc_jack_add_gpios(&priv->hs_jack, 1, ++ &priv->hs_jack_gpio); + if (ret) + return ret; + } + +- /* +- * NULL pdata means we booted with DT. In this case the routing is +- * provided and the card is fully routed, no need to mark pins. +- */ +- if (!pdata || !pdata->custom_routing) +- return ret; +- +- /* Disable not connected paths if not used */ +- twl4030_disconnect_pin(dapm, pdata->has_ear, "Earpiece Spk"); +- twl4030_disconnect_pin(dapm, pdata->has_hf, "Handsfree Spk"); +- twl4030_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone"); +- twl4030_disconnect_pin(dapm, pdata->has_predriv, "Ext Spk"); +- twl4030_disconnect_pin(dapm, pdata->has_carkit, "Carkit Spk"); +- +- twl4030_disconnect_pin(dapm, pdata->has_mainmic, "Main Mic"); +- twl4030_disconnect_pin(dapm, pdata->has_submic, "Sub Mic"); +- twl4030_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic"); +- twl4030_disconnect_pin(dapm, pdata->has_carkitmic, "Carkit Mic"); +- twl4030_disconnect_pin(dapm, pdata->has_digimic0, "Digital0 Mic"); +- twl4030_disconnect_pin(dapm, pdata->has_digimic1, "Digital1 Mic"); +- twl4030_disconnect_pin(dapm, pdata->has_linein, "Line In"); +- +- return ret; ++ return 0; + } + + /* Digital audio interface glue - connects codec <--> CPU */ +@@ -237,11 +200,15 @@ static struct snd_soc_card omap_twl4030_card = { + + static int omap_twl4030_probe(struct platform_device *pdev) + { +- struct omap_tw4030_pdata *pdata = dev_get_platdata(&pdev->dev); +- struct device_node *node = pdev->dev.of_node; + struct snd_soc_card *card = &omap_twl4030_card; ++ struct device_node *node, *dai_node; + struct omap_twl4030 *priv; +- int ret = 0; ++ struct property *prop; ++ int ret; ++ ++ node = pdev->dev.of_node; ++ if (!node) ++ return -ENODEV; + + card->dev = &pdev->dev; + +@@ -249,69 +216,52 @@ static int omap_twl4030_probe(struct platform_device *pdev) + if (priv == NULL) + return -ENOMEM; + +- if (node) { +- struct device_node *dai_node; +- struct property *prop; ++ ret = snd_soc_of_parse_card_name(card, "ti,model"); ++ if (ret) ++ return ret; + +- if (snd_soc_of_parse_card_name(card, "ti,model")) { +- dev_err(&pdev->dev, "Card name is not provided\n"); +- return -ENODEV; +- } +- +- dai_node = of_parse_phandle(node, "ti,mcbsp", 0); +- if (!dai_node) { +- dev_err(&pdev->dev, "McBSP node is not provided\n"); +- return -EINVAL; +- } +- omap_twl4030_dai_links[0].cpus->dai_name = NULL; +- omap_twl4030_dai_links[0].cpus->of_node = dai_node; +- +- omap_twl4030_dai_links[0].platforms->name = NULL; +- omap_twl4030_dai_links[0].platforms->of_node = dai_node; +- +- dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0); +- if (!dai_node) { +- card->num_links = 1; +- } else { +- omap_twl4030_dai_links[1].cpus->dai_name = NULL; +- omap_twl4030_dai_links[1].cpus->of_node = dai_node; +- +- omap_twl4030_dai_links[1].platforms->name = NULL; +- omap_twl4030_dai_links[1].platforms->of_node = dai_node; +- } +- +- /* Optional: audio routing can be provided */ +- prop = of_find_property(node, "ti,audio-routing", NULL); +- if (prop) { +- ret = snd_soc_of_parse_audio_routing(card, +- "ti,audio-routing"); +- if (ret) +- return ret; +- +- card->fully_routed = 1; +- } +- } else if (pdata) { +- if (pdata->card_name) { +- card->name = pdata->card_name; +- } else { +- dev_err(&pdev->dev, "Card name is not provided\n"); +- return -ENODEV; +- } +- +- if (!pdata->voice_connected) +- card->num_links = 1; +- } else { +- dev_err(&pdev->dev, "Missing pdata\n"); ++ if (!card->name) { ++ dev_err(&pdev->dev, "Card name is not provided\n"); + return -ENODEV; + } + ++ dai_node = of_parse_phandle(node, "ti,mcbsp", 0); ++ if (!dai_node) { ++ dev_err(&pdev->dev, "McBSP node is not provided\n"); ++ return -EINVAL; ++ } ++ omap_twl4030_dai_links[0].cpus->dai_name = NULL; ++ omap_twl4030_dai_links[0].cpus->of_node = dai_node; ++ ++ omap_twl4030_dai_links[0].platforms->name = NULL; ++ omap_twl4030_dai_links[0].platforms->of_node = dai_node; ++ ++ dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0); ++ if (!dai_node) { ++ card->num_links = 1; ++ } else { ++ omap_twl4030_dai_links[1].cpus->dai_name = NULL; ++ omap_twl4030_dai_links[1].cpus->of_node = dai_node; ++ ++ omap_twl4030_dai_links[1].platforms->name = NULL; ++ omap_twl4030_dai_links[1].platforms->of_node = dai_node; ++ } ++ ++ /* Optional: audio routing can be provided */ ++ prop = of_find_property(node, "ti,audio-routing", NULL); ++ if (prop) { ++ ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing"); ++ if (ret) ++ return ret; ++ ++ card->fully_routed = 1; ++ } ++ + snd_soc_card_set_drvdata(card, priv); + ret = devm_snd_soc_register_card(&pdev->dev, card); +- if (ret) { +- dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n", +- ret); +- return ret; +- } ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, ++ "devm_snd_soc_register_card() failed\n"); + + return 0; + } +diff --git a/sound/soc/ti/rx51.c b/sound/soc/ti/rx51.c +--- a/sound/soc/ti/rx51.c ++++ b/sound/soc/ti/rx51.c +@@ -358,21 +358,22 @@ static struct snd_soc_card rx51_sound_card = { + static int rx51_soc_probe(struct platform_device *pdev) + { + struct rx51_audio_pdata *pdata; +- struct device_node *np = pdev->dev.of_node; ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; + struct snd_soc_card *card = &rx51_sound_card; + int err; + + if (!of_machine_is_compatible("nokia,omap3-n900")) + return -ENODEV; + +- card->dev = &pdev->dev; ++ card->dev = dev; + + if (np) { + struct device_node *dai_node; + + dai_node = of_parse_phandle(np, "nokia,cpu-dai", 0); + if (!dai_node) { +- dev_err(card->dev, "McBSP node is not provided\n"); ++ dev_err(dev, "McBSP node is not provided\n"); + return -EINVAL; + } + rx51_dai[0].cpus->dai_name = NULL; +@@ -382,7 +383,7 @@ static int rx51_soc_probe(struct platform_device *pdev) + + dai_node = of_parse_phandle(np, "nokia,audio-codec", 0); + if (!dai_node) { +- dev_err(card->dev, "Codec node is not provided\n"); ++ dev_err(dev, "Codec node is not provided\n"); + return -EINVAL; + } + rx51_dai[0].codecs->name = NULL; +@@ -390,7 +391,7 @@ static int rx51_soc_probe(struct platform_device *pdev) + + dai_node = of_parse_phandle(np, "nokia,audio-codec", 1); + if (!dai_node) { +- dev_err(card->dev, "Auxiliary Codec node is not provided\n"); ++ dev_err(dev, "Auxiliary Codec node is not provided\n"); + return -EINVAL; + } + rx51_aux_dev[0].dlc.name = NULL; +@@ -400,7 +401,7 @@ static int rx51_soc_probe(struct platform_device *pdev) + + dai_node = of_parse_phandle(np, "nokia,headphone-amplifier", 0); + if (!dai_node) { +- dev_err(card->dev, "Headphone amplifier node is not provided\n"); ++ dev_err(dev, "Headphone amplifier node is not provided\n"); + return -EINVAL; + } + rx51_aux_dev[1].dlc.name = NULL; +@@ -409,40 +410,36 @@ static int rx51_soc_probe(struct platform_device *pdev) + rx51_codec_conf[1].dlc.of_node = dai_node; + } + +- pdata = devm_kzalloc(card->dev, sizeof(*pdata), GFP_KERNEL); ++ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (pdata == NULL) + return -ENOMEM; + + snd_soc_card_set_drvdata(card, pdata); + +- pdata->tvout_selection_gpio = devm_gpiod_get(card->dev, ++ pdata->tvout_selection_gpio = devm_gpiod_get(dev, + "tvout-selection", + GPIOD_OUT_LOW); +- if (IS_ERR(pdata->tvout_selection_gpio)) { +- dev_err(card->dev, "could not get tvout selection gpio\n"); +- return PTR_ERR(pdata->tvout_selection_gpio); +- } ++ if (IS_ERR(pdata->tvout_selection_gpio)) ++ return dev_err_probe(dev, PTR_ERR(pdata->tvout_selection_gpio), ++ "could not get tvout selection gpio\n"); + +- pdata->eci_sw_gpio = devm_gpiod_get(card->dev, "eci-switch", ++ pdata->eci_sw_gpio = devm_gpiod_get(dev, "eci-switch", + GPIOD_OUT_HIGH); +- if (IS_ERR(pdata->eci_sw_gpio)) { +- dev_err(card->dev, "could not get eci switch gpio\n"); +- return PTR_ERR(pdata->eci_sw_gpio); +- } ++ if (IS_ERR(pdata->eci_sw_gpio)) ++ return dev_err_probe(dev, PTR_ERR(pdata->eci_sw_gpio), ++ "could not get eci switch gpio\n"); + +- pdata->speaker_amp_gpio = devm_gpiod_get(card->dev, ++ pdata->speaker_amp_gpio = devm_gpiod_get(dev, + "speaker-amplifier", + GPIOD_OUT_LOW); +- if (IS_ERR(pdata->speaker_amp_gpio)) { +- dev_err(card->dev, "could not get speaker enable gpio\n"); +- return PTR_ERR(pdata->speaker_amp_gpio); +- } ++ if (IS_ERR(pdata->speaker_amp_gpio)) ++ return dev_err_probe(dev, PTR_ERR(pdata->speaker_amp_gpio), ++ "could not get speaker enable gpio\n"); + +- err = devm_snd_soc_register_card(card->dev, card); +- if (err) { +- dev_err(card->dev, "snd_soc_register_card failed (%d)\n", err); +- return err; +- } ++ err = devm_snd_soc_register_card(dev, card); ++ if (err) ++ return dev_err_probe(dev, err, ++ "snd_soc_register_card() failed\n"); + + return 0; + } +diff --git a/sound/soc/uniphier/aio-cpu.c b/sound/soc/uniphier/aio-cpu.c +--- a/sound/soc/uniphier/aio-cpu.c ++++ b/sound/soc/uniphier/aio-cpu.c +@@ -795,16 +795,12 @@ int uniphier_aio_probe(struct platform_device *pdev) + ret = devm_snd_soc_register_component(dev, &uniphier_aio_component, + chip->chip_spec->dais, + chip->chip_spec->num_dais); +- if (ret) { +- dev_err(dev, "Register component failed.\n"); ++ if (ret) + goto err_out_reset; +- } + + ret = uniphier_aiodma_soc_register_platform(pdev); +- if (ret) { +- dev_err(dev, "Register platform failed.\n"); ++ if (ret) + goto err_out_reset; +- } + + return 0; + +diff --git a/sound/soc/uniphier/aio.h b/sound/soc/uniphier/aio.h +--- a/sound/soc/uniphier/aio.h ++++ b/sound/soc/uniphier/aio.h +@@ -156,7 +156,7 @@ struct uniphier_aio_selector { + int hw; + }; + +-/** ++/* + * 'SoftWare MAPping' setting of UniPhier AIO registers. + * + * We have to setup 'virtual' register maps to access 'real' registers of AIO. +diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c +--- a/sound/soc/ux500/mop500.c ++++ b/sound/soc/ux500/mop500.c +@@ -68,10 +68,11 @@ static void mop500_of_node_put(void) + of_node_put(mop500_dai_links[0].codecs->of_node); + } + +-static int mop500_of_probe(struct platform_device *pdev, +- struct device_node *np) ++static int mop500_of_probe(struct snd_soc_card *card) + { ++ struct device *dev = card->dev; + struct device_node *codec_np, *msp_np[2]; ++ struct device_node *np = dev->of_node; + int i; + + msp_np[0] = of_parse_phandle(np, "stericsson,cpu-dai", 0); +@@ -79,7 +80,7 @@ static int mop500_of_probe(struct platform_device *pdev, + codec_np = of_parse_phandle(np, "stericsson,audio-codec", 0); + + if (!(msp_np[0] && msp_np[1] && codec_np)) { +- dev_err(&pdev->dev, "Phandle missing or invalid\n"); ++ dev_err(dev, "Phandle missing or invalid\n"); + for (i = 0; i < 2; i++) + of_node_put(msp_np[i]); + of_node_put(codec_np); +@@ -95,21 +96,20 @@ static int mop500_of_probe(struct platform_device *pdev, + mop500_dai_links[i].codecs->name = NULL; + } + +- snd_soc_of_parse_card_name(&mop500_card, "stericsson,card-name"); ++ snd_soc_of_parse_card_name(card, "stericsson,card-name"); + + return 0; + } + + static int mop500_probe(struct platform_device *pdev) + { +- struct device_node *np = pdev->dev.of_node; + int ret; + + dev_dbg(&pdev->dev, "%s: Enter.\n", __func__); + + mop500_card.dev = &pdev->dev; + +- ret = mop500_of_probe(pdev, np); ++ ret = mop500_of_probe(&mop500_card); + if (ret) + return ret; + +diff --git a/sound/soc/ux500/mop500_ab8500.c b/sound/soc/ux500/mop500_ab8500.c +--- a/sound/soc/ux500/mop500_ab8500.c ++++ b/sound/soc/ux500/mop500_ab8500.c +@@ -433,5 +433,5 @@ void mop500_ab8500_remove(struct snd_soc_card *card) + clk_put(drvdata->clk_ptr_ulpclk); + clk_put(drvdata->clk_ptr_intclk); + +- snd_soc_card_set_drvdata(card, drvdata); ++ snd_soc_card_set_drvdata(card, NULL); + } +diff --git a/sound/soc/xilinx/xlnx_formatter_pcm.c b/sound/soc/xilinx/xlnx_formatter_pcm.c +--- a/sound/soc/xilinx/xlnx_formatter_pcm.c ++++ b/sound/soc/xilinx/xlnx_formatter_pcm.c +@@ -9,8 +9,6 @@ + #include + #include + #include +-#include +-#include + #include + + #include +diff --git a/sound/soc/xilinx/xlnx_i2s.c b/sound/soc/xilinx/xlnx_i2s.c +--- a/sound/soc/xilinx/xlnx_i2s.c ++++ b/sound/soc/xilinx/xlnx_i2s.c +@@ -9,9 +9,8 @@ + + #include + #include +-#include +-#include + #include ++#include + #include + #include + +@@ -174,7 +173,6 @@ static int xlnx_i2s_probe(struct platform_device *pdev) + int ret; + u32 format; + struct device *dev = &pdev->dev; +- struct device_node *node = dev->of_node; + + drv_data = devm_kzalloc(&pdev->dev, sizeof(*drv_data), GFP_KERNEL); + if (!drv_data) +@@ -184,18 +182,16 @@ static int xlnx_i2s_probe(struct platform_device *pdev) + if (IS_ERR(drv_data->base)) + return PTR_ERR(drv_data->base); + +- ret = of_property_read_u32(node, "xlnx,num-channels", &drv_data->channels); +- if (ret < 0) { +- dev_err(dev, "cannot get supported channels\n"); +- return ret; +- } ++ ret = device_property_read_u32(dev, "xlnx,num-channels", &drv_data->channels); ++ if (ret < 0) ++ return dev_err_probe(dev, ret, "cannot get supported channels\n"); ++ + drv_data->channels *= 2; + +- ret = of_property_read_u32(node, "xlnx,dwidth", &drv_data->data_width); +- if (ret < 0) { +- dev_err(dev, "cannot get data width\n"); +- return ret; +- } ++ ret = device_property_read_u32(dev, "xlnx,dwidth", &drv_data->data_width); ++ if (ret < 0) ++ return dev_err_probe(dev, ret, "cannot get data width\n"); ++ + switch (drv_data->data_width) { + case 16: + format = SNDRV_PCM_FMTBIT_S16_LE; +@@ -207,7 +203,7 @@ static int xlnx_i2s_probe(struct platform_device *pdev) + return -EINVAL; + } + +- if (of_device_is_compatible(node, "xlnx,i2s-transmitter-1.0")) { ++ if (device_is_compatible(dev, "xlnx,i2s-transmitter-1.0")) { + drv_data->dai_drv.name = "xlnx_i2s_playback"; + drv_data->dai_drv.playback.stream_name = "Playback"; + drv_data->dai_drv.playback.formats = format; +@@ -215,7 +211,7 @@ static int xlnx_i2s_probe(struct platform_device *pdev) + drv_data->dai_drv.playback.channels_max = drv_data->channels; + drv_data->dai_drv.playback.rates = SNDRV_PCM_RATE_8000_192000; + drv_data->dai_drv.ops = &xlnx_i2s_dai_ops; +- } else if (of_device_is_compatible(node, "xlnx,i2s-receiver-1.0")) { ++ } else if (device_is_compatible(dev, "xlnx,i2s-receiver-1.0")) { + drv_data->dai_drv.name = "xlnx_i2s_capture"; + drv_data->dai_drv.capture.stream_name = "Capture"; + drv_data->dai_drv.capture.formats = format; +@@ -233,10 +229,8 @@ static int xlnx_i2s_probe(struct platform_device *pdev) + + ret = devm_snd_soc_register_component(&pdev->dev, &xlnx_i2s_component, + &drv_data->dai_drv, 1); +- if (ret) { +- dev_err(&pdev->dev, "i2s component registration failed\n"); ++ if (ret) + return ret; +- } + + dev_info(&pdev->dev, "%s DAI registered\n", drv_data->dai_drv.name); + +diff --git a/sound/soc/xilinx/xlnx_spdif.c b/sound/soc/xilinx/xlnx_spdif.c +--- a/sound/soc/xilinx/xlnx_spdif.c ++++ b/sound/soc/xilinx/xlnx_spdif.c +@@ -10,8 +10,7 @@ + #include + #include + #include +-#include +-#include ++#include + #include + #include + #include +@@ -242,28 +241,24 @@ static int xlnx_spdif_probe(struct platform_device *pdev) + struct spdif_dev_data *ctx; + + struct device *dev = &pdev->dev; +- struct device_node *node = dev->of_node; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->axi_clk = devm_clk_get_enabled(dev, "s_axi_aclk"); +- if (IS_ERR(ctx->axi_clk)) { +- ret = PTR_ERR(ctx->axi_clk); +- dev_err(dev, "failed to get s_axi_aclk(%d)\n", ret); +- return ret; +- } ++ if (IS_ERR(ctx->axi_clk)) ++ return dev_err_probe(dev, PTR_ERR(ctx->axi_clk), ++ "failed to get s_axi_aclk\n"); + + ctx->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ctx->base)) + return PTR_ERR(ctx->base); + +- ret = of_property_read_u32(node, "xlnx,spdif-mode", &ctx->mode); +- if (ret < 0) { +- dev_err(dev, "cannot get SPDIF mode\n"); +- return ret; +- } ++ ret = device_property_read_u32(dev, "xlnx,spdif-mode", &ctx->mode); ++ if (ret < 0) ++ return dev_err_probe(dev, ret, "cannot get SPDIF mode\n"); ++ + if (ctx->mode) { + dai_drv = &xlnx_spdif_tx_dai; + } else { +@@ -274,29 +269,23 @@ static int xlnx_spdif_probe(struct platform_device *pdev) + ret = devm_request_irq(dev, ret, + xlnx_spdifrx_irq_handler, + 0, "XLNX_SPDIF_RX", ctx); +- if (ret) { +- dev_err(dev, "spdif rx irq request failed\n"); +- return -ENODEV; +- } ++ if (ret) ++ return ret; + + init_waitqueue_head(&ctx->chsts_q); + dai_drv = &xlnx_spdif_rx_dai; + } + +- ret = of_property_read_u32(node, "xlnx,aud_clk_i", &ctx->aclk); +- if (ret < 0) { +- dev_err(dev, "cannot get aud_clk_i value\n"); +- return ret; +- } ++ ret = device_property_read_u32(dev, "xlnx,aud_clk_i", &ctx->aclk); ++ if (ret < 0) ++ return dev_err_probe(dev, ret, "cannot get aud_clk_i value\n"); + + dev_set_drvdata(dev, ctx); + + ret = devm_snd_soc_register_component(dev, &xlnx_spdif_component, + dai_drv, 1); +- if (ret) { +- dev_err(dev, "SPDIF component registration failed\n"); ++ if (ret) + return ret; +- } + + writel(XSPDIF_SOFT_RESET_VALUE, ctx->base + XSPDIF_SOFT_RESET_REG); + dev_info(dev, "%s DAI registered\n", dai_drv->name); +diff --git a/sound/soc/xtensa/xtfpga-i2s.c b/sound/soc/xtensa/xtfpga-i2s.c +--- a/sound/soc/xtensa/xtfpga-i2s.c ++++ b/sound/soc/xtensa/xtfpga-i2s.c +@@ -533,34 +533,25 @@ static int xtfpga_i2s_probe(struct platform_device *pdev) + int err, irq; + + i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); +- if (!i2s) { +- err = -ENOMEM; +- goto err; +- } ++ if (!i2s) ++ return -ENOMEM; ++ + platform_set_drvdata(pdev, i2s); + i2s->dev = &pdev->dev; + dev_dbg(&pdev->dev, "dev: %p, i2s: %p\n", &pdev->dev, i2s); + + i2s->regs = devm_platform_ioremap_resource(pdev, 0); +- if (IS_ERR(i2s->regs)) { +- err = PTR_ERR(i2s->regs); +- goto err; +- } ++ if (IS_ERR(i2s->regs)) ++ return PTR_ERR(i2s->regs); + + i2s->regmap = devm_regmap_init_mmio(&pdev->dev, i2s->regs, + &xtfpga_i2s_regmap_config); +- if (IS_ERR(i2s->regmap)) { +- dev_err(&pdev->dev, "regmap init failed\n"); +- err = PTR_ERR(i2s->regmap); +- goto err; +- } ++ if (IS_ERR(i2s->regmap)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->regmap), "regmap init failed\n"); + + i2s->clk = devm_clk_get(&pdev->dev, NULL); +- if (IS_ERR(i2s->clk)) { +- dev_err(&pdev->dev, "couldn't get clock\n"); +- err = PTR_ERR(i2s->clk); +- goto err; +- } ++ if (IS_ERR(i2s->clk)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(i2s->clk), "couldn't get clock\n"); + + regmap_write(i2s->regmap, XTFPGA_I2S_CONFIG, + (0x1 << XTFPGA_I2S_CONFIG_CHANNEL_BASE)); +@@ -568,41 +559,34 @@ static int xtfpga_i2s_probe(struct platform_device *pdev) + regmap_write(i2s->regmap, XTFPGA_I2S_INT_MASK, XTFPGA_I2S_INT_UNDERRUN); + + irq = platform_get_irq(pdev, 0); +- if (irq < 0) { +- err = irq; +- goto err; +- } ++ if (irq < 0) ++ return irq; ++ + err = devm_request_threaded_irq(&pdev->dev, irq, NULL, + xtfpga_i2s_threaded_irq_handler, + IRQF_SHARED | IRQF_ONESHOT, + pdev->name, i2s); +- if (err < 0) { +- dev_err(&pdev->dev, "request_irq failed\n"); +- goto err; +- } ++ if (err < 0) ++ return err; + + err = devm_snd_soc_register_component(&pdev->dev, + &xtfpga_i2s_component, + xtfpga_i2s_dai, + ARRAY_SIZE(xtfpga_i2s_dai)); +- if (err < 0) { +- dev_err(&pdev->dev, "couldn't register component\n"); +- goto err; +- } ++ if (err < 0) ++ return err; ++ + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + err = xtfpga_i2s_runtime_resume(&pdev->dev); +- if (err) +- goto err_pm_disable; ++ if (err) { ++ pm_runtime_disable(&pdev->dev); ++ return err; ++ } + } +- return 0; + +-err_pm_disable: +- pm_runtime_disable(&pdev->dev); +-err: +- dev_err(&pdev->dev, "%s: err = %d\n", __func__, err); +- return err; ++ return 0; + } + + static void xtfpga_i2s_remove(struct platform_device *pdev) diff --git a/pkgbuilds/linux-omarchy/0511-asoc-updates.patch.sig b/pkgbuilds/linux-omarchy/0511-asoc-updates.patch.sig new file mode 100644 index 0000000..943e088 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0511-asoc-updates.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0511-sound-updates-fixes.patch.sig b/pkgbuilds/linux-omarchy/0511-sound-updates-fixes.patch.sig deleted file mode 100644 index d84e156..0000000 Binary files a/pkgbuilds/linux-omarchy/0511-sound-updates-fixes.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/0512-sound-fixes-1.patch b/pkgbuilds/linux-omarchy/0512-sound-fixes-1.patch new file mode 100644 index 0000000..7de337b --- /dev/null +++ b/pkgbuilds/linux-omarchy/0512-sound-fixes-1.patch @@ -0,0 +1,661 @@ +diff --git a/include/sound/rawmidi.h b/include/sound/rawmidi.h +--- a/include/sound/rawmidi.h ++++ b/include/sound/rawmidi.h +@@ -179,7 +179,8 @@ int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info + int snd_rawmidi_kernel_open_nested(struct snd_rawmidi *rmidi, int subdevice, + int mode, struct snd_rawmidi_file *rfile, + int depth); +-int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile); ++int snd_rawmidi_kernel_release_nested(struct snd_rawmidi_file *rfile, ++ int depth); + int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream, + struct snd_rawmidi_params *params); + int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream, +@@ -201,6 +202,11 @@ static inline int snd_rawmidi_kernel_open(struct snd_rawmidi *rmidi, + return snd_rawmidi_kernel_open_nested(rmidi, subdevice, mode, rfile, 0); + } + ++static inline int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile) ++{ ++ return snd_rawmidi_kernel_release_nested(rfile, 0); ++} ++ + /* set up the tied devices */ + static inline void snd_rawmidi_tie_devices(struct snd_rawmidi *r1, + struct snd_rawmidi *r2) +diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c +--- a/sound/aoa/soundbus/i2sbus/core.c ++++ b/sound/aoa/soundbus/i2sbus/core.c +@@ -240,6 +240,8 @@ static int i2sbus_add_dev(struct macio_dev *macio, + } + for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) { + int irq = irq_of_parse_and_map(np, i); ++ if (!irq) ++ goto err; + if (request_irq(irq, ints[i], 0, dev->rnames[i], dev)) + goto err; + dev->interrupts[i] = irq; +diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c +--- a/sound/core/rawmidi.c ++++ b/sound/core/rawmidi.c +@@ -571,7 +571,6 @@ static void rawmidi_release_priv(struct snd_rawmidi_file *rfile) + struct snd_rawmidi *rmidi; + + rmidi = rfile->rmidi; +- guard(mutex)(&rmidi->open_mutex); + if (rfile->input) { + close_substream(rmidi, rfile->input, 1); + rfile->input = NULL; +@@ -585,7 +584,8 @@ static void rawmidi_release_priv(struct snd_rawmidi_file *rfile) + } + + /* called from sound/core/seq/seq_midi.c */ +-int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile) ++int snd_rawmidi_kernel_release_nested(struct snd_rawmidi_file *rfile, ++ int depth) + { + struct snd_rawmidi *rmidi; + +@@ -593,11 +593,13 @@ int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile) + return -ENXIO; + + rmidi = rfile->rmidi; ++ mutex_lock_nested(&rmidi->open_mutex, depth); + rawmidi_release_priv(rfile); ++ mutex_unlock(&rmidi->open_mutex); + module_put(rmidi->card->module); + return 0; + } +-EXPORT_SYMBOL(snd_rawmidi_kernel_release); ++EXPORT_SYMBOL(snd_rawmidi_kernel_release_nested); + + static int snd_rawmidi_release(struct inode *inode, struct file *file) + { +@@ -607,7 +609,8 @@ static int snd_rawmidi_release(struct inode *inode, struct file *file) + + rfile = file->private_data; + rmidi = rfile->rmidi; +- rawmidi_release_priv(rfile); ++ scoped_guard(mutex, &rmidi->open_mutex) ++ rawmidi_release_priv(rfile); + kfree(rfile); + module = rmidi->card->module; + snd_card_file_remove(rmidi->card, file); +diff --git a/sound/core/ump.c b/sound/core/ump.c +--- a/sound/core/ump.c ++++ b/sound/core/ump.c +@@ -1184,7 +1184,8 @@ static int snd_ump_legacy_close(struct snd_rawmidi_substream *substream) + ump->legacy_substreams[dir][group] = NULL; + if (dir == SNDRV_RAWMIDI_STREAM_OUTPUT) { + if (!--ump->legacy_out_opens) +- snd_rawmidi_kernel_release(&ump->legacy_out_rfile); ++ snd_rawmidi_kernel_release_nested(&ump->legacy_out_rfile, ++ SINGLE_DEPTH_NESTING); + } + return 0; + } +diff --git a/sound/hda/codecs/conexant.c b/sound/hda/codecs/conexant.c +--- a/sound/hda/codecs/conexant.c ++++ b/sound/hda/codecs/conexant.c +@@ -207,7 +207,7 @@ static void cx_remove(struct hda_codec *codec) + snd_hda_gen_remove(codec); + } + +-static void cx_process_headset_plugin(struct hda_codec *codec) ++static void cx_process_headset_detect_plug_type(struct hda_codec *codec) + { + unsigned int val; + unsigned int count = 0; +@@ -223,11 +223,9 @@ static void cx_process_headset_plugin(struct hda_codec *codec) + count++; + } while (count < 3); + val = snd_hda_codec_read(codec, 0x1c, 0, 0xcb0, 0x0); +- if (val & 0x800) { +- codec_dbg(codec, "headset plugin, type is CTIA\n"); +- snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24); +- } else if (val & 0x400) { +- codec_dbg(codec, "headset plugin, type is OMTP\n"); ++ if (val & 0xc00) { ++ codec_dbg(codec, "headset plugin, type is %s\n", ++ val & 0x800 ? "CTIA" : "OMTP"); + snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24); + } else { + codec_dbg(codec, "headphone plugin\n"); +@@ -243,10 +241,12 @@ static void cx_update_headset_mic_vref(struct hda_codec *codec, struct hda_jack_ + * Check hp&mic tag to process headset plugin & plugout. + */ + mic_present = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0x0); +- if (!(mic_present & AC_PINSENSE_PRESENCE)) /* mic plugout */ ++ if (!(mic_present & AC_PINSENSE_PRESENCE)) { /* mic plugout */ + snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20); +- else +- cx_process_headset_plugin(codec); ++ } else { ++ cx_process_headset_detect_plug_type(codec); ++ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24); ++ } + } + + static int cx_suspend(struct hda_codec *codec) +diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c +--- a/sound/hda/codecs/realtek/alc269.c ++++ b/sound/hda/codecs/realtek/alc269.c +@@ -1370,6 +1370,61 @@ static void alc269_fixup_hp_gpio_led(struct hda_codec *codec, + alc_fixup_hp_gpio_led(codec, action, 0x08, 0x10); + } + ++/* ++ * HP Laptop 15-fd0xxx (SSID 103c:8bb6) Speaker Mute LED fix ++ * ++ * The speaker mute LED is controlled via VREF100 on NID 0x1a. ++ * This pin must remain powered (D0) even during suspend, otherwise ++ * the LED state is lost and the pin defaults to Hi-Z on resume, ++ * causing the LED to stop responding to mute toggles. ++ * Windows keeps this pin powered unconditionally, so matching that ++ * behavior ensures consistent LED operation across suspend/resume. ++ * The mic-mute LED is controlled via GPIO 0 with active-low polarity. ++ */ ++static unsigned int hp_8bb6_power_filter(struct hda_codec *codec, ++ hda_nid_t nid, ++ unsigned int power_state) ++{ ++ if (nid == 0x1a) ++ return AC_PWRST_D0; ++ return snd_hda_gen_path_power_filter(codec, nid, power_state); ++} ++ ++static int hp_8bb6_speaker_mute_led_set(struct led_classdev *led_cdev, ++ enum led_brightness brightness) ++{ ++ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); ++ unsigned int val = (brightness == LED_OFF) ? PIN_IN : PIN_VREF100; ++ ++ snd_hda_set_pin_ctl_cache(codec, 0x1a, val); ++ return 0; ++} ++ ++static void alc236_fixup_hp_15_fd0xxx(struct hda_codec *codec, ++ const struct hda_fixup *fix, ++ int action) ++{ ++ struct alc_spec *spec = codec->spec; ++ ++ switch (action) { ++ case HDA_FIXUP_ACT_PRE_PROBE: ++ spec->micmute_led_polarity = 1; ++ alc_fixup_hp_gpio_led(codec, action, 0x00, 0x01); ++ ++ spec->mute_led_polarity = 0; ++ snd_hda_gen_add_mute_led_cdev(codec, hp_8bb6_speaker_mute_led_set); ++ ++ codec->power_filter = hp_8bb6_power_filter; ++ spec->no_shutup_pins = 1; ++ break; ++ ++ case HDA_FIXUP_ACT_INIT: ++ if (spec->gen.vmaster_mute.hook) ++ snd_hda_sync_vmaster_hook(&spec->gen.vmaster_mute); ++ break; ++ } ++} ++ + static void alc285_fixup_hp_gpio_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) + { +@@ -4089,6 +4144,7 @@ enum { + ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE, + ALC225_FIXUP_S3_POP_NOISE, + ALC700_FIXUP_INTEL_REFERENCE, ++ ALC700_FIXUP_INTEL_HADES_CANYON, + ALC274_FIXUP_DELL_BIND_DACS, + ALC274_FIXUP_DELL_AIO_LINEOUT_VERB, + ALC298_FIXUP_TPT470_DOCK_FIX, +@@ -4152,6 +4208,7 @@ enum { + ALC236_FIXUP_HP_GPIO_LED, + ALC236_FIXUP_HP_MUTE_LED, + ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF, ++ ALC236_FIXUP_HP_15_FD0XXX, + ALC236_FIXUP_LENOVO_INV_DMIC, + ALC298_FIXUP_SAMSUNG_AMP, + ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS, +@@ -4297,6 +4354,7 @@ enum { + ALC287_FIXUP_LENOVO_LEGION_AW88399, + ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN_HEADSET, + ALC285_LENOVO_DAC_RENAME, ++ ALC287_FIXUP_YOGA9_SPEAKER2_TO_DAC1, + }; + + /* A special fixup for Lenovo C940 and Yoga Duet 7; +@@ -4333,6 +4391,19 @@ static void alc287_fixup_lenovo_yoga_book_9i(struct hda_codec *codec, + __snd_hda_apply_fixup(codec, id, action, 0); + } + ++static void alc700_fixup_intel_hades_canyon(struct hda_codec *codec, ++ const struct hda_fixup *fix, ++ int action) ++{ ++ /* ++ * Windows may leave coef 0x1b bit 0x0400 cleared, causing broken ++ * analog playback after rebooting into Linux. Restore the bit ++ * during codec initialization. ++ */ ++ if (action == HDA_FIXUP_ACT_INIT) ++ alc_update_coef_idx(codec, 0x1b, 0x0400, 0x0400); ++} ++ + static const struct hda_fixup alc269_fixups[] = { + [ALC298_FIXUP_RAZER_BLADE16_2025_PINS] = { + .type = HDA_FIXUP_PINS, +@@ -5411,6 +5482,10 @@ static const struct hda_fixup alc269_fixups[] = { + {} + } + }, ++ [ALC700_FIXUP_INTEL_HADES_CANYON] = { ++ .type = HDA_FIXUP_FUNC, ++ .v.func = alc700_fixup_intel_hades_canyon, ++ }, + [ALC274_FIXUP_DELL_BIND_DACS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc274_fixup_bind_dacs, +@@ -5913,6 +5988,10 @@ static const struct hda_fixup alc269_fixups[] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc236_fixup_hp_mute_led_micmute_gpio, + }, ++ [ALC236_FIXUP_HP_15_FD0XXX] = { ++ .type = HDA_FIXUP_FUNC, ++ .v.func = alc236_fixup_hp_15_fd0xxx, ++ }, + [ALC236_FIXUP_LENOVO_INV_DMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_inv_dmic, +@@ -7010,6 +7089,12 @@ static const struct hda_fixup alc269_fixups[] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_lenovo_dac_rename, + }, ++ [ALC287_FIXUP_YOGA9_SPEAKER2_TO_DAC1] = { ++ .type = HDA_FIXUP_FUNC, ++ .v.func = alc285_fixup_speaker2_to_dac1, ++ .chained = true, ++ .chain_id = ALC287_FIXUP_TXNW2781_I2C, ++ }, + }; + + static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7448,6 +7533,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8bb3, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8bb4, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), ++ SND_PCI_QUIRK(0x103c, 0x8bb6, "HP Laptop 15-fd0039nt", ALC236_FIXUP_HP_15_FD0XXX), + SND_PCI_QUIRK(0x103c, 0x8bbe, "HP Victus 16-r0xxx (MB 8BBE)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8bc8, "HP Victus 15-fa1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8bcd, "HP Omen 16-xd0xxx", ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT), +@@ -8031,6 +8117,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x17aa, 0x3834, "Lenovo IdeaPad Slim 9i 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x383d, "Legion Y9000X 2019", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x3843, "Lenovo Yoga 9i / Yoga Book 9i", ALC287_FIXUP_LENOVO_YOGA_BOOK_9I), ++ SND_PCI_QUIRK(0x17aa, 0x3846, "Lenovo Yoga Pro 9 16IAH10", ALC287_FIXUP_YOGA9_SPEAKER2_TO_DAC1), + /* Yoga Pro 7 14IMH9 shares PCI SSID 17aa:3847 with Legion 7 16ACHG6; + * use codec SSID to distinguish them + */ +@@ -8044,14 +8131,15 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x17aa, 0x3852, "Lenovo Yoga 7 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x3853, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x3855, "Legion 7 16ITHG6", ALC287_FIXUP_LEGION_16ITHG6), ++ SND_PCI_QUIRK(0x17aa, 0x3862, "Lenovo IdeaPad Slim 3 15ABR8", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x3865, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x3866, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x3869, "Lenovo Yoga7 14IAL7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), + HDA_CODEC_QUIRK(0x17aa, 0x386a, "Lenovo Yoga 7 16IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), + HDA_CODEC_QUIRK(0x17aa, 0x386e, "Legion Y9000X 2022 IAH7", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x386e, "Yoga Pro 7 14ARP8", ALC285_FIXUP_YOGA_SPEAKER2_TO_DAC1), +- HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ + HDA_CODEC_QUIRK(0x17aa, 0x38a7, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ ++ HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ + SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x3870, "Lenovo Yoga 7 14ARB7", ALC287_FIXUP_YOGA7_14ARB7_I2C), + SND_PCI_QUIRK(0x17aa, 0x3874, "Legion 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), +@@ -8231,6 +8319,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x2782, 0xa128, "Positivo N15RPE-S", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x2782, 0xa212, "Lunnen Ground 14", ALC269VC_FIXUP_LUNNEN_GROUND_14), + SND_PCI_QUIRK(0x7017, 0x2014, "Star Labs StarFighter", ALC233_FIXUP_STARLABS_STARFIGHTER), ++ SND_PCI_QUIRK(0x8086, 0x2073, "Intel NUC 8 Hades Canyon", ++ ALC700_FIXUP_INTEL_HADES_CANYON), + SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), + SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED), + SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", ALC256_FIXUP_INTEL_NUC10), +diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c +--- a/sound/usb/mixer.c ++++ b/sound/usb/mixer.c +@@ -1237,21 +1237,27 @@ static void init_cur_mix_raw(struct usb_mixer_elem_info *cval, int ch, int idx) + } + + /* +- * Additional checks for sticky mixers ++ * Additional checks for sticky GET_CUR + * +- * Some devices' volume control mixers are sticky, which accept SET_CUR but +- * do absolutely nothing. ++ * Some devices' volume control mixers have sticky GET_CUR, which implies either ++ * stubbed SET_CUR or broken GET_CUR. For the former case, the mixer accepts ++ * SET_CUR but do absolutely nothing, so falling back to soft mixer is the only ++ * way to control the volume. For the latter case, the mixer has effective ++ * SET_CUR despite GET_CUR being constant, and the mixer is usable as long as we ++ * always provide mixer value from the ceche. + * + * Check the return values of GET_CUR with different SET_CUR values. Consider +- * the mixer as sticky if GET_CUR always returns a constant value. ++ * GET_CUR as sticky if GET_CUR always returns a constant value. + * +- * Some devices have effective SET_CUR despite GET_CUR being constant. Do not +- * consider the mixer as sticky if a quirk flag indicates that. ++ * Unfortunately, we can't distinguish between stubbed SET_CUR and broken ++ * GET_CUR with simple read-back tests. Disabling the mixer regardless and ++ * forcing userspace to use soft mixer instead can lead to audible distortion at ++ * low volume on some wireless headphones, probably due to their poorly- ++ * performed lossy codec. + * +- * Gate the registration of sticky mixers to prevent confusing userspace, so +- * that they won't cause ineffective volume control. However, for mixers with +- * effective SET_CUR but broken GET_CUR, the registration can continue normally +- * but further GET_CUR requests will be gated. ++ * Instead, mark GET_CUR as broken regardless and only provide mixer value from ++ * the cache. Users may opt into soft mixer in userspace audio stack if they ++ * need it. + */ + static int check_sticky_volume_control(struct usb_mixer_elem_info *cval, + int channel, int saved) +@@ -1271,24 +1277,13 @@ static int check_sticky_volume_control(struct usb_mixer_elem_info *cval, + return 0; + } + +- if (cval->head.mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_GET_CUR_BROKEN) { +- usb_audio_info(cval->head.mixer->chip, +- "%d:%d: broken mixer GET_CUR (%d/%d/%d => %d)\n", +- cval->head.id, mixer_ctrl_intf(cval->head.mixer), +- cval->min, cval->max, cval->res, saved); +- +- cval->get_cur_broken = 1; +- return -ENXIO; +- } +- +- usb_audio_err(cval->head.mixer->chip, +- "%d:%d: sticky mixer values (%d/%d/%d => %d), disabling\n", +- cval->head.id, mixer_ctrl_intf(cval->head.mixer), +- cval->min, cval->max, cval->res, saved); + usb_audio_info(cval->head.mixer->chip, +- "check MIXER_GET_CUR_BROKEN if you believe the mixer is non-sticky"); ++ "%d:%d: broken mixer GET_CUR (%d/%d/%d => %d)\n", ++ cval->head.id, mixer_ctrl_intf(cval->head.mixer), ++ cval->min, cval->max, cval->res, saved); + +- return -ENODEV; ++ cval->get_cur_broken = 1; ++ return -ENXIO; + } + + /* +@@ -1385,8 +1380,6 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, + goto no_checks; + + ret = check_sticky_volume_control(cval, minchn, saved); +- if (ret == -ENODEV) +- goto sticky; + if (ret) + goto no_checks; + +@@ -1454,34 +1447,15 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, + } + } + +- return 0; +- +-sticky: + /* +- * It makes no sense to restore the saved value for a sticky mixer, +- * since setting any value is a no-op. +- * +- * However, in some rare cases, SET_CUR is effective despite GET_CUR +- * always returns a constant value. These mixers are not sticky, but +- * there's no way to distinguish them. Without any additional +- * information, the best thing we can do is to set the mixer value to +- * the maximum before bailing out, so that a soft mixer can still reach +- * the maximum hardware volume if the mixer turns out to be non-sticky. +- * Meanwhile, all channels must be synchronized to prevent imbalance +- * volume. ++ * When GET_CUR is sticky, the saved value is bogus, so mixer values set ++ * by the sanity checks must be discarded through init_cur_mix_raw(). ++ * After that, we can clear the flag as per QUIRK_FLAG_MIXER_GET_CUR_OK. + */ +- if (!cval->cmask) { +- snd_usb_set_cur_mix_value(cval, 0, 0, cval->max); +- } else { +- idx = 0; +- for (i = 0; i < MAX_CHANNELS; i++) { +- if (cval->cmask & BIT(i)) { +- snd_usb_set_cur_mix_value(cval, i + 1, idx, cval->max); +- idx++; +- } +- } +- } +- return ret; ++ if (cval->head.mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_GET_CUR_OK) ++ cval->get_cur_broken = 0; ++ ++ return 0; + } + + #define get_min_max(cval, def) get_min_max_with_quirks(cval, def, NULL) +diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c +--- a/sound/usb/mixer_maps.c ++++ b/sound/usb/mixer_maps.c +@@ -505,13 +505,13 @@ static const struct usbmix_connector_map gigabyte_b450_connector_map[] = { + {} + }; + +-/* Audient iD14: FU 12 advertises Volume on only 4 of its 6 logical channels +- * and sits on the monitor mixer branch, but it is traced through to the +- * Speaker output terminal and gets named "Speaker Playback Volume". Userspace +- * then adopts it as the stream's hardware volume, and any setting below 0 dB +- * attenuates some channels but not others (20 dB imbalance at 80%). Give it a +- * non-standard name so that it is no longer taken for the stream's master +- * volume, while remaining reachable for anyone who wants the monitor gain. ++/* Audient iD14 MkI and MkII: FU 12 sits on the monitor mixer branch but is ++ * traced through to the Speaker output terminal, so it is named "Speaker ++ * Playback Volume". On MkII it controls only 4 of 6 playback channels. MkI ++ * testing found asymmetric attenuation within the main stereo pair. Userspace ++ * adopts this control as the stream's hardware volume, causing imbalance below ++ * 0 dB. Give it a non-standard name so that userspace no longer treats it as ++ * the stream master, while keeping the monitor gain reachable. + */ + static const struct usbmix_name_map audient_id14_map[] = { + { 12, "Monitor Mix Playback" }, /* FU, partial coverage */ +@@ -602,7 +602,12 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { + .map = maya44_map, + }, + { +- /* Audient iD14 */ ++ /* Audient iD14 MkI */ ++ .id = USB_ID(0x2708, 0x0002), ++ .map = audient_id14_map, ++ }, ++ { ++ /* Audient iD14 MkII */ + .id = USB_ID(0x2708, 0x0008), + .map = audient_id14_map, + }, +diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h +--- a/sound/usb/quirks-table.h ++++ b/sound/usb/quirks-table.h +@@ -1814,6 +1814,28 @@ YAMAHA_DEVICE(0x7010, "UB99"), + } + } + }, ++{ ++ /* ++ * M-Audio Venom ++ * ++ * The AudioControl interface times out on every GET_CUR request, ++ * which adds around 47 seconds to the card registration and ++ * freezes the device, blocking streaming. ++ * Using an explicit composite quirk to skip the mixer entirely. ++ */ ++ USB_DEVICE_VENDOR_SPEC(0x0763, 0x2084), ++ QUIRK_DRIVER_INFO { ++ .vendor_name = "M-Audio", ++ .product_name = "Venom", ++ QUIRK_DATA_COMPOSITE { ++ { QUIRK_DATA_IGNORE(0) }, ++ { QUIRK_DATA_STANDARD_AUDIO(1) }, ++ { QUIRK_DATA_STANDARD_AUDIO(2) }, ++ { QUIRK_DATA_STANDARD_MIDI(3) }, ++ QUIRK_COMPOSITE_END ++ } ++ } ++}, + + /* Casio devices */ + { +diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c +--- a/sound/usb/quirks.c ++++ b/sound/usb/quirks.c +@@ -2215,10 +2215,10 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), + DEVICE_FLG(0x03f0, 0x654a, /* HP 320 FHD Webcam */ + QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), +- DEVICE_FLG(0x040b, 0x0897, /* Weltrend Semiconductor, sold as Redragon H510-PRO Wireless headset */ +- QUIRK_FLAG_MIXER_GET_CUR_BROKEN), + DEVICE_FLG(0x041e, 0x3000, /* Creative SB Extigy */ + QUIRK_FLAG_IGNORE_CTL_ERROR), ++ DEVICE_FLG(0x041e, 0x324d, /* Creative Sound Blaster Play! 3 */ ++ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), + DEVICE_FLG(0x041e, 0x4080, /* Creative Live Cam VF0610 */ + QUIRK_FLAG_GET_SAMPLE_RATE), + DEVICE_FLG(0x045e, 0x083c, /* MS USB Link headset */ +@@ -2256,8 +2256,9 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + DEVICE_FLG(0x046d, 0x0a8f, /* Logitech H390 headset */ + QUIRK_FLAG_CTL_MSG_DELAY_1M | + QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), +- DEVICE_FLG(0x046d, 0x0af7, /* Logitech PRO X 2 LIGHTSPEED */ +- QUIRK_FLAG_MIXER_GET_CUR_BROKEN), ++ DEVICE_FLG(0x046d, 0x0aba, /* Logitech PRO X Wireless */ ++ QUIRK_FLAG_MIXER_GET_CUR_OK | ++ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), + DEVICE_FLG(0x0499, 0x1506, /* Yamaha THR5 */ + QUIRK_FLAG_GENERIC_IMPLICIT_FB), + DEVICE_FLG(0x0499, 0x1509, /* Steinberg UR22 */ +@@ -2329,9 +2330,11 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + DEVICE_FLG(0x0763, 0x2031, /* M-Audio Fast Track C600 */ + QUIRK_FLAG_GENERIC_IMPLICIT_FB), + DEVICE_FLG(0x0763, 0x2080, /* M-Audio Fast Track Ultra */ +- QUIRK_FLAG_MIXER_GET_CUR_BROKEN | QUIRK_FLAG_GENERIC_IMPLICIT_FB), ++ QUIRK_FLAG_GENERIC_IMPLICIT_FB), + DEVICE_FLG(0x0763, 0x2081, /* M-Audio Fast Track Ultra */ +- QUIRK_FLAG_MIXER_GET_CUR_BROKEN | QUIRK_FLAG_GENERIC_IMPLICIT_FB), ++ QUIRK_FLAG_GENERIC_IMPLICIT_FB), ++ DEVICE_FLG(0x0763, 0x2084, /* M-Audio Venom */ ++ QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_DISABLE_AUTOSUSPEND), + DEVICE_FLG(0x07fd, 0x000b, /* MOTU M Series 2nd hardware revision */ + QUIRK_FLAG_CTL_MSG_DELAY_1M), + DEVICE_FLG(0x08bb, 0x2702, /* LineX FM Transmitter */ +@@ -2370,8 +2373,6 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER), + DEVICE_FLG(0x1038, 0x1294, /* SteelSeries Arctis Pro Wireless */ + QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), +- DEVICE_FLG(0x1038, 0x2232, /* SteelSeries Arctis Nova 5 */ +- QUIRK_FLAG_MIXER_GET_CUR_BROKEN), + DEVICE_FLG(0x1101, 0x0003, /* Audioengine D1 */ + QUIRK_FLAG_GET_SAMPLE_RATE), + DEVICE_FLG(0x12d1, 0x3a07, /* HUAWEI USB-C HEADSET */ +@@ -2379,8 +2380,6 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), + DEVICE_FLG(0x1224, 0x2a25, /* Jieli Technology USB PHY 2.0 */ + QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), +- DEVICE_FLG(0x1377, 0x6004, /* Sennheiser MOMENTUM 3 */ +- QUIRK_FLAG_MIXER_GET_CUR_BROKEN), + DEVICE_FLG(0x1395, 0x740a, /* Sennheiser DECT */ + QUIRK_FLAG_GET_SAMPLE_RATE), + DEVICE_FLG(0x1397, 0x0507, /* Behringer UMC202HD */ +@@ -2393,6 +2392,9 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + QUIRK_FLAG_IFB_SILENCE_ON_EMPTY), + DEVICE_FLG(0x13e5, 0x0001, /* Serato Phono */ + QUIRK_FLAG_IGNORE_CTL_ERROR), ++ DEVICE_FLG(0x152a, 0x85dd, /* SMSL USB DAC */ ++ QUIRK_FLAG_DSD_RAW | QUIRK_FLAG_DISABLE_AUTOSUSPEND | ++ QUIRK_FLAG_SKIP_IFACE_SETUP), + DEVICE_FLG(0x152a, 0x880a, /* NeuralDSP Quad Cortex */ + 0), /* Doesn't have the vendor quirk which would otherwise apply */ + DEVICE_FLG(0x1532, 0x055e, /* Razer Nommo V2 X */ +@@ -2505,8 +2507,6 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + QUIRK_FLAG_CTL_MSG_DELAY_1M), + DEVICE_FLG(0x2d99, 0x0026, /* HECATE G2 GAMING HEADSET */ + QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), +- DEVICE_FLG(0x2d99, 0xa024, /* Edifier MF200 */ +- QUIRK_FLAG_MIXER_GET_CUR_BROKEN), + DEVICE_FLG(0x2fc6, 0xf06b, /* MOONDROP Moonriver2 Ti */ + QUIRK_FLAG_CTL_MSG_DELAY), + DEVICE_FLG(0x2fc6, 0xf0b5, /* iBasso DC-Elite */ +@@ -2647,7 +2647,7 @@ static const char *const snd_usb_audio_quirk_flag_names[] = { + QUIRK_STRING_ENTRY(MIXER_PLAYBACK_LINEAR_VOL), + QUIRK_STRING_ENTRY(MIXER_CAPTURE_LINEAR_VOL), + QUIRK_STRING_ENTRY(IFB_SILENCE_ON_EMPTY), +- QUIRK_STRING_ENTRY(MIXER_GET_CUR_BROKEN), ++ QUIRK_STRING_ENTRY(MIXER_GET_CUR_OK), + QUIRK_STRING_ENTRY(PLAYBACK_URB_FIXUP), + QUIRK_STRING_ENTRY(ALWAYS_SET_RATE), + NULL +diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h +--- a/sound/usb/usbaudio.h ++++ b/sound/usb/usbaudio.h +@@ -243,24 +243,18 @@ extern bool snd_usb_skip_validation; + * from snd_usb_handle_sync_urb. Instead fall through and enqueue a + * packet_info containing only size-0 packets, so the OUT ring keeps + * moving (emits silence). Needed by Behringer Flow 8 (1397:050c). +- * QUIRK_FLAG_MIXER_GET_CUR_BROKEN +- * Some mixers are sticky, which means that setting their current volume is a +- * no-op, and reading the current volume returns a constant value. The sticky +- * check disables these mixers to prevent confusing userspace. However, some +- * devices do have a tunable volume despite the reported current volume being +- * constant. As the sticky check can't distinguish between the two categories, +- * setting this flag tells that the device should fall into the second +- * category when GET_CUR returns a constant value, resulting in the sticky +- * check being non-fatal and only disabling GET_CUR instead of the whole mixer. +- * The current volume will then be provided by the internal cache that stores +- * the last set volume ++ * QUIRK_FLAG_MIXER_GET_CUR_OK ++ * On some devices, whether their GET_CUR being sticky depends on whether ++ * hotpluggable components are present. When the hotpluggable components are ++ * missing on probe, their GET_CUR behavior is classified as broken. Set the ++ * flag to prevent the heuristics from gating GET_CUR. + * QUIRK_FLAG_PLAYBACK_URB_FIXUP + * Set URB_ISO_ASAP flag for isochronous URBs and force nurbs to MAX_URBS. + * This is needed for devices that exhibit boot-time audio stuttering due + * to insufficient buffer depth combined with xHCI scheduling variability. + * The larger buffer (MAX_URBS = 12, ~64ms) absorbs system scheduling + * jitter during boot, while URB_ISO_ASAP ensures consistent xHCI scheduling. +- * QUIRK_FLAG_ALWAYS_SET_RATE: ++ * QUIRK_FLAG_ALWAYS_SET_RATE + * Issue SET_CUR for the sample rate even when the clock already reports the + * requested rate. A device advertising a single rate is otherwise never sent + * the request at all, and some require it before streaming will start. +@@ -297,10 +291,10 @@ enum { + QUIRK_TYPE_MIXER_PLAYBACK_LINEAR_VOL = 27, + QUIRK_TYPE_MIXER_CAPTURE_LINEAR_VOL = 28, + QUIRK_TYPE_IFB_SILENCE_ON_EMPTY = 29, +- QUIRK_TYPE_MIXER_GET_CUR_BROKEN = 30, ++ QUIRK_TYPE_MIXER_GET_CUR_OK = 30, + QUIRK_TYPE_PLAYBACK_URB_FIXUP = 31, + QUIRK_TYPE_ALWAYS_SET_RATE = 32, +-/* Please also edit snd_usb_audio_quirk_flag_names */ ++/* Please also edit snd_usb_audio_quirk_flag_names and alsa-configuration.rst */ + }; + + #define QUIRK_FLAG(x) BIT_U64(QUIRK_TYPE_ ## x) +@@ -335,7 +329,7 @@ enum { + #define QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL QUIRK_FLAG(MIXER_PLAYBACK_LINEAR_VOL) + #define QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL QUIRK_FLAG(MIXER_CAPTURE_LINEAR_VOL) + #define QUIRK_FLAG_IFB_SILENCE_ON_EMPTY QUIRK_FLAG(IFB_SILENCE_ON_EMPTY) +-#define QUIRK_FLAG_MIXER_GET_CUR_BROKEN QUIRK_FLAG(MIXER_GET_CUR_BROKEN) ++#define QUIRK_FLAG_MIXER_GET_CUR_OK QUIRK_FLAG(MIXER_GET_CUR_OK) + #define QUIRK_FLAG_PLAYBACK_URB_FIXUP QUIRK_FLAG(PLAYBACK_URB_FIXUP) + #define QUIRK_FLAG_ALWAYS_SET_RATE QUIRK_FLAG(ALWAYS_SET_RATE) + diff --git a/pkgbuilds/linux-omarchy/0512-sound-fixes-1.patch.sig b/pkgbuilds/linux-omarchy/0512-sound-fixes-1.patch.sig new file mode 100644 index 0000000..cdb2260 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0512-sound-fixes-1.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0512-sound-fixes.patch.sig b/pkgbuilds/linux-omarchy/0512-sound-fixes.patch.sig deleted file mode 100644 index c02c1df..0000000 Binary files a/pkgbuilds/linux-omarchy/0512-sound-fixes.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/0511-sound-updates-fixes.patch b/pkgbuilds/linux-omarchy/0513-asoc-fixes-1.patch similarity index 51% rename from pkgbuilds/linux-omarchy/0511-sound-updates-fixes.patch rename to pkgbuilds/linux-omarchy/0513-asoc-fixes-1.patch index c32b5c3..49c1eb3 100644 --- a/pkgbuilds/linux-omarchy/0511-sound-updates-fixes.patch +++ b/pkgbuilds/linux-omarchy/0513-asoc-fixes-1.patch @@ -64,400 +64,9 @@ diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bin #define LPASS_HW_AVTIMER_VOTE 101 #define LPASS_HW_MACRO_VOTE 102 -diff --git a/include/sound/rawmidi.h b/include/sound/rawmidi.h ---- a/include/sound/rawmidi.h -+++ b/include/sound/rawmidi.h -@@ -179,7 +179,8 @@ int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info - int snd_rawmidi_kernel_open_nested(struct snd_rawmidi *rmidi, int subdevice, - int mode, struct snd_rawmidi_file *rfile, - int depth); --int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile); -+int snd_rawmidi_kernel_release_nested(struct snd_rawmidi_file *rfile, -+ int depth); - int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream, - struct snd_rawmidi_params *params); - int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream, -@@ -201,6 +202,11 @@ static inline int snd_rawmidi_kernel_open(struct snd_rawmidi *rmidi, - return snd_rawmidi_kernel_open_nested(rmidi, subdevice, mode, rfile, 0); - } - -+static inline int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile) -+{ -+ return snd_rawmidi_kernel_release_nested(rfile, 0); -+} -+ - /* set up the tied devices */ - static inline void snd_rawmidi_tie_devices(struct snd_rawmidi *r1, - struct snd_rawmidi *r2) -diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c ---- a/sound/aoa/soundbus/i2sbus/core.c -+++ b/sound/aoa/soundbus/i2sbus/core.c -@@ -240,6 +240,8 @@ static int i2sbus_add_dev(struct macio_dev *macio, - } - for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) { - int irq = irq_of_parse_and_map(np, i); -+ if (!irq) -+ goto err; - if (request_irq(irq, ints[i], 0, dev->rnames[i], dev)) - goto err; - dev->interrupts[i] = irq; -diff --git a/sound/core/control_led.c b/sound/core/control_led.c ---- a/sound/core/control_led.c -+++ b/sound/core/control_led.c -@@ -255,6 +255,8 @@ static int snd_ctl_led_set_id(int card_number, struct snd_ctl_elem_id *id, - kctl = snd_ctl_find_id(card, id); - if (!kctl) - return -ENOENT; -+ if (!kctl->info || !kctl->get) -+ return -EINVAL; - ioff = snd_ctl_get_ioff(kctl, id); - vd = &kctl->vd[ioff]; - access = vd->access & SNDRV_CTL_ELEM_ACCESS_LED_MASK; -diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c ---- a/sound/core/rawmidi.c -+++ b/sound/core/rawmidi.c -@@ -571,7 +571,6 @@ static void rawmidi_release_priv(struct snd_rawmidi_file *rfile) - struct snd_rawmidi *rmidi; - - rmidi = rfile->rmidi; -- guard(mutex)(&rmidi->open_mutex); - if (rfile->input) { - close_substream(rmidi, rfile->input, 1); - rfile->input = NULL; -@@ -585,7 +584,8 @@ static void rawmidi_release_priv(struct snd_rawmidi_file *rfile) - } - - /* called from sound/core/seq/seq_midi.c */ --int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile) -+int snd_rawmidi_kernel_release_nested(struct snd_rawmidi_file *rfile, -+ int depth) - { - struct snd_rawmidi *rmidi; - -@@ -593,11 +593,13 @@ int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile) - return -ENXIO; - - rmidi = rfile->rmidi; -+ mutex_lock_nested(&rmidi->open_mutex, depth); - rawmidi_release_priv(rfile); -+ mutex_unlock(&rmidi->open_mutex); - module_put(rmidi->card->module); - return 0; - } --EXPORT_SYMBOL(snd_rawmidi_kernel_release); -+EXPORT_SYMBOL(snd_rawmidi_kernel_release_nested); - - static int snd_rawmidi_release(struct inode *inode, struct file *file) - { -@@ -607,7 +609,8 @@ static int snd_rawmidi_release(struct inode *inode, struct file *file) - - rfile = file->private_data; - rmidi = rfile->rmidi; -- rawmidi_release_priv(rfile); -+ scoped_guard(mutex, &rmidi->open_mutex) -+ rawmidi_release_priv(rfile); - kfree(rfile); - module = rmidi->card->module; - snd_card_file_remove(rmidi->card, file); -diff --git a/sound/core/ump.c b/sound/core/ump.c ---- a/sound/core/ump.c -+++ b/sound/core/ump.c -@@ -1184,7 +1184,8 @@ static int snd_ump_legacy_close(struct snd_rawmidi_substream *substream) - ump->legacy_substreams[dir][group] = NULL; - if (dir == SNDRV_RAWMIDI_STREAM_OUTPUT) { - if (!--ump->legacy_out_opens) -- snd_rawmidi_kernel_release(&ump->legacy_out_rfile); -+ snd_rawmidi_kernel_release_nested(&ump->legacy_out_rfile, -+ SINGLE_DEPTH_NESTING); - } - return 0; - } -diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c ---- a/sound/drivers/mtpav.c -+++ b/sound/drivers/mtpav.c -@@ -642,9 +642,7 @@ static void snd_mtpav_free(struct snd_card *card) - { - struct mtpav *crd = card->private_data; - -- guard(spinlock_irqsave)(&crd->spinlock); -- if (crd->istimer > 0) -- snd_mtpav_remove_output_timer(crd); -+ timer_shutdown_sync(&crd->timer); - } - - /* -diff --git a/sound/hda/codecs/conexant.c b/sound/hda/codecs/conexant.c ---- a/sound/hda/codecs/conexant.c -+++ b/sound/hda/codecs/conexant.c -@@ -207,7 +207,7 @@ static void cx_remove(struct hda_codec *codec) - snd_hda_gen_remove(codec); - } - --static void cx_process_headset_plugin(struct hda_codec *codec) -+static void cx_process_headset_detect_plug_type(struct hda_codec *codec) - { - unsigned int val; - unsigned int count = 0; -@@ -223,11 +223,9 @@ static void cx_process_headset_plugin(struct hda_codec *codec) - count++; - } while (count < 3); - val = snd_hda_codec_read(codec, 0x1c, 0, 0xcb0, 0x0); -- if (val & 0x800) { -- codec_dbg(codec, "headset plugin, type is CTIA\n"); -- snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24); -- } else if (val & 0x400) { -- codec_dbg(codec, "headset plugin, type is OMTP\n"); -+ if (val & 0xc00) { -+ codec_dbg(codec, "headset plugin, type is %s\n", -+ val & 0x800 ? "CTIA" : "OMTP"); - snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24); - } else { - codec_dbg(codec, "headphone plugin\n"); -@@ -243,10 +241,12 @@ static void cx_update_headset_mic_vref(struct hda_codec *codec, struct hda_jack_ - * Check hp&mic tag to process headset plugin & plugout. - */ - mic_present = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0x0); -- if (!(mic_present & AC_PINSENSE_PRESENCE)) /* mic plugout */ -+ if (!(mic_present & AC_PINSENSE_PRESENCE)) { /* mic plugout */ - snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20); -- else -- cx_process_headset_plugin(codec); -+ } else { -+ cx_process_headset_detect_plug_type(codec); -+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24); -+ } - } - - static int cx_suspend(struct hda_codec *codec) -diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c ---- a/sound/hda/codecs/realtek/alc269.c -+++ b/sound/hda/codecs/realtek/alc269.c -@@ -1370,6 +1370,61 @@ static void alc269_fixup_hp_gpio_led(struct hda_codec *codec, - alc_fixup_hp_gpio_led(codec, action, 0x08, 0x10); - } - -+/* -+ * HP Laptop 15-fd0xxx (SSID 103c:8bb6) Speaker Mute LED fix -+ * -+ * The speaker mute LED is controlled via VREF100 on NID 0x1a. -+ * This pin must remain powered (D0) even during suspend, otherwise -+ * the LED state is lost and the pin defaults to Hi-Z on resume, -+ * causing the LED to stop responding to mute toggles. -+ * Windows keeps this pin powered unconditionally, so matching that -+ * behavior ensures consistent LED operation across suspend/resume. -+ * The mic-mute LED is controlled via GPIO 0 with active-low polarity. -+ */ -+static unsigned int hp_8bb6_power_filter(struct hda_codec *codec, -+ hda_nid_t nid, -+ unsigned int power_state) -+{ -+ if (nid == 0x1a) -+ return AC_PWRST_D0; -+ return snd_hda_gen_path_power_filter(codec, nid, power_state); -+} -+ -+static int hp_8bb6_speaker_mute_led_set(struct led_classdev *led_cdev, -+ enum led_brightness brightness) -+{ -+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); -+ unsigned int val = (brightness == LED_OFF) ? PIN_IN : PIN_VREF100; -+ -+ snd_hda_set_pin_ctl_cache(codec, 0x1a, val); -+ return 0; -+} -+ -+static void alc236_fixup_hp_15_fd0xxx(struct hda_codec *codec, -+ const struct hda_fixup *fix, -+ int action) -+{ -+ struct alc_spec *spec = codec->spec; -+ -+ switch (action) { -+ case HDA_FIXUP_ACT_PRE_PROBE: -+ spec->micmute_led_polarity = 1; -+ alc_fixup_hp_gpio_led(codec, action, 0x00, 0x01); -+ -+ spec->mute_led_polarity = 0; -+ snd_hda_gen_add_mute_led_cdev(codec, hp_8bb6_speaker_mute_led_set); -+ -+ codec->power_filter = hp_8bb6_power_filter; -+ spec->no_shutup_pins = 1; -+ break; -+ -+ case HDA_FIXUP_ACT_INIT: -+ if (spec->gen.vmaster_mute.hook) -+ snd_hda_sync_vmaster_hook(&spec->gen.vmaster_mute); -+ break; -+ } -+} -+ - static void alc285_fixup_hp_gpio_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) - { -@@ -4089,6 +4144,7 @@ enum { - ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE, - ALC225_FIXUP_S3_POP_NOISE, - ALC700_FIXUP_INTEL_REFERENCE, -+ ALC700_FIXUP_INTEL_HADES_CANYON, - ALC274_FIXUP_DELL_BIND_DACS, - ALC274_FIXUP_DELL_AIO_LINEOUT_VERB, - ALC298_FIXUP_TPT470_DOCK_FIX, -@@ -4152,6 +4208,7 @@ enum { - ALC236_FIXUP_HP_GPIO_LED, - ALC236_FIXUP_HP_MUTE_LED, - ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF, -+ ALC236_FIXUP_HP_15_FD0XXX, - ALC236_FIXUP_LENOVO_INV_DMIC, - ALC298_FIXUP_SAMSUNG_AMP, - ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS, -@@ -4297,6 +4354,7 @@ enum { - ALC287_FIXUP_LENOVO_LEGION_AW88399, - ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN_HEADSET, - ALC285_LENOVO_DAC_RENAME, -+ ALC287_FIXUP_YOGA9_SPEAKER2_TO_DAC1, - }; - - /* A special fixup for Lenovo C940 and Yoga Duet 7; -@@ -4333,6 +4391,19 @@ static void alc287_fixup_lenovo_yoga_book_9i(struct hda_codec *codec, - __snd_hda_apply_fixup(codec, id, action, 0); - } - -+static void alc700_fixup_intel_hades_canyon(struct hda_codec *codec, -+ const struct hda_fixup *fix, -+ int action) -+{ -+ /* -+ * Windows may leave coef 0x1b bit 0x0400 cleared, causing broken -+ * analog playback after rebooting into Linux. Restore the bit -+ * during codec initialization. -+ */ -+ if (action == HDA_FIXUP_ACT_INIT) -+ alc_update_coef_idx(codec, 0x1b, 0x0400, 0x0400); -+} -+ - static const struct hda_fixup alc269_fixups[] = { - [ALC298_FIXUP_RAZER_BLADE16_2025_PINS] = { - .type = HDA_FIXUP_PINS, -@@ -5411,6 +5482,10 @@ static const struct hda_fixup alc269_fixups[] = { - {} - } - }, -+ [ALC700_FIXUP_INTEL_HADES_CANYON] = { -+ .type = HDA_FIXUP_FUNC, -+ .v.func = alc700_fixup_intel_hades_canyon, -+ }, - [ALC274_FIXUP_DELL_BIND_DACS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc274_fixup_bind_dacs, -@@ -5913,6 +5988,10 @@ static const struct hda_fixup alc269_fixups[] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc236_fixup_hp_mute_led_micmute_gpio, - }, -+ [ALC236_FIXUP_HP_15_FD0XXX] = { -+ .type = HDA_FIXUP_FUNC, -+ .v.func = alc236_fixup_hp_15_fd0xxx, -+ }, - [ALC236_FIXUP_LENOVO_INV_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic, -@@ -7010,6 +7089,12 @@ static const struct hda_fixup alc269_fixups[] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_lenovo_dac_rename, - }, -+ [ALC287_FIXUP_YOGA9_SPEAKER2_TO_DAC1] = { -+ .type = HDA_FIXUP_FUNC, -+ .v.func = alc285_fixup_speaker2_to_dac1, -+ .chained = true, -+ .chain_id = ALC287_FIXUP_TXNW2781_I2C, -+ }, - }; - - static const struct hda_quirk alc269_fixup_tbl[] = { -@@ -7448,6 +7533,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8bb3, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8bb4, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), -+ SND_PCI_QUIRK(0x103c, 0x8bb6, "HP Laptop 15-fd0039nt", ALC236_FIXUP_HP_15_FD0XXX), - SND_PCI_QUIRK(0x103c, 0x8bbe, "HP Victus 16-r0xxx (MB 8BBE)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), - SND_PCI_QUIRK(0x103c, 0x8bc8, "HP Victus 15-fa1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), - SND_PCI_QUIRK(0x103c, 0x8bcd, "HP Omen 16-xd0xxx", ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT), -@@ -8031,6 +8117,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { - SND_PCI_QUIRK(0x17aa, 0x3834, "Lenovo IdeaPad Slim 9i 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x383d, "Legion Y9000X 2019", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x3843, "Lenovo Yoga 9i / Yoga Book 9i", ALC287_FIXUP_LENOVO_YOGA_BOOK_9I), -+ SND_PCI_QUIRK(0x17aa, 0x3846, "Lenovo Yoga Pro 9 16IAH10", ALC287_FIXUP_YOGA9_SPEAKER2_TO_DAC1), - /* Yoga Pro 7 14IMH9 shares PCI SSID 17aa:3847 with Legion 7 16ACHG6; - * use codec SSID to distinguish them - */ -@@ -8044,14 +8131,15 @@ static const struct hda_quirk alc269_fixup_tbl[] = { - SND_PCI_QUIRK(0x17aa, 0x3852, "Lenovo Yoga 7 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x3853, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x3855, "Legion 7 16ITHG6", ALC287_FIXUP_LEGION_16ITHG6), -+ SND_PCI_QUIRK(0x17aa, 0x3862, "Lenovo IdeaPad Slim 3 15ABR8", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x17aa, 0x3865, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x17aa, 0x3866, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x17aa, 0x3869, "Lenovo Yoga7 14IAL7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), - HDA_CODEC_QUIRK(0x17aa, 0x386a, "Lenovo Yoga 7 16IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), - HDA_CODEC_QUIRK(0x17aa, 0x386e, "Legion Y9000X 2022 IAH7", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x17aa, 0x386e, "Yoga Pro 7 14ARP8", ALC285_FIXUP_YOGA_SPEAKER2_TO_DAC1), -- HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ - HDA_CODEC_QUIRK(0x17aa, 0x38a7, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ -+ HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ - SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x17aa, 0x3870, "Lenovo Yoga 7 14ARB7", ALC287_FIXUP_YOGA7_14ARB7_I2C), - SND_PCI_QUIRK(0x17aa, 0x3874, "Legion 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), -@@ -8231,6 +8319,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { - SND_PCI_QUIRK(0x2782, 0xa128, "Positivo N15RPE-S", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x2782, 0xa212, "Lunnen Ground 14", ALC269VC_FIXUP_LUNNEN_GROUND_14), - SND_PCI_QUIRK(0x7017, 0x2014, "Star Labs StarFighter", ALC233_FIXUP_STARLABS_STARFIGHTER), -+ SND_PCI_QUIRK(0x8086, 0x2073, "Intel NUC 8 Hades Canyon", -+ ALC700_FIXUP_INTEL_HADES_CANYON), - SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), - SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED), - SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", ALC256_FIXUP_INTEL_NUC10), -diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c ---- a/sound/pci/ice1712/ice1712.c -+++ b/sound/pci/ice1712/ice1712.c -@@ -2523,7 +2523,7 @@ static int snd_ice1712_probe(struct pci_dev *pci, - const struct pci_device_id *pci_id) - { - static int dev; -- struct snd_card *card; -+ struct snd_card *card __free(snd_card_unref) = NULL; - struct snd_ice1712 *ice; - int pcm_dev = 0, err; - const struct snd_ice1712_card_info * const *tbl, *c; -@@ -2535,8 +2535,8 @@ static int snd_ice1712_probe(struct pci_dev *pci, - return -ENOENT; - } - -- err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, -- sizeof(*ice), &card); -+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, -+ sizeof(*ice), &card); - if (err < 0) - return err; - ice = card->private_data; -@@ -2640,6 +2640,7 @@ static int snd_ice1712_probe(struct pci_dev *pci, - if (err < 0) - return err; - pci_set_drvdata(pci, card); -+ card = NULL; /* probe succeeded, don't release as error */ - dev++; - return 0; - } diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c --- a/sound/soc/amd/acp-config.c +++ b/sound/soc/amd/acp-config.c -@@ -54,7 +54,7 @@ static const struct dmi_system_id acp70_acpi_flag_override_table[] = { - { - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC"), -- DMI_MATCH(DMI_PRODUCT_NAME, "TUF Gaming A14 FA401EA"), -+ DMI_MATCH(DMI_PRODUCT_NAME, "FA401EA"), - }, - }, - { @@ -70,6 +70,13 @@ static const struct dmi_system_id acp70_acpi_flag_override_table[] = { DMI_MATCH(DMI_BOARD_NAME, "8EA1"), }, @@ -816,51 +425,6 @@ diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c ret = sdw_write_no_pm(slave, addr, prep_ch->ch_mask); if (ret) dev_err(dev, "prep failed for port %d, err=%d\n", -diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig ---- a/sound/soc/fsl/Kconfig -+++ b/sound/soc/fsl/Kconfig -@@ -244,7 +244,7 @@ config SND_SOC_MPC5200_AC97 - config SND_MPC52xx_SOC_PCM030 - tristate "SoC AC97 Audio support for Phytec pcm030 and WM9712" - depends on PPC_MPC5200_SIMPLE -- select SND_SOC_MPC5200_AC97 -+ depends on SND_SOC_MPC5200_AC97 - select SND_SOC_WM9712 - help - Say Y if you want to add support for sound on the Phytec pcm030 -@@ -253,7 +253,7 @@ config SND_MPC52xx_SOC_PCM030 - config SND_MPC52xx_SOC_EFIKA - tristate "SoC AC97 Audio support for bbplan Efika and STAC9766" - depends on PPC_EFIKA -- select SND_SOC_MPC5200_AC97 -+ depends on SND_SOC_MPC5200_AC97 - select SND_SOC_STAC9766 - help - Say Y if you want to add support for sound on the Efika. -diff --git a/sound/soc/fsl/efika-audio-fabric.c b/sound/soc/fsl/efika-audio-fabric.c ---- a/sound/soc/fsl/efika-audio-fabric.c -+++ b/sound/soc/fsl/efika-audio-fabric.c -@@ -22,8 +22,6 @@ - #include - #include - --#include "mpc5200_dma.h" -- - #define DRV_NAME "efika-audio-fabric" - - SND_SOC_DAILINK_DEFS(analog, -diff --git a/sound/soc/fsl/pcm030-audio-fabric.c b/sound/soc/fsl/pcm030-audio-fabric.c ---- a/sound/soc/fsl/pcm030-audio-fabric.c -+++ b/sound/soc/fsl/pcm030-audio-fabric.c -@@ -13,8 +13,6 @@ - - #include - --#include "mpc5200_dma.h" -- - #define DRV_NAME "pcm030-audio-fabric" - - struct pcm030_audio_data { diff --git a/sound/soc/loongson/loongson_card.c b/sound/soc/loongson/loongson_card.c --- a/sound/soc/loongson/loongson_card.c +++ b/sound/soc/loongson/loongson_card.c @@ -1275,18 +839,6 @@ diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_u ret = sdw_enable_stream(sdw_stream); break; -diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c ---- a/sound/soc/soc-dapm.c -+++ b/sound/soc/soc-dapm.c -@@ -3614,7 +3614,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, - val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; - mask = e->mask << e->shift_l; - if (e->shift_l != e->shift_r) { -- if (item[1] > e->items) -+ if (item[1] >= e->items) - return -EINVAL; - val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r; - mask |= e->mask << e->shift_r; diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -1317,339 +869,3 @@ diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaen name, PTR_ERR(chan)); pcm->chan[i] = NULL; -diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c ---- a/sound/usb/mixer.c -+++ b/sound/usb/mixer.c -@@ -1237,21 +1237,27 @@ static void init_cur_mix_raw(struct usb_mixer_elem_info *cval, int ch, int idx) - } - - /* -- * Additional checks for sticky mixers -+ * Additional checks for sticky GET_CUR - * -- * Some devices' volume control mixers are sticky, which accept SET_CUR but -- * do absolutely nothing. -+ * Some devices' volume control mixers have sticky GET_CUR, which implies either -+ * stubbed SET_CUR or broken GET_CUR. For the former case, the mixer accepts -+ * SET_CUR but do absolutely nothing, so falling back to soft mixer is the only -+ * way to control the volume. For the latter case, the mixer has effective -+ * SET_CUR despite GET_CUR being constant, and the mixer is usable as long as we -+ * always provide mixer value from the ceche. - * - * Check the return values of GET_CUR with different SET_CUR values. Consider -- * the mixer as sticky if GET_CUR always returns a constant value. -+ * GET_CUR as sticky if GET_CUR always returns a constant value. - * -- * Some devices have effective SET_CUR despite GET_CUR being constant. Do not -- * consider the mixer as sticky if a quirk flag indicates that. -+ * Unfortunately, we can't distinguish between stubbed SET_CUR and broken -+ * GET_CUR with simple read-back tests. Disabling the mixer regardless and -+ * forcing userspace to use soft mixer instead can lead to audible distortion at -+ * low volume on some wireless headphones, probably due to their poorly- -+ * performed lossy codec. - * -- * Gate the registration of sticky mixers to prevent confusing userspace, so -- * that they won't cause ineffective volume control. However, for mixers with -- * effective SET_CUR but broken GET_CUR, the registration can continue normally -- * but further GET_CUR requests will be gated. -+ * Instead, mark GET_CUR as broken regardless and only provide mixer value from -+ * the cache. Users may opt into soft mixer in userspace audio stack if they -+ * need it. - */ - static int check_sticky_volume_control(struct usb_mixer_elem_info *cval, - int channel, int saved) -@@ -1271,24 +1277,13 @@ static int check_sticky_volume_control(struct usb_mixer_elem_info *cval, - return 0; - } - -- if (cval->head.mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_GET_CUR_BROKEN) { -- usb_audio_info(cval->head.mixer->chip, -- "%d:%d: broken mixer GET_CUR (%d/%d/%d => %d)\n", -- cval->head.id, mixer_ctrl_intf(cval->head.mixer), -- cval->min, cval->max, cval->res, saved); -- -- cval->get_cur_broken = 1; -- return -ENXIO; -- } -- -- usb_audio_err(cval->head.mixer->chip, -- "%d:%d: sticky mixer values (%d/%d/%d => %d), disabling\n", -- cval->head.id, mixer_ctrl_intf(cval->head.mixer), -- cval->min, cval->max, cval->res, saved); - usb_audio_info(cval->head.mixer->chip, -- "check MIXER_GET_CUR_BROKEN if you believe the mixer is non-sticky"); -+ "%d:%d: broken mixer GET_CUR (%d/%d/%d => %d)\n", -+ cval->head.id, mixer_ctrl_intf(cval->head.mixer), -+ cval->min, cval->max, cval->res, saved); - -- return -ENODEV; -+ cval->get_cur_broken = 1; -+ return -ENXIO; - } - - /* -@@ -1385,8 +1380,6 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, - goto no_checks; - - ret = check_sticky_volume_control(cval, minchn, saved); -- if (ret == -ENODEV) -- goto sticky; - if (ret) - goto no_checks; - -@@ -1454,34 +1447,15 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, - } - } - -- return 0; -- --sticky: - /* -- * It makes no sense to restore the saved value for a sticky mixer, -- * since setting any value is a no-op. -- * -- * However, in some rare cases, SET_CUR is effective despite GET_CUR -- * always returns a constant value. These mixers are not sticky, but -- * there's no way to distinguish them. Without any additional -- * information, the best thing we can do is to set the mixer value to -- * the maximum before bailing out, so that a soft mixer can still reach -- * the maximum hardware volume if the mixer turns out to be non-sticky. -- * Meanwhile, all channels must be synchronized to prevent imbalance -- * volume. -+ * When GET_CUR is sticky, the saved value is bogus, so mixer values set -+ * by the sanity checks must be discarded through init_cur_mix_raw(). -+ * After that, we can clear the flag as per QUIRK_FLAG_MIXER_GET_CUR_OK. - */ -- if (!cval->cmask) { -- snd_usb_set_cur_mix_value(cval, 0, 0, cval->max); -- } else { -- idx = 0; -- for (i = 0; i < MAX_CHANNELS; i++) { -- if (cval->cmask & BIT(i)) { -- snd_usb_set_cur_mix_value(cval, i + 1, idx, cval->max); -- idx++; -- } -- } -- } -- return ret; -+ if (cval->head.mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_GET_CUR_OK) -+ cval->get_cur_broken = 0; -+ -+ return 0; - } - - #define get_min_max(cval, def) get_min_max_with_quirks(cval, def, NULL) -diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c ---- a/sound/usb/mixer_maps.c -+++ b/sound/usb/mixer_maps.c -@@ -505,13 +505,13 @@ static const struct usbmix_connector_map gigabyte_b450_connector_map[] = { - {} - }; - --/* Audient iD14: FU 12 advertises Volume on only 4 of its 6 logical channels -- * and sits on the monitor mixer branch, but it is traced through to the -- * Speaker output terminal and gets named "Speaker Playback Volume". Userspace -- * then adopts it as the stream's hardware volume, and any setting below 0 dB -- * attenuates some channels but not others (20 dB imbalance at 80%). Give it a -- * non-standard name so that it is no longer taken for the stream's master -- * volume, while remaining reachable for anyone who wants the monitor gain. -+/* Audient iD14 MkI and MkII: FU 12 sits on the monitor mixer branch but is -+ * traced through to the Speaker output terminal, so it is named "Speaker -+ * Playback Volume". On MkII it controls only 4 of 6 playback channels. MkI -+ * testing found asymmetric attenuation within the main stereo pair. Userspace -+ * adopts this control as the stream's hardware volume, causing imbalance below -+ * 0 dB. Give it a non-standard name so that userspace no longer treats it as -+ * the stream master, while keeping the monitor gain reachable. - */ - static const struct usbmix_name_map audient_id14_map[] = { - { 12, "Monitor Mix Playback" }, /* FU, partial coverage */ -@@ -602,7 +602,12 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { - .map = maya44_map, - }, - { -- /* Audient iD14 */ -+ /* Audient iD14 MkI */ -+ .id = USB_ID(0x2708, 0x0002), -+ .map = audient_id14_map, -+ }, -+ { -+ /* Audient iD14 MkII */ - .id = USB_ID(0x2708, 0x0008), - .map = audient_id14_map, - }, -diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h ---- a/sound/usb/quirks-table.h -+++ b/sound/usb/quirks-table.h -@@ -1814,6 +1814,28 @@ YAMAHA_DEVICE(0x7010, "UB99"), - } - } - }, -+{ -+ /* -+ * M-Audio Venom -+ * -+ * The AudioControl interface times out on every GET_CUR request, -+ * which adds around 47 seconds to the card registration and -+ * freezes the device, blocking streaming. -+ * Using an explicit composite quirk to skip the mixer entirely. -+ */ -+ USB_DEVICE_VENDOR_SPEC(0x0763, 0x2084), -+ QUIRK_DRIVER_INFO { -+ .vendor_name = "M-Audio", -+ .product_name = "Venom", -+ QUIRK_DATA_COMPOSITE { -+ { QUIRK_DATA_IGNORE(0) }, -+ { QUIRK_DATA_STANDARD_AUDIO(1) }, -+ { QUIRK_DATA_STANDARD_AUDIO(2) }, -+ { QUIRK_DATA_STANDARD_MIDI(3) }, -+ QUIRK_COMPOSITE_END -+ } -+ } -+}, - - /* Casio devices */ - { -diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c ---- a/sound/usb/quirks.c -+++ b/sound/usb/quirks.c -@@ -2215,10 +2215,10 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { - QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), - DEVICE_FLG(0x03f0, 0x654a, /* HP 320 FHD Webcam */ - QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), -- DEVICE_FLG(0x040b, 0x0897, /* Weltrend Semiconductor, sold as Redragon H510-PRO Wireless headset */ -- QUIRK_FLAG_MIXER_GET_CUR_BROKEN), - DEVICE_FLG(0x041e, 0x3000, /* Creative SB Extigy */ - QUIRK_FLAG_IGNORE_CTL_ERROR), -+ DEVICE_FLG(0x041e, 0x324d, /* Creative Sound Blaster Play! 3 */ -+ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), - DEVICE_FLG(0x041e, 0x4080, /* Creative Live Cam VF0610 */ - QUIRK_FLAG_GET_SAMPLE_RATE), - DEVICE_FLG(0x045e, 0x083c, /* MS USB Link headset */ -@@ -2256,8 +2256,9 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { - DEVICE_FLG(0x046d, 0x0a8f, /* Logitech H390 headset */ - QUIRK_FLAG_CTL_MSG_DELAY_1M | - QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), -- DEVICE_FLG(0x046d, 0x0af7, /* Logitech PRO X 2 LIGHTSPEED */ -- QUIRK_FLAG_MIXER_GET_CUR_BROKEN), -+ DEVICE_FLG(0x046d, 0x0aba, /* Logitech PRO X Wireless */ -+ QUIRK_FLAG_MIXER_GET_CUR_OK | -+ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), - DEVICE_FLG(0x0499, 0x1506, /* Yamaha THR5 */ - QUIRK_FLAG_GENERIC_IMPLICIT_FB), - DEVICE_FLG(0x0499, 0x1509, /* Steinberg UR22 */ -@@ -2329,9 +2330,11 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { - DEVICE_FLG(0x0763, 0x2031, /* M-Audio Fast Track C600 */ - QUIRK_FLAG_GENERIC_IMPLICIT_FB), - DEVICE_FLG(0x0763, 0x2080, /* M-Audio Fast Track Ultra */ -- QUIRK_FLAG_MIXER_GET_CUR_BROKEN | QUIRK_FLAG_GENERIC_IMPLICIT_FB), -+ QUIRK_FLAG_GENERIC_IMPLICIT_FB), - DEVICE_FLG(0x0763, 0x2081, /* M-Audio Fast Track Ultra */ -- QUIRK_FLAG_MIXER_GET_CUR_BROKEN | QUIRK_FLAG_GENERIC_IMPLICIT_FB), -+ QUIRK_FLAG_GENERIC_IMPLICIT_FB), -+ DEVICE_FLG(0x0763, 0x2084, /* M-Audio Venom */ -+ QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_DISABLE_AUTOSUSPEND), - DEVICE_FLG(0x07fd, 0x000b, /* MOTU M Series 2nd hardware revision */ - QUIRK_FLAG_CTL_MSG_DELAY_1M), - DEVICE_FLG(0x08bb, 0x2702, /* LineX FM Transmitter */ -@@ -2370,8 +2373,6 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { - QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER), - DEVICE_FLG(0x1038, 0x1294, /* SteelSeries Arctis Pro Wireless */ - QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), -- DEVICE_FLG(0x1038, 0x2232, /* SteelSeries Arctis Nova 5 */ -- QUIRK_FLAG_MIXER_GET_CUR_BROKEN), - DEVICE_FLG(0x1101, 0x0003, /* Audioengine D1 */ - QUIRK_FLAG_GET_SAMPLE_RATE), - DEVICE_FLG(0x12d1, 0x3a07, /* HUAWEI USB-C HEADSET */ -@@ -2379,8 +2380,6 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { - QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), - DEVICE_FLG(0x1224, 0x2a25, /* Jieli Technology USB PHY 2.0 */ - QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), -- DEVICE_FLG(0x1377, 0x6004, /* Sennheiser MOMENTUM 3 */ -- QUIRK_FLAG_MIXER_GET_CUR_BROKEN), - DEVICE_FLG(0x1395, 0x740a, /* Sennheiser DECT */ - QUIRK_FLAG_GET_SAMPLE_RATE), - DEVICE_FLG(0x1397, 0x0507, /* Behringer UMC202HD */ -@@ -2393,6 +2392,9 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { - QUIRK_FLAG_IFB_SILENCE_ON_EMPTY), - DEVICE_FLG(0x13e5, 0x0001, /* Serato Phono */ - QUIRK_FLAG_IGNORE_CTL_ERROR), -+ DEVICE_FLG(0x152a, 0x85dd, /* SMSL USB DAC */ -+ QUIRK_FLAG_DSD_RAW | QUIRK_FLAG_DISABLE_AUTOSUSPEND | -+ QUIRK_FLAG_SKIP_IFACE_SETUP), - DEVICE_FLG(0x152a, 0x880a, /* NeuralDSP Quad Cortex */ - 0), /* Doesn't have the vendor quirk which would otherwise apply */ - DEVICE_FLG(0x1532, 0x055e, /* Razer Nommo V2 X */ -@@ -2505,8 +2507,6 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { - QUIRK_FLAG_CTL_MSG_DELAY_1M), - DEVICE_FLG(0x2d99, 0x0026, /* HECATE G2 GAMING HEADSET */ - QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), -- DEVICE_FLG(0x2d99, 0xa024, /* Edifier MF200 */ -- QUIRK_FLAG_MIXER_GET_CUR_BROKEN), - DEVICE_FLG(0x2fc6, 0xf06b, /* MOONDROP Moonriver2 Ti */ - QUIRK_FLAG_CTL_MSG_DELAY), - DEVICE_FLG(0x2fc6, 0xf0b5, /* iBasso DC-Elite */ -@@ -2647,7 +2647,7 @@ static const char *const snd_usb_audio_quirk_flag_names[] = { - QUIRK_STRING_ENTRY(MIXER_PLAYBACK_LINEAR_VOL), - QUIRK_STRING_ENTRY(MIXER_CAPTURE_LINEAR_VOL), - QUIRK_STRING_ENTRY(IFB_SILENCE_ON_EMPTY), -- QUIRK_STRING_ENTRY(MIXER_GET_CUR_BROKEN), -+ QUIRK_STRING_ENTRY(MIXER_GET_CUR_OK), - QUIRK_STRING_ENTRY(PLAYBACK_URB_FIXUP), - QUIRK_STRING_ENTRY(ALWAYS_SET_RATE), - NULL -diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h ---- a/sound/usb/usbaudio.h -+++ b/sound/usb/usbaudio.h -@@ -243,24 +243,18 @@ extern bool snd_usb_skip_validation; - * from snd_usb_handle_sync_urb. Instead fall through and enqueue a - * packet_info containing only size-0 packets, so the OUT ring keeps - * moving (emits silence). Needed by Behringer Flow 8 (1397:050c). -- * QUIRK_FLAG_MIXER_GET_CUR_BROKEN -- * Some mixers are sticky, which means that setting their current volume is a -- * no-op, and reading the current volume returns a constant value. The sticky -- * check disables these mixers to prevent confusing userspace. However, some -- * devices do have a tunable volume despite the reported current volume being -- * constant. As the sticky check can't distinguish between the two categories, -- * setting this flag tells that the device should fall into the second -- * category when GET_CUR returns a constant value, resulting in the sticky -- * check being non-fatal and only disabling GET_CUR instead of the whole mixer. -- * The current volume will then be provided by the internal cache that stores -- * the last set volume -+ * QUIRK_FLAG_MIXER_GET_CUR_OK -+ * On some devices, whether their GET_CUR being sticky depends on whether -+ * hotpluggable components are present. When the hotpluggable components are -+ * missing on probe, their GET_CUR behavior is classified as broken. Set the -+ * flag to prevent the heuristics from gating GET_CUR. - * QUIRK_FLAG_PLAYBACK_URB_FIXUP - * Set URB_ISO_ASAP flag for isochronous URBs and force nurbs to MAX_URBS. - * This is needed for devices that exhibit boot-time audio stuttering due - * to insufficient buffer depth combined with xHCI scheduling variability. - * The larger buffer (MAX_URBS = 12, ~64ms) absorbs system scheduling - * jitter during boot, while URB_ISO_ASAP ensures consistent xHCI scheduling. -- * QUIRK_FLAG_ALWAYS_SET_RATE: -+ * QUIRK_FLAG_ALWAYS_SET_RATE - * Issue SET_CUR for the sample rate even when the clock already reports the - * requested rate. A device advertising a single rate is otherwise never sent - * the request at all, and some require it before streaming will start. -@@ -297,10 +291,10 @@ enum { - QUIRK_TYPE_MIXER_PLAYBACK_LINEAR_VOL = 27, - QUIRK_TYPE_MIXER_CAPTURE_LINEAR_VOL = 28, - QUIRK_TYPE_IFB_SILENCE_ON_EMPTY = 29, -- QUIRK_TYPE_MIXER_GET_CUR_BROKEN = 30, -+ QUIRK_TYPE_MIXER_GET_CUR_OK = 30, - QUIRK_TYPE_PLAYBACK_URB_FIXUP = 31, - QUIRK_TYPE_ALWAYS_SET_RATE = 32, --/* Please also edit snd_usb_audio_quirk_flag_names */ -+/* Please also edit snd_usb_audio_quirk_flag_names and alsa-configuration.rst */ - }; - - #define QUIRK_FLAG(x) BIT_U64(QUIRK_TYPE_ ## x) -@@ -335,7 +329,7 @@ enum { - #define QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL QUIRK_FLAG(MIXER_PLAYBACK_LINEAR_VOL) - #define QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL QUIRK_FLAG(MIXER_CAPTURE_LINEAR_VOL) - #define QUIRK_FLAG_IFB_SILENCE_ON_EMPTY QUIRK_FLAG(IFB_SILENCE_ON_EMPTY) --#define QUIRK_FLAG_MIXER_GET_CUR_BROKEN QUIRK_FLAG(MIXER_GET_CUR_BROKEN) -+#define QUIRK_FLAG_MIXER_GET_CUR_OK QUIRK_FLAG(MIXER_GET_CUR_OK) - #define QUIRK_FLAG_PLAYBACK_URB_FIXUP QUIRK_FLAG(PLAYBACK_URB_FIXUP) - #define QUIRK_FLAG_ALWAYS_SET_RATE QUIRK_FLAG(ALWAYS_SET_RATE) - diff --git a/pkgbuilds/linux-omarchy/0513-asoc-fixes-1.patch.sig b/pkgbuilds/linux-omarchy/0513-asoc-fixes-1.patch.sig new file mode 100644 index 0000000..c7079ef Binary files /dev/null and b/pkgbuilds/linux-omarchy/0513-asoc-fixes-1.patch.sig differ diff --git a/pkgbuilds/linux-omarchy-bore/0512-sound-fixes.patch b/pkgbuilds/linux-omarchy/0514-sound-fixes-2.patch similarity index 74% rename from pkgbuilds/linux-omarchy-bore/0512-sound-fixes.patch rename to pkgbuilds/linux-omarchy/0514-sound-fixes-2.patch index 9566376..043c34e 100644 --- a/pkgbuilds/linux-omarchy-bore/0512-sound-fixes.patch +++ b/pkgbuilds/linux-omarchy/0514-sound-fixes-2.patch @@ -1,92 +1,3 @@ -diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c ---- a/sound/core/pcm_native.c -+++ b/sound/core/pcm_native.c -@@ -4023,20 +4023,33 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, - return -EINVAL; - } - runtime = substream->runtime; -- if (runtime->state == SNDRV_PCM_STATE_OPEN) -- return -EBADFD; -- if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) -- return -ENXIO; -+ /* don't race with buffer reallocation in hw_params/hw_free */ -+ if (!atomic_inc_unless_negative(&runtime->buffer_accessing)) -+ return -EBUSY; -+ if (runtime->state == SNDRV_PCM_STATE_OPEN) { -+ err = -EBADFD; -+ goto out; -+ } -+ if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) { -+ err = -ENXIO; -+ goto out; -+ } - if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || -- runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) -- return -EINVAL; -+ runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) { -+ err = -EINVAL; -+ goto out; -+ } - size = area->vm_end - area->vm_start; - offset = area->vm_pgoff << PAGE_SHIFT; - dma_bytes = PAGE_ALIGN(runtime->dma_bytes); -- if ((size_t)size > dma_bytes) -- return -EINVAL; -- if (offset > dma_bytes - size) -- return -EINVAL; -+ if ((size_t)size > dma_bytes) { -+ err = -EINVAL; -+ goto out; -+ } -+ if (offset > dma_bytes - size) { -+ err = -EINVAL; -+ goto out; -+ } - - area->vm_ops = &snd_pcm_vm_ops_data; - area->vm_private_data = substream; -@@ -4046,6 +4059,8 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, - err = snd_pcm_lib_default_mmap(substream, area); - if (!err) - atomic_inc(&substream->mmap_count); -+out: -+ atomic_dec(&runtime->buffer_accessing); - return err; - } - EXPORT_SYMBOL(snd_pcm_mmap_data); -diff --git a/sound/core/ump.c b/sound/core/ump.c ---- a/sound/core/ump.c -+++ b/sound/core/ump.c -@@ -1335,6 +1335,8 @@ static void update_legacy_names(struct snd_ump_endpoint *ump) - { - struct snd_rawmidi *rmidi = ump->legacy_rmidi; - -+ if (!rmidi) -+ return; - update_legacy_substreams(ump, rmidi, SNDRV_RAWMIDI_STREAM_INPUT); - update_legacy_substreams(ump, rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT); - } -@@ -1343,6 +1345,8 @@ static void ump_legacy_set_rawmidi_name(struct snd_ump_endpoint *ump) - { - struct snd_rawmidi *rmidi = ump->legacy_rmidi; - -+ if (!rmidi) -+ return; - snprintf(rmidi->name, sizeof(rmidi->name), "%.68s (MIDI 1.0)", - ump->core.name); - } -diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c ---- a/sound/drivers/dummy.c -+++ b/sound/drivers/dummy.c -@@ -807,7 +807,7 @@ static int snd_dummy_capsrc_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el - left = ucontrol->value.integer.value[0] & 1; - right = ucontrol->value.integer.value[1] & 1; - guard(spinlock_irq)(&dummy->mixer_lock); -- change = dummy->capture_source[addr][0] != left && -+ change = dummy->capture_source[addr][0] != left || - dummy->capture_source[addr][1] != right; - dummy->capture_source[addr][0] = left; - dummy->capture_source[addr][1] = right; diff --git a/sound/hda/codecs/cirrus/cs420x.c b/sound/hda/codecs/cirrus/cs420x.c --- a/sound/hda/codecs/cirrus/cs420x.c +++ b/sound/hda/codecs/cirrus/cs420x.c @@ -233,62 +144,6 @@ diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269 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/hda/core/device.c b/sound/hda/core/device.c ---- a/sound/hda/core/device.c -+++ b/sound/hda/core/device.c -@@ -404,6 +404,7 @@ static void setup_fg_nodes(struct hdac_device *codec) - */ - int snd_hdac_refresh_widgets(struct hdac_device *codec) - { -+ hda_nid_t fg = codec->afg ? codec->afg : codec->mfg; - hda_nid_t start_nid; - int nums, err = 0; - -@@ -412,10 +413,10 @@ int snd_hdac_refresh_widgets(struct hdac_device *codec) - * widgets array. - */ - guard(mutex)(&codec->widget_lock); -- nums = snd_hdac_get_sub_nodes(codec, codec->afg, &start_nid); -+ nums = snd_hdac_get_sub_nodes(codec, fg, &start_nid); - if (!start_nid || nums <= 0 || nums >= 0xff) { - dev_err(&codec->dev, "cannot read sub nodes for FG 0x%02x\n", -- codec->afg); -+ fg); - return -EINVAL; - } - -diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c ---- a/sound/usb/caiaq/audio.c -+++ b/sound/usb/caiaq/audio.c -@@ -828,16 +828,13 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *cdev) - - cdev->data_urbs_in = alloc_urbs(cdev, SNDRV_PCM_STREAM_CAPTURE, &ret); - if (ret < 0) { -- kfree(cdev->data_cb_info); -- free_urbs(cdev->data_urbs_in); -+ snd_usb_caiaq_audio_free(cdev); - return ret; - } - - cdev->data_urbs_out = alloc_urbs(cdev, SNDRV_PCM_STREAM_PLAYBACK, &ret); - if (ret < 0) { -- kfree(cdev->data_cb_info); -- free_urbs(cdev->data_urbs_in); -- free_urbs(cdev->data_urbs_out); -+ snd_usb_caiaq_audio_free(cdev); - return ret; - } - -@@ -858,6 +855,9 @@ void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *cdev) - - dev_dbg(dev, "%s(%p)\n", __func__, cdev); - free_urbs(cdev->data_urbs_in); -+ cdev->data_urbs_in = NULL; - free_urbs(cdev->data_urbs_out); -+ cdev->data_urbs_out = NULL; - kfree(cdev->data_cb_info); -+ cdev->data_cb_info = NULL; - } diff --git a/sound/usb/fcp.c b/sound/usb/fcp.c --- a/sound/usb/fcp.c +++ b/sound/usb/fcp.c diff --git a/pkgbuilds/linux-omarchy/0514-sound-fixes-2.patch.sig b/pkgbuilds/linux-omarchy/0514-sound-fixes-2.patch.sig new file mode 100644 index 0000000..cd8ab3f Binary files /dev/null and b/pkgbuilds/linux-omarchy/0514-sound-fixes-2.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0513-xps13-sof-quirk.patch b/pkgbuilds/linux-omarchy/0515-xps13-sof-quirk.patch similarity index 100% rename from pkgbuilds/linux-omarchy/0513-xps13-sof-quirk.patch rename to pkgbuilds/linux-omarchy/0515-xps13-sof-quirk.patch diff --git a/pkgbuilds/linux-omarchy/0513-xps13-sof-quirk.patch.sig b/pkgbuilds/linux-omarchy/0515-xps13-sof-quirk.patch.sig similarity index 100% rename from pkgbuilds/linux-omarchy/0513-xps13-sof-quirk.patch.sig rename to pkgbuilds/linux-omarchy/0515-xps13-sof-quirk.patch.sig diff --git a/pkgbuilds/linux-omarchy/0514-rt766-stream-config-type.patch b/pkgbuilds/linux-omarchy/0516-rt766-stream-config-type.patch similarity index 100% rename from pkgbuilds/linux-omarchy/0514-rt766-stream-config-type.patch rename to pkgbuilds/linux-omarchy/0516-rt766-stream-config-type.patch diff --git a/pkgbuilds/linux-omarchy/0514-rt766-stream-config-type.patch.sig b/pkgbuilds/linux-omarchy/0516-rt766-stream-config-type.patch.sig similarity index 100% rename from pkgbuilds/linux-omarchy/0514-rt766-stream-config-type.patch.sig rename to pkgbuilds/linux-omarchy/0516-rt766-stream-config-type.patch.sig diff --git a/pkgbuilds/linux-omarchy/0516-hda-realtek-rog-strix-g733zw-speakers.patch b/pkgbuilds/linux-omarchy/0517-hda-realtek-rog-strix-g733zw-speakers.patch similarity index 100% rename from pkgbuilds/linux-omarchy/0516-hda-realtek-rog-strix-g733zw-speakers.patch rename to pkgbuilds/linux-omarchy/0517-hda-realtek-rog-strix-g733zw-speakers.patch diff --git a/pkgbuilds/linux-omarchy/0516-hda-realtek-rog-strix-g733zw-speakers.patch.sig b/pkgbuilds/linux-omarchy/0517-hda-realtek-rog-strix-g733zw-speakers.patch.sig similarity index 100% rename from pkgbuilds/linux-omarchy/0516-hda-realtek-rog-strix-g733zw-speakers.patch.sig rename to pkgbuilds/linux-omarchy/0517-hda-realtek-rog-strix-g733zw-speakers.patch.sig diff --git a/pkgbuilds/linux-omarchy/0517-asoc-amd-yc-acer-aspire-a314-23p.patch b/pkgbuilds/linux-omarchy/0518-asoc-amd-yc-acer-aspire-a314-23p.patch similarity index 100% rename from pkgbuilds/linux-omarchy/0517-asoc-amd-yc-acer-aspire-a314-23p.patch rename to pkgbuilds/linux-omarchy/0518-asoc-amd-yc-acer-aspire-a314-23p.patch diff --git a/pkgbuilds/linux-omarchy/0517-asoc-amd-yc-acer-aspire-a314-23p.patch.sig b/pkgbuilds/linux-omarchy/0518-asoc-amd-yc-acer-aspire-a314-23p.patch.sig similarity index 100% rename from pkgbuilds/linux-omarchy/0517-asoc-amd-yc-acer-aspire-a314-23p.patch.sig rename to pkgbuilds/linux-omarchy/0518-asoc-amd-yc-acer-aspire-a314-23p.patch.sig diff --git a/pkgbuilds/linux-omarchy/0522-sound-fixes-3.patch b/pkgbuilds/linux-omarchy/0522-sound-fixes-3.patch new file mode 100644 index 0000000..48c9872 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0522-sound-fixes-3.patch @@ -0,0 +1,432 @@ +diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c +--- a/sound/hda/codecs/realtek/alc269.c ++++ b/sound/hda/codecs/realtek/alc269.c +@@ -7465,6 +7465,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x887c, "HP Laptop 14s-fq1xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x888a, "HP ENVY x360 Convertible 15-eu0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS), + SND_PCI_QUIRK(0x103c, 0x888d, "HP ZBook Power 15.6 inch G8 Mobile Workstation PC", ALC236_FIXUP_HP_GPIO_LED), ++ SND_PCI_QUIRK(0x103c, 0x8890, "HP Elite Dragonfly Max G2 Notebook PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), + SND_PCI_QUIRK(0x103c, 0x8895, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8896, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8898, "HP EliteBook 845 G8 Notebook PC", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST), +@@ -7580,6 +7581,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x8b92, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8b96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), ++ SND_PCI_QUIRK(0x103c, 0x8ba9, "HP Omen 16-wd0xxx", ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8bb3, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8bb4, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8bb6, "HP Laptop 15-fd0039nt", ALC236_FIXUP_HP_15_FD0XXX), +diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c +--- a/sound/pci/rme9652/hdspm.c ++++ b/sound/pci/rme9652/hdspm.c +@@ -1071,14 +1071,8 @@ struct hdspm { + + + static const struct pci_device_id snd_hdspm_ids[] = { +- { +- .vendor = PCI_VENDOR_ID_XILINX, +- .device = PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI, +- .subvendor = PCI_ANY_ID, +- .subdevice = PCI_ANY_ID, +- .class = 0, +- .class_mask = 0, +- .driver_data = 0}, ++ { PCI_DEVICE(PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI) }, ++ { PCI_DEVICE(0x1d18, 0x3fc6) }, /* RME HDSPe AIO PCI express audio */ + {0,} + }; + +diff --git a/sound/usb/hiface/pcm.c b/sound/usb/hiface/pcm.c +--- a/sound/usb/hiface/pcm.c ++++ b/sound/usb/hiface/pcm.c +@@ -24,7 +24,7 @@ + struct pcm_urb { + struct hiface_chip *chip; + +- struct urb instance; ++ struct urb *instance; + struct usb_anchor submitted; + u8 *buffer; + }; +@@ -193,7 +193,7 @@ static void hiface_pcm_stream_stop(struct pcm_runtime *rt) + if (!time) + usb_kill_anchored_urbs( + &rt->out_urbs[i].submitted); +- usb_kill_urb(&rt->out_urbs[i].instance); ++ usb_kill_urb(rt->out_urbs[i].instance); + } + + rt->stream_state = STREAM_DISABLED; +@@ -215,9 +215,9 @@ static int hiface_pcm_stream_start(struct pcm_runtime *rt) + rt->stream_state = STREAM_STARTING; + for (i = 0; i < PCM_N_URBS; i++) { + memset(rt->out_urbs[i].buffer, 0, PCM_PACKET_SIZE); +- usb_anchor_urb(&rt->out_urbs[i].instance, ++ usb_anchor_urb(rt->out_urbs[i].instance, + &rt->out_urbs[i].submitted); +- ret = usb_submit_urb(&rt->out_urbs[i].instance, ++ ret = usb_submit_urb(rt->out_urbs[i].instance, + GFP_ATOMIC); + if (ret) { + hiface_pcm_stream_stop(rt); +@@ -334,7 +334,7 @@ static void hiface_pcm_out_urb_handler(struct urb *usb_urb) + if (do_period_elapsed) + snd_pcm_period_elapsed(sub->instance); + +- ret = usb_submit_urb(&out_urb->instance, GFP_ATOMIC); ++ ret = usb_submit_urb(out_urb->instance, GFP_ATOMIC); + if (ret < 0) + goto out_fail; + +@@ -492,16 +492,18 @@ static int hiface_pcm_init_urb(struct pcm_urb *urb, + void (*handler)(struct urb *)) + { + urb->chip = chip; +- usb_init_urb(&urb->instance); ++ urb->instance = usb_alloc_urb(0, GFP_KERNEL); ++ if (!urb->instance) ++ return -ENOMEM; + + urb->buffer = kzalloc(PCM_PACKET_SIZE, GFP_KERNEL); + if (!urb->buffer) + return -ENOMEM; + +- usb_fill_bulk_urb(&urb->instance, chip->dev, ++ usb_fill_bulk_urb(urb->instance, chip->dev, + usb_sndbulkpipe(chip->dev, ep), (void *)urb->buffer, + PCM_PACKET_SIZE, handler, urb); +- if (usb_urb_ep_type_check(&urb->instance)) ++ if (usb_urb_ep_type_check(urb->instance)) + return -EINVAL; + init_usb_anchor(&urb->submitted); + +@@ -520,24 +522,26 @@ void hiface_pcm_abort(struct hiface_chip *chip) + } + } + +-static void hiface_pcm_destroy(struct hiface_chip *chip) ++static void hiface_pcm_destroy(struct pcm_runtime *rt) + { +- struct pcm_runtime *rt = chip->pcm; + int i; + +- for (i = 0; i < PCM_N_URBS; i++) +- kfree(rt->out_urbs[i].buffer); ++ if (!rt) ++ return; + +- kfree(chip->pcm); +- chip->pcm = NULL; ++ if (rt->chip) ++ rt->chip->pcm = NULL; ++ ++ for (i = 0; i < PCM_N_URBS; i++) { ++ usb_free_urb(rt->out_urbs[i].instance); ++ kfree(rt->out_urbs[i].buffer); ++ } ++ kfree(rt); + } + + static void hiface_pcm_free(struct snd_pcm *pcm) + { +- struct pcm_runtime *rt = pcm->private_data; +- +- if (rt) +- hiface_pcm_destroy(rt->chip); ++ hiface_pcm_destroy(pcm->private_data); + } + + int hiface_pcm_init(struct hiface_chip *chip, u8 extra_freq) +@@ -587,8 +591,6 @@ int hiface_pcm_init(struct hiface_chip *chip, u8 extra_freq) + return 0; + + error: +- for (i = 0; i < PCM_N_URBS; i++) +- kfree(rt->out_urbs[i].buffer); +- kfree(rt); ++ hiface_pcm_destroy(rt); + return ret; + } +diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c +--- a/sound/usb/misc/ua101.c ++++ b/sound/usb/misc/ua101.c +@@ -109,10 +109,10 @@ struct ua101 { + unsigned int buffer_pos; + unsigned int queue_length; + struct ua101_urb { +- struct urb urb; +- struct usb_iso_packet_descriptor iso_frame_desc[1]; ++ struct urb *urb; + struct list_head ready_list; +- } *urbs[MAX_QUEUE_LENGTH]; ++ struct ua101 *ua; ++ } urbs[MAX_QUEUE_LENGTH]; + struct { + unsigned int size; + void *addr; +@@ -167,15 +167,15 @@ static void abort_usb_playback(struct ua101 *ua) + wake_up(&ua->alsa_playback_wait); + } + +-static void playback_urb_complete(struct urb *usb_urb) ++static void playback_urb_complete(struct urb *urb) + { +- struct ua101_urb *urb = (struct ua101_urb *)usb_urb; +- struct ua101 *ua = urb->urb.context; ++ struct ua101_urb *ua_urb = urb->context; ++ struct ua101 *ua = ua_urb->ua; + +- if (unlikely(urb->urb.status == -ENOENT || /* unlinked */ +- urb->urb.status == -ENODEV || /* device removed */ +- urb->urb.status == -ECONNRESET || /* unlinked */ +- urb->urb.status == -ESHUTDOWN)) { /* device disabled */ ++ if (unlikely(urb->status == -ENOENT || /* unlinked */ ++ urb->status == -ENODEV || /* device removed */ ++ urb->status == -ECONNRESET || /* unlinked */ ++ urb->status == -ESHUTDOWN)) { /* device disabled */ + abort_usb_playback(ua); + abort_alsa_playback(ua); + return; +@@ -184,18 +184,19 @@ static void playback_urb_complete(struct urb *usb_urb) + if (test_bit(USB_PLAYBACK_RUNNING, &ua->states)) { + /* append URB to FIFO */ + guard(spinlock_irqsave)(&ua->lock); +- list_add_tail(&urb->ready_list, &ua->ready_playback_urbs); ++ list_add_tail(&ua_urb->ready_list, &ua->ready_playback_urbs); + if (ua->rate_feedback_count > 0) + queue_work(system_highpri_wq, &ua->playback_work); + ua->playback.substream->runtime->delay -= +- urb->urb.iso_frame_desc[0].length / ++ urb->iso_frame_desc[0].length / + ua->playback.frame_bytes; + } + } + + static void first_playback_urb_complete(struct urb *urb) + { +- struct ua101 *ua = urb->context; ++ struct ua101_urb *ua_urb = urb->context; ++ struct ua101 *ua = ua_urb->ua; + + urb->complete = playback_urb_complete; + playback_urb_complete(urb); +@@ -248,7 +249,8 @@ static void playback_work(struct work_struct *work) + { + struct ua101 *ua = container_of(work, struct ua101, playback_work); + unsigned int frames; +- struct ua101_urb *urb; ++ struct ua101_urb *ua_urb; ++ struct urb *urb; + bool do_period_elapsed = false; + int err; + +@@ -275,23 +277,24 @@ static void playback_work(struct work_struct *work) + ua->rate_feedback_count--; + + /* take URB out of FIFO */ +- urb = list_first_entry(&ua->ready_playback_urbs, +- struct ua101_urb, ready_list); +- list_del(&urb->ready_list); ++ ua_urb = list_first_entry(&ua->ready_playback_urbs, ++ struct ua101_urb, ready_list); ++ list_del(&ua_urb->ready_list); ++ urb = ua_urb->urb; + + /* fill packet with data or silence */ +- urb->urb.iso_frame_desc[0].length = ++ urb->iso_frame_desc[0].length = + frames * ua->playback.frame_bytes; + if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states)) + do_period_elapsed |= copy_playback_data(&ua->playback, +- &urb->urb, ++ urb, + frames); + else +- memset(urb->urb.transfer_buffer, 0, +- urb->urb.iso_frame_desc[0].length); ++ memset(urb->transfer_buffer, 0, ++ urb->iso_frame_desc[0].length); + + /* and off you go ... */ +- err = usb_submit_urb(&urb->urb, GFP_ATOMIC); ++ err = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(err < 0)) { + abort_usb_playback(ua); + abort_alsa_playback(ua); +@@ -342,7 +345,8 @@ static bool copy_capture_data(struct ua101_stream *stream, struct urb *urb, + + static void capture_urb_complete(struct urb *urb) + { +- struct ua101 *ua = urb->context; ++ struct ua101_urb *ua_urb = urb->context; ++ struct ua101 *ua = ua_urb->ua; + struct ua101_stream *stream = &ua->capture; + unsigned int frames, write_ptr; + bool do_period_elapsed; +@@ -413,7 +417,8 @@ static void capture_urb_complete(struct urb *urb) + + static void first_capture_urb_complete(struct urb *urb) + { +- struct ua101 *ua = urb->context; ++ struct ua101_urb *ua_urb = urb->context; ++ struct ua101 *ua = ua_urb->ua; + + urb->complete = capture_urb_complete; + capture_urb_complete(urb); +@@ -427,7 +432,7 @@ static int submit_stream_urbs(struct ua101 *ua, struct ua101_stream *stream) + unsigned int i; + + for (i = 0; i < stream->queue_length; ++i) { +- int err = usb_submit_urb(&stream->urbs[i]->urb, GFP_KERNEL); ++ int err = usb_submit_urb(stream->urbs[i].urb, GFP_KERNEL); + if (err < 0) { + dev_err(&ua->dev->dev, "USB request error %d: %s\n", + err, usb_error_string(err)); +@@ -442,8 +447,8 @@ static void kill_stream_urbs(struct ua101_stream *stream) + unsigned int i; + + for (i = 0; i < stream->queue_length; ++i) +- if (stream->urbs[i]) +- usb_kill_urb(&stream->urbs[i]->urb); ++ if (stream->urbs[i].urb) ++ usb_kill_urb(stream->urbs[i].urb); + } + + static int enable_iso_interface(struct ua101 *ua, unsigned int intf_index) +@@ -508,7 +513,7 @@ static int start_usb_capture(struct ua101 *ua) + return err; + + clear_bit(CAPTURE_URB_COMPLETED, &ua->states); +- ua->capture.urbs[0]->urb.complete = first_capture_urb_complete; ++ ua->capture.urbs[0].urb->complete = first_capture_urb_complete; + ua->rate_feedback_start = 0; + ua->rate_feedback_count = 0; + +@@ -550,7 +555,7 @@ static int start_usb_playback(struct ua101 *ua) + return err; + + clear_bit(PLAYBACK_URB_COMPLETED, &ua->states); +- ua->playback.urbs[0]->urb.complete = ++ ua->playback.urbs[0].urb->complete = + first_playback_urb_complete; + scoped_guard(spinlock_irq, &ua->lock) { + INIT_LIST_HEAD(&ua->ready_playback_urbs); +@@ -580,7 +585,7 @@ static int start_usb_playback(struct ua101 *ua) + add_with_wraparound(ua, &ua->rate_feedback_start, 1); + ua->rate_feedback_count--; + } +- urb = &ua->playback.urbs[i]->urb; ++ urb = ua->playback.urbs[i].urb; + urb->iso_frame_desc[0].length = + frames * ua->playback.frame_bytes; + memset(urb->transfer_buffer, 0, +@@ -1059,7 +1064,7 @@ static int alloc_stream_urbs(struct ua101 *ua, struct ua101_stream *stream, + void (*urb_complete)(struct urb *)) + { + unsigned max_packet_size = stream->max_packet_bytes; +- struct ua101_urb *urb; ++ struct urb *urb; + unsigned int b, u = 0; + + for (b = 0; b < ARRAY_SIZE(stream->buffers); ++b) { +@@ -1070,23 +1075,24 @@ static int alloc_stream_urbs(struct ua101 *ua, struct ua101_stream *stream, + while (size >= max_packet_size) { + if (u >= stream->queue_length) + goto bufsize_error; +- urb = kmalloc_obj(*urb); ++ urb = usb_alloc_urb(1, GFP_KERNEL); + if (!urb) + return -ENOMEM; +- usb_init_urb(&urb->urb); +- urb->urb.dev = ua->dev; +- urb->urb.pipe = stream->usb_pipe; +- urb->urb.transfer_flags = URB_NO_TRANSFER_DMA_MAP; +- urb->urb.transfer_buffer = addr; +- urb->urb.transfer_dma = dma; +- urb->urb.transfer_buffer_length = max_packet_size; +- urb->urb.number_of_packets = 1; +- urb->urb.interval = 1; +- urb->urb.context = ua; +- urb->urb.complete = urb_complete; +- urb->urb.iso_frame_desc[0].offset = 0; +- urb->urb.iso_frame_desc[0].length = max_packet_size; +- stream->urbs[u++] = urb; ++ urb->dev = ua->dev; ++ urb->pipe = stream->usb_pipe; ++ urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; ++ urb->transfer_buffer = addr; ++ urb->transfer_dma = dma; ++ urb->transfer_buffer_length = max_packet_size; ++ urb->number_of_packets = 1; ++ urb->interval = 1; ++ urb->context = &stream->urbs[u]; ++ urb->complete = urb_complete; ++ urb->iso_frame_desc[0].offset = 0; ++ urb->iso_frame_desc[0].length = max_packet_size; ++ stream->urbs[u].ua = ua; ++ stream->urbs[u].urb = urb; ++ u++; + size -= max_packet_size; + addr += max_packet_size; + dma += max_packet_size; +@@ -1104,8 +1110,8 @@ static void free_stream_urbs(struct ua101_stream *stream) + unsigned int i; + + for (i = 0; i < stream->queue_length; ++i) { +- kfree(stream->urbs[i]); +- stream->urbs[i] = NULL; ++ usb_free_urb(stream->urbs[i].urb); ++ stream->urbs[i].urb = NULL; + } + } + +diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c +--- a/sound/usb/quirks.c ++++ b/sound/usb/quirks.c +@@ -849,6 +849,27 @@ static int snd_usb_accessmusic_boot_quirk(struct usb_device *dev) + return 0; + } + ++/* ++ * A post configuration device descriptor read is needed to make the CM1A ++ * operational after reenumeration. ++ */ ++static int snd_usb_cm1a_boot_quirk(struct usb_device *dev) ++{ ++ struct usb_device_descriptor *desc __free(kfree) = kmalloc_obj(*desc); ++ int err; ++ ++ if (!desc) ++ return -ENOMEM; ++ ++ err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, sizeof(*desc)); ++ if (err < 0) { ++ dev_err(&dev->dev, "failed to read device descriptor: %d\n", err); ++ return err; ++ } ++ ++ return 0; ++} ++ + /* + * Some sound cards from Native Instruments are in fact compliant to the USB + * audio standard of version 2 and other approved USB standards, even though +@@ -1681,6 +1702,8 @@ int snd_usb_apply_boot_quirk_once(struct usb_device *dev, + switch (id) { + case USB_ID(0x07fd, 0x0008): /* MOTU M Series, 1st hardware version */ + return snd_usb_motu_m_series_boot_quirk(dev); ++ case USB_ID(0x1397, 0x1234): /* Behringer CM1A */ ++ return snd_usb_cm1a_boot_quirk(dev); + } + + return 0; +@@ -2390,6 +2413,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + QUIRK_FLAG_PLAYBACK_FIRST | QUIRK_FLAG_GENERIC_IMPLICIT_FB), + DEVICE_FLG(0x1397, 0x050c, /* Behringer Flow 8 */ + QUIRK_FLAG_IFB_SILENCE_ON_EMPTY), ++ DEVICE_FLG(0x1397, 0x0510, /* Behringer UV1 */ ++ QUIRK_FLAG_PLAYBACK_FIRST | QUIRK_FLAG_GENERIC_IMPLICIT_FB), + DEVICE_FLG(0x13e5, 0x0001, /* Serato Phono */ + QUIRK_FLAG_IGNORE_CTL_ERROR), + DEVICE_FLG(0x152a, 0x85dd, /* SMSL USB DAC */ diff --git a/pkgbuilds/linux-omarchy/0522-sound-fixes-3.patch.sig b/pkgbuilds/linux-omarchy/0522-sound-fixes-3.patch.sig new file mode 100644 index 0000000..59a91ae Binary files /dev/null and b/pkgbuilds/linux-omarchy/0522-sound-fixes-3.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0523-asoc-fixes-2.patch b/pkgbuilds/linux-omarchy/0523-asoc-fixes-2.patch new file mode 100644 index 0000000..b65c5d4 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0523-asoc-fixes-2.patch @@ -0,0 +1,1040 @@ +diff --git a/sound/hda/core/ext/controller.c b/sound/hda/core/ext/controller.c +--- a/sound/hda/core/ext/controller.c ++++ b/sound/hda/core/ext/controller.c +@@ -90,8 +90,10 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_bus *bus) + + for (idx = 0; idx < link_count; idx++) { + hlink = kzalloc_obj(*hlink); +- if (!hlink) ++ if (!hlink) { ++ snd_hdac_ext_link_free_all(bus); + return -ENOMEM; ++ } + hlink->index = idx; + hlink->bus = bus; + hlink->ml_addr = bus->mlcap + AZX_ML_BASE + +diff --git a/sound/hda/core/ext/stream.c b/sound/hda/core/ext/stream.c +--- a/sound/hda/core/ext/stream.c ++++ b/sound/hda/core/ext/stream.c +@@ -102,8 +102,10 @@ int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx, + + for (i = 0; i < num_stream; i++) { + struct hdac_ext_stream *hext_stream = kzalloc_obj(*hext_stream); +- if (!hext_stream) ++ if (!hext_stream) { ++ snd_hdac_ext_stream_free_all(bus); + return -ENOMEM; ++ } + tag = ++stream_tag; + snd_hdac_ext_stream_init(bus, hext_stream, idx, dir, tag); + idx++; +@@ -111,7 +113,6 @@ int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx, + } + + return 0; +- + } + EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all); + +diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c +--- a/sound/soc/amd/yc/acp6x-mach.c ++++ b/sound/soc/amd/yc/acp6x-mach.c +@@ -388,6 +388,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { + DMI_MATCH(DMI_PRODUCT_NAME, "Nitro ANV15-41"), + } + }, ++ { ++ .driver_data = &acp6x_card, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "RB"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Nitro AN17-41"), ++ } ++ }, + { + .driver_data = &acp6x_card, + .matches = { +@@ -899,6 +906,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { + DMI_MATCH(DMI_BOARD_NAME, "TM2423"), + } + }, ++ { ++ .driver_data = &acp6x_card, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "HP 255R 15.6 inch G10 Notebook PC"), ++ } ++ }, + {} + }; + +diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c +--- a/sound/soc/codecs/ab8500-codec.c ++++ b/sound/soc/codecs/ab8500-codec.c +@@ -14,17 +14,14 @@ + * for ST-Ericsson. + */ + +-#include + #include + #include + #include + #include + #include + #include +-#include + #include + #include +-#include + #include + #include + #include +@@ -54,32 +51,9 @@ + /* Macrocell register definitions */ + #define AB8500_GPIO_DIR4_REG 0x13 /* Bank AB8500_MISC */ + +-/* Nr of FIR/IIR-coeff banks in ANC-block */ +-#define AB8500_NR_OF_ANC_COEFF_BANKS 2 +- +-/* Minimum duration to keep ANC IIR Init bit high or +-low before proceeding with the configuration sequence */ +-#define AB8500_ANC_SM_DELAY 2000 +- +-/* Sidetone states */ +-static const char * const enum_sid_state[] = { +- "Unconfigured", +- "Apply FIR", +- "FIR is configured", +-}; +-enum sid_state { +- SID_UNCONFIGURED = 0, +- SID_APPLY_FIR = 1, +- SID_FIR_CONFIGURED = 2, +-}; +- + /* Private data for AB8500 device-driver */ + struct ab8500_codec_drvdata { + struct regmap *regmap; +- struct mutex ctrl_lock; +- +- /* Sidetone */ +- enum sid_state sid_status; + }; + + static inline const char *amic_micbias_str(enum amic_micbias micbias) +@@ -642,9 +616,6 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = { + NULL, 0), + /* Acoustical Noise Cancellation path */ + +- SND_SOC_DAPM_INPUT("ANC Configure Input"), +- SND_SOC_DAPM_OUTPUT("ANC Configure Output"), +- + SND_SOC_DAPM_MUX("ANC Source", + SND_SOC_NOPM, 0, 0, + dapm_anc_in_select), +@@ -687,10 +658,6 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { + {"Main Supply", NULL, "Audio Power"}, + {"Main Supply", NULL, "Audio Analog Power"}, + +- /* ANC Configure */ +- {"ANC Configure Input", NULL, "Main Supply"}, +- {"ANC Configure Output", NULL, "ANC Configure Input"}, +- + /* Powerup charge pump if DA1/2 is in use */ + + {"DA_IN1", NULL, "ab8500_0p"}, +@@ -972,75 +939,6 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes_mic2_vamicx[] = { + {"MIC2 V-AMICx Enable", NULL, "V-AMIC2"}, + }; + +-/* +- * Control-events +- */ +- +-static int sid_status_control_get(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); +- struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev); +- +- guard(mutex)(&drvdata->ctrl_lock); +- ucontrol->value.enumerated.item[0] = drvdata->sid_status; +- +- return 0; +-} +- +-/* Write sidetone FIR-coefficients configuration sequence */ +-static int sid_status_control_put(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); +- struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev); +- unsigned int param, sidconf; +- int status = 1; +- +- dev_dbg(component->dev, "%s: Enter\n", __func__); +- +- if (ucontrol->value.enumerated.item[0] != SID_APPLY_FIR) { +- dev_err(component->dev, +- "%s: ERROR: This control supports '%s' only!\n", +- __func__, enum_sid_state[SID_APPLY_FIR]); +- return -EIO; +- } +- +- guard(mutex)(&drvdata->ctrl_lock); +- +- sidconf = snd_soc_component_read(component, AB8500_SIDFIRCONF); +- if (((sidconf & BIT(AB8500_SIDFIRCONF_FIRSIDBUSY)) != 0)) { +- if ((sidconf & BIT(AB8500_SIDFIRCONF_ENFIRSIDS)) == 0) { +- dev_err(component->dev, "%s: Sidetone busy while off!\n", +- __func__); +- status = -EPERM; +- } else { +- status = -EBUSY; +- } +- dev_dbg(component->dev, "%s: Exit\n", __func__); +- return status; +- } +- +- snd_soc_component_write(component, AB8500_SIDFIRADR, 0); +- +- for (param = 0; param < AB8500_SID_FIR_COEFFS; param++) { +- snd_soc_component_write(component, AB8500_SIDFIRCOEF1, 0); +- snd_soc_component_write(component, AB8500_SIDFIRCOEF2, 0); +- } +- +- snd_soc_component_update_bits(component, AB8500_SIDFIRADR, +- BIT(AB8500_SIDFIRADR_FIRSIDSET), +- BIT(AB8500_SIDFIRADR_FIRSIDSET)); +- snd_soc_component_update_bits(component, AB8500_SIDFIRADR, +- BIT(AB8500_SIDFIRADR_FIRSIDSET), 0); +- +- drvdata->sid_status = SID_FIR_CONFIGURED; +- +- dev_dbg(component->dev, "%s: Exit\n", __func__); +- +- return status; +-} +- + /* + * Controls - Non-DAPM ASoC + */ +@@ -1324,9 +1222,6 @@ static SOC_ENUM_SINGLE_DECL(soc_enum_bfifomast, + AB8500_FIFOCONF3, AB8500_FIFOCONF3_BFIFOMAST_SHIFT, + enum_slavemaster); + +-/* Sidetone */ +-static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_sidstate, enum_sid_state); +- + /* ANC */ + + static struct snd_kcontrol_new ab8500_ctrls[] = { +@@ -1617,8 +1512,6 @@ static struct snd_kcontrol_new ab8500_ctrls[] = { + AB8500_ANC_WARP_DELAY_MIN, AB8500_ANC_WARP_DELAY_MAX, 0), + + /* Sidetone */ +- SOC_ENUM_EXT("Sidetone Status", soc_enum_sidstate, +- sid_status_control_get, sid_status_control_put), + SOC_SINGLE_STROBE("Sidetone Reset", + AB8500_SIDFIRADR, AB8500_SIDFIRADR_FIRSIDSET, 0), + }; +@@ -2148,10 +2041,8 @@ static void ab8500_codec_of_probe(struct device *dev, struct device_node *np, + + static int ab8500_codec_probe(struct snd_soc_component *component) + { +- struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); + struct device *dev = component->dev; + struct device_node *np = dev->of_node; +- struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(dev); + struct ab8500_codec_platform_data codec_pdata; + int status; + +@@ -2184,10 +2075,6 @@ static int ab8500_codec_probe(struct snd_soc_component *component) + snd_soc_component_write(component, AB8500_SHORTCIRCONF, + BIT(AB8500_SHORTCIRCONF_HSZCDDIS)); + +- snd_soc_dapm_disable_pin(dapm, "ANC Configure Input"); +- +- mutex_init(&drvdata->ctrl_lock); +- + return status; + } + +@@ -2216,7 +2103,6 @@ static int ab8500_codec_driver_probe(struct platform_device *pdev) + GFP_KERNEL); + if (!drvdata) + return -ENOMEM; +- drvdata->sid_status = SID_UNCONFIGURED; + dev_set_drvdata(&pdev->dev, drvdata); + + drvdata->regmap = devm_regmap_init(&pdev->dev, NULL, &pdev->dev, +diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c +--- a/sound/soc/codecs/cs35l56-sdw.c ++++ b/sound/soc/codecs/cs35l56-sdw.c +@@ -386,11 +386,8 @@ static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev) + { + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + +- if (!cs35l56->base.init_done) +- return 0; +- +- /* runtime_resume unmasks the interrupt */ +- cs35l56_mask_soundwire_interrupts(cs35l56); ++ if (cs35l56->sdw_attached) ++ cs35l56_mask_soundwire_interrupts(cs35l56); + + return cs35l56_system_suspend(dev); + } +diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c +--- a/sound/soc/codecs/cs35l56.c ++++ b/sound/soc/codecs/cs35l56.c +@@ -18,9 +18,11 @@ + #include + #include + #include ++#include + #include + #include + #include ++#include + #include + #include + #include +@@ -37,6 +39,13 @@ + #include "wm_adsp.h" + #include "cs35l56.h" + ++/* ++ * snd_soc_register_component() can call component_probe() on all instances ++ * in a card, so deferred registration must be protected across all instances. ++ */ ++static DEFINE_MUTEX(cs35l56_component_register_lock); ++static bool cs35l56_shutting_down; ++ + void cs35l56_mask_soundwire_interrupts(struct cs35l56_private *cs35l56) + { + /* +@@ -1957,6 +1966,11 @@ static void cs35l56_component_register_work(struct work_struct *work) + component_register_work); + int ret; + ++ guard(mutex)(&cs35l56_component_register_lock); ++ ++ if (cs35l56_shutting_down) ++ return; ++ + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(cs35l56->base.dev, pm_err); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm_err); + if (ret) { +@@ -2217,6 +2231,37 @@ EXPORT_NS_GPL_DEV_PM_OPS(cs35l56_pm_ops_i2c_spi, SND_SOC_CS35L56_CORE) = { + }; + #endif + ++static int cs35l56_reboot_notify(struct notifier_block *nb, ++ unsigned long action, void *data) ++{ ++ guard(mutex)(&cs35l56_component_register_lock); ++ cs35l56_shutting_down = true; ++ ++ return NOTIFY_DONE; ++} ++ ++static struct notifier_block cs35l56_reboot_notifier = { ++ .notifier_call = cs35l56_reboot_notify, ++}; ++ ++static int __init cs35l56_modinit(void) ++{ ++ /* ++ * Use reboot notifier to prevent race between shutdown and ++ * snd_soc_register_component(). Driver shutdown() callback would ++ * run too late, after device_shutdown() is already walking the ++ * device list that component registration can modify. ++ */ ++ return register_reboot_notifier(&cs35l56_reboot_notifier); ++} ++module_init(cs35l56_modinit); ++ ++static void __exit cs35l56_modexit(void) ++{ ++ unregister_reboot_notifier(&cs35l56_reboot_notifier); ++} ++module_exit(cs35l56_modexit); ++ + MODULE_DESCRIPTION("ASoC CS35L56 driver"); + MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED"); + MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB"); +diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h +--- a/sound/soc/codecs/cs35l56.h ++++ b/sound/soc/codecs/cs35l56.h +@@ -32,6 +32,7 @@ struct sdw_slave; + struct cs35l56_private { + struct wm_adsp dsp; /* must be first member */ + struct cs35l56_base base; ++ struct work_struct component_register_work; + struct work_struct dsp_work; + struct workqueue_struct *dsp_wq; + struct snd_soc_component *component; +@@ -41,6 +42,7 @@ struct cs35l56_private { + const char *fallback_fw_suffix; + bool soft_resetting; + bool sdw_attached; ++ bool component_registered; + struct completion init_completion; + + int speaker_id; +diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c +--- a/sound/soc/codecs/es8326.c ++++ b/sound/soc/codecs/es8326.c +@@ -26,6 +26,7 @@ struct es8326_priv { + struct snd_soc_component *component; + struct delayed_work jack_detect_work; + struct delayed_work button_press_work; ++ struct delayed_work capture_pop_work; + struct snd_soc_jack *jack; + int irq; + /* The lock protects the situation that an irq is generated +@@ -628,6 +629,7 @@ static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction) + regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, + 0x30, 0x00); + } else { ++ cancel_delayed_work_sync(&es8326->capture_pop_work); + regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE, + 0x0F, 0x0F); + if (es8326->version > ES8326_VERSION_B) { +@@ -666,8 +668,9 @@ static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction) + regmap_update_bits(es8326->regmap, ES8326_ANA_MICBIAS, 0x70, 0x70); + regmap_update_bits(es8326->regmap, ES8326_VMIDSEL, 0x40, 0x00); + } +- regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE, +- 0x0F, 0x00); ++ ++ queue_delayed_work(system_dfl_wq, &es8326->capture_pop_work, ++ msecs_to_jiffies(40)); + } + } + return 0; +@@ -773,6 +776,15 @@ static void es8326_disable_micbias(struct snd_soc_component *component) + snd_soc_dapm_mutex_unlock(dapm); + } + ++static void es8326_capture_pop_handler(struct work_struct *work) ++{ ++ struct es8326_priv *es8326 = ++ container_of(work, struct es8326_priv, capture_pop_work.work); ++ ++ regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE, ++ 0x0F, 0x00); ++} ++ + /* + * For button detection, set the following in soundcard + * snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); +@@ -1140,6 +1152,7 @@ static int es8326_suspend(struct snd_soc_component *component) + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + + cancel_delayed_work_sync(&es8326->jack_detect_work); ++ cancel_delayed_work_sync(&es8326->capture_pop_work); + es8326_disable_micbias(component); + es8326->calibrated = false; + regmap_write(es8326->regmap, ES8326_CLK_MUX, 0x2d); +@@ -1291,6 +1304,8 @@ static int es8326_i2c_probe(struct i2c_client *i2c) + es8326_jack_detect_handler); + INIT_DELAYED_WORK(&es8326->button_press_work, + es8326_jack_button_handler); ++ INIT_DELAYED_WORK(&es8326->capture_pop_work, ++ es8326_capture_pop_handler); + /* ES8316 is level-based while ES8326 is edge-based */ + ret = devm_request_threaded_irq(&i2c->dev, es8326->irq, NULL, es8326_irq, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, +diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c +--- a/sound/soc/codecs/es8389.c ++++ b/sound/soc/codecs/es8389.c +@@ -812,6 +812,23 @@ static int es8389_pcm_hw_free(struct snd_pcm_substream *substream, + return 0; + } + ++static void es8389_standby(struct snd_soc_component *component) ++{ ++ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); ++ ++ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x04); ++ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x04); ++ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xD4); ++ usleep_range(70000, 72000); ++ regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x59); ++ regmap_write(es8389->regmap, ES8389_ADC_EN, 0x00); ++ regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0x00); ++ regmap_write(es8389->regmap, ES8389_RESET, 0x3E); ++ regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x80); ++ usleep_range(8000, 8500); ++ regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x00); ++} ++ + static int es8389_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) + { +@@ -834,18 +851,7 @@ static int es8389_set_bias_level(struct snd_soc_component *component, + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: +- regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x04); +- regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x04); +- regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xD4); +- usleep_range(70000, 72000); +- regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x59); +- regmap_write(es8389->regmap, ES8389_ADC_EN, 0x00); +- regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0x00); +- regmap_write(es8389->regmap, ES8389_RESET, 0x3E); +- regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x80); +- usleep_range(8000, 8500); +- regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x00); +- ++ es8389_standby(component); + clk_disable_unprepare(es8389->mclk); + break; + case SND_SOC_BIAS_OFF: +@@ -1015,7 +1021,7 @@ static int es8389_suspend(struct snd_soc_component *component) + { + struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); + +- es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY); ++ es8389_standby(component); + regcache_cache_only(es8389->regmap, true); + regcache_mark_dirty(es8389->regmap); + +@@ -1084,7 +1090,7 @@ static int es8389_probe(struct snd_soc_component *component) + + es8389->hpf_freq = ES8389_HPF_DEFAULT; + es8389_init(component); +- es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY); ++ es8389_standby(component); + + return 0; + } +diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c +--- a/sound/soc/codecs/rt1318-sdw.c ++++ b/sound/soc/codecs/rt1318-sdw.c +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + #include + #include "rt1318-sdw.h" + +@@ -564,11 +565,10 @@ static int rt1318_sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_soc_component *component = dai->component; + struct rt1318_sdw_priv *rt1318 = + snd_soc_component_get_drvdata(component); +- struct sdw_stream_config stream_config; ++ struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config; +- enum sdw_data_direction direction; + struct sdw_stream_runtime *sdw_stream; +- int retval, port, num_channels, ch_mask; ++ int retval, port; + unsigned int sampling_rate; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); +@@ -582,23 +582,13 @@ static int rt1318_sdw_hw_params(struct snd_pcm_substream *substream, + + /* SoundWire specific configuration */ + /* port 1 for playback */ +- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +- direction = SDW_DATA_DIR_RX; ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + port = 1; +- } else { +- direction = SDW_DATA_DIR_TX; ++ else + port = 2; +- } + +- num_channels = params_channels(params); +- ch_mask = (1 << num_channels) - 1; +- +- stream_config.frame_rate = params_rate(params); +- stream_config.ch_count = num_channels; +- stream_config.bps = snd_pcm_format_width(params_format(params)); +- stream_config.direction = direction; +- +- port_config.ch_mask = ch_mask; ++ /* SoundWire specific configuration */ ++ snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + port_config.num = port; + + retval = sdw_stream_add_slave(rt1318->sdw_slave, &stream_config, +diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c +--- a/sound/soc/codecs/rt1320-sdw.c ++++ b/sound/soc/codecs/rt1320-sdw.c +@@ -3246,7 +3246,7 @@ static int rt1320_sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_soc_component *component = dai->component; + struct rt1320_sdw_priv *rt1320 = + snd_soc_component_get_drvdata(component); +- struct sdw_stream_config stream_config; ++ struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config; + struct sdw_port_config dmic_port_config[2]; + struct sdw_stream_runtime *sdw_stream; +diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c +--- a/sound/soc/codecs/rt712-sdca.c ++++ b/sound/soc/codecs/rt712-sdca.c +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + #include + #include "rt712-sdca.h" + +@@ -1449,11 +1450,10 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + { + struct snd_soc_component *component = dai->component; + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); +- struct sdw_stream_config stream_config; ++ struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config; +- enum sdw_data_direction direction; + struct sdw_stream_runtime *sdw_stream; +- int retval, port, num_channels; ++ int retval, port; + unsigned int sampling_rate; + + dev_dbg(dai->dev, "%s %s id %d", __func__, dai->name, dai->id); +@@ -1471,7 +1471,6 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + + /* SoundWire specific configuration */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +- direction = SDW_DATA_DIR_RX; + if (dai->id == RT712_AIF1) + port = 1; + else if (dai->id == RT712_AIF2) +@@ -1479,7 +1478,6 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + else + return -EINVAL; + } else { +- direction = SDW_DATA_DIR_TX; + if (dai->id == RT712_AIF1) + port = 4; + else if (dai->id == RT712_AIF3) +@@ -1488,13 +1486,8 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + return -EINVAL; + } + +- stream_config.frame_rate = params_rate(params); +- stream_config.ch_count = params_channels(params); +- stream_config.bps = snd_pcm_format_width(params_format(params)); +- stream_config.direction = direction; +- +- num_channels = params_channels(params); +- port_config.ch_mask = GENMASK(num_channels - 1, 0); ++ /* SoundWire specific configuration */ ++ snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + port_config.num = port; + + retval = sdw_stream_add_slave(rt712->slave, &stream_config, +diff --git a/sound/soc/codecs/rt721-sdca.c b/sound/soc/codecs/rt721-sdca.c +--- a/sound/soc/codecs/rt721-sdca.c ++++ b/sound/soc/codecs/rt721-sdca.c +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + #include + + #include "rt721-sdca.h" +@@ -206,6 +207,7 @@ static void rt721_sdca_amp_preset(struct rt721_sdca_priv *rt721) + regmap_write(rt721->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55, + RT721_SDCA_CTL_FU_MUTE, CH_02), 0x00); ++ regmap_write(rt721->regmap, 0x2f5d, 0x1); + } + + static void rt721_sdca_jack_preset(struct rt721_sdca_priv *rt721) +@@ -1268,11 +1270,10 @@ static int rt721_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + { + struct snd_soc_component *component = dai->component; + struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); +- struct sdw_stream_config stream_config; ++ struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config; +- enum sdw_data_direction direction; + struct sdw_stream_runtime *sdw_stream; +- int retval, port, num_channels; ++ int retval, port; + unsigned int sampling_rate; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); +@@ -1291,7 +1292,6 @@ static int rt721_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + * RT721_AIF3 with port = 6 for digital-mic capture + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +- direction = SDW_DATA_DIR_RX; + if (dai->id == RT721_AIF1) + port = 1; + else if (dai->id == RT721_AIF2) +@@ -1299,7 +1299,6 @@ static int rt721_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + else + return -EINVAL; + } else { +- direction = SDW_DATA_DIR_TX; + if (dai->id == RT721_AIF1) + port = 2; + else if (dai->id == RT721_AIF3) +@@ -1307,13 +1306,9 @@ static int rt721_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + else + return -EINVAL; + } +- stream_config.frame_rate = params_rate(params); +- stream_config.ch_count = params_channels(params); +- stream_config.bps = snd_pcm_format_width(params_format(params)); +- stream_config.direction = direction; + +- num_channels = params_channels(params); +- port_config.ch_mask = GENMASK(num_channels - 1, 0); ++ /* SoundWire specific configuration */ ++ snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + port_config.num = port; + + retval = sdw_stream_add_slave(rt721->slave, &stream_config, +diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c +--- a/sound/soc/codecs/rt722-sdca.c ++++ b/sound/soc/codecs/rt722-sdca.c +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + #include + + #include "rt722-sdca.h" +@@ -1442,11 +1443,10 @@ static int rt722_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + { + struct snd_soc_component *component = dai->component; + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); +- struct sdw_stream_config stream_config; ++ struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config; +- enum sdw_data_direction direction; + struct sdw_stream_runtime *sdw_stream; +- int retval, port, num_channels; ++ int retval, port; + unsigned int sampling_rate; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); +@@ -1465,7 +1465,6 @@ static int rt722_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + * RT722_AIF3 with port = 6 for digital-mic capture + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +- direction = SDW_DATA_DIR_RX; + if (dai->id == RT722_AIF1) + port = 1; + else if (dai->id == RT722_AIF2) +@@ -1473,7 +1472,6 @@ static int rt722_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + else + return -EINVAL; + } else { +- direction = SDW_DATA_DIR_TX; + if (dai->id == RT722_AIF1) + port = 2; + else if (dai->id == RT722_AIF3) +@@ -1481,13 +1479,9 @@ static int rt722_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + else + return -EINVAL; + } +- stream_config.frame_rate = params_rate(params); +- stream_config.ch_count = params_channels(params); +- stream_config.bps = snd_pcm_format_width(params_format(params)); +- stream_config.direction = direction; + +- num_channels = params_channels(params); +- port_config.ch_mask = GENMASK(num_channels - 1, 0); ++ /* SoundWire specific configuration */ ++ snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + port_config.num = port; + + retval = sdw_stream_add_slave(rt722->slave, &stream_config, +diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c +--- a/sound/soc/codecs/tas2783-sdw.c ++++ b/sound/soc/codecs/tas2783-sdw.c +@@ -1001,6 +1001,31 @@ static s32 tas_sdw_hw_params(struct snd_pcm_substream *substream, + /* SoundWire specific configuration */ + snd_sdw_params_to_config(substream, params, + &stream_config, &port_config); ++ ++ /* ++ * The two mono amps each render one channel of the stereo stream: ++ * snd_sdw_params_to_config() hands every codec the full mask for ++ * playback, which leaves the pair in mirror mode and one channel ++ * unreproduced. Claim a single channel instead, keyed off the ++ * machine-assigned component prefix rather than the SoundWire ++ * address, which is board-specific: soc_sdw_ti_amp.c names the amps ++ * tas2783-1..4. ++ * ++ * Which side an amp then renders does not follow from the bit that ++ * is set - sdw_compute_slave_ports() advances the payload offset by ++ * the popcount of ch_mask and never looks at which bit it is - but ++ * from the amp's position in the codec order of the DAI link, which ++ * on these boards matches the prefix numbering. ++ */ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && ++ params_channels(params) == 2 && component->name_prefix) { ++ const char *idx_str = strrchr(component->name_prefix, '-'); ++ unsigned long idx; ++ ++ if (idx_str && !kstrtoul(idx_str + 1, 10, &idx) && idx) ++ port_config.ch_mask = (idx & 1) ? BIT(0) : BIT(1); ++ } ++ + /* port 1 for playback */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + port_config.num = 1; +diff --git a/sound/soc/intel/avs/boards/hdaudio.c b/sound/soc/intel/avs/boards/hdaudio.c +--- a/sound/soc/intel/avs/boards/hdaudio.c ++++ b/sound/soc/intel/avs/boards/hdaudio.c +@@ -15,6 +15,22 @@ + #include "../../../codecs/hda.h" + #include "../utils.h" + ++static int avs_link_startup(struct snd_pcm_substream *substream) ++{ ++ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); ++ const struct snd_soc_pcm_stream *stream_info; ++ struct snd_soc_dai *codec_dai; ++ ++ codec_dai = snd_soc_rtd_to_codec(rtd, 0); ++ stream_info = snd_soc_dai_get_pcm_stream(codec_dai, substream->stream); ++ ++ return snd_pcm_hw_constraint_msbits(substream->runtime, 0, 0, stream_info->sig_bits); ++} ++ ++static const struct snd_soc_ops avs_link_ops = { ++ .startup = avs_link_startup, ++}; ++ + static int avs_create_dai_links(struct device *dev, struct hda_codec *codec, int pcm_count, + struct snd_soc_dai_link **links) + { +@@ -43,6 +59,7 @@ static int avs_create_dai_links(struct device *dev, struct hda_codec *codec, int + dl[i].platforms = platform; + dl[i].num_platforms = 1; + dl[i].ignore_pmdown_time = 1; ++ dl[i].ops = &avs_link_ops; + + dl[i].codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL); + dl[i].cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL); +diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c +--- a/sound/soc/intel/avs/core.c ++++ b/sound/soc/intel/avs/core.c +@@ -92,16 +92,28 @@ static int avs_hdac_bus_init_streams(struct hdac_bus *bus) + { + unsigned int cp_streams, pb_streams; + unsigned int gcap; ++ int ret; + + gcap = snd_hdac_chip_readw(bus, GCAP); + cp_streams = (gcap >> 8) & 0x0F; + pb_streams = (gcap >> 12) & 0x0F; + bus->num_streams = cp_streams + pb_streams; + +- snd_hdac_ext_stream_init_all(bus, 0, cp_streams, SNDRV_PCM_STREAM_CAPTURE); +- snd_hdac_ext_stream_init_all(bus, cp_streams, pb_streams, SNDRV_PCM_STREAM_PLAYBACK); ++ ret = snd_hdac_ext_stream_init_all(bus, 0, cp_streams, SNDRV_PCM_STREAM_CAPTURE); ++ if (ret) ++ return ret; ++ ret = snd_hdac_ext_stream_init_all(bus, cp_streams, pb_streams, SNDRV_PCM_STREAM_PLAYBACK); ++ if (ret) ++ goto err; + +- return snd_hdac_bus_alloc_stream_pages(bus); ++ ret = snd_hdac_bus_alloc_stream_pages(bus); ++ if (ret) ++ goto err; ++ ++ return 0; ++err: ++ snd_hdac_ext_stream_free_all(bus); ++ return ret; + } + + static bool avs_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset) +@@ -383,6 +395,18 @@ static int avs_bus_init(struct avs_dev *adev, struct pci_dev *pci, const struct + struct device *dev = &pci->dev; + int ret; + ++ ipc = devm_kzalloc(dev, sizeof(*ipc), GFP_KERNEL); ++ if (!ipc) ++ return -ENOMEM; ++ ++ adev->modcfg_buf = devm_kzalloc(dev, AVS_MAILBOX_SIZE, GFP_KERNEL); ++ if (!adev->modcfg_buf) ++ return -ENOMEM; ++ ++ ret = avs_ipc_init(ipc, dev); ++ if (ret < 0) ++ return ret; ++ + ret = snd_hdac_ext_bus_init(&bus->core, dev, NULL, &soc_hda_ext_bus_ops); + if (ret < 0) + return ret; +@@ -394,17 +418,6 @@ static int avs_bus_init(struct avs_dev *adev, struct pci_dev *pci, const struct + bus->mixer_assigned = -1; + mutex_init(&bus->prepare_mutex); + +- ipc = devm_kzalloc(dev, sizeof(*ipc), GFP_KERNEL); +- if (!ipc) +- return -ENOMEM; +- ret = avs_ipc_init(ipc, dev); +- if (ret < 0) +- return ret; +- +- adev->modcfg_buf = devm_kzalloc(dev, AVS_MAILBOX_SIZE, GFP_KERNEL); +- if (!adev->modcfg_buf) +- return -ENOMEM; +- + adev->dev = dev; + adev->spec = (const struct avs_spec *)id->driver_data; + adev->ipc = ipc; +@@ -456,13 +469,14 @@ static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) + + ret = pcim_request_all_regions(pci, "AVS HDAudio"); + if (ret < 0) +- return ret; ++ goto err_request_regions; + + bus->addr = pci_resource_start(pci, 0); + bus->remap_addr = pci_ioremap_bar(pci, 0); + if (!bus->remap_addr) { + dev_err(bus->dev, "ioremap error\n"); +- return -ENXIO; ++ ret = -ENXIO; ++ goto err_request_regions; + } + + adev->dsp_ba = pci_ioremap_bar(pci, 4); +@@ -526,6 +540,8 @@ static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) + iounmap(adev->dsp_ba); + err_remap_bar4: + iounmap(bus->remap_addr); ++err_request_regions: ++ snd_hdac_ext_bus_exit(bus); + return ret; + } + +diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c +--- a/sound/soc/intel/avs/debugfs.c ++++ b/sound/soc/intel/avs/debugfs.c +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -236,15 +237,20 @@ static int strace_open(struct inode *inode, struct file *file) + if (!try_module_get(adev->dev->driver->owner)) + return -ENODEV; + +- if (kfifo_initialized(&adev->trace_fifo)) +- return -EBUSY; ++ if (kfifo_initialized(&adev->trace_fifo)) { ++ ret = -EBUSY; ++ goto err; ++ } + + ret = kfifo_alloc(&adev->trace_fifo, PAGE_SIZE, GFP_KERNEL); + if (ret < 0) +- return ret; ++ goto err; + + file->private_data = adev; + return 0; ++err: ++ module_put(adev->dev->driver->owner); ++ return ret; + } + + static int strace_release(struct inode *inode, struct file *file) +diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c +--- a/sound/soc/intel/avs/ipc.c ++++ b/sound/soc/intel/avs/ipc.c +@@ -172,7 +172,7 @@ static void avs_dsp_exception_caught(struct avs_dev *adev, union avs_notify_msg + + /* Avoid deadlock as the exception may be the response to SET_D0IX. */ + if (current_work() != &ipc->d0ix_work.work) +- cancel_delayed_work_sync(&ipc->d0ix_work); ++ cancel_delayed_work(&ipc->d0ix_work); + ipc->in_d0ix = false; + /* Re-enabled on recovery completion. */ + pm_runtime_disable(adev->dev); +@@ -395,11 +395,11 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request + struct avs_ipc *ipc = adev->ipc; + int ret; + ++ guard(mutex)(&ipc->msg_mutex); ++ + if (!ipc->ready) + return -EPERM; + +- guard(mutex)(&ipc->msg_mutex); +- + spin_lock(&ipc->rx_lock); + avs_ipc_msg_init(ipc, reply); + avs_dsp_send_tx(adev, request, true); +diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig +--- a/sound/soc/intel/boards/Kconfig ++++ b/sound/soc/intel/boards/Kconfig +@@ -533,12 +533,14 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH + select SND_SOC_CS35L56_SPI + select SND_SOC_CS35L56_SDW + select SND_SOC_ES9356 ++ imply SND_SOC_TAC5XX2_SDW + select SND_SOC_DMIC + select SND_SOC_INTEL_HDA_DSP_COMMON + imply SND_SOC_SDW_MOCKUP + help + Add support for Intel SoundWire-based platforms connected to +- MAX98373, RT700, RT711, RT1308 and RT715 ++ MAX98373, RT700, RT711, RT1308, RT715, TAC5XX2_SDW family (including ++ TAC5572, TAC5682). + If unsure select "N". + + endif +diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c +--- a/sound/soc/intel/boards/sof_rt5682.c ++++ b/sound/soc/intel/boards/sof_rt5682.c +@@ -908,6 +908,14 @@ static const struct platform_device_id board_ids[] = { + SOF_SSP_PORT_BT_OFFLOAD(2) | + SOF_BT_OFFLOAD_PRESENT), + }, ++ { ++ .name = "nvl_rt5682_def", ++ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | ++ SOF_SSP_PORT_CODEC(0) | ++ SOF_SSP_PORT_AMP(1) | ++ SOF_SSP_PORT_BT_OFFLOAD(2) | ++ SOF_BT_OFFLOAD_PRESENT), ++ }, + { + .name = "ptl_rt5682_c1_h02", + .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | +diff --git a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c +--- a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c ++++ b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c +@@ -51,10 +51,10 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_nvl_machines[] = { + }, + { + .comp_ids = &nvl_rt5682_rt5682s_hp, +- .drv_name = "sof_rt5682", +- .sof_tplg_filename = "sof-nvl-rt5682", /* the tplg suffix is added at run time */ +- .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER | +- SND_SOC_ACPI_TPLG_INTEL_SSP_MSB, ++ .drv_name = "nvl_rt5682_def", ++ .sof_tplg_filename = "sof-nvl", /* the tplg suffix is added at run time */ ++ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_AMP_NAME | ++ SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME, + }, + /* place amp/hdmi-in only boards in the end of table */ + { diff --git a/pkgbuilds/linux-omarchy/0523-asoc-fixes-2.patch.sig b/pkgbuilds/linux-omarchy/0523-asoc-fixes-2.patch.sig new file mode 100644 index 0000000..1e9594c Binary files /dev/null and b/pkgbuilds/linux-omarchy/0523-asoc-fixes-2.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0524-sound-fixes-4.patch b/pkgbuilds/linux-omarchy/0524-sound-fixes-4.patch new file mode 100644 index 0000000..39c0ebf --- /dev/null +++ b/pkgbuilds/linux-omarchy/0524-sound-fixes-4.patch @@ -0,0 +1,135 @@ +diff --git a/sound/core/init.c b/sound/core/init.c +--- a/sound/core/init.c ++++ b/sound/core/init.c +@@ -364,7 +364,7 @@ static int snd_card_init(struct snd_card *card, struct device *parent, + sound_debugfs_root); + #endif + #ifdef CONFIG_SND_CTL_DEBUG +- card->value_buf = kmalloc(sizeof(*card->value_buf), GFP_KERNEL); ++ card->value_buf = kmalloc_obj(*card->value_buf); + if (!card->value_buf) + return -ENOMEM; + #endif +diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c +--- a/sound/hda/codecs/realtek/alc269.c ++++ b/sound/hda/codecs/realtek/alc269.c +@@ -4304,6 +4304,7 @@ enum { + ALC287_FIXUP_LEGION_16ACHG6, + ALC287_FIXUP_CS35L41_I2C_2, + ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED, ++ ALC287_FIXUP_CS35L41_I2C_2_HP_MUTE_LEDS, + ALC287_FIXUP_CS35L41_I2C_4, + ALC245_FIXUP_CS35L41_SPI_1, + ALC245_FIXUP_CS35L41_SPI_2, +@@ -6615,6 +6616,12 @@ static const struct hda_fixup alc269_fixups[] = { + .chained = true, + .chain_id = ALC285_FIXUP_HP_MUTE_LED, + }, ++ [ALC287_FIXUP_CS35L41_I2C_2_HP_MUTE_LEDS] = { ++ .type = HDA_FIXUP_FUNC, ++ .v.func = cs35l41_fixup_i2c_two, ++ .chained = true, ++ .chain_id = ALC245_FIXUP_HP_X360_MUTE_LEDS, ++ }, + [ALC287_FIXUP_CS35L41_I2C_4] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l41_fixup_i2c_four, +@@ -7372,6 +7379,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x8158, "HP", ALC256_FIXUP_HP_HEADSET_MIC), + SND_PCI_QUIRK(0x103c, 0x820d, "HP Pavilion 15", ALC295_FIXUP_HP_X360), + SND_PCI_QUIRK(0x103c, 0x8256, "HP", ALC221_FIXUP_HP_FRONT_MIC), ++ SND_PCI_QUIRK(0x103c, 0x8259, "HP OMEN 15-ax202nf", ALC269_FIXUP_HP_MUTE_LED_MIC3), + SND_PCI_QUIRK(0x103c, 0x827e, "HP x360", ALC295_FIXUP_HP_X360), + SND_PCI_QUIRK(0x103c, 0x827f, "HP x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), + SND_PCI_QUIRK(0x103c, 0x82bf, "HP G3 mini", ALC221_FIXUP_HP_MIC_NO_PRESENCE), +@@ -7582,6 +7590,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x8b96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8ba9, "HP Omen 16-wd0xxx", ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT), ++ SND_PCI_QUIRK(0x103c, 0x8bb1, "HP Victus 15-fa1xxx (MB 8BB1)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8bb3, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8bb4, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8bb6, "HP Laptop 15-fd0039nt", ALC236_FIXUP_HP_15_FD0XXX), +@@ -7674,7 +7683,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x8d92, "HP ZBook Firefly 16 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8dcd, "HP Victus 15-fa2xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8d9b, "HP 17 Turbine OmniBook 7 UMA", ALC287_FIXUP_CS35L41_I2C_2), +- SND_PCI_QUIRK(0x103c, 0x8d9c, "HP 17 Turbine OmniBook 7 DIS", ALC287_FIXUP_CS35L41_I2C_2), ++ SND_PCI_QUIRK(0x103c, 0x8d9c, "HP 17 Turbine OmniBook 7 DIS", ALC287_FIXUP_CS35L41_I2C_2_HP_MUTE_LEDS), + SND_PCI_QUIRK(0x103c, 0x8d9d, "HP 17 Turbine OmniBook X UMA", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8d9e, "HP 17 Turbine OmniBook X DIS", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8d9f, "HP 14 Cadet (x360)", ALC287_FIXUP_CS35L41_I2C_2), +diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c +--- a/sound/usb/endpoint.c ++++ b/sound/usb/endpoint.c +@@ -449,7 +449,9 @@ static void push_back_to_ready_list(struct snd_usb_endpoint *ep, + struct snd_urb_ctx *ctx) + { + guard(spinlock_irqsave)(&ep->lock); +- list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs); ++ /* ctx may still be linked: a stale completion racing a stop/restart. */ ++ if (list_empty(&ctx->ready_list)) ++ list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs); + } + + /* +@@ -1037,6 +1039,7 @@ void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep) + */ + static int stop_urbs(struct snd_usb_endpoint *ep, bool force, bool keep_pending) + { ++ struct snd_urb_ctx *ctx, *n; + unsigned int i; + + if (!force && atomic_read(&ep->running)) +@@ -1046,7 +1049,9 @@ static int stop_urbs(struct snd_usb_endpoint *ep, bool force, bool keep_pending) + return 0; + + scoped_guard(spinlock_irqsave, &ep->lock) { +- INIT_LIST_HEAD(&ep->ready_playback_urbs); ++ /* Unlink each ctx; INIT_LIST_HEAD() alone would leave them looking linked. */ ++ list_for_each_entry_safe(ctx, n, &ep->ready_playback_urbs, ready_list) ++ list_del_init(&ctx->ready_list); + ep->next_packet_head = 0; + ep->next_packet_queued = 0; + } +diff --git a/sound/usb/implicit.c b/sound/usb/implicit.c +--- a/sound/usb/implicit.c ++++ b/sound/usb/implicit.c +@@ -76,6 +76,7 @@ static const struct snd_usb_implicit_fb_match playback_implicit_fb_quirks[] = { + + /* Implicit feedback quirk table for capture: only FIXED type */ + static const struct snd_usb_implicit_fb_match capture_implicit_fb_quirks[] = { ++ IMPLICIT_FB_FIXED_DEV(0x1397, 0x0004, 0x01, 1), /* Behringer FCA1616 */ + {} /* terminator */ + }; + +diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c +--- a/sound/usb/mixer_maps.c ++++ b/sound/usb/mixer_maps.c +@@ -531,6 +531,15 @@ static const struct usbmix_name_map audient_id24_map[] = { + {} + }; + ++/* ++ * The GC553Pro returns no data for GET_CUR on its advertised mute control. ++ * SET_CUR succeeds but does not mute capture, so skip the control entirely. ++ */ ++static const struct usbmix_name_map avermedia_gc553pro_map[] = { ++ { 3, NULL, UAC_FU_MUTE }, ++ {} ++}; ++ + /* + * Control map entries + */ +@@ -577,6 +586,10 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { + .id = USB_ID(0x0763, 0x2031), + .selector_map = c400_selectors, + }, ++ { ++ .id = USB_ID(0x07ca, 0x1553), ++ .map = avermedia_gc553pro_map, ++ }, + { + .id = USB_ID(0x08bb, 0x2702), + .map = linex_map, diff --git a/pkgbuilds/linux-omarchy/0524-sound-fixes-4.patch.sig b/pkgbuilds/linux-omarchy/0524-sound-fixes-4.patch.sig new file mode 100644 index 0000000..ac1447b Binary files /dev/null and b/pkgbuilds/linux-omarchy/0524-sound-fixes-4.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0525-asoc-fixes-3.patch b/pkgbuilds/linux-omarchy/0525-asoc-fixes-3.patch new file mode 100644 index 0000000..843a983 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0525-asoc-fixes-3.patch @@ -0,0 +1,549 @@ +diff --git a/include/sound/soc_sdw_utils.h b/include/sound/soc_sdw_utils.h +--- a/include/sound/soc_sdw_utils.h ++++ b/include/sound/soc_sdw_utils.h +@@ -250,8 +250,6 @@ int asoc_sdw_cs_amp_init(struct snd_soc_card *card, + struct snd_soc_dai_link *dai_links, + struct asoc_sdw_codec_info *info, + bool playback); +-int asoc_sdw_cs_spk_feedback_rtd_init(struct snd_soc_pcm_runtime *rtd, +- struct snd_soc_dai *dai); + int asoc_sdw_cs35l56_volume_limit(struct snd_soc_card *card, const char *name_prefix); + + /* MAXIM codec support */ +diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c +--- a/sound/soc/amd/acp/acp-sdw-sof-mach.c ++++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c +@@ -314,7 +314,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) + if (!sof_aux) + return -ENOMEM; + +- ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_devs); ++ ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_confs); + if (ret < 0) + return ret; + +@@ -390,7 +390,7 @@ static int mc_probe(struct platform_device *pdev) + ctx->private = amd_ctx; + card = &ctx->card; + card->dev = &pdev->dev; +- card->name = "amd-soundwire"; ++ card->name = "amd-sdw"; + card->owner = THIS_MODULE; + card->late_probe = asoc_sdw_card_late_probe; + +diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -524,13 +524,13 @@ config SND_SOC_ADAU1977 + tristate + + config SND_SOC_ADAU1977_SPI +- tristate ++ tristate "Analog Devices ADAU1977/ADAU1978/ADAU1979 CODEC - SPI" + depends on SPI_MASTER + select SND_SOC_ADAU1977 + select REGMAP_SPI + + config SND_SOC_ADAU1977_I2C +- tristate ++ tristate "Analog Devices ADAU1977/ADAU1978/ADAU1979 CODEC - I2C" + depends on I2C + select SND_SOC_ADAU1977 + select REGMAP_I2C +diff --git a/sound/soc/codecs/adau1977-i2c.c b/sound/soc/codecs/adau1977-i2c.c +--- a/sound/soc/codecs/adau1977-i2c.c ++++ b/sound/soc/codecs/adau1977-i2c.c +@@ -34,9 +34,18 @@ static const struct i2c_device_id adau1977_i2c_ids[] = { + }; + MODULE_DEVICE_TABLE(i2c, adau1977_i2c_ids); + ++static const struct of_device_id adau1977_i2c_of_match[] = { ++ { .compatible = "adi,adau1977" }, ++ { .compatible = "adi,adau1978" }, ++ { .compatible = "adi,adau1979" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, adau1977_i2c_of_match); ++ + static struct i2c_driver adau1977_i2c_driver = { + .driver = { + .name = "adau1977", ++ .of_match_table = adau1977_i2c_of_match, + }, + .probe = adau1977_i2c_probe, + .id_table = adau1977_i2c_ids, +diff --git a/sound/soc/codecs/adau1977-spi.c b/sound/soc/codecs/adau1977-spi.c +--- a/sound/soc/codecs/adau1977-spi.c ++++ b/sound/soc/codecs/adau1977-spi.c +@@ -53,18 +53,18 @@ static const struct spi_device_id adau1977_spi_ids[] = { + }; + MODULE_DEVICE_TABLE(spi, adau1977_spi_ids); + +-static const struct of_device_id adau1977_spi_of_match[] __maybe_unused = { +- { .compatible = "adi,adau1977" }, +- { .compatible = "adi,adau1978" }, +- { .compatible = "adi,adau1979" }, +- { }, ++static const struct of_device_id adau1977_spi_of_match[] = { ++ { .compatible = "adi,adau1977" }, ++ { .compatible = "adi,adau1978" }, ++ { .compatible = "adi,adau1979" }, ++ { }, + }; + MODULE_DEVICE_TABLE(of, adau1977_spi_of_match); + + static struct spi_driver adau1977_spi_driver = { + .driver = { + .name = "adau1977", +- .of_match_table = of_match_ptr(adau1977_spi_of_match), ++ .of_match_table = adau1977_spi_of_match, + }, + .probe = adau1977_spi_probe, + .id_table = adau1977_spi_ids, +diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c +--- a/sound/soc/codecs/rt712-sdca-sdw.c ++++ b/sound/soc/codecs/rt712-sdca-sdw.c +@@ -18,12 +18,16 @@ + static bool rt712_sdca_readable_register(struct device *dev, unsigned int reg) + { + switch (reg) { ++ case 0x004d: + case 0x201a ... 0x201f: + case 0x2029 ... 0x202a: + case 0x202d ... 0x2034: + case 0x2230 ... 0x2232: + case 0x2f01 ... 0x2f0a: + case 0x2f35 ... 0x2f36: ++ case 0x2f3a: ++ case 0x2f3d: ++ case 0x2f41: + case 0x2f50: + case 0x2f54: + case 0x2f58 ... 0x2f5d: +@@ -48,6 +52,7 @@ static bool rt712_sdca_readable_register(struct device *dev, unsigned int reg) + static bool rt712_sdca_volatile_register(struct device *dev, unsigned int reg) + { + switch (reg) { ++ case 0x004d: + case 0x201b: + case 0x201c: + case 0x201d: +@@ -56,6 +61,9 @@ static bool rt712_sdca_volatile_register(struct device *dev, unsigned int reg) + case 0x2230: + case 0x2f01: + case 0x2f35: ++ case 0x2f3a: ++ case 0x2f3d: ++ case 0x2f41: + case 0x320c: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0): + case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... +diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c +--- a/sound/soc/codecs/rt712-sdca.c ++++ b/sound/soc/codecs/rt712-sdca.c +@@ -73,14 +73,57 @@ static int rt712_sdca_index_update_bits(struct rt712_sdca_priv *rt712, + return rt712_sdca_index_write(rt712, nid, reg, tmp); + } + ++static void rt712_sdca_clk_patch(struct rt712_sdca_priv *rt712) ++{ ++ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0000); ++ regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x03); ++ usleep_range(1000, 1100); ++ regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x02); ++ usleep_range(1000, 1100); ++ regmap_update_bits(rt712->regmap, RT712_PLL2_CONF2, 0x0080, 0x0000); ++ regmap_update_bits(rt712->regmap, RT712_PLL2_CONF2, 0x001f, 0x0017); ++ regmap_update_bits(rt712->regmap, RT712_PLL2_CONF3, 0x0010, 0x0000); ++ regmap_update_bits(rt712->regmap, RT712_PLL2_CONF1, 0x0081, 0x0001); ++ regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x03); ++ usleep_range(1000, 1100); ++ regmap_update_bits(rt712->regmap, RT712_PLL2_CONF1, 0x0081, 0x0081); ++ regmap_update_bits(rt712->regmap, RT712_PLL2_CONF2, 0x0080, 0x0080); ++ regmap_update_bits(rt712->regmap, RT712_PLL2_CONF2, 0x001f, 0x0000); ++ regmap_update_bits(rt712->regmap, RT712_PLL2_CONF3, 0x0010, 0x0010); ++ usleep_range(1000, 1100); ++ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0081); ++} ++ ++static void rt712_sdca_clk_patch2(struct rt712_sdca_priv *rt712) ++{ ++ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_REG, 0x49, 0x0800, ++ 0x0000); ++ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_REG, 0x49, 0xf000, ++ 0x0000); ++ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0000); ++ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_ANALOG_CTL, 0x0c, 0xc000, ++ 0xc000); ++ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_ANALOG_CTL, 0x00, 0xc000, ++ 0xc000); ++ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0081); ++ regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x02); ++ usleep_range(1000, 1100); ++ regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x03); ++ usleep_range(1000, 1100); ++ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0000); ++} ++ + static int rt712_sdca_calibration(struct rt712_sdca_priv *rt712) + { + unsigned int val, loop_rc = 0, loop_dc = 0; + struct device *dev; + struct regmap *regmap = rt712->regmap; ++ unsigned int clk_base; + int chk_cnt = 100; + int ret = 0; + ++ regmap_read(rt712->regmap, RT712_SDW_ROOT_CLK, &clk_base); ++ + mutex_lock(&rt712->calibrate_mutex); + dev = regmap_get_device(regmap); + +@@ -109,8 +152,35 @@ static int rt712_sdca_calibration(struct rt712_sdca_priv *rt712) + if (ret < 0) + goto _cali_fail_; + } +- if (loop_dc == chk_cnt) +- dev_err(dev, "%s, calibration time-out!\n", __func__); ++ ++ if (loop_dc == chk_cnt) { ++ if (clk_base == RT712_CLK_FREQ_24_576MHZ) { ++ rt712_sdca_clk_patch(rt712); ++ rt712_sdca_clk_patch2(rt712); ++ } ++ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_FSM_CTL, 0x4100); ++ rt712_sdca_index_write(rt712, RT712_VENDOR_CALI, ++ RT712_DAC_DC_CALI_CTL1, 0x7883); ++ rt712_sdca_index_write(rt712, RT712_VENDOR_CALI, ++ RT712_DAC_DC_CALI_CTL1, 0xf893); ++ rt712_sdca_index_read(rt712, RT712_VENDOR_CALI, ++ RT712_DAC_DC_CALI_CTL1, &val); ++ ++ for (loop_dc = 0; loop_dc < chk_cnt && ++ (val & RT712_DAC_DC_CALI_TRIGGER); loop_dc++) { ++ usleep_range(10000, 11000); ++ ret = rt712_sdca_index_read(rt712, RT712_VENDOR_CALI, ++ RT712_DAC_DC_CALI_CTL1, &val); ++ ++ if (ret < 0) ++ goto _cali_fail_; ++ } ++ ++ if (loop_dc == chk_cnt) ++ dev_err(dev, "%s, calibration time-out!\n", __func__); ++ else ++ dev_dbg(dev, "%s, calibration success!\n", __func__); ++ } + + if (loop_dc == chk_cnt || loop_rc == chk_cnt) + ret = -ETIMEDOUT; +@@ -1759,9 +1829,13 @@ static void rt712_sdca_va_io_init(struct rt712_sdca_priv *rt712) + + static void rt712_sdca_vb_io_init(struct rt712_sdca_priv *rt712) + { +- int ret = 0; + unsigned int jack_func_status, mic_func_status, amp_func_status; + struct device *dev = &rt712->slave->dev; ++ unsigned int clk_base; ++ int ret = 0; ++ ++ regmap_read(rt712->regmap, RT712_SDW_ROOT_CLK, &clk_base); ++ dev_dbg(dev, "%s clk_base=%x", __func__, clk_base); + + regmap_read(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), &jack_func_status); +@@ -1773,6 +1847,12 @@ static void rt712_sdca_vb_io_init(struct rt712_sdca_priv *rt712) + __func__, jack_func_status, mic_func_status, amp_func_status); + + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_JD_CTL3, 0x7778); ++ ++ if (clk_base == RT712_CLK_FREQ_24_576MHZ) { ++ rt712_sdca_clk_patch(rt712); ++ rt712_sdca_clk_patch2(rt712); ++ } ++ + /* DMIC */ + if ((mic_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt712->first_hw_init)) { + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_DMIC2_FU_IT_FLOAT_CTL, 0x1526); +diff --git a/sound/soc/codecs/rt712-sdca.h b/sound/soc/codecs/rt712-sdca.h +--- a/sound/soc/codecs/rt712-sdca.h ++++ b/sound/soc/codecs/rt712-sdca.h +@@ -162,6 +162,16 @@ struct rt712_dmic_kctrl_priv { + #define RT712_EAPD_HIGH 0x2 + #define RT712_EAPD_LOW 0x0 + ++/* SDW clock root frequency */ ++#define RT712_SDW_ROOT_CLK 0x004d ++#define RT712_SDW_SCALE_CLK0 0x0062 ++#define RT712_SDW_SCALE_CLK1 0x0072 ++ ++/* PLL2 config */ ++#define RT712_PLL2_CONF1 0x2f3a ++#define RT712_PLL2_CONF2 0x2f3d ++#define RT712_PLL2_CONF3 0x2f41 ++ + /* RC Calibration register */ + #define RT712_RC_CAL 0x3201 + +@@ -254,6 +264,14 @@ enum rt712_sdca_version { + RT712_VB, + }; + ++enum { ++ RT712_CLK_FREQ_19_2_MHZ = 1, ++ RT712_CLK_FREQ_24MHZ = 2, ++ RT712_CLK_FREQ_24_576MHZ = 3, ++ RT712_CLK_FREQ_22_5792MHZ = 4, ++}; ++ ++ + int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave); + int rt712_sdca_init(struct device *dev, struct regmap *regmap, + struct regmap *mbq_regmap, struct sdw_slave *slave); +diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c +--- a/sound/soc/codecs/rt721-sdca-sdw.c ++++ b/sound/soc/codecs/rt721-sdca-sdw.c +@@ -70,6 +70,7 @@ static bool rt721_sdca_mbq_readable_register(struct device *dev, unsigned int re + case 0x0310100: + case 0x2000000 ... 0x2000003: + case 0x2000013: ++ case 0x2000026: + case 0x200002c: + case 0x200003c: + case 0x2000046: +@@ -142,6 +143,7 @@ static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int re + case 0x200000d: + case 0x2000019: + case 0x2000020: ++ case 0x2000026: + case 0x200002c: + case 0x2000030: + case 0x2000046: +@@ -155,6 +157,7 @@ static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int re + case 0x5810039: + case 0x5b10018: + case 0x5b10019: ++ case 0x6100006: + return true; + default: + return false; +diff --git a/sound/soc/codecs/rt721-sdca.c b/sound/soc/codecs/rt721-sdca.c +--- a/sound/soc/codecs/rt721-sdca.c ++++ b/sound/soc/codecs/rt721-sdca.c +@@ -1497,6 +1497,15 @@ int rt721_sdca_init(struct device *dev, struct regmap *regmap, + &soc_sdca_dev_rt721, rt721_sdca_dai, ARRAY_SIZE(rt721_sdca_dai)); + } + ++static void rt721_sdca_reset(struct rt721_sdca_priv *rt721) ++{ ++ rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_VENDOR_REG, ++ RT721_VD_HIDDEN_CTRL, RT721_HIDDEN_REG_SW_RESET, ++ RT721_HIDDEN_REG_SW_RESET); ++ rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT, ++ RT721_HDA_LEGACY_RESET_CTL, 0x1, 0x1); ++} ++ + int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave) + { + struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev); +@@ -1530,9 +1539,17 @@ int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave) + } + + pm_runtime_get_noresume(&slave->dev); ++ ++ if (!rt721->first_hw_init) ++ rt721_sdca_reset(rt721); ++ + rt721_sdca_dmic_preset(rt721); + rt721_sdca_amp_preset(rt721); + rt721_sdca_jack_preset(rt721); ++ ++ if (rt721->hs_jack && (!rt721->first_hw_init)) ++ rt721_sdca_jack_init(rt721); ++ + if (rt721->first_hw_init) { + regcache_cache_bypass(rt721->regmap, false); + regcache_mark_dirty(rt721->regmap); +diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c +--- a/sound/soc/codecs/wm_adsp.c ++++ b/sound/soc/codecs/wm_adsp.c +@@ -775,9 +775,12 @@ static int wm_adsp_request_firmware_file(struct wm_adsp *dsp, + s++; + } + ++ adsp_dbg(dsp, "Try '%s'\n", fw->filename); + ret = wm_adsp_firmware_request(&fw->firmware, fw->filename, cs_dsp->dev); + if (ret < 0) { +- adsp_dbg(dsp, "Failed to request '%s': %d\n", fw->filename, ret); ++ if (ret != -ENOENT) ++ adsp_dbg(dsp, "Failed to request '%s': %d\n", fw->filename, ret); ++ + kfree(fw->filename); + fw->filename = NULL; + if (ret != -ENOENT) +diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c +--- a/sound/soc/intel/boards/sof_es8336.c ++++ b/sound/soc/intel/boards/sof_es8336.c +@@ -360,6 +360,15 @@ static const struct dmi_system_id sof_es8336_quirk_table[] = { + .driver_data = (void *)(SOF_ES8336_HEADPHONE_GPIO | + SOC_ES8336_HEADSET_MIC1) + }, ++ { ++ .callback = sof_es8336_quirk_cb, ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "NDZ-WXX9"), ++ }, ++ .driver_data = (void *)(SOF_ES8336_HEADPHONE_GPIO | ++ SOC_ES8336_HEADSET_MIC1) ++ }, + {} + }; + +diff --git a/sound/soc/sdw_utils/soc_sdw_cs_amp.c b/sound/soc/sdw_utils/soc_sdw_cs_amp.c +--- a/sound/soc/sdw_utils/soc_sdw_cs_amp.c ++++ b/sound/soc/sdw_utils/soc_sdw_cs_amp.c +@@ -14,7 +14,6 @@ + #include + #include + +-#define CS_AMP_CHANNELS_PER_AMP 4 + #define CS35L56_SPK_VOLUME_0DB 400 /* 0dB Max */ + + int asoc_sdw_cs35l56_volume_limit(struct snd_soc_card *card, const char *name_prefix) +@@ -64,51 +63,6 @@ int asoc_sdw_cs_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai + } + EXPORT_SYMBOL_NS(asoc_sdw_cs_spk_rtd_init, "SND_SOC_SDW_UTILS"); + +-int asoc_sdw_cs_spk_feedback_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) +-{ +- const struct snd_soc_dai_link *dai_link = rtd->dai_link; +- const struct snd_soc_dai_link_ch_map *ch_map; +- const struct snd_soc_dai_link_component *codec_dlc; +- struct snd_soc_dai *codec_dai; +- u8 ch_slot[8] = {}; +- unsigned int amps_per_bus, ch_per_amp, mask; +- int i, ret; +- +- WARN_ON(dai_link->num_cpus > ARRAY_SIZE(ch_slot)); +- +- /* +- * CS35L56 has 4 TX channels. When the capture is aggregated the +- * same bus slots will be allocated to all the amps on a bus. Only +- * one amp on that bus can be transmitting in each slot so divide +- * the available 4 slots between all the amps on a bus. +- */ +- amps_per_bus = dai_link->num_codecs / dai_link->num_cpus; +- if ((amps_per_bus == 0) || (amps_per_bus > CS_AMP_CHANNELS_PER_AMP)) { +- dev_err(rtd->card->dev, "Illegal num_codecs:%u / num_cpus:%u\n", +- dai_link->num_codecs, dai_link->num_cpus); +- return -EINVAL; +- } +- +- ch_per_amp = CS_AMP_CHANNELS_PER_AMP / amps_per_bus; +- +- for_each_rtd_ch_maps(rtd, i, ch_map) { +- codec_dlc = snd_soc_link_to_codec(rtd->dai_link, i); +- codec_dai = snd_soc_find_dai(codec_dlc); +- mask = GENMASK(ch_per_amp - 1, 0) << ch_slot[ch_map->cpu]; +- +- ret = snd_soc_dai_set_tdm_slot(codec_dai, 0, mask, 4, 32); +- if (ret < 0) { +- dev_err(rtd->card->dev, "Failed to set TDM slot:%d\n", ret); +- return ret; +- } +- +- ch_slot[ch_map->cpu] += ch_per_amp; +- } +- +- return 0; +-} +-EXPORT_SYMBOL_NS(asoc_sdw_cs_spk_feedback_rtd_init, "SND_SOC_SDW_UTILS"); +- + int asoc_sdw_cs_amp_init(struct snd_soc_card *card, + struct snd_soc_dai_link *dai_links, + struct asoc_sdw_codec_info *info, +diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c +--- a/sound/soc/sdw_utils/soc_sdw_utils.c ++++ b/sound/soc/sdw_utils/soc_sdw_utils.c +@@ -811,7 +811,6 @@ struct asoc_sdw_codec_info codec_info_list[] = { + .dai_name = "cs35l56-sdw1c", + .dai_type = SOC_SDW_DAI_TYPE_AMP, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, +- .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, + }, + }, + .dai_num = 2, +@@ -840,7 +839,6 @@ struct asoc_sdw_codec_info codec_info_list[] = { + .dai_name = "cs35l56-sdw1c", + .dai_type = SOC_SDW_DAI_TYPE_AMP, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, +- .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, + }, + }, + .dai_num = 2, +@@ -869,7 +867,6 @@ struct asoc_sdw_codec_info codec_info_list[] = { + .dai_name = "cs35l56-sdw1c", + .dai_type = SOC_SDW_DAI_TYPE_AMP, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, +- .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, + }, + }, + .dai_num = 2, +@@ -898,7 +895,6 @@ struct asoc_sdw_codec_info codec_info_list[] = { + .dai_name = "cs35l56-sdw1c", + .dai_type = SOC_SDW_DAI_TYPE_AMP, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, +- .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, + }, + }, + .dai_num = 2, +@@ -1565,7 +1561,7 @@ int asoc_sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai_link_ch_map *ch_maps; + int ch = params_channels(params); +- unsigned int ch_mask; ++ unsigned int cpu_ch_mask, codec_ch_mask; + int num_codecs; + int step; + int i; +@@ -1575,8 +1571,9 @@ int asoc_sdw_hw_params(struct snd_pcm_substream *substream, + + /* Identical data will be sent to all codecs in playback */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +- ch_mask = GENMASK(ch - 1, 0); ++ cpu_ch_mask = GENMASK(ch - 1, 0); + step = 0; ++ codec_ch_mask = 0; + } else { + num_codecs = rtd->dai_link->num_codecs; + +@@ -1586,17 +1583,24 @@ int asoc_sdw_hw_params(struct snd_pcm_substream *substream, + return -EINVAL; + } + +- ch_mask = GENMASK(ch / num_codecs - 1, 0); +- step = hweight_long(ch_mask); ++ cpu_ch_mask = GENMASK(ch / num_codecs - 1, 0); ++ step = hweight_long(cpu_ch_mask); ++ codec_ch_mask = cpu_ch_mask; + } + + /* + * The captured data will be combined from each cpu DAI if the dai + * link has more than one codec DAIs. Set codec channel mask and + * ASoC will set the corresponding channel numbers for each cpu dai. ++ * ++ * sdw_stream_add_slave() assigns different payload offsets to each ++ * codec in a capture stream, so that the same channels on each ++ * codec map to different channels on the CPU. + */ +- for_each_link_ch_maps(rtd->dai_link, i, ch_maps) +- ch_maps->cpu_ch_mask = ch_mask << (i * step); ++ for_each_link_ch_maps(rtd->dai_link, i, ch_maps) { ++ ch_maps->cpu_ch_mask = cpu_ch_mask << (i * step); ++ ch_maps->codec_ch_mask = codec_ch_mask; ++ } + + return 0; + } diff --git a/pkgbuilds/linux-omarchy/0525-asoc-fixes-3.patch.sig b/pkgbuilds/linux-omarchy/0525-asoc-fixes-3.patch.sig new file mode 100644 index 0000000..0b22640 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0525-asoc-fixes-3.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0526-sof-amd-acp7x-suspend-unknown-revision.patch b/pkgbuilds/linux-omarchy/0526-sof-amd-acp7x-suspend-unknown-revision.patch new file mode 100644 index 0000000..3b71148 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0526-sof-amd-acp7x-suspend-unknown-revision.patch @@ -0,0 +1,13 @@ +diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c +--- a/sound/soc/sof/amd/acp.c ++++ b/sound/soc/sof/amd/acp.c +@@ -1197,7 +1197,8 @@ int amd_sof_acp7x_suspend(struct snd_sof_dev *sdev, u32 target_state) + enable = true; + break; + default: +- break; ++ dev_err(sdev->dev, "Unexpected PCI revision: 0x%x\n", acp_data->pci_rev); ++ return -EINVAL; + } + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_CONTROL, enable); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_ZSC_DSP_CTRL, 1); diff --git a/pkgbuilds/linux-omarchy/0526-sof-amd-acp7x-suspend-unknown-revision.patch.sig b/pkgbuilds/linux-omarchy/0526-sof-amd-acp7x-suspend-unknown-revision.patch.sig new file mode 100644 index 0000000..086972e Binary files /dev/null and b/pkgbuilds/linux-omarchy/0526-sof-amd-acp7x-suspend-unknown-revision.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0527-sound-fixes-5.patch b/pkgbuilds/linux-omarchy/0527-sound-fixes-5.patch new file mode 100644 index 0000000..f9378d4 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0527-sound-fixes-5.patch @@ -0,0 +1,513 @@ +diff --git a/include/sound/core.h b/include/sound/core.h +--- a/include/sound/core.h ++++ b/include/sound/core.h +@@ -340,6 +340,15 @@ static inline void snd_card_unref(struct snd_card *card) + + DEFINE_FREE(snd_card_unref, struct snd_card *, if (_T) snd_card_unref(_T)) + ++/* ++ * For automatic error handling at probe time, assign the card like: ++ * struct snd_card *card __free(snd_card_free) = NULL; ++ * then create and process as usual. ++ * But, don't forget to clear to NULL at successful return for avoiding ++ * the unexpected cleanup! ++ */ ++DEFINE_FREE(snd_card_free, struct snd_card *, if (_T) snd_card_free(_T)) ++ + #define snd_card_set_dev(card, devptr) ((card)->dev = (devptr)) + + /* device.c */ +diff --git a/sound/core/seq/seq_compat.c b/sound/core/seq/seq_compat.c +--- a/sound/core/seq/seq_compat.c ++++ b/sound/core/seq/seq_compat.c +@@ -44,7 +44,9 @@ static int snd_seq_call_port_info_ioctl(struct snd_seq_client *client, unsigned + return -EFAULT; + data->kernel = NULL; + +- err = snd_seq_kernel_client_ctl(client->number, cmd, data); ++ scoped_guard(mutex, &client->ioctl_mutex) { ++ err = snd_seq_kernel_client_ctl(client->number, cmd, data); ++ } + if (err < 0) + return err; + +diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c +--- a/sound/hda/codecs/realtek/alc269.c ++++ b/sound/hda/codecs/realtek/alc269.c +@@ -4025,6 +4025,7 @@ enum { + ALC269_FIXUP_SONY_VAIO, + ALC275_FIXUP_SONY_VAIO_GPIO2, + ALC269_FIXUP_DELL_M101Z, ++ ALC256_FIXUP_ASUS_PM3406CHA, + ALC269_FIXUP_SKU_IGNORE, + ALC269_FIXUP_ASUS_G73JW, + ALC269_FIXUP_ASUS_N7601ZM_PINS, +@@ -4124,6 +4125,7 @@ enum { + ALC285_FIXUP_HP_SPECTRE_X360_EB1, + ALC285_FIXUP_HP_SPECTRE_X360_DF1, + ALC285_FIXUP_HP_ENVY_X360, ++ ALC285_FIXUP_HP_ENVY_13AQ, + ALC288_FIXUP_DELL_HEADSET_MODE, + ALC288_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC288_FIXUP_DELL_XPS_13, +@@ -4199,6 +4201,7 @@ enum { + ALC225_FIXUP_DELL_WYSE_AIO_MIC_NO_PRESENCE, + ALC225_FIXUP_WYSE_AUTO_MUTE, + ALC225_FIXUP_WYSE_DISABLE_MIC_VREF, ++ ALC225_FIXUP_HP_AIO_SPK, + ALC286_FIXUP_ACER_AIO_HEADSET_MIC, + ALC256_FIXUP_ASUS_HEADSET_MIC, + ALC256_FIXUP_ASUS_MIC_NO_PRESENCE, +@@ -4385,6 +4388,7 @@ enum { + ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN_HEADSET, + ALC285_LENOVO_DAC_RENAME, + ALC287_FIXUP_YOGA9_SPEAKER2_TO_DAC1, ++ ALC256_FIXUP_IPASON_SMARTBOOK_S1, + }; + + /* A special fixup for Lenovo C940 and Yoga Duet 7; +@@ -4482,6 +4486,14 @@ static const struct hda_fixup alc269_fixups[] = { + {} + } + }, ++ [ALC256_FIXUP_ASUS_PM3406CHA] = { ++ .type = HDA_FIXUP_VERBS, ++ .v.verbs = (const struct hda_verb[]) { ++ { 0x20, AC_VERB_SET_COEF_INDEX, 0x10 }, ++ { 0x20, AC_VERB_SET_PROC_COEF, 0x7f20 }, ++ {} ++ } ++ }, + [ALC269_FIXUP_SKU_IGNORE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_sku_ignore, +@@ -5720,6 +5732,14 @@ static const struct hda_fixup alc269_fixups[] = { + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC + }, ++ [ALC225_FIXUP_HP_AIO_SPK] = { ++ .type = HDA_FIXUP_VERBS, ++ .v.verbs = (const struct hda_verb[]) { ++ { 0x20, AC_VERB_SET_COEF_INDEX, 0x10 }, ++ { 0x20, AC_VERB_SET_PROC_COEF, 0x0220 }, ++ { } ++ }, ++ }, + [ALC286_FIXUP_ACER_AIO_HEADSET_MIC] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { +@@ -6401,6 +6421,15 @@ static const struct hda_fixup alc269_fixups[] = { + .chained = true, + .chain_id = ALC285_FIXUP_HP_GPIO_AMP_INIT, + }, ++ [ALC285_FIXUP_HP_ENVY_13AQ] = { ++ .type = HDA_FIXUP_PINS, ++ .v.pins = (const struct hda_pintbl[]) { ++ { 0x14, 0x90170150 }, /* front speakers */ ++ { } ++ }, ++ .chained = true, ++ .chain_id = ALC285_FIXUP_HP_GPIO_AMP_INIT, ++ }, + [ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_ideapad_s740_coef, +@@ -7088,6 +7117,8 @@ static const struct hda_fixup alc269_fixups[] = { + [ALC236_FIXUP_DELL_HP_POP_NOISE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_invalidate_dacs, ++ .chained = true, ++ .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE + }, + [ALC274_FIXUP_HP_89E9_GPIO] = { + .type = HDA_FIXUP_FUNC, +@@ -7150,6 +7181,13 @@ static const struct hda_fixup alc269_fixups[] = { + { } + } + }, ++ [ALC256_FIXUP_IPASON_SMARTBOOK_S1] = { ++ .type = HDA_FIXUP_PINS, ++ .v.pins = (const struct hda_pintbl[]) { ++ { 0x1b, 0x90170110 }, /* the real internal speaker */ ++ { } ++ }, ++ }, + }; + + static const struct hda_quirk alc269_fixup_tbl[] = { +@@ -7197,6 +7235,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x142b, "Acer Swift SF314-42", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1025, 0x1430, "Acer TravelMate B311R-31", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1025, 0x1466, "Acer Aspire A515-56", ALC255_FIXUP_ACER_HEADPHONE_AND_MIC), ++ SND_PCI_QUIRK(0x1025, 0x151e, "Acer Nitro AN515-45", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x1534, "Acer Predator PH315-54", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1025, 0x1539, "Acer Nitro 5 AN515-57", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x159c, "Acer Nitro 5 AN515-58", ALC287_FIXUP_ACER_MICMUTE_LED), +@@ -7210,8 +7249,10 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x1679, "Acer Nitro 16 AN16-41", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x169a, "Acer Swift SFG16", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), + SND_PCI_QUIRK(0x1025, 0x171e, "Acer Nitro ANV15-51", ALC245_FIXUP_ACER_MICMUTE_LED), ++ SND_PCI_QUIRK(0x1025, 0x172c, "Acer Nitro ANV16-41", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x1731, "Acer Predator PHN16-72", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x173a, "Acer Swift SFG14-73", ALC245_FIXUP_ACER_MICMUTE_LED), ++ SND_PCI_QUIRK(0x1025, 0x173b, "Acer Swift SFG16-72", ALC245_FIXUP_ACER_MICMUTE_LED), + SND_PCI_QUIRK(0x1025, 0x1758, "Acer Nitro ANV15-41", ALC245_FIXUP_ACER_MICMUTE_LED), + SND_PCI_QUIRK(0x1025, 0x1826, "Acer Helios ZPC", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1025, 0x182c, "Acer Helios ZPD", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), +@@ -7317,6 +7358,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), + SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED), ++ SND_PCI_QUIRK(0x103c, 0x2164, "HP Pavilion 15 Notebook PC", ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x218b, "HP", ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x21f9, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x2210, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), +@@ -7399,9 +7441,11 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x854a, "HP EliteBook 830 G6", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x854d, "HP EliteBook 840 G6", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x856a, "HP Pavilion 15-cs1xxx", ALC295_FIXUP_HP_PAVILION_MUTE_LED_1B), ++ HDA_CODEC_QUIRK(0x103c, 0x85ba, "HP Pavilion AiO 24", ALC225_FIXUP_HP_AIO_SPK), + SND_PCI_QUIRK(0x103c, 0x85c6, "HP Pavilion x360 Convertible 14-dy1xxx", ALC295_FIXUP_HP_MUTE_LED_COEFBIT11), + SND_PCI_QUIRK(0x103c, 0x85de, "HP Envy x360 13-ar0xxx", ALC285_FIXUP_HP_ENVY_X360), + SND_PCI_QUIRK(0x103c, 0x85f0, "HP Laptop 15-dw0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), ++ SND_PCI_QUIRK(0x103c, 0x85f1, "HP Laptop 15-dw1xxx", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + SND_PCI_QUIRK(0x103c, 0x85f3, "HP 250 G8 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x8603, "HP Omen 17-cb0xxx", ALC285_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x860c, "HP ZBook 17 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT), +@@ -7409,6 +7453,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x861f, "HP Elite Dragonfly G1", ALC285_FIXUP_HP_GPIO_AMP_INIT), + SND_PCI_QUIRK(0x103c, 0x864f, "HP Laptop 15-dy0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED), ++ SND_PCI_QUIRK(0x103c, 0x86ad, "HP ENVY 13-aq1xxx", ALC285_FIXUP_HP_ENVY_13AQ), + SND_PCI_QUIRK(0x103c, 0x86c1, "HP Laptop 15-da3001TU", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x86c7, "HP Envy AiO 32", ALC274_FIXUP_HP_ENVY_GPIO), + SND_PCI_QUIRK(0x103c, 0x86c8, "HP Laptop 14s-dr1xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), +@@ -7437,6 +7482,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x8786, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8787, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8788, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED), ++ SND_PCI_QUIRK(0x103c, 0x87b2, "HP Pavilion Gaming Laptop 15-ec1xxx", ALC285_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x87b7, "HP Laptop 14-fq0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x87c8, "HP", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x87cb, "HP Pavilion 15-eg0xxx", ALC287_FIXUP_HP_GPIO_LED), +@@ -7497,6 +7543,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x8975, "HP EliteBook x360 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x897d, "HP mt440 Mobile Thin Client U74", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8981, "HP Elite Dragonfly G3", ALC245_FIXUP_CS35L41_SPI_4), ++ SND_PCI_QUIRK(0x103c, 0x8987, "HP Pavilion 14-dv1001TU", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x898a, "HP Pavilion 15-eg100", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x898e, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x898f, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2), +@@ -7534,7 +7581,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x8a2d, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a2e, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a30, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), +- SND_PCI_QUIRK(0x103c, 0x8a31, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), ++ SND_PCI_QUIRK(0x103c, 0x8a31, "HP ENVY x360 2-in-1 Laptop 15-ey0xxx", ALC287_FIXUP_CS35L41_I2C_2_HP_MUTE_LEDS), + SND_PCI_QUIRK(0x103c, 0x8a34, "HP Pavilion x360 2-in-1 Laptop 14-ek0xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8a36, "HP Pavilion Plus 14-eh0xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8a3d, "HP Victus 15-fb0xxx (MB 8A3D)", ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT), +@@ -7595,6 +7642,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x8bb4, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8bb6, "HP Laptop 15-fd0039nt", ALC236_FIXUP_HP_15_FD0XXX), + SND_PCI_QUIRK(0x103c, 0x8bbe, "HP Victus 16-r0xxx (MB 8BBE)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), ++ SND_PCI_QUIRK(0x103c, 0x8bc7, "HP Pavilion 15-eh3174nw", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8bc8, "HP Victus 15-fa1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8bcd, "HP Omen 16-xd0xxx", ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8bd4, "HP Victus 16-s0xxx (MB 8BD4)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), +@@ -7617,6 +7665,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x8c17, "HP Spectre x360 2-in-1 Laptop 16-aa0xxx", ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX), + SND_PCI_QUIRK(0x103c, 0x8c21, "HP Pavilion Plus Laptop 14-ey0XXX", ALC245_FIXUP_HP_X360_MUTE_LEDS), + SND_PCI_QUIRK(0x103c, 0x8c2d, "HP Victus 15-fa1xxx (MB 8C2D)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), ++ SND_PCI_QUIRK(0x103c, 0x8c2f, "HP Victus 15-fb2xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8c30, "HP Victus 15-fb1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8c3f, "HP Victus 15-fa1xxx (MB 8C3F)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8c46, "HP EliteBook 830 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), +@@ -7726,6 +7775,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x8e37, "HP 16 Piston OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8e3a, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8e3b, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2), ++ SND_PCI_QUIRK(0x103c, 0x8e43, "HP Laptop 15-fd2xxx", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO), + SND_PCI_QUIRK(0x103c, 0x8e60, "HP OmniBook 7 Laptop 16-bh0xxx", ALC245_FIXUP_CS35L41_I2C_2_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8e61, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8e62, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), +@@ -7829,6 +7879,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x1652, "ASUS ROG Zephyrus Do 15 SE", ALC289_FIXUP_ASUS_ZEPHYRUS_DUAL_SPK), + SND_PCI_QUIRK(0x1043, 0x1662, "ASUS GV301QH", ALC294_FIXUP_ASUS_DUAL_SPK), + SND_PCI_QUIRK(0x1043, 0x1663, "ASUS GU603ZI/ZJ/ZQ/ZU/ZV", ALC285_FIXUP_ASUS_HEADSET_MIC), ++ SND_PCI_QUIRK(0x1043, 0x1664, "ASUS ExpertBook PM3406CHA", ALC256_FIXUP_ASUS_PM3406CHA), + SND_PCI_QUIRK(0x1043, 0x1683, "ASUS UM3402YAR", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS UX3402VA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x16b2, "ASUS GU603", ALC289_FIXUP_ASUS_GA401), +@@ -7931,7 +7982,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x3ef0, "ASUS Strix G635LR_LW_LX", ALC287_FIXUP_TXNW2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3f00, "ASUS Strix G815LH_LM_LP", ALC287_FIXUP_TXNW2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3f10, "ASUS Strix G835LR_LW_LX", ALC287_FIXUP_TXNW2781_I2C), +- SND_PCI_QUIRK(0x1043, 0x3f20, "ASUS Strix G615LR_LW", ALC287_FIXUP_TXNW2781_I2C), ++ SND_PCI_QUIRK(0x1043, 0x3f20, "ASUS Strix G615LR_LW", ALC287_FIXUP_TXNW2781_I2C_ASUS), + SND_PCI_QUIRK(0x1043, 0x3f30, "ASUS Strix G815LR_LW", ALC287_FIXUP_TXNW2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3fd0, "ASUS B3605CVA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x3ff0, "ASUS B5405CVA", ALC245_FIXUP_CS35L41_SPI_2), +@@ -7954,6 +8005,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x10cf, 0x1757, "Lifebook E752", ALC269_FIXUP_LIFEBOOK_HP_PIN), + SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC), + SND_PCI_QUIRK(0x10ec, 0x10f2, "Intel Reference board", ALC700_FIXUP_INTEL_REFERENCE), ++ SND_PCI_QUIRK(0x10ec, 0x111e, "Higole F9B", ALC290_FIXUP_MONO_SPEAKERS), + SND_PCI_QUIRK(0x10ec, 0x118c, "Medion EE4254 MD62100", ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE), + SND_PCI_QUIRK(0x10ec, 0x119e, "Positivo SU C1400", ALC269_FIXUP_ASPIRE_HEADSET_MIC), + SND_PCI_QUIRK(0x10ec, 0x11bc, "VAIO VJFE-IL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), +@@ -8179,7 +8231,6 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x17aa, 0x3834, "Lenovo IdeaPad Slim 9i 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x383d, "Legion Y9000X 2019", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x3843, "Lenovo Yoga 9i / Yoga Book 9i", ALC287_FIXUP_LENOVO_YOGA_BOOK_9I), +- SND_PCI_QUIRK(0x17aa, 0x3846, "Lenovo Yoga Pro 9 16IAH10", ALC287_FIXUP_YOGA9_SPEAKER2_TO_DAC1), + /* Yoga Pro 7 14IMH9 shares PCI SSID 17aa:3847 with Legion 7 16ACHG6; + * use codec SSID to distinguish them + */ +@@ -8204,6 +8255,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ + SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x3870, "Lenovo Yoga 7 14ARB7", ALC287_FIXUP_YOGA7_14ARB7_I2C), ++ SND_PCI_QUIRK(0x17aa, 0x3871, "Lenovo ThinkBook 16p Gen 3 ARH", ALC287_FIXUP_YOGA7_14ARB7_I2C), + SND_PCI_QUIRK(0x17aa, 0x3874, "Legion 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x3877, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x3878, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2), +@@ -8254,6 +8306,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x17aa, 0x38d5, "Yoga S990-16 Pro IMH YC Quad", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38d6, "Yoga S990-16 Pro IMH VECO Quad", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38d7, "Lenovo Yoga 9 14IMH9", ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN), ++ SND_PCI_QUIRK(0x17aa, 0x38dc, "Lenovo ThinkBook G8", ALC285_FIXUP_SPEAKER2_TO_DAC1), + SND_PCI_QUIRK(0x17aa, 0x38df, "Yoga Y990 Intel YC Dual", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38e0, "Yoga Y990 Intel VECO Dual", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38f8, "Yoga Book 9i", ALC287_FIXUP_TAS2781_I2C), +@@ -8273,7 +8326,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x17aa, 0x3912, "Lenovo Xiaoxin 14 GT", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), + SND_PCI_QUIRK(0x17aa, 0x3913, "Lenovo 145", ALC236_FIXUP_LENOVO_INV_DMIC), + SND_PCI_QUIRK(0x17aa, 0x391f, "Yoga S990-16 pro Quad YC Quad", ALC287_FIXUP_TXNW2781_I2C), +- SND_PCI_QUIRK(0x17aa, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_TXNW2781_I2C), ++ SND_PCI_QUIRK(0x17aa, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_YOGA9_SPEAKER2_TO_DAC1), + SND_PCI_QUIRK(0x17aa, 0x3929, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x392b, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + HDA_CODEC_QUIRK(0x17aa, 0x3936, "Legion R9000P ADR10H", ALC287_FIXUP_LENOVO_LEGION_AW88399), +@@ -8369,6 +8422,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x2014, 0x800a, "Positivo ARN50", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x2039, 0x0001, "Inspur S14-G1", ALC295_FIXUP_CHROME_BOOK), + SND_PCI_QUIRK(0x2145, 0x0001, "Star Labs StarFighter", ALC233_FIXUP_STARLABS_STARFIGHTER), ++ SND_PCI_QUIRK(0x2782, 0x0206, "IPASON SmartBook S1", ALC256_FIXUP_IPASON_SMARTBOOK_S1), + SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x2782, 0x0228, "Infinix ZERO BOOK 13", ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13), + SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO), +diff --git a/sound/hda/codecs/realtek/realtek.c b/sound/hda/codecs/realtek/realtek.c +--- a/sound/hda/codecs/realtek/realtek.c ++++ b/sound/hda/codecs/realtek/realtek.c +@@ -1292,6 +1292,7 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec) + } + + switch (codec->core.vendor_id) { ++ case 0x10ec0235: + case 0x10ec0255: + alc_process_coef_fw(codec, coef0255); + break; +@@ -1407,6 +1408,7 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, + }; + + switch (codec->core.vendor_id) { ++ case 0x10ec0235: + case 0x10ec0255: + alc_write_coef_idx(codec, 0x45, 0xc489); + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); +@@ -1562,6 +1564,7 @@ static void alc_headset_mode_default(struct hda_codec *codec) + alc_process_coef_fw(codec, coef0225); + alc_hp_enable_unmute(codec, 75); + break; ++ case 0x10ec0235: + case 0x10ec0255: + alc_process_coef_fw(codec, coef0255); + break; +@@ -1663,6 +1666,7 @@ static void alc_headset_mode_ctia(struct hda_codec *codec) + }; + + switch (codec->core.vendor_id) { ++ case 0x10ec0235: + case 0x10ec0255: + alc_process_coef_fw(codec, coef0255); + break; +@@ -1780,6 +1784,7 @@ static void alc_headset_mode_omtp(struct hda_codec *codec) + }; + + switch (codec->core.vendor_id) { ++ case 0x10ec0235: + case 0x10ec0255: + alc_process_coef_fw(codec, coef0255); + break; +@@ -1881,6 +1886,7 @@ static void alc_determine_headset_type(struct hda_codec *codec) + } + + switch (codec->core.vendor_id) { ++ case 0x10ec0235: + case 0x10ec0255: + alc_process_coef_fw(codec, coef0255); + msleep(300); +diff --git a/sound/hda/controllers/tegra.c b/sound/hda/controllers/tegra.c +--- a/sound/hda/controllers/tegra.c ++++ b/sound/hda/controllers/tegra.c +@@ -374,7 +374,8 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) + * in powers of 2, next available ratio is 16 which can be + * used as a limiting factor here. + */ +- if (of_device_is_compatible(np, "nvidia,tegra30-hda")) ++ if (of_device_is_compatible(np, "nvidia,tegra30-hda") || ++ of_device_is_compatible(np, "nvidia,tegra194-hda")) + chip->bus.core.sdo_limit = 16; + + /* codec detection */ +diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c +--- a/sound/pci/ctxfi/cthw20k2.c ++++ b/sound/pci/ctxfi/cthw20k2.c +@@ -1895,6 +1895,7 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) + + if (hw->model == CTSB1270) { + /* Set up the PCM4220 ADC on Titanium HD */ ++ data = hw_read_20kx(hw, GPIO_DATA); + data &= ~0x0C; + if (1 == info->msr) + data |= 0x00; /* Single Speed Mode 32-50kHz */ +@@ -1903,6 +1904,7 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) + else + data |= 0x04; /* Quad Speed Mode 108kHz-216kHz */ + hw_write_20kx(hw, GPIO_DATA, data); ++ usleep_range(10000, 11000); + } + + hw_adc_start(hw); +diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c +--- a/sound/pci/ice1712/ice1712.c ++++ b/sound/pci/ice1712/ice1712.c +@@ -2523,7 +2523,7 @@ static int snd_ice1712_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) + { + static int dev; +- struct snd_card *card __free(snd_card_unref) = NULL; ++ struct snd_card *card __free(snd_card_free) = NULL; + struct snd_ice1712 *ice; + int pcm_dev = 0, err; + const struct snd_ice1712_card_info * const *tbl, *c; +diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c +--- a/sound/usb/caiaq/device.c ++++ b/sound/usb/caiaq/device.c +@@ -562,6 +562,9 @@ static int snd_probe(struct usb_interface *intf, + ret = init_card(caiaqdev(card)); + if (ret < 0) { + dev_err(&usb_dev->dev, "unable to init card! (ret=%d)\n", ret); ++#ifdef CONFIG_SND_USB_CAIAQ_INPUT ++ snd_usb_caiaq_input_disconnect(caiaqdev(card)); ++#endif + snd_card_free(card); + return ret; + } +diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c +--- a/sound/usb/line6/playback.c ++++ b/sound/usb/line6/playback.c +@@ -182,6 +182,13 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) + + fsize *= bytes_per_frame; + ++ if (fsize > line6pcm->max_packet_size_out) { ++ dev_err(line6pcm->line6->ifcdev, ++ "playback packet too large: %d > %d\n", ++ fsize, line6pcm->max_packet_size_out); ++ return -EMSGSIZE; ++ } ++ + fout->offset = urb_size; + fout->length = fsize; + urb_size += fsize; +diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c +--- a/sound/usb/misc/ua101.c ++++ b/sound/usb/misc/ua101.c +@@ -1007,6 +1007,15 @@ static int detect_usb_format(struct ua101 *ua) + } + ua->playback.usb_pipe = usb_sndisocpipe(ua->dev, usb_endpoint_num(epd)); + ua->playback.max_packet_bytes = usb_endpoint_maxp(epd); ++ ++ if (ua->capture.max_packet_bytes / ua->capture.frame_bytes * ++ ua->playback.frame_bytes > ua->playback.max_packet_bytes) { ++ dev_err(&ua->dev->dev, ++ "playback packet size %u too small for %u capture frames\n", ++ ua->playback.max_packet_bytes, ++ ua->capture.max_packet_bytes / ua->capture.frame_bytes); ++ return -ENXIO; ++ } + return 0; + } + +diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c +--- a/sound/usb/mixer_maps.c ++++ b/sound/usb/mixer_maps.c +@@ -749,6 +749,10 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { + .id = USB_ID(0x0db0, 0xa073), + .map = msi_mpg_x570s_carbon_max_wifi_alc4080_map, + }, ++ { /* MSI MAG B850M MORTAR WIFI */ ++ .id = USB_ID(0x0db0, 0xcc78), ++ .map = msi_mpg_x570s_carbon_max_wifi_alc4080_map, ++ }, + { /* MSI TRX40 */ + .id = USB_ID(0x0db0, 0x543d), + .map = trx40_mobo_map, +diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c +--- a/sound/usb/quirks.c ++++ b/sound/usb/quirks.c +@@ -1702,10 +1702,12 @@ int snd_usb_apply_boot_quirk_once(struct usb_device *dev, + switch (id) { + case USB_ID(0x07fd, 0x0008): /* MOTU M Series, 1st hardware version */ + return snd_usb_motu_m_series_boot_quirk(dev); +- case USB_ID(0x1397, 0x1234): /* Behringer CM1A */ +- return snd_usb_cm1a_boot_quirk(dev); + } + ++ /* Behringer devices may need explicit device descriptor read at boot */ ++ if (USB_ID_VENDOR(id) == 0x1397) ++ return snd_usb_cm1a_boot_quirk(dev); ++ + return 0; + } + +@@ -2366,6 +2368,10 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + QUIRK_FLAG_CTL_MSG_DELAY_1M), + DEVICE_FLG(0x0a73, 0x003a, /* Mackie DLZ Creator XS */ + QUIRK_FLAG_ALWAYS_SET_RATE), ++ DEVICE_FLG(0x0b05, 0x1826, /* ASUS SupremeFX Hi-Fi */ ++ QUIRK_FLAG_DISABLE_AUTOSUSPEND), ++ DEVICE_FLG(0x0b05, 0x1827, /* ASUS SupremeFX Hi-Fi */ ++ QUIRK_FLAG_DISABLE_AUTOSUSPEND), + DEVICE_FLG(0x0b05, 0x18a6, /* ASUSTek Computer, Inc. */ + QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE), + DEVICE_FLG(0x0b0e, 0x0349, /* Jabra 550a */ +@@ -2510,8 +2516,6 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), + DEVICE_FLG(0x262a, 0x9302, /* ddHiFi TC44C */ + QUIRK_FLAG_DSD_RAW), +- DEVICE_FLG(0x2708, 0x0002, /* Audient iD14 */ +- QUIRK_FLAG_IGNORE_CTL_ERROR), + DEVICE_FLG(0x2772, 0x0502, /* Musical Fidelity M6s DAC */ + 0), /* for avoiding QUIRK_FLAG_DSD_RAW with vendor match */ + DEVICE_FLG(0x2912, 0x30c8, /* Audioengine D1 */ +@@ -2542,6 +2546,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + QUIRK_FLAG_IGNORE_CTL_ERROR), + DEVICE_FLG(0x3255, 0x0000, /* Luxman D-10X */ + QUIRK_FLAG_ITF_USB_DSD_DAC | QUIRK_FLAG_CTL_MSG_DELAY), ++ DEVICE_FLG(0x32bb, 0x0004, /* HiBy FC4 */ ++ QUIRK_FLAG_DSD_RAW), + DEVICE_FLG(0x3302, 0x17c2, /* TTGK Technology USB-C Audio */ + QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), + DEVICE_FLG(0x339b, 0x3a07, /* Synaptics HONOR USB-C HEADSET */ +@@ -2608,6 +2614,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + QUIRK_FLAG_DSD_RAW), + VENDOR_FLG(0x2622, /* IAG Limited devices */ + QUIRK_FLAG_DSD_RAW), ++ VENDOR_FLG(0x2708, /* Audient devices */ ++ QUIRK_FLAG_IGNORE_CTL_ERROR), + VENDOR_FLG(0x2772, /* Musical Fidelity devices */ + QUIRK_FLAG_DSD_RAW), + VENDOR_FLG(0x278b, /* Rotel? */ diff --git a/pkgbuilds/linux-omarchy/0527-sound-fixes-5.patch.sig b/pkgbuilds/linux-omarchy/0527-sound-fixes-5.patch.sig new file mode 100644 index 0000000..2654792 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0527-sound-fixes-5.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0528-asoc-fixes-4.patch b/pkgbuilds/linux-omarchy/0528-asoc-fixes-4.patch new file mode 100644 index 0000000..2290ae4 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0528-asoc-fixes-4.patch @@ -0,0 +1,523 @@ +diff --git a/drivers/spi/spi-cs42l43.c b/drivers/spi/spi-cs42l43.c +--- a/drivers/spi/spi-cs42l43.c ++++ b/drivers/spi/spi-cs42l43.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -215,12 +216,108 @@ static size_t cs42l43_spi_max_length(struct spi_device *spi) + return CS42L43_SPI_MAX_LENGTH; + } + ++/* ++ * Workaround needed for two speaker ID pins in one ACPI GpioIo() but ++ * the Linux-specific _DSD property only contains one pin. ++ * Create a temporary acpi_gpio_mapping pointing at both pins. ++ */ ++static const struct acpi_gpio_params cs42l43_2bit_speaker_id_from_one_gpioio_params[] = { ++ [0] = { ++ .crs_entry_index = 0, ++ .line_index = 0, ++ }, ++ [1] = { ++ .crs_entry_index = 0, ++ .line_index = 1, ++ }, ++}; ++ ++static const struct acpi_gpio_mapping cs42l43_2bit_speaker_id_from_one_gpioio_mapping[] = { ++ { ++ .name = "spk-id-quirk-gpios", ++ .data = cs42l43_2bit_speaker_id_from_one_gpioio_params, ++ .size = ARRAY_SIZE(cs42l43_2bit_speaker_id_from_one_gpioio_params), ++ }, ++ { } ++}; ++ ++static int cs42l43_get_2bit_speaker_id_from_one_gpioio(struct cs42l43_spi *priv, int *result) ++{ ++ struct fwnode_reference_args args; ++ struct acpi_device *adev; ++ struct gpio_desc *desc; ++ u32 spkid = 0; ++ int i, ret; ++ ++ /* Use the _DSD property to get the node containing the GpioIo() */ ++ ret = fwnode_property_get_reference_args(dev_fwnode(priv->dev), "spk-id-gpios", ++ NULL, 3, 0, &args); ++ if (ret) ++ return ret; ++ ++ struct fwnode_handle *fwnode __free(fwnode_handle) = args.fwnode; ++ ++ /* An acpi_gpio_mapping must be added to the node that contains the GpioIo() */ ++ adev = to_acpi_device_node(fwnode); ++ if (!adev) ++ return -EINVAL; ++ ++ ret = acpi_dev_add_driver_gpios(adev, cs42l43_2bit_speaker_id_from_one_gpioio_mapping); ++ if (ret) ++ return ret; ++ ++ /* gpiod_get_array() can't read from a mapping in a child node */ ++ for (i = 0; i < ARRAY_SIZE(cs42l43_2bit_speaker_id_from_one_gpioio_params); i++) { ++ desc = fwnode_gpiod_get_index(fwnode, "spk-id-quirk", i, GPIOD_IN, ++ dev_name(priv->dev)); ++ if (IS_ERR(desc)) { ++ ret = PTR_ERR(desc); ++ goto out; ++ } ++ ++ ret = gpiod_get_value_cansleep(desc); ++ gpiod_put(desc); ++ if (ret < 0) ++ goto out; ++ ++ spkid |= (u32)ret << i; ++ } ++ ++ dev_dbg(priv->dev, "spk-id-gpios = %u\n", spkid); ++ *result = spkid; ++ ret = 0; ++out: ++ acpi_dev_remove_driver_gpios(adev); ++ ++ return ret; ++} ++ ++static const struct dmi_system_id cs42l43_spk_id_quirks[] = { ++ { ++ .ident = "Dell XPS 13 DX13260", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), ++ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0E53"), ++ }, ++ .driver_data = cs42l43_get_2bit_speaker_id_from_one_gpioio, ++ }, ++ { } ++}; ++ + static int cs42l43_get_speaker_id_gpios(struct cs42l43_spi *priv, int *result) + { ++ const struct dmi_system_id *dmi_id; + struct gpio_descs *descs; + u32 spkid; + int i, ret; + ++ dmi_id = dmi_first_match(cs42l43_spk_id_quirks); ++ if (dmi_id) { ++ int (*get_speaker_id)(struct cs42l43_spi *priv, int *result) = dmi_id->driver_data; ++ ++ return get_speaker_id(priv, result); ++ } ++ + descs = gpiod_get_array_optional(priv->dev, "spk-id", GPIOD_IN); + if (!descs) + return 0; +diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c +--- a/sound/soc/amd/acp-config.c ++++ b/sound/soc/amd/acp-config.c +@@ -51,6 +51,13 @@ static const struct dmi_system_id acp70_acpi_flag_override_table[] = { + DMI_MATCH(DMI_PRODUCT_NAME, "83V9"), + }, + }, ++ { ++ /* Lenovo Yoga Pro 7 14ASP9 (Strix Point, ACP 7.0) */ ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "83HN"), ++ }, ++ }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC"), +@@ -63,6 +70,13 @@ static const struct dmi_system_id acp70_acpi_flag_override_table[] = { + DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook 18 M1807GA"), + }, + }, ++ { ++ /* ASUS Zenbook S14 UM5406GA (Strix Point, ACP 7.0) */ ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Zenbook S14 UM5406GA"), ++ }, ++ }, + { + /* HP OmniBook X Flip 14-kc0xxx (Strix Point, ACP 7.2) */ + .matches = { +diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig +--- a/sound/soc/amd/acp/Kconfig ++++ b/sound/soc/amd/acp/Kconfig +@@ -181,6 +181,7 @@ config SND_SOC_AMD_LEGACY_SDW_MACH + select SND_SOC_RT715_SDW + select SND_SOC_RT715_SDCA_SDW + select SND_SOC_RT722_SDCA_SDW ++ select SND_SOC_TAS2783_SDW + help + This option enables Legacy(No DSP) sound card support for SoundWire + enabled AMD platforms along with ACP PDM controller. +diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c +--- a/sound/soc/amd/acp/amd-acp70-acpi-match.c ++++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c +@@ -748,7 +748,27 @@ static const struct snd_soc_acpi_link_adr acp70_rt721_only[] = { + {} + }; + ++static const struct snd_soc_acpi_link_adr acp70_tas2783a_l0_rt712_vb_l1[] = { ++ { ++ .mask = BIT(0), ++ .num_adr = ARRAY_SIZE(tas2783_2_adr), ++ .adr_d = tas2783_2_adr, ++ }, ++ { ++ .mask = BIT(1), ++ .num_adr = ARRAY_SIZE(rt712_vb_1_group1_adr), ++ .adr_d = rt712_vb_1_group1_adr, ++ }, ++ {} ++}; ++ + struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = { ++ { ++ .link_mask = BIT(0) | BIT(1), ++ .links = acp70_tas2783a_l0_rt712_vb_l1, ++ .machine_check = snd_soc_acpi_amd_sdca_is_device_rt712_vb, ++ .drv_name = "amd_sdw", ++ }, + { + .link_mask = BIT(0), + .links = acp70_tas2783_2, +diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c +--- a/sound/soc/amd/yc/acp6x-mach.c ++++ b/sound/soc/amd/yc/acp6x-mach.c +@@ -248,6 +248,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { + DMI_MATCH(DMI_PRODUCT_NAME, "21J6"), + } + }, ++ { ++ .driver_data = &acp6x_card, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "21JN"), ++ } ++ }, + { + .driver_data = &acp6x_card, + .matches = { +@@ -381,6 +388,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { + DMI_MATCH(DMI_PRODUCT_NAME, "83Q3"), + } + }, ++ { ++ .driver_data = &acp6x_card, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "83UU"), ++ } ++ }, + { + .driver_data = &acp6x_card, + .matches = { +diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -1989,9 +1989,7 @@ config SND_SOC_RT715_SDCA_SDW + config SND_SOC_RT766_SDCA_SDW + tristate "Realtek RT766 SDCA Codec - SDW" + depends on SOUNDWIRE +- depends on SND_SOC_SDCA +- select SND_SOC_SDCA_HID +- select SND_SOC_SDCA_IRQ ++ depends on SND_SOC_SDCA_CLASS + select REGMAP_SOUNDWIRE + select REGMAP_SOUNDWIRE_MBQ + +diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c +--- a/sound/soc/codecs/lpass-wsa-macro.c ++++ b/sound/soc/codecs/lpass-wsa-macro.c +@@ -1841,6 +1841,7 @@ static int wsa_macro_enable_interpolator(struct snd_soc_dapm_widget *w, + int event) + { + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); ++ unsigned int gain; + u16 gain_reg; + u16 reg; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); +@@ -1882,6 +1883,8 @@ static int wsa_macro_enable_interpolator(struct snd_soc_dapm_widget *w, + CDC_WSA_RX_PGA_HALF_DB_MASK, + CDC_WSA_RX_PGA_HALF_DB_ENABLE); + } ++ gain = snd_soc_component_read(component, gain_reg); ++ snd_soc_component_write(component, gain_reg, gain); + wsa_macro_config_ear_spkr_gain(component, wsa, + event, gain_reg); + break; +diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c +--- a/sound/soc/codecs/max98363.c ++++ b/sound/soc/codecs/max98363.c +@@ -200,7 +200,7 @@ static int max98363_sdw_dai_hw_params(struct snd_pcm_substream *substream, + struct max98363_priv *max98363 = + snd_soc_component_get_drvdata(component); + +- struct sdw_stream_config stream_config; ++ struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_runtime *stream; +diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c +--- a/sound/soc/codecs/rt1017-sdca-sdw.c ++++ b/sound/soc/codecs/rt1017-sdca-sdw.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -573,11 +574,10 @@ static int rt1017_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + { + struct snd_soc_component *component = dai->component; + struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component); +- struct sdw_stream_config stream_config; ++ struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config; +- enum sdw_data_direction direction; + struct sdw_stream_runtime *sdw_stream; +- int retval, port, num_channels, ch_mask; ++ int retval, port; + unsigned int sampling_rate; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); +@@ -591,28 +591,17 @@ static int rt1017_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + + /* SoundWire specific configuration */ + /* port 1 for playback */ +- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +- direction = SDW_DATA_DIR_RX; ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + port = 1; +- } else { +- direction = SDW_DATA_DIR_TX; ++ else + port = 2; +- } + +- num_channels = params_channels(params); +- ch_mask = (1 << num_channels) - 1; +- +- stream_config.frame_rate = params_rate(params); +- stream_config.ch_count = num_channels; +- stream_config.bps = snd_pcm_format_width(params_format(params)); +- stream_config.direction = direction; +- +- port_config.ch_mask = ch_mask; ++ snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + port_config.num = port; + + dev_dbg(dai->dev, "frame_rate %d, ch_count %d, bps %d, direction %d, ch_mask %d, port: %d\n", +- params_rate(params), num_channels, snd_pcm_format_width(params_format(params)), +- direction, ch_mask, port); ++ stream_config.frame_rate, stream_config.ch_count, stream_config.bps, ++ stream_config.direction, port_config.ch_mask, port); + + retval = sdw_stream_add_slave(rt1017->sdw_slave, &stream_config, + &port_config, 1, sdw_stream); +diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c +--- a/sound/soc/codecs/rt274.c ++++ b/sound/soc/codecs/rt274.c +@@ -619,9 +619,11 @@ static int rt274_hw_params(struct snd_pcm_substream *substream, + unsigned int val = 0; + int d_len_code = 0, c_len_code = 0; + ++ /* bit 14 Sample Rate Base, 0: 48kHz 1: 44.1kHz */ + switch (params_rate(params)) { +- /* bit 14 0:48K 1:44.1K */ + case 44100: ++ val |= BIT(14); ++ break; + case 48000: + break; + default: +diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c +--- a/sound/soc/codecs/rt286.c ++++ b/sound/soc/codecs/rt286.c +@@ -953,7 +953,7 @@ static int rt286_probe(struct snd_soc_component *component) + + if (rt286->i2c->irq) + schedule_delayed_work(&rt286->jack_detect_work, +- msecs_to_jiffies(50)); ++ msecs_to_jiffies(1250)); + return 0; + } + +diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c +--- a/sound/soc/codecs/wm8903.c ++++ b/sound/soc/codecs/wm8903.c +@@ -676,7 +676,7 @@ SOC_ENUM("DRC Decay Rate", drc_decay), + SOC_ENUM("DRC FF Delay", drc_ff_delay), + SOC_SINGLE("DRC Anticlip Switch", WM8903_DRC_0, 1, 1, 0), + SOC_SINGLE("DRC QR Switch", WM8903_DRC_0, 2, 1, 0), +-SOC_SINGLE_TLV("DRC QR Threshold Volume", WM8903_DRC_0, 6, 3, 0, drc_tlv_max), ++SOC_SINGLE_TLV("DRC QR Threshold Volume", WM8903_DRC_1, 6, 3, 0, drc_tlv_max), + SOC_ENUM("DRC QR Decay Rate", drc_qr_decay), + SOC_SINGLE("DRC Smoothing Switch", WM8903_DRC_0, 3, 1, 0), + SOC_SINGLE("DRC Smoothing Hysteresis Switch", WM8903_DRC_0, 0, 1, 0), +diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c +--- a/sound/soc/intel/boards/bytcr_rt5651.c ++++ b/sound/soc/intel/boards/bytcr_rt5651.c +@@ -410,6 +410,23 @@ static const struct dmi_system_id byt_rt5651_quirk_table[] = { + BYT_RT5651_HP_LR_SWAPPED | + BYT_RT5651_MONO_SPEAKER), + }, ++ { ++ /* Chuwi Hi8 (CWI513) with a BIOS using generic DMI strings */ ++ .callback = byt_rt5651_quirk_cb, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"), ++ DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"), ++ /* Above matches are too generic, add bios-date match */ ++ DMI_MATCH(DMI_BIOS_DATE, "05/21/2016"), ++ }, ++ .driver_data = (void *)(BYT_RT5651_MCLK_EN | ++ BYT_RT5651_JD1_1 | ++ BYT_RT5651_OVCD_TH_2000UA | ++ BYT_RT5651_OVCD_SF_1P0 | ++ BYT_RT5651_IN2_MAP | ++ BYT_RT5651_HP_LR_SWAPPED | ++ BYT_RT5651_MONO_SPEAKER), ++ }, + { + /* Chuwi Vi8 Plus (CWI519) */ + .callback = byt_rt5651_quirk_cb, +diff --git a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c +--- a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c ++++ b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c +@@ -745,6 +745,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { + .links = lnl_cs42l43_l0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-cs42l43-l0.tplg", ++ .machine_check = snd_soc_acpi_intel_force_function_topology, + .get_function_tplg_files = sof_sdw_get_tplg_files, + }, + { +diff --git a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c +--- a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c ++++ b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c +@@ -53,6 +53,22 @@ bool snd_soc_acpi_intel_no_function_topology(void *arg) + } + EXPORT_SYMBOL_NS(snd_soc_acpi_intel_no_function_topology, "SND_SOC_ACPI_INTEL_SDCA_QUIRKS"); + ++static const struct dmi_system_id force_function_topology_quirk_table[] = { ++ { ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "83R0"), ++ }, ++ }, ++ {} ++}; ++ ++bool snd_soc_acpi_intel_force_function_topology(void *arg) ++{ ++ return !dmi_check_system(force_function_topology_quirk_table); ++} ++EXPORT_SYMBOL_NS(snd_soc_acpi_intel_force_function_topology, "SND_SOC_ACPI_INTEL_SDCA_QUIRKS"); ++ + bool snd_soc_acpi_intel_rt712_vb_no_function_topology(void *arg) + { + return snd_soc_acpi_intel_sdca_is_device_rt712_vb(arg) && +diff --git a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h +--- a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h ++++ b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h +@@ -11,6 +11,7 @@ + + bool snd_soc_acpi_intel_sdca_is_device_rt712_vb(void *arg); + bool snd_soc_acpi_intel_no_function_topology(void *arg); ++bool snd_soc_acpi_intel_force_function_topology(void *arg); + bool snd_soc_acpi_intel_rt712_vb_no_function_topology(void *arg); + + #endif +diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c +--- a/sound/soc/qcom/lpass-cpu.c ++++ b/sound/soc/qcom/lpass-cpu.c +@@ -1234,8 +1234,8 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) + } + if (drvdata->mi2s_playback_sd_mode[dai_id] == + LPAIF_I2SCTL_MODE_QUAD01) { +- variant->dai_driver[dai_id].playback.channels_min = 4; +- variant->dai_driver[dai_id].playback.channels_max = 4; ++ variant->dai_driver[i].playback.channels_min = 4; ++ variant->dai_driver[i].playback.channels_max = 4; + } + } + +diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c +--- a/sound/soc/sdca/sdca_class_function.c ++++ b/sound/soc/sdca/sdca_class_function.c +@@ -388,20 +388,27 @@ static int class_function_probe(struct auxiliary_device *auxdev, + + ret = devm_pm_runtime_enable(dev); + if (ret) +- return ret; ++ goto err_pm; + + ret = class_function_boot(drv); + if (ret) +- return ret; ++ goto err_pm; + + ret = devm_snd_soc_register_component(dev, cmp_drv, dais, num_dais); +- if (ret) +- return dev_err_probe(dev, ret, "failed to register component\n"); ++ if (ret) { ++ dev_err_probe(dev, ret, "failed to register component\n"); ++ goto err_pm; ++ } + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return 0; ++ ++err_pm: ++ pm_runtime_put_sync(dev); ++ ++ return ret; + } + + static void class_function_remove(struct auxiliary_device *auxdev) +@@ -490,8 +497,6 @@ static int class_function_suspend(struct device *dev) + struct class_function_drv *drv = auxiliary_get_drvdata(auxdev); + int ret; + +- drv->suspended = true; +- + /* Ensure runtime resume runs on resume */ + ret = pm_runtime_resume_and_get(dev); + if (ret) { +@@ -499,6 +504,8 @@ static int class_function_suspend(struct device *dev) + return ret; + } + ++ drv->suspended = true; ++ + sdca_irq_disable(drv->function, drv->core->irq_info); + + ret = pm_runtime_force_suspend(dev); +diff --git a/sound/soc/tegra/tegra186_asrc.c b/sound/soc/tegra/tegra186_asrc.c +--- a/sound/soc/tegra/tegra186_asrc.c ++++ b/sound/soc/tegra/tegra186_asrc.c +@@ -828,7 +828,7 @@ static const struct snd_kcontrol_new tegra186_asrc_controls[] = { + tegra186_asrc_put_input_threshold), + + SOC_SINGLE_EXT("Stream6 Input Threshold", +- ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 4), 0, 3, 0, ++ ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 5), 0, 3, 0, + tegra186_asrc_get_input_threshold, + tegra186_asrc_put_input_threshold), + diff --git a/pkgbuilds/linux-omarchy/0528-asoc-fixes-4.patch.sig b/pkgbuilds/linux-omarchy/0528-asoc-fixes-4.patch.sig new file mode 100644 index 0000000..ad208a3 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0528-asoc-fixes-4.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0540-media-ipu-bridge-ivsc-no-cvs-lookup.patch b/pkgbuilds/linux-omarchy/0540-media-ipu-bridge-ivsc-no-cvs-lookup.patch deleted file mode 100644 index 3762b2e..0000000 --- a/pkgbuilds/linux-omarchy/0540-media-ipu-bridge-ivsc-no-cvs-lookup.patch +++ /dev/null @@ -1,41 +0,0 @@ -diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c ---- a/drivers/media/pci/intel/ipu-bridge.c -+++ b/drivers/media/pci/intel/ipu-bridge.c -@@ -173,6 +173,19 @@ static const struct acpi_device_id ivsc_acpi_ids[] = { - { "INTC10E1" }, /* PTL */ - }; - -+/* -+ * The subset of ivsc_acpi_ids[] which are IVSC, rather than CVS, devices. The -+ * CVS IDs are deliberately not listed here: new ones keep being added, whereas -+ * this list is complete. -+ */ -+static const struct acpi_device_id ivsc_only_acpi_ids[] = { -+ { "INTC1059" }, -+ { "INTC1095" }, -+ { "INTC100A" }, -+ { "INTC10CF" }, -+ { } -+}; -+ - static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev) - { - unsigned int i; -@@ -224,6 +237,17 @@ static struct device *ipu_bridge_get_ivsc_csi_dev(struct acpi_device *adev) - return csi_dev; - } - -+ /* -+ * The lookups below match on the ACPI companion alone. That is fine for -+ * CVS, which binds a driver to that very device, but not for IVSC: there -+ * the ACPI device also has a driverless platform device, which would be -+ * returned instead of the mei-csi client. Return NULL for IVSC so that -+ * the caller fails and the probe is retried once the IVSC device shows -+ * up. -+ */ -+ if (!acpi_match_device_ids(adev, ivsc_only_acpi_ids)) -+ return NULL; -+ - /* Try to locate CVS device on the I2C bus */ - csi_dev = bus_find_device_by_acpi_dev(&i2c_bus_type, adev); - if (csi_dev) diff --git a/pkgbuilds/linux-omarchy/0540-media-ipu-bridge-ivsc-no-cvs-lookup.patch.sig b/pkgbuilds/linux-omarchy/0540-media-ipu-bridge-ivsc-no-cvs-lookup.patch.sig deleted file mode 100644 index 5f2c204..0000000 Binary files a/pkgbuilds/linux-omarchy/0540-media-ipu-bridge-ivsc-no-cvs-lookup.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/0541-cvs-nova-lake-acpi-id.patch b/pkgbuilds/linux-omarchy/0541-cvs-nova-lake-acpi-id.patch index 39ccf4d..2e401f7 100644 --- a/pkgbuilds/linux-omarchy/0541-cvs-nova-lake-acpi-id.patch +++ b/pkgbuilds/linux-omarchy/0541-cvs-nova-lake-acpi-id.patch @@ -1,7 +1,7 @@ diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c -@@ -862,6 +862,7 @@ static const char * const acpi_honor_dep_ids[] = { +@@ -867,6 +867,7 @@ static const char * const acpi_honor_dep_ids[] = { "INTC10DE", /* CVS (LNL) driver must be loaded to allow camera streaming */ "INTC10E0", /* CVS (ARL) driver must be loaded to allow camera streaming */ "INTC10E1", /* CVS (PTL) driver must be loaded to allow camera streaming */ diff --git a/pkgbuilds/linux-omarchy/0541-cvs-nova-lake-acpi-id.patch.sig b/pkgbuilds/linux-omarchy/0541-cvs-nova-lake-acpi-id.patch.sig index 6ea6cfa..2e58e13 100644 Binary files a/pkgbuilds/linux-omarchy/0541-cvs-nova-lake-acpi-id.patch.sig and b/pkgbuilds/linux-omarchy/0541-cvs-nova-lake-acpi-id.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0543-media-laptop-cameras.patch b/pkgbuilds/linux-omarchy/0543-media-laptop-cameras.patch new file mode 100644 index 0000000..9ffdd20 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0543-media-laptop-cameras.patch @@ -0,0 +1,3293 @@ +diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig +--- a/drivers/media/i2c/Kconfig ++++ b/drivers/media/i2c/Kconfig +@@ -259,6 +259,7 @@ config VIDEO_IMX335 + + config VIDEO_IMX355 + tristate "Sony IMX355 sensor support" ++ select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor driver for the Sony + IMX355 camera. +@@ -287,6 +288,16 @@ config VIDEO_IMX415 + To compile this driver as a module, choose M here: the + module will be called imx415. + ++config VIDEO_IMX471 ++ tristate "Sony IMX471 sensor support" ++ select V4L2_CCI_I2C ++ help ++ This is a Video4Linux2 sensor driver for the Sony ++ IMX471 camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called imx471. ++ + config VIDEO_MAX9271_LIB + tristate + +diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile +--- a/drivers/media/i2c/Makefile ++++ b/drivers/media/i2c/Makefile +@@ -61,6 +61,7 @@ obj-$(CONFIG_VIDEO_IMX335) += imx335.o + obj-$(CONFIG_VIDEO_IMX355) += imx355.o + obj-$(CONFIG_VIDEO_IMX412) += imx412.o + obj-$(CONFIG_VIDEO_IMX415) += imx415.o ++obj-$(CONFIG_VIDEO_IMX471) += imx471.o + obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o + obj-$(CONFIG_VIDEO_ISL7998X) += isl7998x.o + obj-$(CONFIG_VIDEO_KS0127) += ks0127.o +diff --git a/drivers/media/i2c/cvs/core.c b/drivers/media/i2c/cvs/core.c +--- a/drivers/media/i2c/cvs/core.c ++++ b/drivers/media/i2c/cvs/core.c +@@ -31,6 +31,7 @@ + + #define PCI_DEVICE_ID_INTEL_IPU7 0x645d /* MTL / LNL */ + #define PCI_DEVICE_ID_INTEL_IPU7P5 0xb05d /* ARL / PTL */ ++#define PCI_DEVICE_ID_INTEL_IPU8 0xd719 /* NVL */ + + /* + * IPU7 PCI device IDs not covered by ipu6_pci_tbl in ipu6-pci-table.h. +@@ -39,6 +40,7 @@ + static const struct pci_device_id icvs_ipu7_tbl[] = { + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU7) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU7P5) }, ++ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU8) }, + { } + }; + +diff --git a/drivers/media/i2c/cvs/icvs.h b/drivers/media/i2c/cvs/icvs.h +--- a/drivers/media/i2c/cvs/icvs.h ++++ b/drivers/media/i2c/cvs/icvs.h +@@ -432,7 +432,6 @@ enum icvs_state { + * @freq_ctrl: (future) frequency control pointer + * @pads: Local media pads (sink/source) + * @nr_of_lanes: Active CSI-2 lane count +- * @link_freq: Current link frequency (Hz) + * @ipu_link: PM runtime device link (IPU consumer, CVS supplier) + * @res: Resource capability (light/full) + * @caps: Reported device protocol capabilities +@@ -458,7 +457,6 @@ struct icvs { + struct v4l2_ctrl *freq_ctrl; + struct media_pad pads[ICVS_CSI_NUM_PADS]; + u32 nr_of_lanes; +- u64 link_freq; + struct device_link *ipu_link; + enum icvs_resources res; + struct icvs_dev_capabilities caps; +diff --git a/drivers/media/i2c/cvs/v4l2.c b/drivers/media/i2c/cvs/v4l2.c +--- a/drivers/media/i2c/cvs/v4l2.c ++++ b/drivers/media/i2c/cvs/v4l2.c +@@ -46,6 +46,7 @@ static const struct v4l2_mbus_framefmt cvs_csi_format_mbus_default = { + /** + * csi_set_link_cfg - Program default CSI-2 link parameters + * @ctx: CVS device context ++ * @link_freq: Link frequency (Hz) + * + * Populates a HOST_SET_MIPI_CONFIG command using current lane count and + * link frequency, then submits it to the device. +@@ -53,12 +54,12 @@ static const struct v4l2_mbus_framefmt cvs_csi_format_mbus_default = { + * + * Return: 0 on success or negative errno. + */ +-static int csi_set_link_cfg(struct icvs *ctx) ++static int csi_set_link_cfg(struct icvs *ctx, u64 link_freq) + { + struct icvs_cmd cmd = { + .cmd_id = cpu_to_be16(ICVS_HOST_SET_MIPI_CONFIG), + .param.conf.nr_of_lanes = ctx->nr_of_lanes, +- .param.conf.link_freq = ctx->link_freq, ++ .param.conf.link_freq = link_freq, + }; + size_t cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.conf); + +@@ -91,7 +92,7 @@ static int cvs_csi_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev *remote_sd = + media_entity_to_v4l2_subdev(ctx->remote->entity); + struct device *dev = cvs_dev(ctx); +- s64 freq; ++ s64 link_freq; + int ret; + + /* cvs_set_link_owner(ICVS_CSI_LINK_HOST) */ +@@ -99,15 +100,14 @@ static int cvs_csi_enable_streams(struct v4l2_subdev *sd, + if (ret < 0) + return ret; + +- freq = v4l2_get_link_freq(ctx->remote, 0, 0); +- if (freq < 0) { +- ret = freq; ++ link_freq = v4l2_get_link_freq(ctx->remote, 0, 0); ++ if (link_freq < 0) { ++ ret = link_freq; + goto err_rpm_put; + } +- ctx->link_freq = freq; + + if (ctx->i2c_client) { +- ret = csi_set_link_cfg(ctx); ++ ret = csi_set_link_cfg(ctx, link_freq); + if (ret < 0) + goto err_rpm_put_sync; + } +@@ -345,7 +345,6 @@ static int cvs_csi_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad, + if (freq < 0) + return -EINVAL; + +- ctx->link_freq = freq; + cfg->link_freq = freq; + + return 0; +diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c +--- a/drivers/media/i2c/imx355.c ++++ b/drivers/media/i2c/imx355.c +@@ -9,74 +9,97 @@ + #include + #include + #include ++#include + #include + #include + ++#include + #include + #include + #include + #include + +-#define IMX355_REG_MODE_SELECT 0x0100 ++#define IMX355_REG_MODE_SELECT CCI_REG8(0x0100) + #define IMX355_MODE_STANDBY 0x00 + #define IMX355_MODE_STREAMING 0x01 + + /* Chip ID */ +-#define IMX355_REG_CHIP_ID 0x0016 ++#define IMX355_REG_CHIP_ID CCI_REG16(0x0016) + #define IMX355_CHIP_ID 0x0355 + ++#define IMX355_REG_LANE_SEL CCI_REG8(0x0114) ++ ++/* PLL registers that depend on the external clock frequency */ ++#define IMX355_REG_EXTCLK_FREQ CCI_REG16(0x0136) ++#define IMX355_REG_PLL_OP_PREDIV CCI_REG8(0x030d) ++#define IMX355_REG_PLL_OP_MUL CCI_REG16(0x030e) ++#define IMX355_REG_PLL_IVT_PCK_DIV CCI_REG8(0x0301) ++#define IMX355_REG_PLL_IVT_SYSCK_DIV CCI_REG8(0x0303) ++#define IMX355_PLL_OP_PREDIV 2 ++#define IMX355_PLL_IVT_PCK_DIV 5 ++ + /* V_TIMING internal */ +-#define IMX355_REG_FLL 0x0340 ++#define IMX355_REG_FLL CCI_REG16(0x0340) + #define IMX355_FLL_MAX 0xffff ++#define IMX355_VBLANK_MIN 20 ++ ++#define IMX355_REG_LLP CCI_REG16(0x0342) ++#define IMX355_LLP_MAX 0xffff ++ ++#define IMX355_REG_X_ADD_START CCI_REG16(0x0344) ++#define IMX355_REG_Y_ADD_START CCI_REG16(0x0346) ++#define IMX355_REG_X_ADD_END CCI_REG16(0x0348) ++#define IMX355_REG_Y_ADD_END CCI_REG16(0x034a) ++#define IMX355_REG_X_OUT_SIZE CCI_REG16(0x034c) ++#define IMX355_REG_Y_OUT_SIZE CCI_REG16(0x034e) + + /* Exposure control */ +-#define IMX355_REG_EXPOSURE 0x0202 ++#define IMX355_REG_EXPOSURE CCI_REG16(0x0202) + #define IMX355_EXPOSURE_MIN 1 + #define IMX355_EXPOSURE_STEP 1 + #define IMX355_EXPOSURE_DEFAULT 0x0282 ++#define IMX355_EXPOSURE_OFFSET 10 + + /* Analog gain control */ +-#define IMX355_REG_ANALOG_GAIN 0x0204 ++#define IMX355_REG_ANALOG_GAIN CCI_REG16(0x0204) + #define IMX355_ANA_GAIN_MIN 0 + #define IMX355_ANA_GAIN_MAX 960 + #define IMX355_ANA_GAIN_STEP 1 + #define IMX355_ANA_GAIN_DEFAULT 0 + + /* Digital gain control */ +-#define IMX355_REG_DPGA_USE_GLOBAL_GAIN 0x3070 +-#define IMX355_REG_DIG_GAIN_GLOBAL 0x020e ++#define IMX355_REG_DPGA_USE_GLOBAL_GAIN CCI_REG8(0x3070) ++#define IMX355_REG_DIG_GAIN_GLOBAL CCI_REG16(0x020e) + #define IMX355_DGTL_GAIN_MIN 256 + #define IMX355_DGTL_GAIN_MAX 4095 + #define IMX355_DGTL_GAIN_STEP 1 + #define IMX355_DGTL_GAIN_DEFAULT 256 + + /* Test Pattern Control */ +-#define IMX355_REG_TEST_PATTERN 0x0600 ++#define IMX355_REG_TEST_PATTERN CCI_REG16(0x0600) + #define IMX355_TEST_PATTERN_DISABLED 0 + #define IMX355_TEST_PATTERN_SOLID_COLOR 1 + #define IMX355_TEST_PATTERN_COLOR_BARS 2 + #define IMX355_TEST_PATTERN_GRAY_COLOR_BARS 3 + #define IMX355_TEST_PATTERN_PN9 4 + ++#define IMX355_REG_REQ_LINK_BIT_RATE CCI_REG16(0x0820) ++ ++#define IMX355_REG_BINNING_MODE CCI_REG8(0x0900) ++#define IMX355_REG_BINNING_TYPE CCI_REG8(0x0901) ++#define IMX355_REG_BINNING_WEIGHTING CCI_REG8(0x0902) ++ + /* Flip Control */ +-#define IMX355_REG_ORIENTATION 0x0101 ++#define IMX355_REG_ORIENTATION CCI_REG8(0x0101) + +-/* default link frequency and external clock */ +-#define IMX355_LINK_FREQ_DEFAULT 360000000LL +-#define IMX355_EXT_CLK 19200000 +-#define IMX355_LINK_FREQ_INDEX 0 +- +-/* number of data lanes */ +-#define IMX355_DATA_LANES 4 +- +-struct imx355_reg { +- u16 address; +- u8 val; +-}; ++#define IMX355_PIXEL_ARRAY_TOP 0 ++#define IMX355_PIXEL_ARRAY_LEFT 0 ++#define IMX355_PIXEL_ARRAY_WIDTH 3280 ++#define IMX355_PIXEL_ARRAY_HEIGHT 2464 + + struct imx355_reg_list { + u32 num_of_regs; +- const struct imx355_reg *regs; ++ const struct cci_reg_sequence *regs; + }; + + /* Mode : resolution and related config&values */ +@@ -85,28 +108,58 @@ struct imx355_mode { + u32 width; + /* Frame height */ + u32 height; ++ struct v4l2_rect crop; + + /* V-timing */ + u32 fll_def; +- u32 fll_min; + + /* H-timing */ + u32 llp; + +- /* index of link frequency */ +- u32 link_freq_index; +- + /* Default register values */ + struct imx355_reg_list reg_list; + }; + ++struct imx355_clk_params { ++ u32 ext_clk; ++ u16 extclk_freq; /* External clock (MHz) in 8.8 fixed point) */ ++ u16 pll_op_mpy[2]; /* OP system PLL multiplier */ ++ u8 pll_op_prediv[2]; /* OP system pre PLL d */ ++}; ++ ++/* ++ * The clock tree is in single PLL mode, so PREDIV_VT and MPY_IVT do nothing. ++ * In 4 lane mode the MIPI rate is 360Mhz (720Mbit/s) and pixel rate is ++ * 288MPix/s. ++ * In 2 lane mode the MIPI rate is 444MHz (888Mbit/s) and pixel rate ++ * 177.6MPix/s with a 24MHz clock, and 441.6MHz (883.2Mbit/s) and 176.6MPix/s ++ * with a 19.2MHz clock. ++ */ ++static const struct imx355_clk_params imx355_clk_params[] = { ++ { ++ .ext_clk = 19200000, ++ .extclk_freq = 0x1333, ++ .pll_op_mpy = { 75, 92 }, ++ .pll_op_prediv = { 2, 2 } ++ }, ++ { ++ .ext_clk = 24000000, ++ .extclk_freq = 0x1800, ++ .pll_op_mpy = { 60, 111 }, ++ .pll_op_prediv = { 2, 3 } ++ }, ++}; ++ + struct imx355_hwcfg { ++ s64 link_freq_menu; + unsigned long link_freq_bitmap; ++ unsigned int num_lanes; + }; + + struct imx355 { + struct device *dev; + struct clk *clk; ++ struct regmap *regmap; + + struct v4l2_subdev sd; + struct media_pad pad; +@@ -114,24 +167,14 @@ struct imx355 { + struct v4l2_ctrl_handler ctrl_handler; + /* V4L2 Controls */ + struct v4l2_ctrl *link_freq; +- struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hflip; + +- /* Current mode */ +- const struct imx355_mode *cur_mode; +- + struct imx355_hwcfg *hwcfg; +- +- /* +- * Mutex for serialized access: +- * Protect sensor set pad format and start/stop streaming safely. +- * Protect access to sensor v4l2 controls. +- */ +- struct mutex mutex; ++ const struct imx355_clk_params *clk_params; + + struct gpio_desc *reset_gpio; + struct regulator_bulk_data *supplies; +@@ -143,750 +186,147 @@ static const struct regulator_bulk_data imx355_supplies[] = { + { .supply = "dovdd" }, + }; + +-static const struct imx355_reg imx355_global_regs[] = { +- { 0x0136, 0x13 }, +- { 0x0137, 0x33 }, +- { 0x304e, 0x03 }, +- { 0x4348, 0x16 }, +- { 0x4350, 0x19 }, +- { 0x4408, 0x0a }, +- { 0x440c, 0x0b }, +- { 0x4411, 0x5f }, +- { 0x4412, 0x2c }, +- { 0x4623, 0x00 }, +- { 0x462c, 0x0f }, +- { 0x462d, 0x00 }, +- { 0x462e, 0x00 }, +- { 0x4684, 0x54 }, +- { 0x480a, 0x07 }, +- { 0x4908, 0x07 }, +- { 0x4909, 0x07 }, +- { 0x490d, 0x0a }, +- { 0x491e, 0x0f }, +- { 0x4921, 0x06 }, +- { 0x4923, 0x28 }, +- { 0x4924, 0x28 }, +- { 0x4925, 0x29 }, +- { 0x4926, 0x29 }, +- { 0x4927, 0x1f }, +- { 0x4928, 0x20 }, +- { 0x4929, 0x20 }, +- { 0x492a, 0x20 }, +- { 0x492c, 0x05 }, +- { 0x492d, 0x06 }, +- { 0x492e, 0x06 }, +- { 0x492f, 0x06 }, +- { 0x4930, 0x03 }, +- { 0x4931, 0x04 }, +- { 0x4932, 0x04 }, +- { 0x4933, 0x05 }, +- { 0x595e, 0x01 }, +- { 0x5963, 0x01 }, +- { 0x3030, 0x01 }, +- { 0x3031, 0x01 }, +- { 0x3045, 0x01 }, +- { 0x4010, 0x00 }, +- { 0x4011, 0x00 }, +- { 0x4012, 0x00 }, +- { 0x4013, 0x01 }, +- { 0x68a8, 0xfe }, +- { 0x68a9, 0xff }, +- { 0x6888, 0x00 }, +- { 0x6889, 0x00 }, +- { 0x68b0, 0x00 }, +- { 0x3058, 0x00 }, +- { 0x305a, 0x00 }, ++static const struct cci_reg_sequence imx355_global_regs[] = { ++ { CCI_REG8(0x304e), 0x03 }, ++ { CCI_REG8(0x4348), 0x16 }, ++ { CCI_REG8(0x4350), 0x19 }, ++ { CCI_REG8(0x4408), 0x0a }, ++ { CCI_REG8(0x440c), 0x0b }, ++ { CCI_REG8(0x4411), 0x5f }, ++ { CCI_REG8(0x4412), 0x2c }, ++ { CCI_REG8(0x4623), 0x00 }, ++ { CCI_REG8(0x462c), 0x0f }, ++ { CCI_REG8(0x462d), 0x00 }, ++ { CCI_REG8(0x462e), 0x00 }, ++ { CCI_REG8(0x4684), 0x54 }, ++ { CCI_REG8(0x480a), 0x07 }, ++ { CCI_REG8(0x4908), 0x07 }, ++ { CCI_REG8(0x4909), 0x07 }, ++ { CCI_REG8(0x490d), 0x0a }, ++ { CCI_REG8(0x491e), 0x0f }, ++ { CCI_REG8(0x4921), 0x06 }, ++ { CCI_REG8(0x4923), 0x28 }, ++ { CCI_REG8(0x4924), 0x28 }, ++ { CCI_REG8(0x4925), 0x29 }, ++ { CCI_REG8(0x4926), 0x29 }, ++ { CCI_REG8(0x4927), 0x1f }, ++ { CCI_REG8(0x4928), 0x20 }, ++ { CCI_REG8(0x4929), 0x20 }, ++ { CCI_REG8(0x492a), 0x20 }, ++ { CCI_REG8(0x492c), 0x05 }, ++ { CCI_REG8(0x492d), 0x06 }, ++ { CCI_REG8(0x492e), 0x06 }, ++ { CCI_REG8(0x492f), 0x06 }, ++ { CCI_REG8(0x4930), 0x03 }, ++ { CCI_REG8(0x4931), 0x04 }, ++ { CCI_REG8(0x4932), 0x04 }, ++ { CCI_REG8(0x4933), 0x05 }, ++ { CCI_REG8(0x595e), 0x01 }, ++ { CCI_REG8(0x5963), 0x01 }, ++ { CCI_REG8(0x3030), 0x01 }, ++ { CCI_REG8(0x3031), 0x01 }, ++ { CCI_REG8(0x3045), 0x01 }, ++ { CCI_REG8(0x4010), 0x00 }, ++ { CCI_REG8(0x4011), 0x00 }, ++ { CCI_REG8(0x4012), 0x00 }, ++ { CCI_REG8(0x4013), 0x01 }, ++ { CCI_REG8(0x68a8), 0xfe }, ++ { CCI_REG8(0x68a9), 0xff }, ++ { CCI_REG8(0x6888), 0x00 }, ++ { CCI_REG8(0x6889), 0x00 }, ++ { CCI_REG8(0x68b0), 0x00 }, ++ { CCI_REG8(0x3058), 0x00 }, ++ { CCI_REG8(0x305a), 0x00 }, ++ { CCI_REG8(0x0112), 0x0a }, ++ { CCI_REG8(0x0113), 0x0a }, ++ { IMX355_REG_PLL_IVT_PCK_DIV, IMX355_PLL_IVT_PCK_DIV }, ++ { CCI_REG8(0x0303), 0x01 }, ++ { CCI_REG8(0x0305), 0x02 }, ++ { CCI_REG8(0x0306), 0x00 }, ++ { CCI_REG8(0x0307), 0x78 }, ++ { CCI_REG8(0x030b), 0x01 }, ++ { IMX355_REG_PLL_OP_PREDIV, IMX355_PLL_OP_PREDIV }, ++ { CCI_REG8(0x0310), 0x00 }, ++ { CCI_REG8(0x0220), 0x00 }, ++ { CCI_REG8(0x0222), 0x01 }, ++ { CCI_REG8(0x3088), 0x04 }, ++ { CCI_REG8(0x6813), 0x02 }, ++ { CCI_REG8(0x6835), 0x07 }, ++ { CCI_REG8(0x6836), 0x01 }, ++ { CCI_REG8(0x6837), 0x04 }, ++ { CCI_REG8(0x684d), 0x07 }, ++ { CCI_REG8(0x684e), 0x01 }, ++ { CCI_REG8(0x684f), 0x04 }, + }; + +-static const struct imx355_reg_list imx355_global_setting = { +- .num_of_regs = ARRAY_SIZE(imx355_global_regs), +- .regs = imx355_global_regs, ++static const struct cci_reg_sequence mode_3268x2448_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_3268x2448_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x0e }, +- { 0x0343, 0x58 }, +- { 0x0340, 0x0a }, +- { 0x0341, 0x37 }, +- { 0x0344, 0x00 }, +- { 0x0345, 0x08 }, +- { 0x0346, 0x00 }, +- { 0x0347, 0x08 }, +- { 0x0348, 0x0c }, +- { 0x0349, 0xcb }, +- { 0x034a, 0x09 }, +- { 0x034b, 0x97 }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x00 }, +- { 0x0901, 0x11 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x0c }, +- { 0x034d, 0xc4 }, +- { 0x034e, 0x09 }, +- { 0x034f, 0x90 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_3264x2448_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_3264x2448_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x0e }, +- { 0x0343, 0x58 }, +- { 0x0340, 0x0a }, +- { 0x0341, 0x37 }, +- { 0x0344, 0x00 }, +- { 0x0345, 0x08 }, +- { 0x0346, 0x00 }, +- { 0x0347, 0x08 }, +- { 0x0348, 0x0c }, +- { 0x0349, 0xc7 }, +- { 0x034a, 0x09 }, +- { 0x034b, 0x97 }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x00 }, +- { 0x0901, 0x11 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x0c }, +- { 0x034d, 0xc0 }, +- { 0x034e, 0x09 }, +- { 0x034f, 0x90 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_3280x2464_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_3280x2464_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x0e }, +- { 0x0343, 0x58 }, +- { 0x0340, 0x0a }, +- { 0x0341, 0x37 }, +- { 0x0344, 0x00 }, +- { 0x0345, 0x00 }, +- { 0x0346, 0x00 }, +- { 0x0347, 0x00 }, +- { 0x0348, 0x0c }, +- { 0x0349, 0xcf }, +- { 0x034a, 0x09 }, +- { 0x034b, 0x9f }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x00 }, +- { 0x0901, 0x11 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x0c }, +- { 0x034d, 0xd0 }, +- { 0x034e, 0x09 }, +- { 0x034f, 0xa0 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_1940x1096_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_1940x1096_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x0e }, +- { 0x0343, 0x58 }, +- { 0x0340, 0x05 }, +- { 0x0341, 0x1a }, +- { 0x0344, 0x02 }, +- { 0x0345, 0xa0 }, +- { 0x0346, 0x02 }, +- { 0x0347, 0xac }, +- { 0x0348, 0x0a }, +- { 0x0349, 0x33 }, +- { 0x034a, 0x06 }, +- { 0x034b, 0xf3 }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x00 }, +- { 0x0901, 0x11 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x07 }, +- { 0x034d, 0x94 }, +- { 0x034e, 0x04 }, +- { 0x034f, 0x48 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_1936x1096_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_1936x1096_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x0e }, +- { 0x0343, 0x58 }, +- { 0x0340, 0x05 }, +- { 0x0341, 0x1a }, +- { 0x0344, 0x02 }, +- { 0x0345, 0xa0 }, +- { 0x0346, 0x02 }, +- { 0x0347, 0xac }, +- { 0x0348, 0x0a }, +- { 0x0349, 0x2f }, +- { 0x034a, 0x06 }, +- { 0x034b, 0xf3 }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x00 }, +- { 0x0901, 0x11 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x07 }, +- { 0x034d, 0x90 }, +- { 0x034e, 0x04 }, +- { 0x034f, 0x48 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_1924x1080_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_1924x1080_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x0e }, +- { 0x0343, 0x58 }, +- { 0x0340, 0x05 }, +- { 0x0341, 0x1a }, +- { 0x0344, 0x02 }, +- { 0x0345, 0xa8 }, +- { 0x0346, 0x02 }, +- { 0x0347, 0xb4 }, +- { 0x0348, 0x0a }, +- { 0x0349, 0x2b }, +- { 0x034a, 0x06 }, +- { 0x034b, 0xeb }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x00 }, +- { 0x0901, 0x11 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x07 }, +- { 0x034d, 0x84 }, +- { 0x034e, 0x04 }, +- { 0x034f, 0x38 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_1920x1080_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_1920x1080_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x0e }, +- { 0x0343, 0x58 }, +- { 0x0340, 0x05 }, +- { 0x0341, 0x1a }, +- { 0x0344, 0x02 }, +- { 0x0345, 0xa8 }, +- { 0x0346, 0x02 }, +- { 0x0347, 0xb4 }, +- { 0x0348, 0x0a }, +- { 0x0349, 0x27 }, +- { 0x034a, 0x06 }, +- { 0x034b, 0xeb }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x00 }, +- { 0x0901, 0x11 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x07 }, +- { 0x034d, 0x80 }, +- { 0x034e, 0x04 }, +- { 0x034f, 0x38 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_1640x1232_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_1640x1232_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x07 }, +- { 0x0343, 0x2c }, +- { 0x0340, 0x05 }, +- { 0x0341, 0x1a }, +- { 0x0344, 0x00 }, +- { 0x0345, 0x00 }, +- { 0x0346, 0x00 }, +- { 0x0347, 0x00 }, +- { 0x0348, 0x0c }, +- { 0x0349, 0xcf }, +- { 0x034a, 0x09 }, +- { 0x034b, 0x9f }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x01 }, +- { 0x0901, 0x22 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x06 }, +- { 0x034d, 0x68 }, +- { 0x034e, 0x04 }, +- { 0x034f, 0xd0 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_1640x922_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_1640x922_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x07 }, +- { 0x0343, 0x2c }, +- { 0x0340, 0x05 }, +- { 0x0341, 0x1a }, +- { 0x0344, 0x00 }, +- { 0x0345, 0x00 }, +- { 0x0346, 0x01 }, +- { 0x0347, 0x30 }, +- { 0x0348, 0x0c }, +- { 0x0349, 0xcf }, +- { 0x034a, 0x08 }, +- { 0x034b, 0x63 }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x01 }, +- { 0x0901, 0x22 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x06 }, +- { 0x034d, 0x68 }, +- { 0x034e, 0x03 }, +- { 0x034f, 0x9a }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_1300x736_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_1300x736_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x07 }, +- { 0x0343, 0x2c }, +- { 0x0340, 0x05 }, +- { 0x0341, 0x1a }, +- { 0x0344, 0x01 }, +- { 0x0345, 0x58 }, +- { 0x0346, 0x01 }, +- { 0x0347, 0xf0 }, +- { 0x0348, 0x0b }, +- { 0x0349, 0x7f }, +- { 0x034a, 0x07 }, +- { 0x034b, 0xaf }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x01 }, +- { 0x0901, 0x22 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x05 }, +- { 0x034d, 0x14 }, +- { 0x034e, 0x02 }, +- { 0x034f, 0xe0 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_1296x736_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_1296x736_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x07 }, +- { 0x0343, 0x2c }, +- { 0x0340, 0x05 }, +- { 0x0341, 0x1a }, +- { 0x0344, 0x01 }, +- { 0x0345, 0x58 }, +- { 0x0346, 0x01 }, +- { 0x0347, 0xf0 }, +- { 0x0348, 0x0b }, +- { 0x0349, 0x77 }, +- { 0x034a, 0x07 }, +- { 0x034b, 0xaf }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x01 }, +- { 0x0901, 0x22 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x05 }, +- { 0x034d, 0x10 }, +- { 0x034e, 0x02 }, +- { 0x034f, 0xe0 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_1284x720_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_1284x720_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x07 }, +- { 0x0343, 0x2c }, +- { 0x0340, 0x05 }, +- { 0x0341, 0x1a }, +- { 0x0344, 0x01 }, +- { 0x0345, 0x68 }, +- { 0x0346, 0x02 }, +- { 0x0347, 0x00 }, +- { 0x0348, 0x0b }, +- { 0x0349, 0x6f }, +- { 0x034a, 0x07 }, +- { 0x034b, 0x9f }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x01 }, +- { 0x0901, 0x22 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x05 }, +- { 0x034d, 0x04 }, +- { 0x034e, 0x02 }, +- { 0x034f, 0xd0 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_1280x720_regs[] = { ++ { CCI_REG8(0x0700), 0x00 }, ++ { CCI_REG8(0x0701), 0x10 }, + }; + +-static const struct imx355_reg mode_1280x720_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x07 }, +- { 0x0343, 0x2c }, +- { 0x0340, 0x05 }, +- { 0x0341, 0x1a }, +- { 0x0344, 0x01 }, +- { 0x0345, 0x68 }, +- { 0x0346, 0x02 }, +- { 0x0347, 0x00 }, +- { 0x0348, 0x0b }, +- { 0x0349, 0x67 }, +- { 0x034a, 0x07 }, +- { 0x034b, 0x9f }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x01 }, +- { 0x0901, 0x22 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x05 }, +- { 0x034d, 0x00 }, +- { 0x034e, 0x02 }, +- { 0x034f, 0xd0 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x00 }, +- { 0x0701, 0x10 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, +-}; +- +-static const struct imx355_reg mode_820x616_regs[] = { +- { 0x0112, 0x0a }, +- { 0x0113, 0x0a }, +- { 0x0114, 0x03 }, +- { 0x0342, 0x0e }, +- { 0x0343, 0x58 }, +- { 0x0340, 0x02 }, +- { 0x0341, 0x8c }, +- { 0x0344, 0x00 }, +- { 0x0345, 0x00 }, +- { 0x0346, 0x00 }, +- { 0x0347, 0x00 }, +- { 0x0348, 0x0c }, +- { 0x0349, 0xcf }, +- { 0x034a, 0x09 }, +- { 0x034b, 0x9f }, +- { 0x0220, 0x00 }, +- { 0x0222, 0x01 }, +- { 0x0900, 0x01 }, +- { 0x0901, 0x44 }, +- { 0x0902, 0x00 }, +- { 0x034c, 0x03 }, +- { 0x034d, 0x34 }, +- { 0x034e, 0x02 }, +- { 0x034f, 0x68 }, +- { 0x0301, 0x05 }, +- { 0x0303, 0x01 }, +- { 0x0305, 0x02 }, +- { 0x0306, 0x00 }, +- { 0x0307, 0x78 }, +- { 0x030b, 0x01 }, +- { 0x030d, 0x02 }, +- { 0x030e, 0x00 }, +- { 0x030f, 0x4b }, +- { 0x0310, 0x00 }, +- { 0x0700, 0x02 }, +- { 0x0701, 0x78 }, +- { 0x0820, 0x0b }, +- { 0x0821, 0x40 }, +- { 0x3088, 0x04 }, +- { 0x6813, 0x02 }, +- { 0x6835, 0x07 }, +- { 0x6836, 0x01 }, +- { 0x6837, 0x04 }, +- { 0x684d, 0x07 }, +- { 0x684e, 0x01 }, +- { 0x684f, 0x04 }, ++static const struct cci_reg_sequence mode_820x616_regs[] = { ++ { CCI_REG8(0x0700), 0x02 }, ++ { CCI_REG8(0x0701), 0x78 }, + }; + + static const char * const imx355_test_pattern_menu[] = { +@@ -897,23 +337,19 @@ static const char * const imx355_test_pattern_menu[] = { + "Pseudorandom Sequence (PN9)", + }; + +-/* +- * When adding more than the one below, make sure the disallowed ones will +- * actually be disabled in the LINK_FREQ control. +- */ +-static const s64 link_freq_menu_items[] = { +- IMX355_LINK_FREQ_DEFAULT, +-}; +- + /* Mode configs */ + static const struct imx355_mode supported_modes[] = { + { + .width = 3280, + .height = 2464, ++ .crop = { ++ .width = 3280, ++ .height = 2464, ++ .left = 0, ++ .top = 0, ++ }, + .fll_def = 2615, +- .fll_min = 2615, + .llp = 3672, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs), + .regs = mode_3280x2464_regs, +@@ -922,10 +358,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 3268, + .height = 2448, ++ .crop = { ++ .width = 3268, ++ .height = 2448, ++ .left = 8, ++ .top = 8, ++ }, + .fll_def = 2615, +- .fll_min = 2615, + .llp = 3672, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3268x2448_regs), + .regs = mode_3268x2448_regs, +@@ -934,10 +374,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 3264, + .height = 2448, ++ .crop = { ++ .width = 3264, ++ .height = 2448, ++ .left = 8, ++ .top = 8, ++ }, + .fll_def = 2615, +- .fll_min = 2615, + .llp = 3672, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3264x2448_regs), + .regs = mode_3264x2448_regs, +@@ -946,10 +390,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 1940, + .height = 1096, ++ .crop = { ++ .width = 1940, ++ .height = 1096, ++ .left = 672, ++ .top = 684, ++ }, + .fll_def = 1306, +- .fll_min = 1306, + .llp = 3672, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1940x1096_regs), + .regs = mode_1940x1096_regs, +@@ -958,10 +406,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 1936, + .height = 1096, ++ .crop = { ++ .width = 1936, ++ .height = 1096, ++ .left = 672, ++ .top = 684, ++ }, + .fll_def = 1306, +- .fll_min = 1306, + .llp = 3672, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1936x1096_regs), + .regs = mode_1936x1096_regs, +@@ -970,10 +422,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 1924, + .height = 1080, ++ .crop = { ++ .width = 1924, ++ .height = 1080, ++ .left = 680, ++ .top = 692, ++ }, + .fll_def = 1306, +- .fll_min = 1306, + .llp = 3672, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1924x1080_regs), + .regs = mode_1924x1080_regs, +@@ -982,10 +438,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 1920, + .height = 1080, ++ .crop = { ++ .width = 1920, ++ .height = 1080, ++ .left = 680, ++ .top = 692, ++ }, + .fll_def = 1306, +- .fll_min = 1306, + .llp = 3672, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1920x1080_regs), + .regs = mode_1920x1080_regs, +@@ -994,10 +454,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 1640, + .height = 1232, ++ .crop = { ++ .width = 3280, ++ .height = 2464, ++ .left = 0, ++ .top = 0, ++ }, + .fll_def = 1306, +- .fll_min = 1306, + .llp = 1836, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1640x1232_regs), + .regs = mode_1640x1232_regs, +@@ -1006,10 +470,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 1640, + .height = 922, ++ .crop = { ++ .width = 3280, ++ .height = 1844, ++ .left = 0, ++ .top = 304, ++ }, + .fll_def = 1306, +- .fll_min = 1306, + .llp = 1836, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1640x922_regs), + .regs = mode_1640x922_regs, +@@ -1018,10 +486,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 1300, + .height = 736, ++ .crop = { ++ .width = 2600, ++ .height = 1472, ++ .left = 344, ++ .top = 496, ++ }, + .fll_def = 1306, +- .fll_min = 1306, + .llp = 1836, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1300x736_regs), + .regs = mode_1300x736_regs, +@@ -1030,10 +502,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 1296, + .height = 736, ++ .crop = { ++ .width = 2592, ++ .height = 1472, ++ .left = 344, ++ .top = 496, ++ }, + .fll_def = 1306, +- .fll_min = 1306, + .llp = 1836, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1296x736_regs), + .regs = mode_1296x736_regs, +@@ -1042,10 +518,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 1284, + .height = 720, ++ .crop = { ++ .width = 2568, ++ .height = 1440, ++ .left = 360, ++ .top = 512, ++ }, + .fll_def = 1306, +- .fll_min = 1306, + .llp = 1836, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1284x720_regs), + .regs = mode_1284x720_regs, +@@ -1054,10 +534,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 1280, + .height = 720, ++ .crop = { ++ .width = 2560, ++ .height = 1440, ++ .left = 360, ++ .top = 512, ++ }, + .fll_def = 1306, +- .fll_min = 1306, + .llp = 1836, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1280x720_regs), + .regs = mode_1280x720_regs, +@@ -1066,10 +550,14 @@ static const struct imx355_mode supported_modes[] = { + { + .width = 820, + .height = 616, ++ .crop = { ++ .width = 3280, ++ .height = 2464, ++ .left = 0, ++ .top = 0, ++ }, + .fll_def = 652, +- .fll_min = 652, + .llp = 3672, +- .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_820x616_regs), + .regs = mode_820x616_regs, +@@ -1095,116 +583,28 @@ static u32 imx355_get_format_code(struct imx355 *imx355) + { MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SBGGR10_1X10, }, + }; + +- lockdep_assert_held(&imx355->mutex); + code = codes[imx355->vflip->val][imx355->hflip->val]; + + return code; + } + +-/* Read registers up to 4 at a time */ +-static int imx355_read_reg(struct imx355 *imx355, u16 reg, u32 len, u32 *val) +-{ +- struct i2c_client *client = v4l2_get_subdevdata(&imx355->sd); +- struct i2c_msg msgs[2]; +- u8 addr_buf[2]; +- u8 data_buf[4] = { 0 }; +- int ret; +- +- if (len > 4) +- return -EINVAL; +- +- put_unaligned_be16(reg, addr_buf); +- /* Write register address */ +- msgs[0].addr = client->addr; +- msgs[0].flags = 0; +- msgs[0].len = ARRAY_SIZE(addr_buf); +- msgs[0].buf = addr_buf; +- +- /* Read data from register */ +- msgs[1].addr = client->addr; +- msgs[1].flags = I2C_M_RD; +- msgs[1].len = len; +- msgs[1].buf = &data_buf[4 - len]; +- +- ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); +- if (ret != ARRAY_SIZE(msgs)) +- return -EIO; +- +- *val = get_unaligned_be32(data_buf); +- +- return 0; +-} +- +-/* Write registers up to 4 at a time */ +-static int imx355_write_reg(struct imx355 *imx355, u16 reg, u32 len, u32 val) +-{ +- struct i2c_client *client = v4l2_get_subdevdata(&imx355->sd); +- u8 buf[6]; +- +- if (len > 4) +- return -EINVAL; +- +- put_unaligned_be16(reg, buf); +- put_unaligned_be32(val << (8 * (4 - len)), buf + 2); +- if (i2c_master_send(client, buf, len + 2) != len + 2) +- return -EIO; +- +- return 0; +-} +- +-/* Write a list of registers */ +-static int imx355_write_regs(struct imx355 *imx355, +- const struct imx355_reg *regs, u32 len) +-{ +- int ret; +- u32 i; +- +- for (i = 0; i < len; i++) { +- ret = imx355_write_reg(imx355, regs[i].address, 1, regs[i].val); +- if (ret) { +- dev_err_ratelimited(imx355->dev, +- "write reg 0x%4.4x return err %d", +- regs[i].address, ret); +- +- return ret; +- } +- } +- +- return 0; +-} +- +-/* Open sub-device */ +-static int imx355_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +-{ +- struct imx355 *imx355 = to_imx355(sd); +- struct v4l2_mbus_framefmt *try_fmt = +- v4l2_subdev_state_get_format(fh->state, 0); +- +- mutex_lock(&imx355->mutex); +- +- /* Initialize try_fmt */ +- try_fmt->width = imx355->cur_mode->width; +- try_fmt->height = imx355->cur_mode->height; +- try_fmt->code = imx355_get_format_code(imx355); +- try_fmt->field = V4L2_FIELD_NONE; +- +- mutex_unlock(&imx355->mutex); +- +- return 0; +-} +- + static int imx355_set_ctrl(struct v4l2_ctrl *ctrl) + { + struct imx355 *imx355 = container_of(ctrl->handler, + struct imx355, ctrl_handler); ++ const struct v4l2_mbus_framefmt *format = NULL; ++ struct v4l2_subdev_state *state; + s64 max; + int ret; + ++ state = v4l2_subdev_get_locked_active_state(&imx355->sd); ++ format = v4l2_subdev_state_get_format(state, 0); ++ + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ +- max = imx355->cur_mode->height + ctrl->val - 10; ++ max = format->height + ctrl->val - IMX355_EXPOSURE_OFFSET; + __v4l2_ctrl_modify_range(imx355->exposure, + imx355->exposure->minimum, + max, imx355->exposure->step, max); +@@ -1221,31 +621,31 @@ static int imx355_set_ctrl(struct v4l2_ctrl *ctrl) + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + /* Analog gain = 1024/(1024 - ctrl->val) times */ +- ret = imx355_write_reg(imx355, IMX355_REG_ANALOG_GAIN, 2, +- ctrl->val); ++ ret = cci_write(imx355->regmap, IMX355_REG_ANALOG_GAIN, ++ ctrl->val, NULL); + break; + case V4L2_CID_DIGITAL_GAIN: +- ret = imx355_write_reg(imx355, IMX355_REG_DIG_GAIN_GLOBAL, 2, +- ctrl->val); ++ ret = cci_write(imx355->regmap, IMX355_REG_DIG_GAIN_GLOBAL, ++ ctrl->val, NULL); + break; + case V4L2_CID_EXPOSURE: +- ret = imx355_write_reg(imx355, IMX355_REG_EXPOSURE, 2, +- ctrl->val); ++ ret = cci_write(imx355->regmap, IMX355_REG_EXPOSURE, ++ ctrl->val, NULL); + break; + case V4L2_CID_VBLANK: + /* Update FLL that meets expected vertical blanking */ +- ret = imx355_write_reg(imx355, IMX355_REG_FLL, 2, +- imx355->cur_mode->height + ctrl->val); ++ ret = cci_write(imx355->regmap, IMX355_REG_FLL, ++ format->height + ctrl->val, NULL); + break; + case V4L2_CID_TEST_PATTERN: +- ret = imx355_write_reg(imx355, IMX355_REG_TEST_PATTERN, +- 2, ctrl->val); ++ ret = cci_write(imx355->regmap, IMX355_REG_TEST_PATTERN, ++ ctrl->val, NULL); + break; + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: +- ret = imx355_write_reg(imx355, IMX355_REG_ORIENTATION, 1, +- imx355->hflip->val | +- imx355->vflip->val << 1); ++ ret = cci_write(imx355->regmap, IMX355_REG_ORIENTATION, ++ imx355->hflip->val | imx355->vflip->val << 1, ++ NULL); + break; + default: + ret = -EINVAL; +@@ -1272,9 +672,7 @@ static int imx355_enum_mbus_code(struct v4l2_subdev *sd, + if (code->index > 0) + return -EINVAL; + +- mutex_lock(&imx355->mutex); + code->code = imx355_get_format_code(imx355); +- mutex_unlock(&imx355->mutex); + + return 0; + } +@@ -1288,12 +686,9 @@ static int imx355_enum_frame_size(struct v4l2_subdev *sd, + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + +- mutex_lock(&imx355->mutex); + if (fse->code != imx355_get_format_code(imx355)) { +- mutex_unlock(&imx355->mutex); + return -EINVAL; + } +- mutex_unlock(&imx355->mutex); + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; +@@ -1311,36 +706,10 @@ static void imx355_update_pad_format(struct imx355 *imx355, + fmt->format.height = mode->height; + fmt->format.code = imx355_get_format_code(imx355); + fmt->format.field = V4L2_FIELD_NONE; +-} +- +-static int imx355_do_get_pad_format(struct imx355 *imx355, +- struct v4l2_subdev_state *sd_state, +- struct v4l2_subdev_format *fmt) +-{ +- struct v4l2_mbus_framefmt *framefmt; +- +- if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +- framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); +- fmt->format = *framefmt; +- } else { +- imx355_update_pad_format(imx355, imx355->cur_mode, fmt); +- } +- +- return 0; +-} +- +-static int imx355_get_pad_format(struct v4l2_subdev *sd, +- struct v4l2_subdev_state *sd_state, +- struct v4l2_subdev_format *fmt) +-{ +- struct imx355 *imx355 = to_imx355(sd); +- int ret; +- +- mutex_lock(&imx355->mutex); +- ret = imx355_do_get_pad_format(imx355, sd_state, fmt); +- mutex_unlock(&imx355->mutex); +- +- return ret; ++ fmt->format.colorspace = V4L2_COLORSPACE_RAW; ++ fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_601; ++ fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE; ++ fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; + } + + static int +@@ -1351,13 +720,8 @@ imx355_set_pad_format(struct v4l2_subdev *sd, + struct imx355 *imx355 = to_imx355(sd); + const struct imx355_mode *mode; + struct v4l2_mbus_framefmt *framefmt; +- s32 vblank_def; +- s32 vblank_min; ++ struct v4l2_rect *crop; + s64 h_blank; +- u64 pixel_rate; +- u32 height; +- +- mutex_lock(&imx355->mutex); + + /* + * Only one bayer order is supported. +@@ -1370,23 +734,25 @@ imx355_set_pad_format(struct v4l2_subdev *sd, + width, height, + fmt->format.width, fmt->format.height); + imx355_update_pad_format(imx355, mode, fmt); +- if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +- framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); +- *framefmt = fmt->format; +- } else { +- imx355->cur_mode = mode; +- pixel_rate = IMX355_LINK_FREQ_DEFAULT * 2 * 4; +- do_div(pixel_rate, 10); +- __v4l2_ctrl_s_ctrl_int64(imx355->pixel_rate, pixel_rate); ++ framefmt = v4l2_subdev_state_get_format(sd_state, 0); ++ ++ *framefmt = fmt->format; ++ ++ crop = v4l2_subdev_state_get_crop(sd_state, 0); ++ crop->width = mode->crop.width; ++ crop->height = mode->crop.height; ++ crop->left = mode->crop.left; ++ crop->top = mode->crop.top; ++ ++ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + /* Update limits and set FPS to default */ +- height = imx355->cur_mode->height; +- vblank_def = imx355->cur_mode->fll_def - height; +- vblank_min = imx355->cur_mode->fll_min - height; +- height = IMX355_FLL_MAX - height; +- __v4l2_ctrl_modify_range(imx355->vblank, vblank_min, height, 1, +- vblank_def); +- __v4l2_ctrl_s_ctrl(imx355->vblank, vblank_def); +- h_blank = mode->llp - imx355->cur_mode->width; ++ __v4l2_ctrl_modify_range(imx355->vblank, IMX355_VBLANK_MIN, ++ IMX355_FLL_MAX - mode->height, 1, ++ mode->fll_def - mode->height); ++ __v4l2_ctrl_s_ctrl(imx355->vblank, mode->fll_def - mode->height); ++ ++ h_blank = mode->llp - mode->width; ++ + /* + * Currently hblank is not changeable. + * So FPS control is done only by vblank. +@@ -1395,7 +761,42 @@ imx355_set_pad_format(struct v4l2_subdev *sd, + h_blank, 1, h_blank); + } + +- mutex_unlock(&imx355->mutex); ++ return 0; ++} ++ ++static int imx355_get_selection(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_selection *sel) ++{ ++ switch (sel->target) { ++ case V4L2_SEL_TGT_CROP: ++ sel->r = *v4l2_subdev_state_get_crop(sd_state, 0); ++ return 0; ++ case V4L2_SEL_TGT_CROP_DEFAULT: ++ case V4L2_SEL_TGT_CROP_BOUNDS: ++ case V4L2_SEL_TGT_NATIVE_SIZE: ++ sel->r.top = IMX355_PIXEL_ARRAY_TOP; ++ sel->r.left = IMX355_PIXEL_ARRAY_LEFT; ++ sel->r.width = IMX355_PIXEL_ARRAY_WIDTH; ++ sel->r.height = IMX355_PIXEL_ARRAY_HEIGHT; ++ ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++static int imx355_entity_init_state(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_state *sd_state) ++{ ++ struct v4l2_subdev_format fmt = { }; ++ ++ fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; ++ fmt.format.code = MEDIA_BUS_FMT_SRGGB10_1X10; ++ fmt.format.width = supported_modes[0].width; ++ fmt.format.height = supported_modes[0].height; ++ ++ imx355_set_pad_format(subdev, sd_state, &fmt); + + return 0; + } +@@ -1403,52 +804,98 @@ imx355_set_pad_format(struct v4l2_subdev *sd, + /* Start streaming */ + static int imx355_start_streaming(struct imx355 *imx355) + { +- const struct imx355_reg_list *reg_list; +- int ret; ++ const struct v4l2_mbus_framefmt *fmt; ++ struct v4l2_subdev_state *state; ++ const struct imx355_mode *mode; ++ int lane_idx = imx355->hwcfg->num_lanes == 4 ? 0 : 1; ++ struct v4l2_rect *crop; ++ u64 link_bitrate; ++ u8 binning_mode; ++ int ret = 0; + + /* Global Setting */ +- reg_list = &imx355_global_setting; +- ret = imx355_write_regs(imx355, reg_list->regs, reg_list->num_of_regs); +- if (ret) { +- dev_err(imx355->dev, "failed to set global settings"); +- return ret; +- } ++ cci_multi_reg_write(imx355->regmap, imx355_global_regs, ++ ARRAY_SIZE(imx355_global_regs), &ret); + +- /* Apply default values of current mode */ +- reg_list = &imx355->cur_mode->reg_list; +- ret = imx355_write_regs(imx355, reg_list->regs, reg_list->num_of_regs); +- if (ret) { +- dev_err(imx355->dev, "failed to set mode"); +- return ret; +- } ++ /* Apply values of current mode */ ++ state = v4l2_subdev_get_locked_active_state(&imx355->sd); ++ fmt = v4l2_subdev_state_get_format(state, 0); ++ crop = v4l2_subdev_state_get_crop(state, 0); ++ mode = v4l2_find_nearest_size(supported_modes, ++ ARRAY_SIZE(supported_modes), ++ width, height, fmt->width, fmt->height); ++ cci_multi_reg_write(imx355->regmap, mode->reg_list.regs, ++ mode->reg_list.num_of_regs, &ret); ++ ++ /* Set readout crop and size registers */ ++ cci_write(imx355->regmap, IMX355_REG_X_ADD_START, crop->left, ++ &ret); ++ cci_write(imx355->regmap, IMX355_REG_Y_ADD_START, crop->top, &ret); ++ cci_write(imx355->regmap, IMX355_REG_X_ADD_END, ++ crop->width + crop->left - 1, &ret); ++ cci_write(imx355->regmap, IMX355_REG_Y_ADD_END, ++ crop->height + crop->top - 1, &ret); ++ cci_write(imx355->regmap, IMX355_REG_X_OUT_SIZE, fmt->width, &ret); ++ cci_write(imx355->regmap, IMX355_REG_Y_OUT_SIZE, fmt->height, &ret); ++ ++ binning_mode = ((crop->width / fmt->width) << 4) | ++ (crop->height / fmt->height); ++ cci_write(imx355->regmap, IMX355_REG_BINNING_MODE, ++ binning_mode == 0x11 ? 0x00 : 0x01, &ret); ++ cci_write(imx355->regmap, IMX355_REG_BINNING_TYPE, binning_mode, &ret); ++ cci_write(imx355->regmap, IMX355_REG_BINNING_WEIGHTING, 0x00, &ret); ++ ++ /* Set PLL registers for the external clock frequency */ ++ cci_write(imx355->regmap, IMX355_REG_EXTCLK_FREQ, ++ imx355->clk_params->extclk_freq, &ret); ++ cci_write(imx355->regmap, IMX355_REG_PLL_OP_MUL, ++ imx355->clk_params->pll_op_mpy[lane_idx], &ret); ++ cci_write(imx355->regmap, IMX355_REG_PLL_OP_PREDIV, ++ imx355->clk_params->pll_op_prediv[lane_idx], &ret); ++ cci_write(imx355->regmap, IMX355_REG_PLL_IVT_SYSCK_DIV, ++ lane_idx ? 2 : 1, &ret); ++ ++ /* Set MIPI configuration */ ++ cci_write(imx355->regmap, IMX355_REG_LANE_SEL, ++ imx355->hwcfg->num_lanes - 1, &ret); ++ ++ link_bitrate = imx355->link_freq->qmenu_int[imx355->link_freq->val] * ++ imx355->hwcfg->num_lanes * 2; ++ do_div(link_bitrate, 1000000); ++ cci_write(imx355->regmap, IMX355_REG_REQ_LINK_BIT_RATE, link_bitrate, ++ &ret); + + /* set digital gain control to all color mode */ +- ret = imx355_write_reg(imx355, IMX355_REG_DPGA_USE_GLOBAL_GAIN, 1, 1); +- if (ret) +- return ret; ++ cci_write(imx355->regmap, IMX355_REG_DPGA_USE_GLOBAL_GAIN, 1, &ret); ++ ++ /* set line length */ ++ cci_write(imx355->regmap, IMX355_REG_LLP, ++ imx355->hblank->val + fmt->width, &ret); + + /* Apply customized values from user */ +- ret = __v4l2_ctrl_handler_setup(imx355->sd.ctrl_handler); +- if (ret) +- return ret; ++ if (!ret) ++ ret = __v4l2_ctrl_handler_setup(imx355->sd.ctrl_handler); + +- return imx355_write_reg(imx355, IMX355_REG_MODE_SELECT, +- 1, IMX355_MODE_STREAMING); ++ cci_write(imx355->regmap, IMX355_REG_MODE_SELECT, IMX355_MODE_STREAMING, ++ &ret); ++ ++ return ret; + } + + /* Stop streaming */ + static int imx355_stop_streaming(struct imx355 *imx355) + { +- return imx355_write_reg(imx355, IMX355_REG_MODE_SELECT, +- 1, IMX355_MODE_STANDBY); ++ return cci_write(imx355->regmap, IMX355_REG_MODE_SELECT, ++ IMX355_MODE_STANDBY, NULL); + } + + static int imx355_set_stream(struct v4l2_subdev *sd, int enable) + { + struct imx355 *imx355 = to_imx355(sd); ++ struct v4l2_subdev_state *state; + int ret = 0; + +- mutex_lock(&imx355->mutex); ++ state = v4l2_subdev_lock_and_get_active_state(sd); + + if (enable) { + ret = pm_runtime_resume_and_get(imx355->dev); +@@ -1464,21 +911,21 @@ static int imx355_set_stream(struct v4l2_subdev *sd, int enable) + goto err_rpm_put; + } else { + imx355_stop_streaming(imx355); +- pm_runtime_put(imx355->dev); ++ pm_runtime_put_autosuspend(imx355->dev); + } + + /* vflip and hflip cannot change during streaming */ + __v4l2_ctrl_grab(imx355->vflip, enable); + __v4l2_ctrl_grab(imx355->hflip, enable); + +- mutex_unlock(&imx355->mutex); ++ v4l2_subdev_unlock_state(state); + + return ret; + + err_rpm_put: +- pm_runtime_put(imx355->dev); ++ pm_runtime_put_autosuspend(imx355->dev); + err_unlock: +- mutex_unlock(&imx355->mutex); ++ v4l2_subdev_unlock_state(state); + + return ret; + } +@@ -1487,14 +934,14 @@ static int imx355_set_stream(struct v4l2_subdev *sd, int enable) + static int imx355_identify_module(struct imx355 *imx355) + { + int ret; +- u32 val; ++ u64 val; + +- ret = imx355_read_reg(imx355, IMX355_REG_CHIP_ID, 2, &val); ++ ret = cci_read(imx355->regmap, IMX355_REG_CHIP_ID, &val, NULL); + if (ret) + return ret; + + if (val != IMX355_CHIP_ID) { +- dev_err(imx355->dev, "chip id mismatch: %x!=%x", ++ dev_err(imx355->dev, "chip id mismatch: %x!=%llx", + IMX355_CHIP_ID, val); + return -EIO; + } +@@ -1512,9 +959,10 @@ static const struct v4l2_subdev_video_ops imx355_video_ops = { + + static const struct v4l2_subdev_pad_ops imx355_pad_ops = { + .enum_mbus_code = imx355_enum_mbus_code, +- .get_fmt = imx355_get_pad_format, ++ .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = imx355_set_pad_format, + .enum_frame_size = imx355_enum_frame_size, ++ .get_selection = imx355_get_selection, + }; + + static const struct v4l2_subdev_ops imx355_subdev_ops = { +@@ -1528,7 +976,7 @@ static const struct media_entity_operations imx355_subdev_entity_ops = { + }; + + static const struct v4l2_subdev_internal_ops imx355_internal_ops = { +- .open = imx355_open, ++ .init_state = imx355_entity_init_state, + }; + + static int imx355_power_off(struct device *dev) +@@ -1582,13 +1030,11 @@ static int imx355_init_controls(struct imx355 *imx355) + { + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl_handler *ctrl_hdlr; ++ const struct imx355_mode *mode = &supported_modes[0]; + s64 exposure_max; + s64 vblank_def; +- s64 vblank_min; + s64 hblank; + u64 pixel_rate; +- const struct imx355_mode *mode; +- u32 max; + int ret; + + ctrl_hdlr = &imx355->ctrl_handler; +@@ -1596,40 +1042,34 @@ static int imx355_init_controls(struct imx355 *imx355) + if (ret) + return ret; + +- ctrl_hdlr->lock = &imx355->mutex; +- max = ARRAY_SIZE(link_freq_menu_items) - 1; + imx355->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx355_ctrl_ops, +- V4L2_CID_LINK_FREQ, max, 0, +- link_freq_menu_items); ++ V4L2_CID_LINK_FREQ, 0, 0, ++ &imx355->hwcfg->link_freq_menu); + if (imx355->link_freq) + imx355->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + /* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ +- pixel_rate = IMX355_LINK_FREQ_DEFAULT * 2 * 4; ++ pixel_rate = imx355->hwcfg->link_freq_menu * 2 * imx355->hwcfg->num_lanes; + do_div(pixel_rate, 10); +- /* By default, PIXEL_RATE is read only */ +- imx355->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, +- V4L2_CID_PIXEL_RATE, pixel_rate, +- pixel_rate, 1, pixel_rate); ++ ++ v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, V4L2_CID_PIXEL_RATE, ++ pixel_rate, pixel_rate, 1, pixel_rate); + + /* Initialize vblank/hblank/exposure parameters based on current mode */ +- mode = imx355->cur_mode; + vblank_def = mode->fll_def - mode->height; +- vblank_min = mode->fll_min - mode->height; + imx355->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, +- V4L2_CID_VBLANK, vblank_min, ++ V4L2_CID_VBLANK, IMX355_VBLANK_MIN, + IMX355_FLL_MAX - mode->height, + 1, vblank_def); + + hblank = mode->llp - mode->width; +- imx355->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, +- V4L2_CID_HBLANK, hblank, hblank, +- 1, hblank); ++ imx355->hblank = v4l2_ctrl_new_std(ctrl_hdlr, NULL, V4L2_CID_HBLANK, ++ hblank, hblank, 1, hblank); + if (imx355->hblank) + imx355->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + /* fll >= exposure time + adjust parameter (default value is 10) */ +- exposure_max = mode->fll_def - 10; ++ exposure_max = mode->fll_def - IMX355_EXPOSURE_OFFSET; + imx355->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, + V4L2_CID_EXPOSURE, + IMX355_EXPOSURE_MIN, exposure_max, +@@ -1683,14 +1123,17 @@ static int imx355_init_controls(struct imx355 *imx355) + return ret; + } + +-static struct imx355_hwcfg *imx355_get_hwcfg(struct device *dev) ++static struct imx355_hwcfg *imx355_get_hwcfg(struct imx355 *imx355) + { ++ struct device *dev = imx355->dev; + struct imx355_hwcfg *cfg; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; ++ const struct imx355_clk_params *clk = imx355->clk_params; + struct fwnode_handle *ep; + struct fwnode_handle *fwnode = dev_fwnode(dev); ++ int lane_idx; + int ret; + + if (!fwnode) +@@ -1708,13 +1151,18 @@ static struct imx355_hwcfg *imx355_get_hwcfg(struct device *dev) + if (!cfg) + goto out_err; + +- if (bus_cfg.bus.mipi_csi2.num_data_lanes != IMX355_DATA_LANES) ++ if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2 && ++ bus_cfg.bus.mipi_csi2.num_data_lanes != 4) + goto out_err; + ++ cfg->num_lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; ++ ++ lane_idx = cfg->num_lanes == 4 ? 0 : 1; ++ cfg->link_freq_menu = (clk->ext_clk * clk->pll_op_mpy[lane_idx]) / ++ (clk->pll_op_prediv[lane_idx] * 2); + ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, +- link_freq_menu_items, +- ARRAY_SIZE(link_freq_menu_items), ++ &cfg->link_freq_menu, 1, + &cfg->link_freq_bitmap); + if (ret) + goto out_err; +@@ -1741,7 +1189,10 @@ static int imx355_probe(struct i2c_client *client) + + imx355->dev = &client->dev; + +- mutex_init(&imx355->mutex); ++ imx355->regmap = devm_cci_regmap_init_i2c(client, 16); ++ if (IS_ERR(imx355->regmap)) ++ return dev_err_probe(imx355->dev, PTR_ERR(imx355->regmap), ++ "Unable to initialize I2C\n"); + + imx355->clk = devm_v4l2_sensor_clk_get(imx355->dev, NULL); + if (IS_ERR(imx355->clk)) +@@ -1749,7 +1200,13 @@ static int imx355_probe(struct i2c_client *client) + "failed to get clock\n"); + + freq = clk_get_rate(imx355->clk); +- if (freq != IMX355_EXT_CLK) ++ for (unsigned int i = 0; i < ARRAY_SIZE(imx355_clk_params); i++) { ++ if (freq == imx355_clk_params[i].ext_clk) { ++ imx355->clk_params = &imx355_clk_params[i]; ++ break; ++ } ++ } ++ if (!imx355->clk_params) + return dev_err_probe(imx355->dev, -EINVAL, + "external clock %lu is not supported\n", + freq); +@@ -1760,7 +1217,7 @@ static int imx355_probe(struct i2c_client *client) + &imx355->supplies); + if (ret) { + dev_err_probe(imx355->dev, ret, "could not get regulators"); +- goto error_probe; ++ return ret; + } + + imx355->reset_gpio = devm_gpiod_get_optional(imx355->dev, "reset", +@@ -1768,22 +1225,21 @@ static int imx355_probe(struct i2c_client *client) + if (IS_ERR(imx355->reset_gpio)) { + ret = dev_err_probe(imx355->dev, PTR_ERR(imx355->reset_gpio), + "failed to get gpios"); +- goto error_probe; ++ return ret; + } + + /* Initialize subdev */ + v4l2_i2c_subdev_init(&imx355->sd, client, &imx355_subdev_ops); + +- imx355->hwcfg = imx355_get_hwcfg(imx355->dev); ++ imx355->hwcfg = imx355_get_hwcfg(imx355); + if (!imx355->hwcfg) { + dev_err(imx355->dev, "failed to get hwcfg"); +- ret = -ENODEV; +- goto error_probe; ++ return -ENODEV; + } + + ret = imx355_power_on(imx355->dev); + if (ret) +- goto error_probe; ++ return ret; + + /* Check module identity */ + ret = imx355_identify_module(imx355); +@@ -1792,9 +1248,6 @@ static int imx355_probe(struct i2c_client *client) + goto error_power_off; + } + +- /* Set default mode to max resolution */ +- imx355->cur_mode = &supported_modes[0]; +- + ret = imx355_init_controls(imx355); + if (ret) { + dev_err(imx355->dev, "failed to init controls: %d", ret); +@@ -1816,24 +1269,36 @@ static int imx355_probe(struct i2c_client *client) + goto error_handler_free; + } + ++ imx355->sd.state_lock = imx355->ctrl_handler.lock; ++ ret = v4l2_subdev_init_finalize(&imx355->sd); ++ if (ret < 0) { ++ dev_err_probe(imx355->dev, ret, "subdev init error\n"); ++ goto error_media_entity_free; ++ } ++ + /* + * Device is already turned on by i2c-core with ACPI domain PM. + * Enable runtime PM and turn off the device. + */ + pm_runtime_set_active(imx355->dev); + pm_runtime_enable(imx355->dev); ++ pm_runtime_set_autosuspend_delay(imx355->dev, 1000); ++ pm_runtime_use_autosuspend(imx355->dev); + + ret = v4l2_async_register_subdev_sensor(&imx355->sd); + if (ret < 0) +- goto error_media_entity_runtime_pm; ++ goto error_subdev_cleanup_runtime_pm; + + pm_runtime_idle(imx355->dev); + + return 0; + +-error_media_entity_runtime_pm: ++error_subdev_cleanup_runtime_pm: + pm_runtime_disable(imx355->dev); + pm_runtime_set_suspended(imx355->dev); ++ pm_runtime_dont_use_autosuspend(imx355->dev); ++ v4l2_subdev_cleanup(&imx355->sd); ++error_media_entity_free: + media_entity_cleanup(&imx355->sd.entity); + + error_handler_free: +@@ -1842,9 +1307,6 @@ static int imx355_probe(struct i2c_client *client) + error_power_off: + imx355_power_off(imx355->dev); + +-error_probe: +- mutex_destroy(&imx355->mutex); +- + return ret; + } + +@@ -1854,6 +1316,7 @@ static void imx355_remove(struct i2c_client *client) + struct imx355 *imx355 = to_imx355(sd); + + v4l2_async_unregister_subdev(sd); ++ v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + +@@ -1864,7 +1327,7 @@ static void imx355_remove(struct i2c_client *client) + pm_runtime_set_suspended(imx355->dev); + } + +- mutex_destroy(&imx355->mutex); ++ pm_runtime_dont_use_autosuspend(imx355->dev); + } + + static const struct acpi_device_id imx355_acpi_ids[] __maybe_unused = { +diff --git a/drivers/media/i2c/imx471.c b/drivers/media/i2c/imx471.c +new file mode 100644 +--- /dev/null ++++ b/drivers/media/i2c/imx471.c +@@ -0,0 +1,957 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * imx471.c - imx471 sensor driver ++ * ++ * Copyright (C) 2025 Intel Corporation ++ * Copyright (C) 2026 Kate Hsuan ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define IMX471_REG_MODE_SELECT CCI_REG8(0x0100) ++#define IMX471_MODE_STANDBY 0x00 ++#define IMX471_MODE_STREAMING 0x01 ++ ++/* Chip ID */ ++#define IMX471_REG_CHIP_ID CCI_REG16(0x0016) ++#define IMX471_CHIP_ID 0x0471 ++ ++/* V_TIMING internal */ ++#define IMX471_REG_FLL CCI_REG16(0x0340) ++#define IMX471_FLL_MAX 0xffff ++ ++/* Exposure control */ ++#define IMX471_REG_EXPOSURE CCI_REG16(0x0202) ++#define IMX471_EXPOSURE_MIN 1 ++#define IMX471_EXPOSURE_STEP 1 ++#define IMX471_EXPOSURE_DEFAULT 1270 ++ ++/* Default exposure margin */ ++#define IMX471_EXPOSURE_MARGIN 18 ++ ++/* Analog gain control */ ++#define IMX471_REG_ANALOG_GAIN CCI_REG16(0x0204) ++#define IMX471_ANA_GAIN_MIN 0 ++#define IMX471_ANA_GAIN_MAX 800 ++#define IMX471_ANA_GAIN_STEP 1 ++#define IMX471_ANA_GAIN_DEFAULT 0 ++ ++/* Digital gain control */ ++#define IMX471_REG_DPGA_USE_GLOBAL_GAIN CCI_REG16(0x3ff9) ++#define IMX471_REG_DIG_GAIN_GLOBAL CCI_REG16(0x020e) ++#define IMX471_DGTL_GAIN_MIN 256 ++#define IMX471_DGTL_GAIN_MAX 4095 ++#define IMX471_DGTL_GAIN_STEP 1 ++#define IMX471_DGTL_GAIN_DEFAULT 256 ++ ++/* HFLIP and VFLIP control */ ++#define IMX471_REG_ORIENTATION CCI_REG8(0x0101) ++ ++/* Test Pattern Control */ ++#define IMX471_REG_TEST_PATTERN CCI_REG8(0x0600) ++ ++/* default link frequency and external clock */ ++#define IMX471_LINK_FREQ_DEFAULT 200000000LL ++#define IMX471_EXT_CLK 19200000 ++ ++/* PLL */ ++#define IMX471_REG_VTPXCK_DIV CCI_REG8(0x0301) ++#define IMX471_REG_VTSYCK_DIV CCI_REG8(0x0303) ++#define IMX471_REG_PREPLLCK_VT_DIV CCI_REG8(0x0305) ++#define IMX471_REG_PLL_VT_MPY CCI_REG16(0x0306) ++#define IMX471_REG_OPPXCK_DIV CCI_REG8(0x0309) ++#define IMX471_REG_OPSYCK_DIV CCI_REG8(0x030b) ++#define IMX471_REG_PLL_MULT_DRIV CCI_REG8(0x0310) ++#define IMX471_PLL_SINGLE 0 ++#define IMX471_PLL_DUAL 1 ++ ++/* IMX471 native and active pixel array size */ ++#define IMX471_NATIVE_WIDTH 4672 ++#define IMX471_NATIVE_HEIGHT 3512 ++#define IMX471_PIXEL_ARRAY_LEFT 8 ++#define IMX471_PIXEL_ARRAY_TOP 8 ++#define IMX471_PIXEL_ARRAY_WIDTH 4656 ++#define IMX471_PIXEL_ARRAY_HEIGHT 3496 ++ ++#define IMX471_REG_EXCK_FREQ CCI_REG16(0x0136) ++ ++#define IMX471_REG_CSI_DATA_FORMAT CCI_REG16(0x0112) ++#define IMX471_CSI_DATA_FORMAT_RAW10 0x0a0a ++ ++#define IMX471_REG_CSI_LANE_MODE CCI_REG8(0x0114) ++#define IMX471_CSI_2_LANE_MODE 1 ++#define IMX471_CSI_4_LANE_MODE 3 ++ ++#define IMX471_REG_X_ADD_STA CCI_REG16(0x0344) ++#define IMX471_REG_Y_ADD_STA CCI_REG16(0x0346) ++#define IMX471_REG_X_ADD_END CCI_REG16(0x0348) ++#define IMX471_REG_Y_ADD_END CCI_REG16(0x034a) ++#define IMX471_REG_X_OUTPUT_SIZE CCI_REG16(0x034c) ++#define IMX471_REG_Y_OUTPUT_SIZE CCI_REG16(0x034e) ++#define IMX471_REG_X_EVEN_INC CCI_REG8(0x0381) ++#define IMX471_REG_X_ODD_INC CCI_REG8(0x0383) ++#define IMX471_REG_Y_EVEN_INC CCI_REG8(0x0385) ++#define IMX471_REG_Y_ODD_INC CCI_REG8(0x0387) ++ ++#define IMX471_REG_DIG_CROP_X_OFFSET CCI_REG16(0x0408) ++#define IMX471_REG_DIG_CROP_Y_OFFSET CCI_REG16(0x040a) ++#define IMX471_REG_DIG_CROP_WIDTH CCI_REG16(0x040c) ++#define IMX471_REG_DIG_CROP_HEIGHT CCI_REG16(0x040e) ++ ++/* Binning mode */ ++#define IMX471_REG_BINNING_MODE CCI_REG8(0x0900) ++#define IMX471_BINNING_NONE 0 ++#define IMX471_BINNING_ENABLE 1 ++#define IMX471_REG_BINNING_TYPE CCI_REG8(0x0901) ++#define IMX471_REG_BINNING_WEIGHTING CCI_REG8(0x0902) ++ ++#define to_imx471(_sd) container_of_const(_sd, struct imx471, sd) ++ ++static const char * const imx471_supply_name[] = { ++ "vana", ++}; ++ ++struct imx471_mode { ++ u32 width; ++ u32 height; ++ ++ /* V-timing */ ++ u32 fll_def; ++ u32 fll_min; ++ ++ /* H-timing */ ++ u32 llp; ++ ++ const struct cci_reg_sequence *default_mode_regs; ++ unsigned int default_mode_regs_length; ++}; ++ ++struct imx471 { ++ struct v4l2_subdev sd; ++ struct media_pad pad; ++ ++ struct v4l2_ctrl_handler ctrl_handler; ++ struct v4l2_ctrl *vblank; ++ struct v4l2_ctrl *hblank; ++ struct v4l2_ctrl *vflip; ++ struct v4l2_ctrl *hflip; ++ struct v4l2_ctrl *exposure; ++ ++ struct gpio_desc *reset_gpio; ++ struct regulator_bulk_data supplies[ARRAY_SIZE(imx471_supply_name)]; ++ struct clk *img_clk; ++ ++ struct device *dev; ++ struct regmap *regmap; ++}; ++ ++static const struct cci_reg_sequence imx471_global_regs[] = { ++ { IMX471_REG_EXCK_FREQ, 0x1333 }, ++ { CCI_REG8(0x3c7e), 0x08 }, ++ { CCI_REG8(0x3c7f), 0x05 }, ++ { CCI_REG8(0x3e35), 0x00 }, ++ { CCI_REG8(0x3e36), 0x00 }, ++ { CCI_REG8(0x3e37), 0x00 }, ++ { CCI_REG8(0x3f7f), 0x01 }, ++ { CCI_REG8(0x4431), 0x04 }, ++ { CCI_REG8(0x531c), 0x01 }, ++ { CCI_REG8(0x531d), 0x02 }, ++ { CCI_REG8(0x531e), 0x04 }, ++ { CCI_REG8(0x5928), 0x00 }, ++ { CCI_REG8(0x5929), 0x2f }, ++ { CCI_REG8(0x592a), 0x00 }, ++ { CCI_REG8(0x592b), 0x85 }, ++ { CCI_REG8(0x592c), 0x00 }, ++ { CCI_REG8(0x592d), 0x32 }, ++ { CCI_REG8(0x592e), 0x00 }, ++ { CCI_REG8(0x592f), 0x88 }, ++ { CCI_REG8(0x5930), 0x00 }, ++ { CCI_REG8(0x5931), 0x3d }, ++ { CCI_REG8(0x5932), 0x00 }, ++ { CCI_REG8(0x5933), 0x93 }, ++ { CCI_REG8(0x5938), 0x00 }, ++ { CCI_REG8(0x5939), 0x24 }, ++ { CCI_REG8(0x593a), 0x00 }, ++ { CCI_REG8(0x593b), 0x7a }, ++ { CCI_REG8(0x593c), 0x00 }, ++ { CCI_REG8(0x593d), 0x24 }, ++ { CCI_REG8(0x593e), 0x00 }, ++ { CCI_REG8(0x593f), 0x7a }, ++ { CCI_REG8(0x5940), 0x00 }, ++ { CCI_REG8(0x5941), 0x2f }, ++ { CCI_REG8(0x5942), 0x00 }, ++ { CCI_REG8(0x5943), 0x85 }, ++ { CCI_REG8(0x5f0e), 0x6e }, ++ { CCI_REG8(0x5f11), 0xc6 }, ++ { CCI_REG8(0x5f17), 0x5e }, ++ { CCI_REG8(0x7990), 0x01 }, ++ { CCI_REG8(0x7993), 0x5d }, ++ { CCI_REG8(0x7994), 0x5d }, ++ { CCI_REG8(0x7995), 0xa1 }, ++ { CCI_REG8(0x799a), 0x01 }, ++ { CCI_REG8(0x799d), 0x00 }, ++ { CCI_REG8(0x8169), 0x01 }, ++ { CCI_REG8(0x8359), 0x01 }, ++ { CCI_REG8(0x9302), 0x1e }, ++ { CCI_REG8(0x9306), 0x1f }, ++ { CCI_REG8(0x930a), 0x26 }, ++ { CCI_REG8(0x930e), 0x23 }, ++ { CCI_REG8(0x9312), 0x23 }, ++ { CCI_REG8(0x9316), 0x2c }, ++ { CCI_REG8(0x9317), 0x19 }, ++ { CCI_REG8(0xb046), 0x01 }, ++ { CCI_REG8(0xb048), 0x01 }, ++}; ++ ++static const struct cci_reg_sequence mode_1928x1088_regs[] = { ++ { IMX471_REG_CSI_DATA_FORMAT, IMX471_CSI_DATA_FORMAT_RAW10 }, ++ { IMX471_REG_CSI_LANE_MODE, IMX471_CSI_4_LANE_MODE }, ++ { IMX471_REG_X_ADD_STA, 8 }, ++ { IMX471_REG_Y_ADD_STA, 408 }, ++ { IMX471_REG_X_ADD_END, 4647 }, ++ { IMX471_REG_Y_ADD_END, 3051 }, ++ { IMX471_REG_X_EVEN_INC, 1 }, ++ { IMX471_REG_X_ODD_INC, 1 }, ++ { IMX471_REG_Y_EVEN_INC, 1 }, ++ { IMX471_REG_Y_ODD_INC, 1 }, ++ { IMX471_REG_BINNING_MODE, IMX471_BINNING_ENABLE }, ++ { IMX471_REG_BINNING_TYPE, 0x22 }, ++ { IMX471_REG_BINNING_WEIGHTING, 0x08 }, ++ { IMX471_REG_DIG_CROP_X_OFFSET, 208 }, ++ { IMX471_REG_DIG_CROP_Y_OFFSET, 108 }, ++ { IMX471_REG_DIG_CROP_WIDTH, 1928 }, ++ { IMX471_REG_DIG_CROP_HEIGHT, 1088 }, ++ { IMX471_REG_X_OUTPUT_SIZE, 1928 }, ++ { IMX471_REG_Y_OUTPUT_SIZE, 1088 }, ++ { IMX471_REG_VTPXCK_DIV, 0x06 }, ++ { IMX471_REG_VTSYCK_DIV, 0x02 }, ++ { IMX471_REG_PREPLLCK_VT_DIV, 0x02 }, ++ { IMX471_REG_PLL_VT_MPY, 0x0079 }, ++ { IMX471_REG_OPSYCK_DIV, 0x01 }, ++ { CCI_REG8(0x030d), 0x02 }, ++ { CCI_REG8(0x030e), 0x00 }, ++ { CCI_REG8(0x030f), 0x53 }, ++ { IMX471_REG_PLL_MULT_DRIV, IMX471_PLL_DUAL }, ++ { CCI_REG8(0x3f4c), 0x81 }, ++ { CCI_REG8(0x3f4d), 0x81 }, ++ { CCI_REG8(0x3f78), 0x01 }, ++ { CCI_REG8(0x3f79), 0x31 }, ++ { CCI_REG8(0x3ffe), 0x00 }, ++ { CCI_REG8(0x3fff), 0x8a }, ++ { CCI_REG8(0x5f0a), 0xb6 }, ++}; ++ ++static const char * const imx471_test_pattern_menu[] = { ++ "Disabled", ++ "Solid Colour", ++ "Eight Vertical Colour Bars", ++ "Colour Bars With Fade to Grey", ++ "Pseudorandom Sequence (PN9)", ++}; ++ ++static const s64 link_freq_menu_items[] = { ++ IMX471_LINK_FREQ_DEFAULT, ++}; ++ ++/* ++ * The Bayer formats for the flipping. ++ * - no flip ++ * - h flip ++ * - v flip ++ * - h and v flips ++ */ ++static const u32 imx471_hv_flips_bayer_order[] = { ++ MEDIA_BUS_FMT_SRGGB10_1X10, ++ MEDIA_BUS_FMT_SGRBG10_1X10, ++ MEDIA_BUS_FMT_SGBRG10_1X10, ++ MEDIA_BUS_FMT_SBGGR10_1X10, ++}; ++ ++static const struct imx471_mode imx471_modes[] = { ++ { ++ .width = 1928, ++ .height = 1088, ++ .fll_def = 1308, ++ .fll_min = 1308, ++ .llp = 2328, ++ .default_mode_regs = mode_1928x1088_regs, ++ .default_mode_regs_length = ARRAY_SIZE(mode_1928x1088_regs), ++ }, ++}; ++ ++static int imx471_get_regulators(struct device *dev, struct imx471 *sensor) ++{ ++ for (unsigned int i = 0; i < ARRAY_SIZE(imx471_supply_name); i++) ++ sensor->supplies[i].supply = imx471_supply_name[i]; ++ ++ return devm_regulator_bulk_get(dev, ARRAY_SIZE(imx471_supply_name), ++ sensor->supplies); ++} ++ ++static int imx471_set_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct imx471 *sensor = container_of_const(ctrl->handler, ++ struct imx471, ++ ctrl_handler); ++ struct v4l2_subdev_state *state = ++ v4l2_subdev_get_locked_active_state(&sensor->sd); ++ const struct v4l2_mbus_framefmt *format = ++ v4l2_subdev_state_get_format(state, 0); ++ int ret; ++ ++ if (ctrl->id == V4L2_CID_VBLANK) { ++ s64 exposure_max = format->height + ctrl->val - ++ IMX471_EXPOSURE_MARGIN; ++ ret = __v4l2_ctrl_modify_range(sensor->exposure, ++ sensor->exposure->minimum, ++ exposure_max, ++ sensor->exposure->step, ++ exposure_max); ++ if (ret) ++ return ret; ++ } ++ ++ if (!pm_runtime_get_if_in_use(sensor->dev)) ++ return 0; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_ANALOGUE_GAIN: ++ ret = cci_write(sensor->regmap, IMX471_REG_ANALOG_GAIN, ++ ctrl->val, NULL); ++ break; ++ case V4L2_CID_DIGITAL_GAIN: ++ ret = cci_write(sensor->regmap, IMX471_REG_DIG_GAIN_GLOBAL, ++ ctrl->val, NULL); ++ break; ++ case V4L2_CID_EXPOSURE: ++ ret = cci_write(sensor->regmap, IMX471_REG_EXPOSURE, ++ ctrl->val, NULL); ++ break; ++ case V4L2_CID_VBLANK: ++ /* Update FLL that meets expected vertical blanking */ ++ ret = cci_write(sensor->regmap, IMX471_REG_FLL, ++ format->height + ctrl->val, NULL); ++ break; ++ case V4L2_CID_TEST_PATTERN: ++ ret = cci_write(sensor->regmap, IMX471_REG_TEST_PATTERN, ++ ctrl->val, NULL); ++ break; ++ case V4L2_CID_HFLIP: ++ case V4L2_CID_VFLIP: ++ ret = cci_write(sensor->regmap, IMX471_REG_ORIENTATION, ++ sensor->hflip->val | sensor->vflip->val << 1, ++ NULL); ++ break; ++ default: ++ ret = -EINVAL; ++ dev_err(sensor->dev, "ctrl(id:0x%x,val:0x%x) is not handled\n", ++ ctrl->id, ctrl->val); ++ break; ++ } ++ ++ pm_runtime_put(sensor->dev); ++ ++ return ret; ++} ++ ++static const struct v4l2_ctrl_ops imx471_ctrl_ops = { ++ .s_ctrl = imx471_set_ctrl, ++}; ++ ++static u32 imx471_get_format_code(struct imx471 *sensor) ++{ ++ unsigned int i; ++ ++ i = (sensor->vflip->val ? 2 : 0) | (sensor->hflip->val ? 1 : 0); ++ ++ return imx471_hv_flips_bayer_order[i]; ++} ++ ++static int imx471_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ struct imx471 *sensor = to_imx471(sd); ++ ++ if (code->index >= (ARRAY_SIZE(imx471_hv_flips_bayer_order) / 4)) ++ return -EINVAL; ++ ++ code->code = imx471_get_format_code(sensor); ++ ++ return 0; ++} ++ ++static int imx471_enum_frame_size(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_frame_size_enum *fse) ++{ ++ if (fse->index >= ARRAY_SIZE(imx471_modes)) ++ return -EINVAL; ++ ++ fse->min_width = imx471_modes[fse->index].width; ++ fse->max_width = fse->min_width; ++ fse->min_height = imx471_modes[fse->index].height; ++ fse->max_height = fse->min_height; ++ ++ return 0; ++} ++ ++static void imx471_update_pad_format(struct imx471 *sensor, ++ const struct imx471_mode *mode, ++ struct v4l2_subdev_format *fmt) ++{ ++ fmt->format.code = imx471_get_format_code(sensor); ++ fmt->format.width = mode->width; ++ fmt->format.height = mode->height; ++ fmt->format.field = V4L2_FIELD_NONE; ++} ++ ++static int imx471_set_pad_format(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct imx471 *sensor = to_imx471(sd); ++ const struct imx471_mode *mode; ++ int h_blank, ret; ++ ++ mode = v4l2_find_nearest_size(imx471_modes, ARRAY_SIZE(imx471_modes), ++ width, height, fmt->format.width, ++ fmt->format.height); ++ ++ imx471_update_pad_format(sensor, mode, fmt); ++ ++ *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; ++ ++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) ++ return 0; ++ ++ if (media_entity_is_streaming(&sensor->sd.entity)) ++ return -EBUSY; ++ ++ ret = __v4l2_ctrl_modify_range(sensor->vblank, ++ mode->fll_min - mode->height, ++ IMX471_FLL_MAX - mode->height, ++ 1, ++ mode->fll_def - mode->height); ++ if (ret) ++ return ret; ++ ++ h_blank = mode->llp - mode->width; ++ /* ++ * Currently hblank is not changeable. ++ * So FPS control is done only by vblank. ++ */ ++ return __v4l2_ctrl_modify_range(sensor->hblank, h_blank, ++ h_blank, 1, h_blank); ++} ++ ++static int imx471_get_selection(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state, ++ struct v4l2_subdev_selection *sel) ++{ ++ switch (sel->target) { ++ case V4L2_SEL_TGT_CROP: ++ sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); ++ break; ++ ++ case V4L2_SEL_TGT_NATIVE_SIZE: ++ sel->r.top = 0; ++ sel->r.left = 0; ++ sel->r.width = IMX471_NATIVE_WIDTH; ++ sel->r.height = IMX471_NATIVE_HEIGHT; ++ return 0; ++ ++ case V4L2_SEL_TGT_CROP_DEFAULT: ++ case V4L2_SEL_TGT_CROP_BOUNDS: ++ sel->r.top = IMX471_PIXEL_ARRAY_TOP; ++ sel->r.left = IMX471_PIXEL_ARRAY_LEFT; ++ sel->r.width = IMX471_PIXEL_ARRAY_WIDTH; ++ sel->r.height = IMX471_PIXEL_ARRAY_HEIGHT; ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++static int imx471_init_state(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *sd_state) ++{ ++ struct v4l2_subdev_format fmt = { ++ .which = V4L2_SUBDEV_FORMAT_TRY, ++ .format = { ++ .code = MEDIA_BUS_FMT_SRGGB10_1X10, ++ .width = imx471_modes[0].width, ++ .height = imx471_modes[0].height, ++ }, ++ }; ++ ++ return imx471_set_pad_format(sd, sd_state, &fmt); ++} ++ ++static int imx471_identify_module(struct imx471 *sensor) ++{ ++ int ret; ++ u64 val; ++ ++ ret = cci_read(sensor->regmap, IMX471_REG_CHIP_ID, &val, NULL); ++ if (ret) ++ return dev_err_probe(sensor->dev, ret, ++ "failed to read chip id\n"); ++ ++ if (val != IMX471_CHIP_ID) ++ return dev_err_probe(sensor->dev, -EIO, ++ "chip id mismatch: %x!=%llx\n", ++ IMX471_CHIP_ID, val); ++ ++ return 0; ++} ++ ++static int imx471_power_off(struct device *dev) ++{ ++ struct v4l2_subdev *sd = dev_get_drvdata(dev); ++ struct imx471 *sensor = to_imx471(sd); ++ ++ clk_disable_unprepare(sensor->img_clk); ++ gpiod_set_value_cansleep(sensor->reset_gpio, 1); ++ ++ regulator_bulk_disable(ARRAY_SIZE(imx471_supply_name), ++ sensor->supplies); ++ ++ return 0; ++} ++ ++static int imx471_power_on(struct device *dev) ++{ ++ struct v4l2_subdev *sd = dev_get_drvdata(dev); ++ struct imx471 *sensor = to_imx471(sd); ++ int ret; ++ ++ ret = regulator_bulk_enable(ARRAY_SIZE(imx471_supply_name), ++ sensor->supplies); ++ if (ret < 0) { ++ dev_err(dev, "failed to enable regulators: %d\n", ret); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(sensor->img_clk); ++ if (ret < 0) { ++ regulator_bulk_disable(ARRAY_SIZE(imx471_supply_name), ++ sensor->supplies); ++ dev_err(dev, "failed to enable imaging clock: %d\n", ret); ++ return ret; ++ } ++ ++ gpiod_set_value_cansleep(sensor->reset_gpio, 0); ++ ++ usleep_range(10000, 15000); ++ ++ return 0; ++} ++ ++static int imx471_enable_stream(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *state, ++ u32 pad, u64 streams_mask) ++{ ++ struct imx471 *sensor = to_imx471(sd); ++ const struct imx471_mode *mode; ++ struct v4l2_mbus_framefmt *fmt; ++ int ret; ++ ++ ret = pm_runtime_resume_and_get(sensor->dev); ++ if (ret) ++ return ret; ++ ++ ret = imx471_identify_module(sensor); ++ if (ret) ++ goto error_powerdown; ++ ++ ret = cci_multi_reg_write(sensor->regmap, imx471_global_regs, ++ ARRAY_SIZE(imx471_global_regs), NULL); ++ if (ret) { ++ dev_err(sensor->dev, "failed to set global settings: %d\n", ++ ret); ++ goto error_powerdown; ++ } ++ ++ fmt = v4l2_subdev_state_get_format(state, 0); ++ mode = v4l2_find_nearest_size(imx471_modes, ARRAY_SIZE(imx471_modes), ++ width, height, fmt->width, fmt->height); ++ ++ ret = cci_multi_reg_write(sensor->regmap, mode->default_mode_regs, ++ mode->default_mode_regs_length, NULL); ++ if (ret) { ++ dev_err(sensor->dev, "failed to set mode: %d\n", ret); ++ goto error_powerdown; ++ } ++ ++ ret = cci_write(sensor->regmap, IMX471_REG_DPGA_USE_GLOBAL_GAIN, 1, ++ NULL); ++ if (ret) ++ goto error_powerdown; ++ ++ ret = __v4l2_ctrl_handler_setup(&sensor->ctrl_handler); ++ if (ret) ++ goto error_powerdown; ++ ++ ret = cci_write(sensor->regmap, IMX471_REG_MODE_SELECT, ++ IMX471_MODE_STREAMING, NULL); ++ if (ret) ++ goto error_powerdown; ++ ++ __v4l2_ctrl_grab(sensor->vflip, true); ++ __v4l2_ctrl_grab(sensor->hflip, true); ++ ++ return ret; ++ ++error_powerdown: ++ pm_runtime_put(sensor->dev); ++ ++ return ret; ++} ++ ++static int imx471_disable_stream(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *state, ++ u32 pad, u64 streams_mask) ++{ ++ struct imx471 *sensor = to_imx471(sd); ++ int ret; ++ ++ ret = cci_write(sensor->regmap, IMX471_REG_MODE_SELECT, ++ IMX471_MODE_STANDBY, NULL); ++ pm_runtime_put(sensor->dev); ++ ++ if (ret) ++ dev_err(sensor->dev, ++ "failed to disable stream with return value: %d\n", ++ ret); ++ ++ __v4l2_ctrl_grab(sensor->vflip, false); ++ __v4l2_ctrl_grab(sensor->hflip, false); ++ ++ return 0; ++} ++ ++static const struct v4l2_subdev_video_ops imx471_video_ops = { ++ .s_stream = v4l2_subdev_s_stream_helper, ++}; ++ ++static const struct v4l2_subdev_pad_ops imx471_pad_ops = { ++ .enum_mbus_code = imx471_enum_mbus_code, ++ .get_fmt = v4l2_subdev_get_fmt, ++ .set_fmt = imx471_set_pad_format, ++ .get_selection = imx471_get_selection, ++ .enum_frame_size = imx471_enum_frame_size, ++ .enable_streams = imx471_enable_stream, ++ .disable_streams = imx471_disable_stream, ++}; ++ ++static const struct v4l2_subdev_ops imx471_subdev_ops = { ++ .video = &imx471_video_ops, ++ .pad = &imx471_pad_ops, ++}; ++ ++static const struct v4l2_subdev_internal_ops imx471_internal_ops = { ++ .init_state = imx471_init_state, ++}; ++ ++static int imx471_init_controls(struct imx471 *sensor) ++{ ++ const struct imx471_mode *mode = &imx471_modes[0]; ++ struct v4l2_fwnode_device_properties props; ++ struct v4l2_ctrl_handler *ctrl_hdlr; ++ struct v4l2_ctrl *link_freq; ++ s64 exposure_max, hblank; ++ u64 pixel_rate; ++ int ret; ++ ++ ret = v4l2_fwnode_device_parse(sensor->dev, &props); ++ if (ret) { ++ dev_err(sensor->dev, "failed to parse fwnode: %d\n", ret); ++ return ret; ++ } ++ ++ ctrl_hdlr = &sensor->ctrl_handler; ++ v4l2_ctrl_handler_init(ctrl_hdlr, 12); ++ ++ v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx471_ctrl_ops, &props); ++ ++ link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, ++ &imx471_ctrl_ops, ++ V4L2_CID_LINK_FREQ, ++ ARRAY_SIZE(link_freq_menu_items) - 1, ++ 0, ++ link_freq_menu_items); ++ ++ /* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ ++ pixel_rate = div_u64(IMX471_LINK_FREQ_DEFAULT * 2 * 4, 10); ++ ++ v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, ++ V4L2_CID_PIXEL_RATE, pixel_rate, ++ pixel_rate, 1, pixel_rate); ++ ++ sensor->vblank = v4l2_ctrl_new_std(ctrl_hdlr, ++ &imx471_ctrl_ops, ++ V4L2_CID_VBLANK, ++ mode->fll_min - mode->height, ++ IMX471_FLL_MAX - mode->height, ++ 1, ++ mode->fll_def - mode->height); ++ ++ hblank = mode->llp - mode->width; ++ sensor->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, ++ V4L2_CID_HBLANK, hblank, hblank, ++ 1, hblank); ++ ++ /* fll >= exposure time + adjust parameter (default value is 18) */ ++ exposure_max = mode->fll_def - IMX471_EXPOSURE_MARGIN; ++ sensor->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, ++ V4L2_CID_EXPOSURE, ++ IMX471_EXPOSURE_MIN, exposure_max, ++ IMX471_EXPOSURE_STEP, ++ IMX471_EXPOSURE_DEFAULT); ++ ++ v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, ++ IMX471_ANA_GAIN_MIN, IMX471_ANA_GAIN_MAX, ++ IMX471_ANA_GAIN_STEP, IMX471_ANA_GAIN_DEFAULT); ++ ++ v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, V4L2_CID_DIGITAL_GAIN, ++ IMX471_DGTL_GAIN_MIN, IMX471_DGTL_GAIN_MAX, ++ IMX471_DGTL_GAIN_STEP, IMX471_DGTL_GAIN_DEFAULT); ++ ++ v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx471_ctrl_ops, ++ V4L2_CID_TEST_PATTERN, ++ ARRAY_SIZE(imx471_test_pattern_menu) - 1, ++ 0, 0, imx471_test_pattern_menu); ++ ++ sensor->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 0); ++ ++ sensor->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, ++ V4L2_CID_VFLIP, 0, 1, 1, 0); ++ ++ if (ctrl_hdlr->error) { ++ dev_err(sensor->dev, "%s control init failed: %d\n", ++ __func__, ctrl_hdlr->error); ++ goto error; ++ } ++ ++ link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; ++ sensor->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; ++ sensor->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; ++ sensor->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; ++ ++ sensor->sd.ctrl_handler = ctrl_hdlr; ++ ++ return 0; ++ ++error: ++ v4l2_ctrl_handler_free(ctrl_hdlr); ++ ++ return ctrl_hdlr->error; ++} ++ ++static int imx471_check_hwcfg(struct imx471 *sensor) ++{ ++ struct v4l2_fwnode_endpoint bus_cfg = { ++ .bus_type = V4L2_MBUS_CSI2_DPHY, ++ }; ++ struct fwnode_handle *ep, *fwnode = dev_fwnode(sensor->dev); ++ unsigned long link_freq_bitmap; ++ struct clk *clk; ++ int ret; ++ ++ clk = devm_v4l2_sensor_clk_get(sensor->dev, NULL); ++ if (IS_ERR(clk)) ++ return dev_err_probe(sensor->dev, PTR_ERR(clk), ++ "can't get clock frequency\n"); ++ ++ if (clk_get_rate(clk) != IMX471_EXT_CLK) ++ return dev_err_probe(sensor->dev, -EINVAL, ++ "external clock %lu is not supported\n", ++ clk_get_rate(clk)); ++ ++ ep = fwnode_graph_get_endpoint_by_id(fwnode, 0, 0, 0); ++ ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); ++ fwnode_handle_put(ep); ++ if (ret) ++ return dev_err_probe(sensor->dev, ret, ++ "parsing endpoint failed\n"); ++ ++ if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) { ++ ret = dev_err_probe(sensor->dev, -EINVAL, ++ "number of CSI2 data lanes %u is not supported\n", ++ bus_cfg.bus.mipi_csi2.num_data_lanes); ++ goto done_endpoint_free; ++ } ++ ++ ret = v4l2_link_freq_to_bitmap(sensor->dev, bus_cfg.link_frequencies, ++ bus_cfg.nr_of_link_frequencies, ++ link_freq_menu_items, ++ ARRAY_SIZE(link_freq_menu_items), ++ &link_freq_bitmap); ++ ++done_endpoint_free: ++ v4l2_fwnode_endpoint_free(&bus_cfg); ++ ++ return ret; ++} ++ ++static int imx471_probe(struct i2c_client *client) ++{ ++ struct imx471 *sensor; ++ int ret; ++ ++ sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL); ++ if (!sensor) ++ return dev_err_probe(&client->dev, -ENOMEM, ++ "failed to allocate memory\n"); ++ ++ sensor->dev = &client->dev; ++ ++ ret = imx471_check_hwcfg(sensor); ++ if (ret) ++ return dev_err_probe(sensor->dev, ret, ++ "failed to check hwcfg: %d\n", ret); ++ ++ ret = imx471_get_regulators(sensor->dev, sensor); ++ if (ret) ++ return dev_err_probe(sensor->dev, ret, ++ "failed to get regulators\n"); ++ ++ sensor->reset_gpio = devm_gpiod_get_optional(sensor->dev, "reset", ++ GPIOD_OUT_HIGH); ++ if (IS_ERR(sensor->reset_gpio)) ++ return dev_err_probe(sensor->dev, PTR_ERR(sensor->reset_gpio), ++ "failed to get reset gpio\n"); ++ ++ sensor->img_clk = devm_v4l2_sensor_clk_get(sensor->dev, NULL); ++ if (IS_ERR(sensor->img_clk)) ++ return dev_err_probe(sensor->dev, PTR_ERR(sensor->img_clk), ++ "failed to get imaging clock\n"); ++ ++ v4l2_i2c_subdev_init(&sensor->sd, client, &imx471_subdev_ops); ++ ++ sensor->regmap = devm_cci_regmap_init_i2c(client, 16); ++ if (IS_ERR(sensor->regmap)) ++ return dev_err_probe(sensor->dev, PTR_ERR(sensor->regmap), ++ "failed to initialize CCI\n"); ++ ++ ret = imx471_power_on(sensor->dev); ++ if (ret) ++ return dev_err_probe(sensor->dev, ret, ++ "failed to power on\n"); ++ ++ ret = imx471_identify_module(sensor); ++ if (ret) { ++ dev_err_probe(sensor->dev, ret, "failed to find sensor: %d\n", ++ ret); ++ goto error_power_off; ++ } ++ ++ ret = imx471_init_controls(sensor); ++ if (ret) { ++ dev_err_probe(sensor->dev, ret, "failed to init controls: %d\n", ++ ret); ++ goto error_power_off; ++ } ++ ++ sensor->sd.internal_ops = &imx471_internal_ops; ++ sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ sensor->pad.flags = MEDIA_PAD_FL_SOURCE; ++ sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; ++ ++ ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad); ++ if (ret) { ++ dev_err_probe(sensor->dev, ret, ++ "failed to init entity pads: %d\n", ret); ++ goto error_v4l2_ctrl_handler_free; ++ } ++ ++ sensor->sd.state_lock = sensor->ctrl_handler.lock; ++ ret = v4l2_subdev_init_finalize(&sensor->sd); ++ if (ret < 0) { ++ dev_err_probe(sensor->dev, ret, "failed to init subdev: %d\n", ++ ret); ++ goto error_media_entity_pm; ++ } ++ ++ pm_runtime_set_active(sensor->dev); ++ pm_runtime_enable(sensor->dev); ++ ++ ret = v4l2_async_register_subdev_sensor(&sensor->sd); ++ if (ret < 0) ++ goto error_v4l2_subdev_cleanup; ++ ++ pm_runtime_idle(sensor->dev); ++ ++ return 0; ++ ++error_v4l2_subdev_cleanup: ++ pm_runtime_disable(sensor->dev); ++ pm_runtime_set_suspended(sensor->dev); ++ v4l2_subdev_cleanup(&sensor->sd); ++ ++error_media_entity_pm: ++ media_entity_cleanup(&sensor->sd.entity); ++ ++error_v4l2_ctrl_handler_free: ++ v4l2_ctrl_handler_free(sensor->sd.ctrl_handler); ++ ++error_power_off: ++ imx471_power_off(sensor->dev); ++ ++ return ret; ++} ++ ++static void imx471_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ ++ v4l2_async_unregister_subdev(sd); ++ v4l2_subdev_cleanup(sd); ++ media_entity_cleanup(&sd->entity); ++ v4l2_ctrl_handler_free(sd->ctrl_handler); ++ ++ pm_runtime_disable(&client->dev); ++ ++ if (!pm_runtime_status_suspended(&client->dev)) { ++ imx471_power_off(&client->dev); ++ pm_runtime_set_suspended(&client->dev); ++ } ++} ++ ++static DEFINE_RUNTIME_DEV_PM_OPS(imx471_pm_ops, imx471_power_off, ++ imx471_power_on, NULL); ++ ++static const struct acpi_device_id imx471_acpi_ids[] __maybe_unused = { ++ { "SONY471A" }, ++ { "TBE20A0" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(acpi, imx471_acpi_ids); ++ ++static struct i2c_driver imx471_i2c_driver = { ++ .driver = { ++ .name = "imx471", ++ .acpi_match_table = ACPI_PTR(imx471_acpi_ids), ++ .pm = pm_sleep_ptr(&imx471_pm_ops), ++ }, ++ .probe = imx471_probe, ++ .remove = imx471_remove, ++}; ++module_i2c_driver(imx471_i2c_driver); ++ ++MODULE_AUTHOR("Jimmy Su "); ++MODULE_AUTHOR("Serin Yeh "); ++MODULE_AUTHOR("Kate Hsuan "); ++MODULE_DESCRIPTION("Sony imx471 sensor driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c +--- a/drivers/media/i2c/ov2740.c ++++ b/drivers/media/i2c/ov2740.c +@@ -1241,41 +1241,42 @@ static int ov2740_nvmem_read(void *priv, unsigned int off, void *val, + return ret; + } + ++static const struct regmap_config regmap_config = { ++ .val_bits = 8, ++ .reg_bits = 16, ++ .disable_locking = true, ++}; ++ + static int ov2740_register_nvmem(struct i2c_client *client, + struct ov2740 *ov2740) + { + struct nvm_data *nvm; +- struct regmap_config regmap_config = { }; +- struct nvmem_config nvmem_config = { }; + struct regmap *regmap; + struct device *dev = ov2740->dev; ++ struct nvmem_config nvmem_config = { ++ .name = dev_name(dev), ++ .dev = dev, ++ .read_only = true, ++ .root_only = true, ++ .owner = THIS_MODULE, ++ .compat = true, ++ .base_dev = dev, ++ .reg_read = ov2740_nvmem_read, ++ .stride = 1, ++ .word_size = 1, ++ .size = CUSTOMER_USE_OTP_SIZE, ++ }; + + nvm = devm_kzalloc(dev, sizeof(*nvm), GFP_KERNEL); + if (!nvm) + return -ENOMEM; + +- regmap_config.val_bits = 8; +- regmap_config.reg_bits = 16; +- regmap_config.disable_locking = true; + regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + nvm->regmap = regmap; +- +- nvmem_config.name = dev_name(dev); +- nvmem_config.dev = dev; +- nvmem_config.read_only = true; +- nvmem_config.root_only = true; +- nvmem_config.owner = THIS_MODULE; +- nvmem_config.compat = true; +- nvmem_config.base_dev = dev; +- nvmem_config.reg_read = ov2740_nvmem_read; +- nvmem_config.reg_write = NULL; + nvmem_config.priv = nvm; +- nvmem_config.stride = 1; +- nvmem_config.word_size = 1; +- nvmem_config.size = CUSTOMER_USE_OTP_SIZE; + + nvm->nvmem = devm_nvmem_register(dev, &nvmem_config); + if (IS_ERR(nvm->nvmem)) +diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c +--- a/drivers/media/pci/intel/ipu-bridge.c ++++ b/drivers/media/pci/intel/ipu-bridge.c +@@ -49,6 +49,8 @@ + * Please keep the list sorted by ACPI HID. + */ + static const struct ipu_sensor_config ipu_supported_sensors[] = { ++ /* Himax HM1092 */ ++ IPU_SENSOR_CONFIG("HIMX1092", 2, 180000000, 180480000), + /* Himax HM11B1 */ + IPU_SENSOR_CONFIG("HIMX11B1", 1, 384000000), + /* Himax HM2170 */ +@@ -97,6 +99,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { + IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000), + /* Sony IMX471 */ + IPU_SENSOR_CONFIG("SONY471A", 1, 200000000), ++ /* Sony IMX471 (found on Lenovo X1 Carbon G14) */ ++ IPU_SENSOR_CONFIG("TBE20A0", 1, 200000000), + /* Toshiba T4KA3 */ + IPU_SENSOR_CONFIG("XMCC0003", 1, 321468000), + }; +@@ -194,8 +198,8 @@ static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev + for (i = 0; i < ARRAY_SIZE(ivsc_acpi_ids); i++) { + const struct acpi_device_id *acpi_id = &ivsc_acpi_ids[i]; + struct acpi_device *consumer, *ivsc_adev; +- + acpi_handle handle = acpi_device_handle(ACPI_PTR(adev)); ++ + for_each_acpi_dev_match(ivsc_adev, acpi_id->id, NULL, -1) + /* camera sensor depends on IVSC in DSDT if exist */ + for_each_acpi_consumer_dev(ivsc_adev, consumer) diff --git a/pkgbuilds/linux-omarchy/0543-media-laptop-cameras.patch.sig b/pkgbuilds/linux-omarchy/0543-media-laptop-cameras.patch.sig new file mode 100644 index 0000000..2f69b33 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0543-media-laptop-cameras.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0544-ipu-bridge-lenovo-x9-upside-down.patch b/pkgbuilds/linux-omarchy/0544-ipu-bridge-lenovo-x9-upside-down.patch new file mode 100644 index 0000000..abda19b --- /dev/null +++ b/pkgbuilds/linux-omarchy/0544-ipu-bridge-lenovo-x9-upside-down.patch @@ -0,0 +1,49 @@ +diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c +--- a/drivers/media/pci/intel/ipu-bridge.c ++++ b/drivers/media/pci/intel/ipu-bridge.c +@@ -138,6 +138,45 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = { + }, + .driver_data = "OVTI02C1", + }, ++ /* ++ * The first four characters of DMI_BOARD_NAME identify the Lenovo ++ * machine type/model. For example, a DMI_BOARD_NAME starting with ++ * "21Q6" indicates a ThinkPad X9-15. ++ * ++ * Reference: https://psref.lenovo.com/ ++ */ ++ { ++ /* Lenovo X9-14 */ ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_BOARD_NAME, "21QA"), ++ }, ++ .driver_data = "SONY471A", ++ }, ++ { ++ /* Lenovo X9-14 */ ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_BOARD_NAME, "21QB"), ++ }, ++ .driver_data = "SONY471A", ++ }, ++ { ++ /* Lenovo X9-15 */ ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_BOARD_NAME, "21Q6"), ++ }, ++ .driver_data = "SONY471A", ++ }, ++ { ++ /* Lenovo X9-15 */ ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_BOARD_NAME, "21Q7"), ++ }, ++ .driver_data = "SONY471A", ++ }, + {} /* Terminating entry */ + }; + diff --git a/pkgbuilds/linux-omarchy/0544-ipu-bridge-lenovo-x9-upside-down.patch.sig b/pkgbuilds/linux-omarchy/0544-ipu-bridge-lenovo-x9-upside-down.patch.sig new file mode 100644 index 0000000..3b2eaec Binary files /dev/null and b/pkgbuilds/linux-omarchy/0544-ipu-bridge-lenovo-x9-upside-down.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0545-ipu-bridge-dell-16-premium-da16250-upside-down.patch b/pkgbuilds/linux-omarchy/0545-ipu-bridge-dell-16-premium-da16250-upside-down.patch new file mode 100644 index 0000000..7c15eb7 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0545-ipu-bridge-dell-16-premium-da16250-upside-down.patch @@ -0,0 +1,17 @@ +diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c +--- a/drivers/media/pci/intel/ipu-bridge.c ++++ b/drivers/media/pci/intel/ipu-bridge.c +@@ -138,6 +138,13 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = { + }, + .driver_data = "OVTI02C1", + }, ++ { ++ .matches = { ++ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), ++ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 16 (Dell 16 Premium) DA16250"), ++ }, ++ .driver_data = "OVTI02C1", ++ }, + /* + * The first four characters of DMI_BOARD_NAME identify the Lenovo + * machine type/model. For example, a DMI_BOARD_NAME starting with diff --git a/pkgbuilds/linux-omarchy/0545-ipu-bridge-dell-16-premium-da16250-upside-down.patch.sig b/pkgbuilds/linux-omarchy/0545-ipu-bridge-dell-16-premium-da16250-upside-down.patch.sig new file mode 100644 index 0000000..1c5aff2 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0545-ipu-bridge-dell-16-premium-da16250-upside-down.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0546-ipu-bridge-samsung-galaxy-book5-pro-360-upside-down.patch b/pkgbuilds/linux-omarchy/0546-ipu-bridge-samsung-galaxy-book5-pro-360-upside-down.patch new file mode 100644 index 0000000..a42d186 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0546-ipu-bridge-samsung-galaxy-book5-pro-360-upside-down.patch @@ -0,0 +1,18 @@ +diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c +--- a/drivers/media/pci/intel/ipu-bridge.c ++++ b/drivers/media/pci/intel/ipu-bridge.c +@@ -184,6 +184,14 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = { + }, + .driver_data = "SONY471A", + }, ++ { ++ /* Samsung Galaxy Book5 Pro 360 */ ++ .matches = { ++ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), ++ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "960QHA"), ++ }, ++ .driver_data = "OVTI02E1", ++ }, + {} /* Terminating entry */ + }; + diff --git a/pkgbuilds/linux-omarchy/0546-ipu-bridge-samsung-galaxy-book5-pro-360-upside-down.patch.sig b/pkgbuilds/linux-omarchy/0546-ipu-bridge-samsung-galaxy-book5-pro-360-upside-down.patch.sig new file mode 100644 index 0000000..5376099 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0546-ipu-bridge-samsung-galaxy-book5-pro-360-upside-down.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0547-int3472-imx471-vana-power-enable.patch b/pkgbuilds/linux-omarchy/0547-int3472-imx471-vana-power-enable.patch new file mode 100644 index 0000000..db38adc --- /dev/null +++ b/pkgbuilds/linux-omarchy/0547-int3472-imx471-vana-power-enable.patch @@ -0,0 +1,28 @@ +diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c +--- a/drivers/platform/x86/intel/int3472/discrete.c ++++ b/drivers/platform/x86/intel/int3472/discrete.c +@@ -164,6 +164,24 @@ static const struct int3472_gpio_map int3472_gpio_map[] = { + .con_id = "dvdd", + .enable_time_us = 45 * USEC_PER_MSEC, + }, ++ { /* imx471 expects "vana" as con_id for power enable */ ++ .hid = "SONY471A", ++ .type_from = INT3472_GPIO_TYPE_POWER_ENABLE, ++ .type_to = INT3472_GPIO_TYPE_POWER_ENABLE, ++ .con_id = "vana", ++ .enable_time_us = GPIO_REGULATOR_ENABLE_TIME, ++ }, ++ { ++ /* ++ * imx471 (on Lenovo ThinkPads X1 G14) expects "vana" as con_id ++ * for power enable ++ */ ++ .hid = "TBE20A0", ++ .type_from = INT3472_GPIO_TYPE_POWER_ENABLE, ++ .type_to = INT3472_GPIO_TYPE_POWER_ENABLE, ++ .con_id = "vana", ++ .enable_time_us = GPIO_REGULATOR_ENABLE_TIME, ++ }, + }; + + static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3472, u8 *type, diff --git a/pkgbuilds/linux-omarchy/0547-int3472-imx471-vana-power-enable.patch.sig b/pkgbuilds/linux-omarchy/0547-int3472-imx471-vana-power-enable.patch.sig new file mode 100644 index 0000000..b2651bd Binary files /dev/null and b/pkgbuilds/linux-omarchy/0547-int3472-imx471-vana-power-enable.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0548-media-cvs-sensor-node-in-bound-callback.patch b/pkgbuilds/linux-omarchy/0548-media-cvs-sensor-node-in-bound-callback.patch new file mode 100644 index 0000000..b041d90 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0548-media-cvs-sensor-node-in-bound-callback.patch @@ -0,0 +1,37 @@ +diff --git a/drivers/media/i2c/cvs/v4l2.c b/drivers/media/i2c/cvs/v4l2.c +--- a/drivers/media/i2c/cvs/v4l2.c ++++ b/drivers/media/i2c/cvs/v4l2.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -400,7 +401,7 @@ static int cvs_csi_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_async_connection *asc) + { + struct icvs *ctx = notifier_to_csi(notifier); +- int pad; ++ int pad, ret; + + pad = media_entity_get_fwnode_pad(&sd->entity, asc->match.fwnode, + MEDIA_PAD_FL_SOURCE); +@@ -409,9 +410,13 @@ static int cvs_csi_notify_bound(struct v4l2_async_notifier *notifier, + + ctx->remote = &sd->entity.pads[pad]; + +- return media_create_pad_link(&sd->entity, pad, &ctx->subdev.entity, +- ICVS_CSI_PAD_SINK, MEDIA_LNK_FL_ENABLED | +- MEDIA_LNK_FL_IMMUTABLE); ++ ret = media_create_pad_link(&sd->entity, pad, &ctx->subdev.entity, ++ ICVS_CSI_PAD_SINK, MEDIA_LNK_FL_ENABLED | ++ MEDIA_LNK_FL_IMMUTABLE); ++ if (ret) ++ return ret; ++ ++ return v4l2_device_register_subdev_nodes(sd->v4l2_dev); + } + + /** diff --git a/pkgbuilds/linux-omarchy/0548-media-cvs-sensor-node-in-bound-callback.patch.sig b/pkgbuilds/linux-omarchy/0548-media-cvs-sensor-node-in-bound-callback.patch.sig new file mode 100644 index 0000000..b58a4dd Binary files /dev/null and b/pkgbuilds/linux-omarchy/0548-media-cvs-sensor-node-in-bound-callback.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0549-media-imx471-analog-gain-max-960.patch b/pkgbuilds/linux-omarchy/0549-media-imx471-analog-gain-max-960.patch new file mode 100644 index 0000000..a11c4f2 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0549-media-imx471-analog-gain-max-960.patch @@ -0,0 +1,12 @@ +diff --git a/drivers/media/i2c/imx471.c b/drivers/media/i2c/imx471.c +--- a/drivers/media/i2c/imx471.c ++++ b/drivers/media/i2c/imx471.c +@@ -41,7 +41,7 @@ + /* Analog gain control */ + #define IMX471_REG_ANALOG_GAIN CCI_REG16(0x0204) + #define IMX471_ANA_GAIN_MIN 0 +-#define IMX471_ANA_GAIN_MAX 800 ++#define IMX471_ANA_GAIN_MAX 960 + #define IMX471_ANA_GAIN_STEP 1 + #define IMX471_ANA_GAIN_DEFAULT 0 + diff --git a/pkgbuilds/linux-omarchy/0549-media-imx471-analog-gain-max-960.patch.sig b/pkgbuilds/linux-omarchy/0549-media-imx471-analog-gain-max-960.patch.sig new file mode 100644 index 0000000..283ab9e Binary files /dev/null and b/pkgbuilds/linux-omarchy/0549-media-imx471-analog-gain-max-960.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0560-input-updates.patch b/pkgbuilds/linux-omarchy/0560-input-updates.patch new file mode 100644 index 0000000..d8548c7 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0560-input-updates.patch @@ -0,0 +1,3869 @@ +diff --git a/drivers/input/input.c b/drivers/input/input.c +--- a/drivers/input/input.c ++++ b/drivers/input/input.c +@@ -318,7 +318,7 @@ static int input_get_disposition(struct input_dev *dev, + static void input_event_dispose(struct input_dev *dev, int disposition, + unsigned int type, unsigned int code, int value) + { +- if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event) ++ if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event && dev->ready) + dev->event(dev, type, code, value); + + if (disposition & INPUT_PASS_TO_HANDLERS) { +@@ -568,6 +568,75 @@ void input_release_device(struct input_handle *handle) + } + EXPORT_SYMBOL(input_release_device); + ++#define INPUT_DO_TOGGLE(dev, type, bits, on) \ ++ do { \ ++ int i; \ ++ bool active; \ ++ \ ++ if (!test_bit(EV_##type, dev->evbit)) \ ++ break; \ ++ \ ++ for_each_set_bit(i, dev->bits##bit, type##_CNT) { \ ++ active = test_bit(i, dev->bits); \ ++ if (!active && !on) \ ++ continue; \ ++ \ ++ dev->event(dev, EV_##type, i, on ? active : 0); \ ++ } \ ++ } while (0) ++ ++/* ++ * Iterate through the logical state of the input device (LEDs, sounds, ++ * auto-repeat) and explicitly push that state down to the hardware ++ * via dev->event() to match the current logical state (if activate is true), ++ * or forcibly turn off all feedback like LEDs and sounds during teardown ++ * or suspend (if activate is false). ++ * ++ * Primarily used as a state-replay mechanism after a device is opened ++ * or uninhibited, as events might have been dropped by the core while the ++ * hardware was not marked as ready. ++ */ ++static void input_dev_toggle(struct input_dev *dev, bool activate) ++{ ++ if (!dev->event || !dev->ready) ++ return; ++ ++ INPUT_DO_TOGGLE(dev, LED, led, activate); ++ INPUT_DO_TOGGLE(dev, SND, snd, activate); ++ ++ if (activate && test_bit(EV_REP, dev->evbit)) { ++ dev->event(dev, EV_REP, REP_PERIOD, dev->rep[REP_PERIOD]); ++ dev->event(dev, EV_REP, REP_DELAY, dev->rep[REP_DELAY]); ++ } ++} ++ ++static int input_start_device(struct input_dev *dev) ++{ ++ int error; ++ ++ lockdep_assert_held(&dev->mutex); ++ ++ if (dev->users++ == 0 && !dev->inhibited) { ++ if (dev->open) { ++ error = dev->open(dev); ++ if (error) { ++ dev->users--; ++ return error; ++ } ++ } ++ ++ scoped_guard(spinlock_irq, &dev->event_lock) { ++ dev->ready = true; ++ input_dev_toggle(dev, true); ++ } ++ ++ if (dev->poller) ++ input_dev_poller_start(dev->poller); ++ } ++ ++ return 0; ++} ++ + /** + * input_open_device - open input device + * @handle: handle through which device is being accessed +@@ -586,21 +655,9 @@ int input_open_device(struct input_handle *handle) + + handle->open++; + +- if (handle->handler->passive_observer) +- return 0; +- +- if (dev->users++ || dev->inhibited) { +- /* +- * Device is already opened and/or inhibited, +- * so we can exit immediately and report success. +- */ +- return 0; +- } +- +- if (dev->open) { +- error = dev->open(dev); ++ if (!handle->handler->passive_observer) { ++ error = input_start_device(dev); + if (error) { +- dev->users--; + handle->open--; + /* + * Make sure we are not delivering any more +@@ -611,8 +668,8 @@ int input_open_device(struct input_handle *handle) + } + } + +- if (dev->poller) +- input_dev_poller_start(dev->poller); ++ if (handle->open == 1 && handle->handler->start) ++ handle->handler->start(handle); + } + + return 0; +@@ -651,6 +708,12 @@ void input_close_device(struct input_handle *handle) + if (!--dev->users && !dev->inhibited) { + if (dev->poller) + input_dev_poller_stop(dev->poller); ++ ++ scoped_guard(spinlock_irq, &dev->event_lock) { ++ input_dev_toggle(dev, false); ++ dev->ready = false; ++ } ++ + if (dev->close) + dev->close(dev); + } +@@ -1702,37 +1765,6 @@ static int input_dev_uevent(const struct device *device, struct kobj_uevent_env + return 0; + } + +-#define INPUT_DO_TOGGLE(dev, type, bits, on) \ +- do { \ +- int i; \ +- bool active; \ +- \ +- if (!test_bit(EV_##type, dev->evbit)) \ +- break; \ +- \ +- for_each_set_bit(i, dev->bits##bit, type##_CNT) { \ +- active = test_bit(i, dev->bits); \ +- if (!active && !on) \ +- continue; \ +- \ +- dev->event(dev, EV_##type, i, on ? active : 0); \ +- } \ +- } while (0) +- +-static void input_dev_toggle(struct input_dev *dev, bool activate) +-{ +- if (!dev->event) +- return; +- +- INPUT_DO_TOGGLE(dev, LED, led, activate); +- INPUT_DO_TOGGLE(dev, SND, snd, activate); +- +- if (activate && test_bit(EV_REP, dev->evbit)) { +- dev->event(dev, EV_REP, REP_PERIOD, dev->rep[REP_PERIOD]); +- dev->event(dev, EV_REP, REP_DELAY, dev->rep[REP_DELAY]); +- } +-} +- + /** + * input_reset_device() - reset/restore the state of input device + * @dev: input device whose state needs to be reset +@@ -1756,52 +1788,73 @@ static int input_inhibit_device(struct input_dev *dev) + { + guard(mutex)(&dev->mutex); + ++ if (dev->going_away) ++ return -ENODEV; ++ + if (dev->inhibited) + return 0; + + if (dev->users) { +- if (dev->close) +- dev->close(dev); + if (dev->poller) + input_dev_poller_stop(dev->poller); ++ ++ scoped_guard(spinlock_irq, &dev->event_lock) { ++ input_dev_toggle(dev, false); ++ dev->ready = false; ++ } ++ ++ if (dev->close) ++ dev->close(dev); + } + + scoped_guard(spinlock_irq, &dev->event_lock) { + input_mt_release_slots(dev); + input_dev_release_keys(dev); + input_handle_event(dev, EV_SYN, SYN_REPORT, 1); +- input_dev_toggle(dev, false); ++ dev->inhibited = true; + } + +- dev->inhibited = true; +- + return 0; + } + + static int input_uninhibit_device(struct input_dev *dev) + { ++ struct input_handle *handle; + int error; + + guard(mutex)(&dev->mutex); + ++ if (dev->going_away) ++ return -ENODEV; ++ + if (!dev->inhibited) + return 0; + ++ dev->inhibited = false; ++ + if (dev->users) { + if (dev->open) { + error = dev->open(dev); +- if (error) ++ if (error) { ++ dev->inhibited = true; + return error; ++ } + } +- if (dev->poller) +- input_dev_poller_start(dev->poller); ++ scoped_guard(spinlock_irq, &dev->event_lock) ++ dev->ready = true; + } + +- dev->inhibited = false; +- + scoped_guard(spinlock_irq, &dev->event_lock) + input_dev_toggle(dev, true); + ++ if (dev->users && dev->poller) ++ input_dev_poller_start(dev->poller); ++ ++ list_for_each_entry(handle, &dev->h_list, d_node) { ++ if (handle->open && handle->handler->start) ++ handle->handler->start(handle); ++ } ++ + return 0; + } + +@@ -2655,9 +2708,6 @@ int input_register_handle(struct input_handle *handle) + */ + list_add_tail_rcu(&handle->h_node, &handler->h_list); + +- if (handler->start) +- handler->start(handle); +- + return 0; + } + EXPORT_SYMBOL(input_register_handle); +diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig +--- a/drivers/input/keyboard/Kconfig ++++ b/drivers/input/keyboard/Kconfig +@@ -119,44 +119,6 @@ config KEYBOARD_ATKBD + To compile this driver as a module, choose M here: the + module will be called atkbd. + +-config KEYBOARD_ATKBD_HP_KEYCODES +- bool "Use HP keyboard scancodes" +- depends on PARISC && KEYBOARD_ATKBD +- default y +- help +- Say Y here if you have a PA-RISC machine and want to use an AT or +- PS/2 keyboard, and your keyboard uses keycodes that are specific to +- PA-RISC keyboards. +- +- Say N if you use a standard keyboard. +- +-config KEYBOARD_ATKBD_RDI_KEYCODES +- bool "Use PrecisionBook keyboard scancodes" +- depends on KEYBOARD_ATKBD_HP_KEYCODES +- default n +- help +- If you have an RDI PrecisionBook, say Y here if you want to use its +- built-in keyboard (as opposed to an external keyboard). +- +- The PrecisionBook has five keys that conflict with those used by most +- AT and PS/2 keyboards. These are as follows: +- +- PrecisionBook Standard AT or PS/2 +- +- F1 F12 +- Left Ctrl Left Alt +- Caps Lock Left Ctrl +- Right Ctrl Caps Lock +- Left 102nd key (the key to the right of Left Shift) +- +- If you say N here, and use the PrecisionBook keyboard, then each key +- in the left-hand column will be interpreted as the corresponding key +- in the right-hand column. +- +- If you say Y here, and use an external keyboard, then each key in the +- right-hand column will be interpreted as the key shown in the +- left-hand column. +- + config KEYBOARD_QT1050 + tristate "Microchip AT42QT1050 Touch Sensor Chip" + depends on I2C +diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c +--- a/drivers/input/keyboard/atkbd.c ++++ b/drivers/input/keyboard/atkbd.c +@@ -73,13 +73,6 @@ MODULE_PARM_DESC(terminal, "Enable break codes on an IBM Terminal keyboard conne + #define ATKBD_KEYMAP_SIZE 512 + + static const unsigned short atkbd_set2_keycode[ATKBD_KEYMAP_SIZE] = { +-#ifdef CONFIG_KEYBOARD_ATKBD_HP_KEYCODES +- +-/* XXX: need a more general approach */ +- +-#include "hpps2atkbd.h" /* include the keyboard scancodes */ +- +-#else + 0, 67, 65, 63, 61, 59, 60, 88,183, 68, 66, 64, 62, 15, 41,117, + 184, 56, 42, 93, 29, 16, 2, 0,185, 0, 44, 31, 30, 17, 3, 0, + 186, 46, 45, 32, 18, 5, 4, 95,187, 57, 47, 33, 20, 19, 6,183, +@@ -99,7 +92,6 @@ static const unsigned short atkbd_set2_keycode[ATKBD_KEYMAP_SIZE] = { + 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119, 0, + + 0, 0, 0, 65, 99, +-#endif + }; + + static const unsigned short atkbd_set3_keycode[ATKBD_KEYMAP_SIZE] = { +diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c +--- a/drivers/input/keyboard/cap11xx.c ++++ b/drivers/input/keyboard/cap11xx.c +@@ -5,6 +5,8 @@ + * (c) 2014 Daniel Mack + */ + ++#include ++#include + #include + #include + #include +@@ -16,63 +18,43 @@ + #include + #include + ++#define CAP1114_REG_BUTTON_STATUS1 0x03 ++#define CAP1114_REG_BUTTON_STATUS2 0x04 ++#define CAP1114_REG_CONFIG2 0x40 ++#define CAP1114_REG_CONFIG2_VOL_UP_DOWN BIT(1) ++#define CAP1114_REG_LED_OUTPUT_CONTROL1 0x73 ++ + #define CAP11XX_REG_MAIN_CONTROL 0x00 + #define CAP11XX_REG_MAIN_CONTROL_GAIN_SHIFT (6) + #define CAP11XX_REG_MAIN_CONTROL_GAIN_MASK (0xc0) + #define CAP11XX_REG_MAIN_CONTROL_DLSEEP BIT(4) +-#define CAP11XX_REG_GENERAL_STATUS 0x02 + #define CAP11XX_REG_SENSOR_INPUT 0x03 +-#define CAP11XX_REG_NOISE_FLAG_STATUS 0x0a ++#define CAP1114_REG_BUTTON_STATUS2 0x04 + #define CAP11XX_REG_SENOR_DELTA(X) (0x10 + (X)) + #define CAP11XX_REG_SENSITIVITY_CONTROL 0x1f + #define CAP11XX_REG_SENSITIVITY_CONTROL_DELTA_SENSE_MASK 0x70 +-#define CAP11XX_REG_CONFIG 0x20 +-#define CAP11XX_REG_SENSOR_ENABLE 0x21 +-#define CAP11XX_REG_SENSOR_CONFIG 0x22 +-#define CAP11XX_REG_SENSOR_CONFIG2 0x23 +-#define CAP11XX_REG_SAMPLING_CONFIG 0x24 +-#define CAP11XX_REG_CALIBRATION 0x26 +-#define CAP11XX_REG_INT_ENABLE 0x27 + #define CAP11XX_REG_REPEAT_RATE 0x28 + #define CAP11XX_REG_SIGNAL_GUARD_ENABLE 0x29 +-#define CAP11XX_REG_MT_CONFIG 0x2a +-#define CAP11XX_REG_MT_PATTERN_CONFIG 0x2b +-#define CAP11XX_REG_MT_PATTERN 0x2d +-#define CAP11XX_REG_RECALIB_CONFIG 0x2f + #define CAP11XX_REG_SENSOR_THRESH(X) (0x30 + (X)) +-#define CAP11XX_REG_SENSOR_NOISE_THRESH 0x38 +-#define CAP11XX_REG_STANDBY_CHANNEL 0x40 +-#define CAP11XX_REG_STANDBY_CONFIG 0x41 +-#define CAP11XX_REG_STANDBY_SENSITIVITY 0x42 +-#define CAP11XX_REG_STANDBY_THRESH 0x43 + #define CAP11XX_REG_CONFIG2 0x44 + #define CAP11XX_REG_CONFIG2_ALT_POL BIT(6) +-#define CAP11XX_REG_SENSOR_BASE_CNT(X) (0x50 + (X)) +-#define CAP11XX_REG_LED_POLARITY 0x73 + #define CAP11XX_REG_LED_OUTPUT_CONTROL 0x74 + #define CAP11XX_REG_CALIB_SENSITIVITY_CONFIG 0x80 + #define CAP11XX_REG_CALIB_SENSITIVITY_CONFIG2 0x81 +- +-#define CAP11XX_REG_LED_DUTY_CYCLE_1 0x90 +-#define CAP11XX_REG_LED_DUTY_CYCLE_2 0x91 +-#define CAP11XX_REG_LED_DUTY_CYCLE_3 0x92 + #define CAP11XX_REG_LED_DUTY_CYCLE_4 0x93 + +-#define CAP11XX_REG_LED_DUTY_MIN_MASK (0x0f) +-#define CAP11XX_REG_LED_DUTY_MIN_MASK_SHIFT (0) + #define CAP11XX_REG_LED_DUTY_MAX_MASK (0xf0) +-#define CAP11XX_REG_LED_DUTY_MAX_MASK_SHIFT (4) + #define CAP11XX_REG_LED_DUTY_MAX_VALUE (15) + +-#define CAP11XX_REG_SENSOR_CALIB (0xb1 + (X)) +-#define CAP11XX_REG_SENSOR_CALIB_LSB1 0xb9 +-#define CAP11XX_REG_SENSOR_CALIB_LSB2 0xba + #define CAP11XX_REG_PRODUCT_ID 0xfd + #define CAP11XX_REG_MANUFACTURER_ID 0xfe + #define CAP11XX_REG_REVISION 0xff + + #define CAP11XX_MANUFACTURER_ID 0x5d + ++#define CAP11XX_T_RST_FILT_MIN_US 10000 ++#define CAP11XX_T_RST_ON_MIN_MS 400 ++ + #ifdef CONFIG_LEDS_CLASS + struct cap11xx_led { + struct cap11xx_priv *priv; +@@ -85,6 +67,7 @@ struct cap11xx_priv { + struct regmap *regmap; + struct device *dev; + struct input_dev *idev; ++ struct gpio_desc *reset_gpio; + const struct cap11xx_hw_model *model; + + struct cap11xx_led *leds; +@@ -101,47 +84,32 @@ struct cap11xx_priv { + + struct cap11xx_hw_model { + u8 product_id; ++ u8 led_output_control_reg_base; ++ u8 sensor_input_reg_base; + unsigned int num_channels; + unsigned int num_leds; ++ unsigned int num_sensor_thresholds; + bool has_gain; ++ bool has_grouped_sensors; + bool has_irq_config; ++ bool has_repeat_en; + bool has_sensitivity_control; + bool has_signal_guard; + }; + + static const struct reg_default cap11xx_reg_defaults[] = { + { CAP11XX_REG_MAIN_CONTROL, 0x00 }, +- { CAP11XX_REG_GENERAL_STATUS, 0x00 }, +- { CAP11XX_REG_SENSOR_INPUT, 0x00 }, +- { CAP11XX_REG_NOISE_FLAG_STATUS, 0x00 }, + { CAP11XX_REG_SENSITIVITY_CONTROL, 0x2f }, +- { CAP11XX_REG_CONFIG, 0x20 }, +- { CAP11XX_REG_SENSOR_ENABLE, 0x3f }, +- { CAP11XX_REG_SENSOR_CONFIG, 0xa4 }, +- { CAP11XX_REG_SENSOR_CONFIG2, 0x07 }, +- { CAP11XX_REG_SAMPLING_CONFIG, 0x39 }, +- { CAP11XX_REG_CALIBRATION, 0x00 }, +- { CAP11XX_REG_INT_ENABLE, 0x3f }, + { CAP11XX_REG_REPEAT_RATE, 0x3f }, +- { CAP11XX_REG_MT_CONFIG, 0x80 }, +- { CAP11XX_REG_MT_PATTERN_CONFIG, 0x00 }, +- { CAP11XX_REG_MT_PATTERN, 0x3f }, +- { CAP11XX_REG_RECALIB_CONFIG, 0x8a }, + { CAP11XX_REG_SENSOR_THRESH(0), 0x40 }, + { CAP11XX_REG_SENSOR_THRESH(1), 0x40 }, + { CAP11XX_REG_SENSOR_THRESH(2), 0x40 }, + { CAP11XX_REG_SENSOR_THRESH(3), 0x40 }, + { CAP11XX_REG_SENSOR_THRESH(4), 0x40 }, + { CAP11XX_REG_SENSOR_THRESH(5), 0x40 }, +- { CAP11XX_REG_SENSOR_NOISE_THRESH, 0x01 }, +- { CAP11XX_REG_STANDBY_CHANNEL, 0x00 }, +- { CAP11XX_REG_STANDBY_CONFIG, 0x39 }, +- { CAP11XX_REG_STANDBY_SENSITIVITY, 0x02 }, +- { CAP11XX_REG_STANDBY_THRESH, 0x40 }, ++ { CAP11XX_REG_SENSOR_THRESH(6), 0x40 }, ++ { CAP11XX_REG_SENSOR_THRESH(7), 0x40 }, + { CAP11XX_REG_CONFIG2, 0x40 }, +- { CAP11XX_REG_LED_POLARITY, 0x00 }, +- { CAP11XX_REG_SENSOR_CALIB_LSB1, 0x00 }, +- { CAP11XX_REG_SENSOR_CALIB_LSB2, 0x00 }, + }; + + static bool cap11xx_volatile_reg(struct device *dev, unsigned int reg) +@@ -149,6 +117,12 @@ static bool cap11xx_volatile_reg(struct device *dev, unsigned int reg) + switch (reg) { + case CAP11XX_REG_MAIN_CONTROL: + case CAP11XX_REG_SENSOR_INPUT: ++ /* ++ * CAP1114_REG_BUTTON_STATUS1 (CAP11XX_REG_SENSOR_INPUT) and ++ * CAP1114_REG_BUTTON_STATUS2 is volatile for the CAP1114, ++ * which supports more than 8 touch channels. ++ */ ++ case CAP1114_REG_BUTTON_STATUS2: + case CAP11XX_REG_SENOR_DELTA(0): + case CAP11XX_REG_SENOR_DELTA(1): + case CAP11XX_REG_SENOR_DELTA(2): +@@ -257,8 +231,8 @@ static int cap11xx_init_keys(struct cap11xx_priv *priv) + } + + if (!of_property_read_u32_array(node, "microchip,input-threshold", +- priv->thresholds, priv->model->num_channels)) { +- for (i = 0; i < priv->model->num_channels; i++) { ++ priv->thresholds, priv->model->num_sensor_thresholds)) { ++ for (i = 0; i < priv->model->num_sensor_thresholds; i++) { + if (priv->thresholds[i] > 127) { + dev_err(dev, "Invalid input-threshold value %u\n", + priv->thresholds[i]); +@@ -273,10 +247,13 @@ static int cap11xx_init_keys(struct cap11xx_priv *priv) + } + } + +- if (!of_property_read_u32_array(node, "microchip,calib-sensitivity", +- priv->calib_sensitivities, +- priv->model->num_channels)) { +- if (priv->model->has_sensitivity_control) { ++ if (of_property_present(node, "microchip,calib-sensitivity")) { ++ if (!priv->model->has_sensitivity_control) { ++ dev_warn(dev, ++ "This model doesn't support 'calib-sensitivity'\n"); ++ } else if (!of_property_read_u32_array(node, "microchip,calib-sensitivity", ++ priv->calib_sensitivities, ++ priv->model->num_channels)) { + for (i = 0; i < priv->model->num_channels; i++) { + if (!is_power_of_2(priv->calib_sensitivities[i]) || + priv->calib_sensitivities[i] > 4) { +@@ -296,32 +273,31 @@ static int cap11xx_init_keys(struct cap11xx_priv *priv) + if (error) + return error; + } +- } else { +- dev_warn(dev, +- "This model doesn't support 'calib-sensitivity'\n"); + } + } + +- for (i = 0; i < priv->model->num_channels; i++) { +- if (!of_property_read_u32_index(node, "microchip,signal-guard", +- i, &u32_val)) { +- if (u32_val > 1) +- return -EINVAL; +- if (u32_val) +- priv->signal_guard_inputs_mask |= 0x01 << i; +- } +- } +- +- if (priv->signal_guard_inputs_mask) { +- if (priv->model->has_signal_guard) { +- error = regmap_write(priv->regmap, +- CAP11XX_REG_SIGNAL_GUARD_ENABLE, +- priv->signal_guard_inputs_mask); +- if (error) +- return error; +- } else { ++ if (of_property_present(node, "microchip,signal-guard")) { ++ if (!priv->model->has_signal_guard) { + dev_warn(dev, + "This model doesn't support 'signal-guard'\n"); ++ } else { ++ for (i = 0; i < priv->model->num_channels; i++) { ++ if (!of_property_read_u32_index(node, "microchip,signal-guard", ++ i, &u32_val)) { ++ if (u32_val > 1) ++ return -EINVAL; ++ if (u32_val) ++ priv->signal_guard_inputs_mask |= 0x01 << i; ++ } ++ } ++ ++ if (priv->signal_guard_inputs_mask) { ++ error = regmap_write(priv->regmap, ++ CAP11XX_REG_SIGNAL_GUARD_ENABLE, ++ priv->signal_guard_inputs_mask); ++ if (error) ++ return error; ++ } + } + } + +@@ -332,10 +308,23 @@ static int cap11xx_init_keys(struct cap11xx_priv *priv) + of_property_read_u32_array(node, "linux,keycodes", + priv->keycodes, priv->model->num_channels); + +- /* Disable autorepeat. The Linux input system has its own handling. */ +- error = regmap_write(priv->regmap, CAP11XX_REG_REPEAT_RATE, 0); +- if (error) +- return error; ++ /* ++ * CAP1114 needs dedicated configuration to split ++ * grouped sensors into independent inputs. ++ */ ++ if (priv->model->has_grouped_sensors) { ++ error = regmap_set_bits(priv->regmap, CAP1114_REG_CONFIG2, ++ CAP1114_REG_CONFIG2_VOL_UP_DOWN); ++ if (error) ++ return error; ++ } ++ ++ if (priv->model->has_repeat_en) { ++ /* Disable autorepeat. The Linux input system has its own handling. */ ++ error = regmap_write(priv->regmap, CAP11XX_REG_REPEAT_RATE, 0); ++ if (error) ++ return error; ++ } + + return 0; + } +@@ -354,10 +343,25 @@ static irqreturn_t cap11xx_thread_func(int irq_num, void *data) + if (ret < 0) + goto out; + +- ret = regmap_read(priv->regmap, CAP11XX_REG_SENSOR_INPUT, &status); ++ ret = regmap_read(priv->regmap, priv->model->sensor_input_reg_base, &status); + if (ret < 0) + goto out; + ++ if (priv->model->num_channels > 8) { ++ unsigned int status2; ++ ++ ret = regmap_read(priv->regmap, priv->model->sensor_input_reg_base + 1, &status2); ++ if (ret < 0) ++ goto out; ++ ++ /* ++ * CAP1114 STATUS1 register only contains data for the first 6 channels. ++ * the remaining channels is stored in STATUS2. ++ */ ++ status &= GENMASK(5, 0); ++ status |= FIELD_PREP(GENMASK(13, 6), status2); ++ } ++ + for (i = 0; i < priv->idev->keycodemax; i++) + input_report_key(priv->idev, priv->keycodes[i], + status & (1 << i)); +@@ -407,10 +411,16 @@ static int cap11xx_led_set(struct led_classdev *cdev, + * limitation. Brightness levels per LED are either + * 0 (OFF) and 1 (ON). + */ +- return regmap_update_bits(priv->regmap, +- CAP11XX_REG_LED_OUTPUT_CONTROL, +- BIT(led->reg), +- value ? BIT(led->reg) : 0); ++ if (led->reg >= 8) ++ return regmap_update_bits(priv->regmap, ++ priv->model->led_output_control_reg_base + 1, ++ BIT(led->reg - 8), ++ value ? BIT(led->reg - 8) : 0); ++ else ++ return regmap_update_bits(priv->regmap, ++ priv->model->led_output_control_reg_base, ++ BIT(led->reg), ++ value ? BIT(led->reg) : 0); + } + + static int cap11xx_init_leds(struct device *dev, +@@ -420,6 +430,7 @@ static int cap11xx_init_leds(struct device *dev, + struct cap11xx_led *led; + int cnt = of_get_child_count(node); + int error; ++ u32 duty_val; + + if (!num_leds || !cnt) + return 0; +@@ -433,15 +444,26 @@ static int cap11xx_init_leds(struct device *dev, + + priv->leds = led; + ++ /* Set all LEDs to off */ + error = regmap_update_bits(priv->regmap, +- CAP11XX_REG_LED_OUTPUT_CONTROL, 0xff, 0); ++ priv->model->led_output_control_reg_base, ++ GENMASK(min(num_leds, 8) - 1, 0), 0); + if (error) + return error; + ++ if (num_leds > 8) { ++ error = regmap_update_bits(priv->regmap, ++ priv->model->led_output_control_reg_base + 1, ++ GENMASK(num_leds - 8 - 1, 0), 0); ++ if (error) ++ return error; ++ } ++ ++ duty_val = FIELD_PREP(CAP11XX_REG_LED_DUTY_MAX_MASK, ++ CAP11XX_REG_LED_DUTY_MAX_VALUE); ++ + error = regmap_update_bits(priv->regmap, CAP11XX_REG_LED_DUTY_CYCLE_4, +- CAP11XX_REG_LED_DUTY_MAX_MASK, +- CAP11XX_REG_LED_DUTY_MAX_VALUE << +- CAP11XX_REG_LED_DUTY_MAX_MASK_SHIFT); ++ CAP11XX_REG_LED_DUTY_MAX_MASK, duty_val); + if (error) + return error; + +@@ -510,9 +532,20 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client) + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + ++ priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); ++ if (IS_ERR(priv->reset_gpio)) ++ return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), ++ "Failed to get 'reset' GPIO\n"); ++ ++ if (priv->reset_gpio) { ++ usleep_range(CAP11XX_T_RST_FILT_MIN_US, CAP11XX_T_RST_FILT_MIN_US * 2); ++ gpiod_set_value_cansleep(priv->reset_gpio, 0); ++ msleep(CAP11XX_T_RST_ON_MIN_MS); ++ } ++ + error = regmap_read(priv->regmap, CAP11XX_REG_PRODUCT_ID, &val); + if (error) +- return error; ++ return dev_err_probe(dev, error, "Failed to read product ID\n"); + + if (val != cap->product_id) { + dev_err(dev, "Product ID: Got 0x%02x, expected 0x%02x\n", +@@ -522,7 +555,7 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client) + + error = regmap_read(priv->regmap, CAP11XX_REG_MANUFACTURER_ID, &val); + if (error) +- return error; ++ return dev_err_probe(dev, error, "Failed to read manufacturer ID\n"); + + if (val != CAP11XX_MANUFACTURER_ID) { + dev_err(dev, "Manufacturer ID: Got 0x%02x, expected 0x%02x\n", +@@ -531,11 +564,8 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client) + } + + error = regmap_read(priv->regmap, CAP11XX_REG_REVISION, &rev); +- if (error < 0) +- return error; +- +- dev_info(dev, "CAP11XX detected, model %s, revision 0x%02x\n", +- id->name, rev); ++ if (error) ++ return dev_err_probe(dev, error, "Failed to read revision\n"); + + priv->model = cap; + +@@ -599,47 +629,79 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client) + } + + static const struct cap11xx_hw_model cap1106_model = { +- .product_id = 0x55, .num_channels = 6, .num_leds = 0, ++ .product_id = 0x55, ++ .num_channels = 6, .num_leds = 0, .num_sensor_thresholds = 6, ++ .sensor_input_reg_base = CAP11XX_REG_SENSOR_INPUT, + .has_gain = true, + .has_irq_config = true, ++ .has_repeat_en = true, ++}; ++ ++static const struct cap11xx_hw_model cap1114_model = { ++ .product_id = 0x3a, ++ .num_channels = 14, .num_leds = 11, .num_sensor_thresholds = 8, ++ .led_output_control_reg_base = CAP1114_REG_LED_OUTPUT_CONTROL1, ++ .sensor_input_reg_base = CAP1114_REG_BUTTON_STATUS1, ++ .has_grouped_sensors = true, + }; + + static const struct cap11xx_hw_model cap1126_model = { +- .product_id = 0x53, .num_channels = 6, .num_leds = 2, ++ .product_id = 0x53, ++ .num_channels = 6, .num_leds = 2, .num_sensor_thresholds = 6, ++ .led_output_control_reg_base = CAP11XX_REG_LED_OUTPUT_CONTROL, ++ .sensor_input_reg_base = CAP11XX_REG_SENSOR_INPUT, + .has_gain = true, + .has_irq_config = true, ++ .has_repeat_en = true, + }; + + static const struct cap11xx_hw_model cap1188_model = { +- .product_id = 0x50, .num_channels = 8, .num_leds = 8, ++ .product_id = 0x50, ++ .num_channels = 8, .num_leds = 8, .num_sensor_thresholds = 8, ++ .led_output_control_reg_base = CAP11XX_REG_LED_OUTPUT_CONTROL, ++ .sensor_input_reg_base = CAP11XX_REG_SENSOR_INPUT, + .has_gain = true, + .has_irq_config = true, ++ .has_repeat_en = true, + }; + + static const struct cap11xx_hw_model cap1203_model = { +- .product_id = 0x6d, .num_channels = 3, .num_leds = 0, ++ .product_id = 0x6d, ++ .num_channels = 3, .num_leds = 0, .num_sensor_thresholds = 3, ++ .sensor_input_reg_base = CAP11XX_REG_SENSOR_INPUT, ++ .has_repeat_en = true, + }; + + static const struct cap11xx_hw_model cap1206_model = { +- .product_id = 0x67, .num_channels = 6, .num_leds = 0, ++ .product_id = 0x67, ++ .num_channels = 6, .num_leds = 0, .num_sensor_thresholds = 6, ++ .sensor_input_reg_base = CAP11XX_REG_SENSOR_INPUT, ++ .has_repeat_en = true, + }; + + static const struct cap11xx_hw_model cap1293_model = { +- .product_id = 0x6f, .num_channels = 3, .num_leds = 0, ++ .product_id = 0x6f, ++ .num_channels = 3, .num_leds = 0, .num_sensor_thresholds = 3, ++ .sensor_input_reg_base = CAP11XX_REG_SENSOR_INPUT, + .has_gain = true, ++ .has_repeat_en = true, + .has_sensitivity_control = true, + .has_signal_guard = true, + }; + + static const struct cap11xx_hw_model cap1298_model = { +- .product_id = 0x71, .num_channels = 8, .num_leds = 0, ++ .product_id = 0x71, ++ .num_channels = 8, .num_leds = 0, .num_sensor_thresholds = 8, ++ .sensor_input_reg_base = CAP11XX_REG_SENSOR_INPUT, + .has_gain = true, ++ .has_repeat_en = true, + .has_sensitivity_control = true, + .has_signal_guard = true, + }; + + static const struct of_device_id cap11xx_dt_ids[] = { + { .compatible = "microchip,cap1106", .data = &cap1106_model }, ++ { .compatible = "microchip,cap1114", .data = &cap1114_model }, + { .compatible = "microchip,cap1126", .data = &cap1126_model }, + { .compatible = "microchip,cap1188", .data = &cap1188_model }, + { .compatible = "microchip,cap1203", .data = &cap1203_model }, +@@ -652,6 +714,7 @@ MODULE_DEVICE_TABLE(of, cap11xx_dt_ids); + + static const struct i2c_device_id cap11xx_i2c_ids[] = { + { .name = "cap1106", .driver_data = (kernel_ulong_t)&cap1106_model }, ++ { .name = "cap1114", .driver_data = (kernel_ulong_t)&cap1114_model }, + { .name = "cap1126", .driver_data = (kernel_ulong_t)&cap1126_model }, + { .name = "cap1188", .driver_data = (kernel_ulong_t)&cap1188_model }, + { .name = "cap1203", .driver_data = (kernel_ulong_t)&cap1203_model }, +diff --git a/drivers/input/keyboard/charlieplex_keypad.c b/drivers/input/keyboard/charlieplex_keypad.c +--- a/drivers/input/keyboard/charlieplex_keypad.c ++++ b/drivers/input/keyboard/charlieplex_keypad.c +@@ -77,7 +77,9 @@ static int charlieplex_keypad_scan_line(struct charlieplex_keypad *keypad, + int err; + + /* Activate only one line as output at a time. */ +- gpiod_direction_output(line_gpios->desc[oline], 1); ++ err = gpiod_direction_output(line_gpios->desc[oline], 1); ++ if (err) ++ return err; + + if (keypad->settling_time_us) + fsleep(keypad->settling_time_us); +diff --git a/drivers/input/keyboard/hpps2atkbd.h b/drivers/input/keyboard/hpps2atkbd.h +deleted file mode 100644 +--- a/drivers/input/keyboard/hpps2atkbd.h ++++ /dev/null +@@ -1,110 +0,0 @@ +-/* +- * drivers/input/keyboard/hpps2atkbd.h +- * +- * Copyright (c) 2004 Helge Deller +- * Copyright (c) 2002 Laurent Canet +- * Copyright (c) 2002 Thibaut Varene +- * Copyright (c) 2000 Xavier Debacker +- * +- * HP PS/2 AT-compatible Keyboard, found in PA/RISC Workstations & Laptops +- * +- * This file is subject to the terms and conditions of the GNU General Public +- * License. See the file "COPYING" in the main directory of this archive +- * for more details. +- */ +- +- +-/* Is the keyboard an RDI PrecisionBook? */ +-#ifndef CONFIG_KEYBOARD_ATKBD_RDI_KEYCODES +-# define CONFLICT(x,y) x +-#else +-# define CONFLICT(x,y) y +-#endif +- +-/* sadly RDI (Tadpole) decided to ship a different keyboard layout +- than HP for their PS/2 laptop keyboard which leads to conflicting +- keycodes between a normal HP PS/2 keyboard and a RDI Precisionbook. +- HP: RDI: */ +-#define C_07 CONFLICT( KEY_F12, KEY_F1 ) +-#define C_11 CONFLICT( KEY_LEFTALT, KEY_LEFTCTRL ) +-#define C_14 CONFLICT( KEY_LEFTCTRL, KEY_CAPSLOCK ) +-#define C_58 CONFLICT( KEY_CAPSLOCK, KEY_RIGHTCTRL ) +-#define C_61 CONFLICT( KEY_102ND, KEY_LEFT ) +- +-/* Raw SET 2 scancode table */ +- +-/* 00 */ KEY_RESERVED, KEY_F9, KEY_RESERVED, KEY_F5, KEY_F3, KEY_F1, KEY_F2, C_07, +-/* 08 */ KEY_ESC, KEY_F10, KEY_F8, KEY_F6, KEY_F4, KEY_TAB, KEY_GRAVE, KEY_F2, +-/* 10 */ KEY_RESERVED, C_11, KEY_LEFTSHIFT, KEY_RESERVED, C_14, KEY_Q, KEY_1, KEY_F3, +-/* 18 */ KEY_RESERVED, KEY_LEFTALT, KEY_Z, KEY_S, KEY_A, KEY_W, KEY_2, KEY_F4, +-/* 20 */ KEY_RESERVED, KEY_C, KEY_X, KEY_D, KEY_E, KEY_4, KEY_3, KEY_F5, +-/* 28 */ KEY_RESERVED, KEY_SPACE, KEY_V, KEY_F, KEY_T, KEY_R, KEY_5, KEY_F6, +-/* 30 */ KEY_RESERVED, KEY_N, KEY_B, KEY_H, KEY_G, KEY_Y, KEY_6, KEY_F7, +-/* 38 */ KEY_RESERVED, KEY_RIGHTALT, KEY_M, KEY_J, KEY_U, KEY_7, KEY_8, KEY_F8, +-/* 40 */ KEY_RESERVED, KEY_COMMA, KEY_K, KEY_I, KEY_O, KEY_0, KEY_9, KEY_F9, +-/* 48 */ KEY_RESERVED, KEY_DOT, KEY_SLASH, KEY_L, KEY_SEMICOLON, KEY_P, KEY_MINUS, KEY_F10, +-/* 50 */ KEY_RESERVED, KEY_RESERVED, KEY_APOSTROPHE,KEY_RESERVED, KEY_LEFTBRACE, KEY_EQUAL, KEY_F11, KEY_SYSRQ, +-/* 58 */ C_58, KEY_RIGHTSHIFT,KEY_ENTER, KEY_RIGHTBRACE,KEY_BACKSLASH, KEY_BACKSLASH,KEY_F12, KEY_SCROLLLOCK, +-/* 60 */ KEY_DOWN, C_61, KEY_PAUSE, KEY_UP, KEY_DELETE, KEY_END, KEY_BACKSPACE, KEY_INSERT, +-/* 68 */ KEY_RESERVED, KEY_KP1, KEY_RIGHT, KEY_KP4, KEY_KP7, KEY_PAGEDOWN, KEY_HOME, KEY_PAGEUP, +-/* 70 */ KEY_KP0, KEY_KPDOT, KEY_KP2, KEY_KP5, KEY_KP6, KEY_KP8, KEY_ESC, KEY_NUMLOCK, +-/* 78 */ KEY_F11, KEY_KPPLUS, KEY_KP3, KEY_KPMINUS, KEY_KPASTERISK,KEY_KP9, KEY_SCROLLLOCK,KEY_102ND, +-/* 80 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 88 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 90 */ KEY_RESERVED, KEY_RIGHTALT, 255, KEY_RESERVED, KEY_RIGHTCTRL, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 98 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_CAPSLOCK, KEY_RESERVED, KEY_LEFTMETA, +-/* a0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RIGHTMETA, +-/* a8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_COMPOSE, +-/* b0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* b8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* c0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* c8 */ KEY_RESERVED, KEY_RESERVED, KEY_KPSLASH, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* d0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* d8 */ KEY_RESERVED, KEY_RESERVED, KEY_KPENTER, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* e0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* e8 */ KEY_RESERVED, KEY_END, KEY_RESERVED, KEY_LEFT, KEY_HOME, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* f0 */ KEY_INSERT, KEY_DELETE, KEY_DOWN, KEY_RESERVED, KEY_RIGHT, KEY_UP, KEY_RESERVED, KEY_PAUSE, +-/* f8 */ KEY_RESERVED, KEY_RESERVED, KEY_PAGEDOWN, KEY_RESERVED, KEY_SYSRQ, KEY_PAGEUP, KEY_RESERVED, KEY_RESERVED, +- +-/* These are offset for escaped keycodes: */ +- +-/* 00 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_F7, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 08 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 10 */ KEY_RESERVED, KEY_RIGHTALT, KEY_RESERVED, KEY_RESERVED, KEY_RIGHTCTRL, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 18 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 20 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 28 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 30 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 38 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 40 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 48 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 50 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 58 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 60 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 68 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 70 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 78 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 80 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 88 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 90 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* 98 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* a0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* a8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* b0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* b8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* c0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* c8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* d0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* d8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* e0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* e8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* f0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +-/* f8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED +- +-#undef CONFLICT +-#undef C_07 +-#undef C_11 +-#undef C_14 +-#undef C_58 +-#undef C_61 +- +diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c +--- a/drivers/input/keyboard/matrix_keypad.c ++++ b/drivers/input/keyboard/matrix_keypad.c +@@ -16,7 +16,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c +--- a/drivers/input/keyboard/pmic8xxx-keypad.c ++++ b/drivers/input/keyboard/pmic8xxx-keypad.c +@@ -462,15 +462,9 @@ static int pmic8xxx_kp_enable(struct pmic8xxx_kp *kp) + + static int pmic8xxx_kp_disable(struct pmic8xxx_kp *kp) + { +- int rc; +- + kp->ctrl_reg &= ~KEYP_CTRL_KEYP_EN; + +- rc = regmap_write(kp->regmap, KEYP_CTRL, kp->ctrl_reg); +- if (rc < 0) +- return rc; +- +- return rc; ++ return regmap_write(kp->regmap, KEYP_CTRL, kp->ctrl_reg); + } + + static int pmic8xxx_kp_open(struct input_dev *dev) +diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c +--- a/drivers/input/keyboard/samsung-keypad.c ++++ b/drivers/input/keyboard/samsung-keypad.c +@@ -142,7 +142,7 @@ static irqreturn_t samsung_keypad_irq(int irq, void *dev_id) + unsigned int row_state[SAMSUNG_MAX_COLS]; + bool key_down; + +- pm_runtime_get_sync(&keypad->pdev->dev); ++ guard(pm_runtime_active)(&keypad->pdev->dev); + + do { + readl(keypad->base + SAMSUNG_KEYIFSTSCLR); +@@ -158,8 +158,6 @@ static irqreturn_t samsung_keypad_irq(int irq, void *dev_id) + + } while (key_down && !keypad->stopped); + +- pm_runtime_put(&keypad->pdev->dev); +- + return IRQ_HANDLED; + } + +@@ -167,7 +165,7 @@ static void samsung_keypad_start(struct samsung_keypad *keypad) + { + unsigned int val; + +- pm_runtime_get_sync(&keypad->pdev->dev); ++ guard(pm_runtime_active)(&keypad->pdev->dev); + + /* Tell IRQ thread that it may poll the device. */ + keypad->stopped = false; +@@ -182,14 +180,14 @@ static void samsung_keypad_start(struct samsung_keypad *keypad) + /* KEYIFCOL reg clear. */ + writel(0, keypad->base + SAMSUNG_KEYIFCOL); + +- pm_runtime_put(&keypad->pdev->dev); ++ enable_irq(keypad->irq); + } + + static void samsung_keypad_stop(struct samsung_keypad *keypad) + { + unsigned int val; + +- pm_runtime_get_sync(&keypad->pdev->dev); ++ guard(pm_runtime_active)(&keypad->pdev->dev); + + /* Signal IRQ thread to stop polling and disable the handler. */ + keypad->stopped = true; +@@ -205,14 +203,6 @@ static void samsung_keypad_stop(struct samsung_keypad *keypad) + writel(val, keypad->base + SAMSUNG_KEYIFCON); + + clk_disable(keypad->clk); +- +- /* +- * Now that chip should not generate interrupts we can safely +- * re-enable the handler. +- */ +- enable_irq(keypad->irq); +- +- pm_runtime_put(&keypad->pdev->dev); + } + + static int samsung_keypad_open(struct input_dev *input_dev) +@@ -412,7 +402,8 @@ static int samsung_keypad_probe(struct platform_device *pdev) + } + + error = devm_request_threaded_irq(&pdev->dev, keypad->irq, NULL, +- samsung_keypad_irq, IRQF_ONESHOT, ++ samsung_keypad_irq, ++ IRQF_ONESHOT | IRQF_NO_AUTOEN, + dev_name(&pdev->dev), keypad); + if (error) { + dev_err(&pdev->dev, "failed to register keypad interrupt\n"); +@@ -492,15 +483,17 @@ static void samsung_keypad_toggle_wakeup(struct samsung_keypad *keypad, + + val = readl(keypad->base + SAMSUNG_KEYIFCON); + if (enable) { ++ enable_irq_wake(keypad->irq); + val |= SAMSUNG_KEYIFCON_WAKEUPEN; +- if (device_may_wakeup(&keypad->pdev->dev)) +- enable_irq_wake(keypad->irq); ++ writel(val, keypad->base + SAMSUNG_KEYIFCON); + } else { + val &= ~SAMSUNG_KEYIFCON_WAKEUPEN; +- if (device_may_wakeup(&keypad->pdev->dev)) +- disable_irq_wake(keypad->irq); ++ writel(val, keypad->base + SAMSUNG_KEYIFCON); ++ disable_irq_wake(keypad->irq); ++ ++ if (!input_device_enabled(keypad->input_dev)) ++ writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR); + } +- writel(val, keypad->base + SAMSUNG_KEYIFCON); + + clk_disable(keypad->clk); + } +@@ -516,7 +509,8 @@ static int samsung_keypad_suspend(struct device *dev) + if (input_device_enabled(input_dev)) + samsung_keypad_stop(keypad); + +- samsung_keypad_toggle_wakeup(keypad, true); ++ if (device_may_wakeup(dev)) ++ samsung_keypad_toggle_wakeup(keypad, true); + + return 0; + } +@@ -529,7 +523,8 @@ static int samsung_keypad_resume(struct device *dev) + + guard(mutex)(&input_dev->mutex); + +- samsung_keypad_toggle_wakeup(keypad, false); ++ if (device_may_wakeup(dev)) ++ samsung_keypad_toggle_wakeup(keypad, false); + + if (input_device_enabled(input_dev)) + samsung_keypad_start(keypad); +diff --git a/drivers/input/keyboard/snvs_pwrkey.c b/drivers/input/keyboard/snvs_pwrkey.c +--- a/drivers/input/keyboard/snvs_pwrkey.c ++++ b/drivers/input/keyboard/snvs_pwrkey.c +@@ -39,6 +39,9 @@ struct pwrkey_drv_data { + int keycode; + int keystate; /* 1:pressed */ + int wakeup; ++ bool suspended; /* Track suspend state */ ++ bool pending_press; /* Key pressed during suspend, report from timer callback */ ++ spinlock_t lock; /* Protects keystate, suspended and pending_press */ + struct timer_list check_timer; + struct input_dev *input; + u8 minor_rev; +@@ -49,14 +52,38 @@ static void imx_imx_snvs_check_for_events(struct timer_list *t) + struct pwrkey_drv_data *pdata = timer_container_of(pdata, t, + check_timer); + struct input_dev *input = pdata->input; ++ bool state_changed = false; ++ bool pending_press; + u32 state; + + regmap_read(pdata->snvs, SNVS_HPSR_REG, &state); + state = state & SNVS_HPSR_BTN ? 1 : 0; + +- /* only report new event if status changed */ +- if (state ^ pdata->keystate) { +- pdata->keystate = state; ++ scoped_guard(spinlock_irqsave, &pdata->lock) { ++ pending_press = pdata->pending_press; ++ if (pending_press) { ++ pdata->pending_press = false; ++ pdata->keystate = 1; ++ } ++ /* only report new event if status changed */ ++ if (state ^ pdata->keystate) { ++ pdata->keystate = state; ++ state_changed = true; ++ } ++ } ++ ++ /* ++ * Report a press event latched during suspend. If the key is still ++ * held, state_changed will be 0 (keystate already set to 1 above), ++ * so no duplicate press is reported. If already released, ++ * state_changed will fire next to report the release. ++ */ ++ if (pending_press) { ++ input_report_key(input, pdata->keycode, 1); ++ input_sync(input); ++ } ++ ++ if (state_changed) { + input_event(input, EV_KEY, pdata->keycode, state); + input_sync(input); + pm_relax(pdata->input->dev.parent); +@@ -92,8 +119,17 @@ static irqreturn_t imx_snvs_pwrkey_interrupt(int irq, void *dev_id) + input_sync(input); + pm_relax(input->dev.parent); + } else { ++ /* ++ * If the key is pressed during suspend, latch it so ++ * the timer callback can report the press event in ++ * softirq context, avoiding out-of-order events. ++ */ ++ scoped_guard(spinlock_irqsave, &pdata->lock) { ++ if (pdata->suspended) ++ pdata->pending_press = true; ++ } + mod_timer(&pdata->check_timer, +- jiffies + msecs_to_jiffies(DEBOUNCE_TIME)); ++ jiffies + msecs_to_jiffies(DEBOUNCE_TIME)); + } + } + +@@ -112,6 +148,7 @@ static void imx_snvs_pwrkey_act(void *pdata) + + static int imx_snvs_pwrkey_probe(struct platform_device *pdev) + { ++ struct device *dev = &pdev->dev; + struct pwrkey_drv_data *pdata; + struct input_dev *input; + struct device_node *np; +@@ -122,37 +159,35 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev) + u32 vid; + + /* Get SNVS register Page */ +- np = pdev->dev.of_node; ++ np = dev->of_node; + if (!np) +- return -ENODEV; ++ return dev_err_probe(dev, -ENODEV, "Device tree node not found\n"); + +- pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); ++ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->snvs = syscon_regmap_lookup_by_phandle(np, "regmap"); +- if (IS_ERR(pdata->snvs)) { +- dev_err(&pdev->dev, "Can't get snvs syscon\n"); +- return PTR_ERR(pdata->snvs); +- } ++ if (IS_ERR(pdata->snvs)) ++ return dev_err_probe(dev, PTR_ERR(pdata->snvs), "Can't get snvs syscon\n"); + + if (of_property_read_u32(np, "linux,keycode", &pdata->keycode)) { + pdata->keycode = KEY_POWER; +- dev_warn(&pdev->dev, "KEY_POWER without setting in dts\n"); ++ dev_warn(dev, "KEY_POWER without setting in dts\n"); + } + +- clk = devm_clk_get_optional_enabled(&pdev->dev, NULL); +- if (IS_ERR(clk)) { +- dev_err(&pdev->dev, "Failed to get snvs clock (%pe)\n", clk); +- return PTR_ERR(clk); +- } ++ clk = devm_clk_get_optional_enabled(dev, NULL); ++ if (IS_ERR(clk)) ++ return dev_err_probe(dev, PTR_ERR(clk), ++ "Failed to get snvs clock (%pe)\n", clk); + + pdata->wakeup = of_property_read_bool(np, "wakeup-source"); + + pdata->irq = platform_get_irq(pdev, 0); + if (pdata->irq < 0) +- return -EINVAL; ++ return pdata->irq; + ++ spin_lock_init(&pdata->lock); + error = of_property_read_u32(np, "power-off-time-sec", &val); + if (!error) { + switch (val) { +@@ -165,9 +200,8 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev) + bpt = (val / 5) - 1; + break; + default: +- dev_err(&pdev->dev, +- "power-off-time-sec %d out of range\n", val); +- return -EINVAL; ++ return dev_err_probe(dev, -EINVAL, ++ "power-off-time-sec %d out of range\n", val); + } + + regmap_update_bits(pdata->snvs, SNVS_LPCR_REG, SNVS_LPCR_BPT_MASK, +@@ -184,11 +218,9 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev) + + timer_setup(&pdata->check_timer, imx_imx_snvs_check_for_events, 0); + +- input = devm_input_allocate_device(&pdev->dev); +- if (!input) { +- dev_err(&pdev->dev, "failed to allocate the input device\n"); +- return -ENOMEM; +- } ++ input = devm_input_allocate_device(dev); ++ if (!input) ++ return dev_err_probe(dev, -ENOMEM, "failed to allocate the input device\n"); + + input->name = pdev->name; + input->phys = "snvs-pwrkey/input0"; +@@ -197,37 +229,57 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev) + input_set_capability(input, EV_KEY, pdata->keycode); + + /* input customer action to cancel release timer */ +- error = devm_add_action(&pdev->dev, imx_snvs_pwrkey_act, pdata); +- if (error) { +- dev_err(&pdev->dev, "failed to register remove action\n"); +- return error; +- } ++ error = devm_add_action(dev, imx_snvs_pwrkey_act, pdata); ++ if (error) ++ return dev_err_probe(dev, error, "failed to register remove action\n"); + + pdata->input = input; + platform_set_drvdata(pdev, pdata); + +- error = devm_request_irq(&pdev->dev, pdata->irq, +- imx_snvs_pwrkey_interrupt, +- 0, pdev->name, pdev); +- if (error) { +- dev_err(&pdev->dev, "interrupt not available.\n"); +- return error; +- } ++ error = devm_request_irq(dev, pdata->irq, ++ imx_snvs_pwrkey_interrupt, ++ 0, pdev->name, pdev); ++ if (error) ++ return dev_err_probe(dev, error, "interrupt not available.\n"); + + error = input_register_device(input); +- if (error < 0) { +- dev_err(&pdev->dev, "failed to register input device\n"); +- return error; +- } ++ if (error < 0) ++ return dev_err_probe(dev, error, "failed to register input device\n"); + +- device_init_wakeup(&pdev->dev, pdata->wakeup); +- error = dev_pm_set_wake_irq(&pdev->dev, pdata->irq); ++ device_init_wakeup(dev, pdata->wakeup); ++ error = dev_pm_set_wake_irq(dev, pdata->irq); + if (error) +- dev_err(&pdev->dev, "irq wake enable failed.\n"); ++ dev_err(dev, "irq wake enable failed.\n"); + + return 0; + } + ++static int imx_snvs_pwrkey_suspend(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev); ++ ++ guard(spinlock_irq)(&pdata->lock); ++ pdata->suspended = true; ++ ++ return 0; ++} ++ ++static int imx_snvs_pwrkey_resume(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev); ++ ++ guard(spinlock_irq)(&pdata->lock); ++ pdata->suspended = false; ++ ++ return 0; ++} ++ ++static DEFINE_SIMPLE_DEV_PM_OPS(imx_snvs_pwrkey_pm_ops, ++ imx_snvs_pwrkey_suspend, ++ imx_snvs_pwrkey_resume); ++ + static const struct of_device_id imx_snvs_pwrkey_ids[] = { + { .compatible = "fsl,sec-v4.0-pwrkey" }, + { /* sentinel */ } +@@ -238,6 +290,7 @@ static struct platform_driver imx_snvs_pwrkey_driver = { + .driver = { + .name = "snvs_pwrkey", + .of_match_table = imx_snvs_pwrkey_ids, ++ .pm = pm_ptr(&imx_snvs_pwrkey_pm_ops), + }, + .probe = imx_snvs_pwrkey_probe, + }; +diff --git a/drivers/input/keyboard/tca8418_keypad.c b/drivers/input/keyboard/tca8418_keypad.c +--- a/drivers/input/keyboard/tca8418_keypad.c ++++ b/drivers/input/keyboard/tca8418_keypad.c +@@ -254,7 +254,8 @@ static int tca8418_configure(struct tca8418_keypad *keypad_data, + return error; + + error = tca8418_write_byte(keypad_data, REG_CFG, +- CFG_INT_CFG | CFG_OVR_FLOW_IEN | CFG_KE_IEN); ++ CFG_INT_CFG | CFG_OVR_FLOW_IEN | ++ CFG_OVR_FLOW_M | CFG_KE_IEN); + + return error; + } +diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig +--- a/drivers/input/misc/Kconfig ++++ b/drivers/input/misc/Kconfig +@@ -842,6 +842,18 @@ config INPUT_IQS7222 + To compile this driver as a module, choose M here: the + module will be called iqs7222. + ++config INPUT_ISA1200_HAPTIC ++ tristate "Imagis ISA1200 haptic feedback unit" ++ depends on I2C ++ select INPUT_FF_MEMLESS ++ select REGMAP_I2C ++ help ++ Say Y to enable support for the Imagis ISA1200 haptic ++ feedback unit. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called isa1200. ++ + config INPUT_CMA3000 + tristate "VTI CMA3000 Tri-axis accelerometer" + help +diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile +--- a/drivers/input/misc/Makefile ++++ b/drivers/input/misc/Makefile +@@ -49,6 +49,7 @@ obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o + obj-$(CONFIG_INPUT_IQS269A) += iqs269a.o + obj-$(CONFIG_INPUT_IQS626A) += iqs626a.o + obj-$(CONFIG_INPUT_IQS7222) += iqs7222.o ++obj-$(CONFIG_INPUT_ISA1200_HAPTIC) += isa1200.o + obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o + obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o + obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o +diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c +--- a/drivers/input/misc/ims-pcu.c ++++ b/drivers/input/misc/ims-pcu.c +@@ -2234,6 +2234,7 @@ static const struct usb_device_id ims_pcu_id_table[] = { + }, + { } + }; ++MODULE_DEVICE_TABLE(usb, ims_pcu_id_table); + + static const struct attribute_group *ims_pcu_sysfs_groups[] = { + &ims_pcu_attr_group, +diff --git a/drivers/input/misc/isa1200.c b/drivers/input/misc/isa1200.c +new file mode 100644 +--- /dev/null ++++ b/drivers/input/misc/isa1200.c +@@ -0,0 +1,533 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * System control (LDO regulator) ++ * ++ * LDO voltage to register mapping is linear, but it is split in two parts: ++ * 2.3V - 3.0V map to 0x08 - 0x0f; 3.1V - 3.8V map to 0x00 - 0x7 ++ */ ++ ++#define ISA1200_SCTRL 0x00 ++#define ISA1200_LDO_VOLTAGE_BASE 0x08 ++#define ISA1200_LDO_VOLTAGE_STEP 100000 ++#define ISA1200_LDO_VOLTAGE_2V3 23 ++#define ISA1200_LDO_VOLTAGE_3V1 31 ++#define ISA1200_LDO_VOLTAGE_MIN 2300000 ++#define ISA1200_LDO_VOLTAGE_MAX 3800000 ++ ++/* ++ * The output frequency is calculated with this formula: ++ * ++ * base clock frequency ++ * fout = ----------------------------------------- ++ * (128 - PWM_FREQ) * 2 * PLLDIV * PWM_PERIOD ++ * ++ * The base clock frequency is the clock frequency provided on the ++ * clock input to the chip, divided by the value in HCTRL0 ++ * ++ * PWM_FREQ is configured in register HCTRL4, it is common to set this ++ * to 0 to get only two variables to calculate. ++ * ++ * PLLDIV is configured in register HCTRL3 (bits 7..4, so 0..15) ++ * PWM_PERIOD is configured in register HCTRL6 ++ * Further the duty cycle can be configured in HCTRL5 ++ */ ++ ++/* ++ * HCTRL0 configures clock or PWM input and selects the divider for ++ * the clock input. ++ */ ++#define ISA1200_HCTRL0 0x30 ++#define ISA1200_HCTRL0_HAP_ENABLE BIT(7) ++#define ISA1200_HCTRL0_PWM_GEN_MODE BIT(4) ++#define ISA1200_HCTRL0_PWM_INPUT_MODE BIT(3) ++#define ISA1200_HCTRL0_CLKDIV_128 128 ++ ++/* ++ * HCTRL1 configures the motor type and clock sourse ++ */ ++#define ISA1200_HCTRL1 0x31 ++#define ISA1200_HCTRL1_EXT_CLOCK BIT(7) ++#define ISA1200_HCTRL1_DAC_INVERT BIT(6) ++#define ISA1200_HCTRL1_MODE(n) (((n) & 1) << 5) ++ ++/* HCTRL2 controls software reset of the chip */ ++#define ISA1200_HCTRL2 0x32 ++#define ISA1200_HCTRL2_SW_RESET BIT(0) ++ ++/* ++ * HCTRL3 controls the PLL divisor ++ * ++ * Bits [0,1] are always set to 1 (we don't know what they are ++ * used for) and bit 4 and upward control the PLL divisor. ++ */ ++#define ISA1200_HCTRL3 0x33 ++#define ISA1200_HCTRL3_DEFAULT 0x03 ++#define ISA1200_HCTRL3_PLLDIV(n) (((n) & 0xf) << 4) ++ ++/* HCTRL4 controls the PWM frequency of external channel */ ++#define ISA1200_HCTRL4 0x34 ++ ++/* HCTRL5 controls the PWM high duty cycle of internal channel */ ++#define ISA1200_HCTRL5 0x35 ++ ++/* HCTRL6 controls the PWM period of internal channel */ ++#define ISA1200_HCTRL6 0x36 ++#define ISA1200_HCTRL6_PERIOD_SCALE 100 ++ ++/* The use for these registers is unknown but they exist */ ++#define ISA1200_HCTRL7 0x37 ++#define ISA1200_HCTRL8 0x38 ++#define ISA1200_HCTRL9 0x39 ++#define ISA1200_HCTRLA 0x3a ++#define ISA1200_HCTRLB 0x3b ++#define ISA1200_HCTRLC 0x3c ++#define ISA1200_HCTRLD 0x3d ++ ++#define ISA1200_EN_PINS_MAX 2 ++ ++static const struct regulator_bulk_data isa1200_supplies[] = { ++ { .supply = "vdd" }, { .supply = "vddp" }, ++}; ++ ++struct isa1200_config { ++ u32 ldo_voltage; ++ u32 mode; ++ u32 clkdiv; ++ u32 plldiv; ++ u32 freq; ++ u32 period; ++ u32 duty; ++}; ++ ++struct isa1200 { ++ struct input_dev *input; ++ struct regmap *map; ++ ++ struct clk *clk; ++ struct pwm_device *pwm; ++ struct gpio_descs *enable_gpios; ++ struct regulator_bulk_data *supplies; ++ ++ struct work_struct play_work; ++ struct isa1200_config config; ++ ++ int level; ++ bool suspended; ++ bool active; ++}; ++ ++static const struct regmap_config isa1200_regmap_config = { ++ .reg_bits = 8, ++ .val_bits = 8, ++ .max_register = ISA1200_HCTRLD, ++}; ++ ++static void isa1200_start(struct isa1200 *isa) ++{ ++ struct isa1200_config *config = &isa->config; ++ struct device *dev = &isa->input->dev; ++ struct pwm_state state; ++ u8 hctrl0 = 0, hctrl1 = 0; ++ DECLARE_BITMAP(values, ISA1200_EN_PINS_MAX); ++ int err; ++ ++ if (!isa->active) { ++ err = regulator_bulk_enable(ARRAY_SIZE(isa1200_supplies), ++ isa->supplies); ++ if (err) { ++ dev_err(dev, "failed to enable supplies (%d)\n", err); ++ return; ++ } ++ ++ err = clk_prepare_enable(isa->clk); ++ if (err) { ++ dev_err(dev, "failed to enable clock (%d)\n", err); ++ regulator_bulk_disable(ARRAY_SIZE(isa1200_supplies), ++ isa->supplies); ++ return; ++ } ++ ++ bitmap_fill(values, ISA1200_EN_PINS_MAX); ++ gpiod_multi_set_value_cansleep(isa->enable_gpios, values); ++ ++ usleep_range(200, 300); ++ } ++ ++ regmap_write(isa->map, ISA1200_SCTRL, config->ldo_voltage); ++ ++ if (isa->clk) { ++ hctrl0 = ISA1200_HCTRL0_PWM_GEN_MODE; ++ hctrl1 = ISA1200_HCTRL1_EXT_CLOCK; ++ } ++ ++ if (isa->pwm) { ++ hctrl0 = ISA1200_HCTRL0_PWM_INPUT_MODE; ++ hctrl1 = 0; ++ } ++ ++ hctrl0 |= __ffs(config->clkdiv / ISA1200_HCTRL0_CLKDIV_128); ++ hctrl1 |= ISA1200_HCTRL1_DAC_INVERT; ++ hctrl1 |= ISA1200_HCTRL1_MODE(config->mode); ++ ++ regmap_write(isa->map, ISA1200_HCTRL0, hctrl0); ++ regmap_write(isa->map, ISA1200_HCTRL1, hctrl1); ++ ++ /* Make sure to de-assert software reset */ ++ regmap_write(isa->map, ISA1200_HCTRL2, 0x00); ++ ++ /* PLL divisor */ ++ regmap_write(isa->map, ISA1200_HCTRL3, ++ ISA1200_HCTRL3_PLLDIV(config->plldiv) | ++ ISA1200_HCTRL3_DEFAULT); ++ ++ /* Frequency */ ++ regmap_write(isa->map, ISA1200_HCTRL4, config->freq); ++ /* Duty cycle */ ++ regmap_write(isa->map, ISA1200_HCTRL5, config->period >> 1); ++ /* Period */ ++ regmap_write(isa->map, ISA1200_HCTRL6, config->period); ++ ++ hctrl0 |= ISA1200_HCTRL0_HAP_ENABLE; ++ regmap_write(isa->map, ISA1200_HCTRL0, hctrl0); ++ ++ if (isa->clk) ++ regmap_write(isa->map, ISA1200_HCTRL5, config->duty); ++ ++ if (isa->pwm) { ++ pwm_get_state(isa->pwm, &state); ++ state.duty_cycle = config->duty; ++ state.enabled = true; ++ pwm_apply_might_sleep(isa->pwm, &state); ++ } ++ ++ isa->active = true; ++} ++ ++static void isa1200_stop(struct isa1200 *isa) ++{ ++ struct pwm_state state; ++ DECLARE_BITMAP(values, ISA1200_EN_PINS_MAX); ++ ++ if (!isa->active) ++ return; ++ ++ if (isa->pwm) { ++ pwm_get_state(isa->pwm, &state); ++ state.duty_cycle = 0; ++ state.enabled = false; ++ pwm_apply_might_sleep(isa->pwm, &state); ++ } ++ ++ regmap_write(isa->map, ISA1200_HCTRL0, 0x00); ++ ++ bitmap_zero(values, ISA1200_EN_PINS_MAX); ++ gpiod_multi_set_value_cansleep(isa->enable_gpios, values); ++ ++ clk_disable_unprepare(isa->clk); ++ regulator_bulk_disable(ARRAY_SIZE(isa1200_supplies), ++ isa->supplies); ++ ++ isa->active = false; ++} ++ ++static void isa1200_play_work(struct work_struct *work) ++{ ++ struct isa1200 *isa = container_of(work, struct isa1200, play_work); ++ ++ if (!READ_ONCE(isa->suspended)) { ++ if (isa->level) ++ isa1200_start(isa); ++ else ++ isa1200_stop(isa); ++ } ++} ++ ++static int isa1200_vibrator_play_effect(struct input_dev *input, void *data, ++ struct ff_effect *effect) ++{ ++ struct isa1200 *isa = input_get_drvdata(input); ++ int level; ++ ++ /* ++ * TODO: we currently only support rumble. ++ * The ISA1200 can control two motors and some devices ++ * also have two motors mounted. ++ */ ++ level = effect->u.rumble.strong_magnitude; ++ if (!level) ++ level = effect->u.rumble.weak_magnitude; ++ ++ dev_dbg(&input->dev, "FF effect type %d level %d\n", ++ effect->type, level); ++ ++ if (isa->level != level) { ++ isa->level = level; ++ if (!READ_ONCE(isa->suspended)) ++ schedule_work(&isa->play_work); ++ } ++ ++ return 0; ++} ++ ++static void isa1200_vibrator_close(struct input_dev *input) ++{ ++ struct isa1200 *isa = input_get_drvdata(input); ++ ++ cancel_work_sync(&isa->play_work); ++ isa1200_stop(isa); ++ isa->level = 0; ++} ++ ++static int isa1200_of_probe(struct i2c_client *client) ++{ ++ struct isa1200 *isa = i2c_get_clientdata(client); ++ struct isa1200_config *config = &isa->config; ++ struct device *dev = &client->dev; ++ struct fwnode_handle *ldo_node; ++ int err; ++ ++ isa->clk = devm_clk_get_optional(dev, NULL); ++ if (IS_ERR(isa->clk)) ++ return dev_err_probe(dev, PTR_ERR(isa->clk), ++ "failed to get clock\n"); ++ ++ isa->pwm = devm_pwm_get(dev, NULL); ++ if (IS_ERR(isa->pwm)) { ++ err = PTR_ERR(isa->pwm); ++ if (err == -ENODEV || err == -EINVAL) ++ isa->pwm = NULL; ++ else ++ return dev_err_probe(dev, err, "getting PWM\n"); ++ } ++ ++ if (!isa->clk && !isa->pwm) ++ return dev_err_probe(dev, -EINVAL, ++ "clock or PWM are required, none were provided\n"); ++ ++ err = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(isa1200_supplies), ++ isa1200_supplies, &isa->supplies); ++ if (err) ++ return dev_err_probe(dev, err, "failed to get supplies\n"); ++ ++ isa->enable_gpios = devm_gpiod_get_array_optional(dev, "control", ++ GPIOD_OUT_LOW); ++ if (IS_ERR(isa->enable_gpios)) ++ return dev_err_probe(dev, PTR_ERR(isa->enable_gpios), ++ "failed to get enable gpios\n"); ++ ++ if (isa->enable_gpios && isa->enable_gpios->ndescs > ISA1200_EN_PINS_MAX) ++ return dev_err_probe(dev, -EINVAL, "too many enable gpios\n"); ++ ++ ldo_node = device_get_named_child_node(dev, "ldo"); ++ if (!ldo_node) ++ return dev_err_probe(dev, -ENODEV, ++ "failed to get embedded LDO node\n"); ++ ++ err = fwnode_property_read_u32(ldo_node, "regulator-min-microvolt", ++ &config->ldo_voltage); ++ fwnode_handle_put(ldo_node); ++ if (err) ++ return dev_err_probe(dev, err, ++ "failed to get ldo voltage\n"); ++ ++ config->ldo_voltage = clamp(config->ldo_voltage, ++ ISA1200_LDO_VOLTAGE_MIN, ++ ISA1200_LDO_VOLTAGE_MAX); ++ ++ config->ldo_voltage /= ISA1200_LDO_VOLTAGE_STEP; ++ if (config->ldo_voltage < ISA1200_LDO_VOLTAGE_3V1) ++ config->ldo_voltage = config->ldo_voltage - ++ ISA1200_LDO_VOLTAGE_2V3 + ++ ISA1200_LDO_VOLTAGE_BASE; ++ else ++ config->ldo_voltage -= ISA1200_LDO_VOLTAGE_3V1; ++ ++ config->mode = 0; /* LRA_MODE */ ++ device_property_read_u32(dev, "imagis,mode", &config->mode); ++ ++ config->clkdiv = ISA1200_HCTRL0_CLKDIV_128; ++ device_property_read_u32(dev, "imagis,clk-div", &config->clkdiv); ++ if (!config->clkdiv) ++ return dev_err_probe(dev, -EINVAL, "clk-div cannot be zero\n"); ++ ++ config->clkdiv = clamp(config->clkdiv, ISA1200_HCTRL0_CLKDIV_128, ++ ISA1200_HCTRL0_CLKDIV_128 << 3); ++ ++ err = device_property_read_u32(dev, "imagis,pll-div", &config->plldiv); ++ if (err || !config->plldiv) ++ config->plldiv = 1; ++ ++ config->period = 0; ++ config->freq = 0; ++ config->duty = 0; ++ ++ if (isa->clk) { ++ err = device_property_read_u32(dev, "imagis,period-ns", ++ &config->period); ++ if (err) ++ return dev_err_probe(dev, err, ++ "failed to get period\n"); ++ ++ /* ++ * TODO: The scale value is arbitrary, but it fits observations ++ * quite well, and the exact conversion method is unknown. ++ * The period property value returned above is the HCTRL6 ++ * register value set by the vendor code, multiplied by 100. ++ */ ++ config->period /= ISA1200_HCTRL6_PERIOD_SCALE; ++ config->duty = config->period >> 1; ++ } ++ ++ if (isa->pwm) { ++ struct pwm_state state; ++ ++ pwm_init_state(isa->pwm, &state); ++ ++ if (!state.period) ++ return dev_err_probe(dev, -EINVAL, ++ "PWM period cannot be zero\n"); ++ ++ config->freq = div64_u64(NANO, state.period * config->clkdiv); ++ config->duty = state.period >> 1; ++ ++ err = pwm_apply_might_sleep(isa->pwm, &state); ++ if (err) ++ return dev_err_probe(dev, err, ++ "failed to apply initial PWM state\n"); ++ } ++ ++ /* ++ * TODO: If device is using a clock, this property should return the ++ * value written to the HCTRL5 register by downstrem code. It likely ++ * needs to be converted into a meaningful duty cycle value, though ++ * unfortunately the exact conversion mechanism is unknown. If the ++ * device uses PWM, this property will return the correct duty cycle ++ * in nanoseconds. ++ */ ++ device_property_read_u32(dev, "imagis,duty-cycle-ns", &config->duty); ++ ++ return 0; ++} ++ ++static int isa1200_probe(struct i2c_client *client) ++{ ++ struct isa1200 *isa; ++ struct device *dev = &client->dev; ++ int err; ++ ++ isa = devm_kzalloc(dev, sizeof(*isa), GFP_KERNEL); ++ if (!isa) ++ return -ENOMEM; ++ ++ isa->input = devm_input_allocate_device(dev); ++ if (!isa->input) ++ return -ENOMEM; ++ ++ i2c_set_clientdata(client, isa); ++ ++ err = isa1200_of_probe(client); ++ if (err) ++ return err; ++ ++ isa->map = devm_regmap_init_i2c(client, &isa1200_regmap_config); ++ if (IS_ERR(isa->map)) ++ return dev_err_probe(dev, PTR_ERR(isa->map), ++ "failed to initialize register map\n"); ++ ++ INIT_WORK(&isa->play_work, isa1200_play_work); ++ ++ isa->input->name = "isa1200-haptic"; ++ isa->input->id.bustype = BUS_I2C; ++ isa->input->close = isa1200_vibrator_close; ++ ++ isa->active = false; ++ ++ input_set_drvdata(isa->input, isa); ++ ++ /* TODO: this hardware can likely support more than rumble */ ++ input_set_capability(isa->input, EV_FF, FF_RUMBLE); ++ ++ err = input_ff_create_memless(isa->input, NULL, ++ isa1200_vibrator_play_effect); ++ if (err) ++ return dev_err_probe(dev, err, "failed to create FF dev\n"); ++ ++ err = input_register_device(isa->input); ++ if (err) ++ return dev_err_probe(dev, err, "failed to register input dev\n"); ++ ++ return 0; ++} ++ ++static int isa1200_suspend(struct device *dev) ++{ ++ struct isa1200 *isa = dev_get_drvdata(dev); ++ ++ guard(mutex)(&isa->input->mutex); ++ ++ if (input_device_enabled(isa->input)) { ++ WRITE_ONCE(isa->suspended, true); ++ cancel_work_sync(&isa->play_work); ++ isa1200_stop(isa); ++ } ++ ++ return 0; ++} ++ ++static int isa1200_resume(struct device *dev) ++{ ++ struct isa1200 *isa = dev_get_drvdata(dev); ++ ++ guard(mutex)(&isa->input->mutex); ++ ++ if (input_device_enabled(isa->input)) { ++ WRITE_ONCE(isa->suspended, false); ++ if (isa->level) ++ schedule_work(&isa->play_work); ++ } ++ ++ return 0; ++} ++ ++static DEFINE_SIMPLE_DEV_PM_OPS(isa1200_pm_ops, isa1200_suspend, isa1200_resume); ++ ++static const struct of_device_id isa1200_of_match[] = { ++ { .compatible = "imagis,isa1200" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, isa1200_of_match); ++ ++static struct i2c_driver isa1200_i2c_driver = { ++ .driver = { ++ .name = "isa1200", ++ .of_match_table = isa1200_of_match, ++ .pm = pm_sleep_ptr(&isa1200_pm_ops), ++ }, ++ .probe = isa1200_probe, ++}; ++module_i2c_driver(isa1200_i2c_driver); ++ ++MODULE_AUTHOR("Linus Walleij "); ++MODULE_AUTHOR("Svyatoslav Ryhel "); ++MODULE_DESCRIPTION("Imagis ISA1200 haptic feedback unit"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/input/mouse/elan_i2c.h b/drivers/input/mouse/elan_i2c.h +--- a/drivers/input/mouse/elan_i2c.h ++++ b/drivers/input/mouse/elan_i2c.h +@@ -102,7 +102,8 @@ struct elan_transport_ops { + int (*prepare_fw_update)(struct i2c_client *client, u16 ic_type, + u8 iap_version, u16 fw_page_size); + int (*write_fw_block)(struct i2c_client *client, u16 fw_page_size, +- const u8 *page, u16 checksum, int idx); ++ u16 fw_page_delay, const u8 *page, u16 checksum, ++ int idx); + int (*finish_fw_update)(struct i2c_client *client, + struct completion *reset_done); + +diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c +--- a/drivers/input/mouse/elan_i2c_core.c ++++ b/drivers/input/mouse/elan_i2c_core.c +@@ -16,27 +16,27 @@ + */ + + #include ++#include + #include + #include + #include + #include + #include ++#include + #include + #include + #include +-#include +-#include +-#include +-#include +-#include +-#include +-#include + #include +-#include ++#include ++#include + #include + #include + #include + #include ++#include ++#include ++#include ++#include + #include + + #include "elan_i2c.h" +@@ -86,6 +86,7 @@ struct elan_tp_data { + u16 ic_type; + u16 fw_validpage_count; + u16 fw_page_size; ++ u16 fw_page_delay; + u32 fw_signature_address; + + u8 min_baseline; +@@ -127,8 +128,11 @@ static u32 elan_i2c_lookup_quirks(u16 ic_type, u16 product_id) + } + + static int elan_get_fwinfo(u16 ic_type, u8 iap_version, u16 *validpage_count, +- u32 *signature_address, u16 *page_size) ++ u32 *signature_address, u16 *page_size, ++ u16 *page_delay) + { ++ *page_delay = 30; ++ + switch (ic_type) { + case 0x00: + case 0x06: +@@ -164,6 +168,7 @@ static int elan_get_fwinfo(u16 ic_type, u8 iap_version, u16 *validpage_count, + break; + case 0x19: + *validpage_count = 2032; ++ *page_delay = 10; + break; + default: + /* unknown ic type clear value */ +@@ -179,6 +184,7 @@ static int elan_get_fwinfo(u16 ic_type, u8 iap_version, u16 *validpage_count, + if ((ic_type == 0x14 || ic_type == 0x15) && iap_version >= 2) { + *validpage_count /= 8; + *page_size = ETP_FW_PAGE_SIZE_512; ++ *page_delay = 50; + } else if (ic_type >= 0x0D && iap_version >= 1) { + *validpage_count /= 2; + *page_size = ETP_FW_PAGE_SIZE_128; +@@ -368,7 +374,8 @@ static int elan_query_device_info(struct elan_tp_data *data) + error = elan_get_fwinfo(data->ic_type, data->iap_version, + &data->fw_validpage_count, + &data->fw_signature_address, +- &data->fw_page_size); ++ &data->fw_page_size, ++ &data->fw_page_delay); + if (error) + dev_warn(&data->client->dev, + "unexpected iap version %#04x (ic type: %#04x), firmware update will not work\n", +@@ -479,14 +486,16 @@ static int elan_query_device_parameters(struct elan_tp_data *data) + ********************************************************** + */ + static int elan_write_fw_block(struct elan_tp_data *data, u16 page_size, +- const u8 *page, u16 checksum, int idx) ++ u16 page_delay, const u8 *page, u16 checksum, ++ int idx) + { + int retry = ETP_RETRY_COUNT; + int error; + + do { + error = data->ops->write_fw_block(data->client, page_size, +- page, checksum, idx); ++ page_delay, page, checksum, ++ idx); + if (!error) + return 0; + +@@ -525,7 +534,8 @@ static int __elan_update_firmware(struct elan_tp_data *data, + checksum += ((page[j + 1] << 8) | page[j]); + + error = elan_write_fw_block(data, data->fw_page_size, +- page, checksum, i); ++ data->fw_page_delay, page, checksum, ++ i); + if (error) { + dev_err(dev, "write page %d fail: %d\n", i, error); + return error; +diff --git a/drivers/input/mouse/elan_i2c_i2c.c b/drivers/input/mouse/elan_i2c_i2c.c +--- a/drivers/input/mouse/elan_i2c_i2c.c ++++ b/drivers/input/mouse/elan_i2c_i2c.c +@@ -625,7 +625,8 @@ static int elan_i2c_prepare_fw_update(struct i2c_client *client, u16 ic_type, + } + + static int elan_i2c_write_fw_block(struct i2c_client *client, u16 fw_page_size, +- const u8 *page, u16 checksum, int idx) ++ u16 fw_page_delay, const u8 *page, u16 checksum, ++ int idx) + { + struct device *dev = &client->dev; + u8 val[3]; +@@ -650,7 +651,7 @@ static int elan_i2c_write_fw_block(struct i2c_client *client, u16 fw_page_size, + } + + /* Wait for F/W to update one page ROM data. */ +- msleep(fw_page_size == ETP_FW_PAGE_SIZE_512 ? 50 : 35); ++ msleep(fw_page_delay); + + error = elan_i2c_read_cmd(client, ETP_I2C_IAP_CTRL_CMD, val); + if (error) { +diff --git a/drivers/input/mouse/elan_i2c_smbus.c b/drivers/input/mouse/elan_i2c_smbus.c +--- a/drivers/input/mouse/elan_i2c_smbus.c ++++ b/drivers/input/mouse/elan_i2c_smbus.c +@@ -416,7 +416,8 @@ static int elan_smbus_prepare_fw_update(struct i2c_client *client, u16 ic_type, + + + static int elan_smbus_write_fw_block(struct i2c_client *client, u16 fw_page_size, +- const u8 *page, u16 checksum, int idx) ++ u16 fw_page_delay, const u8 *page, u16 checksum, ++ int idx) + { + struct device *dev = &client->dev; + int error; +diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c +--- a/drivers/input/mouse/focaltech.c ++++ b/drivers/input/mouse/focaltech.c +@@ -78,8 +78,8 @@ struct focaltech_finger_state { + * Absolute position (from the bottom left corner) of the + * finger. + */ +- unsigned int x; +- unsigned int y; ++ int x; ++ int y; + }; + + /* +@@ -108,7 +108,7 @@ struct focaltech_hw_state { + }; + + struct focaltech_data { +- unsigned int x_max, y_max; ++ int x_max, y_max; + struct focaltech_hw_state state; + }; + +@@ -126,17 +126,16 @@ static void focaltech_report_state(struct psmouse *psmouse) + input_mt_slot(dev, i); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); + if (active) { +- unsigned int clamped_x, clamped_y; + /* + * The touchpad might report invalid data, so we clamp + * the resulting values so that we do not confuse +- * userspace. ++ * userspace or accumulate coordinate wind-up. + */ +- clamped_x = clamp(finger->x, 0U, priv->x_max); +- clamped_y = clamp(finger->y, 0U, priv->y_max); +- input_report_abs(dev, ABS_MT_POSITION_X, clamped_x); ++ finger->x = clamp(finger->x, 0, priv->x_max); ++ finger->y = clamp(finger->y, 0, priv->y_max); ++ input_report_abs(dev, ABS_MT_POSITION_X, finger->x); + input_report_abs(dev, ABS_MT_POSITION_Y, +- priv->y_max - clamped_y); ++ priv->y_max - finger->y); + input_report_abs(dev, ABS_TOOL_WIDTH, state->width); + } + } +diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c +--- a/drivers/input/mouse/psmouse-base.c ++++ b/drivers/input/mouse/psmouse-base.c +@@ -256,9 +256,8 @@ static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_sta + */ + void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) + { +- serio_pause_rx(psmouse->ps2dev.serio); ++ guard(serio_pause_rx)(psmouse->ps2dev.serio); + __psmouse_set_state(psmouse, new_state); +- serio_continue_rx(psmouse->ps2dev.serio); + } + + /* +@@ -267,7 +266,15 @@ void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) + */ + static int psmouse_handle_byte(struct psmouse *psmouse) + { +- psmouse_ret_t rc = psmouse->protocol_handler(psmouse); ++ psmouse_ret_t rc; ++ ++ /* protocol_handler is NULL when device is being disconnected */ ++ if (unlikely(!psmouse->protocol_handler)) { ++ psmouse->pktcnt = 0; ++ return 0; ++ } ++ ++ rc = psmouse->protocol_handler(psmouse); + + switch (rc) { + case PSMOUSE_BAD_DATA: +@@ -484,13 +491,16 @@ static int psmouse_poll(struct psmouse *psmouse) + PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)); + } + +-static bool psmouse_check_pnp_id(const char *id, const char * const ids[]) ++static bool psmouse_check_pnp_id(const char *p, const char * const ids[]) + { +- int i; ++ const char * const *id; ++ size_t len; + +- for (i = 0; ids[i]; i++) +- if (!strcasecmp(id, ids[i])) ++ for (id = ids; *id; id++) { ++ len = strlen(*id); ++ if (!strncasecmp(p, *id, len) && (p[len] == ' ' || p[len] == '\0')) + return true; ++ } + + return false; + } +@@ -501,28 +511,26 @@ static bool psmouse_check_pnp_id(const char *id, const char * const ids[]) + bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[]) + { + struct serio *serio = psmouse->ps2dev.serio; +- char *p, *fw_id_copy, *save_ptr; +- bool found = false; ++ const char *p = serio->firmware_id; + +- if (strncmp(serio->firmware_id, "PNP: ", 5)) ++ if (!strstarts(p, "PNP: ")) + return false; + +- fw_id_copy = kstrndup(&serio->firmware_id[5], +- sizeof(serio->firmware_id) - 5, +- GFP_KERNEL); +- if (!fw_id_copy) +- return false; ++ p += 5; ++ while (*p) { ++ p = skip_spaces(p); ++ if (!*p) ++ break; + +- save_ptr = fw_id_copy; +- while ((p = strsep(&fw_id_copy, " ")) != NULL) { +- if (psmouse_check_pnp_id(p, ids)) { +- found = true; ++ if (psmouse_check_pnp_id(p, ids)) ++ return true; ++ ++ p = strchr(p, ' '); ++ if (!p) + break; +- } + } + +- kfree(save_ptr); +- return found; ++ return false; + } + + /* +@@ -1311,10 +1319,10 @@ static void psmouse_resync(struct work_struct *work) + bool failed = false, enabled = false; + int i; + +- mutex_lock(&psmouse_mutex); ++ guard(mutex)(&psmouse_mutex); + + if (psmouse->state != PSMOUSE_RESYNCING) +- goto out; ++ return; + + if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { + parent = psmouse_from_serio(serio->parent); +@@ -1392,8 +1400,6 @@ static void psmouse_resync(struct work_struct *work) + + if (parent) + psmouse_activate(parent); +- out: +- mutex_unlock(&psmouse_mutex); + } + + /* +@@ -1404,7 +1410,7 @@ static void psmouse_cleanup(struct serio *serio) + struct psmouse *psmouse = psmouse_from_serio(serio); + struct psmouse *parent = NULL; + +- mutex_lock(&psmouse_mutex); ++ guard(mutex)(&psmouse_mutex); + + if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { + parent = psmouse_from_serio(serio->parent); +@@ -1440,8 +1446,6 @@ static void psmouse_cleanup(struct serio *serio) + + psmouse_activate(parent); + } +- +- mutex_unlock(&psmouse_mutex); + } + + /* +@@ -1452,20 +1456,20 @@ static void psmouse_disconnect(struct serio *serio) + struct psmouse *psmouse = psmouse_from_serio(serio); + struct psmouse *parent = NULL; + +- mutex_lock(&psmouse_mutex); ++ disable_work_sync(&psmouse->resync_work); ++ ++ guard(mutex)(&psmouse_mutex); + + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + +- /* make sure we don't have a resync in progress */ +- mutex_unlock(&psmouse_mutex); +- disable_work_sync(&psmouse->resync_work); +- mutex_lock(&psmouse_mutex); +- + if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { + parent = psmouse_from_serio(serio->parent); + psmouse_deactivate(parent); + } + ++ scoped_guard(serio_pause_rx, serio) ++ psmouse->protocol_handler = NULL; ++ + if (psmouse->disconnect) + psmouse->disconnect(psmouse); + +@@ -1484,8 +1488,6 @@ static void psmouse_disconnect(struct serio *serio) + + if (parent) + psmouse_activate(parent); +- +- mutex_unlock(&psmouse_mutex); + } + + static int psmouse_switch_protocol(struct psmouse *psmouse, +@@ -1654,14 +1656,12 @@ static int __psmouse_reconnect(struct serio *serio, bool fast_reconnect) + enum psmouse_type type; + int rc = -1; + +- mutex_lock(&psmouse_mutex); ++ lockdep_assert_held(&psmouse_mutex); + + if (fast_reconnect) { + reconnect_handler = psmouse->fast_reconnect; +- if (!reconnect_handler) { +- rc = -ENOENT; +- goto out_unlock; +- } ++ if (!reconnect_handler) ++ return -ENOENT; + } else { + reconnect_handler = psmouse->reconnect; + } +@@ -1713,18 +1713,20 @@ static int __psmouse_reconnect(struct serio *serio, bool fast_reconnect) + if (parent) + psmouse_activate(parent); + +-out_unlock: +- mutex_unlock(&psmouse_mutex); + return rc; + } + + static int psmouse_reconnect(struct serio *serio) + { ++ guard(mutex)(&psmouse_mutex); ++ + return __psmouse_reconnect(serio, false); + } + + static int psmouse_fast_reconnect(struct serio *serio) + { ++ guard(mutex)(&psmouse_mutex); ++ + return __psmouse_reconnect(serio, true); + } + +diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c +--- a/drivers/input/mouse/synaptics.c ++++ b/drivers/input/mouse/synaptics.c +@@ -164,6 +164,7 @@ static const char * const topbuttonpad_pnp_ids[] = { + #ifdef CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS + static const char * const smbus_pnp_ids[] = { + /* all of the topbuttonpad_pnp_ids are valid, we just add some extras */ ++ "DLL0597", /* Dell Inspiron 3521 */ + "DLL060d", /* Dell Precision M3800 */ + "LEN0048", /* X1 Carbon 3 */ + "LEN0046", /* X250 */ +diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c +--- a/drivers/input/mouse/synaptics_i2c.c ++++ b/drivers/input/mouse/synaptics_i2c.c +@@ -261,7 +261,7 @@ static s32 synaptics_i2c_reg_set(struct i2c_client *client, u16 reg, u8 val) + if (error) + return error; + +- return error; ++ return 0; + } + + static s32 synaptics_i2c_word_get(struct i2c_client *client, u16 reg) +diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c +--- a/drivers/input/rmi4/rmi_f12.c ++++ b/drivers/input/rmi4/rmi_f12.c +@@ -155,6 +155,10 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12) + offset += 4; + } + ++ /* When platform data are provided, we're done */ ++ if (sensor->x_mm && sensor->y_mm) ++ return 0; ++ + /* + * Use the Query DPM feature when the resolution query register + * exists. +@@ -171,8 +175,10 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12) + } + dpm_resolution = buf[0]; + +- sensor->x_mm = sensor->max_x / dpm_resolution; +- sensor->y_mm = sensor->max_y / dpm_resolution; ++ if (!sensor->x_mm) ++ sensor->x_mm = sensor->max_x / dpm_resolution; ++ if (!sensor->y_mm) ++ sensor->y_mm = sensor->max_y / dpm_resolution; + } else { + if (rmi_register_desc_has_subpacket(item, 3)) { + rx_receivers = buf[offset]; +@@ -184,8 +190,10 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12) + if (rmi_register_desc_has_subpacket(item, 4)) + offset += 1; + +- sensor->x_mm = (pitch_x * rx_receivers) >> 12; +- sensor->y_mm = (pitch_y * tx_receivers) >> 12; ++ if (!sensor->x_mm) ++ sensor->x_mm = (pitch_x * rx_receivers) >> 12; ++ if (!sensor->y_mm) ++ sensor->y_mm = (pitch_y * tx_receivers) >> 12; + } + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: x_mm: %d y_mm: %d\n", __func__, +diff --git a/drivers/input/rmi4/rmi_smbus.c b/drivers/input/rmi4/rmi_smbus.c +--- a/drivers/input/rmi4/rmi_smbus.c ++++ b/drivers/input/rmi4/rmi_smbus.c +@@ -177,13 +177,8 @@ static int smb_block_read(struct rmi_transport_dev *xport, + struct rmi_smb_xport *rmi_smb = + container_of(xport, struct rmi_smb_xport, xport); + struct i2c_client *client = rmi_smb->client; +- int retval; + +- retval = i2c_smbus_read_block_data(client, commandcode, buf); +- if (retval < 0) +- return retval; +- +- return retval; ++ return i2c_smbus_read_block_data(client, commandcode, buf); + } + + static int rmi_smb_read_block(struct rmi_transport_dev *xport, u16 rmiaddr, +diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig +--- a/drivers/input/serio/Kconfig ++++ b/drivers/input/serio/Kconfig +@@ -106,6 +106,33 @@ config SERIO_GSCPS2 + To compile this driver as a module, choose M here: the + module will be called gscps2. + ++config SERIO_GSCPS2_RDI_KEYCODES ++ bool "Use PrecisionBook keyboard scancodes" ++ depends on SERIO_GSCPS2 ++ default n ++ help ++ If you have an RDI PrecisionBook, say Y here if you want to use its ++ built-in keyboard (as opposed to an external keyboard). ++ ++ The PrecisionBook has five keys that conflict with those used by most ++ AT and PS/2 keyboards. These are as follows: ++ ++ PrecisionBook Standard AT or PS/2 ++ ++ F1 F12 ++ Left Ctrl Left Alt ++ Caps Lock Left Ctrl ++ Right Ctrl Caps Lock ++ Left 102nd key (the key to the right of Left Shift) ++ ++ If you say N here, and use the PrecisionBook keyboard, then each key ++ in the left-hand column will be interpreted as the corresponding key ++ in the right-hand column. ++ ++ If you say Y here, and use an external keyboard, then each key in the ++ right-hand column will be interpreted as the key shown in the ++ left-hand column. ++ + config HP_SDC + tristate "HP System Device Controller i8042 Support" + depends on (GSC || HP300) && SERIO +diff --git a/drivers/input/serio/gscps2.c b/drivers/input/serio/gscps2.c +--- a/drivers/input/serio/gscps2.c ++++ b/drivers/input/serio/gscps2.c +@@ -22,18 +22,15 @@ + * was usable/enabled ?) + */ + +-#include +-#include +-#include +-#include ++#include + #include + #include +-#include +-#include ++#include + #include ++#include ++#include + + #include +-#include + #include + + MODULE_AUTHOR("Laurent Canet , Thibaut Varene , Helge Deller "); +@@ -80,6 +77,116 @@ MODULE_LICENSE("GPL"); + #define GSC_ID_KEYBOARD 0 /* device ID values */ + #define GSC_ID_MOUSE 1 + ++#ifndef CONFIG_SERIO_GSCPS2_RDI_KEYCODES ++# define CONFLICT(x, y) x ++#else ++# define CONFLICT(x, y) y ++#endif ++ ++/* ++ * Sadly RDI (Tadpole) decided to ship a different keyboard layout ++ * than HP for their PS/2 laptop keyboard which leads to conflicting ++ * keycodes between a normal HP PS/2 keyboard and a RDI PrecisionBook. ++ * HP: RDI: ++ */ ++#define C_07 CONFLICT(KEY_F12, KEY_F1) ++#define C_11 CONFLICT(KEY_LEFTALT, KEY_LEFTCTRL) ++#define C_14 CONFLICT(KEY_LEFTCTRL, KEY_CAPSLOCK) ++#define C_58 CONFLICT(KEY_CAPSLOCK, KEY_RIGHTCTRL) ++#define C_61 CONFLICT(KEY_102ND, KEY_LEFT) ++ ++/* ++ * Special keycode value recognized by atkbd (ATKBD_KEY_NULL) to silently ++ * discard scancodes without generating input events or "unknown key" warnings. ++ */ ++#define KEY_NULL 255 ++ ++#define KEYMAP_ENTRY(scancode, keycode) (((scancode) << 16) | (keycode)) ++ ++static const u32 gscps2_keymap[] = { ++ KEYMAP_ENTRY(0x01, KEY_F9), KEYMAP_ENTRY(0x03, KEY_F5), ++ KEYMAP_ENTRY(0x04, KEY_F3), KEYMAP_ENTRY(0x05, KEY_F1), ++ KEYMAP_ENTRY(0x06, KEY_F2), KEYMAP_ENTRY(0x07, C_07), ++ KEYMAP_ENTRY(0x08, KEY_ESC), KEYMAP_ENTRY(0x09, KEY_F10), ++ KEYMAP_ENTRY(0x0a, KEY_F8), KEYMAP_ENTRY(0x0b, KEY_F6), ++ KEYMAP_ENTRY(0x0c, KEY_F4), KEYMAP_ENTRY(0x0d, KEY_TAB), ++ KEYMAP_ENTRY(0x0e, KEY_GRAVE), KEYMAP_ENTRY(0x0f, KEY_F2), ++ KEYMAP_ENTRY(0x11, C_11), KEYMAP_ENTRY(0x12, KEY_LEFTSHIFT), ++ KEYMAP_ENTRY(0x14, C_14), KEYMAP_ENTRY(0x15, KEY_Q), ++ KEYMAP_ENTRY(0x16, KEY_1), KEYMAP_ENTRY(0x17, KEY_F3), ++ KEYMAP_ENTRY(0x19, KEY_LEFTALT), KEYMAP_ENTRY(0x1a, KEY_Z), ++ KEYMAP_ENTRY(0x1b, KEY_S), KEYMAP_ENTRY(0x1c, KEY_A), ++ KEYMAP_ENTRY(0x1d, KEY_W), KEYMAP_ENTRY(0x1e, KEY_2), ++ KEYMAP_ENTRY(0x1f, KEY_F4), KEYMAP_ENTRY(0x21, KEY_C), ++ KEYMAP_ENTRY(0x22, KEY_X), KEYMAP_ENTRY(0x23, KEY_D), ++ KEYMAP_ENTRY(0x24, KEY_E), KEYMAP_ENTRY(0x25, KEY_4), ++ KEYMAP_ENTRY(0x26, KEY_3), KEYMAP_ENTRY(0x27, KEY_F5), ++ KEYMAP_ENTRY(0x29, KEY_SPACE), KEYMAP_ENTRY(0x2a, KEY_V), ++ KEYMAP_ENTRY(0x2b, KEY_F), KEYMAP_ENTRY(0x2c, KEY_T), ++ KEYMAP_ENTRY(0x2d, KEY_R), KEYMAP_ENTRY(0x2e, KEY_5), ++ KEYMAP_ENTRY(0x2f, KEY_F6), KEYMAP_ENTRY(0x31, KEY_N), ++ KEYMAP_ENTRY(0x32, KEY_B), KEYMAP_ENTRY(0x33, KEY_H), ++ KEYMAP_ENTRY(0x34, KEY_G), KEYMAP_ENTRY(0x35, KEY_Y), ++ KEYMAP_ENTRY(0x36, KEY_6), KEYMAP_ENTRY(0x37, KEY_F7), ++ KEYMAP_ENTRY(0x39, KEY_RIGHTALT), KEYMAP_ENTRY(0x3a, KEY_M), ++ KEYMAP_ENTRY(0x3b, KEY_J), KEYMAP_ENTRY(0x3c, KEY_U), ++ KEYMAP_ENTRY(0x3d, KEY_7), KEYMAP_ENTRY(0x3e, KEY_8), ++ KEYMAP_ENTRY(0x3f, KEY_F8), KEYMAP_ENTRY(0x41, KEY_COMMA), ++ KEYMAP_ENTRY(0x42, KEY_K), KEYMAP_ENTRY(0x43, KEY_I), ++ KEYMAP_ENTRY(0x44, KEY_O), KEYMAP_ENTRY(0x45, KEY_0), ++ KEYMAP_ENTRY(0x46, KEY_9), KEYMAP_ENTRY(0x47, KEY_F9), ++ KEYMAP_ENTRY(0x49, KEY_DOT), KEYMAP_ENTRY(0x4a, KEY_SLASH), ++ KEYMAP_ENTRY(0x4b, KEY_L), KEYMAP_ENTRY(0x4c, KEY_SEMICOLON), ++ KEYMAP_ENTRY(0x4d, KEY_P), KEYMAP_ENTRY(0x4e, KEY_MINUS), ++ KEYMAP_ENTRY(0x4f, KEY_F10), KEYMAP_ENTRY(0x52, KEY_APOSTROPHE), ++ KEYMAP_ENTRY(0x54, KEY_LEFTBRACE), KEYMAP_ENTRY(0x55, KEY_EQUAL), ++ KEYMAP_ENTRY(0x56, KEY_F11), KEYMAP_ENTRY(0x57, KEY_SYSRQ), ++ KEYMAP_ENTRY(0x58, C_58), KEYMAP_ENTRY(0x59, KEY_RIGHTSHIFT), ++ KEYMAP_ENTRY(0x5a, KEY_ENTER), KEYMAP_ENTRY(0x5b, KEY_RIGHTBRACE), ++ KEYMAP_ENTRY(0x5c, KEY_BACKSLASH), KEYMAP_ENTRY(0x5d, KEY_BACKSLASH), ++ KEYMAP_ENTRY(0x5e, KEY_F12), KEYMAP_ENTRY(0x5f, KEY_SCROLLLOCK), ++ KEYMAP_ENTRY(0x60, KEY_DOWN), KEYMAP_ENTRY(0x61, C_61), ++ KEYMAP_ENTRY(0x62, KEY_PAUSE), KEYMAP_ENTRY(0x63, KEY_UP), ++ KEYMAP_ENTRY(0x64, KEY_DELETE), KEYMAP_ENTRY(0x65, KEY_END), ++ KEYMAP_ENTRY(0x66, KEY_BACKSPACE), KEYMAP_ENTRY(0x67, KEY_INSERT), ++ KEYMAP_ENTRY(0x69, KEY_KP1), KEYMAP_ENTRY(0x6a, KEY_RIGHT), ++ KEYMAP_ENTRY(0x6b, KEY_KP4), KEYMAP_ENTRY(0x6c, KEY_KP7), ++ KEYMAP_ENTRY(0x6d, KEY_PAGEDOWN), KEYMAP_ENTRY(0x6e, KEY_HOME), ++ KEYMAP_ENTRY(0x6f, KEY_PAGEUP), KEYMAP_ENTRY(0x70, KEY_KP0), ++ KEYMAP_ENTRY(0x71, KEY_KPDOT), KEYMAP_ENTRY(0x72, KEY_KP2), ++ KEYMAP_ENTRY(0x73, KEY_KP5), KEYMAP_ENTRY(0x74, KEY_KP6), ++ KEYMAP_ENTRY(0x75, KEY_KP8), KEYMAP_ENTRY(0x76, KEY_ESC), ++ KEYMAP_ENTRY(0x77, KEY_NUMLOCK), KEYMAP_ENTRY(0x78, KEY_F11), ++ KEYMAP_ENTRY(0x79, KEY_KPPLUS), KEYMAP_ENTRY(0x7a, KEY_KP3), ++ KEYMAP_ENTRY(0x7b, KEY_KPMINUS), KEYMAP_ENTRY(0x7c, KEY_KPASTERISK), ++ KEYMAP_ENTRY(0x7d, KEY_KP9), KEYMAP_ENTRY(0x7e, KEY_SCROLLLOCK), ++ KEYMAP_ENTRY(0x7f, KEY_102ND), KEYMAP_ENTRY(0x91, KEY_RIGHTALT), ++ KEYMAP_ENTRY(0x92, KEY_NULL), KEYMAP_ENTRY(0x94, KEY_RIGHTCTRL), ++ KEYMAP_ENTRY(0x9d, KEY_CAPSLOCK), KEYMAP_ENTRY(0x9f, KEY_LEFTMETA), ++ KEYMAP_ENTRY(0xa7, KEY_RIGHTMETA), KEYMAP_ENTRY(0xaf, KEY_COMPOSE), ++ KEYMAP_ENTRY(0xca, KEY_KPSLASH), KEYMAP_ENTRY(0xda, KEY_KPENTER), ++ KEYMAP_ENTRY(0xe9, KEY_END), KEYMAP_ENTRY(0xeb, KEY_LEFT), ++ KEYMAP_ENTRY(0xec, KEY_HOME), KEYMAP_ENTRY(0xf0, KEY_INSERT), ++ KEYMAP_ENTRY(0xf1, KEY_DELETE), KEYMAP_ENTRY(0xf2, KEY_DOWN), ++ KEYMAP_ENTRY(0xf4, KEY_RIGHT), KEYMAP_ENTRY(0xf5, KEY_UP), ++ KEYMAP_ENTRY(0xf7, KEY_PAUSE), KEYMAP_ENTRY(0xfa, KEY_PAGEDOWN), ++ KEYMAP_ENTRY(0xfc, KEY_SYSRQ), KEYMAP_ENTRY(0xfd, KEY_PAGEUP), ++ ++ /* Escaped keycodes */ ++ KEYMAP_ENTRY(0x103, KEY_F7), KEYMAP_ENTRY(0x10b, KEY_LEFTMETA), ++ KEYMAP_ENTRY(0x10c, KEY_RIGHTMETA), KEYMAP_ENTRY(0x111, KEY_RIGHTALT), ++ KEYMAP_ENTRY(0x114, KEY_RIGHTCTRL), ++}; ++ ++static const struct property_entry gscps2_props[] = { ++ PROPERTY_ENTRY_U32_ARRAY("linux,keymap", gscps2_keymap), ++ { } ++}; ++ ++static const struct software_node gscps2_keyboard_node = { ++ .name = "gscps2-keyboard", ++ .properties = gscps2_props, ++}; + + static irqreturn_t gscps2_interrupt(int irq, void *dev); + +@@ -398,6 +505,17 @@ static int __init gscps2_probe(struct parisc_device *dev) + goto fail; + #endif + ++ if (ps2port->id == GSC_ID_KEYBOARD) { ++ ret = device_add_software_node(&serio->dev, ++ &gscps2_keyboard_node); ++ if (ret) { ++ dev_err(&dev->dev, ++ "failed to add software node for keyboard: %d\n", ++ ret); ++ goto fail; ++ } ++ } ++ + pr_info("serio: %s port at 0x%08lx irq %d @ %s\n", + ps2port->port->name, + hpa, +@@ -411,11 +529,16 @@ static int __init gscps2_probe(struct parisc_device *dev) + return 0; + + fail: ++ if (ps2port->id == GSC_ID_KEYBOARD) ++ device_remove_software_node(&serio->dev); ++ + free_irq(dev->irq, ps2port); + + fail_miserably: + iounmap(ps2port->addr); ++#if 0 + release_mem_region(dev->hpa.start, GSC_STATUS + 4); ++#endif + + fail_nomem: + kfree(ps2port); +@@ -434,6 +557,9 @@ static void __exit gscps2_remove(struct parisc_device *dev) + { + struct gscps2port *ps2port = dev_get_drvdata(&dev->dev); + ++ if (ps2port->id == GSC_ID_KEYBOARD) ++ device_remove_software_node(&ps2port->port->dev); ++ + serio_unregister_port(ps2port->port); + free_irq(dev->irq, ps2port); + gscps2_flush(ps2port); +@@ -465,16 +591,25 @@ static struct parisc_driver parisc_ps2_driver __refdata = { + + static int __init gscps2_init(void) + { +- register_parisc_driver(&parisc_ps2_driver); +- return 0; ++ int error; ++ ++ error = software_node_register(&gscps2_keyboard_node); ++ if (error) ++ return error; ++ ++ error = register_parisc_driver(&parisc_ps2_driver); ++ if (error) ++ software_node_unregister(&gscps2_keyboard_node); ++ ++ return error; + } + + static void __exit gscps2_exit(void) + { + unregister_parisc_driver(&parisc_ps2_driver); ++ software_node_unregister(&gscps2_keyboard_node); + } + + + module_init(gscps2_init); + module_exit(gscps2_exit); +- +diff --git a/drivers/input/serio/i8042-acpipnpio.h b/drivers/input/serio/i8042-acpipnpio.h +--- a/drivers/input/serio/i8042-acpipnpio.h ++++ b/drivers/input/serio/i8042-acpipnpio.h +@@ -3,6 +3,7 @@ + #define _I8042_ACPIPNPIO_H + + #include ++#include + + #ifdef CONFIG_X86 + #include +@@ -1486,17 +1487,21 @@ static char i8042_pnp_aux_name[32]; + + static void i8042_pnp_id_to_string(struct pnp_id *id, char *dst, int dst_size) + { +- strscpy(dst, "PNP:", dst_size); ++ struct seq_buf sb; ++ ++ seq_buf_init(&sb, dst, dst_size); ++ seq_buf_printf(&sb, "PNP:"); + + while (id) { +- strlcat(dst, " ", dst_size); +- strlcat(dst, id->id, dst_size); ++ seq_buf_printf(&sb, " %s", id->id); + id = id->next; + } + } + + static int i8042_pnp_kbd_probe(struct pnp_dev *dev, const struct pnp_device_id *did) + { ++ const char *name = pnp_dev_name(dev); ++ + if (pnp_port_valid(dev, 0) && pnp_port_len(dev, 0) == 1) + i8042_pnp_data_reg = pnp_port_start(dev,0); + +@@ -1506,11 +1511,8 @@ static int i8042_pnp_kbd_probe(struct pnp_dev *dev, const struct pnp_device_id * + if (pnp_irq_valid(dev,0)) + i8042_pnp_kbd_irq = pnp_irq(dev, 0); + +- strscpy(i8042_pnp_kbd_name, did->id, sizeof(i8042_pnp_kbd_name)); +- if (strlen(pnp_dev_name(dev))) { +- strlcat(i8042_pnp_kbd_name, ":", sizeof(i8042_pnp_kbd_name)); +- strlcat(i8042_pnp_kbd_name, pnp_dev_name(dev), sizeof(i8042_pnp_kbd_name)); +- } ++ scnprintf(i8042_pnp_kbd_name, sizeof(i8042_pnp_kbd_name), "%s%s%s", ++ did->id, strlen(name) ? ":" : "", name); + i8042_pnp_id_to_string(dev->id, i8042_kbd_firmware_id, + sizeof(i8042_kbd_firmware_id)); + i8042_kbd_fwnode = dev_fwnode(&dev->dev); +@@ -1524,6 +1526,8 @@ static int i8042_pnp_kbd_probe(struct pnp_dev *dev, const struct pnp_device_id * + + static int i8042_pnp_aux_probe(struct pnp_dev *dev, const struct pnp_device_id *did) + { ++ const char *name = pnp_dev_name(dev); ++ + if (pnp_port_valid(dev, 0) && pnp_port_len(dev, 0) == 1) + i8042_pnp_data_reg = pnp_port_start(dev,0); + +@@ -1533,11 +1537,8 @@ static int i8042_pnp_aux_probe(struct pnp_dev *dev, const struct pnp_device_id * + if (pnp_irq_valid(dev, 0)) + i8042_pnp_aux_irq = pnp_irq(dev, 0); + +- strscpy(i8042_pnp_aux_name, did->id, sizeof(i8042_pnp_aux_name)); +- if (strlen(pnp_dev_name(dev))) { +- strlcat(i8042_pnp_aux_name, ":", sizeof(i8042_pnp_aux_name)); +- strlcat(i8042_pnp_aux_name, pnp_dev_name(dev), sizeof(i8042_pnp_aux_name)); +- } ++ scnprintf(i8042_pnp_aux_name, sizeof(i8042_pnp_aux_name), "%s%s%s", ++ did->id, strlen(name) ? ":" : "", name); + i8042_pnp_id_to_string(dev->id, i8042_aux_firmware_id, + sizeof(i8042_aux_firmware_id)); + +diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c +--- a/drivers/input/touchscreen/elo.c ++++ b/drivers/input/touchscreen/elo.c +@@ -257,7 +257,7 @@ static int elo_command_10(struct elo *elo, unsigned char *packet) + + static int elo_setup_10(struct elo *elo) + { +- static const char *elo_types[] = { "Accu", "Dura", "Intelli", "Carroll" }; ++ static const char * const elo_types[] = { "Accu", "Dura", "Intelli", "Carroll" }; + struct input_dev *dev = elo->dev; + unsigned char packet[ELO10_PACKET_LEN] = { ELO10_ID_CMD }; + +@@ -273,7 +273,7 @@ static int elo_setup_10(struct elo *elo) + + dev_info(&elo->serio->dev, + "%sTouch touchscreen, fw: %02x.%02x, features: 0x%02x, controller: 0x%02x\n", +- elo_types[(packet[1] -'0') & 0x03], ++ elo_types[(packet[1] - '0') & 0x03], + packet[5], packet[4], packet[3], packet[7]); + + return 0; +diff --git a/drivers/input/touchscreen/inexio.c b/drivers/input/touchscreen/inexio.c +--- a/drivers/input/touchscreen/inexio.c ++++ b/drivers/input/touchscreen/inexio.c +@@ -81,7 +81,9 @@ static irqreturn_t inexio_interrupt(struct serio *serio, + if (INEXIO_RESPONSE_BEGIN_BYTE&pinexio->data[0]) + inexio_process_data(pinexio); + else +- printk(KERN_DEBUG "inexio.c: unknown/unsynchronized data from device, byte %x\n",pinexio->data[0]); ++ dev_dbg(&serio->dev, ++ "unknown/unsynchronized data from device, byte %x\n", ++ pinexio->data[0]); + + return IRQ_HANDLED; + } +diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c +--- a/drivers/input/touchscreen/iqs5xx.c ++++ b/drivers/input/touchscreen/iqs5xx.c +@@ -785,7 +785,8 @@ static int iqs5xx_fw_file_parse(struct i2c_client *client, + switch (rec_type) { + case IQS5XX_REC_TYPE_DATA: + if (rec_addr < IQS5XX_CHKSM || +- rec_addr > IQS5XX_PMAP_END) { ++ rec_addr > IQS5XX_PMAP_END || ++ rec_len > IQS5XX_PMAP_END + 1 - rec_addr) { + dev_err(&client->dev, + "Invalid address at record %u\n", + rec_num); +diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c +--- a/drivers/input/touchscreen/mms114.c ++++ b/drivers/input/touchscreen/mms114.c +@@ -4,6 +4,8 @@ + // Copyright (c) 2012 Samsung Electronics Co., Ltd. + // Author: Joonyoung Shim + ++#include ++#include + #include + #include + #include +@@ -52,21 +54,13 @@ + #define MMS114_TYPE_TOUCHSCREEN 1 + #define MMS114_TYPE_TOUCHKEY 2 + +-enum mms_type { +- TYPE_MMS114 = 114, +- TYPE_MMS134S = 134, +- TYPE_MMS136 = 136, +- TYPE_MMS152 = 152, +- TYPE_MMS345L = 345, +-}; +- + struct mms114_data { ++ const struct mms_chip *chip; + struct i2c_client *client; + struct input_dev *input_dev; + struct regulator *core_reg; + struct regulator *io_reg; + struct touchscreen_properties props; +- enum mms_type type; + unsigned int contact_threshold; + unsigned int moving_threshold; + +@@ -77,9 +71,23 @@ struct mms114_data { + u8 cache_mode_control; + }; + ++struct mms_chip { ++ const char *name; ++ int event_size; ++ bool has_config_regs; ++ int (*get_version)(struct mms114_data *data); ++}; ++ ++#define MMS114_FLAGS_ID_MASK GENMASK(3, 0) ++#define MMS114_FLAGS_TYPE_MASK GENMASK(6, 5) ++#define MMS114_FLAGS_PRESSED_MASK BIT(7) ++ ++#define MMS114_XY_HI_X_MASK GENMASK(3, 0) ++#define MMS114_XY_HI_Y_MASK GENMASK(7, 4) ++ + struct mms114_touch { +- u8 id:4, reserved_bit4:1, type:2, pressed:1; +- u8 x_hi:4, y_hi:4; ++ u8 flags; ++ u8 xy_hi; + u8 x_lo; + u8 y_lo; + u8 width; +@@ -87,16 +95,16 @@ struct mms114_touch { + u8 reserved[2]; + } __packed; + +-static int __mms114_read_reg(struct mms114_data *data, unsigned int reg, +- unsigned int len, u8 *val) ++static int __mms114_read_reg(struct mms114_data *data, u8 reg, ++ unsigned int len, void *val) + { + struct i2c_client *client = data->client; + struct i2c_msg xfer[2]; +- u8 buf = reg & 0xff; ++ u8 buf = reg; + int error; + +- if (reg <= MMS114_MODE_CONTROL && reg + len > MMS114_MODE_CONTROL) +- BUG(); ++ if (WARN_ON(reg <= MMS114_MODE_CONTROL && reg + len > MMS114_MODE_CONTROL)) ++ return -EINVAL; + + /* Write register */ + xfer[0].addr = client->addr; +@@ -116,12 +124,12 @@ static int __mms114_read_reg(struct mms114_data *data, unsigned int reg, + "%s: i2c transfer failed (%d)\n", __func__, error); + return error < 0 ? error : -EIO; + } +- udelay(MMS114_I2C_DELAY); ++ usleep_range(MMS114_I2C_DELAY, MMS114_I2C_DELAY + 50); + + return 0; + } + +-static int mms114_read_reg(struct mms114_data *data, unsigned int reg) ++static int mms114_read_reg(struct mms114_data *data, u8 reg) + { + u8 val; + int error; +@@ -133,15 +141,14 @@ static int mms114_read_reg(struct mms114_data *data, unsigned int reg) + return error < 0 ? error : val; + } + +-static int mms114_write_reg(struct mms114_data *data, unsigned int reg, +- unsigned int val) ++static int mms114_write_reg(struct mms114_data *data, u8 reg, u8 val) + { + struct i2c_client *client = data->client; + u8 buf[2]; + int error; + +- buf[0] = reg & 0xff; +- buf[1] = val & 0xff; ++ buf[0] = reg; ++ buf[1] = val; + + error = i2c_master_send(client, buf, 2); + if (error != 2) { +@@ -149,7 +156,7 @@ static int mms114_write_reg(struct mms114_data *data, unsigned int reg, + "%s: i2c send failed (%d)\n", __func__, error); + return error < 0 ? error : -EIO; + } +- udelay(MMS114_I2C_DELAY); ++ usleep_range(MMS114_I2C_DELAY, MMS114_I2C_DELAY + 50); + + if (reg == MMS114_MODE_CONTROL) + data->cache_mode_control = val; +@@ -157,32 +164,119 @@ static int mms114_write_reg(struct mms114_data *data, unsigned int reg, + return 0; + } + ++static int mms114_get_version(struct mms114_data *data) ++{ ++ struct device *dev = &data->client->dev; ++ u8 buf[6]; ++ int error; ++ ++ error = __mms114_read_reg(data, MMS114_TSP_REV, 6, buf); ++ if (error) ++ return error; ++ ++ dev_info(dev, "TSP Rev: 0x%x, HW Rev: 0x%x, Firmware Ver: 0x%x\n", ++ buf[0], buf[1], buf[3]); ++ return 0; ++} ++ ++static int mms152_get_version(struct mms114_data *data) ++{ ++ struct device *dev = &data->client->dev; ++ u8 buf[3]; ++ int group; ++ int error; ++ ++ error = __mms114_read_reg(data, MMS152_FW_REV, 3, buf); ++ if (error) ++ return error; ++ ++ group = i2c_smbus_read_byte_data(data->client, MMS152_COMPAT_GROUP); ++ if (group < 0) ++ return group; ++ ++ dev_info(dev, "TSP FW Rev: bootloader 0x%x / core 0x%x / config 0x%x, Compat group: %c\n", ++ buf[0], buf[1], buf[2], group); ++ return 0; ++} ++ ++static int mms345l_get_version(struct mms114_data *data) ++{ ++ struct device *dev = &data->client->dev; ++ u8 buf[3]; ++ int error; ++ ++ error = __mms114_read_reg(data, MMS152_FW_REV, 3, buf); ++ if (error) ++ return error; ++ ++ dev_info(dev, "TSP FW Rev: bootloader 0x%x / core 0x%x / config 0x%x\n", ++ buf[0], buf[1], buf[2]); ++ return 0; ++} ++ ++static const struct mms_chip mms114_descriptor = { ++ .name = "MMS114", ++ .event_size = MMS114_EVENT_SIZE, ++ .has_config_regs = true, ++ .get_version = mms114_get_version, ++}; ++ ++static const struct mms_chip mms134s_descriptor = { ++ .name = "MMS134S", ++ .event_size = MMS136_EVENT_SIZE, ++ .has_config_regs = true, ++ .get_version = mms114_get_version, ++}; ++ ++static const struct mms_chip mms136_descriptor = { ++ .name = "MMS136", ++ .event_size = MMS136_EVENT_SIZE, ++ .has_config_regs = true, ++ .get_version = mms114_get_version, ++}; ++ ++static const struct mms_chip mms152_descriptor = { ++ .name = "MMS152", ++ .event_size = MMS114_EVENT_SIZE, ++ .has_config_regs = false, ++ .get_version = mms152_get_version, ++}; ++ ++static const struct mms_chip mms345l_descriptor = { ++ .name = "MMS345L", ++ .event_size = MMS114_EVENT_SIZE, ++ .has_config_regs = false, ++ .get_version = mms345l_get_version, ++}; ++ + static void mms114_process_mt(struct mms114_data *data, struct mms114_touch *touch) + { + struct i2c_client *client = data->client; + struct input_dev *input_dev = data->input_dev; +- unsigned int id; ++ unsigned int id = FIELD_GET(MMS114_FLAGS_ID_MASK, touch->flags); ++ unsigned int type = FIELD_GET(MMS114_FLAGS_TYPE_MASK, touch->flags); ++ bool pressed = FIELD_GET(MMS114_FLAGS_PRESSED_MASK, touch->flags); + unsigned int x; + unsigned int y; + +- if (touch->id == 0 || touch->id > MMS114_MAX_TOUCH) { +- dev_err(&client->dev, "Wrong touch id (%d)\n", touch->id); ++ if (id == 0 || id > MMS114_MAX_TOUCH) { ++ dev_err(&client->dev, "Wrong touch id (%d)\n", id); + return; + } + +- id = touch->id - 1; +- x = touch->x_lo | touch->x_hi << 8; +- y = touch->y_lo | touch->y_hi << 8; ++ id--; ++ x = touch->x_lo | FIELD_GET(MMS114_XY_HI_X_MASK, touch->xy_hi) << 8; ++ y = touch->y_lo | FIELD_GET(MMS114_XY_HI_Y_MASK, touch->xy_hi) << 8; + + dev_dbg(&client->dev, + "id: %d, type: %d, pressed: %d, x: %d, y: %d, width: %d, strength: %d\n", +- id, touch->type, touch->pressed, ++ id, type, pressed, + x, y, touch->width, touch->strength); + + input_mt_slot(input_dev, id); +- input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, touch->pressed); ++ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, pressed); + +- if (touch->pressed) { ++ if (pressed) { + touchscreen_report_pos(input_dev, &data->props, x, y, true); + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, touch->width); + input_report_abs(input_dev, ABS_MT_PRESSURE, touch->strength); +@@ -195,21 +289,23 @@ static void mms114_process_touchkey(struct mms114_data *data, + struct i2c_client *client = data->client; + struct input_dev *input_dev = data->input_dev; + unsigned int keycode_id; ++ unsigned int id = FIELD_GET(MMS114_FLAGS_ID_MASK, touch->flags); ++ bool pressed = FIELD_GET(MMS114_FLAGS_PRESSED_MASK, touch->flags); + +- if (touch->id == 0) ++ if (id == 0) + return; + +- if (touch->id > data->num_keycodes) { ++ if (id > data->num_keycodes) { + dev_err(&client->dev, "Wrong touch id for touchkey (%d)\n", +- touch->id); ++ id); + return; + } + +- keycode_id = touch->id - 1; ++ keycode_id = id - 1; + dev_dbg(&client->dev, "keycode id: %d, pressed: %d\n", keycode_id, +- touch->pressed); ++ pressed); + +- input_report_key(input_dev, data->keycodes[keycode_id], touch->pressed); ++ input_report_key(input_dev, data->keycodes[keycode_id], pressed); + } + + static irqreturn_t mms114_interrupt(int irq, void *dev_id) +@@ -218,8 +314,8 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id) + struct i2c_client *client = data->client; + struct mms114_touch touch[MMS114_MAX_TOUCH]; + struct mms114_touch *t; ++ int event_size = data->chip->event_size; + int packet_size; +- int event_size; + int touch_size; + int index; + int error; +@@ -234,23 +330,17 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id) + goto out; + } + +- /* MMS136 has slightly different event size */ +- if (data->type == TYPE_MMS134S || data->type == TYPE_MMS136) +- event_size = MMS136_EVENT_SIZE; +- else +- event_size = MMS114_EVENT_SIZE; +- + touch_size = packet_size / event_size; + +- error = __mms114_read_reg(data, MMS114_INFORMATION, packet_size, +- (u8 *)touch); +- if (error < 0) ++ error = __mms114_read_reg(data, MMS114_INFORMATION, packet_size, touch); ++ if (error) + goto out; + + for (index = 0; index < touch_size; index++) { + t = (struct mms114_touch *)((u8 *)touch + index * event_size); ++ unsigned int type = FIELD_GET(MMS114_FLAGS_TYPE_MASK, t->flags); + +- switch (t->type) { ++ switch (type) { + case MMS114_TYPE_TOUCHSCREEN: + mms114_process_mt(data, t); + break; +@@ -261,7 +351,7 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id) + + default: + dev_err(&client->dev, "Wrong touch type (%d)\n", +- t->type); ++ type); + break; + } + } +@@ -290,65 +380,17 @@ static int mms114_set_active(struct mms114_data *data, bool active) + return mms114_write_reg(data, MMS114_MODE_CONTROL, val); + } + +-static int mms114_get_version(struct mms114_data *data) +-{ +- struct device *dev = &data->client->dev; +- u8 buf[6]; +- int group; +- int error; +- +- switch (data->type) { +- case TYPE_MMS345L: +- error = __mms114_read_reg(data, MMS152_FW_REV, 3, buf); +- if (error) +- return error; +- +- dev_info(dev, "TSP FW Rev: bootloader 0x%x / core 0x%x / config 0x%x\n", +- buf[0], buf[1], buf[2]); +- break; +- +- case TYPE_MMS152: +- error = __mms114_read_reg(data, MMS152_FW_REV, 3, buf); +- if (error) +- return error; +- +- group = i2c_smbus_read_byte_data(data->client, +- MMS152_COMPAT_GROUP); +- if (group < 0) +- return group; +- +- dev_info(dev, "TSP FW Rev: bootloader 0x%x / core 0x%x / config 0x%x, Compat group: %c\n", +- buf[0], buf[1], buf[2], group); +- break; +- +- case TYPE_MMS114: +- case TYPE_MMS134S: +- case TYPE_MMS136: +- error = __mms114_read_reg(data, MMS114_TSP_REV, 6, buf); +- if (error) +- return error; +- +- dev_info(dev, "TSP Rev: 0x%x, HW Rev: 0x%x, Firmware Ver: 0x%x\n", +- buf[0], buf[1], buf[3]); +- break; +- } +- +- return 0; +-} +- + static int mms114_setup_regs(struct mms114_data *data) + { + const struct touchscreen_properties *props = &data->props; + int val; + int error; + +- error = mms114_get_version(data); +- if (error < 0) ++ error = data->chip->get_version(data); ++ if (error) + return error; + +- /* MMS114, MMS134S and MMS136 have configuration and power on registers */ +- if (data->type != TYPE_MMS114 && data->type != TYPE_MMS134S && +- data->type != TYPE_MMS136) ++ if (!data->chip->has_config_regs) + return 0; + + error = mms114_set_active(data, true); +@@ -366,21 +408,21 @@ static int mms114_setup_regs(struct mms114_data *data) + if (error < 0) + return error; + +- val = props->max_x & 0xff; ++ val = props->max_y & 0xff; + error = mms114_write_reg(data, MMS114_Y_RESOLUTION, val); + if (error < 0) + return error; + + if (data->contact_threshold) { + error = mms114_write_reg(data, MMS114_CONTACT_THRESHOLD, +- data->contact_threshold); ++ data->contact_threshold); + if (error < 0) + return error; + } + + if (data->moving_threshold) { + error = mms114_write_reg(data, MMS114_MOVING_THRESHOLD, +- data->moving_threshold); ++ data->moving_threshold); + if (error < 0) + return error; + } +@@ -466,9 +508,9 @@ static int mms114_parse_legacy_bindings(struct mms114_data *data) + } + + device_property_read_u32(dev, "contact-threshold", +- &data->contact_threshold); ++ &data->contact_threshold); + device_property_read_u32(dev, "moving-threshold", +- &data->moving_threshold); ++ &data->moving_threshold); + + if (device_property_read_bool(dev, "x-invert")) + props->invert_x = true; +@@ -484,7 +526,6 @@ static int mms114_probe(struct i2c_client *client) + { + struct mms114_data *data; + struct input_dev *input_dev; +- const void *match_data; + int error; + int i; + +@@ -504,12 +545,10 @@ static int mms114_probe(struct i2c_client *client) + data->client = client; + data->input_dev = input_dev; + +- match_data = device_get_match_data(&client->dev); +- if (!match_data) ++ data->chip = i2c_get_match_data(client); ++ if (!data->chip) + return -EINVAL; + +- data->type = (enum mms_type)match_data; +- + data->num_keycodes = device_property_count_u32(&client->dev, + "linux,keycodes"); + if (data->num_keycodes == -EINVAL) { +@@ -521,7 +560,7 @@ static int mms114_probe(struct i2c_client *client) + return data->num_keycodes; + } else if (data->num_keycodes > MMS114_MAX_TOUCHKEYS) { + dev_warn(&client->dev, +- "Found %d linux,keycodes but max is %d, ignoring the rest\n", ++ "Found %d linux,keycodes but max is %d, ignoring the rest\n", + data->num_keycodes, MMS114_MAX_TOUCHKEYS); + data->num_keycodes = MMS114_MAX_TOUCHKEYS; + } +@@ -566,8 +605,7 @@ static int mms114_probe(struct i2c_client *client) + 0, data->props.max_y, 0, 0); + } + +- if (data->type == TYPE_MMS114 || data->type == TYPE_MMS134S || +- data->type == TYPE_MMS136) { ++ if (data->chip->has_config_regs) { + /* + * The firmware handles movement and pressure fuzz, so + * don't duplicate that in software. +@@ -582,8 +620,8 @@ static int mms114_probe(struct i2c_client *client) + } + + input_dev->name = devm_kasprintf(&client->dev, GFP_KERNEL, +- "MELFAS MMS%d Touchscreen", +- data->type); ++ "MELFAS %s Touchscreen", ++ data->chip->name); + if (!input_dev->name) + return -ENOMEM; + +@@ -679,29 +717,22 @@ static int mms114_resume(struct device *dev) + static DEFINE_SIMPLE_DEV_PM_OPS(mms114_pm_ops, mms114_suspend, mms114_resume); + + static const struct i2c_device_id mms114_id[] = { +- { .name = "mms114" }, ++ { .name = "mms114", .driver_data = (kernel_ulong_t)&mms114_descriptor }, ++ { .name = "mms134s", .driver_data = (kernel_ulong_t)&mms134s_descriptor }, ++ { .name = "mms136", .driver_data = (kernel_ulong_t)&mms136_descriptor }, ++ { .name = "mms152", .driver_data = (kernel_ulong_t)&mms152_descriptor }, ++ { .name = "mms345l", .driver_data = (kernel_ulong_t)&mms345l_descriptor }, + { } + }; + MODULE_DEVICE_TABLE(i2c, mms114_id); + + #ifdef CONFIG_OF + static const struct of_device_id mms114_dt_match[] = { +- { +- .compatible = "melfas,mms114", +- .data = (void *)TYPE_MMS114, +- }, { +- .compatible = "melfas,mms134s", +- .data = (void *)TYPE_MMS134S, +- }, { +- .compatible = "melfas,mms136", +- .data = (void *)TYPE_MMS136, +- }, { +- .compatible = "melfas,mms152", +- .data = (void *)TYPE_MMS152, +- }, { +- .compatible = "melfas,mms345l", +- .data = (void *)TYPE_MMS345L, +- }, ++ { .compatible = "melfas,mms114", .data = &mms114_descriptor }, ++ { .compatible = "melfas,mms134s", .data = &mms134s_descriptor }, ++ { .compatible = "melfas,mms136", .data = &mms136_descriptor }, ++ { .compatible = "melfas,mms152", .data = &mms152_descriptor }, ++ { .compatible = "melfas,mms345l", .data = &mms345l_descriptor }, + { } + }; + MODULE_DEVICE_TABLE(of, mms114_dt_match); +@@ -722,4 +753,4 @@ module_i2c_driver(mms114_driver); + /* Module information */ + MODULE_AUTHOR("Joonyoung Shim "); + MODULE_DESCRIPTION("MELFAS mms114 Touchscreen driver"); +-MODULE_LICENSE("GPL v2"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c +--- a/drivers/input/touchscreen/sur40.c ++++ b/drivers/input/touchscreen/sur40.c +@@ -128,8 +128,8 @@ struct sur40_image_header { + /* polling interval (ms) */ + #define POLL_INTERVAL 1 + +-/* maximum number of contacts FIXME: this is a guess? */ +-#define MAX_CONTACTS 64 ++/* maximum number of contacts */ ++#define MAX_CONTACTS 52 + + /* control commands */ + #define SUR40_GET_VERSION 0xb0 /* 12 bytes string */ +diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c +--- a/drivers/input/touchscreen/wacom_w8001.c ++++ b/drivers/input/touchscreen/wacom_w8001.c +@@ -417,6 +417,13 @@ static int w8001_detect(struct w8001 *w8001) + return 0; + } + ++static void w8001_append_suffix(char *dest, const char *suffix, size_t dest_sz) ++{ ++ size_t used = strnlen(dest, dest_sz); ++ ++ strscpy(dest + used, suffix, dest_sz - used); ++} ++ + static int w8001_setup_pen(struct w8001 *w8001, char *basename, + size_t basename_sz) + { +@@ -453,7 +460,7 @@ static int w8001_setup_pen(struct w8001 *w8001, char *basename, + } + + w8001->id = 0x90; +- strlcat(basename, " Penabled", basename_sz); ++ w8001_append_suffix(basename, " Penabled", basename_sz); + + return 0; + } +@@ -503,14 +510,14 @@ static int w8001_setup_touch(struct w8001 *w8001, char *basename, + case 2: + w8001->pktlen = W8001_PKTLEN_TOUCH93; + w8001->id = 0x93; +- strlcat(basename, " 1FG", basename_sz); ++ w8001_append_suffix(basename, " 1FG", basename_sz); + break; + + case 1: + case 3: + case 4: + w8001->pktlen = W8001_PKTLEN_TOUCH9A; +- strlcat(basename, " 1FG", basename_sz); ++ w8001_append_suffix(basename, " 1FG", basename_sz); + w8001->id = 0x9a; + break; + +@@ -534,7 +541,7 @@ static int w8001_setup_touch(struct w8001 *w8001, char *basename, + input_abs_set_res(dev, ABS_MT_POSITION_X, touch.panel_res); + input_abs_set_res(dev, ABS_MT_POSITION_Y, touch.panel_res); + +- strlcat(basename, " 2FG", basename_sz); ++ w8001_append_suffix(basename, " 2FG", basename_sz); + if (w8001->max_pen_x && w8001->max_pen_y) + w8001->id = 0xE3; + else +@@ -542,7 +549,7 @@ static int w8001_setup_touch(struct w8001 *w8001, char *basename, + break; + } + +- strlcat(basename, " Touchscreen", basename_sz); ++ w8001_append_suffix(basename, " Touchscreen", basename_sz); + + return 0; + } +diff --git a/drivers/mfd/rohm-bd71828.c b/drivers/mfd/rohm-bd71828.c +--- a/drivers/mfd/rohm-bd71828.c ++++ b/drivers/mfd/rohm-bd71828.c +@@ -5,7 +5,8 @@ + * ROHM BD718[15/28/79] and BD72720 PMIC driver + */ + +-#include ++#include ++#include + #include + #include + #include +@@ -18,6 +19,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -37,19 +39,6 @@ + }, \ + } + +-static struct gpio_keys_button button = { +- .code = KEY_POWER, +- .gpio = -1, +- .type = EV_KEY, +- .wakeup = 1, +-}; +- +-static const struct gpio_keys_platform_data bd71828_powerkey_data = { +- .buttons = &button, +- .nbuttons = 1, +- .name = "bd71828-pwrkey", +-}; +- + static const struct resource bd71815_rtc_irqs[] = { + DEFINE_RES_IRQ_NAMED(BD71815_INT_RTC0, "bd70528-rtc-alm-0"), + DEFINE_RES_IRQ_NAMED(BD71815_INT_RTC1, "bd70528-rtc-alm-1"), +@@ -174,10 +163,6 @@ static struct mfd_cell bd71828_mfd_cells[] = { + .name = "bd71828-rtc", + .resources = bd71828_rtc_irqs, + .num_resources = ARRAY_SIZE(bd71828_rtc_irqs), +- }, { +- .name = "gpio-keys", +- .platform_data = &bd71828_powerkey_data, +- .pdata_size = sizeof(bd71828_powerkey_data), + }, + }; + +@@ -242,11 +227,8 @@ static const struct mfd_cell bd72720_mfd_cells[] = { + .name = "bd72720-rtc", + .resources = bd72720_rtc_irqs, + .num_resources = ARRAY_SIZE(bd72720_rtc_irqs), +- }, { +- .name = "gpio-keys", +- .platform_data = &bd71828_powerkey_data, +- .pdata_size = sizeof(bd71828_powerkey_data), + }, ++ /* Power button is registered separately */ + }; + + static const struct regmap_range bd71815_volatile_ranges[] = { +@@ -877,6 +859,84 @@ static int set_clk_mode(struct device *dev, struct regmap *regmap, + OUT32K_MODE_CMOS); + } + ++static const struct property_entry bd71828_powerkey_parent_props[] = { ++ PROPERTY_ENTRY_STRING("label", "bd71828-pwrkey"), ++ { } ++}; ++ ++static const struct property_entry bd71828_powerkey_props[] = { ++ PROPERTY_ENTRY_U32("linux,code", KEY_POWER), ++ PROPERTY_ENTRY_BOOL("wakeup-source"), ++ { } ++}; ++ ++#define GPIO_KEYS 0 /* Node corresponding to gpio-keys device itself */ ++#define PWRON_KEY 1 /* Node describing power button in gpio-keys */ ++ ++static int bd71828_i2c_register_swnodes(const struct software_node *nodes) ++{ ++ const struct software_node * const node_group[] = { ++ &nodes[GPIO_KEYS], &nodes[PWRON_KEY], NULL ++ }; ++ ++ return software_node_register_node_group(node_group); ++} ++ ++static void bd71828_i2c_unregister_swnodes(void *data) ++{ ++ const struct software_node *nodes = data; ++ const struct software_node * const node_group[] = { ++ &nodes[GPIO_KEYS], &nodes[PWRON_KEY], NULL ++ }; ++ ++ software_node_unregister_node_group(node_group); ++} ++ ++static int bd71828_i2c_register_pwrbutton(struct device *dev, int button_irq, ++ struct irq_domain *irq_domain) ++{ ++ const struct resource res[] = { ++ DEFINE_RES_IRQ_NAMED(button_irq, "bd71828-pwrkey"), ++ }; ++ struct mfd_cell gpio_keys_cell = { ++ .name = "gpio-keys", ++ .resources = res, ++ .num_resources = ARRAY_SIZE(res), ++ }; ++ struct software_node *nodes; ++ int ret; ++ ++ nodes = devm_kcalloc(dev, 2, sizeof(*nodes), GFP_KERNEL); ++ if (!nodes) ++ return -ENOMEM; ++ ++ nodes[GPIO_KEYS].name = devm_kasprintf(dev, GFP_KERNEL, "%s-power-key", dev_name(dev)); ++ if (!nodes[GPIO_KEYS].name) ++ return -ENOMEM; ++ ++ nodes[GPIO_KEYS].properties = bd71828_powerkey_parent_props; ++ ++ nodes[PWRON_KEY].parent = &nodes[GPIO_KEYS]; ++ nodes[PWRON_KEY].properties = bd71828_powerkey_props; ++ ++ ret = bd71828_i2c_register_swnodes(nodes); ++ if (ret) ++ return ret; ++ ++ ret = devm_add_action_or_reset(dev, bd71828_i2c_unregister_swnodes, nodes); ++ if (ret) ++ return ret; ++ ++ gpio_keys_cell.swnode = &nodes[GPIO_KEYS]; ++ ++ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, &gpio_keys_cell, 1, ++ NULL, 0, irq_domain); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to register power-button"); ++ ++ return 0; ++} ++ + static struct i2c_client *bd71828_dev; + static void bd71828_power_off(void) + { +@@ -929,6 +989,7 @@ static struct regmap *bd72720_do_regmaps(struct i2c_client *i2c) + static int bd71828_i2c_probe(struct i2c_client *i2c) + { + struct regmap_irq_chip_data *irq_data; ++ struct irq_domain *irq_domain; + int ret; + struct regmap *regmap = NULL; + const struct regmap_config *regmap_config; +@@ -1022,23 +1083,23 @@ static int bd71828_i2c_probe(struct i2c_client *i2c) + "Failed to enable main level IRQs\n"); + } + } +- if (button_irq) { +- ret = regmap_irq_get_virq(irq_data, button_irq); +- if (ret < 0) +- return dev_err_probe(&i2c->dev, ret, +- "Failed to get the power-key IRQ\n"); +- +- button.irq = ret; +- } + + ret = set_clk_mode(&i2c->dev, regmap, clkmode_reg); + if (ret) + return ret; + ++ irq_domain = regmap_irq_get_domain(irq_data); ++ + ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO, mfd, cells, +- NULL, 0, regmap_irq_get_domain(irq_data)); ++ NULL, 0, irq_domain); + if (ret) +- return dev_err_probe(&i2c->dev, ret, "Failed to create subdevices\n"); ++ return dev_err_probe(&i2c->dev, ret, "Failed to create subdevices\n"); ++ ++ if (button_irq) { ++ ret = bd71828_i2c_register_pwrbutton(&i2c->dev, button_irq, irq_domain); ++ if (ret) ++ return ret; ++ } + + if (of_device_is_system_power_controller(i2c->dev.of_node) && + chip_type == ROHM_CHIP_TYPE_BD71828) { +diff --git a/drivers/mfd/rohm-bd718x7.c b/drivers/mfd/rohm-bd718x7.c +--- a/drivers/mfd/rohm-bd718x7.c ++++ b/drivers/mfd/rohm-bd718x7.c +@@ -7,7 +7,8 @@ + // Datasheet for BD71837MWV available from + // https://www.rohm.com/datasheet/BD71837MWV/bd71837mwv-e + +-#include ++#include ++#include + #include + #include + #include +@@ -15,37 +16,16 @@ + #include + #include + #include ++#include + #include + #include + +-static struct gpio_keys_button button = { +- .code = KEY_POWER, +- .gpio = -1, +- .type = EV_KEY, +-}; +- +-static struct gpio_keys_platform_data bd718xx_powerkey_data = { +- .buttons = &button, +- .nbuttons = 1, +- .name = "bd718xx-pwrkey", +-}; +- + static struct mfd_cell bd71837_mfd_cells[] = { +- { +- .name = "gpio-keys", +- .platform_data = &bd718xx_powerkey_data, +- .pdata_size = sizeof(bd718xx_powerkey_data), +- }, + { .name = "bd71837-clk", }, + { .name = "bd71837-pmic", }, + }; + + static struct mfd_cell bd71847_mfd_cells[] = { +- { +- .name = "gpio-keys", +- .platform_data = &bd718xx_powerkey_data, +- .pdata_size = sizeof(bd718xx_powerkey_data), +- }, + { .name = "bd71847-clk", }, + { .name = "bd71847-pmic", }, + }; +@@ -125,10 +105,89 @@ static int bd718xx_init_press_duration(struct regmap *regmap, + return 0; + } + ++static const struct property_entry bd718xx_powerkey_parent_props[] = { ++ PROPERTY_ENTRY_STRING("label", "bd718xx-pwrkey"), ++ { } ++}; ++ ++static const struct property_entry bd718xx_powerkey_props[] = { ++ PROPERTY_ENTRY_U32("linux,code", KEY_POWER), ++ { } ++}; ++ ++static const struct resource bd718xx_powerkey_resources[] = { ++ DEFINE_RES_IRQ_NAMED(BD718XX_INT_PWRBTN_S, "bd718xx-pwrkey"), ++}; ++ ++#define GPIO_KEYS 0 /* Node corresponding to gpio-keys device itself */ ++#define PWRON_KEY 1 /* Node describing power button in gpio-keys */ ++ ++static int bd718xx_i2c_register_swnodes(const struct software_node *nodes) ++{ ++ const struct software_node * const node_group[] = { ++ &nodes[GPIO_KEYS], &nodes[PWRON_KEY], NULL ++ }; ++ ++ return software_node_register_node_group(node_group); ++} ++ ++static void bd718xx_i2c_unregister_swnodes(void *data) ++{ ++ const struct software_node *nodes = data; ++ const struct software_node * const node_group[] = { ++ &nodes[GPIO_KEYS], &nodes[PWRON_KEY], NULL ++ }; ++ ++ software_node_unregister_node_group(node_group); ++} ++ ++static int bd718xx_i2c_register_pwrbutton(struct device *dev, ++ struct irq_domain *irq_domain) ++{ ++ struct mfd_cell gpio_keys_cell = { ++ .name = "gpio-keys", ++ .resources = bd718xx_powerkey_resources, ++ .num_resources = ARRAY_SIZE(bd718xx_powerkey_resources), ++ }; ++ struct software_node *nodes; ++ int ret; ++ ++ nodes = devm_kcalloc(dev, 2, sizeof(*nodes), GFP_KERNEL); ++ if (!nodes) ++ return -ENOMEM; ++ ++ nodes[GPIO_KEYS].name = devm_kasprintf(dev, GFP_KERNEL, "%s-power-key", dev_name(dev)); ++ if (!nodes[GPIO_KEYS].name) ++ return -ENOMEM; ++ ++ nodes[GPIO_KEYS].properties = bd718xx_powerkey_parent_props; ++ ++ nodes[PWRON_KEY].parent = &nodes[GPIO_KEYS]; ++ nodes[PWRON_KEY].properties = bd718xx_powerkey_props; ++ ++ ret = bd718xx_i2c_register_swnodes(nodes); ++ if (ret) ++ return ret; ++ ++ ret = devm_add_action_or_reset(dev, bd718xx_i2c_unregister_swnodes, nodes); ++ if (ret) ++ return ret; ++ ++ gpio_keys_cell.swnode = &nodes[GPIO_KEYS]; ++ ++ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, &gpio_keys_cell, 1, ++ NULL, 0, irq_domain); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to register power-button"); ++ ++ return 0; ++} ++ + static int bd718xx_i2c_probe(struct i2c_client *i2c) + { + struct regmap *regmap; + struct regmap_irq_chip_data *irq_data; ++ struct irq_domain *irq_domain; + int ret; + unsigned int chip_type; + struct mfd_cell *mfd; +@@ -169,20 +228,18 @@ static int bd718xx_i2c_probe(struct i2c_client *i2c) + if (ret) + return ret; + +- ret = regmap_irq_get_virq(irq_data, BD718XX_INT_PWRBTN_S); +- +- if (ret < 0) +- return dev_err_probe(&i2c->dev, ret, "Failed to get the IRQ\n"); +- +- button.irq = ret; ++ irq_domain = regmap_irq_get_domain(irq_data); + + ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO, +- mfd, cells, NULL, 0, +- regmap_irq_get_domain(irq_data)); ++ mfd, cells, NULL, 0, irq_domain); + if (ret) +- dev_err_probe(&i2c->dev, ret, "Failed to create subdevices\n"); ++ return dev_err_probe(&i2c->dev, ret, "Failed to create subdevices\n"); + +- return ret; ++ ret = bd718xx_i2c_register_pwrbutton(&i2c->dev, irq_domain); ++ if (ret) ++ return ret; ++ ++ return 0; + } + + static const struct of_device_id bd718xx_of_match[] = { +diff --git a/include/linux/input.h b/include/linux/input.h +--- a/include/linux/input.h ++++ b/include/linux/input.h +@@ -117,7 +117,8 @@ enum input_clock_type { + * user opens device and dev->close() is called when the very + * last user closes the device + * @going_away: marks devices that are in a middle of unregistering and +- * causes input_open_device*() fail with -ENODEV. ++ * causes input_open_device() and input_inhibit/uninhibit_device() ++ * to fail with -ENODEV. + * @dev: driver model's view of this device + * @h_list: list of input handles associated with the device. When + * accessing the list dev->mutex must be held +@@ -128,11 +129,14 @@ enum input_clock_type { + * @devres_managed: indicates that devices is managed with devres framework + * and needs not be explicitly unregistered or freed. + * @timestamp: storage for a timestamp set by input_set_timestamp called +- * by a driver ++ * by a driver + * @inhibited: indicates that the input device is inhibited. If that is +- * the case then input core ignores any events generated by the device. +- * Device's close() is called when it is being inhibited and its open() +- * is called when it is being uninhibited. ++ * the case then input core ignores any events generated by the device. ++ * Device's close() is called when it is being inhibited and its open() ++ * is called when it is being uninhibited. ++ * @ready: indicates that the device has been successfully opened and is ++ * prepared to process events (like LEDs or sounds) sent from the ++ * input core. + */ + struct input_dev { + const char *name; +@@ -209,6 +213,7 @@ struct input_dev { + ktime_t timestamp[INPUT_CLK_MAX]; + + bool inhibited; ++ bool ready; + }; + #define to_input_dev(d) container_of(d, struct input_dev, dev) + +@@ -284,8 +289,9 @@ struct input_handle; + * @connect: called when attaching a handler to an input device + * @disconnect: disconnects a handler from input device + * @start: starts handler for given handle. This function is called by +- * input core right after connect() method and also when a process +- * that "grabbed" a device releases it ++ * input core when device is open and ready to process events, ++ * and also when device is uninhibited or when a process that "grabbed" ++ * a device releases it + * @passive_observer: set to %true by drivers only interested in observing + * data stream from devices if there are other users present. Such + * drivers will not result in starting underlying hardware device +diff --git a/include/linux/input/elan-i2c-ids.h b/include/linux/input/elan-i2c-ids.h +--- a/include/linux/input/elan-i2c-ids.h ++++ b/include/linux/input/elan-i2c-ids.h +@@ -18,7 +18,7 @@ + #ifndef __ELAN_I2C_IDS_H + #define __ELAN_I2C_IDS_H + +-#include ++#include + + static const struct acpi_device_id elan_acpi_id[] = { + { "ELAN0000", 0 }, diff --git a/pkgbuilds/linux-omarchy/0560-input-updates.patch.sig b/pkgbuilds/linux-omarchy/0560-input-updates.patch.sig new file mode 100644 index 0000000..9e9a221 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0560-input-updates.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0560-input.patch b/pkgbuilds/linux-omarchy/0560-input.patch deleted file mode 100644 index 0d58569..0000000 --- a/pkgbuilds/linux-omarchy/0560-input.patch +++ /dev/null @@ -1,76 +0,0 @@ -diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c ---- a/drivers/input/mouse/focaltech.c -+++ b/drivers/input/mouse/focaltech.c -@@ -78,8 +78,8 @@ struct focaltech_finger_state { - * Absolute position (from the bottom left corner) of the - * finger. - */ -- unsigned int x; -- unsigned int y; -+ int x; -+ int y; - }; - - /* -@@ -108,7 +108,7 @@ struct focaltech_hw_state { - }; - - struct focaltech_data { -- unsigned int x_max, y_max; -+ int x_max, y_max; - struct focaltech_hw_state state; - }; - -@@ -126,17 +126,16 @@ static void focaltech_report_state(struct psmouse *psmouse) - input_mt_slot(dev, i); - input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); - if (active) { -- unsigned int clamped_x, clamped_y; - /* - * The touchpad might report invalid data, so we clamp - * the resulting values so that we do not confuse -- * userspace. -+ * userspace or accumulate coordinate wind-up. - */ -- clamped_x = clamp(finger->x, 0U, priv->x_max); -- clamped_y = clamp(finger->y, 0U, priv->y_max); -- input_report_abs(dev, ABS_MT_POSITION_X, clamped_x); -+ finger->x = clamp(finger->x, 0, priv->x_max); -+ finger->y = clamp(finger->y, 0, priv->y_max); -+ input_report_abs(dev, ABS_MT_POSITION_X, finger->x); - input_report_abs(dev, ABS_MT_POSITION_Y, -- priv->y_max - clamped_y); -+ priv->y_max - finger->y); - input_report_abs(dev, ABS_TOOL_WIDTH, state->width); - } - } -diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c ---- a/drivers/input/mouse/psmouse-base.c -+++ b/drivers/input/mouse/psmouse-base.c -@@ -267,7 +267,15 @@ void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) - */ - static int psmouse_handle_byte(struct psmouse *psmouse) - { -- psmouse_ret_t rc = psmouse->protocol_handler(psmouse); -+ psmouse_ret_t rc; -+ -+ /* protocol_handler is NULL when device is being disconnected */ -+ if (unlikely(!psmouse->protocol_handler)) { -+ psmouse->pktcnt = 0; -+ return 0; -+ } -+ -+ rc = psmouse->protocol_handler(psmouse); - - switch (rc) { - case PSMOUSE_BAD_DATA: -@@ -1466,6 +1474,9 @@ static void psmouse_disconnect(struct serio *serio) - psmouse_deactivate(parent); - } - -+ scoped_guard(serio_pause_rx, serio) -+ psmouse->protocol_handler = NULL; -+ - if (psmouse->disconnect) - psmouse->disconnect(psmouse); - diff --git a/pkgbuilds/linux-omarchy/0560-input.patch.sig b/pkgbuilds/linux-omarchy/0560-input.patch.sig deleted file mode 100644 index 643da0a..0000000 Binary files a/pkgbuilds/linux-omarchy/0560-input.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/0561-input-fixes.patch b/pkgbuilds/linux-omarchy/0561-input-fixes.patch new file mode 100644 index 0000000..b067aaa --- /dev/null +++ b/pkgbuilds/linux-omarchy/0561-input-fixes.patch @@ -0,0 +1,22 @@ +diff --git a/drivers/input/touchscreen/tsc2007_core.c b/drivers/input/touchscreen/tsc2007_core.c +--- a/drivers/input/touchscreen/tsc2007_core.c ++++ b/drivers/input/touchscreen/tsc2007_core.c +@@ -221,7 +221,6 @@ static int tsc2007_get_pendown_state_gpio(struct device *dev) + static int tsc2007_probe_properties(struct device *dev, struct tsc2007 *ts) + { + u32 val32; +- u64 val64; + + if (!device_property_read_u32(dev, "ti,max-rt", &val32)) + ts->max_rt = val32; +@@ -237,8 +236,8 @@ static int tsc2007_probe_properties(struct device *dev, struct tsc2007 *ts) + if (!device_property_read_u32(dev, "ti,fuzzz", &val32)) + ts->fuzzz = val32; + +- if (!device_property_read_u64(dev, "ti,poll-period", &val64)) +- ts->poll_period = msecs_to_jiffies(val64); ++ if (!device_property_read_u32(dev, "ti,poll-period", &val32)) ++ ts->poll_period = msecs_to_jiffies(val32); + else + ts->poll_period = msecs_to_jiffies(1); + diff --git a/pkgbuilds/linux-omarchy/0561-input-fixes.patch.sig b/pkgbuilds/linux-omarchy/0561-input-fixes.patch.sig new file mode 100644 index 0000000..27751f1 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0561-input-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0562-hid-fixes.patch b/pkgbuilds/linux-omarchy/0562-hid-fixes.patch new file mode 100644 index 0000000..47e03fa --- /dev/null +++ b/pkgbuilds/linux-omarchy/0562-hid-fixes.patch @@ -0,0 +1,483 @@ +diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_common.h b/drivers/hid/amd-sfh-hid/amd_sfh_common.h +--- a/drivers/hid/amd-sfh-hid/amd_sfh_common.h ++++ b/drivers/hid/amd-sfh-hid/amd_sfh_common.h +@@ -12,11 +12,15 @@ + + #include + #include ++#include + #include "amd_sfh_hid.h" + + #define PCI_DEVICE_ID_AMD_MP2 0x15E4 + #define PCI_DEVICE_ID_AMD_MP2_1_1 0x164A + ++/* The BAR 2 size must cover the highest register offset (0x10958) */ ++#define AMD_SFH_MIN_BAR_SIZE SZ_128K ++ + #define AMD_C2P_MSG(regno) (0x10500 + ((regno) * 4)) + #define AMD_P2C_MSG(regno) (0x10680 + ((regno) * 4)) + +diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c +--- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c ++++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c +@@ -451,6 +451,16 @@ static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i + if (rc) + return rc; + ++ if (!(pci_resource_flags(pdev, 2) & IORESOURCE_MEM)) { ++ dev_err(&pdev->dev, "BAR 2 is not IORESOURCE_MEM\n"); ++ return -ENODEV; ++ } ++ ++ if (pci_resource_len(pdev, 2) < AMD_SFH_MIN_BAR_SIZE) { ++ dev_err(&pdev->dev, "BAR 2 is too small\n"); ++ return -EINVAL; ++ } ++ + rc = pcim_iomap_regions(pdev, BIT(2), DRIVER_NAME); + if (rc) + return rc; +diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c +--- a/drivers/hid/bpf/hid_bpf_dispatch.c ++++ b/drivers/hid/bpf/hid_bpf_dispatch.c +@@ -359,7 +359,7 @@ hid_bpf_release_context(struct hid_bpf_ctx *ctx) + + static int + __hid_bpf_hw_check_params(struct hid_bpf_ctx *ctx, __u8 *buf, size_t *buf__sz, +- enum hid_report_type rtype) ++ enum hid_report_type rtype, bool hw_request) + { + struct hid_report_enum *report_enum; + struct hid_report *report; +@@ -388,6 +388,10 @@ __hid_bpf_hw_check_params(struct hid_bpf_ctx *ctx, __u8 *buf, size_t *buf__sz, + + report_len = hid_report_len(report); + ++ /* unnumbered reports need to have a report ID reserved in the first byte */ ++ if (hw_request && report_enum->numbered == 0) ++ report_len += 1; ++ + if (*buf__sz > report_len) + *buf__sz = report_len; + +@@ -420,7 +424,7 @@ hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz, + return -EDEADLOCK; + + /* check arguments */ +- ret = __hid_bpf_hw_check_params(ctx, buf, &size, rtype); ++ ret = __hid_bpf_hw_check_params(ctx, buf, &size, rtype, true); + if (ret) + return ret; + +@@ -480,7 +484,7 @@ hid_bpf_hw_output_report(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz) + return -EDEADLOCK; + + /* check arguments */ +- ret = __hid_bpf_hw_check_params(ctx, buf, &size, HID_OUTPUT_REPORT); ++ ret = __hid_bpf_hw_check_params(ctx, buf, &size, HID_OUTPUT_REPORT, true); + if (ret) + return ret; + +@@ -506,7 +510,7 @@ __hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *b + return -EDEADLOCK; + + /* check arguments */ +- ret = __hid_bpf_hw_check_params(ctx, buf, &size, type); ++ ret = __hid_bpf_hw_check_params(ctx, buf, &size, type, false); + if (ret) + return ret; + +diff --git a/drivers/hid/hid-alps.c b/drivers/hid/hid-alps.c +--- a/drivers/hid/hid-alps.c ++++ b/drivers/hid/hid-alps.c +@@ -407,6 +407,8 @@ static int u1_raw_event(struct alps_dev *hdata, u8 *data, int size) + return 1; + + case U1_SP_ABSOLUTE_REPORT_ID: ++ if (!hdata->input2) ++ return 0; + sp_x = get_unaligned_le16(data+2); + sp_y = get_unaligned_le16(data+4); + +@@ -738,7 +740,6 @@ static int alps_input_configured(struct hid_device *hdev, struct hid_input *hi) + goto exit; + } + +- data->input2 = input2; + input2->phys = input->phys; + input2->name = "DualPoint Stick"; + input2->id.bustype = BUS_I2C; +@@ -762,11 +763,12 @@ static int alps_input_configured(struct hid_device *hdev, struct hid_input *hi) + __set_bit(INPUT_PROP_POINTER, input2->propbit); + __set_bit(INPUT_PROP_POINTING_STICK, input2->propbit); + +- if (input_register_device(data->input2)) { ++ if (input_register_device(input2)) { + input_free_device(input2); + ret = -ENOENT; + goto exit; + } ++ data->input2 = input2; + } + + exit: +@@ -823,6 +825,24 @@ static int alps_probe(struct hid_device *hdev, const struct hid_device_id *id) + return 0; + } + ++static void alps_remove(struct hid_device *hdev) ++{ ++ struct alps_dev *data = hid_get_drvdata(hdev); ++ ++ /* ++ * input2 ("DualPoint Stick") is allocated separately and is not ++ * tracked in hdev->inputs, so the default remove path ++ * (hid_hw_stop -> hidinput_disconnect) does not unregister it. ++ * ++ * Stop the device first so that no URB callback can touch input2 ++ * while it is being unregistered, then drop it explicitly. ++ */ ++ hid_hw_stop(hdev); ++ ++ if (data->input2) ++ input_unregister_device(data->input2); ++} ++ + static const struct hid_device_id alps_id[] = { + { HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, + USB_VENDOR_ID_ALPS_JP, HID_DEVICE_ID_ALPS_U1_DUAL) }, +@@ -845,6 +865,7 @@ static struct hid_driver alps_driver = { + .input_configured = alps_input_configured, + .resume = pm_ptr(alps_post_resume), + .reset_resume = pm_ptr(alps_post_reset), ++ .remove = alps_remove, + }; + + module_hid_driver(alps_driver); +diff --git a/drivers/hid/hid-corsair-void.c b/drivers/hid/hid-corsair-void.c +--- a/drivers/hid/hid-corsair-void.c ++++ b/drivers/hid/hid-corsair-void.c +@@ -51,20 +51,23 @@ + /* Receiver report information: (ID 102) */ + /* -------------------------------------------------------------------------- */ + /* +- * When queried, the recevier responds with 4 bytes to describe the firmware +- * The first 2 bytes are for the receiver, the second 2 are the headset ++ * When queried, the receiver responds with 5 bytes to describe the firmware ++ * The first byte is the ID, then 2 bytes for the receiver, then 2 for the headset + * The headset firmware version will be 0 if no headset is connected + * +- * 0: Recevier firmware major version ++ * 0: Report ID ++ * 102 for the firmware packet ++ * ++ * 1: Receiver firmware major version + * Major version of the receiver's firmware + * +- * 1: Recevier firmware minor version ++ * 2: Receiver firmware minor version + * Minor version of the receiver's firmware + * +- * 2: Headset firmware major version ++ * 3: Headset firmware major version + * Major version of the headset's firmware + * +- * 3: Headset firmware minor version ++ * 4: Headset firmware minor version + * Minor version of the headset's firmware + */ + /* -------------------------------------------------------------------------- */ +diff --git a/drivers/hid/hid-elecom.c b/drivers/hid/hid-elecom.c +--- a/drivers/hid/hid-elecom.c ++++ b/drivers/hid/hid-elecom.c +@@ -79,7 +79,7 @@ static const __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc, + case USB_DEVICE_ID_ELECOM_M_XT3URBK_00FB: + case USB_DEVICE_ID_ELECOM_M_XT3URBK_018F: + case USB_DEVICE_ID_ELECOM_M_XT3DRBK_00FC: +- case USB_DEVICE_ID_ELECOM_M_XT4DRBK: ++ case USB_DEVICE_ID_ELECOM_M_XT4DRBK_00FD: + /* + * Report descriptor format: + * 12: button bit count +@@ -104,6 +104,7 @@ static const __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc, + mouse_button_fixup(hdev, rdesc, *rsize, 12, 30, 14, 20, 8); + break; + case USB_DEVICE_ID_ELECOM_M_XT3DRBK_018C: ++ case USB_DEVICE_ID_ELECOM_M_XT4DRBK_018E: + /* + * Report descriptor format: + * 22: button bit count +@@ -148,7 +149,8 @@ static const struct hid_device_id elecom_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK_018F) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK_00FC) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK_018C) }, +- { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK_00FD) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK_018E) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1URBK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1DRBK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT2DRBK) }, +diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h +--- a/drivers/hid/hid-ids.h ++++ b/drivers/hid/hid-ids.h +@@ -472,7 +472,8 @@ + #define USB_DEVICE_ID_ELECOM_M_XT3URBK_018F 0x018f + #define USB_DEVICE_ID_ELECOM_M_XT3DRBK_00FC 0x00fc + #define USB_DEVICE_ID_ELECOM_M_XT3DRBK_018C 0x018c +-#define USB_DEVICE_ID_ELECOM_M_XT4DRBK 0x00fd ++#define USB_DEVICE_ID_ELECOM_M_XT4DRBK_00FD 0x00fd ++#define USB_DEVICE_ID_ELECOM_M_XT4DRBK_018E 0x018e + #define USB_DEVICE_ID_ELECOM_M_DT1URBK 0x00fe + #define USB_DEVICE_ID_ELECOM_M_DT1DRBK 0x00ff + #define USB_DEVICE_ID_ELECOM_M_DT2DRBK 0x018d +@@ -759,6 +760,7 @@ + #define I2C_DEVICE_ID_ITE_LENOVO_LEGION_Y720 0x837a + #define USB_DEVICE_ID_ITE_LENOVO_YOGA900 0x8396 + #define I2C_DEVICE_ID_ITE_LENOVO_YOGA_SLIM_7X_KEYBOARD 0x8987 ++#define I2C_DEVICE_ID_ITE_LENOVO_YOGA_SLIM_7X_G11_KEYBOARD 0x83db + #define USB_DEVICE_ID_ITE8595 0x8595 + #define USB_DEVICE_ID_ITE_MEDION_E1239T 0xce50 + +@@ -1283,6 +1285,9 @@ + #define USB_DEVICE_ID_SAMSUNG_WIRELESS_UNIVERSAL_KBD 0xa006 + #define USB_DEVICE_ID_SAMSUNG_WIRELESS_MULTI_HOGP_KBD 0xa064 + ++#define USB_VENDOR_ID_SDINNOVATION 0x36ae ++#define USB_DEVICE_ID_SDINNOVATION_GAMING_KBD 0xfeab ++ + #define USB_VENDOR_ID_SEMICO 0x1a2c + #define USB_DEVICE_ID_SEMICO_USB_KEYKOARD 0x0023 + #define USB_DEVICE_ID_SEMICO_USB_KEYKOARD2 0x0027 +diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c +--- a/drivers/hid/hid-logitech-hidpp.c ++++ b/drivers/hid/hid-logitech-hidpp.c +@@ -4656,6 +4656,8 @@ static const struct hid_device_id hidpp_devices[] = { + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC082) }, + { /* Logitech G502 Lightspeed Wireless Gaming Mouse over USB */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC08D) }, ++ { /* Logitech G502 X Lightspeed Wireless Gaming Mouse over USB */ ++ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC098) }, + { /* Logitech G703 Gaming Mouse over USB */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC087) }, + { /* Logitech G703 Hero Gaming Mouse over USB */ +diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c +--- a/drivers/hid/hid-multitouch.c ++++ b/drivers/hid/hid-multitouch.c +@@ -79,6 +79,7 @@ MODULE_LICENSE("GPL"); + #define MT_QUIRK_APPLE_TOUCHBAR BIT(23) + #define MT_QUIRK_YOGABOOK9I BIT(24) + #define MT_QUIRK_KEEP_LATENCY_ON_CLOSE BIT(25) ++#define MT_QUIRK_IGNORE_FEATURE_ID_MISMATCH BIT(26) + + #define MT_INPUTMODE_TOUCHSCREEN 0x02 + #define MT_INPUTMODE_TOUCHPAD 0x03 +@@ -235,6 +236,7 @@ static void mt_post_parse(struct mt_device *td, struct mt_application *app); + #define MT_CLS_APPLE_TOUCHBAR 0x0114 + #define MT_CLS_YOGABOOK9I 0x0115 + #define MT_CLS_EGALAX_P80H84 0x0116 ++#define MT_CLS_ASUS_ROG_Z13_FOLIO 0x0117 + #define MT_CLS_SIS 0x0457 + + #define MT_DEFAULT_MAXCONTACT 10 +@@ -405,6 +407,16 @@ static const struct mt_class mt_classes[] = { + .quirks = MT_QUIRK_ALWAYS_VALID | + MT_QUIRK_CONTACT_CNT_ACCURATE | + MT_QUIRK_ASUS_CUSTOM_UP }, ++ { .name = MT_CLS_ASUS_ROG_Z13_FOLIO, ++ .quirks = MT_QUIRK_ALWAYS_VALID | ++ MT_QUIRK_IGNORE_DUPLICATES | ++ MT_QUIRK_HOVERING | ++ MT_QUIRK_CONTACT_CNT_ACCURATE | ++ MT_QUIRK_STICKY_FINGERS | ++ MT_QUIRK_WIN8_PTP_BUTTONS | ++ MT_QUIRK_CONFIDENCE | ++ MT_QUIRK_IGNORE_FEATURE_ID_MISMATCH, ++ .export_all_inputs = true }, + { .name = MT_CLS_VTL, + .quirks = MT_QUIRK_ALWAYS_VALID | + MT_QUIRK_CONTACT_CNT_ACCURATE | +@@ -507,6 +519,7 @@ static const struct attribute_group mt_attribute_group = { + + static void mt_get_feature(struct hid_device *hdev, struct hid_report *report) + { ++ struct mt_device *td = hid_get_drvdata(hdev); + int ret; + u32 size = hid_report_len(report); + u8 *buf; +@@ -528,8 +541,14 @@ static void mt_get_feature(struct hid_device *hdev, struct hid_report *report) + dev_warn(&hdev->dev, "failed to fetch feature %d\n", + report->id); + } else { +- /* The report ID in the request and the response should match */ +- if (report->id != buf[0]) { ++ /* ++ * The report ID in the request and the response should match. ++ * Some firmware (e.g. the ASUS ROG Z13 Folio ++ * touchpad) returns a mismatched ID on this specific fetch; ++ * tolerate it only for devices explicitly flagged as such. ++ */ ++ if (report->id != buf[0] && ++ !(td->mtclass.quirks & MT_QUIRK_IGNORE_FEATURE_ID_MISMATCH)) { + hid_err(hdev, "Returned feature report did not match the request\n"); + goto free; + } +@@ -2726,6 +2745,12 @@ static const struct hid_device_id mt_devices[] = { + HID_DEVICE(BUS_I2C, HID_GROUP_MULTITOUCH_WIN_8, + I2C_VENDOR_ID_HANTICK, I2C_PRODUCT_ID_HANTICK_5288) }, + ++ /* Asus ROG Flow Z13 (2025) GZ302EA keyboard-cover touchpad */ ++ { .driver_data = MT_CLS_ASUS_ROG_Z13_FOLIO, ++ HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8, ++ USB_VENDOR_ID_ASUSTEK, ++ USB_DEVICE_ID_ASUSTEK_ROG_Z13_FOLIO) }, ++ + /* Generic MT device */ + { HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH, HID_ANY_ID, HID_ANY_ID) }, + +diff --git a/drivers/hid/hid-oxp.c b/drivers/hid/hid-oxp.c +--- a/drivers/hid/hid-oxp.c ++++ b/drivers/hid/hid-oxp.c +@@ -1552,9 +1552,9 @@ static int oxp_hid_probe(struct hid_device *hdev, + + static void oxp_hid_remove(struct hid_device *hdev) + { +- cancel_delayed_work(&drvdata.oxp_rgb_queue); +- cancel_delayed_work(&drvdata.oxp_btn_queue); +- cancel_delayed_work(&drvdata.oxp_mcu_init); ++ cancel_delayed_work_sync(&drvdata.oxp_rgb_queue); ++ cancel_delayed_work_sync(&drvdata.oxp_btn_queue); ++ cancel_delayed_work_sync(&drvdata.oxp_mcu_init); + hid_hw_close(hdev); + hid_hw_stop(hdev); + } +diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c +--- a/drivers/hid/hid-quirks.c ++++ b/drivers/hid/hid-quirks.c +@@ -183,6 +183,7 @@ static const struct hid_device_id hid_quirks[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_X52_2), HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE }, + { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_X52_PRO), HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE }, + { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_X65), HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SDINNOVATION, USB_DEVICE_ID_SDINNOVATION_GAMING_KBD), HID_QUIRK_ALWAYS_POLL }, + { HID_USB_DEVICE(USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD2), HID_QUIRK_NO_INIT_REPORTS }, + { HID_USB_DEVICE(USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD), HID_QUIRK_NO_INIT_REPORTS }, + { HID_USB_DEVICE(USB_VENDOR_ID_SENNHEISER, USB_DEVICE_ID_SENNHEISER_BTD500USB), HID_QUIRK_NOGET }, +@@ -422,13 +423,14 @@ static const struct hid_device_id hid_have_special_driver[] = { + #endif + #if IS_ENABLED(CONFIG_HID_ELECOM) + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, +- { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XGL20DLBK) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XGL20DLBK) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1MRBK_01AC) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK_00FB) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK_018F) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK_00FC) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK_018C) }, +- { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK_00FD) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK_018E) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1URBK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1DRBK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT2DRBK) }, +diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c +--- a/drivers/hid/hid-roccat.c ++++ b/drivers/hid/hid-roccat.c +@@ -344,8 +344,6 @@ int roccat_connect(const struct class *klass, struct hid_device *hid, int report + return temp; + } + +- mutex_unlock(&devices_lock); +- + init_waitqueue_head(&device->wait); + INIT_LIST_HEAD(&device->readers); + mutex_init(&device->readers_lock); +@@ -356,6 +354,7 @@ int roccat_connect(const struct class *klass, struct hid_device *hid, int report + device->cbuf_end = 0; + device->report_size = report_size; + ++ mutex_unlock(&devices_lock); + return minor; + } + EXPORT_SYMBOL_GPL(roccat_connect); +@@ -369,15 +368,12 @@ void roccat_disconnect(int minor) + + mutex_lock(&devices_lock); + device = devices[minor]; +- mutex_unlock(&devices_lock); + + device->exist = 0; /* TODO exist maybe not needed */ + + device_destroy(device->dev->class, MKDEV(roccat_major, minor)); + +- mutex_lock(&devices_lock); + devices[minor] = NULL; +- mutex_unlock(&devices_lock); + + if (device->open) { + hid_hw_close(device->hid); +@@ -385,6 +381,8 @@ void roccat_disconnect(int minor) + } else { + roccat_free_device(device); + } ++ ++ mutex_unlock(&devices_lock); + } + EXPORT_SYMBOL_GPL(roccat_disconnect); + +diff --git a/drivers/hid/hid-winwing.c b/drivers/hid/hid-winwing.c +--- a/drivers/hid/hid-winwing.c ++++ b/drivers/hid/hid-winwing.c +@@ -315,7 +315,8 @@ static void winwing_haptic_rumble_cb(struct work_struct *work) + static int winwing_play_effect(struct input_dev *dev, void *context, + struct ff_effect *effect) + { +- struct winwing_drv_data *data = (struct winwing_drv_data *) context; ++ struct hid_device *hdev = input_get_drvdata(dev); ++ struct winwing_drv_data *data = hid_get_drvdata(hdev); + + if (effect->type != FF_RUMBLE) + return 0; +@@ -342,7 +343,12 @@ static int winwing_init_ff(struct hid_device *hdev, struct hid_input *hidinput) + + input_set_capability(hidinput->input, EV_FF, FF_RUMBLE); + +- return input_ff_create_memless(hidinput->input, data, ++ /* ++ * input_ff_create_memless() takes ownership of the context pointer ++ * and frees it on teardown; do not hand it the devm-managed drvdata. ++ * winwing_play_effect() fetches it from the input device instead. ++ */ ++ return input_ff_create_memless(hidinput->input, NULL, + winwing_play_effect); + } + +diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c +--- a/drivers/hid/i2c-hid/i2c-hid-core.c ++++ b/drivers/hid/i2c-hid/i2c-hid-core.c +@@ -123,9 +123,11 @@ static const struct i2c_hid_quirks { + __u32 quirks; + } i2c_hid_quirks[] = { + { I2C_VENDOR_ID_HANTICK, I2C_PRODUCT_ID_HANTICK_5288, +- I2C_HID_QUIRK_NO_IRQ_AFTER_RESET }, ++ I2C_HID_QUIRK_NO_IRQ_AFTER_RESET | I2C_HID_QUIRK_BAD_INPUT_SIZE }, + { I2C_VENDOR_ID_ITE, I2C_DEVICE_ID_ITE_VOYO_WINPAD_A15, + I2C_HID_QUIRK_NO_IRQ_AFTER_RESET }, ++ { USB_VENDOR_ID_ITE, I2C_DEVICE_ID_ITE_LENOVO_YOGA_SLIM_7X_G11_KEYBOARD, ++ I2C_HID_QUIRK_NO_IRQ_AFTER_RESET }, + { I2C_VENDOR_ID_RAYDIUM, I2C_PRODUCT_ID_RAYDIUM_3118, + I2C_HID_QUIRK_NO_IRQ_AFTER_RESET }, + { USB_VENDOR_ID_ALPS_JP, HID_ANY_ID, +diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c +--- a/drivers/hid/wacom_sys.c ++++ b/drivers/hid/wacom_sys.c +@@ -113,8 +113,9 @@ static int wacom_wac_pen_serial_enforce(struct hid_device *hdev, + + /* Queue events which have invalid tool type or serial number */ + for (i = 0; i < report->maxfield; i++) { +- for (j = 0; j < report->field[i]->maxusage; j++) { +- struct hid_field *field = report->field[i]; ++ struct hid_field *field = report->field[i]; ++ ++ for (j = 0; j < field->report_count; j++) { + struct hid_usage *usage = &field->usage[j]; + unsigned int equivalent_usage = wacom_equivalent_usage(usage->hid); + unsigned int offset; diff --git a/pkgbuilds/linux-omarchy/0562-hid-fixes.patch.sig b/pkgbuilds/linux-omarchy/0562-hid-fixes.patch.sig new file mode 100644 index 0000000..03c4b82 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0562-hid-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0563-input-fixes-2.patch b/pkgbuilds/linux-omarchy/0563-input-fixes-2.patch new file mode 100644 index 0000000..1682da2 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0563-input-fixes-2.patch @@ -0,0 +1,88 @@ +diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c +--- a/drivers/input/keyboard/atkbd.c ++++ b/drivers/input/keyboard/atkbd.c +@@ -1938,6 +1938,14 @@ static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = { + }, + .callback = atkbd_deactivate_fixup, + }, ++ { ++ /* Lenovo IdeaPad Slim 3 15IWC11 (83RR) */ ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "83RR"), ++ }, ++ .callback = atkbd_deactivate_fixup, ++ }, + { + /* Xiaomi Book Pro 14 (TM2424) */ + .matches = { +diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c +--- a/drivers/input/mouse/synaptics.c ++++ b/drivers/input/mouse/synaptics.c +@@ -192,6 +192,7 @@ static const char * const smbus_pnp_ids[] = { + "LEN2054", /* E480 */ + "LEN2055", /* E580 */ + "LEN2058", /* E490 */ ++ "LEN205b", /* T490 */ + "LEN2068", /* T14 Gen 1 */ + "SYN1221", /* TUXEDO InfinityBook Pro 14 v5 */ + "SYN3003", /* HP EliteBook 850 G1 */ +@@ -1839,8 +1840,10 @@ static int synaptics_setup_intertouch(struct psmouse *psmouse, + return -ENXIO; + } + +- /* Disable intertouch on known-broken board revisions */ +- if (info->board_id == 2722) { ++ /* Disable intertouch on known-broken T440p board revisions */ ++ if (info->board_id == 2722 && ++ psmouse_matches_pnp_id(psmouse, ++ (const char * const []){"LEN0036", NULL})) { + psmouse_info(psmouse, + "Disabling intertouch for board id %u\n", + info->board_id); +diff --git a/drivers/input/serio/i8042-acpipnpio.h b/drivers/input/serio/i8042-acpipnpio.h +--- a/drivers/input/serio/i8042-acpipnpio.h ++++ b/drivers/input/serio/i8042-acpipnpio.h +@@ -671,6 +671,15 @@ static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = { + }, + .driver_data = (void *)(SERIO_QUIRK_NOAUX) + }, ++ { ++ /* Fujitsu LIFEBOOK U7410 */ ++ /* Mux mode also disturbs the EC keyboard, causing spurious ACKs */ ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU CLIENT COMPUTING LIMITED"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U7410"), ++ }, ++ .driver_data = (void *)(SERIO_QUIRK_NOMUX) ++ }, + { + /* Gigabyte M912 */ + .matches = { +diff --git a/drivers/input/touchscreen/s6sy761.c b/drivers/input/touchscreen/s6sy761.c +--- a/drivers/input/touchscreen/s6sy761.c ++++ b/drivers/input/touchscreen/s6sy761.c +@@ -500,10 +500,22 @@ static int s6sy761_suspend(struct device *dev) + static int s6sy761_resume(struct device *dev) + { + struct s6sy761_data *sdata = dev_get_drvdata(dev); ++ int err; ++ ++ err = s6sy761_power_on(sdata); ++ if (err) ++ return err; + + enable_irq(sdata->client->irq); + +- return s6sy761_power_on(sdata); ++ guard(mutex)(&sdata->input->mutex); ++ ++ if (!input_device_enabled(sdata->input)) ++ return 0; ++ ++ err = i2c_smbus_write_byte(sdata->client, S6SY761_SENSE_ON); ++ ++ return err; + } + + static const struct dev_pm_ops s6sy761_pm_ops = { diff --git a/pkgbuilds/linux-omarchy/0563-input-fixes-2.patch.sig b/pkgbuilds/linux-omarchy/0563-input-fixes-2.patch.sig new file mode 100644 index 0000000..5059200 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0563-input-fixes-2.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch b/pkgbuilds/linux-omarchy/0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch index e569f4c..d7870a2 100644 --- a/pkgbuilds/linux-omarchy/0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch +++ b/pkgbuilds/linux-omarchy/0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch @@ -1,7 +1,7 @@ diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c -@@ -1294,12 +1294,14 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id) +@@ -1480,12 +1480,14 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id) return ret; } diff --git a/pkgbuilds/linux-omarchy/0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch.sig b/pkgbuilds/linux-omarchy/0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch.sig index dbe7b90..0d7e5e4 100644 Binary files a/pkgbuilds/linux-omarchy/0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch.sig and b/pkgbuilds/linux-omarchy/0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0600-usb4stream-fixes.patch b/pkgbuilds/linux-omarchy/0600-usb4stream-fixes.patch deleted file mode 100644 index 5fe836e..0000000 --- a/pkgbuilds/linux-omarchy/0600-usb4stream-fixes.patch +++ /dev/null @@ -1,47 +0,0 @@ -diff --git a/drivers/thunderbolt/stream.c b/drivers/thunderbolt/stream.c -index 4cc86d8d6491..43d29d69dcdb 100644 ---- a/drivers/thunderbolt/stream.c -+++ b/drivers/thunderbolt/stream.c -@@ -512,8 +512,10 @@ tbstream_dev_alloc_tx(struct tbstream_dev *sdev, enum tbstream_frame_pdf pdf, - dma_sync_single_for_cpu(dma_dev, sf->frame.buffer_phy, size, - DMA_TO_DEVICE); - if (pdf == TBSTREAM_DATA) { -- if (copy_page_from_iter(sf->page, 0, size, from) != size) -+ if (copy_page_from_iter(sf->page, 0, size, from) != size) { -+ sdev->tx_ring.cons--; - return ERR_PTR(-EFAULT); -+ } - } else { - memset(page_address(sf->page), 0, size); - } -@@ -671,7 +673,7 @@ tbstream_dev_fops_read_iter(struct kiocb *kiocb, struct iov_iter *to) - } - - nbytes = 0; -- while (nbytes < iov_iter_count(to)) { -+ while (iov_iter_count(to)) { - struct tbstream_frame *sf; - size_t size, sf_size; - -@@ -693,7 +695,7 @@ tbstream_dev_fops_read_iter(struct kiocb *kiocb, struct iov_iter *to) - } - - sf_size = tb_ring_frame_size(&sf->frame); -- size = min(iov_iter_count(to) - nbytes, sf_size); -+ size = min(iov_iter_count(to), sf_size); - - if (copy_page_to_iter(sf->page, sf->offset, size, to) != size) { - ret = -EFAULT; -@@ -763,10 +765,10 @@ tbstream_dev_fops_write_iter(struct kiocb *kiocb, struct iov_iter *from) - } - - nbytes = 0; -- while (nbytes < iov_iter_count(from)) { -+ while (iov_iter_count(from)) { - size_t size; - -- size = min(iov_iter_count(from) - nbytes, TB_MAX_FRAME_SIZE); -+ size = min(iov_iter_count(from), TB_MAX_FRAME_SIZE); - ret = tbstream_dev_send_data(sdev, from, size); - if (ret) { - /* diff --git a/pkgbuilds/linux-omarchy/0600-usb4stream-fixes.patch.sig b/pkgbuilds/linux-omarchy/0600-usb4stream-fixes.patch.sig deleted file mode 100644 index 753b735..0000000 Binary files a/pkgbuilds/linux-omarchy/0600-usb4stream-fixes.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/0601-thunderbolt-updates.patch b/pkgbuilds/linux-omarchy/0601-thunderbolt-updates.patch new file mode 100644 index 0000000..7a0d9ca --- /dev/null +++ b/pkgbuilds/linux-omarchy/0601-thunderbolt-updates.patch @@ -0,0 +1,1131 @@ +diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c +--- a/drivers/net/thunderbolt/main.c ++++ b/drivers/net/thunderbolt/main.c +@@ -1362,7 +1362,7 @@ static void tbnet_generate_mac(struct net_device *dev) + dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; + } + +-static int tbnet_probe(struct tb_service *svc, const struct tb_service_id *id) ++static int tbnet_probe(struct tb_service *svc) + { + struct tb_xdomain *xd = tb_service_parent(svc); + struct net_device *dev; +@@ -1482,7 +1482,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(tbnet_pm_ops, tbnet_suspend, tbnet_resume); + + static const struct tb_service_id tbnet_ids[] = { + { TB_SERVICE("network", 1) }, +- { }, ++ { } + }; + MODULE_DEVICE_TABLE(tbsvc, tbnet_ids); + +diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c +--- a/drivers/thunderbolt/debugfs.c ++++ b/drivers/thunderbolt/debugfs.c +@@ -136,13 +136,13 @@ static void *validate_and_copy_from_user(const void __user *user_buf, + if (!access_ok(user_buf, *count)) + return ERR_PTR(-EFAULT); + +- buf = (void *)get_zeroed_page(GFP_KERNEL); ++ buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return ERR_PTR(-ENOMEM); + + nbytes = min_t(size_t, *count, PAGE_SIZE); + if (copy_from_user(buf, user_buf, nbytes)) { +- free_page((unsigned long)buf); ++ kfree(buf); + return ERR_PTR(-EFAULT); + } + +@@ -265,7 +265,7 @@ static ssize_t regs_write(struct tb_switch *sw, struct tb_port *port, + out: + pm_runtime_mark_last_busy(&sw->dev); + pm_runtime_put_autosuspend(&sw->dev); +- free_page((unsigned long)buf); ++ kfree(buf); + + return ret < 0 ? ret : count; + } +@@ -406,7 +406,7 @@ static ssize_t port_sb_regs_write(struct file *file, const char __user *user_buf + out: + pm_runtime_mark_last_busy(&sw->dev); + pm_runtime_put_autosuspend(&sw->dev); +- free_page((unsigned long)buf); ++ kfree(buf); + + return ret < 0 ? ret : count; + } +@@ -439,7 +439,7 @@ static ssize_t retimer_sb_regs_write(struct file *file, + out: + pm_runtime_mark_last_busy(&rt->dev); + pm_runtime_put_autosuspend(&rt->dev); +- free_page((unsigned long)buf); ++ kfree(buf); + + return ret < 0 ? ret : count; + } +@@ -652,7 +652,7 @@ margining_ber_level_write(struct file *file, const char __user *user_buf, + margining->ber_level = val; + + out_free: +- free_page((unsigned long)buf); ++ kfree(buf); + out_unlock: + mutex_unlock(&tb->lock); + +@@ -829,7 +829,7 @@ margining_lanes_write(struct file *file, const char __user *user_buf, + } + } + +- free_page((unsigned long)buf); ++ kfree(buf); + + if (lane == -1) + return -EINVAL; +@@ -958,7 +958,7 @@ margining_error_counter_write(struct file *file, const char __user *user_buf, + else + goto err_free; + +- free_page((unsigned long)buf); ++ kfree(buf); + + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { + if (!margining->software) +@@ -970,7 +970,7 @@ margining_error_counter_write(struct file *file, const char __user *user_buf, + return count; + + err_free: +- free_page((unsigned long)buf); ++ kfree(buf); + return -EINVAL; + } + +@@ -1116,7 +1116,7 @@ static ssize_t margining_mode_write(struct file *file, + mutex_unlock(&tb->lock); + + out_free: +- free_page((unsigned long)buf); ++ kfree(buf); + return ret ? ret : count; + } + +@@ -1503,7 +1503,7 @@ static ssize_t margining_test_write(struct file *file, + mutex_unlock(&tb->lock); + + out_free: +- free_page((unsigned long)buf); ++ kfree(buf); + return ret ? ret : count; + } + +@@ -1569,7 +1569,7 @@ static ssize_t margining_margin_write(struct file *file, + mutex_unlock(&tb->lock); + + out_free: +- free_page((unsigned long)buf); ++ kfree(buf); + return ret ? ret : count; + } + +@@ -1624,7 +1624,7 @@ static ssize_t margining_eye_write(struct file *file, + ret = -EINVAL; + } + +- free_page((unsigned long)buf); ++ kfree(buf); + return ret ? ret : count; + } + +@@ -1934,7 +1934,7 @@ static ssize_t counters_write(struct file *file, const char __user *user_buf, + out: + pm_runtime_mark_last_busy(&sw->dev); + pm_runtime_put_autosuspend(&sw->dev); +- free_page((unsigned long)buf); ++ kfree(buf); + + return ret < 0 ? ret : count; + } +diff --git a/drivers/thunderbolt/dma_test.c b/drivers/thunderbolt/dma_test.c +--- a/drivers/thunderbolt/dma_test.c ++++ b/drivers/thunderbolt/dma_test.c +@@ -636,7 +636,7 @@ static void dma_test_debugfs_init(struct tb_service *svc) + debugfs_create_file("test", 0200, debugfs_dir, svc, &test_fops); + } + +-static int dma_test_probe(struct tb_service *svc, const struct tb_service_id *id) ++static int dma_test_probe(struct tb_service *svc) + { + struct tb_xdomain *xd = tb_service_parent(svc); + struct dma_test *dt; +@@ -689,7 +689,7 @@ static const struct dev_pm_ops dma_test_pm_ops = { + + static const struct tb_service_id dma_test_ids[] = { + { TB_SERVICE("dma_test", 1) }, +- { }, ++ { } + }; + MODULE_DEVICE_TABLE(tbsvc, dma_test_ids); + +diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c +--- a/drivers/thunderbolt/domain.c ++++ b/drivers/thunderbolt/domain.c +@@ -77,12 +77,10 @@ static int tb_service_probe(struct device *dev) + { + struct tb_service *svc = tb_to_service(dev); + struct tb_service_driver *driver; +- const struct tb_service_id *id; + + driver = container_of(dev->driver, struct tb_service_driver, driver); +- id = __tb_service_match(dev, &driver->driver); + +- return driver->probe(svc, id); ++ return driver->probe(svc); + } + + static void tb_service_remove(struct device *dev) +@@ -790,6 +788,21 @@ int tb_domain_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, + transmit_ring, receive_path, receive_ring); + } + ++static void tb_domain_reset_interface(struct tb *tb) ++{ ++ struct tb_nhi *nhi = tb->nhi; ++ ++ if (!nhi->ops->reset_interface) ++ return; ++ ++ guard(mutex)(&tb->lock); ++ ++ /* The reset clears the ring state so stop the control channel */ ++ tb_ctl_stop(tb->ctl); ++ nhi->ops->reset_interface(nhi); ++ tb_ctl_start(tb->ctl); ++} ++ + /** + * tb_domain_disconnect_xdomain_paths() - Disable DMA paths for XDomain + * @tb: Domain disabling the DMA paths +@@ -812,11 +825,20 @@ int tb_domain_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, + int transmit_path, int transmit_ring, + int receive_path, int receive_ring) + { ++ int ret; ++ + if (!tb->cm_ops->disconnect_xdomain_paths) + return -ENOTSUPP; + +- return tb->cm_ops->disconnect_xdomain_paths(tb, xd, transmit_path, ++ ret = tb->cm_ops->disconnect_xdomain_paths(tb, xd, transmit_path, + transmit_ring, receive_path, receive_ring); ++ if (ret) ++ return ret; ++ ++ if (tb->nhi->quirks & QUIRK_RESET_DMA_ON_TEARDOWN) ++ tb_domain_reset_interface(tb); ++ ++ return 0; + } + + static int disconnect_xdomain(struct device *dev, void *data) +diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c +--- a/drivers/thunderbolt/nhi.c ++++ b/drivers/thunderbolt/nhi.c +@@ -235,6 +235,12 @@ static void ring_write_descriptors(struct tb_ring *ring) + { + struct ring_frame *frame, *n; + struct ring_desc *descriptor; ++ u32 flags; ++ ++ flags = RING_DESC_POSTED; ++ if (!(ring->flags & RING_FLAG_NO_INTERRUPT)) ++ flags |= RING_DESC_INTERRUPT; ++ + list_for_each_entry_safe(frame, n, &ring->queue, list) { + if (ring_full(ring)) + break; +@@ -242,7 +248,7 @@ static void ring_write_descriptors(struct tb_ring *ring) + descriptor = &ring->descriptors[ring->head]; + descriptor->phys = frame->buffer_phy; + descriptor->time = 0; +- descriptor->flags = RING_DESC_POSTED | RING_DESC_INTERRUPT; ++ descriptor->flags = flags; + if (ring->is_tx) { + descriptor->length = frame->size; + descriptor->eof = frame->eof; +@@ -339,8 +345,9 @@ EXPORT_SYMBOL_GPL(__tb_ring_enqueue); + * @ring: Ring to poll + * + * This function can be called when @start_poll callback of the @ring +- * has been called. It will read one completed frame from the ring and +- * return it to the caller. ++ * has been called or the ring is created with %RING_FLAG_NO_INTERRUPT. ++ * It will read one completed frame from the ring and return it to the ++ * caller. + * + * Return: Pointer to &struct ring_frame, %NULL if there is no more + * completed frames. +@@ -538,6 +545,12 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, + dev_dbg(nhi->dev, "allocating %s ring %d of size %d\n", + transmit ? "TX" : "RX", hop, size); + ++ if ((flags & RING_FLAG_NO_INTERRUPT) && start_poll) { ++ dev_WARN(nhi->dev, ++ "start_poll() and NO_INTERRUPT cannot be used at the same time\n"); ++ return NULL; ++ } ++ + ring = kzalloc_obj(*ring); + if (!ring) + return NULL; +@@ -568,7 +581,7 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, + if (!ring->descriptors) + goto err_free_ring; + +- if (nhi->ops->request_ring_irq) { ++ if (!(flags & RING_FLAG_NO_INTERRUPT) && nhi->ops->request_ring_irq) { + if (nhi->ops->request_ring_irq(ring, flags & RING_FLAG_NO_SUSPEND)) + goto err_free_descs; + } +@@ -701,7 +714,8 @@ void tb_ring_start(struct tb_ring *ring) + ring_iowrite32options(ring, flags, 0); + } + +- ring_interrupt_active(ring, true); ++ if (!(ring->flags & RING_FLAG_NO_INTERRUPT)) ++ ring_interrupt_active(ring, true); + ring->running = true; + err: + spin_unlock(&ring->lock); +@@ -761,7 +775,8 @@ void tb_ring_stop(struct tb_ring *ring) + RING_TYPE(ring), ring->hop); + goto err; + } +- ring_interrupt_active(ring, false); ++ if (!(ring->flags & RING_FLAG_NO_INTERRUPT)) ++ ring_interrupt_active(ring, false); + + ring_iowrite32options(ring, 0, 0); + ring_iowrite64desc(ring, 0, 0); +@@ -1160,6 +1175,32 @@ static void nhi_reset(struct tb_nhi *nhi) + dev_warn(nhi->dev, "timeout resetting host router\n"); + } + ++/** ++ * nhi_reset_interface() - Reset the host interface ++ * @nhi: Host interface to reset ++ * ++ * Brings the registers in the memory BAR back to their default state and ++ * clears the End-to-End Flow Control state. The caller is responsible for ++ * stopping the control channel over the reset because it clears the ring ++ * state as well. ++ */ ++void nhi_reset_interface(struct tb_nhi *nhi) ++{ ++ u32 val; ++ ++ val = ioread32(nhi->iobase + REG_CAPS); ++ /* Only v1 host interfaces implement the reset */ ++ if (FIELD_GET(REG_CAPS_VERSION_MASK, val) >= REG_CAPS_VERSION_2) ++ return; ++ ++ dev_dbg(nhi->dev, "issuing host interface reset\n"); ++ ++ iowrite32(REG_HOST_INTERFACE_RESET_RST, ++ nhi->iobase + REG_HOST_INTERFACE_RESET); ++ /* Wait for tHIReset (10 ms) to complete */ ++ usleep_range(10000, 20000); ++} ++ + static struct tb *nhi_select_cm(struct tb_nhi *nhi) + { + struct tb *tb; +@@ -1235,6 +1276,8 @@ int nhi_probe(struct tb_nhi *nhi) + + dev_dbg(dev, "NHI initialized, starting thunderbolt\n"); + ++ nhi->host_reset = host_reset; ++ + res = tb_domain_add(tb, host_reset); + if (res) { + /* +diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h +--- a/drivers/thunderbolt/nhi.h ++++ b/drivers/thunderbolt/nhi.h +@@ -36,6 +36,8 @@ irqreturn_t nhi_msi(int irq, void *data); + irqreturn_t ring_msix(int irq, void *data); + int nhi_probe(struct tb_nhi *nhi); + void nhi_shutdown(struct tb_nhi *nhi); ++void nhi_reset_interface(struct tb_nhi *nhi); ++ + extern const struct dev_pm_ops nhi_pm_ops; + + /** +@@ -52,6 +54,7 @@ extern const struct dev_pm_ops nhi_pm_ops; + * @release_ring_irq: NHI specific interrupt release hook + * @is_present: Whether the device is currently present on the parent bus + * @init_interrupts: NHI specific interrupt initialization hook ++ * @reset_interface: Resets the host interface + */ + struct tb_nhi_ops { + int (*init)(struct tb_nhi *nhi); +@@ -66,6 +69,7 @@ struct tb_nhi_ops { + void (*release_ring_irq)(struct tb_ring *ring); + bool (*is_present)(struct tb_nhi *nhi); + int (*init_interrupts)(struct tb_nhi *nhi); ++ void (*reset_interface)(struct tb_nhi *nhi); + }; + + /* +@@ -116,11 +120,24 @@ struct tb_nhi_ops { + #define PCI_DEVICE_ID_INTEL_PTL_P_NHI0 0xe433 + #define PCI_DEVICE_ID_INTEL_PTL_P_NHI1 0xe434 + ++#define PCI_DEVICE_ID_AMD_1AH_M60H_NHI0 0x1120 ++#define PCI_DEVICE_ID_AMD_1AH_M60H_NHI1 0x1121 ++#define PCI_DEVICE_ID_AMD_1AH_M68H_NHI0 0x113b ++#define PCI_DEVICE_ID_AMD_1AH_M68H_NHI1 0x113c ++#define PCI_DEVICE_ID_AMD_1AH_M80H_NHI0 0x1155 ++#define PCI_DEVICE_ID_AMD_1AH_M80H_NHI1 0x1158 ++#define PCI_DEVICE_ID_AMD_1AH_M80H_NHI2 0x1159 ++#define PCI_DEVICE_ID_AMD_1AH_M24H_NHI0 0x151c ++#define PCI_DEVICE_ID_AMD_1AH_M24H_NHI1 0x151d ++#define PCI_DEVICE_ID_AMD_1AH_M70H_NHI0 0x158d ++#define PCI_DEVICE_ID_AMD_1AH_M70H_NHI1 0x158e ++ + #define PCI_CLASS_SERIAL_USB_USB4 0x0c0340 + + /* Host interface quirks */ +-#define QUIRK_AUTO_CLEAR_INT BIT(0) +-#define QUIRK_E2E BIT(1) ++#define QUIRK_AUTO_CLEAR_INT BIT(0) ++#define QUIRK_E2E BIT(1) ++#define QUIRK_RESET_DMA_ON_TEARDOWN BIT(2) + + /* + * Minimal number of vectors when we use MSI-X. Two for control channel +diff --git a/drivers/thunderbolt/nhi_regs.h b/drivers/thunderbolt/nhi_regs.h +--- a/drivers/thunderbolt/nhi_regs.h ++++ b/drivers/thunderbolt/nhi_regs.h +@@ -115,6 +115,10 @@ struct ring_desc { + #define REG_CAPS_VERSION_MASK GENMASK(23, 16) + #define REG_CAPS_VERSION_2 0x40 + ++/* Host Interface Reset - resets TX/RX rings and E2E flow control counters */ ++#define REG_HOST_INTERFACE_RESET 0x39858 ++#define REG_HOST_INTERFACE_RESET_RST BIT(0) ++ + #define REG_DMA_MISC 0x39864 + #define REG_DMA_MISC_INT_AUTO_CLEAR BIT(2) + #define REG_DMA_MISC_DISABLE_AUTO_CLEAR BIT(17) +diff --git a/drivers/thunderbolt/pci.c b/drivers/thunderbolt/pci.c +--- a/drivers/thunderbolt/pci.c ++++ b/drivers/thunderbolt/pci.c +@@ -62,6 +62,27 @@ static void nhi_pci_check_quirks(struct tb_nhi_pci *nhi_pci) + nhi->quirks |= QUIRK_E2E; + break; + } ++ } else if (pdev->vendor == PCI_VENDOR_ID_AMD) { ++ switch (pdev->device) { ++ case PCI_DEVICE_ID_AMD_1AH_M60H_NHI0: ++ case PCI_DEVICE_ID_AMD_1AH_M60H_NHI1: ++ case PCI_DEVICE_ID_AMD_1AH_M68H_NHI0: ++ case PCI_DEVICE_ID_AMD_1AH_M68H_NHI1: ++ case PCI_DEVICE_ID_AMD_1AH_M80H_NHI0: ++ case PCI_DEVICE_ID_AMD_1AH_M80H_NHI1: ++ case PCI_DEVICE_ID_AMD_1AH_M80H_NHI2: ++ case PCI_DEVICE_ID_AMD_1AH_M24H_NHI0: ++ case PCI_DEVICE_ID_AMD_1AH_M24H_NHI1: ++ case PCI_DEVICE_ID_AMD_1AH_M70H_NHI0: ++ case PCI_DEVICE_ID_AMD_1AH_M70H_NHI1: ++ /* ++ * These AMD hosts may hang the Tx ring when the ++ * DMA paths are torn down so they need the host ++ * interface reset after each teardown. ++ */ ++ nhi->quirks |= QUIRK_RESET_DMA_ON_TEARDOWN; ++ break; ++ } + } + } + +@@ -112,7 +133,6 @@ static int nhi_pci_init_msi(struct tb_nhi *nhi) + { + struct tb_nhi_pci *nhi_pci = nhi_to_pci(nhi); + struct pci_dev *pdev = to_pci_dev(nhi->dev); +- struct device *dev = &pdev->dev; + int res, irq, nvec; + + ida_init(&nhi_pci->msix_ida); +@@ -139,7 +159,7 @@ static int nhi_pci_init_msi(struct tb_nhi *nhi) + res = devm_request_irq(&pdev->dev, irq, nhi_msi, + IRQF_NO_SUSPEND, "thunderbolt", nhi); + if (res) +- return dev_err_probe(dev, res, "request_irq failed, aborting\n"); ++ return res; + } + + return 0; +@@ -230,7 +250,7 @@ static void nhi_pci_ring_release_msix(struct tb_ring *ring) + ring->irq = 0; + } + +-static void nhi_pci_shutdown(struct tb_nhi *nhi) ++static void nhi_pci_release_irq(struct tb_nhi *nhi) + { + struct tb_nhi_pci *nhi_pci = nhi_to_pci(nhi); + struct pci_dev *pdev = to_pci_dev(nhi->dev); +@@ -256,9 +276,10 @@ static const struct tb_nhi_ops pci_nhi_default_ops = { + .post_nvm_auth = nhi_pci_complete_dma_port, + .request_ring_irq = nhi_pci_ring_request_msix, + .release_ring_irq = nhi_pci_ring_release_msix, +- .shutdown = nhi_pci_shutdown, ++ .shutdown = nhi_pci_release_irq, + .is_present = nhi_pci_is_present, + .init_interrupts = nhi_pci_init_msi, ++ .reset_interface = nhi_reset_interface, + }; + + /* Ice Lake specific NHI operations */ +@@ -424,7 +445,7 @@ static int icl_nhi_resume(struct tb_nhi *nhi) + + static void icl_nhi_shutdown(struct tb_nhi *nhi) + { +- nhi_pci_shutdown(nhi); ++ nhi_pci_release_irq(nhi); + + icl_nhi_force_power(nhi, false); + } +@@ -442,6 +463,7 @@ static const struct tb_nhi_ops icl_nhi_ops = { + .release_ring_irq = nhi_pci_ring_release_msix, + .is_present = nhi_pci_is_present, + .init_interrupts = nhi_pci_init_msi, ++ .reset_interface = nhi_reset_interface, + }; + + static int nhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +@@ -479,11 +501,19 @@ static int nhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) + return nhi_probe(&nhi_pci->nhi); + } + +-static void nhi_pci_remove(struct pci_dev *pdev) ++static void nhi_pci_do_remove(struct pci_dev *pdev, bool reset) + { + struct tb *tb = pci_get_drvdata(pdev); + struct tb_nhi *nhi = tb->nhi; + ++ /* ++ * On system shutdown/reboot force a host router reset so the ++ * connection manager asserts DPR on connected Thunderbolt 3 devices ++ * before the router tree is removed (see tb_stop()). ++ */ ++ if (reset) ++ nhi->host_reset = true; ++ + pm_runtime_get_sync(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_forbid(&pdev->dev); +@@ -493,6 +523,16 @@ static void nhi_pci_remove(struct pci_dev *pdev) + nhi_shutdown(nhi); + } + ++static void nhi_pci_remove(struct pci_dev *pdev) ++{ ++ nhi_pci_do_remove(pdev, false); ++} ++ ++static void nhi_pci_shutdown(struct pci_dev *pdev) ++{ ++ nhi_pci_do_remove(pdev, true); ++} ++ + static struct pci_device_id nhi_ids[] = { + /* + * We have to specify class, the TB bridges use the same device and +@@ -593,7 +633,7 @@ static struct pci_driver nhi_driver = { + .id_table = nhi_ids, + .probe = nhi_pci_probe, + .remove = nhi_pci_remove, +- .shutdown = nhi_pci_remove, ++ .shutdown = nhi_pci_shutdown, + .driver.pm = &nhi_pm_ops, + }; + +diff --git a/drivers/thunderbolt/stream.c b/drivers/thunderbolt/stream.c +--- a/drivers/thunderbolt/stream.c ++++ b/drivers/thunderbolt/stream.c +@@ -9,10 +9,12 @@ + + #define pr_fmt(fmt) "tbstream: " fmt + ++#include + #include + #include + #include + #include ++#include + #include + #include + #include +@@ -128,6 +130,7 @@ struct tbstream_ring { + * @out_hopid: Out HopID + * @ring_size: Size of the rings + * @throttling: Interrupt throttling rate in ns ++ * @busy_poll: Instead of interrupts, busy poll the rings + * @users: Number of times @cdev has been opened + * @closed: CLOSE packet was received + * @removed: Userspace removed the ConfigFS group underneath. +@@ -147,6 +150,7 @@ struct tbstream_dev { + int out_hopid; + unsigned int ring_size; + unsigned int throttling; ++ bool busy_poll; + int users; + bool closed; + bool removed; +@@ -536,10 +540,39 @@ tbstream_dev_send_data(struct tbstream_dev *sdev, struct iov_iter *from, + return tb_ring_tx(sdev->tx_ring.ring, &sf->frame); + } + ++static void ++tbstream_dev_poll_ring(struct tbstream_dev *sdev, struct tbstream_ring *ring) ++{ ++ struct ring_frame *frame; ++ ++ if (!sdev->busy_poll) ++ return; ++ ++ while ((frame = tb_ring_poll(ring->ring))) ++ frame->callback(ring->ring, frame, false); ++} ++ + static int tbstream_dev_send_close(struct tbstream_dev *sdev) + { + struct tbstream_frame *sf; + ++ if (sdev->busy_poll) { ++ /* ++ * When busy polling it's the write(2) path that ++ * advances the completions so it is possible that the ++ * ring is full at this point. Advance the ring here so ++ * that there is room for the CLOSE packet to be sent. ++ */ ++ ktime_t timeout = ktime_add_ms(ktime_get(), 500); ++ ++ do { ++ if (tbstream_ring_available(&sdev->tx_ring)) ++ break; ++ tbstream_dev_poll_ring(sdev, &sdev->tx_ring); ++ fsleep(15); ++ } while (ktime_before(ktime_get(), timeout)); ++ } ++ + sf = tbstream_dev_alloc_tx(sdev, TBSTREAM_CLOSE, NULL, SZ_256); + if (IS_ERR(sf)) + return PTR_ERR(sf); +@@ -549,12 +582,15 @@ static int tbstream_dev_send_close(struct tbstream_dev *sdev) + static int tbstream_dev_start(struct tbstream_dev *sdev) + { + struct tb_xdomain *xd = tbstream_dev_xdomain(sdev); ++ unsigned int flags = RING_FLAG_FRAME | RING_FLAG_E2E; + u16 sof_mask, eof_mask; + struct tb_ring *ring; + int ret, e2e_tx_hop; + +- ring = tb_ring_alloc_tx(xd->tb->nhi, -1, sdev->ring_size, +- RING_FLAG_FRAME | RING_FLAG_E2E); ++ if (sdev->busy_poll) ++ flags |= RING_FLAG_NO_INTERRUPT; ++ ++ ring = tb_ring_alloc_tx(xd->tb->nhi, -1, sdev->ring_size, flags); + if (!ring) + return -ENOMEM; + sdev->tx_ring.ring = ring; +@@ -567,9 +603,8 @@ static int tbstream_dev_start(struct tbstream_dev *sdev) + sof_mask = BIT(TBSTREAM_FRAME_START); + eof_mask = BIT(TBSTREAM_DATA) | BIT(TBSTREAM_CLOSE); + +- ring = tb_ring_alloc_rx(xd->tb->nhi, -1, sdev->ring_size, +- RING_FLAG_FRAME | RING_FLAG_E2E, e2e_tx_hop, +- sof_mask, eof_mask, NULL, NULL); ++ ring = tb_ring_alloc_rx(xd->tb->nhi, -1, sdev->ring_size, flags, ++ e2e_tx_hop, sof_mask, eof_mask, NULL, NULL); + if (!ring) { + ret = -ENOMEM; + goto err_free_tx_buffers; +@@ -607,15 +642,43 @@ static int tbstream_dev_start(struct tbstream_dev *sdev) + return ret; + } + ++static bool tbstream_dev_tx_drained(const struct tbstream_dev *sdev) ++{ ++ const struct tbstream_ring *ring = &sdev->tx_ring; ++ ++ /* ++ * Everything is completed when number of free TX slots is back ++ * to the maximum. ++ */ ++ return ring->prod - ring->cons == tb_ring_size(ring->ring) - 1; ++} ++ + static void tbstream_dev_stop(struct tbstream_dev *sdev) + { + struct tb_xdomain *xd; + +- /* Wait for the ring to complete any outstanding frames */ +- tb_ring_flush(sdev->tx_ring.ring, 500); +- tb_ring_stop(sdev->tx_ring.ring); +- tb_ring_flush(sdev->rx_ring.ring, 500); +- tb_ring_stop(sdev->rx_ring.ring); ++ if (sdev->busy_poll) { ++ /* ++ * When busy polling we must advance the ring ourselves ++ * to push all outstanding frames on the wire. ++ */ ++ ktime_t timeout = ktime_add_ms(ktime_get(), 500); ++ ++ do { ++ if (tbstream_dev_tx_drained(sdev)) ++ break; ++ tbstream_dev_poll_ring(sdev, &sdev->tx_ring); ++ fsleep(15); ++ } while (ktime_before(ktime_get(), timeout)); ++ ++ tb_ring_stop(sdev->tx_ring.ring); ++ tb_ring_stop(sdev->rx_ring.ring); ++ } else { ++ tb_ring_flush(sdev->tx_ring.ring, 500); ++ tb_ring_stop(sdev->tx_ring.ring); ++ tb_ring_flush(sdev->rx_ring.ring, 500); ++ tb_ring_stop(sdev->rx_ring.ring); ++ } + + xd = tbstream_dev_xdomain(sdev); + if (xd) { +@@ -633,10 +696,24 @@ static void tbstream_dev_stop(struct tbstream_dev *sdev) + sdev->tx_ring.ring = NULL; + } + ++/* Use only with read_iter/write_iter() to handle nowait */ ++static int tbstream_dev_lock(struct tbstream_dev *sdev, bool nowait) ++{ ++ if (nowait) { ++ if (!mutex_trylock(&sdev->lock)) ++ return -EAGAIN; ++ } else { ++ if (mutex_lock_interruptible(&sdev->lock)) ++ return -ERESTARTSYS; ++ } ++ return 0; ++} ++ + static ssize_t + tbstream_dev_fops_read_iter(struct kiocb *kiocb, struct iov_iter *to) + { + struct file *file = kiocb->ki_filp; ++ bool nowait = file->f_flags & O_NONBLOCK || kiocb->ki_flags & IOCB_NOWAIT; + struct tbstream_dev *sdev = to_tbstream_dev(file->private_data); + size_t nbytes; + int ret; +@@ -645,31 +722,50 @@ tbstream_dev_fops_read_iter(struct kiocb *kiocb, struct iov_iter *to) + if (ret) + return ret; + +- if (mutex_lock_interruptible(&sdev->lock)) +- return -ERESTARTSYS; ++ ret = tbstream_dev_lock(sdev, nowait); ++ if (ret) ++ return ret; + +- while (!tbstream_ring_available(&sdev->rx_ring)) { +- mutex_unlock(&sdev->lock); +- +- if (file->f_flags & O_NONBLOCK) +- return -EAGAIN; +- ret = wait_event_interruptible(sdev->wait, +- tbstream_ring_available(&sdev->rx_ring) || +- tbstream_dev_valid(sdev) != 0 || +- tbstream_dev_closed(sdev) || +- tbstream_dev_removed(sdev)); +- if (ret) +- return ret; ++ for (;;) { ++ /* When busy polling, advance any completions manually */ ++ tbstream_dev_poll_ring(sdev, &sdev->rx_ring); + + ret = tbstream_dev_valid(sdev); ++ if (ret) { ++ mutex_unlock(&sdev->lock); ++ return ret; ++ } ++ ++ if (tbstream_dev_closed(sdev) || tbstream_dev_removed(sdev)) { ++ mutex_unlock(&sdev->lock); ++ return 0; ++ } ++ ++ if (tbstream_ring_available(&sdev->rx_ring)) ++ break; ++ ++ mutex_unlock(&sdev->lock); ++ ++ if (nowait) ++ return -EAGAIN; ++ ++ if (sdev->busy_poll) { ++ if (signal_pending(current)) ++ return -ERESTARTSYS; ++ cond_resched(); ++ } else { ++ ret = wait_event_interruptible(sdev->wait, ++ tbstream_ring_available(&sdev->rx_ring) || ++ tbstream_dev_valid(sdev) != 0 || ++ tbstream_dev_closed(sdev) || ++ tbstream_dev_removed(sdev)); ++ if (ret) ++ return ret; ++ } ++ ++ ret = tbstream_dev_lock(sdev, nowait); + if (ret) + return ret; +- +- if (tbstream_dev_closed(sdev) || tbstream_dev_removed(sdev)) +- return 0; +- +- if (mutex_lock_interruptible(&sdev->lock)) +- return -ERESTARTSYS; + } + + nbytes = 0; +@@ -729,6 +825,7 @@ static ssize_t + tbstream_dev_fops_write_iter(struct kiocb *kiocb, struct iov_iter *from) + { + struct file *file = kiocb->ki_filp; ++ bool nowait = file->f_flags & O_NONBLOCK || kiocb->ki_flags & IOCB_NOWAIT; + struct tbstream_dev *sdev = to_tbstream_dev(file->private_data); + size_t nbytes; + int ret; +@@ -737,31 +834,49 @@ tbstream_dev_fops_write_iter(struct kiocb *kiocb, struct iov_iter *from) + if (ret) + return ret; + +- if (mutex_lock_interruptible(&sdev->lock)) +- return -ERESTARTSYS; ++ ret = tbstream_dev_lock(sdev, nowait); ++ if (ret) ++ return ret; + +- while (!tbstream_ring_available(&sdev->tx_ring)) { +- mutex_unlock(&sdev->lock); +- +- if (file->f_flags & O_NONBLOCK) +- return -EAGAIN; +- ret = wait_event_interruptible(sdev->wait, +- tbstream_ring_available(&sdev->tx_ring) || +- tbstream_dev_valid(sdev) != 0 || +- tbstream_dev_closed(sdev) || +- tbstream_dev_removed(sdev)); +- if (ret) +- return ret; ++ for (;;) { ++ tbstream_dev_poll_ring(sdev, &sdev->tx_ring); + + ret = tbstream_dev_valid(sdev); ++ if (ret) { ++ mutex_unlock(&sdev->lock); ++ return ret; ++ } ++ ++ if (tbstream_dev_closed(sdev) || tbstream_dev_removed(sdev)) { ++ mutex_unlock(&sdev->lock); ++ return -ENXIO; ++ } ++ ++ if (tbstream_ring_available(&sdev->tx_ring)) ++ break; ++ ++ mutex_unlock(&sdev->lock); ++ ++ if (nowait) ++ return -EAGAIN; ++ ++ if (sdev->busy_poll) { ++ if (signal_pending(current)) ++ return -ERESTARTSYS; ++ cond_resched(); ++ } else { ++ ret = wait_event_interruptible(sdev->wait, ++ tbstream_ring_available(&sdev->tx_ring) || ++ tbstream_dev_valid(sdev) != 0 || ++ tbstream_dev_closed(sdev) || ++ tbstream_dev_removed(sdev)); ++ if (ret) ++ return ret; ++ } ++ ++ ret = tbstream_dev_lock(sdev, nowait); + if (ret) + return ret; +- +- if (tbstream_dev_closed(sdev) || tbstream_dev_removed(sdev)) +- return -ENXIO; +- +- if (mutex_lock_interruptible(&sdev->lock)) +- return -ERESTARTSYS; + } + + nbytes = 0; +@@ -795,6 +910,13 @@ tbstream_dev_fops_poll(struct file *file, struct poll_table_struct *wait) + struct tbstream_dev *sdev = to_tbstream_dev(file->private_data); + __poll_t mask = 0; + ++ /* ++ * Without interrupts there is nothing that can wake us up so ++ * return failure instead. ++ */ ++ if (sdev->busy_poll) ++ return EPOLLERR; ++ + poll_wait(file, &sdev->wait, wait); + guard(mutex)(&sdev->lock); + if (tbstream_dev_valid(sdev) != 0) { +@@ -904,6 +1026,35 @@ tbstream_dev_from_group(struct config_group *group) + return container_of(group, struct tbstream_dev, group); + } + ++static ssize_t tbstream_dev_busy_poll_show(struct config_item *item, char *buf) ++{ ++ struct config_group *group = to_config_group(item); ++ struct tbstream_dev *sdev = tbstream_dev_from_group(group); ++ ++ return sysfs_emit(buf, "%u\n", sdev->busy_poll); ++} ++ ++static ssize_t ++tbstream_dev_busy_poll_store(struct config_item *item, const char *buf, ++ size_t count) ++{ ++ struct config_group *group = to_config_group(item); ++ struct tbstream_dev *sdev = tbstream_dev_from_group(group); ++ bool busy_poll; ++ int ret; ++ ++ ret = kstrtobool(buf, &busy_poll); ++ if (ret) ++ return ret; ++ ++ guard(mutex)(&sdev->lock); ++ if (sdev->users) ++ return -EBUSY; ++ sdev->busy_poll = busy_poll; ++ return count; ++} ++CONFIGFS_ATTR(tbstream_dev_, busy_poll); ++ + static ssize_t tbstream_dev_index_show(struct config_item *item, char *buf) + { + struct config_group *group = to_config_group(item); +@@ -1210,6 +1361,7 @@ tbstream_dev_throttling_store(struct config_item *item, const char *buf, + CONFIGFS_ATTR(tbstream_dev_, throttling); + + static struct configfs_attribute *tbstream_dev_attrs[] = { ++ &tbstream_dev_attr_busy_poll, + &tbstream_dev_attr_index, + &tbstream_dev_attr_in_hopid, + &tbstream_dev_attr_out_hopid, +@@ -1542,7 +1694,7 @@ static void tbstream_group_detach_stream(struct tbstream *stream) + config_group_put(&sg->group); + } + +-static int tbstream_probe(struct tb_service *svc, const struct tb_service_id *id) ++static int tbstream_probe(struct tb_service *svc) + { + struct tbstream *stream; + +@@ -1632,7 +1784,7 @@ static const struct dev_pm_ops tbstream_pm_ops = { + + static const struct tb_service_id tbstream_ids[] = { + { TB_SERVICE("stream", 1) }, +- { }, ++ { } + }; + MODULE_DEVICE_TABLE(tbsvc, tbstream_ids); + +diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c +--- a/drivers/thunderbolt/switch.c ++++ b/drivers/thunderbolt/switch.c +@@ -682,7 +682,16 @@ int tb_port_disable(struct tb_port *port) + return __tb_port_enable(port, false); + } + +-static int tb_port_reset(struct tb_port *port) ++/** ++ * tb_port_reset() - Reset the port ++ * @port: Port to reset ++ * ++ * Resets @port. For USB4 ports this issues a USB4 port reset and for ++ * legacy ports the link controller port is reset. ++ * ++ * Return: %0 on success, negative errno otherwise. ++ */ ++int tb_port_reset(struct tb_port *port) + { + if (tb_switch_is_usb4(port->sw)) + return port->cap_usb4 ? usb4_port_reset(port) : 0; +diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c +--- a/drivers/thunderbolt/tb.c ++++ b/drivers/thunderbolt/tb.c +@@ -2941,7 +2941,9 @@ static void tb_handle_event(struct tb *tb, enum tb_cfg_pkg_type type, + static void tb_stop(struct tb *tb) + { + struct tb_cm *tcm = tb_priv(tb); ++ struct tb_nhi *nhi = tb->nhi; + struct tb_tunnel *tunnel; ++ struct tb_port *port; + struct tb_tunnel *n; + + cancel_delayed_work(&tcm->remove_work); +@@ -2956,6 +2958,25 @@ static void tb_stop(struct tb *tb) + tb_tunnel_deactivate(tunnel); + tb_tunnel_put(tunnel); + } ++ /* ++ * Signal disconnect to connected devices before the router tree is ++ * removed below. A Thunderbolt 3 device directly connected to a USB4 ++ * host otherwise never receives a disconnect indication, leaving ++ * firmware to poll the dead link for up to ~60 s which on some ++ * platforms turns the shutdown into a warm reset. Asserting ++ * PORT_CS_19.DPR drives SBTX low (USB4 spec section 6.9) so the device ++ * detects SBRX low and goes to Uninitialized Unplugged immediately. ++ */ ++ if (nhi->host_reset) { ++ tb_switch_for_each_port(tb->root_switch, port) { ++ if (!tb_port_is_null(port) || !tb_port_has_remote(port)) ++ continue; ++ if (tb_switch_is_usb4(port->remote->sw)) ++ continue; ++ if (tb_port_reset(port)) ++ tb_port_dbg(port, "downstream port reset failed, continuing\n"); ++ } ++ } + tb_switch_remove(tb->root_switch); + tb->root_switch = NULL; + tcm->hotplug_active = false; /* signal tb_handle_hotplug to quit */ +diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h +--- a/drivers/thunderbolt/tb.h ++++ b/drivers/thunderbolt/tb.h +@@ -1103,6 +1103,7 @@ int tb_port_clear_counter(struct tb_port *port, int counter); + int tb_port_unlock(struct tb_port *port); + int tb_port_enable(struct tb_port *port); + int tb_port_disable(struct tb_port *port); ++int tb_port_reset(struct tb_port *port); + int tb_port_alloc_in_hopid(struct tb_port *port, int hopid, int max_hopid); + void tb_port_release_in_hopid(struct tb_port *port, int hopid); + int tb_port_alloc_out_hopid(struct tb_port *port, int hopid, int max_hopid); +diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c +--- a/drivers/thunderbolt/tunnel.c ++++ b/drivers/thunderbolt/tunnel.c +@@ -48,6 +48,9 @@ + #define TB_DP_AUX_PRIORITY 2 + #define TB_DP_AUX_WEIGHT 1 + ++/* struct tb_regs_hop::initial_credits is 7 bits wide */ ++#define TB_MAX_CREDITS 127 ++ + /* Minimum number of credits needed for PCIe path */ + #define TB_MIN_PCIE_CREDITS 6U + /* +@@ -1778,8 +1781,7 @@ static int tb_dma_reserve_credits(struct tb_path_hop *hop, unsigned int credits) + if (available < TB_MIN_DMA_CREDITS) + return -ENOSPC; + +- while (credits > available) +- credits--; ++ credits = min(credits, available); + + tb_port_dbg(port, "reserving %u credits for DMA path\n", + credits); +@@ -1908,7 +1910,7 @@ struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi, + struct tb_tunnel *tunnel; + size_t npaths = 0, i = 0; + struct tb_path *path; +- int credits; ++ unsigned int credits; + + /* Ring 0 is reserved for control channel */ + if (WARN_ON(!receive_ring || !transmit_ring)) +@@ -1931,6 +1933,11 @@ struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi, + tunnel->destroy = tb_dma_destroy; + + credits = min_not_zero(dma_credits, nhi->sw->max_dma_credits); ++ if (credits > TB_MAX_CREDITS) { ++ tb_tunnel_dbg(tunnel, "%u credits do not fit a hop, using %u\n", ++ credits, TB_MAX_CREDITS); ++ credits = TB_MAX_CREDITS; ++ } + + if (receive_ring > 0) { + path = tb_path_alloc(tb, dst, receive_path, nhi, receive_ring, 0, +diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c +--- a/drivers/thunderbolt/xdomain.c ++++ b/drivers/thunderbolt/xdomain.c +@@ -968,6 +968,9 @@ tb_xdp_schedule_request(struct tb *tb, const struct tb_xdp_header *hdr, + */ + int tb_register_service_driver(struct tb_service_driver *drv) + { ++ if (!drv->probe) ++ return -EINVAL; ++ + drv->driver.bus = &tb_bus_type; + return driver_register(&drv->driver); + } +@@ -1811,6 +1814,7 @@ static void tb_xdomain_state_work(struct work_struct *work) + tb_xdomain_failed(xd); + } else { + xd->state = XDOMAIN_STATE_ENUMERATED; ++ tb_xdomain_queue_properties_changed(xd); + } + break; + +diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h +--- a/include/linux/thunderbolt.h ++++ b/include/linux/thunderbolt.h +@@ -465,7 +465,7 @@ static inline struct tb_service *tb_to_service(struct device *dev) + */ + struct tb_service_driver { + struct device_driver driver; +- int (*probe)(struct tb_service *svc, const struct tb_service_id *id); ++ int (*probe)(struct tb_service *svc); + void (*remove)(struct tb_service *svc); + void (*shutdown)(struct tb_service *svc); + const struct tb_service_id *id_table; +@@ -514,6 +514,11 @@ void tb_service_properties_changed(struct tb_service *svc); + * @hop_count: Number of rings (end point hops) supported by NHI. + * @quirks: NHI specific quirks if any + * @domain_released: Completed when domain has been fully released ++ * @host_reset: Host router was reset on driver load, or forced on system ++ * shutdown/reboot. When set, tb_stop() asserts DPR on connected ++ * downstream ports to signal disconnect before tearing down the ++ * router tree. Only Thunderbolt 3 devices are reset; USB4 ++ * routers are skipped. + */ + struct tb_nhi { + spinlock_t lock; +@@ -528,6 +533,7 @@ struct tb_nhi { + u32 hop_count; + unsigned long quirks; + struct completion domain_released; ++ bool host_reset; + }; + + /** +@@ -592,6 +598,8 @@ struct tb_ring { + #define RING_FLAG_FRAME BIT(1) + /* Enable end-to-end flow control */ + #define RING_FLAG_E2E BIT(2) ++/* Do not enable interrupt for the ring */ ++#define RING_FLAG_NO_INTERRUPT BIT(3) + + struct ring_frame; + typedef void (*ring_cb)(struct tb_ring *, struct ring_frame *, bool canceled); diff --git a/pkgbuilds/linux-omarchy/0601-thunderbolt-updates.patch.sig b/pkgbuilds/linux-omarchy/0601-thunderbolt-updates.patch.sig new file mode 100644 index 0000000..f52f6aa Binary files /dev/null and b/pkgbuilds/linux-omarchy/0601-thunderbolt-updates.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0601-usb4stream-busy-poll.patch b/pkgbuilds/linux-omarchy/0601-usb4stream-busy-poll.patch deleted file mode 100644 index 0cb7d2a..0000000 --- a/pkgbuilds/linux-omarchy/0601-usb4stream-busy-poll.patch +++ /dev/null @@ -1,477 +0,0 @@ -diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c -index 35e3c119d5ee..a4816db5cacd 100644 ---- a/drivers/thunderbolt/nhi.c -+++ b/drivers/thunderbolt/nhi.c -@@ -235,6 +235,12 @@ static void ring_write_descriptors(struct tb_ring *ring) - { - struct ring_frame *frame, *n; - struct ring_desc *descriptor; -+ u32 flags; -+ -+ flags = RING_DESC_POSTED; -+ if (!(ring->flags & RING_FLAG_NO_INTERRUPT)) -+ flags |= RING_DESC_INTERRUPT; -+ - list_for_each_entry_safe(frame, n, &ring->queue, list) { - if (ring_full(ring)) - break; -@@ -242,7 +248,7 @@ static void ring_write_descriptors(struct tb_ring *ring) - descriptor = &ring->descriptors[ring->head]; - descriptor->phys = frame->buffer_phy; - descriptor->time = 0; -- descriptor->flags = RING_DESC_POSTED | RING_DESC_INTERRUPT; -+ descriptor->flags = flags; - if (ring->is_tx) { - descriptor->length = frame->size; - descriptor->eof = frame->eof; -@@ -339,8 +345,9 @@ EXPORT_SYMBOL_GPL(__tb_ring_enqueue); - * @ring: Ring to poll - * - * This function can be called when @start_poll callback of the @ring -- * has been called. It will read one completed frame from the ring and -- * return it to the caller. -+ * has been called or the ring is created with %RING_FLAG_NO_INTERRUPT. -+ * It will read one completed frame from the ring and return it to the -+ * caller. - * - * Return: Pointer to &struct ring_frame, %NULL if there is no more - * completed frames. -@@ -538,6 +545,12 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, - dev_dbg(nhi->dev, "allocating %s ring %d of size %d\n", - transmit ? "TX" : "RX", hop, size); - -+ if ((flags & RING_FLAG_NO_INTERRUPT) && start_poll) { -+ dev_WARN(nhi->dev, -+ "start_poll() and NO_INTERRUPT cannot be used at the same time\n"); -+ return NULL; -+ } -+ - ring = kzalloc_obj(*ring); - if (!ring) - return NULL; -@@ -568,7 +581,7 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, - if (!ring->descriptors) - goto err_free_ring; - -- if (nhi->ops->request_ring_irq) { -+ if (!(flags & RING_FLAG_NO_INTERRUPT) && nhi->ops->request_ring_irq) { - if (nhi->ops->request_ring_irq(ring, flags & RING_FLAG_NO_SUSPEND)) - goto err_free_descs; - } -@@ -701,7 +714,8 @@ void tb_ring_start(struct tb_ring *ring) - ring_iowrite32options(ring, flags, 0); - } - -- ring_interrupt_active(ring, true); -+ if (!(ring->flags & RING_FLAG_NO_INTERRUPT)) -+ ring_interrupt_active(ring, true); - ring->running = true; - err: - spin_unlock(&ring->lock); -@@ -761,7 +775,8 @@ void tb_ring_stop(struct tb_ring *ring) - RING_TYPE(ring), ring->hop); - goto err; - } -- ring_interrupt_active(ring, false); -+ if (!(ring->flags & RING_FLAG_NO_INTERRUPT)) -+ ring_interrupt_active(ring, false); - - ring_iowrite32options(ring, 0, 0); - ring_iowrite64desc(ring, 0, 0); -diff --git a/drivers/thunderbolt/stream.c b/drivers/thunderbolt/stream.c -index 43d29d69dcdb..a7d9fda66875 100644 ---- a/drivers/thunderbolt/stream.c -+++ b/drivers/thunderbolt/stream.c -@@ -9,10 +9,12 @@ - - #define pr_fmt(fmt) "tbstream: " fmt - -+#include - #include - #include - #include - #include -+#include - #include - #include - #include -@@ -128,6 +130,7 @@ struct tbstream_ring { - * @out_hopid: Out HopID - * @ring_size: Size of the rings - * @throttling: Interrupt throttling rate in ns -+ * @busy_poll: Instead of interrupts, busy poll the rings - * @users: Number of times @cdev has been opened - * @closed: CLOSE packet was received - * @removed: Userspace removed the ConfigFS group underneath. -@@ -147,6 +150,7 @@ struct tbstream_dev { - int out_hopid; - unsigned int ring_size; - unsigned int throttling; -+ bool busy_poll; - int users; - bool closed; - bool removed; -@@ -536,10 +540,39 @@ tbstream_dev_send_data(struct tbstream_dev *sdev, struct iov_iter *from, - return tb_ring_tx(sdev->tx_ring.ring, &sf->frame); - } - -+static void -+tbstream_dev_poll_ring(struct tbstream_dev *sdev, struct tbstream_ring *ring) -+{ -+ struct ring_frame *frame; -+ -+ if (!sdev->busy_poll) -+ return; -+ -+ while ((frame = tb_ring_poll(ring->ring))) -+ frame->callback(ring->ring, frame, false); -+} -+ - static int tbstream_dev_send_close(struct tbstream_dev *sdev) - { - struct tbstream_frame *sf; - -+ if (sdev->busy_poll) { -+ /* -+ * When busy polling it's the write(2) path that -+ * advances the completions so it is possible that the -+ * ring is full at this point. Advance the ring here so -+ * that there is room for the CLOSE packet to be sent. -+ */ -+ ktime_t timeout = ktime_add_ms(ktime_get(), 500); -+ -+ do { -+ if (tbstream_ring_available(&sdev->tx_ring)) -+ break; -+ tbstream_dev_poll_ring(sdev, &sdev->tx_ring); -+ fsleep(15); -+ } while (ktime_before(ktime_get(), timeout)); -+ } -+ - sf = tbstream_dev_alloc_tx(sdev, TBSTREAM_CLOSE, NULL, SZ_256); - if (IS_ERR(sf)) - return PTR_ERR(sf); -@@ -549,12 +582,15 @@ static int tbstream_dev_send_close(struct tbstream_dev *sdev) - static int tbstream_dev_start(struct tbstream_dev *sdev) - { - struct tb_xdomain *xd = tbstream_dev_xdomain(sdev); -+ unsigned int flags = RING_FLAG_FRAME | RING_FLAG_E2E; - u16 sof_mask, eof_mask; - struct tb_ring *ring; - int ret, e2e_tx_hop; - -- ring = tb_ring_alloc_tx(xd->tb->nhi, -1, sdev->ring_size, -- RING_FLAG_FRAME | RING_FLAG_E2E); -+ if (sdev->busy_poll) -+ flags |= RING_FLAG_NO_INTERRUPT; -+ -+ ring = tb_ring_alloc_tx(xd->tb->nhi, -1, sdev->ring_size, flags); - if (!ring) - return -ENOMEM; - sdev->tx_ring.ring = ring; -@@ -567,9 +603,8 @@ static int tbstream_dev_start(struct tbstream_dev *sdev) - sof_mask = BIT(TBSTREAM_FRAME_START); - eof_mask = BIT(TBSTREAM_DATA) | BIT(TBSTREAM_CLOSE); - -- ring = tb_ring_alloc_rx(xd->tb->nhi, -1, sdev->ring_size, -- RING_FLAG_FRAME | RING_FLAG_E2E, e2e_tx_hop, -- sof_mask, eof_mask, NULL, NULL); -+ ring = tb_ring_alloc_rx(xd->tb->nhi, -1, sdev->ring_size, flags, -+ e2e_tx_hop, sof_mask, eof_mask, NULL, NULL); - if (!ring) { - ret = -ENOMEM; - goto err_free_tx_buffers; -@@ -607,15 +642,43 @@ static int tbstream_dev_start(struct tbstream_dev *sdev) - return ret; - } - -+static bool tbstream_dev_tx_drained(const struct tbstream_dev *sdev) -+{ -+ const struct tbstream_ring *ring = &sdev->tx_ring; -+ -+ /* -+ * Everything is completed when number of free TX slots is back -+ * to the maximum. -+ */ -+ return ring->prod - ring->cons == tb_ring_size(ring->ring) - 1; -+} -+ - static void tbstream_dev_stop(struct tbstream_dev *sdev) - { - struct tb_xdomain *xd; - -- /* Wait for the ring to complete any outstanding frames */ -- tb_ring_flush(sdev->tx_ring.ring, 500); -- tb_ring_stop(sdev->tx_ring.ring); -- tb_ring_flush(sdev->rx_ring.ring, 500); -- tb_ring_stop(sdev->rx_ring.ring); -+ if (sdev->busy_poll) { -+ /* -+ * When busy polling we must advance the ring ourselves -+ * to push all outstanding frames on the wire. -+ */ -+ ktime_t timeout = ktime_add_ms(ktime_get(), 500); -+ -+ do { -+ if (tbstream_dev_tx_drained(sdev)) -+ break; -+ tbstream_dev_poll_ring(sdev, &sdev->tx_ring); -+ fsleep(15); -+ } while (ktime_before(ktime_get(), timeout)); -+ -+ tb_ring_stop(sdev->tx_ring.ring); -+ tb_ring_stop(sdev->rx_ring.ring); -+ } else { -+ tb_ring_flush(sdev->tx_ring.ring, 500); -+ tb_ring_stop(sdev->tx_ring.ring); -+ tb_ring_flush(sdev->rx_ring.ring, 500); -+ tb_ring_stop(sdev->rx_ring.ring); -+ } - - xd = tbstream_dev_xdomain(sdev); - if (xd) { -@@ -633,10 +696,24 @@ static void tbstream_dev_stop(struct tbstream_dev *sdev) - sdev->tx_ring.ring = NULL; - } - -+/* Use only with read_iter/write_iter() to handle nowait */ -+static int tbstream_dev_lock(struct tbstream_dev *sdev, bool nowait) -+{ -+ if (nowait) { -+ if (!mutex_trylock(&sdev->lock)) -+ return -EAGAIN; -+ } else { -+ if (mutex_lock_interruptible(&sdev->lock)) -+ return -ERESTARTSYS; -+ } -+ return 0; -+} -+ - static ssize_t - tbstream_dev_fops_read_iter(struct kiocb *kiocb, struct iov_iter *to) - { - struct file *file = kiocb->ki_filp; -+ bool nowait = file->f_flags & O_NONBLOCK || kiocb->ki_flags & IOCB_NOWAIT; - struct tbstream_dev *sdev = to_tbstream_dev(file->private_data); - size_t nbytes; - int ret; -@@ -645,31 +722,50 @@ tbstream_dev_fops_read_iter(struct kiocb *kiocb, struct iov_iter *to) - if (ret) - return ret; - -- if (mutex_lock_interruptible(&sdev->lock)) -- return -ERESTARTSYS; -+ ret = tbstream_dev_lock(sdev, nowait); -+ if (ret) -+ return ret; - -- while (!tbstream_ring_available(&sdev->rx_ring)) { -- mutex_unlock(&sdev->lock); -- -- if (file->f_flags & O_NONBLOCK) -- return -EAGAIN; -- ret = wait_event_interruptible(sdev->wait, -- tbstream_ring_available(&sdev->rx_ring) || -- tbstream_dev_valid(sdev) != 0 || -- tbstream_dev_closed(sdev) || -- tbstream_dev_removed(sdev)); -- if (ret) -- return ret; -+ for (;;) { -+ /* When busy polling, advance any completions manually */ -+ tbstream_dev_poll_ring(sdev, &sdev->rx_ring); - - ret = tbstream_dev_valid(sdev); -+ if (ret) { -+ mutex_unlock(&sdev->lock); -+ return ret; -+ } -+ -+ if (tbstream_dev_closed(sdev) || tbstream_dev_removed(sdev)) { -+ mutex_unlock(&sdev->lock); -+ return 0; -+ } -+ -+ if (tbstream_ring_available(&sdev->rx_ring)) -+ break; -+ -+ mutex_unlock(&sdev->lock); -+ -+ if (nowait) -+ return -EAGAIN; -+ -+ if (sdev->busy_poll) { -+ if (signal_pending(current)) -+ return -ERESTARTSYS; -+ cond_resched(); -+ } else { -+ ret = wait_event_interruptible(sdev->wait, -+ tbstream_ring_available(&sdev->rx_ring) || -+ tbstream_dev_valid(sdev) != 0 || -+ tbstream_dev_closed(sdev) || -+ tbstream_dev_removed(sdev)); -+ if (ret) -+ return ret; -+ } -+ -+ ret = tbstream_dev_lock(sdev, nowait); - if (ret) - return ret; -- -- if (tbstream_dev_closed(sdev) || tbstream_dev_removed(sdev)) -- return 0; -- -- if (mutex_lock_interruptible(&sdev->lock)) -- return -ERESTARTSYS; - } - - nbytes = 0; -@@ -729,6 +825,7 @@ static ssize_t - tbstream_dev_fops_write_iter(struct kiocb *kiocb, struct iov_iter *from) - { - struct file *file = kiocb->ki_filp; -+ bool nowait = file->f_flags & O_NONBLOCK || kiocb->ki_flags & IOCB_NOWAIT; - struct tbstream_dev *sdev = to_tbstream_dev(file->private_data); - size_t nbytes; - int ret; -@@ -737,31 +834,49 @@ tbstream_dev_fops_write_iter(struct kiocb *kiocb, struct iov_iter *from) - if (ret) - return ret; - -- if (mutex_lock_interruptible(&sdev->lock)) -- return -ERESTARTSYS; -+ ret = tbstream_dev_lock(sdev, nowait); -+ if (ret) -+ return ret; - -- while (!tbstream_ring_available(&sdev->tx_ring)) { -- mutex_unlock(&sdev->lock); -- -- if (file->f_flags & O_NONBLOCK) -- return -EAGAIN; -- ret = wait_event_interruptible(sdev->wait, -- tbstream_ring_available(&sdev->tx_ring) || -- tbstream_dev_valid(sdev) != 0 || -- tbstream_dev_closed(sdev) || -- tbstream_dev_removed(sdev)); -- if (ret) -- return ret; -+ for (;;) { -+ tbstream_dev_poll_ring(sdev, &sdev->tx_ring); - - ret = tbstream_dev_valid(sdev); -+ if (ret) { -+ mutex_unlock(&sdev->lock); -+ return ret; -+ } -+ -+ if (tbstream_dev_closed(sdev) || tbstream_dev_removed(sdev)) { -+ mutex_unlock(&sdev->lock); -+ return -ENXIO; -+ } -+ -+ if (tbstream_ring_available(&sdev->tx_ring)) -+ break; -+ -+ mutex_unlock(&sdev->lock); -+ -+ if (nowait) -+ return -EAGAIN; -+ -+ if (sdev->busy_poll) { -+ if (signal_pending(current)) -+ return -ERESTARTSYS; -+ cond_resched(); -+ } else { -+ ret = wait_event_interruptible(sdev->wait, -+ tbstream_ring_available(&sdev->tx_ring) || -+ tbstream_dev_valid(sdev) != 0 || -+ tbstream_dev_closed(sdev) || -+ tbstream_dev_removed(sdev)); -+ if (ret) -+ return ret; -+ } -+ -+ ret = tbstream_dev_lock(sdev, nowait); - if (ret) - return ret; -- -- if (tbstream_dev_closed(sdev) || tbstream_dev_removed(sdev)) -- return -ENXIO; -- -- if (mutex_lock_interruptible(&sdev->lock)) -- return -ERESTARTSYS; - } - - nbytes = 0; -@@ -795,6 +910,13 @@ tbstream_dev_fops_poll(struct file *file, struct poll_table_struct *wait) - struct tbstream_dev *sdev = to_tbstream_dev(file->private_data); - __poll_t mask = 0; - -+ /* -+ * Without interrupts there is nothing that can wake us up so -+ * return failure instead. -+ */ -+ if (sdev->busy_poll) -+ return EPOLLERR; -+ - poll_wait(file, &sdev->wait, wait); - guard(mutex)(&sdev->lock); - if (tbstream_dev_valid(sdev) != 0) { -@@ -904,6 +1026,35 @@ tbstream_dev_from_group(struct config_group *group) - return container_of(group, struct tbstream_dev, group); - } - -+static ssize_t tbstream_dev_busy_poll_show(struct config_item *item, char *buf) -+{ -+ struct config_group *group = to_config_group(item); -+ struct tbstream_dev *sdev = tbstream_dev_from_group(group); -+ -+ return sysfs_emit(buf, "%u\n", sdev->busy_poll); -+} -+ -+static ssize_t -+tbstream_dev_busy_poll_store(struct config_item *item, const char *buf, -+ size_t count) -+{ -+ struct config_group *group = to_config_group(item); -+ struct tbstream_dev *sdev = tbstream_dev_from_group(group); -+ bool busy_poll; -+ int ret; -+ -+ ret = kstrtobool(buf, &busy_poll); -+ if (ret) -+ return ret; -+ -+ guard(mutex)(&sdev->lock); -+ if (sdev->users) -+ return -EBUSY; -+ sdev->busy_poll = busy_poll; -+ return count; -+} -+CONFIGFS_ATTR(tbstream_dev_, busy_poll); -+ - static ssize_t tbstream_dev_index_show(struct config_item *item, char *buf) - { - struct config_group *group = to_config_group(item); -@@ -1210,6 +1361,7 @@ tbstream_dev_throttling_store(struct config_item *item, const char *buf, - CONFIGFS_ATTR(tbstream_dev_, throttling); - - static struct configfs_attribute *tbstream_dev_attrs[] = { -+ &tbstream_dev_attr_busy_poll, - &tbstream_dev_attr_index, - &tbstream_dev_attr_in_hopid, - &tbstream_dev_attr_out_hopid, -diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h -index 557288c0274b..8367d106d81b 100644 ---- a/include/linux/thunderbolt.h -+++ b/include/linux/thunderbolt.h -@@ -592,6 +592,8 @@ struct tb_ring { - #define RING_FLAG_FRAME BIT(1) - /* Enable end-to-end flow control */ - #define RING_FLAG_E2E BIT(2) -+/* Do not enable interrupt for the ring */ -+#define RING_FLAG_NO_INTERRUPT BIT(3) - - struct ring_frame; - typedef void (*ring_cb)(struct tb_ring *, struct ring_frame *, bool canceled); diff --git a/pkgbuilds/linux-omarchy/0601-usb4stream-busy-poll.patch.sig b/pkgbuilds/linux-omarchy/0601-usb4stream-busy-poll.patch.sig deleted file mode 100644 index 45711f6..0000000 Binary files a/pkgbuilds/linux-omarchy/0601-usb4stream-busy-poll.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/0602-thunderbolt-fixes.patch b/pkgbuilds/linux-omarchy/0602-thunderbolt-fixes.patch new file mode 100644 index 0000000..f2e51c0 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0602-thunderbolt-fixes.patch @@ -0,0 +1,557 @@ +diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c +--- a/drivers/thunderbolt/ctl.c ++++ b/drivers/thunderbolt/ctl.c +@@ -73,7 +73,6 @@ struct tb_ctl { + #define tb_ctl_dbg_once(ctl, format, arg...) \ + dev_dbg_once((ctl)->nhi->dev, format, ## arg) + +-static DECLARE_WAIT_QUEUE_HEAD(tb_cfg_request_cancel_queue); + /* Serializes access to request kref_get/put */ + static DEFINE_MUTEX(tb_cfg_request_lock); + +@@ -133,41 +132,42 @@ void tb_cfg_request_put(struct tb_cfg_request *req) + static int tb_cfg_request_enqueue(struct tb_ctl *ctl, + struct tb_cfg_request *req) + { ++ tb_cfg_request_get(req); ++ + WARN_ON(test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags)); + WARN_ON(req->ctl); + +- mutex_lock(&ctl->request_queue_lock); ++ guard(mutex)(&ctl->request_queue_lock); + if (!ctl->running) { +- mutex_unlock(&ctl->request_queue_lock); ++ tb_cfg_request_put(req); + return -ENOTCONN; + } + req->ctl = ctl; + list_add_tail(&req->list, &ctl->request_queue); + set_bit(TB_CFG_REQUEST_ACTIVE, &req->flags); +- mutex_unlock(&ctl->request_queue_lock); + return 0; + } + ++static bool tb_cfg_request_is_active(struct tb_cfg_request *req) ++{ ++ return test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags); ++} ++ ++static bool tb_cfg_request_is_canceled(struct tb_cfg_request *req) ++{ ++ return test_bit(TB_CFG_REQUEST_CANCELED, &req->flags); ++} ++ + static void tb_cfg_request_dequeue(struct tb_cfg_request *req) + { + struct tb_ctl *ctl = req->ctl; + +- mutex_lock(&ctl->request_queue_lock); +- if (!test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags)) { +- mutex_unlock(&ctl->request_queue_lock); +- return; ++ guard(mutex)(&ctl->request_queue_lock); ++ if (tb_cfg_request_is_active(req)) { ++ list_del(&req->list); ++ clear_bit(TB_CFG_REQUEST_ACTIVE, &req->flags); ++ tb_cfg_request_put(req); + } +- +- list_del(&req->list); +- clear_bit(TB_CFG_REQUEST_ACTIVE, &req->flags); +- if (test_bit(TB_CFG_REQUEST_CANCELED, &req->flags)) +- wake_up(&tb_cfg_request_cancel_queue); +- mutex_unlock(&ctl->request_queue_lock); +-} +- +-static bool tb_cfg_request_is_active(struct tb_cfg_request *req) +-{ +- return test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags); + } + + static struct tb_cfg_request * +@@ -178,7 +178,7 @@ tb_cfg_request_find(struct tb_ctl *ctl, struct ctl_pkg *pkg) + mutex_lock(&pkg->ctl->request_queue_lock); + list_for_each_entry(iter, &pkg->ctl->request_queue, list) { + tb_cfg_request_get(iter); +- if (iter->match(iter, pkg)) { ++ if (!tb_cfg_request_is_canceled(iter) && iter->match(iter, pkg)) { + req = iter; + break; + } +@@ -512,8 +512,11 @@ static void tb_ctl_rx_callback(struct tb_ring *ring, struct ring_frame *frame, + trace_tb_rx(pkg->ctl->index, frame->eof, pkg->buffer, frame->size, !req); + + if (req) { +- if (req->copy(req, pkg)) +- schedule_work(&req->work); ++ scoped_guard(mutex, &pkg->ctl->request_queue_lock) { ++ if (!tb_cfg_request_is_canceled(req) && ++ req->copy(req, pkg)) ++ schedule_work(&req->work); ++ } + tb_cfg_request_put(req); + } + +@@ -525,11 +528,10 @@ static void tb_cfg_request_work(struct work_struct *work) + { + struct tb_cfg_request *req = container_of(work, typeof(*req), work); + +- if (!test_bit(TB_CFG_REQUEST_CANCELED, &req->flags)) ++ if (!tb_cfg_request_is_canceled(req)) + req->callback(req->callback_data); + + tb_cfg_request_dequeue(req); +- tb_cfg_request_put(req); + } + + /** +@@ -555,10 +557,9 @@ int tb_cfg_request(struct tb_ctl *ctl, struct tb_cfg_request *req, + INIT_WORK(&req->work, tb_cfg_request_work); + INIT_LIST_HEAD(&req->list); + +- tb_cfg_request_get(req); + ret = tb_cfg_request_enqueue(ctl, req); + if (ret) +- goto err_put; ++ return ret; + + ret = tb_ctl_tx(ctl, req->request, req->request_size, + req->request_type); +@@ -572,9 +573,6 @@ int tb_cfg_request(struct tb_ctl *ctl, struct tb_cfg_request *req, + + err_dequeue: + tb_cfg_request_dequeue(req); +-err_put: +- tb_cfg_request_put(req); +- + return ret; + } + +@@ -588,9 +586,10 @@ int tb_cfg_request(struct tb_ctl *ctl, struct tb_cfg_request *req, + */ + void tb_cfg_request_cancel(struct tb_cfg_request *req, int err) + { +- set_bit(TB_CFG_REQUEST_CANCELED, &req->flags); +- schedule_work(&req->work); +- wait_event(tb_cfg_request_cancel_queue, !tb_cfg_request_is_active(req)); ++ scoped_guard(mutex, &req->ctl->request_queue_lock) ++ set_bit(TB_CFG_REQUEST_CANCELED, &req->flags); ++ cancel_work_sync(&req->work); ++ tb_cfg_request_dequeue(req); + req->result.err = err; + } + +diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c +--- a/drivers/thunderbolt/nhi.c ++++ b/drivers/thunderbolt/nhi.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -560,6 +561,8 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, + INIT_LIST_HEAD(&ring->in_flight); + INIT_WORK(&ring->work, ring_work); + init_waitqueue_head(&ring->wait); ++ lockdep_register_key(&ring->lock_key); ++ lockdep_init_map(&ring->work.lockdep_map, "ring.work", &ring->lock_key, 0); + + ring->nhi = nhi; + ring->hop = hop; +@@ -599,6 +602,7 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, + ring->size * sizeof(*ring->descriptors), + ring->descriptors, ring->descriptors_dma); + err_free_ring: ++ lockdep_unregister_key(&ring->lock_key); + kfree(ring); + + return NULL; +@@ -848,6 +852,7 @@ void tb_ring_free(struct tb_ring *ring) + * to finish before freeing the ring. + */ + flush_work(&ring->work); ++ lockdep_unregister_key(&ring->lock_key); + kfree(ring); + } + EXPORT_SYMBOL_GPL(tb_ring_free); +diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c +--- a/drivers/thunderbolt/switch.c ++++ b/drivers/thunderbolt/switch.c +@@ -774,6 +774,7 @@ static int tb_port_alloc_hopid(struct tb_port *port, bool in, int min_hopid, + { + int port_max_hopid; + struct ida *ida; ++ int ret; + + if (in) { + port_max_hopid = port->config.max_in_hop_id; +@@ -793,7 +794,11 @@ static int tb_port_alloc_hopid(struct tb_port *port, bool in, int min_hopid, + if (max_hopid < 0 || max_hopid > port_max_hopid) + max_hopid = port_max_hopid; + +- return ida_alloc_range(ida, min_hopid, max_hopid, GFP_KERNEL); ++ ret = ida_alloc_range(ida, min_hopid, max_hopid, GFP_KERNEL); ++ if (ret >= 0) ++ tb_switch_get(port->sw); ++ ++ return ret; + } + + /** +@@ -832,6 +837,7 @@ int tb_port_alloc_out_hopid(struct tb_port *port, int min_hopid, int max_hopid) + void tb_port_release_in_hopid(struct tb_port *port, int hopid) + { + ida_free(&port->in_hopids, hopid); ++ tb_switch_put(port->sw); + } + + /** +@@ -842,6 +848,7 @@ void tb_port_release_in_hopid(struct tb_port *port, int hopid) + void tb_port_release_out_hopid(struct tb_port *port, int hopid) + { + ida_free(&port->out_hopids, hopid); ++ tb_switch_put(port->sw); + } + + static inline bool tb_switch_is_reachable(const struct tb_switch *parent, +diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c +--- a/drivers/thunderbolt/tb.c ++++ b/drivers/thunderbolt/tb.c +@@ -89,6 +89,7 @@ static void tb_dp_resource_unavailable(struct tb *tb, struct tb_port *port, + const char *reason); + static void tb_queue_dp_bandwidth_request(struct tb *tb, u64 route, u8 port, + int retry, unsigned long delay); ++static void tb_dp_tunnel_active(struct tb_tunnel *tunnel, void *data); + + static void tb_queue_hotplug(struct tb *tb, u64 route, u8 port, bool unplug) + { +@@ -385,7 +386,8 @@ static void tb_switch_discover_tunnels(struct tb_switch *sw, + + switch (port->config.type) { + case TB_TYPE_DP_HDMI_IN: +- tunnel = tb_tunnel_discover_dp(tb, port, alloc_hopids); ++ tunnel = tb_tunnel_discover_dp(tb, port, alloc_hopids, ++ tb_dp_tunnel_active, tb); + tb_increase_tmu_accuracy(tunnel); + break; + +@@ -1910,6 +1912,18 @@ static void tb_dp_tunnel_active(struct tb_tunnel *tunnel, void *data) + struct tb *tb = data; + + mutex_lock(&tb->lock); ++ ++ /* ++ * If the DPRX read was canceled the tunnel is already being torn ++ * down by whoever canceled it. Do not touch the adapters here ++ * because the routers may be gone by now. ++ */ ++ if (tunnel->dprx_canceled) { ++ tb_tunnel_dbg(tunnel, "DPRX read canceled, not activating\n"); ++ mutex_unlock(&tb->lock); ++ return; ++ } ++ + if (tb_tunnel_is_active(tunnel)) { + int consumed_up, consumed_down, ret; + +@@ -1964,8 +1978,6 @@ static void tb_dp_tunnel_active(struct tb_tunnel *tunnel, void *data) + tb_dp_resource_unavailable(tb, in, "DPRX negotiation failed"); + } + mutex_unlock(&tb->lock); +- +- tb_domain_put(tb); + } + + static void tb_tunnel_one_dp(struct tb *tb, struct tb_port *in, +@@ -2026,8 +2038,7 @@ static void tb_tunnel_one_dp(struct tb *tb, struct tb_port *in, + available_up, available_down); + + tunnel = tb_tunnel_alloc_dp(tb, in, out, link_nr, available_up, +- available_down, tb_dp_tunnel_active, +- tb_domain_get(tb)); ++ available_down, tb_dp_tunnel_active, tb); + if (!tunnel) { + tb_port_dbg(out, "could not allocate DP tunnel\n"); + goto err_reclaim_usb; +@@ -2048,7 +2059,6 @@ static void tb_tunnel_one_dp(struct tb *tb, struct tb_port *in, + tb_tunnel_put(tunnel); + err_reclaim_usb: + tb_reclaim_usb3_bandwidth(tb, in, out); +- tb_domain_put(tb); + err_detach_group: + tb_detach_bandwidth_group(in); + err_dealloc_dp: +@@ -2950,11 +2960,12 @@ static void tb_stop(struct tb *tb) + /* tunnels are only present after everything has been initialized */ + list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) { + /* +- * DMA tunnels require the driver to be functional so we +- * tear them down. Other protocol tunnels can be left +- * intact. ++ * DMA tunnels and DP tunnels which are not yet active require ++ * the driver to be functional so we tear them down. ++ * Other protocol tunnels can be left intact. + */ +- if (tb_tunnel_is_dma(tunnel)) ++ if (tb_tunnel_is_dma(tunnel) || ++ (tb_tunnel_is_dp(tunnel) && !tb_tunnel_is_active(tunnel))) + tb_tunnel_deactivate(tunnel); + tb_tunnel_put(tunnel); + } +@@ -3274,11 +3285,11 @@ static void tb_remove_work(struct work_struct *work) + struct tb *tb = tcm_to_tb(tcm); + + mutex_lock(&tb->lock); +- if (tb->root_switch) ++ if (tb->root_switch) { + tb_free_unplugged_children(tb->root_switch); ++ tb_free_unplugged_xdomains(tb->root_switch); ++ } + mutex_unlock(&tb->lock); +- +- tb_free_unplugged_xdomains(tb->root_switch); + } + + static int tb_runtime_resume(struct tb *tb) +diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c +--- a/drivers/thunderbolt/tunnel.c ++++ b/drivers/thunderbolt/tunnel.c +@@ -510,6 +510,7 @@ struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down, + goto err_deactivate; + } + ++ tb_tunnel_set_active(tunnel, true); + tb_tunnel_dbg(tunnel, "discovered\n"); + return tunnel; + +@@ -1093,8 +1094,14 @@ static void tb_dp_dprx_work(struct work_struct *work) + struct tb_tunnel *tunnel = container_of(work, typeof(*tunnel), dprx_work.work); + struct tb *tb = tunnel->tb; + ++ /* ++ * The DPRX read can be canceled while this work is waiting for ++ * tb->lock. Check the flag only once it is held: while the lock is ++ * held the tunnel cannot be torn down under us and the adapters are ++ * safe to access. ++ */ ++ mutex_lock(&tb->lock); + if (!tunnel->dprx_canceled) { +- mutex_lock(&tb->lock); + if (tb_dp_is_usb4(tunnel->src_port->sw) && + tb_dp_wait_dprx(tunnel, TB_DPRX_WAIT_TIMEOUT)) { + if (ktime_before(ktime_get(), tunnel->dprx_timeout)) { +@@ -1106,41 +1113,42 @@ static void tb_dp_dprx_work(struct work_struct *work) + } else { + tb_tunnel_set_active(tunnel, true); + } +- mutex_unlock(&tb->lock); + } ++ mutex_unlock(&tb->lock); + +- if (tunnel->callback) +- tunnel->callback(tunnel, tunnel->callback_data); ++ tunnel->callback(tunnel, tunnel->callback_data); + tb_tunnel_put(tunnel); ++ tb_domain_put(tb); + } + + static int tb_dp_dprx_start(struct tb_tunnel *tunnel) + { + /* +- * Bump up the reference to keep the tunnel around. It will be +- * dropped in tb_dp_dprx_stop() once the tunnel is deactivated. ++ * Bump up the references to keep the tunnel and the domain around ++ * until the work has run or has been canceled. + */ + tb_tunnel_get(tunnel); ++ tb_domain_get(tunnel->tb); + + tunnel->dprx_started = true; ++ tunnel->dprx_canceled = false; ++ tunnel->dprx_timeout = dprx_timeout_to_ktime(dprx_timeout); ++ queue_delayed_work(tunnel->tb->wq, &tunnel->dprx_work, 0); + +- if (tunnel->callback) { +- tunnel->dprx_timeout = dprx_timeout_to_ktime(dprx_timeout); +- queue_delayed_work(tunnel->tb->wq, &tunnel->dprx_work, 0); +- return -EINPROGRESS; +- } +- +- return tb_dp_is_usb4(tunnel->src_port->sw) ? +- tb_dp_wait_dprx(tunnel, dprx_timeout) : 0; ++ return -EINPROGRESS; + } + + static void tb_dp_dprx_stop(struct tb_tunnel *tunnel) + { ++ struct tb *tb = tunnel->tb; ++ + if (tunnel->dprx_started) { + tunnel->dprx_started = false; + tunnel->dprx_canceled = true; +- if (cancel_delayed_work(&tunnel->dprx_work)) ++ if (cancel_delayed_work(&tunnel->dprx_work)) { + tb_tunnel_put(tunnel); ++ tb_domain_put(tb); ++ } + } + } + +@@ -1582,20 +1590,28 @@ static void tb_dp_dump(struct tb_tunnel *tunnel) + * @tb: Pointer to the domain structure + * @in: DP in adapter + * @alloc_hopid: Allocate HopIDs from visited ports ++ * @callback: Callback that is called when the DP tunnel is fully ++ * activated (or there is an error) ++ * @callback_data: Data for @callback + * + * If @in adapter is active, follows the tunnel to the DP out adapter + * and back. Returns the discovered tunnel or %NULL if there was no +- * tunnel. ++ * tunnel. See tb_tunnel_alloc_dp() for @callback. + * + * Return: Pointer to &struct tb_tunnel or %NULL if no tunnel found. + */ + struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in, +- bool alloc_hopid) ++ bool alloc_hopid, ++ void (*callback)(struct tb_tunnel *, void *), ++ void *callback_data) + { + struct tb_tunnel *tunnel; + struct tb_port *port; + struct tb_path *path; + ++ if (WARN_ON(!callback)) ++ return NULL; ++ + if (!tb_dp_port_is_enabled(in)) + return NULL; + +@@ -1611,6 +1627,9 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in, + tunnel->alloc_bandwidth = tb_dp_alloc_bandwidth; + tunnel->consumed_bandwidth = tb_dp_consumed_bandwidth; + tunnel->src_port = in; ++ tunnel->callback = callback; ++ tunnel->callback_data = callback_data; ++ INIT_DELAYED_WORK(&tunnel->dprx_work, tb_dp_dprx_work); + + path = tb_path_discover(in, TB_DP_VIDEO_HOPID, NULL, -1, + &tunnel->dst_port, "Video", alloc_hopid); +@@ -1656,6 +1675,7 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in, + + tb_dp_dump(tunnel); + ++ tb_tunnel_set_active(tunnel, true); + tb_tunnel_dbg(tunnel, "discovered\n"); + return tunnel; + +@@ -1677,16 +1697,16 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in, + * %0 if no available bandwidth. + * @max_down: Maximum available downstream bandwidth for the DP tunnel. + * %0 if no available bandwidth. +- * @callback: Optional callback that is called when the DP tunnel is +- * fully activated (or there is an error) +- * @callback_data: Optional data for @callback ++ * @callback: Callback that is called when the DP tunnel is fully ++ * activated (or there is an error) ++ * @callback_data: Data for @callback + * + * Allocates a tunnel between @in and @out that is capable of tunneling +- * Display Port traffic. If @callback is not %NULL it will be called +- * after tb_tunnel_activate() once the tunnel has been fully activated. +- * It can call tb_tunnel_is_active() to check if activation was +- * successful (or if it returns %false there was some sort of issue). +- * The @callback is called without @tb->lock held. ++ * Display Port traffic. The @callback is called after tb_tunnel_activate() ++ * once the tunnel has been fully activated. It can call ++ * tb_tunnel_is_active() to check if activation was successful (or if it ++ * returns %false there was some sort of issue). The @callback is called ++ * without @tb->lock held. + * + * Return: Pointer to @struct tb_tunnel or %NULL in case of failure. + */ +@@ -1701,7 +1721,7 @@ struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in, + struct tb_path *path; + bool pm_support; + +- if (WARN_ON(!in->cap_adap || !out->cap_adap)) ++ if (WARN_ON(!in->cap_adap || !out->cap_adap || !callback)) + return NULL; + + tunnel = tb_tunnel_alloc(tb, 3, TB_TUNNEL_DP); +@@ -2288,6 +2308,7 @@ struct tb_tunnel *tb_tunnel_discover_usb3(struct tb *tb, struct tb_port *down, + tb_usb3_reclaim_available_bandwidth; + } + ++ tb_tunnel_set_active(tunnel, true); + tb_tunnel_dbg(tunnel, "discovered\n"); + return tunnel; + +diff --git a/drivers/thunderbolt/tunnel.h b/drivers/thunderbolt/tunnel.h +--- a/drivers/thunderbolt/tunnel.h ++++ b/drivers/thunderbolt/tunnel.h +@@ -66,8 +66,8 @@ enum tb_tunnel_state { + * @dprx_canceled: Was DPRX capabilities read poll canceled + * @dprx_timeout: If set DPRX capabilities read poll work will timeout after this passes + * @dprx_work: Worker that is scheduled to poll completion of DPRX capabilities read +- * @callback: Optional callback called when DP tunnel is fully activated +- * @callback_data: Optional data for @callback ++ * @callback: Callback called when DP tunnel is fully activated ++ * @callback_data: Data for @callback + * @paths: All paths required by the tunnel + */ + struct tb_tunnel { +@@ -117,7 +117,9 @@ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up, + bool tb_tunnel_reserved_pci(struct tb_port *port, int *reserved_up, + int *reserved_down); + struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in, +- bool alloc_hopid); ++ bool alloc_hopid, ++ void (*callback)(struct tb_tunnel *, void *), ++ void *callback_data); + struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in, + struct tb_port *out, int link_nr, + int max_up, int max_down, +diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c +--- a/drivers/thunderbolt/xdomain.c ++++ b/drivers/thunderbolt/xdomain.c +@@ -1814,7 +1814,6 @@ static void tb_xdomain_state_work(struct work_struct *work) + tb_xdomain_failed(xd); + } else { + xd->state = XDOMAIN_STATE_ENUMERATED; +- tb_xdomain_queue_properties_changed(xd); + } + break; + +diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h +--- a/include/linux/thunderbolt.h ++++ b/include/linux/thunderbolt.h +@@ -22,6 +22,7 @@ struct device; + #include + #include + #include ++#include + #include + #include + #include +@@ -565,6 +566,7 @@ struct tb_nhi { + * @interval_nsec: Interval counter if interrupt throttling is to be + * used with this ring (in ns) + * @wait: Used to signal that the ring may be empty now ++ * @lock_key: Lock validator class key per-ring + */ + struct tb_ring { + spinlock_t lock; +@@ -590,6 +592,7 @@ struct tb_ring { + void *poll_data; + unsigned int interval_nsec; + wait_queue_head_t wait; ++ struct lock_class_key lock_key; + }; + + /* Leave ring interrupt enabled on suspend */ diff --git a/pkgbuilds/linux-omarchy/0602-thunderbolt-fixes.patch.sig b/pkgbuilds/linux-omarchy/0602-thunderbolt-fixes.patch.sig new file mode 100644 index 0000000..2c4aa9c Binary files /dev/null and b/pkgbuilds/linux-omarchy/0602-thunderbolt-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0603-usb-updates.patch b/pkgbuilds/linux-omarchy/0603-usb-updates.patch new file mode 100644 index 0000000..171a4b4 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0603-usb-updates.patch @@ -0,0 +1,5629 @@ +diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c +--- a/drivers/power/supply/power_supply_core.c ++++ b/drivers/power/supply/power_supply_core.c +@@ -477,6 +477,159 @@ struct power_supply *power_supply_get_by_name(const char *name) + } + EXPORT_SYMBOL_GPL(power_supply_get_by_name); + ++static bool power_supply_is_system_battery(struct power_supply *psy) ++{ ++ union power_supply_propval val; ++ ++ if (psy->desc->type != POWER_SUPPLY_TYPE_BATTERY) ++ return false; ++ ++ if (!power_supply_get_property_direct(psy, POWER_SUPPLY_PROP_SCOPE, ++ &val)) ++ if (val.intval == POWER_SUPPLY_SCOPE_DEVICE) ++ return false; ++ ++ return true; ++} ++ ++static int __power_supply_get_num_system_batteries(struct power_supply *epsy, ++ void *data) ++{ ++ int *count = data; ++ ++ if (power_supply_is_system_battery(epsy)) ++ (*count)++; ++ ++ return 0; ++} ++ ++static int power_supply_get_num_system_batteries(struct device *dev) ++{ ++ int ret, count = 0; ++ ++ ret = power_supply_for_each_psy(&count, ++ __power_supply_get_num_system_batteries); ++ ++ dev_dbg(dev, "%s: count: %d ret %d\n", __func__, count, ret); ++ ++ if (ret) ++ return ret; ++ ++ return count; ++} ++ ++struct psy_get_supplies_data { ++ int cnt; ++ int size; ++ struct power_supply **psys; ++}; ++ ++static int ++__power_supply_populate_system_batteries_array(struct power_supply *epsy, ++ void *_data) ++{ ++ struct psy_get_supplies_data *data = _data; ++ ++ if (power_supply_is_system_battery(epsy)) { ++ if (data->size <= data->cnt) ++ return -EOVERFLOW; ++ ++ get_device(&epsy->dev); ++ data->psys[data->cnt] = epsy; ++ atomic_inc(&epsy->use_cnt); ++ data->cnt++; ++ } ++ ++ return 0; ++} ++ ++static int ++power_supply_populate_system_batteries_array(struct device *dev, int size, ++ struct power_supply **batteries) ++{ ++ int ret; ++ ++ struct psy_get_supplies_data data = { ++ .cnt = 0, ++ .size = size, ++ .psys = batteries, ++ }; ++ ++ ret = power_supply_for_each_psy(&data, ++ __power_supply_populate_system_batteries_array); ++ ++ dev_dbg(dev, "%s Found %d batteries with array size %d ret %d\n", ++ __func__, data.cnt, data.size, ret); ++ ++ if (ret < 0 || !data.cnt) { ++ power_supply_put_system_batteries(batteries, data.cnt); ++ return ret; ++ } ++ ++ return data.cnt; ++} ++ ++/** ++ * power_supply_get_system_batteries() - Fetches references to battery type ++ * power supplies in the system. ++ * @dev: Pointer to device requesting the power supply refs. ++ * @psys: Pointer to an array of power supply refs. ++ * ++ * Helper function to get handles to battery type power supplies in the system. ++ * If acquiring a ref to a power supply fails, then the search for battery type ++ * power supplies will abort and the acquired power supply references will be ++ * released. ++ * ++ * Return: Indicates the number of battery type power supplies returned on ++ * success or a negative error code on failure. ++ * ++ * Call power_supply_put_system_batteries() after use to cleanup resources. ++ */ ++int __must_check power_supply_get_system_batteries(struct device *dev, ++ struct power_supply ***psys) ++{ ++ int ret; ++ ++ if (!psys) ++ return -EINVAL; ++ ++ ret = power_supply_get_num_system_batteries(dev); ++ if (ret <= 0) { ++ *psys = NULL; ++ return ret; ++ } ++ ++ *psys = kzalloc_objs(**psys, ret); ++ if (!*psys) ++ return -ENOMEM; ++ ++ ret = power_supply_populate_system_batteries_array(dev, ret, *psys); ++ if (ret <= 0) ++ *psys = NULL; ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(power_supply_get_system_batteries); ++ ++/** ++ * power_supply_put_system_batteries() - Cleanup resources allocated by ++ * power_supply_get_system_batteries() ++ * @psys: Array of power supply references to release and free. ++ * @count: Number of elements in the array. ++ */ ++void power_supply_put_system_batteries(struct power_supply **psys, int count) ++{ ++ int i; ++ ++ for (i = 0; i < count; i++) { ++ if (psys[i]) ++ power_supply_put(psys[i]); ++ } ++ ++ kfree(psys); ++} ++EXPORT_SYMBOL_GPL(power_supply_put_system_batteries); ++ + /** + * power_supply_put() - Drop reference obtained with power_supply_get_by_name + * @psy: Reference to put +diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c +--- a/drivers/usb/cdns3/cdns3-plat.c ++++ b/drivers/usb/cdns3/cdns3-plat.c +@@ -147,12 +147,12 @@ static int cdns3_plat_probe(struct platform_device *pdev) + cdns->usb2_phy = devm_phy_optional_get(dev, "cdns3,usb2-phy"); + if (IS_ERR(cdns->usb2_phy)) + return dev_err_probe(dev, PTR_ERR(cdns->usb2_phy), +- "Failed to get cdn3,usb2-phy\n"); ++ "Failed to get cdns3,usb2-phy\n"); + + cdns->usb3_phy = devm_phy_optional_get(dev, "cdns3,usb3-phy"); + if (IS_ERR(cdns->usb3_phy)) + return dev_err_probe(dev, PTR_ERR(cdns->usb3_phy), +- "Failed to get cdn3,usb3-phy\n"); ++ "Failed to get cdns3,usb3-phy\n"); + + ret = phy_init(cdns->usb2_phy); + if (ret) +diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c +--- a/drivers/usb/core/config.c ++++ b/drivers/usb/core/config.c +@@ -151,7 +151,7 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, + usb_endpoint_xfer_int(&ep->desc)) && + desc->bmAttributes != 0) { + dev_notice(ddev, "%s endpoint with bmAttributes = %d in config %d interface %d altsetting %d ep 0x%X: setting to zero\n", +- usb_endpoint_xfer_control(&ep->desc) ? "Control" : "Bulk", ++ usb_endpoint_xfer_control(&ep->desc) ? "Control" : "Interrupt", + desc->bmAttributes, + cfgno, inum, asnum, ep->desc.bEndpointAddress); + ep->ss_ep_comp.bmAttributes = 0; +diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c +--- a/drivers/usb/core/devices.c ++++ b/drivers/usb/core/devices.c +@@ -37,6 +37,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -408,7 +409,7 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, + return 0; + /* allocate 2^1 pages = 8K (on i386); + * should be more than enough for one device */ +- pages_start = (char *)__get_free_pages(GFP_NOIO, 1); ++ pages_start = kmalloc(PAGE_SIZE << 1, GFP_NOIO); + if (!pages_start) + return -ENOMEM; + +@@ -479,7 +480,7 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, + if (length > *nbytes) + length = *nbytes; + if (copy_to_user(*buffer, pages_start + *skip_bytes, length)) { +- free_pages((unsigned long)pages_start, 1); ++ kfree(pages_start); + return -EFAULT; + } + *nbytes -= length; +@@ -490,7 +491,7 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, + } else + *skip_bytes -= length; + +- free_pages((unsigned long)pages_start, 1); ++ kfree(pages_start); + + /* Now look at all of this device's children. */ + usb_hub_for_each_child(usbdev, chix, childdev) { +diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c +--- a/drivers/usb/core/devio.c ++++ b/drivers/usb/core/devio.c +@@ -2329,6 +2329,13 @@ static int proc_ioctl(struct usb_dev_state *ps, struct usbdevfs_ioctl *ctl) + if (!connected(ps)) + return -ENODEV; + ++ if (ps->dev->state != USB_STATE_CONFIGURED) ++ return -EHOSTUNREACH; ++ ++ intf = usb_ifnum_to_if(ps->dev, ctl->ifno); ++ if (!intf) ++ return -EINVAL; ++ + /* alloc buffer */ + size = _IOC_SIZE(ctl->ioctl_code); + if (size > 0) { +@@ -2345,11 +2352,7 @@ static int proc_ioctl(struct usb_dev_state *ps, struct usbdevfs_ioctl *ctl) + } + } + +- if (ps->dev->state != USB_STATE_CONFIGURED) +- retval = -EHOSTUNREACH; +- else if (!(intf = usb_ifnum_to_if(ps->dev, ctl->ifno))) +- retval = -EINVAL; +- else switch (ctl->ioctl_code) { ++ switch (ctl->ioctl_code) { + + /* disconnect kernel driver from interface */ + case USBDEVFS_DISCONNECT: +diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c +--- a/drivers/usb/core/hub.c ++++ b/drivers/usb/core/hub.c +@@ -623,11 +623,11 @@ static int hub_ext_port_status(struct usb_hub *hub, int port1, int type, + mutex_lock(&hub->status_mutex); + ret = get_port_status(hub->hdev, port1, &hub->status->port, type, len); + if (ret < len) { +- if (ret != -ENODEV) +- dev_err(hub->intfdev, +- "%s failed (err = %d)\n", __func__, ret); + if (ret >= 0) + ret = -EIO; ++ if (ret != -ENODEV) ++ dev_dbg(hub->intfdev, ++ "get_port_status failed: err = %d\n", ret); + } else { + *status = le16_to_cpu(hub->status->port.wPortStatus); + *change = le16_to_cpu(hub->status->port.wPortChange); +@@ -995,11 +995,11 @@ static int hub_hub_status(struct usb_hub *hub, + mutex_lock(&hub->status_mutex); + ret = get_hub_status(hub->hdev, &hub->status->hub); + if (ret < (int)sizeof(hub->status->hub)) { +- if (ret != -ENODEV) +- dev_err(hub->intfdev, +- "%s failed (err = %d)\n", __func__, ret); + if (ret >= 0) + ret = -EIO; ++ if (ret != -ENODEV) ++ dev_dbg(hub->intfdev, ++ "get_hub_status failed: err = %d\n", ret); + } else { + *status = le16_to_cpu(hub->status->hub.wHubStatus); + *change = le16_to_cpu(hub->status->hub.wHubChange); +diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c +--- a/drivers/usb/dwc2/core_intr.c ++++ b/drivers/usb/dwc2/core_intr.c +@@ -334,9 +334,8 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) + + /** + * dwc2_wakeup_from_lpm_l1 - Exit the device from LPM L1 state +- * + * @hsotg: Programming view of DWC_otg controller +- * ++ * @remotewakeup: Whether this is a remote wakeup + */ + void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg, bool remotewakeup) + { +diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -2188,22 +2188,89 @@ static void dwc3_vbus_draw_work(struct work_struct *work) + ret, dwc->current_limit); + } + +-static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc) ++static int dwc3_psy_notifier(struct notifier_block *nb, ++ unsigned long event, void *data) + { +- struct power_supply *usb_psy; +- const char *usb_psy_name; ++ struct dwc3 *dwc = container_of(nb, struct dwc3, psy_nb); ++ struct power_supply *psy = data; ++ unsigned long flags; ++ ++ if (dwc->usb_psy) ++ return NOTIFY_DONE; ++ ++ if (strcmp(psy->desc->name, dwc->usb_psy_name) != 0) ++ return NOTIFY_DONE; ++ ++ /* Explicitly get the reference for this psy */ ++ psy = power_supply_get_by_name(dwc->usb_psy_name); ++ if (!psy) ++ return NOTIFY_DONE; ++ ++ spin_lock_irqsave(&dwc->lock, flags); ++ /* ++ * The USB power_supply may already be set. This can happen if notifier ++ * callbacks for the USB power_supply race, or if a previous notifier ++ * callback has already successfully fetched and associated the instance. ++ * In such cases, release the newly acquired reference and ignore ++ * subsequent notifications until the notifier is unregistered. ++ */ ++ if (dwc->usb_psy) { ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ power_supply_put(psy); ++ return NOTIFY_DONE; ++ } ++ ++ dwc->usb_psy = psy; ++ if (dwc->current_limit != DWC3_CURRENT_UNSPECIFIED) ++ schedule_work(&dwc->vbus_draw_work); ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ ++ return NOTIFY_OK; ++} ++ ++static void dwc3_get_usb_power_supply(struct dwc3 *dwc) ++{ ++ struct power_supply *psy; ++ unsigned long flags; + int ret; + +- ret = device_property_read_string(dwc->dev, "usb-psy-name", &usb_psy_name); ++ ret = device_property_read_string(dwc->dev, "usb-psy-name", &dwc->usb_psy_name); + if (ret < 0) +- return NULL; +- +- usb_psy = power_supply_get_by_name(usb_psy_name); +- if (!usb_psy) +- return ERR_PTR(-EPROBE_DEFER); ++ return; + + INIT_WORK(&dwc->vbus_draw_work, dwc3_vbus_draw_work); +- return usb_psy; ++ ++ dwc->current_limit = DWC3_CURRENT_UNSPECIFIED; ++ dwc->psy_nb.notifier_call = dwc3_psy_notifier; ++ ret = power_supply_reg_notifier(&dwc->psy_nb); ++ if (ret) { ++ dev_err(dwc->dev, "Failed to register power supply notifier: %d\n", ret); ++ dwc->psy_nb.notifier_call = NULL; ++ return; ++ } ++ ++ psy = power_supply_get_by_name(dwc->usb_psy_name); ++ if (!psy) ++ return; ++ ++ /* Unregister the notifier now that we have the power supply */ ++ power_supply_unreg_notifier(&dwc->psy_nb); ++ dwc->psy_nb.notifier_call = NULL; ++ ++ spin_lock_irqsave(&dwc->lock, flags); ++ /* ++ * It is possible that the notifier callback ran before we reached here ++ * and successfully fetched the power supply. In that case we need to ++ * release the above reference. ++ */ ++ if (dwc->usb_psy) { ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ power_supply_put(psy); ++ return; ++ } ++ ++ dwc->usb_psy = psy; ++ spin_unlock_irqrestore(&dwc->lock, flags); + } + + int dwc3_core_probe(const struct dwc3_probe_data *data) +@@ -2251,9 +2318,9 @@ int dwc3_core_probe(const struct dwc3_probe_data *data) + + dwc3_get_software_properties(dwc, &data->properties); + +- dwc->usb_psy = dwc3_get_usb_power_supply(dwc); +- if (IS_ERR(dwc->usb_psy)) +- return dev_err_probe(dev, PTR_ERR(dwc->usb_psy), "couldn't get usb power supply\n"); ++ spin_lock_init(&dwc->lock); ++ ++ dwc3_get_usb_power_supply(dwc); + + if (!data->ignore_clocks_and_resets) { + dwc->reset = devm_reset_control_array_get_optional_shared(dev); +@@ -2305,7 +2372,6 @@ int dwc3_core_probe(const struct dwc3_probe_data *data) + dwc->num_usb3_ports = 1; + } + +- spin_lock_init(&dwc->lock); + mutex_init(&dwc->mutex); + + pm_runtime_get_noresume(dev); +@@ -2373,6 +2439,8 @@ int dwc3_core_probe(const struct dwc3_probe_data *data) + err_assert_reset: + reset_control_assert(dwc->reset); + err_put_psy: ++ if (dwc->psy_nb.notifier_call) ++ power_supply_unreg_notifier(&dwc->psy_nb); + if (dwc->usb_psy) + power_supply_put(dwc->usb_psy); + +@@ -2429,6 +2497,9 @@ void dwc3_core_remove(struct dwc3 *dwc) + + dwc3_free_event_buffers(dwc); + ++ if (dwc->psy_nb.notifier_call) ++ power_supply_unreg_notifier(&dwc->psy_nb); ++ + if (dwc->usb_psy) { + cancel_work_sync(&dwc->vbus_draw_work); + power_supply_put(dwc->usb_psy); +diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h +--- a/drivers/usb/dwc3/core.h ++++ b/drivers/usb/dwc3/core.h +@@ -722,7 +722,6 @@ struct dwc3_event_buffer { + * @cancelled_list: list of cancelled requests for this endpoint + * @pending_list: list of pending requests for this endpoint + * @started_list: list of started requests on this endpoint +- * @regs: pointer to first endpoint register + * @trb_pool: array of transaction buffers + * @trb_pool_dma: dma address of @trb_pool + * @trb_enqueue: enqueue 'pointer' into TRB array +@@ -1059,6 +1058,8 @@ struct dwc3_glue_ops { + * @role_switch_default_mode: default operation mode of controller while + * usb role is USB_ROLE_NONE. + * @usb_psy: pointer to power supply interface. ++ * @usb_psy_name: name of the USB power supply ++ * @psy_nb: power supply notifier block + * @vbus_draw_work: Work to set the vbus drawing limit + * @current_limit: How much current to draw from vbus, in milliAmperes. + * @usb2_phy: pointer to USB2 PHY +@@ -1251,9 +1252,13 @@ struct dwc3 { + enum usb_dr_mode role_switch_default_mode; + + struct power_supply *usb_psy; ++ const char *usb_psy_name; ++ struct notifier_block psy_nb; + struct work_struct vbus_draw_work; + unsigned int current_limit; + ++#define DWC3_CURRENT_UNSPECIFIED UINT_MAX ++ + u32 fladj; + u32 ref_clk_per; + u32 irq_gadget; +diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c +--- a/drivers/usb/dwc3/dwc3-am62.c ++++ b/drivers/usb/dwc3/dwc3-am62.c +@@ -205,7 +205,9 @@ static int dwc3_ti_init(struct dwc3_am62 *am62) + + dwc3_ti_writel(am62, USBSS_PHY_CONFIG, reg); + +- clk_prepare_enable(am62->usb2_refclk); ++ ret = clk_prepare_enable(am62->usb2_refclk); ++ if (ret) ++ return ret; + + /* Set mode valid bit to indicate role is valid */ + reg = dwc3_ti_readl(am62, USBSS_MODE_CONTROL); +@@ -361,14 +363,19 @@ static int dwc3_ti_resume_common(struct device *dev) + { + struct dwc3_am62 *am62 = dev_get_drvdata(dev); + u32 reg; ++ int ret; + + reg = dwc3_ti_readl(am62, USBSS_DEBUG_CFG); + if (reg != USBSS_DEBUG_CFG_DISABLED) { + /* lost power/context */ +- dwc3_ti_init(am62); ++ ret = dwc3_ti_init(am62); ++ if (ret) ++ return ret; + } else { + dwc3_ti_writel(am62, USBSS_DEBUG_CFG, USBSS_DEBUG_CFG_OFF); +- clk_prepare_enable(am62->usb2_refclk); ++ ret = clk_prepare_enable(am62->usb2_refclk); ++ if (ret) ++ return ret; + } + + if (device_may_wakeup(dev)) { +diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c +--- a/drivers/usb/dwc3/dwc3-qcom.c ++++ b/drivers/usb/dwc3/dwc3-qcom.c +@@ -602,7 +602,7 @@ static void dwc3_qcom_run_stop_notifier(struct dwc3 *dwc, bool is_on) + pm_runtime_mark_last_busy(qcom->dev); + } + +-struct dwc3_glue_ops dwc3_qcom_glue_ops = { ++static struct dwc3_glue_ops dwc3_qcom_glue_ops = { + .pre_set_role = dwc3_qcom_set_role_notifier, + .pre_run_stop = dwc3_qcom_run_stop_notifier, + }; +diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c +--- a/drivers/usb/dwc3/dwc3-xilinx.c ++++ b/drivers/usb/dwc3/dwc3-xilinx.c +@@ -98,18 +98,10 @@ static int dwc3_xlnx_init_versal(struct dwc3_xlnx *priv_data) + + dwc3_xlnx_mask_phy_rst(priv_data, false); + +- /* Assert and De-assert reset */ +- ret = reset_control_assert(crst); +- if (ret < 0) { +- dev_err_probe(dev, ret, "failed to assert Reset\n"); +- return ret; +- } +- +- ret = reset_control_deassert(crst); +- if (ret < 0) { +- dev_err_probe(dev, ret, "failed to De-assert Reset\n"); +- return ret; +- } ++ /* assert and deassert reset */ ++ ret = reset_control_reset(crst); ++ if (ret) ++ return dev_err_probe(dev, ret, "failed to assert and deassert reset\n"); + + dwc3_xlnx_mask_phy_rst(priv_data, true); + dwc3_xlnx_set_coherency(priv_data, XLNX_USB2_TRAFFIC_ROUTE_CONFIG); +@@ -194,7 +186,7 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) + } + + if (priv_data->usb3_phy) { +- /* Set PIPE Power Present signal in FPD Power Present Register*/ ++ /* Set PIPE Power Present signal in FPD Power Present Register */ + writel(FPD_POWER_PRSNT_OPTION, priv_data->regs + XLNX_USB_FPD_POWER_PRSNT); + /* Set the PIPE Clock Select bit in FPD PIPE Clock register */ + writel(PIPE_CLK_SELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK); +diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c +--- a/drivers/usb/dwc3/gadget.c ++++ b/drivers/usb/dwc3/gadget.c +@@ -3129,15 +3129,26 @@ static void dwc3_gadget_set_ssp_rate(struct usb_gadget *g, + static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA) + { + struct dwc3 *dwc = gadget_to_dwc(g); ++ unsigned long flags; + + if (dwc->usb2_phy) + return usb_phy_set_power(dwc->usb2_phy, mA); + +- if (!dwc->usb_psy) +- return -EOPNOTSUPP; ++ spin_lock_irqsave(&dwc->lock, flags); ++ if (!dwc->usb_psy) { ++ if (!dwc->psy_nb.notifier_call) { ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ return -EOPNOTSUPP; ++ } ++ dwc->current_limit = mA; ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ dev_dbg(dwc->dev, "Stored VBUS draw: %u mA (power supply not ready)\n", mA); ++ return 0; ++ } + + dwc->current_limit = mA; + schedule_work(&dwc->vbus_draw_work); ++ spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; + } +diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c +--- a/drivers/usb/dwc3/host.c ++++ b/drivers/usb/dwc3/host.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + + #include "../host/xhci-port.h" + #include "../host/xhci-ext-caps.h" +@@ -46,9 +47,9 @@ static void dwc3_power_off_all_roothub_ports(struct dwc3 *dwc) + return; + } + +- op_regs_base = HC_LENGTH(readl(xhci_regs)); ++ op_regs_base = FIELD_GET(HC_LENGTH, readl(xhci_regs)); + reg = readl(xhci_regs + XHCI_HCSPARAMS1); +- port_num = HCS_MAX_PORTS(reg); ++ port_num = FIELD_GET(HCS_MAX_PORTS, reg); + + for (i = 1; i <= port_num; i++) { + offset = op_regs_base + XHCI_PORTSC_BASE + 0x10 * (i - 1); +diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c +--- a/drivers/usb/gadget/configfs.c ++++ b/drivers/usb/gadget/configfs.c +@@ -125,11 +125,11 @@ static int usb_string_copy(const char *s, char **s_copy) + if (copy) { + str = copy; + } else { +- str = kmalloc(USB_MAX_STRING_WITH_NULL_LEN, GFP_KERNEL); ++ str = kzalloc(USB_MAX_STRING_WITH_NULL_LEN, GFP_KERNEL); + if (!str) + return -ENOMEM; + } +- strcpy(str, s); ++ memcpy(str, s, ret + 1); + if (str[ret - 1] == '\n') + str[ret - 1] = '\0'; + *s_copy = str; +diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c +--- a/drivers/usb/gadget/function/f_fs.c ++++ b/drivers/usb/gadget/function/f_fs.c +@@ -159,7 +159,9 @@ struct ffs_epfile { + struct mutex mutex; + + struct ffs_data *ffs; +- struct ffs_ep *ep; /* P: ffs->eps_lock */ ++ struct ffs_ep *ep; /* P: ffs->eps_lock */ ++ struct ffs_epfile *epfile_in; /* P: ffs->eps_lock */ ++ struct ffs_epfile *epfile_out; /* P: ffs->eps_lock */ + + /* + * Buffer for holding data from partial reads which may happen since +@@ -219,12 +221,13 @@ struct ffs_epfile { + struct ffs_buffer *read_buffer; + #define READ_BUFFER_DROP ((struct ffs_buffer *)ERR_PTR(-ESHUTDOWN)) + +- char name[5]; ++ char name[8]; + + unsigned char in; /* P: ffs->eps_lock */ + unsigned char isoc; /* P: ffs->eps_lock */ + +- unsigned char _pad; ++ u8 zlp_enabled; /* P: ffs->eps_lock */ ++ bool is_rw_proxy; + + /* Protects dmabufs */ + struct mutex dmabufs_mutex; +@@ -991,9 +994,8 @@ static ssize_t __ffs_epfile_read_data(struct ffs_epfile *epfile, + return ret; + } + +-static struct ffs_ep *ffs_epfile_wait_ep(struct file *file) ++static struct ffs_ep *ffs_epfile_wait_ep(struct ffs_epfile *epfile, struct file *file) + { +- struct ffs_epfile *epfile = file->private_data; + struct ffs_ep *ep; + int ret; + +@@ -1020,17 +1022,22 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) + char *data = NULL; + ssize_t ret, data_len = -EINVAL; + int halt; ++ bool is_rw_proxy = epfile->is_rw_proxy; + + /* Are we still active? */ + if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) + return -ENODEV; + +- ep = ffs_epfile_wait_ep(file); ++ /* Proxy to base endpoint if rw_proxy */ ++ if (is_rw_proxy) ++ epfile = io_data->read ? epfile->epfile_out : epfile->epfile_in; ++ ++ ep = ffs_epfile_wait_ep(epfile, file); + if (IS_ERR(ep)) + return PTR_ERR(ep); + + /* Do we halt? */ +- halt = (!io_data->read == !epfile->in); ++ halt = is_rw_proxy ? 0 : (!io_data->read == !epfile->in); + if (halt && epfile->isoc) + return -EINVAL; + +@@ -1127,6 +1134,8 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) + req->buf = data; + req->num_sgs = 0; + } ++ ++ req->zero = !io_data->read ? epfile->zlp_enabled : 0; + req->length = data_len; + + io_data->buf = data; +@@ -1178,6 +1187,8 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) + req->buf = data; + req->num_sgs = 0; + } ++ ++ req->zero = !io_data->read ? epfile->zlp_enabled : 0; + req->length = data_len; + + io_data->buf = data; +@@ -1666,7 +1677,7 @@ static int ffs_dmabuf_transfer(struct file *file, + + priv = attach->importer_priv; + +- ep = ffs_epfile_wait_ep(file); ++ ep = ffs_epfile_wait_ep(epfile, file); + if (IS_ERR(ep)) { + ret = PTR_ERR(ep); + goto err_attachment_put; +@@ -1732,6 +1743,7 @@ static int ffs_dmabuf_transfer(struct file *file, + + /* Now that the dma_fence is in place, queue the transfer. */ + ++ usb_req->zero = epfile->zlp_enabled; + usb_req->length = req->length; + usb_req->buf = NULL; + usb_req->sg = priv->sgt->sgl; +@@ -1779,10 +1791,14 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, + struct ffs_epfile *epfile = file->private_data; + struct ffs_ep *ep; + int ret; ++ __u32 enable_zlp = 0; + + if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) + return -ENODEV; + ++ if (epfile->is_rw_proxy) ++ return -ENOTTY; ++ + switch (code) { + case FUNCTIONFS_DMABUF_ATTACH: + { +@@ -1811,12 +1827,29 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, + + return ffs_dmabuf_transfer(file, &req); + } ++ /* ++ * We handle this IOCTL before ffs_epfile_wait_ep() to allow userspace ++ * to configure ZLP behavior immediately without blocking indefinitely ++ * while waiting for the USB host to connect and enable the endpoint. ++ */ ++ case FUNCTIONFS_ENDPOINT_ENABLE_ZLP: ++ if (!epfile->in) ++ return -EINVAL; ++ ++ if (copy_from_user(&enable_zlp, (void __user *)value, sizeof(enable_zlp))) ++ return -EFAULT; ++ ++ spin_lock_irq(&epfile->ffs->eps_lock); ++ epfile->zlp_enabled = !!enable_zlp; ++ spin_unlock_irq(&epfile->ffs->eps_lock); ++ ++ return 0; + default: + break; + } + + /* Wait for endpoint to be enabled */ +- ep = ffs_epfile_wait_ep(file); ++ ep = ffs_epfile_wait_ep(epfile, file); + if (IS_ERR(ep)) + return PTR_ERR(ep); + +@@ -2214,7 +2247,7 @@ static void ffs_data_closed(struct ffs_data *ffs) + + if (epfiles) + ffs_epfiles_destroy(ffs->sb, epfiles, +- ffs->eps_count); ++ ffs->epfiles_count); + + if (ffs->setup_state == FFS_SETUP_PENDING) + __ffs_ep0_stall(ffs); +@@ -2273,7 +2306,7 @@ static void ffs_data_clear(struct ffs_data *ffs) + * copy of epfile will save us from use-after-free. + */ + if (epfiles) { +- ffs_epfiles_destroy(ffs->sb, epfiles, ffs->eps_count); ++ ffs_epfiles_destroy(ffs->sb, epfiles, ffs->epfiles_count); + ffs->epfiles = NULL; + } + +@@ -2371,11 +2404,16 @@ static void functionfs_unbind(struct ffs_data *ffs) + static int ffs_epfiles_create(struct ffs_data *ffs) + { + struct ffs_epfile *epfile, *epfiles; +- unsigned i, count; ++ unsigned int i, count, epfiles_count; + int err; + + count = ffs->eps_count; +- epfiles = kzalloc_objs(*epfiles, count); ++ epfiles_count = count; ++ if (ffs->user_flags & FUNCTIONFS_RW_PROXY_EPS) ++ epfiles_count += count / 2; ++ ffs->epfiles_count = epfiles_count; ++ ++ epfiles = kzalloc_objs(*epfiles, epfiles_count); + if (!epfiles) + return -ENOMEM; + +@@ -2398,6 +2436,32 @@ static int ffs_epfiles_create(struct ffs_data *ffs) + } + } + ++ if (ffs->user_flags & FUNCTIONFS_RW_PROXY_EPS) { ++ struct ffs_epfile *comp = epfiles + count; ++ ++ for (i = 0; i < count; i += 2, ++comp) { ++ struct ffs_epfile *ep1 = &epfiles[i]; ++ struct ffs_epfile *ep2 = &epfiles[i + 1]; ++ bool ep1_in = ffs->eps_addrmap[i + 1] & USB_ENDPOINT_DIR_MASK; ++ ++ comp->ffs = ffs; ++ comp->is_rw_proxy = true; ++ comp->epfile_in = ep1_in ? ep1 : ep2; ++ comp->epfile_out = ep1_in ? ep2 : ep1; ++ mutex_init(&comp->mutex); ++ mutex_init(&comp->dmabufs_mutex); ++ INIT_LIST_HEAD(&comp->dmabufs); ++ snprintf(comp->name, sizeof(comp->name), "%s_rw", ++ epfiles[i].name); ++ err = ffs_sb_create_file(ffs->sb, comp->name, ++ comp, &ffs_epfile_operations); ++ if (err) { ++ ffs_epfiles_destroy(ffs->sb, epfiles, count + (i / 2)); ++ return err; ++ } ++ } ++ } ++ + ffs->epfiles = epfiles; + return 0; + } +@@ -2975,7 +3039,8 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, + FUNCTIONFS_VIRTUAL_ADDR | + FUNCTIONFS_EVENTFD | + FUNCTIONFS_ALL_CTRL_RECIP | +- FUNCTIONFS_CONFIG0_SETUP)) { ++ FUNCTIONFS_CONFIG0_SETUP | ++ FUNCTIONFS_RW_PROXY_EPS)) { + ret = -ENOSYS; + goto error; + } +@@ -3063,6 +3128,21 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, + goto error; + } + ++ if (ffs->user_flags & FUNCTIONFS_RW_PROXY_EPS) { ++ if (ffs->eps_count % 2) { ++ ret = -EINVAL; ++ goto error; ++ } ++ ++ for (i = 1; i < ffs->eps_count; i += 2) { ++ if ((ffs->eps_addrmap[i] & USB_ENDPOINT_DIR_MASK) == ++ (ffs->eps_addrmap[i + 1] & USB_ENDPOINT_DIR_MASK)) { ++ ret = -EINVAL; ++ goto error; ++ } ++ } ++ } ++ + ffs->raw_descs_data = _data; + ffs->raw_descs = raw_descs; + ffs->raw_descs_length = data - raw_descs; +@@ -3358,7 +3438,7 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, + struct usb_request *req; + struct usb_ep *ep; + u8 bEndpointAddress; +- u16 wMaxPacketSize; ++ __le16 wMaxPacketSize; + + /* + * We back up bEndpointAddress because autoconfig overwrites +diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c +--- a/drivers/usb/gadget/function/f_mass_storage.c ++++ b/drivers/usb/gadget/function/f_mass_storage.c +@@ -203,7 +203,6 @@ + /*------------------------------------------------------------------------*/ + + #define FSG_DRIVER_DESC "Mass Storage Function" +-#define FSG_DRIVER_VERSION "2009/09/11" + + static const char fsg_string_interface[] = "Mass Storage"; + +@@ -3516,8 +3515,6 @@ static struct usb_function_instance *fsg_alloc_inst(void) + if (rc) + goto release_common; + +- pr_info(FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n"); +- + memset(&config, 0, sizeof(config)); + config.removable = true; + rc = fsg_common_create_lun(opts->common, &config, 0, "lun.0", +diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c +--- a/drivers/usb/gadget/function/u_audio.c ++++ b/drivers/usb/gadget/function/u_audio.c +@@ -641,15 +641,15 @@ int u_audio_start_capture(struct g_audio *audio_dev) + ret = config_ep_by_speed(gadget, &audio_dev->func, ep_fback); + if (ret < 0) { + dev_err(dev, "config_ep_by_speed in_ep_fback failed (%d)\n", ret); +- return ret; // TODO: Clean up out_ep ++ goto err_out_ep; + } + +- prm->fb_ep_enabled = true; + ret = usb_ep_enable(ep_fback); + if (ret < 0) { + dev_err(dev, "usb_ep_enable failed for in_ep_fback (%d)\n", ret); +- return ret; // TODO: Clean up out_ep ++ goto err_out_ep; + } ++ prm->fb_ep_enabled = true; + req_len = ep_fback->maxpacket; + + req_fback = usb_ep_alloc_request(ep_fback, GFP_ATOMIC); +@@ -680,6 +680,12 @@ int u_audio_start_capture(struct g_audio *audio_dev) + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); + + return 0; ++ ++err_out_ep: ++ set_active(prm, false); ++ free_ep(prm, ep); ++ ++ return ret; + } + EXPORT_SYMBOL_GPL(u_audio_start_capture); + +diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h +--- a/drivers/usb/gadget/function/u_fs.h ++++ b/drivers/usb/gadget/function/u_fs.h +@@ -252,8 +252,14 @@ struct ffs_data { + + unsigned short strings_count; + unsigned short interfaces_count; ++ ++ /* ++ * eps_count tracks the number of underlying hardware endpoints. ++ * epfiles_count tracks the total number of VFS endpoint files. ++ * When companion endpoints are active, epfiles_count > eps_count. ++ */ + unsigned short eps_count; +- unsigned short _pad1; ++ unsigned short epfiles_count; + + /* filled by __ffs_data_got_strings() */ + /* ids in stringtabs are set in functionfs_bind() */ +diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c +--- a/drivers/usb/gadget/legacy/inode.c ++++ b/drivers/usb/gadget/legacy/inode.c +@@ -471,11 +471,17 @@ static void ep_user_copy_worker(struct work_struct *work) + struct kiocb *iocb = priv->iocb; + size_t ret; + +- kthread_use_mm(mm); +- ret = copy_to_iter(priv->buf, priv->actual, &priv->to); +- kthread_unuse_mm(mm); +- if (!ret) ++ if (mmget_not_zero(mm)) { ++ kthread_use_mm(mm); ++ ret = copy_to_iter(priv->buf, priv->actual, &priv->to); ++ kthread_unuse_mm(mm); ++ mmput(mm); ++ if (!ret) ++ ret = -EFAULT; ++ } else { + ret = -EFAULT; ++ } ++ mmdrop(mm); + + /* completing the iocb can drop the ctx and mm, don't touch mm after */ + iocb->ki_complete(iocb, ret); +@@ -501,6 +507,7 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) + * complete the aio request immediately. + */ + if (priv->to_free == NULL || unlikely(req->actual == 0)) { ++ mmdrop(priv->mm); + kfree(req->buf); + kfree(priv->to_free); + kfree(priv); +@@ -541,6 +548,7 @@ static ssize_t ep_aio(struct kiocb *iocb, + priv->epdata = epdata; + priv->actual = 0; + priv->mm = current->mm; /* mm teardown waits for iocbs in exit_aio() */ ++ mmgrab(priv->mm); + + /* each kiocb is coupled to one usb_request, but we can't + * allocate or submit those if the host disconnected. +@@ -570,6 +578,7 @@ static ssize_t ep_aio(struct kiocb *iocb, + + fail: + spin_unlock_irq(&epdata->dev->lock); ++ mmdrop(priv->mm); + kfree(priv->to_free); + kfree(priv); + put_ep(epdata); +diff --git a/drivers/usb/gadget/legacy/nokia.c b/drivers/usb/gadget/legacy/nokia.c +--- a/drivers/usb/gadget/legacy/nokia.c ++++ b/drivers/usb/gadget/legacy/nokia.c +@@ -50,7 +50,7 @@ static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS; + */ + #define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS + +-#endif /* CONFIG_USB_DEBUG */ ++#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ + + FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); + +diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig +--- a/drivers/usb/gadget/udc/Kconfig ++++ b/drivers/usb/gadget/udc/Kconfig +@@ -90,7 +90,7 @@ config USB_BCM63XX_UDC + + config USB_FSL_USB2 + tristate "Freescale Highspeed USB DR Peripheral Controller" +- depends on FSL_SOC ++ depends on FSL_SOC || COMPILE_TEST + help + Some of Freescale PowerPC and i.MX processors have a High Speed + Dual-Role(DR) USB controller, which supports device mode. +diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c +--- a/drivers/usb/gadget/udc/fsl_qe_udc.c ++++ b/drivers/usb/gadget/udc/fsl_qe_udc.c +@@ -1945,6 +1945,7 @@ static void ch9getstatus(struct qe_udc *udc, u8 request_type, u16 value, + u16 index, u16 length) + { + u16 usb_status = 0; ++ struct usb_request *usb_req; + struct qe_req *req; + struct qe_ep *ep; + int status = 0; +@@ -1983,8 +1984,11 @@ static void ch9getstatus(struct qe_udc *udc, u8 request_type, u16 value, + } + } + +- req = container_of(qe_alloc_request(&ep->ep, GFP_KERNEL), +- struct qe_req, req); ++ usb_req = qe_alloc_request(&ep->ep, GFP_KERNEL); ++ if (!usb_req) ++ goto stall; ++ ++ req = container_of(usb_req, struct qe_req, req); + req->req.length = 2; + req->req.buf = udc->statusbuf; + *(u16 *)req->req.buf = cpu_to_le16(usb_status); +diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c +--- a/drivers/usb/gadget/udc/pch_udc.c ++++ b/drivers/usb/gadget/udc/pch_udc.c +@@ -320,7 +320,6 @@ struct pch_vbus_gpio_data { + * @lock: protects all state + * @stall: stall requested + * @prot_stall: protcol stall requested +- * @registered: driver registered with system + * @suspended: driver in suspended state + * @connected: gadget driver associated + * @vbus_session: required vbus_session state +diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c +--- a/drivers/usb/host/fsl-mph-dr-of.c ++++ b/drivers/usb/host/fsl-mph-dr-of.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + + struct fsl_usb2_dev_data { + char *dr_mode; /* controller mode */ +@@ -183,7 +184,7 @@ static int fsl_usb2_mph_dr_of_probe(struct platform_device *ofdev) + const struct of_device_id *match; + const unsigned char *prop; + static unsigned int idx; +- int i; ++ int i, err; + + if (!of_device_is_available(np)) + return -ENODEV; +@@ -246,6 +247,10 @@ static int fsl_usb2_mph_dr_of_probe(struct platform_device *ofdev) + } + } + ++ err = devm_regulator_get_enable_optional(&ofdev->dev, "vbus"); ++ if (err) ++ return dev_err_probe(&ofdev->dev, err, "failed to get vbus regulator\n"); ++ + for (i = 0; i < ARRAY_SIZE(dev_data->drivers); i++) { + if (!dev_data->drivers[i]) + continue; +diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c +--- a/drivers/usb/host/ohci-dbg.c ++++ b/drivers/usb/host/ohci-dbg.c +@@ -683,7 +683,7 @@ static int fill_buffer(struct debug_buffer *buf) + int ret; + + if (!buf->page) +- buf->page = (char *)get_zeroed_page(GFP_KERNEL); ++ buf->page = kzalloc(PAGE_SIZE, GFP_KERNEL); + + if (!buf->page) { + ret = -ENOMEM; +@@ -729,11 +729,8 @@ static int debug_close(struct inode *inode, struct file *file) + { + struct debug_buffer *buf = file->private_data; + +- if (buf) { +- if (buf->page) +- free_page((unsigned long)buf->page); +- kfree(buf); +- } ++ kfree(buf->page); ++ kfree(buf); + + return 0; + } +diff --git a/drivers/usb/host/xhci-caps.h b/drivers/usb/host/xhci-caps.h +--- a/drivers/usb/host/xhci-caps.h ++++ b/drivers/usb/host/xhci-caps.h +@@ -5,22 +5,23 @@ + */ + + #include ++#include + + /* hc_capbase - bitmasks */ + /* bits 7:0 - Capability Registers Length */ +-#define HC_LENGTH(p) ((p) & 0xff) ++#define HC_LENGTH GENMASK(7, 0) + /* bits 15:8 - Rsvd */ + /* bits 31:16 - Host Controller Interface Version Number */ +-#define HC_VERSION(p) (((p) >> 16) & 0xffff) ++#define HC_VERSION GENMASK(31, 16) + + /* HCSPARAMS1 - hcs_params1 - bitmasks */ + /* bits 7:0 - Number of Device Slots */ +-#define HCS_MAX_SLOTS(p) (((p) >> 0) & 0xff) +-#define HCS_SLOTS_MASK 0xff +-/* bits 18:8 - Number of Interrupters, max values is 1024 */ +-#define HCS_MAX_INTRS(p) (((p) >> 8) & 0x7ff) +-/* bits 31:24, Max Ports - max value is 255 */ +-#define HCS_MAX_PORTS(p) (((p) >> 24) & 0xff) ++#define HCS_SLOTS_MASK GENMASK(7, 0) ++/* bits 18:8 - Number of Interrupters, max values is 1024 */ ++#define HCS_MAX_INTRS GENMASK(18, 8) ++/* bits 23:19 - Rsvd */ ++/* bits 31:24 - Max Ports, max values is 255 */ ++#define HCS_MAX_PORTS GENMASK(31, 24) + + /* HCSPARAMS2 - hcs_params2 - bitmasks */ + /* +@@ -33,24 +34,25 @@ + * Note: 1 Frame = 8 Microframes + * xHCI specification section 5.3.4. + */ +-#define HCS_IST_VALUE(p) ((p) & 0x7) ++#define HCS_IST_VALUE GENMASK(2, 0) + #define HCS_IST_UNIT BIT(3) + /* bits 7:4 - Event Ring Segment Table Max, 2^(n) */ +-#define HCS_ERST_MAX(p) (((p) >> 4) & 0xf) ++#define HCS_ERST_MAX GENMASK(7, 4) + /* bits 20:8 - Rsvd */ + /* bits 25:21 - Max Scratchpad Buffers (Hi), 5 Most significant bits */ +-#define HCS_MAX_SP_HI(p) (((p) >> 21) & 0x1f) ++#define HCS_MAX_SP_HI GENMASK(25, 21) + /* bit 26 - Scratchpad restore, for save/restore HW state */ + /* bits 31:27 - Max Scratchpad Buffers (Lo), 5 Least significant bits */ +-#define HCS_MAX_SP_LO(p) (((p) >> 27) & 0x1f) +-#define HCS_MAX_SCRATCHPAD(p) (HCS_MAX_SP_HI(p) << 5 | HCS_MAX_SP_LO(p)) ++#define HCS_MAX_SP_LO GENMASK(31, 27) ++#define HCS_MAX_SCRATCHPAD(p) (FIELD_GET(HCS_MAX_SP_HI, (p)) << 5 | \ ++ FIELD_GET(HCS_MAX_SP_LO, (p))) + + /* HCSPARAMS3 - hcs_params3 - bitmasks */ + /* bits 7:0 - U1 Device Exit Latency, Max U1 to U0 latency for the roothub ports */ +-#define HCS_U1_LATENCY(p) (((p) >> 0) & 0xff) ++#define HCS_U1_LATENCY GENMASK(7, 0) + /* bits 15:8 - Rsvd */ + /* bits 31:16 - U2 Device Exit Latency, Max U2 to U0 latency for the roothub ports */ +-#define HCS_U2_LATENCY(p) (((p) >> 16) & 0xffff) ++#define HCS_U2_LATENCY GENMASK(31, 16) + + /* HCCPARAMS1 - hcc_params - bitmasks */ + /* bit 0 - 64-bit Addressing Capability */ +@@ -77,19 +79,20 @@ + /* bit 11 - Contiguous Frame ID Capability */ + #define HCC_CFC BIT(11) + /* bits 15:12 - Max size for Primary Stream Arrays, 2^(n+1) */ +-#define HCC_MAX_PSA(p) (1 << ((((p) >> 12) & 0xf) + 1)) ++#define HCC_MAX_PSA GENMASK(15, 12) ++#define GET_MAX_PSA_SIZE(p) (1 << (FIELD_GET(HCC_MAX_PSA, (p)) + 1)) + /* bits 31:16 - xHCI Extended Capabilities Pointer, from PCI base: 2^(n) */ +-#define HCC_EXT_CAPS(p) (((p) >> 16) & 0xffff) ++#define HCC_EXT_CAPS GENMASK(31, 16) + + /* DBOFF - db_off - bitmasks */ + /* bits 1:0 - Rsvd */ + /* bits 31:2 - Doorbell Array Offset */ +-#define DBOFF_MASK (0xfffffffc) ++#define DBOFF_MASK GENMASK(31, 2) + + /* RTSOFF - run_regs_off - bitmasks */ + /* bits 4:0 - Rsvd */ + /* bits 31:5 - Runtime Register Space Offse */ +-#define RTSOFF_MASK (~0x1f) ++#define RTSOFF_MASK GENMASK(31, 5) + + /* HCCPARAMS2 - hcc_params2 - bitmasks */ + /* bit 0 - U3 Entry Capability */ +diff --git a/drivers/usb/host/xhci-dbgtty.c b/drivers/usb/host/xhci-dbgtty.c +--- a/drivers/usb/host/xhci-dbgtty.c ++++ b/drivers/usb/host/xhci-dbgtty.c +@@ -665,11 +665,11 @@ int dbc_tty_init(void) + + void dbc_tty_exit(void) + { +- if (dbc_tty_driver) { +- tty_unregister_driver(dbc_tty_driver); +- tty_driver_kref_put(dbc_tty_driver); +- dbc_tty_driver = NULL; +- } ++ if (IS_ERR_OR_NULL(dbc_tty_driver)) ++ return; + ++ tty_unregister_driver(dbc_tty_driver); ++ tty_driver_kref_put(dbc_tty_driver); + idr_destroy(&dbc_tty_minors); ++ dbc_tty_driver = NULL; + } +diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c +--- a/drivers/usb/host/xhci-debugfs.c ++++ b/drivers/usb/host/xhci-debugfs.c +@@ -9,6 +9,7 @@ + + #include + #include ++#include + + #include "xhci.h" + #include "xhci-debugfs.h" +@@ -791,7 +792,7 @@ void xhci_debugfs_init(struct xhci_hcd *xhci) + xhci->debugfs_root, "reg-cap"); + + xhci_debugfs_regset(xhci, +- HC_LENGTH(readl(&xhci->cap_regs->hc_capbase)), ++ FIELD_GET(HC_LENGTH, readl(&xhci->cap_regs->hc_capbase)), + xhci_op_regs, ARRAY_SIZE(xhci_op_regs), + xhci->debugfs_root, "reg-op"); + +diff --git a/drivers/usb/host/xhci-histb.c b/drivers/usb/host/xhci-histb.c +--- a/drivers/usb/host/xhci-histb.c ++++ b/drivers/usb/host/xhci-histb.c +@@ -276,7 +276,7 @@ static int xhci_histb_probe(struct platform_device *pdev) + if (ret) + goto put_usb3_hcd; + +- if (HCC_MAX_PSA(xhci->hcc_params) >= 4) ++ if (GET_MAX_PSA_SIZE(xhci->hcc_params) >= 4) + xhci->shared_hcd->can_do_streams = 1; + + ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED); +diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c +--- a/drivers/usb/host/xhci-hub.c ++++ b/drivers/usb/host/xhci-hub.c +@@ -115,8 +115,8 @@ static int xhci_create_usb3x_bos_desc(struct xhci_hcd *xhci, char *buf, + + if ((xhci->quirks & XHCI_LPM_SUPPORT)) { + reg = readl(&xhci->cap_regs->hcs_params3); +- ss_cap->bU1devExitLat = HCS_U1_LATENCY(reg); +- ss_cap->bU2DevExitLat = cpu_to_le16(HCS_U2_LATENCY(reg)); ++ ss_cap->bU1devExitLat = FIELD_GET(HCS_U1_LATENCY, reg); ++ ss_cap->bU2DevExitLat = cpu_to_le16(FIELD_GET(HCS_U2_LATENCY, reg)); + } + + if (wLength < le16_to_cpu(bos->wTotalLength)) +@@ -1295,7 +1295,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + } + /* In spec software should not attempt to suspend + * a port unless the port reports that it is in the +- * enabled (PED = ‘1’,PLS < ‘3’) state. ++ * enabled (PED = '1',PLS < '3') state. + */ + portsc = xhci_portsc_readl(port); + if ((portsc & PORT_PE) == 0 || (portsc & PORT_RESET) || +diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c +--- a/drivers/usb/host/xhci-mem.c ++++ b/drivers/usb/host/xhci-mem.c +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + + #include "xhci.h" + #include "xhci-trace.h" +@@ -1001,7 +1002,6 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, + for (i = 0; i < 31; i++) { + dev->eps[i].ep_index = i; + dev->eps[i].vdev = dev; +- dev->eps[i].xhci = xhci; + INIT_LIST_HEAD(&dev->eps[i].cancelled_td_list); + INIT_LIST_HEAD(&dev->eps[i].bw_endpoint_list); + } +@@ -1493,6 +1493,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, + return -ENOMEM; + + virt_dev->eps[ep_index].skip = false; ++ virt_dev->eps[ep_index].next_uframe = -1; + ep_ring = virt_dev->eps[ep_index].new_ring; + xhci_ring_init(xhci, ep_ring); + +@@ -2083,7 +2084,7 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, + addr, port_offset, port_count, major_revision); + /* Port count includes the current port offset */ + if (port_offset == 0 || (port_offset + port_count - 1) > num_ports) +- /* WTF? "Valid values are ‘1’ to MaxPorts" */ ++ /* WTF? "Valid values are '1' to MaxPorts" */ + return; + + port_cap = &xhci->port_caps[xhci->num_port_caps++]; +@@ -2300,7 +2301,7 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int segs, gfp_t flags) + if (!segs) + segs = ERST_DEFAULT_SEGS; + +- max_segs = BIT(HCS_ERST_MAX(xhci->hcs_params2)); ++ max_segs = FIELD_GET(HCS_ERST_MAX, xhci->hcs_params2) << 2; + segs = min(segs, max_segs); + + ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev)); +diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c +--- a/drivers/usb/host/xhci-mtk.c ++++ b/drivers/usb/host/xhci-mtk.c +@@ -468,7 +468,7 @@ static void xhci_mtk_quirks(struct device *dev, struct xhci_hcd *xhci) + * MTK xHCI 0.96: PSA is 1 by default even if doesn't support stream, + * and it's 3 when support it. + */ +- if (xhci->hci_version < 0x100 && HCC_MAX_PSA(xhci->hcc_params) == 4) ++ if (xhci->hci_version < 0x100 && GET_MAX_PSA_SIZE(xhci->hcc_params) == 4) + xhci->quirks |= XHCI_BROKEN_STREAMS; + } + +@@ -650,7 +650,7 @@ static int xhci_mtk_probe(struct platform_device *pdev) + } + + usb3_hcd = xhci_get_usb3_hcd(xhci); +- if (usb3_hcd && HCC_MAX_PSA(xhci->hcc_params) >= 4 && ++ if (usb3_hcd && GET_MAX_PSA_SIZE(xhci->hcc_params) >= 4 && + !(xhci->quirks & XHCI_BROKEN_STREAMS)) + usb3_hcd->can_do_streams = 1; + +diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c +--- a/drivers/usb/host/xhci-pci.c ++++ b/drivers/usb/host/xhci-pci.c +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + + #include "xhci.h" + #include "xhci-trace.h" +@@ -662,7 +663,8 @@ int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id) + } + + usb3_hcd = xhci_get_usb3_hcd(xhci); +- if (usb3_hcd && !(xhci->quirks & XHCI_BROKEN_STREAMS) && HCC_MAX_PSA(xhci->hcc_params) >= 4) ++ if (usb3_hcd && !(xhci->quirks & XHCI_BROKEN_STREAMS) && ++ GET_MAX_PSA_SIZE(xhci->hcc_params) >= 4) + usb3_hcd->can_do_streams = 1; + + /* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */ +diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c +--- a/drivers/usb/host/xhci-plat.c ++++ b/drivers/usb/host/xhci-plat.c +@@ -340,7 +340,7 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s + } + + usb3_hcd = xhci_get_usb3_hcd(xhci); +- if (usb3_hcd && HCC_MAX_PSA(xhci->hcc_params) >= 4 && ++ if (usb3_hcd && GET_MAX_PSA_SIZE(xhci->hcc_params) >= 4 && + !(xhci->quirks & XHCI_BROKEN_STREAMS)) + usb3_hcd->can_do_streams = 1; + +diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -57,6 +57,8 @@ + #include + #include + #include ++#include ++ + #include "xhci.h" + #include "xhci-trace.h" + +@@ -561,8 +563,8 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, + * pointer command pending because the device can choose to start any + * stream once the endpoint is on the HW schedule. + */ +- if ((ep_state & EP_STOP_CMD_PENDING) || (ep_state & SET_DEQ_PENDING) || +- (ep_state & EP_HALTED) || (ep_state & EP_CLEARING_TT)) ++ if (ep_state & (EP_STOP_CMD_PENDING | SET_DEQ_PENDING | EP_HALTED | ++ EP_CLEARING_TT | EP_DROP_PENDING)) + return; + + trace_xhci_ring_ep_doorbell(slot_id, DB_VALUE(ep_index, stream_id)); +@@ -573,9 +575,8 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, + } + + /* Ring the doorbell for any rings with pending URBs */ +-static void ring_doorbell_for_active_rings(struct xhci_hcd *xhci, +- unsigned int slot_id, +- unsigned int ep_index) ++void xhci_ring_doorbell_for_active_rings(struct xhci_hcd *xhci, unsigned int slot_id, ++ unsigned int ep_index) + { + unsigned int stream_id; + struct xhci_virt_ep *ep; +@@ -598,13 +599,6 @@ static void ring_doorbell_for_active_rings(struct xhci_hcd *xhci, + } + } + +-void xhci_ring_doorbell_for_active_rings(struct xhci_hcd *xhci, +- unsigned int slot_id, +- unsigned int ep_index) +-{ +- ring_doorbell_for_active_rings(xhci, slot_id, ep_index); +-} +- + static struct xhci_virt_ep *xhci_get_virt_ep(struct xhci_hcd *xhci, + unsigned int slot_id, + unsigned int ep_index) +@@ -686,112 +680,100 @@ static u64 xhci_get_hw_deq(struct xhci_hcd *xhci, struct xhci_virt_device *vdev, + return le64_to_cpu(ep_ctx->deq); + } + +-static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, +- unsigned int slot_id, unsigned int ep_index, +- unsigned int stream_id, struct xhci_td *td) ++/* ++ * Move the endpoint dequeue pointer to the next queued TD on ring->td_list or ++ * to enqueue if no TDs are queued (empty ring) ++ * All cancelled TDs on ring->td_list should be moved to ep->cancelled_td_list ++ * before calling this function ++ */ ++static int xhci_move_deq_to_next_td(struct xhci_hcd *xhci, ++ struct xhci_virt_ep *ep, ++ unsigned int stream_id) + { +- struct xhci_virt_device *dev = xhci->devs[slot_id]; +- struct xhci_virt_ep *ep = &dev->eps[ep_index]; +- struct xhci_ring *ep_ring; + struct xhci_command *cmd; +- struct xhci_segment *new_seg; +- union xhci_trb *new_deq; +- int new_cycle; ++ struct xhci_ring *ring; ++ struct xhci_td *td; + dma_addr_t addr; +- u64 hw_dequeue; +- bool hw_dequeue_found = false; +- bool td_last_trb_found = false; ++ int new_cycle; + u32 trb_sct = 0; +- int ret; ++ int ret = 0; + +- ep_ring = xhci_triad_to_transfer_ring(xhci, slot_id, +- ep_index, stream_id); +- if (!ep_ring) { ++ ring = xhci_virt_ep_to_ring(xhci, ep, stream_id); ++ if (!ring) { + xhci_warn(xhci, "WARN can't find new dequeue, invalid stream ID %u\n", + stream_id); + return -ENODEV; + } + +- hw_dequeue = xhci_get_hw_deq(xhci, dev, ep_index, stream_id) & TR_DEQ_PTR_MASK; +- new_seg = ep_ring->deq_seg; +- new_deq = ep_ring->dequeue; +- new_cycle = le32_to_cpu(td->end_trb->generic.field[3]) & TRB_CYCLE; +- +- /* +- * Walk the ring until both the next TRB and hw_dequeue are found (don't +- * move hw_dequeue back if it went forward due to a HW bug). Cycle state +- * is loaded from a known good TRB, track later toggles to maintain it. +- */ +- do { +- if (!hw_dequeue_found && xhci_trb_virt_to_dma(new_seg, new_deq) +- == (dma_addr_t)hw_dequeue) { +- hw_dequeue_found = true; +- if (td_last_trb_found) +- break; +- } +- if (new_deq == td->end_trb) +- td_last_trb_found = true; +- +- if (td_last_trb_found && trb_is_link(new_deq) && +- link_trb_toggles_cycle(new_deq)) +- new_cycle ^= 0x1; +- +- next_trb(&new_seg, &new_deq); +- +- /* Search wrapped around, bail out */ +- if (new_deq == ep->ring->dequeue) { +- xhci_err(xhci, "Error: Failed finding new dequeue state\n"); +- return -EINVAL; +- } +- +- } while (!hw_dequeue_found || !td_last_trb_found); +- +- /* Don't update the ring cycle state for the producer (us). */ +- addr = xhci_trb_virt_to_dma(new_seg, new_deq); +- if (addr == 0) { +- xhci_warn(xhci, "Can't find dma of new dequeue ptr\n"); +- xhci_warn(xhci, "deq seg = %p, deq ptr = %p\n", new_seg, new_deq); +- return -EINVAL; +- } +- + if ((ep->ep_state & SET_DEQ_PENDING)) { +- xhci_warn(xhci, "Set TR Deq already pending, don't submit for %pad\n", +- &addr); ++ xhci_warn(xhci, "Set TR Deq already pending\n"); + return -EBUSY; + } + + /* This function gets called from contexts where it cannot sleep */ + cmd = xhci_alloc_command(xhci, false, GFP_ATOMIC); + if (!cmd) { +- xhci_warn(xhci, "Can't alloc Set TR Deq cmd %pad\n", &addr); ++ xhci_warn(xhci, "Can't alloc Set TR Deq cmd\n"); + return -ENOMEM; + } + ++ /* ++ * Move dequeue to the beginning of next td, or to enqueue if ring is ++ * empty. Avoid moving dequeue to a link trb (empty ring) as it causes ++ * issues on some hosts. In that case advance the enqueue to next segment ++ * before moving dequeue to it ++ */ ++ ++ if (list_empty(&ring->td_list)) { ++ if (trb_is_link(ring->enqueue)) ++ inc_enq_past_link(xhci, ring, 0); ++ ep->queued_deq_seg = ring->enq_seg; ++ ep->queued_deq_ptr = ring->enqueue; ++ new_cycle = ring->cycle_state; ++ } else { ++ td = list_first_entry(&ring->td_list, struct xhci_td, td_list); ++ ep->queued_deq_seg = td->start_seg; ++ ep->queued_deq_ptr = td->start_trb; ++ new_cycle = le32_to_cpu(td->start_trb->generic.field[3]) & TRB_CYCLE; ++ } ++ ++ addr = xhci_trb_virt_to_dma(ep->queued_deq_seg, ep->queued_deq_ptr); ++ if (addr == 0) { ++ xhci_warn(xhci, "Can't find new dequeue dma of seg %p, ptr %p\n", ++ ep->queued_deq_seg, ep->queued_deq_ptr); ++ ret = -EINVAL; ++ goto err_out; ++ } ++ + if (stream_id) + trb_sct = SCT_FOR_TRB(SCT_PRI_TR); + ret = queue_command(xhci, cmd, + lower_32_bits(addr) | trb_sct | new_cycle, + upper_32_bits(addr), +- STREAM_ID_FOR_TRB(stream_id), SLOT_ID_FOR_TRB(slot_id) | +- EP_INDEX_FOR_TRB(ep_index) | TRB_TYPE(TRB_SET_DEQ), false); +- if (ret < 0) { +- xhci_free_command(xhci, cmd); +- return ret; +- } +- ep->queued_deq_seg = new_seg; +- ep->queued_deq_ptr = new_deq; ++ STREAM_ID_FOR_TRB(stream_id), SLOT_ID_FOR_TRB(ep->vdev->slot_id) | ++ EP_INDEX_FOR_TRB(ep->ep_index) | TRB_TYPE(TRB_SET_DEQ), false); ++ if (ret < 0) ++ goto err_out; + + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Set TR Deq ptr 0x%llx, cycle %u\n", addr, new_cycle); + +- /* Stop the TD queueing code from ringing the doorbell until +- * this command completes. The HC won't set the dequeue pointer +- * if the ring is running, and ringing the doorbell starts the +- * ring running. ++ /* ++ * Stop the TD queueing code from ringing the doorbell until this ++ * command completes. The HC won't set the dequeue pointer if the ring ++ * is running, and ringing the doorbell starts the ring. + */ + ep->ep_state |= SET_DEQ_PENDING; + xhci_ring_cmd_db(xhci); ++ + return 0; ++ ++err_out: ++ xhci_free_command(xhci, cmd); ++ ep->queued_deq_seg = NULL; ++ ep->queued_deq_ptr = NULL; ++ ++ return ret; + } + + /* flip_cycle means flip the cycle bit of all but the first and last TRB. +@@ -950,7 +932,7 @@ static void xhci_dequeue_td(struct xhci_hcd *xhci, struct xhci_td *td, struct xh + } + + /* Complete the cancelled URBs we unlinked from td_list. */ +-static void xhci_giveback_invalidated_tds(struct xhci_virt_ep *ep) ++static void xhci_giveback_invalidated_tds(struct xhci_hcd *xhci, struct xhci_virt_ep *ep) + { + struct xhci_ring *ring; + struct xhci_td *td, *tmp_td; +@@ -958,17 +940,17 @@ static void xhci_giveback_invalidated_tds(struct xhci_virt_ep *ep) + list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, + cancelled_td_list) { + +- ring = xhci_urb_to_transfer_ring(ep->xhci, td->urb); ++ ring = xhci_urb_to_transfer_ring(xhci, td->urb); + + if (td->cancel_status == TD_CLEARED) { +- xhci_dbg(ep->xhci, "%s: Giveback cancelled URB %p TD\n", ++ xhci_dbg(xhci, "%s: Giveback cancelled URB %p TD\n", + __func__, td->urb); +- xhci_td_cleanup(ep->xhci, td, ring, td->status); ++ xhci_td_cleanup(xhci, td, ring, td->status); + } else { +- xhci_dbg(ep->xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n", ++ xhci_dbg(xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n", + __func__, td->urb, td->cancel_status); + } +- if (ep->xhci->xhc_state & XHCI_STATE_DYING) ++ if (xhci->xhc_state & XHCI_STATE_DYING) + return; + } + } +@@ -1002,15 +984,19 @@ static int xhci_handle_halted_endpoint(struct xhci_hcd *xhci, + struct xhci_td *td, + enum xhci_ep_reset_type reset_type) + { ++ struct xhci_port *rhub_port = ep->vdev->rhub_port; + unsigned int slot_id = ep->vdev->slot_id; + int err; + + /* +- * Avoid resetting endpoint if link is inactive. Can cause host hang. +- * Device will be reset soon to recover the link so don't do anything ++ * Avoid resetting endpoint if link is inactive or device disonnected. ++ * Can cause host hang. ++ * Device will be reset to recover an inactive link, so don't do anything + */ +- if (ep->vdev->flags & VDEV_PORT_ERROR) ++ if (rhub_port->link_inactive || !rhub_port->connected) { ++ ep->ep_state |= EP_DROP_PENDING; + return -ENODEV; ++ } + + /* add td to cancelled list and let reset ep handler take care of it */ + if (reset_type == EP_HARD_RESET) { +@@ -1047,15 +1033,13 @@ static int xhci_handle_halted_endpoint(struct xhci_hcd *xhci, + * only call this when ring is not in a running state + */ + +-static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) ++static int xhci_invalidate_cancelled_tds(struct xhci_hcd *xhci, struct xhci_virt_ep *ep) + { +- struct xhci_hcd *xhci; + struct xhci_td *td = NULL; + struct xhci_td *tmp_td = NULL; + struct xhci_td *cached_td = NULL; + struct xhci_ring *ring; + u64 hw_deq; +- unsigned int slot_id = ep->vdev->slot_id; + int err; + + /* +@@ -1065,8 +1049,6 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) + if (ep->ep_state & SET_DEQ_PENDING) + return 0; + +- xhci = ep->xhci; +- + list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, cancelled_td_list) { + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, + "Removing canceled TD starting at 0x%llx (dma) in stream %u URB %p", +@@ -1080,6 +1062,13 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) + td->urb, td->urb->stream_id); + continue; + } ++ ++ /* device disconnected or link error, ep will be dropped */ ++ if (ep->ep_state & EP_DROP_PENDING) { ++ td->cancel_status = TD_CLEARED; ++ continue; ++ } ++ + /* + * If a ring stopped on the TD we need to cancel then we have to + * move the xHC endpoint ring dequeue pointer past this TD. +@@ -1131,9 +1120,8 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) + if (!cached_td) + return 0; + +- err = xhci_move_dequeue_past_td(xhci, slot_id, ep->ep_index, +- cached_td->urb->stream_id, +- cached_td); ++ err = xhci_move_deq_to_next_td(xhci, ep, cached_td->urb->stream_id); ++ + if (err) { + /* Failed to move past cached td, just set cached TDs to no-op */ + list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, cancelled_td_list) { +@@ -1161,23 +1149,23 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) + * + * Call under xhci->lock on a stopped endpoint. + */ +-void xhci_process_cancelled_tds(struct xhci_virt_ep *ep) ++void xhci_process_cancelled_tds(struct xhci_hcd *xhci, struct xhci_virt_ep *ep) + { +- xhci_invalidate_cancelled_tds(ep); +- xhci_giveback_invalidated_tds(ep); ++ xhci_invalidate_cancelled_tds(xhci, ep); ++ xhci_giveback_invalidated_tds(xhci, ep); + } + + /* + * Returns the TD the endpoint ring halted on. + * Only call for non-running rings without streams. + */ +-static struct xhci_td *find_halted_td(struct xhci_virt_ep *ep) ++static struct xhci_td *find_halted_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep) + { + struct xhci_td *td; + u64 hw_deq; + + if (!list_empty(&ep->ring->td_list)) { /* Not streams compatible */ +- hw_deq = xhci_get_hw_deq(ep->xhci, ep->vdev, ep->ep_index, 0); ++ hw_deq = xhci_get_hw_deq(xhci, ep->vdev, ep->ep_index, 0); + hw_deq &= TR_DEQ_PTR_MASK; + td = list_first_entry(&ep->ring->td_list, struct xhci_td, td_list); + if (trb_in_td(td, hw_deq)) +@@ -1252,7 +1240,7 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id, + reset_type = EP_SOFT_RESET; + } else { + reset_type = EP_HARD_RESET; +- td = find_halted_td(ep); ++ td = find_halted_td(xhci, ep); + if (td) + td->status = -EPROTO; + } +@@ -1310,13 +1298,17 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id, + } + } + ++ /* link is inactive or disconnected, ep is not running and shouldn't be restarted */ ++ if (ep->vdev->rhub_port->link_inactive || !ep->vdev->rhub_port->connected) ++ ep->ep_state |= EP_DROP_PENDING; ++ + /* will queue a set TR deq if stopped on a cancelled, uncleared TD */ +- xhci_invalidate_cancelled_tds(ep); ++ xhci_invalidate_cancelled_tds(xhci, ep); + ep->ep_state &= ~EP_STOP_CMD_PENDING; + + /* Otherwise ring the doorbell(s) to restart queued transfers */ +- xhci_giveback_invalidated_tds(ep); +- ring_doorbell_for_active_rings(xhci, slot_id, ep_index); ++ xhci_giveback_invalidated_tds(xhci, ep); ++ xhci_ring_doorbell_for_active_rings(xhci, slot_id, ep_index); + } + + static void xhci_kill_ring_urbs(struct xhci_hcd *xhci, struct xhci_ring *ring) +@@ -1537,14 +1529,14 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, + /* HW cached TDs cleared from cache, give them back */ + list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, + cancelled_td_list) { +- ep_ring = xhci_urb_to_transfer_ring(ep->xhci, td->urb); ++ ep_ring = xhci_urb_to_transfer_ring(xhci, td->urb); + if (td->cancel_status == TD_CLEARING_CACHE) { + td->cancel_status = TD_CLEARED; +- xhci_dbg(ep->xhci, "%s: Giveback cancelled URB %p TD\n", ++ xhci_dbg(xhci, "%s: Giveback cancelled URB %p TD\n", + __func__, td->urb); +- xhci_td_cleanup(ep->xhci, td, ep_ring, td->status); ++ xhci_td_cleanup(xhci, td, ep_ring, td->status); + } else { +- xhci_dbg(ep->xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n", ++ xhci_dbg(xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n", + __func__, td->urb, td->cancel_status); + } + } +@@ -1555,17 +1547,17 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, + + /* Check for deferred or newly cancelled TDs */ + if (!list_empty(&ep->cancelled_td_list)) { +- xhci_dbg(ep->xhci, "%s: Pending TDs to clear, continuing with invalidation\n", ++ xhci_dbg(xhci, "%s: Pending TDs to clear, continuing with invalidation\n", + __func__); +- xhci_invalidate_cancelled_tds(ep); ++ xhci_invalidate_cancelled_tds(xhci, ep); + /* Try to restart the endpoint if all is done */ +- ring_doorbell_for_active_rings(xhci, slot_id, ep_index); ++ xhci_ring_doorbell_for_active_rings(xhci, slot_id, ep_index); + /* Start giving back any TDs invalidated above */ +- xhci_giveback_invalidated_tds(ep); ++ xhci_giveback_invalidated_tds(xhci, ep); + } else { + /* Restart any rings with pending URBs */ +- xhci_dbg(ep->xhci, "%s: All TDs cleared, ring doorbell\n", __func__); +- ring_doorbell_for_active_rings(xhci, slot_id, ep_index); ++ xhci_dbg(xhci, "%s: All TDs cleared, ring doorbell\n", __func__); ++ xhci_ring_doorbell_for_active_rings(xhci, slot_id, ep_index); + } + } + +@@ -1591,16 +1583,16 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id, + "Ignoring reset ep completion code of %u", cmd_comp_code); + + /* Cleanup cancelled TDs as ep is stopped. May queue a Set TR Deq cmd */ +- xhci_invalidate_cancelled_tds(ep); ++ xhci_invalidate_cancelled_tds(xhci, ep); + + /* Clear our internal halted state */ + ep->ep_state &= ~EP_HALTED; + +- xhci_giveback_invalidated_tds(ep); ++ xhci_giveback_invalidated_tds(xhci, ep); + + /* if this was a soft reset, then restart */ + if ((le32_to_cpu(trb->generic.field[3])) & TRB_TSP) +- ring_doorbell_for_active_rings(xhci, slot_id, ep_index); ++ xhci_ring_doorbell_for_active_rings(xhci, slot_id, ep_index); + } + + static void xhci_handle_cmd_enable_slot(int slot_id, struct xhci_command *command, +@@ -2008,13 +2000,15 @@ static void xhci_cavium_reset_phy_quirk(struct xhci_hcd *xhci) + static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) + { + struct xhci_virt_device *vdev = NULL; +- struct usb_hcd *hcd; +- u32 port_id; +- u32 portsc, cmd_reg; +- unsigned int hcd_portnum; + struct xhci_bus_state *bus_state; +- bool bogus_port_status = false; + struct xhci_port *port; ++ struct usb_hcd *hcd; ++ bool bogus_port_status = false; ++ unsigned int hcd_portnum; ++ u32 cmd_reg; ++ u32 port_id; ++ u32 portsc; ++ u32 pls; + + /* Port status change events always have a successful completion code */ + if (GET_COMP_CODE(le32_to_cpu(event->generic.field[2])) != COMP_SUCCESS) +@@ -2051,6 +2045,7 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) + bus_state = &port->rhub->bus_state; + hcd_portnum = port->hcd_portnum; + portsc = xhci_portsc_readl(port); ++ pls = portsc & PORT_PLS_MASK; + + xhci_dbg(xhci, "Port change event, %d-%d, id %d, portsc: 0x%x\n", + hcd->self.busnum, hcd_portnum + 1, port_id, portsc); +@@ -2062,11 +2057,13 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) + usb_hcd_resume_root_hub(hcd); + } + +- if (vdev && (portsc & PORT_PLS_MASK) == XDEV_INACTIVE) { +- if (!(portsc & PORT_RESET)) +- vdev->flags |= VDEV_PORT_ERROR; +- } else if (vdev && portsc & PORT_RC) { +- vdev->flags &= ~VDEV_PORT_ERROR; ++ /* ++ * Tag broken links to avoid retries while hub driver sorts it out. ++ * Link status is not relible while port is in reset. ++ */ ++ if (!(portsc & PORT_RESET)) { ++ port->link_inactive = (pls == XDEV_INACTIVE); ++ port->connected = !!(portsc & PORT_CONNECT); + } + + if ((portsc & PORT_PLC) && (portsc & PORT_PLS_MASK) == XDEV_RESUME) { +@@ -3978,88 +3975,102 @@ static unsigned int xhci_get_last_burst_packet_count(struct xhci_hcd *xhci, + /* Returns the Isochronous Scheduling Threshold in Microframes. 1 Frame is 8 Microframes. */ + static int xhci_ist_microframes(struct xhci_hcd *xhci) + { +- int ist = HCS_IST_VALUE(xhci->hcs_params2); ++ int ist = FIELD_GET(HCS_IST_VALUE, xhci->hcs_params2); + + if (xhci->hcs_params2 & HCS_IST_UNIT) + ist *= 8; + return ist; + } + +-/* +- * Calculates Frame ID field of the isochronous TRB identifies the +- * target frame that the Interval associated with this Isochronous +- * Transfer Descriptor will start on. Refer to 4.11.2.5 in 1.1 spec. +- * +- * Returns actual frame id on success, negative value on error. +- */ +-static int xhci_get_isoc_frame_id(struct xhci_hcd *xhci, +- struct urb *urb, int index) ++ ++static bool xhci_isoc_td_uses_frame_id(struct xhci_hcd *xhci, struct urb *urb, ++ struct xhci_virt_ep *ep, int i) + { +- int start_frame, ist, ret = 0; +- int start_frame_id, end_frame_id, current_frame_id; ++ if (urb->transfer_flags & URB_ISO_ASAP) ++ return false; + +- if (urb->dev->speed == USB_SPEED_LOW || +- urb->dev->speed == USB_SPEED_FULL) +- start_frame = urb->start_frame + index * urb->interval; +- else +- start_frame = (urb->start_frame + index * urb->interval) >> 3; ++ if (xhci->hcc_params & HCC_CFC) ++ return true; + ++ /* set frame id for first TD of first URB in stream */ ++ if (ep->next_uframe == -1 && i == 0) ++ return true; ++ ++ return false; ++} ++ ++/* ++ * Check if frame is in the valid frame window, including start and end. ++ * If start > end then assume window wrapped around at a limit the frame ++ * value won't exceed. ++ */ ++static bool xhci_frame_in_range(u32 frame, u32 start, u32 end) ++{ ++ /* frame window end wrapped around */ ++ if (start > end) ++ return frame >= start || frame <= end; ++ ++ return frame >= start && frame <= end; ++} ++ ++/* ++ * Set the urb->start_frame of the URB. ++ * ++ * Returns microframe index of first TD ++ */ ++static int xhci_get_isoc_start_frame(struct xhci_hcd *xhci, struct urb *urb, ++ struct xhci_virt_ep *ep) ++{ ++ u32 curr_frame, start_uframe; ++ u32 urb_start, urb_end; ++ u32 win_start, win_end; ++ bool frame_unit; ++ int uinterval; ++ u32 mfindex; ++ int ist; ++ ++ /* check if urb uses frame units instead of microframes */ ++ frame_unit = (urb->dev->speed == USB_SPEED_FULL || ++ urb->dev->speed == USB_SPEED_LOW); ++ ++ uinterval = urb->interval; ++ if (frame_unit) ++ uinterval *= 8; ++ ++ /* get current microframe index and isoc scheduling threshold */ ++ mfindex = readl(&xhci->run_regs->microframe_index); + ist = xhci_ist_microframes(xhci); + +- /* Software shall not schedule an Isoch TD with a Frame ID value that +- * is less than the Start Frame ID or greater than the End Frame ID, +- * where: +- * +- * End Frame ID = (Current MFINDEX register value + 895 ms.) MOD 2048 +- * Start Frame ID = (Current MFINDEX register value + IST + 1) MOD 2048 +- * +- * Both the End Frame ID and Start Frame ID values are calculated +- * in microframes. When software determines the valid Frame ID value; +- * The End Frame ID value should be rounded down to the nearest Frame +- * boundary, and the Start Frame ID value should be rounded up to the +- * nearest Frame boundary. +- */ +- current_frame_id = readl(&xhci->run_regs->microframe_index); +- start_frame_id = roundup(current_frame_id + ist + 1, 8); +- end_frame_id = rounddown(current_frame_id + 895 * 8, 8); ++ /* calculate valid frame window, in frame units, see xhci 4.11.2.5 */ ++ curr_frame = MFINDEX_TO_FRAME(mfindex); ++ win_start = (curr_frame + DIV_ROUND_UP_POW2(ist, 8) + 1) % MAX_FRAMES; ++ win_end = (curr_frame + 895) % MAX_FRAMES; + +- start_frame &= 0x7ff; +- start_frame_id = (start_frame_id >> 3) & 0x7ff; +- end_frame_id = (end_frame_id >> 3) & 0x7ff; +- +- if (start_frame_id < end_frame_id) { +- if (start_frame > end_frame_id || +- start_frame < start_frame_id) +- ret = -EINVAL; +- } else if (start_frame_id > end_frame_id) { +- if ((start_frame > end_frame_id && +- start_frame < start_frame_id)) +- ret = -EINVAL; ++ /* Is this the first URB starting the whole isoc data flow? */ ++ if (ep->next_uframe < 0) { ++ /* align first URB to next interval boundary, or at last to full frame */ ++ start_uframe = mfindex + ist + XHCI_CFC_DELAY; ++ start_uframe = roundup(start_uframe, 8); ++ start_uframe = roundup(start_uframe, uinterval) % MAX_UFRAMES; + } else { +- ret = -EINVAL; +- } ++ /* URB is mid stream and expected to handle the next frame */ ++ start_uframe = ep->next_uframe; ++ urb_start = start_uframe / 8; ++ urb_end = (start_uframe + urb->number_of_packets * uinterval) / 8; ++ urb_end %= MAX_FRAMES; + +- if (index == 0) { +- if (ret == -EINVAL || start_frame == start_frame_id) { +- start_frame = start_frame_id + 1; +- if (urb->dev->speed == USB_SPEED_LOW || +- urb->dev->speed == USB_SPEED_FULL) +- urb->start_frame = start_frame; +- else +- urb->start_frame = start_frame << 3; +- ret = 0; +- } +- } ++ if (!xhci_frame_in_range(urb_start, win_start, win_end)) ++ xhci_dbg(xhci, "Ill-timed isoc URB %p for start frame %d, range %d-%d\n", ++ urb, urb_start, win_start, win_end); + +- if (ret) { +- xhci_warn(xhci, "Frame ID %d (reg %d, index %d) beyond range (%d, %d)\n", +- start_frame, current_frame_id, index, +- start_frame_id, end_frame_id); +- xhci_warn(xhci, "Ignore frame ID field, use SIA bit instead\n"); +- return ret; ++ if (!xhci_frame_in_range(urb_end, win_start, win_end)) ++ xhci_dbg(xhci, "Ill-timed isoc URB %p for end frame %d, range %d-%d\n", ++ urb, urb_start, win_start, win_end); + } ++ /* set urb->start_frame */ ++ urb->start_frame = frame_unit ? start_uframe / 8 : start_uframe; + +- return start_frame; ++ return start_uframe; + } + + /* Check if we should generate event interrupt for a TD in an isoc URB */ +@@ -4099,7 +4110,8 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, + int i, j; + bool more_trbs_coming; + struct xhci_virt_ep *xep; +- int frame_id; ++ int uinterval = urb->interval; ++ int start_uframe; + + xep = &xhci->devs[slot_id]->eps[ep_index]; + ep_ring = xhci->devs[slot_id]->eps[ep_index].ring; +@@ -4115,6 +4127,12 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, + start_cycle = ep_ring->cycle_state; + + urb_priv = urb->hcpriv; ++ ++ if (urb->dev->speed == USB_SPEED_FULL || urb->dev->speed == USB_SPEED_LOW) ++ uinterval = urb->interval * 8; ++ ++ start_uframe = xhci_get_isoc_start_frame(xhci, urb, xep); ++ + /* Queue the TRBs for each TD, even if they are zero-length */ + for (i = 0; i < num_tds; i++) { + unsigned int total_pkt_count, max_pkt; +@@ -4146,14 +4164,16 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, + goto cleanup; + } + td = &urb_priv->td[i]; +- /* use SIA as default, if frame id is used overwrite it */ +- sia_frame_id = TRB_SIA; +- if (!(urb->transfer_flags & URB_ISO_ASAP) && +- (xhci->hcc_params & HCC_CFC)) { +- frame_id = xhci_get_isoc_frame_id(xhci, urb, i); +- if (frame_id >= 0) +- sia_frame_id = TRB_FRAME_ID(frame_id); ++ ++ ++ /* Choose SIA or frame ID based scheduling for this TD */ ++ if (xhci_isoc_td_uses_frame_id(xhci, urb, xep, i)) { ++ sia_frame_id = (start_uframe + i * uinterval) / 8; ++ sia_frame_id = TRB_FRAME_ID(sia_frame_id % MAX_FRAMES); ++ } else { ++ sia_frame_id = TRB_SIA; + } ++ + /* + * Set isoc specific data for the first TRB in a TD. + * Prevent HW from getting the TRBs by keeping the cycle state +@@ -4232,9 +4252,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, + } + } + +- /* store the next frame id */ +- if (xhci->hcc_params & HCC_CFC) +- xep->next_frame_id = urb->start_frame + num_tds * urb->interval; ++ xep->next_uframe = (start_uframe + num_tds * uinterval) % MAX_UFRAMES; + + if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs == 0) { + if (xhci->quirks & XHCI_AMD_PLL_FIX) +@@ -4281,11 +4299,9 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, + struct xhci_virt_device *xdev; + struct xhci_ring *ep_ring; + struct xhci_ep_ctx *ep_ctx; +- int start_frame; ++ struct xhci_virt_ep *xep; + int num_tds, num_trbs, i; + int ret; +- struct xhci_virt_ep *xep; +- int ist; + + xdev = xhci->devs[slot_id]; + xep = &xhci->devs[slot_id]->eps[ep_index]; +@@ -4311,38 +4327,12 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, + */ + check_interval(urb, ep_ctx); + +- /* Calculate the start frame and put it in urb->start_frame. */ +- if ((xhci->hcc_params & HCC_CFC) && !list_empty(&ep_ring->td_list)) { +- if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_RUNNING) { +- urb->start_frame = xep->next_frame_id; +- goto skip_start_over; +- } +- } +- +- start_frame = readl(&xhci->run_regs->microframe_index); +- start_frame &= 0x3fff; + /* +- * Round up to the next frame and consider the time before trb really +- * gets scheduled by hardare. ++ * Check if this starts the isoc data flow. Relies on hw setting ep ctx ++ * state after doorbell ring. Consider adding list_empty(td_list) check + */ +- ist = xhci_ist_microframes(xhci); +- start_frame += ist + XHCI_CFC_DELAY; +- start_frame = roundup(start_frame, 8); +- +- /* +- * Round up to the next ESIT (Endpoint Service Interval Time) if ESIT +- * is greate than 8 microframes. +- */ +- if (urb->dev->speed == USB_SPEED_LOW || +- urb->dev->speed == USB_SPEED_FULL) { +- start_frame = roundup(start_frame, urb->interval << 3); +- urb->start_frame = start_frame >> 3; +- } else { +- start_frame = roundup(start_frame, urb->interval); +- urb->start_frame = start_frame; +- } +- +-skip_start_over: ++ if (GET_EP_CTX_STATE(ep_ctx) != EP_STATE_RUNNING) ++ xep->next_uframe = -1; + + return xhci_queue_isoc_tx(xhci, mem_flags, urb, slot_id, ep_index); + } +diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c +--- a/drivers/usb/host/xhci-tegra.c ++++ b/drivers/usb/host/xhci-tegra.c +@@ -6,6 +6,7 @@ + * Copyright (C) 2014 Google, Inc. + */ + ++#include + #include + #include + #include +@@ -993,7 +994,7 @@ static int tegra_xusb_wait_for_falcon(struct tegra_xusb *tegra) + u32 value; + + cap_regs = tegra->regs; +- op_regs = tegra->regs + HC_LENGTH(readl(&cap_regs->hc_capbase)); ++ op_regs = tegra->regs + FIELD_GET(HC_LENGTH, readl(&cap_regs->hc_capbase)), + + ret = readl_poll_timeout(&op_regs->status, value, !(value & STS_CNR), 1000, 200000); + +@@ -1895,7 +1896,7 @@ static int tegra_xusb_probe(struct platform_device *pdev) + goto remove_usb2; + } + +- if (HCC_MAX_PSA(xhci->hcc_params) >= 4) ++ if (GET_MAX_PSA_SIZE(xhci->hcc_params) >= 4) + xhci->shared_hcd->can_do_streams = 1; + + err = usb_add_hcd(xhci->shared_hcd, tegra->xhci_irq, IRQF_SHARED); +diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c +--- a/drivers/usb/host/xhci.c ++++ b/drivers/usb/host/xhci.c +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + + #include "xhci.h" + #include "xhci-trace.h" +@@ -1664,7 +1665,7 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag + goto free_priv; + } + +- if (xhci->devs[slot_id]->flags & VDEV_PORT_ERROR) { ++ if (xhci->devs[slot_id]->rhub_port->link_inactive) { + xhci_dbg(xhci, "Can't queue urb, port error, link inactive\n"); + ret = -ENODEV; + goto free_priv; +@@ -1852,11 +1853,11 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) + } + + /* In this case no commands are pending but the endpoint is stopped */ +- if (ep->ep_state & EP_CLEARING_TT) { ++ if (ep->ep_state & (EP_CLEARING_TT | EP_DROP_PENDING)) { + /* and cancelled TDs can be given back right away */ + xhci_dbg(xhci, "Invalidating TDs instantly on slot %d ep %d in state 0x%x\n", + urb->dev->slot_id, ep_index, ep->ep_state); +- xhci_process_cancelled_tds(ep); ++ xhci_process_cancelled_tds(xhci, ep); + } else { + /* Otherwise, queue a new Stop Endpoint command */ + command = xhci_alloc_command(xhci, false, GFP_ATOMIC); +@@ -3507,7 +3508,7 @@ static void xhci_calculate_streams_entries(struct xhci_hcd *xhci, + * level page entries), but that's an optional feature for xHCI host + * controllers. xHCs must support at least 4 stream IDs. + */ +- max_streams = HCC_MAX_PSA(xhci->hcc_params); ++ max_streams = GET_MAX_PSA_SIZE(xhci->hcc_params); + if (*num_stream_ctxs > max_streams) { + xhci_dbg(xhci, "xHCI HW only supports %u stream ctx entries.\n", + max_streams); +@@ -3637,7 +3638,7 @@ static int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, + + /* MaxPSASize value 0 (2 streams) means streams are not supported */ + if ((xhci->quirks & XHCI_BROKEN_STREAMS) || +- HCC_MAX_PSA(xhci->hcc_params) < 4) { ++ GET_MAX_PSA_SIZE(xhci->hcc_params) < 4) { + xhci_dbg(xhci, "xHCI controller does not support streams.\n"); + return -ENOSYS; + } +@@ -4035,7 +4036,6 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd, + xhci_get_slot_state(xhci, virt_dev->out_ctx)); + xhci_dbg(xhci, "Not freeing device rings.\n"); + /* Don't treat this as an error. May change my mind later. */ +- virt_dev->flags = 0; + ret = 0; + goto command_cleanup; + case COMP_SUCCESS: +@@ -4087,10 +4087,12 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd, + } + /* If necessary, update the number of active TTs on this root port */ + xhci_update_tt_active_eps(xhci, virt_dev, old_active_eps); +- virt_dev->flags = 0; + ret = 0; + + command_cleanup: ++ for (i = 0; i < EP_CTX_PER_DEV; i++) ++ virt_dev->eps[i].ep_state &= ~EP_DROP_PENDING; ++ + xhci_free_command(xhci, reset_device_cmd); + return ret; + } +@@ -4607,7 +4609,7 @@ static int xhci_calculate_hird_besl(struct xhci_hcd *xhci, + int besl_device = 0; + u32 field; + +- u2del = HCS_U2_LATENCY(xhci->hcs_params3); ++ u2del = FIELD_GET(HCS_U2_LATENCY, xhci->hcs_params3); + field = le32_to_cpu(udev->bos->ext_cap->bmAttributes); + + if (field & USB_BESL_SUPPORT) { +@@ -5459,26 +5461,28 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) + xhci_warn(xhci, "Host controller not accessible, removed?\n"); + return -ENODEV; + } +- xhci->op_regs = hcd->regs + HC_LENGTH(hc_capbase); ++ xhci->op_regs = hcd->regs + FIELD_GET(HC_LENGTH, hc_capbase); ++ + xhci->run_regs = hcd->regs + + (readl(&xhci->cap_regs->run_regs_off) & RTSOFF_MASK); + /* Cache read-only capability registers */ + hcs_params1 = readl(&xhci->cap_regs->hcs_params1); + xhci->hcs_params2 = readl(&xhci->cap_regs->hcs_params2); + xhci->hcs_params3 = readl(&xhci->cap_regs->hcs_params3); +- xhci->hci_version = HC_VERSION(hc_capbase); ++ xhci->hci_version = FIELD_GET(HC_VERSION, hc_capbase); + xhci->hcc_params = readl(&xhci->cap_regs->hcc_params); + if (xhci->hci_version > 0x100) + xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2); + + xhci->dma_mask_bits = 64; +- xhci->max_slots = min(HCS_MAX_SLOTS(hcs_params1), MAX_HC_SLOTS); +- xhci->max_ports = min(HCS_MAX_PORTS(hcs_params1), MAX_HC_PORTS); ++ xhci->max_slots = min(FIELD_GET(HCS_SLOTS_MASK, hcs_params1), MAX_HC_SLOTS); ++ xhci->max_ports = min(FIELD_GET(HCS_MAX_PORTS, hcs_params1), MAX_HC_PORTS); ++ + /* xhci-plat or xhci-pci might have set max_interrupters already */ + if (!xhci->max_interrupters) +- xhci->max_interrupters = min(HCS_MAX_INTRS(hcs_params1), MAX_HC_INTRS); +- else if (xhci->max_interrupters > HCS_MAX_INTRS(hcs_params1)) +- xhci->max_interrupters = HCS_MAX_INTRS(hcs_params1); ++ xhci->max_interrupters = min(FIELD_GET(HCS_MAX_INTRS, hcs_params1), MAX_HC_INTRS); ++ else if (xhci->max_interrupters > FIELD_GET(HCS_MAX_INTRS, hcs_params1)) ++ xhci->max_interrupters = FIELD_GET(HCS_MAX_INTRS, hcs_params1); + + xhci->quirks |= quirks; + +@@ -5519,7 +5523,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) + * DMA_BIT_MASK(32)) in this xhci_gen_setup(). + */ + if (xhci->quirks & XHCI_NO_64BIT_SUPPORT) +- xhci->hcc_params &= ~BIT(0); ++ xhci->hcc_params &= ~HCC_64BIT_ADDR; + + /* + * Set dma_mask and coherent_dma_mask to 64-bits if xHC supports +diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h +--- a/drivers/usb/host/xhci.h ++++ b/drivers/usb/host/xhci.h +@@ -290,6 +290,11 @@ struct xhci_run_regs { + struct xhci_intr_reg ir_set[1024]; + }; + ++/* Bits [13:3] of the microframe index equals the 1ms frame index */ ++#define MFINDEX_TO_FRAME(p) (((p) >> 3) & 0x7ff) ++#define MAX_FRAMES 2048 ++#define MAX_UFRAMES (MAX_FRAMES * 8) ++ + /** + * struct doorbell_array + * +@@ -677,9 +682,9 @@ struct xhci_virt_ep { + #define EP_SOFT_CLEAR_TOGGLE BIT(7) + /* usb_hub_clear_tt_buffer is in progress */ + #define EP_CLEARING_TT BIT(8) ++#define EP_DROP_PENDING BIT(9) /* port disconnect or link error, don't restart */ + /* ---- Related to URB cancellation ---- */ + struct list_head cancelled_td_list; +- struct xhci_hcd *xhci; + /* Dequeue pointer and dequeue segment for a submitted Set TR Dequeue + * command. We'll need to update the ring's dequeue segment and dequeue + * pointer after the command completes. +@@ -699,7 +704,7 @@ struct xhci_virt_ep { + struct list_head bw_endpoint_list; + unsigned long stop_time; + /* Isoch Frame ID checking storage */ +- int next_frame_id; ++ int next_uframe; + /* Use new Isoch TRB layout needed for extended TBC support */ + bool use_extended_tbc; + /* set if this endpoint is controlled via sideband access*/ +@@ -753,14 +758,6 @@ struct xhci_virt_device { + struct xhci_port *rhub_port; + struct xhci_interval_bw_table *bw_table; + struct xhci_tt_bw_info *tt_info; +- /* +- * flags for state tracking based on events and issued commands. +- * Software can not rely on states from output contexts because of +- * latency between events and xHC updating output context values. +- * See xhci 1.1 section 4.8.3 for more details +- */ +- unsigned long flags; +-#define VDEV_PORT_ERROR BIT(0) /* Port error, link inactive */ + + /* The current max exit latency for the enabled USB3 link states. */ + u16 current_mel; +@@ -1480,6 +1477,8 @@ struct xhci_port { + int hcd_portnum; + struct xhci_hub *rhub; + struct xhci_port_cap *port_cap; ++ unsigned int link_inactive:1; ++ unsigned int connected:1; + unsigned int lpm_incapable:1; + unsigned long resume_timestamp; + bool rexit_active; +@@ -1961,7 +1960,7 @@ unsigned int count_trbs(u64 addr, u64 len); + unsigned int xhci_num_trbs_free(struct xhci_ring *ring); + int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, + int suspend, gfp_t gfp_flags); +-void xhci_process_cancelled_tds(struct xhci_virt_ep *ep); ++void xhci_process_cancelled_tds(struct xhci_hcd *xhci, struct xhci_virt_ep *ep); + void xhci_update_erst_dequeue(struct xhci_hcd *xhci, + struct xhci_interrupter *ir, + bool clear_ehb); +diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c +--- a/drivers/usb/misc/cypress_cy7c63.c ++++ b/drivers/usb/misc/cypress_cy7c63.c +@@ -89,8 +89,11 @@ static int vendor_command(struct cypress *dev, unsigned char request, + address, data, iobuf, CYPRESS_MAX_REQSIZE, + USB_CTRL_GET_TIMEOUT); + /* we must not process garbage */ +- if (retval < 2) ++ if (retval < 2) { ++ if (retval >= 0) ++ retval = -EIO; + goto err_buf; ++ } + + /* store returned data (more READs to be added) */ + switch (request) { +@@ -175,8 +178,9 @@ static ssize_t read_port(struct device *dev, struct device_attribute *attr, + dev_dbg(&cyp->udev->dev, "READ_PORT%d called\n", port_num); + + result = vendor_command(cyp, CYPRESS_READ_PORT, read_id, 0); +- + dev_dbg(&cyp->udev->dev, "Result of vendor_command: %d\n\n", result); ++ if (result < 0) ++ return result; + + return sprintf(buf, "%d", cyp->port[port_num]); + } +diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c +--- a/drivers/usb/misc/uss720.c ++++ b/drivers/usb/misc/uss720.c +@@ -223,6 +223,7 @@ static int get_1284_register(struct parport *pp, unsigned char reg, unsigned cha + } + printk(KERN_WARNING "get_1284_register timeout\n"); + kill_all_async_requests_priv(priv); ++ kref_put(&rq->ref_count, destroy_async); + return -EIO; + } + +diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c +--- a/drivers/usb/mtu3/mtu3_host.c ++++ b/drivers/usb/mtu3/mtu3_host.c +@@ -46,6 +46,14 @@ + #define WC1_IS_P_95 BIT(12) + #define WC1_IS_EN_P0_95 BIT(6) + ++/* mt8196 */ ++#define PERI_WK_CTRL0_8196 0x08 ++#define WC0_IS_EN_P0_96 BIT(0) ++#define WC0_IS_EN_P1_96 BIT(7) ++ ++#define PERI_WK_CTRL1_8196 0x10 ++#define WC1_IS_EN_P2_96 BIT(0) ++ + /* mt2712 etc */ + #define PERI_SSUSB_SPM_CTRL 0x0 + #define SSC_IP_SLEEP_EN BIT(4) +@@ -59,6 +67,9 @@ enum ssusb_uwk_vers { + SSUSB_UWK_V1_3, /* mt8195 IP0 */ + SSUSB_UWK_V1_5 = 105, /* mt8195 IP2 */ + SSUSB_UWK_V1_6, /* mt8195 IP3 */ ++ SSUSB_UWK_V1_7, /* mt8196 IP0 */ ++ SSUSB_UWK_V1_8, /* mt8196 IP1 */ ++ SSUSB_UWK_V1_9, /* mt8196 IP2 */ + }; + + /* +@@ -100,6 +111,21 @@ static void ssusb_wakeup_ip_sleep_set(struct ssusb_mtk *ssusb, bool enable) + msk = WC0_IS_EN_P3_95 | WC0_IS_C_95(0x7) | WC0_IS_P_95; + val = enable ? (WC0_IS_EN_P3_95 | WC0_IS_C_95(0x1)) : 0; + break; ++ case SSUSB_UWK_V1_7: ++ reg = ssusb->uwk_reg_base + PERI_WK_CTRL0_8196; ++ msk = WC0_IS_EN_P0_96; ++ val = enable ? msk : 0; ++ break; ++ case SSUSB_UWK_V1_8: ++ reg = ssusb->uwk_reg_base + PERI_WK_CTRL0_8196; ++ msk = WC0_IS_EN_P1_96; ++ val = enable ? msk : 0; ++ break; ++ case SSUSB_UWK_V1_9: ++ reg = ssusb->uwk_reg_base + PERI_WK_CTRL1_8196; ++ msk = WC1_IS_EN_P2_96; ++ val = enable ? msk : 0; ++ break; + case SSUSB_UWK_V2: + reg = ssusb->uwk_reg_base + PERI_SSUSB_SPM_CTRL; + msk = SSC_IP_SLEEP_EN | SSC_SPM_INT_EN; +diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c +--- a/drivers/usb/mtu3/mtu3_plat.c ++++ b/drivers/usb/mtu3/mtu3_plat.c +@@ -544,7 +544,8 @@ static int mtu3_suspend_common(struct device *dev, pm_message_t msg) + + ssusb_phy_power_off(ssusb); + clk_bulk_disable_unprepare(BULK_CLKS_CNT, ssusb->clks); +- ssusb_wakeup_set(ssusb, true); ++ if (device_may_wakeup(dev) && (ssusb->is_host || PMSG_IS_AUTO(msg))) ++ ssusb_wakeup_set(ssusb, true); + return 0; + + sleep_err: +diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig +--- a/drivers/usb/musb/Kconfig ++++ b/drivers/usb/musb/Kconfig +@@ -116,7 +116,6 @@ config USB_MUSB_POLARFIRE_SOC + tristate "Microchip PolarFire SoC platforms" + depends on ARCH_MICROCHIP_POLARFIRE || COMPILE_TEST + depends on NOP_USB_XCEIV +- select USB_MUSB_DUAL_ROLE + help + Say Y here to enable support for USB on Microchip's PolarFire SoC. + +diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c +--- a/drivers/usb/musb/musb_host.c ++++ b/drivers/usb/musb/musb_host.c +@@ -1774,7 +1774,8 @@ void musb_host_rx(struct musb *musb, u8 epnum) + status = -EPIPE; + + } else if (rx_csr & MUSB_RXCSR_H_ERROR) { +- dev_err(musb->controller, "ep%d RX three-strikes error", epnum); ++ dev_err_ratelimited(musb->controller, ++ "ep%d RX three-strikes error\n", epnum); + + /* + * The three-strikes error could only happen when the USB +@@ -1788,6 +1789,17 @@ void musb_host_rx(struct musb *musb, u8 epnum) + rx_csr &= ~MUSB_RXCSR_H_ERROR; + musb_writew(epio, MUSB_RXCSR, rx_csr); + ++ /* ++ * Unplugging a USB-Ethernet adapter while it is busy can make ++ * the controller keep re-asserting the three-strikes error for ++ * this endpoint before the disconnect is processed. That floods ++ * the log and can wedge the host port until reboot. Drop the ++ * stale pending RX interrupt on platforms that support it (e.g. ++ * AM335x/DSPS) to break the storm; the transfer is still ++ * aborted below via the fault path. ++ */ ++ musb_platform_clear_ep_rxintr(musb, epnum); ++ + } else if (rx_csr & MUSB_RXCSR_DATAERROR) { + + if (USB_ENDPOINT_XFER_ISOC != qh->type) { +diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c +--- a/drivers/usb/serial/digi_acceleport.c ++++ b/drivers/usb/serial/digi_acceleport.c +@@ -32,10 +32,6 @@ + /* so we can be sure to send the full buffer in one urb */ + #define DIGI_OUT_BUF_SIZE 8 + +-/* port input buffer length -- must be >= transfer buffer length - 3 */ +-/* so we can be sure to hold at least one full buffer from one urb */ +-#define DIGI_IN_BUF_SIZE 64 +- + /* retry timeout while sleeping */ + #define DIGI_RETRY_TIMEOUT (HZ/10) + +@@ -176,10 +172,10 @@ + /* Structures */ + + struct digi_serial { +- spinlock_t ds_serial_lock; ++ struct mutex open_mutex; + struct usb_serial_port *ds_oob_port; /* out-of-band port */ + int ds_oob_port_num; /* index of out-of-band port */ +- int ds_device_started; ++ int open_count; + }; + + struct digi_port { +@@ -194,7 +190,6 @@ struct digi_port { + int dp_throttled; + int dp_throttle_restart; + wait_queue_head_t dp_flush_wait; +- wait_queue_head_t dp_close_wait; /* wait queue for close */ + wait_queue_head_t write_wait; + struct usb_serial_port *dp_port; + }; +@@ -227,9 +222,7 @@ static unsigned int digi_chars_in_buffer(struct tty_struct *tty); + static int digi_open(struct tty_struct *tty, struct usb_serial_port *port); + static void digi_close(struct usb_serial_port *port); + static void digi_dtr_rts(struct usb_serial_port *port, int on); +-static int digi_startup_device(struct usb_serial *serial); + static int digi_startup(struct usb_serial *serial); +-static void digi_disconnect(struct usb_serial *serial); + static void digi_release(struct usb_serial *serial); + static int digi_port_probe(struct usb_serial_port *port); + static void digi_port_remove(struct usb_serial_port *port); +@@ -272,7 +265,7 @@ static struct usb_serial_driver digi_acceleport_2_device = { + .dtr_rts = digi_dtr_rts, + .write = digi_write, + .write_room = digi_write_room, +- .write_bulk_callback = digi_write_bulk_callback, ++ .write_bulk_callback = digi_write_bulk_callback, + .read_bulk_callback = digi_read_bulk_callback, + .chars_in_buffer = digi_chars_in_buffer, + .throttle = digi_rx_throttle, +@@ -282,7 +275,6 @@ static struct usb_serial_driver digi_acceleport_2_device = { + .tiocmget = digi_tiocmget, + .tiocmset = digi_tiocmset, + .attach = digi_startup, +- .disconnect = digi_disconnect, + .release = digi_release, + .port_probe = digi_port_probe, + .port_remove = digi_port_remove, +@@ -301,7 +293,7 @@ static struct usb_serial_driver digi_acceleport_4_device = { + .close = digi_close, + .write = digi_write, + .write_room = digi_write_room, +- .write_bulk_callback = digi_write_bulk_callback, ++ .write_bulk_callback = digi_write_bulk_callback, + .read_bulk_callback = digi_read_bulk_callback, + .chars_in_buffer = digi_chars_in_buffer, + .throttle = digi_rx_throttle, +@@ -311,7 +303,6 @@ static struct usb_serial_driver digi_acceleport_4_device = { + .tiocmget = digi_tiocmget, + .tiocmset = digi_tiocmset, + .attach = digi_startup, +- .disconnect = digi_disconnect, + .release = digi_release, + .port_probe = digi_port_probe, + .port_remove = digi_port_remove, +@@ -335,7 +326,6 @@ static struct usb_serial_driver * const serial_drivers[] = { + * interruptible_sleep_on_timeout is deprecated and has been replaced + * with the equivalent code. + */ +- + static long cond_wait_interruptible_timeout_irqrestore( + wait_queue_head_t *q, long timeout, + spinlock_t *lock, unsigned long flags) +@@ -351,6 +341,13 @@ __releases(lock) + return timeout; + } + ++static struct usb_serial_port *digi_get_oob_port(struct usb_serial *serial) ++{ ++ struct digi_serial *serial_priv = usb_get_serial_data(serial); ++ ++ return serial_priv->ds_oob_port; ++} ++ + /* + * Digi Write OOB Command + * +@@ -361,15 +358,14 @@ __releases(lock) + * the interruptible flag is true, or a negative error + * returned by usb_submit_urb. + */ +- + static int digi_write_oob_command(struct usb_serial_port *port, + unsigned char *buf, int count, int interruptible) + { +- int ret = 0; +- int len; +- struct usb_serial_port *oob_port = (struct usb_serial_port *)((struct digi_serial *)(usb_get_serial_data(port->serial)))->ds_oob_port; ++ struct usb_serial_port *oob_port = digi_get_oob_port(port->serial); + struct digi_port *oob_priv = usb_get_serial_port_data(oob_port); + unsigned long flags; ++ int ret = 0; ++ int len; + + dev_dbg(&port->dev, + "digi_write_oob_command: TOP: port=%d, count=%d\n", +@@ -406,10 +402,8 @@ static int digi_write_oob_command(struct usb_serial_port *port, + dev_err(&port->dev, "%s: usb_submit_urb failed, ret=%d\n", + __func__, ret); + return ret; +- + } + +- + /* + * Digi Write In Band Command + * +@@ -421,16 +415,15 @@ static int digi_write_oob_command(struct usb_serial_port *port, + * timeout ticks. Returns 0 if successful, or a negative + * error returned by digi_write. + */ +- + static int digi_write_inb_command(struct usb_serial_port *port, + unsigned char *buf, int count, unsigned long timeout) + { +- int ret = 0; +- int len; + struct digi_port *priv = usb_get_serial_port_data(port); + unsigned char *data = port->write_urb->transfer_buffer; + unsigned long expire; + unsigned long flags; ++ int ret = 0; ++ int len; + + dev_dbg(&port->dev, "digi_write_inb_command: TOP: port=%d, count=%d\n", + priv->dp_port_num, count); +@@ -459,7 +452,7 @@ static int digi_write_inb_command(struct usb_serial_port *port, + /* len must be a multiple of 4 and small enough to */ + /* guarantee the write will send buffered data first, */ + /* so commands are in order with data and not split */ +- len = min(count, port->bulk_out_size-2-priv->dp_out_buf_len); ++ len = min(count, port->bulk_out_size - 2 - priv->dp_out_buf_len); + if (len > 4) + len &= ~3; + +@@ -467,11 +460,10 @@ static int digi_write_inb_command(struct usb_serial_port *port, + if (priv->dp_out_buf_len > 0) { + data[0] = DIGI_CMD_SEND_DATA; + data[1] = priv->dp_out_buf_len; +- memcpy(data + 2, priv->dp_out_buf, +- priv->dp_out_buf_len); ++ memcpy(data + 2, priv->dp_out_buf, priv->dp_out_buf_len); + memcpy(data + 2 + priv->dp_out_buf_len, buf, len); +- port->write_urb->transfer_buffer_length +- = priv->dp_out_buf_len + 2 + len; ++ port->write_urb->transfer_buffer_length = ++ priv->dp_out_buf_len + 2 + len; + } else { + memcpy(data, buf, len); + port->write_urb->transfer_buffer_length = len; +@@ -484,7 +476,6 @@ static int digi_write_inb_command(struct usb_serial_port *port, + count -= len; + buf += len; + } +- + } + spin_unlock_irqrestore(&priv->dp_port_lock, flags); + +@@ -495,7 +486,6 @@ static int digi_write_inb_command(struct usb_serial_port *port, + return ret; + } + +- + /* + * Digi Set Modem Signals + * +@@ -505,17 +495,15 @@ static int digi_write_inb_command(struct usb_serial_port *port, + * -EINTR if interrupted while sleeping, or a non-zero error + * returned by usb_submit_urb. + */ +- + static int digi_set_modem_signals(struct usb_serial_port *port, + unsigned int modem_signals, int interruptible) + { +- +- int ret; + struct digi_port *port_priv = usb_get_serial_port_data(port); +- struct usb_serial_port *oob_port = (struct usb_serial_port *) ((struct digi_serial *)(usb_get_serial_data(port->serial)))->ds_oob_port; ++ struct usb_serial_port *oob_port = digi_get_oob_port(port->serial); + struct digi_port *oob_priv = usb_get_serial_port_data(oob_port); + unsigned char *data = oob_port->write_urb->transfer_buffer; + unsigned long flags; ++ int ret; + + dev_dbg(&port->dev, + "digi_set_modem_signals: TOP: port=%d, modem_signals=0x%x\n", +@@ -573,14 +561,13 @@ static int digi_set_modem_signals(struct usb_serial_port *port, + * is only called from close, and only one process can be in close on a + * port at a time, so its ok. + */ +- + static int digi_transmit_idle(struct usb_serial_port *port, + unsigned long timeout) + { +- int ret; +- unsigned char buf[2]; + struct digi_port *priv = usb_get_serial_port_data(port); ++ unsigned char buf[2]; + unsigned long flags; ++ int ret; + + spin_lock_irqsave(&priv->dp_port_lock, flags); + priv->dp_transmit_idle = 0; +@@ -607,16 +594,15 @@ static int digi_transmit_idle(struct usb_serial_port *port, + } + priv->dp_transmit_idle = 0; + spin_unlock_irqrestore(&priv->dp_port_lock, flags); ++ + return 0; +- + } + +- + static void digi_rx_throttle(struct tty_struct *tty) + { +- unsigned long flags; + struct usb_serial_port *port = tty->driver_data; + struct digi_port *priv = usb_get_serial_port_data(port); ++ unsigned long flags; + + /* stop receiving characters by not resubmitting the read urb */ + spin_lock_irqsave(&priv->dp_port_lock, flags); +@@ -625,13 +611,12 @@ static void digi_rx_throttle(struct tty_struct *tty) + spin_unlock_irqrestore(&priv->dp_port_lock, flags); + } + +- + static void digi_rx_unthrottle(struct tty_struct *tty) + { +- int ret = 0; +- unsigned long flags; + struct usb_serial_port *port = tty->driver_data; + struct digi_port *priv = usb_get_serial_port_data(port); ++ unsigned long flags; ++ int ret = 0; + + spin_lock_irqsave(&priv->dp_port_lock, flags); + +@@ -651,7 +636,6 @@ static void digi_rx_unthrottle(struct tty_struct *tty) + __func__, ret, priv->dp_port_num); + } + +- + static void digi_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, + const struct ktermios *old_termios) +@@ -762,7 +746,6 @@ static void digi_set_termios(struct tty_struct *tty, + + /* set stop bits */ + if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) { +- + if ((cflag & CSTOPB)) + arg = DIGI_STOP_BITS_2; + else +@@ -772,7 +755,6 @@ static void digi_set_termios(struct tty_struct *tty, + buf[i++] = priv->dp_port_num; + buf[i++] = arg; + buf[i++] = 0; +- + } + + /* set input flow control */ +@@ -841,7 +823,6 @@ static void digi_set_termios(struct tty_struct *tty, + tty_encode_baud_rate(tty, baud, baud); + } + +- + static int digi_break_ctl(struct tty_struct *tty, int break_state) + { + struct usb_serial_port *port = tty->driver_data; +@@ -855,50 +836,48 @@ static int digi_break_ctl(struct tty_struct *tty, int break_state) + return digi_write_inb_command(port, buf, 4, 0); + } + +- + static int digi_tiocmget(struct tty_struct *tty) + { + struct usb_serial_port *port = tty->driver_data; + struct digi_port *priv = usb_get_serial_port_data(port); +- unsigned int val; + unsigned long flags; ++ unsigned int val; + + spin_lock_irqsave(&priv->dp_port_lock, flags); + val = priv->dp_modem_signals; + spin_unlock_irqrestore(&priv->dp_port_lock, flags); ++ + return val; + } + +- + static int digi_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) + { + struct usb_serial_port *port = tty->driver_data; + struct digi_port *priv = usb_get_serial_port_data(port); +- unsigned int val; + unsigned long flags; ++ unsigned int val; + + spin_lock_irqsave(&priv->dp_port_lock, flags); + val = (priv->dp_modem_signals & ~clear) | set; + spin_unlock_irqrestore(&priv->dp_port_lock, flags); ++ + return digi_set_modem_signals(port, val, 1); + } + +- + static int digi_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) + { +- +- int ret, data_len, new_len; + struct digi_port *priv = usb_get_serial_port_data(port); + unsigned char *data = port->write_urb->transfer_buffer; ++ int ret, data_len, new_len; + unsigned long flags; + + dev_dbg(&port->dev, "digi_write: TOP: port=%d, count=%d\n", + priv->dp_port_num, count); + + /* copy user data (which can sleep) before getting spin lock */ +- count = min(count, port->bulk_out_size-2); ++ count = min(count, port->bulk_out_size - 2); + count = min(64, count); + + /* be sure only one write proceeds at a time */ +@@ -920,7 +899,7 @@ static int digi_write(struct tty_struct *tty, struct usb_serial_port *port, + + /* allow space for any buffered data and for new data, up to */ + /* transfer buffer size - 2 (for command and length bytes) */ +- new_len = min(count, port->bulk_out_size-2-priv->dp_out_buf_len); ++ new_len = min(count, port->bulk_out_size - 2 - priv->dp_out_buf_len); + data_len = new_len + priv->dp_out_buf_len; + + if (data_len == 0) { +@@ -928,7 +907,7 @@ static int digi_write(struct tty_struct *tty, struct usb_serial_port *port, + return 0; + } + +- port->write_urb->transfer_buffer_length = data_len+2; ++ port->write_urb->transfer_buffer_length = data_len + 2; + + *data++ = DIGI_CMD_SEND_DATA; + *data++ = data_len; +@@ -954,39 +933,22 @@ static int digi_write(struct tty_struct *tty, struct usb_serial_port *port, + "%s: usb_submit_urb failed, ret=%d, port=%d\n", + __func__, ret, priv->dp_port_num); + dev_dbg(&port->dev, "digi_write: returning %d\n", ret); +- return ret; + ++ return ret; + } + + static void digi_write_bulk_callback(struct urb *urb) + { +- + struct usb_serial_port *port = urb->context; +- struct usb_serial *serial; +- struct digi_port *priv; +- struct digi_serial *serial_priv; ++ struct digi_serial *serial_priv = usb_get_serial_data(port->serial); ++ struct digi_port *priv = usb_get_serial_port_data(port); ++ unsigned char *data = urb->transfer_buffer; + unsigned long flags; +- int ret = 0; +- int status = urb->status; + bool wakeup; +- +- /* port and serial sanity check */ +- if (port == NULL || (priv = usb_get_serial_port_data(port)) == NULL) { +- pr_err("%s: port or port->private is NULL, status=%d\n", +- __func__, status); +- return; +- } +- serial = port->serial; +- if (serial == NULL || (serial_priv = usb_get_serial_data(serial)) == NULL) { +- dev_err(&port->dev, +- "%s: serial or serial->private is NULL, status=%d\n", +- __func__, status); +- return; +- } ++ int ret = 0; + + /* handle oob callback */ + if (priv->dp_port_num == serial_priv->ds_oob_port_num) { +- dev_dbg(&port->dev, "digi_write_bulk_callback: oob callback\n"); + spin_lock_irqsave(&priv->dp_port_lock, flags); + priv->dp_write_urb_in_use = 0; + wake_up_interruptible(&priv->write_wait); +@@ -999,15 +961,13 @@ static void digi_write_bulk_callback(struct urb *urb) + spin_lock_irqsave(&priv->dp_port_lock, flags); + priv->dp_write_urb_in_use = 0; + if (priv->dp_out_buf_len > 0) { +- *((unsigned char *)(port->write_urb->transfer_buffer)) +- = (unsigned char)DIGI_CMD_SEND_DATA; +- *((unsigned char *)(port->write_urb->transfer_buffer) + 1) +- = (unsigned char)priv->dp_out_buf_len; +- port->write_urb->transfer_buffer_length = +- priv->dp_out_buf_len + 2; +- memcpy(port->write_urb->transfer_buffer + 2, priv->dp_out_buf, +- priv->dp_out_buf_len); +- ret = usb_submit_urb(port->write_urb, GFP_ATOMIC); ++ data[0] = DIGI_CMD_SEND_DATA; ++ data[1] = priv->dp_out_buf_len; ++ memcpy(data + 2, priv->dp_out_buf, priv->dp_out_buf_len); ++ ++ urb->transfer_buffer_length = priv->dp_out_buf_len + 2; ++ ++ ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret == 0) { + priv->dp_write_urb_in_use = 1; + priv->dp_out_buf_len = 0; +@@ -1041,8 +1001,8 @@ static unsigned int digi_write_room(struct tty_struct *tty) + + spin_unlock_irqrestore(&priv->dp_port_lock, flags); + dev_dbg(&port->dev, "digi_write_room: port=%d, room=%u\n", priv->dp_port_num, room); +- return room; + ++ return room; + } + + static unsigned int digi_chars_in_buffer(struct tty_struct *tty) +@@ -1070,17 +1030,53 @@ static void digi_dtr_rts(struct usb_serial_port *port, int on) + digi_set_modem_signals(port, on * (TIOCM_DTR | TIOCM_RTS), 1); + } + ++static int digi_open_oob_port(struct usb_serial *serial) ++{ ++ struct digi_serial *serial_priv = usb_get_serial_data(serial); ++ struct usb_serial_port *oob_port = serial_priv->ds_oob_port; ++ int ret = 0; ++ ++ mutex_lock(&serial_priv->open_mutex); ++ ++ if (serial_priv->open_count++ == 0) { ++ ret = usb_submit_urb(oob_port->read_urb, GFP_KERNEL); ++ if (ret) { ++ dev_err(&serial->interface->dev, "failed to submit OOB read urb: %d\n", ++ ret); ++ serial_priv->open_count--; ++ } ++ } ++ ++ mutex_unlock(&serial_priv->open_mutex); ++ ++ return ret; ++} ++ ++static void digi_close_oob_port(struct usb_serial *serial) ++{ ++ struct digi_serial *serial_priv = usb_get_serial_data(serial); ++ struct usb_serial_port *oob_port = serial_priv->ds_oob_port; ++ ++ mutex_lock(&serial_priv->open_mutex); ++ ++ if (serial_priv->open_count-- == 1) { ++ usb_kill_urb(oob_port->read_urb); ++ usb_kill_urb(oob_port->write_urb); ++ } ++ ++ mutex_unlock(&serial_priv->open_mutex); ++} ++ + static int digi_open(struct tty_struct *tty, struct usb_serial_port *port) + { +- int ret; +- unsigned char buf[32]; + struct digi_port *priv = usb_get_serial_port_data(port); + struct ktermios not_termios; +- int throttled; ++ unsigned char buf[32]; ++ int ret; + +- /* be sure the device is started up */ +- if (digi_startup_device(port->serial) != 0) +- return -ENXIO; ++ ret = digi_open_oob_port(port->serial); ++ if (ret) ++ return ret; + + /* read modem signals automatically whenever they change */ + buf[0] = DIGI_CMD_READ_INPUT_SIGNALS; +@@ -1106,32 +1102,34 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port) + } + + spin_lock_irq(&priv->dp_port_lock); +- throttled = priv->dp_throttle_restart; + priv->dp_throttled = 0; + priv->dp_throttle_restart = 0; + spin_unlock_irq(&priv->dp_port_lock); + +- if (throttled) { +- ret = usb_submit_urb(port->read_urb, GFP_KERNEL); +- if (ret) { +- dev_err(&port->dev, "failed to submit read urb: %d\n", ret); +- return ret; +- } ++ ret = usb_submit_urb(port->read_urb, GFP_KERNEL); ++ if (ret) { ++ dev_err(&port->dev, "failed to submit read urb: %d\n", ret); ++ goto err_close_oob; + } + + return 0; +-} + ++err_close_oob: ++ digi_close_oob_port(port->serial); ++ ++ return ret; ++} + + static void digi_close(struct usb_serial_port *port) + { ++ struct digi_port *priv = usb_get_serial_port_data(port); ++ unsigned char buf[32]; + DEFINE_WAIT(wait); + int ret; +- unsigned char buf[32]; +- struct digi_port *priv = usb_get_serial_port_data(port); ++ ++ usb_kill_urb(port->read_urb); + + mutex_lock(&port->serial->disc_mutex); +- /* if disconnected, just clear flags */ + if (port->serial->disconnected) + goto exit; + +@@ -1177,57 +1175,18 @@ static void digi_close(struct usb_serial_port *port) + TASK_INTERRUPTIBLE); + schedule_timeout(DIGI_CLOSE_TIMEOUT); + finish_wait(&priv->dp_flush_wait, &wait); ++exit: ++ mutex_unlock(&port->serial->disc_mutex); + + /* shutdown any outstanding bulk writes */ + usb_kill_urb(port->write_urb); +-exit: +- spin_lock_irq(&priv->dp_port_lock); +- priv->dp_write_urb_in_use = 0; +- wake_up_interruptible(&priv->dp_close_wait); +- spin_unlock_irq(&priv->dp_port_lock); +- mutex_unlock(&port->serial->disc_mutex); +-} + +- +-/* +- * Digi Startup Device +- * +- * Starts reads on all ports. Must be called AFTER startup, with +- * urbs initialized. Returns 0 if successful, non-zero error otherwise. +- */ +- +-static int digi_startup_device(struct usb_serial *serial) +-{ +- int i, ret = 0; +- struct digi_serial *serial_priv = usb_get_serial_data(serial); +- struct usb_serial_port *port; +- +- /* be sure this happens exactly once */ +- spin_lock(&serial_priv->ds_serial_lock); +- if (serial_priv->ds_device_started) { +- spin_unlock(&serial_priv->ds_serial_lock); +- return 0; +- } +- serial_priv->ds_device_started = 1; +- spin_unlock(&serial_priv->ds_serial_lock); +- +- /* start reading from each bulk in endpoint for the device */ +- /* set USB_DISABLE_SPD flag for write bulk urbs */ +- for (i = 0; i < serial->type->num_ports + 1; i++) { +- port = serial->port[i]; +- ret = usb_submit_urb(port->read_urb, GFP_KERNEL); +- if (ret != 0) { +- dev_err(&port->dev, +- "%s: usb_submit_urb failed, ret=%d, port=%d\n", +- __func__, ret, i); +- break; +- } +- } +- return ret; ++ digi_close_oob_port(port->serial); + } + + static int digi_port_init(struct usb_serial_port *port, unsigned port_num) + { ++ struct digi_serial *serial_priv = usb_get_serial_data(port->serial); + struct digi_port *priv; + + priv = kzalloc_obj(*priv); +@@ -1235,10 +1194,13 @@ static int digi_port_init(struct usb_serial_port *port, unsigned port_num) + return -ENOMEM; + + spin_lock_init(&priv->dp_port_lock); ++ ++ if (port == serial_priv->ds_oob_port) ++ lockdep_set_subclass(&priv->dp_port_lock, SINGLE_DEPTH_NESTING); ++ + priv->dp_port_num = port_num; + init_waitqueue_head(&priv->dp_transmit_idle_wait); + init_waitqueue_head(&priv->dp_flush_wait); +- init_waitqueue_head(&priv->dp_close_wait); + init_waitqueue_head(&priv->write_wait); + priv->dp_port = port; + +@@ -1275,10 +1237,13 @@ static int digi_startup(struct usb_serial *serial) + if (!serial_priv) + return -ENOMEM; + +- spin_lock_init(&serial_priv->ds_serial_lock); ++ mutex_init(&serial_priv->open_mutex); ++ + serial_priv->ds_oob_port_num = oob_port_num; + serial_priv->ds_oob_port = serial->port[oob_port_num]; + ++ usb_set_serial_data(serial, serial_priv); ++ + ret = digi_port_init(serial_priv->ds_oob_port, + serial_priv->ds_oob_port_num); + if (ret) { +@@ -1286,24 +1251,9 @@ static int digi_startup(struct usb_serial *serial) + return ret; + } + +- usb_set_serial_data(serial, serial_priv); +- + return 0; + } + +- +-static void digi_disconnect(struct usb_serial *serial) +-{ +- int i; +- +- /* stop reads and writes on all ports */ +- for (i = 0; i < serial->type->num_ports + 1; i++) { +- usb_kill_urb(serial->port[i]->read_urb); +- usb_kill_urb(serial->port[i]->write_urb); +- } +-} +- +- + static void digi_release(struct usb_serial *serial) + { + struct digi_serial *serial_priv; +@@ -1333,30 +1283,25 @@ static void digi_port_remove(struct usb_serial_port *port) + static void digi_read_bulk_callback(struct urb *urb) + { + struct usb_serial_port *port = urb->context; +- struct digi_port *priv; +- struct digi_serial *serial_priv; +- int ret; ++ struct usb_serial *serial = port->serial; ++ struct digi_serial *serial_priv = usb_get_serial_data(serial); ++ struct digi_port *priv = usb_get_serial_port_data(port); + int status = urb->status; +- +- /* port sanity check, do not resubmit if port is not valid */ +- if (port == NULL) +- return; +- priv = usb_get_serial_port_data(port); +- if (priv == NULL) { +- dev_err(&port->dev, "%s: port->private is NULL, status=%d\n", +- __func__, status); +- return; +- } +- if (port->serial == NULL || +- (serial_priv = usb_get_serial_data(port->serial)) == NULL) { +- dev_err(&port->dev, "%s: serial is bad or serial->private " +- "is NULL, status=%d\n", __func__, status); +- return; +- } ++ int ret; + + /* do not resubmit urb if it has any status error */ +- if (status) { +- dev_err(&port->dev, ++ switch (status) { ++ case 0: ++ break; ++ case -ENOENT: ++ case -ECONNRESET: ++ case -ESHUTDOWN: ++ dev_err(&serial->interface->dev, ++ "%s: nonzero read bulk status: status=%d, port=%d\n", ++ __func__, status, priv->dp_port_num); ++ return; ++ default: ++ dev_err(&serial->interface->dev, + "%s: nonzero read bulk status: status=%d, port=%d\n", + __func__, status, priv->dp_port_num); + return; +@@ -1374,11 +1319,10 @@ static void digi_read_bulk_callback(struct urb *urb) + /* continue read */ + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret != 0 && ret != -EPERM) { +- dev_err(&port->dev, ++ dev_err(&serial->interface->dev, + "%s: failed resubmitting urb, ret=%d, port=%d\n", + __func__, ret, priv->dp_port_num); + } +- + } + + /* +@@ -1390,7 +1334,6 @@ static void digi_read_bulk_callback(struct urb *urb) + * It returns 0 if successful, 1 if successful but the port is + * throttled, and -1 if the sanity checks failed. + */ +- + static int digi_read_inb_callback(struct urb *urb) + { + struct usb_serial_port *port = urb->context; +@@ -1468,10 +1411,8 @@ static int digi_read_inb_callback(struct urb *urb) + dev_dbg(&port->dev, "%s: unknown opcode: %d\n", __func__, opcode); + + return throttled ? 1 : 0; +- + } + +- + /* + * Digi Read OOB Callback + * +@@ -1480,10 +1421,8 @@ static int digi_read_inb_callback(struct urb *urb) + * the port->serial is valid. It returns 0 if successful, and + * -1 if the sanity checks failed. + */ +- + static int digi_read_oob_callback(struct urb *urb) + { +- + struct usb_serial_port *port = urb->context; + struct usb_serial *serial = port->serial; + struct tty_struct *tty; +@@ -1491,8 +1430,8 @@ static int digi_read_oob_callback(struct urb *urb) + unsigned char *buf = urb->transfer_buffer; + int opcode, line, status, val; + unsigned long flags; +- int i; + unsigned int rts; ++ int i; + + if (urb->actual_length < 4) + return -1; +@@ -1504,7 +1443,8 @@ static int digi_read_oob_callback(struct urb *urb) + status = buf[i + 2]; + val = buf[i + 3]; + +- dev_dbg(&port->dev, "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d\n", ++ dev_dbg(&serial->interface->dev, ++ "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d\n", + opcode, line, status, val); + + if (status != 0 || line >= serial->type->num_ports) +@@ -1562,8 +1502,8 @@ static int digi_read_oob_callback(struct urb *urb) + } + tty_kref_put(tty); + } +- return 0; + ++ return 0; + } + + module_usb_serial_driver(serial_drivers, id_table_combined); +diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c +--- a/drivers/usb/serial/keyspan_pda.c ++++ b/drivers/usb/serial/keyspan_pda.c +@@ -31,7 +31,6 @@ + struct keyspan_pda_private { + int tx_room; + struct work_struct unthrottle_work; +- struct usb_serial *serial; + struct usb_serial_port *port; + bool throttled; + bool throttle_req; +diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c +--- a/drivers/usb/serial/metro-usb.c ++++ b/drivers/usb/serial/metro-usb.c +@@ -36,6 +36,7 @@ + struct metrousb_private { + spinlock_t lock; + int throttled; ++ int throttle_req; + unsigned long control_state; + }; + +@@ -108,7 +109,7 @@ static void metrousb_read_int_callback(struct urb *urb) + unsigned char *data = urb->transfer_buffer; + unsigned long flags; + int throttled = 0; +- int result = 0; ++ int result; + + dev_dbg(&port->dev, "%s\n", __func__); + +@@ -143,7 +144,10 @@ static void metrousb_read_int_callback(struct urb *urb) + + /* Set any port variables. */ + spin_lock_irqsave(&metro_priv->lock, flags); +- throttled = metro_priv->throttled; ++ if (metro_priv->throttle_req) { ++ metro_priv->throttled = 1; ++ throttled = 1; ++ } + spin_unlock_irqrestore(&metro_priv->lock, flags); + + if (throttled) +@@ -169,12 +173,13 @@ static int metrousb_open(struct tty_struct *tty, struct usb_serial_port *port) + struct usb_serial *serial = port->serial; + struct metrousb_private *metro_priv = usb_get_serial_port_data(port); + unsigned long flags; +- int result = 0; ++ int result; + + /* Set the private data information for the port. */ + spin_lock_irqsave(&metro_priv->lock, flags); + metro_priv->control_state = 0; + metro_priv->throttled = 0; ++ metro_priv->throttle_req = 0; + spin_unlock_irqrestore(&metro_priv->lock, flags); + + /* Clear the urb pipe. */ +@@ -269,7 +274,7 @@ static void metrousb_throttle(struct tty_struct *tty) + + /* Set the private information for the port to stop reading data. */ + spin_lock_irqsave(&metro_priv->lock, flags); +- metro_priv->throttled = 1; ++ metro_priv->throttle_req = 1; + spin_unlock_irqrestore(&metro_priv->lock, flags); + } + +@@ -321,19 +326,23 @@ static void metrousb_unthrottle(struct tty_struct *tty) + struct usb_serial_port *port = tty->driver_data; + struct metrousb_private *metro_priv = usb_get_serial_port_data(port); + unsigned long flags; +- int result = 0; ++ int throttled; ++ int result; + + /* Set the private information for the port to resume reading data. */ + spin_lock_irqsave(&metro_priv->lock, flags); ++ throttled = metro_priv->throttled; + metro_priv->throttled = 0; ++ metro_priv->throttle_req = 0; + spin_unlock_irqrestore(&metro_priv->lock, flags); + +- /* Submit the urb to read from the port. */ +- result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); +- if (result) +- dev_err(&port->dev, +- "failed submitting interrupt in urb error code=%d\n", +- result); ++ if (throttled) { ++ result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); ++ if (result) { ++ dev_err(&port->dev, "failed to submit interrupt in urb: %d\n", ++ result); ++ } ++ } + } + + static struct usb_serial_driver metrousb_device = { +diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c +--- a/drivers/usb/serial/pl2303.c ++++ b/drivers/usb/serial/pl2303.c +@@ -51,6 +51,7 @@ static const struct usb_device_id id_table[] = { + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GL) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GE) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GS) }, ++ { USB_DEVICE(PL2303_VENDOR_ID, PL256X_PRODUCT_ID_4P) }, + { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) }, + { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) }, + { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID), +@@ -141,10 +142,15 @@ MODULE_DEVICE_TABLE(usb, id_table); + #define VENDOR_WRITE_REQUEST_TYPE 0x40 + #define VENDOR_WRITE_REQUEST 0x01 + #define VENDOR_WRITE_NREQUEST 0x80 ++#define VENDOR_WRITE_MPREQUEST 0x80 + + #define VENDOR_READ_REQUEST_TYPE 0xc0 + #define VENDOR_READ_REQUEST 0x01 + #define VENDOR_READ_NREQUEST 0x81 ++#define VENDOR_READ_MPREQUEST 0x80 ++ ++#define PL256X_RESET_REQUEST_TYPE 0x40 ++#define PL256X_RESET_REQUEST 0x96 + + #define UART_STATE_INDEX 8 + #define UART_STATE_MSR_MASK 0x8b +@@ -172,6 +178,16 @@ MODULE_DEVICE_TABLE(usb, id_table); + #define PL2303_HXN_FLOWCTRL_RTS_CTS 0x18 + #define PL2303_HXN_FLOWCTRL_XON_XOFF 0x0c + ++#define PL256X_PORT_A_FLOWCTRL_REG 0xc005 ++#define PL256X_PORT_B_FLOWCTRL_REG 0xd005 ++#define PL256X_PORT_C_FLOWCTRL_REG 0xe005 ++#define PL256X_PORT_D_FLOWCTRL_REG 0xf005 ++ ++#define PL256X_FLOWCTRL_MASK 0x43 ++#define PL256X_FLOWCTRL_XON_XOFF 0x40 ++#define PL256X_FLOWCTRL_RTS_CTS 0x03 ++#define PL256X_FLOWCTRL_NONE 0x00 ++ + static int pl2303_set_break(struct usb_serial_port *port, bool enable); + + enum pl2303_type { +@@ -181,6 +197,7 @@ enum pl2303_type { + TYPE_TB, + TYPE_HXD, + TYPE_HXN, ++ TYPE_MP, + TYPE_COUNT + }; + +@@ -196,6 +213,8 @@ struct pl2303_type_data { + struct pl2303_serial_private { + const struct pl2303_type_data *type; + unsigned long quirks; ++ u16 interface_num; ++ u16 flowctrl_reg; + }; + + struct pl2303_private { +@@ -236,8 +255,30 @@ static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = { + .max_baud_rate = 12000000, + .no_divisors = true, + }, ++ [TYPE_MP] = { ++ .name = "MP", ++ .max_baud_rate = 24000000, ++ .no_divisors = true, ++ }, + }; + ++static int pl256x_uart_reset(struct usb_serial *serial) ++{ ++ struct pl2303_serial_private *spriv = usb_get_serial_data(serial); ++ struct device *dev = &serial->interface->dev; ++ int res; ++ ++ res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), ++ PL256X_RESET_REQUEST, PL256X_RESET_REQUEST_TYPE, ++ 0, spriv->interface_num, NULL, 0, 100); ++ if (res) { ++ dev_err(dev, "failed to reset device: %d\n", res); ++ return res; ++ } ++ ++ return 0; ++} ++ + static int pl2303_vendor_read(struct usb_serial *serial, u16 value, + unsigned char buf[1]) + { +@@ -248,6 +289,8 @@ static int pl2303_vendor_read(struct usb_serial *serial, u16 value, + + if (spriv->type == &pl2303_type_data[TYPE_HXN]) + request = VENDOR_READ_NREQUEST; ++ else if (spriv->type == &pl2303_type_data[TYPE_MP]) ++ request = VENDOR_READ_MPREQUEST; + else + request = VENDOR_READ_REQUEST; + +@@ -279,6 +322,8 @@ static int pl2303_vendor_write(struct usb_serial *serial, u16 value, u16 index) + + if (spriv->type == &pl2303_type_data[TYPE_HXN]) + request = VENDOR_WRITE_NREQUEST; ++ else if (spriv->type == &pl2303_type_data[TYPE_MP]) ++ request = VENDOR_WRITE_MPREQUEST; + else + request = VENDOR_WRITE_REQUEST; + +@@ -304,10 +349,12 @@ static int pl2303_update_reg(struct usb_serial *serial, u8 reg, u8 mask, u8 val) + if (!buf) + return -ENOMEM; + +- if (spriv->type == &pl2303_type_data[TYPE_HXN]) ++ if (spriv->type == &pl2303_type_data[TYPE_HXN] || ++ spriv->type == &pl2303_type_data[TYPE_MP]) { + ret = pl2303_vendor_read(serial, reg, buf); +- else ++ } else { + ret = pl2303_vendor_read(serial, reg | 0x80, buf); ++ } + + if (ret) + goto out_free; +@@ -458,6 +505,14 @@ static int pl2303_detect_type(struct usb_serial *serial) + case 0x905: /* GT-2AB */ + case 0x1005: /* GC-Q20 */ + return TYPE_HXN; ++ case 0x3302: /* PL2533 VC 2 Port */ ++ case 0x3304: /* PL2533 VC 4 Port */ ++ case 0x4302: /* PL2543 VC 2 Port */ ++ case 0x4304: /* PL2543 VC 4 Port */ ++ case 0x6502: /* PL2565 VC 2 Port */ ++ case 0x6504: /* PL2565 VC 4 Port */ ++ case 0x6506: /* PL2565 VC 4 Port QFN64 package */ ++ return TYPE_MP; + } + break; + } +@@ -491,6 +546,7 @@ static int pl2303_startup(struct usb_serial *serial) + struct pl2303_serial_private *spriv; + enum pl2303_type type; + unsigned char *buf; ++ unsigned int ifnum; + int ret; + + ret = pl2303_detect_type(serial); +@@ -500,20 +556,43 @@ static int pl2303_startup(struct usb_serial *serial) + type = ret; + dev_dbg(&serial->interface->dev, "device type: %s\n", pl2303_type_data[type].name); + ++ ifnum = serial->interface->altsetting->desc.bInterfaceNumber; ++ + spriv = kzalloc_obj(*spriv); + if (!spriv) + return -ENOMEM; + ++ if (type == TYPE_MP) { ++ switch (ifnum) { ++ case 0: ++ spriv->flowctrl_reg = PL256X_PORT_A_FLOWCTRL_REG; ++ break; ++ case 1: ++ spriv->flowctrl_reg = PL256X_PORT_B_FLOWCTRL_REG; ++ break; ++ case 2: ++ spriv->flowctrl_reg = PL256X_PORT_C_FLOWCTRL_REG; ++ break; ++ case 3: ++ spriv->flowctrl_reg = PL256X_PORT_D_FLOWCTRL_REG; ++ break; ++ default: ++ kfree(spriv); ++ return -ENODEV; ++ } ++ } ++ + spriv->type = &pl2303_type_data[type]; + spriv->quirks = (unsigned long)usb_get_serial_data(serial); + spriv->quirks |= spriv->type->quirks; ++ spriv->interface_num = ifnum; + + if (type == TYPE_HXD && pl2303_is_hxd_clone(serial)) + spriv->quirks |= PL2303_QUIRK_NO_BREAK_GETLINE; + + usb_set_serial_data(serial, spriv); + +- if (type != TYPE_HXN) { ++ if (type != TYPE_HXN && type != TYPE_MP) { + buf = kmalloc(1, GFP_KERNEL); + if (!buf) { + kfree(spriv); +@@ -575,13 +654,15 @@ static void pl2303_port_remove(struct usb_serial_port *port) + static int pl2303_set_control_lines(struct usb_serial_port *port, u8 value) + { + struct usb_device *dev = port->serial->dev; ++ struct usb_serial *serial = port->serial; ++ struct pl2303_serial_private *spriv = usb_get_serial_data(serial); + int retval; + + dev_dbg(&port->dev, "%s - %02x\n", __func__, value); + + retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE, +- value, 0, NULL, 0, 100); ++ value, spriv->interface_num, NULL, 0, 100); + if (retval) + dev_err(&port->dev, "%s - failed: %d\n", __func__, retval); + +@@ -761,7 +842,7 @@ static int pl2303_get_line_request(struct usb_serial_port *port, + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, +- 0, 0, buf, 7, 100); ++ 0, spriv->interface_num, buf, 7, 100); + if (ret != 7) { + dev_err(&port->dev, "%s - failed: %d\n", __func__, ret); + +@@ -780,11 +861,13 @@ static int pl2303_set_line_request(struct usb_serial_port *port, + unsigned char buf[7]) + { + struct usb_device *udev = port->serial->dev; ++ struct usb_serial *serial = port->serial; ++ struct pl2303_serial_private *spriv = usb_get_serial_data(serial); + int ret; + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE, +- 0, 0, buf, 7, 100); ++ 0, spriv->interface_num, buf, 7, 100); + if (ret < 0) { + dev_err(&port->dev, "%s - failed: %d\n", __func__, ret); + return ret; +@@ -939,6 +1022,9 @@ static void pl2303_set_termios(struct tty_struct *tty, + pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG, + PL2303_HXN_FLOWCTRL_MASK, + PL2303_HXN_FLOWCTRL_RTS_CTS); ++ } else if (spriv->type == &pl2303_type_data[TYPE_MP]) { ++ pl2303_vendor_write(serial, spriv->flowctrl_reg, ++ PL256X_FLOWCTRL_RTS_CTS); + } else { + pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0x60); + } +@@ -947,6 +1033,9 @@ static void pl2303_set_termios(struct tty_struct *tty, + pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG, + PL2303_HXN_FLOWCTRL_MASK, + PL2303_HXN_FLOWCTRL_XON_XOFF); ++ } else if (spriv->type == &pl2303_type_data[TYPE_MP]) { ++ pl2303_vendor_write(serial, spriv->flowctrl_reg, ++ PL256X_FLOWCTRL_XON_XOFF); + } else { + pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0xc0); + } +@@ -955,6 +1044,9 @@ static void pl2303_set_termios(struct tty_struct *tty, + pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG, + PL2303_HXN_FLOWCTRL_MASK, + PL2303_HXN_FLOWCTRL_NONE); ++ } else if (spriv->type == &pl2303_type_data[TYPE_MP]) { ++ pl2303_vendor_write(serial, spriv->flowctrl_reg, ++ PL256X_FLOWCTRL_NONE); + } else { + pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0); + } +@@ -1002,6 +1094,8 @@ static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port) + pl2303_vendor_write(serial, PL2303_HXN_RESET_REG, + PL2303_HXN_RESET_UPSTREAM_PIPE | + PL2303_HXN_RESET_DOWNSTREAM_PIPE); ++ } else if (spriv->type == &pl2303_type_data[TYPE_MP]) { ++ pl256x_uart_reset(serial); + } else { + pl2303_vendor_write(serial, 8, 0); + pl2303_vendor_write(serial, 9, 0); +@@ -1112,7 +1206,7 @@ static int pl2303_set_break(struct usb_serial_port *port, bool enable) + + result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + BREAK_REQUEST, BREAK_REQUEST_TYPE, state, +- 0, NULL, 0, 100); ++ spriv->interface_num, NULL, 0, 100); + if (result) { + dev_err(&port->dev, "error sending break = %d\n", result); + return result; +diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h +--- a/drivers/usb/serial/pl2303.h ++++ b/drivers/usb/serial/pl2303.h +@@ -26,7 +26,7 @@ + #define PL2303_PRODUCT_ID_HCR331 0x331a + #define PL2303_PRODUCT_ID_MOTOROLA 0x0307 + #define PL2303_PRODUCT_ID_ZTEK 0xe1f1 +- ++#define PL256X_PRODUCT_ID_4P 0x2533 + + #define ATEN_VENDOR_ID 0x0557 + #define ATEN_VENDOR_ID2 0x0547 +diff --git a/drivers/usb/typec/altmodes/Kconfig b/drivers/usb/typec/altmodes/Kconfig +--- a/drivers/usb/typec/altmodes/Kconfig ++++ b/drivers/usb/typec/altmodes/Kconfig +@@ -5,6 +5,7 @@ menu "USB Type-C Alternate Mode drivers" + config TYPEC_DP_ALTMODE + tristate "DisplayPort Alternate Mode driver" + depends on DRM ++ select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF + help + DisplayPort USB Type-C Alternate Mode allows DisplayPort + displays and adapters to be attached to the USB Type-C +diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c +--- a/drivers/usb/typec/altmodes/displayport.c ++++ b/drivers/usb/typec/altmodes/displayport.c +@@ -764,6 +764,7 @@ int dp_altmode_probe(struct typec_altmode *alt) + struct typec_altmode *plug = typec_altmode_get_plug(alt, TYPEC_PLUG_SOP_P); + struct fwnode_handle *fwnode; + struct dp_altmode *dp; ++ u32 cap = DP_CAP_CAPABILITY(alt->vdo); + + /* Port can only be DFP_U. */ + if (typec_altmode_get_data_role(alt) != TYPEC_HOST) +@@ -778,6 +779,18 @@ int dp_altmode_probe(struct typec_altmode *alt) + return -ENODEV; + } + ++ /* ++ * Make sure the DisplayPort VDO is valid (VESA DPAM v2.1a, Section ++ * 5.4.1, Table 5-6, DP Capabilities VDO). A device exposing DP on a ++ * USB-C receptacle must advertise at least one pin assignment for the ++ * capability it claims, otherwise Alt Mode can never be configured. ++ */ ++ if ((cap == DP_CAP_DFP_D && !DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo)) || ++ (cap == DP_CAP_UFP_D && !DP_CAP_PIN_ASSIGN_UFP_D(alt->vdo))) { ++ typec_altmode_put_plug(plug); ++ return -ENODEV; ++ } ++ + dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL); + if (!dp) { + typec_altmode_put_plug(plug); +@@ -790,7 +803,6 @@ int dp_altmode_probe(struct typec_altmode *alt) + dp->alt = alt; + + alt->desc = "DisplayPort"; +- typec_altmode_set_ops(alt, &dp_altmode_ops); + + if (plug) { + plug->desc = "Displayport"; +@@ -811,6 +823,10 @@ int dp_altmode_probe(struct typec_altmode *alt) + if (plug) + typec_altmode_set_drvdata(plug, dp); + ++ if ((alt->vdo & DP_CAP_RECEPTACLE) && typec_cable_altmode_unsupported(alt)) ++ return 0; ++ ++ typec_altmode_set_ops(alt, &dp_altmode_ops); + if (!alt->mode_selection) { + dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER; + schedule_work(&dp->work); +diff --git a/drivers/usb/typec/altmodes/thunderbolt.c b/drivers/usb/typec/altmodes/thunderbolt.c +--- a/drivers/usb/typec/altmodes/thunderbolt.c ++++ b/drivers/usb/typec/altmodes/thunderbolt.c +@@ -284,6 +284,10 @@ static int tbt_altmode_probe(struct typec_altmode *alt) + + alt->desc = "Thunderbolt3"; + typec_altmode_set_drvdata(alt, tbt); ++ ++ if (typec_cable_altmode_unsupported(alt)) ++ return 0; ++ + typec_altmode_set_ops(alt, &tbt_altmode_ops); + + if (!alt->mode_selection && tbt_ready(alt)) { +diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c +--- a/drivers/usb/typec/class.c ++++ b/drivers/usb/typec/class.c +@@ -1429,6 +1429,77 @@ int typec_cable_is_active(struct typec_cable *cable) + } + EXPORT_SYMBOL_GPL(typec_cable_is_active); + ++enum typec_cable_altmode_support { ++ CABLE_SUPPORT_UNKNOWN, ++ CABLE_SUPPORTED, ++ CABLE_NOT_SUPPORTED, ++}; ++ ++static enum typec_cable_altmode_support ++typec_cable_check_altmode_support(struct typec_cable *cable, ++ struct typec_altmode *alt) ++{ ++ struct typec_altmode *plug; ++ u32 speed; ++ ++ /* ++ * Check if the cable has an e-marker, supports modal operation, and the ++ * SOP' altmode nodes are created. ++ */ ++ plug = typec_altmode_get_plug(alt, TYPEC_PLUG_SOP_P); ++ if (plug) { ++ typec_altmode_put_plug(plug); ++ return CABLE_SUPPORTED; ++ } ++ ++ /* The identity is not specified */ ++ if (!cable->identity) ++ return CABLE_SUPPORT_UNKNOWN; ++ ++ /* Non-e-marked cable */ ++ if (!cable->identity->id_header) ++ return CABLE_NOT_SUPPORTED; ++ ++ switch (PD_IDH_PTYPE(cable->identity->id_header)) { ++ case IDH_PTYPE_PCABLE: ++ speed = VDO_TYPEC_CABLE_SPEED(cable->identity->vdo[0]); ++ if (speed == CABLE_USB2_ONLY) ++ return CABLE_NOT_SUPPORTED; ++ return CABLE_SUPPORTED; ++ case IDH_PTYPE_ACABLE: ++ /* ++ * Active cables must establish an SOP' communication ++ * node. Since that check failed at the beginning of ++ * this function, this active cable does not support ++ * this specific altmode. ++ */ ++ return CABLE_NOT_SUPPORTED; ++ } ++ ++ return CABLE_SUPPORT_UNKNOWN; ++} ++ ++/** ++ * typec_cable_altmode_unsupported - Check if a cable restricts altmode ++ * @alt: The Alternate Mode to evaluate ++ * ++ * Returns true if the connected cable is incapable of handling the altmode. ++ */ ++bool typec_cable_altmode_unsupported(struct typec_altmode *alt) ++{ ++ enum typec_cable_altmode_support support = CABLE_SUPPORT_UNKNOWN; ++ struct typec_cable *cable; ++ ++ cable = typec_cable_get(typec_altmode2port(alt)); ++ if (cable) { ++ support = typec_cable_check_altmode_support(cable, alt); ++ typec_cable_put(cable); ++ } ++ ++ return support == CABLE_NOT_SUPPORTED; ++} ++EXPORT_SYMBOL_GPL(typec_cable_altmode_unsupported); ++ + /** + * typec_cable_set_identity - Report result from Discover Identity command + * @cable: The cable updated identity values +diff --git a/drivers/usb/typec/mux/tusb1046.c b/drivers/usb/typec/mux/tusb1046.c +--- a/drivers/usb/typec/mux/tusb1046.c ++++ b/drivers/usb/typec/mux/tusb1046.c +@@ -178,6 +178,7 @@ static const struct of_device_id tusb1046_match_table[] = { + {.compatible = "ti,tusb1046"}, + {}, + }; ++MODULE_DEVICE_TABLE(of, tusb1046_match_table); + + static struct i2c_driver tusb1046_driver = { + .driver = { +diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c +--- a/drivers/usb/typec/tcpm/tcpm.c ++++ b/drivers/usb/typec/tcpm/tcpm.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -212,6 +213,24 @@ static const char * const tcpm_ams_str[] = { + FOREACH_AMS(GENERATE_STRING) + }; + ++#define FOREACH_VDM_DISCOVERY(S) \ ++ S(VDM_DISCOVERY_UNKNOWN), \ ++ S(VDM_DISCOVERY_PARTNER_IDENT), \ ++ S(VDM_DISCOVERY_CABLE_IDENT), \ ++ S(VDM_DISCOVERY_PARTNER_SVIDS), \ ++ S(VDM_DISCOVERY_PARTNER_MODES), \ ++ S(VDM_DISCOVERY_CABLE_SVIDS), \ ++ S(VDM_DISCOVERY_CABLE_MODES), \ ++ S(VDM_DISCOVERY_COMPLETE) ++ ++enum vdm_discovery_states { ++ FOREACH_VDM_DISCOVERY(GENERATE_ENUM) ++}; ++ ++static const char * const vdm_discovery_state_strings[] = { ++ FOREACH_VDM_DISCOVERY(GENERATE_STRING) ++}; ++ + enum vdm_states { + VDM_STATE_ERR_BUSY = -3, + VDM_STATE_ERR_SEND = -2, +@@ -232,7 +251,9 @@ enum pd_msg_request { + PD_MSG_DATA_SINK_CAP, + PD_MSG_DATA_SOURCE_CAP, + PD_MSG_DATA_REV, +- PD_MSG_EXT_SINK_CAP_EXT ++ PD_MSG_EXT_SINK_CAP_EXT, ++ PD_MSG_DATA_BATT_STATUS, ++ PD_MSG_EXT_BATT_CAP, + }; + + enum adev_actions { +@@ -274,7 +295,7 @@ enum frs_typec_current { + #define ALTMODE_DISCOVERY_MAX (SVID_DISCOVERY_MAX * MODE_DISCOVERY_MAX) + + #define GET_SINK_CAP_RETRY_MS 100 +-#define SEND_DISCOVER_RETRY_MS 100 ++#define SEND_DISCOVERY_VDM_RETRY_MS 100 + + struct pd_mode_data { + int svid_index; /* current SVID index */ +@@ -387,7 +408,15 @@ struct pd_timings { + }; + + /* Convert microwatt to watt */ +-#define UW_TO_W(pow) ((pow) / 1000000) ++#define UW_TO_W(pow) (div_u64((pow), 1000000)) ++ ++/* ++ * As per USB PD Spec Rev 3.18 (Sec. 6.5.13.11), the number of fixed batteries ++ * that a port can be queried is restricted to 4. ++ */ ++#define MAX_NUM_FIXED_BATT 4 ++ ++#define BATTERY_PROPERTY_UNKNOWN 0xffff + + /* + * struct pd_identifier - Contains info about PD identifiers +@@ -483,8 +512,6 @@ struct tcpm_port { + bool vbus_source; + bool vbus_charge; + +- /* Set to true when Discover_Identity Command is expected to be sent in Ready states. */ +- bool send_discover; + bool op_vsafe5v; + + int try_role; +@@ -510,8 +537,8 @@ struct tcpm_port { + struct kthread_work vdm_state_machine; + struct hrtimer enable_frs_timer; + struct kthread_work enable_frs; +- struct hrtimer send_discover_timer; +- struct kthread_work send_discover_work; ++ struct hrtimer vdm_discovery_timer; ++ struct kthread_work vdm_discovery_work; + bool state_machine_running; + /* Set to true when VDM State Machine has following actions. */ + bool vdm_sm_running; +@@ -578,6 +605,9 @@ struct tcpm_port { + + u32 bist_request; + ++ /* VDM Discovery State to determine message sent */ ++ enum vdm_discovery_states vdm_discovery_state; ++ + /* PD state for Vendor Defined Messages */ + enum vdm_states vdm_state; + u32 vdm_retries; +@@ -641,12 +671,6 @@ struct tcpm_port { + bool potential_contaminant; + + /* SOP* Related Fields */ +- /* +- * Flag to determine if SOP' Discover Identity is available. The flag +- * is set if Discover Identity on SOP' does not immediately follow +- * Discover Identity on SOP. +- */ +- bool send_discover_prime; + /* + * tx_sop_type determines which SOP* a message is being sent on. + * For messages that are queued and not sent immediately such as in +@@ -683,6 +707,9 @@ struct tcpm_port { + + struct pd_identifier pd_ident; + struct sink_caps_ext_data sink_caps_ext; ++ struct power_supply **fixed_batt; ++ u32 fixed_batt_cnt; ++ u32 batt_request_id; + #ifdef CONFIG_DEBUG_FS + struct dentry *dentry; + struct mutex logbuffer_lock; /* log buffer access lock */ +@@ -771,6 +798,9 @@ static const char * const pd_rev[] = { + #define tcpm_wait_for_discharge(port) \ + (((port)->auto_vbus_discharge_enabled && !(port)->vbus_vsafe0v) ? PD_T_SAFE_0V : 0) + ++#define tcpm_can_send_vdm(state) \ ++ ((state == SRC_READY || state == SNK_READY || state == SRC_VDM_IDENTITY_REQUEST)) ++ + static enum tcpm_state tcpm_default_state(struct tcpm_port *port) + { + if (port->port_type == TYPEC_PORT_DRP) { +@@ -1470,6 +1500,20 @@ static int tcpm_pd_send_sink_caps(struct tcpm_port *port) + return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); + } + ++static void tcpm_get_fixed_batt(struct tcpm_port *port) ++{ ++ int ret; ++ ++ if (!port->self_powered || port->fixed_batt_cnt > 0) ++ return; ++ ++ ret = power_supply_get_system_batteries(port->dev, &port->fixed_batt); ++ if (ret < 0) ++ tcpm_log(port, "Failed to get battery array, ret=%d", ret); ++ else ++ port->fixed_batt_cnt = ret; ++} ++ + static int tcpm_pd_send_sink_cap_ext(struct tcpm_port *port) + { + u16 operating_snk_watt = port->operating_snk_mw / 1000; +@@ -1482,6 +1526,8 @@ static int tcpm_pd_send_sink_cap_ext(struct tcpm_port *port) + if (!port->self_powered) + data->spr_op_pdp = operating_snk_watt; + ++ tcpm_get_fixed_batt(port); ++ + /* + * SPR Sink Minimum PDP indicates the minimum power required to operate + * a sink device in its lowest level of functionality without requiring +@@ -1507,6 +1553,7 @@ static int tcpm_pd_send_sink_cap_ext(struct tcpm_port *port) + skedb.load_step = data->load_step; + skedb.load_char = cpu_to_le16(data->load_char); + skedb.compliance = data->compliance; ++ skedb.batt_info = min(port->fixed_batt_cnt, MAX_NUM_FIXED_BATT); + skedb.modes = data->modes; + skedb.spr_min_pdp = data->spr_min_pdp; + skedb.spr_op_pdp = data->spr_op_pdp; +@@ -1525,6 +1572,153 @@ static int tcpm_pd_send_sink_cap_ext(struct tcpm_port *port) + port->message_id, + data_obj_cnt, + 1 /* Denotes if ext header */)); ++ ++ return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); ++} ++ ++static u16 tcpm_charge_to_energy(int charge, int voltage) ++{ ++ u64 energy = div_u64((u64)charge * voltage, 1000000); ++ ++ /* Battery telemetry is reported in increments of 0.1Wh */ ++ return (u16)UW_TO_W(energy * 10); ++} ++ ++static int tcpm_pd_send_batt_status(struct tcpm_port *port) ++{ ++ u16 present_charge = BATTERY_PROPERTY_UNKNOWN; ++ bool batt_present = false, invalid_ref = true; ++ u32 batt_id = port->batt_request_id; ++ union power_supply_propval val; ++ struct power_supply *batt; ++ u8 charging_status = 0; ++ struct pd_message msg; ++ int ret, charge_now; ++ u32 bsdo; ++ ++ tcpm_get_fixed_batt(port); ++ memset(&msg, 0, sizeof(msg)); ++ ++ if (batt_id >= port->fixed_batt_cnt || batt_id >= MAX_NUM_FIXED_BATT) ++ goto send_status; ++ ++ invalid_ref = false; ++ batt = port->fixed_batt[batt_id]; ++ ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_PRESENT, &val); ++ if (ret) ++ tcpm_log(port, ++ "Failed to fetch power_supply_prop_present ret %d", ++ ret); ++ else ++ batt_present = val.intval > 0; ++ ++ ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_CHARGE_NOW, ++ &val); ++ if (!ret) { ++ charge_now = val.intval; ++ ret = power_supply_get_property(batt, ++ POWER_SUPPLY_PROP_VOLTAGE_AVG, ++ &val); ++ if (!ret) ++ present_charge = tcpm_charge_to_energy(charge_now, ++ val.intval); ++ } ++ ++ ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_STATUS, &val); ++ if (!ret) { ++ switch (val.intval) { ++ case POWER_SUPPLY_STATUS_CHARGING: ++ charging_status = BSDO_BATTERY_INFO_CHARGING; ++ break; ++ case POWER_SUPPLY_STATUS_DISCHARGING: ++ charging_status = BSDO_BATTERY_INFO_DISCHARGING; ++ break; ++ case POWER_SUPPLY_STATUS_NOT_CHARGING: ++ case POWER_SUPPLY_STATUS_FULL: ++ charging_status = BSDO_BATTERY_INFO_IDLE; ++ break; ++ default: ++ charging_status = BSDO_BATTERY_INFO_RSVD; ++ break; ++ } ++ } ++ ++send_status: ++ ++ bsdo = BSDO(present_charge, charging_status, batt_present, invalid_ref); ++ msg.payload[0] = cpu_to_le32(bsdo); ++ msg.header = PD_HEADER_LE(PD_DATA_BATT_STATUS, ++ port->pwr_role, ++ port->data_role, ++ port->negotiated_rev, ++ port->message_id, ++ 1); ++ ++ return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); ++} ++ ++static int tcpm_pd_send_batt_cap(struct tcpm_port *port) ++{ ++ u16 design_cap = BATTERY_PROPERTY_UNKNOWN; ++ u16 charge_cap = BATTERY_PROPERTY_UNKNOWN; ++ u32 batt_id = port->batt_request_id; ++ union power_supply_propval val; ++ struct batt_cap_ext_msg bcdb; ++ struct power_supply *batt; ++ bool invalid_ref = true; ++ struct pd_message msg; ++ u8 data_obj_cnt; ++ int ret, vol; ++ ++ tcpm_get_fixed_batt(port); ++ memset(&msg, 0, sizeof(msg)); ++ ++ if (batt_id >= port->fixed_batt_cnt || batt_id >= MAX_NUM_FIXED_BATT) ++ goto send_cap; ++ ++ invalid_ref = false; ++ batt = port->fixed_batt[batt_id]; ++ ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_VOLTAGE_AVG, ++ &val); ++ if (!ret) { ++ vol = val.intval; ++ ret = power_supply_get_property(batt, ++ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, ++ &val); ++ if (!ret) ++ design_cap = tcpm_charge_to_energy(val.intval, vol); ++ ++ ret = power_supply_get_property(batt, ++ POWER_SUPPLY_PROP_CHARGE_FULL, ++ &val); ++ if (!ret) ++ charge_cap = tcpm_charge_to_energy(val.intval, vol); ++ } ++ ++send_cap: ++ ++ /* ++ * As per the USB PD Rev3.1 v1.8 spec, if a battery VID (assigned by the ++ * USB-IF) does not exist or an invalid battery reference is made by the ++ * requestor, then set the VID field to 0xffff. If the VID field is ++ * 0xffff, set the PID field to 0. ++ */ ++ bcdb.vid = BATTERY_PROPERTY_UNKNOWN; ++ bcdb.pid = 0; ++ bcdb.batt_design_cap = cpu_to_le16(design_cap); ++ bcdb.batt_last_chg_cap = cpu_to_le16(charge_cap); ++ bcdb.batt_type = invalid_ref ? BATT_CAP_BATT_TYPE_INVALID_REF : 0; ++ memcpy(msg.ext_msg.data, &bcdb, sizeof(bcdb)); ++ msg.ext_msg.header = PD_EXT_HDR_LE(sizeof(bcdb), ++ 0, /* Denotes if request chunk */ ++ 0, /* Chunk number */ ++ 1 /* Chunked */); ++ ++ data_obj_cnt = count_chunked_data_objs(sizeof(bcdb)); ++ msg.header = PD_HEADER_EXT_LE(PD_EXT_BATT_CAP, port->pwr_role, ++ port->data_role, port->negotiated_rev, ++ port->message_id, data_obj_cnt); ++ + return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); + } + +@@ -1559,13 +1753,19 @@ static void mod_enable_frs_delayed_work(struct tcpm_port *port, unsigned int del + } + } + +-static void mod_send_discover_delayed_work(struct tcpm_port *port, unsigned int delay_ms) ++static void mod_vdm_discovery_cancel_delayed_work(struct tcpm_port *port) ++{ ++ hrtimer_cancel(&port->vdm_discovery_timer); ++ kthread_cancel_work_sync(&port->vdm_discovery_work); ++} ++ ++static void mod_vdm_discovery_delayed_work(struct tcpm_port *port, unsigned int delay_ms) + { + if (delay_ms) { +- hrtimer_start(&port->send_discover_timer, ms_to_ktime(delay_ms), HRTIMER_MODE_REL); ++ hrtimer_start(&port->vdm_discovery_timer, ms_to_ktime(delay_ms), HRTIMER_MODE_REL); + } else { +- hrtimer_cancel(&port->send_discover_timer); +- kthread_queue_work(port->wq, &port->send_discover_work); ++ hrtimer_cancel(&port->vdm_discovery_timer); ++ kthread_queue_work(port->wq, &port->vdm_discovery_work); + } + } + +@@ -1773,16 +1973,11 @@ static void tcpm_queue_vdm(struct tcpm_port *port, const u32 header, + WARN_ON(!mutex_is_locked(&port->lock)); + + /* If is sending discover_identity, handle received message first */ +- if (PD_VDO_SVDM(vdo_hdr) && PD_VDO_CMD(vdo_hdr) == CMD_DISCOVER_IDENT) { +- if (tx_sop_type == TCPC_TX_SOP_PRIME) +- port->send_discover_prime = true; +- else +- port->send_discover = true; +- mod_send_discover_delayed_work(port, SEND_DISCOVER_RETRY_MS); +- } else { ++ if (PD_VDO_SVDM(vdo_hdr) && PD_VDO_CMD(vdo_hdr) == CMD_DISCOVER_IDENT) ++ mod_vdm_discovery_delayed_work(port, SEND_DISCOVERY_VDM_RETRY_MS); ++ else + /* Make sure we are not still processing a previous VDM packet */ + WARN_ON(port->vdm_state > VDM_STATE_DONE); +- } + + port->vdo_count = cnt + 1; + port->vdo_data[0] = header; +@@ -1805,8 +2000,7 @@ static void tcpm_queue_vdm_work(struct kthread_work *work) + struct tcpm_port *port = event->port; + + mutex_lock(&port->lock); +- if (port->state != SRC_READY && port->state != SNK_READY && +- port->state != SRC_VDM_IDENTITY_REQUEST) { ++ if (!tcpm_can_send_vdm(port->state)) { + tcpm_log_force(port, "dropping altmode_vdm_event"); + goto port_unlock; + } +@@ -2034,7 +2228,7 @@ static void tcpm_register_partner_altmodes(struct tcpm_port *port) + if (!port->partner) + return; + +- for (i = 0; i < modep->altmodes; i++) { ++ for (i = 0; i < modep->altmodes && i < ALTMODE_DISCOVERY_MAX; i++) { + altmode = typec_partner_register_altmode(port->partner, + &modep->altmode_desc[i]); + if (IS_ERR(altmode)) { +@@ -2052,9 +2246,10 @@ static void tcpm_register_plug_altmodes(struct tcpm_port *port) + struct typec_altmode *altmode; + int i; + +- typec_plug_set_num_altmodes(port->plug_prime, modep->altmodes); ++ typec_plug_set_num_altmodes(port->plug_prime, ++ min(modep->altmodes, ALTMODE_DISCOVERY_MAX)); + +- for (i = 0; i < modep->altmodes; i++) { ++ for (i = 0; i < modep->altmodes && i < ALTMODE_DISCOVERY_MAX; i++) { + altmode = typec_plug_register_altmode(port->plug_prime, + &modep->altmode_desc[i]); + if (IS_ERR(altmode)) { +@@ -2154,6 +2349,19 @@ static bool tcpm_cable_vdm_supported(struct tcpm_port *port) + tcpm_can_communicate_sop_prime(port); + } + ++static void tcpm_update_vdm_discovery_state(struct tcpm_port *port, ++ enum vdm_discovery_states new_state) ++{ ++ enum vdm_discovery_states old_state = port->vdm_discovery_state; ++ ++ if (old_state != new_state) ++ tcpm_log_force(port, "vdm discovery state changed: %s -> %s", ++ vdm_discovery_state_strings[old_state], ++ vdm_discovery_state_strings[new_state]); ++ ++ port->vdm_discovery_state = new_state; ++} ++ + static int tcpm_handle_discover_mode(struct tcpm_port *port, u32 *response, + enum tcpm_transmit_type rx_sop_type, + enum tcpm_transmit_type *response_tx_sop_type) +@@ -2171,6 +2379,7 @@ static int tcpm_handle_discover_mode(struct tcpm_port *port, u32 *response, + response[0] = VDO(svid, 1, + typec_get_negotiated_svdm_version(typec), + CMD_DISCOVER_MODES); ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_PARTNER_MODES); + return 1; + } + +@@ -2179,10 +2388,12 @@ static int tcpm_handle_discover_mode(struct tcpm_port *port, u32 *response, + response[0] = VDO(USB_SID_PD, 1, + typec_get_cable_svdm_version(typec), + CMD_DISCOVER_SVID); ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_PARTNER_MODES); + return 1; + } + + tcpm_register_partner_altmodes(port); ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); + } else if (rx_sop_type == TCPC_TX_SOP_PRIME) { + modep = &port->mode_data_prime; + modep->svid_index++; +@@ -2193,11 +2404,13 @@ static int tcpm_handle_discover_mode(struct tcpm_port *port, u32 *response, + response[0] = VDO(svid, 1, + typec_get_cable_svdm_version(typec), + CMD_DISCOVER_MODES); ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_CABLE_MODES); + return 1; + } + + tcpm_register_plug_altmodes(port); + tcpm_register_partner_altmodes(port); ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); + } + + return 0; +@@ -2376,18 +2589,18 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, + typec_cable_set_svdm_version(port->cable, + svdm_version); + } ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_PARTNER_IDENT); ++ + /* 6.4.4.3.1 */ + svdm_consume_identity(port, p, cnt); + /* Attempt Vconn swap, delay SOP' discovery if necessary */ + if (tcpm_attempt_vconn_swap_discovery(port)) { +- port->send_discover_prime = true; + port->upcoming_state = VCONN_SWAP_SEND; + ret = tcpm_ams_start(port, VCONN_SWAP); + if (!ret) + return 0; + /* Cannot perform Vconn swap */ + port->upcoming_state = INVALID_STATE; +- port->send_discover_prime = false; + } + + /* +@@ -2398,7 +2611,6 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, + if (IS_ERR_OR_NULL(port->cable) && + tcpm_can_communicate_sop_prime(port)) { + *response_tx_sop_type = TCPC_TX_SOP_PRIME; +- port->send_discover_prime = true; + response[0] = VDO(USB_SID_PD, 1, + typec_get_negotiated_svdm_version(typec), + CMD_DISCOVER_IDENT); +@@ -2426,6 +2638,7 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, + tcpm_set_state(port, SRC_SEND_CAPABILITIES, 0); + return 0; + } ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_CABLE_IDENT); + + *response_tx_sop_type = TCPC_TX_SOP; + response[0] = VDO(USB_SID_PD, 1, +@@ -2445,16 +2658,27 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, + rlen = 1; + } else { + if (rx_sop_type == TCPC_TX_SOP) { ++ tcpm_update_vdm_discovery_state(port, ++ VDM_DISCOVERY_PARTNER_SVIDS); + if (modep->nsvids && supports_modal(port)) { + response[0] = VDO(modep->svids[0], 1, svdm_version, + CMD_DISCOVER_MODES); + rlen = 1; ++ } else { ++ tcpm_update_vdm_discovery_state(port, ++ VDM_DISCOVERY_COMPLETE); + } + } else if (rx_sop_type == TCPC_TX_SOP_PRIME) { ++ tcpm_update_vdm_discovery_state(port, ++ VDM_DISCOVERY_CABLE_SVIDS); + if (modep_prime->nsvids) { + response[0] = VDO(modep_prime->svids[0], 1, + svdm_version, CMD_DISCOVER_MODES); + rlen = 1; ++ } else { ++ tcpm_register_partner_altmodes(port); ++ tcpm_update_vdm_discovery_state(port, ++ VDM_DISCOVERY_COMPLETE); + } + } + } +@@ -2505,8 +2729,13 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, + case CMDT_RSP_NAK: + tcpm_ams_finish(port); + switch (cmd) { ++ /* ++ * The cable is not allowed to respond with NAK so this must've happened over SOP ++ */ + case CMD_DISCOVER_IDENT: + case CMD_DISCOVER_SVID: ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); ++ break; + case VDO_CMD_VENDOR(0) ... VDO_CMD_VENDOR(15): + break; + case CMD_DISCOVER_MODES: +@@ -2686,44 +2915,6 @@ static void tcpm_handle_vdm_request(struct tcpm_port *port, + port->vdm_sm_running = false; + } + +-static void tcpm_send_vdm(struct tcpm_port *port, u32 vid, int cmd, +- const u32 *data, int count, enum tcpm_transmit_type tx_sop_type) +-{ +- int svdm_version; +- u32 header; +- +- switch (tx_sop_type) { +- case TCPC_TX_SOP_PRIME: +- /* +- * If the port partner is discovered, then the port partner's +- * SVDM Version will be returned +- */ +- svdm_version = typec_get_cable_svdm_version(port->typec_port); +- if (svdm_version < 0) +- svdm_version = SVDM_VER_MAX; +- break; +- case TCPC_TX_SOP: +- svdm_version = typec_get_negotiated_svdm_version(port->typec_port); +- if (svdm_version < 0) +- return; +- break; +- default: +- svdm_version = typec_get_negotiated_svdm_version(port->typec_port); +- if (svdm_version < 0) +- return; +- break; +- } +- +- if (WARN_ON(count > VDO_MAX_SIZE - 1)) +- count = VDO_MAX_SIZE - 1; +- +- /* set VDM header with VID & CMD */ +- header = VDO(vid, ((vid & USB_SID_PD) == USB_SID_PD) ? +- 1 : (PD_VDO_CMD(cmd) <= CMD_ATTENTION), +- svdm_version, cmd); +- tcpm_queue_vdm(port, header, data, count, tx_sop_type); +-} +- + static unsigned int vdm_ready_timeout(u32 vdm_hdr) + { + unsigned int timeout; +@@ -2769,8 +2960,7 @@ static void vdm_run_state_machine(struct tcpm_port *port) + * if there's traffic or we're not in PDO ready state don't send + * a VDM. + */ +- if (port->state != SRC_READY && port->state != SNK_READY && +- port->state != SRC_VDM_IDENTITY_REQUEST) { ++ if (!tcpm_can_send_vdm(port->state)) { + port->vdm_sm_running = false; + break; + } +@@ -2780,22 +2970,10 @@ static void vdm_run_state_machine(struct tcpm_port *port) + switch (PD_VDO_CMD(vdo_hdr)) { + case CMD_DISCOVER_IDENT: + res = tcpm_ams_start(port, DISCOVER_IDENTITY); +- if (res == 0) { +- switch (port->tx_sop_type) { +- case TCPC_TX_SOP_PRIME: +- port->send_discover_prime = false; +- break; +- case TCPC_TX_SOP: +- port->send_discover = false; +- break; +- default: +- port->send_discover = false; +- break; +- } +- } else if (res == -EAGAIN) { ++ if (res == -EAGAIN) { + port->vdo_data[0] = 0; +- mod_send_discover_delayed_work(port, +- SEND_DISCOVER_RETRY_MS); ++ mod_vdm_discovery_delayed_work(port, ++ SEND_DISCOVERY_VDM_RETRY_MS); + } + break; + case CMD_DISCOVER_SVID: +@@ -2853,6 +3031,7 @@ static void vdm_run_state_machine(struct tcpm_port *port) + */ + if (port->state == SRC_VDM_IDENTITY_REQUEST) { + tcpm_ams_finish(port); ++ port->vdo_data[0] = 0; + port->vdm_state = VDM_STATE_DONE; + tcpm_set_state(port, SRC_SEND_CAPABILITIES, 0); + /* +@@ -2869,6 +3048,7 @@ static void vdm_run_state_machine(struct tcpm_port *port) + tcpm_ams_finish(port); + } else { + tcpm_ams_finish(port); ++ port->vdo_data[0] = 0; + if (port->tx_sop_type == TCPC_TX_SOP) + break; + /* Handle SOP' Transmission Errors */ +@@ -2878,11 +3058,11 @@ static void vdm_run_state_machine(struct tcpm_port *port) + * discovery process on SOP only. + */ + case CMD_DISCOVER_IDENT: +- port->vdo_data[0] = 0; + response[0] = VDO(USB_SID_PD, 1, + typec_get_negotiated_svdm_version( + port->typec_port), + CMD_DISCOVER_SVID); ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_CABLE_IDENT); + tcpm_queue_vdm(port, response[0], &response[1], + 0, TCPC_TX_SOP); + break; +@@ -2892,9 +3072,11 @@ static void vdm_run_state_machine(struct tcpm_port *port) + */ + case CMD_DISCOVER_SVID: + tcpm_register_partner_altmodes(port); ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); + break; + case CMD_DISCOVER_MODES: + tcpm_register_partner_altmodes(port); ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); + break; + default: + break; +@@ -3807,7 +3989,8 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, + PD_MSG_CTRL_NOT_SUPP, + NONE_AMS); + } else { +- if (port->send_discover && port->negotiated_rev < PD_REV30) { ++ if (port->vdm_discovery_state == VDM_DISCOVERY_UNKNOWN && ++ port->negotiated_rev < PD_REV30) { + tcpm_queue_message(port, PD_MSG_CTRL_WAIT); + break; + } +@@ -3823,7 +4006,8 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, + PD_MSG_CTRL_NOT_SUPP, + NONE_AMS); + } else { +- if (port->send_discover && port->negotiated_rev < PD_REV30) { ++ if (port->vdm_discovery_state == VDM_DISCOVERY_UNKNOWN && ++ port->negotiated_rev < PD_REV30) { + tcpm_queue_message(port, PD_MSG_CTRL_WAIT); + break; + } +@@ -3832,7 +4016,8 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, + } + break; + case PD_CTRL_VCONN_SWAP: +- if (port->send_discover && port->negotiated_rev < PD_REV30) { ++ if (port->vdm_discovery_state == VDM_DISCOVERY_UNKNOWN && ++ port->negotiated_rev < PD_REV30) { + tcpm_queue_message(port, PD_MSG_CTRL_WAIT); + break; + } +@@ -3891,6 +4076,7 @@ static void tcpm_pd_ext_msg_request(struct tcpm_port *port, + { + enum pd_ext_msg_type type = pd_header_type_le(msg->header); + unsigned int data_size = pd_ext_header_data_size_le(msg->ext_msg.header); ++ const struct pd_chunked_ext_message_data *ext_msg = &msg->ext_msg; + + /* stopping VDM state machine if interrupted by other Messages */ + if (tcpm_vdm_ams(port)) { +@@ -3899,7 +4085,7 @@ static void tcpm_pd_ext_msg_request(struct tcpm_port *port, + mod_vdm_delayed_work(port, 0); + } + +- if (!(le16_to_cpu(msg->ext_msg.header) & PD_EXT_HDR_CHUNKED)) { ++ if (!(le16_to_cpu(ext_msg->header) & PD_EXT_HDR_CHUNKED)) { + tcpm_pd_handle_msg(port, PD_MSG_CTRL_NOT_SUPP, NONE_AMS); + tcpm_log(port, "Unchunked extended messages unsupported"); + return; +@@ -3924,9 +4110,25 @@ static void tcpm_pd_ext_msg_request(struct tcpm_port *port, + NONE_AMS, 0); + } + break; +- case PD_EXT_SOURCE_CAP_EXT: +- case PD_EXT_GET_BATT_CAP: + case PD_EXT_GET_BATT_STATUS: ++ if (data_size >= 1) { ++ port->batt_request_id = ext_msg->data[0]; ++ tcpm_pd_handle_msg(port, PD_MSG_DATA_BATT_STATUS, ++ GETTING_BATTERY_STATUS); ++ } else { ++ tcpm_set_state(port, SOFT_RESET_SEND, 0); ++ } ++ break; ++ case PD_EXT_GET_BATT_CAP: ++ if (data_size >= 1) { ++ port->batt_request_id = ext_msg->data[0]; ++ tcpm_pd_handle_msg(port, PD_MSG_EXT_BATT_CAP, ++ GETTING_BATTERY_CAPABILITIES); ++ } else { ++ tcpm_set_state(port, SOFT_RESET_SEND, 0); ++ } ++ break; ++ case PD_EXT_SOURCE_CAP_EXT: + case PD_EXT_BATT_CAP: + case PD_EXT_GET_MANUFACTURER_INFO: + case PD_EXT_MANUFACTURER_INFO: +@@ -4137,6 +4339,22 @@ static bool tcpm_send_queued_message(struct tcpm_port *port) + ret); + tcpm_ams_finish(port); + break; ++ case PD_MSG_DATA_BATT_STATUS: ++ ret = tcpm_pd_send_batt_status(port); ++ if (ret) ++ tcpm_log(port, ++ "Failed to send battery status ret=%d", ++ ret); ++ tcpm_ams_finish(port); ++ break; ++ case PD_MSG_EXT_BATT_CAP: ++ ret = tcpm_pd_send_batt_cap(port); ++ if (ret) ++ tcpm_log(port, ++ "Failed to send battery cap ret=%d", ++ ret); ++ tcpm_ams_finish(port); ++ break; + default: + break; + } +@@ -4853,8 +5071,7 @@ static int tcpm_src_attach(struct tcpm_port *port) + port->partner = NULL; + + port->attached = true; +- port->send_discover = true; +- port->send_discover_prime = false; ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_UNKNOWN); + + return 0; + +@@ -4896,11 +5113,11 @@ static void tcpm_unregister_altmodes(struct tcpm_port *port) + struct pd_mode_data *modep_prime = &port->mode_data_prime; + int i; + +- for (i = 0; i < modep->altmodes; i++) { ++ for (i = 0; i < modep->altmodes && i < ALTMODE_DISCOVERY_MAX; i++) { + typec_unregister_altmode(port->partner_altmode[i]); + port->partner_altmode[i] = NULL; + } +- for (i = 0; i < modep_prime->altmodes; i++) { ++ for (i = 0; i < modep_prime->altmodes && i < ALTMODE_DISCOVERY_MAX; i++) { + typec_unregister_altmode(port->plug_prime_altmode[i]); + port->plug_prime_altmode[i] = NULL; + } +@@ -4931,6 +5148,8 @@ static void tcpm_reset_port(struct tcpm_port *port) + port->in_ams = false; + port->ams = NONE_AMS; + port->vdm_sm_running = false; ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_UNKNOWN); ++ mod_vdm_discovery_cancel_delayed_work(port); + tcpm_unregister_altmodes(port); + tcpm_typec_disconnect(port); + port->attached = false; +@@ -5014,8 +5233,7 @@ static int tcpm_snk_attach(struct tcpm_port *port) + port->partner = NULL; + + port->attached = true; +- port->send_discover = true; +- port->send_discover_prime = false; ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_UNKNOWN); + + return 0; + } +@@ -5422,16 +5640,11 @@ static void run_state_machine(struct tcpm_port *port) + * as well. + */ + if (port->explicit_contract) { +- if (port->send_discover_prime) { +- port->tx_sop_type = TCPC_TX_SOP_PRIME; +- } else { +- port->tx_sop_type = TCPC_TX_SOP; ++ if (port->vdm_discovery_state == VDM_DISCOVERY_UNKNOWN) + tcpm_set_initial_svdm_version(port); +- } +- mod_send_discover_delayed_work(port, 0); ++ mod_vdm_discovery_delayed_work(port, 0); + } else { +- port->send_discover = false; +- port->send_discover_prime = false; ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); + } + + /* +@@ -5814,16 +6027,11 @@ static void run_state_machine(struct tcpm_port *port) + * as well. + */ + if (port->explicit_contract) { +- if (port->send_discover_prime) { +- port->tx_sop_type = TCPC_TX_SOP_PRIME; +- } else { +- port->tx_sop_type = TCPC_TX_SOP; ++ if (port->vdm_discovery_state == VDM_DISCOVERY_UNKNOWN) + tcpm_set_initial_svdm_version(port); +- } +- mod_send_discover_delayed_work(port, 0); ++ mod_vdm_discovery_delayed_work(port, 0); + } else { +- port->send_discover = false; +- port->send_discover_prime = false; ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); + } + + power_supply_changed(port->psy); +@@ -5869,8 +6077,8 @@ static void run_state_machine(struct tcpm_port *port) + port->tcpc->set_pd_rx(port->tcpc, false); + tcpm_unregister_altmodes(port); + port->nr_sink_caps = 0; +- port->send_discover = true; +- port->send_discover_prime = false; ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_UNKNOWN); ++ mod_vdm_discovery_cancel_delayed_work(port); + if (port->pwr_role == TYPEC_SOURCE) + tcpm_set_state(port, SRC_HARD_RESET_VBUS_OFF, + PD_T_PS_HARD_RESET); +@@ -6017,25 +6225,15 @@ static void run_state_machine(struct tcpm_port *port) + /* DR_Swap states */ + case DR_SWAP_SEND: + tcpm_pd_send_control(port, PD_CTRL_DR_SWAP, TCPC_TX_SOP); +- if (port->data_role == TYPEC_DEVICE || port->negotiated_rev > PD_REV20) { +- port->send_discover = true; +- port->send_discover_prime = false; +- } + tcpm_set_state_cond(port, DR_SWAP_SEND_TIMEOUT, + PD_T_SENDER_RESPONSE); + break; + case DR_SWAP_ACCEPT: + tcpm_pd_send_control(port, PD_CTRL_ACCEPT, TCPC_TX_SOP); +- if (port->data_role == TYPEC_DEVICE || port->negotiated_rev > PD_REV20) { +- port->send_discover = true; +- port->send_discover_prime = false; +- } + tcpm_set_state_cond(port, DR_SWAP_CHANGE_DR, 0); + break; + case DR_SWAP_SEND_TIMEOUT: + tcpm_swap_complete(port, -ETIMEDOUT); +- port->send_discover = false; +- port->send_discover_prime = false; + tcpm_ams_finish(port); + tcpm_set_state(port, ready_state(port), 0); + break; +@@ -6047,6 +6245,8 @@ static void run_state_machine(struct tcpm_port *port) + else + tcpm_set_roles(port, true, TYPEC_STATE_USB, port->pwr_role, + TYPEC_HOST); ++ if (port->data_role == TYPEC_HOST || port->negotiated_rev > PD_REV20) ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_UNKNOWN); + tcpm_ams_finish(port); + tcpm_set_state(port, ready_state(port), 0); + break; +@@ -6330,9 +6530,7 @@ static void run_state_machine(struct tcpm_port *port) + + /* Cable states */ + case SRC_VDM_IDENTITY_REQUEST: +- port->send_discover_prime = true; +- port->tx_sop_type = TCPC_TX_SOP_PRIME; +- mod_send_discover_delayed_work(port, 0); ++ mod_vdm_discovery_delayed_work(port, 0); + port->upcoming_state = SRC_SEND_CAPABILITIES; + break; + +@@ -7052,8 +7250,8 @@ static void tcpm_enable_frs_work(struct kthread_work *work) + goto unlock; + + /* Send when the state machine is idle */ +- if (port->state != SNK_READY || port->vdm_sm_running || port->send_discover || +- port->send_discover_prime) ++ if (port->state != SNK_READY || port->vdm_sm_running || ++ port->vdm_discovery_state == VDM_DISCOVERY_UNKNOWN) + goto resched; + + port->upcoming_state = GET_SINK_CAP; +@@ -7070,29 +7268,160 @@ static void tcpm_enable_frs_work(struct kthread_work *work) + mutex_unlock(&port->lock); + } + +-static void tcpm_send_discover_work(struct kthread_work *work) ++static void tcpm_vdm_discovery_work(struct kthread_work *work) + { +- struct tcpm_port *port = container_of(work, struct tcpm_port, send_discover_work); ++ struct tcpm_port *port = container_of(work, struct tcpm_port, vdm_discovery_work); ++ enum tcpm_transmit_type tx_sop_type = TCPC_TX_SOP; ++ struct typec_port *typec = port->typec_port; ++ struct pd_mode_data *modep, *modep_prime; ++ u32 msg[2] = { }; ++ int svdm_version; + + mutex_lock(&port->lock); +- /* No need to send DISCOVER_IDENTITY anymore */ +- if (!port->send_discover && !port->send_discover_prime) +- goto unlock; + +- if (port->data_role == TYPEC_DEVICE && port->negotiated_rev < PD_REV30) { +- port->send_discover = false; +- port->send_discover_prime = false; ++ tcpm_log_force(port, "%s state [%s]", __func__, ++ vdm_discovery_state_strings[port->vdm_discovery_state]); ++ ++ /* No need to perform work if Discovery process is complete */ ++ if (port->vdm_discovery_state == VDM_DISCOVERY_COMPLETE) + goto unlock; +- } + + /* Retry if the port is not idle */ +- if ((port->state != SRC_READY && port->state != SNK_READY && +- port->state != SRC_VDM_IDENTITY_REQUEST) || port->vdm_sm_running) { +- mod_send_discover_delayed_work(port, SEND_DISCOVER_RETRY_MS); ++ if (!tcpm_can_send_vdm(port->state) || port->vdm_sm_running) { ++ mod_vdm_discovery_delayed_work(port, SEND_DISCOVERY_VDM_RETRY_MS); + goto unlock; + } + +- tcpm_send_vdm(port, USB_SID_PD, CMD_DISCOVER_IDENT, NULL, 0, port->tx_sop_type); ++ modep = &port->mode_data; ++ modep_prime = &port->mode_data_prime; ++ ++ svdm_version = typec_get_negotiated_svdm_version(typec); ++ ++ switch (port->vdm_discovery_state) { ++ /* ++ * The port has not received a Discover Identity response from the port partner. ++ * ++ * 1. The port will send Discover Identity to the partner over SOP in the SRC_READY and ++ * SNK_READY states if there is an explicit contract ++ * 2. The port will send Discover Identity to the cable over SOP' in the ++ * SRC_VDM_IDENTITY_REQUEST state if capable of doing so. ++ */ ++ case VDM_DISCOVERY_UNKNOWN: ++ /* Can't send Discover Identity, VDM discovery is complete */ ++ if (port->data_role == TYPEC_DEVICE && port->negotiated_rev < PD_REV30) { ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); ++ goto unlock; ++ } ++ ++ if (port->state == SRC_VDM_IDENTITY_REQUEST) { ++ tx_sop_type = TCPC_TX_SOP_PRIME; ++ svdm_version = SVDM_VER_MAX; ++ } ++ ++ msg[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_IDENT); ++ break; ++ /* ++ * The port has received a Discover Identity ACK from the port partner. ++ * ++ * 1. The port will send Discover Identity to the cable over SOP' in the SRC_READY and ++ * SNK_READY states if it did not previously discover the cable but is capable of doing ++ * so. ++ * 2. The port will send Discover SVIDs to the partner over SOP in the SRC_READY and ++ * SNK_READY states otherwise. ++ */ ++ case VDM_DISCOVERY_PARTNER_IDENT: ++ if (tcpm_can_communicate_sop_prime(port) && !port->cable) { ++ tx_sop_type = TCPC_TX_SOP_PRIME; ++ msg[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_IDENT); ++ } else { ++ if (tcpm_can_communicate_sop_prime(port)) ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_CABLE_IDENT); ++ msg[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_SVID); ++ } ++ break; ++ /* ++ * The port has received a Discover Identity ACK from the cable. ++ * ++ * 1. The port will send Discover SVIDs to the partner over SOP. ++ */ ++ case VDM_DISCOVERY_CABLE_IDENT: ++ msg[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_SVID); ++ break; ++ /* ++ * The port has received a Discover SVIDs ACK from the partner or the last SVIDs supported ++ * by the partner. ++ * ++ * 1. The port will send Discover Modes for the first SVID over SOP if the partner supports ++ * modal operation and valid SVIDs were registered. ++ * 2. The vdm_discovery_state will move to VDM_DISCOVERY_COMPLETE otherwise. ++ */ ++ case VDM_DISCOVERY_PARTNER_SVIDS: ++ if (modep->nsvids && supports_modal(port)) { ++ msg[0] = VDO(modep->svids[0], 1, svdm_version, CMD_DISCOVER_MODES); ++ } else { ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); ++ goto unlock; ++ } ++ break; ++ /* ++ * The port has received a Discover Modes ACK from the partner for any mode. ++ * ++ * 1. The port will send Discover Modes for the next SVID that has not been discovered to ++ * the port partner over SOP. ++ * 2. The port will send Discover SVIDs over SOP' if the port can communicate over SOP' ++ * and the cable supports VDMs. ++ * 3. The vdm_discovery_state will move to VDM_DISCOVERY_COMPLETE otherwise. ++ */ ++ case VDM_DISCOVERY_PARTNER_MODES: ++ /* Not all modes have been discovered yet */ ++ if (modep->svid_index < modep->nsvids) { ++ msg[0] = VDO(modep_prime->svids[modep->svid_index], 1, svdm_version, ++ CMD_DISCOVER_MODES); ++ } else if (tcpm_can_communicate_sop_prime(port) && tcpm_cable_vdm_supported(port)) { ++ tx_sop_type = TCPC_TX_SOP_PRIME; ++ svdm_version = typec_get_cable_svdm_version(typec); ++ msg[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_SVID); ++ } else { ++ tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); ++ goto unlock; ++ } ++ break; ++ /* ++ * The port has received a Discover SVIDs ACK from the cable over SOP'. ++ * ++ * 1. The port will send Discover Modes for the first SVID over SOP'. ++ */ ++ case VDM_DISCOVERY_CABLE_SVIDS: ++ if (modep_prime->nsvids) { ++ tx_sop_type = TCPC_TX_SOP_PRIME; ++ svdm_version = typec_get_cable_svdm_version(typec); ++ msg[0] = VDO(modep_prime->svids[0], 1, svdm_version, CMD_DISCOVER_MODES); ++ } else { ++ goto unlock; ++ } ++ break; ++ /* ++ * The port has received a Discover Modes ACK from the cable for any mode. ++ * ++ * 1. The port will send Discover Modes for the next SVID that has not been discovered to ++ * the cable over SOP'. ++ */ ++ case VDM_DISCOVERY_CABLE_MODES: ++ if (modep_prime->svid_index < modep_prime->nsvids) { ++ tx_sop_type = TCPC_TX_SOP_PRIME; ++ svdm_version = typec_get_cable_svdm_version(typec); ++ msg[0] = VDO(modep_prime->svids[modep->svid_index], 1, svdm_version, ++ CMD_DISCOVER_MODES); ++ } else { ++ goto unlock; ++ } ++ break; ++ default: ++ goto unlock; ++ } ++ ++ if (svdm_version >= 0) ++ tcpm_queue_vdm(port, msg[0], &msg[1], 0, tx_sop_type); + + unlock: + mutex_unlock(&port->lock); +@@ -8508,12 +8837,12 @@ static enum hrtimer_restart enable_frs_timer_handler(struct hrtimer *timer) + return HRTIMER_NORESTART; + } + +-static enum hrtimer_restart send_discover_timer_handler(struct hrtimer *timer) ++static enum hrtimer_restart vdm_discovery_timer_handler(struct hrtimer *timer) + { +- struct tcpm_port *port = container_of(timer, struct tcpm_port, send_discover_timer); ++ struct tcpm_port *port = container_of(timer, struct tcpm_port, vdm_discovery_timer); + + if (port->registered) +- kthread_queue_work(port->wq, &port->send_discover_work); ++ kthread_queue_work(port->wq, &port->vdm_discovery_work); + return HRTIMER_NORESTART; + } + +@@ -8547,14 +8876,14 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) + kthread_init_work(&port->vdm_state_machine, vdm_state_machine_work); + kthread_init_work(&port->event_work, tcpm_pd_event_handler); + kthread_init_work(&port->enable_frs, tcpm_enable_frs_work); +- kthread_init_work(&port->send_discover_work, tcpm_send_discover_work); ++ kthread_init_work(&port->vdm_discovery_work, tcpm_vdm_discovery_work); + hrtimer_setup(&port->state_machine_timer, state_machine_timer_handler, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + hrtimer_setup(&port->vdm_state_machine_timer, vdm_state_machine_timer_handler, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hrtimer_setup(&port->enable_frs_timer, enable_frs_timer_handler, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); +- hrtimer_setup(&port->send_discover_timer, send_discover_timer_handler, CLOCK_MONOTONIC, ++ hrtimer_setup(&port->vdm_discovery_timer, vdm_discovery_timer_handler, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + + spin_lock_init(&port->pd_event_lock); +@@ -8649,11 +8978,12 @@ void tcpm_unregister_port(struct tcpm_port *port) + port->registered = false; + kthread_destroy_worker(port->wq); + +- hrtimer_cancel(&port->send_discover_timer); ++ hrtimer_cancel(&port->vdm_discovery_timer); + hrtimer_cancel(&port->enable_frs_timer); + hrtimer_cancel(&port->vdm_state_machine_timer); + hrtimer_cancel(&port->state_machine_timer); + ++ power_supply_put_system_batteries(port->fixed_batt, port->fixed_batt_cnt); + tcpm_reset_port(port); + + tcpm_port_unregister_pd(port); +diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c +--- a/drivers/usb/typec/tipd/core.c ++++ b/drivers/usb/typec/tipd/core.c +@@ -129,6 +129,7 @@ enum { + TPS_MODE_BIST, + TPS_MODE_DISC, + TPS_MODE_PTCH, ++ TPS_MODE_APP1, + }; + + static const char *const modes[] = { +@@ -137,6 +138,7 @@ static const char *const modes[] = { + [TPS_MODE_BIST] = "BIST", + [TPS_MODE_DISC] = "DISC", + [TPS_MODE_PTCH] = "PTCH", ++ [TPS_MODE_APP1] = "APP1", + }; + + /* Unrecognized commands will be replaced with "!CMD" */ +@@ -158,6 +160,7 @@ struct tipd_data { + int (*init)(struct tps6598x *tps); + int (*switch_power_state)(struct tps6598x *tps, u8 target_state); + bool (*read_data_status)(struct tps6598x *tps); ++ bool (*read_power_status)(struct tps6598x *tps); + int (*reset)(struct tps6598x *tps); + int (*connect)(struct tps6598x *tps, u32 status); + }; +@@ -629,6 +632,34 @@ static bool tps6598x_read_power_status(struct tps6598x *tps) + return true; + } + ++/* ++ * TPS66993 deprecated Power_Status register (0x3F). BC1.2 is not supported ++ * and the remaining bits are redundant with STATUS register (0x1A). ++ * Synthesize pwr_status from the already-read STATUS register. ++ */ ++static bool tps66993_read_power_status(struct tps6598x *tps) ++{ ++ u16 pwr_status = 0; ++ ++ /* Same masks as TPS_POWER_STATUS_CONNECTION() / SOURCESINK() / PWROPMODE() in tps6598x.h */ ++ if (tps->status & TPS_STATUS_PLUG_PRESENT) ++ pwr_status |= FIELD_PREP(TPS_POWER_STATUS_CONNECTION_MASK, 1); ++ ++ /* SOURCESINK: 1=sink; STATUS.PortRole 1=source, opposite convention */ ++ if (!TPS_STATUS_TO_TYPEC_PORTROLE(tps->status)) ++ pwr_status |= FIELD_PREP(TPS_POWER_STATUS_SOURCESINK_MASK, 1); ++ ++ if (TPS_STATUS_VBUS_STATUS(tps->status) == TPS_STATUS_VBUS_STATUS_PD) ++ pwr_status |= FIELD_PREP(TPS_POWER_STATUS_TYPEC_CURRENT_MASK, ++ TPS_POWER_STATUS_TYPEC_CURRENT_PD); ++ ++ tps->pwr_status = pwr_status; ++ ++ tps->data->trace_power_status(pwr_status); ++ ++ return true; ++} ++ + static void tps6598x_handle_plug_event(struct tps6598x *tps, u32 status) + { + int ret; +@@ -906,7 +937,7 @@ static irqreturn_t cd321x_interrupt(int irq, void *data) + goto err_unlock; + + if (event & APPLE_CD_REG_INT_POWER_STATUS_UPDATE) { +- if (!tps6598x_read_power_status(tps)) ++ if (!tps->data->read_power_status(tps)) + goto err_unlock; + if (TPS_POWER_STATUS_PWROPMODE(tps->pwr_status) == TYPEC_PWR_MODE_PD) { + if (tps6598x_read_partner_identity(tps)) { +@@ -961,7 +992,7 @@ static irqreturn_t tps25750_interrupt(int irq, void *data) + goto err_clear_ints; + + if (event[0] & TPS_REG_INT_POWER_STATUS_UPDATE) +- if (!tps6598x_read_power_status(tps)) ++ if (!tps->data->read_power_status(tps)) + goto err_clear_ints; + + if (event[0] & TPS_REG_INT_DATA_STATUS_UPDATE) +@@ -1034,17 +1065,25 @@ static irqreturn_t tps6598x_interrupt(int irq, void *data) + if (!tps6598x_read_status(tps, &status)) + goto err_unlock; + ++ tps->status = status; ++ + if ((event1[0] | event2[0]) & TPS_REG_INT_POWER_STATUS_UPDATE) +- if (!tps6598x_read_power_status(tps)) ++ if (!tps->data->read_power_status(tps)) + goto err_unlock; + + if ((event1[0] | event2[0]) & TPS_REG_INT_DATA_STATUS_UPDATE) + if (!tps->data->read_data_status(tps)) + goto err_unlock; + +- /* Handle plug insert or removal */ +- if ((event1[0] | event2[0]) & TPS_REG_INT_PLUG_EVENT) ++ /* ++ * Refresh power status before connect - needed for TPS66993 which ++ * synthesizes pwr_status from STATUS and never gets POWER_STATUS_UPDATE. ++ */ ++ if ((event1[0] | event2[0]) & TPS_REG_INT_PLUG_EVENT) { ++ if (!tps->data->read_power_status(tps)) ++ goto err_unlock; + tps6598x_handle_plug_event(tps, status); ++ } + + err_unlock: + mutex_unlock(&tps->lock); +@@ -1080,6 +1119,7 @@ static int tps6598x_check_mode(struct tps6598x *tps) + + switch (ret) { + case TPS_MODE_APP: ++ case TPS_MODE_APP1: + case TPS_MODE_PTCH: + return ret; + case TPS_MODE_BOOT: +@@ -1753,7 +1793,7 @@ static int tps6598x_probe(struct i2c_client *client) + struct tps6598x *tps; + struct fwnode_handle *fwnode; + u32 status; +- u32 vid; ++ u32 vid = 0; + int ret; + + data = i2c_get_match_data(client); +@@ -1781,8 +1821,11 @@ static int tps6598x_probe(struct i2c_client *client) + + if (!device_is_compatible(tps->dev, "ti,tps25750")) { + ret = tps6598x_read32(tps, TPS_REG_VID, &vid); +- if (ret < 0 || !vid) ++ if (ret < 0 || !vid) { ++ dev_err(tps->dev, "failed to read vendor ID: %d, vid: %#x\n", ++ ret, vid); + return -ENODEV; ++ } + } + + /* +@@ -1818,6 +1861,8 @@ static int tps6598x_probe(struct i2c_client *client) + goto err_clear_mask; + } + ++ tps->status = status; ++ + /* + * This fwnode has a "compatible" property, but is never populated as a + * struct device. Instead we simply parse it to read the properties. +@@ -1845,7 +1890,7 @@ static int tps6598x_probe(struct i2c_client *client) + + if (status & TPS_STATUS_PLUG_PRESENT) { + ret = -EINVAL; +- if (!tps6598x_read_power_status(tps)) ++ if (!tps->data->read_power_status(tps)) + goto err_unregister_port; + if (!tps->data->read_data_status(tps)) + goto err_unregister_port; +@@ -1860,7 +1905,7 @@ static int tps6598x_probe(struct i2c_client *client) + IRQF_SHARED | IRQF_ONESHOT, + dev_name(&client->dev), tps); + } else { +- dev_warn(tps->dev, "Unable to find the interrupt, switching to polling\n"); ++ dev_dbg(tps->dev, "no IRQ specified, using polling mode\n"); + INIT_DELAYED_WORK(&tps->wq_poll, tps6598x_poll_work); + queue_delayed_work(system_power_efficient_wq, &tps->wq_poll, + msecs_to_jiffies(POLL_INTERVAL)); +@@ -1987,6 +2032,7 @@ static const struct tipd_data cd321x_data = { + .trace_status = trace_tps6598x_status, + .init = cd321x_init, + .read_data_status = cd321x_read_data_status, ++ .read_power_status = tps6598x_read_power_status, + .reset = cd321x_reset, + .switch_power_state = cd321x_switch_power_state, + .connect = cd321x_connect, +@@ -2006,6 +2052,25 @@ static const struct tipd_data tps6598x_data = { + .apply_patch = tps6598x_apply_patch, + .init = tps6598x_init, + .read_data_status = tps6598x_read_data_status, ++ .read_power_status = tps6598x_read_power_status, ++ .reset = tps6598x_reset, ++ .connect = tps6598x_connect, ++}; ++ ++static const struct tipd_data tps66993_data = { ++ .irq_handler = tps6598x_interrupt, ++ .irq_mask1 = TPS_REG_INT_DATA_STATUS_UPDATE | ++ TPS_REG_INT_PLUG_EVENT, ++ .tps_struct_size = sizeof(struct tps6598x), ++ .register_port = tps6598x_register_port, ++ .unregister_port = tps6598x_unregister_port, ++ .trace_data_status = trace_tps6598x_data_status, ++ .trace_power_status = trace_tps6598x_power_status, ++ .trace_status = trace_tps6598x_status, ++ .apply_patch = tps6598x_apply_patch, ++ .init = tps6598x_init, ++ .read_data_status = tps6598x_read_data_status, ++ .read_power_status = tps66993_read_power_status, + .reset = tps6598x_reset, + .connect = tps6598x_connect, + }; +@@ -2024,12 +2089,14 @@ static const struct tipd_data tps25750_data = { + .apply_patch = tps25750_apply_patch, + .init = tps25750_init, + .read_data_status = tps6598x_read_data_status, ++ .read_power_status = tps6598x_read_power_status, + .reset = tps25750_reset, + .connect = tps6598x_connect, + }; + + static const struct of_device_id tps6598x_of_match[] = { + { .compatible = "ti,tps6598x", &tps6598x_data}, ++ { .compatible = "ti,tps66993", &tps66993_data}, + { .compatible = "apple,cd321x", &cd321x_data}, + { .compatible = "ti,tps25750", &tps25750_data}, + {} +diff --git a/drivers/usb/typec/tipd/tps6598x.h b/drivers/usb/typec/tipd/tps6598x.h +--- a/drivers/usb/typec/tipd/tps6598x.h ++++ b/drivers/usb/typec/tipd/tps6598x.h +@@ -142,9 +142,13 @@ + #define TPS_SYSTEM_POWER_STATE_S4 0x04 + #define TPS_SYSTEM_POWER_STATE_S5 0x05 + +-/* TPS_REG_POWER_STATUS bits */ +-#define TPS_POWER_STATUS_CONNECTION(x) TPS_FIELD_GET(BIT(0), (x)) +-#define TPS_POWER_STATUS_SOURCESINK(x) TPS_FIELD_GET(BIT(1), (x)) ++/* TPS_REG_POWER_STATUS bits (masks shared by TPS_FIELD_GET accessors and FIELD_PREP) */ ++#define TPS_POWER_STATUS_CONNECTION_MASK BIT(0) ++#define TPS_POWER_STATUS_SOURCESINK_MASK BIT(1) ++#define TPS_POWER_STATUS_CONNECTION(x) \ ++ TPS_FIELD_GET(TPS_POWER_STATUS_CONNECTION_MASK, (x)) ++#define TPS_POWER_STATUS_SOURCESINK(x) \ ++ TPS_FIELD_GET(TPS_POWER_STATUS_SOURCESINK_MASK, (x)) + #define TPS_POWER_STATUS_BC12_DET(x) TPS_FIELD_GET(BIT(2), (x)) + + #define TPS_POWER_STATUS_TYPEC_CURRENT_MASK GENMASK(3, 2) +diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c +--- a/drivers/usb/typec/ucsi/debugfs.c ++++ b/drivers/usb/typec/ucsi/debugfs.c +@@ -82,8 +82,8 @@ static int ucsi_resp_show(struct seq_file *s, void *not_used) + if (ucsi->debugfs->status) + return ucsi->debugfs->status; + +- seq_printf(s, "0x%016llx%016llx\n", ucsi->debugfs->response.high, +- ucsi->debugfs->response.low); ++ seq_printf(s, "0x%016llx%016llx%016llx\n", ucsi->debugfs->response.ext, ++ ucsi->debugfs->response.high, ucsi->debugfs->response.low); + return 0; + } + DEFINE_SHOW_ATTRIBUTE(ucsi_resp); +diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c +--- a/drivers/usb/typec/ucsi/ucsi.c ++++ b/drivers/usb/typec/ucsi/ucsi.c +@@ -1102,8 +1102,8 @@ static int ucsi_register_plug(struct ucsi_connector *con) + plug = typec_register_plug(con->cable, &desc); + if (IS_ERR(plug)) { + dev_err(con->ucsi->dev, +- "con%d: failed to register plug (%ld)\n", con->num, +- PTR_ERR(plug)); ++ "con%d: failed to register plug (%pe)\n", con->num, ++ plug); + return PTR_ERR(plug); + } + +@@ -1161,8 +1161,8 @@ static int ucsi_register_cable(struct ucsi_connector *con) + cable = typec_register_cable(con->port, &desc); + if (IS_ERR(cable)) { + dev_err(con->ucsi->dev, +- "con%d: failed to register cable (%ld)\n", con->num, +- PTR_ERR(cable)); ++ "con%d: failed to register cable (%pe)\n", con->num, ++ cable); + return PTR_ERR(cable); + } + +@@ -1291,8 +1291,8 @@ static int ucsi_register_partner(struct ucsi_connector *con) + partner = typec_register_partner(con->port, &desc); + if (IS_ERR(partner)) { + dev_err(con->ucsi->dev, +- "con%d: failed to register partner (%ld)\n", con->num, +- PTR_ERR(partner)); ++ "con%d: failed to register partner (%pe)\n", con->num, ++ partner); + return PTR_ERR(partner); + } + +@@ -2221,7 +2221,7 @@ static void ucsi_init_work(struct work_struct *work) + return; + } + +- queue_delayed_work(system_long_wq, &ucsi->work, ++ queue_delayed_work(system_dfl_long_wq, &ucsi->work, + UCSI_ROLE_SWITCH_INTERVAL); + } + } +@@ -2345,7 +2345,7 @@ int ucsi_register(struct ucsi *ucsi) + UCSI_BCD_GET_MINOR(ucsi->version), + UCSI_BCD_GET_SUBMINOR(ucsi->version)); + +- queue_delayed_work(system_long_wq, &ucsi->work, 0); ++ queue_delayed_work(system_dfl_long_wq, &ucsi->work, 0); + + ucsi_debugfs_register(ucsi); + return 0; +diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h +--- a/drivers/usb/typec/ucsi/ucsi.h ++++ b/drivers/usb/typec/ucsi/ucsi.h +@@ -466,6 +466,7 @@ struct ucsi_debugfs_entry { + struct ucsi_data { + u64 low; + u64 high; ++ u64 ext; + } response; + int status; + u8 message_out[MESSAGE_OUT_MAX_LEN]; +diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h +--- a/include/linux/power_supply.h ++++ b/include/linux/power_supply.h +@@ -806,11 +806,26 @@ extern int power_supply_reg_notifier(struct notifier_block *nb); + extern void power_supply_unreg_notifier(struct notifier_block *nb); + #if IS_ENABLED(CONFIG_POWER_SUPPLY) + extern struct power_supply *power_supply_get_by_name(const char *name); ++extern int __must_check power_supply_get_system_batteries(struct device *dev, ++ struct power_supply ***psys); ++extern void power_supply_put_system_batteries(struct power_supply **psys, int count); + extern void power_supply_put(struct power_supply *psy); + #else + static inline void power_supply_put(struct power_supply *psy) {} + static inline struct power_supply *power_supply_get_by_name(const char *name) + { return NULL; } ++static inline int __must_check power_supply_get_system_batteries(struct device *dev, ++ struct power_supply ***psys) ++{ ++ if (psys) ++ *psys = NULL; ++ return 0; ++} ++ ++static inline void power_supply_put_system_batteries(struct power_supply **psys, ++ int count) ++{ ++} + #endif + extern struct power_supply *power_supply_get_by_reference(struct fwnode_handle *fwnode, + const char *property); +diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h +--- a/include/linux/usb/pd.h ++++ b/include/linux/usb/pd.h +@@ -106,6 +106,9 @@ enum pd_ext_msg_type { + #define PD_HEADER_LE(type, pwr, data, rev, id, cnt) \ + cpu_to_le16(PD_HEADER((type), (pwr), (data), (rev), (id), (cnt), (0))) + ++#define PD_HEADER_EXT_LE(type, pwr, data, rev, id, cnt) \ ++ cpu_to_le16(PD_HEADER((type), (pwr), (data), (rev), (id), (cnt), (1))) ++ + static inline unsigned int pd_header_cnt(u16 header) + { + return (header >> PD_HEADER_CNT_SHIFT) & PD_HEADER_CNT_MASK; +@@ -219,6 +222,25 @@ static inline u8 count_chunked_data_objs(u32 size) + return ((size / 4) + (size % 4 ? 1 : 0)); + } + ++/** ++ * batt_cap_ext_msg - Battery capability extended PD message ++ * @vid: Battery Vendor ID (assigned by USB-IF) ++ * @pid: Battery Product ID (assigned by battery or device vendor) ++ * @batt_design_cap: Battery design capacity in 0.1Wh ++ * @batt_last_chg_cap: Battery last full charge capacity in 0.1Wh ++ * @batt_type: Battery Type. bit0 when set indicates invalid battery reference. ++ * Rest of the bits are reserved. ++ */ ++struct batt_cap_ext_msg { ++ __le16 vid; ++ __le16 pid; ++ __le16 batt_design_cap; ++ __le16 batt_last_chg_cap; ++ u8 batt_type; ++} __packed; ++ ++#define BATT_CAP_BATT_TYPE_INVALID_REF BIT(0) ++ + /* Sink Caps Extended Data Block Version */ + #define SKEDB_VER_1_0 1 + +@@ -724,4 +746,33 @@ void usb_power_delivery_unlink_device(struct usb_power_delivery *pd, struct devi + + #endif /* CONFIG_TYPEC */ + ++/* Battery Status Data Object */ ++#define BSDO_PRESENT_CAPACITY GENMASK(31, 16) ++#define BSDO_CHG_STATUS GENMASK(11, 10) ++#define BSDO_BATTERY_PRESENT BIT(9) ++#define BSDO_INVALID_BATTERY_REFERENCE BIT(8) ++ ++/* ++ * Battery Charge Status: Battery Charging Status Values as defined in ++ * "USB PD Spec Rev3.1 Ver1.8", "Table 6-46 Battery Status Data Object (BSDO)". ++ */ ++#define BSDO_BATTERY_INFO_CHARGING 0x0 ++#define BSDO_BATTERY_INFO_DISCHARGING 0x1 ++#define BSDO_BATTERY_INFO_IDLE 0x2 ++#define BSDO_BATTERY_INFO_RSVD 0x3 ++ ++/** ++ * BSDO() - Pack data into Battery Status Data Object format. ++ * @batt_charge: Battery's present state of charge in 0.1WH increment. ++ * @chg_status: Battery charge status. ++ * @batt_present: Indicates that battery is present/attached when set else absent when unset. ++ * @invalid_ref: Indicates that an invalid battery reference was made in the Get_Battery_Status ++ * request. ++ */ ++#define BSDO(batt_charge, chg_status, batt_present, invalid_ref) \ ++ ((FIELD_PREP(BSDO_PRESENT_CAPACITY, batt_charge)) | \ ++ (FIELD_PREP(BSDO_CHG_STATUS, chg_status)) | \ ++ ((batt_present) ? BSDO_BATTERY_PRESENT : 0) | \ ++ ((invalid_ref) ? BSDO_INVALID_BATTERY_REFERENCE : 0)) ++ + #endif /* __LINUX_USB_PD_H */ +diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h +--- a/include/linux/usb/typec.h ++++ b/include/linux/usb/typec.h +@@ -337,6 +337,7 @@ void typec_unregister_cable(struct typec_cable *cable); + struct typec_cable *typec_cable_get(struct typec_port *port); + void typec_cable_put(struct typec_cable *cable); + int typec_cable_is_active(struct typec_cable *cable); ++bool typec_cable_altmode_unsupported(struct typec_altmode *alt); + + struct typec_plug *typec_register_plug(struct typec_cable *cable, + struct typec_plug_desc *desc); +diff --git a/include/uapi/linux/usb/functionfs.h b/include/uapi/linux/usb/functionfs.h +--- a/include/uapi/linux/usb/functionfs.h ++++ b/include/uapi/linux/usb/functionfs.h +@@ -25,6 +25,7 @@ enum functionfs_flags { + FUNCTIONFS_EVENTFD = 32, + FUNCTIONFS_ALL_CTRL_RECIP = 64, + FUNCTIONFS_CONFIG0_SETUP = 128, ++ FUNCTIONFS_RW_PROXY_EPS = 256, + }; + + /* Descriptor of an non-audio endpoint */ +@@ -414,4 +415,27 @@ struct usb_functionfs_event { + #define FUNCTIONFS_DMABUF_TRANSFER _IOW('g', 133, \ + struct usb_ffs_dmabuf_transfer_req) + ++/* ++ * Enable or disable automatic zero-length packet (ZLP) appending for the ++ * endpoint. The argument is a pointer to a __u32: 0 to disable, non-zero to ++ * enable. ++ * ++ * When enabled, the kernel will automatically append a ZLP at the end of ++ * a transfer if the payload length is an exact multiple of the endpoint's ++ * max packet size. ++ * ++ * This is useful for compatibility with legacy protocols which require ++ * automatic ZLP appending to data written from userspace. ++ * ++ * This ioctl can only be used on IN endpoints. It can be called at any time ++ * after the FunctionFS instance is active, even before the host has connected ++ * or enabled the endpoint. ++ * ++ * Returns zero on success, or a negative errno value on error: ++ * -ENODEV: The FunctionFS instance is not active. ++ * -EINVAL: The endpoint is not an IN endpoint. ++ * -EFAULT: Invalid user space pointer for the argument. ++ */ ++#define FUNCTIONFS_ENDPOINT_ENABLE_ZLP _IOW('g', 134, __u32) ++ + #endif /* _UAPI__LINUX_FUNCTIONFS_H__ */ +diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs +--- a/rust/kernel/usb.rs ++++ b/rust/kernel/usb.rs +@@ -24,11 +24,8 @@ + }; + use core::{ + marker::PhantomData, +- mem::{ +- offset_of, +- MaybeUninit, // +- }, +- ptr::NonNull, ++ mem::offset_of, ++ ptr::NonNull, // + }; + + /// An adapter for the registration of USB drivers. +@@ -130,8 +127,7 @@ pub const fn from_id(vendor: u16, product: u16) -> Self { + match_flags: bindings::USB_DEVICE_ID_MATCH_DEVICE as u16, + idVendor: vendor, + idProduct: product, +- // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`. +- ..unsafe { MaybeUninit::zeroed().assume_init() } ++ ..pin_init::zeroed() + }) + } + +@@ -143,8 +139,7 @@ pub const fn from_device_ver(vendor: u16, product: u16, bcd_lo: u16, bcd_hi: u16 + idProduct: product, + bcdDevice_lo: bcd_lo, + bcdDevice_hi: bcd_hi, +- // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`. +- ..unsafe { MaybeUninit::zeroed().assume_init() } ++ ..pin_init::zeroed() + }) + } + +@@ -155,8 +150,7 @@ pub const fn from_device_info(class: u8, subclass: u8, protocol: u8) -> Self { + bDeviceClass: class, + bDeviceSubClass: subclass, + bDeviceProtocol: protocol, +- // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`. +- ..unsafe { MaybeUninit::zeroed().assume_init() } ++ ..pin_init::zeroed() + }) + } + +@@ -167,8 +161,7 @@ pub const fn from_interface_info(class: u8, subclass: u8, protocol: u8) -> Self + bInterfaceClass: class, + bInterfaceSubClass: subclass, + bInterfaceProtocol: protocol, +- // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`. +- ..unsafe { MaybeUninit::zeroed().assume_init() } ++ ..pin_init::zeroed() + }) + } + +@@ -180,8 +173,7 @@ pub const fn from_device_interface_class(vendor: u16, product: u16, class: u8) - + idVendor: vendor, + idProduct: product, + bInterfaceClass: class, +- // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`. +- ..unsafe { MaybeUninit::zeroed().assume_init() } ++ ..pin_init::zeroed() + }) + } + +@@ -193,8 +185,7 @@ pub const fn from_device_interface_protocol(vendor: u16, product: u16, protocol: + idVendor: vendor, + idProduct: product, + bInterfaceProtocol: protocol, +- // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`. +- ..unsafe { MaybeUninit::zeroed().assume_init() } ++ ..pin_init::zeroed() + }) + } + +@@ -206,8 +197,7 @@ pub const fn from_device_interface_number(vendor: u16, product: u16, number: u8) + idVendor: vendor, + idProduct: product, + bInterfaceNumber: number, +- // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`. +- ..unsafe { MaybeUninit::zeroed().assume_init() } ++ ..pin_init::zeroed() + }) + } + +@@ -227,8 +217,7 @@ pub const fn from_device_and_interface_info( + bInterfaceClass: class, + bInterfaceSubClass: subclass, + bInterfaceProtocol: protocol, +- // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`. +- ..unsafe { MaybeUninit::zeroed().assume_init() } ++ ..pin_init::zeroed() + }) + } + } +@@ -393,6 +382,7 @@ fn as_ref(&self) -> &Device { + + // SAFETY: Instances of `Interface` are always reference-counted. + unsafe impl AlwaysRefCounted for Interface { ++ #[inline] + fn inc_ref(&self) { + // SAFETY: The invariants of `Interface` guarantee that `self.as_raw()` + // returns a valid `struct usb_interface` pointer, for which we will +@@ -400,6 +390,7 @@ fn inc_ref(&self) { + unsafe { bindings::usb_get_intf(self.as_raw()) }; + } + ++ #[inline] + unsafe fn dec_ref(obj: NonNull) { + // SAFETY: The safety requirements guarantee that the refcount is non-zero. + unsafe { bindings::usb_put_intf(obj.cast().as_ptr()) } +@@ -444,6 +435,7 @@ fn as_raw(&self) -> *mut bindings::usb_device { + + // SAFETY: Instances of `Device` are always reference-counted. + unsafe impl AlwaysRefCounted for Device { ++ #[inline] + fn inc_ref(&self) { + // SAFETY: The invariants of `Device` guarantee that `self.as_raw()` + // returns a valid `struct usb_device` pointer, for which we will +@@ -451,6 +443,7 @@ fn inc_ref(&self) { + unsafe { bindings::usb_get_dev(self.as_raw()) }; + } + ++ #[inline] + unsafe fn dec_ref(obj: NonNull) { + // SAFETY: The safety requirements guarantee that the refcount is non-zero. + unsafe { bindings::usb_put_dev(obj.cast().as_ptr()) } diff --git a/pkgbuilds/linux-omarchy/0603-usb-updates.patch.sig b/pkgbuilds/linux-omarchy/0603-usb-updates.patch.sig new file mode 100644 index 0000000..d35c194 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0603-usb-updates.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0604-xhci-fixes.patch b/pkgbuilds/linux-omarchy/0604-xhci-fixes.patch new file mode 100644 index 0000000..8545f24 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0604-xhci-fixes.patch @@ -0,0 +1,35 @@ +diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c +--- a/drivers/usb/host/xhci-mem.c ++++ b/drivers/usb/host/xhci-mem.c +@@ -2301,7 +2301,7 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int segs, gfp_t flags) + if (!segs) + segs = ERST_DEFAULT_SEGS; + +- max_segs = FIELD_GET(HCS_ERST_MAX, xhci->hcs_params2) << 2; ++ max_segs = BIT(FIELD_GET(HCS_ERST_MAX, xhci->hcs_params2)); + segs = min(segs, max_segs); + + ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev)); +diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -4328,11 +4328,16 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, + check_interval(urb, ep_ctx); + + /* +- * Check if this starts the isoc data flow. Relies on hw setting ep ctx +- * state after doorbell ring. Consider adding list_empty(td_list) check ++ * Schedule the URB discontiguously if all previous URBs have completed. ++ * XXX core can't tell if completions are pending but not running yet. + */ +- if (GET_EP_CTX_STATE(ep_ctx) != EP_STATE_RUNNING) ++ if (list_empty(&ep_ring->td_list) && ++ !hcd_periodic_completion_in_progress(xhci_to_hcd(xhci), urb->ep)) { ++ if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_RUNNING) ++ xhci_dbg(xhci, "Unexpected running ring at isoc stream start, uframe: %d\n", ++ xep->next_uframe); + xep->next_uframe = -1; ++ } + + return xhci_queue_isoc_tx(xhci, mem_flags, urb, slot_id, ep_index); + } diff --git a/pkgbuilds/linux-omarchy/0604-xhci-fixes.patch.sig b/pkgbuilds/linux-omarchy/0604-xhci-fixes.patch.sig new file mode 100644 index 0000000..d93226e Binary files /dev/null and b/pkgbuilds/linux-omarchy/0604-xhci-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0605-thunderbolt-pcie-rescan-after-tunnel.patch b/pkgbuilds/linux-omarchy/0605-thunderbolt-pcie-rescan-after-tunnel.patch new file mode 100644 index 0000000..a0b7740 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0605-thunderbolt-pcie-rescan-after-tunnel.patch @@ -0,0 +1,186 @@ +diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c +--- a/drivers/thunderbolt/domain.c ++++ b/drivers/thunderbolt/domain.c +@@ -909,6 +909,10 @@ int tb_domain_init(void) + { + int ret; + ++ ret = tb_pci_rescan_init(); ++ if (ret) ++ return ret; ++ + tb_configfs_init(); + tb_debugfs_init(); + tb_acpi_init(); +@@ -927,6 +931,7 @@ err_xdomain: + err_acpi: + tb_acpi_exit(); + tb_debugfs_exit(); ++ tb_pci_rescan_exit(); + + return ret; + } +@@ -940,4 +945,5 @@ void tb_domain_exit(void) + tb_acpi_exit(); + tb_debugfs_exit(); + tb_configfs_exit(); ++ tb_pci_rescan_exit(); + } +diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c +--- a/drivers/thunderbolt/tb.c ++++ b/drivers/thunderbolt/tb.c +@@ -9,7 +9,9 @@ + #include + #include + #include ++#include + #include ++#include + #include + + #include "tb.h" +@@ -18,6 +20,7 @@ + + #define TB_TIMEOUT 100 /* ms */ + #define TB_RELEASE_BW_TIMEOUT 10000 /* ms */ ++#define TB_PCI_RESCAN_DELAY 300 /* ms */ + + /* + * How many time bandwidth allocation request from graphics driver is +@@ -49,6 +52,13 @@ MODULE_PARM_DESC(asym_threshold, + "threshold (Mb/s) when to Gen 4 switch link symmetry. 0 disables. (default: " + __MODULE_STRING(TB_ASYM_THRESHOLD) ")"); + ++struct tb_pci_rescan { ++ struct delayed_work work; ++ struct kref kref; ++ struct pci_bus *bus; ++ bool stopped; ++}; ++ + /** + * struct tb_cm - Simple Thunderbolt connection manager + * @tunnel_list: List of active tunnels +@@ -67,8 +77,11 @@ struct tb_cm { + bool hotplug_active; + struct delayed_work remove_work; + struct tb_bandwidth_group groups[MAX_GROUPS]; ++ struct tb_pci_rescan *pci_rescan; + }; + ++static struct workqueue_struct *tb_pci_rescan_wq; ++ + static inline struct tb *tcm_to_tb(struct tb_cm *tcm) + { + return ((void *)tcm - sizeof(struct tb)); +@@ -2282,6 +2295,82 @@ static int tb_disconnect_pci(struct tb *tb, struct tb_switch *sw) + return 0; + } + ++static void tb_pci_rescan_release(struct kref *kref) ++{ ++ struct tb_pci_rescan *rescan = container_of(kref, typeof(*rescan), kref); ++ ++ put_device(&rescan->bus->dev); ++ kfree(rescan); ++} ++ ++static void tb_pci_rescan_work_fn(struct work_struct *work) ++{ ++ struct tb_pci_rescan *rescan = ++ container_of(to_delayed_work(work), typeof(*rescan), work); ++ ++ pci_lock_rescan_remove(); ++ if (!READ_ONCE(rescan->stopped)) ++ pci_rescan_bus(rescan->bus); ++ pci_unlock_rescan_remove(); ++ kref_put(&rescan->kref, tb_pci_rescan_release); ++} ++ ++static void tb_pci_rescan_schedule(struct tb *tb) ++{ ++ struct tb_cm *tcm = tb_priv(tb); ++ struct tb_pci_rescan *rescan = tcm->pci_rescan; ++ ++ if (!rescan) { ++ struct pci_bus *bus; ++ ++ if (!dev_is_pci(tb->nhi->dev)) ++ return; ++ bus = to_pci_dev(tb->nhi->dev)->bus; ++ if (bus->parent) ++ bus = bus->parent; ++ ++ rescan = kzalloc_obj(*rescan); ++ if (!rescan) ++ return; ++ INIT_DELAYED_WORK(&rescan->work, tb_pci_rescan_work_fn); ++ kref_init(&rescan->kref); ++ get_device(&bus->dev); ++ rescan->bus = bus; ++ tcm->pci_rescan = rescan; ++ } ++ ++ kref_get(&rescan->kref); ++ if (mod_delayed_work(tb_pci_rescan_wq, &rescan->work, ++ msecs_to_jiffies(TB_PCI_RESCAN_DELAY))) ++ kref_put(&rescan->kref, tb_pci_rescan_release); ++} ++ ++static void tb_pci_rescan_stop(struct tb_cm *tcm) ++{ ++ struct tb_pci_rescan *rescan = tcm->pci_rescan; ++ ++ if (!rescan) ++ return; ++ ++ WRITE_ONCE(rescan->stopped, true); ++ if (cancel_delayed_work(&rescan->work)) ++ kref_put(&rescan->kref, tb_pci_rescan_release); ++ kref_put(&rescan->kref, tb_pci_rescan_release); ++ tcm->pci_rescan = NULL; ++} ++ ++int tb_pci_rescan_init(void) ++{ ++ tb_pci_rescan_wq = alloc_workqueue("thunderbolt_pci_rescan", ++ WQ_FREEZABLE | WQ_UNBOUND, 0); ++ return tb_pci_rescan_wq ? 0 : -ENOMEM; ++} ++ ++void tb_pci_rescan_exit(void) ++{ ++ destroy_workqueue(tb_pci_rescan_wq); ++} ++ + static int tb_tunnel_pci(struct tb *tb, struct tb_switch *sw) + { + struct tb_port *up, *down, *port; +@@ -2323,6 +2412,7 @@ static int tb_tunnel_pci(struct tb *tb, struct tb_switch *sw) + tb_sw_warn(sw, "failed to connect xHCI\n"); + + list_add_tail(&tunnel->list, &tcm->tunnel_list); ++ tb_pci_rescan_schedule(tb); + return 0; + } + +@@ -2957,6 +3047,7 @@ static void tb_stop(struct tb *tb) + struct tb_tunnel *n; + + cancel_delayed_work(&tcm->remove_work); ++ tb_pci_rescan_stop(tcm); + /* tunnels are only present after everything has been initialized */ + list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) { + /* +diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h +--- a/drivers/thunderbolt/tb.h ++++ b/drivers/thunderbolt/tb.h +@@ -759,6 +759,8 @@ static inline int tb_port_write(struct tb_port *port, const void *buffer, + + struct tb *icm_probe(struct tb_nhi *nhi); + struct tb *tb_probe(struct tb_nhi *nhi); ++int tb_pci_rescan_init(void); ++void tb_pci_rescan_exit(void); + + extern const struct device_type tb_domain_type; + extern const struct device_type tb_retimer_type; diff --git a/pkgbuilds/linux-omarchy/0605-thunderbolt-pcie-rescan-after-tunnel.patch.sig b/pkgbuilds/linux-omarchy/0605-thunderbolt-pcie-rescan-after-tunnel.patch.sig new file mode 100644 index 0000000..63eb3c6 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0605-thunderbolt-pcie-rescan-after-tunnel.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0606-thunderbolt-fixes-2.patch b/pkgbuilds/linux-omarchy/0606-thunderbolt-fixes-2.patch new file mode 100644 index 0000000..0d85afa --- /dev/null +++ b/pkgbuilds/linux-omarchy/0606-thunderbolt-fixes-2.patch @@ -0,0 +1,239 @@ +diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c +--- a/drivers/thunderbolt/domain.c ++++ b/drivers/thunderbolt/domain.c +@@ -788,21 +788,6 @@ int tb_domain_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, + transmit_ring, receive_path, receive_ring); + } + +-static void tb_domain_reset_interface(struct tb *tb) +-{ +- struct tb_nhi *nhi = tb->nhi; +- +- if (!nhi->ops->reset_interface) +- return; +- +- guard(mutex)(&tb->lock); +- +- /* The reset clears the ring state so stop the control channel */ +- tb_ctl_stop(tb->ctl); +- nhi->ops->reset_interface(nhi); +- tb_ctl_start(tb->ctl); +-} +- + /** + * tb_domain_disconnect_xdomain_paths() - Disable DMA paths for XDomain + * @tb: Domain disabling the DMA paths +@@ -825,20 +810,11 @@ int tb_domain_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, + int transmit_path, int transmit_ring, + int receive_path, int receive_ring) + { +- int ret; +- + if (!tb->cm_ops->disconnect_xdomain_paths) + return -ENOTSUPP; + +- ret = tb->cm_ops->disconnect_xdomain_paths(tb, xd, transmit_path, ++ return tb->cm_ops->disconnect_xdomain_paths(tb, xd, transmit_path, + transmit_ring, receive_path, receive_ring); +- if (ret) +- return ret; +- +- if (tb->nhi->quirks & QUIRK_RESET_DMA_ON_TEARDOWN) +- tb_domain_reset_interface(tb); +- +- return 0; + } + + static int disconnect_xdomain(struct device *dev, void *data) +diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c +--- a/drivers/thunderbolt/nhi.c ++++ b/drivers/thunderbolt/nhi.c +@@ -1180,32 +1180,6 @@ static void nhi_reset(struct tb_nhi *nhi) + dev_warn(nhi->dev, "timeout resetting host router\n"); + } + +-/** +- * nhi_reset_interface() - Reset the host interface +- * @nhi: Host interface to reset +- * +- * Brings the registers in the memory BAR back to their default state and +- * clears the End-to-End Flow Control state. The caller is responsible for +- * stopping the control channel over the reset because it clears the ring +- * state as well. +- */ +-void nhi_reset_interface(struct tb_nhi *nhi) +-{ +- u32 val; +- +- val = ioread32(nhi->iobase + REG_CAPS); +- /* Only v1 host interfaces implement the reset */ +- if (FIELD_GET(REG_CAPS_VERSION_MASK, val) >= REG_CAPS_VERSION_2) +- return; +- +- dev_dbg(nhi->dev, "issuing host interface reset\n"); +- +- iowrite32(REG_HOST_INTERFACE_RESET_RST, +- nhi->iobase + REG_HOST_INTERFACE_RESET); +- /* Wait for tHIReset (10 ms) to complete */ +- usleep_range(10000, 20000); +-} +- + static struct tb *nhi_select_cm(struct tb_nhi *nhi) + { + struct tb *tb; +diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h +--- a/drivers/thunderbolt/nhi.h ++++ b/drivers/thunderbolt/nhi.h +@@ -36,8 +36,6 @@ irqreturn_t nhi_msi(int irq, void *data); + irqreturn_t ring_msix(int irq, void *data); + int nhi_probe(struct tb_nhi *nhi); + void nhi_shutdown(struct tb_nhi *nhi); +-void nhi_reset_interface(struct tb_nhi *nhi); +- + extern const struct dev_pm_ops nhi_pm_ops; + + /** +@@ -54,7 +52,6 @@ extern const struct dev_pm_ops nhi_pm_ops; + * @release_ring_irq: NHI specific interrupt release hook + * @is_present: Whether the device is currently present on the parent bus + * @init_interrupts: NHI specific interrupt initialization hook +- * @reset_interface: Resets the host interface + */ + struct tb_nhi_ops { + int (*init)(struct tb_nhi *nhi); +@@ -69,7 +66,6 @@ struct tb_nhi_ops { + void (*release_ring_irq)(struct tb_ring *ring); + bool (*is_present)(struct tb_nhi *nhi); + int (*init_interrupts)(struct tb_nhi *nhi); +- void (*reset_interface)(struct tb_nhi *nhi); + }; + + /* +@@ -120,24 +116,11 @@ struct tb_nhi_ops { + #define PCI_DEVICE_ID_INTEL_PTL_P_NHI0 0xe433 + #define PCI_DEVICE_ID_INTEL_PTL_P_NHI1 0xe434 + +-#define PCI_DEVICE_ID_AMD_1AH_M60H_NHI0 0x1120 +-#define PCI_DEVICE_ID_AMD_1AH_M60H_NHI1 0x1121 +-#define PCI_DEVICE_ID_AMD_1AH_M68H_NHI0 0x113b +-#define PCI_DEVICE_ID_AMD_1AH_M68H_NHI1 0x113c +-#define PCI_DEVICE_ID_AMD_1AH_M80H_NHI0 0x1155 +-#define PCI_DEVICE_ID_AMD_1AH_M80H_NHI1 0x1158 +-#define PCI_DEVICE_ID_AMD_1AH_M80H_NHI2 0x1159 +-#define PCI_DEVICE_ID_AMD_1AH_M24H_NHI0 0x151c +-#define PCI_DEVICE_ID_AMD_1AH_M24H_NHI1 0x151d +-#define PCI_DEVICE_ID_AMD_1AH_M70H_NHI0 0x158d +-#define PCI_DEVICE_ID_AMD_1AH_M70H_NHI1 0x158e +- + #define PCI_CLASS_SERIAL_USB_USB4 0x0c0340 + + /* Host interface quirks */ +-#define QUIRK_AUTO_CLEAR_INT BIT(0) +-#define QUIRK_E2E BIT(1) +-#define QUIRK_RESET_DMA_ON_TEARDOWN BIT(2) ++#define QUIRK_AUTO_CLEAR_INT BIT(0) ++#define QUIRK_E2E BIT(1) + + /* + * Minimal number of vectors when we use MSI-X. Two for control channel +diff --git a/drivers/thunderbolt/nhi_regs.h b/drivers/thunderbolt/nhi_regs.h +--- a/drivers/thunderbolt/nhi_regs.h ++++ b/drivers/thunderbolt/nhi_regs.h +@@ -115,10 +115,6 @@ struct ring_desc { + #define REG_CAPS_VERSION_MASK GENMASK(23, 16) + #define REG_CAPS_VERSION_2 0x40 + +-/* Host Interface Reset - resets TX/RX rings and E2E flow control counters */ +-#define REG_HOST_INTERFACE_RESET 0x39858 +-#define REG_HOST_INTERFACE_RESET_RST BIT(0) +- + #define REG_DMA_MISC 0x39864 + #define REG_DMA_MISC_INT_AUTO_CLEAR BIT(2) + #define REG_DMA_MISC_DISABLE_AUTO_CLEAR BIT(17) +diff --git a/drivers/thunderbolt/pci.c b/drivers/thunderbolt/pci.c +--- a/drivers/thunderbolt/pci.c ++++ b/drivers/thunderbolt/pci.c +@@ -62,27 +62,6 @@ static void nhi_pci_check_quirks(struct tb_nhi_pci *nhi_pci) + nhi->quirks |= QUIRK_E2E; + break; + } +- } else if (pdev->vendor == PCI_VENDOR_ID_AMD) { +- switch (pdev->device) { +- case PCI_DEVICE_ID_AMD_1AH_M60H_NHI0: +- case PCI_DEVICE_ID_AMD_1AH_M60H_NHI1: +- case PCI_DEVICE_ID_AMD_1AH_M68H_NHI0: +- case PCI_DEVICE_ID_AMD_1AH_M68H_NHI1: +- case PCI_DEVICE_ID_AMD_1AH_M80H_NHI0: +- case PCI_DEVICE_ID_AMD_1AH_M80H_NHI1: +- case PCI_DEVICE_ID_AMD_1AH_M80H_NHI2: +- case PCI_DEVICE_ID_AMD_1AH_M24H_NHI0: +- case PCI_DEVICE_ID_AMD_1AH_M24H_NHI1: +- case PCI_DEVICE_ID_AMD_1AH_M70H_NHI0: +- case PCI_DEVICE_ID_AMD_1AH_M70H_NHI1: +- /* +- * These AMD hosts may hang the Tx ring when the +- * DMA paths are torn down so they need the host +- * interface reset after each teardown. +- */ +- nhi->quirks |= QUIRK_RESET_DMA_ON_TEARDOWN; +- break; +- } + } + } + +@@ -279,7 +258,6 @@ static const struct tb_nhi_ops pci_nhi_default_ops = { + .shutdown = nhi_pci_release_irq, + .is_present = nhi_pci_is_present, + .init_interrupts = nhi_pci_init_msi, +- .reset_interface = nhi_reset_interface, + }; + + /* Ice Lake specific NHI operations */ +@@ -463,7 +441,6 @@ static const struct tb_nhi_ops icl_nhi_ops = { + .release_ring_irq = nhi_pci_ring_release_msix, + .is_present = nhi_pci_is_present, + .init_interrupts = nhi_pci_init_msi, +- .reset_interface = nhi_reset_interface, + }; + + static int nhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +diff --git a/drivers/thunderbolt/quirks.c b/drivers/thunderbolt/quirks.c +--- a/drivers/thunderbolt/quirks.c ++++ b/drivers/thunderbolt/quirks.c +@@ -99,6 +99,9 @@ static const struct tb_quirk tb_quirks[] = { + quirk_usb3_maximum_bandwidth }, + { 0x8087, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HUB_40G_BRIDGE, 0x0000, 0x0000, + quirk_usb3_maximum_bandwidth }, ++ /* CLx is unstable on the Anker Prime TB5 dock (DROM 01ea:83b5) */ ++ { 0x8087, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HUB_80G_BRIDGE, 0x01ea, 0x83b5, ++ quirk_clx_disable }, + /* + * Block Runtime PM in DP redrive mode for Intel Barlow Ridge host + * controllers. +diff --git a/drivers/thunderbolt/stream.c b/drivers/thunderbolt/stream.c +--- a/drivers/thunderbolt/stream.c ++++ b/drivers/thunderbolt/stream.c +@@ -980,6 +980,9 @@ static int tbstream_dev_fops_open(struct inode *inode, struct file *file) + } + + mutex_unlock(&sdev->lock); ++ ++ /* Stream handles IOCB_NOWAIT just fine */ ++ file->f_mode |= FMODE_NOWAIT; + return 0; + + err_unlock: +diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c +--- a/drivers/thunderbolt/xdomain.c ++++ b/drivers/thunderbolt/xdomain.c +@@ -377,6 +377,10 @@ static int tb_xdp_properties_request(struct tb_ctl *ctl, u64 route, + + len += sizeof(res->hdr.xd_hdr) / 4; + len -= sizeof(*res) / 4; ++ if (len > TB_XDP_PROPERTIES_MAX_DATA_LENGTH) { ++ ret = -EINVAL; ++ goto err; ++ } + + if (res->offset != req.offset) { + ret = -EINVAL; diff --git a/pkgbuilds/linux-omarchy/0606-thunderbolt-fixes-2.patch.sig b/pkgbuilds/linux-omarchy/0606-thunderbolt-fixes-2.patch.sig new file mode 100644 index 0000000..8e90b6c Binary files /dev/null and b/pkgbuilds/linux-omarchy/0606-thunderbolt-fixes-2.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0607-usb-fixes.patch b/pkgbuilds/linux-omarchy/0607-usb-fixes.patch new file mode 100644 index 0000000..2e3ea13 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0607-usb-fixes.patch @@ -0,0 +1,1673 @@ +diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c +--- a/drivers/usb/cdns3/cdns3-gadget.c ++++ b/drivers/usb/cdns3/cdns3-gadget.c +@@ -3269,6 +3269,10 @@ static void cdns3_gadget_exit(struct cdns *cdns) + usb_del_gadget(&priv_dev->gadget); + devm_free_irq(cdns->dev, cdns->dev_irq, priv_dev); + ++ /* The works can still be queued until the IRQ is freed. */ ++ cancel_work_sync(&priv_dev->pending_status_wq); ++ cancel_work_sync(&priv_dev->aligned_buf_wq); ++ + cdns3_free_all_eps(priv_dev); + + while (!list_empty(&priv_dev->aligned_buf_list)) { +diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c +--- a/drivers/usb/cdns3/cdns3-pci-wrap.c ++++ b/drivers/usb/cdns3/cdns3-pci-wrap.c +@@ -96,6 +96,11 @@ static int cdns3_pci_probe(struct pci_dev *pdev, + + if (pci_is_enabled(func)) { + wrap = pci_get_drvdata(func); ++ if (!wrap) { ++ dev_err(&pdev->dev, ++ "second function not initialized, retrying\n"); ++ return -EPROBE_DEFER; ++ } + } else { + wrap = kzalloc_obj(*wrap); + if (!wrap) +diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c +--- a/drivers/usb/chipidea/ci_hdrc_tegra.c ++++ b/drivers/usb/chipidea/ci_hdrc_tegra.c +@@ -306,8 +306,10 @@ static int tegra_usb_probe(struct platform_device *pdev) + + pm_runtime_enable(&pdev->dev); + err = pm_runtime_resume_and_get(&pdev->dev); +- if (err) ++ if (err) { ++ pm_runtime_disable(&pdev->dev); + return err; ++ } + + if (device_property_present(&pdev->dev, "nvidia,needs-double-reset")) + usb->needs_double_reset = true; +diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c +--- a/drivers/usb/class/cdc-acm.c ++++ b/drivers/usb/class/cdc-acm.c +@@ -331,7 +331,6 @@ static void acm_process_notification(struct acm *acm, unsigned char *buf) + + spin_lock_irqsave(&acm->read_lock, flags); + acm->ctrlin = newctrl; +- acm->oldcount = acm->iocount; + + if (difference & USB_CDC_SERIAL_STATE_DSR) + acm->iocount.dsr++; +@@ -788,6 +787,9 @@ static void acm_port_shutdown(struct tty_port *port) + usb_autopm_put_interface_async(acm->control); + } + ++ if (acm->disconnected) ++ return; ++ + acm_unpoison_urbs(acm); + + if (acm->quirks & ALWAYS_POLL_CTRL) { +@@ -1027,11 +1029,16 @@ static int wait_serial_change(struct acm *acm, unsigned long arg) + DECLARE_WAITQUEUE(wait, current); + struct async_icount old, new; + +- do { ++ spin_lock_irq(&acm->read_lock); ++ old = acm->iocount; ++ spin_unlock_irq(&acm->read_lock); ++ ++ add_wait_queue(&acm->wioctl, &wait); ++ for (;;) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ + spin_lock_irq(&acm->read_lock); +- old = acm->oldcount; + new = acm->iocount; +- acm->oldcount = new; + spin_unlock_irq(&acm->read_lock); + + if ((arg & TIOCM_DSR) && +@@ -1044,22 +1051,20 @@ static int wait_serial_change(struct acm *acm, unsigned long arg) + old.rng != new.rng) + break; + +- add_wait_queue(&acm->wioctl, &wait); +- set_current_state(TASK_INTERRUPTIBLE); +- schedule(); +- remove_wait_queue(&acm->wioctl, &wait); + if (acm->disconnected) { +- if (arg & TIOCM_CD) +- break; +- else +- rv = -ENODEV; +- } else { +- if (signal_pending(current)) +- rv = -ERESTARTSYS; ++ rv = -ENODEV; ++ break; + } +- } while (!rv); + +- ++ schedule(); ++ ++ if (signal_pending(current)) { ++ rv = -ERESTARTSYS; ++ break; ++ } ++ } ++ __set_current_state(TASK_RUNNING); ++ remove_wait_queue(&acm->wioctl, &wait); + + return rv; + } +diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h +--- a/drivers/usb/class/cdc-acm.h ++++ b/drivers/usb/class/cdc-acm.h +@@ -90,7 +90,6 @@ struct acm { + unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ + unsigned int ctrlout; /* output control lines (DTR, RTS) */ + struct async_icount iocount; /* counters for control line changes */ +- struct async_icount oldcount; /* for comparison of counter */ + wait_queue_head_t wioctl; /* for ioctl */ + unsigned int writesize; /* max packet size for the output bulk endpoint */ + unsigned int readsize,ctrlsize; /* buffer sizes for freeing */ +diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c +--- a/drivers/usb/core/hub.c ++++ b/drivers/usb/core/hub.c +@@ -73,8 +73,11 @@ + /* + * Give SS hubs 200ms time after wake to train downstream links before + * assuming no port activity and allowing hub to runtime suspend back. ++ * Root hubs have no upstream hub whose wake propagation needs to be ++ * accounted for, so they need less time, use 120ms for them. + */ + #define USB_SS_PORT_U0_WAKE_TIME 200 /* ms */ ++#define USB_SS_RH_PORT_U0_WAKE_TIME 120 /* ms */ + + /* Protect struct usb_device->state and ->children members + * Note: Both are also protected by ->dev.sem, except that ->state can +@@ -1358,7 +1361,9 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) + + queue_delayed_work(system_power_efficient_wq, + &hub->post_resume_work, +- msecs_to_jiffies(USB_SS_PORT_U0_WAKE_TIME)); ++ msecs_to_jiffies(hdev->parent ? ++ USB_SS_PORT_U0_WAKE_TIME : ++ USB_SS_RH_PORT_U0_WAKE_TIME)); + return; + } + +diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c +--- a/drivers/usb/core/message.c ++++ b/drivers/usb/core/message.c +@@ -1337,7 +1337,8 @@ static void remove_intf_ep_devs(struct usb_interface *intf) + * + * Disables the endpoint for URB submission and nukes all pending URBs. + * If @reset_hardware is set then also deallocates hcd/hardware state +- * for the endpoint. ++ * for the endpoint (clearing both ep_in and ep_out pointers for ++ * bidirectional non-ep0 control endpoints). + */ + void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr, + bool reset_hardware) +@@ -1358,6 +1359,11 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr, + dev->ep_in[epnum] = NULL; + } + if (ep) { ++ if (reset_hardware && epnum != 0 && ++ usb_endpoint_xfer_control(&ep->desc)) { ++ dev->ep_out[epnum] = NULL; ++ dev->ep_in[epnum] = NULL; ++ } + ep->enabled = 0; + usb_hcd_flush_endpoint(dev, ep); + if (reset_hardware) +diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c +--- a/drivers/usb/dwc2/hcd.c ++++ b/drivers/usb/dwc2/hcd.c +@@ -5082,14 +5082,13 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg) + } + + cancel_work_sync(&hsotg->phy_reset_work); +- +- timer_delete(&hsotg->wkp_timer); + } + + static void dwc2_hcd_release(struct dwc2_hsotg *hsotg) + { + /* Turn off all host-specific interrupts */ + dwc2_disable_host_interrupts(hsotg); ++ timer_shutdown_sync(&hsotg->wkp_timer); + + dwc2_hcd_free(hsotg); + } +diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h +--- a/drivers/usb/dwc3/core.h ++++ b/drivers/usb/dwc3/core.h +@@ -49,6 +49,7 @@ + #define DWC3_ENDPOINTS_NUM 32 + #define DWC3_XHCI_RESOURCES_NUM 2 + #define DWC3_ISOC_MAX_RETRIES 5 ++#define DWC3_ERR_RECOVERY_MAX 3 + + #define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */ + #define DWC3_EVENT_BUFFERS_SIZE 4096 +@@ -841,6 +842,12 @@ enum dwc3_link_state { + DWC3_LINK_STATE_MASK = 0x0f, + }; + ++enum dwc3_err_state { ++ DWC3_ERR_NONE = 0, ++ DWC3_ERR_RECOVERY, ++ DWC3_ERR_UNRECOVERABLE, ++}; ++ + /* TRB Length, PCM and Status */ + #define DWC3_TRB_SIZE_MASK (0x00ffffff) + #define DWC3_TRB_SIZE_LENGTH(n) ((n) & DWC3_TRB_SIZE_MASK) +@@ -1004,6 +1011,7 @@ struct dwc3_glue_ops { + /** + * struct dwc3 - representation of our controller + * @drd_work: workqueue used for role swapping ++ * @err_recovery_work: workqueue used for controller error recovery + * @ep0_trb: trb which is used for the ctrl_req + * @bounce: address of bounce buffer + * @setup_buf: used while precessing STD USB requests +@@ -1013,6 +1021,7 @@ struct dwc3_glue_ops { + * @ep0_in_setup: one control transfer is completed and enter setup phase + * @lock: for synchronizing + * @mutex: for mode switching ++ * @connect_mutex: for the pull-up and err_recovery_work + * @dev: pointer to our struct device + * @sysdev: pointer to the DMA-capable device + * @xhci: pointer to our xHCI child +@@ -1079,6 +1088,9 @@ struct dwc3_glue_ops { + * @ep0_next_event: hold the next expected event + * @ep0state: state of endpoint zero + * @link_state: link state ++ * @err_state: current error recovery state. ++ * @err_recovery_count: number of consecutive error recovery attempts until ++ * confirmed healthy and reset to 0 on reset event. + * @speed: device speed (super, high, full, low) + * @hwparams: copy of hwparams registers + * @regset: debugfs pointer to regdump file +@@ -1189,6 +1201,7 @@ struct dwc3_glue_ops { + */ + struct dwc3 { + struct work_struct drd_work; ++ struct work_struct err_recovery_work; + struct dwc3_trb *ep0_trb; + void *bounce; + u8 *setup_buf; +@@ -1203,6 +1216,9 @@ struct dwc3 { + /* mode switching lock */ + struct mutex mutex; + ++ /* serializes pull-up run/stop vs error recovery */ ++ struct mutex connect_mutex; ++ + struct device *dev; + struct device *sysdev; + +@@ -1332,6 +1348,9 @@ struct dwc3 { + enum dwc3_ep0_next ep0_next_event; + enum dwc3_ep0_state ep0state; + enum dwc3_link_state link_state; ++ enum dwc3_err_state err_state; ++ ++ u32 err_recovery_count; + + u16 u2sel; + u16 u2pel; +diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c +--- a/drivers/usb/dwc3/dwc3-am62.c ++++ b/drivers/usb/dwc3/dwc3-am62.c +@@ -299,6 +299,7 @@ static int dwc3_ti_probe(struct platform_device *pdev) + + err_pm_disable: + clk_disable_unprepare(am62->usb2_refclk); ++ pm_runtime_put_noidle(dev); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + return ret; +diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c +--- a/drivers/usb/dwc3/ep0.c ++++ b/drivers/usb/dwc3/ep0.c +@@ -200,6 +200,11 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, + int ret; + + spin_lock_irqsave(&dwc->lock, flags); ++ if (dwc->err_state != DWC3_ERR_NONE) { ++ ret = -ESHUTDOWN; ++ goto out; ++ } ++ + if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) { + dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n", + dep->name); +@@ -271,6 +276,10 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value) + int ret; + + spin_lock_irqsave(&dwc->lock, flags); ++ if (dwc->err_state != DWC3_ERR_NONE) { ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ return -ESHUTDOWN; ++ } + ret = __dwc3_gadget_ep0_set_halt(ep, value); + spin_unlock_irqrestore(&dwc->lock, flags); + +diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c +--- a/drivers/usb/dwc3/gadget.c ++++ b/drivers/usb/dwc3/gadget.c +@@ -1149,6 +1149,10 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep, + return 0; + + spin_lock_irqsave(&dwc->lock, flags); ++ if (dwc->err_state != DWC3_ERR_NONE) { ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ return -ESHUTDOWN; ++ } + ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT); + spin_unlock_irqrestore(&dwc->lock, flags); + +@@ -2060,6 +2064,10 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, + int ret; + + spin_lock_irqsave(&dwc->lock, flags); ++ if (dwc->err_state != DWC3_ERR_NONE) { ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ return -ESHUTDOWN; ++ } + ret = __dwc3_gadget_ep_queue(dep, req); + spin_unlock_irqrestore(&dwc->lock, flags); + +@@ -2292,6 +2300,10 @@ static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value) + int ret; + + spin_lock_irqsave(&dwc->lock, flags); ++ if (dwc->err_state != DWC3_ERR_NONE) { ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ return -ESHUTDOWN; ++ } + ret = __dwc3_gadget_ep_set_halt(dep, value, false); + spin_unlock_irqrestore(&dwc->lock, flags); + +@@ -2306,6 +2318,10 @@ static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep) + int ret; + + spin_lock_irqsave(&dwc->lock, flags); ++ if (dwc->err_state != DWC3_ERR_NONE) { ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ return -ESHUTDOWN; ++ } + dep->flags |= DWC3_EP_WEDGE; + + if (dep->number == 0 || dep->number == 1) +@@ -2437,6 +2453,10 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g) + } + + spin_lock_irqsave(&dwc->lock, flags); ++ if (dwc->err_state != DWC3_ERR_NONE) { ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ return -ESHUTDOWN; ++ } + if (!dwc->gadget->wakeup_armed) { + dev_err(dwc->dev, "not armed for remote wakeup\n"); + spin_unlock_irqrestore(&dwc->lock, flags); +@@ -2464,6 +2484,10 @@ static int dwc3_gadget_func_wakeup(struct usb_gadget *g, int intf_id) + } + + spin_lock_irqsave(&dwc->lock, flags); ++ if (dwc->err_state != DWC3_ERR_NONE) { ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ return -ESHUTDOWN; ++ } + /* + * If the link is in U3, signal for remote wakeup and wait for the + * link to transition to U0 before sending device notification. +@@ -2833,10 +2857,13 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) + + synchronize_irq(dwc->irq_gadget); + ++ /* Serialize against dwc3_err_recovery_work() */ ++ mutex_lock(&dwc->connect_mutex); + if (!is_on) + ret = dwc3_gadget_soft_disconnect(dwc); + else + ret = dwc3_gadget_soft_connect(dwc); ++ mutex_unlock(&dwc->connect_mutex); + + pm_runtime_put(dwc->dev); + +@@ -4153,6 +4180,10 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) + + dwc->suspended = false; + ++ /* The controller is recovered. */ ++ if (dwc->err_state == DWC3_ERR_NONE) ++ dwc->err_recovery_count = 0; ++ + /* + * Ideally, dwc3_reset_gadget() would trigger the function + * drivers to stop any active transfers through ep disable. +@@ -4640,6 +4671,12 @@ static irqreturn_t dwc3_thread_interrupt(int irq, void *_evt) + return ret; + } + ++static void dwc3_schedule_err_recovery(struct dwc3 *dwc) ++{ ++ dwc->err_state = DWC3_ERR_RECOVERY; ++ schedule_work(&dwc->err_recovery_work); ++} ++ + static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt) + { + struct dwc3 *dwc = evt->dwc; +@@ -4673,9 +4710,21 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt) + return IRQ_NONE; + + if (count > evt->length) { +- dev_err_ratelimited(dwc->dev, "invalid count(%u) > evt->length(%u)\n", ++ dev_err(dwc->dev, "invalid count(%u) > evt->length(%u)\n", + count, evt->length); +- return IRQ_NONE; ++ /* ++ * This is a fatal error - the driver and controller are out of ++ * sync on which event has been consumed. Reinitializing the ++ * controller is required to recover. Write the bogus count back ++ * to GEVNTCOUNT to clear the IRQ source, consistent with the ++ * stale event clearing in dwc3_event_buffers_setup(), then ++ * schedule error recovery. ++ */ ++ spin_lock(&dwc->lock); ++ dwc3_schedule_err_recovery(dwc); ++ spin_unlock(&dwc->lock); ++ dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), count); ++ return IRQ_HANDLED; + } + + evt->count = count; +@@ -4735,6 +4784,57 @@ static void dwc_gadget_release(struct device *dev) + kfree(gadget); + } + ++static void dwc3_err_recovery_work(struct work_struct *work) ++{ ++ struct dwc3 *dwc = container_of(work, struct dwc3, err_recovery_work); ++ unsigned long flags; ++ int ret; ++ ++ /* serializes against dwc3_gadget_pullup() */ ++ mutex_lock(&dwc->connect_mutex); ++ ++ ret = dwc3_gadget_soft_disconnect(dwc); ++ ++ dwc3_disconnect_gadget_sleepable(dwc); ++ ++ if (ret) ++ goto err_unrecoverable; ++ ++ if (dwc->softconnect) { ++ u32 count; ++ /* ++ * Wait irq to finish before soft_connect resets evt->lpos and ++ * the event buffer registers to avoid racing with ++ * dwc3_process_event_buf(). ++ */ ++ synchronize_irq(dwc->irq_gadget); ++ ++ spin_lock_irqsave(&dwc->lock, flags); ++ count = ++dwc->err_recovery_count; ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ ++ if (count > DWC3_ERR_RECOVERY_MAX) ++ goto err_unrecoverable; ++ ++ ret = dwc3_gadget_soft_connect(dwc); ++ if (ret) ++ goto err_unrecoverable; ++ } ++ mutex_unlock(&dwc->connect_mutex); ++ ++ spin_lock_irqsave(&dwc->lock, flags); ++ dwc->err_state = DWC3_ERR_NONE; ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ return; ++ ++err_unrecoverable: ++ dev_err(dwc->dev, "Unable to recover the controller\n"); ++ mutex_unlock(&dwc->connect_mutex); ++ spin_lock_irqsave(&dwc->lock, flags); ++ dwc->err_state = DWC3_ERR_UNRECOVERABLE; ++ spin_unlock_irqrestore(&dwc->lock, flags); ++} ++ + /** + * dwc3_gadget_init - initializes gadget related registers + * @dwc: pointer to our controller context structure +@@ -4778,6 +4878,8 @@ int dwc3_gadget_init(struct dwc3 *dwc) + } + + init_completion(&dwc->ep0_in_setup); ++ INIT_WORK(&dwc->err_recovery_work, dwc3_err_recovery_work); ++ mutex_init(&dwc->connect_mutex); + dwc->gadget = kzalloc_obj(struct usb_gadget); + if (!dwc->gadget) { + ret = -ENOMEM; +@@ -4874,6 +4976,8 @@ void dwc3_gadget_exit(struct dwc3 *dwc) + if (!dwc->gadget) + return; + ++ cancel_work_sync(&dwc->err_recovery_work); ++ mutex_destroy(&dwc->connect_mutex); + dwc3_enable_susphy(dwc, true); + usb_del_gadget(dwc->gadget); + dwc3_gadget_free_endpoints(dwc); +diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c +--- a/drivers/usb/gadget/function/f_fs.c ++++ b/drivers/usb/gadget/function/f_fs.c +@@ -1877,8 +1877,9 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, + break; + case FUNCTIONFS_ENDPOINT_DESC: + { ++ const struct usb_endpoint_descriptor *desc; ++ struct usb_endpoint_descriptor desc1; + int desc_idx; +- struct usb_endpoint_descriptor desc1, *desc; + + switch (epfile->ffs->gadget->speed) { + case USB_SPEED_SUPER: +@@ -1892,7 +1893,17 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, + desc_idx = 0; + } + +- desc = epfile->ep->descs[desc_idx]; ++ do { ++ desc = epfile->ep->descs[desc_idx]; ++ } while (!desc && --desc_idx >= 0); ++ ++ if (!desc) ++ desc = epfile->ep->ep->desc; ++ if (!desc) { ++ ret = -EINVAL; ++ break; ++ } ++ + memcpy(&desc1, desc, desc->bLength); + + spin_unlock_irq(&epfile->ffs->eps_lock); +diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c +--- a/drivers/usb/gadget/function/f_midi2.c ++++ b/drivers/usb/gadget/function/f_midi2.c +@@ -1633,8 +1633,8 @@ struct f_midi2_usb_config { + + /* MIDI 1.0 jacks */ + unsigned char jack_in, jack_out, jack_id; +- struct usb_midi_in_jack_descriptor jack_ins[MAX_CABLES]; +- struct usb_midi_out_jack_descriptor_1 jack_outs[MAX_CABLES]; ++ struct usb_midi_in_jack_descriptor jack_ins[MAX_CABLES * 2]; ++ struct usb_midi_out_jack_descriptor_1 jack_outs[MAX_CABLES * 2]; + }; + + static int append_config(struct f_midi2_usb_config *config, void *d) +diff --git a/drivers/usb/gadget/function/f_uac1_legacy.c b/drivers/usb/gadget/function/f_uac1_legacy.c +--- a/drivers/usb/gadget/function/f_uac1_legacy.c ++++ b/drivers/usb/gadget/function/f_uac1_legacy.c +@@ -797,6 +797,9 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f) + + static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value) + { ++ if (cmd >= ARRAY_SIZE(con->data)) ++ return -EINVAL; ++ + con->data[cmd] = value; + + return 0; +@@ -804,6 +807,9 @@ static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value) + + static int generic_get_cmd(struct usb_audio_control *con, u8 cmd) + { ++ if (cmd >= ARRAY_SIZE(con->data)) ++ return -EINVAL; ++ + return con->data[cmd]; + } + +diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c +--- a/drivers/usb/gadget/udc/aspeed-vhub/core.c ++++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c +@@ -267,6 +267,9 @@ static void ast_vhub_remove(struct platform_device *pdev) + for (i = 0; i < vhub->max_ports; i++) + ast_vhub_del_dev(&vhub->ports[i].dev); + ++ /* Final drain; the worker takes vhub->lock, so stay outside of it */ ++ cancel_work_sync(&vhub->wake_work); ++ + spin_lock_irqsave(&vhub->lock, flags); + + /* Mask & ack all interrupts */ +@@ -328,6 +331,7 @@ static int ast_vhub_probe(struct platform_device *pdev) + return -ENOMEM; + + spin_lock_init(&vhub->lock); ++ INIT_WORK(&vhub->wake_work, ast_vhub_wake_work); + vhub->pdev = pdev; + vhub->port_irq_mask = GENMASK(VHUB_IRQ_DEV1_BIT + vhub->max_ports - 1, + VHUB_IRQ_DEV1_BIT); +diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c +--- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c ++++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c +@@ -280,7 +280,7 @@ static int ast_vhub_udc_wakeup(struct usb_gadget* gadget) + int rc = -EINVAL; + + spin_lock_irqsave(&d->vhub->lock, flags); +- if (!d->wakeup_en) ++ if (!d->wakeup_en || !d->registered) + goto err; + + DDBG(d, "Device initiated wakeup\n"); +diff --git a/drivers/usb/gadget/udc/aspeed-vhub/hub.c b/drivers/usb/gadget/udc/aspeed-vhub/hub.c +--- a/drivers/usb/gadget/udc/aspeed-vhub/hub.c ++++ b/drivers/usb/gadget/udc/aspeed-vhub/hub.c +@@ -558,7 +558,7 @@ void ast_vhub_device_connect(struct ast_vhub *vhub, + ast_vhub_send_host_wakeup(vhub); + } + +-static void ast_vhub_wake_work(struct work_struct *work) ++void ast_vhub_wake_work(struct work_struct *work) + { + struct ast_vhub *vhub = container_of(work, + struct ast_vhub, +@@ -588,6 +588,8 @@ static void ast_vhub_wake_work(struct work_struct *work) + + void ast_vhub_hub_wake_all(struct ast_vhub *vhub) + { ++ lockdep_assert_held(&vhub->lock); ++ + /* + * A device is trying to wake the world, because this + * can recurse into the device, we break the call chain +@@ -1076,7 +1078,6 @@ static int ast_vhub_init_desc(struct ast_vhub *vhub) + int ast_vhub_init_hub(struct ast_vhub *vhub) + { + vhub->speed = USB_SPEED_UNKNOWN; +- INIT_WORK(&vhub->wake_work, ast_vhub_wake_work); + + return ast_vhub_init_desc(vhub); + } +diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h +--- a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h ++++ b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h +@@ -547,6 +547,7 @@ void ast_vhub_hub_suspend(struct ast_vhub *vhub); + void ast_vhub_hub_resume(struct ast_vhub *vhub); + void ast_vhub_hub_reset(struct ast_vhub *vhub); + void ast_vhub_hub_wake_all(struct ast_vhub *vhub); ++void ast_vhub_wake_work(struct work_struct *work); + + /* dev.c */ + int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx); +diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c +--- a/drivers/usb/gadget/udc/dummy_hcd.c ++++ b/drivers/usb/gadget/udc/dummy_hcd.c +@@ -343,9 +343,11 @@ static void dummy_giveback(struct dummy *dum, struct usb_ep *_ep, + { + bool fifo = req == &dum->fifo_req; + ++ ++dum->callback_usage; + spin_unlock(&dum->lock); + usb_gadget_giveback_request(_ep, &req->req); + spin_lock(&dum->lock); ++ --dum->callback_usage; + if (fifo) + dum->fifo_req_busy = 0; + } +@@ -759,11 +761,13 @@ static int dummy_queue(struct usb_ep *_ep, struct usb_request *_req, + req->req.complete = fifo_complete; + + list_add_tail(&req->queue, &ep->queue); ++ ++dum->callback_usage; + spin_unlock(&dum->lock); + _req->actual = _req->length; + _req->status = 0; + usb_gadget_giveback_request(_ep, _req); + spin_lock(&dum->lock); ++ --dum->callback_usage; + } else + list_add_tail(&req->queue, &ep->queue); + spin_unlock_irqrestore(&dum->lock, flags); +diff --git a/drivers/usb/host/octeon-hcd.c b/drivers/usb/host/octeon-hcd.c +--- a/drivers/usb/host/octeon-hcd.c ++++ b/drivers/usb/host/octeon-hcd.c +@@ -378,8 +378,22 @@ struct octeon_hcd { + struct cvmx_usb_transaction *active_split; + struct cvmx_usb_tx_fifo periodic; + struct cvmx_usb_tx_fifo nonperiodic; ++ struct hrtimer sof_timer; + }; + ++static int cvmx_usb_poll(struct octeon_hcd *usb); ++ ++static enum hrtimer_restart octeon_usb_sof_timer(struct hrtimer *t) ++{ ++ struct octeon_hcd *usb = container_of(t, struct octeon_hcd, sof_timer); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&usb->lock, flags); ++ cvmx_usb_poll(usb); ++ spin_unlock_irqrestore(&usb->lock, flags); ++ return HRTIMER_NORESTART; ++} ++ + /* + * This macro logically sets a single field in a CSR. It does the sequence + * read, modify, and write +@@ -578,7 +592,7 @@ static int cvmx_wait_tx_rx(struct octeon_hcd *usb, int fflsh_type) + int result; + u64 address = CVMX_USBCX_GRSTCTL(usb->index); + u64 done = cvmx_get_cycle() + 100 * +- (u64)octeon_get_clock_rate / 1000000; ++ (u64)octeon_get_clock_rate() / 1000000; + union cvmx_usbcx_grstctl c; + + while (1) { +@@ -853,6 +867,14 @@ static int cvmx_usb_initialize(struct device *dev, + * USBC_GAHBCFG[PTXFEMPLVL] + * Global interrupt mask, USBC_GAHBCFG[GLBLINTRMSK] = 1 + */ ++ usbcx_gahbcfg.u32 = cvmx_usb_read_csr32(usb, ++ CVMX_USBCX_GHWCFG3(usb->index)); ++ if (usbcx_gahbcfg.u32 == 0xffffffff || usbcx_gahbcfg.u32 == 0) { ++ dev_err(dev, "USB core is not responding (GHWCFG3=0x%08x)\n", ++ usbcx_gahbcfg.u32); ++ return -ENODEV; ++ } ++ + usbcx_gahbcfg.u32 = 0; + usbcx_gahbcfg.s.dmaen = !(usb->init_flags & + CVMX_USB_INITIALIZE_FLAGS_NO_DMA); +@@ -1900,6 +1922,7 @@ static void cvmx_usb_schedule(struct octeon_hcd *usb, int is_sof) + int channel; + struct cvmx_usb_pipe *pipe; + int need_sof; ++ u64 min_due; + enum cvmx_usb_transfer ttype; + + if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA) { +@@ -1940,15 +1963,33 @@ static void cvmx_usb_schedule(struct octeon_hcd *usb, int is_sof) + * future that might need to be scheduled + */ + need_sof = 0; ++ min_due = ~0ull; + for (ttype = CVMX_USB_TRANSFER_CONTROL; + ttype <= CVMX_USB_TRANSFER_INTERRUPT; ttype++) { + list_for_each_entry(pipe, &usb->active_pipes[ttype], node) { +- if (pipe->next_tx_frame > usb->frame_number) { +- need_sof = 1; +- break; +- } ++ if (pipe->next_tx_frame > usb->frame_number && ++ pipe->next_tx_frame < min_due) ++ min_due = pipe->next_tx_frame; + } + } ++ if (min_due != ~0ull) { ++ u64 delta = min_due - usb->frame_number; ++ ++ /* ++ * frame_number is resynced from HFNUM on every poll, so a ++ * deadline that is many frames away does not need an ++ * interrupt on every SOF to count them down - sleep on the ++ * timer instead and keep SOF interrupts for deadlines within ++ * a few frames. Stay well below the 16383-frame wrap of ++ * HFNUM. One (micro)frame is 125us in high-speed mode. ++ */ ++ if (delta <= 4 || delta > 8000) ++ need_sof = 1; ++ else ++ hrtimer_start(&usb->sof_timer, ++ ns_to_ktime((delta - 2) * 125000), ++ HRTIMER_MODE_REL); ++ } + USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), + cvmx_usbcx_gintmsk, sofmsk, need_sof); + } +@@ -3638,6 +3679,8 @@ static int octeon_usb_probe(struct platform_device *pdev) + usb = (struct octeon_hcd *)hcd->hcd_priv; + + spin_lock_init(&usb->lock); ++ hrtimer_setup(&usb->sof_timer, octeon_usb_sof_timer, CLOCK_MONOTONIC, ++ HRTIMER_MODE_REL); + + usb->init_flags = initialize_flags; + +@@ -3688,6 +3731,7 @@ static void octeon_usb_remove(struct platform_device *pdev) + unsigned long flags; + + usb_remove_hcd(hcd); ++ hrtimer_cancel(&usb->sof_timer); + spin_lock_irqsave(&usb->lock, flags); + status = cvmx_usb_shutdown(usb); + spin_unlock_irqrestore(&usb->lock, flags); +diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c +--- a/drivers/usb/host/ohci-da8xx.c ++++ b/drivers/usb/host/ohci-da8xx.c +@@ -447,6 +447,7 @@ static int ohci_da8xx_probe(struct platform_device *pdev) + + err_remove_hcd: + usb_remove_hcd(hcd); ++ device_wakeup_disable(hcd->self.controller); + err: + usb_put_hcd(hcd); + return error; +@@ -457,6 +458,7 @@ static void ohci_da8xx_remove(struct platform_device *pdev) + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + usb_remove_hcd(hcd); ++ device_wakeup_disable(hcd->self.controller); + usb_put_hcd(hcd); + } + +diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c +--- a/drivers/usb/host/ohci-s3c2410.c ++++ b/drivers/usb/host/ohci-s3c2410.c +@@ -335,6 +335,7 @@ ohci_hcd_s3c2410_remove(struct platform_device *dev) + struct usb_hcd *hcd = platform_get_drvdata(dev); + + usb_remove_hcd(hcd); ++ device_wakeup_disable(hcd->self.controller); + s3c2410_stop_hc(dev); + usb_put_hcd(hcd); + } +diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c +--- a/drivers/usb/host/ohci-spear.c ++++ b/drivers/usb/host/ohci-spear.c +@@ -103,6 +103,7 @@ static void spear_ohci_hcd_drv_remove(struct platform_device *pdev) + struct spear_ohci *sohci_p = to_spear_ohci(hcd); + + usb_remove_hcd(hcd); ++ device_wakeup_disable(hcd->self.controller); + clk_disable_unprepare(sohci_p->clk); + + usb_put_hcd(hcd); +diff --git a/drivers/usb/host/ohci-st.c b/drivers/usb/host/ohci-st.c +--- a/drivers/usb/host/ohci-st.c ++++ b/drivers/usb/host/ohci-st.c +@@ -235,6 +235,7 @@ static void st_ohci_platform_remove(struct platform_device *dev) + int clk; + + usb_remove_hcd(hcd); ++ device_wakeup_disable(hcd->self.controller); + + if (pdata->power_off) + pdata->power_off(dev); +diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c +--- a/drivers/usb/serial/bus.c ++++ b/drivers/usb/serial/bus.c +@@ -165,7 +165,11 @@ int usb_serial_bus_register(struct usb_serial_driver *driver) + + void usb_serial_bus_deregister(struct usb_serial_driver *driver) + { +- free_dynids(driver); + driver_unregister(&driver->driver); ++ free_dynids(driver); + } + ++void usb_serial_bus_remove_new_id(struct usb_serial_driver *driver) ++{ ++ driver_remove_file(&driver->driver, &driver_attr_new_id); ++} +diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c +--- a/drivers/usb/serial/cp210x.c ++++ b/drivers/usb/serial/cp210x.c +@@ -223,6 +223,7 @@ static const struct usb_device_id id_table[] = { + { USB_DEVICE(0x19CF, 0x3000) }, /* Parrot NMEA GPS Flight Recorder */ + { USB_DEVICE(0x1ADB, 0x0001) }, /* Schweitzer Engineering C662 Cable */ + { USB_DEVICE(0x1B1C, 0x1C00) }, /* Corsair USB Dongle */ ++ { USB_DEVICE(0x1B1C, 0x1C02) }, /* Corsair AX1500i Power Supply */ + { USB_DEVICE(0x1B93, 0x1013) }, /* Phoenix Contact UPS Device */ + { USB_DEVICE(0x1BA4, 0x0002) }, /* Silicon Labs 358x factory default */ + { USB_DEVICE(0x1BE3, 0x07A6) }, /* WAGO 750-923 USB Service Cable */ +diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c +--- a/drivers/usb/serial/generic.c ++++ b/drivers/usb/serial/generic.c +@@ -266,6 +266,7 @@ EXPORT_SYMBOL_GPL(usb_serial_generic_chars_in_buffer); + void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout) + { + struct usb_serial_port *port = tty->driver_data; ++ struct tty_port *tport = &port->port; + unsigned int bps; + unsigned long period; + unsigned long expire; +@@ -285,7 +286,14 @@ void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout) + __func__, jiffies_to_msecs(timeout), + jiffies_to_msecs(period)); + expire = jiffies + timeout; +- while (!port->serial->type->tx_empty(port)) { ++ for (;;) { ++ mutex_lock(&tport->mutex); ++ if (tty_io_error(tty) || port->serial->type->tx_empty(port)) { ++ mutex_unlock(&tport->mutex); ++ break; ++ } ++ mutex_unlock(&tport->mutex); ++ + schedule_timeout_interruptible(period); + if (signal_pending(current)) + break; +diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c +--- a/drivers/usb/serial/option.c ++++ b/drivers/usb/serial/option.c +@@ -260,6 +260,7 @@ static void option_instat_callback(struct urb *urb); + #define QUECTEL_PRODUCT_EM060K_12a 0x012a + #define QUECTEL_PRODUCT_EM060K_12b 0x012b + #define QUECTEL_PRODUCT_EM060K_12c 0x012c ++#define QUECTEL_PRODUCT_RG660QB 0x013d + #define QUECTEL_PRODUCT_EG91 0x0191 + #define QUECTEL_PRODUCT_EG95 0x0195 + #define QUECTEL_PRODUCT_BG96 0x0296 +@@ -280,6 +281,7 @@ static void option_instat_callback(struct urb *urb); + #define QUECTEL_PRODUCT_EC200U 0x0901 + #define QUECTEL_PRODUCT_EG912Y 0x6001 + #define QUECTEL_PRODUCT_EC200S_CN 0x6002 ++#define QUECTEL_PRODUCT_EG060W 0x6004 + #define QUECTEL_PRODUCT_EC200A 0x6005 + #define QUECTEL_PRODUCT_EG916Q 0x6007 + #define QUECTEL_PRODUCT_EM061K_LWW 0x6008 +@@ -1268,12 +1270,15 @@ static const struct usb_device_id option_ids[] = { + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200A, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200U, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200S_CN, 0xff, 0, 0) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG060W, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200T, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG912Y, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG916Q, 0xff, 0x00, 0x00) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500K, 0xff, 0x00, 0x00) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG650V, 0xff, 0xff, 0x30) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG650V, 0xff, 0, 0) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG660QB, 0xff, 0xff, 0x30) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG660QB, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0xff, 0x30) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0xff, 0x40) }, +@@ -2168,6 +2173,8 @@ static const struct usb_device_id option_ids[] = { + { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9003, 0xff) }, /* Simcom SIM7500/SIM7600 MBIM mode */ + { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9011, 0xff), /* Simcom SIM7500/SIM7600 RNDIS mode */ + .driver_info = RSVD(7) }, ++ { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x902b, 0xff), ++ .driver_info = RSVD(4) }, + { USB_DEVICE(0x1e0e, 0x9071), /* Simcom SIM8230 RMNET mode */ + .driver_info = RSVD(3) | RSVD(4) }, + { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9078, 0xff), /* Simcom SIM8230 ECM mode */ +@@ -2429,6 +2436,13 @@ static const struct usb_device_id option_ids[] = { + .driver_info = RSVD(5) }, + { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe167, 0xff), /* Foxconn T99W640 MBIM */ + .driver_info = RSVD(3) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x01) }, /* Compal EXC-T1 */ ++ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x02) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x03) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x04) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x30) }, /* Compal EXM-G1x */ ++ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x40) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x60) }, + { USB_DEVICE(0x1508, 0x1001), /* Fibocom NL668 (IOT version) */ + .driver_info = RSVD(4) | RSVD(5) | RSVD(6) }, + { USB_DEVICE(0x1782, 0x4d10) }, /* Fibocom L610 (AT mode) */ +diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c +--- a/drivers/usb/serial/quatech2.c ++++ b/drivers/usb/serial/quatech2.c +@@ -52,6 +52,7 @@ + #define QT2_XMIT_FLUSH 0x05 /* no following info */ + #define QT2_CONTROL_ESCAPE 0xff /* pass through previous 2 control bytes */ + ++#define MIN_BAUD_RATE 50 + #define MAX_BAUD_RATE 921600 + #define DEFAULT_BAUD_RATE 9600 + +@@ -156,7 +157,7 @@ static inline int calc_baud_divisor(int baudrate) + + static inline int qt2_set_port_config(struct usb_device *dev, + unsigned char port_number, +- u16 baudrate, u16 lcr) ++ speed_t baudrate, u16 lcr) + { + int divisor = calc_baud_divisor(baudrate); + u16 index = ((u16) (lcr << 8) | (u16) (port_number)); +@@ -257,9 +258,9 @@ static void qt2_set_termios(struct tty_struct *tty, + struct usb_device *dev = port->serial->dev; + struct qt2_port_private *port_priv; + struct ktermios *termios = &tty->termios; +- u16 baud; + unsigned int cflag = termios->c_cflag; + u16 new_lcr = 0; ++ speed_t baud; + int status; + + port_priv = usb_get_serial_port_data(port); +@@ -277,6 +278,18 @@ static void qt2_set_termios(struct tty_struct *tty, + if (!baud) + baud = 9600; + ++ if (baud < MIN_BAUD_RATE || baud > MAX_BAUD_RATE) { ++ if (old_termios) ++ baud = tty_termios_baud_rate(old_termios); ++ else ++ baud = clamp(baud, MIN_BAUD_RATE, MAX_BAUD_RATE); ++ ++ tty_encode_baud_rate(tty, baud, baud); ++ ++ if (!baud) ++ baud = 9600; ++ } ++ + status = qt2_set_port_config(dev, port_priv->device_port, baud, + new_lcr); + if (status < 0) +@@ -373,7 +386,7 @@ static int qt2_open(struct tty_struct *tty, struct usb_serial_port *port) + port_priv->device_port = (u8) device_port; + + if (tty) +- qt2_set_termios(tty, port, &tty->termios); ++ qt2_set_termios(tty, port, NULL); + + return 0; + +diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c +--- a/drivers/usb/serial/ssu100.c ++++ b/drivers/usb/serial/ssu100.c +@@ -32,6 +32,7 @@ + + #define SERIAL_EVEN_PARITY (UART_LCR_PARITY | UART_LCR_EPAR) + ++#define MIN_BAUD_RATE 50 + #define MAX_BAUD_RATE 460800 + + #define ATC_DISABLED 0x00 +@@ -217,9 +218,10 @@ static void ssu100_set_termios(struct tty_struct *tty, + { + struct usb_device *dev = port->serial->dev; + struct ktermios *termios = &tty->termios; +- u16 baud, divisor, remainder; ++ speed_t baud, remainder; + unsigned int cflag = termios->c_cflag; + u16 urb_value = 0; /* will hold the new flags */ ++ u16 divisor; + int result; + + if (cflag & PARENB) { +@@ -235,6 +237,18 @@ static void ssu100_set_termios(struct tty_struct *tty, + if (!baud) + baud = 9600; + ++ if (baud < MIN_BAUD_RATE || baud > MAX_BAUD_RATE) { ++ if (old_termios) ++ baud = tty_termios_baud_rate(old_termios); ++ else ++ baud = clamp(baud, MIN_BAUD_RATE, MAX_BAUD_RATE); ++ ++ tty_encode_baud_rate(tty, baud, baud); ++ ++ if (!baud) ++ baud = 9600; ++ } ++ + dev_dbg(&port->dev, "%s - got baud = %d\n", __func__, baud); + + +@@ -310,7 +324,7 @@ static int ssu100_open(struct tty_struct *tty, struct usb_serial_port *port) + dev_dbg(&port->dev, "%s - set uart failed\n", __func__); + + if (tty) +- ssu100_set_termios(tty, port, &tty->termios); ++ ssu100_set_termios(tty, port, NULL); + + return usb_serial_generic_open(tty, port); + } +diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c +--- a/drivers/usb/serial/usb-serial.c ++++ b/drivers/usb/serial/usb-serial.c +@@ -400,17 +400,13 @@ static unsigned int serial_chars_in_buffer(struct tty_struct *tty) + static void serial_wait_until_sent(struct tty_struct *tty, int timeout) + { + struct usb_serial_port *port = tty->driver_data; +- struct usb_serial *serial = port->serial; + + dev_dbg(&port->dev, "%s\n", __func__); + + if (!port->serial->type->wait_until_sent) + return; + +- mutex_lock(&serial->disc_mutex); +- if (!serial->disconnected) +- port->serial->type->wait_until_sent(tty, timeout); +- mutex_unlock(&serial->disc_mutex); ++ port->serial->type->wait_until_sent(tty, timeout); + } + + static void serial_throttle(struct tty_struct *tty) +@@ -438,8 +434,13 @@ static int serial_get_serial(struct tty_struct *tty, struct serial_struct *ss) + struct usb_serial_port *port = tty->driver_data; + struct tty_port *tport = &port->port; + unsigned int close_delay, closing_wait; ++ int ret = 0; + + mutex_lock(&tport->mutex); ++ if (tty_io_error(tty)) { ++ ret = -EIO; ++ goto out_unlock; ++ } + + close_delay = jiffies_to_msecs(tport->close_delay) / 10; + closing_wait = tport->closing_wait; +@@ -452,10 +453,10 @@ static int serial_get_serial(struct tty_struct *tty, struct serial_struct *ss) + + if (port->serial->type->get_serial) + port->serial->type->get_serial(tty, ss); +- ++out_unlock: + mutex_unlock(&tport->mutex); + +- return 0; ++ return ret; + } + + static int serial_set_serial(struct tty_struct *tty, struct serial_struct *ss) +@@ -471,6 +472,10 @@ static int serial_set_serial(struct tty_struct *tty, struct serial_struct *ss) + closing_wait = msecs_to_jiffies(closing_wait * 10); + + mutex_lock(&tport->mutex); ++ if (tty_io_error(tty)) { ++ ret = -EIO; ++ goto out_unlock; ++ } + + if (!capable(CAP_SYS_ADMIN)) { + if (close_delay != tport->close_delay || +@@ -498,6 +503,7 @@ static int serial_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) + { + struct usb_serial_port *port = tty->driver_data; ++ struct tty_port *tport = &port->port; + int retval = -ENOIOCTLCMD; + + dev_dbg(&port->dev, "%s - cmd 0x%04x\n", __func__, cmd); +@@ -508,8 +514,21 @@ static int serial_ioctl(struct tty_struct *tty, + retval = port->serial->type->tiocmiwait(tty, arg); + break; + default: +- if (port->serial->type->ioctl) ++ if (!port->serial->type->ioctl) ++ break; ++ ++ if (cmd == TIOCSRS485) ++ down_write(&tty->termios_rwsem); ++ ++ mutex_lock(&tport->mutex); ++ if (tty_io_error(tty)) ++ retval = -EIO; ++ else + retval = port->serial->type->ioctl(tty, cmd, arg); ++ mutex_unlock(&tport->mutex); ++ ++ if (cmd == TIOCSRS485) ++ up_write(&tty->termios_rwsem); + } + + return retval; +@@ -519,25 +538,40 @@ static void serial_set_termios(struct tty_struct *tty, + const struct ktermios *old) + { + struct usb_serial_port *port = tty->driver_data; ++ struct tty_port *tport = &port->port; + + dev_dbg(&port->dev, "%s\n", __func__); + +- if (port->serial->type->set_termios) +- port->serial->type->set_termios(tty, port, old); +- else ++ if (!port->serial->type->set_termios) { + tty_termios_copy_hw(&tty->termios, old); ++ return; ++ } ++ ++ mutex_lock(&tport->mutex); ++ if (!tty_io_error(tty)) ++ port->serial->type->set_termios(tty, port, old); ++ mutex_unlock(&tport->mutex); + } + + static int serial_break(struct tty_struct *tty, int break_state) + { + struct usb_serial_port *port = tty->driver_data; ++ struct tty_port *tport = &port->port; ++ int ret; + + dev_dbg(&port->dev, "%s\n", __func__); + +- if (port->serial->type->break_ctl) +- return port->serial->type->break_ctl(tty, break_state); ++ if (!port->serial->type->break_ctl) ++ return -ENOTTY; + +- return -ENOTTY; ++ mutex_lock(&tport->mutex); ++ if (tty_io_error(tty)) ++ ret = -EIO; ++ else ++ ret = port->serial->type->break_ctl(tty, break_state); ++ mutex_unlock(&tport->mutex); ++ ++ return ret; + } + + static int serial_proc_show(struct seq_file *m, void *v) +@@ -578,24 +612,44 @@ static int serial_proc_show(struct seq_file *m, void *v) + static int serial_tiocmget(struct tty_struct *tty) + { + struct usb_serial_port *port = tty->driver_data; ++ struct tty_port *tport = &port->port; ++ int ret; + + dev_dbg(&port->dev, "%s\n", __func__); + +- if (port->serial->type->tiocmget) +- return port->serial->type->tiocmget(tty); +- return -ENOTTY; ++ if (!port->serial->type->tiocmget) ++ return -ENOTTY; ++ ++ mutex_lock(&tport->mutex); ++ if (tty_io_error(tty)) ++ ret = -EIO; ++ else ++ ret = port->serial->type->tiocmget(tty); ++ mutex_unlock(&tport->mutex); ++ ++ return ret; + } + + static int serial_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) + { + struct usb_serial_port *port = tty->driver_data; ++ struct tty_port *tport = &port->port; ++ int ret; + + dev_dbg(&port->dev, "%s\n", __func__); + +- if (port->serial->type->tiocmset) +- return port->serial->type->tiocmset(tty, set, clear); +- return -ENOTTY; ++ if (!port->serial->type->tiocmset) ++ return -ENOTTY; ++ ++ mutex_lock(&tport->mutex); ++ if (tty_io_error(tty)) ++ ret = -EIO; ++ else ++ ret = port->serial->type->tiocmset(tty, set, clear); ++ mutex_unlock(&tport->mutex); ++ ++ return ret; + } + + static int serial_get_icount(struct tty_struct *tty, +@@ -1191,11 +1245,16 @@ static void usb_serial_disconnect(struct usb_interface *interface) + usb_serial_port_poison_urbs(port); + wake_up_interruptible(&port->port.delta_msr_wait); + cancel_work_sync(&port->work); ++ } ++ ++ if (serial->type->disconnect) ++ serial->type->disconnect(serial); ++ ++ for (i = 0; i < serial->num_ports; ++i) { ++ port = serial->port[i]; + if (device_is_registered(&port->dev)) + device_del(&port->dev); + } +- if (serial->type->disconnect) +- serial->type->disconnect(serial); + + release_sibling(serial, interface); + +@@ -1464,7 +1523,7 @@ int __usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers + { + int rc; + struct usb_driver *udriver; +- struct usb_serial_driver * const *sd; ++ struct usb_serial_driver * const *sd, * const *s; + + /* + * udriver must be registered before any of the serial drivers, +@@ -1517,9 +1576,11 @@ int __usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers + return 0; + + err_deregister_drivers: ++ for (s = serial_drivers; s < sd; ++s) ++ usb_serial_bus_remove_new_id(*s); ++ usb_deregister(udriver); + while (sd-- > serial_drivers) + usb_serial_deregister(*sd); +- usb_deregister(udriver); + err_free_driver: + kfree(udriver); + return rc; +@@ -1537,10 +1598,23 @@ EXPORT_SYMBOL_GPL(__usb_serial_register_drivers); + void usb_serial_deregister_drivers(struct usb_serial_driver *const serial_drivers[]) + { + struct usb_driver *udriver = (*serial_drivers)->usb_driver; ++ struct usb_serial_driver * const *sd; ++ ++ /* ++ * udriver must be deregistered before the serial drivers so that ++ * I/O is stopped before unbinding the ports. ++ * ++ * Remove the new_id attributes to prevent ids from being added and ++ * triggering a probe of udriver after it has been deregistered. ++ */ ++ for (sd = serial_drivers; *sd; ++sd) ++ usb_serial_bus_remove_new_id(*sd); + +- for (; *serial_drivers; ++serial_drivers) +- usb_serial_deregister(*serial_drivers); + usb_deregister(udriver); ++ ++ for (sd = serial_drivers; *sd; ++sd) ++ usb_serial_deregister(*sd); ++ + kfree(udriver); + } + EXPORT_SYMBOL_GPL(usb_serial_deregister_drivers); +diff --git a/drivers/usb/serial/xr_serial.c b/drivers/usb/serial/xr_serial.c +--- a/drivers/usb/serial/xr_serial.c ++++ b/drivers/usb/serial/xr_serial.c +@@ -850,12 +850,9 @@ static int xr_get_rs485_config(struct tty_struct *tty, + struct usb_serial_port *port = tty->driver_data; + struct xr_data *data = usb_get_serial_port_data(port); + +- down_read(&tty->termios_rwsem); +- if (copy_to_user(argp, &data->rs485, sizeof(data->rs485))) { +- up_read(&tty->termios_rwsem); ++ /* core holds port mutex */ ++ if (copy_to_user(argp, &data->rs485, sizeof(data->rs485))) + return -EFAULT; +- } +- up_read(&tty->termios_rwsem); + + return 0; + } +@@ -871,10 +868,9 @@ static int xr_set_rs485_config(struct tty_struct *tty, + return -EFAULT; + xr_sanitize_serial_rs485(&rs485); + +- down_write(&tty->termios_rwsem); ++ /* core holds termios rwsem and port mutex */ + data->rs485 = rs485; + xr_set_flow_mode(tty, port, NULL); +- up_write(&tty->termios_rwsem); + + if (copy_to_user(argp, &rs485, sizeof(rs485))) + return -EFAULT; +diff --git a/drivers/usb/storage/sierra_ms.c b/drivers/usb/storage/sierra_ms.c +--- a/drivers/usb/storage/sierra_ms.c ++++ b/drivers/usb/storage/sierra_ms.c +@@ -77,6 +77,13 @@ static int sierra_get_swoc_info(struct usb_device *udev, + sizeof(struct swoc_info), /* __u16 size */ + USB_CTRL_SET_TIMEOUT); /* int timeout */ + ++ /* ++ * A short IN transfer leaves the tail of swocInfo uninitialized; ++ * only a full transfer is valid. ++ */ ++ if (result != sizeof(struct swoc_info)) ++ return -EIO; ++ + swocInfo->LinuxSKU = le16_to_cpu(swocInfo->LinuxSKU); + swocInfo->LinuxVer = le16_to_cpu(swocInfo->LinuxVer); + return result; +diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c +--- a/drivers/usb/typec/anx7411.c ++++ b/drivers/usb/typec/anx7411.c +@@ -1566,8 +1566,11 @@ static void anx7411_i2c_remove(struct i2c_client *client) + anx7411_partner_unregister_altmode(plat); + anx7411_unregister_partner(plat); + +- if (plat->workqueue) ++ if (plat->workqueue) { ++ disable_irq(plat->intp_irq); ++ cancel_work_sync(&plat->work); + destroy_workqueue(plat->workqueue); ++ } + + i2c_unregister_device(plat->spi_client); + +diff --git a/drivers/usb/typec/port-mapper.c b/drivers/usb/typec/port-mapper.c +--- a/drivers/usb/typec/port-mapper.c ++++ b/drivers/usb/typec/port-mapper.c +@@ -42,6 +42,23 @@ static int usb4_port_compare(struct device *dev, void *fwnode) + return usb4_usb3_port_match(dev, fwnode); + } + ++static bool typec_has_usb4_host_interface(const struct fwnode_handle *fwnode) ++{ ++ if (!IS_REACHABLE(CONFIG_USB4)) ++ return false; ++ ++ struct fwnode_handle *nhi_fwnode __free(fwnode_handle) = ++ fwnode_find_reference(fwnode, "usb4-host-interface", 0); ++ ++ /* ++ * The USB4 port can only appear if the host interface is enabled in ++ * the firmware and has been enumerated as a device. The latter is ++ * the same check as in usb_acpi_add_usb4_devlink(). ++ */ ++ return !IS_ERR(nhi_fwnode) && fwnode_device_is_available(nhi_fwnode) && ++ nhi_fwnode->dev; ++} ++ + static int typec_port_compare(struct device *dev, void *fwnode) + { + return device_match_fwnode(dev, fwnode); +@@ -64,11 +81,11 @@ static int typec_port_match(struct device *dev, void *data) + adev_fwnode); + + /* +- * If dev is USB 3.x port, it may have reference to the ++ * If dev is USB 3.x port, it may have reference to an available + * USB4 host interface in which case we can also link the + * Type-C port with the USB4 port. + */ +- if (fwnode_property_present(adev_fwnode, "usb4-host-interface")) ++ if (typec_has_usb4_host_interface(adev_fwnode)) + component_match_add(&arg->port->dev, &arg->match, + usb4_port_compare, adev_fwnode); + } +diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c +--- a/drivers/usb/typec/tcpm/tcpm.c ++++ b/drivers/usb/typec/tcpm/tcpm.c +@@ -1756,7 +1756,8 @@ static void mod_enable_frs_delayed_work(struct tcpm_port *port, unsigned int del + static void mod_vdm_discovery_cancel_delayed_work(struct tcpm_port *port) + { + hrtimer_cancel(&port->vdm_discovery_timer); +- kthread_cancel_work_sync(&port->vdm_discovery_work); ++ if (port->wq) ++ kthread_cancel_work_sync(&port->vdm_discovery_work); + } + + static void mod_vdm_discovery_delayed_work(struct tcpm_port *port, unsigned int delay_ms) +@@ -7113,7 +7114,7 @@ static void tcpm_pd_event_handler(struct kthread_work *work) + port->upcoming_state = FR_SWAP_SEND; + ret = tcpm_ams_start(port, FAST_ROLE_SWAP); + if (ret == -EAGAIN) +- port->upcoming_state = INVALID_STATE; ++ tcpm_set_state(port, ERROR_RECOVERY, 0); + } else { + tcpm_log(port, "Discarding FRS_SIGNAL! Not in sink ready"); + } +@@ -8977,6 +8978,7 @@ void tcpm_unregister_port(struct tcpm_port *port) + + port->registered = false; + kthread_destroy_worker(port->wq); ++ port->wq = NULL; + + hrtimer_cancel(&port->vdm_discovery_timer); + hrtimer_cancel(&port->enable_frs_timer); +diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c +--- a/drivers/usb/typec/tipd/core.c ++++ b/drivers/usb/typec/tipd/core.c +@@ -343,7 +343,7 @@ static void tps6598x_set_data_role(struct tps6598x *tps, + + static int tps6598x_connect(struct tps6598x *tps, u32 status) + { +- struct typec_partner_desc desc; ++ struct typec_partner_desc desc = { }; + enum typec_pwr_opmode mode; + int ret; + +@@ -354,7 +354,6 @@ static int tps6598x_connect(struct tps6598x *tps, u32 status) + + desc.usb_pd = mode == TYPEC_PWR_MODE_PD; + desc.accessory = TYPEC_ACCESSORY_NONE; /* XXX: handle accessories */ +- desc.identity = NULL; + + if (desc.usb_pd) { + ret = tps6598x_read_partner_identity(tps); +@@ -850,11 +849,10 @@ static void cd321x_update_work(struct work_struct *work) + + /* Set up partner if we were previously disconnected (or changed). */ + if (!tps->partner) { +- struct typec_partner_desc desc; ++ struct typec_partner_desc desc = { }; + + desc.usb_pd = is_pd; + desc.accessory = TYPEC_ACCESSORY_NONE; /* XXX: handle accessories */ +- desc.identity = NULL; + + if (desc.usb_pd) + desc.identity = &st.partner_identity; +@@ -1792,6 +1790,7 @@ static int tps6598x_probe(struct i2c_client *client) + const struct tipd_data *data; + struct tps6598x *tps; + struct fwnode_handle *fwnode; ++ bool patch_loaded = false; + u32 status; + u32 vid = 0; + int ret; +@@ -1847,6 +1846,7 @@ static int tps6598x_probe(struct i2c_client *client) + return ret; + + if (ret == TPS_MODE_PTCH) { ++ patch_loaded = true; + ret = tps->data->init(tps); + if (ret) + return ret; +@@ -1937,7 +1937,8 @@ static int tps6598x_probe(struct i2c_client *client) + tps6598x_write64(tps, TPS_REG_INT_MASK1, 0); + err_reset_controller: + /* Reset PD controller to remove any applied patch */ +- tps->data->reset(tps); ++ if (patch_loaded) ++ tps->data->reset(tps); + + return ret; + } +diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c +--- a/drivers/usb/typec/ucsi/displayport.c ++++ b/drivers/usb/typec/ucsi/displayport.c +@@ -71,11 +71,14 @@ static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo) + if (ret < 0) { + if (ucsi->version > 0x0100) + goto err_unlock; +- cur = 0xff; + } + +- if (cur < UCSI_MAX_ALTMODES) { +- ret = dp->con->port_altmode[cur] == alt ? 0 : -EBUSY; ++ if (cur != 0xff) { ++ if (cur < UCSI_MAX_ALTMODES) ++ ret = dp->con->port_altmode[cur] == alt ? 0 : -EBUSY; ++ else ++ ret = -EINVAL; ++ + goto err_unlock; + } + +diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c +--- a/drivers/usb/typec/ucsi/ucsi.c ++++ b/drivers/usb/typec/ucsi/ucsi.c +@@ -1801,10 +1801,21 @@ static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con) + { + struct fwnode_handle *fwnode; + int i = 1; ++ int ret; ++ u32 port; + +- device_for_each_child_node(con->ucsi->dev, fwnode) +- if (i++ == con->num) +- return fwnode; ++ device_for_each_child_node(con->ucsi->dev, fwnode) { ++ ret = fwnode_property_read_u32(fwnode, "reg", &port); ++ if (ret < 0) { ++ if (i == con->num) ++ return fwnode; ++ } else { ++ if (port == con->num - 1) ++ return fwnode; ++ } ++ ++ i++; ++ } + return NULL; + } + +diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c +--- a/drivers/usb/typec/ucsi/ucsi_acpi.c ++++ b/drivers/usb/typec/ucsi/ucsi_acpi.c +@@ -121,6 +121,33 @@ static const struct ucsi_operations ucsi_acpi_ops = { + .async_control = ucsi_acpi_async_control + }; + ++static int ucsi_acer_read_version(struct ucsi *ucsi, u16 *version) ++{ ++ struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); ++ int ret; ++ ++ ret = ucsi_acpi_read_version(ucsi, version); ++ if (ret) ++ return ret; ++ ++ if (!*version) { ++ dev_warn(ua->dev, "UCSI version is zero, assuming 1.2\n"); ++ *version = UCSI_VERSION_1_2; ++ } ++ ++ return 0; ++} ++ ++static const struct ucsi_operations ucsi_acer_ops = { ++ .read_version = ucsi_acer_read_version, ++ .read_cci = ucsi_acpi_read_cci, ++ .poll_cci = ucsi_acpi_poll_cci, ++ .read_message_in = ucsi_acpi_read_message_in, ++ .write_message_out = ucsi_acpi_write_message_out, ++ .sync_control = ucsi_sync_control_common, ++ .async_control = ucsi_acpi_async_control ++}; ++ + static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command, u32 *cci, + void *val, size_t len, void *msg_out, + size_t msg_out_size) +@@ -162,6 +189,13 @@ static const struct ucsi_operations ucsi_gram_ops = { + }; + + static const struct dmi_system_id ucsi_acpi_quirks[] = { ++ { ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Nitro ANV15-41"), ++ }, ++ .driver_data = (void *)&ucsi_acer_ops, ++ }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"), +diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c +--- a/drivers/usb/typec/ucsi/ucsi_glink.c ++++ b/drivers/usb/typec/ucsi/ucsi_glink.c +@@ -362,6 +362,10 @@ static void pmic_glink_ucsi_destroy(void *data) + { + struct pmic_glink_ucsi *ucsi = data; + ++ /* Drain the work items before ucsi_destroy() frees the ucsi instance */ ++ cancel_work_sync(&ucsi->notify_work); ++ cancel_work_sync(&ucsi->register_work); ++ + /* Protect to make sure we're not in a middle of a transaction from a glink callback */ + mutex_lock(&ucsi->lock); + ucsi_destroy(ucsi->ucsi); +@@ -467,8 +471,15 @@ static void pmic_glink_ucsi_remove(struct auxiliary_device *adev) + { + struct pmic_glink_ucsi *ucsi = dev_get_drvdata(&adev->dev); + +- /* Unregister first to stop having read & writes */ +- ucsi_unregister(ucsi->ucsi); ++ /* Callbacks can queue work until devres releases the client */ ++ disable_work_sync(&ucsi->notify_work); ++ disable_work_sync(&ucsi->register_work); ++ ++ /* register_work may have unregistered the instance already */ ++ if (ucsi->ucsi_registered) { ++ ucsi->ucsi_registered = false; ++ ucsi_unregister(ucsi->ucsi); ++ } + } + + static const struct auxiliary_device_id pmic_glink_ucsi_id_table[] = { +diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h +--- a/include/linux/usb/serial.h ++++ b/include/linux/usb/serial.h +@@ -381,6 +381,7 @@ void usb_serial_handle_dcd_change(struct usb_serial_port *usb_port, + + int usb_serial_bus_register(struct usb_serial_driver *device); + void usb_serial_bus_deregister(struct usb_serial_driver *device); ++void usb_serial_bus_remove_new_id(struct usb_serial_driver *driver); + + extern const struct bus_type usb_serial_bus_type; + extern struct tty_driver *usb_serial_tty_driver; diff --git a/pkgbuilds/linux-omarchy/0607-usb-fixes.patch.sig b/pkgbuilds/linux-omarchy/0607-usb-fixes.patch.sig new file mode 100644 index 0000000..864678b Binary files /dev/null and b/pkgbuilds/linux-omarchy/0607-usb-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0610-typec-cable-altmode-check.patch b/pkgbuilds/linux-omarchy/0610-typec-cable-altmode-check.patch deleted file mode 100644 index 3db7549..0000000 --- a/pkgbuilds/linux-omarchy/0610-typec-cable-altmode-check.patch +++ /dev/null @@ -1,132 +0,0 @@ -diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c -index 263a89c5f324..5ee33a69b9cf 100644 ---- a/drivers/usb/typec/altmodes/displayport.c -+++ b/drivers/usb/typec/altmodes/displayport.c -@@ -790,7 +790,6 @@ int dp_altmode_probe(struct typec_altmode *alt) - dp->alt = alt; - - alt->desc = "DisplayPort"; -- typec_altmode_set_ops(alt, &dp_altmode_ops); - - if (plug) { - plug->desc = "Displayport"; -@@ -811,6 +810,10 @@ int dp_altmode_probe(struct typec_altmode *alt) - if (plug) - typec_altmode_set_drvdata(plug, dp); - -+ if ((alt->vdo & DP_CAP_RECEPTACLE) && typec_cable_altmode_unsupported(alt)) -+ return 0; -+ -+ typec_altmode_set_ops(alt, &dp_altmode_ops); - if (!alt->mode_selection) { - dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER; - schedule_work(&dp->work); -diff --git a/drivers/usb/typec/altmodes/thunderbolt.c b/drivers/usb/typec/altmodes/thunderbolt.c -index 32250b94262a..2eccdddf1b1f 100644 ---- a/drivers/usb/typec/altmodes/thunderbolt.c -+++ b/drivers/usb/typec/altmodes/thunderbolt.c -@@ -284,6 +284,10 @@ static int tbt_altmode_probe(struct typec_altmode *alt) - - alt->desc = "Thunderbolt3"; - typec_altmode_set_drvdata(alt, tbt); -+ -+ if (typec_cable_altmode_unsupported(alt)) -+ return 0; -+ - typec_altmode_set_ops(alt, &tbt_altmode_ops); - - if (!alt->mode_selection && tbt_ready(alt)) { -diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c -index 0595e8cb83aa..54c7810b563b 100644 ---- a/drivers/usb/typec/class.c -+++ b/drivers/usb/typec/class.c -@@ -1429,6 +1429,77 @@ int typec_cable_is_active(struct typec_cable *cable) - } - EXPORT_SYMBOL_GPL(typec_cable_is_active); - -+enum typec_cable_altmode_support { -+ CABLE_SUPPORT_UNKNOWN, -+ CABLE_SUPPORTED, -+ CABLE_NOT_SUPPORTED, -+}; -+ -+static enum typec_cable_altmode_support -+typec_cable_check_altmode_support(struct typec_cable *cable, -+ struct typec_altmode *alt) -+{ -+ struct typec_altmode *plug; -+ u32 speed; -+ -+ /* -+ * Check if the cable has an e-marker, supports modal operation, and the -+ * SOP' altmode nodes are created. -+ */ -+ plug = typec_altmode_get_plug(alt, TYPEC_PLUG_SOP_P); -+ if (plug) { -+ typec_altmode_put_plug(plug); -+ return CABLE_SUPPORTED; -+ } -+ -+ /* The identity is not specified */ -+ if (!cable->identity) -+ return CABLE_SUPPORT_UNKNOWN; -+ -+ /* Non-e-marked cable */ -+ if (!cable->identity->id_header) -+ return CABLE_NOT_SUPPORTED; -+ -+ switch (PD_IDH_PTYPE(cable->identity->id_header)) { -+ case IDH_PTYPE_PCABLE: -+ speed = VDO_TYPEC_CABLE_SPEED(cable->identity->vdo[0]); -+ if (speed == CABLE_USB2_ONLY) -+ return CABLE_NOT_SUPPORTED; -+ return CABLE_SUPPORTED; -+ case IDH_PTYPE_ACABLE: -+ /* -+ * Active cables must establish an SOP' communication -+ * node. Since that check failed at the beginning of -+ * this function, this active cable does not support -+ * this specific altmode. -+ */ -+ return CABLE_NOT_SUPPORTED; -+ } -+ -+ return CABLE_SUPPORT_UNKNOWN; -+} -+ -+/** -+ * typec_cable_altmode_unsupported - Check if a cable restricts altmode -+ * @alt: The Alternate Mode to evaluate -+ * -+ * Returns true if the connected cable is incapable of handling the altmode. -+ */ -+bool typec_cable_altmode_unsupported(struct typec_altmode *alt) -+{ -+ enum typec_cable_altmode_support support = CABLE_SUPPORT_UNKNOWN; -+ struct typec_cable *cable; -+ -+ cable = typec_cable_get(typec_altmode2port(alt)); -+ if (cable) { -+ support = typec_cable_check_altmode_support(cable, alt); -+ typec_cable_put(cable); -+ } -+ -+ return support == CABLE_NOT_SUPPORTED; -+} -+EXPORT_SYMBOL_GPL(typec_cable_altmode_unsupported); -+ - /** - * typec_cable_set_identity - Report result from Discover Identity command - * @cable: The cable updated identity values -diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h -index d61ec38216fa..10a783b738ef 100644 ---- a/include/linux/usb/typec.h -+++ b/include/linux/usb/typec.h -@@ -337,6 +337,7 @@ void typec_unregister_cable(struct typec_cable *cable); - struct typec_cable *typec_cable_get(struct typec_port *port); - void typec_cable_put(struct typec_cable *cable); - int typec_cable_is_active(struct typec_cable *cable); -+bool typec_cable_altmode_unsupported(struct typec_altmode *alt); - - struct typec_plug *typec_register_plug(struct typec_cable *cable, - struct typec_plug_desc *desc); diff --git a/pkgbuilds/linux-omarchy/0610-typec-cable-altmode-check.patch.sig b/pkgbuilds/linux-omarchy/0610-typec-cable-altmode-check.patch.sig deleted file mode 100644 index 939524b..0000000 Binary files a/pkgbuilds/linux-omarchy/0610-typec-cable-altmode-check.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/0660-btusb-mediatek-mt7922-13d3-3625.patch b/pkgbuilds/linux-omarchy/0660-btusb-mediatek-mt7922-13d3-3625.patch index 8789452..3b51649 100644 --- a/pkgbuilds/linux-omarchy/0660-btusb-mediatek-mt7922-13d3-3625.patch +++ b/pkgbuilds/linux-omarchy/0660-btusb-mediatek-mt7922-13d3-3625.patch @@ -1,7 +1,7 @@ diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c -@@ -801,6 +801,8 @@ static const struct usb_device_id quirks_table[] = { +@@ -819,6 +819,8 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3613), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH }, diff --git a/pkgbuilds/linux-omarchy/0660-btusb-mediatek-mt7922-13d3-3625.patch.sig b/pkgbuilds/linux-omarchy/0660-btusb-mediatek-mt7922-13d3-3625.patch.sig index ade8cc2..f497ab2 100644 Binary files a/pkgbuilds/linux-omarchy/0660-btusb-mediatek-mt7922-13d3-3625.patch.sig and b/pkgbuilds/linux-omarchy/0660-btusb-mediatek-mt7922-13d3-3625.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0661-rtw89-command-offload-source.patch b/pkgbuilds/linux-omarchy/0661-rtw89-command-offload-source.patch deleted file mode 100644 index 539fe9a..0000000 --- a/pkgbuilds/linux-omarchy/0661-rtw89-command-offload-source.patch +++ /dev/null @@ -1,32 +0,0 @@ -diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c ---- a/drivers/net/wireless/realtek/rtw89/fw.c -+++ b/drivers/net/wireless/realtek/rtw89/fw.c -@@ -11780,7 +11780,7 @@ static void rtw89_fw_cmd_ofld_write_rf(struct rtw89_dev *rtwdev, - static void rtw89_fw_cmd_ofld_udelay(struct rtw89_dev *rtwdev, u32 us) - { - struct rtw89_fw_cmd_ofld_arg cmd = { -- .src = RTW89_FW_CMD_OFLD_SRC_OTHER, -+ .src = RTW89_FW_CMD_OFLD_SRC_BB, - .type = RTW89_FW_CMD_OFLD_DELAY, - .value = us, - }; -@@ -11794,7 +11794,7 @@ static void rtw89_fw_cmd_ofld_udelay(struct rtw89_dev *rtwdev, u32 us) - static void rtw89_fw_cmd_ofld_mdelay(struct rtw89_dev *rtwdev, u32 ms) - { - struct rtw89_fw_cmd_ofld_arg cmd = { -- .src = RTW89_FW_CMD_OFLD_SRC_OTHER, -+ .src = RTW89_FW_CMD_OFLD_SRC_BB, - .type = RTW89_FW_CMD_OFLD_DELAY, - .value = ms * 1000, - }; -diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h ---- a/drivers/net/wireless/realtek/rtw89/fw.h -+++ b/drivers/net/wireless/realtek/rtw89/fw.h -@@ -3142,7 +3142,6 @@ enum rtw89_fw_cmd_ofld_arg_src { - RTW89_FW_CMD_OFLD_SRC_RF, - RTW89_FW_CMD_OFLD_SRC_MAC, - RTW89_FW_CMD_OFLD_SRC_RF_DDIE, -- RTW89_FW_CMD_OFLD_SRC_OTHER, - }; - - enum rtw89_fw_cmd_ofld_arg_type { diff --git a/pkgbuilds/linux-omarchy/0661-rtw89-command-offload-source.patch.sig b/pkgbuilds/linux-omarchy/0661-rtw89-command-offload-source.patch.sig deleted file mode 100644 index 0459681..0000000 Binary files a/pkgbuilds/linux-omarchy/0661-rtw89-command-offload-source.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/0663-btintel-pcie-serialize-reset-type.patch b/pkgbuilds/linux-omarchy/0663-btintel-pcie-serialize-reset-type.patch new file mode 100644 index 0000000..2464f58 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0663-btintel-pcie-serialize-reset-type.patch @@ -0,0 +1,145 @@ +diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c +--- a/drivers/bluetooth/btintel_pcie.c ++++ b/drivers/bluetooth/btintel_pcie.c +@@ -2499,8 +2499,6 @@ static void btintel_pcie_inc_recovery_count(struct pci_dev *pdev, + } + } + +-static void btintel_pcie_reset(struct hci_dev *hdev); +- + static int btintel_pcie_acpi_reset_method(struct btintel_pcie_data *data) + { + union acpi_object *obj, argv4; +@@ -2693,56 +2691,86 @@ static void btintel_pcie_reset_work(struct work_struct *wk) + pci_unlock_rescan_remove(); + } + +-static void btintel_pcie_reset(struct hci_dev *hdev) ++/* Schedule a device reset of the requested type. ++ * ++ * BTINTEL_PCIE_RECOVERY_IN_PROGRESS serializes all reset requesters ++ * (sysfs reset attribute, hci_cmd_timeout(), hw_error, resume error ++ * path, etc.) so that: ++ * ++ * - dev_data->reset_type is written by exactly one caller (the ++ * thread that wins test_and_set_bit), eliminating the race where ++ * a second hw_error could clobber an already-scheduled reset's ++ * type; ++ * - the write happens AFTER the bit is set, so reset_work observes ++ * it through schedule_work()'s memory ordering; ++ * - losers return without touching reset_type or scheduling the ++ * work, so concurrent triggers are silently coalesced into the ++ * in-flight one (whose recovery will reinitialize the device ++ * regardless of the dropped trigger's variant). ++ * ++ * The bit is cleared only by .remove() / re-probe via fresh devm ++ * allocation, which is the intended one-shot semantics: a reset ++ * tears down and re-probes 'data', so there is no "in-flight" ++ * reset to follow up after device_reprobe() succeeds. ++ */ ++static void btintel_pcie_request_reset(struct btintel_pcie_data *data, ++ enum btintel_pcie_reset_type type) + { +- struct btintel_pcie_data *data; +- +- data = hci_get_drvdata(hdev); +- + if (!test_bit(BTINTEL_PCIE_SETUP_DONE, &data->flags)) + return; + + if (test_and_set_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags)) + return; + ++ data->reset_type = type; ++ + pci_dev_get(data->pdev); + schedule_work(&data->reset_work); + } + ++static void btintel_pcie_hci_reset(struct hci_dev *hdev) ++{ ++ struct btintel_pcie_data *data = hci_get_drvdata(hdev); ++ ++ btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR); ++} ++ + static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code) + { +- struct btintel_pcie_dev_recovery *data; ++ struct btintel_pcie_dev_recovery *rec; + struct btintel_pcie_data *dev_data = hci_get_drvdata(hdev); + struct pci_dev *pdev = dev_data->pdev; ++ enum btintel_pcie_reset_type type; + time64_t retry_window; + ++ if (test_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &dev_data->flags)) ++ return; ++ + btintel_pcie_dump_debug_registers(hdev); + +- data = btintel_pcie_get_recovery(pdev, &hdev->dev); +- if (!data) ++ rec = btintel_pcie_get_recovery(pdev, &hdev->dev); ++ if (!rec) + return; + +- if (code == 0x13) +- dev_data->reset_type = BTINTEL_PCIE_IOSF_PRR_PLDR; +- else +- dev_data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR; ++ type = (code == 0x13) ? BTINTEL_PCIE_IOSF_PRR_PLDR ++ : BTINTEL_PCIE_IOSF_PRR_FLR; + + bt_dev_err(hdev, "Encountered exception err:0x%x triggering: %s", code, +- dev_data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR ? "PLDR" : "FLR"); +- retry_window = ktime_get_boottime_seconds() - data->last_error; ++ type == BTINTEL_PCIE_IOSF_PRR_PLDR ? "PLDR" : "FLR"); ++ retry_window = ktime_get_boottime_seconds() - rec->last_error; + + if (retry_window < BTINTEL_PCIE_RESET_WINDOW_SECS && +- data->count >= BTINTEL_PCIE_FLR_MAX_RETRY) { ++ rec->count >= BTINTEL_PCIE_FLR_MAX_RETRY) { + bt_dev_err(hdev, "Exhausted maximum: %d recovery attempts: %d", +- BTINTEL_PCIE_FLR_MAX_RETRY, data->count); ++ BTINTEL_PCIE_FLR_MAX_RETRY, rec->count); + bt_dev_dbg(hdev, "Boot time: %lld seconds", + ktime_get_boottime_seconds()); + bt_dev_dbg(hdev, "last error at: %lld seconds", +- data->last_error); ++ rec->last_error); + return; + } + btintel_pcie_inc_recovery_count(pdev, &hdev->dev); +- btintel_pcie_reset(hdev); ++ btintel_pcie_request_reset(dev_data, type); + } + + static bool btintel_pcie_wakeup(struct hci_dev *hdev) +@@ -2832,7 +2860,7 @@ static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data) + hdev->hw_error = btintel_pcie_hw_error; + hdev->set_diag = btintel_set_diag; + hdev->set_bdaddr = btintel_set_bdaddr; +- hdev->reset = btintel_pcie_reset; ++ hdev->reset = btintel_pcie_hci_reset; + hdev->wakeup = btintel_pcie_wakeup; + hdev->hci_drv = &btintel_pcie_hci_drv; + +@@ -3124,8 +3152,7 @@ static int btintel_pcie_resume(struct device *dev) + if (data->pm_sx_event == PM_EVENT_FREEZE || + data->pm_sx_event == PM_EVENT_HIBERNATE) { + set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags); +- data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR; +- btintel_pcie_reset(data->hdev); ++ btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR); + return 0; + } + +@@ -3156,7 +3183,7 @@ static int btintel_pcie_resume(struct device *dev) + queue_work(data->coredump_workqueue, &data->coredump_work); + } + set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags); +- btintel_pcie_reset(data->hdev); ++ btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR); + } + return err; + } diff --git a/pkgbuilds/linux-omarchy/0663-btintel-pcie-serialize-reset-type.patch.sig b/pkgbuilds/linux-omarchy/0663-btintel-pcie-serialize-reset-type.patch.sig new file mode 100644 index 0000000..590f6a4 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0663-btintel-pcie-serialize-reset-type.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0664-btintel-pcie-stale-cache-in-dxstate-fallback.patch b/pkgbuilds/linux-omarchy/0664-btintel-pcie-stale-cache-in-dxstate-fallback.patch new file mode 100644 index 0000000..54219c0 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0664-btintel-pcie-stale-cache-in-dxstate-fallback.patch @@ -0,0 +1,30 @@ +diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c +--- a/drivers/bluetooth/btintel_pcie.c ++++ b/drivers/bluetooth/btintel_pcie.c +@@ -3069,10 +3069,12 @@ static int btintel_pcie_set_dxstate(struct btintel_pcie_data *data, u32 dxstate) + BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES, + BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0); + +- /* A hardware bug may cause the alive interrupt to be missed. +- * Check if the controller reached the expected state and retry +- * the operation only if it hasn't. ++ /* A hardware bug may cause the alive interrupt to be missed. Refresh ++ * boot_stage_cache from hardware, since only the interrupt handler ++ * updates it. Finally retry only if the state check still fails. + */ ++ data->boot_stage_cache = btintel_pcie_rd_reg32(data, ++ BTINTEL_PCIE_CSR_BOOT_STAGE_REG); + if (dxstate == BTINTEL_PCIE_STATE_D0) { + if (btintel_pcie_in_d0(data)) + return 0; +diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h +--- a/drivers/bluetooth/btintel_pcie.h ++++ b/drivers/bluetooth/btintel_pcie.h +@@ -50,6 +50,7 @@ + #define BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_HALTED (BIT(14)) + #define BTINTEL_PCIE_CSR_BOOT_STAGE_MAC_ACCESS_ON (BIT(16)) + #define BTINTEL_PCIE_CSR_BOOT_STAGE_ALIVE (BIT(23)) ++/* Reflects live D-state. Updated by hardware on every D-state transition. */ + #define BTINTEL_PCIE_CSR_BOOT_STAGE_D3_STATE_READY (BIT(24)) + + /* Registers for MSI-X */ diff --git a/pkgbuilds/linux-omarchy/0664-btintel-pcie-stale-cache-in-dxstate-fallback.patch.sig b/pkgbuilds/linux-omarchy/0664-btintel-pcie-stale-cache-in-dxstate-fallback.patch.sig new file mode 100644 index 0000000..c9f68b6 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0664-btintel-pcie-stale-cache-in-dxstate-fallback.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0665-btintel-pcie-pm-flow-s0ix-s3-s4.patch b/pkgbuilds/linux-omarchy/0665-btintel-pcie-pm-flow-s0ix-s3-s4.patch new file mode 100644 index 0000000..2ec0e80 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0665-btintel-pcie-pm-flow-s0ix-s3-s4.patch @@ -0,0 +1,145 @@ +diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c +--- a/drivers/bluetooth/btintel_pcie.c ++++ b/drivers/bluetooth/btintel_pcie.c +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -3046,7 +3047,8 @@ static void btintel_pcie_coredump(struct device *dev) + + static int btintel_pcie_set_dxstate(struct btintel_pcie_data *data, u32 dxstate) + { +- int retry = 0, status; ++ int retry = 0; ++ long status; + u32 dx_intr_timeout_ms = 200; + + do { +@@ -3098,18 +3100,23 @@ static int btintel_pcie_suspend_late(struct device *dev, pm_message_t mesg) + + data = pci_get_drvdata(pdev); + +- dxstate = (mesg.event == PM_EVENT_SUSPEND ? +- BTINTEL_PCIE_STATE_D3_HOT : BTINTEL_PCIE_STATE_D3_COLD); +- +- data->pm_sx_event = mesg.event; ++ /* S0ix (s2idle) uses D3_HOT; S3, freeze and hibernate use D3_COLD. */ ++ if (mesg.event == PM_EVENT_SUSPEND && ++ pm_suspend_target_state == PM_SUSPEND_TO_IDLE) ++ dxstate = BTINTEL_PCIE_STATE_D3_HOT; ++ else ++ dxstate = BTINTEL_PCIE_STATE_D3_COLD; + + start = ktime_get(); + + /* Refer: 6.4.11.7 -> Platform power management */ + err = btintel_pcie_set_dxstate(data, dxstate); + +- if (err) ++ if (err) { ++ bt_dev_err(data->hdev, "Failed to set dxstate:%u (%d)", ++ dxstate, err); + return err; ++ } + + bt_dev_dbg(data->hdev, + "device entered into d3 state from d0 in %lld us", +@@ -3132,7 +3139,7 @@ static int btintel_pcie_freeze(struct device *dev) + return btintel_pcie_suspend_late(dev, PMSG_FREEZE); + } + +-static int btintel_pcie_resume(struct device *dev) ++static int btintel_pcie_resume_event(struct device *dev, pm_message_t mesg) + { + struct pci_dev *pdev = to_pci_dev(dev); + struct btintel_pcie_data *data; +@@ -3140,19 +3147,15 @@ static int btintel_pcie_resume(struct device *dev) + int err; + + data = pci_get_drvdata(pdev); +- data->gp0_received = false; + + start = ktime_get(); + +- /* When the system enters S4 (hibernate) mode, bluetooth device loses +- * power, which results in the erasure of its loaded firmware. +- * Consequently, function level reset (flr) is required on system +- * resume to bring the controller back into an operational state by +- * initiating a new firmware download. ++ /* S3 and S4 may cut power, erasing the firmware. Force FLR to recover ++ * instead of a normal D0 transition. + */ +- +- if (data->pm_sx_event == PM_EVENT_FREEZE || +- data->pm_sx_event == PM_EVENT_HIBERNATE) { ++ if (mesg.event == PM_EVENT_RESTORE || ++ (mesg.event == PM_EVENT_RESUME && ++ pm_suspend_target_state == PM_SUSPEND_MEM)) { + set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags); + btintel_pcie_request_reset(data, BTINTEL_PCIE_IOSF_PRR_FLR); + return 0; +@@ -3161,7 +3164,9 @@ static int btintel_pcie_resume(struct device *dev) + /* Refer: 6.4.11.7 -> Platform power management */ + err = btintel_pcie_set_dxstate(data, BTINTEL_PCIE_STATE_D0); + +- if (err == 0) { ++ if (err) { ++ bt_dev_err(data->hdev, "Failed to set D0 state (%d)", err); ++ } else { + bt_dev_dbg(data->hdev, + "device entered into d0 state from d3 in %lld us", + ktime_to_us(ktime_get() - start)); +@@ -3190,13 +3195,28 @@ static int btintel_pcie_resume(struct device *dev) + return err; + } + ++static int btintel_pcie_resume(struct device *dev) ++{ ++ return btintel_pcie_resume_event(dev, PMSG_RESUME); ++} ++ ++static int btintel_pcie_restore(struct device *dev) ++{ ++ return btintel_pcie_resume_event(dev, PMSG_RESTORE); ++} ++ ++static int btintel_pcie_thaw(struct device *dev) ++{ ++ return btintel_pcie_resume_event(dev, PMSG_THAW); ++} ++ + static const struct dev_pm_ops btintel_pcie_pm_ops = { + .suspend = btintel_pcie_suspend, + .resume = btintel_pcie_resume, + .freeze = btintel_pcie_freeze, +- .thaw = btintel_pcie_resume, ++ .thaw = btintel_pcie_thaw, + .poweroff = btintel_pcie_hibernate, +- .restore = btintel_pcie_resume, ++ .restore = btintel_pcie_restore, + }; + + static struct pci_driver btintel_pcie_driver = { +diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h +--- a/drivers/bluetooth/btintel_pcie.h ++++ b/drivers/bluetooth/btintel_pcie.h +@@ -478,7 +478,6 @@ struct btintel_pcie_dump_header { + * @txq: TX Queue struct + * @rxq: RX Queue struct + * @alive_intr_ctxt: Alive interrupt context +- * @pm_sx_event: PM event on which system got suspended + */ + struct btintel_pcie_data { + struct pci_dev *pdev; +@@ -533,7 +532,6 @@ struct btintel_pcie_data { + enum btintel_pcie_reset_type reset_type; + struct btintel_pcie_dbgc dbgc; + struct btintel_pcie_dump_header dmp_hdr; +- u8 pm_sx_event; + u32 debug_evt_addr; + u32 debug_evt_size; + }; diff --git a/pkgbuilds/linux-omarchy/0665-btintel-pcie-pm-flow-s0ix-s3-s4.patch.sig b/pkgbuilds/linux-omarchy/0665-btintel-pcie-pm-flow-s0ix-s3-s4.patch.sig new file mode 100644 index 0000000..89af410 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0665-btintel-pcie-pm-flow-s0ix-s3-s4.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0666-btintel-pcie-verify-serialize-dstate.patch b/pkgbuilds/linux-omarchy/0666-btintel-pcie-verify-serialize-dstate.patch new file mode 100644 index 0000000..b1305d3 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0666-btintel-pcie-verify-serialize-dstate.patch @@ -0,0 +1,113 @@ +diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c +--- a/drivers/bluetooth/btintel_pcie.c ++++ b/drivers/bluetooth/btintel_pcie.c +@@ -1012,6 +1012,10 @@ static void btintel_pcie_msix_gp0_handler(struct btintel_pcie_data *data) + submit_rx = false; + signal_waitq = false; + ++ /* Serialize the alive_intr_ctxt claim against set_dxstate()'s ++ * register-based fallback, which can run concurrently. ++ */ ++ spin_lock(&data->irq_lock); + switch (data->alive_intr_ctxt) { + case BTINTEL_PCIE_ROM: + data->alive_intr_ctxt = BTINTEL_PCIE_FW_DL; +@@ -1067,6 +1071,7 @@ static void btintel_pcie_msix_gp0_handler(struct btintel_pcie_data *data) + data->alive_intr_ctxt); + break; + } ++ spin_unlock(&data->irq_lock); + + if (submit_rx) { + btintel_pcie_reset_ia(data); +@@ -3047,7 +3052,7 @@ static void btintel_pcie_coredump(struct device *dev) + + static int btintel_pcie_set_dxstate(struct btintel_pcie_data *data, u32 dxstate) + { +- int retry = 0; ++ int retry = 0, err; + long status; + u32 dx_intr_timeout_ms = 200; + +@@ -3059,30 +3064,66 @@ static int btintel_pcie_set_dxstate(struct btintel_pcie_data *data, u32 dxstate) + status = wait_event_timeout(data->gp0_wait_q, data->gp0_received, + msecs_to_jiffies(dx_intr_timeout_ms)); + +- if (status) +- return 0; ++ if (!status) { ++ bt_dev_warn(data->hdev, ++ "Timeout (%u ms) on alive interrupt for D%d entry, retry count %d", ++ dx_intr_timeout_ms, dxstate, retry); + +- bt_dev_warn(data->hdev, +- "Timeout (%u ms) on alive interrupt for D%d entry, retry count %d", +- dx_intr_timeout_ms, dxstate, retry); ++ /* clear gp0 cause */ ++ btintel_pcie_clr_reg_bits(data, ++ BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES, ++ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0); ++ } + +- /* clear gp0 cause */ +- btintel_pcie_clr_reg_bits(data, +- BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES, +- BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0); +- +- /* A hardware bug may cause the alive interrupt to be missed. Refresh +- * boot_stage_cache from hardware, since only the interrupt handler +- * updates it. Finally retry only if the state check still fails. ++ /* gp0_received is set at the top of the handler, before the switch on ++ * alive_intr_ctxt. Error and lockdown are filtered out above it, but a ++ * matched case can still complete without doing anything - D3 breaks ++ * unchanged while the controller has not reached D0 - and a hardware ++ * bug may drop the interrupt outright. Either way the flag says a gp0 ++ * was handled, not that the transition completed, and only the register ++ * knows. Refresh the cache here and retry only if the state check still ++ * fails. + */ + data->boot_stage_cache = btintel_pcie_rd_reg32(data, + BTINTEL_PCIE_CSR_BOOT_STAGE_REG); + if (dxstate == BTINTEL_PCIE_STATE_D0) { +- if (btintel_pcie_in_d0(data)) ++ if (btintel_pcie_in_d0(data)) { ++ bool armed; ++ ++ /* Claim the transition under irq_lock so a ++ * concurrent gp0 handler run cannot also arm RX. ++ */ ++ spin_lock(&data->irq_lock); ++ armed = data->alive_intr_ctxt == BTINTEL_PCIE_D0; ++ if (!armed) ++ data->alive_intr_ctxt = BTINTEL_PCIE_D0; ++ spin_unlock(&data->irq_lock); ++ ++ if (armed) ++ return 0; ++ ++ /* Do what the handler's D3 -> D0 branch ++ * would have done, unless it already has. ++ */ ++ btintel_pcie_reset_ia(data); ++ err = btintel_pcie_start_rx(data); ++ if (err) ++ return err; ++ + return 0; ++ } + } else { +- if (btintel_pcie_in_d3(data)) ++ if (btintel_pcie_in_d3(data)) { ++ /* Do what the handler's D0 -> D3 branch ++ * would have done, unless it already has. ++ */ ++ spin_lock(&data->irq_lock); ++ if (data->alive_intr_ctxt != BTINTEL_PCIE_D3) ++ data->alive_intr_ctxt = BTINTEL_PCIE_D3; ++ spin_unlock(&data->irq_lock); ++ + return 0; ++ } + } + + } while (++retry < BTINTEL_PCIE_DX_TRANSITION_MAX_RETRIES); diff --git a/pkgbuilds/linux-omarchy/0666-btintel-pcie-verify-serialize-dstate.patch.sig b/pkgbuilds/linux-omarchy/0666-btintel-pcie-verify-serialize-dstate.patch.sig new file mode 100644 index 0000000..8fbfd35 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0666-btintel-pcie-verify-serialize-dstate.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0667-btintel-pcie-gp0-cause-w1c-write.patch b/pkgbuilds/linux-omarchy/0667-btintel-pcie-gp0-cause-w1c-write.patch new file mode 100644 index 0000000..9927412 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0667-btintel-pcie-gp0-cause-w1c-write.patch @@ -0,0 +1,20 @@ +diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c +--- a/drivers/bluetooth/btintel_pcie.c ++++ b/drivers/bluetooth/btintel_pcie.c +@@ -3069,10 +3069,12 @@ static int btintel_pcie_set_dxstate(struct btintel_pcie_data *data, u32 dxstate) + "Timeout (%u ms) on alive interrupt for D%d entry, retry count %d", + dx_intr_timeout_ms, dxstate, retry); + +- /* clear gp0 cause */ +- btintel_pcie_clr_reg_bits(data, +- BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES, +- BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0); ++ /* MSIX_HW_INT_CAUSES is W1C. Write only GP0 so other ++ * pending causes are not acknowledged here. ++ */ ++ btintel_pcie_wr_reg32(data, ++ BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES, ++ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0); + } + + /* gp0_received is set at the top of the handler, before the switch on diff --git a/pkgbuilds/linux-omarchy/0667-btintel-pcie-gp0-cause-w1c-write.patch.sig b/pkgbuilds/linux-omarchy/0667-btintel-pcie-gp0-cause-w1c-write.patch.sig new file mode 100644 index 0000000..817ba81 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0667-btintel-pcie-gp0-cause-w1c-write.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0668-btintel-pcie-dstate-races.patch b/pkgbuilds/linux-omarchy/0668-btintel-pcie-dstate-races.patch new file mode 100644 index 0000000..d25c602 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0668-btintel-pcie-dstate-races.patch @@ -0,0 +1,86 @@ +diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c +--- a/drivers/bluetooth/btintel_pcie.c ++++ b/drivers/bluetooth/btintel_pcie.c +@@ -1016,6 +1016,8 @@ static void btintel_pcie_msix_gp0_handle + * register-based fallback, which can run concurrently. + */ + spin_lock(&data->irq_lock); ++ data->boot_stage_cache = btintel_pcie_rd_reg32(data, ++ BTINTEL_PCIE_CSR_BOOT_STAGE_REG); + switch (data->alive_intr_ctxt) { + case BTINTEL_PCIE_ROM: + data->alive_intr_ctxt = BTINTEL_PCIE_FW_DL; +@@ -1071,12 +1073,12 @@ static void btintel_pcie_msix_gp0_handle + data->alive_intr_ctxt); + break; + } +- spin_unlock(&data->irq_lock); + + if (submit_rx) { + btintel_pcie_reset_ia(data); + btintel_pcie_start_rx(data); + } ++ spin_unlock(&data->irq_lock); + + if (signal_waitq) { + bt_dev_dbg(data->hdev, "wake up gp0 wait_q"); +@@ -3086,40 +3088,34 @@ static int btintel_pcie_set_dxstate(stru + * knows. Refresh the cache here and retry only if the state check still + * fails. + */ ++ spin_lock(&data->irq_lock); + data->boot_stage_cache = btintel_pcie_rd_reg32(data, + BTINTEL_PCIE_CSR_BOOT_STAGE_REG); + if (dxstate == BTINTEL_PCIE_STATE_D0) { + if (btintel_pcie_in_d0(data)) { +- bool armed; ++ err = 0; + + /* Claim the transition under irq_lock so a + * concurrent gp0 handler run cannot also arm RX. + */ +- spin_lock(&data->irq_lock); +- armed = data->alive_intr_ctxt == BTINTEL_PCIE_D0; +- if (!armed) ++ if (data->alive_intr_ctxt != BTINTEL_PCIE_D0) { + data->alive_intr_ctxt = BTINTEL_PCIE_D0; +- spin_unlock(&data->irq_lock); + +- if (armed) +- return 0; +- +- /* Do what the handler's D3 -> D0 branch +- * would have done, unless it already has. +- */ +- btintel_pcie_reset_ia(data); +- err = btintel_pcie_start_rx(data); +- if (err) +- return err; ++ /* Do what the handler's D3 -> D0 branch ++ * would have done, unless it already has. ++ */ ++ btintel_pcie_reset_ia(data); ++ err = btintel_pcie_start_rx(data); ++ } ++ spin_unlock(&data->irq_lock); + +- return 0; ++ return err; + } + } else { + if (btintel_pcie_in_d3(data)) { + /* Do what the handler's D0 -> D3 branch + * would have done, unless it already has. + */ +- spin_lock(&data->irq_lock); + if (data->alive_intr_ctxt != BTINTEL_PCIE_D3) + data->alive_intr_ctxt = BTINTEL_PCIE_D3; + spin_unlock(&data->irq_lock); +@@ -3127,6 +3123,7 @@ static int btintel_pcie_set_dxstate(stru + return 0; + } + } ++ spin_unlock(&data->irq_lock); + + } while (++retry < BTINTEL_PCIE_DX_TRANSITION_MAX_RETRIES); + diff --git a/pkgbuilds/linux-omarchy/0668-btintel-pcie-dstate-races.patch.sig b/pkgbuilds/linux-omarchy/0668-btintel-pcie-dstate-races.patch.sig new file mode 100644 index 0000000..93a7c8a Binary files /dev/null and b/pkgbuilds/linux-omarchy/0668-btintel-pcie-dstate-races.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0669-btintel-version-tlv-parse-errors.patch b/pkgbuilds/linux-omarchy/0669-btintel-version-tlv-parse-errors.patch new file mode 100644 index 0000000..164dbb6 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0669-btintel-version-tlv-parse-errors.patch @@ -0,0 +1,24 @@ +diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c +--- a/drivers/bluetooth/btintel.c ++++ b/drivers/bluetooth/btintel.c +@@ -720,6 +720,7 @@ static int btintel_read_version_tlv(struct hci_dev *hdev, + { + struct sk_buff *skb; + const u8 param[1] = { 0xFF }; ++ int err; + + if (!version) + return -EINVAL; +@@ -738,10 +739,10 @@ static int btintel_read_version_tlv(struct hci_dev *hdev, + return -EIO; + } + +- btintel_parse_version_tlv(hdev, version, skb); ++ err = btintel_parse_version_tlv(hdev, version, skb); + + kfree_skb(skb); +- return 0; ++ return err; + } + + /* ------- REGMAP IBT SUPPORT ------- */ diff --git a/pkgbuilds/linux-omarchy/0669-btintel-version-tlv-parse-errors.patch.sig b/pkgbuilds/linux-omarchy/0669-btintel-version-tlv-parse-errors.patch.sig new file mode 100644 index 0000000..caff3f4 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0669-btintel-version-tlv-parse-errors.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0670-bluetooth-fixes.patch b/pkgbuilds/linux-omarchy/0670-bluetooth-fixes.patch new file mode 100644 index 0000000..14623cd --- /dev/null +++ b/pkgbuilds/linux-omarchy/0670-bluetooth-fixes.patch @@ -0,0 +1,485 @@ +diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c +--- a/drivers/bluetooth/btintel.c ++++ b/drivers/bluetooth/btintel.c +@@ -288,7 +288,8 @@ void btintel_hw_error(struct hci_dev *hdev, u8 code) + goto unlock; + } + +- bt_dev_err(hdev, "Exception info %s", (char *)(skb->data + 1)); ++ bt_dev_err(hdev, "Exception info %.*s", (int)(skb->len - 1), ++ (char *)(skb->data + 1)); + + kfree_skb(skb); + +@@ -407,8 +408,16 @@ int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name) + /* DDC file contains one or more DDC structure which has + * Length (1 byte), DDC ID (2 bytes), and DDC value (Length - 2). + */ +- while (fw->size > fw_ptr - fw->data) { +- u8 cmd_plen = fw_ptr[0] + sizeof(u8); ++ while (fw->size > (size_t)(fw_ptr - fw->data)) { ++ size_t remaining = fw->size - (fw_ptr - fw->data); ++ unsigned int cmd_plen = fw_ptr[0] + 1U; ++ ++ if (cmd_plen < 3 || cmd_plen > U8_MAX || cmd_plen > remaining) { ++ bt_dev_err(hdev, "Malformed DDC record (plen=%u, remaining=%zu)", ++ cmd_plen, remaining); ++ release_firmware(fw); ++ return -EINVAL; ++ } + + skb = __hci_cmd_sync(hdev, 0xfc8b, cmd_plen, fw_ptr, + HCI_INIT_TIMEOUT); +diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c +--- a/drivers/bluetooth/btintel_pcie.c ++++ b/drivers/bluetooth/btintel_pcie.c +@@ -1200,7 +1200,7 @@ static int btintel_pcie_recv_frame(struct btintel_pcie_data *data, + { + int ret; + u8 pkt_type; +- u16 plen; ++ u32 plen; + u32 pcie_pkt_type; + void *pdata; + struct hci_dev *hdev = data->hdev; +@@ -2149,6 +2149,16 @@ static int btintel_pcie_send_frame(struct hci_dev *hdev, + if (test_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags)) + return -ENODEV; + ++ /* Account for the 4-byte PCIe type header prepended before the ++ * DMA copy. Written as a subtraction to avoid wrap-around on ++ * attacker-controlled skb->len. ++ */ ++ if (skb->len > BTINTEL_PCIE_BUFFER_SIZE - BTINTEL_PCIE_HCI_TYPE_LEN) { ++ bt_dev_err(hdev, "Packet too large: %u > %u", skb->len, ++ BTINTEL_PCIE_BUFFER_SIZE - BTINTEL_PCIE_HCI_TYPE_LEN); ++ return -EMSGSIZE; ++ } ++ + /* Due to the fw limitation, the type header of the packet should be + * 4 bytes unlike 1 byte for UART. In UART, the firmware can read + * the first byte to get the packet type and redirect the rest of data +diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h +--- a/include/net/bluetooth/hci_core.h ++++ b/include/net/bluetooth/hci_core.h +@@ -62,7 +62,7 @@ struct inquiry_entry { + NAME_PENDING, + NAME_KNOWN, + } name_state; +- __u32 timestamp; ++ unsigned long timestamp; + struct inquiry_data data; + }; + +@@ -78,7 +78,7 @@ struct discovery_state { + struct list_head all; /* All devices found during inquiry */ + struct list_head unknown; /* Name state not known */ + struct list_head resolve; /* Name needs to be resolved */ +- __u32 timestamp; ++ unsigned long timestamp; + bdaddr_t last_adv_addr; + u8 last_adv_addr_type; + s8 last_adv_rssi; +@@ -561,6 +561,7 @@ struct hci_dev { + struct list_head link_keys; + struct list_head long_term_keys; + struct list_head identity_resolving_keys; ++ struct mutex remote_oob_lock; + struct list_head remote_oob_data; + struct list_head le_accept_list; + struct list_head le_resolv_list; +diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c +--- a/net/bluetooth/hci_conn.c ++++ b/net/bluetooth/hci_conn.c +@@ -302,11 +302,13 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data) + cp.tx_bandwidth = cpu_to_le32(0x00001f40); + cp.rx_bandwidth = cpu_to_le32(0x00001f40); + ++ hci_dev_lock(hdev); ++ + switch (conn->codec.id) { + case BT_CODEC_MSBC: + if (!find_next_esco_param(conn, esco_param_msbc, + ARRAY_SIZE(esco_param_msbc))) +- return -EINVAL; ++ goto unlock; + + param = &esco_param_msbc[conn->attempt - 1]; + cp.tx_coding_format.id = 0x05; +@@ -332,7 +334,7 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data) + case BT_CODEC_TRANSPARENT: + if (!find_next_esco_param(conn, esco_param_msbc, + ARRAY_SIZE(esco_param_msbc))) +- return -EINVAL; ++ goto unlock; + + param = &esco_param_msbc[conn->attempt - 1]; + cp.tx_coding_format.id = 0x03; +@@ -359,11 +361,11 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data) + if (conn->parent && lmp_esco_capable(conn->parent)) { + if (!find_next_esco_param(conn, esco_param_cvsd, + ARRAY_SIZE(esco_param_cvsd))) +- return -EINVAL; ++ goto unlock; + param = &esco_param_cvsd[conn->attempt - 1]; + } else { + if (conn->attempt > ARRAY_SIZE(sco_param_cvsd)) +- return -EINVAL; ++ goto unlock; + param = &sco_param_cvsd[conn->attempt - 1]; + } + cp.tx_coding_format.id = 2; +@@ -386,9 +388,11 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data) + cp.out_transport_unit_size = 16; + break; + default: +- return -EINVAL; ++ goto unlock; + } + ++ hci_dev_unlock(hdev); ++ + cp.retrans_effort = param->retrans_effort; + cp.pkt_type = __cpu_to_le16(param->pkt_type); + cp.max_latency = __cpu_to_le16(param->max_latency); +@@ -397,6 +401,10 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data) + return -EIO; + + return 0; ++ ++unlock: ++ hci_dev_unlock(hdev); ++ return -EINVAL; + } + + static bool hci_setup_sync_conn(struct hci_conn *conn, __u16 handle) +diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c +--- a/net/bluetooth/hci_core.c ++++ b/net/bluetooth/hci_core.c +@@ -1486,8 +1486,10 @@ int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, + + BT_DBG("%s removing %pMR (%u)", hdev->name, bdaddr, bdaddr_type); + ++ mutex_lock(&hdev->remote_oob_lock); + list_del(&data->list); + kfree(data); ++ mutex_unlock(&hdev->remote_oob_lock); + + return 0; + } +@@ -1496,10 +1498,12 @@ void hci_remote_oob_data_clear(struct hci_dev *hdev) + { + struct oob_data *data, *n; + ++ mutex_lock(&hdev->remote_oob_lock); + list_for_each_entry_safe(data, n, &hdev->remote_oob_data, list) { + list_del(&data->list); + kfree(data); + } ++ mutex_unlock(&hdev->remote_oob_lock); + } + + int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, +@@ -1508,11 +1512,14 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, + { + struct oob_data *data; + ++ mutex_lock(&hdev->remote_oob_lock); + data = hci_find_remote_oob_data(hdev, bdaddr, bdaddr_type); + if (!data) { + data = kmalloc_obj(*data); +- if (!data) ++ if (!data) { ++ mutex_unlock(&hdev->remote_oob_lock); + return -ENOMEM; ++ } + + bacpy(&data->bdaddr, bdaddr); + data->bdaddr_type = bdaddr_type; +@@ -1545,6 +1552,8 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, + + BT_DBG("%s for %pMR", hdev->name, bdaddr); + ++ mutex_unlock(&hdev->remote_oob_lock); ++ + return 0; + } + +@@ -2482,6 +2491,7 @@ struct hci_dev *hci_alloc_dev_priv(int sizeof_priv) + mutex_init(&hdev->lock); + mutex_init(&hdev->req_lock); + mutex_init(&hdev->mgmt_pending_lock); ++ mutex_init(&hdev->remote_oob_lock); + + ida_init(&hdev->unset_handle_ida); + +@@ -2554,8 +2564,10 @@ int hci_register_dev(struct hci_dev *hdev) + return id; + + error = dev_set_name(&hdev->dev, "hci%u", id); +- if (error) ++ if (error) { ++ ida_free(&hci_index_ida, id); + return error; ++ } + + hdev->name = dev_name(&hdev->dev); + hdev->id = id; +@@ -3323,6 +3335,8 @@ static void hci_queue_iso(struct hci_conn *conn, struct sk_buff_head *queue, + + skb_shinfo(skb)->frag_list = NULL; + ++ spin_lock_bh(&queue->lock); ++ + __skb_queue_tail(queue, skb); + + do { +@@ -3337,6 +3351,8 @@ static void hci_queue_iso(struct hci_conn *conn, struct sk_buff_head *queue, + + __skb_queue_tail(queue, skb); + } while (list); ++ ++ spin_unlock_bh(&queue->lock); + } + + bt_dev_dbg(hdev, "hcon %p queued %d", conn, skb_queue_len(queue)); +@@ -3400,9 +3416,6 @@ static struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, + struct hci_conn *conn = NULL, *c; + unsigned int num = 0, min = ~0; + +- /* We don't have to lock device here. Connections are always +- * added and removed with TX task disabled. */ +- + rcu_read_lock(); + + list_for_each_entry_rcu(c, &h->list, list) { +@@ -3607,13 +3620,15 @@ static void __check_timeout(struct hci_dev *hdev, unsigned int cnt, u8 type) + } + + /* Schedule SCO */ +-static void hci_sched_sco(struct hci_dev *hdev, __u8 type) ++static void __hci_sched_sco(struct hci_dev *hdev, __u8 type) + { + struct hci_conn *conn; + struct sk_buff *skb; + int quote, *cnt; + unsigned int pkts = hdev->sco_pkts; + ++ lockdep_assert_held(&hdev->lock); ++ + bt_dev_dbg(hdev, "type %u", type); + + if (!hci_conn_num(hdev, type) || !pkts) +@@ -3648,6 +3663,13 @@ static void hci_sched_sco(struct hci_dev *hdev, __u8 type) + queue_work(hdev->workqueue, &hdev->tx_work); + } + ++static void hci_sched_sco(struct hci_dev *hdev, __u8 type) ++{ ++ hci_dev_lock(hdev); ++ __hci_sched_sco(hdev, type); ++ hci_dev_unlock(hdev); ++} ++ + static void hci_sched_acl_pkt(struct hci_dev *hdev) + { + unsigned int cnt = hdev->acl_cnt; +@@ -3657,6 +3679,8 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev) + + __check_timeout(hdev, cnt, ACL_LINK); + ++ hci_dev_lock(hdev); ++ + while (hdev->acl_cnt && + (chan = hci_chan_sent(hdev, ACL_LINK, "e))) { + u32 priority = (skb_peek(&chan->data_q))->priority; +@@ -3681,13 +3705,15 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev) + chan->conn->sent++; + + /* Send pending SCO packets right away */ +- hci_sched_sco(hdev, SCO_LINK); +- hci_sched_sco(hdev, ESCO_LINK); ++ __hci_sched_sco(hdev, SCO_LINK); ++ __hci_sched_sco(hdev, ESCO_LINK); + } + } + + if (cnt != hdev->acl_cnt) + hci_prio_recalculate(hdev, ACL_LINK); ++ ++ hci_dev_unlock(hdev); + } + + static void hci_sched_acl(struct hci_dev *hdev) +@@ -3716,6 +3742,8 @@ static void hci_sched_le(struct hci_dev *hdev) + + __check_timeout(hdev, *cnt, LE_LINK); + ++ hci_dev_lock(hdev); ++ + tmp = *cnt; + while (*cnt && (chan = hci_chan_sent(hdev, LE_LINK, "e))) { + u32 priority = (skb_peek(&chan->data_q))->priority; +@@ -3737,13 +3765,15 @@ static void hci_sched_le(struct hci_dev *hdev) + chan->conn->sent++; + + /* Send pending SCO packets right away */ +- hci_sched_sco(hdev, SCO_LINK); +- hci_sched_sco(hdev, ESCO_LINK); ++ __hci_sched_sco(hdev, SCO_LINK); ++ __hci_sched_sco(hdev, ESCO_LINK); + } + } + + if (*cnt != tmp) + hci_prio_recalculate(hdev, LE_LINK); ++ ++ hci_dev_unlock(hdev); + } + + /* Schedule iso */ +@@ -3762,6 +3792,8 @@ static void hci_sched_iso(struct hci_dev *hdev, __u8 type) + + __check_timeout(hdev, *cnt, type); + ++ hci_dev_lock(hdev); ++ + while (*cnt && (conn = hci_low_sent(hdev, type, "e))) { + while (quote-- && (skb = skb_dequeue(&conn->data_q))) { + BT_DBG("skb %p len %d", skb, skb->len); +@@ -3775,6 +3807,8 @@ static void hci_sched_iso(struct hci_dev *hdev, __u8 type) + (*cnt)--; + } + } ++ ++ hci_dev_unlock(hdev); + } + + static void hci_tx_work(struct work_struct *work) +diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c +--- a/net/bluetooth/hci_debugfs.c ++++ b/net/bluetooth/hci_debugfs.c +@@ -364,7 +364,7 @@ static int inquiry_cache_show(struct seq_file *f, void *p) + + list_for_each_entry(e, &cache->all, all) { + struct inquiry_data *data = &e->data; +- seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n", ++ seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %lu\n", + &data->bdaddr, + data->pscan_rep_mode, data->pscan_period_mode, + data->pscan_mode, data->dev_class[2], +diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c +--- a/net/bluetooth/hci_sync.c ++++ b/net/bluetooth/hci_sync.c +@@ -312,6 +312,10 @@ static void hci_cmd_sync_work(struct work_struct *work) + while (1) { + struct hci_cmd_sync_work_entry *entry; + ++ /* Leave the backlog to hci_cmd_sync_clear() */ ++ if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) ++ break; ++ + mutex_lock(&hdev->cmd_sync_work_lock); + entry = list_first_entry_or_null(&hdev->cmd_sync_work_list, + struct hci_cmd_sync_work_entry, +@@ -658,6 +662,8 @@ void hci_cmd_sync_clear(struct hci_dev *hdev) + { + struct hci_cmd_sync_work_entry *entry, *tmp; + ++ /* cmd_work is disabled, the pending request can only time out */ ++ hci_cmd_sync_cancel_sync(hdev, ENODEV); + cancel_work_sync(&hdev->cmd_sync_work); + cancel_work_sync(&hdev->reenable_adv_work); + +@@ -5678,6 +5684,7 @@ int hci_stop_discovery_sync(struct hci_dev *hdev) + { + struct discovery_state *d = &hdev->discovery; + struct inquiry_entry *e; ++ bdaddr_t addr; + int err; + + bt_dev_dbg(hdev, "state %u", hdev->discovery.state); +@@ -5713,15 +5720,21 @@ int hci_stop_discovery_sync(struct hci_dev *hdev) + return 0; + + if (d->state == DISCOVERY_RESOLVING || d->state == DISCOVERY_STOPPING) { ++ hci_dev_lock(hdev); + e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, + NAME_PENDING); +- if (!e) ++ if (!e) { ++ hci_dev_unlock(hdev); + return 0; ++ } ++ ++ bacpy(&addr, &e->data.bdaddr); ++ hci_dev_unlock(hdev); + + /* Ignore cancel errors since it should interfere with stopping + * of the discovery. + */ +- hci_remote_name_cancel_sync(hdev, &e->data.bdaddr); ++ hci_remote_name_cancel_sync(hdev, &addr); + } + + return 0; +@@ -7151,6 +7164,7 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) + bacpy(&cp.bdaddr, &conn->dst); + cp.pscan_rep_mode = 0x02; + ++ hci_dev_lock(hdev); + ie = hci_inquiry_cache_lookup(hdev, &conn->dst); + if (ie) { + if (inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) { +@@ -7162,6 +7176,7 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) + + memcpy(conn->dev_class, ie->data.dev_class, 3); + } ++ hci_dev_unlock(hdev); + + cp.pkt_type = cpu_to_le16(conn->pkt_type); + if (lmp_rswitch_capable(hdev) && !(hdev->link_mode & HCI_LM_MASTER)) +diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c +--- a/net/bluetooth/rfcomm/tty.c ++++ b/net/bluetooth/rfcomm/tty.c +@@ -462,7 +462,7 @@ static int __rfcomm_release_dev(void __user *arg) + /* Shut down TTY synchronously before freeing rfcomm_dev */ + tty_port_tty_vhangup(&dev->port); + +- if (!test_bit(RFCOMM_TTY_OWNED, &dev->status)) ++ if (!test_and_set_bit(RFCOMM_TTY_OWNED, &dev->status)) + tty_port_put(&dev->port); + + tty_port_put(&dev->port); +@@ -724,10 +724,9 @@ static int rfcomm_tty_install(struct tty_driver *driver, struct tty_struct *tty) + * when the last process closes the tty. The behaviour is expected by + * userspace. + */ +- if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) { +- set_bit(RFCOMM_TTY_OWNED, &dev->status); ++ if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags) && ++ !test_and_set_bit(RFCOMM_TTY_OWNED, &dev->status)) + tty_port_put(&dev->port); +- } + + return 0; + } +diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c +--- a/net/bluetooth/smp.c ++++ b/net/bluetooth/smp.c +@@ -661,6 +661,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn, + else + bdaddr_type = BDADDR_LE_RANDOM; + ++ mutex_lock(&hdev->remote_oob_lock); + oob_data = hci_find_remote_oob_data(hdev, &hcon->dst, + bdaddr_type); + if (oob_data && oob_data->present) { +@@ -671,6 +672,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn, + SMP_DBG("OOB Remote Confirmation: %16phN", smp->pcnf); + SMP_DBG("OOB Remote Random: %16phN", smp->rr); + } ++ mutex_unlock(&hdev->remote_oob_lock); + + } else { + authreq &= ~SMP_AUTH_SC; diff --git a/pkgbuilds/linux-omarchy/0670-bluetooth-fixes.patch.sig b/pkgbuilds/linux-omarchy/0670-bluetooth-fixes.patch.sig new file mode 100644 index 0000000..cadba43 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0670-bluetooth-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0701-pci-amd-enhanced-atomics.patch b/pkgbuilds/linux-omarchy/0701-pci-amd-enhanced-atomics.patch new file mode 100644 index 0000000..c76cb96 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0701-pci-amd-enhanced-atomics.patch @@ -0,0 +1,109 @@ +diff --git a/arch/x86/pci/fixup.c b/arch/x86/pci/fixup.c +--- a/arch/x86/pci/fixup.c ++++ b/arch/x86/pci/fixup.c +@@ -886,6 +886,105 @@ static void quirk_clear_strap_no_soft_reset_dev2_f0(struct pci_dev *dev) + } + } + DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, 0x15b8, quirk_clear_strap_no_soft_reset_dev2_f0); ++ ++/* ++ * Enhanced atomic operations can cause corruption with 64-bit IOVA ++ * on these devices. ++ */ ++#define RX_ENH_ATOMIC_EN BIT(8) ++ ++static const u32 nbio_7_7_pcie_smn_addrs[] = { ++ 0x111401d0, ++ 0x111411d0, ++ 0x111421d0, ++ 0x111431d0, ++ 0x111441d0, ++ 0x112401d0, ++ 0x112411d0, ++ 0x112421d0, ++ 0x112431d0, ++ 0x112441d0, ++ 0x112451d0, ++ 0x113401d0, ++ 0x114401d0, ++}; ++ ++static const u32 nbio_7_11_pcie_smn_addrs[] = { ++ 0x112401d0, ++ 0x112411d0, ++ 0x112421d0, ++ 0x112431d0, ++ 0x112441d0, ++ 0x112451d0, ++ 0x113401d0, ++ 0x113411d0, ++ 0x113421d0, ++ 0x113431d0, ++ 0x113441d0, ++ 0x113451d0, ++}; ++ ++static void quirk_amd_nbio_enhanced_atomic(struct pci_dev *host_bridge, ++ const u32 *smn_addrs, ++ size_t nr_smn_addrs) ++{ ++ bool changed = false; ++ size_t i; ++ u32 data; ++ int ret; ++ ++ for (i = 0; i < nr_smn_addrs; i++) { ++ ret = amd_smn_read(0, smn_addrs[i], &data); ++ if (ret) ++ continue; ++ if (!(data & RX_ENH_ATOMIC_EN)) ++ continue; ++ data = data & ~RX_ENH_ATOMIC_EN; ++ ret = amd_smn_write(0, smn_addrs[i], data); ++ if (ret) ++ continue; ++ if (changed) ++ continue; ++ ret = amd_smn_read(0, smn_addrs[i], &data); ++ if (ret) ++ continue; ++ if (data & RX_ENH_ATOMIC_EN) ++ continue; ++ changed = true; ++ } ++ ++ if (changed) ++ pci_info(host_bridge, "enhanced atomics disabled\n"); ++} ++ ++static void quirk_amd_nbio_7_7_disable_enhanced_atomic(struct pci_dev *dev) ++{ ++ quirk_amd_nbio_enhanced_atomic(dev, nbio_7_7_pcie_smn_addrs, ++ ARRAY_SIZE(nbio_7_7_pcie_smn_addrs)); ++} ++ ++static void quirk_amd_nbio_7_11_disable_enhanced_atomic(struct pci_dev *dev) ++{ ++ quirk_amd_nbio_enhanced_atomic(dev, nbio_7_11_pcie_smn_addrs, ++ ARRAY_SIZE(nbio_7_11_pcie_smn_addrs)); ++} ++ ++/* Phoenix, Hawk Point (NBIO 7.7) */ ++DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, 0x14E8, ++ quirk_amd_nbio_7_7_disable_enhanced_atomic); ++DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, 0x14E8, ++ quirk_amd_nbio_7_7_disable_enhanced_atomic); ++ ++/* Strix, Krackan, Strix Halo (NBIO 7.11) */ ++DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, 0x1507, ++ quirk_amd_nbio_7_11_disable_enhanced_atomic); ++DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, 0x1507, ++ quirk_amd_nbio_7_11_disable_enhanced_atomic); ++DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, 0x1122, ++ quirk_amd_nbio_7_11_disable_enhanced_atomic); ++DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, 0x1122, ++ quirk_amd_nbio_7_11_disable_enhanced_atomic); ++ + #endif + + /* diff --git a/pkgbuilds/linux-omarchy/0701-pci-amd-enhanced-atomics.patch.sig b/pkgbuilds/linux-omarchy/0701-pci-amd-enhanced-atomics.patch.sig new file mode 100644 index 0000000..7732219 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0701-pci-amd-enhanced-atomics.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0702-ahci-jmb585-32bit-dma-revert.patch b/pkgbuilds/linux-omarchy/0702-ahci-jmb585-32bit-dma-revert.patch new file mode 100644 index 0000000..9794028 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0702-ahci-jmb585-32bit-dma-revert.patch @@ -0,0 +1,42 @@ +diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c +--- a/drivers/ata/ahci.c ++++ b/drivers/ata/ahci.c +@@ -68,7 +68,6 @@ enum board_ids { + /* board IDs for specific chipsets in alphabetical order */ + board_ahci_al, + board_ahci_avn, +- board_ahci_jmb585, + board_ahci_mcp65, + board_ahci_mcp77, + board_ahci_mcp89, +@@ -213,15 +212,6 @@ static const struct ata_port_info ahci_port_info[] = { + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_avn_ops, + }, +- /* JMicron JMB582/585: 64-bit DMA is broken, force 32-bit */ +- [board_ahci_jmb585] = { +- AHCI_HFLAGS (AHCI_HFLAG_IGN_IRQ_IF_ERR | +- AHCI_HFLAG_32BIT_ONLY), +- .flags = AHCI_FLAG_COMMON, +- .pio_mask = ATA_PIO4, +- .udma_mask = ATA_UDMA6, +- .port_ops = &ahci_ops, +- }, + [board_ahci_mcp65] = { + AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP | + AHCI_HFLAG_YES_NCQ), +@@ -954,14 +944,6 @@ static const struct pci_device_id ahci_pci_tbl[] = { + /* Elkhart Lake AHCI */ + PCI_VDEVICE(INTEL, 0x4b63), + .driver_data = board_ahci_pcs_quirk, +- }, { +- /* JMicron JMB582/585: force 32-bit DMA (broken 64-bit implementation) */ +- PCI_VDEVICE(JMICRON, 0x0582), +- .driver_data = board_ahci_jmb585, +- +- }, { +- PCI_VDEVICE(JMICRON, 0x0585), +- .driver_data = board_ahci_jmb585, + }, { + /* JMicron 360/1/3/5/6, match class to avoid IDE function */ + PCI_DEVICE(PCI_VENDOR_ID_JMICRON, PCI_ANY_ID), diff --git a/pkgbuilds/linux-omarchy/0702-ahci-jmb585-32bit-dma-revert.patch.sig b/pkgbuilds/linux-omarchy/0702-ahci-jmb585-32bit-dma-revert.patch.sig new file mode 100644 index 0000000..58c4c7c Binary files /dev/null and b/pkgbuilds/linux-omarchy/0702-ahci-jmb585-32bit-dma-revert.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0703-pci-vmd-scan-bios-enumerated-devices.patch b/pkgbuilds/linux-omarchy/0703-pci-vmd-scan-bios-enumerated-devices.patch new file mode 100644 index 0000000..0fe6c04 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0703-pci-vmd-scan-bios-enumerated-devices.patch @@ -0,0 +1,297 @@ +diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c +--- a/drivers/pci/controller/vmd.c ++++ b/drivers/pci/controller/vmd.c +@@ -37,6 +37,12 @@ + #define MB2_SHADOW_OFFSET 0x2000 + #define MB2_SHADOW_SIZE 16 + ++/* DMR BAR4 register offsets */ ++#define SHADOW_MEMBAR1_28C1 0x2818 /* MEMBAR1 physical address */ ++#define SHADOW_MEMBAR2_28C1 0x2820 /* MEMBAR2 physical address */ ++#define BASE_ID_REG_28C1 0x2840 ++#define MEMBAR2_OFFSET_28C1 0x30d0 ++ + enum vmd_features { + /* + * Device may contain registers which hint the physical location of the +@@ -77,6 +83,15 @@ enum vmd_features { + * proper power management of the SoC. + */ + VMD_FEAT_BIOS_PM_QUIRK = (1 << 5), ++ ++ /* ++ * Newer VMD with device ID 0x28C1 has unique settings compared to its ++ * predecessor where BIOS enumerates the entire VMD device tree and ++ * stores respective configurations including bus start range and ++ * shadow registers in VMD MMIO space in VMD BAR4/BAR5, otherwise ++ * referred to as MEMBAR2 or MSI-X BAR. ++ */ ++ VMD_FEAT_USE_BIOS_INFO = (1 << 6), + }; + + #define VMD_BIOS_PM_QUIRK_LTR 0x1003 /* 3145728 ns */ +@@ -142,6 +157,7 @@ struct vmd_dev { + u8 first_vec; + char *name; + int instance; ++ unsigned long features; + }; + + static inline struct vmd_dev *vmd_from_bus(struct pci_bus *bus) +@@ -366,6 +382,9 @@ static void vmd_set_msi_remapping(struct vmd_dev *vmd, bool enable) + { + u16 reg; + ++ if (!!(vmd->features & VMD_FEAT_USE_BIOS_INFO)) ++ return; ++ + pci_read_config_word(vmd->dev, PCI_REG_VMCONFIG, ®); + reg = enable ? (reg & ~VMCONFIG_MSI_REMAP) : + (reg | VMCONFIG_MSI_REMAP); +@@ -389,11 +408,22 @@ static void vmd_remove_irq_domain(struct vmd_dev *vmd) + } + } + ++static unsigned int vmd_bus_to_ecam(struct vmd_dev *vmd, unsigned int busnr) ++{ ++ if (!!(vmd->features & VMD_FEAT_USE_BIOS_INFO)) ++ return busnr; ++ ++ return busnr - vmd->busn_start; ++} ++ + static void __iomem *vmd_cfg_addr(struct vmd_dev *vmd, struct pci_bus *bus, + unsigned int devfn, int reg, int len) + { +- unsigned int busnr_ecam = bus->number - vmd->busn_start; +- u32 offset = PCIE_ECAM_OFFSET(busnr_ecam, devfn, reg); ++ unsigned int busnr_ecam; ++ u32 offset; ++ ++ busnr_ecam = vmd_bus_to_ecam(vmd, bus->number); ++ offset = PCIE_ECAM_OFFSET(busnr_ecam, devfn, reg); + + if (offset + len >= resource_size(&vmd->dev->resource[VMD_CFGBAR])) + return NULL; +@@ -518,22 +548,37 @@ static inline void vmd_acpi_begin(void) { } + static inline void vmd_acpi_end(void) { } + #endif /* CONFIG_ACPI */ + ++static resource_size_t vmd_cfgbar_ecam_space(struct vmd_dev *vmd) ++{ ++ resource_size_t cfgbar_buses; ++ unsigned int ecam_start; ++ ++ cfgbar_buses = resource_size(&vmd->dev->resource[VMD_CFGBAR]) >> 20; ++ ecam_start = vmd_bus_to_ecam(vmd, vmd->resources[0].start); ++ if (ecam_start >= cfgbar_buses) ++ return 0; ++ ++ return cfgbar_buses - ecam_start; ++} + static void vmd_domain_reset(struct vmd_dev *vmd) + { + u16 bus, max_buses = resource_size(&vmd->resources[0]); + u8 dev, functions, fn, hdr_type; ++ unsigned int ecam_bus; + char __iomem *base; + ++ max_buses = min_t(u16, max_buses, vmd_cfgbar_ecam_space(vmd)); + for (bus = 0; bus < max_buses; bus++) { ++ ecam_bus = vmd_bus_to_ecam(vmd, vmd->resources[0].start + bus); + for (dev = 0; dev < 32; dev++) { +- base = vmd->cfgbar + PCIE_ECAM_OFFSET(bus, ++ base = vmd->cfgbar + PCIE_ECAM_OFFSET(ecam_bus, + PCI_DEVFN(dev, 0), 0); + + hdr_type = readb(base + PCI_HEADER_TYPE); + + functions = (hdr_type & PCI_HEADER_TYPE_MFD) ? 8 : 1; + for (fn = 0; fn < functions; fn++) { +- base = vmd->cfgbar + PCIE_ECAM_OFFSET(bus, ++ base = vmd->cfgbar + PCIE_ECAM_OFFSET(ecam_bus, + PCI_DEVFN(dev, fn), 0); + + hdr_type = readb(base + PCI_HEADER_TYPE) & +@@ -661,6 +706,46 @@ static int vmd_get_bus_number_start(struct vmd_dev *vmd) + return 0; + } + ++static int vmd_get_bus_info_from_bar4(struct vmd_dev *vmd, ++ resource_size_t *offset1, ++ resource_size_t *offset2) ++{ ++ u64 phys1, phys2, bar4_2840; ++ void __iomem *bar4; ++ u32 base_id; ++ u8 base_bus; ++ ++ bar4 = pci_ioremap_bar(vmd->dev, 4); ++ if (!bar4) ++ return -ENOMEM; ++ ++ /* Read shadow registers for MEMBAR1 and MEMBAR2 physical addresses */ ++ phys1 = readq(bar4 + SHADOW_MEMBAR1_28C1); ++ phys2 = readq(bar4 + SHADOW_MEMBAR2_28C1); ++ ++ /* ++ * Read and set bus start number from Base ID register. 24-bit Base ID ++ * register is part of 64-bit shadowed reqid hide range register and ++ * holds segment, bus, device and function. ++ */ ++ bar4_2840 = readq(bar4 + BASE_ID_REG_28C1); ++ base_id = bar4_2840 & 0xFFFFFF; ++ base_bus = base_id >> 8; ++ vmd->busn_start = base_bus; ++ ++ /* Calculate offsets like vmd_get_phys_offsets() does */ ++ if (phys1) ++ *offset1 = vmd->dev->resource[VMD_MEMBAR1].start - ++ (phys1 & PCI_BASE_ADDRESS_MEM_MASK); ++ if (phys2) ++ *offset2 = vmd->dev->resource[VMD_MEMBAR2].start - ++ (phys2 & PCI_BASE_ADDRESS_MEM_MASK); ++ ++ pci_iounmap(vmd->dev, bar4); ++ ++ return 0; ++} ++ + static irqreturn_t vmd_irq(int irq, void *data) + { + struct vmd_irq_list *irqs = data; +@@ -711,6 +796,52 @@ static int vmd_alloc_irqs(struct vmd_dev *vmd) + return 0; + } + ++static int vmd_prepare_offsets_and_bus(struct vmd_dev *vmd, ++ unsigned long features, ++ resource_size_t *membar2_offset, ++ resource_size_t *offset1, ++ resource_size_t *offset2) ++{ ++ int ret; ++ ++ /* ++ * Shadow registers may exist in certain VMD device IDs which allow ++ * guests to correctly assign host physical addresses to the root ports ++ * and child devices. These registers will either return the host value ++ * or 0, depending on an enable bit in the VMD device. ++ * ++ * For certain VMD devices (i.e. 0x28C1), BIOS places device info ++ * in BAR4 shadow registers to determine the base bus number and memory ++ * offsets. ++ */ ++ if (features & VMD_FEAT_USE_BIOS_INFO) { ++ *membar2_offset = MEMBAR2_OFFSET_28C1; ++ ret = vmd_get_bus_info_from_bar4(vmd, offset1, offset2); ++ if (ret) ++ return ret; ++ } else if (features & VMD_FEAT_HAS_MEMBAR_SHADOW) { ++ *membar2_offset = MB2_SHADOW_OFFSET + MB2_SHADOW_SIZE; ++ ret = vmd_get_phys_offsets(vmd, true, offset1, offset2); ++ if (ret) ++ return ret; ++ } else if (features & VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP) { ++ ret = vmd_get_phys_offsets(vmd, false, offset1, offset2); ++ if (ret) ++ return ret; ++ } ++ ++ /* ++ * Certain VMD devices may have a root port configuration option which ++ * limits the bus range to between 0-127, 128-255, or 224-255. ++ */ ++ if (features & VMD_FEAT_HAS_BUS_RESTRICTIONS) { ++ ret = vmd_get_bus_number_start(vmd); ++ if (ret) ++ return ret; ++ } ++ return 0; ++} ++ + /* + * Since VMD is an aperture to regular PCIe root ports, only allow it to + * control features that the OS is allowed to control on the physical PCI bus. +@@ -780,42 +911,24 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) + LIST_HEAD(resources); + resource_size_t offset[2] = {0}; + resource_size_t membar2_offset = 0x2000; ++ resource_size_t busn_end; + struct pci_bus *child; + struct pci_dev *dev; + int ret; + +- /* +- * Shadow registers may exist in certain VMD device ids which allow +- * guests to correctly assign host physical addresses to the root ports +- * and child devices. These registers will either return the host value +- * or 0, depending on an enable bit in the VMD device. +- */ +- if (features & VMD_FEAT_HAS_MEMBAR_SHADOW) { +- membar2_offset = MB2_SHADOW_OFFSET + MB2_SHADOW_SIZE; +- ret = vmd_get_phys_offsets(vmd, true, &offset[0], &offset[1]); +- if (ret) +- return ret; +- } else if (features & VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP) { +- ret = vmd_get_phys_offsets(vmd, false, &offset[0], &offset[1]); +- if (ret) +- return ret; +- } +- +- /* +- * Certain VMD devices may have a root port configuration option which +- * limits the bus range to between 0-127, 128-255, or 224-255 +- */ +- if (features & VMD_FEAT_HAS_BUS_RESTRICTIONS) { +- ret = vmd_get_bus_number_start(vmd); +- if (ret) +- return ret; +- } ++ ret = vmd_prepare_offsets_and_bus(vmd, features, &membar2_offset, ++ &offset[0], &offset[1]); ++ if (ret) ++ return ret; + ++ /* Do not let resource[0] end go out of bounds */ + res = &vmd->dev->resource[VMD_CFGBAR]; ++ busn_end = vmd->busn_start + (resource_size(res) >> 20) - 1; ++ busn_end = min_t(resource_size_t, busn_end, 0xff); + vmd->resources[0] = (struct resource) { + .name = "VMD CFGBAR", + .start = vmd->busn_start, +- .end = vmd->busn_start + (resource_size(res) >> 20) - 1, ++ .end = busn_end, + .flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED, + }; + +@@ -998,6 +1111,7 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id) + + vmd->dev = dev; + vmd->sysdata.domain = PCI_DOMAIN_NR_NOT_SET; ++ vmd->features = features; + vmd->instance = ida_alloc(&vmd_instance_ida, GFP_KERNEL); + if (vmd->instance < 0) + return vmd->instance; +@@ -1114,6 +1228,10 @@ static const struct pci_device_id vmd_ids[] = { + .driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW | + VMD_FEAT_HAS_BUS_RESTRICTIONS | + VMD_FEAT_CAN_BYPASS_MSI_REMAP,}, ++ {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_VMD_28C1), ++ .driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW | ++ VMD_FEAT_CAN_BYPASS_MSI_REMAP | ++ VMD_FEAT_USE_BIOS_INFO,}, + {PCI_VDEVICE(INTEL, 0x467f), + .driver_data = VMD_FEATS_CLIENT,}, + {PCI_VDEVICE(INTEL, 0x4c3d), +diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h +--- a/include/linux/pci_ids.h ++++ b/include/linux/pci_ids.h +@@ -2892,6 +2892,7 @@ + #define PCI_DEVICE_ID_INTEL_HDA_ICH8 0x284b + #define PCI_DEVICE_ID_INTEL_ICH8_6 0x2850 + #define PCI_DEVICE_ID_INTEL_VMD_28C0 0x28c0 ++#define PCI_DEVICE_ID_INTEL_VMD_28C1 0x28c1 + #define PCI_DEVICE_ID_INTEL_ICH9_0 0x2910 + #define PCI_DEVICE_ID_INTEL_ICH9_2 0x2912 + #define PCI_DEVICE_ID_INTEL_ICH9_3 0x2913 diff --git a/pkgbuilds/linux-omarchy/0703-pci-vmd-scan-bios-enumerated-devices.patch.sig b/pkgbuilds/linux-omarchy/0703-pci-vmd-scan-bios-enumerated-devices.patch.sig new file mode 100644 index 0000000..7e9f3d1 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0703-pci-vmd-scan-bios-enumerated-devices.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0704-pci-vmd-meteor-lake-mtl016-flush.patch b/pkgbuilds/linux-omarchy/0704-pci-vmd-meteor-lake-mtl016-flush.patch new file mode 100644 index 0000000..22be8d3 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0704-pci-vmd-meteor-lake-mtl016-flush.patch @@ -0,0 +1,120 @@ +diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c +--- a/drivers/pci/controller/vmd.c ++++ b/drivers/pci/controller/vmd.c +@@ -92,6 +92,22 @@ enum vmd_features { + * referred to as MEMBAR2 or MSI-X BAR. + */ + VMD_FEAT_USE_BIOS_INFO = (1 << 6), ++ ++ /* ++ * Meteor Lake VMD (device ID 0x7d0b) is affected by erratum MTL016: ++ * the VMD may signal its MSI before the posted writes that carry the ++ * child device's DMA data have landed in memory. The demuxed handler ++ * then runs against a not-yet-coherent completion queue and misses the ++ * completion entirely, so the I/O is only recovered when the block ++ * layer timeout fires and polls the queue ("timeout, completion ++ * polled"). Intel's documented workaround is to issue a dummy read ++ * to the MSI initiator (the child device) before handling the ++ * interrupt: the read completion cannot pass the device's earlier ++ * posted writes, so it pulls them into memory per PCIe ordering ++ * rules. A read that terminates at the VMD itself does not order ++ * against the child's writes and is not sufficient (measured). ++ */ ++ VMD_FEAT_INTERRUPT_QUIRK = (1 << 7), + }; + + #define VMD_BIOS_PM_QUIRK_LTR 0x1003 /* 3145728 ns */ +@@ -114,6 +130,9 @@ static DEFINE_RAW_SPINLOCK(list_lock); + * @irq: back pointer to parent. + * @enabled: true if driver enabled IRQ + * @virq: the virtual IRQ value provided to the requesting driver. ++ * @flush_addr: config space address of the initiating device, read before ++ * demuxing to flush its posted writes (MTL016); NULL if the ++ * VMD is not affected. + * + * Every MSI/MSI-X IRQ requested for a device in a VMD domain will be mapped to + * a VMD IRQ using this structure. +@@ -123,8 +142,14 @@ struct vmd_irq { + struct vmd_irq_list *irq; + bool enabled; + unsigned int virq; ++ void __iomem *flush_addr; + }; + ++struct vmd_dev; ++ ++static void __iomem *vmd_cfg_addr(struct vmd_dev *vmd, struct pci_bus *bus, ++ unsigned int devfn, int reg, int len); ++ + /** + * struct vmd_irq_list - list of driver requested IRQs mapping to a VMD vector + * @irq_list: the list of irq's the VMD one demuxes to. +@@ -132,12 +157,15 @@ struct vmd_irq { + * @count: number of child IRQs assigned to this vector; used to track + * sharing. + * @virq: The underlying VMD Linux interrupt number ++ * @vmd: back pointer to the owning VMD device; serializes the MTL016 ++ * flush read against other config space access. + */ + struct vmd_irq_list { + struct list_head irq_list; + struct srcu_struct srcu; + unsigned int count; + unsigned int virq; ++ struct vmd_dev *vmd; + }; + + struct vmd_dev { +@@ -295,6 +323,13 @@ static int vmd_msi_alloc(struct irq_domain *domain, unsigned int virq, + INIT_LIST_HEAD(&vmdirq->node); + vmdirq->irq = vmd_next_irq(vmd, desc); + vmdirq->virq = virq + i; ++ if (vmd->features & VMD_FEAT_INTERRUPT_QUIRK) { ++ struct pci_dev *pdev = msi_desc_to_pci_dev(desc); ++ ++ vmdirq->flush_addr = vmd_cfg_addr(vmd, pdev->bus, ++ pdev->devfn, ++ PCI_VENDOR_ID, 2); ++ } + + irq_domain_set_info(domain, virq + i, vmdirq->irq->virq, + &vmd_msi_controller, vmdirq, +@@ -753,8 +788,20 @@ static irqreturn_t vmd_irq(int irq, void *data) + int idx; + + idx = srcu_read_lock(&irqs->srcu); +- list_for_each_entry_rcu(vmdirq, &irqs->irq_list, node) ++ list_for_each_entry_rcu(vmdirq, &irqs->irq_list, node) { ++ /* ++ * MTL016: the MSI may have outrun the initiating device's ++ * posted writes (e.g. its NVMe completion entry). A read ++ * that completes at the initiator flushes them, so the ++ * demuxed handler observes a coherent completion queue. ++ * The value is discarded; only the ordering matters. ++ */ ++ if (vmdirq->flush_addr) { ++ guard(raw_spinlock)(&irqs->vmd->cfg_lock); ++ readw(vmdirq->flush_addr); ++ } + generic_handle_irq(vmdirq->virq); ++ } + srcu_read_unlock(&irqs->srcu, idx); + + return IRQ_HANDLED; +@@ -785,6 +832,7 @@ static int vmd_alloc_irqs(struct vmd_dev *vmd) + return err; + + INIT_LIST_HEAD(&vmd->irqs[i].irq_list); ++ vmd->irqs[i].vmd = vmd; + vmd->irqs[i].virq = pci_irq_vector(dev, i); + err = devm_request_irq(&dev->dev, vmd->irqs[i].virq, + vmd_irq, IRQF_NO_THREAD, +@@ -1239,7 +1287,7 @@ static const struct pci_device_id vmd_ids[] = { + {PCI_VDEVICE(INTEL, 0xa77f), + .driver_data = VMD_FEATS_CLIENT,}, + {PCI_VDEVICE(INTEL, 0x7d0b), +- .driver_data = VMD_FEATS_CLIENT,}, ++ .driver_data = VMD_FEATS_CLIENT | VMD_FEAT_INTERRUPT_QUIRK,}, + {PCI_VDEVICE(INTEL, 0xad0b), + .driver_data = VMD_FEATS_CLIENT,}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_VMD_9A0B), diff --git a/pkgbuilds/linux-omarchy/0704-pci-vmd-meteor-lake-mtl016-flush.patch.sig b/pkgbuilds/linux-omarchy/0704-pci-vmd-meteor-lake-mtl016-flush.patch.sig new file mode 100644 index 0000000..69bd67e Binary files /dev/null and b/pkgbuilds/linux-omarchy/0704-pci-vmd-meteor-lake-mtl016-flush.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0705-pci-toshiba-ahci-ssd-dma-alias-quirk.patch b/pkgbuilds/linux-omarchy/0705-pci-toshiba-ahci-ssd-dma-alias-quirk.patch new file mode 100644 index 0000000..49806cf --- /dev/null +++ b/pkgbuilds/linux-omarchy/0705-pci-toshiba-ahci-ssd-dma-alias-quirk.patch @@ -0,0 +1,13 @@ +diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c +--- a/drivers/pci/quirks.c ++++ b/drivers/pci/quirks.c +@@ -4370,6 +4370,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_JMICRON, + DECLARE_PCI_FIXUP_HEADER(0x1c28, /* Lite-On */ + 0x0122, /* Plextor M6E (Marvell 88SS9183)*/ + quirk_dma_func1_alias); ++/* Toshiba AHCI SSD controller in MacBookAir6,1 and MacBookAir7,1 */ ++DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TOSHIBA, 0x010b, ++ quirk_dma_func1_alias); + + /* + * Some devices DMA with the wrong devfn, not just the wrong function. diff --git a/pkgbuilds/linux-omarchy/0705-pci-toshiba-ahci-ssd-dma-alias-quirk.patch.sig b/pkgbuilds/linux-omarchy/0705-pci-toshiba-ahci-ssd-dma-alias-quirk.patch.sig new file mode 100644 index 0000000..ab588ee Binary files /dev/null and b/pkgbuilds/linux-omarchy/0705-pci-toshiba-ahci-ssd-dma-alias-quirk.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0740-hwmon-updates.patch b/pkgbuilds/linux-omarchy/0740-hwmon-updates.patch new file mode 100644 index 0000000..a66a783 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0740-hwmon-updates.patch @@ -0,0 +1,7419 @@ +diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig +--- a/drivers/hwmon/Kconfig ++++ b/drivers/hwmon/Kconfig +@@ -224,6 +224,7 @@ config SENSORS_ADT7462 + config SENSORS_ADT7470 + tristate "Analog Devices ADT7470" + depends on I2C ++ depends on PWM || PWM=n + select REGMAP_I2C + help + If you say yes here you get support for the Analog Devices +@@ -292,11 +293,11 @@ config SENSORS_ASC7621 + will be called asc7621. + + config SENSORS_ASUS_ROG_RYUJIN +- tristate "ASUS ROG RYUJIN II 360 hardware monitoring driver" ++ tristate "ASUS ROG RYUJIN hardware monitoring driver" + depends on HID + help + If you say yes here you get support for the fans and sensors of +- the ASUS ROG RYUJIN II 360 AIO CPU liquid cooler. ++ supported ASUS ROG RYUJIN II and III AIO CPU liquid coolers. + + This driver can also be built as a module. If so, the module + will be called asus_rog_ryujin. +@@ -335,6 +336,17 @@ config SENSORS_K10TEMP + This driver can also be built as a module. If so, the module + will be called k10temp. + ++config SENSORS_KB9002 ++ tristate "Kandou KB9002 PCIe retimer" ++ depends on I2C ++ help ++ If you say yes here you get support for the integrated ++ temperature sensor and firmware version readout on Kandou ++ KB9002 PCIe 5.0 retimers, accessed over SMBus. ++ ++ This driver can also be built as a module. If so, the module ++ will be called kb9002. ++ + config SENSORS_KBATT + tristate "KEBA battery controller support" + depends on KEBA_CP500 +@@ -403,7 +415,6 @@ config SENSORS_ARCTIC_FAN_CONTROLLER + config SENSORS_ARM_SCMI + tristate "ARM SCMI Sensors" + depends on ARM_SCMI_PROTOCOL +- depends on THERMAL || !THERMAL_OF + help + This driver provides support for temperature, voltage, current + and power sensors available on SCMI based platforms. The actual +@@ -415,7 +426,6 @@ config SENSORS_ARM_SCMI + config SENSORS_ARM_SCPI + tristate "ARM SCPI Sensors" + depends on ARM_SCPI_PROTOCOL +- depends on THERMAL || !THERMAL_OF + help + This driver provides support for temperature, voltage, current + and power sensors available on ARM Ltd's SCP based platforms. The +@@ -435,7 +445,6 @@ config SENSORS_ASB100 + config SENSORS_ASPEED + tristate "ASPEED AST2400/AST2500 PWM and Fan tach driver" + depends on ARCH_ASPEED || COMPILE_TEST +- depends on THERMAL || THERMAL=n + select REGMAP + help + This driver provides support for ASPEED AST2400/AST2500 PWM +@@ -506,8 +515,8 @@ config SENSORS_CORSAIR_PSU + If you say yes here you get support for Corsair PSUs with a HID + interface. + Currently this driver supports the (RM/HX)550i, (RM/HX)650i, +- (RM/HX)750i, (RM/HX)850i, (RM/HX)1000i and HX1200i power supplies +- by Corsair. ++ (RM/HX)750i, (RM/HX)850i, (RM/HX)1000i, HX1200i and HX1500i power ++ supplies by Corsair. + + This driver can also be built as a module. If so, the module + will be called corsair-psu. +@@ -788,7 +797,6 @@ config SENSORS_G762 + config SENSORS_GPIO_FAN + tristate "GPIO fan" + depends on GPIOLIB || COMPILE_TEST +- depends on THERMAL || THERMAL=n + help + If you say yes here you get support for fans connected to GPIO lines. + +@@ -802,7 +810,7 @@ config SENSORS_GXP_FAN_CTRL + If you say yes here you get support for GXP fan control functionality. + + The GXP controls fan function via the CPLD through the use of PWM +- registers. This driver reports status and pwm setting of the fans. ++ registers. This driver enables pwm setting of the fans. + + config SENSORS_HIH6130 + tristate "Honeywell Humidicon HIH-6130 humidity/temperature sensor" +@@ -942,6 +950,19 @@ config SENSORS_JC42 + This driver can also be built as a module. If so, the module + will be called jc42. + ++config SENSORS_POLARFIRE_SOC_TVS ++ tristate "PolarFire SoC (MPFS) temperature and voltage sensor" ++ depends on POLARFIRE_SOC_SYSCONS || COMPILE_TEST ++ depends on MFD_SYSCON ++ help ++ This driver adds support for the PolarFire SoC (MPFS) Temperature and ++ Voltage Sensor. ++ ++ To compile this driver as a module, choose M here. the ++ module will be called tvs-mpfs. ++ ++ If unsure, say N. ++ + config SENSORS_POWERZ + tristate "ChargerLAB POWER-Z USB-C tester" + depends on USB +@@ -1357,7 +1378,6 @@ config SENSORS_MAX6639 + config SENSORS_MAX6650 + tristate "Maxim MAX6650 sensor chip" + depends on I2C +- depends on THERMAL || THERMAL=n + help + If you say yes here you get support for the MAX6650 / MAX6651 + sensor chips. +@@ -1732,7 +1752,6 @@ config SENSORS_PC87427 + config SENSORS_NTC_THERMISTOR + tristate "NTC thermistor support" + depends on IIO +- depends on THERMAL || !THERMAL_OF + help + This driver supports NTC thermistors sensor reading and its + interpretation. The driver can also monitor the temperature and +@@ -1933,7 +1952,6 @@ config SENSORS_PT5161L + config SENSORS_PWM_FAN + tristate "PWM fan" + depends on PWM || COMPILE_TEST +- depends on THERMAL || THERMAL=n + help + If you say yes here you get support for fans connected to PWM lines. + The driver uses the generic PWM interface, thus it will work on a +@@ -1945,7 +1963,6 @@ config SENSORS_PWM_FAN + config SENSORS_QNAP_MCU_HWMON + tristate "QNAP MCU hardware monitoring" + depends on MFD_QNAP_MCU +- depends on THERMAL || THERMAL=n + help + Say yes here to enable support for fan and temperature sensor + connected to a QNAP MCU, as found in a number of QNAP network +@@ -2010,8 +2027,9 @@ config SENSORS_SHT3x + depends on I2C + select CRC8 + help +- If you say yes here you get support for the Sensiron SHT30, SHT31 and +- SHT85 humidity and temperature sensors. ++ If you say yes here you get support for the Sensiron SHT30, SHT31, ++ SHT85 and the compatible GXCAS GXHT30 humidity and temperature ++ sensors. + + This driver can also be built as a module. If so, the module + will be called sht3x. +@@ -2070,6 +2088,17 @@ config SENSORS_DME1737 + This driver can also be built as a module. If so, the module + will be called dme1737. + ++config SENSORS_EIC7700_PVT ++ tristate "Eswin EIC7700 Voltage, Temperature sensor driver" ++ depends on ARCH_ESWIN || COMPILE_TEST ++ select POLYNOMIAL ++ help ++ If you say yes here you get support for Eswin EIC7700 PVT sensor ++ embedded into the SoC. ++ ++ This driver can also be built as a module. If so, the module will be ++ called eic7700-pvt. ++ + config SENSORS_EMC1403 + tristate "SMSC EMC1403/23 thermal sensor" + depends on I2C +diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile +--- a/drivers/hwmon/Makefile ++++ b/drivers/hwmon/Makefile +@@ -72,6 +72,7 @@ obj-$(CONFIG_SENSORS_DME1737) += dme1737.o + obj-$(CONFIG_SENSORS_DRIVETEMP) += drivetemp.o + obj-$(CONFIG_SENSORS_DS620) += ds620.o + obj-$(CONFIG_SENSORS_DS1621) += ds1621.o ++obj-$(CONFIG_SENSORS_EIC7700_PVT) += eic7700-pvt.o + obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o + obj-$(CONFIG_SENSORS_EMC1812) += emc1812.o + obj-$(CONFIG_SENSORS_EMC2103) += emc2103.o +@@ -112,6 +113,7 @@ obj-$(CONFIG_SENSORS_IT87) += it87.o + obj-$(CONFIG_SENSORS_JC42) += jc42.o + obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o + obj-$(CONFIG_SENSORS_K10TEMP) += k10temp.o ++obj-$(CONFIG_SENSORS_KB9002) += kb9002.o + obj-$(CONFIG_SENSORS_KBATT) += kbatt.o + obj-$(CONFIG_SENSORS_KFAN) += kfan.o + obj-$(CONFIG_SENSORS_LAN966X) += lan966x-hwmon.o +@@ -197,6 +199,7 @@ obj-$(CONFIG_SENSORS_NZXT_SMART2) += nzxt-smart2.o + obj-$(CONFIG_SENSORS_PC87360) += pc87360.o + obj-$(CONFIG_SENSORS_PC87427) += pc87427.o + obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o ++obj-$(CONFIG_SENSORS_POLARFIRE_SOC_TVS) += tvs-mpfs.o + obj-$(CONFIG_SENSORS_POWERZ) += powerz.o + obj-$(CONFIG_SENSORS_POWR1220) += powr1220.o + obj-$(CONFIG_SENSORS_PROM21_XHCI) += prom21-xhci.o +diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c +--- a/drivers/hwmon/acpi_power_meter.c ++++ b/drivers/hwmon/acpi_power_meter.c +@@ -21,8 +21,6 @@ + #include + + #define ACPI_POWER_METER_NAME "power_meter" +-#define ACPI_POWER_METER_DEVICE_NAME "Power Meter" +-#define ACPI_POWER_METER_CLASS "pwr_meter_resource" + + #define NUM_SENSORS 17 + +@@ -877,7 +875,7 @@ static void acpi_power_meter_notify(acpi_handle handle, u32 event, void *data) + break; + } + +- acpi_bus_generate_netlink_event(ACPI_POWER_METER_CLASS, ++ acpi_bus_generate_netlink_event("pwr_meter_resource", + dev_name(&resource->acpi_dev->dev), + event, 0); + } +@@ -899,8 +897,6 @@ static int acpi_power_meter_probe(struct platform_device *pdev) + resource->sensors_valid = 0; + resource->acpi_dev = device; + mutex_init(&resource->lock); +- strscpy(acpi_device_name(device), ACPI_POWER_METER_DEVICE_NAME); +- strscpy(acpi_device_class(device), ACPI_POWER_METER_CLASS); + + platform_set_drvdata(pdev, resource); + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -22,6 +22,9 @@ + #include + #include + #include ++#include ++#include ++#include + + /* Addresses to scan */ + static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; +@@ -100,6 +103,7 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; + #define ADT7470_REG_FAN_MAX(x) (ADT7470_REG_FAN_MAX_BASE_ADDR + ((x) * 2)) + + #define ADT7470_PWM_COUNT 4 ++#define ADT7470_PWM_MAX 255 + #define ADT7470_REG_PWM(x) (ADT7470_REG_PWM_BASE_ADDR + (x)) + #define ADT7470_REG_PWM_MAX(x) (ADT7470_REG_PWM_MAX_BASE_ADDR + (x)) + #define ADT7470_REG_PWM_MIN(x) (ADT7470_REG_PWM_MIN_BASE_ADDR + (x)) +@@ -163,6 +167,7 @@ struct adt7470_data { + char limits_valid; + unsigned long sensors_last_updated; /* In jiffies */ + unsigned long limits_last_updated; /* In jiffies */ ++ bool use_pwm_framework; + + int num_temp_sensors; /* -1 = probe */ + int temperatures_probed; +@@ -850,7 +855,7 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val + + switch (attr) { + case hwmon_pwm_input: +- val = clamp_val(val, 0, 255); ++ val = clamp_val(val, 0, ADT7470_PWM_MAX); + mutex_lock(&data->lock); + err = regmap_write(data->regmap, ADT7470_REG_PWM(channel), + val); +@@ -886,6 +891,133 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val + return err; + } + ++struct adt7470_pwm_wfhw { ++ u8 val; ++}; ++ ++static int adt7470_pwm_round_waveform_tohw(struct pwm_chip *chip, ++ struct pwm_device *pwm, ++ const struct pwm_waveform *wf, ++ void *_wfhw) ++{ ++ struct adt7470_data *data = pwmchip_get_drvdata(chip); ++ struct adt7470_pwm_wfhw *wfhw = _wfhw; ++ u64 actual_period; ++ ++ if (wf->duty_length_ns == 0) { ++ wfhw->val = 0; ++ return 0; ++ } ++ ++ /* ++ * The PWM frequency (period) is a single chip-wide setting shared by ++ * all 4 channels, so it cannot be changed on a per-pwm_device basis ++ * through this API. The duty cycle is rounded against the currently ++ * configured hardware period rather than the period requested in ++ * @wf; round_waveform_fromhw() reports the actual resulting ++ * waveform back so the core/consumer can detect a mismatch. ++ */ ++ actual_period = DIV_ROUND_UP_ULL(NSEC_PER_SEC, data->pwm_freq); ++ ++ if (actual_period > wf->period_length_ns) ++ /* period too short */ ++ return 1; ++ ++ if (wf->duty_length_ns >= actual_period) { ++ wfhw->val = ADT7470_PWM_MAX; ++ } else { ++ wfhw->val = mul_u64_u64_div_u64(wf->duty_length_ns, ++ ADT7470_PWM_MAX, ++ actual_period); ++ } ++ ++ return 0; ++} ++ ++static int adt7470_pwm_round_waveform_fromhw(struct pwm_chip *chip, ++ struct pwm_device *pwm, ++ const void *_wfhw, ++ struct pwm_waveform *wf) ++{ ++ struct adt7470_data *data = pwmchip_get_drvdata(chip); ++ const struct adt7470_pwm_wfhw *wfhw = _wfhw; ++ ++ wf->period_length_ns = DIV_ROUND_UP_ULL(NSEC_PER_SEC, data->pwm_freq); ++ wf->duty_offset_ns = 0; ++ wf->duty_length_ns = DIV_ROUND_UP_ULL((u64)wfhw->val * wf->period_length_ns, ++ ADT7470_PWM_MAX); ++ return 0; ++} ++ ++static int adt7470_pwm_read_waveform(struct pwm_chip *chip, ++ struct pwm_device *pwm, ++ void *_wfhw) ++{ ++ struct adt7470_data *data = pwmchip_get_drvdata(chip); ++ struct device *dev = regmap_get_device(data->regmap); ++ struct adt7470_pwm_wfhw *wfhw = _wfhw; ++ ++ data = adt7470_update_device(dev); ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ ++ /* ++ * No lock needed: like the other hwmon_ops read callbacks in this ++ * driver (e.g. adt7470_pwm_read()), this only does a single byte ++ * read from the cache populated by adt7470_update_device(). ++ */ ++ wfhw->val = data->pwm[pwm->hwpwm]; ++ ++ return 0; ++} ++ ++static int adt7470_pwm_write_waveform(struct pwm_chip *chip, ++ struct pwm_device *pwm, ++ const void *_wfhw) ++{ ++ struct adt7470_data *data = pwmchip_get_drvdata(chip); ++ const struct adt7470_pwm_wfhw *wfhw = _wfhw; ++ unsigned int pwm_auto_reg_mask; ++ int err; ++ ++ if (pwm->hwpwm % 2) ++ pwm_auto_reg_mask = ADT7470_PWM2_AUTO_MASK; ++ else ++ pwm_auto_reg_mask = ADT7470_PWM1_AUTO_MASK; ++ ++ guard(mutex)(&data->lock); ++ ++ if (data->pwm[pwm->hwpwm] == wfhw->val && ++ data->pwm_automatic[pwm->hwpwm] == 0) ++ return 0; ++ ++ /* Put the PWM channel in manual mode before updating it. */ ++ err = regmap_update_bits(data->regmap, ++ ADT7470_REG_PWM_CFG(pwm->hwpwm), ++ pwm_auto_reg_mask, 0); ++ if (err < 0) ++ return err; ++ ++ data->pwm_automatic[pwm->hwpwm] = 0; ++ ++ err = regmap_write(data->regmap, ++ ADT7470_REG_PWM(pwm->hwpwm), wfhw->val); ++ if (err < 0) ++ return err; ++ ++ data->pwm[pwm->hwpwm] = wfhw->val; ++ ++ return 0; ++} ++ ++static const struct pwm_ops adt7470_pwm_ops = { ++ .sizeof_wfhw = sizeof(struct adt7470_pwm_wfhw), ++ .round_waveform_tohw = adt7470_pwm_round_waveform_tohw, ++ .round_waveform_fromhw = adt7470_pwm_round_waveform_fromhw, ++ .read_waveform = adt7470_pwm_read_waveform, ++ .write_waveform = adt7470_pwm_write_waveform, ++}; ++ + static ssize_t pwm_max_show(struct device *dev, + struct device_attribute *devattr, char *buf) + { +@@ -910,7 +1042,7 @@ static ssize_t pwm_max_store(struct device *dev, + if (kstrtol(buf, 10, &temp)) + return -EINVAL; + +- temp = clamp_val(temp, 0, 255); ++ temp = clamp_val(temp, 0, ADT7470_PWM_MAX); + + mutex_lock(&data->lock); + data->pwm_max[attr->index] = temp; +@@ -945,7 +1077,7 @@ static ssize_t pwm_min_store(struct device *dev, + if (kstrtol(buf, 10, &temp)) + return -EINVAL; + +- temp = clamp_val(temp, 0, 255); ++ temp = clamp_val(temp, 0, ADT7470_PWM_MAX); + + mutex_lock(&data->lock); + data->pwm_min[attr->index] = temp; +@@ -1106,6 +1238,10 @@ static struct attribute *adt7470_attrs[] = { + &dev_attr_alarm_mask.attr, + &dev_attr_num_temp_sensors.attr, + &dev_attr_auto_update_interval.attr, ++ NULL ++}; ++ ++static struct attribute *adt7470_pwm_attrs[] = { + &sensor_dev_attr_force_pwm_max.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, +@@ -1130,7 +1266,32 @@ static struct attribute *adt7470_attrs[] = { + NULL + }; + +-ATTRIBUTE_GROUPS(adt7470); ++static const struct attribute_group adt7470_group = { ++ .attrs = adt7470_attrs, ++}; ++ ++static umode_t adt7470_pwm_is_visible(struct kobject *kobj, ++ struct attribute *attr, int index) ++{ ++ struct device *dev = kobj_to_dev(kobj); ++ struct adt7470_data *data = dev_get_drvdata(dev); ++ ++ if (data->use_pwm_framework) ++ return 0; ++ ++ return attr->mode; ++} ++ ++static const struct attribute_group adt7470_pwm_group = { ++ .attrs = adt7470_pwm_attrs, ++ .is_visible = adt7470_pwm_is_visible, ++}; ++ ++static const struct attribute_group *adt7470_groups[] = { ++ &adt7470_group, ++ &adt7470_pwm_group, ++ NULL, ++}; + + static int adt7470_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long *val) +@@ -1165,6 +1326,7 @@ static int adt7470_write(struct device *dev, enum hwmon_sensor_types type, u32 a + static umode_t adt7470_is_visible(const void *_data, enum hwmon_sensor_types type, + u32 attr, int channel) + { ++ const struct adt7470_data *data = _data; + umode_t mode = 0; + + switch (type) { +@@ -1197,6 +1359,14 @@ static umode_t adt7470_is_visible(const void *_data, enum hwmon_sensor_types typ + } + break; + case hwmon_pwm: ++ /* Hide all pwm attributes if this device is exposed to the PWM ++ * framework ++ */ ++ if (data->use_pwm_framework) { ++ mode = 0; ++ break; ++ } ++ + switch (attr) { + case hwmon_pwm_input: + case hwmon_pwm_enable: +@@ -1226,6 +1396,8 @@ static const struct hwmon_ops adt7470_hwmon_ops = { + }; + + static const struct hwmon_channel_info * const adt7470_info[] = { ++ HWMON_CHANNEL_INFO(chip, ++ HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, +@@ -1327,6 +1499,29 @@ static int adt7470_probe(struct i2c_client *client) + + data->pwm_freq = (u32)freq_val; + ++ data->use_pwm_framework = false; ++ ++ if (device_property_present(dev, "#pwm-cells")) { ++ if (IS_REACHABLE(CONFIG_PWM)) { ++ struct pwm_chip *chip; ++ ++ chip = devm_pwmchip_alloc(dev, ADT7470_PWM_COUNT, 0); ++ if (IS_ERR(chip)) ++ return PTR_ERR(chip); ++ ++ chip->ops = &adt7470_pwm_ops; ++ pwmchip_set_drvdata(chip, data); ++ ++ err = devm_pwmchip_add(dev, chip); ++ if (err) ++ return dev_err_probe(dev, err, "failed to register PWM chip\n"); ++ ++ data->use_pwm_framework = true; ++ } else { ++ dev_warn(dev, "#pwm-cells present but CONFIG_PWM disabled. HWMON PWM attributes not hidden.\n"); ++ } ++ } ++ + /* Register sysfs hooks */ + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + &adt7470_chip_info, +diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c +--- a/drivers/hwmon/applesmc.c ++++ b/drivers/hwmon/applesmc.c +@@ -33,6 +33,7 @@ + #include + #include + #include ++#include + + /* data port used by Apple SMC */ + #define APPLESMC_DATA_PORT 0x300 +@@ -133,6 +134,7 @@ static struct applesmc_registers { + bool init_complete; /* true when fully initialized */ + struct applesmc_entry *cache; /* cached key entries */ + const char **index; /* temperature key index */ ++ char fan_positions[10][17]; /* cached fan position labels */ + } smcreg = { + .mutex = __MUTEX_INITIALIZER(smcreg.mutex), + }; +@@ -143,8 +145,8 @@ static s16 rest_x; + static s16 rest_y; + static u8 backlight_state[2]; + +-static struct device *hwmon_dev; + static struct input_dev *applesmc_idev; ++static struct device *hwmon_dev; + + /* + * Last index written to key_at_index sysfs file, and value to use for all other +@@ -372,7 +374,8 @@ static const struct applesmc_entry *applesmc_get_entry_by_index(int index) + __be32 be; + int ret = 0; + +- if (cache->valid) ++ /* Pairs with smp_store_release() to ensure cache contents are visible */ ++ if (smp_load_acquire(&cache->valid)) + return cache; + + mutex_lock(&smcreg.mutex); +@@ -391,7 +394,8 @@ static const struct applesmc_entry *applesmc_get_entry_by_index(int index) + cache->len = info[0]; + memcpy(cache->type, &info[1], 4); + cache->flags = info[5]; +- cache->valid = true; ++ /* Pairs with smp_load_acquire() to commit cache contents before setting valid */ ++ smp_store_release(&cache->valid, true); + + out: + mutex_unlock(&smcreg.mutex); +@@ -566,7 +570,7 @@ static int applesmc_init_smcreg_try(void) + { + struct applesmc_registers *s = &smcreg; + bool left_light_sensor = false, right_light_sensor = false; +- unsigned int count; ++ unsigned int count, i; + u8 tmp[1]; + int ret; + +@@ -597,6 +601,16 @@ static int applesmc_init_smcreg_try(void) + if (s->fan_count > 10) + s->fan_count = 10; + ++ for (i = 0; i < s->fan_count; i++) { ++ char newkey[5]; ++ ++ scnprintf(newkey, sizeof(newkey), FAN_ID_FMT, i); ++ ret = applesmc_read_key(newkey, s->fan_positions[i], 16); ++ s->fan_positions[i][16] = 0; ++ if (ret) ++ scnprintf(s->fan_positions[i], 17, " Fan %d", i); ++ } ++ + ret = applesmc_get_lower_bound(&s->temp_begin, "T"); + if (ret) + return ret; +@@ -808,33 +822,6 @@ static ssize_t applesmc_light_show(struct device *dev, + return sysfs_emit(sysfsbuf, "(%d,%d)\n", left, right); + } + +-/* Displays sensor key as label */ +-static ssize_t applesmc_show_sensor_label(struct device *dev, +- struct device_attribute *devattr, char *sysfsbuf) +-{ +- const char *key = smcreg.index[to_index(devattr)]; +- +- return sysfs_emit(sysfsbuf, "%s\n", key); +-} +- +-/* Displays degree Celsius * 1000 */ +-static ssize_t applesmc_show_temperature(struct device *dev, +- struct device_attribute *devattr, char *sysfsbuf) +-{ +- const char *key = smcreg.index[to_index(devattr)]; +- int ret; +- s16 value; +- int temp; +- +- ret = applesmc_read_s16(key, &value); +- if (ret) +- return ret; +- +- temp = 250 * (value >> 6); +- +- return sysfs_emit(sysfsbuf, "%d\n", temp); +-} +- + static ssize_t applesmc_show_fan_speed(struct device *dev, + struct device_attribute *attr, char *sysfsbuf) + { +@@ -854,99 +841,6 @@ static ssize_t applesmc_show_fan_speed(struct device *dev, + return sysfs_emit(sysfsbuf, "%u\n", speed); + } + +-static ssize_t applesmc_store_fan_speed(struct device *dev, +- struct device_attribute *attr, +- const char *sysfsbuf, size_t count) +-{ +- int ret; +- unsigned long speed; +- char newkey[5]; +- u8 buffer[2]; +- +- if (kstrtoul(sysfsbuf, 10, &speed) < 0 || speed >= 0x4000) +- return -EINVAL; /* Bigger than a 14-bit value */ +- +- scnprintf(newkey, sizeof(newkey), fan_speed_fmt[to_option(attr)], +- to_index(attr)); +- +- buffer[0] = (speed >> 6) & 0xff; +- buffer[1] = (speed << 2) & 0xff; +- ret = applesmc_write_key(newkey, buffer, 2); +- +- if (ret) +- return ret; +- else +- return count; +-} +- +-static ssize_t applesmc_show_fan_manual(struct device *dev, +- struct device_attribute *attr, char *sysfsbuf) +-{ +- int ret; +- u16 manual = 0; +- u8 buffer[2]; +- +- ret = applesmc_read_key(FANS_MANUAL, buffer, 2); +- if (ret) +- return ret; +- +- manual = ((buffer[0] << 8 | buffer[1]) >> to_index(attr)) & 0x01; +- return sysfs_emit(sysfsbuf, "%d\n", manual); +-} +- +-static ssize_t applesmc_store_fan_manual(struct device *dev, +- struct device_attribute *attr, +- const char *sysfsbuf, size_t count) +-{ +- int ret; +- u8 buffer[2]; +- unsigned long input; +- u16 val; +- +- if (kstrtoul(sysfsbuf, 10, &input) < 0) +- return -EINVAL; +- +- ret = applesmc_read_key(FANS_MANUAL, buffer, 2); +- if (ret) +- goto out; +- +- val = (buffer[0] << 8 | buffer[1]); +- +- if (input) +- val = val | (0x01 << to_index(attr)); +- else +- val = val & ~(0x01 << to_index(attr)); +- +- buffer[0] = (val >> 8) & 0xFF; +- buffer[1] = val & 0xFF; +- +- ret = applesmc_write_key(FANS_MANUAL, buffer, 2); +- +-out: +- if (ret) +- return ret; +- else +- return count; +-} +- +-static ssize_t applesmc_show_fan_position(struct device *dev, +- struct device_attribute *attr, char *sysfsbuf) +-{ +- int ret; +- char newkey[5]; +- u8 buffer[17]; +- +- scnprintf(newkey, sizeof(newkey), FAN_ID_FMT, to_index(attr)); +- +- ret = applesmc_read_key(newkey, buffer, 16); +- buffer[16] = 0; +- +- if (ret) +- return ret; +- +- return sysfs_emit(sysfsbuf, "%s\n", buffer + 4); +-} +- + static ssize_t applesmc_calibrate_show(struct device *dev, + struct device_attribute *attr, char *sysfsbuf) + { +@@ -1094,22 +988,7 @@ static struct applesmc_node_group light_sensor_group[] = { + { } + }; + +-static struct applesmc_node_group fan_group[] = { +- { "fan%d_label", applesmc_show_fan_position }, +- { "fan%d_input", applesmc_show_fan_speed, NULL, 0 }, +- { "fan%d_min", applesmc_show_fan_speed, applesmc_store_fan_speed, 1 }, +- { "fan%d_max", applesmc_show_fan_speed, NULL, 2 }, +- { "fan%d_safe", applesmc_show_fan_speed, NULL, 3 }, +- { "fan%d_output", applesmc_show_fan_speed, applesmc_store_fan_speed, 4 }, +- { "fan%d_manual", applesmc_show_fan_manual, applesmc_store_fan_manual }, +- { } +-}; + +-static struct applesmc_node_group temp_group[] = { +- { "temp%d_label", applesmc_show_sensor_label }, +- { "temp%d_input", applesmc_show_temperature }, +- { } +-}; + + /* Module stuff */ + +@@ -1312,8 +1191,238 @@ static const struct dmi_system_id applesmc_whitelist[] __initconst = { + }; + MODULE_DEVICE_TABLE(dmi, applesmc_whitelist); + ++static struct applesmc_dev_attr *fan_safe_attrs; ++static struct attribute **fan_safe_attr_list; ++static struct attribute_group fan_safe_group; ++static const struct attribute_group *applesmc_extra_groups[2]; ++ ++static u32 *applesmc_temp_config; ++static u32 *applesmc_fan_config; ++static u32 *applesmc_pwm_config; ++static struct hwmon_channel_info *applesmc_info_temp; ++static struct hwmon_channel_info *applesmc_info_fan; ++static struct hwmon_channel_info *applesmc_info_pwm; ++static const struct hwmon_channel_info **applesmc_info_arr; ++static struct hwmon_chip_info *applesmc_chip; ++ ++static void applesmc_free_hwmon(void) ++{ ++ kfree(applesmc_temp_config); ++ kfree(applesmc_fan_config); ++ kfree(applesmc_pwm_config); ++ kfree(applesmc_info_temp); ++ kfree(applesmc_info_fan); ++ kfree(applesmc_info_pwm); ++ kfree(applesmc_info_arr); ++ kfree(applesmc_chip); ++ kfree(fan_safe_attrs); ++ kfree(fan_safe_attr_list); ++} ++ ++static umode_t applesmc_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type, ++ u32 attr, int channel) ++{ ++ switch (type) { ++ case hwmon_temp: ++ if (attr == hwmon_temp_input || attr == hwmon_temp_label) ++ return 0444; ++ break; ++ case hwmon_fan: ++ switch (attr) { ++ case hwmon_fan_input: ++ case hwmon_fan_label: ++ case hwmon_fan_max: ++ return 0444; ++ case hwmon_fan_min: ++ case hwmon_fan_target: ++ return 0644; ++ default: ++ break; ++ } ++ break; ++ case hwmon_pwm: ++ if (attr == hwmon_pwm_enable) ++ return 0644; ++ break; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++static int applesmc_hwmon_read(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, long *val) ++{ ++ int ret; ++ ++ switch (type) { ++ case hwmon_temp: ++ if (attr == hwmon_temp_input) { ++ const char *key = smcreg.index[channel]; ++ s16 value; ++ ++ ret = applesmc_read_s16(key, &value); ++ if (ret) ++ return ret; ++ *val = 250 * (value >> 6); ++ return 0; ++ } ++ break; ++ case hwmon_fan: ++ switch (attr) { ++ case hwmon_fan_input: { ++ char key[5]; ++ u8 buffer[2]; ++ ++ scnprintf(key, sizeof(key), "F%dAc", channel); ++ ret = applesmc_read_key(key, buffer, 2); ++ if (ret) ++ return ret; ++ *val = ((buffer[0] << 8 | buffer[1]) >> 2); ++ return 0; ++ } ++ case hwmon_fan_min: { ++ char key[5]; ++ u8 buffer[2]; ++ ++ scnprintf(key, sizeof(key), "F%dMn", channel); ++ ret = applesmc_read_key(key, buffer, 2); ++ if (ret) ++ return ret; ++ *val = ((buffer[0] << 8 | buffer[1]) >> 2); ++ return 0; ++ } ++ case hwmon_fan_max: { ++ char key[5]; ++ u8 buffer[2]; ++ ++ scnprintf(key, sizeof(key), "F%dMx", channel); ++ ret = applesmc_read_key(key, buffer, 2); ++ if (ret) ++ return ret; ++ *val = ((buffer[0] << 8 | buffer[1]) >> 2); ++ return 0; ++ } ++ case hwmon_fan_target: { ++ char key[5]; ++ u8 buffer[2]; ++ ++ scnprintf(key, sizeof(key), "F%dTg", channel); ++ ret = applesmc_read_key(key, buffer, 2); ++ if (ret) ++ return ret; ++ *val = ((buffer[0] << 8 | buffer[1]) >> 2); ++ return 0; ++ } ++ default: ++ break; ++ } ++ break; ++ case hwmon_pwm: ++ if (attr == hwmon_pwm_enable) { ++ u8 buffer[2]; ++ ++ ret = applesmc_read_key(FANS_MANUAL, buffer, 2); ++ if (ret) ++ return ret; ++ *val = (((buffer[0] << 8 | buffer[1]) >> channel) & 0x01) ? 1 : 2; ++ return 0; ++ } ++ break; ++ default: ++ break; ++ } ++ return -EOPNOTSUPP; ++} ++ ++static int applesmc_hwmon_write(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, long val) ++{ ++ int ret; ++ ++ switch (type) { ++ case hwmon_fan: ++ if (attr == hwmon_fan_min || attr == hwmon_fan_target) { ++ char key[5]; ++ u8 buffer[2]; ++ const char *fmt = (attr == hwmon_fan_min) ? "F%dMn" : "F%dTg"; ++ ++ if (val < 0 || val >= 0x4000) ++ return -EINVAL; ++ scnprintf(key, sizeof(key), fmt, channel); ++ buffer[0] = (val >> 6) & 0xff; ++ buffer[1] = (val << 2) & 0xff; ++ return applesmc_write_key(key, buffer, 2); ++ } ++ break; ++ case hwmon_pwm: ++ if (attr == hwmon_pwm_enable) { ++ u8 buffer[2]; ++ u16 manual_val; ++ const struct applesmc_entry *entry; ++ ++ if (val != 1 && val != 2) ++ return -EINVAL; ++ ++ entry = applesmc_get_entry_by_key(FANS_MANUAL); ++ if (IS_ERR(entry)) ++ return PTR_ERR(entry); ++ ++ mutex_lock(&smcreg.mutex); ++ ret = read_smc(APPLESMC_READ_CMD, entry->key, buffer, 2); ++ if (ret) ++ goto out_unlock; ++ manual_val = (buffer[0] << 8 | buffer[1]); ++ if (val == 1) ++ manual_val |= (0x01 << channel); ++ else ++ manual_val &= ~(0x01 << channel); ++ buffer[0] = (manual_val >> 8) & 0xff; ++ buffer[1] = manual_val & 0xff; ++ ret = write_smc(APPLESMC_WRITE_CMD, entry->key, buffer, 2); ++out_unlock: ++ mutex_unlock(&smcreg.mutex); ++ return ret; ++ } ++ break; ++ default: ++ break; ++ } ++ return -EOPNOTSUPP; ++} ++ ++static int applesmc_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, const char **str) ++{ ++ switch (type) { ++ case hwmon_temp: ++ if (attr == hwmon_temp_label) { ++ *str = smcreg.index[channel]; ++ return 0; ++ } ++ break; ++ case hwmon_fan: ++ if (attr == hwmon_fan_label) { ++ *str = smcreg.fan_positions[channel] + 4; ++ return 0; ++ } ++ break; ++ default: ++ break; ++ } ++ return -EOPNOTSUPP; ++} ++ ++static const struct hwmon_ops applesmc_hwmon_ops = { ++ .is_visible = applesmc_hwmon_is_visible, ++ .read = applesmc_hwmon_read, ++ .write = applesmc_hwmon_write, ++ .read_string = applesmc_hwmon_read_string, ++}; ++ + static int __init applesmc_init(void) + { ++ int i; + int ret; + + if (!dmi_check_system(applesmc_whitelist)) { +@@ -1348,17 +1457,97 @@ static int __init applesmc_init(void) + if (ret) + goto out_smcreg; + +- ret = applesmc_create_nodes(fan_group, smcreg.fan_count); +- if (ret) ++ /* allocate hwmon channel configs */ ++ applesmc_temp_config = kcalloc(smcreg.index_count + 1, ++ sizeof(*applesmc_temp_config), GFP_KERNEL); ++ applesmc_fan_config = kcalloc(smcreg.fan_count + 1, ++ sizeof(*applesmc_fan_config), GFP_KERNEL); ++ applesmc_pwm_config = kcalloc(smcreg.fan_count + 1, ++ sizeof(*applesmc_pwm_config), GFP_KERNEL); ++ if (!applesmc_temp_config || !applesmc_fan_config || !applesmc_pwm_config) { ++ ret = -ENOMEM; + goto out_info; ++ } + +- ret = applesmc_create_nodes(temp_group, smcreg.index_count); +- if (ret) +- goto out_fans; ++ for (i = 0; i < smcreg.index_count; i++) ++ applesmc_temp_config[i] = HWMON_T_INPUT | HWMON_T_LABEL; ++ applesmc_temp_config[smcreg.index_count] = 0; ++ ++ for (i = 0; i < smcreg.fan_count; i++) { ++ applesmc_fan_config[i] = HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | ++ HWMON_F_MAX | HWMON_F_TARGET; ++ applesmc_pwm_config[i] = HWMON_PWM_ENABLE; ++ } ++ applesmc_fan_config[smcreg.fan_count] = 0; ++ applesmc_pwm_config[smcreg.fan_count] = 0; ++ ++ applesmc_info_temp = kzalloc_obj(*applesmc_info_temp, GFP_KERNEL); ++ applesmc_info_fan = kzalloc_obj(*applesmc_info_fan, GFP_KERNEL); ++ applesmc_info_pwm = kzalloc_obj(*applesmc_info_pwm, GFP_KERNEL); ++ if (!applesmc_info_temp || !applesmc_info_fan || !applesmc_info_pwm) { ++ ret = -ENOMEM; ++ goto out_info; ++ } ++ ++ applesmc_info_temp->type = hwmon_temp; ++ applesmc_info_temp->config = applesmc_temp_config; ++ ++ applesmc_info_fan->type = hwmon_fan; ++ applesmc_info_fan->config = applesmc_fan_config; ++ ++ applesmc_info_pwm->type = hwmon_pwm; ++ applesmc_info_pwm->config = applesmc_pwm_config; ++ ++ applesmc_info_arr = kcalloc(4, sizeof(*applesmc_info_arr), GFP_KERNEL); ++ if (!applesmc_info_arr) { ++ ret = -ENOMEM; ++ goto out_info; ++ } ++ ++ applesmc_info_arr[0] = applesmc_info_temp; ++ applesmc_info_arr[1] = applesmc_info_fan; ++ applesmc_info_arr[2] = applesmc_info_pwm; ++ applesmc_info_arr[3] = NULL; ++ ++ applesmc_chip = kzalloc_obj(*applesmc_chip, GFP_KERNEL); ++ if (!applesmc_chip) { ++ ret = -ENOMEM; ++ goto out_info; ++ } ++ ++ applesmc_chip->ops = &applesmc_hwmon_ops; ++ applesmc_chip->info = applesmc_info_arr; ++ ++ /* Create non-standard fanX_safe attributes group */ ++ fan_safe_attrs = kcalloc(smcreg.fan_count, ++ sizeof(*fan_safe_attrs), GFP_KERNEL); ++ fan_safe_attr_list = kcalloc(smcreg.fan_count + 1, ++ sizeof(*fan_safe_attr_list), GFP_KERNEL); ++ if (!fan_safe_attrs || !fan_safe_attr_list) { ++ ret = -ENOMEM; ++ goto out_info; ++ } ++ ++ for (i = 0; i < smcreg.fan_count; i++) { ++ struct applesmc_dev_attr *node = &fan_safe_attrs[i]; ++ ++ scnprintf(node->name, sizeof(node->name), "fan%d_safe", i + 1); ++ node->sda.index = (3 << 16) | (i & 0xffff); /* Option 3 (safe speed) */ ++ node->sda.dev_attr.show = applesmc_show_fan_speed; ++ node->sda.dev_attr.store = NULL; ++ sysfs_attr_init(&node->sda.dev_attr.attr); ++ node->sda.dev_attr.attr.name = node->name; ++ node->sda.dev_attr.attr.mode = 0444; ++ fan_safe_attr_list[i] = &node->sda.dev_attr.attr; ++ } ++ fan_safe_attr_list[smcreg.fan_count] = NULL; ++ fan_safe_group.attrs = fan_safe_attr_list; ++ applesmc_extra_groups[0] = &fan_safe_group; ++ applesmc_extra_groups[1] = NULL; + + ret = applesmc_create_accelerometer(); + if (ret) +- goto out_temperature; ++ goto out_info; + + ret = applesmc_create_light_sensor(); + if (ret) +@@ -1368,7 +1557,8 @@ static int __init applesmc_init(void) + if (ret) + goto out_light_sysfs; + +- hwmon_dev = hwmon_device_register(&pdev->dev); ++ hwmon_dev = hwmon_device_register_with_info(&pdev->dev, "applesmc", NULL, ++ applesmc_chip, applesmc_extra_groups); + if (IS_ERR(hwmon_dev)) { + ret = PTR_ERR(hwmon_dev); + goto out_light_ledclass; +@@ -1382,11 +1572,8 @@ static int __init applesmc_init(void) + applesmc_release_light_sensor(); + out_accelerometer: + applesmc_release_accelerometer(); +-out_temperature: +- applesmc_destroy_nodes(temp_group); +-out_fans: +- applesmc_destroy_nodes(fan_group); + out_info: ++ applesmc_free_hwmon(); + applesmc_destroy_nodes(info_group); + out_smcreg: + applesmc_destroy_smcreg(); +@@ -1407,13 +1594,12 @@ static void __exit applesmc_exit(void) + applesmc_release_key_backlight(); + applesmc_release_light_sensor(); + applesmc_release_accelerometer(); +- applesmc_destroy_nodes(temp_group); +- applesmc_destroy_nodes(fan_group); + applesmc_destroy_nodes(info_group); + applesmc_destroy_smcreg(); + platform_device_unregister(pdev); + platform_driver_unregister(&applesmc_driver); + release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS); ++ applesmc_free_hwmon(); + } + + module_init(applesmc_init); +diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c +--- a/drivers/hwmon/asus-ec-sensors.c ++++ b/drivers/hwmon/asus-ec-sensors.c +@@ -185,6 +185,20 @@ enum ec_sensors { + #define SENSOR_TEMP_SENSOR_EXTRA_2 BIT(ec_sensor_temp_sensor_extra_2) + #define SENSOR_TEMP_SENSOR_EXTRA_3 BIT(ec_sensor_temp_sensor_extra_3) + ++/* ++ * The values for temperature sensor readings without physical sensors connected. ++ * The value varies across generations and is seemingly defined by the EC chip ++ * used in the given board. ++ */ ++static const s32 temperature_blank_values[] = {-62, -60, -40}; ++ ++static const s32 environment_temp_sensors = ++ SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_T_SENSOR_ALT1 | ++ SENSOR_TEMP_WATER_IN | SENSOR_TEMP_WATER_OUT | ++ SENSOR_TEMP_WATER_BLOCK_IN | SENSOR_TEMP_WATER_BLOCK_OUT | ++ SENSOR_TEMP_T_SENSOR_2 | SENSOR_TEMP_SENSOR_EXTRA_1 | ++ SENSOR_TEMP_SENSOR_EXTRA_2 | SENSOR_TEMP_SENSOR_EXTRA_3; ++ + enum board_family { + family_unknown, + family_amd_400_series, +@@ -386,6 +400,8 @@ static const struct ec_sensor_info sensors_family_intel_600[] = { + [ec_sensor_temp_t_sensor] = + EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d), + [ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e), ++ [ec_sensor_fan_cpu_opt] = ++ EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0), + [ec_sensor_fan_water_flow] = + EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xbe), + [ec_sensor_temp_water_in] = +@@ -487,6 +503,14 @@ static const struct ec_board_info board_info_crosshair_x670e_hero = { + .family = family_amd_600_series, + }; + ++static const struct ec_board_info board_info_crosshair_x870e_hero = { ++ .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | ++ SENSOR_TEMP_MB | SENSOR_TEMP_VRM | ++ SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CPU_OPT, ++ .mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0, ++ .family = family_amd_800_series, ++}; ++ + static const struct ec_board_info board_info_maximus_vi_hero = { + .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | + SENSOR_TEMP_T_SENSOR | +@@ -527,6 +551,14 @@ static const struct ec_board_info board_info_maximus_z790_extreme = { + .family = family_intel_700_series, + }; + ++static const struct ec_board_info board_info_maximus_z790_hero = { ++ .sensors = SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM | ++ SENSOR_SET_TEMP_WATER | SENSOR_FAN_WATER_FLOW | ++ SENSOR_FAN_CPU_OPT, ++ .mutex_path = ASUS_HW_ACCESS_MUTEX_RMTW_ASMX, ++ .family = family_intel_700_series, ++}; ++ + static const struct ec_board_info board_info_prime_x470_pro = { + .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | + SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM | +@@ -590,6 +622,13 @@ static const struct ec_board_info board_info_pro_art_x870E_creator_wifi = { + .family = family_amd_800_series, + }; + ++static const struct ec_board_info board_info_pro_art_z690_creator_wifi = { ++ .sensors = SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM | ++ SENSOR_FAN_CPU_OPT, ++ .mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PC00_LPCB_SIO1_MUT0, ++ .family = family_intel_600_series, ++}; ++ + static const struct ec_board_info board_info_pro_ws_trx50_sage_wifi = { + /* Board also has a nct6798 */ + .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | SENSOR_TEMP_VRME | +@@ -743,7 +782,7 @@ static const struct ec_board_info board_info_strix_x870_i_gaming_wifi = { + + static const struct ec_board_info board_info_strix_x870e_e_gaming_wifi = { + .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | +- SENSOR_TEMP_MB | SENSOR_TEMP_VRM | ++ SENSOR_TEMP_MB | SENSOR_TEMP_VRM | SENSOR_TEMP_T_SENSOR | + SENSOR_FAN_CPU_OPT, + .mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0, + .family = family_amd_800_series, +@@ -859,6 +898,8 @@ static const struct dmi_system_id dmi_table[] = { + &board_info_pro_art_x670E_creator_wifi), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt X870E-CREATOR WIFI", + &board_info_pro_art_x870E_creator_wifi), ++ DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt Z690-CREATOR WIFI", ++ &board_info_pro_art_z690_creator_wifi), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("Pro WS TRX50-SAGE WIFI", + &board_info_pro_ws_trx50_sage_wifi), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("Pro WS TRX50-SAGE WIFI A", +@@ -883,6 +924,8 @@ static const struct dmi_system_id dmi_table[] = { + &board_info_crosshair_x670e_gene), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR X670E HERO", + &board_info_crosshair_x670e_hero), ++ DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR X870E HERO", ++ &board_info_crosshair_x870e_hero), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS XI HERO", + &board_info_maximus_xi_hero), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS XI HERO (WI-FI)", +@@ -893,6 +936,8 @@ static const struct dmi_system_id dmi_table[] = { + &board_info_maximus_z690_formula), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS Z790 EXTREME", + &board_info_maximus_z790_extreme), ++ DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS Z790 HERO", ++ &board_info_maximus_z790_hero), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-E GAMING", + &board_info_strix_b550_e_gaming), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-I GAMING", +@@ -927,8 +972,12 @@ static const struct dmi_system_id dmi_table[] = { + &board_info_strix_x870_i_gaming_wifi), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X870E-E GAMING WIFI", + &board_info_strix_x870e_e_gaming_wifi), ++ DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X870E-E GAMING WIFI7 R2", ++ &board_info_strix_x870e_e_gaming_wifi), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X870E-H GAMING WIFI7", + &board_info_strix_x870e_h_gaming_wifi7), ++ DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z390-E GAMING", ++ &board_info_strix_z390_f_gaming), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z390-F GAMING", + &board_info_strix_z390_f_gaming), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z490-F GAMING", +@@ -955,6 +1004,7 @@ static const struct dmi_system_id dmi_table[] = { + }; + + struct ec_sensor { ++ /* this is ec_sensors enum value */ + unsigned int info_index; + s32 cached_value; + }; +@@ -1047,6 +1097,12 @@ get_sensor_info(const struct ec_sensors_data *state, int index) + return state->sensors_info + state->sensors[index].info_index; + } + ++static enum ec_sensors ++get_ec_sensor_type(const struct ec_sensors_data *state, int index) ++{ ++ return state->sensors[index].info_index; ++} ++ + static int find_ec_sensor_index(const struct ec_sensors_data *ec, + enum hwmon_sensor_types type, int channel) + { +@@ -1290,6 +1346,17 @@ static int get_cached_value_or_update(const struct device *dev, + return 0; + } + ++static bool is_blank_temperature_value(s32 value) ++{ ++ size_t i; ++ ++ for (i = 0; i < ARRAY_SIZE(temperature_blank_values); ++i) { ++ if (value == temperature_blank_values[i]) ++ return true; ++ } ++ return false; ++} ++ + /* + * Now follow the functions that implement the hwmon interface + */ +@@ -1297,6 +1364,8 @@ static int get_cached_value_or_update(const struct device *dev, + static int asus_ec_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) + { ++ const struct ec_sensor_info *sensor_info; ++ enum ec_sensors ec_sensor; + int ret; + s32 value = 0; + +@@ -1308,12 +1377,19 @@ static int asus_ec_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + } + + ret = get_cached_value_or_update(dev, sidx, state, &value); +- if (!ret) { +- *val = scale_sensor_value(value, +- get_sensor_info(state, sidx)->type); +- } ++ if (ret) ++ return ret; + +- return ret; ++ sensor_info = get_sensor_info(state, sidx); ++ if (sensor_info->type == hwmon_temp) { ++ ec_sensor = get_ec_sensor_type(state, sidx); ++ if ((environment_temp_sensors & BIT(ec_sensor)) && ++ is_blank_temperature_value(value)) ++ return -ENODATA; ++ } ++ *val = scale_sensor_value(value, sensor_info->type); ++ ++ return 0; + } + + static int asus_ec_hwmon_read_string(struct device *dev, +diff --git a/drivers/hwmon/asus_rog_ryujin.c b/drivers/hwmon/asus_rog_ryujin.c +--- a/drivers/hwmon/asus_rog_ryujin.c ++++ b/drivers/hwmon/asus_rog_ryujin.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0+ + /* +- * hwmon driver for Asus ROG Ryujin II 360 AIO cooler. ++ * hwmon driver for Asus ROG Ryujin AIO coolers. + * + * Copyright 2024 Aleksa Savic + */ +@@ -17,6 +17,9 @@ + + #define USB_VENDOR_ID_ASUS_ROG 0x0b05 + #define USB_PRODUCT_ID_RYUJIN_AIO 0x1988 /* ASUS ROG RYUJIN II 360 */ ++#define USB_PRODUCT_ID_RYUJIN_III_EXTREME 0x1bcb ++#define USB_PRODUCT_ID_RYUJIN_III_EVA 0x1ade ++#define USB_PRODUCT_ID_RYUJIN_III_WHITE 0x1ada + + struct rog_ryujin_device_info { + u8 temp_offset; +@@ -34,6 +37,14 @@ static const struct rog_ryujin_device_info rog_ryujin_ii_360_info = { + .has_controller = true, + }; + ++static const struct rog_ryujin_device_info rog_ryujin_iii_info = { ++ .temp_offset = 5, ++ .pump_speed_offset = 7, ++ .fan_speed_offset = 10, ++ .duty_channel = 1, ++ .has_controller = false, ++}; ++ + #define STATUS_VALIDITY 1500 /* ms */ + #define MAX_REPORT_LENGTH 65 + +@@ -173,7 +184,6 @@ static int rog_ryujin_write_expanded(struct rog_ryujin_data *priv, const u8 *cmd + static int rog_ryujin_execute_cmd(struct rog_ryujin_data *priv, const u8 *cmd, int cmd_length, + struct completion *status_completion) + { +- unsigned long flags; + int ret; + + /* +@@ -181,9 +191,9 @@ static int rog_ryujin_execute_cmd(struct rog_ryujin_data *priv, const u8 *cmd, i + * completion. Reinit is done because hidraw could have triggered + * the raw event parsing and marked the passed in completion as done. + */ +- spin_lock_irqsave(&priv->status_report_request_lock, flags); ++ spin_lock_bh(&priv->status_report_request_lock); + reinit_completion(status_completion); +- spin_unlock_irqrestore(&priv->status_report_request_lock, flags); ++ spin_unlock_bh(&priv->status_report_request_lock); + + /* Send command for getting data */ + ret = rog_ryujin_write_expanded(priv, cmd, cmd_length); +@@ -411,18 +421,15 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + int size) + { + struct rog_ryujin_data *priv = hid_get_drvdata(hdev); +- unsigned long flags; + + if (size < 2 || data[0] != RYUJIN_CMD_PREFIX) + return 0; + +- spin_lock_irqsave(&priv->status_report_request_lock, flags); +- + if (data[1] == RYUJIN_GET_COOLER_STATUS_CMD_RESPONSE) { + if (size <= priv->info->temp_offset + 1 || + size <= priv->info->pump_speed_offset + 1 || + size <= priv->info->fan_speed_offset + 1) +- goto unlock; ++ return 0; + + /* Received coolant temp and speeds of pump and internal fan */ + priv->temp_input[0] = data[priv->info->temp_offset] * 1000 + +@@ -436,7 +443,7 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + complete_all(&priv->cooler_status_received); + } else if (data[1] == RYUJIN_GET_CONTROLLER_SPEED_CMD_RESPONSE) { + if (size <= RYUJIN_CONTROLLER_SPEED_3 + 1) +- goto unlock; ++ return 0; + + /* Received speeds of four fans attached to the controller */ + priv->speed_input[2] = get_unaligned_le16(data + RYUJIN_CONTROLLER_SPEED_1); +@@ -448,7 +455,7 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + complete_all(&priv->controller_status_received); + } else if (data[1] == RYUJIN_GET_COOLER_DUTY_CMD_RESPONSE) { + if (size <= RYUJIN_INTERNAL_FAN_DUTY) +- goto unlock; ++ return 0; + + /* Received report for pump and internal fan duties (in %) */ + if (data[RYUJIN_PUMP_DUTY] == 0 && data[RYUJIN_INTERNAL_FAN_DUTY] == 0) { +@@ -467,7 +474,7 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + * We're expecting a report, so parse it. + */ + goto read_cooler_duty; +- goto unlock; ++ return 0; + } + read_cooler_duty: + priv->duty_input[0] = rog_ryujin_percent_to_pwm(data[RYUJIN_PUMP_DUTY]); +@@ -477,7 +484,7 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + complete_all(&priv->cooler_duty_received); + } else if (data[1] == RYUJIN_GET_CONTROLLER_DUTY_CMD_RESPONSE) { + if (size <= RYUJIN_CONTROLLER_DUTY) +- goto unlock; ++ return 0; + + /* Received report for controller duty for fans (in PWM) */ + if (data[RYUJIN_CONTROLLER_DUTY] == 0) { +@@ -496,7 +503,7 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + * We're expecting a report, so parse it. + */ + goto read_controller_duty; +- goto unlock; ++ return 0; + } + read_controller_duty: + priv->duty_input[2] = data[RYUJIN_CONTROLLER_DUTY]; +@@ -505,8 +512,6 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + complete_all(&priv->controller_duty_received); + } + +-unlock: +- spin_unlock_irqrestore(&priv->status_report_request_lock, flags); + return 0; + } + +@@ -596,6 +601,12 @@ static void rog_ryujin_remove(struct hid_device *hdev) + static const struct hid_device_id rog_ryujin_table[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ASUS_ROG, USB_PRODUCT_ID_RYUJIN_AIO), + .driver_data = (kernel_ulong_t)&rog_ryujin_ii_360_info }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ASUS_ROG, USB_PRODUCT_ID_RYUJIN_III_EXTREME), ++ .driver_data = (kernel_ulong_t)&rog_ryujin_iii_info }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ASUS_ROG, USB_PRODUCT_ID_RYUJIN_III_EVA), ++ .driver_data = (kernel_ulong_t)&rog_ryujin_iii_info }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_ASUS_ROG, USB_PRODUCT_ID_RYUJIN_III_WHITE), ++ .driver_data = (kernel_ulong_t)&rog_ryujin_iii_info }, + { } + }; + +@@ -625,4 +636,4 @@ module_exit(rog_ryujin_exit); + + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Aleksa Savic "); +-MODULE_DESCRIPTION("Hwmon driver for Asus ROG Ryujin II 360 AIO cooler"); ++MODULE_DESCRIPTION("Hwmon driver for Asus ROG Ryujin AIO coolers"); +diff --git a/drivers/hwmon/axi-fan-control.c b/drivers/hwmon/axi-fan-control.c +--- a/drivers/hwmon/axi-fan-control.c ++++ b/drivers/hwmon/axi-fan-control.c +@@ -508,8 +508,7 @@ static int axi_fan_control_probe(struct platform_device *pdev) + IRQF_ONESHOT | IRQF_TRIGGER_HIGH, + NULL, ctl); + if (ret) +- return dev_err_probe(&pdev->dev, ret, +- "failed to request an irq\n"); ++ return ret; + + return 0; + } +diff --git a/drivers/hwmon/chipcap2.c b/drivers/hwmon/chipcap2.c +--- a/drivers/hwmon/chipcap2.c ++++ b/drivers/hwmon/chipcap2.c +@@ -22,6 +22,8 @@ + #include + #include + #include ++#include ++#include + + #define CC2_START_CM 0xA0 + #define CC2_START_NOM 0x80 +@@ -83,6 +85,7 @@ struct cc2_data { + struct i2c_client *client; + struct regulator *regulator; + const char *name; ++ const char *label; + int irq_ready; + int irq_low; + int irq_high; +@@ -444,6 +447,8 @@ static umode_t cc2_is_visible(const void *data, enum hwmon_sensor_types type, + switch (attr) { + case hwmon_humidity_input: + return 0444; ++ case hwmon_humidity_label: ++ return cc2->label ? 0444 : 0; + case hwmon_humidity_min_alarm: + return cc2->rh_alarm.low_alarm_visible ? 0444 : 0; + case hwmon_humidity_max_alarm: +@@ -461,6 +466,8 @@ static umode_t cc2_is_visible(const void *data, enum hwmon_sensor_types type, + switch (attr) { + case hwmon_temp_input: + return 0444; ++ case hwmon_temp_label: ++ return cc2->label ? 0444 : 0; + default: + return 0; + } +@@ -547,6 +554,16 @@ static int cc2_humidity_max_alarm_status(struct cc2_data *data, long *val) + return 0; + } + ++static int cc2_read_string(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, const char **str) ++{ ++ struct cc2_data *data = dev_get_drvdata(dev); ++ ++ *str = data->label; ++ ++ return 0; ++} ++ + static int cc2_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long *val) + { +@@ -665,8 +682,9 @@ static int cc2_request_alarm_irqs(struct cc2_data *data, struct device *dev) + } + + static const struct hwmon_channel_info *cc2_info[] = { +- HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), +- HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT | HWMON_H_MIN | HWMON_H_MAX | ++ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL), ++ HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT | HWMON_H_LABEL | ++ HWMON_H_MIN | HWMON_H_MAX | + HWMON_H_MIN_HYST | HWMON_H_MAX_HYST | + HWMON_H_MIN_ALARM | HWMON_H_MAX_ALARM), + NULL +@@ -675,6 +693,7 @@ static const struct hwmon_channel_info *cc2_info[] = { + static const struct hwmon_ops cc2_hwmon_ops = { + .is_visible = cc2_is_visible, + .read = cc2_read, ++ .read_string = cc2_read_string, + .write = cc2_write, + }; + +@@ -705,6 +724,8 @@ static int cc2_probe(struct i2c_client *client) + return dev_err_probe(dev, PTR_ERR(data->regulator), + "Failed to get regulator\n"); + ++ device_property_read_string(dev, "label", &data->label); ++ + ret = cc2_request_ready_irq(data, dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to request ready irq\n"); +diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c +--- a/drivers/hwmon/coretemp.c ++++ b/drivers/hwmon/coretemp.c +@@ -68,7 +68,7 @@ enum coretemp_attr_index { + * This value is passed as "id" field to rdmsr/wrmsr functions. + * @status_reg: One of IA32_THERM_STATUS or IA32_PACKAGE_THERM_STATUS, + * from where the temperature values should be read. +- * @attr_size: Total number of pre-core attrs displayed in the sysfs. ++ * @attr_size: Number of sysfs attributes in this temperature data group. + */ + struct temp_data { + int temp; +diff --git a/drivers/hwmon/corsair-psu.c b/drivers/hwmon/corsair-psu.c +--- a/drivers/hwmon/corsair-psu.c ++++ b/drivers/hwmon/corsair-psu.c +@@ -884,7 +884,7 @@ static const struct hid_device_id corsairpsu_idtable[] = { + { HID_USB_DEVICE(0x1b1c, 0x1c0c) }, /* Corsair RM850i */ + { HID_USB_DEVICE(0x1b1c, 0x1c0d) }, /* Corsair RM1000i */ + { HID_USB_DEVICE(0x1b1c, 0x1c1e) }, /* Corsair HX1000i Series 2023 */ +- { HID_USB_DEVICE(0x1b1c, 0x1c1f) }, /* Corsair HX1500i Legacy and Series 2023 */ ++ { HID_USB_DEVICE(0x1b1c, 0x1c1f) }, /* Corsair HX1500i Legacy, Series 2023 and 2025 */ + { HID_USB_DEVICE(0x1b1c, 0x1c23) }, /* Corsair HX1200i Series 2023 */ + { HID_USB_DEVICE(0x1b1c, 0x1c27) }, /* Corsair HX1200i Series 2025 */ + { }, +diff --git a/drivers/hwmon/cros_ec_hwmon.c b/drivers/hwmon/cros_ec_hwmon.c +--- a/drivers/hwmon/cros_ec_hwmon.c ++++ b/drivers/hwmon/cros_ec_hwmon.c +@@ -148,9 +148,17 @@ static bool cros_ec_hwmon_is_error_temp(u8 temp) + temp == EC_TEMP_SENSOR_NOT_CALIBRATED; + } + ++/* This differs slightly from the variant in units.h to avoid rounding inconsistencies. */ ++#define CROS_EC_HWMON_ABSOLUTE_ZERO_MILLICELSIUS (-273000) ++ ++static long cros_ec_hwmon_kelvin_to_millicelsius(long t) ++{ ++ return t * MILLIDEGREE_PER_DEGREE + CROS_EC_HWMON_ABSOLUTE_ZERO_MILLICELSIUS; ++} ++ + static long cros_ec_hwmon_temp_to_millicelsius(u8 temp) + { +- return kelvin_to_millicelsius((((long)temp) + EC_TEMP_SENSOR_OFFSET)); ++ return cros_ec_hwmon_kelvin_to_millicelsius((((long)temp) + EC_TEMP_SENSOR_OFFSET)); + } + + static bool cros_ec_hwmon_attr_is_temp_threshold(u32 attr) +@@ -228,8 +236,13 @@ static int cros_ec_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + ret = cros_ec_hwmon_read_temp_threshold(priv->cros_ec, channel, + cros_ec_hwmon_attr_to_thres(attr), + &threshold); +- if (ret == 0) +- *val = kelvin_to_millicelsius(threshold); ++ if (ret == 0) { ++ /* Limit to sensible, non-overflowing values. */ ++ if (threshold > 255 + 273) ++ *val = 255000; ++ else ++ *val = cros_ec_hwmon_kelvin_to_millicelsius(threshold); ++ } + } + } + +diff --git a/drivers/hwmon/da9055-hwmon.c b/drivers/hwmon/da9055-hwmon.c +--- a/drivers/hwmon/da9055-hwmon.c ++++ b/drivers/hwmon/da9055-hwmon.c +@@ -256,11 +256,8 @@ static int da9055_hwmon_probe(struct platform_device *pdev) + NULL, da9055_auxadc_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "adc-irq", hwmon); +- if (ret != 0) { +- dev_err(hwmon->da9055->dev, "DA9055 ADC IRQ failed ret=%d\n", +- ret); ++ if (ret != 0) + return ret; +- } + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, "da9055", + hwmon, +diff --git a/drivers/hwmon/eic7700-pvt.c b/drivers/hwmon/eic7700-pvt.c +new file mode 100644 +--- /dev/null ++++ b/drivers/hwmon/eic7700-pvt.c +@@ -0,0 +1,509 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * ESWIN EIC7700 Voltage, Temperature sensor driver ++ * ++ * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd. ++ * ++ * Authors: ++ * Yulin Lu ++ * Huan He ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "eic7700-pvt.h" ++ ++static const struct pvt_sensor_info pvt_info[] = { ++ PVT_SENSOR_INFO(0, "Temperature", hwmon_temp, TEMP), ++ PVT_SENSOR_INFO(0, "Voltage", hwmon_in, VOLT), ++}; ++ ++static const char * const pvt_clk_names[PVT_CLK_NUM] = {"enable", "apb"}; ++ ++/* ++ * The original translation formulae of the temperature (in degrees of Celsius) ++ * to PVT data and vice-versa are following: ++ * N = 6.0818e-8*(T^4) +1.2873e-5*(T^3) + 7.2244e-3*(T^2) + 3.6484*(T^1) + ++ * 1.6198e2, ++ * T = -1.8439e-11*(N^4) + 8.0705e-8*(N^3) + -1.8501e-4*(N^2) + ++ * 3.2843e-1*(N^1) - 4.8690e1, ++ * where T = [-40, 125]C and N = [27, 771]. ++ * They must be accordingly altered to be suitable for the integer arithmetics. ++ * The technique is called 'factor redistribution', which just makes sure the ++ * multiplications and divisions are made so to have a result of the operations ++ * within the integer numbers limit. In addition we need to translate the ++ * formulae to accept millidegrees of Celsius. Here what they look like after ++ * the alterations: ++ * N = (60818e-20*(T^4) + 12873e-14*(T^3) + 72244e-9*(T^2) + 36484e-3*T + ++ * 16198e2) / 1e4, ++ * T = -18439e-12*(N^4) + 80705e-9*(N^3) - 185010e-6*(N^2) + 328430e-3*N - ++ * 48690, ++ * where T = [-40000, 125000] mC and N = [27, 771]. ++ */ ++static const struct polynomial poly_N_to_temp = { ++ .total_divider = 1, ++ .terms = { ++ {4, -18439, 1000, 1}, ++ {3, 80705, 1000, 1}, ++ {2, -185010, 1000, 1}, ++ {1, 328430, 1000, 1}, ++ {0, -48690, 1, 1} ++ } ++}; ++ ++/* ++ * Similar alterations are performed for the voltage conversion equations. ++ * The original formulae are: ++ * N = 1.3905e3*V - 5.7685e2, ++ * V = (N + 5.7685e2) / 1.3905e3, ++ * where V = [0.72, 0.88] V and N = [424, 646]. ++ * After the optimization they looks as follows: ++ * N = (13905e-3*V - 5768.5) / 10, ++ * V = (N * 10^5 / 13905 + 57685 * 10^3 / 13905) / 10. ++ * where V = [720, 880] mV and N = [424, 646]. ++ */ ++static const struct polynomial poly_N_to_volt = { ++ .total_divider = 10, ++ .terms = { ++ {1, 100000, 13905, 1}, ++ {0, 57685000, 1, 13905} ++ } ++}; ++ ++static inline u32 eic7700_pvt_update(void __iomem *reg, u32 mask, u32 data) ++{ ++ u32 old; ++ ++ old = readl_relaxed(reg); ++ writel((old & ~mask) | (data & mask), reg); ++ ++ return old & mask; ++} ++ ++static inline void eic7700_pvt_set_mode(struct pvt_hwmon *pvt, u32 mode) ++{ ++ u32 old; ++ ++ mode = FIELD_PREP(PVT_MODE_MASK, mode); ++ ++ old = eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, 0); ++ eic7700_pvt_update(pvt->regs + PVT_MODE, PVT_MODE_MASK, mode); ++ eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, old); ++} ++ ++static inline void eic7700_pvt_set_trim(struct pvt_hwmon *pvt, u32 val) ++{ ++ u32 old; ++ ++ old = eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, 0); ++ writel(val, pvt->regs + PVT_TRIM); ++ eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, old); ++} ++ ++static irqreturn_t eic7700_pvt_hard_isr(int irq, void *data) ++{ ++ struct pvt_hwmon *pvt = data; ++ u32 stat, val; ++ int active; ++ ++ if (IS_ENABLED(CONFIG_PM)) { ++ active = pm_runtime_get_if_active(pvt->dev); ++ if (active <= 0) ++ return IRQ_NONE; ++ } ++ ++ stat = readl(pvt->regs + PVT_INT); ++ if (!(stat & PVT_INT_STAT)) { ++ if (IS_ENABLED(CONFIG_PM)) ++ pm_runtime_put(pvt->dev); ++ return IRQ_NONE; ++ } ++ ++ eic7700_pvt_update(pvt->regs + PVT_INT, PVT_INT_CLR, PVT_INT_CLR); ++ /* ++ * Read the data, update the cache and notify a waiter of this event. ++ */ ++ val = readl(pvt->regs + PVT_DATA); ++ WRITE_ONCE(pvt->data_cache, FIELD_GET(PVT_DATA_OUT, val)); ++ complete(&pvt->conversion); ++ ++ if (IS_ENABLED(CONFIG_PM)) ++ pm_runtime_put(pvt->dev); ++ ++ return IRQ_HANDLED; ++} ++ ++static int eic7700_pvt_read_data(struct pvt_hwmon *pvt, ++ enum pvt_sensor_type type, long *val) ++{ ++ unsigned long timeout; ++ u32 data; ++ int ret; ++ ++ /* ++ * Wait for PVT conversion to complete and update the data cache. The ++ * data read procedure is following: set the requested PVT sensor mode, ++ * enable conversion, wait until conversion is finished, then disable ++ * conversion and IRQ, and read the cached data. ++ */ ++ reinit_completion(&pvt->conversion); ++ ++ eic7700_pvt_set_mode(pvt, pvt_info[type].mode); ++ eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, PVT_ENA_EN); ++ ++ /* ++ * Wait with timeout since in case if the sensor is suddenly powered ++ * down the request won't be completed and the caller will hang up on ++ * this procedure until the power is back up again. Multiply the ++ * timeout by the factor of two to prevent a false timeout. ++ */ ++ timeout = 2 * usecs_to_jiffies(ktime_to_us(pvt->timeout)); ++ ret = wait_for_completion_timeout(&pvt->conversion, timeout); ++ ++ eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, 0); ++ eic7700_pvt_update(pvt->regs + PVT_INT, PVT_INT_CLR, PVT_INT_CLR); ++ ++ if (!ret) ++ synchronize_irq(pvt->irq); ++ ++ data = READ_ONCE(pvt->data_cache); ++ ++ if (!ret) ++ return -ETIMEDOUT; ++ ++ if (type == PVT_TEMP) ++ *val = polynomial_calc(&poly_N_to_temp, data); ++ else ++ *val = polynomial_calc(&poly_N_to_volt, data); ++ ++ return 0; ++} ++ ++static const struct hwmon_channel_info *pvt_channel_info[] = { ++ HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), ++ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL), ++ HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LABEL), ++ NULL ++}; ++ ++static umode_t eic7700_pvt_hwmon_is_visible(const void *data, ++ enum hwmon_sensor_types type, ++ u32 attr, int ch) ++{ ++ switch (type) { ++ case hwmon_temp: ++ switch (attr) { ++ case hwmon_temp_input: ++ case hwmon_temp_label: ++ return 0444; ++ } ++ break; ++ case hwmon_in: ++ switch (attr) { ++ case hwmon_in_input: ++ case hwmon_in_label: ++ return 0444; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ ++static int eic7700_pvt_hwmon_read(struct device *dev, ++ enum hwmon_sensor_types type, u32 attr, ++ int ch, long *val) ++{ ++ struct pvt_hwmon *pvt = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = pm_runtime_get_sync(pvt->dev); ++ if (ret < 0) { ++ dev_err(pvt->dev, "Failed to resume PVT device: %d\n", ret); ++ pm_runtime_put_noidle(pvt->dev); ++ return ret; ++ } ++ ++ switch (type) { ++ case hwmon_temp: ++ switch (attr) { ++ case hwmon_temp_input: ++ ret = eic7700_pvt_read_data(pvt, ch, val); ++ break; ++ default: ++ ret = -EOPNOTSUPP; ++ } ++ break; ++ case hwmon_in: ++ if (attr == hwmon_in_input) ++ ret = eic7700_pvt_read_data(pvt, PVT_VOLT + ch, val); ++ else ++ ret = -EOPNOTSUPP; ++ break; ++ default: ++ ret = -EOPNOTSUPP; ++ } ++ ++ pm_runtime_mark_last_busy(pvt->dev); ++ pm_runtime_put_autosuspend(pvt->dev); ++ return ret; ++} ++ ++static int eic7700_pvt_hwmon_read_string(struct device *dev, ++ enum hwmon_sensor_types type, u32 attr, ++ int ch, const char **str) ++{ ++ switch (type) { ++ case hwmon_temp: ++ if (attr == hwmon_temp_label) { ++ *str = pvt_info[ch].label; ++ return 0; ++ } ++ break; ++ case hwmon_in: ++ if (attr == hwmon_in_label) { ++ *str = pvt_info[PVT_VOLT + ch].label; ++ return 0; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ return -EOPNOTSUPP; ++} ++ ++static const struct hwmon_ops pvt_hwmon_ops = { ++ .is_visible = eic7700_pvt_hwmon_is_visible, ++ .read = eic7700_pvt_hwmon_read, ++ .read_string = eic7700_pvt_hwmon_read_string ++}; ++ ++static const struct hwmon_chip_info pvt_hwmon_info = { ++ .ops = &pvt_hwmon_ops, ++ .info = pvt_channel_info ++}; ++ ++static struct pvt_hwmon *eic7700_pvt_create_data(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct pvt_hwmon *pvt; ++ ++ pvt = devm_kzalloc(dev, sizeof(*pvt), GFP_KERNEL); ++ if (!pvt) ++ return ERR_PTR(-ENOMEM); ++ ++ pvt->dev = dev; ++ init_completion(&pvt->conversion); ++ ++ return pvt; ++} ++ ++static int eic7700_pvt_init_iface(struct pvt_hwmon *pvt) ++{ ++ /* ++ * Make sure controller are disabled so not to accidentally have ISR ++ * executed before the driver data is fully initialized. Clear the IRQ ++ * status as well. ++ */ ++ eic7700_pvt_update(pvt->regs + PVT_ENA, PVT_ENA_EN, 0); ++ eic7700_pvt_update(pvt->regs + PVT_INT, PVT_INT_CLR, PVT_INT_CLR); ++ readl(pvt->regs + PVT_INT); ++ readl(pvt->regs + PVT_DATA); ++ ++ /* Setup default sensor mode and temperature trim. */ ++ eic7700_pvt_set_mode(pvt, pvt_info[PVT_TEMP].mode); ++ ++ /* ++ * Max conversion latency (~333 µs) derived from PVT spec: ++ * maximum sampling rate = 3000 samples/sec. ++ */ ++ pvt->timeout = ns_to_ktime(PVT_TOUT_MIN); ++ ++ eic7700_pvt_set_trim(pvt, PVT_TRIM_DEF); ++ ++ return 0; ++} ++ ++static int eic7700_pvt_request_irq(struct pvt_hwmon *pvt) ++{ ++ struct platform_device *pdev = to_platform_device(pvt->dev); ++ int ret; ++ ++ pvt->irq = platform_get_irq(pdev, 0); ++ if (pvt->irq < 0) ++ return pvt->irq; ++ ++ ret = devm_request_threaded_irq(pvt->dev, pvt->irq, ++ eic7700_pvt_hard_isr, NULL, ++ IRQF_TRIGGER_HIGH, "pvt", pvt); ++ if (ret) { ++ dev_err(pvt->dev, "Couldn't request PVT IRQ\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int eic7700_pvt_create_hwmon(struct pvt_hwmon *pvt) ++{ ++ pvt->hwmon = devm_hwmon_device_register_with_info(pvt->dev, "pvt", ++ pvt, &pvt_hwmon_info, ++ NULL); ++ if (IS_ERR(pvt->hwmon)) { ++ dev_err(pvt->dev, "Couldn't create hwmon device\n"); ++ return PTR_ERR(pvt->hwmon); ++ } ++ ++ return 0; ++} ++ ++static void eic7700_pvt_disable_pm_runtime(void *data) ++{ ++ struct pvt_hwmon *pvt = data; ++ ++ pm_runtime_dont_use_autosuspend(pvt->dev); ++ pm_runtime_disable(pvt->dev); ++ ++ if (!pm_runtime_status_suspended(pvt->dev)) { ++ clk_bulk_disable_unprepare(PVT_CLK_NUM, pvt->clks); ++ pm_runtime_set_suspended(pvt->dev); ++ } ++} ++ ++static int eic7700_pvt_probe(struct platform_device *pdev) ++{ ++ struct reset_control *rst; ++ struct pvt_hwmon *pvt; ++ int i, ret; ++ ++ pvt = eic7700_pvt_create_data(pdev); ++ if (IS_ERR(pvt)) ++ return PTR_ERR(pvt); ++ ++ platform_set_drvdata(pdev, pvt); ++ ++ pvt->regs = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(pvt->regs)) ++ return PTR_ERR(pvt->regs); ++ ++ for (i = 0; i < PVT_CLK_NUM; i++) ++ pvt->clks[i].id = pvt_clk_names[i]; ++ ++ ret = devm_clk_bulk_get(&pdev->dev, PVT_CLK_NUM, pvt->clks); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, ++ "Couldn't get clock descriptors\n"); ++ ++ rst = devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL); ++ if (IS_ERR(rst)) ++ return dev_err_probe(pvt->dev, PTR_ERR(rst), ++ "Couldn't get reset control\n"); ++ ++ ret = clk_bulk_prepare_enable(PVT_CLK_NUM, pvt->clks); ++ if (ret) ++ return dev_err_probe(pvt->dev, ret, ++ "Failed to enable clocks\n"); ++ ++ ret = eic7700_pvt_init_iface(pvt); ++ if (ret) { ++ clk_bulk_disable_unprepare(PVT_CLK_NUM, pvt->clks); ++ return ret; ++ } ++ ++ if (IS_ENABLED(CONFIG_PM)) ++ clk_bulk_disable_unprepare(PVT_CLK_NUM, pvt->clks); ++ ++ pm_runtime_enable(&pdev->dev); ++ pm_runtime_set_autosuspend_delay(&pdev->dev, 3000); ++ pm_runtime_use_autosuspend(&pdev->dev); ++ pm_runtime_get_noresume(&pdev->dev); ++ ++ ret = devm_add_action_or_reset(pvt->dev, eic7700_pvt_disable_pm_runtime, ++ pvt); ++ if (ret) { ++ pm_runtime_put_noidle(&pdev->dev); ++ return dev_err_probe(&pdev->dev, ret, ++ "Can't register PM cleanup\n"); ++ } ++ ++ ret = eic7700_pvt_request_irq(pvt); ++ if (ret) ++ goto err_put_pm_runtime; ++ ++ ret = eic7700_pvt_create_hwmon(pvt); ++ if (ret) ++ goto err_put_pm_runtime; ++ ++ pm_runtime_put_autosuspend(&pdev->dev); ++ ++ return 0; ++ ++err_put_pm_runtime: ++ pm_runtime_put_noidle(&pdev->dev); ++ return ret; ++} ++ ++static int __maybe_unused eic7700_pvt_runtime_resume(struct device *dev) ++{ ++ struct pvt_hwmon *pvt = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = clk_bulk_prepare_enable(PVT_CLK_NUM, pvt->clks); ++ if (ret) { ++ dev_err(dev, "Failed to enable clocks: %d\n", ret); ++ return ret; ++ } ++ ++ eic7700_pvt_set_trim(pvt, PVT_TRIM_DEF); ++ ++ return 0; ++} ++ ++static int __maybe_unused eic7700_pvt_runtime_suspend(struct device *dev) ++{ ++ struct pvt_hwmon *pvt = dev_get_drvdata(dev); ++ ++ clk_bulk_disable_unprepare(PVT_CLK_NUM, pvt->clks); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops eic7700_pvt_pm_ops = { ++ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) ++ RUNTIME_PM_OPS(eic7700_pvt_runtime_suspend, eic7700_pvt_runtime_resume, ++ NULL) ++}; ++ ++static const struct of_device_id pvt_of_match[] = { ++ { .compatible = "eswin,eic7700-pvt"}, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, pvt_of_match); ++ ++static struct platform_driver pvt_driver = { ++ .probe = eic7700_pvt_probe, ++ .driver = { ++ .name = "eic7700-pvt", ++ .of_match_table = pvt_of_match, ++ .pm = pm_ptr(&eic7700_pvt_pm_ops), ++ }, ++}; ++module_platform_driver(pvt_driver); ++ ++MODULE_AUTHOR("Yulin Lu "); ++MODULE_AUTHOR("Huan He "); ++MODULE_DESCRIPTION("Eswin eic7700 PVT driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/hwmon/eic7700-pvt.h b/drivers/hwmon/eic7700-pvt.h +new file mode 100644 +--- /dev/null ++++ b/drivers/hwmon/eic7700-pvt.h +@@ -0,0 +1,99 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * ESWIN EIC7700 Voltage, Temperature sensor driver ++ * ++ * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd. ++ */ ++#ifndef __HWMON_EIC7700_PVT_H__ ++#define __HWMON_EIC7700_PVT_H__ ++ ++#include ++#include ++#include ++#include ++ ++/* ESWIN EIC7700 PVT registers and their bitfields */ ++#define PVT_TRIM 0x04 ++#define PVT_MODE 0x08 ++#define PVT_MODE_MASK GENMASK(2, 0) ++#define PVT_CTRL_MODE_TEMP 0x0 ++#define PVT_CTRL_MODE_VOLT 0x4 ++#define PVT_ENA 0x0c ++#define PVT_ENA_EN BIT(0) ++#define PVT_INT 0x10 ++#define PVT_INT_STAT BIT(0) ++#define PVT_INT_CLR BIT(1) ++#define PVT_DATA 0x14 ++#define PVT_DATA_OUT GENMASK(9, 0) ++ ++/* ++ * PVT sensors-related limits and default values ++ * @PVT_TEMP_CHS: Number of temperature hwmon channels. ++ * @PVT_VOLT_CHS: Number of voltage hwmon channels. ++ * @PVT_TRIM_DEF: Default temperature sensor trim value (set a proper value ++ * when one is determined for ESWIN EIC7700 SoC). ++ * @PVT_TOUT_MIN: Minimal timeout between samples in nanoseconds. ++ */ ++#define PVT_TEMP_CHS 1 ++#define PVT_VOLT_CHS 1 ++#define PVT_TRIM_DEF 0 ++#define PVT_TOUT_MIN (NSEC_PER_SEC / 3000) ++ ++/* ++ * enum pvt_sensor_type - ESWIN EIC7700 PVT sensor types (correspond to each PVT ++ * sampling mode) ++ * @PVT_TEMP: PVT Temperature sensor. ++ * @PVT_VOLT: PVT Voltage sensor. ++ */ ++enum pvt_sensor_type { ++ PVT_TEMP = 0, ++ PVT_VOLT ++}; ++ ++#define PVT_CLK_NUM 2 ++ ++/* ++ * struct pvt_sensor_info - ESWIN EIC7700 PVT sensor informational structure ++ * @channel: Sensor channel ID. ++ * @label: hwmon sensor label. ++ * @mode: PVT mode corresponding to the channel. ++ * @type: Sensor type. ++ */ ++struct pvt_sensor_info { ++ int channel; ++ const char *label; ++ u32 mode; ++ enum hwmon_sensor_types type; ++}; ++ ++#define PVT_SENSOR_INFO(_ch, _label, _type, _mode) \ ++ { \ ++ .channel = _ch, \ ++ .label = _label, \ ++ .mode = PVT_CTRL_MODE_ ##_mode, \ ++ .type = _type, \ ++ } ++ ++/* ++ * struct pvt_hwmon - Eswin EIC7700 PVT private data ++ * @dev: device structure of the PVT platform device. ++ * @hwmon: hwmon device structure. ++ * @regs: pointer to the Eswin EIC7700 PVT registers region. ++ * @irq: PVT events IRQ number. ++ * @clks: PVT clock descriptors. ++ * @data_cache: data cache in raw format. ++ * @conversion: data conversion completion. ++ * @timeout: conversion timeout. ++ */ ++struct pvt_hwmon { ++ struct device *dev; ++ struct device *hwmon; ++ void __iomem *regs; ++ int irq; ++ struct clk_bulk_data clks[PVT_CLK_NUM]; ++ u32 data_cache; ++ struct completion conversion; ++ ktime_t timeout; ++}; ++ ++#endif /* __HWMON_EIC7700_PVT_H__ */ +diff --git a/drivers/hwmon/emc1403.c b/drivers/hwmon/emc1403.c +--- a/drivers/hwmon/emc1403.c ++++ b/drivers/hwmon/emc1403.c +@@ -18,6 +18,7 @@ + #include + #include + #include ++#include + #include + + #define THERMAL_PID_REG 0xfd +@@ -654,14 +655,20 @@ static int emc1403_probe(struct i2c_client *client) + { + struct thermal_data *data; + struct device *hwmon_dev; +- const struct i2c_device_id *id = i2c_match_id(emc1403_idtable, client); ++ int ret; ++ ++ ret = devm_regulator_get_enable(&client->dev, "vdd"); ++ if (ret) ++ return dev_err_probe(&client->dev, ret, ++ "Failed to enable regulator\n"); + + data = devm_kzalloc(&client->dev, sizeof(struct thermal_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + +- data->chip = id->driver_data; ++ data->chip = (uintptr_t)i2c_get_match_data(client); ++ + data->regmap = devm_regmap_init_i2c(client, &emc1403_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); +@@ -677,10 +684,20 @@ static const unsigned short emc1403_address_list[] = { + 0x18, 0x1c, 0x29, 0x3c, 0x4c, 0x4d, 0x5c, I2C_CLIENT_END + }; + ++static const struct of_device_id emc1403_of_match[] = { ++ { .compatible = "smsc,emc1402", .data = (void *)emc1402 }, ++ { .compatible = "smsc,emc1403", .data = (void *)emc1403 }, ++ { .compatible = "smsc,emc1404", .data = (void *)emc1404 }, ++ { .compatible = "smsc,emc1428", .data = (void *)emc1428 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, emc1403_of_match); ++ + static struct i2c_driver sensor_emc1403 = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "emc1403", ++ .of_match_table = emc1403_of_match, + }, + .detect = emc1403_detect, + .probe = emc1403_probe, +diff --git a/drivers/hwmon/gxp-fan-ctrl.c b/drivers/hwmon/gxp-fan-ctrl.c +--- a/drivers/hwmon/gxp-fan-ctrl.c ++++ b/drivers/hwmon/gxp-fan-ctrl.c +@@ -1,59 +1,16 @@ + // SPDX-License-Identifier: GPL-2.0-only +-/* Copyright (C) 2022 Hewlett-Packard Enterprise Development Company, L.P. */ ++/* Copyright (C) 2023 Hewlett-Packard Enterprise Development Company, L.P. */ + +-#include + #include + #include + #include + #include + #include + +-#define OFS_FAN_INST 0 /* Is 0 because plreg base will be set at INST */ +-#define OFS_FAN_FAIL 2 /* Is 2 bytes after base */ +-#define OFS_SEVSTAT 0 /* Is 0 because fn2 base will be set at SEVSTAT */ +-#define POWER_BIT 24 +- + struct gxp_fan_ctrl_drvdata { + void __iomem *base; +- void __iomem *plreg; +- void __iomem *fn2; + }; + +-static bool fan_installed(struct device *dev, int fan) +-{ +- struct gxp_fan_ctrl_drvdata *drvdata = dev_get_drvdata(dev); +- u8 val; +- +- val = readb(drvdata->plreg + OFS_FAN_INST); +- +- return !!(val & BIT(fan)); +-} +- +-static long fan_failed(struct device *dev, int fan) +-{ +- struct gxp_fan_ctrl_drvdata *drvdata = dev_get_drvdata(dev); +- u8 val; +- +- val = readb(drvdata->plreg + OFS_FAN_FAIL); +- +- return !!(val & BIT(fan)); +-} +- +-static long fan_enabled(struct device *dev, int fan) +-{ +- struct gxp_fan_ctrl_drvdata *drvdata = dev_get_drvdata(dev); +- u32 val; +- +- /* +- * Check the power status as if the platform is off the value +- * reported for the PWM will be incorrect. Report fan as +- * disabled. +- */ +- val = readl(drvdata->fn2 + OFS_SEVSTAT); +- +- return !!((val & BIT(POWER_BIT)) && fan_installed(dev, fan)); +-} +- + static int gxp_pwm_write(struct device *dev, u32 attr, int channel, long val) + { + struct gxp_fan_ctrl_drvdata *drvdata = dev_get_drvdata(dev); +@@ -80,37 +37,11 @@ static int gxp_fan_ctrl_write(struct device *dev, enum hwmon_sensor_types type, + } + } + +-static int gxp_fan_read(struct device *dev, u32 attr, int channel, long *val) +-{ +- switch (attr) { +- case hwmon_fan_enable: +- *val = fan_enabled(dev, channel); +- return 0; +- case hwmon_fan_fault: +- *val = fan_failed(dev, channel); +- return 0; +- default: +- return -EOPNOTSUPP; +- } +-} +- + static int gxp_pwm_read(struct device *dev, u32 attr, int channel, long *val) + { + struct gxp_fan_ctrl_drvdata *drvdata = dev_get_drvdata(dev); +- u32 reg; + +- /* +- * Check the power status of the platform. If the platform is off +- * the value reported for the PWM will be incorrect. In this case +- * report a PWM of zero. +- */ +- +- reg = readl(drvdata->fn2 + OFS_SEVSTAT); +- +- if (reg & BIT(POWER_BIT)) +- *val = fan_installed(dev, channel) ? readb(drvdata->base + channel) : 0; +- else +- *val = 0; ++ *val = readb(drvdata->base + channel); + + return 0; + } +@@ -119,8 +50,6 @@ static int gxp_fan_ctrl_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) + { + switch (type) { +- case hwmon_fan: +- return gxp_fan_read(dev, attr, channel, val); + case hwmon_pwm: + return gxp_pwm_read(dev, attr, channel, val); + default: +@@ -135,16 +64,6 @@ static umode_t gxp_fan_ctrl_is_visible(const void *_data, + umode_t mode = 0; + + switch (type) { +- case hwmon_fan: +- switch (attr) { +- case hwmon_fan_enable: +- case hwmon_fan_fault: +- mode = 0444; +- break; +- default: +- break; +- } +- break; + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: +@@ -168,15 +87,6 @@ static const struct hwmon_ops gxp_fan_ctrl_ops = { + }; + + static const struct hwmon_channel_info * const gxp_fan_ctrl_info[] = { +- HWMON_CHANNEL_INFO(fan, +- HWMON_F_FAULT | HWMON_F_ENABLE, +- HWMON_F_FAULT | HWMON_F_ENABLE, +- HWMON_F_FAULT | HWMON_F_ENABLE, +- HWMON_F_FAULT | HWMON_F_ENABLE, +- HWMON_F_FAULT | HWMON_F_ENABLE, +- HWMON_F_FAULT | HWMON_F_ENABLE, +- HWMON_F_FAULT | HWMON_F_ENABLE, +- HWMON_F_FAULT | HWMON_F_ENABLE), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, +@@ -211,18 +121,6 @@ static int gxp_fan_ctrl_probe(struct platform_device *pdev) + return dev_err_probe(dev, PTR_ERR(drvdata->base), + "failed to map base\n"); + +- drvdata->plreg = devm_platform_ioremap_resource_byname(pdev, +- "pl"); +- if (IS_ERR(drvdata->plreg)) +- return dev_err_probe(dev, PTR_ERR(drvdata->plreg), +- "failed to map plreg\n"); +- +- drvdata->fn2 = devm_platform_ioremap_resource_byname(pdev, +- "fn2"); +- if (IS_ERR(drvdata->fn2)) +- return dev_err_probe(dev, PTR_ERR(drvdata->fn2), +- "failed to map fn2\n"); +- + hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, + "hpe_gxp_fan_ctrl", + drvdata, +diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c +--- a/drivers/hwmon/hwmon.c ++++ b/drivers/hwmon/hwmon.c +@@ -57,8 +57,8 @@ struct hwmon_device_attribute { + }; + + #define to_hwmon_attr(d) \ +- container_of(d, struct hwmon_device_attribute, dev_attr) +-#define to_dev_attr(a) container_of(a, struct device_attribute, attr) ++ container_of_const(d, struct hwmon_device_attribute, dev_attr) ++#define to_dev_attr(a) container_of_const(a, struct device_attribute, attr) + + /* + * Thermal zone information +@@ -71,27 +71,27 @@ struct hwmon_thermal_data { + }; + + static ssize_t +-name_show(struct device *dev, struct device_attribute *attr, char *buf) ++name_show(struct device *dev, const struct device_attribute *attr, char *buf) + { + return sysfs_emit(buf, "%s\n", to_hwmon_device(dev)->name); + } +-static DEVICE_ATTR_RO(name); ++static const DEVICE_ATTR_RO(name); + + static ssize_t +-label_show(struct device *dev, struct device_attribute *attr, char *buf) ++label_show(struct device *dev, const struct device_attribute *attr, char *buf) + { + return sysfs_emit(buf, "%s\n", to_hwmon_device(dev)->label); + } +-static DEVICE_ATTR_RO(label); ++static const DEVICE_ATTR_RO(label); + +-static struct attribute *hwmon_dev_attrs[] = { ++static const struct attribute *const hwmon_dev_attrs[] = { + &dev_attr_name.attr, + &dev_attr_label.attr, + NULL + }; + + static umode_t hwmon_dev_attr_is_visible(struct kobject *kobj, +- struct attribute *attr, int n) ++ const struct attribute *attr, int n) + { + struct device *dev = kobj_to_dev(kobj); + struct hwmon_device *hdev = to_hwmon_device(dev); +@@ -106,8 +106,8 @@ static umode_t hwmon_dev_attr_is_visible(struct kobject *kobj, + } + + static const struct attribute_group hwmon_dev_attr_group = { +- .attrs = hwmon_dev_attrs, +- .is_visible = hwmon_dev_attr_is_visible, ++ .attrs_const = hwmon_dev_attrs, ++ .is_visible_const = hwmon_dev_attr_is_visible, + }; + + static const struct attribute_group *hwmon_dev_attr_groups[] = { +@@ -115,13 +115,13 @@ static const struct attribute_group *hwmon_dev_attr_groups[] = { + NULL + }; + +-static void hwmon_free_attrs(struct attribute **attrs) ++static void hwmon_free_attrs(const struct attribute *const *attrs) + { + int i; + + for (i = 0; attrs[i]; i++) { +- struct device_attribute *dattr = to_dev_attr(attrs[i]); +- struct hwmon_device_attribute *hattr = to_hwmon_attr(dattr); ++ const struct device_attribute *dattr = to_dev_attr(attrs[i]); ++ const struct hwmon_device_attribute *hattr = to_hwmon_attr(dattr); + + kfree(hattr); + } +@@ -132,8 +132,8 @@ static void hwmon_dev_release(struct device *dev) + { + struct hwmon_device *hwdev = to_hwmon_device(dev); + +- if (hwdev->group.attrs) +- hwmon_free_attrs(hwdev->group.attrs); ++ if (hwdev->group.attrs_const) ++ hwmon_free_attrs(hwdev->group.attrs_const); + kfree(hwdev->groups); + kfree(hwdev->label); + kfree(hwdev); +@@ -341,7 +341,7 @@ static int hwmon_match_device(struct device *dev, const void *data) + return dev->class == &hwmon_class; + } + +-static ssize_t pec_show(struct device *dev, struct device_attribute *dummy, ++static ssize_t pec_show(struct device *dev, const struct device_attribute *dummy, + char *buf) + { + struct i2c_client *client = to_i2c_client(dev); +@@ -349,7 +349,7 @@ static ssize_t pec_show(struct device *dev, struct device_attribute *dummy, + return sysfs_emit(buf, "%d\n", !!(client->flags & I2C_CLIENT_PEC)); + } + +-static ssize_t pec_store(struct device *dev, struct device_attribute *devattr, ++static ssize_t pec_store(struct device *dev, const struct device_attribute *devattr, + const char *buf, size_t count) + { + struct i2c_client *client = to_i2c_client(dev); +@@ -389,7 +389,7 @@ static ssize_t pec_store(struct device *dev, struct device_attribute *devattr, + return err; + } + +-static DEVICE_ATTR_RW(pec); ++static const DEVICE_ATTR_RW(pec); + + static void hwmon_remove_pec(void *dev) + { +@@ -424,9 +424,9 @@ static int hwmon_pec_register(struct device *hdev) + /* sysfs attribute management */ + + static ssize_t hwmon_attr_show(struct device *dev, +- struct device_attribute *devattr, char *buf) ++ const struct device_attribute *devattr, char *buf) + { +- struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr); ++ const struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr); + struct hwmon_device *hwdev = to_hwmon_device(dev); + s64 val64; + long val; +@@ -449,10 +449,10 @@ static ssize_t hwmon_attr_show(struct device *dev, + } + + static ssize_t hwmon_attr_show_string(struct device *dev, +- struct device_attribute *devattr, ++ const struct device_attribute *devattr, + char *buf) + { +- struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr); ++ const struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr); + struct hwmon_device *hwdev = to_hwmon_device(dev); + enum hwmon_sensor_types type = hattr->type; + const char *s; +@@ -472,10 +472,10 @@ static ssize_t hwmon_attr_show_string(struct device *dev, + } + + static ssize_t hwmon_attr_store(struct device *dev, +- struct device_attribute *devattr, ++ const struct device_attribute *devattr, + const char *buf, size_t count) + { +- struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr); ++ const struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr); + struct hwmon_device *hwdev = to_hwmon_device(dev); + long val; + int ret; +@@ -509,12 +509,12 @@ static bool is_string_attr(enum hwmon_sensor_types type, u32 attr) + (type == hwmon_fan && attr == hwmon_fan_label); + } + +-static struct attribute *hwmon_genattr(const void *drvdata, +- enum hwmon_sensor_types type, +- u32 attr, +- int index, +- const char *template, +- const struct hwmon_ops *ops) ++static const struct attribute *hwmon_genattr(const void *drvdata, ++ enum hwmon_sensor_types type, ++ u32 attr, ++ int index, ++ const char *template, ++ const struct hwmon_ops *ops) + { + struct hwmon_device_attribute *hattr; + struct device_attribute *dattr; +@@ -551,8 +551,8 @@ static struct attribute *hwmon_genattr(const void *drvdata, + hattr->ops = ops; + + dattr = &hattr->dev_attr; +- dattr->show = is_string ? hwmon_attr_show_string : hwmon_attr_show; +- dattr->store = hwmon_attr_store; ++ dattr->show_const = is_string ? hwmon_attr_show_string : hwmon_attr_show; ++ dattr->store_const = hwmon_attr_store; + + a = &dattr->attr; + sysfs_attr_init(a); +@@ -830,7 +830,7 @@ static int hwmon_num_channel_attrs(const struct hwmon_channel_info *info) + } + + static int hwmon_genattrs(const void *drvdata, +- struct attribute **attrs, ++ const struct attribute **attrs, + const struct hwmon_ops *ops, + const struct hwmon_channel_info *info) + { +@@ -849,7 +849,7 @@ static int hwmon_genattrs(const void *drvdata, + u32 attr; + + while (attr_mask) { +- struct attribute *a; ++ const struct attribute *a; + + attr = __ffs(attr_mask); + attr_mask &= ~BIT(attr); +@@ -868,11 +868,11 @@ static int hwmon_genattrs(const void *drvdata, + return aindex; + } + +-static struct attribute ** ++static const struct attribute ** + __hwmon_create_attrs(const void *drvdata, const struct hwmon_chip_info *chip) + { + int ret, i, aindex = 0, nattrs = 0; +- struct attribute **attrs; ++ const struct attribute **attrs; + + for (i = 0; chip->info[i]; i++) + nattrs += hwmon_num_channel_attrs(chip->info[i]); +@@ -927,7 +927,7 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata, + hdev = &hwdev->dev; + + if (chip) { +- struct attribute **attrs; ++ const struct attribute **attrs; + int ngroups = 2; /* terminating NULL plus &hwdev->groups */ + + if (groups) +@@ -946,7 +946,7 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata, + goto free_hwmon; + } + +- hwdev->group.attrs = attrs; ++ hwdev->group.attrs_const = attrs; + ngroups = 0; + hwdev->groups[ngroups++] = &hwdev->group; + +diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c +--- a/drivers/hwmon/ina2xx.c ++++ b/drivers/hwmon/ina2xx.c +@@ -124,6 +124,7 @@ static const struct regmap_config ina2xx_regmap_config = { + enum ina2xx_ids { + ina219, + ina226, ++ ina232, + ina234, + ina260, + sy24655 +@@ -210,6 +211,20 @@ static const struct ina2xx_config ina2xx_config[] = { + .current_shift = 4, + .has_update_interval = true, + }, ++ [ina232] = { ++ .config_default = INA226_CONFIG_DEFAULT, ++ .calibration_value = 2048, ++ .shunt_div = 400, ++ .shunt_voltage_shift = 0, ++ .bus_voltage_shift = 0, ++ .bus_voltage_lsb = 1600, ++ .power_lsb_factor = 32, ++ .has_alerts = true, ++ .has_ishunt = false, ++ .has_power_average = false, ++ .current_shift = 0, ++ .has_update_interval = true, ++ }, + [ina260] = { + .config_default = INA260_CONFIG_DEFAULT, + .shunt_div = 400, +@@ -1123,6 +1138,7 @@ static const struct i2c_device_id ina2xx_id[] = { + { .name = "ina226", .driver_data = ina226 }, + { .name = "ina230", .driver_data = ina226 }, + { .name = "ina231", .driver_data = ina226 }, ++ { .name = "ina232", .driver_data = ina232 }, + { .name = "ina234", .driver_data = ina234 }, + { .name = "ina260", .driver_data = ina260 }, + { .name = "sy24655", .driver_data = sy24655 }, +@@ -1155,6 +1171,10 @@ static const struct of_device_id __maybe_unused ina2xx_of_match[] = { + .compatible = "ti,ina231", + .data = (void *)ina226 + }, ++ { ++ .compatible = "ti,ina232", ++ .data = (void *)ina232 ++ }, + { + .compatible = "ti,ina234", + .data = (void *)ina234 +diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c +--- a/drivers/hwmon/k10temp.c ++++ b/drivers/hwmon/k10temp.c +@@ -207,6 +207,10 @@ static const char *k10temp_temp_label[] = { + "Tccd10", + "Tccd11", + "Tccd12", ++ "Tccd13", ++ "Tccd14", ++ "Tccd15", ++ "Tccd16", + }; + + static int k10temp_read_labels(struct device *dev, +@@ -243,7 +247,7 @@ static int k10temp_read_temp(struct device *dev, u32 attr, int channel, + if (*val < 0 && !data->disp_negative) + *val = 0; + break; +- case 2 ... 13: /* Tccd{1-12} */ ++ case 2 ... 17: /* Tccd{1-16} */ + ret = read_ccd_temp_reg(data, channel - 2, ®val); + + if (ret) +@@ -384,6 +388,10 @@ static const struct hwmon_channel_info * const k10temp_info[] = { + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, ++ HWMON_T_INPUT | HWMON_T_LABEL, ++ HWMON_T_INPUT | HWMON_T_LABEL, ++ HWMON_T_INPUT | HWMON_T_LABEL, ++ HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL), + NULL + }; +@@ -515,6 +523,10 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) + } + } else if (boot_cpu_data.x86 == 0x1a) { + switch (boot_cpu_data.x86_model) { ++ case 0x00 ... 0x2f: /* Zen5 Turin */ ++ data->ccd_offset = 0x1F0; ++ k10temp_get_ccd_support(data, 16); ++ break; + case 0x40 ... 0x4f: /* Zen5 Ryzen Desktop */ + data->ccd_offset = 0x308; + k10temp_get_ccd_support(data, 8); +diff --git a/drivers/hwmon/kb9002.c b/drivers/hwmon/kb9002.c +new file mode 100644 +--- /dev/null ++++ b/drivers/hwmon/kb9002.c +@@ -0,0 +1,460 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Kandou KB9002 PCIe 5.0 retimer hwmon driver. ++ * ++ * The retimer exposes a system management bus (SMBus 3.0 with PEC) ++ * target for firmware-managed status registers. This driver assumes ++ * the chip is strapped to SMBus mode and exports the aggregated ++ * maximum die temperature as hwmon temp1_input (millidegrees Celsius) ++ * plus the firmware version and boot status under debugfs. ++ * ++ * The raw-I2C path (kb9002_i2c_read/write, used only at probe to switch ++ * the host interface) carries the 32-bit register address and data ++ * big-endian. The SMBus path (kb9002_fw_read/kb9002_smbus_hw_read) ++ * carries the register address and returned data little-endian. ++ * ++ * Datasheet: Kandou KB9002 PCIe retimer (KA-015171-PD). ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define KB9002_DEV_NAME "kb9002" ++ ++/* ++ * SMBus read command codes. Each read is a two-phase PEC-protected ++ * transaction: prime writes the target address, data reads it back with ++ * the register contents. FW reads use a 16-bit address, HW reads 32-bit. ++ */ ++#define KB9002_CC_FW_READ_PRIME 0x82 ++#define KB9002_CC_FW_READ_DATA 0x81 ++#define KB9002_CC_HW_READ_PRIME 0x8a ++#define KB9002_CC_HW_READ_DATA 0x89 ++ ++/* Firmware register offsets (16-bit). */ ++#define KB9002_FW_REG_VID 0x0004 ++#define KB9002_FW_REG_FW_VERSION 0x0500 ++#define KB9002_FW_REG_TEMP_MAXIMUM 0x0550 ++ ++#define KB9002_VID_MASK GENMASK(31, 16) ++#define KB9002_VID_KANDOU 0x1e6f ++ ++/* Firmware boot status: 0xe8 in the top byte means init completed OK. */ ++#define KB9002_HW_REG_FW_BOOT_STATUS 0xe0090008 ++#define KB9002_FW_BOOT_STATUS_OK_MSB 0xe8 ++ ++/* ++ * Hardware registers reached over raw I2C (32-bit addressing). The ++ * host-interface bit selects SMBus (set) vs raw-I2C target; parts ++ * strapped to raw I2C need it set before SMBus access works. ++ */ ++#define KB9002_HW_REG_REVID 0x00480004 ++#define KB9002_HW_REG_HOST_IF 0x00480008 ++#define KB9002_HOST_IF_SMBUS BIT(1) ++ ++#define KB9002_REVID_MASK GENMASK(7, 0) ++#define KB9002_REVID_B0 0x10 ++#define KB9002_REVID_B1 0x11 ++ ++/* Retries to drain a stray leading 0xff from the raw-I2C FIFO. */ ++#define KB9002_REVID_READ_RETRIES 16 ++ ++/* Temperature: 32-bit Q16.16 absolute Kelvin. */ ++#define KB9002_TEMP_FRAC_BITS 16 ++#define KB9002_ABS_ZERO_MILLI_C (-273150) ++ ++/* Firmware takes up to ~2s to respond after a host-interface change. */ ++#define KB9002_FW_READY_POLL_US (25 * USEC_PER_MSEC) ++#define KB9002_FW_READY_TIMEOUT_US (2 * USEC_PER_SEC) ++ ++struct kb9002_data { ++ struct i2c_client *client; ++ struct device *hwmon_dev; ++}; ++ ++/* Raw-I2C read: write the 32-bit BE address, then read 4 BE data bytes. */ ++static int kb9002_i2c_read(struct i2c_client *client, u32 reg, u32 *val) ++{ ++ u8 addr[4]; ++ u8 rbuf[4]; ++ struct i2c_msg msgs[2] = { ++ { ++ .addr = client->addr, ++ .flags = 0, ++ .len = sizeof(addr), ++ .buf = addr, ++ }, ++ { ++ .addr = client->addr, ++ .flags = I2C_M_RD, ++ .len = sizeof(rbuf), ++ .buf = rbuf, ++ }, ++ }; ++ int ret; ++ ++ put_unaligned_be32(reg, addr); ++ ++ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); ++ if (ret < 0) ++ return ret; ++ if (ret != ARRAY_SIZE(msgs)) ++ return -EIO; ++ ++ *val = get_unaligned_be32(rbuf); ++ return 0; ++} ++ ++/* Raw-I2C write: 4 BE address bytes followed by 4 BE data bytes. */ ++static int kb9002_i2c_write(struct i2c_client *client, u32 reg, u32 val) ++{ ++ u8 buf[8]; ++ struct i2c_msg msg = { ++ .addr = client->addr, ++ .flags = 0, ++ .len = sizeof(buf), ++ .buf = buf, ++ }; ++ int ret; ++ ++ put_unaligned_be32(reg, &buf[0]); ++ put_unaligned_be32(val, &buf[4]); ++ ++ ret = i2c_transfer(client->adapter, &msg, 1); ++ if (ret < 0) ++ return ret; ++ if (ret != 1) ++ return -EIO; ++ ++ return 0; ++} ++ ++/* ++ * Read the silicon revision ID. A fresh FIFO may start with a stray ++ * 0xff that shifts the result, so drain one byte between retries until ++ * the top byte is no longer 0xff. ++ */ ++static int kb9002_read_revid(struct i2c_client *client, u32 *revid) ++{ ++ u8 dummy; ++ int ret; ++ int i; ++ ++ for (i = 0; i < KB9002_REVID_READ_RETRIES; i++) { ++ ret = kb9002_i2c_read(client, KB9002_HW_REG_REVID, revid); ++ if (ret) ++ return ret; ++ if ((*revid >> 24) != 0xff) ++ return 0; ++ /* Drain one byte from the chip to re-align the I2C FIFO. */ ++ i2c_master_recv(client, &dummy, 1); ++ } ++ ++ return -EIO; ++} ++ ++/* ++ * Read a 32-bit firmware register over SMBus: block-write the 16-bit LE ++ * address, then block-read the echoed address plus 4 LE data bytes. ++ */ ++static int kb9002_fw_read(struct kb9002_data *data, u16 reg, u32 *val) ++{ ++ struct i2c_client *client = data->client; ++ u8 addr[2]; ++ u8 rbuf[I2C_SMBUS_BLOCK_MAX]; ++ int ret; ++ ++ put_unaligned_le16(reg, addr); ++ ++ ret = i2c_smbus_write_block_data(client, KB9002_CC_FW_READ_PRIME, ++ sizeof(addr), addr); ++ if (ret < 0) ++ return ret; ++ ++ ret = i2c_smbus_read_block_data(client, KB9002_CC_FW_READ_DATA, rbuf); ++ if (ret < 0) ++ return ret; ++ if (ret < (int)(sizeof(addr) + sizeof(*val))) ++ return -EIO; ++ ++ *val = get_unaligned_le32(&rbuf[sizeof(addr)]); ++ return 0; ++} ++ ++/* Like kb9002_fw_read but for a hardware register (32-bit LE address). */ ++static int kb9002_smbus_hw_read(struct kb9002_data *data, u32 reg, u32 *val) ++{ ++ struct i2c_client *client = data->client; ++ u8 addr[4]; ++ u8 rbuf[I2C_SMBUS_BLOCK_MAX]; ++ int ret; ++ ++ put_unaligned_le32(reg, addr); ++ ++ ret = i2c_smbus_write_block_data(client, KB9002_CC_HW_READ_PRIME, ++ sizeof(addr), addr); ++ if (ret < 0) ++ return ret; ++ ++ ret = i2c_smbus_read_block_data(client, KB9002_CC_HW_READ_DATA, rbuf); ++ if (ret < 0) ++ return ret; ++ if (ret < (int)(sizeof(addr) + sizeof(*val))) ++ return -EIO; ++ ++ *val = get_unaligned_le32(&rbuf[sizeof(addr)]); ++ return 0; ++} ++ ++/* ++ * Switch the host interface from raw-I2C to SMBus and wait for firmware ++ * to come back up. Called only when SMBus access failed in probe, i.e. ++ * the chip is strapped to raw-I2C mode. Confirms the revision, sets the ++ * SMBus-mode bit, then polls until firmware responds again. ++ */ ++static int kb9002_enable_smbus_target(struct kb9002_data *data) ++{ ++ struct i2c_client *client = data->client; ++ u32 revid; ++ u32 val; ++ int op_ret; ++ int ret; ++ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) ++ return dev_err_probe(&client->dev, -ENODEV, ++ "raw I2C required to switch to SMBus mode\n"); ++ ++ ret = kb9002_read_revid(client, &revid); ++ if (ret) ++ return dev_err_probe(&client->dev, ret, ++ "revision ID read failed\n"); ++ ++ switch (FIELD_GET(KB9002_REVID_MASK, revid)) { ++ case KB9002_REVID_B0: ++ case KB9002_REVID_B1: ++ break; ++ default: ++ return dev_err_probe(&client->dev, -ENODEV, ++ "unsupported revision ID 0x%08x\n", revid); ++ } ++ ++ ret = kb9002_i2c_read(client, KB9002_HW_REG_HOST_IF, &val); ++ if (ret) ++ return dev_err_probe(&client->dev, ret, ++ "host interface read failed\n"); ++ ++ val |= KB9002_HOST_IF_SMBUS; ++ ++ ret = kb9002_i2c_write(client, KB9002_HW_REG_HOST_IF, val); ++ if (ret) ++ return dev_err_probe(&client->dev, ret, ++ "host interface write failed\n"); ++ ++ /* Wait until firmware re-initialisation completes. */ ++ ret = read_poll_timeout(kb9002_fw_read, op_ret, op_ret == 0, ++ KB9002_FW_READY_POLL_US, ++ KB9002_FW_READY_TIMEOUT_US, true, ++ data, KB9002_FW_REG_VID, &val); ++ if (ret) ++ return dev_err_probe(&client->dev, ret, ++ "firmware not responding over SMBus\n"); ++ ++ return 0; ++} ++ ++/* Convert Q16.16 absolute Kelvin to millidegrees Celsius. */ ++static long kb9002_temp_to_milli_c(u32 raw) ++{ ++ s64 milli_k = ((s64)raw * 1000) >> KB9002_TEMP_FRAC_BITS; ++ ++ return (long)milli_k + KB9002_ABS_ZERO_MILLI_C; ++} ++ ++static int kb9002_read_temp(struct kb9002_data *data, long *val) ++{ ++ u32 raw; ++ int ret; ++ ++ ret = kb9002_fw_read(data, KB9002_FW_REG_TEMP_MAXIMUM, &raw); ++ if (ret) ++ return ret; ++ ++ *val = kb9002_temp_to_milli_c(raw); ++ return 0; ++} ++ ++static umode_t kb9002_is_visible(const void *drvdata, ++ enum hwmon_sensor_types type, ++ u32 attr, int channel) ++{ ++ return 0444; ++} ++ ++static int kb9002_read(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, long *val) ++{ ++ struct kb9002_data *data = dev_get_drvdata(dev); ++ ++ if (type == hwmon_temp && attr == hwmon_temp_input) ++ return kb9002_read_temp(data, val); ++ ++ return -EOPNOTSUPP; ++} ++ ++static const struct hwmon_ops kb9002_hwmon_ops = { ++ .is_visible = kb9002_is_visible, ++ .read = kb9002_read, ++}; ++ ++static const struct hwmon_channel_info * const kb9002_hwmon_info[] = { ++ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), ++ NULL, ++}; ++ ++static const struct hwmon_chip_info kb9002_chip_info = { ++ .ops = &kb9002_hwmon_ops, ++ .info = kb9002_hwmon_info, ++}; ++ ++static int kb9002_fw_version_show(struct seq_file *s, void *unused) ++{ ++ struct kb9002_data *data = s->private; ++ u32 ver; ++ int ret; ++ ++ guard(hwmon_lock)(data->hwmon_dev); ++ ++ ret = kb9002_fw_read(data, KB9002_FW_REG_FW_VERSION, &ver); ++ if (ret) ++ return ret; ++ ++ seq_printf(s, "%u.%02u.%02u.%u\n", ++ (ver >> 24) & 0xff, (ver >> 16) & 0xff, ++ (ver >> 8) & 0xff, (ver >> 0) & 0xff); ++ return 0; ++} ++DEFINE_SHOW_ATTRIBUTE(kb9002_fw_version); ++ ++static int kb9002_fw_load_status_show(struct seq_file *s, void *unused) ++{ ++ struct kb9002_data *data = s->private; ++ u32 status; ++ int ret; ++ ++ guard(hwmon_lock)(data->hwmon_dev); ++ ++ ret = kb9002_smbus_hw_read(data, KB9002_HW_REG_FW_BOOT_STATUS, &status); ++ if (ret) ++ return ret; ++ ++ seq_printf(s, "%s\n", ++ (status >> 24) == KB9002_FW_BOOT_STATUS_OK_MSB ? ++ "normal" : "abnormal"); ++ return 0; ++} ++DEFINE_SHOW_ATTRIBUTE(kb9002_fw_load_status); ++ ++static void kb9002_debugfs_init(struct kb9002_data *data) ++{ ++ struct dentry *dir = data->client->debugfs; ++ ++ debugfs_create_file("fw_ver", 0444, dir, data, ++ &kb9002_fw_version_fops); ++ debugfs_create_file("fw_load_status", 0444, dir, data, ++ &kb9002_fw_load_status_fops); ++} ++ ++static int kb9002_probe(struct i2c_client *client) ++{ ++ struct device *dev = &client->dev; ++ struct kb9002_data *data; ++ struct device *hwmon_dev; ++ u32 vid; ++ int ret; ++ ++ if (!i2c_check_functionality(client->adapter, ++ I2C_FUNC_SMBUS_BLOCK_DATA | ++ I2C_FUNC_SMBUS_PEC)) ++ return -ENODEV; ++ ++ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ data->client = client; ++ ++ /* All firmware register accesses are PEC-protected. */ ++ client->flags |= I2C_CLIENT_PEC; ++ ++ i2c_set_clientdata(client, data); ++ ++ /* ++ * Try SMBus first. If the chip is strapped to raw-I2C mode it ++ * will not respond to SMBus framing, so fall back to switching ++ * the host interface over raw I2C and retry. ++ */ ++ ret = kb9002_fw_read(data, KB9002_FW_REG_VID, &vid); ++ if (ret) { ++ dev_dbg(dev, "SMBus probe failed (%d), trying raw-I2C host-interface switch\n", ++ ret); ++ ret = kb9002_enable_smbus_target(data); ++ if (ret) ++ return ret; ++ ret = kb9002_fw_read(data, KB9002_FW_REG_VID, &vid); ++ if (ret) ++ return dev_err_probe(dev, ret, ++ "VID read failed after host-interface switch\n"); ++ } ++ if (FIELD_GET(KB9002_VID_MASK, vid) != KB9002_VID_KANDOU) ++ return dev_err_probe(dev, -ENODEV, ++ "unexpected VID 0x%08x\n", vid); ++ ++ hwmon_dev = devm_hwmon_device_register_with_info(dev, KB9002_DEV_NAME, ++ data, ++ &kb9002_chip_info, ++ NULL); ++ if (IS_ERR(hwmon_dev)) ++ return PTR_ERR(hwmon_dev); ++ ++ data->hwmon_dev = hwmon_dev; ++ kb9002_debugfs_init(data); ++ return 0; ++} ++ ++static const struct i2c_device_id kb9002_id[] = { ++ { .name = KB9002_DEV_NAME }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, kb9002_id); ++ ++static const struct of_device_id kb9002_of_match[] = { ++ { .compatible = "kandou,kb9002" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, kb9002_of_match); ++ ++static struct i2c_driver kb9002_driver = { ++ .driver = { ++ .name = KB9002_DEV_NAME, ++ .of_match_table = kb9002_of_match, ++ }, ++ .probe = kb9002_probe, ++ .id_table = kb9002_id, ++}; ++module_i2c_driver(kb9002_driver); ++ ++MODULE_AUTHOR("Andy Chung "); ++MODULE_DESCRIPTION("Kandou KB9002 PCIe retimer hwmon driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c +--- a/drivers/hwmon/lm90.c ++++ b/drivers/hwmon/lm90.c +@@ -101,6 +101,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -108,7 +109,7 @@ + #include + #include + #include +-#include ++#include + #include + #include + #include +@@ -295,7 +296,7 @@ static const struct i2c_device_id lm90_id[] = { + }; + MODULE_DEVICE_TABLE(i2c, lm90_id); + +-static const struct of_device_id __maybe_unused lm90_of_match[] = { ++static const struct of_device_id lm90_of_match[] = { + { + .compatible = "adi,adm1032", + .data = (void *)adm1032 +@@ -2602,7 +2603,6 @@ static void lm90_stop_work(void *_data) + + static int lm90_init_client(struct i2c_client *client, struct lm90_data *data) + { +- struct device_node *np = client->dev.of_node; + int config, convrate; + + if (data->flags & LM90_HAVE_CONVRATE) { +@@ -2626,7 +2626,7 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data) + + /* Check Temperature Range Select */ + if (data->flags & LM90_HAVE_EXTENDED_TEMP) { +- if (of_property_read_bool(np, "ti,extended-range-enable")) ++ if (device_property_read_bool(&client->dev, "ti,extended-range-enable")) + config |= 0x04; + if (!(config & 0x04)) + data->flags &= ~LM90_HAVE_EXTENDED_TEMP; +@@ -2692,36 +2692,41 @@ static irqreturn_t lm90_irq_thread(int irq, void *dev_id) + return IRQ_NONE; + } + +-static int lm90_probe_channel_from_dt(struct i2c_client *client, +- struct device_node *child, +- struct lm90_data *data) ++static int lm90_probe_channel(struct i2c_client *client, ++ struct fwnode_handle *child, ++ struct lm90_data *data) + { + u32 id; + s32 val; + int err; + struct device *dev = &client->dev; + +- err = of_property_read_u32(child, "reg", &id); ++ err = fwnode_property_read_u32(child, "reg", &id); + if (err) { +- dev_err(dev, "missing reg property of %pOFn\n", child); ++ dev_err(dev, "missing reg property of %pfw\n", child); + return err; + } + + if (id >= MAX_CHANNELS) { +- dev_err(dev, "invalid reg property value %d in %pOFn\n", id, child); ++ dev_err(dev, "invalid reg property value %d in %pfw\n", id, child); + return -EINVAL; + } + +- err = of_property_read_string(child, "label", &data->channel_label[id]); ++ err = fwnode_property_read_string(child, "label", &data->channel_label[id]); + if (err == -ENODATA || err == -EILSEQ) { +- dev_err(dev, "invalid label property in %pOFn\n", child); ++ dev_err(dev, "invalid label property in %pfw\n", child); + return err; + } + + if (data->channel_label[id]) + data->channel_config[id] |= HWMON_T_LABEL; + +- err = of_property_read_s32(child, "temperature-offset-millicelsius", &val); ++ /* ++ * fwnode_property_read_u32() has no signed equivalent. ++ * temperature-offset-millicelsius is signed, so read and reinterpret it as s32 to ++ * preserve negative offsets values (same behavior as the old of_property_read_s32()). ++ */ ++ err = fwnode_property_read_u32(child, "temperature-offset-millicelsius", (u32 *)&val); + if (!err) { + if (id == 0) { + dev_err(dev, "temperature-offset-millicelsius can't be set for internal channel\n"); +@@ -2739,18 +2744,17 @@ static int lm90_probe_channel_from_dt(struct i2c_client *client, + return 0; + } + +-static int lm90_parse_dt_channel_info(struct i2c_client *client, +- struct lm90_data *data) ++static int lm90_parse_channel_info(struct i2c_client *client, ++ struct lm90_data *data) + { + int err; + struct device *dev = &client->dev; +- const struct device_node *np = dev->of_node; + +- for_each_child_of_node_scoped(np, child) { +- if (strcmp(child->name, "channel")) ++ device_for_each_child_node_scoped(dev, child) { ++ if (!fwnode_name_eq(child, "channel")) + continue; + +- err = lm90_probe_channel_from_dt(client, child, data); ++ err = lm90_probe_channel(client, child, data); + if (err) + return err; + } +@@ -2887,12 +2891,10 @@ static int lm90_probe(struct i2c_client *client) + /* Set maximum conversion rate */ + data->max_convrate = lm90_params[data->kind].max_convrate; + +- /* Parse device-tree channel information */ +- if (client->dev.of_node) { +- err = lm90_parse_dt_channel_info(client, data); +- if (err) +- return err; +- } ++ /* Parse channel information */ ++ err = lm90_parse_channel_info(client, data); ++ if (err) ++ return err; + + /* Initialize the LM90 chip */ + err = lm90_init_client(client, data); +@@ -2918,10 +2920,8 @@ static int lm90_probe(struct i2c_client *client) + err = devm_request_threaded_irq(dev, client->irq, + NULL, lm90_irq_thread, + IRQF_ONESHOT, "lm90", client); +- if (err < 0) { +- dev_err(dev, "cannot request IRQ %d\n", client->irq); ++ if (err < 0) + return err; +- } + } + + return 0; +@@ -2985,7 +2985,7 @@ static struct i2c_driver lm90_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "lm90", +- .of_match_table = of_match_ptr(lm90_of_match), ++ .of_match_table = lm90_of_match, + .pm = pm_sleep_ptr(&lm90_pm_ops), + }, + .probe = lm90_probe, +diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c +--- a/drivers/hwmon/nct6775-core.c ++++ b/drivers/hwmon/nct6775-core.c +@@ -33,6 +33,7 @@ + * (0xd451) + * nct6798d 14 7 7 2+6 0xd428 0xc1 0x5ca3 + * (0xd429) ++ * nct5585d 14 7 7 2+6 0xd428 0xc1 0x5ca3 + * nct6796d-s 18 7 7 6+2 0xd801 0xc1 0x5ca3 + * nct6799d-r 18 7 7 6+2 0xd802 0xc1 0x5ca3 + * +diff --git a/drivers/hwmon/nct6775-platform.c b/drivers/hwmon/nct6775-platform.c +--- a/drivers/hwmon/nct6775-platform.c ++++ b/drivers/hwmon/nct6775-platform.c +@@ -34,7 +34,7 @@ static const char * const nct6775_sio_names[] __initconst = { + [nct6795] = "NCT6795D", + [nct6796] = "NCT6796D", + [nct6797] = "NCT6797D", +- [nct6798] = "NCT6798D", ++ [nct6798] = "NCT6798D/NCT5585D", + [nct6799] = "NCT6796D-S/NCT6799D-R", + }; + +diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c +--- a/drivers/hwmon/npcm750-pwm-fan.c ++++ b/drivers/hwmon/npcm750-pwm-fan.c +@@ -996,10 +996,8 @@ static int npcm7xx_pwm_fan_probe(struct platform_device *pdev) + sprintf(name, "NPCM7XX-FAN-MD%d", i); + ret = devm_request_irq(dev, data->fan_irq[i], npcm7xx_fan_isr, + 0, name, (void *)data); +- if (ret) { +- dev_err(dev, "register IRQ fan%d failed\n", i); ++ if (ret) + return ret; +- } + } + + for_each_child_of_node_scoped(np, child) { +diff --git a/drivers/hwmon/nzxt-kraken3.c b/drivers/hwmon/nzxt-kraken3.c +--- a/drivers/hwmon/nzxt-kraken3.c ++++ b/drivers/hwmon/nzxt-kraken3.c +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: GPL-2.0+ + /* +- * hwmon driver for NZXT Kraken X53/X63/X73, Z53/Z63/Z73 and 2023/2023 Elite all in one coolers. ++ * hwmon driver for NZXT Kraken X53/X63/X73, Z53/Z63/Z73, 2023/2023 Elite, and ++ * 2024 Elite all in one coolers. + * X53 and Z53 in code refer to all models in their respective series (shortened for brevity). + * 2023 models use the Z53 code paths. + * +@@ -25,6 +26,7 @@ + #define USB_PRODUCT_ID_Z53 0x3008 + #define USB_PRODUCT_ID_KRAKEN2023 0x300E + #define USB_PRODUCT_ID_KRAKEN2023_ELITE 0x300C ++#define USB_PRODUCT_ID_KRAKEN2024_ELITE 0x3012 + + enum kinds { X53, Z53, KRAKEN2023 } __packed; + enum pwm_enable { off, manual, curve } __packed; +@@ -927,6 +929,10 @@ static int kraken3_probe(struct hid_device *hdev, const struct hid_device_id *id + priv->kind = KRAKEN2023; + device_name = "kraken2023elite"; + break; ++ case USB_PRODUCT_ID_KRAKEN2024_ELITE: ++ priv->kind = KRAKEN2023; ++ device_name = "kraken2024elite"; ++ break; + default: + ret = -ENODEV; + goto fail_and_close; +@@ -994,6 +1000,7 @@ static const struct hid_device_id kraken3_table[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_NZXT, USB_PRODUCT_ID_Z53) }, + { HID_USB_DEVICE(USB_VENDOR_ID_NZXT, USB_PRODUCT_ID_KRAKEN2023) }, + { HID_USB_DEVICE(USB_VENDOR_ID_NZXT, USB_PRODUCT_ID_KRAKEN2023_ELITE) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_NZXT, USB_PRODUCT_ID_KRAKEN2024_ELITE) }, + { } + }; + +diff --git a/drivers/hwmon/peci/common.h b/drivers/hwmon/peci/common.h +--- a/drivers/hwmon/peci/common.h ++++ b/drivers/hwmon/peci/common.h +@@ -31,7 +31,7 @@ struct peci_sensor_data { + + /** + * peci_sensor_need_update() - check whether sensor update is needed or not +- * @sensor: pointer to sensor data struct ++ * @state: pointer to sensor state struct + * + * Return: true if update is needed, false if not. + */ +@@ -44,7 +44,7 @@ static inline bool peci_sensor_need_update(struct peci_sensor_state *state) + + /** + * peci_sensor_mark_updated() - mark the sensor is updated +- * @sensor: pointer to sensor data struct ++ * @state: pointer to sensor state struct + */ + static inline void peci_sensor_mark_updated(struct peci_sensor_state *state) + { +diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig +--- a/drivers/hwmon/pmbus/Kconfig ++++ b/drivers/hwmon/pmbus/Kconfig +@@ -52,8 +52,8 @@ config SENSORS_ADM1275 + help + If you say yes here you get hardware monitoring support for Analog + Devices ADM1075, ADM1272, ADM1273, ADM1275, ADM1276, ADM1278, ADM1281, +- ADM1293, ADM1294 and SQ24905C Hot-Swap Controller and +- Digital Power Monitors. ++ ADM1293, ADM1294, ROHM BD12780, ROHM BD12790, and SQ24905C ++ Hot-Swap Controller and Digital Power Monitors. + + This driver can also be built as a module. If so, the module will + be called adm1275. +@@ -394,10 +394,10 @@ config SENSORS_MAX20751 + be called max20751. + + config SENSORS_MAX20830 +- tristate "Analog Devices MAX20830" ++ tristate "Analog Devices MAX20830 and compatibles" + help + If you say yes here you get hardware monitoring support for Analog +- Devices MAX20830. ++ Devices MAX20830, MAX20830C, and MAX20840C. + + This driver can also be built as a module. If so, the module will + be called max20830. +@@ -434,8 +434,9 @@ config SENSORS_MAX34440 + tristate "Maxim MAX34440 and compatibles" + help + If you say yes here you get hardware monitoring support for Maxim +- MAX34440, MAX34441, MAX34446, MAX34451, MAX34460, and MAX34461. +- Other compatible are ADPM12160, and ADPM12200. ++ MAX34440, MAX34441, MAX34446, MAX34451, MAX34452, MAX34460, and ++ MAX34461. Other compatible devices are ADPM12160, ADPM12200, and ++ ADPM12250. + + This driver can also be built as a module. If so, the module will + be called max34440. +@@ -607,6 +608,15 @@ config SENSORS_MPQ7932 + This driver can also be built as a module. If so, the module will + be called mpq7932. + ++config SENSORS_MPQ82D00 ++ tristate "MPS MPQ82D00" ++ help ++ If you say yes here you get hardware monitoring functionality support ++ for power management IC MPS MPQ82D00. ++ ++ This driver can also be built as a module. If so, the module will ++ be called mpq82d00. ++ + config SENSORS_MPQ8785 + tristate "MPS MPQ8785" + help +@@ -616,6 +626,37 @@ config SENSORS_MPQ8785 + This driver can also be built as a module. If so, the module will + be called mpq8785. + ++config SENSORS_MPQ8646 ++ tristate "MPS MPQ8646" ++ depends on REGULATOR || !REGULATOR ++ depends on NVMEM || !NVMEM ++ help ++ If you say yes here you get hardware monitoring support for the ++ Monolithic Power Systems MPQ8646. ++ ++ This driver can also be built as a module. If so, the module ++ will be called mpq8646. ++ ++config SENSORS_MPQ8646_DEBUG_UNSAFE ++ bool "MPQ8646 unsafe write/provisioning debugfs (DANGEROUS)" ++ depends on SENSORS_MPQ8646 && DEBUG_FS ++ default n ++ help ++ Expose additional WRITE-able debugfs files in the client's ++ pmbus debugfs directory, ++ /sys/kernel/debug/i2c/i2c-/-/ ++ ++ These are provisioning and bring-up aids. On many designs the ++ MPQ8646 powers the SoC core rail, so a wrong write can brown out ++ or permanently mis-provision the board. ++ ++ WARNING: Wrong register writes can and likely will physically ++ damage or destroy the chip and/or the board. ++ ++ It shall never be set in a production, shipping, or default configuration. ++ ++ If unsure, say N. ++ + config SENSORS_PIM4328 + tristate "Flex PIM4328 and compatibles" + help +@@ -677,6 +718,25 @@ config SENSORS_STEF48H28 + This driver can also be built as a module. If so, the module will + be called stef48h28. + ++config SENSORS_SQ24860 ++ tristate "Silergy SQ24860" ++ help ++ If you say yes here you get hardware monitoring support for Silergy ++ SQ24860 eFuse. ++ ++ This driver can also be built as a module. If so, the module will ++ be called sq24860. ++ ++config SENSORS_SQ24860_REGULATOR ++ bool "Regulator support for SQ24860" ++ depends on SENSORS_SQ24860 && REGULATOR ++ default SENSORS_SQ24860 ++ help ++ If you say yes here you get regulator support for Silergy SQ24860. ++ The regulator is registered through the PMBus regulator framework and ++ can be used to control the output exposed by the device. ++ This option is only useful if regulator framework support is needed. ++ + config SENSORS_STPDDC60 + tristate "ST STPDDC60" + help +@@ -767,6 +827,15 @@ config SENSORS_UCD9200 + This driver can also be built as a module. If so, the module will + be called ucd9200. + ++config SENSORS_VT7505 ++ tristate "Analog Devices MAX16545, MAX16550 and Volterra VT7505" ++ help ++ If you say yes here you get hardware monitoring support for Analog ++ Devices MAX16545, MAX16550 and Volterra VT7505 PMBus controllers. ++ ++ This driver can also be built as a module. If so, the module will ++ be called vt7505. ++ + config SENSORS_XDP710 + tristate "Infineon XDP710 family" + help +@@ -803,6 +872,16 @@ config SENSORS_XDPE1A2G7B + This driver can also be built as a module. If so, the module will + be called xdpe1a2g7b. + ++config SENSORS_XDPE1A2G7B_REGULATOR ++ bool "Regulator support for XDPE1A2G7B and compatibles" ++ depends on SENSORS_XDPE1A2G7B && REGULATOR ++ help ++ If you say yes here you get regulator support for Infineon ++ XDPE1A2G5B and XDPE1A2G7B multi-phase digital controllers. ++ ++ This enables the controllers to be used as regulator devices, ++ providing voltage control through the regulator framework. ++ + config SENSORS_XDPE122 + tristate "Infineon XDPE122 family" + help +diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile +--- a/drivers/hwmon/pmbus/Makefile ++++ b/drivers/hwmon/pmbus/Makefile +@@ -59,12 +59,15 @@ obj-$(CONFIG_SENSORS_MP5990) += mp5990.o + obj-$(CONFIG_SENSORS_MP9941) += mp9941.o + obj-$(CONFIG_SENSORS_MP9945) += mp9945.o + obj-$(CONFIG_SENSORS_MPQ7932) += mpq7932.o ++obj-$(CONFIG_SENSORS_MPQ82D00) += mpq82d00.o + obj-$(CONFIG_SENSORS_MPQ8785) += mpq8785.o ++obj-$(CONFIG_SENSORS_MPQ8646) += mpq8646.o + obj-$(CONFIG_SENSORS_PLI1209BC) += pli1209bc.o + obj-$(CONFIG_SENSORS_PM6764TR) += pm6764tr.o + obj-$(CONFIG_SENSORS_PXE1610) += pxe1610.o + obj-$(CONFIG_SENSORS_Q54SJ108A2) += q54sj108a2.o + obj-$(CONFIG_SENSORS_STEF48H28) += stef48h28.o ++obj-$(CONFIG_SENSORS_SQ24860) += sq24860.o + obj-$(CONFIG_SENSORS_STPDDC60) += stpddc60.o + obj-$(CONFIG_SENSORS_TDA38640) += tda38640.o + obj-$(CONFIG_SENSORS_TPS25990) += tps25990.o +@@ -73,6 +76,7 @@ obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o + obj-$(CONFIG_SENSORS_TPS546D24) += tps546d24.o + obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o + obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o ++obj-$(CONFIG_SENSORS_VT7505) += vt7505.o + obj-$(CONFIG_SENSORS_XDP710) += xdp710.o + obj-$(CONFIG_SENSORS_XDP720) += xdp720.o + obj-$(CONFIG_SENSORS_XDPE122) += xdpe12284.o +diff --git a/drivers/hwmon/pmbus/acbel-fsg032.c b/drivers/hwmon/pmbus/acbel-fsg032.c +--- a/drivers/hwmon/pmbus/acbel-fsg032.c ++++ b/drivers/hwmon/pmbus/acbel-fsg032.c +@@ -52,6 +52,7 @@ static const struct i2c_device_id acbel_fsg032_id[] = { + { .name = "acbel_fsg032" }, + { } + }; ++MODULE_DEVICE_TABLE(i2c, acbel_fsg032_id); + + static struct pmbus_driver_info acbel_fsg032_info = { + .pages = 1, +diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c +--- a/drivers/hwmon/pmbus/adm1275.c ++++ b/drivers/hwmon/pmbus/adm1275.c +@@ -19,7 +19,7 @@ + #include "pmbus.h" + + enum chips { adm1075, adm1272, adm1273, adm1275, adm1276, adm1278, adm1281, +- adm1293, adm1294, sq24905c }; ++ adm1293, adm1294, bd12780, bd12790, sq24905c }; + + #define ADM1275_MFR_STATUS_IOUT_WARN2 BIT(0) + #define ADM1293_MFR_STATUS_VAUX_UV_WARN BIT(5) +@@ -47,6 +47,8 @@ enum chips { adm1075, adm1272, adm1273, adm1275, adm1276, adm1278, adm1281, + #define ADM1278_VOUT_EN BIT(1) + + #define ADM1278_PMON_DEFCONFIG (ADM1278_VOUT_EN | ADM1278_TEMP1_EN | ADM1278_TSFILT) ++/* The BD127[89]0 data sheets mark TSFILT bit as reserved. */ ++#define BD12780_PMON_DEFCONFIG (ADM1278_VOUT_EN | ADM1278_TEMP1_EN) + + #define ADM1293_IRANGE_25 0 + #define ADM1293_IRANGE_50 BIT(6) +@@ -134,6 +136,30 @@ static const struct coefficients adm1272_coefficients[] = { + + }; + ++/* ++ * BD12790 coefficients derived from preliminary datasheet, Table 1 (p.18) ++ * and the PMBus direct-format relationship X = (Y * 10^(-R) - b) / m. ++ * ++ * Voltage: V[V] = 14.77e-3 * code (60V) / 24.62e-3 * code (100V) ++ * -> m = 6770, R=-2 / m = 4062, R=-2 ++ * Current: code = I[A] * RS * 132802.1 + 2048 (15mV) / * 66401.06 + 2048 (30mV) ++ * -> m = 1328, b = 2048 * 10^(-R) = 20480, R=-1 / m = 664, same b and R ++ * Power: code = k * RS * PIN, k = 35119.94 / 17559.97 / 21071.44 / 10535.72 ++ * -> m = round(k * 10^(-3-R)), R=-2 for 60V/15mV, R=-3 for the other three ++ * Temperature: code = 4.2 * T + 3188 -> m = 42, b = 3188 * 10 = 31880, R=-1 ++ */ ++static const struct coefficients bd12790_coefficients[] = { ++ [0] = { 6770, 0, -2 }, /* voltage, vrange 60V */ ++ [1] = { 4062, 0, -2 }, /* voltage, vrange 100V */ ++ [2] = { 1328, 20480, -1 }, /* current, vsense range 15mV */ ++ [3] = { 664, 20480, -1 }, /* current, vsense range 30mV */ ++ [4] = { 3512, 0, -2 }, /* power, vrange 60V, irange 15mV */ ++ [5] = { 21071, 0, -3 }, /* power, vrange 100V, irange 15mV */ ++ [6] = { 17560, 0, -3 }, /* power, vrange 60V, irange 30mV */ ++ [7] = { 10536, 0, -3 }, /* power, vrange 100V, irange 30mV */ ++ [8] = { 42, 31880, -1 }, /* temperature */ ++}; ++ + static const struct coefficients adm1275_coefficients[] = { + [0] = { 19199, 0, -2 }, /* voltage, vrange set */ + [1] = { 6720, 0, -1 }, /* voltage, vrange not set */ +@@ -487,6 +513,8 @@ static const struct i2c_device_id adm1275_id[] = { + { .name = "adm1281", .driver_data = adm1281 }, + { .name = "adm1293", .driver_data = adm1293 }, + { .name = "adm1294", .driver_data = adm1294 }, ++ { .name = "bd12780", .driver_data = bd12780 }, ++ { .name = "bd12790", .driver_data = bd12790 }, + { .name = "mc09c", .driver_data = sq24905c }, + { } + }; +@@ -494,12 +522,13 @@ MODULE_DEVICE_TABLE(i2c, adm1275_id); + + /* Enable VOUT & TEMP1 if not enabled (disabled by default) */ + static int adm1275_enable_vout_temp(struct adm1275_data *data, +- struct i2c_client *client, int config) ++ struct i2c_client *client, int config, ++ u16 defconfig) + { + int ret; + +- if ((config & ADM1278_PMON_DEFCONFIG) != ADM1278_PMON_DEFCONFIG) { +- config |= ADM1278_PMON_DEFCONFIG; ++ if ((config & defconfig) != defconfig) { ++ config |= defconfig; + ret = adm1275_write_pmon_config(data, client, config); + if (ret < 0) { + dev_err(&client->dev, "Failed to enable VOUT/TEMP1 monitoring\n"); +@@ -524,23 +553,19 @@ static int adm1275_probe(struct i2c_client *client) + u32 shunt; + u32 avg; + +- if (!i2c_check_functionality(client->adapter, +- I2C_FUNC_SMBUS_READ_BYTE_DATA +- | I2C_FUNC_SMBUS_BLOCK_DATA)) +- return -ENODEV; +- +- ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, block_buffer); ++ ret = pmbus_read_smbus_i2c_block_data(client, PMBUS_MFR_ID, block_buffer); + if (ret < 0) { + dev_err(&client->dev, "Failed to read Manufacturer ID\n"); + return ret; + } + if ((ret != 3 || strncmp(block_buffer, "ADI", 3)) && +- (ret != 2 || strncmp(block_buffer, "SY", 2))) { ++ (ret != 2 || strncmp(block_buffer, "SY", 2)) && ++ (ret != 4 || strncmp(block_buffer, "ROHM", 4))) { + dev_err(&client->dev, "Unsupported Manufacturer ID\n"); + return -ENODEV; + } + +- ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, block_buffer); ++ ret = pmbus_read_smbus_i2c_block_data(client, PMBUS_MFR_MODEL, block_buffer); + if (ret < 0) { + dev_err(&client->dev, "Failed to read Manufacturer Model\n"); + return ret; +@@ -562,6 +587,7 @@ static int adm1275_probe(struct i2c_client *client) + if (mid->driver_data == adm1272 || mid->driver_data == adm1273 || + mid->driver_data == adm1278 || mid->driver_data == adm1281 || + mid->driver_data == adm1293 || mid->driver_data == adm1294 || ++ mid->driver_data == bd12780 || mid->driver_data == bd12790 || + mid->driver_data == sq24905c) + config_read_fn = i2c_smbus_read_word_data; + else +@@ -642,6 +668,7 @@ static int adm1275_probe(struct i2c_client *client) + data->have_power_sampling = true; + + coefficients = adm1272_coefficients; ++ + vindex = (config & ADM1275_VRANGE) ? 1 : 0; + cindex = (config & ADM1272_IRANGE) ? 3 : 2; + /* pindex depends on the combination of the above */ +@@ -666,7 +693,53 @@ static int adm1275_probe(struct i2c_client *client) + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + +- ret = adm1275_enable_vout_temp(data, client, config); ++ ret = adm1275_enable_vout_temp(data, client, config, ++ ADM1278_PMON_DEFCONFIG); ++ if (ret) ++ return ret; ++ ++ if (config & ADM1278_VIN_EN) ++ info->func[0] |= PMBUS_HAVE_VIN; ++ break; ++ ++ /* ++ * The BD12790 is almost identical to the adm1272. Only the defconfig ++ * and coefficients have minor differences. ++ */ ++ case bd12790: ++ data->have_vout = true; ++ data->have_pin_max = true; ++ data->have_temp_max = true; ++ data->have_power_sampling = true; ++ ++ coefficients = bd12790_coefficients; ++ ++ vindex = (config & ADM1275_VRANGE) ? 1 : 0; ++ cindex = (config & ADM1272_IRANGE) ? 3 : 2; ++ /* pindex depends on the combination of the above */ ++ switch (config & (ADM1275_VRANGE | ADM1272_IRANGE)) { ++ case 0: ++ default: ++ pindex = 4; ++ break; ++ case ADM1275_VRANGE: ++ pindex = 5; ++ break; ++ case ADM1272_IRANGE: ++ pindex = 6; ++ break; ++ case ADM1275_VRANGE | ADM1272_IRANGE: ++ pindex = 7; ++ break; ++ } ++ tindex = 8; ++ ++ info->func[0] |= PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT | ++ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | ++ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; ++ ++ ret = adm1275_enable_vout_temp(data, client, config, ++ BD12780_PMON_DEFCONFIG); + if (ret) + return ret; + +@@ -728,13 +801,45 @@ static int adm1275_probe(struct i2c_client *client) + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + +- ret = adm1275_enable_vout_temp(data, client, config); ++ ret = adm1275_enable_vout_temp(data, client, config, ++ ADM1278_PMON_DEFCONFIG); + if (ret) + return ret; + + if (config & ADM1278_VIN_EN) + info->func[0] |= PMBUS_HAVE_VIN; + break; ++ ++ /* ++ * The BD12780 is almost functionally identical with the adm1278 above. ++ * Only differences visible to the driver are lack of TSFILT bits and ++ * different identification register contents. ++ */ ++ case bd12780: ++ data->have_vout = true; ++ data->have_pin_max = true; ++ data->have_temp_max = true; ++ data->have_power_sampling = true; ++ ++ coefficients = adm1278_coefficients; ++ vindex = 0; ++ cindex = 1; ++ pindex = 2; ++ tindex = 3; ++ ++ info->func[0] |= PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT | ++ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | ++ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; ++ ++ ret = adm1275_enable_vout_temp(data, client, config, ++ BD12780_PMON_DEFCONFIG); ++ if (ret) ++ return ret; ++ ++ if (config & ADM1278_VIN_EN) ++ info->func[0] |= PMBUS_HAVE_VIN; ++ ++ break; + case adm1293: + case adm1294: + data->have_iout_min = true; +@@ -870,9 +975,27 @@ static int adm1275_probe(struct i2c_client *client) + return pmbus_do_probe(client, info); + } + ++static const struct of_device_id adm1275_of_match[] = { ++ { .compatible = "adi,adm1075", }, ++ { .compatible = "adi,adm1272", }, ++ { .compatible = "adi,adm1273", }, ++ { .compatible = "adi,adm1275", }, ++ { .compatible = "adi,adm1276", }, ++ { .compatible = "adi,adm1278", }, ++ { .compatible = "adi,adm1281", }, ++ { .compatible = "adi,adm1293", }, ++ { .compatible = "adi,adm1294", }, ++ { .compatible = "rohm,bd12780", }, ++ { .compatible = "rohm,bd12790", }, ++ { .compatible = "silergy,mc09c", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, adm1275_of_match); ++ + static struct i2c_driver adm1275_driver = { + .driver = { + .name = "adm1275", ++ .of_match_table = adm1275_of_match, + }, + .probe = adm1275_probe, + .id_table = adm1275_id, +diff --git a/drivers/hwmon/pmbus/ir36021.c b/drivers/hwmon/pmbus/ir36021.c +--- a/drivers/hwmon/pmbus/ir36021.c ++++ b/drivers/hwmon/pmbus/ir36021.c +@@ -31,21 +31,13 @@ static int ir36021_probe(struct i2c_client *client) + u8 buf[I2C_SMBUS_BLOCK_MAX]; + int ret; + +- if (!i2c_check_functionality(client->adapter, +- I2C_FUNC_SMBUS_READ_BYTE_DATA +- | I2C_FUNC_SMBUS_READ_WORD_DATA +- | I2C_FUNC_SMBUS_READ_BLOCK_DATA)) +- return -ENODEV; +- +- ret = i2c_smbus_read_i2c_block_data(client, PMBUS_MFR_MODEL, 2, buf); +- if (ret < 0) { +- dev_err(&client->dev, "Failed to read PMBUS_MFR_MODEL\n"); +- return ret; +- } +- if (ret != 2 || buf[0] != 0x01 || buf[1] != 0x2d) { +- dev_err(&client->dev, "MFR_MODEL unrecognised\n"); +- return -ENODEV; +- } ++ ret = pmbus_read_smbus_i2c_block_data(client, PMBUS_MFR_MODEL, buf); ++ if (ret < 0) ++ return dev_err_probe(&client->dev, ret, ++ "Failed to read PMBUS_MFR_MODEL\n"); ++ if (ret != 1 || buf[0] != 0x2d) ++ return dev_err_probe(&client->dev, -ENODEV, ++ "MFR_MODEL unrecognised\n"); + + return pmbus_do_probe(client, &ir36021_info); + } +diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c +--- a/drivers/hwmon/pmbus/lm25066.c ++++ b/drivers/hwmon/pmbus/lm25066.c +@@ -35,6 +35,7 @@ enum chips { lm25056, lm25066, lm5064, lm5066, lm5066i }; + #define LM25066_READ_AVG_PIN 0xdf + + #define LM25066_DEV_SETUP_CL BIT(4) /* Current limit */ ++#define LM25066_DEV_SETUP_CL_CFG BIT(2) /* Current limit configuration */ + + #define LM25066_SAMPLES_FOR_AVG_MAX 4096 + +@@ -485,6 +486,42 @@ static int lm25066_probe(struct i2c_client *client) + + data->id = (enum chips)(unsigned long)i2c_get_match_data(client); + ++ if (data->id != lm25056) { ++ int config_new = config; ++ const char *cl_setting; ++ int ret; ++ ++ if (!of_property_read_string(client->dev.of_node, ++ "ti,current-range", &cl_setting)) { ++ config_new |= LM25066_DEV_SETUP_CL_CFG; ++ if (strcmp(cl_setting, "high") == 0) { ++ if (data->id == lm25066) ++ config_new |= LM25066_DEV_SETUP_CL; ++ else ++ config_new &= ~LM25066_DEV_SETUP_CL; ++ } else if (strcmp(cl_setting, "low") == 0) { ++ if (data->id == lm25066) ++ config_new &= ~LM25066_DEV_SETUP_CL; ++ else ++ config_new |= LM25066_DEV_SETUP_CL; ++ } else { ++ dev_err(&client->dev, ++ "invalid current-range setting: %s\n", ++ cl_setting); ++ return -EINVAL; ++ } ++ } ++ ++ if (config_new != config) { ++ ret = i2c_smbus_write_byte_data(client, ++ LM25066_DEVICE_SETUP, ++ config_new); ++ if (ret < 0) ++ return ret; ++ config = config_new; ++ } ++ } ++ + info = &data->info; + + info->pages = 1; +diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c +--- a/drivers/hwmon/pmbus/ltc2978.c ++++ b/drivers/hwmon/pmbus/ltc2978.c +@@ -611,17 +611,13 @@ static int ltc2978_get_id(struct i2c_client *client) + u8 buf[I2C_SMBUS_BLOCK_MAX]; + int ret; + +- if (!i2c_check_functionality(client->adapter, +- I2C_FUNC_SMBUS_READ_BLOCK_DATA)) +- return -ENODEV; +- +- ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf); ++ ret = pmbus_read_smbus_i2c_block_data(client, PMBUS_MFR_ID, buf); + if (ret < 0) + return ret; + if (ret < 3 || (strncmp(buf, "LTC", 3) && strncmp(buf, "ADI", 3))) + return -ENODEV; + +- ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf); ++ ret = pmbus_read_smbus_i2c_block_data(client, PMBUS_MFR_MODEL, buf); + if (ret < 0) + return ret; + for (id = <c2978_id[0]; strlen(id->name); id++) { +@@ -637,16 +633,17 @@ static int ltc2978_get_id(struct i2c_client *client) + u8 buf[I2C_SMBUS_BLOCK_MAX]; + int ret; + +- ret = i2c_smbus_read_i2c_block_data(client, PMBUS_IC_DEVICE_ID, +- sizeof(buf), buf); ++ ret = pmbus_read_smbus_i2c_block_data(client, PMBUS_IC_DEVICE_ID, ++ buf); + if (ret < 0) + return ret; + +- if (!strncmp(buf + 1, "LT7170", 6) || +- !strncmp(buf + 1, "LT7170-1", 8)) ++ if (ret < 6) ++ return -ENODEV; ++ ++ if (!strncmp(buf, "LT7170", 6)) + return lt7170; +- if (!strncmp(buf + 1, "LT7171", 6) || +- !strncmp(buf + 1, "LT7171-1", 8)) ++ if (!strncmp(buf, "LT7171", 6)) + return lt7171; + + return -ENODEV; +diff --git a/drivers/hwmon/pmbus/max20830.c b/drivers/hwmon/pmbus/max20830.c +--- a/drivers/hwmon/pmbus/max20830.c ++++ b/drivers/hwmon/pmbus/max20830.c +@@ -7,12 +7,69 @@ + + #include + #include ++#include + #include + #include + #include "pmbus.h" + + #define MAX20830_IC_DEVICE_ID_LENGTH 9 + ++struct max20830_data { ++ struct pmbus_driver_info info; ++ u32 vout_rfb1; ++ u32 vout_rfb2; ++}; ++ ++static const char * const supported_chip_ids[] = { ++ "MAX20830", ++ "MAX20830C", ++ "MAX20840C", ++}; ++ ++/* ++ * MAX20830 only supports READ_VOUT for VOUT monitoring. ++ * ++ * Limit registers (VOUT_OV_WARN_LIMIT, VOUT_OV_FAULT_LIMIT, etc.) are not ++ * supported by this driver and return -ENODATA. This means sysfs attributes ++ * like in1_max, in1_crit, etc. will not be available. Only in1_input (the ++ * scaled output voltage) is supported. ++ * ++ * MAX20830 uses an external resistor divider for voltage sensing: ++ * - VOUT_COMMAND and VOUT_MAX set the reference voltage at the feedback pin ++ * - READ_VOUT reports the feedback voltage, which needs to be scaled for actual ++ * output voltage ++ * ++ * Scaling formula: vout_actual = vout_fb × (1 + RFB1 / RFB2) ++ * ++ * If regulator support is added in the future, some adjustments are needed to ++ * ensure correct feedback voltages are set. ++ */ ++static int max20830_read_word_data(struct i2c_client *client, int page, ++ int phase, int reg) ++{ ++ const struct pmbus_driver_info *info = pmbus_get_driver_info(client); ++ const struct max20830_data *data = container_of(info, struct max20830_data, info); ++ int ret; ++ u64 temp; ++ ++ switch (reg) { ++ case PMBUS_READ_VOUT: ++ ret = pmbus_read_word_data(client, page, phase, reg); ++ if (ret < 0) ++ return ret; ++ ++ /* Apply voltage divider scaling if resistors are non-zero */ ++ if (data->vout_rfb1 && data->vout_rfb2) { ++ temp = (u64)data->vout_rfb1 + (u64)data->vout_rfb2; ++ temp = DIV_ROUND_CLOSEST_ULL((u64)ret * temp, data->vout_rfb2); ++ ret = clamp_val(temp, 0, 0xFFFF); ++ } ++ return ret; ++ default: ++ return -ENODATA; ++ } ++} ++ + static struct pmbus_driver_info max20830_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, +@@ -23,61 +80,53 @@ static struct pmbus_driver_info max20830_info = { + PMBUS_HAVE_TEMP | + PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP, ++ .read_word_data = max20830_read_word_data, ++ .have_pmbus_revision = true, ++ .pmbus_revision = PMBUS_REV_13, + }; + + static int max20830_probe(struct i2c_client *client) + { + u8 buf[I2C_SMBUS_BLOCK_MAX + 1] = {}; +- int ret; ++ struct max20830_data *data; ++ int i, ret; + +- if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BLOCK_DATA) && +- !i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) +- return -ENODEV; ++ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; + +- /* +- * Use i2c_smbus_read_block_data() if supported, otherwise fall back +- * to i2c_smbus_read_i2c_block_data() to support I2C controllers +- * which do not support SMBus block reads. +- */ +- if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BLOCK_DATA)) { +- /* Reads 9 Data bytes from MAX20830 */ +- ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf); +- if (ret < 0) +- return dev_err_probe(&client->dev, ret, +- "Failed to read IC_DEVICE_ID\n"); +- } else { +- /* Reads 1 length byte + 9 Data bytes from MAX20830 */ +- ret = i2c_smbus_read_i2c_block_data(client, PMBUS_IC_DEVICE_ID, +- MAX20830_IC_DEVICE_ID_LENGTH + 1, +- buf); +- if (ret < 0) +- return dev_err_probe(&client->dev, ret, +- "Failed to read IC_DEVICE_ID\n"); +- /* +- * Moves data forward, removing the length byte, this is to +- * match the format of i2c_smbus_read_block_data(). +- * Also adjust return value to reflect length byte removal. +- */ +- memmove(buf, buf + 1, MAX20830_IC_DEVICE_ID_LENGTH); +- ret = ret - 1; +- } ++ data->info = max20830_info; + +- /* +- * MAX20830 IC_DEVICE_ID sends string data "MAX20830\0". +- * Return value should at least be 9 bytes of data. +- */ ++ /* Read optional voltage divider resistor values */ ++ device_property_read_u32(&client->dev, "adi,vout-rfb1-ohms", &data->vout_rfb1); ++ device_property_read_u32(&client->dev, "adi,vout-rfb2-ohms", &data->vout_rfb2); ++ ++ ret = pmbus_read_smbus_i2c_block_data(client, PMBUS_IC_DEVICE_ID, buf); ++ if (ret < 0) ++ return dev_err_probe(&client->dev, ret, ++ "Failed to read IC_DEVICE_ID\n"); ++ ++ /* Verify we read the expected number of bytes */ + if (ret < MAX20830_IC_DEVICE_ID_LENGTH) + return dev_err_probe(&client->dev, -ENODEV, +- "IC_DEVICE_ID too short: expected at least 9 bytes, got %d\n", +- ret); ++ "IC_DEVICE_ID too short: expected %d bytes, got %d\n", ++ MAX20830_IC_DEVICE_ID_LENGTH, ret); + +- /* 9 bytes of data, buf[0]-buf[7] = "MAX20830", buf[8] = '\0' */ +- buf[MAX20830_IC_DEVICE_ID_LENGTH - 1] = '\0'; +- if (strncmp(buf, "MAX20830", MAX20830_IC_DEVICE_ID_LENGTH - 1)) ++ /* Null-terminate the string */ ++ buf[ret] = '\0'; ++ ++ /* Verify the device ID matches what we expect */ ++ for (i = 0; i < ARRAY_SIZE(supported_chip_ids); i++) { ++ if (!strcmp(buf, supported_chip_ids[i])) ++ break; ++ } ++ ++ /* No match found - unsupported device */ ++ if (i == ARRAY_SIZE(supported_chip_ids)) + return dev_err_probe(&client->dev, -ENODEV, +- "Unsupported device: '%s'\n", buf); ++ "Unsupported device: '%*pE'\n", ret, buf); + +- return pmbus_do_probe(client, &max20830_info); ++ return pmbus_do_probe(client, &data->info); + } + + static const struct i2c_device_id max20830_id[] = { +diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c +--- a/drivers/hwmon/pmbus/max34440.c ++++ b/drivers/hwmon/pmbus/max34440.c +@@ -23,6 +23,7 @@ enum chips { + max34441, + max34446, + max34451, ++ max34452, + max34460, + max34461, + }; +@@ -59,6 +60,7 @@ enum chips { + #define MAX34440_IOUT_OC_FAULT_LIMIT 0x4A + + #define MAX34451ETNA6_MFR_REV 0x0012 ++#define MAX34451ETNA8_MFR_REV 0x0014 + + #define MAX34451_MFR_CHANNEL_CONFIG 0xe4 + #define MAX34451_MFR_CHANNEL_CONFIG_SEL_MASK 0x3f +@@ -105,16 +107,21 @@ static int max34440_read_word_data(struct i2c_client *client, int page, + case PMBUS_UT_FAULT_LIMIT: + case PMBUS_MFR_MAX_TEMP_1: + /* +- * MAX34451/ADPM family do not support VIN/IIN limit registers, +- * manufacturer-specific min/max registers, or undercurrent/ +- * undertemperature fault limits. Accessing these triggers CML +- * error and asserts ALERT. ++ * MAX34451/MAX34452/ADPM family do not support VIN/IIN limit ++ * registers, manufacturer-specific min/max registers, or ++ * undercurrent/undertemperature fault limits. Accessing these ++ * triggers CML error and asserts ALERT. + */ +- if (data->id == max34451 || data->id == adpm12160 || +- data->id == adpm12200 || data->id == adpm12250) ++ if (data->id == max34451 || data->id == max34452 || ++ data->id == adpm12160 || data->id == adpm12200 || ++ data->id == adpm12250) + return -ENXIO; + ret = -ENODATA; + break; ++ case PMBUS_VOUT_OV_WARN_LIMIT: ++ if (data->id == max34452) ++ return -ENXIO; ++ return -ENODATA; + case PMBUS_VIRT_READ_VOUT_MIN: + ret = pmbus_read_word_data(client, page, phase, + MAX34440_MFR_VOUT_MIN); +@@ -125,8 +132,8 @@ static int max34440_read_word_data(struct i2c_client *client, int page, + break; + case PMBUS_VIRT_READ_IOUT_AVG: + if (data->id != max34446 && data->id != max34451 && +- data->id != adpm12160 && data->id != adpm12200 && +- data->id != adpm12250) ++ data->id != max34452 && data->id != adpm12160 && ++ data->id != adpm12200 && data->id != adpm12250) + return -ENXIO; + ret = pmbus_read_word_data(client, page, phase, + MAX34446_MFR_IOUT_AVG); +@@ -191,6 +198,10 @@ static int max34440_write_word_data(struct i2c_client *client, int page, + ret = pmbus_write_word_data(client, page, data->iout_oc_warn_limit, + word); + break; ++ case PMBUS_VOUT_OV_WARN_LIMIT: ++ if (data->id == max34452) ++ return -ENXIO; ++ return -ENODATA; + case PMBUS_VIRT_RESET_POUT_HISTORY: + ret = pmbus_write_word_data(client, page, + MAX34446_MFR_POUT_PEAK, 0); +@@ -211,8 +222,8 @@ static int max34440_write_word_data(struct i2c_client *client, int page, + ret = pmbus_write_word_data(client, page, + MAX34440_MFR_IOUT_PEAK, 0); + if (!ret && (data->id == max34446 || data->id == max34451 || +- data->id == adpm12160 || data->id == adpm12200 || +- data->id == adpm12250)) ++ data->id == max34452 || data->id == adpm12160 || ++ data->id == adpm12200 || data->id == adpm12250)) + ret = pmbus_write_word_data(client, page, + MAX34446_MFR_IOUT_AVG, 0); + +@@ -280,12 +291,13 @@ static int max34451_read_byte_data(struct i2c_client *client, int page, int reg) + case PMBUS_STATUS_BYTE: + case PMBUS_STATUS_OTHER: + /* +- * MAX34451/ADPM family do not support STATUS_BYTE or ++ * MAX34451/MAX34452/ADPM family do not support STATUS_BYTE or + * STATUS_OTHER registers. Accessing them triggers CML + * error and asserts ALERT. + */ +- if (data->id == max34451 || data->id == adpm12160 || +- data->id == adpm12200 || data->id == adpm12250) ++ if (data->id == max34451 || data->id == max34452 || ++ data->id == adpm12160 || data->id == adpm12200 || ++ data->id == adpm12250) + return -ENXIO; + return -ENODATA; + default: +@@ -303,12 +315,13 @@ static int max34451_write_byte_data(struct i2c_client *client, int page, + case PMBUS_STATUS_BYTE: + case PMBUS_STATUS_OTHER: + /* +- * MAX34451/ADPM family do not support STATUS_BYTE or ++ * MAX34451/MAX34452/ADPM family do not support STATUS_BYTE or + * STATUS_OTHER registers. Writing to them triggers CML + * error and asserts ALERT. + */ +- if (data->id == max34451 || data->id == adpm12160 || +- data->id == adpm12200 || data->id == adpm12250) ++ if (data->id == max34451 || data->id == max34452 || ++ data->id == adpm12160 || data->id == adpm12200 || ++ data->id == adpm12250) + return -ENXIO; + return -ENODATA; + default: +@@ -339,16 +352,28 @@ static int max34451_set_supported_funcs(struct i2c_client *client, + if (rv < 0) + return rv; + +- if (rv >= MAX34451ETNA6_MFR_REV) { ++ if (data->id == max34451 && rv >= MAX34451ETNA6_MFR_REV) { + max34451_na6 = true; + data->info.format[PSC_VOLTAGE_IN] = direct; + data->info.format[PSC_CURRENT_IN] = direct; +- data->info.m[PSC_VOLTAGE_IN] = 1; ++ + data->info.b[PSC_VOLTAGE_IN] = 0; +- data->info.R[PSC_VOLTAGE_IN] = 3; +- data->info.m[PSC_CURRENT_IN] = 1; + data->info.b[PSC_CURRENT_IN] = 0; +- data->info.R[PSC_CURRENT_IN] = 2; ++ if (rv >= MAX34451ETNA8_MFR_REV) { ++ data->info.m[PSC_VOLTAGE_IN] = 125; ++ data->info.R[PSC_VOLTAGE_IN] = 0; ++ data->info.m[PSC_VOLTAGE_OUT] = 125; ++ data->info.R[PSC_VOLTAGE_OUT] = 0; ++ data->info.m[PSC_CURRENT_IN] = 250; ++ data->info.R[PSC_CURRENT_IN] = -1; ++ data->info.m[PSC_CURRENT_OUT] = 250; ++ data->info.R[PSC_CURRENT_OUT] = -1; ++ } else { ++ data->info.m[PSC_VOLTAGE_IN] = 1; ++ data->info.R[PSC_VOLTAGE_IN] = 3; ++ data->info.m[PSC_CURRENT_IN] = 1; ++ data->info.R[PSC_CURRENT_IN] = 2; ++ } + data->iout_oc_fault_limit = PMBUS_IOUT_OC_FAULT_LIMIT; + data->iout_oc_warn_limit = PMBUS_IOUT_OC_WARN_LIMIT; + } +@@ -665,6 +690,32 @@ static struct pmbus_driver_info max34440_info[] = { + .write_word_data = max34440_write_word_data, + .page_change_delay = MAX34440_PAGE_CHANGE_DELAY, + }, ++ [max34452] = { ++ .pages = 21, ++ .format[PSC_VOLTAGE_OUT] = direct, ++ .format[PSC_TEMPERATURE] = direct, ++ .format[PSC_CURRENT_OUT] = direct, ++ .m[PSC_VOLTAGE_OUT] = 1, ++ .b[PSC_VOLTAGE_OUT] = 0, ++ .R[PSC_VOLTAGE_OUT] = 3, ++ .m[PSC_CURRENT_OUT] = 1, ++ .b[PSC_CURRENT_OUT] = 0, ++ .R[PSC_CURRENT_OUT] = 2, ++ .m[PSC_TEMPERATURE] = 1, ++ .b[PSC_TEMPERATURE] = 0, ++ .R[PSC_TEMPERATURE] = 2, ++ /* func 0-15 is set dynamically before probing */ ++ .func[16] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, ++ .func[17] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, ++ .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, ++ .func[19] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, ++ .func[20] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, ++ .read_byte_data = max34451_read_byte_data, ++ .read_word_data = max34440_read_word_data, ++ .write_byte_data = max34451_write_byte_data, ++ .write_word_data = max34440_write_word_data, ++ .page_change_delay = MAX34440_PAGE_CHANGE_DELAY, ++ }, + [max34460] = { + .pages = 18, + .format[PSC_VOLTAGE_OUT] = direct, +@@ -748,7 +799,7 @@ static int max34440_probe(struct i2c_client *client) + data->iout_oc_fault_limit = MAX34440_IOUT_OC_FAULT_LIMIT; + data->iout_oc_warn_limit = MAX34440_IOUT_OC_WARN_LIMIT; + +- if (data->id == max34451) { ++ if (data->id == max34451 || data->id == max34452) { + rv = max34451_set_supported_funcs(client, data); + if (rv) + return rv; +@@ -769,6 +820,7 @@ static const struct i2c_device_id max34440_id[] = { + { .name = "max34441", .driver_data = max34441 }, + { .name = "max34446", .driver_data = max34446 }, + { .name = "max34451", .driver_data = max34451 }, ++ { .name = "max34452", .driver_data = max34452 }, + { .name = "max34460", .driver_data = max34460 }, + { .name = "max34461", .driver_data = max34461 }, + { } +diff --git a/drivers/hwmon/pmbus/mp2975.c b/drivers/hwmon/pmbus/mp2975.c +--- a/drivers/hwmon/pmbus/mp2975.c ++++ b/drivers/hwmon/pmbus/mp2975.c +@@ -176,7 +176,6 @@ mp2975_vid2direct(int vrf, int val) + static u16 mp2975_data2reg_linear11(s64 val) + { + s16 exponent = 0, mantissa; +- bool negative = false; + + /* simple case */ + if (val == 0) +@@ -196,10 +195,6 @@ static u16 mp2975_data2reg_linear11(s64 val) + /* Convert mantissa from milli-units to units */ + mantissa = clamp_val(DIV_ROUND_CLOSEST_ULL(val, 1000), 0, 0x3ff); + +- /* restore sign */ +- if (negative) +- mantissa = -mantissa; +- + /* Convert to 5 bit exponent, 11 bit mantissa */ + return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800); + } +diff --git a/drivers/hwmon/pmbus/mpq82d00.c b/drivers/hwmon/pmbus/mpq82d00.c +new file mode 100644 +--- /dev/null ++++ b/drivers/hwmon/pmbus/mpq82d00.c +@@ -0,0 +1,249 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Hardware monitoring driver for MPS Synchronous Step-Down Converter(MPQ82D00) ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "pmbus.h" ++ ++#define MPQ82D00_VOUT_DIV 64 ++ ++#define MPQ82D00_PAGE_NUM 1 ++ ++#define MPQ82D00_RAIL1_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | \ ++ PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP | \ ++ PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | \ ++ PMBUS_HAVE_STATUS_VOUT | \ ++ PMBUS_HAVE_STATUS_IOUT | \ ++ PMBUS_HAVE_STATUS_TEMP | \ ++ PMBUS_HAVE_STATUS_INPUT) ++ ++struct mpq82d00_data { ++ struct pmbus_driver_info info; ++ int vout_scale; ++}; ++ ++#define to_mpq82d00_data(x) container_of(x, struct mpq82d00_data, info) ++ ++static u16 mpq82d00_linear_exp_transfer(u16 word, u16 expect_exponent) ++{ ++ s16 exponent, mantissa, target_exponent; ++ ++ exponent = ((s16)word) >> 11; ++ mantissa = ((s16)((word & 0x7ff) << 5)) >> 5; ++ target_exponent = (s16)((expect_exponent & 0x1f) << 11) >> 11; ++ ++ /* ++ * The MPQ82D00 does not support negtive limit value, if a negtive ++ * limit value is written, the limit value will become to 0. And ++ * the maximum positive limit value is limitted to 0x3FF. ++ */ ++ if (mantissa < 0) { ++ mantissa = 0; ++ } else { ++ if (exponent > target_exponent) { ++ mantissa = (1023 >> (exponent - target_exponent)) >= mantissa ? ++ mantissa << (exponent - target_exponent) : ++ 0x3FF; ++ } else { ++ mantissa = clamp_val(mantissa >> (target_exponent - exponent), ++ 0, 0x3FF); ++ } ++ } ++ ++ return mantissa | ((expect_exponent << 11) & 0xf800); ++} ++ ++static int mpq82d00_read_byte_data(struct i2c_client *client, int page, int reg) ++{ ++ int ret; ++ ++ switch (reg) { ++ case PMBUS_VOUT_MODE: ++ /* ++ * The MPQ82D00 does not follow standard PMBus protocol completely, ++ * and the calculation of vout in this driver is based on direct ++ * format. As a result, the format of vout is enforced to direct. ++ */ ++ ret = PB_VOUT_MODE_DIRECT; ++ break; ++ default: ++ /* ++ * These registers are not explicitly handled by the driver, ++ * as a result, return -ENODATA directly. ++ */ ++ ret = -ENODATA; ++ break; ++ } ++ ++ return ret; ++} ++ ++static int mpq82d00_read_word_data(struct i2c_client *client, int page, ++ int phase, int reg) ++{ ++ const struct pmbus_driver_info *info = pmbus_get_driver_info(client); ++ struct mpq82d00_data *data = to_mpq82d00_data(info); ++ int ret; ++ ++ switch (reg) { ++ case PMBUS_STATUS_WORD: ++ case PMBUS_READ_VIN: ++ case PMBUS_READ_IOUT: ++ case PMBUS_READ_POUT: ++ case PMBUS_READ_PIN: ++ case PMBUS_READ_TEMPERATURE_1: ++ case PMBUS_IOUT_OC_FAULT_LIMIT: ++ case PMBUS_OT_FAULT_LIMIT: ++ /* ++ * These registers are not explicitly handled by the driver, ++ * as a result, return -ENODATA directly. ++ */ ++ ret = -ENODATA; ++ break; ++ case PMBUS_READ_VOUT: ++ ret = pmbus_read_word_data(client, page, phase, reg); ++ if (ret < 0) ++ return ret; ++ ++ ret = DIV_ROUND_CLOSEST((ret & GENMASK(11, 0)) * data->vout_scale, ++ MPQ82D00_VOUT_DIV); ++ break; ++ default: ++ /* ++ * The MPQ82D00 do not support other telemetry and limit ++ * value reading, so, return -EINVAL directly. ++ */ ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++static int mpq82d00_write_word_data(struct i2c_client *client, int page, int reg, ++ u16 word) ++{ ++ int ret; ++ ++ switch (reg) { ++ case PMBUS_OT_FAULT_LIMIT: ++ /* ++ * The PMBUS_OT_FAULT_LIMIT of MPQ82D00 is linear11 format, ++ * and the exponent is a constant value(5'b00000), so the ++ * exponent of word parameter should be converted to 5'b00000. ++ */ ++ ret = pmbus_write_word_data(client, page, reg, ++ mpq82d00_linear_exp_transfer(word, 0x00)); ++ break; ++ case PMBUS_IOUT_OC_FAULT_LIMIT: ++ /* ++ * The PMBUS_IOUT_OC_FAULT_LIMIT of MPQ82D00 is linear11 format, ++ * and the exponent is a constant value(5'b11100), so the ++ * exponent of word parameter should be converted to 5'b11100. ++ */ ++ ret = pmbus_write_word_data(client, page, reg, ++ mpq82d00_linear_exp_transfer(word, 0x1C)); ++ break; ++ default: ++ /* ++ * The MPQ82D00 do not support other limit value configuration, ++ * so, return -EINVAL directly. ++ */ ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++static int mpq82d00_identify(struct i2c_client *client, struct pmbus_driver_info *info) ++{ ++ struct mpq82d00_data *data = to_mpq82d00_data(info); ++ int ret; ++ ++ ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); ++ if (ret < 0) ++ return ret; ++ ++ ret = i2c_smbus_read_byte_data(client, PMBUS_VOUT_MODE); ++ if (ret < 0) ++ return ret; ++ ++ /* ++ * The MPQ82D00 supports three vout mode. If PMBUS_VOUT_MODE ++ * bit5 is 1, the vout scale is 5mv/LSB.If PMBUS_VOUT_MODE bit5 ++ * is 0, it is linear mode, the vout scale is 1.953125mv/LSB. ++ */ ++ if (FIELD_GET(GENMASK(5, 5), ret)) ++ data->vout_scale = 320; ++ else ++ data->vout_scale = 125; ++ ++ return 0; ++} ++ ++static struct pmbus_driver_info mpq82d00_info = { ++ .pages = MPQ82D00_PAGE_NUM, ++ .format[PSC_VOLTAGE_IN] = linear, ++ .format[PSC_CURRENT_OUT] = linear, ++ .format[PSC_TEMPERATURE] = linear, ++ .format[PSC_VOLTAGE_OUT] = direct, ++ .format[PSC_POWER] = linear, ++ ++ .m[PSC_VOLTAGE_OUT] = 1, ++ .R[PSC_VOLTAGE_OUT] = 3, ++ .b[PSC_VOLTAGE_OUT] = 0, ++ ++ .func[0] = MPQ82D00_RAIL1_FUNC, ++ .read_word_data = mpq82d00_read_word_data, ++ .read_byte_data = mpq82d00_read_byte_data, ++ .write_word_data = mpq82d00_write_word_data, ++ .identify = mpq82d00_identify, ++}; ++ ++static int mpq82d00_probe(struct i2c_client *client) ++{ ++ struct mpq82d00_data *data; ++ ++ data = devm_kzalloc(&client->dev, sizeof(struct mpq82d00_data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ memcpy(&data->info, &mpq82d00_info, sizeof(mpq82d00_info)); ++ ++ return pmbus_do_probe(client, &data->info); ++} ++ ++static const struct i2c_device_id mpq82d00_id[] = { ++ {.name = "mpq82d00"}, ++ {} ++}; ++MODULE_DEVICE_TABLE(i2c, mpq82d00_id); ++ ++static const struct of_device_id __maybe_unused mpq82d00_of_match[] = { ++ {.compatible = "mps,mpq82d00"}, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, mpq82d00_of_match); ++ ++static struct i2c_driver mpq82d00_driver = { ++ .driver = { ++ .name = "mpq82d00", ++ .of_match_table = mpq82d00_of_match, ++ }, ++ .probe = mpq82d00_probe, ++ .id_table = mpq82d00_id, ++}; ++ ++module_i2c_driver(mpq82d00_driver); ++ ++MODULE_AUTHOR("Wensheng Wang ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "pmbus.h" ++ ++/* Default cadence for the in-driver alarm-poll fallback (ms) */ ++#define MPQ8646_ALARM_POLL_MS_DEFAULT 1000 ++ ++/* MPS vendor-extended command codes (NOT in PMBus 1.3 Part II) */ ++#define MPS_CLEAR_LAST_FAULT 0x08 ++#define MPS_MFR_CFG_EXT 0xF5 ++#define MPS_MFR_CFG_EXT_CLR_LAST_EN BIT(6) ++#define MPS_PROTECTION_LAST 0xFB ++ ++/* ++ * PMBus 1.3 NVM commit / revert commands. MPS equivalent of ++ * STORE_ALL (15h) and RESTORE_ALL (16h). ++ */ ++#define PMBUS_STORE_USER_ALL 0x15 ++#define PMBUS_RESTORE_USER_ALL 0x16 ++ ++/* PMBus 1.3 timing / UVLO command codes */ ++#define PMBUS_VIN_ON 0x35 ++#define PMBUS_VIN_OFF 0x36 ++#define PMBUS_TON_DELAY 0x60 ++#define PMBUS_TON_RISE 0x61 ++#define PMBUS_TOFF_DELAY 0x64 ++#define PMBUS_TOFF_FALL 0x65 ++ ++/* MPS vendor-extended observability / identity registers */ ++#define MPS_MFR_CONFIG_ID 0xC0 ++#define MPS_MFR_CONFIG_CODE_REV 0xC1 ++#define MPS_MFR_PRODUCT_REV_USER 0xC2 ++#define MPS_MFR_SILICON_REV 0xC3 ++#define MPS_MFR_RETRY_TIMES 0xF4 ++#define MPS_MFR_VBOOT_CFG 0xFC ++ ++/* ++ * MPS_MFR_PMBUS_LOCK (EEh): 16-bit WORD whose low two bits gate ++ * subsequent PMBus writes ++ * bits[1:0] = 00 -- unlocked (POR default) ++ * 01 -- lock all writes EXCEPT VOUT_COMMAND (0x21) ++ * so the operator can still DVFS the rail ++ * 11 -- lock all writes ++ * A negative-going PG edge resets these bits to 00, the lock ++ * is operationally reversible without a full chip POR. ++ */ ++#define MPS_MFR_PMBUS_LOCK 0xEE ++ ++/* ++ * Retry parameters for the MFR_CFG_EXT gate-close write after ++ * CLEAR_LAST_FAULT. Bench-observed NVM-busy NACK window on this ++ * silicon is about 1 ms; the datasheet does not have information. ++ */ ++#define MPQ8646_NVM_RETRY_MAX 5 ++#define MPQ8646_NVM_RETRY_DELAY_US_MIN 2000 ++#define MPQ8646_NVM_RETRY_DELAY_US_MAX 4000 ++ ++#define MPQ8646_DEBUG(client, fmt, ...) \ ++ dev_dbg(&(client)->dev, fmt, ##__VA_ARGS__) ++ ++/* ++ * Maximum legal value for VOUT_SCALE_LOOP (0x29) on the MPQ8646 silicon ++ * The chip uses an 11-bit VOUT feedback-divider scale register. ++ */ ++#define MPQ8646_VOUT_SCALE_LOOP_MAX GENMASK(10, 0) ++ ++/* Forward declaration */ ++struct mpq8646_dbg_reg_ctx; ++ ++/* Per-instance state */ ++struct mpq8646_priv { ++ struct pmbus_driver_info info; /* must be first, container_of target */ ++ struct i2c_client *client; ++ ++ /* Serialises CLEAR_LAST_FAULT sequences and PROTECTION_LAST reads */ ++ struct mutex mps_lock; ++ ++ /* Set alarm_poll_interval_ms = 0 to disable. */ ++ struct delayed_work alarm_poll_work; ++ u32 alarm_poll_interval_ms; ++ ++#ifdef CONFIG_DEBUG_FS ++ /* the only debugfs file that can re-arm the poll worker */ ++ struct dentry *dbg_poll; ++ struct mpq8646_dbg_reg_ctx *dbg_reg_ctx; ++#endif ++}; ++ ++static inline struct mpq8646_priv *mpq8646_priv_from_client(struct i2c_client *client) ++{ ++ const struct pmbus_driver_info *info = pmbus_get_driver_info(client); ++ ++ return container_of(info, struct mpq8646_priv, info); ++} ++ ++/* ++ * VID-mode m/b/R coefficients, same values as the mpq8785 driver for ++ * the MPS VID encoding. Per the PMBus Direct formula used by ++ * pmbus_core, X = (Y * 10^-R - b) / m, so m=64 / b=0 / R=1 yields ++ * 1.5625 mV per LSB. ++ */ ++#define MPQ8646_VID_M 64 ++#define MPQ8646_VID_B 0 ++#define MPQ8646_VID_R 1 ++ ++static int mpq8646_identify(struct i2c_client *client, ++ struct pmbus_driver_info *info) ++{ ++ int vout_mode; ++ ++ vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); ++ if (vout_mode < 0) ++ return vout_mode; ++ ++ switch (vout_mode & PB_VOUT_MODE_MODE_MASK) { ++ case PB_VOUT_MODE_LINEAR: ++ info->format[PSC_VOLTAGE_OUT] = linear; ++ break; ++ case PB_VOUT_MODE_VID: ++ case PB_VOUT_MODE_DIRECT: ++ info->format[PSC_VOLTAGE_OUT] = direct; ++ info->m[PSC_VOLTAGE_OUT] = MPQ8646_VID_M; ++ info->b[PSC_VOLTAGE_OUT] = MPQ8646_VID_B; ++ info->R[PSC_VOLTAGE_OUT] = MPQ8646_VID_R; ++ break; ++ default: ++ return -ENODEV; ++ } ++ ++ return 0; ++}; ++ ++static int mpq8646_read_byte_data(struct i2c_client *client, int page, int reg) ++{ ++ int ret; ++ ++ MPQ8646_DEBUG(client, "read_byte_data page=%d reg=0x%02x\n", page, reg); ++ ++ switch (reg) { ++ case PMBUS_VOUT_MODE: ++ ret = pmbus_read_byte_data(client, page, reg); ++ MPQ8646_DEBUG(client, " VOUT_MODE raw=0x%02x ret=%d\n", ret, ret); ++ if (ret < 0) ++ return ret; ++ ++ if ((ret & PB_VOUT_MODE_MODE_MASK) == PB_VOUT_MODE_VID) ++ return PB_VOUT_MODE_DIRECT; ++ ++ return ret; ++ case PMBUS_STATUS_BYTE: ++ case PMBUS_STATUS_CML: ++ case PMBUS_STATUS_OTHER: ++ case PMBUS_STATUS_MFR_SPECIFIC: ++ case PMBUS_STATUS_FAN_12: ++ case PMBUS_STATUS_FAN_34: ++ return -ENXIO; ++ case PMBUS_MFR_LOCATION: ++ case PMBUS_MFR_DATE: ++ case PMBUS_MFR_SERIAL: ++ case PMBUS_IC_DEVICE_ID: ++ case PMBUS_IC_DEVICE_REV: ++ MPQ8646_DEBUG(client, " unsupported mfr-info reg 0x%02x -> ENXIO\n", reg); ++ return -ENXIO; ++ default: ++ return -ENODATA; ++ } ++} ++ ++/* ++ * Reference: ltc2978.c::ltc2978_write_word_data which uses the ++ * same virtual-register channel for its real chip-side peak reset. ++ */ ++static int mpq8646_write_word_data(struct i2c_client *client, int page, ++ int reg, u16 word) ++{ ++ struct mpq8646_priv *priv = mpq8646_priv_from_client(client); ++ int rc; ++ ++ switch (reg) { ++ case PMBUS_VIRT_RESET_VIN_HISTORY: ++ case PMBUS_VIRT_RESET_VOUT_HISTORY: ++ case PMBUS_VIRT_RESET_IOUT_HISTORY: ++ case PMBUS_VIRT_RESET_TEMP_HISTORY: ++ MPQ8646_DEBUG(client, "reset_history virt reg=0x%04x -> CLEAR_FAULTS\n", ++ reg); ++ rc = i2c_smbus_write_byte(priv->client, PMBUS_CLEAR_FAULTS); ++ if (rc < 0) ++ MPQ8646_DEBUG(client, " CLEAR_FAULTS rc=%d\n", rc); ++ return rc < 0 ? rc : 0; ++ default: ++ return -ENODATA; /* let pmbus_core do the direct write */ ++ } ++} ++ ++static int mpq8646_read_word_data(struct i2c_client *client, int page, ++ int phase, int reg) ++{ ++ int rc; ++ ++ MPQ8646_DEBUG(client, "read_word_data page=%d phase=%d reg=0x%02x\n", ++ page, phase, reg); ++ ++ switch (reg) { ++ case PMBUS_READ_VIN: ++ case PMBUS_READ_VOUT: ++ case PMBUS_READ_IOUT: ++ case PMBUS_READ_TEMPERATURE_1: ++ case PMBUS_STATUS_WORD: ++ case PMBUS_VOUT_OV_FAULT_LIMIT: ++ case PMBUS_VOUT_OV_WARN_LIMIT: ++ case PMBUS_VOUT_UV_WARN_LIMIT: ++ case PMBUS_VOUT_UV_FAULT_LIMIT: ++ case PMBUS_IOUT_OC_FAULT_LIMIT: ++ case PMBUS_IOUT_OC_WARN_LIMIT: ++ case PMBUS_OT_FAULT_LIMIT: ++ case PMBUS_OT_WARN_LIMIT: ++ case PMBUS_VIN_OV_FAULT_LIMIT: ++ case PMBUS_VIN_OV_WARN_LIMIT: ++ case PMBUS_VIN_UV_WARN_LIMIT: ++ case PMBUS_VIN_UV_FAULT_LIMIT: ++ case PMBUS_MFR_VIN_MAX: ++ case PMBUS_MFR_VOUT_MAX: ++ case PMBUS_MFR_IOUT_MAX: ++ case PMBUS_MFR_MAX_TEMP_1: ++ break; ++ case PMBUS_VIRT_RESET_VIN_HISTORY: ++ case PMBUS_VIRT_RESET_VOUT_HISTORY: ++ case PMBUS_VIRT_RESET_IOUT_HISTORY: ++ case PMBUS_VIRT_RESET_TEMP_HISTORY: ++ return 0; ++ default: ++ return -ENODATA; ++ } ++ ++ rc = i2c_smbus_read_word_data(client, reg); ++ ++ MPQ8646_DEBUG(client, " reg=0x%02x rc=%d\n", reg, rc); ++ return rc; ++} ++ ++static struct pmbus_driver_info mpq8646_info = { ++ .pages = 1, ++ .format[PSC_VOLTAGE_IN] = direct, ++ .format[PSC_CURRENT_OUT] = direct, ++ .format[PSC_TEMPERATURE] = direct, ++ .m[PSC_VOLTAGE_IN] = 4, ++ .b[PSC_VOLTAGE_IN] = 0, ++ .R[PSC_VOLTAGE_IN] = 1, ++ .m[PSC_CURRENT_OUT] = 16, ++ .b[PSC_CURRENT_OUT] = 0, ++ .R[PSC_CURRENT_OUT] = 0, ++ .m[PSC_TEMPERATURE] = 1, ++ .b[PSC_TEMPERATURE] = 0, ++ .R[PSC_TEMPERATURE] = 0, ++ .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | ++ PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP | ++ PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_VOUT | ++ PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_TEMP, ++}; ++ ++#if IS_ENABLED(CONFIG_REGULATOR) ++static const struct regulator_desc mpq8646_reg_desc[] = { ++ PMBUS_REGULATOR("vout", 0), ++}; ++#endif /* CONFIG_REGULATOR */ ++ ++#if IS_ENABLED(CONFIG_NVMEM) ++/* ++ * Expose using ++ * /sys/bus/nvmem/devices//nvmem ++ * ++ * Layout (16 bytes): ++ * offset 0..1 PROTECTION_LAST (0xFB) word, LE -- NVM ++ * offset 2..3 MFR_RETRY_TIMES (0xF4) word, LE -- NVM ++ * offset 4..5 MFR_CONFIG_ID (0xC0) word, LE -- NVM ++ * offset 6..7 MFR_VBOOT_CFG (0xFC) word, LE -- NVM ++ * offset 8 MFR_SILICON_REV (0xC3) byte -- NVM ++ * offset 9..15 reserved (zero-fill, leaves room for additions) ++ */ ++#define MPQ8646_NVMEM_SIZE 16 ++ ++struct mpq8646_nvmem_entry { ++ unsigned int off; ++ u8 reg; ++ bool is_word; ++}; ++ ++static const struct mpq8646_nvmem_entry mpq8646_nvmem_map[] = { ++ { 0, MPS_PROTECTION_LAST, true }, ++ { 2, MPS_MFR_RETRY_TIMES, true }, ++ { 4, MPS_MFR_CONFIG_ID, true }, ++ { 6, MPS_MFR_VBOOT_CFG, true }, ++ { 8, MPS_MFR_SILICON_REV, false }, ++}; ++ ++static int mpq8646_nvmem_read(void *data, unsigned int offset, void *val, ++ size_t bytes) ++{ ++ struct mpq8646_priv *priv = data; ++ u8 *out = val; ++ size_t i; ++ ++ if (offset >= MPQ8646_NVMEM_SIZE) ++ return -EINVAL; ++ if (offset + bytes > MPQ8646_NVMEM_SIZE) ++ bytes = MPQ8646_NVMEM_SIZE - offset; ++ ++ memset(out, 0, bytes); ++ ++ guard(mutex)(&priv->mps_lock); ++ for (i = 0; i < ARRAY_SIZE(mpq8646_nvmem_map); i++) { ++ const struct mpq8646_nvmem_entry *e = &mpq8646_nvmem_map[i]; ++ unsigned int e_start = e->off; ++ unsigned int e_end = e_start + (e->is_word ? 2 : 1); ++ u8 raw[2]; ++ int rc; ++ unsigned int j; ++ ++ if (e_end <= offset || e_start >= offset + bytes) ++ continue; /* outside requested slice */ ++ ++ if (e->is_word) ++ rc = i2c_smbus_read_word_data(priv->client, e->reg); ++ else ++ rc = i2c_smbus_read_byte_data(priv->client, e->reg); ++ if (rc < 0) ++ continue; /* leave the zero-fill in place */ ++ ++ raw[0] = rc & 0xff; ++ raw[1] = (rc >> 8) & 0xff; ++ ++ for (j = 0; j < e_end - e_start; j++) { ++ unsigned int abs = e_start + j; ++ ++ if (abs >= offset && abs < offset + bytes) ++ out[abs - offset] = raw[j]; ++ } ++ } ++ return 0; ++} ++#endif /* CONFIG_NVMEM */ ++ ++static const struct i2c_device_id mpq8646_id[] = { ++ { .name = "mpq8646" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(i2c, mpq8646_id); ++ ++static const struct of_device_id __maybe_unused mpq8646_of_match[] = { ++ { .compatible = "mps,mpq8646" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, mpq8646_of_match); ++ ++static struct pmbus_platform_data mpq8646_no_pec_pdata = { ++ .flags = PMBUS_NO_CAPABILITY, ++}; ++ ++#ifdef CONFIG_DEBUG_FS ++/* ++ * Read-only decode cases in the client's pmbus debugfs directory: ++ * the MPS-specific STATUS_WORD and PROTECTION_LAST bit decode plus the ++ * identity/timing registers. ++ */ ++ ++struct mpq_status_bit { ++ u16 mask; ++ const char *name; ++}; ++ ++static const struct mpq_status_bit mpq8646_status_word_bits[] = { ++ { PB_STATUS_VOUT, "VOUT" }, ++ { PB_STATUS_IOUT_POUT, "IOUT_POUT" }, ++ { PB_STATUS_INPUT, "INPUT" }, ++ { PB_STATUS_WORD_MFR, "NVM_SUMMARY" }, ++ { PB_STATUS_POWER_GOOD_N, "POWER_GOOD#" }, ++ { PB_STATUS_FANS, "FANS" }, ++ { PB_STATUS_OTHER, "OTHER" }, ++ { PB_STATUS_UNKNOWN, "WATCH_DOG" }, ++ { PB_STATUS_BUSY, "BUSY" }, ++ { PB_STATUS_OFF, "OFF" }, ++ { PB_STATUS_VOUT_OV, "VOUT_OV_FAULT" }, ++ { PB_STATUS_IOUT_OC, "IOUT_OC_FAULT" }, ++ { PB_STATUS_VIN_UV, "VIN_UV_FAULT" }, ++ { PB_STATUS_TEMPERATURE, "TEMP" }, ++ { PB_STATUS_CML, "CML" }, ++ { PB_STATUS_NONE_ABOVE, "DRMOS_FAULT" }, ++ { /* sentinel */ } ++}; ++ ++/* PROTECTION_LAST (0xFB) bit names, it survives chip POR */ ++static const struct mpq_status_bit mpq8646_protection_last_bits[] = { ++ { BIT(15), "INIT_FAULT" }, ++ { BIT(14), "NVM_CRC_ERROR" }, ++ { BIT(13), "NVM_FAULT" }, ++ { BIT(12), "OC_PHASE_FAULT" }, ++ { BIT(11), "OTP_SELF_FAULT" }, ++ { BIT(9), "SWITCH_PRD_FAULT" }, ++ { BIT(8), "VIN_OV_FAULT" }, ++ { BIT(7), "VOUT_OV_FAULT" }, ++ { BIT(6), "VOUT_UV_FAULT" }, ++ { BIT(5), "OC_TOT_FAULT" }, ++ { BIT(4), "VIN_UVLO_FAULT" }, ++ { BIT(3), "DRMOS_OTP" }, ++ { /* sentinel */ } ++}; ++ ++static void mpq8646_print_bits(struct seq_file *s, u16 v, ++ const struct mpq_status_bit *tab) ++{ ++ const struct mpq_status_bit *t; ++ bool first = true; ++ ++ seq_printf(s, "0x%04x", v); ++ if (!v) { ++ seq_puts(s, " [clean]\n"); ++ return; ++ } ++ seq_puts(s, " ["); ++ for (t = tab; t->mask; t++) { ++ if (v & t->mask) { ++ if (!first) ++ seq_putc(s, ' '); ++ seq_puts(s, t->name); ++ first = false; ++ } ++ } ++ seq_puts(s, "]\n"); ++} ++ ++static int mpq8646_dbg_status_decoded_show(struct seq_file *s, void *unused) ++{ ++ struct mpq8646_priv *priv = s->private; ++ int rc; ++ ++ scoped_guard(pmbus_lock, priv->client) ++ rc = i2c_smbus_read_word_data(priv->client, PMBUS_STATUS_WORD); ++ if (rc < 0) { ++ seq_printf(s, "ERROR: STATUS_WORD read failed (%d)\n", rc); ++ return 0; ++ } ++ seq_puts(s, "STATUS_WORD: "); ++ mpq8646_print_bits(s, (u16)rc, mpq8646_status_word_bits); ++ seq_puts(s, "(MPS extensions: bit12=NVM_SUMMARY, bit8=WATCH_DOG, bit0=DRMOS_FAULT)\n"); ++ return 0; ++} ++DEFINE_SHOW_ATTRIBUTE(mpq8646_dbg_status_decoded); ++ ++static int mpq8646_dbg_protection_last_show(struct seq_file *s, void *unused) ++{ ++ struct mpq8646_priv *priv = s->private; ++ int rc; ++ ++ guard(pmbus_lock)(priv->client); ++ scoped_guard(mutex, &priv->mps_lock) ++ rc = i2c_smbus_read_word_data(priv->client, MPS_PROTECTION_LAST); ++ ++ if (rc < 0) { ++ seq_printf(s, "ERROR: PROTECTION_LAST read failed (%d)\n", rc); ++ return 0; ++ } ++ seq_puts(s, "PROTECTION_LAST: "); ++ mpq8646_print_bits(s, (u16)rc, mpq8646_protection_last_bits); ++ seq_puts(s, "(NVM-backed, survives chip POR)\n"); ++ return 0; ++} ++DEFINE_SHOW_ATTRIBUTE(mpq8646_dbg_protection_last); ++ ++struct mpq8646_dbg_reg { ++ u8 reg; ++ bool is_word; ++ const char *name; ++}; ++ ++static const struct mpq8646_dbg_reg mpq8646_dbg_regs[] = { ++ /* MPS vendor extensions (read-only identity / observability) */ ++ { MPS_MFR_CONFIG_ID, true, "mfr_config_id" }, ++ { MPS_MFR_CONFIG_CODE_REV, true, "mfr_config_code_rev" }, ++ { MPS_MFR_SILICON_REV, false, "mfr_silicon_rev" }, ++ { MPS_MFR_RETRY_TIMES, true, "mfr_retry_times" }, ++ { MPS_MFR_VBOOT_CFG, true, "mfr_vboot_cfg" }, ++ /* PMBus 1.3 standard timing / UVLO (read-only introspection) */ ++ { PMBUS_VIN_ON, true, "vin_on" }, ++ { PMBUS_VIN_OFF, true, "vin_off" }, ++ { PMBUS_TON_DELAY, true, "ton_delay" }, ++ { PMBUS_TON_RISE, true, "ton_rise" }, ++ { PMBUS_TOFF_DELAY, true, "toff_delay" }, ++ { PMBUS_TOFF_FALL, true, "toff_fall" }, ++}; ++ ++struct mpq8646_dbg_reg_ctx { ++ struct mpq8646_priv *priv; ++ const struct mpq8646_dbg_reg *desc; ++}; ++ ++static int mpq8646_dbg_reg_show(struct seq_file *s, void *unused) ++{ ++ struct mpq8646_dbg_reg_ctx *ctx = s->private; ++ int rc; ++ ++ guard(pmbus_lock)(ctx->priv->client); ++ if (ctx->desc->is_word) ++ rc = i2c_smbus_read_word_data(ctx->priv->client, ++ ctx->desc->reg); ++ else ++ rc = i2c_smbus_read_byte_data(ctx->priv->client, ++ ctx->desc->reg); ++ ++ if (rc < 0) { ++ seq_printf(s, "ERROR: reg 0x%02x (%s) read failed (%d)\n", ++ ctx->desc->reg, ctx->desc->name, rc); ++ return 0; ++ } ++ seq_printf(s, "0x%0*x\n", ctx->desc->is_word ? 4 : 2, rc); ++ return 0; ++} ++DEFINE_SHOW_ATTRIBUTE(mpq8646_dbg_reg); ++ ++#ifdef CONFIG_SENSORS_MPQ8646_DEBUG_UNSAFE ++/* ++ * Write/provisioning data, disabled by default: NVM commit and ++ * revert, the CLEAR_LAST_FAULT sequences and a small set of named ++ * writable registers. ++ */ ++ ++static void mpq8646_unsafe_banner(struct mpq8646_priv *priv) ++{ ++ dev_warn(&priv->client->dev, ++ "**********************************************************\n" ++ "** WARNING WARNING WARNING WARNING WARNING WARNING **\n" ++ "** **\n" ++ "** The MPQ8646 provisioning debugfs writes are enabled. **\n" ++ "** Wrong register writes can and likely will physically **\n" ++ "** damage or destroy the chip and/or the board. **\n" ++ "** **\n" ++ "** If you see this message and you are not debugging **\n" ++ "** the kernel, report this immediately to your system **\n" ++ "** administrator! **\n" ++ "** **\n" ++ "** WARNING WARNING WARNING WARNING WARNING WARNING **\n" ++ "**********************************************************\n"); ++} ++ ++static int mpq8646_dbg_clear_protection_last(void *data, u64 val) ++{ ++ struct mpq8646_priv *priv = data; ++ int rc; ++ ++ if (!val) ++ return 0; ++ ++ guard(pmbus_lock)(priv->client); ++ scoped_guard(mutex, &priv->mps_lock) ++ rc = i2c_smbus_write_byte(priv->client, MPS_CLEAR_LAST_FAULT); ++ if (rc < 0) ++ dev_warn(&priv->client->dev, ++ "clear_protection_last: CLEAR_LAST_FAULT write failed (%d)\n", ++ rc); ++ return rc; ++} ++DEFINE_DEBUGFS_ATTRIBUTE(mpq8646_dbg_clear_protection_last_fops, ++ NULL, mpq8646_dbg_clear_protection_last, "%llu\n"); ++ ++static int mpq8646_dbg_clear_protection_last_force(void *data, u64 val) ++{ ++ struct mpq8646_priv *priv = data; ++ int rc, ret; ++ int wp_orig, cfg_orig; ++ ++ if (!val) ++ return 0; ++ ++ guard(pmbus_lock)(priv->client); ++ guard(mutex)(&priv->mps_lock); ++ ++ wp_orig = i2c_smbus_read_byte_data(priv->client, PMBUS_WRITE_PROTECT); ++ if (wp_orig < 0) { ++ dev_warn(&priv->client->dev, ++ "clear_protection_last_force: WRITE_PROTECT read failed (%d), aborting\n", ++ wp_orig); ++ return wp_orig; ++ } ++ cfg_orig = i2c_smbus_read_word_data(priv->client, MPS_MFR_CFG_EXT); ++ if (cfg_orig < 0) { ++ dev_warn(&priv->client->dev, ++ "clear_protection_last_force: MFR_CFG_EXT read failed (%d), aborting\n", ++ cfg_orig); ++ return cfg_orig; ++ } ++ ++ if (wp_orig != 0) { ++ rc = i2c_smbus_write_byte_data(priv->client, ++ PMBUS_WRITE_PROTECT, 0); ++ if (rc < 0) { ++ dev_warn(&priv->client->dev, ++ "clear_protection_last_force: WP clear failed (%d), aborting\n", ++ rc); ++ return rc; ++ } ++ } ++ ++ ret = i2c_smbus_write_word_data(priv->client, MPS_MFR_CFG_EXT, ++ (u16)cfg_orig | MPS_MFR_CFG_EXT_CLR_LAST_EN); ++ if (ret < 0) { ++ dev_warn(&priv->client->dev, ++ "clear_protection_last_force: gate open failed (%d)\n", ++ ret); ++ } else { ++ ret = i2c_smbus_write_byte(priv->client, MPS_CLEAR_LAST_FAULT); ++ if (ret < 0) ++ dev_warn(&priv->client->dev, ++ "clear_protection_last_force: CLEAR_LAST_FAULT failed (%d) even with gate open\n", ++ ret); ++ ++ for (int attempt = 0; attempt < MPQ8646_NVM_RETRY_MAX; attempt++) { ++ rc = i2c_smbus_write_word_data(priv->client, ++ MPS_MFR_CFG_EXT, ++ (u16)cfg_orig); ++ if (rc >= 0) ++ break; ++ usleep_range(MPQ8646_NVM_RETRY_DELAY_US_MIN, ++ MPQ8646_NVM_RETRY_DELAY_US_MAX); ++ } ++ if (rc < 0) { ++ dev_warn(&priv->client->dev, ++ "clear_protection_last_force: MFR_CFG_EXT restore failed after retries (%d): gate may stay open until POR\n", ++ rc); ++ if (ret == 0) ++ ret = rc; ++ } ++ } ++ ++ if (wp_orig != 0) { ++ rc = i2c_smbus_write_byte_data(priv->client, ++ PMBUS_WRITE_PROTECT, ++ (u8)wp_orig); ++ if (rc < 0) { ++ dev_warn(&priv->client->dev, ++ "clear_protection_last_force: WP restore failed (%d)\n", ++ rc); ++ if (ret == 0) ++ ret = rc; ++ } ++ } ++ return ret; ++} ++DEFINE_DEBUGFS_ATTRIBUTE(mpq8646_dbg_clear_protection_last_force_fops, ++ NULL, mpq8646_dbg_clear_protection_last_force, "%llu\n"); ++ ++static int mpq8646_dbg_store_all(void *data, u64 val) ++{ ++ struct mpq8646_priv *priv = data; ++ int rc; ++ ++ if (!val) ++ return 0; ++ ++ guard(pmbus_lock)(priv->client); ++ scoped_guard(mutex, &priv->mps_lock) ++ rc = i2c_smbus_write_byte(priv->client, PMBUS_STORE_USER_ALL); ++ if (rc < 0) ++ dev_warn(&priv->client->dev, ++ "store_all: STORE_USER_ALL (0x15) write failed (%d)\n", ++ rc); ++ return rc; ++} ++DEFINE_DEBUGFS_ATTRIBUTE(mpq8646_dbg_store_all_fops, ++ NULL, mpq8646_dbg_store_all, "%llu\n"); ++ ++static int mpq8646_dbg_restore_all(void *data, u64 val) ++{ ++ struct mpq8646_priv *priv = data; ++ int rc; ++ ++ if (!val) ++ return 0; ++ ++ guard(pmbus_lock)(priv->client); ++ scoped_guard(mutex, &priv->mps_lock) ++ rc = i2c_smbus_write_byte(priv->client, PMBUS_RESTORE_USER_ALL); ++ if (rc < 0) ++ dev_warn(&priv->client->dev, ++ "restore_all: RESTORE_USER_ALL (0x16) write failed (%d)\n", ++ rc); ++ return rc; ++} ++DEFINE_DEBUGFS_ATTRIBUTE(mpq8646_dbg_restore_all_fops, ++ NULL, mpq8646_dbg_restore_all, "%llu\n"); ++ ++static const struct mpq8646_dbg_reg mpq8646_dbg_regs_unsafe[] = { ++ /* PMBus 1.3 control / margin */ ++ { PMBUS_ON_OFF_CONFIG, false, "on_off_config" }, ++ { PMBUS_VOUT_MARGIN_HIGH, true, "vout_margin_high" }, ++ { PMBUS_VOUT_MARGIN_LOW, true, "vout_margin_low" }, ++ /* MPS PMBus-level write-protect */ ++ { MPS_MFR_PMBUS_LOCK, true, "mfr_pmbus_lock" }, ++ /* MPS user-writable product revision */ ++ { MPS_MFR_PRODUCT_REV_USER, true, "mfr_product_rev_user" }, ++}; ++ ++static int mpq8646_dbg_reg_get(void *data, u64 *val) ++{ ++ struct mpq8646_dbg_reg_ctx *ctx = data; ++ int rc; ++ ++ guard(pmbus_lock)(ctx->priv->client); ++ if (ctx->desc->is_word) ++ rc = i2c_smbus_read_word_data(ctx->priv->client, ++ ctx->desc->reg); ++ else ++ rc = i2c_smbus_read_byte_data(ctx->priv->client, ++ ctx->desc->reg); ++ if (rc < 0) ++ return rc; ++ *val = rc; ++ return 0; ++} ++ ++static int mpq8646_dbg_reg_set(void *data, u64 val) ++{ ++ struct mpq8646_dbg_reg_ctx *ctx = data; ++ int rc; ++ ++ guard(pmbus_lock)(ctx->priv->client); ++ if (ctx->desc->is_word) ++ rc = i2c_smbus_write_word_data(ctx->priv->client, ++ ctx->desc->reg, (u16)val); ++ else ++ rc = i2c_smbus_write_byte_data(ctx->priv->client, ++ ctx->desc->reg, (u8)val); ++ return rc < 0 ? rc : 0; ++} ++DEFINE_DEBUGFS_ATTRIBUTE(mpq8646_dbg_reg_rw_fops, ++ mpq8646_dbg_reg_get, mpq8646_dbg_reg_set, "0x%llx\n"); ++ ++static void mpq8646_debugfs_register_unsafe(struct mpq8646_priv *priv, ++ struct dentry *root) ++{ ++ struct mpq8646_dbg_reg_ctx *ctx; ++ size_t i; ++ ++ mpq8646_unsafe_banner(priv); ++ ++ debugfs_create_file_unsafe("clear_protection_last", 0200, root, priv, ++ &mpq8646_dbg_clear_protection_last_fops); ++ debugfs_create_file_unsafe("clear_protection_last_force", 0200, root, ++ priv, ++ &mpq8646_dbg_clear_protection_last_force_fops); ++ debugfs_create_file_unsafe("store_all", 0200, root, priv, ++ &mpq8646_dbg_store_all_fops); ++ debugfs_create_file_unsafe("restore_all", 0200, root, priv, ++ &mpq8646_dbg_restore_all_fops); ++ ++ ctx = devm_kcalloc(&priv->client->dev, ++ ARRAY_SIZE(mpq8646_dbg_regs_unsafe), ++ sizeof(*ctx), GFP_KERNEL); ++ if (!ctx) ++ return; ++ for (i = 0; i < ARRAY_SIZE(mpq8646_dbg_regs_unsafe); i++) { ++ ctx[i].priv = priv; ++ ctx[i].desc = &mpq8646_dbg_regs_unsafe[i]; ++ debugfs_create_file_unsafe(mpq8646_dbg_regs_unsafe[i].name, ++ 0600, root, &ctx[i], ++ &mpq8646_dbg_reg_rw_fops); ++ } ++} ++#else ++static inline void mpq8646_debugfs_register_unsafe(struct mpq8646_priv *priv, ++ struct dentry *root) {} ++#endif /* CONFIG_SENSORS_MPQ8646_DEBUG_UNSAFE */ ++ ++static int mpq8646_dbg_poll_interval_get(void *data, u64 *val) ++{ ++ struct mpq8646_priv *priv = data; ++ ++ *val = priv->alarm_poll_interval_ms; ++ return 0; ++} ++ ++static int mpq8646_dbg_poll_interval_set(void *data, u64 val) ++{ ++ struct mpq8646_priv *priv = data; ++ bool was_off = !priv->alarm_poll_interval_ms; ++ ++ priv->alarm_poll_interval_ms = (u32)val; ++ ++ if (val && was_off && !priv->client->irq) ++ schedule_delayed_work(&priv->alarm_poll_work, ++ msecs_to_jiffies((u32)val)); ++ return 0; ++} ++DEFINE_DEBUGFS_ATTRIBUTE(mpq8646_dbg_poll_interval_fops, ++ mpq8646_dbg_poll_interval_get, ++ mpq8646_dbg_poll_interval_set, "%llu\n"); ++ ++static void mpq8646_debugfs_register(struct mpq8646_priv *priv) ++{ ++ struct dentry *root; ++ size_t i; ++ ++ root = pmbus_get_debugfs_dir(priv->client); ++ if (!root) ++ return; ++ ++ /* MPS extensions: status decode + NVM-backed PROTECTION_LAST */ ++ debugfs_create_file("status_decoded", 0400, root, priv, ++ &mpq8646_dbg_status_decoded_fops); ++ debugfs_create_file("protection_last", 0400, root, priv, ++ &mpq8646_dbg_protection_last_fops); ++ priv->dbg_poll = ++ debugfs_create_file_unsafe("alarm_poll_interval_ms", 0600, ++ root, priv, ++ &mpq8646_dbg_poll_interval_fops); ++ ++ priv->dbg_reg_ctx = devm_kcalloc(&priv->client->dev, ++ ARRAY_SIZE(mpq8646_dbg_regs), ++ sizeof(*priv->dbg_reg_ctx), ++ GFP_KERNEL); ++ if (!priv->dbg_reg_ctx) ++ return; ++ for (i = 0; i < ARRAY_SIZE(mpq8646_dbg_regs); i++) { ++ priv->dbg_reg_ctx[i].priv = priv; ++ priv->dbg_reg_ctx[i].desc = &mpq8646_dbg_regs[i]; ++ debugfs_create_file(mpq8646_dbg_regs[i].name, 0400, ++ root, &priv->dbg_reg_ctx[i], ++ &mpq8646_dbg_reg_fops); ++ } ++ ++ mpq8646_debugfs_register_unsafe(priv, root); ++} ++ ++static void mpq8646_debugfs_unregister(struct mpq8646_priv *priv) ++{ ++ debugfs_remove(priv->dbg_poll); ++} ++#else ++static inline void mpq8646_debugfs_register(struct mpq8646_priv *priv) {} ++static inline void mpq8646_debugfs_unregister(struct mpq8646_priv *priv) {} ++#endif /* CONFIG_DEBUG_FS */ ++ ++static void mpq8646_alarm_poll_work(struct work_struct *work) ++{ ++ struct mpq8646_priv *priv = container_of(to_delayed_work(work), ++ struct mpq8646_priv, ++ alarm_poll_work); ++ ++ if (priv->client->irq) ++ return; /* SMBALERT# wired; polling not needed */ ++ ++ if (!priv->alarm_poll_interval_ms) ++ return; /* polling disabled; don't re-arm */ ++ ++ pmbus_check_and_notify_faults(priv->client); ++ ++ schedule_delayed_work(&priv->alarm_poll_work, ++ msecs_to_jiffies(priv->alarm_poll_interval_ms)); ++} ++ ++static int mpq8646_probe(struct i2c_client *client) ++{ ++ struct device *dev = &client->dev; ++ struct pmbus_driver_info *info; ++ struct mpq8646_priv *priv; ++ u32 voltage_scale; ++ int ret; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ priv->client = client; ++ mutex_init(&priv->mps_lock); ++ memcpy(&priv->info, &mpq8646_info, sizeof(priv->info)); ++ info = &priv->info; ++ ++ info->identify = mpq8646_identify; ++ info->read_byte_data = mpq8646_read_byte_data; ++ info->read_word_data = mpq8646_read_word_data; ++ info->write_word_data = mpq8646_write_word_data; ++ dev->platform_data = &mpq8646_no_pec_pdata; ++ ++#if IS_ENABLED(CONFIG_REGULATOR) ++ info->reg_desc = mpq8646_reg_desc; ++ info->num_regulators = ARRAY_SIZE(mpq8646_reg_desc); ++#endif ++ ++ INIT_DELAYED_WORK(&priv->alarm_poll_work, mpq8646_alarm_poll_work); ++ priv->alarm_poll_interval_ms = MPQ8646_ALARM_POLL_MS_DEFAULT; ++ ++ if (!device_property_read_u32(dev, "mps,vout-fb-divider-ratio-permille", ++ &voltage_scale)) { ++ if (voltage_scale > MPQ8646_VOUT_SCALE_LOOP_MAX) ++ return -EINVAL; ++ ++ ret = i2c_smbus_write_word_data(client, PMBUS_VOUT_SCALE_LOOP, ++ voltage_scale); ++ if (ret) ++ return ret; ++ } ++ ++ ret = pmbus_do_probe(client, info); ++ if (ret) ++ return ret; ++ ++ mpq8646_debugfs_register(priv); ++ ++ if (!client->irq) ++ schedule_delayed_work(&priv->alarm_poll_work, ++ msecs_to_jiffies(priv->alarm_poll_interval_ms)); ++ ++#if IS_ENABLED(CONFIG_NVMEM) ++ { ++ struct nvmem_config cfg = { ++ .dev = &client->dev, ++ .name = dev_name(&client->dev), ++ .owner = THIS_MODULE, ++ .read_only = true, ++ .root_only = true, ++ .word_size = 1, ++ .stride = 1, ++ .size = MPQ8646_NVMEM_SIZE, ++ .reg_read = mpq8646_nvmem_read, ++ .priv = priv, ++ }; ++ struct nvmem_device *nv = devm_nvmem_register(&client->dev, &cfg); ++ ++ if (IS_ERR(nv)) ++ dev_warn(&client->dev, ++ "nvmem snapshot register failed (%pe)\n", ++ nv); ++ } ++#endif ++ return 0; ++}; ++ ++static void mpq8646_remove(struct i2c_client *client) ++{ ++ struct mpq8646_priv *priv = mpq8646_priv_from_client(client); ++ ++ mpq8646_debugfs_unregister(priv); ++ cancel_delayed_work_sync(&priv->alarm_poll_work); ++} ++ ++static struct i2c_driver mpq8646_driver = { ++ .driver = { ++ .name = "mpq8646", ++ .of_match_table = of_match_ptr(mpq8646_of_match), ++ }, ++ .probe = mpq8646_probe, ++ .remove = mpq8646_remove, ++ .id_table = mpq8646_id, ++}; ++ ++module_i2c_driver(mpq8646_driver); ++ ++MODULE_AUTHOR("Vincent Jardin "); ++MODULE_DESCRIPTION("PMBus driver for MPS MPQ8646 (extended observability)"); ++MODULE_LICENSE("GPL"); ++MODULE_IMPORT_NS("PMBUS"); +diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h +--- a/drivers/hwmon/pmbus/pmbus.h ++++ b/drivers/hwmon/pmbus/pmbus.h +@@ -421,10 +421,12 @@ enum pmbus_data_format { linear = 0, ieee754, direct, vid }; + enum vrm_version { vr11 = 0, vr12, vr13, imvp9, amd625mv, nvidia195mv }; + + /* PMBus revision identifiers */ +-#define PMBUS_REV_10 0x00 /* PMBus revision 1.0 */ +-#define PMBUS_REV_11 0x11 /* PMBus revision 1.1 */ +-#define PMBUS_REV_12 0x22 /* PMBus revision 1.2 */ +-#define PMBUS_REV_13 0x33 /* PMBus revision 1.3 */ ++#define PMBUS_REV_10 0x00 /* PMBus revision 1.0 */ ++#define PMBUS_REV_11 0x11 /* PMBus revision 1.1 */ ++#define PMBUS_REV_12 0x22 /* PMBus revision 1.2 */ ++#define PMBUS_REV_13 0x33 /* PMBus revision 1.3 */ ++#define PMBUS_REV_131 0x44 /* PMBus revision 1.3.1 */ ++#define PMBUS_REV_14 0x55 /* PMBus revision 1.4 */ + + /* Operation type flags for pmbus_update_ts */ + #define PMBUS_OP_WRITE BIT(0) +@@ -489,6 +491,16 @@ struct pmbus_driver_info { + int access_delay; /* in microseconds */ + int write_delay; /* in microseconds */ + int page_change_delay; /* in microseconds */ ++ ++ /* ++ * Some chips do not support the PMBUS_REVISION command. ++ * Drivers for such chips can report the supported PMBus revision here. ++ * ++ * Drivers must set have_pmbus_revision to true and provide the ++ * supported PMBus version in pmbus_revision. ++ */ ++ bool have_pmbus_revision; /* true if pmbus_revision is valid */ ++ u8 pmbus_revision; /* PMBus revision */ + }; + + /* Regulator ops */ +@@ -550,6 +562,7 @@ void pmbus_set_update(struct i2c_client *client, u8 reg, bool update); + void pmbus_wait(struct i2c_client *client); + void pmbus_update_ts(struct i2c_client *client, int op); + int pmbus_set_page(struct i2c_client *client, int page, int phase); ++int pmbus_read_smbus_i2c_block_data(struct i2c_client *client, u8 reg, char *data_buf); + int pmbus_read_word_data(struct i2c_client *client, int page, int phase, + u8 reg); + int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg, +@@ -561,6 +574,7 @@ int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, + int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, + u8 mask, u8 value); + void pmbus_clear_faults(struct i2c_client *client); ++void pmbus_check_and_notify_faults(struct i2c_client *client); + bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg); + bool pmbus_check_word_register(struct i2c_client *client, int page, int reg); + int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info); +diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c +--- a/drivers/hwmon/pmbus/pmbus_core.c ++++ b/drivers/hwmon/pmbus/pmbus_core.c +@@ -96,7 +96,8 @@ struct pmbus_data { + + u32 flags; /* from platform data */ + +- u8 revision; /* The PMBus revision the device is compliant with */ ++ bool have_pmbus_revision; ++ u8 revision; /* The PMBus revision the device is compliant with */ + + int exponent[PMBUS_PAGES]; + /* linear mode: exponent for output voltages */ +@@ -181,7 +182,12 @@ EXPORT_SYMBOL_NS_GPL(pmbus_set_update, "PMBUS"); + void pmbus_wait(struct i2c_client *client) + { + struct pmbus_data *data = i2c_get_clientdata(client); +- s64 delay = ktime_us_delta(data->next_access_backoff, ktime_get()); ++ s64 delay; ++ ++ if (!data) ++ return; ++ ++ delay = ktime_us_delta(data->next_access_backoff, ktime_get()); + + if (delay > 0) + fsleep(delay); +@@ -192,8 +198,14 @@ EXPORT_SYMBOL_NS_GPL(pmbus_wait, "PMBUS"); + void pmbus_update_ts(struct i2c_client *client, int op) + { + struct pmbus_data *data = i2c_get_clientdata(client); +- const struct pmbus_driver_info *info = data->info; +- int delay = info->access_delay; ++ const struct pmbus_driver_info *info; ++ int delay; ++ ++ if (!data) ++ return; ++ ++ info = data->info; ++ delay = info->access_delay; + + if (op & PMBUS_OP_WRITE) + delay = max(delay, info->write_delay); +@@ -517,6 +529,81 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, + } + EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, "PMBUS"); + ++/** ++ * pmbus_read_smbus_i2c_block_data() - Read SMBus/I2C block data ++ * @client: Handle to slave device ++ * @reg: Byte interpreted by slave ++ * @data_buf: Byte array into which data will be read ++ * Return: Negative errno or number of bytes read ++ * ++ * PMBus internal function to read a SMBus block from a PMBus chip. ++ * ++ * PMBus chips report various properties using SMBus block read operations. ++ * However, not all I2C controllers support this operation. ++ * ++ * Execute SMBus block read if supported. If not supported, but SMBus I2C block ++ * read is supported, use it instead. Note that at most 31 data bytes can be ++ * read from the device if i2c_smbus_read_i2c_block_data() is used to read the ++ * data. This is a SMBUs protocol limit which can not be avoided. ++ * ++ * Return -EOPNOTSUPP if neither I2C_FUNC_SMBUS_READ_BLOCK_DATA nor ++ * I2C_FUNC_SMBUS_READ_I2C_BLOCK is supported. ++ * ++ * Callers must hold pmbus_lock or execute calls from the probe function. ++ */ ++int pmbus_read_smbus_i2c_block_data(struct i2c_client *client, u8 reg, char *data_buf) ++{ ++ u8 buf[I2C_SMBUS_BLOCK_MAX]; ++ int blen, len, ret; ++ ++ if (i2c_check_functionality(client->adapter, ++ I2C_FUNC_SMBUS_READ_BLOCK_DATA)) { ++ pmbus_wait(client); ++ ret = i2c_smbus_read_block_data(client, reg, data_buf); ++ pmbus_update_ts(client, 0); ++ return ret; ++ } ++ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { ++ dev_err_once(&client->dev, "I2C adapter does not support I2C_FUNC_SMBUS_READ_I2C_BLOCK\n"); ++ return -EOPNOTSUPP; ++ } ++ ++ /* ++ * If the returned data is valid SMBus block data, the first byte ++ * must be the data length. ++ * ++ * i2c_smbus_read_i2c_block_data() may return an error if the chip ++ * sends NACK before the number of requested bytes is received. ++ * Handle this by reading the data length first, then reading the ++ * entire message up to I2C_SMBUS_BLOCK_MAX bytes. This ensures ++ * that requested number of bytes never exceeds the number of ++ * bytes sent by the chip. ++ */ ++ pmbus_wait(client); ++ ret = i2c_smbus_read_i2c_block_data(client, reg, 1, buf); ++ pmbus_update_ts(client, 0); ++ if (ret < 0) ++ return ret; ++ ++ len = buf[0]; ++ if (len == 0) ++ return 0; ++ blen = len; ++ if (len >= I2C_SMBUS_BLOCK_MAX) ++ len = I2C_SMBUS_BLOCK_MAX - 1; ++ pmbus_wait(client); ++ ret = i2c_smbus_read_i2c_block_data(client, reg, len + 1, buf); ++ pmbus_update_ts(client, 0); ++ if (ret < 0) ++ return ret; ++ if (buf[0] != blen) ++ return -EIO; ++ memcpy(data_buf, buf + 1, len); ++ return len; ++} ++EXPORT_SYMBOL_NS_GPL(pmbus_read_smbus_i2c_block_data, "PMBUS"); ++ + static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg, + char *data_buf) + { +@@ -526,11 +613,7 @@ static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg, + if (rv < 0) + return rv; + +- pmbus_wait(client); +- rv = i2c_smbus_read_block_data(client, reg, data_buf); +- pmbus_update_ts(client, 0); +- +- return rv; ++ return pmbus_read_smbus_i2c_block_data(client, reg, data_buf); + } + + static struct pmbus_sensor *pmbus_find_sensor(struct pmbus_data *data, int page, +@@ -2849,9 +2932,16 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, + if (!(data->flags & PMBUS_NO_WRITE_PROTECT)) + pmbus_init_wp(client, data); + +- ret = i2c_smbus_read_byte_data(client, PMBUS_REVISION); +- if (ret >= 0) +- data->revision = ret; ++ if (info->have_pmbus_revision) { ++ data->have_pmbus_revision = true; ++ data->revision = info->pmbus_revision; ++ } else { ++ ret = i2c_smbus_read_byte_data(client, PMBUS_REVISION); ++ if (ret >= 0) { ++ data->have_pmbus_revision = true; ++ data->revision = ret; ++ } ++ } + + if (data->info->pages) + pmbus_clear_faults(client); +@@ -3455,10 +3545,9 @@ static int pmbus_write_smbalert_mask(struct i2c_client *client, u8 page, u8 reg, + return ret; + } + +-static irqreturn_t pmbus_fault_handler(int irq, void *pdata) ++void pmbus_check_and_notify_faults(struct i2c_client *client) + { +- struct pmbus_data *data = pdata; +- struct i2c_client *client = to_i2c_client(data->dev); ++ struct pmbus_data *data = i2c_get_clientdata(client); + int i, status, event; + + guard(pmbus_lock)(client); +@@ -3471,6 +3560,15 @@ static irqreturn_t pmbus_fault_handler(int irq, void *pdata) + } + + pmbus_clear_faults(client); ++} ++EXPORT_SYMBOL_NS_GPL(pmbus_check_and_notify_faults, "PMBUS"); ++ ++static irqreturn_t pmbus_fault_handler(int irq, void *pdata) ++{ ++ struct pmbus_data *data = pdata; ++ struct i2c_client *client = to_i2c_client(data->dev); ++ ++ pmbus_check_and_notify_faults(client); + + return IRQ_HANDLED; + } +@@ -3514,10 +3612,8 @@ static int pmbus_irq_setup(struct i2c_client *client, struct pmbus_data *data) + /* Register notifiers */ + err = devm_request_threaded_irq(dev, client->irq, NULL, pmbus_fault_handler, + IRQF_ONESHOT, "pmbus-irq", data); +- if (err) { +- dev_err(dev, "failed to request an irq %d\n", err); ++ if (err) + return err; +- } + + return 0; + } +@@ -3543,6 +3639,17 @@ static int pmbus_debugfs_get(void *data, u64 *val) + DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops, pmbus_debugfs_get, NULL, + "0x%02llx\n"); + ++static int pmbus_debugfs_get_revision(void *data, u64 *val) ++{ ++ struct pmbus_data *pdata = data; ++ ++ *val = pdata->revision; ++ ++ return 0; ++} ++DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_revision_ops, pmbus_debugfs_get_revision, NULL, ++ "0x%02llx\n"); ++ + static int pmbus_debugfs_get_status(void *data, u64 *val) + { + struct pmbus_debugfs_entry *entry = data; +@@ -3698,14 +3805,9 @@ static void pmbus_init_debugfs(struct i2c_client *client, + &entries[idx++], + &pmbus_debugfs_ops); + } +- if (pmbus_check_byte_register(client, 0, PMBUS_REVISION)) { +- entries[idx].client = client; +- entries[idx].page = 0; +- entries[idx].reg = PMBUS_REVISION; +- debugfs_create_file("pmbus_revision", 0444, debugfs, +- &entries[idx++], +- &pmbus_debugfs_ops); +- } ++ if (data->have_pmbus_revision) ++ debugfs_create_file("pmbus_revision", 0444, debugfs, data, ++ &pmbus_debugfs_revision_ops); + + for (i = 0; i < ARRAY_SIZE(pmbus_debugfs_block_data); i++) { + const struct pmbus_debugfs_data *d = &pmbus_debugfs_block_data[i]; +diff --git a/drivers/hwmon/pmbus/sq24860.c b/drivers/hwmon/pmbus/sq24860.c +new file mode 100644 +--- /dev/null ++++ b/drivers/hwmon/pmbus/sq24860.c +@@ -0,0 +1,430 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Author: Ziming Zhu ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "pmbus.h" ++ ++#define SQ24860_IIN_CAL_GAIN 0x38 ++#define SQ24860_READ_VAUX 0xd0 ++#define SQ24860_READ_VIN_MIN 0xd1 ++#define SQ24860_READ_VIN_PEAK 0xd2 ++#define SQ24860_READ_IIN_PEAK 0xd4 ++#define SQ24860_READ_PIN_PEAK 0xd5 ++#define SQ24860_READ_TEMP_AVG 0xd6 ++#define SQ24860_READ_TEMP_PEAK 0xd7 ++#define SQ24860_READ_VOUT_MIN 0xda ++#define SQ24860_READ_VIN_AVG 0xdc ++#define SQ24860_READ_VOUT_AVG 0xdd ++#define SQ24860_READ_IIN_AVG 0xde ++#define SQ24860_READ_PIN_AVG 0xdf ++#define SQ24860_VIREF 0xe0 ++#define SQ24860_PK_MIN_AVG 0xea ++#define PK_MIN_AVG_RST_PEAK BIT(7) ++#define PK_MIN_AVG_RST_AVG BIT(6) ++#define PK_MIN_AVG_RST_MIN BIT(5) ++#define PK_MIN_AVG_AVG_CNT GENMASK(2, 0) ++#define SQ24860_MFR_WRITE_PROTECT 0xf8 ++#define SQ24860_UNLOCKED BIT(7) ++ ++#define SQ24860_8B_SHIFT 2 ++#define SQ24860_IIN_OCF_NUM 1000000 ++#define SQ24860_IIN_OCF_DIV 129278 ++#define SQ24860_IIN_OCF_OFF 165 ++ ++#define PK_MIN_AVG_RST_MASK (PK_MIN_AVG_RST_PEAK | \ ++ PK_MIN_AVG_RST_AVG | \ ++ PK_MIN_AVG_RST_MIN) ++#define SQ24860_MAX_SAMPLES BIT(FIELD_MAX(PK_MIN_AVG_AVG_CNT)) ++/* ++ * Arbitrary default Rimon value: 1.6kOhm ++ */ ++#define SQ24860_DEFAULT_RIMON 1600000000 ++#define SQ24860_GIMON 18180 ++ ++#define SQ24860_VAUX_DIV 20 ++ ++static int sq24860_write_iin_cal_gain(struct i2c_client *client, u32 rimon) ++{ ++ u64 temp = 6400ULL * 1000000000ULL * 1000ULL; ++ u64 denom; ++ u64 word; ++ ++ if (!rimon) ++ return -EINVAL; ++ ++ denom = (u64)rimon * SQ24860_GIMON; ++ word = div64_u64(temp, denom); ++ if (!word || word > U16_MAX) ++ return -EINVAL; ++ ++ return i2c_smbus_write_word_data(client, SQ24860_IIN_CAL_GAIN, ++ (u16)word); ++} ++ ++static int sq24860_mfr_write_protect_set(struct i2c_client *client, ++ u8 protect) ++{ ++ u8 val; ++ ++ switch (protect) { ++ case 0: ++ val = 0xa2; ++ break; ++ case PB_WP_ALL: ++ val = 0x0; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return pmbus_write_byte_data(client, -1, SQ24860_MFR_WRITE_PROTECT, ++ val); ++} ++ ++static int sq24860_mfr_write_protect_get(struct i2c_client *client) ++{ ++ int ret = pmbus_read_byte_data(client, -1, SQ24860_MFR_WRITE_PROTECT); ++ ++ if (ret < 0) ++ return ret; ++ ++ return (ret & SQ24860_UNLOCKED) ? 0 : PB_WP_ALL; ++} ++ ++static int sq24860_read_word_data(struct i2c_client *client, ++ int page, int phase, int reg) ++{ ++ int ret; ++ ++ switch (reg) { ++ case PMBUS_VIRT_READ_VIN_MAX: ++ ret = pmbus_read_word_data(client, page, phase, ++ SQ24860_READ_VIN_PEAK); ++ break; ++ ++ case PMBUS_VIRT_READ_VIN_MIN: ++ ret = pmbus_read_word_data(client, page, phase, ++ SQ24860_READ_VIN_MIN); ++ break; ++ ++ case PMBUS_VIRT_READ_VIN_AVG: ++ ret = pmbus_read_word_data(client, page, phase, ++ SQ24860_READ_VIN_AVG); ++ break; ++ ++ case PMBUS_VIRT_READ_VOUT_MIN: ++ ret = pmbus_read_word_data(client, page, phase, ++ SQ24860_READ_VOUT_MIN); ++ break; ++ ++ case PMBUS_VIRT_READ_VOUT_AVG: ++ ret = pmbus_read_word_data(client, page, phase, ++ SQ24860_READ_VOUT_AVG); ++ break; ++ ++ case PMBUS_VIRT_READ_IIN_AVG: ++ ret = pmbus_read_word_data(client, page, phase, ++ SQ24860_READ_IIN_AVG); ++ break; ++ ++ case PMBUS_VIRT_READ_IIN_MAX: ++ ret = pmbus_read_word_data(client, page, phase, ++ SQ24860_READ_IIN_PEAK); ++ break; ++ ++ case PMBUS_VIRT_READ_TEMP_AVG: ++ ret = pmbus_read_word_data(client, page, phase, ++ SQ24860_READ_TEMP_AVG); ++ break; ++ ++ case PMBUS_VIRT_READ_TEMP_MAX: ++ ret = pmbus_read_word_data(client, page, phase, ++ SQ24860_READ_TEMP_PEAK); ++ break; ++ ++ case PMBUS_VIRT_READ_PIN_AVG: ++ ret = pmbus_read_word_data(client, page, phase, ++ SQ24860_READ_PIN_AVG); ++ break; ++ ++ case PMBUS_VIRT_READ_PIN_MAX: ++ ret = pmbus_read_word_data(client, page, phase, ++ SQ24860_READ_PIN_PEAK); ++ break; ++ ++ case PMBUS_VIRT_READ_VMON: ++ ret = pmbus_read_word_data(client, page, phase, ++ SQ24860_READ_VAUX); ++ if (ret < 0) ++ break; ++ ret = DIV_ROUND_CLOSEST(ret, SQ24860_VAUX_DIV); ++ break; ++ ++ case PMBUS_VIN_UV_WARN_LIMIT: ++ case PMBUS_VIN_UV_FAULT_LIMIT: ++ case PMBUS_VIN_OV_WARN_LIMIT: ++ case PMBUS_VIN_OV_FAULT_LIMIT: ++ case PMBUS_VOUT_UV_WARN_LIMIT: ++ case PMBUS_IIN_OC_WARN_LIMIT: ++ case PMBUS_OT_WARN_LIMIT: ++ case PMBUS_OT_FAULT_LIMIT: ++ case PMBUS_PIN_OP_WARN_LIMIT: ++ /* ++ * These registers provide an 8 bits value instead of a ++ * 10bits one. Just shifting twice the register value is ++ * enough to make the sensor type conversion work, even ++ * if the datasheet provides different m, b and R for ++ * those. ++ */ ++ ret = pmbus_read_word_data(client, page, phase, reg); ++ if (ret < 0) ++ break; ++ ret <<= SQ24860_8B_SHIFT; ++ break; ++ ++ case PMBUS_IIN_OC_FAULT_LIMIT: ++ /* ++ * VIREF directly sets the over-current limit at which the eFuse ++ * will turn the FET off and trigger a fault. Expose it through ++ * this generic property instead of a manufacturer specific one. ++ */ ++ ret = pmbus_read_byte_data(client, page, SQ24860_VIREF); ++ if (ret < 0) ++ break; ++ ret = DIV_ROUND_CLOSEST(ret * SQ24860_IIN_OCF_NUM, ++ SQ24860_IIN_OCF_DIV); ++ ret += SQ24860_IIN_OCF_OFF; ++ break; ++ ++ case PMBUS_VIRT_SAMPLES: ++ ret = pmbus_read_byte_data(client, page, SQ24860_PK_MIN_AVG); ++ if (ret < 0) ++ break; ++ ret = BIT(FIELD_GET(PK_MIN_AVG_AVG_CNT, ret)); ++ break; ++ ++ case PMBUS_VIRT_RESET_TEMP_HISTORY: ++ case PMBUS_VIRT_RESET_VIN_HISTORY: ++ case PMBUS_VIRT_RESET_IIN_HISTORY: ++ case PMBUS_VIRT_RESET_PIN_HISTORY: ++ case PMBUS_VIRT_RESET_VOUT_HISTORY: ++ ret = 0; ++ break; ++ ++ default: ++ ret = -ENODATA; ++ break; ++ } ++ ++ return ret; ++} ++ ++static int sq24860_write_word_data(struct i2c_client *client, ++ int page, int reg, u16 value) ++{ ++ int ret; ++ ++ switch (reg) { ++ case PMBUS_VIN_UV_WARN_LIMIT: ++ case PMBUS_VIN_UV_FAULT_LIMIT: ++ case PMBUS_VIN_OV_WARN_LIMIT: ++ case PMBUS_VIN_OV_FAULT_LIMIT: ++ case PMBUS_VOUT_UV_WARN_LIMIT: ++ case PMBUS_IIN_OC_WARN_LIMIT: ++ case PMBUS_OT_WARN_LIMIT: ++ case PMBUS_OT_FAULT_LIMIT: ++ case PMBUS_PIN_OP_WARN_LIMIT: ++ value = max_t(s16, (s16)value, 0); ++ value >>= SQ24860_8B_SHIFT; ++ value = clamp_val(value, 0, 0xff); ++ ret = pmbus_write_word_data(client, page, reg, value); ++ break; ++ ++ case PMBUS_IIN_OC_FAULT_LIMIT: ++ value = max_t(s16, (s16)value, SQ24860_IIN_OCF_OFF); ++ value -= SQ24860_IIN_OCF_OFF; ++ value = DIV_ROUND_CLOSEST(((unsigned int)value) * SQ24860_IIN_OCF_DIV, ++ SQ24860_IIN_OCF_NUM); ++ value = clamp_val(value, 0, 0x3f); ++ ret = pmbus_write_byte_data(client, page, SQ24860_VIREF, value); ++ break; ++ ++ case PMBUS_VIRT_SAMPLES: ++ value = clamp_val(value, 1, SQ24860_MAX_SAMPLES); ++ value = ilog2(value); ++ ret = pmbus_update_byte_data(client, page, SQ24860_PK_MIN_AVG, ++ PK_MIN_AVG_AVG_CNT, ++ FIELD_PREP(PK_MIN_AVG_AVG_CNT, value)); ++ break; ++ ++ case PMBUS_VIRT_RESET_TEMP_HISTORY: ++ case PMBUS_VIRT_RESET_VIN_HISTORY: ++ case PMBUS_VIRT_RESET_IIN_HISTORY: ++ case PMBUS_VIRT_RESET_PIN_HISTORY: ++ case PMBUS_VIRT_RESET_VOUT_HISTORY: ++ /* ++ * SQ24860 has history resets based on MIN/AVG/PEAK instead of per ++ * sensor type. Exposing this quirk in hwmon is not desirable so ++ * reset MIN, AVG and PEAK together. Even is there effectively only ++ * one reset, which resets everything, expose the 5 entries so ++ * userspace is not required map a sensor type to another to trigger ++ * a reset ++ */ ++ ret = pmbus_update_byte_data(client, 0, SQ24860_PK_MIN_AVG, ++ PK_MIN_AVG_RST_MASK, ++ PK_MIN_AVG_RST_MASK); ++ break; ++ ++ default: ++ ret = -ENODATA; ++ break; ++ } ++ ++ return ret; ++} ++ ++static int sq24860_read_byte_data(struct i2c_client *client, ++ int page, int reg) ++{ ++ int ret; ++ ++ switch (reg) { ++ case PMBUS_WRITE_PROTECT: ++ ret = sq24860_mfr_write_protect_get(client); ++ break; ++ ++ default: ++ ret = -ENODATA; ++ break; ++ } ++ ++ return ret; ++} ++ ++static int sq24860_write_byte_data(struct i2c_client *client, ++ int page, int reg, u8 byte) ++{ ++ int ret; ++ ++ switch (reg) { ++ case PMBUS_WRITE_PROTECT: ++ ret = sq24860_mfr_write_protect_set(client, byte); ++ break; ++ ++ default: ++ ret = -ENODATA; ++ break; ++ } ++ ++ return ret; ++} ++ ++#if IS_ENABLED(CONFIG_SENSORS_SQ24860_REGULATOR) ++static const struct regulator_desc sq24860_reg_desc[] = { ++ PMBUS_REGULATOR_ONE_NODE("vout"), ++}; ++#endif ++ ++static const struct pmbus_driver_info sq24860_base_info = { ++ .pages = 1, ++ .format[PSC_VOLTAGE_IN] = direct, ++ .m[PSC_VOLTAGE_IN] = 64, ++ .b[PSC_VOLTAGE_IN] = 0, ++ .R[PSC_VOLTAGE_IN] = 0, ++ .format[PSC_VOLTAGE_OUT] = direct, ++ .m[PSC_VOLTAGE_OUT] = 64, ++ .b[PSC_VOLTAGE_OUT] = 0, ++ .R[PSC_VOLTAGE_OUT] = 0, ++ .format[PSC_TEMPERATURE] = direct, ++ .m[PSC_TEMPERATURE] = 1, ++ .b[PSC_TEMPERATURE] = 0, ++ .R[PSC_TEMPERATURE] = 0, ++ /* ++ * Current and power measurements depend on the calibration gain ++ * programmed from the board-specific IMON resistor value. ++ */ ++ .format[PSC_CURRENT_IN] = direct, ++ .m[PSC_CURRENT_IN] = 16, ++ .b[PSC_CURRENT_IN] = 0, ++ .R[PSC_CURRENT_IN] = 0, ++ .format[PSC_POWER] = direct, ++ .m[PSC_POWER] = 2, ++ .b[PSC_POWER] = 0, ++ .R[PSC_POWER] = 0, ++ .func[0] = PMBUS_HAVE_VIN | ++ PMBUS_HAVE_VOUT | ++ PMBUS_HAVE_VMON | ++ PMBUS_HAVE_IIN | ++ PMBUS_HAVE_PIN | ++ PMBUS_HAVE_TEMP | ++ PMBUS_HAVE_STATUS_VOUT | ++ PMBUS_HAVE_STATUS_IOUT | ++ PMBUS_HAVE_STATUS_INPUT | ++ PMBUS_HAVE_STATUS_TEMP | ++ PMBUS_HAVE_SAMPLES, ++ .read_word_data = sq24860_read_word_data, ++ .write_word_data = sq24860_write_word_data, ++ .read_byte_data = sq24860_read_byte_data, ++ .write_byte_data = sq24860_write_byte_data, ++ ++#if IS_ENABLED(CONFIG_SENSORS_SQ24860_REGULATOR) ++ .reg_desc = sq24860_reg_desc, ++ .num_regulators = ARRAY_SIZE(sq24860_reg_desc), ++#endif ++}; ++ ++static const struct i2c_device_id sq24860_i2c_id[] = { ++ { "sq24860" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(i2c, sq24860_i2c_id); ++ ++static const struct of_device_id sq24860_of_match[] = { ++ { .compatible = "silergy,sq24860" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, sq24860_of_match); ++ ++static int sq24860_probe(struct i2c_client *client) ++{ ++ struct device *dev = &client->dev; ++ struct pmbus_driver_info *info; ++ u32 rimon; ++ int ret; ++ ++ if (device_property_read_u32(dev, "silergy,rimon-micro-ohms", &rimon)) ++ rimon = SQ24860_DEFAULT_RIMON; ++ ret = sq24860_write_iin_cal_gain(client, rimon); ++ if (ret < 0) ++ return dev_err_probe(&client->dev, ret, ++ "Failed to set gain\n"); ++ info = devm_kmemdup(dev, &sq24860_base_info, sizeof(*info), GFP_KERNEL); ++ if (!info) ++ return -ENOMEM; ++ ++ return pmbus_do_probe(client, info); ++} ++ ++static struct i2c_driver sq24860_driver = { ++ .driver = { ++ .name = "sq24860", ++ .of_match_table = sq24860_of_match, ++ }, ++ .probe = sq24860_probe, ++ .id_table = sq24860_i2c_id, ++}; ++module_i2c_driver(sq24860_driver); ++ ++MODULE_AUTHOR("Ziming Zhu "); ++MODULE_DESCRIPTION("PMBUS driver for SQ24860 eFuse"); ++MODULE_LICENSE("GPL"); ++MODULE_IMPORT_NS("PMBUS"); +diff --git a/drivers/hwmon/pmbus/vt7505.c b/drivers/hwmon/pmbus/vt7505.c +new file mode 100644 +--- /dev/null ++++ b/drivers/hwmon/pmbus/vt7505.c +@@ -0,0 +1,287 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Hardware monitoring driver for Analog Devices MAX16545/MAX16550 and ++ * Volterra VT7505 PMBus controllers. ++ * ++ * Copyright 2026 Hewlett Packard Enterprise Development LP ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "pmbus.h" ++ ++#define VT7505_MFR_CONFIG 0xd0 ++#define VT7505_CFG_OCP_S_FILT_MASK GENMASK(15, 14) ++ ++#define VT7505_MFR_PEAK_VIN 0xd1 ++#define VT7505_MFR_PEAK_IOUT 0xd2 ++#define VT7505_MFR_PEAK_PIN 0xd3 ++#define VT7505_MFR_PEAK_TEMP 0xd4 ++#define VT7505_MFR_CLEAR_PEAKS 0xd5 ++#define VT7505_MFR_PEAK_VOUT 0xfd ++ ++#define VT7505_RLOAD_DEFAULT 4750 ++/* Largest RLOAD for which coeff * rload / 1000 still fits in s32. */ ++#define VT7505_RLOAD_MAX 283383959U ++ ++struct vt7505_chip_data { ++ int temp_m; ++ int temp_b; ++ bool has_ocp_filter; ++}; ++ ++static const struct vt7505_chip_data max16545_data = { ++ .temp_m = 205, ++ .temp_b = 6545, ++}; ++ ++static const struct vt7505_chip_data max16550_data = { ++ .temp_m = 199, ++ .temp_b = 7046, ++ .has_ocp_filter = true, ++}; ++ ++static const struct vt7505_chip_data vt7505_data = { ++ .temp_m = 205, ++ .temp_b = 6545, ++ .has_ocp_filter = true, ++}; ++ ++static int vt7505_read_word_data(struct i2c_client *client, int page, ++ int phase, int reg) ++{ ++ switch (reg) { ++ case PMBUS_VIRT_READ_VIN_MAX: ++ return pmbus_read_word_data(client, page, phase, ++ VT7505_MFR_PEAK_VIN); ++ case PMBUS_VIRT_READ_IOUT_MAX: ++ return pmbus_read_word_data(client, page, phase, ++ VT7505_MFR_PEAK_IOUT); ++ case PMBUS_VIRT_READ_PIN_MAX: ++ return pmbus_read_word_data(client, page, phase, ++ VT7505_MFR_PEAK_PIN); ++ case PMBUS_VIRT_READ_TEMP_MAX: ++ return pmbus_read_word_data(client, page, phase, ++ VT7505_MFR_PEAK_TEMP); ++ case PMBUS_VIRT_READ_VOUT_MAX: ++ return pmbus_read_word_data(client, page, phase, ++ VT7505_MFR_PEAK_VOUT); ++ case PMBUS_VIRT_RESET_VIN_HISTORY: ++ case PMBUS_VIRT_RESET_IOUT_HISTORY: ++ case PMBUS_VIRT_RESET_PIN_HISTORY: ++ case PMBUS_VIRT_RESET_TEMP_HISTORY: ++ case PMBUS_VIRT_RESET_VOUT_HISTORY: ++ return 0; ++ default: ++ return -ENODATA; ++ } ++} ++ ++static int vt7505_write_word_data(struct i2c_client *client, int page, ++ int reg, u16 word) ++{ ++ switch (reg) { ++ /* ++ * A single reset command clears all peak values. CLEAR_PEAKS is a ++ * send-byte command; the device NAKs a word or byte-data write to it. ++ */ ++ case PMBUS_VIRT_RESET_VIN_HISTORY: ++ case PMBUS_VIRT_RESET_IOUT_HISTORY: ++ case PMBUS_VIRT_RESET_PIN_HISTORY: ++ case PMBUS_VIRT_RESET_TEMP_HISTORY: ++ case PMBUS_VIRT_RESET_VOUT_HISTORY: ++ return pmbus_write_byte(client, page, ++ VT7505_MFR_CLEAR_PEAKS); ++ default: ++ return -ENODATA; ++ } ++} ++ ++/* ++ * None of these controllers implement the standard PMBus WRITE_PROTECT ++ * (0x10) register, so tell the core not to access it. ++ */ ++static struct pmbus_platform_data vt7505_pdata = { ++ .flags = PMBUS_NO_WRITE_PROTECT, ++}; ++ ++static int vt7505_set_ocp_filter(struct i2c_client *client) ++{ ++ u32 ocp_us; ++ u8 field; ++ int ret; ++ u16 word; ++ ++ if (of_property_read_u32(client->dev.of_node, "adi,ocp-severe-filter-us", ++ &ocp_us)) ++ return 0; ++ ++ switch (ocp_us) { ++ case 0: ++ field = 0; ++ break; ++ case 1: ++ field = 1; ++ break; ++ case 2: ++ field = 2; ++ break; ++ case 10: ++ field = 3; ++ break; ++ default: ++ return dev_err_probe(&client->dev, -EINVAL, ++ "invalid adi,ocp-severe-filter-us value %u\n", ++ ocp_us); ++ } ++ ++ ret = i2c_smbus_read_word_data(client, VT7505_MFR_CONFIG); ++ if (ret < 0) ++ return dev_err_probe(&client->dev, ret, ++ "failed to read MFR_CONFIG\n"); ++ ++ word = ret & ~VT7505_CFG_OCP_S_FILT_MASK; ++ word |= FIELD_PREP(VT7505_CFG_OCP_S_FILT_MASK, field); ++ ++ ret = i2c_smbus_write_word_data(client, VT7505_MFR_CONFIG, word); ++ if (ret < 0) ++ return dev_err_probe(&client->dev, ret, ++ "failed to write MFR_CONFIG\n"); ++ ++ return 0; ++} ++ ++static void vt7505_set_m(int *m, u32 rload) ++{ ++ u64 val = (u64)*m * rload; ++ ++ /* rload is range-checked in probe, so the result fits in int. */ ++ *m = DIV_ROUND_CLOSEST_ULL(val, 1000); ++} ++ ++static int vt7505_probe(struct i2c_client *client) ++{ ++ struct device *dev = &client->dev; ++ const struct vt7505_chip_data *chip; ++ struct pmbus_driver_info *info; ++ u32 rload; ++ int ret; ++ ++ chip = i2c_get_match_data(client); ++ if (!chip) ++ return -ENODEV; ++ ++ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); ++ if (!info) ++ return -ENOMEM; ++ ++ dev->platform_data = &vt7505_pdata; ++ ++ /* ++ * The m coefficient used in the direct-format current and power ++ * calculations depends on RLOAD, the external current-report resistor ++ * connected between the ILOAD pin and ground. Use the default value if ++ * none is specified. ++ */ ++ if (of_property_read_u32(dev->of_node, "adi,rload-ohms", &rload)) ++ rload = VT7505_RLOAD_DEFAULT; ++ ++ if (!rload || rload > VT7505_RLOAD_MAX) ++ return dev_err_probe(dev, -EINVAL, ++ "adi,rload-ohms must be 1-%u\n", ++ VT7505_RLOAD_MAX); ++ ++ info->pages = 1; ++ info->read_word_data = vt7505_read_word_data; ++ info->write_word_data = vt7505_write_word_data; ++ ++ info->format[PSC_VOLTAGE_IN] = direct; ++ info->format[PSC_VOLTAGE_OUT] = direct; ++ info->format[PSC_CURRENT_IN] = direct; ++ info->format[PSC_CURRENT_OUT] = direct; ++ info->format[PSC_POWER] = direct; ++ info->format[PSC_TEMPERATURE] = direct; ++ ++ /* ++ * Direct data format coefficients from the device datasheet ("PMBus ++ * Equation Parameters"). The current and power m coefficients scale ++ * with RLOAD; vt7505_set_m() applies the 1/1000 factor below, giving ++ * m = 3.824 * RLOAD for current and 0.895 * RLOAD for power. The ++ * temperature coefficients are chip specific (see the chip data). ++ */ ++ info->m[PSC_VOLTAGE_IN] = 7578; ++ info->R[PSC_VOLTAGE_IN] = -2; ++ info->m[PSC_VOLTAGE_OUT] = 7578; ++ info->R[PSC_VOLTAGE_OUT] = -2; ++ info->m[PSC_CURRENT_IN] = 3824; ++ info->b[PSC_CURRENT_IN] = -4300; ++ info->R[PSC_CURRENT_IN] = -3; ++ info->m[PSC_CURRENT_OUT] = 3824; ++ info->b[PSC_CURRENT_OUT] = -4300; ++ info->R[PSC_CURRENT_OUT] = -3; ++ info->m[PSC_POWER] = 895; ++ info->b[PSC_POWER] = -9100; ++ info->R[PSC_POWER] = -2; ++ ++ vt7505_set_m(&info->m[PSC_CURRENT_IN], rload); ++ vt7505_set_m(&info->m[PSC_CURRENT_OUT], rload); ++ vt7505_set_m(&info->m[PSC_POWER], rload); ++ ++ info->m[PSC_TEMPERATURE] = chip->temp_m; ++ info->b[PSC_TEMPERATURE] = chip->temp_b; ++ info->R[PSC_TEMPERATURE] = -2; ++ ++ info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT | ++ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | ++ PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | ++ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | ++ PMBUS_HAVE_IIN | PMBUS_HAVE_PIN; ++ ++ /* ++ * The severe OCP deglitch filter is programmable on the MAX16550 and ++ * the VT7505, but fixed on the MAX16545. ++ */ ++ if (chip->has_ocp_filter) { ++ ret = vt7505_set_ocp_filter(client); ++ if (ret) ++ return ret; ++ } ++ ++ return pmbus_do_probe(client, info); ++} ++ ++static const struct i2c_device_id vt7505_id[] = { ++ { .name = "max16545", .driver_data = (kernel_ulong_t)&max16545_data }, ++ { .name = "max16550", .driver_data = (kernel_ulong_t)&max16550_data }, ++ { .name = "vt7505", .driver_data = (kernel_ulong_t)&vt7505_data }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, vt7505_id); ++ ++static const struct of_device_id vt7505_of_match[] = { ++ { .compatible = "adi,max16545", .data = &max16545_data }, ++ { .compatible = "adi,max16550", .data = &max16550_data }, ++ { .compatible = "adi,vt7505", .data = &vt7505_data }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, vt7505_of_match); ++ ++static struct i2c_driver vt7505_driver = { ++ .driver = { ++ .name = "vt7505", ++ .of_match_table = vt7505_of_match, ++ }, ++ .probe = vt7505_probe, ++ .id_table = vt7505_id, ++}; ++module_i2c_driver(vt7505_driver); ++ ++MODULE_AUTHOR("Georgi Vlaev "); ++MODULE_DESCRIPTION("PMBus driver for Analog Devices MAX16545/MAX16550 and Volterra VT7505"); ++MODULE_LICENSE("GPL"); ++MODULE_IMPORT_NS("PMBUS"); +diff --git a/drivers/hwmon/pmbus/xdpe1a2g7b.c b/drivers/hwmon/pmbus/xdpe1a2g7b.c +--- a/drivers/hwmon/pmbus/xdpe1a2g7b.c ++++ b/drivers/hwmon/pmbus/xdpe1a2g7b.c +@@ -57,6 +57,13 @@ static int xdpe1a2g7b_identify(struct i2c_client *client, + return 0; + } + ++#if IS_ENABLED(CONFIG_SENSORS_XDPE1A2G7B_REGULATOR) ++static const struct regulator_desc xdpe1a2g7b_reg_desc[] = { ++ PMBUS_REGULATOR("vout", 0), ++ PMBUS_REGULATOR("vout", 1), ++}; ++#endif ++ + static struct pmbus_driver_info xdpe1a2g7b_info = { + .pages = XDPE1A2G7B_PAGE_NUM, + .identify = xdpe1a2g7b_identify, +@@ -72,6 +79,10 @@ static struct pmbus_driver_info xdpe1a2g7b_info = { + .func[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | PMBUS_HAVE_STATUS_INPUT, ++#if IS_ENABLED(CONFIG_SENSORS_XDPE1A2G7B_REGULATOR) ++ .num_regulators = XDPE1A2G7B_PAGE_NUM, ++ .reg_desc = xdpe1a2g7b_reg_desc, ++#endif + }; + + static int xdpe1a2g7b_probe(struct i2c_client *client) +diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c +--- a/drivers/hwmon/pwm-fan.c ++++ b/drivers/hwmon/pwm-fan.c +@@ -634,12 +634,8 @@ static int pwm_fan_probe(struct platform_device *pdev) + if (tach->irq > 0) { + ret = devm_request_irq(dev, tach->irq, pulse_handler, + IRQF_NO_THREAD, pdev->name, tach); +- if (ret) { +- dev_err(dev, +- "Failed to request interrupt: %d\n", +- ret); ++ if (ret) + return ret; +- } + } + + if (!ctx->pulses_per_revolution[i]) { +diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c +--- a/drivers/hwmon/sht15.c ++++ b/drivers/hwmon/sht15.c +@@ -981,10 +981,8 @@ static int sht15_probe(struct platform_device *pdev) + IRQF_TRIGGER_FALLING, + "sht15 data", + data); +- if (ret) { +- dev_err(&pdev->dev, "failed to get irq for data line\n"); ++ if (ret) + goto err_release_reg; +- } + disable_irq_nosync(gpiod_to_irq(data->data)); + ret = sht15_connection_reset(data); + if (ret) +diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c +--- a/drivers/hwmon/sht3x.c ++++ b/drivers/hwmon/sht3x.c +@@ -62,7 +62,7 @@ static const unsigned char sht3x_cmd_read_serial_number[] = { 0x37, 0x80 }; + #define SHT3X_MAX_HUMIDITY 100000 + + enum sht3x_chips { +- sht3x, ++ sht3x = 1, + sts3x, + }; + +@@ -940,8 +940,19 @@ static const struct i2c_device_id sht3x_ids[] = { + + MODULE_DEVICE_TABLE(i2c, sht3x_ids); + ++static const struct of_device_id sht3x_of_match[] = { ++ { .compatible = "sensirion,sht30", .data = (void *)(uintptr_t)sht3x }, ++ { .compatible = "sensirion,sts30", .data = (void *)(uintptr_t)sts3x }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(of, sht3x_of_match); ++ + static struct i2c_driver sht3x_i2c_driver = { +- .driver.name = "sht3x", ++ .driver = { ++ .name = "sht3x", ++ .of_match_table = sht3x_of_match, ++ }, + .probe = sht3x_probe, + .id_table = sht3x_ids, + }; +diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c +--- a/drivers/hwmon/tmp102.c ++++ b/drivers/hwmon/tmp102.c +@@ -396,6 +396,8 @@ static DEFINE_SIMPLE_DEV_PM_OPS(tmp102_dev_pm_ops, tmp102_suspend, tmp102_resume + + static const struct i2c_device_id tmp102_id[] = { + { .name = "tmp102" }, ++ { .name = "tmp110" }, ++ { .name = "tmp113" }, + { } + }; + MODULE_DEVICE_TABLE(i2c, tmp102_id); +diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c +--- a/drivers/hwmon/tmp401.c ++++ b/drivers/hwmon/tmp401.c +@@ -695,7 +695,7 @@ static int tmp401_probe(struct i2c_client *client) + info->type = hwmon_chip; + info->config = data->chip_channel_config; + +- data->chip_channel_config[0] = HWMON_C_UPDATE_INTERVAL; ++ data->chip_channel_config[0] = HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL; + + info = &data->temp_info; + info->type = hwmon_temp; +diff --git a/drivers/hwmon/tvs-mpfs.c b/drivers/hwmon/tvs-mpfs.c +new file mode 100644 +--- /dev/null ++++ b/drivers/hwmon/tvs-mpfs.c +@@ -0,0 +1,390 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Author: Lars Randers ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MPFS_TVS_CTRL 0x08 ++#define MPFS_TVS_OUTPUT0 0x24 ++#define MPFS_TVS_OUTPUT1 0x28 ++ ++#define MPFS_TVS_CTRL_TEMP_VALID BIT(19) ++#define MPFS_TVS_CTRL_V2P5_VALID BIT(18) ++#define MPFS_TVS_CTRL_V1P8_VALID BIT(17) ++#define MPFS_TVS_CTRL_V1P05_VALID BIT(16) ++ ++#define MPFS_TVS_CTRL_TEMP_ENABLE BIT(3) ++#define MPFS_TVS_CTRL_V2P5_ENABLE BIT(2) ++#define MPFS_TVS_CTRL_V1P8_ENABLE BIT(1) ++#define MPFS_TVS_CTRL_V1P05_ENABLE BIT(0) ++#define MPFS_TVS_CTRL_ENABLE_ALL GENMASK(3, 0) ++ ++/* ++ * For all of these the value in millivolts is stored in 16 bits, with an upper ++ * sign bit and a lower 3 bits of decimal. These masks discard the sign bit and ++ * decimal places, because if Linux is running these voltages cannot be negative ++ * and so avoid having to convert to two's complement. ++ */ ++#define MPFS_OUTPUT0_V1P8_MASK GENMASK(30, 19) ++#define MPFS_OUTPUT0_V1P05_MASK GENMASK(14, 3) ++#define MPFS_OUTPUT1_V2P5_MASK GENMASK(14, 3) ++ ++/* ++ * The register map claims that the temperature is stored in bits 31:16, but ++ * application note "AN4682: PolarFire FPGA Temperature and Voltage Sensor" ++ * says that 31 is reserved. Temperature is in kelvin, so what's probably a ++ * sign bit has no value anyway. ++ */ ++#define MPFS_OUTPUT1_TEMP_MASK GENMASK(30, 16) ++ ++#define MPFS_TVS_INTERVAL_MASK GENMASK(15, 8) ++#define MPFS_TVS_INTERVAL_OFFSET 8 ++/* The interval register is in increments of 32 us */ ++#define MPFS_TVS_INTERVAL_SCALE 32 ++/* with 254 usable increments of 32 us available, 8 ms is the integer limit */ ++#define MPFS_TVS_INTERVAL_MAX_MS 8 ++ ++/* 273.1875 in 11.4 fixed-point notation */ ++#define MPFS_TVS_K_TO_C 0x1113 ++ ++enum mpfs_tvs_sensors { ++ SENSOR_V1P05 = 0, ++ SENSOR_V1P8, ++ SENSOR_V2P5, ++}; ++ ++static const char * const mpfs_tvs_voltage_labels[] = { "1P05", "1P8", "2P5" }; ++ ++struct mpfs_tvs { ++ struct regmap *regmap; ++}; ++ ++static int mpfs_tvs_voltage_read(struct mpfs_tvs *data, u32 attr, ++ int channel, long *val) ++{ ++ u32 tmp, control; ++ ++ if (attr != hwmon_in_input && attr != hwmon_in_enable) ++ return -EOPNOTSUPP; ++ ++ regmap_read(data->regmap, MPFS_TVS_CTRL, &control); ++ ++ switch (channel) { ++ case SENSOR_V2P5: ++ if (attr == hwmon_in_enable) { ++ *val = FIELD_GET(MPFS_TVS_CTRL_V2P5_ENABLE, control); ++ break; ++ } ++ ++ if (!(control & MPFS_TVS_CTRL_V2P5_VALID)) ++ return -ENODATA; ++ ++ regmap_read(data->regmap, MPFS_TVS_OUTPUT1, &tmp); ++ *val = FIELD_GET(MPFS_OUTPUT1_V2P5_MASK, tmp); ++ break; ++ case SENSOR_V1P8: ++ if (attr == hwmon_in_enable) { ++ *val = FIELD_GET(MPFS_TVS_CTRL_V1P8_ENABLE, control); ++ break; ++ } ++ ++ if (!(control & MPFS_TVS_CTRL_V1P8_VALID)) ++ return -ENODATA; ++ ++ regmap_read(data->regmap, MPFS_TVS_OUTPUT0, &tmp); ++ *val = FIELD_GET(MPFS_OUTPUT0_V1P8_MASK, tmp); ++ break; ++ case SENSOR_V1P05: ++ if (attr == hwmon_in_enable) { ++ *val = FIELD_GET(MPFS_TVS_CTRL_V1P05_ENABLE, control); ++ break; ++ } ++ ++ if (!(control & MPFS_TVS_CTRL_V1P05_VALID)) ++ return -ENODATA; ++ ++ regmap_read(data->regmap, MPFS_TVS_OUTPUT0, &tmp); ++ *val = FIELD_GET(MPFS_OUTPUT0_V1P05_MASK, tmp); ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} ++ ++static int mpfs_tvs_voltage_write(struct mpfs_tvs *data, u32 attr, ++ int channel, long val) ++{ ++ u32 tmp; ++ ++ if (attr != hwmon_in_enable) ++ return -EOPNOTSUPP; ++ ++ if (val > 1 || val < 0) ++ return -EINVAL; ++ ++ switch (channel) { ++ case SENSOR_V2P5: ++ tmp = FIELD_PREP(MPFS_TVS_CTRL_V2P5_ENABLE, val); ++ regmap_update_bits(data->regmap, MPFS_TVS_CTRL, ++ MPFS_TVS_CTRL_V2P5_ENABLE, tmp); ++ break; ++ case SENSOR_V1P8: ++ tmp = FIELD_PREP(MPFS_TVS_CTRL_V1P8_ENABLE, val); ++ regmap_update_bits(data->regmap, MPFS_TVS_CTRL, ++ MPFS_TVS_CTRL_V1P8_ENABLE, tmp); ++ break; ++ case SENSOR_V1P05: ++ tmp = FIELD_PREP(MPFS_TVS_CTRL_V1P05_ENABLE, val); ++ regmap_update_bits(data->regmap, MPFS_TVS_CTRL, ++ MPFS_TVS_CTRL_V1P05_ENABLE, tmp); ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} ++ ++static int mpfs_tvs_temp_read(struct mpfs_tvs *data, u32 attr, long *val) ++{ ++ u32 tmp, control; ++ ++ if (attr != hwmon_temp_input && attr != hwmon_temp_enable) ++ return -EOPNOTSUPP; ++ ++ regmap_read(data->regmap, MPFS_TVS_CTRL, &control); ++ ++ if (attr == hwmon_temp_enable) { ++ *val = FIELD_GET(MPFS_TVS_CTRL_TEMP_ENABLE, control); ++ return 0; ++ } ++ ++ if (!(control & MPFS_TVS_CTRL_TEMP_VALID)) ++ return -ENODATA; ++ ++ regmap_read(data->regmap, MPFS_TVS_OUTPUT1, &tmp); ++ *val = FIELD_GET(MPFS_OUTPUT1_TEMP_MASK, tmp); ++ *val -= MPFS_TVS_K_TO_C; ++ *val = (1000 * *val) >> 4; /* fixed point (11.4) to millidegrees */ ++ ++ return 0; ++} ++ ++static int mpfs_tvs_temp_write(struct mpfs_tvs *data, u32 attr, long val) ++{ ++ u32 tmp; ++ ++ if (attr != hwmon_temp_enable) ++ return -EOPNOTSUPP; ++ ++ if (val > 1 || val < 0) ++ return -EINVAL; ++ ++ tmp = FIELD_PREP(MPFS_TVS_CTRL_TEMP_ENABLE, val); ++ regmap_update_bits(data->regmap, MPFS_TVS_CTRL, ++ MPFS_TVS_CTRL_TEMP_ENABLE, tmp); ++ ++ return 0; ++} ++ ++static int mpfs_tvs_interval_read(struct mpfs_tvs *data, u32 attr, long *val) ++{ ++ u32 tmp; ++ ++ if (attr != hwmon_chip_update_interval) ++ return -EOPNOTSUPP; ++ ++ regmap_read(data->regmap, MPFS_TVS_CTRL, &tmp); ++ *val = FIELD_GET(MPFS_TVS_INTERVAL_MASK, tmp); ++ *val *= MPFS_TVS_INTERVAL_SCALE; ++ *val = roundup(*val, 1000); ++ *val /= 1000; ++ ++ return 0; ++} ++ ++static int mpfs_tvs_interval_write(struct mpfs_tvs *data, u32 attr, long val) ++{ ++ long temp = val; ++ ++ if (attr != hwmon_chip_update_interval) ++ return -EOPNOTSUPP; ++ ++ temp = clamp(temp, 0, MPFS_TVS_INTERVAL_MAX_MS); ++ ++ temp *= 1000; ++ temp /= MPFS_TVS_INTERVAL_SCALE; ++ ++ temp <<= MPFS_TVS_INTERVAL_OFFSET; ++ regmap_update_bits(data->regmap, MPFS_TVS_CTRL, ++ MPFS_TVS_INTERVAL_MASK, temp); ++ ++ return 0; ++} ++ ++static umode_t mpfs_tvs_is_visible(const void *data, ++ enum hwmon_sensor_types type, ++ u32 attr, int channel) ++{ ++ if (type == hwmon_chip && attr == hwmon_chip_update_interval) ++ return 0644; ++ ++ if (type == hwmon_temp) { ++ switch (attr) { ++ case hwmon_temp_enable: ++ return 0644; ++ case hwmon_temp_input: ++ case hwmon_temp_label: ++ return 0444; ++ default: ++ return 0; ++ } ++ } ++ ++ if (type == hwmon_in) { ++ switch (attr) { ++ case hwmon_in_enable: ++ return 0644; ++ case hwmon_in_input: ++ case hwmon_in_label: ++ return 0444; ++ default: ++ return 0; ++ } ++ } ++ ++ return 0; ++} ++ ++static int mpfs_tvs_read(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, long *val) ++{ ++ struct mpfs_tvs *data = dev_get_drvdata(dev); ++ ++ switch (type) { ++ case hwmon_temp: ++ return mpfs_tvs_temp_read(data, attr, val); ++ case hwmon_in: ++ return mpfs_tvs_voltage_read(data, attr, channel, val); ++ case hwmon_chip: ++ return mpfs_tvs_interval_read(data, attr, val); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static int mpfs_tvs_write(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, long val) ++{ ++ struct mpfs_tvs *data = dev_get_drvdata(dev); ++ ++ switch (type) { ++ case hwmon_temp: ++ return mpfs_tvs_temp_write(data, attr, val); ++ case hwmon_in: ++ return mpfs_tvs_voltage_write(data, attr, channel, val); ++ case hwmon_chip: ++ return mpfs_tvs_interval_write(data, attr, val); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static int mpfs_tvs_read_labels(struct device *dev, ++ enum hwmon_sensor_types type, ++ u32 attr, int channel, ++ const char **str) ++{ ++ switch (type) { ++ case hwmon_temp: ++ *str = "Die Temp"; ++ return 0; ++ case hwmon_in: ++ *str = mpfs_tvs_voltage_labels[channel]; ++ return 0; ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static const struct hwmon_ops mpfs_tvs_ops = { ++ .is_visible = mpfs_tvs_is_visible, ++ .read_string = mpfs_tvs_read_labels, ++ .read = mpfs_tvs_read, ++ .write = mpfs_tvs_write, ++}; ++ ++static const struct hwmon_channel_info *mpfs_tvs_info[] = { ++ HWMON_CHANNEL_INFO(chip, ++ HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL), ++ HWMON_CHANNEL_INFO(temp, ++ HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_ENABLE), ++ HWMON_CHANNEL_INFO(in, ++ HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_ENABLE, ++ HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_ENABLE, ++ HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_ENABLE), ++ NULL ++}; ++ ++static const struct hwmon_chip_info mpfs_tvs_chip_info = { ++ .ops = &mpfs_tvs_ops, ++ .info = mpfs_tvs_info, ++}; ++ ++static int mpfs_tvs_probe(struct platform_device *pdev) ++{ ++ struct device *hwmon_dev; ++ struct mpfs_tvs *data; ++ ++ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ data->regmap = device_node_to_regmap(pdev->dev.parent->of_node); ++ if (IS_ERR(data->regmap)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(data->regmap), ++ "Failed to find syscon regmap\n"); ++ ++ /* ++ * It's an MMIO regmap with no resources, there's nothing that can fail ++ * and return an error ++ */ ++ regmap_write(data->regmap, MPFS_TVS_CTRL, MPFS_TVS_CTRL_ENABLE_ALL); ++ ++ hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, "mpfs_tvs", ++ data, ++ &mpfs_tvs_chip_info, ++ NULL); ++ if (IS_ERR(hwmon_dev)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(hwmon_dev), ++ "hwmon device registration failed.\n"); ++ ++ return 0; ++} ++ ++static struct platform_driver mpfs_tvs_driver = { ++ .probe = mpfs_tvs_probe, ++ .driver = { ++ .name = "mpfs-tvs", ++ }, ++}; ++module_platform_driver(mpfs_tvs_driver); ++ ++MODULE_AUTHOR("Lars Randers "); ++MODULE_DESCRIPTION("PolarFire SoC temperature & voltage sensor driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/hwmon/xgene-hwmon.c b/drivers/hwmon/xgene-hwmon.c +--- a/drivers/hwmon/xgene-hwmon.c ++++ b/drivers/hwmon/xgene-hwmon.c +@@ -133,10 +133,6 @@ static int xgene_hwmon_pcc_rd(struct xgene_hwmon_dev *ctx, u32 *msg) + init_completion(&ctx->rd_complete); + ctx->resp_pending = true; + +- /* Write signature for subspace */ +- WRITE_ONCE(generic_comm_base->signature, +- cpu_to_le32(PCC_SIGNATURE | ctx->mbox_idx)); +- + /* Write to the shared command region */ + WRITE_ONCE(generic_comm_base->command, + cpu_to_le16(MSG_TYPE(msg[0]) | PCC_CMD_GENERATE_DB_INTR)); +diff --git a/drivers/hwmon/yogafan.c b/drivers/hwmon/yogafan.c +--- a/drivers/hwmon/yogafan.c ++++ b/drivers/hwmon/yogafan.c +@@ -53,6 +53,7 @@ struct yoga_fan_data { + }; + + /* Specific configurations mapped via DMI */ ++ + static const struct yogafan_config yoga_8bit_fans_cfg = { + .multiplier = 100, + .fan_count = 1, +@@ -71,6 +72,24 @@ static const struct yogafan_config legion_16bit_dual_cfg = { + .paths = { "\\_SB.PCI0.LPC0.EC0.FANS", "\\_SB.PCI0.LPC0.EC0.FA2S" } + }; + ++static const struct yogafan_config loq_15iax9_8bit_dual_cfg = { ++ .multiplier = 100, ++ .fan_count = 2, ++ .paths = { "\\_SB.PC00.LPCB.EC0.FA1S", "\\_SB.PC00.LPCB.EC0.FA2S" } ++}; ++ ++static const struct yogafan_config xiaoxin_8bit_dual_cfg = { ++ .multiplier = 100, ++ .fan_count = 2, ++ .paths = { "\\_SB.PCI0.LPC0.EC0.FANS", "\\_SB.PCI0.LPC0.EC0.FA2S" } ++}; ++ ++static const struct yogafan_config yoga_pro_7_14iah10_cfg = { ++ .multiplier = 100, ++ .fan_count = 1, ++ .paths = { "\\_SB.PC00.LPCB.EC0.FANS", NULL } ++}; ++ + static void apply_rllag_filter(struct yoga_fan_data *data, int idx, long raw_rpm) + { + ktime_t now = ktime_get_boottime(); +@@ -170,6 +189,54 @@ static const struct hwmon_chip_info yoga_fan_chip_info = { + }; + + static const struct dmi_system_id yogafan_quirks[] = { ++ { ++ .ident = "Lenovo LOQ 15IAX9", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_FAMILY, "LOQ 15IAX9"), ++ }, ++ .driver_data = (void *)&loq_15iax9_8bit_dual_cfg, ++ }, ++ { ++ .ident = "Lenovo XiaoXin Pro 13ARE 2020", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_FAMILY, "XiaoXinPro-13ARE 2020"), ++ }, ++ .driver_data = (void *)&xiaoxin_8bit_dual_cfg, ++ }, ++ { ++ .ident = "Lenovo IdeaPad 3 15ALC6", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_FAMILY, "IdeaPad 3 15ALC6"), ++ }, ++ .driver_data = (void *)&ideapad_8bit_fan0_cfg, ++ }, ++ { ++ .ident = "Lenovo Legion Pro 7 16AFR10H", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_FAMILY, "Legion Pro 7 16AFR10H"), ++ }, ++ .driver_data = (void *)&xiaoxin_8bit_dual_cfg, ++ }, ++ { ++ .ident = "Lenovo Yoga Pro 7 14IAH10", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_FAMILY, "Yoga Pro 7 14IAH10"), ++ }, ++ .driver_data = (void *)&yoga_pro_7_14iah10_cfg, ++ }, ++ { ++ .ident = "Lenovo Yoga 7 16ARP8", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_FAMILY, "Yoga 7 16ARP8"), ++ }, ++ .driver_data = (void *)&xiaoxin_8bit_dual_cfg, ++ }, + { + .ident = "Lenovo Yoga", + .matches = { +diff --git a/include/linux/hwmon-sysfs.h b/include/linux/hwmon-sysfs.h +--- a/include/linux/hwmon-sysfs.h ++++ b/include/linux/hwmon-sysfs.h +@@ -15,10 +15,10 @@ struct sensor_device_attribute{ + int index; + }; + #define to_sensor_dev_attr(_dev_attr) \ +- container_of(_dev_attr, struct sensor_device_attribute, dev_attr) ++ container_of_const(_dev_attr, struct sensor_device_attribute, dev_attr) + +-#define SENSOR_ATTR(_name, _mode, _show, _store, _index) \ +- { .dev_attr = __ATTR(_name, _mode, _show, _store), \ ++#define SENSOR_ATTR(_name, _mode, _show, _store, _index) \ ++ { .dev_attr = __DEVICE_ATTR(_name, _mode, _show, _store), \ + .index = _index } + + #define SENSOR_ATTR_RO(_name, _func, _index) \ +@@ -49,11 +49,11 @@ struct sensor_device_attribute_2 { + u8 nr; + }; + #define to_sensor_dev_attr_2(_dev_attr) \ +- container_of(_dev_attr, struct sensor_device_attribute_2, dev_attr) ++ container_of_const(_dev_attr, struct sensor_device_attribute_2, dev_attr) + +-#define SENSOR_ATTR_2(_name, _mode, _show, _store, _nr, _index) \ +- { .dev_attr = __ATTR(_name, _mode, _show, _store), \ +- .index = _index, \ ++#define SENSOR_ATTR_2(_name, _mode, _show, _store, _nr, _index) \ ++ { .dev_attr = __DEVICE_ATTR(_name, _mode, _show, _store), \ ++ .index = _index, \ + .nr = _nr } + + #define SENSOR_ATTR_2_RO(_name, _func, _nr, _index) \ diff --git a/pkgbuilds/linux-omarchy/0740-hwmon-updates.patch.sig b/pkgbuilds/linux-omarchy/0740-hwmon-updates.patch.sig new file mode 100644 index 0000000..9a153ff Binary files /dev/null and b/pkgbuilds/linux-omarchy/0740-hwmon-updates.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0741-hwmon-fixes-1.patch b/pkgbuilds/linux-omarchy/0741-hwmon-fixes-1.patch new file mode 100644 index 0000000..15533e0 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0741-hwmon-fixes-1.patch @@ -0,0 +1,179 @@ +diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c +--- a/drivers/hwmon/applesmc.c ++++ b/drivers/hwmon/applesmc.c +@@ -1481,9 +1481,9 @@ static int __init applesmc_init(void) + applesmc_fan_config[smcreg.fan_count] = 0; + applesmc_pwm_config[smcreg.fan_count] = 0; + +- applesmc_info_temp = kzalloc_obj(*applesmc_info_temp, GFP_KERNEL); +- applesmc_info_fan = kzalloc_obj(*applesmc_info_fan, GFP_KERNEL); +- applesmc_info_pwm = kzalloc_obj(*applesmc_info_pwm, GFP_KERNEL); ++ applesmc_info_temp = kzalloc_obj(*applesmc_info_temp); ++ applesmc_info_fan = kzalloc_obj(*applesmc_info_fan); ++ applesmc_info_pwm = kzalloc_obj(*applesmc_info_pwm); + if (!applesmc_info_temp || !applesmc_info_fan || !applesmc_info_pwm) { + ret = -ENOMEM; + goto out_info; +@@ -1498,7 +1498,7 @@ static int __init applesmc_init(void) + applesmc_info_pwm->type = hwmon_pwm; + applesmc_info_pwm->config = applesmc_pwm_config; + +- applesmc_info_arr = kcalloc(4, sizeof(*applesmc_info_arr), GFP_KERNEL); ++ applesmc_info_arr = kzalloc_objs(*applesmc_info_arr, 4); + if (!applesmc_info_arr) { + ret = -ENOMEM; + goto out_info; +@@ -1509,7 +1509,7 @@ static int __init applesmc_init(void) + applesmc_info_arr[2] = applesmc_info_pwm; + applesmc_info_arr[3] = NULL; + +- applesmc_chip = kzalloc_obj(*applesmc_chip, GFP_KERNEL); ++ applesmc_chip = kzalloc_obj(*applesmc_chip); + if (!applesmc_chip) { + ret = -ENOMEM; + goto out_info; +@@ -1519,10 +1519,9 @@ static int __init applesmc_init(void) + applesmc_chip->info = applesmc_info_arr; + + /* Create non-standard fanX_safe attributes group */ +- fan_safe_attrs = kcalloc(smcreg.fan_count, +- sizeof(*fan_safe_attrs), GFP_KERNEL); +- fan_safe_attr_list = kcalloc(smcreg.fan_count + 1, +- sizeof(*fan_safe_attr_list), GFP_KERNEL); ++ fan_safe_attrs = kzalloc_objs(*fan_safe_attrs, smcreg.fan_count); ++ fan_safe_attr_list = kzalloc_objs(*fan_safe_attr_list, ++ smcreg.fan_count + 1); + if (!fan_safe_attrs || !fan_safe_attr_list) { + ret = -ENOMEM; + goto out_info; +diff --git a/drivers/hwmon/asus_rog_ryujin.c b/drivers/hwmon/asus_rog_ryujin.c +--- a/drivers/hwmon/asus_rog_ryujin.c ++++ b/drivers/hwmon/asus_rog_ryujin.c +@@ -184,6 +184,7 @@ static int rog_ryujin_write_expanded(struct rog_ryujin_data *priv, const u8 *cmd + static int rog_ryujin_execute_cmd(struct rog_ryujin_data *priv, const u8 *cmd, int cmd_length, + struct completion *status_completion) + { ++ unsigned long flags; + int ret; + + /* +@@ -191,9 +192,9 @@ static int rog_ryujin_execute_cmd(struct rog_ryujin_data *priv, const u8 *cmd, i + * completion. Reinit is done because hidraw could have triggered + * the raw event parsing and marked the passed in completion as done. + */ +- spin_lock_bh(&priv->status_report_request_lock); ++ spin_lock_irqsave(&priv->status_report_request_lock, flags); + reinit_completion(status_completion); +- spin_unlock_bh(&priv->status_report_request_lock); ++ spin_unlock_irqrestore(&priv->status_report_request_lock, flags); + + /* Send command for getting data */ + ret = rog_ryujin_write_expanded(priv, cmd, cmd_length); +@@ -421,15 +422,18 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + int size) + { + struct rog_ryujin_data *priv = hid_get_drvdata(hdev); ++ unsigned long flags; + + if (size < 2 || data[0] != RYUJIN_CMD_PREFIX) + return 0; + ++ spin_lock_irqsave(&priv->status_report_request_lock, flags); ++ + if (data[1] == RYUJIN_GET_COOLER_STATUS_CMD_RESPONSE) { + if (size <= priv->info->temp_offset + 1 || + size <= priv->info->pump_speed_offset + 1 || + size <= priv->info->fan_speed_offset + 1) +- return 0; ++ goto unlock; + + /* Received coolant temp and speeds of pump and internal fan */ + priv->temp_input[0] = data[priv->info->temp_offset] * 1000 + +@@ -443,7 +447,7 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + complete_all(&priv->cooler_status_received); + } else if (data[1] == RYUJIN_GET_CONTROLLER_SPEED_CMD_RESPONSE) { + if (size <= RYUJIN_CONTROLLER_SPEED_3 + 1) +- return 0; ++ goto unlock; + + /* Received speeds of four fans attached to the controller */ + priv->speed_input[2] = get_unaligned_le16(data + RYUJIN_CONTROLLER_SPEED_1); +@@ -455,7 +459,7 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + complete_all(&priv->controller_status_received); + } else if (data[1] == RYUJIN_GET_COOLER_DUTY_CMD_RESPONSE) { + if (size <= RYUJIN_INTERNAL_FAN_DUTY) +- return 0; ++ goto unlock; + + /* Received report for pump and internal fan duties (in %) */ + if (data[RYUJIN_PUMP_DUTY] == 0 && data[RYUJIN_INTERNAL_FAN_DUTY] == 0) { +@@ -474,7 +478,7 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + * We're expecting a report, so parse it. + */ + goto read_cooler_duty; +- return 0; ++ goto unlock; + } + read_cooler_duty: + priv->duty_input[0] = rog_ryujin_percent_to_pwm(data[RYUJIN_PUMP_DUTY]); +@@ -484,7 +488,7 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + complete_all(&priv->cooler_duty_received); + } else if (data[1] == RYUJIN_GET_CONTROLLER_DUTY_CMD_RESPONSE) { + if (size <= RYUJIN_CONTROLLER_DUTY) +- return 0; ++ goto unlock; + + /* Received report for controller duty for fans (in PWM) */ + if (data[RYUJIN_CONTROLLER_DUTY] == 0) { +@@ -503,7 +507,7 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + * We're expecting a report, so parse it. + */ + goto read_controller_duty; +- return 0; ++ goto unlock; + } + read_controller_duty: + priv->duty_input[2] = data[RYUJIN_CONTROLLER_DUTY]; +@@ -512,6 +516,8 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo + complete_all(&priv->controller_duty_received); + } + ++unlock: ++ spin_unlock_irqrestore(&priv->status_report_request_lock, flags); + return 0; + } + +diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c +--- a/drivers/hwmon/hwmon.c ++++ b/drivers/hwmon/hwmon.c +@@ -318,6 +318,11 @@ static int hwmon_attr_base(enum hwmon_sensor_types type) + return 1; + } + ++static bool is_hwmon_device(struct device *dev) ++{ ++ return dev->class == &hwmon_class; ++} ++ + #if IS_REACHABLE(CONFIG_I2C) + + /* +@@ -338,7 +343,7 @@ static int hwmon_attr_base(enum hwmon_sensor_types type) + + static int hwmon_match_device(struct device *dev, const void *data) + { +- return dev->class == &hwmon_class; ++ return is_hwmon_device(dev); + } + + static ssize_t pec_show(struct device *dev, const struct device_attribute *dummy, +@@ -781,6 +786,9 @@ int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type, + const char *template; + int base; + ++ if (WARN(!is_hwmon_device(dev), "%s is not a hardware monitoring device\n", ++ dev_name(dev))) ++ return -EINVAL; + if (type >= ARRAY_SIZE(__templates)) + return -EINVAL; + if (attr >= __templates_size[type]) diff --git a/pkgbuilds/linux-omarchy/0741-hwmon-fixes-1.patch.sig b/pkgbuilds/linux-omarchy/0741-hwmon-fixes-1.patch.sig new file mode 100644 index 0000000..11778c9 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0741-hwmon-fixes-1.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0742-hwmon-fixes-2.patch b/pkgbuilds/linux-omarchy/0742-hwmon-fixes-2.patch new file mode 100644 index 0000000..9552822 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0742-hwmon-fixes-2.patch @@ -0,0 +1,12 @@ +diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c +--- a/drivers/hwmon/k10temp.c ++++ b/drivers/hwmon/k10temp.c +@@ -523,7 +523,7 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) + } + } else if (boot_cpu_data.x86 == 0x1a) { + switch (boot_cpu_data.x86_model) { +- case 0x00 ... 0x2f: /* Zen5 Turin */ ++ case 0x00 ... 0x1f: /* Zen5 Turin */ + data->ccd_offset = 0x1F0; + k10temp_get_ccd_support(data, 16); + break; diff --git a/pkgbuilds/linux-omarchy/0742-hwmon-fixes-2.patch.sig b/pkgbuilds/linux-omarchy/0742-hwmon-fixes-2.patch.sig new file mode 100644 index 0000000..db34c4b Binary files /dev/null and b/pkgbuilds/linux-omarchy/0742-hwmon-fixes-2.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0750-applesmc-cache-race.patch b/pkgbuilds/linux-omarchy/0750-applesmc-cache-race.patch deleted file mode 100644 index bd8dd7c..0000000 --- a/pkgbuilds/linux-omarchy/0750-applesmc-cache-race.patch +++ /dev/null @@ -1,31 +0,0 @@ -diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c ---- a/drivers/hwmon/applesmc.c -+++ b/drivers/hwmon/applesmc.c -@@ -33,6 +33,7 @@ - #include - #include - #include -+#include - - /* data port used by Apple SMC */ - #define APPLESMC_DATA_PORT 0x300 -@@ -372,7 +373,8 @@ static const struct applesmc_entry *applesmc_get_entry_by_index(int index) - __be32 be; - int ret = 0; - -- if (cache->valid) -+ /* Pairs with smp_store_release() to ensure cache contents are visible */ -+ if (smp_load_acquire(&cache->valid)) - return cache; - - mutex_lock(&smcreg.mutex); -@@ -391,7 +393,8 @@ static const struct applesmc_entry *applesmc_get_entry_by_index(int index) - cache->len = info[0]; - memcpy(cache->type, &info[1], 4); - cache->flags = info[5]; -- cache->valid = true; -+ /* Pairs with smp_load_acquire() to commit cache contents before setting valid */ -+ smp_store_release(&cache->valid, true); - - out: - mutex_unlock(&smcreg.mutex); diff --git a/pkgbuilds/linux-omarchy/0750-applesmc-cache-race.patch.sig b/pkgbuilds/linux-omarchy/0750-applesmc-cache-race.patch.sig deleted file mode 100644 index d3799b1..0000000 Binary files a/pkgbuilds/linux-omarchy/0750-applesmc-cache-race.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/0751-applesmc-key-backlight-workqueue-leak.patch b/pkgbuilds/linux-omarchy/0751-applesmc-key-backlight-workqueue-leak.patch deleted file mode 100644 index 6eda26b..0000000 --- a/pkgbuilds/linux-omarchy/0751-applesmc-key-backlight-workqueue-leak.patch +++ /dev/null @@ -1,22 +0,0 @@ -diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c ---- a/drivers/hwmon/applesmc.c -+++ b/drivers/hwmon/applesmc.c -@@ -1252,12 +1252,17 @@ static void applesmc_release_light_sensor(void) - - static int applesmc_create_key_backlight(void) - { -+ int ret; -+ - if (!smcreg.has_key_backlight) - return 0; - applesmc_led_wq = create_singlethread_workqueue("applesmc-led"); - if (!applesmc_led_wq) - return -ENOMEM; -- return led_classdev_register(&pdev->dev, &applesmc_backlight); -+ ret = led_classdev_register(&pdev->dev, &applesmc_backlight); -+ if (ret) -+ destroy_workqueue(applesmc_led_wq); -+ return ret; - } - - static void applesmc_release_key_backlight(void) diff --git a/pkgbuilds/linux-omarchy/0751-applesmc-key-backlight-workqueue-leak.patch.sig b/pkgbuilds/linux-omarchy/0751-applesmc-key-backlight-workqueue-leak.patch.sig deleted file mode 100644 index f0da512..0000000 Binary files a/pkgbuilds/linux-omarchy/0751-applesmc-key-backlight-workqueue-leak.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/0800-platform-updates.patch b/pkgbuilds/linux-omarchy/0800-platform-updates.patch index bb6a9de..c6018e3 100644 --- a/pkgbuilds/linux-omarchy/0800-platform-updates.patch +++ b/pkgbuilds/linux-omarchy/0800-platform-updates.patch @@ -364,78 +364,6 @@ diff --git a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.c b/drivers/hid/am pr_warn_once("Undefined laptop placement\n"); } -diff --git a/drivers/platform/arm64/qcom-hamoa-ec.c b/drivers/platform/arm64/qcom-hamoa-ec.c ---- a/drivers/platform/arm64/qcom-hamoa-ec.c -+++ b/drivers/platform/arm64/qcom-hamoa-ec.c -@@ -92,8 +92,10 @@ static int qcom_ec_read(struct qcom_ec *ec, u8 cmd, u8 resp_len, u8 *resp) - return ret; - else if (ret == 0 || ret == 0xff) - return -EOPNOTSUPP; -+ else if (ret != resp_len) -+ return -EIO; - -- if (resp[0] >= resp_len) -+ if (resp[0] != resp_len - 1) - return -EINVAL; - - return 0; -diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c ---- a/drivers/platform/mellanox/mlxbf-bootctl.c -+++ b/drivers/platform/mellanox/mlxbf-bootctl.c -@@ -10,6 +10,7 @@ - - #include - #include -+#include - #include - #include - #include -diff --git a/drivers/platform/mellanox/mlxbf-pmc.c b/drivers/platform/mellanox/mlxbf-pmc.c ---- a/drivers/platform/mellanox/mlxbf-pmc.c -+++ b/drivers/platform/mellanox/mlxbf-pmc.c -@@ -2262,13 +2262,19 @@ static int mlxbf_pmc_map_counters(struct device *dev) - - static int mlxbf_pmc_probe(struct platform_device *pdev) - { -- struct acpi_device *acpi_dev = ACPI_COMPANION(&pdev->dev); -- const char *hid = acpi_device_hid(acpi_dev); - struct device *dev = &pdev->dev; -+ struct acpi_device *acpi_dev; - struct arm_smccc_res res; -+ const char *hid; - guid_t guid; - int ret; - -+ acpi_dev = ACPI_COMPANION(&pdev->dev); -+ if (!acpi_dev) -+ return -ENODEV; -+ -+ hid = acpi_device_hid(acpi_dev); -+ - /* Ensure we have the UUID we expect for this service. */ - arm_smccc_smc(MLXBF_PMC_SIP_SVC_UID, 0, 0, 0, 0, 0, 0, 0, &res); - guid_parse(mlxbf_pmc_svc_uuid_str, &guid); -diff --git a/drivers/platform/surface/surface_acpi_notify.c b/drivers/platform/surface/surface_acpi_notify.c ---- a/drivers/platform/surface/surface_acpi_notify.c -+++ b/drivers/platform/surface/surface_acpi_notify.c -@@ -777,12 +777,16 @@ static int san_consumer_links_setup(struct platform_device *pdev) - - static int san_probe(struct platform_device *pdev) - { -- struct acpi_device *san = ACPI_COMPANION(&pdev->dev); - struct ssam_controller *ctrl; -+ struct acpi_device *san; - struct san_data *data; - acpi_status astatus; - int status; - -+ san = ACPI_COMPANION(&pdev->dev); -+ if (!san) -+ return -ENODEV; -+ - ctrl = ssam_client_bind(&pdev->dev); - if (IS_ERR(ctrl)) - return PTR_ERR(ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ctrl); diff --git a/drivers/platform/surface/surface_aggregator_hub.c b/drivers/platform/surface/surface_aggregator_hub.c --- a/drivers/platform/surface/surface_aggregator_hub.c +++ b/drivers/platform/surface/surface_aggregator_hub.c @@ -520,20 +448,6 @@ diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig depends on INPUT select INPUT_SPARSEKMAP select NEW_LEDS -diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c ---- a/drivers/platform/x86/acer-wmi.c -+++ b/drivers/platform/x86/acer-wmi.c -@@ -1581,7 +1581,9 @@ static int WMI_gaming_execute_u32_u64(u32 method_id, u32 in, u64 *out) - return -EIO; - - obj = result.pointer; -- if (obj && out) { -+ if (!obj && out) { -+ ret = -ENOMSG; -+ } else if (obj && out) { - switch (obj->type) { - case ACPI_TYPE_INTEGER: - *out = obj->integer.value; diff --git a/drivers/platform/x86/amd/Kconfig b/drivers/platform/x86/amd/Kconfig --- a/drivers/platform/x86/amd/Kconfig +++ b/drivers/platform/x86/amd/Kconfig @@ -1795,19 +1709,6 @@ diff --git a/drivers/platform/x86/amd/hsmp/hsmp.h b/drivers/platform/x86/amd/hsm + */ +extern struct rw_semaphore hsmp_sock_rwsem; #endif /* HSMP_H */ -diff --git a/drivers/platform/x86/amd/hsmp/hwmon.c b/drivers/platform/x86/amd/hsmp/hwmon.c ---- a/drivers/platform/x86/amd/hsmp/hwmon.c -+++ b/drivers/platform/x86/amd/hsmp/hwmon.c -@@ -31,6 +31,9 @@ static int hsmp_hwmon_write(struct device *dev, enum hwmon_sensor_types type, - if (attr != hwmon_power_cap) - return -EOPNOTSUPP; - -+ if (val < 0) -+ return -EINVAL; -+ - msg.num_args = 1; - msg.args[0] = val / MICROWATT_PER_MILLIWATT; - msg.msg_id = HSMP_SET_SOCKET_POWER_LIMIT; diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/x86/amd/hsmp/plat.c --- a/drivers/platform/x86/amd/hsmp/plat.c +++ b/drivers/platform/x86/amd/hsmp/plat.c @@ -3763,41 +3664,6 @@ diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-w { KE_KEY, 0x8C, { KEY_SWITCHVIDEOMODE } }, /* SDSP DVI only */ { KE_KEY, 0x8D, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + DVI */ { KE_KEY, 0x8E, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + DVI */ -diff --git a/drivers/platform/x86/asus-wireless.c b/drivers/platform/x86/asus-wireless.c ---- a/drivers/platform/x86/asus-wireless.c -+++ b/drivers/platform/x86/asus-wireless.c -@@ -132,6 +132,10 @@ static int asus_wireless_probe(struct platform_device *pdev) - const struct acpi_device_id *id; - int err; - -+ id = acpi_match_acpi_device(device_ids, adev); -+ if (!id) -+ return -ENODEV; -+ - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; -@@ -139,6 +143,7 @@ static int asus_wireless_probe(struct platform_device *pdev) - platform_set_drvdata(pdev, data); - - data->adev = adev; -+ data->hswc_params = (const struct hswc_params *)id->driver_data; - - data->idev = devm_input_allocate_device(&pdev->dev); - if (!data->idev) -@@ -153,12 +158,6 @@ static int asus_wireless_probe(struct platform_device *pdev) - if (err) - return err; - -- id = acpi_match_acpi_device(device_ids, adev); -- if (!id) -- return 0; -- -- data->hswc_params = (const struct hswc_params *)id->driver_data; -- - data->wq = create_singlethread_workqueue("asus_wireless_workqueue"); - if (!data->wq) - return -ENOMEM; diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -3832,30 +3698,6 @@ diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c /* Throttle thermal policy ****************************************************/ static int throttle_thermal_policy_write(struct asus_wmi *asus) { -@@ -5244,20 +5268,20 @@ static int asus_wmi_add(struct platform_device *pdev) - return 0; - - fail_wmi_handler: -+ asus_screenpad_exit(asus); -+fail_screenpad: - asus_wmi_backlight_exit(asus); - fail_backlight: - asus_wmi_rfkill_exit(asus); --fail_screenpad: -- asus_screenpad_exit(asus); - fail_rfkill: - asus_wmi_led_exit(asus); - fail_leds: -+fail_custom_fan_curve: - fail_hwmon: - asus_wmi_input_exit(asus); - fail_input: - asus_wmi_sysfs_exit(asus->platform_device); - fail_sysfs: --fail_custom_fan_curve: - fail_platform_profile_setup: - fail_fan_boost_mode: - fail_platform: diff --git a/drivers/platform/x86/dell/dell-smbios-base.c b/drivers/platform/x86/dell/dell-smbios-base.c --- a/drivers/platform/x86/dell/dell-smbios-base.c +++ b/drivers/platform/x86/dell/dell-smbios-base.c @@ -4164,43 +4006,6 @@ diff --git a/drivers/platform/x86/dell/dell-smbios.h b/drivers/platform/x86/dell void dell_smbios_unregister_device(struct device *d); int dell_smbios_error(int value); -diff --git a/drivers/platform/x86/dell/dell-wmi-base.c b/drivers/platform/x86/dell/dell-wmi-base.c ---- a/drivers/platform/x86/dell/dell-wmi-base.c -+++ b/drivers/platform/x86/dell/dell-wmi-base.c -@@ -456,7 +456,7 @@ static int dell_wmi_process_key(struct wmi_device *wdev, int type, int code, __l - key++; - used = 1; - } else if (type == 0x0012 && code == 0x000d && remaining > 0) { -- value = (le16_to_cpu(buffer[2]) == 2); -+ value = (le16_to_cpu(buffer[0]) == 2); - used = 1; - } - -@@ -843,9 +843,22 @@ static int __init dell_wmi_init(void) - - err = dell_privacy_register_driver(); - if (err) -- return err; -+ goto out_smbios; - -- return wmi_driver_register(&dell_wmi_driver); -+ err = wmi_driver_register(&dell_wmi_driver); -+ if (err) -+ goto out_privacy; -+ -+ return 0; -+ -+out_privacy: -+ dell_privacy_unregister_driver(); -+ -+out_smbios: -+ if (wmi_requires_smbios_request) -+ dell_wmi_events_set_enabled(false); -+ -+ return err; - } - late_initcall(dell_wmi_init); - diff --git a/drivers/platform/x86/dell/dell-wmi-ddv.c b/drivers/platform/x86/dell/dell-wmi-ddv.c --- a/drivers/platform/x86/dell/dell-wmi-ddv.c +++ b/drivers/platform/x86/dell/dell-wmi-ddv.c @@ -4251,35 +4056,6 @@ diff --git a/drivers/platform/x86/dell/dell-wmi-ddv.c b/drivers/platform/x86/del } static ssize_t dell_wmi_ddv_query_string(struct wmi_device *wdev, enum dell_ddv_method method, -diff --git a/drivers/platform/x86/dell/dell-wmi-privacy.c b/drivers/platform/x86/dell/dell-wmi-privacy.c ---- a/drivers/platform/x86/dell/dell-wmi-privacy.c -+++ b/drivers/platform/x86/dell/dell-wmi-privacy.c -@@ -92,11 +92,11 @@ bool dell_privacy_has_mic_mute(void) - { - struct privacy_wmi_data *priv; - -- mutex_lock(&list_mutex); -+ guard(mutex)(&list_mutex); -+ - priv = list_first_entry_or_null(&wmi_list, - struct privacy_wmi_data, - list); -- mutex_unlock(&list_mutex); - - return priv && (priv->features_present & BIT(DELL_PRIVACY_TYPE_AUDIO)); - } -diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h b/drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h ---- a/drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h -+++ b/drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h -@@ -107,7 +107,7 @@ enum { - static int get_##type##_instance_id(struct kobject *kobj) \ - { \ - int i; \ -- for (i = 0; i <= wmi_priv.type##_instances_count; i++) { \ -+ for (i = 0; i < wmi_priv.type##_instances_count; i++) { \ - if (!(strcmp(kobj->name, wmi_priv.type##_data[i].attribute_name)))\ - return i; \ - } \ diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -4415,21 +4191,6 @@ diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujits if (error) return error; -diff --git a/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c ---- a/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c -+++ b/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c -@@ -353,6 +353,11 @@ static int hp_populate_password_elements_from_package(union acpi_object *passwor - case PSWD_ENCODINGS: - size = min_t(u32, password_data->encodings_size, MAX_ENCODINGS_SIZE); - for (pos_values = 0; pos_values < size; pos_values++) { -+ if (elem + pos_values >= password_obj_count) { -+ pr_err("Error elem-objects package is too small\n"); -+ return -EINVAL; -+ } -+ - ret = hp_convert_hexstr_to_str(password_obj[elem + pos_values].string.pointer, - password_obj[elem + pos_values].string.length, - &str_value, &value_len); diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -4463,22 +4224,17 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c #define zero_if_sup(tmp) (zero_insize_support?0:sizeof(tmp)) // use when zero insize is required -@@ -133,11 +142,52 @@ static const struct thermal_profile_params omen_v1_no_ec_thermal_params = { +@@ -133,24 +142,41 @@ static const struct thermal_profile_params omen_v1_no_ec_thermal_params = { .ec_tp_offset = HP_NO_THERMAL_PROFILE_OFFSET, }; --/* -- * A generic pointer for the currently-active board's thermal profile -- * parameters. -- */ --static struct thermal_profile_params *active_thermal_profile_params; +struct hp_wmi_fan_profile_params { + int (*get_fan_speed)(int fan); + bool fan_table; +}; + -+struct hp_wmi_board_params { -+ const struct thermal_profile_params *thermal_profile; + struct hp_wmi_board_params { + const struct thermal_profile_params *thermal_profile; + const struct hp_wmi_fan_profile_params *fan_profile; +}; + @@ -4487,41 +4243,30 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c +static const struct hp_wmi_fan_profile_params victus_s_fan_profile_params = { + .get_fan_speed = hp_wmi_get_fan_speed_victus_s, + .fan_table = true, -+}; -+ -+static const struct hp_wmi_board_params victus_s_board_params = { -+ .thermal_profile = &victus_s_thermal_params, -+ .fan_profile = &victus_s_fan_profile_params, -+}; -+ -+static const struct hp_wmi_board_params omen_v1_board_params = { -+ .thermal_profile = &omen_v1_thermal_params, -+ .fan_profile = &victus_s_fan_profile_params, -+}; -+ -+static const struct hp_wmi_board_params omen_v1_legacy_board_params = { -+ .thermal_profile = &omen_v1_legacy_thermal_params, -+ .fan_profile = &victus_s_fan_profile_params, -+}; -+ -+static const struct hp_wmi_board_params omen_v1_no_ec_board_params = { -+ .thermal_profile = &omen_v1_no_ec_thermal_params, -+ .fan_profile = &victus_s_fan_profile_params, -+}; -+ -+static const struct hp_wmi_board_params *active_board_params; -+ -+static const struct thermal_profile_params *hp_wmi_thermal_profile(void) -+{ -+ if (!active_board_params) -+ return NULL; -+ -+ return active_board_params->thermal_profile; -+} + }; - /* DMI board names of devices that should use the omen specific path for - * thermal profiles. -@@ -157,7 +207,7 @@ static const char * const omen_thermal_profile_boards[] = { + static const struct hp_wmi_board_params victus_s_board_params = { + .thermal_profile = &victus_s_thermal_params, ++ .fan_profile = &victus_s_fan_profile_params, + }; + + static const struct hp_wmi_board_params omen_v1_board_params = { + .thermal_profile = &omen_v1_thermal_params, ++ .fan_profile = &victus_s_fan_profile_params, + }; + + static const struct hp_wmi_board_params omen_v1_legacy_board_params = { + .thermal_profile = &omen_v1_legacy_thermal_params, ++ .fan_profile = &victus_s_fan_profile_params, + }; + + static const struct hp_wmi_board_params omen_v1_no_ec_board_params = { + .thermal_profile = &omen_v1_no_ec_thermal_params, ++ .fan_profile = &victus_s_fan_profile_params, + }; + + static const struct hp_wmi_board_params *active_board_params; +@@ -181,7 +207,7 @@ static const char * const omen_thermal_profile_boards[] = { "886B", "886C", "88C8", "88CB", "88D1", "88D2", "88F4", "88F5", "88F6", "88F7", "88FD", "88FE", "88FF", "8900", "8901", "8902", "8912", "8917", "8918", "8949", "894A", "89EB", @@ -4530,33 +4275,21 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c "8BAD", "8C58", "8E41", -@@ -187,83 +237,103 @@ static const char * const victus_thermal_profile_boards[] = { - "8A25", - }; - --/* DMI Board names of Victus 16-r and Victus 16-s laptops */ --static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst = { -+/* DMI board-specific feature data for Omen and Victus laptops. */ -+static const struct dmi_system_id hp_wmi_feature_boards[] __initconst = { - { +@@ -217,6 +243,10 @@ static const struct dmi_system_id hp_wmi_feature_boards[] __initconst = { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8902") }, -- .driver_data = (void *)&omen_v1_legacy_thermal_params, -+ .driver_data = (void *)&omen_v1_legacy_board_params, -+ }, + .driver_data = (void *)&omen_v1_legacy_board_params, + }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8A3D") }, + .driver_data = (void *)&victus_s_board_params, - }, ++ }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8A44") }, -- .driver_data = (void *)&omen_v1_legacy_thermal_params, -+ .driver_data = (void *)&omen_v1_legacy_board_params, - }, - { + .driver_data = (void *)&omen_v1_legacy_board_params, +@@ -225,6 +255,14 @@ static const struct dmi_system_id hp_wmi_feature_boards[] __initconst = { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8A4D") }, -- .driver_data = (void *)&omen_v1_legacy_thermal_params, -+ .driver_data = (void *)&omen_v1_legacy_board_params, -+ }, + .driver_data = (void *)&omen_v1_legacy_board_params, + }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BAA") }, + .driver_data = (void *)&omen_v1_board_params, @@ -4564,107 +4297,33 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BA9") }, + .driver_data = (void *)&omen_v1_board_params, - }, ++ }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BAB") }, -- .driver_data = (void *)&omen_v1_thermal_params, -+ .driver_data = (void *)&omen_v1_board_params, - }, - { + .driver_data = (void *)&omen_v1_board_params, +@@ -233,6 +271,10 @@ static const struct dmi_system_id hp_wmi_feature_boards[] __initconst = { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8B2F") }, -- .driver_data = (void *)&victus_s_thermal_params, -+ .driver_data = (void *)&victus_s_board_params, -+ }, + .driver_data = (void *)&victus_s_board_params, + }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BB3") }, + .driver_data = (void *)&omen_v1_no_ec_board_params, - }, ++ }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BBE") }, -- .driver_data = (void *)&victus_s_thermal_params, -+ .driver_data = (void *)&victus_s_board_params, - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BC2") }, -- .driver_data = (void *)&omen_v1_thermal_params, -+ .driver_data = (void *)&omen_v1_board_params, - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BCA") }, -- .driver_data = (void *)&omen_v1_thermal_params, -+ .driver_data = (void *)&omen_v1_board_params, - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BCD") }, -- .driver_data = (void *)&omen_v1_thermal_params, -+ .driver_data = (void *)&omen_v1_board_params, - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BD4") }, -- .driver_data = (void *)&victus_s_thermal_params, -+ .driver_data = (void *)&victus_s_board_params, - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BD5") }, -- .driver_data = (void *)&victus_s_thermal_params, -+ .driver_data = (void *)&victus_s_board_params, - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8C76") }, -- .driver_data = (void *)&omen_v1_thermal_params, -+ .driver_data = (void *)&omen_v1_board_params, - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8C77") }, -- .driver_data = (void *)&omen_v1_thermal_params, -+ .driver_data = (void *)&omen_v1_board_params, - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8C78") }, -- .driver_data = (void *)&omen_v1_thermal_params, -+ .driver_data = (void *)&omen_v1_board_params, - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8C99") }, -- .driver_data = (void *)&victus_s_thermal_params, -+ .driver_data = (void *)&victus_s_board_params, - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8C9C") }, -- .driver_data = (void *)&victus_s_thermal_params, -+ .driver_data = (void *)&victus_s_board_params, - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8D26") }, -- .driver_data = (void *)&omen_v1_legacy_thermal_params, -+ .driver_data = (void *)&omen_v1_legacy_board_params, - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8D41") }, -- .driver_data = (void *)&omen_v1_no_ec_thermal_params, -+ .driver_data = (void *)&omen_v1_no_ec_board_params, - }, - { + .driver_data = (void *)&victus_s_board_params, +@@ -289,6 +331,10 @@ static const struct dmi_system_id hp_wmi_feature_boards[] __initconst = { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8D87") }, -- .driver_data = (void *)&omen_v1_no_ec_thermal_params, -+ .driver_data = (void *)&omen_v1_no_ec_board_params, -+ }, + .driver_data = (void *)&omen_v1_no_ec_board_params, + }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8D88") }, + .driver_data = (void *)&omen_v1_no_ec_board_params, - }, ++ }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8DD6") }, -@@ -271,7 +341,7 @@ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst - }, - { - .matches = { DMI_MATCH(DMI_BOARD_NAME, "8E35") }, -- .driver_data = (void *)&omen_v1_legacy_thermal_params, -+ .driver_data = (void *)&omen_v1_legacy_board_params, - }, - {}, - }; -@@ -336,6 +406,7 @@ enum hp_wmi_commandtype { + .driver_data = (void *)&omen_v1_no_ec_board_params, +@@ -360,6 +406,7 @@ enum hp_wmi_commandtype { HPWMI_POSTCODEERROR_QUERY = 0x2a, HPWMI_SYSTEM_DEVICE_MODE = 0x40, HPWMI_THERMAL_PROFILE_QUERY = 0x4c, @@ -4672,7 +4331,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c }; struct victus_power_limits { -@@ -504,9 +575,9 @@ struct hp_wmi_hwmon_priv { +@@ -528,9 +575,9 @@ struct hp_wmi_hwmon_priv { struct mutex lock; /* protects mode, pwm */ u8 min_rpm; u8 max_rpm; @@ -4684,7 +4343,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c struct delayed_work keep_alive_dwork; }; -@@ -833,24 +904,20 @@ static int hp_wmi_fan_speed_max_set(int enabled) +@@ -857,24 +904,20 @@ static int hp_wmi_fan_speed_max_set(int enabled) return enabled; } @@ -4719,7 +4378,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c ret = hp_wmi_get_fan_count_userdefine_trigger(); if (ret < 0) -@@ -867,7 +934,9 @@ static int hp_wmi_fan_speed_set(struct hp_wmi_hwmon_priv *priv, u8 speed) +@@ -891,7 +934,9 @@ static int hp_wmi_fan_speed_set(struct hp_wmi_hwmon_priv *priv, u8 speed) static int hp_wmi_fan_speed_reset(struct hp_wmi_hwmon_priv *priv) { @@ -4730,7 +4389,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c } static int hp_wmi_fan_speed_max_reset(struct hp_wmi_hwmon_priv *priv) -@@ -1128,12 +1197,134 @@ static int camera_shutter_input_setup(void) +@@ -1152,12 +1197,134 @@ static int camera_shutter_input_setup(void) return err; } @@ -4865,7 +4524,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c static struct attribute *hp_wmi_attrs[] = { &dev_attr_display.attr, -@@ -1142,6 +1333,7 @@ static struct attribute *hp_wmi_attrs[] = { +@@ -1166,6 +1333,7 @@ static struct attribute *hp_wmi_attrs[] = { &dev_attr_dock.attr, &dev_attr_tablet.attr, &dev_attr_postcode.attr, @@ -4873,7 +4532,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c NULL, }; ATTRIBUTE_GROUPS(hp_wmi); -@@ -1793,6 +1985,38 @@ static bool is_victus_s_thermal_profile(void) +@@ -1817,6 +1985,38 @@ static bool is_victus_s_thermal_profile(void) return is_victus_s_board; } @@ -4912,77 +4571,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c static int victus_s_gpu_thermal_profile_get(bool *ctgp_enable, bool *ppab_enable, u8 *dstate, -@@ -1878,7 +2102,10 @@ static int platform_profile_victus_s_get_ec(enum platform_profile_option *profil - u8 current_dstate, current_gpu_slowdown_temp, tp; - const struct thermal_profile_params *params; - -- params = active_thermal_profile_params; -+ params = hp_wmi_thermal_profile(); -+ if (!params) -+ return -ENODEV; -+ - if (params->ec_tp_offset == HP_EC_OFFSET_UNKNOWN || - params->ec_tp_offset == HP_NO_THERMAL_PROFILE_OFFSET) { - *profile = active_platform_profile; -@@ -1890,10 +2117,10 @@ static int platform_profile_victus_s_get_ec(enum platform_profile_option *profil - return ret; - - /* -- * We cannot use active_thermal_profile_params here, because boards -- * like 8C78 have tp == 0x0 || tp == 0x1 after cold boot, but logically -- * it should have tp == 0x30 || tp == 0x31, as corrected by the Omen -- * Gaming Hub on windows. Hence accept both of these values. -+ * Boards like 8C78 have tp == 0x0 || tp == 0x1 after cold boot, -+ * but logically it should have tp == 0x30 || tp == 0x31, as -+ * corrected by the Omen Gaming Hub on windows. Hence accept both -+ * of these values. - */ - if (tp == victus_s_thermal_params.performance || - tp == omen_v1_thermal_params.performance) { -@@ -1928,12 +2155,12 @@ static int platform_profile_victus_s_get_ec(enum platform_profile_option *profil - - static int platform_profile_victus_s_set_ec(enum platform_profile_option profile) - { -- struct thermal_profile_params *params; -+ const struct thermal_profile_params *params; - bool gpu_ctgp_enable, gpu_ppab_enable; - u8 gpu_dstate; /* Test shows 1 = 100%, 2 = 50%, 3 = 25%, 4 = 12.5% */ - int err, tp; - -- params = active_thermal_profile_params; -+ params = hp_wmi_thermal_profile(); - if (!params) - return -ENODEV; - -@@ -2199,6 +2426,7 @@ static const struct platform_profile_ops hp_wmi_platform_profile_ops = { - static int thermal_profile_setup(struct platform_device *device) - { - const struct platform_profile_ops *ops; -+ const struct thermal_profile_params *params; - int err, tp; - - if (is_omen_thermal_profile()) { -@@ -2230,13 +2458,17 @@ static int thermal_profile_setup(struct platform_device *device) - - ops = &platform_profile_victus_ops; - } else if (is_victus_s_thermal_profile()) { -+ params = hp_wmi_thermal_profile(); -+ if (!params) -+ return -ENODEV; -+ - /* - * For an unknown EC layout board, platform_profile_victus_s_get_ec(), - * behaves like a wrapper around active_platform_profile, to avoid using - * uninitialized data, we default to PLATFORM_PROFILE_BALANCED. - */ -- if (active_thermal_profile_params->ec_tp_offset == HP_EC_OFFSET_UNKNOWN || -- active_thermal_profile_params->ec_tp_offset == HP_NO_THERMAL_PROFILE_OFFSET) { -+ if (params->ec_tp_offset == HP_EC_OFFSET_UNKNOWN || -+ params->ec_tp_offset == HP_NO_THERMAL_PROFILE_OFFSET) { - active_platform_profile = PLATFORM_PROFILE_BALANCED; - } else { - err = platform_profile_victus_s_get_ec(&active_platform_profile); -@@ -2404,7 +2636,7 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv) +@@ -2436,7 +2636,7 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv) switch (priv->mode) { case PWM_MODE_MAX: @@ -4991,7 +4580,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c ret = hp_wmi_get_fan_count_userdefine_trigger(); if (ret < 0) return ret; -@@ -2416,16 +2648,16 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv) +@@ -2448,16 +2648,16 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv) secs_to_jiffies(KEEP_ALIVE_DELAY_SECS)); return 0; case PWM_MODE_MANUAL: @@ -5011,7 +4600,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c ret = hp_wmi_get_fan_count_userdefine_trigger(); if (ret < 0) return ret; -@@ -2449,12 +2681,12 @@ static umode_t hp_wmi_hwmon_is_visible(const void *data, +@@ -2481,12 +2681,12 @@ static umode_t hp_wmi_hwmon_is_visible(const void *data, { switch (type) { case hwmon_pwm: @@ -5027,7 +4616,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c return 0444; } else { if (hp_wmi_get_fan_speed(channel) >= 0) -@@ -2478,8 +2710,8 @@ static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, +@@ -2510,8 +2710,8 @@ static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, priv = dev_get_drvdata(dev); switch (type) { case hwmon_fan: @@ -5038,7 +4627,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c else ret = hp_wmi_get_fan_speed(channel); if (ret < 0) -@@ -2488,10 +2720,10 @@ static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, +@@ -2520,10 +2720,10 @@ static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, return 0; case hwmon_pwm: if (attr == hwmon_pwm_input) { @@ -5051,7 +4640,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c if (rpm < 0) return rpm; *val = rpm_to_pwm(rpm / 100, priv); -@@ -2518,14 +2750,15 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, +@@ -2550,14 +2750,15 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { struct hp_wmi_hwmon_priv *priv; @@ -5069,7 +4658,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c return -EOPNOTSUPP; /* PWM input is invalid when not in manual mode */ if (priv->mode != PWM_MODE_MANUAL) -@@ -2534,7 +2767,10 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, +@@ -2566,7 +2767,10 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, /* ensure PWM input is within valid fan speeds */ rpm = pwm_to_rpm(val, priv); rpm = clamp_val(rpm, priv->min_rpm, priv->max_rpm); @@ -5081,7 +4670,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c return hp_wmi_apply_fan_settings(priv); } switch (val) { -@@ -2542,16 +2778,20 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, +@@ -2574,16 +2778,20 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, priv->mode = PWM_MODE_MAX; return hp_wmi_apply_fan_settings(priv); case PWM_MODE_MANUAL: @@ -5107,7 +4696,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c priv->mode = PWM_MODE_MANUAL; return hp_wmi_apply_fan_settings(priv); case PWM_MODE_AUTO: -@@ -2567,7 +2807,7 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, +@@ -2599,7 +2807,7 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, static const struct hwmon_channel_info * const info[] = { HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT), @@ -5116,7 +4705,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c NULL }; -@@ -2608,14 +2848,14 @@ static int hp_wmi_setup_fan_settings(struct hp_wmi_hwmon_priv *priv) +@@ -2640,14 +2848,14 @@ static int hp_wmi_setup_fan_settings(struct hp_wmi_hwmon_priv *priv) struct victus_s_fan_table *fan_table; u8 min_rpm, max_rpm; u8 cpu_rpm, gpu_rpm, noise_db; @@ -5134,7 +4723,7 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c return 0; ret = hp_wmi_perform_query(HPWMI_VICTUS_S_GET_FAN_TABLE_QUERY, -@@ -2654,10 +2894,8 @@ static int hp_wmi_setup_fan_settings(struct hp_wmi_hwmon_priv *priv) +@@ -2686,10 +2894,8 @@ static int hp_wmi_setup_fan_settings(struct hp_wmi_hwmon_priv *priv) if (min_rpm == U8_MAX || max_rpm == 0) return -EINVAL; @@ -5145,54 +4734,6 @@ diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c return 0; } -@@ -2697,24 +2935,25 @@ static int hp_wmi_hwmon_init(void) - return 0; - } - --static void __init setup_active_thermal_profile_params(void) -+static void __init setup_active_board_params(void) - { - const struct dmi_system_id *id; -+ const struct thermal_profile_params *params; - -- /* -- * Currently only victus_s devices use the -- * active_thermal_profile_params -- */ -- id = dmi_first_match(victus_s_thermal_profile_boards); -+ id = dmi_first_match(hp_wmi_feature_boards); - if (id) { -+ active_board_params = id->driver_data; -+ params = hp_wmi_thermal_profile(); -+ if (!params) -+ return; -+ - /* - * Marking this boolean is required to ensure that - * is_victus_s_thermal_profile() behaves like a valid - * wrapper. - */ - is_victus_s_board = true; -- active_thermal_profile_params = id->driver_data; -- if (active_thermal_profile_params->ec_tp_offset == HP_EC_OFFSET_UNKNOWN) { -+ if (params->ec_tp_offset == HP_EC_OFFSET_UNKNOWN) { - pr_warn("Unknown EC layout for board %s. Thermal profile readback will be disabled. Please report this to platform-driver-x86@vger.kernel.org\n", - dmi_get_system_info(DMI_BOARD_NAME)); - } -@@ -2749,10 +2988,10 @@ static int __init hp_wmi_init(void) - } - - /* -- * Setup active board's thermal profile parameters before -- * starting platform driver probe. -+ * Setup active board feature data before starting platform -+ * driver probe. - */ -- setup_active_thermal_profile_params(); -+ setup_active_board_params(); - err = platform_driver_probe(&hp_wmi_driver, hp_wmi_bios_setup); - if (err) - goto err_unregister_device; diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c --- a/drivers/platform/x86/huawei-wmi.c +++ b/drivers/platform/x86/huawei-wmi.c @@ -6042,7 +5583,7 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c #define SB_GGOV_METHOD "\\_SB.GGOV" #define GOV_TLED 0x2020008 -@@ -78,42 +88,95 @@ MODULE_PARM_DESC(fw_debug, "Enable printing of firmware debug messages"); +@@ -78,26 +88,29 @@ MODULE_PARM_DESC(fw_debug, "Enable printing of firmware debug messages"); #define WMBB_USB_CHARGE 0x10B #define WMBB_BATT_LIMIT 0x10C @@ -6084,8 +5625,8 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c -#define INIT_SPARSE_KEYMAP 0x80 static int battery_limit_use_wmbb; -+static bool kbd_backlight_available; - static struct led_classdev kbd_backlight; + static bool kbd_backlight_available; +@@ -105,16 +118,65 @@ static struct led_classdev kbd_backlight; static enum led_brightness get_kbd_backlight_level(struct device *dev); static const struct key_entry wmi_keymap[] = { @@ -6158,7 +5699,7 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c static int ggov(u32 arg0) { union acpi_object args[1]; -@@ -154,30 +217,97 @@ static int ggov(u32 arg0) +@@ -155,30 +217,97 @@ static int ggov(u32 arg0) return res; } @@ -6273,13 +5814,14 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c } static union acpi_object *lg_wmbb(struct device *dev, u32 method_id, u32 arg1, u32 arg2) -@@ -211,72 +341,11 @@ static union acpi_object *lg_wmbb(struct device *dev, u32 method_id, u32 arg1, u +@@ -212,76 +341,11 @@ static union acpi_object *lg_wmbb(struct device *dev, u32 method_id, u32 arg1, u return (union acpi_object *)buffer.pointer; } -static void wmi_notify(union acpi_object *obj, void *context) -{ - long data = (long)context; +- unsigned int brightness; - - pr_debug("event guid %li\n", data); - if (!obj) @@ -6290,8 +5832,11 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c - struct key_entry *key; - - if (eventcode == 0x10000000) { -- led_classdev_notify_brightness_hw_changed( -- &kbd_backlight, get_kbd_backlight_level(kbd_backlight.dev->parent)); +- if (kbd_backlight_available) { +- brightness = get_kbd_backlight_level(kbd_backlight.dev->parent); +- led_classdev_notify_brightness_hw_changed(&kbd_backlight, +- brightness); +- } - } else { - key = sparse_keymap_entry_from_scancode( - wmi_input_dev, eventcode); @@ -6346,7 +5891,7 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c int ret; ret = kstrtoul(buffer, 10, &value); -@@ -285,10 +354,10 @@ static ssize_t fan_mode_store(struct device *dev, +@@ -290,10 +354,10 @@ static ssize_t fan_mode_store(struct device *dev, if (value >= 3) return -EINVAL; @@ -6361,7 +5906,7 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c return count; } -@@ -296,22 +365,14 @@ static ssize_t fan_mode_store(struct device *dev, +@@ -301,22 +365,14 @@ static ssize_t fan_mode_store(struct device *dev, static ssize_t fan_mode_show(struct device *dev, struct device_attribute *attr, char *buffer) { @@ -6390,7 +5935,7 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c } static ssize_t usb_charge_store(struct device *dev, -@@ -361,41 +422,30 @@ static ssize_t reader_mode_store(struct device *dev, +@@ -366,41 +422,30 @@ static ssize_t reader_mode_store(struct device *dev, const char *buffer, size_t count) { bool value; @@ -6441,7 +5986,7 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c } static ssize_t fn_lock_store(struct device *dev, -@@ -403,40 +453,30 @@ static ssize_t fn_lock_store(struct device *dev, +@@ -408,40 +453,30 @@ static ssize_t fn_lock_store(struct device *dev, const char *buffer, size_t count) { bool value; @@ -6491,7 +6036,7 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c } static ssize_t charge_control_end_threshold_store(struct device *dev, -@@ -453,14 +493,18 @@ static ssize_t charge_control_end_threshold_store(struct device *dev, +@@ -458,14 +493,18 @@ static ssize_t charge_control_end_threshold_store(struct device *dev, if (value == 100 || value == 80) { union acpi_object *r; @@ -6516,7 +6061,7 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c return count; } -@@ -471,8 +515,9 @@ static ssize_t charge_control_end_threshold_show(struct device *device, +@@ -476,8 +515,9 @@ static ssize_t charge_control_end_threshold_show(struct device *device, struct device_attribute *attr, char *buf) { @@ -6527,7 +6072,7 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c if (battery_limit_use_wmbb) { r = lg_wmbb(&pf_device->dev, WMBB_BATT_LIMIT, WM_GET, 0); -@@ -485,19 +530,13 @@ static ssize_t charge_control_end_threshold_show(struct device *device, +@@ -490,19 +530,13 @@ static ssize_t charge_control_end_threshold_show(struct device *device, } status = r->buffer.pointer[0x10]; @@ -6552,7 +6097,7 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c if (status != 80 && status != 100) status = 0; -@@ -563,10 +602,7 @@ static const struct attribute_group dev_attribute_group = { +@@ -568,10 +602,7 @@ static const struct attribute_group dev_attribute_group = { static void tpad_led_set(struct led_classdev *cdev, enum led_brightness brightness) { @@ -6564,7 +6109,7 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c } static enum led_brightness tpad_led_get(struct led_classdev *cdev) -@@ -579,47 +615,50 @@ static LED_DEVICE(tpad_led, 1, 0); +@@ -584,47 +615,50 @@ static LED_DEVICE(tpad_led, 1, 0); static void kbd_backlight_set(struct led_classdev *cdev, enum led_brightness brightness) { @@ -6644,7 +6189,7 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c } static enum led_brightness kbd_backlight_get(struct led_classdev *cdev) -@@ -629,26 +668,163 @@ static enum led_brightness kbd_backlight_get(struct led_classdev *cdev) +@@ -634,26 +668,163 @@ static enum led_brightness kbd_backlight_get(struct led_classdev *cdev) static LED_DEVICE(kbd_backlight, 255, LED_BRIGHT_HW_CHANGED); @@ -6822,42 +6367,36 @@ diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c static acpi_status lg_laptop_address_space_write(struct device *dev, acpi_physical_address address, size_t size, u64 value) { -@@ -860,15 +1036,23 @@ static int acpi_probe(struct platform_device *pdev) +@@ -865,10 +1036,6 @@ static int acpi_probe(struct platform_device *pdev) if (year >= 2019) battery_limit_use_wmbb = 1; -+ /* LEDs are optional */ -+ ret = devm_led_classdev_register(&pdev->dev, &kbd_backlight); -+ if (ret < 0) -+ kbd_backlight_available = false; -+ else -+ kbd_backlight_available = true; -+ -+ devm_led_classdev_register(&pdev->dev, &tpad_led); -+ +- ret = sysfs_create_group(&pf_device->dev.kobj, &dev_attribute_group); +- if (ret) +- goto out_platform_device; +- + /* LEDs are optional */ + ret = devm_led_classdev_register(&pdev->dev, &kbd_backlight); + if (ret < 0) +@@ -878,7 +1045,14 @@ static int acpi_probe(struct platform_device *pdev) + + devm_led_classdev_register(&pdev->dev, &tpad_led); + +- wmi_input_setup(); + ret = lg_laptop_input_init(&pdev->dev, device->handle); + if (ret < 0) + goto out_platform_device; + - ret = sysfs_create_group(&pf_device->dev.kobj, &dev_attribute_group); - if (ret) - goto out_platform_device; - -- /* LEDs are optional */ -- led_classdev_register(&pf_device->dev, &kbd_backlight); -- led_classdev_register(&pf_device->dev, &tpad_led); -- -- wmi_input_setup(); ++ ret = sysfs_create_group(&pf_device->dev.kobj, &dev_attribute_group); ++ if (ret) ++ goto out_platform_device; ++ battery_hook_register(&battery_hook); return 0; -@@ -884,11 +1068,7 @@ static void acpi_remove(struct platform_device *pdev) - { +@@ -895,7 +1069,6 @@ static void acpi_remove(struct platform_device *pdev) sysfs_remove_group(&pf_device->dev.kobj, &dev_attribute_group); -- led_classdev_unregister(&tpad_led); -- led_classdev_unregister(&kbd_backlight); -- battery_hook_unregister(&battery_hook); - wmi_input_destroy(); platform_device_unregister(pf_device); diff --git a/pkgbuilds/linux-omarchy/0800-platform-updates.patch.sig b/pkgbuilds/linux-omarchy/0800-platform-updates.patch.sig index 1afc8bb..37ef2d1 100644 Binary files a/pkgbuilds/linux-omarchy/0800-platform-updates.patch.sig and b/pkgbuilds/linux-omarchy/0800-platform-updates.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0802-platform-fixes.patch b/pkgbuilds/linux-omarchy/0802-platform-fixes.patch new file mode 100644 index 0000000..b7d82cc --- /dev/null +++ b/pkgbuilds/linux-omarchy/0802-platform-fixes.patch @@ -0,0 +1,240 @@ +diff --git a/drivers/platform/x86/amd/pmf/spc.c b/drivers/platform/x86/amd/pmf/spc.c +--- a/drivers/platform/x86/amd/pmf/spc.c ++++ b/drivers/platform/x86/amd/pmf/spc.c +@@ -17,7 +17,6 @@ + #include + #include "pmf.h" + +-#ifdef CONFIG_AMD_PMF_DEBUG + u32 amd_pmf_get_ta_custom_bios_inputs(struct ta_pmf_enact_table *in, int index) + { + switch (index) { +@@ -31,6 +30,7 @@ u32 amd_pmf_get_ta_custom_bios_inputs(struct ta_pmf_enact_table *in, int index) + } + EXPORT_SYMBOL(amd_pmf_get_ta_custom_bios_inputs); + ++#ifdef CONFIG_AMD_PMF_DEBUG + void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) + { + int i; +diff --git a/drivers/platform/x86/x86-android-tablets/core.c b/drivers/platform/x86/x86-android-tablets/core.c +--- a/drivers/platform/x86/x86-android-tablets/core.c ++++ b/drivers/platform/x86/x86-android-tablets/core.c +@@ -156,7 +156,6 @@ static struct platform_device **pdevs; + static struct serdev_device **serdevs; + static const struct software_node **gpio_button_swnodes; + static const struct software_node **swnode_group; +-static const struct software_node **gpiochip_node_group; + static void (*exit_handler)(void); + + static __init struct i2c_adapter * +@@ -362,6 +361,15 @@ static const struct software_node *cherryview_gpiochip_node_group[] = { + NULL + }; + ++const struct software_node crystalcove_gpiochip_node = { ++ .name = "INT33FD:00", ++}; ++ ++static const struct software_node *crystalcove_gpiochip_node_group[] = { ++ &crystalcove_gpiochip_node, ++ NULL ++}; ++ + static void gpio_secondary_unset(void *data) + { + struct device *dev = data; +@@ -377,27 +385,28 @@ static void gpio_secondary_unregister_node_group(void *data) + software_node_unregister_node_group(nodes); + } + +-static int gpio_secondary_fwnode_init(struct device *parent) ++static int gpio_secondary_fwnode_init(struct device *parent, ++ const struct software_node * const *node_group) + { + const struct software_node *const *swnode; + struct fwnode_handle *fwnode; + struct device *phys_dev; + int ret; + +- if (!gpiochip_node_group) ++ if (!node_group) + return 0; + +- ret = software_node_register_node_group(gpiochip_node_group); ++ ret = software_node_register_node_group(node_group); + if (ret) + return ret; + + ret = devm_add_action_or_reset(parent, + gpio_secondary_unregister_node_group, +- gpiochip_node_group); ++ (void *)node_group); + if (ret) + return ret; + +- for (swnode = gpiochip_node_group; *swnode; swnode++) { ++ for (swnode = node_group; *swnode; swnode++) { + struct device *dev __free(put_device) = + acpi_bus_find_device_by_name((*swnode)->name); + if (!dev) +@@ -460,6 +469,7 @@ static void x86_android_tablet_remove(struct platform_device *pdev) + + static __init int x86_android_tablet_probe(struct platform_device *pdev) + { ++ const struct software_node * const *gpiochip_node_group; + const struct x86_dev_info *dev_info; + const struct dmi_system_id *id; + int i, ret = 0; +@@ -491,12 +501,20 @@ static __init int x86_android_tablet_probe(struct platform_device *pdev) + break; + } + +- ret = gpio_secondary_fwnode_init(&pdev->dev); ++ ret = gpio_secondary_fwnode_init(&pdev->dev, gpiochip_node_group); + if (ret) { + x86_android_tablet_remove(pdev); + return ret; + } + ++ if (dev_info->has_crystalcove) { ++ ret = gpio_secondary_fwnode_init(&pdev->dev, crystalcove_gpiochip_node_group); ++ if (ret) { ++ x86_android_tablet_remove(pdev); ++ return ret; ++ } ++ } ++ + ret = software_node_register_node_group(dev_info->swnode_group); + if (ret) { + x86_android_tablet_remove(pdev); +diff --git a/drivers/platform/x86/x86-android-tablets/lenovo.c b/drivers/platform/x86/x86-android-tablets/lenovo.c +--- a/drivers/platform/x86/x86-android-tablets/lenovo.c ++++ b/drivers/platform/x86/x86-android-tablets/lenovo.c +@@ -61,13 +61,6 @@ static struct lp855x_platform_data lenovo_lp8557_reg_only_pdata = { + .initial_brightness = 128, + }; + +-static const struct software_node arizona_gpiochip_node = { +- .name = "arizona", +-}; +- +-static const struct software_node crystalcove_gpiochip_node = { +- .name = "gpio_crystalcove", +-}; + + /* Lenovo Yoga Book X90F / X90L's Android factory image has everything hardcoded */ + +@@ -416,15 +409,17 @@ static const struct platform_device_info lenovo_yoga_tab2_830_1050_pdevs[] __ini + + #define LENOVO_YOGA_TAB2_830_1050_CODEC_NAME "spi-10WM5102:00" + ++static const struct software_node lenovo_yoga_tab2_830_1050_wm5102; ++ + static const struct property_entry lenovo_yoga_tab2_830_1050_wm1502_props[] = { + PROPERTY_ENTRY_GPIO("reset-gpios", + &crystalcove_gpiochip_node, 3, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("wlf,ldoena-gpios", + &baytrail_gpiochip_nodes[1], 23, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("wlf,spkvdd-ena-gpios", +- &arizona_gpiochip_node, 2, GPIO_ACTIVE_HIGH), ++ &lenovo_yoga_tab2_830_1050_wm5102, 2, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("wlf,micd-pol-gpios", +- &arizona_gpiochip_node, 4, GPIO_ACTIVE_LOW), ++ &lenovo_yoga_tab2_830_1050_wm5102, 4, GPIO_ACTIVE_LOW), + { } + }; + +@@ -432,14 +427,6 @@ static const struct software_node lenovo_yoga_tab2_830_1050_wm5102 = { + .properties = lenovo_yoga_tab2_830_1050_wm1502_props, + }; + +-static const struct software_node *lenovo_yoga_tab2_830_1050_swnodes[] = { +- &crystalcove_gpiochip_node, +- &arizona_gpiochip_node, +- &lenovo_yoga_tab2_830_1050_wm5102, +- &generic_lipo_hv_4v35_battery_node, +- NULL +-}; +- + static int __init lenovo_yoga_tab2_830_1050_init(struct device *dev); + static void lenovo_yoga_tab2_830_1050_exit(void); + +@@ -455,8 +442,9 @@ const struct x86_dev_info lenovo_yoga_tab2_830_1050_info __initconst = { + .pdev_info = lenovo_yoga_tab2_830_1050_pdevs, + .pdev_count = ARRAY_SIZE(lenovo_yoga_tab2_830_1050_pdevs), + .gpio_button_swnodes = lenovo_yoga_tab2_830_1050_lid_swnodes, +- .swnode_group = lenovo_yoga_tab2_830_1050_swnodes, ++ .swnode_group = generic_lipo_hv_4v35_battery_swnodes, + .modules = lenovo_yoga_tab2_modules, ++ .has_crystalcove = true, + .gpiochip_type = X86_GPIOCHIP_BAYTRAIL, + .init = lenovo_yoga_tab2_830_1050_init, + .exit = lenovo_yoga_tab2_830_1050_exit, +@@ -800,8 +788,9 @@ const struct x86_dev_info lenovo_yoga_tab2_1380_info __initconst = { + .pdev_info = lenovo_yoga_tab2_1380_pdevs, + .pdev_count = ARRAY_SIZE(lenovo_yoga_tab2_1380_pdevs), + .gpio_button_swnodes = lenovo_yoga_tab2_830_1050_lid_swnodes, +- .swnode_group = lenovo_yoga_tab2_830_1050_swnodes, ++ .swnode_group = generic_lipo_hv_4v35_battery_swnodes, + .modules = lenovo_yoga_tab2_modules, ++ .has_crystalcove = true, + .gpiochip_type = X86_GPIOCHIP_BAYTRAIL, + .init = lenovo_yoga_tab2_1380_init, + .exit = lenovo_yoga_tab2_830_1050_exit, +@@ -985,13 +974,15 @@ static struct arizona_pdata lenovo_yt3_wm5102_pdata = { + }, + }; + ++static const struct software_node lenovo_yt3_wm5102; ++ + static const struct property_entry lenovo_yt3_wm1502_props[] = { + PROPERTY_ENTRY_GPIO("wlf,spkvdd-ena-gpios", + &cherryview_gpiochip_nodes[0], 75, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("wlf,ldoena-gpios", + &cherryview_gpiochip_nodes[0], 81, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("reset-gpios", &cherryview_gpiochip_nodes[0], 82, GPIO_ACTIVE_HIGH), +- PROPERTY_ENTRY_GPIO("wlf,micd-pol-gpios", &arizona_gpiochip_node, 2, GPIO_ACTIVE_HIGH), ++ PROPERTY_ENTRY_GPIO("wlf,micd-pol-gpios", &lenovo_yt3_wm5102, 2, GPIO_ACTIVE_HIGH), + { } + }; + +@@ -1000,11 +991,6 @@ static const struct software_node lenovo_yt3_wm5102 = { + .name = "wm5102", + }; + +-static const struct software_node *lenovo_yt3_swnodes[] = { +- &arizona_gpiochip_node, +- &lenovo_yt3_wm5102, +- NULL +-}; + + static const struct x86_spi_dev_info lenovo_yt3_spi_devs[] __initconst = { + { +@@ -1072,7 +1058,6 @@ const struct x86_dev_info lenovo_yt3_info __initconst = { + .i2c_client_count = ARRAY_SIZE(lenovo_yt3_i2c_clients), + .spi_dev_info = lenovo_yt3_spi_devs, + .spi_dev_count = ARRAY_SIZE(lenovo_yt3_spi_devs), +- .swnode_group = lenovo_yt3_swnodes, + .modules = lenovo_yt3_modules, + .gpiochip_type = X86_GPIOCHIP_CHERRYVIEW, + .init = lenovo_yt3_init, +diff --git a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h +--- a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h ++++ b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h +@@ -96,6 +96,7 @@ struct x86_dev_info { + int (*init)(struct device *dev); + void (*exit)(void); + bool use_pci; ++ bool has_crystalcove; + enum x86_gpiochip_type gpiochip_type; + }; + +@@ -107,6 +108,7 @@ int x86_acpi_irq_helper_get(const struct x86_acpi_irq_data *data); + /* Software nodes representing GPIO chips used by various tablets */ + extern const struct software_node baytrail_gpiochip_nodes[]; + extern const struct software_node cherryview_gpiochip_nodes[]; ++extern const struct software_node crystalcove_gpiochip_node; + + /* + * Extern declarations of x86_dev_info structs so there can be a single diff --git a/pkgbuilds/linux-omarchy/0802-platform-fixes.patch.sig b/pkgbuilds/linux-omarchy/0802-platform-fixes.patch.sig new file mode 100644 index 0000000..f2ddd7a Binary files /dev/null and b/pkgbuilds/linux-omarchy/0802-platform-fixes.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/0860-ftrace-module-enable-sorted-symbol-search.patch b/pkgbuilds/linux-omarchy/0860-ftrace-module-enable-sorted-symbol-search.patch new file mode 100644 index 0000000..79ded30 --- /dev/null +++ b/pkgbuilds/linux-omarchy/0860-ftrace-module-enable-sorted-symbol-search.patch @@ -0,0 +1,170 @@ +diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c +--- a/kernel/trace/ftrace.c ++++ b/kernel/trace/ftrace.c +@@ -18,6 +18,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -4385,6 +4386,109 @@ static int test_for_valid_rec(struct dyn_ftrace *rec) + return 1; + } + ++#if defined(CONFIG_MODULES) && defined(CONFIG_KALLSYMS) ++#define FTRACE_MOD_SYMS ++/* ++ * test_for_valid_rec() resolves an address with kallsyms_lookup(), which ++ * scans the whole symbol table of a module. Calling it for every record of ++ * a module being loaded costs O(records * symbols): several seconds for a ++ * driver as large as amdgpu, all of it under ftrace_lock. Sort the symbol ++ * addresses of the module once instead and binary search them. The module ++ * symbol table itself is left untouched, as livepatch relies on its order. ++ */ ++struct ftrace_mod_syms { ++ unsigned long *addrs; ++ unsigned int nr; ++}; ++ ++static int ftrace_cmp_addr(const void *a, const void *b) ++{ ++ unsigned long x = *(const unsigned long *)a; ++ unsigned long y = *(const unsigned long *)b; ++ ++ return x < y ? -1 : x > y; ++} ++ ++/* Collect the symbols find_kallsyms_symbol() would consider. */ ++static void ftrace_mod_syms_init(struct ftrace_mod_syms *syms, ++ struct module *mod) ++{ ++ /* A coming module cannot have its kallsyms replaced under us. */ ++ struct mod_kallsyms *kallsyms = rcu_dereference_raw(mod->kallsyms); ++ unsigned int i; ++ ++ syms->nr = 0; ++ syms->addrs = kvmalloc_array(kallsyms->num_symtab, ++ sizeof(*syms->addrs), GFP_KERNEL); ++ if (!syms->addrs) ++ return; ++ ++ for (i = 1; i < kallsyms->num_symtab; i++) { ++ const Elf_Sym *sym = &kallsyms->symtab[i]; ++ const char *name = kallsyms->strtab + sym->st_name; ++ ++ if (sym->st_shndx == SHN_UNDEF || *name == '\0' || ++ is_mapping_symbol(name)) ++ continue; ++ syms->addrs[syms->nr++] = kallsyms_symbol_value(sym); ++ } ++ ++ sort(syms->addrs, syms->nr, sizeof(*syms->addrs), ftrace_cmp_addr, NULL); ++} ++ ++/* Same answer as test_for_valid_rec(), using the sorted addresses. */ ++static int test_for_valid_mod_rec(struct ftrace_mod_syms *syms, ++ struct module *mod, struct dyn_ftrace *rec) ++{ ++ unsigned long ip = rec->ip, base, best; ++ unsigned int lo = 0, hi = syms->nr, mid; ++ struct module_memory *mod_mem = NULL; ++ ++ if (!syms->addrs) ++ return test_for_valid_rec(rec); ++ ++ for_each_mod_mem_type(type) { ++#ifndef CONFIG_KALLSYMS_ALL ++ if (!mod_mem_type_is_text(type)) ++ continue; ++#endif ++ if (within_module_mem_type(ip, mod, type)) { ++ mod_mem = &mod->mem[type]; ++ break; ++ } ++ } ++ if (!mod_mem) ++ goto invalid; ++ base = (unsigned long)mod_mem->base; ++ ++ /* Find the last symbol at or below ip. */ ++ while (lo < hi) { ++ mid = lo + (hi - lo) / 2; ++ if (syms->addrs[mid] <= ip) ++ lo = mid + 1; ++ else ++ hi = mid; ++ } ++ if (!lo) ++ goto invalid; ++ best = syms->addrs[lo - 1]; ++ ++ /* Weak functions can cause invalid addresses */ ++ if (best < base || ip - best > FTRACE_MCOUNT_MAX_OFFSET) ++ goto invalid; ++ return 1; ++ ++invalid: ++ rec->flags |= FTRACE_FL_DISABLED; ++ return 0; ++} ++ ++static void ftrace_mod_syms_free(struct ftrace_mod_syms *syms) ++{ ++ kvfree(syms->addrs); ++} ++#endif ++ + static struct workqueue_struct *ftrace_check_wq __initdata; + static struct work_struct ftrace_check_work __initdata; + +@@ -8010,11 +8114,30 @@ void ftrace_release_mod(struct module *mod) + } + } + ++#ifndef FTRACE_MOD_SYMS ++struct ftrace_mod_syms { }; ++ ++static inline void ftrace_mod_syms_init(struct ftrace_mod_syms *syms, ++ struct module *mod) { } ++ ++static inline int test_for_valid_mod_rec(struct ftrace_mod_syms *syms, ++ struct module *mod, ++ struct dyn_ftrace *rec) ++{ ++ return test_for_valid_rec(rec); ++} ++ ++static inline void ftrace_mod_syms_free(struct ftrace_mod_syms *syms) { } ++#endif ++ + void ftrace_module_enable(struct module *mod) + { ++ struct ftrace_mod_syms syms; + struct dyn_ftrace *rec; + struct ftrace_page *pg; + ++ ftrace_mod_syms_init(&syms, mod); ++ + mutex_lock(&ftrace_lock); + + if (ftrace_disabled) +@@ -8050,7 +8173,7 @@ void ftrace_module_enable(struct module *mod) + cond_resched(); + + /* Weak functions should still be ignored */ +- if (!test_for_valid_rec(rec)) { ++ if (!test_for_valid_mod_rec(&syms, mod, rec)) { + /* Clear all other flags. Should not be enabled anyway */ + rec->flags = FTRACE_FL_DISABLED; + continue; +@@ -8087,6 +8210,8 @@ void ftrace_module_enable(struct module *mod) + out_unlock: + mutex_unlock(&ftrace_lock); + ++ ftrace_mod_syms_free(&syms); ++ + process_cached_mods(mod->name); + } + diff --git a/pkgbuilds/linux-omarchy/0860-ftrace-module-enable-sorted-symbol-search.patch.sig b/pkgbuilds/linux-omarchy/0860-ftrace-module-enable-sorted-symbol-search.patch.sig new file mode 100644 index 0000000..a486f08 Binary files /dev/null and b/pkgbuilds/linux-omarchy/0860-ftrace-module-enable-sorted-symbol-search.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/8201-xe-shrinker-return-freed-page-count.patch b/pkgbuilds/linux-omarchy/8201-xe-shrinker-return-freed-page-count.patch deleted file mode 100644 index f340536..0000000 --- a/pkgbuilds/linux-omarchy/8201-xe-shrinker-return-freed-page-count.patch +++ /dev/null @@ -1,132 +0,0 @@ -diff --git c/drivers/gpu/drm/xe/xe_shrinker.c i/drivers/gpu/drm/xe/xe_shrinker.c ---- c/drivers/gpu/drm/xe/xe_shrinker.c -+++ i/drivers/gpu/drm/xe/xe_shrinker.c -@@ -54,13 +54,14 @@ xe_shrinker_mod_pages(struct xe_shrinker *shrinker, long shrinkable, long purgea - write_unlock(&shrinker->lock); - } - --static s64 __xe_shrinker_walk(struct xe_device *xe, -+static int __xe_shrinker_walk(struct xe_device *xe, - struct ttm_operation_ctx *ctx, - const struct xe_bo_shrink_flags flags, -- unsigned long to_scan, unsigned long *scanned) -+ unsigned long to_scan, unsigned long *scanned, -+ unsigned long *freed) - { - unsigned int mem_type; -- s64 freed = 0, lret; -+ s64 lret; - - for (mem_type = XE_PL_SYSTEM; mem_type <= XE_PL_TT; ++mem_type) { - struct ttm_resource_manager *man = ttm_manager_type(&xe->ttm, mem_type); -@@ -82,7 +83,7 @@ static s64 __xe_shrinker_walk(struct xe_device *xe, - if (lret < 0) - return lret; - -- freed += lret; -+ *freed += lret; - if (*scanned >= to_scan) - break; - } -@@ -90,7 +91,7 @@ static s64 __xe_shrinker_walk(struct xe_device *xe, - xe_assert(xe, !IS_ERR(ttm_bo)); - } - -- return freed; -+ return 0; - } - - /* -@@ -99,40 +100,35 @@ static s64 __xe_shrinker_walk(struct xe_device *xe, - * add writeback. This avoids stalls and explicit writebacks with light or - * moderate memory pressure. - */ --static s64 xe_shrinker_walk(struct xe_device *xe, -+static int xe_shrinker_walk(struct xe_device *xe, - struct ttm_operation_ctx *ctx, - const struct xe_bo_shrink_flags flags, -- unsigned long to_scan, unsigned long *scanned) -+ unsigned long to_scan, unsigned long *scanned, -+ unsigned long *freed) - { - bool no_wait_gpu = true; - struct xe_bo_shrink_flags save_flags = flags; -- s64 lret, freed; -+ int ret; - - swap(no_wait_gpu, ctx->no_wait_gpu); - save_flags.writeback = false; -- lret = __xe_shrinker_walk(xe, ctx, save_flags, to_scan, scanned); -+ ret = __xe_shrinker_walk(xe, ctx, save_flags, to_scan, scanned, freed); - swap(no_wait_gpu, ctx->no_wait_gpu); -- if (lret < 0 || *scanned >= to_scan) -- return lret; -+ if (ret || *scanned >= to_scan) -+ return ret; - -- freed = lret; - if (!ctx->no_wait_gpu) { -- lret = __xe_shrinker_walk(xe, ctx, save_flags, to_scan, scanned); -- if (lret < 0) -- return lret; -- freed += lret; -- if (*scanned >= to_scan) -- return freed; -+ ret = __xe_shrinker_walk(xe, ctx, save_flags, to_scan, scanned, -+ freed); -+ if (ret || *scanned >= to_scan) -+ return ret; - } - -- if (flags.writeback) { -- lret = __xe_shrinker_walk(xe, ctx, flags, to_scan, scanned); -- if (lret < 0) -- return lret; -- freed += lret; -- } -+ if (flags.writeback) -+ ret = __xe_shrinker_walk(xe, ctx, flags, to_scan, scanned, -+ freed); - -- return freed; -+ return ret; - } - - static unsigned long -@@ -214,7 +210,6 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con - bool runtime_pm; - bool purgeable; - bool can_backup = !!(sc->gfp_mask & __GFP_FS); -- s64 lret; - - nr_to_scan = sc->nr_to_scan; - -@@ -225,12 +220,9 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con - /* Might need runtime PM. Try to wake early if it looks like it. */ - runtime_pm = xe_shrinker_runtime_pm_get(shrinker, false, nr_to_scan, can_backup); - -- if (purgeable && nr_scanned < nr_to_scan) { -- lret = xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags, -- nr_to_scan, &nr_scanned); -- if (lret >= 0) -- freed += lret; -- } -+ if (purgeable && nr_scanned < nr_to_scan) -+ xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags, -+ nr_to_scan, &nr_scanned, &freed); - - sc->nr_scanned = nr_scanned; - if (nr_scanned >= nr_to_scan || !can_backup) -@@ -242,10 +234,8 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con - - shrink_flags.purge = false; - -- lret = xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags, -- nr_to_scan, &nr_scanned); -- if (lret >= 0) -- freed += lret; -+ xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags, -+ nr_to_scan, &nr_scanned, &freed); - - sc->nr_scanned = nr_scanned; - out: diff --git a/pkgbuilds/linux-omarchy/8201-xe-shrinker-return-freed-page-count.patch.sig b/pkgbuilds/linux-omarchy/8201-xe-shrinker-return-freed-page-count.patch.sig deleted file mode 100644 index bbe1ce3..0000000 Binary files a/pkgbuilds/linux-omarchy/8201-xe-shrinker-return-freed-page-count.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/8202-xe-shrinker-runtime-pm-for-non-system-memory.patch b/pkgbuilds/linux-omarchy/8202-xe-shrinker-runtime-pm-for-non-system-memory.patch deleted file mode 100644 index 29ccecf..0000000 --- a/pkgbuilds/linux-omarchy/8202-xe-shrinker-runtime-pm-for-non-system-memory.patch +++ /dev/null @@ -1,149 +0,0 @@ -diff --git c/drivers/gpu/drm/xe/xe_shrinker.c i/drivers/gpu/drm/xe/xe_shrinker.c ---- c/drivers/gpu/drm/xe/xe_shrinker.c -+++ i/drivers/gpu/drm/xe/xe_shrinker.c -@@ -54,13 +54,33 @@ xe_shrinker_mod_pages(struct xe_shrinker *shrinker, long shrinkable, long purgea - write_unlock(&shrinker->lock); - } - --static int __xe_shrinker_walk(struct xe_device *xe, -+static bool __xe_shrinker_runtime_pm_get(struct xe_shrinker *shrinker) -+{ -+ struct xe_device *xe = shrinker->xe; -+ -+ if (xe_pm_runtime_get_if_active(xe)) -+ return true; -+ -+ if (xe_rpm_reclaim_safe(xe) && !ttm_bo_shrink_avoid_wait()) { -+ xe_pm_runtime_get(xe); -+ return true; -+ } -+ -+ queue_work(xe->unordered_wq, &shrinker->pm_worker); -+ -+ return false; -+} -+ -+static int __xe_shrinker_walk(struct xe_shrinker *shrinker, - struct ttm_operation_ctx *ctx, - const struct xe_bo_shrink_flags flags, - unsigned long to_scan, unsigned long *scanned, - unsigned long *freed) - { -+ struct xe_device *xe = shrinker->xe; - unsigned int mem_type; -+ bool rpm = false; -+ int ret = 0; - s64 lret; - - for (mem_type = XE_PL_SYSTEM; mem_type <= XE_PL_TT; ++mem_type) { -@@ -75,23 +95,36 @@ static int __xe_shrinker_walk(struct xe_device *xe, - if (!man || !man->use_tt) - continue; - -+ if (mem_type != XE_PL_SYSTEM && !rpm && -+ xe_device_is_l2_flush_optimized(xe)) { -+ if (!__xe_shrinker_runtime_pm_get(shrinker)) -+ break; -+ rpm = true; -+ } -+ - ttm_bo_lru_for_each_reserved_guarded(&curs, man, &arg, ttm_bo) { - if (!ttm_bo_shrink_suitable(ttm_bo, ctx)) - continue; - - lret = xe_bo_shrink(ctx, ttm_bo, flags, scanned); -- if (lret < 0) -- return lret; -+ if (lret < 0) { -+ ret = lret; -+ goto out; -+ } - - *freed += lret; - if (*scanned >= to_scan) -- break; -+ goto out; - } - /* Trylocks should never error, just fail. */ - xe_assert(xe, !IS_ERR(ttm_bo)); - } - -- return 0; -+out: -+ if (rpm) -+ xe_pm_runtime_put(xe); -+ -+ return ret; - } - - /* -@@ -100,7 +133,7 @@ static int __xe_shrinker_walk(struct xe_device *xe, - * add writeback. This avoids stalls and explicit writebacks with light or - * moderate memory pressure. - */ --static int xe_shrinker_walk(struct xe_device *xe, -+static int xe_shrinker_walk(struct xe_shrinker *shrinker, - struct ttm_operation_ctx *ctx, - const struct xe_bo_shrink_flags flags, - unsigned long to_scan, unsigned long *scanned, -@@ -112,20 +145,21 @@ static int xe_shrinker_walk(struct xe_device *xe, - - swap(no_wait_gpu, ctx->no_wait_gpu); - save_flags.writeback = false; -- ret = __xe_shrinker_walk(xe, ctx, save_flags, to_scan, scanned, freed); -+ ret = __xe_shrinker_walk(shrinker, ctx, save_flags, to_scan, scanned, -+ freed); - swap(no_wait_gpu, ctx->no_wait_gpu); - if (ret || *scanned >= to_scan) - return ret; - - if (!ctx->no_wait_gpu) { -- ret = __xe_shrinker_walk(xe, ctx, save_flags, to_scan, scanned, -+ ret = __xe_shrinker_walk(shrinker, ctx, save_flags, to_scan, scanned, - freed); - if (ret || *scanned >= to_scan) - return ret; - } - - if (flags.writeback) -- ret = __xe_shrinker_walk(xe, ctx, flags, to_scan, scanned, -+ ret = __xe_shrinker_walk(shrinker, ctx, flags, to_scan, scanned, - freed); - - return ret; -@@ -176,16 +210,7 @@ static bool xe_shrinker_runtime_pm_get(struct xe_shrinker *shrinker, bool force, - return false; - } - -- if (!xe_pm_runtime_get_if_active(xe)) { -- if (xe_rpm_reclaim_safe(xe) && !ttm_bo_shrink_avoid_wait()) { -- xe_pm_runtime_get(xe); -- return true; -- } -- queue_work(xe->unordered_wq, &shrinker->pm_worker); -- return false; -- } -- -- return true; -+ return __xe_shrinker_runtime_pm_get(shrinker); - } - - static void xe_shrinker_runtime_pm_put(struct xe_shrinker *shrinker, bool runtime_pm) -@@ -221,7 +246,7 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con - runtime_pm = xe_shrinker_runtime_pm_get(shrinker, false, nr_to_scan, can_backup); - - if (purgeable && nr_scanned < nr_to_scan) -- xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags, -+ xe_shrinker_walk(shrinker, &ctx, shrink_flags, - nr_to_scan, &nr_scanned, &freed); - - sc->nr_scanned = nr_scanned; -@@ -234,7 +259,7 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con - - shrink_flags.purge = false; - -- xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags, -+ xe_shrinker_walk(shrinker, &ctx, shrink_flags, - nr_to_scan, &nr_scanned, &freed); - - sc->nr_scanned = nr_scanned; diff --git a/pkgbuilds/linux-omarchy/8202-xe-shrinker-runtime-pm-for-non-system-memory.patch.sig b/pkgbuilds/linux-omarchy/8202-xe-shrinker-runtime-pm-for-non-system-memory.patch.sig deleted file mode 100644 index 0707c41..0000000 Binary files a/pkgbuilds/linux-omarchy/8202-xe-shrinker-runtime-pm-for-non-system-memory.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/8203-xe-shrinker-release-through-the-put-helper.patch b/pkgbuilds/linux-omarchy/8203-xe-shrinker-release-through-the-put-helper.patch deleted file mode 100644 index 6af456f..0000000 --- a/pkgbuilds/linux-omarchy/8203-xe-shrinker-release-through-the-put-helper.patch +++ /dev/null @@ -1,39 +0,0 @@ -diff --git c/drivers/gpu/drm/xe/xe_shrinker.c i/drivers/gpu/drm/xe/xe_shrinker.c ---- c/drivers/gpu/drm/xe/xe_shrinker.c -+++ i/drivers/gpu/drm/xe/xe_shrinker.c -@@ -71,6 +71,12 @@ static bool __xe_shrinker_runtime_pm_get(struct xe_shrinker *shrinker) - return false; - } - -+static void xe_shrinker_runtime_pm_put(struct xe_shrinker *shrinker, bool runtime_pm) -+{ -+ if (runtime_pm) -+ xe_pm_runtime_put(shrinker->xe); -+} -+ - static int __xe_shrinker_walk(struct xe_shrinker *shrinker, - struct ttm_operation_ctx *ctx, - const struct xe_bo_shrink_flags flags, -@@ -121,8 +127,7 @@ static int __xe_shrinker_walk(struct xe_shrinker *shrinker, - } - - out: -- if (rpm) -- xe_pm_runtime_put(xe); -+ xe_shrinker_runtime_pm_put(shrinker, rpm); - - return ret; - } -@@ -213,12 +218,6 @@ static bool xe_shrinker_runtime_pm_get(struct xe_shrinker *shrinker, bool force, - return __xe_shrinker_runtime_pm_get(shrinker); - } - --static void xe_shrinker_runtime_pm_put(struct xe_shrinker *shrinker, bool runtime_pm) --{ -- if (runtime_pm) -- xe_pm_runtime_put(shrinker->xe); --} -- - static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_control *sc) - { - struct xe_shrinker *shrinker = to_xe_shrinker(shrink); diff --git a/pkgbuilds/linux-omarchy/8203-xe-shrinker-release-through-the-put-helper.patch.sig b/pkgbuilds/linux-omarchy/8203-xe-shrinker-release-through-the-put-helper.patch.sig deleted file mode 100644 index ddd9329..0000000 Binary files a/pkgbuilds/linux-omarchy/8203-xe-shrinker-release-through-the-put-helper.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/8204-mm-opportunistic-compaction.patch b/pkgbuilds/linux-omarchy/8204-mm-opportunistic-compaction.patch index 0a31aa0..353ca7c 100644 --- a/pkgbuilds/linux-omarchy/8204-mm-opportunistic-compaction.patch +++ b/pkgbuilds/linux-omarchy/8204-mm-opportunistic-compaction.patch @@ -100,7 +100,7 @@ diff --git c/mm/internal.h i/mm/internal.h diff --git c/mm/shrinker.c i/mm/shrinker.c --- c/mm/shrinker.c +++ i/mm/shrinker.c -@@ -474,7 +474,7 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, +@@ -476,7 +476,7 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, #ifdef CONFIG_MEMCG static unsigned long shrink_slab_memcg(gfp_t gfp_mask, int nid, @@ -109,7 +109,7 @@ diff --git c/mm/shrinker.c i/mm/shrinker.c { struct shrinker_info *info; unsigned long ret, freed = 0; -@@ -536,6 +536,7 @@ static unsigned long shrink_slab_memcg(gfp_t gfp_mask, int nid, +@@ -538,6 +538,7 @@ static unsigned long shrink_slab_memcg(gfp_t gfp_mask, int nid, .gfp_mask = gfp_mask, .nid = nid, .memcg = memcg, @@ -117,7 +117,7 @@ diff --git c/mm/shrinker.c i/mm/shrinker.c }; struct shrinker *shrinker; int shrinker_id = calc_shrinker_id(index, offset); -@@ -595,7 +596,8 @@ static unsigned long shrink_slab_memcg(gfp_t gfp_mask, int nid, +@@ -597,7 +598,8 @@ static unsigned long shrink_slab_memcg(gfp_t gfp_mask, int nid, } #else /* !CONFIG_MEMCG */ static unsigned long shrink_slab_memcg(gfp_t gfp_mask, int nid, @@ -127,7 +127,7 @@ diff --git c/mm/shrinker.c i/mm/shrinker.c { return 0; } -@@ -607,6 +609,7 @@ static unsigned long shrink_slab_memcg(gfp_t gfp_mask, int nid, +@@ -609,6 +611,7 @@ static unsigned long shrink_slab_memcg(gfp_t gfp_mask, int nid, * @nid: node whose slab caches to target * @memcg: memory cgroup whose slab caches to target * @priority: the reclaim priority @@ -135,7 +135,7 @@ diff --git c/mm/shrinker.c i/mm/shrinker.c * * Call the shrink functions to age shrinkable caches. * -@@ -622,7 +625,7 @@ static unsigned long shrink_slab_memcg(gfp_t gfp_mask, int nid, +@@ -624,7 +627,7 @@ static unsigned long shrink_slab_memcg(gfp_t gfp_mask, int nid, * Returns the number of reclaimed slab objects. */ unsigned long shrink_slab(gfp_t gfp_mask, int nid, struct mem_cgroup *memcg, @@ -144,7 +144,7 @@ diff --git c/mm/shrinker.c i/mm/shrinker.c { unsigned long ret, freed = 0; struct shrinker *shrinker; -@@ -635,7 +638,8 @@ unsigned long shrink_slab(gfp_t gfp_mask, int nid, struct mem_cgroup *memcg, +@@ -637,7 +640,8 @@ unsigned long shrink_slab(gfp_t gfp_mask, int nid, struct mem_cgroup *memcg, * oom. */ if (!mem_cgroup_disabled() && !mem_cgroup_is_root(memcg)) @@ -154,7 +154,7 @@ diff --git c/mm/shrinker.c i/mm/shrinker.c /* * lockless algorithm of global shrink. -@@ -664,6 +668,7 @@ unsigned long shrink_slab(gfp_t gfp_mask, int nid, struct mem_cgroup *memcg, +@@ -666,6 +670,7 @@ unsigned long shrink_slab(gfp_t gfp_mask, int nid, struct mem_cgroup *memcg, .gfp_mask = gfp_mask, .nid = nid, .memcg = memcg, diff --git a/pkgbuilds/linux-omarchy/8204-mm-opportunistic-compaction.patch.sig b/pkgbuilds/linux-omarchy/8204-mm-opportunistic-compaction.patch.sig index 1b113b3..69d7459 100644 Binary files a/pkgbuilds/linux-omarchy/8204-mm-opportunistic-compaction.patch.sig and b/pkgbuilds/linux-omarchy/8204-mm-opportunistic-compaction.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/8206-mm-carry-order-and-hint-in-one-word.patch b/pkgbuilds/linux-omarchy/8206-mm-carry-order-and-hint-in-one-word.patch index 92b39ef..65dd70b 100644 --- a/pkgbuilds/linux-omarchy/8206-mm-carry-order-and-hint-in-one-word.patch +++ b/pkgbuilds/linux-omarchy/8206-mm-carry-order-and-hint-in-one-word.patch @@ -1,6 +1,6 @@ -diff --git c/include/linux/mmzone.h i/include/linux/mmzone.h ---- c/include/linux/mmzone.h -+++ i/include/linux/mmzone.h +diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h +--- a/include/linux/mmzone.h ++++ b/include/linux/mmzone.h @@ -1498,6 +1498,32 @@ enum kswapd_opportunistic_compaction_type { KSWAPD_OPPORTUNISTIC_COMPACTION, }; @@ -56,10 +56,10 @@ diff --git c/include/linux/mmzone.h i/include/linux/mmzone.h enum zone_type kswapd_highest_zoneidx; atomic_t kswapd_failures; /* Number of 'reclaimed == 0' runs */ -diff --git c/mm/mm_init.c i/mm/mm_init.c ---- c/mm/mm_init.c -+++ i/mm/mm_init.c -@@ -1553,7 +1553,8 @@ void __ref free_area_init_core_hotplug(struct pglist_data *pgdat) +diff --git a/mm/mm_init.c b/mm/mm_init.c +--- a/mm/mm_init.c ++++ b/mm/mm_init.c +@@ -1558,7 +1558,8 @@ int __ref free_area_init_core_hotplug(struct pglist_data *pgdat) * when it starts in the near future. */ pgdat->nr_zones = 0; @@ -69,9 +69,9 @@ diff --git c/mm/mm_init.c i/mm/mm_init.c pgdat->kswapd_highest_zoneidx = 0; pgdat->node_start_pfn = 0; pgdat->node_present_pages = 0; -diff --git c/mm/vmscan.c i/mm/vmscan.c ---- c/mm/vmscan.c -+++ i/mm/vmscan.c +diff --git a/mm/vmscan.c b/mm/vmscan.c +--- a/mm/vmscan.c ++++ b/mm/vmscan.c @@ -7413,6 +7413,49 @@ static enum zone_type kswapd_highest_zoneidx(pg_data_t *pgdat, return curr_idx == MAX_NR_ZONES ? prev_highest_zoneidx : curr_idx; } diff --git a/pkgbuilds/linux-omarchy/8206-mm-carry-order-and-hint-in-one-word.patch.sig b/pkgbuilds/linux-omarchy/8206-mm-carry-order-and-hint-in-one-word.patch.sig index de65251..2e878d2 100644 Binary files a/pkgbuilds/linux-omarchy/8206-mm-carry-order-and-hint-in-one-word.patch.sig and b/pkgbuilds/linux-omarchy/8206-mm-carry-order-and-hint-in-one-word.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/8208-mm-thp-deferred-split-uses-hint.patch b/pkgbuilds/linux-omarchy/8208-mm-thp-deferred-split-uses-hint.patch deleted file mode 100644 index 8de9983..0000000 --- a/pkgbuilds/linux-omarchy/8208-mm-thp-deferred-split-uses-hint.patch +++ /dev/null @@ -1,21 +0,0 @@ -diff --git a/mm/huge_memory.c b/mm/huge_memory.c ---- a/mm/huge_memory.c -+++ b/mm/huge_memory.c -@@ -4476,6 +4476,17 @@ static unsigned long deferred_split_count(struct shrinker *shrink, - { - unsigned long count; - -+ /* -+ * Splitting a partially mapped folio during an opportunistic -+ * high-order pass frees its unmapped tail pages but shatters the -+ * folio, so the pass works against the contiguous block it is -+ * reclaiming for. Report nothing rather than SHRINK_EMPTY: for a -+ * memcg aware shrinker SHRINK_EMPTY clears the memcg shrinker bit, -+ * which would also skip the next normal pass. -+ */ -+ if (sc->opportunistic_compaction) -+ return 0; -+ - count = list_lru_shrink_count(&deferred_split_lru, sc); - return count ?: SHRINK_EMPTY; - } diff --git a/pkgbuilds/linux-omarchy/8208-mm-thp-deferred-split-uses-hint.patch.sig b/pkgbuilds/linux-omarchy/8208-mm-thp-deferred-split-uses-hint.patch.sig deleted file mode 100644 index 88c17d4..0000000 Binary files a/pkgbuilds/linux-omarchy/8208-mm-thp-deferred-split-uses-hint.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/8211-xe-shrinker-skip-active-vm.patch b/pkgbuilds/linux-omarchy/8211-xe-shrinker-skip-active-vm.patch new file mode 100644 index 0000000..76470c1 --- /dev/null +++ b/pkgbuilds/linux-omarchy/8211-xe-shrinker-skip-active-vm.patch @@ -0,0 +1,116 @@ +diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c +--- a/drivers/gpu/drm/xe/xe_bo.c ++++ b/drivers/gpu/drm/xe/xe_bo.c +@@ -28,6 +28,7 @@ + #include "xe_ggtt.h" + #include "xe_map.h" + #include "xe_migrate.h" ++#include "xe_module.h" + #include "xe_pat.h" + #include "xe_pm.h" + #include "xe_preempt_fence.h" +@@ -1244,6 +1245,26 @@ xe_bo_eviction_valuable(struct ttm_buffer_object *bo, const struct ttm_place *pl + return true; + } + ++static bool xe_bo_bound_to_active_vm(struct xe_bo *bo) ++{ ++ u64 window = (u64)READ_ONCE(xe_modparam.shrink_active_ms) * NSEC_PER_MSEC; ++ u64 now = ktime_get_ns(); ++ struct drm_gpuvm_bo *vm_bo; ++ ++ if (!window) ++ return false; ++ ++ drm_gem_for_each_gpuvm_bo(vm_bo, &bo->ttm.base) { ++ struct xe_vm *vm = gpuvm_to_vm(vm_bo->vm); ++ u64 last = READ_ONCE(vm->last_exec_ns); ++ ++ if (!xe_vm_in_fault_mode(vm) && last && now - last < window) ++ return true; ++ } ++ ++ return false; ++} ++ + /** + * xe_bo_shrink() - Try to shrink an xe bo. + * @ctx: The struct ttm_operation_ctx used for shrinking. +@@ -1295,6 +1316,10 @@ long xe_bo_shrink(struct ttm_operation_ctx *ctx, struct ttm_buffer_object *bo, + goto out_unref; + } + ++ if (xe_bo_bound_to_active_vm(xe_bo) && ++ try_wait_for_completion(&xe->pm_block)) ++ goto out_unref; ++ + /* System CCS needs gpu copy when moving PL_TT -> PL_SYSTEM */ + needs_rpm = (!IS_DGFX(xe) && bo->resource->mem_type != XE_PL_SYSTEM && + xe_bo_needs_ccs_pages(xe_bo)); +diff --git a/drivers/gpu/drm/xe/xe_defaults.h b/drivers/gpu/drm/xe/xe_defaults.h +--- a/drivers/gpu/drm/xe/xe_defaults.h ++++ b/drivers/gpu/drm/xe/xe_defaults.h +@@ -22,5 +22,6 @@ + #define XE_DEFAULT_WEDGED_MODE XE_WEDGED_MODE_UPON_CRITICAL_ERROR + #define XE_DEFAULT_WEDGED_MODE_STR "upon-critical-error" + #define XE_DEFAULT_SVM_NOTIFIER_SIZE 512 ++#define XE_DEFAULT_SHRINK_ACTIVE_MS 1000 + + #endif +diff --git a/drivers/gpu/drm/xe/xe_exec.c b/drivers/gpu/drm/xe/xe_exec.c +--- a/drivers/gpu/drm/xe/xe_exec.c ++++ b/drivers/gpu/drm/xe/xe_exec.c +@@ -344,6 +344,7 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file) + if (!xe_vm_in_lr_mode(vm)) + xe_exec_queue_last_fence_set(q, vm, &job->drm.s_fence->finished); + xe_sched_job_push(job); ++ WRITE_ONCE(vm->last_exec_ns, ktime_get_ns()); + xe_vm_reactivate_rebind(vm); + + if (!err && !xe_vm_in_lr_mode(vm)) { +diff --git a/drivers/gpu/drm/xe/xe_module.c b/drivers/gpu/drm/xe/xe_module.c +--- a/drivers/gpu/drm/xe/xe_module.c ++++ b/drivers/gpu/drm/xe/xe_module.c +@@ -30,6 +30,7 @@ struct xe_modparam xe_modparam = { + #endif + .wedged_mode = XE_DEFAULT_WEDGED_MODE, + .svm_notifier_size = XE_DEFAULT_SVM_NOTIFIER_SIZE, ++ .shrink_active_ms = XE_DEFAULT_SHRINK_ACTIVE_MS, + /* the rest are 0 by default */ + }; + +@@ -37,6 +38,11 @@ module_param_named(svm_notifier_size, xe_modparam.svm_notifier_size, uint, 0600) + MODULE_PARM_DESC(svm_notifier_size, "Set the svm notifier size in MiB, must be power of 2 " + "[default=" __stringify(XE_DEFAULT_SVM_NOTIFIER_SIZE) "]"); + ++module_param_named(shrink_active_ms, xe_modparam.shrink_active_ms, uint, 0600); ++MODULE_PARM_DESC(shrink_active_ms, "Do not back up a buffer object bound to a VM that submitted work " ++ "within this many ms, 0=disabled " ++ "[default=" __stringify(XE_DEFAULT_SHRINK_ACTIVE_MS) "]"); ++ + module_param_named_unsafe(force_execlist, xe_modparam.force_execlist, bool, 0444); + MODULE_PARM_DESC(force_execlist, "Force Execlist submission"); + +diff --git a/drivers/gpu/drm/xe/xe_module.h b/drivers/gpu/drm/xe/xe_module.h +--- a/drivers/gpu/drm/xe/xe_module.h ++++ b/drivers/gpu/drm/xe/xe_module.h +@@ -25,6 +25,7 @@ struct xe_modparam { + #endif + unsigned int wedged_mode; + u32 svm_notifier_size; ++ unsigned int shrink_active_ms; + }; + + extern struct xe_modparam xe_modparam; +diff --git a/drivers/gpu/drm/xe/xe_vm_types.h b/drivers/gpu/drm/xe/xe_vm_types.h +--- a/drivers/gpu/drm/xe/xe_vm_types.h ++++ b/drivers/gpu/drm/xe/xe_vm_types.h +@@ -245,6 +245,8 @@ struct xe_vm { + /** @lru_bulk_move: Bulk LRU move list for this VM's BOs */ + struct ttm_lru_bulk_move lru_bulk_move; + ++ u64 last_exec_ns; ++ + u64 size; + + struct xe_pt *pt_root[XE_MAX_TILES_PER_DEVICE]; diff --git a/pkgbuilds/linux-omarchy/8211-xe-shrinker-skip-active-vm.patch.sig b/pkgbuilds/linux-omarchy/8211-xe-shrinker-skip-active-vm.patch.sig new file mode 100644 index 0000000..7467cb6 Binary files /dev/null and b/pkgbuilds/linux-omarchy/8211-xe-shrinker-skip-active-vm.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/8212-xe-shrinker-backup-in-kswapd-only.patch b/pkgbuilds/linux-omarchy/8212-xe-shrinker-backup-in-kswapd-only.patch new file mode 100644 index 0000000..f77d2f2 --- /dev/null +++ b/pkgbuilds/linux-omarchy/8212-xe-shrinker-backup-in-kswapd-only.patch @@ -0,0 +1,95 @@ +diff --git a/drivers/gpu/drm/xe/xe_defaults.h b/drivers/gpu/drm/xe/xe_defaults.h +--- a/drivers/gpu/drm/xe/xe_defaults.h ++++ b/drivers/gpu/drm/xe/xe_defaults.h +@@ -23,5 +23,6 @@ + #define XE_DEFAULT_WEDGED_MODE_STR "upon-critical-error" + #define XE_DEFAULT_SVM_NOTIFIER_SIZE 512 + #define XE_DEFAULT_SHRINK_ACTIVE_MS 1000 ++#define XE_DEFAULT_SHRINK_DIRECT_BACKUP false + + #endif +diff --git a/drivers/gpu/drm/xe/xe_module.c b/drivers/gpu/drm/xe/xe_module.c +--- a/drivers/gpu/drm/xe/xe_module.c ++++ b/drivers/gpu/drm/xe/xe_module.c +@@ -31,6 +31,7 @@ struct xe_modparam xe_modparam = { + .wedged_mode = XE_DEFAULT_WEDGED_MODE, + .svm_notifier_size = XE_DEFAULT_SVM_NOTIFIER_SIZE, + .shrink_active_ms = XE_DEFAULT_SHRINK_ACTIVE_MS, ++ .shrink_direct_backup = XE_DEFAULT_SHRINK_DIRECT_BACKUP, + /* the rest are 0 by default */ + }; + +@@ -43,6 +44,10 @@ MODULE_PARM_DESC(shrink_active_ms, "Do not back up a buffer object bound to a VM + "within this many ms, 0=disabled " + "[default=" __stringify(XE_DEFAULT_SHRINK_ACTIVE_MS) "]"); + ++module_param_named(shrink_direct_backup, xe_modparam.shrink_direct_backup, bool, 0600); ++MODULE_PARM_DESC(shrink_direct_backup, "Back up buffer objects in direct reclaim too, not only in kswapd " ++ "[default=" __stringify(XE_DEFAULT_SHRINK_DIRECT_BACKUP) "]"); ++ + module_param_named_unsafe(force_execlist, xe_modparam.force_execlist, bool, 0444); + MODULE_PARM_DESC(force_execlist, "Force Execlist submission"); + +diff --git a/drivers/gpu/drm/xe/xe_module.h b/drivers/gpu/drm/xe/xe_module.h +--- a/drivers/gpu/drm/xe/xe_module.h ++++ b/drivers/gpu/drm/xe/xe_module.h +@@ -26,6 +26,7 @@ struct xe_modparam { + unsigned int wedged_mode; + u32 svm_notifier_size; + unsigned int shrink_active_ms; ++ bool shrink_direct_backup; + }; + + extern struct xe_modparam xe_modparam; +diff --git a/drivers/gpu/drm/xe/xe_shrinker.c b/drivers/gpu/drm/xe/xe_shrinker.c +--- a/drivers/gpu/drm/xe/xe_shrinker.c ++++ b/drivers/gpu/drm/xe/xe_shrinker.c +@@ -4,6 +4,7 @@ + */ + + #include ++#include + + #include + #include +@@ -11,6 +12,7 @@ + #include + + #include "xe_bo.h" ++#include "xe_module.h" + #include "xe_pm.h" + #include "xe_shrinker.h" + +@@ -170,9 +172,12 @@ static int xe_shrinker_walk(struct xe_shrinker *shrinker, + return ret; + } + +-static inline bool xe_shrinker_can_backup(const struct shrink_control *sc) ++static inline bool xe_shrinker_can_backup(struct xe_shrinker *shrinker, ++ const struct shrink_control *sc) + { +- return (sc->gfp_mask & __GFP_FS) && !sc->opportunistic_compaction; ++ return (sc->gfp_mask & __GFP_FS) && !sc->opportunistic_compaction && ++ (current_is_kswapd() || READ_ONCE(xe_modparam.shrink_direct_backup) || ++ !try_wait_for_completion(&shrinker->xe->pm_block)); + } + + static unsigned long +@@ -188,7 +193,7 @@ xe_shrinker_count(struct shrinker *shrink, struct shrink_control *sc) + * wants, and it needs __GFP_FS. + */ + read_lock(&shrinker->lock); +- if (xe_shrinker_can_backup(sc)) ++ if (xe_shrinker_can_backup(shrinker, sc)) + num_pages = min_t(unsigned long, + ttm_backup_bytes_avail() >> PAGE_SHIFT, + shrinker->shrinkable_pages); +@@ -241,7 +246,7 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con + }; + bool runtime_pm; + bool purgeable; +- bool can_backup = xe_shrinker_can_backup(sc); ++ bool can_backup = xe_shrinker_can_backup(shrinker, sc); + + nr_to_scan = sc->nr_to_scan; + diff --git a/pkgbuilds/linux-omarchy/8212-xe-shrinker-backup-in-kswapd-only.patch.sig b/pkgbuilds/linux-omarchy/8212-xe-shrinker-backup-in-kswapd-only.patch.sig new file mode 100644 index 0000000..ae4ebed Binary files /dev/null and b/pkgbuilds/linux-omarchy/8212-xe-shrinker-backup-in-kswapd-only.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/8213-xe-hibernation-writeback-gated.patch b/pkgbuilds/linux-omarchy/8213-xe-hibernation-writeback-gated.patch new file mode 100644 index 0000000..b639c28 --- /dev/null +++ b/pkgbuilds/linux-omarchy/8213-xe-hibernation-writeback-gated.patch @@ -0,0 +1,112 @@ +diff --git a/drivers/gpu/drm/xe/xe_defaults.h b/drivers/gpu/drm/xe/xe_defaults.h +--- a/drivers/gpu/drm/xe/xe_defaults.h ++++ b/drivers/gpu/drm/xe/xe_defaults.h +@@ -24,5 +24,6 @@ + #define XE_DEFAULT_SVM_NOTIFIER_SIZE 512 + #define XE_DEFAULT_SHRINK_ACTIVE_MS 1000 + #define XE_DEFAULT_SHRINK_DIRECT_BACKUP false ++#define XE_DEFAULT_HIBERNATE_WRITEBACK false + + #endif +diff --git a/drivers/gpu/drm/xe/xe_module.c b/drivers/gpu/drm/xe/xe_module.c +--- a/drivers/gpu/drm/xe/xe_module.c ++++ b/drivers/gpu/drm/xe/xe_module.c +@@ -32,6 +32,7 @@ struct xe_modparam xe_modparam = { + .svm_notifier_size = XE_DEFAULT_SVM_NOTIFIER_SIZE, + .shrink_active_ms = XE_DEFAULT_SHRINK_ACTIVE_MS, + .shrink_direct_backup = XE_DEFAULT_SHRINK_DIRECT_BACKUP, ++ .hibernate_writeback = XE_DEFAULT_HIBERNATE_WRITEBACK, + /* the rest are 0 by default */ + }; + +@@ -48,6 +49,10 @@ module_param_named(shrink_direct_backup, xe_modparam.shrink_direct_backup, bool, + MODULE_PARM_DESC(shrink_direct_backup, "Back up buffer objects in direct reclaim too, not only in kswapd " + "[default=" __stringify(XE_DEFAULT_SHRINK_DIRECT_BACKUP) "]"); + ++module_param_named(hibernate_writeback, xe_modparam.hibernate_writeback, bool, 0600); ++MODULE_PARM_DESC(hibernate_writeback, "Write buffer objects to swap before hibernation, " ++ "integrated GPUs only [default=" __stringify(XE_DEFAULT_HIBERNATE_WRITEBACK) "]"); ++ + module_param_named_unsafe(force_execlist, xe_modparam.force_execlist, bool, 0444); + MODULE_PARM_DESC(force_execlist, "Force Execlist submission"); + +diff --git a/drivers/gpu/drm/xe/xe_module.h b/drivers/gpu/drm/xe/xe_module.h +--- a/drivers/gpu/drm/xe/xe_module.h ++++ b/drivers/gpu/drm/xe/xe_module.h +@@ -27,6 +27,7 @@ struct xe_modparam { + u32 svm_notifier_size; + unsigned int shrink_active_ms; + bool shrink_direct_backup; ++ bool hibernate_writeback; + }; + + extern struct xe_modparam xe_modparam; +diff --git a/drivers/gpu/drm/xe/xe_pm.c b/drivers/gpu/drm/xe/xe_pm.c +--- a/drivers/gpu/drm/xe/xe_pm.c ++++ b/drivers/gpu/drm/xe/xe_pm.c +@@ -26,6 +26,7 @@ + #include "xe_pcode.h" + #include "xe_printk.h" + #include "xe_pxp.h" ++#include "xe_shrinker.h" + #include "xe_sriov_vf_ccs.h" + #include "xe_sysctrl.h" + #include "xe_trace.h" +@@ -450,6 +451,9 @@ static int xe_pm_notifier_callback(struct notifier_block *nb, + if (err) + drm_dbg(&xe->drm, "Notifier evict user failed (%d)\n", err); + ++ if (action == PM_HIBERNATION_PREPARE) ++ xe_shrinker_backup_for_hibernation(xe->mem.shrinker); ++ + err = xe_bo_notifier_prepare_all_pinned(xe); + if (err) + drm_dbg(&xe->drm, "Notifier prepare pin failed (%d)\n", err); +diff --git a/drivers/gpu/drm/xe/xe_shrinker.c b/drivers/gpu/drm/xe/xe_shrinker.c +--- a/drivers/gpu/drm/xe/xe_shrinker.c ++++ b/drivers/gpu/drm/xe/xe_shrinker.c +@@ -172,6 +172,34 @@ static int xe_shrinker_walk(struct xe_shrinker *shrinker, + return ret; + } + ++void xe_shrinker_backup_for_hibernation(struct xe_shrinker *shrinker) ++{ ++ struct ttm_operation_ctx ctx = { ++ .interruptible = false, ++ .no_wait_gpu = false, ++ }; ++ struct xe_bo_shrink_flags flags = { ++ .purge = false, ++ .writeback = true, ++ }; ++ unsigned long to_scan, scanned = 0, freed = 0; ++ ktime_t start = ktime_get(); ++ int ret; ++ ++ if (!READ_ONCE(xe_modparam.hibernate_writeback) || IS_DGFX(shrinker->xe) || ++ !ttm_backup_bytes_avail()) ++ return; ++ ++ read_lock(&shrinker->lock); ++ to_scan = shrinker->shrinkable_pages; ++ read_unlock(&shrinker->lock); ++ ++ ret = __xe_shrinker_walk(shrinker, &ctx, flags, to_scan, &scanned, &freed); ++ drm_info(&shrinker->xe->drm, ++ "Hibernation writeback: %lu of %lu pages scanned, %lu freed in %lld ms (%d)\n", ++ scanned, to_scan, freed, ktime_ms_delta(ktime_get(), start), ret); ++} ++ + static inline bool xe_shrinker_can_backup(struct xe_shrinker *shrinker, + const struct shrink_control *sc) + { +diff --git a/drivers/gpu/drm/xe/xe_shrinker.h b/drivers/gpu/drm/xe/xe_shrinker.h +--- a/drivers/gpu/drm/xe/xe_shrinker.h ++++ b/drivers/gpu/drm/xe/xe_shrinker.h +@@ -13,4 +13,6 @@ void xe_shrinker_mod_pages(struct xe_shrinker *shrinker, long shrinkable, long p + + int xe_shrinker_create(struct xe_device *xe); + ++void xe_shrinker_backup_for_hibernation(struct xe_shrinker *shrinker); ++ + #endif diff --git a/pkgbuilds/linux-omarchy/8213-xe-hibernation-writeback-gated.patch.sig b/pkgbuilds/linux-omarchy/8213-xe-hibernation-writeback-gated.patch.sig new file mode 100644 index 0000000..eb7552a Binary files /dev/null and b/pkgbuilds/linux-omarchy/8213-xe-hibernation-writeback-gated.patch.sig differ diff --git a/pkgbuilds/linux-omarchy/9999-bpftool-strip-wformat-bootstrap.patch b/pkgbuilds/linux-omarchy/9999-bpftool-strip-wformat-bootstrap.patch deleted file mode 100644 index ff35b72..0000000 --- a/pkgbuilds/linux-omarchy/9999-bpftool-strip-wformat-bootstrap.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile ---- a/tools/bpf/bpftool/Makefile -+++ b/tools/bpf/bpftool/Makefile -@@ -99,7 +99,7 @@ endif - HOST_LDFLAGS := $(LDFLAGS) - - # Remove warnings for libbpf bootstrap build --LIBBPF_BOOTSTRAP_CFLAGS := $(filter-out -W -Wall -Wextra -Wformat -Wformat-signedness,$(HOST_CFLAGS)) -+LIBBPF_BOOTSTRAP_CFLAGS := $(filter-out -W -Wall -Wextra -Wformat%,$(HOST_CFLAGS)) - - INSTALL ?= install - RM ?= rm -f diff --git a/pkgbuilds/linux-omarchy/9999-bpftool-strip-wformat-bootstrap.patch.sig b/pkgbuilds/linux-omarchy/9999-bpftool-strip-wformat-bootstrap.patch.sig deleted file mode 100644 index 303bf7f..0000000 Binary files a/pkgbuilds/linux-omarchy/9999-bpftool-strip-wformat-bootstrap.patch.sig and /dev/null differ diff --git a/pkgbuilds/linux-omarchy/PKGBUILD b/pkgbuilds/linux-omarchy/PKGBUILD index cc70049..7d129f7 100644 --- a/pkgbuilds/linux-omarchy/PKGBUILD +++ b/pkgbuilds/linux-omarchy/PKGBUILD @@ -2,8 +2,8 @@ # Adopted from the Arch Linux linux package. pkgbase=linux-omarchy -pkgver=7.2.5 -pkgrel=6 +pkgver=7.2.8 +pkgrel=2 pkgdesc='Omarchy Linux' url='https://omarchy.org' arch=( @@ -36,14 +36,7 @@ options=( !strip ) _major=${pkgver%%.*} -if [[ $pkgver == *rc* ]]; then - _base=${pkgver%rc*} - _prev=${_base%.*}.$(( ${_base##*.} - 1 )) - _srcname=linux-${_prev%.0} - _rcpatch=patch-${pkgver//rc/-rc} -else - _srcname=linux-$pkgver -fi +_srcname=linux-7.2.8 source=( https://cdn.kernel.org/pub/linux/kernel/v${_major}.x/${_srcname}.tar.{xz,sign} ${_rcpatch:+https://cdn.kernel.org/pub/linux/kernel/v${_major}.x/stable-review/${_rcpatch}.xz} @@ -53,95 +46,144 @@ source=( 0120-tlbpull.patch{,.sig} 0121-smp-preempt.patch{,.sig} 0130-sched-detach-tasks.patch{,.sig} - 0131-sched-avg-idle.patch{,.sig} 0140-sched-always-inline.patch{,.sig} 0141-sched-urgent-fixes.patch{,.sig} - 0142-sched-itmt-no-debugfs-dependency.patch{,.sig} 0143-sched-hybrid-cluster-balancing.patch{,.sig} 0144-sched-nohz-idle-core.patch{,.sig} 0145-sched-eevdf-tunables.patch{,.sig} - 0200-idle.patch{,.sig} - 0210-pstate.patch{,.sig} - 0211-amd-pstate-fixes.patch{,.sig} - 0212-amd-pstate-epp-cache.patch{,.sig} + 0210-pm-updates.patch{,.sig} + 0211-pm-fixes-1.patch{,.sig} + 0212-pm-fixes-2.patch{,.sig} + 0213-pm-fixes-3.patch{,.sig} 0220-x86-amd-zen5-tlb-sizes.patch{,.sig} 0250-zsmalloc.patch{,.sig} 0260-mglru-exec-protect.patch{,.sig} 0270-ksm-rmap-walk.patch{,.sig} 0280-mm-updates.patch{,.sig} + 0281-mm-no-deferred-kernel-page-table-free-at-boot.patch{,.sig} 0290-zstd-bmi2-fallback-aliases.patch{,.sig} 0291-zstd-bmi2-cpu-feature-dispatch.patch{,.sig} 0292-crypto-zstd-defer-cstream-init.patch{,.sig} 0293-crypto-zstd-defer-dstream-init.patch{,.sig} 0295-af-alg-restrict.patch{,.sig} - 0296-x86-mm-pmd-modify-keep-dirty-bit.patch{,.sig} 0300-btrfs.patch{,.sig} - 0301-btrfs-fixes.patch{,.sig} + 0301-btrfs-fixes-1.patch{,.sig} 0302-btrfs-zstd-decompress-direct-to-page.patch{,.sig} + 0303-btrfs-fixes-2.patch{,.sig} + 0304-btrfs-fixes-3.patch{,.sig} 0310-fuse-eof-zeroing.patch{,.sig} 0311-fuse-perf.patch{,.sig} 0312-fuse-writethrough-uptodate.patch{,.sig} 0313-fuse-background-wakeup.patch{,.sig} + 0320-vfs-open-consume-path-reference.patch{,.sig} 0350-drm-edid-populate-monitor-range-from-displayid-adaptive-sync.patch{,.sig} 0360-gpu-mem-cgroup.patch{,.sig} - 0361-drm-ttm-swapped-out-resource-leaves-bulk-move.patch{,.sig} 0400-drm-i915-alpm-limit-pr-alpm-to-panel-replay.patch{,.sig} - 0401-drm-i915-psr-exit-panel-replay-for-alpm-lag.patch{,.sig} - 0402-psr2-early-transport-panels.patch{,.sig} + 0401-psr2-early-transport-panels.patch{,.sig} + 0402-drm-i915-quirk-panel-replay-exit-on-flush.patch{,.sig} + 0403-drm-i915-quirk-dell-xps14-ptl-panel-replay-exit-on-flush.patch{,.sig} + 0404-drm-i915-quirk-dell-xps16-ptl-panel-replay-exit-on-flush.patch{,.sig} + 0405-drm-i915-quirk-dell-xps13-dx13260-ptl-psr1-only.patch{,.sig} + 0406-drm-i915-quirk-dell-xps13-dx13260-wcl-panel-replay-exit-on-flush.patch{,.sig} 0411-drm-xe-display-no-stolen-framebuffers.patch{,.sig} 0420-safe-window.patch{,.sig} 0430-fbc.patch{,.sig} 0440-xe3-peak-bandwidth.patch{,.sig} + 0442-drm-i915-psr-clear-stale-sel-fetch-bits.patch{,.sig} + 0443-drm-i915-dsb-delayed-vblank-scanline-shift.patch{,.sig} + 0444-drm-i915-vrr-dc-balance-off-by-default.patch{,.sig} 0450-amd-hdmi-vrr-allm.patch{,.sig} 0451-amd-vtem-tmds-links.patch{,.sig} 0452-amd-hdmi-frl-default.patch{,.sig} + 0453-amd-vtem-null-sink.patch{,.sig} + 0454-amd-hdmi-frl-status-polling.patch{,.sig} + 0455-amd-hdmi-frl-cap-on-nondestructive-verify.patch{,.sig} + 0456-amd-dml-frl-compressed-cap-check.patch{,.sig} 0460-vesa-displayid-dsc-bpp.patch{,.sig} 0461-vesa-dsc-passthru-mode-match-fix.patch{,.sig} 0472-amdgpu-userq-post-reset-error.patch{,.sig} - 0473-i915-ptl-cdclk-sanitize.patch{,.sig} 0474-amd-display-oled-vesa-backlight.patch{,.sig} 0475-revert-drm-i915-dp-as-sdp-vrr-or-pr.patch{,.sig} + 0476-amd-display-ycbcr420-only-modes.patch{,.sig} + 0477-amd-display-irq-atomic-rmw.patch{,.sig} + 0478-amd-freesync-vcp-code-check-hdmi-pcon.patch{,.sig} + 0479-amd-hdmi-allm-hf-vsif-in-vsp-infopacket.patch{,.sig} 0510-sound-updates.patch{,.sig} - 0511-sound-updates-fixes.patch{,.sig} - 0512-sound-fixes.patch{,.sig} - 0513-xps13-sof-quirk.patch{,.sig} - 0514-rt766-stream-config-type.patch{,.sig} - 0516-hda-realtek-rog-strix-g733zw-speakers.patch{,.sig} - 0517-asoc-amd-yc-acer-aspire-a314-23p.patch{,.sig} - 0540-media-ipu-bridge-ivsc-no-cvs-lookup.patch{,.sig} + 0511-asoc-updates.patch{,.sig} + 0512-sound-fixes-1.patch{,.sig} + 0513-asoc-fixes-1.patch{,.sig} + 0514-sound-fixes-2.patch{,.sig} + 0515-xps13-sof-quirk.patch{,.sig} + 0516-rt766-stream-config-type.patch{,.sig} + 0517-hda-realtek-rog-strix-g733zw-speakers.patch{,.sig} + 0518-asoc-amd-yc-acer-aspire-a314-23p.patch{,.sig} + 0522-sound-fixes-3.patch{,.sig} + 0523-asoc-fixes-2.patch{,.sig} + 0524-sound-fixes-4.patch{,.sig} + 0525-asoc-fixes-3.patch{,.sig} + 0526-sof-amd-acp7x-suspend-unknown-revision.patch{,.sig} + 0527-sound-fixes-5.patch{,.sig} + 0528-asoc-fixes-4.patch{,.sig} 0541-cvs-nova-lake-acpi-id.patch{,.sig} 0542-media-cvs-wake-irq-without-claiming-gpio.patch{,.sig} - 0560-input.patch{,.sig} + 0543-media-laptop-cameras.patch{,.sig} + 0544-ipu-bridge-lenovo-x9-upside-down.patch{,.sig} + 0545-ipu-bridge-dell-16-premium-da16250-upside-down.patch{,.sig} + 0546-ipu-bridge-samsung-galaxy-book5-pro-360-upside-down.patch{,.sig} + 0547-int3472-imx471-vana-power-enable.patch{,.sig} + 0548-media-cvs-sensor-node-in-bound-callback.patch{,.sig} + 0549-media-imx471-analog-gain-max-960.patch{,.sig} + 0560-input-updates.patch{,.sig} + 0561-input-fixes.patch{,.sig} + 0562-hid-fixes.patch{,.sig} + 0563-input-fixes-2.patch{,.sig} 0565-i2c-asue140d-touchpad-100khz.patch{,.sig} 0566-hid-asus-no-keyboard-init-reports-to-touchpads.patch{,.sig} - 0600-usb4stream-fixes.patch{,.sig} - 0601-usb4stream-busy-poll.patch{,.sig} - 0610-typec-cable-altmode-check.patch{,.sig} + 0601-thunderbolt-updates.patch{,.sig} + 0602-thunderbolt-fixes.patch{,.sig} + 0603-usb-updates.patch{,.sig} + 0604-xhci-fixes.patch{,.sig} + 0605-thunderbolt-pcie-rescan-after-tunnel.patch{,.sig} + 0606-thunderbolt-fixes-2.patch{,.sig} + 0607-usb-fixes.patch{,.sig} 0620-usb-string-sanitize.patch{,.sig} 0650-wireguard-tstamp-type.patch{,.sig} 0660-btusb-mediatek-mt7922-13d3-3625.patch{,.sig} - 0661-rtw89-command-offload-source.patch{,.sig} 0662-iwlwifi-mld-skip-tx-when-firmware-dead.patch{,.sig} + 0663-btintel-pcie-serialize-reset-type.patch{,.sig} + 0664-btintel-pcie-stale-cache-in-dxstate-fallback.patch{,.sig} + 0665-btintel-pcie-pm-flow-s0ix-s3-s4.patch{,.sig} + 0666-btintel-pcie-verify-serialize-dstate.patch{,.sig} + 0667-btintel-pcie-gp0-cause-w1c-write.patch{,.sig} + 0668-btintel-pcie-dstate-races.patch{,.sig} + 0669-btintel-version-tlv-parse-errors.patch{,.sig} + 0670-bluetooth-fixes.patch{,.sig} 0700-pci-target-speed-quirk.patch{,.sig} - 0750-applesmc-cache-race.patch{,.sig} - 0751-applesmc-key-backlight-workqueue-leak.patch{,.sig} + 0701-pci-amd-enhanced-atomics.patch{,.sig} + 0702-ahci-jmb585-32bit-dma-revert.patch{,.sig} + 0703-pci-vmd-scan-bios-enumerated-devices.patch{,.sig} + 0704-pci-vmd-meteor-lake-mtl016-flush.patch{,.sig} + 0705-pci-toshiba-ahci-ssd-dma-alias-quirk.patch{,.sig} + 0740-hwmon-updates.patch{,.sig} + 0741-hwmon-fixes-1.patch{,.sig} + 0742-hwmon-fixes-2.patch{,.sig} 0770-acpi-pm-acer-swift3-sf314-56g-power-resource.patch{,.sig} 0800-platform-updates.patch{,.sig} 0801-amd-pmf-util-unbind-use-after-free.patch{,.sig} + 0802-platform-fixes.patch{,.sig} 0850-futex-wait-multiple.patch{,.sig} 0851-futex-wait-multiple-fixes.patch{,.sig} 0852-futex-wait-multiple-abi-fixes.patch{,.sig} - 8201-xe-shrinker-return-freed-page-count.patch{,.sig} - 8202-xe-shrinker-runtime-pm-for-non-system-memory.patch{,.sig} - 8203-xe-shrinker-release-through-the-put-helper.patch{,.sig} + 0860-ftrace-module-enable-sorted-symbol-search.patch{,.sig} 8204-mm-opportunistic-compaction.patch{,.sig} 8205-mm-hint-uses-allocation-order.patch{,.sig} 8206-mm-carry-order-and-hint-in-one-word.patch{,.sig} 8207-mm-classify-huge-page-allocations-as-failable.patch{,.sig} - 8208-mm-thp-deferred-split-uses-hint.patch{,.sig} 8209-xe-shrinker-use-opportunistic-hint.patch{,.sig} 8210-xe-shrinker-single-backup-decision.patch{,.sig} - 9999-bpftool-strip-wformat-bootstrap.patch{,.sig} + 8211-xe-shrinker-skip-active-vm.patch{,.sig} + 8212-xe-shrinker-backup-in-kswapd-only.patch{,.sig} + 8213-xe-hibernation-writeback-gated.patch{,.sig} ) source_x86_64=(config.x86_64) validpgpkeys=( @@ -149,39 +191,35 @@ validpgpkeys=( 647F28654894E3BD457199BE38DBBDC86092693E # Greg Kroah-Hartman 12D27D5D8C8E9BF1AABC7C7C7C64768D3DE334E7 # Krzysztof Wilczynski ) -b2sums=('48551bee71cd02815136fb8abe7da4464c2e17c89ef35cb0c0530c8b969fe12127ca97ab6c656ea8e5b29f2c4a8fe4cc143a626f4cca6b972a8105991e4c6905' +b2sums=('5326dde778eb945f282740ef0d8765b46fbd198f1209cdfd7b91e5dac1312fc01f8bf0b2bc67b16a76de736edd8ea21b38b6c91e696ec0c4b39746cd21a15214' 'SKIP' - '6d92fb81077232b8cd1ae500b3aabc71434792e750ea6126dc095e50d4278d3e42b3cfdc3c8bc693ea14b109f6adfa3a1ed095187fc5501962fbe8f049f867bc' + '60eac409f1ba50fe319f6b3aa5b0af81bc7ce5628ca41c1af346a6d5ef0fc9c577527742d1f70ece7e07ea410fcd112b32d64f0a05af5b532f27b62e5e7d837a' 'SKIP' 'cc2a66a097b5567e80c59b7d6c492fef5e667fc71a0390908712c69512c4136dbaf3d150b62d04eff960c796f7a94e6d0915d3b92cb336318b49f5f516a3bba6' 'SKIP' - 'f6c00ac2400580dffe3605d2693809396e18185f0c59b21b86f1f22a1f8d6529c51e8cc2474bc40f74a96631f415da51521a8b35d5b096e9b9d164c2755fb091' + 'c5a06c5632ba1eeb592f19b7f1581acfb7adf603b098d1d966359a1bb266a330a5707b64aa1f0e0346003e0b3008f46d8af669b621ccacd9f219838fb5e122bc' 'SKIP' '6aa6ea3690f3e4e145818650537cd110d33720d662af0b3660d95e15a3f91e48c993e31117c0a2fde852f55a77a83e3a50a1a95c0df78a33ae75d43d46a9fe15' 'SKIP' - 'f664077b274be12da33f0145ebea8fafda3f3e3bf96f449c31b9a56768d39b763c246394ee4ed144f7f2d1916c32085fa59dc18736bb4a28c94568e3e1919d56' + '91e65754432c2d33fd44d81f9c8664b8c233dece280ed27800d9a319f035db18764ec6b8c90617238c6a54ab98fef9f524ca65facbe7434c00891a2dcde1eea9' 'SKIP' - '7fb673924d268c0e3c416c7aa6200e96d62d156eb397832de12668b8a90d6b9c7311f1958e0f2a6d93de8e82fa3166082106c2bcbaaf42bb11b13bb83d6f732d' + '39dfe3393acf96effce036db341dc7310f33f2f6431edc5e9cfe95edae0e9782dbce46a29fcec9134cfcff361cdffcb24f195bc0c648e79e94eca19ec93962e5' 'SKIP' - '42597d4f8377d6f4c58c26c415b0621db16a37da89bc1eb079750eef1c3f7bf04c9a2ac6f334d6a3c4c439827df4aa1b92d53c15f6e3857f79d8be7eb79ea7bd' + '9f0d647d9cbe6f758f2d260920c3fda78ed91ed52f8c3108ff9a1a263505b18430ea21700311e5e162dfcdadc8feef120b999d622db803363785c8beca1f50ec' 'SKIP' - '35075dae2576cb2b8f94df048bbd4fc6b667fc176310306f8939cf9263a50a9a558102af99c90746fa7b17d98c6a321c0cc36b2bdffab5fa714e3e1256be889b' + 'c94778de1f106d30c7f2628be8b99f5bed8864e09dc3298e5c85181804150df49f3b151eaa94f801f14b1d6c3b132184d133840c4903192b565799cae226a4e9' 'SKIP' - '3434a5bbcd2bd8b4e2013400bf4d771ad5c8295ec74f67ff7acaf917954a01d25931679a71ddb8995fd7703b0b9904db4e6fe5f384d3705cf4d7b80dbf5dba55' - 'SKIP' - 'bcf83ace482aa07e64ef6cdc1d6ae9b8f52ef79c8d15421e2d8525b1fcd3bd555be49fee48a87c2daa103a5f6a564046568722f03bf768f73dbd280acf59376d' - 'SKIP' - '38d2d63ca4eee7ce83d22e60ebeb9d7f41a019cf910a8a7d151e4a1eccb14478a0054e72ae6494c89e2d66effead44e59534899fd861fe05a68460ae2d2cd32a' + '3d5a9ed6d78d5f82b43da9fb43cb57b5621faa7601ff04bba71b5c3ead05bd4fd916323ce68e50cf178e2a9c14bc826ea85ab203c878de26446618f180a5fb4c' 'SKIP' '64a2e489285c40df691f822dbca67e2b2f21446f300465f01bb589380173f6da182eda71d09a840e7ea3e14acae940d3cde66fc3b476ef6417c6e8479841cc56' 'SKIP' - 'd1007ee452cdf92967ec7055f1f39a3007a69842040e12f5063a1d2f24fd6d1eeaff2357abf96d0e330541a4781f93fecbcb116a4c73371913582eb72b8f9466' + '921a4b6ca25fb1a10588d7a3df65277cf6acf1115e618741486d230bd8eee16daef3f712518f98c8a49222aed468e706015eb238ba289769a464f5f33a21b21e' 'SKIP' - '0d3309140e496ad552d1a15d4fbf04b5ad03877bc73ae689f57554e91b7458578f2c88b6bbd37dd9fdf822a8d48c2cd6f961ec735ae5887f96fa51d908d203ee' + '486987a57ff24ae8c58953c01db6c8e4f4eca4093a8484b28b9a647790a6757a381631e13d64605bb009613985b686a0f98ef58b2e2f9c9ddc5b4d0de1c62de1' 'SKIP' - '13beae6f8d23a1f075410f50c09bc94f24f505571af1feb3cee58f732c0d649a9ce5939bc11642c4f3c8dfbd967939b2a0a205220abf06f10d559c4c43a946a3' + 'e2d21e10be9239260aaf41ec5584885555d3d5150a817fdc2b04001444ae07cebefd140b20e841c0988843749e850ad52f93b98d005d5b158e158454c2020691' 'SKIP' - '2f24f88d8f960148046c36222fdaaadfed2ca88c60240fa1d111430898192ca45aeaa93a3afa24ad02b11c4dc8c3904a92f17c0284396f2f43352ad934a35056' + 'cd6d3232202e8e6a44e80783becc1b527336027420e308f354b32b6de1f83de210aac78ee0c639ba23030b4a0811cf7728bcaeed08e9dbe11caf3edff380ed6c' 'SKIP' 'e59c4da548b2ea4fd6144886e3e2e765b3e73972121e9e73dbc05c066834b2b35e6ef4c17617e77efc90260566bf7c0910d2138d8d5c1c3b6094eeac9de64cfe' 'SKIP' @@ -191,7 +229,9 @@ b2sums=('48551bee71cd02815136fb8abe7da4464c2e17c89ef35cb0c0530c8b969fe12127ca97a 'SKIP' '31abdd15ae3f9709d6912c90319a0b62d8380e1ae1169575c0e8e419f8ce264a7cf7c72558a85719cd5a1cae7341007adb3e3936268999ebd5215c2ed288f22c' 'SKIP' - '4efb9ab5258aee01b07bc322b160d2f6e3571ff82c4f3e317cab152a919c67522629d56fb79d517da034373cd6924bc3a20c98073197c93590feb1a34e902358' + 'f95af54bcfaf17e80d9c0f60243d946aff2bbb796ae47c92a97a353f598483f03ca87fccefbaa47639b1018b82e7879dae4b8759e8fd9206b33d219560d682a7' + 'SKIP' + 'ff9a3e5df8457880b25caef9de90a7eb513ad8de99296ed503268749a32d8fa79da11f598a5fd80d28231abdd65a247e2aa92ce7c8ccc9f497fa483eee93aa82' 'SKIP' '55951c59286df43f069302b0b1036424812f80b45c5ea13219fce1bfc3e831731432c216288d722e44493c736cbf89a3e44801b5a4f1f242c17b5a89b236a5ef' 'SKIP' @@ -203,65 +243,99 @@ b2sums=('48551bee71cd02815136fb8abe7da4464c2e17c89ef35cb0c0530c8b969fe12127ca97a 'SKIP' 'ad2cfdc3d8de2193110325395e11015577f26ef1081307bb236ed73fbd109822dd44ea026a57c2e17233e1fb7982e78aa986b71c043f42887e6c5d7f51b84ea9' 'SKIP' - '19c3dd32bbe3ed7caec86e90a6af39b07eb99d5a36515cf061bb6979e5a403ca98a0e00e584a9f746330d663e5fc0868ba7fc98ab2df264bc863bdf22ef3dd5e' + '92cadbb0f7c288987838534f523f85a67d07db19ce27fd6c8717f73c02f7ec7b7b47276fea8b2a86593178dbc4f44e774727050f2b46baafdb7296028fb24a0f' 'SKIP' - 'ed2ee4849b715d80ba103fe090669062fc08c6864c64e8f8291f7f21092f0184ca5528bdd3a9318f077ef00cbe8cf28bacd1e7a9cb9f9ae1047becd930a0c42e' - 'SKIP' - 'f09e80ed25fdda9b2c79bd5ea3701985596d478b0b4e6948a1ea8fb789ef944e71640ae200442afcc23a9f0e07b34d83365e223b620baf776bbd864977e633a0' + '0b281a5780efd064a6c98a08a41eadb1286a2aa5a7f1bab33d3adaa82713f4676bd3acf07556f3617ab0649f95fa5ce3a0418da1b6ae48cb580f0d7d5d9752b3' 'SKIP' 'a51e5f2a5e37c350fb4d715ece644c565a508970c3aeb1d28cb6cd03a0c7316a1935e028b64f20a03818926c611b50ae0ffd77d04c676d604d303beed0f96e1f' 'SKIP' - '25dcd2c4a4287f26c01419b7d6430629d2827b6efff473361c68d6ec44e94e6d2bb8ab8215dbae5da25781a668f571cfc28b60c5693eb4ed629bf80fe2f68ccf' + '71e8a5d91d285519367ba73241d01adc3b61a2b7b21f8061ecc3749fbf9d87f5ca414809343fd37530bfd5226f919cc3d87dc3b4034cc5b8be9c720f8b4676b2' 'SKIP' - '8d6eeb2330a3ca7c522cef77bf7336fac9444a0884d5d101c933abd3a31a3711529a31f9a181d4795148ecbcc24425750587314ce28e44c9ab2fd2de8365e853' + '64ca5b3c45b579c51c04f1177c8e6eb25db4c76f17f7b253c5ed959c9688bafaa97da701257435b34338432e88071f04a8d4049ba66895dd3ba804173cb08b28' + 'SKIP' + '4dfbcdb23d7117b75202944547f7ce956dd8fa7405bc5ca4856135e2becc21d950eb6e1aaec082da4290ac6cffa402686c2c9137d4dc23952796ce3334b95ff7' + 'SKIP' + '51a7a4f87d53e4b3d25041a0536b238d4550fcfdef0e7de5aa75c26a7a9217f6f3e260670154b9fa6674470f72e5e09c76571e6224d8be4eb5dd4383fef64a55' 'SKIP' 'fd487cb02212ffdae5d8beb7d895819377686085b95658dcf597a534b017adcb65dfdf06e0276e7322a46dc28118803b05c246c22e7d3c361f1778ed9e12a3c6' 'SKIP' - 'e18e5086c531a37837f6e7df72ce10a38289b02de4cea376772d45cccb5aabc628fe4f3f9607f646903a7cf59b02692703e6b26800604cf958136987df0b4d5d' + '2c58614d716943cc1883dc513c2206e82f49574eca79f3e7dde21f8dd0f02f18611b2b2a444745df820c30c887d8263bdf9786a4a90b7979efee8d86633cbb88' + 'SKIP' + '218d01002d5487e2cb29f479f9ef9a2beb8226d2adccd1a9f932dc162a8502003db19b1bf74395b0239a2a15888131bcdc969b737cf56358dac6ab14d1b4501d' 'SKIP' '1b340a502dcf5a1680caf5320b3aa72aa4317e4cf1d877c1f6425e281a5668d5f092417e1f282371c0a211344f5c187fb3bdaf3f97c7e122951edf7e4c08ca58' 'SKIP' '1124c5c3fd5104c886c841ead1ae9afb42683388b640b1a86523dad9919c6bd59a1f78b07ee5a213e0dca77c636a29676af0592d4ed1735ea35f282f6f23e46f' 'SKIP' - '15855e9c28cb0abaab7ab606904dfc84d426ccab44edae59f0e1f092202b80525929f8d394d04ca1513d1d5dfc2cfc108a66a9c1cb42b9966c6417562ef20689' - 'SKIP' '505bf01a9d6b0926b557b673bb686d1a4ff73ee70539991e52272fbf58aad3ea25cf5ffc5c2d094a2d5e78b18a1a134f2ab530f595073a2d3eba9ff2c1418e1e' 'SKIP' - 'b33b46c37f12f7650a67c3731b87ff84add1ac8623cc96b52223a1ef9946bcaa990ed7c7036674e228335f57ec9ed421ea4d6c1dd72d19b516215d9ac2c7c3ee' - 'SKIP' 'b4bf4c4c0672bad91d7f8d309e741104918dd8ee8688a00d600379eba5828d1b8157953377c73d0b58050b4ff888c43013ede4e9c959be8d9c028a6143bf58ed' 'SKIP' + 'a5ae2f6c29ba850292fcd1cd39d7af6041169ad9d73151d8fcc07ec2e1da473f3ac801eda892082b615d7f19c2a3ac17bceeb1d100dc1e2ff4b30287bbd8fc8b' + 'SKIP' + '3b8e01822f9f97842c5f7d7211df876d46d8477321546bd5b8cb74484550a2c520ffe351ce0ebb7df5659d2745187081883c091574c855ae7361315d153bc557' + 'SKIP' + '90e189032c6a04ca809607a5c9a8e798ff2f3dfa7d82bb510eb608da961196bb4b53f35907bfa9ce4006fe182102900bfa20f4566b146aaddaace4fc19adc305' + 'SKIP' + 'b9f60ea91694fb859543cf895ac9d654edd0aad92a3bce6ba9cfcfc450f413e49bc8cdb08adc6fba1a46ef319c10cdc501e3ffe6b37de222b11cd1c6cf138db9' + 'SKIP' + 'f80573494f330b31af70f1a079100082e00e3c7e185bbc51928d09d2ad3ccabb3134c72a0edcef994d6cc517015190d88c41301cf73c73419c594977f86a2906' + 'SKIP' 'e403bdc300284c4f0f4302e501b8ccfe5e9af04e73a6070eb6615322f5f40ed6edad53af2ed278741f61a4e3811b92faf1af02f113dbcc2a19bde335a55930d7' 'SKIP' '5de29d0eb5264e24ab9228bea2ea4b549b3fe37ea67c045df9f83064f11c2f557b9641e772d28db59f8d258418c77435b66ff471c3695043f0f8275d5e7b1685' 'SKIP' - '3c2cd960103ad97ce35679f906b3c9be79a9744959c5dfd0ea338d8075b698790934694b2f71fde026b164c5246cba71ffe3cd5dfc1464da338136fbbaea2f1c' + '25a4ec87d1c789cab55e8ee6357276eb2e87b5e70eba2cd978b2c898e7b79de3fbc8cf5997fcd660998223b6173c98e440c853ad7c3e116f52a48d38622dfb46' 'SKIP' '2b33b11e48d8dd8d6743f26170fb99eae38541e3665e2448d91cedf729cca7c0278bfba1078ecbe88201eac4407ec5bd0292a97dbab9cfc67e42646712befc1f' 'SKIP' - '71934436987ecf6914e5c559445c2b7ee92a31c467552342094c93a59d32563aa9930e710556cc1e271eeb352705a9df9b3702482f87d045d132233cee4ab48b' + '5ef0d9561a570941752fd1976a2929eb25fb4dc16ff7fad09ce71e0d6881924f1ba8839574f7f877bd8089d116d689e33a54c14e2f177fe797dc86b305a74c0f' 'SKIP' - '9fb1318a3a181644c67b2adcac0cb04d1b88dd0e632751967239d4e8d4698f193795d00194b0f68505964c84572b67168dcbed02ad5bb7abd41e406a62111489' + '0415549ab1bf3ef0f2af4e62130b66e80d31dd3a1d1acda1cfcc2e4c926f25cf9449153736216a564cbd0710c0f0f1294607bf27c26790bad16e0f8956127e32' + 'SKIP' + 'a0a17aa48cdbf60db7a6d3677fae8a3388757b0f1de71043d8eb93028710f5a26aa57eb2011199b84e9689f7e4457843b74a5731c7dc14e4d1fca44b957bf33e' + 'SKIP' + 'ff1f07b3ad829d444b92c93fa63ab2c08277fa512483806d119a4cc75efbbcfc197dc982d9a146b7e9b94a9ca5bac95eed54735f607d7715cd52444ff0039e30' + 'SKIP' + 'a5670a422ae3d084f20800a5ca2c5615efaef4cdc58e466ceb25a8c56597e592cbf3eea7f4b8b28f1e7c79e9ecc7ff29c284d7be41de26fc6291df020fdd3d33' 'SKIP' '353b7e89a7044b7426db730d5b900eb347907aa0c20e307d2317925d6021a969a343649ede7a32e698dad60dd4472eb197c9578fb0e36fc354a60266fd680cd7' 'SKIP' - '803223224f22aca25dfe34d73e2bd49438f0cfa83b71baa545fa6fe93ee1a631b6b62c7324dc1c0d2d2ded94c36185a83af39b4f4a3362f554a1943ec2e2ec3e' + 'd70e2f90fd0630f53b9680a1a935294d0a9e86e52ef9a712905a7799827ced2ff5acfa27a51980cf6f8157d60d9831cc0d62c623f98cf577810a4cab02b334fe' 'SKIP' - 'b0a7ab192066c472f3ebc39bc9afc9fef0528ff7c98bf67c5b9cb679cc6d93eab3d25e91d8d74c9c52f72803949a3ade7940e677b9a3114605b966df94da93bb' + '88a071e8e20be14db31253152064a26961b74c329793b7fd0c81b629973bd39040e9a66189e3dfda31ecc189f82e65781b310603f498e13456b8090662f80b0a' + 'SKIP' + '0cdfeb8a469295f0d7afc27800f5109f5c88c4afda93056d4c4fc9601988334c32d287e1490966ae21bbe80c5e4f8b8da2cdb6a019ec72682521972840247b8c' + 'SKIP' + 'acc6611ef0605b18b3326e2170f32c3daad02b6472b4fd8fd2a8ba4f99a35b319f2d67b6f1f017e9beb6e8cdb8edc081e97382f3cbe7999d8ad57bd5e929a9bb' + 'SKIP' + '29f44ba6256ca05c8f40f0f1b0e813a5b3928f23e37a23cef9e7e148832f41d8ab4ec43b398ef21a06cc8588e4feee24fbf1f3ec4428ef1b22b1d43a776022c8' + 'SKIP' + 'f3437588231ac856c45eee210b6d1e4f31019480665ae31f62e0fcc4b9931cea4bdcd2bb95345cb9dea26dba43931ccc73e9c9ed209ab3281a1618378658ec0d' 'SKIP' '824089f9f45e2ff9e788d874bb9294b9208b4486fd22c56cb59d4d88cb996ff5a81ac22f8ff136ddffa1e40330f02e53ab759ffea7e4535c11d4f8195069966b' 'SKIP' - '696309582c56fad530a0bf2be12bb744dedac11ed69642b14eade0a26baf1ce52b36074b89b963b40c9c6fbc4857cdc71b2a6c6d64afcbb2df716bbb57579986' - 'SKIP' - '14212fca051d3e9a85f75616ff7f95bbd119ef8e5400dafd4447f70db2c4e9c03d971ec4fdfb4db2319d2613062d74f54dfd5d0a100ab0fa27ac6c05e16bd063' + '22e8b6835a86ad485e5b23ba73a4e1515242b06f42063c99d0dda1f9248ce222e003acd43a517dced198a28bbd1812ce5faf66fa589717734558783fa385bb13' 'SKIP' '7228e249d25a99f5106040b8ddb5705979b3a5af21ca60d0d43a9ecaf6eaccef6bde8ea69c896cffbf2b5963ec4f68987e69b184a8885c7f4c98d2b05fd59fbb' 'SKIP' - 'a54ac1c6d43fe88dee08884ed2e5bad0a0b6d11f34a46dc9801e675860afb7641b57384248a144a621e66a73541d73da11f71671116f1cc7f21534526411439a' + '4d5c5e2fbb5d8fe50a899bcfc629645d1bc9ef995c76611ecb38d25070552487d53ea59cb48bb3922d9d9965cc1f7d37ceea4f2ec789ed1bf37d69b831dea6bf' 'SKIP' - '2e486cabad0d45aeb760d031c7946e9887c710b29dbe9c16e5f4bae6a230031dba4d817f1ee25a0bbca849d1c71b2f84052ffb73c4a6c4350a6ea5ee83c5a40e' + '47015b9baebb1d6dd2a68321980c1c97da097825966f480c8569bcc9df5bef0257c51d5f7398960f28016258eaf1fff25bf142a64a01b829e882b4c1dda10612' 'SKIP' - '23e2a5def526fa886d1f1b05607bfe6b1ecbb2d6009d023145adff9eadc3135f9078faca6638966b586e81e4ab9d48d23b5758504d5a043d5f46e7a701d185fa' + '4f3df2be1b52573ef4299934896dd5b4a5b9ebfa1f69c92f125edee1aa2db2f99bd2e6cf67734e1c953e9413bc6936067e8ac619cad66a5fe8dbcfcfc7906b83' + 'SKIP' + 'ee01ba98e614695154ba62789874843e48ce2a4715b3e6ca0304da0526f705672bb29253e0c52dc8f8fd3148c072067232ae6f4e86315dfc27b1563073cb92eb' + 'SKIP' + '0963ee814b6f64903d67757c5c55a93ec1f5b16374af16aeff2e7e494254d19031d4e1422428f4f7a8167592b7750a1db87476871eeb5cba851b377e59911149' + 'SKIP' + 'dcb350ef2987b801e481b5cf32cdfbe47018939f6b43e45b6dcff87eb75cc1a48f25e679caa823f3fe6391048f216ed9d95ae79b9cac237041857458f56e7f7a' + 'SKIP' + '0e4156949673d16caa6ee1405bf33d432ce2632e433d0abea2f4e9bc5b413500573ca06c10b8dfb477d2a6c7b4201f66ebe81ffb05302e3771dd2f0c515fc5da' + 'SKIP' + '9576c977d2c95cc474e84ac8dd7683a71df38d1d6e1ad80065cfa78db8185c7a52bb408af9881a71767a1c7e3f011424a180d0cba574b14ea53f65a567784181' + 'SKIP' + '4834e1cc74f3902e8e88eb28f9fb1f2bc3cdcda6745083092e7fc39c131bb0b1a8b06d74e6112e5d4c97d14e8b56302096691005474e6e5d2720da02463246a9' 'SKIP' '5d82f32f7c04b084530722b9848254aac289c2a754a10e16821083a871ec14065dd9ee83e72b8e9e8ba2915f3fbaf17b0874df724002eb08a5f4f84d1b6aecd8' 'SKIP' @@ -271,110 +345,172 @@ b2sums=('48551bee71cd02815136fb8abe7da4464c2e17c89ef35cb0c0530c8b969fe12127ca97a 'SKIP' 'eb79554ef0d014b94bc72d4d559c2cc5419df96486acb3b6a09d5e8e9dc48db626ce0f90338c67fe873b2cc3c45c3f308c8080532b60f74a07c9e39e01d34197' 'SKIP' - 'f657143cdd9c937d78315f4617392e60f7c46f5cec882e060f740f77a27dd4c045878cb66e568a6d9ae74e9c706ffb3052817db6f327c87b85ff2f46eea678e2' + '89482f70ea83fde5a88554f54c866d11c98baab1cbcb7fa6fe0079187821736b7ad9896246cb3976ec9d471fc0ebcec6dab32b95b4928704e23e3f4ff935f221' 'SKIP' - '0acbd87a09a62af860d14f342d61c82c29ec1711038d4abf4ae473df187fbda6aa49386d88928c5dc4c3379e097478b1d40d9ebe9360e64d02e59161164b2a3f' + '31202d94636ee8a62214952d0d54eb12b8e1e264edd20c374678dd442d5fc9f8b3ffa2e94cdb6d27c58004a660823d62ffe11aa81decb5af166cc6912486b9cc' + 'SKIP' + 'c0551a31a3c63b70966c9f87eb852cbbf43a13f649bb14b6fd2e8ca23f7fb311ba55d94ddb589a78a432eb225cd2336e553d399787071cee9933f2078fe8b5ad' + 'SKIP' + 'e6f6460dd43c5a7e4d943e6545479438286712237c9feec357317b1431aa778684132b734c5c113f1e57180de366411542c29f62507095e7db62e2d9aa6ad9a6' + 'SKIP' + '58899f7c2bb14ee442c6cfedb9a354f729c03cefd325cfe71da23f33d5a548f6083d5ad922231108fb58ee00f7ef2a2d45ce58c5767079e3ae3efaff306924d8' + 'SKIP' + 'acfcf80889e146bde867b12a7c1b4a7b88a71c09603d58974b647d8066cc281b300b7a43c0e1820f22fafb5f7056522091b9c6c0072ae6a4835e603578c5f95f' + 'SKIP' + 'c446acd1c5e498f0972febf76bf6198b4ee72c6e3e4ef285862564f2467d7ad7ad74eee88191fe1fd887a90a19ad61a3945ed9ee22a2ff6902fc5a806788b151' + 'SKIP' + '8bf8f181ffbd249b0af3a8fcc8463d84daa3c4aeb567595065c311f29b2dec2344fdd240d30314eae2c3d4a4daac53dad34e8452df11e386ce0a028267b18c0d' 'SKIP' 'bb06066e90132f0172333d46be54ca816eb7d3230a193a6cae765d8edc50df4a6ba9fb7c0647cd6befc8c3bbbe1d96c285ee202812477d7295c1130ea8cdd6f4' 'SKIP' - 'f1a94986b4b3f310fecf981e3619f9787a0c8cb0b4fb3f39e5fe85ee3400e7e50278d41bf5468f65e5bd890358d2aabe28d3e11ee838ccbda2ac7af16be568d9' + '788488e828ac47a55ab78ad43251f7a2e5ad15bd5b5468bb31d4ee31e5de01a79b5680d5b048cb123eb3b7bde75e6b3d1e366e745009ed4129c8a6585d92b6b1' + 'SKIP' + '3bdf369bc73fad9bcf5b866043ff007365302318f42de7107fee47a7e8404c02c9c8fb5762d07ef10aebd2eb815c5483fc4960b7936e30ea5aa969a55f392474' + 'SKIP' + 'bdc66e50a9ef697e3baeee768f0f2dc56485afeb8a4e9f9ffd45f183d9614c33e0d0e72a8babca87963d1d9d701148eb41990730f2215a1c40e0965cc7f05e78' + 'SKIP' + 'a22e13b9bf0eac5d4190fd42bfde72ad0830cdbde8c0d309b2dfe9b0151e4c85f9f14f595ba3f36af479173ba1274f7700293c3d61dd913f711196a2dae8d6dd' + 'SKIP' + 'ebf13eb83afc051e3b9c84bfff7b326d73de6bac03f5b4d5996485d06a9cc87aca795b5c029646f804d62314a97e4a2b5da0b5f72f71556f33b62fd26529797e' + 'SKIP' + '8c6b5b96a3992c7cfbe50384fc0a4504e6d522c6918739f822b5bab29066eaeee7d1cc525b41a9b5b354ce020b1837e88d9bb940f3d2ba55a034aa9a845d489e' + 'SKIP' + 'aa24ab789db9badbd38db52f1e01fbc9d5abc09d812e2426ff281c869bbc0923cd903842d0c10ed8cf0e1a4695d3508b8ee4650f6a3eac3655247830aebb2c0b' + 'SKIP' + '1cf95a50e985ac37abd0faf86f4138e510cfb33af0ec172a2ca63f9252e6e4cba2f6c43a0babb11a96973bef3b7bf309a0f22ede26dc437673fcc77afef7fc31' + 'SKIP' + 'd5ab07d8a1db9191fc3b0d1f44c0237e93d662fb8bf04dd55385d8cded9589f66c8212a4d37c651c505fcfcb4a26da072c0be71d97fa0774b4724ba925a7af8e' + 'SKIP' + '7da8c7cd405efee8b37aff9916215b2d7052850f734b693656d2b66bae485661484efadc7207fa00dca5aa02d5e1bbaec2df9abc3e583f280097a939fc32096b' + 'SKIP' + '36fa6403c6519e064972afa0905cbff55f88fb89cad7582a89079f657537458da3d93deae474e33e018928d03090e1a373c6246b180d79f43366e16ccee533f7' 'SKIP' '44dcaad2ab326ebee62d3e63207a161dcb1b63bdb21412b5624ca7875bb6e99997528683a633d7dd526dc5ae9408e73bd066e2a1e5b34ba5c829162df2265346' 'SKIP' - '162a2c2104d5f036657ace6eef2ac2d533c0934376fab2521ff1d3fccd704e3d08fa4f6162e9100a844db2aeb57fa3ce214b8058bd227f38b058ab7606ee7816' + '69c1d61ffec58e2d3058d9e9e5fda57a089397d5ad451224f75f1cc2816c7f3fbd650b31bfb5911157fd364f77cb6f720e0825ae3deed8fe9f03576b8f7f1b31' 'SKIP' - 'd50d5a69087a5ba6c416a6391232764c8281a8094645175fdb2cfc1bb011e33335ae4e96d79364681bea61edcb58ba9acb491e08c301d0667481759d0ee1e183' + 'e3d60abe9fbfa046409598588dd23a9b16439474595136526e3169ff47982e011ede263d403211f5f5692ac43cf88af5bbf0d11b8e7e959478305c67687c525d' 'SKIP' - '539b1d0e764f0ace3e572ac49f20a790183155ddc0bcd3a8d040f44c9891dd36b1a0accb8c0288267afbd33e6ceadf6fc3a5e3db40491c695e0c1d3dd64af40c' + '1fb895cb1deb1b227515087e12fb0f7fab70b0db25a1a535fa3c294a5455af4d987d02c7c8ff24aed41359055d1383ea90bf9709999382610ed156dbc4069086' 'SKIP' - 'f5cae6eabf2066d5deef737a15967d5d11340de34b181f3b3d841023507fc58af2b68926eb04bb342e1adb857165400f862f2b35ffec4204a107b3b22a1571e7' + 'a2fb510d537b9afe8719334e5578a79a8ce7b021ff76c59830c749dbd963c38b7dfb60a61422b88f76886ccffb5ad5cb456b307a14ed68a5c86981eefd4fe3d0' + 'SKIP' + '5ecc37fe83f8dad00a82aa40a0c75d7518d6458b2f22d77820a28a34d772f1255cdb2befa5e525344d5deabac8f5dd092434a9ee2a9c27ccca5c5c231790c017' + 'SKIP' + 'bf138fd2ba2618ae9ff64838706c2212b478513e04b54ca3bdd8bf97292a8a310d73e0cd96533be62f139b645d7ac5de22a4c3bc1fd9f67e807bb862e965ea08' + 'SKIP' + '825397852f1c25b18c37d25bc53a7706af1eedd97cb9cf85ac590d1044e2254d06c4639bd54cac06ad42534399081b4ca17b0b10605c152ed41a22674c0ae42d' + 'SKIP' + '42a4c3debfa847f46c26db0d3e7cb1a540d3d15deae359b582eb2c71514c2cdb36c28c8c57b22dc32d0cee4d00ad7519da01c66127446692c81ea7a9178d722e' 'SKIP' 'ab28a1f4f1756b8451840566d8100cb3d0b498b13d006593c72829b9783e94900bae78ab47c576d156e1d08c56602274b3f7d5bd885de17336623b23c3e4f255' 'SKIP' '8481f23c33a84d8efb798f4b83f78011f74cd5a3dec428b177d7e600922836c1eec61c4238724038221d8ca2e2cdf20462c5911085da3461aa315f7dd19d96d8' 'SKIP' - '19ebb39f3e53ce77dc3522d15c88270c5ca3fdd76fa8bd417852d08a1d85b30bc6eed3e11c95392b694dfc59bb8b43e41d43df4954f00593c8fbe50bd9e199cc' - 'SKIP' - '4dc42e9bfb5ee0f952bfd7befc9d333044aec11fc3a3852d4636f2e38ab139eb92c497eb3e30921b6ee8ae7d6e5834dc334ad25419bac2c875a9fd7eb7945f9b' + 'eca2778279d88085b6bf7af742773ad533906f98b146ba2526a9e00136e154913f233be728105765b0d2fc2774068f670a4ab1b49d8a7486d20e04d116f71c0b' 'SKIP' 'fea498b5c5ada6d7f57caf652b68f687081aae8552d2d04cd9e96764efd1b896208671f4f501210bb557710cca3ceb0c02ac2b5da6d4c04c210e87ec2d715384' 'SKIP' + '39650b6ac7f62174d4b24e5267f9c6042c22452f16c649e032570eacf428e83ef8e10959fe54adf8573ac1c84d6082eb713ae5f173165af7d1f687e5d26fe6bc' + 'SKIP' + '3acc8aeadbac6ea2dd5a4fba4c10165dc1e6a4a55b838bff184fa46e827df67ea4e9a8535e17ed8cc80cc5338252306a76049af5956dc767bd591729b2aceb4c' + 'SKIP' + 'd442bf561a4b5dd1058c45a8181157feb268a84da94ec7f260dc6d10f7e0ddb71f1097bef945509f3d212431975ad3197f778bcc1c93ce1829ed2070bcf6bf9c' + 'SKIP' + 'c966ed5918d71acb91e988f3776d84db0b072f42e3e3f05cc178fa92a0d902fa3e34beb790c963042b52bc500d6ab9a2802b84a8411ca02cc1e28d361603bb2d' + 'SKIP' + '60fde203ea68744ea9bb8b81a18aedc8a4de8a985dc65bfd6b2f6b43210f87c2875e53eb61f4498c9b236892abde92da32755b69ee5bdb23f079079f22b529ac' + 'SKIP' + '56420f41da60382143b4df2d1fd58384180ca3a179b9f1b5082b2e4b74976ed13a29725c622e61c698631d923384082b43c9f703ce417d09e99556e4600f981f' + 'SKIP' + '5d880f3cdb32ed6fb7ce26ffbd1774fda56641d9304b5372806b6e559dab31dcac233cd6765a0b64bb6afe99fd9602402fb5e3b8c791cd1a9eaf9af28ca01bee' + 'SKIP' + 'a6098df3b653702e2689ec25934839c8a986b0e2f113b216ff4f86c8c5d530584f61d33bf4a027d1b304deb989406a3b342f13e11a66cc91ec69457c5d2e6f14' + 'SKIP' '4a4af88fc8a8c37ae6d20b35d19e60a17672569d5489be120edbcc62ab3ba4bbc43b78cd0799b9fd73c84b089364320bcbad6fcb7128c2cb06d15ed984d6fc8c' 'SKIP' - '0959ca78ff31299adf1091f4aa1b32cd2e9066aba5e44f0658c91bcb93fe8714f7b671ffa2cd498ecbb6c6e643ac6571bfb193034af206599122fd7c2a3c23a8' + '411fd61157830b68da00e19be0d0db7ed61de97fd99b46a31c3958c5f66885265521e9e099c4f3404975db9de9b178cb698dbbe1399d81bf13ecff79b9ebd8b8' 'SKIP' - '03ca740048e48c5b6948f972f7a826240cf51c9b5c8645d1b1c50366e78cbf45f5b45b7b47f87f231b50aab46ef2f8fd78bc53899ba1c319a19f9f61c7cecb90' + '49adc1fc4c68da5c39d96746007d19107ae2d293518cfb36f3742aeba7723db31660fb1f7b3579e8fa88bb53206dbd1e718934f9b37e87925c6c0b06759e77fd' + 'SKIP' + 'f8a67f01fca5614f0eb3bfef59658913fb9b88b043f57c8bfedf04ad79912ff04d5d356dce8ff453ca001c151ab1bc017dcd7daffd5024e2bc20b087d08ab8ce' + 'SKIP' + '561a635f88fb3729937e1632d736a8981002cffd57cf16edad538381de8bb6a583f48b05e4d2bf1c8b6c03aece2495ac2850c1d66c4b540af2b88e1a9a0d9c2b' + 'SKIP' + 'da96dbbe3b6b4e189cf7bdce14647bba07bbc5599ce28ed385a1c34d237ee7ae3ac595d2552444615f2b15c2b7eda42f40743d5bdebd443e3ec31e5743f59056' + 'SKIP' + 'c25bff6e5be62aca887ae0781899e37a39e1aec90f0eb934ec5394abf560f4d02ad1fb3973a45d00058b46555bbc8d9febfcc8026e210f53a8864a91b3b80a9d' + 'SKIP' + 'e70e0077249bf008e069041a0239c6bb63075044cd0df3218649ff92a6a2d43467026886f2a6a70c70552268f6936764459d7cc0817a103b6a0f74c37c52c72d' + 'SKIP' + 'f1f853a0ef095a53080629a00598b7b1e0023141fb1890622ffd8efc4688afdeb87be4f1a096b76868840d45c10b3c88bb33f44677ff70378d63ec81eb3110e3' 'SKIP' '1b63569b589e994d1869e2f8eaf5d4ee225fe68862ac5c848e46544b827ca5fddb4bf060f5f39001608fd9bb284eec005f5f9b095307d3a008c9ea8957c2fad1' 'SKIP' - '073514dabc88f0206911eb27eb36157e425849221056438340b22ee7bfa6997b58ce7dc4470b09d7ee96f6374f117d91cc19d9032401b2355d1c0aba6fc75771' + 'f6430036af77a31a9ddd16200bd3c2c7b1b7e46475c7917dee91c835766be046a31736123ba8429ca2f0f8d3b93ef60522873bbec45d50b954cbb8319e6f25ba' 'SKIP' 'd7dffb68fd22db7ef41d1ac193dd297fb6150f034f257a44e6cb0a4848dc4a94e328c2e26d5296a72993e2db75d09e3ac46456d337a5bae767fb266082d3cc66' 'SKIP' + '4e4792acaf0455f41cb1541d20d37e7a39104aa2dfb505e9be29dc3e7fecf328557780982b32cd8f67f77594542f5b8104766a4e8b5ce2270c9c601728363b3f' + 'SKIP' 'aca0527f5630671a319e7ecff958a3a64f29df73b427c67c1d29763cc1801fa82e96761c3a61c22d936913943a49261da64d490a0752ba9ffc8e0c1d92f99835' 'SKIP' '803736a8e836560b3e50b7c0c8cf1f9ebb16b8642cc7bc6e5167d19037bd757431565fe4581776e50eb2693c49f778a24fecff41a553612e7e498f755b8f2f67' 'SKIP' '4bc132d9c34deaae9b13d08dcff8ea83aa8f9ce4398531e0e0a80df30da3808815f3a587fe5627f6553b8e9c2aea6cc1749b752250a91c94f1cf8ba4f3eb5e8d' 'SKIP' - '2b007e58c0541c6ae71ac07cc8c0762343a50e1f3eb52db5631ce725c4f074f41c639fbf6d8695daa78ee1d6a8cb9a0c31d0a9d9ea2a3a10c05f7f6421dda184' + 'b76c03e8c59c0d2c06d00130cbe4984932762107f3d8180f90ac6c63bd435d6b8dfd317b9d1e756a89c513a20bdb52dcf4f3887daa6598449e560ca1076c5a1d' 'SKIP' - 'd3b2c0e4fd9f26a0eded375522478f8e9403232799d233eed4668920f8a9942ec6bafbc928f14459e4736f63a9a66d3ae2e9f70dc810145acb5fc522b13ecbd9' - 'SKIP' - 'c7b0be7a05a304e9993fe8ebccefa44944bf9d27dad9f7b3cc7111221bf50b2db71ca51cafc89dc03b237aef3aba7114543c8475cc393d13e61726e68898d3af' - 'SKIP' - '4008c7443bc221a142a939deac86918bef8f09ff1ca99a899586b13120edb8007450daf0b9d143770a9f04d616fe96f527b6aac160be45e5cfa66c0da898039c' + '42703ec988c2b1865fbe4eef67840f4370958e3e874d9f8a03a46c43619bed7656c56cae0ef1d8cca5340648b47ba7ddf813d25d9db3307f10146c45b8e34ade' 'SKIP' '029d9e899dd9e7bdbcacee34ee9b468e2e2a8f726e38d014891c8353f6afcc52a58c7d9289fb0487cce4d90c8d51f578c27b1dbcdab42de7308f50b7baf27877' 'SKIP' - '9ede433f4e01a5a313a133a8029d64250596582b93a33e434b8e2ee5bc53ba7f91947e1be2aaf57776028cc89e7ca62cc90067ee64650b3e503acad858777a8a' + '343b48e982195e3f07aa95dac09d5e09d5c02f823b91cc4f0775fa9529e9a985923a5af48067a1f071b99613acfa25b18e3b083e094f3cd97f6758a495c91c3e' 'SKIP' '9ac15b3542629c5333cc7d7f53b4f2995d807a31331b9e04d3da051be0ddde7e8c3cbc79837ffb49a4d364db1680cdc6d763465fa56bd4d26b16b765985babe1' 'SKIP' - '25fc5f03732a584c864ee2ad20a74e16777998b0b34f880bbe9d646cf1944bac1f276bbd62515475337eaa81c0f398d79888aca6dc18bcd06ebb7cee9e4a1d8e' - 'SKIP' '70e35154f9385156a11280294cc254cb314fd89653daedb46546a6bd64c550e3fc7e7a8df3e653c516687f0317bcc710fce82b4d649472333c501ed85fcd6cf6' 'SKIP' '576fd826f28d945782840865f3e5b7c87d7f6f91f08e473ff0d06a37caa2f89cdd9cb4a7aafef5551363aab9c60e887ee794d5b6292874c12baf88c23ea5668e' 'SKIP' - 'b69cf36cc5633e507866e67f59557646dda7d6ea436b145f3f3356c4b12e1300c94977a7d1214835f2476c94a30dd672076241b920d96d744fbc199539df506f' + '01125c624a4971686d02a23918e3da697645a322e5a7638159636d9a16c82f815efdad1d8e22c507518dc0b92b6842277637cb2d0940f4eaab40c0faa77d8da6' + 'SKIP' + '322e4e255a2b900ad224ff376b00199c40e26fc5407e15dd36c1ca4e51c7ca0431397a69696959d1c1910b02a4bdac8efbaa20f6f6f6dd5424b711c149ea367b' + 'SKIP' + 'ae93d961d0eef41ed39c473fe9478b62fab13ba395333b3c11b34b7787904a3d2684520ec76dbe2603dd96a146a68bcbe98da1b7e4cc1e659246127384bce22c' 'SKIP') -b2sums_x86_64=('5cbc3cba5e3abe4c77a54aeb2015b04e39dc80351accaa7c268da3e67577c0ad47109b8693bf2dee8d5366d0a0df3e6439c32d18fcf5ea07345bfcf517e5036d') +b2sums_x86_64=('92ba00a8ce0ff16806ca125e22434eb687ac8b74d9daf4fffe0cc8de54b13affc75b48792c7c32f3427387c2e147046d169da2d6353f4b9e2abe04addbf5369b') # https://www.kernel.org/pub/linux/kernel/v7.x/sha256sums.asc -sha256sums=('55ddf0df8325d9dad96fcff7bd93977d22e3f50af06527572af59b77c7632b78' +sha256sums=('12e8d5a973d1ad7c5a5c69882e4022b131ed715db7003fdcd760ddf8c3e51941' 'SKIP' - '95f3e9209629044028373af987dc0e270a5a15acb8c8e49c5f05057220c75fe2' + '777e4bbfe44531c6504fa646ee74e95e8ff11f264a3fa281b528e25960e741ad' 'SKIP' '6cf74fc3f28a751354187b6cf610c3e43cf9088b5f730b68dbb575828c3a7478' 'SKIP' - 'a9171e731d08a454a50174889af8936fab962b33c37c67169b0cdf21b0b80821' + '58f21f8c48dbbb1295d488815aae50003155ec9ea5633ffbca4f0e17b18e8803' 'SKIP' '6a27e1b363f1bb133b9905f84b00e4d01885cac84858db46bdbf764849c9984b' 'SKIP' - 'dc3facd0cea0f1f99cfd824203db8da8cd0a970d07b2c4c88dab661655687545' + '83d51eee049d9898a217d3846edeef317365f4b74b277988582668fa71cb4a28' 'SKIP' - '9066c85e02351d86d4c61b86eb34d45fbd8022f42ce1739aee37931e7959298f' + '86dcf3d18fba4756eccba9997a3302445de347e878369c7b119851354d6a1c1e' 'SKIP' - 'bc0cc15d29d5b817201daf0f694543f932e6af319c24c1f603fc89e71b7c27cf' + 'c89cb6da5e007551dfc6437a485adb664e9ac68e6bb80763dea757ef66c989da' 'SKIP' - 'a101a8e061983d0a1cf5b47efb28840e4a8d3b00daee53a6a7f773e2472ffdc9' + '01319b540a2cb4772eb4cccdc8c85ba4c96b150be0dcc88e3ac1290b4fdf6cdc' 'SKIP' - '9492d7db11fd327ff06a52cad945bf8ff6dfcabec72c435bf02b4432ee67475e' - 'SKIP' - 'a90ad7d91ecd9c7f64c9f84a65626241bc86c08c5c255e6684b7e26bcf5a47f8' - 'SKIP' - 'c206a0d1ac334a36f9ec5b8b2d706eaf6b726d32e304848d0ada50b6f25885b8' + '4fc90cc8e9d9979d7c839fd1761e42221577667ceb7e4502c354081b8d40d3f5' 'SKIP' '40177376862e4d7f45684c5e2a743f3da40d59817b0afa3050a3002ac6838085' 'SKIP' - '720720067a21e8c57d2619f4c0b46846ac060ec6db97088634d62ab8628c70a9' + 'df870910371645a95140a3f92424db43c4e1a92bd493393e598d61c6266f1c7b' 'SKIP' - '3d1299d19aa9fbc96d25f67154009813e05e92340340953ac8355649cff837a4' + '3d5550935087a7d82b527227e8f08ed98c2ffdb830b526c09e30b93137b3e607' 'SKIP' - 'dac26fb42f5df71b30c46b3c5894c6016af5a283c57b45173386767ca53b1bf8' + '2351136c78ad2dfe68e34bf9a70d377724257f6239e68bf8f501637b607af6b2' 'SKIP' - '66eb2c49dbf708cc781d411d0e44ed613480f0ef66d522272929d9cb7bb21e93' + '6c93475fc28d211ad7c15c691ea882deedeea8c0defe153cdde0a2a74488c7a0' 'SKIP' '718b40e15350049e03c0ed281b9257fca5432b8dec2e0e145e8b26507ee8892d' 'SKIP' @@ -384,7 +520,9 @@ sha256sums=('55ddf0df8325d9dad96fcff7bd93977d22e3f50af06527572af59b77c7632b78' 'SKIP' '7c25d0a53a115bc1f072ff0cfdd9aa88858e754ae8b15026a2582656e8bd9c43' 'SKIP' - '79799f12cdf42de1cf6c5d483896b439d77bc2e582e10c7215f5375a76612a72' + '336d72ab830bb33ef2a23bda1869e1fc071af64b24779281e197073ab70c851a' + 'SKIP' + '829300e576f418e64101a20d87f30f7c90798cd6e179bfb72768a83d99fa711e' 'SKIP' '5e3b455024fab2855b590091c9d14b0d12d5363f3d4bf025b31b9aa88b65eba5' 'SKIP' @@ -396,65 +534,99 @@ sha256sums=('55ddf0df8325d9dad96fcff7bd93977d22e3f50af06527572af59b77c7632b78' 'SKIP' 'e1226c836a9c2fb6daff109e56ba21c13fd198e109cbb181de920af04fe153e6' 'SKIP' - '8d60f7dd26ef904419aa966427a9af2f2c9a079bb092ea0b58274ab905f6047a' + '2cdd81f5520b935e1d99629fbd6e1cc9d1351b301396316f37946b95084f1a26' 'SKIP' - '258597006eb96e1fb185ec9303ceb48148eedce95608b57900383f6df59d2989' - 'SKIP' - 'f935409f9aeba3314adfad7db1b3d895557a07029a80d10969804e49c0e270ca' + 'e65c2645b0d01301a91874183faed44a390b7a402d2dffd45171d210034dcc93' 'SKIP' 'd8e64b9da3beab7c33832c019da50d0977b39dc32ec1f649a7c3bc93a2fdb9a5' 'SKIP' - '1780f66b157de6d249301331e31b7505f6e6fa26686fa4c618623567e4b758ae' + '5e2515879d58eba10c861f73318f4c4b478b8dd592680eb58961aa4065d4c3df' 'SKIP' - '2501c98aaea6a53eeaa18d696f60b677e710d0f2d89f7525e47ba806de1878b3' + '874efac3eb32970f3e06275adc072a384b89ff69e2e5b7452d6146a6e89049b0' + 'SKIP' + '43aa4a11d9d48f5094b4c0e807d1d31598e861a50695bdfa52f19819b5ff5285' + 'SKIP' + '2b49e4c98c5d704bee0a0e2b38051d59177161bca7b0188e6c0feb555a95fbf1' 'SKIP' 'e6b770d37e80509ed7ec5d8b59b4d2e5d6dba54ba659d7392de3b567f1eb6e54' 'SKIP' - 'b8596b5b5b546fdc309e10326b0e2ccea5a0538d4919630ed2845cded78580aa' + 'a9b3ef8aa79488d1cd813507e040e0762d4c8c5845b7f4ecc29c6cea1ade8056' + 'SKIP' + '264d49d74eb4006c8eded08e39409045032920d76df035aebe8a1d0939d4b7b7' 'SKIP' '5cc215215fed4247c2d6b9732deff586331a07d54c3b00538a81921093a3a1a1' 'SKIP' 'e303da14a3c8a15fd1cb45394138f03dd16e66d2ac0d1ebcd933977df937d771' 'SKIP' - '503eba8d7a80b978fccdaac798f57cbb6cd2b2b04fd04c46399daa004b6646dc' - 'SKIP' '1f0c958b64ba48b8dc8b5548e1ad1934b8d4903fc16fec15014759e3681ea275' 'SKIP' - '47994d576008a377de612ed77e6b2e7bc6b24ad3a30a6d157646d380ae323142' - 'SKIP' 'd9eb3717ba98e270b0a998f8dd90cbda072f63b6a6ea8f66763bde3ea64e591d' 'SKIP' + 'ce634014dc0131a44c3c95a8e9cb0a9de433f59d73fd99d3e3297adde65fef71' + 'SKIP' + 'd069e594d170adb016c5000d9cb7eac12c3c267a6b60c27afde7f046dba0509b' + 'SKIP' + '78feb2e2fabccaf954fdae5b776a22cec4a3f4135fbd24f79596a5274fc0129b' + 'SKIP' + 'f864c7daeaac0bad482bbf3853583d8a6cbbf2bf264562e397fc26355ffd5504' + 'SKIP' + 'c3a709af1589bd0c467565dc66ec4c01bed9c2b94314dde30c839084e594557b' + 'SKIP' '1e2fddb2e0c5d184a2e8c774886cf9be2f2f292e4f05832b50e5b49c48a7bacc' 'SKIP' 'b9b8771b6a8bd7128f4344ad189e32c155abf72abd3ff646dcf5c78b08eb94c4' 'SKIP' - 'f62837e8ee51070f8139f718e0e80a4ce0c0dea9ce2328c10c63403aca5b3d2e' + '34991cf6cec6c22bc1a55a76bc5f72b405165a2f14ef977a0446df7c18bdfbb4' 'SKIP' 'cae59282e3d1afd69f4f5dc00a180c712ef464c98b8fd7ccc9b03de944dc79b9' 'SKIP' - '5a74c8b2ba11e5876eeeb5ba530ae97e384b48943fa7a31e13396ecb82c5a8ce' + '3b06b5285923c251b0ebaf69911875f796cf7f1d7580ec33bb218509fea313a7' 'SKIP' - '8b954ea37a2190022064c82688bc8cb1cf79a90598f078035be7d4041003cda9' + '15609bef72571868fec6b8971f53b74441e5e93fbdd3c677ede963d0719cbc40' + 'SKIP' + '1231a6e23557936b811ce5dc184c23a7fbd268a20d582b6820c67125df8f07c0' + 'SKIP' + 'ec12a77671afb85f8297c86fc97f8314b4f00a1630acb199af12e3550a9d4ec8' + 'SKIP' + '1b1660064a3dadf2bb3006b140d1c7016a1ad051f27e7a4d6d9f3f40d491fe9c' 'SKIP' '40ac31789399a5964c4fd820d4cdc7214add91109bbcf4a614619e5d8b367441' 'SKIP' - '707460d08c15f451d0a0e567c6e35522f3e1409bbe3bfbcf7160f33af06391b7' + 'ab3b968d8bc2073e8c53478aa5d5afbadc5329a22b1486a014e5d0d84bfcf752' 'SKIP' - 'eebbe50deab379d09b5149ef30769ad6dfa11109696934ee66b108ec406da942' + '2a2a81e647aefda80192d230cd1dbae2aeb611f66b7d6c83694b9f9f8e2befdb' + 'SKIP' + '2772f0733267d12f36ae2a339c9aeaf0a133a20381a6b73e0c26f2ef92996ff3' + 'SKIP' + '3998c774884fdfc94be3da3c4c3641bd4b5aeb1878d8ef13bf0e27cd9f2e9994' + 'SKIP' + '0d5afb79681a948a65943f557d143c15126e2497e71c5ff50ac13ed8619a67e5' + 'SKIP' + 'e95f8fb6d116b30c2b86d9fb51747a9f9b4e5f980c1a3b627a38940db9d0e521' 'SKIP' 'dab92dbc6eac02771c72801d94e6a53aabcf629a9cd79e85e0ce2ba44a65b06b' 'SKIP' - '30e2fc90c950869ab2fe1535b609f35dc1d46d6b6816de4afcbf687dbc224f0a' - 'SKIP' - '1fdf8c4d8ee07c02883b827a11a84c1e5f70570545b8a4a232f6b59dd02049fa' + 'f55bc6366f8b9ffef744b289788a19e8604239e84c1af8b929a6922cf07d223d' 'SKIP' '0057dff07a3ce9c7b7085edd1f912958e01ce8f7400151d55dfb74c0c6a084e2' 'SKIP' - '2762f72620781a672d9a21bcc591b862b04a756b271cd1009a7390eb1b77635e' + '9c4c16b9593449ce556a4d6325e5e2205cb1698f9000a967ba7df118395b1ef0' 'SKIP' - '541325476efee83232ab8a6c62c14cf3c3c957c49cf053ed2869f7780f86d032' + '00afdc277c53dff0898ac86fd44ead020e007373134d7c7c333285a9f1dbf786' 'SKIP' - '8a8eb0934424ab6312f26e03a06be5d0401360ee60a20a87b11f7c3c6590103c' + 'bed39a5e6bbb7cdac3f56a883a4684b2a63f3e8fe2f8cdcdd0b89b374b8d2a21' + 'SKIP' + '75d7fc371ba9b00a78af2e4b0fdb7cf79bf25a6620c983662d6b2e90cf5ce8c0' + 'SKIP' + '0fe32924142868879ac9417c67b0233b6ef8ddb0163183352479f5163bf7b5df' + 'SKIP' + '7444186ad4f6d8e2b52a3729064f649b035af6eae4f4ad46b9768c006419fdbc' + 'SKIP' + '94750a925a726ad6631d0486e7f3cb3796b3941715b188e790fa135d0aa5734d' + 'SKIP' + '143193a175084d65035bcfdcc81e9dfef2e794dd56d637556db2c73b117f708f' + 'SKIP' + 'f18821fa9527c7ee07a49b9c1c983a3584d5058f9734b520b5d51cb485c93f17' 'SKIP' 'f7ff13ea46359217d696c6ccd49bc3d74c441ac5893d5151d95138017b0d10aa' 'SKIP' @@ -464,73 +636,139 @@ sha256sums=('55ddf0df8325d9dad96fcff7bd93977d22e3f50af06527572af59b77c7632b78' 'SKIP' '7245c6e2cb5c0b0ddf800894f087770cbf8ccc6f8b3f53b9a21c704e9dfa42c2' 'SKIP' - '135140bce40644079be7743b51f8618fc829a8868ac53137050f76c45eca4061' + 'd49629fd003900f13418c05d8c2fc37c8dea2fd8a15debb5b863008c9c46021f' 'SKIP' - 'e6ec816ae309445c6c11e3433c04d0c9100e16c0e23268226d13c19e7be49edb' + '411fe33ab7021dcf73b02cdc80a64094a511333c52dd754e54d382fe1145de08' + 'SKIP' + '5104ee45fc11994b1cb44130811435c84fe4db532a2fa9a722c7b03dbbdc8bf1' + 'SKIP' + 'f7617e3c4248e015f6f174e7c8659299d831035d0fb3c89acd1a858a202f91b9' + 'SKIP' + 'e7dd17ccd4b417a1813c21285c738a3fdede1326d8a1445086ea1af294c3710b' + 'SKIP' + '058aa32a18723035b18a770e1bd7f4b4f101bf5618bf0114e1874a398de42e06' + 'SKIP' + '833c5ede23f4a014f428c64b7da550cdcd7ecc939dfeb45dce172b18417888a5' + 'SKIP' + '21a9196887fe8453218e4805e171b19958005f38e5d21886c58a55f8d9b86795' 'SKIP' '29c61ec984bd342113b3c4310a3d5fddfd6d7db83847cfb26b84cc730385320a' 'SKIP' - '53425998005a4115bdf6001b9ffbe62c66bb2db205994b2e13005a3417ea6cbb' + '4b5abcc24f557e83e4d385c8304c781d6ab78fc0a3935db97e01e7d27fc617ea' + 'SKIP' + '0dc5f0f91640715ee1744fb8cfe680de6ac1370f4df8e17d31016744f31d3638' + 'SKIP' + 'cd068c7e199f458356886eb321f759a91894bca374c71ccac242407d8b22a1f5' + 'SKIP' + '618369a173be9a5a2f91fb6044931d897bbf4911586bd94a10b43a25838872a2' + 'SKIP' + 'bd3535f78183d56b133986b814ec31a8c1aa933d71b3a611937e6377ee04af10' + 'SKIP' + 'bb1e2ab62b109e434bd2c2635b5912602b8a8cc0ba24743b7bb5f691dcde2353' + 'SKIP' + '55ddb7c072f3933eef42ac48f3b32f90a5298f5cf90b4ba5b2128a197250494a' + 'SKIP' + '62e446b250e96fb65a3068bac424e1e5fe4482ad6d7ac473fa94cb8ac047c0ab' + 'SKIP' + '8b55dbedf730b0dc675793418c21b909aed5db4311248bd277d3184839d261f0' + 'SKIP' + '5a09e7b8e10fdc61839ada49e9a33a64b240fa54847c1c0e0044518297196f0b' + 'SKIP' + 'b857f4c98ff5998adab1765a06ad307f9334a8d7d9644a3de556f2b0e9cb2b4b' 'SKIP' 'c28fca9ba015e96599627acf42e52737a6a846bdcbfd3d4af4147eda3109e12d' 'SKIP' - '5ce97dadb9eb1a8b0da41a8446ee74e01a5a7e7ab439bbea7a711318f04ae24f' + 'd15cdfc9b86c40a119a4f21f4fbaec72efebc67677a7386df46a61816d4c6e02' 'SKIP' - '44f1c7bd18ea7f8772ce41e24dddd5f314e416ec8dfac6afe1ce8680c1bb4903' + '1b070eef78ccffb3763b464f5a1b0636936f4c33b0be9b44ab116c5dcbadbebe' 'SKIP' - '0bb8ae5b6f23e13c60953eb5bbc8939e8f6c8faf2b216a20203af3b6f0217134' + 'eb0923de01175707151930d28b1a1a912d24bbc171248fa9f45f57a2125d077a' 'SKIP' - '907c92ef681154d10959a5f0cbe2a636688036990ba42e059af00cc2311b4be4' + 'c076ed3752bcc2033e435043668fe007eb1ea1760597416fbb5d90bed4947bec' + 'SKIP' + '0fa9fdc6d6c85435d7b3cb38b51823f458ab273c527fcbe21b945dbd89686304' + 'SKIP' + 'ef66f6c32ac98a311b7437265b21a8c70746f1ea4dfbe28aff655e778d09c28b' + 'SKIP' + 'b45303b559ab8990ecb4e0281e5d529625311e795d6c3a9a52c2c27498141e18' + 'SKIP' + '1728e516153acca3c343f5184c9894fe6f1a1fbc95abe1594f544ef601db39ed' 'SKIP' '534e68a989d36823f14a10299803f294eef39391619b4c329d78c20539d9fcd5' 'SKIP' '516e6ec51ca0330989128a4eb2a8f6785e5d45d930899cbbbf37b8147ddfd854' 'SKIP' - 'bda3d6842f15998ce3f3e33d0e6e420df9c9c2abcba13716af478720c5933753' - 'SKIP' - '3e7e7dba1ca88930c2c3f0d5411ddcee8c2e12fca59d859e2383dccaf9a08022' + '757ccc291d53030b89088802615ecf727df6f18ab2515cc0075008ebbd564e36' 'SKIP' '3a271bc6b0219152047fb2b78d9e695eb791b796ea6d8233096912c77d62a5c0' 'SKIP' + 'fd6bf3ed3d16c788330630fdd15604836ec2a0b6a7db2b04216f756da9117bd2' + 'SKIP' + '4d598b9e00edf7c17d915eec7c8d369fc0ea40badb7f4576cb3dedd92a685d4b' + 'SKIP' + '98ff0d6196470c582b3e3841ef35bf82a63b57ebaef952885bd1905bcb8e627b' + 'SKIP' + 'de2682af1db7f5ae8cd74da4f9020bfb7d2167f030eb15f0afb7bd46ffe49e84' + 'SKIP' + '73dc22b7f97a6cc5a5f35d6b30b9b7b12be2566160a024769550290a0e7e4029' + 'SKIP' + 'f06834dfaa9e976a5778fa4fd07117a85bbff3b788c91e61d8cbc628e1b8bfb3' + 'SKIP' + '11884198740c5ac572cbe87086b1744ce19f0e1f856775e6edccce843ab097a3' + 'SKIP' + 'e6d848172228e98dde3a7ec679e7ea6bb0a42cca0b0105c58c8f9d6d44e33943' + 'SKIP' '4e80f2d05591175d4ed0a1a144509e97260e5593472aae586c07956215d99162' 'SKIP' - '8ebce8f38e38bd66879d040028c2448e5bc7dcde9df0ccee2b4221015591b162' + '1b442ad6f28c684068844ce93616690b84651e35870802b02937351bea078b4a' 'SKIP' - '0b0e05b615ae6eb825bf7d7fa568f551b0dff6c2cee62c9285c1b0d63224ea1c' + '9be4f91e6a4be87791d700f44c228fe599468f06a4f627b79e75718ac020db4d' + 'SKIP' + 'ed635ed3b63cdb61614b8805aa5a9515d5b538522165be625dca89bdb7d6e801' + 'SKIP' + 'e28c29b9f359db85a60bfd49d2f28ba51f4d2272eb79b7d3e19ba8b3d5acdac9' + 'SKIP' + '3175c56fd2935848ce99fe10be1bc348231909f8479c3d3af911c5c7ab7f381b' + 'SKIP' + '945c90d99f9a9fa47d61e471f76d17c0229ffa0fa8cda22de990a2ccc4194bb2' + 'SKIP' + '70ff41b2341836c27c5545a7948258f137d991c2879cbeb29f7af740c11d20e6' + 'SKIP' + 'b4efd997d3f09cbf258fd0f78aca44e4c96694dc4efc5c8c9a677ea67ebf5a54' 'SKIP' '2e1e2a0eb039ce61c1612bf6c7ab5227b99df053bcd5592da7758715dd333fda' 'SKIP' - '17de1f76da3db42dc3c7829a22953cd2c6de916999d809e3fb0bbb741753ec8f' + '6800dd82115ebe71d80dde0bb368fbd8563caa6c29127ffde0d6b5df81b502b0' 'SKIP' 'd90cc6570a47c8754e4c5d45834e0434c0a1fcabda9b8933b1fba84e3dabc083' 'SKIP' + 'a008c052ca657efcd4e27b8f7b819adbc9606e261aac37d41a40cf67c094cff9' + 'SKIP' '028bf538c870465e1752514725372fa9eef6b7178d9b8d1e2ecc15edc5a63cc8' 'SKIP' 'a92a84405ee9845738c6b52d8f0f0eca16eb40afe0ebe0f4e9e1168597675311' 'SKIP' '44a0b10a6dc83465ea86f5fba936b2bfbeffb1b3f0b2e04013bfed99ba4f6c5a' 'SKIP' - '229b28cee6f2b8afb5888eed453ce7f82de033900383bd91957339007c9e6cbf' + 'ab4a43d7b8d56a4405aaebaac00dd33d2019cae407039fad4b0dae9ba1ffaa78' 'SKIP' - 'c5d61db05b19fde06102b0b3dbd76e1989c8f07f3b7d175176bd2d73f1a71e5c' - 'SKIP' - '5988d7a37b4b71a64ed09fea872f08515a3c7029d33fd053a6a1822bb811ddb5' - 'SKIP' - '1ec4fb93700b0696ba2c2a371f218f13b32dcc745523c7ee26a5c3cd7af253fc' + 'f81f6b5769e47716ca318ecf76ab1c0fafa0e165fa858a64251359937bffcf04' 'SKIP' 'c67fee35a42866dd3c9056ff099fc504cdad9530c107d959331b5572b21af818' 'SKIP' - '36cb306751c57de3e61c7a4cd18a7f131eef0dc1de15a4e28751d1e5b08bace8' + 'fdfe62019e6ef5dfba2fabecc6ee5b58400f41ef000883fed135ecaca881b40c' 'SKIP' '07683e522d6dea3d3ce658ee80bf4317f6c3caf248259da960bc4610f4d1e807' 'SKIP' - '66a0933d2d4f2edc8999b6fdf0fe10b5b607646fd6f1a1d534bf26270543f8f1' - 'SKIP' 'c5279468d93e562f509a6b599a2b7ca7ee0032d3fe35314c5769fc6c9a9234ab' 'SKIP' 'c0111614ec44014cbf621eedf5a4ef302a855423bc1633ee692392950e971d0e' 'SKIP' - '1cbe1ddd3b37cd0c7e4bad4af08e681eda2f7a61698004322bb87f320aa95dbb' + 'c2f916fa04d91edf926abf8fed086302fe538ba62d92450af0ddb6d3a33942f1' + 'SKIP' + 'd17774053ab07de92e18abdb0ff4d00352a02b6a97c48e28d8c875954c8eec36' + 'SKIP' + '774f337663b215c5aa8877c06ffce95be44403fe77516c1be9ef9789e2d049e5' 'SKIP') export KBUILD_BUILD_HOST=omarchy @@ -572,7 +810,8 @@ build() { cd $_srcname MAKEFLAGS="-j$(nproc) $MAKEFLAGS" make all - make -C tools/bpf/bpftool vmlinux.h feature-clang-bpf-co-re=1 + env -u CFLAGS -u CXXFLAGS -u LDFLAGS \ + make -C tools/bpf/bpftool vmlinux.h feature-clang-bpf-co-re=1 } _package() { diff --git a/pkgbuilds/linux-omarchy/config.x86_64 b/pkgbuilds/linux-omarchy/config.x86_64 index 283d023..60a9459 100644 --- a/pkgbuilds/linux-omarchy/config.x86_64 +++ b/pkgbuilds/linux-omarchy/config.x86_64 @@ -996,9 +996,9 @@ CONFIG_SOFTIRQ_ON_OWN_STACK=y CONFIG_ARCH_HAS_ELF_RANDOMIZE=y CONFIG_HAVE_ARCH_MMAP_RND_BITS=y CONFIG_HAVE_EXIT_THREAD=y -CONFIG_ARCH_MMAP_RND_BITS=32 +CONFIG_ARCH_MMAP_RND_BITS=28 CONFIG_HAVE_ARCH_MMAP_RND_COMPAT_BITS=y -CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS=8 CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES=y CONFIG_HAVE_PAGE_SIZE_4KB=y CONFIG_PAGE_SIZE_4KB=y @@ -2558,7 +2558,7 @@ CONFIG_EFI_CAPSULE_LOADER=m # CONFIG_EFI_TEST is not set CONFIG_EFI_DEV_PATH_PARSER=y CONFIG_APPLE_PROPERTIES=y -CONFIG_RESET_ATTACK_MITIGATION=y +# CONFIG_RESET_ATTACK_MITIGATION is not set CONFIG_EFI_RCI2_TABLE=y # CONFIG_EFI_DISABLE_PCI_DMA is not set CONFIG_EFI_EARLYCON=y @@ -4669,6 +4669,7 @@ CONFIG_INPUT_IMS_PCU=m CONFIG_INPUT_IQS269A=m CONFIG_INPUT_IQS626A=m CONFIG_INPUT_IQS7222=m +CONFIG_INPUT_ISA1200_HAPTIC=m CONFIG_INPUT_CMA3000=m CONFIG_INPUT_CMA3000_I2C=m CONFIG_INPUT_XEN_KBDDEV_FRONTEND=m @@ -5485,6 +5486,7 @@ CONFIG_SENSORS_ASUS_ROG_RYUJIN=m CONFIG_SENSORS_AXI_FAN_CONTROL=m CONFIG_SENSORS_K8TEMP=m CONFIG_SENSORS_K10TEMP=m +CONFIG_SENSORS_KB9002=m CONFIG_SENSORS_KBATT=m CONFIG_SENSORS_KFAN=m CONFIG_SENSORS_FAM15H_POWER=m @@ -5676,7 +5678,10 @@ CONFIG_SENSORS_MP9941=m CONFIG_SENSORS_MP9945=m CONFIG_SENSORS_MPQ7932_REGULATOR=y CONFIG_SENSORS_MPQ7932=m +CONFIG_SENSORS_MPQ82D00=m CONFIG_SENSORS_MPQ8785=m +CONFIG_SENSORS_MPQ8646=m +# CONFIG_SENSORS_MPQ8646_DEBUG_UNSAFE is not set CONFIG_SENSORS_PIM4328=m CONFIG_SENSORS_PLI1209BC=m CONFIG_SENSORS_PLI1209BC_REGULATOR=y @@ -5684,6 +5689,8 @@ CONFIG_SENSORS_PM6764TR=m CONFIG_SENSORS_PXE1610=m CONFIG_SENSORS_Q54SJ108A2=m CONFIG_SENSORS_STEF48H28=m +CONFIG_SENSORS_SQ24860=m +CONFIG_SENSORS_SQ24860_REGULATOR=y CONFIG_SENSORS_STPDDC60=m CONFIG_SENSORS_TDA38640=m CONFIG_SENSORS_TDA38640_REGULATOR=y @@ -5694,10 +5701,12 @@ CONFIG_SENSORS_TPS53679=m CONFIG_SENSORS_TPS546D24=m CONFIG_SENSORS_UCD9000=m CONFIG_SENSORS_UCD9200=m +CONFIG_SENSORS_VT7505=m CONFIG_SENSORS_XDP710=m CONFIG_SENSORS_XDP720=m CONFIG_SENSORS_XDPE152=m CONFIG_SENSORS_XDPE1A2G7B=m +CONFIG_SENSORS_XDPE1A2G7B_REGULATOR=y CONFIG_SENSORS_XDPE122=m CONFIG_SENSORS_XDPE122_REGULATOR=y CONFIG_SENSORS_ZL6100=m @@ -6769,6 +6778,7 @@ CONFIG_VIDEO_IMX290=m CONFIG_VIDEO_IMX296=m CONFIG_VIDEO_IMX319=m CONFIG_VIDEO_IMX355=m +CONFIG_VIDEO_IMX471=m CONFIG_VIDEO_MAX9271_LIB=m CONFIG_VIDEO_MT9M001=m CONFIG_VIDEO_MT9M111=m @@ -10197,7 +10207,7 @@ CONFIG_AMD_IOMMU=y CONFIG_DMAR_TABLE=y CONFIG_INTEL_IOMMU=y CONFIG_INTEL_IOMMU_SVM=y -CONFIG_INTEL_IOMMU_DEFAULT_ON=y +# CONFIG_INTEL_IOMMU_DEFAULT_ON is not set CONFIG_INTEL_IOMMU_FLOPPY_WA=y CONFIG_INTEL_IOMMU_SCALABLE_MODE_DEFAULT_ON=y CONFIG_INTEL_IOMMU_PERF_EVENTS=y @@ -11844,7 +11854,10 @@ CONFIG_INTEGRITY_MACHINE_KEYRING=y # CONFIG_INTEGRITY_CA_MACHINE_KEYRING is not set CONFIG_LOAD_UEFI_KEYS=y CONFIG_INTEGRITY_AUDIT=y -# CONFIG_IMA is not set +CONFIG_IMA=y +CONFIG_IMA_DEFAULT_HASH_SHA256=y +CONFIG_IMA_WRITE_POLICY=y +CONFIG_IMA_READ_POLICY=y # CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT is not set # CONFIG_EVM is not set # CONFIG_DEFAULT_SECURITY_SELINUX is not set @@ -12274,7 +12287,7 @@ CONFIG_NEED_DMA_MAP_STATE=y CONFIG_ARCH_DMA_ADDR_T_64BIT=y CONFIG_ARCH_HAS_FORCE_DMA_UNENCRYPTED=y CONFIG_SWIOTLB=y -CONFIG_SWIOTLB_DYNAMIC=y +# CONFIG_SWIOTLB_DYNAMIC is not set CONFIG_DMA_NEED_SYNC=y CONFIG_DMA_COHERENT_POOL=y CONFIG_DMA_CMA=y